diff --git a/app/ecs-components/animation.js b/app/ecs-components/animation.js index 8655aec..e51eb74 100644 --- a/app/ecs-components/animation.js +++ b/app/ecs-components/animation.js @@ -1,3 +1,7 @@ -export default { - frame: {type: 'uint16'}, -}; +import Component from '@/ecs/component.js'; + +export default class Animation extends Component { + static properties = { + frame: {type: 'uint16'}, + }; +} diff --git a/app/ecs-components/area-size.js b/app/ecs-components/area-size.js index 6eedd1d..cb92e15 100644 --- a/app/ecs-components/area-size.js +++ b/app/ecs-components/area-size.js @@ -1,4 +1,8 @@ -export default { - x: {type: 'uint16'}, - y: {type: 'uint16'}, +import Component from '@/ecs/component.js'; + +export default class AreaSize extends Component { + static properties = { + x: {type: 'uint16'}, + y: {type: 'uint16'}, + }; } diff --git a/app/ecs-components/camera.js b/app/ecs-components/camera.js index 6eedd1d..26112ad 100644 --- a/app/ecs-components/camera.js +++ b/app/ecs-components/camera.js @@ -1,4 +1,8 @@ -export default { - x: {type: 'uint16'}, - y: {type: 'uint16'}, +import Component from '@/ecs/component.js'; + +export default class Camera extends Component { + static properties = { + x: {type: 'uint16'}, + y: {type: 'uint16'}, + }; } diff --git a/app/ecs-components/controlled.js b/app/ecs-components/controlled.js index 13e6f36..5dd8c2a 100644 --- a/app/ecs-components/controlled.js +++ b/app/ecs-components/controlled.js @@ -1,8 +1,12 @@ -export default { - locked: {type: 'uint8'}, - moveUp: {type: 'float32'}, - moveRight: {type: 'float32'}, - moveDown: {type: 'float32'}, - moveLeft: {type: 'float32'}, - changeSlot: {type: 'int8'}, -}; +import Component from '@/ecs/component.js'; + +export default class Controlled extends Component { + static properties = { + locked: {type: 'uint8'}, + moveUp: {type: 'float32'}, + moveRight: {type: 'float32'}, + moveDown: {type: 'float32'}, + moveLeft: {type: 'float32'}, + changeSlot: {type: 'int8'}, + }; +} diff --git a/app/ecs-components/direction.js b/app/ecs-components/direction.js index fc88cd8..ce84026 100644 --- a/app/ecs-components/direction.js +++ b/app/ecs-components/direction.js @@ -1,3 +1,7 @@ -export default { - direction: {type: 'uint8'}, -}; +import Component from '@/ecs/component.js'; + +export default class Direction extends Component { + static properties = { + direction: {type: 'uint8'}, + }; +} diff --git a/app/ecs-components/ecs.js b/app/ecs-components/ecs.js index a3ae9ea..4d07eae 100644 --- a/app/ecs-components/ecs.js +++ b/app/ecs-components/ecs.js @@ -1,3 +1,7 @@ -export default { - path: {type: 'string'}, +import Component from '@/ecs/component.js'; + +export default class Ecs extends Component { + static properties = { + path: {type: 'string'}, + }; } diff --git a/app/ecs-components/emitter.js b/app/ecs-components/emitter.js index 072fc2f..4acc681 100644 --- a/app/ecs-components/emitter.js +++ b/app/ecs-components/emitter.js @@ -1,31 +1,25 @@ -import Schema from '@/ecs/schema.js'; +import Component from '@/ecs/component.js'; -export default function(Component) { - return class Emitter extends Component { - mergeDiff(original, update) { - const merged = {}; - if (update.emit) { - merged.emit = { - ...original.emit, - ...update.emit, - } +export default class Emitter extends Component { + mergeDiff(original, update) { + const merged = {}; + if (update.emit) { + merged.emit = { + ...original.emit, + ...update.emit, } - return merged; } - instanceFromSchema() { - const Component = this; - const Instance = super.instanceFromSchema(); - return class EmitterInstance extends Instance { - emitting = []; - id = 0; - emit(specification) { - Component.markChange(this.entity, 'emit', {[this.id++]: specification}); - } - }; - } - static schema = new Schema({ - type: 'object', - properties: {}, - }); + return merged; } -} + instanceFromSchema() { + const Component = this; + const Instance = super.instanceFromSchema(); + return class EmitterInstance extends Instance { + emitting = []; + id = 0; + emit(specification) { + Component.markChange(this.entity, 'emit', {[this.id++]: specification}); + } + }; + } +} \ No newline at end of file diff --git a/app/ecs-components/engine.js b/app/ecs-components/engine.js index ff8b4c5..72c1225 100644 --- a/app/ecs-components/engine.js +++ b/app/ecs-components/engine.js @@ -1 +1,3 @@ -export default {}; +import Component from '@/ecs/component.js'; + +export default class Engine extends Component {} diff --git a/app/ecs-components/forces.js b/app/ecs-components/forces.js index 8412efb..d127585 100644 --- a/app/ecs-components/forces.js +++ b/app/ecs-components/forces.js @@ -1,6 +1,10 @@ -export default { - forceX: {type: 'float32'}, - forceY: {type: 'float32'}, - impulseX: {type: 'float32'}, - impulseY: {type: 'float32'}, +import Component from '@/ecs/component.js'; + +export default class Forces extends Component { + static properties = { + forceX: {type: 'float32'}, + forceY: {type: 'float32'}, + impulseX: {type: 'float32'}, + impulseY: {type: 'float32'}, + }; } diff --git a/app/ecs-components/health.js b/app/ecs-components/health.js index 7c7106b..01b860a 100644 --- a/app/ecs-components/health.js +++ b/app/ecs-components/health.js @@ -1,3 +1,7 @@ -export default { - health: {type: 'uint32'}, -}; +import Component from '@/ecs/component.js'; + +export default class Health extends Component { + static properties = { + health: {type: 'uint32'}, + }; +} diff --git a/app/ecs-components/index.js b/app/ecs-components/index.js index 2773236..91de02e 100644 --- a/app/ecs-components/index.js +++ b/app/ecs-components/index.js @@ -1,37 +1,14 @@ -import Component from '@/ecs/component.js'; -import Schema from '@/ecs/schema.js'; import gather from '@/util/gather.js'; -const specificationsAndOrDecorators = gather( +const Gathered = gather( import.meta.glob('./*.js', {eager: true, import: 'default'}), ); const Components = {}; -for (const componentName in specificationsAndOrDecorators) { - // TODO: byKey, byId, ... - if (Number.isInteger(+componentName)) { - continue; - } - const specificationOrDecorator = specificationsAndOrDecorators[componentName]; - if ('function' === typeof specificationOrDecorator) { - Components[componentName] = specificationOrDecorator( - class Decorated extends Component { - static componentName = componentName; - } - ); - if (!Components[componentName]) { - throw new Error(`Component ${componentName} decorator returned nothing`); - } - } - else { - Components[componentName] = class WrappedComponent extends Component { - static componentName = componentName; - static schema = new Schema({ - type: 'object', - properties: specificationOrDecorator, - }); - } - } +for (const componentName in Gathered) { + Components[componentName] = class Named extends Gathered[componentName] { + static componentName = componentName; + }; } export default Components; diff --git a/app/ecs-components/inventory.js b/app/ecs-components/inventory.js index 69ef133..52c0128 100644 --- a/app/ecs-components/inventory.js +++ b/app/ecs-components/inventory.js @@ -1,114 +1,109 @@ -import Schema from '@/ecs/schema.js'; +import Component from '@/ecs/component.js'; -export default function(Component) { - return class Inventory extends Component { - insertMany(entities) { - for (const [id, {slotChange}] of entities) { - if (slotChange) { - const {slots} = this.get(id); - for (const slotIndex in slotChange) { - if (false === slotChange[slotIndex]) { - delete slots[slotIndex]; - } - else { - slots[slotIndex] = { - ...slots[slotIndex], - ...slotChange[slotIndex], - }; - } +export default class Inventory extends Component { + insertMany(entities) { + for (const [id, {slotChange}] of entities) { + if (slotChange) { + const {slots} = this.get(id); + for (const slotIndex in slotChange) { + if (false === slotChange[slotIndex]) { + delete slots[slotIndex]; + } + else { + slots[slotIndex] = { + ...slots[slotIndex], + ...slotChange[slotIndex], + }; } } } - return super.insertMany(entities); } - mergeDiff(original, update) { - if (!update.slotChange) { - return super.mergeDiff(original, update); - } - const slotChange = { - ...original.slotChange, - }; - for (const index in update.slotChange) { - if (false === update.slotChange[index]) { - slotChange[index] = false; - } - else { - slotChange[index] = { - ...slotChange[index], - ...update.slotChange[index], - }; - } - } - return {slotChange}; + return super.insertMany(entities); + } + mergeDiff(original, update) { + if (!update.slotChange) { + return super.mergeDiff(original, update); } - instanceFromSchema() { - const Instance = super.instanceFromSchema(); - const Component = this; - Instance.prototype.item = async function (slot) { - const {slots} = this; - if (!(slot in slots)) { - return undefined; - } - const json = await ( - fetch([slots[slot].source, 'item.json'].join('/')) - .then((response) => (response.ok ? response.json() : {})) - ); - const item = { - ...slots[slot], - ...json, + const slotChange = { + ...original.slotChange, + }; + for (const index in update.slotChange) { + if (false === update.slotChange[index]) { + slotChange[index] = false; + } + else { + slotChange[index] = { + ...slotChange[index], + ...update.slotChange[index], }; - const instance = this; - const proxy = new Proxy(item, { - set(target, property, value) { - slots[slot][property] = value; - if ('qty' === property && value <= 0) { - Component.markChange(instance.entity, 'slotChange', {[slot]: false}); - delete slots[slot]; - } - else { - Component.markChange(instance.entity, 'slotChange', {[slot]: {[property]: value}}); - } - return true; - }, - }); - return proxy; - }; - Instance.prototype.swapSlots = function(l, r) { - const {slots} = this; - const tmp = slots[l]; - const change = {}; - if (slots[r]) { - change[l] = slots[l] = slots[r]; - } - else { - change[l] = false; - delete slots[l]; - } - if (tmp) { - change[r] = slots[r] = tmp; - } - else { - change[r] = false; - delete slots[r]; - } - Component.markChange(this.entity, 'slotChange', change); - }; - return Instance; + } } - static schema = new Schema({ - type: 'object', - properties: { - slots: { - type: 'map', - value: { - type: 'object', - properties: { - quantity: {type: 'uint16'}, - source: {type: 'string'}, - }, - }, + return {slotChange}; + } + instanceFromSchema() { + const Instance = super.instanceFromSchema(); + const Component = this; + Instance.prototype.item = async function (slot) { + const {slots} = this; + if (!(slot in slots)) { + return undefined; + } + const json = await ( + fetch([slots[slot].source, 'item.json'].join('/')) + .then((response) => (response.ok ? response.json() : {})) + ); + const item = { + ...slots[slot], + ...json, + }; + const instance = this; + const proxy = new Proxy(item, { + set(target, property, value) { + slots[slot][property] = value; + if ('qty' === property && value <= 0) { + Component.markChange(instance.entity, 'slotChange', {[slot]: false}); + delete slots[slot]; + } + else { + Component.markChange(instance.entity, 'slotChange', {[slot]: {[property]: value}}); + } + return true; + }, + }); + return proxy; + }; + Instance.prototype.swapSlots = function(l, r) { + const {slots} = this; + const tmp = slots[l]; + const change = {}; + if (slots[r]) { + change[l] = slots[l] = slots[r]; + } + else { + change[l] = false; + delete slots[l]; + } + if (tmp) { + change[r] = slots[r] = tmp; + } + else { + change[r] = false; + delete slots[r]; + } + Component.markChange(this.entity, 'slotChange', change); + }; + return Instance; + } + static properties = { + slots: { + type: 'map', + value: { + type: 'object', + properties: { + quantity: {type: 'uint16'}, + source: {type: 'string'}, }, }, - }); - } + }, + }; } diff --git a/app/ecs-components/main-entity.js b/app/ecs-components/main-entity.js index ff8b4c5..5af8249 100644 --- a/app/ecs-components/main-entity.js +++ b/app/ecs-components/main-entity.js @@ -1 +1,3 @@ -export default {}; +import Component from '@/ecs/component.js'; + +export default class MainEntity extends Component {} diff --git a/app/ecs-components/position.js b/app/ecs-components/position.js index c629f5f..9cf92eb 100644 --- a/app/ecs-components/position.js +++ b/app/ecs-components/position.js @@ -1,29 +1,24 @@ -import Schema from '@/ecs/schema.js'; +import Component from '@/ecs/component.js'; -export default function(Component) { - return class Wielder extends Component { - instanceFromSchema() { - const Instance = super.instanceFromSchema(); - const Component = this; - Object.defineProperty(Instance.prototype, 'tile', { - get: function () { - const {TileLayers} = Component.ecs.get(1); - const {Position: {x, y}} = Component.ecs.get(this.entity); - const {tileSize} = TileLayers.layers[0]; - return { - x: (x - (x % tileSize.x)) / tileSize.x, - y: (y - (y % tileSize.y)) / tileSize.y, - } - }, - }); - return Instance; - } - static schema = new Schema({ - type: 'object', - properties: { - x: {type: 'float32'}, - y: {type: 'float32'}, +export default class Position extends Component { + instanceFromSchema() { + const Instance = super.instanceFromSchema(); + const Component = this; + Object.defineProperty(Instance.prototype, 'tile', { + get: function () { + const {TileLayers} = Component.ecs.get(1); + const {Position: {x, y}} = Component.ecs.get(this.entity); + const {tileSize} = TileLayers.layers[0]; + return { + x: (x - (x % tileSize.x)) / tileSize.x, + y: (y - (y % tileSize.y)) / tileSize.y, + } }, }); + return Instance; } + static properties = { + x: {type: 'float32'}, + y: {type: 'float32'}, + }; } diff --git a/app/ecs-components/rendered.js b/app/ecs-components/rendered.js deleted file mode 100644 index ff8b4c5..0000000 --- a/app/ecs-components/rendered.js +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/app/ecs-components/sound.js b/app/ecs-components/sound.js index ed7b792..590bacc 100644 --- a/app/ecs-components/sound.js +++ b/app/ecs-components/sound.js @@ -1,29 +1,23 @@ -import Schema from '@/ecs/schema.js'; +import Component from '@/ecs/component.js'; -export default function(Component) { - return class Sound extends Component { - mergeDiff(original, update) { - const merged = {}; - if (update.play) { - merged.play = [ - ...(original.play ?? []), - ...update.play, - ]; +export default class Sound extends Component { + mergeDiff(original, update) { + const merged = {}; + if (update.play) { + merged.play = [ + ...(original.play ?? []), + ...update.play, + ]; + } + return merged; + } + instanceFromSchema() { + const Component = this; + const Instance = super.instanceFromSchema(); + return class SoundInstance extends Instance { + play(source) { + Component.markChange(this.entity, 'play', [source]); } - return merged; - } - instanceFromSchema() { - const Component = this; - const Instance = super.instanceFromSchema(); - return class SoundInstance extends Instance { - play(source) { - Component.markChange(this.entity, 'play', [source]); - } - }; - } - static schema = new Schema({ - type: 'object', - properties: {}, - }); + }; } } diff --git a/app/ecs-components/speed.js b/app/ecs-components/speed.js index 6c1ecbb..68703ee 100644 --- a/app/ecs-components/speed.js +++ b/app/ecs-components/speed.js @@ -1,3 +1,7 @@ -export default { - speed: {type: 'float32'}, -}; +import Component from '@/ecs/component.js'; + +export default class Speed extends Component { + static properties = { + speed: {type: 'float32'}, + }; +} diff --git a/app/ecs-components/sprite.js b/app/ecs-components/sprite.js index 27759e8..d6f1ee6 100644 --- a/app/ecs-components/sprite.js +++ b/app/ecs-components/sprite.js @@ -1,10 +1,15 @@ +import Component from '@/ecs/component.js'; + import vector2d from "./helpers/vector-2d"; -export default { - anchor: vector2d('float32', {x: 0.5, y: 0.5}), - animation: {type: 'string'}, - elapsed: {type: 'float32'}, - frame: {type: 'uint16'}, - frames: {type: 'uint16'}, - source: {type: 'string'}, - speed: {type: 'float32'}, -}; + +export default class Sprite extends Component { + static properties = { + anchor: vector2d('float32', {x: 0.5, y: 0.5}), + animation: {type: 'string'}, + elapsed: {type: 'float32'}, + frame: {type: 'uint16'}, + frames: {type: 'uint16'}, + source: {type: 'string'}, + speed: {type: 'float32'}, + }; +} diff --git a/app/ecs-components/ticking.js b/app/ecs-components/ticking.js index 76be97b..4cd9320 100644 --- a/app/ecs-components/ticking.js +++ b/app/ecs-components/ticking.js @@ -1,44 +1,39 @@ -import Schema from '@/ecs/schema.js'; +import Component from '@/ecs/component.js'; -export default function(Component) { - return class Ticking extends Component { - instanceFromSchema() { - const Instance = super.instanceFromSchema(); +export default class Ticking extends Component { + instanceFromSchema() { + const Instance = super.instanceFromSchema(); - Instance.prototype.$$finished = []; - Instance.prototype.$$tickingPromises = []; - Instance.prototype.addTickingPromise = function(tickingPromise) { - this.$$tickingPromises.push(tickingPromise); - tickingPromise.then(() => { - this.$$finished.push(tickingPromise); - }); - } - Instance.prototype.tick = function(elapsed) { - for (const tickingPromise of this.$$finished) { - this.$$tickingPromises.splice( - this.$$tickingPromises.indexOf(tickingPromise), - 1, - ); - } - this.$$finished = []; - for (const tickingPromise of this.$$tickingPromises) { - tickingPromise.tick(elapsed); - } - for (const tickingPromise of this.$$finished) { - this.$$tickingPromises.splice( - this.$$tickingPromises.indexOf(tickingPromise), - 1, - ); - } - this.$$finished = []; - } - return Instance; + Instance.prototype.$$finished = []; + Instance.prototype.$$tickingPromises = []; + Instance.prototype.addTickingPromise = function(tickingPromise) { + this.$$tickingPromises.push(tickingPromise); + tickingPromise.then(() => { + this.$$finished.push(tickingPromise); + }); } - static schema = new Schema({ - type: 'object', - properties: { - isTicking: {defaultValue: 1, type: 'uint8'}, - }, - }); + Instance.prototype.tick = function(elapsed) { + for (const tickingPromise of this.$$finished) { + this.$$tickingPromises.splice( + this.$$tickingPromises.indexOf(tickingPromise), + 1, + ); + } + this.$$finished = []; + for (const tickingPromise of this.$$tickingPromises) { + tickingPromise.tick(elapsed); + } + for (const tickingPromise of this.$$finished) { + this.$$tickingPromises.splice( + this.$$tickingPromises.indexOf(tickingPromise), + 1, + ); + } + this.$$finished = []; + } + return Instance; + } + static properties = { + isTicking: {defaultValue: 1, type: 'uint8'}, }; } diff --git a/app/ecs-components/tile-layers.js b/app/ecs-components/tile-layers.js index 0c639cf..c262de5 100644 --- a/app/ecs-components/tile-layers.js +++ b/app/ecs-components/tile-layers.js @@ -1,112 +1,107 @@ +import Component from '@/ecs/component.js'; + import vector2d from './helpers/vector-2d'; -import Schema from '@/ecs/schema.js'; - -export default function(Component) { - return class TileLayers extends Component { - insertMany(entities) { - for (const [id, {layerChange}] of entities) { - if (layerChange) { - const component = this.get(id); - const {layers} = component; - for (const layerIndex in layerChange) { - for (const calculated in layerChange[layerIndex]) { - const tile = layerChange[layerIndex][calculated]; - layers[layerIndex].data[calculated] = tile; - } - layers[layerIndex] = {...layers[layerIndex]}; +export default class TileLayers extends Component { + insertMany(entities) { + for (const [id, {layerChange}] of entities) { + if (layerChange) { + const component = this.get(id); + const {layers} = component; + for (const layerIndex in layerChange) { + for (const calculated in layerChange[layerIndex]) { + const tile = layerChange[layerIndex][calculated]; + layers[layerIndex].data[calculated] = tile; } + layers[layerIndex] = {...layers[layerIndex]}; } } - return super.insertMany(entities); } - mergeDiff(original, update) { - if (!update.layerChange) { - return super.mergeDiff(original, update); - } - const layerChange = { - ...original.layerChange, + return super.insertMany(entities); + } + mergeDiff(original, update) { + if (!update.layerChange) { + return super.mergeDiff(original, update); + } + const layerChange = { + ...original.layerChange, + }; + for (const index in update.layerChange) { + layerChange[index] = { + ...layerChange[index], + ...update.layerChange[index], }; - for (const index in update.layerChange) { - layerChange[index] = { - ...layerChange[index], - ...update.layerChange[index], - }; - } - return {layerChange}; } - instanceFromSchema() { - const Instance = super.instanceFromSchema(); - const Component = this; - Instance.prototype.layer = function (index) { - const {layers} = this; - if (!(index in layers)) { - return undefined; + return {layerChange}; + } + instanceFromSchema() { + const Instance = super.instanceFromSchema(); + const Component = this; + Instance.prototype.layer = function (index) { + const {layers} = this; + if (!(index in layers)) { + return undefined; + } + const instance = this; + class LayerProxy { + constructor(layer) { + this.layer = layer; } - const instance = this; - class LayerProxy { - constructor(layer) { - this.layer = layer; - } - get area() { - return this.layer.area; - } - get source() { - return this.layer.source; - } - stamp(at, data) { - const changes = {}; - for (const row in data) { - const columns = data[row]; - for (const column in columns) { - const tile = columns[column]; - const x = at.x + parseInt(column); - const y = at.y + parseInt(row); - if (x < 0 || y < 0 || x >= this.layer.area.x || y >= this.layer.area.y) { - continue; - } - const calculated = y * this.layer.area.x + x; - this.layer.data[calculated] = tile; - changes[calculated] = tile; + get area() { + return this.layer.area; + } + get source() { + return this.layer.source; + } + stamp(at, data) { + const changes = {}; + for (const row in data) { + const columns = data[row]; + for (const column in columns) { + const tile = columns[column]; + const x = at.x + parseInt(column); + const y = at.y + parseInt(row); + if (x < 0 || y < 0 || x >= this.layer.area.x || y >= this.layer.area.y) { + continue; } + const calculated = y * this.layer.area.x + x; + this.layer.data[calculated] = tile; + changes[calculated] = tile; } - Component.markChange(instance.entity, 'layerChange', {[index]: changes}); - } - tile({x, y}) { - if (x < 0 || y < 0 || x >= this.layer.area.x || y >= this.layer.area.y) { - return undefined; - } - return this.layer.data[y * this.layer.area.x + x]; - } - get tileSize() { - return this.layer.tileSize; } + Component.markChange(instance.entity, 'layerChange', {[index]: changes}); } - return new LayerProxy(layers[index]); - }; - return Instance; - } - static schema = new Schema({ - type: 'object', - properties: { - layers: { - type: 'array', - subtype: { - type: 'object', - properties: { - area: vector2d('float32'), - data: { - type: 'array', - subtype: { - type: 'uint16', - }, - }, - source: {type: 'string'}, - tileSize: vector2d('float32'), + tile({x, y}) { + if (x < 0 || y < 0 || x >= this.layer.area.x || y >= this.layer.area.y) { + return undefined; + } + return this.layer.data[y * this.layer.area.x + x]; + } + get tileSize() { + return this.layer.tileSize; + } + } + return new LayerProxy(layers[index]); + }; + return Instance; + } + static properties = { + layers: { + type: 'array', + subtype: { + type: 'object', + properties: { + area: vector2d('float32'), + data: { + type: 'array', + subtype: { + type: 'uint16', }, }, + source: {type: 'string'}, + tileSize: vector2d('float32'), }, }, - }); - } + }, + }; } diff --git a/app/ecs-components/transient.js b/app/ecs-components/transient.js deleted file mode 100644 index ff8b4c5..0000000 --- a/app/ecs-components/transient.js +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/app/ecs-components/visible-aabb.js b/app/ecs-components/visible-aabb.js index bfceaf1..dff834d 100644 --- a/app/ecs-components/visible-aabb.js +++ b/app/ecs-components/visible-aabb.js @@ -1,6 +1,10 @@ -export default { - x0: {type: 'float32'}, - x1: {type: 'float32'}, - y0: {type: 'float32'}, - y1: {type: 'float32'}, +import Component from '@/ecs/component.js'; + +export default class VisibleAabb extends Component { + static properties = { + x0: {type: 'float32'}, + x1: {type: 'float32'}, + y0: {type: 'float32'}, + y1: {type: 'float32'}, + }; } diff --git a/app/ecs-components/wandering.js b/app/ecs-components/wandering.js deleted file mode 100644 index ff8b4c5..0000000 --- a/app/ecs-components/wandering.js +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/app/ecs-components/wielder.js b/app/ecs-components/wielder.js index a4ded67..c867124 100644 --- a/app/ecs-components/wielder.js +++ b/app/ecs-components/wielder.js @@ -1,76 +1,71 @@ -import Schema from '@/ecs/schema.js'; +import Component from '@/ecs/component.js'; -export default function(Component) { - return class Wielder extends Component { - instanceFromSchema() { - const Instance = super.instanceFromSchema(); - const Component = this; - Instance.prototype.activeItem = async function () { - const {Inventory, Wielder} = Component.ecs.get(this.entity); - return Inventory.item(Wielder.activeSlot + 1); - }; - Instance.prototype.project = function(position, projection) { - const {TileLayers: {layers: [layer]}} = Component.ecs.get(1); - const {Direction: {direction}} = Component.ecs.get(this.entity); - let startX = position.x; - let startY = position.y; - switch (direction) { - case 0: - startX += projection.distance[1]; - startY -= projection.distance[0]; - break; - case 1: - startX += projection.distance[0]; - startY += projection.distance[1]; - break; - case 2: - startX -= projection.distance[1]; - startY += projection.distance[0]; - break; - case 3: - startX -= projection.distance[0]; - startY -= projection.distance[1]; - break; - } - const projected = []; - for (const row in projection.grid) { - const columns = projection.grid[row]; - for (const column in columns) { - const targeted = projection.grid[row][column]; - if (targeted) { - let axe; - switch (direction) { - case 0: - axe = [column, row]; - break; - case 1: - axe = [-row, column]; - break; - case 2: - axe = [-column, -row]; - break; - case 3: - axe = [row, -column]; - break; - } - const x = startX + parseInt(axe[0]); - const y = startY + parseInt(axe[1]); - if (x < 0 || y < 0 || x >= layer.area.x || y >= layer.area.y) { - continue; - } - projected.push({x, y}); +export default class Wielder extends Component { + instanceFromSchema() { + const Instance = super.instanceFromSchema(); + const Component = this; + Instance.prototype.activeItem = async function () { + const {Inventory, Wielder} = Component.ecs.get(this.entity); + return Inventory.item(Wielder.activeSlot + 1); + }; + Instance.prototype.project = function(position, projection) { + const {TileLayers: {layers: [layer]}} = Component.ecs.get(1); + const {Direction: {direction}} = Component.ecs.get(this.entity); + let startX = position.x; + let startY = position.y; + switch (direction) { + case 0: + startX += projection.distance[1]; + startY -= projection.distance[0]; + break; + case 1: + startX += projection.distance[0]; + startY += projection.distance[1]; + break; + case 2: + startX -= projection.distance[1]; + startY += projection.distance[0]; + break; + case 3: + startX -= projection.distance[0]; + startY -= projection.distance[1]; + break; + } + const projected = []; + for (const row in projection.grid) { + const columns = projection.grid[row]; + for (const column in columns) { + const targeted = projection.grid[row][column]; + if (targeted) { + let axe; + switch (direction) { + case 0: + axe = [column, row]; + break; + case 1: + axe = [-row, column]; + break; + case 2: + axe = [-column, -row]; + break; + case 3: + axe = [row, -column]; + break; } + const x = startX + parseInt(axe[0]); + const y = startY + parseInt(axe[1]); + if (x < 0 || y < 0 || x >= layer.area.x || y >= layer.area.y) { + continue; + } + projected.push({x, y}); } } - return projected; } - return Instance; + return projected; } - static schema = new Schema({ - type: 'object', - properties: { - activeSlot: {type: 'uint16'}, - }, - }); + return Instance; } + static properties = { + activeSlot: {type: 'uint16'}, + }; } diff --git a/app/ecs/component.js b/app/ecs/component.js index 16dfe86..769c422 100644 --- a/app/ecs/component.js +++ b/app/ecs/component.js @@ -7,12 +7,8 @@ export default class Component { Instance; map = {}; pool = []; - serializer; - - static schema = new Schema({ - type: 'object', - properties: {}, - }); + static properties = {}; + static $$schema; constructor(ecs) { this.ecs = ecs; @@ -41,7 +37,8 @@ export default class Component { createMany(entries) { if (entries.length > 0) { const allocated = this.allocateMany(entries.length); - const keys = Object.keys(this.constructor.properties); + const {properties} = this.constructor.schema.specification; + const keys = Object.keys(properties); for (let i = 0; i < entries.length; ++i) { const [entityId, values = {}] = entries[i]; this.map[entityId] = allocated[i]; @@ -51,7 +48,7 @@ export default class Component { } for (let k = 0; k < keys.length; ++k) { const j = keys[k]; - const {defaultValue} = this.constructor.properties[j]; + const {defaultValue} = properties[j]; if (j in values) { this.data[allocated[i]][j] = values[j]; } @@ -64,7 +61,7 @@ export default class Component { } deserialize(entityId, view, offset) { - const {properties} = this.constructor; + const {properties} = this.constructor.schema.specification; const instance = this.get(entityId); const deserialized = this.constructor.schema.deserialize(view, offset); for (const key in properties) { @@ -92,9 +89,10 @@ export default class Component { } static filterDefaults(instance) { + const {properties} = this.schema.specification; const json = {}; - for (const key in this.properties) { - const {defaultValue} = this.properties[key]; + for (const key in properties) { + const {defaultValue} = properties[key]; if (key in instance && instance[key] !== defaultValue) { json[key] = instance[key]; } @@ -131,15 +129,15 @@ export default class Component { instanceFromSchema() { const Component = this; + const {specification} = Component.constructor.schema; const Instance = class { $$entity = 0; constructor() { this.$$reset(); } $$reset() { - const {properties} = Component.constructor; - for (const key in properties) { - const {defaultValue} = properties[key]; + for (const key in specification.properties) { + const {defaultValue} = specification.properties[key]; this[`$$${key}`] = defaultValue; } } @@ -157,7 +155,7 @@ export default class Component { this.$$reset(); }, }; - for (const key in Component.constructor.properties) { + for (const key in specification.properties) { properties[key] = { get: function get() { return this[`$$${key}`]; @@ -182,8 +180,14 @@ export default class Component { return {...original, ...update}; } - static get properties() { - return this.schema.specification.properties; + static get schema() { + if (!this.$$schema) { + this.$$schema = new Schema({ + type: 'object', + properties: this.properties, + }); + } + return this.$$schema; } serialize(entityId, view, offset) { diff --git a/app/ecs/component.test.js b/app/ecs/component.test.js index 639f06d..cfd0a58 100644 --- a/app/ecs/component.test.js +++ b/app/ecs/component.test.js @@ -1,14 +1,12 @@ import {expect, test} from 'vitest'; -import Schema from './schema.js'; import Component from './component.js'; test('creates instances', () => { class CreatingComponent extends Component { - static schema = new Schema({ - type: 'object', - properties: {foo: {defaultValue: 'bar', type: 'string'}}, - }); + static properties = { + foo: {defaultValue: 'bar', type: 'string'}, + }; } const ComponentInstance = new CreatingComponent(); ComponentInstance.create(1); @@ -18,10 +16,9 @@ test('creates instances', () => { test('does not serialize default values', () => { class CreatingComponent extends Component { - static schema = new Schema({ - type: 'object', - properties: {foo: {defaultValue: 'bar', type: 'string'}, bar: {type: 'uint8'}}, - }); + static properties = { + foo: {defaultValue: 'bar', type: 'string'}, bar: {type: 'uint8'}, + }; } const fakeEcs = {markChange() {}}; const ComponentInstance = new CreatingComponent(fakeEcs); @@ -35,10 +32,9 @@ test('does not serialize default values', () => { test('reuses instances', () => { class ReusingComponent extends Component { - static schema = new Schema({ - type: 'object', - properties: {foo: {type: 'string'}}, - }); + static properties = { + foo: {type: 'string'}, + }; } const ComponentInstance = new ReusingComponent(); ComponentInstance.create(1); diff --git a/app/ecs/ecs.test.js b/app/ecs/ecs.test.js index 957f401..317cca2 100644 --- a/app/ecs/ecs.test.js +++ b/app/ecs/ecs.test.js @@ -2,26 +2,22 @@ import {expect, test} from 'vitest'; import Component from './component.js'; import Ecs from './ecs.js'; -import Schema from './schema.js'; import System from './system.js'; -function wrapSpecification(name, specification) { +function wrapProperties(name, properties) { return class WrappedComponent extends Component { static componentName = name; - static schema = new Schema({ - type: 'object', - properties: specification, - }); + static properties = properties; }; } -const Empty = wrapSpecification('Empty', {}); +const Empty = wrapProperties('Empty', {}); -const Name = wrapSpecification('Name', { +const Name = wrapProperties('Name', { name: {type: 'string'}, }); -const Position = wrapSpecification('Position', { +const Position = wrapProperties('Position', { x: {type: 'int32', defaultValue: 32}, y: {type: 'int32'}, z: {type: 'int32'}, @@ -117,7 +113,7 @@ test('inserts components into entities', () => { }); test('ticks systems', () => { - const Momentum = wrapSpecification('Momentum', { + const Momentum = wrapProperties('Momentum', { x: {type: 'int32'}, y: {type: 'int32'}, z: {type: 'int32'}, @@ -220,7 +216,7 @@ test('schedules entities to be deleted when ticking systems', () => { test('adds components to and remove components from entities when ticking systems', () => { let addLength, removeLength; const ecs = new Ecs({ - Components: {Foo: wrapSpecification('Foo', {bar: {type: 'uint8'}})}, + Components: {Foo: wrapProperties('Foo', {bar: {type: 'uint8'}})}, Systems: { AddComponent: class extends System { static queries() { diff --git a/app/ecs/query.test.js b/app/ecs/query.test.js index 9fe8975..0136fe8 100644 --- a/app/ecs/query.test.js +++ b/app/ecs/query.test.js @@ -2,21 +2,17 @@ import {expect, test} from 'vitest'; import Component from './component.js'; import Query from './query.js'; -import Schema from './schema.js'; -function wrapSpecification(name, specification) { +function wrapProperties(name, properties) { return class WrappedComponent extends Component { static name = name; - static schema = new Schema({ - type: 'object', - properties: specification, - }); + static properties = properties; }; } -const A = new (wrapSpecification('A', {a: {type: 'int32', defaultValue: 420}})); -const B = new (wrapSpecification('B', {b: {type: 'int32', defaultValue: 69}})); -const C = new (wrapSpecification('C', {c: {type: 'int32'}})); +const A = new (wrapProperties('A', {a: {type: 'int32', defaultValue: 420}})); +const B = new (wrapProperties('B', {b: {type: 'int32', defaultValue: 69}})); +const C = new (wrapProperties('C', {c: {type: 'int32'}})); const Components = {A, B, C}; Components.A.createMany([[2], [3]]); @@ -70,7 +66,7 @@ test('can deindex', () => { }); test('can reindex', () => { - const Test = new (wrapSpecification('Test', {a: {type: 'int32', defaultValue: 420}})); + const Test = new (wrapProperties('Test', {a: {type: 'int32', defaultValue: 420}})); Test.createMany([[2], [3]]); const query = new Query(['Test'], fakeEcs({Test})); query.reindex([2, 3]);