Add basic home-run handling

Still needs to pan the camera back from the home run while the runners circle the bases.
Also add a wrapping-pattern.png, though I'm not sure if it's actually used?
This commit is contained in:
Sage Vaillancourt 2025-02-18 13:58:44 -05:00
parent 1bdcc62347
commit 2d812f2046
6 changed files with 54 additions and 23 deletions

View File

@ -16,7 +16,8 @@
---@field onThirdOut fun() ---@field onThirdOut fun()
Baserunning = {} Baserunning = {}
-- TODO: Implement slides. Would require making fielders' gloves "real objects" whose state is tracked. -- TODO: Implement slides? Would require making fielders' gloves "real objects" whose state is tracked.
-- TODO: Don't allow runners to occupy the same base!
---@param announcer Announcer ---@param announcer Announcer
---@return Baserunning ---@return Baserunning

View File

@ -98,6 +98,7 @@ C.Offense = {
batting = "batting", batting = "batting",
running = "running", running = "running",
walking = "walking", walking = "walking",
homeRun = "homeRun",
} }
---@alias Side "offense" | "defense" ---@alias Side "offense" | "defense"
@ -124,17 +125,23 @@ C.WalkedRunnerSpeed = 10
C.ResetFieldersAfterSeconds = 2.5 C.ResetFieldersAfterSeconds = 2.5
C.OutfieldWall = { C.OutfieldWall = {
{ x = 0, y = 137 }, { x = -400, y = -103 },
{ x = 233, y = 32 }, { x = -167, y = -208 },
{ x = 450, y = 29 }, { x = 50, y = -211 },
{ x = 550, y = 59 }, { x = 150, y = -181 },
{ x = 739, y = 64 }, { x = 339, y = -176 },
{ x = 850, y = 19 }, { x = 450, y = -221 },
{ x = 1100, y = 31 }, { x = 700, y = -209 },
{ x = 1185, y = 181 }, { x = 785, y = -59 },
{ x = 1201, y = 224 }, { x = 801, y = -16 },
} }
C.BottomOfOutfieldWall = {}
for i, v in ipairs(C.OutfieldWall) do
C.BottomOfOutfieldWall[i] = utils.xy(v.x, v.y + 40)
end
if not playdate then if not playdate then
return C return C
end end

View File

@ -80,7 +80,10 @@ end
---@return boolean inCatchingRange ---@return boolean inCatchingRange
local function updateFielderPosition(deltaSeconds, fielder, ballPos) local function updateFielderPosition(deltaSeconds, fielder, ballPos)
if fielder.target ~= nil then if fielder.target ~= nil then
if not utils.moveAtSpeed(fielder, fielder.speed * deltaSeconds, fielder.target) then if
utils.pointIsSquarelyAboveLine(fielder, C.BottomOfOutfieldWall)
or not utils.moveAtSpeed(fielder, fielder.speed * deltaSeconds, fielder.target)
then
fielder.target = nil fielder.target = nil
end end
end end

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -416,8 +416,7 @@ function Game:updateBatting(batDeg, batSpeed)
local ballDestX = self.state.ball.x + (ballVelX * C.BattingPower) local ballDestX = self.state.ball.x + (ballVelX * C.BattingPower)
local ballDestY = self.state.ball.y + (ballVelY * C.BattingPower) local ballDestY = self.state.ball.y + (ballVelY * C.BattingPower)
pitchTracker:reset() pitchTracker:reset()
local hitBallScaler = gfx.animator.new(2000, 9 + (mult * mult * 0.5), C.SmallestBallRadius, utils.easingHill) local flyTimeMs = 2000
self.state.ball:launch(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000, nil, hitBallScaler)
-- TODO? A dramatic eye-level view on a home-run could be sick. -- TODO? A dramatic eye-level view on a home-run could be sick.
local battingTeamStats = self:battingTeamCurrentInning() local battingTeamStats = self:battingTeamCurrentInning()
battingTeamStats.hits[#battingTeamStats.hits + 1] = utils.xy(ballDestX, ballDestY) battingTeamStats.hits[#battingTeamStats.hits + 1] = utils.xy(ballDestX, ballDestY)
@ -428,8 +427,21 @@ function Game:updateBatting(batDeg, batSpeed)
-- TODO: Have a fielder chase for the fly-out -- TODO: Have a fielder chase for the fly-out
return return
end end
self.baserunning:convertBatterToRunner()
if utils.pointIsSquarelyAboveLine(utils.xy(ballDestX, ballDestY), C.OutfieldWall) then
playdate.timer.new(flyTimeMs, function()
-- Verify that the home run wasn't intercepted
if utils.within(1, self.state.ball.x, ballDestX) and utils.within(1, self.state.ball.y, ballDestY) then
self.announcer:say("HOME RUN!")
self.state.offenseState = C.Offense.homeRun
end
end)
end
local hitBallScaler = gfx.animator.new(2000, 9 + (mult * mult * 0.5), C.SmallestBallRadius, utils.easingHill)
self.state.ball:launch(ballDestX, ballDestY, playdate.easingFunctions.outQuint, flyTimeMs, nil, hitBallScaler)
self.baserunning:convertBatterToRunner()
self.fielding:haveSomeoneChase(ballDestX, ballDestY) self.fielding:haveSomeoneChase(ballDestX, ballDestY)
end end
end end
@ -490,14 +502,7 @@ function Game:updateGameState()
end end
if self.state.secondsSincePitchAllowed > C.ReturnToPitcherAfterSeconds and not self.state.pitchIsOver then if self.state.secondsSincePitchAllowed > C.ReturnToPitcherAfterSeconds and not self.state.pitchIsOver then
local outcome = pitchTracker:updatePitchCounts(self.state.didSwing) local outcome = pitchTracker:updatePitchCounts(self.state.didSwing, self:fieldingTeamCurrentInning())
local currentPitchingStats = self:fieldingTeamCurrentInning().pitching
if outcome == PitchOutcomes.Strike or outcome == PitchOutcomes.StrikeOut then
currentPitchingStats.strikes = currentPitchingStats.strikes + 1
end
if outcome == PitchOutcomes.Ball or outcome == PitchOutcomes.Walk then
currentPitchingStats.balls = currentPitchingStats.balls + 1
end
if outcome == PitchOutcomes.StrikeOut then if outcome == PitchOutcomes.StrikeOut then
self:strikeOut() self:strikeOut()
elseif outcome == PitchOutcomes.Walk then elseif outcome == PitchOutcomes.Walk then
@ -560,6 +565,12 @@ function Game:updateGameState()
if not self:updateNonBatterRunners(C.WalkedRunnerSpeed, true) then if not self:updateNonBatterRunners(C.WalkedRunnerSpeed, true) then
self.state.offenseState = C.Offense.batting self.state.offenseState = C.Offense.batting
end end
elseif self.state.offenseState == C.Offense.homeRun then
self:updateNonBatterRunners(C.WalkedRunnerSpeed * 3, false)
if #self.baserunning.runners == 0 then
self.state.offenseState = C.Offense.batting
self.baserunning:pushNewBatter()
end
end end
local fielderHoldingBall = self.fielding:updateFielderPositions(self.state.ball, self.state.deltaSeconds) local fielderHoldingBall = self.fielding:updateFielderPositions(self.state.ball, self.state.deltaSeconds)

View File

@ -83,6 +83,10 @@ function utils.moveAtSpeed(mover, speed, target)
return false return false
end end
function utils.within(within, n1, n2)
return math.abs(n1 - n2) < within
end
---@generic T ---@generic T
---@param array T[] ---@param array T[]
---@param condition fun(T): boolean ---@param condition fun(T): boolean
@ -266,19 +270,24 @@ pitchTracker = {
end, end,
---@param didSwing boolean ---@param didSwing boolean
updatePitchCounts = function(self, didSwing) ---@param fieldingTeamInningData TeamInningData
updatePitchCounts = function(self, didSwing, fieldingTeamInningData)
if not self.recordedPitchX then if not self.recordedPitchX then
return return
end end
local currentPitchingStats = fieldingTeamInningData.pitching
if didSwing or self.recordedPitchX > C.StrikeZoneStartX and self.recordedPitchX < C.StrikeZoneEndX then if didSwing or self.recordedPitchX > C.StrikeZoneStartX and self.recordedPitchX < C.StrikeZoneEndX then
self.strikes = self.strikes + 1 self.strikes = self.strikes + 1
currentPitchingStats.strikes = currentPitchingStats.strikes + 1
if self.strikes >= 3 then if self.strikes >= 3 then
self:reset() self:reset()
return PitchOutcomes.StrikeOut return PitchOutcomes.StrikeOut
end end
else else
self.balls = self.balls + 1 self.balls = self.balls + 1
currentPitchingStats.balls = currentPitchingStats.balls + 1
if self.balls >= 4 then if self.balls >= 4 then
self:reset() self:reset()
return PitchOutcomes.Walk return PitchOutcomes.Walk