diff --git a/src/constants.lua b/src/constants.lua
index a95877e..0cad138 100644
--- a/src/constants.lua
+++ b/src/constants.lua
@@ -31,7 +31,7 @@ C.FieldHeight = C.Bases[C.Home].y - C.Bases[C.Second].y
-- Pseudo-base for batter to target
C.RightHandedBattersBox = {
x = C.Bases[C.Home].x - 30,
- y = C.Bases[C.Home].y,
+ y = C.Bases[C.Home].y + 10,
}
---@type table
@@ -94,22 +94,16 @@ C.BatLength = 35
-- TODO: enums implemented this way are probably going to be difficult to serialize!
----@alias OffenseState table
+---@alias OffenseState "batting" | "running" | "walking"
--- An enum for what state the offense is in
---@type table
C.Offense = {
- batting = {},
- running = {},
- walking = {},
+ batting = "batting",
+ running = "running",
+ walking = "walking",
}
----@alias Side table
---- An enum for which side (offense or defense) a team is on.
----@type table
-C.Sides = {
- offense = {},
- defense = {},
-}
+---@alias Side "offense" | "defense"
C.PitcherStartPos = {
x = C.Screen.W * 0.48,
diff --git a/src/fielding.lua b/src/fielding.lua
index 942e668..6d2ae6f 100644
--- a/src/fielding.lua
+++ b/src/fielding.lua
@@ -13,7 +13,8 @@
---@field fielderHoldingBall Fielder | nil
Fielding = {}
-local FielderDanceAnimator = playdate.graphics.animator.new(1, 10, 0, utils.easingHill)
+-- selene: allow(unscoped_variables)
+FielderDanceAnimator = playdate.graphics.animator.new(1, 10, 0, utils.easingHill)
FielderDanceAnimator.repeatCount = C.DanceBounceCount - 1
---@param name string
@@ -187,18 +188,6 @@ function Fielding:celebrate()
FielderDanceAnimator:reset(C.DanceBounceMs)
end
----@param fielderSprites SpriteCollection
----@param ball Point3d
----@return boolean ballIsHeldByAFielder
-function Fielding:drawFielders(fielderSprites, ball)
- local ballIsHeld = false
- local danceOffset = FielderDanceAnimator:currentValue()
- for _, fielder in pairs(self.fielders) do
- ballIsHeld = drawFielder(fielderSprites, ball, fielder.x, fielder.y + danceOffset) or ballIsHeld
- end
- return ballIsHeld
-end
-
if not playdate or playdate.TEST_MODE then
return { Fielding, newFielder }
end
diff --git a/src/main.lua b/src/main.lua
index c67d019..be9d0f5 100644
--- a/src/main.lua
+++ b/src/main.lua
@@ -46,10 +46,10 @@ import 'npc.lua'
local gfx , C = playdate.graphics, C
---@alias Team { score: number, benchPosition: XyPair }
----@type table
+---@type table
local teams = {
home = {
- score = 0,
+ score = 0, -- TODO: Extract this last bit of global mutable state.
benchPosition = utils.xy(C.Screen.W + 10, C.Center.y),
},
away = {
@@ -58,17 +58,19 @@ local teams = {
},
}
+---@alias TeamId 'home' | 'away'
+
--- Well, maybe not "Settings", but passive state that probably won't change much, if at all, during a game.
---@class Settings
---@field finalInning number
----@field userTeam Team | nil
+---@field userTeam TeamId | nil
---@field awayTeamSprites SpriteCollection
---@field homeTeamSprites SpriteCollection
---@class MutableState
---@field deltaSeconds number
---@field ball Ball
----@field battingTeam Team
+---@field battingTeam TeamId
---@field catcherThrownBall boolean
---@field offenseState OffenseState
---@field inning number
@@ -104,20 +106,24 @@ Game = {}
---@param state MutableState | nil
---@return Game
function Game.new(settings, announcer, fielding, baserunning, npc, state)
+ teams.away.score = 0
+ teams.home.score = 0
announcer = announcer or Announcer.new()
fielding = fielding or Fielding.new()
- settings.userTeam = teams.away
+ settings.userTeam = nil -- "away"
local homeTeamBlipper = blipper.new(100, settings.homeTeamSprites.smiling, settings.homeTeamSprites.lowHat)
local awayTeamBlipper = blipper.new(100, settings.awayTeamSprites.smiling, settings.awayTeamSprites.lowHat)
- local battingTeam = teams.away
- local runnerBlipper = battingTeam == teams.away and awayTeamBlipper or homeTeamBlipper
+ local battingTeam = "away"
+ local runnerBlipper = battingTeam == "away" and awayTeamBlipper or homeTeamBlipper
local ball = Ball.new(gfx.animator)
local o = setmetatable({
settings = settings,
announcer = announcer,
fielding = fielding,
+ homeTeamBlipper = homeTeamBlipper,
+ awayTeamBlipper = awayTeamBlipper,
state = state or {
batBase = utils.xy(C.Center.x - 34, 215),
batTip = utils.xy(0, 0),
@@ -132,8 +138,6 @@ function Game.new(settings, announcer, fielding, baserunning, npc, state)
secondsSincePitchAllowed = 0,
battingTeamSprites = settings.awayTeamSprites,
fieldingTeamSprites = settings.homeTeamSprites,
- homeTeamBlipper = homeTeamBlipper,
- awayTeamBlipper = awayTeamBlipper,
runnerBlipper = runnerBlipper,
},
}, { __index = Game })
@@ -196,6 +200,27 @@ local Pitches = {
end,
}
+---@param teamId TeamId
+---@return TeamId
+local function getOppositeTeamId(teamId)
+ if teamId == "home" then
+ return "away"
+ elseif teamId == "away" then
+ return "home"
+ else
+ error("Unknown TeamId: " .. (teamId or "nil"))
+ end
+end
+
+function Game:getBattingTeam()
+ return teams[self.state.battingTeam] or error("Unknown battingTeam: " .. (self.state.battingTeam or "nil"))
+end
+
+function Game:getFieldingTeam()
+ return teams[getOppositeTeamId(self.state.battingTeam)]
+end
+
+---@param side Side
---@return boolean userIsOnSide, boolean userIsOnOtherSide
function Game:userIsOn(side)
if self.settings.userTeam == nil then
@@ -204,9 +229,9 @@ function Game:userIsOn(side)
end
local ret
if self.settings.userTeam == self.state.battingTeam then
- ret = side == C.Sides.offense
+ ret = side == "offense"
else
- ret = side == C.Sides.defense
+ ret = side == "defense"
end
return ret, not ret
end
@@ -242,12 +267,11 @@ end
function Game:nextHalfInning()
pitchTracker:reset()
- local currentlyFieldingTeam = self.state.battingTeam == teams.home and teams.away or teams.home
local gameOver = self.state.inning == self.settings.finalInning and teams.away.score ~= teams.home.score
if not gameOver then
self.fielding:celebrate()
self.state.secondsSinceLastRunnerMove = -7
- self.fielding:benchTo(currentlyFieldingTeam.benchPosition)
+ self.fielding:benchTo(self:getFieldingTeam().benchPosition)
self.announcer:say("SWITCHING SIDES...")
end
@@ -258,7 +282,7 @@ function Game:nextHalfInning()
if self.state.battingTeam == teams.home then
self.state.inning = self.state.inning + 1
end
- self.state.battingTeam = currentlyFieldingTeam
+ self.state.battingTeam = getOppositeTeamId(self.state.battingTeam)
playdate.timer.new(2000, function()
if self.state.battingTeam == teams.home then
self.state.battingTeamSprites = self.settings.homeTeamSprites
@@ -275,7 +299,8 @@ end
---@param scoredRunCount number
function Game:score(scoredRunCount)
- self.state.battingTeam.score = self.state.battingTeam.score + scoredRunCount
+ local batting = self:getBattingTeam()
+ batting.score = batting.score + scoredRunCount
self.announcer:say("SCORE!")
end
@@ -422,7 +447,7 @@ function Game:updateGameState()
self.state.ball:updatePosition()
- local userOnOffense, userOnDefense = self:userIsOn(C.Sides.offense)
+ local userOnOffense, userOnDefense = self:userIsOn("offense")
if userOnDefense then
throwMeter:applyCharge(self.state.deltaSeconds, crankLimited)
@@ -540,28 +565,54 @@ function Game:update()
GrassBackground:draw(-400, -240)
- local ballIsHeld = self.fielding:drawFielders(self.state.fieldingTeamSprites, ball)
+ ---@type { y: number, drawAction: fun() }[]
+ local characterDraws = {}
+ function addDraw(y, drawAction)
+ characterDraws[#characterDraws + 1] = { y = y, drawAction = drawAction }
+ end
+
+ local danceOffset = FielderDanceAnimator:currentValue()
+ local ballIsHeld = false
+ for _, fielder in pairs(self.fielding.fielders) do
+ addDraw(fielder.y + danceOffset, function()
+ ballIsHeld = drawFielder(
+ self.state.fieldingTeamSprites,
+ self.state.ball,
+ fielder.x,
+ fielder.y + danceOffset
+ ) or ballIsHeld
+ end)
+ end
+
+ local playerHeightOffset = 20
+ -- TODO? Scale sprites down as y increases
+ for _, runner in pairs(self.baserunning.runners) do
+ addDraw(runner.y, function()
+ if runner == self.baserunning.batter then
+ if self.state.batAngleDeg > 50 and self.state.batAngleDeg < 200 then
+ self.state.battingTeamSprites.back:draw(runner.x, runner.y - playerHeightOffset)
+ else
+ self.state.battingTeamSprites.smiling:draw(runner.x, runner.y - playerHeightOffset)
+ end
+ else
+ -- TODO? Change blip speed depending on runner speed?
+ self.state.runnerBlipper:draw(false, runner.x, runner.y - playerHeightOffset)
+ end
+ end)
+ end
+
+ table.sort(characterDraws, function(a, b)
+ return a.y < b.y
+ end)
+ for _, character in pairs(characterDraws) do
+ character.drawAction()
+ end
if self.state.offenseState == C.Offense.batting then
gfx.setLineWidth(5)
gfx.drawLine(self.state.batBase.x, self.state.batBase.y, self.state.batTip.x, self.state.batTip.y)
end
- local playerHeightOffset = 10
- -- TODO? Scale sprites down as y increases
- for _, runner in pairs(self.baserunning.runners) do
- if runner == self.baserunning.batter then
- if self.state.batAngleDeg > 50 and self.state.batAngleDeg < 200 then
- self.state.battingTeamSprites.back:draw(runner.x, runner.y - playerHeightOffset)
- else
- self.state.battingTeamSprites.smiling:draw(runner.x, runner.y - playerHeightOffset)
- end
- else
- -- TODO? Change blip speed depending on runner speed?
- self.state.runnerBlipper:draw(false, runner.x, runner.y - playerHeightOffset)
- end
- end
-
for _, runner in pairs(self.baserunning.outRunners) do
self.state.battingTeamSprites.frowning:draw(runner.x, runner.y - playerHeightOffset)
end
@@ -580,7 +631,7 @@ function Game:update()
if math.abs(offsetX) > 10 or math.abs(offsetY) > 10 then
drawMinimap(self.baserunning.runners, self.fielding.fielders)
end
- drawScoreboard(0, C.Screen.H * 0.77, teams, self.baserunning.outs, self.state.battingTeam, self.state.inning)
+ drawScoreboard(0, C.Screen.H * 0.77, teams, self.baserunning.outs, self:getBattingTeam(), self.state.inning)
drawBallsAndStrikes(290, C.Screen.H - 20, pitchTracker.balls, pitchTracker.strikes)
self.announcer:draw(C.Center.x, 10)
diff --git a/src/npc.lua b/src/npc.lua
index 781ce3c..812be91 100644
--- a/src/npc.lua
+++ b/src/npc.lua
@@ -27,7 +27,7 @@ function Npc:updateBatAngle(ball, catcherThrownBall, deltaSec)
npcBatDeg = npcBatDeg + (deltaSec * npcBatSpeed)
else
npcBatSpeed = (1 + math.random()) * BaseNpcBatSpeed
- npcBatDeg = 200
+ npcBatDeg = 230
end
return npcBatDeg
end