Jump to content

Onspawnscript

From OpenBOR
Revision as of 21:27, 18 August 2026 by Dcurrent (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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_OWNER
  • ENTITY_PROPERTY_PARENT
  • ENTITY_PROPERTY_PLAYER_INDEX
  • ENTITY_PROPERTY_POSITION_X
  • ENTITY_PROPERTY_POSITION_Y
  • ENTITY_PROPERTY_POSITION_Z
  • ENTITY_PROPERTY_SPAWN_TYPE
  • ENTITY_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:

  1. The global_model Onspawnscript executes with the new entity as self.
  2. The spawned entity's own model Onspawnscript executes with the same self.
  3. 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.

See Also