Merge pull request #27 from BlackMATov/dev

Dev
This commit is contained in:
2025-10-20 16:16:39 +07:00
committed by GitHub
10 changed files with 1768 additions and 788 deletions

118
README.md
View File

@@ -59,6 +59,7 @@
- [Chunk](#chunk)
- [Builder](#builder)
- [Changelog](#changelog)
- [v1.4.0](#v140)
- [v1.3.0](#v130)
- [v1.2.0](#v120)
- [v1.1.0](#v110)
@@ -153,11 +154,11 @@ function evolved.alive_any(...) end
Sometimes (for debugging purposes, for example), it is necessary to extract the index and version from an identifier or to pack them back into an identifier. The [`evolved.pack`](#evolvedpack) and [`evolved.unpack`](#evolvedunpack) functions can be used for this purpose.
```lua
---@param index integer
---@param version integer
---@param primary integer
---@param secondary integer
---@return evolved.id id
---@nodiscard
function evolved.pack(index, version) end
function evolved.pack(primary, secondary) end
---@param id evolved.id
---@return integer primary
@@ -461,7 +462,7 @@ local health, stamina = evolved.id(2)
local enemy = evolved.builder()
:set(health, 100)
:set(stamina, 50)
:spawn()
:build()
```
Builders can be reused, so you can create a builder with a specific set of fragments and components and then use it to spawn multiple entities with the same fragments and components.
@@ -502,16 +503,16 @@ local evolved = require 'evolved'
local health = evolved.builder()
:name('health')
:spawn()
:build()
local stamina = evolved.builder()
:name('stamina')
:spawn()
:build()
local player = evolved.builder()
:set(health, 100)
:set(stamina, 50)
:spawn()
:build()
for fragment, component in evolved.each(player) do
print(string.format('Fragment (%s) has value %d',
@@ -600,7 +601,7 @@ The builder interface can be used to create queries too. It is more convenient t
local query = evolved.builder()
:include(health, poisoned)
:exclude(resistant)
:spawn()
:build()
```
We don't have to set both [`evolved.INCLUDES`](#evolvedincludes) and [`evolved.EXCLUDES`](#evolvedexcludes) fragments, we can even do it without filters at all, then the query will match all chunks in the world.
@@ -657,7 +658,7 @@ local health, poisoned = evolved.id(2)
local player = evolved.builder()
:set(health, 100)
:set(poisoned, true)
:spawn()
:build()
-- start a deferred scope
evolved.defer()
@@ -685,7 +686,7 @@ local health, poisoned = evolved.id(2)
local player = evolved.builder()
:set(health, 100)
:set(poisoned, true)
:spawn()
:build()
-- start a deferred scope
evolved.defer()
@@ -733,7 +734,7 @@ local destroying_mark = evolved.id()
local destroying_mark_query = evolved.builder()
:include(destroying_mark)
:spawn()
:build()
-- destroy all entities with the destroying_mark fragment
evolved.batch_destroy(destroying_mark_query)
@@ -755,7 +756,7 @@ local health, max_health = evolved.id(2)
local query = evolved.builder()
:include(health, max_health)
:spawn()
:build()
local system = evolved.builder()
:query(query)
@@ -768,7 +769,7 @@ local system = evolved.builder()
health_components[i] + 1,
max_health_components[i])
end
end):spawn()
end):build()
```
The [`evolved.process`](#evolvedprocess) function is used to process systems. It takes systems as arguments and executes them in the order they were passed.
@@ -795,7 +796,7 @@ local system = evolved.builder()
health_components[i] - 1,
0)
end
end):spawn()
end):build()
evolved.process(system)
```
@@ -814,7 +815,7 @@ local velocity_x, velocity_y = evolved.id(2)
local physical_body_query = evolved.builder()
:include(position_x, position_y)
:include(velocity_x, velocity_y)
:spawn()
:build()
local physics_group = evolved.id()
@@ -829,7 +830,7 @@ evolved.builder()
vx[i] = vx[i] + gravity_x
vy[i] = vy[i] + gravity_y
end
end):spawn()
end):build()
evolved.builder()
:group(physics_group)
@@ -845,7 +846,7 @@ evolved.builder()
px[i] = px[i] + vx[i]
py[i] = py[i] + vy[i]
end
end):spawn()
end):build()
evolved.process(physics_group)
```
@@ -862,7 +863,7 @@ local system = evolved.builder()
:epilogue(function()
print('Epilogue')
end)
:spawn()
:build()
evolved.process(system)
```
@@ -904,7 +905,7 @@ local evolved = require 'evolved'
local health = evolved.builder()
:on_set(function(entity, fragment, component)
print('health set to ' .. component)
end):spawn()
end):build()
local player = evolved.id()
evolved.set(player, health, 100) -- prints "health set to 100"
@@ -929,7 +930,7 @@ local enemy_prefab = evolved.builder()
:prefab()
:set(health, 100)
:set(stamina, 50)
:spawn()
:build()
local enemy_clone = evolved.clone(enemy_prefab)
@@ -951,16 +952,16 @@ local evolved = require 'evolved'
local enemy_tag = evolved.builder()
:tag()
:spawn()
:build()
local only_enabled_enemies = evolved.builder()
:include(enemy_tag)
:spawn()
:build()
local all_enemies_including_disabled = evolved.builder()
:include(enemy_tag)
:include(evolved.DISABLED)
:spawn()
:build()
```
#### Shared Components
@@ -976,11 +977,11 @@ local position = evolved.id()
local enemy1 = evolved.builder()
:set(position, initial_position)
:spawn()
:build()
local enemy2 = evolved.builder()
:set(position, initial_position)
:spawn()
:build()
-- the enemy1 and enemy2 share the same table,
-- and that's definitely not what we want in this case
@@ -1005,15 +1006,15 @@ end
local position = evolved.builder()
:default(vector2(0, 0))
:duplicate(vector2_duplicate)
:spawn()
:build()
local enemy1 = evolved.builder()
:set(position)
:spawn()
:build()
local enemy2 = evolved.builder()
:set(position)
:spawn()
:build()
-- the enemy1 and enemy2 have different tables now
assert(evolved.get(enemy1, position) ~= evolved.get(enemy2, position))
@@ -1029,21 +1030,21 @@ local evolved = require 'evolved'
local position = evolved.builder()
:default(vector2(0, 0))
:duplicate(vector2_duplicate)
:spawn()
:build()
local velocity = evolved.builder()
:default(vector2(0, 0))
:duplicate(vector2_duplicate)
:spawn()
:build()
local physical = evolved.builder()
:tag()
:require(position, velocity)
:spawn()
:build()
local enemy = evolved.builder()
:set(physical)
:spawn()
:build()
assert(evolved.has_all(enemy, position, velocity))
```
@@ -1057,11 +1058,11 @@ local evolved = require 'evolved'
local world = evolved.builder()
:tag()
:spawn()
:build()
local entity = evolved.builder()
:set(world)
:spawn()
:build()
-- destroy the world fragment that is attached to the entity
evolved.destroy(world)
@@ -1082,11 +1083,11 @@ local evolved = require 'evolved'
local world = evolved.builder()
:tag()
:destruction_policy(evolved.DESTRUCTION_POLICY_DESTROY_ENTITY)
:spawn()
:build()
local entity = evolved.builder()
:set(world)
:spawn()
:build()
-- destroy the world fragment that is attached to the entity
evolved.destroy(world)
@@ -1181,10 +1182,10 @@ commit :: boolean
cancel :: boolean
spawn :: <fragment, component>? -> entity
multi_spawn :: integer, <fragment, component>? -> entity[]
multi_spawn :: integer, <fragment, component>? -> entity[], integer
clone :: entity, <fragment, component>? -> entity
multi_clone :: integer, entity, <fragment, component>? -> entity[]
multi_clone :: integer, entity, <fragment, component>? -> entity[], integer
alive :: entity -> boolean
alive_all :: entity... -> boolean
@@ -1245,11 +1246,14 @@ chunk_mt:components :: fragment... -> storage...
```
builder :: builder
builder_mt:build :: entity? -> entity
builder_mt:multi_build :: integer, entity? -> entity[], integer
builder_mt:spawn :: entity
builder_mt:multi_spawn :: integer -> entity[]
builder_mt:multi_spawn :: integer -> entity[], integer
builder_mt:clone :: entity -> entity
builder_mt:multi_clone :: integer, entity -> entity[]
builder_mt:multi_clone :: integer, entity -> entity[], integer
builder_mt:has :: fragment -> boolean
builder_mt:has_all :: fragment... -> boolean
@@ -1296,6 +1300,12 @@ builder_mt:destruction_policy :: id -> builder
## Changelog
### v1.4.0
- Improved query execution performance by caching some internal calculations
- Added the universal [`builder.build`](#evolvedbuilder_mtbuild) and [`builder.multi_build`](#evolvedbuilder_mtmulti_build) methods that can be used to spawn or clone entities depending on the method arguments
- [`evolved.ON_REMOVE`](#evolvedon_remove) hooks are now invoked before the fragment is actually removed from the entity
### v1.3.0
- Added the new [`evolved.cancel`](#evolvedcancel) function
@@ -1400,11 +1410,11 @@ function evolved.name(...) end
### `evolved.pack`
```lua
---@param index integer
---@param version integer
---@param primary integer
---@param secondary integer
---@return evolved.id id
---@nodiscard
function evolved.pack(index, version) end
function evolved.pack(primary, secondary) end
```
### `evolved.unpack`
@@ -1452,6 +1462,7 @@ function evolved.spawn(components) end
---@param entity_count integer
---@param components? table<evolved.fragment, evolved.component>
---@return evolved.entity[] entity_list
---@return integer entity_count
function evolved.multi_spawn(entity_count, components) end
```
@@ -1471,6 +1482,7 @@ function evolved.clone(prefab, components) end
---@param prefab evolved.entity
---@param components? table<evolved.fragment, evolved.component>
---@return evolved.entity[] entity_list
---@return integer entity_count
function evolved.multi_clone(entity_count, prefab, components) end
```
@@ -1776,6 +1788,24 @@ function evolved.chunk_mt:components(...) end
function evolved.builder() end
```
#### `evolved.builder_mt:build`
```lua
---@param prefab? evolved.entity
---@return evolved.entity entity
function evolved.builder_mt:build(prefab) end
```
### `evolved.builder_mt:multi_build`
```lua
---@param entity_count integer
---@param prefab? evolved.entity
---@return evolved.entity[] entity_list
---@return integer entity_count
function evolved.builder_mt:multi_build(entity_count, prefab) end
```
#### `evolved.builder_mt:spawn`
```lua
@@ -1788,6 +1818,7 @@ function evolved.builder_mt:spawn() end
```lua
---@param entity_count integer
---@return evolved.entity[] entity_list
---@return integer entity_count
function evolved.builder_mt:multi_spawn(entity_count) end
```
@@ -1805,6 +1836,7 @@ function evolved.builder_mt:clone(prefab) end
---@param entity_count integer
---@param prefab evolved.entity
---@return evolved.entity[] entity_list
---@return integer entity_count
function evolved.builder_mt:multi_clone(entity_count, prefab) end
```

View File

@@ -2,7 +2,6 @@
## Backlog
- Queries can cache major chunks to avoid finding them every time.
- observers and events
- add INDEX fragment trait
- use compact prefix-tree for chunks
@@ -11,8 +10,9 @@
## Thoughts
- We can return deferred status from modifying operations and spawn/clone methods.
- Should we make one builder:build method instead of :spawn and :clone?
- We should have a way to not copy components on deferred spawn/clone.
## Known Issues
- Required fragments are slower than they should be
- Errors in hooks are cannot be handled properly right now

View File

@@ -1,7 +1,9 @@
require 'develop.samples.systems'
require 'develop.testing.build_tests'
require 'develop.testing.cancel_tests'
require 'develop.testing.locate_tests'
require 'develop.testing.main_tests'
require 'develop.testing.multi_spawn_tests'
require 'develop.testing.name_tests'
require 'develop.testing.requires_fragment_tests'
@@ -14,13 +16,13 @@ require 'develop.benchmarks.process_bmarks'
require 'develop.benchmarks.spawn_bmarks'
require 'develop.benchmarks.table_bmarks'
require 'develop.untests'
local basics = require 'develop.basics'
print '----------------------------------------'
basics.describe_fuzz 'develop.fuzzing.destroy_fuzz'
print '----------------------------------------'
basics.describe_fuzz 'develop.fuzzing.execute_fuzz'
print '----------------------------------------'
basics.describe_fuzz 'develop.fuzzing.batch_destroy_fuzz'
print '----------------------------------------'
basics.describe_fuzz 'develop.fuzzing.explicit_fuzz'

View File

@@ -0,0 +1,267 @@
local evo = require 'evolved'
evo.debug_mode(true)
---
---
---
---
---
local __table_unpack = (function()
---@diagnostic disable-next-line: deprecated
return table.unpack or unpack
end)()
---
---
---
---
---
local all_fragment_list = {} ---@type evolved.fragment[]
for i = 1, math.random(1, 10) do
local fragment = evo.id()
all_fragment_list[i] = fragment
end
---@param query evolved.query
local function generate_query(query)
local include_set = {}
local include_list = {}
local include_count = 0
for _ = 1, math.random(0, #all_fragment_list) do
local include = all_fragment_list[math.random(1, #all_fragment_list)]
if not include_set[include] then
include_count = include_count + 1
include_set[include] = include_count
include_list[include_count] = include
end
end
local exclude_set = {}
local exclude_list = {}
local exclude_count = 0
for _ = 1, math.random(0, #all_fragment_list) do
local exclude = all_fragment_list[math.random(1, #all_fragment_list)]
if not exclude_set[exclude] then
exclude_count = exclude_count + 1
exclude_set[exclude] = exclude_count
exclude_list[exclude_count] = exclude
end
end
if include_count > 0 then
evo.set(query, evo.INCLUDES, include_list)
end
if exclude_count > 0 then
evo.set(query, evo.EXCLUDES, exclude_list)
end
end
---@param query_count integer
---@return evolved.query[] query_list
---@return integer query_count
---@nodiscard
local function generate_queries(query_count)
local query_list = {} ---@type evolved.query[]
for i = 1, query_count do
local query = evo.id()
query_list[i] = query
generate_query(query)
end
return query_list, query_count
end
---@param entity evolved.entity
local function generate_entity(entity)
for _ = 0, math.random(0, #all_fragment_list) do
local fragment = all_fragment_list[math.random(1, #all_fragment_list)]
evo.set(entity, fragment)
end
end
---@param entity_count integer
---@return evolved.entity[] entity_list
---@return integer entity_count
local function generate_entities(entity_count)
local entity_list = {} ---@type evolved.entity[]
for i = 1, entity_count do
local entity = evo.id()
entity_list[i] = entity
generate_entity(entity)
end
return entity_list, entity_count
end
local pre_query_list, pre_query_count = generate_queries(math.random(1, 10))
local pre_entity_list, pre_entity_count = generate_entities(math.random(1, 10))
for _ = 1, math.random(1, 5) do
local fragment = all_fragment_list[math.random(1, #all_fragment_list)]
evo.set(fragment, evo.EXPLICIT)
end
for _ = 1, math.random(1, 5) do
local query = pre_query_list[math.random(1, pre_query_count)]
if math.random(1, 2) == 1 then
generate_query(query)
else
if math.random(1, 2) == 1 then
evo.remove(query, evo.INCLUDES)
else
evo.remove(query, evo.EXCLUDES)
end
end
end
local post_query_list, post_query_count = generate_queries(math.random(1, 10))
local post_entity_list, post_entity_count = generate_entities(math.random(1, 10))
---
---
---
---
---
local all_query_list = {}
local all_query_count = 0
local all_entity_list = {}
local all_entity_count = 0
for i = 1, pre_query_count do
all_query_count = all_query_count + 1
all_query_list[all_query_count] = pre_query_list[i]
end
for i = 1, post_query_count do
all_query_count = all_query_count + 1
all_query_list[all_query_count] = post_query_list[i]
end
for i = 1, pre_entity_count do
all_entity_count = all_entity_count + 1
all_entity_list[all_entity_count] = pre_entity_list[i]
end
for i = 1, post_entity_count do
all_entity_count = all_entity_count + 1
all_entity_list[all_entity_count] = post_entity_list[i]
end
---
---
---
---
---
local function execute_query(query)
local query_chunk_set = {}
local query_entity_set = {}
local query_include_list = evo.get(query, evo.INCLUDES) or {}
local query_exclude_list = evo.get(query, evo.EXCLUDES) or {}
local query_include_set = {}
for _, include in ipairs(query_include_list) do
query_include_set[include] = true
end
for chunk, entity_list, entity_count in evo.execute(query) do
assert(not query_chunk_set[chunk])
query_chunk_set[chunk] = true
for i = 1, entity_count do
local entity = entity_list[i]
assert(not query_entity_set[entity])
query_entity_set[entity] = true
end
assert(chunk:has_all(__table_unpack(query_include_list)))
assert(not chunk:has_any(__table_unpack(query_exclude_list)))
end
for i = 1, all_entity_count do
local entity = all_entity_list[i]
local is_entity_matched =
evo.has_all(entity, __table_unpack(query_include_list))
and not evo.has_any(entity, __table_unpack(query_exclude_list))
for fragment in evo.each(entity) do
if evo.has(fragment, evo.EXPLICIT) and not query_include_set[fragment] then
is_entity_matched = false
end
end
if is_entity_matched then
assert(query_entity_set[entity])
else
assert(not query_entity_set[entity])
end
end
end
for i = 1, all_query_count do
execute_query(all_query_list[i])
end
---
---
---
---
---
for _ = 1, math.random(1, 5) do
local fragment = all_fragment_list[math.random(1, #all_fragment_list)]
evo.set(fragment, evo.EXPLICIT)
end
for _ = 1, math.random(1, 5) do
local query = pre_query_list[math.random(1, pre_query_count)]
if math.random(1, 2) == 1 then
generate_query(query)
else
if math.random(1, 2) == 1 then
evo.remove(query, evo.INCLUDES)
else
evo.remove(query, evo.EXCLUDES)
end
end
end
for i = 1, all_query_count do
execute_query(all_query_list[i])
end
---
---
---
---
---
if math.random(1, 2) == 1 then
evo.collect_garbage()
end
evo.destroy(__table_unpack(all_query_list))
evo.destroy(__table_unpack(all_entity_list))
evo.destroy(__table_unpack(all_fragment_list))
if math.random(1, 2) == 1 then
evo.collect_garbage()
end

View File

@@ -0,0 +1,41 @@
local evo = require 'evolved'
do
local f1, f2 = evo.id(2)
do
local e = evo.builder():set(f1, 42):set(f2, 'hello'):build()
assert(evo.has(e, f1) and evo.get(e, f1) == 42)
assert(evo.has(e, f2) and evo.get(e, f2) == 'hello')
end
do
local p = evo.builder():set(f1, 42):build()
local e = evo.builder():set(f2, 'hello'):build(p)
assert(evo.has(e, f1) and evo.get(e, f1) == 42)
assert(evo.has(e, f2) and evo.get(e, f2) == 'hello')
end
do
local entity_list, entity_count = evo.builder():set(f1, 42):set(f2, 'hello'):multi_build(5)
assert(entity_count == 5)
for i = 1, entity_count do
local e = entity_list[i]
assert(evo.has(e, f1) and evo.get(e, f1) == 42)
assert(evo.has(e, f2) and evo.get(e, f2) == 'hello')
end
end
do
local p = evo.builder():set(f1, 42):build()
local entity_list, entity_count = evo.builder():set(f2, 'hello'):multi_build(5, p)
assert(entity_count == 5)
for i = 1, entity_count do
local e = entity_list[i]
assert(evo.has(e, f1) and evo.get(e, f1) == 42)
assert(evo.has(e, f2) and evo.get(e, f2) == 'hello')
end
end
end

View File

@@ -2530,7 +2530,7 @@ do
end)
:on_remove(function(e, f, c)
f2_remove_count = f2_remove_count + 1
assert(evo.get(e, f) == nil)
assert(evo.get(e, f) == c)
assert(evo.alive(f))
assert(c == 82)
end)
@@ -3321,7 +3321,7 @@ do
remove_count = remove_count + 1
assert(f == f1)
assert(c == 51)
assert(evo.get(e, f1) == nil)
assert(evo.get(e, f1) == c)
do
evo.remove(e, f2)
@@ -3348,7 +3348,7 @@ do
remove_count = remove_count + 1
assert(f == f2)
assert(c == 51)
assert(evo.get(e, f2) == nil)
assert(evo.get(e, f2) == c)
end)
end
@@ -3518,8 +3518,7 @@ do
assert(e == e1 or e == e2)
assert(f == f1)
assert(c == 51)
assert(evo.get(e1, f1) == nil)
assert(evo.get(e2, f1) == nil)
assert(evo.get(e, f) == c)
end)
do
@@ -6449,3 +6448,650 @@ do
assert(b:has_all() and not b:has_any())
assert(b:has(ff) and b:has(ft) and b:has_all(ff, ft) and b:has_any(ff, ft))
end
do
do
local f = evo.id()
local q = evo.builder():include(f):spawn()
local e = evo.builder():set(f, 42):spawn()
local iter, state = evo.execute(q)
local chunk, entity_list, entity_count = iter(state)
assert(chunk and entity_list and entity_count)
assert(chunk == evo.chunk(f) and entity_count == 1 and entity_list[1] == e)
end
do
local f = evo.id()
local e = evo.builder():set(f, 42):spawn()
local q = evo.builder():include(f):spawn()
local iter, state = evo.execute(q)
local chunk, entity_list, entity_count = iter(state)
assert(chunk and entity_list and entity_count)
assert(chunk == evo.chunk(f) and entity_count == 1 and entity_list[1] == e)
end
do
local f1, f2 = evo.id(2)
local q = evo.builder():exclude(f2):spawn()
local e = evo.builder():set(f1, 42):spawn()
local e_count = 0
for chunk, entity_list, entity_count in evo.execute(q) do
for i = 1, entity_count do
if entity_list[i] == e then
e_count = e_count + 1
assert(chunk == evo.chunk(f1))
assert(chunk:components(f1)[1] == 42)
end
end
end
assert(e_count == 1)
end
do
local f1, f2, f3 = evo.id(3)
local q = evo.builder():exclude(f2):spawn()
local e1 = evo.builder():set(f1, 42):spawn()
local e2 = evo.builder():set(f1, 42):set(f3, 21):spawn()
local e1_count, e2_count = 0, 0
for chunk, entity_list, entity_count in evo.execute(q) do
for i = 1, entity_count do
if entity_list[i] == e1 then
e1_count = e1_count + 1
assert(chunk == evo.chunk(f1))
assert(chunk:components(f1)[1] == 42)
end
if entity_list[i] == e2 then
e2_count = e2_count + 1
assert(chunk == evo.chunk(f1, f3))
assert(chunk:components(f1)[1] == 42)
assert(chunk:components(f3)[1] == 21)
end
end
end
assert(e1_count == 1)
assert(e2_count == 1)
end
do
local f1, f2 = evo.id(2)
local e = evo.builder():set(f1, 42):spawn()
local q = evo.builder():exclude(f2):spawn()
local e_count = 0
for chunk, entity_list, entity_count in evo.execute(q) do
for i = 1, entity_count do
if entity_list[i] == e then
e_count = e_count + 1
assert(chunk == evo.chunk(f1))
assert(chunk:components(f1)[1] == 42)
end
end
end
assert(e_count == 1)
end
do
local f1, f2, f3 = evo.id(3)
local e1 = evo.builder():set(f1, 42):spawn()
local e2 = evo.builder():set(f1, 42):set(f3, 21):spawn()
local q = evo.builder():exclude(f2):spawn()
local e1_count, e2_count = 0, 0
for chunk, entity_list, entity_count in evo.execute(q) do
for i = 1, entity_count do
if entity_list[i] == e1 then
e1_count = e1_count + 1
assert(chunk == evo.chunk(f1))
assert(chunk:components(f1)[1] == 42)
end
if entity_list[i] == e2 then
e2_count = e2_count + 1
assert(chunk == evo.chunk(f1, f3))
assert(chunk:components(f1)[1] == 42)
assert(chunk:components(f3)[1] == 21)
end
end
end
assert(e1_count == 1)
assert(e2_count == 1)
end
end
do
local f1, f2 = evo.id(2)
local q1 = evo.builder():include(f1):spawn()
local q2 = evo.builder():include(f2):spawn()
local e12 = evo.builder():set(f1, 42):set(f2, 21):spawn()
do
local iter, state = evo.execute(q1)
local chunk, entity_list, entity_count = iter(state)
assert(chunk and entity_list and entity_count)
assert(chunk == evo.chunk(f1, f2) and entity_count == 1 and entity_list[1] == e12)
end
do
local iter, state = evo.execute(q2)
local chunk, entity_list, entity_count = iter(state)
assert(chunk and entity_list and entity_count)
assert(chunk == evo.chunk(f1, f2) and entity_count == 1 and entity_list[1] == e12)
end
evo.set(f1, evo.EXPLICIT)
do
local iter, state = evo.execute(q1)
local chunk, entity_list, entity_count = iter(state)
assert(chunk and entity_list and entity_count)
assert(chunk == evo.chunk(f1, f2) and entity_count == 1 and entity_list[1] == e12)
end
do
local iter, state = evo.execute(q2)
local chunk, entity_list, entity_count = iter(state)
assert(not chunk and not entity_list and not entity_count)
end
end
do
local function v2(x, y) return { x = x or 0, y = y or 0 } end
local function v2_clone(v) return { x = v.x, y = v.y } end
local f1, f2 = evo.id(2)
local f1_default = v2(1, 2)
local f2_default = v2(3, 4)
evo.set(f1, evo.DEFAULT, f1_default)
evo.set(f2, evo.DEFAULT, f2_default)
evo.set(f1, evo.DUPLICATE, v2_clone)
evo.set(f2, evo.DUPLICATE, v2_clone)
local e1 = evo.builder():set(f1, v2(10, 11)):spawn()
local e2 = evo.builder():set(f1, v2(12, 13)):set(f2, v2(14, 15)):spawn()
do
assert(evo.has(e1, f1) and evo.get(e1, f1).x == 10 and evo.get(e1, f1).y == 11)
assert(evo.get(e1, f1) ~= f1_default)
assert(evo.has(e2, f1) and evo.get(e2, f1).x == 12 and evo.get(e2, f1).y == 13)
assert(evo.get(e2, f1) ~= f1_default)
assert(evo.has(e2, f2) and evo.get(e2, f2).x == 14 and evo.get(e2, f2).y == 15)
assert(evo.get(e2, f2) ~= f2_default)
end
evo.set(f1, evo.TAG)
do
assert(evo.has(e1, f1) and evo.get(e1, f1) == nil)
assert(evo.has(e2, f1) and evo.get(e2, f1) == nil)
assert(evo.has(e2, f2) and evo.get(e2, f2).x == 14 and evo.get(e2, f2).y == 15)
assert(evo.get(e2, f2) ~= f2_default)
end
evo.remove(f1, evo.TAG)
do
assert(evo.has(e1, f1) and evo.get(e1, f1).x == 1 and evo.get(e1, f1).y == 2)
assert(evo.get(e1, f1) ~= f1_default)
assert(evo.has(e2, f1) and evo.get(e2, f1).x == 1 and evo.get(e2, f1).y == 2)
assert(evo.get(e2, f1) ~= f1_default)
assert(evo.has(e2, f2) and evo.get(e2, f2).x == 14 and evo.get(e2, f2).y == 15)
assert(evo.get(e2, f2) ~= f2_default)
end
end
do
local f1, f2, f3 = evo.id(3)
evo.set(f1, evo.REQUIRES, { f2, f3 })
evo.set(f2, evo.DEFAULT, false)
evo.set(f3, evo.TAG)
local v_set_sum = 0
local v_insert_sum = 0
local f3_set_times = 0
local f3_insert_times = 0
evo.set(f1, evo.ON_SET, function(e, f, v)
assert(f == f1)
v_set_sum = v_set_sum + v
assert(evo.get(e, f) == v)
end)
evo.set(f1, evo.ON_INSERT, function(e, f, v)
assert(f == f1)
v_insert_sum = v_insert_sum + v
assert(evo.get(e, f) == v)
end)
evo.set(f3, evo.ON_SET, function(e, f, v)
assert(f == f3)
f3_set_times = f3_set_times + 1
assert(v == nil)
assert(evo.has(e, f))
end)
evo.set(f3, evo.ON_INSERT, function(e, f, v)
assert(f == f3)
f3_insert_times = f3_insert_times + 1
assert(v == nil)
assert(evo.has(e, f))
end)
do
local e = evo.spawn({ [f1] = 42 })
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) == 42)
assert(evo.has(e, f2) and evo.get(e, f2) == false)
end
do
local entity_prefab = evo.builder():set(f1, 42):spawn()
local e = evo.clone(entity_prefab)
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) == 42)
assert(evo.has(e, f2) and evo.get(e, f2) == false)
end
do
local entity_prefab = evo.builder():set(f1, 42):spawn()
evo.remove(entity_prefab, f2, f3)
local e = evo.clone(entity_prefab, { [f1] = 21 })
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) == 21)
assert(evo.has(e, f2) and evo.get(e, f2) == false)
end
assert(v_set_sum == 42 * 4 + 21 * 1)
assert(v_insert_sum == 42 * 4 + 21 * 1)
assert(f3_set_times == 5)
assert(f3_insert_times == 5)
end
do
local function v2(x, y) return { x = x or 0, y = y or 0 } end
local function v2_clone(v) return { x = v.x, y = v.y } end
local f1, f2, f3, f4, f5, f6, f7 = evo.id(7)
evo.set(f1, evo.REQUIRES, { f2, f3, f4 })
evo.set(f5, evo.REQUIRES, { f6, f7 })
local f1_default = v2(1, 2)
local f2_default = v2(3, 4)
local f3_default = v2(10, 11)
local f4_default = v2(12, 13)
local f6_default = v2(14, 15)
local f7_default = v2(16, 17)
evo.set(f1, evo.DEFAULT, f1_default)
evo.set(f2, evo.DEFAULT, f2_default)
evo.set(f3, evo.DEFAULT, f3_default)
evo.set(f4, evo.DEFAULT, f4_default)
evo.set(f6, evo.DEFAULT, f6_default)
evo.set(f7, evo.DEFAULT, f7_default)
evo.set(f1, evo.DUPLICATE, v2_clone)
evo.set(f2, evo.DUPLICATE, v2_clone)
evo.set(f3, evo.DUPLICATE, v2_clone)
evo.set(f5, evo.DUPLICATE, v2_clone)
evo.set(f6, evo.DUPLICATE, v2_clone)
do
local entity_list, entity_count = evo.multi_spawn(2, { [f1] = v2(5, 6), [f2] = v2(7, 8) })
assert(entity_list and #entity_list == 2)
assert(entity_count == 2)
local q_f1 = evo.builder():include(f1):spawn()
local f2_v = v2(20, 21)
evo.batch_set(q_f1, f2, f2_v)
for i = 1, entity_count do
local e = entity_list[i]
assert(evo.has(e, f1) and evo.get(e, f1).x == 5 and evo.get(e, f1).y == 6)
assert(evo.has(e, f2) and evo.get(e, f2).x == 20 and evo.get(e, f2).y == 21)
assert(evo.has(e, f3) and evo.get(e, f3).x == 10 and evo.get(e, f3).y == 11)
assert(evo.has(e, f4) and evo.get(e, f4).x == 12 and evo.get(e, f4).y == 13)
assert(evo.get(e, f1) ~= f1_default)
assert(evo.get(e, f2) ~= f2_v and evo.get(e, f2) ~= f2_default)
assert(evo.get(e, f3) ~= f3_default)
assert(evo.get(e, f4) == f4_default)
end
local f5_v = v2(30, 31)
evo.batch_set(q_f1, f5, f5_v)
for i = 1, entity_count do
local e = entity_list[i]
assert(evo.has(e, f1) and evo.get(e, f1).x == 5 and evo.get(e, f1).y == 6)
assert(evo.has(e, f2) and evo.get(e, f2).x == 20 and evo.get(e, f2).y == 21)
assert(evo.has(e, f3) and evo.get(e, f3).x == 10 and evo.get(e, f3).y == 11)
assert(evo.has(e, f4) and evo.get(e, f4).x == 12 and evo.get(e, f4).y == 13)
assert(evo.has(e, f5) and evo.get(e, f5).x == 30 and evo.get(e, f5).y == 31)
assert(evo.has(e, f6) and evo.get(e, f6).x == 14 and evo.get(e, f6).y == 15)
assert(evo.has(e, f7) and evo.get(e, f7).x == 16 and evo.get(e, f7).y == 17)
assert(evo.get(e, f1) ~= f1_default)
assert(evo.get(e, f2) ~= f2_v and evo.get(e, f2) ~= f2_default)
assert(evo.get(e, f3) ~= f3_default)
assert(evo.get(e, f4) == f4_default)
assert(evo.get(e, f5) ~= f5_v)
assert(evo.get(e, f6) ~= f6_default)
assert(evo.get(e, f7) == f7_default)
end
f5_v = v2(32, 33)
evo.batch_set(q_f1, f5, f5_v)
for i = 1, entity_count do
local e = entity_list[i]
assert(evo.has(e, f1) and evo.get(e, f1).x == 5 and evo.get(e, f1).y == 6)
assert(evo.has(e, f2) and evo.get(e, f2).x == 20 and evo.get(e, f2).y == 21)
assert(evo.has(e, f3) and evo.get(e, f3).x == 10 and evo.get(e, f3).y == 11)
assert(evo.has(e, f4) and evo.get(e, f4).x == 12 and evo.get(e, f4).y == 13)
assert(evo.has(e, f5) and evo.get(e, f5).x == 32 and evo.get(e, f5).y == 33)
assert(evo.has(e, f6) and evo.get(e, f6).x == 14 and evo.get(e, f6).y == 15)
assert(evo.has(e, f7) and evo.get(e, f7).x == 16 and evo.get(e, f7).y == 17)
assert(evo.get(e, f1) ~= f1_default)
assert(evo.get(e, f2) ~= f2_v and evo.get(e, f2) ~= f2_default)
assert(evo.get(e, f3) ~= f3_default)
assert(evo.get(e, f4) == f4_default)
assert(evo.get(e, f5) ~= f5_v)
assert(evo.get(e, f6) ~= f6_default)
assert(evo.get(e, f7) == f7_default)
end
end
end
do
local function v2(x, y) return { x = x or 0, y = y or 0 } end
local function v2_clone(v) return { x = v.x, y = v.y } end
local f1, f2, f3, f4, f5, f6, f7 = evo.id(7)
evo.set(f1, evo.REQUIRES, { f2, f3, f4 })
evo.set(f5, evo.REQUIRES, { f6, f7 })
local f1_default = v2(1, 2)
local f2_default = v2(3, 4)
local f3_default = v2(10, 11)
local f4_default = v2(12, 13)
local f6_default = v2(14, 15)
local f7_default = v2(16, 17)
evo.set(f1, evo.DEFAULT, f1_default)
evo.set(f2, evo.DEFAULT, f2_default)
evo.set(f3, evo.DEFAULT, f3_default)
evo.set(f4, evo.DEFAULT, f4_default)
evo.set(f6, evo.DEFAULT, f6_default)
evo.set(f7, evo.DEFAULT, f7_default)
evo.set(f1, evo.DUPLICATE, v2_clone)
evo.set(f2, evo.DUPLICATE, v2_clone)
evo.set(f3, evo.DUPLICATE, v2_clone)
evo.set(f5, evo.DUPLICATE, v2_clone)
evo.set(f6, evo.DUPLICATE, v2_clone)
local f5_set_sum = 0
local f5_insert_sum = 0
local f6_set_sum = 0
local f6_insert_sum = 0
local f7_set_sum = 0
local f7_insert_sum = 0
evo.set(f5, evo.ON_SET, function(e, f, v)
assert(evo.get(e, f) == v)
assert(f == f5)
f5_set_sum = f5_set_sum + v.x + v.y
end)
evo.set(f5, evo.ON_INSERT, function(e, f, v)
assert(evo.get(e, f) == v)
assert(f == f5)
f5_insert_sum = f5_insert_sum + v.x + v.y
end)
evo.set(f6, evo.ON_SET, function(e, f, v)
assert(evo.get(e, f) == v)
assert(f == f6)
f6_set_sum = f6_set_sum + v.x + v.y
end)
evo.set(f6, evo.ON_INSERT, function(e, f, v)
assert(evo.get(e, f) == v)
assert(f == f6)
f6_insert_sum = f6_insert_sum + v.x + v.y
end)
evo.set(f7, evo.ON_SET, function(e, f, v)
assert(evo.get(e, f) == v)
assert(f == f7)
f7_set_sum = f7_set_sum + v.x + v.y
end)
evo.set(f7, evo.ON_INSERT, function(e, f, v)
assert(evo.get(e, f) == v)
assert(f == f7)
f7_insert_sum = f7_insert_sum + v.x + v.y
end)
do
local entity_list, entity_count = evo.multi_spawn(2, { [f1] = v2(5, 6), [f2] = v2(7, 8) })
assert(entity_list and #entity_list == 2)
assert(entity_count == 2)
local q_f1 = evo.builder():include(f1):spawn()
local f2_v = v2(20, 21)
evo.batch_set(q_f1, f2, f2_v)
for i = 1, entity_count do
local e = entity_list[i]
assert(evo.has(e, f1) and evo.get(e, f1).x == 5 and evo.get(e, f1).y == 6)
assert(evo.has(e, f2) and evo.get(e, f2).x == 20 and evo.get(e, f2).y == 21)
assert(evo.has(e, f3) and evo.get(e, f3).x == 10 and evo.get(e, f3).y == 11)
assert(evo.has(e, f4) and evo.get(e, f4).x == 12 and evo.get(e, f4).y == 13)
assert(evo.get(e, f1) ~= f1_default)
assert(evo.get(e, f2) ~= f2_v and evo.get(e, f2) ~= f2_default)
assert(evo.get(e, f3) ~= f3_default)
assert(evo.get(e, f4) == f4_default)
end
assert(f5_set_sum == 0)
assert(f5_insert_sum == 0)
assert(f6_set_sum == 0)
assert(f6_insert_sum == 0)
assert(f7_set_sum == 0)
assert(f7_insert_sum == 0)
local f5_v = v2(30, 31)
evo.batch_set(q_f1, f5, f5_v)
assert(f5_set_sum == (30 + 31) * entity_count)
assert(f5_insert_sum == (30 + 31) * entity_count)
assert(f6_set_sum == (14 + 15) * entity_count)
assert(f6_insert_sum == (14 + 15) * entity_count)
assert(f7_set_sum == (16 + 17) * entity_count)
assert(f7_insert_sum == (16 + 17) * entity_count)
for i = 1, entity_count do
local e = entity_list[i]
assert(evo.has(e, f1) and evo.get(e, f1).x == 5 and evo.get(e, f1).y == 6)
assert(evo.has(e, f2) and evo.get(e, f2).x == 20 and evo.get(e, f2).y == 21)
assert(evo.has(e, f3) and evo.get(e, f3).x == 10 and evo.get(e, f3).y == 11)
assert(evo.has(e, f4) and evo.get(e, f4).x == 12 and evo.get(e, f4).y == 13)
assert(evo.has(e, f5) and evo.get(e, f5).x == 30 and evo.get(e, f5).y == 31)
assert(evo.has(e, f6) and evo.get(e, f6).x == 14 and evo.get(e, f6).y == 15)
assert(evo.has(e, f7) and evo.get(e, f7).x == 16 and evo.get(e, f7).y == 17)
assert(evo.get(e, f1) ~= f1_default)
assert(evo.get(e, f2) ~= f2_v and evo.get(e, f2) ~= f2_default)
assert(evo.get(e, f3) ~= f3_default)
assert(evo.get(e, f4) == f4_default)
assert(evo.get(e, f5) ~= f5_v)
assert(evo.get(e, f6) ~= f6_default)
assert(evo.get(e, f7) == f7_default)
end
f5_set_sum = 0
f5_insert_sum = 0
f6_set_sum = 0
f6_insert_sum = 0
f7_set_sum = 0
f7_insert_sum = 0
f5_v = v2(32, 33)
evo.batch_set(q_f1, f5, f5_v)
assert(f5_set_sum == (32 + 33) * entity_count)
assert(f5_insert_sum == 0)
assert(f6_set_sum == 0)
assert(f6_insert_sum == 0)
assert(f7_set_sum == 0)
assert(f7_insert_sum == 0)
for i = 1, entity_count do
local e = entity_list[i]
assert(evo.has(e, f1) and evo.get(e, f1).x == 5 and evo.get(e, f1).y == 6)
assert(evo.has(e, f2) and evo.get(e, f2).x == 20 and evo.get(e, f2).y == 21)
assert(evo.has(e, f3) and evo.get(e, f3).x == 10 and evo.get(e, f3).y == 11)
assert(evo.has(e, f4) and evo.get(e, f4).x == 12 and evo.get(e, f4).y == 13)
assert(evo.has(e, f5) and evo.get(e, f5).x == 32 and evo.get(e, f5).y == 33)
assert(evo.has(e, f6) and evo.get(e, f6).x == 14 and evo.get(e, f6).y == 15)
assert(evo.has(e, f7) and evo.get(e, f7).x == 16 and evo.get(e, f7).y == 17)
assert(evo.get(e, f1) ~= f1_default)
assert(evo.get(e, f2) ~= f2_v and evo.get(e, f2) ~= f2_default)
assert(evo.get(e, f3) ~= f3_default)
assert(evo.get(e, f4) == f4_default)
assert(evo.get(e, f5) ~= f5_v)
assert(evo.get(e, f6) ~= f6_default)
assert(evo.get(e, f7) == f7_default)
end
end
end
do
local s1, s2 = evo.id(2)
local process_order = ''
evo.set(s1, evo.PROLOGUE, function()
process_order = process_order .. '1'
end)
evo.set(s2, evo.PROLOGUE, function()
process_order = process_order .. '2'
end)
do
process_order = ''
evo.set(s2, evo.GROUP, s1)
evo.process(s1)
assert(process_order == '12')
end
do
process_order = ''
evo.remove(s2, evo.GROUP)
evo.process(s1)
assert(process_order == '1')
end
end
do
local s1, s2, s3 = evo.id(3)
local process_order = ''
evo.set(s1, evo.PROLOGUE, function()
process_order = process_order .. '1'
end)
evo.set(s2, evo.PROLOGUE, function()
process_order = process_order .. '2'
end)
evo.set(s3, evo.PROLOGUE, function()
process_order = process_order .. '3'
end)
do
process_order = ''
evo.set(s2, evo.GROUP, s1)
evo.process(s1)
assert(process_order == '12')
end
do
process_order = ''
evo.set(s2, evo.GROUP, s3)
evo.process(s1)
assert(process_order == '1')
evo.process(s3)
assert(process_order == '132')
end
end
do
local f1, f2 = evo.id(2)
evo.set(f1, evo.NAME, 'f1')
evo.set(f2, evo.NAME, 'f2')
do
local c1 = evo.chunk(f1)
assert(tostring(c1) == '<f1>')
local c2 = evo.chunk(f2)
assert(tostring(c2) == '<f2>')
local c12 = evo.chunk(f1, f2)
assert(tostring(c12) == '<f1, f2>')
local c21 = evo.chunk(f2, f1)
assert(tostring(c21) == '<f1, f2>')
end
do
local b = evo.builder():set(f1)
assert(tostring(b) == '<f1>')
b:set(f1, 1):set(f2, 2)
assert(tostring(b) == '<f1, f2>')
end
end

View File

@@ -306,3 +306,302 @@ do
end
end
end
do
local f1, f2, f3 = evo.id(3)
evo.set(f1, evo.REQUIRES, { f2, f3 })
evo.set(f3, evo.TAG)
do
local entity_list, entity_count = evo.multi_spawn(2, { [f1] = 42 })
assert(entity_list and #entity_list == 2)
assert(entity_count == 2)
for i = 1, entity_count do
local e = entity_list[i]
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) == 42)
assert(evo.has(e, f2) and evo.get(e, f2) == true)
assert(evo.has(e, f3) and evo.get(e, f3) == nil)
end
end
do
local entity_prefab = evo.builder():set(f1, 42):spawn()
local clone_list, clone_count = evo.multi_clone(2, entity_prefab)
assert(clone_list and #clone_list == 2)
assert(clone_count == 2)
for i = 1, clone_count do
local e = clone_list[i]
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) == 42)
assert(evo.has(e, f2) and evo.get(e, f2) == true)
assert(evo.has(e, f3) and evo.get(e, f3) == nil)
end
end
do
local entity_prefab = evo.builder():set(f1, 42):spawn()
evo.remove(entity_prefab, f2, f3)
local clone_list, clone_count = evo.multi_clone(2, entity_prefab, { [f1] = 21 })
assert(clone_list and #clone_list == 2)
assert(clone_count == 2)
for i = 1, clone_count do
local e = clone_list[i]
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) == 21)
assert(evo.has(e, f2) and evo.get(e, f2) == true)
assert(evo.has(e, f3) and evo.get(e, f3) == nil)
end
end
evo.set(f2, evo.DEFAULT, false)
do
local entity_list, entity_count = evo.multi_spawn(2, { [f1] = 42 })
assert(entity_list and #entity_list == 2)
assert(entity_count == 2)
for i = 1, entity_count do
local e = entity_list[i]
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) == 42)
assert(evo.has(e, f2) and evo.get(e, f2) == false)
assert(evo.has(e, f3) and evo.get(e, f3) == nil)
end
end
do
local entity_prefab = evo.builder():set(f1, 42):spawn()
local clone_list, clone_count = evo.multi_clone(2, entity_prefab)
assert(clone_list and #clone_list == 2)
assert(clone_count == 2)
for i = 1, clone_count do
local e = clone_list[i]
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) == 42)
assert(evo.has(e, f2) and evo.get(e, f2) == false)
assert(evo.has(e, f3) and evo.get(e, f3) == nil)
end
end
do
local entity_prefab = evo.builder():set(f1, 42):spawn()
evo.remove(entity_prefab, f2, f3)
local clone_list, clone_count = evo.multi_clone(2, entity_prefab, { [f1] = 21 })
assert(clone_list and #clone_list == 2)
assert(clone_count == 2)
for i = 1, clone_count do
local e = clone_list[i]
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) == 21)
assert(evo.has(e, f2) and evo.get(e, f2) == false)
assert(evo.has(e, f3) and evo.get(e, f3) == nil)
end
end
local v_set_sum = 0
local v_insert_sum = 0
local f3_set_times = 0
local f3_insert_times = 0
evo.set(f1, evo.ON_SET, function(e, f, v)
assert(f == f1)
v_set_sum = v_set_sum + v
assert(evo.get(e, f) == v)
end)
evo.set(f1, evo.ON_INSERT, function(e, f, v)
assert(f == f1)
v_insert_sum = v_insert_sum + v
assert(evo.get(e, f) == v)
end)
evo.set(f3, evo.ON_SET, function(e, f, v)
assert(f == f3)
f3_set_times = f3_set_times + 1
assert(v == nil)
assert(evo.has(e, f))
end)
evo.set(f3, evo.ON_INSERT, function(e, f, v)
assert(f == f3)
f3_insert_times = f3_insert_times + 1
assert(v == nil)
assert(evo.has(e, f))
end)
do
local entity_list, entity_count = evo.multi_spawn(2, { [f1] = 42 })
assert(entity_list and #entity_list == 2)
assert(entity_count == 2)
for i = 1, entity_count do
local e = entity_list[i]
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) == 42)
assert(evo.has(e, f2) and evo.get(e, f2) == false)
end
end
do
local entity_prefab = evo.builder():set(f1, 42):spawn()
local clone_list, clone_count = evo.multi_clone(2, entity_prefab)
assert(clone_list and #clone_list == 2)
assert(clone_count == 2)
for i = 1, clone_count do
local e = clone_list[i]
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) == 42)
assert(evo.has(e, f2) and evo.get(e, f2) == false)
end
end
do
local entity_prefab = evo.builder():set(f1, 42):spawn()
evo.remove(entity_prefab, f2, f3)
local clone_list, clone_count = evo.multi_clone(2, entity_prefab, { [f1] = 21 })
assert(clone_list and #clone_list == 2)
assert(clone_count == 2)
for i = 1, clone_count do
local e = clone_list[i]
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) == 21)
assert(evo.has(e, f2) and evo.get(e, f2) == false)
end
end
assert(v_set_sum == 42 * 6 + 21 * 2)
assert(v_insert_sum == 42 * 6 + 21 * 2)
assert(f3_set_times == 8)
assert(f3_insert_times == 8)
end
do
local function v2(x, y) return { x = x or 0, y = y or 0 } end
local function v2_clone(v) return { x = v.x, y = v.y } end
local f1, f2, f3, f4 = evo.id(4)
evo.set(f1, evo.REQUIRES, { f2, f3, f4 })
local f1_default = v2(1, 2)
local f2_default = v2(3, 4)
local f3_default = v2(10, 11)
local f4_default = v2(12, 13)
evo.set(f1, evo.DEFAULT, f1_default)
evo.set(f2, evo.DEFAULT, f2_default)
evo.set(f3, evo.DEFAULT, f3_default)
evo.set(f4, evo.DEFAULT, f4_default)
evo.set(f1, evo.DUPLICATE, v2_clone)
evo.set(f2, evo.DUPLICATE, v2_clone)
evo.set(f3, evo.DUPLICATE, v2_clone)
do
local entity_list, entity_count = evo.multi_spawn(2, { [f1] = v2(5, 6), [f2] = v2(7, 8) })
assert(entity_list and #entity_list == 2)
assert(entity_count == 2)
for i = 1, entity_count do
local e = entity_list[i]
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) ~= f1_default)
assert(evo.get(e, f1).x == 5 and evo.get(e, f1).y == 6)
assert(evo.has(e, f2) and evo.get(e, f2) ~= f2_default)
assert(evo.get(e, f2).x == 7 and evo.get(e, f2).y == 8)
assert(evo.has(e, f3) and evo.get(e, f3) ~= f3_default)
assert(evo.get(e, f3).x == 10 and evo.get(e, f3).y == 11)
assert(evo.has(e, f4) and evo.get(e, f4) == f4_default)
end
end
do
local entity_prefab = evo.builder():set(f1, v2(5, 6)):set(f2, v2(7, 8)):spawn()
local clone_list, clone_count = evo.multi_clone(2, entity_prefab, { [f2] = f2_default })
assert(clone_list and #clone_list == 2)
assert(clone_count == 2)
for i = 1, clone_count do
local e = clone_list[i]
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) ~= f1_default and evo.get(e, f1) ~= evo.get(entity_prefab, f1))
assert(evo.get(e, f1).x == 5 and evo.get(e, f1).y == 6)
assert(evo.has(e, f2) and evo.get(e, f2) ~= f2_default and evo.get(e, f2) ~= evo.get(entity_prefab, f2))
assert(evo.get(e, f2).x == 3 and evo.get(e, f2).y == 4)
assert(evo.has(e, f3) and evo.get(e, f3) ~= f3_default)
assert(evo.get(e, f3).x == 10 and evo.get(e, f3).y == 11)
assert(evo.has(e, f4) and evo.get(e, f4) == f4_default)
end
end
do
local entity_prefab = evo.builder():set(f1, v2(5, 6)):set(f2, v2(7, 8)):spawn()
evo.remove(entity_prefab, f2, f3, f4)
local clone_list, clone_count = evo.multi_clone(2, entity_prefab, { [f2] = f2_default })
assert(clone_list and #clone_list == 2)
assert(clone_count == 2)
for i = 1, clone_count do
local e = clone_list[i]
assert(e and not evo.empty(e))
assert(evo.has(e, f1) and evo.get(e, f1) ~= f1_default and evo.get(e, f1) ~= evo.get(entity_prefab, f1))
assert(evo.get(e, f1).x == 5 and evo.get(e, f1).y == 6)
assert(evo.has(e, f2) and evo.get(e, f2) ~= f2_default and evo.get(e, f2) ~= evo.get(entity_prefab, f2))
assert(evo.get(e, f2).x == 3 and evo.get(e, f2).y == 4)
assert(evo.has(e, f3) and evo.get(e, f3) ~= f3_default)
assert(evo.get(e, f3).x == 10 and evo.get(e, f3).y == 11)
assert(evo.has(e, f4) and evo.get(e, f4) == f4_default)
end
end
end

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
rockspec_format = "3.0"
package = "evolved.lua"
version = "1.4.0-0"
source = {
url = "git://github.com/BlackMATov/evolved.lua",
tag = "v1.4.0",
}
description = {
homepage = "https://github.com/BlackMATov/evolved.lua",
summary = "Evolved ECS (Entity-Component-System) for Lua",
detailed = [[
`evolved.lua` is a fast and flexible ECS (Entity-Component-System) library for Lua.
It is designed to be simple and easy to use, while providing all the features needed to create complex systems with blazing performance.
]],
license = "MIT",
labels = {
"ecs",
"entity",
"entities",
"component",
"components",
"entity-component",
"entity-component-system",
},
}
dependencies = {
"lua >= 5.1",
}
build = {
type = "builtin",
modules = {
evolved = "evolved.lua",
}
}