90 lines
2.4 KiB
Lua
90 lines
2.4 KiB
Lua
|
local Object = require "lib/classic"
|
||
|
|
||
|
---@alias AnimationFinishBehavior
|
||
|
---| '"loop"' Loop the animation over from the beginning.
|
||
|
---| '"stop"' Freeze the animation on the last frame.
|
||
|
---| '"goto"' Go to another state.
|
||
|
|
||
|
---@class State
|
||
|
---@field animation Animation The animation frames for this state.
|
||
|
---@field on_finish? AnimationFinishBehavior What to do after this state's animation finishes. Defaults to "loop".
|
||
|
---@field next? string The next state to go to in the case of "goto" on_finish.
|
||
|
|
||
|
local Animator = Object:extend()
|
||
|
|
||
|
---Creates a new animator.
|
||
|
---@param states { [string]: State } A dictionary of all states in this animator.
|
||
|
---@param first_state string The first state of this animator.
|
||
|
function Animator:new(states, first_state)
|
||
|
---The states in this animator.
|
||
|
self.states = states
|
||
|
|
||
|
---The current state.
|
||
|
self.state = first_state
|
||
|
|
||
|
---The timestep of the current frame.
|
||
|
self.step = 0
|
||
|
|
||
|
---The current frame in the current animation.
|
||
|
self.frame = 1
|
||
|
|
||
|
--- The next state to progress to.
|
||
|
self.next_state = nil
|
||
|
end
|
||
|
|
||
|
function Animator:update(dt)
|
||
|
local state = self.states[self.state]
|
||
|
local frames = state.animation.frames
|
||
|
|
||
|
--Step the current timestep and return if the frame hasn't changed.
|
||
|
self.step = self.step + dt
|
||
|
if self.step <= frames[self.frame].duration then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
--Step the current frame and return if there are more.
|
||
|
self.step = 0
|
||
|
self.frame = self.frame + 1
|
||
|
if self.frame <= #frames then
|
||
|
return
|
||
|
end
|
||
|
|
||
|
--We've reached the end of the current state.
|
||
|
if self.next_state then
|
||
|
self.state = self.next_state
|
||
|
self.frame = 1
|
||
|
self.next_state = nil
|
||
|
elseif state.on_finish == "goto" then
|
||
|
self.state = state.next
|
||
|
self.frame = 1
|
||
|
elseif state.on_finish == "stop" then
|
||
|
self.frame = #frames
|
||
|
elseif state.on_finish == "loop" or state.on_finish == nil then
|
||
|
self.frame = 1
|
||
|
else
|
||
|
error("Invalid state on_finish " .. state.on_finish)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function Animator:draw(x, y)
|
||
|
local state = self.states[self.state]
|
||
|
state.animation:draw(self.frame, x, y)
|
||
|
end
|
||
|
|
||
|
---Sets the state of this animator.
|
||
|
---@param state string The name of the state to set.
|
||
|
---@param queue? boolean If true, queues the state change for after the current state.
|
||
|
function Animator:setState(state, queue)
|
||
|
if not self.states[state] then
|
||
|
error("Invalid state " .. state)
|
||
|
elseif queue then
|
||
|
self.next_state = state
|
||
|
else
|
||
|
self.state = state
|
||
|
self.step = 0
|
||
|
self.frame = 1
|
||
|
end
|
||
|
end
|
||
|
|
||
|
return Animator
|