diff --git a/resources/images/DarkModeIconWhite.svg b/resources/images/DarkModeIconWhite.svg new file mode 100644 index 000000000..dfaa4880e --- /dev/null +++ b/resources/images/DarkModeIconWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/EmojiIconWhite.svg b/resources/images/EmojiIconWhite.svg index f60610a35..08da8d0f5 100644 --- a/resources/images/EmojiIconWhite.svg +++ b/resources/images/EmojiIconWhite.svg @@ -1,258 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/resources/images/ExitIconWhite.svg b/resources/images/ExitIconWhite.svg new file mode 100644 index 000000000..7a963caa1 --- /dev/null +++ b/resources/images/ExitIconWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/ExplosionIconWhite.svg b/resources/images/ExplosionIconWhite.svg new file mode 100644 index 000000000..8eb4bd102 --- /dev/null +++ b/resources/images/ExplosionIconWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/FocusIconWhite.svg b/resources/images/FocusIconWhite.svg new file mode 100644 index 000000000..d198044a6 --- /dev/null +++ b/resources/images/FocusIconWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/InfoIconRegularWhite.svg b/resources/images/InfoIconRegularWhite.svg new file mode 100644 index 000000000..134c3e502 --- /dev/null +++ b/resources/images/InfoIconRegularWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/InfoIconSolidWhite.svg b/resources/images/InfoIconSolidWhite.svg new file mode 100644 index 000000000..f932784ec --- /dev/null +++ b/resources/images/InfoIconSolidWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/LeaderboardIconRegularWhite.svg b/resources/images/LeaderboardIconRegularWhite.svg new file mode 100644 index 000000000..173cd60cb --- /dev/null +++ b/resources/images/LeaderboardIconRegularWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/LeaderboardIconSolidWhite.svg b/resources/images/LeaderboardIconSolidWhite.svg new file mode 100644 index 000000000..8926616c2 --- /dev/null +++ b/resources/images/LeaderboardIconSolidWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/MouseIconWhite.svg b/resources/images/MouseIconWhite.svg new file mode 100644 index 000000000..fcbe98b5d --- /dev/null +++ b/resources/images/MouseIconWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/NinjaIconWhite.svg b/resources/images/NinjaIconWhite.svg new file mode 100644 index 000000000..e46699b7e --- /dev/null +++ b/resources/images/NinjaIconWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/PauseIconWhite.svg b/resources/images/PauseIconWhite.svg new file mode 100644 index 000000000..e803fcb7e --- /dev/null +++ b/resources/images/PauseIconWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/PlayIconWhite.svg b/resources/images/PlayIconWhite.svg new file mode 100644 index 000000000..773dd690e --- /dev/null +++ b/resources/images/PlayIconWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/PopulationIconSolidWhite.svg b/resources/images/PopulationIconSolidWhite.svg new file mode 100644 index 000000000..afe475e60 --- /dev/null +++ b/resources/images/PopulationIconSolidWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/ReplayRegularIconWhite.svg b/resources/images/ReplayRegularIconWhite.svg new file mode 100644 index 000000000..10a1f799d --- /dev/null +++ b/resources/images/ReplayRegularIconWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/ReplaySolidIconWhite.svg b/resources/images/ReplaySolidIconWhite.svg new file mode 100644 index 000000000..2e731ccf3 --- /dev/null +++ b/resources/images/ReplaySolidIconWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/SamLauncherUnitWhite.png b/resources/images/SamLauncherUnitWhite.png new file mode 100644 index 000000000..5a282e98d Binary files /dev/null and b/resources/images/SamLauncherUnitWhite.png differ diff --git a/resources/images/TeamIconRegularWhite.svg b/resources/images/TeamIconRegularWhite.svg new file mode 100644 index 000000000..c6c2645f3 --- /dev/null +++ b/resources/images/TeamIconRegularWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/TeamIconSolidWhite.svg b/resources/images/TeamIconSolidWhite.svg new file mode 100644 index 000000000..10eca9b55 --- /dev/null +++ b/resources/images/TeamIconSolidWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/TreeIconWhite.svg b/resources/images/TreeIconWhite.svg new file mode 100644 index 000000000..c30908a51 --- /dev/null +++ b/resources/images/TreeIconWhite.svg @@ -0,0 +1 @@ + diff --git a/resources/images/TroopIconWhite.svg b/resources/images/TroopIconWhite.svg new file mode 100644 index 000000000..5f1d53bd1 --- /dev/null +++ b/resources/images/TroopIconWhite.svg @@ -0,0 +1,4 @@ + + + diff --git a/resources/images/WorkerIconBlack.svg b/resources/images/WorkerIconBlack.svg new file mode 100644 index 000000000..3852227a4 --- /dev/null +++ b/resources/images/WorkerIconBlack.svg @@ -0,0 +1 @@ + diff --git a/resources/images/WorkerIconWhite.svg b/resources/images/WorkerIconWhite.svg new file mode 100644 index 000000000..5129642bc --- /dev/null +++ b/resources/images/WorkerIconWhite.svg @@ -0,0 +1,4 @@ + + + diff --git a/resources/lang/ar.json b/resources/lang/ar.json index 68492030d..1c608031c 100644 --- a/resources/lang/ar.json +++ b/resources/lang/ar.json @@ -214,7 +214,7 @@ "emojis_desc": "تبديل ما إذا كانت الرموز التعبيرية تظهر في اللعبة", "anonymous_names_label": "🥷 الأسماء المخفية", "anonymous_names_desc": "إخفاء أسماء اللاعبين الحقيقية باستخدام أسماء عشوائية على شاشتك", - "left_click_label": "🖱️ النقر الأيسر لفتح القائمة", + "left_click_label": " النقر الأيسر لفتح القائمة", "left_click_desc": "عند التفعيل، النقر الأيسر يفتح القائمة وزر السيف للهجوم. عند التعطيل، النقر الأيسر يهاجم مباشرة", "attack_ratio_label": "⚔️ نسبة الهجوم", "attack_ratio_desc": "نسبة القوات المُرسلة في الهجوم (1–100%)", diff --git a/resources/lang/bg.json b/resources/lang/bg.json index 5889cbf2e..ced1fcb61 100644 --- a/resources/lang/bg.json +++ b/resources/lang/bg.json @@ -230,7 +230,7 @@ "special_effects_desc": "Превключване на специалните ефекти. Деактивиране, за да се увеличи производителността", "anonymous_names_label": "🥷 Скрити имена", "anonymous_names_desc": "Скриване на истинските имена на играчите с произволни такива на екрана ти.", - "left_click_label": "🖱️ Щтракване на ляв бутон, за да се отвори менюто", + "left_click_label": "Щтракване на ляв бутон, за да се отвори менюто", "left_click_desc": "Когато е ВКЛЮЧЕНО, щракването с ляв бутон отваря менюто и атаките се извършват чрез бутона на меч. Когато е ИЗКЛЮЧЕНО, щракването с ляв бутон атакува директно.", "attack_ratio_label": "⚔️ Съотношение на атака", "attack_ratio_desc": "Какъв процент от Вашите войници да се изпратят в атака (1–100%)", diff --git a/resources/lang/cs.json b/resources/lang/cs.json index bc3e827ef..2f7410f20 100644 --- a/resources/lang/cs.json +++ b/resources/lang/cs.json @@ -230,7 +230,7 @@ "special_effects_desc": "Určuje zda jsou zapnuté speciální efekty. Deaktivuj pro zlepšení výkonu", "anonymous_names_label": "🥷 Skrýt jména", "anonymous_names_desc": "Skrýt skutečná jména hráčů a nahradit je náhodnými názvy na vaší obrazovce.", - "left_click_label": "🖱️ Levým tlačítkem myši Otevřít Menu", + "left_click_label": "Levým tlačítkem myši Otevřít Menu", "left_click_desc": "Pokud je zapnuto otevře \"Levé tlačítko\" myši menu a \"Útok\" tlačítko zaútočí. Pokud je vypnuto, útočí se přímo levým tlačítkem myši.", "attack_ratio_label": "⚔️ Poměr útoku", "attack_ratio_desc": "Jaké procento tvých vojáků poslat při útoku (1–100%)", diff --git a/resources/lang/en.json b/resources/lang/en.json index 9191490b4..244d1f426 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -247,17 +247,17 @@ "title": "User Settings", "tab_basic": "Basic Settings", "tab_keybinds": "Keybinds", - "dark_mode_label": "🌙 Dark Mode", + "dark_mode_label": "Dark Mode", "dark_mode_desc": "Toggle the site’s appearance between light and dark themes", - "emojis_label": "😊 Emojis", + "emojis_label": "Emojis", "emojis_desc": "Toggle whether emojis are shown in game", - "alert_frame_label": "🚨 Alert Frame", + "alert_frame_label": "Alert Frame", "alert_frame_desc": "Toggle the alert frame. When enabled, the frame will be displayed when you are betrayed.", - "special_effects_label": "💥 Special effects", + "special_effects_label": "Special effects", "special_effects_desc": "Toggle special effects. Deactivate to improve performances", - "anonymous_names_label": "🥷 Hidden Names", + "anonymous_names_label": "Hidden Names", "anonymous_names_desc": "Hide real player names with random ones on your screen.", - "left_click_label": "🖱️ Left Click to Open Menu", + "left_click_label": "Left Click to Open Menu", "left_click_desc": "When ON, left-click opens menu and sword button attacks. When OFF, left-click attacks directly.", "attack_ratio_label": "⚔️ Attack Ratio", "attack_ratio_desc": "What percentage of your troops to send in an attack (1–100%)", diff --git a/resources/lang/eo.json b/resources/lang/eo.json index 937c035e7..fbd9077cd 100644 --- a/resources/lang/eo.json +++ b/resources/lang/eo.json @@ -230,7 +230,7 @@ "special_effects_desc": "Baskuli specialajn efektojn. Malaktivigu por plibonigi funkciadon", "anonymous_names_label": "🥷 Maskitaj Nomoj", "anonymous_names_desc": "Kaŝi verajn uzantnomojn kun hazardaj sur via ekrano.", - "left_click_label": "🖱️Maldekstra alklako por malfermi menuon", + "left_click_label": "Maldekstra alklako por malfermi menuon", "left_click_desc": "Kiam aktiviga, maldekstra alklako malfermas menuon kaj glava atakbutono. Kiam malaktiviga, maldekstra alklako atakas direkten.", "attack_ratio_label": "⚔️ Atakkvociento", "attack_ratio_desc": "Kian procenton de viaj trupoj sendi en atako (1–100%)", diff --git a/resources/lang/es.json b/resources/lang/es.json index 5ff191327..93c877be5 100644 --- a/resources/lang/es.json +++ b/resources/lang/es.json @@ -230,7 +230,7 @@ "special_effects_desc": "Activa los efectos especiales. Desactiva para mejorar el rendimiento", "anonymous_names_label": "🥷 Nombres ocultos", "anonymous_names_desc": "Oculta los nombres de jugadores reales con nombres aleatorios en tu pantalla.", - "left_click_label": "🖱️ Clic izquierdo para abrir el menú", + "left_click_label": "Clic izquierdo para abrir el menú", "left_click_desc": "Cuando está activado, el clic izquierdo abre el menú y el botón de espada ataca. Cuando está desactivado, el clic izquierdo ataca directamente.", "attack_ratio_label": "⚔️ Ratio de ataque", "attack_ratio_desc": "Porcentaje de tus tropas que se envían en un ataque (1–100%)", diff --git a/resources/lang/fr.json b/resources/lang/fr.json index 4514eaf13..d6edbf01b 100644 --- a/resources/lang/fr.json +++ b/resources/lang/fr.json @@ -230,7 +230,7 @@ "special_effects_desc": "Activer/désactiver les effets spéciaux. Désactiver pour améliorer les performances", "anonymous_names_label": "🥷 Noms masqués", "anonymous_names_desc": "Cacher le vrai nom des joueurs avec des noms aléatoires sur votre écran.", - "left_click_label": "🖱️ Clic gauche pour ouvrir le menu", + "left_click_label": "Clic gauche pour ouvrir le menu", "left_click_desc": "Activé, un clic gauche ouvre le menu et le bouton épée d'attaque. Désactivé, un clic gauche attaque directement.", "attack_ratio_label": "⚔️ Ratio d'attaque", "attack_ratio_desc": "Quel pourcentage de vos troupes envoyer dans une attaque (1–100%)", diff --git a/resources/lang/he.json b/resources/lang/he.json index 87f1c4845..407b73ba3 100644 --- a/resources/lang/he.json +++ b/resources/lang/he.json @@ -230,7 +230,7 @@ "special_effects_desc": "מאפשר הצגה של אפקטים מיוחדים. בטל כדי לשפר את הביצועים של המשחק.", "anonymous_names_label": "🥷 שמות חבויים", "anonymous_names_desc": "החלף את שמות השחקנים האמיתיים עם שמות רנדומליים במסך שלך.", - "left_click_label": "לחצן שמאלי לפתיחת תפריט 🖱️", + "left_click_label": "לחצן שמאלי לפתיחת תפריט", "left_click_desc": "כאשר מופעל, הלחצן השמאלי פותח את התפריט וכפתור החרב תוקף. כאשר מכובה, הלחצן השמאלי תוקף ישירות.", "attack_ratio_label": "יחס תקיפה ⚔️", "attack_ratio_desc": "כמה אחוז מהחיילים שלך לשלוח בהתקפה (1-100%)", diff --git a/resources/lang/it.json b/resources/lang/it.json index 65d2e181b..555aec201 100644 --- a/resources/lang/it.json +++ b/resources/lang/it.json @@ -230,7 +230,7 @@ "special_effects_desc": "Attiva/disattiva gli effetti speciali. Disattiva per migliorare le prestazioni", "anonymous_names_label": "🥷 Nomi Nascosti", "anonymous_names_desc": "Nascondi i nomi dei giocatori reali sullo schermo con nomi casuali.", - "left_click_label": "🖱️ Clic sinistro per aprire il menu", + "left_click_label": "Clic sinistro per aprire il menu", "left_click_desc": "Quando attivo, il clic sinistro apre il menu e il pulsante di attacco. Quando non attivo, fare clic con il tasto sinistro attaccherà direttamente.", "attack_ratio_label": "⚔️ Rapporto Di Attacco", "attack_ratio_desc": "Quale percentuale delle tue truppe inviare in attacco (1–100%)", diff --git a/resources/lang/nl.json b/resources/lang/nl.json index 270537185..248d8b5cf 100644 --- a/resources/lang/nl.json +++ b/resources/lang/nl.json @@ -230,7 +230,7 @@ "special_effects_desc": "Speciale effecten aanzetten. Zet uit om de prestaties van het spel te verbeteren", "anonymous_names_label": "🥷 Verborgen namen", "anonymous_names_desc": "Vervang echte spelersnamen door willekeurige namen op je scherm.", - "left_click_label": "🖱️ Linkermuisknop voor openen menu", + "left_click_label": "Linkermuisknop voor openen menu", "left_click_desc": "Als AAN: linkermuisknop opent het Radiale menu met zwaard-aanvalsknop. Als UIT: linkermuisknop opent direct de aanval.", "attack_ratio_label": "⚔️ Aanvalsverhouding", "attack_ratio_desc": "Welk percentage van je troepen je bij een aanval stuurt (1-100%)", diff --git a/resources/lang/pl.json b/resources/lang/pl.json index f5bf6b993..92d9b2cfb 100644 --- a/resources/lang/pl.json +++ b/resources/lang/pl.json @@ -214,7 +214,7 @@ "emojis_desc": "Przełącz, czy emotki mają być wyświetlane w grze", "anonymous_names_label": "🥷 Ukryte Nazwy", "anonymous_names_desc": "Ukryj prawdziwe nazwy graczy, zastępując je losowymi na swoim ekranie.", - "left_click_label": "🖱️ Kliknij lewym przyciskiem myszy aby otworzyć Menu", + "left_click_label": "Kliknij lewym przyciskiem myszy aby otworzyć Menu", "left_click_desc": "Gdy WŁĄCZONE, lewy przycisk myszy otwiera menu, a przycisk z mieczem atakuje. Gdy WYŁĄCZONE, lewy przycisk myszy atakuje bezpośrednio.", "attack_ratio_label": "⚔️ Współczynnik ataku", "attack_ratio_desc": "Jaki procent swoich żołnierzy chcesz wysłać do ataku (1–100%)", diff --git a/resources/lang/ru.json b/resources/lang/ru.json index 3bb5995b9..6da44d1f3 100644 --- a/resources/lang/ru.json +++ b/resources/lang/ru.json @@ -230,7 +230,7 @@ "special_effects_desc": "Переключить специальные эффекты. Отключите для улучшения производительности", "anonymous_names_label": "🥷 Скрытые имена", "anonymous_names_desc": "Скрыть настоящие имена игроков и заменить их случайными.", - "left_click_label": "🖱️ Открытие меню левой кнопкой мыши", + "left_click_label": "Открытие меню левой кнопкой мыши", "left_click_desc": "ВКЛЮЧЕНО: щелчок левой кнопкой мыши открывает меню, атака совершается кнопкой с мечом. ВЫКЛЮЧЕНО: нажатие левой кнопкой мыши совершает атаку напрямую.", "attack_ratio_label": "⚔️ Соотношение атаки", "attack_ratio_desc": "Какой процент ваших войск отправлять в бой (1–100%)", diff --git a/resources/lang/tp.json b/resources/lang/tp.json index c27219372..5cfeb1bbe 100644 --- a/resources/lang/tp.json +++ b/resources/lang/tp.json @@ -214,7 +214,7 @@ "emojis_desc": "o ante e lon pi sitelen Emosi", "anonymous_names_label": "🥷 nimi len", "anonymous_names_desc": "o len e nimi lon pi jan musi kepeken nimi nasa lon musi sina", - "left_click_label": "🖱️ ilo luka soto li open e lipu ken", + "left_click_label": "ilo luka soto li open e lipu ken", "left_click_desc": "ni li lon la ilo luka soto li open e lipu ken la ilo luka utala li utala. ni li pini la ilo luka soto li utala taso.", "attack_ratio_label": "⚔️ nanpa utala", "attack_ratio_desc": "sina utala kepeken kulupu utala la o pana e kulupu suli ni (kipisi wan tawa wan ale)", diff --git a/resources/lang/uk.json b/resources/lang/uk.json index 5f2f95a63..e54eabec3 100644 --- a/resources/lang/uk.json +++ b/resources/lang/uk.json @@ -230,7 +230,7 @@ "special_effects_desc": "Перемкнути спеціальні ефекти. Вимкніть для поліпшення продуктивності", "anonymous_names_label": "🥷 Приховані імена", "anonymous_names_desc": "Приховати справжні імена гравців і замінити їх випадковими.", - "left_click_label": "🖱️ Відкриття меню лівою кнопкою миші", + "left_click_label": "Відкриття меню лівою кнопкою миші", "left_click_desc": "УВІМКНЕНО: лівий клац відкриває меню, атака здійснюється кнопкою з мечем. ВИМКНЕНО: лівий клац здійснює атаку напряму.", "attack_ratio_label": "⚔️ Співвідношення атаки", "attack_ratio_desc": "Який відсоток ваших військ відправляти в напад (1–100%)", diff --git a/src/client/Main.ts b/src/client/Main.ts index a25a1cd0d..7cc946362 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -366,7 +366,7 @@ class Client { "host-lobby-modal", "join-private-lobby-modal", "game-starting-modal", - "top-bar", + "game-top-bar", "help-modal", "user-setting", ].forEach((tag) => { diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index f61de2d5a..7fc0f547f 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -14,6 +14,8 @@ import { EmojiTable } from "./layers/EmojiTable"; import { EventsDisplay } from "./layers/EventsDisplay"; import { FxLayer } from "./layers/FxLayer"; import { GameLeftSidebar } from "./layers/GameLeftSidebar"; +import { GameRightSidebar } from "./layers/GameRightSidebar"; +import { GameTopBar } from "./layers/GameTopBar"; import { GutterAdModal } from "./layers/GutterAdModal"; import { HeadsUpMessage } from "./layers/HeadsUpMessage"; import { Layer } from "./layers/Layer"; @@ -21,7 +23,6 @@ import { Leaderboard } from "./layers/Leaderboard"; import { MainRadialMenu } from "./layers/MainRadialMenu"; import { MultiTabModal } from "./layers/MultiTabModal"; import { NameLayer } from "./layers/NameLayer"; -import { OptionsMenu } from "./layers/OptionsMenu"; import { PlayerInfoOverlay } from "./layers/PlayerInfoOverlay"; import { PlayerPanel } from "./layers/PlayerPanel"; import { RailroadLayer } from "./layers/RailroadLayer"; @@ -33,7 +34,6 @@ import { StructureLayer } from "./layers/StructureLayer"; import { TeamStats } from "./layers/TeamStats"; import { TerrainLayer } from "./layers/TerrainLayer"; import { TerritoryLayer } from "./layers/TerritoryLayer"; -import { TopBar } from "./layers/TopBar"; import { UILayer } from "./layers/UILayer"; import { UnitInfoModal } from "./layers/UnitInfoModal"; import { UnitLayer } from "./layers/UnitLayer"; @@ -135,25 +135,28 @@ export function createRenderer( winModal.eventBus = eventBus; winModal.game = game; - const optionsMenu = document.querySelector("options-menu") as OptionsMenu; - if (!(optionsMenu instanceof OptionsMenu)) { - console.error("options menu not found"); - } - optionsMenu.eventBus = eventBus; - optionsMenu.game = game; - const replayPanel = document.querySelector("replay-panel") as ReplayPanel; if (!(replayPanel instanceof ReplayPanel)) { - console.error("ReplayPanel element not found in the DOM"); + console.error("replay panel not found"); } replayPanel.eventBus = eventBus; replayPanel.game = game; - const topBar = document.querySelector("top-bar") as TopBar; - if (!(topBar instanceof TopBar)) { + const gameRightSidebar = document.querySelector( + "game-right-sidebar", + ) as GameRightSidebar; + if (!(gameRightSidebar instanceof GameRightSidebar)) { + console.error("Game Right bar not found"); + } + gameRightSidebar.game = game; + gameRightSidebar.eventBus = eventBus; + + const gameTopBar = document.querySelector("game-top-bar") as GameTopBar; + if (!(gameTopBar instanceof GameTopBar)) { console.error("top bar not found"); } - topBar.game = game; + gameTopBar.game = game; + gameTopBar.eventBus = eventBus; const playerPanel = document.querySelector("player-panel") as PlayerPanel; if (!(playerPanel instanceof PlayerPanel)) { @@ -248,13 +251,13 @@ export function createRenderer( new SpawnTimer(game, transformHandler), leaderboard, gameLeftSidebar, + gameTopBar, + gameRightSidebar, controlPanel, playerInfo, winModal, - optionsMenu, replayPanel, teamStats, - topBar, playerPanel, headsUpMessage, unitInfoModal, diff --git a/src/client/graphics/icons/LeaderboardRegularIcon.ts b/src/client/graphics/icons/LeaderboardRegularIcon.ts deleted file mode 100644 index 46f455fb0..000000000 --- a/src/client/graphics/icons/LeaderboardRegularIcon.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { LitElement, css, html } from "lit"; -import { customElement, property } from "lit/decorators.js"; - -@customElement("leaderboard-regular-icon") -export class LeaderboardRegularIcon extends LitElement { - @property({ type: String }) size = "24"; // Accepts "24", "32", etc. - @property({ type: String }) color = "currentColor"; - - static styles = css` - :host { - display: inline-block; - vertical-align: middle; - } - svg { - display: block; - } - `; - - render() { - return html` - - - - `; - } -} diff --git a/src/client/graphics/icons/LeaderboardSolidIcon.ts b/src/client/graphics/icons/LeaderboardSolidIcon.ts deleted file mode 100644 index 31847cda3..000000000 --- a/src/client/graphics/icons/LeaderboardSolidIcon.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { LitElement, css, html } from "lit"; -import { customElement, property } from "lit/decorators.js"; - -@customElement("leaderboard-solid-icon") -export class LeaderboardSolidIcon extends LitElement { - @property({ type: String }) size = "24"; // Accepts "24", "32", etc. - @property({ type: String }) color = "currentColor"; - - static styles = css` - :host { - display: inline-block; - vertical-align: middle; - } - svg { - display: block; - } - `; - - render() { - return html` - - - - `; - } -} diff --git a/src/client/graphics/icons/TeamRegularIcon.ts b/src/client/graphics/icons/TeamRegularIcon.ts deleted file mode 100644 index cac1c7acf..000000000 --- a/src/client/graphics/icons/TeamRegularIcon.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { LitElement, css, html } from "lit"; -import { customElement, property } from "lit/decorators.js"; - -@customElement("team-regular-icon") -export class TeamRegularIcon extends LitElement { - @property({ type: String }) size = "24"; // Accepts "24", "32", etc. - @property({ type: String }) color = "currentColor"; - - static styles = css` - :host { - display: inline-block; - vertical-align: middle; - } - svg { - display: block; - } - `; - - render() { - return html` - - - - `; - } -} diff --git a/src/client/graphics/icons/TeamSolidIcon.ts b/src/client/graphics/icons/TeamSolidIcon.ts deleted file mode 100644 index d4e021e6a..000000000 --- a/src/client/graphics/icons/TeamSolidIcon.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { LitElement, css, html } from "lit"; -import { customElement, property } from "lit/decorators.js"; - -@customElement("team-solid-icon") -export class TeamSolidIcon extends LitElement { - @property({ type: String }) size = "24"; // Accepts "24", "32", etc. - @property({ type: String }) color = "currentColor"; - - static styles = css` - :host { - display: inline-block; - vertical-align: middle; - } - svg { - display: block; - } - `; - - render() { - return html` - - - - `; - } -} diff --git a/src/client/graphics/layers/ControlPanel.ts b/src/client/graphics/layers/ControlPanel.ts index cce6c9c54..8929ede8e 100644 --- a/src/client/graphics/layers/ControlPanel.ts +++ b/src/client/graphics/layers/ControlPanel.ts @@ -1,13 +1,11 @@ import { LitElement, html } from "lit"; import { customElement, state } from "lit/decorators.js"; -import factoryIcon from "../../../../resources/images/FactoryIconWhite.svg"; import { translateText } from "../../../client/Utils"; import { EventBus } from "../../../core/EventBus"; -import { Gold, UnitType } from "../../../core/game/Game"; import { GameView } from "../../../core/game/GameView"; import { AttackRatioEvent } from "../../InputHandler"; import { SendSetTargetTroopRatioEvent } from "../../Transport"; -import { renderNumber, renderTroops } from "../../Utils"; +import { renderTroops } from "../../Utils"; import { UIState } from "../UIState"; import { Layer } from "./Layer"; @@ -29,37 +27,13 @@ export class ControlPanel extends LitElement implements Layer { @state() private _population: number; - @state() - private _maxPopulation: number; - - @state() - private popRate: number; - - @state() - private _troops: number; - - @state() - private _workers: number; - @state() private _isVisible = false; @state() private _manpower: number = 0; - @state() - private _gold: Gold; - - @state() - private _goldPerSecond: Gold; - - @state() - private _factories: number; - private _lastPopulationIncreaseRate: number; - - private _popRateIsIncreasing: boolean = true; - private init_: boolean = false; init() { @@ -118,22 +92,13 @@ export class ControlPanel extends LitElement implements Layer { const popIncreaseRate = player.population() - this._population; if (this.game.ticks() % 5 === 0) { - this._popRateIsIncreasing = - popIncreaseRate >= this._lastPopulationIncreaseRate; this._lastPopulationIncreaseRate = popIncreaseRate; } this._population = player.population(); - this._maxPopulation = this.game.config().maxPopulation(player); - this._gold = player.gold(); - this._troops = player.troops(); - this._workers = player.workers(); - this.popRate = this.game.config().populationIncreaseRate(player) * 10; - this._goldPerSecond = this.game.config().goldAdditionRate(player) * 10n; this.currentTroopRatio = player.troops() / player.population(); this.requestUpdate(); - this._factories = player.units(UnitType.Factory).length; } onAttackRatioChange(newRatio: number) { @@ -209,51 +174,21 @@ export class ControlPanel extends LitElement implements Layer {
e.preventDefault()} > - -
- +
@@ -94,11 +94,20 @@ export class GameLeftSidebar extends LitElement implements Layer {
` : null} -
+
- ${this.isLeaderboardShow - ? html` ` - : html` `} + treeIcon
${this.isTeamGame ? html` @@ -106,9 +115,14 @@ export class GameLeftSidebar extends LitElement implements Layer { class="w-6 h-6 cursor-pointer" @click=${this.toggleTeamLeaderboard} > - ${this.isTeamLeaderboardShow - ? html` ` - : html` `} + treeIcon
` : null} diff --git a/src/client/graphics/layers/GameRightSidebar.ts b/src/client/graphics/layers/GameRightSidebar.ts new file mode 100644 index 000000000..7363efc60 --- /dev/null +++ b/src/client/graphics/layers/GameRightSidebar.ts @@ -0,0 +1,139 @@ +import { html, LitElement } from "lit"; +import { customElement, state } from "lit/decorators.js"; +import exitIcon from "../../../../resources/images/ExitIconWhite.svg"; +import pauseIcon from "../../../../resources/images/PauseIconWhite.svg"; +import playIcon from "../../../../resources/images/PlayIconWhite.svg"; +import replayRegularIcon from "../../../../resources/images/ReplayRegularIconWhite.svg"; +import replaySolidIcon from "../../../../resources/images/ReplaySolidIconWhite.svg"; +import { EventBus } from "../../../core/EventBus"; +import { GameType } from "../../../core/game/Game"; +import { GameView } from "../../../core/game/GameView"; +import { PauseGameEvent } from "../../Transport"; +import { Layer } from "./Layer"; + +@customElement("game-right-sidebar") +export class GameRightSidebar extends LitElement implements Layer { + public game: GameView; + public eventBus: EventBus; + @state() + private _isSinglePlayer: boolean = false; + + @state() + private _isReplayVisible: boolean = false; + + @state() + private _isVisible: boolean = true; + + @state() + private isPaused: boolean = false; + + @state() + private isExistButtonVisible: boolean = true; + + createRenderRoot() { + return this; + } + + init() { + this._isSinglePlayer = + this.game?.config()?.gameConfig()?.gameType === GameType.Singleplayer || + this.game.config().isReplay(); + this._isVisible = true; + this.game.inSpawnPhase(); + this.requestUpdate(); + } + + tick() { + if (!this.game.inSpawnPhase()) { + this.isExistButtonVisible = false; + } + } + + private toggleReplayPanel(): void { + this._isReplayVisible = !this._isReplayVisible; + } + + private onPauseButtonClick() { + this.isPaused = !this.isPaused; + this.eventBus.emit(new PauseGameEvent(this.isPaused)); + } + + private onExitButtonClick() { + const isAlive = this.game.myPlayer()?.isAlive(); + if (isAlive) { + const isConfirmed = confirm("Are you sure you want to exit the game?"); + if (!isConfirmed) return; + } + // redirect to the home page + window.location.href = "/"; + } + + render() { + return html` + + `; + } +} diff --git a/src/client/graphics/layers/GameTopBar.ts b/src/client/graphics/layers/GameTopBar.ts new file mode 100644 index 000000000..0b2109ac7 --- /dev/null +++ b/src/client/graphics/layers/GameTopBar.ts @@ -0,0 +1,471 @@ +import { html, LitElement } from "lit"; +import { customElement, query, state } from "lit/decorators.js"; +import cityIcon from "../../../../resources/images/CityIconWhite.svg"; +import darkModeIcon from "../../../../resources/images/DarkModeIconWhite.svg"; +import emojiIcon from "../../../../resources/images/EmojiIconWhite.svg"; +import exitIcon from "../../../../resources/images/ExitIconWhite.svg"; +import explosionIcon from "../../../../resources/images/ExplosionIconWhite.svg"; +import factoryIcon from "../../../../resources/images/FactoryIconWhite.svg"; +import goldCoinIcon from "../../../../resources/images/GoldCoinIcon.svg"; +import missileSiloIcon from "../../../../resources/images/MissileSiloUnit.png"; +import mouseIcon from "../../../../resources/images/MouseIconWhite.svg"; +import ninjaIcon from "../../../../resources/images/NinjaIconWhite.svg"; +import populationIcon from "../../../../resources/images/PopulationIconSolidWhite.svg"; +import portIcon from "../../../../resources/images/PortIcon.svg"; +import samLauncherIcon from "../../../../resources/images/SamLauncherUnitWhite.png"; +import settingsIcon from "../../../../resources/images/SettingIconWhite.svg"; +import defensePostIcon from "../../../../resources/images/ShieldIconWhite.svg"; +import treeIcon from "../../../../resources/images/TreeIconWhite.svg"; +import troopIcon from "../../../../resources/images/TroopIconWhite.svg"; +import workerIcon from "../../../../resources/images/WorkerIconWhite.svg"; +import { translateText } from "../../../client/Utils"; +import { EventBus } from "../../../core/EventBus"; +import { UnitType } from "../../../core/game/Game"; +import { GameUpdateType } from "../../../core/game/GameUpdates"; +import { GameView } from "../../../core/game/GameView"; +import { UserSettings } from "../../../core/game/UserSettings"; +import { AlternateViewEvent, RefreshGraphicsEvent } from "../../InputHandler"; +import { renderNumber, renderTroops } from "../../Utils"; +import { Layer } from "./Layer"; + +@customElement("game-top-bar") +export class GameTopBar extends LitElement implements Layer { + public game: GameView; + public eventBus: EventBus; + private _userSettings: UserSettings = new UserSettings(); + private _population = 0; + private _troops = 0; + private _cities = 0; + private _factories = 0; + private _workers = 0; + private _missileSilo = 0; + private _port = 0; + private _defensePost = 0; + private _samLauncher = 0; + private _lastPopulationIncreaseRate = 0; + private _popRateIsIncreasing = false; + private hasWinner = false; + + @state() + private showSettingsMenu = false; + @state() + private alternateView: boolean = false; + + @state() + private timer: number = 0; + + @query(".settings-container") + private settingsContainer!: HTMLElement; + + createRenderRoot() { + return this; + } + + init() { + this.requestUpdate(); + } + + tick() { + this.updatePopulationIncrease(); + const player = this.game?.myPlayer(); + if (!player) return; + this._troops = player.troops(); + this._workers = player.workers(); + this._cities = player.totalUnitLevels(UnitType.City); + this._missileSilo = player.totalUnitLevels(UnitType.MissileSilo); + this._port = player.totalUnitLevels(UnitType.Port); + this._defensePost = player.totalUnitLevels(UnitType.DefensePost); + this._samLauncher = player.totalUnitLevels(UnitType.SAMLauncher); + this._factories = player.totalUnitLevels(UnitType.Factory); + const updates = this.game.updatesSinceLastTick(); + if (updates) { + this.hasWinner = this.hasWinner || updates[GameUpdateType.Win].length > 0; + } + if (this.game.inSpawnPhase()) { + this.timer = 0; + } else if (!this.hasWinner && this.game.ticks() % 10 === 0) { + this.timer++; + } + this.requestUpdate(); + } + + connectedCallback() { + super.connectedCallback(); + window.addEventListener("click", this.handleOutsideClick, true); + } + + disconnectedCallback() { + window.removeEventListener("click", this.handleOutsideClick, true); + super.disconnectedCallback(); + } + private handleOutsideClick = (event: MouseEvent) => { + if ( + this.showSettingsMenu && + this.settingsContainer && + !this.settingsContainer.contains(event.target as Node) + ) { + this.showSettingsMenu = false; + } + }; + + private onExitButtonClick() { + const isAlive = this.game.myPlayer()?.isAlive(); + if (isAlive) { + const isConfirmed = confirm("Are you sure you want to exit the game?"); + if (!isConfirmed) return; + } + // redirect to the home page + window.location.href = "/"; + } + + private onTerrainButtonClick() { + this.alternateView = !this.alternateView; + this.eventBus.emit(new AlternateViewEvent(this.alternateView)); + this.requestUpdate(); + } + + private onToggleEmojisButtonClick() { + this._userSettings.toggleEmojis(); + this.requestUpdate(); + } + + private onToggleSpecialEffectsButtonClick() { + this._userSettings.toggleFxLayer(); + this.requestUpdate(); + } + + private onToggleDarkModeButtonClick() { + this._userSettings.toggleDarkMode(); + this.requestUpdate(); + this.eventBus.emit(new RefreshGraphicsEvent()); + } + + private onToggleRandomNameModeButtonClick() { + this._userSettings.toggleRandomName(); + } + private onToggleLeftClickOpensMenu() { + this._userSettings.toggleLeftClickOpenMenu(); + } + + private toggleSettingsMenu() { + this.showSettingsMenu = !this.showSettingsMenu; + } + + private updatePopulationIncrease() { + const player = this.game?.myPlayer(); + if (player === null) return; + const popIncreaseRate = player.population() - this._population; + if (this.game.ticks() % 5 === 0) { + this._popRateIsIncreasing = + popIncreaseRate >= this._lastPopulationIncreaseRate; + this._lastPopulationIncreaseRate = popIncreaseRate; + } + } + + private secondsToHms = (d: number): string => { + const h = Math.floor(d / 3600); + const m = Math.floor((d % 3600) / 60); + const s = Math.floor((d % 3600) % 60); + let time = d === 0 ? "-" : `${s}s`; + if (m > 0) time = `${m}m` + time; + if (h > 0) time = `${h}h` + time; + return time; + }; + + render() { + const myPlayer = this.game?.myPlayer(); + if (!this.game || !myPlayer || this.game.inSpawnPhase()) { + return null; + } + + const isAlt = this.game.config().isReplay(); + if (isAlt) { + return html` +
+
+ ${this.secondsToHms(this.timer)} +
+
+ `; + } + const popRate = myPlayer + ? this.game.config().populationIncreaseRate(myPlayer) * 10 + : 0; + const maxPop = myPlayer ? this.game.config().maxPopulation(myPlayer) : 0; + const goldPerSecond = myPlayer + ? this.game.config().goldAdditionRate(myPlayer) * 10n + : 0n; + + return html` +
+
+ ${myPlayer?.isAlive() && !this.game.inSpawnPhase() + ? html` +
+
+
+
+ gold + +${renderNumber(goldPerSecond)} +
+
${renderNumber(myPlayer.gold())}
+
+
+
+ population + + +${renderTroops(popRate)} + +
+
+ ${renderTroops(myPlayer.population())} / + ${renderTroops(maxPop)} +
+
+
+
+
+ troops + ${renderTroops(this._troops)} +
+
+ gold + ${renderTroops(this._workers)} +
+
+
+
+
+ gold + ${renderNumber(this._cities)} +
+
+ gold + ${renderNumber(this._factories)} +
+
+ gold + ${renderNumber(this._port)} +
+
+ gold + ${renderNumber(this._defensePost)} +
+
+ gold + ${renderNumber(this._missileSilo)} +
+
+ gold + ${renderNumber(this._samLauncher)} +
+
+
+
+ ` + : html`
`} +
+
+ ${this.secondsToHms(this.timer)} +
+
+ settings + ${this.showSettingsMenu + ? html` +
+ + + + + + + +
+ ` + : null} +
+
+
+
+ `; + } +} diff --git a/src/client/graphics/layers/HeadsUpMessage.ts b/src/client/graphics/layers/HeadsUpMessage.ts index 5c1e206d9..95881a898 100644 --- a/src/client/graphics/layers/HeadsUpMessage.ts +++ b/src/client/graphics/layers/HeadsUpMessage.ts @@ -34,8 +34,8 @@ export class HeadsUpMessage extends LitElement implements Layer { return html`
e.preventDefault()} diff --git a/src/client/graphics/layers/PlayerInfoOverlay.ts b/src/client/graphics/layers/PlayerInfoOverlay.ts index ca2c97354..161d40e7f 100644 --- a/src/client/graphics/layers/PlayerInfoOverlay.ts +++ b/src/client/graphics/layers/PlayerInfoOverlay.ts @@ -352,11 +352,11 @@ export class PlayerInfoOverlay extends LitElement implements Layer { return html`
e.preventDefault()} >
${this.player !== null ? this.renderPlayerInfo(this.player) : ""} ${this.unit !== null ? this.renderUnitInfo(this.unit) : ""} diff --git a/src/client/graphics/layers/ReplayPanel.ts b/src/client/graphics/layers/ReplayPanel.ts index 622b233ff..0303485c9 100644 --- a/src/client/graphics/layers/ReplayPanel.ts +++ b/src/client/graphics/layers/ReplayPanel.ts @@ -1,7 +1,6 @@ import { html, LitElement } from "lit"; -import { customElement, state } from "lit/decorators.js"; +import { customElement, property, state } from "lit/decorators.js"; import { EventBus } from "../../../core/EventBus"; -import { GameType } from "../../../core/game/Game"; import { GameView } from "../../../core/game/GameView"; import { ReplaySpeedChangeEvent } from "../../InputHandler"; import { @@ -16,27 +15,26 @@ export class ReplayPanel extends LitElement implements Layer { public game: GameView | undefined; public eventBus: EventBus | undefined; + @property({ type: Boolean }) + visible: boolean = false; + @state() private _replaySpeedMultiplier: number = defaultReplaySpeedMultiplier; - private _isSinglePlayer: boolean = false; - @state() - private _isVisible = false; + @property({ type: Boolean }) + isSingleplayer = false; - init() { - this._isSinglePlayer = - this.game?.config().gameConfig().gameType === GameType.Singleplayer; - if (this._isSinglePlayer) { - this.setVisible(true); - } + createRenderRoot() { + return this; // Enable Tailwind CSS } - tick() { - if (!this._isVisible && this.game?.config().isReplay()) { - this.setVisible(true); - } + init() {} - this.requestUpdate(); + tick() { + if (!this.visible) return; + if (this.game!.ticks() % 10 === 0) { + this.requestUpdate(); + } } onReplaySpeedChange(value: ReplaySpeedMultiplier) { @@ -44,85 +42,45 @@ export class ReplayPanel extends LitElement implements Layer { this.eventBus?.emit(new ReplaySpeedChangeEvent(value)); } - renderLayer(context: CanvasRenderingContext2D) { - // Render any necessary canvas elements - } - - shouldTransform(): boolean { + renderLayer(_ctx: CanvasRenderingContext2D) {} + shouldTransform() { return false; } - setVisible(visible: boolean) { - this._isVisible = visible; - this.requestUpdate(); - } - render() { - if (!this._isVisible) { - return html``; - } + if (!this.visible) return html``; return html`
e.preventDefault()} + @contextmenu=${(e: Event) => e.preventDefault()} >
- - - - + ${this.renderSpeedButton(ReplaySpeedMultiplier.slow, "×0.5")} + ${this.renderSpeedButton(ReplaySpeedMultiplier.normal, "×1")} + ${this.renderSpeedButton(ReplaySpeedMultiplier.fast, "×2")} + ${this.renderSpeedButton(ReplaySpeedMultiplier.fastest, "max")}
`; } - createRenderRoot() { - return this; // Disable shadow DOM to allow Tailwind styles + private renderSpeedButton(value: ReplaySpeedMultiplier, label: string) { + const isActive = this._replaySpeedMultiplier === value; + return html` + + `; } } diff --git a/src/client/graphics/layers/SpawnTimer.ts b/src/client/graphics/layers/SpawnTimer.ts index 081dbcb0b..c3a5f97cc 100644 --- a/src/client/graphics/layers/SpawnTimer.ts +++ b/src/client/graphics/layers/SpawnTimer.ts @@ -16,8 +16,11 @@ export class SpawnTimer implements Layer { tick() { if (this.game.inSpawnPhase()) { - this.ratios[0] = - this.game.ticks() / this.game.config().numSpawnPhaseTurns(); + // During spawn phase, only one segment filling full width + this.ratios = [ + this.game.ticks() / this.game.config().numSpawnPhaseTurns(), + ]; + this.colors = ["rgba(0, 128, 255, 0.7)"]; return; } @@ -33,18 +36,17 @@ export class SpawnTimer implements Layer { const team = player.team(); if (team === null) throw new Error("Team is null"); const tiles = teamTiles.get(team) ?? 0; - const sum = tiles + player.numTilesOwned(); - teamTiles.set(team, sum); + teamTiles.set(team, tiles + player.numTilesOwned()); } const theme = this.game.config().theme(); const total = sumIterator(teamTiles.values()); if (total === 0) return; + for (const [team, count] of teamTiles) { const ratio = count / total; - const color = theme.teamColor(team).toRgbString(); this.ratios.push(ratio); - this.colors.push(color); + this.colors.push(theme.teamColor(team).toRgbString()); } } @@ -53,12 +55,23 @@ export class SpawnTimer implements Layer { } renderLayer(context: CanvasRenderingContext2D) { - if (this.ratios === null) return; - if (this.ratios.length === 0) return; - if (this.colors.length === 0) return; + if (this.ratios.length === 0 || this.colors.length === 0) return; const barHeight = 10; const barWidth = this.transformHandler.width(); + let yOffset: number; + + if (this.game.inSpawnPhase()) { + // At spawn time, draw at top + yOffset = 0; + } else if (this.game.config().gameConfig().gameMode === GameMode.Team) { + // After spawn, only in team mode, offset based on screen width + const screenW = window.innerWidth; + yOffset = screenW > 1024 ? 80 : 58; + } else { + // Not spawn and not team mode: no bar + return; + } let x = 0; let filledRatio = 0; @@ -67,7 +80,7 @@ export class SpawnTimer implements Layer { const segmentWidth = barWidth * ratio; context.fillStyle = this.colors[i]; - context.fillRect(x, 0, segmentWidth, barHeight); + context.fillRect(x, yOffset, segmentWidth, barHeight); x += segmentWidth; filledRatio += ratio; @@ -76,8 +89,6 @@ export class SpawnTimer implements Layer { } function sumIterator(values: MapIterator) { - // To use reduce, we'd need to allocate an array: - // return Array.from(values).reduce((sum, v) => sum + v, 0); let total = 0; for (const value of values) { total += value; diff --git a/src/client/graphics/layers/TopBar.ts b/src/client/graphics/layers/TopBar.ts deleted file mode 100644 index c6d58536a..000000000 --- a/src/client/graphics/layers/TopBar.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { LitElement, html } from "lit"; -import { customElement } from "lit/decorators.js"; -import { translateText } from "../../../client/Utils"; -import { GameView } from "../../../core/game/GameView"; -import { renderNumber, renderTroops } from "../../Utils"; -import { Layer } from "./Layer"; - -@customElement("top-bar") -export class TopBar extends LitElement implements Layer { - public game: GameView; - private isVisible = false; - private _population = 0; - private _lastPopulationIncreaseRate = 0; - private _popRateIsIncreasing = false; - - createRenderRoot() { - return this; - } - - init() { - this.isVisible = true; - this.requestUpdate(); - } - - tick() { - this.updatePopulationIncrease(); - this.requestUpdate(); - } - - private updatePopulationIncrease() { - const player = this.game?.myPlayer(); - if (player === null) return; - const popIncreaseRate = player.population() - this._population; - if (this.game.ticks() % 5 === 0) { - this._popRateIsIncreasing = - popIncreaseRate >= this._lastPopulationIncreaseRate; - this._lastPopulationIncreaseRate = popIncreaseRate; - } - } - - render() { - if (!this.isVisible) { - return html``; - } - - const myPlayer = this.game?.myPlayer(); - if (!myPlayer?.isAlive() || this.game?.inSpawnPhase()) { - return html``; - } - - const popRate = this.game.config().populationIncreaseRate(myPlayer) * 10; - const maxPop = this.game.config().maxPopulation(myPlayer); - const goldPerSecond = this.game.config().goldAdditionRate(myPlayer) * 10n; - - return html` -
- -
- ${translateText("control_panel.pop")}: - ${renderTroops(myPlayer.population())} / - ${renderTroops(maxPop)} - (+${renderTroops(popRate)}) -
- -
- ${translateText("control_panel.gold")}: - ${renderNumber(myPlayer.gold())} - (+${renderNumber(goldPerSecond)}) -
-
- `; - } -} diff --git a/src/client/index.html b/src/client/index.html index 77c4ece35..d0b166ef2 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -265,7 +265,6 @@
-
-
+
@@ -363,7 +359,8 @@ - + + diff --git a/src/client/styles.css b/src/client/styles.css index e3bc2ab11..05950fe70 100644 --- a/src/client/styles.css +++ b/src/client/styles.css @@ -37,6 +37,14 @@ background: rgba(255, 255, 255, 0.3); } +.hide-scrollbar { + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE/Edge */ +} +.hide-scrollbar::-webkit-scrollbar { + display: none; /* Chrome, Safari */ +} + .start-game-button { width: 100%; max-width: 300px; diff --git a/src/core/game/GameView.ts b/src/core/game/GameView.ts index f50bd6c85..284447b51 100644 --- a/src/core/game/GameView.ts +++ b/src/core/game/GameView.ts @@ -264,10 +264,17 @@ export class PlayerView { targetTroopRatio(): number { return this.data.targetTroopRatio; } + troops(): number { return this.data.troops; } + totalUnitLevels(type: UnitType): number { + return this.units(type) + .map((unit) => unit.level()) + .reduce((a, b) => a + b, 0); + } + isAlliedWith(other: PlayerView): boolean { return this.data.allies.some((n) => other.smallID() === n); }