Jump to content

Animationscript: Difference between revisions

From OpenBOR
No edit summary
Line 377: Line 377:


[[Category:Openbor]]
[[Category:Openbor]]
[[Category:Animation]]
[[Category:Model]]
[[Category:Model]]
[[Category:Script]]
[[Category:Script]]
[[Category:Animation]]
[[Category:Script Events]]

Revision as of 18:54, 17 August 2026

animationscript defines OpenBOR's animation-update script. The engine executes it whenever an animating entity enters or is moved to an indexed animation frame, including normal advancement, loop transitions, animation starts, and explicit frame updates.

The command name is technically exact - the compiled script belongs to the model's animation system and may service every animation in that model. Conceptually, its runtime role is a frame-update hook. It provides the current entity, animation, and frame index at the precise point where the indexed animation state changes.

This placement makes Animationscript one of OpenBOR's most flexible action-building tools. Creators may launch effects, branch attacks, coordinate linked entities, modify velocity, manage animation phases, create cancels, spawn projectiles, apply state changes, or replace native frame behavior before the engine finishes processing the new index.

Frames and Animation Data

OpenBOR uses the term frame for each indexed step presented to creators, and the model frame command adds such a step to an animation. Internally, however, there is no standalone frame object containing a collection of properties.

Why No Frame Object?

OpenBOR deliberately eschews the object-oriented convention of conceptual frames as a discrete objects in favor of a data-oriented architecture. This is an engine-level optimization rather than a missing abstraction.

Each conceptual frame may comprise dozens of possible properties, while a single animation may contain dozens of frame positions and every loaded model may contain dozens of animations. Animation-frame data is sparse and highly heterogeneous, making a conventional fixed-layout frame representation potentially wasteful.

OpenBOR instead keeps frame-specific data as arrayed members of the owning animation, keyed by a shared frame-position index. This avoids allocating a complete record for every conceptual frame while retaining direct access to the properties that actually define each indexed position.

Each animation owns parallel property arrays. Sprite, delay, movement, offset, collision, sound, draw method, vulnerability, and other frame-specific data use the same zero-based index across their respective arrays. The frame local supplied to Animationscript is this shared frame-position index.

Animationscript is therefore best understood as code executed when an animation updates its current indexed position, rather than as a callback belonging to a separate frame object.

Syntax

animationscript {path}

# Default
# No external animation-script source
  • {path} - Path to an OpenBOR Script source file containing animation helper functions, constants, and optional lifecycle functions.
  • The command belongs in a model definition.
  • The external source file does not define main().
  • OpenBOR generates the Animationscript main() function while loading the model.
  • Model @cmd and @script content is merged into the generated entry point.
  • The compiled script is shared by the model, while each entity supplies its own execution context and locals.

Models may omit the animationscript command when they only call built-in functions through @cmd or use animation-level @script. OpenBOR still generates and compiles the required animation script from that model content.

Example model header:

name example_character
type enemy

animationscript data/scripts/example_animation.c

External source file:

void set_animation_phase(int phase)
{
    void self = getlocalvar("self");
    setentityvar(self, "animation_phase", phase);
}

void clear_animation_phase()
{
    void self = getlocalvar("self");
    setentityvar(self, "animation_phase", 0);
}

The file supplies callable functions, but no main(). OpenBOR builds the entry point that calls these functions from the model's animation instructions.

Helper source may also be embedded directly:

animationscript @script
void set_animation_phase(int phase)
{
    void self = getlocalvar("self");
    setentityvar(self, "animation_phase", phase);
}
@end_script

Calling Functions with @cmd

@cmd converts a model instruction into a script function call tied to the immediately following frame command.

anim attack1
    @cmd set_animation_phase 1
    frame data/chars/example/attack_01.png

    @cmd set_animation_phase 2
    frame data/chars/example/attack_02.png

    @cmd clear_animation_phase
    frame data/chars/example/attack_03.png

OpenBOR generates the frame-index checks and function calls during model loading. In this example, the phase values become 1, 2, and 0 as the animation enters indexes 0, 1, and 2.

Several @cmd instructions may precede the same frame. Their calls execute in model-source order.

Arguments are compiled as script expressions. Values may include integers, decimals, quoted strings, constants, NULL(), getlocalvar("self"), or other expressions accepted by the script parser.

Inline @script

Model @script inserts code directly into the generated Animationscript entry point for the current animation.

anim walk
    @script
        void self = getlocalvar("self");

        if(frame == 0)
        {
            setentityvar(self, "walk_cycle", 1);
        }
    @end_script

    frame data/chars/example/walk_01.png
    frame data/chars/example/walk_02.png
    frame data/chars/example/walk_03.png

Unlike @cmd, placement of @script beside a particular frame does not bind it to that frame. Inserted code is evaluated on every animation update within the containing animation. Use the automatically generated frame variable when behavior should apply only to selected indexes.

The generated entry point already declares frame and animhandle. Inline code may use those names directly. Retrieve self or animnum with getlocalvar() when needed.

Functions cannot be declared inside a model @script block because the block is inserted inside generated main(). Place reusable function definitions in the external or header-level Animationscript source.

Generated Entry Point

During model loading, OpenBOR performs the following work:

  1. Reads the source supplied by animationscript, when present.
  2. Collects animation-level @script blocks.
  3. Converts each @cmd instruction into a function call guarded by concrete animation and frame indexes.
  4. Generates main() with local frame and animhandle variables.
  5. Merges generated animation routing into the helper source.
  6. Compiles the completed script once for the model.

Creators should not supply their own main(). The engine-owned entry point is what connects model animation data, @cmd, @script, and runtime frame updates.

Generated source for animation commands is written to Logs/ScriptLog.txt. That file is useful when an @cmd call, argument, inline block, or generated line fails to compile.

Runtime Timing

When OpenBOR updates an entity's animation frame, the relevant order is:

  1. Validates the requested index against the current animation.
  2. Makes the entity the active execution subject.
  3. Assigns the requested index as the entity's current animation position.
  4. Calculates the next animation timestamp from the new index's delay and clears frame pause state.
  5. Populates self, animnum, frame, and animhandle.
  6. Executes the current model's Animationscript.
  7. Executes the entity's default-model Animationscript when the current model differs from its default model.
  8. Stops processing the old animation update when script changed the animation or frame index.
  9. Otherwise applies remaining native on-entry effects for the index.

Native effects after Animationscript include configured movement, base adjustment, facing reversal, weapon changes, quake, sub-entity operations, child spawning, sounds, jump velocity, and legacy projectile launching.

The new frame index is already current when script begins. Frame-indexed collision, sprite, draw, delay, and other animation data can therefore be inspected through the current entity and animation. Remaining native on-entry operations have not yet executed.

Interrupting Native Frame Processing

OpenBOR records the animation and requested frame before running Animationscript. After script returns, the engine compares those values with the entity's current animation and frame.

Changing either one interrupts the remainder of the original frame update. Native movement, flip, weapon, quake, spawn, sound, jump, and projectile instructions belonging to the abandoned frame do not execute afterward.

This behavior supports clean branching and replacement:

Pattern Use of animationscript
Conditional attack branch Check input, hit state, meter, target position, or project variables, then enter another animation before the old frame launches its remaining native effects.
Dynamic action phase Mark startup, active, recovery, invulnerable, armored, cancelable, or vulnerable phases as specific indexes begin.
Motion control Adjust velocity or position before native frame movement and jump impulses are applied, or replace the frame to suppress them.
Synchronized choreography Move, bind, animate, or release targets and helper entities at exact animation indexes for throws, team attacks, mounts, cut-ins, or transformations.
Custom effect routing Spawn projectiles, particles, sounds, camera effects, trails, decals, or interface feedback from reusable helper functions.
Procedural animation behavior Choose alternate timing, sprite state, direction, movement, or follow-up logic from runtime conditions rather than fixed model data alone.
State cleanup Clear temporary flags, armor, invincibility, bindings, overlays, or resources at the exact animation update where their phase ends.
Debugging and telemetry Record animation transitions and frame indexes, inspect unexpected branches, or expose current action phases to development tools.

Frame-changing functions may immediately start another animation update. Scripts that repeatedly reset the same animation or redirect to a frame whose code redirects back can create recursive execution or an endless transition chain. Guard such branches with state or a condition that becomes false after the transition.

Execution Frequency

Animationscript responds to indexed animation updates, not display refresh or every entity update.

Situation Animationscript execution Notes
Normal animation start Yes Entering a newly started animation normally updates index 0 immediately.
Natural frame advance Yes Runs when the configured delay expires and animation processing advances to the next index.
Loop transition Yes Runs when the animation moves from its loop end to the configured loop start.
Frame held for several logic or display updates No repeated execution Delay controls how long the current index remains active; Animationscript does not poll throughout that hold.
Infinite-delay frame Once on entry No further execution occurs until another operation changes or updates the animation frame.
Stopped non-looping animation No further execution Reaching the end disables animation advancement unless another operation starts or updates animation state.
updateframe() Yes, while the entity is animating Explicitly routes the requested index through native frame-update processing.
Native land-frame, drop-frame, bind-frame, or grab-frame adjustment Yes, while animating These systems explicitly call the same frame-update path.
Direct raw animation-position assignment No generated update by itself Directly changing a property is not equivalent to updateframe() and does not apply the full frame-entry path.
Synchronized animation change retaining the current index No immediate fresh update Native synchronization may transfer the existing position without re-entering frame-update processing.
Entity frozen or paused without advancing No repeated execution The script resumes when animation processing reaches another frame update.

Repeated visits to an index are separate updates. Looping back to frame 0, explicitly calling updateframe(), or later re-entering the same animation may execute its @cmd and @script code again.

Local Variables

Local variable Type Value
self Entity pointer Entity whose animation frame is being updated.
animnum Integer Current logical animation identifier, such as a named ANI_* constant. The same logical identifier may refer to different animation objects on different models.
frame Integer Current zero-based index shared by the animation's frame-specific property arrays.
animhandle Integer Unique runtime identifier of the concrete loaded animation object. OpenBOR uses this value to route generated @cmd and @script sections to the correct animation.

Use animnum for semantic comparisons against ANI_* constants. Use animhandle only when concrete animation identity matters. Runtime handles depend on loaded animation allocation and should not be hardcoded or stored as portable project data.

Assignments to event locals do not directly change the entity or animation. Use animation, entity, and frame-update interfaces for native changes.

The event does not provide attacker, damage, tag, blocked, or other combat-event locals. Read relevant entity or project state when animation behavior depends on prior combat context.

@cmd and @script Comparison

Feature @cmd @script
Scope Immediately following frame command Every frame update in the containing animation
Generated form Function call guarded by animation handle and frame index Inline code guarded by animation handle
Best use Short, reusable, precisely timed function calls Conditional logic evaluated throughout an animation
Function definitions Calls existing functions Cannot define functions inside the inserted block
Frame test Generated automatically Creator supplies a frame condition when needed

External helper functions keep complex logic reusable and testable, while @cmd provides compact timing in model data. Inline @script is useful when the decision itself needs to be evaluated at each animation update.

Return and Exit Behavior

Animationscript has no event acceptance or cancellation return value. Returning a value does not confirm a frame, while returning from generated or inline code only ends that script execution path.

Stopping script execution does not by itself stop native processing for the current frame. OpenBOR automatically interrupts the remaining old-frame operations when script changes the entity's animation or frame index. Other state changes affect later native instructions according to the rules of those individual systems.

Functions called through @cmd may return values for use by other script code, but the generated standalone call does not consume the returned value.

Model and Default-Model Execution

OpenBOR executes the Animationscript belonging to the entity's current model first. When model switching has made the current model different from the entity's original default model, OpenBOR also executes the default model's Animationscript afterward.

Both executions receive the same entity, logical animation ID, current frame index, and concrete animation handle. Generated animation-handle guards determine which model-specific @cmd and @script sections match the concrete animation.

This behavior is based on current and default model ownership, not on a special model name. Naming a model global_model does not automatically invoke it for every entity. Shared helpers may instead be included in participating animation-script sources.

Lifecycle functions such as oncreate() and ondestroy(), when present, follow script-instance creation and destruction. They do not run on every animation frame update.

Script Runtime cadence Primary use
animationscript When an animating entity enters or is moved through an indexed animation frame Precisely timed action logic and animation-state transitions.
Updatescript Entity update cadence Continuous entity behavior that should not depend on animation advancement.
Thinkscript Entity thinking cadence AI decisions and periodic behavior selection.
Ondrawscript Entity drawing cadence Visual preparation and draw-time behavior.
Onpainscript Native pain entry Reaction logic when an entity successfully enters standing pain.
Onfallscript Native fall entry Reaction logic when an entity successfully enters fall.

See Also