Add pages to docs

This commit is contained in:
Ukendio 2024-05-25 03:53:35 +02:00
parent 4375150683
commit 144cd044e8
6 changed files with 711 additions and 278 deletions

45
docs/api-types.md Normal file
View file

@ -0,0 +1,45 @@
# World
A World contains all ECS data
Games can have multiple worlds, although typically only one is necessary. These worlds are isolated from each other, meaning they donot share the same entities nor component IDs.
---
# Entity
An unique id.
Entities consist out of a number unique to the entity in the lower 32 bits, and a counter used to track entity liveliness in the upper 32 bits. When an id is recycled, its generation count is increased. This causes recycled ids to be very large (>4 billion), which is normal.
---
# QueryIter
A result from the `World:query` function.
Queries are used to iterate over entities that match against the set collection of components.
Calling it in a loop will allow iteration over the results.
```lua
for id, enemy, charge, model in world:query(Enemy, Charge, Model) do
-- Do something
end
```
### QueryIter.without
QueryIter.without(iter: QueryIter
...: [Entity](../api-types/Entity)): QueryIter
Create a new Query Iterator from the filter
#### Parameters
world The world.
... The collection of components to filter archetypes against.
#### Returns
The new query iterator.

189
docs/api/world.md Normal file
View file

@ -0,0 +1,189 @@
# World
### World.new
World.new(): [World](../api-types/World)
Create a new world.
#### Returns
A new world
---
### World.entity
World.entity(world: [World](../api-types/World)): [Entity](../api-types/Entity)
Creates an entity in the world.
#### Returns
A new entiity id
---
### World.target
World.target(world: [World](../api-types/World),
entity: [Entity](../api-types/Entity),
rel: [Entity](../api-types/Entity)): [Entity](../api-types/Entity)
Get the target of a relationship.
This will return a target (second element of a pair) of the entity for the specified relationship.
#### Parameters
world The world.
entity The entity.
rel The relationship between the entity and the target.
#### Returns
The first target for the relationship
---
### World.add
World.add(world: [World](../api-types/World),
entity: [Entity](../api-types/Entity),
id: [Entity](../api-types/Entity)): [Entity](../api-types/Entity)
Add a (component) id to an entity.
This operation adds a single (component) id to an entity.
If the entity already has the id, this operation will have no side effects.
#### Parameters
world The world.
entity The entity.
id The id to add.
---
### World.remove
World.remove(world: [World](../api-types/World),
entity: [Entity](../api-types/Entity),
id: [Entity](../api-types/Entity)): [Entity](../api-types/Entity)
Remove a (component) id to an entity.
This operation removes a single (component) id to an entity.
If the entity already has the id, this operation will have no side effects.
#### Parameters
world The world.
entity The entity.
id The id to add.
---
### World.get
World.get(world: [World](../api-types/World),
entity: [Entity](../api-types/Entity),
id: [Entity](../api-types/Entity)): any
Gets the component data.
#### Parameters
world The world.
entity The entity.
id The id of component to get.
#### Returns
The component data, nil if the entity does not have the componnet.
---
### World.set
World.set(world: [World](../api-types/World),
entity: [Entity](../api-types/Entity),
id: [Entity](../api-types/Entity)
data: any)
Set the value of a component.
#### Parameters
world The world.
entity The entity.
id The id of the componment set.
data The data to the component.
---
### World.query
World.query(world: [World](../api-types/World),
...: [Entity](../api-types/Entity)): [QueryIter](../api-types/QueryIter)
Create a QueryIter from the list of filters.
#### Parameters
world The world.
... The collection of components to match entities against.
#### Returns
The query iterator.
---
# Pair
### pair
pair(first: [Entity](../api-types/Entity),
second: [Entity](../api-types/Entity)): [Entity](../api-types/Entity)
Creates a composite key.
#### Parameters
first The first element.
second The second element.
#### Returns
The pair of the two elements
---
### IS_PAIR
jecs.IS_PAIR(id: [Entity](../api-types/Entity)): boolean
Creates a composite key.
#### Parameters
id The id to check.
#### Returns
If id is a pair.
---
# Constants
### OnAdd
---
### OnRemove
---
### Rest
---
### OnSet
---
### Wildcard
Matches any id, returns all matches.

5
docs/world.md Normal file
View file

@ -0,0 +1,5 @@
# World
A World contains all ECS data
Games can have multiple worlds, although typically only one is necessary. These worlds are isolated from each other.
is queryable and can be used to get entities with a specific set of components. Entities are simply ever-increasing integers.

View file

@ -103,14 +103,6 @@ local function ECS_IS_PAIR(e: number)
return (e % 2 ^ 4) // FLAGS_PAIR ~= 0
end
function separate(e: number)
local _typeFlags = e % 0x10
-- Revert to //= after highligting gets fixed
--
e = e // ECS_ID_FLAGS_MASK
return e // ECS_ENTITY_MASK, e % ECS_GENERATION_MASK, _typeFlags
end
-- HIGH 24 bits LOW 24 bits
local function ECS_GENERATION(e: i53)
e = e // 0x10
@ -118,7 +110,9 @@ local function ECS_GENERATION(e: i53)
end
local function ECS_GENERATION_INC(e: i53)
local id, generation, flags = separate(e)
local flags = e // 0x10
local id = flags // ECS_ENTITY_MASK
local generation = flags % ECS_GENERATION_MASK
return ECS_COMBINE(id, generation + 1) + flags
end

186
mkdocs.yml Normal file
View file

@ -0,0 +1,186 @@
site_name: Fusion
site_url: https://elttob.uk/Fusion/
repo_name: dphfox/Fusion
repo_url: https://github.com/dphfox/Fusion
extra:
version:
provider: mike
theme:
name: material
custom_dir: docs/assets/overrides
logo: assets/logo
favicon: assets/logo-dark.svg
palette:
- media: "(prefers-color-scheme: dark)"
scheme: fusiondoc-dark
toggle:
icon: octicons/sun-24
title: Switch to light theme
- media: "(prefers-color-scheme: light)"
scheme: fusiondoc-light
toggle:
icon: octicons/moon-24
title: Switch to dark theme
font:
text: Plus Jakarta Sans
code: JetBrains Mono
features:
- navigation.tabs
- navigation.top
- navigation.sections
- navigation.instant
- navigation.indexes
- search.suggest
- search.highlight
icon:
repo: octicons/mark-github-16
extra_css:
- assets/theme/fusiondoc.css
- assets/theme/colours.css
- assets/theme/code.css
- assets/theme/paragraph.css
- assets/theme/page.css
- assets/theme/admonition.css
- assets/theme/404.css
- assets/theme/api-reference.css
- assets/theme/dev-tools.css
extra_javascript:
- assets/scripts/smooth-scroll.js
nav:
- Home: index.md
- Tutorials:
- Get Started: tutorials/index.md
- Installing Fusion: tutorials/get-started/installing-fusion.md
- Developer Tools: tutorials/get-started/developer-tools.md
- Getting Help: tutorials/get-started/getting-help.md
- Fundamentals:
- Scopes: tutorials/fundamentals/scopes.md
- Values: tutorials/fundamentals/values.md
- Observers: tutorials/fundamentals/observers.md
- Computeds: tutorials/fundamentals/computeds.md
- Tables:
- ForValues: tutorials/tables/forvalues.md
- ForKeys: tutorials/tables/forkeys.md
- ForPairs: tutorials/tables/forpairs.md
- Animation:
- Tweens: tutorials/animation/tweens.md
- Springs: tutorials/animation/springs.md
- Roblox:
- Hydration: tutorials/roblox/hydration.md
- New Instances: tutorials/roblox/new-instances.md
- Parenting: tutorials/roblox/parenting.md
- Events: tutorials/roblox/events.md
- Change Events: tutorials/roblox/change-events.md
- Outputs: tutorials/roblox/outputs.md
- References: tutorials/roblox/references.md
- Best Practices:
- Components: tutorials/best-practices/components.md
- Instance Handling: tutorials/best-practices/instance-handling.md
- Callbacks: tutorials/best-practices/callbacks.md
- State: tutorials/best-practices/state.md
- Sharing Values: tutorials/best-practices/sharing-values.md
- Error Safety: tutorials/best-practices/error-safety.md
- Optimisation: tutorials/best-practices/optimisation.md
- Examples:
- Home: examples/index.md
- Cookbook:
- examples/cookbook/index.md
- Player List: examples/cookbook/player-list.md
- Animated Computed: examples/cookbook/animated-computed.md
- Fetch Data From Server: examples/cookbook/fetch-data-from-server.md
- Light & Dark Theme: examples/cookbook/light-and-dark-theme.md
- Button Component: examples/cookbook/button-component.md
- Loading Spinner: examples/cookbook/loading-spinner.md
- Drag & Drop: examples/cookbook/drag-and-drop.md
- API Reference:
- api-reference/index.md
- General:
- Errors: api-reference/general/errors.md
- Types:
- Contextual: api-reference/general/types/contextual.md
- Version: api-reference/general/types/version.md
- Members:
- Contextual: api-reference/general/members/contextual.md
- Safe: api-reference/general/members/safe.md
- version: api-reference/general/members/version.md
- Memory:
- Types:
- Scope: api-reference/memory/types/scope.md
- ScopedObject: api-reference/memory/types/scopedobject.md
- Task: api-reference/memory/types/task.md
- Members:
- deriveScope: api-reference/memory/members/derivescope.md
- doCleanup: api-reference/memory/members/docleanup.md
- scoped: api-reference/memory/members/scoped.md
- State:
- Types:
- UsedAs: api-reference/state/types/usedas.md
- Computed: api-reference/state/types/computed.md
- Dependency: api-reference/state/types/dependency.md
- Dependent: api-reference/state/types/dependent.md
- For: api-reference/state/types/for.md
- Observer: api-reference/state/types/observer.md
- StateObject: api-reference/state/types/stateobject.md
- Use: api-reference/state/types/use.md
- Value: api-reference/state/types/value.md
- Members:
- Computed: api-reference/state/members/computed.md
- ForKeys: api-reference/state/members/forkeys.md
- ForPairs: api-reference/state/members/forpairs.md
- ForValues: api-reference/state/members/forvalues.md
- Observer: api-reference/state/members/observer.md
- peek: api-reference/state/members/peek.md
- Value: api-reference/state/members/value.md
- Roblox:
- Types:
- Child: api-reference/roblox/types/child.md
- PropertyTable: api-reference/roblox/types/propertytable.md
- SpecialKey: api-reference/roblox/types/specialkey.md
- Members:
- Attribute: api-reference/roblox/members/attribute.md
- AttributeChange: api-reference/roblox/members/attributechange.md
- AttributeOut: api-reference/roblox/members/attributeout.md
- Children: api-reference/roblox/members/children.md
- Hydrate: api-reference/roblox/members/hydrate.md
- New: api-reference/roblox/members/new.md
- OnChange: api-reference/roblox/members/onchange.md
- OnEvent: api-reference/roblox/members/onevent.md
- Out: api-reference/roblox/members/out.md
- Ref: api-reference/roblox/members/ref.md
- Animation:
- Types:
- Animatable: api-reference/animation/types/animatable.md
- Spring: api-reference/animation/types/spring.md
- Tween: api-reference/animation/types/tween.md
- Members:
- Tween: api-reference/animation/members/tween.md
- Spring: api-reference/animation/members/spring.md
- Extras:
- Home: extras/index.md
- Backgrounds: extras/backgrounds.md
- Brand Guidelines: extras/brand-guidelines.md
markdown_extensions:
- admonition
- attr_list
- meta
- md_in_html
- pymdownx.superfences
- pymdownx.betterem
- pymdownx.details
- pymdownx.tabbed:
alternate_style: true
- pymdownx.inlinehilite
- toc:
permalink: true
- pymdownx.highlight:
guess_lang: false
- pymdownx.emoji:
emoji_index: !!python/name:materialx.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg

View file

@ -1,5 +1,5 @@
local testkit = require("../testkit")
local jecs = require("../lib/init")
local testkit = require("../testkit")
local __ = jecs.Wildcard
local ECS_ID, ECS_GENERATION = jecs.ECS_ID, jecs.ECS_GENERATION
local ECS_GENERATION_INC = jecs.ECS_GENERATION_INC
@ -11,329 +11,343 @@ local ECS_PAIR_OBJECT = jecs.ECS_PAIR_OBJECT
local TEST, CASE, CHECK, FINISH, SKIP = testkit.test()
local function CHECK_NO_ERR<T...>(s: string, fn: (T...) -> (), ...: T...)
local ok, err: string? = pcall(fn, ...)
local ok, err: string? = pcall(fn, ...)
if not CHECK(not ok, 2) then
local i = string.find(err :: string, " ")
assert(i)
local msg = string.sub(err :: string, i+1)
CHECK(msg == s, 2)
end
if not CHECK(not ok, 2) then
local i = string.find(err :: string, " ")
assert(i)
local msg = string.sub(err :: string, i + 1)
CHECK(msg == s, 2)
end
end
local N = 10
TEST("world", function()
do CASE "should be iterable"
local world = jecs.World.new()
local A = world:component()
local B = world:component()
local eA = world:entity()
world:set(eA, A, true)
local eB = world:entity()
world:set(eB, B, true)
local eAB = world:entity()
world:set(eAB, A, true)
world:set(eAB, B, true)
TEST("world", function()
do
CASE("should be iterable")
local world = jecs.World.new()
local A = world:component()
local B = world:component()
local eA = world:entity()
world:set(eA, A, true)
local eB = world:entity()
world:set(eB, B, true)
local eAB = world:entity()
world:set(eAB, A, true)
world:set(eAB, B, true)
local count = 0
for id, data in world do
count += 1
if id == eA then
CHECK(data[A] == true)
CHECK(data[B] == nil)
elseif id == eB then
CHECK(data[A] == nil)
CHECK(data[B] == true)
elseif id == eAB then
CHECK(data[A] == true)
CHECK(data[B] == true)
end
end
local count = 0
for id, data in world do
count += 1
if id == eA then
CHECK(data[A] == true)
CHECK(data[B] == nil)
elseif id == eB then
CHECK(data[A] == nil)
CHECK(data[B] == true)
elseif id == eAB then
CHECK(data[A] == true)
CHECK(data[B] == true)
end
end
-- components are registered in the entity index as well
-- so this test has to add 2 to account for them
CHECK(count == 3 + 2)
end
-- components are registered in the entity index as well
-- so this test has to add 2 to account for them
CHECK(count == 3 + 2)
end
do CASE "should query all matching entities"
local world = jecs.World.new()
local A = world:component()
local B = world:component()
do
CASE("should query all matching entities")
local world = jecs.World.new()
local A = world:component()
local B = world:component()
local entities = {}
for i = 1, N do
local id = world:entity()
local entities = {}
for i = 1, N do
local id = world:entity()
world:set(id, A, true)
if i > 5 then world:set(id, B, true) end
entities[i] = id
end
world:set(id, A, true)
if i > 5 then
world:set(id, B, true)
end
entities[i] = id
end
for id in world:query(A) do
table.remove(entities, CHECK(table.find(entities, id)))
end
for id in world:query(A) do
table.remove(entities, CHECK(table.find(entities, id)))
end
CHECK(#entities == 0)
CHECK(#entities == 0)
end
end
do
CASE("should query all matching entities when irrelevant component is removed")
local world = jecs.World.new()
local A = world:component()
local B = world:component()
local C = world:component()
do CASE "should query all matching entities when irrelevant component is removed"
local world = jecs.World.new()
local A = world:component()
local B = world:component()
local C = world:component()
local entities = {}
for i = 1, N do
local id = world:entity()
local entities = {}
for i = 1, N do
local id = world:entity()
-- specifically put them in disorder to track regression
-- https://github.com/Ukendio/jecs/pull/15
world:set(id, B, true)
world:set(id, A, true)
if i > 5 then
world:remove(id, B)
end
entities[i] = id
end
-- specifically put them in disorder to track regression
-- https://github.com/Ukendio/jecs/pull/15
world:set(id, B, true)
world:set(id, A, true)
if i > 5 then world:remove(id, B) end
entities[i] = id
end
local added = 0
for id in world:query(A) do
added += 1
table.remove(entities, CHECK(table.find(entities, id)))
end
local added = 0
for id in world:query(A) do
added += 1
table.remove(entities, CHECK(table.find(entities, id)))
end
CHECK(added == N)
end
CHECK(added == N)
end
do
CASE("should query all entities without B")
local world = jecs.World.new()
local A = world:component()
local B = world:component()
do CASE "should query all entities without B"
local world = jecs.World.new()
local A = world:component()
local B = world:component()
local entities = {}
for i = 1, N do
local id = world:entity()
local entities = {}
for i = 1, N do
local id = world:entity()
world:set(id, A, true)
if i < 5 then
entities[i] = id
else
world:set(id, B, true)
end
end
world:set(id, A, true)
if i < 5 then
entities[i] = id
else
world:set(id, B, true)
end
end
for id in world:query(A):without(B) do
table.remove(entities, CHECK(table.find(entities, id)))
end
for id in world:query(A):without(B) do
table.remove(entities, CHECK(table.find(entities, id)))
end
CHECK(#entities == 0)
end
CHECK(#entities == 0)
do
CASE("should allow setting components in arbitrary order")
local world = jecs.World.new()
end
local Health = world:entity()
local Poison = world:component()
do CASE "should allow setting components in arbitrary order"
local world = jecs.World.new()
local id = world:entity()
world:set(id, Poison, 5)
world:set(id, Health, 50)
local Health = world:entity()
local Poison = world:component()
CHECK(world:get(id, Poison) == 5)
end
local id = world:entity()
world:set(id, Poison, 5)
world:set(id, Health, 50)
do
CASE("should allow deleting components")
local world = jecs.World.new()
CHECK(world:get(id, Poison) == 5)
end
local Health = world:entity()
local Poison = world:component()
do CASE "should allow deleting components"
local world = jecs.World.new()
local id = world:entity()
world:set(id, Poison, 5)
world:set(id, Health, 50)
local id1 = world:entity()
world:set(id1, Poison, 500)
world:set(id1, Health, 50)
local Health = world:entity()
local Poison = world:component()
world:delete(id)
local id = world:entity()
world:set(id, Poison, 5)
world:set(id, Health, 50)
local id1 = world:entity()
world:set(id1, Poison, 500)
world:set(id1, Health, 50)
CHECK(world:get(id, Poison) == nil)
CHECK(world:get(id, Health) == nil)
CHECK(world:get(id1, Poison) == 500)
CHECK(world:get(id1, Health) == 50)
end
world:delete(id)
do
CASE("should allow remove that doesn't exist on entity")
local world = jecs.World.new()
CHECK(world:get(id, Poison) == nil)
CHECK(world:get(id, Health) == nil)
CHECK(world:get(id1, Poison) == 500)
CHECK(world:get(id1, Health) == 50)
local Health = world:entity()
local Poison = world:component()
end
local id = world:entity()
world:set(id, Health, 50)
world:remove(id, Poison)
do CASE "should allow remove that doesn't exist on entity"
local world = jecs.World.new()
CHECK(world:get(id, Poison) == nil)
CHECK(world:get(id, Health) == 50)
end
local Health = world:entity()
local Poison = world:component()
do
CASE("should increment generation")
local world = jecs.World.new()
local e = world:entity()
CHECK(ECS_ID(e) == 1 + jecs.Rest)
CHECK(getAlive(world.entityIndex, ECS_ID(e)) == e)
CHECK(ECS_GENERATION(e) == 0) -- 0
e = ECS_GENERATION_INC(e)
CHECK(ECS_GENERATION(e) == 1) -- 1
end
local id = world:entity()
world:set(id, Health, 50)
world:remove(id, Poison)
do
CASE("should get alive from index in the dense array")
local world = jecs.World.new()
local _e = world:entity()
local e2 = world:entity()
local e3 = world:entity()
CHECK(world:get(id, Poison) == nil)
CHECK(world:get(id, Health) == 50)
end
CHECK(IS_PAIR(world:entity()) == false)
do CASE "should increment generation"
local world = jecs.World.new()
local e = world:entity()
CHECK(ECS_ID(e) == 1 + jecs.Rest)
CHECK(getAlive(world.entityIndex, ECS_ID(e)) == e)
CHECK(ECS_GENERATION(e) == 0) -- 0
e = ECS_GENERATION_INC(e)
CHECK(ECS_GENERATION(e) == 1) -- 1
end
local pair = ECS_PAIR(e2, e3)
CHECK(IS_PAIR(pair) == true)
CHECK(ECS_PAIR_RELATION(world.entityIndex, pair) == e2)
CHECK(ECS_PAIR_OBJECT(world.entityIndex, pair) == e3)
end
do CASE "should get alive from index in the dense array"
local world = jecs.World.new()
local _e = world:entity()
local e2 = world:entity()
local e3 = world:entity()
do
CASE("should allow querying for relations")
local world = jecs.World.new()
local Eats = world:entity()
local Apples = world:entity()
local bob = world:entity()
CHECK(IS_PAIR(world:entity()) == false)
world:set(bob, ECS_PAIR(Eats, Apples), true)
for e, bool in world:query(ECS_PAIR(Eats, Apples)) do
CHECK(e == bob)
CHECK(bool)
end
end
local pair = ECS_PAIR(e2, e3)
CHECK(IS_PAIR(pair) == true)
CHECK(ECS_PAIR_RELATION(world.entityIndex, pair) == e2)
CHECK(ECS_PAIR_OBJECT(world.entityIndex, pair) == e3)
end
do
CASE("should allow wildcards in queries")
local world = jecs.World.new()
local Eats = world:entity()
local Apples = world:entity()
local bob = world:entity()
do CASE "should allow querying for relations"
local world = jecs.World.new()
local Eats = world:entity()
local Apples = world:entity()
local bob = world:entity()
world:set(bob, ECS_PAIR(Eats, Apples), true)
for e, bool in world:query(ECS_PAIR(Eats, Apples)) do
CHECK(e == bob)
CHECK(bool)
end
end
do CASE "should allow wildcards in queries"
local world = jecs.World.new()
local Eats = world:entity()
local Apples = world:entity()
local bob = world:entity()
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
local w = jecs.Wildcard
for e, data in world:query(ECS_PAIR(Eats, w)) do
CHECK(e == bob)
CHECK(data == "bob eats apples")
end
for e, data in world:query(ECS_PAIR(w, Apples)) do
CHECK(e == bob)
CHECK(data == "bob eats apples")
end
end
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
do CASE "should match against multiple pairs"
local world = jecs.World.new()
local Eats = world:entity()
local Apples = world:entity()
local Oranges =world:entity()
local bob = world:entity()
local alice = world:entity()
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
world:set(alice, ECS_PAIR(Eats, Oranges), "alice eats oranges")
local w = jecs.Wildcard
local count = 0
for e, data in world:query(ECS_PAIR(Eats, w)) do
count += 1
if e == bob then
CHECK(data == "bob eats apples")
else
CHECK(data == "alice eats oranges")
end
end
local w = jecs.Wildcard
for e, data in world:query(ECS_PAIR(Eats, w)) do
CHECK(e == bob)
CHECK(data == "bob eats apples")
end
for e, data in world:query(ECS_PAIR(w, Apples)) do
CHECK(e == bob)
CHECK(data == "bob eats apples")
end
end
CHECK(count == 2)
count = 0
do
CASE("should match against multiple pairs")
local world = jecs.World.new()
local Eats = world:entity()
local Apples = world:entity()
local Oranges = world:entity()
local bob = world:entity()
local alice = world:entity()
for e, data in world:query(ECS_PAIR(w, Apples)) do
count += 1
CHECK(data == "bob eats apples")
end
CHECK(count == 1)
end
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
world:set(alice, ECS_PAIR(Eats, Oranges), "alice eats oranges")
do CASE "should only relate alive entities"
local world = jecs.World.new()
local Eats = world:entity()
local Apples = world:entity()
local Oranges = world:entity()
local bob = world:entity()
local alice = world:entity()
world:set(bob, Apples, "apples")
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
world:set(alice, ECS_PAIR(Eats, Oranges), "alice eats oranges")
local w = jecs.Wildcard
local count = 0
for e, data in world:query(ECS_PAIR(Eats, w)) do
count += 1
if e == bob then
CHECK(data == "bob eats apples")
else
CHECK(data == "alice eats oranges")
end
end
world:delete(Apples)
local Wildcard = jecs.Wildcard
local count = 0
for _, data in world:query(ECS_PAIR(Wildcard, Apples)) do
count += 1
end
CHECK(count == 2)
count = 0
world:delete(ECS_PAIR(Eats, Apples))
CHECK(count == 0)
CHECK(world:get(bob, ECS_PAIR(Eats, Apples)) == nil)
end
for e, data in world:query(ECS_PAIR(w, Apples)) do
count += 1
CHECK(data == "bob eats apples")
end
CHECK(count == 1)
end
do CASE "should error when setting invalid pair"
local world = jecs.World.new()
local Eats = world:entity()
local Apples = world:entity()
local bob = world:entity()
do
CASE("should only relate alive entities")
world:delete(Apples)
local world = jecs.World.new()
local Eats = world:entity()
local Apples = world:entity()
local Oranges = world:entity()
local bob = world:entity()
local alice = world:entity()
CHECK_NO_ERR("Apples should be dead", function()
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
end)
end
world:set(bob, Apples, "apples")
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
world:set(alice, ECS_PAIR(Eats, Oranges), "alice eats oranges")
do CASE "should find target for ChildOf"
local world = jecs.World.new()
world:delete(Apples)
local Wildcard = jecs.Wildcard
local ChildOf = world:component()
local Name = world:component()
local count = 0
for _, data in world:query(ECS_PAIR(Wildcard, Apples)) do
count += 1
end
local function parent(entity)
return world:target(entity, ChildOf)
end
world:delete(ECS_PAIR(Eats, Apples))
local bob = world:entity()
local alice = world:entity()
local sara = world:entity()
world:add(bob, ECS_PAIR(ChildOf, alice))
world:set(bob, Name, "bob")
world:add(sara, ECS_PAIR(ChildOf, alice))
world:set(sara, Name, "sara")
CHECK(parent(bob) == alice) -- O(1)
CHECK(count == 0)
CHECK(world:get(bob, ECS_PAIR(Eats, Apples)) == nil)
end
local count = 0
for _, name in world:query(Name, ECS_PAIR(ChildOf, alice)) do
print(name)
count += 1
end
CHECK(count == 2)
end
do
CASE("should error when setting invalid pair")
local world = jecs.World.new()
local Eats = world:entity()
local Apples = world:entity()
local bob = world:entity()
world:delete(Apples)
world:set(bob, ECS_PAIR(Eats, Apples), "bob eats apples")
end
do
CASE("should find target for ChildOf")
local world = jecs.World.new()
local ChildOf = world:component()
local Name = world:component()
local function parent(entity)
return world:target(entity, ChildOf)
end
local bob = world:entity()
local alice = world:entity()
local sara = world:entity()
world:add(bob, ECS_PAIR(ChildOf, alice))
world:set(bob, Name, "bob")
world:add(sara, ECS_PAIR(ChildOf, alice))
world:set(sara, Name, "sara")
CHECK(parent(bob) == alice) -- O(1)
local count = 0
for _, name in world:query(Name, ECS_PAIR(ChildOf, alice)) do
print(name)
count += 1
end
CHECK(count == 2)
end
end)
FINISH()
FINISH()