diff --git a/app/main.css b/app/main.css index e7c3ced..db620b8 100644 --- a/app/main.css +++ b/app/main.css @@ -100,6 +100,50 @@ table th { margin: 1em; padding: 1em; } +#app > main #home-menu #banner #card { + display: block; + max-width: 50%; + margin: 0 auto; +} +#app > main #home-menu #levels-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + list-style: none; + margin: 0.5em; + padding: 0; + justify-content: space-around; +} +#app > main #home-menu #levels-list li { + font-size: 1em; + flex: 0 0 15%; + margin: 0.5em; + height: 4em; + display: flex; + flex-direction: column; + box-shadow: 0.1rem 0.1rem 0 rgba(0, 0, 0, 0.1); + padding: 0.5em; +} +#app > main #home-menu #levels-list li button { + cursor: pointer; + color: #fff; + background: #c44; + border: 2px solid #ccc; + border-radius: 1rem; + box-shadow: 0.1rem 0.1rem 0 rgba(0, 0, 0, 0.5); +} +#app > main #home-menu #levels-list li button:hover { + border: 2px solid #fff; +} +#app > main #home-menu #levels-list li button:active { + border: 2px solid #888; +} +#app > main #home-menu #levels-list li span { + display: block; + margin-top: 1em; + font-size: 0.75em; + text-align: center; +} #app > main .ui-button { position: absolute; cursor: pointer; diff --git a/app/main.css.map b/app/main.css.map index 37a1cf6..c68d4e9 100644 --- a/app/main.css.map +++ b/app/main.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":"","sources":["../src/main.scss"],"names":[],"mappings":"AAWA;EACE;;;AAGF;EACE;EACA;EACA,YAlBM;EAmBN,OAdK;;;AAiBP;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAGF;EACE,OAxBQ;EAyBR;;AAEA;EACE,OA3BgB;;;AA+BpB;EACE;EACA;;AAEA;AAAA;EAEE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA,YAxDM;;;AA2DR;AAAA;EAEE;EACA;EACA;EACA,YAxDQ;EAyDR,OAhEM;;;AAoEN;EACE;EACA;;;AAIJ;EACE;;AAEA;EACE,OA/EI;EAgFJ;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YApGI;;AAuGN;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA","file":"main.css"} \ No newline at end of file +{"version":3,"sourceRoot":"","sources":["../src/main.scss"],"names":[],"mappings":"AAWA;EACE;;;AAGF;EACE;EACA;EACA,YAlBM;EAmBN,OAdK;;;AAiBP;AAAA;EAEE;EACA;EACA;EACA;EACA;;;AAGF;EACE,OAxBQ;EAyBR;;AAEA;EACE,OA3BgB;;;AA+BpB;EACE;EACA;;AAEA;AAAA;EAEE;;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA,YAxDM;;;AA2DR;AAAA;EAEE;EACA;EACA;EACA,YAxDQ;EAyDR,OAhEM;;;AAoEN;EACE;EACA;;;AAIJ;EACE;;AAEA;EACE,OA/EI;EAgFJ;;;AAIJ;EACE;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,YApGI;;AAuGN;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;;AAMA;EACE;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAIJ;EACE;EACA;EACA;EACA;;AAMR;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAIJ;EACE;EACA;;AAGF;EACE;EACA;;AAGF;EACE;EACA","file":"main.css"} \ No newline at end of file diff --git a/app/main.js b/app/main.js index a6de6b3..d4b1842 100644 --- a/app/main.js +++ b/app/main.js @@ -146,7 +146,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ AvO)\n/* harmony export */ });\n/* harmony import */ var _avo_constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @avo/constants */ \"./src/avo/constants.js\");\n/* harmony import */ var _avo_physics__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @avo/physics */ \"./src/avo/physics.js\");\n/* harmony import */ var _avo_levels__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @avo/levels */ \"./src/avo/levels.js\");\n/* harmony import */ var _avo_image_asset__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @avo/image-asset */ \"./src/avo/image-asset.js\");\n/* harmony import */ var _avo_json_asset__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @avo/json-asset */ \"./src/avo/json-asset.js\");\n/* harmony import */ var _avo_interaction__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @avo/interaction */ \"./src/avo/interaction/index.js\");\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n\n\n\n\n\n\nvar searchParams = new URLSearchParams(window.location.search);\nvar DEBUG = searchParams.get('debug') || false;\nvar STARTING_LEVEL = Number.isInteger(parseInt(searchParams.get('level'))) ? parseInt(searchParams.get('level')) - 1 : 0;\n\nvar AvO = /*#__PURE__*/function () {\n function AvO() {\n var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n _classCallCheck(this, AvO);\n\n var _args$width = args.width,\n width = _args$width === void 0 ? 24 * _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE : _args$width,\n _args$height = args.height,\n height = _args$height === void 0 ? 16 * _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE : _args$height;\n this.html = {\n main: document.getElementById('main'),\n canvas: document.getElementById('canvas'),\n homeMenu: document.getElementById('home-menu'),\n interactionMenu: document.getElementById('interaction-menu'),\n buttonHome: document.getElementById('button-home'),\n buttonFullscreen: document.getElementById('button-fullscreen'),\n buttonReload: document.getElementById('button-reload')\n };\n this.homeMenu = false;\n this.setHomeMenu(false);\n this.interactionMenu = false;\n this.setInteractionMenu(false);\n this.canvas2d = this.html.canvas.getContext('2d');\n this.canvasWidth = width;\n this.canvasHeight = height;\n this.camera = {\n target: null,\n // Target atom to follow. If null, camera is static.\n x: 0,\n y: 0\n };\n this.setupUI();\n this.initialised = false;\n this.assets = {\n // \"exampleImage\": new ImageAsset('assets/simple-bg.png'),\n // \"exampleJson\": new JsonAsset('assets/example.json'),\n 'cny2022': new _avo_image_asset__WEBPACK_IMPORTED_MODULE_3__.default('assets/cny2022-sprites.png')\n };\n this.secretAssets = {// \"secretImage\": new ImageAsset('secrets/simple-bg.png'),\n // \"secretJson\": new JsonAsset('secrets/example.json'),\n };\n this.hero = null;\n this.atoms = [];\n this.rules = {};\n this.levels = new _avo_levels__WEBPACK_IMPORTED_MODULE_2__.default(this);\n this.playerAction = _avo_constants__WEBPACK_IMPORTED_MODULE_0__.PLAYER_ACTIONS.IDLE;\n this.playerInput = {\n // Mouse/touchscreen input\n pointerStart: undefined,\n pointerCurrent: undefined,\n pointerEnd: undefined,\n // Keys that are currently being pressed.\n // keysPressed = { key: { duration, acknowledged } }\n keysPressed: {}\n };\n this.prevTime = null;\n this.nextFrame = window.requestAnimationFrame(this.main.bind(this));\n this.paused = false; // Pause gameplay\n }\n\n _createClass(AvO, [{\n key: \"initialisationCheck\",\n value: function initialisationCheck() {\n var _this = this;\n\n // Assets check\n var allAssetsReady = true;\n var numReadyAssets = 0;\n var numTotalAssets = 0;\n Object.keys(this.assets).forEach(function (id) {\n var asset = _this.assets[id];\n allAssetsReady = allAssetsReady && asset.ready;\n if (asset.ready) numReadyAssets++;\n numTotalAssets++;\n });\n Object.keys(this.secretAssets).forEach(function (id) {\n var secretAsset = _this.secretAssets[id];\n var secretAssetIsReady = secretAsset.ready || secretAsset.error;\n allAssetsReady = allAssetsReady && secretAssetIsReady;\n if (secretAssetIsReady) numReadyAssets++;\n numTotalAssets++;\n }); // Paint status\n\n this.canvas2d.clearRect(0, 0, this.canvasWidth, this.canvasHeight);\n this.canvas2d.textAlign = 'start';\n this.canvas2d.textBaseline = 'top';\n this.canvas2d.fillStyle = '#ccc';\n this.canvas2d.font = \"1em monospace\";\n this.canvas2d.fillText(\"Loading \".concat(numReadyAssets, \" / \").concat(numTotalAssets, \" \"), _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE);\n\n if (allAssetsReady) {\n // Clean up secret assets\n Object.keys(this.secretAssets).forEach(function (id) {\n if (_this.secretAssets[id].error) delete _this.secretAssets[id];\n }); // Let's go!\n\n this.initialised = true;\n this.showUI();\n this.levels.load(STARTING_LEVEL);\n }\n }\n /*\r\n Section: General Logic\r\n ----------------------------------------------------------------------------\r\n */\n\n }, {\n key: \"main\",\n value: function main(time) {\n var timeStep = this.prevTime ? time - this.prevTime : time;\n this.prevTime = time;\n\n if (this.initialised) {\n this.play(timeStep);\n this.paint();\n } else {\n this.initialisationCheck();\n }\n\n this.nextFrame = window.requestAnimationFrame(this.main.bind(this));\n }\n }, {\n key: \"play\",\n value: function play(timeStep) {\n var _this2 = this;\n\n // If a menu is open, pause all action gameplay\n if (this.homeMenu || this.interactionMenu) return; // Run the action gameplay\n // ----------------\n\n if (!this.paused) {\n this.atoms.forEach(function (atom) {\n return atom.play(timeStep);\n });\n this.checkCollisions(timeStep);\n }\n\n for (var id in this.rules) {\n this.rules[id].play(timeStep);\n } // Cleanup\n\n\n this.atoms = this.atoms.filter(function (atom) {\n return !atom._expired;\n });\n\n for (var _id in this.rules) {\n if (this.rules[_id]._expired) delete this.rules[_id];\n } // ----------------\n // Increment the duration of each currently pressed key\n\n\n if (!this.paused) {\n Object.keys(this.playerInput.keysPressed).forEach(function (key) {\n if (_this2.playerInput.keysPressed[key]) _this2.playerInput.keysPressed[key].duration += timeStep;\n });\n }\n }\n /*\r\n Draw a line of sight (cast a ray) starting from the hero, in the direction\r\n of they are facing.\r\n */\n\n }, {\n key: \"paintLineOfSight\",\n value: function paintLineOfSight() {\n if (!this.hero) return;\n var hero = this.hero;\n var c2d = this.canvas2d;\n var camera = this.camera;\n var MAX_LINE_OF_SIGHT_DISTANCE = 320; // Intended line of sight, i.e. a ray starting from the hero.\n\n var lineOfSight = {\n start: {\n x: hero.x,\n y: hero.y\n },\n end: {\n x: hero.x + MAX_LINE_OF_SIGHT_DISTANCE * Math.cos(hero.rotation),\n y: hero.y + MAX_LINE_OF_SIGHT_DISTANCE * Math.sin(hero.rotation)\n }\n };\n var actualLineOfSightEndPoint = undefined; // For each atom, see if it intersects with the hero's LOS\n\n this.atoms.forEach(function (atom) {\n if (atom === hero) return; // TODO: check for opaqueness and/or if the atom is visible.\n\n var vertices = atom.vertices;\n if (vertices.length < 2) return; // Every atom has a \"shape\" that can be represented by a polygon.\n // (Yes, even circles.) Check each segment (aka edge aka side) of the\n // polygon.\n\n for (var i = 0; i < vertices.length; i++) {\n var segment = {\n start: {\n x: vertices[i].x,\n y: vertices[i].y\n },\n end: {\n x: vertices[(i + 1) % vertices.length].x,\n y: vertices[(i + 1) % vertices.length].y\n }\n }; // Find the intersection. We want to find the intersection point\n // closest to the hero (the LOS ray's starting point).\n\n var intersection = _avo_physics__WEBPACK_IMPORTED_MODULE_1__.default.getLineIntersection(lineOfSight, segment);\n\n if (!actualLineOfSightEndPoint || intersection && intersection.distanceFactor < actualLineOfSightEndPoint.distanceFactor) {\n actualLineOfSightEndPoint = intersection;\n }\n }\n });\n\n if (!actualLineOfSightEndPoint) {\n actualLineOfSightEndPoint = {\n x: hero.x + MAX_LINE_OF_SIGHT_DISTANCE * Math.cos(hero.rotation),\n y: hero.y + MAX_LINE_OF_SIGHT_DISTANCE * Math.sin(hero.rotation)\n };\n } // Expected line of sight\n\n\n c2d.beginPath();\n c2d.moveTo(lineOfSight.start.x + camera.x, lineOfSight.start.y + camera.y);\n c2d.lineTo(lineOfSight.end.x + camera.x, lineOfSight.end.y + camera.y);\n c2d.closePath();\n c2d.strokeStyle = '#c88';\n c2d.lineWidth = 3;\n c2d.setLineDash([5, 5]);\n c2d.stroke();\n c2d.setLineDash([]); // Actual line of sight\n\n c2d.beginPath();\n c2d.moveTo(lineOfSight.start.x + camera.x, lineOfSight.start.y + camera.y);\n c2d.lineTo(actualLineOfSightEndPoint.x + camera.x, actualLineOfSightEndPoint.y + camera.y);\n c2d.closePath();\n c2d.strokeStyle = '#39f';\n c2d.lineWidth = 3;\n c2d.stroke(); // Expected end of line of sight\n\n c2d.beginPath();\n c2d.arc(lineOfSight.end.x + camera.x, lineOfSight.end.y + camera.y, 4, 0, 2 * Math.PI);\n c2d.closePath();\n c2d.fillStyle = '#c88';\n c2d.fill(); // Actual end of line of sight\n\n c2d.beginPath();\n c2d.arc(actualLineOfSightEndPoint.x + camera.x, actualLineOfSightEndPoint.y + camera.y, 8, 0, 2 * Math.PI);\n c2d.closePath();\n c2d.fillStyle = '#39f';\n c2d.fill();\n }\n }, {\n key: \"paint\",\n value: function paint() {\n var _this3 = this;\n\n var c2d = this.canvas2d;\n var camera = this.camera; // Camera Controls: focus the camera on the target atom, if any.\n // ----------------\n\n if (camera.target) {\n camera.x = this.canvasWidth / 2 - camera.target.x;\n camera.y = this.canvasHeight / 2 - camera.target.y;\n }\n\n c2d.clearRect(0, 0, this.canvasWidth, this.canvasHeight);\n c2d.strokeStyle = 'rgba(128, 128, 128, 0.05)';\n c2d.lineWidth = 2; // ----------------\n // Draw grid\n // ----------------\n\n var offsetX = this.camera.x % _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE - _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE;\n var offsetY = this.camera.y % _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE - _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE;\n\n for (var y = offsetY; y < this.canvasHeight; y += _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE) {\n for (var x = offsetX; x < this.canvasWidth; x += _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE) {\n c2d.beginPath();\n c2d.rect(x, y, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE);\n c2d.stroke(); // Debug Grid\n\n if (DEBUG) {\n c2d.fillStyle = '#ccc';\n c2d.font = \"1em Source Code Pro\";\n c2d.textAlign = 'center';\n c2d.textBaseline = 'middle';\n var col = Math.floor((x - this.camera.x) / _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE);\n var row = Math.floor((y - this.camera.y) / _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE);\n c2d.fillText(col + ',' + row, x + _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE / 2, y + _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE / 2); // using template strings here messes up colours in Brackets.\n }\n }\n } // ----------------\n // Draw atoms and other elements\n // ----------------\n\n\n var MAX_LAYER = 3;\n\n var _loop = function _loop(layer) {\n _this3.atoms.forEach(function (atom) {\n return atom.paint(layer);\n });\n\n for (var id in _this3.rules) {\n _this3.rules[id].paint(layer);\n }\n };\n\n for (var layer = 0; layer <= MAX_LAYER; layer++) {\n _loop(layer);\n } // ----------------\n // Draw player input\n // ----------------\n\n\n if (this.playerAction === _avo_constants__WEBPACK_IMPORTED_MODULE_0__.PLAYER_ACTIONS.POINTER_DOWN && this.playerInput.pointerCurrent) {\n var inputCoords = this.playerInput.pointerCurrent;\n c2d.strokeStyle = '#888';\n c2d.lineWidth = _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE / 8;\n c2d.beginPath();\n c2d.arc(inputCoords.x, inputCoords.y, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE * 1.5, 0, 2 * Math.PI);\n c2d.stroke();\n } // ----------------\n // Draw UI data\n // ----------------\n\n /*\r\n const X_OFFSET = TILE_SIZE * 2.5\r\n const Y_OFFSET = TILE_SIZE * -1.0\r\n c2d.font = '3em Source Code Pro'\r\n c2d.textBaseline = 'bottom'\r\n c2d.lineWidth = 8\r\n const health = Math.max(this.hero?.health, 0) || 0\r\n let text = '❤️'.repeat(health)\r\n c2d.textAlign = 'left'\r\n c2d.strokeStyle = '#fff'\r\n c2d.strokeText(text, X_OFFSET, this.canvasHeight + Y_OFFSET)\r\n c2d.fillStyle = '#c44'\r\n c2d.fillText(text, X_OFFSET, this.canvasHeight + Y_OFFSET)\r\n text = this.hero?.action?.name + ' (' + this.hero?.moveSpeed.toFixed(2) + ')'\r\n c2d.textAlign = 'right'\r\n c2d.strokeStyle = '#fff'\r\n c2d.strokeText(text, this.canvasWidth - X_OFFSET, this.canvasHeight + Y_OFFSET)\r\n c2d.fillStyle = '#c44'\r\n c2d.fillText(text, this.canvasWidth - X_OFFSET, this.canvasHeight + Y_OFFSET)\r\n // ----------------\r\n this.paintLineOfSight()\r\n */\n\n }\n /*\r\n Section: UI and Event Handling\r\n ----------------------------------------------------------------------------\r\n */\n\n }, {\n key: \"setupUI\",\n value: function setupUI() {\n this.html.canvas.width = this.canvasWidth;\n this.html.canvas.height = this.canvasHeight;\n\n if (window.PointerEvent) {\n this.html.canvas.addEventListener('pointerdown', this.onPointerDown.bind(this));\n this.html.canvas.addEventListener('pointermove', this.onPointerMove.bind(this));\n this.html.canvas.addEventListener('pointerup', this.onPointerUp.bind(this));\n this.html.canvas.addEventListener('pointercancel', this.onPointerUp.bind(this));\n } else {\n this.html.canvas.addEventListener('mousedown', this.onPointerDown.bind(this));\n this.html.canvas.addEventListener('mousemove', this.onPointerMove.bind(this));\n this.html.canvas.addEventListener('mouseup', this.onPointerUp.bind(this));\n } // Prevent \"touch and hold to open context menu\" menu on touchscreens.\n\n\n this.html.canvas.addEventListener('touchstart', stopEvent);\n this.html.canvas.addEventListener('touchmove', stopEvent);\n this.html.canvas.addEventListener('touchend', stopEvent);\n this.html.canvas.addEventListener('touchcancel', stopEvent);\n this.html.buttonHome.addEventListener('click', this.buttonHome_onClick.bind(this));\n this.html.buttonFullscreen.addEventListener('click', this.buttonFullscreen_onClick.bind(this));\n this.html.buttonReload.addEventListener('click', this.buttonReload_onClick.bind(this));\n this.html.main.addEventListener('keydown', this.onKeyDown.bind(this));\n this.html.main.addEventListener('keyup', this.onKeyUp.bind(this));\n window.addEventListener('resize', this.updateUI.bind(this));\n this.updateUI();\n this.hideUI(); // Hide until all assets are ready\n\n this.html.main.focus();\n }\n }, {\n key: \"hideUI\",\n value: function hideUI() {\n this.html.buttonHome.style.visibility = 'hidden';\n this.html.buttonReload.style.visibility = 'hidden';\n }\n }, {\n key: \"showUI\",\n value: function showUI() {\n this.html.buttonHome.style.visibility = 'visible';\n this.html.buttonReload.style.visibility = 'visible';\n }\n }, {\n key: \"updateUI\",\n value: function updateUI() {\n // Fit the interaction layers (menus, etc) to the canvas\n var mainDivBounds = this.html.main.getBoundingClientRect();\n var canvasBounds = this.html.canvas.getBoundingClientRect();\n this.html.homeMenu.style.width = \"\".concat(canvasBounds.width, \"px\");\n this.html.homeMenu.style.height = \"\".concat(canvasBounds.height, \"px\");\n this.html.homeMenu.style.top = \"\".concat(canvasBounds.top - mainDivBounds.top, \"px\");\n this.html.homeMenu.style.left = \"\".concat(canvasBounds.left, \"px\");\n this.html.interactionMenu.style.width = \"\".concat(canvasBounds.width, \"px\");\n this.html.interactionMenu.style.height = \"\".concat(canvasBounds.height, \"px\");\n this.html.interactionMenu.style.top = \"\".concat(canvasBounds.top - mainDivBounds.top, \"px\");\n this.html.interactionMenu.style.left = \"\".concat(canvasBounds.left, \"px\");\n }\n }, {\n key: \"setHomeMenu\",\n value: function setHomeMenu(homeMenu) {\n this.homeMenu = homeMenu;\n\n if (homeMenu) {\n this.html.homeMenu.style.visibility = 'visible';\n this.html.buttonReload.style.visibility = 'hidden';\n } else {\n this.html.homeMenu.style.visibility = 'hidden';\n this.html.buttonReload.style.visibility = 'visible';\n this.html.main.focus();\n }\n }\n }, {\n key: \"setInteractionMenu\",\n value: function setInteractionMenu(interactionMenu) {\n var div = this.html.interactionMenu;\n this.interactionMenu && this.interactionMenu.unload(); // Unload the old menu, if any\n\n this.interactionMenu = interactionMenu; // Set the new menu\n\n if (interactionMenu) {\n while (div.firstChild) {\n div.removeChild(div.firstChild);\n } // Clear div\n\n\n interactionMenu.load(div); // load the new menu\n\n div.style.visibility = 'visible';\n } else {\n div.style.visibility = 'hidden';\n this.html.main.focus();\n }\n }\n }, {\n key: \"onPointerDown\",\n value: function onPointerDown(e) {\n var coords = getEventCoords(e, this.html.canvas);\n this.playerAction = _avo_constants__WEBPACK_IMPORTED_MODULE_0__.PLAYER_ACTIONS.POINTER_DOWN;\n this.playerInput.pointerStart = coords;\n this.playerInput.pointerCurrent = coords;\n this.playerInput.pointerEnd = undefined;\n this.html.main.focus();\n return stopEvent(e);\n }\n }, {\n key: \"onPointerMove\",\n value: function onPointerMove(e) {\n var coords = getEventCoords(e, this.html.canvas);\n this.playerInput.pointerCurrent = coords;\n return stopEvent(e);\n }\n }, {\n key: \"onPointerUp\",\n value: function onPointerUp(e) {\n var coords = getEventCoords(e, this.html.canvas);\n\n if (this.playerAction === _avo_constants__WEBPACK_IMPORTED_MODULE_0__.PLAYER_ACTIONS.POINTER_DOWN) {\n this.playerInput.pointerEnd = coords;\n this.playerAction = _avo_constants__WEBPACK_IMPORTED_MODULE_0__.PLAYER_ACTIONS.IDLE;\n }\n\n return stopEvent(e);\n }\n }, {\n key: \"onKeyDown\",\n value: function onKeyDown(e) {\n // Special cases\n switch (e.key) {\n // Open home menu\n case 'Escape':\n this.setHomeMenu(!this.homeMenu);\n break;\n // DEBUG\n\n case 'z':\n if (!this.interactionMenu) {\n this.setInteractionMenu(new _avo_interaction__WEBPACK_IMPORTED_MODULE_5__.default(this));\n }\n\n break;\n } // General input\n\n\n if (!this.playerInput.keysPressed[e.key]) {\n this.playerInput.keysPressed[e.key] = {\n duration: 0,\n acknowledged: false\n };\n }\n }\n }, {\n key: \"onKeyUp\",\n value: function onKeyUp(e) {\n this.playerInput.keysPressed[e.key] = undefined;\n }\n }, {\n key: \"buttonHome_onClick\",\n value: function buttonHome_onClick() {\n this.setHomeMenu(!this.homeMenu);\n }\n }, {\n key: \"buttonFullscreen_onClick\",\n value: function buttonFullscreen_onClick() {\n var isFullscreen = document.fullscreenElement;\n\n if (!isFullscreen) {\n if (this.html.main.requestFullscreen) {\n this.html.main.className = 'fullscreen';\n this.html.main.requestFullscreen();\n }\n } else {\n var _document$exitFullscr, _document;\n\n (_document$exitFullscr = (_document = document).exitFullscreen) === null || _document$exitFullscr === void 0 ? void 0 : _document$exitFullscr.call(_document);\n this.html.main.className = '';\n }\n\n this.updateUI();\n }\n }, {\n key: \"buttonReload_onClick\",\n value: function buttonReload_onClick() {\n this.levels.reload();\n }\n /*\r\n Section: Gameplay\r\n ----------------------------------------------------------------------------\r\n */\n\n }, {\n key: \"addRule\",\n value: function addRule(rule) {\n if (!rule) return;\n var id = rule._type;\n this.rules[id] = rule;\n }\n }, {\n key: \"clearRules\",\n value: function clearRules() {\n for (var id in this.rules) {\n delete this.rules[id];\n }\n }\n /*\r\n Section: Misc\r\n ----------------------------------------------------------------------------\r\n */\n\n }, {\n key: \"checkCollisions\",\n value: function checkCollisions(timeStep) {\n for (var a = 0; a < this.atoms.length; a++) {\n var atomA = this.atoms[a];\n\n for (var b = a + 1; b < this.atoms.length; b++) {\n var atomB = this.atoms[b];\n var collisionCorrection = _avo_physics__WEBPACK_IMPORTED_MODULE_1__.default.checkCollision(atomA, atomB);\n\n if (collisionCorrection) {\n atomA.onCollision(atomB, collisionCorrection.a);\n atomB.onCollision(atomA, collisionCorrection.b);\n }\n }\n }\n }\n }]);\n\n return AvO;\n}();\n\n\n\nfunction getEventCoords(event, element) {\n var xRatio = element.width && element.offsetWidth ? element.width / element.offsetWidth : 1;\n var yRatio = element.height && element.offsetHeight ? element.height / element.offsetHeight : 1;\n var x = event.offsetX * xRatio;\n var y = event.offsetY * yRatio;\n return {\n x: x,\n y: y\n };\n}\n\nfunction stopEvent(e) {\n if (!e) return false;\n e.preventDefault && e.preventDefault();\n e.stopPropagation && e.stopPropagation();\n e.returnValue = false;\n e.cancelBubble = true;\n return false;\n}\n\n//# sourceURL=webpack://cny2022/./src/avo/avo.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ AvO)\n/* harmony export */ });\n/* harmony import */ var _avo_constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @avo/constants */ \"./src/avo/constants.js\");\n/* harmony import */ var _avo_physics__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @avo/physics */ \"./src/avo/physics.js\");\n/* harmony import */ var _avo_levels__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @avo/levels */ \"./src/avo/levels.js\");\n/* harmony import */ var _avo_image_asset__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @avo/image-asset */ \"./src/avo/image-asset.js\");\n/* harmony import */ var _avo_json_asset__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @avo/json-asset */ \"./src/avo/json-asset.js\");\n/* harmony import */ var _avo_interaction__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @avo/interaction */ \"./src/avo/interaction/index.js\");\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n\n\n\n\n\n\nvar searchParams = new URLSearchParams(window.location.search);\nvar DEBUG = searchParams.get('debug') || false;\nvar STARTING_LEVEL = Number.isInteger(parseInt(searchParams.get('level'))) ? parseInt(searchParams.get('level')) - 1 : 0;\n\nvar AvO = /*#__PURE__*/function () {\n function AvO() {\n var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n\n _classCallCheck(this, AvO);\n\n var _args$width = args.width,\n width = _args$width === void 0 ? 24 * _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE : _args$width,\n _args$height = args.height,\n height = _args$height === void 0 ? 16 * _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE : _args$height;\n this.html = {\n main: document.getElementById('main'),\n canvas: document.getElementById('canvas'),\n homeMenu: document.getElementById('home-menu'),\n interactionMenu: document.getElementById('interaction-menu'),\n buttonHome: document.getElementById('button-home'),\n buttonFullscreen: document.getElementById('button-fullscreen'),\n buttonReload: document.getElementById('button-reload')\n };\n this.homeMenu = false;\n this.setHomeMenu(false);\n this.interactionMenu = false;\n this.setInteractionMenu(false);\n this.canvas2d = this.html.canvas.getContext('2d');\n this.canvasWidth = width;\n this.canvasHeight = height;\n this.camera = {\n target: null,\n // Target atom to follow. If null, camera is static.\n x: 0,\n y: 0\n };\n this.setupUI();\n this.initialised = false;\n this.assets = {\n // \"exampleImage\": new ImageAsset('assets/simple-bg.png'),\n // \"exampleJson\": new JsonAsset('assets/example.json'),\n 'cny2022': new _avo_image_asset__WEBPACK_IMPORTED_MODULE_3__.default('assets/cny2022-sprites.png')\n };\n this.secretAssets = {// \"secretImage\": new ImageAsset('secrets/simple-bg.png'),\n // \"secretJson\": new JsonAsset('secrets/example.json'),\n };\n this.hero = null;\n this.atoms = [];\n this.rules = {};\n this.levels = new _avo_levels__WEBPACK_IMPORTED_MODULE_2__.default(this);\n this.playerAction = _avo_constants__WEBPACK_IMPORTED_MODULE_0__.PLAYER_ACTIONS.IDLE;\n this.playerInput = {\n // Mouse/touchscreen input\n pointerStart: undefined,\n pointerCurrent: undefined,\n pointerEnd: undefined,\n // Keys that are currently being pressed.\n // keysPressed = { key: { duration, acknowledged } }\n keysPressed: {}\n };\n this.prevTime = null;\n this.nextFrame = window.requestAnimationFrame(this.main.bind(this));\n this.paused = false; // Pause gameplay\n }\n\n _createClass(AvO, [{\n key: \"initialisationCheck\",\n value: function initialisationCheck() {\n var _this = this;\n\n // Assets check\n var allAssetsReady = true;\n var numReadyAssets = 0;\n var numTotalAssets = 0;\n Object.keys(this.assets).forEach(function (id) {\n var asset = _this.assets[id];\n allAssetsReady = allAssetsReady && asset.ready;\n if (asset.ready) numReadyAssets++;\n numTotalAssets++;\n });\n Object.keys(this.secretAssets).forEach(function (id) {\n var secretAsset = _this.secretAssets[id];\n var secretAssetIsReady = secretAsset.ready || secretAsset.error;\n allAssetsReady = allAssetsReady && secretAssetIsReady;\n if (secretAssetIsReady) numReadyAssets++;\n numTotalAssets++;\n }); // Paint status\n\n this.canvas2d.clearRect(0, 0, this.canvasWidth, this.canvasHeight);\n this.canvas2d.textAlign = 'start';\n this.canvas2d.textBaseline = 'top';\n this.canvas2d.fillStyle = '#ccc';\n this.canvas2d.font = \"1em monospace\";\n this.canvas2d.fillText(\"Loading \".concat(numReadyAssets, \" / \").concat(numTotalAssets, \" \"), _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE);\n\n if (allAssetsReady) {\n // Clean up secret assets\n Object.keys(this.secretAssets).forEach(function (id) {\n if (_this.secretAssets[id].error) delete _this.secretAssets[id];\n }); // Let's go!\n\n this.initialised = true;\n this.showUI();\n this.setHomeMenu(true); // this.levels.load(STARTING_LEVEL)\n }\n }\n /*\r\n Section: General Logic\r\n ----------------------------------------------------------------------------\r\n */\n\n }, {\n key: \"main\",\n value: function main(time) {\n var timeStep = this.prevTime ? time - this.prevTime : time;\n this.prevTime = time;\n\n if (this.initialised) {\n this.play(timeStep);\n this.paint();\n } else {\n this.initialisationCheck();\n }\n\n this.nextFrame = window.requestAnimationFrame(this.main.bind(this));\n }\n }, {\n key: \"play\",\n value: function play(timeStep) {\n var _this2 = this;\n\n // If a menu is open, pause all action gameplay\n if (this.homeMenu || this.interactionMenu) return; // Run the action gameplay\n // ----------------\n\n if (!this.paused) {\n this.atoms.forEach(function (atom) {\n return atom.play(timeStep);\n });\n this.checkCollisions(timeStep);\n }\n\n for (var id in this.rules) {\n this.rules[id].play(timeStep);\n } // Cleanup\n\n\n this.atoms = this.atoms.filter(function (atom) {\n return !atom._expired;\n });\n\n for (var _id in this.rules) {\n if (this.rules[_id]._expired) delete this.rules[_id];\n } // ----------------\n // Increment the duration of each currently pressed key\n\n\n if (!this.paused) {\n Object.keys(this.playerInput.keysPressed).forEach(function (key) {\n if (_this2.playerInput.keysPressed[key]) _this2.playerInput.keysPressed[key].duration += timeStep;\n });\n }\n }\n /*\r\n Draw a line of sight (cast a ray) starting from the hero, in the direction\r\n of they are facing.\r\n */\n\n }, {\n key: \"paintLineOfSight\",\n value: function paintLineOfSight() {\n if (!this.hero) return;\n var hero = this.hero;\n var c2d = this.canvas2d;\n var camera = this.camera;\n var MAX_LINE_OF_SIGHT_DISTANCE = 320; // Intended line of sight, i.e. a ray starting from the hero.\n\n var lineOfSight = {\n start: {\n x: hero.x,\n y: hero.y\n },\n end: {\n x: hero.x + MAX_LINE_OF_SIGHT_DISTANCE * Math.cos(hero.rotation),\n y: hero.y + MAX_LINE_OF_SIGHT_DISTANCE * Math.sin(hero.rotation)\n }\n };\n var actualLineOfSightEndPoint = undefined; // For each atom, see if it intersects with the hero's LOS\n\n this.atoms.forEach(function (atom) {\n if (atom === hero) return; // TODO: check for opaqueness and/or if the atom is visible.\n\n var vertices = atom.vertices;\n if (vertices.length < 2) return; // Every atom has a \"shape\" that can be represented by a polygon.\n // (Yes, even circles.) Check each segment (aka edge aka side) of the\n // polygon.\n\n for (var i = 0; i < vertices.length; i++) {\n var segment = {\n start: {\n x: vertices[i].x,\n y: vertices[i].y\n },\n end: {\n x: vertices[(i + 1) % vertices.length].x,\n y: vertices[(i + 1) % vertices.length].y\n }\n }; // Find the intersection. We want to find the intersection point\n // closest to the hero (the LOS ray's starting point).\n\n var intersection = _avo_physics__WEBPACK_IMPORTED_MODULE_1__.default.getLineIntersection(lineOfSight, segment);\n\n if (!actualLineOfSightEndPoint || intersection && intersection.distanceFactor < actualLineOfSightEndPoint.distanceFactor) {\n actualLineOfSightEndPoint = intersection;\n }\n }\n });\n\n if (!actualLineOfSightEndPoint) {\n actualLineOfSightEndPoint = {\n x: hero.x + MAX_LINE_OF_SIGHT_DISTANCE * Math.cos(hero.rotation),\n y: hero.y + MAX_LINE_OF_SIGHT_DISTANCE * Math.sin(hero.rotation)\n };\n } // Expected line of sight\n\n\n c2d.beginPath();\n c2d.moveTo(lineOfSight.start.x + camera.x, lineOfSight.start.y + camera.y);\n c2d.lineTo(lineOfSight.end.x + camera.x, lineOfSight.end.y + camera.y);\n c2d.closePath();\n c2d.strokeStyle = '#c88';\n c2d.lineWidth = 3;\n c2d.setLineDash([5, 5]);\n c2d.stroke();\n c2d.setLineDash([]); // Actual line of sight\n\n c2d.beginPath();\n c2d.moveTo(lineOfSight.start.x + camera.x, lineOfSight.start.y + camera.y);\n c2d.lineTo(actualLineOfSightEndPoint.x + camera.x, actualLineOfSightEndPoint.y + camera.y);\n c2d.closePath();\n c2d.strokeStyle = '#39f';\n c2d.lineWidth = 3;\n c2d.stroke(); // Expected end of line of sight\n\n c2d.beginPath();\n c2d.arc(lineOfSight.end.x + camera.x, lineOfSight.end.y + camera.y, 4, 0, 2 * Math.PI);\n c2d.closePath();\n c2d.fillStyle = '#c88';\n c2d.fill(); // Actual end of line of sight\n\n c2d.beginPath();\n c2d.arc(actualLineOfSightEndPoint.x + camera.x, actualLineOfSightEndPoint.y + camera.y, 8, 0, 2 * Math.PI);\n c2d.closePath();\n c2d.fillStyle = '#39f';\n c2d.fill();\n }\n }, {\n key: \"paint\",\n value: function paint() {\n var _this3 = this;\n\n var c2d = this.canvas2d;\n var camera = this.camera; // Camera Controls: focus the camera on the target atom, if any.\n // ----------------\n\n if (camera.target) {\n camera.x = this.canvasWidth / 2 - camera.target.x;\n camera.y = this.canvasHeight / 2 - camera.target.y;\n }\n\n c2d.clearRect(0, 0, this.canvasWidth, this.canvasHeight);\n c2d.strokeStyle = 'rgba(128, 128, 128, 0.05)';\n c2d.lineWidth = 2; // ----------------\n // Draw grid\n // ----------------\n\n var offsetX = this.camera.x % _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE - _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE;\n var offsetY = this.camera.y % _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE - _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE;\n\n for (var y = offsetY; y < this.canvasHeight; y += _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE) {\n for (var x = offsetX; x < this.canvasWidth; x += _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE) {\n c2d.beginPath();\n c2d.rect(x, y, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE);\n c2d.stroke(); // Debug Grid\n\n if (DEBUG) {\n c2d.fillStyle = '#ccc';\n c2d.font = \"1em Source Code Pro\";\n c2d.textAlign = 'center';\n c2d.textBaseline = 'middle';\n var col = Math.floor((x - this.camera.x) / _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE);\n var row = Math.floor((y - this.camera.y) / _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE);\n c2d.fillText(col + ',' + row, x + _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE / 2, y + _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE / 2); // using template strings here messes up colours in Brackets.\n }\n }\n } // ----------------\n // Draw atoms and other elements\n // ----------------\n\n\n var MAX_LAYER = 3;\n\n var _loop = function _loop(layer) {\n _this3.atoms.forEach(function (atom) {\n return atom.paint(layer);\n });\n\n for (var id in _this3.rules) {\n _this3.rules[id].paint(layer);\n }\n };\n\n for (var layer = 0; layer <= MAX_LAYER; layer++) {\n _loop(layer);\n } // ----------------\n // Draw player input\n // ----------------\n\n\n if (this.playerAction === _avo_constants__WEBPACK_IMPORTED_MODULE_0__.PLAYER_ACTIONS.POINTER_DOWN && this.playerInput.pointerCurrent) {\n var inputCoords = this.playerInput.pointerCurrent;\n c2d.strokeStyle = '#888';\n c2d.lineWidth = _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE / 8;\n c2d.beginPath();\n c2d.arc(inputCoords.x, inputCoords.y, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.TILE_SIZE * 1.5, 0, 2 * Math.PI);\n c2d.stroke();\n } // ----------------\n // Draw UI data\n // ----------------\n\n /*\r\n const X_OFFSET = TILE_SIZE * 2.5\r\n const Y_OFFSET = TILE_SIZE * -1.0\r\n c2d.font = '3em Source Code Pro'\r\n c2d.textBaseline = 'bottom'\r\n c2d.lineWidth = 8\r\n const health = Math.max(this.hero?.health, 0) || 0\r\n let text = '❤️'.repeat(health)\r\n c2d.textAlign = 'left'\r\n c2d.strokeStyle = '#fff'\r\n c2d.strokeText(text, X_OFFSET, this.canvasHeight + Y_OFFSET)\r\n c2d.fillStyle = '#c44'\r\n c2d.fillText(text, X_OFFSET, this.canvasHeight + Y_OFFSET)\r\n text = this.hero?.action?.name + ' (' + this.hero?.moveSpeed.toFixed(2) + ')'\r\n c2d.textAlign = 'right'\r\n c2d.strokeStyle = '#fff'\r\n c2d.strokeText(text, this.canvasWidth - X_OFFSET, this.canvasHeight + Y_OFFSET)\r\n c2d.fillStyle = '#c44'\r\n c2d.fillText(text, this.canvasWidth - X_OFFSET, this.canvasHeight + Y_OFFSET)\r\n // ----------------\r\n this.paintLineOfSight()\r\n */\n\n }\n /*\r\n Section: UI and Event Handling\r\n ----------------------------------------------------------------------------\r\n */\n\n }, {\n key: \"setupUI\",\n value: function setupUI() {\n this.html.canvas.width = this.canvasWidth;\n this.html.canvas.height = this.canvasHeight;\n\n if (window.PointerEvent) {\n this.html.canvas.addEventListener('pointerdown', this.onPointerDown.bind(this));\n this.html.canvas.addEventListener('pointermove', this.onPointerMove.bind(this));\n this.html.canvas.addEventListener('pointerup', this.onPointerUp.bind(this));\n this.html.canvas.addEventListener('pointercancel', this.onPointerUp.bind(this));\n } else {\n this.html.canvas.addEventListener('mousedown', this.onPointerDown.bind(this));\n this.html.canvas.addEventListener('mousemove', this.onPointerMove.bind(this));\n this.html.canvas.addEventListener('mouseup', this.onPointerUp.bind(this));\n } // Prevent \"touch and hold to open context menu\" menu on touchscreens.\n\n\n this.html.canvas.addEventListener('touchstart', stopEvent);\n this.html.canvas.addEventListener('touchmove', stopEvent);\n this.html.canvas.addEventListener('touchend', stopEvent);\n this.html.canvas.addEventListener('touchcancel', stopEvent);\n this.html.buttonHome.addEventListener('click', this.buttonHome_onClick.bind(this));\n this.html.buttonFullscreen.addEventListener('click', this.buttonFullscreen_onClick.bind(this));\n this.html.buttonReload.addEventListener('click', this.buttonReload_onClick.bind(this));\n this.html.main.addEventListener('keydown', this.onKeyDown.bind(this));\n this.html.main.addEventListener('keyup', this.onKeyUp.bind(this));\n window.addEventListener('resize', this.updateUI.bind(this));\n this.updateUI();\n this.hideUI(); // Hide until all assets are ready\n\n this.html.main.focus();\n }\n }, {\n key: \"hideUI\",\n value: function hideUI() {\n this.html.buttonHome.style.visibility = 'hidden';\n this.html.buttonReload.style.visibility = 'hidden';\n }\n }, {\n key: \"showUI\",\n value: function showUI() {\n this.html.buttonHome.style.visibility = 'visible';\n this.html.buttonReload.style.visibility = 'visible';\n }\n }, {\n key: \"updateUI\",\n value: function updateUI() {\n // Fit the interaction layers (menus, etc) to the canvas\n var mainDivBounds = this.html.main.getBoundingClientRect();\n var canvasBounds = this.html.canvas.getBoundingClientRect();\n this.html.homeMenu.style.width = \"\".concat(canvasBounds.width, \"px\");\n this.html.homeMenu.style.height = \"\".concat(canvasBounds.height, \"px\");\n this.html.homeMenu.style.top = \"\".concat(canvasBounds.top - mainDivBounds.top, \"px\");\n this.html.homeMenu.style.left = \"\".concat(canvasBounds.left, \"px\");\n this.html.interactionMenu.style.width = \"\".concat(canvasBounds.width, \"px\");\n this.html.interactionMenu.style.height = \"\".concat(canvasBounds.height, \"px\");\n this.html.interactionMenu.style.top = \"\".concat(canvasBounds.top - mainDivBounds.top, \"px\");\n this.html.interactionMenu.style.left = \"\".concat(canvasBounds.left, \"px\");\n }\n }, {\n key: \"setHomeMenu\",\n value: function setHomeMenu(homeMenu) {\n this.homeMenu = homeMenu;\n\n if (homeMenu) {\n this.html.homeMenu.style.visibility = 'visible';\n this.html.buttonReload.style.visibility = 'hidden';\n this.updateCNY2022HomeMenu();\n } else {\n this.html.homeMenu.style.visibility = 'hidden';\n this.html.buttonReload.style.visibility = 'visible';\n this.html.main.focus();\n }\n }\n /*\r\n Set up the level list on the home menu.\r\n */\n\n }, {\n key: \"updateCNY2022HomeMenu\",\n value: function updateCNY2022HomeMenu() {\n var _this4 = this;\n\n var levelsList = document.getElementById('levels-list');\n\n while (levelsList.firstChild) {\n levelsList.removeChild(levelsList.firstChild);\n } // Clear\n\n\n var _loop2 = function _loop2(levelIndex) {\n var levelNumber = levelIndex + 1;\n var li = document.createElement('li');\n var button = document.createElement('button');\n var span = document.createElement('span');\n button.innerText = \"Level \".concat(levelNumber);\n button.addEventListener('click', function () {\n _this4.levels.load(levelIndex);\n\n _this4.setHomeMenu(false);\n });\n var score = _this4.levels.cny2022HighScores[levelIndex];\n span.innerText = Number.isInteger(score) ? \"Score: \".concat(score) : 'New!';\n li.appendChild(button);\n li.appendChild(span);\n levelsList.appendChild(li);\n };\n\n for (var levelIndex = 0; levelIndex < this.levels.cny2022LevelGenerators.length; levelIndex++) {\n _loop2(levelIndex);\n }\n }\n }, {\n key: \"setInteractionMenu\",\n value: function setInteractionMenu(interactionMenu) {\n var div = this.html.interactionMenu;\n this.interactionMenu && this.interactionMenu.unload(); // Unload the old menu, if any\n\n this.interactionMenu = interactionMenu; // Set the new menu\n\n if (interactionMenu) {\n while (div.firstChild) {\n div.removeChild(div.firstChild);\n } // Clear div\n\n\n interactionMenu.load(div); // load the new menu\n\n div.style.visibility = 'visible';\n } else {\n div.style.visibility = 'hidden';\n this.html.main.focus();\n }\n }\n }, {\n key: \"onPointerDown\",\n value: function onPointerDown(e) {\n var coords = getEventCoords(e, this.html.canvas);\n this.playerAction = _avo_constants__WEBPACK_IMPORTED_MODULE_0__.PLAYER_ACTIONS.POINTER_DOWN;\n this.playerInput.pointerStart = coords;\n this.playerInput.pointerCurrent = coords;\n this.playerInput.pointerEnd = undefined;\n this.html.main.focus();\n return stopEvent(e);\n }\n }, {\n key: \"onPointerMove\",\n value: function onPointerMove(e) {\n var coords = getEventCoords(e, this.html.canvas);\n this.playerInput.pointerCurrent = coords;\n return stopEvent(e);\n }\n }, {\n key: \"onPointerUp\",\n value: function onPointerUp(e) {\n var coords = getEventCoords(e, this.html.canvas);\n\n if (this.playerAction === _avo_constants__WEBPACK_IMPORTED_MODULE_0__.PLAYER_ACTIONS.POINTER_DOWN) {\n this.playerInput.pointerEnd = coords;\n this.playerAction = _avo_constants__WEBPACK_IMPORTED_MODULE_0__.PLAYER_ACTIONS.IDLE;\n }\n\n return stopEvent(e);\n }\n }, {\n key: \"onKeyDown\",\n value: function onKeyDown(e) {\n // Special cases\n switch (e.key) {\n // Open home menu\n case 'Escape':\n this.setHomeMenu(!this.homeMenu);\n break;\n // DEBUG\n\n /*\r\n case 'z':\r\n if (!this.interactionMenu) {\r\n this.setInteractionMenu(new Interaction(this))\r\n }\r\n break\r\n */\n } // General input\n\n\n if (!this.playerInput.keysPressed[e.key]) {\n this.playerInput.keysPressed[e.key] = {\n duration: 0,\n acknowledged: false\n };\n }\n }\n }, {\n key: \"onKeyUp\",\n value: function onKeyUp(e) {\n this.playerInput.keysPressed[e.key] = undefined;\n }\n }, {\n key: \"buttonHome_onClick\",\n value: function buttonHome_onClick() {\n this.setHomeMenu(!this.homeMenu);\n }\n }, {\n key: \"buttonFullscreen_onClick\",\n value: function buttonFullscreen_onClick() {\n var isFullscreen = document.fullscreenElement;\n\n if (!isFullscreen) {\n if (this.html.main.requestFullscreen) {\n this.html.main.className = 'fullscreen';\n this.html.main.requestFullscreen();\n }\n } else {\n var _document$exitFullscr, _document;\n\n (_document$exitFullscr = (_document = document).exitFullscreen) === null || _document$exitFullscr === void 0 ? void 0 : _document$exitFullscr.call(_document);\n this.html.main.className = '';\n }\n\n this.updateUI();\n }\n }, {\n key: \"buttonReload_onClick\",\n value: function buttonReload_onClick() {\n this.levels.reload();\n }\n /*\r\n Section: Gameplay\r\n ----------------------------------------------------------------------------\r\n */\n\n }, {\n key: \"addRule\",\n value: function addRule(rule) {\n if (!rule) return;\n var id = rule._type;\n this.rules[id] = rule;\n }\n }, {\n key: \"clearRules\",\n value: function clearRules() {\n for (var id in this.rules) {\n delete this.rules[id];\n }\n }\n /*\r\n Section: Misc\r\n ----------------------------------------------------------------------------\r\n */\n\n }, {\n key: \"checkCollisions\",\n value: function checkCollisions(timeStep) {\n for (var a = 0; a < this.atoms.length; a++) {\n var atomA = this.atoms[a];\n\n for (var b = a + 1; b < this.atoms.length; b++) {\n var atomB = this.atoms[b];\n var collisionCorrection = _avo_physics__WEBPACK_IMPORTED_MODULE_1__.default.checkCollision(atomA, atomB);\n\n if (collisionCorrection) {\n atomA.onCollision(atomB, collisionCorrection.a);\n atomB.onCollision(atomA, collisionCorrection.b);\n }\n }\n }\n }\n }]);\n\n return AvO;\n}();\n\n\n\nfunction getEventCoords(event, element) {\n var xRatio = element.width && element.offsetWidth ? element.width / element.offsetWidth : 1;\n var yRatio = element.height && element.offsetHeight ? element.height / element.offsetHeight : 1;\n var x = event.offsetX * xRatio;\n var y = event.offsetY * yRatio;\n return {\n x: x,\n y: y\n };\n}\n\nfunction stopEvent(e) {\n if (!e) return false;\n e.preventDefault && e.preventDefault();\n e.stopPropagation && e.stopPropagation();\n e.returnValue = false;\n e.cancelBubble = true;\n return false;\n}\n\n//# sourceURL=webpack://cny2022/./src/avo/avo.js?"); /***/ }), @@ -216,7 +216,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \***************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Levels)\n/* harmony export */ });\n/* harmony import */ var _avo_constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @avo/constants */ \"./src/avo/constants.js\");\n/* harmony import */ var _avo_atom_types_hero__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @avo/atom/types/hero */ \"./src/avo/atom/types/hero.js\");\n/* harmony import */ var _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @avo/atom/types/wall */ \"./src/avo/atom/types/wall.js\");\n/* harmony import */ var _avo_atom_types_ball__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @avo/atom/types/ball */ \"./src/avo/atom/types/ball.js\");\n/* harmony import */ var _avo_atom_types_enemy__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @avo/atom/types/enemy */ \"./src/avo/atom/types/enemy.js\");\n/* harmony import */ var _avo_atom_types_cny2022_cat__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @avo/atom/types/cny2022/cat */ \"./src/avo/atom/types/cny2022/cat.js\");\n/* harmony import */ var _avo_atom_types_cny2022_laser_pointer__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @avo/atom/types/cny2022/laser-pointer */ \"./src/avo/atom/types/cny2022/laser-pointer.js\");\n/* harmony import */ var _avo_atom_types_cny2022_goal__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @avo/atom/types/cny2022/goal */ \"./src/avo/atom/types/cny2022/goal.js\");\n/* harmony import */ var _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @avo/atom/types/cny2022/coin */ \"./src/avo/atom/types/cny2022/coin.js\");\n/* harmony import */ var _avo_atom_types_cny2022_vase__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! @avo/atom/types/cny2022/vase */ \"./src/avo/atom/types/cny2022/vase.js\");\n/* harmony import */ var _avo_atom_types_cny2022_glass_wall__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! @avo/atom/types/cny2022/glass-wall */ \"./src/avo/atom/types/cny2022/glass-wall.js\");\n/* harmony import */ var _avo_rule_types_zelda_controls__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! @avo/rule/types/zelda-controls */ \"./src/avo/rule/types/zelda-controls.js\");\n/* harmony import */ var _avo_rule_types_cny2022_controls__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! @avo/rule/types/cny2022-controls */ \"./src/avo/rule/types/cny2022-controls.js\");\n/* harmony import */ var _avo_rule_types_cny2022_victory__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! @avo/rule/types/cny2022-victory */ \"./src/avo/rule/types/cny2022-victory.js\");\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar Levels = /*#__PURE__*/function () {\n function Levels(app) {\n _classCallCheck(this, Levels);\n\n this._app = app;\n this.current = 0;\n }\n\n _createClass(Levels, [{\n key: \"reset\",\n value: function reset() {\n var app = this._app;\n app.hero = undefined;\n app.atoms = [];\n app.clearRules();\n app.camera = {\n target: null,\n x: 0,\n y: 0\n };\n app.playerAction = _avo_constants__WEBPACK_IMPORTED_MODULE_0__.PLAYER_ACTIONS.IDLE;\n app.setInteractionMenu(false);\n app.paused = false;\n }\n }, {\n key: \"load\",\n value: function load() {\n var level = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n var app = this._app;\n this.current = level;\n this.reset(); // this.generate_cny2022_default()\n\n this.generate_cny2022_level_1();\n }\n }, {\n key: \"reload\",\n value: function reload() {\n this.load(this.current);\n }\n /*\r\n Default top-down adventure level.\r\n */\n\n }, {\n key: \"generate_zelda_default\",\n value: function generate_zelda_default() {\n var app = this._app;\n app.hero = new _avo_atom_types_hero__WEBPACK_IMPORTED_MODULE_1__.default(app, 11, 1);\n app.atoms.push(app.hero);\n app.camera.target = app.hero;\n app.addRule(new _avo_rule_types_zelda_controls__WEBPACK_IMPORTED_MODULE_11__.default(app)); // app.addRule(new CNY2022Controls(app))\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 0, 0, 1, 23)); // West Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 22, 0, 1, 23)); // East Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 1, 0, 21, 1)); // North Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 1, 22, 21, 1)); // South Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 3, 2, 3, 1));\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 3, 4, 3, 1));\n app.atoms.push(new _avo_atom_types_ball__WEBPACK_IMPORTED_MODULE_3__.default(app, 8, 6));\n var enemy = new _avo_atom_types_enemy__WEBPACK_IMPORTED_MODULE_4__.default(app, 4, 8);\n enemy.rotation = -45 / 180 * Math.PI;\n app.atoms.push(enemy);\n }\n }, {\n key: \"generate_cny2022_default\",\n value: function generate_cny2022_default() {\n var app = this._app;\n var cat = new _avo_atom_types_cny2022_cat__WEBPACK_IMPORTED_MODULE_5__.default(app, 3, 13);\n var laserPointer = new _avo_atom_types_cny2022_laser_pointer__WEBPACK_IMPORTED_MODULE_6__.default(app, (_avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS - 1) / 2, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 3);\n app.atoms.push(cat);\n app.atoms.push(laserPointer);\n app.addRule(new _avo_rule_types_cny2022_controls__WEBPACK_IMPORTED_MODULE_12__.default(app, cat, laserPointer));\n this.createOuterWalls();\n }\n }, {\n key: \"generate_cny2022_level_1\",\n value: function generate_cny2022_level_1() {\n var app = this._app;\n var cat = new _avo_atom_types_cny2022_cat__WEBPACK_IMPORTED_MODULE_5__.default(app, 3, (_avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 1) / 2);\n var laserPointer = new _avo_atom_types_cny2022_laser_pointer__WEBPACK_IMPORTED_MODULE_6__.default(app, (_avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS - 1) / 2, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 3);\n app.atoms.push(cat);\n app.atoms.push(laserPointer);\n app.addRule(new _avo_rule_types_cny2022_controls__WEBPACK_IMPORTED_MODULE_12__.default(app, cat, laserPointer));\n app.addRule(new _avo_rule_types_cny2022_victory__WEBPACK_IMPORTED_MODULE_13__.default(app)); // Layout\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 11, 6, 18, 1));\n app.atoms.push(new _avo_atom_types_cny2022_glass_wall__WEBPACK_IMPORTED_MODULE_10__.default(app, 11, 15, 18, 1));\n app.atoms.push(new _avo_atom_types_cny2022_glass_wall__WEBPACK_IMPORTED_MODULE_10__.default(app, 11, 16, 1, 3));\n app.atoms.push(new _avo_atom_types_cny2022_glass_wall__WEBPACK_IMPORTED_MODULE_10__.default(app, 28, 16, 1, 3));\n app.atoms.push(new _avo_atom_types_cny2022_goal__WEBPACK_IMPORTED_MODULE_7__.default(app, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS - 3, (_avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 1) / 2)); // Coins\n\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 11.5, 3));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 15.5, 3));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 19.5, 3));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 23.5, 3));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 27.5, 3));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 5.5, 14));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 3.5, 15.5));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 7.5, 15.5));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 4.5, 17.5));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 6.5, 17.5)); // Vases\n\n app.atoms.push(new _avo_atom_types_cny2022_vase__WEBPACK_IMPORTED_MODULE_9__.default(app, 13.5, 9.5));\n app.atoms.push(new _avo_atom_types_cny2022_vase__WEBPACK_IMPORTED_MODULE_9__.default(app, 16.5, 9.5));\n app.atoms.push(new _avo_atom_types_cny2022_vase__WEBPACK_IMPORTED_MODULE_9__.default(app, 19.5, 9.5));\n app.atoms.push(new _avo_atom_types_cny2022_vase__WEBPACK_IMPORTED_MODULE_9__.default(app, 22.5, 9.5));\n app.atoms.push(new _avo_atom_types_cny2022_vase__WEBPACK_IMPORTED_MODULE_9__.default(app, 25.5, 9.5));\n this.createOuterWalls();\n }\n }, {\n key: \"createOuterWalls\",\n value: function createOuterWalls() {\n var app = this._app;\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 0, 0, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS, 1)); // North Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 0, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 1, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS, 1)); // South Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 0, 1, 1, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 2)); // West Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS - 1, 1, 1, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 2)); // East Wall\n }\n }]);\n\n return Levels;\n}();\n\n\n\n//# sourceURL=webpack://cny2022/./src/avo/levels.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Levels)\n/* harmony export */ });\n/* harmony import */ var _avo_constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @avo/constants */ \"./src/avo/constants.js\");\n/* harmony import */ var _avo_atom_types_hero__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @avo/atom/types/hero */ \"./src/avo/atom/types/hero.js\");\n/* harmony import */ var _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @avo/atom/types/wall */ \"./src/avo/atom/types/wall.js\");\n/* harmony import */ var _avo_atom_types_ball__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @avo/atom/types/ball */ \"./src/avo/atom/types/ball.js\");\n/* harmony import */ var _avo_atom_types_enemy__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! @avo/atom/types/enemy */ \"./src/avo/atom/types/enemy.js\");\n/* harmony import */ var _avo_atom_types_cny2022_cat__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! @avo/atom/types/cny2022/cat */ \"./src/avo/atom/types/cny2022/cat.js\");\n/* harmony import */ var _avo_atom_types_cny2022_laser_pointer__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! @avo/atom/types/cny2022/laser-pointer */ \"./src/avo/atom/types/cny2022/laser-pointer.js\");\n/* harmony import */ var _avo_atom_types_cny2022_goal__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! @avo/atom/types/cny2022/goal */ \"./src/avo/atom/types/cny2022/goal.js\");\n/* harmony import */ var _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! @avo/atom/types/cny2022/coin */ \"./src/avo/atom/types/cny2022/coin.js\");\n/* harmony import */ var _avo_atom_types_cny2022_vase__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! @avo/atom/types/cny2022/vase */ \"./src/avo/atom/types/cny2022/vase.js\");\n/* harmony import */ var _avo_atom_types_cny2022_glass_wall__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! @avo/atom/types/cny2022/glass-wall */ \"./src/avo/atom/types/cny2022/glass-wall.js\");\n/* harmony import */ var _avo_rule_types_zelda_controls__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! @avo/rule/types/zelda-controls */ \"./src/avo/rule/types/zelda-controls.js\");\n/* harmony import */ var _avo_rule_types_cny2022_controls__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! @avo/rule/types/cny2022-controls */ \"./src/avo/rule/types/cny2022-controls.js\");\n/* harmony import */ var _avo_rule_types_cny2022_victory__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! @avo/rule/types/cny2022-victory */ \"./src/avo/rule/types/cny2022-victory.js\");\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nvar CNY2022_HIGHSCORE_STORAGE_KEY = 'cny2022.highscores';\n\nvar Levels = /*#__PURE__*/function () {\n function Levels(app) {\n _classCallCheck(this, Levels);\n\n this._app = app;\n this.current = 0;\n this.cny2022LevelGenerators = [this.generate_cny2022_level_1.bind(this), this.generate_cny2022_level_2.bind(this)];\n this.cny2022HighScores = this.cny2022LevelGenerators.map(function () {\n return undefined;\n });\n this.loadCNY2022HighScores();\n }\n\n _createClass(Levels, [{\n key: \"reset\",\n value: function reset() {\n var app = this._app;\n app.hero = undefined;\n app.atoms = [];\n app.clearRules();\n app.camera = {\n target: null,\n x: 0,\n y: 0\n };\n app.playerAction = _avo_constants__WEBPACK_IMPORTED_MODULE_0__.PLAYER_ACTIONS.IDLE;\n app.setInteractionMenu(false);\n app.paused = false;\n }\n }, {\n key: \"load\",\n value: function load() {\n var level = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n var app = this._app;\n this.current = level;\n this.reset();\n\n if (this.cny2022LevelGenerators[level]) {\n this.cny2022LevelGenerators[level]();\n }\n }\n }, {\n key: \"reload\",\n value: function reload() {\n this.load(this.current);\n }\n }, {\n key: \"registerCNY2022Score\",\n value: function registerCNY2022Score(score) {\n var highscore = this.cny2022HighScores[this.current];\n\n if (highscore === undefined || highscore < score) {\n this.cny2022HighScores[this.current] = score;\n }\n\n this.saveCNY2022HighScores();\n }\n }, {\n key: \"saveCNY2022HighScores\",\n value: function saveCNY2022HighScores() {\n var _window;\n\n var storage = (_window = window) === null || _window === void 0 ? void 0 : _window.localStorage;\n if (!storage) return;\n storage.setItem(CNY2022_HIGHSCORE_STORAGE_KEY, JSON.stringify(this.cny2022HighScores));\n }\n }, {\n key: \"loadCNY2022HighScores\",\n value: function loadCNY2022HighScores() {\n var _window2;\n\n var storage = (_window2 = window) === null || _window2 === void 0 ? void 0 : _window2.localStorage;\n if (!storage) return;\n\n try {\n var str = storage.getItem(CNY2022_HIGHSCORE_STORAGE_KEY);\n this.cny2022HighScores = str ? JSON.parse(str) : [];\n } catch (err) {\n this.cny2022HighScores = [];\n console.error(err);\n }\n }\n /*\r\n Default top-down adventure level.\r\n */\n\n }, {\n key: \"generate_zelda_default\",\n value: function generate_zelda_default() {\n var app = this._app;\n app.hero = new _avo_atom_types_hero__WEBPACK_IMPORTED_MODULE_1__.default(app, 11, 1);\n app.atoms.push(app.hero);\n app.camera.target = app.hero;\n app.addRule(new _avo_rule_types_zelda_controls__WEBPACK_IMPORTED_MODULE_11__.default(app)); // app.addRule(new CNY2022Controls(app))\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 0, 0, 1, 23)); // West Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 22, 0, 1, 23)); // East Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 1, 0, 21, 1)); // North Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 1, 22, 21, 1)); // South Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 3, 2, 3, 1));\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 3, 4, 3, 1));\n app.atoms.push(new _avo_atom_types_ball__WEBPACK_IMPORTED_MODULE_3__.default(app, 8, 6));\n var enemy = new _avo_atom_types_enemy__WEBPACK_IMPORTED_MODULE_4__.default(app, 4, 8);\n enemy.rotation = -45 / 180 * Math.PI;\n app.atoms.push(enemy);\n }\n }, {\n key: \"generate_cny2022_default\",\n value: function generate_cny2022_default() {\n var app = this._app;\n var cat = new _avo_atom_types_cny2022_cat__WEBPACK_IMPORTED_MODULE_5__.default(app, 3, 13);\n var laserPointer = new _avo_atom_types_cny2022_laser_pointer__WEBPACK_IMPORTED_MODULE_6__.default(app, (_avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS - 1) / 2, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 3);\n app.atoms.push(cat);\n app.atoms.push(laserPointer);\n app.addRule(new _avo_rule_types_cny2022_controls__WEBPACK_IMPORTED_MODULE_12__.default(app, cat, laserPointer));\n this.createOuterWalls();\n }\n }, {\n key: \"generate_cny2022_level_1\",\n value: function generate_cny2022_level_1() {\n var app = this._app;\n var cat = new _avo_atom_types_cny2022_cat__WEBPACK_IMPORTED_MODULE_5__.default(app, 3, (_avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 1) / 2);\n var laserPointer = new _avo_atom_types_cny2022_laser_pointer__WEBPACK_IMPORTED_MODULE_6__.default(app, (_avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS - 1) / 2, 3);\n app.atoms.push(cat);\n app.atoms.push(laserPointer);\n app.addRule(new _avo_rule_types_cny2022_controls__WEBPACK_IMPORTED_MODULE_12__.default(app, cat, laserPointer));\n app.addRule(new _avo_rule_types_cny2022_victory__WEBPACK_IMPORTED_MODULE_13__.default(app)); // Layout\n\n app.atoms.push(new _avo_atom_types_cny2022_glass_wall__WEBPACK_IMPORTED_MODULE_10__.default(app, 11, 6, 18, 1));\n app.atoms.push(new _avo_atom_types_cny2022_glass_wall__WEBPACK_IMPORTED_MODULE_10__.default(app, 11, 1, 1, 5));\n app.atoms.push(new _avo_atom_types_cny2022_glass_wall__WEBPACK_IMPORTED_MODULE_10__.default(app, 28, 1, 1, 5));\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 11, 13, 18, 1));\n app.atoms.push(new _avo_atom_types_cny2022_goal__WEBPACK_IMPORTED_MODULE_7__.default(app, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS - 3, (_avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 1) / 2)); // Coins\n\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 18, 11));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 21, 11));\n\n for (var i = 0; i < 4; i++) {\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 15 + i * 3, 15));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 15 + i * 3, 17));\n }\n\n this.createOuterWalls();\n }\n }, {\n key: \"generate_cny2022_level_2\",\n value: function generate_cny2022_level_2() {\n var app = this._app;\n var cat = new _avo_atom_types_cny2022_cat__WEBPACK_IMPORTED_MODULE_5__.default(app, 3, (_avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 1) / 2);\n var laserPointer = new _avo_atom_types_cny2022_laser_pointer__WEBPACK_IMPORTED_MODULE_6__.default(app, (_avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS - 1) / 2, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 3);\n app.atoms.push(cat);\n app.atoms.push(laserPointer);\n app.addRule(new _avo_rule_types_cny2022_controls__WEBPACK_IMPORTED_MODULE_12__.default(app, cat, laserPointer));\n app.addRule(new _avo_rule_types_cny2022_victory__WEBPACK_IMPORTED_MODULE_13__.default(app)); // Layout\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 11, 6, 18, 1));\n app.atoms.push(new _avo_atom_types_cny2022_glass_wall__WEBPACK_IMPORTED_MODULE_10__.default(app, 11, 15, 18, 1));\n app.atoms.push(new _avo_atom_types_cny2022_glass_wall__WEBPACK_IMPORTED_MODULE_10__.default(app, 11, 16, 1, 3));\n app.atoms.push(new _avo_atom_types_cny2022_glass_wall__WEBPACK_IMPORTED_MODULE_10__.default(app, 28, 16, 1, 3));\n app.atoms.push(new _avo_atom_types_cny2022_goal__WEBPACK_IMPORTED_MODULE_7__.default(app, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS - 3, (_avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 1) / 2)); // Coins\n\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 11.5, 3));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 15.5, 3));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 19.5, 3));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 23.5, 3));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 27.5, 3));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 5.5, 14));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 3.5, 15.5));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 7.5, 15.5));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 4.5, 17.5));\n app.atoms.push(new _avo_atom_types_cny2022_coin__WEBPACK_IMPORTED_MODULE_8__.default(app, 6.5, 17.5)); // Vases\n\n app.atoms.push(new _avo_atom_types_cny2022_vase__WEBPACK_IMPORTED_MODULE_9__.default(app, 13.5, 9.5));\n app.atoms.push(new _avo_atom_types_cny2022_vase__WEBPACK_IMPORTED_MODULE_9__.default(app, 16.5, 9.5));\n app.atoms.push(new _avo_atom_types_cny2022_vase__WEBPACK_IMPORTED_MODULE_9__.default(app, 19.5, 9.5));\n app.atoms.push(new _avo_atom_types_cny2022_vase__WEBPACK_IMPORTED_MODULE_9__.default(app, 22.5, 9.5));\n app.atoms.push(new _avo_atom_types_cny2022_vase__WEBPACK_IMPORTED_MODULE_9__.default(app, 25.5, 9.5));\n this.createOuterWalls();\n }\n }, {\n key: \"createOuterWalls\",\n value: function createOuterWalls() {\n var app = this._app;\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 0, 0, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS, 1)); // North Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 0, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 1, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS, 1)); // South Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, 0, 1, 1, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 2)); // West Wall\n\n app.atoms.push(new _avo_atom_types_wall__WEBPACK_IMPORTED_MODULE_2__.default(app, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_COLS - 1, 1, 1, _avo_constants__WEBPACK_IMPORTED_MODULE_0__.CNY2022_ROWS - 2)); // East Wall\n }\n }]);\n\n return Levels;\n}();\n\n\n\n//# sourceURL=webpack://cny2022/./src/avo/levels.js?"); /***/ }), @@ -276,7 +276,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac \***********************************************/ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ CNY2022Victory)\n/* harmony export */ });\n/* harmony import */ var _avo_rule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @avo/rule */ \"./src/avo/rule/index.js\");\n/* harmony import */ var _avo_constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @avo/constants */ \"./src/avo/constants.js\");\n/* harmony import */ var _avo_misc__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @avo/misc */ \"./src/avo/misc.js\");\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _get(target, property, receiver) { if (typeof Reflect !== \"undefined\" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); }\n\nfunction _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\nvar VICTORY_COUNTER_MAX = 500;\n/*\r\nThis Rule keeps track of scores and the victory condition.\r\n- If the Cat touches a Coin, the score will increase.\r\n- If a Vase is broken, the score will decrease.\r\n- If the Cat touches the Goal, the player wins the level.\r\n- If the player has won the level, play the victory message.\r\n- The player should always be able to see their score\r\n */\n\nvar CNY2022Victory = /*#__PURE__*/function (_Rule) {\n _inherits(CNY2022Victory, _Rule);\n\n var _super = _createSuper(CNY2022Victory);\n\n function CNY2022Victory(app) {\n var _this;\n\n _classCallCheck(this, CNY2022Victory);\n\n _this = _super.call(this, app);\n _this._type = 'cny2022-victory';\n _this.victory = false; // bool: has the player finished the level?\n\n _this.victoryCounter = 0;\n _this.score = 0;\n return _this;\n }\n\n _createClass(CNY2022Victory, [{\n key: \"play\",\n value: function play(timeStep) {\n var app = this._app;\n\n _get(_getPrototypeOf(CNY2022Victory.prototype), \"play\", this).call(this, timeStep);\n\n if (this.victory) {\n this.victoryCounter = Math.min(this.victoryCounter + timeStep, VICTORY_COUNTER_MAX);\n if (this.victoryCounter >= VICTORY_COUNTER_MAX) app.paused = true;\n }\n }\n /*\r\n Paint the score and/or victory screen\r\n - Paint the victory screen, if the player has won the level\r\n - Paint the score on the top layer\r\n */\n\n }, {\n key: \"paint\",\n value: function paint() {\n var layer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n if (layer !== 2) return;\n var app = this._app;\n var c2d = this._app.canvas2d; // Victory message\n\n if (this.victory) {\n var VICTORY_TEXT = 'YOU DID IT!';\n var VICTORY_X = app.canvasWidth / 2;\n var VICTORY_Y = app.canvasHeight / 2;\n var VICTORY_SIZE_START = 5;\n var VICTORY_SIZE_END = 10; // Paint victory text\n\n var progress = Math.min(this.victoryCounter / VICTORY_COUNTER_MAX, 1); // returns 0.0 to 1.0\n\n var smoothedProgress = (0,_avo_misc__WEBPACK_IMPORTED_MODULE_2__.easeOut)(progress);\n var textSize = (smoothedProgress * (VICTORY_SIZE_END - VICTORY_SIZE_START) + VICTORY_SIZE_START).toFixed(2);\n var blockOpacity = smoothedProgress * 0.5;\n c2d.fillStyle = \"rgba(128, 128, 128, \".concat(blockOpacity, \")\");\n c2d.fillRect(0, 0, app.canvasWidth, app.canvasHeight);\n c2d.font = \"\".concat(textSize, \"em Source Code Pro\");\n c2d.textAlign = 'center';\n c2d.textBaseline = 'middle';\n c2d.lineWidth = 8;\n c2d.strokeStyle = '#eee';\n c2d.strokeText(VICTORY_TEXT, VICTORY_X, VICTORY_Y);\n c2d.fillStyle = '#c44';\n c2d.fillText(VICTORY_TEXT, VICTORY_X, VICTORY_Y);\n } // Score\n\n\n var SCORE_TEXT = \"\".concat(this.score, \" points\");\n var SCORE_X = app.canvasWidth - _avo_constants__WEBPACK_IMPORTED_MODULE_1__.TILE_SIZE * 1;\n var SCORE_Y = app.canvasHeight - _avo_constants__WEBPACK_IMPORTED_MODULE_1__.TILE_SIZE * 0.1;\n c2d.font = \"3em sans-serif\";\n c2d.textAlign = 'right';\n c2d.textBaseline = 'bottom';\n c2d.lineWidth = 8;\n c2d.strokeStyle = '#eee';\n c2d.strokeText(SCORE_TEXT, SCORE_X, SCORE_Y);\n c2d.fillStyle = '#ca4';\n c2d.fillText(SCORE_TEXT, SCORE_X, SCORE_Y);\n }\n /*\r\n Triggers the victory for the level. Usually called by Goal Atom.\r\n */\n\n }, {\n key: \"triggerVictory\",\n value: function triggerVictory() {\n if (this.victory) return; // Don't trigger more than once\n\n var app = this._app;\n this.victory = true;\n }\n }]);\n\n return CNY2022Victory;\n}(_avo_rule__WEBPACK_IMPORTED_MODULE_0__.default);\n\n\n\n//# sourceURL=webpack://cny2022/./src/avo/rule/types/cny2022-victory.js?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ CNY2022Victory)\n/* harmony export */ });\n/* harmony import */ var _avo_rule__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @avo/rule */ \"./src/avo/rule/index.js\");\n/* harmony import */ var _avo_constants__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @avo/constants */ \"./src/avo/constants.js\");\n/* harmony import */ var _avo_misc__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @avo/misc */ \"./src/avo/misc.js\");\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _get(target, property, receiver) { if (typeof Reflect !== \"undefined\" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); }\n\nfunction _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\nvar VICTORY_COUNTER_MAX = 500;\nvar RETURN_TO_HOME_MENU_COUNTER_MAX = 1500;\n/*\r\nThis Rule keeps track of scores and the victory condition.\r\n- If the Cat touches a Coin, the score will increase.\r\n- If a Vase is broken, the score will decrease.\r\n- If the Cat touches the Goal, the player wins the level.\r\n- If the player has won the level, play the victory message.\r\n- The player should always be able to see their score\r\n */\n\nvar CNY2022Victory = /*#__PURE__*/function (_Rule) {\n _inherits(CNY2022Victory, _Rule);\n\n var _super = _createSuper(CNY2022Victory);\n\n function CNY2022Victory(app) {\n var _this;\n\n _classCallCheck(this, CNY2022Victory);\n\n _this = _super.call(this, app);\n _this._type = 'cny2022-victory'; // Once the player has finished the level, show the victory message over a\n // period of time, and then pause the gameplay.\n\n _this.victory = false; // bool: has the player finished the level?\n\n _this.victoryCounter = 0; // Once the player has seen the victory message, wait a moment before\n // opening the home menu. (The home menu should only be opened automatically\n // ONCE.)\n\n _this.returnToHomeMenuCounter = 0;\n _this.returnedToHomeMenu = false; // bool: has the home menu been opened?\n\n _this.score = 0;\n return _this;\n }\n\n _createClass(CNY2022Victory, [{\n key: \"play\",\n value: function play(timeStep) {\n var app = this._app;\n\n _get(_getPrototypeOf(CNY2022Victory.prototype), \"play\", this).call(this, timeStep); // If the cat has reached the exit, run the victory phase.\n\n\n if (this.victory) {\n this.victoryCounter = Math.min(this.victoryCounter + timeStep, VICTORY_COUNTER_MAX); // Victory phase part 1: show the victory message over a period of time,\n // and after that pause the game.\n\n if (this.victoryCounter >= VICTORY_COUNTER_MAX) {\n app.paused = true;\n this.returnToHomeMenuCounter = Math.min(this.returnToHomeMenuCounter + timeStep, RETURN_TO_HOME_MENU_COUNTER_MAX); // Victory phase part 2: once the victory message has finished playing,\n // wait a moment and then open the home menu again. Note that the home\n // menu should only be opened automatically ONCE.\n\n if (this.returnToHomeMenuCounter >= RETURN_TO_HOME_MENU_COUNTER_MAX && !this.returnedToHomeMenu) {\n this.returnedToHomeMenu = true;\n app.setHomeMenu(true);\n }\n }\n }\n }\n /*\r\n Paint the score and/or victory screen\r\n - Paint the victory screen, if the player has won the level\r\n - Paint the score on the top layer\r\n */\n\n }, {\n key: \"paint\",\n value: function paint() {\n var layer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;\n if (layer !== 2) return;\n var app = this._app;\n var c2d = this._app.canvas2d; // Victory message\n\n if (this.victory) {\n var VICTORY_TEXT = 'YOU DID IT!';\n var VICTORY_X = app.canvasWidth / 2;\n var VICTORY_Y = app.canvasHeight / 2;\n var VICTORY_SIZE_START = 5;\n var VICTORY_SIZE_END = 10; // Paint victory text\n\n var progress = Math.min(this.victoryCounter / VICTORY_COUNTER_MAX, 1); // returns 0.0 to 1.0\n\n var smoothedProgress = (0,_avo_misc__WEBPACK_IMPORTED_MODULE_2__.easeOut)(progress);\n var textSize = (smoothedProgress * (VICTORY_SIZE_END - VICTORY_SIZE_START) + VICTORY_SIZE_START).toFixed(2);\n var blockOpacity = smoothedProgress * 0.5;\n c2d.fillStyle = \"rgba(128, 128, 128, \".concat(blockOpacity, \")\");\n c2d.fillRect(0, 0, app.canvasWidth, app.canvasHeight);\n c2d.font = \"\".concat(textSize, \"em Source Code Pro\");\n c2d.textAlign = 'center';\n c2d.textBaseline = 'middle';\n c2d.lineWidth = 8;\n c2d.strokeStyle = '#eee';\n c2d.strokeText(VICTORY_TEXT, VICTORY_X, VICTORY_Y);\n c2d.fillStyle = '#c44';\n c2d.fillText(VICTORY_TEXT, VICTORY_X, VICTORY_Y);\n } // Score\n\n\n var SCORE_TEXT = \"\".concat(this.score, \" points\");\n var SCORE_X = app.canvasWidth - _avo_constants__WEBPACK_IMPORTED_MODULE_1__.TILE_SIZE * 1;\n var SCORE_Y = app.canvasHeight - _avo_constants__WEBPACK_IMPORTED_MODULE_1__.TILE_SIZE * 0.1;\n c2d.font = \"3em sans-serif\";\n c2d.textAlign = 'right';\n c2d.textBaseline = 'bottom';\n c2d.lineWidth = 8;\n c2d.strokeStyle = '#eee';\n c2d.strokeText(SCORE_TEXT, SCORE_X, SCORE_Y);\n c2d.fillStyle = '#ca4';\n c2d.fillText(SCORE_TEXT, SCORE_X, SCORE_Y);\n }\n /*\r\n Triggers the victory for the level. Usually called by Goal Atom.\r\n */\n\n }, {\n key: \"triggerVictory\",\n value: function triggerVictory() {\n if (this.victory) return; // Don't trigger more than once\n\n this._app.levels.registerCNY2022Score(this.score);\n\n this.victory = true;\n }\n }]);\n\n return CNY2022Victory;\n}(_avo_rule__WEBPACK_IMPORTED_MODULE_0__.default);\n\n\n\n//# sourceURL=webpack://cny2022/./src/avo/rule/types/cny2022-victory.js?"); /***/ }), diff --git a/assets/cny2022.png b/assets/cny2022.png new file mode 100644 index 0000000..ca1ee32 Binary files /dev/null and b/assets/cny2022.png differ diff --git a/index.html b/index.html index 7b403ac..464b169 100644 --- a/index.html +++ b/index.html @@ -34,10 +34,51 @@

Chinese New Year 2022 - Year of the Tiger

-

Adventure Menu

-

- Created by Shaun A. Noordin -

+ +
diff --git a/src/avo/avo.js b/src/avo/avo.js index 02fb050..e0e9292 100644 --- a/src/avo/avo.js +++ b/src/avo/avo.js @@ -120,7 +120,8 @@ export default class AvO { // Let's go! this.initialised = true this.showUI() - this.levels.load(STARTING_LEVEL) + this.setHomeMenu(true) + // this.levels.load(STARTING_LEVEL) } } @@ -440,6 +441,7 @@ export default class AvO { if (homeMenu) { this.html.homeMenu.style.visibility = 'visible' this.html.buttonReload.style.visibility = 'hidden' + this.updateCNY2022HomeMenu() } else { this.html.homeMenu.style.visibility = 'hidden' this.html.buttonReload.style.visibility = 'visible' @@ -447,6 +449,36 @@ export default class AvO { } } + /* + Set up the level list on the home menu. + */ + updateCNY2022HomeMenu () { + const levelsList = document.getElementById('levels-list') + while (levelsList.firstChild) { levelsList.removeChild(levelsList.firstChild) } // Clear + + for (let levelIndex = 0 ; levelIndex < this.levels.cny2022LevelGenerators.length; levelIndex ++) { + const levelNumber = levelIndex + 1 + const li = document.createElement('li') + const button = document.createElement('button') + const span = document.createElement('span') + + button.innerText = `Level ${levelNumber}` + button.addEventListener('click', () => { + this.levels.load(levelIndex) + this.setHomeMenu(false) + }) + + const score = this.levels.cny2022HighScores[levelIndex] + span.innerText = (Number.isInteger(score)) + ? `Score: ${score}` + : 'New!' + + li.appendChild(button) + li.appendChild(span) + levelsList.appendChild(li) + } + } + setInteractionMenu (interactionMenu) { const div = this.html.interactionMenu @@ -503,11 +535,13 @@ export default class AvO { break // DEBUG + /* case 'z': if (!this.interactionMenu) { this.setInteractionMenu(new Interaction(this)) } break + */ } // General input diff --git a/src/avo/levels.js b/src/avo/levels.js index 943de79..dfe30f5 100644 --- a/src/avo/levels.js +++ b/src/avo/levels.js @@ -18,10 +18,20 @@ import ZeldaControls from '@avo/rule/types/zelda-controls' import CNY2022Controls from '@avo/rule/types/cny2022-controls' import CNY2022Victory from '@avo/rule/types/cny2022-victory' +const CNY2022_HIGHSCORE_STORAGE_KEY = 'cny2022.highscores' + export default class Levels { constructor (app) { this._app = app this.current = 0 + + this.cny2022LevelGenerators = [ + this.generate_cny2022_level_1.bind(this), + this.generate_cny2022_level_2.bind(this), + ] + this.cny2022HighScores = this.cny2022LevelGenerators.map(() => undefined) + + this.loadCNY2022HighScores() } reset () { @@ -42,14 +52,44 @@ export default class Levels { this.current = level this.reset() - // this.generate_cny2022_default() - this.generate_cny2022_level_1() + + if (this.cny2022LevelGenerators[level]) { + this.cny2022LevelGenerators[level]() + } } reload () { this.load(this.current) } + registerCNY2022Score (score) { + const highscore = this.cny2022HighScores[this.current] + + if (highscore === undefined || highscore < score) { + this.cny2022HighScores[this.current] = score + } + + this.saveCNY2022HighScores() + } + + saveCNY2022HighScores () { + const storage = window?.localStorage + if (!storage) return + storage.setItem(CNY2022_HIGHSCORE_STORAGE_KEY, JSON.stringify(this.cny2022HighScores)) + } + + loadCNY2022HighScores () { + const storage = window?.localStorage + if (!storage) return + try { + const str = storage.getItem(CNY2022_HIGHSCORE_STORAGE_KEY) + this.cny2022HighScores = (str) ? JSON.parse(str) : [] + } catch (err) { + this.cny2022HighScores = [] + console.error(err) + } + } + /* Default top-down adventure level. */ @@ -92,6 +132,34 @@ export default class Levels { generate_cny2022_level_1 () { const app = this._app + const cat = new Cat(app, 3, (CNY2022_ROWS - 1) / 2) + const laserPointer = new LaserPointer(app, (CNY2022_COLS - 1) / 2, 3) + app.atoms.push(cat) + app.atoms.push(laserPointer) + app.addRule(new CNY2022Controls(app, cat, laserPointer)) + app.addRule(new CNY2022Victory(app)) + + // Layout + app.atoms.push(new GlassWall(app, 11, 6, 18, 1)) + app.atoms.push(new GlassWall(app, 11, 1, 1, 5)) + app.atoms.push(new GlassWall(app, 28, 1, 1, 5)) + app.atoms.push(new Wall(app, 11, 13, 18, 1)) + app.atoms.push(new Goal(app, CNY2022_COLS - 3, (CNY2022_ROWS - 1) / 2)) + + // Coins + app.atoms.push(new Coin(app, 18, 11)) + app.atoms.push(new Coin(app, 21, 11)) + for (let i = 0 ; i < 4 ; i++) { + app.atoms.push(new Coin(app, 15 + i * 3, 15)) + app.atoms.push(new Coin(app, 15 + i * 3, 17)) + } + + this.createOuterWalls() + } + + generate_cny2022_level_2 () { + const app = this._app + const cat = new Cat(app, 3, (CNY2022_ROWS - 1) / 2) const laserPointer = new LaserPointer(app, (CNY2022_COLS - 1) / 2, CNY2022_ROWS - 3) app.atoms.push(cat) diff --git a/src/avo/rule/types/cny2022-victory.js b/src/avo/rule/types/cny2022-victory.js index 7741547..7ab1e19 100644 --- a/src/avo/rule/types/cny2022-victory.js +++ b/src/avo/rule/types/cny2022-victory.js @@ -3,6 +3,7 @@ import { TILE_SIZE } from '@avo/constants' import { easeOut } from '@avo/misc' const VICTORY_COUNTER_MAX = 500 +const RETURN_TO_HOME_MENU_COUNTER_MAX = 1500 /* This Rule keeps track of scores and the victory condition. @@ -17,9 +18,17 @@ export default class CNY2022Victory extends Rule { super(app) this._type = 'cny2022-victory' + // Once the player has finished the level, show the victory message over a + // period of time, and then pause the gameplay. this.victory = false // bool: has the player finished the level? this.victoryCounter = 0 + // Once the player has seen the victory message, wait a moment before + // opening the home menu. (The home menu should only be opened automatically + // ONCE.) + this.returnToHomeMenuCounter = 0 + this.returnedToHomeMenu = false // bool: has the home menu been opened? + this.score = 0 } @@ -27,10 +36,25 @@ export default class CNY2022Victory extends Rule { const app = this._app super.play(timeStep) + // If the cat has reached the exit, run the victory phase. if (this.victory) { this.victoryCounter = Math.min(this.victoryCounter + timeStep, VICTORY_COUNTER_MAX) - if (this.victoryCounter >= VICTORY_COUNTER_MAX) app.paused = true + // Victory phase part 1: show the victory message over a period of time, + // and after that pause the game. + if (this.victoryCounter >= VICTORY_COUNTER_MAX) { + app.paused = true + + this.returnToHomeMenuCounter = Math.min(this.returnToHomeMenuCounter + timeStep, RETURN_TO_HOME_MENU_COUNTER_MAX) + + // Victory phase part 2: once the victory message has finished playing, + // wait a moment and then open the home menu again. Note that the home + // menu should only be opened automatically ONCE. + if (this.returnToHomeMenuCounter >= RETURN_TO_HOME_MENU_COUNTER_MAX && !this.returnedToHomeMenu) { + this.returnedToHomeMenu = true + app.setHomeMenu(true) + } + } } } @@ -93,7 +117,7 @@ export default class CNY2022Victory extends Rule { triggerVictory () { if (this.victory) return // Don't trigger more than once - const app = this._app + this._app.levels.registerCNY2022Score(this.score) this.victory = true } } diff --git a/src/main.scss b/src/main.scss index 7c111b2..5eed32c 100644 --- a/src/main.scss +++ b/src/main.scss @@ -120,6 +120,61 @@ table { } } + #home-menu { + #banner { + #card { + display: block; + max-width: 50%; + margin: 0 auto; + } + } + + #levels-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + list-style: none; + margin: 0.5em; + padding: 0; + justify-content: space-around; + + li { + font-size: 1em; + flex: 0 0 15%; + margin: 0.5em; + height: 4em; + display: flex; + flex-direction: column; + box-shadow: 0.1rem 0.1rem 0 rgba(0, 0, 0, 0.1); + padding: 0.5em; + + button { + cursor: pointer; + color: #fff; + background: #c44; + border: 2px solid #ccc; + border-radius: 1rem; + box-shadow: 0.1rem 0.1rem 0 rgba(0, 0, 0, 0.5); + + &:hover { + border: 2px solid #fff; + } + + &:active { + border: 2px solid #888; + } + } + + span { + display: block; + margin-top: 1.0em; + font-size: 0.75em; + text-align: center; + } + } + } + } + .ui-button { position: absolute; cursor: pointer;