Compare commits
3 Commits
6007ac971f
...
4c9fbcdee7
Author | SHA1 | Date |
---|---|---|
|
4c9fbcdee7 | |
|
1ccf8765ee | |
|
5c45b7bba0 |
|
@ -44,6 +44,7 @@ function Announcer:popIn()
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param text string
|
||||||
function Announcer:say(text)
|
function Announcer:say(text)
|
||||||
self.textQueue[#self.textQueue + 1] = text
|
self.textQueue[#self.textQueue + 1] = text
|
||||||
if #self.textQueue == 1 then
|
if #self.textQueue == 1 then
|
||||||
|
|
|
@ -8,6 +8,8 @@ Glove = playdate.graphics.image.new("images/game/Glove.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
PlayerFrown = playdate.graphics.image.new("images/game/PlayerFrown.png")
|
PlayerFrown = playdate.graphics.image.new("images/game/PlayerFrown.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
BigBat = playdate.graphics.image.new("images/game/BigBat.png")
|
||||||
|
-- luacheck: ignore
|
||||||
GloveHoldingBall = playdate.graphics.image.new("images/game/GloveHoldingBall.png")
|
GloveHoldingBall = playdate.graphics.image.new("images/game/GloveHoldingBall.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
GameLogo = playdate.graphics.image.new("images/game/GameLogo.png")
|
GameLogo = playdate.graphics.image.new("images/game/GameLogo.png")
|
||||||
|
@ -24,6 +26,8 @@ Minimap = playdate.graphics.image.new("images/game/Minimap.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
GrassBackground = playdate.graphics.image.new("images/game/GrassBackground.png")
|
GrassBackground = playdate.graphics.image.new("images/game/GrassBackground.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
|
GrassBackgroundSmall = playdate.graphics.image.new("images/game/GrassBackgroundSmall.png")
|
||||||
|
-- luacheck: ignore
|
||||||
LightPlayerBase = playdate.graphics.image.new("images/game/LightPlayerBase.png")
|
LightPlayerBase = playdate.graphics.image.new("images/game/LightPlayerBase.png")
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
LightPlayerBack = playdate.graphics.image.new("images/game/LightPlayerBack.png")
|
LightPlayerBack = playdate.graphics.image.new("images/game/LightPlayerBack.png")
|
||||||
|
|
|
@ -91,8 +91,6 @@ C.SmallestBallRadius = 6
|
||||||
|
|
||||||
C.BatLength = 35
|
C.BatLength = 35
|
||||||
|
|
||||||
-- TODO: enums implemented this way are probably going to be difficult to serialize!
|
|
||||||
|
|
||||||
---@alias OffenseState "batting" | "running" | "walking"
|
---@alias OffenseState "batting" | "running" | "walking"
|
||||||
--- An enum for what state the offense is in
|
--- An enum for what state the offense is in
|
||||||
---@type table<string, OffenseState>
|
---@type table<string, OffenseState>
|
||||||
|
@ -125,6 +123,18 @@ C.WalkedRunnerSpeed = 10
|
||||||
|
|
||||||
C.ResetFieldersAfterSeconds = 2.5
|
C.ResetFieldersAfterSeconds = 2.5
|
||||||
|
|
||||||
|
C.OutfieldWall = {
|
||||||
|
{ x = 0, y = 137 },
|
||||||
|
{ x = 233, y = 32 },
|
||||||
|
{ x = 450, y = 29 },
|
||||||
|
{ x = 550, y = 59 },
|
||||||
|
{ x = 739, y = 64 },
|
||||||
|
{ x = 850, y = 19 },
|
||||||
|
{ x = 1100, y = 31 },
|
||||||
|
{ x = 1185, y = 181 },
|
||||||
|
{ x = 1201, y = 224 },
|
||||||
|
}
|
||||||
|
|
||||||
if not playdate then
|
if not playdate then
|
||||||
return C
|
return C
|
||||||
end
|
end
|
||||||
|
|
61
src/dbg.lua
|
@ -35,6 +35,67 @@ function dbg.loadTheBases(br)
|
||||||
br.runners[4].nextBase = C.Bases[C.Home]
|
br.runners[4].nextBase = C.Bases[C.Home]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local hitSamples = {
|
||||||
|
away = {
|
||||||
|
{
|
||||||
|
utils.xy(7.88733, -16.3434),
|
||||||
|
utils.xy(378.3376, 30.49521),
|
||||||
|
utils.xy(367.1036, 21.55336),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
utils.xy(379.8051, -40.82794),
|
||||||
|
utils.xy(-444.5791, -30.30901),
|
||||||
|
utils.xy(-30.43079, -30.50307),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
utils.xy(227.8881, -14.56854),
|
||||||
|
utils.xy(293.5208, 39.38919),
|
||||||
|
utils.xy(154.4738, -26.55899),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
home = {
|
||||||
|
{
|
||||||
|
utils.xy(146.2505, -89.12155),
|
||||||
|
utils.xy(429.5428, 59.62944),
|
||||||
|
utils.xy(272.4666, -78.578),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
utils.xy(485.0516, 112.8341),
|
||||||
|
utils.xy(290.9232, -4.946442),
|
||||||
|
utils.xy(263.4262, -6.482407),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
utils.xy(260.6927, -63.63049),
|
||||||
|
utils.xy(392.1548, -44.22421),
|
||||||
|
utils.xy(482.5545, 105.3476),
|
||||||
|
utils.xy(125.5928, 18.53091),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
---@return Statistics
|
||||||
|
function dbg.mockStatistics(inningCount)
|
||||||
|
inningCount = inningCount or 9
|
||||||
|
local stats = Statistics.new()
|
||||||
|
for i = 1, inningCount - 1 do
|
||||||
|
stats.innings[i].home.score = math.floor(math.random() * 5)
|
||||||
|
stats.innings[i].away.score = math.floor(math.random() * 5)
|
||||||
|
if hitSamples.home[i] ~= nil then
|
||||||
|
stats.innings[i].home.hits = hitSamples.home[i]
|
||||||
|
end
|
||||||
|
if hitSamples.away[i] ~= nil then
|
||||||
|
stats.innings[i].away.hits = hitSamples.away[i]
|
||||||
|
end
|
||||||
|
stats:pushInning()
|
||||||
|
end
|
||||||
|
|
||||||
|
local homeScore, awayScore = utils.totalScores(stats)
|
||||||
|
if homeScore == awayScore then
|
||||||
|
stats.innings[#stats.innings].home.score = 1 + stats.innings[#stats.innings].home.score
|
||||||
|
end
|
||||||
|
return stats
|
||||||
|
end
|
||||||
|
|
||||||
---@param points XyPair[]
|
---@param points XyPair[]
|
||||||
function dbg.drawLine(points)
|
function dbg.drawLine(points)
|
||||||
for i = 2, #points do
|
for i = 2, #points do
|
||||||
|
|
|
@ -0,0 +1,248 @@
|
||||||
|
---@alias TeamInningData { score: number, pitching: { balls: number, strikes: number }, hits: XyPair[] }
|
||||||
|
|
||||||
|
--- E.g. statistics[1].home.pitching.balls
|
||||||
|
---@class Statistics
|
||||||
|
---@field innings: (table<TeamId, TeamInningData>)[]
|
||||||
|
|
||||||
|
Statistics = {}
|
||||||
|
|
||||||
|
local function newTeamInning()
|
||||||
|
return {
|
||||||
|
score = 0,
|
||||||
|
pitching = {
|
||||||
|
balls = 0,
|
||||||
|
strikes = 0,
|
||||||
|
},
|
||||||
|
hits = {},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return table<TeamId, TeamInningData>
|
||||||
|
local function newInning()
|
||||||
|
return {
|
||||||
|
home = newTeamInning(),
|
||||||
|
away = newTeamInning(),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return Statistics
|
||||||
|
function Statistics.new()
|
||||||
|
return setmetatable({
|
||||||
|
innings = { newInning() },
|
||||||
|
}, { __index = Statistics })
|
||||||
|
end
|
||||||
|
|
||||||
|
function Statistics:pushInning()
|
||||||
|
self.innings[#self.innings + 1] = newInning()
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class BoxScore
|
||||||
|
---@field stats Statistics
|
||||||
|
---@field private targetY number
|
||||||
|
BoxScore = {}
|
||||||
|
|
||||||
|
---@param stats Statistics
|
||||||
|
function BoxScore.new(stats)
|
||||||
|
return setmetatable({
|
||||||
|
stats = stats,
|
||||||
|
targetY = 0,
|
||||||
|
}, { __index = BoxScore })
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: Convert the box-score into a whole "scene"
|
||||||
|
-- * Scroll left and right through games that go into extra innings
|
||||||
|
-- * Scroll up and down through other stats.
|
||||||
|
-- + Balls and strikes
|
||||||
|
-- + Batting average
|
||||||
|
-- + Graph of team scores over time
|
||||||
|
-- + Farthest hit ball
|
||||||
|
|
||||||
|
local MarginY <const> = 70
|
||||||
|
|
||||||
|
local SmallFont <const> = playdate.graphics.font.new("fonts/font-full-circle.pft")
|
||||||
|
local ScoreFont <const> = playdate.graphics.font.new("fonts/Asheville-Sans-14-Bold.pft")
|
||||||
|
local NumWidth <const> = ScoreFont:getTextWidth("0")
|
||||||
|
local NumHeight <const> = ScoreFont:getHeight()
|
||||||
|
local AwayWidth <const> = ScoreFont:getTextWidth("AWAY")
|
||||||
|
local InningMargin = 4
|
||||||
|
|
||||||
|
local InningDrawWidth <const> = (InningMargin * 2) + (NumWidth * 2)
|
||||||
|
local ScoreDrawHeight = NumHeight * 2
|
||||||
|
|
||||||
|
-- luacheck: ignore 143
|
||||||
|
---@type pd_graphics_lib
|
||||||
|
local gfx = playdate.graphics
|
||||||
|
|
||||||
|
local function formatScore(n)
|
||||||
|
if n <= 9 then
|
||||||
|
return " " .. n
|
||||||
|
elseif n <= 19 then
|
||||||
|
return " " .. n
|
||||||
|
else
|
||||||
|
return tostring(n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local HomeY <const> = -4 + (NumHeight * 2) + MarginY
|
||||||
|
local AwayY <const> = -4 + (NumHeight * 3) + MarginY
|
||||||
|
|
||||||
|
local function drawInning(x, inningNumber, homeScore, awayScore)
|
||||||
|
gfx.setColor(gfx.kColorBlack)
|
||||||
|
gfx.setColor(gfx.kColorWhite)
|
||||||
|
gfx.setLineWidth(1)
|
||||||
|
gfx.drawRect(x, 34 + MarginY, InningDrawWidth, ScoreDrawHeight)
|
||||||
|
|
||||||
|
inningNumber = " " .. inningNumber
|
||||||
|
homeScore = formatScore(homeScore)
|
||||||
|
awayScore = formatScore(awayScore)
|
||||||
|
|
||||||
|
x = x - 8 + (InningDrawWidth / 2)
|
||||||
|
ScoreFont:drawTextAligned(inningNumber, x, -4 + NumHeight + MarginY, gfx.kAlignRight)
|
||||||
|
ScoreFont:drawTextAligned(awayScore, x, HomeY, gfx.kAlignRight)
|
||||||
|
ScoreFont:drawTextAligned(homeScore, x, AwayY, gfx.kAlignRight)
|
||||||
|
end
|
||||||
|
|
||||||
|
function BoxScore:drawBoxScore()
|
||||||
|
local inningStart = 4 + (AwayWidth * 1.5)
|
||||||
|
local widthAndMarg = InningDrawWidth + 4
|
||||||
|
ScoreFont:drawTextAligned(" HOME", 10, HomeY, gfx.kAlignRight)
|
||||||
|
ScoreFont:drawTextAligned("AWAY", 10, AwayY, gfx.kAlignRight)
|
||||||
|
for i = 1, #self.stats.innings do
|
||||||
|
local inningStats = self.stats.innings[i]
|
||||||
|
drawInning(inningStart + ((i - 1) * widthAndMarg), i, inningStats.home.score, inningStats.away.score)
|
||||||
|
end
|
||||||
|
local homeScore, awayScore = utils.totalScores(self.stats)
|
||||||
|
drawInning(4 + inningStart + (widthAndMarg * #self.stats.innings), "F", homeScore, awayScore)
|
||||||
|
ScoreFont:drawTextAligned("v", C.Center.x, C.Screen.H - 40, gfx.kAlignCenter)
|
||||||
|
end
|
||||||
|
|
||||||
|
local GraphM = 10
|
||||||
|
local GraphW = C.Screen.W - (GraphM * 2)
|
||||||
|
local GraphH = C.Screen.H - (GraphM * 2)
|
||||||
|
|
||||||
|
function BoxScore:drawScoreGraph(y)
|
||||||
|
-- TODO: Actually draw score legend
|
||||||
|
|
||||||
|
-- Offset by 2 to support a) the zero-index b) the score legend
|
||||||
|
local segmentWidth = GraphW / (#self.stats.innings + 2)
|
||||||
|
|
||||||
|
local legendX = segmentWidth * (#self.stats.innings + 2) - GraphM
|
||||||
|
gfx.drawLine(GraphM / 2, y + GraphM + GraphH, legendX, y + GraphM + GraphH)
|
||||||
|
gfx.drawLine(legendX, y + GraphM, legendX, y + GraphH + GraphM)
|
||||||
|
|
||||||
|
gfx.setLineWidth(3)
|
||||||
|
local homeScore, awayScore = utils.totalScores(self.stats)
|
||||||
|
local highestScore = math.max(homeScore, awayScore)
|
||||||
|
|
||||||
|
local heightPerPoint = (GraphH - 6) / highestScore
|
||||||
|
|
||||||
|
function point(inning, score)
|
||||||
|
return utils.xy(GraphM + (inning * segmentWidth), y + GraphM + GraphH + (score * -heightPerPoint))
|
||||||
|
end
|
||||||
|
|
||||||
|
function drawLine(teamId)
|
||||||
|
local linePoints = { point(0, 0) }
|
||||||
|
local scoreTotal = 0
|
||||||
|
for i, inning in ipairs(self.stats.innings) do
|
||||||
|
scoreTotal = scoreTotal + inning[teamId].score
|
||||||
|
linePoints[#linePoints + 1] = point(i, scoreTotal)
|
||||||
|
end
|
||||||
|
dbg.drawLine(linePoints)
|
||||||
|
local finalPoint = linePoints[#linePoints]
|
||||||
|
SmallFont:drawTextAligned(string.upper(teamId), finalPoint.x + 3, finalPoint.y - 7, gfx.kAlignRight)
|
||||||
|
end
|
||||||
|
|
||||||
|
drawLine("home")
|
||||||
|
gfx.setDitherPattern(0.5)
|
||||||
|
drawLine("away")
|
||||||
|
gfx.setDitherPattern(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param realHit XyPair
|
||||||
|
---@return XyPair
|
||||||
|
function convertHitToMini(realHit)
|
||||||
|
-- Convert to all-positive y
|
||||||
|
local y = realHit.y + C.Screen.H
|
||||||
|
y = y / 2
|
||||||
|
|
||||||
|
local x = realHit.x + C.Screen.W
|
||||||
|
x = x / 3
|
||||||
|
return utils.xy(x, y)
|
||||||
|
end
|
||||||
|
|
||||||
|
function BoxScore:drawHitChart(y)
|
||||||
|
local leftMargin = 8
|
||||||
|
GrassBackgroundSmall:drawCentered(C.Center.x, y + C.Center.y + 54)
|
||||||
|
gfx.setLineWidth(1)
|
||||||
|
ScoreFont:drawTextAligned("AWAY", leftMargin, y + C.Screen.H - NumHeight, gfx.kAlignRight)
|
||||||
|
gfx.setColor(gfx.kColorBlack)
|
||||||
|
gfx.setDitherPattern(0.5, gfx.image.kDitherTypeBayer2x2)
|
||||||
|
gfx.fillRect(leftMargin, y + C.Screen.H - NumHeight, ScoreFont:getTextWidth("AWAY"), NumHeight)
|
||||||
|
|
||||||
|
gfx.setColor(gfx.kColorWhite)
|
||||||
|
gfx.setDitherPattern(0.5)
|
||||||
|
for _, inning in ipairs(self.stats.innings) do
|
||||||
|
for _, hit in ipairs(inning.away.hits) do
|
||||||
|
local miniHitPos = convertHitToMini(hit)
|
||||||
|
gfx.fillCircleAtPoint(miniHitPos.x + 10, miniHitPos.y + y, 4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
gfx.setColor(gfx.kColorWhite)
|
||||||
|
gfx.setDitherPattern(0)
|
||||||
|
ScoreFont:drawTextAligned(" HOME", leftMargin, y + C.Screen.H - (NumHeight * 2), gfx.kAlignRight)
|
||||||
|
for _, inning in ipairs(self.stats.innings) do
|
||||||
|
for _, hit in ipairs(inning.home.hits) do
|
||||||
|
local miniHitPos = convertHitToMini(hit)
|
||||||
|
gfx.fillCircleAtPoint(miniHitPos.x + 10, miniHitPos.y + y, 4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local screens = {
|
||||||
|
BoxScore.drawBoxScore,
|
||||||
|
BoxScore.drawScoreGraph,
|
||||||
|
BoxScore.drawHitChart,
|
||||||
|
}
|
||||||
|
|
||||||
|
function BoxScore:render()
|
||||||
|
local originalDrawMode = gfx.getImageDrawMode()
|
||||||
|
gfx.clear(gfx.kColorBlack)
|
||||||
|
gfx.setImageDrawMode(gfx.kDrawModeInverted)
|
||||||
|
gfx.setColor(gfx.kColorBlack)
|
||||||
|
|
||||||
|
for i, screen in ipairs(screens) do
|
||||||
|
screen(self, (i - 1) * C.Screen.H)
|
||||||
|
end
|
||||||
|
|
||||||
|
gfx.setImageDrawMode(originalDrawMode)
|
||||||
|
end
|
||||||
|
|
||||||
|
local renderedImage
|
||||||
|
|
||||||
|
function BoxScore:update()
|
||||||
|
if not renderedImage then
|
||||||
|
renderedImage = gfx.image.new(C.Screen.W, C.Screen.H * #screens)
|
||||||
|
gfx.pushContext(renderedImage)
|
||||||
|
self:render()
|
||||||
|
gfx.popContext()
|
||||||
|
end
|
||||||
|
|
||||||
|
local deltaSeconds = playdate.getElapsedTime()
|
||||||
|
playdate.resetElapsedTime()
|
||||||
|
|
||||||
|
gfx.setDrawOffset(0, self.targetY)
|
||||||
|
renderedImage:draw(0, 0)
|
||||||
|
|
||||||
|
local crankChange = playdate.getCrankChange()
|
||||||
|
if crankChange ~= 0 then
|
||||||
|
self.targetY = self.targetY - (crankChange * 0.8)
|
||||||
|
else
|
||||||
|
local closestScreen = math.floor(0.5 + (self.targetY / C.Screen.H)) * C.Screen.H
|
||||||
|
if math.abs(self.targetY - closestScreen) > 3 then
|
||||||
|
local needsIncrease = self.targetY < closestScreen
|
||||||
|
local change = needsIncrease and 200 * deltaSeconds or -200 * deltaSeconds
|
||||||
|
self.targetY = self.targetY + change
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.targetY = math.max(math.min(self.targetY, 0), -C.Screen.H * (#screens - 1))
|
||||||
|
end
|
|
@ -83,11 +83,10 @@ local ScoreboardHeight <const> = 55
|
||||||
local Indicator = "> "
|
local Indicator = "> "
|
||||||
local IndicatorWidth <const> = ScoreFont:getTextWidth(Indicator)
|
local IndicatorWidth <const> = ScoreFont:getTextWidth(Indicator)
|
||||||
|
|
||||||
---@param teams any
|
|
||||||
---@param battingTeam any
|
---@param battingTeam any
|
||||||
---@return string, number, string, number
|
---@return string, number, string, number
|
||||||
function getIndicators(teams, battingTeam)
|
function getIndicators(battingTeam)
|
||||||
if teams.home == battingTeam then
|
if battingTeam == "home" then
|
||||||
return Indicator, 0, "", IndicatorWidth
|
return Indicator, 0, "", IndicatorWidth
|
||||||
end
|
end
|
||||||
return "", IndicatorWidth, Indicator, 0
|
return "", IndicatorWidth, Indicator, 0
|
||||||
|
@ -101,11 +100,11 @@ local stats = {
|
||||||
battingTeam = nil,
|
battingTeam = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawScoreboardImpl(x, y, teams)
|
function drawScoreboardImpl(x, y)
|
||||||
local homeScore = stats.homeScore
|
local homeScore = stats.homeScore
|
||||||
local awayScore = stats.awayScore
|
local awayScore = stats.awayScore
|
||||||
|
|
||||||
local homeIndicator, homeOffset, awayIndicator, awayOffset = getIndicators(teams, stats.battingTeam)
|
local homeIndicator, homeOffset, awayIndicator, awayOffset = getIndicators(stats.battingTeam)
|
||||||
|
|
||||||
local homeScoreText = homeIndicator .. "HOME " .. (homeScore > 9 and homeScore or " " .. homeScore)
|
local homeScoreText = homeIndicator .. "HOME " .. (homeScore > 9 and homeScore or " " .. homeScore)
|
||||||
local awayScoreText = awayIndicator .. "AWAY " .. (awayScore > 9 and awayScore or " " .. awayScore)
|
local awayScoreText = awayIndicator .. "AWAY " .. (awayScore > 9 and awayScore or " " .. awayScore)
|
||||||
|
@ -146,17 +145,17 @@ end
|
||||||
|
|
||||||
local newStats = stats
|
local newStats = stats
|
||||||
|
|
||||||
function drawScoreboard(x, y, teams, outs, battingTeam, inning)
|
function drawScoreboard(x, y, homeScore, awayScore, outs, battingTeam, inning)
|
||||||
if
|
if
|
||||||
newStats.homeScore ~= teams.home.score
|
newStats.homeScore ~= homeScore
|
||||||
or newStats.awayScore ~= teams.away.score
|
or newStats.awayScore ~= awayScore
|
||||||
or newStats.outs ~= outs
|
or newStats.outs ~= outs
|
||||||
or newStats.inning ~= inning
|
or newStats.inning ~= inning
|
||||||
or newStats.battingTeam ~= battingTeam
|
or newStats.battingTeam ~= battingTeam
|
||||||
then
|
then
|
||||||
newStats = {
|
newStats = {
|
||||||
homeScore = teams.home.score,
|
homeScore = homeScore,
|
||||||
awayScore = teams.away.score,
|
awayScore = awayScore,
|
||||||
outs = outs,
|
outs = outs,
|
||||||
inning = inning,
|
inning = inning,
|
||||||
battingTeam = battingTeam,
|
battingTeam = battingTeam,
|
||||||
|
@ -165,5 +164,5 @@ function drawScoreboard(x, y, teams, outs, battingTeam, inning)
|
||||||
stats = newStats
|
stats = newStats
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
drawScoreboardImpl(x, y, teams)
|
drawScoreboardImpl(x, y)
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
Transitions = {
|
||||||
|
---@type Scene | nil
|
||||||
|
nextScene = nil,
|
||||||
|
---@type Scene | nil
|
||||||
|
previousScene = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
local gfx = playdate.graphics
|
||||||
|
|
||||||
|
local previousSceneImage
|
||||||
|
local previousSceneMask
|
||||||
|
local nextSceneImage
|
||||||
|
|
||||||
|
local batImageTable = {}
|
||||||
|
|
||||||
|
local batOffset = 80
|
||||||
|
local degStep = 3
|
||||||
|
|
||||||
|
function loadBatImageTable()
|
||||||
|
for deg = 90 - (degStep * 3), 270 + (degStep * 3), degStep do
|
||||||
|
local img = gfx.image.new(C.Screen.W, C.Screen.H)
|
||||||
|
gfx.pushContext(img)
|
||||||
|
BigBat:drawRotated(C.Center.x, C.Screen.H + batOffset, 90 + deg)
|
||||||
|
gfx.popContext()
|
||||||
|
batImageTable[deg] = img
|
||||||
|
end
|
||||||
|
end
|
||||||
|
loadBatImageTable()
|
||||||
|
|
||||||
|
local function update()
|
||||||
|
local lastAngle
|
||||||
|
local seamAngle = math.rad(270)
|
||||||
|
while seamAngle > math.rad(90) do
|
||||||
|
local deltaSeconds = playdate.getElapsedTime()
|
||||||
|
playdate.resetElapsedTime()
|
||||||
|
seamAngle = seamAngle - (deltaSeconds * 3)
|
||||||
|
local seamAngleDeg = math.floor(math.deg(seamAngle))
|
||||||
|
seamAngleDeg = seamAngleDeg - (seamAngleDeg % degStep)
|
||||||
|
|
||||||
|
-- Skip re-drawing if no change
|
||||||
|
if lastAngle ~= seamAngleDeg then
|
||||||
|
lastAngle = seamAngleDeg
|
||||||
|
nextSceneImage:draw(0, 0)
|
||||||
|
|
||||||
|
gfx.pushContext(previousSceneMask)
|
||||||
|
gfx.setImageDrawMode(gfx.kDrawModeFillBlack)
|
||||||
|
batImageTable[seamAngleDeg]:draw(0, 0)
|
||||||
|
gfx.popContext()
|
||||||
|
|
||||||
|
previousSceneImage:setMaskImage(previousSceneMask)
|
||||||
|
previousSceneImage:draw(0, 0)
|
||||||
|
batImageTable[seamAngleDeg]:draw(0, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
coroutine.yield()
|
||||||
|
end
|
||||||
|
playdate.update = function()
|
||||||
|
Transitions.nextScene:update()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param nextScene fun() The next playdate.update function
|
||||||
|
function transitionTo(nextScene)
|
||||||
|
if not Transitions.nextScene then
|
||||||
|
error("Expected Transitions to already have nextScene defined! E.g. by calling transitionBetween")
|
||||||
|
end
|
||||||
|
local previousScene = Transitions.nextScene
|
||||||
|
transitionBetween(previousScene, nextScene)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param previousScene Scene Has the current playdate.update function
|
||||||
|
---@param nextScene Scene Has the next playdate.update function
|
||||||
|
function transitionBetween(previousScene, nextScene)
|
||||||
|
playdate.wait(2) -- TODO: There's some sort of timing wack here.
|
||||||
|
playdate.update = update
|
||||||
|
|
||||||
|
previousSceneImage = gfx.image.new(C.Screen.W, C.Screen.H)
|
||||||
|
gfx.pushContext(previousSceneImage)
|
||||||
|
previousScene:update()
|
||||||
|
gfx.popContext()
|
||||||
|
|
||||||
|
nextSceneImage = gfx.image.new(C.Screen.W, C.Screen.H)
|
||||||
|
gfx.pushContext(nextSceneImage)
|
||||||
|
nextScene:update()
|
||||||
|
gfx.popContext()
|
||||||
|
|
||||||
|
previousSceneMask = gfx.image.new(C.Screen.W, C.Screen.H, gfx.kColorWhite)
|
||||||
|
previousSceneImage:setMaskImage(previousSceneMask)
|
||||||
|
|
||||||
|
Transitions.nextScene = nextScene
|
||||||
|
Transitions.previousScene = previousScene
|
||||||
|
end
|
|
@ -5,8 +5,6 @@
|
||||||
--- @field target XyPair | nil
|
--- @field target XyPair | nil
|
||||||
--- @field speed number
|
--- @field speed number
|
||||||
|
|
||||||
-- TODO: Run down baserunners in a pickle.
|
|
||||||
|
|
||||||
---@class Fielding
|
---@class Fielding
|
||||||
---@field fielders table<string, Fielder>
|
---@field fielders table<string, Fielder>
|
||||||
---@field fielderHoldingBall Fielder | nil
|
---@field fielderHoldingBall Fielder | nil
|
||||||
|
@ -182,7 +180,7 @@ function Fielding:userThrowTo(targetBase, ball, throwFlyMs)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Fielding:celebrate()
|
function Fielding.celebrate()
|
||||||
FielderDanceAnimator:reset(C.DanceBounceMs)
|
FielderDanceAnimator:reset(C.DanceBounceMs)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,295 @@
|
||||||
|
tracking=1
|
||||||
|
space 3
|
||||||
|
! 2
|
||||||
|
" 5
|
||||||
|
# 9
|
||||||
|
$ 8
|
||||||
|
% 12
|
||||||
|
& 11
|
||||||
|
' 3
|
||||||
|
( 5
|
||||||
|
) 5
|
||||||
|
* 8
|
||||||
|
+ 8
|
||||||
|
, 3
|
||||||
|
- 6
|
||||||
|
. 2
|
||||||
|
/ 6
|
||||||
|
0 9
|
||||||
|
1 4
|
||||||
|
2 9
|
||||||
|
3 9
|
||||||
|
4 9
|
||||||
|
5 9
|
||||||
|
6 9
|
||||||
|
7 9
|
||||||
|
8 10
|
||||||
|
9 9
|
||||||
|
: 2
|
||||||
|
; 2
|
||||||
|
< 7
|
||||||
|
= 7
|
||||||
|
> 7
|
||||||
|
? 9
|
||||||
|
@ 11
|
||||||
|
A 10
|
||||||
|
B 9
|
||||||
|
C 9
|
||||||
|
D 9
|
||||||
|
E 8
|
||||||
|
F 8
|
||||||
|
G 9
|
||||||
|
H 9
|
||||||
|
I 2
|
||||||
|
J 8
|
||||||
|
K 10
|
||||||
|
L 9
|
||||||
|
M 12
|
||||||
|
N 9
|
||||||
|
O 9
|
||||||
|
P 9
|
||||||
|
Q 9
|
||||||
|
R 9
|
||||||
|
S 9
|
||||||
|
T 10
|
||||||
|
U 9
|
||||||
|
V 10
|
||||||
|
W 14
|
||||||
|
X 8
|
||||||
|
Y 8
|
||||||
|
Z 8
|
||||||
|
[ 3
|
||||||
|
\ 6
|
||||||
|
] 3
|
||||||
|
^ 6
|
||||||
|
_ 8
|
||||||
|
` 3
|
||||||
|
a 8
|
||||||
|
b 8
|
||||||
|
c 8
|
||||||
|
d 8
|
||||||
|
e 8
|
||||||
|
f 6
|
||||||
|
g 8
|
||||||
|
h 8
|
||||||
|
i 2
|
||||||
|
j 4
|
||||||
|
k 8
|
||||||
|
l 2
|
||||||
|
m 12
|
||||||
|
n 8
|
||||||
|
o 8
|
||||||
|
p 8
|
||||||
|
q 8
|
||||||
|
r 6
|
||||||
|
s 8
|
||||||
|
t 6
|
||||||
|
u 8
|
||||||
|
v 8
|
||||||
|
w 12
|
||||||
|
x 9
|
||||||
|
y 8
|
||||||
|
z 8
|
||||||
|
{ 6
|
||||||
|
| 2
|
||||||
|
} 6
|
||||||
|
~ 10
|
||||||
|
… 8
|
||||||
|
¥ 8
|
||||||
|
‼ 5
|
||||||
|
™ 8
|
||||||
|
© 11
|
||||||
|
® 11
|
||||||
|
。 16
|
||||||
|
、 16
|
||||||
|
ぁ 16
|
||||||
|
あ 16
|
||||||
|
ぃ 16
|
||||||
|
い 16
|
||||||
|
ぅ 16
|
||||||
|
う 16
|
||||||
|
ぇ 16
|
||||||
|
え 16
|
||||||
|
ぉ 16
|
||||||
|
お 16
|
||||||
|
か 16
|
||||||
|
が 16
|
||||||
|
き 16
|
||||||
|
ぎ 16
|
||||||
|
く 16
|
||||||
|
ぐ 16
|
||||||
|
け 16
|
||||||
|
げ 16
|
||||||
|
こ 16
|
||||||
|
ご 16
|
||||||
|
さ 16
|
||||||
|
ざ 16
|
||||||
|
し 16
|
||||||
|
じ 16
|
||||||
|
す 16
|
||||||
|
ず 16
|
||||||
|
せ 16
|
||||||
|
ぜ 16
|
||||||
|
そ 16
|
||||||
|
ぞ 16
|
||||||
|
た 16
|
||||||
|
だ 16
|
||||||
|
ち 16
|
||||||
|
ぢ 16
|
||||||
|
っ 16
|
||||||
|
つ 16
|
||||||
|
づ 16
|
||||||
|
て 16
|
||||||
|
で 16
|
||||||
|
と 16
|
||||||
|
ど 16
|
||||||
|
な 16
|
||||||
|
に 16
|
||||||
|
ぬ 16
|
||||||
|
ね 16
|
||||||
|
の 16
|
||||||
|
は 16
|
||||||
|
ば 16
|
||||||
|
ぱ 16
|
||||||
|
ひ 16
|
||||||
|
び 16
|
||||||
|
ぴ 16
|
||||||
|
ふ 16
|
||||||
|
ぶ 16
|
||||||
|
ぷ 16
|
||||||
|
へ 16
|
||||||
|
べ 16
|
||||||
|
ぺ 16
|
||||||
|
ほ 16
|
||||||
|
ぼ 16
|
||||||
|
ぽ 16
|
||||||
|
ま 16
|
||||||
|
み 16
|
||||||
|
む 16
|
||||||
|
め 16
|
||||||
|
も 16
|
||||||
|
ゃ 16
|
||||||
|
や 16
|
||||||
|
ゅ 16
|
||||||
|
ゆ 16
|
||||||
|
ょ 16
|
||||||
|
よ 16
|
||||||
|
ら 16
|
||||||
|
り 16
|
||||||
|
る 16
|
||||||
|
れ 16
|
||||||
|
ろ 16
|
||||||
|
ゎ 16
|
||||||
|
わ 16
|
||||||
|
ゐ 16
|
||||||
|
ゑ 16
|
||||||
|
を 16
|
||||||
|
ん 16
|
||||||
|
ゔ 16
|
||||||
|
ゕ 16
|
||||||
|
ゖ 16
|
||||||
|
゛ 1
|
||||||
|
゜ 0
|
||||||
|
ゝ 16
|
||||||
|
ゞ 16
|
||||||
|
ゟ 16
|
||||||
|
゠ 16
|
||||||
|
ァ 16
|
||||||
|
ア 16
|
||||||
|
ィ 16
|
||||||
|
イ 16
|
||||||
|
ゥ 16
|
||||||
|
ウ 16
|
||||||
|
ェ 16
|
||||||
|
エ 16
|
||||||
|
ォ 16
|
||||||
|
オ 16
|
||||||
|
カ 16
|
||||||
|
ガ 16
|
||||||
|
キ 16
|
||||||
|
ギ 16
|
||||||
|
ク 16
|
||||||
|
グ 16
|
||||||
|
ケ 16
|
||||||
|
ゲ 16
|
||||||
|
コ 16
|
||||||
|
ゴ 16
|
||||||
|
サ 16
|
||||||
|
ザ 16
|
||||||
|
シ 16
|
||||||
|
ジ 16
|
||||||
|
ス 16
|
||||||
|
ズ 16
|
||||||
|
セ 16
|
||||||
|
ゼ 16
|
||||||
|
ソ 16
|
||||||
|
ゾ 16
|
||||||
|
タ 16
|
||||||
|
ダ 16
|
||||||
|
チ 16
|
||||||
|
ヂ 16
|
||||||
|
ッ 16
|
||||||
|
ツ 16
|
||||||
|
ヅ 16
|
||||||
|
テ 16
|
||||||
|
デ 16
|
||||||
|
ト 16
|
||||||
|
ド 16
|
||||||
|
ナ 16
|
||||||
|
ニ 16
|
||||||
|
ヌ 16
|
||||||
|
ネ 16
|
||||||
|
ノ 16
|
||||||
|
ハ 16
|
||||||
|
バ 16
|
||||||
|
パ 16
|
||||||
|
ヒ 16
|
||||||
|
ビ 16
|
||||||
|
ピ 16
|
||||||
|
フ 16
|
||||||
|
ブ 16
|
||||||
|
プ 16
|
||||||
|
ヘ 16
|
||||||
|
ベ 16
|
||||||
|
ペ 16
|
||||||
|
ホ 16
|
||||||
|
ボ 16
|
||||||
|
ポ 16
|
||||||
|
マ 16
|
||||||
|
ミ 16
|
||||||
|
ム 16
|
||||||
|
メ 16
|
||||||
|
モ 16
|
||||||
|
ャ 16
|
||||||
|
ヤ 16
|
||||||
|
ュ 16
|
||||||
|
ユ 16
|
||||||
|
ョ 16
|
||||||
|
ヨ 16
|
||||||
|
ラ 16
|
||||||
|
リ 16
|
||||||
|
ル 16
|
||||||
|
レ 16
|
||||||
|
ロ 16
|
||||||
|
ヮ 16
|
||||||
|
ワ 16
|
||||||
|
ヰ 16
|
||||||
|
ヱ 16
|
||||||
|
ヲ 16
|
||||||
|
ン 16
|
||||||
|
ヴ 16
|
||||||
|
ヵ 16
|
||||||
|
ヶ 16
|
||||||
|
ヷ 16
|
||||||
|
ヸ 16
|
||||||
|
ヹ 16
|
||||||
|
ヺ 16
|
||||||
|
・ 16
|
||||||
|
ー 16
|
||||||
|
ヽ 16
|
||||||
|
ヾ 16
|
||||||
|
ヿ 16
|
||||||
|
「 16
|
||||||
|
」 16
|
||||||
|
円 16
|
||||||
|
<EFBFBD> 13
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.4 KiB |
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 7.0 KiB |
|
@ -24,9 +24,7 @@ local function startGame()
|
||||||
awayTeamSprites = AwayTeamSprites,
|
awayTeamSprites = AwayTeamSprites,
|
||||||
})
|
})
|
||||||
playdate.resetElapsedTime()
|
playdate.resetElapsedTime()
|
||||||
playdate.update = function()
|
transitionBetween(MainMenu, next)
|
||||||
next:update()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function pausingEaser(baseEaser)
|
local function pausingEaser(baseEaser)
|
||||||
|
@ -64,9 +62,10 @@ local function arrayElementFromCrank(array, crankPosition)
|
||||||
return array[i]
|
return array[i]
|
||||||
end
|
end
|
||||||
|
|
||||||
local currentLogo = nil
|
local currentLogo
|
||||||
|
|
||||||
function MainMenu.update()
|
--luacheck: ignore
|
||||||
|
function MainMenu:update()
|
||||||
playdate.timer.updateTimers()
|
playdate.timer.updateTimers()
|
||||||
crankStartPos = crankStartPos or playdate.getCrankPosition()
|
crankStartPos = crankStartPos or playdate.getCrankPosition()
|
||||||
|
|
||||||
|
|
90
src/main.lua
|
@ -8,6 +8,8 @@ import 'CoreLibs/timer.lua'
|
||||||
import 'CoreLibs/ui.lua'
|
import 'CoreLibs/ui.lua'
|
||||||
-- stylua: ignore end
|
-- stylua: ignore end
|
||||||
|
|
||||||
|
--- @alias Scene { update: fun(self: self) }
|
||||||
|
|
||||||
--- @alias EasingFunc fun(number, number, number, number): number
|
--- @alias EasingFunc fun(number, number, number, number): number
|
||||||
|
|
||||||
--- @alias LaunchBall fun(
|
--- @alias LaunchBall fun(
|
||||||
|
@ -24,9 +26,11 @@ import 'CoreLibs/ui.lua'
|
||||||
import 'utils.lua'
|
import 'utils.lua'
|
||||||
import 'constants.lua'
|
import 'constants.lua'
|
||||||
import 'assets.lua'
|
import 'assets.lua'
|
||||||
import 'draw/player.lua'
|
import 'draw/box-score.lua'
|
||||||
import 'draw/overlay.lua'
|
|
||||||
import 'draw/fielder.lua'
|
import 'draw/fielder.lua'
|
||||||
|
import 'draw/overlay.lua'
|
||||||
|
import 'draw/player.lua'
|
||||||
|
import 'draw/transitions.lua'
|
||||||
|
|
||||||
import 'main-menu.lua'
|
import 'main-menu.lua'
|
||||||
|
|
||||||
|
@ -44,15 +48,13 @@ import 'npc.lua'
|
||||||
|
|
||||||
local gfx <const>, C <const> = playdate.graphics, C
|
local gfx <const>, C <const> = playdate.graphics, C
|
||||||
|
|
||||||
---@alias Team { score: number, benchPosition: XyPair }
|
---@alias Team { benchPosition: XyPair }
|
||||||
---@type table<TeamId, Team>
|
---@type table<TeamId, Team>
|
||||||
local teams <const> = {
|
local teams <const> = {
|
||||||
home = {
|
home = {
|
||||||
score = 0, -- TODO: Extract this last bit of global mutable state.
|
|
||||||
benchPosition = utils.xy(C.Screen.W + 10, C.Center.y),
|
benchPosition = utils.xy(C.Screen.W + 10, C.Center.y),
|
||||||
},
|
},
|
||||||
away = {
|
away = {
|
||||||
score = 0,
|
|
||||||
benchPosition = utils.xy(-10, C.Center.y),
|
benchPosition = utils.xy(-10, C.Center.y),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -73,6 +75,7 @@ local teams <const> = {
|
||||||
---@field catcherThrownBall boolean
|
---@field catcherThrownBall boolean
|
||||||
---@field offenseState OffenseState
|
---@field offenseState OffenseState
|
||||||
---@field inning number
|
---@field inning number
|
||||||
|
---@field stats Statistics
|
||||||
---@field batBase XyPair
|
---@field batBase XyPair
|
||||||
---@field batTip XyPair
|
---@field batTip XyPair
|
||||||
---@field batAngleDeg number
|
---@field batAngleDeg number
|
||||||
|
@ -104,8 +107,6 @@ Game = {}
|
||||||
---@param state MutableState | nil
|
---@param state MutableState | nil
|
||||||
---@return Game
|
---@return Game
|
||||||
function Game.new(settings, announcer, fielding, baserunning, npc, state)
|
function Game.new(settings, announcer, fielding, baserunning, npc, state)
|
||||||
teams.away.score = 0
|
|
||||||
teams.home.score = 0
|
|
||||||
announcer = announcer or Announcer.new()
|
announcer = announcer or Announcer.new()
|
||||||
fielding = fielding or Fielding.new()
|
fielding = fielding or Fielding.new()
|
||||||
settings.userTeam = nil -- "away"
|
settings.userTeam = nil -- "away"
|
||||||
|
@ -137,6 +138,7 @@ function Game.new(settings, announcer, fielding, baserunning, npc, state)
|
||||||
battingTeamSprites = settings.awayTeamSprites,
|
battingTeamSprites = settings.awayTeamSprites,
|
||||||
fieldingTeamSprites = settings.homeTeamSprites,
|
fieldingTeamSprites = settings.homeTeamSprites,
|
||||||
runnerBlipper = runnerBlipper,
|
runnerBlipper = runnerBlipper,
|
||||||
|
stats = Statistics.new(),
|
||||||
},
|
},
|
||||||
}, { __index = Game })
|
}, { __index = Game })
|
||||||
|
|
||||||
|
@ -210,10 +212,6 @@ local function getOppositeTeamId(teamId)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Game:getBattingTeam()
|
|
||||||
return teams[self.state.battingTeam] or error("Unknown battingTeam: " .. (self.state.battingTeam or "nil"))
|
|
||||||
end
|
|
||||||
|
|
||||||
function Game:getFieldingTeam()
|
function Game:getFieldingTeam()
|
||||||
return teams[getOppositeTeamId(self.state.battingTeam)]
|
return teams[getOppositeTeamId(self.state.battingTeam)]
|
||||||
end
|
end
|
||||||
|
@ -265,24 +263,33 @@ end
|
||||||
|
|
||||||
function Game:nextHalfInning()
|
function Game:nextHalfInning()
|
||||||
pitchTracker:reset()
|
pitchTracker:reset()
|
||||||
local gameOver = self.state.inning == self.settings.finalInning and teams.away.score ~= teams.home.score
|
local homeScore, awayScore = utils.totalScores(self.state.stats)
|
||||||
if not gameOver then
|
-- TODO end the game if away team just batted and home team is winning
|
||||||
self.fielding:celebrate()
|
local gameOver = self.state.battingTeam == "home"
|
||||||
self.state.secondsSinceLastRunnerMove = -7
|
and self.state.inning == self.settings.finalInning
|
||||||
self.fielding:benchTo(self:getFieldingTeam().benchPosition)
|
and awayScore ~= homeScore
|
||||||
self.announcer:say("SWITCHING SIDES...")
|
|
||||||
end
|
|
||||||
|
|
||||||
if gameOver then
|
if gameOver then
|
||||||
self.announcer:say("AND THAT'S THE BALL GAME!")
|
self.announcer:say("AND THAT'S THE BALL GAME!")
|
||||||
else
|
playdate.timer.new(3000, function()
|
||||||
|
transitionTo(BoxScore.new(self.state.stats))
|
||||||
|
end)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
Fielding.celebrate()
|
||||||
|
self.state.secondsSinceLastRunnerMove = -7
|
||||||
|
self.fielding:benchTo(self:getFieldingTeam().benchPosition)
|
||||||
|
self.announcer:say("SWITCHING SIDES...")
|
||||||
|
|
||||||
self.fielding:resetFielderPositions()
|
self.fielding:resetFielderPositions()
|
||||||
if self.state.battingTeam == teams.home then
|
if self.state.battingTeam == "home" then
|
||||||
self.state.inning = self.state.inning + 1
|
self.state.inning = self.state.inning + 1
|
||||||
|
self.state.stats:pushInning()
|
||||||
end
|
end
|
||||||
self.state.battingTeam = getOppositeTeamId(self.state.battingTeam)
|
self.state.battingTeam = getOppositeTeamId(self.state.battingTeam)
|
||||||
playdate.timer.new(2000, function()
|
playdate.timer.new(2000, function()
|
||||||
if self.state.battingTeam == teams.home then
|
if self.state.battingTeam == "home" then
|
||||||
self.state.battingTeamSprites = self.settings.homeTeamSprites
|
self.state.battingTeamSprites = self.settings.homeTeamSprites
|
||||||
self.state.runnerBlipper = self.homeTeamBlipper
|
self.state.runnerBlipper = self.homeTeamBlipper
|
||||||
self.state.fieldingTeamSprites = self.settings.awayTeamSprites
|
self.state.fieldingTeamSprites = self.settings.awayTeamSprites
|
||||||
|
@ -293,12 +300,21 @@ function Game:nextHalfInning()
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@return TeamInningData
|
||||||
|
function Game:battingTeamCurrentInning()
|
||||||
|
return self.state.stats.innings[self.state.inning][self.state.battingTeam]
|
||||||
|
end
|
||||||
|
|
||||||
|
---@return TeamInningData
|
||||||
|
function Game:fieldingTeamCurrentInning()
|
||||||
|
return self.state.stats.innings[self.state.inning][getOppositeTeamId(self.state.battingTeam)]
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param scoredRunCount number
|
---@param scoredRunCount number
|
||||||
function Game:score(scoredRunCount)
|
function Game:score(scoredRunCount)
|
||||||
local batting = self:getBattingTeam()
|
self:battingTeamCurrentInning().score = self:battingTeamCurrentInning().score + scoredRunCount
|
||||||
batting.score = batting.score + scoredRunCount
|
|
||||||
self.announcer:say("SCORE!")
|
self.announcer:say("SCORE!")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -394,6 +410,8 @@ function Game:updateBatting(batDeg, batSpeed)
|
||||||
local hitBallScaler = gfx.animator.new(2000, 9 + (mult * mult * 0.5), C.SmallestBallRadius, utils.easingHill)
|
local hitBallScaler = gfx.animator.new(2000, 9 + (mult * mult * 0.5), C.SmallestBallRadius, utils.easingHill)
|
||||||
self.state.ball:launch(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000, nil, hitBallScaler)
|
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()
|
||||||
|
battingTeamStats.hits[#battingTeamStats.hits + 1] = utils.xy(ballDestX, ballDestY)
|
||||||
|
|
||||||
if utils.isFoulBall(ballDestX, ballDestY) then
|
if utils.isFoulBall(ballDestX, ballDestY) then
|
||||||
self.announcer:say("Foul ball!")
|
self.announcer:say("Foul ball!")
|
||||||
|
@ -401,7 +419,6 @@ 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()
|
self.baserunning:convertBatterToRunner()
|
||||||
|
|
||||||
self.fielding:haveSomeoneChase(ballDestX, ballDestY)
|
self.fielding:haveSomeoneChase(ballDestX, ballDestY)
|
||||||
|
@ -465,6 +482,13 @@ function Game:updateGameState()
|
||||||
|
|
||||||
if self.state.secondsSincePitchAllowed > C.ReturnToPitcherAfterSeconds and not self.state.catcherThrownBall then
|
if self.state.secondsSincePitchAllowed > C.ReturnToPitcherAfterSeconds and not self.state.catcherThrownBall then
|
||||||
local outcome = pitchTracker:updatePitchCounts()
|
local outcome = pitchTracker:updatePitchCounts()
|
||||||
|
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
|
||||||
|
@ -488,7 +512,6 @@ function Game:updateGameState()
|
||||||
self:updateBatting(self.state.batAngleDeg, batSpeed)
|
self:updateBatting(self.state.batAngleDeg, batSpeed)
|
||||||
|
|
||||||
-- Walk batter to the plate
|
-- Walk batter to the plate
|
||||||
-- TODO: Ensure batter can't be nil, here
|
|
||||||
self.baserunning:updateRunner(self.baserunning.batter, nil, crankLimited, self.state.deltaSeconds)
|
self.baserunning:updateRunner(self.baserunning.batter, nil, crankLimited, self.state.deltaSeconds)
|
||||||
|
|
||||||
if self.state.secondsSincePitchAllowed > C.PitchAfterSeconds then
|
if self.state.secondsSincePitchAllowed > C.PitchAfterSeconds then
|
||||||
|
@ -513,7 +536,8 @@ function Game:updateGameState()
|
||||||
if self.state.secondsSinceLastRunnerMove > C.ResetFieldersAfterSeconds then
|
if self.state.secondsSinceLastRunnerMove > C.ResetFieldersAfterSeconds then
|
||||||
-- End of play. Throw the ball back to the pitcher
|
-- End of play. Throw the ball back to the pitcher
|
||||||
self.state.ball:launch(C.PitchStartX, C.PitchStartY, playdate.easingFunctions.linear, nil, true)
|
self.state.ball:launch(C.PitchStartX, C.PitchStartY, playdate.easingFunctions.linear, nil, true)
|
||||||
self.fielding:markAllIneligible() -- This is ugly, and ideally would not be necessary if Fielding handled the return throw directly.
|
-- This is ugly, and ideally would not be necessary if Fielding handled the return throw directly.
|
||||||
|
self.fielding:markAllIneligible()
|
||||||
self.fielding:resetFielderPositions()
|
self.fielding:resetFielderPositions()
|
||||||
self.state.offenseState = C.Offense.batting
|
self.state.offenseState = C.Offense.batting
|
||||||
-- TODO: Remove, or replace with nextBatter()
|
-- TODO: Remove, or replace with nextBatter()
|
||||||
|
@ -547,8 +571,6 @@ function Game:updateGameState()
|
||||||
actionQueue:runWaiting(self.state.deltaSeconds)
|
actionQueue:runWaiting(self.state.deltaSeconds)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: Swappable update() for main menu, etc.
|
|
||||||
|
|
||||||
function Game:update()
|
function Game:update()
|
||||||
playdate.timer.updateTimers()
|
playdate.timer.updateTimers()
|
||||||
gfx.animation.blinker.updateAll()
|
gfx.animation.blinker.updateAll()
|
||||||
|
@ -629,11 +651,21 @@ function Game:update()
|
||||||
if math.abs(offsetX) > 10 or math.abs(offsetY) > 10 then
|
if math.abs(offsetX) > 10 or math.abs(offsetY) > 10 then
|
||||||
drawMinimap(self.baserunning.runners, self.fielding.fielders)
|
drawMinimap(self.baserunning.runners, self.fielding.fielders)
|
||||||
end
|
end
|
||||||
drawScoreboard(0, C.Screen.H * 0.77, teams, self.baserunning.outs, self:getBattingTeam(), self.state.inning)
|
local homeScore, awayScore = utils.totalScores(self.state.stats)
|
||||||
|
drawScoreboard(
|
||||||
|
0,
|
||||||
|
C.Screen.H * 0.77,
|
||||||
|
homeScore,
|
||||||
|
awayScore,
|
||||||
|
self.baserunning.outs,
|
||||||
|
self.state.battingTeam,
|
||||||
|
self.state.inning
|
||||||
|
)
|
||||||
drawBallsAndStrikes(290, C.Screen.H - 20, pitchTracker.balls, pitchTracker.strikes)
|
drawBallsAndStrikes(290, C.Screen.H - 20, pitchTracker.balls, pitchTracker.strikes)
|
||||||
self.announcer:draw(C.Center.x, 10)
|
self.announcer:draw(C.Center.x, 10)
|
||||||
|
|
||||||
if playdate.isCrankDocked() then
|
if playdate.isCrankDocked() then
|
||||||
|
-- luacheck: ignore
|
||||||
playdate.ui.crankIndicator:draw()
|
playdate.ui.crankIndicator:draw()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,6 +22,7 @@ end
|
||||||
---@param catcherThrownBall boolean
|
---@param catcherThrownBall boolean
|
||||||
---@param deltaSec number
|
---@param deltaSec number
|
||||||
---@return number
|
---@return number
|
||||||
|
-- luacheck: no unused
|
||||||
function Npc:updateBatAngle(ball, catcherThrownBall, deltaSec)
|
function Npc:updateBatAngle(ball, catcherThrownBall, deltaSec)
|
||||||
if not catcherThrownBall and ball.y > 200 and ball.y < 230 and (ball.x < C.Center.x + 15) then
|
if not catcherThrownBall and ball.y > 200 and ball.y < 230 and (ball.x < C.Center.x + 15) then
|
||||||
npcBatDeg = npcBatDeg + (deltaSec * npcBatSpeed)
|
npcBatDeg = npcBatDeg + (deltaSec * npcBatSpeed)
|
||||||
|
|
|
@ -232,8 +232,23 @@ function utils.getNearestOf(array, x, y, extraCondition)
|
||||||
return nearest, nearestDistance
|
return nearest, nearestDistance
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param stats Statistics
|
||||||
|
---@return number homeScore, number awayScore
|
||||||
|
function utils.totalScores(stats)
|
||||||
|
local homeScore = 0
|
||||||
|
local awayScore = 0
|
||||||
|
for _, inning in pairs(stats.innings) do
|
||||||
|
homeScore = homeScore + inning.home.score
|
||||||
|
awayScore = awayScore + inning.away.score
|
||||||
|
end
|
||||||
|
|
||||||
|
return homeScore, awayScore
|
||||||
|
end
|
||||||
|
|
||||||
PitchOutcomes = {
|
PitchOutcomes = {
|
||||||
|
Strike = {},
|
||||||
StrikeOut = {},
|
StrikeOut = {},
|
||||||
|
Ball = {},
|
||||||
Walk = {},
|
Walk = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|