New black away uniforms!

Generate sprites from component images during load.
Should make it easy to swap out logos at runtime.
This commit is contained in:
Sage Vaillancourt 2025-02-11 08:32:51 -05:00
parent 0646663e5e
commit b9d25e18d8
16 changed files with 115 additions and 24 deletions

View File

@ -1,6 +1,9 @@
-- GENERATED FILE - DO NOT EDIT -- GENERATED FILE - DO NOT EDIT
-- Instead, edit the source file directly: assets.lua2p. -- Instead, edit the source file directly: assets.lua2p.
--selene: allow(unused_variable)
--selene: allow(unscoped_variables)
DarkPlayerBack = playdate.graphics.image.new("images/game/DarkPlayerBack.png")
--selene: allow(unused_variable) --selene: allow(unused_variable)
--selene: allow(unscoped_variables) --selene: allow(unscoped_variables)
Glove = playdate.graphics.image.new("images/game/Glove.png") Glove = playdate.graphics.image.new("images/game/Glove.png")
@ -9,25 +12,37 @@ Glove = playdate.graphics.image.new("images/game/Glove.png")
PlayerFrown = playdate.graphics.image.new("images/game/PlayerFrown.png") PlayerFrown = playdate.graphics.image.new("images/game/PlayerFrown.png")
--selene: allow(unused_variable) --selene: allow(unused_variable)
--selene: allow(unscoped_variables) --selene: allow(unscoped_variables)
PlayerBack = playdate.graphics.image.new("images/game/PlayerBack.png") BaseLogo = playdate.graphics.image.new("images/game/BaseLogo.png")
--selene: allow(unused_variable)
--selene: allow(unscoped_variables)
PlayerLowHat = playdate.graphics.image.new("images/game/PlayerLowHat.png")
--selene: allow(unused_variable) --selene: allow(unused_variable)
--selene: allow(unscoped_variables) --selene: allow(unscoped_variables)
GloveHoldingBall = playdate.graphics.image.new("images/game/GloveHoldingBall.png") GloveHoldingBall = playdate.graphics.image.new("images/game/GloveHoldingBall.png")
--selene: allow(unused_variable) --selene: allow(unused_variable)
--selene: allow(unscoped_variables) --selene: allow(unscoped_variables)
Hat = playdate.graphics.image.new("images/game/Hat.png")
--selene: allow(unused_variable)
--selene: allow(unscoped_variables)
DarkPlayerBase = playdate.graphics.image.new("images/game/DarkPlayerBase.png")
--selene: allow(unused_variable)
--selene: allow(unscoped_variables)
MenuImage = playdate.graphics.image.new("images/game/MenuImage.png") MenuImage = playdate.graphics.image.new("images/game/MenuImage.png")
--selene: allow(unused_variable) --selene: allow(unused_variable)
--selene: allow(unscoped_variables) --selene: allow(unscoped_variables)
Player = playdate.graphics.image.new("images/game/Player.png") PlayerSmile = playdate.graphics.image.new("images/game/PlayerSmile.png")
--selene: allow(unused_variable) --selene: allow(unused_variable)
--selene: allow(unscoped_variables) --selene: allow(unscoped_variables)
Minimap = playdate.graphics.image.new("images/game/Minimap.png") Minimap = playdate.graphics.image.new("images/game/Minimap.png")
--selene: allow(unused_variable) --selene: allow(unused_variable)
--selene: allow(unscoped_variables) --selene: allow(unscoped_variables)
FrownLogo = playdate.graphics.image.new("images/game/FrownLogo.png")
--selene: allow(unused_variable)
--selene: allow(unscoped_variables)
GrassBackground = playdate.graphics.image.new("images/game/GrassBackground.png") GrassBackground = playdate.graphics.image.new("images/game/GrassBackground.png")
--selene: allow(unused_variable)
--selene: allow(unscoped_variables)
LightPlayerBase = playdate.graphics.image.new("images/game/LightPlayerBase.png")
--selene: allow(unused_variable)
--selene: allow(unscoped_variables)
LightPlayerBack = playdate.graphics.image.new("images/game/LightPlayerBack.png")
--selene: allow(unused_variable) --selene: allow(unused_variable)
--selene: allow(unscoped_variables) --selene: allow(unscoped_variables)

View File

@ -129,6 +129,7 @@ end
---@return Runner ---@return Runner
function Baserunning:newRunner() function Baserunning:newRunner()
local new = { local new = {
-- imageSet = math.random() < C.WokeMeter and FemmeSet or MascSet, -- TODO? lol.
x = C.RightHandedBattersBox.x - 60, x = C.RightHandedBattersBox.x - 60,
y = C.RightHandedBattersBox.y + 60, y = C.RightHandedBattersBox.y + 60,
nextBase = C.RightHandedBattersBox, nextBase = C.RightHandedBattersBox,

View File

@ -1,6 +1,3 @@
-- selene: allow(shadowing)
local gfx = playdate.graphics
local GloveSizeX, GloveSizeY <const> = Glove:getSize() local GloveSizeX, GloveSizeY <const> = Glove:getSize()
local GloveOffX, GloveOffY <const> = GloveSizeX / 2, GloveSizeY / 2 local GloveOffX, GloveOffY <const> = GloveSizeX / 2, GloveSizeY / 2
@ -10,7 +7,7 @@ local GloveOffX, GloveOffY <const> = GloveSizeX / 2, GloveSizeY / 2
---@return boolean isHoldingBall ---@return boolean isHoldingBall
local function drawFielderGlove(ball, fielderX, fielderY) local function drawFielderGlove(ball, fielderX, fielderY)
local distanceFromBall = utils.distanceBetweenZ(fielderX, fielderY, 0, ball.x, ball.y, ball.z) local distanceFromBall = utils.distanceBetweenZ(fielderX, fielderY, 0, ball.x, ball.y, ball.z)
local shoulderX, shoulderY = fielderX + 10, fielderY + 5 local shoulderX, shoulderY = fielderX + 10, fielderY - 5
if distanceFromBall > 20 then if distanceFromBall > 20 then
Glove:draw(shoulderX, shoulderY) Glove:draw(shoulderX, shoulderY)
return false return false
@ -20,11 +17,12 @@ local function drawFielderGlove(ball, fielderX, fielderY)
end end
end end
---@param playerSprites SpriteCollection
---@param ball Point3d ---@param ball Point3d
---@param x number ---@param x number
---@param y number ---@param y number
---@return boolean isHoldingBall ---@return boolean isHoldingBall
function drawFielder(ball, x, y) function drawFielder(playerSprites, ball, x, y)
gfx.fillRect(x, y, 14, 25) playerSprites.smiling:draw(x, y - 20)
return drawFielderGlove(ball, x, y) return drawFielderGlove(ball, x, y)
end end

58
src/draw/player.lua Normal file
View File

@ -0,0 +1,58 @@
-- selene: allow(shadowing)
local gfx = playdate.graphics
---@alias SpriteCollection { smiling: pd_image, lowHat: pd_image, frowning: pd_image, back: pd_image }
---@param image pd_image
---@param drawInverted boolean
function maybeDrawInverted(image, x, y, drawInverted)
-- TODO: Bring logo up a pixel on the dark player base?
local drawMode = gfx.getImageDrawMode()
if drawInverted then
gfx.setImageDrawMode(gfx.kDrawModeInverted)
end
image:draw(x, y)
gfx.setImageDrawMode(drawMode)
end
---@return SpriteCollection
---@param base pd_image
---@param isDark boolean
function buildCollection(base, back, logo, isDark)
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)
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)
local frowning = gfx.image.new(base:getSize())
gfx.lockFocus(frowning)
base:draw(0, 0)
maybeDrawInverted(logo, 3, 25, isDark)
Hat:draw(6, 0)
PlayerFrown:draw(5, 9)
gfx.unlockFocus()
return {
smiling = smiling,
lowHat = lowHat,
frowning = frowning,
back = back,
}
end
--selene: allow(unscoped_variables)
AwayTeamSprites = buildCollection(DarkPlayerBase, DarkPlayerBack, BaseLogo, true)
--selene: allow(unscoped_variables)
HomeTeamSprites = buildCollection(LightPlayerBase, LightPlayerBack, FrownLogo, false)

View File

@ -156,12 +156,14 @@ function Fielding:celebrate()
FielderDanceAnimator:reset(C.DanceBounceMs) FielderDanceAnimator:reset(C.DanceBounceMs)
end end
---@param fielderSprites SpriteCollection
---@param ball Point3d ---@param ball Point3d
---@return boolean ballIsHeldByAFielder ---@return boolean ballIsHeldByAFielder
function Fielding:drawFielders(ball) function Fielding:drawFielders(fielderSprites, ball)
local ballIsHeld = false local ballIsHeld = false
local danceOffset = FielderDanceAnimator:currentValue()
for _, fielder in pairs(self.fielders) do for _, fielder in pairs(self.fielders) do
ballIsHeld = drawFielder(ball, fielder.x, fielder.y + FielderDanceAnimator:currentValue()) or ballIsHeld ballIsHeld = drawFielder(fielderSprites, ball, fielder.x, fielder.y + danceOffset) or ballIsHeld
end end
return ballIsHeld return ballIsHeld
end end

View File

@ -25,20 +25,23 @@ blipper = {}
--- Build an object that simply "blips" between the given images at the given interval. --- 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. --- Expects `playdate.graphics.animation.blinker.updateAll()` to be called on every update.
function blipper.new(msInterval, image1, image2) function blipper.new(msInterval, smiling, lowHat)
local blinker = playdate.graphics.animation.blinker.new(msInterval, msInterval, true) local blinker = playdate.graphics.animation.blinker.new(msInterval, msInterval, true)
blinker:start() blinker:start()
return { return {
blinker = blinker, blinker = blinker,
image1 = image1, smiling = smiling,
image2 = image2, lowHat = lowHat,
draw = function(self, disableBlipping, x, y) draw = function(self, disableBlipping, x, y)
local currentImage = (disableBlipping or self.blinker.on) and self.image2 or self.image1 local currentImage = (disableBlipping or self.blinker.on) and self.lowHat or self.smiling
local offsetY = currentImage == PlayerLowHat and -1 or 0 local offsetY = currentImage == lowHat and -1 or 0
currentImage:draw(x, y + offsetY) currentImage:draw(x, y + offsetY)
end, end,
} }
end end
--selene: allow(unscoped_variables) --selene: allow(unscoped_variables)
PlayerImageBlipper = blipper.new(100, Player, PlayerLowHat) HomeTeamBlipper = blipper.new(100, HomeTeamSprites.smiling, HomeTeamSprites.lowHat)
--selene: allow(unscoped_variables)
AwayTeamBlipper = blipper.new(100, AwayTeamSprites.smiling, AwayTeamSprites.lowHat)

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

BIN
src/images/game/Hat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 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.7 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 601 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

View File

@ -23,6 +23,7 @@ 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/overlay.lua' import 'draw/overlay.lua'
import 'draw/fielder.lua' import 'draw/fielder.lua'
@ -64,6 +65,9 @@ local teams <const> = {
local UserTeam <const> = teams.away local UserTeam <const> = teams.away
local battingTeam = teams.away local battingTeam = teams.away
local battingTeamSprites = AwayTeamSprites
local fieldingTeamSprites = HomeTeamSprites
local runnerBlipper = battingTeam == teams.away and AwayTeamBlipper or HomeTeamBlipper
local inning = 1 local inning = 1
local offenseState = C.Offense.batting local offenseState = C.Offense.batting
@ -174,6 +178,15 @@ local function nextHalfInning()
-- Delay to keep end-of-inning on the scoreboard for a few seconds -- Delay to keep end-of-inning on the scoreboard for a few seconds
playdate.timer.new(3000, function() playdate.timer.new(3000, function()
battingTeam = currentlyFieldingTeam battingTeam = currentlyFieldingTeam
if battingTeam == teams.home then
battingTeamSprites = HomeTeamSprites
runnerBlipper = HomeTeamBlipper
fieldingTeamSprites = AwayTeamSprites
else
battingTeamSprites = AwayTeamSprites
fieldingTeamSprites = HomeTeamSprites
runnerBlipper = AwayTeamBlipper
end
if gameOver then if gameOver then
announcer:say("AND THAT'S THE BALL GAME!") announcer:say("AND THAT'S THE BALL GAME!")
else else
@ -279,6 +292,7 @@ local function updateBatting(batDeg, batSpeed)
pitchTracker:reset() pitchTracker:reset()
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)
launchBall(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000, nil, hitBallScaler) launchBall(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000, nil, hitBallScaler)
-- TODO? A dramatic eye-level view on a home-run could be sick.
if utils.isFoulBall(ballDestX, ballDestY) then if utils.isFoulBall(ballDestX, ballDestY) then
announcer:say("Foul ball!") announcer:say("Foul ball!")
@ -451,7 +465,7 @@ function playdate.update()
GrassBackground:draw(-400, -240) GrassBackground:draw(-400, -240)
local ballIsHeld = fielding:drawFielders(ball) local ballIsHeld = fielding:drawFielders(fieldingTeamSprites, ball)
if offenseState == C.Offense.batting then if offenseState == C.Offense.batting then
gfx.setLineWidth(5) gfx.setLineWidth(5)
@ -463,18 +477,18 @@ function playdate.update()
for _, runner in pairs(baserunning.runners) do for _, runner in pairs(baserunning.runners) do
if runner == baserunning.batter then if runner == baserunning.batter then
if batAngleDeg > 50 and batAngleDeg < 200 then if batAngleDeg > 50 and batAngleDeg < 200 then
PlayerBack:draw(runner.x, runner.y - playerHeightOffset) battingTeamSprites.back:draw(runner.x, runner.y - playerHeightOffset)
else else
Player:draw(runner.x, runner.y - playerHeightOffset) battingTeamSprites.smiling:draw(runner.x, runner.y - playerHeightOffset)
end end
else else
-- TODO? Change blip speed depending on runner speed? -- TODO? Change blip speed depending on runner speed?
PlayerImageBlipper:draw(false, runner.x, runner.y - playerHeightOffset) runnerBlipper:draw(false, runner.x, runner.y - playerHeightOffset)
end end
end end
for _, runner in pairs(baserunning.outRunners) do for _, runner in pairs(baserunning.outRunners) do
PlayerFrown:draw(runner.x, runner.y - playerHeightOffset) battingTeamSprites.frowning:draw(runner.x, runner.y - playerHeightOffset)
end end
if not ballIsHeld then if not ballIsHeld then