From a81b080cc830d3073fda40ec777ab24f72cacfb9 Mon Sep 17 00:00:00 2001 From: Elizabeth Hunt Date: Sat, 25 Apr 2026 20:42:30 -0700 Subject: Getting a sword slashing animation working --- dyl.lua | 248 +++++++++++++++++++++++++++++++++++++++++-------------------- dyl.p8 | 26 +++---- entity.lua | 24 ++++-- util.lua | 116 ++++++++++++++++++----------- world.lua | 27 ++++++- 5 files changed, 297 insertions(+), 144 deletions(-) diff --git a/dyl.lua b/dyl.lua index 6c2a012..6a337cd 100644 --- a/dyl.lua +++ b/dyl.lua @@ -1,64 +1,139 @@ +_slashing_timer_sec = 0.12 +function spawn_slashing_particle(e) + slash_animation = entity() + _slash_animation_distance = 3 + function slash_animation:transition_state() + -- TODO: Find a better place to put this lol. This has nothing to do with state + -- transitions + slash_animation:equipped_from(e, _slash_animation_distance) + end + return slash_animation:b_type(Entities.Enemy) + :b_line_of_sight(e.line_of_sight) + :b_position(vec2(e.position)) + :b_sprite_position(vec2(position)) + :b_render_order(-1) + :b_live_for(_slashing_timer_sec) + :b_add_state( + States.Active, { + animation = { + pos_y = { sequence = { 115, 116, 117 }, dt = _slashing_timer_sec / 3, reflect = vec2(true, true) }, + neg_y = { sequence = { 115, 116, 117 }, dt = _slashing_timer_sec / 3 }, + pos_x = { sequence = { 112, 113, 114 }, dt = _slashing_timer_sec / 3 }, + neg_x = { sequence = { 112, 113, 114 }, dt = _slashing_timer_sec / 3, reflect = vec2(true, false) } + } + } + ) + :b_state(States.Active) + :build() +end + +enemy = entity() +function enemy:transition_state() +end +enemy:b_type(Entities.Enemy) + :b_position(vec2(30, 40)) + :b_sprite_position(vec2(30, 30)) + :b_line_of_sight(vec2(1, 0)) + :b_render_order(1) + :b_collidable() + :b_add_state( + States.Idle, { + animation = { + pos_y = { sequence = { 32, 33 }, dt = 1 }, + neg_y = { sequence = { 32, 33 }, dt = 1 }, + pos_x = { sequence = { 32, 33 }, dt = 1 }, + neg_x = { sequence = { 32, 33 }, dt = 1, reflect = vec2(true, false) } + } + } + ) + :b_add_state( + States.Walk, { + animation = { + pos_x = { sequence = { 32, 34 }, dt = 0.20 }, + neg_x = { sequence = { 32, 34 }, dt = 0.20, reflect = vec2(true, false) }, + pos_y = { sequence = { 32, 34 }, dt = 0.20 }, + neg_y = { sequence = { 32, 34 }, dt = 0.20 } + } + } + ) + :b_state(States.Walk) + :b_equipped({ sword }) + :build() + + bow = entity() function bow:transition_state() end -bow - :b_type(Entities.Bow) +bow:b_type(Entities.Bow) :b_position(vec2(0, 0)) :b_sprite_position(vec2(0, 0)) :b_line_of_sight(vec2(1, 0)) :b_render_order(1) :b_collidable() - :b_add_state(States.Equipped, { - animation={ - pos_y = { sequence = { 96 }, dt = 1, reflect = vec2(false, true) }, - neg_y = { sequence = { 96 }, dt = 1 }, - pos_x = { sequence = { 80 }, dt = 1 }, - neg_x = { sequence = { 80 }, dt = 1, reflect = vec2(true, false) } - } - }) - :b_add_state(States.Drawn, { - animation={ - pos_y = { sequence = { 81 }, dt = 1, reflect = vec2(false, true) }, - neg_y = { sequence = { 81 }, dt = 1 }, - pos_x = { sequence = { 81 }, dt = 1 }, - neg_x = { sequence = { 81 }, dt = 1, reflect = vec2(true, false) } - } - }) - :b_add_state(States.Drawing, { - animation = bow.states[States.Equipped] - }) + :b_add_state( + States.Equipped, { + animation = { + pos_y = { sequence = { 96 }, dt = 1, reflect = vec2(false, true) }, + neg_y = { sequence = { 96 }, dt = 1 }, + pos_x = { sequence = { 80 }, dt = 1 }, + neg_x = { sequence = { 80 }, dt = 1, reflect = vec2(true, false) } + } + } + ) + :b_add_state( + States.Drawn, { + animation = { + pos_y = { sequence = { 81 }, dt = 1, reflect = vec2(false, true) }, + neg_y = { sequence = { 81 }, dt = 1 }, + pos_x = { sequence = { 81 }, dt = 1 }, + neg_x = { sequence = { 81 }, dt = 1, reflect = vec2(true, false) } + } + } + ) + :b_add_state( + States.Drawing, { + animation = bow.states[States.Equipped] + } + ) :b_state(States.Equipped) :build() sword = entity() -_slashing_timer_ms = 500 function sword:transition_state() - if(btn(5)) then self:transition_to(States.Slashing) - elseif self.state == States.Slashing and self.state_stopwatch > _slashing_timer_ms then self:transition_to(States.Equipped) end + if button_just_pressed(5) and self.state == States.Equipped then + self:transition_to(States.Slashing) + spawn_slashing_particle(self) + elseif self.state == States.Slashing and self.state_stopwatch > _slashing_timer_sec then + self:transition_to(States.Equipped) + end end sword :b_type(Entities.Sword) :b_line_of_sight(vec2(1, 0)) :b_position(vec2(0, 0)) :b_sprite_position(vec2(0, 0)) - :b_render_order(1) + :b_render_order(0) :b_collidable() - :b_add_state(States.Equipped, { - animation={ - pos_y = { sequence = { 82 }, dt = 1, reflect = vec2(false, true) }, - neg_y = { sequence = { 82 }, dt = 1, reflect = vec2(true, false)}, - pos_x = { sequence = { 82 }, dt = 1 }, - neg_x = { sequence = { 82 }, dt = 1, reflect = vec2(true, false) } - } - }) - :b_add_state(States.Slashing, { - animation={ - pos_y = { sequence = { 82, 82+16 }, dt = 1, reflect = vec2(false, true) }, - neg_y = { sequence = { 82, 82+16 }, dt = 1, reflect = vec2(true, false)}, - pos_x = { sequence = { 82, 82+16 }, dt = 1 }, - neg_x = { sequence = { 82, 82+16 }, dt = 1, reflect = vec2(true, false) } - } - }) + :b_add_state( + States.Equipped, { + animation = { + pos_y = { sequence = { 99 }, dt = 1, reflect = vec2(false, true) }, + neg_y = { sequence = { 99 }, dt = 1, reflect = vec2(true, false) }, + pos_x = { sequence = { 98 }, dt = 1 }, + neg_x = { sequence = { 98 }, dt = 1, reflect = vec2(true, false) } + } + } + ) + :b_add_state( + States.Slashing, { + animation = { + pos_y = { sequence = { 82, 98 }, dt = _slashing_timer_sec / 2, reflect = vec2(false, true) }, + neg_y = { sequence = { 82, 98 }, dt = _slashing_timer_sec / 2, reflect = vec2(true, false) }, + pos_x = { sequence = { 82, 99 }, dt = _slashing_timer_sec / 2, reflect = vec2(false, false)}, + neg_x = { sequence = { 82, 99 }, dt = _slashing_timer_sec / 2, reflect = vec2(true, false) } + } + } + ) :b_state(States.Equipped) :build() @@ -70,61 +145,74 @@ function player:transition_state() self:transition_to(States.Walk) end end -player:b_type(Entities.Player) +player + :b_type(Entities.Player) :b_line_of_sight(vec2(1, 0)) - :b_render_order(0) + :b_render_order(2) :b_collidable() - :b_position(vec2(30,30)) - :b_sprite_position(vec2(30,30)) - :b_add_state(States.Idle, { - animation = { - pos_y = { sequence = { 3, 19 }, dt = 1 }, - neg_y = { sequence = { 6, 22 }, dt = 1 }, - pos_x = { sequence = { 0, 1 }, dt = 1 }, - neg_x = { sequence = { 0, 1 }, dt = 1, reflect = vec2(true, false) } - } - }) - :b_add_state(States.Walk, { - animation = { - pos_x = { sequence = { 2, 0 }, dt = 0.20 }, - neg_x = { sequence = { 2, 0 }, dt = 0.20, reflect = vec2(true, false) }, - pos_y = { sequence = { 4, 5 }, dt = 0.20 }, - neg_y = { sequence = { 7, 8 }, dt = 0.20 } - } - }) - :b_state("idle") + :b_position(vec2(30, 30)) + :b_sprite_position(vec2(30, 30)) + :b_add_state( + States.Idle, { + animation = { + pos_y = { sequence = { 3, 19 }, dt = 1 }, + neg_y = { sequence = { 6, 22 }, dt = 1 }, + pos_x = { sequence = { 0, 1 }, dt = 1 }, + neg_x = { sequence = { 0, 1 }, dt = 1, reflect = vec2(true, false) } + } + } + ) + :b_add_state( + States.Walk, { + animation = { + pos_x = { sequence = { 2, 0 }, dt = 0.20 }, + neg_x = { sequence = { 2, 0 }, dt = 0.20, reflect = vec2(true, false) }, + pos_y = { sequence = { 4, 5 }, dt = 0.20 }, + neg_y = { sequence = { 7, 8 }, dt = 0.20 } + } + } + ) + :b_state(States.Idle) :b_equipped({ sword }) :build() _walk_speed = 35 -- powerup increase? function handle_input() - dpos = vec2(0, 0) - if btn(0) then dpos.x -= 1 end - if btn(1) then dpos.x += 1 end - if btn(3) then dpos.y += 1 end - if btn(2) then dpos.y -= 1 end + dpos = vec2(0, 0) + if btn(0) then dpos.x -= 1 end + if btn(1) then dpos.x += 1 end + if btn(3) then dpos.y += 1 end + if btn(2) then dpos.y -= 1 end - player.velocity = dpos:normal() * _walk_speed + player.velocity = dpos:normal() * _walk_speed end function _init() end -step_t = time() -function _update60() - cls(0) -- clear here in case we want to print to screen outside of _draw +_step_t = time() +function _update() + -- clear here in case we want to print to screen outside of _draw + cls(0) + + old_step = _step_t + _step_t = time() + step_dt = _step_t - old_step - old_step = step_t - step_t = time() - step_dt = step_t - old_step + foreach(update_hooks, function(f) f(step_dt) end) - handle_input() - qsort(World, function(a, b) return a.equipped != nil end) - foreach(World, function (e) e:update(step_dt) end) + handle_input() + World.foreach(function(e) if (not e.equipped) then e:update(step_dt) end end) + World.foreach(function(e) if (e.equipped) then e:update(step_dt) end end) - run_collisions() + run_collisions() + + World.cull_the_dead() end function _draw() - foreach(qsort(World, function(a, b) return a.render_order < b.render_order end), function (e) e:render() end) -end + qsort(World, function(a, b) return b.render_order < a.render_order end) + World.foreach(function(e) + e:render() + end) +end \ No newline at end of file diff --git a/dyl.p8 b/dyl.p8 index 0367033..423b331 100644 --- a/dyl.p8 +++ b/dyl.p8 @@ -61,21 +61,21 @@ __gfx__ 00504000005040000049000000490000006500000055500000500000005000000000000000000000000000000000000000000000000000000000000000000000 00040000000400000400000004000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000000000004000000000000800000000000050000000600000000000000000000000000000000000000000000000000000000000000000000 -00000000000900000000000000490000000008e00006500000050000000500000000000000000000000000000000000000000000000000000000000000000000 -0049440000474400009000000097000000008e000055550000050000000500000000000000000000000000000000000000000000000000000000000000000000 -040000400407004004477770000570000058e0000055650000955000009550000000000000000000000000000000000000000000000000000000000000000000 -00555500005705000090000000005700006500000005050000055500000555000000000000000000000000000000000000000000000000000000000000000000 -00000000000550000000000000000570060000000000000000500000000500000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000070000000000000000000800000000000050000000600000000000000000000000000000000000000000000000000000000000000000000 +00000000000900000070000000000000000008e00006500000050000000500000000000000000000000000000000000000000000000000000000000000000000 +0049440000474400007000000000000000008e000055550000050000000500000000000000000000000000000000000000000000000000000000000000000000 +040000400407004009490000000900000058e0000055650000955000009550000000000000000000000000000000000000000000000000000000000000000000 +00555500005705000040000000447777006500000005050000055500000555000000000000000000000000000000000000000000000000000000000000000000 +00000000000550000000000000090000060000000000000000500000000500000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00056000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000005650600000000000000000000005006500000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000560000000000000000005606056600650000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000060000000000000000650005665000660000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000650000005605000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -00000000000000000056600000000000000006000050000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00056000000600000000000000000000006000000000050000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000005650600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000560000000000005600000005665000000066000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000060000000000050000000066506060000056000000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000650000005605000000000005000000000005600000000000000000000000000000000000000000000000000000000000000000000000000000000 +00000000000000000056600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000600000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 diff --git a/entity.lua b/entity.lua index 945b787..56dcd74 100644 --- a/entity.lua +++ b/entity.lua @@ -2,11 +2,13 @@ Entities = { Player = 0, Sword = 1, Enemy = 2, - Bow = 3 + Bow = 3, + Particle = 4 } States = { Walk = "walk", Idle = "idle", + Active = "active", Equipped = "equipped", Slashing = "slashing", Drawing = "drawing", @@ -51,6 +53,10 @@ function Entity:b_collidable() self.collision = true return self end +function Entity:b_live_for(t) + self.life_time = t + return self +end function Entity:b_line_of_sight(vec) self.line_of_sight = vec2(vec) return self @@ -69,10 +75,13 @@ function Entity:transition_to(state_name) self.state = state_name self.state_stopwatch = 0 end - print(self.state) return self end +function Entity:kill() + self.life_time = -1 +end + function Entity:update(dt) self:integrate(dt) @@ -86,6 +95,10 @@ function Entity:update(dt) assert(self.transition_state) self:transition_state() + + if self.life_time ~= nil then + self.life_time -= dt + end end function Entity:update_line_of_sight() @@ -147,10 +160,11 @@ function Entity:integrate(dt) end _equipped_item_distance = 6 -function Entity:equipped_from(parent) +function Entity:equipped_from(parent, dist) + dist = dist or _equipped_item_distance self.line_of_sight = vec2(parent.line_of_sight) - offset = (parent.line_of_sight * _equipped_item_distance) + offset = (parent.line_of_sight * dist) self.position = parent.position + offset self.sprite_position = parent.sprite_position + offset @@ -203,5 +217,5 @@ function entity() collision = false }, Entity )) - return World[id] + return World.get(id) end diff --git a/util.lua b/util.lua index 157d7ad..b73838a 100644 --- a/util.lua +++ b/util.lua @@ -1,49 +1,51 @@ +update_hooks = {} + -- https://pico-8.fandom.com/wiki/Qsort -function qsort(a,c,l,r) - c,l,r=c or function(a,b) return a