From aca14fbfb0af484d76f841bffdbcf1470cddeef2 Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Wed, 19 Mar 2025 15:27:53 -0400 Subject: [PATCH] Move tiny-tools into tiny.lua directly. Less implicit global var work. The global is still there, but it's explicitly captured in world.lua, removing any weird require-ordering rules. --- lib/tiny.lua | 48 ++++++++++++++++++++++++++++++++ main.lua | 17 +++++------ systems/camera-pan.lua | 41 ++++++++------------------- systems/collision-detection.lua | 10 ++++--- systems/collision-resolution.lua | 4 ++- systems/decay.lua | 8 ++++-- systems/draw.lua | 3 +- systems/gravity.lua | 9 +++--- systems/input.lua | 10 ++++--- systems/velocity.lua | 7 +++-- tiny-debug.lua | 2 +- tiny-tools.lua | 38 ------------------------- tiny-types.lua | 7 +++++ world.lua | 2 ++ 14 files changed, 107 insertions(+), 99 deletions(-) delete mode 100644 tiny-tools.lua create mode 100644 world.lua diff --git a/lib/tiny.lua b/lib/tiny.lua index 4fb3c89..290f938 100644 --- a/lib/tiny.lua +++ b/lib/tiny.lua @@ -905,6 +905,53 @@ function tiny.setSystemIndex(world, system, index) return oldIndex end +---@author Sage Vaillancourt +--- +--- A convenience function for quickly building a well-typed system matching the given shape. +--- Typing does not work when a literal tiny filter is passed as the `shape`, but this function can still be convenient. +---@generic T +---@param name string The `.name` of the system. Shouldn't be used for identifying it in code, but can be helpful when debugging. +---@param shape T | fun() +---@param process fun(entity: T, dt: number, system: System) | nil +---@param compare nil | fun(system: System, entityA: T, entityB: T): boolean If provided, will instead initialize a sortedProcessingSystem. +---@return System | { entities: T[] } +function tiny.filteredSystem(world, name, shape, process, compare) + assert(type(name) == "string") + assert(process == nil or type(process) == "function") + + local system = compare and tiny.sortedProcessingSystem() or tiny.processingSystem() + system.compare = compare + system.name = name + + if type(shape) == "table" then + local keys = {} + for key, value in pairs(shape) do + local isTable = type(value) == "table" + local isMaybe = isTable and value.maybe ~= nil + + if not isMaybe then + -- ^ Don't require any Maybe types + keys[#keys + 1] = key + end + end + system.filter = tiny.requireAll(unpack(keys)) + elseif type(shape) == "function" then + system.filter = shape + else + error("Unknown type for `shape` argument: " .. type(shape)) + end + + if not process then + return world:addSystem(system) + end + + function system:process(e, dt) + process(e, dt, self) + end + + return world:addSystem(system) +end + -- Construct world metatable. worldMetaTable = { __index = { @@ -921,6 +968,7 @@ worldMetaTable = { getEntityCount = tiny.getEntityCount, getSystemCount = tiny.getSystemCount, setSystemIndex = tiny.setSystemIndex, + filteredSystem = tiny.filteredSystem, }, __tostring = function() return "" diff --git a/main.lua b/main.lua index 3b469f4..d7ed858 100644 --- a/main.lua +++ b/main.lua @@ -1,20 +1,17 @@ require("tiny-debug") require("utils") -require("tiny-tools") - -local tiny = require("lib/tiny") -World = tiny.world() - require("generated/filter-types") require("generated/assets") require("generated/all-systems") +local world = require("world") + local scenarios = { default = function() -- TODO: Add default entities end, textTestScenario = function() - World:addEntity({ + world:addEntity({ position = { x = 0, y = 600 }, drawAsText = { text = "Hello, world!", @@ -41,7 +38,7 @@ function love.update(dt) delta = dt if love.keyboard.isDown("r") then - World:clearEntities() + world:clearEntities() currentScenario() freeze = false end @@ -54,7 +51,7 @@ function love.update(dt) return end - World:update(delta, function(_, system) + world:update(delta, function(_, system) if system.deferToEnd then return false end @@ -63,14 +60,14 @@ function love.update(dt) end function love.draw() - World:update(delta, function(_, system) + world:update(delta, function(_, system) if system.deferToEnd then return false end return system.isDrawSystem end) - World:update(delta, function(_, system) + world:update(delta, function(_, system) return system.deferToEnd end) end diff --git a/systems/camera-pan.lua b/systems/camera-pan.lua index 8f38509..99cd43b 100644 --- a/systems/camera-pan.lua +++ b/systems/camera-pan.lua @@ -1,15 +1,18 @@ -Camera = { +local world = require("world") + +local camera = { pan = { x = 0, y = 0, }, } -expireBelowScreenSystem = filteredSystem("expireBelowScreen", { position = T.XyPair, expireBelowScreenBy = T.number }) +local expireBelowScreen = + world:filteredSystem("expireBelowScreen", { position = T.XyPair, expireBelowScreenBy = T.number }) local focusPriority = {} -cameraPanSystem = filteredSystem("cameraPan", { focusPriority = T.number, position = T.XyPair }, function(e, _) +cameraPanSystem = world:filteredSystem("cameraPan", { focusPriority = T.number, position = T.XyPair }, function(e, _) if e.focusPriority >= focusPriority.priority then focusPriority.position = e.position end @@ -21,35 +24,13 @@ function cameraPanSystem.preProcess() end function cameraPanSystem:postProcess() - Camera.pan.x = math.max(0, focusPriority.position.x - 200) - Camera.pan.y = math.min(0, focusPriority.position.y - 120) - -- TODO: set draw offset + camera.pan.x = math.max(0, focusPriority.position.x - 200) + camera.pan.y = math.min(0, focusPriority.position.y - 120) + love.graphics.translate(0, -camera.pan.y) - for _, entity in pairs(expireBelowScreenSystem.entities) do - if entity.position.y - (Camera.pan.y + 240) > entity.expireBelowScreenBy then + for _, entity in pairs(expireBelowScreen.entities) do + if entity.position.y - (camera.pan.y + 240) > entity.expireBelowScreenBy then self.world:removeEntity(entity) end end end - -local cameraTopIsh, cameraBottomIsh - -local enableNearCameraY = filteredSystem( - "enableNearCameraY", - { enableNearCameraY = Arr(T.Entity) }, - function(e, _, system) - if e.position.y > cameraTopIsh and e.position.y < cameraBottomIsh then - for _, enable in ipairs(e.enableNearCameraY) do - enable.velocity = e.velocity - system.world:addEntity(enable) - end - system.world:removeEntity(e) - end - end -) - -local within = 1000 -function enableNearCameraY:preProcess() - cameraTopIsh = Camera.pan.y - within - cameraBottomIsh = Camera.pan.y + 240 + within -end diff --git a/systems/collision-detection.lua b/systems/collision-detection.lua index 47b2203..971f5c9 100644 --- a/systems/collision-detection.lua +++ b/systems/collision-detection.lua @@ -1,4 +1,6 @@ -collidingEntities = filteredSystem("collidingEntitites", { +local world = require("world") + +local collidingEntities = world:filteredSystem("collidingEntitites", { position = T.XyPair, size = T.XyPair, canCollideWith = T.BitMask, @@ -19,14 +21,14 @@ local function intersects(rect, rectOther) return leftOther < right and left < rightOther and topOther < bottom and top < bottomOther end -filteredSystem( +world:filteredSystem( "collisionDetection", { position = T.XyPair, size = T.XyPair, canBeCollidedBy = T.BitMask, isSolid = Maybe(T.bool) }, --- Here, the entity, e, refers to some entity that a moving object may be colliding *into* + -- Here, the entity, e, refers to some entity that a moving object may be colliding *into* function(e, _, system) for _, collider in pairs(collidingEntities.entities) do if - (e ~= collider) + (e ~= collider) and collider.canCollideWith and e.canBeCollidedBy and bit.band(collider.canCollideWith, e.canBeCollidedBy) ~= 0 diff --git a/systems/collision-resolution.lua b/systems/collision-resolution.lua index 4a54df7..aee3f58 100644 --- a/systems/collision-resolution.lua +++ b/systems/collision-resolution.lua @@ -1,4 +1,6 @@ -filteredSystem("collisionResolution", { collisionBetween = T.Collision }, function(e, _, system) +local world = require("world") + +world:filteredSystem("collisionResolution", { collisionBetween = T.Collision }, function(e, _, system) local collidedInto, collider = e.collisionBetween[1], e.collisionBetween[2] system.world:removeEntity(e) end) diff --git a/systems/decay.lua b/systems/decay.lua index c8581e6..efa12ad 100644 --- a/systems/decay.lua +++ b/systems/decay.lua @@ -1,15 +1,17 @@ -filteredSystem("decay", { decayAfterSeconds = T.number }, function(e, dt, system) +local world = require("world") + +world:filteredSystem("decay", { decayAfterSeconds = T.number }, function(e, dt, system) e.decayAfterSeconds = e.decayAfterSeconds - dt if e.decayAfterSeconds <= 0 then system.world:removeEntity(e) end end) -LiveForNFrames = filteredSystem("liveForNFrames", { liveForNFrames = T.number }, function(e, _, system) +LiveForNFrames = world:filteredSystem("liveForNFrames", { liveForNFrames = T.number }, function(e, _, system) e.liveForNFrames = e.liveForNFrames - 1 if e.liveForNFrames <= 0 then system.world:removeEntity(e) end end) -LiveForNFrames.deferToEnd = true \ No newline at end of file +LiveForNFrames.deferToEnd = true diff --git a/systems/draw.lua b/systems/draw.lua index 538e689..01b8f15 100644 --- a/systems/draw.lua +++ b/systems/draw.lua @@ -1,3 +1,4 @@ +local world = require("world") local gfx = love.graphics ---@generic T @@ -5,7 +6,7 @@ local gfx = love.graphics ---@param process fun(entity: T, dt: number, system: System) | nil ---@return System | { entities: T[] } local function drawSystem(name, shape, process) - local system = filteredSystem(name, shape, process, function(_, a, b) + local system = world:filteredSystem(name, shape, process, function(_, a, b) if a.z ~= nil and b.z ~= nil then return a.z < b.z end diff --git a/systems/gravity.lua b/systems/gravity.lua index fbd1879..557709e 100644 --- a/systems/gravity.lua +++ b/systems/gravity.lua @@ -1,16 +1,17 @@ +local world = require("world") local min = math.min -World:addEntity({ gravity = -300 }) +world:addEntity({ gravity = -300 }) -local gravities = filteredSystem("gravities", { gravity = T.number }) +local gravities = world:filteredSystem("gravities", { gravity = T.number }) -filteredSystem("changeGravity", { changeGravityTo = T.number }, function(e, _, _) +world:filteredSystem("changeGravity", { changeGravityTo = T.number }, function(e, _, _) for _, ge in pairs(gravities.entities) do ge.gravity = e.changeGravityTo end end) -filteredSystem("fall", { velocity = T.XyPair, mass = T.number }, function(e, dt) +world:filteredSystem("fall", { velocity = T.XyPair, mass = T.number }, function(e, dt) for _, ge in pairs(gravities.entities) do e.velocity.y = min(400, e.velocity.y - (ge.gravity * dt * e.mass) - (0.5 * dt * dt)) end diff --git a/systems/input.lua b/systems/input.lua index 747f45d..7bffc43 100644 --- a/systems/input.lua +++ b/systems/input.lua @@ -1,7 +1,9 @@ +local world = require("world") + ---@type KeyState local keyState = {} -local keyInputSystem = filteredSystem("keyInput", { canReceiveKeys = T.marker }, function(e, _, system) +local keyInputSystem = world:filteredSystem("keyInput", { canReceiveKeys = T.marker }, function(e, _, system) e.keyState = keyState system.world:addEntity(e) end) @@ -22,7 +24,7 @@ function ClearKeyState() end end -local mouse = filteredSystem("mouse", { mouseKeyPress = { position = T.XyPair, key = T.number } }) +local mouse = world:filteredSystem("mouse", { mouseKeyPress = { position = T.XyPair, key = T.number } }) function MouseJustPressed(key, clear) clear = clear ~= nil and clear or true @@ -30,7 +32,7 @@ function MouseJustPressed(key, clear) if event.mouseKeyPress and event.mouseKeyPress.key == key then if clear then event.mouseKeyPress = nil - World:removeEntity(event) + world:removeEntity(event) end return true end @@ -45,7 +47,7 @@ function love.mousemoved(x, y) end function love.mousepressed(x, y, key) - World:addEntity({ + world:addEntity({ mouseKeyPress = { position = { x = x, diff --git a/systems/velocity.lua b/systems/velocity.lua index 53b1298..9b4207f 100644 --- a/systems/velocity.lua +++ b/systems/velocity.lua @@ -1,6 +1,7 @@ +local world = require("world") local sqrt = math.sqrt -filteredSystem("velocity", { position = T.XyPair, velocity = T.XyPair }, function(e, dt, system) +world:filteredSystem("velocity", { position = T.XyPair, velocity = T.XyPair }, function(e, dt, system) if sqrt((e.velocity.x * e.velocity.x) + (e.velocity.y * e.velocity.y)) < 2 then -- velocity = nil else @@ -9,8 +10,8 @@ filteredSystem("velocity", { position = T.XyPair, velocity = T.XyPair }, functio end end) -filteredSystem("drag", { velocity = T.XyPair, drag = T.number }, function(e, dt, system) +world:filteredSystem("drag", { velocity = T.XyPair, drag = T.number }, function(e, dt, system) local currentDrag = e.drag * dt e.velocity.x = e.velocity.x - (e.velocity.x * currentDrag * dt) e.velocity.y = e.velocity.y - (e.velocity.y * currentDrag * dt) -end) \ No newline at end of file +end) diff --git a/tiny-debug.lua b/tiny-debug.lua index 793663a..c1c0956 100644 --- a/tiny-debug.lua +++ b/tiny-debug.lua @@ -39,4 +39,4 @@ if tinyWarnWhenNonDataOnEntities then return valType end end -end \ No newline at end of file +end diff --git a/tiny-tools.lua b/tiny-tools.lua deleted file mode 100644 index df8c85c..0000000 --- a/tiny-tools.lua +++ /dev/null @@ -1,38 +0,0 @@ -local tiny = require("lib/tiny") - ----@generic T ----@param shape T | fun() ----@param process fun(entity: T, dt: number, system: System) | nil ----@param compare nil | fun(system: System, entityA: T, entityB: T): boolean ----@return System | { entities: T[] } -function filteredSystem(name, shape, process, compare) - assert(type(name) == "string") - assert(type(shape) == "table" or type(shape) == "function") - assert(process == nil or type(process) == "function") - - local system = compare and tiny.sortedProcessingSystem() or tiny.processingSystem() - system.compare = compare - system.name = name - if type(shape) == "table" then - local keys = {} - for key, value in pairs(shape) do - local isTable = type(value) == "table" - local isMaybe = isTable and value.maybe ~= nil - - if not isMaybe then - -- ^ Don't require any Maybe types - keys[#keys + 1] = key - end - end - system.filter = tiny.requireAll(unpack(keys)) - elseif type(shape) == "function" then - system.filter = shape - end - if not process then - return World:addSystem(system) - end - function system:process(e, dt) - process(e, dt, self) - end - return World:addSystem(system) -end diff --git a/tiny-types.lua b/tiny-types.lua index 205f198..1e20f82 100644 --- a/tiny-types.lua +++ b/tiny-types.lua @@ -46,3 +46,10 @@ function World:getSystemCount() end --- the order in which they Systems processed, because lower indexed Systems are --- processed first. Returns the old system.index. function World:setSystemIndex(world, system, index) end + +---@generic T +---@param shape T | fun() +---@param process fun(entity: T, dt: number, system: System) | nil +---@param compare nil | fun(system: System, entityA: T, entityB: T): boolean +---@return System | { entities: T[] } +function World:filteredSystem(name, shape, process, compare) end diff --git a/world.lua b/world.lua new file mode 100644 index 0000000..b1c85cf --- /dev/null +++ b/world.lua @@ -0,0 +1,2 @@ +local tiny = require("lib/tiny") +return tiny.world()