mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-24 01:53:37 +00:00
Merge branch 'main' of github.com:openfrontio/OpenFrontIO
This commit is contained in:
+249
-167
@@ -1,5 +1,6 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, query } from "lit/decorators.js";
|
||||
import { translateText } from "../client/Utils";
|
||||
import "./components/Difficulties";
|
||||
import "./components/Maps";
|
||||
|
||||
@@ -16,262 +17,343 @@ export class HelpModal extends LitElement {
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<o-modal id="helpModal" title="Instructions" translationKey="main.instructions">
|
||||
<o-modal
|
||||
id="helpModal"
|
||||
title="Instructions"
|
||||
translationKey="main.instructions"
|
||||
>
|
||||
<div class="flex flex-col items-center">
|
||||
<div data-i18n="help_modal.hotkeys" class="text-center text-2xl font-bold mb-4">Hotkeys</div>
|
||||
<div class="text-center text-2xl font-bold mb-4">
|
||||
${translateText("help_modal.hotkeys")}
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-i18n="help_modal.table_key">Key</th>
|
||||
<th data-i18n="help_modal.table_action">Action</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>${translateText("help_modal.table_key")}</th>
|
||||
<th>${translateText("help_modal.table_action")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-left">
|
||||
<tr>
|
||||
<td>Space</td>
|
||||
<td data-i18n="help_modal.action_alt_view">Alternate view (terrain/countries)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shift + left click</td>
|
||||
<td data-i18n="help_modal.action_attack_altclick">Attack (when left click is set to open menu)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ctrl + left click</td>
|
||||
<td data-i18n="help_modal.action_build">Open build menu</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>C</td>
|
||||
<td data-i18n="help_modal.action_center">Center camera on player</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Q / E</td>
|
||||
<td data-i18n="help_modal.action_zoom">Zoom out/in</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>W / A / S / D</td>
|
||||
<td data-i18n="help_modal.action_move_camera">Move camera</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1 / 2</td>
|
||||
<td data-i18n="help_modal.action_ratio_change">Decrease/Increase attack ratio</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shift + scroll down / scroll up</td>
|
||||
<td data-i18n="help_modal.action_ratio_change">Decrease/Increase attack ratio</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ALT + R</td>
|
||||
<td data-i18n="help_modal.action_reset_gfx">Reset graphics</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Space</td>
|
||||
<td>${translateText("help_modal.action_alt_view")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shift + left click</td>
|
||||
<td>${translateText("help_modal.action_attack_altclick")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ctrl + left click</td>
|
||||
<td>${translateText("help_modal.action_build")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>C</td>
|
||||
<td>${translateText("help_modal.action_center")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Q / E</td>
|
||||
<td>${translateText("help_modal.action_zoom")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>W / A / S / D</td>
|
||||
<td>${translateText("help_modal.action_move_camera")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>1 / 2</td>
|
||||
<td>${translateText("help_modal.action_ratio_change")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Shift + scroll down / scroll up</td>
|
||||
<td>${translateText("help_modal.action_ratio_change")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ALT + R</td>
|
||||
<td>${translateText("help_modal.action_reset_gfx")}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<hr class="mt-6 mb-4">
|
||||
<hr class="mt-6 mb-4" />
|
||||
|
||||
<div data-i18n="help_modal.ui_section" class="text-2xl font-bold text-center mb-4">Game UI</div>
|
||||
<div class="text-2xl font-bold text-center mb-4">
|
||||
${translateText("help_modal.ui_section")}
|
||||
</div>
|
||||
<div class="flex flex-col md:flex-row gap-4">
|
||||
<div class="flex flex-col items-center">
|
||||
<div data-i18n="help_modal.ui_leaderboard" class="text-gray-300">Leaderboard</div>
|
||||
<img src="/images/helpModal/leaderboard.webp" alt="Leaderboard" title="Leaderboard" />
|
||||
<div class="text-gray-300">
|
||||
${translateText("help_modal.ui_leaderboard")}
|
||||
</div>
|
||||
<img
|
||||
src="/images/helpModal/leaderboard.webp"
|
||||
alt="Leaderboard"
|
||||
title="Leaderboard"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p data-i18n="help_modal.ui_leaderboard_desc">Shows the top players of the game and their names, % owned land and gold.</p>
|
||||
<p>${translateText("help_modal.ui_leaderboard_desc")}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="mt-6 mb-4">
|
||||
<hr class="mt-6 mb-4" />
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-4">
|
||||
<div class="flex flex-col items-center w-full md:w-[80%]">
|
||||
<div data-i18n="help_modal.ui_control" class="text-gray-300">Control panel</div>
|
||||
<img src="/images/helpModal/controlPanel.webp" alt="Control panel" title="Control panel" />
|
||||
<div class="text-gray-300">
|
||||
${translateText("help_modal.ui_control")}
|
||||
</div>
|
||||
<img
|
||||
src="/images/helpModal/controlPanel.webp"
|
||||
alt="Control panel"
|
||||
title="Control panel"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p data-i18n="help_modal.ui_control_desc" class="mb-4">The control panel contains the following elements:</p>
|
||||
<p class="mb-4">${translateText("help_modal.ui_control_desc")}</p>
|
||||
<ul>
|
||||
<li data-i18n="help_modal.ui_pop" class="mb-4">Pop - The amount of units you have, your max population and the rate at which you gain them.</li>
|
||||
<li data-i18n="help_modal.ui_gold" class="mb-4">Gold - The amount of gold you have and the rate at which you gain it.</li>
|
||||
<li data-i18n="help_modal.ui_troops_workers" class="mb-4">Troops and Workers - The amount of allocated troops and workers. Troops are used to attack or defend against attacks. Workers are used to generate gold. You can adjust the number of troops and workers using the slider.</li>
|
||||
<li data-i18n="help_modal.ui_attack_ratio" class="mb-4">Attack ratio - The amount of troops that will be used when you attack. You can adjust the attack ratio using the slider.</li>
|
||||
<li class="mb-4">${translateText("help_modal.ui_pop")}</li>
|
||||
<li class="mb-4">${translateText("help_modal.ui_gold")}</li>
|
||||
<li class="mb-4">
|
||||
${translateText("help_modal.ui_troops_workers")}
|
||||
</li>
|
||||
<li class="mb-4">
|
||||
${translateText("help_modal.ui_attack_ratio")}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="mt-6 mb-4">
|
||||
<hr class="mt-6 mb-4" />
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-4">
|
||||
<div class="flex flex-col items-center">
|
||||
<div data-i18n="help_modal.ui_options" class="text-gray-300">Options</div>
|
||||
<img src="/images/helpModal/options.webp" alt="Options" title="Options" />
|
||||
<div class="text-gray-300">
|
||||
${translateText("help_modal.ui_options")}
|
||||
</div>
|
||||
<img
|
||||
src="/images/helpModal/options.webp"
|
||||
alt="Options"
|
||||
title="Options"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<p data-i18n="help_modal.ui_options_desc" class="mb-4">The following elements can be found inside:</p>
|
||||
<p class="mb-4">${translateText("help_modal.ui_options_desc")}</p>
|
||||
<ul>
|
||||
<li data-i18n="help_modal.option_pause" class="mb-4">Pause/Unpause the game - Only available in single player mode.</li>
|
||||
<li data-i18n="help_modal.option_timer" class="mb-4">Timer - Time passed since the start of the game.</li>
|
||||
<li data-i18n="help_modal.option_exit" class="mb-4">Exit button.</li>
|
||||
<li data-i18n="help_modal.option_settings" class="mb-4">Settings - Open the settings menu. Inside you can toggle the Alternate View, Dark Mode, Emojis and action on left click.</li>
|
||||
<li class="mb-4">${translateText("help_modal.option_pause")}</li>
|
||||
<li class="mb-4">${translateText("help_modal.option_timer")}</li>
|
||||
<li class="mb-4">${translateText("help_modal.option_exit")}</li>
|
||||
<li class="mb-4">
|
||||
${translateText("help_modal.option_settings")}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="mt-6 mb-4">
|
||||
<hr class="mt-6 mb-4" />
|
||||
|
||||
<div data-i18n="radial_title" class="text-2xl font-bold text-center">Radial menu</div>
|
||||
<div class="text-2xl font-bold text-center">
|
||||
${translateText("help_modal.radial_title")}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-4">
|
||||
<img src="/images/helpModal/radialMenu.webp" alt="Radial menu" title="Radial menu", class="radial-menu-image" />
|
||||
<img
|
||||
src="/images/helpModal/radialMenu.webp"
|
||||
alt="Radial menu"
|
||||
title="Radial menu"
|
||||
,
|
||||
class="radial-menu-image"
|
||||
/>
|
||||
<div>
|
||||
<p data-i18n="help_modal.radial_desc" class="mb-4">Right clicking (or touch on mobile) opens the radial menu. From there you can:</p>
|
||||
<p class="mb-4">${translateText("help_modal.radial_desc")}</p>
|
||||
<ul>
|
||||
<li class="mb-4"><div class="inline-block icon build-icon"></div><span data-i18n="help_modal.radial_build"> - Open the build menu.</span></li>
|
||||
<li class="mb-4">
|
||||
<img src="/images/InfoIcon.svg" class="inline-block icon" style="fill: white; background: transparent;"/><span data-i18n="help_modal.radial_info"> - Open the Info menu.</span></li>
|
||||
<li class="mb-4"><div class="inline-block icon boat-icon"></div><span data-i18n="help_modal.radial_boat"> - Send a boat to attack at the selected location (only available if you have access to water).</span></li>
|
||||
<li class="mb-4"><div class="inline-block icon cancel-icon"></div><span data-i18n="help_modal.radial_close"> - Close the menu.</span></li>
|
||||
<div class="inline-block icon build-icon"></div>
|
||||
<span>${translateText("help_modal.radial_build")}</span>
|
||||
</li>
|
||||
<li class="mb-4">
|
||||
<img
|
||||
src="/images/InfoIcon.svg"
|
||||
class="inline-block icon"
|
||||
style="fill: white; background: transparent;"
|
||||
/>
|
||||
<span>${translateText("help_modal.radial_info")}</span>
|
||||
</li>
|
||||
<li class="mb-4">
|
||||
<div class="inline-block icon boat-icon"></div>
|
||||
<span>${translateText("help_modal.radial_boat")}</span>
|
||||
</li>
|
||||
<li class="mb-4">
|
||||
<div class="inline-block icon cancel-icon"></div>
|
||||
<span>${translateText("help_modal.radial_close")}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="mt-6 mb-4">
|
||||
<hr class="mt-6 mb-4" />
|
||||
|
||||
<div>
|
||||
<div data-i18n="help_modal.info_title" class="text-2xl font-bold text-center">Info menu</div>
|
||||
<div class="text-2xl font-bold text-center">
|
||||
${translateText("help_modal.info_title")}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-4 mt-2">
|
||||
<div class="flex flex-col items-center w-full md:w-[80%]">
|
||||
<div data-i18n="help_modal.info_enemy_panel" class="text-gray-300">Enemy info panel</div>
|
||||
<img src="/images/helpModal/infoMenu.webp" alt="Enemy info panel" title="Enemy info panel" />
|
||||
<divclass="text-gray-300">
|
||||
${translateText("help_modal.info_enemy_panel")}
|
||||
</div>
|
||||
<img
|
||||
src="/images/helpModal/infoMenu.webp"
|
||||
alt="Enemy info panel"
|
||||
title="Enemy info panel"
|
||||
/>
|
||||
</div>
|
||||
<div class="pt-4">
|
||||
<p data-i18n="help_modal.info_enemy_desc" class="mb-4">
|
||||
Contains information such for the selected player name, gold, troops, and if the player is a traitor. Traitor is a player who betrayed and attacked a player who was in an alliance with them. The icons below represent the following interactions:
|
||||
</p>
|
||||
<p class="mb-4">${translateText("help_modal.info_enemy_desc")}</p>
|
||||
<ul>
|
||||
<li class="mb-4"><div class="inline-block icon target-icon"></div><span data-i18n="help_modal.info_target"> - Place a target mark on the player, marking it for all allies, used to coordinate attacks.</span></li>
|
||||
<li class="mb-4"><div class="inline-block icon alliance-icon"></div><span data-i18n="help_modal.info_alliance"> - Send an alliance request to the player. Allies can share resources and troops, but can't attack each other.</span></li>
|
||||
<li><div class="inline-block icon emoji-icon"></div><span data-i18n="help_modal.info_emoji"> - Send an emoji to the player.</span></li>
|
||||
<li class="mb-4">
|
||||
<div class="inline-block icon target-icon"></div>
|
||||
<span>${translateText("help_modal.info_target")}</span>
|
||||
</li>
|
||||
<li class="mb-4">
|
||||
<div class="inline-block icon alliance-icon"></div>
|
||||
<span>${translateText("help_modal.info_alliance")}</span>
|
||||
</li>
|
||||
<li>
|
||||
<div class="inline-block icon emoji-icon"></div>
|
||||
<span>${translateText("help_modal.info_emoji")}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="mt-6 mb-4">
|
||||
<hr class="mt-6 mb-4" />
|
||||
|
||||
<div class="flex flex-col md:flex-row gap-4">
|
||||
<div class="flex flex-col items-center w-full md:w-[62%]">
|
||||
<div data-i18n="help_modal.info_ally_panel" class="text-gray-300">Ally info panel</div>
|
||||
<img src="/images/helpModal/infoMenuAlly.webp" alt="Ally info panel" title="Ally info panel" />
|
||||
<div class="text-gray-300">
|
||||
${translateText("help_modal.info_ally_panel")}
|
||||
</div>
|
||||
<img
|
||||
src="/images/helpModal/infoMenuAlly.webp"
|
||||
alt="Ally info panel"
|
||||
title="Ally info panel"
|
||||
/>
|
||||
</div>
|
||||
<div class="pt-4">
|
||||
<p data-i18n="help_modal.info_ally_desc" class="mb-4">
|
||||
When you ally with a player, the following new icons become available:
|
||||
</p>
|
||||
<p class="mb-4">${translateText("help_modal.info_ally_desc")}</p>
|
||||
<ul>
|
||||
<li class="mb-4"><div class="inline-block icon betray-icon"></div><span data-i18n="help_modal.ally_betray"> - Betray your ally, ending the alliance. You will now have a permanent icon stuck next to your name. Bots are less likely to ally with you and players will think twice before doing so.</span></li>
|
||||
<li class="mb-4"><div class="inline-block icon donate-icon"></div><span data-i18n="help_modal.ally_donate"> - Donate some of your troops to your ally. Used when they're low on troops and are being attacked, or when they need that extra power to crush an enemy.</span></li>
|
||||
<li class="mb-4">
|
||||
<div class="inline-block icon betray-icon"></div>
|
||||
<span>${translateText("help_modal.ally_betray")}</span>
|
||||
</li>
|
||||
<li class="mb-4">
|
||||
<div class="inline-block icon donate-icon"></div>
|
||||
<span>${translateText("help_modal.ally_donate")}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="mt-6 mb-4">
|
||||
<hr class="mt-6 mb-4" />
|
||||
|
||||
<div>
|
||||
<div data-i18n="help_modal.build_menu_title" class="text-2xl font-bold mb-4 text-center">Build menu</div>
|
||||
<div class="text-2xl font-bold mb-4 text-center">
|
||||
${translateText("help_modal.build_menu_title")}
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-i18n="help_modal.build_name">Name</th>
|
||||
<th data-i18n="help_modal.build_icon">Icon</th>
|
||||
<th data-i18n="help_modal.build_desc">Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>${translateText("help_modal.build_name")}</th>
|
||||
<th>${translateText("help_modal.build_icon")}</th>
|
||||
<th>${translateText("help_modal.build_desc")}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-left">
|
||||
<tr>
|
||||
<td data-i18n="help_modal.build_city">City</td>
|
||||
<td><div class="icon city-icon"></div></td>
|
||||
<td data-i18n="help_modal.build_city_desc">
|
||||
Increases your max population. Useful when you can't
|
||||
expand your territory or you're about to hit your
|
||||
population limit.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-i18n="help_modal.build_defense">Defense Post</td>
|
||||
<td><div class="icon defense-post-icon"></div></td>
|
||||
<td data-i18n="help_modal.build_defense_desc">
|
||||
Increases defenses around nearby borders. Attacks from
|
||||
enemies are slower and have more casualties.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-i18n="help_modal.build_port">Port</td>
|
||||
<td><div class="icon port-icon"></div></td>
|
||||
<td data-i18n="help_modal.build_port_desc">
|
||||
Automatically sends trade ships between ports of your
|
||||
country and other countries (except if you clicked "stop
|
||||
trade" on them or they clicked "stop trade on you"), giving
|
||||
gold to both sides. Allows building Battleships. Can only
|
||||
be built near water.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-i18n="help_modal.build_warship">Warship</td>
|
||||
<td><div class="icon warship-icon"></div></td>
|
||||
<td data-i18n="help_modal.build_warship_desc">
|
||||
Patrols in an area, capturing trade ships and destroying
|
||||
enemy Warships and Boats. Spawns from the nearest Port and
|
||||
patrols the area you first clicked to build it.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-i18n="help_modal.build_silo">Missile Silo</td>
|
||||
<td><div class="icon missile-silo-icon"></div></td>
|
||||
<td data-i18n="help_modal.build_silo_desc">Allows launching missiles.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-i18n="help_modal.build_sam">SAM Launcher</td>
|
||||
<td><div class="icon sam-launcher-icon"></div></td>
|
||||
<td data-i18n="help_modal.build_sam_desc">Has a 75% chance to intercept enemy missiles in it's 100 pixel range.
|
||||
The SAM has a 7.5 second cooldown and can not intercept MIRVs.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-i18n="help_modal.build_atom">Atom Bomb</td>
|
||||
<td><div class="icon atom-bomb-icon"></div></td></td>
|
||||
<td data-i18n="help_modal.build_atom_desc">Small explosive bomb that destroys territory, buildings, ships and boats. Spawns from the nearest Missile Silo and lands in the area you first clicked to build it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-i18n="help_modal.build_hydrogen">Hydrogen Bomb</td>
|
||||
<td><div class="icon hydrogen-bomb-icon"></div></td></td>
|
||||
<td data-i18n="help_modal.build_hydrogen_desc">Large explosive bomb. Spawns from the nearest Missile Silo and lands in the area you first clicked to build it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td data-i18n="help_modal.build_mirv">MIRV</td>
|
||||
<td><div class="icon mirv-icon"></div></td>
|
||||
<td data-i18n="help_modal.build_mirv_desc">The most powerful bomb in the game. Splits up into smaller bombs that will cover a huge range of territory. Only damages the player that you first clicked on to build it. Spawns from the nearest Missile Silo and lands in the area you first clicked to build it.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>${translateText("help_modal.build_city")}</td>
|
||||
<td><div class="icon city-icon"></div></td>
|
||||
<td>${translateText("help_modal.build_city_desc")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>${translateText("help_modal.build_defense")}</td>
|
||||
<td><div class="icon defense-post-icon"></div></td>
|
||||
<td>${translateText("help_modal.build_defense_desc")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>${translateText("help_modal.build_port")}</td>
|
||||
<td><div class="icon port-icon"></div></td>
|
||||
<td>${translateText("help_modal.build_port_desc")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>${translateText("help_modal.build_warship")}</td>
|
||||
<td><div class="icon warship-icon"></div></td>
|
||||
<td>${translateText("help_modal.build_warship_desc")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>${translateText("help_modal.build_silo")}</td>
|
||||
<td><div class="icon missile-silo-icon"></div></td>
|
||||
<td>${translateText("help_modal.build_silo_desc")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>${translateText("help_modal.build_sam")}</td>
|
||||
<td><div class="icon sam-launcher-icon"></div></td>
|
||||
<td>${translateText("help_modal.build_sam_desc")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>${translateText("help_modal.build_atom")}</td>
|
||||
<td><div class="icon atom-bomb-icon"></div></td>
|
||||
<td>${translateText("help_modal.build_atom_desc")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>${translateText("help_modal.build_hydrogen")}</td>
|
||||
<td><div class="icon hydrogen-bomb-icon"></div></td>
|
||||
<td>${translateText("help_modal.build_hydrogen_desc")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>${translateText("help_modal.build_mirv")}</td>
|
||||
<td><div class="icon mirv-icon"></div></td>
|
||||
<td>${translateText("help_modal.build_mirv_desc")}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<hr class="mt-6 mb-4">
|
||||
<hr class="mt-6 mb-4" />
|
||||
|
||||
<div>
|
||||
<div data-i18n="help_modal.player_icons" class="text-2xl font-bold text-center">Player icons</div>
|
||||
<p data-i18n="help_modal.icon_desc">Examples of some of the ingame icons you will encounter and what they mean:</p>
|
||||
<div class="text-2xl font-bold text-center">
|
||||
${translateText("help_modal.player_icons")}
|
||||
</div>
|
||||
<p>${translateText("help_modal.icon_desc")}</p>
|
||||
<div class="flex flex-col md:flex-row gap-4 mt-2">
|
||||
<div class="flex flex-col items-center">
|
||||
<div data-i18n="help_modal.icon_crown" class="text-gray-300">Crown - This is the number 1 player in the leaderboard</div>
|
||||
<img src="/images/helpModal/number1.webp" alt="Number 1 player" title="Number 1 player" />
|
||||
<div class="text-gray-300">
|
||||
${translateText("help_modal.icon_crown")}
|
||||
</div>
|
||||
<img
|
||||
src="/images/helpModal/number1.webp"
|
||||
alt="Number 1 player"
|
||||
title="Number 1 player"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center">
|
||||
<div data-i18n="help_modal.icon_traitor" class="text-gray-300">Crossed swords - Traitor. This player attacked an ally.</div>
|
||||
<img src="/images/helpModal/traitor.webp" alt="Traitor" title="Traitor" />
|
||||
<div class="text-gray-300">
|
||||
${translateText("help_modal.icon_traitor")}
|
||||
</div>
|
||||
<img
|
||||
src="/images/helpModal/traitor.webp"
|
||||
alt="Traitor"
|
||||
title="Traitor"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-center">
|
||||
<div data-i18n="help_modal.icon_ally" class="text-gray-300">Handshake - Ally. This player is your ally.</div>
|
||||
<div class="text-gray-300">
|
||||
${translateText("help_modal.icon_ally")}
|
||||
</div>
|
||||
<img src="/images/helpModal/ally.webp" alt="Ally" title="Ally" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -72,7 +72,7 @@ export class HostLobbyModal extends LitElement {
|
||||
<div class="options-layout">
|
||||
<!-- Map Selection -->
|
||||
<div class="options-section">
|
||||
<div class="option-title">${translateText("host_modal.map")}</div>
|
||||
<div class="option-title">${translateText("map.map")}</div>
|
||||
<div class="option-cards">
|
||||
${Object.entries(GameMapType)
|
||||
.filter(([key]) => isNaN(Number(key)))
|
||||
@@ -110,7 +110,7 @@ export class HostLobbyModal extends LitElement {
|
||||
|
||||
<!-- Difficulty Selection -->
|
||||
<div class="options-section">
|
||||
<div class="option-title">${translateText("host_modal.difficulty")}</div>
|
||||
<div class="option-title">${translateText("difficulty.difficulty")}</div>
|
||||
<div class="option-cards">
|
||||
${Object.entries(Difficulty)
|
||||
.filter(([key]) => isNaN(Number(key)))
|
||||
@@ -186,13 +186,13 @@ export class HostLobbyModal extends LitElement {
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="host-modal-disable-npcs"
|
||||
for="disable-npcs"
|
||||
class="option-card ${this.disableNPCs ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="host-modal-disable-npcs"
|
||||
id="disable-npcs"
|
||||
@change=${this.handleDisableNPCsChange}
|
||||
.checked=${this.disableNPCs}
|
||||
/>
|
||||
@@ -202,13 +202,13 @@ export class HostLobbyModal extends LitElement {
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="host-modal-instant-build"
|
||||
for="instant-build"
|
||||
class="option-card ${this.instantBuild ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="host-modal-instant-build"
|
||||
id="instant-build"
|
||||
@change=${this.handleInstantBuildChange}
|
||||
.checked=${this.instantBuild}
|
||||
/>
|
||||
@@ -218,13 +218,13 @@ export class HostLobbyModal extends LitElement {
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="host-modal-infinite-gold"
|
||||
for="infinite-gold"
|
||||
class="option-card ${this.infiniteGold ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="host-modal-infinite-gold"
|
||||
id="infinite-gold"
|
||||
@change=${this.handleInfiniteGoldChange}
|
||||
.checked=${this.infiniteGold}
|
||||
/>
|
||||
@@ -234,13 +234,13 @@ export class HostLobbyModal extends LitElement {
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="host-modal-infinite-troops"
|
||||
for="infinite-troops"
|
||||
class="option-card ${this.infiniteTroops ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="host-modal-infinite-troops"
|
||||
id="infinite-troops"
|
||||
@change=${this.handleInfiniteTroopsChange}
|
||||
.checked=${this.infiniteTroops}
|
||||
/>
|
||||
@@ -250,13 +250,13 @@ export class HostLobbyModal extends LitElement {
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="host-modal-disable-nukes"
|
||||
for="disable-nukes"
|
||||
class="option-card ${this.disableNukes ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="host-modal-disable-nukes"
|
||||
id="disable-nukes"
|
||||
@change=${this.handleDisableNukesChange}
|
||||
.checked=${this.disableNukes}
|
||||
/>
|
||||
|
||||
+197
-100
@@ -1,42 +1,61 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import "./LanguageModal";
|
||||
|
||||
// Import language files
|
||||
import bgTranslations from "../../resources/lang/bg.json";
|
||||
import deTranslations from "../../resources/lang/de.json";
|
||||
import enTranslations from "../../resources/lang/en.json";
|
||||
import esTranslations from "../../resources/lang/es.json";
|
||||
import frTranslations from "../../resources/lang/fr.json";
|
||||
import jaTranslations from "../../resources/lang/ja.json";
|
||||
import nlTranslations from "../../resources/lang/nl.json";
|
||||
|
||||
const translations = {
|
||||
en: enTranslations,
|
||||
bg: bgTranslations,
|
||||
ja: jaTranslations,
|
||||
fr: frTranslations,
|
||||
nl: nlTranslations,
|
||||
de: deTranslations,
|
||||
es: esTranslations,
|
||||
};
|
||||
|
||||
type Translation = Partial<(typeof translations)[keyof typeof translations]>;
|
||||
import bg from "../../resources/lang/bg.json";
|
||||
import de from "../../resources/lang/de.json";
|
||||
import en from "../../resources/lang/en.json";
|
||||
import es from "../../resources/lang/es.json";
|
||||
import fr from "../../resources/lang/fr.json";
|
||||
import ja from "../../resources/lang/ja.json";
|
||||
import nl from "../../resources/lang/nl.json";
|
||||
import pl from "../../resources/lang/pl.json";
|
||||
import ru from "../../resources/lang/ru.json";
|
||||
import uk from "../../resources/lang/uk.json";
|
||||
|
||||
@customElement("lang-selector")
|
||||
export class LangSelector extends LitElement {
|
||||
@state() public translations: Translation = {};
|
||||
@state() private defaultTranslations = {};
|
||||
@state() public translations: any = {};
|
||||
@state() private defaultTranslations: any = {};
|
||||
@state() private currentLang: string = "en";
|
||||
@state() private languageList: any[] = [];
|
||||
@state() private showModal: boolean = false;
|
||||
@state() private debugMode: boolean = false;
|
||||
|
||||
private dKeyPressed: boolean = false;
|
||||
|
||||
private languageMap: Record<string, any> = {
|
||||
bg,
|
||||
de,
|
||||
en,
|
||||
es,
|
||||
fr,
|
||||
ja,
|
||||
nl,
|
||||
pl,
|
||||
ru,
|
||||
uk,
|
||||
};
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
return this; // Use Light DOM if you prefer this
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.setupDebugKey();
|
||||
this.initializeLanguage();
|
||||
}
|
||||
|
||||
private setupDebugKey() {
|
||||
window.addEventListener("keydown", (e) => {
|
||||
if (e.key.toLowerCase() === "t") this.dKeyPressed = true;
|
||||
});
|
||||
window.addEventListener("keyup", (e) => {
|
||||
if (e.key.toLowerCase() === "t") this.dKeyPressed = false;
|
||||
});
|
||||
}
|
||||
|
||||
private async initializeLanguage() {
|
||||
const locale = new Intl.Locale(navigator.language);
|
||||
const defaultLang = locale.language;
|
||||
@@ -46,21 +65,86 @@ export class LangSelector extends LitElement {
|
||||
this.translations = await this.loadLanguage(userLang);
|
||||
this.currentLang = userLang;
|
||||
|
||||
this.applyTranslation();
|
||||
await this.loadLanguageList();
|
||||
this.applyTranslation(this.translations);
|
||||
}
|
||||
|
||||
private async loadLanguage(lang: string): Promise<Translation> {
|
||||
private async loadLanguage(lang: string): Promise<any> {
|
||||
return Promise.resolve(this.languageMap[lang] || {});
|
||||
}
|
||||
|
||||
private async loadLanguageList() {
|
||||
try {
|
||||
const translation = translations[lang as keyof typeof translations];
|
||||
if (!translation) throw new Error(`Language file not found: ${lang}`);
|
||||
return translation;
|
||||
} catch (error) {
|
||||
console.error("🚨 Translation load error:", error);
|
||||
return {};
|
||||
const data = this.languageMap;
|
||||
let list: any[] = [];
|
||||
|
||||
const browserLang = new Intl.Locale(navigator.language).language;
|
||||
|
||||
for (const langCode of Object.keys(data)) {
|
||||
const langData = data[langCode].lang;
|
||||
if (!langData) continue;
|
||||
|
||||
list.push({
|
||||
code: langData.lang_code ?? langCode,
|
||||
native: langData.native ?? langCode,
|
||||
en: langData.en ?? langCode,
|
||||
svg: langData.svg ?? langCode,
|
||||
});
|
||||
}
|
||||
|
||||
let debugLang: any = null;
|
||||
if (this.dKeyPressed) {
|
||||
debugLang = {
|
||||
code: "debug",
|
||||
native: "Debug",
|
||||
en: "Debug",
|
||||
svg: "xx",
|
||||
};
|
||||
this.debugMode = true;
|
||||
}
|
||||
|
||||
const currentLangEntry = list.find((l) => l.code === this.currentLang);
|
||||
const browserLangEntry =
|
||||
browserLang !== this.currentLang && browserLang !== "en"
|
||||
? list.find((l) => l.code === browserLang)
|
||||
: undefined;
|
||||
const englishEntry =
|
||||
this.currentLang !== "en"
|
||||
? list.find((l) => l.code === "en")
|
||||
: undefined;
|
||||
|
||||
list = list.filter(
|
||||
(l) =>
|
||||
l.code !== this.currentLang &&
|
||||
l.code !== browserLang &&
|
||||
l.code !== "en" &&
|
||||
l.code !== "debug",
|
||||
);
|
||||
|
||||
list.sort((a, b) => a.en.localeCompare(b.en));
|
||||
|
||||
const finalList: any[] = [];
|
||||
if (currentLangEntry) finalList.push(currentLangEntry);
|
||||
if (englishEntry) finalList.push(englishEntry);
|
||||
if (browserLangEntry) finalList.push(browserLangEntry);
|
||||
finalList.push(...list);
|
||||
if (debugLang) finalList.push(debugLang);
|
||||
|
||||
this.languageList = finalList;
|
||||
} catch (err) {
|
||||
console.error("Failed to load language list:", err);
|
||||
}
|
||||
}
|
||||
|
||||
private applyTranslation() {
|
||||
private async changeLanguage(lang: string) {
|
||||
localStorage.setItem("lang", lang);
|
||||
this.translations = await this.loadLanguage(lang);
|
||||
this.currentLang = lang;
|
||||
this.applyTranslation(this.translations);
|
||||
this.showModal = false;
|
||||
}
|
||||
|
||||
private applyTranslation(translations: any) {
|
||||
const components = [
|
||||
"single-player-modal",
|
||||
"host-lobby-modal",
|
||||
@@ -75,32 +159,44 @@ export class LangSelector extends LitElement {
|
||||
"help-modal",
|
||||
"username-input",
|
||||
"public-lobby",
|
||||
"o-modal",
|
||||
"o-button",
|
||||
];
|
||||
|
||||
const main = this.translations.main;
|
||||
if (main && "title" in main) {
|
||||
document.title = main.title;
|
||||
}
|
||||
document.title = translations.main?.title || document.title;
|
||||
|
||||
document.querySelectorAll("[data-i18n]").forEach((element) => {
|
||||
const key = element.getAttribute("data-i18n");
|
||||
const text = this.translateText(key);
|
||||
const keys = key?.split(".") || [];
|
||||
let text = translations;
|
||||
|
||||
for (const k of keys) {
|
||||
text = text?.[k];
|
||||
if (!text) break;
|
||||
}
|
||||
|
||||
if (!text && this.defaultTranslations) {
|
||||
let fallback = this.defaultTranslations;
|
||||
for (const k of keys) {
|
||||
fallback = fallback?.[k];
|
||||
if (!fallback) break;
|
||||
}
|
||||
text = fallback;
|
||||
}
|
||||
|
||||
if (text) {
|
||||
element.innerHTML = text;
|
||||
} else {
|
||||
console.warn(`Missing translation key: ${key}`);
|
||||
console.warn(`Translation key not found: ${key}`);
|
||||
}
|
||||
});
|
||||
|
||||
components.forEach((tagName) => {
|
||||
const el = document.querySelector(tagName) as LitElement;
|
||||
if (el && typeof el.requestUpdate === "function") {
|
||||
el.requestUpdate();
|
||||
} else {
|
||||
console.warn(
|
||||
`requestUpdate() not available on <${tagName}> or element not found.`,
|
||||
);
|
||||
}
|
||||
components.forEach((tag) => {
|
||||
document.querySelectorAll(tag).forEach((el) => {
|
||||
if (typeof (el as any).requestUpdate === "function") {
|
||||
(el as any).requestUpdate();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -109,13 +205,19 @@ export class LangSelector extends LitElement {
|
||||
params: Record<string, string | number> = {},
|
||||
): string {
|
||||
const keys = key.split(".");
|
||||
let text = findTranslation(keys, this.translations);
|
||||
if (!text && this.defaultTranslations) {
|
||||
text = findTranslation(keys, this.defaultTranslations);
|
||||
let text: any = this.translations;
|
||||
|
||||
for (const k of keys) {
|
||||
text = text?.[k];
|
||||
if (!text) break;
|
||||
}
|
||||
|
||||
if (text == null || typeof text !== "string") {
|
||||
return null;
|
||||
if (!text && this.defaultTranslations) {
|
||||
text = this.defaultTranslations;
|
||||
for (const k of keys) {
|
||||
text = text?.[k];
|
||||
if (!text) return key;
|
||||
}
|
||||
}
|
||||
|
||||
for (const [param, value] of Object.entries(params)) {
|
||||
@@ -125,58 +227,53 @@ export class LangSelector extends LitElement {
|
||||
return text;
|
||||
}
|
||||
|
||||
private async changeLanguage(lang: string) {
|
||||
localStorage.setItem("lang", lang);
|
||||
this.translations = await this.loadLanguage(lang);
|
||||
this.currentLang = lang;
|
||||
this.applyTranslation();
|
||||
private openModal() {
|
||||
this.debugMode = this.dKeyPressed;
|
||||
this.showModal = true;
|
||||
this.loadLanguageList();
|
||||
}
|
||||
|
||||
render() {
|
||||
const currentLang =
|
||||
this.languageList.find((l) => l.code === this.currentLang) ??
|
||||
(this.currentLang === "debug"
|
||||
? {
|
||||
code: "debug",
|
||||
native: "Debug",
|
||||
en: "Debug",
|
||||
svg: "xx",
|
||||
}
|
||||
: {
|
||||
native: "English",
|
||||
en: "English",
|
||||
svg: "xx",
|
||||
});
|
||||
|
||||
return html`
|
||||
<select
|
||||
@change=${(e: Event) =>
|
||||
this.changeLanguage((e.target as HTMLSelectElement).value)}
|
||||
class="text-center appearance-none w-full bg-blue-100 hover:bg-blue-200 text-blue-900 p-3 sm:p-4 lg:p-5 font-medium text-sm sm:text-base lg:text-lg rounded-md border-none cursor-pointer transition-colors duration-300"
|
||||
>
|
||||
<option value="en" ?selected=${this.currentLang === "en"}>
|
||||
English
|
||||
</option>
|
||||
<option value="bg" ?selected=${this.currentLang === "bg"}>
|
||||
Български
|
||||
</option>
|
||||
<option value="ja" ?selected=${this.currentLang === "ja"}>
|
||||
日本語
|
||||
</option>
|
||||
<option value="fr" ?selected=${this.currentLang === "fr"}>
|
||||
Français
|
||||
</option>
|
||||
<option value="nl" ?selected=${this.currentLang === "nl"}>
|
||||
Nederlands
|
||||
</option>
|
||||
<option value="de" ?selected=${this.currentLang === "de"}>
|
||||
Deutsch
|
||||
</option>
|
||||
<option value="es" ?selected=${this.currentLang === "es"}>
|
||||
Español
|
||||
</option>
|
||||
</select>
|
||||
<div class="container__row">
|
||||
<button
|
||||
id="lang-selector"
|
||||
@click=${this.openModal}
|
||||
class="text-center appearance-none w-full bg-blue-100 hover:bg-blue-200 text-blue-900 p-3 sm:p-4 lg:p-5 font-medium text-sm sm:text-base lg:text-lg rounded-md border-none cursor-pointer transition-colors duration-300 flex items-center gap-2 justify-center"
|
||||
>
|
||||
<img
|
||||
id="lang-flag"
|
||||
class="w-6 h-4"
|
||||
src="/flags/${currentLang.svg}.svg"
|
||||
alt="flag"
|
||||
/>
|
||||
<span id="lang-name">${currentLang.native} (${currentLang.en})</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<language-modal
|
||||
.visible=${this.showModal}
|
||||
.languageList=${this.languageList}
|
||||
.currentLang=${this.currentLang}
|
||||
@language-selected=${(e: CustomEvent) =>
|
||||
this.changeLanguage(e.detail.lang)}
|
||||
@close-modal=${() => (this.showModal = false)}
|
||||
></language-modal>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
function findTranslation(
|
||||
keys: string[],
|
||||
translations: Translation,
|
||||
): string | null {
|
||||
let ptr: unknown = translations;
|
||||
for (const k of keys) {
|
||||
ptr = ptr?.[k];
|
||||
if (!ptr) break;
|
||||
}
|
||||
if (ptr && typeof ptr === "string") {
|
||||
return ptr;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("language-modal")
|
||||
export class LanguageModal extends LitElement {
|
||||
@property({ type: Boolean }) visible = false;
|
||||
@property({ type: Array }) languageList: any[] = [];
|
||||
@property({ type: String }) currentLang = "en";
|
||||
|
||||
static styles = css`
|
||||
.modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 50;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.modal-content {
|
||||
background: white;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 10px 15px rgba(0, 0, 0, 0.2);
|
||||
padding: 1.5rem;
|
||||
width: 24rem;
|
||||
max-width: 100%;
|
||||
max-height: 90vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.language-list {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.lang-button {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
border-radius: 0.375rem;
|
||||
transition: background-color 0.3s;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.lang-button:hover {
|
||||
background-color: #ebf8ff;
|
||||
}
|
||||
|
||||
.lang-button.active {
|
||||
background-color: #bee3f8;
|
||||
}
|
||||
|
||||
.flag-icon {
|
||||
width: 24px;
|
||||
height: 16px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.close-button {
|
||||
background-color: #3182ce;
|
||||
color: white;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.375rem;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.close-button:hover {
|
||||
background-color: #2b6cb0;
|
||||
}
|
||||
`;
|
||||
|
||||
private selectLanguage(lang: string) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("language-selected", {
|
||||
detail: { lang },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
updated(changedProps: Map<string, unknown>) {
|
||||
if (changedProps.has("visible")) {
|
||||
if (this.visible) {
|
||||
document.body.style.overflow = "hidden";
|
||||
} else {
|
||||
document.body.style.overflow = "auto";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.body.style.overflow = "auto";
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="modal ${this.visible ? "" : "hidden"}">
|
||||
<div class="modal-content">
|
||||
<h2 class="text-xl font-semibold mb-4">Select Language</h2>
|
||||
|
||||
<div class="language-list">
|
||||
${this.languageList.map((lang) => {
|
||||
const isActive = this.currentLang === lang.code;
|
||||
return html`
|
||||
<button
|
||||
class="lang-button ${isActive ? "active" : ""}"
|
||||
@click=${() => this.selectLanguage(lang.code)}
|
||||
>
|
||||
<img
|
||||
src="/flags/${lang.svg}.svg"
|
||||
class="flag-icon"
|
||||
alt="${lang.code}"
|
||||
/>
|
||||
<span>${lang.native} (${lang.en})</span>
|
||||
</button>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="close-button"
|
||||
@click=${() =>
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("close-modal", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
)}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import { HostLobbyModal as HostPrivateLobbyModal } from "./HostLobbyModal";
|
||||
import { JoinPrivateLobbyModal } from "./JoinPrivateLobbyModal";
|
||||
import "./LangSelector";
|
||||
import { LangSelector } from "./LangSelector";
|
||||
import { LanguageModal } from "./LanguageModal";
|
||||
import "./PublicLobby";
|
||||
import { PublicLobby } from "./PublicLobby";
|
||||
import { SinglePlayerModal } from "./SinglePlayerModal";
|
||||
@@ -56,9 +57,15 @@ class Client {
|
||||
const langSelector = document.querySelector(
|
||||
"lang-selector",
|
||||
) as LangSelector;
|
||||
const LanguageModal = document.querySelector(
|
||||
"lang-selector",
|
||||
) as LanguageModal;
|
||||
if (!langSelector) {
|
||||
consolex.warn("Lang selector element not found");
|
||||
}
|
||||
if (!LanguageModal) {
|
||||
consolex.warn("Language modal element not found");
|
||||
}
|
||||
|
||||
this.flagInput = document.querySelector("flag-input") as FlagInput;
|
||||
if (!this.flagInput) {
|
||||
|
||||
@@ -35,7 +35,7 @@ export class SinglePlayerModal extends LitElement {
|
||||
<div class="options-layout">
|
||||
<!-- Map Selection -->
|
||||
<div class="options-section">
|
||||
<div class="option-title">${translateText("single_modal.map")}</div>
|
||||
<div class="option-title">${translateText("map.map")}</div>
|
||||
<div class="option-cards">
|
||||
${Object.entries(GameMapType)
|
||||
.filter(([key]) => isNaN(Number(key)))
|
||||
@@ -80,7 +80,7 @@ export class SinglePlayerModal extends LitElement {
|
||||
<!-- Difficulty Selection -->
|
||||
<div class="options-section">
|
||||
<div class="option-title">
|
||||
${translateText("single_modal.difficulty")}
|
||||
${translateText("difficulty.difficulty")}
|
||||
</div>
|
||||
<div class="option-cards">
|
||||
${Object.entries(Difficulty)
|
||||
@@ -160,13 +160,13 @@ export class SinglePlayerModal extends LitElement {
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="disable-npcs"
|
||||
for="singleplayer-modal-disable-npcs"
|
||||
class="option-card ${this.disableNPCs ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="disable-npcs"
|
||||
id="singleplayer-modal-disable-npcs"
|
||||
@change=${this.handleDisableNPCsChange}
|
||||
.checked=${this.disableNPCs}
|
||||
/>
|
||||
@@ -175,13 +175,13 @@ export class SinglePlayerModal extends LitElement {
|
||||
</div>
|
||||
</label>
|
||||
<label
|
||||
for="instant-build"
|
||||
for="singleplayer-modal-instant-build"
|
||||
class="option-card ${this.instantBuild ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="instant-build"
|
||||
id="singleplayer-modal-instant-build"
|
||||
@change=${this.handleInstantBuildChange}
|
||||
.checked=${this.instantBuild}
|
||||
/>
|
||||
@@ -191,13 +191,13 @@ export class SinglePlayerModal extends LitElement {
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="infinite-gold"
|
||||
for="singleplayer-modal-infinite-gold"
|
||||
class="option-card ${this.infiniteGold ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="infinite-gold"
|
||||
id="singleplayer-modal-infinite-gold"
|
||||
@change=${this.handleInfiniteGoldChange}
|
||||
.checked=${this.infiniteGold}
|
||||
/>
|
||||
@@ -207,13 +207,13 @@ export class SinglePlayerModal extends LitElement {
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="infinite-troops"
|
||||
for="singleplayer-modal-infinite-troops"
|
||||
class="option-card ${this.infiniteTroops ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="infinite-troops"
|
||||
id="singleplayer-modal-infinite-troops"
|
||||
@change=${this.handleInfiniteTroopsChange}
|
||||
.checked=${this.infiniteTroops}
|
||||
/>
|
||||
@@ -223,13 +223,13 @@ export class SinglePlayerModal extends LitElement {
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="disable-nukes"
|
||||
for="singleplayer-modal-disable-nukes"
|
||||
class="option-card ${this.disableNukes ? "selected" : ""}"
|
||||
>
|
||||
<div class="checkbox-icon"></div>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="disable-nukes"
|
||||
id="singleplayer-modal-disable-nukes"
|
||||
@change=${this.handleDisableNukesChange}
|
||||
.checked=${this.disableNukes}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
import { classMap } from "lit/directives/class-map.js";
|
||||
import { translateText } from "../../Utils";
|
||||
|
||||
@customElement("o-button")
|
||||
export class OButton extends LitElement {
|
||||
@@ -18,7 +19,6 @@ export class OButton extends LitElement {
|
||||
render() {
|
||||
return html`
|
||||
<button
|
||||
data-i18n="${this.translationKey}"
|
||||
class=${classMap({
|
||||
"c-button": true,
|
||||
"c-button--block": this.block,
|
||||
@@ -28,7 +28,9 @@ export class OButton extends LitElement {
|
||||
})}
|
||||
?disabled=${this.disable}
|
||||
>
|
||||
${this.title}
|
||||
${`${this.translationKey}` === ""
|
||||
? `${this.title}`
|
||||
: `${translateText(this.translationKey)}`}
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { translateText } from "../../Utils";
|
||||
|
||||
@customElement("o-modal")
|
||||
export class OModal extends LitElement {
|
||||
@@ -74,11 +75,10 @@ export class OModal extends LitElement {
|
||||
? html`
|
||||
<aside class="c-modal">
|
||||
<div class="c-modal__wrapper">
|
||||
<header
|
||||
class="c-modal__header"
|
||||
data-i18n="${this.translationKey}"
|
||||
>
|
||||
${this.title}
|
||||
<header class="c-modal__header">
|
||||
${`${this.translationKey}` === ""
|
||||
? `${this.title}`
|
||||
: `${translateText(this.translationKey)}`}
|
||||
<div class="c-modal__close" @click=${this.close}>X</div>
|
||||
</header>
|
||||
<section class="c-modal__content">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { translateText } from "../client/Utils";
|
||||
|
||||
@customElement("game-starting-modal")
|
||||
export class GameStartingModal extends LitElement {
|
||||
@@ -88,8 +89,8 @@ export class GameStartingModal extends LitElement {
|
||||
render() {
|
||||
return html`
|
||||
<div class="modal ${this.isVisible ? "visible" : ""}">
|
||||
<h2>Game is Starting...</h2>
|
||||
<p>Preparing for the lobby to start. Please wait.</p>
|
||||
<h2>${translateText("game_starting_modal.title")}</h2>
|
||||
<p>${translateText("game_starting_modal.desc")}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -339,6 +339,24 @@
|
||||
<player-panel></player-panel>
|
||||
<help-modal></help-modal>
|
||||
<dark-mode-button></dark-mode-button>
|
||||
<div
|
||||
id="language-modal"
|
||||
class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden flex justify-center items-center"
|
||||
>
|
||||
<div class="bg-white rounded-lg shadow-lg p-6 w-96 max-w-full">
|
||||
<h2 class="text-xl font-semibold mb-4">Select Language</h2>
|
||||
<div
|
||||
id="language-list"
|
||||
class="space-y-2 max-h-80 overflow-y-auto"
|
||||
></div>
|
||||
<button
|
||||
class="mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded"
|
||||
onclick="document.getElementById('language-modal').classList.add('hidden')"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Scripts -->
|
||||
<script>
|
||||
// Remove preload class after everything is loaded
|
||||
|
||||
Reference in New Issue
Block a user