diff --git a/Makefile b/Makefile
index d05e21e..bdd74f7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,6 @@
+lint:
+ stylua src/
+
all:
pdc src BatterUp.pdx
diff --git a/src/ecs.lua b/src/ecs.lua
index 10f6fbf..fa585fa 100644
--- a/src/ecs.lua
+++ b/src/ecs.lua
@@ -1,4 +1,4 @@
-ecs = { }
+ecs = {}
local ALL_ENTITIES = {}
@@ -12,9 +12,9 @@ function ecs.addEntity(entity)
ALL_ENTITIES[entity] = true
for _, system in pairs(SYSTEMS) do
if entityMatchesShapes(entity, system.shapes) then
- system.entityCache[entity] = true
+ system.entityCache[entity] = true
else
- system.entityCache[entity] = nil
+ system.entityCache[entity] = nil
end
end
end
@@ -22,7 +22,7 @@ end
function ecs.removeEntity(entity)
ALL_ENTITIES[entity] = nil
for _, system in pairs(SYSTEMS) do
- system.entityCache[entity] = nil
+ system.entityCache[entity] = nil
end
end
@@ -36,7 +36,7 @@ end
function allKeysIncluded(entity, filter)
for k, _ in pairs(filter) do
if not entity[k] then
- return false
+ return false
end
end
return true
@@ -45,7 +45,7 @@ end
function entityMatchesShapes(entity, shapes)
for _, shape in pairs(shapes) do
if not allKeysIncluded(entity, shape) then
- return false
+ return false
end
end
return true
@@ -61,25 +61,26 @@ end
---@param wShape W?
---@return fun(callback: fun(componentT: T, componentU: U, componentV: V, componentW: W))
function ecs.entitiesHavingShapes(tShape, uShape, vShape, wShape)
- return function()
- end
+ return function() end
end
-- Print contents of `tbl`, with indentation.
-- `indent` sets the initial level of indentation.
-function tprint (tbl, indent)
- if not indent then indent = 0 end
- for k, v in pairs(tbl) do
- formatting = string.rep(" ", indent) .. k .. ": "
- if type(v) == "table" then
- print(formatting)
- tprint(v, indent+1)
- elseif type(v) == 'boolean' then
- print(formatting .. tostring(v))
- else
- print(formatting .. v)
- end
- end
+function tprint(tbl, indent)
+ if not indent then
+ indent = 0
+ end
+ for k, v in pairs(tbl) do
+ formatting = string.rep(" ", indent) .. k .. ": "
+ if type(v) == "table" then
+ print(formatting)
+ tprint(v, indent + 1)
+ elseif type(v) == "boolean" then
+ print(formatting .. tostring(v))
+ else
+ print(formatting .. v)
+ end
+ end
end
function addSystem(callback, keys, shapes)
@@ -87,7 +88,7 @@ function addSystem(callback, keys, shapes)
callback = callback,
keys = keys,
shapes = shapes,
- entityCache = nil
+ entityCache = nil,
}
end
@@ -98,18 +99,26 @@ end
---@param deltaSeconds number
function ecs.update(deltaSeconds)
- for _,system in pairs(SYSTEMS) do
+ for _, system in pairs(SYSTEMS) do
if not system.entityCache then
- system.entityCache = {}
- for entity,_ in pairs(ALL_ENTITIES) do
+ system.entityCache = {}
+ for entity, _ in pairs(ALL_ENTITIES) do
if entityMatchesShapes(entity, system.shapes) then
system.entityCache[entity] = true
end
end
end
local keys = system.keys
- for entity,_ in pairs(system.entityCache) do
- system.callback(deltaSeconds, entity, entity[keys[1]], entity[keys[2]], entity[keys[3]], entity[keys[4]], entity)
+ for entity, _ in pairs(system.entityCache) do
+ system.callback(
+ deltaSeconds,
+ entity,
+ entity[keys[1]],
+ entity[keys[2]],
+ entity[keys[3]],
+ entity[keys[4]],
+ entity
+ )
end
end
end
@@ -130,21 +139,21 @@ end
---@param wShape { [WKey]: W } | fun(entity: any, componentT: T, componentU: U, componentV: V, any) | nil
---@param finalFunc fun(entity: any, componentT: T, componentU: U, componentV: V, componentW: W, any) | nil
function ecs.forEntitiesWith(tShape, uShape, vShape, wShape, finalFunc)
- local maybeShapes = {tShape, uShape, vShape, wShape, finalFunc}
+ local maybeShapes = { tShape, uShape, vShape, wShape, finalFunc }
local shapes = {}
local callback
- for _,maybeShape in pairs(maybeShapes) do
+ for _, maybeShape in pairs(maybeShapes) do
if type(maybeShape) == "table" then
- shapes[#shapes + 1] = maybeShape
+ shapes[#shapes + 1] = maybeShape
elseif type(maybeShape) == "function" then
callback = maybeShape
end
end
local keys = {}
- for _,shape in pairs(shapes) do
- for key,_ in pairs(shape) do
+ for _, shape in pairs(shapes) do
+ for key, _ in pairs(shape) do
keys[#keys + 1] = key
end
end
@@ -159,15 +168,15 @@ local Target = { target = XYPair }
local Velocity = { velocity = XYPair }
function ecs.overlayOnto(entity, value)
- for key,v in pairs(value) do
+ for key, v in pairs(value) do
entity[key] = v
end
ecs.addEntity(entity)
end
local data = {
- position = {x = 0, y = 0},
- velocity = {x = 1, y = 2},
+ position = { x = 0, y = 0 },
+ velocity = { x = 1, y = 2 },
}
---@generic T
@@ -183,14 +192,14 @@ end
---@param shape `T`
---@param value `T`
function ecs.set(entity, shape, value)
- for key,v in pairs(shape) do
+ for key, v in pairs(shape) do
entity[key] = value[v]
end
end
ecs.addEntity(data)
-ecs.forEntitiesWith(Position, Velocity, function (delta, e, pos, vel)
+ecs.forEntitiesWith(Position, Velocity, function(delta, e, pos, vel)
pos.x = pos.x + (delta * vel.x)
pos.y = pos.y + (delta * vel.y)
print("position")
@@ -200,12 +209,12 @@ ecs.forEntitiesWith(Position, Velocity, function (delta, e, pos, vel)
})
end)
-ecs.forEntitiesWith(Target, function (delta, e, pos, vel)
+ecs.forEntitiesWith(Target, function(delta, e, pos, vel)
pos.x = pos.x + (delta * vel.x)
pos.y = pos.y + (delta * vel.y)
print("position")
tprint(pos, 1)
- ecs.set(e, Target, 'hallo')
+ ecs.set(e, Target, "hallo")
end)
-ecs.update(1)
\ No newline at end of file
+ecs.update(1)
diff --git a/src/graphics.lua b/src/graphics.lua
index 0970d64..8421c22 100644
--- a/src/graphics.lua
+++ b/src/graphics.lua
@@ -5,18 +5,18 @@ local ballBuffer = 5
--- XOX
--- Where each character is the size of the screen, and 'O' is the default view.
function getDrawOffset(screenW, screenH, ballX, ballY)
- local offsetX, offsetY
- if ballY < ballBuffer then
- offsetY = math.max(ballBuffer, -1 * (ballY - ballBuffer))
- else
- offsetY = 0
- end
- if ballX > 0 and ballX < (screenW - ballBuffer) then
- offsetX = 0
- elseif ballX < ballBuffer then
- offsetX = math.max(-1 * screenW, -1 * (ballX - ballBuffer))
- elseif ballX > (screenW - ballBuffer) then
- offsetX = math.min(screenW * 2, -1 * (ballX - ballBuffer))
- end
- return offsetX, offsetY
-end
\ No newline at end of file
+ local offsetX, offsetY
+ if ballY < ballBuffer then
+ offsetY = math.max(ballBuffer, -1 * (ballY - ballBuffer))
+ else
+ offsetY = 0
+ end
+ if ballX > 0 and ballX < (screenW - ballBuffer) then
+ offsetX = 0
+ elseif ballX < ballBuffer then
+ offsetX = math.max(-1 * screenW, -1 * (ballX - ballBuffer))
+ elseif ballX > (screenW - ballBuffer) then
+ offsetX = math.min(screenW * 2, -1 * (ballX - ballBuffer))
+ end
+ return offsetX, offsetY
+end
diff --git a/src/main.lua b/src/main.lua
index 0b8da10..a148ce6 100644
--- a/src/main.lua
+++ b/src/main.lua
@@ -1,3 +1,4 @@
+-- stylua: ignore start
import 'CoreLibs/animation.lua'
import 'CoreLibs/animator.lua'
import 'CoreLibs/easing.lua'
@@ -7,6 +8,7 @@ import 'CoreLibs/ui.lua'
import 'graphics.lua'
import 'utils.lua'
+-- stylua: ignore end
--- @alias XYPair { x: number, y: number }
@@ -20,7 +22,7 @@ local gfx = playdate.graphics
local SCREEN = {
W = playdate.display.getWidth(),
- H = playdate.display.getHeight()
+ H = playdate.display.getHeight(),
}
local CENTER = xy(SCREEN.W / 2, SCREEN.H / 2)
@@ -29,17 +31,13 @@ local batCrackSound = playdate.sound.sampleplayer.new("sounds/bat-crack-reverb.w
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)
+local fielderDanceAnimator = gfx.animator.new(danceBounceMs, 10, 0, easingHill) --, -1 * danceBounceMs * danceBounceCount)
fielderDanceAnimator.repeatCount = danceBounceCount - 1
local pitchFlyTimeMs = 2500
@@ -56,14 +54,14 @@ local TAG_DISTANCE = 20
local ball = {
x = CENTER.x,
y = BALL_OFFSCREEN,
- size = 6
+ size = 6,
}
local BAT_LENGTH = 45
local MODES = {
- batting = {},
- running = {}
+ batting = {},
+ running = {},
}
local currentMode = MODES.batting
@@ -84,88 +82,88 @@ 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)
+ 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),
}
---@type table
local nextBaseMap = {
- [bases[FIRST]] = bases[SECOND],
- [bases[SECOND]] = bases[THIRD],
- [bases[THIRD]] = bases[HOME]
+ [bases[FIRST]] = bases[SECOND],
+ [bases[SECOND]] = bases[THIRD],
+ [bases[THIRD]] = bases[HOME],
}
function newFielder(speed)
return {
- speed = speed
+ speed = speed,
}
end
---@type table
local fielders = {
- first = newFielder(40),
- second = newFielder(40),
- shortstop = newFielder(40),
- third = newFielder(40),
- pitcher = newFielder(30),
- catcher = newFielder(20),
- left = newFielder(40),
- center = newFielder(40),
- right = newFielder(40)
+ first = newFielder(40),
+ second = newFielder(40),
+ shortstop = newFielder(40),
+ third = newFielder(40),
+ pitcher = newFielder(30),
+ catcher = newFielder(20),
+ left = newFielder(40),
+ center = newFielder(40),
+ right = newFielder(40),
}
local PITCHER_POS = {
x = SCREEN.W * 0.48,
- y = SCREEN.H * 0.40
+ y = SCREEN.H * 0.40,
}
--- Resets the target positions of all fielders to their defaults (at their field positions).
---@param fromOffTheField boolean If provided, also sets all runners' current position to one centralized location.
function resetFielderPositions(fromOffTheField)
if fromOffTheField then
- for _,fielder in pairs(fielders) do
+ for _, fielder in pairs(fielders) do
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(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)
end
local PLAYER_STARTING_X = bases[HOME].x - 40
local PLAYER_STARTING_Y = bases[HOME].y - 3
--- @type Runner[]
-local runners = { }
+local runners = {}
--- @type Runner[]
-local outRunners = { }
+local outRunners = {}
local nameI = 1
-local runnerNames = {"Barbara", "Steve", "Jerry", "Beatrice"}
+local runnerNames = { "Barbara", "Steve", "Jerry", "Beatrice" }
---@return Runner
function newRunner()
- local new = {
- x = PLAYER_STARTING_X,
- y = PLAYER_STARTING_Y,
- nextBase = nil,
- prevBase = nil,
- name = runnerNames[nameI]
- }
- nameI = nameI + 1
- runners[#runners + 1] = new
- return new
+ local new = {
+ x = PLAYER_STARTING_X,
+ y = PLAYER_STARTING_Y,
+ nextBase = nil,
+ prevBase = nil,
+ name = runnerNames[nameI],
+ }
+ nameI = nameI + 1
+ runners[#runners + 1] = new
+ return new
end
---@type Runner | nil
@@ -174,45 +172,45 @@ batter.nextBase = bases[FIRST]
batter.forcedTo = bases[FIRST]
function throwBall(destX, destY, easingFunc, flyTimeMs, floaty)
- if not flyTimeMs then
- flyTimeMs = distanceBetween(ball.x, ball.y, destX, destY) * 5
- end
- ballSizeAnimator:reset(flyTimeMs)
- ballAnimatorY = gfx.animator.new(flyTimeMs, ball.y, destY, easingFunc)
- ballAnimatorX = gfx.animator.new(flyTimeMs, ball.x, destX, easingFunc)
- if floaty then
- ballFloatAnimator:reset(flyTimeMs)
- end
+ if not flyTimeMs then
+ flyTimeMs = distanceBetween(ball.x, ball.y, destX, destY) * 5
+ end
+ ballSizeAnimator:reset(flyTimeMs)
+ ballAnimatorY = gfx.animator.new(flyTimeMs, ball.y, destY, easingFunc)
+ ballAnimatorX = gfx.animator.new(flyTimeMs, ball.x, destX, easingFunc)
+ if floaty then
+ ballFloatAnimator:reset(flyTimeMs)
+ end
end
function pitch()
- currentMode = MODES.batting
+ currentMode = MODES.batting
ballAnimatorX = gfx.animator.new(0, PITCH_START_X, PITCH_START_X, playdate.easingFunctions.linear)
- ballAnimatorY = pitchAnimator
- pitchAnimator:reset()
+ ballAnimatorY = pitchAnimator
+ pitchAnimator:reset()
end
function playdate.AButtonDown()
if not batter then
return
end
- pitch()
+ pitch()
end
function playdate.upButtonDown()
- batBase.y = batBase.y - 1
+ batBase.y = batBase.y - 1
end
function playdate.downButtonDown()
- batBase.y = batBase.y + 1
+ batBase.y = batBase.y + 1
end
function playdate.rightButtonDown()
- batBase.x = batBase.x + 1
+ batBase.x = batBase.x + 1
end
function playdate.leftButtonDown()
- batBase.x = batBase.x - 1
+ batBase.x = batBase.x - 1
end
local elapsedSec = 0
@@ -221,23 +219,22 @@ local crankChange = 0
local BASE_HITBOX = 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
- return base
- end
- end
+ for _, base in ipairs(bases) do
+ if distanceBetween(x, y, base.x, base.y) < BASE_HITBOX then
+ return base
+ end
+ end
- return nil
+ return nil
end
local BALL_CATCH_HITBOX = 3
function isTouchingBall(x, y)
- local ballDistance = distanceBetween(x, y, ball.x, ball.y)
- return ballDistance < BALL_CATCH_HITBOX
+ local ballDistance = distanceBetween(x, y, ball.x, ball.y)
+ return ballDistance < BALL_CATCH_HITBOX
end
-function updateForcedTos()
-end
+function updateForcedTos() end
local outs = 0
local homeScore = 0
@@ -246,106 +243,104 @@ local awayScore = 0
function outRunner(runnerIndex)
print("You're out, runner" .. runnerIndex .. "!")
outs = math.min(3, outs + 1)
- outRunners[#outRunners+1] = runners[runnerIndex]
- table.remove(runners, runnerIndex)
- updateForcedTos()
- fielderDanceAnimator:reset()
+ outRunners[#outRunners + 1] = runners[runnerIndex]
+ table.remove(runners, runnerIndex)
+ updateForcedTos()
+ fielderDanceAnimator:reset()
end
function updateFielders()
- local touchingBaseCache = buildCache(
- function(runner)
- return isTouchingBase(runner.x, runner.y)
- end
- )
+ local touchingBaseCache = buildCache(function(runner)
+ return isTouchingBase(runner.x, runner.y)
+ end)
- for _,fielder in pairs(fielders) do
- -- TODO: Target unforced runners (or their target bases) for tagging
- -- With new Position-based scheme, fielders are now able to set `fielder.target = runner` to track directly
- if fielder.target ~= nil then
- local x, y, distance = normalizeVector(fielder.x, fielder.y, fielder.target.x, fielder.target.y)
+ for _, fielder in pairs(fielders) do
+ -- TODO: Target unforced runners (or their target bases) for tagging
+ -- With new Position-based scheme, fielders are now able to set `fielder.target = runner` to track directly
+ if fielder.target ~= nil then
+ local x, y, distance = normalizeVector(fielder.x, fielder.y, fielder.target.x, fielder.target.y)
- if distance > 1 then
- fielder.x = fielder.x - (x * fielder.speed * deltaSeconds)
- fielder.y = fielder.y - (y * fielder.speed * deltaSeconds)
- else
- if fielder.onArrive then
- fielder.onArrive()
- end
- fielder.target = nil
- end
- end
+ if distance > 1 then
+ fielder.x = fielder.x - (x * fielder.speed * deltaSeconds)
+ fielder.y = fielder.y - (y * fielder.speed * deltaSeconds)
+ else
+ if fielder.onArrive then
+ fielder.onArrive()
+ end
+ fielder.target = nil
+ end
+ end
- 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.forcedTo == touchedBase and touchedBase ~= runnerOnBase 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.forcedTo == touchedBase and touchedBase ~= runnerOnBase then
print("Force out of runner:")
printTable(runner)
- outRunner(i)
- elseif not runnerOnBase then
- local fielderDistance = distanceBetween(runner.x, runner.y, fielder.x, fielder.y)
- if fielderDistance < TAG_DISTANCE then
+ outRunner(i)
+ elseif not runnerOnBase then
+ local fielderDistance = distanceBetween(runner.x, runner.y, fielder.x, fielder.y)
+ if fielderDistance < TAG_DISTANCE then
print("Tagged out!")
- outRunner(i)
- end
- end
- end
- end
- end
+ outRunner(i)
+ end
+ end
+ end
+ end
+ end
end
--- Returns true if at least one runner is still moving
---@return boolean
function updateRunners()
- local autoRunSpeed = 20
- --autoRunSpeed = 140
- local nonPlayerRunners = filter(runners, function (runner)
- return runner ~= batter
- end)
+ local autoRunSpeed = 20
+ --autoRunSpeed = 140
+ local nonPlayerRunners = filter(runners, function(runner)
+ return runner ~= batter
+ end)
- local runnerMoved = false
- for _,runner in pairs(nonPlayerRunners) do
- local _, nearestBaseDistance = getNearestOf(bases, runner.x, runner.y)
- if runner.nextBase then
- local nb = runner.nextBase
- local x, y, distance = normalizeVector(runner.x, runner.y, nb.x, nb.y)
+ local runnerMoved = false
+ for _, runner in pairs(nonPlayerRunners) do
+ local _, nearestBaseDistance = getNearestOf(bases, runner.x, runner.y)
+ if runner.nextBase then
+ local nb = runner.nextBase
+ local x, y, distance = normalizeVector(runner.x, runner.y, nb.x, nb.y)
- if distance > 1 then
- local mult = 1
- if crankChange < 0 then
- mult = -1
- end
- local prevX, prevY = runner.x, runner.y
- -- TODO: Drift toward nearest base?
- local autoRun = nearestBaseDistance > 5 and mult * autoRunSpeed * deltaSeconds or 0
- mult = autoRun + (crankChange / 20)
- runner.x = runner.x - (x * mult)
+ if distance > 1 then
+ local mult = 1
+ if crankChange < 0 then
+ mult = -1
+ end
+ local prevX, prevY = runner.x, runner.y
+ -- TODO: Drift toward nearest base?
+ local autoRun = nearestBaseDistance > 5 and mult * autoRunSpeed * deltaSeconds or 0
+ mult = autoRun + (crankChange / 20)
+ runner.x = runner.x - (x * mult)
runner.y = runner.y - (y * mult)
runnerMoved = runnerMoved or prevX ~= runner.x or prevY ~= runner.y
- else
- runner.nextBase = nextBaseMap[runner.nextBase]
- runner.forcedTo = nil
- end
- end
- end
+ else
+ runner.nextBase = nextBaseMap[runner.nextBase]
+ runner.forcedTo = nil
+ end
+ end
+ end
- return runnerMoved
+ return runnerMoved
end
---@return boolean
function ballIsBeingThrown()
- return false
+ return false
end
---@return boolean
function throwArrivedBeforeRunner()
- return false
+ return false
end
function getRunnerTargeting(base)
- for _,runner in pairs(runners) do
+ for _, runner in pairs(runners) do
if runner.nextBase == base then
return runner
end
@@ -356,44 +351,44 @@ 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
+ targets[#targets + 1] = base
else
return targets
end
end
return targets
- -- return { bases[FIRST] }
+ -- return { bases[FIRST] }
end
--- Returns the position,distance of the basest closest to the runner furthest from a base
---@return { x: number, y: number } | nil, number | nil
function getBaseOfStrandedRunner()
local farRunnersBase, farDistance
- for _,runner in pairs(runners) do
- local base, distance = getNearestOf(bases, runner.x, runner.y, function(base)
+ for _, runner in pairs(runners) do
+ local base, distance = getNearestOf(bases, runner.x, runner.y, function(base)
return runner.nextBase == base
- end)
+ end)
if farRunnersBase == nil then
- farRunnersBase = base
- farDistance = distance
+ farRunnersBase = base
+ farDistance = distance
elseif farDistance < distance then
farRunnersBase = base
farDistance = distance
end
- end
+ end
- return farRunnersBase, farDistance
+ return farRunnersBase, farDistance
end
--- Returns x,y of the throw target
---@return number|nil, number|nil
function getNextThrowTarget()
-- TODO: Handle missed throws, check for fielders at target, etc.
- local targets = getForcedOutTargets()
- if #targets ~= 0 then
+ local targets = getForcedOutTargets()
+ if #targets ~= 0 then
return targets[1].x, targets[1].y
end
@@ -427,42 +422,44 @@ function updateBatting()
ball.size = 6
end
- local batAngle = math.rad(playdate.getCrankPosition() + CRANK_OFFSET_DEG)
- batTip.x = batBase.x + (BAT_LENGTH * math.sin(batAngle))
- batTip.y = batBase.y + (BAT_LENGTH * math.cos(batAngle))
+ local batAngle = math.rad(playdate.getCrankPosition() + CRANK_OFFSET_DEG)
+ batTip.x = batBase.x + (BAT_LENGTH * math.sin(batAngle))
+ batTip.y = batBase.y + (BAT_LENGTH * math.cos(batAngle))
- if acceleratedChange >= 0 and
- pointDirectlyUnderLine(ball.x, ball.y, batBase.x, batBase.y, batTip.x, batTip.y, SCREEN.H) then
- batCrackSound:play()
- currentMode = MODES.running
- ballAngle = batAngle + math.rad(90)
+ if
+ acceleratedChange >= 0
+ and pointDirectlyUnderLine(ball.x, ball.y, batBase.x, batBase.y, batTip.x, batTip.y, SCREEN.H)
+ then
+ batCrackSound:play()
+ currentMode = MODES.running
+ ballAngle = batAngle + math.rad(90)
- local mult = math.abs(acceleratedChange / 15)
- local ballVelX = mult * 10 * math.sin(ballAngle)
- local ballVelY = mult * 5 * math.cos(ballAngle)
- if ballVelY > 0 then
- ballVelX = ballVelX * -1
- ballVelY = ballVelY * -1
- end
- ballDestX = ball.x + (ballVelX * HIT_MULT)
- ballDestY = ball.y + (ballVelY * HIT_MULT)
- throwBall(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000)
+ local mult = math.abs(acceleratedChange / 15)
+ local ballVelX = mult * 10 * math.sin(ballAngle)
+ local ballVelY = mult * 5 * math.cos(ballAngle)
+ if ballVelY > 0 then
+ ballVelX = ballVelX * -1
+ ballVelY = ballVelY * -1
+ end
+ ballDestX = ball.x + (ballVelX * HIT_MULT)
+ ballDestY = ball.y + (ballVelY * HIT_MULT)
+ throwBall(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000)
- fielders.first.target = bases[FIRST]
- batter.nextBase = bases[FIRST]
- batter.forcedTo = bases[FIRST]
- batter = nil -- Demote batter to a mere runner
+ fielders.first.target = bases[FIRST]
+ batter.nextBase = bases[FIRST]
+ batter.forcedTo = bases[FIRST]
+ batter = nil -- Demote batter to a mere runner
- local chasingFielder = getNearestOf(fielders, ballDestX, ballDestY)
- chasingFielder.target = { x = ballDestX, y = ballDestY }
- chasingFielder.onArrive = function()
- local targetX, targetY = getNextThrowTarget()
- if targetX ~= nil then
+ local chasingFielder = getNearestOf(fielders, ballDestX, ballDestY)
+ chasingFielder.target = { x = ballDestX, y = ballDestY }
+ chasingFielder.onArrive = function()
+ local targetX, targetY = getNextThrowTarget()
+ if targetX ~= nil then
throwBall(targetX, targetY, playdate.easingFunctions.linear, nil, true)
chasingFielder.onArrive = nil
- end
- end
- end
+ end
+ end
+ end
end
function updateRunning()
@@ -495,19 +492,19 @@ function updateOutRunners()
end
function updateGameState()
- deltaSeconds = playdate.getElapsedTime() or 0
- playdate.resetElapsedTime()
- elapsedSec = elapsedSec + deltaSeconds
- crankChange, acceleratedChange = playdate.getCrankChange() --[[@as number, number]]
+ deltaSeconds = playdate.getElapsedTime() or 0
+ playdate.resetElapsedTime()
+ elapsedSec = elapsedSec + deltaSeconds
+ crankChange, acceleratedChange = playdate.getCrankChange() --[[@as number, number]]
ball.x = ballAnimatorX:currentValue()
ball.y = ballAnimatorY:currentValue() + ballFloatAnimator:currentValue()
- if currentMode == MODES.batting then
+ if currentMode == MODES.batting then
updateBatting()
- elseif currentMode == MODES.running then
+ elseif currentMode == MODES.running then
updateRunning()
- end
+ end
updateFielders()
updateOutRunners()
@@ -520,15 +517,15 @@ function drawScoreboard()
local y = SCREEN.H * 0.95
local x = SCREEN.W * 0.05
- gfx.setLineWidth(1)
- gfx.setColor(gfx.kColorBlack)
+ gfx.setLineWidth(1)
+ gfx.setColor(gfx.kColorBlack)
gfx.fillRect(x - 15, y - 44, 73, 55)
- gfx.setColor(gfx.kColorWhite)
- for i=outs,2 do
+ gfx.setColor(gfx.kColorWhite)
+ for i = outs, 2 do
gfx.drawCircleAtPoint(x + (i * 2.5 * OUT_BUBBLE_SIZE), y, OUT_BUBBLE_SIZE)
end
- for i=0,(outs - 1) do
+ for i = 0, (outs - 1) do
gfx.fillCircleAtPoint(x + (i * 2.5 * OUT_BUBBLE_SIZE), y, OUT_BUBBLE_SIZE)
end
@@ -540,51 +537,48 @@ function drawScoreboard()
end
function playdate.update()
- updateGameState()
- playdate.graphics.animation.blinker.updateAll()
+ updateGameState()
+ playdate.graphics.animation.blinker.updateAll()
- gfx.clear()
+ gfx.clear()
if ball.x < BALL_OFFSCREEN then
-- TODO: Show baserunning minimap when panning?
local offsetX, offsetY = getDrawOffset(SCREEN.W, SCREEN.H, ball.x, ball.y)
gfx.setDrawOffset(offsetX, offsetY)
- end
+ end
- grassBackground:draw(-400, -240)
+ grassBackground:draw(-400, -240)
- gfx.setColor(gfx.kColorBlack)
- gfx.setLineWidth(2)
+ gfx.setColor(gfx.kColorBlack)
+ gfx.setLineWidth(2)
- gfx.drawCircleAtPoint(ball.x, ball.y, ball.size)
+ gfx.drawCircleAtPoint(ball.x, ball.y, ball.size)
- local fielderDanceHeight = fielderDanceAnimator:currentValue()
- for _,fielder in pairs(fielders) do
- gfx.fillRect(fielder.x, fielder.y - fielderDanceHeight, 14, 25)
- end
+ 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
+ gfx.drawLine(batBase.x, batBase.y, batTip.x, batTip.y)
+ end
- if playdate.isCrankDocked() then -- or (crankChange < 2 and currentMode == MODES.running) then
- playdate.ui.crankIndicator:draw()
- end
+ 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? 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)
- end
- for _,runner in pairs(outRunners) do
+ playerImageBlipper:draw(false, runner.x, runner.y)
+ end
+ for _, runner in pairs(outRunners) do
playerFrown:draw(runner.x, runner.y)
- end
+ end
- drawScoreboard()
+ drawScoreboard()
end
-init()
\ No newline at end of file
+init()
diff --git a/src/utils.lua b/src/utils.lua
index 07cd0f4..5a693c3 100644
--- a/src/utils.lua
+++ b/src/utils.lua
@@ -1,5 +1,7 @@
+-- stylua: ignore start
import 'CoreLibs/animation.lua'
import 'CoreLibs/graphics.lua'
+-- stylua: ignore end
-- number = ((number * 2) - 1)
-- return number * number
@@ -8,28 +10,28 @@ import 'CoreLibs/graphics.lua'
-- return c * t / d + b
function easingHill(t, b, c, d)
- c = c + 0.0 -- convert to float to prevent integer overflow
- t = t / d
- t = ((t * 2) - 1)
- t = t * t
- return (c * t) + b
+ c = c + 0.0 -- convert to float to prevent integer overflow
+ t = t / d
+ t = ((t * 2) - 1)
+ t = t * t
+ return (c * t) + b
end
---@param x number
---@param y number
---@return XYPair
function xy(x, y)
- return {
- x = x,
- y = y
- }
+ return {
+ x = x,
+ y = y,
+ }
end
--- Returns the normalized vector as two values, plus the distance between the given points.
---@return number, number, number
function normalizeVector(x1, y1, x2, y2)
- local distance, a, b = distanceBetween(x1, y1, x2, y2)
- return a / distance, b / distance, distance
+ local distance, a, b = distanceBetween(x1, y1, x2, y2)
+ return a / distance, b / distance, distance
end
---@generic T
@@ -37,13 +39,13 @@ end
---@param condition fun(T): boolean
---@return T[]
function filter(array, condition)
- local newArray = {}
- for _,element in pairs(array) do
- if condition(element) then
- newArray[#newArray + 1] = element
- end
- end
- return newArray
+ local newArray = {}
+ for _, element in pairs(array) do
+ if condition(element) then
+ newArray[#newArray + 1] = element
+ end
+ end
+ return newArray
end
---@generic TIn
@@ -52,39 +54,39 @@ end
---@param mapper fun(TIn): TOut
---@return TOut[]
function map(array, mapper)
- local newArray = {}
- for _,element in pairs(array) do
- newArray[#newArray + 1] = mapper(element)
- end
- return newArray
+ local newArray = {}
+ for _, element in pairs(array) do
+ newArray[#newArray + 1] = mapper(element)
+ end
+ return newArray
end
---@return number, number, number
function distanceBetween(x1, y1, x2, y2)
- local a = x1 - x2
- local b = y1 - y2
- return math.sqrt((a*a) + (b*b)), a, b
+ local a = x1 - x2
+ local b = y1 - y2
+ return math.sqrt((a * a) + (b * b)), a, b
end
--- Returns true only if the point is below the given line, within the x bounds of said line, and above the bottomBound
--- @return boolean
function pointDirectlyUnderLine(pointX, pointY, lineX1, lineY1, lineX2, lineY2, bottomBound)
- -- This check currently assumes right-handedness.
- -- I.e. it assumes the ball is to the right of batBaseX
- if pointX < lineX1 or pointX > lineX2 or pointY > bottomBound then
- return false
- end
+ -- This check currently assumes right-handedness.
+ -- I.e. it assumes the ball is to the right of batBaseX
+ if pointX < lineX1 or pointX > lineX2 or pointY > bottomBound then
+ return false
+ end
- local m = (lineY2 - lineY1) / (lineX2 - lineX1)
+ local m = (lineY2 - lineY1) / (lineX2 - lineX1)
- -- y = mx + b
- -- b = y1 - (m * x1)
- local b = lineY1 - (m * lineX1)
- local yOnLine = (m * pointX) + b
- local yP = pointY
- local yDelta = yOnLine - yP
+ -- y = mx + b
+ -- b = y1 - (m * x1)
+ local b = lineY1 - (m * lineX1)
+ local yOnLine = (m * pointX) + b
+ local yP = pointY
+ local yDelta = yOnLine - yP
- return yDelta <= 0
+ return yDelta <= 0
end
--- Returns the nearest position object from the given point, as well as its distance from that point
@@ -94,9 +96,9 @@ end
---@param y number
---@return T,number|nil
function getNearestOf(array, x, y, extraCondition)
- local nearest, nearestDistance = nil, nil
- for _, element in pairs(array) do
- if not extraCondition or extraCondition(element) then
+ local nearest, nearestDistance = nil, nil
+ for _, element in pairs(array) do
+ if not extraCondition or extraCondition(element) then
if nearest == nil then
nearest = element
nearestDistance = distanceBetween(element.x, element.y, x, y)
@@ -107,29 +109,29 @@ function getNearestOf(array, x, y, extraCondition)
nearestDistance = distance
end
end
- end
- end
+ end
+ end
- return nearest, nearestDistance
+ return nearest, nearestDistance
end
local NO_VALUE = {}
function buildCache(fetcher)
- local cacheData = {}
- return {
- cacheDate = cacheData,
- get = function(key)
- if cacheData[key] == NO_VALUE then
- return nil
- end
- if cacheData[key] ~= nil then
- return cacheData[key]
- end
- cacheData[key] = fetcher(key) or NO_VALUE
- return cacheData[key]
- end
- }
+ local cacheData = {}
+ return {
+ cacheDate = cacheData,
+ get = function(key)
+ if cacheData[key] == NO_VALUE then
+ return nil
+ end
+ if cacheData[key] ~= nil then
+ return cacheData[key]
+ end
+ cacheData[key] = fetcher(key) or NO_VALUE
+ return cacheData[key]
+ end,
+ }
end
blipper = {}
@@ -146,6 +148,6 @@ function blipper.new(msInterval, imagePath1, imagePath2)
draw = function(self, disableBlipping, x, y)
local currentImage = (disableBlipping or self.blinker.on) and self.image2 or self.image1
currentImage:draw(x, y)
- end
+ end,
}
-end
\ No newline at end of file
+end