Actor pointer
Actor pointers are used to keep track of an actor's relationships with others, such as which one spawned them, or which ones they spawned.
Target, master and tracer
There are 3 main pointers pointers that are available to all Actor classes, both in ZScript and DECORATE:
- target
- tracer
- master
ZScript, however, offers a plethora of other pointers, both for actors and for more specific classes.
These pointers are used differently, depending on which type of actor they're used in:
Projectiles
- target: A projectile's target is not the actor that it will seek after. Instead, this is the actor who fired the projectile (though a little counter-intuitive at that).
- tracer: This is set automatically only if the projectile has the +SEEKERMISSILE flag and calls a seeking function, like A_SeekerMissile. This is the actor the projectile will home at.
- master: Unused by default.
Monsters
- master: Unused by default. Can be set if the monster was spawned using A_SpawnItemEx with the SXF_SETMASTER flag, or is transferred over with the SXF_TRANSFERPOINTERS flag.
- target: This refers to whom the monster is chasing and will attack when called upon with A_Chase.
- tracer: Unused.
Special cases
Many special actors have different handling for these pointers which do not fit in the generic categories above. They include:
- Projectiles that call A_Explode will use their
target
as the source responsible for damage inflicted by the explosion (unless theXF_NOTMISSILE
flag is passed to the function). - SpawnShot (A_SpawnFly and A_SpawnSound will move it towards its target, not its tracer as with other projectiles).
- Archvile (A_VileTarget assigns its
tracer
to the spawned ArchvileFire, and A_VileAttack detonates the fire) - MinotaurFriend uses the
tracer
pointer to keep track of the player who summoned it. - HolyTail (A_CHolyTail uses the
tracer
pointer to manage its trail). - Lightning and derived classes use the
tracer
,target
andlastenemy
pointers to manage cohesion between the various components of the lightning column. - Dragon (its dedicated movement functions use the tracer fields as path nodes for its navigation)
- TeleportFog uses the actor who teleported as the
target
.
This list is not exhaustive.
ZScript
ZScript can utilize all of the above mentioned DECORATE pointers (target
, master
and tracer
), but also features a wide range of unique pointers. In addition, custom pointers can be declared.
Some of the most commonly used actor pointers in ZScript are:
- self
- Can be used by any ZScript class and points to the class itself.
- owner
- Used by inventory items and anything that inherits from them. This pointer refers to the actor that is holding the item.
- invoker
- Appears in the context of classes based on StateProvider, of which currently there are only two: Weapon and CustomInventory. This pointer is used in the states of those classes (outside of the Spawn state, which is used while the Weapon/CustomInventory is simply a pickup in the world) and action functions defined in those classes (such as A_FireBullets). In the context of these functions,
self
becomes the PlayerPawn owner of the Weapon/CustomInventory, whileinvoker
points to the Weapon/CustomInventory itself.
- player
- Used by the PlayerPawn class and poitns to the related PlayerInfo struct. PlayerInfo itself features a lot of unique pointers.
- mo
- Used by the PlayerInfo struct, and points to the PlayerPawn actor attached to it.
- LastHeard, LastEnemy, LastLookActor
- Used by enemies, these pointers are normally initially obtained with A_Look and similar functions and are used by the enemy to track its target when it can't be seen. To make an enemy completely forget about the target it was chasing, all of these pointers plus target must be set to
null
(alternatively, A_ClearTarget can be called).
- BlockingMobj
- Points to another actor that is blocking the movement of the current actor.
- DamageSource (New from 4.13.0)
- Used by hitscan puffs, points to the actor responsible for the attack that spawned the puff.
Note: Pointers listed above are pointers to instances of the Actor class only. However, actors have pointers to many non-Actor objects as well, such as BlockingLine that points to a Line that blocks the actor's movement, or cursector that points to the Sector the actor is currently is in, and others. You can find the full list of actors fields on GZDoom Github reponsitory
|
Pointers in ZScript can (and often need to) be stringed together. For example, to access the currently selected weapon from an inventory item, owner.player.readyweapon
is needed.
When killed, this version of Imp will spawn another Imp which has 500 health, is twice as slow and appears twice as big:
class DoomImpEnhanced : DoomImp replaces DoomImp
{
States
{
Death:
#### # 0
{
A_NoBlocking();
Actor foo = Spawn("DoomImp",pos);
if (foo) //it's important to null-check the spawned actor, just in case
{
foo.health = 500;
foo.scale *= 2;
foo.speed *= 0.5;
}
}
#### # 1 A_FadeOut(0.01);
wait;
}
}
Note, this doesn't cause recursive spawning of DoomImpEnhanced, because the Spawn function in ZScript by default spawns a specific class forcefully, ignoring Actor replacement rules.
Declaration and scope
Like all variables, pointers can be class-wide (class-scoped, i.e. available anywhere inside the class):
class MyClass : ParentClass
{
Actor foo; // This pointer is available anywhere within MyClass
... // the rest of the code
}
...and local (i.e. available only within a specific code block/function):
class MyClass : ParentClass
{
void MyFunction()
{
Actor foo; // This pointer is available only within MyFunction()
... // the rest of the function
}
}
class MyActor : Actor
{
States
{
Spawn:
PLAY A 10
{
Actor foo; // This is only available within this anonymous function
...
}
loop;
}
}
Casting
In the examples above, the type of the pointer is Actor
, which means it can point to a specific instance of an actor on the level. If a pointer is not only declared but immediately pointed to something, the Actor
part can be replaced with let
—this keyword sets the type automatically:
// Spawns a DoomImp at the same position as the calling actor
// and casts it to 'foo':
Actor foo = Spawn('DoomImp', pos);
// So does this:
let foo = Spawn('DoomImp', pos);
The type of the pointer doesn't have to be Actor
; it can point to a more specific class, like Inventory. However, the majority of already existing pointers (like target
/master
/tracer
) or pointers returned by common functions, like Spawn, are always implicitly Actor
pointers. This means that these pointers will only give you access to functions, fields and properties defined in the Actor class, but not to the ones defined in its child classes like Inventory or Weapon. That's why for example this is not possible:
// This won't compile!
// This code tries to spawn a Soulsphere, and then
// increase its amount x2. This causes an error,
// because Spawn returns an Actor pointer, but the
// 'amount' field is only defined in Inventory and
// doesn't exist in Actor:
let foo = Spawn('Soulsphere', pos);
foo.amount *= 2;
To get around this limitation, you need type casting. Type casting lets you tell the engine that this specific actor is not just an Actor, but a more specific class. The syntax for this is:
SpecificClass ptr = SpecificClass(otherpointer);
// This is also valid and works the same way:
let ptr = SpecificClass(otherpointer);
This example implies that otherpointer
is a generic pointer type (like Actor), and the author knows that it's actually an instance of SpecificClass, and tells the engine that ptr
should become a pointer to SpecificClass. Doing this sets the type of ptr
as SpecificClass (rather than Actor) and unlocks access to fields, pointers and functions defined in SpecificClass.
Note: while let ptr = SpecificClass(otherpointer)
is valid, SpecificClass ptr = otherpointer
is not valid and will not perform a cast. The casting itself is the SpecificClass(otherpointer)
part.
To be able to spawn an item and modify its properties, it looks like this:
let foo = Inventory(Spawn('Soulsphere', pos));
// Null-check to make sure the cast didn't fail:
if (foo)
{
foo.amount *= 2;
}
In the example above we can also cast the result of Spawn as Soulsphere
, but since the field we want to modify it is defined in Inventory, not in Soulsphere specifically, we can cast to a more general class.
Casting combined with null-checking can also be used as a form of an inheritance check instead of the is operator:
let foo = Zombieman(target);
if (foo)
{
// This code will only execute if the target pointer
// points to a Zombieman, or an actor that inherits
// from Zombieman.
}
Action functions, Weapon and CustomInventory
Classes based on StateProvider (currently this is Weapon and CustomInventory) have special handling of pointers within their states. With the exception of the Spawn state sequence (which is treated the same way as in any other actor), the state sequences of these classes have to interact with 3 different entities at the same time: the item/weapon itself; its owner (the player); the PSprite class which is responsible for handling the actual animations. As a result, pointers in these state sequences are treated as follows:
- invoker — this is the item/weapon itself
- self — this is not the item/weapon itself; this pointer actually pointers to the owner, i.e. the PlayerPawn that is currently using the item/weapon
- This pointer can be strung together further:
self.player
will point to the PlayerInfo struct of the owner;self.player.mo
will point to the owner again, but this time cast as PlayerInfo, not Actor.
The same rules apply to the functions defined with the action modifier. The majority of functions defined in the Weapon class are defined as action functions (for example, this applies to all). Within the context of those functions, invoker
is the weapon, while self
is its owner.
Note: the action
modifiedr should not be confused with DECORATE action functions. In DECORATE terminology any function that can be called from an actor state is called an "action function"; in ZScript the majority of Actor functions are not defined as action
, and don't need to be, and the action
modifier only applies to functions where diferentiating between invoker and self is relevant. If a regular, non-StateProvider actor uses an action
function, in it invoker and self will be identical.
DECORATE & ACS
Several DECORATE and ACS functions support custom retrieval and assignment of pointer values. The data location or method of retrieval is specified using named pointer selectors.
In contrast to ZScript, these pointers function as internal constants that can point to a specific actor.
All pointers are automatically supported by all implementing functions, unless the function documentation specifies otherwise.
ACS constant | DECORATE constant | ZScript analog | Static? | Modifiable? | Explanation |
---|---|---|---|---|---|
AAPTR_NULL | AAPTR_NULL | null | Yes | No | A null pointer. In ZScript this is not limited to actors. |
AAPTR_DEFAULT | AAPTR_DEFAULT | self Actor(self) |
No | No | Returns the source actor itself (null if there is no source actor). In ZScript, this is not limited to actors. If the context is ambiguous, ZScript may require using Actor(self) to explicitly tell the code the pointer is an actor in this instance. |
AAPTR_PLAYER# | AAPTR_PLAYER# | players[#].mo Note: In ZScript # would be 1 value smaller, because player numbers in ZScript begin with 0 |
Yes | No | Pointers to the PlayerPawn of the specified player. Note, in DECORATE these pointers begin with 1, but in ZScript player numbers begin with 0. In ZScript the global players array contains all players'PlayerInfo structs, and those structs have a mo pointer to their PlayerPawn actors.So, DECORATE's AAPTR_PLAYER1 is equal to players[0].mo in ZScript. For ZScript, see also PlayerNumber. Scripting tip: AAPTR_PLAYER1 points to player 1. AAPTR_PLAYER1<<X (shift bits X up) points to player (1 + X). This fact can be applied in ACS loops if you need to reference each active player in sequence. |
AAPTR_MASTER | AAPTR_MASTER | master | No | Yes | The actor's master pointer. Players normally don't have masters. |
AAPTR_TARGET | AAPTR_TARGET | target | No | Yes | The actor's target pointer. Players normally don't have targets. Note: Most functions use a special approach to find the target of a player; checking what they are aiming/looking at. This corresponds to AAPTR_PLAYER_GETTARGET. To make a single function that conforms to this standard, use the selector combination AAPTR_TARGET|AAPTR_PLAYER_GETTARGET. The most applicable method will be used (AAPTR_PLAYER_GETTARGET for any player). |
AAPTR_TRACER | AAPTR_TRACER | tracer | No | Yes | The actor's target tracer. Players normally don't have tracers. |
AAPTR_FRIENDPLAYER | AAPTR_FRIENDPLAYER | players[friendplayer].mo | No | No | If it's a FRIENDLY monster, this will be a pointer to the player they're friendly to. This is only relevant in multiplayer, where friendly monsters may not be equally friendly to everyone. Note, in ZScript the friendplayer field is numeric field on an actor, so to get the actual PlayerPawnof that player, checking the players global array is necessary. Also, if calling from a different actor/class,friendplayer will have to be prefixed with a pointer to the actor whose friendplayer you want to get. For more details on friendliness in ZScript, see IsFriend and IsHostile functions. |
AAPTR_GET_LINETARGET | AAPTR_LINETARGET | None. See AimTarget actor function for obtaining the same pointers in ZScript. |
No | No | Get the actor in the line of sight. This is similar to AAPTR_PLAYER_GETTARGET, except it is used for non-player actors. |
AAPTR_PLAYER_GETTARGET | AAPTR_PLAYER_GETTARGET | No | No | Get the actor in the player's line of sight. Most target-specific functions use this approach to determining the player's target. This only works if the actor has the SHOOTABLE and SOLID flags, and also lacks the NOBLOCKMAP flag, much like A_JumpIfTargetInLOS. | |
AAPTR_PLAYER_GETCONVERSATION | AAPTR_PLAYER_GETCONVERSATION | player.ConversationNPC | No | No | Get the actor currently talking to the player. Best used from a Strife dialogue that gives a custom inventory item, or starts a script with ACS_ExecuteWithResult (as it processes immediately). In ZScript this pointer is defined in the PlayerInfo struct and is not available for the base Actor class. |
Assignment
The following selectors expose fields for manipulation: AAPTR_MASTER, AAPTR_TARGET and AAPTR_TRACER.
Assignment operations will often, but not necessarily, prevent some assignments from occuring. Examples of such events are:
- An actor pointing to itself
- An infinite chain of references (two actors referencing eachother as master or target)
Prevention may involve cancelling the operation, or setting the pointer to NULL. Details on this should be included in the documentation of the individual function.
Revision information - assigning to pointers: Significant changes to this functionality are unlikely. Functions that support a set of selectors different from AAPTR_MASTER, AAPTR_TARGET, AAPTR_TRACER should list the supported features, along with any needed revision information.