Switch to spaces.
This commit is contained in:
parent
44ba30ee97
commit
9df836e0bf
4
Makefile
4
Makefile
|
@ -4,8 +4,8 @@ all:
|
|||
pdc src BatterUp.pdx
|
||||
|
||||
check:
|
||||
stylua -c src/
|
||||
stylua -c --indent-type Spaces src/
|
||||
cat __stub.ext.lua <(sed 's/^function/-- selene: allow(unused_variable)\nfunction/' ${PLAYDATE_SDK_PATH}/CoreLibs/__types.lua) ${SOURCE_FILES} | grep -v '^import' | sed 's/<const>//g' | selene -
|
||||
|
||||
lint:
|
||||
stylua src/
|
||||
stylua --indent-type Spaces src/
|
||||
|
|
|
@ -3,60 +3,60 @@ local AnnouncementTransitionMs <const> = 300
|
|||
local AnnouncerMarginX <const> = 26
|
||||
|
||||
local AnnouncerAnimatorInY <const> =
|
||||
playdate.graphics.animator.new(AnnouncementTransitionMs, -70, 0, playdate.easingFunctions.outBounce)
|
||||
playdate.graphics.animator.new(AnnouncementTransitionMs, -70, 0, playdate.easingFunctions.outBounce)
|
||||
local AnnouncerAnimatorOutY <const> =
|
||||
playdate.graphics.animator.new(AnnouncementTransitionMs, 0, -70, playdate.easingFunctions.outQuint)
|
||||
playdate.graphics.animator.new(AnnouncementTransitionMs, 0, -70, playdate.easingFunctions.outQuint)
|
||||
|
||||
-- selene: allow(unscoped_variables)
|
||||
announcer = {
|
||||
textQueue = {},
|
||||
animatorY = AnnouncerAnimatorInY,
|
||||
textQueue = {},
|
||||
animatorY = AnnouncerAnimatorInY,
|
||||
}
|
||||
|
||||
local DurationMs <const> = 3000
|
||||
|
||||
function announcer.popIn(self)
|
||||
self.animatorY = AnnouncerAnimatorInY
|
||||
self.animatorY:reset()
|
||||
self.animatorY = AnnouncerAnimatorInY
|
||||
self.animatorY:reset()
|
||||
|
||||
playdate.timer.new(DurationMs, function()
|
||||
self.animatorY = AnnouncerAnimatorOutY
|
||||
self.animatorY:reset()
|
||||
-- If this popIn() call was inside a timer, successive messages would be
|
||||
-- allowed to transition out. However, the Out animation, shortly followed by
|
||||
-- a new message popping in, is actually *more* jarring than the interrupt.
|
||||
if #self.textQueue ~= 1 then
|
||||
self:popIn()
|
||||
table.remove(self.textQueue, 1)
|
||||
else
|
||||
playdate.timer.new(AnnouncementTransitionMs, function()
|
||||
table.remove(self.textQueue, 1)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
playdate.timer.new(DurationMs, function()
|
||||
self.animatorY = AnnouncerAnimatorOutY
|
||||
self.animatorY:reset()
|
||||
-- If this popIn() call was inside a timer, successive messages would be
|
||||
-- allowed to transition out. However, the Out animation, shortly followed by
|
||||
-- a new message popping in, is actually *more* jarring than the interrupt.
|
||||
if #self.textQueue ~= 1 then
|
||||
self:popIn()
|
||||
table.remove(self.textQueue, 1)
|
||||
else
|
||||
playdate.timer.new(AnnouncementTransitionMs, function()
|
||||
table.remove(self.textQueue, 1)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function announcer.say(self, text)
|
||||
self.textQueue[#self.textQueue + 1] = text
|
||||
if #self.textQueue == 1 then
|
||||
self:popIn()
|
||||
end
|
||||
self.textQueue[#self.textQueue + 1] = text
|
||||
if #self.textQueue == 1 then
|
||||
self:popIn()
|
||||
end
|
||||
end
|
||||
|
||||
function announcer.draw(self, x, y)
|
||||
if #self.textQueue == 0 then
|
||||
return
|
||||
end
|
||||
x = x - 5 -- Infield center is slightly offset from screen center
|
||||
if #self.textQueue == 0 then
|
||||
return
|
||||
end
|
||||
x = x - 5 -- Infield center is slightly offset from screen center
|
||||
|
||||
local gfx = playdate.graphics
|
||||
local originalDrawMode = gfx.getImageDrawMode()
|
||||
local width = math.max(150, (AnnouncerMarginX * 2) + AnnouncementFont:getTextWidth(self.textQueue[1]))
|
||||
local animY = self.animatorY:currentValue()
|
||||
local gfx = playdate.graphics
|
||||
local originalDrawMode = gfx.getImageDrawMode()
|
||||
local width = math.max(150, (AnnouncerMarginX * 2) + AnnouncementFont:getTextWidth(self.textQueue[1]))
|
||||
local animY = self.animatorY:currentValue()
|
||||
|
||||
gfx.setColor(gfx.kColorBlack)
|
||||
gfx.fillRect(x - (width / 2), y + animY, width, 50)
|
||||
gfx.setImageDrawMode(gfx.kDrawModeInverted)
|
||||
AnnouncementFont:drawTextAligned(self.textQueue[1], x, y + 10 + animY, kTextAlignment.center)
|
||||
gfx.setImageDrawMode(originalDrawMode)
|
||||
gfx.setColor(gfx.kColorBlack)
|
||||
gfx.fillRect(x - (width / 2), y + animY, width, 50)
|
||||
gfx.setImageDrawMode(gfx.kDrawModeInverted)
|
||||
AnnouncementFont:drawTextAligned(self.textQueue[1], x, y + 10 + animY, kTextAlignment.center)
|
||||
gfx.setImageDrawMode(originalDrawMode)
|
||||
end
|
||||
|
|
216
src/ecs.lua
216
src/ecs.lua
|
@ -9,46 +9,46 @@ local systems <const> = {}
|
|||
|
||||
-- TODO: Add entity to any existing systems
|
||||
function ecs.addEntity(entity)
|
||||
allEntities[entity] = true
|
||||
for _, system in pairs(systems) do
|
||||
if entityMatchesShapes(entity, system.shapes) then
|
||||
system.entityCache[entity] = true
|
||||
else
|
||||
system.entityCache[entity] = nil
|
||||
end
|
||||
end
|
||||
allEntities[entity] = true
|
||||
for _, system in pairs(systems) do
|
||||
if entityMatchesShapes(entity, system.shapes) then
|
||||
system.entityCache[entity] = true
|
||||
else
|
||||
system.entityCache[entity] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ecs.removeEntity(entity)
|
||||
allEntities[entity] = nil
|
||||
for _, system in pairs(systems) do
|
||||
system.entityCache[entity] = nil
|
||||
end
|
||||
allEntities[entity] = nil
|
||||
for _, system in pairs(systems) do
|
||||
system.entityCache[entity] = nil
|
||||
end
|
||||
end
|
||||
|
||||
local Placeholder = {}
|
||||
---@generic T
|
||||
---@return T
|
||||
function ecs.field()
|
||||
return Placeholder
|
||||
return Placeholder
|
||||
end
|
||||
|
||||
function allKeysIncluded(entity, filter)
|
||||
for k, _ in pairs(filter) do
|
||||
if not entity[k] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
for k, _ in pairs(filter) do
|
||||
if not entity[k] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function entityMatchesShapes(entity, shapes)
|
||||
for _, shape in pairs(shapes) do
|
||||
if not allKeysIncluded(entity, shape) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
for _, shape in pairs(shapes) do
|
||||
if not allKeysIncluded(entity, shape) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
---@generic T
|
||||
|
@ -61,66 +61,66 @@ 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
|
||||
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)
|
||||
systems[#systems + 1] = {
|
||||
callback = callback,
|
||||
keys = keys,
|
||||
shapes = shapes,
|
||||
entityCache = nil,
|
||||
}
|
||||
systems[#systems + 1] = {
|
||||
callback = callback,
|
||||
keys = keys,
|
||||
shapes = shapes,
|
||||
entityCache = nil,
|
||||
}
|
||||
end
|
||||
|
||||
---@return boolean
|
||||
function is(entity, shape)
|
||||
return allKeysIncluded(entity, shape)
|
||||
return allKeysIncluded(entity, shape)
|
||||
end
|
||||
|
||||
---@param deltaSeconds number
|
||||
function ecs.update(deltaSeconds)
|
||||
for _, system in pairs(systems) do
|
||||
if not system.entityCache then
|
||||
system.entityCache = {}
|
||||
for entity, _ in pairs(allEntities) 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
|
||||
)
|
||||
end
|
||||
end
|
||||
for _, system in pairs(systems) do
|
||||
if not system.entityCache then
|
||||
system.entityCache = {}
|
||||
for entity, _ in pairs(allEntities) 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
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns a function that accepts a callback. This callback will receive one argument for each Shape provided.
|
||||
|
@ -139,26 +139,26 @@ 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 shapes = {}
|
||||
local callback
|
||||
local maybeShapes = { tShape, uShape, vShape, wShape, finalFunc }
|
||||
local shapes = {}
|
||||
local callback
|
||||
|
||||
for _, maybeShape in pairs(maybeShapes) do
|
||||
if type(maybeShape) == "table" then
|
||||
shapes[#shapes + 1] = maybeShape
|
||||
elseif type(maybeShape) == "function" then
|
||||
callback = maybeShape
|
||||
end
|
||||
end
|
||||
for _, maybeShape in pairs(maybeShapes) do
|
||||
if type(maybeShape) == "table" then
|
||||
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
|
||||
keys[#keys + 1] = key
|
||||
end
|
||||
end
|
||||
local keys = {}
|
||||
for _, shape in pairs(shapes) do
|
||||
for key, _ in pairs(shape) do
|
||||
keys[#keys + 1] = key
|
||||
end
|
||||
end
|
||||
|
||||
addSystem(callback, keys, shapes)
|
||||
addSystem(callback, keys, shapes)
|
||||
end
|
||||
|
||||
local f = ecs.field()
|
||||
|
@ -168,15 +168,15 @@ local Target = { target = XYPair }
|
|||
local Velocity = { velocity = XYPair }
|
||||
|
||||
function ecs.overlayOnto(entity, value)
|
||||
for key, v in pairs(value) do
|
||||
entity[key] = v
|
||||
end
|
||||
ecs.addEntity(entity)
|
||||
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
|
||||
|
@ -184,7 +184,7 @@ local data = {
|
|||
---@param entity unknown
|
||||
---@return T
|
||||
function ecs.get(shape, entity)
|
||||
return entity
|
||||
return entity
|
||||
end
|
||||
|
||||
---@generic T
|
||||
|
@ -192,29 +192,29 @@ end
|
|||
---@param shape `T`
|
||||
---@param value `T`
|
||||
function ecs.set(entity, shape, value)
|
||||
for key, v in pairs(shape) do
|
||||
entity[key] = value[v]
|
||||
end
|
||||
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)
|
||||
pos.x = pos.x + (delta * vel.x)
|
||||
pos.y = pos.y + (delta * vel.y)
|
||||
print("position")
|
||||
tprint(pos, 1)
|
||||
ecs.set(Target, e, {
|
||||
--target = { x = 10, y = 10}
|
||||
})
|
||||
pos.x = pos.x + (delta * vel.x)
|
||||
pos.y = pos.y + (delta * vel.y)
|
||||
print("position")
|
||||
tprint(pos, 1)
|
||||
ecs.set(Target, e, {
|
||||
--target = { x = 10, y = 10}
|
||||
})
|
||||
end)
|
||||
|
||||
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")
|
||||
pos.x = pos.x + (delta * vel.x)
|
||||
pos.y = pos.y + (delta * vel.y)
|
||||
print("position")
|
||||
tprint(pos, 1)
|
||||
ecs.set(e, Target, "hallo")
|
||||
end)
|
||||
|
||||
ecs.update(1)
|
||||
|
|
|
@ -3,21 +3,21 @@
|
|||
--- 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 > screenH then
|
||||
return 0, 0
|
||||
end
|
||||
offsetY = math.max(0, -1 * ballY)
|
||||
local offsetX, offsetY
|
||||
if ballY > screenH then
|
||||
return 0, 0
|
||||
end
|
||||
offsetY = math.max(0, -1 * ballY)
|
||||
|
||||
if ballX > 0 and ballX < screenW then
|
||||
offsetX = 0
|
||||
elseif ballX < 0 then
|
||||
offsetX = math.max(-1 * screenW, ballX * -1)
|
||||
elseif ballX > screenW then
|
||||
offsetX = math.min(screenW * 2, (ballX * -1) + screenW)
|
||||
end
|
||||
if ballX > 0 and ballX < screenW then
|
||||
offsetX = 0
|
||||
elseif ballX < 0 then
|
||||
offsetX = math.max(-1 * screenW, ballX * -1)
|
||||
elseif ballX > screenW then
|
||||
offsetX = math.min(screenW * 2, (ballX * -1) + screenW)
|
||||
end
|
||||
|
||||
return offsetX * 1.3, offsetY * 1.5
|
||||
return offsetX * 1.3, offsetY * 1.5
|
||||
end
|
||||
|
||||
-- selene: allow(unscoped_variables)
|
||||
|
@ -26,15 +26,15 @@ 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)
|
||||
blinker:start()
|
||||
return {
|
||||
blinker = blinker,
|
||||
image1 = playdate.graphics.image.new(imagePath1),
|
||||
image2 = playdate.graphics.image.new(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,
|
||||
}
|
||||
local blinker = playdate.graphics.animation.blinker.new(msInterval, msInterval, true)
|
||||
blinker:start()
|
||||
return {
|
||||
blinker = blinker,
|
||||
image1 = playdate.graphics.image.new(imagePath1),
|
||||
image2 = playdate.graphics.image.new(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
|
||||
|
|
908
src/main.lua
908
src/main.lua
File diff suppressed because it is too large
Load Diff
|
@ -10,53 +10,53 @@ local IndicatorWidth <const> = ScoreFont:getTextWidth(Indicator)
|
|||
---@param battingTeam any
|
||||
---@return string, number, string, number
|
||||
function getIndicators(teams, battingTeam)
|
||||
if teams.home == battingTeam then
|
||||
return Indicator, 0, "", IndicatorWidth
|
||||
end
|
||||
return "", IndicatorWidth, Indicator, 0
|
||||
if teams.home == battingTeam then
|
||||
return Indicator, 0, "", IndicatorWidth
|
||||
end
|
||||
return "", IndicatorWidth, Indicator, 0
|
||||
end
|
||||
|
||||
function drawScoreboard(x, y, teams, outs, battingTeam, inning)
|
||||
local gfx = playdate.graphics
|
||||
local gfx = playdate.graphics
|
||||
|
||||
local homeScore = teams.home.score
|
||||
local awayScore = teams.away.score
|
||||
local homeScore = teams.home.score
|
||||
local awayScore = teams.away.score
|
||||
|
||||
local homeIndicator, homeOffset, awayIndicator, awayOffset = getIndicators(teams, battingTeam)
|
||||
local homeIndicator, homeOffset, awayIndicator, awayOffset = getIndicators(teams, battingTeam)
|
||||
|
||||
local homeScoreText = homeIndicator .. "HOME " .. (homeScore > 9 and homeScore or " " .. homeScore)
|
||||
local awayScoreText = awayIndicator .. "AWAY " .. (awayScore > 9 and awayScore or " " .. awayScore)
|
||||
local homeScoreText = homeIndicator .. "HOME " .. (homeScore > 9 and homeScore or " " .. homeScore)
|
||||
local awayScoreText = awayIndicator .. "AWAY " .. (awayScore > 9 and awayScore or " " .. awayScore)
|
||||
|
||||
local rectWidth = (ScoreboardMarginX * 2)
|
||||
+ ScoreboardMarginRight
|
||||
+ ScoreFont:getTextWidth(homeScoreText)
|
||||
+ homeOffset
|
||||
local rectWidth = (ScoreboardMarginX * 2)
|
||||
+ ScoreboardMarginRight
|
||||
+ ScoreFont:getTextWidth(homeScoreText)
|
||||
+ homeOffset
|
||||
|
||||
gfx.setLineWidth(1)
|
||||
gfx.setColor(gfx.kColorBlack)
|
||||
gfx.fillRect(x, y, rectWidth, ScoreboardHeight)
|
||||
gfx.setLineWidth(1)
|
||||
gfx.setColor(gfx.kColorBlack)
|
||||
gfx.fillRect(x, y, rectWidth, ScoreboardHeight)
|
||||
|
||||
local originalDrawMode = gfx.getImageDrawMode()
|
||||
gfx.setImageDrawMode(gfx.kDrawModeInverted)
|
||||
local originalDrawMode = gfx.getImageDrawMode()
|
||||
gfx.setImageDrawMode(gfx.kDrawModeInverted)
|
||||
|
||||
ScoreFont:drawText(homeScoreText, x + ScoreboardMarginX + homeOffset, y + 6)
|
||||
ScoreFont:drawText(awayScoreText, x + ScoreboardMarginX + awayOffset, y + 22)
|
||||
local inningOffsetX = (x + ScoreboardMarginX + IndicatorWidth) + (4 * 2.5 * OutBubbleRadius)
|
||||
ScoreFont:drawText(inning, inningOffsetX, y + 39)
|
||||
ScoreFont:drawText(homeScoreText, x + ScoreboardMarginX + homeOffset, y + 6)
|
||||
ScoreFont:drawText(awayScoreText, x + ScoreboardMarginX + awayOffset, y + 22)
|
||||
local inningOffsetX = (x + ScoreboardMarginX + IndicatorWidth) + (4 * 2.5 * OutBubbleRadius)
|
||||
ScoreFont:drawText(inning, inningOffsetX, y + 39)
|
||||
|
||||
gfx.setImageDrawMode(originalDrawMode)
|
||||
gfx.setImageDrawMode(originalDrawMode)
|
||||
|
||||
gfx.setColor(gfx.kColorWhite)
|
||||
gfx.setColor(gfx.kColorWhite)
|
||||
|
||||
function circleParams(i)
|
||||
local circleOffset = i * 2.5 * OutBubbleRadius
|
||||
return (x + ScoreboardMarginX + OutBubbleRadius + IndicatorWidth) + circleOffset, y + 46, OutBubbleRadius
|
||||
end
|
||||
function circleParams(i)
|
||||
local circleOffset = i * 2.5 * OutBubbleRadius
|
||||
return (x + ScoreboardMarginX + OutBubbleRadius + IndicatorWidth) + circleOffset, y + 46, OutBubbleRadius
|
||||
end
|
||||
|
||||
for i = outs, 2 do
|
||||
gfx.drawCircleAtPoint(circleParams(i))
|
||||
end
|
||||
for i = 0, (outs - 1) do
|
||||
gfx.fillCircleAtPoint(circleParams(i))
|
||||
end
|
||||
for i = outs, 2 do
|
||||
gfx.drawCircleAtPoint(circleParams(i))
|
||||
end
|
||||
for i = 0, (outs - 1) do
|
||||
gfx.fillCircleAtPoint(circleParams(i))
|
||||
end
|
||||
end
|
||||
|
|
142
src/utils.lua
142
src/utils.lua
|
@ -4,40 +4,40 @@ import 'CoreLibs/graphics.lua'
|
|||
-- stylua: ignore end
|
||||
|
||||
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
|
||||
|
||||
-- Useful for quick print-the-value-in-place debugging.
|
||||
-- selene: allow(unused_variable)
|
||||
function label(value, name)
|
||||
if type(value) == "table" then
|
||||
print(name .. ":")
|
||||
printTable(value)
|
||||
else
|
||||
print(name .. ": " .. value)
|
||||
end
|
||||
return value
|
||||
if type(value) == "table" then
|
||||
print(name .. ":")
|
||||
printTable(value)
|
||||
else
|
||||
print(name .. ": " .. value)
|
||||
end
|
||||
return value
|
||||
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
|
||||
|
@ -45,41 +45,41 @@ 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
|
||||
|
||||
---@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
|
||||
|
@ -89,23 +89,23 @@ 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
|
||||
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
|
||||
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
|
||||
return nearest, nearestDistance
|
||||
end
|
||||
|
||||
--- Marker used by buildCache to indicate a cached `nil` value.
|
||||
|
@ -122,18 +122,18 @@ local NoValue <const> = {}
|
|||
---@generic Value
|
||||
---@return { get: fun(key: Key): Value }
|
||||
function buildCache(fetcher)
|
||||
local cacheData = {}
|
||||
return {
|
||||
get = function(key)
|
||||
if cacheData[key] == NoValue then
|
||||
return nil
|
||||
end
|
||||
if cacheData[key] ~= nil then
|
||||
return cacheData[key]
|
||||
end
|
||||
local fetched = fetcher(key)
|
||||
cacheData[key] = fetched ~= nil and fetched or NoValue
|
||||
return cacheData[key]
|
||||
end,
|
||||
}
|
||||
local cacheData = {}
|
||||
return {
|
||||
get = function(key)
|
||||
if cacheData[key] == NoValue then
|
||||
return nil
|
||||
end
|
||||
if cacheData[key] ~= nil then
|
||||
return cacheData[key]
|
||||
end
|
||||
local fetched = fetcher(key)
|
||||
cacheData[key] = fetched ~= nil and fetched or NoValue
|
||||
return cacheData[key]
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue