Modding:Parts
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
- some data for that object, and
- some event handlers.
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 ofIActivePart
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
- ↑
XRL.World.Parts.Description
- ↑
XRL.World.Parts.Render
- ↑
XRL.World.Parts.Mutations
- ↑
XRL.World.Parts.CallToArmsScore
- ↑
XRL.World.Parts.OmonporchBattlePart
- ↑
XRL.World.IPart
- ↑
XRL.World.Parts.IActivePart
- ↑
XRL.World.Parts.IGrenade
- ↑
XRL.World.Parts.IModification
- ↑
XRL.World.Parts.IPlayerPart
- ↑
XRL.World.Parts.IPoweredPart
- ↑
XRL.World.IScribedPart
- ↑
XRL.World.ZoneParts.IZonePart
|