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

40
LICENSE
View file

@ -1,21 +1,21 @@
MIT License MIT License
Copyright (c) 2018 Justin van der Leij Copyright (c) 2018 Justin van der Leij
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

View file

@ -1 +1 @@
# Concord # Concord

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")