Fix defend state, add animation tables (not used quite yet), and add onExit functions to the state machine.

This commit is contained in:
Anna Rose 2023-10-04 01:19:05 -04:00
parent 9a26eb4b3a
commit feb7541944
4 changed files with 99 additions and 47 deletions

View File

@ -16,6 +16,10 @@ function Entity:init(img, health, armor)
self.armor = armor or 0
self.introAnimator = nil
-- For imagetable-based animation
self.animationIndex = nil
self.animationTimer = nil
-- movement direction, every update() the entity will move along this vector and return
-- collision data to the subclass
self.vector = geom.vector2D.new(0, 0)
@ -32,15 +36,15 @@ function Entity:init(img, health, armor)
local states = {
["INIT"] = {
main=self.runInit,
transition=self.onInit
incoming=self.onInit
},
["INTRO"] = {
main=self.runIntro,
transition=self.onIntro,
incoming=self.onIntro,
},
["READY"] = {
main=self.runReady,
transition=self.onReady,
incoming=self.onReady,
},
}
self.fsm = StateMachine.new({self}, states)
@ -68,6 +72,26 @@ end
function Entity:update()
-- update state machine
self.fsm:execute()
if self.animationTimer then
local targetIndex = math.floor(self.animationTimer.value)
if self.animationIndex ~= targetIndex then
self:setImage(self.animationTable:getImage(targetIndex))
end
end
end
-- animate the entity based on the passed in imageTable. If reverse is true, play the
-- animation backwards. duration is how long the animation should be in total
function Entity:animate(imgTable, duration, reverse)
self.animationTable = imgTable
local startValue = 1
local endValue = #imgTable
if reverse then
startValue = #imgTable
endValue = 1
end
self.animationTimer = playdate.timer.new(duration, startValue, endValue)
end
-- State machine-controlled functions

View File

@ -69,25 +69,7 @@ function Kani:init(ui)
-- the UI gets passed in to Kani so that we can update pieces of it easily.
self.ui = ui
self.inputHandlers = {
upButtonDown = function() self.vector.dy = -3 end,
downButtonDown = function() self.vector.dy = 3 end,
leftButtonDown = function() self.vector.dx = -3 end,
rightButtonDown = function() self.vector.dx = 3 end,
upButtonUp = function() self.vector.dy = 0 end,
downButtonUp = function() self.vector.dy = 0 end,
leftButtonUp = function() self.vector.dx = 0 end,
rightButtonUp = function() self.vector.dx = 0 end,
cranked = function(change, accelChange) self:chargeReserve(change) end,
AButtonDown = function()
self.firingTimer = playdate.timer.keyRepeatTimerWithDelay(self.weaponChargeSpeed / 2,
self.weaponChargeSpeed,
self.chargeShot, self)
end,
AButtonUp = function() self:fire() end,
}
self.fsm:changeState("READY")
self.fsm:addState("DEFEND", self.runDefend, self.onDefend, self.onDefendExit)
end
function Kani:chargeReserve(change)
@ -138,24 +120,65 @@ end
function Kani:damage(amount)
Kani.super.damage(self, amount)
self.ui.healthMeter:setValue(self.health)
print("Kani health now at " .. self.health) -- debug
if self.health == 0 then
-- TODO: end game here
end
end
function Kani:addInputHandlers()
playdate.inputHandlers.push(self.inputHandlers)
end
function Kani:removeInputHandlers()
playdate.inputHandlers.pop()
end
-- move that crab!
function Kani:runReady()
-- input handling
if playdate.buttonIsPressed(playdate.kButtonDown) then
self.vector.dy = 3
elseif playdate.buttonIsPressed(playdate.kButtonUp) then
self.vector.dy = -3
else
self.vector.dy = 0
end
if playdate.buttonIsPressed(playdate.kButtonLeft) then
self.vector.dx = -3
elseif playdate.buttonIsPressed(playdate.kButtonRight) then
self.vector.dx = 3
else
self.vector.dx = 0
end
local change = select(1, playdate.getCrankChange())
self:chargeReserve(change)
if playdate.buttonJustPressed(playdate.kButtonA) then
self.firingTimer = playdate.timer.keyRepeatTimerWithDelay(self.weaponChargeSpeed / 2,
self.weaponChargeSpeed,
self.chargeShot, self)
end
if playdate.buttonJustReleased(playdate.kButtonA) then
self:fire()
end
if playdate.buttonIsPressed(playdate.kButtonB) then
print("Triggering entrance to DEFEND mode")
self.fsm:changeState("DEFEND")
end
-- collision handling
local collisions = Kani.super.runReady(self)
for i=0, #collisions, 1 do
-- TODO: handle player-triggered collisions
end
end
function Kani:onDefend()
self.armor = 5
end
function Kani:onDefendExit()
self.armor = 0
end
function Kani:runDefend()
if not playdate.buttonIsPressed(playdate.kButtonB) then
self.fsm:changeState("READY")
end
end

View File

@ -23,7 +23,6 @@ function setup()
ui = UI()
player = Kani(ui)
player:moveTo(16, 120)
player:addInputHandlers()
player:add()
ui:add()
currentWave = newWave()
@ -31,13 +30,6 @@ function setup()
makeWalls()
drawBackground()
-- debug, TODO remove this code
playdate.inputHandlers.push({
BButtonUp = function()
print(gfx.sprite.spriteCount())
end
})
end
function makeWalls()

View File

@ -10,24 +10,26 @@ end
-- params is a table of parameters to always pass to the functions
-- stateTable is a table of initial states. Entries should be of the form:
-- name = {main=mainFunc, transition=transFunc}
-- name = {main=mainFunc, incoming=inFunc, outgoing=outFunc}
-- where mainFunc and transFunc are functions that should be executed in relation to the state.
-- the State Machine will call mainFunc on execute()
-- the State Machine will call transFunc when the state is entered
-- the State Machine will call inFunc when the state is entered
-- the State Machine will call outFunc when the state in exited
function StateMachine:init(params, stateTable)
self.currentState = nil
self.parameters = params or {}
self.states = stateTable or {}
end
function StateMachine:addState(name, mainFunc, transFunc)
self.states[name] = {main=mainFunc, transition=transFunc}
function StateMachine:addState(name, mainFunc, inFunc, outFunc)
self.states[name] = {main=mainFunc, incoming=inFunc, outgoing=outFunc}
end
-- execute the main function for the current state.
-- returns true if the function was executed, false otherwise
function StateMachine:execute()
if self.currentState == nil or not self.states[self.currentState] then
if self.currentState == nil or not self.states[self.currentState]
or not self.states[self.currentState].main then
return false
end
@ -37,11 +39,22 @@ end
-- change to the specified state. returns true if the state change is successful
function StateMachine:changeState(state)
if not self.states[state] then return false end
if not self.states[state] then
print("WARN (StateMachine): tried to enter nonexistent state " .. state)
return false
end
-- run the exit function for the previous state
if self.currentState and self.states[self.currentState].outgoing then
self.states[self.currentState].outgoing(table.unpack(self.parameters))
end
-- change state
self.currentState = state
if self.states[state].transition then
self.states[state].transition(table.unpack(self.parameters))
-- run the entrance function for the new state
if self.states[state].incoming then
self.states[state].incoming(table.unpack(self.parameters))
end
return true
end