A warioware-like browser game; Finals project

✨ lottss of stuff, testing and including. Revamp of loop system and much more

Signed-off-by: Xaiya Schumin <d.schumin@proton.me>

+147 -84
+8
package-lock.json
··· 12 12 "express": "^5.2.1" 13 13 }, 14 14 "devDependencies": { 15 + "@types/ejs": "^3.1.5", 15 16 "@types/express": "^5.0.6", 16 17 "@types/node": "^20.1.2", 17 18 "typescript": "^5.0.4", ··· 38 39 "dependencies": { 39 40 "@types/node": "*" 40 41 } 42 + }, 43 + "node_modules/@types/ejs": { 44 + "version": "3.1.5", 45 + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", 46 + "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", 47 + "dev": true, 48 + "license": "MIT" 41 49 }, 42 50 "node_modules/@types/express": { 43 51 "version": "5.0.6",
+1
package.json
··· 8 8 }, 9 9 "author": "xaiyadev", 10 10 "devDependencies": { 11 + "@types/ejs": "^3.1.5", 11 12 "@types/express": "^5.0.6", 12 13 "@types/node": "^20.1.2", 13 14 "typescript": "^5.0.4",
+2 -2
src/frontend/public/components/overlay/indicator/indicator.ts
··· 1 - import {ComponentInterface} from "../../component"; 1 + import { ComponentInterface } from "../../component.js"; 2 2 import { controls } from "../../../controller/miniGame/miniGame.js"; 3 3 4 4 export default class IndicatorComponent implements ComponentInterface { ··· 11 11 this.element = element; 12 12 } 13 13 14 - switch(show: boolean, control?: controls) { 14 + switch(show: boolean, control?: controls | null) { 15 15 if (this.element) { 16 16 if (control) 17 17 this.element.children[0].innerHTML = control.toString(); /* TODO: change into game-state */
+43 -34
src/frontend/public/controller/gameState.ts
··· 1 1 import Overlay from "../components/overlay/overlay.js"; 2 - import CountdownComponent from "../components/overlay/countdown/countdown.js"; 3 - import MiniGame from "./miniGame/miniGame.js"; 2 + import MiniGameController from "./miniGame/miniGameController.js"; 3 + import {miniGameInterface} from "./miniGame/miniGame"; 4 4 5 5 export enum loopStates { 6 6 IN_GAME, ··· 17 17 private gameTime: number 18 18 19 19 private overlay: Overlay 20 - private miniGame: MiniGame 20 + private miniGameController: MiniGameController 21 21 22 22 constructor() { 23 - this.speed = 1 23 + this.speed = 0.5 24 24 this.loopState = loopStates.RESET 25 25 26 26 this.gameLoop = null 27 - this.gameTime = 100; // the game-time will always go from 100 -> 0 27 + this.gameTime = 100; // the game-time will always go from 100 -> 0 TODO: gametime is depend on the framreate of the screen 28 28 29 29 this.overlay = new Overlay(); 30 - this.miniGame = new MiniGame(); 30 + this.miniGameController = new MiniGameController(); 31 31 32 32 this.init() 33 33 } ··· 35 35 init() { 36 36 this.pauseGameEvent(); 37 37 38 - this.miniGame.switchLoadedRandom(); 38 + this.miniGameController.switchLoadedRandom(); 39 39 this.loop(); 40 40 } 41 41 42 42 loop(): void { 43 + // check and define available micro-game 44 + const miniGame: miniGameInterface | null = this.miniGameController.loaded; 45 + if (!miniGame) throw new Error("No micro-game loaded !!") 43 46 44 - if (this.gameTime >= 0) { 45 - this.loopState = loopStates.IN_GAME 46 - this.overlay.countdown.updateTime(this.gameTime) 47 + // Game loop 48 + if (this.gameTime >= 0) this.run(miniGame) 49 + else if (this.gameTime >= -50) this.switch(miniGame); 50 + else this.reset(miniGame) 47 51 48 - if (this.miniGame.loaded) this.miniGame.loaded.run(); 49 - else console.error("No mini-game currently loaded") 50 52 53 + // game timer and speed 54 + this.gameTime -= this.speed; 55 + this.gameLoop = requestAnimationFrame(this.loop.bind(this)) 51 56 52 - this.gameTime -= this.speed; 53 - } else { 57 + } 58 + 59 + run(miniGame: miniGameInterface) { 60 + if (this.loopState != loopStates.IN_GAME) { 61 + this.loopState = loopStates.IN_GAME 62 + miniGame.render(); 63 + } 54 64 55 - // should only switch the game once, after entering switch mode 56 - if (this.loopState !== loopStates.SWITCH) 57 - this.miniGame.switchLoadedRandom(); 65 + this.overlay.countdown.updateTime(this.gameTime) 66 + miniGame.loop(); 67 + } 58 68 59 - if (this.gameTime >= -50) { 60 - this.loopState = loopStates.SWITCH 69 + switch(miniGame: miniGameInterface) { 70 + // Stuff that happens the first time we enter the switch-state 71 + if (this.loopState !== loopStates.SWITCH) { 72 + this.loopState = loopStates.SWITCH 61 73 62 - this.miniGame.loaded?.reset(); 63 - this.overlay.indicator.switch(true, this.miniGame.loaded?.control) // TODO: when pausing while having the indicator the indicator will despawn and pause at the new start of new game 74 + this.miniGameController.switchLoadedRandom(); 75 + } 64 76 65 - this.gameTime -= this.speed; 66 - } else { 67 - this.loopState = loopStates.RESET 77 + // all the other fluff 78 + this.overlay.indicator.switch(true, miniGame.control) // TODO: when pausing while having the indicator the indicator will despawn and pause at the new start of new game 79 + } 68 80 69 - this.overlay.indicator.switch(false) 70 - this.reset() 71 - } 81 + reset(miniGame: miniGameInterface) { 82 + if (this.loopState != loopStates.RESET) { 83 + this.loopState = loopStates.RESET; 72 84 85 + this.overlay.indicator.switch(false) 86 + this.gameTime = 100; 87 + miniGame.render(); 73 88 } 74 - 75 - this.gameLoop = requestAnimationFrame(this.loop.bind(this)) 76 89 } 90 + 77 91 78 92 pause() { 79 93 if (this.gameLoop) { ··· 83 97 console.error("No Game is running!") 84 98 } 85 99 } 86 - 87 - reset() { 88 - this.gameTime = 100; 89 - } 90 - 91 100 92 101 private pauseGameEvent() { 93 102 document.addEventListener("keydown", (e: KeyboardEvent) => {
+16
src/frontend/public/controller/miniGame/maze/maze.ts
··· 1 + import {BaseMiniGame, controls} from "../miniGame.js"; 2 + 3 + export default class Maze extends BaseMiniGame { 4 + constructor() { 5 + super(controls.keyboard); 6 + } 7 + 8 + loop() { 9 + 10 + } 11 + 12 + finish() { 13 + 14 + } 15 + 16 + }
src/frontend/public/controller/miniGame/maze/template.ejs

This is a binary file and will not be displayed.

+32 -32
src/frontend/public/controller/miniGame/miniGame.ts
··· 1 - import Smash from "./smash.js"; 1 + import ejs from "ejs"; 2 2 3 3 export enum controls { 4 4 "mouse" = 0, ··· 7 7 } 8 8 9 9 export interface miniGameInterface { 10 - control: controls, 11 - run(): void, 10 + control: controls | null, 11 + score: number, 12 + 13 + loop(): void, 14 + finish(): void, 15 + 16 + render(): void, 17 + unRender(): void, 18 + 12 19 reset(): void, // TODO 13 20 } 14 21 15 - export default class MiniGame { 16 - games: miniGameInterface[] 17 - loaded: miniGameInterface | null 22 + export class BaseMiniGame implements miniGameInterface { 23 + control: controls | null; 24 + score: number; 18 25 19 26 constructor( 20 - games = [ 21 - new Smash(), 22 - new testMiniGame(), 23 - ], 27 + control: controls | null = null, 28 + score: number = 0, 24 29 ) { 25 - this.games = games; 26 - this.loaded = null; 30 + this.control = control; 31 + this.score = score; 27 32 } 28 33 29 - switchLoadedRandom(): miniGameInterface { 30 - const selected: miniGameInterface = this.games[Math.floor(Math.random() * this.games.length)]; 31 - 32 - this.loaded = selected; 33 - return selected; 34 + loop(): void { 35 + throw new Error("This is game is yet to be implemented"); 34 36 } 35 37 36 - /* 37 - - Switch Game (overlay?) 38 - - Game Interface with default configurations 39 - - Game Handlers (should export form here) 40 - */ 41 - 42 - } 38 + render(): void { 39 + // render .ejs file 40 + let file = ejs.renderFile("./template.ejs", { }) 43 41 42 + console.log(file) 43 + } 44 44 45 + unRender() { 45 46 47 + } 46 48 47 - export class testMiniGame implements miniGameInterface { 48 - control: controls; 49 49 50 - constructor() { 51 - this.control = controls.keyboard_mouse; 50 + finish(): void { 51 + this.reset(); 52 52 } 53 53 54 - run(): void { 55 54 55 + reset(): void { 56 + this.score = 0; 57 + this.unRender(); 56 58 } 57 59 58 - reset() { 59 - } 60 + } 60 61 61 - }
+32
src/frontend/public/controller/miniGame/miniGameController.ts
··· 1 + import {controls, miniGameInterface} from "./miniGame.js"; 2 + import Smash from "./smash.js"; 3 + import Maze from "./maze/maze.js"; 4 + 5 + export default class MiniGameController { 6 + games: miniGameInterface[] 7 + loaded: miniGameInterface | null 8 + 9 + constructor( 10 + games = [ 11 + new Smash(), 12 + ], 13 + ) { 14 + this.games = games; 15 + this.loaded = null; 16 + } 17 + 18 + switchLoadedRandom(): miniGameInterface { 19 + // const selected: miniGameInterface = this.games[Math.floor(Math.random() * this.games.length)]; 20 + const selected = new Maze(); 21 + 22 + this.loaded = selected; 23 + return selected; 24 + } 25 + 26 + /* 27 + - Switch Game (overlay?) 28 + - Game Interface with default configurations 29 + - Game Handlers (should export form here) 30 + */ 31 + 32 + }
+12 -15
src/frontend/public/controller/miniGame/smash.ts
··· 1 - import {controls, miniGameInterface} from "./miniGame.js"; 1 + import {BaseMiniGame, controls} from "./miniGame.js"; 2 2 3 - export default class Smash implements miniGameInterface { 4 - control: controls; 5 - 6 - amount: number 3 + export default class Smash extends BaseMiniGame { 7 4 8 5 constructor() { 9 - this.control = controls.keyboard; 6 + super(); 7 + } 10 8 11 - this.amount = 0; 9 + loop() { 10 + this.keyboardEvent(); 11 + console.log(this.score); 12 12 } 13 13 14 - run() { 15 - // smash keyboard -> specific amounts of letters win 16 - // theme? 14 + render() { 17 15 18 - this.keyboardEvent(); 19 - console.log(this.amount); 20 16 } 21 17 22 - reset() { 23 - this.amount = 0; 18 + 19 + finish() { 20 + 24 21 } 25 22 26 23 keyboardEvent() { 27 24 document.addEventListener("keydown", (e: KeyboardEvent) => { 28 25 if (e.key == "Escape") return; // Pausing the game shouldn't count for the score 29 - this.amount++; 26 + this.score++; 30 27 }) 31 28 } 32 29
+1 -1
src/frontend/tsconfig.json
··· 2 2 "extends": "../../tsconfig.json", 3 3 "compilerOptions": { 4 4 "module": "es6", 5 - "lib": [ "dom" ] 5 + "lib": [ "dom", "es2016" ] 6 6 }, 7 7 }