Get most of the way toward basic new-round behavior
Committing while in a mostly-working state so I can get some refactoring done.
This commit is contained in:
parent
2e87bc8836
commit
590121f7a6
|
@ -782,10 +782,11 @@ function tiny.update(world, dt, filter)
|
|||
for i = 1, #systems do
|
||||
local system = systems[i]
|
||||
if system.active and ((not filter) or filter(world, system)) then
|
||||
|
||||
-- TODO: Track how long each system runs for during any given frame.
|
||||
-- Update Systems that have an update method (most Systems)
|
||||
local update = system.update
|
||||
if update then
|
||||
--local currentMs = playdate.getCurrentTimeMilliseconds()
|
||||
local interval = system.interval
|
||||
if interval then
|
||||
local bufferedTime = (system.bufferedTime or 0) + dt
|
||||
|
@ -797,11 +798,14 @@ function tiny.update(world, dt, filter)
|
|||
else
|
||||
update(system, dt)
|
||||
end
|
||||
--local endTimeMs = playdate.getCurrentTimeMilliseconds()
|
||||
--print(tostring(endTimeMs - currentMs) .. "ms taken to update " .. system.name)
|
||||
end
|
||||
|
||||
system.modified = false
|
||||
end
|
||||
end
|
||||
--print("")
|
||||
|
||||
-- Iterate through Systems IN ORDER AGAIN
|
||||
for i = 1, #systems do
|
||||
|
|
|
@ -3,13 +3,16 @@ Cart = {}
|
|||
local sizeX, sizeY = CartSprite:getSize()
|
||||
local size = { x = sizeX, y = sizeY * 0.75 }
|
||||
|
||||
cartSystem = filteredSystem("cart", { isCart = T.marker })
|
||||
|
||||
function Cart.reset(o)
|
||||
o.isCart = T.marker
|
||||
o.position = {
|
||||
x = 20,
|
||||
y = 50,
|
||||
}
|
||||
o.velocity = {
|
||||
x = 200 + (100 * math.random()),
|
||||
x = 300 + (100 * math.random()),
|
||||
y = 175 * (math.random() - 1),
|
||||
}
|
||||
o.size = size
|
||||
|
@ -33,9 +36,11 @@ function Cart.reset(o)
|
|||
y = self.position.y,
|
||||
},
|
||||
})
|
||||
-- Focus on the center, where the cart stopped
|
||||
world:addEntity({
|
||||
position = { x = self.position.x, y = self.position.y },
|
||||
focusPriority = 1,
|
||||
removeAtRoundStart = true,
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
effectSystem = filteredSystem({ canReceive })
|
||||
effectSystem = filteredSystem("effects", { canReceive })
|
||||
|
||||
Effects = {}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ Ingredients = {}
|
|||
local ingredientCache = {}
|
||||
|
||||
local _ingredientCacheIndex = 1
|
||||
local maxCache = 100
|
||||
local maxCache = 80
|
||||
for i = 1, maxCache do
|
||||
ingredientCache[i] = {}
|
||||
end
|
||||
|
@ -19,6 +19,16 @@ function Ingredients.cacheSize()
|
|||
return maxCache
|
||||
end
|
||||
|
||||
function Ingredients.clearCache(world)
|
||||
for _, o in ipairs(ingredientCache) do
|
||||
for key in pairs(o) do
|
||||
o[key] = nil
|
||||
end
|
||||
world:addEntity(o)
|
||||
end
|
||||
world:addEntity(Ingredients.getFirst())
|
||||
end
|
||||
|
||||
function Ingredients.nextInCache()
|
||||
local index = _ingredientCacheIndex
|
||||
_ingredientCacheIndex = _ingredientCacheIndex + 1
|
||||
|
|
15
src/main.lua
15
src/main.lua
|
@ -18,8 +18,8 @@ import("systems/filter-types.lua")
|
|||
import("systems/gravity.lua")
|
||||
import("systems/move-toward.lua")
|
||||
import("systems/velocity.lua")
|
||||
import("systems/rounds.lua")
|
||||
import("systems/spawner.lua")
|
||||
import("systems/rounds.lua")
|
||||
import("systems/camera-pan.lua")
|
||||
import("systems/collision-resolution.lua")
|
||||
import("systems/collision-detection.lua")
|
||||
|
@ -74,12 +74,18 @@ world:addEntity(Ingredients.getFirst())
|
|||
-- TODO: Re-enable when cart stops
|
||||
playdate.setAutoLockDisabled(true)
|
||||
|
||||
local startMsOffset = -playdate.getCurrentTimeMilliseconds()
|
||||
|
||||
function playdate.update()
|
||||
local deltaSeconds = playdate.getElapsedTime()
|
||||
playdate.resetElapsedTime()
|
||||
playdate.timer.updateTimers()
|
||||
gfx.clear(gfx.kColorWhite)
|
||||
playdate.drawFPS(5, 5)
|
||||
local fps = playdate.getFPS()
|
||||
if fps > 0 and fps < 20 then
|
||||
local currentTime = playdate.getCurrentTimeMilliseconds() + startMsOffset
|
||||
print("At " .. (currentTime / 1000) .. "s, FPS below 20: " .. fps)
|
||||
end
|
||||
|
||||
floor.position.x = Camera.pan.x - 600
|
||||
|
||||
|
@ -87,9 +93,4 @@ function playdate.update()
|
|||
|
||||
gfx.setDrawOffset(0, 0)
|
||||
Score:draw()
|
||||
|
||||
if playdate.buttonJustPressed(playdate.kButtonA) then
|
||||
Cart.reset(cart)
|
||||
init()
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,9 +9,9 @@ Camera = {
|
|||
},
|
||||
}
|
||||
|
||||
expireBelowScreenSystem = filteredSystem({ position = T.XyPair, expireBelowScreenBy = T.number })
|
||||
expireBelowScreenSystem = filteredSystem("expireBelowScreen", { position = T.XyPair, expireBelowScreenBy = T.number })
|
||||
|
||||
cameraPanSystem = filteredSystem({ focusPriority = T.number, position = T.XyPair }, function(e, dt)
|
||||
cameraPanSystem = filteredSystem("cameraPan", { focusPriority = T.number, position = T.XyPair }, function(e, dt)
|
||||
if e.focusPriority >= focusPriority.priority then
|
||||
focusPriority.position = e.position
|
||||
end
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
collidingEntities = filteredSystem({
|
||||
collidingEntities = filteredSystem("collidingEntitites", {
|
||||
velocity = T.XyPair,
|
||||
position = T.XyPair,
|
||||
size = T.XyPair,
|
||||
canCollideWith = T.bitMask,
|
||||
isSolid = Maybe(T.bool),
|
||||
})
|
||||
|
||||
collisionDetection = filteredSystem(
|
||||
collisionDetection = filteredSystem("collisionDetection",
|
||||
{ position = T.XyPair, size = T.XyPair, canBeCollidedBy = T.bitMask, isSolid = Maybe(T.bool) },
|
||||
-- Here, the entity, e, refers to some entity that a moving object may be colliding *into*
|
||||
function(e, _, system)
|
||||
for _, collider in pairs(collidingEntities.entities) do
|
||||
if (e ~= collider) and collider.canCollideWith and ((collider.canCollideWith & e.canBeCollidedBy) ~= 0) then
|
||||
if (e ~= collider) and collider.canCollideWith and e.canBeCollidedBy and ((collider.canCollideWith & e.canBeCollidedBy) ~= 0) then
|
||||
local colliderTop = collider.position.y
|
||||
local colliderBottom = collider.position.y + collider.size.y
|
||||
local entityTop = e.position.y
|
||||
|
@ -33,3 +34,8 @@ collisionDetection = filteredSystem(
|
|||
end
|
||||
end
|
||||
)
|
||||
|
||||
function collisionDetection:preProcess()
|
||||
-- print("collidingEntities count: " .. #collidingEntities.entities)
|
||||
-- print("collidedEntities count: " .. #self.entities)
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
collisionResolution = filteredSystem({ collisionBetween = T.Collision }, function(e, dt, system)
|
||||
collisionResolution = filteredSystem("collisionResolution", { collisionBetween = T.Collision }, function(e, dt, system)
|
||||
local collidedInto, collider = e.collisionBetween[1], e.collisionBetween[2]
|
||||
local colliderTop = collidedInto.position.y
|
||||
|
||||
|
@ -22,6 +22,7 @@ collisionResolution = filteredSystem({ collisionBetween = T.Collision }, functio
|
|||
|
||||
if collider.focusOnCollide then
|
||||
system.world:addEntity({
|
||||
removeAtRoundStart = true,
|
||||
focusPriority = collider.focusOnCollide,
|
||||
position = {
|
||||
x = collider.position.x,
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
local gfx <const> = playdate.graphics
|
||||
|
||||
drawRectanglesSystem = filteredSystem({ position = T.XyPair, drawAsRectangle = { size = T.XyPair } }, function(e, dt)
|
||||
drawRectanglesSystem = filteredSystem("drawRectangles", { position = T.XyPair, drawAsRectangle = { size = T.XyPair } }, function(e, dt)
|
||||
gfx.fillRect(e.position.x, e.position.y, e.drawAsRectangle.size.x, e.drawAsRectangle.size.y)
|
||||
end)
|
||||
|
||||
drawSpriteSystem = filteredSystem({ position = T.XyPair, drawAsSprite = T.PdImage }, function(e, dt, system)
|
||||
drawSpriteSystem = filteredSystem("drawSprites", { position = T.XyPair, drawAsSprite = T.PdImage }, function(e, dt, system)
|
||||
e.drawAsSprite:draw(e.position.x, e.position.y)
|
||||
end)
|
||||
|
||||
local textHeight = AshevilleSans14Bold:getHeight()
|
||||
local xMargin = 4
|
||||
|
||||
drawTextSystem = filteredSystem(
|
||||
drawTextSystem = filteredSystem("drawText",
|
||||
{ position = T.XyPair, drawAsText = { text = T.str, style = Maybe(T.str) } },
|
||||
function(e, dt)
|
||||
local textWidth = AshevilleSans14Bold:getTextWidth(e.drawAsText.text)
|
||||
|
|
|
@ -55,6 +55,8 @@ T = {
|
|||
InRelations = {},
|
||||
---@type InputState
|
||||
InputState = {},
|
||||
---@type Entity
|
||||
Entity = {},
|
||||
}
|
||||
|
||||
---@generic T
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
local G = -300
|
||||
fallSystem = filteredSystem({ velocity = T.XyPair, mass = T.number }, function(e, dt)
|
||||
fallSystem = filteredSystem("fall", { velocity = T.XyPair, mass = T.number }, function(e, dt)
|
||||
e.velocity.y = e.velocity.y - (G * dt * e.mass) - (0.5 * dt * dt)
|
||||
end)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
local buttonJustPressed = playdate.buttonJustPressed
|
||||
local inputState = {}
|
||||
|
||||
inputSystem = filteredSystem({ canReceiveInput = T.marker }, function(e, _, system)
|
||||
inputSystem = filteredSystem("input", { canReceiveInput = T.marker }, function(e, _, system)
|
||||
e.inputState = inputState
|
||||
system.world:addEntity(e)
|
||||
end)
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
menuController = filteredSystem({ menuItems = T.InRelations, inputState = T.InputState }, function(e, _, _)
|
||||
---@alias MenuItem { onSelect: fun(), highlighted: boolean, navigateDown: MenuItem | nil, navigateUp: MenuItem | nil }
|
||||
|
||||
---@type MenuItem[]
|
||||
local MenuItems = {}
|
||||
|
||||
menuController = filteredSystem("menuController", { menuItems = MenuItems, inputState = T.InputState }, function(e, _, system)
|
||||
for _, menuItem in pairs(e.menuItems) do
|
||||
if menuItem.highlighted then
|
||||
if e.inputState.aJustPressed then
|
||||
menuItem.onSelect()
|
||||
menuItem.onSelect(system.world)
|
||||
end
|
||||
if e.inputState.downJustPressed and menuItem.navigateDown then
|
||||
menuItem.highlighted = false
|
||||
|
|
|
@ -15,7 +15,7 @@ local function normalizeVector(xy1, xy2)
|
|||
return x / distance, y / distance, distance
|
||||
end
|
||||
|
||||
moveTowardSystem = filteredSystem({ moveToward = MoveToward, position = T.XyPair }, function(e, dt, system)
|
||||
moveTowardSystem = filteredSystem("moveToward", { moveToward = MoveToward, position = T.XyPair }, function(e, dt, system)
|
||||
local xNorm, yNorm, distance = normalizeVector(e.position, e.moveToward.target)
|
||||
if distance > e.moveToward.range then
|
||||
return
|
||||
|
|
|
@ -1,10 +1,66 @@
|
|||
collectedEntities = filteredSystem({ collected = T.PdImage })
|
||||
collectedEntities = filteredSystem("collectedEntities", { collected = T.PdImage })
|
||||
|
||||
local onCollidingRemove = { "mass", "velocity", "canCollideWith" }
|
||||
|
||||
roundSystem = filteredSystem({ roundAction = T.RoundStateAction, position = Maybe(T.XyPair) }, function(e, _, system)
|
||||
if e.roundAction == "end" then
|
||||
playdate.setAutoLockDisabled(false)
|
||||
local Drop = { i = T.number, delay = T.number, startAt = T.XyPair }
|
||||
|
||||
disableCollisionWhenRoundEnds = filteredSystem("disableCollisionWhenRoundEnds", { disableCollisionWhenRoundEnds = T.marker })
|
||||
|
||||
collectableDropSystem = filteredSystem("collectableDrop", { drop = Drop }, function(e, dt, system)
|
||||
e.drop.delay = e.drop.delay - dt
|
||||
if e.drop.delay > 0 then
|
||||
return
|
||||
end
|
||||
local collX, collY = e.drop.sprite:getSize()
|
||||
system.world:addEntity({
|
||||
drawAsSprite = e.drop.sprite,
|
||||
size = { x = collX, y = collY / 2 },
|
||||
mass = 0.5,
|
||||
velocity = { x = 0, y = 0 },
|
||||
position = { x = e.drop.startAt.x - (collX / 2), y = e.drop.startAt.y },
|
||||
canCollideWith = 2,
|
||||
canBeCollidedBy = 2,
|
||||
isSolid = true,
|
||||
stopMovingOnCollision = true,
|
||||
onCollidingRemove = onCollidingRemove,
|
||||
focusOnCollide = e.drop.i,
|
||||
expireBelowScreenBy = 5,
|
||||
removeAtRoundStart = true,
|
||||
})
|
||||
system.world:removeEntity(e)
|
||||
end)
|
||||
|
||||
removeAtRoundStart = filteredSystem("removeAtRoundStart", { removeAtRoundStart = T.bool })
|
||||
|
||||
filteredSystem("afterDelayAdd", { afterDelayAdd = { entity = T.Entity, delay = T.number } }, function(e, dt, system)
|
||||
e.afterDelayAdd.delay = e.afterDelayAdd.delay - dt
|
||||
if e.afterDelayAdd.delay > 0 then
|
||||
return
|
||||
end
|
||||
system.world:addEntity(e.afterDelayAdd.entity)
|
||||
system.world:removeEntity(e)
|
||||
end)
|
||||
|
||||
roundSystem = filteredSystem("round", { roundAction = T.RoundStateAction, position = Maybe(T.XyPair) }, function(e, _, system)
|
||||
if e.roundAction == "start" then
|
||||
for _, cart in pairs(cartSystem.entities) do
|
||||
Cart.reset(cart)
|
||||
system.world:addEntity(cart)
|
||||
end
|
||||
for _, remove in pairs(removeAtRoundStart.entities) do
|
||||
system.world:removeEntity(remove)
|
||||
end
|
||||
system.world:addSystem(spawnerSystem)
|
||||
Ingredients.clearCache(system.world)
|
||||
elseif e.roundAction == "end" then
|
||||
system.world:removeSystem(spawnerSystem)
|
||||
for _, toExpire in pairs(disableCollisionWhenRoundEnds.entities) do
|
||||
toExpire.canCollideWith = nil
|
||||
toExpire.canBeCollidedBy = nil
|
||||
--system.world:addEntity(toExpire)
|
||||
system.world:removeEntity(toExpire)
|
||||
end
|
||||
-- playdate.setAutoLockDisabled(false)
|
||||
|
||||
local y = e.position.y - 240
|
||||
local rectWidth = 150
|
||||
|
@ -20,31 +76,27 @@ roundSystem = filteredSystem({ roundAction = T.RoundStateAction, position = Mayb
|
|||
canBeCollidedBy = 2,
|
||||
isSolid = true,
|
||||
stopMovingOnCollision = true,
|
||||
removeAtRoundStart = true,
|
||||
})
|
||||
|
||||
-- TODO: Big ol' numbers displaying how many ingredients were collected?
|
||||
-- TODO: Could layer ingredients in rows of three? Maybe just when it's higher?
|
||||
local delayPerDrop = 150
|
||||
local delayPerDrop = 0.100
|
||||
local delay = 0
|
||||
for i, collectable in ipairs(collectedEntities.entities) do
|
||||
local collX, collY = collectable.collected:getSize()
|
||||
local _, collY = collectable.collected:getSize()
|
||||
y = y - collY - 15
|
||||
playdate.timer.new(delay, function(ee, ccollX, ccollY, yy, ii, ssystem, ccollectable)
|
||||
ssystem.world:addEntity({
|
||||
drawAsSprite = ccollectable.collected,
|
||||
size = { x = ccollX, y = ccollY / 2 },
|
||||
mass = 0.5,
|
||||
velocity = { x = 0, y = 0 },
|
||||
position = { x = ee.position.x - (ccollX / 2), y = yy },
|
||||
canCollideWith = 2,
|
||||
canBeCollidedBy = 2,
|
||||
isSolid = true,
|
||||
stopMovingOnCollision = true,
|
||||
onCollidingRemove = onCollidingRemove,
|
||||
focusOnCollide = ii,
|
||||
expireBelowScreenBy = 5,
|
||||
})
|
||||
end, e, collX, collY, y, i, system, collectable)
|
||||
system.world:addEntity({
|
||||
drop = {
|
||||
sprite = collectable.collected,
|
||||
i = i,
|
||||
delay = delay,
|
||||
startAt = {
|
||||
x = e.position.x,
|
||||
y = y
|
||||
}
|
||||
},
|
||||
})
|
||||
delay = delay + delayPerDrop
|
||||
system.world:removeEntity(collectable)
|
||||
end
|
||||
|
@ -61,9 +113,11 @@ roundSystem = filteredSystem({ roundAction = T.RoundStateAction, position = Mayb
|
|||
canReceiveInput = T.marker,
|
||||
}
|
||||
local upgradeBelow
|
||||
local i = #collectedEntities.entities
|
||||
for _, upgrade in ipairs(availableUpgrades) do
|
||||
i = i + 1
|
||||
local collX, collY = 75, 21
|
||||
y = y - collY - 15
|
||||
y = y - collY - 15 - 15
|
||||
local upgradeEntity = {
|
||||
onSelect = upgrade.apply,
|
||||
drawAsText = {
|
||||
|
@ -82,6 +136,7 @@ roundSystem = filteredSystem({ roundAction = T.RoundStateAction, position = Mayb
|
|||
focusOnCollide = i,
|
||||
navigateDown = upgradeBelow,
|
||||
highlighted = true,
|
||||
removeAtRoundStart = true,
|
||||
}
|
||||
if upgradeBelow then
|
||||
upgradeBelow.navigateUp = upgradeEntity
|
||||
|
@ -90,12 +145,15 @@ roundSystem = filteredSystem({ roundAction = T.RoundStateAction, position = Mayb
|
|||
end
|
||||
upgradeBelow = upgradeEntity
|
||||
menuEntity.menuItems[#menuEntity.menuItems + 1] = upgradeEntity
|
||||
playdate.timer.new(delay, function(_system, _upgradeEntity)
|
||||
_system.world:addEntity(_upgradeEntity)
|
||||
end, system, upgradeEntity)
|
||||
delay = delay + delayPerDrop
|
||||
system.world:addEntity({
|
||||
afterDelayAdd = {
|
||||
delay = delay,
|
||||
entity = upgradeEntity
|
||||
},
|
||||
})
|
||||
system.world:addEntity(menuEntity)
|
||||
end
|
||||
system.world:removeEntity(e)
|
||||
end
|
||||
system.world:removeEntity(e)
|
||||
end)
|
||||
|
|
|
@ -3,7 +3,7 @@ local odds = 0
|
|||
---@type { canSpawn: CanSpawn }
|
||||
local selectedSpawner
|
||||
|
||||
spawnerSystem = filteredSystem({ canSpawn = T.CanSpawn, odds = T.number }, function(spawner, _, _)
|
||||
spawnerSystem = filteredSystem("spawner", { canSpawn = T.CanSpawn, odds = T.number }, function(spawner, _, _)
|
||||
if odds <= 0 then
|
||||
return
|
||||
end
|
||||
|
@ -35,7 +35,7 @@ function spawnerSystem:preProcess()
|
|||
return tiny.SKIP_PROCESS
|
||||
end
|
||||
|
||||
local spawnEveryX = 26
|
||||
local spawnEveryX = 30
|
||||
|
||||
-- Currently spawns AT MOST one new ingredient per frame, which is probably not enough at high speeds!
|
||||
function spawnerSystem:postProcess()
|
||||
|
@ -55,8 +55,6 @@ function spawnerSystem:postProcess()
|
|||
self.world:addEntity(newlySpawned)
|
||||
end
|
||||
|
||||
local expireWhenOffScreenBy = { x = 2000, y = 480 }
|
||||
|
||||
---@param world World
|
||||
function addAllSpawners(world)
|
||||
function addCollectableSpawner(name, spawnerOdds, score, sprite, canBounce, yRange)
|
||||
|
@ -78,6 +76,7 @@ function addAllSpawners(world)
|
|||
drawAsSprite = sprite,
|
||||
collectable = sprite,
|
||||
expireWhenOffScreenBy = expireWhenOffScreenBy,
|
||||
disableCollisionWhenRoundEnds = T.marker,
|
||||
canBounce = canBounce,
|
||||
},
|
||||
},
|
||||
|
@ -85,7 +84,7 @@ function addAllSpawners(world)
|
|||
end
|
||||
|
||||
addCollectableSpawner("Lettuce", 0.7, 1, LettuceSprite, {
|
||||
flat = { x = 22, y = 190 },
|
||||
flat = { x = 12, y = 220 },
|
||||
mult = { x = 1, y = -0.5 },
|
||||
})
|
||||
|
||||
|
@ -113,20 +112,25 @@ function getAvailableSpawnerUpgrades()
|
|||
end
|
||||
|
||||
if spawner.canSpawn.entity.score then
|
||||
local name = "Double " .. spawner.name .. " value"
|
||||
upgrades[#upgrades + 1] = {
|
||||
name = "Double " .. spawner.name .. " value",
|
||||
apply = function()
|
||||
name = name,
|
||||
apply = function(world)
|
||||
print("Applying " .. name)
|
||||
spawner.canSpawn.entity.score = spawner.canSpawn.entity.score * 2
|
||||
world:addEntity({ roundAction = "start" })
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
assert(spawner.odds, "Expected all spawners to have an `odds` field!")
|
||||
local name = "Double " .. spawner.name .. " frequency"
|
||||
upgrades[#upgrades + 1] = {
|
||||
name = "Double " .. spawner.name .. " frequency",
|
||||
apply = function()
|
||||
name = name,
|
||||
apply = function(world)
|
||||
print("Applying " .. name)
|
||||
spawner.odds = spawner.odds * 2
|
||||
-- addEntity({ roundAction = "NEXT_ROUND" })
|
||||
world:addEntity({ roundAction = "start" })
|
||||
end,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
local sqrt = math.sqrt
|
||||
|
||||
velocitySystem = filteredSystem({ position = T.XyPair, velocity = T.XyPair }, function(e, dt, system)
|
||||
velocitySystem = filteredSystem("velocity", { position = T.XyPair, velocity = T.XyPair }, function(e, dt, system)
|
||||
if sqrt((e.velocity.x * e.velocity.x) + (e.velocity.y * e.velocity.y)) < 2 then
|
||||
e.velocity = nil
|
||||
if e.spawnEntitiesWhenStopped then
|
||||
|
|
|
@ -13,8 +13,9 @@ local isSimulator = playdate.isSimulator
|
|||
---@param shape T
|
||||
---@param process fun(entity: T, dt: number, system: System)
|
||||
---@return System | { entities: T[] }
|
||||
function filteredSystem(shape, process)
|
||||
function filteredSystem(name, shape, process)
|
||||
local system = tiny.processingSystem()
|
||||
system.name = name
|
||||
local keys = {}
|
||||
for key, value in pairs(shape) do
|
||||
if type(value) ~= "table" or value.maybe == nil then
|
||||
|
|
|
@ -4,7 +4,7 @@ Utils = {}
|
|||
---@generic T
|
||||
---@param fromArr T[]
|
||||
---@param n number
|
||||
---@generic T[]
|
||||
---@return T[]
|
||||
function Utils.getNDifferentValues(fromArr, n)
|
||||
assert(n >= 0, "n must be a non-negative integer")
|
||||
if n > #fromArr then
|
||||
|
@ -26,3 +26,49 @@ function Utils.getNDifferentValues(fromArr, n)
|
|||
end
|
||||
return randoms
|
||||
end
|
||||
|
||||
--- Track the number of instances of a given element, instead of needing multiple copies.
|
||||
---@class CountSet
|
||||
---@field private data table<table, number>
|
||||
---@field private elementCount number
|
||||
CountSet = {}
|
||||
|
||||
function CountSet.new()
|
||||
return setmetatable({ data = {}, elementCount = 0 }, { __index = CountSet })
|
||||
end
|
||||
|
||||
function CountSet:add(element)
|
||||
local existing = self.data[element]
|
||||
if existing then
|
||||
self.data[element] = existing + 1
|
||||
else
|
||||
self.data[element] = 1
|
||||
end
|
||||
self.elementCount = self.elementCount + 1
|
||||
end
|
||||
|
||||
function CountSet:balancedRandomPop()
|
||||
if self.elementCount == 0 then
|
||||
return
|
||||
end
|
||||
local toPop = math.random(self.elementCount)
|
||||
for element, count in pairs(self.data) do
|
||||
toPop = toPop - count
|
||||
if toPop <= 0 then
|
||||
local newCount = count - 1
|
||||
if newCount == 0 then
|
||||
self.data[element] = nil
|
||||
else
|
||||
self.data[element] = newCount
|
||||
end
|
||||
self.elementCount = self.elementCount - 1
|
||||
return element
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function CountSet:iterRandom()
|
||||
return function()
|
||||
return self:balancedRandomPop()
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue