From 4fc49d3631b53a3896cc991a32a23e0339683bef Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Thu, 6 Feb 2025 13:34:03 -0500 Subject: [PATCH] Add minimap. Some other small refactors. --- src/images/game/minimap.png | Bin 0 -> 1316 bytes src/main.lua | 92 ++++++++++++++++-------------------- src/utils.lua | 33 ++++++++++++- 3 files changed, 72 insertions(+), 53 deletions(-) create mode 100644 src/images/game/minimap.png diff --git a/src/images/game/minimap.png b/src/images/game/minimap.png new file mode 100644 index 0000000000000000000000000000000000000000..18aa8e8e6b094d4422b2ba6b3380f780ca8bf835 GIT binary patch literal 1316 zcmV+<1>5?GP)EX>4Tx04R}tkv&MmKpe$iQ%glE4h9i%$WWc^q9Ts93Pq?8YK2xEOfLO`CM`*d zi=*ILaPVWX>fqw6tAnc`2!4RL3r>nIQsV!TLW>v=j{EWM-sA2aAT%pXw|e4$ZrElz znUJ!%RVn-m0U-<_2vK2HZBCX`@Eu?G2=MhT#y-|(eB_sHPi~FJwK| zIB#**D|ObsCx2liZ>%hHo#rT#Si}-!NKjEn85P)w(Q1%lAx--UAODE!m&v7)s|-et z1yrF!asA+b@Vi^9Fg58VMG`>h#c@8yfbcHRYB4ex=P&u2Zz9T zk+Ro)-rd{X+rMYp{rvzUB65aA1Y)`X000JJOGiWi{{a60|De66lK=n!32;bRa{vG? zBLDy{BLR4&KXw2B00(qQO+^Rk0tOK_Bb2Mf761SM8FWQhbVF}#ZDnqB07G(RVRU6= zAa`kWXdp*PO;A^X4i^9b0{uxuK~#9!?VQJw!ypVmu_pij%gKpM;xR_OjXCXZ)*wBr zQNl6Cc#o&C8$1YP?KOXRzx539IF99Kn&U){J;!-j3oa4*w@++8=l}cdgVWzu#u)>( z6x9;$>V-)A)s%DgB?4&iM!nonD*JpMxPwQ)!+efyzcQcoF>_%HSH-xkg*!|2B3t0u z;=JIk6wpO_B zgXi76vM}|9uEvFEcoetvih&e}kj3(Lu%h7YAbzuOgisilT-AYDaE}re`zzh(>0AiED`ER@5qadoR2U^TJBKxF_3)w8svW_h4!q!$2 z2@$Hpi-rgt;6*}&F7TotLMM0;5TP49Z-~$lo+m`;3eO87P$6CIp=O0gfws269rf(##8%jqzGA9s+%6UVK_%7YwA@j-*J>e}6nOB7v1@AHj zR)ZJ`Z<$1@K#YdBoyy%k=(QpueL+_6P*XWiORNNsR0mdo7zJ+(QwISlShpbE+bLmk87 z(nhmgIqUD2M8Y$3VJSrJRmV~A#!$RR`VJyRfyhvj$^xV7L=Si_03#rJ!TZa?PfKip ao!bWyF3p{}dZk1F0000 = playdate.sound.sampleplayer.new("sounds/boot-tune.wav") local TinnyBackground = playdate.sound.sampleplayer.new("sounds/tinny-background.wav") local BatCrackSound = playdate.sound.sampleplayer.new("sounds/bat-crack-reverb.wav") local GrassBackground = gfx.image.new("images/game/grass.png") --[[@as pd_image]] +local Minimap = gfx.image.new("images/game/minimap.png") --[[@as pd_image]] local PlayerFrown = gfx.image.new("images/game/player-frown.png") --[[@as pd_image]] local PlayerSmile = gfx.image.new("images/game/player.png") --[[@as pd_image]] local PlayerBack = gfx.image.new("images/game/player-back.png") --[[@as pd_image]] @@ -327,7 +328,7 @@ function pitch(pitchFlyTimeMs, pitchTypeIndex) catcherThrownBall = false offenseMode = Offense.batting - local current = Pitches[pitchTypeIndex or math.random(#Pitches)] + local current = Pitches[pitchTypeIndex] ballAnimatorX = current.x ballAnimatorY = current.y or Pitches[1].y @@ -355,13 +356,9 @@ local BaseHitbox = 10 ---@param y number ---@return Base | nil function isTouchingBase(x, y) - for _, base in ipairs(Bases) do - if utils.distanceBetween(x, y, base.x, base.y) < BaseHitbox then - return base - end - end - - return nil + return utils.first(Bases, function(base) + return utils.distanceBetween(x, y, base.x, base.y) < BaseHitbox + end) end local BallCatchHitbox = 3 @@ -377,19 +374,16 @@ end ---@param base Base ---@return Runner | nil -function getRunnerTargeting(base) - for _, runner in pairs(runners) do - if runner.nextBase == base then - return runner - end - end - return nil +function getRunnerWithNextBase(base) + return utils.first(runners, function(runner) + return runner.nextBase == base + end) end function updateForcedRunners() local stillForced = true for _, base in ipairs(Bases) do - local runnerTargetingBase = getRunnerTargeting(base) + local runnerTargetingBase = getRunnerWithNextBase(base) if runnerTargetingBase then if stillForced then runnerTargetingBase.forcedTo = base @@ -453,7 +447,7 @@ end function getForcedOutTargets() local targets = {} for _, base in ipairs(Bases) do - local runnerTargetingBase = getRunnerTargeting(base) + local runnerTargetingBase = getRunnerWithNextBase(base) if runnerTargetingBase then targets[#targets + 1] = base else @@ -490,7 +484,6 @@ function getNextOutTarget() end local baseCloseToStrandedRunner = getBaseOfStrandedRunner() - -- TODO: If another fielder is closer, throw it to them, instead if baseCloseToStrandedRunner then return baseCloseToStrandedRunner.x, baseCloseToStrandedRunner.y end @@ -510,28 +503,10 @@ function tryToMakeAnOut(fielder) end end ---- Push the given obect at the given speed toward a target. Speed should be pre-multiplied by the frame's delta time. ---- Stops when within 1. Returns true only if the object did actually move. ----@param mover { x: number, y: number } ----@param speed number ----@param target { x: number, y: number } ----@return boolean -function moveAtSpeed(mover, speed, target) - local x, y, distance = utils.normalizeVector(mover.x, mover.y, target.x, target.y) - - if distance > 1 then - mover.x = mover.x - (x * speed) - mover.y = mover.y - (y * speed) - return true - end - - return false -end - ---@param fielder Fielder function updateFielder(fielder) if fielder.target ~= nil then - if not moveAtSpeed(fielder, fielder.speed * deltaSeconds, fielder.target) then + if not utils.moveAtSpeed(fielder, fielder.speed * deltaSeconds, fielder.target) then fielder.target = nil end end @@ -540,7 +515,6 @@ function updateFielder(fielder) return end - -- TODO: Check for double-plays or other available outs. local touchedBase = isTouchingBase(fielder.x, fielder.y) for i, runner in pairs(runners) do local runnerOnBase = isTouchingBase(runner.x, runner.y) @@ -570,7 +544,7 @@ function updateFielders() end -- if offenseMode == Offense.batting then - -- moveAtSpeed( + -- utils.moveAtSpeed( -- fielders.catcher, -- fielders.catcher.speed * 2 * deltaSeconds, -- { x = math.min(Center.x + 15, ball.x), y = fielders.catcher.y } @@ -623,7 +597,6 @@ function updateRunner(runner, runnerIndex, appliedSpeed) end end - -- TODO: Also move if forced to 😅 local autoRun = (nearestBaseDistance > 40 or runner.forcedTo) and mult * autoRunSpeed or nearestBaseDistance < 5 and 0 or (nearestBase == runner.nextBase and autoRunSpeed or -1 * autoRunSpeed) @@ -698,15 +671,13 @@ end function updateRunning(appliedSpeed) ball.size = ballSizeAnimator:currentValue() - local nonBatterRunners = utils.filter(runners, function(runner) - return runner ~= batter - end) - local runnerMoved = false -- TODO: Filter for the runner closest to the currently-held direction button - for runnerIndex, runner in ipairs(nonBatterRunners) do - runnerMoved = updateRunner(runner, runnerIndex, appliedSpeed) or runnerMoved + for runnerIndex, runner in ipairs(runners) do + if runner ~= batter then + runnerMoved = updateRunner(runner, runnerIndex, appliedSpeed) or runnerMoved + end end return runnerMoved @@ -768,7 +739,7 @@ function buttonControlledThrow(thrower, throwFlyMs, forbidThrowHome) if playdate.buttonIsPressed(playdate.kButtonLeft) then targetBase = Bases[Third] elseif playdate.buttonIsPressed(playdate.kButtonUp) then - targetBase = Bases[Second] -- TODO - or shortstop - whoever's closer + targetBase = Bases[Second] elseif playdate.buttonIsPressed(playdate.kButtonRight) then targetBase = Bases[First] elseif not forbidThrowHome and playdate.buttonIsPressed(playdate.kButtonDown) then @@ -820,7 +791,7 @@ function updateGameState() if secondsSincePitchAllowed > PitchAfterSeconds then if playerOnOffense then - pitch(PitchFlyMs) + pitch(PitchFlyMs, math.random(#Pitches)) else local throwFly = readThrow() if throwFly and not buttonControlledThrow(fielders.pitcher, throwFly, true) then @@ -864,8 +835,25 @@ function updateGameState() walkAwayOutRunners() end --- TODO -function drawMinimap() end +local MinimapSizeX, MinimapSizeY = Minimap:getSize() +local MinimapPosX, MinimapPosY = Screen.W - MinimapSizeX, Screen.H - MinimapSizeY + +local FieldHeight = Bases[Home].y - Bases[Second].y + +local MinimapMultX = 0.75 * MinimapSizeX / Screen.W +local MinimapOffsetX = MinimapPosX + 5 +local MinimapMultY = 0.70 * MinimapSizeY / FieldHeight +local MinimapOffsetY = MinimapPosY - 15 + +function drawMinimap() + Minimap:draw(MinimapPosX, MinimapPosY) + gfx.setColor(gfx.kColorBlack) + for _, runner in pairs(runners) do + local x = (MinimapMultX * runner.x) + MinimapOffsetX + local y = (MinimapMultY * runner.y) + MinimapOffsetY + gfx.fillRect(x, y, 8, 8) + end +end ---@param fielder Fielder ---@return boolean isHoldingBall @@ -916,7 +904,7 @@ function playdate.update() playdate.ui.crankIndicator:draw() end - -- TODO? Change blip speed depending on runner speed? + -- TODO? Scale sprites down as y increases for _, runner in pairs(runners) do if runner == batter then if batAngleDeg > 50 and batAngleDeg < 200 then @@ -925,7 +913,7 @@ function playdate.update() PlayerSmile:draw(runner.x, runner.y) end else - -- TODO? Scale sprites down as y increases + -- TODO? Change blip speed depending on runner speed? PlayerImageBlipper:draw(false, runner.x, runner.y) end end diff --git a/src/utils.lua b/src/utils.lua index 3de3407..b401a7d 100644 --- a/src/utils.lua +++ b/src/utils.lua @@ -47,6 +47,24 @@ function utils.normalizeVector(x1, y1, x2, y2) return x / distance, y / distance, distance end +--- Push the given obect at the given speed toward a target. Speed should be pre-multiplied by the frame's delta time. +--- Stops when within 1. Returns true only if the object did actually move. +---@param mover { x: number, y: number } +---@param speed number +---@param target { x: number, y: number } +---@return boolean +function utils.moveAtSpeed(mover, speed, target) + local x, y, distance = utils.normalizeVector(mover.x, mover.y, target.x, target.y) + + if distance > 1 then + mover.x = mover.x - (x * speed) + mover.y = mover.y - (y * speed) + return true + end + + return false +end + ---@generic T ---@param array T[] ---@param condition fun(T): boolean @@ -61,6 +79,19 @@ function utils.filter(array, condition) return newArray end +---@generic T +---@param array T[] +---@param condition fun(T): boolean +---@return T | nil +function utils.first(array, condition) + for _, element in ipairs(array) do + if condition(element) then + return element + end + end + return nil +end + ---@return number distance, number x, number y function utils.distanceBetween(x1, y1, x2, y2) local x = x1 - x2 @@ -102,7 +133,7 @@ end ---@param array T[] ---@param x number ---@param y number ----@return T,number|nil +---@return T nearest,number |nil distance function utils.getNearestOf(array, x, y, extraCondition) local nearest, nearestDistance = nil, nil for _, element in pairs(array) do