138 lines
4.5 KiB
JavaScript
138 lines
4.5 KiB
JavaScript
import { before, beforeEach, describe, it } from 'node:test'
|
|
import { addComponent, addEntity, createWorld, defineComponent, resetWorld, Types } from 'bitecs'
|
|
import assert from './assert.js'
|
|
import { query } from '../src/query.js'
|
|
|
|
let engine = {}
|
|
|
|
const create = (world, ...components) => {
|
|
const entity = addEntity(world)
|
|
components.forEach(cmp => addComponent(world, cmp, entity))
|
|
return entity
|
|
}
|
|
|
|
const relate = (world, from, type, ...b) => {
|
|
return b.map(to => {
|
|
const edge = addEntity(world)
|
|
addComponent(world, type, edge)
|
|
update(type, { from, to }, edge)
|
|
return edge
|
|
})
|
|
}
|
|
|
|
const update = (type, values, entity) => {
|
|
Object.entries(values).forEach(([k, v]) => { type[k][entity] = v })
|
|
}
|
|
|
|
describe('query', () => {
|
|
before(() => {
|
|
const world = { default: createWorld() }
|
|
const component = {
|
|
Player: defineComponent(null, 10),
|
|
NPC: defineComponent(null, 10),
|
|
Health: defineComponent({ current: Types.ui8, max: Types.ui8 }),
|
|
Knows: defineComponent({
|
|
from: Types.eid,
|
|
to: Types.eid
|
|
}, 10)
|
|
}
|
|
|
|
engine = { world, component }
|
|
})
|
|
|
|
beforeEach(() => {
|
|
Object.values(engine.world).forEach(world => resetWorld(world))
|
|
})
|
|
|
|
it('should query on single components', () => {
|
|
const world = engine.world.default
|
|
const { Player, NPC, Knows } = engine.component
|
|
|
|
const player = create(world, Player) // 0
|
|
const a = create(world, NPC) // 1
|
|
const b = create(world, NPC) // 2
|
|
const c = create(world, NPC) // 3
|
|
|
|
relate(world, player, Knows, a, c) // 4, 5
|
|
relate(world, a, Knows, player, b, c) // 6, 7, 8
|
|
relate(world, b, Knows, a) // 9
|
|
relate(world, c, Knows, player) // 10
|
|
|
|
// tag components should return an empty object
|
|
assert.deepEqual(
|
|
query('MATCH (player:Player) RETURN player', engine).unwrap(),
|
|
[{ player: {} }]
|
|
)
|
|
|
|
assert.deepEqual(
|
|
query('MATCH (player:Player)-[e1:Knows]->(a:NPC) RETURN player, e1, a', engine).unwrap(),
|
|
[
|
|
{ player: {}, e1: { from: 0, to: 1 }, a: {} },
|
|
{ player: {}, e1: { from: 0, to: 3 }, a: {} }
|
|
]
|
|
)
|
|
|
|
assert.deepEqual(
|
|
query('MATCH (player:Player)-[e1:Knows]->(a:NPC)-[e2:Knows]->(b:NPC) RETURN player, e1, a, e2, b', engine).unwrap(),
|
|
[
|
|
{ player: {}, e1: { from: 0, to: 1 }, a: {}, e2: { from: 1, to: 2 }, b: {} },
|
|
{ player: {}, e1: { from: 0, to: 1 }, a: {}, e2: { from: 1, to: 3 }, b: {} }
|
|
]
|
|
)
|
|
})
|
|
|
|
it('should match multiple components', () => {
|
|
const world = engine.world.default
|
|
const { Player, Health } = engine.component
|
|
const player = create(world, Player, Health)
|
|
|
|
Health.max[player] = 50
|
|
Health.current[player] = 25
|
|
|
|
// an unspecified component should return an Entity
|
|
assert.deepEqual(
|
|
query('MATCH (e, h:Health) RETURN e, h.max', engine).unwrap(),
|
|
[{ e: 11, h: { max: 50 } }]
|
|
)
|
|
|
|
assert.deepEqual(
|
|
query('MATCH (e:Entity, h:Health) RETURN e, h.max', engine).unwrap(),
|
|
[{ e: 11, h: { max: 50 } }]
|
|
)
|
|
})
|
|
|
|
it('should support filtering', () => {
|
|
const world = engine.world.default
|
|
const { Player, Health } = engine.component
|
|
const player = create(world, Player, Health) // 12
|
|
const enemy = create(world, Health) // 13
|
|
|
|
update(Health, { max: 50, current: 25 }, player)
|
|
update(Health, { max: 50, current: 35 }, enemy)
|
|
|
|
assert.deepEqual(
|
|
query('MATCH (e, h:Health) WHERE h.current < 30 RETURN e, h.max', engine).unwrap(),
|
|
[{ e: 12, h: { max: 50 } }]
|
|
)
|
|
|
|
assert.deepEqual(
|
|
query('MATCH (e, h:Health) WHERE e = 13 AND h.max = 50 OR h.current < 25 RETURN e, h.max', engine).unwrap(),
|
|
[{ e: 13, h: { max: 50 } }]
|
|
)
|
|
|
|
assert.deepEqual(
|
|
query('MATCH (e, h:Health) WHERE h.max = 50 OR h.current > 45 RETURN e, h.max AS maxHealth', engine).unwrap(),
|
|
[
|
|
{ e: 12, maxHealth: 50 },
|
|
{ e: 13, maxHealth: 50 }
|
|
]
|
|
)
|
|
assert.deepEqual(
|
|
query('MATCH (e, :Player, h:Health) WHERE (h.max = 50 OR h.current > 45) AND e = 12 RETURN e, h.max AS maxHealth', engine).unwrap(),
|
|
[
|
|
{ e: 12, maxHealth: 50 }
|
|
]
|
|
)
|
|
})
|
|
})
|
|
|