diff --git a/Makefile b/Makefile index d059610..5bee817 100644 --- a/Makefile +++ b/Makefile @@ -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 all: diff --git a/src/action-queue.lua b/src/action-queue.lua new file mode 100644 index 0000000..a268e87 --- /dev/null +++ b/src/action-queue.lua @@ -0,0 +1,50 @@ +---@alias ActionResult {} + +---@type table +-- 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 \ No newline at end of file diff --git a/src/field.lua b/src/field.lua index 0c43df9..d01746d 100644 --- a/src/field.lua +++ b/src/field.lua @@ -21,6 +21,8 @@ Field = { center = newFielder("C.Center", 40), right = newFielder("Right", 40), }, + ---@type Fielder | nil + fielderTouchingBall = nil, } --- Actually only benches the infield, because outfielders are far away! @@ -86,12 +88,34 @@ end ---@param ball XYPair ---@return Fielder | nil fielderTouchingBall nil if no fielder is currently touching the ball function Field.updateFielderPositions(self, ball, deltaSeconds) - local fielderTouchingBall + local fielderTouchingBallLocal = nil for _, fielder in pairs(self.fielders) do local isTouchingBall = updateFielderPosition(deltaSeconds, fielder, ball) if isTouchingBall then - fielderTouchingBall = fielder + fielderTouchingBallLocal = fielder 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 \ No newline at end of file diff --git a/src/main.lua b/src/main.lua index bd3a0b5..3240bfb 100644 --- a/src/main.lua +++ b/src/main.lua @@ -28,6 +28,7 @@ import 'utils.lua' import 'constants.lua' import 'assets.lua' +import 'action-queue.lua' import 'announcer.lua' import 'dbg.lua' import 'field.lua' @@ -85,7 +86,7 @@ local teams = { }, } -local PlayerTeam = teams.away +local PlayerTeam = teams.home local battingTeam = teams.away local outs = 0 local inning = 1 @@ -286,10 +287,9 @@ local function readThrow() return nil end ----@param thrower Fielder ---@param throwFlyMs number ---@return boolean didThrow -local function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome) +local function buttonControlledThrow(throwFlyMs, forbidThrowHome) local targetBase if playdate.buttonIsPressed(playdate.kButtonLeft) then targetBase = C.Bases[C.Third] @@ -303,12 +303,10 @@ local function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome) return false end - local closestFielder = utils.getNearestOf(Field.fielders, targetBase.x, targetBase.y, function(fielder) - return fielder ~= thrower - end) + -- Power for this throw has already been determined + throwMeter = 0 - throwBall(targetBase.x, targetBase.y, playdate.easingFunctions.linear, throwFlyMs) - closestFielder.target = targetBase + Field:playerThrowTo(targetBase, throwBall, throwFlyMs) secondsSinceLastRunnerMove = 0 offenseMode = C.Offense.running @@ -583,7 +581,7 @@ local function updateGameState() if secondsSincePitchAllowed > C.PitchAfterSeconds then if playerOnDefense then local throwFly = readThrow() - if throwFly and not buttonControlledThrow(pitcher, throwFly, true) then + if throwFly and not buttonControlledThrow(throwFly, true) then playerPitch(throwFly) end else @@ -614,19 +612,21 @@ local function updateGameState() local fielderHoldingBall = Field:updateFielderPositions(ball, deltaSeconds) + if playerOnDefense then + local throwFly = readThrow() + if throwFly then + buttonControlledThrow(throwFly) + end + end if fielderHoldingBall then local outedSomeRunner = outEligibleRunners(fielderHoldingBall) - if playerOnDefense then - local throwFly = readThrow() - if throwFly then - buttonControlledThrow(Field.fielders.pitcher, throwFly) - end - else - npcFielderAction(fielder, outedSomeRunner) + if playerOnOffense then + npcFielderAction(fielderHoldingBall, outedSomeRunner) end end walkAwayOutRunners() + actionQueue:runWaiting(deltaSeconds) end function playdate.update() diff --git a/src/npc.lua b/src/npc.lua index 2e62c84..33cfca3 100644 --- a/src/npc.lua +++ b/src/npc.lua @@ -44,18 +44,18 @@ local function getForcedOutTargets(runners) return targets 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 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 + --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