luncher/src/systems/spawner.lua

148 lines
4.8 KiB
Lua

local odds = 0
---@type { canSpawn: CanSpawn }
local selectedSpawner
spawnerSystem = filteredSystem("spawner", { canSpawn = T.CanSpawn, odds = T.number }, function(spawner, _, _)
if odds <= 0 then
return
end
odds = odds - spawner.odds
if odds <= 0 then
selectedSpawner = spawner
else
selectedSpawner = selectedSpawner or spawner
end
end)
--- May cause process() and postProcess() to be skipped
function spawnerSystem:preProcess()
local newestX = Ingredients.newestIngredient().position.x
local panX = Camera.pan.x
local rightEdge = panX + 400
local totalOdds = 0
for _, spawner in pairs(spawnerSystem.entities) do
totalOdds = totalOdds + spawner.odds
end
if newestX < rightEdge + 100 then
odds = math.random() * totalOdds
selectedSpawner = nil
return -- A new ingredient needs spawning!
end
-- We do not need a new ingredient at this time
return tiny.SKIP_PROCESS
end
local spawnEveryX = 30
-- 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 + spawnEveryX, y = math.random(yRange.top, yRange.bottom) }
self.world:addEntity(newlySpawned)
end
---@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({
-- NOTE: This name should NOT be used to identify the spawner.
-- It should only be used to supply visual information when searching for available upgrades.
name = name,
odds = spawnerOdds,
canSpawn = {
yRange = yRange,
entity = {
score = score,
canBeCollidedBy = 1,
expireAfterCollision = true,
size = size,
drawAsSprite = sprite,
collectable = sprite,
expireWhenOffScreenBy = expireWhenOffScreenBy,
disableCollisionWhenRoundEnds = T.marker,
canBounce = canBounce,
},
},
})
end
addCollectableSpawner("Lettuce", 0.7, 1, LettuceSprite, {
flat = { x = 12, y = 220 },
mult = { x = 1, y = -0.5 },
})
addCollectableSpawner("Tomato", 0.1, 15, TomatoSprite)
addCollectableSpawner("Mushroom", 0.02, 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
---@alias Upgrade { name: string, apply = fun() }
---@return Upgrade[]
function getAvailableSpawnerUpgrades()
local upgrades = {}
for _, spawner in pairs(spawnerSystem.entities) do
if spawner.hasUpgradeSpeed then
-- upgrades[#upgrades + 1] = { hasUpgradeSpeed = spawner.hasUpgradeSpeed }
end
if spawner.canSpawn.entity.score then
local name = "Double " .. spawner.name .. " value"
upgrades[#upgrades + 1] = {
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 = name,
apply = function(world)
print("Applying " .. name)
spawner.odds = spawner.odds * 2
world:addEntity({ roundAction = "start" })
end,
}
-- if not spawner.canSpawn.entity.velocity then
-- upgrades[#upgrades + 1] = {
-- name = spawner.name .. " Movement",
-- upgrade = function()
-- spawner.canSpawn.entity.velocity = { x = -10, y = 0 }
-- end,
-- }
-- end
end
return upgrades
end