Onspawnscript
onspawnscript defines a model-level lifecycle hook that executes when an entity enters play through an Onspawn-aware spawn system. It receives the new entity as self after model defaults and the spawning system's primary context have been established, while the entity's first ordinary update still remains ahead.
Onspawnscript is the natural place to initialize behavior that belongs to an entity instance rather than its shared model. Typical uses include configuring artificial intelligence, registering the instance with project systems, applying spawn-source rules, connecting a child or summon to its creator, selecting an initial variant, preparing custom movement, and creating coordinated effects.
The hook is not limited to entities listed directly in a level. Gameplay players, script-spawned entities, animation-command children and summons, native projectiles, dust, flashes, steam, and several other engine-created entities use the same event. Player respawns create a new entity instance, so they invoke Onspawnscript again.
Syntax
onspawnscript {path}
# Default
# No corresponding Onspawnscript
{path}- Path to an OpenBOR Script source file.- The command belongs in a model definition.
- OpenBOR loads and compiles the supplied source with the model.
- The source defines a normal
main()entry point. - The script's return value is ignored.
Example model header:
name example_enemy
type enemy
onspawnscript data/scripts/example_spawn.c
Event code may also be embedded directly in the model:
onspawnscript @script
void main()
{
void self = getlocalvar("self");
initialize_entity_instance(self);
}
@end_script
initialize_entity_instance() in this example represents a project-defined function.
Local Variables
| Local | Type | Value |
|---|---|---|
self
|
Entity pointer | Newly spawned entity whose Onspawn event is in progress. |
No parent, owner, player index, spawn location, spawn type, level entry, or source entity is supplied as a separate local. Retrieve available context from self.
Useful entity properties include:
ENTITY_PROPERTY_OWNERENTITY_PROPERTY_PARENTENTITY_PROPERTY_PLAYER_INDEXENTITY_PROPERTY_POSITION_XENTITY_PROPERTY_POSITION_YENTITY_PROPERTY_POSITION_ZENTITY_PROPERTY_SPAWN_TYPEENTITY_PROPERTY_UNIQUE_ID
Availability reflects the native spawning path. Projectile and subentity systems normally assign owner or parent relationships before invoking Onspawnscript. Unrelated entities can legitimately have empty owner and parent properties.
Runtime Timing
OpenBOR creates and prepares an entity before dispatching Onspawnscript. By callback time, the entity normally has:
- Its model data and model scripts copied into the new instance.
- Health, magic, direction, position, palette, lifespan, and basic timestamps initialized.
- Native behavior defaults for its model type established.
- Its starting animation selected when the model supplies one.
- Source-specific parent, owner, faction, velocity, base, or spawn-type information assigned by many native spawning systems.
The starting animation can enter its first indexed animation position during default initialization. Animationscript may consequently execute for that position before Onspawnscript.
Onspawnscript then runs synchronously inside the spawning operation. The new entity is available immediately to the script, though OpenBOR deliberately skips its ordinary main entity update during the logical tick in which it was created. Updateentityscript, scheduled thinking, animation advancement, and attack collision therefore wait for a later eligible tick.
Post-update processing still includes every existing entity. Terrain adjustment, gravity, pending movement, and binding can act on a fresh entity during its spawn tick. Position, velocity, base, movement, or binding changes made by Onspawnscript may therefore affect those later stages immediately.
Global and Model Hooks
The cached model named global_model may also define onspawnscript. When both global and entity model hooks exist, OpenBOR executes them in this order:
- The
global_modelOnspawnscript executes with the new entity asself. - The spawned entity's own model Onspawnscript executes with the same
self. - The spawning system resumes any work scheduled after model callbacks.
Changes made by the global hook are visible to the entity model hook. This ordering supports a broad project policy followed by specialized model initialization.
Global Onspawnscript is especially useful for instance registration, common metadata, universal difficulty rules, faction normalization, diagnostics, and routing behavior by model or spawn type. It runs for every entity whose spawning path dispatches the Onspawn event, including helper effects and project-created children. Global logic that creates more entities must guard its own spawn paths to avoid unintended recursion.
Spawn Source and SPAWN_TYPE
ENTITY_PROPERTY_SPAWN_TYPE identifies how many native systems created an entity. Common exposed values include:
| Constant | Typical source |
|---|---|
SPAWN_TYPE_NONE
|
No specialized classification. Generic script spawn() normally begins with this value.
|
SPAWN_TYPE_LEVEL
|
Spawn entry read from a level. |
SPAWN_TYPE_PLAYER_MAIN
|
Gameplay player created by the player spawning system. |
SPAWN_TYPE_CHILD
|
Child created by the model child-spawn system. |
SPAWN_TYPE_CMD_SPAWN
|
Entity created by an animation's spawn command.
|
SPAWN_TYPE_CMD_SUMMON
|
Entity created by an animation's summon command.
|
SPAWN_TYPE_PROJECTILE_NORMAL
|
Standard native projectile. |
SPAWN_TYPE_PROJECTILE_BOMB
|
Native bomb projectile. |
SPAWN_TYPE_PROJECTILE_STAR
|
Native star projectile. |
SPAWN_TYPE_FLASH
|
Native hit-flash effect. |
SPAWN_TYPE_DUST_JUMP, SPAWN_TYPE_DUST_FALL, or SPAWN_TYPE_DUST_LAND
|
Native movement dust effect. |
SPAWN_TYPE_STEAM
|
Native steam effect. |
Spawn type is context, not a complete event record. Several sources can share SPAWN_TYPE_NONE, and specialized wrappers may assign or revise classification after Onspawnscript has already executed. Parent, owner, model, position, player index, and project-managed data can provide additional routing information.
This compact example gives summoned entities different setup from ordinary spawns:
void main()
{
void self = getlocalvar("self");
void parent = get_entity_property(
self,
ENTITY_PROPERTY_PARENT
);
int spawn_type = get_entity_property(
self,
ENTITY_PROPERTY_SPAWN_TYPE
);
if(spawn_type == SPAWN_TYPE_CMD_SUMMON && parent)
{
initialize_summon(self, parent);
}
else
{
initialize_standard_spawn(self, spawn_type);
}
}
The called functions are project-defined and illustrate source-aware routing rather than a complete summon system.
Relationship to Level spawnscript
Onspawnscript and a level entry's spawnscript are separate hooks.
| Feature | onspawnscript
|
Level spawnscript
|
|---|---|---|
| Ownership | Model, plus optional global_model hook.
|
One level spawn entry. |
| Scope | Follows the model wherever an Onspawn-aware system creates it. | Applies only to the level entry containing the command. |
| Main local | self
|
self, plus spawn-entry coordinates and timing data.
|
| Order for a level entry | Global Onspawnscript, then entity model Onspawnscript. | Executes after both Onspawnscript callbacks. |
| Typical role | Entity-instance initialization and model-wide behavior. | Placement-specific level logic. |
Level spawn properties such as alias, configured health, magic, palette, faction, aggression, entity type override, and parent are applied before Onspawnscript. The level entry's spawnscript follows afterward and can inspect or revise the result.
Use Onspawnscript when behavior belongs to every created instance of a model. Use level spawnscript when behavior belongs to one appearance at one location.
Players, Joining, and Respawning
Gameplay players use the ordinary smart-spawn path with SPAWN_TYPE_PLAYER_MAIN. Their player index is already available when Onspawnscript executes.
Some player-slot adjustments occur afterward. Stored health and magic restoration, final weapon selection, command-history reset, and related player bookkeeping can follow the callback. Onspawnscript should not assume every player-specific value is final merely because the base entity exists.
Initial level players and players joining during play both invoke Onspawnscript as part of creating their gameplay entity. Mid-level death and continue handling also creates a fresh player entity, invokes Onspawnscript, completes player-specific setup, then invokes the separate Respawnscript for that player slot. Joining follows with Joinscript instead.
Onspawnscript is therefore suitable for initialization required on every new player instance. Respawnscript remains suitable for game-wide continuation logic that specifically belongs to the respawn event.
Caller Adjustments and Low-Level Spawns
Onspawnscript is a lifecycle dispatch point rather than the raw memory-allocation routine. Normal gameplay spawn systems deliberately invoke it after establishing their relevant context. Low-level engine code can allocate an entity without dispatching the event, and a specialized wrapper can continue adjusting the entity after the callback.
Current examples of later adjustments include player restoration, level spawnscript, final item-drop placement, and some specialized spawn classifications. An internally created equipped weapon object or player-selection sample can also use low-level creation without an Onspawn dispatch.
Consequences for creator code include:
- Treat properties as the state available at callback time, not a guarantee that no spawning wrapper will change them later.
- Prefer owner, parent, and spawn type only when the relevant source is known to establish them before dispatch.
- Use the later source-specific hook when logic depends on post-Onspawn data.
- Do not expect changing an existing entity's model to invoke Onspawnscript. Onmodelcopyscript covers model-copy events.
Return Value and Entity Removal
The return value is ignored. Returning 0, 1, or another value does not cancel creation.
Script may change the new entity directly, and normal entity-removal functions can mark it for removal. The callback chain does not perform an existence test between the global hook, entity model hook, and a following level spawnscript. Removing self from an early callback does not prevent later callbacks in that chain from being entered with the same entity pointer.
Spawn eligibility is best decided before creation when possible. When removal is intentional, every later callback in the applicable chain should tolerate an entity already marked for removal.
Nested Spawns and Recursion
Onspawnscript executes synchronously. If it creates another entity whose spawning path dispatches Onspawnscript, the new callback runs before the original spawning operation finishes.
This behavior enables coordinated construction. One entity can create helpers, visual components, controllers, shadows, weapons, or linked participants and initialize each through its own model hook. It also permits a single parent spawn to build a complete multi-entity system before ordinary updates begin.
Unbounded spawn chains can recurse until entity allocation or script execution fails. Common hazards include:
- A model's Onspawnscript spawning another copy of the same model.
- Two models spawning each other during their Onspawn callbacks.
- Global Onspawnscript creating an effect that re-enters the same global creation rule.
Guard nested creation by model, spawn type, parent, owner, project state, or another explicit condition.
Quick Examples
Universal Project Initialization
Placed on global_model, this pattern applies common rules while excluding transient native effects:
void main()
{
void self = getlocalvar("self");
int spawn_type = get_entity_property(
self,
ENTITY_PROPERTY_SPAWN_TYPE
);
if(spawn_type == SPAWN_TYPE_FLASH
|| spawn_type == SPAWN_TYPE_DUST_JUMP
|| spawn_type == SPAWN_TYPE_DUST_FALL
|| spawn_type == SPAWN_TYPE_DUST_LAND)
{
return;
}
register_spawned_entity(self, spawn_type);
apply_global_spawn_rules(self);
}
register_spawned_entity() and apply_global_spawn_rules() represent project systems. Explicit exclusions also keep the global hook lightweight for frequently created effects.
Owner-Aware Projectile Setup
Native projectile systems normally establish the owner before dispatching Onspawnscript:
void main()
{
void self = getlocalvar("self");
void owner = get_entity_property(
self,
ENTITY_PROPERTY_OWNER
);
if(owner)
{
inherit_projectile_behavior(self, owner);
}
}
This pattern can select projectile power, visual treatment, allegiance rules, sound, homing behavior, or other features from the firing entity without placing the same setup in every attack animation.
Stable Instance Registration
Each new entity receives a process-lifetime unique identifier before Onspawnscript:
void main()
{
void self = getlocalvar("self");
int instance_id = get_entity_property(
self,
ENTITY_PROPERTY_UNIQUE_ID
);
register_instance(self, instance_id);
select_spawn_variant(self, instance_id);
}
The project-defined functions might register a controller target or choose deterministic instance-specific presentation. The entity pointer remains the direct handle; the unique identifier is useful when project data must distinguish one lifetime from a later entity occupying the same engine slot.
Common Uses
- Initializing custom artificial intelligence or state machines.
- Registering a new entity with managers, controllers, interface systems, or encounter logic.
- Applying global difficulty, game-mode, or accessibility rules.
- Configuring summons and children from their parent.
- Configuring projectiles from their owner.
- Building compound entities from several linked parts.
- Selecting an initial palette, animation, behavior package, or presentation variant.
- Applying spawn-source-specific rules without duplicating setup in level files and animation commands.
- Preparing initial velocity, movement, binding, faction, or combat state before the first ordinary update.
- Logging or diagnosing entity creation across a project.
Common Mistakes
- Confusing model Onspawnscript with a level entry's
spawnscript. - Expecting a return value to cancel entity creation.
- Assuming Onspawnscript executes once per model rather than once per dispatched entity instance.
- Assuming every low-level entity allocation dispatches the hook.
- Assuming every property is final when specialized caller adjustments may still follow.
- Expecting a model change on an existing entity to invoke Onspawnscript.
- Expecting the fresh entity's ordinary update to run during the same logical tick.
- Forgetting that gravity, movement, terrain, and binding can still process the fresh entity during that tick.
- Spawning entities recursively without an explicit termination condition.
- Using expensive global logic for high-frequency dust, flash, steam, or projectile creation without filtering.