Skip to content

Callbacks

Alexandre Mondaini Calvão edited this page Dec 3, 2025 · 1 revision

Callbacks

The PathfindingEngine supports callbacks to notify your game when path-related events occur.

Available Callbacks

Callback Triggered When
on_node_reached Entity arrives at any waypoint along its path
on_path_completed Entity reaches final destination
on_path_blocked No path available to requested destination

Setting Up Callbacks

const Config = struct {
    pub const Entity = u64;
    pub const Context = *Game;
};

const Engine = pathfinding.PathfindingEngine(Config);

fn onNodeReached(game: *Game, entity: u64, node: u32) void {
    // Called each time entity reaches a waypoint
}

fn onPathCompleted(game: *Game, entity: u64, node: u32) void {
    // Called when entity reaches final destination
    game.score += 10;
}

fn onPathBlocked(game: *Game, entity: u64, node: u32) void {
    // Called when no path exists
    game.showMessage("Can't reach destination!");
}

pub fn main() !void {
    var engine = try Engine.init(allocator);
    
    // Assign callbacks
    engine.on_node_reached = onNodeReached;
    engine.on_path_completed = onPathCompleted;
    engine.on_path_blocked = onPathBlocked;
    
    // ...
}

Callback Signature

All callbacks have the same signature:

fn callback(context: Context, entity: Entity, node: NodeId) void
  • context - Your game context (from Config.Context)
  • entity - The entity that triggered the event
  • node - The relevant node ID

When Callbacks Fire

Callbacks are fired during tick(), after all entity positions are updated:

engine.tick(&game, delta);
// ^ Callbacks fire here, at the end of tick

This ensures:

  • All positions are consistent when callbacks run
  • Callbacks can safely query other entities
  • No iterator invalidation issues

Example: Patrol System

const PatrolState = struct {
    current_target: usize = 0,
    patrol_points: []const u32,
};

fn onPatrolNodeReached(game: *Game, entity: u64, node: u32) void {
    const patrol = game.getPatrol(entity) orelse return;
    
    // Move to next patrol point
    patrol.current_target = (patrol.current_target + 1) % patrol.patrol_points.len;
    const next = patrol.patrol_points[patrol.current_target];
    
    game.engine.requestPath(entity, next) catch {};
}

Example: Quest Trigger

fn onNodeReached(game: *Game, entity: u64, node: u32) void {
    // Check if this node has a quest trigger
    if (game.quest_triggers.get(node)) |quest_id| {
        if (game.isPlayer(entity)) {
            game.triggerQuest(quest_id);
        }
    }
}

Example: Score System

fn onPathCompleted(game: *Game, entity: u64, node: u32) void {
    const entity_type = game.getEntityType(entity);
    
    switch (entity_type) {
        .player => {
            game.score += 10;
            game.playSound(.destination_reached);
        },
        .enemy => {
            // Enemy reached its target
            game.alertPlayer();
        },
        else => {},
    }
}

Optional Callbacks

All callbacks are optional. If not set, nothing happens:

var engine = try Engine.init(allocator);
// No callbacks set - events are silently ignored

Callback Safety

Callbacks run after the main update loop, so it's safe to:

  • Query entity positions
  • Request new paths
  • Modify game state

However, avoid:

  • Calling tick() recursively
  • Removing the entity that triggered the callback (defer to next frame)

Clone this wiki locally