Jump to content

Move Blocking Events

From OpenBOR
Revision as of 19:50, 17 August 2026 by Dcurrent (talk | contribs)

The onblock*script family provides OpenBOR's movement-obstruction hooks. Each event runs when native movement resolution detects a particular boundary or blocking object for an entity.

These events are unrelated to guarding attacks. Combat blocks use Didblockscript and other combat hooks. Onblock scripts respond to physical movement constraints such as screen edges, playfield depth limits, level walls, platform sides, obstacle entities, and overhead platforms.

Together, the six hooks let creators build surface reactions, wall slides, ricochets, navigation responses, impact effects, environmental interactions, movement-state changes, boundary feedback, and diagnostics without replacing OpenBOR's complete movement solver.

OpenBOR uses X for horizontal position, Z for depth, and Y for altitude in its author-facing movement terminology. Each hook's suffix identifies either the relevant axis or the kind of object that blocked movement.

Event Family

Command Suffix meaning Trigger Event-specific locals
onblockyscript Altitude - Y axis Rising entity contacts the underside of an overhead platform. obstacle
onblockoscript Obstacle Lateral X or Z movement encounters an obstacle or trap entity. plane, obstacle
onblockpscript Platform Lateral X or Z movement encounters the side of a platform entity. plane, platform
onblocksscript Screen Horizontal movement reaches the current screen boundary. None
onblockwscript Wall Lateral X or Z movement encounters level wall geometry. plane, index, height, depth, type
onblockzscript Z axis - depth Depth movement reaches the level's minimum or maximum Z boundary. None

Every hook also receives self, the entity whose movement was obstructed.

The double s in onblocksscript is correct. One s identifies the screen variant, while the second begins the common script suffix.

Syntax

Each command belongs in a model definition and accepts an OpenBOR Script source path:

onblockyscript {path}
onblockoscript {path}
onblockpscript {path}
onblocksscript {path}
onblockwscript {path}
onblockzscript {path}

# Default
# No corresponding Onblock script
  • {path} - Path to an OpenBOR Script source file.
  • OpenBOR loads and compiles each supplied script with the model.
  • Every source file defines a normal main() entry point.
  • The script's return value is ignored.
  • Models may define any combination of the six hooks.

Example model header:

name example_character
type enemy

onblockyscript data/scripts/example_block_altitude.c
onblockoscript data/scripts/example_block_obstacle.c
onblockpscript data/scripts/example_block_platform.c
onblocksscript data/scripts/example_block_screen.c
onblockwscript data/scripts/example_block_wall.c
onblockzscript data/scripts/example_block_depth.c

Event code may also be embedded directly in the model:

onblockoscript @script
void main()
{
    void self = getlocalvar("self");
    void obstacle = getlocalvar("obstacle");
    int plane = getlocalvar("plane");
}
@end_script

Shared source may be assigned to several Onblock commands when a project uses common helper functions. Each command still creates a distinct script context and receives only the locals belonging to its own event.

Common Event Timing

Lateral Onblock events are part of native movement resolution. The relevant sequence is:

  1. OpenBOR receives proposed X and Z movement for self.
  2. Applicable boundary and collision checks inspect the proposed destination.
  3. Native resolution clamps a boundary movement or removes the component blocked by an object.
  4. OpenBOR populates the matching Onblock script's local variables.
  5. The Onblock script executes.
  6. Remaining collision categories inspect any movement components still available.
  7. Final movement validation runs.
  8. OpenBOR applies the surviving X and Z movement.
  9. Onmove scripts execute for axes that actually moved.

Object collisions remove only the blocked component. Diagonal movement blocked on X may retain Z movement, allowing the entity to slide along the surface. Screen and Z-boundary checks instead clamp movement to the applicable boundary.

The callback observes a detected obstruction, not a guaranteed final movement result. Later collision checks or final validation may still reject the remaining destination after an Onblock script has executed.

Collision checks occur in the following general order:

  1. Minimum and maximum Z boundaries.
  2. Current screen boundary.
  3. Holes.
  4. Obstacle and trap entities.
  5. Platform entities.
  6. Level wall geometry.
  7. Final combined-destination validation.

Processing stops early when no movement component remains. Later obstruction categories are not tested after both X and Z movement have been removed.

Onblockyscript

onblockyscript handles obstruction along the Y axis, OpenBOR's altitude axis. It is the overhead counterpart to lateral collision hooks.

The event runs when all relevant conditions are met:

  1. self is undergoing active altitude processing.
  2. self has a usable height.
  3. self is subject to platforms.
  4. self is moving upward.
  5. Native platform detection finds an entity overhead.
  6. The overhead entity does not allow upward pass-through with nohithead or the equivalent movement flag.
  7. self is not already latched to an overhead obstruction.

Before the callback, OpenBOR sets upward velocity to 0 and stores the overhead entity as self's current head obstruction. Native altitude movement and gravity processing continue after the callback.

The obstacle local points to the overhead platform entity. Its legacy name does not mean the target must have obstacle type.

Continuous contact invokes onblockyscript only once. OpenBOR retains the head-obstruction latch until the overhead entity is no longer detected, then allows a later contact to invoke the event again.

Compact ceiling-response example:

void main()
{
    void self = getlocalvar("self");
    void overhead = getlocalvar("obstacle");

    handle_ceiling_contact(self, overhead);
}

Project helper handle_ceiling_contact() could apply a bounce, start a wall-cling or ceiling-cling state, break a fragile platform, play an impact effect, or route the entity into another action.

Velocity changes made during the callback affect the altitude movement and gravity processing that follow. This allows the script to replace the native zeroed upward velocity with a project-defined rebound or other response.

Onblockoscript

onblockoscript runs when lateral movement encounters an entity of obstacle or trap type that is acting as a solid obstruction rather than platform geometry.

OpenBOR removes the blocked X or Z movement component before executing the script. The other component may remain available for sliding movement.

Local Type Value
self Entity pointer Entity whose movement encountered the obstruction.
plane Integer PLANE_X or PLANE_Z, identifying the blocked movement component.
obstacle Entity pointer Obstacle or trap entity detected by movement resolution.

Compact obstacle-response example:

void main()
{
    void self = getlocalvar("self");
    void obstacle = getlocalvar("obstacle");
    int plane = getlocalvar("plane");

    handle_obstacle_contact(self, obstacle, plane);
}

Diagonal contact with the same obstacle on both X and Z invokes the event once for that movement attempt. Contact with different obstacles on the two axes may invoke it once for each obstacle.

Falling contact may cause native ANI_HITOBSTACLE selection after the script returns when the entity supports that animation and has not already entered its native obstructed state.

Onblockpscript

onblockpscript runs when lateral movement encounters the side of an entity providing platform geometry. Landing on top of a platform is not this event, while hitting its underside during upward altitude movement belongs to onblockascript.

OpenBOR removes the blocked X or Z movement component before executing the script. The other component may remain available for sliding movement.

Local Type Value
self Entity pointer Entity whose movement encountered the platform.
plane Integer PLANE_X or PLANE_Z, identifying the blocked movement component.
platform Entity pointer Entity whose platform geometry blocked movement.

Compact platform-response example:

void main()
{
    void self = getlocalvar("self");
    void platform = getlocalvar("platform");
    int plane = getlocalvar("plane");

    handle_platform_side(self, platform, plane);
}

X and Z contact checks are independent. Diagonal movement may invoke onblockpscript twice, including two calls for the same platform when both components are blocked.

Falling contact may cause native ANI_HITPLATFORM selection after the script returns when the entity supports that animation and has not already entered its native obstructed state.

Onblocksscript

onblocksscript runs when horizontal movement would carry self beyond the current screen boundary and the entity is subject to screen limits.

The event receives only self. No local identifies the left or right boundary. Projects may infer the side from entity position, facing, velocity, input, or their own movement state.

OpenBOR adjusts the proposed horizontal movement so the entity reaches the boundary rather than discarding the complete X component. Remaining collision checks still run before the adjusted movement is committed.

Compact screen-boundary example:

void main()
{
    void self = getlocalvar("self");

    handle_screen_boundary(self);
}

Potential uses include camera-edge warnings, AI redirection, off-screen entrance control, player tethering, invisible-boundary effects, and developer visualization.

Onblockwscript

onblockwscript runs when lateral movement encounters wall geometry defined by the current level. Walls are level terrain records rather than entities, so the event supplies copied wall metadata and its collection index instead of an entity pointer.

OpenBOR removes the blocked X or Z movement component before executing the script. The other component may remain available for sliding movement.

Local Type Value
self Entity pointer Entity whose movement encountered the wall.
plane Integer PLANE_X or PLANE_Z, identifying the blocked movement component.
index Integer Wall's zero-based index in the current level wall collection.
height Decimal Wall height.
depth Decimal Wall depth.
type Integer Creator-defined wall type value.

Compact material-routing example:

void main()
{
    void self = getlocalvar("self");
    int plane = getlocalvar("plane");
    int index = getlocalvar("index");
    int type = getlocalvar("type");
    float height = getlocalvar("height");
    float depth = getlocalvar("depth");

    handle_wall_contact(self, index, type, plane, height, depth);
}

Wall type and index support material effects, breakable-wall registries, switches, climbing rules, scripted doors, hazards, wall-specific sounds, and encounter logic.

X and Z contact checks are independent. Diagonal movement may invoke onblockwscript twice, including two calls for the same wall when both components are blocked.

Falling contact may cause native ANI_HITWALL selection after the script returns when wall type qualifies, the entity supports that animation, and the entity has not already entered its native obstructed state.

Onblockzscript

onblockzscript runs when depth movement would carry self beyond the level's minimum or maximum Z boundary and the entity is subject to the applicable limit.

This event represents the playfield's depth boundary, not collision with an object located on the Z axis. Obstacle, platform, and wall collisions along Z invoke their object-specific hooks with plane set to PLANE_Z.

The event receives only self. No local identifies the minimum or maximum boundary. Projects may infer the side from entity position, velocity, input, or their own movement state.

OpenBOR adjusts the proposed depth movement so the entity reaches the applicable boundary rather than discarding the complete Z component. Remaining collision checks still run before the adjusted movement is committed.

Compact depth-boundary example:

void main()
{
    void self = getlocalvar("self");

    handle_depth_boundary(self);
}

Potential uses include lane-limit feedback, AI redirection, arena-edge effects, custom depth wrapping, movement-state transitions, and boundary diagnostics.

Plane Constants

onblockoscript, onblockpscript, and onblockwscript supply a plane local. Compare it with named constants rather than raw integers:

void main()
{
    int plane = getlocalvar("plane");

    if(plane == openborconstant("PLANE_X"))
    {
        handle_horizontal_block();
    }
    else if(plane == openborconstant("PLANE_Z"))
    {
        handle_depth_block();
    }
}

Current lateral obstruction events do not emit PLANE_Y. OpenBOR's author-facing altitude counterpart is handled separately by onblockyscript.

Native Response and Script Control

Onblock scripts observe native obstruction resolution but do not return a replacement movement result.

Hook Native state before script Native processing after script
onblockyscript Upward velocity is set to 0; overhead-obstruction handle is stored. Altitude movement, gravity, velocity limits, and later gravity processing continue.
onblockoscript Blocked X or Z movement component is set to 0. Remaining collision checks continue; native hit-obstacle animation may be selected.
onblockpscript Blocked X or Z movement component is set to 0. Remaining collision checks continue; native hit-platform animation may be selected.
onblocksscript X movement is clamped to the screen boundary. Remaining collision checks and final movement validation continue.
onblockwscript Blocked X or Z movement component is set to 0. Remaining collision checks continue; native hit-wall animation may be selected.
onblockzscript Z movement is clamped to the minimum or maximum boundary. Remaining collision checks and final movement validation continue.

Returning 0 does not cancel the obstruction, while returning 1 does not authorize the original movement. The movement components used by native resolution are internal values and are not supplied as writable locals.

Scripts may still alter entity state through property functions. Useful responses include changing velocity, position, animation, action state, movement flags, AI targets, variables, or blocker properties. Changes should account for the native work that follows the callback.

Native falling-hit animation selection occurs after the obstacle, platform, or wall callback. Scripts that select their own collision animation may have it replaced unless project logic also prevents the applicable native selection condition.

Numeric locals such as plane, index, height, depth, and type are copied values. Assigning a new value to one of these locals does not modify the movement result or level wall. Entity-pointer locals reference live entities, so property changes made through those handles affect the corresponding object.

Trigger Scope and Frequency

Onblock events are movement-resolution callbacks rather than universal collision observers.

Situation Onblock execution Notes
Native movement attempt through the entity's movement function Yes, when the matching enabled check detects an obstruction. Normal walking, AI movement, animation movement, platform-carried movement, and other routes using the native movement function may qualify.
Repeated movement attempts into the same lateral object or boundary Repeats Lateral callbacks may execute on every qualifying movement attempt.
Continuous overhead contact Once until contact clears onblockyscript uses an overhead-obstruction latch.
Diagonal movement blocked on both axes May execute more than once Platform and wall checks are axis-specific; obstacle logic suppresses a duplicate when the same obstacle blocked both axes.
Entity not subject to the relevant boundary or object class No Matching movement flags must enable the native collision check.
Direct position assignment or teleport No Raw position changes do not pass through movement obstruction resolution.
Standalone movement test or pathfinding query No Tests report availability without synthesizing Onblock events.
Landing on a platform No lateral Onblock event Landing uses altitude and platform-landing processing.
Hitting a platform underside while rising onblockyscript Requires platform subjectivity, usable entity height, and a solid overhead target.
Attack is guarded No Combat blocking belongs to Didblockscript.

Final combined-destination validation can reject movement without identifying one specific Onblock category. Consequently, not every failed movement attempt invokes an Onblock script.

Practical Patterns

Pattern Use of the Onblock family
Surface-specific impact feedback Choose sounds, particles, sparks, dust, screen effects, or controller feedback from blocker class, wall type, and collision plane.
Ricochet and rebound systems Reverse or reshape X, Z, or Y velocity for projectiles, knockback, pinball movement, vehicles, and arena hazards.
Wall slides, climbs, and clings Detect lateral wall or platform contact, verify entity state, then enter climbing, hanging, sliding, or wall-jump logic.
Breakable and interactive terrain Use wall index, wall type, platform handle, or obstacle handle to damage surfaces, activate switches, open routes, or notify a level controller.
AI navigation response Record blocked axes, redirect goals, request another path, reverse movement, or mark temporarily unreachable destinations.
Arena and camera boundaries Use screen and Z-boundary callbacks for warnings, tethers, lane effects, camera feedback, or project-specific wrapping rules.
Movement-state transitions Route dashes, charges, rolls, knockback, flight, swimming, mounts, or scripted vehicles into contact-specific follow-up actions.
Cooperative interactions Let one entity react to a moving platform, pushable obstacle, shared wall mechanism, or another player's environment change.
Diagnostics and level tools Display collision plane, blocker identity, wall index, type, dimensions, or boundary contacts while tuning movement geometry.

Common Mistakes

Mistake Result Better approach
Treating Onblock as attack guarding Scripts never run for an ordinary guarded hit. Use Didblockscript for combat defense.
Returning a value to restore movement Native movement ignores the script return value. Change entity state explicitly or design a later movement response.
Assigning a new value to plane The copied local changes, while native movement remains unchanged. Modify entity properties or project state through their proper interfaces.
Expecting one callback per sustained lateral contact Movement attempts can invoke the event repeatedly. Add a project latch or cooldown when only contact entry should count.
Expecting repeated onblockyscript calls during one ceiling contact The overhead latch suppresses repeats until separation. Treat it as contact entry or track continued contact through entity state.
Assuming onblockpscript means landing Side collision invokes the platform hook; landing does not. Use platform-landing state or another appropriate event for landing logic.
Assuming onblockzscript means any Z collision Only the playfield's minimum or maximum Z boundary invokes it. Use the object-specific hook and PLANE_Z for obstacle, platform, or wall collisions.
Selecting a custom falling-impact animation without accounting for native follow-up Native hit-obstacle, hit-platform, or hit-wall selection may replace it after the callback. Prevent the native condition or apply the custom transition from a later controlled state.
Expecting direct teleports to emit obstruction events Raw position changes bypass the movement solver. Test the destination explicitly or route movement through an appropriate movement function.

See Also