Add box score and transitions
Add constants defining the top of the outfield wall (not used yet) Take scores out of mutable global state (that might be just about all of it sewn up) Finish switching batttingTeam to a TeamId value
This commit is contained in:
parent
6007ac971f
commit
5c45b7bba0
|
@ -44,6 +44,7 @@ function Announcer:popIn()
|
|||
end)
|
||||
end
|
||||
|
||||
---@param text string
|
||||
function Announcer:say(text)
|
||||
self.textQueue[#self.textQueue + 1] = text
|
||||
if #self.textQueue == 1 then
|
||||
|
|
|
@ -8,6 +8,8 @@ Glove = playdate.graphics.image.new("images/game/Glove.png")
|
|||
-- luacheck: ignore
|
||||
PlayerFrown = playdate.graphics.image.new("images/game/PlayerFrown.png")
|
||||
-- luacheck: ignore
|
||||
BigBat = playdate.graphics.image.new("images/game/BigBat.png")
|
||||
-- luacheck: ignore
|
||||
GloveHoldingBall = playdate.graphics.image.new("images/game/GloveHoldingBall.png")
|
||||
-- luacheck: ignore
|
||||
GameLogo = playdate.graphics.image.new("images/game/GameLogo.png")
|
||||
|
|
|
@ -125,6 +125,18 @@ C.WalkedRunnerSpeed = 10
|
|||
|
||||
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
|
||||
return C
|
||||
end
|
||||
|
|
15
src/dbg.lua
15
src/dbg.lua
|
@ -35,8 +35,23 @@ function dbg.loadTheBases(br)
|
|||
br.runners[4].nextBase = C.Bases[C.Home]
|
||||
end
|
||||
|
||||
---@return BoxScoreData
|
||||
function dbg.mockBoxScoreData(inningCount)
|
||||
inningCount = inningCount or 9
|
||||
local data = {
|
||||
home = {},
|
||||
away = {},
|
||||
}
|
||||
for i = 1, inningCount do
|
||||
data.home[i] = math.floor(math.random() * 5)
|
||||
data.away[i] = math.floor(math.random() * 5)
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
---@param points XyPair[]
|
||||
function dbg.drawLine(points)
|
||||
playdate.graphics.setColor(playdate.graphics.kColorWhite)
|
||||
for i = 2, #points do
|
||||
local prev = points[i - 1]
|
||||
local next = points[i]
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
---@class BoxScore
|
||||
---@field data BoxScoreData
|
||||
BoxScore = {}
|
||||
|
||||
---@param data BoxScoreData
|
||||
function BoxScore.new(data)
|
||||
return setmetatable({
|
||||
data = data,
|
||||
}, { __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 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:update()
|
||||
local originalDrawMode = gfx.getImageDrawMode()
|
||||
gfx.clear(gfx.kColorBlack)
|
||||
gfx.setImageDrawMode(gfx.kDrawModeInverted)
|
||||
gfx.setColor(gfx.kColorBlack)
|
||||
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.data.away do
|
||||
drawInning(inningStart + ((i - 1) * widthAndMarg), i, self.data.home[i], self.data.away[i])
|
||||
end
|
||||
local homeScore, awayScore = utils.totalScores(self.data)
|
||||
drawInning(4 + inningStart + (widthAndMarg * #self.data.away), "F", homeScore, awayScore)
|
||||
gfx.setImageDrawMode(originalDrawMode)
|
||||
end
|
|
@ -83,11 +83,10 @@ local ScoreboardHeight <const> = 55
|
|||
local Indicator = "> "
|
||||
local IndicatorWidth <const> = ScoreFont:getTextWidth(Indicator)
|
||||
|
||||
---@param teams any
|
||||
---@param battingTeam any
|
||||
---@return string, number, string, number
|
||||
function getIndicators(teams, battingTeam)
|
||||
if teams.home == battingTeam then
|
||||
function getIndicators(battingTeam)
|
||||
if battingTeam == "home" then
|
||||
return Indicator, 0, "", IndicatorWidth
|
||||
end
|
||||
return "", IndicatorWidth, Indicator, 0
|
||||
|
@ -101,11 +100,11 @@ local stats = {
|
|||
battingTeam = nil,
|
||||
}
|
||||
|
||||
function drawScoreboardImpl(x, y, teams)
|
||||
function drawScoreboardImpl(x, y)
|
||||
local homeScore = stats.homeScore
|
||||
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 awayScoreText = awayIndicator .. "AWAY " .. (awayScore > 9 and awayScore or " " .. awayScore)
|
||||
|
@ -146,17 +145,17 @@ end
|
|||
|
||||
local newStats = stats
|
||||
|
||||
function drawScoreboard(x, y, teams, outs, battingTeam, inning)
|
||||
function drawScoreboard(x, y, homeScore, awayScore, outs, battingTeam, inning)
|
||||
if
|
||||
newStats.homeScore ~= teams.home.score
|
||||
or newStats.awayScore ~= teams.away.score
|
||||
newStats.homeScore ~= homeScore
|
||||
or newStats.awayScore ~= awayScore
|
||||
or newStats.outs ~= outs
|
||||
or newStats.inning ~= inning
|
||||
or newStats.battingTeam ~= battingTeam
|
||||
then
|
||||
newStats = {
|
||||
homeScore = teams.home.score,
|
||||
awayScore = teams.away.score,
|
||||
homeScore = homeScore,
|
||||
awayScore = awayScore,
|
||||
outs = outs,
|
||||
inning = inning,
|
||||
battingTeam = battingTeam,
|
||||
|
@ -165,5 +164,5 @@ function drawScoreboard(x, y, teams, outs, battingTeam, inning)
|
|||
stats = newStats
|
||||
end)
|
||||
end
|
||||
drawScoreboardImpl(x, y, teams)
|
||||
drawScoreboardImpl(x, y)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
Transitions = {
|
||||
---@type Scene | nil
|
||||
nextScene = nil,
|
||||
---@type Scene | nil
|
||||
previousScene = nil,
|
||||
}
|
||||
|
||||
local gfx = playdate.graphics
|
||||
|
||||
-- local function animateOut() end
|
||||
-- local function animateIn() end
|
||||
|
||||
local previousSceneImage
|
||||
local previousSceneMask
|
||||
|
||||
local nextSceneImage
|
||||
|
||||
-- local nextSceneOffScreen = false
|
||||
-- local stillAnimating = false
|
||||
|
||||
-- TODO: Okay but like imagine.
|
||||
-- Push a blank image context,
|
||||
-- draw the previous scene on one side,
|
||||
-- draw the next scene on the other,
|
||||
-- SWING a bat to cover the seam.
|
||||
|
||||
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
|
|
@ -182,7 +182,7 @@ function Fielding:userThrowTo(targetBase, ball, throwFlyMs)
|
|||
end)
|
||||
end
|
||||
|
||||
function Fielding:celebrate()
|
||||
function Fielding.celebrate()
|
||||
FielderDanceAnimator:reset(C.DanceBounceMs)
|
||||
end
|
||||
|
||||
|
|
Binary file not shown.
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
|
Binary file not shown.
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 9.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
|
@ -24,9 +24,7 @@ local function startGame()
|
|||
awayTeamSprites = AwayTeamSprites,
|
||||
})
|
||||
playdate.resetElapsedTime()
|
||||
playdate.update = function()
|
||||
next:update()
|
||||
end
|
||||
transitionBetween(MainMenu, next)
|
||||
end
|
||||
|
||||
local function pausingEaser(baseEaser)
|
||||
|
@ -64,9 +62,10 @@ local function arrayElementFromCrank(array, crankPosition)
|
|||
return array[i]
|
||||
end
|
||||
|
||||
local currentLogo = nil
|
||||
local currentLogo
|
||||
|
||||
function MainMenu.update()
|
||||
--luacheck: ignore
|
||||
function MainMenu:update()
|
||||
playdate.timer.updateTimers()
|
||||
crankStartPos = crankStartPos or playdate.getCrankPosition()
|
||||
|
||||
|
|
98
src/main.lua
98
src/main.lua
|
@ -8,6 +8,8 @@ import 'CoreLibs/timer.lua'
|
|||
import 'CoreLibs/ui.lua'
|
||||
-- stylua: ignore end
|
||||
|
||||
--- @alias Scene { update: fun(self: self) }
|
||||
|
||||
--- @alias EasingFunc fun(number, number, number, number): number
|
||||
|
||||
--- @alias LaunchBall fun(
|
||||
|
@ -24,9 +26,11 @@ import 'CoreLibs/ui.lua'
|
|||
import 'utils.lua'
|
||||
import 'constants.lua'
|
||||
import 'assets.lua'
|
||||
import 'draw/player.lua'
|
||||
import 'draw/overlay.lua'
|
||||
import 'draw/box-score.lua'
|
||||
import 'draw/fielder.lua'
|
||||
import 'draw/overlay.lua'
|
||||
import 'draw/player.lua'
|
||||
import 'draw/transitions.lua'
|
||||
|
||||
import 'main-menu.lua'
|
||||
|
||||
|
@ -44,19 +48,18 @@ import 'npc.lua'
|
|||
|
||||
local gfx <const>, C <const> = playdate.graphics, C
|
||||
|
||||
---@alias Team { score: number, benchPosition: XyPair }
|
||||
---@alias Team { benchPosition: XyPair }
|
||||
---@type table<TeamId, Team>
|
||||
local teams <const> = {
|
||||
home = {
|
||||
score = 0, -- TODO: Extract this last bit of global mutable state.
|
||||
benchPosition = utils.xy(C.Screen.W + 10, C.Center.y),
|
||||
},
|
||||
away = {
|
||||
score = 0,
|
||||
benchPosition = utils.xy(-10, C.Center.y),
|
||||
},
|
||||
}
|
||||
|
||||
---@alias BoxScoreData table<TeamId, number[]>
|
||||
---@alias TeamId 'home' | 'away'
|
||||
|
||||
--- Well, maybe not "Settings", but passive state that probably won't change much, if at all, during a game.
|
||||
|
@ -73,6 +76,7 @@ local teams <const> = {
|
|||
---@field catcherThrownBall boolean
|
||||
---@field offenseState OffenseState
|
||||
---@field inning number
|
||||
---@field boxScore BoxScoreData
|
||||
---@field batBase XyPair
|
||||
---@field batTip XyPair
|
||||
---@field batAngleDeg number
|
||||
|
@ -104,8 +108,6 @@ 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 = nil -- "away"
|
||||
|
@ -137,6 +139,10 @@ function Game.new(settings, announcer, fielding, baserunning, npc, state)
|
|||
battingTeamSprites = settings.awayTeamSprites,
|
||||
fieldingTeamSprites = settings.homeTeamSprites,
|
||||
runnerBlipper = runnerBlipper,
|
||||
boxScore = {
|
||||
home = { 0 },
|
||||
away = { 0 },
|
||||
},
|
||||
},
|
||||
}, { __index = Game })
|
||||
|
||||
|
@ -210,10 +216,6 @@ local function getOppositeTeamId(teamId)
|
|||
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
|
||||
|
@ -265,40 +267,49 @@ end
|
|||
|
||||
function Game:nextHalfInning()
|
||||
pitchTracker:reset()
|
||||
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(self:getFieldingTeam().benchPosition)
|
||||
self.announcer:say("SWITCHING SIDES...")
|
||||
end
|
||||
local homeScore, awayScore = utils.totalScores(self.state.boxScore)
|
||||
local gameOver = self.state.battingTeam == "home"
|
||||
and self.state.inning == self.settings.finalInning
|
||||
and awayScore ~= homeScore
|
||||
|
||||
if gameOver then
|
||||
self.announcer:say("AND THAT'S THE BALL GAME!")
|
||||
else
|
||||
self.fielding:resetFielderPositions()
|
||||
if self.state.battingTeam == teams.home then
|
||||
self.state.inning = self.state.inning + 1
|
||||
end
|
||||
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
|
||||
self.state.runnerBlipper = self.homeTeamBlipper
|
||||
self.state.fieldingTeamSprites = self.settings.awayTeamSprites
|
||||
else
|
||||
self.state.battingTeamSprites = self.settings.awayTeamSprites
|
||||
self.state.fieldingTeamSprites = self.settings.homeTeamSprites
|
||||
self.state.runnerBlipper = self.awayTeamBlipper
|
||||
end
|
||||
playdate.timer.new(3000, function()
|
||||
transitionTo(BoxScore.new(self.state.boxScore))
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
Fielding.celebrate()
|
||||
self.state.secondsSinceLastRunnerMove = -7
|
||||
self.fielding:benchTo(self:getFieldingTeam().benchPosition)
|
||||
self.announcer:say("SWITCHING SIDES...")
|
||||
|
||||
self.fielding:resetFielderPositions()
|
||||
if self.state.battingTeam == "home" then
|
||||
self.state.inning = self.state.inning + 1
|
||||
self.state.boxScore.home[self.state.inning] = 0
|
||||
self.state.boxScore.away[self.state.inning] = 0
|
||||
end
|
||||
self.state.battingTeam = getOppositeTeamId(self.state.battingTeam)
|
||||
playdate.timer.new(2000, function()
|
||||
if self.state.battingTeam == "home" then
|
||||
self.state.battingTeamSprites = self.settings.homeTeamSprites
|
||||
self.state.runnerBlipper = self.homeTeamBlipper
|
||||
self.state.fieldingTeamSprites = self.settings.awayTeamSprites
|
||||
else
|
||||
self.state.battingTeamSprites = self.settings.awayTeamSprites
|
||||
self.state.fieldingTeamSprites = self.settings.homeTeamSprites
|
||||
self.state.runnerBlipper = self.awayTeamBlipper
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
---@param scoredRunCount number
|
||||
function Game:score(scoredRunCount)
|
||||
local batting = self:getBattingTeam()
|
||||
batting.score = batting.score + scoredRunCount
|
||||
local battingTeamBoxScore = self.state.boxScore[self.state.battingTeam]
|
||||
battingTeamBoxScore[self.state.inning] = battingTeamBoxScore[self.state.inning] + scoredRunCount
|
||||
|
||||
self.announcer:say("SCORE!")
|
||||
end
|
||||
|
||||
|
@ -513,7 +524,8 @@ function Game:updateGameState()
|
|||
if self.state.secondsSinceLastRunnerMove > C.ResetFieldersAfterSeconds then
|
||||
-- End of play. Throw the ball back to the pitcher
|
||||
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.state.offenseState = C.Offense.batting
|
||||
-- TODO: Remove, or replace with nextBatter()
|
||||
|
@ -629,11 +641,21 @@ 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:getBattingTeam(), self.state.inning)
|
||||
local homeScore, awayScore = utils.totalScores(self.state.boxScore)
|
||||
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)
|
||||
self.announcer:draw(C.Center.x, 10)
|
||||
|
||||
if playdate.isCrankDocked() then
|
||||
-- luacheck: ignore
|
||||
playdate.ui.crankIndicator:draw()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,6 +22,7 @@ end
|
|||
---@param catcherThrownBall boolean
|
||||
---@param deltaSec number
|
||||
---@return number
|
||||
-- luacheck: no unused
|
||||
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
|
||||
npcBatDeg = npcBatDeg + (deltaSec * npcBatSpeed)
|
||||
|
|
|
@ -232,6 +232,22 @@ function utils.getNearestOf(array, x, y, extraCondition)
|
|||
return nearest, nearestDistance
|
||||
end
|
||||
|
||||
---@param box BoxScore
|
||||
---@return number homeScore, number awayScore
|
||||
function utils.totalScores(box)
|
||||
local homeScore = 0
|
||||
for _, score in pairs(box.home) do
|
||||
homeScore = homeScore + score
|
||||
end
|
||||
|
||||
local awayScore = 0
|
||||
for _, score in pairs(box.away) do
|
||||
awayScore = awayScore + score
|
||||
end
|
||||
|
||||
return homeScore, awayScore
|
||||
end
|
||||
|
||||
PitchOutcomes = {
|
||||
StrikeOut = {},
|
||||
Walk = {},
|
||||
|
|
Loading…
Reference in New Issue