commit
af5b494cdc
|
@ -0,0 +1,3 @@
|
||||||
|
*.pdx
|
||||||
|
.idea
|
||||||
|
lib/
|
|
@ -0,0 +1,4 @@
|
||||||
|
std = "lua54+playdate"
|
||||||
|
stds.project = {
|
||||||
|
globals = {"playdate", "tiny"},
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"Lua.runtime.version": "Lua 5.4",
|
||||||
|
"Lua.diagnostics.disable": ["undefined-global", "lowercase-global"],
|
||||||
|
"Lua.diagnostics.globals": ["playdate", "import", "tiny"],
|
||||||
|
"Lua.workspace.library": ["/home/sage/Downloads/PlaydateSDK-2.6.2/CoreLibs"],
|
||||||
|
"Lua.workspace.preloadFileSize": 1000
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
src/assets.lua
|
|
@ -0,0 +1,15 @@
|
||||||
|
all:
|
||||||
|
pdc --skip-unknown src Luncher.pdx
|
||||||
|
|
||||||
|
assets:
|
||||||
|
# lua lib/preprocess-cl.lua src/assets.lua2p
|
||||||
|
|
||||||
|
check: assets
|
||||||
|
stylua -c --indent-type Spaces src/
|
||||||
|
luacheck -g -d --codes src/ --exclude-files src/test/
|
||||||
|
|
||||||
|
test: check
|
||||||
|
(cd src; find ./test -name '*lua' | xargs -L1 -I %% lua %% -v)
|
||||||
|
|
||||||
|
lint:
|
||||||
|
stylua --indent-type Spaces src/
|
|
@ -0,0 +1,4 @@
|
||||||
|
# Luncher
|
||||||
|
|
||||||
|
Build the BIGGEST burger by **lunching** it through *delicious* ingredients.
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
Booster = {}
|
||||||
|
|
||||||
|
local gfx <const> = playdate.graphics
|
||||||
|
|
||||||
|
local function newBooster(x, y)
|
||||||
|
return setmetatable({
|
||||||
|
position = { x = x, y = y },
|
||||||
|
size = { x = 40, y = 10 },
|
||||||
|
}, { __index = Booster })
|
||||||
|
end
|
||||||
|
|
||||||
|
function Booster:elasticity(velocity)
|
||||||
|
velocity.y = (math.abs(velocity.y) + 190) * -0.5
|
||||||
|
velocity.x = velocity.x + 50
|
||||||
|
end
|
||||||
|
|
||||||
|
function Booster:draw()
|
||||||
|
gfx.fillRect(self.position.x, self.position.y, self.size.x, self.size.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
local boosterIndex = 1
|
||||||
|
local boosterCache = {}
|
||||||
|
for i = 1, 200 do
|
||||||
|
boosterCache[i] = newBooster(-999, 999)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Booster.cacheSize()
|
||||||
|
return #boosterCache
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Boosters should be "initialized" almost always increasing in X value
|
||||||
|
function Booster.get(x, y)
|
||||||
|
local booster = boosterCache[boosterIndex]
|
||||||
|
booster.position.x = x
|
||||||
|
booster.position.y = y
|
||||||
|
|
||||||
|
boosterIndex = boosterIndex + 1
|
||||||
|
if boosterIndex > #boosterCache then
|
||||||
|
boosterIndex = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return booster
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Assumes that at least one Booster has already been created.
|
||||||
|
function Booster.newestBooster()
|
||||||
|
return boosterCache[boosterIndex]
|
||||||
|
end
|
|
@ -0,0 +1,42 @@
|
||||||
|
function bouncer(v)
|
||||||
|
return function()
|
||||||
|
local h0 = 0.1 -- m/s
|
||||||
|
-- local v = 10 -- m/s, current velocity
|
||||||
|
local g = 10 -- m/s/s
|
||||||
|
local t = 0 -- starting time
|
||||||
|
local rho = 0.60 -- coefficient of restitution
|
||||||
|
local tau = 0.10 -- contact time for bounce
|
||||||
|
local hmax = h0 -- keep track of the maximum height
|
||||||
|
local h = h0
|
||||||
|
local hstop = 0.01 -- stop when bounce is less than 1 cm
|
||||||
|
local freefall = true -- state: freefall or in contact
|
||||||
|
local t_last = -math.sqrt(2 * h0 / g) -- time we would have launched to get to h0 at t=0
|
||||||
|
local vmax = math.sqrt(v * g)
|
||||||
|
|
||||||
|
while hmax > hstop do
|
||||||
|
local dt = coroutine.yield(h, not freefall)
|
||||||
|
if freefall then
|
||||||
|
local hnew = h + v * dt - 0.5 * g * dt * dt
|
||||||
|
if hnew < 0 then
|
||||||
|
-- Bounced!
|
||||||
|
t = t_last + 2 * math.sqrt(2 * hmax / g)
|
||||||
|
freefall = false
|
||||||
|
t_last = t + tau
|
||||||
|
h = 0
|
||||||
|
else
|
||||||
|
t = t + dt
|
||||||
|
v = v - g * dt
|
||||||
|
h = hnew
|
||||||
|
end
|
||||||
|
else
|
||||||
|
t = t + tau
|
||||||
|
vmax = vmax * rho
|
||||||
|
v = vmax
|
||||||
|
freefall = true
|
||||||
|
h = 0
|
||||||
|
end
|
||||||
|
hmax = 0.5 * vmax * vmax / g
|
||||||
|
end
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
Burger = {}
|
||||||
|
|
||||||
|
local gfx <const> = playdate.graphics
|
||||||
|
|
||||||
|
function Burger.reset(o)
|
||||||
|
o.position = {
|
||||||
|
x = 20,
|
||||||
|
y = 50,
|
||||||
|
}
|
||||||
|
o.velocity = {
|
||||||
|
x = 100 * math.random(),
|
||||||
|
y = 150 * (math.random() - 1),
|
||||||
|
}
|
||||||
|
o.size = {
|
||||||
|
x = 10,
|
||||||
|
y = 10,
|
||||||
|
}
|
||||||
|
o.mass = T.marker
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
|
||||||
|
function Burger.new()
|
||||||
|
return setmetatable(Burger.reset({}), { __index = Burger })
|
||||||
|
end
|
||||||
|
|
||||||
|
function Burger:draw()
|
||||||
|
gfx.fillCircleAtPoint(burger.position.x, burger.position.y, 10)
|
||||||
|
end
|
|
@ -0,0 +1,79 @@
|
||||||
|
-- stylua: ignore start
|
||||||
|
import 'CoreLibs/animation.lua'
|
||||||
|
import 'CoreLibs/animator.lua'
|
||||||
|
import 'CoreLibs/easing.lua'
|
||||||
|
import 'CoreLibs/graphics.lua'
|
||||||
|
import 'CoreLibs/object.lua'
|
||||||
|
import 'CoreLibs/timer.lua'
|
||||||
|
import 'CoreLibs/ui.lua'
|
||||||
|
import 'CoreLibs/utilities/where.lua'
|
||||||
|
-- stylua: ignore end
|
||||||
|
|
||||||
|
import("../lib/tiny.lua")
|
||||||
|
import("tiny-tools.lua")
|
||||||
|
import("systems/filter-types.lua")
|
||||||
|
import("systems/collision-detection.lua")
|
||||||
|
import("systems/draw.lua")
|
||||||
|
import("systems/gravity.lua")
|
||||||
|
import("systems/velocity.lua")
|
||||||
|
import("booster.lua")
|
||||||
|
import("burger.lua")
|
||||||
|
|
||||||
|
local tiny <const> = tiny
|
||||||
|
local gfx <const> = playdate.graphics
|
||||||
|
playdate.display.setRefreshRate(50)
|
||||||
|
gfx.setBackgroundColor(gfx.kColorWhite)
|
||||||
|
|
||||||
|
burger = Burger.new()
|
||||||
|
floor = {
|
||||||
|
position = { x = 0, y = 230 },
|
||||||
|
size = { x = 10000, y = 10 },
|
||||||
|
isSolid = true,
|
||||||
|
}
|
||||||
|
function floor:elasticity(velocity)
|
||||||
|
velocity.x = velocity.x * 0.9
|
||||||
|
velocity.y = velocity.y * -0.7
|
||||||
|
end
|
||||||
|
function floor:draw()
|
||||||
|
gfx.fillRect(floor.position.x, floor.position.y, floor.size.x, floor.size.y)
|
||||||
|
end
|
||||||
|
|
||||||
|
Camera = {
|
||||||
|
pan = {
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
world = tiny.world(fallSystem, velocitySystem, collisionDetection, drawSystem, burger, floor)
|
||||||
|
for i = 1, Booster.cacheSize() do
|
||||||
|
world:addEntity(Booster.get(i * 60 + (math.random(0, 50)), math.random(50, 200)))
|
||||||
|
end
|
||||||
|
|
||||||
|
function playdate.update()
|
||||||
|
local deltaSeconds = playdate.getElapsedTime()
|
||||||
|
playdate.resetElapsedTime()
|
||||||
|
gfx.clear(gfx.kColorWhite)
|
||||||
|
playdate.drawFPS(5, 5)
|
||||||
|
|
||||||
|
Camera.pan.x = math.min(0, -burger.position.x + 200)
|
||||||
|
Camera.pan.y = burger.position.y + 120
|
||||||
|
local newestX = Booster.newestBooster().position.x
|
||||||
|
local panX = -Camera.pan.x
|
||||||
|
printTable({ panX = panX, newestX = newestX })
|
||||||
|
local offset = 600
|
||||||
|
if newestX < panX then
|
||||||
|
Booster.get(panX + offset + (math.random(0, 50)), math.random(50, 200))
|
||||||
|
end
|
||||||
|
floor.position.x = -Camera.pan.x - offset
|
||||||
|
gfx.setDrawOffset(Camera.pan.x, Camera.pan.y)
|
||||||
|
|
||||||
|
world:update(deltaSeconds)
|
||||||
|
|
||||||
|
if playdate.buttonJustPressed(playdate.kButtonA) then
|
||||||
|
Burger.reset(burger)
|
||||||
|
for i = 1, Booster.cacheSize() do
|
||||||
|
Booster.get(i * 60 + (math.random(0, 50)), math.random(50, 200))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
collisionDetection = tiny.filteredSystem(
|
||||||
|
{ position = T.XyPair, size = T.XyPair, elasticity = T.Elasticity, isSolid = Maybe(T.bool) },
|
||||||
|
-- Here, the entity, e, refers to some entity that the burger global(!) may be colliding with.
|
||||||
|
function(e)
|
||||||
|
if not burger.velocity then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local burgerTop = burger.position.y
|
||||||
|
local burgerBottom = burger.position.y + burger.size.y
|
||||||
|
local entityTop = e.position.y
|
||||||
|
local entityBottom = entityTop + e.size.y
|
||||||
|
|
||||||
|
local withinY = (entityTop > burgerTop and entityTop < burgerBottom)
|
||||||
|
or (entityBottom > burgerTop and entityBottom < burgerBottom)
|
||||||
|
|
||||||
|
if not withinY then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if burger.position.x < e.position.x + e.size.x and burger.position.x + burger.size.x > e.position.x then
|
||||||
|
if e.isSolid then
|
||||||
|
-- Assumes impact from the top
|
||||||
|
burger.position.y = entityTop - burger.size.y
|
||||||
|
end
|
||||||
|
e:elasticity(burger.velocity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
drawSystem = tiny.filteredSystem({ draw = T.SelfFunction }, function(e, dt)
|
||||||
|
e:draw()
|
||||||
|
end)
|
|
@ -0,0 +1,27 @@
|
||||||
|
---@alias XyPair { x: number, y: number }
|
||||||
|
|
||||||
|
---@alias Elasticity fun(self, velocity: XyPair)
|
||||||
|
|
||||||
|
T = {
|
||||||
|
---@type XyPair
|
||||||
|
XyPair = { x = 1, y = 1 },
|
||||||
|
bool = true,
|
||||||
|
number = 0,
|
||||||
|
numberArray = { 1, 2, 3 },
|
||||||
|
str = "",
|
||||||
|
marker = {},
|
||||||
|
---@type fun(self)
|
||||||
|
SelfFunction = function(self) end,
|
||||||
|
---@type Elasticity
|
||||||
|
Elasticity = {
|
||||||
|
isRigid = true,
|
||||||
|
apply = function() end,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
---@generic T
|
||||||
|
---@param t T
|
||||||
|
---@return nil | T
|
||||||
|
function Maybe(t)
|
||||||
|
return { maybe = t }
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
local G = -300
|
||||||
|
fallSystem = tiny.filteredSystem({ velocity = T.XyPair, mass = T.marker }, function(e, dt)
|
||||||
|
e.velocity.y = e.velocity.y - (G * dt) - (0.5 * dt * dt)
|
||||||
|
end)
|
|
@ -0,0 +1,11 @@
|
||||||
|
local sqrt = math.sqrt
|
||||||
|
|
||||||
|
velocitySystem = tiny.filteredSystem({ position = T.XyPair, velocity = T.XyPair }, function(e, dt)
|
||||||
|
if sqrt((e.velocity.x * e.velocity.x) + (e.velocity.y * e.velocity.y)) < 0.1 then
|
||||||
|
e.velocity = nil
|
||||||
|
world:addEntity(e)
|
||||||
|
else
|
||||||
|
e.position.x = e.position.x + (e.velocity.x * dt)
|
||||||
|
e.position.y = e.position.y + (e.velocity.y * dt)
|
||||||
|
end
|
||||||
|
end)
|
|
@ -0,0 +1,55 @@
|
||||||
|
local isSimulator = playdate.isSimulator
|
||||||
|
|
||||||
|
-- local function keysContain(key, allowedKeys)
|
||||||
|
-- for _, allowedKey in pairs(allowedKeys) do
|
||||||
|
-- if key == allowedKey then
|
||||||
|
-- return true
|
||||||
|
-- end
|
||||||
|
-- end
|
||||||
|
-- return false
|
||||||
|
-- end
|
||||||
|
|
||||||
|
---@generic T
|
||||||
|
---@param shape T
|
||||||
|
---@param process fun(entity: T, dt: number, system: System)
|
||||||
|
---@return
|
||||||
|
function tiny.filteredSystem(shape, process)
|
||||||
|
local system = tiny.processingSystem()
|
||||||
|
local keys = {}
|
||||||
|
for key, value in pairs(shape) do
|
||||||
|
if type(value) ~= "table" or value.maybe == nil then
|
||||||
|
-- ^ Don't require any Maybe types
|
||||||
|
keys[#keys + 1] = key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
system.filter = tiny.requireAll(table.unpack(keys))
|
||||||
|
if isSimulator then
|
||||||
|
-- local acceptableKeys = ""
|
||||||
|
-- for _, key in ipairs(keys) do
|
||||||
|
-- acceptableKeys = acceptableKeys .. "'" .. key .. "', "
|
||||||
|
-- end
|
||||||
|
-- acceptableKeys = acceptableKeys .. "]"
|
||||||
|
function system:process(e, dt)
|
||||||
|
-- local _e = e
|
||||||
|
-- e = setmetatable({}, {
|
||||||
|
-- __newindex = function()
|
||||||
|
-- error("Do not attempt to mutate entity data!")
|
||||||
|
-- end,
|
||||||
|
-- __index = function(_, key)
|
||||||
|
-- -- TODO: also assert their types
|
||||||
|
-- assert(
|
||||||
|
-- keysContain(key, keys),
|
||||||
|
-- "Attempted to use key '" .. key .. "' - should be one of [ " .. acceptableKeys
|
||||||
|
-- )
|
||||||
|
-- return _e[key]
|
||||||
|
-- end,
|
||||||
|
-- })
|
||||||
|
process(e, dt, self)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
function system:process(e, dt)
|
||||||
|
process(e, dt, self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return system
|
||||||
|
end
|
Loading…
Reference in New Issue