diff --git a/dist/app.js b/dist/app.js index 3c98b05..959d651 100644 --- a/dist/app.js +++ b/dist/app.js @@ -1016,7 +1016,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Camera: () => (/* binding */ Camera)\n/* harmony export */ });\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./point */ \"./src/point.ts\");\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var rot_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! rot-js */ \"./node_modules/rot-js/lib/index.js\");\n/* harmony import */ var _input_utility__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./input-utility */ \"./src/input-utility.ts\");\n/* harmony import */ var tinygesture__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! tinygesture */ \"./node_modules/tinygesture/dist/TinyGesture.js\");\n\r\n\r\n\r\n\r\n\r\nclass Camera {\r\n constructor(game, ui) {\r\n this.game = game;\r\n this.ui = ui;\r\n this.viewport = {\r\n width: 360,\r\n height: 800,\r\n };\r\n this.currentZoom = 1;\r\n this.defaultZoom = 1;\r\n this.moveSpeed = 0.3;\r\n this.isPanning = false;\r\n this.handlePointerDrag = (g) => {\r\n let dragModifier = 1;\r\n this.isPanning = true;\r\n // increase drag speed to prevent sluggish feeling\r\n const modifiedVelocityX = g.velocityX * dragModifier;\r\n const modifiedVelocityY = g.velocityY * dragModifier;\r\n this.ui.gameDisplay.stage.pivot.x -=\r\n // scale the velocity by the size of the stage\r\n modifiedVelocityX / this.ui.gameDisplay.stage.scale.x;\r\n this.ui.gameDisplay.stage.pivot.y -=\r\n modifiedVelocityY / this.ui.gameDisplay.stage.scale.x;\r\n };\r\n this.handlePanEnd = (g) => {\r\n this.isPanning = false;\r\n let momentumModifier = 0.8;\r\n const momentumDuration = 300; // in milliseconds\r\n const momentumInterval = 16; // in milliseconds\r\n const momentumX = (g.velocityX * momentumModifier) / this.ui.gameDisplay.stage.scale.x;\r\n const momentumY = (g.velocityY * momentumModifier) / this.ui.gameDisplay.stage.scale.x;\r\n let elapsed = 0;\r\n const momentumTimer = setInterval(() => {\r\n elapsed += momentumInterval;\r\n const progress = elapsed / momentumDuration;\r\n if (progress >= 1) {\r\n clearInterval(momentumTimer);\r\n return;\r\n }\r\n const momentumStepX = momentumX * (1 - progress);\r\n const momentumStepY = momentumY * (1 - progress);\r\n this.ui.gameDisplay.stage.pivot.x -= momentumStepX;\r\n this.ui.gameDisplay.stage.pivot.y -= momentumStepY;\r\n }, momentumInterval);\r\n };\r\n this.handlePinchZoom = (g) => {\r\n // TODO: fix bugs\r\n // BUG: pinch zooming is not centered on the pinch point\r\n // BUG: pinch zooming initially sets scale to distance between fingers\r\n // BUG: weird behavior when moving while pinching\r\n const scaleSpeed = 0.2;\r\n const pivotX = this.ui.gameDisplay.stage.pivot.x;\r\n const pivotY = this.ui.gameDisplay.stage.pivot.y;\r\n const posX = this.ui.gameDisplay.stage.position.x;\r\n const posY = this.ui.gameDisplay.stage.position.y;\r\n const scaleX = this.ui.gameDisplay.stage.scale.x;\r\n const scaleY = this.ui.gameDisplay.stage.scale.y;\r\n let scaleDiff = scaleX - g.scale;\r\n scaleDiff *= -1 * scaleSpeed;\r\n scaleDiff = Math.max(-0.2, Math.min(0.2, scaleDiff));\r\n const newScaleX = scaleX * g.scale * (1 - scaleSpeed);\r\n const newScaleY = scaleY * g.scale * (1 - scaleSpeed);\r\n this.currentZoom += scaleDiff;\r\n this.ui.gameDisplay.stage.scale.x += scaleDiff;\r\n this.ui.gameDisplay.stage.scale.y += scaleDiff;\r\n };\r\n this.handleDoubleTap = (g) => {\r\n const zoomInAmount = 1.75;\r\n this.ui.gameDisplay.stage.scale.x *= zoomInAmount;\r\n this.ui.gameDisplay.stage.scale.y *= zoomInAmount;\r\n };\r\n this.handleZoom = (e) => {\r\n e.preventDefault();\r\n const scaleSpeed = 0.1;\r\n const pivotX = this.ui.gameDisplay.stage.pivot.x;\r\n const pivotY = this.ui.gameDisplay.stage.pivot.y;\r\n let scale = this.ui.gameDisplay.stage.scale.x;\r\n // modify the scale based on the scroll delta\r\n scale += -1 * Math.max(-1, Math.min(1, e.deltaY)) * scaleSpeed * scale;\r\n // clamp to reasonable values\r\n scale = Math.max(0.4, Math.min(10, scale));\r\n this.currentZoom = scale;\r\n // update the scale and position of the stage\r\n this.ui.gameDisplay.stage.setTransform(this.game.userInterface.gameDisplay.stage.position.x, this.game.userInterface.gameDisplay.stage.position.y, scale, // scale\r\n scale, null, // rotation\r\n null, // skew\r\n null, pivotX, pivotY);\r\n };\r\n this.keyMap = {};\r\n this.keyMap[rot_js__WEBPACK_IMPORTED_MODULE_2__.KEYS.VK_W] = 0; // up\r\n this.keyMap[rot_js__WEBPACK_IMPORTED_MODULE_2__.KEYS.VK_D] = 2; // right\r\n this.keyMap[rot_js__WEBPACK_IMPORTED_MODULE_2__.KEYS.VK_S] = 4; // down\r\n this.keyMap[rot_js__WEBPACK_IMPORTED_MODULE_2__.KEYS.VK_A] = 6; // left\r\n this.centerViewport(this.ui.gameDisplay.stage, this.ui.gameCanvasContainer.clientWidth, this.ui.gameCanvasContainer.clientHeight, this.viewport.width, this.viewport.height);\r\n this.defaultZoom = this.ui.gameDisplay.stage.scale.x;\r\n this.initEventListeners();\r\n }\r\n centerViewport(stage, screenWidth, screenHeight, virtualWidth, virtualHeight) {\r\n // not sure if this next line does anything...\r\n // this.gameDisplay.renderer.resize(screenWidth, screenHeight);\r\n const gameWidthPixels = this.game.gameSize.width * _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size;\r\n const gameHeightPixels = this.game.gameSize.height * _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size;\r\n const screenCenterX = screenWidth / 2;\r\n const screenCenterY = screenHeight / 2;\r\n this.setPivot(stage, gameWidthPixels / 2, gameHeightPixels / 2);\r\n stage.position.set(screenCenterX, screenCenterY);\r\n stage.scale.x = (screenWidth / virtualWidth) * this.currentZoom;\r\n stage.scale.y = (screenHeight / virtualHeight) * this.currentZoom;\r\n if (stage.scale.x < stage.scale.y) {\r\n stage.scale.y = stage.scale.x;\r\n }\r\n else {\r\n stage.scale.x = stage.scale.y;\r\n }\r\n }\r\n setPivot(stage, x, y) {\r\n stage.pivot.set(x, y);\r\n }\r\n centerOn(x, y) {\r\n // TODO\r\n console.log(`TODO: centerOn: ${x}, ${y}`);\r\n // const offset = (game.touchScreen ? touchOffsetY : 0);\r\n // const offset = 0;\r\n // const tw = ((x * -this.gameDisplay.getOptions().tileWidth) +\r\n // (this.gameDisplayOptions.width * this.gameDisplayOptions.tileWidth / 2) + -4);\r\n // const th = ((y * -this.gameDisplayOptions.tileHeight) +\r\n // (this.gameDisplayOptions.height * this.gameDisplayOptions.tileHeight / 2) + offset);\r\n // if (this.gameCanvasContainer) {\r\n // // this applies the animation effect\r\n // this.gameCanvasContainer.style.transition = \"transform 0.5s ease-out 0s\";\r\n // if (this.gameCanvas) {\r\n // this.gameCanvas.getContext('2d').imageSmoothingEnabled = false;\r\n // }\r\n // // this sets the scale and position to focus on the player\r\n // this.gameCanvasContainer.style.transform =\r\n // \"scale(\" + this.scale + \") \" + \"translate3d(\" + Math.floor(tw) +\r\n // \"px,\" + Math.floor(th) + \"px,0px)\";\r\n // }\r\n }\r\n setViewportZoom(stage, newZoom) {\r\n this.currentZoom = newZoom;\r\n stage.scale.set(newZoom);\r\n }\r\n initEventListeners() {\r\n window.onresize = () => this.centerViewport(this.ui.gameDisplay.stage, this.ui.gameCanvasContainer.clientWidth, this.ui.gameCanvasContainer.clientHeight, this.viewport.width, this.viewport.height);\r\n const gesture = new tinygesture__WEBPACK_IMPORTED_MODULE_4__[\"default\"](this.ui.gameCanvasContainer);\r\n gesture.on(\"pinch\", (event) => {\r\n event.preventDefault();\r\n this.handlePinchZoom(gesture);\r\n });\r\n gesture.on(\"panmove\", (event) => {\r\n this.handlePointerDrag(gesture);\r\n });\r\n gesture.on(\"panend\", (event) => {\r\n this.handlePanEnd(gesture);\r\n });\r\n gesture.on(\"doubletap\", (event) => {\r\n // The gesture was a double tap. The 'tap' event will also have been fired on\r\n // the first tap.\r\n console.log(\"double tap\");\r\n this.handleDoubleTap(gesture);\r\n });\r\n this.ui.gameCanvasContainer.addEventListener(\"wheel\", this.handleZoom, {\r\n passive: false,\r\n });\r\n window.addEventListener(\"keydown\", this.handleInput.bind(this), {\r\n passive: false,\r\n });\r\n }\r\n Act() {\r\n return _input_utility__WEBPACK_IMPORTED_MODULE_3__.InputUtility.waitForInput(this.handleInput.bind(this));\r\n }\r\n moveCamera(stage, direction) {\r\n stage.pivot.x +=\r\n (direction[0] * _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size * this.moveSpeed) / this.currentZoom;\r\n stage.pivot.y +=\r\n (direction[1] * _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size * this.moveSpeed) / this.currentZoom;\r\n return true;\r\n }\r\n // public getViewportSizeInTiles(pad: boolean = false): {\r\n // width: number;\r\n // height: number;\r\n // } {\r\n // let width =\r\n // this.ui.gameCanvasContainer.clientWidth /\r\n // (Tile.size * this.ui.gameDisplay.stage.scale.x);\r\n // let height =\r\n // this.ui.gameCanvasContainer.clientHeight /\r\n // (Tile.size * this.ui.gameDisplay.stage.scale.x);\r\n // if (pad) {\r\n // // min of 5 padding\r\n // width += Math.max(5, width / 4);\r\n // height += Math.max(5, height / 4);\r\n // }\r\n // width = Math.ceil(width);\r\n // height = Math.ceil(height);\r\n // return { width, height };\r\n // }\r\n getViewportInTiles(pad = false) {\r\n let width = this.ui.gameCanvasContainer.clientWidth /\r\n (_tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size * this.ui.gameDisplay.stage.scale.x);\r\n let height = this.ui.gameCanvasContainer.clientHeight /\r\n (_tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size * this.ui.gameDisplay.stage.scale.x);\r\n if (pad) {\r\n // min of 5 padding\r\n width += Math.max(10, width);\r\n height += Math.max(10, height);\r\n }\r\n const center = this.getViewportCenterTile();\r\n width = Math.ceil(width);\r\n height = Math.ceil(height);\r\n return { width, height, center };\r\n }\r\n getViewportCenterTile() {\r\n const pivotXTile = this.game.userInterface.gameDisplay.stage.pivot.x / _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size;\r\n const pivotYTile = this.game.userInterface.gameDisplay.stage.pivot.y / _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size;\r\n let tilesOffsetX = pivotXTile * this.game.userInterface.gameDisplay.stage.scale.x;\r\n tilesOffsetX = Math.ceil(pivotXTile);\r\n const xPoint = tilesOffsetX;\r\n let tilesOffsetY = (pivotYTile / _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size) *\r\n this.game.userInterface.gameDisplay.stage.scale.y;\r\n tilesOffsetY = Math.ceil(pivotYTile);\r\n const yPoint = tilesOffsetY;\r\n return new _point__WEBPACK_IMPORTED_MODULE_0__.Point(xPoint, yPoint);\r\n }\r\n handleInput(event) {\r\n let validInput = false;\r\n let code = event.keyCode;\r\n if (code in this.keyMap) {\r\n let diff = rot_js__WEBPACK_IMPORTED_MODULE_2__.DIRS[8][this.keyMap[code]];\r\n if (this.moveCamera(this.ui.gameDisplay.stage, diff)) {\r\n validInput = true;\r\n }\r\n this.moveCamera(this.ui.gameDisplay.stage, diff);\r\n }\r\n else if (code === rot_js__WEBPACK_IMPORTED_MODULE_2__.KEYS.VK_HOME) {\r\n this.setViewportZoom(this.ui.gameDisplay.stage, this.defaultZoom);\r\n this.centerViewport(this.ui.gameDisplay.stage, this.ui.gameCanvasContainer.clientWidth, this.ui.gameCanvasContainer.clientHeight, this.viewport.width, this.viewport.height);\r\n validInput = true;\r\n }\r\n return validInput;\r\n }\r\n}\r\n\n\n//# sourceURL=webpack://rotjs-typescript-basics/./src/camera.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Camera: () => (/* binding */ Camera)\n/* harmony export */ });\n/* harmony import */ var _point__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./point */ \"./src/point.ts\");\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var rot_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! rot-js */ \"./node_modules/rot-js/lib/index.js\");\n/* harmony import */ var _input_utility__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./input-utility */ \"./src/input-utility.ts\");\n/* harmony import */ var tinygesture__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! tinygesture */ \"./node_modules/tinygesture/dist/TinyGesture.js\");\n\r\n\r\n\r\n\r\n\r\nclass Camera {\r\n constructor(game, ui) {\r\n this.game = game;\r\n this.ui = ui;\r\n this.viewport = {\r\n width: 360,\r\n height: 800,\r\n };\r\n this.currentZoom = 1;\r\n this.defaultZoom = 1;\r\n this.moveSpeed = 0.3;\r\n this.isPanning = false;\r\n this.handlePointerDrag = (g) => {\r\n let dragModifier = 1;\r\n this.isPanning = true;\r\n // increase drag speed to prevent sluggish feeling\r\n const modifiedVelocityX = g.velocityX * dragModifier;\r\n const modifiedVelocityY = g.velocityY * dragModifier;\r\n this.ui.gameDisplay.stage.pivot.x -=\r\n // scale the velocity by the size of the stage\r\n modifiedVelocityX / this.ui.gameDisplay.stage.scale.x;\r\n this.ui.gameDisplay.stage.pivot.y -=\r\n modifiedVelocityY / this.ui.gameDisplay.stage.scale.x;\r\n };\r\n this.handlePanEnd = (g) => {\r\n this.isPanning = false;\r\n let momentumModifier = 0.8;\r\n const momentumDuration = 250; // in milliseconds\r\n const momentumInterval = 1000 / 60; // in milliseconds\r\n const momentumX = (g.velocityX * momentumModifier) / this.ui.gameDisplay.stage.scale.x;\r\n const momentumY = (g.velocityY * momentumModifier) / this.ui.gameDisplay.stage.scale.x;\r\n let elapsed = 0;\r\n const momentumTimer = setInterval(() => {\r\n elapsed += momentumInterval;\r\n const progress = elapsed / momentumDuration;\r\n if (progress >= 1) {\r\n clearInterval(momentumTimer);\r\n return;\r\n }\r\n const momentumStepX = momentumX * (1 - progress);\r\n const momentumStepY = momentumY * (1 - progress);\r\n this.ui.gameDisplay.stage.pivot.x -= momentumStepX;\r\n this.ui.gameDisplay.stage.pivot.y -= momentumStepY;\r\n }, momentumInterval);\r\n };\r\n this.handlePinchZoom = (g) => {\r\n // TODO: fix bugs\r\n // BUG: pinch zooming is not centered on the pinch point\r\n // BUG: pinch zooming initially sets scale to distance between fingers\r\n // BUG: weird behavior when moving while pinching\r\n const scaleSpeed = 0.2;\r\n const pivotX = this.ui.gameDisplay.stage.pivot.x;\r\n const pivotY = this.ui.gameDisplay.stage.pivot.y;\r\n const posX = this.ui.gameDisplay.stage.position.x;\r\n const posY = this.ui.gameDisplay.stage.position.y;\r\n const scaleX = this.ui.gameDisplay.stage.scale.x;\r\n const scaleY = this.ui.gameDisplay.stage.scale.y;\r\n let scaleDiff = scaleX - g.scale;\r\n scaleDiff *= -1 * scaleSpeed;\r\n scaleDiff = Math.max(-0.2, Math.min(0.2, scaleDiff));\r\n const newScaleX = scaleX * g.scale * (1 - scaleSpeed);\r\n const newScaleY = scaleY * g.scale * (1 - scaleSpeed);\r\n this.currentZoom += scaleDiff;\r\n this.ui.gameDisplay.stage.scale.x += scaleDiff;\r\n this.ui.gameDisplay.stage.scale.y += scaleDiff;\r\n };\r\n this.handleDoubleTap = (g) => {\r\n const zoomInAmount = 1.75;\r\n this.ui.gameDisplay.stage.scale.x *= zoomInAmount;\r\n this.ui.gameDisplay.stage.scale.y *= zoomInAmount;\r\n };\r\n this.handleZoom = (e) => {\r\n e.preventDefault();\r\n const scaleSpeed = 0.1;\r\n const pivotX = this.ui.gameDisplay.stage.pivot.x;\r\n const pivotY = this.ui.gameDisplay.stage.pivot.y;\r\n let scale = this.ui.gameDisplay.stage.scale.x;\r\n // modify the scale based on the scroll delta\r\n scale += -1 * Math.max(-1, Math.min(1, e.deltaY)) * scaleSpeed * scale;\r\n // clamp to reasonable values\r\n scale = Math.max(0.4, Math.min(10, scale));\r\n this.currentZoom = scale;\r\n // update the scale and position of the stage\r\n this.ui.gameDisplay.stage.setTransform(this.game.userInterface.gameDisplay.stage.position.x, this.game.userInterface.gameDisplay.stage.position.y, scale, // scale\r\n scale, null, // rotation\r\n null, // skew\r\n null, pivotX, pivotY);\r\n };\r\n this.keyMap = {};\r\n this.keyMap[rot_js__WEBPACK_IMPORTED_MODULE_2__.KEYS.VK_W] = 0; // up\r\n this.keyMap[rot_js__WEBPACK_IMPORTED_MODULE_2__.KEYS.VK_D] = 2; // right\r\n this.keyMap[rot_js__WEBPACK_IMPORTED_MODULE_2__.KEYS.VK_S] = 4; // down\r\n this.keyMap[rot_js__WEBPACK_IMPORTED_MODULE_2__.KEYS.VK_A] = 6; // left\r\n this.centerViewport(this.ui.gameDisplay.stage, this.ui.gameCanvasContainer.clientWidth, this.ui.gameCanvasContainer.clientHeight, this.viewport.width, this.viewport.height);\r\n this.defaultZoom = this.ui.gameDisplay.stage.scale.x;\r\n this.initEventListeners();\r\n }\r\n centerViewport(stage, screenWidth, screenHeight, virtualWidth, virtualHeight) {\r\n // not sure if this next line does anything...\r\n // this.gameDisplay.renderer.resize(screenWidth, screenHeight);\r\n const gameWidthPixels = this.game.gameSize.width * _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size;\r\n const gameHeightPixels = this.game.gameSize.height * _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size;\r\n const screenCenterX = screenWidth / 2;\r\n const screenCenterY = screenHeight / 2;\r\n this.setPivot(stage, gameWidthPixels / 2, gameHeightPixels / 2);\r\n stage.position.set(screenCenterX, screenCenterY);\r\n stage.scale.x = (screenWidth / virtualWidth) * this.currentZoom;\r\n stage.scale.y = (screenHeight / virtualHeight) * this.currentZoom;\r\n if (stage.scale.x < stage.scale.y) {\r\n stage.scale.y = stage.scale.x;\r\n }\r\n else {\r\n stage.scale.x = stage.scale.y;\r\n }\r\n }\r\n setPivot(stage, x, y) {\r\n stage.pivot.set(x, y);\r\n }\r\n centerOn(x, y) {\r\n // TODO\r\n console.log(`TODO: centerOn: ${x}, ${y}`);\r\n // const offset = (game.touchScreen ? touchOffsetY : 0);\r\n // const offset = 0;\r\n // const tw = ((x * -this.gameDisplay.getOptions().tileWidth) +\r\n // (this.gameDisplayOptions.width * this.gameDisplayOptions.tileWidth / 2) + -4);\r\n // const th = ((y * -this.gameDisplayOptions.tileHeight) +\r\n // (this.gameDisplayOptions.height * this.gameDisplayOptions.tileHeight / 2) + offset);\r\n // if (this.gameCanvasContainer) {\r\n // // this applies the animation effect\r\n // this.gameCanvasContainer.style.transition = \"transform 0.5s ease-out 0s\";\r\n // if (this.gameCanvas) {\r\n // this.gameCanvas.getContext('2d').imageSmoothingEnabled = false;\r\n // }\r\n // // this sets the scale and position to focus on the player\r\n // this.gameCanvasContainer.style.transform =\r\n // \"scale(\" + this.scale + \") \" + \"translate3d(\" + Math.floor(tw) +\r\n // \"px,\" + Math.floor(th) + \"px,0px)\";\r\n // }\r\n }\r\n setViewportZoom(stage, newZoom) {\r\n this.currentZoom = newZoom;\r\n stage.scale.set(newZoom);\r\n }\r\n initEventListeners() {\r\n window.onresize = () => this.centerViewport(this.ui.gameDisplay.stage, this.ui.gameCanvasContainer.clientWidth, this.ui.gameCanvasContainer.clientHeight, this.viewport.width, this.viewport.height);\r\n const gesture = new tinygesture__WEBPACK_IMPORTED_MODULE_4__[\"default\"](this.ui.gameCanvasContainer);\r\n gesture.on(\"pinch\", (event) => {\r\n event.preventDefault();\r\n this.handlePinchZoom(gesture);\r\n });\r\n gesture.on(\"panmove\", (event) => {\r\n this.handlePointerDrag(gesture);\r\n });\r\n gesture.on(\"panend\", (event) => {\r\n this.handlePanEnd(gesture);\r\n });\r\n gesture.on(\"doubletap\", (event) => {\r\n // The gesture was a double tap. The 'tap' event will also have been fired on\r\n // the first tap.\r\n console.log(\"double tap\");\r\n this.handleDoubleTap(gesture);\r\n });\r\n this.ui.gameCanvasContainer.addEventListener(\"wheel\", this.handleZoom, {\r\n passive: false,\r\n });\r\n window.addEventListener(\"keydown\", this.handleInput.bind(this), {\r\n passive: false,\r\n });\r\n }\r\n Act() {\r\n return _input_utility__WEBPACK_IMPORTED_MODULE_3__.InputUtility.waitForInput(this.handleInput.bind(this));\r\n }\r\n moveCamera(stage, direction) {\r\n stage.pivot.x +=\r\n (direction[0] * _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size * this.moveSpeed) / this.currentZoom;\r\n stage.pivot.y +=\r\n (direction[1] * _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size * this.moveSpeed) / this.currentZoom;\r\n return true;\r\n }\r\n // public getViewportSizeInTiles(pad: boolean = false): {\r\n // width: number;\r\n // height: number;\r\n // } {\r\n // let width =\r\n // this.ui.gameCanvasContainer.clientWidth /\r\n // (Tile.size * this.ui.gameDisplay.stage.scale.x);\r\n // let height =\r\n // this.ui.gameCanvasContainer.clientHeight /\r\n // (Tile.size * this.ui.gameDisplay.stage.scale.x);\r\n // if (pad) {\r\n // // min of 5 padding\r\n // width += Math.max(5, width / 4);\r\n // height += Math.max(5, height / 4);\r\n // }\r\n // width = Math.ceil(width);\r\n // height = Math.ceil(height);\r\n // return { width, height };\r\n // }\r\n getViewportInTiles(pad = false) {\r\n let width = this.ui.gameCanvasContainer.clientWidth /\r\n (_tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size * this.ui.gameDisplay.stage.scale.x);\r\n let height = this.ui.gameCanvasContainer.clientHeight /\r\n (_tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size * this.ui.gameDisplay.stage.scale.x);\r\n if (pad) {\r\n // min of 5 padding\r\n width += Math.max(10, width);\r\n height += Math.max(10, height);\r\n }\r\n const center = this.getViewportCenterTile();\r\n width = Math.ceil(width);\r\n height = Math.ceil(height);\r\n return { width, height, center };\r\n }\r\n getViewportCenterTile() {\r\n const pivotXTile = this.game.userInterface.gameDisplay.stage.pivot.x / _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size;\r\n const pivotYTile = this.game.userInterface.gameDisplay.stage.pivot.y / _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size;\r\n let tilesOffsetX = pivotXTile * this.game.userInterface.gameDisplay.stage.scale.x;\r\n tilesOffsetX = Math.ceil(pivotXTile);\r\n const xPoint = tilesOffsetX;\r\n let tilesOffsetY = (pivotYTile / _tile__WEBPACK_IMPORTED_MODULE_1__.Tile.size) *\r\n this.game.userInterface.gameDisplay.stage.scale.y;\r\n tilesOffsetY = Math.ceil(pivotYTile);\r\n const yPoint = tilesOffsetY;\r\n return new _point__WEBPACK_IMPORTED_MODULE_0__.Point(xPoint, yPoint);\r\n }\r\n handleInput(event) {\r\n let validInput = false;\r\n let code = event.keyCode;\r\n if (code in this.keyMap) {\r\n let diff = rot_js__WEBPACK_IMPORTED_MODULE_2__.DIRS[8][this.keyMap[code]];\r\n if (this.moveCamera(this.ui.gameDisplay.stage, diff)) {\r\n validInput = true;\r\n }\r\n this.moveCamera(this.ui.gameDisplay.stage, diff);\r\n }\r\n else if (code === rot_js__WEBPACK_IMPORTED_MODULE_2__.KEYS.VK_HOME) {\r\n this.setViewportZoom(this.ui.gameDisplay.stage, this.defaultZoom);\r\n this.centerViewport(this.ui.gameDisplay.stage, this.ui.gameCanvasContainer.clientWidth, this.ui.gameCanvasContainer.clientHeight, this.viewport.width, this.viewport.height);\r\n validInput = true;\r\n }\r\n return validInput;\r\n }\r\n}\r\n\n\n//# sourceURL=webpack://rotjs-typescript-basics/./src/camera.ts?"); /***/ }), @@ -1082,7 +1082,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Game: () => (/* binding */ Game)\n/* harmony export */ });\n/* harmony import */ var rot_js_lib_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! rot-js/lib/index */ \"./node_modules/rot-js/lib/index.js\");\n/* harmony import */ var _entities_player__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./entities/player */ \"./src/entities/player.ts\");\n/* harmony import */ var _entities_shrub__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./entities/shrub */ \"./src/entities/shrub.ts\");\n/* harmony import */ var _entities_person__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./entities/person */ \"./src/entities/person.ts\");\n/* harmony import */ var _game_state__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./game-state */ \"./src/game-state.ts\");\n/* harmony import */ var _input_utility__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./input-utility */ \"./src/input-utility.ts\");\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _user_interface__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./user-interface */ \"./src/user-interface.ts\");\n/* harmony import */ var _entities_animal__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./entities/animal */ \"./src/entities/animal.ts\");\n/* harmony import */ var _renderer__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./renderer */ \"./src/renderer.ts\");\n/* harmony import */ var _map_world__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./map-world */ \"./src/map-world.ts\");\n/* harmony import */ var _time_manager__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./time-manager */ \"./src/time-manager.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n};\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nclass Game {\r\n constructor() {\r\n // RNG.setSeed(1234);\r\n this.entityCount = 5;\r\n this.treeCount = 20;\r\n // sensible default\r\n let width = 350;\r\n let height = 350;\r\n let fontSize = 20;\r\n // how/why should this change?\r\n fontSize = 20;\r\n this.gameSize = { width: width, height: height };\r\n this.mapSize = { width: this.gameSize.width, height: this.gameSize.height };\r\n this.timeManager = new _time_manager__WEBPACK_IMPORTED_MODULE_11__.TimeManager(this);\r\n this.userInterface = new _user_interface__WEBPACK_IMPORTED_MODULE_7__.UserInterface(this);\r\n this.gameState = new _game_state__WEBPACK_IMPORTED_MODULE_4__.GameState();\r\n this.map = new _map_world__WEBPACK_IMPORTED_MODULE_10__.MapWorld(this);\r\n this.renderer = new _renderer__WEBPACK_IMPORTED_MODULE_9__.Renderer(this);\r\n }\r\n Init() {\r\n return __awaiter(this, void 0, void 0, function* () {\r\n yield this.initializeGame();\r\n return true;\r\n });\r\n }\r\n start() {\r\n this.mainLoop();\r\n }\r\n mapIsPassable(x, y) {\r\n return this.map.isPassable(x, y);\r\n }\r\n occupiedByEntity(x, y) {\r\n for (let enemy of this.entities) {\r\n if (enemy.position.x == x && enemy.position.y == y) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n getPlayerPosition() {\r\n var _a;\r\n return (_a = this.player) === null || _a === void 0 ? void 0 : _a.position;\r\n }\r\n checkBox(x, y) {\r\n switch (this.map.getTileType(x, y)) {\r\n case _tile__WEBPACK_IMPORTED_MODULE_6__.Tile.shrub.type:\r\n this.map.setTile(x, y, _tile__WEBPACK_IMPORTED_MODULE_6__.Tile.cutTree);\r\n this.userInterface.statusLine.boxes += 1;\r\n if (this.treePoint.x == x && this.treePoint.y == y) {\r\n this.userInterface.messageLog.appendText(\"Continue with 'spacebar' or 'return'.\");\r\n this.userInterface.messageLog.appendText(\"Hooray! You found a pineapple.\");\r\n this.gameState.foundPineapple = true;\r\n }\r\n else {\r\n this.userInterface.messageLog.appendText(\"This box is empty.\");\r\n }\r\n break;\r\n case _tile__WEBPACK_IMPORTED_MODULE_6__.Tile.cutTree.type:\r\n this.map.setTile(x, y, _tile__WEBPACK_IMPORTED_MODULE_6__.Tile.treeStump);\r\n this.userInterface.messageLog.appendText(\"You destroy this box!\");\r\n break;\r\n case _tile__WEBPACK_IMPORTED_MODULE_6__.Tile.treeStump.type:\r\n this.userInterface.messageLog.appendText(\"This box is already destroyed.\");\r\n break;\r\n default:\r\n this.userInterface.messageLog.appendText(\"There is no box here!\");\r\n break;\r\n }\r\n }\r\n destroyBox(actor, x, y) {\r\n switch (this.map.getTileType(x, y)) {\r\n case 5 /* Plant */:\r\n case 7 /* CutTree */:\r\n this.map.setTile(x, y, _tile__WEBPACK_IMPORTED_MODULE_6__.Tile.treeStump);\r\n if (this.treePoint.x == x && this.treePoint.y == y) {\r\n this.userInterface.messageLog.appendText(\"Continue with 'spacebar' or 'return'.\");\r\n this.userInterface.messageLog.appendText(`Game over - ${this.getActorName(actor)} detroyed the box with the pineapple.`);\r\n this.gameState.pineappleWasDestroyed = true;\r\n }\r\n else {\r\n this.userInterface.messageLog.appendText(`${this.getActorName(actor)} detroyed a box.`);\r\n }\r\n break;\r\n case 8 /* TreeStump */:\r\n this.userInterface.messageLog.appendText(\"This box is already destroyed.\");\r\n break;\r\n default:\r\n this.userInterface.messageLog.appendText(\"There is no box here!\");\r\n break;\r\n }\r\n }\r\n catchPlayer(actor) {\r\n this.userInterface.messageLog.appendText(\"Continue with 'spacebar' or 'return'.\");\r\n this.userInterface.messageLog.appendText(`Game over - you were captured by ${this.getActorName(actor)}!`);\r\n this.gameState.playerWasCaught = true;\r\n }\r\n getTileType(x, y) {\r\n return this.map.getTileType(x, y);\r\n }\r\n getRandomTilePositions(type, quantity = 1) {\r\n return this.map.getRandomTilePositions(type, quantity);\r\n }\r\n getRandomPlantPositions(type, quantity = 1) {\r\n let buffer = [];\r\n let result = [];\r\n for (let key in this.plants) {\r\n if (this.plants[key].type === type) {\r\n buffer.push(this.plants[key].position);\r\n }\r\n }\r\n let index;\r\n while (buffer.length > 0 && result.length < quantity) {\r\n index = Math.floor(rot_js_lib_index__WEBPACK_IMPORTED_MODULE_0__.RNG.getUniform() * buffer.length);\r\n result.push(buffer.splice(index, 1)[0]);\r\n }\r\n return result;\r\n }\r\n getRandomEntityPositions(type, quantity = 1) {\r\n let buffer = [];\r\n let result = [];\r\n for (let key in this.entities) {\r\n if (this.entities[key].type === type) {\r\n buffer.push(this.entities[key].position);\r\n }\r\n }\r\n let index;\r\n while (buffer.length > 0 && result.length < quantity) {\r\n index = Math.floor(rot_js_lib_index__WEBPACK_IMPORTED_MODULE_0__.RNG.getUniform() * buffer.length);\r\n result.push(buffer.splice(index, 1)[0]);\r\n }\r\n return result;\r\n }\r\n initializeGame() {\r\n return __awaiter(this, void 0, void 0, function* () {\r\n yield this.userInterface.init();\r\n this.gameState.reset();\r\n this.map.generateMap(this.mapSize.width, this.mapSize.height);\r\n this.generatePlants();\r\n this.generateBeings();\r\n for (let entity of this.entities) {\r\n this.timeManager.addToSchedule(entity, true);\r\n }\r\n this.userInterface.refreshPanel();\r\n return true;\r\n });\r\n }\r\n mainLoop() {\r\n return __awaiter(this, void 0, void 0, function* () {\r\n let actor;\r\n while (true) {\r\n yield Game.delay(this.timeManager.turnDelayInMs);\r\n if (!this.timeManager.isPaused) {\r\n actor = this.timeManager.nextOnSchedule();\r\n if (actor) {\r\n actor.plan();\r\n this.timeManager.setDuration(actor.action.durationInTurns);\r\n yield actor.act();\r\n }\r\n // refresh all screens in case actor affected them\r\n this.userInterface.refreshPanel();\r\n }\r\n if (this.gameState.isGameOver()) {\r\n yield _input_utility__WEBPACK_IMPORTED_MODULE_5__.InputUtility.waitForInput(this.userInterface.HandleInputConfirm.bind(this));\r\n yield this.initializeGame();\r\n }\r\n }\r\n });\r\n }\r\n // private async mainLoop(): Promise {\r\n // let actor: Actor;\r\n // console.log(\"main loop\");\r\n // while (true) {\r\n // await this.userInterface.camera.Act();\r\n // actor = this.scheduler.next();\r\n // // actor = null;\r\n // // if (!actor) {\r\n // // await this.userInterface.camera.Act();\r\n // // }\r\n // if (actor) {\r\n // const actionDuration = Math.ceil(RNG.getUniform() * 20);\r\n // this.scheduler.setDuration(actionDuration);\r\n // await actor.act();\r\n // if (actor.type === TileType.Player) {\r\n // this.userInterface.statusLine.turns += 1;\r\n // }\r\n // if (this.gameState.foundPineapple) {\r\n // this.userInterface.statusLine.pineapples += 1;\r\n // }\r\n // }\r\n // // this.drawUserInterface();\r\n // this.userInterface.refreshPanel();\r\n // // this.map.UpdateFOV(this.player);\r\n // if (this.gameState.isGameOver()) {\r\n // await InputUtility.waitForInput(\r\n // this.userInterface.HandleInputConfirm.bind(this)\r\n // );\r\n // await this.initializeGame();\r\n // }\r\n // }\r\n // }\r\n getActorName(actor) {\r\n switch (actor.type) {\r\n case 6 /* Player */:\r\n return `Player`;\r\n case 4 /* Entity */:\r\n return `%c{${actor.tile.color}}Entity%c{}`;\r\n case 5 /* Plant */:\r\n return `%c{${actor.tile.color}}Plant%c{}`;\r\n default:\r\n return \"unknown actor\";\r\n }\r\n }\r\n generatePlants() {\r\n this.plants = [];\r\n let positions = this.map.getRandomTilePositions(_tile__WEBPACK_IMPORTED_MODULE_6__.Tile.Biomes.grassland.biome, this.treeCount);\r\n for (let position of positions) {\r\n this.plants.push(new _entities_shrub__WEBPACK_IMPORTED_MODULE_2__.Shrub(this, position));\r\n }\r\n this.treePoint = positions[0];\r\n }\r\n generatePlayer() {\r\n const pos = this.map.getRandomTilePositions(_tile__WEBPACK_IMPORTED_MODULE_6__.Tile.Biomes.grassland.biome, 1)[0];\r\n this.player = new _entities_player__WEBPACK_IMPORTED_MODULE_1__.Player(this, pos);\r\n }\r\n generateBeings() {\r\n this.entities = [];\r\n let positions = this.map.getRandomTilePositions(_tile__WEBPACK_IMPORTED_MODULE_6__.Tile.Biomes.grassland.biome, this.entityCount);\r\n // this.player = new Player(this, positions.splice(0, 1)[0]);\r\n for (let position of positions) {\r\n if (rot_js_lib_index__WEBPACK_IMPORTED_MODULE_0__.RNG.getUniform() < 0.5) {\r\n this.entities.push(new _entities_person__WEBPACK_IMPORTED_MODULE_3__.Person(this, position));\r\n }\r\n else {\r\n this.entities.push(new _entities_animal__WEBPACK_IMPORTED_MODULE_8__.Animal(this, position));\r\n }\r\n }\r\n }\r\n static delay(delayInMs) {\r\n return new Promise((resolve) => setTimeout(() => {\r\n return resolve(true);\r\n }, delayInMs));\r\n }\r\n}\r\n\n\n//# sourceURL=webpack://rotjs-typescript-basics/./src/game.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Game: () => (/* binding */ Game)\n/* harmony export */ });\n/* harmony import */ var rot_js_lib_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! rot-js/lib/index */ \"./node_modules/rot-js/lib/index.js\");\n/* harmony import */ var _entities_player__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./entities/player */ \"./src/entities/player.ts\");\n/* harmony import */ var _entities_shrub__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./entities/shrub */ \"./src/entities/shrub.ts\");\n/* harmony import */ var _entities_person__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./entities/person */ \"./src/entities/person.ts\");\n/* harmony import */ var _game_state__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./game-state */ \"./src/game-state.ts\");\n/* harmony import */ var _input_utility__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./input-utility */ \"./src/input-utility.ts\");\n/* harmony import */ var _tile__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./tile */ \"./src/tile.ts\");\n/* harmony import */ var _user_interface__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./user-interface */ \"./src/user-interface.ts\");\n/* harmony import */ var _entities_animal__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./entities/animal */ \"./src/entities/animal.ts\");\n/* harmony import */ var _renderer__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./renderer */ \"./src/renderer.ts\");\n/* harmony import */ var _map_world__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./map-world */ \"./src/map-world.ts\");\n/* harmony import */ var _time_manager__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./time-manager */ \"./src/time-manager.ts\");\nvar __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n};\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nclass Game {\r\n constructor() {\r\n // RNG.setSeed(1234);\r\n this.entityCount = 5;\r\n this.treeCount = 20;\r\n this.msPerFrame = 1000 / 60; // desired interval is 60fps\r\n this.msPerLoop = 1000 / 20; // desired interval is 1000 ms / runs per second\r\n // sensible default\r\n // let width = 350;\r\n // let height = 350;\r\n let width = 350;\r\n let height = 350;\r\n let fontSize = 20;\r\n // how/why should this change?\r\n fontSize = 20;\r\n this.gameSize = { width: width, height: height };\r\n this.mapSize = { width: this.gameSize.width, height: this.gameSize.height };\r\n this.timeManager = new _time_manager__WEBPACK_IMPORTED_MODULE_11__.TimeManager(this);\r\n this.userInterface = new _user_interface__WEBPACK_IMPORTED_MODULE_7__.UserInterface(this);\r\n this.gameState = new _game_state__WEBPACK_IMPORTED_MODULE_4__.GameState();\r\n this.map = new _map_world__WEBPACK_IMPORTED_MODULE_10__.MapWorld(this);\r\n this.renderer = new _renderer__WEBPACK_IMPORTED_MODULE_9__.Renderer(this);\r\n }\r\n Init() {\r\n return __awaiter(this, void 0, void 0, function* () {\r\n yield this.initializeGame();\r\n return true;\r\n });\r\n }\r\n start() {\r\n requestAnimationFrame(this.gameLoop.bind(this));\r\n requestAnimationFrame(this.renderLoop.bind(this));\r\n }\r\n mapIsPassable(x, y) {\r\n return this.map.isPassable(x, y);\r\n }\r\n occupiedByEntity(x, y) {\r\n for (let enemy of this.entities) {\r\n if (enemy.position.x == x && enemy.position.y == y) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n getPlayerPosition() {\r\n var _a;\r\n return (_a = this.player) === null || _a === void 0 ? void 0 : _a.position;\r\n }\r\n checkBox(x, y) {\r\n switch (this.map.getTileType(x, y)) {\r\n case _tile__WEBPACK_IMPORTED_MODULE_6__.Tile.shrub.type:\r\n this.map.setTile(x, y, _tile__WEBPACK_IMPORTED_MODULE_6__.Tile.cutTree);\r\n this.userInterface.statusLine.boxes += 1;\r\n if (this.treePoint.x == x && this.treePoint.y == y) {\r\n this.userInterface.messageLog.appendText(\"Continue with 'spacebar' or 'return'.\");\r\n this.userInterface.messageLog.appendText(\"Hooray! You found a pineapple.\");\r\n this.gameState.foundPineapple = true;\r\n }\r\n else {\r\n this.userInterface.messageLog.appendText(\"This box is empty.\");\r\n }\r\n break;\r\n case _tile__WEBPACK_IMPORTED_MODULE_6__.Tile.cutTree.type:\r\n this.map.setTile(x, y, _tile__WEBPACK_IMPORTED_MODULE_6__.Tile.treeStump);\r\n this.userInterface.messageLog.appendText(\"You destroy this box!\");\r\n break;\r\n case _tile__WEBPACK_IMPORTED_MODULE_6__.Tile.treeStump.type:\r\n this.userInterface.messageLog.appendText(\"This box is already destroyed.\");\r\n break;\r\n default:\r\n this.userInterface.messageLog.appendText(\"There is no box here!\");\r\n break;\r\n }\r\n }\r\n destroyBox(actor, x, y) {\r\n switch (this.map.getTileType(x, y)) {\r\n case 5 /* Plant */:\r\n case 7 /* CutTree */:\r\n this.map.setTile(x, y, _tile__WEBPACK_IMPORTED_MODULE_6__.Tile.treeStump);\r\n if (this.treePoint.x == x && this.treePoint.y == y) {\r\n this.userInterface.messageLog.appendText(\"Continue with 'spacebar' or 'return'.\");\r\n this.userInterface.messageLog.appendText(`Game over - ${this.getActorName(actor)} detroyed the box with the pineapple.`);\r\n this.gameState.pineappleWasDestroyed = true;\r\n }\r\n else {\r\n this.userInterface.messageLog.appendText(`${this.getActorName(actor)} detroyed a box.`);\r\n }\r\n break;\r\n case 8 /* TreeStump */:\r\n this.userInterface.messageLog.appendText(\"This box is already destroyed.\");\r\n break;\r\n default:\r\n this.userInterface.messageLog.appendText(\"There is no box here!\");\r\n break;\r\n }\r\n }\r\n catchPlayer(actor) {\r\n this.userInterface.messageLog.appendText(\"Continue with 'spacebar' or 'return'.\");\r\n this.userInterface.messageLog.appendText(`Game over - you were captured by ${this.getActorName(actor)}!`);\r\n this.gameState.playerWasCaught = true;\r\n }\r\n getTileType(x, y) {\r\n return this.map.getTileType(x, y);\r\n }\r\n getRandomTilePositions(type, quantity = 1) {\r\n return this.map.getRandomTilePositions(type, quantity);\r\n }\r\n getRandomPlantPositions(type, quantity = 1) {\r\n let buffer = [];\r\n let result = [];\r\n for (let key in this.plants) {\r\n if (this.plants[key].type === type) {\r\n buffer.push(this.plants[key].position);\r\n }\r\n }\r\n let index;\r\n while (buffer.length > 0 && result.length < quantity) {\r\n index = Math.floor(rot_js_lib_index__WEBPACK_IMPORTED_MODULE_0__.RNG.getUniform() * buffer.length);\r\n result.push(buffer.splice(index, 1)[0]);\r\n }\r\n return result;\r\n }\r\n getRandomEntityPositions(type, quantity = 1) {\r\n let buffer = [];\r\n let result = [];\r\n for (let key in this.entities) {\r\n if (this.entities[key].type === type) {\r\n buffer.push(this.entities[key].position);\r\n }\r\n }\r\n let index;\r\n while (buffer.length > 0 && result.length < quantity) {\r\n index = Math.floor(rot_js_lib_index__WEBPACK_IMPORTED_MODULE_0__.RNG.getUniform() * buffer.length);\r\n result.push(buffer.splice(index, 1)[0]);\r\n }\r\n return result;\r\n }\r\n initializeGame() {\r\n return __awaiter(this, void 0, void 0, function* () {\r\n yield this.userInterface.init();\r\n this.gameState.reset();\r\n this.map.generateMap(this.mapSize.width, this.mapSize.height);\r\n this.generatePlants();\r\n this.generateBeings();\r\n for (let entity of this.entities) {\r\n this.timeManager.addToSchedule(entity, true);\r\n }\r\n this.userInterface.refreshPanel();\r\n return true;\r\n });\r\n }\r\n gameLoop(now) {\r\n return __awaiter(this, void 0, void 0, function* () {\r\n requestAnimationFrame(this.gameLoop.bind(this));\r\n if (!this.lastGameLoopTime) {\r\n this.lastGameLoopTime = now;\r\n }\r\n const elapsed = now - this.lastGameLoopTime;\r\n if (elapsed > this.msPerLoop / this.timeManager.timeScale) {\r\n let actor;\r\n if (!this.timeManager.isPaused) {\r\n actor = this.timeManager.nextOnSchedule();\r\n if (actor) {\r\n actor.plan();\r\n this.timeManager.setDuration(actor.action.durationInTurns);\r\n yield actor.act();\r\n }\r\n }\r\n if (this.gameState.isGameOver()) {\r\n yield _input_utility__WEBPACK_IMPORTED_MODULE_5__.InputUtility.waitForInput(this.userInterface.HandleInputConfirm.bind(this));\r\n yield this.initializeGame();\r\n }\r\n this.lastGameLoopTime = now;\r\n }\r\n });\r\n }\r\n renderLoop(now) {\r\n requestAnimationFrame(this.renderLoop.bind(this));\r\n if (!this.lastRenderTime) {\r\n this.lastRenderTime = now;\r\n }\r\n const elapsed = now - this.lastRenderTime;\r\n if (elapsed > this.msPerFrame) {\r\n this.userInterface.refreshPanel();\r\n this.lastRenderTime = now;\r\n }\r\n }\r\n getActorName(actor) {\r\n switch (actor.type) {\r\n case 6 /* Player */:\r\n return `Player`;\r\n case 4 /* Entity */:\r\n return `%c{${actor.tile.color}}Entity%c{}`;\r\n case 5 /* Plant */:\r\n return `%c{${actor.tile.color}}Plant%c{}`;\r\n default:\r\n return \"unknown actor\";\r\n }\r\n }\r\n generatePlants() {\r\n this.plants = [];\r\n let positions = this.map.getRandomTilePositions(_tile__WEBPACK_IMPORTED_MODULE_6__.Tile.Biomes.grassland.biome, this.treeCount);\r\n for (let position of positions) {\r\n this.plants.push(new _entities_shrub__WEBPACK_IMPORTED_MODULE_2__.Shrub(this, position));\r\n }\r\n this.treePoint = positions[0];\r\n }\r\n generatePlayer() {\r\n const pos = this.map.getRandomTilePositions(_tile__WEBPACK_IMPORTED_MODULE_6__.Tile.Biomes.grassland.biome, 1)[0];\r\n this.player = new _entities_player__WEBPACK_IMPORTED_MODULE_1__.Player(this, pos);\r\n }\r\n generateBeings() {\r\n this.entities = [];\r\n let positions = this.map.getRandomTilePositions(_tile__WEBPACK_IMPORTED_MODULE_6__.Tile.Biomes.grassland.biome, this.entityCount);\r\n // this.player = new Player(this, positions.splice(0, 1)[0]);\r\n for (let position of positions) {\r\n if (rot_js_lib_index__WEBPACK_IMPORTED_MODULE_0__.RNG.getUniform() < 0.5) {\r\n this.entities.push(new _entities_person__WEBPACK_IMPORTED_MODULE_3__.Person(this, position));\r\n }\r\n else {\r\n this.entities.push(new _entities_animal__WEBPACK_IMPORTED_MODULE_8__.Animal(this, position));\r\n }\r\n }\r\n }\r\n static delay(delayInMs) {\r\n return new Promise((resolve) => setTimeout(() => {\r\n return resolve(true);\r\n }, delayInMs));\r\n }\r\n}\r\n\n\n//# sourceURL=webpack://rotjs-typescript-basics/./src/game.ts?"); /***/ }), @@ -1181,7 +1181,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { "use strict"; -eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ TimeManager: () => (/* binding */ TimeManager)\n/* harmony export */ });\n/* harmony import */ var rot_js_lib_scheduler_action__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! rot-js/lib/scheduler/action */ \"./node_modules/rot-js/lib/scheduler/action.js\");\n\r\nclass TimeManager {\r\n constructor(game) {\r\n this.game = game;\r\n this.scheduler = new rot_js_lib_scheduler_action__WEBPACK_IMPORTED_MODULE_0__[\"default\"]();\r\n this.isPaused = false;\r\n this.defaultTurnDelayInMs = 100;\r\n this.turnDelayInMs = this.defaultTurnDelayInMs;\r\n this.maxTimeScale = 10;\r\n this.dayLength = 50;\r\n this.nightLength = 20;\r\n this.daysPerYear = 4;\r\n this.transitionTime = 10;\r\n this.timeScale = 1;\r\n this.currentYear = 1;\r\n this.currentDay = 1;\r\n this.currentTime = 0;\r\n this.currentTurn = 0;\r\n this.isDayTime = true;\r\n this.isNighttime = !this.isDayTime;\r\n }\r\n addToSchedule(actor, repeat, initialTimeDelay) {\r\n return this.scheduler.add(actor, repeat, initialTimeDelay);\r\n }\r\n nextOnSchedule() {\r\n this.currentTurn = this.scheduler.getTime();\r\n this.calculateCurrentTime();\r\n return this.scheduler.next();\r\n }\r\n calculateCurrentTime() {\r\n this.currentYear =\r\n Math.floor(this.currentTurn / this.dayLength / this.daysPerYear) + 1;\r\n this.currentDay =\r\n (Math.floor(this.currentTurn / this.dayLength) % this.daysPerYear) + 1;\r\n this.currentTime = this.currentTurn % this.dayLength;\r\n }\r\n getCurrentTimeForDisplay() {\r\n return `Year: ${this.currentYear} - Day: ${this.currentDay} - Hour: ${this.currentTime}`;\r\n }\r\n setDuration(time) {\r\n return this.scheduler.setDuration(time);\r\n }\r\n togglePause() {\r\n this.isPaused = !this.isPaused;\r\n console.log(\"isPaused: \", this.isPaused);\r\n }\r\n setTimescale(scale) {\r\n this.timeScale = scale;\r\n if (this.timeScale > this.maxTimeScale) {\r\n this.timeScale = this.maxTimeScale;\r\n }\r\n if (this.timeScale <= 0) {\r\n this.timeScale = 0;\r\n this.isPaused = true;\r\n }\r\n this.turnDelayInMs = this.defaultTurnDelayInMs / this.timeScale;\r\n console.log(\"turnDelayInMs: \", this.turnDelayInMs);\r\n }\r\n}\r\n\n\n//# sourceURL=webpack://rotjs-typescript-basics/./src/time-manager.ts?"); +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ TimeManager: () => (/* binding */ TimeManager)\n/* harmony export */ });\n/* harmony import */ var rot_js_lib_scheduler_action__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! rot-js/lib/scheduler/action */ \"./node_modules/rot-js/lib/scheduler/action.js\");\n\r\nclass TimeManager {\r\n constructor(game) {\r\n this.game = game;\r\n this.scheduler = new rot_js_lib_scheduler_action__WEBPACK_IMPORTED_MODULE_0__[\"default\"]();\r\n this.isPaused = false;\r\n this.maxTimeScale = 10;\r\n this.dayLength = 50;\r\n this.nightLength = 20;\r\n this.daysPerYear = 4;\r\n this.transitionTime = 10;\r\n this.timeScale = 1;\r\n this.currentYear = 1;\r\n this.currentDay = 1;\r\n this.currentTime = 0;\r\n this.currentTurn = 0;\r\n this.isDayTime = true;\r\n this.isNighttime = !this.isDayTime;\r\n }\r\n addToSchedule(actor, repeat, initialTimeDelay) {\r\n return this.scheduler.add(actor, repeat, initialTimeDelay);\r\n }\r\n nextOnSchedule() {\r\n this.currentTurn = this.scheduler.getTime();\r\n this.calculateCurrentTime();\r\n return this.scheduler.next();\r\n }\r\n calculateCurrentTime() {\r\n this.currentYear =\r\n Math.floor(this.currentTurn / this.dayLength / this.daysPerYear) + 1;\r\n this.currentDay =\r\n (Math.floor(this.currentTurn / this.dayLength) % this.daysPerYear) + 1;\r\n this.currentTime = this.currentTurn % this.dayLength;\r\n }\r\n getCurrentTimeForDisplay() {\r\n return `Year: ${this.currentYear} - Day: ${this.currentDay} - Hour: ${this.currentTime}`;\r\n }\r\n setDuration(time) {\r\n return this.scheduler.setDuration(time);\r\n }\r\n togglePause() {\r\n this.isPaused = !this.isPaused;\r\n console.log(\"isPaused: \", this.isPaused);\r\n }\r\n setTimescale(scale) {\r\n this.timeScale = scale;\r\n if (this.timeScale > this.maxTimeScale) {\r\n this.timeScale = this.maxTimeScale;\r\n }\r\n if (this.timeScale <= 0) {\r\n this.timeScale = 0;\r\n this.isPaused = true;\r\n }\r\n else {\r\n this.isPaused = false;\r\n }\r\n }\r\n}\r\n\n\n//# sourceURL=webpack://rotjs-typescript-basics/./src/time-manager.ts?"); /***/ }), diff --git a/src/camera.ts b/src/camera.ts index d3e056e..aa8bcac 100644 --- a/src/camera.ts +++ b/src/camera.ts @@ -245,8 +245,8 @@ export class Camera { private handlePanEnd = (g: TinyGesture) => { this.isPanning = false; let momentumModifier = 0.8; - const momentumDuration = 300; // in milliseconds - const momentumInterval = 16; // in milliseconds + const momentumDuration = 250; // in milliseconds + const momentumInterval = 1000 / 60; // in milliseconds const momentumX = (g.velocityX * momentumModifier) / this.ui.gameDisplay.stage.scale.x; const momentumY = diff --git a/src/game.ts b/src/game.ts index ead3656..ad07fb2 100644 --- a/src/game.ts +++ b/src/game.ts @@ -33,10 +33,18 @@ export class Game { private treePoint: Point; + private lastRenderTime: number; + private msPerFrame: number = 1000 / 60; // desired interval is 60fps + + private lastGameLoopTime: number; + private msPerLoop: number = 1000 / 20; // desired interval is 1000 ms / runs per second + constructor() { // RNG.setSeed(1234); // sensible default + // let width = 350; + // let height = 350; let width = 350; let height = 350; let fontSize = 20; @@ -60,7 +68,8 @@ export class Game { } public start() { - this.mainLoop(); + requestAnimationFrame(this.gameLoop.bind(this)); + requestAnimationFrame(this.renderLoop.bind(this)); } mapIsPassable(x: number, y: number): boolean { @@ -213,11 +222,16 @@ export class Game { return true; } - private async mainLoop(): Promise { - let actor: Actor; - while (true) { - await Game.delay(this.timeManager.turnDelayInMs); + private async gameLoop(now: number) { + requestAnimationFrame(this.gameLoop.bind(this)); + if (!this.lastGameLoopTime) { + this.lastGameLoopTime = now; + } + const elapsed = now - this.lastGameLoopTime; + + if (elapsed > this.msPerLoop / this.timeManager.timeScale) { + let actor: Actor; if (!this.timeManager.isPaused) { actor = this.timeManager.nextOnSchedule(); @@ -226,8 +240,6 @@ export class Game { this.timeManager.setDuration(actor.action.durationInTurns); await actor.act(); } - // refresh all screens in case actor affected them - this.userInterface.refreshPanel(); } if (this.gameState.isGameOver()) { @@ -236,46 +248,23 @@ export class Game { ); await this.initializeGame(); } + this.lastGameLoopTime = now; } } - // private async mainLoop(): Promise { - // let actor: Actor; - // console.log("main loop"); - // while (true) { - // await this.userInterface.camera.Act(); - - // actor = this.scheduler.next(); - // // actor = null; - // // if (!actor) { - // // await this.userInterface.camera.Act(); - // // } - - // if (actor) { - // const actionDuration = Math.ceil(RNG.getUniform() * 20); - // this.scheduler.setDuration(actionDuration); - // await actor.act(); - // if (actor.type === TileType.Player) { - // this.userInterface.statusLine.turns += 1; - // } - // if (this.gameState.foundPineapple) { - // this.userInterface.statusLine.pineapples += 1; - // } - // } - - // // this.drawUserInterface(); - // this.userInterface.refreshPanel(); - - // // this.map.UpdateFOV(this.player); - - // if (this.gameState.isGameOver()) { - // await InputUtility.waitForInput( - // this.userInterface.HandleInputConfirm.bind(this) - // ); - // await this.initializeGame(); - // } - // } - // } + private renderLoop(now: number) { + requestAnimationFrame(this.renderLoop.bind(this)); + + if (!this.lastRenderTime) { + this.lastRenderTime = now; + } + const elapsed = now - this.lastRenderTime; + + if (elapsed > this.msPerFrame) { + this.userInterface.refreshPanel(); + this.lastRenderTime = now; + } + } private getActorName(actor: Actor): string { switch (actor.type) { diff --git a/src/time-manager.ts b/src/time-manager.ts index 9d1b19a..1129617 100644 --- a/src/time-manager.ts +++ b/src/time-manager.ts @@ -29,13 +29,9 @@ export class TimeManager { public currentTime: number; public currentTurn: number; - private defaultTurnDelayInMs: number; - constructor(private game: Game) { this.scheduler = new Action(); this.isPaused = false; - this.defaultTurnDelayInMs = 100; - this.turnDelayInMs = this.defaultTurnDelayInMs; this.maxTimeScale = 10; this.dayLength = 50; @@ -95,8 +91,8 @@ export class TimeManager { if (this.timeScale <= 0) { this.timeScale = 0; this.isPaused = true; + } else { + this.isPaused = false; } - this.turnDelayInMs = this.defaultTurnDelayInMs / this.timeScale; - console.log("turnDelayInMs: ", this.turnDelayInMs); } }