Turn spawners into entities driven by the spawnerSystem.
This commit is contained in:
parent
5d16c75cf6
commit
8162c7f723
|
@ -1,10 +1,28 @@
|
||||||
|
effectSystem = filteredSystem({ canReceive })
|
||||||
|
|
||||||
Effects = {}
|
Effects = {}
|
||||||
|
|
||||||
---@param entity { canBeBounced: CanBeBounced }
|
---@param entity { canBeBounced: CanBeBounced }
|
||||||
function Effects.makeBouncier(entity)
|
function Effects.makeBouncier(entity)
|
||||||
entity.canBeBounced.mult = entity.canBeBounced.mult * 1.5
|
entity.canBeBounced.mult = entity.canBeBounced.mult * 1.5
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param entity { mass: number }
|
---@param entity { mass: number }
|
||||||
function Effects.makeFloatier(entity)
|
function Effects.makeFloatier(entity)
|
||||||
entity.mass = entity.mass * 0.75
|
entity.mass = entity.mass * 0.75
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Cause the given ingredient to spawn more frequently
|
||||||
|
function Effects.moreOfIngredient(ingredient, spawner)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Each of the given ingredient will have a *mult score from now on.
|
||||||
|
function Effects.multiplyIngredientValue(ingredient, mult, spawner)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Each ingredient *multiplies* score in some way, in addition to adding.
|
||||||
|
function Effects.multiplicativeIngredientValue(ingredient, addedMult, spawner)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
Cheese = {}
|
|
||||||
|
|
||||||
local sizeX, sizeY = CheeseSprite:getSize()
|
|
||||||
local size = { x = sizeX, y = sizeY }
|
|
||||||
local expireWhenOffScreenBy = { x = 2000, y = 480 }
|
|
||||||
|
|
||||||
local canBounce = {
|
|
||||||
flat = { x = 50, y = 390 },
|
|
||||||
mult = { x = 0.7, y = -0.5 },
|
|
||||||
}
|
|
||||||
|
|
||||||
function Cheese.initialize(o, x, y)
|
|
||||||
o.score = 5
|
|
||||||
o.canBeCollidedBy = 1
|
|
||||||
o.expireAfterCollision = true
|
|
||||||
o.size = size
|
|
||||||
o.position = { x = x, y = y }
|
|
||||||
o.drawAsSprite = CheeseSprite
|
|
||||||
o.collectable = o.drawAsSprite
|
|
||||||
o.expireWhenOffScreenBy = expireWhenOffScreenBy
|
|
||||||
o.canBounce = canBounce
|
|
||||||
newestBooster = o
|
|
||||||
return newestBooster
|
|
||||||
end
|
|
|
@ -1,8 +1,3 @@
|
||||||
import("ingredients/cheese.lua")
|
|
||||||
import("ingredients/lettuce.lua")
|
|
||||||
import("ingredients/mushroom.lua")
|
|
||||||
import("ingredients/tomato.lua")
|
|
||||||
|
|
||||||
---@class
|
---@class
|
||||||
Ingredients = {}
|
Ingredients = {}
|
||||||
|
|
||||||
|
@ -16,7 +11,7 @@ end
|
||||||
|
|
||||||
local newestIngredient = {}
|
local newestIngredient = {}
|
||||||
function Ingredients.newestIngredient()
|
function Ingredients.newestIngredient()
|
||||||
assert(newestIngredient.position ~= nil, "All ingredients are implicitly required to have a position component.")
|
-- assert(newestIngredient.position ~= nil, "All ingredients are implicitly required to have a position component.")
|
||||||
return newestIngredient
|
return newestIngredient
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -24,7 +19,7 @@ function Ingredients.cacheSize()
|
||||||
return maxCache
|
return maxCache
|
||||||
end
|
end
|
||||||
|
|
||||||
function Ingredients.getNext(x, y)
|
function Ingredients.nextInCache()
|
||||||
local index = _ingredientCacheIndex
|
local index = _ingredientCacheIndex
|
||||||
_ingredientCacheIndex = _ingredientCacheIndex + 1
|
_ingredientCacheIndex = _ingredientCacheIndex + 1
|
||||||
if _ingredientCacheIndex >= maxCache then
|
if _ingredientCacheIndex >= maxCache then
|
||||||
|
@ -35,16 +30,13 @@ function Ingredients.getNext(x, y)
|
||||||
for key in pairs(o) do
|
for key in pairs(o) do
|
||||||
o[key] = nil
|
o[key] = nil
|
||||||
end
|
end
|
||||||
newestIngredient = o
|
|
||||||
|
|
||||||
local odds = math.random()
|
newestIngredient = o
|
||||||
if odds > 0.3 then
|
return o
|
||||||
return Lettuce.initialize(o, x, y)
|
end
|
||||||
elseif odds > 0.2 then
|
|
||||||
return Tomato.initialize(o, x, y)
|
function Ingredients.getFirst()
|
||||||
elseif odds > 0.05 then
|
local ret = Ingredients.nextInCache()
|
||||||
return Mushroom.initialize(o, x, 218 + math.random(1, 5))
|
ret.position = { x = 0, y = 0 }
|
||||||
else
|
return ret
|
||||||
return Cheese.initialize(o, x, y)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
Lettuce = {}
|
|
||||||
|
|
||||||
local sizeX, sizeY = LettuceSprite:getSize()
|
|
||||||
local size = { x = sizeX, y = sizeY / 2 }
|
|
||||||
local expireWhenOffScreenBy = { x = 2000, y = 480 }
|
|
||||||
|
|
||||||
local canBounce = {
|
|
||||||
flat = { x = 22, y = 190 },
|
|
||||||
mult = { x = 1, y = -0.5 },
|
|
||||||
}
|
|
||||||
|
|
||||||
function Lettuce.initialize(o, x, y)
|
|
||||||
o.score = 1
|
|
||||||
o.canBeCollidedBy = 1
|
|
||||||
o.expireAfterCollision = true
|
|
||||||
o.size = size
|
|
||||||
o.position = { x = x, y = y }
|
|
||||||
o.drawAsSprite = LettuceSprite
|
|
||||||
o.collectable = o.drawAsSprite
|
|
||||||
o.expireWhenOffScreenBy = expireWhenOffScreenBy
|
|
||||||
o.canBounce = canBounce
|
|
||||||
newestBooster = o
|
|
||||||
return newestBooster
|
|
||||||
end
|
|
|
@ -1,24 +0,0 @@
|
||||||
Mushroom = {}
|
|
||||||
|
|
||||||
local sizeX, sizeY = MushroomSprite:getSize()
|
|
||||||
local size = { x = sizeX, y = sizeY }
|
|
||||||
local expireWhenOffScreenBy = { x = 2000, y = 480 }
|
|
||||||
|
|
||||||
local canBounce = {
|
|
||||||
flat = { x = 0, y = 290 },
|
|
||||||
mult = { x = 1, y = -1 },
|
|
||||||
}
|
|
||||||
|
|
||||||
function Mushroom.initialize(o, x, y)
|
|
||||||
o.score = 5
|
|
||||||
o.canBeCollidedBy = 1
|
|
||||||
o.expireAfterCollision = true
|
|
||||||
o.size = size
|
|
||||||
o.position = { x = x, y = y }
|
|
||||||
o.drawAsSprite = MushroomSprite
|
|
||||||
o.collectable = o.drawAsSprite
|
|
||||||
o.expireWhenOffScreenBy = expireWhenOffScreenBy
|
|
||||||
o.canBounce = canBounce
|
|
||||||
newestBooster = o
|
|
||||||
return newestBooster
|
|
||||||
end
|
|
|
@ -1,18 +0,0 @@
|
||||||
Tomato = {}
|
|
||||||
|
|
||||||
local sizeX, sizeY = TomatoSprite:getSize()
|
|
||||||
local size = { x = sizeX, y = sizeY }
|
|
||||||
local expireWhenOffScreenBy = { x = 2000, y = 480 }
|
|
||||||
|
|
||||||
function Tomato.initialize(o, x, y)
|
|
||||||
o.score = 15
|
|
||||||
o.canBeCollidedBy = 1
|
|
||||||
o.expireAfterCollision = true
|
|
||||||
o.size = size
|
|
||||||
o.position = { x = x, y = y }
|
|
||||||
o.drawAsSprite = TomatoSprite
|
|
||||||
o.collectable = o.drawAsSprite
|
|
||||||
o.expireWhenOffScreenBy = expireWhenOffScreenBy
|
|
||||||
newestBooster = o
|
|
||||||
return newestBooster
|
|
||||||
end
|
|
31
src/main.lua
31
src/main.lua
|
@ -17,6 +17,7 @@ import("systems/collision-resolution.lua")
|
||||||
import("systems/draw.lua")
|
import("systems/draw.lua")
|
||||||
import("systems/gravity.lua")
|
import("systems/gravity.lua")
|
||||||
import("systems/rounds.lua")
|
import("systems/rounds.lua")
|
||||||
|
import("systems/spawner.lua")
|
||||||
import("systems/velocity.lua")
|
import("systems/velocity.lua")
|
||||||
import("ingredients/ingredients.lua")
|
import("ingredients/ingredients.lua")
|
||||||
import("cart.lua")
|
import("cart.lua")
|
||||||
|
@ -41,14 +42,6 @@ floor = {
|
||||||
sparkOnCollision = true,
|
sparkOnCollision = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
Camera = {
|
|
||||||
--- The upper-left corner of the Camera is represented by this `pan` value
|
|
||||||
pan = {
|
|
||||||
x = 0,
|
|
||||||
y = 0,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
Score = {
|
Score = {
|
||||||
points = 0,
|
points = 0,
|
||||||
}
|
}
|
||||||
|
@ -67,6 +60,7 @@ world = tiny.world(
|
||||||
fallSystem,
|
fallSystem,
|
||||||
velocitySystem,
|
velocitySystem,
|
||||||
roundSystem,
|
roundSystem,
|
||||||
|
spawnerSystem,
|
||||||
collectedEntities,
|
collectedEntities,
|
||||||
collidingEntities,
|
collidingEntities,
|
||||||
collisionResolution,
|
collisionResolution,
|
||||||
|
@ -77,16 +71,9 @@ world = tiny.world(
|
||||||
floor,
|
floor,
|
||||||
cart
|
cart
|
||||||
)
|
)
|
||||||
|
addAllSpawners(world)
|
||||||
|
|
||||||
local ingredientsEveryX = 60
|
world:addEntity(Ingredients.getFirst())
|
||||||
|
|
||||||
local function init()
|
|
||||||
for i = 1, Ingredients.cacheSize() do
|
|
||||||
world:addEntity(Ingredients.getNext(i * ingredientsEveryX + (math.random(0, 20)), math.random(50, 200)))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
init()
|
|
||||||
|
|
||||||
-- TODO: Re-enable when cart stops
|
-- TODO: Re-enable when cart stops
|
||||||
playdate.setAutoLockDisabled(true)
|
playdate.setAutoLockDisabled(true)
|
||||||
|
@ -97,15 +84,7 @@ function playdate.update()
|
||||||
gfx.clear(gfx.kColorWhite)
|
gfx.clear(gfx.kColorWhite)
|
||||||
playdate.drawFPS(5, 5)
|
playdate.drawFPS(5, 5)
|
||||||
|
|
||||||
local newestX = Ingredients.newestIngredient().position.x
|
floor.position.x = Camera.pan.x - 600
|
||||||
local panX = Camera.pan.x
|
|
||||||
local rightEdge = panX + 400
|
|
||||||
local offset = 600
|
|
||||||
while newestX < rightEdge + 100 do
|
|
||||||
newestX = newestX + ingredientsEveryX
|
|
||||||
world:addEntity(Ingredients.getNext(newestX, math.random(-500, 150)))
|
|
||||||
end
|
|
||||||
floor.position.x = Camera.pan.x - offset
|
|
||||||
|
|
||||||
world:update(deltaSeconds)
|
world:update(deltaSeconds)
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,13 @@ local gfx <const> = playdate.graphics
|
||||||
|
|
||||||
local focusPriority = {}
|
local focusPriority = {}
|
||||||
|
|
||||||
|
Camera = {
|
||||||
|
pan = {
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
cameraPanSystem = filteredSystem({ focusPriority = T.number, position = T.XyPair }, function(e, dt)
|
cameraPanSystem = filteredSystem({ focusPriority = T.number, position = T.XyPair }, function(e, dt)
|
||||||
if e.focusPriority >= focusPriority.priority then
|
if e.focusPriority >= focusPriority.priority then
|
||||||
focusPriority.position = e.position
|
focusPriority.position = e.position
|
||||||
|
@ -14,7 +21,7 @@ function cameraPanSystem:preProcess()
|
||||||
end
|
end
|
||||||
|
|
||||||
function cameraPanSystem:postProcess()
|
function cameraPanSystem:postProcess()
|
||||||
local panX = math.max(0, focusPriority.position.x - 200)
|
Camera.pan.x = math.max(0, focusPriority.position.x - 200)
|
||||||
local panY = math.min(0, focusPriority.position.y - 120)
|
Camera.pan.y = math.min(0, focusPriority.position.y - 120)
|
||||||
gfx.setDrawOffset(-panX, -panY)
|
gfx.setDrawOffset(-Camera.pan.x, -Camera.pan.y)
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,6 +16,8 @@ local XyPair = { x = 1, y = 1 }
|
||||||
|
|
||||||
---@alias RoundStateAction "end" | "start"
|
---@alias RoundStateAction "end" | "start"
|
||||||
|
|
||||||
|
---@alias CanSpawn { entity: Entity }
|
||||||
|
|
||||||
T = {
|
T = {
|
||||||
XyPair = XyPair,
|
XyPair = XyPair,
|
||||||
bool = true,
|
bool = true,
|
||||||
|
@ -44,7 +46,9 @@ T = {
|
||||||
---@type pd_image
|
---@type pd_image
|
||||||
PdImage = {},
|
PdImage = {},
|
||||||
---@type RoundStateAction
|
---@type RoundStateAction
|
||||||
RoundStateAction = "start"
|
RoundStateAction = "start",
|
||||||
|
---@type CanSpawn
|
||||||
|
CanSpawn = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
---@generic T
|
---@generic T
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
local odds = 0
|
||||||
|
|
||||||
|
---@type { canSpawn: CanSpawn }
|
||||||
|
local selectedSpawner
|
||||||
|
|
||||||
|
spawnerSystem = filteredSystem({ canSpawn = T.CanSpawn, odds = T.number }, function(spawner, _, system)
|
||||||
|
if odds <= 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
odds = odds - spawner.odds
|
||||||
|
if odds <= 0 then
|
||||||
|
selectedSpawner = spawner
|
||||||
|
else
|
||||||
|
selectedSpawner = selectedSpawner or spawner
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
--- process() and postProcess() are skipped when preProcess() returns true
|
||||||
|
function spawnerSystem:preProcess()
|
||||||
|
local newestX = Ingredients.newestIngredient().position.x
|
||||||
|
local panX = Camera.pan.x
|
||||||
|
local rightEdge = panX + 400
|
||||||
|
|
||||||
|
if newestX < rightEdge + 100 then
|
||||||
|
odds = math.random()
|
||||||
|
selectedSpawner = nil
|
||||||
|
return false -- A new ingredient needs spawning!
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We do not need a new ingredient at this time
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Currently spawns AT MOST one new ingredient per frame, which is probably not enough at high speeds!
|
||||||
|
function spawnerSystem:postProcess()
|
||||||
|
local newestX = Ingredients.newestIngredient().position.x
|
||||||
|
local newlySpawned = Ingredients.nextInCache()
|
||||||
|
|
||||||
|
-- TODO: If performance becomes an issue, maybe just swap out __index
|
||||||
|
for k, v in pairs(selectedSpawner.canSpawn.entity) do
|
||||||
|
newlySpawned[k] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: May not need to include lower spawners when we reach higher altitudes.
|
||||||
|
local panY = math.floor(Camera.pan.y or 0)
|
||||||
|
local yRange = selectedSpawner.canSpawn.yRange or { top = panY - 240, bottom = panY + 220 }
|
||||||
|
newlySpawned.position = { x = newestX + 60, y = math.random(yRange.top, yRange.bottom) }
|
||||||
|
|
||||||
|
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)
|
||||||
|
local sizeX, sizeY = sprite:getSize()
|
||||||
|
local size = { x = sizeX, y = sizeY / 2 }
|
||||||
|
|
||||||
|
world:addEntity({
|
||||||
|
name = name,
|
||||||
|
odds = spawnerOdds,
|
||||||
|
canSpawn = {
|
||||||
|
yRange = yRange,
|
||||||
|
entity = {
|
||||||
|
score = score,
|
||||||
|
canBeCollidedBy = 1,
|
||||||
|
expireAfterCollision = true,
|
||||||
|
size = size,
|
||||||
|
drawAsSprite = sprite,
|
||||||
|
collectable = sprite,
|
||||||
|
expireWhenOffScreenBy = expireWhenOffScreenBy,
|
||||||
|
canBounce = canBounce,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
addCollectableSpawner("Lettuce", 0.7, 1, LettuceSprite, {
|
||||||
|
flat = { x = 22, y = 190 },
|
||||||
|
mult = { x = 1, y = -0.5 },
|
||||||
|
})
|
||||||
|
|
||||||
|
addCollectableSpawner("Tomato", 0.1, 15, TomatoSprite)
|
||||||
|
|
||||||
|
addCollectableSpawner("Mushroom", 0.7, 5, MushroomSprite, {
|
||||||
|
flat = { x = 0, y = 290 },
|
||||||
|
mult = { x = 1, y = -1 },
|
||||||
|
}, { top = 219, bottom = 223 })
|
||||||
|
|
||||||
|
addCollectableSpawner("Cheese", 0.05, 1, CheeseSprite, {
|
||||||
|
flat = { x = 50, y = 390 },
|
||||||
|
mult = { x = 0.7, y = -0.5 },
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function getAvailableSpawnerUpgrades()
|
||||||
|
local upgrades = {}
|
||||||
|
for _, spawner in pairs(spawnSystem.entities) do
|
||||||
|
if spawner.hasUpgradeSpeed then
|
||||||
|
upgrades[#upgrades + 1] = { hasUpgradeSpeed = spawner.hasUpgradeSpeed }
|
||||||
|
end
|
||||||
|
if spawner.hasUpgradeValue then
|
||||||
|
upgrades[#upgrades + 1] = { hasUpgradeValue = spawner.hasUpgradeValue }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return upgrades
|
||||||
|
end
|
Loading…
Reference in New Issue