diff --git a/resources/lang/en.json b/resources/lang/en.json index d9966976e0..1739779358 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -404,6 +404,19 @@ "host_modal": { "title": "Create Private Lobby", "label": "Private", + "presets_title": "Presets", + "presets_select": "Saved presets", + "presets_default": "Default", + "presets_name": "Preset name", + "presets_name_placeholder": "Enter preset name", + "presets_apply": "Apply", + "presets_save": "Save", + "presets_delete": "Delete", + "presets_auto_apply": "Auto-apply last used preset", + "presets_saved": "Preset saved: {name}", + "presets_applied": "Preset applied: {name}", + "presets_deleted": "Preset deleted: {name}", + "presets_not_found": "Preset not found", "mode": "Mode", "team_count": "Number of Teams", "team_type": "Team Type", diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts index 8b3e314f45..d024f7a2e6 100644 --- a/src/client/HostLobbyModal.ts +++ b/src/client/HostLobbyModal.ts @@ -1,11 +1,10 @@ import { TemplateResult, html } from "lit"; -import { customElement, state } from "lit/decorators.js"; +import { customElement, query, state } from "lit/decorators.js"; import { translateText } from "../client/Utils"; import { getServerConfigFromClient } from "../core/configuration/ConfigLoader"; import { Difficulty, Duos, - GameMapSize, GameMapType, GameMode, HumansVsNations, @@ -27,49 +26,63 @@ import "./components/CopyButton"; import "./components/Difficulties"; import "./components/FluentSlider"; import "./components/LobbyPlayerView"; +import "./components/LobbyPresetControls"; +import type { LobbyPresetControls } from "./components/LobbyPresetControls"; import "./components/map/MapPicker"; import { modalHeader } from "./components/ui/ModalHeader"; import { crazyGamesSDK } from "./CrazyGamesSDK"; import { JoinLobbyEvent } from "./Main"; import { terrainMapFileLoader } from "./TerrainMapFileLoader"; +import { + GameConfigPatch, + applyHostLobbyGameConfigPatch, + buildHostLobbyGameConfigPatch, + buildPrivateLobbyGameConfig, + resetHostLobbyGameConfigFormState, +} from "./utilities/GameConfigFormState"; import { renderToggleInputCard, renderToggleInputCardInput, } from "./utilities/RenderToggleInputCard"; import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions"; + @customElement("host-lobby-modal") export class HostLobbyModal extends BaseModal { - @state() private selectedMap: GameMapType = GameMapType.World; - @state() private selectedDifficulty: Difficulty = Difficulty.Easy; - @state() private disableNations = false; - @state() private gameMode: GameMode = GameMode.FFA; - @state() private teamCount: TeamCountConfig = 2; + @query("lobby-preset-controls") private presetControls?: LobbyPresetControls; + + @state() private selectedMap!: GameMapType; + @state() private selectedDifficulty!: Difficulty; + @state() private disableNations!: boolean; + @state() private gameMode!: GameMode; + @state() private teamCount!: TeamCountConfig; constructor() { super(); this.id = "page-host-lobby"; + this.resetGameSettingsToDefaults(); } - @state() private bots: number = 400; - @state() private spawnImmunity: boolean = false; - @state() private spawnImmunityDurationMinutes: number | undefined = undefined; - @state() private infiniteGold: boolean = false; - @state() private donateGold: boolean = false; - @state() private infiniteTroops: boolean = false; - @state() private donateTroops: boolean = false; - @state() private maxTimer: boolean = false; - @state() private maxTimerValue: number | undefined = undefined; - @state() private instantBuild: boolean = false; - @state() private randomSpawn: boolean = false; - @state() private compactMap: boolean = false; - @state() private goldMultiplier: boolean = false; - @state() private goldMultiplierValue: number | undefined = undefined; - @state() private startingGold: boolean = false; - @state() private startingGoldValue: number | undefined = undefined; + + @state() private bots!: number; + @state() private spawnImmunity!: boolean; + @state() private spawnImmunityDurationMinutes!: number | undefined; + @state() private infiniteGold!: boolean; + @state() private donateGold!: boolean; + @state() private infiniteTroops!: boolean; + @state() private donateTroops!: boolean; + @state() private maxTimer!: boolean; + @state() private maxTimerValue!: number | undefined; + @state() private instantBuild!: boolean; + @state() private randomSpawn!: boolean; + @state() private compactMap!: boolean; + @state() private goldMultiplier!: boolean; + @state() private goldMultiplierValue!: number | undefined; + @state() private startingGold!: boolean; + @state() private startingGoldValue!: number | undefined; @state() private lobbyId = ""; @state() private lobbyUrlSuffix = ""; @state() private clients: ClientInfo[] = []; - @state() private useRandomMap: boolean = false; - @state() private disabledUnits: UnitType[] = []; + @state() private useRandomMap!: boolean; + @state() private disabledUnits!: UnitType[]; @state() private lobbyCreatorClientID: string = ""; @state() private nationCount: number = 0; @@ -136,6 +149,19 @@ export class HostLobbyModal extends BaseModal { } } + private handlePresetApply = async (patch: GameConfigPatch) => { + this.applyGameConfigPatch(patch); + await this.loadNationCount(); + await this.putGameConfig(); + }; + + private handlePresetReset = async () => { + this.resetGameSettingsToDefaults(); + this.requestUpdate(); + await this.loadNationCount(); + await this.putGameConfig(); + }; + render() { const maxTimerHandlers = this.createToggleHandlers( () => this.maxTimer, @@ -190,6 +216,14 @@ export class HostLobbyModal extends BaseModal {