Modding:Parts

From Caves of Qud Wiki
Jump to navigation Jump to search
This article is a stub. You can help Caves of Qud Wiki by expanding it.
This article is a stub. You can help Caves of Qud Wiki by expanding it.

Introduction

Parts are a core element of how creatures, objects, zones, and more are implemented in the game. In essence, a "part" can be thought of as a bit of code attached to an object, consisting primarily of

Many important game elements are implemented using parts. Some examples include:

  • The look description of objects is implemented using the Description part[1].
  • The tile used to render objects is implemented using the Render part[2].
  • Mutations (and bonuses to mutations) are stored in the Mutations part[3], and individual mutations are also parts in and of themselves.
  • The player's score in A Call to Arms is implemented as a zone part[4].
  • The code used to track the start of the quest Reclamation is implemented as a part that gets added to the player[5].

The Snapjaw Mages tutorial introduces many important parts, and tables with other common parts for objects can be found in Modding:Objects.

Types of parts

Over time, many different types of parts have been implemented to support different kinds of functionality. The following (non-exhaustive) list specifies some of the more common and useful parts that the game uses:

  • IPart[6]: this is the most generic kind of part that can be added to an object.
    • BaseMutation: a part used to represent a single mutation.
    • IActivePart[7]: used to represent active parts. This encompasses parts that are only activated under certain conditions to perform various behaviors.
    • IGrenade[8]: specialized part used to enable grenade behaviors (either when throwing or manually detonating grenades).
    • IModification[9]: generic part that is used to implement item mods.
    • IPlayerPart[10]: a part that follows the player around even after changing bodies. This is usually used to implement player-specific behavior that is independent of the body that the player is in.
    • IPoweredPart[11]: a more specific variant of IActivePart which is generally used to represent parts for objects and abilities that require power, e.g. via energy cells.
    • IScribedPart[12]: a special type of part, primarily intended to be used by modders, that makes it easier to add or remove fields between mod versions. See Modding:Serialization (Saving/Loading) for more details.
  • IZonePart[13]: a part that can be applied to a zone. These are often used to help keep track of zone-wide events.

Examples

A part can be added to an object by adding the <part/> XML tag to it. For instance, the following code would merge the MyPart part into snapjaw scavengers:

<objects>
  <object Name="Snapjaw Scavenger" Load="Merge">
    <part Name="MyPart" />
  </object>
</objects>

The same tag can be used in Worlds.xml to add a part to a zone. For example, the sultan tombs in the Tomb of the Eaters use the AmbientStabilization part to provide ambient normality:

<zone Level="5" x="0" y="2" Name="Tomb of *Sultan1Name*" NameContext="the Tomb of the Eaters" DisableForcedConnections="Yes">
  <map FileName="Sultan1SW.rpm"></map>
  <builder Class="FlagInside"></builder>
  <music Track="Music/Deeper Eaters" />
  <part Name="AmbientStabilization" Strength="40" />
</zone>

Here is an example of a relatively simple part, implemented in C#. This part does not do anything by itself, it just contains a field Foo:

using System;

namespace XRL.World.Parts
{
    // Almost all parts will want to have [Serializable] attached to
    // them, because that will allow the game to convert the part
    // to data when saving the game. In some rare circumstances, you
    // can mark a part [NonSerialized] to make sure that the game
    // _does not_ save the part.
    [Serializable]
    public class MyPart : IPart
    {
        public string Foo = "hello, world!";
    }
}

This part could be attached to an object by adding <part Name="MyPart"/> to the object. If you wanted to specify a custom value for Foo for the part, then you could do something like

<part Name="MyPart" Foo="goodbye!" />

By itself this part is not very useful. The game could hypothetically access and use the Foo data attached to it with some code like

if (gameObject.TryGetPart<MyPart>(out var part))
{
    // Do something with part.Foo
}

However, this is a use case for which you would typically attach a <tag/>, <property/>, or an <intproperty/> to an object.

So in most cases, you will want your part to also include an event handler. Here is another version of MyPart which prepends the value of Foo to an object's display name:

using System;

namespace XRL.World.Parts
{
    // Almost all parts will want to have [Serializable] attached to
    // them, because that will allow the game to convert the part
    // to data when saving the game. In some rare circumstances, you
    // can mark a part [NonSerialized] to make sure that the game
    // _does not_ save the part.
    [Serializable]
    public class MyPart : IPart
    {
        public string Foo = "hello, world!";

        public override bool WantEvent(int ID, int cascade)
        {
            return ID == GetShortDescriptionEvent.ID
                || base.WantEvent(ID, cascade);
        }

        public override bool HandleEvent(GetShortDescriptionEvent E)
        {
            E.AddMark(Foo, DescriptionBuilder.ORDER_ADJUST_EXTREMELY_EARLY);
            return base.HandleEvent(E);
        }
    }
}

Now our MyPart part will actually do something with the data Foo that it comes with. In particular, it will listen for the GetShortDescriptionEvent event. When that event occurs (typically to get a creature or object's name), it will prepend the value of Foo to the name.

See Modding:Events for more details.

References

  1. XRL.World.Parts.Description
  2. XRL.World.Parts.Render
  3. XRL.World.Parts.Mutations
  4. XRL.World.Parts.CallToArmsScore
  5. XRL.World.Parts.OmonporchBattlePart
  6. XRL.World.IPart
  7. XRL.World.Parts.IActivePart
  8. XRL.World.Parts.IGrenade
  9. XRL.World.Parts.IModification
  10. XRL.World.Parts.IPlayerPart
  11. XRL.World.Parts.IPoweredPart
  12. XRL.World.IScribedPart
  13. XRL.World.ZoneParts.IZonePart