Redoing folder layouts

This commit is contained in:
Justin van der Leij 2018-04-06 12:28:42 +02:00 committed by GitHub
parent 167f432bc9
commit 5afe539d86
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 802 additions and 21 deletions

41
concord/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,
})

80
concord/entity.lua Normal file
View file

@ -0,0 +1,80 @@
local PATH = (...):gsub('%.[^%.]+$', '')
local List = require(PATH..".list")
local Entity = {}
Entity.__index = Entity
--- Creates and initializes a new Entity.
-- @return A new Entity
function Entity.new()
local e = setmetatable({
components = {},
removed = {},
instances = List(),
}, Entity)
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.removed[component] = true
return self
end
--- Checks the Entity against the pools again.
-- @return self
function Entity:apply()
for i = 1, self.instances.size do
self.instances:get(i):checkEntity(self)
end
for component, _ in pairs(self.removed) do
self.components[component] = nil
self.removed[component] = nil
end
return self
end
--- Destroys the Entity.
-- @return self
function Entity:destroy()
for i = 1, self.instances.size do
self.instances:get(i):removeEntity(self)
end
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] ~= nil
end
return setmetatable(Entity, {
__call = function(_, ...) return Entity.new(...) end,
})

81
concord/init.lua Normal file
View file

@ -0,0 +1,81 @@
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")
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

170
concord/instance.lua Normal file
View file

@ -0,0 +1,170 @@
local PATH = (...):gsub('%.[^%.]+$', '')
local List = require(PATH..".list")
local Instance = {}
Instance.__index = Instance
--- Creates a new Instance.
-- @return The new instance
function Instance.new()
local instance = setmetatable({
entities = List(),
systems = List(),
events = {},
removed = {},
}, Instance)
return instance
end
--- Adds an Entity to the Instance.
-- @param e The Entity to add
-- @return self
function Instance:addEntity(e)
e.instances:add(self)
self.entities:add(e)
self:checkEntity(e)
return self
end
--- Checks an Entity against all the systems in the Instance.
-- @param e The Entity to check
-- @return self
function Instance:checkEntity(e)
for i = 1, self.systems.size do
self.systems:get(i):__check(e)
end
return self
end
--- Marks an Entity as removed from the Instance.
-- @param e The Entity to mark
-- @return self
function Instance:removeEntity(e)
self.removed[#self.removed + 1] = e
return self
end
--- Completely removes all marked Entities in the Instance.
-- @return self
function Instance:flush()
if #self.removed > 0 then
for i = 1, #self.removed do
local e = self.removed[i]
e.instances:remove(self)
self.entities:remove(e)
for i = 1, self.systems.size do
self.systems:get(i):__remove(e)
end
end
self.removed = {}
end
return self
end
--- Adds a System to the Instance.
-- @param system The System to add
-- @param eventName The Event to register to
-- @param callback The function name to call. Defaults to eventName
-- @param enabled If the system is enabled. Defaults to true
-- @return self
function Instance:addSystem(system, eventName, callback, enabled)
if system.__instance and system.__instance ~= self then
error("System already in instance '" ..tostring(system.__instance).."'")
end
if not self.systems:has(system) then
self.systems:add(system)
system.__instance = self
end
if eventName then
self.events[eventName] = self.events[eventName] or {}
local i = #self.events[eventName] + 1
self.events[eventName][i] = {
system = system,
callback = callback or eventName,
enabled = enabled == nil and true or enabled,
}
end
return self
end
--- Enables a System in the Instance.
-- @param system The System to enable
-- @param eventName The Event it was registered to
-- @param callback The callback it was registered with. Defaults to eventName
-- @return self
function Instance:enableSystem(system, eventName, callback)
return self:setSystem(system, eventName, callback, true)
end
--- Disables a System in the Instance.
-- @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 Instance:disableSystem(system, eventName, callback)
return self:setSystem(system, eventName, callback, false)
end
--- Sets a System 'enable' in the Instance.
-- @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 Instance:setSystem(system, eventName, callback, enable)
callback = callback or eventName
local listeners = self.events[eventName]
if listeners then
for i = 1, #listeners do
local listener = listeners[i]
if listener.system == system and listener.callback == callback then
listener.enabled = enable
break
end
end
end
return self
end
--- Emits an Event in the Instance.
-- @param eventName The Event that should be emitted
-- @param ... Parameters passed to listeners
-- @return self
function Instance:emit(eventName, ...)
self:flush()
local listeners = self.events[eventName]
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
return setmetatable(Instance, {
__call = function(_, ...) return Instance.new(...) end,
})

75
concord/list.lua Normal file
View file

@ -0,0 +1,75 @@
local List = {}
local mt = {__index = List}
--- Creates a new List.
-- @return A new list
function List.new()
return setmetatable({
objects = {},
pointers = {},
size = 0,
}, mt)
end
--- Clears the List completely.
-- @return self
function List:clear()
self.objects = {}
self.pointers = {}
self.size = 0
return self
end
--- Adds an object to the List.
-- @param obj The object to add
-- @return self
function List:add(obj)
local size = self.size + 1
self.objects[size] = obj
self.pointers[obj] = size
self.size = size
return self
end
--- Removes an object from the List.
-- @param obj The object to remove
-- @return self
function List:remove(obj)
local index = self.pointers[obj]
local size = self.size
if index == size then
self.objects[size] = nil
else
local other = self.objects[size]
self.objects[index] = other
self.pointers[other] = index
self.objects[size] = nil
end
self.pointers[obj] = nil
self.size = size - 1
end
--- Gets an object by numerical index.
-- @param index The index to look at
-- @return The object at the index
function List:get(index)
return self.objects[index]
end
--- Gets if the List has the object.
-- @param obj The object to search for
-- @param true if the list has the object, false otherwise
function List:has(obj)
return self.pointers[obj] and true
end
return setmetatable(List, {
__call = function() return List.new() end,
})

37
concord/pool.lua Normal file
View file

@ -0,0 +1,37 @@
local PATH = (...):gsub('%.[^%.]+$', '')
local List = require(PATH..".list")
local Pool = {}
Pool.__index = Pool
--- Creates a new Pool
-- @param name Identifier for the Pool.
-- @param filter Table containing the required Components
-- @return The new Pool
function Pool.new(name, filter)
local pool = setmetatable(List(), Pool)
pool.name = name
pool.filter = filter
return pool
end
--- Checks if an Entity is eligible for the Pool.
-- @param e The Entity to check
-- @return True if the entity is eligible, false otherwise
function Pool:eligible(e)
for _, component in ipairs(self.filter) do
if not e.components[component] or e.removed[component] then
return false
end
end
return true
end
return setmetatable(Pool, {
__index = List,
__call = function(_, ...) return Pool.new(...) end,
})

169
concord/system.lua Normal file
View file

@ -0,0 +1,169 @@
local PATH = (...):gsub('%.[^%.]+$', '')
local Component = require(PATH..".component")
local Pool = require(PATH..".pool")
local System = {}
System.mt = {
__index = System,
__call = function(systemProto, ...)
local system = setmetatable({
__all = {},
__pools = {},
__instance = nil,
}, systemProto)
for _, filter in pairs(systemProto.__filter) 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
system:init(...)
return system
end,
}
--- Creates a new System prototype.
-- @param ... Variable amounts of filters
-- @return A new System prototype
function System.new(...)
local systemProto = setmetatable({
__filter = {...},
}, System.mt)
systemProto.__index = systemProto
return systemProto
end
--- Default initialization function.
-- @param ... Varags
function System:init(...)
end
--- Builds a Pool for the System.
-- @param baseFilter The 'raw' Filter
-- @return A new Pool
function System:__buildPool(baseFilter)
local name = "pool"
local filter = {}
for i, v in ipairs(baseFilter) do
if type(v) == "table" then
filter[#filter + 1] = v
elseif type(v) == "string" then
name = v
end
end
return Pool(name, filter)
end
--- Checks and applies an Entity to the System's pools.
-- @param e The Entity to check
-- @return True if the Entity was added, false if it was removed. Nil if nothing happend
function System:__check(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
--- Removed an Entity from the System.
-- @param e The Entity to remove
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
--- Tries to add an Entity to the System.
-- @param e The Entity to add
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
--- Tries to remove an Entity from the System.
-- @param e The Entity to remove
function System:__tryRemove(e)
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
--- Returns the Instance the System is in.
-- @return The Instance
function System:getInstance()
return self.__instance
end
--- Returns if the System has the Entity.
-- @param The Entity to check for
-- @return True if the System has the Entity. False otherwise
function System:__has(e)
return self.__all[e] and true
end
--- Default callback for adding an Entity.
-- @param e The Entity that was added
function System:entityAdded(e)
end
--- Default callback for adding an Entity to a pool.
-- @param e The Entity that was added
-- @param pool The pool the Entity was added to
function System:entityAddedTo(e, pool)
end
--- Default callback for removing an Entity.
-- @param e The Entity that was removed
function System:entityRemoved(e)
end
--- Default callback for removing an Entity from a pool.
-- @param e The Entity that was removed
-- @param pool The pool the Entity was removed from
function System:entityRemovedFrom(e, pool)
end
return setmetatable(System, {
__call = function(_, ...) return System.new(...) end,
})

View file

@ -0,0 +1,9 @@
function love.conf(t)
t.identity = "Platformer"
t.version = "11.0"
t.console = true
t.window.vsync = false
t.window.width = 720
t.window.height = 720
end

View file

@ -0,0 +1,118 @@
local Fluid = require("fluid").init({
useEvents = true
})
local Entity = Fluid.entity
local Component = Fluid.component
local System = Fluid.system
local Game = Fluid.instance()
Fluid.addInstance(Game)
local Position = Component(function(e, x, y)
e.x = x
e.y = y
end)
local Rectangle = Component(function(e, w, h)
e.w = w
e.h = h
end)
local Circle = Component(function(e, r)
e.r = r
end)
local Color = Component(function(e, r, g, b, a)
e.r = r
e.g = g
e.b = b
e.a = a
end)
local RectangleRenderer = System({Position, Rectangle})
function RectangleRenderer:draw()
local e
for i = 1, self.pool.size do
e = self.pool:get(i)
local position = e:get(Position)
local rectangle = e:get(Rectangle)
local color = e:get(Color)
love.graphics.setColor(255, 255, 255)
if color then
love.graphics.setColor(color.r, color.g, color.b, color.a)
end
love.graphics.rectangle("fill", position.x, position.y, rectangle.w, rectangle.h)
end
end
local CircleRenderer = System({Position, Circle})
function CircleRenderer:draw()
local e
for i = 1, self.pool.size do
e = self.pool:get(i)
local position = e:get(Position)
local circle = e:get(Circle)
local color = e:get(Color)
love.graphics.setColor(255, 255, 255)
if color then
love.graphics.setColor(color.r, color.g, color.b, color.a)
end
love.graphics.circle("fill", position.x, position.y, circle.r)
end
end
local RandomRemover = System({})
function RandomRemover:init()
self.time = 0
end
function RandomRemover:update(dt)
self.time = self.time + dt
if self.time >= 0.25 then
self.time = 0
if self.pool.size > 0 then
local i = love.math.random(1, self.pool.size)
self.pool:get(i):destroy()
end
end
love.window.setTitle(love.timer.getFPS())
end
Game:addSystem(RandomRemover(), "update")
Game:addSystem(RectangleRenderer(), "draw")
Game:addSystem(CircleRenderer(), "draw")
for i = 1, 100 do
local e = Entity()
e:give(Position, love.math.random(0, 700), love.math.random(0, 700))
e:give(Rectangle, love.math.random(5, 20), love.math.random(5, 20))
if love.math.random(0, 1) == 0 then
e:give(Color, love.math.random(), love.math.random(), love.math.random(), 1)
end
Game:addEntity(e)
end
for i = 1, 100 do
local e = Entity()
e:give(Position, love.math.random(0, 700), love.math.random(0, 700))
e:give(Circle, love.math.random(5, 20))
if love.math.random(0, 1) == 0 then
e:give(Color, love.math.random(), love.math.random(), love.math.random(), 1)
end
Game:addEntity(e)
end

1
init.lua Normal file
View file

@ -0,0 +1 @@
return require("concord")