diff --git a/src/announcer.lua b/src/announcer.lua index 71ab997..d72458b 100644 --- a/src/announcer.lua +++ b/src/announcer.lua @@ -23,7 +23,7 @@ function Announcer.new() }, { __index = Announcer }) end -local DurationMs = 3000 +local DurationMs = 2000 function Announcer:popIn() self.animatorY = AnnouncerAnimatorInY diff --git a/src/baserunning.lua b/src/baserunning.lua index 0897c73..18f95a5 100644 --- a/src/baserunning.lua +++ b/src/baserunning.lua @@ -17,6 +17,8 @@ ---@field onThirdOut fun() Baserunning = {} +-- TODO: Implement slides. Would require making fielders' gloves "real objects" whose state is tracked. + ---@param announcer any ---@return Baserunning function Baserunning.new(announcer, onThirdOut) @@ -196,6 +198,9 @@ function Baserunning:updateRunner(runner, runnerIndex, appliedSpeed, deltaSecond end end + -- TODO: Make this less "sticky" for the user. + -- Currently it can be a little hard to run *past* a base. + local autoRun = (nearestBaseDistance > 40 or runner.forcedTo) and mult * autoRunSpeed or nearestBaseDistance < 5 and 0 or (nearestBase == runner.nextBase and autoRunSpeed or -1 * autoRunSpeed) diff --git a/src/fielding.lua b/src/fielding.lua index e11b6f7..353e5ac 100644 --- a/src/fielding.lua +++ b/src/fielding.lua @@ -5,6 +5,8 @@ --- speed: number, --- } +-- TODO: Run down baserunners in a pickle. + -- selene: allow(unscoped_variables) ---@class Fielding ---@field fielders table @@ -117,6 +119,9 @@ function Fielding:updateFielderPositions(ball, deltaSeconds) fielderTouchingBall = fielder end end + -- TODO: The need is growing for a distinction between touching the ball and holding the ball. + -- Or, at least, fielders need to start *stopping* the ball when they make contact with it. + -- Right now, a line-drive *through* first will be counted as an out. self.fielderTouchingBall = fielderTouchingBall return fielderTouchingBall end diff --git a/src/main.lua b/src/main.lua index 89e5eb2..7f90495 100644 --- a/src/main.lua +++ b/src/main.lua @@ -42,15 +42,23 @@ local gfx , C = playdate.graphics, C local announcer = Announcer.new() local fielding = Fielding.new() +-- TODO: Find a way to get baserunning and npc instantiated closer to the top, here. +-- Currently difficult because they depend on nextHalfInning/each other. ----@alias SimpleAnimator { currentValue: fun(self): number; reset: fun(self, durationMs: number | nil) } +------------------ +-- GLOBAL STATE -- +------------------ local deltaSeconds = 0 - local ball = Ball.new(gfx.animator) ----@alias Team { score: number, benchPosition: XyPair } +local batBase = utils.xy(C.Center.x - 34, 215) +local batTip = utils.xy(0, 0) +local batAngleDeg = C.CrankOffsetDeg +local catcherThrownBall = false + +---@alias Team { score: number, benchPosition: XyPair } ---@type table local teams = { home = { @@ -63,25 +71,28 @@ local teams = { }, } -local UserTeam = teams.away -local battingTeam = teams.away -local battingTeamSprites = AwayTeamSprites -local fieldingTeamSprites = HomeTeamSprites -local runnerBlipper = battingTeam == teams.away and AwayTeamBlipper or HomeTeamBlipper local inning = 1 + +local battingTeam = teams.away local offenseState = C.Offense.batting -- TODO: Replace with timers, repeatedly reset, instead of constantly setting to 0 local secondsSinceLastRunnerMove = 0 -local secondsSincePitchAllowed = -5 +local secondsSincePitchAllowed = 0 -local catcherThrownBall = false +-- These are only sort-of global state. They are purely graphical, +-- but they need to be kept in sync with the rest of the globals. +local runnerBlipper = battingTeam == teams.away and AwayTeamBlipper or HomeTeamBlipper +local battingTeamSprites = AwayTeamSprites +local fieldingTeamSprites = HomeTeamSprites -local batBase = utils.xy(C.Center.x - 34, 215) -local batTip = utils.xy(0, 0) +------------------------- +-- END OF GLOBAL STATE -- +------------------------- -local batAngleDeg = C.CrankOffsetDeg +local UserTeam = teams.away +---@alias SimpleAnimator { currentValue: fun(self): number; reset: fun(self, durationMs: number | nil) } ---@alias Pitch { x: SimpleAnimator, y: SimpleAnimator, z: SimpleAnimator | nil } ---@type Pitch[] @@ -113,8 +124,12 @@ local Pitches = { }, } ----@return boolean userIsOnSide, boolean playerIsOnOtherSide +---@return boolean userIsOnSide, boolean userIsOnOtherSide local function userIsOn(side) + if UserTeam == nil then + -- Both teams are NPC-driven + return false, false + end local ret if UserTeam == battingTeam then ret = side == C.Sides.offense @@ -124,13 +139,7 @@ local function userIsOn(side) return ret, not ret end ---- Launches the ball from its current position to the given destination. ----@param destX number ----@param destY number ----@param easingFunc EasingFunc ----@param flyTimeMs number | nil ----@param floaty boolean | nil ----@param customBallScaler pd_animator | nil +---@type LaunchBall local function launchBall(destX, destY, easingFunc, flyTimeMs, floaty, customBallScaler) throwMeter:reset() ball:launch(destX, destY, easingFunc, flyTimeMs, floaty, customBallScaler) @@ -174,28 +183,26 @@ local function nextHalfInning() announcer:say("SWITCHING SIDES...") end - -- TODO: Make the overlay handle its own dang delay. - -- Delay to keep end-of-inning on the scoreboard for a few seconds - playdate.timer.new(3000, function() - battingTeam = currentlyFieldingTeam + if gameOver then + announcer:say("AND THAT'S THE BALL GAME!") + else + fielding:resetFielderPositions() if battingTeam == teams.home then - battingTeamSprites = HomeTeamSprites - runnerBlipper = HomeTeamBlipper - fieldingTeamSprites = AwayTeamSprites - else - battingTeamSprites = AwayTeamSprites - fieldingTeamSprites = HomeTeamSprites - runnerBlipper = AwayTeamBlipper + inning = inning + 1 end - if gameOver then - announcer:say("AND THAT'S THE BALL GAME!") - else - fielding:resetFielderPositions() + battingTeam = currentlyFieldingTeam + playdate.timer.new(2000, function() if battingTeam == teams.home then - inning = inning + 1 + battingTeamSprites = HomeTeamSprites + runnerBlipper = HomeTeamBlipper + fieldingTeamSprites = AwayTeamSprites + else + battingTeamSprites = AwayTeamSprites + fieldingTeamSprites = HomeTeamSprites + runnerBlipper = AwayTeamBlipper end - end - end) + end) + end end local baserunning = Baserunning.new(announcer, nextHalfInning) @@ -443,7 +450,7 @@ local function updateGameState() end if fielderHoldingBall then local outedSomeRunner = baserunning:outEligibleRunners(fielderHoldingBall) - if userOnOffense then + if not userOnDefense then npc:fielderAction(offenseState, fielderHoldingBall, outedSomeRunner, ball, launchBall) end end diff --git a/src/npc.lua b/src/npc.lua index 832993f..6694379 100644 --- a/src/npc.lua +++ b/src/npc.lua @@ -48,7 +48,7 @@ function Npc:runningSpeed(ball) local runner1 = self.runners[1] - local ballIsFar = utils.distanceBetweenZ(ball.x, ball.y, ball.z, runner1.x, runner1.y, 0) > 250 + local ballIsFar = utils.distanceBetweenZ(ball.x, ball.y, ball.z, runner1.x, runner1.y, 0) > 300 if ballIsFar or runner1.forcedTo then return baseRunningSpeed