mirror of
https://github.com/Keyslam-Group/Concord.git
synced 2025-09-02 12:24:11 -04:00
289 lines
7.4 KiB
Lua
289 lines
7.4 KiB
Lua
--- World
|
|
|
|
local PATH = (...):gsub('%.[^%.]+$', '')
|
|
|
|
local Worlds = require(PATH..".worlds")
|
|
local Type = require(PATH..".type")
|
|
local List = require(PATH..".list")
|
|
|
|
local World = {}
|
|
World.__index = World
|
|
|
|
--- Creates a new World.
|
|
-- @return The new World
|
|
function World.new(name)
|
|
if (type(name) ~= "string") then
|
|
error("bad argument #1 to 'World.new' (string expected, got "..type(name)..")", 2)
|
|
end
|
|
|
|
local world = setmetatable({
|
|
entities = List(),
|
|
systems = List(),
|
|
|
|
events = {},
|
|
|
|
__added = {},
|
|
__removed = {},
|
|
|
|
__systemLookup = {},
|
|
|
|
__name = name,
|
|
__isWorld = true,
|
|
}, World)
|
|
|
|
Worlds.register(name, world)
|
|
|
|
return world
|
|
end
|
|
|
|
--- Adds an Entity to the World.
|
|
-- @param e The Entity to add
|
|
-- @return self
|
|
function World:addEntity(e)
|
|
if not Type.isEntity(e) then
|
|
error("bad argument #1 to 'World:addEntity' (Entity expected, got "..type(e)..")", 2)
|
|
end
|
|
|
|
if e.world then
|
|
error("bad argument #1 to 'World:addEntity' (Entity was already added to a world)", 2)
|
|
end
|
|
|
|
e.world = self
|
|
e.__wasAdded = true
|
|
|
|
self.entities:add(e)
|
|
|
|
return self
|
|
end
|
|
|
|
--- Removes an entity from the World.
|
|
-- @param e The Entity to mark
|
|
-- @return self
|
|
function World:removeEntity(e)
|
|
if not Type.isEntity(e) then
|
|
error("bad argument #1 to 'World:removeEntity' (Entity expected, got "..type(e)..")", 2)
|
|
end
|
|
|
|
e.__wasRemoved = true
|
|
|
|
return self
|
|
end
|
|
|
|
-- @return self
|
|
function World:flush()
|
|
local e
|
|
for i = 1, self.entities.size do
|
|
e = self.entities:get(i)
|
|
|
|
if e.__isDirty then
|
|
e:__flush()
|
|
|
|
if (not e.__wasAdded) then -- The __wasAdded check below will handle this instead
|
|
for j = 1, self.systems.size do
|
|
self.systems:get(j):__evaluate(e)
|
|
end
|
|
end
|
|
|
|
e.__isDirty = false
|
|
end
|
|
|
|
if e.__wasAdded then
|
|
e.__wasAdded = false
|
|
|
|
for j = 1, self.systems.size do
|
|
self.systems:get(j):__evaluate(e)
|
|
end
|
|
|
|
self:onEntityAdded(e)
|
|
end
|
|
|
|
if e.__wasRemoved then
|
|
e.world = nil
|
|
self.entities:remove(e)
|
|
|
|
for j = 1, self.systems.size do
|
|
self.systems:get(j):__remove(e)
|
|
end
|
|
|
|
e.__wasRemoved = false
|
|
|
|
self:onEntityRemoved(e)
|
|
end
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
function World:hasSystem(baseSystem)
|
|
if not Type.isBaseSystem(baseSystem) then
|
|
error("bad argument #1 to 'World:getSystem' (baseSystem expected, got "..type(baseSystem)..")", 2)
|
|
end
|
|
|
|
return self.__systemLookup[baseSystem] and true or false
|
|
end
|
|
|
|
function World:getSystem(baseSystem)
|
|
if not Type.isBaseSystem(baseSystem) then
|
|
error("bad argument #1 to 'World:getSystem' (baseSystem expected, got "..type(baseSystem)..")", 2)
|
|
end
|
|
|
|
return self.__systemLookup[baseSystem]
|
|
end
|
|
|
|
--- Adds a System to the World.
|
|
-- @param baseSystem The BaseSystem of the system to add
|
|
-- @param callbackName The callbackName to register to
|
|
-- @param callback The function name to call. Defaults to callbackName
|
|
-- @param enabled If the system is enabled. Defaults to true
|
|
-- @return self
|
|
function World:addSystem(baseSystem, callbackName, callback, enabled)
|
|
if not Type.isBaseSystem(baseSystem) then
|
|
error("bad argument #1 to 'World:addSystem' (baseSystem expected, got "..type(baseSystem)..")", 2)
|
|
end
|
|
|
|
local system = self.__systemLookup[baseSystem]
|
|
if (not system) then
|
|
-- System was not created for this world yet, so we create it ourselves
|
|
system = baseSystem(self)
|
|
|
|
self.__systemLookup[baseSystem] = system
|
|
|
|
self.systems:add(system)
|
|
end
|
|
|
|
-- Retroactively evaluate all entities for this system
|
|
for i = 1, self.entities.size do
|
|
system:__evaluate(self.entities:get(i))
|
|
end
|
|
|
|
if callbackName then
|
|
self.events[callbackName] = self.events[callbackName] or {}
|
|
|
|
local i = #self.events[callbackName] + 1
|
|
self.events[callbackName][i] = {
|
|
system = system,
|
|
callback = callback or callbackName,
|
|
enabled = enabled == nil or enabled,
|
|
}
|
|
|
|
if enabled == nil or enabled then
|
|
system:enabledCallback(callback or callbackName)
|
|
end
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Enables a System in the World.
|
|
-- @param system The System to enable
|
|
-- @param callbackName The Event it was registered to
|
|
-- @param callback The callback it was registered with. Defaults to eventName
|
|
-- @return self
|
|
function World:enableSystem(system, callbackName, callback)
|
|
if not Type.isSystem(system) then
|
|
error("bad argument #1 to 'World:enableSystem' (System expected, got "..type(system)..")", 2)
|
|
end
|
|
|
|
return self:setSystem(system, callbackName, callback, true)
|
|
end
|
|
|
|
--- Disables a System in the World.
|
|
-- @param system The System to disable
|
|
-- @param eventName The Event it was registered to
|
|
-- @param callback The callback it was registered with. Defaults to eventName
|
|
-- @return self
|
|
function World:disableSystem(system, callbackName, callback)
|
|
if not Type.isSystem(system) then
|
|
error("bad argument #1 to 'World:disableSystem' (System expected, got "..type(system)..")", 2)
|
|
end
|
|
|
|
return self:setSystem(system, callbackName, callback, false)
|
|
end
|
|
|
|
--- Sets a System 'enable' in the World.
|
|
-- @param system The System to set
|
|
-- @param eventName The Event it was registered to
|
|
-- @param callback The callback it was registered with. Defaults to eventName
|
|
-- @param enable The state to set it to
|
|
-- @return self
|
|
function World:setSystem(system, callbackName, callback, enable)
|
|
if not Type.isSystem(system) then
|
|
error("bad argument #1 to 'World:setSystem' (System expected, got "..type(system)..")", 2)
|
|
end
|
|
|
|
callback = callback or callbackName
|
|
|
|
if callback then
|
|
local listeners = self.events[callbackName]
|
|
|
|
if listeners then
|
|
for i = 1, #listeners do
|
|
local listener = listeners[i]
|
|
|
|
if listener.system == system and listener.callback == callback then
|
|
if enable and not listener.enabled then
|
|
system:enabledCallback(callback)
|
|
elseif not enable and listener.enabled then
|
|
system:disabledCallback(callback)
|
|
end
|
|
|
|
listener.enabled = enable
|
|
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Emits an Event in the World.
|
|
-- @param eventName The Event that should be emitted
|
|
-- @param ... Parameters passed to listeners
|
|
-- @return self
|
|
function World:emit(callbackName, ...)
|
|
if not callbackName or type(callbackName) ~= "string" then
|
|
error("bad argument #1 to 'World:emit' (String expected, got "..type(callbackName)..")")
|
|
end
|
|
|
|
local listeners = self.events[callbackName]
|
|
|
|
if listeners then
|
|
for i = 1, #listeners do
|
|
local listener = listeners[i]
|
|
|
|
if listener.enabled then
|
|
listener.system[listener.callback](listener.system, ...)
|
|
end
|
|
end
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Removes all entities from the World
|
|
-- @return self
|
|
function World:clear()
|
|
for i = 1, self.entities.size do
|
|
self.removeEntity(self.entities[i])
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
--- Default callback for adding an Entity.
|
|
-- @param e The Entity that was added
|
|
function World:onEntityAdded(e) -- luacheck: ignore
|
|
end
|
|
|
|
--- Default callback for removing an Entity.
|
|
-- @param e The Entity that was removed
|
|
function World:onEntityRemoved(e) -- luacheck: ignore
|
|
end
|
|
|
|
return setmetatable(World, {
|
|
__call = function(_, ...)
|
|
return World.new(...)
|
|
end,
|
|
})
|