Sort runners/fielders by `y` for draw order
Pulled Fielding:drawFielders() back out into main.lua for this. Also switching some table-keyed enums to string literal types.
This commit is contained in:
parent
51855e13cf
commit
bbaaca4a2d
|
@ -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<Base, Base | nil>
|
||||
|
@ -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<string, OffenseState>
|
||||
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<string, Side>
|
||||
C.Sides = {
|
||||
offense = {},
|
||||
defense = {},
|
||||
}
|
||||
---@alias Side "offense" | "defense"
|
||||
|
||||
C.PitcherStartPos = {
|
||||
x = C.Screen.W * 0.48,
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
---@field fielderHoldingBall Fielder | nil
|
||||
Fielding = {}
|
||||
|
||||
local FielderDanceAnimator <const> = 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
|
||||
|
|
117
src/main.lua
117
src/main.lua
|
@ -46,10 +46,10 @@ import 'npc.lua'
|
|||
local gfx <const>, C <const> = playdate.graphics, C
|
||||
|
||||
---@alias Team { score: number, benchPosition: XyPair }
|
||||
---@type table<string, Team>
|
||||
---@type table<TeamId, Team>
|
||||
local teams <const> = {
|
||||
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 <const> = {
|
|||
},
|
||||
}
|
||||
|
||||
---@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 <const> = {
|
|||
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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue