Full Code of nidorx/ecs-lua for AI

master c6be4a85b13c cached
127 files
947.8 KB
258.9k tokens
1 requests
Download .txt
Showing preview only (993K chars total). Download the full file or copy to clipboard to get everything.
Repository: nidorx/ecs-lua
Branch: master
Commit: c6be4a85b13c
Files: 127
Total size: 947.8 KB

Directory structure:
gitextract_abg99oc9/

├── .editorconfig
├── .gitignore
├── .luacov
├── .travis.yml
├── CONTRIBUTING.md
├── ECS.lua
├── ECS_concat.lua
├── LICENSE
├── README.md
├── build.lua
├── docs/
│   ├── .nojekyll
│   ├── README.md
│   ├── _coverpage.md
│   ├── _navbar.md
│   ├── _sidebar.md
│   ├── api.md
│   ├── architecture.md
│   ├── assets/
│   │   ├── boids.rbxl
│   │   ├── logo-r.psd
│   │   ├── pipeline_ecs.psd
│   │   ├── pipeline_old.psd
│   │   ├── repository-open-graph.psd
│   │   ├── tutorial.rbxl
│   │   └── version.psd
│   ├── faq.md
│   ├── favicon/
│   │   ├── browserconfig.xml
│   │   └── manifest.json
│   ├── getting-started.md
│   ├── index.html
│   ├── pt-br/
│   │   ├── README.md
│   │   ├── _coverpage.md
│   │   ├── _navbar.md
│   │   ├── _sidebar.md
│   │   ├── api.md
│   │   ├── architecture.md
│   │   ├── faq.md
│   │   ├── getting-started.md
│   │   ├── tutorial-boids.md
│   │   ├── tutorial-pacman.md
│   │   ├── tutorial-shoot.md
│   │   └── tutorial.md
│   ├── style.css
│   ├── tutorial-boids.md
│   ├── tutorial-pacman.md
│   ├── tutorial-shoot.md
│   ├── tutorial.md
│   ├── z_old_TECH_DETAILS.md
│   └── z_old_TUTORIAL.md
├── examples/
│   └── pong/
│       ├── .editorconfig
│       ├── .gitignore
│       ├── default.project.json
│       ├── pong.rbxlx
│       └── src/
│           ├── client/
│           │   ├── Constants.lua
│           │   ├── Main.client.lua
│           │   ├── Utility.lua
│           │   ├── components/
│           │   │   ├── AudioSource.lua
│           │   │   ├── Ball.lua
│           │   │   ├── BasePart.lua
│           │   │   ├── Paddle.lua
│           │   │   ├── Player.lua
│           │   │   ├── Position.lua
│           │   │   ├── Score.lua
│           │   │   └── Velocity.lua
│           │   └── systems/
│           │       ├── AudioSystem.lua
│           │       ├── BallSystem.lua
│           │       ├── CameraSystem.lua
│           │       ├── MoveSystem.lua
│           │       ├── PaddleHitSystem.lua
│           │       ├── PaddleSystem.lua
│           │       ├── PlayerAiThinkSystem.lua
│           │       ├── PlayerHumanInputSystem.lua
│           │       ├── RenderSystem.lua
│           │       └── ScoreSystem.lua
│           ├── server/
│           │   └── Main.server.lua
│           └── shared/
│               └── ECS.lua
├── modules/
│   ├── bin/
│   │   └── luacov
│   ├── luacov/
│   │   ├── defaults.lua
│   │   ├── hook.lua
│   │   ├── linescanner.lua
│   │   ├── reporter/
│   │   │   └── default.lua
│   │   ├── reporter.lua
│   │   ├── runner.lua
│   │   ├── stats.lua
│   │   ├── tick.lua
│   │   └── util.lua
│   ├── luacov.lua
│   ├── luaunit.lua
│   └── minify.lua
├── roblox/
│   ├── README.md
│   ├── RobloxUtils.lua
│   └── tutorial/
│       ├── default.project.json
│       └── src/
│           ├── client/
│           │   ├── benchmark/
│           │   │   ├── init.client.lua
│           │   │   └── soa.lua
│           │   └── tutorial/
│           │       └── init.client.lua
│           ├── server/
│           │   └── tutorial/
│           │       └── init.server.lua
│           └── shared/
│               ├── teste.lua
│               └── tutorial/
│                   ├── component/
│                   │   ├── FiringComponent.lua
│                   │   └── WeaponComponent.lua
│                   └── system/
│                       ├── CleanupFiringSystem.lua
│                       ├── FiringSystem.lua
│                       └── PlayerShootingSystem.lua
├── src/
│   ├── Archetype.lua
│   ├── Component.lua
│   ├── ComponentFSM.lua
│   ├── ECS.lua
│   ├── Entity.lua
│   ├── EntityRepository.lua
│   ├── Event.lua
│   ├── Query.lua
│   ├── QueryResult.lua
│   ├── RobloxLoopManager.lua
│   ├── System.lua
│   ├── SystemExecutor.lua
│   ├── Timer.lua
│   ├── Utility.lua
│   └── World.lua
├── test/
│   ├── README.md
│   ├── test_Archetype.lua
│   ├── test_Component.lua
│   ├── test_Entity.lua
│   ├── test_EntityRepository.lua
│   ├── test_Event.lua
│   ├── test_Query.lua
│   ├── test_QueryResult.lua
│   ├── test_SystemExecutor.lua
│   └── test_World.lua
└── test.lua

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true

[*.{lua,js,json}]
charset = utf-8
indent_style = space
indent_size = 3


================================================
FILE: .gitignore
================================================
# IDE
.idea
.vscode

luacov.report.out

src/*.zip


================================================
FILE: .luacov
================================================
return {
	include = {
		"^src",
      "src%/.+$"
	},
	exclude = {
		"%.test$",
	},
   runreport = true,
   deletestats = true,
   --reporter = "html"
}


================================================
FILE: .travis.yml
================================================
language: python

env:
  - LUA="lua=5.1"
  - LUA="lua=5.2"
  - LUA="lua=5.3"
  - LUA="lua=5.4"
  - LUA="luajit=2.0"
  - LUA="luajit=2.1"

before_install:
  - pip install hererocks
  - hererocks lua_install --$LUA -r latest
  - source lua_install/bin/activate

script:
  - lua test.lua -v && tail -22 ./luacov.report.out && lua build.lua


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to ECS Lua
Thanks for considering contributing to ECS Lua! This guide has a few tips and guidelines to make contributing to the 
project as easy as possible.

## Bug Reports
Any bugs (or things that look like bugs) can be reported on the [GitHub issue tracker](https://github.com/nidorx/ecs-lua/issues).

Make sure you check to see if someone has already reported your bug first! Don't fret about it; if we notice a duplicate 
we'll send you a link to the right issue!

## Feature Requests
If there are any features you think are missing from ECS Lua, you can post a request in the 
[GitHub issue tracker](https://github.com/nidorx/ecs-lua/issues).

Just like bug reports, take a peak at the issue tracker for duplicates before opening a new feature request.

## Documentation
[ECS Lua documentation](https://nidorx.github.io/ecs-lua/) is built using [Docsify](https://docsify.js.org/#/), a 
fairly simple documentation generator.

## Working on ECS Lua
To get started working on ECS Lua, you'll need:
* Git
* Lua 5.1

You can run all of ECS Lua tests with:

```sh
lua test.lua -v
```

The LuaCov coverage report is available in the `luacov.report.out` file.

To build the concatenated and minified versions, run the command

```sh
lua build.lua
```

## Pull Requests
Before starting a pull request, open an issue about the feature or bug. This helps us prevent duplicated and wasted 
effort. These issues are a great place to ask for help if you run into problems!

### Code Style

In short:

- **SPACE** for indentation
- Identation size = 3 spaces
- Double quotes
- One statement per line

### Tests
When submitting a bug fix, create a test that verifies the broken behavior and that the bug fix works. This helps us 
avoid regressions!

When submitting a new feature, add tests for all functionality.

We use [LuaCov](https://keplerproject.github.io/luacov) for keeping track of code coverage. We'd like it to be as 
close to 100% as possible, but it's not always possible. Adding tests just for the purpose of getting coverage isn't 
useful; we should strive to make only useful tests!


================================================
FILE: ECS.lua
================================================
--[[
	ECS Lua v2.2.0

	ECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.

	This is a minified version of ECS Lua, to see the full source code visit
	https://github.com/nidorx/ecs-lua

   Discussions about this script are at https://devforum.roblox.com/t/841175

	------------------------------------------------------------------------------

	MIT License

	Copyright (c) 2021 Alex Rodin

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	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
	SOFTWARE.
]]
local a,b={},{}local function c(d)if(not a[d])then a[d]={r=b[d]()}end return a[d].r end b["Archetype"]=function()local d={}local e={}local f={}local g=0 local h={}h.__index=h function h.Of(i)local j={}local k={}for m,n in ipairs(i)do if(n.IsCType and not n.isComponent)then if n.IsQualifier then if k[n]==nil then k[n]=true table.insert(j,n.Id)end n=n.SuperClass end if k[n]==nil then k[n]=true table.insert(j,n.Id)end end end table.sort(j)local l="_"..table.concat(j,"_")if d[l]==nil then d[l]=setmetatable({id=l,_components=k},h)g=g+1 end return d[l]end function h.Version()return g end function h:Has(i)return(self._components[i]==true)end function h:With(i)if self._components[i]==true then return self end local j=e[self]if not j then j={}e[self]=j end local k=j[i]if k==nil then local l={i}for m,n in pairs(self._components)do table.insert(l,m)end k=h.Of(l)j[i]=k end return k end function h:WithAll(i)local j={}for k,l in pairs(self._components)do table.insert(j,k)end for k,l in ipairs(i)do if self._components[l]==nil then table.insert(j,l)end end return h.Of(j)end function h:Without(i)if self._components[i]==nil then return self end local j=f[self]if not j then j={}f[self]=j end local k=j[i]if k==nil then local l={}for m,n in pairs(self._components)do if m~=i then table.insert(l,m)end end k=h.Of(l)j[i]=k end return k end function h:WithoutAll(i)local j={}for l,m in ipairs(i)do j[m]=true end local k={}for l,m in pairs(self._components)do if j[l]==nil then table.insert(k,l)end end return h.Of(k)end h.EMPTY=h.Of({})return h end b["Component"]=function()local d=c("Utility")local e=c("ComponentFSM")local f=d.copyDeep local g=d.mergeDeep local h=0 local function i(l,m)h=h+1 local n={Id=h,IsCType=true,SuperClass=m}n.__index=n if m==nil then m=n m._Qualifiers={["Primary"]=n}m._QualifiersArr={n}m._Initializers={}else m.HasQualifier=true n.IsQualifier=true n.HasQualifier=true end local o=m._Qualifiers local p=m._QualifiersArr setmetatable(n,{__call=function(q,r)return n.New(r)end,__index=function(q,r)if(r=="States")then return m.__States end if(r=="Case"or r=="StateInitial")then return rawget(m,r)end end,__newindex=function(q,r,s)if(r=="Case"or r=="States"or r=="StateInitial")then if n==m then if(r=="States")then if not m.IsFSM then e.AddCapability(m,s)for t,u in pairs(o)do if u~=m then e.AddMethods(m,u)end end end else rawset(q,r,s)end end else rawset(q,r,s)end end})if m.IsFSM then e.AddMethods(m,n)end function n.Qualifier(q)if type(q)~="string"then for s,t in ipairs(p)do if t==q then return q end end return nil end local r=o[q]if r==nil then r=i(l,m)o[q]=r table.insert(p,r)end return r end function n.Qualifiers(...)local q={...}if#q==0 then return p else local r={}local s={}for t,u in ipairs({...})do local v=n.Qualifier(u)if v and s[v]==nil then s[v]=true table.insert(r,v)end end return r end end function n.New(q)if(q~=nil and type(q)~="table")then q={value=q}end local r=setmetatable(l(q)or{},n)for s,t in ipairs(m._Initializers)do t(r)end r.isComponent=true r._qualifiers={[n]=r}return r end function n:GetType()return n end function n:Is(q)return q==n or q==m end function n:Primary()return self._qualifiers[m]end function n:Qualified(q)return self._qualifiers[n.Qualifier(q)]end function n:QualifiedAll()local q={}for r,s in pairs(o)do q[r]=self._qualifiers[s]end return q end function n:Merge(q)if m.HasQualifier then if self==q then return end if self._qualifiers==q._qualifiers then return end if not q:Is(m)then return end local r=n local s=q:GetType()local t if r==m then t=self._qualifiers elseif s==m then t=q._qualifiers elseif self._qualifiers[m]~=nil then t=self._qualifiers[m]._qualifiers elseif q._qualifiers[m]~=nil then t=q._qualifiers[m]._qualifiers end if t~=nil then if self._qualifiers~=t then for u,v in pairs(self._qualifiers)do if m~=u then t[u]=v v._qualifiers=t end end end if q._qualifiers~=t then for u,v in pairs(q._qualifiers)do if m~=u then t[u]=v v._qualifiers=t end end end else for u,v in pairs(q._qualifiers)do if r~=u then self._qualifiers[u]=v v._qualifiers=self._qualifiers end end end end end function n:Detach()if not m.HasQualifier then return end self._qualifiers[n]=nil self._qualifiers={[n]=self}end return n end local function j(l)return l or{}end local k={}function k.Create(l)local m=j if l~=nil then local n=type(l)if(n=="function")then m=l else if(n~="table")then l={value=l}end m=function(o)local p=f(l)if(o~=nil)then g(p,o)end return p end end end return i(m,nil)end return k end b["ComponentFSM"]=function()local d=c("Query")local e=d.Filter(function(g,h)local i=h.States local j=h.IsSuperClass local k=h.ComponentClass if j then local l=k.Qualifiers()for m,n in ipairs(l)do local o=g[n]if(o~=nil and i[o:GetState()]==true)then return true end end return false else local l=g[k]if l==nil then return false end return i[l:GetState()]==true end end)local f={}function f.AddCapability(g,h)g.IsFSM=true local i=setmetatable({},{__newindex=function(j,k,l)if(type(l)~="table")then l={l}end if table.find(l,"*")then rawset(j,k,"*")else local m=table.find(l,k)if m~=nil then table.remove(l,m)if#l==0 then l="*"end end rawset(j,k,l)end end})rawset(g,"__States",i)for j,k in pairs(h)do if g.StateInitial==nil then g.StateInitial=j end i[j]=k end f.AddMethods(g,g)table.insert(g._Initializers,function(j)j:SetState(g.StateInitial)end)end function f.AddMethods(g,h)h.IsFSM=true local i=g.States function h.In(...)local j={}local k=0 for l,m in ipairs({...})do if(i[m]~=nil and j[m]==nil)then k=k+1 j[m]=true end end if k==0 then return{}end return e({States=j,IsSuperClass=(h==g),ComponentClass=h,})end function h:SetState(j)if(j==nil or i[j]==nil)then return end local k=self:GetState()if(k==j)then return end if(k~=nil)then local m=i[k]if(m~="*"and table.find(m,j)==nil)then return end end self._state=j self._statePrev=k self._stateTime=os.clock()local l=g.Case and g.Case[j]if l then l(self,k)end end function h:GetState()return self._state or g.StateInitial end function h:GetPrevState()return self._statePrev or nil end function h:GetStateTime()return self._stateTime or 0 end end return f end b["ECS"]=function()local d=c("Query")local e=c("World")local f=c("System")local g=c("Archetype")local h=c("Component")local function i(k)e.LoopManager=k end pcall(function()if(game and game.ClassName=="DataModel")then i(c("RobloxLoopManager")())end end)local j={Query=d,World=e.New,System=f.Create,Archetype=g,Component=h.Create,SetLoopManager=i}if _G.ECS==nil then _G.ECS=j else local k=_G.warn or print k("ECS Lua was not registered in the global variables, there is already another object registered.")end return j end b["Entity"]=function()local d=c("Archetype")local e=0 local function f(m,...)local n={...}local o=m._data if(#n==1)then local q=n[1]if(q.IsCType and not q.isComponent)then return o[q]else return nil end end local p={}for q,r in ipairs(n)do if(r.IsCType and not r.isComponent)then table.insert(p,o[r])end end return table.unpack(p)end local function g(m,n,o)local p=m._data local q for r,s in ipairs(o.Qualifiers())do if s~=o then q=p[s]if q then break end end end if q then q:Merge(n)end end local function h(m,...)local n={...}local o=m._data local p=m.archetype local q=p local r={}local s=n[1]if(s and s.IsCType and not s.isComponent)then local t=n[2]local u if t==nil then local v=o[s]if v then v:Detach()end o[s]=nil q=q:Without(s)elseif(type(t)=="table"and t.isComponent)then local v=o[s]if(v~=t)then if v then v:Detach()end s=t:GetType()o[s]=t q=q:With(s)if(s.HasQualifier or s.IsQualifier)then g(m,t,s)end end else local v=o[s]if v then v:Detach()end local w=s(t)o[s]=w q=q:With(s)if(s.HasQualifier or s.IsQualifier)then g(m,w,s)end end else for t,u in ipairs(n)do if(u.isComponent)then local v=u:GetType()local w=o[v]if(w~=u)then if w then w:Detach()end o[v]=u q=q:With(v)if(v.HasQualifier or v.IsQualifier)then g(m,u,v)end end end end end if(p~=q)then m.archetype=q m._onChange:Fire(m,p)end end local function i(m,...)local n=m._data local o=m.archetype local p=o for q,r in ipairs({...})do if r.isComponent then local s=r:GetType()local t=n[s]if t then t:Detach()end n[s]=nil p=p:Without(s)elseif r.IsCType then local s=n[r]if s then s:Detach()end n[r]=nil p=p:Without(r)end end if m.archetype~=p then m.archetype=p m._onChange:Fire(m,o)end end local function j(m,n)local o=m._data local p={}if(n~=nil and n.IsCType and not n.isComponent)then local q=n.Qualifiers()for r,s in ipairs(q)do local t=o[s]if t then table.insert(p,t)end end else for q,r in pairs(o)do table.insert(p,r)end end return p end local function k(m,n)if(n~=nil and n.IsCType and not n.isComponent)then local o=m._data local p=n.Qualifiers()for q,r in ipairs(p)do local s=o[r]if s then return s end end end end local l={__index=function(m,n)if(type(n)=="table")then return f(m,n)end end,__newindex=function(m,n,o)local p=true if(type(n)=="table"and(n.IsCType and not n.isComponent))then h(m,n,o)else rawset(m,n,o)end end}function l.New(m,n)local o=d.EMPTY local p={}if(n~=nil and#n>0)then local q={}for r,s in ipairs(n)do local t=s:GetType()table.insert(q,t)p[t]=s end o=d.Of(q)end e=e+1 return setmetatable({_data=p,_onChange=m,id=e,isAlive=false,archetype=o,Get=f,Set=h,Unset=i,GetAll=j,GetAny=k,},l)end return l end b["EntityRepository"]=function()local d=c("Event")local e={}e.__index=e function e.New()return setmetatable({_archetypes={},_entitiesArchetype={},},e)end function e:Insert(f)if(self._entitiesArchetype[f]==nil)then local g=f.archetype local h=self._archetypes[g]if(h==nil)then h={count=0,entities={}}self._archetypes[g]=h end h.entities[f]=true h.count=h.count+1 self._entitiesArchetype[f]=g else self:Update(f)end end function e:Remove(f)local g=self._entitiesArchetype[f]if g==nil then return end self._entitiesArchetype[f]=nil local h=self._archetypes[g]if(h~=nil and h.entities[f]==true)then h.entities[f]=nil h.count=h.count-1 if(h.count==0)then self._archetypes[g]=nil end end end function e:Update(f)local g=self._entitiesArchetype[f]if(g==nil or g==f.archetype)then return end self:Remove(f)self:Insert(f)end function e:Query(f)local g={}for h,i in pairs(self._archetypes)do if f:Match(h)then table.insert(g,i.entities)end end return f:Result(g),#g>0 end function e:FastCheck(f)for g,h in pairs(self._archetypes)do if f:Match(g)then return true end end return false end return e end b["Event"]=function()local d={}d.__index=d function d.New(f,g)return setmetatable({_event=f,_handler=g},d)end function d:Disconnect()local f=self._event if(f and not f.destroyed)then local g=table.find(f._handlers,self._handler)if g~=nil then table.remove(f._handlers,g)end end setmetatable(self,nil)end local e={}e.__index=e function e.New()return setmetatable({_handlers={}},e)end function e:Connect(f)if(type(f)=="function")then table.insert(self._handlers,f)return d.New(self,f)end error(("Event:Connect(%s)"):format(typeof(f)),2)end function e:Fire(...)if not self.destroyed then for f,g in ipairs(self._handlers)do g(table.unpack({...}))end end end function e:Destroy()setmetatable(self,nil)self._handlers=nil self.destroyed=true end return e end b["Query"]=function()local d=c("QueryResult")local e={}local f={}f.__index=f setmetatable(f,{__call=function(i,j,k,l)return f.New(j,k,l)end,})local function g(i,j,k)local l={}local m={}local n={}for o,p in ipairs(i)do if(l[p]==nil)then if(p.IsCType and not p.isComponent)then l[p]=true table.insert(m,p)table.insert(n,p.Id)else if p.Filter then l[p]=true p[j]=true table.insert(k,p)end end end end if#m>0 then table.sort(n)local o="_"..table.concat(n,"_")return m,o end end function f.New(i,j,k)local l={}local m,n,o if(j~=nil)then j,m=g(j,"IsAnyFilter",l)end if(i~=nil)then i,n=g(i,"IsAllFilter",l)end if(k~=nil)then k,o=g(k,"IsNoneFilter",l)end return setmetatable({isQuery=true,_any=j,_all=i,_none=k,_anyKey=m,_allKey=n,_noneKey=o,_cache={},_clauses=#l>0 and l or nil},f)end function f:Result(i)return d.New(i,self._clauses)end function f:Match(i)local j=self._cache local k=j[i]if k~=nil then return k else local l=e[i]if(l==nil)then l={Any={},All={},None={}}e[i]=l end local m=self._noneKey if m then local p=l.None[m]if(p==nil)then p=true for q,r in ipairs(self._none)do if i:Has(r)then p=false break end end l.None[m]=p end if(p==false)then j[i]=false return false end end local n=self._anyKey if n then local p=l.Any[n]if(p==nil)then p=false if(l.All[n]==true)then p=true else for q,r in ipairs(self._any)do if i:Has(r)then p=true break end end end l.Any[n]=p end if(p==false)then j[i]=false return false end end local o=self._allKey if o then local p=l.All[o]if(p==nil)then local q=true for r,s in ipairs(self._all)do if(not i:Has(s))then q=false break end end if q then p=true else p=false end l.All[o]=p end j[i]=p return p end j[i]=true return true end end local function h()local i={isQueryBuilder=true}local j function i.All(...)j=nil i._all={...}return i end function i.Any(...)j=nil i._any={...}return i end function i.None(...)j=nil i._none={...}return i end function i.Build()if j==nil then j=f.New(i._all,i._any,i._none)end return j end return i end function f.All(...)return h().All(...)end function f.Any(...)return h().Any(...)end function f.None(...)return h().None(...)end function f.Filter(i)return function(j)return{Filter=i,Config=j}end end return f end b["QueryResult"]=function()local function d(l,m,n)return m,(l(m)==true),true end local function e(l,m,n)return l(m),true,true end local function f(l,m,n)local o=(n<=l)return m,o,o end local function g(l,m,n)local o=true for p,q in ipairs(l)do if(q.Filter(m,q.Config)==true)then o=false break end end return m,o,true end local function h(l,m,n)local o=true for p,q in ipairs(l)do if(q.Filter(m,q.Config)==false)then o=false break end end return m,o,true end local function i(l,m,n)local o=false for p,q in ipairs(l)do if(q.Filter(m,q.Config)==true)then o=true break end end return m,o,true end local j={}local k={}k.__index=k function k.New(l,m)local n=j if(m and#m>0)then local o={}local p={}local q={}n={}for r,s in ipairs(m)do if s.IsNoneFilter then table.insert(q,s)elseif s.IsAnyFilter then table.insert(p,s)else table.insert(o,s)end end if(#q>0)then table.insert(n,{g,q})end if(#o>0)then table.insert(n,{h,o})end if(#p>0)then table.insert(n,{i,p})end end return setmetatable({chunks=l,_pipeline=n,},k)end function k:With(l,m)local n={}for o,p in ipairs(self._pipeline)do table.insert(n,p)end table.insert(n,{l,m})return setmetatable({chunks=self.chunks,_pipeline=n,},k)end function k:Filter(l)return self:With(d,l)end function k:Map(l)return self:With(e,l)end function k:Limit(l)return self:With(f,l)end function k:AnyMatch(l)local m=false self:ForEach(function(n)if l(n)then m=true end return m end)return m end function k:AllMatch(l)local m=true self:ForEach(function(n)if(not l(n))then m=false end return m==false end)return m end function k:FindAny()local l self:ForEach(function(m)l=m return true end)return l end function k:ToArray()local l={}self:ForEach(function(m)table.insert(l,m)end)return l end function k:Iterator()local l=coroutine.create(function()self:ForEach(function(m,n)coroutine.yield(m,n)end)end)return function()local m,n,o=coroutine.resume(l)return o,n end end function k:ForEach(l)local m=1 local n=self._pipeline local o=#n>0 if(not o)then for p,q in ipairs(self.chunks)do for r,s in pairs(q)do if(l(r,m)==true)then return end m=m+1 end end else for p,q in ipairs(self.chunks)do for r,s in pairs(q)do local t=false local u=true local v=r if(u and o)then for w,x in ipairs(n)do local y,z,A=x[1](x[2],v,m)if(not A)then t=true end if z then v=y else u=false break end end end if u then if(l(v,m)==true)then return end m=m+1 end if t then return end end end end end return k end b["RobloxLoopManager"]=function()local function d()local e=game:GetService("RunService")return{Register=function(f)local g=e.Stepped:Connect(function()f:Update("process",os.clock())end)local h=e.Heartbeat:Connect(function()f:Update("transform",os.clock())end)local i if(not e:IsServer())then i=e.RenderStepped:Connect(function()f:Update("render",os.clock())end)end return function()g:Disconnect()h:Disconnect()if i then i:Disconnect()end end end}end return d end b["System"]=function()local d={"task","render","process","transform"}local e={}function e.Create(f,g,h,i)if(f==nil or not table.find(d,f))then error("The step parameter must one of ",table.concat(d,", "))end if(g and type(g)=="function")then i=g g=nil elseif h and type(h)=="function"then i=h h=nil end if(g and type(g)=="table"and(g.isQuery or g.isQueryBuilder))then h=g g=nil end if(g==nil or g<0)then g=50 end if type(h)=="function"then i=h h=nil end if(h and h.isQueryBuilder)then h=h.Build()end local j={Step=f,Order=g,Query=h,}j.__index=j function j.New(k,l)local m=setmetatable({version=0,_world=k,_config=l,},j)if m.Initialize then m:Initialize(l)end return m end function j:GetType()return j end function j:Result(k)return self._world:Exec(k or j.Query)end function j:Destroy()if self.OnDestroy then self.OnDestroy()end setmetatable(self,nil)for k,l in pairs(self)do self[k]=nil end end if i and type(i)=="function"then j.Update=i end return j end return e end b["SystemExecutor"]=function()local function d(i)local j={}local k={}for l,m in ipairs(i)do local n=m:GetType()if(m._TaskState==nil)then m._TaskState="suspended"end if not k[n]then local o={Type=n,System=m,Depends={}}k[n]=o table.insert(j,o)end end for l,m in ipairs(j)do local n=m.Type.Before if(n~=nil and#n>0)then for p,q in ipairs(n)do local r=k[q]if r then r.Depends[m]=true end end end local o=m.Type.After if(o~=nil and#o>0)then for p,q in ipairs(o)do local r=k[q]if r then m.Depends[r]=true end end end end return j end local function e(i,j)return i.Order<j.Order end local f={}f.__index=f function f.New(i)local j=setmetatable({_world=i,_onExit={},_onEnter={},_onRemove={},_task={},_render={},_process={},_transform={},_schedulers={},_lastFrameMatchQueries={},_currentFrameMatchQueries={},},f)i:OnQueryMatch(function(k)j._currentFrameMatchQueries[k]=true end)return j end function f:SetSystems(i)local j={}local k={}local l={}local m={}local n={}local o={}local p={}for q,r in pairs(i)do local s=r.Step if r.Update then if s=="task"then table.insert(m,r)elseif s=="process"then table.insert(o,r)elseif s=="transform"then table.insert(p,r)elseif s=="render"then table.insert(n,r)end end if(r.Query and r.Query.isQuery and s~="task")then if r.OnExit then table.insert(j,r)end if r.OnEnter then table.insert(k,r)end if r.OnRemove then table.insert(l,r)end end end m=d(m)table.sort(j,e)table.sort(k,e)table.sort(l,e)table.sort(n,e)table.sort(o,e)table.sort(p,e)self._onExit=j self._onEnter=k self._onRemove=l self._task=m self._render=n self._process=o self._transform=p end function f:ExecOnExitEnter(i,j)local k=true local l={}for m,n in pairs(j)do local o=l[n]if not o then o={}l[n]=o end local p=m.archetype local q=o[p]if not q then q={}o[p]=q end table.insert(q,m)k=false end if k then return end self:_ExecOnEnter(i,l)self:_ExecOnExit(i,l)end function f:_ExecOnEnter(i,j)local k=self._world for l,m in ipairs(self._onEnter)do local n=m.Query for o,p in pairs(j)do if not n:Match(o)then for q,r in pairs(p)do if n:Match(q)then for s,t in ipairs(r)do k.version=k.version+1 m:OnEnter(i,t)m.version=k.version end end end end end end end function f:_ExecOnExit(i,j)local k=self._world for l,m in ipairs(self._onExit)do local n=m.Query for o,p in pairs(j)do if n:Match(o)then for q,r in pairs(p)do if not n:Match(q)then for s,t in ipairs(r)do k.version=k.version+1 m:OnExit(i,t)m.version=k.version end end end end end end end function f:ExecOnRemove(i,j)local k=true local l={}for n,o in pairs(j)do local p=l[o]if not p then p={}l[o]=p end table.insert(p,n)k=false end if k then return end local m=self._world for n,o in ipairs(self._onRemove)do for p,q in pairs(l)do if o.Query:Match(p)then for r,s in ipairs(q)do m.version=m.version+1 o:OnRemove(i,s)o.version=m.version end end end end end local function g(i,j,k)local l=i._world local m=i._lastFrameMatchQueries local n=i._currentFrameMatchQueries for o,p in ipairs(j)do local q=true if p.Query then local r=p.Query if m[r]==true or n[r]==true then q=true else q=l:FastCheck(r)n[r]=q end end if q then if(p.ShouldUpdate==nil or p.ShouldUpdate(k))then l.version=l.version+1 p:Update(k)p.version=l.version end end end end function f:ExecProcess(i)self._currentFrameMatchQueries={}g(self,self._process,i)end function f:ExecTransform(i)g(self,self._transform,i)end function f:ExecRender(i)g(self,self._render,i)self._lastFrameMatchQueries=self._currentFrameMatchQueries end function f:ExecTasks(i)while i>0 do local j=false local k,l=0,#self._schedulers-1 while k<=l do k=k+1 local m=self._schedulers[k]local n,o=m.Resume(i)if o then j=true end i=i-(n+0.00001)if(i<=0)then break end end if not j then return end end end local function h(i,j,k,l)local m=i.System m._TaskState="running"if(m.ShouldUpdate==nil or m.ShouldUpdate(j))then k.version=k.version+1 m:Update(j)m.version=k.version end m._TaskState="suspended"l(i)end function f:ScheduleTasks(i)local j=self._world local k={}local l={}local m={}local n={}local o={}local p,q=0,#self._task-1 while p<=q do p=p+1 local s=self._task[p]if(s.System._TaskState=="suspended")then s.System._TaskState="scheduled"local t=false for u,v in pairs(s.Depends)do t=true if o[u]==nil then o[u]={}end table.insert(o[u],s)end if(not t)then table.insert(k,s)end m[s]=true end end local function r(s)s.Thread=nil s.LastExecTime=nil n[s]=true if o[s]then local t=o[s]local u,v=0,#t-1 while u<=v do u=u+1 local w=t[u]if m[w]then local x=true for y,z in pairs(w.Depends)do if n[y]~=true then x=false break end end if x then m[w]=nil w.LastExecTime=0 w.Thread=coroutine.create(h)table.insert(l,w)end end end end end if#k>0 then local s,t=0,#k-1 while s<=t do s=s+1 local v=k[s]m[v]=nil v.LastExecTime=0 v.Thread=coroutine.create(h)table.insert(l,v)end local u u={Resume=function(v)table.sort(l,function(A,B)return A.LastExecTime<B.LastExecTime end)local w=0 local x,y=0,#l-1 while x<=y do x=x+1 local A=l[x]if A.Thread~=nil then local B=os.clock()A.LastExecTime=B coroutine.resume(A.Thread,A,i,j,r)w=w+(os.clock()-B)if(w>v)then break end end end for A,B in ipairs(l)do if B.Thread==nil then local C=table.find(l,B)if C~=nil then table.remove(l,C)end end end local z=#l>0 if(not z)then local A=table.find(self._schedulers,u)if A~=nil then table.remove(self._schedulers,A)end end return w,z end}table.insert(self._schedulers,u)end end return f end b["Timer"]=function()local d=4 local function e(g)local h=0.0 local i=0.0 return function(j,k,l,m)local n=g.DeltaFixed local o=j-i if o>0.25 then o=0.25 end i=j g.Now=j h=h+o if k=="process"then if h>=n then g.Interpolation=1 l(g)local p=0 while(h>=n and p<d)do m(g)p=p+1 g.Process=g.Process+n h=h-n end end else g.Interpolation=math.min(math.max(h/n,0),1)l(g)m(g)end end end local f={}f.__index=f function f.New(g)local h={Now=0,Frame=0,Process=0,Delta=0,DeltaFixed=0,Interpolation=0}local i=setmetatable({Time=h,Frequency=0,_update=e(h)},f)i:SetFrequency(g)return i end function f:SetFrequency(g)if g==nil then g=30 end local h=math.floor(math.abs(g)/2)*2 if h<2 then h=2 end if g~=h then g=h end self.Frequency=g self.Time.DeltaFixed=1000/g/1000 end function f:Update(g,h,i,j)self._update(g,h,i,j)end return f end b["Utility"]=function()local d={}if table.unpack==nil then table.unpack=unpack end if table.find==nil then table.find=function(g,h,i)local j=#g for k=i or 1,j,1 do if g[k]==h then return k end end return nil end end local function e(g)local h={}for i,j in pairs(g)do if type(j)=="table"then j=e(j)end h[i]=j end return h end d.copyDeep=e local function f(g,h)for i,j in pairs(h)do if(type(j)=="table")then local k=g[i]if(k==nil or type(k)~="table")then g[i]=e(j)else g[i]=f(k,j)end else g[i]=j end end return g end d.mergeDeep=f return d end b["World"]=function()local d=c("Timer")local e=c("Event")local f=c("Entity")local g=c("Archetype")local h=c("SystemExecutor")local i=c("EntityRepository")local j={}j.__index=j function j.New(k,l,m)local n=setmetatable({version=0,maxTasksExecTime=0.013333333333333334,_dirty=false,_timer=d.New(l),_systems={},_repository=i.New(),_entitiesCreated={},_entitiesRemoved={},_entitiesUpdated={},_onQueryMatch=e.New(),_onChangeArchetypeEvent=e.New(),},j)n._executor=h.New(n)n._onChangeArchetypeEvent:Connect(function(o,p,q)n:_OnChangeArchetype(o,p,q)end)if(k~=nil)then for o,p in ipairs(k)do n:AddSystem(p)end end if(not m and j.LoopManager)then n._loopCancel=j.LoopManager.Register(n)end return n end function j:SetFrequency(k)k=self._timer:SetFrequency(k)end function j:GetFrequency(k)return self._timer.Frequency end function j:AddSystem(k,l)if k then if l==nil then l={}end if self._systems[k]==nil then self._systems[k]=k.New(self,l)self._executor:SetSystems(self._systems)end end end function j:Entity(...)local k=f.New(self._onChangeArchetypeEvent,{...})self._dirty=true self._entitiesCreated[k]=true k.version=self.version k.isAlive=false return k end function j:Remove(k)if self._entitiesRemoved[k]==true then return end if self._entitiesCreated[k]==true then self._entitiesCreated[k]=nil else self._repository:Remove(k)self._entitiesRemoved[k]=true if self._entitiesUpdated[k]==nil then self._entitiesUpdated[k]=k.archetype end end self._dirty=true k.isAlive=false end function j:Exec(k)if(k.isQueryBuilder)then k=k.Build()end local l,m=self._repository:Query(k)if m then self._onQueryMatch:Fire(k)end return l end function j:FastCheck(k)if(k.isQueryBuilder)then k=k.Build()end return self._repository:FastCheck(k)end function j:OnQueryMatch(k)return self._onQueryMatch:Connect(k)end function j:Update(k,l)self._timer:Update(l,k,function(m)if k=="process"then self._executor:ScheduleTasks(m)end self._executor:ExecTasks(self.maxTasksExecTime)end,function(m)if k=="process"then self._executor:ExecProcess(m)elseif k=="transform"then self._executor:ExecTransform(m)else self._executor:ExecRender(m)end while self._dirty do self._dirty=false local n={}for q,r in pairs(self._entitiesRemoved)do n[q]=self._entitiesUpdated[q]self._entitiesUpdated[q]=nil end self._entitiesRemoved={}self._executor:ExecOnRemove(m,n)n=nil local o={}local p=false for q,r in pairs(self._entitiesUpdated)do if(r~=q.archetype)then p=true o[q]=r end end self._entitiesUpdated={}for q,r in pairs(self._entitiesCreated)do p=true o[q]=g.EMPTY q.isAlive=true self._repository:Insert(q)end self._entitiesCreated={}if p then self._executor:ExecOnExitEnter(m,o)o=nil end end end)end function j:Destroy()if self._loopCancel then self._loopCancel()self._loopCancel=nil end if self._onChangeArchetypeEvent then self._onChangeArchetypeEvent:Destroy()self._onChangeArchetypeEvent=nil end self._repository=nil if self._systems then for k,l in pairs(self._systems)do l:Destroy()end self._systems=nil end self._timer=nil self._ExecPlan=nil self._entitiesCreated=nil self._entitiesUpdated=nil self._entitiesRemoved=nil setmetatable(self,nil)end function j:_OnChangeArchetype(k,l,m)if k.isAlive then if self._entitiesUpdated[k]==nil then self._dirty=true self._entitiesUpdated[k]=l end self._repository:Update(k)k.version=self.version end end return j end return c("ECS")

================================================
FILE: ECS_concat.lua
================================================
--[[
	ECS Lua v2.2.0

	ECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.

	This is a minified version of ECS Lua, to see the full source code visit
	https://github.com/nidorx/ecs-lua

   Discussions about this script are at https://devforum.roblox.com/t/841175

	------------------------------------------------------------------------------

	MIT License

	Copyright (c) 2021 Alex Rodin

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	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
	SOFTWARE.
]]

local __M__, __F__ = {}, {}
local function __REQUIRE__(m)
   if (not __M__[m]) then
      __M__[m] = { r = __F__[m]() }
   end
   return __M__[m].r
end

__F__["Archetype"] = function()
   -- src/Archetype.lua
   
   local archetypes = {}
   
   local CACHE_WITH = {}
   local CACHE_WITHOUT = {}
   
   -- Version of the last registered archetype. Used to cache the systems execution plan
   local Version = 0
   
   --[[
      An Archetype is a unique combination of component types. The EntityRepository uses the archetype to group all 
      entities that have the same sets of components.
   
      An entity can change archetype fluidly over its lifespan. For example, when you add or remove components, 
      the archetype of the affected entity changes.
   
      An archetype object is not a container; rather it is an identifier to each unique combination of component 
      types that an application has created at run time, either directly or implicitly.
   
      You can create archetypes directly using ECS.Archetype.Of(Components[]). You also implicitly create archetypes 
      whenever you add or remove a component from an entity. An Archetype object is an immutable singleton; 
      creating an archetype with the same set of components, either directly or implicitly, results in the same 
      archetype.
   
      The ECS framework uses archetypes to group entities that have the same structure together. The ECS framework stores 
      component data in blocks of memory called chunks. A given chunk stores only entities having the same archetype. 
      You can get the Archetype object for a chunk from its Archetype property.
   
      Use ECS.Archetype.Of(Components[]) to get a Archetype reference.
   ]]
   local Archetype  = {}
   Archetype.__index = Archetype
   
   --[[
      Gets the reference to an archetype from the informed components
   
      @param componentClasses {ComponentClass[]} Component that define this archetype
      @return Archetype
   ]]
   function Archetype.Of(componentClasses)
   
      local ids = {}
      local cTypes = {}
      for _, cType in ipairs(componentClasses) do
         if (cType.IsCType and not cType.isComponent) then
            if cType.IsQualifier then
               if cTypes[cType] == nil then    
                  cTypes[cType] = true
                  table.insert(ids, cType.Id)
               end
               cType = cType.SuperClass
            end
            if cTypes[cType] == nil then    
               cTypes[cType] = true
               table.insert(ids, cType.Id)
            end
         end
      end
   
      table.sort(ids)
      local id = "_" .. table.concat(ids, "_")
   
      if archetypes[id] == nil then
         archetypes[id] = setmetatable({
            id = id,
            _components = cTypes
         }, Archetype)
         Version = Version + 1
      end
   
      return archetypes[id]
   end
   
   --[[
      Get the version of archetype definitions
   
      @return number
   ]]
   function Archetype.Version()
      return Version
   end
   
   --[[
      Checks whether this archetype has the informed component
   
      @param componentClass {ComponentClass}
      @return bool
   ]]
   function Archetype:Has(componentClass)
      return (self._components[componentClass] == true)
   end
   
   --[[
      Gets the reference to an archetype that has the current components + the informed component
   
      @param componentClass {ComponentClass}
      @return Archetype
   ]]
   function Archetype:With(componentClass)
      if self._components[componentClass] == true then
         -- component exists in that list, returns the archetype itself
         return self
      end
   
      local cache = CACHE_WITH[self]
      if not cache then
         cache =  {}
         CACHE_WITH[self] = cache
      end
   
      local other = cache[componentClass]
      if other == nil then
         local componentTs = {componentClass}
         for component,_ in pairs(self._components) do
            table.insert(componentTs, component)
         end
         other = Archetype.Of(componentTs)
         cache[componentClass] = other
      end
      return other
   end
   
   --[[
      Gets the reference to an archetype that has the current components + the informed components
   
      @param componentClasses {ComponentClass[]}
      @return Archetype
   ]]
   function Archetype:WithAll(componentClasses)
   
      local cTypes = {}
      for component,_ in pairs(self._components) do
         table.insert(cTypes, component)
      end
      
      for _,component in ipairs(componentClasses) do
         if self._components[component] == nil then
            table.insert(cTypes, component)
         end
      end
   
      return Archetype.Of(cTypes)
   end
   
   --[[
      Gets the reference to an archetype that has the current components - the informed component
      
      @param componentClass {ComponentClass}
      @return Archetype
   ]]
   function Archetype:Without(componentClass)
   
      if self._components[componentClass] == nil then
         -- component does not exist in this list, returns the archetype itself
         return self
      end
   
      local cache = CACHE_WITHOUT[self]
      if not cache then
         cache =  {}
         CACHE_WITHOUT[self] = cache
      end
   
      local other = cache[componentClass]
      if other == nil then      
         local componentTs = {}
         for component,_ in pairs(self._components) do
            if component ~= componentClass then
               table.insert(componentTs, component)
            end
         end
         other =  Archetype.Of(componentTs)
         cache[componentClass] = other
      end
         
      return other
   end
   
   --[[
      Gets the reference to an archetype that has the current components - the informed components
   
      @param componentClasses {ComponentClass[]}
      @return Archetype
   ]]
   function Archetype:WithoutAll(componentClasses)
   
      local toIgnoreIdx = {}
      for _,component in ipairs(componentClasses) do
         toIgnoreIdx[component] = true
      end
      
      local cTypes = {}
      for component,_ in pairs(self._components) do
         if toIgnoreIdx[component] == nil then
            table.insert(cTypes, component)
         end
      end
   
      return Archetype.Of(cTypes)
   end
   
   -- Generic archetype, for entities that do not have components
   Archetype.EMPTY = Archetype.Of({})
   
   return Archetype
   
end

__F__["Component"] = function()
   -- src/Component.lua
   local Utility = __REQUIRE__("Utility")
   local ComponentFSM = __REQUIRE__("ComponentFSM")
   
   local copyDeep = Utility.copyDeep
   local mergeDeep = Utility.mergeDeep
   
   local CLASS_SEQ = 0
   
   --[[
      @param initializer {function(table) => table}
      @param superClass {ComponentClass}
      @return ComponentClass
   ]]
   local function createComponentClass(initializer, superClass)
      CLASS_SEQ = CLASS_SEQ + 1
   
      local ComponentClass = {
         Id = CLASS_SEQ,
         IsCType = true,
         -- Primary component
         SuperClass = superClass
      }
      ComponentClass.__index = ComponentClass
   
      if superClass == nil then
         superClass = ComponentClass
         superClass._Qualifiers = { ["Primary"] = ComponentClass }
         superClass._QualifiersArr = { ComponentClass }
         superClass._Initializers = {}
      else
         superClass.HasQualifier = true
         ComponentClass.IsQualifier = true
         ComponentClass.HasQualifier = true
      end
   
      local Qualifiers = superClass._Qualifiers
      local QualifiersArr = superClass._QualifiersArr
   
      setmetatable(ComponentClass, {
         __call = function(t, value)
            return ComponentClass.New(value)
         end,
         __index = function(t, key)
            if (key == "States") then
               return superClass.__States       
            end
            if (key == "Case" or key == "StateInitial") then
               return rawget(superClass, key)       
            end
         end,
         __newindex = function(t, key, value)
            if (key == "Case" or key == "States" or key == "StateInitial") then
               -- (FMS) Finite State Machine
               if ComponentClass == superClass then
                  if (key == "States") then
                     if not superClass.IsFSM then
                        ComponentFSM.AddCapability(superClass, value)
                        for _, qualifiedClass in pairs(Qualifiers) do
                           if qualifiedClass ~= superClass then
                              ComponentFSM.AddMethods(superClass, qualifiedClass)               
                           end
                        end
                     end
                  else
                     rawset(t, key, value)
                  end
               end
            else
               rawset(t, key, value)
            end
         end
      })
   
      if superClass.IsFSM then
         ComponentFSM.AddMethods(superClass, ComponentClass)               
      end
   
      --[[
         Gets a qualifier for this type of component. If the qualifier does not exist, a new class will be created, 
         otherwise it brings the already registered class qualifier reference with the same name.
   
         @param qualifier {string|ComponentClass}
         @return ComponentClass
      ]]
      function ComponentClass.Qualifier(qualifier)
         if type(qualifier) ~= "string" then
            for _, qualifiedClass in ipairs(QualifiersArr) do
               if qualifiedClass == qualifier then
                  return qualifier
               end
            end
            return nil
         end
   
         local qualifiedClass = Qualifiers[qualifier]
         if qualifiedClass == nil then
            qualifiedClass = createComponentClass(initializer, superClass)
            Qualifiers[qualifier] = qualifiedClass
            table.insert(QualifiersArr, qualifiedClass)
         end
         return qualifiedClass
      end
   
      --[[
         Get all qualified class
   
         @param ... {string|ComponentClass} (Optional) Allows to filter the specific qualifiers
         @return ComponentClass[]
      ]]
      function ComponentClass.Qualifiers(...)
         local filter = {...}
         if #filter == 0 then
            return QualifiersArr
         else
            local qualifiers = {}
            local cTypes = {}
            for _,qualifier in ipairs({...}) do
               local qualifiedClass = ComponentClass.Qualifier(qualifier)
               if qualifiedClass and cTypes[qualifiedClass] == nil then
                  cTypes[qualifiedClass] = true
                  table.insert(qualifiers, qualifiedClass)
               end
            end
            return qualifiers      
         end
      end
   
      --[[
         Constructor
   
         @param value {any} If the value is not a table, it will be converted to the format "{ value = value}"
         @return Component
      ]]
      function ComponentClass.New(value)
         if (value ~= nil and type(value) ~= "table") then
            -- local MyComponent = Component({ value = Vector3.new(0, 0, 0) })
            -- local component = MyComponent(Vector3.new(10, 10, 10))
            value = { value = value }
         end
         local component = setmetatable(initializer(value) or {}, ComponentClass)
         for _, fn in ipairs(superClass._Initializers) do
            fn(component)
         end
         component.isComponent = true
         component._qualifiers = { [ComponentClass] = component }
         return component
      end
   
      --[[
         Get this component's class
   
         @return ComponentClass
      ]]
      function ComponentClass:GetType()
         return ComponentClass
      end
   
      --[[
         Check if this component is of the type informed
   
         @param componentClass {ComponentClass}
         @return bool
      ]]
      function ComponentClass:Is(componentClass)
         return componentClass == ComponentClass or componentClass == superClass
      end
   
      --[[
         Get the instance for the primary qualifier of this class
   
         @return Component|nil
      ]]
      function ComponentClass:Primary()
         return self._qualifiers[superClass]
      end
   
      --[[
         Get the instance for the given qualifier of this class
   
         @param name {string|ComponentClass}
         @return Component|nil
      ]]
      function ComponentClass:Qualified(qualifier)
         return self._qualifiers[ComponentClass.Qualifier(qualifier)]
      end
   
      --[[
         Get all instances for all qualifiers of that class
   
         @return Component[]
      ]]
      function ComponentClass:QualifiedAll()
         local qualifiedAll = {}
         for name, qualifiedClass in pairs(Qualifiers) do
            qualifiedAll[name] = self._qualifiers[qualifiedClass]
         end
         return qualifiedAll
      end
   
      --[[
         Merges data from the other component into the current component. This method should not be invoked, it is used
         by the entity to ensure correct retrieval of a component's qualifiers.
   
         @param other {Component}
      ]]
      function ComponentClass:Merge(other)
         if superClass.HasQualifier then
            if self == other then
               return
            end
      
            if self._qualifiers == other._qualifiers then
               return
            end
      
            if not other:Is(superClass) then
               return
            end
      
            local selfClass = ComponentClass
            local otherClass = other:GetType()
      
            -- does anyone know the reference to the primary entity?
            local primaryQualifiers
            if selfClass == superClass then
               primaryQualifiers = self._qualifiers
            elseif otherClass == superClass then
               primaryQualifiers = other._qualifiers
            elseif self._qualifiers[superClass] ~= nil then
               primaryQualifiers = self._qualifiers[superClass]._qualifiers
            elseif other._qualifiers[superClass] ~= nil then
               primaryQualifiers = other._qualifiers[superClass]._qualifiers
            end
      
            if primaryQualifiers ~= nil then
               if self._qualifiers ~= primaryQualifiers then
                  for qualifiedClass, component in pairs(self._qualifiers) do
                     if superClass ~= qualifiedClass then
                        primaryQualifiers[qualifiedClass] = component
                        component._qualifiers = primaryQualifiers
                     end
                  end
               end
      
               if other._qualifiers ~= primaryQualifiers then
                  for qualifiedClass, component in pairs(other._qualifiers) do
                     if superClass ~= qualifiedClass then
                        primaryQualifiers[qualifiedClass] = component
                        component._qualifiers = primaryQualifiers
                     end
                  end
               end
            else
               -- none of the instances know the Primary, use the current object reference
               for qualifiedClass, component in pairs(other._qualifiers) do
                  if selfClass ~= qualifiedClass then
                     self._qualifiers[qualifiedClass] = component
                     component._qualifiers = self._qualifiers
                  end
               end
            end
         end
      end
   
      --[[
         Unlink this component with the other qualifiers
      ]]
      function ComponentClass:Detach()
         if not superClass.HasQualifier then
            return
         end
   
         -- remove old unlink
         self._qualifiers[ComponentClass] = nil
   
         -- new link
         self._qualifiers = { [ComponentClass] = self }
      end
   
      return ComponentClass
   end
   
   local function defaultInitializer(value)
      return value or {}
   end
   
   --[[
      A Component is an object that can store data but should have not behaviour (As that should be handled by systems). 
   ]]
   local Component = {}
   
   --[[
      Register a new ComponentClass
   
      @param template {table|function(table?) -> table} 
         When `table`, this template will be used for creating component instances
         When it's a `function`, it will be invoked when a new component is instantiated. The creation parameter of the 
            component is passed to template function
         If the template type is different from `table` and `function`, **ECS Lua** will generate a template in the format 
            `{ value = template }`.
      @return ComponentClass  
   ]]
   function Component.Create(template)
   
      local initializer = defaultInitializer
   
      if template ~= nil then
         local ttype = type(template)
         if (ttype == "function") then
            initializer = template
         else
            if (ttype ~= "table") then
               template = { value = template }
            end
   
            initializer = function(value)
               local data = copyDeep(template)
               if (value ~= nil) then
                  mergeDeep(data, value)
               end
               return data
            end
         end
      end
      
      return createComponentClass(initializer, nil)
   end
   
   return Component
   
end

__F__["ComponentFSM"] = function()
   -- src/ComponentFSM.lua
   --[[
      Facilitate the construction and use of a Finite State Machine (FSM) using ECS
   
      Example:
         local Movement = ECS.Component({ Speed = 0 })
   
         Movement.States = {
            Standing = "*",
            Walking  = {"Standing", "Running"},
            Running  = {"Walking"}
         }
   
         Movement.StateInitial = "Standing"
   
         Movement.Case = {
            Standing = function(self, previous)
               self.Speed = 0
            end,
            Walking = function(self, previous)
               self.Speed = 5
            end,
            Running = function(self, previous)
               self.Speed = 10
            end
         }
         
         local movement = entity[Movement]
         print(movement:GetState()) -- "Standing"
         movement:SetState("Walking")
         print(movement:GetPrevState()) -- "Standing"
         movement:GetStateTime()
   
         if (movement:GetState() == "Standing") then
            movement.Speed = 0
         end
   ]]
   
   
   local Query = __REQUIRE__("Query")
   
   --[[
      Filter used in Query and QueryResult
   
      @see QueryResult.lua
   
      Ex. ECS.Query.All(Movement.In("Standing", "Walking"))
   ]]
   local queryFilterCTypeStateIn = Query.Filter(function(entity, config)
      local states = config.States
      local isSuperClass = config.IsSuperClass
      local componentClass = config.ComponentClass
   
      if isSuperClass then
         local qualifiers = componentClass.Qualifiers()
         for _, qualifier in ipairs(qualifiers) do
            local component = entity[qualifier]
            if (component ~= nil and states[component:GetState()] == true) then
               return true
            end
         end
         return false
      else
         local component = entity[componentClass]
         if component == nil then
            return false
         end
         return states[component:GetState()] == true
      end
   end)
   
   local ComponentFSM = {}
   
   --[[
      Adds FSM capability to a ComponentClass
   
      @param superClass {ComonentClass}
      @param states { {[key=string] => string|string[]}}
   
      @see Component.lua - createComponentClass() - ComponentClass__newindex
   ]]
   function ComponentFSM.AddCapability(superClass, states)
      
      superClass.IsFSM = true
   
      local cTypeStates = setmetatable({}, {
         __newindex = function(states, newState, value)   
   
            if (type(value) ~= "table") then
               value = {value}
            end
   
            if table.find(value, "*") then
               rawset(states, newState, "*")
            else
               local idxSelf = table.find(value, newState)
               if idxSelf ~= nil then
                  table.remove(value, idxSelf)
                  if #value == 0 then
                     value = "*"
                  end
               end
               rawset(states, newState, value)
            end
         end
      })
      rawset(superClass, "__States", cTypeStates)
   
      for state,value in pairs(states) do
         if superClass.StateInitial == nil then
            superClass.StateInitial = state
         end
         cTypeStates[state] = value
      end
   
      ComponentFSM.AddMethods(superClass, superClass)
   
      table.insert(superClass._Initializers, function(component)
         component:SetState(superClass.StateInitial)
      end)
   end
   
   
   
   --[[
      Adds FSM state change methods to a ComponentClass
   
      @param superClass {ComponentClass}
      @param componentClass {ComponentClass}
   ]]
   function ComponentFSM.AddMethods(superClass, componentClass)
   
      componentClass.IsFSM = true
      local cTypeStates = superClass.States
   
      --[[
         Creates a clause used to filter repository entities in a Query or QueryResult
   
         @param ... {string[]} 
         @return Clause
   
         Ex. ECS.Query.All(Movement.In("Walking", "Running"))
      ]]
      function componentClass.In(...)
         
         local states = {}
         local count = 0
         for _,state in ipairs({...}) do
            if (cTypeStates[state] ~= nil and states[state] == nil) then
               count = count + 1
               states[state] = true
            end
         end
   
         if count == 0 then
            -- In any state
            return {}
         end
   
         return queryFilterCTypeStateIn({
            States = states,
            IsSuperClass = (componentClass == superClass),
            ComponentClass = componentClass, 
         })
      end   
   
      --[[
         Defines the current state of the FSM
   
         @param newState {string}
      ]]
      function componentClass:SetState(newState)      
         if (newState == nil or cTypeStates[newState] == nil) then
            return
         end
   
         local actual = self:GetState()
         if (actual == newState) then
            return
         end
   
         if (actual ~= nil ) then
            local transtions = cTypeStates[actual]
            if (transtions ~= "*" and table.find(transtions, newState) == nil) then
               -- not allowed
               return
            end
         end
   
         self._state = newState
         self._statePrev = actual
         self._stateTime = os.clock()
   
         local action = superClass.Case and superClass.Case[newState]
         if action then
            action(self, actual)
         end
      end
   
      --[[
         Get the current state of the FSM
   
         @return string
      ]]
      function componentClass:GetState()
         return self._state or superClass.StateInitial
      end
   
      --[[
         Get the previous state of the FSM
   
         @return string|nil
      ]]
      function componentClass:GetPrevState()
         return self._statePrev or nil
      end
   
      --[[
         Gets the time it changed to the current state
      ]]
      function componentClass:GetStateTime()
         return self._stateTime or 0
      end
   end
   
   return ComponentFSM
   
end

__F__["ECS"] = function()
   -- src/ECS.lua
   --[[
      ECS Lua v2.2.0
   
      ECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.
   
      https://github.com/nidorx/ecs-lua
   
      Discussions about this script are at https://devforum.roblox.com/t/841175
   
      ------------------------------------------------------------------------------
   
      MIT License
   
      Copyright (c) 2020 Alex Rodin
   
      Permission is hereby granted, free of charge, to any person obtaining a copy
      of this software and associated documentation files (the "Software"), to deal
      in the Software without restriction, including without limitation the rights
      to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      copies of the Software, and to permit persons to whom the Software is
      furnished to do so, subject to the following conditions:
   
      The above copyright notice and this permission notice shall be included in all
      copies or substantial portions of the Software.
   
      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      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
      SOFTWARE.
   ]]
   
   local Query = __REQUIRE__("Query")
   local World = __REQUIRE__("World")
   local System = __REQUIRE__("System")
   local Archetype = __REQUIRE__("Archetype")
   local Component = __REQUIRE__("Component")
   
   local function setLoopManager(manager)
      World.LoopManager = manager
   end
   
   pcall(function()
      if (game and game.ClassName == "DataModel") then
         -- is roblox
         setLoopManager(__REQUIRE__("RobloxLoopManager")())
      end
   end)
   
   --[[
     @TODO
      - Server entities
      - Client - Server sincronization (snapshot, delta, spatial index, grid manhatham distance)
      - Table pool (avoid GC)
      - System readonly? Paralel execution
      - Debugging?
      - Benchmark (Local Script vs ECS implementation)
      - Basic physics (managed)
      - SharedComponent?
      - Serializaton
         - world:Serialize()
         - world:Serialize(entity)
         - entity:Serialize()
         - component:Serialize()
   ]]
   local ECS = {
      Query = Query,
      World = World.New,
      System = System.Create,
      Archetype = Archetype,
      Component = Component.Create,
      SetLoopManager = setLoopManager
   }
   
   if _G.ECS == nil then
      _G.ECS = ECS
   else
      local warn = _G.warn or print
      warn("ECS Lua was not registered in the global variables, there is already another object registered.")
   end
   
   return ECS
   
end

__F__["Entity"] = function()
   -- src/Entity.lua
   --[[
      The entity is a fundamental part of the Entity Component System. Everything in your game that has data or an 
      identity of its own is an entity. However, an entity does not contain either data or behavior itself. Instead, 
      the data is stored in the components and the behavior is provided by the systems that process those components. 
   ]]
   
   local Archetype = __REQUIRE__("Archetype")
   
   local SEQ  = 0
   
   --[[
      [GET]
      01) comp1 = entity[CompType1]
      02) comp1 = entity:Get(CompType1)
      03) comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)
   ]]
   local function getComponent(entity, ...)
   
      local values = {...}
      local data = entity._data
      
      if (#values == 1) then
         local cType = values[1]
         if (cType.IsCType and not cType.isComponent) then
            -- 01) comp1 = entity[CompType1]
            -- 02) comp1 = entity:Get(CompType1)
            return data[cType]
         else
            return nil
         end
      end
      
      -- 03) comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)
      local components = {}   
      for i,cType in ipairs(values) do
         if (cType.IsCType and not cType.isComponent) then         
            table.insert(components, data[cType])
         end
      end
      return table.unpack(components)
   end
   
   --[[
      Merges the qualifiers of a new added component.
   
      @param entity {Entity}
      @param newComponent {Component}
      @param newComponentClass {ComponentClass}
   ]]
   local function mergeComponents(entity, newComponent, newComponentClass)
      local data = entity._data
      local otherComponent
      -- get first instance
      for _,oCType in ipairs(newComponentClass.Qualifiers()) do
         if oCType ~= newComponentClass then
            otherComponent = data[oCType]
            if otherComponent then
               break
            end
         end
      end
   
      if otherComponent then
         otherComponent:Merge(newComponent)
      end
   end
   
   --[[
      [SET]
      01) entity[CompType1] = nil
      02) entity[CompType1] = value
      03) entity:Set(CompType1, nil)   
      04) entity:Set(CompType1, value)
      05) entity:Set(comp1)
      06) entity:Set(comp1, comp2, ...)
   ]]
   local function setComponent(entity, ...)
   
      local values = {...}
      local data = entity._data
      local archetypeOld = entity.archetype
      local archetypeNew = archetypeOld
   
      local toMerge = {}
   
      local cType = values[1]   
      if (cType and cType.IsCType and not cType.isComponent) then
         local value = values[2]
         local component
         -- 01) entity[CompType1] = nil
         -- 02) entity[CompType1] = value
         -- 03) entity:Set(CompType1, nil)   
         -- 04) entity:Set(CompType1, value)
         if value == nil then
            local old = data[cType]
            if old then
               old:Detach()
            end
   
            data[cType] = nil
            archetypeNew = archetypeNew:Without(cType)
   
         elseif (type(value) == "table" and value.isComponent) then
            local old = data[cType]         
            if (old ~= value) then
               if old then
                  old:Detach()
               end
   
               cType = value:GetType()
               data[cType] = value
               archetypeNew = archetypeNew:With(cType)
   
               -- merge components
               if (cType.HasQualifier or cType.IsQualifier) then
                  mergeComponents(entity, value, cType)
               end
            end
         else
            local old = data[cType]
            if old then
               old:Detach()
            end
   
            local component = cType(value)
            data[cType] = component
            archetypeNew = archetypeNew:With(cType)
   
            -- merge components
            if (cType.HasQualifier or cType.IsQualifier) then
               mergeComponents(entity, component, cType)
            end
         end
      else
         -- 05) entity:Set(comp1)
         -- 06) entity:Set(comp1, comp2, ...)
         for i,component in ipairs(values) do
            if (component.isComponent) then
               local cType = component:GetType()       
               local old = data[cType]
               if (old ~= component) then
                  if old then
                     old:Detach()
                  end
   
                  data[cType] = component
                  archetypeNew = archetypeNew:With(cType)
                  
                  -- merge components
                  if (cType.HasQualifier or cType.IsQualifier) then
                     mergeComponents(entity, component, cType)
                  end
               end              
            end
         end
      end
   
      if (archetypeOld ~= archetypeNew) then
         entity.archetype = archetypeNew
         entity._onChange:Fire(entity, archetypeOld)
      end
   end
   
   --[[
      [UNSET]
      01) enity:Unset(comp1)
      02) entity[CompType1] = nil
      03) enity:Unset(CompType1)
      04) enity:Unset(comp1, comp1, ...)
      05) enity:Unset(CompType1, CompType2, ...)
   ]]
   local function unsetComponent(entity, ...)
   
      local data = entity._data
      local archetypeOld = entity.archetype
      local archetypeNew = archetypeOld
   
      for _,value in ipairs({...}) do
         if value.isComponent then
            -- 01) enity:Unset(comp1)
            -- 04) enity:Unset(comp1, comp1, ...)
            local cType = value:GetType()  
            local old = data[cType]
            if old then
               old:Detach()
            end
            data[cType] = nil
            archetypeNew = archetypeNew:Without(cType)
            
         elseif value.IsCType then
            -- 02) entity[CompType1] = nil
            -- 03) enity:Unset(CompType1)
            -- 05) enity:Unset(CompType1, CompType2, ...)
            local old = data[value]
            if old then
               old:Detach()
            end
            data[value] = nil
            archetypeNew = archetypeNew:Without(value)
         end
      end
   
      if entity.archetype ~= archetypeNew then
         entity.archetype = archetypeNew
         entity._onChange:Fire(entity, archetypeOld)
      end
   end
   
   --[[
      01) comps = entity:GetAll()
      01) qualifiers = entity:GetAll(PrimaryClass)
   ]]
   local function getAll(entity, qualifier)
      local data = entity._data
      local components = {}
      if (qualifier ~= nil and qualifier.IsCType and not qualifier.isComponent) then
         local ctypes = qualifier.Qualifiers()
         for _,cType in ipairs(ctypes) do
            local component = data[cType]
            if component then
               table.insert(components, component)
            end
         end
      else
         for _, component in pairs(data) do
            table.insert(components, component)
         end
      end
   
      return components
   end
   
   --[[
      01) comp = entity:GetAny(PrimaryClass)
   ]]
   local function getAny(entity, qualifier)
      if (qualifier ~= nil and qualifier.IsCType and not qualifier.isComponent) then
         local data = entity._data
         local ctypes = qualifier.Qualifiers()
         for _,cType in ipairs(ctypes) do
            local component = data[cType]
            if component then
               return component
            end
         end
      end
   end
   
   local Entity = {
      __index = function(e, key)
         if (type(key) == "table") then 
            -- 01) local comp1 = entity[CompType1]
            -- 01) local comps = entity[{CompType1, CompType2, ...}]
            return getComponent(e, key)
         end
      end,
      __newindex = function(e, key, value)
         local isComponentSet = true
         if (type(key) == "table" and (key.IsCType and not key.isComponent)) then
            -- 01) entity[CompType1] = nil
            -- 02) entity[CompType1] = value
            setComponent(e, key, value)
         else
            rawset(e, key, value)
         end
      end
   }
   
   --[[
      Creates an entity having components of the specified types.
   
      @param onChange {Event}
      @param components {Component[]} (Optional)
   ]]
   function Entity.New(onChange, components)
   
      local archetype = Archetype.EMPTY
      local data = {}
      if (components ~= nil and #components > 0) then
         local cTypes = {}
         for _, component in ipairs(components) do
            local cType = component:GetType()
            table.insert(cTypes, cType)
            data[cType] = component
         end
         archetype = Archetype.Of(cTypes)
      end
   
      SEQ = SEQ + 1
   
      return setmetatable({
         _data = data,
         _onChange = onChange,
         id = SEQ,
         isAlive = false,
         archetype = archetype,
         Get = getComponent,
         Set = setComponent,
         Unset = unsetComponent,
         GetAll = getAll,
         GetAny = getAny,
      }, Entity)
   end
   
   return Entity
   
end

__F__["EntityRepository"] = function()
   -- src/EntityRepository.lua
   local Event = __REQUIRE__("Event")
   
   --[[
      The repository (database) of entities in a world.
   
      The repository indexes entities by archetype. Whenever the entity's archetype is changed, the entity is 
      transported to the correct storage.
   ]]
   local EntityRepository = {}
   EntityRepository.__index = EntityRepository
   
   --[[
      Create a new repository
   
      @return EntityRepository
   ]]
   function EntityRepository.New()
      return setmetatable({
         _archetypes = {},
         _entitiesArchetype = {},
         
      }, EntityRepository)
   end
   
   --[[
      Insert an entity into this repository
   
      @param entity {Entity}
   ]]
   function EntityRepository:Insert(entity)
      if (self._entitiesArchetype[entity] == nil) then
         local archetype = entity.archetype
         local storage = self._archetypes[archetype]
         if (storage == nil) then
            storage = { count = 0, entities = {} }
            self._archetypes[archetype] = storage
         end
      
         storage.entities[entity] = true
         storage.count = storage.count + 1
         
         self._entitiesArchetype[entity] = archetype
      else
         self:Update(entity)
      end
   end
   
   --[[
      Remove an entity from this repository
   
      @param entity {Entity}
   ]]
   function EntityRepository:Remove(entity)
      local archetypeOld = self._entitiesArchetype[entity]
      if archetypeOld == nil then
         return
      end
      self._entitiesArchetype[entity] = nil
   
      local storage = self._archetypes[archetypeOld]
      if (storage ~= nil and storage.entities[entity] == true) then
         storage.entities[entity] = nil
         storage.count = storage.count - 1
         if (storage.count == 0) then
            self._archetypes[archetypeOld] = nil
         end
      end
   end
   
   --[[
      Updates the entity in the repository, if necessary, moves the entity from one storage to another
   
      @param entity {Entity}
   ]]
   function EntityRepository:Update(entity)
      local archetypeOld = self._entitiesArchetype[entity]
      if (archetypeOld == nil or archetypeOld == entity.archetype) then
         return
      end
   
      self:Remove(entity)
      self:Insert(entity)
   end
   
   --[[
      Execute the query entered in this repository
   
      @param query {Query}
      @return QueryResult
   ]]
   function EntityRepository:Query(query)
      local chunks = {}
      for archetype, storage in pairs(self._archetypes) do
         if query:Match(archetype) then
            table.insert(chunks, storage.entities)
         end
      end
      return query:Result(chunks), #chunks > 0
   end
   
   --[[
      Quick check to find out if a query is applicable.
   
      @param query {Query}
      @return bool
   ]]
   function EntityRepository:FastCheck(query)
      for archetype, storage in pairs(self._archetypes) do
         if query:Match(archetype) then
            return true
         end
      end
      return false
   end
   
   return EntityRepository
   
end

__F__["Event"] = function()
   -- src/Event.lua
   
   --[[
      Subscription
   ]]
   local Connection = {}
   Connection.__index = Connection
   
   function Connection.New(event, handler)
      return setmetatable({ _event = event, _handler = handler }, Connection)
   end
   
   -- Unsubscribe
   function Connection:Disconnect()
      local event = self._event
      if (event and not event.destroyed) then
         local idx = table.find(event._handlers, self._handler)
         if idx ~= nil then
            table.remove(event._handlers, idx)
         end
      end
      setmetatable(self, nil)
   end 
   
   --[[
      Observer Pattern
   
      Allows the application to fire events of a particular type.
   ]]
   local Event = {}
   Event.__index = Event
   
   function Event.New()
   	return setmetatable({ _handlers = {} }, Event)
   end
   
   function Event:Connect(handler)
   	if (type(handler) == "function") then
         table.insert(self._handlers, handler)
         return Connection.New(self, handler)
   	end
   
      error(("Event:Connect(%s)"):format(typeof(handler)), 2)
   end
   
   function Event:Fire(...)
   	if not self.destroyed then
         for i,handler in ipairs(self._handlers) do
            handler(table.unpack({...}))
         end
   	end
   end
   
   function Event:Destroy()
   	setmetatable(self, nil)
      self._handlers = nil
      self.destroyed = true
   end
   
   return Event
   
end

__F__["Query"] = function()
   -- src/Query.lua
   
   local QueryResult = __REQUIRE__("QueryResult")
   
   --[[
      Global cache result.
   
      The validated components are always the same (reference in memory, except within the archetypes),
      in this way, you can save the result of a query in an archetype, reducing the overall execution
      time (since we don't need to iterate all the time)
   
      @type KEY string = concat(Array<ComponentClass.Id>, "_")
      @Type {
         [Archetype] = {
            Any  = { [KEY] = bool },
            All  = { [KEY] = bool },
            None = { [KEY] = bool },
         }
      }
   ]]
   local CACHE = {}
   
   --[[
      Interface for creating filters for existing entities in the ECS world
   ]]
   local Query = {}
   Query.__index = Query
   setmetatable(Query, {
      __call = function(t, all, any, none)
         return Query.New(all, any, none)
      end,
   })
   
   local function parseFilters(list, clauseGroup, clauses)
      local indexed = {}
      local cTypes = {}
      local cTypeIds = {}
      
      for i,item in ipairs(list) do
         if (indexed[item] == nil) then
            if (item.IsCType and not item.isComponent) then
               indexed[item] = true
               table.insert(cTypes, item)
               table.insert(cTypeIds, item.Id)
            else
               -- clauses
               if item.Filter then
                  indexed[item] = true
                  item[clauseGroup] = true
                  table.insert(clauses, item)
               end
            end
         end
      end
   
      if #cTypes > 0 then
         table.sort(cTypeIds)
         local cTypesKey = "_" .. table.concat(cTypeIds, "_")   
         return cTypes, cTypesKey
      end
   end
   
   --[[
      Create a new Query used to filter entities in the world. It makes use of local and global cache in order to 
      decrease the validation time (avoids looping in runtime of systems)
   
      @param all {Array<ComponentClass|Clause>} Optional All component types in this array must exist in the archetype
      @param any {Array<ComponentClass|Clause>} Optional At least one of the component types in this array must exist in the archetype
      @param none {Array<ComponentClass|Clause>} Optional None of the component types in this array can exist in the archetype
   ]]
   function Query.New(all, any, none)
   
      -- used by QueryResult
      local clauses = {}
   
      local anyKey, allKey, noneKey
   
      if (any ~= nil) then
         any, anyKey = parseFilters(any, "IsAnyFilter", clauses)
      end
   
      if (all ~= nil) then
         all, allKey = parseFilters(all, "IsAllFilter", clauses)
      end
   
      if (none ~= nil) then
         none, noneKey = parseFilters(none, "IsNoneFilter", clauses)
      end
   
      return setmetatable({
         isQuery = true,
         _any = any,
         _all = all,
         _none = none,
         _anyKey = anyKey,
         _allKey = allKey,
         _noneKey = noneKey,
         _cache = {}, -- local cache (L1)
         _clauses = #clauses > 0 and clauses or nil
      }, Query)
   end
   
   --[[
      Generate a QueryResult with the chunks entered and the clauses of the current query
   
      @param chunks {Chunk}
      @return QueryResult
   ]]
   function Query:Result(chunks)
      return QueryResult.New(chunks, self._clauses)
   end
   
   --[[
      Checks if the entered archetype is valid by the query definition
   
      @param archetype {Archetype}
      @return bool
   ]]
   function Query:Match(archetype)
   
      -- cache L1
      local localCache = self._cache
      
      -- check local cache (L1)
      local cacheResult = localCache[archetype]
      if cacheResult ~= nil then
         return cacheResult
      else
         -- check global cache (executed by other filter instance)
         local globalCache = CACHE[archetype]
         if (globalCache == nil) then
            globalCache = { Any = {}, All = {}, None = {} }
            CACHE[archetype] = globalCache
         end
         
         -- check if these combinations exist in this component array
   
         local noneKey = self._noneKey
         if noneKey then
            local isNoneValid = globalCache.None[noneKey]
            if (isNoneValid == nil) then
               isNoneValid = true
               for _, cType in ipairs(self._none) do
                  if archetype:Has(cType) then
                     isNoneValid = false
                     break
                  end
               end
               globalCache.None[noneKey] = isNoneValid
            end
   
            if (isNoneValid == false) then
               localCache[archetype] = false
               return false
            end     
         end
   
         local anyKey = self._anyKey
         if anyKey then
            local isAnyValid = globalCache.Any[anyKey]
            if (isAnyValid == nil) then
               isAnyValid = false
               if (globalCache.All[anyKey] == true) then
                  isAnyValid = true
               else
                  for _, cType in ipairs(self._any) do
                     if archetype:Has(cType) then
                        isAnyValid = true
                        break
                     end
                  end
               end
               globalCache.Any[anyKey] = isAnyValid
            end
   
            if (isAnyValid == false) then
               localCache[archetype] = false
               return false
            end
         end
   
         local allKey = self._allKey
         if allKey then
            local isAllValid = globalCache.All[allKey]
            if (isAllValid == nil) then
               local haveAll = true
               for _, cType in ipairs(self._all) do
                  if (not archetype:Has(cType)) then
                     haveAll = false
                     break
                  end
               end
   
               if haveAll then
                  isAllValid = true
               else
                  isAllValid = false
               end
   
               globalCache.All[allKey] = isAllValid
            end
   
            localCache[archetype] = isAllValid
            return isAllValid
         end
   
         -- empty query = SELECT * FROM
         localCache[archetype] = true
         return true
      end
   end
   
   local function builder()
      local builder = {
         isQueryBuilder = true
      }
      local query
   
      function builder.All(...)
         query = nil
         builder._all = {...}
         return builder
      end
      
      function builder.Any(...)
         query = nil
         builder._any = {...}
         return builder
      end
      
      function builder.None(...)
         query = nil
         builder._none = {...}
         return builder
      end
   
      function builder.Build()
         if query == nil then
            query = Query.New(builder._all, builder._any, builder._none)
         end
         return query
      end
   
      return builder
   end
   
   function Query.All(...)
      return builder().All(...)
   end
   
   function Query.Any(...)
      return builder().Any(...)
   end
   
   function Query.None(...)
      return builder().None(...)
   end
   
   --[[
      Create custom filters that can be used in Queries. Its execution is delayed, invoked only in QueryResult methods
   
      The result of executing the clause depends on how it was used in the query.
   
      Ex. If used in Query.All() the result is the inverse of using the same clause in Query.None()
   
         local Player = ECS.Component({ health = 100 })
   
         local HealthPlayerFilter = ECS.Query.Filter(function(entity, config)
            local player = entity[Player]
            return player.health >= config.minHealth and player.health <= config.maxHealth
         end)
   
         local healthyClause = HealthPlayerFilter({
            minHealth = 80,
            maxHealth = 100,
         })
   
         local healthyQuery = ECS.Query.All(Player, healthyClause)
         world:Exec(healthyQuery):ForEach(function(entity)
            -- this player is very healthy
         end)
   
         local notHealthyQuery = ECS.Query.All(Player).None(healthyClause)
         world:Exec(healthyQuery):ForEach(function(entity)
            -- this player is NOT very healthy
         end)
   
         local dyingClause = HealthPlayerClause({
            minHealth = 1,
            maxHealth = 20,
         })
   
         local dyingQuery = ECS.Query.All(Player, dyingClause)
         world:Exec(dyingQuery):ForEach(function(entity)
            -- this player is about to die
         end)
   
         local notDyingQuery = ECS.Query.All(Player).None(dyingClause)
         world:Exec(notDyingQuery):ForEach(function(entity)
            -- this player is NOT about to die
         end)
   
      @param filter {function(entity, config):bool} 
      @return function(config):Clause
   ]]
   function Query.Filter(filter)
      return function (config)
         return {
            Filter = filter,
            Config = config
         }
      end
   end
   
   return Query
   
end

__F__["QueryResult"] = function()
   -- src/QueryResult.lua
   
   --[[
      OperatorFunction = function(param, value, count) => newValue, acceptItem, mustContinue
   ]]
   
   local function operatorFilter(predicate, value, count)
      return value, (predicate(value) == true), true
   end
   
   local function operatorMap(mapper, value, count)
      return mapper(value), true, true
   end
   
   local function operatorLimit(limit, value, count)
      local accept = (count <= limit)
      return value, accept, accept
   end
   
   local function operatorClauseNone(clauses, value, count)
      local acceptItem = true
      for _,clause in ipairs(clauses) do
         if (clause.Filter(value, clause.Config) == true) then
            acceptItem = false
            break
         end 
      end
      return value, acceptItem, true
   end
   
   local function operatorClauseAll(clauses, value, count)
      local acceptItem = true
      for _,clause in ipairs(clauses) do
         if (clause.Filter(value, clause.Config) == false) then
            acceptItem = false
            break
         end
      end
      return value, acceptItem, true
   end
   
   local function operatorClauseAny(clauses, value, count)
      local acceptItem = false
      for _,clause in ipairs(clauses) do
         if (clause.Filter(value, clause.Config) == true) then
            acceptItem = true
            break
         end
      end
      return value, acceptItem, true
   end
   
   local EMPTY_OBJECT = {}
   
   --[[
      The result of a Query that was executed on an EntityStorage.
   
      QueryResult provides several methods to facilitate the filtering of entities resulting from the execution of the 
      query.
   ]]
   local QueryResult = {}
   QueryResult.__index = QueryResult
   
   --[[
      Build a new QueryResult
   
      @param chunks { Array<{ [Entity] = true }> }
      @clauses {Clause[]}
   
      @see Query.lua
      @see EntityRepository:Query(query)
      @return QueryResult
   ]]
   function QueryResult.New(chunks, clauses)
   
      local pipeline = EMPTY_OBJECT
      if (clauses and #clauses > 0) then
         local all = {}
         local any = {}
         local none = {}
   
         pipeline = {}
         
         for i,clause in ipairs(clauses) do
            if clause.IsNoneFilter then
               table.insert(none, clause)
            elseif clause.IsAnyFilter then
               table.insert(any, clause)
            else
               table.insert(all, clause)
            end 
         end
   
         if (#none > 0) then
            table.insert(pipeline, {operatorClauseNone, none})
         end
         
         if (#all > 0) then
            table.insert(pipeline, {operatorClauseAll, all})
         end
   
         if (#any > 0) then
            table.insert(pipeline, {operatorClauseAny, any})
         end
        
      end
   
      return setmetatable({
         chunks = chunks,
         _pipeline = pipeline,
      }, QueryResult)
   end
   
   --[[ -------------------------------------------------------------------------------------------------------------------
      Intermediate Operations
   
      Intermediate operations return a new QueryResult. They are always lazy; executing an intermediate operation such as 
      QueryResult:Filter() does not actually perform any filtering, but instead creates a new QueryResult that, when traversed, 
      contains the elements of the initial QueryResult that match the given predicate. Traversal of the pipeline source 
      does not begin until the terminal operation of the pipeline is executed.
   ]] ---------------------------------------------------------------------------------------------------------------------
   
   --[[
      Returns a QueryResult consisting of the elements of this QueryResult with a new pipeline operation
   
      @param operation {function(param, value, count) -> newValue, accept, continues}
      @param param {any}
      @return the new QueryResult
   ]]
   function QueryResult:With(operation, param)
      local pipeline = {}
      for _,operator in ipairs(self._pipeline) do
         table.insert(pipeline, operator)
      end
      table.insert(pipeline, { operation, param })
   
      return setmetatable({
         chunks = self.chunks,
         _pipeline = pipeline,
      }, QueryResult)
   end
   
   --[[
      Returns a QueryResult consisting of the elements of this QueryResult that match the given predicate.
   
      @param predicate {function(value) -> bool} a predicate to apply to each element to determine if it should be included
      @return the new QueryResult
   ]]
   function QueryResult:Filter(predicate)
      return self:With(operatorFilter, predicate)
   end
   
   --[[
      Returns a QueryResult consisting of the results of applying the given function to the elements of this QueryResult.
   
      @param mapper {function(value) -> newValue} a function to apply to each element
      @return the new QueryResult
   ]]
   function QueryResult:Map(mapper)
      return self:With(operatorMap, mapper)
   end
   
   --[[
      Returns a QueryResult consisting of the elements of this QueryResult, truncated to be no longer than maxSize in length.
      
      This is a short-circuiting stateful intermediate operation.
   
      @param maxSize {number}
      @return the new QueryResult
   ]]
   function QueryResult:Limit(maxSize)
      return self:With(operatorLimit, maxSize)
   end
   
   --[[ -------------------------------------------------------------------------------------------------------------------
      Terminal Operations
   
      Terminal operations, such as QueryResult:ForEach or QueryResult.AllMatch, may traverse the QueryResult to produce a 
      result or a side-effect. After the terminal operation is performed, the pipeline is considered consumed, and can no 
      longer be used; if you need to traverse the same data source again, you must return to the data source to get a new 
      QueryResult.
   ]] ---------------------------------------------------------------------------------------------------------------------
   
   --[[
      Returns whether any elements of this result match the provided predicate.
   
      @param predicate { function(value) -> bool} a predicate to apply to elements of this result
      @returns true if any elements of the result match the provided predicate, otherwise false
   ]]
   function QueryResult:AnyMatch(predicate)
      local anyMatch = false
      self:ForEach(function(value)
         if predicate(value) then
            anyMatch = true
         end
         -- break if true
         return anyMatch
      end)
      return anyMatch
   end
   
   --[[
      Returns whether all elements of this result match the provided predicate.
   
      @param predicate { function(value) -> bool} a predicate to apply to elements of this result
      @returns true if either all elements of the result match the provided predicate or the result is empty, otherwise false
   ]]
   function QueryResult:AllMatch(predicate)
      local allMatch = true
      self:ForEach(function(value)
         if (not predicate(value)) then
            allMatch = false
         end
         -- break if false
         return allMatch == false
      end)
      return allMatch
   end
   
   --[[
      Returns some element of the result, or nil if the result is empty.
   
      This is a short-circuiting terminal operation.
   
      The behavior of this operation is explicitly nondeterministic; it is free to select any element in the result. 
      
      Multiple invocations on the same result may not return the same value.
   
      @return {any}
   ]]
   function QueryResult:FindAny()
      local out
      self:ForEach(function(value)
         out = value
         -- break
         return true
      end)
      return out
   end
   
   --[[
      Returns an array containing the elements of this QueryResult.
   
      This is a terminal operation.
   ]]
   function QueryResult:ToArray()
      local array = {}
      self:ForEach(function(value)
         table.insert(array, value)
      end)
      return array
   end
   
   --[[
      Returns an Iterator, to use in for loop
   
      for count, entity in result:Iterator() do
         print(entity.id)
      end
   ]]
   function QueryResult:Iterator()
      local thread = coroutine.create(function()
         self:ForEach(function(value, count)
            -- These will be passed back again next iteration
            coroutine.yield(value, count)
         end)
      end)
   
      return function()
         local success, item, index = coroutine.resume(thread)
         return index, item
      end
   end
   
   --[[
      Performs an action for each element of this QueryResult.
   
      This is a terminal operation.
   
      The behavior of this operation is explicitly nondeterministic. This operation does not guarantee to respect the 
      encounter order of the QueryResult.
   
      @param action {function(value, count) -> bool} A action to perform on the elements, breaks execution case returns true
   ]]
   function QueryResult:ForEach(action)
      local count = 1
      local pipeline = self._pipeline
   
      local hasPipeline = #pipeline > 0 
      if (not hasPipeline) then
         -- faster
         for _, entities in ipairs(self.chunks) do
            for entity, _ in pairs(entities) do
               if (action(entity, count) == true) then
                  return
               end
               count = count + 1  
            end
         end
      else
         -- Pipeline this QueryResult, applying callback to each value
         for i, entities in ipairs(self.chunks) do
            for entity,_ in pairs(entities) do
               local mustStop = false
               local itemAccepted = true
   
               local value = entity
               if (itemAccepted and hasPipeline) then               
                  for _, operator in ipairs(pipeline) do
                     local newValue, acceptItem, canContinue = operator[1](operator[2], value, count)
                     if (not canContinue) then
                        mustStop = true
                     end
      
                     if acceptItem then
                        value = newValue
                     else
                        itemAccepted = false
                        break
                     end
                  end
               end
               
               if itemAccepted then
                  if (action(value, count) == true) then
                     return
                  end
                  count = count + 1
               end
      
               if mustStop then
                  return
               end
            end
         end
      end
   end
   
   return QueryResult
   
end

__F__["RobloxLoopManager"] = function()
   -- src/RobloxLoopManager.lua
   local function InitManager()
      local RunService = game:GetService("RunService")
      
      return {
         Register = function(world)         
            -- if not RunService:IsRunning() then
            --    return
            -- end
            local beforePhysics = RunService.Stepped:Connect(function()
               world:Update("process", os.clock())
            end)
      
            local afterPhysics = RunService.Heartbeat:Connect(function()
               world:Update("transform", os.clock())
            end)
      
            local beforeRender
            if (not RunService:IsServer()) then
               beforeRender = RunService.RenderStepped:Connect(function()
                  world:Update("render", os.clock())
               end)
            end
      
            return function()
               beforePhysics:Disconnect()
               afterPhysics:Disconnect()
               if beforeRender then
                  beforeRender:Disconnect()
               end
            end
         end
      }
   end
   
   return InitManager
   
end

__F__["System"] = function()
   -- src/System.lua
   
   local STEPS = { "task", "render", "process", "transform" }
   
   local System = {}
   
   --[[
      Create new System Class
   
      @param step {process|transform|render|task}
      @param order {number} (Optional) Allows you to set an execution order (for systems that are not `task`). Default 50
      @param query {Query|QueryBuilder} (Optional) Filters the entities that will be processed by this system
      @param updateFn {function(self, Time)} (Optional) A shortcut for creating systems that only have the Update method
      @return SystemClass
   ]]
   function System.Create(step, order, query, updateFn)
   
      if (step == nil or not table.find(STEPS, step)) then
         error("The step parameter must one of ", table.concat(STEPS, ", "))
      end
   
      if (order and type(order) == "function") then
         updateFn = order
         order = nil
      elseif query and type(query) == "function" then
         updateFn = query
         query = nil
      end
   
      if (order and type(order) == "table" and (order.isQuery or order.isQueryBuilder)) then
         query = order
         order = nil
      end
   
      if (order == nil or order < 0) then
         order = 50
      end
   
      if type(query) == "function" then
         updateFn = query
         query = nil
      end
   
      if (query and query.isQueryBuilder) then
         query = query.Build()
      end
   
      local SystemClass = {
         Step = step,
         -- Allows you to define the execution priority level for this system
         Order = order,
         Query = query,
         -- After = {SystemC, SystemD}, When the system is a task, it allows you to define that this system should run AFTER other specific systems.
         -- Before = {SystemA, SystemB}, When the system is a task, it allows you to define that this system should run BEFORE other specific systems.
         --[[
   
            ShouldUpdate(Time) -> bool - Invoked before 'Update', allows you to control the execution of the update
            Update(Time)
   
            [QuerySystem]
               OnRemove(Time, enity)
               OnExit(Time, entity)
               OnEnter(Time, entity)
         ]]
      }
      SystemClass.__index = SystemClass
   
      --[[
         Create an instance of this system
   
         @param world {World}
         @param config {table}
      ]]
      function SystemClass.New(world, config)
         local system = setmetatable({
            version = 0,
            _world = world,
            _config = config,
         }, SystemClass)
   
         if system.Initialize then
            system:Initialize(config)
         end
   
         return system
      end
   
       --[[
         Get this system class
   
         @return SystemClass
      ]]
      function SystemClass:GetType()
         return SystemClass
      end
   
      --[[
         Run a query in the world. A shortcut to `self._world:Exec(query)`
   
         @query {Query|QueryBuilder} Optional If nil, use default query
         @return QueryResult
      ]]
      function SystemClass:Result(query)
         return self._world:Exec(query or SystemClass.Query)
      end
   
      --[[
         destroy this instance
      ]]
      function SystemClass:Destroy() 
         if self.OnDestroy then
            self.OnDestroy()
         end
         setmetatable(self, nil)
         for k,v in pairs(self) do
            self[k] = nil
         end
      end
   
      if updateFn and type(updateFn) == "function" then
         SystemClass.Update = updateFn
      end
   
      return SystemClass
   end
   
   return System
   
end

__F__["SystemExecutor"] = function()
   -- src/SystemExecutor.lua
   
   --[[
      After = {SystemC, SystemD}, An update order that requests ECS update this system after it updates another specified system.
      Before = {SystemA, SystemB}, An update order that requests ECS update this system before it updates another specified system.
   ]]
   local function mapTaskDependencies(systems)
   
      local nodes = {}
      local nodesByType = {}
   
      for i,system in ipairs(systems) do
         local sType = system:GetType()
   
         if (system._TaskState == nil) then
            -- suspended, scheduled, running
            system._TaskState = "suspended"
         end
   
         if not nodesByType[sType] then
            local node = {
               Type = sType,
               System = system,
               -- @type {[Node]=true}
               Depends = {}
            }
            nodesByType[sType] = node
            table.insert(nodes, node)        
         end
      end
   
      for _, node in ipairs(nodes) do
          -- this system will update Before another specified system
          local before = node.Type.Before
          if (before ~= nil and #before > 0) then
             for _,sTypeOther in ipairs(before) do
                local otherNode = nodesByType[sTypeOther]
                if otherNode then
                   otherNode.Depends[node] = true
                end
             end
          end
   
         -- this system will update After another specified system
         local after = node.Type.After
         if (after ~= nil and #after > 0) then
            for _,sTypeOther in ipairs(after) do
               local otherNode = nodesByType[sTypeOther]
               if otherNode then
                  node.Depends[otherNode] = true
               end
            end
         end
      end
   
      return nodes
   end
   
   local function orderSystems(a, b)
      return a.Order < b.Order
   end
   
   --[[
      Responsible for coordinating and executing the systems methods
   ]]
   local SystemExecutor = {}
   SystemExecutor.__index = SystemExecutor
   
   function SystemExecutor.New(world)   
      local executor =  setmetatable({
         _world = world,
         _onExit = {},
         _onEnter = {},
         _onRemove = {},
         _task = {},
         _render = {},
         _process = {},
         _transform = {},
         _schedulers = {},
         _lastFrameMatchQueries = {},
         _currentFrameMatchQueries = {},
      }, SystemExecutor)
   
      world:OnQueryMatch(function(query)
         executor._currentFrameMatchQueries[query] = true
      end)
   
      return executor
   end
   
   function SystemExecutor:SetSystems(systems)
      local onExit = {}
      local onEnter = {}
      local onRemove = {}
      -- system:Update()
      local updateTask = {}
      local updateRender = {}
      local updateProcess = {}
      local updateTransform = {}
   
      for _, system in pairs(systems) do      
         local step = system.Step
         if system.Update then
            if step == "task" then
               table.insert(updateTask, system)
               
            elseif step == "process" then
               table.insert(updateProcess, system) 
   
            elseif step == "transform" then
               table.insert(updateTransform, system)
   
            elseif step == "render" then
               table.insert(updateRender, system)
   
            end
         end
   
         if (system.Query and system.Query.isQuery and step ~= "task") then
            if system.OnExit then
               table.insert(onExit, system)
            end
   
            if system.OnEnter then
               table.insert(onEnter, system)
            end
      
            if system.OnRemove then
               table.insert(onRemove, system)
            end
         end
      end
   
      updateTask = mapTaskDependencies(updateTask)
      
      table.sort(onExit, orderSystems)
      table.sort(onEnter, orderSystems)
      table.sort(onRemove, orderSystems)
      table.sort(updateRender, orderSystems)
      table.sort(updateProcess, orderSystems)
      table.sort(updateTransform, orderSystems)
   
      self._onExit = onExit
      self._onEnter = onEnter
      self._onRemove = onRemove
      self._task = updateTask
      self._render = updateRender
      self._process = updateProcess
      self._transform = updateTransform
   end
   
   --[[
         
      @param Time
      @param changedEntities { { [Entity] = Old<Archetype> } }
   ]]
   function SystemExecutor:ExecOnExitEnter(Time, changedEntities)
      local isEmpty = true
   
      -- { [Old<Archetype>] = { [New<Archetype>] = {Entity, Entity, ...} } }
      local oldIndexed = {}
      for entity, archetypeOld in pairs(changedEntities) do
         local newIndexed = oldIndexed[archetypeOld]
         if not newIndexed then
            newIndexed = {}
            oldIndexed[archetypeOld] = newIndexed
         end
         local archetypeNew = entity.archetype
   
         local entities = newIndexed[archetypeNew]
         if not entities then
            entities = {}
            newIndexed[archetypeNew] = entities
         end
         table.insert(entities, entity)
         isEmpty = false
      end
      if isEmpty then
         return
      end
      self:_ExecOnEnter(Time, oldIndexed)
      self:_ExecOnExit(Time, oldIndexed)
   end
   
   --[[
      Executes the systems' OnEnter method
   
      @param Time {Time}
      @param entities {{[Key=Entity] => Archetype}}
      ]]
   function SystemExecutor:_ExecOnEnter(Time, oldIndexed)
      local world = self._world
      for _, system in ipairs(self._onEnter) do
         local query = system.Query
         for archetypeOld, newIndexed in pairs(oldIndexed) do
            if not query:Match(archetypeOld) then
               for archetypeNew, entities in pairs(newIndexed) do
                  if query:Match(archetypeNew) then
                     for i,entity in ipairs(entities) do                  
                        world.version = world.version + 1   -- increment Global System Version (GSV)
                        system:OnEnter(Time, entity)        -- local dirty = entity.version > system.version
                        system.version = world.version      -- update last system version with GSV
                     end
                  end
               end
            end         
         end
      end
   end
   
   --[[
      Executes the systems' OnExit method
   
      @param Time {Time}
      @param entities {{[Key=Entity] => Archetype}}
   ]]
   function SystemExecutor:_ExecOnExit(Time, oldIndexed)
      local world = self._world
      for _, system in ipairs(self._onExit) do
         local query = system.Query
         for archetypeOld, newIndexed in pairs(oldIndexed) do
            if query:Match(archetypeOld) then
               for archetypeNew, entities in pairs(newIndexed) do
                  if not query:Match(archetypeNew) then
                     for i,entity in ipairs(entities) do                  
                        world.version = world.version + 1   -- increment Global System Version (GSV)
                        system:OnExit(Time, entity)         -- local dirty = entity.version > system.version
                        system.version = world.version      -- update last system version with GSV
                     end
                  end
               end
            end         
         end
      end
   end
   
   --[[
      Executes the systems' OnRemove method
   
      @param Time {Time}
      @param entities {{[Key=Entity] => Archetype}}
   ]]
   function SystemExecutor:ExecOnRemove(Time, removedEntities)
      
      local isEmpty = true
      local oldIndexed = {}
      for entity, archetypeOld in pairs(removedEntities) do
         local entities = oldIndexed[archetypeOld]
         if not entities then
            entities = {}
            oldIndexed[archetypeOld] = entities
         end
         table.insert(entities, entity)
         isEmpty = false
      end
      if isEmpty then
         return
      end
      
      local world = self._world
      for _, system in ipairs(self._onRemove) do 
         for archetypeOld, entities in pairs(oldIndexed) do
            if system.Query:Match(archetypeOld) then
               for i,entity in ipairs(entities) do  
                  world.version = world.version + 1   -- increment Global System Version (GSV)
                  system:OnRemove(Time, entity)       -- local dirty = entity.version > system.version
                  system.version = world.version      -- update last system version with GSV
               end
            end
         end
      end
   end
   
   local function execUpdate(executor, systems, Time)
      local world = executor._world
      local lastFrameMatchQueries = executor._lastFrameMatchQueries
      local currentFrameMatchQueries = executor._currentFrameMatchQueries
      for j, system in ipairs(systems) do
         local canExec = true
         if system.Query then
            local query = system.Query
            if lastFrameMatchQueries[query] == true or currentFrameMatchQueries[query] == true then
               -- If the query ran in the last frame, it is likely to run successfully on this
               canExec = true
            else
               -- Always revalidates, the repository undergoes constant change
               canExec = world:FastCheck(query)
               currentFrameMatchQueries[query] = canExec
            end
         end
         if canExec then
            if (system.ShouldUpdate == nil or system.ShouldUpdate(Time)) then
               world.version = world.version + 1   -- increment Global System Version (GSV)
               system:Update(Time)                 -- local dirty = entity.version > system.version
               system.version = world.version      -- update last system version with GSV
            end
         end
      end
   end
   
   function SystemExecutor:ExecProcess(Time)
      self._currentFrameMatchQueries = {}
      execUpdate(self, self._process, Time)   
   end
   
   function SystemExecutor:ExecTransform(Time)
      execUpdate(self, self._transform, Time)
   end
   
   function SystemExecutor:ExecRender(Time)
      execUpdate(self, self._render, Time)
      self._lastFrameMatchQueries = self._currentFrameMatchQueries
   end
   
   --[[
      Starts the execution of Jobs.
   
      Each Job is performed in an individual coroutine
   
      @param maxExecTime {number} limits the amount of time jobs can run
   ]]
   function SystemExecutor:ExecTasks(maxExecTime)
      while maxExecTime > 0 do
         local hasMore = false
   
         -- https://github.com/wahern/cqueues/issues/231#issuecomment-562838785
         local i, len = 0, #self._schedulers-1
         while i <= len do
            i = i + 1
   
            local scheduler = self._schedulers[i]
            local tasksTime, hasMoreTask = scheduler.Resume(maxExecTime)
            
            if hasMoreTask then
               hasMore = true
            end
      
            maxExecTime = maxExecTime - (tasksTime + 0.00001)
            
            if (maxExecTime <= 0) then
               break
            end
         end
   
         if not hasMore then
            return
         end
      end
   end
   
   local function execTask(node, Time, world, onComplete)
      local system = node.System
      system._TaskState = "running"
      if (system.ShouldUpdate == nil or system.ShouldUpdate(Time)) then
         world.version = world.version + 1   -- increment Global System Version (GSV)
         system:Update(Time)                 -- local dirty = entity.version > system.version
         system.version = world.version      -- update last system version with GSV
      end
      system._TaskState = "suspended"
      onComplete(node)
   end
   
   --[[
      Invoked at the beginning of each frame, it schedules the execution of the next tasks
   ]]
   function SystemExecutor:ScheduleTasks(Time)
      local world = self._world
   
      local rootNodes = {}    -- Node[]
      local runningNodes = {} -- Node[]
      local scheduled = {}    -- { [Node] = true }
      local completed = {}    -- { [Node] = true }
      local dependents = {}   -- { [Node] = Node[] }
   
      local i, len = 0, #self._task-1
      while i <= len do
         i = i + 1
         local node = self._task[i]
         
         if (node.System._TaskState == "suspended") then
            -- will be executed
            node.System._TaskState = "scheduled"
   
            local hasDependencies = false
            for other,_ in pairs(node.Depends) do
               hasDependencies = true
               if dependents[other] == nil then
                  dependents[other] = {}
               end
               table.insert(dependents[other], node)
            end
            
            if (not hasDependencies) then
               table.insert(rootNodes, node)
            end
   
            scheduled[node] = true
         end
      end
   
      -- suspended, scheduled, running
      local function onComplete(node)
   
         node.Thread = nil
         node.LastExecTime = nil
         completed[node] = true
   
         -- alguma outra tarefa depende da execucao deste no para executar?
         if dependents[node] then
            local dependentsFromNode = dependents[node]
   
            local i, len = 0, #dependentsFromNode-1
            while i <= len do
               i = i + 1
               local dependent = dependentsFromNode[i]
               if scheduled[dependent] then
                  local allDependenciesCompleted = true
                  for otherNode,_ in pairs(dependent.Depends) do
                     if completed[otherNode] ~= true then
                        allDependenciesCompleted = false
                        break
                     end
                  end
      
                  if allDependenciesCompleted then
                     scheduled[dependent] = nil
                     dependent.LastExecTime = 0
                     dependent.Thread = coroutine.create(execTask)
                     table.insert(runningNodes, dependent)
                  end
               end
            end
         end
      end
   
      if #rootNodes > 0 then
         local i, len = 0, #rootNodes-1
         while i <= len do
            i = i + 1
            local node = rootNodes[i]
            scheduled[node] = nil
            node.LastExecTime = 0
            node.Thread = coroutine.create(execTask)
            table.insert(runningNodes, node)
         end
   
         local scheduler
         scheduler = {
            Resume = function(maxExecTime)
   
               -- orders the threads, executing the ones with the least execution time first this prevents long tasks 
               -- from taking up all the processing time
               table.sort(runningNodes, function(nodeA, nodeB)
                  return nodeA.LastExecTime < nodeB.LastExecTime
               end)
   
               local totalTime = 0
   
               -- https://github.com/wahern/cqueues/issues/231#issuecomment-562838785
               local i, len = 0, #runningNodes-1
               while i <= len do
                  i = i + 1
                  local node = runningNodes[i]
   
                  if node.Thread ~= nil then
                     local execTime = os.clock()
                     node.LastExecTime = execTime
      
                     coroutine.resume(node.Thread, node, Time, world, onComplete)
      
                     totalTime = totalTime + (os.clock() - execTime)
      
                     if (totalTime > maxExecTime) then
                        break
                     end
                  end
               end
   
               -- remove completed
               for i,node in ipairs(runningNodes) do
                  if node.Thread == nil then                  
                     local idx = table.find(runningNodes, node)
                     if idx ~= nil then
                        table.remove(runningNodes, idx)
                     end
                  end
               end
   
               local hasMore = #runningNodes > 0
      
               if (not hasMore) then
                  local idx = table.find(self._schedulers, scheduler)
                  if idx ~= nil then
                     table.remove(self._schedulers, idx)
                  end
               end
   
               return totalTime, hasMore
            end
         }
   
         table.insert(self._schedulers, scheduler)
      end
   end
   
   return SystemExecutor
   
end

__F__["Timer"] = function()
   -- src/Timer.lua
   
   -- if execution is slow, perform a maximum of 4 simultaneous updates in order to keep the fixrate
   local MAX_SKIP_FRAMES = 4
   
   local function loop(Time)
   
      local accumulator = 0.0
      local lastStepTime = 0.0
   
      return function (newTime, stepName, beforeUpdateFn, updateFn)
         local dtFixed = Time.DeltaFixed
         local stepTime = newTime - lastStepTime
         if stepTime > 0.25 then
            stepTime = 0.25
         end
         lastStepTime = newTime
   
         Time.Now = newTime
   
         -- 1000/30/1000 = 0.03333333333333333
         accumulator = accumulator + stepTime
         
         --[[
            Adjusting the framerate, the world must run on the same frequency,
            this ensures determinism in the execution of the scripts
   
            Each system in "transform" step is executed at a predetermined frequency (in Hz).
   
            Ex. If the game is running on the client at 30FPS but a system needs to be run at
            120Hz or 240Hz, this logic will ensure that this frequency is reached
   
            @see https://gafferongames.com/post/fix_your_timestep/
            @see https://gameprogrammingpatterns.com/game-loop.html
            @see https://bell0bytes.eu/the-game-loop/
         ]]
         if stepName == "process" then
            if accumulator >= dtFixed then       
               Time.Interpolation = 1
   
               beforeUpdateFn(Time)
               local nLoops = 0
               while (accumulator >= dtFixed and nLoops < MAX_SKIP_FRAMES) do
                  updateFn(Time)
                  nLoops = nLoops + 1
                  Time.Process = Time.Process + dtFixed
                  accumulator = accumulator - dtFixed
               end
            end
         else
            Time.Interpolation = math.min(math.max(accumulator/dtFixed, 0), 1)
            beforeUpdateFn(Time)
            updateFn(Time)
         end
      end
   end
   
   local Timer = {}
   Timer.__index = Timer
   
   function Timer.New(frequency)
      local Time = {
         Now = 0,
         -- The time at the beginning of this frame. The world receives the current time at the beginning
         -- of each frame, with the value increasing per frame.
         Frame = 0,         
         Process = 0, -- The time the latest process step has started.
         Delta = 0, -- The completion time in seconds since the last frame.
         DeltaFixed = 0,
         -- INTERPOLATION: The proportion of time since the previous transform relative to processDeltaTime
         Interpolation = 0
      }
   
      local timer = setmetatable({
         -- Public, visible by systems
         Time = Time,
         Frequency = 0,
         _update = loop(Time)
      }, Timer)
   
      timer:SetFrequency(frequency)
   
      return timer
   end
   
   --[[
      Changes the frequency of execution of the "process" step
   
      @param frequency {number}
   ]]
   function Timer:SetFrequency(frequency)
   
      -- frequency: number,
      -- The maximum times per second this system should be updated. Defaults 30
      if frequency == nil then
         frequency = 30
      end
   
      local safeFrequency  = math.floor(math.abs(frequency)/2)*2
      if safeFrequency < 2 then
         safeFrequency = 2
      end
   
      if frequency ~= safeFrequency then
         frequency = safeFrequency
      end
   
      self.Frequency = frequency
      self.Time.DeltaFixed = 1000/frequency/1000
   end
   
   function Timer:Update(now, step, beforeUpdate, update)
      self._update(now, step, beforeUpdate, update)
   end
   
   return Timer
   
end

__F__["Utility"] = function()
   -- src/Utility.lua
   --[[
      Utility library.
   ]]
   local Utility = {}
   
   if table.unpack == nil then
   	table.unpack = unpack
   end
   
   if table.find == nil then
      --[[
         Within the given array-like table haystack, find the first occurrence of value needle, starting from index init 
         or the beginning if not provided. If the value is not found, nil is returned.
   
         A linear search algorithm is performed.
      ]]
      table.find = function(haystack, needle, init)
         local len = #haystack
         for i = init or 1, len, 1 do
            if haystack[i] == needle then
               return i
            end
         end
   
         return nil
      end
   end
   
   local function copyDeep(src)
   	local copy = {}
      for k, v in pairs(src) do
         if type(v) == "table" then
            v = copyDeep(v)
         end
         copy[k] = v
      end
   	
   	return copy
   end
   Utility.copyDeep = copyDeep
   
   --[[
      Faz o merge dos atributos src com o dest
      Quando o um atributo do segundo é um "table", faz uma copia do valor
   ]]
   local function mergeDeep(dest, src)
      for k,valueSrc in pairs(src) do
         if (type(valueSrc) == "table") then
            local valueDest = dest[k]
            if (valueDest == nil or type(valueDest) ~= "table") then
               dest[k] = copyDeep(valueSrc)
            else
               dest[k] = mergeDeep(valueDest, valueSrc)
            end
         else
            dest[k] = valueSrc
         end
      end
   	return dest
   end
   Utility.mergeDeep = mergeDeep
   
   return Utility
   
end

__F__["World"] = function()
   -- src/World.lua
   local Timer = __REQUIRE__("Timer")
   local Event = __REQUIRE__("Event")
   local Entity = __REQUIRE__("Entity")
   local Archetype = __REQUIRE__("Archetype")
   local SystemExecutor = __REQUIRE__("SystemExecutor")
   local EntityRepository = __REQUIRE__("EntityRepository")
   
   local World = {}
   World.__index = World
   
   --[[  
      Create a new world instance
   
      @param systemClasses {SystemClass[]} (Optional) Array of system classes
      @param frequency {number} (Optional) define the frequency that the `process` step will be executed. Default 30
      @param disableAutoUpdate {bool} (Optional) when `~= false`, the world automatically registers in the `LoopManager`, 
      receiving the `World:Update()` method from it. Default false
   ]]
   function World.New(systemClasses, frequency, disableAutoUpdate)   
      local world = setmetatable({
         --[[
            Global System Version (GSV).
   
            Before executing the Update method of each system, the world version is incremented, so at this point, the 
            world version will always be higher than the running system version.
   
            Whenever an entity archetype is changed (received or lost component) the entity's version is updated to the 
            current version of the world.
   
            After executing the System Update method, the version of this system is updated to the current world version.
   
            This mechanism allows a system to know if an entity has been modified after the last execution of this same 
            system, as the entity's version is superior to the version of the last system execution. Thus, a system can 
            contain logic if it only operates on "dirty" entities, which have undergone changes. The code for this 
            validation on a system is `local isDirty = entity.version > self.version`
         ]]
         version = 0,
         --[[
            Allows you to define the maximum time that the JobSystem can operate in each frame.
   
            The default value is 0.011666666666666665 = ((1000/60/1000)*0.7)
   
            A game that runs at 30fps has 0.0333 seconds to do all the processing for each frame, including rendering
               - 30FPS = ((1000/30/1000)*0.7)/3 = 0.007777777777777777
   
            A game that runs at 60fps has 0.0166 seconds to do all the processing for each frame, including rendering
               - 60FPS = ((1000/60/1000)*0.7)/3 = 0.0038888888888888883
         ]]
         maxTasksExecTime = 0.013333333333333334,
         _dirty = false, -- True when create/remove entity, add/remove entity component (change archetype)
         _timer = Timer.New(frequency),
         _systems = {}, -- systems in this world
         _repository = EntityRepository.New(),
         _entitiesCreated = {}, -- created during the execution of the Update
         _entitiesRemoved = {}, -- removed during execution (only removed after the last execution step)
         _entitiesUpdated = {}, -- changed during execution (received or lost components, therefore, changed the archetype)
         _onQueryMatch = Event.New(),
         _onChangeArchetypeEvent = Event.New(),
      }, World)
   
      -- System execution plan
      world._executor = SystemExecutor.New(world)
   
      world._onChangeArchetypeEvent:Connect(function(entity, archetypeOld, archetypeNew)      
         world:_OnChangeArchetype(entity, archetypeOld, archetypeNew)
      end)
   
      -- add systems
      if (systemClasses ~= nil) then
         for _, systemClass in ipairs(systemClasses) do
            world:AddSystem(systemClass)
         end
      end
   
      if (not disableAutoUpdate and World.LoopManager) then
         world._loopCancel = World.LoopManager.Register(world)
      end
   
      return world
   end
   
   --[[
      Changes the frequency of execution of the "process" step
   
      @param frequency {number}
   ]]
   function World:SetFrequency(frequency) 
      frequency = self._timer:SetFrequency(frequency) 
   end
   
   --[[
      Get the frequency of execution of the "process" step
   
      @return number
   ]]
   function World:GetFrequency(frequency) 
      return self._timer.Frequency
   end
   
   --[[
      Add a new system to the world.
   
      Only one instance per type is accepted. If there is already another instance of this system in the world, any new 
      invocation of this method will be ignored.
   
      @param systemClass {SystemClass} The system to be added in the world
      @param config {table} (Optional) System instance configuration
   ]]
   function World:AddSystem(systemClass, config)
      if systemClass then
         if config == nil then
            config = {}
         end
        
         if self._systems[systemClass] == nil then
            self._systems[systemClass] = systemClass.New(self, config)
   
            self._executor:SetSystems(self._systems)
         end
      end
   end
   
   --[[
      Create a new entity.
   
      The entity is created in DEAD state (entity.isAlive == false) and will only be visible for queries after the 
      cleaning step (OnRemove, OnEnter, OnExit) of the current step
   
      @param args {Component[]}
      @return Entity
   ]]
   function World:Entity(...)
      local entity = Entity.New(self._onChangeArchetypeEvent, {...})
   
      self._dirty = true
      self._entitiesCreated[entity] = true
      
      entity.version = self.version -- update entity version using current Global System Version (GSV)
      entity.isAlive = false
   
      return entity
   end
   
   --[[
      Performs immediate removal of an entity.
   
      If the entity was created in this step and the cleanup process has not happened yet (therefore the entity is 
      inactive, entity.isAlive == false), the `OnRemove` event will never be fired.
   
      If the entity is alive (entity.isAlive == true), even though it is removed immediately, the `OnRemove` event will be 
      fired at the end of the current step.
   
      @param entity {Entity}
   ]]
   function World:Remove(entity)
   
      if self._entitiesRemoved[entity] == true then
         return
      end
   
      if self._entitiesCreated[entity] == true then
         self._entitiesCreated[entity] = nil
      else
         self._repository:Remove(entity)
         self._entitiesRemoved[entity] = true
   
         if self._entitiesUpdated[entity] == nil then
            self._entitiesUpdated[entity] = entity.archetype
         end
      end
   
      self._dirty = true
      entity.isAlive = false
   end
   
   --[[
      Run a query in this world
   
      @param query {Query|QueryBuilder}
      @return QueryResult
   ]]
   function World:Exec(query)
      if (query.isQueryBuilder) then
         query = query.Build()
      end
   
      local result, match = self._repository:Query(query)
   
      if match then
         self._onQueryMatch:Fire(query)
      end
   
      return result
   end
   
   --[[
      Quick check to find out if a query is applicable.
   
      @param query {Query|QueryBuilder}
      @return QueryResult
   ]]
   function World:FastCheck(query)
      if (query.isQueryBuilder) then
         query = query.Build()
      end
   
      return self._repository:FastCheck(query)
   end
   
   --[[
      Add a callback that is reported whenever a query has been successfully executed. Used internally 
      to quickly find out if a QuerySystem will run.
   ]]
   function World:OnQueryMatch(callback)
      return self._onQueryMatch:Connect(callback)
   end
   
   --[[
      Perform world update.
   
      When registered, LoopManager will invoke World Update for each step in the sequence.
   
      - process At the beginning of each frame
      - transform After the game engine's physics engine runs
      - render Before rendering the current frame
   
      @param step {"process"|"transform"|"render"}
      @param now {number} Usually os.clock()
   ]]
   function World:Update(step, now)
   
      
      self._timer:Update(
         now, step,
         function(Time)
            --[[
               JobSystem
               .------------------.
               |     pipeline     |
               |------------------| 
               | s:ShouldUpdate() |
               | s:Update()       |
               '------------------'
            ]]
            if step == "process" then
               self._executor:ScheduleTasks(Time)
            end
            -- run suspended Tasks
            self._executor:ExecTasks(self.maxTasksExecTime)
         end,
         function(Time)
            --[[
               .------------------.
               |     pipeline     |
               |------------------| 
               | s:ShouldUpdate() |
               | s:Update()       |
               |                  |
               |-- CLEAR ---------|
               | s:OnRemove()     |
               | s:OnExit()       |
               | s:OnEnter()      |
               '------------------'
            ]]
            if step == "process" then
               self._executor:ExecProcess(Time)
            elseif step == "transform" then
               self._executor:ExecTransform(Time)
            else
               self._executor:ExecRender(Time)
            end
   
            -- cleans up after running scripts
            while self._dirty do
               self._dirty = false
            
               -- 1: remove entities
               local entitiesRemoved = {}
               for entity,_ in pairs(self._entitiesRemoved) do
                  entitiesRemoved[entity] = self._entitiesUpdated[entity]
                  self._entitiesUpdated[entity] = nil
               end
               self._entitiesRemoved = {}
               self._executor:ExecOnRemove(Time, entitiesRemoved)
               entitiesRemoved = nil
            
               local changed = {}
               local hasChange = false
            
               -- 2: Update entities in memory
               for entity, archetypeOld in pairs(self._entitiesUpdated) do
                  if (archetypeOld ~= entity.archetype) then
                     hasChange = true
                     changed[entity] = archetypeOld
                  end
               end
               self._entitiesUpdated = {}
            
               -- 3: Add new entities
               for entity, _ in pairs(self._entitiesCreated) do
                  hasChange = true
                  changed[entity] = Archetype.EMPTY
            
                  entity.isAlive = true
                  self._repository:Insert(entity) 
               end
               self._entitiesCreated = {}
            
               if hasChange then
                  self._executor:ExecOnExitEnter(Time, changed)
                  changed = nil
               end
            end
         end
      )
   end
   
   --[[
      Destroy this instance, removing all entities, systems and events
   ]]
   function World:Destroy()
   
      if self._loopCancel then
         self._loopCancel()
         self._loopCancel = nil
      end
   
      if self._onChangeArchetypeEvent then
         self._onChangeArchetypeEvent:Destroy()
         self._onChangeArchetypeEvent = nil
      end
   
      self._repository = nil
   
      if self._systems then
         for _,system in pairs(self._systems) do
            system:Destroy()
         end
         self._systems = nil
      end
      
      self._timer = nil
      self._ExecPlan = nil
      self._entitiesCreated = nil
      self._entitiesUpdated = nil
      self._entitiesRemoved = nil
   
      setmetatable(self, nil)
   end
   
   function World:_OnChangeArchetype(entity, archetypeOld, archetypeNew)
      if entity.isAlive then
   
         if self._entitiesUpdated[entity] == nil then
            self._dirty = true
            self._entitiesUpdated[entity] = archetypeOld
         end
      
         self._repository:Update(entity)
   
         -- update entity version using current Global System Version (GSV)
         entity.version = self.version
      end
   end
   
   return World
   
end

return __REQUIRE__("ECS")

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2020 Alex Rodin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
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
SOFTWARE.


================================================
FILE: README.md
================================================
<p align="center">
   <a href="https://nidorx.github.io/ecs-lua">
      <img 
         src="docs/assets/logo.svg" 
         alt="https://nidorx.github.io/ecs-lua" 
      />
   </a>
</p>

<p align="center">
   <a href="https://app.travis-ci.com/nidorx/ecs-lua">
      <img src="https://app.travis-ci.com/nidorx/ecs-lua.svg?branch=master" alt="Build Status" />
   </a>
</p>

<p align="center">
  <strong><a href="https://nidorx.github.io/ecs-lua#/">Read the Documentation</a></strong>
</p>

# What is it?

<strong>ECS Lua</strong> is a fast and easy to use ECS (Entity Component System) engine for game development.

<div align="center">

![](docs/assets/diagram-1.png)

</div>

The basic idea of this pattern is to stop defining entities using a 
[hierarchy](https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)) of classes and start doing use of 
[composition](https://en.wikipedia.org/wiki/Object_composition) in a Data Oriented Programming paradigm.
([More information on Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system)).
Programming with an ECS can result in code that is more efficient and easier to extend over time.


# How does it work?

<div align="center">

![ECS Lua pipeline](docs/assets/pipeline.png)

</div>


# Talk is cheap. Show me the code!

```lua
local World, System, Query, Component = ECS.World, ECS.System, ECS.Query, ECS.Component

local Health = Component(100)
local Position = Component({ x = 0, y = 0})

local isInAcid = Query.Filter(function()
   return true  -- it's wet season
end)

local InAcidSystem = System("process", Query.All( Health, Position, isInAcid() ))

function InAcidSystem:Update()
   for i, entity in self:Result():Iterator() do
      local health = entity[Health]
      health.value = health.value - 0.01
   end
end

local world = World({ InAcidSystem })

world:Entity(Position({ x = 5.0 }), Health())
```

# Features

**ECS Lua** has no external dependencies and is compatible and tested with [Lua 5.1], [Lua 5.2], [Lua 5.3], [Lua 5.4],
[LuaJit] and [Roblox Luau](https://luau-lang.org/)

- **Game engine agnostic**: It can be used in any engine that has the Lua scripting language.
- **Ergonomic**: Focused on providing a simple yet efficient API
- **FSM**: Finite State Machines in an easy and intuitive way
- **JobSystem**: To running systems in parallel (through [coroutines])
- **Reactive**: Systems can be informed when an entity changes
- **Predictable**:
   - The systems will work in the order they were registered or based on the priority set when registering them.
   - Reactive events do not generate a random callback when issued, they are executed at a predefined step.

# Goal

To be a lightweight, simple, ergonomic and high-performance ECS library that can be easily extended. The **ECS Lua**
does not strictly follow _"pure ECS design"_.

# Usage

Read our [Full Documentation][docs] to learn how to use **ECS Lua**.

# Get involved
All kinds of contributions are welcome!

🐛 **Found a bug?**  
Let me know by [creating an issue][new-issue].

❓ **Have a question?**  
[Roblox DevForum][discussions] is a good place to start.

⚙️ **Interested in fixing a [bug][bugs] or adding a [feature][features]?**  
Check out the [contributing guidelines](CONTRIBUTING.md).

📖 **Can we improve [our documentation][docs]?**  
Pull requests even for small changes can be helpful. Each page in the docs can be edited by clicking the 
"Edit on GitHub" link at the bottom right.

[docs]: https://nidorx.github.io/ecs-lua
[bugs]: https://github.com/nidorx/ecs-lua/issues?q=is%3Aissue+is%3Aopen+label%3Abug
[features]: https://github.com/nidorx/ecs-lua/issues?q=is%3Aissue+is%3Aopen+label%3Afeature
[new-issue]: https://github.com/nidorx/ecs-lua/issues/new/choose
[discussions]: https://devforum.roblox.com/t/841175
[Lua 5.1]:https://app.travis-ci.com/github/nidorx/ecs-lua
[Lua 5.2]:https://app.travis-ci.com/github/nidorx/ecs-lua
[Lua 5.3]:https://app.travis-ci.com/github/nidorx/ecs-lua
[Lua 5.4]:https://app.travis-ci.com/github/nidorx/ecs-lua
[LuaJit]:https://app.travis-ci.com/github/nidorx/ecs-lua
[coroutines]:http://www.lua.org/pil/9.1.html

# License

This code is distributed under the terms and conditions of the [MIT license](LICENSE).





================================================
FILE: build.lua
================================================


local OUTPUT_CONCAT = "ECS_concat"

local OUTPUT_MINIFIED = "ECS"

local SRC_FILES = {
   "Archetype",
   "Component",
   "ComponentFSM",
   "ECS",
   "Entity",
   "EntityRepository",
   "Event",
   "Query",
   "QueryResult",
   "RobloxLoopManager",
   "System",
   "SystemExecutor",
   "Timer",
   "Utility",
   "World"
}

local HEADER = [[
	ECS Lua v2.2.0

	ECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.

	This is a minified version of ECS Lua, to see the full source code visit
	https://github.com/nidorx/ecs-lua

   Discussions about this script are at https://devforum.roblox.com/t/841175

	------------------------------------------------------------------------------

	MIT License

	Copyright (c) 2021 Alex Rodin

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	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
	SOFTWARE.]]

HEADER = "--[[\n"..HEADER.."\n]]\n"

package.path = package.path .. ";modules/?.lua"

local function concat()
   local concatContent = {
      HEADER,
      "local __M__, __F__ = {}, {}",
      "local function __REQUIRE__(m)",
      "   if (not __M__[m]) then",
      "      __M__[m] = { r = __F__[m]() }",
      "   end",
      "   return __M__[m].r",
      "end",   
      "",   
   }
   
   for i,name in ipairs(SRC_FILES) do
      local sourceFile = io.open("./src/"..name..".lua", "r")
      if not sourceFile then
         error("Could not open the input file `" .. OUTPUT_MINIFIED .. "`", 0)
      end
   
      local content = sourceFile:read( "*a" )
   
      for _,oname in ipairs(SRC_FILES) do
         content = content:gsub('require[(]["\']'..oname..'["\'][)]', '__REQUIRE__("'..oname..'")')
      end
   
      table.insert(concatContent, table.concat({
         '__F__["'..name..'"] = function()',
         ("   -- src/"..name..".lua\n"..content):gsub("\n", "\n   "),
         "end",
         "",
      }, "\n"))
      sourceFile:close()
   end
   
   table.insert(concatContent, 'return __REQUIRE__("ECS")')
   
   -- write ECS_concat.lua
   local fileConcat = io.open(OUTPUT_CONCAT..".lua", "w" )
   fileConcat:write( table.concat(concatContent, "\n"))
   fileConcat:close()  
   
   -- teste import
   local ecsConcat = require(OUTPUT_CONCAT)
   _G.ECS = nil
end

local function minify()
   local min = require('minify')

   local sourceFile = io.open(OUTPUT_CONCAT..".lua", 'r')
   if not sourceFile then
      error("Could not open the input file `" .. OUTPUT_CONCAT..".lua" .. "`", 0)
   end

   local data = sourceFile:read('*all')
   local ast = min.CreateLuaParser(data)
   local global_scope, root_scope = min.AddVariableInfo(ast)

   min.MinifyVariables(global_scope, root_scope)
   min.StripAst(ast)
   local minifiedContent = min.AstToString(ast)

   -- write ECS.lua
   local fileMinified = io.open(OUTPUT_MINIFIED..".lua", "w" )
   fileMinified:write(HEADER .. minifiedContent)
   fileMinified:close()

   -- teste import
   local ecsMinified = require(OUTPUT_MINIFIED)
   _G.ECS = nil
end

concat()
minify()


================================================
FILE: docs/.nojekyll
================================================


================================================
FILE: docs/README.md
================================================
# What is it?

**ECS Lua** is a fast and easy to use ECS (Entity Component System) engine for game development.

![](assets/diagram-1.png)

The basic idea of this pattern is to stop defining entities using a 
[hierarchy](https://en.wikipedia.org/wiki/Inheritance_(object-oriented_programming)) of classes and start doing use of 
[composition](https://en.wikipedia.org/wiki/Object_composition) in a Data Oriented Programming paradigm.
([More information on Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system)).
Programming with an ECS can result in code that is more efficient and easier to extend over time.


# How does it work?

![ECS Lua pipeline](assets/pipeline.png)


# Talk is cheap. Show me the code!

```lua
local World, System, Query, Component = ECS.World, ECS.System, ECS.Query, ECS.Component

local Health = Component(100)
local Position = Component({ x = 0, y = 0})

local isInAcid = Query.Filter(function()
   return true  -- it's wet season
end)

local InAcidSystem = System("process", Query.All( Health, Position, isInAcid() ))

function InAcidSystem:Update()
   for i, entity in self:Result():Iterator() do
      local health = entity[Health]
      health.value = health.value - 0.01
   end
end

local world = World({ InAcidSystem })

world.Entity(Position({ x: 5.0 }), Health())
```

# Features

**ECS Lua** has no external dependencies and is compatible and tested with [Lua 5.1], [Lua 5.2], [Lua 5.3], [Lua 5.4],
[LuaJit] and [Roblox Luau](https://luau-lang.org/)

- **Game engine agnostic**: It can be used in any engine that has the Lua scripting language.
- **Ergonomic**: Focused on providing a simple yet efficient API
- **FSM**: Finite State Machines in an easy and intuitive way
- **JobSystem**: To running systems in parallel (through [coroutines])
- **Reactive**: Systems can be informed when an entity changes
- **Predictable**:
   - The systems will work in the order they were registered or based on the priority set when registering them.
   - Reactive events do not generate a random callback when issued, they are executed at a predefined step.

# Goal

To be a lightweight, simple, ergonomic and high-performance ECS library that can be easily extended. The **ECS Lua**
does not strictly follow _"pure ECS design"_.

# Next steps

You can browse or search for specific subjects in the side menu. Here are some relevant links:

<br>
<br>

<div class="home-row clearfix" style="text-align:center">
   <div class="home-col"><div class="panel home-panel"><div class="panel-body">

   [![Installation](assets/icon-download.png ":no-zoom")](/getting-started?id=installation)

   </div><div class="panel-heading">

   [Installation](/getting-started?id=installation)

   </div></div></div>

   <div class="home-col"><div class="panel home-panel"><div class="panel-body">

   [![General Concepts](assets/icon-parts.png ":no-zoom")](/getting-started?id=general-concepts)

   </div><div class="panel-heading">

   [General Concepts](/getting-started?id=general-concepts)

   </div></div></div>

   <div class="home-col"><div class="panel home-panel"><div class="panel-body">

   [![Architecture](assets/icon-advanced.png ":no-zoom")](/architecture)

   </div><div class="panel-heading">

   [Architecture](/architecture)

   </div></div></div>

   <div class="home-col"><div class="panel home-panel"><div class="panel-body">

   [![Tutorials](assets/icon-tutorial.png ":no-zoom")](/tutorial)

   </div><div class="panel-heading">

   [Tutorials](/tutorial)

   </div></div></div>
</div>

[Lua 5.1]:https://app.travis-ci.com/github/nidorx/ecs-lua
[Lua 5.2]:https://app.travis-ci.com/github/nidorx/ecs-lua
[Lua 5.3]:https://app.travis-ci.com/github/nidorx/ecs-lua
[Lua 5.4]:https://app.travis-ci.com/github/nidorx/ecs-lua
[LuaJit]:https://app.travis-ci.com/github/nidorx/ecs-lua


================================================
FILE: docs/_coverpage.md
================================================
<div class="logo-container">
  <div class="heats">
    <div class="h r1 c1"></div>
    <div class="h r1 c2"></div>
    <div class="h r1 c3"></div>
    <div class="h r1 c4"></div>
    <div class="h r1 c5"></div>
    <div class="h r2 c1"></div>
    <div class="h r2 c2"></div>
    <div class="h r2 c3"></div>
    <div class="h r2 c4"></div>
    <div class="h r2 c5"></div>
    <div class="h r3 c1"></div>
    <div class="h r3 c2"></div>
    <div class="h r3 c3"></div>
    <div class="h r3 c4"></div>
    <div class="h r3 c5"></div>
    <div class="h r4 c1"></div>
    <div class="h r4 c2"></div>
    <div class="h r4 c3"></div>
    <div class="h r4 c4"></div>
    <div class="h r4 c5"></div>
    <div class="h r5 c1"></div>
    <div class="h r5 c2"></div>
    <div class="h r5 c3"></div>
    <div class="h r5 c4"></div>
    <div class="h r5 c5"></div>
    
   <svg 
      class="ghost"
      width="50mm" 
      height="50mm" 
      version="1.1" 
      viewBox="0 0 50 50">
      <path d="m4.8916 45.894-2e-6 -2.9104h-2.9104l-1e-6 -20.902h2.9104v-8.9958h3.175v-3.1992h3.4396v-2.8862l6.8792-9.1e-6v-2.9104h13.229v2.9104h6.4824v2.8862h3.7041v3.1992h-6.7469v3.199l-3.4396 1e-3 -2e-6 8.4414h3.4396l2e-6 2.9104 6.7469-1.7e-4v-5.5559l6.2178 2.3e-4v20.902h-3.175v2.9265l-6.7469-0.016v-2.9104h-3.0428v-3.1749h-3.4396v3.1749l-3.4396 0.0256v2.8848h-6.6146v-2.9104h-3.175v-3.1749h-3.175v3.1749h-3.7042c0.0062 0.97335 0 1.9371 0 2.9104-2.2049-2e-4 -4.4097 0-6.6146 0zm16.669-18.256-2e-6 -2.9104h3.4397v-8.4426h-3.4397v-3.199h-6.35v3.199h-3.7042l2e-6 8.4425 3.7042 1.5e-4v2.9103z" fill="#fb860c" class="body"></path>
      <g class="eye eye--left">
         <g class="pupil">
            <g class="inner">
               <path class="pupil-color" d="m18.385 24.728v-5.5563h6.6147v5.5563z" fill="#020202"></path>
               <path class="eyelid" d="m18.385 24.728v-5.5563h6.6147v5.5563z" fill="#edf4f7"></path>
            </g>
         </g>
      </g>
      <g class="eye eye--right">
         <g class="pupil">
            <g class="inner">
               <path class="pupil-color" d="m38.494 24.728v-5.5563h6.6147v5.5563z" fill="#020202"></path>
               <path class="eyelid"  d="m38.494 24.728v-5.5563h6.6147v5.5563z" fill="#edf4f7"></path>
            </g>
         </g>
      </g>
   </svg>
  </div>
</div>

# ECS Lua
[![Build Status](https://app.travis-ci.com/nidorx/ecs-lua.svg?branch=master)](https://app.travis-ci.com/nidorx/ecs-lua)

[GitHub](https://github.com/nidorx/ecs-lua)
[Get Started](/?id=what-is-it)

<!-- background color -->
![color](#ffffff)


================================================
FILE: docs/_navbar.md
================================================
- [Home](/) &nbsp; &nbsp;
- Language <span class="arrow">&#x25BE;</span>
  - [English](/)
  - [Português do Brasil](/pt-br/)


================================================
FILE: docs/_sidebar.md
================================================
- [Home](/)
- [Installation](/getting-started?id=installation)
- [General Concepts](/getting-started?id=general-concepts)
   - [Component](/getting-started?id=component)
   - [Systems and Queries](/getting-started?id=systems-and-queries)
   - [World](/getting-started?id=world)
   - [Entity](/getting-started?id=entity)
- [Example](/getting-started?id=putting-everything-together)
- [Architecture](/architecture)
  - [Component](/architecture?id=component)
    - [Qualifiers](/architecture?id=qualifiers)
    - [FSM - Finite State Machines](/architecture?id=fsm-finite-state-machines)
  - [Entity](/architecture?id=entity)
  - [Query](/architecture?id=query)  
  - [Sistema](/architecture?id=system)
    - [Tasks](/architecture?id=task)
  - [World](/architecture?id=world)
- [Tutorials](/tutorial)
  - [Shoot](/tutorial-shoot)
  - [Pacman](/tutorial-pacman)
  - [Boids](/tutorial-boids)
- [API](/api)
  - [ECS](/api?id=ecs)
  - [Archetype](/api?id=archetype)
  - [Component](/api?id=component)
  - [Entity](/api?id=entity)
  - [LoopManager](/api?id=loopmanager)
  - [Query](/api?id=query)
  - [QueryBuilder](/api?id=querybuilder)
  - [QueryResult](/api?id=queryresult)
  - [System](/api?id=system)
  - [Time](/api?id=time)
  - [World](/api?id=world)



================================================
FILE: docs/api.md
================================================
# API
<div class="api-docs">


# ECS
- `ECS.Query`
   - _@type_ `QueryClass`
- `ECS.Archetype`
   - _@type_ `ArchetypeClass`
- `ECS.World(systemClasses, frequency, disableAutoUpdate)`
   - Create a new world instance
   - _@param_ `systemClasses` `SystemClass[]` _optional_ Array of system classes
   - _@param_ `frequency` `number` _optional_ Define the frequency that the `process` step will be executed. Default 30
   - _@param_ `disableAutoUpdate` `bool` _optional_ When `~= false`, the world automatically registers in the `LoopManager`, 
   receiving the `World:Update()` method from it. Default false
   - _@return_ `World`
- `ECS.System(step, order, query, updateFn)`
   - Create new System Class
   - _@param_ `step` `process|transform|render|task`
   - _@param_ `order` `number` _optional_ Allows you to set an execution order (for systems that are not `task`). Default 50
   - _@param_ `query` `Query|QueryBuilder` _optional_ Filters the entities that will be processed by this system
   - _@param_ `updateFn` `Function(self, Time)` _optional_ A shortcut for creating systems that only have the Update method
   - _@return_ `SystemClass`
- `ECS.Component(template)`
   - Register a new ComponentClass
   - _@param_ `template` `table|function(table):table|any` 
      - When `table`, this template will be used for creating component instances
      - When it's a `function`, it will be invoked when a new component is instantiated. The creation parameter of the 
      component is passed to template function
      - If the template type is different from `table` and `function`, **ECS Lua** will generate a template in the format 
      `{ value = template }`.
   - _@return_ `ComponentClass`
- `ECS.SetLoopManager(manager)`
   - Defines the LoopManager that will be used by the worlds to receive the automatic update
   - _@param_ `manager` `LoopManager`

# Archetype

An Archetype is a unique combination of component types. The EntityRepository uses the archetype to group all 
entities that have the same sets of components.

An entity can change archetype fluidly over its lifespan. For example, when you add or remove components, the archetype 
of the affected entity changes.

An archetype object is not a container; rather it is an identifier to each unique combination of component types that 
an application has created at run time, either directly or implicitly.

You can create archetypes directly using `ECS.Archetype.Of(Components[])`. You also implicitly create archetypes 
whenever you add or remove a component from an entity. An Archetype object is an immutable singleton; creating an 
archetype with the same set of components, either directly or implicitly, results in the same archetype.

The ECS framework uses archetypes to group entities that have the same structure together. The ECS framework stores 
component data in blocks of memory called chunks. A given chunk stores only entities having the same archetype. You can 
get the Archetype object for a chunk from its Archetype property.

Use `ECS.Archetype.Of(Components[])` to get a Archetype reference.

- `Archetype.EMPTY`
   - Generic archetype, for entities that do not have components
   - _@type_ `Archetype`
- `Archetype.Of(componentClasses)`
   - Gets the reference to an archetype from the informed components
   - _@param_ `componentClasses` `ComponentClass[]` Component that define this archetype
   - _@return_ `Archetype`
- `Archetype.Version()`
   - Get the version of archetype definitions
   - _@return_ `number`
- `Archetype:Has(componentClass)`
   - Checks whether this archetype has the informed component
   - _@param_ `componentClass` `ComponentClass`
   - _@return_ `bool`
- `Archetype:With(componentClass)`
   - Gets the reference to an archetype that has the current components `+` the informed component
   - _@param_ `componentClass` `ComponentClass`
   - _@return_ `Archetype`
- `Archetype:WithAll(componentClasses)`
   - Gets the reference to an archetype that has the current components `+` the informed components
   - _@param_ `componentClass` `ComponentClass[]`
   - _@return_ `Archetype`
- `Archetype:Without(componentClass)`
   - Gets the reference to an archetype that has the current components `-` the informed component
   - _@param_ `componentClass` `ComponentClass`
   - _@return_ `Archetype`
- `Archetype:WithoutAll(componentClasses)`
   - Gets the reference to an archetype that has the current components `-` the informed components
   - _@param_ `componentClass` `ComponentClass[]`
   - _@return_ `Archetype`

# Component
- `ComponentClass.Id`
   - Identifier of this component
   - _@type_ `number`
- `ComponentClass.IsCType`
   - Indicates that this class is a Component
   - _@type_ `true`
- `ComponentClass.SuperClass`
   - Used internally for Qualifiers, it indicates the base class of this Component (or primary component).
   - _@type_ `ComponentClass`
- `ComponentClass.HasQualifier`
   - Indicates that this Component has qualifiers
   - _@type_ `bool`
- `ComponentClass.IsQualifier`
   - Indicates that this specific class is a Component qualifier.
   - _@type_ `bool`
- `ComponentClass.IsFSM`
   - Indicates that this Component is a [FSM - Finite State Machine][fsm]
   - _@type_ `bool`
- `ComponentClass.States`
   - When set, this Component becomes a [FSM - Finite State Machine][fsm]
      ```lua
      local Movement = ECS.Component({ speed = 0 })
      Movement.States = {
         Standing = {"Walking"},
         Walking  = "*",
         Running  = {"Walking"}
      }
      ```
   - _@type_ `table` _optional_
- `ComponentClass.StateInitial`
   - When the component is [FSM][fsm], it allows defining the initial state for new instances.
   - _@type_ `string` _optional_
- `ComponentClass.Case`
   - When the component is [FSM][fsm], it allows the component to handle transactions between states.
      ```lua
      Movement.Case = {
         Standing = function(self, previous)
            self.speed = 0
         end,
         Walking = function(self, previous)
            self.speed = 5
         end,
         Running = function(self, previous)
            self.speed = 10
         end
      }
      ```
   - _@type_ `table` _optional_
- `ComponentClass.Qualifier(qualifier)`
   - Gets a qualifier for this type of component. If the qualifier does not exist, a new class will be created, 
   otherwise it brings the already registered class qualifier reference with the same name.
   - _@param_ `qualifier` `string|ComponentClass`
   - _@return_ `ComponentClass`
- `ComponentClass.Qualifiers(...)`
   - Get all qualified class
   - _@param_ `...` `string|ComponentClass` _optional_ Allows to filter the specific qualifiers
   - _@return_ `ComponentClass[]`
- `ComponentClass(value)` | `ComponentClass.New(value)`
   - Builder, instantiate a new component of this type
   - _@param_ `value` `any` _optional_  If the value is not a table, it will be converted to the format `{ value = value}`
   - _@return_ `Component`
- `ComponentClass:GetType()`
   - Get this component's class
   - _@return_ `ComponentClass`
- `ComponentClass:Is(componentClass)`
   - Check if this component is of the type informed
   - _@param_ `componentClass` `ComponentClass|ComponentSuperClass`
   - _@return_ `bool`
- `ComponentClass:Primary()`
   - Get the instance for the primary qualifier of this class
   - _@return_ `Component|nil`
- `ComponentClass:Qualified(qualifier)`
   - Get the instance for the given qualifier of this class
   - _@param_ `qualifier` `string|ComponentClass`
   - _@return_ `Component|nil`
- `ComponentClass:QualifiedAll()`
   - Get all instances for all qualifiers of that class
   - _@return_ `Component[]`
- `ComponentClass:Merge(other)`
   - Merges data from the other component into the current component. **IMPORTANT!** This method should not be invoked, 
   it is used by the entity to ensure correct retrieval of a component's qualifiers.
   - _@param_ `other` `Component`
- `ComponentClass:Detach()`
   - Unlink this component with the other qualifiers. **IMPORTANT!** This method should not be invoked, it is used by 
   the entity to ensure correct retrieval of a component's qualifiers.
- `ComponentClass.In(...)`
   - When the component is [FSM][fsm], creates a clause used to filter repository entities in a Query or QueryResult. 
      ```lua
      ECS.Query.All(Movement.In("Walking", "Running"))
      ```
   - _@param_ `...` `string[]`
   - _@return_ `Clause`
- `ComponentClass:SetState(newState)`
   - When the component is [FSM][fsm], defines the current state
   - _@param_ `newState` `string`
- `ComponentClass:GetState()`
   - When the component is [FSM][fsm], get the current state
   - _@return_ `string`
- `ComponentClass:GetPrevState()`
   - When the component is [FSM][fsm], get the previous state
   - _@return_ `string|nil`
- `ComponentClass:GetStateTime()`
   - When the component is [FSM][fsm], gets the time it changed to the current state. Whenever the state is changed, the 
   instant is persisted internally using `os.clock()`
   - _@return_ `number`

# Entity
- `Entity.id`
   - Identifier of this entity
   - _@type_ `number`
- `Entity.isAlive`
   - The entity is created in _DEAD_ state (`entity.isAlive == false`) and will only be visible for queries after the 
   cleaning step _(`OnRemove`,`OnEnter`,`OnExit`)_ by the world
   - _@type_ `bool`
- `Entity.archetype`
   - The entity archetype
   - _@type_ `Archetype`
- `Entity.New(onChange, components)`
   - Creates an entity having components of the specified types.
   - _@param_ `onChange` `Event`
   - _@param_ `components` `Component[]` _optional_
   - _@return_ `Entity`
- `Entity:Get(componentClass)` | `entity[componentClass]`
   - Gets an entity component
   - _@param_ `componentClass` `ComponentClass` 
   - _@return_ `Component`
- `Entity:Get(...)`
   - Get multiple entity components at once
      ```lua
      local comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)
      ```
   - _@param_ `..` `ComponentClass[]` 
   - _@return_ `Component ...`
- `Entity:Set(componentClass, value)` | `entity[componentClass] = value`
   - Sets the value of a component
   - _@param_ `componentClass` `ComponentClass` 
   - _@param_ `value` `any|nil` When nil, unset the component.
- `Entity:Set(...)`
   - Arrow one or more instances of a component
   - _@param_ `...` `Component`
- `Entity:Unset(componentClass|Component, ...)` | `entity[componentClass] = nil`
   - Remove one or more components from the entity
   - _@param_ `...` `componentClass|Component` 

# LoopManager
In order for the world's systems to receive an update, the `World:Update(step, now)` method must be invoked on each 
frame. To automate this process, **ECS Lua** provides a functionality so that, at the time of instantiation of a 
new world, it registers to receive the update automatically.

```lua
local MyLoopManager = {
   Register = function(world)
      local beforePhysics = MyGameEngine.BeforePhysics(function()
         world:Update("process", os.clock())
      end)

      local afterPhysics = MyGameEngine.AfterPhysics(function()
         world:Update("transform", os.clock())
      end)

      local beforeRender
      if (not MyGameEngine.IsServer()) then
         beforeRender = MyGameEngine.BeforeRender(function()
            world:Update("render", os.clock())
         end)
      end

      return function()
         beforePhysics:Disconnect()
         afterPhysics:Disconnect()
         if beforeRender then
            beforeRender:Disconnect()
         end
      end
   end
}

ECS.SetLoopManager(MyLoopManager)
```

- `LoopManager.Register(world)`
   - Allows the world to register to be updated.
   - _@param_ `world` `World` 
   - _@return_ `function` The world will invoke when destroyed

# Query
- `Query(all, any, none)` | `Query.New(all, any, none)`
   - Create a new Query used to filter entities in the world. It makes use of local and global cache in order to 
   decrease the validation time (avoids looping in runtime of systems)
   - _@param_ `all` `Array<ComponentClass|Clause>` _optional_
   - _@param_ `any` `Array<ComponentClass|Clause>` _optional_
   - _@param_ `none` `Array<ComponentClass|Clause>` _optional_
   - _@return_ `Query`
- `Query.All(...)`
   - _@param_ `...` `Array<ComponentClass|Clause>` 
   - _@return_ `QueryBuilder`
- `Query.Any(...)`
   - _@param_ `...` `Array<ComponentClass|Clause>` 
   - _@return_ `QueryBuilder`
- `Query.None(...)`
   - _@param_ `...` `Array<ComponentClass|Clause>` 
   - _@return_ `QueryBuilder`
- `Query.Filter(filter)`
   - Create custom filters that can be used in Queries. Its execution is delayed, invoked only in `QueryResult` methods.
   The result of executing the clause depends on how it was used in the query.
   Ex. If used in `Query.All()` the result is the inverse of using the same clause in `Query.None()`
      ```lua
      local Player = ECS.Component({ health = 100 })

      local HealthPlayerFilter = ECS.Query.Filter(function(entity, config)
         local player = entity[Player]
         return player.health >= config.minHealth and player.health <= config.maxHealth
      end)

      local healthyClause = HealthPlayerFilter({
         minHealth = 80,
         maxHealth = 100,
      })

      local healthyQuery = ECS.Query.All(Player, healthyClause)
      world:Exec(healthyQuery):ForEach(function(entity)
         -- this player is very healthy
      end)

      local notHealthyQuery = ECS.Query.All(Player).None(healthyClause)
      world:Exec(healthyQuery):ForEach(function(entity)
         -- this player is NOT very healthy
      end)

      local dyingClause = HealthPlayerClause({
         minHealth = 1,
         maxHealth = 20,
      })

      local dyingQuery = ECS.Query.All(Player, dyingClause)
      world:Exec(dyingQuery):ForEach(function(entity)
         -- this player is about to die
      end)

      local notDyingQuery = ECS.Query.All(Player).None(dyingClause)
      world:Exec(notDyingQuery):ForEach(function(entity)
         -- this player is NOT about to die
      end)
      ```
   - _@param_ `filter` `function(entity, config) -> bool`
   - _@return_ `function(config) -> Clause`
- `Query:Result(chunks)`
   - Generate a `QueryResult` with the chunks entered and the clauses of the current query
   - _@param_ `chunks` `Array<{ [Entity] = true }>` 
   - _@return_ `QueryResult`
- `Query:Match(archetype)`
   - Checks if the entered archetype is valid by the query definition
   - _@param_ `archetype` `Archetype` 
   - _@return_ `bool`

# QueryBuilder
- `QueryBuilder.isQueryBuilder`
   - Indicates that this is an instance of a QueryBuilder
   - _@type_ `true`
- `QueryBuilder.All(...)`
   - _@param_ `...` `Array<ComponentClass|Clause>` 
   - _@return_ `QueryBuilder`
- `QueryBuilder.Any(...)`
   - _@param_ `...` `Array<ComponentClass|Clause>` 
   - _@return_ `QueryBuilder`
- `QueryBuilder.None(...)`
   - _@param_ `...` `Array<ComponentClass|Clause>` 
   - _@return_ `QueryBuilder`
- `QueryBuilder.Build()`
   - _@return_ `Query`

# QueryResult
The result of a Query that was executed on an EntityStorage.

QueryResult provides several methods to facilitate the filtering of entities resulting from the execution of the query.

- **Intermediate Operations**
   -  Intermediate operations return a new QueryResult. They are always lazy; executing an intermediate operation such as 
   `QueryResult:Filter()` does not actually perform any filtering, but instead creates a new QueryResult that, when traversed, 
   contains the elements of the initial QueryResult that match the given predicate. Traversal of the pipeline source 
   does not begin until the terminal operation of the pipeline is executed.
- **Terminal Operations**
   - Terminal operations, such as `QueryResult:ForEach` or `QueryResult.AllMatch`, may traverse the QueryResult to produce a 
   result or a side-effect.

- `QueryResult.New(chunks, clauses)`
   - Build a new QueryResult
   - _@param_ `chunks` `Array<{ [Entity] = true }>`
   - _@param_ `clauses` `Clause[]` _optional_
   - _@return_ `QueryResult`
- `QueryResult:With(operation, param)`
   - Returns a QueryResult consisting of the elements of this QueryResult with a new pipeline operation
   - _@param_ `operation` `function(param, value, count) -> newValue, acceptItem, continuesLoop`
   - _@param_ `param` `any`
   - _@return_ `QueryResult` the new QueryResult
- `QueryResult:Filter(predicate)`
   - Returns a QueryResult consisting of the elements of this QueryResult that match the given predicate.
   - _@param_ `predicate` `function(value) -> bool` a predicate to apply to each element to determine if it should be included
   - _@return_ `QueryResult` the new QueryResult
- `QueryResult:Map(mapper)`
   - Returns a QueryResult consisting of the results of applying the given function to the elements of this QueryResult.
   - _@param_ `mapper` `function(value) -> newValue` a function to apply to each element
   - _@return_ `QueryResult` the new QueryResult
- `QueryResult:Limit(maxSize)`
   - Returns a QueryResult consisting of the elements of this QueryResult, truncated to be no longer than maxSize in length.
   - _@param_ `maxSize` `number`
   - _@return_ `QueryResult` the new QueryResult
- `QueryResult:AnyMatch(predicate)`
   - Returns whether any elements of this result match the provided predicate.
   - _@param_ `predicate` `function(value) -> bool` a predicate to apply to elements of this result
   - _@return_ `true` if any elements of the result match the provided predicate, otherwise `false`
- `QueryResult:AllMatch(predicate)`
   - Returns whether all elements of this result match the provided predicate.
   - _@param_ `predicate` `function(value) -> bool` a predicate to apply to elements of this result
   - _@return_ `true` if either all elements of the result match the provided predicate or the result is empty, otherwise `false`
- `QueryResult:FindAny()`
   - Returns some element of the result, or nil if the result is empty.

   This is a short-circuiting terminal operation.

   The behavior of this operation is explicitly nondeterministic; it is free to select any element in the result. 
   
   Multiple invocations on the same result may not return the same value.
   - _@return_ `any`
- `QueryResult:ForEach(action)`
   - Performs an action for each element of this QueryResult.

   This is a terminal operation.

   The behavior of this operation is explicitly nondeterministic. This operation does not guarantee to respect the 
   encounter order of the QueryResult.
   - _@param_ `action` `function(value, count) -> bool` A action to perform on the elements, breaks execution case returns true
- `QueryResult:ToArray()`
   - Returns an array containing the elements of this QueryResult.
   - _@return_ `Array<any>`
- `QueryResult:Iterator()`
   - Returns an Iterator, to use in for loop
      ```lua
      for count, entity in result:Iterator() do
         print(entity.id)
         break
      end
      ```  
   - _@return_ `Iterator`

# System

- `System.Step`
   - Step that this system will run
   - _@type_ `string` `process|transform|render|task`
- `System.Order`
   - For systems that are not `task`, execution order
   - _@type_ `number`
- `System.Query`
   - Filters the entities that will be processed by this system
   - _@type_ `number`
- `System.After`
   - When the system is a task, it allows you to define that this system should run AFTER other specific systems.
      ```lua
      local log = {}

      local Task_A = System.Create('task', function()
         -- In this example, TASK_A takes time to execute, delaying its execution
         local i = 0
         while i <= 4000 do
            i = i + 1
            if i%1000 == 0 then
               coroutine.yield()
            end
         end
         
         table.insert(log, 'A')
      end)

      local Task_B = System.Create('task', function()
         table.insert(log, 'B')
      end)

      local Task_C = System.Create('task', function()
         table.insert(log, 'C')
      end)

      local Task_D = System.Create('task', function()
         table.insert(log, 'D')
      end)

      local Task_E = System.Create('task', function()
         table.insert(log, 'E')
      end)

      local Task_F = System.Create('task', function()
         table.insert(log, 'F')
      end)

      local Task_G = System.Create('task', function()
         table.insert(log, 'G')
      end)
      
      local Task_H = System.Create('task', function(self)
         table.insert(log, 'H')
      end)

      --[[         
         A<-------C<---+-----F<----+
                  |    |     |     |
             +----+    E<----+     H
             |         |           |
         B<--+----D<---+------G<---+

         A - has no dependency
         B - has no dependency
         C - Depends on A,B
         D - Depends on B
         E - Depends on A,B,C,D
         F - Depends on A,B,C,D,E
         G - Depends on B,D
         H - Depends on A,B,C,D,E,F,G

         Completion order will be B,D,G,A,C,E,F,H      

         > In this example, TASK_A takes time to execute, delaying its execution
      ]]
      Task_A.Before = {Task_C}
      Task_B.Before = {Task_D}
      Task_C.After = {Task_B}
      Task_D.Before = {Task_G}
      Task_F.After = {Task_E}
      Task_E.After = {Task_D, Task_C}
      Task_C.Before = {Task_F}
      Task_H.After = {Task_F, Task_G}
      ```
   - _@type_ `SystemClass[]`
- `System.Before`
   - When the system is a task, it allows you to define that this system should run BEFORE other specific systems.
   - _@see_ `System.After`
   - _@type_ `SystemClass[]`
- `System.version`
   - System Version (GSV).
   - _@see_ `World.version`
   - _@type_ `Number`
- `System._world`
   - _@type_ `World`
- `System._config`
   - _@type_ `table`
- `System.New(world, config)`
   - Create an instance of this system
   - _@param_ `world` `World`
   - _@param_ `config` `table`
   - _@return_ `System`
- `System:GetType()`
   - Get this system class
   - _@return_ `SystemClass`
- `System:Result(query)`
   - Run a query in the world. A shortcut to `self._world:Exec(query)`
   - _@param_ `query` `Query|QueryBuilder` _optional_ If nil, use default query
   - _@return_ `QueryResult`
- `System:Destroy()`
   - Destroy this instance
- `System:OnDestroy()`
   - Allows you to perform some processing or cleaning when the instance is being destroyed
- `System:ShouldUpdate(Time)`
   - Invoked before 'Update', allows you to control the execution of the update
   - _@param_ `Time` `Time`
   - _@return_ `bool` If true, the Update method will be invoked.
- `System:Update(Time)`
   - Run the system's main method
   - _@param_ `Time` `Time`
- `System:OnRemove(Time, entity)`
   - When it is a `QuerySystem`, it allows to be informed when an entity with the characteristics of the query is 
   removed from the world. This method is performed in the step cleanup process.
   - _@param_ `Time` `Time`
   - _@param_ `entity` `Entity`
- `System:OnExit(Time, entity)`
   - When it is a `QuerySystem`, it allows to be informed when an entity has lost the characteristics of that query 
   (has suffered an archetype change and the current query no longer applies). This method is performed in the step 
   cleanup process.
   - _@param_ `Time` `Time`
   - _@param_ `entity` `Entity`
- `System:OnEnter(Time, entity)`
   - When it is a QuerySystem, it allows to be informed when an entity received the characteristics expected by this 
   query (it suffered an archetype change and the current query now applies). This method is performed in the step 
   cleanup process.
   - _@param_ `Time` `Time`
   - _@param_ `entity` `Entity`


# Time
Singleton, reference to the world's global processing time.

- `Time.Now`
   - World Runtime
   - _@type_ `number`
- `Time.NowReal`
   - Real time, received in `World:Update(step, now)` method
   - _@type_ `number`
- `Time.Frame`
   - The time at the beginning of this frame (process). The world receives the current time at the beginning of each 
   frame, with the value increasing per frame.
   - _@type_ `number`
- `Time.FrameReal`
   - The REAL time at the beginning of this frame (`World:Update(step, now)`).
   - _@type_ `number`
- `Time.Process`
   - The time the latest process step has started.
   - _@type_ `number`
- `Time.Delta`
   - The completion time in seconds since the last frame.
   - _@type_ `number`
- `Time.DeltaFixed`
   - Based on world update frequency (`process` step). 
      ```lua
      DeltaFixed = 1000/frequency/1000
      ```
   - _@see_ `World:SetFrequency()`
   - _@type_ `number`
- `Time.Interpolation`
   - The proportion of time since the previous transform relative to processDeltaTime. Used to do interpolation during 
   the rendering step. Allows the `process` step to run at low frequency _(Ex. 30hz)_ and `render` at the maximum rate 
   of the player's device _(Ex. 60hz)_
   - _@type_ `number`

# World
- `World.version`
   - Global System Version (GSV).

   Before executing the `Update()` method of each system, the world version is incremented, so at this point, the 
   world version will always be higher than the running system version.

   Whenever an entity archetype is changed (received or lost component) the entity's version is updated to the current 
   version of the world.

   After executing the System Update method, the version of this system is updated to the current world version.

   This mechanism allows a system to know if an entity has been modified after the last execution of this same system, 
   as the entity's version is superior to the version of the last system execution. Thus, a system can contain logic if 
   it only operates on "dirty" entities, which have undergone changes. The code for this validation on a system is: 
      - `local isDirty = entity.version > self.version`
   - _@type_ `number`
- `World.maxTasksExecTime`
   - Allows you to define the maximum time that the `JobSystem` can operate in each frame. The default value is 
   `0.011666666666666665` = `((1000/60/1000)*0.7)`
      - A game that runs at 30fps has 0.0333 seconds to do all the processing for each frame, including rendering
         - 30FPS = `(1000/30/1000)` = `0.03333333333333333`
      - A game that runs at 60fps has 0.0166 seconds to do all the processing for each frame, including rendering
         - 60FPS = `(1000/60/1000)` = `0.016666666666666666`
   - _@type_ `number` Default `0.011666666666666665`
- `World:SetFrequency(frequency)`
   - Define the frequency that the `process` step will be executed
   - _@param_ `frequency` `number` _optional_ Default 30
- `World:GetFrequency()`
   - Get the frequency of execution of the `process` step
   - _@return_ `number`
- `World:AddSystem(systemClass, config)`
   - Add a new system to the world. Only one instance per type is accepted. If there is already another instance of this 
   system in the world, any new invocation of this method will be ignored.
   - _@param_ `systemClass` `SystemClass` The system to be added in the world
   - _@param_ `config` `table` _optional_ System instance configuration
- `World:Entity(...)`
   - Create a new entity. The entity is created in _DEAD_ state (`entity.isAlive == false`) and will only be visible for 
   queries after the cleaning step _(`OnRemove`,`OnEnter`,`OnExit`)_ of the current step
   - _@param_ `...` `Component[]` _optional_ Instance of the components that this entity will have
   - _@return_ `Entity`
- `World:Remove(entity)`
   - Performs immediate removal of an entity.

   If the entity was created in this step and the cleanup process has not happened yet (therefore the entity is inactive, 
   `entity.isAlive == false`), the `OnRemove` event will never be fired.

   If the entity is alive (`entity.isAlive == true`), even though it is removed immediately, the `OnRemove` event will 
   be fired at the end of the current step.
   - _@param_ `entity` `Entity`
- `World:Exec(query)`
   - Run a query in this world
   - _@param_ `query` `Query|QueryBuilder`
   - _@return_ `QueryResult`
- `World:Update(step, now)`
   - Perform world update. When registered, the `LoopManager` will invoke World Update for each step in the sequence.
      - `process` At the beginning of each frame
      - `transform` After the game engine's physics engine runs
      - `render` Before rendering the current frame
   - _@param_ `step` `"process"|"transform"|"render"`
   - _@param_ `now` `number` Usually os.clock()
- `World:Destroy()`
   - Destroy this instance, removing all entities, systems and events

</div>

[fsm]:https://en.wikipedia.org/wiki/Finite-state_machine


================================================
FILE: docs/architecture.md
================================================
# Architecture

In Software Engineering, ECS is the acronym for Entity Component System, is a software architecture pattern used 
primarily in video game development. An ECS follows the principle of "composition rather than inheritance" that allows 
greater flexibility in defining entities, where each object in a game scene is an entity (eg enemies, projectiles, 
vehicles, etc.). Each entity consists of of one or more components that add behavior or functionality. Therefore, 
the behavior of an entity can be changed at runtime by simply adding or removing components. This eliminates problems of
The ambiguity with which deep and vast inheritance hierarchies, which are difficult to understand, maintain and extend.

For more details:
- [Frequently Asked Questions about ECS](https://github.com/SanderMertens/ecs-faq)
- [Entity Systems Wiki](http://entity-systems.wikidot.com/)
- [Evolve your hierarchy](http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/)
- [ECS on Wikipedia](https://en.wikipedia.org/wiki/Entity_component_system)
- [ECS with Elixir](https://yos.io/2016/09/17/entity-component-systems/)
- [2017 GDC - Overwatch Gameplay Architecture e Netcode](https://www.youtube.com/watch?v=W3aieHjyNvw&ab_channel=GDC)


## Component

They represent the different characteristics of an entity, such as position, speed, geometry, physics, and hit points.
Components only store raw data for an aspect of the object and how it interacts with the world. In others words, the 
component labels the entity as having this particular aspect.

In **ECS Lua**, the creation of a component is done through the `ECS.Component(template)` method.

The `template` parameter can be of any type, where:
- When `table`, this template will be used for creating component instances
   ```lua
   local Component = ECS.Component({
      x = 0, y = 0, z = 0 
   })

   local comp = Component({ x = 33, z = 80 })
   print(comp.x, comp.y, comp.z) -- > 33, 0, 80

   -- it is the same as
   local comp = Component.New({ x = 33, z = 80 })
   print(comp.x, comp.y, comp.z) -- > 33, 0, 80
   ```
- When it's a `function`, it will be invoked when a new component is instantiated. The creation parameter of the 
component is passed to template function
   ```lua
   local Component = ECS.Component(function(param)
      return {
         x = param.x or 1,
         y = param.y or 1,
         z = param.z or 1
      }
   end)

   local comp = Component({ x = 33, z = 80 })
   print(comp.x, comp.y, comp.z) -- > 33, 1, 80
   ```
- If the template type is different from `table` and `function`, **ECS Lua** will generate a template in the format 
`{ value = template }`.
   ```lua
   local Component = ECS.Component(55)

   local comp1 = Component()
   print(comp1.value) -- > 55

   local comp2 = Component({ value = 80 })
   print(comp2.value) -- > 80

   local comp3 = Component("XPTO")
   print(comp3.value) -- > "XPTO"
   ```

### Methods

In **ECS Lua**, components are classes and can therefore have auxiliary methods.

> IMPORTANT! Avoid creating methods that modify component instance data directly, the ideal is that these logics 
stay within the systems, which are, by definition, responsible for changing the data of the entities and their 
components.

```lua
local Person = ECS.Component({
   name = "",
   surname = "",
   birth = 0
})

function Person:FullName()
   return self.name.." "..self.surname
end

function Person:Age()
   return tonumber(os.date("%Y", os.time())) - self.birth
end


local person = Person({ name = "John", surname = "Doe", birth = 2000 })

print(person:FullName()) -- John Doe
print(person:Age()) -- 21
```

### Qualifiers

In "pure ECS" implementations there is a premise that a component can only be added once to an entity. In the vast 
majority of scenarios, this is true. For example, you don't want your entity to have two positions, it makes no sense! 
Therefore, your entity will only have one component of type Position.

But sometimes you will build some functionality that needs your entity to have this behavior, to have more than one 
component of the same **TYPE**. When the framework doesn't support this kind of implementation, you end up with code 
full of hacks to [work around](https://en.wikipedia.org/wiki/Workaround) the problem.

**ECS Lua** implements the **Qualifiers** mechanism so that you can represent a category of components.

To illustrate the usage, let's think about the following scenario: I want to add a 
[Buff](https://en.wikipedia.org/wiki/Game_balance#Buff) system to my game so that my character can receive extra life 
points in specific situations. We want to have the freedom to increase or decrease the amount of buff according to the
region of the map where the player is.

Looking at the scenario above, we could create, at first, the solution below. A `HealthBuff` component to record the 
amount of extra life and two systems. The first `MapRegionSystem` decides how many additional health points the player 
will have for each region, while the second `HealthSystem` represents some system functionality that need to get the 
player's life total at a certain time.

```lua
-- components
local Player = ECS.Component({ health = 100, region = "easy", healthTotal = 100 })
local HealthBuff = ECS.Component({ value = 10 })

-- systems
local MapRegionSystem = System("process", 1, Query.All(Player))

function MapRegionSystem:Update(Time)
   for i, entity in self:Result():Iterator() do
      local player = entity[Player]      
      
      if player.region == "easy" then
         entity[HealthBuff] = nil -- remove buff
      else
         local buff = entity[HealthBuff]
         if buff == nil then
            buff = HealthBuff(0)
            entity:Set(buff)
         end

         if player.region == "hard" then
            buff.value = 15
         elseif player.region == "hell" then
            buff.value = 40
         end  
      end    
   end
end

local HealthSystem = System("process", 2, Query.All(Player).Any(HealthBuff)) 

function HealthSystem:Update(Time)
   for i, entity in self:Result():Iterator() do
      local player = entity[Player]

      local buff = entity[HealthBuff]
      if buff then 
         player.healthTotal = player.health + buff.value
      else
         player.healthTotal = player.health
      end
   end
end
```

So far quiet. However, imagine now that my player can receive **SEVERAL** buffers.
   - He can receive a buffer for the character he is using;
   - another buff when unlocking an item and;
   - you can also buy buffs from the in-game shop.

In this new scenario our solution does not meet, because the `MapRegionSystem` system code does not have the information 
about the other factors, and to attend to it, it will have to know or manage several possible states to decide which is 
the amount of health the player will receive for being in a specific region. The other systems in the game too needed 
to know the region to decide how much buffer to add. In a "pure ECS" solution, we're going to start:

1. share state between systems
1. create "Component TAGs" to facilitate the management of this distributed state,
1. inflate components with an attribute for each system type.

At first this doesn't seem to be a problem, but over time, multiple systems will be called unnecessarily (just to do an 
if and not process that entity). These systems now have extra responsibilities, increasing the complexity of the code, 
making maintenance difficult and facilitating the appearance of bugs.

In **ECS Lua** we solve this kind of problem by creating qualifiers, through the static method 
`ComponentClass.Qualifier(qualifier)`. It accepts a string as a parameter and returns a reference to a specialized class 
of our component. This generated class maintains a strong link with the base class, allowing more complex queries.

Let's change our example using qualifiers.

```lua
-- components
local Player = ECS.Component({ health = 100, region = "easy", healthTotal = 100 })
local HealthBuff = ECS.Component({ value = 10 })
local HealthBuffItem = HealthBuff.Qualifier("Item")
local HealthBuffMapRegion = HealthBuff.Qualifier("Region")
local Item = ECS.Component({ rarity = 0 })

-- systems
local PlayerItemSystem = System("process", 1, Query.All(Player, Item))

function PlayerItemSystem:Update(Time)
   for i, entity in self:Result():Iterator() do
      local item = entity[Item]
      local player = entity[Player]
      
      if item.rarity == "legendary" then
         entity[HealthBuffItem] = 15 -- same as entity:Set(HealthBuffItem.New(15))
      else
         entity[HealthBuffItem] = nil 
      end
   end
end

local MapRegionSystem = System("process", 1, Query.All(Player))

function MapRegionSystem:Update(Time)
   for i, entity in self:Result():Iterator() do
      local player = entity[Player]
      
      if player.region == "easy" then
         entity[HealthBuffMapRegion] = nil
      else
         local buff = entity[HealthBuffMapRegion]
         if buff == nil then
            buff = HealthBuffMapRegion(0)
            entity:Set(buff)
         end

         if player.region == "hard" then
            buff.value = 15
         elseif player.region == "hell" then
            buff.value = 40
         end      
      end
   end
end

local HealthSystem = System("process", 2, Query.All(Player).Any(HealthBuff))

function HealthSystem:Update(Time)
   for i, entity in self:Result():Iterator() do
      local player = entity[Player]

      local healthTotal = player.health

      local buffers = entity:GetAll(HealthBuff)
      for i,buff in ipairs(buffers) do
         healthTotal = healthTotal + buff.value
      end

      player.healthTotal = player.health
   end
end
```

Okay, in this new implementation, the `MapRegionSystem` system only cares about the `HealthBuffMapRegion` qualifier,
while the `PlayerItemSystem` system only manages the `HealthBuffItem` qualifier. We can now create systems that 
specialize in qualifiers and manage only this attribute of the entity. The `HealthSystem` gets and processes all 
entities that have any qualifier from the `HealthBuff` component.

[Check the API](/api?id=component) other methods that can be useful when working with qualifiers.

### FSM - Finite State Machines

__UNDER_CONSTRUCTION__


```lua
local Movement = Component.Create({ Speed = 0 })

--  [Standing] <--> [Walking] <--> [Running]
Movement.States = {
   Standing = {"Walking"},
   Walking  = "*",
   Running  = {"Walking"}
}

Movement.StateInitial = "Standing"

Movement.Case = {
   Standing = function(self, previous)
      print("Transition from "..previous.." to Standing")
   end,
   Walking = function(self, previous)
      print("Transition from "..previous.." to Walking")
   end,
   Running = function(self, previous)
      print("Transition from "..previous.." to Running")
   end
}


local movement = Movement()

movement:SetState("Walking")
movement:SetState("Running")

print(movement:GetState()) -- Running
print(movement:GetPrevState()) -- Walking

movement:SetState("Standing") -- invalid, Running -> Walking|Running
print(movement:GetState()) -- Running
print(movement:GetPrevState()) -- Walking

movement:SetState(nil)
print(movement:GetState()) -- Running
print(movement:GetPrevState()) -- Walking

movement:SetState("INVALID_STATE")
print(movement:GetState()) -- Running
print(movement:GetPrevState()) -- Walking


-- query
local queryStanding = Query.All(Movement.In("Standing"))
local queryInMovement = Query.Any(Movement.In("Walking", "Running"))


-- qualifier
local MovementB = Movement.Qualifier("Sub")
 -- ignored, "States", "StateInitial" and "Case" only work in primary class
MovementB.States = { Standing = {"Walking"} }
```

## Entity

__UNDER_CONSTRUCTION__

```lua
--[[
   [GET]
   01) comp1 = entity[CompType1]
   02) comp1 = entity:Get(CompType1)
   03) comp1, comp2, comp3 = entity:Get(CompType1, CompType2, CompType3)
]]

--[[
   [SET]
   01) entity[CompType1] = nil
   02) entity[CompType1] = value
   03) entity:Set(CompType1, nil)   
   04) entity:Set(CompType1, value)
   05) entity:Set(comp1)
   06) entity:Set(comp1, comp2, ...)
]]

--[[
   [UNSET]
   01) enity:Unset(comp1)
   02) entity[CompType1] = nil
   03) enity:Unset(CompType1)
   04) enity:Unset(comp1, comp1, ...)
   05) enity:Unset(CompType1, CompType2, ...)
]]

--[[
   [Utils]
   01) comps = entity:GetAll()
   01) qualifiers = entity:GetAll(PrimaryClass)
]]
```

## Query

__UNDER_CONSTRUCTION__

## System

__UNDER_CONSTRUCTION__

## Task

__UNDER_CONSTRUCTION__

```lua
local log = {}

local Task_A = System.Create('task', function()
   -- In this example, TASK_A takes time to execute, delaying its execution
   local i = 0
   while i <= 4000 do
      i = i + 1
      if i%1000 == 0 then
         -- Processing is parallel, any time-consuming task must invoke coroutine.yield() after a period of time to 
         -- not block processing
         coroutine.yield()
      end
   end
   
   table.insert(log, 'A')
end)

local Task_B = System.Create('task', function()
   table.insert(log, 'B')
end)

local Task_C = System.Create('task', function()
   table.insert(log, 'C')
end)

local Task_D = System.Create('task', function()
   table.insert(log, 'D')
end)

local Task_E = System.Create('task', function()
   table.insert(log, 'E')
end)

local Task_F = System.Create('task', function()
   table.insert(log, 'F')
end)

local Task_G = System.Create('task', function()
   table.insert(log, 'G')
end)

local Task_H = System.Create('task', function(self)
   table.insert(log, 'H')
end)

--[[         
   A<-------C<---+-----F<----+
            |    |     |     |
       +----+    E<----+     H
       |         |           |
   B<--+----D<---+------G<---+

   A - has no dependency
   B - has no dependency
   C - Depends on A,B
   D - Depends on B
   E - Depends on A,B,C,D
   F - Depends on A,B,C,D,E
   G - Depends on B,D
   H - Depends on A,B,C,D,E,F,G

   Completion order will be B,D,G,A,C,E,F,H      

   > In this example, TASK_A takes time to execute, delaying its execution
]]
Task_A.Before = {Task_C}
Task_B.Before = {Task_D}
Task_C.After = {Task_B}
Task_D.Before = {Task_G}
Task_F.After = {Task_E}
Task_E.After = {Task_D, Task_C}
Task_C.Before = {Task_F}
Task_H.After = {Task_F, Task_G}
```

## World

__UNDER_CONSTRUCTION__







================================================
FILE: docs/faq.md
================================================
# FAQ

__UNDER_CONSTRUCTION__


================================================
FILE: docs/favicon/browserconfig.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/ms-icon-70x70.png"/><square150x150logo src="/ms-icon-150x150.png"/><square310x310logo src="/ms-icon-310x310.png"/><TileColor>#ffffff</TileColor></tile></msapplication></browserconfig>

================================================
FILE: docs/favicon/manifest.json
================================================
{
 "name": "App",
 "icons": [
  {
   "src": "\/android-icon-36x36.png",
   "sizes": "36x36",
   "type": "image\/png",
   "density": "0.75"
  },
  {
   "src": "\/android-icon-48x48.png",
   "sizes": "48x48",
   "type": "image\/png",
   "density": "1.0"
  },
  {
   "src": "\/android-icon-72x72.png",
   "sizes": "72x72",
   "type": "image\/png",
   "density": "1.5"
  },
  {
   "src": "\/android-icon-96x96.png",
   "sizes": "96x96",
   "type": "image\/png",
   "density": "2.0"
  },
  {
   "src": "\/android-icon-144x144.png",
   "sizes": "144x144",
   "type": "image\/png",
   "density": "3.0"
  },
  {
   "src": "\/android-icon-192x192.png",
   "sizes": "192x192",
   "type": "image\/png",
   "density": "4.0"
  }
 ]
}

================================================
FILE: docs/getting-started.md
================================================
# Installation

**ECS Lua** has no external dependencies, so just download the latest version available on 
[releases page](https://github.com/nidorx/ecs-lua/releases) of the project.

There are 3 options to use **ECS Lua**

1. **ECS.lua** Minified version in a single file
1. **ECS_concat.lua** Version concatenated with the original comments, which can be used for debugging during the
development
1. **ECS.zip** Version with files from the `src` directory.
   > Important! All files do the `require` for dependencies that are in the same directory, if it is
   using in a Lua project, register in `package.path`.

   > These `require` do not work in Roblox Luau, due to the import format that Roblox uses.

After importing **ECS Lua**, it is ready to be used. **ECS Lua** registers the global variable `_G.ECS` to
make it easy to use, so you can use the engine in both ways `local ECS = require("ECS")`
(in Roblox `local ECS = require(game.ReplicatedStorage:WaitForChild("ECS"))`) or simply `_G.ECS`.

## LoopManager

In order for the world's systems to receive an update, the `World:Update(step, now)` method must be invoked on each 
frame. To automate this process, **ECS Lua** provides a functionality so that, at the time of instantiation of a 
new world, it registers to receive the update automatically.

The implementation of this method is very simple and more details can be seen in the section
[Architecture - World](/architecture?id=world).

> If you use Roblox you don't need to worry, **ECS Lua** already has a default implementation when it runs on
Roblox, more details below.

## Roblox

You can install directly from Roblox Studio by searching the toolbox for `ECS-lua`, this is the 
[minified engine version](https://www.roblox.com/library/5887881675). When using **ECS Lua** in Roblox, the engine 
already automatically identifies and registers a `LoopManager`, so no additional steps are needed.

# General Concepts

Some common terms in ECS engines are:
- [Entities](/architecture?id=entity): An object with a unique ID that can have multiple components attached to it.
- [Components](/architecture?id=component): Different characteristics of an entity. eg geometry, physics, hit points. Data is only stored in components.
- [Systems](/architecture?id=system): It does the real work, applying the rules of the game, processing entities and modifying their components.
- [Queries](/architecture?id=query): Used by systems to determine which entities they are interested in, based on the components the entities have.
- [World](/architecture?id=world): A container for entities, components, systems and queries.

![General Concepts](assets/diagram-1.png)

The normal workflow when building an ECS-based program:
- Create the `Components` that shape the data you need to use in your game/application.
- Create the `Systems` that will use these `Components` to read and transform the entity data.
- Create `Entities` and attach `Components` to them.
- Run all systems at each frame, perform `Query` in `World` to decide which entities will be modified.

## Component

Components are objects that contain data. In **ECS Lua**, just call the `ECS.Component(template)` method to define a 
`Class` of a component.

The `template` parameter can be of any type, where:
- When `table`, this template will be used for creating component instances;
- When it's a `function`, it will be invoked on instantiation.
- If the template type is different, **ECS Lua** will generate a template in the format `{ value = template }`, this is 
the format used in the `Acceleration` component below.

```lua
local Position = ECS.Component({ 
   x = 0, y = 0, z = 0 
})

-- the same as:
-- ECS.Component({ value = 0.1 })
l
Download .txt
gitextract_abg99oc9/

├── .editorconfig
├── .gitignore
├── .luacov
├── .travis.yml
├── CONTRIBUTING.md
├── ECS.lua
├── ECS_concat.lua
├── LICENSE
├── README.md
├── build.lua
├── docs/
│   ├── .nojekyll
│   ├── README.md
│   ├── _coverpage.md
│   ├── _navbar.md
│   ├── _sidebar.md
│   ├── api.md
│   ├── architecture.md
│   ├── assets/
│   │   ├── boids.rbxl
│   │   ├── logo-r.psd
│   │   ├── pipeline_ecs.psd
│   │   ├── pipeline_old.psd
│   │   ├── repository-open-graph.psd
│   │   ├── tutorial.rbxl
│   │   └── version.psd
│   ├── faq.md
│   ├── favicon/
│   │   ├── browserconfig.xml
│   │   └── manifest.json
│   ├── getting-started.md
│   ├── index.html
│   ├── pt-br/
│   │   ├── README.md
│   │   ├── _coverpage.md
│   │   ├── _navbar.md
│   │   ├── _sidebar.md
│   │   ├── api.md
│   │   ├── architecture.md
│   │   ├── faq.md
│   │   ├── getting-started.md
│   │   ├── tutorial-boids.md
│   │   ├── tutorial-pacman.md
│   │   ├── tutorial-shoot.md
│   │   └── tutorial.md
│   ├── style.css
│   ├── tutorial-boids.md
│   ├── tutorial-pacman.md
│   ├── tutorial-shoot.md
│   ├── tutorial.md
│   ├── z_old_TECH_DETAILS.md
│   └── z_old_TUTORIAL.md
├── examples/
│   └── pong/
│       ├── .editorconfig
│       ├── .gitignore
│       ├── default.project.json
│       ├── pong.rbxlx
│       └── src/
│           ├── client/
│           │   ├── Constants.lua
│           │   ├── Main.client.lua
│           │   ├── Utility.lua
│           │   ├── components/
│           │   │   ├── AudioSource.lua
│           │   │   ├── Ball.lua
│           │   │   ├── BasePart.lua
│           │   │   ├── Paddle.lua
│           │   │   ├── Player.lua
│           │   │   ├── Position.lua
│           │   │   ├── Score.lua
│           │   │   └── Velocity.lua
│           │   └── systems/
│           │       ├── AudioSystem.lua
│           │       ├── BallSystem.lua
│           │       ├── CameraSystem.lua
│           │       ├── MoveSystem.lua
│           │       ├── PaddleHitSystem.lua
│           │       ├── PaddleSystem.lua
│           │       ├── PlayerAiThinkSystem.lua
│           │       ├── PlayerHumanInputSystem.lua
│           │       ├── RenderSystem.lua
│           │       └── ScoreSystem.lua
│           ├── server/
│           │   └── Main.server.lua
│           └── shared/
│               └── ECS.lua
├── modules/
│   ├── bin/
│   │   └── luacov
│   ├── luacov/
│   │   ├── defaults.lua
│   │   ├── hook.lua
│   │   ├── linescanner.lua
│   │   ├── reporter/
│   │   │   └── default.lua
│   │   ├── reporter.lua
│   │   ├── runner.lua
│   │   ├── stats.lua
│   │   ├── tick.lua
│   │   └── util.lua
│   ├── luacov.lua
│   ├── luaunit.lua
│   └── minify.lua
├── roblox/
│   ├── README.md
│   ├── RobloxUtils.lua
│   └── tutorial/
│       ├── default.project.json
│       └── src/
│           ├── client/
│           │   ├── benchmark/
│           │   │   ├── init.client.lua
│           │   │   └── soa.lua
│           │   └── tutorial/
│           │       └── init.client.lua
│           ├── server/
│           │   └── tutorial/
│           │       └── init.server.lua
│           └── shared/
│               ├── teste.lua
│               └── tutorial/
│                   ├── component/
│                   │   ├── FiringComponent.lua
│                   │   └── WeaponComponent.lua
│                   └── system/
│                       ├── CleanupFiringSystem.lua
│                       ├── FiringSystem.lua
│                       └── PlayerShootingSystem.lua
├── src/
│   ├── Archetype.lua
│   ├── Component.lua
│   ├── ComponentFSM.lua
│   ├── ECS.lua
│   ├── Entity.lua
│   ├── EntityRepository.lua
│   ├── Event.lua
│   ├── Query.lua
│   ├── QueryResult.lua
│   ├── RobloxLoopManager.lua
│   ├── System.lua
│   ├── SystemExecutor.lua
│   ├── Timer.lua
│   ├── Utility.lua
│   └── World.lua
├── test/
│   ├── README.md
│   ├── test_Archetype.lua
│   ├── test_Component.lua
│   ├── test_Entity.lua
│   ├── test_EntityRepository.lua
│   ├── test_Event.lua
│   ├── test_Query.lua
│   ├── test_QueryResult.lua
│   ├── test_SystemExecutor.lua
│   └── test_World.lua
└── test.lua
Condensed preview — 127 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,031K chars).
[
  {
    "path": ".editorconfig",
    "chars": 189,
    "preview": "root = true\n\n# Unix-style newlines with a newline ending every file\n[*]\nend_of_line = lf\ninsert_final_newline = true\n\n[*"
  },
  {
    "path": ".gitignore",
    "chars": 50,
    "preview": "# IDE\n.idea\n.vscode\n\nluacov.report.out\n\nsrc/*.zip\n"
  },
  {
    "path": ".luacov",
    "chars": 152,
    "preview": "return {\n\tinclude = {\n\t\t\"^src\",\n      \"src%/.+$\"\n\t},\n\texclude = {\n\t\t\"%.test$\",\n\t},\n   runreport = true,\n   deletestats ="
  },
  {
    "path": ".travis.yml",
    "chars": 337,
    "preview": "language: python\n\nenv:\n  - LUA=\"lua=5.1\"\n  - LUA=\"lua=5.2\"\n  - LUA=\"lua=5.3\"\n  - LUA=\"lua=5.4\"\n  - LUA=\"luajit=2.0\"\n  - "
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 2106,
    "preview": "# Contributing to ECS Lua\nThanks for considering contributing to ECS Lua! This guide has a few tips and guidelines to ma"
  },
  {
    "path": "ECS.lua",
    "chars": 28553,
    "preview": "--[[\n\tECS Lua v2.2.0\n\n\tECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.\n\n\tTh"
  },
  {
    "path": "ECS_concat.lua",
    "chars": 101378,
    "preview": "--[[\n\tECS Lua v2.2.0\n\n\tECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.\n\n\tTh"
  },
  {
    "path": "LICENSE",
    "chars": 1067,
    "preview": "MIT License\n\nCopyright (c) 2020 Alex Rodin\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
  },
  {
    "path": "README.md",
    "chars": 4234,
    "preview": "<p align=\"center\">\n   <a href=\"https://nidorx.github.io/ecs-lua\">\n      <img \n         src=\"docs/assets/logo.svg\" \n     "
  },
  {
    "path": "build.lua",
    "chars": 3951,
    "preview": "\n\nlocal OUTPUT_CONCAT = \"ECS_concat\"\n\nlocal OUTPUT_MINIFIED = \"ECS\"\n\nlocal SRC_FILES = {\n   \"Archetype\",\n   \"Component\","
  },
  {
    "path": "docs/.nojekyll",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docs/README.md",
    "chars": 3818,
    "preview": "# What is it?\n\n**ECS Lua** is a fast and easy to use ECS (Entity Component System) engine for game development.\n\n![](ass"
  },
  {
    "path": "docs/_coverpage.md",
    "chars": 2560,
    "preview": "<div class=\"logo-container\">\n  <div class=\"heats\">\n    <div class=\"h r1 c1\"></div>\n    <div class=\"h r1 c2\"></div>\n    <"
  },
  {
    "path": "docs/_navbar.md",
    "chars": 125,
    "preview": "- [Home](/) &nbsp; &nbsp;\n- Language <span class=\"arrow\">&#x25BE;</span>\n  - [English](/)\n  - [Português do Brasil](/pt-"
  },
  {
    "path": "docs/_sidebar.md",
    "chars": 1251,
    "preview": "- [Home](/)\n- [Installation](/getting-started?id=installation)\n- [General Concepts](/getting-started?id=general-concepts"
  },
  {
    "path": "docs/api.md",
    "chars": 28622,
    "preview": "# API\n<div class=\"api-docs\">\n\n\n# ECS\n- `ECS.Query`\n   - _@type_ `QueryClass`\n- `ECS.Archetype`\n   - _@type_ `ArchetypeCl"
  },
  {
    "path": "docs/architecture.md",
    "chars": 14312,
    "preview": "# Architecture\n\nIn Software Engineering, ECS is the acronym for Entity Component System, is a software architecture patt"
  },
  {
    "path": "docs/faq.md",
    "chars": 30,
    "preview": "# FAQ\n\n__UNDER_CONSTRUCTION__\n"
  },
  {
    "path": "docs/favicon/browserconfig.xml",
    "chars": 281,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<browserconfig><msapplication><tile><square70x70logo src=\"/ms-icon-70x70.png\"/><s"
  },
  {
    "path": "docs/favicon/manifest.json",
    "chars": 720,
    "preview": "{\n \"name\": \"App\",\n \"icons\": [\n  {\n   \"src\": \"\\/android-icon-36x36.png\",\n   \"sizes\": \"36x36\",\n   \"type\": \"image\\/png\",\n  "
  },
  {
    "path": "docs/getting-started.md",
    "chars": 11860,
    "preview": "# Installation\n\n**ECS Lua** has no external dependencies, so just download the latest version available on \n[releases pa"
  },
  {
    "path": "docs/index.html",
    "chars": 6352,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n   <meta charset=\"UTF-8\" />\n   <title>ECS-lua - Entity Component System in Lua<"
  },
  {
    "path": "docs/pt-br/README.md",
    "chars": 4073,
    "preview": "# O que é?\n\n**ECS Lua** é um motor ECS (Entity Component System) rápido e fácil de usar para o desenvolvimento de jogos."
  },
  {
    "path": "docs/pt-br/_coverpage.md",
    "chars": 2559,
    "preview": "<div class=\"logo-container\">\n  <div class=\"heats\">\n    <div class=\"h r1 c1\"></div>\n    <div class=\"h r1 c2\"></div>\n    <"
  },
  {
    "path": "docs/pt-br/_navbar.md",
    "chars": 132,
    "preview": "- [Início](/pt-br/) &nbsp; &nbsp;\n- Idiomas <span class=\"arrow\">&#x25BE;</span>\n  - [English](/)\n  - [Português do Brasi"
  },
  {
    "path": "docs/pt-br/_sidebar.md",
    "chars": 1460,
    "preview": "- [Início](/pt-br/)\n- [Instalação](/pt-br/getting-started?id=instalação)\n- [Conceitos Gerais](/pt-br/getting-started?id="
  },
  {
    "path": "docs/pt-br/api.md",
    "chars": 28622,
    "preview": "# API\n<div class=\"api-docs\">\n\n\n# ECS\n- `ECS.Query`\n   - _@type_ `QueryClass`\n- `ECS.Archetype`\n   - _@type_ `ArchetypeCl"
  },
  {
    "path": "docs/pt-br/architecture.md",
    "chars": 13138,
    "preview": "# Arquitetura\n\nEm Engenharia de Software, ECS é o acrônimo de Entity Component System (em português: Sistema de Componen"
  },
  {
    "path": "docs/pt-br/faq.md",
    "chars": 47,
    "preview": "# Perguntas Frequentes\n\n__UNDER_CONSTRUCTION__\n"
  },
  {
    "path": "docs/pt-br/getting-started.md",
    "chars": 12628,
    "preview": "# Instalação\n\nO **ECS Lua** nao possui dependencias externas, portanto, basta fazer o download da ultima versao disponív"
  },
  {
    "path": "docs/pt-br/tutorial-boids.md",
    "chars": 178,
    "preview": "# Tutorial - Boids\n\n__UNDER_CONSTRUCTION__\n\n> OBJETIVO: Aplicar o uso do Job System para atualizar algumas centenas (ou "
  },
  {
    "path": "docs/pt-br/tutorial-pacman.md",
    "chars": 138,
    "preview": "# Tutorial - Pacman\n\n__UNDER_CONSTRUCTION__\n\n> OBJETIVO: Demonstrar o uso de maquinas de estado para criar a \"inteligenc"
  },
  {
    "path": "docs/pt-br/tutorial-shoot.md",
    "chars": 153,
    "preview": "# Tutorial - Jogo de Tiro\n\n__UNDER_CONSTRUCTION__\n\n> OBJETIVO: Demonstrar o uso das principais funcionalidades do ECS Lu"
  },
  {
    "path": "docs/pt-br/tutorial.md",
    "chars": 36,
    "preview": "# Tutoriais\n\n__UNDER_CONSTRUCTION__\n"
  },
  {
    "path": "docs/style.css",
    "chars": 8639,
    "preview": ".meta-container {\n   text-align: right;\n}\n\n.meta-container>.edit-button {\n   text-decoration: none;\n   font-size      : "
  },
  {
    "path": "docs/tutorial-boids.md",
    "chars": 171,
    "preview": "# Tutorial - Boids\n\n__UNDER_CONSTRUCTION__\n\n> OBJECTIVE: To apply the use of the Job System to update a few hundred (or "
  },
  {
    "path": "docs/tutorial-pacman.md",
    "chars": 135,
    "preview": "# Tutorial - Pacman\n\n__UNDER_CONSTRUCTION__\n\n> OBJECTIVE: Demonstrate the use of state machines to create the \"intellige"
  },
  {
    "path": "docs/tutorial-shoot.md",
    "chars": 148,
    "preview": "# Tutorial - Shooting Game\n\n__UNDER_CONSTRUCTION__\n\n> OBJECTIVE: Demonstrate the use of the main features of ECS Lua and"
  },
  {
    "path": "docs/tutorial.md",
    "chars": 36,
    "preview": "# Tutorials\n\n__UNDER_CONSTRUCTION__\n"
  },
  {
    "path": "docs/z_old_TECH_DETAILS.md",
    "chars": 9232,
    "preview": "# Roblox-ECS - Technical implementation details\n\n## Roblox Pipeline\n\nBefore going into the details, let's review some im"
  },
  {
    "path": "docs/z_old_TUTORIAL.md",
    "chars": 21263,
    "preview": "## Roblox-ECS Tutorial - Shooting Game\n\nIn this topic, we will see how to implement a simple shooting game, inspired by "
  },
  {
    "path": "examples/pong/.editorconfig",
    "chars": 197,
    "preview": "root = true\n\n# Unix-style newlines with a newline ending every file\n[*]\nend_of_line = lf\ninsert_final_newline = true\n\n[*"
  },
  {
    "path": "examples/pong/.gitignore",
    "chars": 117,
    "preview": "# IDE\n.idea\n*.iml\n.vscode\n\n# Roblox Studio\n# place-dev.rbxl\n**/*.rbxl.lock\n**/*.rbxlx.lock\n**/*.blend1\n\n# misc\nbuild\n"
  },
  {
    "path": "examples/pong/default.project.json",
    "chars": 638,
    "preview": "{\n\t\"name\": \"ecs-lua-pong\",\n\t\"tree\": {\n      \"$className\": \"DataModel\",\n      \"ReplicatedStorage\": {\n         \"$className"
  },
  {
    "path": "examples/pong/pong.rbxlx",
    "chars": 125033,
    "preview": "<roblox xmlns:xmime=\"http://www.w3.org/2005/05/xmlmime\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noName"
  },
  {
    "path": "examples/pong/src/client/Constants.lua",
    "chars": 345,
    "preview": "\nlocal BALL_SPEED = 50\n\nlocal constants = {\n   BALL_BOOST = 0.3,\n   BALL_RADIUS = 1,\n   BALL_SPEED = BALL_SPEED,\n   BALL"
  },
  {
    "path": "examples/pong/src/client/Main.client.lua",
    "chars": 1437,
    "preview": "\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nlocal Constants = require(script.Parent:WaitForChild(\""
  },
  {
    "path": "examples/pong/src/client/Utility.lua",
    "chars": 224,
    "preview": "\nlocal Utility = {}\n\nfunction Utility.map(x, inMin, inMax, outMin, outMax)\n   return (x - inMin)*(outMax - outMin)/(inMa"
  },
  {
    "path": "examples/pong/src/client/components/AudioSource.lua",
    "chars": 597,
    "preview": "\nlocal AudioSource = _G.ECS.Component({\n   clip = \"\",     -- sound asset\n   volume = 10,    -- playback volume between ["
  },
  {
    "path": "examples/pong/src/client/components/Ball.lua",
    "chars": 98,
    "preview": "\nlocal Ball = _G.ECS.Component({\n   secondary = false,\n   initialDirection = nil,\n})\n\nreturn Ball\n"
  },
  {
    "path": "examples/pong/src/client/components/BasePart.lua",
    "chars": 54,
    "preview": "\nlocal BasePart = _G.ECS.Component()\n\nreturn BasePart\n"
  },
  {
    "path": "examples/pong/src/client/components/Paddle.lua",
    "chars": 191,
    "preview": "local Paddle = _G.ECS.Component({\n   side = \"left\",\n   hits = 0,\n   target = 0,    -- -1 = bottom, 0 = middle, 1 = top\n "
  },
  {
    "path": "examples/pong/src/client/components/Player.lua",
    "chars": 136,
    "preview": "\nlocal Player = _G.ECS.Component()\nlocal PlayerAI = Player.Qualifier(\"AI\")\nlocal PlayerHuman = Player.Qualifier(\"Human\")"
  },
  {
    "path": "examples/pong/src/client/components/Position.lua",
    "chars": 74,
    "preview": "\nlocal Position = _G.ECS.Component(Vector3.new(0, 0, 0))\n\nreturn Position\n"
  },
  {
    "path": "examples/pong/src/client/components/Score.lua",
    "chars": 49,
    "preview": "\nlocal Score = _G.ECS.Component(0)\n\nreturn Score\n"
  },
  {
    "path": "examples/pong/src/client/components/Velocity.lua",
    "chars": 74,
    "preview": "\nlocal Velocity = _G.ECS.Component(Vector3.new(0, 0, 0))\n\nreturn Velocity\n"
  },
  {
    "path": "examples/pong/src/client/systems/AudioSystem.lua",
    "chars": 1604,
    "preview": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Components = Client.components\nlocal BasePart = require(Co"
  },
  {
    "path": "examples/pong/src/client/systems/BallSystem.lua",
    "chars": 2720,
    "preview": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Constants = require(Client.Constants)\n\nlocal Components = "
  },
  {
    "path": "examples/pong/src/client/systems/CameraSystem.lua",
    "chars": 327,
    "preview": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Constants = require(Client.Constants)\n\nlocal CFRAME = CFra"
  },
  {
    "path": "examples/pong/src/client/systems/MoveSystem.lua",
    "chars": 612,
    "preview": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Components = Client.components\nlocal Velocity = require(Co"
  },
  {
    "path": "examples/pong/src/client/systems/PaddleHitSystem.lua",
    "chars": 4728,
    "preview": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Utility = require(Client.Utility)\nlocal Constants = requir"
  },
  {
    "path": "examples/pong/src/client/systems/PaddleSystem.lua",
    "chars": 1843,
    "preview": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Utility = require(Client.Utility)\nlocal Constants = requir"
  },
  {
    "path": "examples/pong/src/client/systems/PlayerAiThinkSystem.lua",
    "chars": 2049,
    "preview": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\n\nlocal Utility = require(Client.Utility)\nlocal Constants = requi"
  },
  {
    "path": "examples/pong/src/client/systems/PlayerHumanInputSystem.lua",
    "chars": 1012,
    "preview": "local ECS = _G.ECS\n\nlocal UserInputService = game:GetService(\"UserInputService\")\nlocal CurrentCamera = game.workspace.Cu"
  },
  {
    "path": "examples/pong/src/client/systems/RenderSystem.lua",
    "chars": 640,
    "preview": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Components = Client.components\nlocal Position = require(Co"
  },
  {
    "path": "examples/pong/src/client/systems/ScoreSystem.lua",
    "chars": 2442,
    "preview": "local ECS = _G.ECS\n\nlocal Client = script.Parent.Parent\nlocal Constants = require(Client.Constants)\n\nlocal Components = "
  },
  {
    "path": "examples/pong/src/server/Main.server.lua",
    "chars": 40,
    "preview": "game.Players.CharacterAutoLoads = false\n"
  },
  {
    "path": "examples/pong/src/shared/ECS.lua",
    "chars": 28606,
    "preview": "--[[\n\tECS Lua v2.2.0\n\n\tECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.\n\n\tTh"
  },
  {
    "path": "modules/bin/luacov",
    "chars": 2526,
    "preview": "#!/usr/bin/env lua\nlocal runner = require(\"luacov.runner\")\n\nlocal patterns = {}\nlocal configfile\nlocal reporter\n\nlocal h"
  },
  {
    "path": "modules/luacov/defaults.lua",
    "chars": 2889,
    "preview": "--- Default values for configuration options.\n-- For project specific configuration create '.luacov' file in your projec"
  },
  {
    "path": "modules/luacov/hook.lua",
    "chars": 2186,
    "preview": "------------------------\n-- Hook module, creates debug hook used by LuaCov.\n-- @class module\n-- @name luacov.hook\nlocal "
  },
  {
    "path": "modules/luacov/linescanner.lua",
    "chars": 11136,
    "preview": "local LineScanner = {}\nLineScanner.__index = LineScanner\n\nfunction LineScanner:new()\n   return setmetatable({\n      firs"
  },
  {
    "path": "modules/luacov/reporter/default.lua",
    "chars": 32,
    "preview": "return require \"luacov.reporter\""
  },
  {
    "path": "modules/luacov/reporter.lua",
    "chars": 13890,
    "preview": "------------------------\n-- Report module, will transform statistics file into a report.\n-- @class module\n-- @name luaco"
  },
  {
    "path": "modules/luacov/runner.lua",
    "chars": 21936,
    "preview": "---------------------------------------------------\n-- Statistics collecting module.\n-- Calling the module table is a sh"
  },
  {
    "path": "modules/luacov/stats.lua",
    "chars": 2078,
    "preview": "-----------------------------------------------------\n-- Manages the file with statistics (being) collected.\n-- @class m"
  },
  {
    "path": "modules/luacov/tick.lua",
    "chars": 323,
    "preview": "\n--- Load luacov using this if you want it to periodically\n-- save the stats file. This is useful if your script is\n-- a"
  },
  {
    "path": "modules/luacov/util.lua",
    "chars": 2371,
    "preview": "---------------------------------------------------\n-- Utility module.\n-- @class module\n-- @name luacov.util\nlocal util "
  },
  {
    "path": "modules/luacov.lua",
    "chars": 281,
    "preview": "--- Loads `luacov.runner` and immediately starts it.\n-- Useful for launching scripts from the command-line. Returns the "
  },
  {
    "path": "modules/luaunit.lua",
    "chars": 126555,
    "preview": "--[[\n        luaunit.lua\n\nDescription: A unit testing framework\nHomepage: https://github.com/bluebird75/luaunit\nDevelopm"
  },
  {
    "path": "modules/minify.lua",
    "chars": 87031,
    "preview": "--[[\n   MIT License\n\n   Copyright (c) 2017 Mark Langen\n\n   Permission is hereby granted, free of charge, to any person o"
  },
  {
    "path": "roblox/README.md",
    "chars": 4543,
    "preview": "# Roblox-ECS\n\nLINK: https://github.com/nidorx/ecs-lua/blob/master/src/shared/ECSUtil.lua\n\n## Utility Systems and Compone"
  },
  {
    "path": "roblox/RobloxUtils.lua",
    "chars": 16828,
    "preview": "local ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\n-- precision\nlocal EPSILON = 0.000000001\n\nlocal functio"
  },
  {
    "path": "roblox/tutorial/default.project.json",
    "chars": 515,
    "preview": "{\n   \"name\": \"dat.GUI\",\n   \"tree\": {\n      \"$className\": \"DataModel\",\n      \"ReplicatedStorage\": {\n         \"$className\""
  },
  {
    "path": "roblox/tutorial/src/client/benchmark/init.client.lua",
    "chars": 1699,
    "preview": "repeat wait() until game:GetService('Players').LocalPlayer.Character\n\nlocal DISABLED = true\n\n--[[\n   Benchmark, data ori"
  },
  {
    "path": "roblox/tutorial/src/client/benchmark/soa.lua",
    "chars": 2906,
    "preview": "\nlocal case = {\n   name = 'Struct of Arrays vs. Array of Structs'\n}\n\n-- produce equal sequences of numbers for both test"
  },
  {
    "path": "roblox/tutorial/src/client/tutorial/init.client.lua",
    "chars": 2198,
    "preview": "repeat wait() until game.Players.LocalPlayer.Character\n\nlocal Players \t   = game:GetService(\"Players\")\nlocal Player \t   "
  },
  {
    "path": "roblox/tutorial/src/server/tutorial/init.server.lua",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "roblox/tutorial/src/shared/teste.lua",
    "chars": 2465,
    "preview": "local ECS = {}\n\nlocal Query, System, Component,  = ECS.Query, ECS.System, ECS.Component,\n\n\nlocal Transform = Component({"
  },
  {
    "path": "roblox/tutorial/src/shared/tutorial/component/FiringComponent.lua",
    "chars": 206,
    "preview": "local ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nreturn ECS.Component('Firing', function(firedAt)\n   if "
  },
  {
    "path": "roblox/tutorial/src/shared/tutorial/component/WeaponComponent.lua",
    "chars": 96,
    "preview": "local ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\nreturn ECS.Component('Weapon')\n"
  },
  {
    "path": "roblox/tutorial/src/shared/tutorial/system/CleanupFiringSystem.lua",
    "chars": 698,
    "preview": "\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\n\n-- Components\nlocal Components = game.ReplicatedStorag"
  },
  {
    "path": "roblox/tutorial/src/shared/tutorial/system/FiringSystem.lua",
    "chars": 1557,
    "preview": "\nlocal ECS      = require(game.ReplicatedStorage:WaitForChild(\"ECS\"))\nlocal ECSUtil  = require(game.ReplicatedStorage:Wa"
  },
  {
    "path": "roblox/tutorial/src/shared/tutorial/system/PlayerShootingSystem.lua",
    "chars": 872,
    "preview": "\nlocal UserInputService = game:GetService(\"UserInputService\")\nlocal ECS = require(game.ReplicatedStorage:WaitForChild(\"E"
  },
  {
    "path": "src/Archetype.lua",
    "chars": 5688,
    "preview": "\nlocal archetypes = {}\n\nlocal CACHE_WITH = {}\nlocal CACHE_WITHOUT = {}\n\n-- Version of the last registered archetype. Use"
  },
  {
    "path": "src/Component.lua",
    "chars": 10047,
    "preview": "local Utility = require(\"Utility\")\nlocal ComponentFSM = require(\"ComponentFSM\")\n\nlocal copyDeep = Utility.copyDeep\nlocal"
  },
  {
    "path": "src/ComponentFSM.lua",
    "chars": 5435,
    "preview": "--[[\n   Facilitate the construction and use of a Finite State Machine (FSM) using ECS\n\n   Example:\n      local Movement "
  },
  {
    "path": "src/ECS.lua",
    "chars": 2672,
    "preview": "--[[\n   ECS Lua v2.2.0\n\n   ECS Lua is a fast and easy to use ECS (Entity Component System) engine for game development.\n"
  },
  {
    "path": "src/Entity.lua",
    "chars": 8252,
    "preview": "--[[\n   The entity is a fundamental part of the Entity Component System. Everything in your game that has data or an \n  "
  },
  {
    "path": "src/EntityRepository.lua",
    "chars": 2733,
    "preview": "local Event = require(\"Event\")\n\n--[[\n   The repository (database) of entities in a world.\n\n   The repository indexes ent"
  },
  {
    "path": "src/Event.lua",
    "chars": 1211,
    "preview": "\n--[[\n   Subscription\n]]\nlocal Connection = {}\nConnection.__index = Connection\n\nfunction Connection.New(event, handler)\n"
  },
  {
    "path": "src/Query.lua",
    "chars": 8224,
    "preview": "\nlocal QueryResult = require(\"QueryResult\")\n\n--[[\n   Global cache result.\n\n   The validated components are always the sa"
  },
  {
    "path": "src/QueryResult.lua",
    "chars": 9796,
    "preview": "\n--[[\n   OperatorFunction = function(param, value, count) => newValue, acceptItem, mustContinue\n]]\n\nlocal function opera"
  },
  {
    "path": "src/RobloxLoopManager.lua",
    "chars": 971,
    "preview": "local function InitManager()\n   local RunService = game:GetService(\"RunService\")\n   \n   return {\n      Register = functi"
  },
  {
    "path": "src/System.lua",
    "chars": 3255,
    "preview": "\nlocal STEPS = { \"task\", \"render\", \"process\", \"transform\" }\n\nlocal System = {}\n\n--[[\n   Create new System Class\n\n   @par"
  },
  {
    "path": "src/SystemExecutor.lua",
    "chars": 15144,
    "preview": "\n--[[\n   After = {SystemC, SystemD}, An update order that requests ECS update this system after it updates another speci"
  },
  {
    "path": "src/Timer.lua",
    "chars": 3293,
    "preview": "\n-- if execution is slow, perform a maximum of 4 simultaneous updates in order to keep the fixrate\nlocal MAX_SKIP_FRAMES"
  },
  {
    "path": "src/Utility.lua",
    "chars": 1413,
    "preview": "--[[\n   Utility library.\n]]\nlocal Utility = {}\n\nif table.unpack == nil then\n\ttable.unpack = unpack\nend\n\nif table.find =="
  },
  {
    "path": "src/World.lua",
    "chars": 11000,
    "preview": "local Timer = require(\"Timer\")\nlocal Event = require(\"Event\")\nlocal Entity = require(\"Entity\")\nlocal Archetype = require"
  },
  {
    "path": "test/README.md",
    "chars": 40,
    "preview": "see [CONTRIBUTING.md](/CONTRIBUTING.md)\n"
  },
  {
    "path": "test/test_Archetype.lua",
    "chars": 5071,
    "preview": "local lu = require('luaunit')\n\nlocal Archetype = require('Archetype')\n\nTestArchetype = {}\n\nlocal SEQ = 1\nlocal function "
  },
  {
    "path": "test/test_Component.lua",
    "chars": 15242,
    "preview": "local lu = require('luaunit')\n\nfunction sleep(a)\n   local sec = tonumber(os.clock() + a); \n   while (os.clock() < sec) d"
  },
  {
    "path": "test/test_Entity.lua",
    "chars": 20972,
    "preview": "local lu = require('luaunit')\n\nlocal Event = require('Event')\nlocal Entity = require('Entity')\nlocal Component = require"
  },
  {
    "path": "test/test_EntityRepository.lua",
    "chars": 4069,
    "preview": "local lu = require('luaunit')\n\nlocal EntityRepository = require('EntityRepository')\n\nlocal function query(...)\n   local "
  },
  {
    "path": "test/test_Event.lua",
    "chars": 944,
    "preview": "local lu = require('luaunit')\n\nlocal Event = require('Event')\n\nTestEvent = {}\n\nfunction TestEvent:test_ConnectFireDiscon"
  },
  {
    "path": "test/test_Query.lua",
    "chars": 2416,
    "preview": "local lu = require('luaunit')\n\nlocal Query = require('Query')\nlocal Archetype = require('Archetype')\nlocal Component = r"
  },
  {
    "path": "test/test_QueryResult.lua",
    "chars": 10318,
    "preview": "local lu = require('luaunit')\n\nlocal Entity = require('Entity')\nlocal Archetype = require('Archetype')\nlocal Component ="
  },
  {
    "path": "test/test_SystemExecutor.lua",
    "chars": 13182,
    "preview": "local lu = require('luaunit')\n\nlocal World = require('World')\nlocal Query = require('Query')\nlocal System = require('Sys"
  },
  {
    "path": "test/test_World.lua",
    "chars": 10101,
    "preview": "local lu = require('luaunit')\n\nlocal World = require('World')\nlocal Query = require('Query')\nlocal System = require('Sys"
  },
  {
    "path": "test.lua",
    "chars": 634,
    "preview": "\npackage.path = package.path .. \";modules/?.lua\"\npackage.path = package.path .. \";src/?.lua\"\nlocal lu = require(\"luaunit"
  }
]

// ... and 7 more files (download for full content)

About this extraction

This page contains the full source code of the nidorx/ecs-lua GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 127 files (947.8 KB), approximately 258.9k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!