diff options
| author | Elizabeth Alexander Hunt <me@liz.coffee> | 2026-07-02 11:55:17 -0700 |
|---|---|---|
| committer | Elizabeth Alexander Hunt <me@liz.coffee> | 2026-07-02 11:55:17 -0700 |
| commit | 6bf4b90c90f15f4ab60833bddf5b5756d1a6b1f6 (patch) | |
| tree | ed97e39ec77c5231ffd2c394493e68d00ddac5a4 /Homework/cs5410 | |
| download | misc-undergrad-main.tar.gz misc-undergrad-main.zip | |
Diffstat (limited to 'Homework/cs5410')
134 files changed, 4323 insertions, 0 deletions
diff --git a/Homework/cs5410/bbiy/README.md b/Homework/cs5410/bbiy/README.md new file mode 100644 index 0000000..da290c7 --- /dev/null +++ b/Homework/cs5410/bbiy/README.md @@ -0,0 +1,20 @@ +# BBIY (Big Blue Is You) + +It's like Baba is You, but Aggies (and not as good :P) + +Most artwork is from the initial game, as found [here](https://www.spriters-resource.com/pc_computer/babaisyou/sheet/115231/), besides an ugly AF custom "Big Blue" (I am no artist and I spent way too long on it). + +Music is "Fluffing a Duck" by Kevin Macleod, found [on YouTube](https://www.youtube.com/watch?v=yQjAF3frudY). + +Background image is stolen [from here](https://i.pinimg.com/originals/b2/2a/a2/b22aa22b2f3f55b6468361158d52e2e7.gif). +## Building and running + +It's as simple as: + +``` +npm install +npm run start +``` + +## Play online +[on my website](https://simponic.xyz/bbiy/) diff --git a/Homework/cs5410/bbiy/assets/image/background.gif b/Homework/cs5410/bbiy/assets/image/background.gif Binary files differnew file mode 100644 index 0000000..4188529 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/background.gif diff --git a/Homework/cs5410/bbiy/assets/image/bigblue.png b/Homework/cs5410/bbiy/assets/image/bigblue.png Binary files differnew file mode 100644 index 0000000..93f22ea --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/bigblue.png diff --git a/Homework/cs5410/bbiy/assets/image/flag.png b/Homework/cs5410/bbiy/assets/image/flag.png Binary files differnew file mode 100644 index 0000000..0a53d60 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/flag.png diff --git a/Homework/cs5410/bbiy/assets/image/floor.png b/Homework/cs5410/bbiy/assets/image/floor.png Binary files differnew file mode 100644 index 0000000..92ec9f7 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/floor.png diff --git a/Homework/cs5410/bbiy/assets/image/grass.png b/Homework/cs5410/bbiy/assets/image/grass.png Binary files differnew file mode 100644 index 0000000..1991e8c --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/grass.png diff --git a/Homework/cs5410/bbiy/assets/image/hedge.png b/Homework/cs5410/bbiy/assets/image/hedge.png Binary files differnew file mode 100644 index 0000000..9295e61 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/hedge.png diff --git a/Homework/cs5410/bbiy/assets/image/lava.png b/Homework/cs5410/bbiy/assets/image/lava.png Binary files differnew file mode 100644 index 0000000..bf95837 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/lava.png diff --git a/Homework/cs5410/bbiy/assets/image/rock.png b/Homework/cs5410/bbiy/assets/image/rock.png Binary files differnew file mode 100644 index 0000000..ce108b0 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/rock.png diff --git a/Homework/cs5410/bbiy/assets/image/wall.png b/Homework/cs5410/bbiy/assets/image/wall.png Binary files differnew file mode 100644 index 0000000..6912323 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wall.png diff --git a/Homework/cs5410/bbiy/assets/image/water.png b/Homework/cs5410/bbiy/assets/image/water.png Binary files differnew file mode 100644 index 0000000..47703e0 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/water.png diff --git a/Homework/cs5410/bbiy/assets/image/wordBigBlue.png b/Homework/cs5410/bbiy/assets/image/wordBigBlue.png Binary files differnew file mode 100644 index 0000000..6fc767e --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordBigBlue.png diff --git a/Homework/cs5410/bbiy/assets/image/wordFlag.png b/Homework/cs5410/bbiy/assets/image/wordFlag.png Binary files differnew file mode 100644 index 0000000..d848902 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordFlag.png diff --git a/Homework/cs5410/bbiy/assets/image/wordIs.png b/Homework/cs5410/bbiy/assets/image/wordIs.png Binary files differnew file mode 100644 index 0000000..878e40c --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordIs.png diff --git a/Homework/cs5410/bbiy/assets/image/wordKill.png b/Homework/cs5410/bbiy/assets/image/wordKill.png Binary files differnew file mode 100644 index 0000000..e781260 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordKill.png diff --git a/Homework/cs5410/bbiy/assets/image/wordLava.png b/Homework/cs5410/bbiy/assets/image/wordLava.png Binary files differnew file mode 100644 index 0000000..be23819 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordLava.png diff --git a/Homework/cs5410/bbiy/assets/image/wordPush.png b/Homework/cs5410/bbiy/assets/image/wordPush.png Binary files differnew file mode 100644 index 0000000..bdf847e --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordPush.png diff --git a/Homework/cs5410/bbiy/assets/image/wordRock.png b/Homework/cs5410/bbiy/assets/image/wordRock.png Binary files differnew file mode 100644 index 0000000..6809956 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordRock.png diff --git a/Homework/cs5410/bbiy/assets/image/wordSink.png b/Homework/cs5410/bbiy/assets/image/wordSink.png Binary files differnew file mode 100644 index 0000000..8b06deb --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordSink.png diff --git a/Homework/cs5410/bbiy/assets/image/wordStop.png b/Homework/cs5410/bbiy/assets/image/wordStop.png Binary files differnew file mode 100644 index 0000000..e8dc434 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordStop.png diff --git a/Homework/cs5410/bbiy/assets/image/wordWall.png b/Homework/cs5410/bbiy/assets/image/wordWall.png Binary files differnew file mode 100644 index 0000000..24564a1 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordWall.png diff --git a/Homework/cs5410/bbiy/assets/image/wordWater.png b/Homework/cs5410/bbiy/assets/image/wordWater.png Binary files differnew file mode 100644 index 0000000..5141d08 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordWater.png diff --git a/Homework/cs5410/bbiy/assets/image/wordWin.png b/Homework/cs5410/bbiy/assets/image/wordWin.png Binary files differnew file mode 100644 index 0000000..0c018ea --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordWin.png diff --git a/Homework/cs5410/bbiy/assets/image/wordYou.png b/Homework/cs5410/bbiy/assets/image/wordYou.png Binary files differnew file mode 100644 index 0000000..a0613c5 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/image/wordYou.png diff --git a/Homework/cs5410/bbiy/assets/sound/death.mp3 b/Homework/cs5410/bbiy/assets/sound/death.mp3 Binary files differnew file mode 100644 index 0000000..b60a956 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/sound/death.mp3 diff --git a/Homework/cs5410/bbiy/assets/sound/move.mp3 b/Homework/cs5410/bbiy/assets/sound/move.mp3 Binary files differnew file mode 100644 index 0000000..59d2de5 --- /dev/null +++ b/Homework/cs5410/bbiy/assets/sound/move.mp3 diff --git a/Homework/cs5410/bbiy/assets/sound/music.mp3 b/Homework/cs5410/bbiy/assets/sound/music.mp3 Binary files differnew file mode 100644 index 0000000..0d879cc --- /dev/null +++ b/Homework/cs5410/bbiy/assets/sound/music.mp3 diff --git a/Homework/cs5410/bbiy/assets/sound/win.mp3 b/Homework/cs5410/bbiy/assets/sound/win.mp3 Binary files differnew file mode 100644 index 0000000..ad2887c --- /dev/null +++ b/Homework/cs5410/bbiy/assets/sound/win.mp3 diff --git a/Homework/cs5410/bbiy/index.html b/Homework/cs5410/bbiy/index.html new file mode 100644 index 0000000..d89a6b9 --- /dev/null +++ b/Homework/cs5410/bbiy/index.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <title>Big Blue Is You!</title> + <link rel="stylesheet" href="styles/style.css"> + </head> + + <body> + <div style="text-align:center"> + <div class="canvas-holder"> + <canvas id="game-canvas" width="1000" height="1000"></canvas> + <!-- + <div class="game-hud" id="hud"> + <p>Hello, world!</p> + </div> + --> + <div class="menu" style="display: none" id="menu"> + </div> + </div> + </div> + + <script> + const game = { + running: true, + lastTimeStamp: performance.now(), + controls: JSON.parse( + localStorage.getItem("controls") || JSON.stringify({ + left: "a", + right: "d", + up: "w", + down: "s", + undo: "z", + reset: "r", + }) + ), + level: 0, + canvas: { + width: document.getElementById('game-canvas').width, + height: document.getElementById('game-canvas').height, + context: document.getElementById('game-canvas').getContext("2d"), + }, + }; + </script> + + <script data-main = "src/bootstrap" src = "src/require.js"></script> + </body> +</html> diff --git a/Homework/cs5410/bbiy/index.js b/Homework/cs5410/bbiy/index.js new file mode 100644 index 0000000..8b046c1 --- /dev/null +++ b/Homework/cs5410/bbiy/index.js @@ -0,0 +1,55 @@ +const express = require('express'); +const path = require('path'); +const fs = require('fs'); + +const MAP_LAYERS=2; + +const app = express(); +app.route('/').get((req, res) => { + res.sendFile(path.join(__dirname, 'index.html')); +}); + +app.route('/levels').get((req, res) => { + fs.readFile(path.join(__dirname, 'levels-all.bbiy'), 'utf8', (err, data) => { + if (err) { + console.log(err); + return; + } + + const levels = []; + const lines = data.split('\r\n'); + do { + const levelName = lines.shift(); + if (!levelName) { + break; + } + const [xDim, yDim] = lines.shift().split(' x ').map((x) => parseInt(x)); + + let level = Array(yDim).fill(null).map(() => Array(xDim).fill(null).map(() => [])); + for (let i = 0; i < MAP_LAYERS; i++) { + for (let y = 0; y < yDim; y++) { + const line = lines.shift().split(''); + for (let x = 0; x < xDim; x++) { + if (line[x] !== ' ') { + level[y][x].push(line[x]); + } + } + } + } + levels.push({ + levelName, + gridSize: {xDim, yDim}, + level, + }); + } while (lines.length); + + // Send the array of objects + res.send(levels); + }); +}); + +app.use(express.static('.')); + +app.listen(3000, () => { + console.log('Listening on 3000'); +}); diff --git a/Homework/cs5410/bbiy/levels-all.bbiy b/Homework/cs5410/bbiy/levels-all.bbiy new file mode 100644 index 0000000..a098f30 --- /dev/null +++ b/Homework/cs5410/bbiy/levels-all.bbiy @@ -0,0 +1,252 @@ +(almost) No Rules
+20 x 20
+hhhhhhhhhhhhhhhhhhhh
+h h
+h h
+h h
+h h
+h h
+h h
+h wwwwwwwwwwww h
+h r h
+h b r f h
+h r h
+h aaaaa vvvvv h
+h h
+h h
+h h
+h h
+h h
+h h
+h h
+hhhhhhhhhhhhhhhhhhhh
+
+
+ V IK A IN
+
+ W R
+ IS IP
+
+
+
+
+
+
+
+ BIY IX
+ F
+
+
+
+
+
+Level-1
+20 x 20
+hhhhhhhhhhhhhhhhhhhh
+h h
+h h
+h h
+h h
+h h
+h h
+h h
+h llllllllllll h
+h llllllllllll h
+h llllllllllll h
+h h
+h h
+h h
+h h
+h h
+h h
+h h
+h h
+hhhhhhhhhhhhhhhhhhhh
+
+
+
+
+
+ WIS RIP
+
+ wwwwwwwwwwww
+ r
+ b r f
+ r
+ wwwwwwwwwwww
+
+ BIY FIX
+
+
+
+
+
+
+Level-2
+20 x 20
+hhhhhhhhhhhhhhhhhhhh
+h h
+h g h
+h h
+h g g h
+h g g h
+h h
+h h
+h h
+h h
+h h
+h h
+h h
+h h
+h h
+h g g h
+h g h
+h g gg h
+h h
+hhhhhhhhhhhhhhhhhhhh
+
+
+
+
+ wwwwwwww
+ w w
+ w I w
+ wwwww X w
+ w w
+ w F f b w
+ w w
+ wwwwwwwwwwww
+
+ B W
+ I I
+ Y S
+
+
+
+
+Level-3
+20 x 20
+hhhhhhhhhhhhhhhhhhhh
+h h
+h g g h
+h g g h
+h h
+h g g gg h
+h g g h
+h g h
+h h
+h h
+h g h
+h h
+h gh
+h h
+h gg h
+h h
+h g h
+h gg h
+h h
+hhhhhhhhhhhhhhhhhhhh
+
+
+
+ FIX
+ wwwww
+ w w
+ w f w
+ w w
+ wwwwwwwwwww
+ w w
+ w WIS w
+ w w
+ w w
+ w b w
+ w w
+ w w
+ wwwwwwwwwww
+ BIY
+
+
+Level-4
+20 x 20
+hhhhhhhhhhhhhhhhhhhh
+h h
+h g h
+h g gh
+h h
+h g g h
+h h
+h gg h
+h h
+h h
+h h
+h h
+h h
+h g h
+h h
+hg h
+h h
+h g ggg h
+h g g h
+hhhhhhhhhhhhhhhhhhhh
+
+ BWw
+ IIw AIN
+ YSw wwwwwwww
+ www w w
+ w RIP w
+ w w
+ w b rr w
+ w w
+ wwwwwaaaawwwww
+ w w
+ w w
+ w w
+ waaa w
+ waaa FIX w
+ wfaa w
+ wwwwwwwwwwwwww
+
+
+
+Level-5
+20 x 20
+hhhhhhhhhhhhhhhhhhhh
+h h
+h g g h
+h gg h
+h g h
+h h
+h h
+h ggg h
+h g g h
+h g ggg h
+h g h
+h h
+h h
+h gg g h
+h h
+h h
+h g g h
+h g h
+h h
+hhhhhhhhhhhhhhhhhhhh
+
+
+ r
+ r r
+ r W
+ I
+ wwwww vvvvv S
+ w w v v
+ w b r v f v
+ w w v v
+ wwwww vvvvv
+
+
+ wBIYw r
+ F
+ RIP I
+ X
+
+ VIK
+
diff --git a/Homework/cs5410/bbiy/package-lock.json b/Homework/cs5410/bbiy/package-lock.json new file mode 100644 index 0000000..0a4e4d5 --- /dev/null +++ b/Homework/cs5410/bbiy/package-lock.json @@ -0,0 +1,876 @@ +{ + "name": "bigblueisyou", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "bigblueisyou", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "^4.17.3" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "node_modules/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.7", + "raw-body": "2.4.3", + "type-is": "~1.6.18" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", + "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.19.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.7", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==", + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "dependencies": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "engines": { + "node": ">= 0.8" + } + } + }, + "dependencies": { + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.9.7", + "raw-body": "2.4.3", + "type-is": "~1.6.18" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "express": { + "version": "4.17.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.3.tgz", + "integrity": "sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==", + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.19.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.4.2", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.9.7", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.17.2", + "serve-static": "1.14.2", + "setprototypeof": "1.2.0", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.3.tgz", + "integrity": "sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==", + "requires": { + "bytes": "3.1.2", + "http-errors": "1.8.1", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "send": { + "version": "0.17.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.2.tgz", + "integrity": "sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "1.8.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, + "serve-static": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.2.tgz", + "integrity": "sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.2" + } + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + } + } +} diff --git a/Homework/cs5410/bbiy/package.json b/Homework/cs5410/bbiy/package.json new file mode 100644 index 0000000..e3dfbf8 --- /dev/null +++ b/Homework/cs5410/bbiy/package.json @@ -0,0 +1,15 @@ +{ + "name": "bigblueisyou", + "version": "1.0.0", + "description": "Big Blue Is You!", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node index.js" + }, + "author": "Logan Hunt", + "license": "ISC", + "dependencies": { + "express": "^4.17.3" + } +} 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 diff --git a/Homework/cs5410/bbiy/styles/style.css b/Homework/cs5410/bbiy/styles/style.css new file mode 100644 index 0000000..9f0dc5c --- /dev/null +++ b/Homework/cs5410/bbiy/styles/style.css @@ -0,0 +1,73 @@ +body { + margin: 0; + font-family: Lucida Sans Typewriter,Lucida Console,monaco,Bitstream Vera Sans Mono,monospace; + background-image: url("/assets/image/background.gif"); + background-size: cover; + image-rendering: pixelated; +} + +.canvas-holder { + padding-top: 4vh; + padding-bottom: 4vh; +} +.canvas-holder canvas { + padding: 0; + margin: auto; + display: block; + height: 90vh; + width: auto; + border: 3px solid grey; + border-radius: 5px; + max-width: 100%; + background-color: rgba(0, 0, 25, 0.9); +} + +.game-hud { + position: absolute; + top: 0; + left: 50%; + transform: translate(-50%, -5px); + z-index: 1; + + background-color: rgba(255,255,255,0.5); + padding-left: 10px; + padding-right: 10px; + padding-top: 3px; + border: 1px solid white; + border-radius: 5px; +} + +.game-hud p { + margin: 0; + padding: 0; + font-size: 3vh; + text-align: center; +} + +.menu { + text-align:center; + max-width: 400px; + left: 50%; + top: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + position:absolute; + + background-color: rgba(200,200,255,0.90); + border: 1px solid #fff; + border-radius: 5px; + padding: 12px; + min-width: 400px; +} + +.menu-button { + background-color: #fff; + border-radius: 5px; + padding: 12px; + margin-bottom: 8px; + cursor: pointer; +} + +.menu-button:hover { + background-color: #d0d0d0; +}
\ No newline at end of file diff --git a/Homework/cs5410/centipede/assets/images/centipede-assets.png b/Homework/cs5410/centipede/assets/images/centipede-assets.png Binary files differnew file mode 100644 index 0000000..20b9eac --- /dev/null +++ b/Homework/cs5410/centipede/assets/images/centipede-assets.png diff --git a/Homework/cs5410/centipede/assets/sounds/enemy_hit.wav b/Homework/cs5410/centipede/assets/sounds/enemy_hit.wav Binary files differnew file mode 100644 index 0000000..1b9c53f --- /dev/null +++ b/Homework/cs5410/centipede/assets/sounds/enemy_hit.wav diff --git a/Homework/cs5410/centipede/assets/sounds/laser.wav b/Homework/cs5410/centipede/assets/sounds/laser.wav Binary files differnew file mode 100644 index 0000000..16159a4 --- /dev/null +++ b/Homework/cs5410/centipede/assets/sounds/laser.wav diff --git a/Homework/cs5410/centipede/assets/sounds/mushroom_hit.wav b/Homework/cs5410/centipede/assets/sounds/mushroom_hit.wav Binary files differnew file mode 100644 index 0000000..ef26432 --- /dev/null +++ b/Homework/cs5410/centipede/assets/sounds/mushroom_hit.wav diff --git a/Homework/cs5410/centipede/css/style.css b/Homework/cs5410/centipede/css/style.css new file mode 100644 index 0000000..7142e28 --- /dev/null +++ b/Homework/cs5410/centipede/css/style.css @@ -0,0 +1,63 @@ +body { + margin: 0; + font-family: Lucida Sans Typewriter,Lucida Console,monaco,Bitstream Vera Sans Mono,monospace; +} + +.canvas-holder canvas { + padding: 0; + margin: auto; + display: block; + height: 100vh; + width: auto; + max-width: 100%; + background-color: black; +} + +.game-hud { + position: absolute; + top: 0; + left: 50%; + transform: translate(-50%, -5px); + z-index: 1; + + background-color: rgba(255,255,255,0.5); + padding-left: 10px; + padding-right: 10px; + padding-top: 3px; + border: 1px solid white; + border-radius: 5px; +} + +.game-hud p { + margin: 0; + padding: 0; + font-size: 3vh; + text-align: center; +} + +.menu { + text-align:center; + left: 50%; + top: 50%; + -webkit-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + position:absolute; + + background-color: rgba(255,255,255,0.75); + border: 1px solid #fff; + border-radius: 5px; + padding: 12px; + min-width: 400px; +} + +.menu-button { + background-color: #fff; + border-radius: 5px; + padding: 12px; + margin-bottom: 8px; + cursor: pointer; +} + +.menu-button:hover { + background-color: #d0d0d0; +}
\ No newline at end of file diff --git a/Homework/cs5410/centipede/index.html b/Homework/cs5410/centipede/index.html new file mode 100644 index 0000000..3d73470 --- /dev/null +++ b/Homework/cs5410/centipede/index.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <title>Centipede</title> + <link rel="stylesheet" href="css/style.css"> + </head> + + <body> + <div style="text-align:center"> + <div class="canvas-holder"> + <canvas id="game-canvas" width="1400" height="1000"></canvas> + <div class="game-hud" id="hud"> + <p>Hello, world!</p> + </div> + <div class="menu" style="display: none" id="menu"> + </div> + </div> + </div> + + <script src="js/game/game.js"></script> + <script src="js/game/input.js"></script> + <script src="js/game/menu.js"></script> + <script src="js/game/graphics.js"></script> + <script src="js/game/sprites.js"></script> + <script src="js/game/sounds.js"></script> + <script src="js/game/object.js"></script> + <script src="js/game/objects/player.js"></script> + <script src="js/game/objects/mushroom.js"></script> + <script src="js/game/objects/bullet.js"></script> + <script src="js/game/objects/centipede.js"></script> + <script src="js/game/objects/explosion.js"></script> + <script src="js/game/objects/flea.js"></script> + <script src="js/game/objects/spider.js"></script> + <script src="js/game/objects/scorpion.js"></script> + + <script src="js/main.js"></script> + </body> +</html>
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/game.js b/Homework/cs5410/centipede/js/game/game.js new file mode 100644 index 0000000..30454f9 --- /dev/null +++ b/Homework/cs5410/centipede/js/game/game.js @@ -0,0 +1,39 @@ +const game = { + stopped: false, + width: document.getElementById('game-canvas').width, + height: document.getElementById('game-canvas').height, + level: 1, +}; + +game.resume = () => { + game.stopped = false; + game.lastTimeStamp = performance.now(); + menu.reRegisterKeys(); + requestAnimationFrame(gameLoop); +} + +game.resetObjects = () => { + game.player.x = game.width/2; + game.player.y = game.height/2; + game.bullets = []; + game.explosions = []; + game.mushroomDims = {width: 40, height: 40}; + game.mushrooms = game.Mushroom.generateMushrooms(game.mushroomDims); + game.centipede = game.Centipede({segments: Math.min(game.level*5 + 5, 15), startX: game.width/2, startY: 0, rot: 180, width: 40, height: 40, dx: 0.2, dy: 0}); + game.spiders = []; + game.fleas = []; + game.scorpions = []; +} + +game.gameOver = () => { + menu.showMenu(); + menu.setState('game-over'); + menu.addScore(game.score); + + menu.onHide = initialize; +} + +game.getObjects = () => [game.player, ...game.bullets, ...game.mushrooms, ...game.spiders, ...game.fleas, ...game.scorpions, game.centipede, ...game.explosions]; +game.getBulletCollidableObjects = () => [...game.mushrooms, ...game.spiders, ...game.fleas, ...game.scorpions, game.centipede]; +game.getMushroomCollidableObjects = () => [game.player, ...game.scorpions, game.centipede]; +game.getPlayerCollidableObjects = () => [...game.spiders, ...game.fleas, ...game.scorpions, game.centipede]
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/graphics.js b/Homework/cs5410/centipede/js/game/graphics.js new file mode 100644 index 0000000..3a0d7f9 --- /dev/null +++ b/Homework/cs5410/centipede/js/game/graphics.js @@ -0,0 +1,55 @@ +game.graphics = ( + (context) => { + context.imageSmoothingEnabled = false; + const clear = () => { + context.clearRect(0, 0, game.width, game.height); + }; + + const Sprite = ({sheetSrc, spriteX, spriteY, spriteWidth, spriteHeight, timePerFrame, cols, rows, numFrames, drawFunction}) => { + timePerFrame = timePerFrame ?? 100; + numFrames = numFrames ?? 1; + cols = cols ?? numFrames; + rows = rows ?? 1; + + let ready = false; + + let image; + if (sheetSrc) { + image = new Image(); + image.src = sheetSrc; + image.onload = () => { ready = true; }; + } + + let currentFrame = 0; + let lastTime = performance.now(); + + let draw; + if (!drawFunction) { + draw = (_elapsedTime, {x, y, rot, width, height}) => { + + if (ready) { + 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, timePerFrame, numFrames }; + } + + return { clear, Sprite }; + } +)(document.getElementById("game-canvas").getContext("2d"));
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/input.js b/Homework/cs5410/centipede/js/game/input.js new file mode 100644 index 0000000..6ceff13 --- /dev/null +++ b/Homework/cs5410/centipede/js/game/input.js @@ -0,0 +1,32 @@ +game.input = (() => { + "use strict"; + const Keyboard = () => { + const keys = {}; + const handlers = {}; + const keyPress = (event) => { + keys[event.key] = event.timeStamp; + }; + const keyRelease = (event) => { + delete keys[event.key]; + }; + const registerCommand = (key, handler) => { + handlers[key] = handler; + }; + const unregisterCommand = (key) => { + delete handlers[key]; + } + const update = (elapsedTime) => { + for (let key in keys) { + if (keys.hasOwnProperty(key)) { + if (handlers[key]) { + handlers[key](elapsedTime); + } + } + } + }; + window.addEventListener("keydown", keyPress); + window.addEventListener("keyup", keyRelease); + return {keys, handlers, registerCommand, unregisterCommand, update}; + } + return { Keyboard }; +})();
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/menu.js b/Homework/cs5410/centipede/js/game/menu.js new file mode 100644 index 0000000..d4a7fe9 --- /dev/null +++ b/Homework/cs5410/centipede/js/game/menu.js @@ -0,0 +1,143 @@ +const menu = {}; +menu.initialize = () => { + menu.scores = localStorage.getItem("scores") ? JSON.parse(localStorage.getItem("scores")) : []; + menu.state = "main"; + + menu.controls = localStorage.getItem("controls") ? JSON.parse(localStorage.getItem("controls")) : { + "moveUp": "ArrowUp", + "moveDown": "ArrowDown", + "moveLeft": "ArrowLeft", + "moveRight": "ArrowRight", + "shoot": " ", + }; +} + +menu.setState = (state) => { + menu.state = state; + menu.draw(); +} + +menu.escapeEventListener = (e) => { + if (e.key == "Escape") { + menu.setState('main'); + menu.draw(); + } +} + +menu.showMenu = () => { + menu.draw(); + game.stopped = true; + window.addEventListener("keydown", menu.escapeEventListener); +} + +menu.reRegisterKeys = () => { + Object.keys(game.keyboard.handlers).map(key => game.keyboard.unregisterCommand(key)); + game.keyboard.registerCommand(menu.controls.moveUp, game.player.moveUp); + game.keyboard.registerCommand(menu.controls.moveDown, game.player.moveDown); + game.keyboard.registerCommand(menu.controls.moveLeft, game.player.moveLeft); + game.keyboard.registerCommand(menu.controls.moveRight, game.player.moveRight); + game.keyboard.registerCommand(menu.controls.shoot, game.player.shoot); + game.keyboard.registerCommand("Escape", menu.showMenu); + localStorage.setItem("controls", JSON.stringify(menu.controls)); +} + +menu.addScore = (score) => { + menu.scores.push(score); + menu.scores.sort((a, b) => b - a); + localStorage.setItem("scores", JSON.stringify(menu.scores)); +} + +menu.hide = () => { + const menuElement = document.getElementById("menu"); + menuElement.style.display = "none"; + menu.reRegisterKeys(); + window.removeEventListener("keydown", menu.escapeEventListener); + if (menu.onHide) { + menu.onHide(); + menu.onHide = null; + } + game.resume(); +} + +menu.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; + } + menu.controls[action] = event.key; + element.innerHTML = event.key; + } + window.addEventListener("keydown", handleKey); +} + +menu.draw = () => { + const menuElement = document.getElementById("menu"); + menuElement.style.display = "block"; + menuElement.innerHTML = `<h1>Centipede</h1>`; + if (menu.state == "main") { + menuElement.innerHTML += ` + <div class='menu-button' onclick='menu.setState("controls")'>Change Controls</div> + <div class='menu-button' onclick='menu.setState("credits")'>Credits</div> + <div class='menu-button' onclick='menu.setState("scores")'>High Scores</div> + `; + } + else if (menu.state == "controls") { + menuElement.innerHTML += ` + <div> + <p> + Move left: <button id="moveLeft" onfocus='menu.listenFor("moveLeft", "moveLeft")'>${menu.controls.moveLeft}</button> + <br> + Move right: <button id="moveRight" onfocus='menu.listenFor("moveRight", "moveRight")'>${menu.controls.moveRight}</button> + <br> + Move up: <button id="moveUp" onfocus='menu.listenFor("moveUp", "moveUp")'>${menu.controls.moveUp}</button> + <br> + Move down: <button id="moveDown" onfocus='menu.listenFor("moveDown", "moveDown")'>${menu.controls.moveDown}</button> + <br> + Shoot: <button id="shoot" onfocus='menu.listenFor("shoot", "shoot")'>${menu.controls.shoot}</button> + </p> + </div> + ` + } else if (menu.state == "credits") { + menuElement.innerHTML += ` + <div> + <p> + Sounds from <a href="https://opengameart.org/content/laser-fire">dklon</a> + <br> + Sprites from <a href="https://www.pngkit.com/view/u2w7r5u2e6u2a9r5_general-sprites-centipede-arcade-game-sprites/">PNGKit</a> + <br> + Some code from <a href="https://www.usu.edu/directory/?person=56DB0BFCCAEECEC8D5">Dr. Mathias</a> + <br> + Developed by Logan Hunt + </p> + </div> + ` + } else if (menu.state == "scores") { + menuElement.innerHTML += ` + <div> + <p> + ${menu.scores.map((score, index) => `${index + 1}: ${score}<br>`).join("")} + </p> + </div> + ` + } else if (menu.state == "game-over") { + menuElement.innerHTML += ` + <div> + <p> + Game Over + <br> + Your final score was: ${game.score} + </div> + ` + } + + menuElement.innerHTML += "<div class='menu-button' onclick='menu.hide()'>Resume Game</div>" + if (menu.state !== "main") { + menuElement.innerHTML += "<div class='menu-button' onclick='menu.setState(\"main\")'>Back</div>" + } +} + +menu.initialize();
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/object.js b/Homework/cs5410/centipede/js/game/object.js new file mode 100644 index 0000000..1e22ec6 --- /dev/null +++ b/Homework/cs5410/centipede/js/game/object.js @@ -0,0 +1,51 @@ +game.Object = (object) => { + object.dx = object.dx ?? 0; + object.dy = object.dy ?? 0; + object.rot = object.rot ?? 0; + object.drot = object.drot ?? 0; + object.alive = object.alive ?? true; + + object.poisonedTimer = object.poisonedTimer ?? 4000; + object.poisoned = false; + object.elapsedPoisonedTimer = 0; + object.poison = () => { + object.poisoned = true; + object.elapsedPoisonedTimer = 0; + } + + object.intersects = (other) => { + if (object.x + object.width <= other.x) { + return false; + } + if (object.x >= other.x + other.width) { + return false; + } + if (object.y + object.height <= other.y) { + return false; + } + if (object.y >= other.y + other.height) { + return false; + } + return true; + } + + object.update = (elapsedTime) => { + if (object.poisoned && object.y >= game.height - object.height) { + object.elapsedPoisonedTimer += elapsedTime; + if (object.elapsedPoisonedTimer > object.poisonedTimer) { + object.poisoned = false; + object.elapsedPoisonedTimer = 0; + } + } + + object.x += (object.poisoned ? 0 : object.dx)*elapsedTime; + object.y += (object.poisoned ? 0.2 : object.dy)*elapsedTime; + object.rot += object.drot*elapsedTime; + }; + + object.draw = (elapsedTime) => { + object.sprite.draw(elapsedTime, object); + }; + + return object; +}; diff --git a/Homework/cs5410/centipede/js/game/objects/bullet.js b/Homework/cs5410/centipede/js/game/objects/bullet.js new file mode 100644 index 0000000..5791aca --- /dev/null +++ b/Homework/cs5410/centipede/js/game/objects/bullet.js @@ -0,0 +1,11 @@ +game.Bullet = (spec) => { + const object = game.Object(spec); + const parentUpdate = object.update; + object.update = (elapsedTime) => { + parentUpdate(elapsedTime); + if (object.y < 0) { + object.alive = false; + } + }; + return object; +}
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/objects/centipede.js b/Homework/cs5410/centipede/js/game/objects/centipede.js new file mode 100644 index 0000000..7e4c8f6 --- /dev/null +++ b/Homework/cs5410/centipede/js/game/objects/centipede.js @@ -0,0 +1,105 @@ +game.CentipedePiece = (spec) => { + const object = game.Object(spec); + object.poisonedTimer = 1000; + const parentUpdate = object.update; + object.turningState = { + turning: false, + turnDirectionY: 1, + objectStateBeforeTurn: null, + }; + object.turn = () => { + object.turningState.objectStateBeforeTurn = {dx: object.dx, dy: object.dy, x: object.x, y: object.y}; + object.turningState.turning = true; + if (object.y >= game.height - object.height) { + object.turningState.turnDirectionY = -1; + } + if (object.y <= 0) { + object.turningState.turnDirectionY = 1; + } + }; + object.update = (elapsedTime) => { + if (object.poisoned) { + object.turningState.turning = false; + } + else if ((object.x+object.width > game.width || object.x < 0) && !object.turningState.turning) { + object.x = Math.min(Math.max(object.x, 0), game.width - object.width); + object.turn(); + } + if (object.turningState.turning) { + object.dx = 0; + object.dy = Math.abs(object.turningState.objectStateBeforeTurn.dx) * object.turningState.turnDirectionY; + object.rot = object.dy > 0 ? -90 : 90; + if (Math.abs(object.turningState.objectStateBeforeTurn.y - object.y) >= object.height) { + object.y = object.turningState.objectStateBeforeTurn.y + object.height * object.turningState.turnDirectionY; + object.dx = -object.turningState.objectStateBeforeTurn.dx; + object.rot = object.dx > 0 ? 180 : 0; + object.dy = 0; + object.turningState.turning = false; + } + } + parentUpdate(elapsedTime); + object.y = Math.min(Math.max(object.y, 0), game.height - object.height); + }; + object.onMushroomCollision = (mushroom) => { + if (mushroom.poisoned && object.dy === 0) { + object.poison(); + return; + } + if (!object.turningState.turning) { + if (mushroom.x < object.x && object.dx > 0) { + return; + } + object.turn(); + } + } + return object; +} + +game.Centipede = (spec) => { + const segments = [ + ...Array(spec.segments).fill(0).map((_, i) => game.CentipedePiece({...spec, x: spec.startX - spec.width*(i+1), y: spec.startY, sprite: game.sprites.centipedeBody})), + game.CentipedePiece({...spec, x: spec.startX, y: spec.startY, sprite: game.sprites.centipedeHead}), + ]; + + const update = (elapsedTime) => { + segments.map((segment) => segment.update(elapsedTime)); + } + + const draw = (elapsedTime) => { + segments.map((segment) => segment.draw(elapsedTime)); + } + + const intersects = (object) => { + return segments.filter((segment) => segment.intersects(object)).length; + } + + const onBulletCollision = (bullet) => { + if (bullet.alive) { + const segment = segments.find((segment) => segment.intersects(bullet)); + const segmentIndex = segments.indexOf(segment); + + const {mushX, mushY} = game.Mushroom.toMushCoords(segment); + game.explosions.push(game.Explosion({...game.Mushroom.toGameCoords({mushX, mushY}), width: segment.width, height: segment.height, sprite: game.sprites.explosionSmall})); + if (!game.mushrooms.find((mushroom) => mushroom.mushX === mushX && mushroom.mushY === mushY)) { + game.mushrooms.push(game.Mushroom({mushX, mushY, ...game.mushroomDims})); + } + game.score += segment.sprite === game.sprites.centipedeHead ? 20 : 5; + game.sounds.enemy_hit.load(); + game.sounds.enemy_hit.play(); + segments.splice(segmentIndex, 1); + } + bullet.alive = false; + } + + const onMushroomCollision = (mushroom) => { + segments.find((segment) => segment.intersects(mushroom)).onMushroomCollision(mushroom); + } + + const onPlayerCollision = (player) => { + player.kill(); + } + + const alive = () => segments.length ? true : false; + + return {update, draw, segments, intersects, onBulletCollision, onMushroomCollision, onPlayerCollision, alive}; +}
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/objects/explosion.js b/Homework/cs5410/centipede/js/game/objects/explosion.js new file mode 100644 index 0000000..f38d820 --- /dev/null +++ b/Homework/cs5410/centipede/js/game/objects/explosion.js @@ -0,0 +1,14 @@ +game.Explosion = (spec) => { + const object = game.Object(spec); + let explosionTime = 0; + const parentUpdate = object.update; + object.update = (elapsedTime) => { + parentUpdate(elapsedTime); + explosionTime += elapsedTime; + + if (explosionTime > (object.sprite.numFrames * object.sprite.timePerFrame)) { + object.alive = false; + } + } + return object; +}
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/objects/flea.js b/Homework/cs5410/centipede/js/game/objects/flea.js new file mode 100644 index 0000000..23479cc --- /dev/null +++ b/Homework/cs5410/centipede/js/game/objects/flea.js @@ -0,0 +1,49 @@ +game.Flea = (spec) => { + const object = game.Object(spec); + const parentUpdate = object.update; + object.mushroomCoords = game.Mushroom.toMushCoords(object); + object.update = (elapsedTime) => { + const newMushroomCoords = game.Mushroom.toMushCoords(object); + if (newMushroomCoords.mushY !== object.mushroomCoords.mushY || newMushroomCoords.mushX !== object.mushroomCoords.mushX) { + if (Math.random() < Math.min(0.15 + 0.05*game.level, 0.7)) { + if (!game.mushrooms.find((mushroom) => mushroom.mushX === newMushroomCoords.mushX && mushroom.mushY === newMushroomCoords.mushY)) { + game.mushrooms.push(game.Mushroom({...newMushroomCoords, ...game.mushroomDims})); + } + } + object.mushroomCoords = newMushroomCoords; + } + parentUpdate(elapsedTime); + }; + + object.onMushroomCollision = (mushroom) => { + if (mushroom.poisoned) { + mushroom.state = 0; + object.poison(); + } + } + + object.explode = () => { + game.explosions.push(game.Explosion({x: object.x, y: object.y, width: object.width, height: object.height, sprite: game.sprites.explosionSmall})); + game.sounds.enemy_hit.load(); + game.sounds.enemy_hit.play(); + } + + object.onBulletCollision = (bullet) => { + game.score += 20; + object.alive = false; + object.explode(); + } + + object.onPlayerCollision = (player) => { + object.alive = false; + player.kill(); + object.explode(); + } + + object.onBulletCollision = (bullet) => { + object.explode(); + object.alive = false; + } + + return object; +}
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/objects/mushroom.js b/Homework/cs5410/centipede/js/game/objects/mushroom.js new file mode 100644 index 0000000..b1f4787 --- /dev/null +++ b/Homework/cs5410/centipede/js/game/objects/mushroom.js @@ -0,0 +1,43 @@ +game.Mushroom = (spec) => { + spec.state = spec.state ?? 4; + const {mushX, mushY} = spec; + const objectSpec = {...spec}; + objectSpec.x = mushX * objectSpec.width; + objectSpec.y = mushY * objectSpec.height; + const object = {...spec, ...game.Object(objectSpec)}; + object.onBulletCollision = (bullet) => { + if (bullet.alive) { + object.state--; + game.score += 5; + game.sounds.mushroom_hit.load(); + game.sounds.mushroom_hit.play(); + } + bullet.alive = false; + }; + object.draw = (elapsedTime) => { + if (object.state) { + object.sprite = object.poisoned ? game.sprites.poisonMushrooms[object.state-1] : game.sprites.regularMushrooms[object.state-1]; + object.sprite.draw(elapsedTime, object); + } + } + return object; +}; + +game.Mushroom.toMushCoords = (coords) => { + return {mushX: Math.ceil(coords.x / game.mushroomDims.width), mushY: Math.ceil(coords.y / game.mushroomDims.height)}; +} + +game.Mushroom.toGameCoords = (mushCoords) => { + return {x: mushCoords.mushX * game.mushroomDims.width, y: mushCoords.mushY * game.mushroomDims.height}; +} + +game.Mushroom.generateMushrooms = (mushroomSpec) => { + const mushPositions = new Set(); + for (let i = 0; i < Math.max(Math.random(), 0.05) * game.height / mushroomSpec.height * game.width / mushroomSpec.width * game.level * 0.5; i++) { + mushPositions.add(JSON.stringify([Math.floor(Math.random() * game.width / mushroomSpec.width), Math.max(1, Math.floor(Math.random() * (game.height / mushroomSpec.height - 3)))])); + } + return Array.from(mushPositions).map((pos) => { + const [mushX, mushY] = JSON.parse(pos); + return game.Mushroom({...mushroomSpec, mushX, mushY}); + }); +} diff --git a/Homework/cs5410/centipede/js/game/objects/player.js b/Homework/cs5410/centipede/js/game/objects/player.js new file mode 100644 index 0000000..a1f6ea0 --- /dev/null +++ b/Homework/cs5410/centipede/js/game/objects/player.js @@ -0,0 +1,63 @@ +game.Player = (spec) => { + const object = game.Object(spec); + object.poisonedTimer = 4000; + object.elapsedPoisonedTimer = 0; + object.poisoned = false; + object.bulletTimer = spec.bulletTimer ?? 150; + object.maxPlayerHeight = spec.maxPlayerHeight ?? game.height - object.height*6; + object.elapsedBulletTimer = 0; + object.lives = spec.lives ?? 3; + + const parentUpdate = object.update; + object.update = (elapsedTime) => { + parentUpdate(elapsedTime); + object.x = Math.max(0, Math.min(object.x, game.width - object.width)); + object.y = Math.max(object.maxPlayerHeight, Math.min(object.y, game.height - object.height)); + object.dx = object.dy = 0; + object.elapsedBulletTimer += elapsedTime; + }; + object.moveUp = () => { + object.dy = -0.75; + } + object.moveDown = () => { + object.dy = 0.75; + } + object.moveLeft = () => { + object.dx = -0.5; + } + object.moveRight = () => { + object.dx = 0.5; + } + + object.shoot = () => { + if (object.elapsedBulletTimer > object.bulletTimer) { + object.elapsedBulletTimer = 0; + game.bullets.push(game.Bullet({x: object.x + object.width/2 - 5, y: object.y-object.height/2, dx: 0, dy: -1.5, width: 5, height: 50, sprite: game.sprites.bullet})); + game.sounds.laser.load(); + game.sounds.laser.play(); + } + } + + object.onMushroomCollision = (mushroom) => { + if (mushroom.poisoned) { + mushroom.state = 0; + object.poison(); + } + if (mushroom.x > object.x) { + object.x = mushroom.x - mushroom.width; + } + if (mushroom.x < object.x) { + object.x = mushroom.x + mushroom.width; + } + } + + object.kill = () => { + object.lives--; + game.resetObjects(); + if (object.lives == 0) { + game.gameOver(); + } + } + + return object; +}
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/objects/scorpion.js b/Homework/cs5410/centipede/js/game/objects/scorpion.js new file mode 100644 index 0000000..036db14 --- /dev/null +++ b/Homework/cs5410/centipede/js/game/objects/scorpion.js @@ -0,0 +1,32 @@ +game.Scorpion = (spec) => { + const object = game.Object(spec); + + const parentUpdate = object.update; + object.update = (elapsedTime) => { + parentUpdate(elapsedTime); + }; + + object.explode = () => { + game.explosions.push(game.Explosion({x: object.x, y: object.y, width: object.width, height: object.height, sprite: game.sprites.explosionBig})); + game.sounds.enemy_hit.load(); + game.sounds.enemy_hit.play(); + } + + object.onBulletCollision = (bullet) => { + game.score += 100; + object.alive = false; + object.explode(); + } + + object.onPlayerCollision = (player) => { + object.alive = false; + player.kill(); + object.explode(); + } + + object.onMushroomCollision = (mushroom) => { + mushroom.poisoned = true; + } + + return object; +}
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/objects/spider.js b/Homework/cs5410/centipede/js/game/objects/spider.js new file mode 100644 index 0000000..732c0a3 --- /dev/null +++ b/Homework/cs5410/centipede/js/game/objects/spider.js @@ -0,0 +1,45 @@ +game.Spider = (spec) => { + const object = game.Object(spec); + + const parentUpdate = object.update; + + object.randomizeVel = () => { + object.dx = Math.min(Math.random(), 0.25 + 0.05*game.level) * (Math.random() > 0.5 ? 1 : -1); + object.dy = Math.min(Math.random(), 0.25 + 0.05*game.level) * (Math.random() > 0.5 ? 1 : -1); + } + + object.update = (elapsedTime) => { + if (Math.random() < 0.01*game.level) { + object.randomizeVel(); + } + if (object.x < 0 || object.x > game.width - object.width) { + object.dx = -object.dx; + } + if (object.y < 0 || object.y > game.height - object.height) { + object.dy = -object.dy; + } + object.x = Math.max(0, Math.min(game.width - object.width, object.x)); + object.y = Math.max(0, Math.min(game.height - object.height, object.y)); + parentUpdate(elapsedTime); + }; + + object.explode = () => { + game.explosions.push(game.Explosion({x: object.x, y: object.y, width: object.width, height: object.height, sprite: game.sprites.explosionBig})); + game.sounds.enemy_hit.load(); + game.sounds.enemy_hit.play(); + } + + object.onBulletCollision = (bullet) => { + game.score += 150; + object.alive = false; + object.explode(); + } + + object.onPlayerCollision = (player) => { + object.alive = false; + player.kill(); + object.explode(); + } + + return object; +}
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/sounds.js b/Homework/cs5410/centipede/js/game/sounds.js new file mode 100644 index 0000000..584dbd3 --- /dev/null +++ b/Homework/cs5410/centipede/js/game/sounds.js @@ -0,0 +1,5 @@ +game.sounds = { + mushroom_hit: new Audio("assets/sounds/mushroom_hit.wav"), + enemy_hit: new Audio("assets/sounds/enemy_hit.wav"), + laser: new Audio("assets/sounds/laser.wav"), +}
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/game/sprites.js b/Homework/cs5410/centipede/js/game/sprites.js new file mode 100644 index 0000000..3cdd77f --- /dev/null +++ b/Homework/cs5410/centipede/js/game/sprites.js @@ -0,0 +1,120 @@ +game.sprites = { + centipedeHead: game.graphics.Sprite({ + sheetSrc: "assets/images/centipede-assets.png", + spriteX: 0, + spriteY: 0, + spriteWidth: 40, + spriteHeight: 40, + numFrames: 4, + timePerFrame: 100, + }), + centipedeBody: game.graphics.Sprite({ + sheetSrc: "assets/images/centipede-assets.png", + spriteX: 0, + spriteY: 80, + spriteWidth: 40, + spriteHeight: 40, + numFrames: 4, + timePerFrame: 100, + }), + spider: game.graphics.Sprite({ + sheetSrc: "assets/images/centipede-assets.png", + spriteX: 0, + spriteY: 160, + spriteWidth: 80, + spriteHeight: 40, + numFrames: 8, + timePerFrame: 100, + cols: 4, + rows: 2, + }), + flea: game.graphics.Sprite({ + sheetSrc: "assets/images/centipede-assets.png", + spriteX: 320, + spriteY: 160, + spriteWidth: 45, + spriteHeight: 40, + numFrames: 4, + timePerFrame: 500, + cols: 2, + rows: 2, + }), + scorpion: game.graphics.Sprite({ + sheetSrc: "assets/images/centipede-assets.png", + spriteX: 0, + spriteY: 280, + spriteWidth: 80, + spriteHeight: 40, + numFrames: 4, + timePerFrame: 500, + cols: 4, + }), + ship: game.graphics.Sprite({ + sheetSrc: "assets/images/centipede-assets.png", + spriteX: 0, + spriteY: 400, + spriteWidth: 40, + spriteHeight: 40, + numFrames: 1, + timePerFrame: 0, + cols: 1, + rows: 1 + }), + bullet: game.graphics.Sprite({ + drawFunction: (_elapsedTime, {x, y, rot, width, height}, context) => { + 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 fillStyle = context.fillStyle; + context.fillStyle = "#FF0000"; + context.fillRect(x, y, width, height); + context.fillStyle = fillStyle; + context.restore(); + } + }), + explosionBig: game.graphics.Sprite({ + sheetSrc: "assets/images/centipede-assets.png", + spriteX: 0, + spriteY: 320, + numFrames: 8, + spriteWidth: 80, + spriteHeight: 40, + cols: 4, + rows: 2, + timePerFrame: 30, + }), + explosionSmall: game.graphics.Sprite({ + sheetSrc: "assets/images/centipede-assets.png", + spriteX: 320, + spriteY: 320, + numFrames: 6, + spriteWidth: 40, + spriteHeight: 40, + cols: 3, + rows: 2, + timePerFrame: 30, + }), + regularMushrooms: [3,2,1,0].map(i => + game.graphics.Sprite({ + sheetSrc: "assets/images/centipede-assets.png", + spriteX: 320 + i*40, + spriteY: 0, + numFrames: 1, + spriteWidth: 40, + spriteHeight: 40, + timePerFrame: 0, + }) + ), + poisonMushrooms: [3,2,1,0].map(i => + game.graphics.Sprite({ + sheetSrc: "assets/images/centipede-assets.png", + spriteX: 320 + i*40, + spriteY: 40, + numFrames: 1, + spriteWidth: 40, + spriteHeight: 40, + timePerFrame: 0, + }) + ) +};
\ No newline at end of file diff --git a/Homework/cs5410/centipede/js/main.js b/Homework/cs5410/centipede/js/main.js new file mode 100644 index 0000000..f4c954d --- /dev/null +++ b/Homework/cs5410/centipede/js/main.js @@ -0,0 +1,75 @@ +let handleInput; +const initialize = () => { + game.level = 1; + game.score = 0; + game.totalTime = 0; + + game.keyboard = game.input.Keyboard(); + handleInput = game.keyboard.update; + + game.lastTimeStamp = performance.now(); + + game.player = game.Player({x: game.width/2 - 20, y: game.height-40, width: 40, height: 40, sprite: game.sprites.ship}); + + game.resetObjects(); +}; + +const update = (elapsedTime) => { + game.totalTime += elapsedTime; + + game.mushrooms.map((mushroom) => game.getMushroomCollidableObjects().filter((object) => object.intersects(mushroom))).map((objects, i) => { + objects.map((object => object.onMushroomCollision ? object.onMushroomCollision(game.mushrooms[i]) : null)); + }); + game.bullets.map((bullet) => game.getBulletCollidableObjects().filter((object) => object.intersects(bullet))).map((objects, i) => { + objects.map((object) => object.onBulletCollision ? object.onBulletCollision(game.bullets[i]) : null) + }) + game.getPlayerCollidableObjects().map((object) => object.intersects(game.player) ? object.onPlayerCollision(game.player) : null); + + game.bullets = game.bullets.filter((bullet) => bullet.alive); + game.spiders = game.spiders.filter((spider) => spider.alive); + game.explosions = game.explosions.filter((explosion) => explosion.alive); + game.mushrooms = game.mushrooms.filter((mushroom) => mushroom.state > 0); + game.scorpions = game.scorpions.filter((scorpion) => scorpion.x >= 0 && scorpion.x <= game.width - scorpion.width && scorpion.alive); + game.fleas = game.fleas.filter((flea) => flea.y < game.height - flea.height && flea.alive); + game.getObjects().map((object) => object.update(elapsedTime)); + + if (Math.random() < 0.002 * game.level && game.spiders.length < game.level) { + game.spiders.push(game.Spider({x: game.width/2, y: 0, width: 80, height: 40, rot: 0, dx: -0.2, dy: 0, sprite: game.sprites.spider})); + } + if (Math.random() < 0.001 * game.level && game.fleas.length < game.level) { + game.fleas.push(game.Flea({x: Math.floor(Math.random() * game.width / game.mushroomDims.width) * game.mushroomDims.width, y: 0, width: 40, height: 40, rot: 0, dx: 0, dy: 0.2, sprite: game.sprites.flea})); + } + if (Math.random() < 0.001 * game.level && game.scorpions.length < game.level) { + game.scorpions.push(game.Scorpion({y: Math.floor(Math.random() * game.height / game.mushroomDims.height) * game.mushroomDims.height, x: 0, width: 80, height: 40, rot: 0, dx: 0.2, dy: 0, sprite: game.sprites.scorpion})); + } + + if (!game.centipede.alive()) { + game.resetObjects(); + game.level++; + game.score += game.level*400; + } +}; + +const render = (elapsedTime) => { + game.graphics.clear(); + game.getObjects().map((object) => object.draw(elapsedTime)); + + document.getElementById("hud").innerHTML = `Level: ${game.level} Lives: ${game.player.lives} Score: ${game.score} ${game.player.poisoned ? "<span style='color:green'>POISONED<span>" : ""}`; +}; + +const gameLoop = (time) => { + const elapsedTime = time - game.lastTimeStamp; + game.lastTimeStamp = time; + + handleInput(elapsedTime); + update(elapsedTime); + render(elapsedTime); + + if (!game.stopped) { + requestAnimationFrame(gameLoop); + } +}; + +initialize(); +menu.reRegisterKeys(); +requestAnimationFrame(gameLoop);
\ No newline at end of file diff --git a/Homework/cs5410/maize-maze/LICENSE.md b/Homework/cs5410/maize-maze/LICENSE.md new file mode 100644 index 0000000..47a36cd --- /dev/null +++ b/Homework/cs5410/maize-maze/LICENSE.md @@ -0,0 +1,21 @@ +Copyright (c) 2012-2022 Scott Chacon and others + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/Homework/cs5410/maize-maze/README.md b/Homework/cs5410/maize-maze/README.md new file mode 100644 index 0000000..d48c881 --- /dev/null +++ b/Homework/cs5410/maize-maze/README.md @@ -0,0 +1,9 @@ +# A Simple Maze Game + +Controls: ++ Arrow-keys / WASD / IJKL ++ h to toggle hint ++ p to toggle path ++ b to toggle breadcrumbs + + diff --git a/Homework/cs5410/maize-maze/css/styles.css b/Homework/cs5410/maize-maze/css/styles.css new file mode 100644 index 0000000..4c8aac0 --- /dev/null +++ b/Homework/cs5410/maize-maze/css/styles.css @@ -0,0 +1,24 @@ +body { + margin: 0; + padding: 0; + overflow: scroll; + /* Font from https://www.cssfontstack.com/Lucida-Console */ + font-family: Lucida Console,Lucida Sans Typewriter,monaco,Bitstream Vera Sans Mono,monospace; + background: radial-gradient(circle, transparent 20%, black 80vw), white; +} + +.canvas-holder canvas { + padding: 0; + margin: auto; + display: block; + height: 70vh; + width: auto; + max-width: 100%; +} + +.button { + border: 1px solid black; + border-radius: 5px; + padding: 5px; + cursor: pointer; +}
\ No newline at end of file diff --git a/Homework/cs5410/maize-maze/images/corn.png b/Homework/cs5410/maize-maze/images/corn.png Binary files differnew file mode 100644 index 0000000..f392216 --- /dev/null +++ b/Homework/cs5410/maize-maze/images/corn.png diff --git a/Homework/cs5410/maize-maze/images/fire.png b/Homework/cs5410/maize-maze/images/fire.png Binary files differnew file mode 100644 index 0000000..5e7b2ec --- /dev/null +++ b/Homework/cs5410/maize-maze/images/fire.png diff --git a/Homework/cs5410/maize-maze/images/popcorn.jpg b/Homework/cs5410/maize-maze/images/popcorn.jpg Binary files differnew file mode 100644 index 0000000..ad73e91 --- /dev/null +++ b/Homework/cs5410/maize-maze/images/popcorn.jpg diff --git a/Homework/cs5410/maize-maze/index.html b/Homework/cs5410/maize-maze/index.html new file mode 100644 index 0000000..36bae40 --- /dev/null +++ b/Homework/cs5410/maize-maze/index.html @@ -0,0 +1,75 @@ +<!DOCTYPE html> +<html> + <head> + <title>Amazeing Maize Maze</title> + <meta charset="utf-8"> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous"> + <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> + <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script> + <link href="css/styles.css" rel="stylesheet"> + </head> + <body> + + <div class="modal fade" id="help-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h5 class="modal-title" id="exampleModalLabel">Controls</h5> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body"> + <ul> + <li> + Move with arrow keys, WASD, or IJKL + </li> + <li> + h to toggle a hint + </li> + <li> + p to toggle the shortest path + </li> + <li> + b to toggle breadcrumbs + </li> + </ul> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> + </div> + </div> + </div> + </div> + + <div style="text-align: center"> + <h1>The A-maze-ing Maize Maze!</h1> + <p>Get to the fire to make popcorn! Current Time: <span style="color: green; font-size:24px" id="elapsed-time"></span> seconds</p> + <p> + <button type="button" class="button" data-toggle="modal" data-target="#help-modal"> + Show me the controls! + </button> + </p> + <p> + <a onclick="initialize(5)" class="button">5x5</a> + <a onclick="initialize(10)" class="button">10x10</a> + <a onclick="initialize(15)" class="button">15x15</a> + <a onclick="initialize(20)" class="button">20x20</a> + </p> + </div> + <div class="canvas-holder"> + <canvas id="canvas" width="1000" height="1000"></canvas> + </div> + <div style="text-align: center;"> + <br> + "Low" scores (player path length - shortest path length): + <div id="scores" style="max-height: 5vh; overflow-y: scroll"></div> + </div> + <script src="js/assets.js"></script> + <script src="js/helpers.js"></script> + <script src="js/json-ds.js"></script> + <script src="js/keyboard.js"></script> + <script src="js/maze.js"></script> + <script src="js/game.js"></script> + </body> +</html> diff --git a/Homework/cs5410/maize-maze/js/assets.js b/Homework/cs5410/maize-maze/js/assets.js new file mode 100644 index 0000000..54ea6bb --- /dev/null +++ b/Homework/cs5410/maize-maze/js/assets.js @@ -0,0 +1,8 @@ +const GOAL = new Image(); +GOAL.src = "./images/fire.png"; + +const PLAYER = new Image(); +PLAYER.src = "./images/corn.png"; + +const BACKGROUND = new Image(); +BACKGROUND.src = "./images/popcorn.jpg"; diff --git a/Homework/cs5410/maize-maze/js/game.js b/Homework/cs5410/maize-maze/js/game.js new file mode 100644 index 0000000..1249a20 --- /dev/null +++ b/Homework/cs5410/maize-maze/js/game.js @@ -0,0 +1,249 @@ +let canvas,context; + +const HEIGHT = 1000; +const WIDTH = 1000; + +// Next assignment: don't use so much effing global data +let n = 10; +let maze; + +let total_time; + +let player_pos = []; +let goal_pos = []; + +let myInput = input.Keyboard(); + +let show_player_path = false; +let player_path = []; +let show_next_move = false; +let show_shortest_path = false; +let shortest_path = []; + +let do_score_update = false; +let scores = []; +let initial_shortest_path_length = 0; +let current_score; + +let do_time_update = false; +let dom_time; +let elapsed_time; +let last_time; + +function render_maze(maze, n, dx, dy) { + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + context.beginPath(); + if (maze[i][j].left) { + context.moveTo(j * dx, i * dy); + context.lineTo(j * dx, (i + 1) * dy); + } + if (maze[i][j].right) { + context.moveTo((j + 1) * dx, i * dy); + context.lineTo((j + 1) * dx, (i + 1) * dy); + } + if (maze[i][j].top) { + context.moveTo(j * dx, i * dy); + context.lineTo((j + 1) * dx, i * dy); + } + if (maze[i][j].bottom) { + context.moveTo(j * dx, (i + 1) * dy); + context.lineTo((j + 1) * dx, (i + 1) * dy); + } + context.stroke(); + } + } +} + +function render_player(x, y, dx, dy) { + context.drawImage(PLAYER, x*dx, y*dy, dx, dy); +} + +function render_scores(scores) { + let html = scores.sort((a,b) => a-b).map((x,i) => `${i+1}: ${x}`).join('<br>'); + document.getElementById('scores').innerHTML = html; +} + +function render_time(total_time) { + document.getElementById('elapsed-time').innerHTML = total_time; +} + +function render_goal(x, y, dx, dy) { + context.drawImage(GOAL, x*dx, y*dy, dx, dy); +} + +function render_background(n, dx, dy) { + for (let x = 0; x < WIDTH; x += WIDTH/n) { + for (let y = 0; y < HEIGHT; y += HEIGHT/n) { + context.drawImage(BACKGROUND, x, y, dx, dy); + } + } +} + +function render_circle(x,y,r,color) { + context.beginPath(); + context.fillStyle = color; + context.arc(x, y, r, 0, 2*Math.PI); + context.fill(); + context.stroke(); +} + +function render_path(path, dx, dy, color) { + path.map((coord) => { + render_circle(coord[0]*dx + dx/2, coord[1]*dy + dy/2, Math.min(dx/4, dy/4), color); + }); +} + +function render(elapsed) { + const dx = WIDTH / n; + const dy = HEIGHT / n; + + context.fillStyle = 'rgba(255,255,255,255)'; + context.fillRect(0, 0, canvas.width, canvas.height); + context.rect(0, 0, canvas.width, canvas.height); + context.stroke(); + + render_background(n, dx, dy); + render_maze(maze, n, dx, dy); + + if (show_player_path) { + render_path(player_path, dx, dy, 'rgba(255, 0, 0, 255)'); + } + + if (show_shortest_path) { + render_path(shortest_path, dx, dy, 'rgba(255, 255, 0, 255)'); + } + + if (show_next_move && shortest_path.length>1) { + render_path([shortest_path[1]], dx, dy, 'rgba(255, 255, 0, 255)'); + } + + if (do_score_update) { + render_scores(scores); + } + if (do_time_update) { + render_time(dom_time); + } + + render_player(Math.floor(player_pos[0]), Math.floor(player_pos[1]), dx, dy); + render_goal(goal_pos[0], goal_pos[1], dx, dy); +} + +let key_actions_down = {}; +let key_actions = { + "move_up": ['ArrowUp', 'i', 'w'], + "move_right": ['ArrowRight', 'l', 'd'], + "move_down": ['ArrowDown', 'k', 's'], + "move_left": ['ArrowLeft', 'j', 'a'], + "breadcrumbs": ['b'], + "shortest_path": ['p'], + "hint": ['h'] +}; +function handle_input(input) { + if (input) { + if (any(key_actions['move_up'].map((x) => input.keys[x])) && !key_actions_down['move_up'] && player_pos[1] > 0) { + key_actions_down['move_up'] = true; + if (!maze[player_pos[1]][player_pos[0]].top) { + player_pos[1] -= 1; + } + } + if (any(key_actions['move_right'].map((x) => input.keys[x])) && !key_actions_down['move_right'] && player_pos[0] < n-1) { + key_actions_down['move_right'] = true; + if (!maze[player_pos[1]][player_pos[0]].right) { + player_pos[0] += 1; + } + } + if (any(key_actions['move_down'].map((x) => input.keys[x])) && !key_actions_down['move_down'] && player_pos[1] < n-1) { + key_actions_down['move_down'] = true; + if (!maze[player_pos[1]][player_pos[0]].bottom) { + player_pos[1] += 1; + } + } + if (any(key_actions['move_left'].map((x) => input.keys[x])) && !key_actions_down['move_left'] && player_pos[0] > 0) { + key_actions_down['move_left'] = true; + if (!maze[player_pos[1]][player_pos[0]].left) { + player_pos[0] -= 1; + } + } + if (input.keys['b'] && !key_actions_down['breadcrumbs']) { + key_actions_down['breadcrumbs'] = true; + show_player_path = !show_player_path; + } + if (input.keys['p'] && !key_actions_down['shortest_path']) { + key_actions_down['shortest_path'] = true; + show_shortest_path = !show_shortest_path; + } + if (input.keys['h'] && !key_actions_down['hint']) { + key_actions_down['hint'] = true; + show_next_move = !show_next_move; + } + Object.keys(key_actions).map((x) => { + if (key_actions_down[x] && !any(key_actions[x].map((y) => input.keys[y]))) { + key_actions_down[x] = false; + } + }); + } +} + +function update(elapsed) { + total_time += elapsed; + + if (do_score_update) { + do_score_update = false; + } + if (do_time_update) { + do_time_update = false; + } + + if (JSON.stringify(player_pos) !== JSON.stringify(player_path[player_path.length-1])) { + player_path.push([player_pos[0], player_pos[1]]); + shortest_path = solve_maze(maze, player_pos[0], player_pos[1], goal_pos[0], goal_pos[1]); + } + + if (total_time / 1000 > dom_time) { + dom_time = Math.floor(total_time/1000); + } + do_time_update = true; + + if (player_pos[0] == goal_pos[0] && player_pos[1] == goal_pos[1]) { + current_score = player_path.length - initial_shortest_path_length; + scores.push(current_score); + initialize(); + do_score_update = true; + } +} + +function initialize(new_n) { + if (new_n) { + n = new_n; + } + maze = generate_maze(n); + player_pos = [random_in_range(0, 2), random_in_range(0, 2)]; + goal_pos = [random_in_range(n-2, n), random_in_range(n-2, n)]; + player_path = []; + shortest_path = solve_maze(maze, player_pos[0], player_pos[1], goal_pos[0], goal_pos[1]); + initial_shortest_path_length = shortest_path.length; + total_time = 0; + current_score = 0; + dom_time = 0; + last_time = performance.now(); +} + +function game_loop(time_stamp) { + elapsed_time = time_stamp - last_time; + last_time = time_stamp; + handle_input(myInput); + update(elapsed_time); + render(elapsed_time); + + // Wow! Tail call recursion! /sarcasm + requestAnimationFrame(game_loop); +} + +window.onload = function() { + initialize(); + canvas = document.getElementById('canvas'); + context = canvas.getContext('2d'); + + game_loop(performance.now()); +} diff --git a/Homework/cs5410/maize-maze/js/helpers.js b/Homework/cs5410/maize-maze/js/helpers.js new file mode 100644 index 0000000..1bc6579 --- /dev/null +++ b/Homework/cs5410/maize-maze/js/helpers.js @@ -0,0 +1,18 @@ +function random_in_range(a,z) { + return Math.floor(Math.random() * (z - a) + a); +} + +function shuffle_array(a) { + for (let i = 0; i < a.length; i++) { + let j = random_in_range(0, i+1); + temp = a[i]; a[i] = a[j]; a[j] = temp; + } +} + +function sub(p1, p2) { + return [p1[0] - p2[0], p1[1] - p2[1]]; +} + +function any(l) { + return l.filter((x) => x).length > 0; +}
\ No newline at end of file diff --git a/Homework/cs5410/maize-maze/js/json-ds.js b/Homework/cs5410/maize-maze/js/json-ds.js new file mode 100644 index 0000000..feb0622 --- /dev/null +++ b/Homework/cs5410/maize-maze/js/json-ds.js @@ -0,0 +1,30 @@ +// If I were to rewrite this, I would use IEFE's - Dean was right about OO in JS +class JSONSet { + items = new Set(); + constructor(initial){ + if (initial) { + this.apply_set_function('add', initial); + } + } + apply_set_function(f_name, x) { + return this.items[f_name](JSON.stringify(x)); + } +} + +class JSONHash { + items = {}; + constructor(initial_key, initial_value){ + if (initial_key && initial_value) { + this.items[JSON.stringify(initial)] = initial_value; + } + } + set_value(key, value) { + this.items[JSON.stringify(key)] = value; + } + get_value(key) { + return this.items[JSON.stringify(key)]; + } + delete_value(key) { + delete this.items[JSON.stringify(key)]; + } +}
\ No newline at end of file diff --git a/Homework/cs5410/maize-maze/js/keyboard.js b/Homework/cs5410/maize-maze/js/keyboard.js new file mode 100644 index 0000000..2196834 --- /dev/null +++ b/Homework/cs5410/maize-maze/js/keyboard.js @@ -0,0 +1,22 @@ +// Shameless stolen code from "Process the Input" presentation +let input = (function() { + function Keyboard() { + let that = { + keys : {} + }; + function keyPress(e) { + that.keys[e.key] = e.timeStamp; + } + function keyRelease(e) { + delete that.keys[e.key]; + } + window.addEventListener('keydown', keyPress); + window.addEventListener('keyup', keyRelease); + + return that; + } + + return { + Keyboard : Keyboard + }; +}());
\ No newline at end of file diff --git a/Homework/cs5410/maize-maze/js/maze.js b/Homework/cs5410/maize-maze/js/maze.js new file mode 100644 index 0000000..8bede6b --- /dev/null +++ b/Homework/cs5410/maize-maze/js/maze.js @@ -0,0 +1,104 @@ +function get_neighbors(maze, x, y) { + let neighbor_indices = []; + if (!maze[y][x].left && x > 0) { + neighbor_indices.push([x-1,y]); + } + if (!maze[y][x].right && x < maze[0].length-1) { + neighbor_indices.push([x+1,y]); + } + if (!maze[y][x].top && y > 0) { + neighbor_indices.push([x,y-1]); + } + if (!maze[y][x].bottom && y < maze.length-1) { + neighbor_indices.push([x,y+1]); + } + return neighbor_indices; +} + +function new_cell() { + return { + left: false, + right: false, + top: false, + bottom: false + } +} + +function generate_maze(n) { + let grid = new Array(n).fill().map((x) => (new Array(n).fill().map(new_cell))); + + let point_sets = []; + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + point_sets.push(new JSONSet([j,i])) + } + } + + let edges = []; + for (let i = 0; i < n; i++) { + for (let j = 0; j < n; j++) { + if (i !== n-1) { + edges.push([[i,j],[i+1,j]]) + } + if (j !== n-1) { + edges.push([[i,j],[i,j+1]]) + } + } + } + shuffle_array(edges); + + let maze_edges = edges.map((x) => x); + + while (edges.length) { + let edge = edges.pop(); + + let set_inds = edge.map((i) => point_sets.findIndex((x) => x.apply_set_function('has', i))); + if (set_inds[0] == -1 || set_inds[1] == -1) { + throw new Error("Could not find correct index"); + } + if (set_inds[0] == set_inds[1]) { + continue; + } + + // Perform the union of the sets + for (let i of point_sets[set_inds[1]].items) { + point_sets[set_inds[0]].items.add(i); + } + + point_sets.splice(set_inds[1], 1); + maze_edges = maze_edges.filter((x) => x !== edge); + } + maze_edges.forEach((edge) => { + let direction = sub(edge[0], edge[1]); + if (direction[0] == -1) { + grid[edge[0][1]][edge[0][0]].right = grid[edge[1][1]][edge[1][0]].left = true; + } + else if (direction[1] == -1) { + grid[edge[0][1]][edge[0][0]].bottom = grid[edge[1][1]][edge[1][0]].top = true; + } + }) + return grid; +} + +function solve_maze(maze, x, y, end_x, end_y) { + let path = new JSONHash(); + let visited = new JSONSet(); + let queue = [[x,y]]; + while (queue.length) { + let cell = queue.shift(); + visited.apply_set_function('add', cell); + if (cell[0] == end_x && cell[1] == end_y) { + let sol_path = [[end_x, end_y]]; + while (sol_path[0][0] != x || sol_path[0][1] != y) { + sol_path.unshift(path.get_value(sol_path[0])); + } + return sol_path; + } + for (let i of get_neighbors(maze, cell[0], cell[1])) { + if (!visited.apply_set_function('has', i)) { + queue.push(i); + path.set_value(i, cell); + } + } + } +} diff --git a/Homework/cs5410/maize-maze/screenshot.png b/Homework/cs5410/maize-maze/screenshot.png Binary files differnew file mode 100644 index 0000000..965eb61 --- /dev/null +++ b/Homework/cs5410/maize-maze/screenshot.png |
