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