diff --git a/docs/index.html b/docs/index.html index c76a1e6..fd17121 100644 --- a/docs/index.html +++ b/docs/index.html @@ -113,7 +113,7 @@
generated by LDoc 1.4.6 -Last updated 2022-10-07 12:40:59 +Last updated 2022-10-11 19:12:05
diff --git a/docs/modules/asset.html b/docs/modules/asset.html index 2a03f0b..a939867 100644 --- a/docs/modules/asset.html +++ b/docs/modules/asset.html @@ -337,7 +337,7 @@
generated by LDoc 1.4.6 -Last updated 2022-10-07 12:40:59 +Last updated 2022-10-11 19:12:05
diff --git a/docs/modules/ecs.html b/docs/modules/ecs.html index 3716568..6196154 100644 --- a/docs/modules/ecs.html +++ b/docs/modules/ecs.html @@ -70,7 +70,8 @@ Main differences include:

  • ability to nest systems (more organisation potential);
  • instanciation of systems for each world (no shared state) (several worlds can coexist at the same time easily);
  • adding and removing entities is done instantaneously (no going isane over tiny-ecs cache issues);
  • -
  • ability to add and remove components from entities after they were added to the world (more dynamic entities).
  • +
  • ability to add and remove components from entities after they were added to the world (more dynamic entities);
  • +
  • much better performance for ordered systems (entities are stored in a skip list internally).
  • @@ -83,6 +84,8 @@ if you don’t use them.

    The module returns a table that contains several functions, world or scene are starting points to create your world.

    +

    This library was designed to be reasonably fast; on my machine using LuaJIT, in the duration of a frame (1/60 seconds) about 40000 entities can be added to an unordered system or 8000 to an ordered system. Complexities are documented for each function.

    +

    No mandatory dependency. Optional dependency: ubiquitousse.scene, to allow quick creation of ECS-based scenes (ecs.scene).

    Usage:

    @@ -140,6 +143,26 @@ end

    Components.

    + + + + + + + + + + + + + + + + + + + +
    Entity.firstFirst element of the highest layer linked list of entities: { entity, next_element, element_in_lower_layer }.
    Entity.firstBaseFirst element of the base layer.
    Entity.previousList of hash map (one per skip listlayer) of entities in the system and their previous linked list element.
    Entity.nLayersNumber of layers in the skip list.
    Entity.nNumber of elements in the skip list.

    System objects

    @@ -519,6 +542,117 @@ component or if it doesn’t exist in the entity. } + +
    + + Entity.first +
    +
    + First element of the highest layer linked list of entities: { entity, next_element, element_in_lower_layer }. + The default entity head is always added as a first element to simplify algorithms; remember to skip it. + + + + + + +

    Fields:

    + + + + + + +
    +
    + + Entity.firstBase +
    +
    + First element of the base layer. + + + + + + + + + + + +
    +
    + + Entity.previous +
    +
    + List of hash map (one per skip listlayer) of entities in the system and their previous linked list element. + Does not contain a key for the head entity. + This make each linked list layer effectively a doubly linked list, but with fast access to the previous element using this map (and therefore O(1) deletion). + + + + + + +

    Fields:

    + + + + + + +
    +
    + + Entity.nLayers +
    +
    + Number of layers in the skip list. + + + + + + + + + + + +
    +
    + + Entity.n +
    +
    + Number of elements in the skip list. + + + + + + + + + + +

    System objects

    @@ -1306,7 +1440,7 @@ avoid repeating your filters or allow controlling several system from a single p If you do that, since System:remove will not search for entities in systems where they should have been filtered out, the added entities will not be removed when calling System:remove on a parent system or the world. The entity can be removed by calling System:remove on the system System:add was called on.

    -

    Complexity: O(1) per unordered system, O(entityCount) per ordered system. +

    Complexity: O(1) per unordered system, O(log2(entityCount)) per ordered system. @@ -1349,7 +1483,7 @@ avoid repeating your filters or allow controlling several system from a single p

    If you intend to call this on a subsystem instead of the world, please read the warning in System:add.

    -

    Complexity: O(1) per system. +

    Complexity: O(1) per unordered system, O(log2(entityCount)) per ordered system. @@ -1429,7 +1563,7 @@ avoid repeating your filters or allow controlling several system from a single p

    Will recalculate the entity position in the entity list for this system and its subsystems. Will skip entities that are not in the system.

    -

    Complexity: O(entityCount) per system. +

    Complexity: O(1) per unordered system, O(log2(entityCount)) per ordered system. @@ -1501,7 +1635,9 @@ avoid repeating your filters or allow controlling several system from a single p System:iter ()

    - Returns an iterator that iterate through the entties in this system, in order. + Returns an iterator that iterate through the entties in this system, in order.

    + +

    Complexity: O(1) per iteration; O(entityCount) for the full iteration @@ -1525,9 +1661,9 @@ avoid repeating your filters or allow controlling several system from a single p System:get (i)

    - Get the ith entity in the system. - This is a simple wrapper around iter; it will iterate over all the entities in the system in order until we reach the desired one. - Complexity: O(i) + Get the ith entity in the system.

    + +

    Complexity: O(i) @@ -1558,7 +1694,9 @@ avoid repeating your filters or allow controlling several system from a single p System:clear ()

    - Remove every entity from the system and its subsystems. + Remove every entity from the system and its subsystems.

    + +

    Complexity: O(entityCount) per system @@ -1578,7 +1716,9 @@ avoid repeating your filters or allow controlling several system from a single p

    Try to update the system and its subsystems. Should be called on every game update.

    -

    Subsystems are updated after their parent system. +

    Subsystems are updated after their parent system.

    + +

    Complexity: O(entityCount) per system if system:process is defined; O(1) per system otherwise. @@ -1605,7 +1745,9 @@ avoid repeating your filters or allow controlling several system from a single p

    Try to draw the system and its subsystems. Should be called on every game draw.

    -

    Subsystems are drawn after their parent system. +

    Subsystems are drawn after their parent system.

    + +

    — Complexity: O(entityCount) per system if system:render is defined; O(1) per system otherwise. @@ -1629,7 +1771,9 @@ avoid repeating your filters or allow controlling several system from a single p if the method exists and the entity is in the system. c is the system component associated with the current system, and e is the Entity.

    -

    Think of it as a way to perform custom callbacks issued from an entity event, similar to System:onAdd. +

    Think of it as a way to perform custom callbacks issued from an entity event, similar to System:onAdd.

    + +

    Complexity: O(1) per system @@ -1681,7 +1825,9 @@ its sibling systems (i.e. completely stop the propagation of the event). disable System:onUpdate behaviour on the system and its subsystems).

    "capture" would be for example used to prevent other systems from handling the event (for example to make sure an - input event is handled only once by a single system). + input event is handled only once by a single system).

    + +

    Complexity: O(1) per system @@ -1710,6 +1856,7 @@ its sibling systems (i.e. completely stop the propagation of the event).

    Remove all the entities and subsystems in this system. + Complexity: O(entityCount) per system @@ -1729,7 +1876,7 @@ its sibling systems (i.e. completely stop the propagation of the event).
    generated by LDoc 1.4.6 -Last updated 2022-10-07 12:40:59 +Last updated 2022-10-11 19:12:05
    diff --git a/docs/modules/input.html b/docs/modules/input.html index 921936a..42552b9 100644 --- a/docs/modules/input.html +++ b/docs/modules/input.html @@ -238,6 +238,10 @@ end
    + + + + @@ -1181,6 +1185,31 @@ player.fire.event:bind("pressed", + Input:set (...) + +
    + Manually set the state of this input. + + + + + + +

    Parameters:

    + + + + + +
    @@ -1694,7 +1723,7 @@ player.fire.event:bind("pressed",
    generated by LDoc 1.4.6 -Last updated 2022-10-07 12:40:59 +Last updated 2022-10-11 19:12:05
    diff --git a/docs/modules/ldtk.html b/docs/modules/ldtk.html index 6bcca65..4dc42bd 100644 --- a/docs/modules/ldtk.html +++ b/docs/modules/ldtk.html @@ -2066,7 +2066,7 @@ end Level background.

    If there is a background image, background.image contains a table {image=image, x=number, y=number, sx=number, sy=number} - where image is the LÖVE image (or image filepath if LÖVE not available) x and y are the top-left position, + where image is the LÖVE image (or image filepath if LÖVE not available) x and y are the top-left position, and sx and sy the horizontal and vertical scale factors. @@ -2142,7 +2142,7 @@ end

  • Enum are converted into a Lua string giving the currently selected enum value.
  • Filepath are converted into a Lua string giving the file path.
  • Arrays are converted into a Lua table with the elements in it as a list.
  • -
  • Points are converted into a Lua table with the fields x and y, in pixels: { x=number, y=number }.
  • +
  • Points are converted into a Lua table with the fields x and y, in pixels: { x=number, y=number }.
  • Colors are converted into a Lua table with the red, green and blue components in [0-1] as a list: {r,g,b}.
  • Tiles are converted into a Lua table { tileset = associated tileset object, quad = associated quad } where quad is a LÖVE Quad if LÖVE is available, otherwise a table { x, y, width, height }.
  • EntityRef are converted into a Lua table { level = level, layerIid = layer IID, entityIid = entity IID, entity = see explanation }. If the entity being refernced belongs to another level and this level is not loaded, entity will be nil; otherwise (same level or the other level is also loaded), it will contain the entity.
  • @@ -2170,7 +2170,7 @@ end
    generated by LDoc 1.4.6 -Last updated 2022-10-07 12:40:59 +Last updated 2022-10-11 19:12:05
    diff --git a/docs/modules/scene.html b/docs/modules/scene.html index c48edf3..0b9bdf6 100644 --- a/docs/modules/scene.html +++ b/docs/modules/scene.html @@ -703,7 +703,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-10-07 12:40:59 +Last updated 2022-10-11 19:12:05
    diff --git a/docs/modules/signal.html b/docs/modules/signal.html index 7a39cd0..8b93b1c 100644 --- a/docs/modules/signal.html +++ b/docs/modules/signal.html @@ -788,7 +788,7 @@ signal.event:bind("keypressed", function(key, scancode) print("pr
    generated by LDoc 1.4.6 -Last updated 2022-10-07 12:40:59 +Last updated 2022-10-11 19:12:05
    diff --git a/docs/modules/timer.html b/docs/modules/timer.html index 3abef1f..9cf1428 100644 --- a/docs/modules/timer.html +++ b/docs/modules/timer.html @@ -1154,7 +1154,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-10-07 12:40:59 +Last updated 2022-10-11 19:12:05
    diff --git a/docs/modules/ubiquitousse.html b/docs/modules/ubiquitousse.html index 92e2bb4..5c59f85 100644 --- a/docs/modules/ubiquitousse.html +++ b/docs/modules/ubiquitousse.html @@ -394,7 +394,7 @@ the repository to save you a few seconds.

    generated by LDoc 1.4.6 -Last updated 2022-10-07 12:40:59 +Last updated 2022-10-11 19:12:05
    diff --git a/docs/modules/util.html b/docs/modules/util.html index e7d3fca..67f3da4 100644 --- a/docs/modules/util.html +++ b/docs/modules/util.html @@ -785,7 +785,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-10-07 12:40:59 +Last updated 2022-10-11 19:12:05
    diff --git a/docs/topics/LICENSE.html b/docs/topics/LICENSE.html index 31fba8b..6188111 100644 --- a/docs/topics/LICENSE.html +++ b/docs/topics/LICENSE.html @@ -65,7 +65,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-10-07 12:40:59 +Last updated 2022-10-11 19:12:05
    diff --git a/docs/topics/README.md.html b/docs/topics/README.md.html index e9c7f06..1371bcf 100644 --- a/docs/topics/README.md.html +++ b/docs/topics/README.md.html @@ -87,7 +87,7 @@
    generated by LDoc 1.4.6 -Last updated 2022-10-07 12:40:59 +Last updated 2022-10-11 19:12:05
    diff --git a/ecs/ecs.can b/ecs/ecs.can index a7c34e2..c61d5dd 100644 --- a/ecs/ecs.can +++ b/ecs/ecs.can @@ -6,7 +6,8 @@ Main differences include: * ability to nest systems (more organisation potential); * instanciation of systems for each world (no shared state) (several worlds can coexist at the same time easily); * adding and removing entities is done instantaneously (no going isane over tiny-ecs cache issues); -* ability to add and remove components from entities after they were added to the world (more dynamic entities). +* ability to add and remove components from entities after they were added to the world (more dynamic entities); +* much better performance for ordered systems (entities are stored in a skip list internally). And a fair amount of other quality-of-life features. @@ -17,6 +18,8 @@ if you don't use them. The module returns a table that contains several functions, `world` or `scene` are starting points to create your world. +This library was designed to be reasonably fast; on my machine using LuaJIT, in the duration of a frame (1/60 seconds) about 40000 entities can be added to an unordered system or 8000 to an ordered system. Complexities are documented for each function. + No mandatory dependency. Optional dependency: `ubiquitousse.scene`, to allow quick creation of ECS-based scenes (`ecs.scene`). @@ -54,8 +57,7 @@ if not loaded then scene = nil end let ecs --- TODO: Implement a skip list for faster search. --- better control over system order: process, draw, methods? (for lag reasons and dependencies) +-- TODO: better control over system order: process, draw, methods? (for lag reasons and dependencies) --- Entities are regular tables that get processed by `System`s. -- @@ -98,9 +100,6 @@ local sprite = { } ]]-- ---- Special value used as the first element of each system's linked list of entities. -let head = {} - --- Recursively remove subsystems from a system. let recDestroySystems = (system) for i=#system.systems, 1, -1 do @@ -122,17 +121,6 @@ let recCallOnRemoveFromWorld = (world, systems) end end ---- Iterate through the next entity, based on state s: { previousLinkedListItem } -let nextEntity = (s) - if s[1] then - let var = s[1][1] - s[1] = s[1][2] - return var - else - return nil - end -end - --- Recursively copy content of a into b if it isn't already present. -- Don't copy keys, will preserve metatable but not copy them. let copy = (a, b, cache={}) @@ -156,6 +144,149 @@ let copy = (a, b, cache={}) end end +--- Skip list implementation --- +-- Well technically it's a conbination of a skip list (for ordering) and a hash map (for that sweet O(1) search). Takes more memory but oh so efficient. + +--- Special value used as the first element of each linked list. +let head = {} + +--- Create a new linked list. +let skipNew = () + let s = { + --- First element of the highest layer linked list of entities: { entity, next_element, element_in_lower_layer }. + -- The default entity `head` is always added as a first element to simplify algorithms; remember to skip it. + first = { head, nil }, + --- First element of the base layer. + firstBase = nil, + --- List of hash map (one per skip listlayer) of entities in the system and their previous linked list element. + -- Does not contain a key for the `head` entity. + -- This make each linked list layer effectively a doubly linked list, but with fast access to the previous element using this map (and therefore O(1) deletion). + previous = { {} }, + --- Number of layers in the skip list. + nLayers = 1, + --- Number of elements in the skip list. + n = 0 + } + s.firstBase = s.first + return s +end + +--- Iterate through the next entity in a linked list, based on state s: { previousLinkedListItem } +let nextEntity = (s) + if s[1] then + let var = s[1][1] + s[1] = s[1][2] + return var + else + return nil + end +end +--- Returns an iterator over all the elements of the skip list. +-- Complexity: O(n) like expected +let skipIter = :() + return nextEntity, { @firstBase[2] } +end + +--- Add new layers (if needed) in order to target O(log2(n)) complexity for skip list operations. +-- i.e. add layers until we have log2(n) layers +-- Complexity: O(log2(n)) if you never called it before, but you should call it with every insert for O(1) +let skipAddLayers = :() + while @n > 2^@nLayers do + @first = { head, nil, @first } + table.insert(@previous, {}) + @nLayers += 1 + end +end + +--- 1/2 chance of being true, 1/2 of being false! How exciting! +let coinFlip = () + return math.random(0,1) == 1 +end +--- Insert an element into the skip list using system:compare for ordering. +-- Behavior undefined if e is already in the skip list. +-- Complexity: if luck is on your side, O(log2(n)); O(n) if the universe hates you; O(1) if compare always returns true and nLayers = 1 +let skipInsert = :(system, e) + -- find previous entity in each layer + let prevLayer = {} + let prev = @first -- no need to process first entity as it is the special `head` entity + for i=@nLayers, 1, -1 do + while true do + -- next is end of layer or greater entity: select this prev entity + if prev[2] == nil or system:compare(e, prev[2][1]) then + prevLayer[i] = prev + -- not on base layer: go down a layer, for loop will continue + if prev[3] then + prev = prev[3] -- same entity on lower layer + break + end + break + -- next entity on current layer + else + prev = prev[2] + end + end + end + -- add to each layer + let inLowerLayer + for i=1, @nLayers do + prev = prevLayer[i] + if i == 1 or coinFlip() then -- always present in base layer, otherwise 1/2 chance + let nxt = prev[2] + prev[2] = { e, nxt, inLowerLayer } + @previous[i][e] = prev + if nxt then + @previous[i][nxt[1]] = prev[2] + end + inLowerLayer = prev[2] + else + break + end + end + @n += 1 +end + +--- Remove an element from the skip list. +-- Behavior undefined if e is not in the skip list. +-- Complexity: O(nLayers) at most, which should be O(log2(n)) if you called skipAddLayers often enough +let skipDelete = :(e) + -- remove from each layer + for i=1, @nLayers do + let previous = @previous[i] + if previous[e] then + let prev = previous[e] + prev[2] = prev[2][2] + previous[e] = nil + if prev[2] then + previous[prev[2][1]] = prev + end + else + break -- won't appear on higher layers either + end + end + @n -= 1 +end + +--- Reorder an element into the skip list. +-- Behavior undefined if e is not in the skip list. +-- Complexity: if luck is on your side, O(log2(n)); O(n) if the universe hates you; O(1) if compare always returns true and nLayers = 1 +let skipReorder = :(system, e) + skipDelete(@, e) + skipInsert(@, system, e) +end + +--- Returns the ith element of the skip list. +-- Complexity: O(n) +let skipIndex = :(i) + local n = 1 + for e in skipIter(@) do + if n == i then + return e + end + n += 1 + end + return nil +end + --[[-- Systems and Worlds. Systems are what do the processing on your entities. A system contains a list of entities; the entities in this list are selected using a `filter`, and the system will only operate on those filtered entities. @@ -385,14 +516,12 @@ let system_mt = { --- Private fields --- - --- First element of the linked list of entities: { entity, next_element }. - -- The default entity `head` is always added as a first element to simplify algorithms; remember to skip it. + --- Hash map of the entities currently in the system. + -- Used to quickly check if an entity is present of not in this system. + -- This is actually the _skiplist.previous[1] table. -- @local - _first = nil, - --- Hash map of entities in the system and their previous linked list element. Does not contain a key for the `head` entity. - -- This make the list effectively a doubly linked list, but with fast access to the previous element using this map (and therefore O(1) deletion). - -- @local - _previous = nil, + _has = nil, + --- Amount of time waited since last update (if interval is set). -- @local _waited = 0, @@ -412,37 +541,27 @@ let system_mt = { -- If you do that, since `System:remove` will not search for entities in systems where they should have been filtered out, the added entities will not be removed -- when calling `System:remove` on a parent system or the world. The entity can be removed by calling `System:remove` on the system `System:add` was called on. -- - -- Complexity: O(1) per unordered system, O(entityCount) per ordered system. + -- Complexity: O(1) per unordered system, O(log2(entityCount)) per ordered system. -- @tparam Entity e entity to add -- @tparam Entity... ... other entities to add -- @treturn Entity,... `e,...` the function arguments add = :(e, ...) - if e ~= nil and not @_previous[e] and @filter(e) then + if e ~= nil and not @_has[e] and @filter(e) then -- copy default system component if @component and @default then copy({ [@component] = @default }, e) end - -- add to linked list - let entity = @_first - while entity[2] ~= nil do -- no need to process first entity as it is the special `head` entity - if @compare(e, entity[2][1]) then - let nxt = entity[2] - entity[2] = { e, nxt } - @_previous[e] = entity - @_previous[nxt[1]] = entity[2] - break - end - entity = entity[2] - end - if entity[2] == nil then - entity[2] = { e, nil } - @_previous[e] = entity + -- ordered system: add new layer if needed + if @compare ~= system_mt.compare then + skipAddLayers(@_skiplist) end + -- add to skip list + skipInsert(@_skiplist, @, e) -- notify addition @entityCount += 1 @onAdd(e, e[@component]) -- add to subsystems (if it wasn't immediately removed in onAdd) - if @_previous[e] then + if @_has[e] then for _, s in ipairs(@systems) do s:add(e) end @@ -462,27 +581,21 @@ let system_mt = { -- -- If you intend to call this on a subsystem instead of the world, please read the warning in `System:add`. -- - -- Complexity: O(1) per system. + -- Complexity: O(1) per unordered system, O(log2(entityCount)) per ordered system. -- @tparam Entity e entity to remove -- @tparam Entity... ... other entities to remove -- @treturn Entity,... `e,...` the function arguments remove = :(e, ...) if e ~= nil then - if @_previous[e] then + if @_has[e] then -- remove from subsystems for _, s in ipairs(@systems) do s:remove(e) end end - if @_previous[e] then -- recheck in case it was removed already from a subsystem onRemove callback - -- remove from linked list - let prev = @_previous[e] - prev[2] = prev[2][2] - if prev[2] then - @_previous[prev[2][1]] = prev - end + if @_has[e] then -- recheck in case it was removed already from a subsystem onRemove callback + skipDelete(@_skiplist, e) -- notify removal - @_previous[e] = nil @entityCount -= 1 @onRemove(e, e[@component]) end @@ -504,9 +617,9 @@ let system_mt = { -- @treturn Entity,... `e,...` the function arguments refresh = :(e, ...) if e ~= nil then - if not @_previous[e] then + if not @_has[e] then @add(e) - elseif @_previous[e] then + else if not @filter(e) then @remove(e) else @@ -527,35 +640,13 @@ let system_mt = { -- Will recalculate the entity position in the entity list for this system and its subsystems. -- Will skip entities that are not in the system. -- - -- Complexity: O(entityCount) per system. + -- Complexity: O(1) per unordered system, O(log2(entityCount)) per ordered system. -- @tparam Entity e entity to reorder -- @tparam Entity... ... other entities to reorder -- @treturn Entity,... `e,...` the function arguments reorder = :(e, ...) - if e ~= nil and @_previous[e] then - let prev = @_previous[e] -- { prev, { e, next } } - let next = prev[2][2] - -- remove e from linked list - prev[2] = next - if next then - @_previous[next[1]] = prev - end - -- find position so that prev < e <= next - while prev[1] ~= head and @compare(e, prev[1]) do -- ensure prev < e - next = prev - prev = @_previous[prev[1]] - end - while next ~= nil and not @compare(e, next[1]) do -- ensure e <= next - prev = next - next = next[2] - end - -- reinsert e in linked list - let new = { e, next } - @_previous[e] = prev - if next then - @_previous[next[1]] = new - end - prev[2] = new + if e ~= nil and @_has[e] then + skipReorder(@_skiplist, @, e) -- Reorder in subsystems for _, s in ipairs(@systems) do s:reorder(e) @@ -574,7 +665,7 @@ let system_mt = { -- @tparam Entity... ... other entities that may be in the system -- @treturn boolean `true` if every entity is in the system has = :(e, ...) - let has = e == nil or not not @_previous[e] + let has = e == nil or not not @_has[e] if ... then return has and @has(...) else @@ -582,28 +673,25 @@ let system_mt = { end end, --- Returns an iterator that iterate through the entties in this system, in order. + -- + -- Complexity: O(1) per iteration; O(entityCount) for the full iteration -- @treturn iterator iterator over the entities in this system iter = :() - return nextEntity, { @_first[2] } + return skipIter(@_skiplist) end, --- Get the `i`th entity in the system. - -- This is a simple wrapper around `iter`; it _will_ iterate over all the entities in the system in order until we reach the desired one. + -- -- Complexity: O(i) -- @tparam number i the index of the entity -- @treturn Entity the entity; `nil` if there is no such entity in the system get = :(i) - local n = 1 - for e in @iter() do - if n == i then - return e - end - n += 1 - end - return nil + return skipIndex(@_skiplist, i) end, --- Remove every entity from the system and its subsystems. + -- + -- Complexity: O(entityCount) per system clear = :() - for e in @iter() do + for e in skipIter(@_skiplist) do @remove(e) end for _, s in ipairs(@systems) do @@ -613,6 +701,8 @@ let system_mt = { --- Try to update the system and its subsystems. Should be called on every game update. -- -- Subsystems are updated after their parent system. + -- + -- Complexity: O(entityCount) per system if system:process is defined; O(1) per system otherwise. -- @number dt delta-time since last update update = :(dt) if @active then @@ -624,7 +714,7 @@ let system_mt = { end @onUpdate(dt) if @process ~= system_mt.process then - for e in @iter() do + for e in skipIter(@_skiplist) do @process(e, e[@component], dt) end end @@ -640,11 +730,13 @@ let system_mt = { --- Try to draw the system and its subsystems. Should be called on every game draw. -- -- Subsystems are drawn after their parent system. + -- + -- -- Complexity: O(entityCount) per system if system:render is defined; O(1) per system otherwise. draw = :() if @visible then @onDraw() if @render ~= system_mt.render then - for e in @iter() do + for e in skipIter(@_skiplist) do @render(e, e[@component]) end end @@ -661,16 +753,18 @@ let system_mt = { -- associated with the current system, and `e` is the `Entity`. -- -- Think of it as a way to perform custom callbacks issued from an entity event, similar to `System:onAdd`. + -- + -- Complexity: O(1) per system -- @tparam string name name of the callback -- @tparam Entity e the entity to perform the callback on -- @param ... other arguments to pass to the callback callback = :(name, e, ...) -- call callback - if @_previous[e] and @[name] then + if @_has[e] and @[name] then @[name](@, e, e[@component], ...) end -- callback on subsystems (if it wasn't removed during the callback) - if @_previous[e] then + if @_has[e] then for _, ss in ipairs(@systems) do ss:callback(name, e, ...) end @@ -695,6 +789,7 @@ let system_mt = { -- `"capture"` would be for example used to prevent other systems from handling the event (for example to make sure an -- input event is handled only once by a single system). -- + -- Complexity: O(1) per system -- @tparam string name name of the callback -- @param ... other arguments to pass to the callback emit = :(name, ...) @@ -713,6 +808,7 @@ let system_mt = { return status end, --- Remove all the entities and subsystems in this system. + -- Complexity: O(entityCount) per system destroy = :() recCallOnRemoveFromWorld(@world, { @ }) recDestroySystems({ systems = { @ } }) @@ -736,8 +832,7 @@ let recInstanciateSystems = (world, systems) world = world, w = world, s = world.s, - _first = { head }, - _previous = {}, + _skiplist = skipNew() }, { __index = :(k) if s[k] ~= nil then @@ -747,6 +842,7 @@ let recInstanciateSystems = (world, systems) end end }) + system._has = system._skiplist.previous[1] -- create filter if type(s.filter) == "string" then system.filter = (_, e) return e[s.filter] ~= nil end @@ -791,9 +887,9 @@ ecs = { let world = setmetatable({ filter = ecs.all(), s = {}, - _first = { head }, - _previous = {} + _skiplist = skipNew(), }, { __index = system_mt }) + world._has = world._skiplist.previous[1] world.world = world world.w = world world.systems = recInstanciateSystems(world, {...}) diff --git a/ecs/ecs.lua b/ecs/ecs.lua index 620a502..e558f77 100644 --- a/ecs/ecs.lua +++ b/ecs/ecs.lua @@ -1,517 +1,542 @@ -local loaded, scene -- ./ecs/ecs.can:51 -if ... then -- ./ecs/ecs.can:52 -loaded, scene = pcall(require, (...):match("^(.-)ecs") .. "scene") -- ./ecs/ecs.can:52 -end -- ./ecs/ecs.can:52 -if not loaded then -- ./ecs/ecs.can:53 -scene = nil -- ./ecs/ecs.can:53 -end -- ./ecs/ecs.can:53 -local ecs -- ./ecs/ecs.can:55 -local recDestroySystems -- ./ecs/ecs.can:102 -recDestroySystems = function(system) -- ./ecs/ecs.can:102 -for i = # system["systems"], 1, - 1 do -- ./ecs/ecs.can:103 -local s -- ./ecs/ecs.can:104 -s = system["systems"][i] -- ./ecs/ecs.can:104 -recDestroySystems(s) -- ./ecs/ecs.can:105 -s:onDestroy() -- ./ecs/ecs.can:106 -system["systems"][i] = nil -- ./ecs/ecs.can:107 -if s["name"] then -- ./ecs/ecs.can:108 -system["world"]["s"][s["name"]] = nil -- ./ecs/ecs.can:109 -end -- ./ecs/ecs.can:109 -end -- ./ecs/ecs.can:109 -end -- ./ecs/ecs.can:109 -local recCallOnRemoveFromWorld -- ./ecs/ecs.can:114 -recCallOnRemoveFromWorld = function(world, systems) -- ./ecs/ecs.can:114 -for _, s in ipairs(systems) do -- ./ecs/ecs.can:115 -s:clear() -- ./ecs/ecs.can:116 -recCallOnRemoveFromWorld(world, s["systems"]) -- ./ecs/ecs.can:117 -s:onRemoveFromWorld(world) -- ./ecs/ecs.can:118 -end -- ./ecs/ecs.can:118 -end -- ./ecs/ecs.can:118 -local nextEntity -- ./ecs/ecs.can:123 -nextEntity = function(s) -- ./ecs/ecs.can:123 -if s[1] then -- ./ecs/ecs.can:124 -local var -- ./ecs/ecs.can:125 -var = s[1][1] -- ./ecs/ecs.can:125 -s[1] = s[1][2] -- ./ecs/ecs.can:126 -return var -- ./ecs/ecs.can:127 -else -- ./ecs/ecs.can:127 -return nil -- ./ecs/ecs.can:129 -end -- ./ecs/ecs.can:129 -end -- ./ecs/ecs.can:129 -local copy -- ./ecs/ecs.can:135 -copy = function(a, b, cache) -- ./ecs/ecs.can:135 -if cache == nil then cache = {} end -- ./ecs/ecs.can:135 -for k, v in pairs(a) do -- ./ecs/ecs.can:136 -if type(v) == "table" then -- ./ecs/ecs.can:137 -if b[k] == nil then -- ./ecs/ecs.can:138 -if cache[v] then -- ./ecs/ecs.can:139 -b[k] = cache[v] -- ./ecs/ecs.can:140 -else -- ./ecs/ecs.can:140 -cache[v] = {} -- ./ecs/ecs.can:142 -b[k] = cache[v] -- ./ecs/ecs.can:143 -copy(v, b[k], cache) -- ./ecs/ecs.can:144 -setmetatable(b[k], getmetatable(v)) -- ./ecs/ecs.can:145 -end -- ./ecs/ecs.can:145 -elseif type(b[k]) == "table" then -- ./ecs/ecs.can:147 -copy(v, b[k], cache) -- ./ecs/ecs.can:148 -end -- ./ecs/ecs.can:148 -elseif b[k] == nil then -- ./ecs/ecs.can:150 -b[k] = v -- ./ecs/ecs.can:151 -end -- ./ecs/ecs.can:151 -end -- ./ecs/ecs.can:151 -end -- ./ecs/ecs.can:151 -local system_mt -- ./ecs/ecs.can:217 -system_mt = { -- ./ecs/ecs.can:217 -["name"] = nil, -- ./ecs/ecs.can:231 -["systems"] = nil, -- ./ecs/ecs.can:239 -["interval"] = false, -- ./ecs/ecs.can:245 -["active"] = true, -- ./ecs/ecs.can:249 -["visible"] = true, -- ./ecs/ecs.can:253 -["component"] = nil, -- ./ecs/ecs.can:260 -["default"] = nil, -- ./ecs/ecs.can:270 -["filter"] = function(self, e) -- ./ecs/ecs.can:291 -return false -- ./ecs/ecs.can:291 -end, -- ./ecs/ecs.can:291 -["compare"] = function(self, e1, e2) -- ./ecs/ecs.can:303 -return true -- ./ecs/ecs.can:303 -end, -- ./ecs/ecs.can:303 -["onAdd"] = function(self, e, c) -- ./ecs/ecs.can:309 - -- ./ecs/ecs.can:309 -end, -- ./ecs/ecs.can:309 -["onRemove"] = function(self, e, c) -- ./ecs/ecs.can:314 - -- ./ecs/ecs.can:314 -end, -- ./ecs/ecs.can:314 -["onInstance"] = function(self) -- ./ecs/ecs.can:317 - -- ./ecs/ecs.can:317 -end, -- ./ecs/ecs.can:317 -["onAddToWorld"] = function(self, world) -- ./ecs/ecs.can:321 - -- ./ecs/ecs.can:321 -end, -- ./ecs/ecs.can:321 -["onRemoveFromWorld"] = function(self, world) -- ./ecs/ecs.can:325 - -- ./ecs/ecs.can:325 -end, -- ./ecs/ecs.can:325 -["onDestroy"] = function(self) -- ./ecs/ecs.can:328 - -- ./ecs/ecs.can:328 -end, -- ./ecs/ecs.can:328 -["onUpdate"] = function(self, dt) -- ./ecs/ecs.can:333 - -- ./ecs/ecs.can:333 -end, -- ./ecs/ecs.can:333 -["onDraw"] = function(self) -- ./ecs/ecs.can:337 - -- ./ecs/ecs.can:337 -end, -- ./ecs/ecs.can:337 -["process"] = function(self, e, c, dt) -- ./ecs/ecs.can:344 - -- ./ecs/ecs.can:344 -end, -- ./ecs/ecs.can:344 -["render"] = function(self, e, c) -- ./ecs/ecs.can:350 - -- ./ecs/ecs.can:350 -end, -- ./ecs/ecs.can:350 -["onUpdateEnd"] = function(self, dt) -- ./ecs/ecs.can:355 - -- ./ecs/ecs.can:355 -end, -- ./ecs/ecs.can:355 -["onDrawEnd"] = function(self) -- ./ecs/ecs.can:359 - -- ./ecs/ecs.can:359 -end, -- ./ecs/ecs.can:359 -["world"] = nil, -- ./ecs/ecs.can:369 -["w"] = nil, -- ./ecs/ecs.can:373 -["entityCount"] = 0, -- ./ecs/ecs.can:377 -["s"] = nil, -- ./ecs/ecs.can:381 -["_first"] = nil, -- ./ecs/ecs.can:387 -["_previous"] = nil, -- ./ecs/ecs.can:391 -["_waited"] = 0, -- ./ecs/ecs.can:394 -["add"] = function(self, e, ...) -- ./ecs/ecs.can:415 -if e ~= nil and not self["_previous"][e] and self:filter(e) then -- ./ecs/ecs.can:416 -if self["component"] and self["default"] then -- ./ecs/ecs.can:418 -copy({ [self["component"]] = self["default"] }, e) -- ./ecs/ecs.can:419 -end -- ./ecs/ecs.can:419 -if self["_first"] == nil then -- ./ecs/ecs.can:422 -self["_first"] = { -- ./ecs/ecs.can:423 -e, -- ./ecs/ecs.can:423 -nil -- ./ecs/ecs.can:423 -} -- ./ecs/ecs.can:423 -self["_previous"][e] = true -- ./ecs/ecs.can:424 -elseif self:compare(e, self["_first"][1]) then -- ./ecs/ecs.can:425 -local nxt -- ./ecs/ecs.can:426 -nxt = self["_first"] -- ./ecs/ecs.can:426 -self["_first"] = { -- ./ecs/ecs.can:427 -e, -- ./ecs/ecs.can:427 -nxt -- ./ecs/ecs.can:427 -} -- ./ecs/ecs.can:427 -self["_previous"][e] = true -- ./ecs/ecs.can:428 -self["_previous"][nxt[1]] = self["_first"] -- ./ecs/ecs.can:429 -else -- ./ecs/ecs.can:429 -local entity -- ./ecs/ecs.can:431 -entity = self["_first"] -- ./ecs/ecs.can:431 -while entity[2] ~= nil do -- ./ecs/ecs.can:432 -if self:compare(e, entity[2][1]) then -- ./ecs/ecs.can:433 -local nxt -- ./ecs/ecs.can:434 -nxt = entity[2] -- ./ecs/ecs.can:434 -entity[2] = { -- ./ecs/ecs.can:435 -e, -- ./ecs/ecs.can:435 -nxt -- ./ecs/ecs.can:435 -} -- ./ecs/ecs.can:435 -self["_previous"][e] = entity -- ./ecs/ecs.can:436 -self["_previous"][nxt[1]] = entity[2] -- ./ecs/ecs.can:437 -break -- ./ecs/ecs.can:438 -end -- ./ecs/ecs.can:438 -entity = entity[2] -- ./ecs/ecs.can:440 -end -- ./ecs/ecs.can:440 -if entity[2] == nil then -- ./ecs/ecs.can:442 -entity[2] = { -- ./ecs/ecs.can:443 -e, -- ./ecs/ecs.can:443 -nil -- ./ecs/ecs.can:443 -} -- ./ecs/ecs.can:443 -self["_previous"][e] = entity -- ./ecs/ecs.can:444 -end -- ./ecs/ecs.can:444 -end -- ./ecs/ecs.can:444 -self["entityCount"] = self["entityCount"] + (1) -- ./ecs/ecs.can:448 -self:onAdd(e, e[self["component"]]) -- ./ecs/ecs.can:449 -if self["_previous"][e] then -- ./ecs/ecs.can:451 -for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:452 -s:add(e) -- ./ecs/ecs.can:453 -end -- ./ecs/ecs.can:453 -end -- ./ecs/ecs.can:453 -end -- ./ecs/ecs.can:453 -if ... then -- ./ecs/ecs.can:457 -return e, self:add(...) -- ./ecs/ecs.can:458 -else -- ./ecs/ecs.can:458 -return e -- ./ecs/ecs.can:460 -end -- ./ecs/ecs.can:460 -end, -- ./ecs/ecs.can:460 -["remove"] = function(self, e, ...) -- ./ecs/ecs.can:475 -if e ~= nil then -- ./ecs/ecs.can:476 -if self["_previous"][e] then -- ./ecs/ecs.can:477 -for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:479 -s:remove(e) -- ./ecs/ecs.can:480 -end -- ./ecs/ecs.can:480 -end -- ./ecs/ecs.can:480 -if self["_previous"][e] then -- ./ecs/ecs.can:483 -local prev -- ./ecs/ecs.can:485 -prev = self["_previous"][e] -- ./ecs/ecs.can:485 -if prev == true then -- ./ecs/ecs.can:486 -self["_first"] = self["_first"][2] -- ./ecs/ecs.can:487 -if self["_first"] then -- ./ecs/ecs.can:488 -self["_previous"][self["_first"][1]] = true -- ./ecs/ecs.can:489 -end -- ./ecs/ecs.can:489 -else -- ./ecs/ecs.can:489 -prev[2] = prev[2][2] -- ./ecs/ecs.can:492 -if prev[2] then -- ./ecs/ecs.can:493 -self["_previous"][prev[2][1]] = prev -- ./ecs/ecs.can:494 -end -- ./ecs/ecs.can:494 -end -- ./ecs/ecs.can:494 -self["_previous"][e] = nil -- ./ecs/ecs.can:498 -self["entityCount"] = self["entityCount"] - (1) -- ./ecs/ecs.can:499 -self:onRemove(e, e[self["component"]]) -- ./ecs/ecs.can:500 -end -- ./ecs/ecs.can:500 -end -- ./ecs/ecs.can:500 -if ... then -- ./ecs/ecs.can:503 -return e, self:remove(...) -- ./ecs/ecs.can:504 -else -- ./ecs/ecs.can:504 -return e -- ./ecs/ecs.can:506 -end -- ./ecs/ecs.can:506 -end, -- ./ecs/ecs.can:506 -["refresh"] = function(self, e, ...) -- ./ecs/ecs.can:518 -if e ~= nil then -- ./ecs/ecs.can:519 -if not self["_previous"][e] then -- ./ecs/ecs.can:520 -self:add(e) -- ./ecs/ecs.can:521 -elseif self["_previous"][e] then -- ./ecs/ecs.can:522 -if not self:filter(e) then -- ./ecs/ecs.can:523 -self:remove(e) -- ./ecs/ecs.can:524 -else -- ./ecs/ecs.can:524 -for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:526 -s:refresh(e) -- ./ecs/ecs.can:527 -end -- ./ecs/ecs.can:527 -end -- ./ecs/ecs.can:527 -end -- ./ecs/ecs.can:527 -end -- ./ecs/ecs.can:527 -if ... then -- ./ecs/ecs.can:532 -return e, self:refresh(...) -- ./ecs/ecs.can:533 -else -- ./ecs/ecs.can:533 -return e -- ./ecs/ecs.can:535 -end -- ./ecs/ecs.can:535 -end, -- ./ecs/ecs.can:535 -["reorder"] = function(self, e, ...) -- ./ecs/ecs.can:547 -if e ~= nil then -- ./ecs/ecs.can:548 -if self["_previous"][e] then -- ./ecs/ecs.can:549 -local prev -- ./ecs/ecs.can:550 -prev = self["_previous"][e] -- ./ecs/ecs.can:550 -local next -- ./ecs/ecs.can:551 -next = prev == true and self["_first"][2] or prev[2][2] -- ./ecs/ecs.can:551 -if prev == true then -- ./ecs/ecs.can:553 -self["_first"] = self["_first"][2] -- ./ecs/ecs.can:554 -else -- ./ecs/ecs.can:554 -prev[2] = next -- ./ecs/ecs.can:556 +local loaded, scene -- ./ecs/ecs.can:54 +if ... then -- ./ecs/ecs.can:55 +loaded, scene = pcall(require, (...):match("^(.-)ecs") .. "scene") -- ./ecs/ecs.can:55 +end -- ./ecs/ecs.can:55 +if not loaded then -- ./ecs/ecs.can:56 +scene = nil -- ./ecs/ecs.can:56 +end -- ./ecs/ecs.can:56 +local ecs -- ./ecs/ecs.can:58 +local recDestroySystems -- ./ecs/ecs.can:104 +recDestroySystems = function(system) -- ./ecs/ecs.can:104 +for i = # system["systems"], 1, - 1 do -- ./ecs/ecs.can:105 +local s -- ./ecs/ecs.can:106 +s = system["systems"][i] -- ./ecs/ecs.can:106 +recDestroySystems(s) -- ./ecs/ecs.can:107 +s:onDestroy() -- ./ecs/ecs.can:108 +system["systems"][i] = nil -- ./ecs/ecs.can:109 +if s["name"] then -- ./ecs/ecs.can:110 +system["world"]["s"][s["name"]] = nil -- ./ecs/ecs.can:111 +end -- ./ecs/ecs.can:111 +end -- ./ecs/ecs.can:111 +end -- ./ecs/ecs.can:111 +local recCallOnRemoveFromWorld -- ./ecs/ecs.can:116 +recCallOnRemoveFromWorld = function(world, systems) -- ./ecs/ecs.can:116 +for _, s in ipairs(systems) do -- ./ecs/ecs.can:117 +s:clear() -- ./ecs/ecs.can:118 +recCallOnRemoveFromWorld(world, s["systems"]) -- ./ecs/ecs.can:119 +s:onRemoveFromWorld(world) -- ./ecs/ecs.can:120 +end -- ./ecs/ecs.can:120 +end -- ./ecs/ecs.can:120 +local copy -- ./ecs/ecs.can:126 +copy = function(a, b, cache) -- ./ecs/ecs.can:126 +if cache == nil then cache = {} end -- ./ecs/ecs.can:126 +for k, v in pairs(a) do -- ./ecs/ecs.can:127 +if type(v) == "table" then -- ./ecs/ecs.can:128 +if b[k] == nil then -- ./ecs/ecs.can:129 +if cache[v] then -- ./ecs/ecs.can:130 +b[k] = cache[v] -- ./ecs/ecs.can:131 +else -- ./ecs/ecs.can:131 +cache[v] = {} -- ./ecs/ecs.can:133 +b[k] = cache[v] -- ./ecs/ecs.can:134 +copy(v, b[k], cache) -- ./ecs/ecs.can:135 +setmetatable(b[k], getmetatable(v)) -- ./ecs/ecs.can:136 +end -- ./ecs/ecs.can:136 +elseif type(b[k]) == "table" then -- ./ecs/ecs.can:138 +copy(v, b[k], cache) -- ./ecs/ecs.can:139 +end -- ./ecs/ecs.can:139 +elseif b[k] == nil then -- ./ecs/ecs.can:141 +b[k] = v -- ./ecs/ecs.can:142 +end -- ./ecs/ecs.can:142 +end -- ./ecs/ecs.can:142 +end -- ./ecs/ecs.can:142 +local head -- ./ecs/ecs.can:151 +head = {} -- ./ecs/ecs.can:151 +local skipNew -- ./ecs/ecs.can:154 +skipNew = function() -- ./ecs/ecs.can:154 +local s -- ./ecs/ecs.can:155 +s = { -- ./ecs/ecs.can:155 +["first"] = { -- ./ecs/ecs.can:158 +head, -- ./ecs/ecs.can:158 +nil -- ./ecs/ecs.can:158 +}, -- ./ecs/ecs.can:158 +["firstBase"] = nil, -- ./ecs/ecs.can:160 +["previous"] = { {} }, -- ./ecs/ecs.can:164 +["nLayers"] = 1, -- ./ecs/ecs.can:166 +["n"] = 0 -- ./ecs/ecs.can:168 +} -- ./ecs/ecs.can:168 +s["firstBase"] = s["first"] -- ./ecs/ecs.can:170 +return s -- ./ecs/ecs.can:171 +end -- ./ecs/ecs.can:171 +local nextEntity -- ./ecs/ecs.can:175 +nextEntity = function(s) -- ./ecs/ecs.can:175 +if s[1] then -- ./ecs/ecs.can:176 +local var -- ./ecs/ecs.can:177 +var = s[1][1] -- ./ecs/ecs.can:177 +s[1] = s[1][2] -- ./ecs/ecs.can:178 +return var -- ./ecs/ecs.can:179 +else -- ./ecs/ecs.can:179 +return nil -- ./ecs/ecs.can:181 +end -- ./ecs/ecs.can:181 +end -- ./ecs/ecs.can:181 +local skipIter -- ./ecs/ecs.can:186 +skipIter = function(self) -- ./ecs/ecs.can:186 +return nextEntity, { self["firstBase"][2] } -- ./ecs/ecs.can:187 +end -- ./ecs/ecs.can:187 +local skipAddLayers -- ./ecs/ecs.can:193 +skipAddLayers = function(self) -- ./ecs/ecs.can:193 +while self["n"] > 2 ^ self["nLayers"] do -- ./ecs/ecs.can:194 +self["first"] = { -- ./ecs/ecs.can:195 +head, -- ./ecs/ecs.can:195 +nil, -- ./ecs/ecs.can:195 +self["first"] -- ./ecs/ecs.can:195 +} -- ./ecs/ecs.can:195 +table["insert"](self["previous"], {}) -- ./ecs/ecs.can:196 +self["nLayers"] = self["nLayers"] + (1) -- ./ecs/ecs.can:197 +end -- ./ecs/ecs.can:197 +end -- ./ecs/ecs.can:197 +local coinFlip -- ./ecs/ecs.can:202 +coinFlip = function() -- ./ecs/ecs.can:202 +return math["random"](0, 1) == 1 -- ./ecs/ecs.can:203 +end -- ./ecs/ecs.can:203 +local skipInsert -- ./ecs/ecs.can:208 +skipInsert = function(self, system, e) -- ./ecs/ecs.can:208 +local prevLayer -- ./ecs/ecs.can:210 +prevLayer = {} -- ./ecs/ecs.can:210 +local prev -- ./ecs/ecs.can:211 +prev = self["first"] -- ./ecs/ecs.can:211 +for i = self["nLayers"], 1, - 1 do -- ./ecs/ecs.can:212 +while true do -- ./ecs/ecs.can:213 +if prev[2] == nil or system:compare(e, prev[2][1]) then -- ./ecs/ecs.can:215 +prevLayer[i] = prev -- ./ecs/ecs.can:216 +if prev[3] then -- ./ecs/ecs.can:218 +prev = prev[3] -- ./ecs/ecs.can:219 +break -- ./ecs/ecs.can:220 +end -- ./ecs/ecs.can:220 +break -- ./ecs/ecs.can:222 +else -- ./ecs/ecs.can:222 +prev = prev[2] -- ./ecs/ecs.can:225 +end -- ./ecs/ecs.can:225 +end -- ./ecs/ecs.can:225 +end -- ./ecs/ecs.can:225 +local inLowerLayer -- ./ecs/ecs.can:230 +for i = 1, self["nLayers"] do -- ./ecs/ecs.can:231 +prev = prevLayer[i] -- ./ecs/ecs.can:232 +if i == 1 or coinFlip() then -- ./ecs/ecs.can:233 +local nxt -- ./ecs/ecs.can:234 +nxt = prev[2] -- ./ecs/ecs.can:234 +prev[2] = { -- ./ecs/ecs.can:235 +e, -- ./ecs/ecs.can:235 +nxt, -- ./ecs/ecs.can:235 +inLowerLayer -- ./ecs/ecs.can:235 +} -- ./ecs/ecs.can:235 +self["previous"][i][e] = prev -- ./ecs/ecs.can:236 +if nxt then -- ./ecs/ecs.can:237 +self["previous"][i][nxt[1]] = prev[2] -- ./ecs/ecs.can:238 +end -- ./ecs/ecs.can:238 +inLowerLayer = prev[2] -- ./ecs/ecs.can:240 +else -- ./ecs/ecs.can:240 +break -- ./ecs/ecs.can:242 +end -- ./ecs/ecs.can:242 +end -- ./ecs/ecs.can:242 +self["n"] = self["n"] + (1) -- ./ecs/ecs.can:245 +end -- ./ecs/ecs.can:245 +local skipDelete -- ./ecs/ecs.can:251 +skipDelete = function(self, e) -- ./ecs/ecs.can:251 +for i = 1, self["nLayers"] do -- ./ecs/ecs.can:253 +local previous -- ./ecs/ecs.can:254 +previous = self["previous"][i] -- ./ecs/ecs.can:254 +if previous[e] then -- ./ecs/ecs.can:255 +local prev -- ./ecs/ecs.can:256 +prev = previous[e] -- ./ecs/ecs.can:256 +prev[2] = prev[2][2] -- ./ecs/ecs.can:257 +previous[e] = nil -- ./ecs/ecs.can:258 +if prev[2] then -- ./ecs/ecs.can:259 +previous[prev[2][1]] = prev -- ./ecs/ecs.can:260 +end -- ./ecs/ecs.can:260 +else -- ./ecs/ecs.can:260 +break -- ./ecs/ecs.can:263 +end -- ./ecs/ecs.can:263 +end -- ./ecs/ecs.can:263 +self["n"] = self["n"] - (1) -- ./ecs/ecs.can:266 +end -- ./ecs/ecs.can:266 +local skipReorder -- ./ecs/ecs.can:272 +skipReorder = function(self, system, e) -- ./ecs/ecs.can:272 +skipDelete(self, e) -- ./ecs/ecs.can:273 +skipInsert(self, system, e) -- ./ecs/ecs.can:274 +end -- ./ecs/ecs.can:274 +local skipIndex -- ./ecs/ecs.can:279 +skipIndex = function(self, i) -- ./ecs/ecs.can:279 +local n = 1 -- ./ecs/ecs.can:280 +for e in skipIter(self) do -- ./ecs/ecs.can:281 +if n == i then -- ./ecs/ecs.can:282 +return e -- ./ecs/ecs.can:283 +end -- ./ecs/ecs.can:283 +n = n + (1) -- ./ecs/ecs.can:285 +end -- ./ecs/ecs.can:285 +return nil -- ./ecs/ecs.can:287 +end -- ./ecs/ecs.can:287 +local system_mt -- ./ecs/ecs.can:351 +system_mt = { -- ./ecs/ecs.can:351 +["name"] = nil, -- ./ecs/ecs.can:365 +["systems"] = nil, -- ./ecs/ecs.can:373 +["interval"] = false, -- ./ecs/ecs.can:379 +["active"] = true, -- ./ecs/ecs.can:383 +["visible"] = true, -- ./ecs/ecs.can:387 +["component"] = nil, -- ./ecs/ecs.can:394 +["default"] = nil, -- ./ecs/ecs.can:404 +["filter"] = function(self, e) -- ./ecs/ecs.can:425 +return false -- ./ecs/ecs.can:425 +end, -- ./ecs/ecs.can:425 +["compare"] = function(self, e1, e2) -- ./ecs/ecs.can:437 +return true -- ./ecs/ecs.can:437 +end, -- ./ecs/ecs.can:437 +["onAdd"] = function(self, e, c) -- ./ecs/ecs.can:443 + -- ./ecs/ecs.can:443 +end, -- ./ecs/ecs.can:443 +["onRemove"] = function(self, e, c) -- ./ecs/ecs.can:448 + -- ./ecs/ecs.can:448 +end, -- ./ecs/ecs.can:448 +["onInstance"] = function(self) -- ./ecs/ecs.can:451 + -- ./ecs/ecs.can:451 +end, -- ./ecs/ecs.can:451 +["onAddToWorld"] = function(self, world) -- ./ecs/ecs.can:455 + -- ./ecs/ecs.can:455 +end, -- ./ecs/ecs.can:455 +["onRemoveFromWorld"] = function(self, world) -- ./ecs/ecs.can:459 + -- ./ecs/ecs.can:459 +end, -- ./ecs/ecs.can:459 +["onDestroy"] = function(self) -- ./ecs/ecs.can:462 + -- ./ecs/ecs.can:462 +end, -- ./ecs/ecs.can:462 +["onUpdate"] = function(self, dt) -- ./ecs/ecs.can:467 + -- ./ecs/ecs.can:467 +end, -- ./ecs/ecs.can:467 +["onDraw"] = function(self) -- ./ecs/ecs.can:471 + -- ./ecs/ecs.can:471 +end, -- ./ecs/ecs.can:471 +["process"] = function(self, e, c, dt) -- ./ecs/ecs.can:478 + -- ./ecs/ecs.can:478 +end, -- ./ecs/ecs.can:478 +["render"] = function(self, e, c) -- ./ecs/ecs.can:484 + -- ./ecs/ecs.can:484 +end, -- ./ecs/ecs.can:484 +["onUpdateEnd"] = function(self, dt) -- ./ecs/ecs.can:489 + -- ./ecs/ecs.can:489 +end, -- ./ecs/ecs.can:489 +["onDrawEnd"] = function(self) -- ./ecs/ecs.can:493 + -- ./ecs/ecs.can:493 +end, -- ./ecs/ecs.can:493 +["world"] = nil, -- ./ecs/ecs.can:503 +["w"] = nil, -- ./ecs/ecs.can:507 +["entityCount"] = 0, -- ./ecs/ecs.can:511 +["s"] = nil, -- ./ecs/ecs.can:515 +["_has"] = nil, -- ./ecs/ecs.can:523 +["_waited"] = 0, -- ./ecs/ecs.can:527 +["add"] = function(self, e, ...) -- ./ecs/ecs.can:548 +if e ~= nil and not self["_has"][e] and self:filter(e) then -- ./ecs/ecs.can:549 +if self["component"] and self["default"] then -- ./ecs/ecs.can:551 +copy({ [self["component"]] = self["default"] }, e) -- ./ecs/ecs.can:552 +end -- ./ecs/ecs.can:552 +if self["compare"] ~= system_mt["compare"] then -- ./ecs/ecs.can:555 +skipAddLayers(self["_skiplist"]) -- ./ecs/ecs.can:556 end -- ./ecs/ecs.can:556 -if next then -- ./ecs/ecs.can:558 -self["_previous"][next[1]] = prev -- ./ecs/ecs.can:559 -end -- ./ecs/ecs.can:559 -while prev ~= true and self:compare(e, prev[1]) do -- ./ecs/ecs.can:562 -next = prev -- ./ecs/ecs.can:563 -prev = self["_previous"][prev[1]] -- ./ecs/ecs.can:564 -end -- ./ecs/ecs.can:564 -while next ~= nil and not self:compare(e, next[1]) do -- ./ecs/ecs.can:566 -prev = next -- ./ecs/ecs.can:567 -next = next[2] -- ./ecs/ecs.can:568 -end -- ./ecs/ecs.can:568 -local new -- ./ecs/ecs.can:571 -new = { -- ./ecs/ecs.can:571 -e, -- ./ecs/ecs.can:571 -next -- ./ecs/ecs.can:571 -} -- ./ecs/ecs.can:571 -self["_previous"][e] = prev -- ./ecs/ecs.can:572 -if next then -- ./ecs/ecs.can:573 -self["_previous"][next[1]] = new -- ./ecs/ecs.can:574 -end -- ./ecs/ecs.can:574 -if prev == true then -- ./ecs/ecs.can:576 -self["_first"] = new -- ./ecs/ecs.can:577 -else -- ./ecs/ecs.can:577 -prev[2] = new -- ./ecs/ecs.can:579 -end -- ./ecs/ecs.can:579 -for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:582 -s:reorder(e) -- ./ecs/ecs.can:583 -end -- ./ecs/ecs.can:583 -end -- ./ecs/ecs.can:583 -end -- ./ecs/ecs.can:583 -if ... then -- ./ecs/ecs.can:587 -return e, self:reorder(...) -- ./ecs/ecs.can:588 -else -- ./ecs/ecs.can:588 -return e -- ./ecs/ecs.can:590 -end -- ./ecs/ecs.can:590 -end, -- ./ecs/ecs.can:590 -["has"] = function(self, e, ...) -- ./ecs/ecs.can:599 -local has -- ./ecs/ecs.can:600 -has = e == nil or not not self["_previous"][e] -- ./ecs/ecs.can:600 -if ... then -- ./ecs/ecs.can:601 -return has and self:has(...) -- ./ecs/ecs.can:602 -else -- ./ecs/ecs.can:602 -return has -- ./ecs/ecs.can:604 -end -- ./ecs/ecs.can:604 -end, -- ./ecs/ecs.can:604 -["iter"] = function(self) -- ./ecs/ecs.can:609 -return nextEntity, { self["_first"] } -- ./ecs/ecs.can:610 -end, -- ./ecs/ecs.can:610 -["get"] = function(self, i) -- ./ecs/ecs.can:617 -local n = 1 -- ./ecs/ecs.can:618 -for e in self:iter() do -- ./ecs/ecs.can:619 -if n == i then -- ./ecs/ecs.can:620 -return e -- ./ecs/ecs.can:621 -end -- ./ecs/ecs.can:621 -n = n + (1) -- ./ecs/ecs.can:623 -end -- ./ecs/ecs.can:623 -return nil -- ./ecs/ecs.can:625 -end, -- ./ecs/ecs.can:625 -["clear"] = function(self) -- ./ecs/ecs.can:628 -for e in self:iter() do -- ./ecs/ecs.can:629 -self:remove(e) -- ./ecs/ecs.can:630 -end -- ./ecs/ecs.can:630 -for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:632 -s:clear() -- ./ecs/ecs.can:633 -end -- ./ecs/ecs.can:633 -end, -- ./ecs/ecs.can:633 -["update"] = function(self, dt) -- ./ecs/ecs.can:640 -if self["active"] then -- ./ecs/ecs.can:641 -if self["interval"] then -- ./ecs/ecs.can:642 -self["_waited"] = self["_waited"] + (dt) -- ./ecs/ecs.can:643 -if self["_waited"] < self["interval"] then -- ./ecs/ecs.can:644 -return -- ./ecs/ecs.can:645 -end -- ./ecs/ecs.can:645 -end -- ./ecs/ecs.can:645 -self:onUpdate(dt) -- ./ecs/ecs.can:648 -if self["process"] ~= system_mt["process"] then -- ./ecs/ecs.can:649 -for e in self:iter() do -- ./ecs/ecs.can:650 -self:process(e, e[self["component"]], dt) -- ./ecs/ecs.can:651 -end -- ./ecs/ecs.can:651 -end -- ./ecs/ecs.can:651 -for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:654 -s:update(dt) -- ./ecs/ecs.can:655 -end -- ./ecs/ecs.can:655 -self:onUpdateEnd(dt) -- ./ecs/ecs.can:657 -if self["interval"] then -- ./ecs/ecs.can:658 -self["_waited"] = self["_waited"] - (self["interval"]) -- ./ecs/ecs.can:659 -end -- ./ecs/ecs.can:659 -end -- ./ecs/ecs.can:659 -end, -- ./ecs/ecs.can:659 -["draw"] = function(self) -- ./ecs/ecs.can:666 -if self["visible"] then -- ./ecs/ecs.can:667 -self:onDraw() -- ./ecs/ecs.can:668 -if self["render"] ~= system_mt["render"] then -- ./ecs/ecs.can:669 -for e in self:iter() do -- ./ecs/ecs.can:670 -self:render(e, e[self["component"]]) -- ./ecs/ecs.can:671 -end -- ./ecs/ecs.can:671 -end -- ./ecs/ecs.can:671 -for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:674 -s:draw() -- ./ecs/ecs.can:675 -end -- ./ecs/ecs.can:675 -self:onDrawEnd() -- ./ecs/ecs.can:677 -end -- ./ecs/ecs.can:677 -end, -- ./ecs/ecs.can:677 -["callback"] = function(self, name, e, ...) -- ./ecs/ecs.can:690 -if self["_previous"][e] and self[name] then -- ./ecs/ecs.can:692 -self[name](self, e, e[self["component"]], ...) -- ./ecs/ecs.can:693 -end -- ./ecs/ecs.can:693 -if self["_previous"][e] then -- ./ecs/ecs.can:696 -for _, ss in ipairs(self["systems"]) do -- ./ecs/ecs.can:697 -ss:callback(name, e, ...) -- ./ecs/ecs.can:698 -end -- ./ecs/ecs.can:698 +skipInsert(self["_skiplist"], self, e) -- ./ecs/ecs.can:559 +self["entityCount"] = self["entityCount"] + (1) -- ./ecs/ecs.can:561 +self:onAdd(e, e[self["component"]]) -- ./ecs/ecs.can:562 +if self["_has"][e] then -- ./ecs/ecs.can:564 +for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:565 +s:add(e) -- ./ecs/ecs.can:566 +end -- ./ecs/ecs.can:566 +end -- ./ecs/ecs.can:566 +end -- ./ecs/ecs.can:566 +if ... then -- ./ecs/ecs.can:570 +return e, self:add(...) -- ./ecs/ecs.can:571 +else -- ./ecs/ecs.can:571 +return e -- ./ecs/ecs.can:573 +end -- ./ecs/ecs.can:573 +end, -- ./ecs/ecs.can:573 +["remove"] = function(self, e, ...) -- ./ecs/ecs.can:588 +if e ~= nil then -- ./ecs/ecs.can:589 +if self["_has"][e] then -- ./ecs/ecs.can:590 +for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:592 +s:remove(e) -- ./ecs/ecs.can:593 +end -- ./ecs/ecs.can:593 +end -- ./ecs/ecs.can:593 +if self["_has"][e] then -- ./ecs/ecs.can:596 +skipDelete(self["_skiplist"], e) -- ./ecs/ecs.can:597 +self["entityCount"] = self["entityCount"] - (1) -- ./ecs/ecs.can:599 +self:onRemove(e, e[self["component"]]) -- ./ecs/ecs.can:600 +end -- ./ecs/ecs.can:600 +end -- ./ecs/ecs.can:600 +if ... then -- ./ecs/ecs.can:603 +return e, self:remove(...) -- ./ecs/ecs.can:604 +else -- ./ecs/ecs.can:604 +return e -- ./ecs/ecs.can:606 +end -- ./ecs/ecs.can:606 +end, -- ./ecs/ecs.can:606 +["refresh"] = function(self, e, ...) -- ./ecs/ecs.can:618 +if e ~= nil then -- ./ecs/ecs.can:619 +if not self["_has"][e] then -- ./ecs/ecs.can:620 +self:add(e) -- ./ecs/ecs.can:621 +else -- ./ecs/ecs.can:621 +if not self:filter(e) then -- ./ecs/ecs.can:623 +self:remove(e) -- ./ecs/ecs.can:624 +else -- ./ecs/ecs.can:624 +for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:626 +s:refresh(e) -- ./ecs/ecs.can:627 +end -- ./ecs/ecs.can:627 +end -- ./ecs/ecs.can:627 +end -- ./ecs/ecs.can:627 +end -- ./ecs/ecs.can:627 +if ... then -- ./ecs/ecs.can:632 +return e, self:refresh(...) -- ./ecs/ecs.can:633 +else -- ./ecs/ecs.can:633 +return e -- ./ecs/ecs.can:635 +end -- ./ecs/ecs.can:635 +end, -- ./ecs/ecs.can:635 +["reorder"] = function(self, e, ...) -- ./ecs/ecs.can:647 +if e ~= nil and self["_has"][e] then -- ./ecs/ecs.can:648 +skipReorder(self["_skiplist"], self, e) -- ./ecs/ecs.can:649 +for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:651 +s:reorder(e) -- ./ecs/ecs.can:652 +end -- ./ecs/ecs.can:652 +end -- ./ecs/ecs.can:652 +if ... then -- ./ecs/ecs.can:655 +return e, self:reorder(...) -- ./ecs/ecs.can:656 +else -- ./ecs/ecs.can:656 +return e -- ./ecs/ecs.can:658 +end -- ./ecs/ecs.can:658 +end, -- ./ecs/ecs.can:658 +["has"] = function(self, e, ...) -- ./ecs/ecs.can:667 +local has -- ./ecs/ecs.can:668 +has = e == nil or not not self["_has"][e] -- ./ecs/ecs.can:668 +if ... then -- ./ecs/ecs.can:669 +return has and self:has(...) -- ./ecs/ecs.can:670 +else -- ./ecs/ecs.can:670 +return has -- ./ecs/ecs.can:672 +end -- ./ecs/ecs.can:672 +end, -- ./ecs/ecs.can:672 +["iter"] = function(self) -- ./ecs/ecs.can:679 +return skipIter(self["_skiplist"]) -- ./ecs/ecs.can:680 +end, -- ./ecs/ecs.can:680 +["get"] = function(self, i) -- ./ecs/ecs.can:687 +return skipIndex(self["_skiplist"], i) -- ./ecs/ecs.can:688 +end, -- ./ecs/ecs.can:688 +["clear"] = function(self) -- ./ecs/ecs.can:693 +for e in skipIter(self["_skiplist"]) do -- ./ecs/ecs.can:694 +self:remove(e) -- ./ecs/ecs.can:695 +end -- ./ecs/ecs.can:695 +for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:697 +s:clear() -- ./ecs/ecs.can:698 end -- ./ecs/ecs.can:698 end, -- ./ecs/ecs.can:698 -["emit"] = function(self, name, ...) -- ./ecs/ecs.can:723 -local status -- ./ecs/ecs.can:725 -if self[name] then -- ./ecs/ecs.can:726 -status = self[name](self, ...) -- ./ecs/ecs.can:727 -end -- ./ecs/ecs.can:727 -if status ~= "stop" and status ~= "capture" then -- ./ecs/ecs.can:730 -for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:731 -status = s:emit(name, ...) -- ./ecs/ecs.can:732 -if status == "capture" then -- ./ecs/ecs.can:733 -break -- ./ecs/ecs.can:733 -end -- ./ecs/ecs.can:733 -end -- ./ecs/ecs.can:733 -end -- ./ecs/ecs.can:733 -return status -- ./ecs/ecs.can:736 -end, -- ./ecs/ecs.can:736 -["destroy"] = function(self) -- ./ecs/ecs.can:739 -recCallOnRemoveFromWorld(self["world"], { self }) -- ./ecs/ecs.can:740 -recDestroySystems({ ["systems"] = { self } }) -- ./ecs/ecs.can:741 -end -- ./ecs/ecs.can:741 -} -- ./ecs/ecs.can:741 -local alwaysTrue -- ./ecs/ecs.can:746 -alwaysTrue = function() -- ./ecs/ecs.can:746 -return true -- ./ecs/ecs.can:746 +["update"] = function(self, dt) -- ./ecs/ecs.can:707 +if self["active"] then -- ./ecs/ecs.can:708 +if self["interval"] then -- ./ecs/ecs.can:709 +self["_waited"] = self["_waited"] + (dt) -- ./ecs/ecs.can:710 +if self["_waited"] < self["interval"] then -- ./ecs/ecs.can:711 +return -- ./ecs/ecs.can:712 +end -- ./ecs/ecs.can:712 +end -- ./ecs/ecs.can:712 +self:onUpdate(dt) -- ./ecs/ecs.can:715 +if self["process"] ~= system_mt["process"] then -- ./ecs/ecs.can:716 +for e in skipIter(self["_skiplist"]) do -- ./ecs/ecs.can:717 +self:process(e, e[self["component"]], dt) -- ./ecs/ecs.can:718 +end -- ./ecs/ecs.can:718 +end -- ./ecs/ecs.can:718 +for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:721 +s:update(dt) -- ./ecs/ecs.can:722 +end -- ./ecs/ecs.can:722 +self:onUpdateEnd(dt) -- ./ecs/ecs.can:724 +if self["interval"] then -- ./ecs/ecs.can:725 +self["_waited"] = self["_waited"] - (self["interval"]) -- ./ecs/ecs.can:726 +end -- ./ecs/ecs.can:726 +end -- ./ecs/ecs.can:726 +end, -- ./ecs/ecs.can:726 +["draw"] = function(self) -- ./ecs/ecs.can:735 +if self["visible"] then -- ./ecs/ecs.can:736 +self:onDraw() -- ./ecs/ecs.can:737 +if self["render"] ~= system_mt["render"] then -- ./ecs/ecs.can:738 +for e in skipIter(self["_skiplist"]) do -- ./ecs/ecs.can:739 +self:render(e, e[self["component"]]) -- ./ecs/ecs.can:740 +end -- ./ecs/ecs.can:740 +end -- ./ecs/ecs.can:740 +for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:743 +s:draw() -- ./ecs/ecs.can:744 +end -- ./ecs/ecs.can:744 +self:onDrawEnd() -- ./ecs/ecs.can:746 end -- ./ecs/ecs.can:746 -local alwaysFalse -- ./ecs/ecs.can:747 -alwaysFalse = function() -- ./ecs/ecs.can:747 -return false -- ./ecs/ecs.can:747 -end -- ./ecs/ecs.can:747 -local recInstanciateSystems -- ./ecs/ecs.can:752 -recInstanciateSystems = function(world, systems) -- ./ecs/ecs.can:752 -local t -- ./ecs/ecs.can:753 -t = {} -- ./ecs/ecs.can:753 -for _, s in ipairs(systems) do -- ./ecs/ecs.can:754 -local system -- ./ecs/ecs.can:755 -system = setmetatable({ -- ./ecs/ecs.can:757 -["systems"] = recInstanciateSystems(world, s["systems"] or {}), -- ./ecs/ecs.can:758 -["world"] = world, -- ./ecs/ecs.can:759 -["w"] = world, -- ./ecs/ecs.can:760 -["s"] = world["s"], -- ./ecs/ecs.can:761 -["_previous"] = {} -- ./ecs/ecs.can:762 -}, { ["__index"] = function(self, k) -- ./ecs/ecs.can:764 -if s[k] ~= nil then -- ./ecs/ecs.can:765 -return s[k] -- ./ecs/ecs.can:766 -else -- ./ecs/ecs.can:766 -return system_mt[k] -- ./ecs/ecs.can:768 -end -- ./ecs/ecs.can:768 -end }) -- ./ecs/ecs.can:768 -if type(s["filter"]) == "string" then -- ./ecs/ecs.can:773 -system["filter"] = function(_, e) -- ./ecs/ecs.can:774 -return e[s["filter"]] ~= nil -- ./ecs/ecs.can:774 -end -- ./ecs/ecs.can:774 -elseif type(s["filter"]) == "table" then -- ./ecs/ecs.can:775 -system["filter"] = ecs["all"](unpack(s["filter"])) -- ./ecs/ecs.can:776 -elseif type(s["filter"]) == "boolean" then -- ./ecs/ecs.can:777 -if s["filter"] then -- ./ecs/ecs.can:778 -system["filter"] = alwaysTrue -- ./ecs/ecs.can:779 -else -- ./ecs/ecs.can:779 -system["filter"] = alwaysFalse -- ./ecs/ecs.can:781 -end -- ./ecs/ecs.can:781 -end -- ./ecs/ecs.can:781 -if not s["component"] and s["name"] then -- ./ecs/ecs.can:785 -s["component"] = s["name"] -- ./ecs/ecs.can:786 -end -- ./ecs/ecs.can:786 -table["insert"](t, system) -- ./ecs/ecs.can:789 -if s["name"] then -- ./ecs/ecs.can:790 -world["s"][s["name"]] = system -- ./ecs/ecs.can:791 -end -- ./ecs/ecs.can:791 -system:onInstance() -- ./ecs/ecs.can:793 -end -- ./ecs/ecs.can:793 -return t -- ./ecs/ecs.can:795 -end -- ./ecs/ecs.can:795 -local recCallOnAddToWorld -- ./ecs/ecs.can:798 -recCallOnAddToWorld = function(world, systems) -- ./ecs/ecs.can:798 -for _, s in ipairs(systems) do -- ./ecs/ecs.can:799 -recCallOnAddToWorld(world, s["systems"]) -- ./ecs/ecs.can:800 -s:onAddToWorld(world) -- ./ecs/ecs.can:801 -end -- ./ecs/ecs.can:801 -end -- ./ecs/ecs.can:801 -ecs = { -- ./ecs/ecs.can:807 -["world"] = function(...) -- ./ecs/ecs.can:812 -local world -- ./ecs/ecs.can:813 -world = setmetatable({ -- ./ecs/ecs.can:813 -["filter"] = ecs["all"](), -- ./ecs/ecs.can:814 -["s"] = {}, -- ./ecs/ecs.can:815 -["_previous"] = {} -- ./ecs/ecs.can:816 -}, { ["__index"] = system_mt }) -- ./ecs/ecs.can:817 -world["world"] = world -- ./ecs/ecs.can:818 -world["w"] = world -- ./ecs/ecs.can:819 -world["systems"] = recInstanciateSystems(world, { ... }) -- ./ecs/ecs.can:820 -recCallOnAddToWorld(world, world["systems"]) -- ./ecs/ecs.can:821 -return world -- ./ecs/ecs.can:822 -end, -- ./ecs/ecs.can:822 -["all"] = function(...) -- ./ecs/ecs.can:828 -if ... then -- ./ecs/ecs.can:829 -local l -- ./ecs/ecs.can:830 -l = { ... } -- ./ecs/ecs.can:830 -return function(s, e) -- ./ecs/ecs.can:831 -for _, k in ipairs(l) do -- ./ecs/ecs.can:832 -if e[k] == nil then -- ./ecs/ecs.can:833 -return false -- ./ecs/ecs.can:834 -end -- ./ecs/ecs.can:834 -end -- ./ecs/ecs.can:834 -return true -- ./ecs/ecs.can:837 -end -- ./ecs/ecs.can:837 -else -- ./ecs/ecs.can:837 -return alwaysTrue -- ./ecs/ecs.can:840 -end -- ./ecs/ecs.can:840 -end, -- ./ecs/ecs.can:840 -["any"] = function(...) -- ./ecs/ecs.can:847 -if ... then -- ./ecs/ecs.can:848 -local l -- ./ecs/ecs.can:849 -l = { ... } -- ./ecs/ecs.can:849 -return function(s, e) -- ./ecs/ecs.can:850 -for _, k in ipairs(l) do -- ./ecs/ecs.can:851 -if e[k] ~= nil then -- ./ecs/ecs.can:852 -return true -- ./ecs/ecs.can:853 -end -- ./ecs/ecs.can:853 -end -- ./ecs/ecs.can:853 -return false -- ./ecs/ecs.can:856 -end -- ./ecs/ecs.can:856 -else -- ./ecs/ecs.can:856 -return alwaysFalse -- ./ecs/ecs.can:859 -end -- ./ecs/ecs.can:859 -end, -- ./ecs/ecs.can:859 -["scene"] = function(name, systems, entities) -- ./ecs/ecs.can:877 -if systems == nil then systems = {} end -- ./ecs/ecs.can:877 -if entities == nil then entities = {} end -- ./ecs/ecs.can:877 -assert(scene, "ubiquitousse.scene unavailable") -- ./ecs/ecs.can:878 -local s -- ./ecs/ecs.can:879 -s = scene["new"](name) -- ./ecs/ecs.can:879 -local w -- ./ecs/ecs.can:880 -s["enter"] = function(self) -- ./ecs/ecs.can:882 -local sys, ent = systems, entities -- ./ecs/ecs.can:883 -if type(systems) == "function" then -- ./ecs/ecs.can:884 -sys = { systems() } -- ./ecs/ecs.can:884 -end -- ./ecs/ecs.can:884 -if type(entities) == "function" then -- ./ecs/ecs.can:885 -ent = { entities() } -- ./ecs/ecs.can:885 -end -- ./ecs/ecs.can:885 -w = ecs["world"](unpack(sys)) -- ./ecs/ecs.can:886 -w:add(unpack(ent)) -- ./ecs/ecs.can:887 -end -- ./ecs/ecs.can:887 -s["exit"] = function(self) -- ./ecs/ecs.can:889 -w:destroy() -- ./ecs/ecs.can:890 -end -- ./ecs/ecs.can:890 -s["suspend"] = function(self) -- ./ecs/ecs.can:892 -w:emit("onSuspend") -- ./ecs/ecs.can:893 -end -- ./ecs/ecs.can:893 -s["resume"] = function(self) -- ./ecs/ecs.can:895 -w:emit("onResume") -- ./ecs/ecs.can:896 -end -- ./ecs/ecs.can:896 -s["update"] = function(self, dt) -- ./ecs/ecs.can:898 -w:update(dt) -- ./ecs/ecs.can:899 -end -- ./ecs/ecs.can:899 -s["draw"] = function(self) -- ./ecs/ecs.can:901 -w:draw() -- ./ecs/ecs.can:902 -end -- ./ecs/ecs.can:902 -return s -- ./ecs/ecs.can:905 -end -- ./ecs/ecs.can:905 -} -- ./ecs/ecs.can:905 -return ecs -- ./ecs/ecs.can:909 +end, -- ./ecs/ecs.can:746 +["callback"] = function(self, name, e, ...) -- ./ecs/ecs.can:761 +if self["_has"][e] and self[name] then -- ./ecs/ecs.can:763 +self[name](self, e, e[self["component"]], ...) -- ./ecs/ecs.can:764 +end -- ./ecs/ecs.can:764 +if self["_has"][e] then -- ./ecs/ecs.can:767 +for _, ss in ipairs(self["systems"]) do -- ./ecs/ecs.can:768 +ss:callback(name, e, ...) -- ./ecs/ecs.can:769 +end -- ./ecs/ecs.can:769 +end -- ./ecs/ecs.can:769 +end, -- ./ecs/ecs.can:769 +["emit"] = function(self, name, ...) -- ./ecs/ecs.can:795 +local status -- ./ecs/ecs.can:797 +if self[name] then -- ./ecs/ecs.can:798 +status = self[name](self, ...) -- ./ecs/ecs.can:799 +end -- ./ecs/ecs.can:799 +if status ~= "stop" and status ~= "capture" then -- ./ecs/ecs.can:802 +for _, s in ipairs(self["systems"]) do -- ./ecs/ecs.can:803 +status = s:emit(name, ...) -- ./ecs/ecs.can:804 +if status == "capture" then -- ./ecs/ecs.can:805 +break -- ./ecs/ecs.can:805 +end -- ./ecs/ecs.can:805 +end -- ./ecs/ecs.can:805 +end -- ./ecs/ecs.can:805 +return status -- ./ecs/ecs.can:808 +end, -- ./ecs/ecs.can:808 +["destroy"] = function(self) -- ./ecs/ecs.can:812 +recCallOnRemoveFromWorld(self["world"], { self }) -- ./ecs/ecs.can:813 +recDestroySystems({ ["systems"] = { self } }) -- ./ecs/ecs.can:814 +end -- ./ecs/ecs.can:814 +} -- ./ecs/ecs.can:814 +local alwaysTrue -- ./ecs/ecs.can:819 +alwaysTrue = function() -- ./ecs/ecs.can:819 +return true -- ./ecs/ecs.can:819 +end -- ./ecs/ecs.can:819 +local alwaysFalse -- ./ecs/ecs.can:820 +alwaysFalse = function() -- ./ecs/ecs.can:820 +return false -- ./ecs/ecs.can:820 +end -- ./ecs/ecs.can:820 +local recInstanciateSystems -- ./ecs/ecs.can:825 +recInstanciateSystems = function(world, systems) -- ./ecs/ecs.can:825 +local t -- ./ecs/ecs.can:826 +t = {} -- ./ecs/ecs.can:826 +for _, s in ipairs(systems) do -- ./ecs/ecs.can:827 +local system -- ./ecs/ecs.can:828 +system = setmetatable({ -- ./ecs/ecs.can:830 +["systems"] = recInstanciateSystems(world, s["systems"] or {}), -- ./ecs/ecs.can:831 +["world"] = world, -- ./ecs/ecs.can:832 +["w"] = world, -- ./ecs/ecs.can:833 +["s"] = world["s"], -- ./ecs/ecs.can:834 +["_skiplist"] = skipNew() -- ./ecs/ecs.can:835 +}, { ["__index"] = function(self, k) -- ./ecs/ecs.can:837 +if s[k] ~= nil then -- ./ecs/ecs.can:838 +return s[k] -- ./ecs/ecs.can:839 +else -- ./ecs/ecs.can:839 +return system_mt[k] -- ./ecs/ecs.can:841 +end -- ./ecs/ecs.can:841 +end }) -- ./ecs/ecs.can:841 +system["_has"] = system["_skiplist"]["previous"][1] -- ./ecs/ecs.can:845 +if type(s["filter"]) == "string" then -- ./ecs/ecs.can:847 +system["filter"] = function(_, e) -- ./ecs/ecs.can:848 +return e[s["filter"]] ~= nil -- ./ecs/ecs.can:848 +end -- ./ecs/ecs.can:848 +elseif type(s["filter"]) == "table" then -- ./ecs/ecs.can:849 +system["filter"] = ecs["all"](unpack(s["filter"])) -- ./ecs/ecs.can:850 +elseif type(s["filter"]) == "boolean" then -- ./ecs/ecs.can:851 +if s["filter"] then -- ./ecs/ecs.can:852 +system["filter"] = alwaysTrue -- ./ecs/ecs.can:853 +else -- ./ecs/ecs.can:853 +system["filter"] = alwaysFalse -- ./ecs/ecs.can:855 +end -- ./ecs/ecs.can:855 +end -- ./ecs/ecs.can:855 +if not s["component"] and s["name"] then -- ./ecs/ecs.can:859 +s["component"] = s["name"] -- ./ecs/ecs.can:860 +end -- ./ecs/ecs.can:860 +table["insert"](t, system) -- ./ecs/ecs.can:863 +if s["name"] then -- ./ecs/ecs.can:864 +world["s"][s["name"]] = system -- ./ecs/ecs.can:865 +end -- ./ecs/ecs.can:865 +system:onInstance() -- ./ecs/ecs.can:867 +end -- ./ecs/ecs.can:867 +return t -- ./ecs/ecs.can:869 +end -- ./ecs/ecs.can:869 +local recCallOnAddToWorld -- ./ecs/ecs.can:872 +recCallOnAddToWorld = function(world, systems) -- ./ecs/ecs.can:872 +for _, s in ipairs(systems) do -- ./ecs/ecs.can:873 +recCallOnAddToWorld(world, s["systems"]) -- ./ecs/ecs.can:874 +s:onAddToWorld(world) -- ./ecs/ecs.can:875 +end -- ./ecs/ecs.can:875 +end -- ./ecs/ecs.can:875 +ecs = { -- ./ecs/ecs.can:881 +["world"] = function(...) -- ./ecs/ecs.can:886 +local world -- ./ecs/ecs.can:887 +world = setmetatable({ -- ./ecs/ecs.can:887 +["filter"] = ecs["all"](), -- ./ecs/ecs.can:888 +["s"] = {}, -- ./ecs/ecs.can:889 +["_skiplist"] = skipNew() -- ./ecs/ecs.can:890 +}, { ["__index"] = system_mt }) -- ./ecs/ecs.can:891 +world["_has"] = world["_skiplist"]["previous"][1] -- ./ecs/ecs.can:892 +world["world"] = world -- ./ecs/ecs.can:893 +world["w"] = world -- ./ecs/ecs.can:894 +world["systems"] = recInstanciateSystems(world, { ... }) -- ./ecs/ecs.can:895 +recCallOnAddToWorld(world, world["systems"]) -- ./ecs/ecs.can:896 +return world -- ./ecs/ecs.can:897 +end, -- ./ecs/ecs.can:897 +["all"] = function(...) -- ./ecs/ecs.can:903 +if ... then -- ./ecs/ecs.can:904 +local l -- ./ecs/ecs.can:905 +l = { ... } -- ./ecs/ecs.can:905 +return function(s, e) -- ./ecs/ecs.can:906 +for _, k in ipairs(l) do -- ./ecs/ecs.can:907 +if e[k] == nil then -- ./ecs/ecs.can:908 +return false -- ./ecs/ecs.can:909 +end -- ./ecs/ecs.can:909 +end -- ./ecs/ecs.can:909 +return true -- ./ecs/ecs.can:912 +end -- ./ecs/ecs.can:912 +else -- ./ecs/ecs.can:912 +return alwaysTrue -- ./ecs/ecs.can:915 +end -- ./ecs/ecs.can:915 +end, -- ./ecs/ecs.can:915 +["any"] = function(...) -- ./ecs/ecs.can:922 +if ... then -- ./ecs/ecs.can:923 +local l -- ./ecs/ecs.can:924 +l = { ... } -- ./ecs/ecs.can:924 +return function(s, e) -- ./ecs/ecs.can:925 +for _, k in ipairs(l) do -- ./ecs/ecs.can:926 +if e[k] ~= nil then -- ./ecs/ecs.can:927 +return true -- ./ecs/ecs.can:928 +end -- ./ecs/ecs.can:928 +end -- ./ecs/ecs.can:928 +return false -- ./ecs/ecs.can:931 +end -- ./ecs/ecs.can:931 +else -- ./ecs/ecs.can:931 +return alwaysFalse -- ./ecs/ecs.can:934 +end -- ./ecs/ecs.can:934 +end, -- ./ecs/ecs.can:934 +["scene"] = function(name, systems, entities) -- ./ecs/ecs.can:952 +if systems == nil then systems = {} end -- ./ecs/ecs.can:952 +if entities == nil then entities = {} end -- ./ecs/ecs.can:952 +assert(scene, "ubiquitousse.scene unavailable") -- ./ecs/ecs.can:953 +local s -- ./ecs/ecs.can:954 +s = scene["new"](name) -- ./ecs/ecs.can:954 +local w -- ./ecs/ecs.can:955 +s["enter"] = function(self) -- ./ecs/ecs.can:957 +local sys, ent = systems, entities -- ./ecs/ecs.can:958 +if type(systems) == "function" then -- ./ecs/ecs.can:959 +sys = { systems() } -- ./ecs/ecs.can:959 +end -- ./ecs/ecs.can:959 +if type(entities) == "function" then -- ./ecs/ecs.can:960 +ent = { entities() } -- ./ecs/ecs.can:960 +end -- ./ecs/ecs.can:960 +w = ecs["world"](unpack(sys)) -- ./ecs/ecs.can:961 +w:add(unpack(ent)) -- ./ecs/ecs.can:962 +end -- ./ecs/ecs.can:962 +s["exit"] = function(self) -- ./ecs/ecs.can:964 +w:destroy() -- ./ecs/ecs.can:965 +end -- ./ecs/ecs.can:965 +s["suspend"] = function(self) -- ./ecs/ecs.can:967 +w:emit("onSuspend") -- ./ecs/ecs.can:968 +end -- ./ecs/ecs.can:968 +s["resume"] = function(self) -- ./ecs/ecs.can:970 +w:emit("onResume") -- ./ecs/ecs.can:971 +end -- ./ecs/ecs.can:971 +s["update"] = function(self, dt) -- ./ecs/ecs.can:973 +w:update(dt) -- ./ecs/ecs.can:974 +end -- ./ecs/ecs.can:974 +s["draw"] = function(self) -- ./ecs/ecs.can:976 +w:draw() -- ./ecs/ecs.can:977 +end -- ./ecs/ecs.can:977 +return s -- ./ecs/ecs.can:980 +end -- ./ecs/ecs.can:980 +} -- ./ecs/ecs.can:980 +return ecs -- ./ecs/ecs.can:984
    Set the state of this input and its children to a neutral position (i.e.
    Input:set (...)Manually set the state of this input.
    Input:setJoystick (joystick) Set the joystick associated with this input.