Add dark-skin player sprites.

SpriteCollection -> PlayerImageBundle
SpriteCollection is now PlayerImageBundle[]
This commit is contained in:
Sage Vaillancourt 2025-02-20 20:33:46 -05:00
parent 384a14fe5f
commit 786f80b0df
25 changed files with 201 additions and 65 deletions

View File

@ -3,7 +3,13 @@
-- luacheck: ignore
---@type pd_image
DarkPlayerBack = playdate.graphics.image.new("images/game/DarkPlayerBack.png")
DarkPlayerFrown = playdate.graphics.image.new("images/game/DarkPlayerFrown.png")
-- luacheck: ignore
---@type pd_image
LightPlayerAwayBase = playdate.graphics.image.new("images/game/LightPlayerAwayBase.png")
-- luacheck: ignore
---@type pd_image
LightPlayerSmile = playdate.graphics.image.new("images/game/LightPlayerSmile.png")
-- luacheck: ignore
---@type pd_image
Glove = playdate.graphics.image.new("images/game/Glove.png")
@ -15,37 +21,52 @@ PerfectPowerFlickerRight = playdate.graphics.image.new("images/game/PerfectPower
DarkSkinFan = playdate.graphics.image.new("images/game/DarkSkinFan.png")
-- luacheck: ignore
---@type pd_image
PlayerFrown = playdate.graphics.image.new("images/game/PlayerFrown.png")
DarkPlayerAwayBase = playdate.graphics.image.new("images/game/DarkPlayerAwayBase.png")
-- luacheck: ignore
---@type pd_image
PerfectPowerFlickerLeft = playdate.graphics.image.new("images/game/PerfectPowerFlickerLeft.png")
-- luacheck: ignore
---@type pd_image
LightPlayerFrown = playdate.graphics.image.new("images/game/LightPlayerFrown.png")
-- luacheck: ignore
---@type pd_image
LightPlayerAwayBack = playdate.graphics.image.new("images/game/LightPlayerAwayBack.png")
-- luacheck: ignore
---@type pd_image
DarkPlayerAwayBack = playdate.graphics.image.new("images/game/DarkPlayerAwayBack.png")
-- luacheck: ignore
---@type pd_image
LightPlayerHomeBack = playdate.graphics.image.new("images/game/LightPlayerHomeBack.png")
-- luacheck: ignore
---@type pd_image
BigBat = playdate.graphics.image.new("images/game/BigBat.png")
-- luacheck: ignore
---@type pd_image
LightSkinFan = playdate.graphics.image.new("images/game/LightSkinFan.png")
-- luacheck: ignore
---@type pd_image
DarkPlayerHomeBack = playdate.graphics.image.new("images/game/DarkPlayerHomeBack.png")
-- luacheck: ignore
---@type pd_image
GloveHoldingBall = playdate.graphics.image.new("images/game/GloveHoldingBall.png")
-- luacheck: ignore
---@type pd_image
GameLogo = playdate.graphics.image.new("images/game/GameLogo.png")
-- luacheck: ignore
---@type pd_image
Hat = playdate.graphics.image.new("images/game/Hat.png")
LightPlayerHomeBase = playdate.graphics.image.new("images/game/LightPlayerHomeBase.png")
-- luacheck: ignore
---@type pd_image
DarkPlayerBase = playdate.graphics.image.new("images/game/DarkPlayerBase.png")
Hat = playdate.graphics.image.new("images/game/Hat.png")
-- luacheck: ignore
---@type pd_image
MenuImage = playdate.graphics.image.new("images/game/MenuImage.png")
-- luacheck: ignore
---@type pd_image
PlayerSmile = playdate.graphics.image.new("images/game/PlayerSmile.png")
Minimap = playdate.graphics.image.new("images/game/Minimap.png")
-- luacheck: ignore
---@type pd_image
Minimap = playdate.graphics.image.new("images/game/Minimap.png")
DarkPlayerHomeBase = playdate.graphics.image.new("images/game/DarkPlayerHomeBase.png")
-- luacheck: ignore
---@type pd_image
GrassBackground = playdate.graphics.image.new("images/game/GrassBackground.png")
@ -54,13 +75,10 @@ GrassBackground = playdate.graphics.image.new("images/game/GrassBackground.png")
GrassBackgroundSmall = playdate.graphics.image.new("images/game/GrassBackgroundSmall.png")
-- luacheck: ignore
---@type pd_image
LightPlayerBase = playdate.graphics.image.new("images/game/LightPlayerBase.png")
DarkPlayerSmile = playdate.graphics.image.new("images/game/DarkPlayerSmile.png")
-- luacheck: ignore
---@type pd_image
PerfectPowerBg = playdate.graphics.image.new("images/game/PerfectPowerBg.png")
-- luacheck: ignore
---@type pd_image
LightPlayerBack = playdate.graphics.image.new("images/game/LightPlayerBack.png")
-- luacheck: ignore
---@type pd_sampleplayer

View File

@ -1,10 +1,10 @@
--- @alias Runner {
--- x: number,
--- y: number,
--- nextBase: Base,
--- prevBase: Base | nil,
--- forcedTo: Base | nil,
--- }
--- @class Runner
--- @field x number
--- @field y number
--- @field nextBase Base
--- @field prevBase Base | nil
--- @field forcedTo Base | nil
--- @field spriteIndex number
---@class Baserunning
---@field runners Runner[]
@ -158,6 +158,7 @@ function Baserunning:pushNewBatter()
nextBase = C.RightHandedBattersBox,
prevBase = nil,
forcedTo = C.Bases[C.First],
spriteIndex = math.random(#HomeTeamSpriteGroup),
}
self.runners[#self.runners + 1] = new
self.batter = new

View File

@ -17,7 +17,7 @@ local function drawFielderGlove(ball, fielderX, fielderY, flip)
end
end
---@param playerSprites SpriteCollection
---@param playerSprites PlayerImageBundle
---@param ball Point3d
---@param x number
---@param y number

View File

@ -1,7 +1,9 @@
-- selene: allow(shadowing)
local gfx = playdate.graphics
---@alias SpriteCollection { smiling: pd_image, lowHat: pd_image, frowning: pd_image, back: pd_image }
---@alias PlayerImageBundle { smiling: pd_image, lowHat: pd_image, frowning: pd_image, back: pd_image }
---@alias SpriteCollection PlayerImageBundle[]
---@param image pd_image
---@param drawInverted boolean
@ -16,31 +18,31 @@ function maybeDrawInverted(image, x, y, drawInverted)
end
--- TODO: Custom names on jerseys?
---@return SpriteCollection
---@return PlayerImageBundle
---@param base pd_image
---@param isDark boolean
function buildCollection(base, back, logo, isDark)
---@param isInverted boolean
function buildPlayerBundle(base, back, smile, frown, logo, isInverted)
local smiling = gfx.image.new(base:getSize())
gfx.lockFocus(smiling)
base:draw(0, 0)
Hat:draw(6, 0)
PlayerSmile:draw(5, 9)
maybeDrawInverted(logo, 3, 25, isDark)
smile:draw(5, 9)
maybeDrawInverted(logo, 3, 25, isInverted)
local lowHat = gfx.image.new(base:getSize())
gfx.lockFocus(lowHat)
base:draw(0, 0)
Hat:draw(6, 2)
PlayerSmile:draw(5, 9)
maybeDrawInverted(logo, 3, 25, isDark)
smile:draw(5, 9)
maybeDrawInverted(logo, 3, 25, isInverted)
local frowning = gfx.image.new(base:getSize())
gfx.lockFocus(frowning)
base:draw(0, 0)
maybeDrawInverted(logo, 3, 25, isDark)
maybeDrawInverted(logo, 3, 25, isInverted)
Hat:draw(6, 0)
PlayerFrown:draw(5, 9)
frown:draw(5, 9)
gfx.unlockFocus()
@ -52,20 +54,24 @@ function buildCollection(base, back, logo, isDark)
}
end
--selene: allow(unscoped_variables)
---@type SpriteCollection
AwayTeamSprites = nil
AwayTeamSpriteGroup = nil
--selene: allow(unscoped_variables)
---@type SpriteCollection
HomeTeamSprites = nil
HomeTeamSpriteGroup = nil
function replaceAwayLogo(logo)
AwayTeamSprites = buildCollection(DarkPlayerBase, DarkPlayerBack, logo, true)
AwayTeamSpriteGroup = {
buildPlayerBundle(DarkPlayerAwayBase, DarkPlayerAwayBack, DarkPlayerSmile, DarkPlayerFrown, logo, true),
buildPlayerBundle(LightPlayerAwayBase, LightPlayerAwayBack, LightPlayerSmile, LightPlayerFrown, logo, true),
}
end
function replaceHomeLogo(logo)
HomeTeamSprites = buildCollection(LightPlayerBase, LightPlayerBack, logo, false)
HomeTeamSpriteGroup = {
buildPlayerBundle(DarkPlayerHomeBase, DarkPlayerHomeBack, DarkPlayerSmile, DarkPlayerFrown, logo, true),
buildPlayerBundle(LightPlayerHomeBase, LightPlayerHomeBack, LightPlayerSmile, LightPlayerFrown, logo, true),
}
end
replaceAwayLogo(Logos[1].image)

View File

@ -3,6 +3,7 @@
--- @field y number
--- @field target XyPair | nil
--- @field speed number
--- @field spriteIndex number
---@class Fielders
---@field first Fielder
@ -30,6 +31,7 @@ local function newFielder(name, speed)
return {
name = name,
speed = speed * C.FielderRunMult,
spriteIndex = math.random(#HomeTeamSpriteGroup)
}
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

104
src/fonts/Nano Sans.fnt Normal file
View File

@ -0,0 +1,104 @@
tracking=1
space 2
A 3
B 3
T 3
a 3
b 3
c 3
d 3
e 3
f 3
g 3
h 3
i 1
l 2
q 3
r 3
s 3
w 5
z 3
j 1
n 3
o 3
p 3
m 5
k 3
t 3
u 3
v 3
y 3
x 3
. 1
C 3
D 3
E 3
F 3
G 3
H 3
I 3
0 3
1 3
8 3
9 3
7 3
6 3
5 3
4 3
3 3
2 3
: 1
; 1
! 1
" 3
{ 3
} 3
| 1
J 3
K 3
L 3
M 5
N 4
O 3
W 5
U 3
V 3
X 3
Y 3
Z 3
Q 3
S 3
R 3
P 3
[ 2
] 2
^ 3
< 3
= 3
> 3
? 3
@ 4
\ 3
_ 3
` 2
~ 5
¥ 3
… 5
™ 5
‼ 3
© 5
® 5
<EFBFBD> 5
# 5
/ 3
- 3
+ 3
, 1
* 3
) 2
( 2
' 1
$ 3
% 3
& 4

View File

@ -27,15 +27,16 @@ blipper = {}
--- Build an object that simply "blips" between the given images at the given interval.
--- Expects `playdate.graphics.animation.blinker.updateAll()` to be called on every update.
function blipper.new(msInterval, smiling, lowHat)
function blipper.new(msInterval, spriteCollection)
local blinker = playdate.graphics.animation.blinker.new(msInterval, msInterval, true)
blinker:start()
return {
blinker = blinker,
smiling = smiling,
lowHat = lowHat,
draw = function(self, disableBlipping, x, y)
local currentImage = (disableBlipping or self.blinker.on) and self.lowHat or self.smiling
draw = function(self, disableBlipping, x, y, hasSpriteIndex)
local spriteBundle = spriteCollection[hasSpriteIndex.spriteIndex]
local currentImage = (disableBlipping or self.blinker.on) and spriteBundle.lowHat or spriteBundle.smiling
local offsetY = currentImage == lowHat and -1 or 0
currentImage:draw(x, y + offsetY)
end,

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 738 B

After

Width:  |  Height:  |  Size: 738 B

View File

Before

Width:  |  Height:  |  Size: 601 B

After

Width:  |  Height:  |  Size: 601 B

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 603 B

View File

@ -5,7 +5,8 @@ MainMenu = {
}
local gfx = playdate.graphics
local StartFont <const> = gfx.font.new("fonts/Roobert-20-Medium.pft")
local StartFont <const> = gfx.font.new("fonts/Roobert-11-Medium.pft")
local TinyFont <const> = gfx.font.new("fonts/Nano Sans.pft")
--- Take control of playdate.update
--- Will replace playdate.update when the menu is done.
@ -21,8 +22,8 @@ local inningCountSelection = 3
local function startGame()
local next = MainMenu.next.new({
finalInning = inningCountSelection,
homeTeamSprites = HomeTeamSprites,
awayTeamSprites = AwayTeamSprites,
homeTeamSpriteGroup = HomeTeamSpriteGroup,
awayTeamSpriteGroup = AwayTeamSpriteGroup,
})
playdate.resetElapsedTime()
transitionBetween(MainMenu, next)
@ -87,16 +88,18 @@ function MainMenu:update()
startGame()
end
if playdate.buttonJustPressed(playdate.kButtonUp) then
inningCountSelection = inningCountSelection + 1
inningCountSelection = math.min(99, inningCountSelection + 1)
end
if playdate.buttonJustPressed(playdate.kButtonDown) then
inningCountSelection = inningCountSelection - 1
inningCountSelection = math.max(1, inningCountSelection - 1)
end
GameLogo:drawCentered(C.Center.x, 50)
local logoCenter = 90
GameLogo:drawCentered(C.Center.x, logoCenter)
TinyFont:drawTextAligned("a game by Sage", C.Center.x, logoCenter + 35, kTextAlignment.center)
StartFont:drawTextAligned("Press A to start!", C.Center.x, 140, kTextAlignment.center)
gfx.drawTextAligned("with " .. inningCountSelection .. " innings", C.Center.x, 190, kTextAlignment.center)
StartFont:drawTextAligned("Press A to start!", C.Center.x, 180, kTextAlignment.center)
gfx.drawTextAligned("with " .. inningCountSelection .. " innings", C.Center.x, 210, kTextAlignment.center)
local ball = {
x = animatorX:currentValue(),
@ -105,10 +108,9 @@ function MainMenu:update()
size = 6,
}
local ballIsHeld = drawFielder(AwayTeamSprites, ball, 30, 200)
ballIsHeld = drawFielder(HomeTeamSprites, ball, 350, 200, playdate.graphics.kImageFlippedX) or ballIsHeld
local ballIsHeld = drawFielder(AwayTeamSpriteGroup[1], ball, 30, 200)
ballIsHeld = drawFielder(HomeTeamSpriteGroup[2], ball, 350, 200, playdate.graphics.kImageFlippedX) or ballIsHeld
-- drawFielder(AwayTeamSprites, { x = 0, y = 0, z = 0 }, ball.x, ball.y)
if not ballIsHeld then
gfx.setLineWidth(2)

View File

@ -69,8 +69,8 @@ local teams <const> = {
---@class Settings
---@field finalInning number
---@field userTeam TeamId | nil
---@field awayTeamSprites SpriteCollection
---@field homeTeamSprites SpriteCollection
---@field awayTeamSpriteGroup SpriteCollection
---@field homeTeamSpriteGroup SpriteCollection
---@class MutableState
---@field deltaSeconds number
@ -114,8 +114,8 @@ function Game.new(settings, announcer, fielding, baserunning, npc, state)
fielding = fielding or Fielding.new()
settings.userTeam = "away"
local homeTeamBlipper = blipper.new(100, settings.homeTeamSprites.smiling, settings.homeTeamSprites.lowHat)
local awayTeamBlipper = blipper.new(100, settings.awayTeamSprites.smiling, settings.awayTeamSprites.lowHat)
local homeTeamBlipper = blipper.new(100, settings.homeTeamSpriteGroup)
local awayTeamBlipper = blipper.new(100, settings.awayTeamSpriteGroup)
local battingTeam = "away"
local runnerBlipper = battingTeam == "away" and awayTeamBlipper or homeTeamBlipper
local ball = Ball.new(gfx.animator)
@ -138,8 +138,8 @@ function Game.new(settings, announcer, fielding, baserunning, npc, state)
inning = 1,
pitchIsOver = true,
didSwing = false,
battingTeamSprites = settings.awayTeamSprites,
fieldingTeamSprites = settings.homeTeamSprites,
battingTeamSprites = settings.awayTeamSpriteGroup,
fieldingTeamSprites = settings.homeTeamSpriteGroup,
runnerBlipper = runnerBlipper,
stats = Statistics.new(),
},
@ -266,12 +266,12 @@ function Game:nextHalfInning()
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.battingTeamSprites = self.settings.homeTeamSpriteGroup
self.state.runnerBlipper = self.homeTeamBlipper
self.state.fieldingTeamSprites = self.settings.awayTeamSprites
self.state.fieldingTeamSprites = self.settings.awayTeamSpriteGroup
else
self.state.battingTeamSprites = self.settings.awayTeamSprites
self.state.fieldingTeamSprites = self.settings.homeTeamSprites
self.state.battingTeamSprites = self.settings.awayTeamSpriteGroup
self.state.fieldingTeamSprites = self.settings.homeTeamSpriteGroup
self.state.runnerBlipper = self.awayTeamBlipper
end
end)
@ -608,7 +608,7 @@ function Game:update()
for _, fielder in pairs(self.fielding.fielders) do
addDraw(fielder.y + danceOffset, function()
local ballHeldByThisFielder =
drawFielder(self.state.fieldingTeamSprites, self.state.ball, fielder.x, fielder.y + danceOffset)
drawFielder(self.state.fieldingTeamSprites[fielder.spriteIndex], self.state.ball, fielder.x, fielder.y + danceOffset)
if ballHeldByThisFielder then
ballHeldBy = fielder
end
@ -620,13 +620,13 @@ function Game:update()
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)
self.state.battingTeamSprites[runner.spriteIndex].back:draw(runner.x, runner.y - playerHeightOffset)
else
self.state.battingTeamSprites.smiling:draw(runner.x, runner.y - playerHeightOffset)
self.state.battingTeamSprites[runner.spriteIndex].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)
self.state.runnerBlipper:draw(false, runner.x, runner.y - playerHeightOffset, runner)
end
end)
end
@ -644,13 +644,15 @@ function Game:update()
end
for _, runner in pairs(self.baserunning.outRunners) do
self.state.battingTeamSprites.frowning:draw(runner.x, runner.y - playerHeightOffset)
self.state.battingTeamSprites[runner.spriteIndex].frowning:draw(runner.x, runner.y - playerHeightOffset)
end
for _, runner in pairs(self.baserunning.scoredRunners) do
self.state.battingTeamSprites.smiling:draw(runner.x, runner.y - playerHeightOffset)
self.state.battingTeamSprites[runner.spriteIndex].smiling:draw(runner.x, runner.y - playerHeightOffset)
end
throwMeter:drawNearFielder(ballHeldBy)
if self:userIsOn('defense') then
throwMeter:drawNearFielder(ballHeldBy)
end
if not ballHeldBy then
gfx.setLineWidth(2)