Merge branch 'openfrontio:main' into main

This commit is contained in:
Mittanicz
2025-03-29 11:16:54 +01:00
committed by GitHub
13 changed files with 358 additions and 504 deletions
+5
View File
@@ -138,6 +138,7 @@
"title": "Private Lobby",
"map": "Karte",
"difficulty": "Schwierigkeit",
"mode": "Modus",
"options_title": "Optionen",
"bots": "Bots: ",
"bots_disabled": "Deaktiviert",
@@ -156,5 +157,9 @@
"Balanced": "Ausgewogen",
"Intense": "Intensiv",
"Impossible": "Unmöglich"
},
"game_mode": {
"ffa": "Free for All",
"teams": "Teams"
}
}
+180
View File
@@ -0,0 +1,180 @@
import { LitElement, html } from "lit";
import { customElement, state } from "lit/decorators.js";
// Import language files
import enTranslations from "../../resources/lang/en.json";
import bgTranslations from "../../resources/lang/bg.json";
import jaTranslations from "../../resources/lang/ja.json";
import frTranslations from "../../resources/lang/fr.json";
import nlTranslations from "../../resources/lang/nl.json";
import deTranslations from "../../resources/lang/de.json";
import esTranslations from "../../resources/lang/es.json";
const translations = {
en: enTranslations,
bg: bgTranslations,
ja: jaTranslations,
fr: frTranslations,
nl: nlTranslations,
de: deTranslations,
es: esTranslations,
};
@customElement("lang-selector")
export class LangSelector extends LitElement {
@state() public translations: any = {};
@state() private defaultTranslations: any = {};
@state() private currentLang: string = "en";
createRenderRoot() {
return this;
}
connectedCallback() {
super.connectedCallback();
this.initializeLanguage();
}
private async initializeLanguage() {
const locale = new Intl.Locale(navigator.language);
const defaultLang = locale.language;
const userLang = localStorage.getItem("lang") || defaultLang;
this.defaultTranslations = await this.loadLanguage("en");
this.translations = await this.loadLanguage(userLang);
this.currentLang = userLang;
this.applyTranslation(this.translations);
}
private async loadLanguage(lang: string): Promise<any> {
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 {};
}
}
private applyTranslation(translations: any) {
const components = [
"single-player-modal",
"host-lobby-modal",
"join-private-lobby-modal",
"emoji-table",
"leader-board",
"build-menu",
"win-modal",
"game-starting-modal",
"top-bar",
"player-panel",
"help-modal",
"username-input",
"public-lobby",
];
document.title = translations.main?.title || document.title;
document.querySelectorAll("[data-i18n]").forEach((element) => {
const key = element.getAttribute("data-i18n");
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}`);
}
});
components.forEach((tagName) => {
const el = document.querySelector(tagName) as any;
if (el && typeof el.requestUpdate === "function") {
el.requestUpdate();
} else {
console.warn(
`requestUpdate() not available on <${tagName}> or element not found.`,
);
}
});
}
public translateText(
key: string,
params: Record<string, string | number> = {},
): string {
const keys = key.split(".");
let text: any = this.translations;
for (const k of keys) {
text = text?.[k];
if (!text) break;
}
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)) {
text = text.replace(`{${param}}`, String(value));
}
return text;
}
private async changeLanguage(lang: string) {
localStorage.setItem("lang", lang);
this.translations = await this.loadLanguage(lang);
this.currentLang = lang;
this.applyTranslation(this.translations);
}
render() {
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>
`;
}
}
+21
View File
@@ -26,6 +26,8 @@ import { GameType } from "../core/game/Game";
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
import GoogleAdElement from "./GoogleAdElement";
import { GameConfig, GameInfo, GameRecord } from "../core/Schemas";
import "./LangSelector";
import { LangSelector } from "./LangSelector";
export interface JoinLobbyEvent {
// Multiplayer games only have gameID, gameConfig is not known until game starts.
@@ -51,6 +53,13 @@ class Client {
constructor() {}
initialize(): void {
const langSelector = document.querySelector(
"lang-selector",
) as LangSelector;
if (!langSelector) {
consolex.warn("Lang selector element not found");
}
this.flagInput = document.querySelector("flag-input") as FlagInput;
if (!this.flagInput) {
consolex.warn("Flag input element not found");
@@ -146,6 +155,18 @@ class Client {
});
page();
function updateSliderProgress(slider) {
const percent =
((slider.value - slider.min) / (slider.max - slider.min)) * 100;
slider.style.setProperty("--progress", `${percent}%`);
}
document
.querySelectorAll("#bots-count, #private-lobby-bots-count")
.forEach((slider) => {
updateSliderProgress(slider);
slider.addEventListener("input", () => updateSliderProgress(slider));
});
}
private async handleJoinLobby(event: CustomEvent) {
+18 -20
View File
@@ -1,3 +1,5 @@
import { LangSelector } from "./LangSelector";
export function renderTroops(troops: number): string {
return renderNumber(troops / 10);
}
@@ -71,29 +73,25 @@ export function generateCryptoRandomUUID(): string {
);
}
export function translateText(
// Re-export translateText from LangSelector
export const translateText = (
key: string,
params: Record<string, string | number> = {},
): string {
const keys = key.split(".");
let text: any = (window as any).translations;
for (const k of keys) {
text = text?.[k];
if (!text) break;
): string => {
const langSelector = document.querySelector("lang-selector") as LangSelector;
if (!langSelector) {
console.warn("LangSelector not found in DOM");
return key;
}
if (!text && (window as any).defaultTranslations) {
text = (window as any).defaultTranslations;
for (const k of keys) {
text = text?.[k];
if (!text) return key;
}
// Wait for translations to be loaded
if (
!langSelector.translations ||
Object.keys(langSelector.translations).length === 0
) {
console.warn("Translations not loaded yet");
return key;
}
for (const [param, value] of Object.entries(params)) {
text = text.replace(`{${param}}`, String(value));
}
return text;
}
return langSelector.translateText(key, params);
};
+6 -1
View File
@@ -261,8 +261,13 @@ export class EventsDisplay extends LitElement implements Layer {
const traitor = this.game.playerBySmallID(update.traitorID) as PlayerView;
if (!betrayed.isTraitor() && traitor === myPlayer) {
const malusPercent = Math.round(
(1 - this.game.config().traitorDefenseDebuff()) * 100,
);
this.addEvent({
description: `You broke your alliance with ${betrayed.name()}, making you a TRAITOR`,
description:
`You broke your alliance with ${betrayed.name()}, making you a TRAITOR ` +
`(${malusPercent}% defense debuff)`,
type: MessageType.ERROR,
highlight: true,
createdAt: this.game.ticks(),
+1 -108
View File
@@ -260,18 +260,7 @@
secondary
></o-button>
<div class="container__row">
<select
id="lang-selector"
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">English</option>
<option value="bg">Български</option>
<option value="ja">日本語</option>
<option value="fr">Français</option>
<option value="nl">Nederlands</option>
<option value="de">Deutsch</option>
<option value="es">Español</option>
</select>
<lang-selector class="w-full"></lang-selector>
</div>
</div>
</main>
@@ -359,102 +348,6 @@
});
});
</script>
<script>
document.addEventListener("DOMContentLoaded", async function () {
const locale = new Intl.Locale(navigator.language);
const defaultLang = locale.language;
const userLang = localStorage.getItem("lang") || defaultLang;
async function loadLanguage(lang) {
try {
const response = await fetch(`/lang/${lang}.json`);
if (!response.ok)
throw new Error(`Language file not found: ${lang}`);
return await response.json();
} catch (error) {
console.error("🚨 Translation load error:", error);
return {};
}
}
function applyTranslation(translations) {
const components = [
"single-player-modal",
"host-lobby-modal",
"join-private-lobby-modal",
"emoji-table",
"leader-board",
"build-menu",
"win-modal",
"game-starting-modal",
"top-bar",
"player-panel",
"help-modal",
"username-input",
"public-lobby",
];
document.title = translations.main?.title || document.title;
document.querySelectorAll("[data-i18n]").forEach((element) => {
const key = element.getAttribute("data-i18n");
const keys = key.split(".");
let text = translations;
for (const k of keys) {
text = text?.[k];
if (!text) break;
}
if (!text && window.defaultTranslations) {
let fallback = window.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}`);
}
});
components.forEach((tagName) => {
const el = document.querySelector(tagName);
if (el && typeof el.requestUpdate === "function") {
el.requestUpdate();
} else {
console.warn(
`requestUpdate() not available on <${tagName}> or element not found.`,
);
}
});
}
async function changeLanguage(lang) {
// console.log(`Changing language to: ${lang}`);
localStorage.setItem("lang", lang);
const translations = await loadLanguage(lang);
window.translations = translations;
applyTranslation(translations);
}
const defaultTranslations = await loadLanguage("en");
window.defaultTranslations = defaultTranslations;
const translations = await loadLanguage(userLang);
window.translations = translations;
applyTranslation(translations);
const langSelector = document.getElementById("lang-selector");
if (langSelector) {
langSelector.value = userLang;
}
document
.getElementById("lang-selector")
.addEventListener("change", function (event) {
changeLanguage(event.target.value);
});
});
</script>
<!-- Analytics -->
<script
+10 -1
View File
@@ -240,15 +240,24 @@ label.option-card:hover {
#private-lobby-bots-count {
width: 80%;
height: 16px;
appearance: none;
}
#bots-count::-webkit-slider-runnable-track,
#private-lobby-bots-count::-webkit-slider-runnable-track {
background: #0075ff;
appearance: none;
background: linear-gradient(
to right,
#0075ff var(--progress, 0%),
white var(--progress, 0%)
);
height: 8px;
}
#bots-count::-webkit-slider-thumb,
#private-lobby-bots-count::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
background: #0075ff;
border-color: #0075ff;
position: relative;
+54 -143
View File
@@ -1,5 +1,9 @@
import { colord, Colord } from "colord";
export const red: Colord = colord({ r: 235, g: 53, b: 53 }); // Bright Red
export const blue: Colord = colord({ r: 41, g: 98, b: 255 }); // Royal Blue
export const botColor: Colord = colord({ r: 210, g: 206, b: 200 }); // Muted Beige Gray
export const territoryColors: Colord[] = [
colord({ r: 230, g: 100, b: 100 }), // Bright Red
colord({ r: 100, g: 180, b: 230 }), // Sky Blue
@@ -206,147 +210,54 @@ export const humanColors: Colord[] = [
colord({ r: 234, g: 179, b: 8 }), // Daffodil
];
export const redShades = [
colord({ r: 210, g: 0, b: 0 }), // Most distinct colors sorted by brightness
colord({ r: 225, g: 0, b: 0 }),
colord({ r: 240, g: 0, b: 0 }),
colord({ r: 255, g: 0, b: 0 }),
colord({ r: 210, g: 21, b: 21 }),
colord({ r: 225, g: 23, b: 23 }),
colord({ r: 240, g: 25, b: 25 }),
colord({ r: 255, g: 26, b: 26 }),
colord({ r: 210, g: 42, b: 42 }),
colord({ r: 225, g: 45, b: 45 }),
colord({ r: 240, g: 48, b: 48 }),
colord({ r: 255, g: 51, b: 51 }),
colord({ r: 195, g: 59, b: 59 }),
colord({ r: 210, g: 63, b: 63 }),
colord({ r: 225, g: 68, b: 68 }),
colord({ r: 240, g: 72, b: 72 }),
colord({ r: 255, g: 77, b: 77 }),
colord({ r: 180, g: 72, b: 72 }),
colord({ r: 195, g: 78, b: 78 }),
colord({ r: 210, g: 84, b: 84 }),
colord({ r: 225, g: 90, b: 90 }),
colord({ r: 240, g: 96, b: 96 }),
colord({ r: 255, g: 102, b: 102 }),
colord({ r: 165, g: 83, b: 83 }),
colord({ r: 180, g: 90, b: 90 }),
colord({ r: 195, g: 98, b: 98 }),
colord({ r: 210, g: 105, b: 105 }),
colord({ r: 225, g: 113, b: 113 }),
colord({ r: 240, g: 120, b: 120 }),
colord({ r: 255, g: 128, b: 128 }),
colord({ r: 150, g: 90, b: 90 }),
colord({ r: 165, g: 99, b: 99 }),
colord({ r: 180, g: 108, b: 108 }),
colord({ r: 195, g: 117, b: 117 }),
colord({ r: 210, g: 126, b: 126 }),
colord({ r: 225, g: 135, b: 135 }),
colord({ r: 240, g: 144, b: 144 }),
colord({ r: 255, g: 153, b: 153 }),
colord({ r: 135, g: 95, b: 95 }),
colord({ r: 150, g: 105, b: 105 }),
colord({ r: 165, g: 116, b: 116 }),
colord({ r: 180, g: 126, b: 126 }),
colord({ r: 195, g: 137, b: 137 }),
colord({ r: 210, g: 147, b: 147 }),
colord({ r: 225, g: 158, b: 158 }),
colord({ r: 240, g: 168, b: 168 }),
colord({ r: 255, g: 179, b: 179 }),
colord({ r: 120, g: 108, b: 108 }),
colord({ r: 135, g: 122, b: 122 }),
colord({ r: 150, g: 135, b: 135 }),
colord({ r: 165, g: 149, b: 149 }),
colord({ r: 180, g: 162, b: 162 }),
colord({ r: 195, g: 176, b: 176 }),
colord({ r: 210, g: 189, b: 189 }),
colord({ r: 225, g: 203, b: 203 }),
colord({ r: 240, g: 216, b: 216 }),
colord({ r: 255, g: 230, b: 230 }),
colord({ r: 195, g: 0, b: 0 }),
colord({ r: 195, g: 20, b: 20 }),
colord({ r: 195, g: 39, b: 39 }),
colord({ r: 180, g: 54, b: 54 }),
colord({ r: 165, g: 66, b: 66 }),
colord({ r: 150, g: 75, b: 75 }),
colord({ r: 135, g: 81, b: 81 }),
colord({ r: 120, g: 84, b: 84 }),
colord({ r: 135, g: 108, b: 108 }),
colord({ r: 150, g: 120, b: 120 }),
colord({ r: 165, g: 132, b: 132 }),
colord({ r: 180, g: 144, b: 144 }),
];
export const blueShades = [
colord({ r: 0, g: 0, b: 120 }), // Most distinct colors sorted by brightness
colord({ r: 0, g: 0, b: 135 }),
colord({ r: 0, g: 0, b: 150 }),
colord({ r: 0, g: 0, b: 165 }),
colord({ r: 0, g: 0, b: 180 }),
colord({ r: 0, g: 0, b: 195 }),
colord({ r: 0, g: 0, b: 210 }),
colord({ r: 0, g: 0, b: 225 }),
colord({ r: 0, g: 0, b: 240 }),
colord({ r: 0, g: 0, b: 255 }),
colord({ r: 12, g: 12, b: 120 }),
colord({ r: 14, g: 14, b: 135 }),
colord({ r: 15, g: 15, b: 150 }),
colord({ r: 17, g: 17, b: 165 }),
colord({ r: 18, g: 18, b: 180 }),
colord({ r: 20, g: 20, b: 195 }),
colord({ r: 21, g: 21, b: 210 }),
colord({ r: 23, g: 23, b: 225 }),
colord({ r: 25, g: 25, b: 240 }),
colord({ r: 26, g: 26, b: 255 }),
colord({ r: 24, g: 24, b: 120 }),
colord({ r: 27, g: 27, b: 135 }),
colord({ r: 30, g: 30, b: 150 }),
colord({ r: 33, g: 33, b: 165 }),
colord({ r: 36, g: 36, b: 180 }),
colord({ r: 39, g: 39, b: 195 }),
colord({ r: 42, g: 42, b: 210 }),
colord({ r: 45, g: 45, b: 225 }),
colord({ r: 48, g: 48, b: 240 }),
colord({ r: 51, g: 51, b: 255 }),
colord({ r: 36, g: 36, b: 120 }),
colord({ r: 41, g: 41, b: 135 }),
colord({ r: 45, g: 45, b: 150 }),
colord({ r: 50, g: 50, b: 165 }),
colord({ r: 54, g: 54, b: 180 }),
colord({ r: 59, g: 59, b: 195 }),
colord({ r: 63, g: 63, b: 210 }),
colord({ r: 68, g: 68, b: 225 }),
colord({ r: 72, g: 72, b: 240 }),
colord({ r: 77, g: 77, b: 255 }),
colord({ r: 48, g: 48, b: 120 }),
colord({ r: 54, g: 54, b: 135 }),
colord({ r: 60, g: 60, b: 150 }),
colord({ r: 66, g: 66, b: 165 }),
colord({ r: 72, g: 72, b: 180 }),
colord({ r: 78, g: 78, b: 195 }),
colord({ r: 84, g: 84, b: 210 }),
colord({ r: 90, g: 90, b: 225 }),
colord({ r: 96, g: 96, b: 240 }),
colord({ r: 102, g: 102, b: 255 }),
colord({ r: 60, g: 60, b: 120 }),
colord({ r: 68, g: 68, b: 135 }),
colord({ r: 75, g: 75, b: 150 }),
colord({ r: 83, g: 83, b: 165 }),
colord({ r: 90, g: 90, b: 180 }),
colord({ r: 98, g: 98, b: 195 }),
colord({ r: 105, g: 105, b: 210 }),
colord({ r: 113, g: 113, b: 225 }),
colord({ r: 120, g: 120, b: 240 }),
colord({ r: 128, g: 128, b: 255 }),
colord({ r: 72, g: 72, b: 120 }),
colord({ r: 81, g: 81, b: 135 }),
colord({ r: 90, g: 90, b: 150 }),
colord({ r: 99, g: 99, b: 165 }),
colord({ r: 108, g: 108, b: 180 }),
colord({ r: 117, g: 117, b: 195 }),
colord({ r: 126, g: 126, b: 210 }),
colord({ r: 135, g: 135, b: 225 }),
colord({ r: 144, g: 144, b: 240 }),
colord({ r: 153, g: 153, b: 255 }),
export const botColors: Colord[] = [
colord({ r: 190, g: 120, b: 120 }), // Muted Red
colord({ r: 120, g: 160, b: 190 }), // Muted Sky Blue
colord({ r: 190, g: 160, b: 100 }), // Muted Golden Yellow
colord({ r: 160, g: 120, b: 190 }), // Muted Purple
colord({ r: 100, g: 170, b: 130 }), // Muted Emerald Green
colord({ r: 190, g: 130, b: 160 }), // Muted Pink
colord({ r: 120, g: 150, b: 100 }), // Muted Olive Green
colord({ r: 190, g: 140, b: 120 }), // Muted Peach
colord({ r: 100, g: 120, b: 160 }), // Muted Navy Blue
colord({ r: 170, g: 170, b: 120 }), // Muted Lime Yellow
colord({ r: 160, g: 120, b: 130 }), // Muted Maroon
colord({ r: 120, g: 170, b: 170 }), // Muted Turquoise
colord({ r: 170, g: 140, b: 100 }), // Muted Light Orange
colord({ r: 140, g: 120, b: 160 }), // Muted Lavender
colord({ r: 150, g: 170, b: 130 }), // Muted Light Green
colord({ r: 170, g: 120, b: 140 }), // Muted Hot Pink
colord({ r: 120, g: 140, b: 120 }), // Muted Sea Green
colord({ r: 180, g: 160, b: 160 }), // Muted Light Pink
colord({ r: 130, g: 130, b: 160 }), // Muted Periwinkle
colord({ r: 160, g: 150, b: 120 }), // Muted Sand
colord({ r: 120, g: 160, b: 150 }), // Muted Aquamarine
colord({ r: 170, g: 150, b: 170 }), // Muted Orchid
colord({ r: 150, g: 160, b: 120 }), // Muted Yellow Green
colord({ r: 120, g: 130, b: 140 }), // Muted Steel Blue
colord({ r: 180, g: 140, b: 140 }), // Muted Salmon
colord({ r: 140, g: 160, b: 170 }), // Muted Light Blue
colord({ r: 170, g: 150, b: 130 }), // Muted Tan
colord({ r: 160, g: 130, b: 160 }), // Muted Plum
colord({ r: 130, g: 170, b: 130 }), // Muted Light Sea Green
colord({ r: 170, g: 130, b: 130 }), // Muted Coral
colord({ r: 130, g: 150, b: 170 }), // Muted Cornflower Blue
colord({ r: 170, g: 170, b: 140 }), // Muted Khaki
colord({ r: 150, g: 130, b: 150 }), // Muted Purple Gray
colord({ r: 140, g: 160, b: 140 }), // Muted Dark Sea Green
colord({ r: 170, g: 130, b: 120 }), // Muted Dark Salmon
colord({ r: 130, g: 150, b: 160 }), // Muted Cadet Blue
colord({ r: 160, g: 160, b: 150 }), // Muted Tan Gray
colord({ r: 150, g: 140, b: 160 }), // Muted Medium Purple
colord({ r: 150, g: 170, b: 150 }), // Muted Pale Green
colord({ r: 160, g: 140, b: 130 }), // Muted Rosy Brown
colord({ r: 140, g: 150, b: 160 }), // Muted Light Slate Gray
colord({ r: 160, g: 150, b: 140 }), // Muted Dark Khaki
colord({ r: 140, g: 130, b: 140 }), // Muted Thistle
colord({ r: 150, g: 160, b: 160 }), // Muted Pale Blue Green
colord({ r: 160, g: 140, b: 150 }), // Muted Puce
colord({ r: 130, g: 160, b: 150 }), // Muted Medium Aquamarine
colord({ r: 160, g: 150, b: 160 }), // Muted Mauve
colord({ r: 150, g: 160, b: 140 }), // Muted Dark Olive Green
colord({ r: 150, g: 140, b: 150 }), // Muted Dusty Rose
];
+16 -3
View File
@@ -5,7 +5,14 @@ import { PseudoRandom } from "../PseudoRandom";
import { simpleHash } from "../Util";
import { GameMap, TileRef } from "../game/GameMap";
import { PlayerView } from "../game/GameView";
import { blueShades, humanColors, redShades, territoryColors } from "./Colors";
import {
blue,
botColor,
botColors,
humanColors,
red,
territoryColors,
} from "./Colors";
export const pastelTheme = new (class implements Theme {
private rand = new PseudoRandom(123);
@@ -30,15 +37,21 @@ export const pastelTheme = new (class implements Theme {
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
territoryColor(player: PlayerView): Colord {
if (player.teamName() == TeamName.Bot) {
return botColor;
}
if (player.teamName() == TeamName.Red) {
return redShades[simpleHash(player.id()) % redShades.length];
return red;
}
if (player.teamName() == TeamName.Blue) {
return blueShades[simpleHash(player.id()) % blueShades.length];
return blue;
}
if (player.info().playerType == PlayerType.Human) {
return humanColors[simpleHash(player.id()) % humanColors.length];
}
if (player.info().playerType == PlayerType.Bot) {
return botColors[simpleHash(player.id()) % botColors.length];
}
return territoryColors[simpleHash(player.id()) % territoryColors.length];
}
+24 -221
View File
@@ -1,17 +1,18 @@
import { Colord, colord, random } from "colord";
import {
Game,
PlayerID,
PlayerInfo,
PlayerType,
TerrainType,
} from "../game/Game";
import { PlayerType, TeamName, TerrainType } from "../game/Game";
import { Theme } from "./Config";
import { time } from "console";
import { PseudoRandom } from "../PseudoRandom";
import { simpleHash } from "../Util";
import { GameMap, TileRef } from "../game/GameMap";
import { PlayerView } from "../game/GameView";
import {
blue,
botColor,
botColors,
humanColors,
red,
territoryColors,
} from "./Colors";
export const pastelThemeDark = new (class implements Theme {
private rand = new PseudoRandom(123);
@@ -29,212 +30,6 @@ export const pastelThemeDark = new (class implements Theme {
private water = colord({ r: 14, g: 11, b: 30 });
private shorelineWater = colord({ r: 50, g: 50, b: 50 });
private territoryColors: Colord[] = [
colord({ r: 230, g: 100, b: 100 }), // Bright Red
colord({ r: 100, g: 180, b: 230 }), // Sky Blue
colord({ r: 230, g: 180, b: 80 }), // Golden Yellow
colord({ r: 180, g: 100, b: 230 }), // Purple
colord({ r: 80, g: 200, b: 120 }), // Emerald Green
colord({ r: 230, g: 130, b: 180 }), // Pink
colord({ r: 100, g: 160, b: 80 }), // Olive Green
colord({ r: 230, g: 150, b: 100 }), // Peach
colord({ r: 80, g: 130, b: 190 }), // Navy Blue
colord({ r: 210, g: 210, b: 100 }), // Lime Yellow
colord({ r: 190, g: 100, b: 130 }), // Maroon
colord({ r: 100, g: 210, b: 210 }), // Turquoise
colord({ r: 210, g: 140, b: 80 }), // Light Orange
colord({ r: 150, g: 110, b: 190 }), // Lavender
colord({ r: 180, g: 210, b: 120 }), // Light Green
colord({ r: 210, g: 100, b: 160 }), // Hot Pink
colord({ r: 100, g: 140, b: 110 }), // Sea Green
colord({ r: 230, g: 180, b: 180 }), // Light Pink
colord({ r: 120, g: 120, b: 190 }), // Periwinkle
colord({ r: 190, g: 170, b: 100 }), // Sand
colord({ r: 100, g: 180, b: 160 }), // Aquamarine
colord({ r: 210, g: 160, b: 200 }), // Orchid
colord({ r: 170, g: 190, b: 100 }), // Yellow Green
colord({ r: 100, g: 130, b: 150 }), // Steel Blue
colord({ r: 230, g: 140, b: 140 }), // Salmon
colord({ r: 140, g: 180, b: 220 }), // Light Blue
colord({ r: 200, g: 160, b: 110 }), // Tan
colord({ r: 180, g: 130, b: 180 }), // Plum
colord({ r: 130, g: 200, b: 130 }), // Light Sea Green
colord({ r: 220, g: 120, b: 120 }), // Coral
colord({ r: 120, g: 160, b: 200 }), // Cornflower Blue
colord({ r: 200, g: 200, b: 140 }), // Khaki
colord({ r: 160, g: 120, b: 160 }), // Purple Gray
colord({ r: 140, g: 180, b: 140 }), // Dark Sea Green
colord({ r: 200, g: 130, b: 110 }), // Dark Salmon
colord({ r: 130, g: 170, b: 190 }), // Cadet Blue
colord({ r: 190, g: 180, b: 160 }), // Tan Gray
colord({ r: 170, g: 140, b: 190 }), // Medium Purple
colord({ r: 160, g: 190, b: 160 }), // Pale Green
colord({ r: 190, g: 150, b: 130 }), // Rosy Brown
colord({ r: 140, g: 150, b: 180 }), // Light Slate Gray
colord({ r: 180, g: 170, b: 140 }), // Dark Khaki
colord({ r: 150, g: 130, b: 150 }), // Thistle
colord({ r: 170, g: 190, b: 180 }), // Pale Blue Green
colord({ r: 190, g: 140, b: 150 }), // Puce
colord({ r: 130, g: 180, b: 170 }), // Medium Aquamarine
colord({ r: 180, g: 160, b: 180 }), // Mauve
colord({ r: 160, g: 180, b: 140 }), // Dark Olive Green
colord({ r: 170, g: 150, b: 170 }), // Dusty Rose
colord({ r: 100, g: 180, b: 230 }), // Sky Blue
colord({ r: 230, g: 180, b: 80 }), // Golden Yellow
colord({ r: 180, g: 100, b: 230 }), // Purple
colord({ r: 80, g: 200, b: 120 }), // Emerald Green
colord({ r: 230, g: 130, b: 180 }), // Pink
colord({ r: 100, g: 160, b: 80 }), // Olive Green
colord({ r: 230, g: 150, b: 100 }), // Peach
colord({ r: 80, g: 130, b: 190 }), // Navy Blue
colord({ r: 210, g: 210, b: 100 }), // Lime Yellow
colord({ r: 190, g: 100, b: 130 }), // Maroon
colord({ r: 100, g: 210, b: 210 }), // Turquoise
colord({ r: 210, g: 140, b: 80 }), // Light Orange
colord({ r: 150, g: 110, b: 190 }), // Lavender
colord({ r: 180, g: 210, b: 120 }), // Light Green
colord({ r: 210, g: 100, b: 160 }), // Hot Pink
colord({ r: 100, g: 140, b: 110 }), // Sea Green
colord({ r: 230, g: 180, b: 180 }), // Light Pink
colord({ r: 120, g: 120, b: 190 }), // Periwinkle
colord({ r: 190, g: 170, b: 100 }), // Sand
colord({ r: 100, g: 180, b: 160 }), // Aquamarine
colord({ r: 210, g: 160, b: 200 }), // Orchid
colord({ r: 170, g: 190, b: 100 }), // Yellow Green
colord({ r: 100, g: 130, b: 150 }), // Steel Blue
colord({ r: 230, g: 140, b: 140 }), // Salmon
colord({ r: 140, g: 180, b: 220 }), // Light Blue
colord({ r: 200, g: 160, b: 110 }), // Tan
colord({ r: 180, g: 130, b: 180 }), // Plum
colord({ r: 130, g: 200, b: 130 }), // Light Sea Green
colord({ r: 220, g: 120, b: 120 }), // Coral
colord({ r: 120, g: 160, b: 200 }), // Cornflower Blue
colord({ r: 200, g: 200, b: 140 }), // Khaki
colord({ r: 160, g: 120, b: 160 }), // Purple Gray
colord({ r: 140, g: 180, b: 140 }), // Dark Sea Green
colord({ r: 200, g: 130, b: 110 }), // Dark Salmon
colord({ r: 130, g: 170, b: 190 }), // Cadet Blue
colord({ r: 190, g: 180, b: 160 }), // Tan Gray
colord({ r: 170, g: 140, b: 190 }), // Medium Purple
colord({ r: 160, g: 190, b: 160 }), // Pale Green
colord({ r: 190, g: 150, b: 130 }), // Rosy Brown
colord({ r: 140, g: 150, b: 180 }), // Light Slate Gray
colord({ r: 180, g: 170, b: 140 }), // Dark Khaki
colord({ r: 150, g: 130, b: 150 }), // Thistle
colord({ r: 170, g: 190, b: 180 }), // Pale Blue Green
colord({ r: 190, g: 140, b: 150 }), // Puce
colord({ r: 130, g: 180, b: 170 }), // Medium Aquamarine
colord({ r: 180, g: 160, b: 180 }), // Mauve
colord({ r: 160, g: 180, b: 140 }), // Dark Olive Green
colord({ r: 170, g: 150, b: 170 }), // Dusty Rose
];
private humanColors: Colord[] = [
// Original set
colord({ r: 235, g: 75, b: 75 }), // Bright Red
colord({ r: 67, g: 190, b: 84 }), // Fresh Green
colord({ r: 59, g: 130, b: 246 }), // Royal Blue
colord({ r: 245, g: 158, b: 11 }), // Amber
colord({ r: 236, g: 72, b: 153 }), // Deep Pink
colord({ r: 48, g: 178, b: 180 }), // Teal
colord({ r: 168, g: 85, b: 247 }), // Vibrant Purple
colord({ r: 251, g: 191, b: 36 }), // Marigold
colord({ r: 74, g: 222, b: 128 }), // Mint
colord({ r: 239, g: 68, b: 68 }), // Crimson
colord({ r: 34, g: 197, b: 94 }), // Emerald
colord({ r: 96, g: 165, b: 250 }), // Sky Blue
colord({ r: 249, g: 115, b: 22 }), // Tangerine
colord({ r: 192, g: 132, b: 252 }), // Lavender
colord({ r: 45, g: 212, b: 191 }), // Turquoise
colord({ r: 244, g: 114, b: 182 }), // Rose
colord({ r: 132, g: 204, b: 22 }), // Lime
colord({ r: 56, g: 189, b: 248 }), // Light Blue
colord({ r: 234, g: 179, b: 8 }), // Sunflower
colord({ r: 217, g: 70, b: 239 }), // Fuchsia
colord({ r: 16, g: 185, b: 129 }), // Sea Green
colord({ r: 251, g: 146, b: 60 }), // Light Orange
colord({ r: 147, g: 51, b: 234 }), // Bright Purple
colord({ r: 79, g: 70, b: 229 }), // Indigo
colord({ r: 245, g: 101, b: 101 }), // Coral
colord({ r: 134, g: 239, b: 172 }), // Light Green
colord({ r: 59, g: 130, b: 246 }), // Cerulean
colord({ r: 253, g: 164, b: 175 }), // Salmon Pink
colord({ r: 147, g: 197, b: 253 }), // Powder Blue
colord({ r: 252, g: 211, b: 77 }), // Golden
colord({ r: 190, g: 92, b: 251 }), // Amethyst
colord({ r: 82, g: 183, b: 136 }), // Jade
colord({ r: 248, g: 113, b: 113 }), // Warm Red
colord({ r: 99, g: 202, b: 253 }), // Azure
colord({ r: 240, g: 171, b: 252 }), // Orchid
colord({ r: 163, g: 230, b: 53 }), // Yellow Green
colord({ r: 234, g: 88, b: 12 }), // Burnt Orange
colord({ r: 125, g: 211, b: 252 }), // Crystal Blue
colord({ r: 251, g: 113, b: 133 }), // Watermelon
colord({ r: 52, g: 211, b: 153 }), // Spearmint
colord({ r: 167, g: 139, b: 250 }), // Periwinkle
colord({ r: 245, g: 158, b: 11 }), // Honey
colord({ r: 110, g: 231, b: 183 }), // Seafoam
colord({ r: 233, g: 213, b: 255 }), // Light Lilac
colord({ r: 202, g: 138, b: 4 }), // Rich Gold
colord({ r: 151, g: 255, b: 187 }), // Fresh Mint
colord({ r: 220, g: 38, b: 38 }), // Ruby
colord({ r: 124, g: 58, b: 237 }), // Royal Purple
colord({ r: 45, g: 212, b: 191 }), // Ocean
colord({ r: 252, g: 165, b: 165 }), // Peach
// Additional 50 colors
colord({ r: 179, g: 136, b: 255 }), // Light Purple
colord({ r: 133, g: 77, b: 14 }), // Chocolate
colord({ r: 52, g: 211, b: 153 }), // Aquamarine
colord({ r: 234, g: 179, b: 8 }), // Mustard
colord({ r: 236, g: 72, b: 153 }), // Hot Pink
colord({ r: 147, g: 197, b: 253 }), // Sky
colord({ r: 249, g: 115, b: 22 }), // Pumpkin
colord({ r: 167, g: 139, b: 250 }), // Iris
colord({ r: 16, g: 185, b: 129 }), // Pine
colord({ r: 251, g: 146, b: 60 }), // Mango
colord({ r: 192, g: 132, b: 252 }), // Wisteria
colord({ r: 79, g: 70, b: 229 }), // Sapphire
colord({ r: 245, g: 101, b: 101 }), // Salmon
colord({ r: 134, g: 239, b: 172 }), // Spring Green
colord({ r: 59, g: 130, b: 246 }), // Ocean Blue
colord({ r: 253, g: 164, b: 175 }), // Rose Gold
colord({ r: 16, g: 185, b: 129 }), // Forest
colord({ r: 252, g: 211, b: 77 }), // Sunshine
colord({ r: 190, g: 92, b: 251 }), // Grape
colord({ r: 82, g: 183, b: 136 }), // Eucalyptus
colord({ r: 248, g: 113, b: 113 }), // Cherry
colord({ r: 99, g: 202, b: 253 }), // Arctic
colord({ r: 240, g: 171, b: 252 }), // Lilac
colord({ r: 163, g: 230, b: 53 }), // Chartreuse
colord({ r: 234, g: 88, b: 12 }), // Rust
colord({ r: 125, g: 211, b: 252 }), // Ice Blue
colord({ r: 251, g: 113, b: 133 }), // Strawberry
colord({ r: 52, g: 211, b: 153 }), // Sage
colord({ r: 167, g: 139, b: 250 }), // Violet
colord({ r: 245, g: 158, b: 11 }), // Apricot
colord({ r: 110, g: 231, b: 183 }), // Mint Green
colord({ r: 233, g: 213, b: 255 }), // Thistle
colord({ r: 202, g: 138, b: 4 }), // Bronze
colord({ r: 151, g: 255, b: 187 }), // Pistachio
colord({ r: 220, g: 38, b: 38 }), // Fire Engine
colord({ r: 124, g: 58, b: 237 }), // Electric Purple
colord({ r: 45, g: 212, b: 191 }), // Caribbean
colord({ r: 252, g: 165, b: 165 }), // Melon
colord({ r: 168, g: 85, b: 247 }), // Byzantium
colord({ r: 74, g: 222, b: 128 }), // Kelly Green
colord({ r: 239, g: 68, b: 68 }), // Cardinal
colord({ r: 34, g: 197, b: 94 }), // Shamrock
colord({ r: 96, g: 165, b: 250 }), // Marina
colord({ r: 249, g: 115, b: 22 }), // Carrot
colord({ r: 192, g: 132, b: 252 }), // Heliotrope
colord({ r: 45, g: 212, b: 191 }), // Lagoon
colord({ r: 244, g: 114, b: 182 }), // Bubble Gum
colord({ r: 132, g: 204, b: 22 }), // Apple
colord({ r: 56, g: 189, b: 248 }), // Electric Blue
colord({ r: 234, g: 179, b: 8 }), // Daffodil
];
private _selfColor = colord({ r: 0, g: 255, b: 0 });
private _allyColor = colord({ r: 255, g: 255, b: 0 });
private _enemyColor = colord({ r: 255, g: 0, b: 0 });
@@ -242,14 +37,22 @@ export const pastelThemeDark = new (class implements Theme {
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
territoryColor(player: PlayerView): Colord {
if (player.info().playerType == PlayerType.Human) {
return this.humanColors[
simpleHash(player.info().name) % this.humanColors.length
];
if (player.teamName() == TeamName.Bot) {
return botColor;
}
return this.territoryColors[
simpleHash(player.info().name) % this.territoryColors.length
];
if (player.teamName() == TeamName.Red) {
return red;
}
if (player.teamName() == TeamName.Blue) {
return blue;
}
if (player.info().playerType == PlayerType.Human) {
return humanColors[simpleHash(player.id()) % humanColors.length];
}
if (player.info().playerType == PlayerType.Bot) {
return botColors[simpleHash(player.id()) % botColors.length];
}
return territoryColors[simpleHash(player.id()) % territoryColors.length];
}
textColor(player: PlayerView): string {
+8 -1
View File
@@ -82,7 +82,14 @@ export class AttackExecution implements Execution {
: mg.player(this._targetID);
if (this.target && this.target.isPlayer()) {
(this.target as Player).addEmbargo(this._owner.id());
const targetPlayer = this.target as Player;
if (
targetPlayer.type() != PlayerType.Bot &&
this._owner.type() != PlayerType.Bot
) {
// Don't let bots embargo since they can't trade anyways.
targetPlayer.addEmbargo(this._owner.id());
}
}
if (this._owner == this.target) {
+1
View File
@@ -41,6 +41,7 @@ export enum Difficulty {
export enum TeamName {
Red = "Red",
Blue = "Blue",
Bot = "Bot",
}
export enum GameMapType {
+14 -6
View File
@@ -19,6 +19,7 @@ import {
Team,
GameMode,
TeamName,
PlayerType,
} from "./Game";
import { GameUpdate } from "./GameUpdates";
import { GameUpdateType } from "./GameUpdates";
@@ -74,7 +75,11 @@ export class GameImpl implements Game {
private _stats: StatsImpl = new StatsImpl();
private teams_: Team[] = [];
private playerTeams: Team[] = [
{ name: TeamName.Red },
{ name: TeamName.Blue },
];
private botTeam: Team = { name: TeamName.Bot };
constructor(
private _map: GameMap,
@@ -95,9 +100,6 @@ export class GameImpl implements Game {
),
);
this.unitGrid = new UnitGrid(this._map);
if (this._config.gameConfig().gameMode == GameMode.Team) {
this.teams_ = [{ name: TeamName.Red }, { name: TeamName.Blue }];
}
}
isOnEdgeOfMap(ref: TileRef): boolean {
return this._map.isOnEdgeOfMap(ref);
@@ -343,8 +345,11 @@ export class GameImpl implements Game {
if (this._config.gameConfig().gameMode != GameMode.Team) {
return null;
}
if (player.playerType == PlayerType.Bot) {
return this.botTeam;
}
const rand = simpleHash(player.id);
return this.teams_[rand % this.teams_.length];
return this.playerTeams[rand % this.playerTeams.length];
}
player(id: PlayerID | null): Player {
@@ -541,7 +546,10 @@ export class GameImpl implements Game {
}
teams(): Team[] {
return this.teams_;
if (this._config.gameConfig().gameMode != GameMode.Team) {
return [];
}
return [this.botTeam, ...this.playerTeams];
}
displayMessage(