BatterUp/src/npc.lua

144 lines
4.1 KiB
Lua

local npcBatDeg = 0
local BaseNpcBatSpeed <const> = 1500
local npcBatSpeed = 1500
---@class Npc
---@field runners Runner[]
---@field fielders Fielder[]
-- selene: allow(unscoped_variables)
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 catcherThrownBall boolean
---@param deltaSec number
---@return number
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
npcBatDeg = npcBatDeg + (deltaSec * npcBatSpeed)
else
npcBatSpeed = (1 + math.random()) * BaseNpcBatSpeed
npcBatDeg = 200
end
return npcBatDeg
end
function Npc:batSpeed()
return npcBatSpeed
end
---@return number
function Npc:runningSpeed()
if #self.runners == 0 then
return 0
end
local touchedBase = utils.isTouchingBase(self.runners[1].x, self.runners[1].y)
if not touchedBase or touchedBase == C.Bases[C.Home] then
return 10
end
return 0
end
---@param runners Runner[]
---@return Base[]
local function getForcedOutTargets(runners)
local targets = {}
for _, base in ipairs(C.Bases) do
local runnerTargetingBase = utils.getRunnerWithNextBase(runners, base)
if runnerTargetingBase then
targets[#targets + 1] = base
else
return targets
end
end
return targets
end
--- Returns the position,distance of the base closest to the runner who is *furthest* from a base
---@param runners Runner[]
---@return Base | nil, number | nil
local function getBaseOfStrandedRunner(runners)
local farRunnersBase, farDistance
for _, runner in pairs(runners) do
--if runner ~= batter then
local nearestBase, distance = utils.getNearestOf(C.Bases, runner.x, runner.y)
if farRunnersBase == nil or farDistance < distance then
farRunnersBase = nearestBase
farDistance = distance
end
--end
end
return farRunnersBase, farDistance
end
--- Returns x,y of the out target
---@param runners Runner[]
---@return number|nil, number|nil
local function getNextOutTarget(runners)
-- TODO: Handle missed throws, check for fielders at target, etc.
local targets = getForcedOutTargets(runners)
if #targets ~= 0 then
return targets[#targets].x, targets[#targets].y
end
local baseCloseToStrandedRunner = getBaseOfStrandedRunner(runners)
if baseCloseToStrandedRunner then
return baseCloseToStrandedRunner.x, baseCloseToStrandedRunner.y
end
end
---@param fielders Fielder[]
---@param fielder Fielder
---@param runners Runner[]
---@param launchBall LaunchBall
local function tryToMakeAPlay(fielders, fielder, runners, ball, launchBall)
local targetX, targetY = getNextOutTarget(runners)
if targetX ~= nil and targetY ~= nil then
local nearestFielder = utils.getNearestOf(fielders, targetX, targetY)
nearestFielder.target = utils.xy(targetX, targetY)
if nearestFielder == fielder then
ball.heldBy = fielder
else
launchBall(targetX, targetY, playdate.easingFunctions.linear, nil, true)
end
end
end
---@param offenseState OffenseState
---@param fielder Fielder
---@param outedSomeRunner boolean
---@param ball { x: number, y: number, heldBy: Fielder | nil }
---@param launchBall LaunchBall
function Npc:fielderAction(offenseState, fielder, outedSomeRunner, ball, launchBall)
if offenseState ~= C.Offense.running then
return
end
if outedSomeRunner then
-- Delay a little before the next play
playdate.timer.new(750, function()
tryToMakeAPlay(self.fielders, fielder, self.runners, ball, launchBall)
end)
else
tryToMakeAPlay(self.fielders, fielder, self.runners, ball, launchBall)
end
end
---@return number
function Npc:pitchSpeed()
return 2
end
if not playdate then
return Npc
end