diff --git a/ecs/ecs.can b/ecs/ecs.can index b0c8648..afb531b 100644 --- a/ecs/ecs.can +++ b/ecs/ecs.can @@ -1,7 +1,7 @@ --- ubiquitousse.ecs -- Optional dependency: ubiquitousse.scene, to allow quick creation of ECS-based scenes. -local loaded, newScene = pcall(require, (...):match("^(.-)ecs").."scene") -if not loaded then newScene = nil end +local loaded, scene = pcall(require, (...):match("^(.-)ecs").."scene") +if not loaded then scene = nil end --- Entity Component System library, inspired by the excellent tiny-ecs. Main differences include: -- * ability to nest systems; @@ -268,70 +268,72 @@ let recCallOnAddToWorld = (world, systems) end end ---- Create and returns a world system based on a list of systems. --- The systems will be instancied for this world. -let world = (...) - let world = setmetatable({ - filter = (e) return true end, - s = {} - }, { __index = system_mt }) - world.world = world - world.systems = recInstanciateSystems(world, {...}) - recCallOnAddToWorld(world, world.systems) - return world -end - ---- Returns a filter that returns true if, for every argument, a field with the same name exists in the entity. -let all = (...) - let l = {...} - return function(s, e) - for _, k in ipairs(l) do - if e[k] == nil then - return false - end - end - return true - end -end - ---- Returns a filter that returns true if one of the arguments if the name of a field in the entity. -let any = (...) - let l = {...} - return function(s, e) - for _, k in ipairs(l) do - if e[k] ~= nil then - return true - end - end - return false - end -end - -let scene = (name, systems={}, entities={}) - let s = newScene(name) - let w - - function s:enter() - w = world(unpack(systems)) - w:add(unpack(entities)) - end - function s:exit() - w:destroy() - end - function s:update(dt) - w:update(dt) - end - function s:draw() - w:draw() - end - - return s -end - --- ECS module. -return { - world = world, - all = all, - any = any, - scene = scene +let ecs = { + --- Create and returns a world system based on a list of systems. + -- The systems will be instancied for this world. + -- @impl ubiquitousse + world = (...) + let world = setmetatable({ + filter = (e) return true end, + s = {} + }, { __index = system_mt }) + world.world = world + world.systems = recInstanciateSystems(world, {...}) + recCallOnAddToWorld(world, world.systems) + return world + end, + + --- Returns a filter that returns true if, for every argument, a field with the same name exists in the entity. + -- @impl ubiquitousse + all = (...) + let l = {...} + return function(s, e) + for _, k in ipairs(l) do + if e[k] == nil then + return false + end + end + return true + end + end, + + --- Returns a filter that returns true if one of the arguments if the name of a field in the entity. + -- @impl ubiquitousse + any = (...) + let l = {...} + return function(s, e) + for _, k in ipairs(l) do + if e[k] ~= nil then + return true + end + end + return false + end + end, + + --- If uqt.scene is available, returns a new scene that will consist of a ECS world with the specified systems and entities. + -- @impl ubiquitousse + scene = (name, systems={}, entities={}) + let s = scene.new(name) + let w + + function s:enter() + w = ecs.world(unpack(systems)) + w:add(unpack(entities)) + end + function s:exit() + w:destroy() + end + function s:update(dt) + w:update(dt) + end + function s:draw() + w:draw() + end + + return s + end } + +return ecs diff --git a/input/input.lua b/input/input.lua index 403cc19..472de08 100644 --- a/input/input.lua +++ b/input/input.lua @@ -501,6 +501,7 @@ input = { -- identifier, preceded by a % : for example "gamepad.axis.1.leftx%-0.5" should return true when the left-stick of the first gamepad is moved to the right -- by more of 50%. The negative threshold value means that the button will be pressed only when the axis has a negative value (in the example, it won't be -- pressed when the axis is moved to the right). + -- Buttons can also be defined by a list of buttons (string or functions), in which case the button will be considered down if all the buttons are down. --- Makes a new button detector from a identifier string. -- The function may error if the identifier is incorrect. @@ -509,7 +510,7 @@ input = { -- @impl backend basicButtonDetector = function(str) end, - --- Make a new button detector from a detector function of string. + --- Make a new button detector from a detector function, string, or list of buttons. -- @tparam string, function button identifier -- @impl ubiquitousse buttonDetector = function(obj) @@ -517,6 +518,19 @@ input = { return obj elseif type(obj) == "string" then return input.basicButtonDetector(obj) + elseif type(obj) == "table" then + local l = {} + for _, b in ipairs(obj) do + table.insert(l, input.buttonDetector(b)) + end + return function() + for _, b in ipairs(l) do + if not b() then + return false + end + end + return true + end end error(("Not a valid button detector: %s"):format(obj)) end, diff --git a/scene/scene.lua b/scene/scene.lua index de6525e..48d6c62 100644 --- a/scene/scene.lua +++ b/scene/scene.lua @@ -26,7 +26,7 @@ if not loaded then timer = nil end -- * all scene change callbacks are called after setting scene.current to the new scene but before changing scene.stack -- * all scene exit/suspend callbacks are called before scene enter/resume callbacks local scene -scene = setmetatable({ +scene = { --- The current scene table. -- @impl ubiquitousse current = nil, @@ -34,6 +34,9 @@ scene = setmetatable({ --- Shortcut for scene.current.timer. -- @impl ubiquitousse timer = nil, + --- Shortcut for scene.current.signal. + -- @impl ubiquitousse + signal = nil, --- The scene stack: list of scene, from the farest one to the nearest. -- @impl ubiquitousse @@ -77,6 +80,7 @@ scene = setmetatable({ name = name or "unamed", -- The scene name. timer = timer and timer.new(), -- Scene-specific TimerRegistry, if uqt.time is available. + signal = signal and signal.new(), -- Scene-specific SignalRegistry, if uqt.signal is available. enter = function(self, ...) end, -- Called when entering a scene. exit = function(self) end, -- Called when exiting a scene, and not expecting to come back (scene may be unloaded). @@ -101,8 +105,12 @@ scene = setmetatable({ local previous = scene.current scene.current = type(scenePath) == "string" and scene.load(scene.prefix..scenePath) or scenePath scene.timer = scene.current.timer + scene.signal = scene.current.signal scene.current.name = scene.current.name or tostring(scenePath) - if previous then previous:exit() end + if previous then + previous:exit() + if timer then previous.timer:clear() end + end scene.current:enter(...) scene.stack[math.max(#scene.stack, 1)] = scene.current end, @@ -118,6 +126,7 @@ scene = setmetatable({ local previous = scene.current scene.current = type(scenePath) == "string" and scene.load(scene.prefix..scenePath) or scenePath scene.timer = scene.current.timer + scene.signal = scene.current.signal scene.current.name = scene.current.name or tostring(scenePath) if previous then previous:suspend() end scene.current:enter(...) @@ -131,8 +140,12 @@ scene = setmetatable({ pop = function() local previous = scene.current scene.current = scene.stack[#scene.stack-1] - scene.timer = scene.current.timer - if previous then previous:exit() end + scene.timer = scene.current and scene.current.timer or nil + scene.signal = scene.current and scene.current.signal or nil + if previous then + previous:exit() + if timer then previous.timer:clear() end + end if scene.current then scene.current:resume() end table.remove(scene.stack) end, @@ -164,12 +177,7 @@ scene = setmetatable({ draw = function(...) if scene.current then scene.current:draw(...) end end -}, { - --- scene(...) is a shortcut for scene.new(...) - __call = function(self, ...) - return scene.new(...) - end -}) +} -- Bind signals if signal then