Finish rule system; add burn and sink; particle effects on kill, sink

This commit is contained in:
Logan Hunt 2022-04-19 16:02:53 -06:00
parent 9b597426ac
commit 58ceff7949
Signed by untrusted user who does not match committer: simponic
GPG Key ID: 52B3774857EB24B1
22 changed files with 128 additions and 56 deletions

4
src/bootstrap.js vendored
View File

@ -18,7 +18,9 @@ game.bootstrap = (() => {
'src/components/appearence.js', 'src/components/controllable.js', 'src/components/pushable.js', 'src/components/appearence.js', 'src/components/controllable.js', 'src/components/pushable.js',
'src/components/loadPriority.js', 'src/components/stop.js', 'src/components/alive.js', 'src/components/loadPriority.js', 'src/components/stop.js', 'src/components/alive.js',
'src/components/sprite.js', 'src/components/particles.js', 'src/components/noun.js', 'src/components/sprite.js', 'src/components/particles.js', 'src/components/noun.js',
'src/components/name.js', 'src/components/verb.js', 'src/components/name.js', 'src/components/verb.js', 'src/components/burn.js',
'src/components/burnable.js', 'src/components/sink.js', 'src/components/sinkable.js',
'src/components/win.js',
], ],
id: 'components' id: 'components'
}, },

1
src/components/burn.js Normal file
View File

@ -0,0 +1 @@
game.components.Burn = () => game.Component('burn', {});

View File

@ -0,0 +1 @@
game.components.Burnable = () => game.Component('burnable', {});

1
src/components/sink.js Normal file
View File

@ -0,0 +1 @@
game.components.Sink = () => game.Component('sink', {});

View File

@ -0,0 +1 @@
game.components.Sinkable = () => game.Component('sinkable', {});

1
src/components/win.js Normal file
View File

@ -0,0 +1 @@
game.components.Win = () => game.Component('win', {});

View File

@ -5,8 +5,9 @@ game.createBigBlue = () => {
bigBlue.addComponent(game.components.Alive()); bigBlue.addComponent(game.components.Alive());
bigBlue.addComponent(game.components.Sprite({spriteName: "bigBlue"})); bigBlue.addComponent(game.components.Sprite({spriteName: "bigBlue"}));
// bigBlue.addComponent(game.components.Controllable({controls: ['left', 'right', 'up', 'down']})); // bigBlue.addComponent(game.components.Controllable({controls: ['left', 'right', 'up', 'down']}));
bigBlue.addComponent(game.components.Name({selector: "bigblue"})); bigBlue.addComponent(game.components.Name({selector: "bigblue"}));
bigBlue.addComponent(game.components.Burnable());
bigBlue.addComponent(game.components.Sinkable());
return bigBlue; return bigBlue;
}; };

View File

@ -1,4 +1,4 @@
game.createBorderParticles = () => { game.createBorderParticles = (spawnerSpec) => {
const particleSpawner = game.Entity(); const particleSpawner = game.Entity();
const spawnFunction = (particleSpec) => { const spawnFunction = (particleSpec) => {
switch (Math.floor(Math.random() * 4)) { switch (Math.floor(Math.random() * 4)) {
@ -24,7 +24,7 @@ game.createBorderParticles = () => {
particleSpawner.addComponent(game.components.Particles({ particleSpawner.addComponent(game.components.Particles({
spec: { spec: {
spawnFunction, spawnFunction,
colors: ["#16f7c9", "#0d6e5a", "#2fa18a", "#48cfb4", "#58877d", "#178054", "#2cdb92"], colors: ["#666666", "#777777", "#888888", "#999999"],
maxSpeed: 0.20, maxSpeed: 0.20,
minRadius: 1, minRadius: 1,
maxRadius: 3, maxRadius: 3,
@ -32,6 +32,7 @@ game.createBorderParticles = () => {
maxLife: 300, maxLife: 300,
minAmount: 20, minAmount: 20,
maxAmount: 50, maxAmount: 50,
...spawnerSpec,
} }
})); }));
particleSpawner.addComponent(game.components.LoadPriority({priority: 1})); particleSpawner.addComponent(game.components.LoadPriority({priority: 1}));

View File

@ -4,5 +4,6 @@ game.createFlag = () => {
flag.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); flag.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100}));
flag.addComponent(game.components.Alive()); flag.addComponent(game.components.Alive());
flag.addComponent(game.components.Sprite({spriteName: "flag"})) flag.addComponent(game.components.Sprite({spriteName: "flag"}))
flag.addComponent(game.components.Name({selector: "flag"}));
return flag; return flag;
} }

View File

@ -4,6 +4,6 @@ game.createLava = () => {
lava.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); lava.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100}));
lava.addComponent(game.components.Alive()); lava.addComponent(game.components.Alive());
lava.addComponent(game.components.Sprite({spriteName: "lava"})) lava.addComponent(game.components.Sprite({spriteName: "lava"}))
lava.addComponent(game.components.Name({selector: "lava"}));
return lava; return lava;
} }

View File

@ -8,6 +8,7 @@ game.createRock = () => {
// rock.addComponent(game.components.Pushable()); // rock.addComponent(game.components.Pushable());
rock.addComponent(game.components.Name({selector: "rock"})); rock.addComponent(game.components.Name({selector: "rock"}));
rock.addComponent(game.components.Sinkable());
return rock; return rock;
}; };

View File

@ -4,5 +4,6 @@ game.createWater = () => {
water.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); water.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100}));
water.addComponent(game.components.Alive()); water.addComponent(game.components.Alive());
water.addComponent(game.components.Sprite({spriteName: "water"})) water.addComponent(game.components.Sprite({spriteName: "water"}))
water.addComponent(game.components.Name({selector: "water"}))
return water; return water;
} }

View File

@ -4,6 +4,7 @@ game.createWordFlag = () => {
wordFlag.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); wordFlag.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100}));
wordFlag.addComponent(game.components.Pushable({pushable: true})); wordFlag.addComponent(game.components.Pushable({pushable: true}));
wordFlag.addComponent(game.components.Alive()); wordFlag.addComponent(game.components.Alive());
wordFlag.addComponent(game.components.Sprite({spriteName: "wordFlag"})) wordFlag.addComponent(game.components.Sprite({spriteName: "wordFlag"}));
wordFlag.addComponent(game.components.Noun({select: "flag"}));
return wordFlag; return wordFlag;
} }

View File

@ -5,5 +5,6 @@ game.createWordKill = () => {
wordKill.addComponent(game.components.Pushable({pushable: true})); wordKill.addComponent(game.components.Pushable({pushable: true}));
wordKill.addComponent(game.components.Alive()); wordKill.addComponent(game.components.Alive());
wordKill.addComponent(game.components.Sprite({spriteName: "wordKill"})) wordKill.addComponent(game.components.Sprite({spriteName: "wordKill"}))
wordKill.addComponent(game.components.Verb({action: "burn"}));
return wordKill; return wordKill;
} }

View File

@ -6,5 +6,6 @@ game.createWordLava = () => {
wordLava.addComponent(game.components.Alive()); wordLava.addComponent(game.components.Alive());
wordLava.addComponent(game.components.Sprite({spriteName: "wordLava"})) wordLava.addComponent(game.components.Sprite({spriteName: "wordLava"}))
wordLava.addComponent(game.components.Noun({select: "lava"}));
return wordLava; return wordLava;
} }

View File

@ -4,6 +4,7 @@ game.createWordSink = () => {
wordSink.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); wordSink.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100}));
wordSink.addComponent(game.components.Pushable({pushable: true})); wordSink.addComponent(game.components.Pushable({pushable: true}));
wordSink.addComponent(game.components.Alive()); wordSink.addComponent(game.components.Alive());
wordSink.addComponent(game.components.Sprite({spriteName: "wordSink"})) wordSink.addComponent(game.components.Sprite({spriteName: "wordSink"}));
wordSink.addComponent(game.components.Verb({action: "sink"}));
return wordSink; return wordSink;
} }

View File

@ -4,6 +4,7 @@ game.createWordWater = () => {
wordWater.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); wordWater.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100}));
wordWater.addComponent(game.components.Pushable({pushable: true})); wordWater.addComponent(game.components.Pushable({pushable: true}));
wordWater.addComponent(game.components.Alive()); wordWater.addComponent(game.components.Alive());
wordWater.addComponent(game.components.Sprite({spriteName: "wordWater"})) wordWater.addComponent(game.components.Sprite({spriteName: "wordWater"}));
wordWater.addComponent(game.components.Noun({select: "water"}));
return wordWater; return wordWater;
} }

View File

@ -4,6 +4,7 @@ game.createWordWin = () => {
wordWin.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); wordWin.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100}));
wordWin.addComponent(game.components.Pushable({pushable: true})); wordWin.addComponent(game.components.Pushable({pushable: true}));
wordWin.addComponent(game.components.Alive()); wordWin.addComponent(game.components.Alive());
wordWin.addComponent(game.components.Sprite({spriteName: "wordWin"})) wordWin.addComponent(game.components.Sprite({spriteName: "wordWin"}));
wordWin.addComponent(game.components.Verb({action: "win"}));
return wordWin; return wordWin;
} }

View File

@ -2,50 +2,83 @@ game.system.Collision = (entitiesGrid) => {
const update = (elapsedTime, entities, changedIds) => { const update = (elapsedTime, entities, changedIds) => {
const thisChangedIds = new Set(); const thisChangedIds = new Set();
for (let entity of Object.keys(entities).map((id) => entities[id])) { for (let entity of Object.keys(entities).map((id) => entities[id])) {
if (entity.hasComponent("controllable") && entity.hasComponent("gridPosition") && entity.hasComponent("momentum")) { if (entity.hasComponent("position") && entity.hasComponent("gridPosition") && (entity.hasComponent("burnable") || entity.hasComponent("sinkable")) && entity.hasComponent("alive")) {
const momentum = unitize(entity.components.momentum); for (let collided of entitiesGrid[entity.components.gridPosition.y][entity.components.gridPosition.x].values()) {
if (!equivalence(entity, collided) && collided.hasComponent("alive")){
let found; if (entity.hasComponent("burnable") && collided.hasComponent("burn")) {
const proposed = {x: entity.components.gridPosition.x + momentum.dx, y: entity.components.gridPosition.y + momentum.dy}; const burnedParticleSpawner = game.createBorderParticles({colors: ["#f58d42", "#d6600b", "#c7a312", "#f2b844"]});
const entitiesToPush = []; burnedParticleSpawner.addComponent(game.components.Position(collided.components.position));
let wall = false; burnedParticleSpawner.addComponent(game.components.Appearance({width: game.canvas.width / game.config.xDim, height: game.canvas.height / game.config.yDim}));
do { game.entities[burnedParticleSpawner.id] = burnedParticleSpawner;
const proposedClampedInBounds = clamp(proposed, game.config.xDim-1, game.config.yDim-1); entity.removeComponent("alive");
if (!equivalence(proposed, proposedClampedInBounds)) { break;
break; } else if (entity.hasComponent("sinkable") && collided.hasComponent("sink")) {
} const sunkParticleSpawner = game.createBorderParticles({colors: ["#16f7c9", "#0d6e5a", "#2fa18a", "#48cfb4", "#58877d", "#178054", "#2cdb92"]});
sunkParticleSpawner.addComponent(game.components.Position(collided.components.position));
found = false; sunkParticleSpawner.addComponent(game.components.Appearance({width: game.canvas.width / game.config.xDim, height: game.canvas.height / game.config.yDim}));
game.entities[sunkParticleSpawner.id] = sunkParticleSpawner;
const entitiesInCell = entitiesGrid[proposed.y][proposed.x]; entity.removeComponent("alive");
collided.removeComponent("alive");
for (let next of entitiesInCell.values()) {
if (next.hasComponent("stop")) {
wall = next;
found = false;
break; break;
} }
if (next.hasComponent("pushable")) {
entitiesToPush.push(next);
found = true;
}
} }
}
}
if (entity.hasComponent("controllable") && entity.hasComponent("gridPosition")) {
for (let collided of entitiesGrid[entity.components.gridPosition.y][entity.components.gridPosition.x].values()) {
if (collided.hasComponent("win")) {
game.systems.menu.bringUpMenu();
game.systems.menu.setState("levelSelect");
}
}
proposed.x += momentum.dx; if (entity.hasComponent("momentum")) {
proposed.y += momentum.dy; const momentum = unitize(entity.components.momentum);
} while(found);
if (wall) { let found;
entity.removeComponent("momentum"); const proposed = {x: entity.components.gridPosition.x + momentum.dx, y: entity.components.gridPosition.y + momentum.dy};
} else { const entitiesToPush = [];
entitiesToPush.map((e) => { let wall = false;
const pushedParticleSpawner = game.createBorderParticles(); do {
pushedParticleSpawner.addComponent(game.components.Position(e.components.position)); const proposedClampedInBounds = clamp(proposed, game.config.xDim-1, game.config.yDim-1);
pushedParticleSpawner.addComponent(game.components.Appearance({width: game.canvas.width / game.config.xDim, height: game.canvas.height / game.config.yDim})); if (!equivalence(proposed, proposedClampedInBounds)) {
game.entities[pushedParticleSpawner.id] = pushedParticleSpawner; break;
}
e.addComponent(game.components.Momentum({...momentum})); found = false;
});
const entitiesInCell = entitiesGrid[proposed.y][proposed.x];
for (let next of entitiesInCell.values()) {
if (next.hasComponent("alive")) {
if (next.hasComponent("stop")) {
wall = next;
found = false;
break;
}
if (next.hasComponent("pushable")) {
entitiesToPush.push(next);
found = true;
}
}
}
proposed.x += momentum.dx;
proposed.y += momentum.dy;
} while(found);
if (wall) {
entity.removeComponent("momentum");
} else {
entitiesToPush.map((e) => {
const pushedParticleSpawner = game.createBorderParticles({maxSpeed: 0.1, minAmount: 10, maxAmount: 15});
pushedParticleSpawner.addComponent(game.components.Position(e.components.position));
pushedParticleSpawner.addComponent(game.components.Appearance({width: game.canvas.width / game.config.xDim, height: game.canvas.height / game.config.yDim}));
game.entities[pushedParticleSpawner.id] = pushedParticleSpawner;
e.addComponent(game.components.Momentum({...momentum}));
});
}
} }
} }
} }

View File

@ -12,7 +12,7 @@ game.system.KeyboardInput = () => {
const update = (elapsedTime, entities, changedIds) => { const update = (elapsedTime, entities, changedIds) => {
for (let id in entities) { for (let id in entities) {
const entity = entities[id]; const entity = entities[id];
if (entity.hasComponent('controllable')) { if (entity.hasComponent('controllable') && entity.hasComponent('alive')) {
const controls = entity.components.controllable.controls; const controls = entity.components.controllable.controls;
if (!changedIds.has(entity.id)) { if (!changedIds.has(entity.id)) {
if (controls.includes('left') && keys[game.controls.left]) { if (controls.includes('left') && keys[game.controls.left]) {

View File

@ -1,6 +1,7 @@
game.system.Logic = (entitiesGrid) => { game.system.Logic = (entitiesGrid) => {
"use strict"; "use strict";
let currentVerbRules = []; let currentVerbRules = [];
let previousControllableIds = new Set();
const isWord = (entity) => entity.hasComponent("gridPosition") && (entity.hasComponent("verb") || entity.hasComponent("noun")); const isWord = (entity) => entity.hasComponent("gridPosition") && (entity.hasComponent("verb") || entity.hasComponent("noun"));
const getFirstWordEntity = (gridPosition) => { const getFirstWordEntity = (gridPosition) => {
@ -19,12 +20,16 @@ game.system.Logic = (entitiesGrid) => {
"stop": game.components.Stop(), "stop": game.components.Stop(),
"push": game.components.Pushable(), "push": game.components.Pushable(),
"you": game.components.Controllable({controls: ['left', 'right', 'up', 'down']}), "you": game.components.Controllable({controls: ['left', 'right', 'up', 'down']}),
"burn": game.components.Burn(),
"sink": game.components.Sink(),
"win": game.components.Win(),
}; };
const nounsToEntityCreators = { const nounsToEntityCreators = {
"rock": game.createRock, "rock": game.createRock,
"wall": game.createWall, "wall": game.createWall,
"bigblue": game.createBigBlue,
"flag": game.createFlag,
}; };
const doOnRule = (rule, entities, direction) => { const doOnRule = (rule, entities, direction) => {
@ -38,26 +43,42 @@ game.system.Logic = (entitiesGrid) => {
currentVerbRules.push(rule); currentVerbRules.push(rule);
} }
for (let id in entities) { for (let id in entities) {
if (entities[id].hasComponent("name") && entities[id].components.name.selector == entityName) { const entity = entities[id];
if (entity.hasComponent("alive") && entity.hasComponent("name") && entity.components.name.selector == entityName) {
changedEntityIds.push(id); changedEntityIds.push(id);
const component = verbActionsToComponent[verb]; const component = verbActionsToComponent[verb];
if (component) { if (component) {
if (direction == "apply") { if (direction == "apply") {
entities[id].addComponent(component); if (verb == "you") {
if (!previousControllableIds.has(id)) {
const newYouParticleSpawner = game.createBorderParticles({colors: ["#ffc0cb", "#ffb6c1", "#ffc1cc", "#ffbcd9", "#ff1493"], minAmount: 80, maxAmount: 150, maxSpeed: 0.5});
newYouParticleSpawner.addComponent(game.components.Position(entity.components.position));
newYouParticleSpawner.addComponent(game.components.Appearance({width: game.canvas.width / game.config.xDim, height: game.canvas.height / game.config.yDim}));
game.entities[newYouParticleSpawner.id] = newYouParticleSpawner;
}
}
entity.addComponent(component);
} else if (direction == "deapply") { } else if (direction == "deapply") {
entities[id].removeComponent(component.name); if (entity.hasComponent("controllable")) {
previousControllableIds.add(id);
}
entity.removeComponent(component.name);
} }
} }
} }
} }
if (direction == "apply" && changedEntityIds.some((id) => previousControllableIds.has(id))) {
previousControllableIds = new Set();
}
} }
if (application.hasComponent("noun")) { if (application.hasComponent("noun")) {
const applicationEntityName = application.components.noun.select; const applicationEntityName = application.components.noun.select;
for (let id in entities) { for (let id in entities) {
if (entities[id].hasComponent("name") && entities[id].components.name.selector == entityName) { const entity = entities[id];
if (entity.hasComponent("name") && entity.components.name.selector == entityName) {
const e = nounsToEntityCreators[applicationEntityName](); const e = nounsToEntityCreators[applicationEntityName]();
entities[id].components.name = e.components.name; entity.components.name = e.components.name;
entities[id].components.sprite = e.components.sprite; entity.components.sprite = e.components.sprite;
} }
} }
} }

View File

@ -14,7 +14,7 @@ game.system.Render = (graphics) => {
const drawSpec = {...entity.components.position, ...entity.components.appearance}; const drawSpec = {...entity.components.position, ...entity.components.appearance};
if (entity.hasComponent("sprite")) { if (entity.hasComponent("sprite")) {
game.sprites[entity.components.sprite.spriteName].draw(elapsedTime, drawSpec); game.sprites[entity.components.sprite.spriteName].draw(elapsedTime, drawSpec);
} else if (entity.hasComponent("particles")) { } else if (entity.hasComponent("particles") && entity.particleSprite) {
entity.particleSprite.draw(elapsedTime, drawSpec); entity.particleSprite.draw(elapsedTime, drawSpec);
} }
} }