Combat And Defense Examples

These scripts cover defensive mechanics, weapon-dependent move redirects, break specials, parry windows, and hit-pause reactions.

Direction Switch During Attacks

This pattern lets a character face the held direction while attacking, and clears stale victim/attacker links so old hit relationships do not persist after turning around.

local function clear_victim_information(character)
    local victim = character.victim

    if victim ~= nil then
        victim.attacker_character_id = -1
    end

    character.attacked_character_id = -1
end

return function()
    local character = game.this_character

    if character == nil or not character.is_player then
        return true
    end

    if character.is_holding_right and not character.is_facing_right then
        character.is_facing_right = true
        clear_victim_information(character)
    elseif character.is_holding_left and character.is_facing_right then
        character.is_facing_right = false
        clear_victim_information(character)
    end

    return true
end

Blocking With Hit Absorption

Attach this to a block animation. Throws still beat the block; strikes are absorbed and converted into attacker hit pause.

return function()
    local character = game.this_character

    if character == nil then
        return true
    end

    character.is_blocking = true
    character.is_knocked_down = false

    -- Grabs are checked before strike absorption so throws can beat blocking.
    if character.check_being_grabbed() then
        return true
    end

    local was_being_hit = character.is_being_hit
    character.is_being_hit = false

    if was_being_hit then
        local attacker = character.attacker

        if attacker ~= nil then
            attacker.hit_pause_timer = attacker.hit_pause_timer + 20
            attacker.custom_hit_pause_delay_frames =
                attacker.custom_hit_pause_delay_frames + 20

            character.custom_hit_pause_delay_frames =
                attacker.custom_hit_pause_delay_frames - 10
        end

        character.is_facing_right = character.was_hit_from_right
        character.damage_taken = 0

        if character.current_animation_frame < 5 then
            character.current_animation_frame = 5
        end
    end

    if not character.is_animation_over then
        return false
    end

    character.is_blocking = false
    character.impulse_x = 0
    character.velocity_x = 0
    character.inner_timer = 0
    character.set_animation(0)
    character.object_mode = 2

    return false
end

Enter Block From Being Hit

Attach this to a being-hit animation. If the player holds a guard input before the normal hit reaction continues, the script resets to standing so normal back-attack or block checks can run.

return function()
    local player = game.this_player

    if player == nil then
        return true
    end

    local guard_input =
        player.is_holding_button_x or
        (player.is_holding_button_b and player.is_holding_button_c)

    if guard_input and not player.hold_x_button_consumed then
        player.set_animation(0)
        player.object_mode = 2
    end

    return true
end

Predictive Parry

Use predict_frame_being_hit to enter a parry/repel animation only when an incoming hit is close enough.

return function()
    local player = game.this_player

    if player == nil then
        return true
    end

    if player.custom_value_3 > 0 then
        player.custom_value_3 = player.custom_value_3 - 1
        return true
    end

    if not player.pressed_forward then
        return true
    end

    local hit_frame = player.predict_frame_being_hit(14)

    if hit_frame > 3 then
        player.set_animation(41)
        player.has_double_forward_tap = false
        return false
    end

    -- Short cooldown for failed attempts.
    player.custom_value_3 = 10
    return true
end

Repel On Hit Pause

This aerial counter watches for hit pause. If the attack connects and freezes the move, the script changes into a recoil/repel animation and pushes the character away.

return function()
    local character = game.this_character

    if character == nil or not character.is_player or not character.is_in_the_air then
        return true
    end

    -- Hit pause greater than 1 means the active hit connected recently.
    if character.hit_pause_timer > 1 then
        character.set_animation(54)
        character.impulse_x = -65 * character.direction_signal
        character.impulse_y = -262
        return false
    end

    character.impulse_y = character.impulse_y - 13
    character.position_offset_y = character.position_offset_y + character.impulse_y
    character.position_x = character.position_x + character.impulse_x

    return false
end

Weapon-Specific Move Redirect

Use this on a blitz or special animation to redirect to a weapon-specific animation when the player holds a specific weapon. For the complete Character Editor workflow, including where to create and attach the script, see Play a Different Animation for a Held Weapon.

return function()
    local character = game.this_character

    if character == nil then
        return true
    end

    local weapon = character.weapon

    if weapon == nil then
        return true
    end

    if weapon.pack_name == "Classic" and weapon.name == "Knife" then
        character.set_animation(41)
        return false
    end

    return true
end

Replace 41 with the target animation's editor list position. Entity names and pack names are case-sensitive. Comparing names avoids a magic numeric ID and works for custom weapons as well as original ones.

Defensive Break Special

This lets the player spend health to break out with a special. It flips direction if the player is holding the opposite side.

local function flip_to_input_direction(character)
    if character.is_holding_right and not character.is_facing_right then
        character.impulse_x = -character.impulse_x
        character.is_facing_right = true
    elseif character.is_holding_left and character.is_facing_right then
        character.impulse_x = -character.impulse_x
        character.is_facing_right = false
    end
end

return function()
    local player = game.this_player

    if player == nil then
        return true
    end

    if player.health > 8 and player.pressed_button_a then
        if player.is_holding_left or player.is_holding_right then
            flip_to_input_direction(player)
            player.set_animation(37)
            player.object_mode = 40
        else
            player.set_animation(35)
            player.object_mode = 36
        end
    end

    if player.back_attack_input_pressed then
        player.hold_x_button_consumed = true
        flip_to_input_direction(player)
        player.set_animation(38)
        player.object_mode = 20
    end

    return true
end

Aerial Special With Health Cost

This is a full controller replacement: vulnerability window, hit checks, hit pause, custom movement, health cost, and return to standing.

local function face_input_direction(character)
    if character.is_holding_right then
        character.is_facing_right = true
    elseif character.is_holding_left then
        character.is_facing_right = false
    end
end

return function()
    local character = game.this_character

    if character == nil then
        return true
    end

    local vulnerable_now = character.current_animation_frame > 33
    character.is_vulnerable = vulnerable_now

    if vulnerable_now and character.check_being_hit() then
        return false
    end

    if character.hit_pause_timer > 0 then
        character.hit_pause_timer = character.hit_pause_timer - 1
        return false
    end

    if character.current_animation_frame == 0 then
        face_input_direction(character)
        character.impulse_x = 300
        character.impulse_y = 200
        character.is_jump_down_attack = false
        character.is_jump_attack = false
    elseif character.current_animation_frame == 24 then
        face_input_direction(character)
        character.impulse_x = 400
    end

    if character.is_animation_over then
        character.health = character.health - 8
        character.acceleration_x = 0
        character.acceleration_y = 0
        character.acceleration_z = 0
        character.impulse_x = 0
        character.impulse_y = 0
        character.impulse_z = 0
        character.is_vulnerable = true
        character.set_animation(0)
        character.object_mode = 2
        return false
    end

    if not character.is_in_the_air and character.impulse_x > 0 then
        local ground_acceleration_x = -20
        if character.current_animation_frame > 24 then
            ground_acceleration_x = -30
        end

        character.impulse_x = character.impulse_x + ground_acceleration_x
        if character.impulse_x < 0 then
            character.impulse_x = 0
        end
    end

    character.check_hitting_entities()

    character.velocity_x =
        character.velocity_x + character.impulse_x * character.direction_signal

    if character.position_offset_y < 0 then
        character.velocity_y = character.velocity_y + character.impulse_z
        character.position_offset_y =
            character.position_offset_y + character.impulse_y
        character.impulse_y = character.impulse_y + 32

        if character.position_offset_y >= 0 then
            character.position_offset_y = 0
        end
    end

    return false
end