diff --git a/resources/lang/en.json b/resources/lang/en.json
index d9b6c0d9b..56c8941cd 100644
--- a/resources/lang/en.json
+++ b/resources/lang/en.json
@@ -280,6 +280,8 @@
},
"map": {
"map": "Map",
+ "featured": "Featured",
+ "all": "All",
"world": "World",
"giantworldmap": "Giant World Map",
"europe": "Europe",
@@ -330,6 +332,7 @@
"amazonriver": "Amazon River"
},
"map_categories": {
+ "featured": "Featured",
"continental": "Continental",
"regional": "Regional",
"fantasy": "Other",
diff --git a/src/client/HelpModal.ts b/src/client/HelpModal.ts
index a5d150f3f..e696db76f 100644
--- a/src/client/HelpModal.ts
+++ b/src/client/HelpModal.ts
@@ -3,7 +3,6 @@ import { customElement, state } from "lit/decorators.js";
import { translateText } from "../client/Utils";
import { BaseModal } from "./components/BaseModal";
import "./components/Difficulties";
-import "./components/Maps";
import { modalHeader } from "./components/ui/ModalHeader";
import { TroubleshootingModal } from "./TroubleshootingModal";
diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts
index 5355f9f33..eff76894b 100644
--- a/src/client/HostLobbyModal.ts
+++ b/src/client/HostLobbyModal.ts
@@ -12,7 +12,6 @@ import {
Quads,
Trios,
UnitType,
- mapCategories,
} from "../core/game/Game";
import {
ClientInfo,
@@ -28,7 +27,7 @@ import "./components/CopyButton";
import "./components/Difficulties";
import "./components/FluentSlider";
import "./components/LobbyPlayerView";
-import "./components/Maps";
+import "./components/map/MapPicker";
import { modalHeader } from "./components/ui/ModalHeader";
import { crazyGamesSDK } from "./CrazyGamesSDK";
import { JoinLobbyEvent } from "./Main";
@@ -38,7 +37,6 @@ import {
renderToggleInputCardInput,
} from "./utilities/RenderToggleInputCard";
import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions";
-import randomMap from "/images/RandomMap.webp?url";
@customElement("host-lobby-modal")
export class HostLobbyModal extends BaseModal {
@state() private selectedMap: GameMapType = GameMapType.World;
@@ -209,80 +207,14 @@ export class HostLobbyModal extends BaseModal {
${translateText("map.map")}
-
-
- ${Object.entries(mapCategories).map(
- ([categoryKey, maps]) => html`
-
-
- ${translateText(`map_categories.${categoryKey}`)}
-
-
- ${maps.map((mapValue) => {
- const mapKey = Object.entries(GameMapType).find(
- ([, v]) => v === mapValue,
- )?.[0];
- return html`
-
this.handleMapSelection(mapValue)}
- class="cursor-pointer transition-transform duration-200 active:scale-95"
- >
-
-
- `;
- })}
-
-
- `,
- )}
-
-
-
- ${translateText("map_categories.special")}
-
-
-
-
-
-
+
+ this.handleMapSelection(mapValue)}
+ .onSelectRandom=${() => this.handleSelectRandomMap()}
+ >
diff --git a/src/client/SinglePlayerModal.ts b/src/client/SinglePlayerModal.ts
index dece359e4..e423d7596 100644
--- a/src/client/SinglePlayerModal.ts
+++ b/src/client/SinglePlayerModal.ts
@@ -13,7 +13,6 @@ import {
Quads,
Trios,
UnitType,
- mapCategories,
} from "../core/game/Game";
import { UserSettings } from "../core/game/UserSettings";
import { TeamCountConfig } from "../core/Schemas";
@@ -24,7 +23,7 @@ import "./components/baseComponents/Modal";
import { BaseModal } from "./components/BaseModal";
import "./components/Difficulties";
import "./components/FluentSlider";
-import "./components/Maps";
+import "./components/map/MapPicker";
import { modalHeader } from "./components/ui/ModalHeader";
import { fetchCosmetics } from "./Cosmetics";
import { FlagInput } from "./FlagInput";
@@ -35,7 +34,6 @@ import {
renderToggleInputCardInput,
} from "./utilities/RenderToggleInputCard";
import { renderUnitTypeOptions } from "./utilities/RenderUnitTypeOptions";
-import randomMap from "/images/RandomMap.webp?url";
@customElement("single-player-modal")
export class SinglePlayerModal extends BaseModal {
@@ -197,84 +195,15 @@ export class SinglePlayerModal extends BaseModal {
-
- ${Object.entries(mapCategories).map(
- ([categoryKey, maps]) => html`
-
-
- ${translateText(`map_categories.${categoryKey}`)}
-
-
- ${maps.map((mapValue) => {
- const mapKey = Object.keys(GameMapType).find(
- (key) =>
- GameMapType[key as keyof typeof GameMapType] ===
- mapValue,
- );
- return html`
-
this.handleMapSelection(mapValue)}
- class="cursor-pointer transition-transform duration-200 active:scale-95"
- >
-
-
- `;
- })}
-
-
- `,
- )}
-
-
-
-
- ${translateText("map_categories.special")}
-
-
-
-
-
-
+
+ this.handleMapSelection(mapValue)}
+ .onSelectRandom=${() => this.handleSelectRandomMap()}
+ >
diff --git a/src/client/components/Maps.ts b/src/client/components/map/MapDisplay.ts
similarity index 96%
rename from src/client/components/Maps.ts
rename to src/client/components/map/MapDisplay.ts
index e46e4691b..b7fc1364c 100644
--- a/src/client/components/Maps.ts
+++ b/src/client/components/map/MapDisplay.ts
@@ -1,8 +1,8 @@
import { LitElement, html } from "lit";
import { customElement, property, state } from "lit/decorators.js";
-import { Difficulty, GameMapType } from "../../core/game/Game";
-import { terrainMapFileLoader } from "../TerrainMapFileLoader";
-import { translateText } from "../Utils";
+import { Difficulty, GameMapType } from "../../../core/game/Game";
+import { terrainMapFileLoader } from "../../TerrainMapFileLoader";
+import { translateText } from "../../Utils";
@customElement("map-display")
export class MapDisplay extends LitElement {
diff --git a/src/client/components/map/MapPicker.ts b/src/client/components/map/MapPicker.ts
new file mode 100644
index 000000000..52607622e
--- /dev/null
+++ b/src/client/components/map/MapPicker.ts
@@ -0,0 +1,183 @@
+import { LitElement, html } from "lit";
+import { customElement, property, state } from "lit/decorators.js";
+import {
+ Difficulty,
+ GameMapType,
+ mapCategories,
+} from "../../../core/game/Game";
+import { translateText } from "../../Utils";
+import "./MapDisplay";
+import randomMap from "/images/RandomMap.webp?url";
+
+const featuredMaps: GameMapType[] = [
+ GameMapType.World,
+ GameMapType.Europe,
+ GameMapType.NorthAmerica,
+ GameMapType.SouthAmerica,
+ GameMapType.Asia,
+ GameMapType.Africa,
+ GameMapType.Japan,
+];
+
+@customElement("map-picker")
+export class MapPicker extends LitElement {
+ @property({ type: String }) selectedMap: GameMapType = GameMapType.World;
+ @property({ type: Boolean }) useRandomMap = false;
+ @property({ type: Boolean }) showMedals = false;
+ @property({ type: Boolean }) randomMapDivider = false;
+ @property({ attribute: false }) mapWins: Map> =
+ new Map();
+ @property({ attribute: false }) onSelectMap?: (map: GameMapType) => void;
+ @property({ attribute: false }) onSelectRandom?: () => void;
+ @state() private showAllMaps = false;
+
+ createRenderRoot() {
+ return this;
+ }
+
+ private handleMapSelection(mapValue: GameMapType) {
+ this.onSelectMap?.(mapValue);
+ }
+
+ private handleSelectRandomMap = () => {
+ this.onSelectRandom?.();
+ };
+
+ private getWins(mapValue: GameMapType): Set {
+ return this.mapWins?.get(mapValue) ?? new Set();
+ }
+
+ private renderMapCard(mapValue: GameMapType) {
+ const mapKey = Object.entries(GameMapType).find(
+ ([_, value]) => value === mapValue,
+ )?.[0];
+ return html`
+ this.handleMapSelection(mapValue)}
+ class="cursor-pointer transition-transform duration-200 active:scale-95"
+ >
+
+
+ `;
+ }
+
+ private renderAllMaps() {
+ const mapCategoryEntries = Object.entries(mapCategories);
+ return html`
+ ${mapCategoryEntries.map(
+ ([categoryKey, maps]) => html`
+
+
+ ${translateText(`map_categories.${categoryKey}`)}
+
+
+ ${maps.map((mapValue) => this.renderMapCard(mapValue))}
+
+
+ `,
+ )}
+
`;
+ }
+
+ private renderFeaturedMaps() {
+ let featuredMapList = featuredMaps;
+ if (!featuredMapList.includes(this.selectedMap)) {
+ featuredMapList = [this.selectedMap, ...featuredMaps];
+ }
+ return html`
+
+ ${translateText("map_categories.featured")}
+
+
+ ${featuredMapList.map((mapValue) => this.renderMapCard(mapValue))}
+
+
`;
+ }
+
+ render() {
+ return html`
+
+
+
+
+
+
+
+ ${this.showAllMaps ? this.renderAllMaps() : this.renderFeaturedMaps()}
+
+
+ ${translateText("map_categories.special")}
+
+
+
+
+
+
+ `;
+ }
+}