mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 10:21:27 +00:00
Improve JoinLobbyModal ✨ (#3482)
## Description: Perviously, JoinLobbyModal did not show settings like "Infinite gold" or "Instant Build" or changed tribe count. Now it does. Only if the setting differs from the default. I tested a lot of scenarios, I also thought of the public game modifiers. And we show a small map image now. Public game with lots of modifiers: <img width="780" height="758" alt="Screenshot 2026-03-21 011805" src="https://github.com/user-attachments/assets/9d3fcaa9-3a50-42b2-a351-ac737ef18230" /> A private game with lots of custom settings: <img width="776" height="530" alt="Screenshot 2026-03-21 011940" src="https://github.com/user-attachments/assets/8f9a3809-844d-4f24-8f92-46c4ce480f8c" /> A private game with disabled units: <img width="786" height="562" alt="Screenshot 2026-03-21 012134" src="https://github.com/user-attachments/assets/61058329-1d86-4667-a945-7819b89cbf41" /> Regular public FFA (No modifiers): <img width="780" height="372" alt="Screenshot 2026-03-21 012228" src="https://github.com/user-attachments/assets/abdc42f0-8f2c-40c1-8719-76c648a12bae" /> This PR also includes a fix for UsernameInput: <img width="910" height="647" alt="Screenshot 2026-03-20 222021" src="https://github.com/user-attachments/assets/e1922395-9dfc-4b32-b987-e2dbff9af917" /> This PR also fixes the default private lobby difficulty in GameManager ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin
This commit is contained in:
+199
-41
@@ -2,13 +2,10 @@ import { html, TemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators.js";
|
||||
import {
|
||||
calculateServerTimeOffset,
|
||||
getActiveModifiers,
|
||||
getGameModeLabel,
|
||||
getMapName,
|
||||
getSecondsUntilServerTimestamp,
|
||||
getServerNow,
|
||||
renderDuration,
|
||||
renderNumber,
|
||||
translateText,
|
||||
} from "../client/Utils";
|
||||
import { EventBus } from "../core/EventBus";
|
||||
@@ -22,11 +19,18 @@ import {
|
||||
PublicGameInfo,
|
||||
} from "../core/Schemas";
|
||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||
import { GameMode, GameType, HumansVsNations } from "../core/game/Game";
|
||||
import {
|
||||
Difficulty,
|
||||
GameMapSize,
|
||||
GameMode,
|
||||
GameType,
|
||||
HumansVsNations,
|
||||
} from "../core/game/Game";
|
||||
import { getApiBase } from "./Api";
|
||||
import { crazyGamesSDK } from "./CrazyGamesSDK";
|
||||
import { JoinLobbyEvent } from "./Main";
|
||||
import { terrainMapFileLoader } from "./TerrainMapFileLoader";
|
||||
import { normaliseMapKey } from "./Utils";
|
||||
import { BaseModal } from "./components/BaseModal";
|
||||
import "./components/CopyButton";
|
||||
import "./components/LobbyConfigItem";
|
||||
@@ -426,45 +430,197 @@ export class JoinLobbyModal extends BaseModal {
|
||||
|
||||
const c = this.gameConfig;
|
||||
const mapName = getMapName(c.gameMap);
|
||||
const modeName = getGameModeLabel(c);
|
||||
const modifiers = getActiveModifiers(c.publicGameModifiers);
|
||||
const normalizedMap = normaliseMapKey(c.gameMap);
|
||||
const thumbnailUrl = `/maps/${encodeURIComponent(normalizedMap)}/thumbnail.webp`;
|
||||
const isTeam = c.gameMode === GameMode.Team;
|
||||
|
||||
let modeSubtitle: string;
|
||||
if (!isTeam) {
|
||||
modeSubtitle = translateText("game_mode.ffa");
|
||||
} else if (c.playerTeams === HumansVsNations) {
|
||||
modeSubtitle = translateText("host_modal.teams_Humans Vs Nations");
|
||||
} else if (typeof c.playerTeams === "string") {
|
||||
modeSubtitle = translateText("host_modal.teams_" + c.playerTeams);
|
||||
} else if (typeof c.playerTeams === "number") {
|
||||
modeSubtitle = translateText("public_lobby.teams", {
|
||||
num: c.playerTeams,
|
||||
});
|
||||
} else {
|
||||
modeSubtitle = translateText("game_mode.ffa");
|
||||
}
|
||||
|
||||
const pm = c.publicGameModifiers;
|
||||
const cards: TemplateResult[] = [];
|
||||
if (pm?.isCrowded)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("host_modal.crowded")}
|
||||
.value=${translateText("common.enabled")}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
if (
|
||||
pm?.isHardNations ||
|
||||
(c.gameType === GameType.Private && c.difficulty !== Difficulty.Easy)
|
||||
)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("difficulty.difficulty")}
|
||||
.value=${translateText(`difficulty.${c.difficulty.toLowerCase()}`)}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
if (c.infiniteTroops)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("host_modal.infinite_troops")}
|
||||
.value=${translateText("common.enabled")}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
if (c.infiniteGold)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("host_modal.infinite_gold")}
|
||||
.value=${translateText("common.enabled")}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
if (c.instantBuild)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("host_modal.instant_build")}
|
||||
.value=${translateText("common.enabled")}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
if (c.randomSpawn)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("host_modal.random_spawn")}
|
||||
.value=${translateText("common.enabled")}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
if (c.maxTimerValue)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("private_lobby.game_length")}
|
||||
.value=${`${c.maxTimerValue} min`}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
if (
|
||||
c.spawnImmunityDuration &&
|
||||
Math.round(c.spawnImmunityDuration / 10) !== 5
|
||||
) {
|
||||
const totalSeconds = Math.round(c.spawnImmunityDuration / 10);
|
||||
const immunityValue =
|
||||
totalSeconds < 60
|
||||
? `${totalSeconds}s`
|
||||
: totalSeconds % 60 > 0
|
||||
? `${Math.floor(totalSeconds / 60)}m ${totalSeconds % 60}s`
|
||||
: `${Math.floor(totalSeconds / 60)} min`;
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("private_lobby.pvp_immunity")}
|
||||
.value=${immunityValue}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
}
|
||||
if (c.startingGold)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("private_lobby.starting_gold")}
|
||||
.value=${`${parseFloat((c.startingGold / 1_000_000).toPrecision(12))}M`}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
if (c.goldMultiplier)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("host_modal.gold_multiplier")}
|
||||
.value=${`x${c.goldMultiplier}`}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
if (c.disableAlliances)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText(
|
||||
"public_game_modifier.disable_alliances_label",
|
||||
)}
|
||||
.value=${translateText("common.disabled")}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
if ((isTeam && !c.donateGold) || (!isTeam && c.donateGold))
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("host_modal.donate_gold")}
|
||||
.value=${translateText(
|
||||
c.donateGold ? "common.enabled" : "common.disabled",
|
||||
)}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
if ((isTeam && !c.donateTroops) || (!isTeam && c.donateTroops))
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("host_modal.donate_troops")}
|
||||
.value=${translateText(
|
||||
c.donateTroops ? "common.enabled" : "common.disabled",
|
||||
)}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
const isCompact =
|
||||
c.gameMapSize === GameMapSize.Compact || c.publicGameModifiers?.isCompact;
|
||||
if (isCompact)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("host_modal.compact_map")}
|
||||
.value=${translateText("common.enabled")}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
{
|
||||
const defaultBots = isCompact ? 100 : 400;
|
||||
if (c.bots !== defaultBots)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("host_modal.bots")}
|
||||
.value=${String(c.bots)}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
}
|
||||
{
|
||||
const defaultNations = isCompact
|
||||
? Math.max(0, Math.floor(this.nationCount * 0.25))
|
||||
: this.nationCount;
|
||||
if (typeof c.nations === "number" && c.nations !== defaultNations)
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("host_modal.nations")}
|
||||
.value=${String(c.nations)}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
}
|
||||
if (c.nations === "disabled" && !(c.gameType === GameType.Public && isTeam))
|
||||
cards.push(
|
||||
html`<lobby-config-item
|
||||
.label=${translateText("host_modal.nations")}
|
||||
.value=${translateText("common.disabled")}
|
||||
></lobby-config-item>`,
|
||||
);
|
||||
|
||||
return html`
|
||||
<div class="grid grid-cols-2 sm:grid-cols-3 gap-2">
|
||||
<lobby-config-item
|
||||
.label=${translateText("map.map")}
|
||||
.value=${mapName}
|
||||
></lobby-config-item>
|
||||
<lobby-config-item
|
||||
.label=${translateText("host_modal.mode")}
|
||||
.value=${modeName}
|
||||
></lobby-config-item>
|
||||
${modifiers.map(
|
||||
(m) => html`
|
||||
<lobby-config-item
|
||||
.label=${translateText(m.labelKey)}
|
||||
.value=${m.formattedValue ??
|
||||
(m.value !== undefined
|
||||
? renderNumber(m.value)
|
||||
: translateText("common.enabled"))}
|
||||
></lobby-config-item>
|
||||
`,
|
||||
)}
|
||||
${c.gameMode !== GameMode.FFA &&
|
||||
c.playerTeams &&
|
||||
c.playerTeams !== HumansVsNations
|
||||
? html`
|
||||
<lobby-config-item
|
||||
.label=${typeof c.playerTeams === "string"
|
||||
? translateText("host_modal.team_type")
|
||||
: translateText("host_modal.team_count")}
|
||||
.value=${typeof c.playerTeams === "string"
|
||||
? translateText("host_modal.teams_" + c.playerTeams)
|
||||
: c.playerTeams.toString()}
|
||||
></lobby-config-item>
|
||||
`
|
||||
: html``}
|
||||
<div class="flex items-center gap-3 mb-6">
|
||||
<img
|
||||
src=${thumbnailUrl}
|
||||
alt=${mapName ?? c.gameMap}
|
||||
class="w-20 h-20 rounded-lg object-cover border border-white/10 shrink-0"
|
||||
@error=${(e: Event) => {
|
||||
(e.target as HTMLImageElement).style.display = "none";
|
||||
}}
|
||||
/>
|
||||
<div class="flex flex-col gap-1">
|
||||
<span class="text-lg font-bold text-white">${mapName}</span>
|
||||
<span class="text-sm text-white/60">${modeSubtitle}</span>
|
||||
</div>
|
||||
</div>
|
||||
${cards.length > 0
|
||||
? html`<div class="grid grid-cols-2 sm:grid-cols-3 gap-2 mb-6">
|
||||
${cards}
|
||||
</div>`
|
||||
: html``}
|
||||
${this.renderDisabledUnits()}
|
||||
`;
|
||||
}
|
||||
@@ -495,7 +651,9 @@ export class JoinLobbyModal extends BaseModal {
|
||||
};
|
||||
|
||||
return html`
|
||||
<div class="mt-4 p-3 bg-red-500/10 border border-red-500/20 rounded-lg">
|
||||
<div
|
||||
class="mt-4 mb-6 p-3 bg-red-500/10 border border-red-500/20 rounded-lg"
|
||||
>
|
||||
<div
|
||||
class="text-xs font-bold text-red-400 uppercase tracking-widest mb-2"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user