Class-ify npc -> Npc

In the future, may keep a small memory of past pitches/plays, and use it to adjust swings, baserunning, etc.
This commit is contained in:
Sage Vaillancourt 2025-02-09 12:30:53 -05:00
parent 575c9e0a18
commit 4d69e77d9f
2 changed files with 33 additions and 22 deletions

View File

@ -42,6 +42,7 @@ local gfx <const>, C <const> = playdate.graphics, C
local announcer = Announcer.new() local announcer = Announcer.new()
local baserunning = Baserunning.new(announcer) local baserunning = Baserunning.new(announcer)
local fielding = Fielding.new() local fielding = Fielding.new()
local npc = Npc.new(baserunning.runners, fielding.fielders)
---@alias SimpleAnimator { currentValue: fun(self): number; reset: fun(self, durationMs: number | nil) } ---@alias SimpleAnimator { currentValue: fun(self): number; reset: fun(self, durationMs: number | nil) }
@ -389,8 +390,8 @@ local function updateGameState()
batAngleDeg = (playdate.getCrankPosition() + C.CrankOffsetDeg) % 360 batAngleDeg = (playdate.getCrankPosition() + C.CrankOffsetDeg) % 360
batSpeed = crankLimited batSpeed = crankLimited
else else
batAngleDeg = npc.updateBatAngle(ball, catcherThrownBall, deltaSeconds) batAngleDeg = npc:updateBatAngle(ball, catcherThrownBall, deltaSeconds)
batSpeed = npc.batSpeed() * deltaSeconds batSpeed = npc:batSpeed() * deltaSeconds
end end
updateBatting(batAngleDeg, batSpeed) updateBatting(batAngleDeg, batSpeed)
@ -410,7 +411,7 @@ local function updateGameState()
end end
end end
elseif offenseState == C.Offense.running then elseif offenseState == C.Offense.running then
local appliedSpeed = playerOnOffense and crankLimited or npc.runningSpeed(baserunning.runners) local appliedSpeed = playerOnOffense and crankLimited or npc:runningSpeed()
if updateNonBatterRunners(appliedSpeed) then if updateNonBatterRunners(appliedSpeed) then
secondsSinceLastRunnerMove = 0 secondsSinceLastRunnerMove = 0
else else
@ -441,7 +442,7 @@ local function updateGameState()
if fielderHoldingBall then if fielderHoldingBall then
local outedSomeRunner = baserunning:outEligibleRunners(fielderHoldingBall) local outedSomeRunner = baserunning:outEligibleRunners(fielderHoldingBall)
if playerOnOffense then if playerOnOffense then
npc.fielderAction(fielding, baserunning, offenseState, fielderHoldingBall, outedSomeRunner, ball, launchBall) npc:fielderAction(offenseState, fielderHoldingBall, outedSomeRunner, ball, launchBall)
end end
end end

View File

@ -2,14 +2,27 @@ local npcBatDeg = 0
local BaseNpcBatSpeed <const> = 1500 local BaseNpcBatSpeed <const> = 1500
local npcBatSpeed = 1500 local npcBatSpeed = 1500
---@class Npc
---@field runners Runner[]
---@field fielders Fielder[]
-- selene: allow(unscoped_variables) -- selene: allow(unscoped_variables)
npc = {} Npc = {}
---@param runners Runner[]
---@param fielders Fielder[]
---@return Npc
function Npc.new(runners, fielders)
return setmetatable({
runners = runners,
fielders = fielders,
}, { __index = Npc })
end
---@param ball XyPair ---@param ball XyPair
---@param catcherThrownBall boolean ---@param catcherThrownBall boolean
---@param deltaSec number ---@param deltaSec number
---@return number ---@return number
function npc.updateBatAngle(ball, catcherThrownBall, deltaSec) function Npc:updateBatAngle(ball, catcherThrownBall, deltaSec)
if not catcherThrownBall and ball.y > 200 and ball.y < 230 and (ball.x < C.Center.x + 15) then if not catcherThrownBall and ball.y > 200 and ball.y < 230 and (ball.x < C.Center.x + 15) then
npcBatDeg = npcBatDeg + (deltaSec * npcBatSpeed) npcBatDeg = npcBatDeg + (deltaSec * npcBatSpeed)
else else
@ -19,17 +32,16 @@ function npc.updateBatAngle(ball, catcherThrownBall, deltaSec)
return npcBatDeg return npcBatDeg
end end
function npc.batSpeed() function Npc:batSpeed()
return npcBatSpeed return npcBatSpeed
end end
---@param runners Runner[]
---@return number ---@return number
function npc.runningSpeed(runners) function Npc:runningSpeed()
if #runners == 0 then if #self.runners == 0 then
return 0 return 0
end end
local touchedBase = utils.isTouchingBase(runners[1].x, runners[1].y) local touchedBase = utils.isTouchingBase(self.runners[1].x, self.runners[1].y)
if not touchedBase or touchedBase == C.Bases[C.Home] then if not touchedBase or touchedBase == C.Bases[C.Home] then
return 10 return 10
end end
@ -72,7 +84,7 @@ end
--- Returns x,y of the out target --- Returns x,y of the out target
---@param runners Runner[] ---@param runners Runner[]
---@return number|nil, number|nil ---@return number|nil, number|nil
function npc.getNextOutTarget(runners) local function getNextOutTarget(runners)
-- TODO: Handle missed throws, check for fielders at target, etc. -- TODO: Handle missed throws, check for fielders at target, etc.
local targets = getForcedOutTargets(runners) local targets = getForcedOutTargets(runners)
if #targets ~= 0 then if #targets ~= 0 then
@ -85,14 +97,14 @@ function npc.getNextOutTarget(runners)
end end
end end
---@param fielding Fielding ---@param fielders Fielder[]
---@param fielder Fielder ---@param fielder Fielder
---@param runners Runner[] ---@param runners Runner[]
---@param launchBall LaunchBall ---@param launchBall LaunchBall
function npc.tryToMakeAPlay(fielding, fielder, runners, ball, launchBall) local function tryToMakeAPlay(fielders, fielder, runners, ball, launchBall)
local targetX, targetY = npc.getNextOutTarget(runners) local targetX, targetY = getNextOutTarget(runners)
if targetX ~= nil and targetY ~= nil then if targetX ~= nil and targetY ~= nil then
local nearestFielder = utils.getNearestOf(fielding.fielders, targetX, targetY) local nearestFielder = utils.getNearestOf(fielders, targetX, targetY)
nearestFielder.target = utils.xy(targetX, targetY) nearestFielder.target = utils.xy(targetX, targetY)
if nearestFielder == fielder then if nearestFielder == fielder then
ball.heldBy = fielder ball.heldBy = fielder
@ -102,27 +114,25 @@ function npc.tryToMakeAPlay(fielding, fielder, runners, ball, launchBall)
end end
end end
---@param fielding Fielding
---@param baserunning Baserunning
---@param offenseState OffenseState ---@param offenseState OffenseState
---@param fielder Fielder ---@param fielder Fielder
---@param outedSomeRunner boolean ---@param outedSomeRunner boolean
---@param ball { x: number, y: number, heldBy: Fielder | nil } ---@param ball { x: number, y: number, heldBy: Fielder | nil }
---@param launchBall LaunchBall ---@param launchBall LaunchBall
function npc.fielderAction(fielding, baserunning, offenseState, fielder, outedSomeRunner, ball, launchBall) function Npc:fielderAction(offenseState, fielder, outedSomeRunner, ball, launchBall)
if offenseState ~= C.Offense.running then if offenseState ~= C.Offense.running then
return return
end end
if outedSomeRunner then if outedSomeRunner then
-- Delay a little before the next play -- Delay a little before the next play
playdate.timer.new(750, function() playdate.timer.new(750, function()
npc.tryToMakeAPlay(fielding, fielder, baserunning.runners, ball, launchBall) tryToMakeAPlay(self.fielders, fielder, self.runners, ball, launchBall)
end) end)
else else
npc.tryToMakeAPlay(fielding, fielder, baserunning.runners, ball, launchBall) tryToMakeAPlay(self.fielders, fielder, self.runners, ball, launchBall)
end end
end end
if not playdate then if not playdate then
return npc return Npc
end end