Add .luarc.json for library reading.
Move more stuff to utils. More type hints. *Some* fleshing-out of getNextThrowTarget() etc. Closer to proper multi-baserunner support.
This commit is contained in:
parent
4093f9705a
commit
533c625d47
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"Lua.runtime.version": "Lua 5.4",
|
||||||
|
"Lua.diagnostics.disable": ["undefined-global", "lowercase-global"],
|
||||||
|
"Lua.diagnostics.globals": ["playdate", "import"],
|
||||||
|
"Lua.runtime.nonstandardSymbol": ["+=", "-=", "*=", "/="],
|
||||||
|
"Lua.workspace.library": ["/home/sage/Downloads/PlaydateSDK-2.6.2/CoreLibs"],
|
||||||
|
"Lua.workspace.preloadFileSize": 1000
|
||||||
|
}
|
281
src/main.lua
281
src/main.lua
|
@ -12,14 +12,14 @@ import 'utils.lua'
|
||||||
|
|
||||||
--- @alias Runner { x: number, y: number, nextBase: Base, prevBase: Base, forcedTo: Base }
|
--- @alias Runner { x: number, y: number, nextBase: Base, prevBase: Base, forcedTo: Base }
|
||||||
|
|
||||||
--- @alias Fielder { onArrive: fun() | nil, x: number | nil, y: number | nil, targetX: number | nil, targetY: number | nil }
|
--- @alias Fielder { onArrive: fun() | nil, x: number | nil, y: number | nil, target: Position | nil, speed: number }
|
||||||
|
|
||||||
local gfx = playdate.graphics
|
local gfx = playdate.graphics
|
||||||
playdate.display.setRefreshRate(50)
|
playdate.display.setRefreshRate(50)
|
||||||
gfx.setBackgroundColor(gfx.kColorWhite)
|
gfx.setBackgroundColor(gfx.kColorWhite)
|
||||||
playdate.setMenuImage(gfx.image.new("images/game/menu-image.png"))
|
playdate.setMenuImage(gfx.image.new("images/game/menu-image.png"))
|
||||||
|
|
||||||
local grassBackground = gfx.image.new("images/game/grass.png")
|
local grassBackground = gfx.image.new("images/game/grass.png") or {}
|
||||||
|
|
||||||
local playerImageBlipper = blipper.new(
|
local playerImageBlipper = blipper.new(
|
||||||
100,
|
100,
|
||||||
|
@ -27,6 +27,7 @@ local playerImageBlipper = blipper.new(
|
||||||
"images/game/player-lowhat.png"
|
"images/game/player-lowhat.png"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
---@type Position
|
||||||
local backgroundPan = {
|
local backgroundPan = {
|
||||||
x = 0,
|
x = 0,
|
||||||
y = 0,
|
y = 0,
|
||||||
|
@ -48,7 +49,7 @@ local batLength = 45
|
||||||
|
|
||||||
local tagDistance = 20
|
local tagDistance = 20
|
||||||
|
|
||||||
local ballY = ballStartY
|
local ballY = 999 -- ballStartY
|
||||||
local ballX = 200
|
local ballX = 200
|
||||||
local ballSize = 6
|
local ballSize = 6
|
||||||
|
|
||||||
|
@ -83,54 +84,63 @@ local nextBaseMap = {
|
||||||
[bases.third] = bases.home
|
[bases.third] = bases.home
|
||||||
}
|
}
|
||||||
|
|
||||||
local fielderSpeed = 40
|
|
||||||
|
|
||||||
---@type table<string, Fielder>
|
---@type table<string, Fielder>
|
||||||
local fielders = {
|
local fielders = {
|
||||||
first = {
|
first = {
|
||||||
x = nil,
|
speed = 40,
|
||||||
y = nil,
|
|
||||||
},
|
},
|
||||||
second = {},
|
second = {
|
||||||
shortstop = {},
|
speed = 40,
|
||||||
third = {},
|
},
|
||||||
pitcher = {},
|
shortstop = {
|
||||||
left = {},
|
speed = 40,
|
||||||
center = {},
|
},
|
||||||
right = {}
|
third = {
|
||||||
|
speed = 40,
|
||||||
|
},
|
||||||
|
pitcher = {
|
||||||
|
speed = 30,
|
||||||
|
},
|
||||||
|
catcher = {
|
||||||
|
speed = 20,
|
||||||
|
},
|
||||||
|
left = {
|
||||||
|
speed = 40,
|
||||||
|
},
|
||||||
|
center = {
|
||||||
|
speed = 40,
|
||||||
|
},
|
||||||
|
right = {
|
||||||
|
speed = 40,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetFielderPositions()
|
--- Resets the target positions of all fielders to their defaults (at their field positions).
|
||||||
fielders.first.x = screenW - 65
|
---@param fromOffTheField boolean If provided, also sets all runners' current position to one centralized location.
|
||||||
fielders.first.y = screenH * 0.48
|
function resetFielderPositions(fromOffTheField)
|
||||||
|
if fromOffTheField then
|
||||||
|
for _,fielder in pairs(fielders) do
|
||||||
|
fielder.x = centerX
|
||||||
|
fielder.y = screenH
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
fielders.second.x = screenW * 0.70
|
fielders.first.target = { x = screenW - 65, y = screenH * 0.48 }
|
||||||
fielders.second.y = screenH * 0.30
|
fielders.second.target = { x = screenW * 0.70, y = screenH * 0.30 }
|
||||||
|
fielders.shortstop.target = { x = screenW * 0.30, y = screenH * 0.30 }
|
||||||
fielders.shortstop.x = screenW * 0.30
|
fielders.third.target = { x = screenW * 0.1, y = screenH * 0.48 }
|
||||||
fielders.shortstop.y = screenH * 0.30
|
fielders.pitcher.target = { x = screenW * 0.48, y = screenH * 0.40 }
|
||||||
|
fielders.catcher.target = { x = screenW * 0.475, y = screenH * 0.92 }
|
||||||
fielders.third.x = screenW * 0.1
|
fielders.left.target = { x = screenW * -1, y = screenH * -0.2 }
|
||||||
fielders.third.y = screenH * 0.48
|
fielders.center.target = { x = centerX, y = screenH * -0.4 }
|
||||||
|
fielders.right.target = { x = screenW * 2, y = screenH * fielders.left.target.y }
|
||||||
fielders.pitcher.x = screenW * 0.48
|
|
||||||
fielders.pitcher.y = screenH * 0.40
|
|
||||||
|
|
||||||
fielders.left.x = screenW * -1
|
|
||||||
fielders.left.y = screenH * -0.2
|
|
||||||
|
|
||||||
fielders.center.x = fielders.second.x
|
|
||||||
fielders.center.y = screenH * -0.4
|
|
||||||
|
|
||||||
fielders.right.x = screenW * 2
|
|
||||||
fielders.right.y = screenH * fielders.left.y
|
|
||||||
end
|
end
|
||||||
resetFielderPositions()
|
resetFielderPositions(true)
|
||||||
|
|
||||||
local playerStartingX = bases.home.x - 40
|
local playerStartingX = bases.home.x - 40
|
||||||
local playerStartingY = bases.home.y - 3
|
local playerStartingY = bases.home.y - 3
|
||||||
|
|
||||||
--- @type table<Runner, Runner>
|
--- @type Runner[]
|
||||||
local runners = { }
|
local runners = { }
|
||||||
|
|
||||||
---@return Runner
|
---@return Runner
|
||||||
|
@ -145,6 +155,7 @@ function newRunner()
|
||||||
return new
|
return new
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@type Runner | nil
|
||||||
local batter = newRunner()
|
local batter = newRunner()
|
||||||
|
|
||||||
function throwBall(destX, destY, easingFunc, flyTimeMs)
|
function throwBall(destX, destY, easingFunc, flyTimeMs)
|
||||||
|
@ -158,18 +169,12 @@ end
|
||||||
|
|
||||||
function pitch()
|
function pitch()
|
||||||
pitchAnimator:reset()
|
pitchAnimator:reset()
|
||||||
resetFielderPositions()
|
ballY = ballStartY
|
||||||
ballX = 200
|
ballX = 200
|
||||||
currentMode = MODES.batting
|
currentMode = MODES.batting
|
||||||
|
|
||||||
backgroundPan.y = 0
|
backgroundPan.y = 0
|
||||||
backgroundPan.x = 0
|
backgroundPan.x = 0
|
||||||
|
|
||||||
-- TODO: Add new runners, instead
|
|
||||||
--runners = {}
|
|
||||||
--batter.x = playerStartingX
|
|
||||||
--batter.y = playerStartingY
|
|
||||||
batter = newRunner()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function playdate.AButtonDown()
|
function playdate.AButtonDown()
|
||||||
|
@ -177,19 +182,19 @@ function playdate.AButtonDown()
|
||||||
end
|
end
|
||||||
|
|
||||||
function playdate.upButtonDown()
|
function playdate.upButtonDown()
|
||||||
batBaseY -= 1
|
batBaseY = batBaseY - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
function playdate.downButtonDown()
|
function playdate.downButtonDown()
|
||||||
batBaseY += 1
|
batBaseY = batBaseY + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
function playdate.rightButtonDown()
|
function playdate.rightButtonDown()
|
||||||
batBaseX += 1
|
batBaseX = batBaseX + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
function playdate.leftButtonDown()
|
function playdate.leftButtonDown()
|
||||||
batBaseX -= 1
|
batBaseX = batBaseX - 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local pitchClockSec = 99
|
local pitchClockSec = 99
|
||||||
|
@ -211,8 +216,6 @@ end
|
||||||
local ballCatchHitbox = 3
|
local ballCatchHitbox = 3
|
||||||
function isTouchingBall(x, y)
|
function isTouchingBall(x, y)
|
||||||
local ballDistance = distanceBetween(x, y, ballX, ballY)
|
local ballDistance = distanceBetween(x, y, ballX, ballY)
|
||||||
-- print("ballDistance:")
|
|
||||||
-- print(ballDistance)
|
|
||||||
return ballDistance < ballCatchHitbox
|
return ballDistance < ballCatchHitbox
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -224,31 +227,34 @@ function outRunner(runnerIndex)
|
||||||
updateForcedTos()
|
updateForcedTos()
|
||||||
end
|
end
|
||||||
|
|
||||||
function updateInfield()
|
function updateFielders()
|
||||||
if ballDestX == nil or ballDestY == nil then
|
local touchingBaseCache = buildCache(
|
||||||
return
|
function(runner)
|
||||||
|
return isTouchingBase(runner.x, runner.y)
|
||||||
end
|
end
|
||||||
|
)
|
||||||
|
|
||||||
for _,fielder in pairs(fielders) do
|
for _,fielder in pairs(fielders) do
|
||||||
if fielder.targetX ~= nil and fielder.targetY ~= nil then
|
-- TODO: Target unforced runners (or their target bases) for tagging
|
||||||
local x, y, distance = normalizeVector(fielder.x, fielder.y, fielder.targetX, fielder.targetY)
|
-- 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
|
if distance > 1 then
|
||||||
fielder.x -= x * fielderSpeed * deltaTime
|
fielder.x = fielder.x - (x * fielder.speed * deltaTime)
|
||||||
fielder.y -= y * fielderSpeed * deltaTime
|
fielder.y = fielder.y - (y * fielder.speed * deltaTime)
|
||||||
else
|
else
|
||||||
if fielder.onArrive then
|
if fielder.onArrive then
|
||||||
fielder.onArrive()
|
fielder.onArrive()
|
||||||
end
|
end
|
||||||
fielder.targetX = nil
|
fielder.target = nil
|
||||||
fielder.targetY = nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if isTouchingBall(fielder.x, fielder.y) then
|
if isTouchingBall(fielder.x, fielder.y) then
|
||||||
local touchedBase = isTouchingBase(fielder.x, fielder.y)
|
local touchedBase = isTouchingBase(fielder.x, fielder.y)
|
||||||
for i,runner in pairs(runners) do
|
for i,runner in pairs(runners) do
|
||||||
local runnerOnBase = isTouchingBase(runner.x, runner.y)
|
local runnerOnBase = touchingBaseCache.get(runner)
|
||||||
if touchedBase and runner.forcedTo == touchedBase and touchedBase ~= runnerOnBase then
|
if touchedBase and runner.forcedTo == touchedBase and touchedBase ~= runnerOnBase then
|
||||||
outRunner(i)
|
outRunner(i)
|
||||||
elseif not runnerOnBase then
|
elseif not runnerOnBase then
|
||||||
|
@ -262,37 +268,18 @@ function updateInfield()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the nearest base, as well as the distance from that base
|
--- Returns true if at least one runner is still moving
|
||||||
---@generic T : {x: number, y: number}
|
---@return boolean
|
||||||
---@param array T[]
|
|
||||||
---@param x: number
|
|
||||||
---@param y: number
|
|
||||||
---@return T,number
|
|
||||||
function getNearestOf(array, x, y)
|
|
||||||
local nearest, nearestDistance = nil, nil
|
|
||||||
for _, element in pairs(array) do
|
|
||||||
if nearest == nil then
|
|
||||||
nearest = element
|
|
||||||
nearestDistance = distanceBetween(element.x, element.y, x, y)
|
|
||||||
else
|
|
||||||
local distance = distanceBetween(element.x, element.y, x, y)
|
|
||||||
if distance < nearestDistance then
|
|
||||||
nearest = element
|
|
||||||
nearestDistance = distance
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nearest, nearestDistance
|
|
||||||
end
|
|
||||||
|
|
||||||
function updateRunners()
|
function updateRunners()
|
||||||
local runnerSpeed = 20
|
local autoRunSpeed = 20
|
||||||
|
--autoRunSpeed = 140
|
||||||
local nonPlayerRunners = filter(runners, function (runner)
|
local nonPlayerRunners = filter(runners, function (runner)
|
||||||
return runner ~= batter
|
return runner ~= batter
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
local runnerMoved = false
|
||||||
for _,runner in pairs(nonPlayerRunners) do
|
for _,runner in pairs(nonPlayerRunners) do
|
||||||
local nearestBase, nearestBaseDistance = getNearestOf(bases, runner.x, runner.y)
|
local _, nearestBaseDistance = getNearestOf(bases, runner.x, runner.y)
|
||||||
if runner.nextBase then
|
if runner.nextBase then
|
||||||
local nb = runner.nextBase
|
local nb = runner.nextBase
|
||||||
local x, y, distance = normalizeVector(runner.x, runner.y, nb.x, nb.y)
|
local x, y, distance = normalizeVector(runner.x, runner.y, nb.x, nb.y)
|
||||||
|
@ -302,17 +289,21 @@ function updateRunners()
|
||||||
if crankChange < 0 then
|
if crankChange < 0 then
|
||||||
mult = -1
|
mult = -1
|
||||||
end
|
end
|
||||||
|
local prevX, prevY = runner.x, runner.y
|
||||||
-- TODO: Drift toward nearest base?
|
-- TODO: Drift toward nearest base?
|
||||||
local autoRun = nearestBaseDistance > 5 and mult * runnerSpeed * deltaTime or 0
|
local autoRun = nearestBaseDistance > 5 and mult * autoRunSpeed * deltaTime or 0
|
||||||
mult = autoRun + (crankChange / 20)
|
mult = autoRun + (crankChange / 20)
|
||||||
runner.x -= x * mult
|
runner.x = runner.x - (x * mult)
|
||||||
runner.y -= y * mult
|
runner.y = runner.y - (y * mult)
|
||||||
|
runnerMoved = runnerMoved or prevX ~= runner.x or prevY ~= runner.y
|
||||||
else
|
else
|
||||||
runner.nextBase = nextBaseMap[runner.nextBase]
|
runner.nextBase = nextBaseMap[runner.nextBase]
|
||||||
runner.forcedTo = nil
|
runner.forcedTo = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return runnerMoved
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return boolean
|
---@return boolean
|
||||||
|
@ -325,32 +316,82 @@ function throwArrivedBeforeRunner()
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function getRunnerTargeting(base)
|
||||||
|
for _,runner in pairs(runners) do
|
||||||
|
if runner.nextBase == base then
|
||||||
|
return runner
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
---@return Base[]
|
---@return Base[]
|
||||||
function getForcedOutTargets()
|
function getForcedOutTargets()
|
||||||
return { bases.first }
|
local targets = {}
|
||||||
|
for _,base in pairs(bases) do
|
||||||
|
local runnerTargetingBase = getRunnerTargeting(base)
|
||||||
|
if runnerTargetingBase then
|
||||||
|
targets[#targets+1] = base
|
||||||
|
else
|
||||||
|
return targets
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return targets
|
||||||
|
-- return { bases.first }
|
||||||
end
|
end
|
||||||
|
|
||||||
---@return number,number
|
--- Returns the position,distance of the basest closest to the runner furthest from a base
|
||||||
function getNextThrowTarget()
|
function getBaseOfStrandedRunner()
|
||||||
local targets = getForcedOutTargets()
|
local farRunnersBase, farDistance
|
||||||
return targets[1].x, targets[1].y
|
for _,runner in pairs(runners) do
|
||||||
|
local base, distance = getNearestOf(bases, runner.x, runner.y, function(base)
|
||||||
|
return runner.nextBase == base
|
||||||
|
end)
|
||||||
|
if farRunnersBase == nil then
|
||||||
|
farRunnersBase = base
|
||||||
|
farDistance = distance
|
||||||
|
elseif farDistance < distance then
|
||||||
|
farRunnersBase = base
|
||||||
|
farDistance = distance
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return farRunnersBase, farDistance
|
||||||
end
|
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()
|
||||||
|
print("forcedOutTargets:")
|
||||||
|
printTable(targets)
|
||||||
|
if #targets ~= 0 then
|
||||||
|
return targets[1].x, targets[1].y
|
||||||
|
end
|
||||||
|
|
||||||
|
-- local baseCloseToStrandedRunner = getBaseOfStrandedRunner()
|
||||||
|
-- return baseCloseToStrandedRunner.x, baseCloseToStrandedRunner.y
|
||||||
|
end
|
||||||
|
|
||||||
|
local resetFieldersAfterSeconds = 5
|
||||||
|
local secondsSinceLastRunnerMove = 0
|
||||||
|
|
||||||
function updateGameState()
|
function updateGameState()
|
||||||
deltaTime = playdate.getElapsedTime()
|
deltaTime = playdate.getElapsedTime() or 0
|
||||||
playdate.resetElapsedTime()
|
playdate.resetElapsedTime()
|
||||||
elapsedTime = elapsedTime + deltaTime
|
elapsedTime = elapsedTime + deltaTime
|
||||||
|
|
||||||
if elapsedTime > pitchClockSec then
|
-- if elapsedTime > pitchClockSec then
|
||||||
elapsedTime = 0
|
-- elapsedTime = 0
|
||||||
pitch()
|
-- pitch()
|
||||||
end
|
-- end
|
||||||
|
|
||||||
if currentMode == MODES.running then
|
if currentMode == MODES.running then
|
||||||
ballX = hitAnimatorX:currentValue()
|
ballX = hitAnimatorX:currentValue()
|
||||||
ballY = hitAnimatorY:currentValue()
|
ballY = hitAnimatorY:currentValue()
|
||||||
ballSize = ballSizeAnimator:currentValue()
|
ballSize = ballSizeAnimator:currentValue()
|
||||||
else
|
elseif ballY < 999 then
|
||||||
ballY = pitchAnimator:currentValue()
|
ballY = pitchAnimator:currentValue()
|
||||||
ballSize = 6
|
ballSize = 6
|
||||||
end
|
end
|
||||||
|
@ -375,25 +416,36 @@ function updateGameState()
|
||||||
ballDestX = ballX + (ballVelX * hitMult)
|
ballDestX = ballX + (ballVelX * hitMult)
|
||||||
ballDestY = ballY + (ballVelY * hitMult)
|
ballDestY = ballY + (ballVelY * hitMult)
|
||||||
throwBall(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000)
|
throwBall(ballDestX, ballDestY, playdate.easingFunctions.outQuint, 2000)
|
||||||
local chasingFielder = getNearestOf(fielders, ballDestX, ballDestY)
|
|
||||||
chasingFielder.targetX = ballDestX
|
fielders.first.target = bases.first
|
||||||
chasingFielder.targetY = ballDestY
|
|
||||||
chasingFielder.onArrive = function()
|
|
||||||
local targetX, targetY = getNextThrowTarget()
|
|
||||||
throwBall(targetX, targetY, playdate.easingFunctions.linear)
|
|
||||||
chasingFielder.onArrive = nil
|
|
||||||
end
|
|
||||||
fielders.first.targetX = bases.first.x
|
|
||||||
fielders.first.targetY = bases.first.y
|
|
||||||
batter.nextBase = bases.first
|
batter.nextBase = bases.first
|
||||||
batter.forcedTo = bases.first
|
batter.forcedTo = bases.first
|
||||||
batter = nil -- Demote batter to a mere runner
|
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
|
||||||
|
throwBall(targetX, targetY, playdate.easingFunctions.linear)
|
||||||
|
chasingFielder.onArrive = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if currentMode == MODES.running then
|
if currentMode == MODES.running then
|
||||||
updateRunners()
|
if updateRunners() then
|
||||||
updateInfield()
|
secondsSinceLastRunnerMove = 0
|
||||||
|
else
|
||||||
|
secondsSinceLastRunnerMove = secondsSinceLastRunnerMove + deltaTime
|
||||||
|
if secondsSinceLastRunnerMove > resetFieldersAfterSeconds then
|
||||||
|
resetFielderPositions(false)
|
||||||
|
currentMode = MODES.batting
|
||||||
|
batter = newRunner()
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
updateFielders()
|
||||||
end
|
end
|
||||||
|
|
||||||
function playdate.update()
|
function playdate.update()
|
||||||
|
@ -443,6 +495,7 @@ function playdate.update()
|
||||||
|
|
||||||
-- TODO? Change blip speed depending on runner speed?
|
-- TODO? Change blip speed depending on runner speed?
|
||||||
for _,runner in pairs(runners) do
|
for _,runner in pairs(runners) do
|
||||||
|
-- TODO? Scale sprites down as y increases
|
||||||
playerImageBlipper:draw(
|
playerImageBlipper:draw(
|
||||||
false,
|
false,
|
||||||
runner.x + backgroundPan.x,
|
runner.x + backgroundPan.x,
|
||||||
|
|
|
@ -25,7 +25,7 @@ end
|
||||||
---@generic TIn
|
---@generic TIn
|
||||||
---@generic TOut
|
---@generic TOut
|
||||||
---@param array TIn[]
|
---@param array TIn[]
|
||||||
---@param condition fun(TIn): TOut
|
---@param mapper fun(TIn): TOut
|
||||||
---@return TOut[]
|
---@return TOut[]
|
||||||
function map(array, mapper)
|
function map(array, mapper)
|
||||||
local newArray = {}
|
local newArray = {}
|
||||||
|
@ -42,6 +42,8 @@ function distanceBetween(x1, y1, x2, y2)
|
||||||
return math.sqrt((a*a) + (b*b)), a, b
|
return math.sqrt((a*a) + (b*b)), a, b
|
||||||
end
|
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)
|
function pointDirectlyUnderLine(pointX, pointY, lineX1, lineY1, lineX2, lineY2, bottomBound)
|
||||||
-- This check currently assumes right-handedness.
|
-- This check currently assumes right-handedness.
|
||||||
-- I.e. it assumes the ball is to the right of batBaseX
|
-- I.e. it assumes the ball is to the right of batBaseX
|
||||||
|
@ -61,10 +63,56 @@ function pointDirectlyUnderLine(pointX, pointY, lineX1, lineY1, lineX2, lineY2,
|
||||||
return yDelta <= 0
|
return yDelta <= 0
|
||||||
end
|
end
|
||||||
|
|
||||||
blipper = {
|
--- Returns the nearest position object from the given point, as well as its distance from that point
|
||||||
--- Build an object that simply "blips" between the given images at the given interval.
|
---@generic T : {x: number, y: number | nil}
|
||||||
--- Expects `playdate.graphics.animation.blinker.updateAll()` to be called on every update.
|
---@param array T[]
|
||||||
new = function(msInterval, imagePath1, imagePath2)
|
---@param x number
|
||||||
|
---@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
|
||||||
|
if nearest == nil then
|
||||||
|
nearest = element
|
||||||
|
nearestDistance = distanceBetween(element.x, element.y, x, y)
|
||||||
|
else
|
||||||
|
local distance = distanceBetween(element.x, element.y, x, y)
|
||||||
|
if distance < nearestDistance then
|
||||||
|
nearest = element
|
||||||
|
nearestDistance = distance
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
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, imagePath1, imagePath2)
|
||||||
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 {
|
||||||
|
@ -76,5 +124,4 @@ blipper = {
|
||||||
currentImage:draw(x, y)
|
currentImage:draw(x, y)
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
}
|
|
Loading…
Reference in New Issue