diff options
Diffstat (limited to 'Homework/cs5410/bbiy/src')
64 files changed, 1442 insertions, 0 deletions
diff --git a/Homework/cs5410/bbiy/src/bootstrap.js b/Homework/cs5410/bbiy/src/bootstrap.js new file mode 100644 index 0000000..09e7ec5 --- /dev/null +++ b/Homework/cs5410/bbiy/src/bootstrap.js @@ -0,0 +1,117 @@ +game.bootstrap = (() => { + const image_extensions = ["png", "jpeg", "jpg"]; + const audio_extensions = ["mp3"]; + const scripts = [ + { + src: [ + 'src/utils/objectEquivalence.js', 'src/utils/unitizeVector.js', 'src/utils/clamp.js', 'src/utils/loadLevel.js', + 'src/utils/randInRange.js' + ], + id: 'utils' + }, + { src: ['src/render/graphics.js'], id: 'graphics' }, + { src: ['src/render/sprites.js'], id: 'sprites' }, + { src: ['src/components/component.js'], id: 'component' }, + { + src: [ + 'src/components/position.js', 'src/components/momentum.js', 'src/components/gridPosition.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/sprite.js', 'src/components/particles.js', 'src/components/noun.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' + }, + { src: ['src/entities/entity.js'], id: 'entity' }, + { + src: [ + 'src/entities/bigblue.js', 'src/entities/flag.js', 'src/entities/floor.js', 'src/entities/grass.js', 'src/entities/hedge.js', + 'src/entities/lava.js', 'src/entities/rock.js', 'src/entities/wall.js', 'src/entities/wordBigBlue.js', + 'src/entities/wordFlag.js', 'src/entities/wordIs.js', 'src/entities/wordKill.js', 'src/entities/wordLava.js', + 'src/entities/wordPush.js', 'src/entities/wordRock.js', 'src/entities/wordSink.js', 'src/entities/wordStop.js', + 'src/entities/wordWall.js', 'src/entities/wordWater.js', 'src/entities/wordWin.js', 'src/entities/wordYou.js', + 'src/entities/borderParticles.js', 'src/entities/water.js' + ], + id: 'entities' + }, + { src: ['src/systems/system.js'], id: 'system' }, + { + src: [ + 'src/systems/render.js', 'src/systems/grid.js', 'src/systems/physics.js', 'src/systems/keyboardInput.js', + 'src/systems/collision.js', 'src/systems/undo.js', 'src/systems/particle.js', 'src/systems/menu.js', 'src/systems/logic.js', + ], + id: 'systems' }, + { src: ['src/game.js'], id: 'game' }, + ]; + const assets = {}; + [ + "bigblue", "flag", "floor", "grass", "hedge", "lava", "rock", + "wall", "wordBigBlue", "wordFlag", "wordIs", "wordKill", "wordLava", + "wordPush", "wordRock", "wordSink", "wordStop", "wordWall", "wordWater", + "wordWin", "wordYou", "water" + ].map((x) => assets[x] = `assets/image/${x}.png`); + [ + "music", "death", "move", "win" + ].map((x) => assets[x] = `assets/sound/${x}.mp3`); + + const loadScripts = function(onDone) { + if (scripts.length) { + let script = scripts.shift(); + require(script.src, () => { + loadScripts(onDone); + }); + } else { + onDone(); + } + } + + const loadAsset = (source) => { + let fileExtension = source.substr(source.lastIndexOf('.') + 1); + return fetch(source) + .then((r) => r.blob()) + .then((r) => { + let asset; + if (image_extensions.includes(fileExtension)) { + asset = new Image(); + } else if (audio_extensions.includes(fileExtension)) { + asset = new Audio(); + } + asset.src = URL.createObjectURL(r); + const ready = () => URL.revokeObjectURL(asset.src); + if (asset instanceof Image) { + asset.onload = ready; + } else if (asset instanceof Audio) { + asset.oncanplaythrough = ready; + } + return asset; + }) + } + + const loadAssets = function() { + game.assets = {}; + const promises = []; + for (let key in assets) { + promises.push(loadAsset(assets[key], (asset) => { + game.assets[key] = asset; + }, (error) => { + console.log(error) + })); + } + return Promise.all(Object.keys(assets).map((key) => { + return loadAsset(assets[key]).then((asset) => game.assets[key] = asset); + })); + } + + const loadLevels = function() { + game.levels = []; + fetch('/levels') + .then((r) => r.json()) + .then((r) => game.levels = r); + } + + Promise.all([loadAssets(), loadLevels()]).then(() => { + loadScripts(() => game.initialize()); + }) +})(); diff --git a/Homework/cs5410/bbiy/src/components/alive.js b/Homework/cs5410/bbiy/src/components/alive.js new file mode 100644 index 0000000..08b0890 --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/alive.js @@ -0,0 +1 @@ +game.components.Alive = () => game.Component('alive');
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/components/appearence.js b/Homework/cs5410/bbiy/src/components/appearence.js new file mode 100644 index 0000000..8c2130a --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/appearence.js @@ -0,0 +1 @@ +game.components.Appearance = ({rot, width, height}) => game.Component('appearance', {rot, width, height});
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/components/burn.js b/Homework/cs5410/bbiy/src/components/burn.js new file mode 100644 index 0000000..f138e9d --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/burn.js @@ -0,0 +1 @@ +game.components.Burn = () => game.Component('burn', {});
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/components/burnable.js b/Homework/cs5410/bbiy/src/components/burnable.js new file mode 100644 index 0000000..d5767e7 --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/burnable.js @@ -0,0 +1 @@ +game.components.Burnable = () => game.Component('burnable', {});
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/components/component.js b/Homework/cs5410/bbiy/src/components/component.js new file mode 100644 index 0000000..9b55a1f --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/component.js @@ -0,0 +1,8 @@ +game.components = {}; + +game.Component = (name, spec) => { + return { + name, + ...spec + }; +}; diff --git a/Homework/cs5410/bbiy/src/components/controllable.js b/Homework/cs5410/bbiy/src/components/controllable.js new file mode 100644 index 0000000..84b05ee --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/controllable.js @@ -0,0 +1 @@ +game.components.Controllable = ({ controls }) => game.Component('controllable', { controls });
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/components/gridPosition.js b/Homework/cs5410/bbiy/src/components/gridPosition.js new file mode 100644 index 0000000..ede4b23 --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/gridPosition.js @@ -0,0 +1 @@ +game.components.GridPosition = ({x, y}) => game.Component('gridPosition', {x, y});
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/components/loadPriority.js b/Homework/cs5410/bbiy/src/components/loadPriority.js new file mode 100644 index 0000000..35c3887 --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/loadPriority.js @@ -0,0 +1 @@ +game.components.LoadPriority = ({priority}) => game.Component('loadPriority', {priority});
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/components/momentum.js b/Homework/cs5410/bbiy/src/components/momentum.js new file mode 100644 index 0000000..b974aa7 --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/momentum.js @@ -0,0 +1 @@ +game.components.Momentum = ({dx, dy}) => game.Component('momentum', {dx, dy});
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/components/name.js b/Homework/cs5410/bbiy/src/components/name.js new file mode 100644 index 0000000..d3bf14a --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/name.js @@ -0,0 +1 @@ +game.components.Name = ({selector}) => game.Component('name', {selector}); diff --git a/Homework/cs5410/bbiy/src/components/noun.js b/Homework/cs5410/bbiy/src/components/noun.js new file mode 100644 index 0000000..4b01e06 --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/noun.js @@ -0,0 +1 @@ +game.components.Noun = ({select}) => game.Component('noun', {select}); diff --git a/Homework/cs5410/bbiy/src/components/particles.js b/Homework/cs5410/bbiy/src/components/particles.js new file mode 100644 index 0000000..8120ff1 --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/particles.js @@ -0,0 +1 @@ +game.components.Particles = (spec) => game.Component('particles', {spec}); diff --git a/Homework/cs5410/bbiy/src/components/position.js b/Homework/cs5410/bbiy/src/components/position.js new file mode 100644 index 0000000..072a1a6 --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/position.js @@ -0,0 +1 @@ +game.components.Position = ({x, y}) => game.Component('position', {x, y})
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/components/pushable.js b/Homework/cs5410/bbiy/src/components/pushable.js new file mode 100644 index 0000000..85087ea --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/pushable.js @@ -0,0 +1 @@ +game.components.Pushable = () => game.Component('pushable'); diff --git a/Homework/cs5410/bbiy/src/components/sink.js b/Homework/cs5410/bbiy/src/components/sink.js new file mode 100644 index 0000000..3a4d9dc --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/sink.js @@ -0,0 +1 @@ +game.components.Sink = () => game.Component('sink', {});
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/components/sinkable.js b/Homework/cs5410/bbiy/src/components/sinkable.js new file mode 100644 index 0000000..1fcb132 --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/sinkable.js @@ -0,0 +1 @@ +game.components.Sinkable = () => game.Component('sinkable', {});
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/components/sprite.js b/Homework/cs5410/bbiy/src/components/sprite.js new file mode 100644 index 0000000..f45975d --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/sprite.js @@ -0,0 +1 @@ +game.components.Sprite = ({spriteName}) => game.Component('sprite', {spriteName});
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/components/stop.js b/Homework/cs5410/bbiy/src/components/stop.js new file mode 100644 index 0000000..17d4c38 --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/stop.js @@ -0,0 +1 @@ +game.components.Stop = () => game.Component("stop"); diff --git a/Homework/cs5410/bbiy/src/components/verb.js b/Homework/cs5410/bbiy/src/components/verb.js new file mode 100644 index 0000000..393e48c --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/verb.js @@ -0,0 +1 @@ +game.components.Verb = ({action}) => game.Component('verb', {action}); diff --git a/Homework/cs5410/bbiy/src/components/win.js b/Homework/cs5410/bbiy/src/components/win.js new file mode 100644 index 0000000..ea905fc --- /dev/null +++ b/Homework/cs5410/bbiy/src/components/win.js @@ -0,0 +1 @@ +game.components.Win = () => game.Component('win', {});
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/entities/bigblue.js b/Homework/cs5410/bbiy/src/entities/bigblue.js new file mode 100644 index 0000000..45fbc90 --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/bigblue.js @@ -0,0 +1,13 @@ +game.createBigBlue = () => { + const bigBlue = game.Entity(); + bigBlue.addComponent(game.components.LoadPriority({priority: 1})); + bigBlue.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + bigBlue.addComponent(game.components.Alive()); + bigBlue.addComponent(game.components.Sprite({spriteName: "bigBlue"})); + +// bigBlue.addComponent(game.components.Controllable({controls: ['left', 'right', 'up', 'down']})); + bigBlue.addComponent(game.components.Name({selector: "bigblue"})); + bigBlue.addComponent(game.components.Burnable()); + bigBlue.addComponent(game.components.Sinkable()); + return bigBlue; +}; diff --git a/Homework/cs5410/bbiy/src/entities/borderParticles.js b/Homework/cs5410/bbiy/src/entities/borderParticles.js new file mode 100644 index 0000000..e1a7e98 --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/borderParticles.js @@ -0,0 +1,41 @@ +game.createBorderParticles = (spawnerSpec) => { + const particleSpawner = game.Entity(); + const spawnFunction = (particleSpec) => { + switch (Math.floor(Math.random() * 4)) { + case 0: + particleSpec.y = 0; + particleSpec.dy = -Math.abs(particleSpec.dy); + break; + case 1: + particleSpec.x = 1; + particleSpec.dx = Math.abs(particleSpec.dx); + break; + case 2: + particleSpec.y = 1; + particleSpec.dy = Math.abs(particleSpec.dy); + break; + case 3: + particleSpec.x = 0; + particleSpec.dx = -Math.abs(particleSpec.dx); + break; + } + return particleSpec; + }; + particleSpawner.addComponent(game.components.Particles({ + spec: { + spawnFunction, + colors: ["#666666", "#777777", "#888888", "#999999"], + maxSpeed: 0.20, + minRadius: 1, + maxRadius: 3, + minLife: 100, + maxLife: 300, + minAmount: 20, + maxAmount: 50, + ...spawnerSpec, + } + })); + particleSpawner.addComponent(game.components.LoadPriority({priority: 1})); + particleSpawner.addComponent(game.components.Alive()); + return particleSpawner; +} diff --git a/Homework/cs5410/bbiy/src/entities/entity.js b/Homework/cs5410/bbiy/src/entities/entity.js new file mode 100644 index 0000000..3da032c --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/entity.js @@ -0,0 +1,23 @@ +game.nextId = 0; + +game.Entity = (id=game.nextId++) => { + const components = {}; + + const addComponent = (component) => { + components[component.name] = component; + }; + const hasComponent = (componentName) => components[componentName] !== undefined; + const removeComponent = (componentName) => { + if (hasComponent(componentName)) { + delete components[componentName]; + } + }; + + return { + id, + components, + addComponent, + removeComponent, + hasComponent + }; +}
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/entities/flag.js b/Homework/cs5410/bbiy/src/entities/flag.js new file mode 100644 index 0000000..e8c662a --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/flag.js @@ -0,0 +1,12 @@ +game.createFlag = () => { + const flag = game.Entity(); + flag.addComponent(game.components.LoadPriority({priority: 5})); + flag.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + flag.addComponent(game.components.Alive()); + flag.addComponent(game.components.Sprite({spriteName: "flag"})) + flag.addComponent(game.components.Name({selector: "flag"})); + + flag.addComponent(game.components.Burnable()); + flag.addComponent(game.components.Sinkable()); + return flag; +} diff --git a/Homework/cs5410/bbiy/src/entities/floor.js b/Homework/cs5410/bbiy/src/entities/floor.js new file mode 100644 index 0000000..95cbcf2 --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/floor.js @@ -0,0 +1,8 @@ +game.createFloor = () => { + const floor = game.Entity(); + floor.addComponent(game.components.LoadPriority({priority: 6})); + floor.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + floor.addComponent(game.components.Alive()); + floor.addComponent(game.components.Sprite({spriteName: "floor"})) + return floor; +} diff --git a/Homework/cs5410/bbiy/src/entities/grass.js b/Homework/cs5410/bbiy/src/entities/grass.js new file mode 100644 index 0000000..7f26712 --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/grass.js @@ -0,0 +1,8 @@ +game.createGrass = () => { + const grass = game.Entity(); + grass.addComponent(game.components.LoadPriority({priority: 6})); + grass.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + grass.addComponent(game.components.Alive()); + grass.addComponent(game.components.Sprite({spriteName: "grass"})) + return grass; +} diff --git a/Homework/cs5410/bbiy/src/entities/hedge.js b/Homework/cs5410/bbiy/src/entities/hedge.js new file mode 100644 index 0000000..90d069b --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/hedge.js @@ -0,0 +1,9 @@ +game.createHedge = () => { + const hedge = game.Entity(); + hedge.addComponent(game.components.LoadPriority({priority: 6})); + hedge.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + hedge.addComponent(game.components.Stop({stop: true})); + hedge.addComponent(game.components.Alive()); + hedge.addComponent(game.components.Sprite({spriteName: "hedge"})) + return hedge; +} diff --git a/Homework/cs5410/bbiy/src/entities/lava.js b/Homework/cs5410/bbiy/src/entities/lava.js new file mode 100644 index 0000000..e529812 --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/lava.js @@ -0,0 +1,9 @@ +game.createLava = () => { + const lava = game.Entity(); + lava.addComponent(game.components.LoadPriority({priority: 6})); + lava.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + lava.addComponent(game.components.Alive()); + lava.addComponent(game.components.Sprite({spriteName: "lava"})) + lava.addComponent(game.components.Name({selector: "lava"})); + return lava; +} diff --git a/Homework/cs5410/bbiy/src/entities/rock.js b/Homework/cs5410/bbiy/src/entities/rock.js new file mode 100644 index 0000000..a87710a --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/rock.js @@ -0,0 +1,14 @@ +game.createRock = () => { + const rock = game.Entity(); + rock.addComponent(game.components.LoadPriority({priority: 2})); + rock.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + rock.addComponent(game.components.Alive()); + rock.addComponent(game.components.Sprite({spriteName: "rock"}));; + + +// rock.addComponent(game.components.Pushable()); + rock.addComponent(game.components.Name({selector: "rock"})); + rock.addComponent(game.components.Sinkable()); + + return rock; +}; diff --git a/Homework/cs5410/bbiy/src/entities/wall.js b/Homework/cs5410/bbiy/src/entities/wall.js new file mode 100644 index 0000000..e7549ca --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wall.js @@ -0,0 +1,13 @@ +game.createWall = () => { + const wall = game.Entity(); + wall.addComponent(game.components.LoadPriority({priority: 3})); + wall.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); +// wall.addComponent(game.components.Stop()); + wall.addComponent(game.components.Name({selector: "wall"})); + wall.addComponent(game.components.Alive()); + wall.addComponent(game.components.Sprite({spriteName: "wall"})); + + wall.addComponent(game.components.Burnable()); + wall.addComponent(game.components.Sinkable()); + return wall; +}; diff --git a/Homework/cs5410/bbiy/src/entities/water.js b/Homework/cs5410/bbiy/src/entities/water.js new file mode 100644 index 0000000..11522b1 --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/water.js @@ -0,0 +1,9 @@ +game.createWater = () => { + const water = game.Entity(); + water.addComponent(game.components.LoadPriority({priority: 5})); + water.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + water.addComponent(game.components.Alive()); + water.addComponent(game.components.Sprite({spriteName: "water"})) + water.addComponent(game.components.Name({selector: "water"})) + return water; +} diff --git a/Homework/cs5410/bbiy/src/entities/wordBigBlue.js b/Homework/cs5410/bbiy/src/entities/wordBigBlue.js new file mode 100644 index 0000000..15aaa0e --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordBigBlue.js @@ -0,0 +1,10 @@ +game.createWordBigBlue = () => { + const wordBigBlue = game.Entity(); + wordBigBlue.addComponent(game.components.LoadPriority({priority: 3})); + wordBigBlue.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + wordBigBlue.addComponent(game.components.Pushable({pushable: true})); + wordBigBlue.addComponent(game.components.Alive()); + wordBigBlue.addComponent(game.components.Sprite({spriteName: "wordBigBlue"})); + wordBigBlue.addComponent(game.components.Noun({select: "bigblue"})); + return wordBigBlue; +}; diff --git a/Homework/cs5410/bbiy/src/entities/wordFlag.js b/Homework/cs5410/bbiy/src/entities/wordFlag.js new file mode 100644 index 0000000..79617ea --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordFlag.js @@ -0,0 +1,10 @@ +game.createWordFlag = () => { + const wordFlag = game.Entity(); + wordFlag.addComponent(game.components.LoadPriority({priority: 3})); + wordFlag.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + wordFlag.addComponent(game.components.Pushable({pushable: true})); + wordFlag.addComponent(game.components.Alive()); + wordFlag.addComponent(game.components.Sprite({spriteName: "wordFlag"})); + wordFlag.addComponent(game.components.Noun({select: "flag"})); + return wordFlag; +} diff --git a/Homework/cs5410/bbiy/src/entities/wordIs.js b/Homework/cs5410/bbiy/src/entities/wordIs.js new file mode 100644 index 0000000..041f10c --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordIs.js @@ -0,0 +1,11 @@ +game.createWordIs = () => { + const wordIs = game.Entity(); + wordIs.addComponent(game.components.LoadPriority({priority: 3})); + wordIs.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + // wordIs.addComponent(game.components.Stop({stop: true})); + wordIs.addComponent(game.components.Pushable()); + wordIs.addComponent(game.components.Alive()); + wordIs.addComponent(game.components.Sprite({spriteName: "wordIs"})); + wordIs.addComponent(game.components.Verb({action: "Is"})); + return wordIs; +}; diff --git a/Homework/cs5410/bbiy/src/entities/wordKill.js b/Homework/cs5410/bbiy/src/entities/wordKill.js new file mode 100644 index 0000000..6906e9c --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordKill.js @@ -0,0 +1,10 @@ +game.createWordKill = () => { + const wordKill = game.Entity(); + wordKill.addComponent(game.components.LoadPriority({priority: 3})); + wordKill.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + wordKill.addComponent(game.components.Pushable({pushable: true})); + wordKill.addComponent(game.components.Alive()); + wordKill.addComponent(game.components.Sprite({spriteName: "wordKill"})) + wordKill.addComponent(game.components.Verb({action: "burn"})); + return wordKill; +} diff --git a/Homework/cs5410/bbiy/src/entities/wordLava.js b/Homework/cs5410/bbiy/src/entities/wordLava.js new file mode 100644 index 0000000..cf31286 --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordLava.js @@ -0,0 +1,11 @@ +game.createWordLava = () => { + const wordLava = game.Entity(); + wordLava.addComponent(game.components.LoadPriority({priority: 3})); + wordLava.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + wordLava.addComponent(game.components.Pushable({pushable: true})); + wordLava.addComponent(game.components.Alive()); + + wordLava.addComponent(game.components.Sprite({spriteName: "wordLava"})) + wordLava.addComponent(game.components.Noun({select: "lava"})); + return wordLava; +} diff --git a/Homework/cs5410/bbiy/src/entities/wordPush.js b/Homework/cs5410/bbiy/src/entities/wordPush.js new file mode 100644 index 0000000..5594e46 --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordPush.js @@ -0,0 +1,10 @@ +game.createWordPush = () => { + const wordPush = game.Entity(); + wordPush.addComponent(game.components.LoadPriority({priority: 3})); + wordPush.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + wordPush.addComponent(game.components.Pushable({pushable: true})); + wordPush.addComponent(game.components.Alive()); + wordPush.addComponent(game.components.Sprite({spriteName: "wordPush"})); + wordPush.addComponent(game.components.Verb({action: "push"})); + return wordPush; +}; diff --git a/Homework/cs5410/bbiy/src/entities/wordRock.js b/Homework/cs5410/bbiy/src/entities/wordRock.js new file mode 100644 index 0000000..648f6ba --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordRock.js @@ -0,0 +1,10 @@ +game.createWordRock = () => { + const wordRock = game.Entity(); + wordRock.addComponent(game.components.LoadPriority({priority: 3})); + wordRock.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + wordRock.addComponent(game.components.Pushable({pushable: true})); + wordRock.addComponent(game.components.Alive()); + wordRock.addComponent(game.components.Sprite({spriteName: "wordRock"})); + wordRock.addComponent(game.components.Noun({select: "rock"})); + return wordRock; +}; diff --git a/Homework/cs5410/bbiy/src/entities/wordSink.js b/Homework/cs5410/bbiy/src/entities/wordSink.js new file mode 100644 index 0000000..369f922 --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordSink.js @@ -0,0 +1,10 @@ +game.createWordSink = () => { + const wordSink = game.Entity(); + wordSink.addComponent(game.components.LoadPriority({priority: 3})); + wordSink.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + wordSink.addComponent(game.components.Pushable({pushable: true})); + wordSink.addComponent(game.components.Alive()); + wordSink.addComponent(game.components.Sprite({spriteName: "wordSink"})); + wordSink.addComponent(game.components.Verb({action: "sink"})); + return wordSink; +} diff --git a/Homework/cs5410/bbiy/src/entities/wordStop.js b/Homework/cs5410/bbiy/src/entities/wordStop.js new file mode 100644 index 0000000..e2ad45c --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordStop.js @@ -0,0 +1,10 @@ +game.createWordStop = () => { + const wordStop = game.Entity(); + wordStop.addComponent(game.components.LoadPriority({priority: 3})); + wordStop.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + wordStop.addComponent(game.components.Pushable({pushable: true})); + wordStop.addComponent(game.components.Alive()); + wordStop.addComponent(game.components.Sprite({spriteName: "wordStop"})); + wordStop.addComponent(game.components.Verb({action: "stop"})); + return wordStop; +}; diff --git a/Homework/cs5410/bbiy/src/entities/wordWall.js b/Homework/cs5410/bbiy/src/entities/wordWall.js new file mode 100644 index 0000000..277ab69 --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordWall.js @@ -0,0 +1,10 @@ +game.createWordWall = () => { + const wordWall = game.Entity(); + wordWall.addComponent(game.components.LoadPriority({priority: 3})); + wordWall.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + wordWall.addComponent(game.components.Pushable({pushable: true})); + wordWall.addComponent(game.components.Alive()); + wordWall.addComponent(game.components.Noun({select: "wall"})); + wordWall.addComponent(game.components.Sprite({spriteName: "wordWall"})); + return wordWall; +}; diff --git a/Homework/cs5410/bbiy/src/entities/wordWater.js b/Homework/cs5410/bbiy/src/entities/wordWater.js new file mode 100644 index 0000000..eb0c1be --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordWater.js @@ -0,0 +1,10 @@ +game.createWordWater = () => { + const wordWater = game.Entity(); + wordWater.addComponent(game.components.LoadPriority({priority: 3})); + wordWater.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + wordWater.addComponent(game.components.Pushable({pushable: true})); + wordWater.addComponent(game.components.Alive()); + wordWater.addComponent(game.components.Sprite({spriteName: "wordWater"})); + wordWater.addComponent(game.components.Noun({select: "water"})); + return wordWater; +} diff --git a/Homework/cs5410/bbiy/src/entities/wordWin.js b/Homework/cs5410/bbiy/src/entities/wordWin.js new file mode 100644 index 0000000..e8948ee --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordWin.js @@ -0,0 +1,10 @@ +game.createWordWin = () => { + const wordWin = game.Entity(); + wordWin.addComponent(game.components.LoadPriority({priority: 3})); + wordWin.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + wordWin.addComponent(game.components.Pushable({pushable: true})); + wordWin.addComponent(game.components.Alive()); + wordWin.addComponent(game.components.Sprite({spriteName: "wordWin"})); + wordWin.addComponent(game.components.Verb({action: "win"})); + return wordWin; +} diff --git a/Homework/cs5410/bbiy/src/entities/wordYou.js b/Homework/cs5410/bbiy/src/entities/wordYou.js new file mode 100644 index 0000000..1bf698f --- /dev/null +++ b/Homework/cs5410/bbiy/src/entities/wordYou.js @@ -0,0 +1,10 @@ +game.createWordYou = () => { + const wordYou = game.Entity(); + wordYou.addComponent(game.components.LoadPriority({priority: 3})); + wordYou.addComponent(game.components.Appearance({rot: 0, width: 100, height: 100})); + wordYou.addComponent(game.components.Pushable({pushable: true})); + wordYou.addComponent(game.components.Alive()); + wordYou.addComponent(game.components.Sprite({spriteName: "wordYou"})); + wordYou.addComponent(game.components.Verb({action: "you"})); + return wordYou; +}; diff --git a/Homework/cs5410/bbiy/src/game.js b/Homework/cs5410/bbiy/src/game.js new file mode 100644 index 0000000..5a7e391 --- /dev/null +++ b/Homework/cs5410/bbiy/src/game.js @@ -0,0 +1,72 @@ +game.loop = (timeStamp) => { + if (!game.running) { + return; + } + + let elapsedTime = timeStamp - game.lastTimeStamp; + game.lastTimeStamp = timeStamp; + + const changedIds = new Set(); + game.systemOrder.map((i) => { + game.systems[i] + .update(elapsedTime, game.entities, changedIds) + .forEach((id) => changedIds.add(id)); + }); + + for (let id in game.entities) { + if (game.entities[id].hasComponent("particles") && !game.entities[id].hasComponent("alive")) { + delete game.entities[id]; + } + } + + requestAnimationFrame(game.loop); +}; + +game.toggleRunning = () => { + game.running = !game.running; +}; + +game.startLoop = () => { + game.running = true; + game.lastTimeStamp = performance.now(); + game.assets.music.play(); + if (game.assets.music.paused) { + alert("Failed to start background music; please allow autoplay in your browser settings."); + } + requestAnimationFrame(game.loop); +}; + +game.loadSystems = () => { + game.systemOrder = ["grid", "collision", "physics", "keyboardInput", "particle", "logic", "undo", "render"]; + game.systems = { }; + game.systems.physics = game.system.Physics(), + game.systems.grid = game.system.Grid(game.entitiesGrid); + game.systems.collision = game.system.Collision(game.entitiesGrid); + game.systems.render = game.system.Render(game.graphics); + game.systems.particle = game.system.Particle(game.canvas.context); + game.systems.keyboardInput = game.system.KeyboardInput(); + game.systems.logic = game.system.Logic(game.entitiesGrid); + game.systems.undo = game.system.Undo(game.entitiesGrid, game.systems.logic, game.systems.grid); + game.systems.menu = game.system.Menu(); +}; + +game.loadLevelIndex = (level) => { + game.win = false; + + game.level = level; + [game.entities, game.config] = game.loadLevel(game.levels[game.level]); + + // Maintained by grid system as a side-effect + game.entitiesGrid = Array(game.config.yDim).fill(null).map(() => Array(game.config.xDim).fill(null).map(() => new Map())); + game.loadSystems(); +}; + +game.initialize = () => { + game.assets.music.addEventListener('ended', function() { + this.currentTime = 0; + this.play(); + }, false); + + game.loadLevelIndex(0); + game.startLoop(); +}; diff --git a/Homework/cs5410/bbiy/src/render/graphics.js b/Homework/cs5410/bbiy/src/render/graphics.js new file mode 100644 index 0000000..aa4195e --- /dev/null +++ b/Homework/cs5410/bbiy/src/render/graphics.js @@ -0,0 +1,47 @@ +// Pretty much a copy of my centipede code +game.graphics = ( + (context) => { + context.imageSmoothingEnabled = false; + const clear = () => { + context.clearRect(0, 0, game.canvas.width, game.canvas.height); + }; + + const Sprite = ({image, spriteX, spriteY, spriteWidth, spriteHeight, timePerFrame, cols, rows, numFrames, color, drawFunction}) => { + timePerFrame = timePerFrame ?? 100; + numFrames = numFrames ?? 1; + cols = cols ?? numFrames; + rows = rows ?? 1; + spriteX = spriteX ?? 0; + spriteY = spriteY ?? 0; + + let currentFrame = 0; + let lastTime = performance.now(); + + let draw; + if (!drawFunction) { + draw = (_elapsedTime, {x, y, rot, width, height}) => { + if (numFrames > 1) { + if (performance.now()-lastTime > timePerFrame) { + lastTime = performance.now(); + currentFrame = (currentFrame + 1) % numFrames; + } + } + context.save(); + context.translate(x+width/2, y+height/2); + context.rotate(rot * Math.PI / 180); + context.translate(-x-width/2, -y-height/2); + const row = currentFrame % rows; + const col = Math.floor(currentFrame / rows); + context.drawImage(image, spriteX+col*spriteWidth, spriteY+row*spriteHeight, spriteWidth, spriteHeight, x, y, width, height); + + context.restore(); + }; + } else { + draw = (elapsedTime, drawSpec) => drawFunction(elapsedTime, drawSpec, context); + } + return { draw }; + } + + return { clear, Sprite }; + } +)(game.canvas.context); diff --git a/Homework/cs5410/bbiy/src/render/sprites.js b/Homework/cs5410/bbiy/src/render/sprites.js new file mode 100644 index 0000000..d8ae126 --- /dev/null +++ b/Homework/cs5410/bbiy/src/render/sprites.js @@ -0,0 +1,156 @@ +game.sprites = { + bigBlue: game.graphics.Sprite({ + image: game.assets.bigblue, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + flag: game.graphics.Sprite({ + image: game.assets.flag, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + floor: game.graphics.Sprite({ + image: game.assets.floor, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + hedge: game.graphics.Sprite({ + image: game.assets.hedge, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 250, + }), + grass: game.graphics.Sprite({ + image: game.assets.grass, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + lava: game.graphics.Sprite({ + image: game.assets.lava, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + rock: game.graphics.Sprite({ + image: game.assets.rock, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wall: game.graphics.Sprite({ + image: game.assets.wall, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + water: game.graphics.Sprite({ + image: game.assets.water, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordBigBlue: game.graphics.Sprite({ + image: game.assets.wordBigBlue, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordFlag: game.graphics.Sprite({ + image: game.assets.wordFlag, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordIs: game.graphics.Sprite({ + image: game.assets.wordIs, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordKill: game.graphics.Sprite({ + image: game.assets.wordKill, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordLava: game.graphics.Sprite({ + image: game.assets.wordLava, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordPush: game.graphics.Sprite({ + image: game.assets.wordPush, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordRock: game.graphics.Sprite({ + image: game.assets.wordRock, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordSink: game.graphics.Sprite({ + image: game.assets.wordSink, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordStop: game.graphics.Sprite({ + image: game.assets.wordStop, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordWall: game.graphics.Sprite({ + image: game.assets.wordWall, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordWater: game.graphics.Sprite({ + image: game.assets.wordWater, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordWin: game.graphics.Sprite({ + image: game.assets.wordWin, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), + wordYou: game.graphics.Sprite({ + image: game.assets.wordYou, + spriteHeight: 24, + spriteWidth: 24, + numFrames: 3, + timePerFrame: 100, + }), +}; diff --git a/Homework/cs5410/bbiy/src/require.js b/Homework/cs5410/bbiy/src/require.js new file mode 100644 index 0000000..a4203f0 --- /dev/null +++ b/Homework/cs5410/bbiy/src/require.js @@ -0,0 +1,5 @@ +/** vim: et:ts=4:sw=4:sts=4 + * @license RequireJS 2.3.6 Copyright jQuery Foundation and other contributors. + * Released under MIT license, https://github.com/requirejs/requirejs/blob/master/LICENSE + */ +var requirejs,require,define;!function(global,setTimeout){var req,s,head,baseElement,dataMain,src,interactiveScript,currentlyAddingScript,mainScript,subPath,version="2.3.6",commentRegExp=/\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/gm,cjsRequireRegExp=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,jsSuffixRegExp=/\.js$/,currDirRegExp=/^\.\//,op=Object.prototype,ostring=op.toString,hasOwn=op.hasOwnProperty,isBrowser=!("undefined"==typeof window||"undefined"==typeof navigator||!window.document),isWebWorker=!isBrowser&&"undefined"!=typeof importScripts,readyRegExp=isBrowser&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,defContextName="_",isOpera="undefined"!=typeof opera&&"[object Opera]"===opera.toString(),contexts={},cfg={},globalDefQueue=[],useInteractive=!1;function commentReplace(e,t){return t||""}function isFunction(e){return"[object Function]"===ostring.call(e)}function isArray(e){return"[object Array]"===ostring.call(e)}function each(e,t){var i;if(e)for(i=0;i<e.length&&(!e[i]||!t(e[i],i,e));i+=1);}function eachReverse(e,t){var i;if(e)for(i=e.length-1;-1<i&&(!e[i]||!t(e[i],i,e));i-=1);}function hasProp(e,t){return hasOwn.call(e,t)}function getOwn(e,t){return hasProp(e,t)&&e[t]}function eachProp(e,t){var i;for(i in e)if(hasProp(e,i)&&t(e[i],i))break}function mixin(i,e,r,n){return e&&eachProp(e,function(e,t){!r&&hasProp(i,t)||(!n||"object"!=typeof e||!e||isArray(e)||isFunction(e)||e instanceof RegExp?i[t]=e:(i[t]||(i[t]={}),mixin(i[t],e,r,n)))}),i}function bind(e,t){return function(){return t.apply(e,arguments)}}function scripts(){return document.getElementsByTagName("script")}function defaultOnError(e){throw e}function getGlobal(e){if(!e)return e;var t=global;return each(e.split("."),function(e){t=t[e]}),t}function makeError(e,t,i,r){var n=new Error(t+"\nhttps://requirejs.org/docs/errors.html#"+e);return n.requireType=e,n.requireModules=r,i&&(n.originalError=i),n}if(void 0===define){if(void 0!==requirejs){if(isFunction(requirejs))return;cfg=requirejs,requirejs=void 0}void 0===require||isFunction(require)||(cfg=require,require=void 0),req=requirejs=function(e,t,i,r){var n,o,a=defContextName;return isArray(e)||"string"==typeof e||(o=e,isArray(t)?(e=t,t=i,i=r):e=[]),o&&o.context&&(a=o.context),(n=getOwn(contexts,a))||(n=contexts[a]=req.s.newContext(a)),o&&n.configure(o),n.require(e,t,i)},req.config=function(e){return req(e)},req.nextTick=void 0!==setTimeout?function(e){setTimeout(e,4)}:function(e){e()},require||(require=req),req.version=version,req.jsExtRegExp=/^\/|:|\?|\.js$/,req.isBrowser=isBrowser,s=req.s={contexts:contexts,newContext:newContext},req({}),each(["toUrl","undef","defined","specified"],function(t){req[t]=function(){var e=contexts[defContextName];return e.require[t].apply(e,arguments)}}),isBrowser&&(head=s.head=document.getElementsByTagName("head")[0],baseElement=document.getElementsByTagName("base")[0],baseElement&&(head=s.head=baseElement.parentNode)),req.onError=defaultOnError,req.createNode=function(e,t,i){var r=e.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");return r.type=e.scriptType||"text/javascript",r.charset="utf-8",r.async=!0,r},req.load=function(t,i,r){var e,n=t&&t.config||{};if(isBrowser)return(e=req.createNode(n,i,r)).setAttribute("data-requirecontext",t.contextName),e.setAttribute("data-requiremodule",i),!e.attachEvent||e.attachEvent.toString&&e.attachEvent.toString().indexOf("[native code")<0||isOpera?(e.addEventListener("load",t.onScriptLoad,!1),e.addEventListener("error",t.onScriptError,!1)):(useInteractive=!0,e.attachEvent("onreadystatechange",t.onScriptLoad)),e.src=r,n.onNodeCreated&&n.onNodeCreated(e,n,i,r),currentlyAddingScript=e,baseElement?head.insertBefore(e,baseElement):head.appendChild(e),currentlyAddingScript=null,e;if(isWebWorker)try{setTimeout(function(){},0),importScripts(r),t.completeLoad(i)}catch(e){t.onError(makeError("importscripts","importScripts failed for "+i+" at "+r,e,[i]))}},isBrowser&&!cfg.skipDataMain&&eachReverse(scripts(),function(e){if(head||(head=e.parentNode),dataMain=e.getAttribute("data-main"))return mainScript=dataMain,cfg.baseUrl||-1!==mainScript.indexOf("!")||(mainScript=(src=mainScript.split("/")).pop(),subPath=src.length?src.join("/")+"/":"./",cfg.baseUrl=subPath),mainScript=mainScript.replace(jsSuffixRegExp,""),req.jsExtRegExp.test(mainScript)&&(mainScript=dataMain),cfg.deps=cfg.deps?cfg.deps.concat(mainScript):[mainScript],!0}),define=function(e,i,t){var r,n;"string"!=typeof e&&(t=i,i=e,e=null),isArray(i)||(t=i,i=null),!i&&isFunction(t)&&(i=[],t.length&&(t.toString().replace(commentRegExp,commentReplace).replace(cjsRequireRegExp,function(e,t){i.push(t)}),i=(1===t.length?["require"]:["require","exports","module"]).concat(i))),useInteractive&&(r=currentlyAddingScript||getInteractiveScript())&&(e||(e=r.getAttribute("data-requiremodule")),n=contexts[r.getAttribute("data-requirecontext")]),n?(n.defQueue.push([e,i,t]),n.defQueueMap[e]=!0):globalDefQueue.push([e,i,t])},define.amd={jQuery:!0},req.exec=function(text){return eval(text)},req(cfg)}function newContext(u){var i,e,l,c,d,g={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},p={},f={},r={},h=[],m={},n={},v={},x=1,b=1;function q(e,t,i){var r,n,o,a,s,u,c,d,p,f,l=t&&t.split("/"),h=g.map,m=h&&h["*"];if(e&&(u=(e=e.split("/")).length-1,g.nodeIdCompat&&jsSuffixRegExp.test(e[u])&&(e[u]=e[u].replace(jsSuffixRegExp,"")),"."===e[0].charAt(0)&&l&&(e=l.slice(0,l.length-1).concat(e)),function(e){var t,i;for(t=0;t<e.length;t++)if("."===(i=e[t]))e.splice(t,1),t-=1;else if(".."===i){if(0===t||1===t&&".."===e[2]||".."===e[t-1])continue;0<t&&(e.splice(t-1,2),t-=2)}}(e),e=e.join("/")),i&&h&&(l||m)){e:for(o=(n=e.split("/")).length;0<o;o-=1){if(s=n.slice(0,o).join("/"),l)for(a=l.length;0<a;a-=1)if((r=getOwn(h,l.slice(0,a).join("/")))&&(r=getOwn(r,s))){c=r,d=o;break e}!p&&m&&getOwn(m,s)&&(p=getOwn(m,s),f=o)}!c&&p&&(c=p,d=f),c&&(n.splice(0,d,c),e=n.join("/"))}return getOwn(g.pkgs,e)||e}function E(t){isBrowser&&each(scripts(),function(e){if(e.getAttribute("data-requiremodule")===t&&e.getAttribute("data-requirecontext")===l.contextName)return e.parentNode.removeChild(e),!0})}function w(e){var t=getOwn(g.paths,e);if(t&&isArray(t)&&1<t.length)return t.shift(),l.require.undef(e),l.makeRequire(null,{skipMap:!0})([e]),!0}function y(e){var t,i=e?e.indexOf("!"):-1;return-1<i&&(t=e.substring(0,i),e=e.substring(i+1,e.length)),[t,e]}function S(e,t,i,r){var n,o,a,s,u=null,c=t?t.name:null,d=e,p=!0,f="";return e||(p=!1,e="_@r"+(x+=1)),u=(s=y(e))[0],e=s[1],u&&(u=q(u,c,r),o=getOwn(m,u)),e&&(u?f=i?e:o&&o.normalize?o.normalize(e,function(e){return q(e,c,r)}):-1===e.indexOf("!")?q(e,c,r):e:(u=(s=y(f=q(e,c,r)))[0],f=s[1],i=!0,n=l.nameToUrl(f))),{prefix:u,name:f,parentMap:t,unnormalized:!!(a=!u||o||i?"":"_unnormalized"+(b+=1)),url:n,originalName:d,isDefine:p,id:(u?u+"!"+f:f)+a}}function k(e){var t=e.id,i=getOwn(p,t);return i||(i=p[t]=new l.Module(e)),i}function M(e,t,i){var r=e.id,n=getOwn(p,r);!hasProp(m,r)||n&&!n.defineEmitComplete?(n=k(e)).error&&"error"===t?i(n.error):n.on(t,i):"defined"===t&&i(m[r])}function O(i,e){var t=i.requireModules,r=!1;e?e(i):(each(t,function(e){var t=getOwn(p,e);t&&(t.error=i,t.events.error&&(r=!0,t.emit("error",i)))}),r||req.onError(i))}function j(){globalDefQueue.length&&(each(globalDefQueue,function(e){var t=e[0];"string"==typeof t&&(l.defQueueMap[t]=!0),h.push(e)}),globalDefQueue=[])}function P(e){delete p[e],delete f[e]}function R(){var e,r,t=1e3*g.waitSeconds,n=t&&l.startTime+t<(new Date).getTime(),o=[],a=[],s=!1,u=!0;if(!i){if(i=!0,eachProp(f,function(e){var t=e.map,i=t.id;if(e.enabled&&(t.isDefine||a.push(e),!e.error))if(!e.inited&&n)w(i)?s=r=!0:(o.push(i),E(i));else if(!e.inited&&e.fetched&&t.isDefine&&(s=!0,!t.prefix))return u=!1}),n&&o.length)return(e=makeError("timeout","Load timeout for modules: "+o,null,o)).contextName=l.contextName,O(e);u&&each(a,function(e){!function n(o,a,s){var e=o.map.id;o.error?o.emit("error",o.error):(a[e]=!0,each(o.depMaps,function(e,t){var i=e.id,r=getOwn(p,i);!r||o.depMatched[t]||s[i]||(getOwn(a,i)?(o.defineDep(t,m[i]),o.check()):n(r,a,s))}),s[e]=!0)}(e,{},{})}),n&&!r||!s||!isBrowser&&!isWebWorker||d||(d=setTimeout(function(){d=0,R()},50)),i=!1}}function a(e){hasProp(m,e[0])||k(S(e[0],null,!0)).init(e[1],e[2])}function o(e,t,i,r){e.detachEvent&&!isOpera?r&&e.detachEvent(r,t):e.removeEventListener(i,t,!1)}function s(e){var t=e.currentTarget||e.srcElement;return o(t,l.onScriptLoad,"load","onreadystatechange"),o(t,l.onScriptError,"error"),{node:t,id:t&&t.getAttribute("data-requiremodule")}}function T(){var e;for(j();h.length;){if(null===(e=h.shift())[0])return O(makeError("mismatch","Mismatched anonymous define() module: "+e[e.length-1]));a(e)}l.defQueueMap={}}return c={require:function(e){return e.require?e.require:e.require=l.makeRequire(e.map)},exports:function(e){if(e.usingExports=!0,e.map.isDefine)return e.exports?m[e.map.id]=e.exports:e.exports=m[e.map.id]={}},module:function(e){return e.module?e.module:e.module={id:e.map.id,uri:e.map.url,config:function(){return getOwn(g.config,e.map.id)||{}},exports:e.exports||(e.exports={})}}},(e=function(e){this.events=getOwn(r,e.id)||{},this.map=e,this.shim=getOwn(g.shim,e.id),this.depExports=[],this.depMaps=[],this.depMatched=[],this.pluginMaps={},this.depCount=0}).prototype={init:function(e,t,i,r){r=r||{},this.inited||(this.factory=t,i?this.on("error",i):this.events.error&&(i=bind(this,function(e){this.emit("error",e)})),this.depMaps=e&&e.slice(0),this.errback=i,this.inited=!0,this.ignore=r.ignore,r.enabled||this.enabled?this.enable():this.check())},defineDep:function(e,t){this.depMatched[e]||(this.depMatched[e]=!0,this.depCount-=1,this.depExports[e]=t)},fetch:function(){if(!this.fetched){this.fetched=!0,l.startTime=(new Date).getTime();var e=this.map;if(!this.shim)return e.prefix?this.callPlugin():this.load();l.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],bind(this,function(){return e.prefix?this.callPlugin():this.load()}))}},load:function(){var e=this.map.url;n[e]||(n[e]=!0,l.load(this.map.id,e))},check:function(){if(this.enabled&&!this.enabling){var t,e,i=this.map.id,r=this.depExports,n=this.exports,o=this.factory;if(this.inited){if(this.error)this.emit("error",this.error);else if(!this.defining){if(this.defining=!0,this.depCount<1&&!this.defined){if(isFunction(o)){if(this.events.error&&this.map.isDefine||req.onError!==defaultOnError)try{n=l.execCb(i,o,r,n)}catch(e){t=e}else n=l.execCb(i,o,r,n);if(this.map.isDefine&&void 0===n&&((e=this.module)?n=e.exports:this.usingExports&&(n=this.exports)),t)return t.requireMap=this.map,t.requireModules=this.map.isDefine?[this.map.id]:null,t.requireType=this.map.isDefine?"define":"require",O(this.error=t)}else n=o;if(this.exports=n,this.map.isDefine&&!this.ignore&&(m[i]=n,req.onResourceLoad)){var a=[];each(this.depMaps,function(e){a.push(e.normalizedMap||e)}),req.onResourceLoad(l,this.map,a)}P(i),this.defined=!0}this.defining=!1,this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else hasProp(l.defQueueMap,i)||this.fetch()}},callPlugin:function(){var u=this.map,c=u.id,e=S(u.prefix);this.depMaps.push(e),M(e,"defined",bind(this,function(e){var o,t,i,r=getOwn(v,this.map.id),n=this.map.name,a=this.map.parentMap?this.map.parentMap.name:null,s=l.makeRequire(u.parentMap,{enableBuildCallback:!0});return this.map.unnormalized?(e.normalize&&(n=e.normalize(n,function(e){return q(e,a,!0)})||""),M(t=S(u.prefix+"!"+n,this.map.parentMap,!0),"defined",bind(this,function(e){this.map.normalizedMap=t,this.init([],function(){return e},null,{enabled:!0,ignore:!0})})),void((i=getOwn(p,t.id))&&(this.depMaps.push(t),this.events.error&&i.on("error",bind(this,function(e){this.emit("error",e)})),i.enable()))):r?(this.map.url=l.nameToUrl(r),void this.load()):((o=bind(this,function(e){this.init([],function(){return e},null,{enabled:!0})})).error=bind(this,function(e){this.inited=!0,(this.error=e).requireModules=[c],eachProp(p,function(e){0===e.map.id.indexOf(c+"_unnormalized")&&P(e.map.id)}),O(e)}),o.fromText=bind(this,function(e,t){var i=u.name,r=S(i),n=useInteractive;t&&(e=t),n&&(useInteractive=!1),k(r),hasProp(g.config,c)&&(g.config[i]=g.config[c]);try{req.exec(e)}catch(e){return O(makeError("fromtexteval","fromText eval for "+c+" failed: "+e,e,[c]))}n&&(useInteractive=!0),this.depMaps.push(r),l.completeLoad(i),s([i],o)}),void e.load(u.name,s,o,g))})),l.enable(e,this),this.pluginMaps[e.id]=e},enable:function(){(f[this.map.id]=this).enabled=!0,this.enabling=!0,each(this.depMaps,bind(this,function(e,t){var i,r,n;if("string"==typeof e){if(e=S(e,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap),this.depMaps[t]=e,n=getOwn(c,e.id))return void(this.depExports[t]=n(this));this.depCount+=1,M(e,"defined",bind(this,function(e){this.undefed||(this.defineDep(t,e),this.check())})),this.errback?M(e,"error",bind(this,this.errback)):this.events.error&&M(e,"error",bind(this,function(e){this.emit("error",e)}))}i=e.id,r=p[i],hasProp(c,i)||!r||r.enabled||l.enable(e,this)})),eachProp(this.pluginMaps,bind(this,function(e){var t=getOwn(p,e.id);t&&!t.enabled&&l.enable(e,this)})),this.enabling=!1,this.check()},on:function(e,t){var i=this.events[e];i||(i=this.events[e]=[]),i.push(t)},emit:function(e,t){each(this.events[e],function(e){e(t)}),"error"===e&&delete this.events[e]}},(l={config:g,contextName:u,registry:p,defined:m,urlFetched:n,defQueue:h,defQueueMap:{},Module:e,makeModuleMap:S,nextTick:req.nextTick,onError:O,configure:function(e){if(e.baseUrl&&"/"!==e.baseUrl.charAt(e.baseUrl.length-1)&&(e.baseUrl+="/"),"string"==typeof e.urlArgs){var i=e.urlArgs;e.urlArgs=function(e,t){return(-1===t.indexOf("?")?"?":"&")+i}}var r=g.shim,n={paths:!0,bundles:!0,config:!0,map:!0};eachProp(e,function(e,t){n[t]?(g[t]||(g[t]={}),mixin(g[t],e,!0,!0)):g[t]=e}),e.bundles&&eachProp(e.bundles,function(e,t){each(e,function(e){e!==t&&(v[e]=t)})}),e.shim&&(eachProp(e.shim,function(e,t){isArray(e)&&(e={deps:e}),!e.exports&&!e.init||e.exportsFn||(e.exportsFn=l.makeShimExports(e)),r[t]=e}),g.shim=r),e.packages&&each(e.packages,function(e){var t;t=(e="string"==typeof e?{name:e}:e).name,e.location&&(g.paths[t]=e.location),g.pkgs[t]=e.name+"/"+(e.main||"main").replace(currDirRegExp,"").replace(jsSuffixRegExp,"")}),eachProp(p,function(e,t){e.inited||e.map.unnormalized||(e.map=S(t,null,!0))}),(e.deps||e.callback)&&l.require(e.deps||[],e.callback)},makeShimExports:function(t){return function(){var e;return t.init&&(e=t.init.apply(global,arguments)),e||t.exports&&getGlobal(t.exports)}},makeRequire:function(o,a){function s(e,t,i){var r,n;return a.enableBuildCallback&&t&&isFunction(t)&&(t.__requireJsBuild=!0),"string"==typeof e?isFunction(t)?O(makeError("requireargs","Invalid require call"),i):o&&hasProp(c,e)?c[e](p[o.id]):req.get?req.get(l,e,o,s):(r=S(e,o,!1,!0).id,hasProp(m,r)?m[r]:O(makeError("notloaded",'Module name "'+r+'" has not been loaded yet for context: '+u+(o?"":". Use require([])")))):(T(),l.nextTick(function(){T(),(n=k(S(null,o))).skipMap=a.skipMap,n.init(e,t,i,{enabled:!0}),R()}),s)}return a=a||{},mixin(s,{isBrowser:isBrowser,toUrl:function(e){var t,i=e.lastIndexOf("."),r=e.split("/")[0];return-1!==i&&(!("."===r||".."===r)||1<i)&&(t=e.substring(i,e.length),e=e.substring(0,i)),l.nameToUrl(q(e,o&&o.id,!0),t,!0)},defined:function(e){return hasProp(m,S(e,o,!1,!0).id)},specified:function(e){return e=S(e,o,!1,!0).id,hasProp(m,e)||hasProp(p,e)}}),o||(s.undef=function(i){j();var e=S(i,o,!0),t=getOwn(p,i);t.undefed=!0,E(i),delete m[i],delete n[e.url],delete r[i],eachReverse(h,function(e,t){e[0]===i&&h.splice(t,1)}),delete l.defQueueMap[i],t&&(t.events.defined&&(r[i]=t.events),P(i))}),s},enable:function(e){getOwn(p,e.id)&&k(e).enable()},completeLoad:function(e){var t,i,r,n=getOwn(g.shim,e)||{},o=n.exports;for(j();h.length;){if(null===(i=h.shift())[0]){if(i[0]=e,t)break;t=!0}else i[0]===e&&(t=!0);a(i)}if(l.defQueueMap={},r=getOwn(p,e),!t&&!hasProp(m,e)&&r&&!r.inited){if(!(!g.enforceDefine||o&&getGlobal(o)))return w(e)?void 0:O(makeError("nodefine","No define call for "+e,null,[e]));a([e,n.deps||[],n.exportsFn])}R()},nameToUrl:function(e,t,i){var r,n,o,a,s,u,c=getOwn(g.pkgs,e);if(c&&(e=c),u=getOwn(v,e))return l.nameToUrl(u,t,i);if(req.jsExtRegExp.test(e))a=e+(t||"");else{for(r=g.paths,o=(n=e.split("/")).length;0<o;o-=1)if(s=getOwn(r,n.slice(0,o).join("/"))){isArray(s)&&(s=s[0]),n.splice(0,o,s);break}a=n.join("/"),a=("/"===(a+=t||(/^data\:|^blob\:|\?/.test(a)||i?"":".js")).charAt(0)||a.match(/^[\w\+\.\-]+:/)?"":g.baseUrl)+a}return g.urlArgs&&!/^blob\:/.test(a)?a+g.urlArgs(e,a):a},load:function(e,t){req.load(l,e,t)},execCb:function(e,t,i,r){return t.apply(r,i)},onScriptLoad:function(e){if("load"===e.type||readyRegExp.test((e.currentTarget||e.srcElement).readyState)){interactiveScript=null;var t=s(e);l.completeLoad(t.id)}},onScriptError:function(e){var i=s(e);if(!w(i.id)){var r=[];return eachProp(p,function(e,t){0!==t.indexOf("_@r")&&each(e.depMaps,function(e){if(e.id===i.id)return r.push(t),!0})}),O(makeError("scripterror",'Script error for "'+i.id+(r.length?'", needed by: '+r.join(", "):'"'),e,[i.id]))}}}).require=l.makeRequire(),l}function getInteractiveScript(){return interactiveScript&&"interactive"===interactiveScript.readyState||eachReverse(scripts(),function(e){if("interactive"===e.readyState)return interactiveScript=e}),interactiveScript}}(this,"undefined"==typeof setTimeout?void 0:setTimeout);
\ No newline at end of file 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 }; +}; diff --git a/Homework/cs5410/bbiy/src/utils/clamp.js b/Homework/cs5410/bbiy/src/utils/clamp.js new file mode 100644 index 0000000..b0da861 --- /dev/null +++ b/Homework/cs5410/bbiy/src/utils/clamp.js @@ -0,0 +1,6 @@ +const clamp = (vector, maxX, maxY) => { + const newVector = {...vector}; + newVector.x = Math.max(0, Math.min(maxX, vector.x)); + newVector.y = Math.max(0, Math.min(maxY, vector.y)); + return newVector; +}; diff --git a/Homework/cs5410/bbiy/src/utils/loadLevel.js b/Homework/cs5410/bbiy/src/utils/loadLevel.js new file mode 100644 index 0000000..3bd86d9 --- /dev/null +++ b/Homework/cs5410/bbiy/src/utils/loadLevel.js @@ -0,0 +1,87 @@ +game.loadLevel = (level) => { + const entities = {}; + const config = {...level.gridSize}; + for (let y = 0; y < level.gridSize.yDim; y++) { + for (let x = 0; x < level.gridSize.xDim; x++) { + const cell = level.level[y][x]; + cell.forEach((letter) => { + let entity; + switch (letter) { + case 'b': + entity = game.createBigBlue(); + break; + case 'h': + entity = game.createHedge(); + break; + case 'r': + entity = game.createRock(); + break; + case 'f': + entity = game.createFlag(); + break; + case 'g': + entity = game.createGrass(); + break; + case 'l': + entity = game.createFloor(); + break; + case 'w': + entity = game.createWall(); + break; + case 'a': + entity = game.createWater(); + break; + case 'A': + entity = game.createWordWater(); + break; + case 'N': + entity = game.createWordSink(); + break; + case 'V': + entity = game.createWordLava(); + break; + case 'v': + entity = game.createLava(); + break; + case 'K': + entity = game.createWordKill(); + break; + case 'W': + entity = game.createWordWall(); + break; + case 'I': + entity = game.createWordIs(); + break; + case 'S': + entity = game.createWordStop(); + break; + case 'R': + entity = game.createWordRock(); + break; + case 'P': + entity = game.createWordPush(); + break; + case 'B': + entity = game.createWordBigBlue(); + break; + case 'Y': + entity = game.createWordYou(); + break; + case 'F': + entity = game.createWordFlag(); + break; + case 'X': + entity = game.createWordWin(); + break; + default: + break; + } + if (entity) { + entity.addComponent(game.components.GridPosition({x, y})); + entities[entity.id] = entity; + } + }); + } + } + return [entities, config]; +};
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/utils/objectEquivalence.js b/Homework/cs5410/bbiy/src/utils/objectEquivalence.js new file mode 100644 index 0000000..e33dd76 --- /dev/null +++ b/Homework/cs5410/bbiy/src/utils/objectEquivalence.js @@ -0,0 +1,14 @@ +const equivalence = (obj1, obj2) => { + if (obj1 === null) { + return obj1 === null; + } + return Object.keys(obj1).every(key => { + if (obj2[key] === undefined) { + return false; + } + if (typeof obj1[key] === 'object') { + return equivalence(obj1[key], obj2[key]); + } + return obj1[key] === obj2[key]; + }); +}; diff --git a/Homework/cs5410/bbiy/src/utils/randInRange.js b/Homework/cs5410/bbiy/src/utils/randInRange.js new file mode 100644 index 0000000..528221a --- /dev/null +++ b/Homework/cs5410/bbiy/src/utils/randInRange.js @@ -0,0 +1,4 @@ +const randomInRange = (min, max) => { + // min <= random number <= max + return Math.floor(Math.random() * (max - min + 1)) + min; +}
\ No newline at end of file diff --git a/Homework/cs5410/bbiy/src/utils/unitizeVector.js b/Homework/cs5410/bbiy/src/utils/unitizeVector.js new file mode 100644 index 0000000..6297ca8 --- /dev/null +++ b/Homework/cs5410/bbiy/src/utils/unitizeVector.js @@ -0,0 +1,11 @@ +const unitize = (vector) => { + // Not *technically* a unit vector, but has x,y components of |magnitude| = 1 + Object.keys(vector).forEach((key) => { + if (typeof vector[key] === 'object') { + vector[key] = vector[key].unitize(); + } else if (typeof vector[key] === 'number') { + vector[key] = (vector[key] === 0 ? 0 : vector[key] / Math.abs(vector[key])); + } + }); + return vector; +}
\ No newline at end of file |
