Switch to spaces.

This commit is contained in:
Sage Vaillancourt 2025-02-04 13:06:41 -05:00
parent 44ba30ee97
commit 9df836e0bf
7 changed files with 732 additions and 732 deletions

View File

@ -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/

View File

@ -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

View File

@ -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)

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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