Initial commit

This commit is contained in:
Justin van der Leij 2018-01-25 15:19:31 +01:00
commit 58549d6b0a
14 changed files with 665 additions and 0 deletions

41
fluid/component.lua Normal file
View file

@ -0,0 +1,41 @@
local Component = {}
Component.__index = Component
--- Creates a new Component.
-- @param populate A function that populates the Bag with values
-- @param inherit States if the Bag should inherit the Component's functions
-- @return A Component object
function Component.new(populate, inherit)
local component = setmetatable({
__populate = populate,
__inherit = inherit,
}, Component)
if inherit then
component.__mt = {__index = component}
end
return component
end
-- Creates and initializes a new Bag.
-- @param ... The values passed to the populate function
-- @return A new initialized Bag
function Component:initialize(...)
if self.__populate then
local bag = {}
self.__populate(bag, ...)
if self.__inherit then
setmetatable(bag, self.__mt)
end
return bag
end
return true
end
return setmetatable(Component, {
__call = function(_, ...) return Component.new(...) end,
})

75
fluid/entity.lua Normal file
View file

@ -0,0 +1,75 @@
local Entity = {
entities = {},
}
Entity.__index = Entity
--- Creates and initializes a new Entity.
-- @return A new Entity
function Entity.new()
local e = setmetatable({
id = #Entity.entities + 1,
components = {},
systems = {},
keys = {},
instance = nil,
}, Entity)
Entity.entities[e.id] = e
return e
end
--- Gives an Entity a component with values.
-- @param component The Component to add
-- @param ... The values passed to the Component
-- @return self
function Entity:give(component, ...)
self.components[component] = component:initialize(...)
return self
end
--- Removes a component from an Entity.
-- @param component The Component to remove
-- @return self
function Entity:remove(component)
self.components[component] = nil
return self
end
--- Checks the Entity against the pools again.
-- @return self
function Entity:check()
self.instance:checkEntity(self)
return self
end
--- Removed an Entity from the instance.
-- @return self
function Entity:destroy()
Entity.entities[self.id] = nil
self.instance:destroyEntity(self)
return self
end
--- Gets a Component from the Entity
-- @param component The Component to get
-- @return The Bag from the Component
function Entity:get(component)
return self.components[component]
end
--- Returns true if the Entity has the Component
-- @params component The Component to check against
-- @return True if the entity has the Bag. False otherwise
function Entity:has(component)
return self.components[component] and true
end
return setmetatable(Entity, {
__call = function(_, ...) return Entity.new(...) end,
})

79
fluid/eventManager.lua Normal file
View file

@ -0,0 +1,79 @@
local EventManager = {}
EventManager.__index = EventManager
function EventManager.new()
local eventManager = setmetatable({
queue = {count = 0},
listeners = {},
}, EventManager)
return eventManager
end
function EventManager:push(event)
queue.count = queue.count + 1
queue[queue.count] = event
return self
end
function EventManager:emit(name, ...)
local listeners = self.listeners[name]
if listeners then
for i = 1, #listeners do
local listener = listeners[i]
listener[name](listener, ...)
end
end
return self
end
function EventManager:register(name, listener)
local listeners = self.listeners[name]
if not listeners then
listeners = {count = 0}
self.listeners[name] = listeners
end
listeners.count = listeners.count + 1
listeners[listeners.count] = listener
return self
end
function EventManager:deregister(name, listener)
local listeners = self.listeners[name]
if listeners then
for index, other in ipairs(listeners) do
if listener == other then
table.remove(listeners, index)
listeners.count = listeners.count - 1
return
end
end
end
return self
end
function EventManager:process()
local queue = self.queue
for i = 1, queue.count do
self:emit(queue[i])
queue[i] = nil
end
queue.count = 0
return self
end
return setmetatable(EventManager, {
__call = function(_, ...) return EventManager.new(...) end,
})

82
fluid/init.lua Normal file
View file

@ -0,0 +1,82 @@
local PATH = (...):gsub('%.init$', '')
local Fluid = {}
function Fluid.init(settings)
Fluid.entity = require(PATH..".entity")
Fluid.component = require(PATH..".component")
Fluid.system = require(PATH..".system")
Fluid.instance = require(PATH..".instance")
Fluid.eventManager = require(PATH..".eventManager")
if settings and settings.useEvents then
Fluid.instances = {}
Fluid.addInstance = function(instance)
table.insert(Fluid.instances, instance)
end
Fluid.removeInstance = function(instance)
for i, instance in ipairs(Fluid.instances) do
table.remove(Fluid.instances, i)
break
end
end
love.run = function()
if love.math then
love.math.setRandomSeed(os.time())
love.timer.step()
end
for _, instance in ipairs(Fluid.instances) do
instance:emit("load", arg)
end
if love.timer then love.timer.step() end
local dt = 0
while true do
if love.event then
love.event.pump()
for name, a, b, c, d, e, f in love.event.poll() do
for _, instance in ipairs(Fluid.instances) do
instance:emit(name, a, b, c, d, e, f)
end
if name == "quit" then
return a
end
end
end
if love.timer then
love.timer.step()
dt = love.timer.getDelta()
end
for _, instance in ipairs(Fluid.instances) do
instance:emit("update", dt)
end
if love.graphics and love.graphics.isActive() then
love.graphics.clear(love.graphics.getBackgroundColor())
love.graphics.origin()
for _, instance in ipairs(Fluid.instances) do
instance:emit("draw")
end
love.graphics.present()
end
if love.timer then love.timer.sleep(0.001) end
end
end
end
return Fluid
end
return Fluid

86
fluid/instance.lua Normal file
View file

@ -0,0 +1,86 @@
local PATH = (...):gsub('%.[^%.]+$', '')
local Pool = require(PATH..".pool")
local EventManager = require(PATH..".eventManager")
local Instance = {}
Instance.__index = Instance
function Instance.new()
local instance = setmetatable({
entities = Pool(),
eventManager = EventManager(),
systems = {},
namedSystems = {},
}, Instance)
return instance
end
function Instance:addEntity(e)
e.instance = self
self.entities:add(e)
self:checkEntity(e)
end
function Instance:checkEntity(e)
for _, system in ipairs(self.systems) do
if system:entityUpdated(e) then
e.systems[#e.systems + 1] = system
end
end
end
function Instance:destroyEntity(e)
self.entities:remove(e)
for _, system in ipairs(e.systems) do
system:remove(e)
end
end
function Instance:addSystem(system, eventName)
if not self.namedSystems[system] then
self.systems[#self.systems + 1] = system
self.namedSystems[system] = system
end
self.eventManager:register(eventName, system)
return self
end
function Instance:removeSystem(system)
for index, other in ipairs(self.systems) do
if system == other then
table.remove(self.systems, index)
end
end
self.namedSystems[system] = nil
return self
end
function Instance:emit(...)
self.eventManager:emit(...)
return self
end
function Instance:update(dt)
self:emit("update", dt)
return self
end
function Instance:draw()
self:emit("draw")
return self
end
return setmetatable(Instance, {
__call = function(_, ...) return Instance.new(...) end,
})

53
fluid/list.lua Normal file
View file

@ -0,0 +1,53 @@
local List = {}
local mt = {__index = List}
function List.new()
return setmetatable({
numerical = {},
named = {},
size = 0,
}, mt)
end
function List:clear()
self.numerical = {}
self.named = {}
self.size = 0
end
function List:add(obj)
local size = self.size + 1
self.numerical[size] = obj
self.named[obj] = size
self.size = size
end
function List:remove(obj)
local index = self.named[obj]
local size = self.size
if index == size then
self.numerical[size] = nil
else
local other = self.numerical[size]
self.numerical[index] = other
self.named[other] = index
end
self.named[obj] = nil
self.size = size - 1
end
function List:get(i)
return self.numerical[i]
end
function List:getIndex(obj)
return self.named[obj]
end
return setmetatable(List, {
__call = function() return List.new() end,
})

57
fluid/pool.lua Normal file
View file

@ -0,0 +1,57 @@
local Pool = {}
Pool.__index = Pool
function Pool.new(name, filter)
local pool = setmetatable({
__filter = filter,
__name = name,
}, Pool)
return pool
end
function Pool:eligible(e)
for _, component in ipairs(self.__filter) do
if not e.components[component] then
return false
end
end
return true
end
function Pool:add(e)
local key = #self + 1
self[key] = e
e.keys[self] = key
end
function Pool:has(e)
return e.keys[self] and true
end
function Pool:remove(e, pool)
local key = e.keys[self]
if key then
local count = #self
if key == count then
self[key] = nil
e.keys[self] = nil
else
local swap = self[count]
self[key] = swap
self[count] = nil
swap.keys[self] = key
end
return true
end
end
return setmetatable(Pool, {
__call = function(_, ...) return Pool.new(...) end,
})

118
fluid/system.lua Normal file
View file

@ -0,0 +1,118 @@
local PATH = (...):gsub('%.[^%.]+$', '')
local Component = require(PATH..".component")
local Pool = require(PATH..".pool")
local System = {}
System.__index = System
function System.new(...)
local system = setmetatable({
__all = {},
__pools = {},
}, System)
for _, filter in pairs({...}) do
local pool = system:buildPool(filter)
if not system[pool.__name] then
system[pool.__name] = pool
system.__pools[#system.__pools + 1] = pool
else
error("Pool with name '"..pool.__name.."' already exists.")
end
end
return system
end
function System:buildPool(pool)
local name = "pool"
local filter = {}
for i, v in ipairs(pool) do
if type(v) == "table" then
filter[#filter + 1] = v
elseif type(v) == "string" then
name = v
end
end
return Pool(name, filter)
end
function System:entityUpdated(e)
local systemHas = self:has(e)
for _, pool in ipairs(self.__pools) do
local poolHas = pool:has(e)
local eligible = pool:eligible(e)
if not poolHas and eligible then
pool:add(e)
self:entityAddedTo(e, pool)
self:tryAdd(e)
return true
elseif poolHas and not eligible then
pool:remove(e)
self:entityRemovedFrom(e, pool)
self:tryRemove(e)
return false
end
end
end
function System:tryAdd(e)
if not self:has(e) then
self.__all[e] = 0
self:entityAdded(e)
end
self.__all[e] = self.__all[e] + 1
end
function System:tryRemove()
if self:has(e) then
self.__all[e] = self.__all[e] - 1
if self.__all[e] == 0 then
self.__all[e] = nil
self:entityRemoved(e)
end
end
end
function System:remove(e)
if self:has(e) then
for _, pool in ipairs(self.__pools) do
if pool:has(e) then
pool:remove(e)
self:entityRemovedFrom(e, pool)
end
end
self.__all[e] = nil
self:entityRemoved(e)
end
end
function System:has(e)
return self.__all[e] and true
end
function System:entityAdded(e)
end
function System:entityAddedTo(e, pool)
end
function System:entityRemoved(e)
end
function System:entityRemovedFrom(e, pool)
end
return setmetatable(System, {
__call = function(_, ...) return System.new(...) end,
})