Spawn

From The DarkMod Wiki
Jump to navigationJump to search

Spawn is a term describing the "birth" of an entity in the game world. Multiplayer gamers are probably familiar with the term respawn. Entities are the only objects which are able to spawn in the first place.

It might be interesting for you to know what happens behind the scenes when an entity spawns. This can happen at various times during runtime:

  • When starting a new map
  • The SDK code is spawning an entity while the game is running, e.g. when a projectile is fired / hitting a surface
  • A script spawns a new entity

Spawning Entities from a Script

To spawn a new entity via a map script, run this code

entity newGuard = sys.spawn("atdm:ai_merc_elite");

which will spawn an elite mercenary (at the world origin <0,0,0>, to be precise, which is probably not what you want, but that's not the subject here). The call will tell the SDK to spawn a new entity with the given entity class name. The corresponding SDK function is idGameLocal::SpawnEntityDef(), which will take care of the rest.

idGameLocal::SpawnEntityDef

The code in the gameLocal function SpawnEntityDef will try to look up the specified entityDef atdm:ai_merc_elite. If the entityDef is not found (i.e. it's defined nowhere in the .def files) the game will exit gracefully.

If the entityDef is found, the code attempts to read the "spawnclass" keyvalue from it. This key will tell the code which C++ class it needs to instantiate - in the case of the elite mercenary the C++ class this is idAI.

SpawnEntityDef("atdm:ai_merc_elite") => entityDef atdm:ai_merc_elite => "spawnclass" = "idAI"

The next steps are:

  1. Set a new name for this entity (each entity's name must be unique in the game)
  2. Apply the difficulty settings to the entity's spawnargs (this happens in TDM only)
  3. Find the C++ class with that name and create a new instance
  4. Call the Spawn() function of the entire class hierarchy

The first two steps are relatively easy and straightforward, just look at the code and you'll see what's going on.

Finding the C++ class is also rather easy: thanks to a number of code macros each C++ entity class has a name, which can be looked up in some kind of dictionary. Given the class was actually found, the CreateInstance() function is invoked on that class, which allocates the memory required by that class and calls the C++ constructor. The constructor is also registering the entity in the idGameLocal structures and performs a few other organisational tasks to link the new object.

Note that the class idAI is actually deriving from idEntity, which is the base class for all entities. The inheritance scheme for idAI is at the time of writing:

  • idEntity
    • idAnimatedEntity
      • idAFEntity_Base
        • idAFEntity_Gibbable
          • idActor
            • idAI

The standard C++ code will take care that all the constructors in the inheritance chain are called, starting from idEntity downwards.

Once the entity is allocated, constructed and registered, the Spawn() function is invoked on every level in the class hierarchy, i.e. the code calls idEntity::Spawn(), then idAnimatedEntity::Spawn(), ... and finally idAI::Spawn().

When C++ programmers look at the Spawn() function declaration, they might start wondering how this is possible with the Spawn() method being actually non-virtual (compare game/entity.h):

void	Spawn();

It happens like this: Instead of making the idEntity::Spawn() virtual and relying on the subclasses to pass the Spawn() call up to the base class, id software chose to implement some trickery to make this happen automatically. Therefore, it's important to note that you don't need to call the base class in the derived class' spawn function. Behind the scenes, this is handled in a recursive function idClass::CallSpawnFunc which inspects the (also home-grown) type information and calls the Spawn function pointer manually at each level.

Note: the same applies to the idEntity::Save and idEntity::Restore routines. You don't need to save the base classes during saving and loading, if your class is actually deriving from idEntity.