wand-magic-sparklesEffects

Effects temporarily take control of an entity for complex behaviors like dashes, attacks, eating animations, cutscenes, or death/respawn sequences.

Effects can be used on players, NPCs, or any entity. They’re especially useful when you want:

  • Movement you control in code (dash/knockback/roll)

  • “Lock out” player input for a short sequence (eat, revive, cutscene)

  • A timed window of state (invincibility, slow, stun)

  • UI that is tied to a temporary state (respawn countdown)

Active vs passive

All Out supports two kinds of effects:

  • Active effects: one at a time per entity. Setting a new active effect interrupts the current active effect.

  • Passive effects: multiple can be attached at once (stackable buffs/debuffs/status effects).

circle-info

An active effect is also inserted into the entity’s effect list, so it shows up in effect_iterator just like passive effects.

Quick start (a simple timed effect)

Create a class that inherits from Effect_Base, then attach it to an entity.

Apple_Pop_Effect :: class : Effect_Base {
    sprite: Sprite_Renderer;
    start_scale: v2;
    start_pos: v2;

    effect_start :: method() {
        sprite = entity->get_component(Sprite_Renderer);
        start_scale = entity.local_scale;
        start_pos = entity.local_position;
        set_duration(0.4); // auto-removes after 0.4s
    }

    effect_update :: method(dt: float) {
        t := get_elapsed_time() / 0.4;

        // Scale up then shrink to nothing
        scale_curve: float;
        if t < 0.3 {
            scale_curve = lerp(1.0, 1.3, Ease.out_back(t / 0.3));
        } else {
            scale_curve = lerp(1.3, 0.0, Ease.in_back((t - 0.3) / 0.7));
        }

        entity->set_local_scale(start_scale * scale_curve);

        // Float upward slightly
        rise := Ease.out_quad(t) * 0.5;
        entity->set_local_position({start_pos.x, start_pos.y + rise});

        // Fade out near the end
        if sprite != null {
            alpha := 1.0 - Ease.in_quad(max(0.0, (t - 0.5) / 0.5));
            sprite.color.w = alpha;
        }
    }

    effect_end :: method(interrupt: bool) {
        // Clean up when done (or interrupted)
        entity->destroy();
    }
}

Attach it as an active effect:

Effect lifecycle

Effects are callback-based. Implement only what you need:

  • effect_start(): called once when the effect is attached

  • effect_update(dt): called every frame

  • effect_late_update(dt): called every frame after effect_update

  • effect_end(interrupt): called when the effect is removed

Active effects (exclusive control)

Use set_active_effect for effects that should be mutually exclusive (dash, attack windup, death sequence).

If the entity already has an active effect, it is ended with interrupt = true.

Passive effects (stackable buffs / status)

Use add_passive_effect for effects that can stack (slow, poison, shield, invincibility window).

API reference

Effect_Base

Applying / removing effects

Checking / iterating effects

circle-exclamation

freeze_player vs disable_movement_inputs

  • player_specific.freeze_player = true: locks player position entirely.

  • player_specific.disable_movement_inputs = true: ignores input, but your effect can still move the player (dash/roll).

Both only apply when player != null (i.e. the effect is on an entity that has a Player component).

Effect examples

Dash / roll (active effect)

Invincibility window (passive effect)

Use a passive effect to represent the invincible state, and check for it wherever you apply damage.

2x size for a few seconds (passive effect)

Death / respawn sequence (active effect with local UI)

Cutscene lock (active effect)

Best practices

  • Use set_active_effect when the effect “owns” the entity for a short sequence (dash/attack/death/cutscene).

  • Use add_passive_effect for stackable state (buffs/debuffs/status).

  • Use set_duration(...) for timed effects instead of manual timers.

  • Always restore any modified state in effect_end (friction, scale, animation triggers, etc).

  • Keep purely cosmetic UI/effects local (wrap in player->is_local()), and keep gameplay state server-authoritative.

Last updated