diff --git a/assets/images/DiceFive.png b/assets/images/DiceFive.png new file mode 100644 index 0000000..e15f317 Binary files /dev/null and b/assets/images/DiceFive.png differ diff --git a/assets/images/DiceFour.png b/assets/images/DiceFour.png new file mode 100644 index 0000000..509a398 Binary files /dev/null and b/assets/images/DiceFour.png differ diff --git a/assets/images/DiceOne.png b/assets/images/DiceOne.png new file mode 100644 index 0000000..ecfbdef Binary files /dev/null and b/assets/images/DiceOne.png differ diff --git a/assets/images/DiceSix.png b/assets/images/DiceSix.png new file mode 100644 index 0000000..ddbc5ab Binary files /dev/null and b/assets/images/DiceSix.png differ diff --git a/assets/images/DiceThree.png b/assets/images/DiceThree.png new file mode 100644 index 0000000..89c285c Binary files /dev/null and b/assets/images/DiceThree.png differ diff --git a/assets/images/DiceTwo.png b/assets/images/DiceTwo.png new file mode 100644 index 0000000..ab9d0c0 Binary files /dev/null and b/assets/images/DiceTwo.png differ diff --git a/components.lua b/components.lua new file mode 100644 index 0000000..1d5dd5e --- /dev/null +++ b/components.lua @@ -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 diff --git a/generated/all-systems.lua b/generated/all-systems.lua index 02b06cd..b42ed97 100644 --- a/generated/all-systems.lua +++ b/generated/all-systems.lua @@ -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") diff --git a/generated/assets.lua b/generated/assets.lua index 7d629f6..43a7139 100644 --- a/generated/assets.lua +++ b/generated/assets.lua @@ -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 diff --git a/generated/assets.lua2p b/generated/assets.lua2p index 7b4672a..814adc1 100644 --- a/generated/assets.lua2p +++ b/generated/assets.lua2p @@ -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)) diff --git a/generated/filter-types.lua b/generated/filter-types.lua index 82b5138..2325272 100644 --- a/generated/filter-types.lua +++ b/generated/filter-types.lua @@ -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 @@ -31,6 +32,9 @@ T = { ---@type Collision Collision = SOME_TABLE, + ---@type Drawable + Drawable = SOME_TABLE, + ---@type Entity Entity = SOME_TABLE, diff --git a/generated/filter-types.lua2p b/generated/filter-types.lua2p index 078b475..7718c74 100644 --- a/generated/filter-types.lua2p +++ b/generated/filter-types.lua2p @@ -60,6 +60,7 @@ local SOME_TABLE = {} Collision = "{ collidedInto: Entity, collider: Entity }", BitMask = "number", FontData = "love.FontData", + Drawable = "love.Drawable", KeyState = "table", })) T = { diff --git a/main.lua b/main.lua index d7ed858..5724e62 100644 --- a/main.lua +++ b/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 diff --git a/scenarios.lua b/scenarios.lua new file mode 100644 index 0000000..9d88b5f --- /dev/null +++ b/scenarios.lua @@ -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 diff --git a/systems/animation.lua b/systems/animation.lua new file mode 100644 index 0000000..7c1bb20 --- /dev/null +++ b/systems/animation.lua @@ -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 diff --git a/systems/diceRoll.lua b/systems/diceRoll.lua new file mode 100644 index 0000000..415d76c --- /dev/null +++ b/systems/diceRoll.lua @@ -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) diff --git a/systems/draw.lua b/systems/draw.lua index 01b8f15..0feff85 100644 --- a/systems/draw.lua +++ b/systems/draw.lua @@ -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 diff --git a/systems/scoreDisplay.lua b/systems/scoreDisplay.lua new file mode 100644 index 0000000..0f3f356 --- /dev/null +++ b/systems/scoreDisplay.lua @@ -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) diff --git a/systems/upgradeScorer.lua b/systems/upgradeScorer.lua new file mode 100644 index 0000000..7072c5e --- /dev/null +++ b/systems/upgradeScorer.lua @@ -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)