Extending creatures and items classes

You can derive descendants of CastleCreatures and CastleItems classes, to customize the behavior of creatures and items. There are a lot of methods to override and behaviors to customize. For example, you can make something interesting happen when you use an item (like heal the player), or when creature creature state changes (e.g. creatures explodes into other creatures when dying). See examples/fps_game/fps_game.lpr for an example that customizes what happens when using a medkit item.

You can start your customizations from full-features classes, like TWalkAttackCreatureResource and TWalkAttackCreature. Or you can take the basic TCreatureResource and TCreature, and extend them to your liking.

This is a good moment to browse the classes inside CastleCreatures and CastleItems unit, if you haven't already. Some of the important creature/item classes:

  • TCreatureResource working with TCreature
    • TWalkAttackCreatureResource working with TWalkAttackCreature
    • TMissileCreatureResource working with TMissileCreature
    • TStillCreatureResource working with TStillCreature
  • TItemResource working with TInventoryItem
    • TItemWeaponResource working with TItemWeapon

Why are there two classes (TXxxResource and TXxx) for everything?

A "resource" is an information shared by all creatures/items of given type.

  1. For example you can have two instances of TCreatureResource: Werewolf and Knight. (Actually, they would probably be instances of TWalkAttackCreatureResource, as TCreatureResource is abstract.) Using them you can create and place on your your level millions of actual werewolves and knights (instances of TWalkAttackCreature). Every werewolf on the level will have potentially different life (fully healed vs almost dead) and state (attacking, walking, dying and such), but all werewolves will share the same resource, so e.g. all werewolves will use the same dying animation (TWalkAttackCreatureResource.DieAnimation) and dying sound (TCreatureResource.SoundDie).

  2. A similar example for items: you can have two instances of class TItemResource: Sword and LifePotion. (Actually, TItemWeaponResource, which is a descendant of TItemResource, sounds like a better candidate for the Sword.) Using them, you can create millions of actual swords and life potions, and place them of your level (as well as in inventories of creatures/players). Every life potion (TInventoryItem instance) may keep some individual information (for example, how much of the potion is already used/drunk), but all life potions will share the same TItemResource instance, so e.g. they all will be displayed using the same model on 3D level (TItemResource.BaseAnimation) and the same image in 2D inventory (TItemResource.Image).

Everything is designed to give you a lot of properties to set (most of them are also settable by resource.xml files) and a lot of methods to override. All creatures and items descend from common classes in CastleTransform unit, so see also there for various things that you can override and use.

You can code new creatures/items behaviors by deriving new classes from our existing classes in CastleCreatures and CastleItems and CastleTransform units. This is the most flexible way to customize everything about a creature/item.

You usually override two classes to define a new creature/item:

  1. The resource class (descendant of T3DResource, like TCreatureResource or TItemResource). The resource class defines the shared information for the whole creature/item kind. For example, you can image a TWerewolfResource that is derived from TWalkAttackCreatureResource and adds the ability to make a howling sound from time to time. TWerewolfResource would introduce a property like HowlingSoundName to be able to define sound for it. Or imagine a TPotionResource that is derived from TItemResource and add a properties saying which player attribute (health, mana, stamina) is regenerated and how much.

    The resource class can be registered, like

    uses ..., CastleResources;
     
    ...
    RegisterResourceClass(TWerewolfResource, 'Werewolf');
    RegisterResourceClass(TPotionResource, 'Potion');

    which allows this class to be referenced inside resource.xml files using the type attribute, like type="Werewolf". Many resource.xml files may use the same type="xxx", they only must have a different name="xxx". Every resource.xml file that uses type="Werewolf" will make a new instance of the TWerewolfResource class to be created at the Resources.LoadFromFiles call.

    This way you can imagine creating a couple of resource.xml files that define a couple of resource instances:

    <resource
      name="WerewolfRookie"
      type="Werewolf"
      default_max_life="10.0"
      sound_howling="werewolf_rookie_howling">
      <model>...</model>
    </resource>
    <resource
      name="WerewolfBoss"
      type="Werewolf"
      default_max_life="1000000.0"
      sound_howling="werewolf_boss_howling">
      <model>...</model>
    </resource>
    <resource
      name="SmallLifePotion"
      type="Potion"
      regenerate_stat="Life"
      regenerate_amount="10.0">
      <model>...</model>
    </resource>
    <resource
      name="LargeLifePotion"
      type="Potion"
      regenerate_stat="Life"
      regenerate_amount="50.0">
      <model>...</model>
    </resource>
    <resource
      name="ManaPotion"
      type="Potion"
      regenerate_stat="Mana"
      regenerate_amount="10.0">
      <model>...</model>
    </resource>

    As you can see in the above examples, you can use the same resource class in many ways. Practically speaking, you only need to create a new resource class (like TWerewolfResource) when you really need to introduce a new behavior that needs to be implemented using ObjectPascal. Otherwise, if what you want can be achieved by tweaking the value of a property of an existing class, then you don't need new resource class, you only need to create new resource.xml file that refers to the same class but sets different value for given property.

    After calling Resources.LoadFromFiles, the Resources list will be filled with instances of appropriate classes. You can find them by name, e.g.

    var
      WerewolfRookie: TWerewolfResource;
      ...
      WerewolfRookie := Resources.FindName('WerewolfRookie') as TWerewolfResource;

    Defining a new property in a resource class usually means defining a normal ObjectPascal property and overriding T3DResource.LoadFromFile to load the value of this property. See existing units like CastleCreatures and CastleItems for a lot of examples.

  2. The second class will be used to represent a single occurrence of this creature/item in the 3D world. This has a reference to the appropriate resource (for shared information) and can have it's own properties, specific to this current instance. For example, creatures have their current Life.

    You can imagine that a potion instance could have a property saying e.g. how much of it was drunk (if you want to allow player to drink only parts of the potions).

    The resource class indicates the related non-resource class by the TCreatureResource.CreatureClass or TItemResource.ItemClass method (although it's not necessary, but usually you want to define non-resource class together with resource class to implement the behavior you need). These are used by TCreatureResource.CreateCreature and TItemResource.CreateItem methods, which you can use to create creature/item occurrence by code:

    type
      TWerewolfResource = class(TWalkAttackCreatureResource)
      public
        function CreatureClass: TCreatureClass; override;
        ...
      end;
     
      TWerewolf = class(TWalkAttackCreature)
        ...
      end;
     
    function TWerewolfResource.CreatureClass: TCreatureClass;
    begin
      Result := TWerewolf;
    end;
     
    ...
    { and if you want to create werewolves programmatically
      (not just by placing "placeholders" on level 3D model) then do this: }
    for I := 1 to 100 do
      WerewolfRookie.CreateCreature(Level,
        Vector3(1, 2, 3) { position }
        Vector3(1, 0, 0) { direction });

    There are many possible classes to override. Overriding the more specialized (finished) classes, like TWalkAttackCreature*, is nice to merely tweak some detail. Overriding the more basic classes, like TCreature*, is good if you're prepared to implement something (like creature AI) completely from scratch on your own.

    Overriding the really basic classes from CastleTransform unit (and optionally also CastleResources, if you want your 3D object to be associated with a resource) is for advanced usage, when you want to define a 3D object within your game that doesn't really fit our creatures/items definitions. CastleTransform contains a lot of classes to make it easy to create your own, dynamic 3D objects.