silphius/app/particles/emitter.js

94 lines
2.2 KiB
JavaScript
Raw Normal View History

2024-07-30 09:56:53 -05:00
import K from 'kefir';
export default class Emitter {
constructor(ecs) {
this.ecs = ecs;
this.scheduled = [];
}
async allocate({entity, shape}) {
const allocated = this.ecs.get(await this.ecs.create(entity));
if (shape) {
switch (shape.type) {
case 'filledRect': {
allocated.Position.x += Math.random() * shape.payload.width - (shape.payload.width / 2);
allocated.Position.y += Math.random() * shape.payload.height - (shape.payload.height / 2);
break;
}
}
}
return allocated;
}
emit(particle) {
particle = {
...particle,
entity: {
Position: {},
Sprite: {},
VisibleAabb: {},
...particle.entity,
},
}
let {count = 1} = particle;
const {frequency = 0} = particle;
const stream = K.stream((emitter) => {
if (0 === frequency) {
const promises = [];
for (let i = 0; i < count; ++i) {
promises.push(
this.allocate(particle)
.then((entity) => {
emitter.emit(entity);
}),
);
}
Promise.all(promises)
.then(() => {
emitter.end();
});
return;
}
const promise = this.allocate(particle)
.then((entity) => {
emitter.emit(entity);
});
count -= 1;
if (0 === count) {
promise.then(() => {
emitter.end();
});
return;
}
const promises = [promise];
let accumulated = 0;
const scheduled = (elapsed) => {
accumulated += elapsed;
while (accumulated > frequency && count > 0) {
promises.push(
this.allocate(particle)
.then((entity) => {
emitter.emit(entity);
}),
);
accumulated -= frequency;
count -= 1;
}
if (0 === count) {
this.scheduled.splice(this.scheduled.indexOf(scheduled), 1);
Promise.all(promises).then(() => {
emitter.end();
});
}
};
this.scheduled.push(scheduled);
});
return stream;
}
tick(elapsed) {
for (const ticker of this.scheduled) {
ticker(elapsed);
}
}
}