From 4546902f2f854cad172164a8238a0a3718f6a151 Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Sat, 1 Feb 2025 17:45:10 -0500 Subject: [PATCH] Backfill with Use PascalCase for all constants, explain in new README. Split 'make check' into a separate command (less likely to shift your files out from under you than 'make lint') Add __stub.ext.lua with quick explainer for why it's useful. --- Makefile | 8 +- README.md | 30 +++++ __stub.ext.lua | 31 ++++++ src/graphics.lua | 4 +- src/main.lua | 285 +++++++++++++++++++++++------------------------ 5 files changed, 210 insertions(+), 148 deletions(-) create mode 100644 README.md create mode 100644 __stub.ext.lua diff --git a/Makefile b/Makefile index a56fb5b..72976b1 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,10 @@ +SOURCE_FILES := src/utils.lua src/graphics.lua src/main.lua + all: pdc src BatterUp.pdx -lint: +check: + cat __stub.ext.lua <(sed 's/^function/-- selene: allow(unused_variable)\nfunction/' ${PLAYDATE_SDK_PATH}/CoreLibs/__stub.lua) ${SOURCE_FILES} | grep -v '^import' | sed 's///g' | selene - + +lint: check stylua src/ - cat <(sed 's/^function/-- selene: allow(unused_variable)\nfunction/' ${PLAYDATE_SDK_PATH}/CoreLibs/__stub.lua) src/utils.lua src/graphics.lua src/main.lua | grep -v '^import' | selene - diff --git a/README.md b/README.md new file mode 100644 index 0000000..96426df --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# Batter Up! + +## Style Guide + +This project uses StyLua for linting, and Selene for various other checks. + +In addition to using ``, where applicable, PascalCase is used for any +values which should **never** be mutated, including any elements in a table. + +E.g. + +```lua +local Screen = { x = 400, y = 240 } +``` + +should be treated the same as + +```lua +local ScreenX = 400 +local ScreenY = 240 +``` + +Though the compiler will not enforce this. Furthermore, PascalCase is also used +for values that may mutate *themselves* through method calls. For example, +instances of `playdate.graphics.animator`. The one exception is `gfx` + +N.b. Selene does not understand ``, so `make check` uses a `sed` hack to +filter them out. + +`camelCase` tables may be ``, but can still have mutable fields. \ No newline at end of file diff --git a/__stub.ext.lua b/__stub.ext.lua new file mode 100644 index 0000000..2a90888 --- /dev/null +++ b/__stub.ext.lua @@ -0,0 +1,31 @@ +-- A small additional stub to keep Selene from complaining about playdate being missing. +-- Strictly, just putting `playdate = {}` would alleviate these warnings, but I've also +-- found that this quick structure, *actually* prepended to __stub.lua can help +-- SumnekoLua (an IntelliJ plugin) better provide autocompletion. + +-- selene: allow(unused_variable) +-- selene: allow(unscoped_variables) +playdate = { + datastore = {}, + display = {}, + file = {}, + frameTimer = {}, + geometry = {}, + graphics = {}, + inputHandlers = {}, + json = {}, + keyboard = {}, + math = {}, + menu = { + item = {} + }, + pathfinder = {}, + simulator = {}, + sound = { + sampleplayer = {} + }, + string = {}, + table = {}, + timer = {}, + ui = {} +} \ No newline at end of file diff --git a/src/graphics.lua b/src/graphics.lua index e07f182..a4d8dba 100644 --- a/src/graphics.lua +++ b/src/graphics.lua @@ -1,4 +1,4 @@ -local ballBuffer = 5 +local ballBuffer = 5 --- Assumes that background image is of size --- XXX @@ -7,7 +7,7 @@ local ballBuffer = 5 function getDrawOffset(screenW, screenH, ballX, ballY) local offsetX, offsetY if ballY > screenH then - return 0, 0 + return 0, 0 end if ballY < ballBuffer then offsetY = math.max(ballBuffer, -1 * (ballY - ballBuffer)) diff --git a/src/main.lua b/src/main.lua index 64c340c..3b113d9 100644 --- a/src/main.lua +++ b/src/main.lua @@ -18,85 +18,88 @@ import 'utils.lua' --- @alias Fielder { onArrive: fun() | nil, x: number | nil, y: number | nil, target: XYPair | nil, speed: number } -local gfx = playdate.graphics +local gfx = playdate.graphics -local SCREEN = { +local Screen = { W = playdate.display.getWidth(), H = playdate.display.getHeight(), } -local CENTER = xy(SCREEN.W / 2, SCREEN.H / 2) +local Center = xy(Screen.W / 2, Screen.H / 2) -local batCrackSound = playdate.sound.sampleplayer.new("sounds/bat-crack-reverb.wav") -local grassBackground = gfx.image.new("images/game/grass.png") --[[@as PlaydateGraphicsImage]] -local playerFrown = gfx.image.new("images/game/player-frown.png") --[[@as PlaydateGraphicsImage]] +local BatCrackSound = playdate.sound.sampleplayer.new("sounds/bat-crack-reverb.wav") +local GrassBackground = gfx.image.new("images/game/grass.png") --[[@as PlaydateGraphicsImage]] +local PlayerFrown = gfx.image.new("images/game/player-frown.png") --[[@as PlaydateGraphicsImage]] -local playerImageBlipper = blipper.new(100, "images/game/player.png", "images/game/player-lowhat.png") +local PlayerImageBlipper = blipper.new(100, "images/game/player.png", "images/game/player-lowhat.png") -local BALL_OFFSCREEN = 999 +local DanceBounceMs = 500 +local DanceBounceCount = 4 +local FielderDanceAnimator = gfx.animator.new(DanceBounceMs, 10, 0, easingHill) --, -1 * DanceBounceMs * DanceBounceCount) +FielderDanceAnimator.repeatCount = DanceBounceCount - 1 -local danceBounceMs = 500 -local danceBounceCount = 4 -local fielderDanceAnimator = gfx.animator.new(danceBounceMs, 10, 0, easingHill) --, -1 * danceBounceMs * danceBounceCount) -fielderDanceAnimator.repeatCount = danceBounceCount - 1 +local BallOffscreen = 999 -local pitchFlyTimeMs = 2500 -local PITCH_START_X = 200 -local PITCH_START_Y, PITCH_END_Y = 90, 250 -local pitchAnimator = gfx.animator.new(pitchFlyTimeMs, PITCH_START_Y, PITCH_END_Y, playdate.easingFunctions.outQuint) +local PitchFlyMs = 2500 +local PitchStartX = 200 +local PitchStartY , PitchEndY = 90, 250 -local CRANK_OFFSET_DEG = 90 -local BAT_OFFSET = xy(10, 25) -local batBase = xy(CENTER.x - 34, 215) -local batTip = xy(0, 0) +local PitchAnimator = gfx.animator.new(PitchFlyMs, PitchStartY, PitchEndY, playdate.easingFunctions.outQuint) -local TAG_DISTANCE = 20 +local CrankOffsetDeg = 90 +local BatOffset = xy(10, 25) -local ball = { - x = CENTER.x, - y = CENTER.y, +local batBase = xy(Center.x - 34, 215) +local batTip = xy(0, 0) + +local TagDistance = 20 + +local ball = { + x = Center.x, + y = Center.y, size = 6, } -local BAT_LENGTH = 45 +local BatLength = 45 -local MODES = { +local Modes = { batting = {}, running = {}, } -local currentMode = MODES.batting +local currentMode = Modes.batting + +local ballAnimatorY = gfx.animator.new(0, BallOffscreen, BallOffscreen, playdate.easingFunctions.linear) +local ballAnimatorX = gfx.animator.new(0, BallOffscreen, BallOffscreen, playdate.easingFunctions.linear) -local ballAnimatorY = gfx.animator.new(0, BALL_OFFSCREEN, BALL_OFFSCREEN, playdate.easingFunctions.linear) -local ballAnimatorX = gfx.animator.new(0, BALL_OFFSCREEN, BALL_OFFSCREEN, playdate.easingFunctions.linear) -- TODO? Replace this AND ballSizeAnimator with a ballHeightAnimator -- ...that might lose some of the magic of both. Compromise available? idk -local ballFloatAnimator = gfx.animator.new(2000, -60, 0, easingHill) -local ballSizeMs = 2000 -local ballSizeAnimator = gfx.animator.new(ballSizeMs, 9, 6, easingHill) +local ballFloatAnimator = gfx.animator.new(2000, -60, 0, easingHill) +local BallSizeMs = 2000 +local ballSizeAnimator = gfx.animator.new(BallSizeMs, 9, 6, easingHill) -local HIT_MULT = 10 +local HitMult = 10 local deltaSeconds = 0 -local FIRST, SECOND, THIRD, HOME = 1, 2, 3, 4 +local First , Second , Third , Home = 1, 2, 3, 4 ---@type Base[] -local bases = { - xy(SCREEN.W * 0.93, SCREEN.H * 0.52), - xy(SCREEN.W * 0.47, SCREEN.H * 0.19), - xy(SCREEN.W * 0.03, SCREEN.H * 0.52), - xy(SCREEN.W * 0.474, SCREEN.H * 0.79), +local Bases = { + xy(Screen.W * 0.93, Screen.H * 0.52), + xy(Screen.W * 0.47, Screen.H * 0.19), + xy(Screen.W * 0.03, Screen.H * 0.52), + xy(Screen.W * 0.474, Screen.H * 0.79), } -- Pseudo-base for batter to target -local rightHandedBattersBox = xy(bases[HOME].x - 35, bases[HOME].y) +local RightHandedBattersBox = xy(Bases[Home].x - 35, Bases[Home].y) ---@type table -local nextBaseMap = { - [bases[FIRST]] = bases[SECOND], - [bases[SECOND]] = bases[THIRD], - [bases[THIRD]] = bases[HOME], +local NextBaseMap = { + [Bases[First]] = Bases[Second], + [Bases[Second]] = Bases[Third], + [Bases[Third]] = Bases[Home], } function newFielder(speed) @@ -106,7 +109,7 @@ function newFielder(speed) end ---@type table -local fielders = { +local fielders = { first = newFielder(40), second = newFielder(40), shortstop = newFielder(40), @@ -118,9 +121,9 @@ local fielders = { right = newFielder(40), } -local PITCHER_POS = { - x = SCREEN.W * 0.48, - y = SCREEN.H * 0.40, +local PitcherStartPos = { + x = Screen.W * 0.48, + y = Screen.H * 0.40, } --- Resets the target positions of all fielders to their defaults (at their field positions). @@ -128,45 +131,40 @@ local PITCHER_POS = { function resetFielderPositions(fromOffTheField) if fromOffTheField then for _, fielder in pairs(fielders) do - fielder.x = CENTER.x - fielder.y = SCREEN.H + fielder.x = Center.x + fielder.y = Screen.H end end - fielders.first.target = xy(SCREEN.W - 65, SCREEN.H * 0.48) - fielders.second.target = xy(SCREEN.W * 0.70, SCREEN.H * 0.30) - fielders.shortstop.target = xy(SCREEN.W * 0.30, SCREEN.H * 0.30) - fielders.third.target = xy(SCREEN.W * 0.1, SCREEN.H * 0.48) - fielders.pitcher.target = xy(PITCHER_POS.x, PITCHER_POS.y) - fielders.catcher.target = xy(SCREEN.W * 0.475, SCREEN.H * 0.92) - fielders.left.target = xy(SCREEN.W * -1, SCREEN.H * -0.2) - fielders.center.target = xy(CENTER.x, SCREEN.H * -0.4) - fielders.right.target = xy(SCREEN.W * 2, SCREEN.H * fielders.left.target.y) + fielders.first.target = xy(Screen.W - 65, Screen.H * 0.48) + fielders.second.target = xy(Screen.W * 0.70, Screen.H * 0.30) + fielders.shortstop.target = xy(Screen.W * 0.30, Screen.H * 0.30) + fielders.third.target = xy(Screen.W * 0.1, Screen.H * 0.48) + fielders.pitcher.target = xy(PitcherStartPos.x, PitcherStartPos.y) + fielders.catcher.target = xy(Screen.W * 0.475, Screen.H * 0.92) + fielders.left.target = xy(Screen.W * -1, Screen.H * -0.2) + fielders.center.target = xy(Center.x, Screen.H * -0.4) + fielders.right.target = xy(Screen.W * 2, Screen.H * fielders.left.target.y) end -local PLAYER_STARTING_X = bases[HOME].x - 40 -local PLAYER_STARTING_Y = bases[HOME].y - 3 +local BatterStartingX = Bases[Home].x - 40 +local BatterStartingY = Bases[Home].y - 3 --- @type Runner[] -local runners = {} +local runners = {} --- @type Runner[] -local outRunners = {} - -local nameI = 1 -local runnerNames = { "Barbara", "Steve", "Jerry", "Beatrice" } +local outRunners = {} ---@return Runner function newRunner() local new = { - x = PLAYER_STARTING_X - 60, - y = PLAYER_STARTING_Y + 60, - nextBase = rightHandedBattersBox, + x = BatterStartingX - 60, + y = BatterStartingY + 60, + nextBase = RightHandedBattersBox, prevBase = nil, - name = runnerNames[nameI], - forcedTo = bases[FIRST] + forcedTo = Bases[First], } - nameI = nameI + 1 runners[#runners + 1] = new return new end @@ -188,10 +186,10 @@ function throwBall(destX, destY, easingFunc, flyTimeMs, floaty) end function pitch() - currentMode = MODES.batting - ballAnimatorX = gfx.animator.new(0, PITCH_START_X, PITCH_START_X, playdate.easingFunctions.linear) - ballAnimatorY = pitchAnimator - pitchAnimator:reset() + currentMode = Modes.batting + ballAnimatorX = gfx.animator.new(0, PitchStartX, PitchStartX, playdate.easingFunctions.linear) + ballAnimatorY = PitchAnimator + PitchAnimator:reset() end function playdate.AButtonDown() @@ -201,31 +199,23 @@ function playdate.AButtonDown() pitch() end -function playdate.upButtonDown() - batBase.y = batBase.y - 1 -end +function playdate.upButtonDown() end -function playdate.downButtonDown() - batBase.y = batBase.y + 1 -end +function playdate.downButtonDown() end -function playdate.rightButtonDown() - batBase.x = batBase.x + 1 -end +function playdate.rightButtonDown() end -function playdate.leftButtonDown() - batBase.x = batBase.x - 1 -end +function playdate.leftButtonDown() end local elapsedSec = 0 local crankChange = 0 local acceleratedChange -local BASE_HITBOX = 13 +local BaseHitbox = 13 --- Returns the base being touched by the runner at (x,y), or nil, if no base is being touched function isTouchingBase(x, y) - for _, base in ipairs(bases) do - if distanceBetween(x, y, base.x, base.y) < BASE_HITBOX then + for _, base in ipairs(Bases) do + if distanceBetween(x, y, base.x, base.y) < BaseHitbox then return base end end @@ -233,14 +223,12 @@ function isTouchingBase(x, y) return nil end -local BALL_CATCH_HITBOX = 3 +local BallCatchHitbox = 3 function isTouchingBall(x, y) local ballDistance = distanceBetween(x, y, ball.x, ball.y) - return ballDistance < BALL_CATCH_HITBOX + return ballDistance < BallCatchHitbox end -function updateForcedTos() end - local outs = 0 local homeScore = 0 local awayScore = 0 @@ -249,15 +237,13 @@ function outRunner(runnerIndex) outs = math.min(3, outs + 1) outRunners[#outRunners + 1] = runners[runnerIndex] table.remove(runners, runnerIndex) - updateForcedTos() - fielderDanceAnimator:reset() + FielderDanceAnimator:reset() end -- TODO: Away score function score(runnerIndex) outRunners[#outRunners + 1] = runners[runnerIndex] table.remove(runners, runnerIndex) - updateForcedTos() homeScore = homeScore + 1 end @@ -283,14 +269,19 @@ function updateFielders() end end - if currentMode == MODES.running and isTouchingBall(fielder.x, fielder.y) then + if currentMode == Modes.running and isTouchingBall(fielder.x, fielder.y) then local touchedBase = isTouchingBase(fielder.x, fielder.y) for i, runner in pairs(runners) do local runnerOnBase = touchingBaseCache.get(runner) - if touchedBase and runner.prevBase and runner.forcedTo == touchedBase and touchedBase ~= runnerOnBase then + if + touchedBase + and runner.prevBase + and runner.forcedTo == touchedBase + and touchedBase ~= runnerOnBase + then outRunner(i) elseif not runnerOnBase then - if distanceBetween(runner.x, runner.y, fielder.x, fielder.y) < TAG_DISTANCE then + if distanceBetween(runner.x, runner.y, fielder.x, fielder.y) < TagDistance then outRunner(i) end end @@ -304,14 +295,20 @@ end function updateRunners() local autoRunSpeed = 20 --autoRunSpeed = 140 - local currentRunners = currentMode == MODES.batting and {batter} or filter(runners, function(runner) - return runner ~= batter - end) + local currentRunners = currentMode == Modes.batting and { batter } + or filter(runners, function(runner) + return runner ~= batter + end) local runnerMoved = false for runnerIndex, runner in ipairs(currentRunners) do - local nearestBase, nearestBaseDistance = getNearestOf(bases, runner.x, runner.y) - if nearestBaseDistance < 5 and runner.prevBase and runner.nextBase == bases[HOME] and nearestBase == bases[HOME] then + local nearestBase, nearestBaseDistance = getNearestOf(Bases, runner.x, runner.y) + if + nearestBaseDistance < 5 + and runner.prevBase + and runner.nextBase == Bases[Home] + and nearestBase == Bases[Home] + then score(runnerIndex) end if runner.nextBase then @@ -331,7 +328,7 @@ function updateRunners() runner.y = runner.y - (y * mult) runnerMoved = runnerMoved or prevX ~= runner.x or prevY ~= runner.y else - runner.nextBase = nextBaseMap[runner.nextBase] + runner.nextBase = NextBaseMap[runner.nextBase] runner.forcedTo = nil end end @@ -352,7 +349,7 @@ end ---@return Base[] function getForcedOutTargets() local targets = {} - for _, base in ipairs(bases) do + for _, base in ipairs(Bases) do local runnerTargetingBase = getRunnerTargeting(base) if runnerTargetingBase then targets[#targets + 1] = base @@ -361,7 +358,6 @@ function getForcedOutTargets() end end return targets - -- return { bases[FIRST] } end --- Returns the position,distance of the basest closest to the runner furthest from a base @@ -369,7 +365,7 @@ end function getBaseOfStrandedRunner() local farRunnersBase, farDistance for _, runner in pairs(runners) do - local nearestBase, distance = getNearestOf(bases, runner.x, runner.y, function(base) + local nearestBase, distance = getNearestOf(Bases, runner.x, runner.y, function(base) return runner.nextBase == base end) if farRunnersBase == nil or farDistance < distance then @@ -396,10 +392,10 @@ function getNextThrowTarget() end end -local resetFieldersAfterSeconds = 4 +local ResetFieldersAfterSeconds = 4 local secondsSinceLastRunnerMove = 0 -local pitchAfterSeconds = 5 +local PitchAfterSeconds = 5 local secondsSincePitchAllowed = -5 function init() @@ -407,32 +403,33 @@ function init() gfx.setBackgroundColor(gfx.kColorWhite) playdate.setMenuImage(gfx.image.new("images/game/menu-image.png")) resetFielderPositions(true) + playdate.getSystemMenu():addMenuItem("Restart game", function() end) end function updateBatting() secondsSincePitchAllowed = secondsSincePitchAllowed + deltaSeconds - if secondsSincePitchAllowed > pitchAfterSeconds then + if secondsSincePitchAllowed > PitchAfterSeconds then pitch() secondsSincePitchAllowed = 0 end - if ball.y < BALL_OFFSCREEN then + if ball.y < BallOffscreen then ball.y = ballAnimatorY:currentValue() + ballFloatAnimator:currentValue() ball.size = 6 end - local batAngle = math.rad(playdate.getCrankPosition() + CRANK_OFFSET_DEG) + local batAngle = math.rad(playdate.getCrankPosition() + CrankOffsetDeg) -- TODO: animate bat-flip or something - batBase.x = batter and (batter.x + BAT_OFFSET.x) or 0 - batBase.y = batter and (batter.y + BAT_OFFSET.y) or 0 - batTip.x = batBase.x + (BAT_LENGTH * math.sin(batAngle)) - batTip.y = batBase.y + (BAT_LENGTH * math.cos(batAngle)) + batBase.x = batter and (batter.x + BatOffset.x) or 0 + batBase.y = batter and (batter.y + BatOffset.y) or 0 + batTip.x = batBase.x + (BatLength * math.sin(batAngle)) + batTip.y = batBase.y + (BatLength * math.cos(batAngle)) if acceleratedChange >= 0 - and pointDirectlyUnderLine(ball.x, ball.y, batBase.x, batBase.y, batTip.x, batTip.y, SCREEN.H) + and pointDirectlyUnderLine(ball.x, ball.y, batBase.x, batBase.y, batTip.x, batTip.y, Screen.H) then - batCrackSound:play() - currentMode = MODES.running + BatCrackSound:play() + currentMode = Modes.running local ballAngle = batAngle + math.rad(90) local mult = math.abs(crankChange / 15) @@ -442,15 +439,15 @@ function updateBatting() ballVelX = ballVelX * -1 ballVelY = ballVelY * -1 end - local ballDestX = ball.x + (ballVelX * HIT_MULT) - local ballDestY = ball.y + (ballVelY * HIT_MULT) + local ballDestX = ball.x + (ballVelX * HitMult) + local ballDestY = ball.y + (ballVelY * HitMult) -- Hit! throwBall(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000) - fielders.first.target = bases[FIRST] - batter.nextBase = bases[FIRST] - batter.prevBase = bases[HOME] - batter.forcedTo = bases[FIRST] + fielders.first.target = Bases[First] + batter.nextBase = Bases[First] + batter.prevBase = Bases[Home] + batter.forcedTo = Bases[First] batter = nil -- Demote batter to a mere runner local chasingFielder = getNearestOf(fielders, ballDestX, ballDestY) @@ -472,10 +469,10 @@ function updateRunning() secondsSinceLastRunnerMove = 0 else secondsSinceLastRunnerMove = secondsSinceLastRunnerMove + deltaSeconds - if secondsSinceLastRunnerMove > resetFieldersAfterSeconds then - throwBall(PITCH_START_X, PITCH_START_Y, playdate.easingFunctions.linear, nil, true) + if secondsSinceLastRunnerMove > ResetFieldersAfterSeconds then + throwBall(PitchStartX, PitchStartY, playdate.easingFunctions.linear, nil, true) resetFielderPositions(false) - currentMode = MODES.batting + currentMode = Modes.batting batter = newRunner() end end @@ -483,7 +480,7 @@ end function updateOutRunners() for i, runner in ipairs(outRunners) do - if runner.x < SCREEN.W + 50 and runner.y < SCREEN.H + 50 then + if runner.x < Screen.W + 50 and runner.y < Screen.H + 50 then runner.x = runner.x + (deltaSeconds * 25) runner.y = runner.y + (deltaSeconds * 25) else @@ -501,10 +498,10 @@ function updateGameState() ball.x = ballAnimatorX:currentValue() ball.y = ballAnimatorY:currentValue() + ballFloatAnimator:currentValue() - if currentMode == MODES.batting then + if currentMode == Modes.batting then updateBatting() updateRunners() - elseif currentMode == MODES.running then + elseif currentMode == Modes.running then updateRunning() end @@ -516,8 +513,8 @@ local OUT_BUBBLE_SIZE = 6 function drawScoreboard() gfx.setDrawOffset(0, 0) - local y = SCREEN.H * 0.95 - local x = SCREEN.W * 0.05 + local y = Screen.H * 0.95 + local x = Screen.W * 0.05 gfx.setLineWidth(1) gfx.setColor(gfx.kColorBlack) @@ -544,40 +541,40 @@ function playdate.update() gfx.clear() - if ball.x < BALL_OFFSCREEN then + if ball.x < BallOffscreen then -- TODO: Show baserunning minimap when panning? - local offsetX, offsetY = getDrawOffset(SCREEN.W, SCREEN.H, ball.x, ball.y) + local offsetX, offsetY = getDrawOffset(Screen.W, Screen.H, ball.x, ball.y) gfx.setDrawOffset(offsetX, offsetY) end - grassBackground:draw(-400, -240) + GrassBackground:draw(-400, -240) gfx.setColor(gfx.kColorBlack) gfx.setLineWidth(2) gfx.drawCircleAtPoint(ball.x, ball.y, ball.size) - local fielderDanceHeight = fielderDanceAnimator:currentValue() + local fielderDanceHeight = FielderDanceAnimator:currentValue() for _, fielder in pairs(fielders) do gfx.fillRect(fielder.x, fielder.y - fielderDanceHeight, 14, 25) end - if currentMode == MODES.batting then + if currentMode == Modes.batting then gfx.setLineWidth(5) gfx.drawLine(batBase.x, batBase.y, batTip.x, batTip.y) end - if playdate.isCrankDocked() then -- or (crankChange < 2 and currentMode == MODES.running) then + if playdate.isCrankDocked() then -- or (crankChange < 2 and currentMode == Modes.running) then playdate.ui.crankIndicator:draw() end -- TODO? Change blip speed depending on runner speed? for _, runner in pairs(runners) do -- TODO? Scale sprites down as y increases - playerImageBlipper:draw(false, runner.x, runner.y) + PlayerImageBlipper:draw(false, runner.x, runner.y) end for _, runner in pairs(outRunners) do - playerFrown:draw(runner.x, runner.y) + PlayerFrown:draw(runner.x, runner.y) end drawScoreboard()