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 = {}
|
||||
|
||||
---@param entity { canBeBounced: CanBeBounced }
|
||||
function Effects.makeBouncier(entity)
|
||||
entity.canBeBounced.mult = entity.canBeBounced.mult * 1.5
|
||||
end
|
||||
|
||||
---@param entity { mass: number }
|
||||
function Effects.makeFloatier(entity)
|
||||
entity.mass = entity.mass * 0.75
|
||||
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
|
||||
Ingredients = {}
|
||||
|
||||
|
@ -16,7 +11,7 @@ end
|
|||
|
||||
local 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
|
||||
end
|
||||
|
||||
|
@ -24,7 +19,7 @@ function Ingredients.cacheSize()
|
|||
return maxCache
|
||||
end
|
||||
|
||||
function Ingredients.getNext(x, y)
|
||||
function Ingredients.nextInCache()
|
||||
local index = _ingredientCacheIndex
|
||||
_ingredientCacheIndex = _ingredientCacheIndex + 1
|
||||
if _ingredientCacheIndex >= maxCache then
|
||||
|
@ -35,16 +30,13 @@ function Ingredients.getNext(x, y)
|
|||
for key in pairs(o) do
|
||||
o[key] = nil
|
||||
end
|
||||
newestIngredient = o
|
||||
|
||||
local odds = math.random()
|
||||
if odds > 0.3 then
|
||||
return Lettuce.initialize(o, x, y)
|
||||
elseif odds > 0.2 then
|
||||
return Tomato.initialize(o, x, y)
|
||||
elseif odds > 0.05 then
|
||||
return Mushroom.initialize(o, x, 218 + math.random(1, 5))
|
||||
else
|
||||
return Cheese.initialize(o, x, y)
|
||||
end
|
||||
newestIngredient = o
|
||||
return o
|
||||
end
|
||||
|
||||
function Ingredients.getFirst()
|
||||
local ret = Ingredients.nextInCache()
|
||||
ret.position = { x = 0, y = 0 }
|
||||
return ret
|
||||
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/gravity.lua")
|
||||
import("systems/rounds.lua")
|
||||
import("systems/spawner.lua")
|
||||
import("systems/velocity.lua")
|
||||
import("ingredients/ingredients.lua")
|
||||
import("cart.lua")
|
||||
|
@ -41,14 +42,6 @@ floor = {
|
|||
sparkOnCollision = true,
|
||||
}
|
||||
|
||||
Camera = {
|
||||
--- The upper-left corner of the Camera is represented by this `pan` value
|
||||
pan = {
|
||||
x = 0,
|
||||
y = 0,
|
||||
},
|
||||
}
|
||||
|
||||
Score = {
|
||||
points = 0,
|
||||
}
|
||||
|
@ -67,6 +60,7 @@ world = tiny.world(
|
|||
fallSystem,
|
||||
velocitySystem,
|
||||
roundSystem,
|
||||
spawnerSystem,
|
||||
collectedEntities,
|
||||
collidingEntities,
|
||||
collisionResolution,
|
||||
|
@ -77,16 +71,9 @@ world = tiny.world(
|
|||
floor,
|
||||
cart
|
||||
)
|
||||
addAllSpawners(world)
|
||||
|
||||
local ingredientsEveryX = 60
|
||||
|
||||
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()
|
||||
world:addEntity(Ingredients.getFirst())
|
||||
|
||||
-- TODO: Re-enable when cart stops
|
||||
playdate.setAutoLockDisabled(true)
|
||||
|
@ -97,15 +84,7 @@ function playdate.update()
|
|||
gfx.clear(gfx.kColorWhite)
|
||||
playdate.drawFPS(5, 5)
|
||||
|
||||
local newestX = Ingredients.newestIngredient().position.x
|
||||
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
|
||||
floor.position.x = Camera.pan.x - 600
|
||||
|
||||
world:update(deltaSeconds)
|
||||
|
||||
|
|
|
@ -2,6 +2,13 @@ local gfx <const> = playdate.graphics
|
|||
|
||||
local focusPriority = {}
|
||||
|
||||
Camera = {
|
||||
pan = {
|
||||
x = 0,
|
||||
y = 0,
|
||||
},
|
||||
}
|
||||
|
||||
cameraPanSystem = filteredSystem({ focusPriority = T.number, position = T.XyPair }, function(e, dt)
|
||||
if e.focusPriority >= focusPriority.priority then
|
||||
focusPriority.position = e.position
|
||||
|
@ -14,7 +21,7 @@ function cameraPanSystem:preProcess()
|
|||
end
|
||||
|
||||
function cameraPanSystem:postProcess()
|
||||
local panX = math.max(0, focusPriority.position.x - 200)
|
||||
local panY = math.min(0, focusPriority.position.y - 120)
|
||||
gfx.setDrawOffset(-panX, -panY)
|
||||
Camera.pan.x = math.max(0, focusPriority.position.x - 200)
|
||||
Camera.pan.y = math.min(0, focusPriority.position.y - 120)
|
||||
gfx.setDrawOffset(-Camera.pan.x, -Camera.pan.y)
|
||||
end
|
||||
|
|
|
@ -16,6 +16,8 @@ local XyPair = { x = 1, y = 1 }
|
|||
|
||||
---@alias RoundStateAction "end" | "start"
|
||||
|
||||
---@alias CanSpawn { entity: Entity }
|
||||
|
||||
T = {
|
||||
XyPair = XyPair,
|
||||
bool = true,
|
||||
|
@ -44,7 +46,9 @@ T = {
|
|||
---@type pd_image
|
||||
PdImage = {},
|
||||
---@type RoundStateAction
|
||||
RoundStateAction = "start"
|
||||
RoundStateAction = "start",
|
||||
---@type CanSpawn
|
||||
CanSpawn = {}
|
||||
}
|
||||
|
||||
---@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