Buffer player-controlled fielder throws.

Use new actionQueue to do so.
Move thrower and receiver selection into fielder.lua
Correct SOURCE_FILES listing in Makefile
This commit is contained in:
Sage Vaillancourt 2025-02-08 22:02:29 -05:00
parent b119310859
commit 82d1dac5de
5 changed files with 101 additions and 27 deletions

View File

@ -1,4 +1,4 @@
SOURCE_FILES := $(shell grep "import '" src/main.lua | grep -v CoreLibs | sed "s/.*'\(.*\)'.*/\1/") main.lua SOURCE_FILES := $(shell grep "import '" src/main.lua | grep -v CoreLibs | sed "s/.*'\(.*\)'.*/src\/\1/") src/main.lua
GENERATED_FILES := src/assets.lua GENERATED_FILES := src/assets.lua
all: all:

50
src/action-queue.lua Normal file
View File

@ -0,0 +1,50 @@
---@alias ActionResult {}
---@type table<string, ActionResult>
-- selene: allow(unscoped_variables)
ActionResult = {
Succeeded = {},
Failed = {},
NeedsMoreTime = {},
}
-- selene: allow(unscoped_variables)
actionQueue = {
---@type ({ action: Action, expireTimeMs: number })[]
queue = {},
}
---@alias Action fun(deltaSeconds: number): ActionResult
--- Added actions will be called on every runWaiting() update.
--- They will continue to be executed until they return Succeeded or Failed instead of NeedsMoreTime.
--- Replaces any existing action with the given name.
--- If the first call of action() doesn't return NeedsMoreTime, will skip adding to the queue.
---@param name string
---@param maxTimeMs number
---@param action Action
function actionQueue.upsert(self, name, maxTimeMs, action)
if action(0) ~= ActionResult.NeedsMoreTime then
return
end
self.queue[name] = {
action = action,
expireTimeMs = maxTimeMs + playdate.getCurrentTimeMilliseconds(),
}
end
--- Must call on every playdate.update() to check for (and run) any waiting tasks.
--- Actions that return NeedsMoreTime will not be removed from the queue unless they have expired.
function actionQueue.runWaiting(self, deltaSeconds)
local currentTimeMs = playdate.getCurrentTimeMilliseconds()
for name, actionObject in pairs(self.queue) do
local result = actionObject.action(deltaSeconds)
if
result ~= ActionResult.NeedsMoreTime
or currentTimeMs > actionObject.expireTimeMs
then
self.queue[name] = nil
end
end
end

View File

@ -21,6 +21,8 @@ Field = {
center = newFielder("C.Center", 40), center = newFielder("C.Center", 40),
right = newFielder("Right", 40), right = newFielder("Right", 40),
}, },
---@type Fielder | nil
fielderTouchingBall = nil,
} }
--- Actually only benches the infield, because outfielders are far away! --- Actually only benches the infield, because outfielders are far away!
@ -86,12 +88,34 @@ end
---@param ball XYPair ---@param ball XYPair
---@return Fielder | nil fielderTouchingBall nil if no fielder is currently touching the ball ---@return Fielder | nil fielderTouchingBall nil if no fielder is currently touching the ball
function Field.updateFielderPositions(self, ball, deltaSeconds) function Field.updateFielderPositions(self, ball, deltaSeconds)
local fielderTouchingBall local fielderTouchingBallLocal = nil
for _, fielder in pairs(self.fielders) do for _, fielder in pairs(self.fielders) do
local isTouchingBall = updateFielderPosition(deltaSeconds, fielder, ball) local isTouchingBall = updateFielderPosition(deltaSeconds, fielder, ball)
if isTouchingBall then if isTouchingBall then
fielderTouchingBall = fielder fielderTouchingBallLocal = fielder
end end
end end
return fielderTouchingBall self.fielderTouchingBall = fielderTouchingBallLocal
return fielderTouchingBallLocal
end
-- TODO? Start moving target fielders close sooner?
local function playerThrowToImpl(field, targetBase, throwBall, throwFlyMs)
if field.fielderTouchingBall == nil then
return ActionResult.NeedsMoreTime
end
local closestFielder = utils.getNearestOf(field.fielders, targetBase.x, targetBase.y, function(fielder)
return fielder ~= field.fielderTouchingBall -- presumably, this is who will be doing the throwing
end)
closestFielder.target = targetBase
throwBall(targetBase.x, targetBase.y, playdate.easingFunctions.linear, throwFlyMs)
end
--- Buffer in a fielder throw action.
function Field.playerThrowTo(self, targetBase, throwBall, throwFlyMs)
local maxTryTimeMs = 5000
actionQueue:upsert('playerThrowTo', maxTryTimeMs, function()
return playerThrowToImpl(self, targetBase, throwBall, throwFlyMs)
end)
end end

View File

@ -28,6 +28,7 @@ import 'utils.lua'
import 'constants.lua' import 'constants.lua'
import 'assets.lua' import 'assets.lua'
import 'action-queue.lua'
import 'announcer.lua' import 'announcer.lua'
import 'dbg.lua' import 'dbg.lua'
import 'field.lua' import 'field.lua'
@ -85,7 +86,7 @@ local teams <const> = {
}, },
} }
local PlayerTeam <const> = teams.away local PlayerTeam <const> = teams.home
local battingTeam = teams.away local battingTeam = teams.away
local outs = 0 local outs = 0
local inning = 1 local inning = 1
@ -286,10 +287,9 @@ local function readThrow()
return nil return nil
end end
---@param thrower Fielder
---@param throwFlyMs number ---@param throwFlyMs number
---@return boolean didThrow ---@return boolean didThrow
local function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome) local function buttonControlledThrow(throwFlyMs, forbidThrowHome)
local targetBase local targetBase
if playdate.buttonIsPressed(playdate.kButtonLeft) then if playdate.buttonIsPressed(playdate.kButtonLeft) then
targetBase = C.Bases[C.Third] targetBase = C.Bases[C.Third]
@ -303,12 +303,10 @@ local function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome)
return false return false
end end
local closestFielder = utils.getNearestOf(Field.fielders, targetBase.x, targetBase.y, function(fielder) -- Power for this throw has already been determined
return fielder ~= thrower throwMeter = 0
end)
throwBall(targetBase.x, targetBase.y, playdate.easingFunctions.linear, throwFlyMs) Field:playerThrowTo(targetBase, throwBall, throwFlyMs)
closestFielder.target = targetBase
secondsSinceLastRunnerMove = 0 secondsSinceLastRunnerMove = 0
offenseMode = C.Offense.running offenseMode = C.Offense.running
@ -583,7 +581,7 @@ local function updateGameState()
if secondsSincePitchAllowed > C.PitchAfterSeconds then if secondsSincePitchAllowed > C.PitchAfterSeconds then
if playerOnDefense then if playerOnDefense then
local throwFly = readThrow() local throwFly = readThrow()
if throwFly and not buttonControlledThrow(pitcher, throwFly, true) then if throwFly and not buttonControlledThrow(throwFly, true) then
playerPitch(throwFly) playerPitch(throwFly)
end end
else else
@ -614,19 +612,21 @@ local function updateGameState()
local fielderHoldingBall = Field:updateFielderPositions(ball, deltaSeconds) local fielderHoldingBall = Field:updateFielderPositions(ball, deltaSeconds)
if fielderHoldingBall then
local outedSomeRunner = outEligibleRunners(fielderHoldingBall)
if playerOnDefense then if playerOnDefense then
local throwFly = readThrow() local throwFly = readThrow()
if throwFly then if throwFly then
buttonControlledThrow(Field.fielders.pitcher, throwFly) buttonControlledThrow(throwFly)
end end
else end
npcFielderAction(fielder, outedSomeRunner) if fielderHoldingBall then
local outedSomeRunner = outEligibleRunners(fielderHoldingBall)
if playerOnOffense then
npcFielderAction(fielderHoldingBall, outedSomeRunner)
end end
end end
walkAwayOutRunners() walkAwayOutRunners()
actionQueue:runWaiting(deltaSeconds)
end end
function playdate.update() function playdate.update()

View File

@ -44,18 +44,18 @@ local function getForcedOutTargets(runners)
return targets return targets
end end
--- Returns the position,distance of the basest closest to the runner furthest from a base --- Returns the position,distance of the base closest to the runner who is *furthest* from a base
---@return Base | nil, number | nil ---@return Base | nil, number | nil
local function getBaseOfStrandedRunner(runners) local function getBaseOfStrandedRunner(runners)
local farRunnersBase, farDistance local farRunnersBase, farDistance
for _, runner in pairs(runners) do for _, runner in pairs(runners) do
if runner ~= batter then --if runner ~= batter then
local nearestBase, distance = utils.getNearestOf(C.Bases, runner.x, runner.y) local nearestBase, distance = utils.getNearestOf(C.Bases, runner.x, runner.y)
if farRunnersBase == nil or farDistance < distance then if farRunnersBase == nil or farDistance < distance then
farRunnersBase = nearestBase farRunnersBase = nearestBase
farDistance = distance farDistance = distance
end end
end --end
end end
return farRunnersBase, farDistance return farRunnersBase, farDistance