refactor: less magic
This commit is contained in:
parent
9bae378ac7
commit
45cb158f2a
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1 +1,3 @@
|
|||
export default {};
|
||||
import Component from '@/ecs/component.js';
|
||||
|
||||
export default class Engine extends Component {}
|
||||
|
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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'},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
export default {};
|
||||
import Component from '@/ecs/component.js';
|
||||
|
||||
export default class MainEntity extends Component {}
|
||||
|
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export default {};
|
|
@ -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: {},
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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'),
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export default {};
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export default {};
|
|
@ -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'},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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]);
|
||||
|
|
Loading…
Reference in New Issue
Block a user