From 6f0a32f05486c2ab47415bcf8b57571d4b79a1e9 Mon Sep 17 00:00:00 2001 From: Greg Considine Date: Sun, 14 Mar 2021 17:22:14 -0400 Subject: [PATCH] [WIP] Add mode menu. Add factory-based gameMode creation --- src/js/core/global_registries.js | 4 ++ src/js/game/core.js | 5 ++- src/js/game/game_mode.js | 44 ++++++++++++++++--- src/js/game/game_mode_registry.js | 10 +++++ src/js/game/modes/puzzle_edit.js | 11 +++++ src/js/game/modes/puzzle_play.js | 11 +++++ src/js/game/modes/regular.js | 4 ++ src/js/main.js | 2 + src/js/savegame/savegame_serializer.js | 1 + src/js/states/main_menu.js | 59 +++++++++++++++++++++++--- translations/base-en.yaml | 1 + 11 files changed, 139 insertions(+), 13 deletions(-) create mode 100644 src/js/game/game_mode_registry.js create mode 100644 src/js/game/modes/puzzle_edit.js create mode 100644 src/js/game/modes/puzzle_play.js diff --git a/src/js/core/global_registries.js b/src/js/core/global_registries.js index ad45850c..723bf567 100644 --- a/src/js/core/global_registries.js +++ b/src/js/core/global_registries.js @@ -5,6 +5,7 @@ import { Factory } from "./factory"; * @typedef {import("../game/time/base_game_speed").BaseGameSpeed} BaseGameSpeed * @typedef {import("../game/component").Component} Component * @typedef {import("../game/base_item").BaseItem} BaseItem + * @typedef {import("../game/game_mode").GameMode} GameMode * @typedef {import("../game/meta_building").MetaBuilding} MetaBuilding @@ -19,6 +20,9 @@ export let gBuildingsByCategory = null; /** @type {FactoryTemplate} */ export let gComponentRegistry = new Factory("component"); +/** @type {FactoryTemplate} */ +export let gGameModeRegistry = new Factory("gameMode"); + /** @type {FactoryTemplate} */ export let gGameSpeedRegistry = new Factory("gamespeed"); diff --git a/src/js/game/core.js b/src/js/game/core.js index f4b3e9ee..b8828f45 100644 --- a/src/js/game/core.js +++ b/src/js/game/core.js @@ -9,6 +9,7 @@ import { registerCanvas, } from "../core/buffer_utils"; import { globalConfig } from "../core/config"; +import { gGameModeRegistry } from "./game_mode_registry"; import { getDeviceDPI, resizeHighDPICanvas } from "../core/dpi_manager"; import { DrawParameters } from "../core/draw_parameters"; import { gMetaBuildingRegistry } from "../core/global_registries"; @@ -31,7 +32,7 @@ import { KeyActionMapper } from "./key_action_mapper"; import { GameLogic } from "./logic"; import { MapView } from "./map_view"; import { defaultBuildingVariant } from "./meta_building"; -import { RegularGameMode } from "./modes/regular"; +import { GameMode } from "./game_mode"; import { ProductionAnalytics } from "./production_analytics"; import { GameRoot } from "./root"; import { ShapeDefinitionManager } from "./shape_definition_manager"; @@ -104,7 +105,7 @@ export class GameCore { root.dynamicTickrate = new DynamicTickrate(root); // Init game mode - root.gameMode = new RegularGameMode(root); + root.gameMode = GameMode.create(root); // Init classes root.camera = new Camera(root); diff --git a/src/js/game/game_mode.js b/src/js/game/game_mode.js index 15403eb5..920cc30a 100644 --- a/src/js/game/game_mode.js +++ b/src/js/game/game_mode.js @@ -1,8 +1,10 @@ /* typehints:start */ import { enumHubGoalRewards } from "./tutorial_goals"; +import { GameRoot } from "./root"; /* typehints:end */ -import { GameRoot } from "./root"; +import { gGameModeRegistry } from "../core/global_registries"; +import { types, BasicSerializableObject } from "../savegame/serialization"; /** @typedef {{ * shape: string, @@ -24,13 +26,43 @@ import { GameRoot } from "./root"; * throughputOnly?: boolean * }} LevelDefinition */ -export class GameMode { - /** - * - * @param {GameRoot} root - */ +export class GameMode extends BasicSerializableObject { + /** @returns {string} */ + static getId() { + abstract; + return "unknown-mode"; + } + + static getSchema() { + return { + id: types.string + } + } + + static create (root) { + let id; + + if (!root.savegame.gameMode || !root.savegame.gameMode.id) { + id = "Regular"; + } else { + id = root.savegame.gameMode.id + } + + const Mode = gGameModeRegistry.findById(id); + + return new Mode(root); + } + + /** @param {GameRoot} root */ constructor(root) { + super(); this.root = root; + this.id = this.getId(); + } + + getId() { + // @ts-ignore + return this.constructor.getId(); } /** diff --git a/src/js/game/game_mode_registry.js b/src/js/game/game_mode_registry.js new file mode 100644 index 00000000..03daceb0 --- /dev/null +++ b/src/js/game/game_mode_registry.js @@ -0,0 +1,10 @@ +import { gGameModeRegistry } from "../core/global_registries"; +import { PuzzleEditGameMode } from "./modes/puzzle_edit"; +import { PuzzlePlayGameMode } from "./modes/puzzle_play"; +import { RegularGameMode } from "./modes/regular"; + +export function initGameModeRegistry() { + gGameModeRegistry.register(PuzzleEditGameMode); + gGameModeRegistry.register(PuzzlePlayGameMode); + gGameModeRegistry.register(RegularGameMode); +} diff --git a/src/js/game/modes/puzzle_edit.js b/src/js/game/modes/puzzle_edit.js new file mode 100644 index 00000000..39dc24dc --- /dev/null +++ b/src/js/game/modes/puzzle_edit.js @@ -0,0 +1,11 @@ +import { GameMode } from "../game_mode"; + +export class PuzzleEditGameMode extends GameMode { + static getId() { + return "PuzzleEdit"; + } + + constructor(root) { + super(root); + } +} diff --git a/src/js/game/modes/puzzle_play.js b/src/js/game/modes/puzzle_play.js new file mode 100644 index 00000000..49a9b681 --- /dev/null +++ b/src/js/game/modes/puzzle_play.js @@ -0,0 +1,11 @@ +import { GameMode } from "../game_mode"; + +export class PuzzlePlayGameMode extends GameMode { + static getId() { + return "PuzzlePlay"; + } + + constructor(root) { + super(root); + } +} diff --git a/src/js/game/modes/regular.js b/src/js/game/modes/regular.js index e99f4a7c..851b5653 100644 --- a/src/js/game/modes/regular.js +++ b/src/js/game/modes/regular.js @@ -454,6 +454,10 @@ const fullVersionLevels = generateLevelDefinitions(false); const demoVersionLevels = generateLevelDefinitions(true); export class RegularGameMode extends GameMode { + static getId() { + return "Regular"; + } + constructor(root) { super(root); } diff --git a/src/js/main.js b/src/js/main.js index 5b9df699..94f3d37a 100644 --- a/src/js/main.js +++ b/src/js/main.js @@ -9,6 +9,7 @@ import { initComponentRegistry } from "./game/component_registry"; import { initDrawUtils } from "./core/draw_utils"; import { initItemRegistry } from "./game/item_registry"; import { initMetaBuildingRegistry } from "./game/meta_building_registry"; +import { initGameModeRegistry } from "./game/game_mode_registry"; import { initGameSpeedRegistry } from "./game/game_speed_registry"; const logger = createLogger("main"); @@ -81,6 +82,7 @@ initDrawUtils(); initComponentRegistry(); initItemRegistry(); initMetaBuildingRegistry(); +initGameModeRegistry(); initGameSpeedRegistry(); let app = null; diff --git a/src/js/savegame/savegame_serializer.js b/src/js/savegame/savegame_serializer.js index c1247225..f52c2c70 100644 --- a/src/js/savegame/savegame_serializer.js +++ b/src/js/savegame/savegame_serializer.js @@ -33,6 +33,7 @@ export class SavegameSerializer { camera: root.camera.serialize(), time: root.time.serialize(), map: root.map.serialize(), + gameMode: root.gameMode.serialize(), entityMgr: root.entityMgr.serialize(), hubGoals: root.hubGoals.serialize(), pinnedShapes: root.hud.parts.pinnedShapes.serialize(), diff --git a/src/js/states/main_menu.js b/src/js/states/main_menu.js index af05855f..b2748a00 100644 --- a/src/js/states/main_menu.js +++ b/src/js/states/main_menu.js @@ -207,6 +207,11 @@ export class MainMenuState extends GameState { const qs = this.htmlElement.querySelector.bind(this.htmlElement); if (G_IS_DEV && globalConfig.debug.fastGameEnter) { + if (globalConfig.debug.testPuzzleMode) { + this.onPuzzlePlayButtonClicked(); + return; + } + const games = this.app.savegameMgr.getSavegamesMetaData(); if (games.length > 0 && globalConfig.debug.resumeGameOnFastEnter) { this.resumeGame(games[0]); @@ -308,21 +313,65 @@ export class MainMenuState extends GameState { buttonContainer.appendChild(importButtonElement); } - const modeButtonContainer = this.htmlElement.querySelector(".bottomContainer .buttons"); - removeAllChildren(modeButtonContainer); + const bottomButtonContainer = this.htmlElement.querySelector(".bottomContainer .buttons"); + removeAllChildren(bottomButtonContainer); const puzzleModeButton = makeButton( - modeButtonContainer, + bottomButtonContainer, ["styledButton"], T.mainMenu.puzzleMode ); - modeButtonContainer.appendChild(puzzleModeButton); + bottomButtonContainer.appendChild(puzzleModeButton); this.trackClicks(puzzleModeButton, this.onPuzzleModeButtonClicked); } + renderPuzzleModeMenu() { + const savegames = this.htmlElement.querySelector(".mainContainer .savegames"); + + if (savegames) { + savegames.remove(); + } + + const buttonContainer = this.htmlElement.querySelector(".mainContainer .buttons"); + removeAllChildren(buttonContainer); + + const playButtonElement = makeButtonElement( + ["playButton", "styledButton"], + T.mainMenu.play + ); + + buttonContainer.appendChild(playButtonElement); + this.trackClicks(playButtonElement, this.onPuzzlePlayButtonClicked); + + const bottomButtonContainer = this.htmlElement.querySelector(".bottomContainer .buttons"); + removeAllChildren(bottomButtonContainer); + + const backButton = makeButton( + bottomButtonContainer, + ["styledButton"], + T.mainMenu.back + ); + + bottomButtonContainer.appendChild(backButton); + this.trackClicks(backButton, this.onBackButtonClicked); + } + + onPuzzlePlayButtonClicked() { + const savegame = this.app.savegameMgr.createNewSavegame(); + + this.moveToState("InGameState", { + savegame, + }); + } + onPuzzleModeButtonClicked() { - this.moveToState("InGameState"); + this.renderPuzzleModeMenu(); + } + + onBackButtonClicked() { + this.renderMainMenu(); + this.renderSavegames(); } onSteamLinkClicked() { diff --git a/translations/base-en.yaml b/translations/base-en.yaml index f88f4f12..e341a06a 100644 --- a/translations/base-en.yaml +++ b/translations/base-en.yaml @@ -117,6 +117,7 @@ mainMenu: savegameLevelUnknown: Unknown Level savegameUnnamed: Unnamed puzzleMode: Puzzle Mode + back: Back dialogs: buttons: