diff options
Diffstat (limited to 'Homework/cs5410/bbiy/src/systems')
| -rw-r--r-- | Homework/cs5410/bbiy/src/systems/collision.js | 93 | ||||
| -rw-r--r-- | Homework/cs5410/bbiy/src/systems/grid.js | 71 | ||||
| -rw-r--r-- | Homework/cs5410/bbiy/src/systems/keyboardInput.js | 46 | ||||
| -rw-r--r-- | Homework/cs5410/bbiy/src/systems/logic.js | 149 | ||||
| -rw-r--r-- | Homework/cs5410/bbiy/src/systems/menu.js | 114 | ||||
| -rw-r--r-- | Homework/cs5410/bbiy/src/systems/particle.js | 57 | ||||
| -rw-r--r-- | Homework/cs5410/bbiy/src/systems/physics.js | 16 | ||||
| -rw-r--r-- | Homework/cs5410/bbiy/src/systems/render.js | 26 | ||||
| -rw-r--r-- | Homework/cs5410/bbiy/src/systems/system.js | 1 | ||||
| -rw-r--r-- | Homework/cs5410/bbiy/src/systems/undo.js | 32 |
10 files changed, 605 insertions, 0 deletions
diff --git a/Homework/cs5410/bbiy/src/systems/collision.js b/Homework/cs5410/bbiy/src/systems/collision.js new file mode 100644 index 0000000..0732c74 --- /dev/null +++ b/Homework/cs5410/bbiy/src/systems/collision.js @@ -0,0 +1,93 @@ +game.system.Collision = (entitiesGrid) => { + const update = (elapsedTime, entities, changedIds) => { + const thisChangedIds = new Set(); + for (let entity of Object.keys(entities).map((id) => entities[id])) { + if (entity.hasComponent("position") && entity.hasComponent("gridPosition") && (entity.hasComponent("burnable") || entity.hasComponent("sinkable")) && entity.hasComponent("alive")) { + for (let collided of entitiesGrid[entity.components.gridPosition.y][entity.components.gridPosition.x].values()) { + if (!equivalence(entity, collided) && collided.hasComponent("alive")){ + if (entity.hasComponent("burnable") && collided.hasComponent("burn")) { + const burnedParticleSpawner = game.createBorderParticles({colors: ["#f58d42", "#d6600b", "#c7a312", "#f2b844"]}); + burnedParticleSpawner.addComponent(game.components.Position(collided.components.position)); + burnedParticleSpawner.addComponent(game.components.Appearance({width: game.canvas.width / game.config.xDim, height: game.canvas.height / game.config.yDim})); + game.entities[burnedParticleSpawner.id] = burnedParticleSpawner; + entity.removeComponent("alive"); + game.assets.death.play(); + 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)); + sunkParticleSpawner.addComponent(game.components.Appearance({width: game.canvas.width / game.config.xDim, height: game.canvas.height / game.config.yDim})); + game.entities[sunkParticleSpawner.id] = sunkParticleSpawner; + entity.removeComponent("alive"); + collided.removeComponent("alive"); + game.assets.death.play(); + break; + } + } + } + } + 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.assets.win.play(); + game.win = true; + game.systems.menu.bringUpMenu(); + game.systems.menu.setState("levelSelect"); + } + } + + if (entity.hasComponent("momentum")) { + const momentum = unitize(entity.components.momentum); + + let found; + const proposed = {x: entity.components.gridPosition.x + momentum.dx, y: entity.components.gridPosition.y + momentum.dy}; + const entitiesToPush = []; + let wall = false; + do { + const proposedClampedInBounds = clamp(proposed, game.config.xDim-1, game.config.yDim-1); + if (!equivalence(proposed, proposedClampedInBounds)) { + break; + } + + 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 { + game.assets.move.play(); + 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})); + }); + } + } + } + } + return thisChangedIds; + }; + return { update }; +}; diff --git a/Homework/cs5410/bbiy/src/systems/grid.js b/Homework/cs5410/bbiy/src/systems/grid.js new file mode 100644 index 0000000..6d4cf84 --- /dev/null +++ b/Homework/cs5410/bbiy/src/systems/grid.js @@ -0,0 +1,71 @@ +game.system.Grid = (entitiesGrid) => { + let gridWidth = game.canvas.width / game.config.xDim; + let gridHeight = game.canvas.height / game.config.yDim; + + const gameCoordsToGrid = ({ x, y }) => { + return { x: Math.floor((x+gridWidth/2) / game.canvas.width * game.config.yDim), y: Math.floor((y+gridHeight/2) / game.canvas.height * game.config.yDim) }; + }; + + const gridCoordsToGame = ({ x, y }) => { + return { x: x * gridWidth, y: y * gridHeight }; + }; + + const rebuildGrid = (entities) => { + let changedIds = new Set(); + entities.map(entity => { + const { x, y } = entity.components.gridPosition; + if (!entitiesGrid[y][x].has(entity.id)) { + changedIds.add(entity.id); + } + }); + entitiesGrid.forEach((row) => row.forEach((entitiesInCell) => { + for (let id of entitiesInCell.keys()) { + if (changedIds.has(id)) { + entitiesInCell.delete(id); + } + } + })); + changedIds.forEach(id => { + // TODO: Figure out why we HAVE to use game.entities rather than entities + // Hint: it breaks when changing a level in the menu + const entity = game.entities[id]; + const { x, y } = entity.components.gridPosition; + entitiesGrid[y][x].set(entity.id, entity); + }); + }; + + const update = (_elapsedTime, entities, changedIds) => { + gridEntities = Object.keys(entities).filter((x) => entities[x].hasComponent("gridPosition")).map((x) => entities[x]); + const thisChangedIds = new Set(); + gridEntities.map((entity) => { + if (entity.hasComponent("appearance")) { + entity.components.appearance.width = gridWidth; + entity.components.appearance.height = gridHeight; + } + if (entity.hasComponent("gridPosition")) { + const oldGridCoords = entity.components.gridPosition; + if (entity.hasComponent("position")) { + const gameCoords = gridCoordsToGame(entity.components.gridPosition); + if (Math.abs(entity.components.position.x - gameCoords.x) >= gridWidth/2 || Math.abs(entity.components.position.y - gameCoords.y) >= gridHeight/2) { + entity.components.gridPosition = gameCoordsToGrid(entity.components.position); + if (entity.hasComponent("momentum")) { + entity.removeComponent("momentum"); + } + } + } + if (!entity.hasComponent("position") || !equivalence(entity.components.gridPosition, oldGridCoords)) { + entity.components.position = { + ...entity.components.position, + ...gridCoordsToGame(entity.components.gridPosition) + }; + thisChangedIds.add(entity.id); + } + } + }); + rebuildGrid(gridEntities); + return thisChangedIds; + }; + + + return { gameCoordsToGrid, gridCoordsToGame, update, gridWidth, gridHeight }; +}; diff --git a/Homework/cs5410/bbiy/src/systems/keyboardInput.js b/Homework/cs5410/bbiy/src/systems/keyboardInput.js new file mode 100644 index 0000000..1b399ca --- /dev/null +++ b/Homework/cs5410/bbiy/src/systems/keyboardInput.js @@ -0,0 +1,46 @@ +game.system.KeyboardInput = () => { + "use strict"; + const keys = {}; + const keyPress = (event) => { + if (!event.repeat) { + keys[event.key] = true; + } + }; + const removeKey = (event) => { + delete keys[event.key]; + }; + const update = (elapsedTime, entities, changedIds) => { + for (let id in entities) { + const entity = entities[id]; + if (entity.hasComponent('controllable') && entity.hasComponent('alive')) { + const controls = entity.components.controllable.controls; + if (!changedIds.has(entity.id)) { + if (controls.includes('left') && keys[game.controls.left]) { + entity.addComponent(game.components.Momentum({ dx: -1, dy: 0 })); + } else if (controls.includes('right') && keys[game.controls.right]) { + entity.addComponent(game.components.Momentum({ dx: 1, dy: 0 })); + } else if (controls.includes('up') && keys[game.controls.up]) { + entity.addComponent(game.components.Momentum({ dx: 0, dy: -1 })); + } else if (controls.includes('down') && keys[game.controls.down]) { + entity.addComponent(game.components.Momentum({ dx: 0, dy: 1 })); + } + } + } + } + if (keys[game.controls.undo]) { + game.systems.undo.undo(entities); + } + if (keys[game.controls.reset]) { + game.loadLevelIndex(game.level); + } + if (keys["Escape"]) { + game.systems.menu.bringUpMenu(); + } + Object.keys(keys).map((key) => delete keys[key]); + + return new Set(); + }; + window.addEventListener("keydown", keyPress); + window.addEventListener("keyup", removeKey); + return { keys, update }; +} diff --git a/Homework/cs5410/bbiy/src/systems/logic.js b/Homework/cs5410/bbiy/src/systems/logic.js new file mode 100644 index 0000000..e8854c1 --- /dev/null +++ b/Homework/cs5410/bbiy/src/systems/logic.js @@ -0,0 +1,149 @@ +game.system.Logic = (entitiesGrid) => { + "use strict"; + let currentVerbRules = []; + let previousVerbState = { + controllable: new Set(), + win: new Set(), + }; + const isWord = (entity) => entity.hasComponent("gridPosition") && (entity.hasComponent("verb") || entity.hasComponent("noun")); + + const getFirstWordEntity = (gridPosition) => { + if (!equivalence(gridPosition, clamp(gridPosition, game.config.xDim, game.config.yDim))) { + return null; + } + for (let entity of entitiesGrid[gridPosition.y][gridPosition.x].values()) { + if (isWord(entity)) { + return entity; + } + } + return null; + }; + + const verbActionsToComponent = { + "stop": game.components.Stop(), + "push": game.components.Pushable(), + "you": game.components.Controllable({controls: ['left', 'right', 'up', 'down']}), + "burn": game.components.Burn(), + "sink": game.components.Sink(), + "win": game.components.Win(), + }; + + const nounsToEntityCreators = { + "rock": game.createRock, + "wall": game.createWall, + "bigblue": game.createBigBlue, + "flag": game.createFlag, + "lava": game.createLava, + "water": game.createWater, + }; + + const doOnRule = (rule, entities, direction) => { + const [applyee, application] = [entities[rule[0]], entities[rule[1]]]; + const changedEntityIds = []; + if (applyee.hasComponent("noun")) { + const entityName = applyee.components.noun.select; + if (application.hasComponent("verb")) { + const verb = application.components.verb.action; + if (direction == "apply") { + currentVerbRules.push(rule); + } + for (let id in entities) { + const entity = entities[id]; + if (entity.hasComponent("alive") && entity.hasComponent("name") && entity.components.name.selector == entityName) { + changedEntityIds.push(id); + const component = verbActionsToComponent[verb]; + if (component) { + if (direction == "apply") { + if (verb == "you") { + if (!previousVerbState.controllable.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; + } + } + if (verb == "win") { + if (!previousVerbState.win.has(id)) { + game.assets.win.play(); + } + } + entity.addComponent(component); + } else if (direction == "deapply") { + if (entity.hasComponent("controllable")) { + previousVerbState.controllable.add(id); + } + if (entity.hasComponent("win")) { + previousVerbState.win.add(id); + } + entity.removeComponent(component.name); + } + } + } + } + if (direction == "apply") { + if (changedEntityIds.some((id) => previousVerbState.controllable.has(id))) { + previousVerbState.controllable = new Set(); + } + } + } + if (application.hasComponent("noun")) { + const applicationEntityName = application.components.noun.select; + for (let id in entities) { + const entity = entities[id]; + if (entity.hasComponent("name") && entity.components.name.selector == entityName) { + const e = nounsToEntityCreators[applicationEntityName](); + ["name", "sprite", "burnable", "sinkable"].map((name) => { + if (e.hasComponent(name)) { + entity.components[name] = e.components[name]; + } + }); + } + } + } + }; + return changedEntityIds; + }; + + const parseRules = (entities) => { + currentVerbRules.map((rule) => doOnRule(rule, entities, "deapply")); + currentVerbRules = []; + const isWordGridPositions = []; + const changedEntityIds = new Set(); + entitiesGrid.forEach((row) => row.forEach((entitiesInCell) => { + for (let entity of entitiesInCell.values()) { + if (isWord(entity) && entity.hasComponent("verb") && entity.components.verb.action == "Is") { + isWordGridPositions.push(entity.components.gridPosition); + } + } + })); + let newRules = []; + isWordGridPositions.forEach((gridPosition) => { + const east = getFirstWordEntity({y: gridPosition.y, x: gridPosition.x - 1}); + const west = getFirstWordEntity({y: gridPosition.y, x: gridPosition.x + 1}); + const north = getFirstWordEntity({x: gridPosition.x, y: gridPosition.y - 1}); + const south = getFirstWordEntity({x: gridPosition.x, y: gridPosition.y + 1}); + + if (east && west) { + newRules.push([east.id, west.id]); + } + if (north && south) { + newRules.push([north.id, south.id]); + } + }); + newRules = newRules.sort((a, b) => (entities[b[1]].hasComponent("noun") ? 1 : -1) - (entities[a[1]].hasComponent("noun") ? 1 : -1)); + newRules.map((rule) => doOnRule(rule, entities, "apply").map((id) => changedEntityIds.add(id))); + return changedEntityIds; + }; + + const update = (_elapsedTime, entities, changedIds) => { + for (let id of changedIds) { + const changed = entities[id]; + if (changed.hasComponent("verb") || changed.hasComponent("noun")) { + return parseRules(entities); + } + } + return new Set(); + }; + + return { update, parseRules }; +}; diff --git a/Homework/cs5410/bbiy/src/systems/menu.js b/Homework/cs5410/bbiy/src/systems/menu.js new file mode 100644 index 0000000..abd6748 --- /dev/null +++ b/Homework/cs5410/bbiy/src/systems/menu.js @@ -0,0 +1,114 @@ +game.system.Menu = () => { + let state; + + const menuElement = document.getElementById("menu"); + + const escapeEventListener = (e) => { + if (e.key == "Escape") { + setState('main'); + } + }; + + const setState = (newState) => { + state = newState; + draw(); + }; + + const bringUpMenu = () => { + game.running = false; + game.assets.music.pause(); + window.addEventListener("keydown", escapeEventListener); + setState("main"); + }; + + const hide = () => { + menuElement.style.display = "none"; + game.startLoop(); + }; + + const listenFor = (action, elementId) => { + const element = document.getElementById(elementId); + element.innerHTML = "Listening..."; + const handleKey = (event) => { + window.removeEventListener("keydown", handleKey); + if (event.key == "Escape") { + element.innerHTML = menu.controls[action]; + return; + } + game.controls[action] = event.key; + localStorage.setItem("controls", JSON.stringify(game.controls)); + element.innerHTML = event.key; + }; + window.addEventListener("keydown", handleKey); + }; + + const setLevel = (index) => { + game.loadLevelIndex(index); + hide(); + }; + + const draw = () => { + menuElement.style.display = "block"; + menuElement.innerHTML = `<h1><span style='color: blue'>Big Blue</span> Is <span style='color: green'>You</h1>`; + if (state == "main") { + menuElement.innerHTML += ` + <div class='menu-button' onclick='game.systems.menu.setState("controls")'>Change Controls</div> + <div class='menu-button' onclick='game.systems.menu.setState("credits")'>Credits</div> + <div class='menu-button' onclick='game.systems.menu.setState("levelSelect")'>Select Level</div> + `; + } + else if (state == "controls") { + menuElement.innerHTML += ` + <div> + <p> + Move left: <button id="moveLeft" onfocus='game.systems.menu.listenFor("left", "moveLeft")'>${game.controls.left}</button> + <br> + Move right: <button id="moveRight" onfocus='game.systems.menu.listenFor("right", "moveRight")'>${game.controls.right}</button> + <br> + Move up: <button id="moveUp" onfocus='game.systems.menu.listenFor("up", "moveUp")'>${game.controls.up}</button> + <br> + Move down: <button id="moveDown" onfocus='game.systems.menu.listenFor("down", "moveDown")'>${game.controls.down}</button> + <br> + Undo: <button id="undo" onfocus='game.systems.menu.listenFor("undo", "undo")'>${game.controls.undo}</button> + <br> + Reset: <button id="reset" onfocus='game.systems.menu.listenFor("reset", "reset")'>${game.controls.reset}</button> + </p> + </div> + `; + } else if (state == "credits") { + menuElement.innerHTML += ` + <div> + <p> + Sprites from Baba Is You, as hosted <a href="https://www.spriters-resource.com/pc_computer/babaisyou/sheet/115231/">here</a>, and a few custom ones. + <br> + Background is from <a href="https://i.pinimg.com/originals/b2/2a/a2/b22aa22b2f3f55b6468361158d52e2e7.gif">PinImg</a>. + <br> + Music is <a href="https://www.youtube.com/watch?v=yQjAF3frudY">Fluffing A Duck</a> by Kevin MacLeod. + <br> + Other sound effects generated on <a href="https://www.sfxr.me/">SFXR</a>. + <br> + Developed by Logan Hunt, Ethan Payne + </p> + </div> + `; + } else if (state == "levelSelect") { + menuElement.innerHTML += ` + <div> + <p> Select a level to play: </p> + ${ + game.levels.map((level, index) => { + return `<div class='menu-button' onclick='game.systems.menu.setLevel(${index});'>${level.levelName}</div>`; + }).join("") + } + `; + } + if (!game.win) { + menuElement.innerHTML += "<div class='menu-button' onclick='game.systems.menu.hide()'>Resume Game</div>"; + } + if (state !== "main") { + menuElement.innerHTML += "<div class='menu-button' onclick='game.systems.menu.setState(\"main\")'>Back</div>"; + } + }; + + return { bringUpMenu, setState, listenFor, hide, setLevel, state }; +}; diff --git a/Homework/cs5410/bbiy/src/systems/particle.js b/Homework/cs5410/bbiy/src/systems/particle.js new file mode 100644 index 0000000..56d8c47 --- /dev/null +++ b/Homework/cs5410/bbiy/src/systems/particle.js @@ -0,0 +1,57 @@ +game.system.Particle = () => { + "use strict"; + const particleSpawners = {}; + + const particleSpawner = ({colors, maxAmount, minAmount, minLife, maxLife, minRadius, maxRadius, maxSpeed, spawnFunction}) => { + return Array(randomInRange(minAmount, maxAmount)).fill(0).map(() => { + let particleSpec = { + x: Math.random(), + y: Math.random(), + dx: Math.random() * maxSpeed - maxSpeed / 2, + dy: Math.random() * maxSpeed - maxSpeed / 2, + radius: randomInRange(minRadius, maxRadius), + color: colors[randomInRange(0, colors.length-1)], + lifetime: randomInRange(minLife, maxLife), + elapsed: 0, + }; + if (spawnFunction) { + particleSpec = {...particleSpec, ...spawnFunction(particleSpec)}; + } + return particleSpec; + }); + }; + + const update = (elapsedTime, entities, _changedIds) => { + for (let id in entities) { + const entity = entities[id]; + if (entity.hasComponent("particles")) { + if (!particleSpawners[entity.id]) { + particleSpawners[entity.id] = particleSpawner(entity.components.particles.spec.spec); + entities[id].particleSprite = game.graphics.Sprite({ + drawFunction: (elapsedTime, {x, y, width, height}, context) => { + let particleSpawner = particleSpawners[entity.id]; + particleSpawner.map((particleSpec) => particleSpec.elapsed += elapsedTime); + particleSpawners[id] = particleSpawner.filter((particleSpec) => particleSpec.lifetime > particleSpec.elapsed); + particleSpawner = particleSpawners[id]; + if (particleSpawner.length === 0) { + entities[id].removeComponent("alive"); + } + particleSpawner.map((particleSpec) => { + const position = {x: (particleSpec.x * width) + x + particleSpec.dx * particleSpec.elapsed, y: (particleSpec.y * height) + y + particleSpec.dy * particleSpec.elapsed}; + const fill = context.fillStyle; + context.fillStyle = particleSpec.color; + context.beginPath(); + context.arc(position.x, position.y, particleSpec.radius, 0, 2 * Math.PI); + context.fill(); + context.fillStyle = fill; + }); + } + }); + } + } + } + return new Set(); + }; + + return { update }; +}; diff --git a/Homework/cs5410/bbiy/src/systems/physics.js b/Homework/cs5410/bbiy/src/systems/physics.js new file mode 100644 index 0000000..18c3cb3 --- /dev/null +++ b/Homework/cs5410/bbiy/src/systems/physics.js @@ -0,0 +1,16 @@ +game.system.Physics = () => { + const update = (elapsedTime, entities, changedIds) => { + for (let id in entities) { + const entity = entities[id]; + if (entity.hasComponent("momentum") && entity.hasComponent("appearance")) { + const {dx, dy} = entity.components.momentum; + entity.components.position.x += dx * elapsedTime; + entity.components.position.y += dy * elapsedTime; + entity.components.position = clamp(entity.components.position, game.canvas.width - entity.components.appearance.width, game.canvas.height - entity.components.appearance.height); + } + } + + return new Set(); + }; + return { update }; +}; diff --git a/Homework/cs5410/bbiy/src/systems/render.js b/Homework/cs5410/bbiy/src/systems/render.js new file mode 100644 index 0000000..e0db7e3 --- /dev/null +++ b/Homework/cs5410/bbiy/src/systems/render.js @@ -0,0 +1,26 @@ +game.system.Render = (graphics) => { + const update = (elapsedTime, entities, _changedIds) => { + graphics.clear(); + + const entitiesArray = Object.keys(entities).map(key => entities[key]); + const sortedEntities = entitiesArray.sort((a, b) => { + const aprior = a.hasComponent("loadPriority") ? a.components.loadPriority.priority : 0; + const bprior = b.hasComponent("loadPriority") ? b.components.loadPriority.priority : 0; + return bprior - aprior; + }); + + sortedEntities.forEach((entity) => { + if (entity.hasComponent("position") && entity.hasComponent("appearance") && entity.hasComponent("alive")) { + const drawSpec = {...entity.components.position, ...entity.components.appearance}; + if (entity.hasComponent("sprite")) { + game.sprites[entity.components.sprite.spriteName].draw(elapsedTime, drawSpec); + } else if (entity.hasComponent("particles") && entity.particleSprite) { + entity.particleSprite.draw(elapsedTime, drawSpec); + } + } + }); + + return new Set(); + }; + return { update }; +}; diff --git a/Homework/cs5410/bbiy/src/systems/system.js b/Homework/cs5410/bbiy/src/systems/system.js new file mode 100644 index 0000000..f331401 --- /dev/null +++ b/Homework/cs5410/bbiy/src/systems/system.js @@ -0,0 +1 @@ +game.system = {};
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/systems/undo.js b/Homework/cs5410/bbiy/src/systems/undo.js new file mode 100644 index 0000000..086d6b4 --- /dev/null +++ b/Homework/cs5410/bbiy/src/systems/undo.js @@ -0,0 +1,32 @@ +game.system.Undo = (entitiesGrid, logicSystem, gridSystem) => { + const states = []; + + const update = (elapsedTime, entities, changedIds) => { + if (changedIds.size) { + const state = {}; + for (let id in entities) { + if (entities[id].hasComponent("gridPosition")) { + state[id] = JSON.parse(JSON.stringify(entities[id].components)); + } + } + states.push(state); + } + return new Set(); + }; + + const undo = (entities) => { + let state = states.slice(0, -1).pop(); + if (states.length > 1) { + states.pop(); + } + for (let id in state) { + for (let componentName in state[id]) { + entities[id].addComponent({name: componentName, ...state[id][componentName]}); + } + } + gridSystem.update(0, entities, new Set()); + logicSystem.parseRules(entities); + }; + + return { update, undo }; +}; |
