generated from sage/tiny-ecs-love-template
Start sketching out how scoring might work.
Import some fixes from tiny-ecs-love-template. Add some simple sprites for dice. Sketch out some basic bus/slot logic. Implement simple sprite animation system.
This commit is contained in:
parent
ab655c1b8b
commit
cb7c3ac779
Binary file not shown.
After Width: | Height: | Size: 687 B |
Binary file not shown.
After Width: | Height: | Size: 631 B |
Binary file not shown.
After Width: | Height: | Size: 497 B |
Binary file not shown.
After Width: | Height: | Size: 650 B |
Binary file not shown.
After Width: | Height: | Size: 621 B |
Binary file not shown.
After Width: | Height: | Size: 567 B |
|
@ -0,0 +1,33 @@
|
|||
local components = {}
|
||||
|
||||
---@param entity Entity
|
||||
function components.allowedInEngine(entity)
|
||||
entity.allowedInEngine = T.marker
|
||||
end
|
||||
|
||||
---@param entity Entity
|
||||
function components.allowedInWheels(entity)
|
||||
entity.allowedInWheels = T.marker
|
||||
end
|
||||
|
||||
---@param entity Entity
|
||||
function components.allowedInPropulsion(entity)
|
||||
entity.allowedInPropulsion = T.marker
|
||||
end
|
||||
|
||||
---@param entity Entity
|
||||
function components.allowedInSeating(entity)
|
||||
entity.allowedInSeating = T.marker
|
||||
end
|
||||
|
||||
---@param entity Entity
|
||||
function components.allowedInStyling(entity)
|
||||
entity.allowedInStyling = T.marker
|
||||
end
|
||||
|
||||
---@param entity Entity
|
||||
function components.allowedInBrakes(entity)
|
||||
entity.allowedInBrakes = T.marker
|
||||
end
|
||||
|
||||
return components
|
|
@ -1,8 +1,12 @@
|
|||
require("../systems/animation")
|
||||
require("../systems/camera-pan")
|
||||
require("../systems/collision-detection")
|
||||
require("../systems/collision-resolution")
|
||||
require("../systems/decay")
|
||||
require("../systems/diceRoll")
|
||||
require("../systems/draw")
|
||||
require("../systems/gravity")
|
||||
require("../systems/input")
|
||||
require("../systems/scoreDisplay")
|
||||
require("../systems/upgradeScorer")
|
||||
require("../systems/velocity")
|
||||
|
|
|
@ -1,11 +1,35 @@
|
|||
-- GENERATED FILE - DO NOT EDIT
|
||||
-- Instead, edit the source file directly: assets.lua2p.
|
||||
|
||||
-- luacheck: ignore
|
||||
---@type love.Texture
|
||||
DiceFive = love.graphics.newImage("assets/images/DiceFive.png")
|
||||
|
||||
-- luacheck: ignore
|
||||
---@type love.Texture
|
||||
DiceFour = love.graphics.newImage("assets/images/DiceFour.png")
|
||||
|
||||
-- luacheck: ignore
|
||||
---@type love.Texture
|
||||
DiceOne = love.graphics.newImage("assets/images/DiceOne.png")
|
||||
|
||||
-- luacheck: ignore
|
||||
---@type love.Texture
|
||||
DiceSix = love.graphics.newImage("assets/images/DiceSix.png")
|
||||
|
||||
-- luacheck: ignore
|
||||
---@type love.Texture
|
||||
DiceThree = love.graphics.newImage("assets/images/DiceThree.png")
|
||||
|
||||
-- luacheck: ignore
|
||||
---@type love.Texture
|
||||
DiceTwo = love.graphics.newImage("assets/images/DiceTwo.png")
|
||||
|
||||
|
||||
|
||||
|
||||
-- luacheck: ignore
|
||||
---@type FontData
|
||||
---@type fun(fontSize: number | nil): love.Font
|
||||
EtBt7001Z0xa = function(fontSize)
|
||||
return love.graphics.newFont("assets/fonts/EtBt7001Z0xa.ttf", fontSize)
|
||||
end
|
||||
|
|
|
@ -25,9 +25,11 @@ function generatedFileWarning()
|
|||
return "-- GENERATED FILE - DO NOT EDIT\n-- Instead, edit the source file directly: assets.lua2p."
|
||||
end)!!(generatedFileWarning())
|
||||
|
||||
!!(dirLookup('assets/images', 'png', 'love.graphics.newImage', 'Image'))
|
||||
!!(dirLookup('assets/sounds', 'wav', 'love.sound.newSoundData', 'SoundData'))
|
||||
!!(dirLookup('assets/music', 'wav', 'love.sound.newSoundData', 'SoundData'))
|
||||
!!(dirLookup('assets/fonts', 'ttf', 'love.graphics.newFont', 'FontData', function(varName, newFunc, file)
|
||||
!!(dirLookup('assets/images', 'png', 'love.graphics.newImage', 'love.Texture'))
|
||||
!!(dirLookup('assets/sounds', 'ogg', 'love.audio.newSource', 'love.Source', function(varName, newFunc, file)
|
||||
return varName .. ' = ' .. newFunc .. '("' .. file .. '", "static")'
|
||||
end))
|
||||
!!(dirLookup('assets/music', 'wav', 'love.sound.newSoundData', 'love.SoundData'))
|
||||
!!(dirLookup('assets/fonts', 'ttf', 'love.graphics.newFont', 'fun(fontSize: number | nil): love.Font', function(varName, newFunc, file)
|
||||
return varName .. ' = function(fontSize)\n return ' .. newFunc .. '("' .. file .. '", fontSize)\nend'
|
||||
end))
|
||||
|
|
|
@ -8,6 +8,7 @@ local SOME_TABLE = {}
|
|||
---@alias AnyComponent any
|
||||
---@alias BitMask number
|
||||
---@alias Collision { collidedInto: Entity, collider: Entity }
|
||||
---@alias Drawable love.Drawable
|
||||
---@alias Entity table
|
||||
---@alias FontData love.FontData
|
||||
---@alias KeyState table<string, boolean>
|
||||
|
@ -31,6 +32,9 @@ T = {
|
|||
---@type Collision
|
||||
Collision = SOME_TABLE,
|
||||
|
||||
---@type Drawable
|
||||
Drawable = SOME_TABLE,
|
||||
|
||||
---@type Entity
|
||||
Entity = SOME_TABLE,
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ local SOME_TABLE = {}
|
|||
Collision = "{ collidedInto: Entity, collider: Entity }",
|
||||
BitMask = "number",
|
||||
FontData = "love.FontData",
|
||||
Drawable = "love.Drawable",
|
||||
KeyState = "table<string, boolean>",
|
||||
}))
|
||||
T = {
|
||||
|
|
56
main.lua
56
main.lua
|
@ -4,42 +4,38 @@ require("generated/filter-types")
|
|||
require("generated/assets")
|
||||
require("generated/all-systems")
|
||||
|
||||
UiZIndex = math.huge
|
||||
|
||||
local smallFont = EtBt7001Z0xa(12)
|
||||
|
||||
local scenarios = require("scenarios")
|
||||
|
||||
local world = require("world")
|
||||
|
||||
local scenarios = {
|
||||
default = function()
|
||||
-- TODO: Add default entities
|
||||
end,
|
||||
textTestScenario = function()
|
||||
world:addEntity({
|
||||
position = { x = 0, y = 600 },
|
||||
drawAsText = {
|
||||
text = "Hello, world!",
|
||||
style = TextStyle.Inverted,
|
||||
},
|
||||
velocity = { x = 240, y = -500 },
|
||||
mass = 1,
|
||||
decayAfterSeconds = 10,
|
||||
})
|
||||
end,
|
||||
}
|
||||
|
||||
local currentScenario = scenarios.textTestScenario
|
||||
local currentScenario = scenarios.default
|
||||
local freeze = false
|
||||
local delta
|
||||
|
||||
local playerBus
|
||||
|
||||
function love.load()
|
||||
currentScenario()
|
||||
love.graphics.setBackgroundColor(1, 1, 1)
|
||||
playerBus = currentScenario(world)
|
||||
love.graphics.setBackgroundColor(0, 0, 0)
|
||||
love.graphics.setFont(EtBt7001Z0xa(32))
|
||||
end
|
||||
|
||||
local nth = 1
|
||||
|
||||
world:addEntity({
|
||||
triggerDiceRoll = "",
|
||||
})
|
||||
|
||||
function love.update(dt)
|
||||
delta = dt
|
||||
|
||||
if love.keyboard.isDown("r") then
|
||||
world:clearEntities()
|
||||
currentScenario()
|
||||
playerBus = currentScenario(world)
|
||||
freeze = false
|
||||
end
|
||||
|
||||
|
@ -51,6 +47,18 @@ function love.update(dt)
|
|||
return
|
||||
end
|
||||
|
||||
-- if nth == 3 then
|
||||
-- world:addEntity({
|
||||
-- scoringTrigger = {
|
||||
-- mask = 1,
|
||||
-- target = playerBus,
|
||||
-- },
|
||||
-- })
|
||||
-- nth = 1
|
||||
-- else
|
||||
-- nth = nth + 1
|
||||
-- end
|
||||
|
||||
world:update(delta, function(_, system)
|
||||
if system.deferToEnd then
|
||||
return false
|
||||
|
@ -60,6 +68,10 @@ function love.update(dt)
|
|||
end
|
||||
|
||||
function love.draw()
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
love.graphics.setFont(smallFont)
|
||||
love.graphics.print("FPS: "..tostring(love.timer.getFPS( )), 10, 10)
|
||||
|
||||
world:update(delta, function(_, system)
|
||||
if system.deferToEnd then
|
||||
return false
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
local scenarios = {}
|
||||
|
||||
local width, height = love.graphics.getWidth(), love.graphics.getHeight()
|
||||
local bigFont = EtBt7001Z0xa(96)
|
||||
local bigHeight = bigFont:getHeight()
|
||||
|
||||
local midFont = EtBt7001Z0xa(32)
|
||||
local midHeight = midFont:getHeight()
|
||||
|
||||
function BitAt(n)
|
||||
return math.floor(2 ^ (n - 1))
|
||||
end
|
||||
|
||||
---@param world World
|
||||
function scenarios.default(world)
|
||||
-- TODO: Add default entities
|
||||
local busText = "BUS"
|
||||
local textWidth = bigFont:getWidth(busText)
|
||||
|
||||
local playerScoreDisplay = world:addEntity({
|
||||
position = { x = width - 100, y = 20 },
|
||||
z = UiZIndex,
|
||||
drawAsText = {
|
||||
text = "0",
|
||||
style = TextStyle.Inverted,
|
||||
font = midFont,
|
||||
},
|
||||
})
|
||||
|
||||
local bus = world:addEntity({
|
||||
position = { x = 400, y = (height - bigHeight) / 2 },
|
||||
drawAsText = {
|
||||
font = bigFont,
|
||||
text = busText,
|
||||
style = TextStyle.Inverted,
|
||||
},
|
||||
score = 0,
|
||||
scoreDisplay = playerScoreDisplay,
|
||||
slots = {},
|
||||
})
|
||||
|
||||
local slotNum = 0
|
||||
local function buildSlot(text, otherEntityData)
|
||||
otherEntityData.position = { x = 20, y = 40 + (slotNum * midHeight * 1.2) }
|
||||
otherEntityData.drawAsText = {
|
||||
font = midFont,
|
||||
text = text,
|
||||
style = TextStyle.Inverted,
|
||||
}
|
||||
otherEntityData.upgrades = {}
|
||||
|
||||
bus.slots[#bus.slots + 1] = world:addEntity(otherEntityData)
|
||||
slotNum = slotNum + 1
|
||||
return otherEntityData
|
||||
end
|
||||
local wheels = buildSlot("Wheels", {
|
||||
scoredOn = BitAt(1),
|
||||
})
|
||||
wheels.upgrades[#wheels.upgrades + 1] = {
|
||||
flatScore = 1,
|
||||
}
|
||||
buildSlot("Propulsion", {
|
||||
scoredOn = BitAt(2),
|
||||
})
|
||||
buildSlot("Passengers", {
|
||||
scoredOn = BitAt(3),
|
||||
})
|
||||
buildSlot("Style", {
|
||||
scoredOn = BitAt(4),
|
||||
})
|
||||
buildSlot("Brakes", {
|
||||
scoredOn = BitAt(5),
|
||||
})
|
||||
buildSlot("Engine", {
|
||||
scoredOn = BitAt(6),
|
||||
})
|
||||
|
||||
return bus
|
||||
end
|
||||
|
||||
return scenarios
|
|
@ -0,0 +1,41 @@
|
|||
local world = require("world")
|
||||
|
||||
local function getCurrentTimeMilliseconds()
|
||||
return love.timer.getTime() * 1000
|
||||
end
|
||||
|
||||
local Animation = {
|
||||
animation = {
|
||||
initialMs = T.number,
|
||||
msPerFrame = T.number,
|
||||
frames = Arr({ drawable = T.Drawable }),
|
||||
},
|
||||
position = T.XyPair,
|
||||
drawAsSprite = T.Drawable
|
||||
}
|
||||
|
||||
---@return { initialMs: number, frames: Entity[], msPerFrame: number }
|
||||
function BuildAnimation(frames, msPerFrame)
|
||||
return {
|
||||
initialMs = getCurrentTimeMilliseconds(),
|
||||
frames = frames,
|
||||
msPerFrame = msPerFrame,
|
||||
}
|
||||
end
|
||||
|
||||
local currentMs = getCurrentTimeMilliseconds()
|
||||
|
||||
local animation = world:filteredSystem("animation", Animation, function(e, _, system)
|
||||
local nextFrameIndex = 1 + math.floor((currentMs - e.animation.initialMs) / e.animation.msPerFrame)
|
||||
if nextFrameIndex > #e.animation.frames then
|
||||
e.animation = nil
|
||||
system.world:addEntity(e)
|
||||
else
|
||||
e.drawAsSprite = e.animation.frames[nextFrameIndex]
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
function animation:preProcess()
|
||||
currentMs = getCurrentTimeMilliseconds()
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
local world = require("world")
|
||||
local width, height = love.graphics.getWidth(), love.graphics.getHeight()
|
||||
|
||||
local dice = {
|
||||
DiceOne,
|
||||
DiceTwo,
|
||||
DiceThree,
|
||||
DiceFour,
|
||||
DiceFive,
|
||||
DiceSix,
|
||||
}
|
||||
|
||||
world:filteredSystem("dice", { triggerDiceRoll = T.marker }, function(e, dt, system)
|
||||
system.world:removeEntity(e)
|
||||
local frames = {}
|
||||
for _ = 1, 20 do
|
||||
frames[#frames + 1] = dice[math.random(#dice)]
|
||||
end
|
||||
system.world:addEntity({
|
||||
position = {
|
||||
x = 20,
|
||||
y = height - DiceOne:getHeight() - 20,
|
||||
},
|
||||
drawAsSprite = dice[math.random(#dice)],
|
||||
animation = BuildAnimation(frames, 100)
|
||||
})
|
||||
end)
|
|
@ -40,7 +40,13 @@ drawSystem(
|
|||
"drawText",
|
||||
{ position = T.XyPair, drawAsText = { text = T.str, style = Maybe(T.str), font = Maybe(T.FontData) } },
|
||||
function(e)
|
||||
local font = e.drawAsText.font or gfx.getFont() -- e.drawAsText.font or AshevilleSans14Bold
|
||||
local font
|
||||
if e.drawAsText.font ~= nil then
|
||||
font = e.drawAsText.font
|
||||
gfx.setFont(font)
|
||||
else
|
||||
font = gfx.getFont()
|
||||
end
|
||||
local textHeight = font:getHeight()
|
||||
local textWidth = font:getWidth(e.drawAsText.text)
|
||||
|
||||
|
@ -49,14 +55,14 @@ drawSystem(
|
|||
local bgWidth, bgHeight = textWidth + (margin * 2), textHeight + 2
|
||||
|
||||
if e.drawAsText.style == TextStyle.Inverted then
|
||||
gfx.setColor(0, 0, 0)
|
||||
gfx.setColor(1, 1, 1)
|
||||
gfx.rectangle("fill", bgLeftEdge, bgTopEdge, textWidth + margin, textHeight + 2)
|
||||
gfx.setColor(1, 1, 1)
|
||||
gfx.setColor(0, 0, 0)
|
||||
elseif e.drawAsText.style == TextStyle.Bordered then
|
||||
gfx.setColor(1, 1, 1)
|
||||
gfx.setColor(0, 0, 0)
|
||||
gfx.rectangle("fill", bgLeftEdge, bgTopEdge, bgWidth, bgHeight)
|
||||
|
||||
gfx.setColor(0, 0, 0)
|
||||
gfx.setColor(1, 1, 1)
|
||||
gfx.rectangle("line", bgLeftEdge, bgTopEdge, bgWidth, bgHeight)
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
local world = require("world")
|
||||
local width, height = love.graphics.getWidth(), love.graphics.getHeight()
|
||||
local midFont = EtBt7001Z0xa(32)
|
||||
|
||||
local AddScore = {
|
||||
addingScoreTo = {
|
||||
score = T.number,
|
||||
scoreDisplay = Maybe({ drawAsText = { text = T.str } }),
|
||||
},
|
||||
scoreToAdd = T.number,
|
||||
}
|
||||
|
||||
local z = 1
|
||||
|
||||
world:filteredSystem("score", { addScore = AddScore }, function(e, _, system)
|
||||
system.world:removeEntity(e)
|
||||
local target, scoreToAdd = e.addScore.addingScoreTo, e.addScore.scoreToAdd
|
||||
target.score = target.score + scoreToAdd
|
||||
|
||||
if target.scoreDisplay ~= nil then
|
||||
target.scoreDisplay.drawAsText.text = "" .. target.score
|
||||
system.world:addEntity({
|
||||
z = z,
|
||||
drawAsText = {
|
||||
text = "+" .. scoreToAdd,
|
||||
style = TextStyle.Inverted,
|
||||
font = midFont,
|
||||
},
|
||||
position = {
|
||||
x = target.scoreDisplay.position.x,
|
||||
y = target.scoreDisplay.position.y,
|
||||
},
|
||||
velocity = { x = 100 * (math.random() - 0.5), y = -100 },
|
||||
mass = 1,
|
||||
decayAfterSeconds = 3,
|
||||
})
|
||||
-- Prevents z-fighting of falling score updates
|
||||
z = (z + 1) % 1000
|
||||
end
|
||||
end)
|
|
@ -0,0 +1,42 @@
|
|||
local world = require("world")
|
||||
local width, height = love.graphics.getWidth(), love.graphics.getHeight()
|
||||
|
||||
---@class ScoringTarget
|
||||
local ScoringTarget = {
|
||||
slots = Arr({
|
||||
upgrades = Arr(T.Entity),
|
||||
}),
|
||||
score = T.number,
|
||||
}
|
||||
|
||||
local ScoringTrigger = {
|
||||
mask = T.number,
|
||||
target = ScoringTarget,
|
||||
}
|
||||
|
||||
--- Probably need a way to access the total so far.
|
||||
---@param w World
|
||||
---@param target ScoringTarget
|
||||
local function addSlotScores(w, target, slotIndex)
|
||||
local upgrades = target.slots[slotIndex].upgrades
|
||||
for i, upgrade in ipairs(upgrades) do
|
||||
if upgrade.flatScore ~= nil then
|
||||
w:addEntity({
|
||||
addScore = {
|
||||
addingScoreTo = target,
|
||||
scoreToAdd = upgrade.flatScore,
|
||||
},
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
world:filteredSystem("upgradeScorer", { scoringTrigger = ScoringTrigger }, function(e, _, system)
|
||||
system.world:removeEntity(e)
|
||||
local target, mask = e.scoringTrigger.target, e.scoringTrigger.mask
|
||||
for i, slot in ipairs(target.slots) do
|
||||
if bit.band(BitAt(i), mask) ~= 0 then
|
||||
addSlotScores(system.world, target, i)
|
||||
end
|
||||
end
|
||||
end)
|
Loading…
Reference in New Issue