Drop available upgrades after ingredient stack.
***Temporarily*** use timers to defer ingredient spawn This should be more component-y for sure.
This commit is contained in:
parent
f570a4a966
commit
94b391801d
|
@ -310,6 +310,7 @@ end
|
|||
-- Use an empty table as a key for identifying Systems. Any table that contains
|
||||
-- this key is considered a System rather than an Entity.
|
||||
local systemTableKey = { "SYSTEM_TABLE_KEY" }
|
||||
tiny.SKIP_PROCESS = { "SKIP_PROCESS_KEY" }
|
||||
|
||||
-- Checks if a table is a System.
|
||||
local function isSystem(table)
|
||||
|
@ -322,12 +323,12 @@ local function processingSystemUpdate(system, dt)
|
|||
local process = system.process
|
||||
local postProcess = system.postProcess
|
||||
|
||||
local shouldSkipSystemProcess = false
|
||||
local shouldSkipSystemProcess
|
||||
if preProcess then
|
||||
shouldSkipSystemProcess = preProcess(system, dt)
|
||||
end
|
||||
|
||||
if process and not shouldSkipSystemProcess then
|
||||
if process and shouldSkipSystemProcess ~= tiny.SKIP_PROCESS then
|
||||
if system.nocache then
|
||||
local entities = system.world.entities
|
||||
local filter = system.filter
|
||||
|
@ -347,7 +348,7 @@ local function processingSystemUpdate(system, dt)
|
|||
end
|
||||
end
|
||||
|
||||
if postProcess and not shouldSkipSystemProcess then
|
||||
if postProcess and shouldSkipSystemProcess ~= tiny.SKIP_PROCESS then
|
||||
postProcess(system, dt)
|
||||
end
|
||||
end
|
||||
|
@ -474,6 +475,8 @@ end
|
|||
--- Adds an Entity to the world.
|
||||
-- Also call this on Entities that have changed Components such that they
|
||||
-- match different Filters. Returns the Entity.
|
||||
-- TODO: Track entity age when debugging?
|
||||
-- TODO: Track debugName field when debugging?
|
||||
function tiny.addEntity(world, entity)
|
||||
local e2c = world.entitiesToChange
|
||||
e2c[#e2c + 1] = entity
|
||||
|
|
|
@ -9,8 +9,8 @@ function Cart.reset(o)
|
|||
y = 50,
|
||||
}
|
||||
o.velocity = {
|
||||
x = 10 + (150 * math.random()),
|
||||
y = 150 * (math.random() - 1),
|
||||
x = 200 + (100 * math.random()),
|
||||
y = 175 * (math.random() - 1),
|
||||
}
|
||||
o.size = size
|
||||
o.canBeBounced = {
|
||||
|
@ -37,7 +37,6 @@ function Cart.reset(o)
|
|||
position = { x = self.position.x, y = self.position.y },
|
||||
focusPriority = 1,
|
||||
})
|
||||
|
||||
end
|
||||
|
||||
return o
|
||||
|
|
|
@ -13,16 +13,10 @@ function Effects.makeFloatier(entity)
|
|||
end
|
||||
|
||||
--- Cause the given ingredient to spawn more frequently
|
||||
function Effects.moreOfIngredient(ingredient, spawner)
|
||||
|
||||
end
|
||||
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
|
||||
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
|
||||
function Effects.multiplicativeIngredientValue(ingredient, addedMult, spawner) end
|
||||
|
|
|
@ -16,11 +16,13 @@ import("systems/collision-detection.lua")
|
|||
import("systems/collision-resolution.lua")
|
||||
import("systems/draw.lua")
|
||||
import("systems/gravity.lua")
|
||||
import("systems/move-toward.lua")
|
||||
import("systems/rounds.lua")
|
||||
import("systems/spawner.lua")
|
||||
import("systems/velocity.lua")
|
||||
import("ingredients/ingredients.lua")
|
||||
import("cart.lua")
|
||||
import("utils.lua")
|
||||
|
||||
local tiny <const> = tiny
|
||||
local gfx <const> = playdate.graphics
|
||||
|
@ -58,9 +60,11 @@ end
|
|||
|
||||
world = tiny.world(
|
||||
fallSystem,
|
||||
moveTowardSystem,
|
||||
velocitySystem,
|
||||
roundSystem,
|
||||
spawnerSystem,
|
||||
expireBelowScreenSystem,
|
||||
collectedEntities,
|
||||
collidingEntities,
|
||||
collisionResolution,
|
||||
|
@ -68,6 +72,7 @@ world = tiny.world(
|
|||
cameraPanSystem,
|
||||
drawRectanglesSystem,
|
||||
drawSpriteSystem,
|
||||
drawTextSystem,
|
||||
floor,
|
||||
cart
|
||||
)
|
||||
|
|
|
@ -9,6 +9,8 @@ Camera = {
|
|||
},
|
||||
}
|
||||
|
||||
expireBelowScreenSystem = filteredSystem({ position = T.XyPair, expireBelowScreenBy = T.number })
|
||||
|
||||
cameraPanSystem = filteredSystem({ focusPriority = T.number, position = T.XyPair }, function(e, dt)
|
||||
if e.focusPriority >= focusPriority.priority then
|
||||
focusPriority.position = e.position
|
||||
|
@ -24,4 +26,11 @@ function cameraPanSystem:postProcess()
|
|||
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)
|
||||
|
||||
for _, entity in pairs(expireBelowScreenSystem.entities) do
|
||||
if entity.position.y - (Camera.pan.y + 240) > entity.expireBelowScreenBy then
|
||||
print("Entity expired - was too far below screen!")
|
||||
self.world:removeEntity(entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,7 +26,7 @@ collisionResolution = filteredSystem({ collisionBetween = T.Collision }, functio
|
|||
position = {
|
||||
x = collider.position.x,
|
||||
y = collider.position.y,
|
||||
}
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -55,25 +55,5 @@ collisionResolution = filteredSystem({ collisionBetween = T.Collision }, functio
|
|||
system.world:addEntity({ collected = collidedInto.collectable })
|
||||
end
|
||||
|
||||
-- if collidedInto.sparkOnCollision and collider.sparkOnCollision and (math.abs(collider.velocity.x) + math.abs(collider.velocity.y)) > 2 then
|
||||
-- local size = { x = 1, y = 1 }
|
||||
-- local spark = {
|
||||
-- position = {
|
||||
-- x = collider.position.x,
|
||||
-- y = collider.position.y + collider.size.y - 2,
|
||||
-- },
|
||||
-- velocity = {
|
||||
-- x = -collider.velocity.x,
|
||||
-- y = 150,
|
||||
-- },
|
||||
-- size = size,
|
||||
-- canCollideWith = 1,
|
||||
-- mass = 1,
|
||||
-- drawAsSprite = Spark,
|
||||
-- expireAfterCollision = true,
|
||||
-- }
|
||||
-- system.world:addEntity(spark)
|
||||
-- end
|
||||
|
||||
system.world:removeEntity(e)
|
||||
end)
|
||||
|
|
|
@ -4,6 +4,10 @@ drawRectanglesSystem = filteredSystem({ position = T.XyPair, drawAsRectangle = {
|
|||
gfx.fillRect(e.position.x, e.position.y, e.drawAsRectangle.size.x, e.drawAsRectangle.size.y)
|
||||
end)
|
||||
|
||||
drawTextSystem = filteredSystem({ position = T.XyPair, drawAsText = { text = T.str } }, function(e, dt)
|
||||
gfx.drawTextAligned(e.drawAsText.text, e.position.x, e.position.y, gfx.kAlignCenter)
|
||||
end)
|
||||
|
||||
drawSpriteSystem = filteredSystem({ position = T.XyPair, drawAsSprite = T.PdImage }, function(e, dt, system)
|
||||
e.drawAsSprite:draw(e.position.x, e.position.y)
|
||||
end)
|
||||
|
|
|
@ -48,7 +48,7 @@ T = {
|
|||
---@type RoundStateAction
|
||||
RoundStateAction = "start",
|
||||
---@type CanSpawn
|
||||
CanSpawn = {}
|
||||
CanSpawn = {},
|
||||
}
|
||||
|
||||
---@generic T
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
local MoveToward = {
|
||||
target = T.XyPair,
|
||||
range = T.number,
|
||||
speed = T.number,
|
||||
}
|
||||
|
||||
local sqrt, abs = math.sqrt, math.abs
|
||||
|
||||
---@param xy1 XyPair
|
||||
---@param xy2 XyPair
|
||||
local function normalizeVector(xy1, xy2)
|
||||
local x = xy1.x - xy2.x
|
||||
local y = xy1.y - xy2.y
|
||||
local distance = sqrt((x * x) + (y * y))
|
||||
return x / distance, y / distance, distance
|
||||
end
|
||||
|
||||
moveTowardSystem = filteredSystem(
|
||||
{ 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
|
||||
end
|
||||
|
||||
-- TODO May be incorrect when signs are mismatched between vel and diff
|
||||
local xVel = xNorm * e.moveToward.speed * dt
|
||||
if abs(e.position.x - e.moveToward.target.x) < abs(xVel) then
|
||||
e.position.x = e.moveToward.target.x
|
||||
else
|
||||
e.position.x = e.position.x + xVel
|
||||
end
|
||||
|
||||
local yVel = yNorm * e.moveToward.speed * dt
|
||||
if abs(e.position.y - e.moveToward.target.y) < abs(yVel) then
|
||||
e.position.y = e.moveToward.target.y
|
||||
else
|
||||
e.position.y = e.position.y + yVel
|
||||
end
|
||||
end
|
||||
)
|
|
@ -1,8 +1,11 @@
|
|||
collectedEntities = filteredSystem({ 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 y = e.position.y - 240
|
||||
local rectWidth = 150
|
||||
local plateSize = { x = rectWidth, y = 10 }
|
||||
|
@ -19,25 +22,57 @@ roundSystem = filteredSystem({ roundAction = T.RoundStateAction, position = Mayb
|
|||
stopMovingOnCollision = true,
|
||||
})
|
||||
|
||||
local delayPerDrop = 150
|
||||
local delay = 0
|
||||
for i, collectable in ipairs(collectedEntities.entities) do
|
||||
local collX, collY = collectable.collected:getSize()
|
||||
local onCollidingRemove = {"mass", "velocity", "canCollideWith"}
|
||||
y = y - collY - 15
|
||||
system.world:addEntity({
|
||||
drawAsSprite = collectable.collected,
|
||||
size = { x = collX, y = collY / 2 },
|
||||
mass = 0.5,
|
||||
velocity = { x = 0, y = 0 },
|
||||
position = { x = e.position.x - (collX / 2), y = y },
|
||||
canCollideWith = 2,
|
||||
canBeCollidedBy = 2,
|
||||
isSolid = true,
|
||||
stopMovingOnCollision = true,
|
||||
onCollidingRemove = onCollidingRemove,
|
||||
focusOnCollide = i,
|
||||
})
|
||||
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)
|
||||
delay = delay + delayPerDrop
|
||||
system.world:removeEntity(collectable)
|
||||
end
|
||||
|
||||
local availableUpgrades = Utils.getNDifferentValues(getAvailableSpawnerUpgrades(), 3)
|
||||
|
||||
y = y - 50
|
||||
for _, upgrade in ipairs(availableUpgrades) do
|
||||
printTable(upgrade)
|
||||
local collX, collY = 75, 30
|
||||
y = y - collY - 15
|
||||
playdate.timer.new(delay, function(ee, ccollX, ccollY, yy, ii, ssystem, ccollectable)
|
||||
ssystem.world:addEntity({
|
||||
drawAsText = {
|
||||
text = upgrade.name,
|
||||
},
|
||||
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,
|
||||
})
|
||||
end, e, collX, collY, y, i, system, collectable)
|
||||
delay = delay + delayPerDrop
|
||||
end
|
||||
system.world:removeEntity(e)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
|
|
@ -3,7 +3,7 @@ local odds = 0
|
|||
---@type { canSpawn: CanSpawn }
|
||||
local selectedSpawner
|
||||
|
||||
spawnerSystem = filteredSystem({ canSpawn = T.CanSpawn, odds = T.number }, function(spawner, _, system)
|
||||
spawnerSystem = filteredSystem({ canSpawn = T.CanSpawn, odds = T.number }, function(spawner, _, _)
|
||||
if odds <= 0 then
|
||||
return
|
||||
end
|
||||
|
@ -15,22 +15,28 @@ spawnerSystem = filteredSystem({ canSpawn = T.CanSpawn, odds = T.number }, funct
|
|||
end
|
||||
end)
|
||||
|
||||
--- process() and postProcess() are skipped when preProcess() returns true
|
||||
--- 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()
|
||||
odds = math.random() * totalOdds
|
||||
selectedSpawner = nil
|
||||
return false -- A new ingredient needs spawning!
|
||||
return -- A new ingredient needs spawning!
|
||||
end
|
||||
|
||||
-- We do not need a new ingredient at this time
|
||||
return true
|
||||
return tiny.SKIP_PROCESS
|
||||
end
|
||||
|
||||
local spawnEveryX = 26
|
||||
|
||||
-- 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
|
||||
|
@ -44,7 +50,7 @@ function spawnerSystem:postProcess()
|
|||
-- 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) }
|
||||
newlySpawned.position = { x = newestX + spawnEveryX, y = math.random(yRange.top, yRange.bottom) }
|
||||
|
||||
self.world:addEntity(newlySpawned)
|
||||
end
|
||||
|
@ -58,6 +64,8 @@ function addAllSpawners(world)
|
|||
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 = {
|
||||
|
@ -83,7 +91,7 @@ function addAllSpawners(world)
|
|||
|
||||
addCollectableSpawner("Tomato", 0.1, 15, TomatoSprite)
|
||||
|
||||
addCollectableSpawner("Mushroom", 0.7, 5, MushroomSprite, {
|
||||
addCollectableSpawner("Mushroom", 0.02, 5, MushroomSprite, {
|
||||
flat = { x = 0, y = 290 },
|
||||
mult = { x = 1, y = -1 },
|
||||
}, { top = 219, bottom = 223 })
|
||||
|
@ -94,15 +102,41 @@ function addAllSpawners(world)
|
|||
})
|
||||
end
|
||||
|
||||
---@alias Upgrade { name: string, apply = fun() }
|
||||
|
||||
---@return Upgrade[]
|
||||
function getAvailableSpawnerUpgrades()
|
||||
local upgrades = {}
|
||||
for _, spawner in pairs(spawnSystem.entities) do
|
||||
for _, spawner in pairs(spawnerSystem.entities) do
|
||||
if spawner.hasUpgradeSpeed then
|
||||
upgrades[#upgrades + 1] = { hasUpgradeSpeed = spawner.hasUpgradeSpeed }
|
||||
end
|
||||
if spawner.hasUpgradeValue then
|
||||
upgrades[#upgrades + 1] = { hasUpgradeValue = spawner.hasUpgradeValue }
|
||||
|
||||
if spawner.canSpawn.entity.score then
|
||||
upgrades[#upgrades + 1] = {
|
||||
name = "Double " .. spawner.name .. " value",
|
||||
apply = function()
|
||||
spawner.canSpawn.entity.score = spawner.canSpawn.entity.score * 2
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
assert(spawner.odds, "Expected all spawners to have an `odds` field!")
|
||||
upgrades[#upgrades + 1] = {
|
||||
name = "Double " .. spawner.name .. " frequency",
|
||||
apply = function()
|
||||
spawner.odds = spawner.odds * 2
|
||||
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
|
||||
end
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
Utils = {}
|
||||
|
||||
--- Returns up to `n` random values from the given array. Will return fewer if `n > #fromArr`
|
||||
---@generic T
|
||||
---@param fromArr T[]
|
||||
---@param n number
|
||||
---@generic T[]
|
||||
function Utils.getNDifferentValues(fromArr, n)
|
||||
assert(n >= 0, "n must be a non-negative integer")
|
||||
if n > #fromArr then
|
||||
n = #fromArr
|
||||
end
|
||||
local found = 0
|
||||
local indexes = {}
|
||||
while found < n do
|
||||
local randomIndex = math.random(#fromArr)
|
||||
if not indexes[randomIndex] then
|
||||
found = found + 1
|
||||
indexes[randomIndex] = true
|
||||
end
|
||||
end
|
||||
|
||||
local randoms = {}
|
||||
for i in pairs(indexes) do
|
||||
randoms[#randoms + 1] = fromArr[i]
|
||||
end
|
||||
return randoms
|
||||
end
|
Loading…
Reference in New Issue