mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 20:44:14 +00:00
format
This commit is contained in:
@@ -213,10 +213,10 @@ export class ClientGameRunner {
|
||||
}
|
||||
const players: PlayerRecord[] = [
|
||||
{
|
||||
persistentID: getPersistentID(),
|
||||
username: this.lobby.playerName,
|
||||
clientID: this.lobby.clientID,
|
||||
persistentID: getPersistentID(),
|
||||
stats: update.allPlayersStats[this.lobby.clientID],
|
||||
username: this.lobby.playerName,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -312,8 +312,8 @@ export class ClientGameRunner {
|
||||
}
|
||||
while (turn.turnNumber - 1 > this.turnsSeen) {
|
||||
this.worker.sendTurn({
|
||||
turnNumber: this.turnsSeen,
|
||||
intents: [],
|
||||
turnNumber: this.turnsSeen,
|
||||
});
|
||||
this.turnsSeen++;
|
||||
}
|
||||
@@ -471,9 +471,9 @@ export class ClientGameRunner {
|
||||
);
|
||||
|
||||
upgradeUnits.push({
|
||||
distance,
|
||||
unitId: bu.canUpgrade,
|
||||
unitType: bu.type,
|
||||
distance,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,16 +38,16 @@ export async function handlePurchase(priceId: string) {
|
||||
const response = await fetch(
|
||||
`${getApiBase()}/stripe/create-checkout-session`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
authorization: getAuthHeader(),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
cancelUrl: `${window.location.origin}#purchase-completed=false`,
|
||||
priceId,
|
||||
successUrl: `${window.location.origin}#purchase-completed=true`,
|
||||
cancelUrl: `${window.location.origin}#purchase-completed=false`,
|
||||
}),
|
||||
headers: {
|
||||
authorization: getAuthHeader(),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -36,9 +36,9 @@ export class FlagInput extends LitElement {
|
||||
private dispatchFlagEvent() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("flag-change", {
|
||||
detail: { flag: this.flag },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { flag: this.flag },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -87,9 +87,9 @@ export class FlagInputModal extends LitElement {
|
||||
localStorage.setItem("flag", flag);
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("flag-change", {
|
||||
detail: { flag },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { flag },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -544,12 +544,12 @@ export class HostLobbyModal extends LitElement {
|
||||
.then(() => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("join-lobby", {
|
||||
detail: {
|
||||
gameID: this.lobbyId,
|
||||
clientID: this.lobbyCreatorClientID,
|
||||
} as JoinLobbyEvent,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
clientID: this.lobbyCreatorClientID,
|
||||
gameID: this.lobbyId,
|
||||
} as JoinLobbyEvent,
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -655,24 +655,24 @@ export class HostLobbyModal extends LitElement {
|
||||
const response = await fetch(
|
||||
`${window.location.origin}/${config.workerPath(this.lobbyId)}/api/game/${this.lobbyId}`,
|
||||
{
|
||||
method: "PUT",
|
||||
body: JSON.stringify({
|
||||
bots: this.bots,
|
||||
difficulty: this.selectedDifficulty,
|
||||
disabledUnits: this.disabledUnits,
|
||||
disableNPCs: this.disableNPCs,
|
||||
donateGold: this.donateGold,
|
||||
donateTroops: this.donateTroops,
|
||||
gameMap: this.selectedMap,
|
||||
gameMode: this.gameMode,
|
||||
infiniteGold: this.infiniteGold,
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
playerTeams: this.teamCount,
|
||||
} satisfies Partial<GameConfig>),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
gameMap: this.selectedMap,
|
||||
difficulty: this.selectedDifficulty,
|
||||
disableNPCs: this.disableNPCs,
|
||||
bots: this.bots,
|
||||
infiniteGold: this.infiniteGold,
|
||||
donateGold: this.donateGold,
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
donateTroops: this.donateTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
gameMode: this.gameMode,
|
||||
disabledUnits: this.disabledUnits,
|
||||
playerTeams: this.teamCount,
|
||||
} satisfies Partial<GameConfig>),
|
||||
method: "PUT",
|
||||
},
|
||||
);
|
||||
return response;
|
||||
@@ -709,10 +709,10 @@ export class HostLobbyModal extends LitElement {
|
||||
const response = await fetch(
|
||||
`${window.location.origin}/${config.workerPath(this.lobbyId)}/api/start_game/${this.lobbyId}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
return response;
|
||||
@@ -736,10 +736,10 @@ export class HostLobbyModal extends LitElement {
|
||||
private async pollPlayers() {
|
||||
const config = await getServerConfigFromClient();
|
||||
fetch(`/${config.workerPath(this.lobbyId)}/api/game/${this.lobbyId}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "GET",
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then(GameInfoSchema.parse)
|
||||
@@ -754,9 +754,9 @@ export class HostLobbyModal extends LitElement {
|
||||
// Dispatch event to be handled by WebSocket instead of HTTP
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("kick-player", {
|
||||
detail: { target: clientID },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { target: clientID },
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -769,10 +769,10 @@ async function createLobby(creatorClientID: string): Promise<GameInfo> {
|
||||
const response = await fetch(
|
||||
`/${config.workerPath(id)}/api/create_game/${id}?creatorClientID=${encodeURIComponent(creatorClientID)}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "POST",
|
||||
// body: JSON.stringify(data), // Include this if you need to send data
|
||||
},
|
||||
);
|
||||
|
||||
@@ -145,20 +145,20 @@ export class InputHandler {
|
||||
|
||||
initialize() {
|
||||
this.keybinds = {
|
||||
toggleView: "Space",
|
||||
centerCamera: "KeyC",
|
||||
moveUp: "KeyW",
|
||||
moveDown: "KeyS",
|
||||
moveLeft: "KeyA",
|
||||
moveRight: "KeyD",
|
||||
zoomOut: "KeyQ",
|
||||
zoomIn: "KeyE",
|
||||
altKey: "AltLeft",
|
||||
attackRatioDown: "Digit1",
|
||||
attackRatioUp: "Digit2",
|
||||
boatAttack: "KeyB",
|
||||
centerCamera: "KeyC",
|
||||
groundAttack: "KeyG",
|
||||
modifierKey: "ControlLeft",
|
||||
altKey: "AltLeft",
|
||||
moveDown: "KeyS",
|
||||
moveLeft: "KeyA",
|
||||
moveRight: "KeyD",
|
||||
moveUp: "KeyW",
|
||||
toggleView: "Space",
|
||||
zoomIn: "KeyE",
|
||||
zoomOut: "KeyQ",
|
||||
...(JSON.parse(localStorage.getItem("settings.keybinds") ?? "{}") ?? {}),
|
||||
};
|
||||
|
||||
|
||||
@@ -145,9 +145,9 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
this.message = "";
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("leave-lobby", {
|
||||
detail: { lobby: this.lobbyIdInput.value },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { lobby: this.lobbyIdInput.value },
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -211,8 +211,8 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
const url = `/${config.workerPath(lobbyId)}/api/game/${lobbyId}/exists`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
const json = await response.json();
|
||||
@@ -224,12 +224,12 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("join-lobby", {
|
||||
detail: {
|
||||
gameID: lobbyId,
|
||||
clientID: getClientID(lobbyId),
|
||||
} as JoinLobbyEvent,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
clientID: getClientID(lobbyId),
|
||||
gameID: lobbyId,
|
||||
} as JoinLobbyEvent,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -245,8 +245,8 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
const archiveUrl = `/${config.workerPath(lobbyId)}/api/archived_game/${lobbyId}`;
|
||||
|
||||
const archiveResponse = await fetch(archiveUrl, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
const json = await archiveResponse.json();
|
||||
@@ -268,13 +268,13 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
if (archiveData.exists) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("join-lobby", {
|
||||
detail: {
|
||||
gameID: lobbyId,
|
||||
gameRecord: archiveData.gameRecord,
|
||||
clientID: getClientID(lobbyId),
|
||||
} as JoinLobbyEvent,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
clientID: getClientID(lobbyId),
|
||||
gameID: lobbyId,
|
||||
gameRecord: archiveData.gameRecord,
|
||||
} as JoinLobbyEvent,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -291,10 +291,10 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
fetch(
|
||||
`/${config.workerPath(this.lobbyIdInput.value)}/api/game/${this.lobbyIdInput.value}`,
|
||||
{
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method: "GET",
|
||||
},
|
||||
)
|
||||
.then((response) => response.json())
|
||||
|
||||
+18
-18
@@ -51,32 +51,32 @@ export class LangSelector extends LitElement {
|
||||
ar,
|
||||
bg,
|
||||
bn,
|
||||
cs,
|
||||
da,
|
||||
de,
|
||||
en,
|
||||
es,
|
||||
eo,
|
||||
es,
|
||||
fi,
|
||||
fr,
|
||||
it,
|
||||
gl,
|
||||
he,
|
||||
hi,
|
||||
it,
|
||||
ja,
|
||||
ko,
|
||||
nl,
|
||||
pl,
|
||||
"pt-BR": pt_BR,
|
||||
ru,
|
||||
sh,
|
||||
tr,
|
||||
tp,
|
||||
uk,
|
||||
cs,
|
||||
he,
|
||||
da,
|
||||
fi,
|
||||
"sv-SE": sv_SE,
|
||||
"zh-CN": zh_CN,
|
||||
ko,
|
||||
gl,
|
||||
sl,
|
||||
sk,
|
||||
sl,
|
||||
"sv-SE": sv_SE,
|
||||
tp,
|
||||
tr,
|
||||
uk,
|
||||
"zh-CN": zh_CN,
|
||||
};
|
||||
|
||||
createRenderRoot() {
|
||||
@@ -148,8 +148,8 @@ export class LangSelector extends LitElement {
|
||||
|
||||
list.push({
|
||||
code: langData.lang_code ?? langCode,
|
||||
native: langData.native ?? langCode,
|
||||
en: langData.en ?? langCode,
|
||||
native: langData.native ?? langCode,
|
||||
svg: langData.svg ?? langCode,
|
||||
});
|
||||
}
|
||||
@@ -159,8 +159,8 @@ export class LangSelector extends LitElement {
|
||||
if (this.debugKeyPressed) {
|
||||
debugLang = {
|
||||
code: "debug",
|
||||
native: "Debug",
|
||||
en: "Debug",
|
||||
native: "Debug",
|
||||
svg: "xx",
|
||||
};
|
||||
this.debugMode = true;
|
||||
@@ -290,13 +290,13 @@ export class LangSelector extends LitElement {
|
||||
(this.currentLang === "debug"
|
||||
? {
|
||||
code: "debug",
|
||||
native: "Debug",
|
||||
en: "Debug",
|
||||
native: "Debug",
|
||||
svg: "xx",
|
||||
}
|
||||
: {
|
||||
native: "English",
|
||||
en: "English",
|
||||
native: "English",
|
||||
svg: "uk_us_flag",
|
||||
});
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ export class LanguageModal extends LitElement {
|
||||
private readonly selectLanguage = (lang: string) => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("language-selected", {
|
||||
detail: { lang },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { lang },
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,9 +12,9 @@ import { replacer } from "../core/Util";
|
||||
const LocalStatsDataSchema = z.record(
|
||||
ID,
|
||||
z.object({
|
||||
lobby: GameConfigSchema.partial(),
|
||||
// Only once the game is over
|
||||
gameRecord: GameRecordSchema.optional(),
|
||||
lobby: GameConfigSchema.partial(),
|
||||
}),
|
||||
);
|
||||
type LocalStatsData = z.infer<typeof LocalStatsDataSchema>;
|
||||
|
||||
@@ -76,9 +76,9 @@ export class LocalServer {
|
||||
throw new Error("missing gameStartInfo");
|
||||
}
|
||||
this.clientMessage({
|
||||
type: "start",
|
||||
gameStartInfo: this.lobbyConfig.gameStartInfo,
|
||||
turns: [],
|
||||
type: "start",
|
||||
} satisfies ServerStartGameMessage);
|
||||
}
|
||||
|
||||
@@ -122,11 +122,11 @@ export class LocalServer {
|
||||
}, server hash: ${archivedHash}`,
|
||||
);
|
||||
this.clientMessage({
|
||||
type: "desync",
|
||||
turn: clientMsg.turnNumber,
|
||||
correctHash: archivedHash,
|
||||
clientsWithCorrectHash: 0,
|
||||
correctHash: archivedHash,
|
||||
totalActiveClients: 1,
|
||||
turn: clientMsg.turnNumber,
|
||||
type: "desync",
|
||||
yourHash: clientMsg.hash,
|
||||
});
|
||||
} else {
|
||||
@@ -160,14 +160,14 @@ export class LocalServer {
|
||||
this.intents = this.replayTurns[this.turns.length].intents;
|
||||
}
|
||||
const pastTurn: Turn = {
|
||||
turnNumber: this.turns.length,
|
||||
intents: this.intents,
|
||||
turnNumber: this.turns.length,
|
||||
};
|
||||
this.turns.push(pastTurn);
|
||||
this.intents = [];
|
||||
this.clientMessage({
|
||||
type: "turn",
|
||||
turn: pastTurn,
|
||||
type: "turn",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -179,10 +179,10 @@ export class LocalServer {
|
||||
}
|
||||
const players: PlayerRecord[] = [
|
||||
{
|
||||
persistentID: getPersistentID(),
|
||||
username: this.lobbyConfig.playerName,
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
persistentID: getPersistentID(),
|
||||
stats: this.allPlayersStats[this.lobbyConfig.clientID],
|
||||
username: this.lobbyConfig.playerName,
|
||||
},
|
||||
];
|
||||
if (this.lobbyConfig.gameStartInfo === undefined) {
|
||||
|
||||
+7
-7
@@ -471,18 +471,18 @@ class Client {
|
||||
this.gameStop = joinLobby(
|
||||
this.eventBus,
|
||||
{
|
||||
gameID: lobby.gameID,
|
||||
serverConfig: config,
|
||||
pattern: this.userSettings.getSelectedPattern(),
|
||||
clientID: getClientID(lobby.gameID),
|
||||
flag:
|
||||
this.flagInput === null || this.flagInput.getCurrentFlag() === "xx"
|
||||
? ""
|
||||
: this.flagInput.getCurrentFlag(),
|
||||
playerName: this.usernameInput?.getCurrentUsername() ?? "",
|
||||
token: getPlayToken(),
|
||||
clientID: getClientID(lobby.gameID),
|
||||
gameStartInfo: lobby.gameStartInfo ?? lobby.gameRecord?.info,
|
||||
gameID: lobby.gameID,
|
||||
gameRecord: lobby.gameRecord,
|
||||
gameStartInfo: lobby.gameStartInfo ?? lobby.gameRecord?.info,
|
||||
pattern: this.userSettings.getSelectedPattern(),
|
||||
playerName: this.usernameInput?.getCurrentUsername() ?? "",
|
||||
serverConfig: config,
|
||||
token: getPlayToken(),
|
||||
},
|
||||
() => {
|
||||
console.log("Closing modals");
|
||||
|
||||
@@ -74,8 +74,8 @@ export class NewsModal extends LitElement {
|
||||
<div class="news-container">
|
||||
<div class="news-content">
|
||||
${resolveMarkdown(this.markdown, {
|
||||
includeImages: true,
|
||||
includeCodeBlockClassNames: true,
|
||||
includeImages: true,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -206,20 +206,20 @@ export class PublicLobby extends LitElement {
|
||||
this.currLobby = lobby;
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("join-lobby", {
|
||||
detail: {
|
||||
gameID: lobby.gameID,
|
||||
clientID: getClientID(lobby.gameID),
|
||||
} as JoinLobbyEvent,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
clientID: getClientID(lobby.gameID),
|
||||
gameID: lobby.gameID,
|
||||
} as JoinLobbyEvent,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("leave-lobby", {
|
||||
detail: { lobby: this.currLobby },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { lobby: this.currLobby },
|
||||
}),
|
||||
);
|
||||
this.leaveLobby();
|
||||
|
||||
@@ -439,43 +439,43 @@ export class SinglePlayerModal extends LitElement {
|
||||
}
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("join-lobby", {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
clientID,
|
||||
gameID,
|
||||
gameStartInfo: {
|
||||
config: {
|
||||
bots: this.bots,
|
||||
difficulty: this.selectedDifficulty,
|
||||
disabledUnits: this.disabledUnits
|
||||
.map((u) => Object.values(UnitType).find((ut) => ut === u))
|
||||
.filter((ut): ut is UnitType => ut !== undefined),
|
||||
disableNPCs: this.disableNPCs,
|
||||
donateGold: this.donateGold,
|
||||
donateTroops: this.donateTroops,
|
||||
gameMap: this.selectedMap,
|
||||
gameMode: this.gameMode,
|
||||
gameType: GameType.Singleplayer,
|
||||
infiniteGold: this.infiniteGold,
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
playerTeams: this.teamCount,
|
||||
},
|
||||
gameID,
|
||||
players: [
|
||||
{
|
||||
clientID,
|
||||
username: usernameInput.getCurrentUsername(),
|
||||
flag:
|
||||
flagInput.getCurrentFlag() === "xx"
|
||||
? ""
|
||||
: flagInput.getCurrentFlag(),
|
||||
pattern: this.userSettings.getSelectedPattern(),
|
||||
username: usernameInput.getCurrentUsername(),
|
||||
},
|
||||
],
|
||||
config: {
|
||||
gameMap: this.selectedMap,
|
||||
gameType: GameType.Singleplayer,
|
||||
gameMode: this.gameMode,
|
||||
playerTeams: this.teamCount,
|
||||
difficulty: this.selectedDifficulty,
|
||||
disableNPCs: this.disableNPCs,
|
||||
bots: this.bots,
|
||||
infiniteGold: this.infiniteGold,
|
||||
donateGold: this.donateGold,
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
donateTroops: this.donateTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
disabledUnits: this.disabledUnits
|
||||
.map((u) => Object.values(UnitType).find((ut) => ut === u))
|
||||
.filter((ut): ut is UnitType => ut !== undefined),
|
||||
},
|
||||
},
|
||||
} satisfies JoinLobbyEvent,
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
this.close();
|
||||
|
||||
+40
-40
@@ -375,14 +375,14 @@ export class Transport {
|
||||
|
||||
joinGame(numTurns: number) {
|
||||
this.sendMsg({
|
||||
type: "join",
|
||||
gameID: this.lobbyConfig.gameID,
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
lastTurn: numTurns,
|
||||
token: this.lobbyConfig.token,
|
||||
username: this.lobbyConfig.playerName,
|
||||
flag: this.lobbyConfig.flag,
|
||||
gameID: this.lobbyConfig.gameID,
|
||||
lastTurn: numTurns,
|
||||
pattern: this.lobbyConfig.pattern,
|
||||
token: this.lobbyConfig.token,
|
||||
type: "join",
|
||||
username: this.lobbyConfig.playerName,
|
||||
} satisfies ClientJoinMessage);
|
||||
}
|
||||
|
||||
@@ -408,26 +408,26 @@ export class Transport {
|
||||
|
||||
private onSendAllianceRequest(event: SendAllianceRequestIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "allianceRequest",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
recipient: event.recipient.id(),
|
||||
type: "allianceRequest",
|
||||
});
|
||||
}
|
||||
|
||||
private onAllianceRequestReplyUIEvent(event: SendAllianceReplyIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "allianceRequestReply",
|
||||
accept: event.accepted,
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
requestor: event.requestor.id(),
|
||||
accept: event.accepted,
|
||||
type: "allianceRequestReply",
|
||||
});
|
||||
}
|
||||
|
||||
private onBreakAllianceRequestUIEvent(event: SendBreakAllianceIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "breakAlliance",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
recipient: event.recipient.id(),
|
||||
type: "breakAlliance",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -435,114 +435,114 @@ export class Transport {
|
||||
event: SendAllianceExtensionIntentEvent,
|
||||
) {
|
||||
this.sendIntent({
|
||||
type: "allianceExtension",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
recipient: event.recipient.id(),
|
||||
type: "allianceExtension",
|
||||
});
|
||||
}
|
||||
|
||||
private onSendSpawnIntentEvent(event: SendSpawnIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "spawn",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
flag: this.lobbyConfig.flag,
|
||||
pattern: this.lobbyConfig.pattern,
|
||||
name: this.lobbyConfig.playerName,
|
||||
pattern: this.lobbyConfig.pattern,
|
||||
playerType: PlayerType.Human,
|
||||
tile: event.tile,
|
||||
type: "spawn",
|
||||
});
|
||||
}
|
||||
|
||||
private onSendAttackIntent(event: SendAttackIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "attack",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
targetID: event.targetID,
|
||||
troops: event.troops,
|
||||
type: "attack",
|
||||
});
|
||||
}
|
||||
|
||||
private onSendBoatAttackIntent(event: SendBoatAttackIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "boat",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
targetID: event.targetID,
|
||||
troops: event.troops,
|
||||
dst: event.dst,
|
||||
src: event.src,
|
||||
targetID: event.targetID,
|
||||
troops: event.troops,
|
||||
type: "boat",
|
||||
});
|
||||
}
|
||||
|
||||
private onSendUpgradeStructureIntent(event: SendUpgradeStructureIntentEvent) {
|
||||
this.sendIntent({
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
type: "upgrade_structure",
|
||||
unit: event.unitType,
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
unitId: event.unitId,
|
||||
});
|
||||
}
|
||||
|
||||
private onSendTargetPlayerIntent(event: SendTargetPlayerIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "targetPlayer",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
target: event.targetID,
|
||||
type: "targetPlayer",
|
||||
});
|
||||
}
|
||||
|
||||
private onSendEmojiIntent(event: SendEmojiIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "emoji",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
emoji: event.emoji,
|
||||
recipient:
|
||||
event.recipient === AllPlayers ? AllPlayers : event.recipient.id(),
|
||||
emoji: event.emoji,
|
||||
type: "emoji",
|
||||
});
|
||||
}
|
||||
|
||||
private onSendDonateGoldIntent(event: SendDonateGoldIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "donate_gold",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
recipient: event.recipient.id(),
|
||||
gold: event.gold,
|
||||
recipient: event.recipient.id(),
|
||||
type: "donate_gold",
|
||||
});
|
||||
}
|
||||
|
||||
private onSendDonateTroopIntent(event: SendDonateTroopsIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "donate_troops",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
recipient: event.recipient.id(),
|
||||
troops: event.troops,
|
||||
type: "donate_troops",
|
||||
});
|
||||
}
|
||||
|
||||
private onSendQuickChatIntent(event: SendQuickChatEvent) {
|
||||
this.sendIntent({
|
||||
type: "quick_chat",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
recipient: event.recipient.id(),
|
||||
quickChatKey: event.quickChatKey,
|
||||
recipient: event.recipient.id(),
|
||||
target: event.target,
|
||||
type: "quick_chat",
|
||||
});
|
||||
}
|
||||
|
||||
private onSendEmbargoIntent(event: SendEmbargoIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "embargo",
|
||||
action: event.action,
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
targetID: event.target.id(),
|
||||
action: event.action,
|
||||
type: "embargo",
|
||||
});
|
||||
}
|
||||
|
||||
private onBuildUnitIntent(event: BuildUnitIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "build_unit",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
unit: event.unit,
|
||||
tile: event.tile,
|
||||
type: "build_unit",
|
||||
unit: event.unit,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -561,9 +561,9 @@ export class Transport {
|
||||
private onSendWinnerEvent(event: SendWinnerEvent) {
|
||||
if (this.isLocal || this.socket?.readyState === WebSocket.OPEN) {
|
||||
this.sendMsg({
|
||||
allPlayersStats: event.allPlayersStats,
|
||||
type: "winner",
|
||||
winner: event.winner,
|
||||
allPlayersStats: event.allPlayersStats,
|
||||
} satisfies ClientSendWinnerMessage);
|
||||
} else {
|
||||
console.log(
|
||||
@@ -577,9 +577,9 @@ export class Transport {
|
||||
private onSendHashEvent(event: SendHashEvent) {
|
||||
if (this.isLocal || this.socket?.readyState === WebSocket.OPEN) {
|
||||
this.sendMsg({
|
||||
type: "hash",
|
||||
turnNumber: event.tick,
|
||||
hash: event.hash,
|
||||
turnNumber: event.tick,
|
||||
type: "hash",
|
||||
} satisfies ClientHashMessage);
|
||||
} else {
|
||||
console.log(
|
||||
@@ -592,50 +592,50 @@ export class Transport {
|
||||
|
||||
private onCancelAttackIntentEvent(event: CancelAttackIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "cancel_attack",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
attackID: event.attackID,
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
type: "cancel_attack",
|
||||
});
|
||||
}
|
||||
|
||||
private onCancelBoatIntentEvent(event: CancelBoatIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "cancel_boat",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
type: "cancel_boat",
|
||||
unitID: event.unitID,
|
||||
});
|
||||
}
|
||||
|
||||
private onMoveWarshipEvent(event: MoveWarshipIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "move_warship",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
unitId: event.unitId,
|
||||
tile: event.tile,
|
||||
type: "move_warship",
|
||||
unitId: event.unitId,
|
||||
});
|
||||
}
|
||||
|
||||
private onSendDeleteUnitIntent(event: SendDeleteUnitIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "delete_unit",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
type: "delete_unit",
|
||||
unitId: event.unitId,
|
||||
});
|
||||
}
|
||||
|
||||
private onSendKickPlayerIntent(event: SendKickPlayerIntentEvent) {
|
||||
this.sendIntent({
|
||||
type: "kick_player",
|
||||
clientID: this.lobbyConfig.clientID,
|
||||
target: event.target,
|
||||
type: "kick_player",
|
||||
});
|
||||
}
|
||||
|
||||
private sendIntent(intent: Intent) {
|
||||
if (this.isLocal || this.socket?.readyState === WebSocket.OPEN) {
|
||||
const msg = {
|
||||
type: "intent",
|
||||
intent,
|
||||
type: "intent",
|
||||
} satisfies ClientIntentMessage;
|
||||
this.sendMsg(msg);
|
||||
} else {
|
||||
|
||||
@@ -100,9 +100,9 @@ export class UserSettingModal extends LitElement {
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("dark-mode-changed", {
|
||||
detail: { darkMode: enabled },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { darkMode: enabled },
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -93,9 +93,9 @@ export class UsernameInput extends LitElement {
|
||||
private dispatchUsernameEvent() {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("username-change", {
|
||||
detail: { username: this.username },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { username: this.username },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
+4
-4
@@ -150,11 +150,11 @@ export const translateText = (
|
||||
* Severity colors mapping for message types
|
||||
*/
|
||||
export const severityColors: Record<string, string> = {
|
||||
fail: "text-red-400",
|
||||
warn: "text-yellow-400",
|
||||
success: "text-green-400",
|
||||
info: "text-gray-200",
|
||||
blue: "text-blue-400",
|
||||
fail: "text-red-400",
|
||||
info: "text-gray-200",
|
||||
success: "text-green-400",
|
||||
warn: "text-yellow-400",
|
||||
white: "text-white",
|
||||
};
|
||||
|
||||
|
||||
@@ -6,35 +6,35 @@ import { translateText } from "../Utils";
|
||||
|
||||
// Add map descriptions
|
||||
export const MapDescription: Record<keyof typeof GameMapType, string> = {
|
||||
World: "World",
|
||||
GiantWorldMap: "Giant World Map",
|
||||
Africa: "Africa",
|
||||
Asia: "Asia",
|
||||
Australia: "Australia",
|
||||
Baikal: "Baikal",
|
||||
BetweenTwoSeas: "Between Two Seas",
|
||||
BlackSea: "Black Sea",
|
||||
Britannia: "Britannia",
|
||||
DeglaciatedAntarctica: "Deglaciated Antarctica",
|
||||
EastAsia: "East Asia",
|
||||
Europe: "Europe",
|
||||
EuropeClassic: "Europe Classic",
|
||||
FalklandIslands: "Falkland Islands",
|
||||
FaroeIslands: "Faroe Islands",
|
||||
GatewayToTheAtlantic: "Gateway to the Atlantic",
|
||||
GiantWorldMap: "Giant World Map",
|
||||
Halkidiki: "Halkidiki",
|
||||
Iceland: "Iceland",
|
||||
Italia: "Italia",
|
||||
Mars: "Mars",
|
||||
MarsRevised: "Mars Revised",
|
||||
Mena: "MENA",
|
||||
NorthAmerica: "North America",
|
||||
Oceania: "Oceania",
|
||||
BlackSea: "Black Sea",
|
||||
Africa: "Africa",
|
||||
Pangaea: "Pangaea",
|
||||
Asia: "Asia",
|
||||
Mars: "Mars",
|
||||
MarsRevised: "Mars Revised",
|
||||
SouthAmerica: "South America",
|
||||
Britannia: "Britannia",
|
||||
GatewayToTheAtlantic: "Gateway to the Atlantic",
|
||||
Australia: "Australia",
|
||||
Iceland: "Iceland",
|
||||
EastAsia: "East Asia",
|
||||
BetweenTwoSeas: "Between Two Seas",
|
||||
FaroeIslands: "Faroe Islands",
|
||||
DeglaciatedAntarctica: "Deglaciated Antarctica",
|
||||
FalklandIslands: "Falkland Islands",
|
||||
Baikal: "Baikal",
|
||||
Halkidiki: "Halkidiki",
|
||||
StraitOfGibraltar: "Strait of Gibraltar",
|
||||
Italia: "Italia",
|
||||
Yenisei: "Yenisei",
|
||||
Pluto: "Pluto",
|
||||
SouthAmerica: "South America",
|
||||
StraitOfGibraltar: "Strait of Gibraltar",
|
||||
World: "World",
|
||||
Yenisei: "Yenisei",
|
||||
};
|
||||
|
||||
@customElement("map-display")
|
||||
|
||||
@@ -23,8 +23,8 @@ export class OButton extends LitElement {
|
||||
"c-button": true,
|
||||
"c-button--block": this.block,
|
||||
"c-button--blockDesktop": this.blockDesktop,
|
||||
"c-button--secondary": this.secondary,
|
||||
"c-button--disabled": this.disable,
|
||||
"c-button--secondary": this.secondary,
|
||||
})}
|
||||
?disabled=${this.disable}
|
||||
>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { translateText } from "../../../../client/Utils";
|
||||
export class SettingKeybind extends LitElement {
|
||||
@property() label = "Setting";
|
||||
@property() description = "";
|
||||
@property({ type: String, reflect: true }) action = "";
|
||||
@property({ reflect: true, type: String }) action = "";
|
||||
@property({ type: String }) defaultKey = "";
|
||||
@property({ type: String }) value = "";
|
||||
@property({ type: Boolean }) easter = false;
|
||||
@@ -80,9 +80,9 @@ export class SettingKeybind extends LitElement {
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("change", {
|
||||
detail: { action: this.action, value: code },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { action: this.action, value: code },
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -94,9 +94,9 @@ export class SettingKeybind extends LitElement {
|
||||
this.value = this.defaultKey;
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("change", {
|
||||
detail: { action: this.action, value: this.defaultKey },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { action: this.action, value: this.defaultKey },
|
||||
}),
|
||||
);
|
||||
}
|
||||
@@ -105,9 +105,9 @@ export class SettingKeybind extends LitElement {
|
||||
this.value = "";
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("change", {
|
||||
detail: { action: this.action, value: "Null" },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { action: this.action, value: "Null" },
|
||||
}),
|
||||
);
|
||||
this.requestUpdate();
|
||||
|
||||
@@ -21,9 +21,9 @@ export class SettingNumber extends LitElement {
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("change", {
|
||||
detail: { value: newValue },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { value: newValue },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ export class SettingSlider extends LitElement {
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("change", {
|
||||
detail: { value: this.value },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { value: this.value },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ export class SettingToggle extends LitElement {
|
||||
@property() label = "Setting";
|
||||
@property() description = "";
|
||||
@property() id = "";
|
||||
@property({ type: Boolean, reflect: true }) checked = false;
|
||||
@property({ reflect: true, type: Boolean }) checked = false;
|
||||
@property({ type: Boolean }) easter = false;
|
||||
|
||||
createRenderRoot() {
|
||||
@@ -18,9 +18,9 @@ export class SettingToggle extends LitElement {
|
||||
this.checked = input.checked;
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("change", {
|
||||
detail: { checked: this.checked },
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: { checked: this.checked },
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,103 +27,103 @@ type AnimatedSpriteConfig = {
|
||||
|
||||
const ANIMATED_SPRITE_CONFIG: Partial<Record<FxType, AnimatedSpriteConfig>> = {
|
||||
[FxType.MiniFire]: {
|
||||
url: miniFire,
|
||||
frameWidth: 7,
|
||||
frameCount: 6,
|
||||
frameDuration: 100,
|
||||
frameWidth: 7,
|
||||
looping: true,
|
||||
originX: 3,
|
||||
originY: 11,
|
||||
url: miniFire,
|
||||
},
|
||||
[FxType.MiniSmoke]: {
|
||||
url: miniSmoke,
|
||||
frameWidth: 11,
|
||||
frameCount: 4,
|
||||
frameDuration: 120,
|
||||
frameWidth: 11,
|
||||
looping: true,
|
||||
originX: 2,
|
||||
originY: 10,
|
||||
url: miniSmoke,
|
||||
},
|
||||
[FxType.MiniBigSmoke]: {
|
||||
url: miniBigSmoke,
|
||||
frameWidth: 24,
|
||||
frameCount: 5,
|
||||
frameDuration: 120,
|
||||
frameWidth: 24,
|
||||
looping: true,
|
||||
originX: 9,
|
||||
originY: 14,
|
||||
url: miniBigSmoke,
|
||||
},
|
||||
[FxType.MiniSmokeAndFire]: {
|
||||
url: miniSmokeAndFire,
|
||||
frameWidth: 24,
|
||||
frameCount: 5,
|
||||
frameDuration: 120,
|
||||
frameWidth: 24,
|
||||
looping: true,
|
||||
originX: 9,
|
||||
originY: 14,
|
||||
url: miniSmokeAndFire,
|
||||
},
|
||||
[FxType.MiniExplosion]: {
|
||||
url: miniExplosion,
|
||||
frameWidth: 13,
|
||||
frameCount: 4,
|
||||
frameDuration: 70,
|
||||
frameWidth: 13,
|
||||
looping: false,
|
||||
originX: 6,
|
||||
originY: 6,
|
||||
url: miniExplosion,
|
||||
},
|
||||
[FxType.Dust]: {
|
||||
url: dust,
|
||||
frameWidth: 9,
|
||||
frameCount: 3,
|
||||
frameDuration: 100,
|
||||
frameWidth: 9,
|
||||
looping: false,
|
||||
originX: 4,
|
||||
originY: 5,
|
||||
url: dust,
|
||||
},
|
||||
[FxType.UnitExplosion]: {
|
||||
url: unitExplosion,
|
||||
frameWidth: 19,
|
||||
frameCount: 4,
|
||||
frameDuration: 70,
|
||||
frameWidth: 19,
|
||||
looping: false,
|
||||
originX: 9,
|
||||
originY: 9,
|
||||
url: unitExplosion,
|
||||
},
|
||||
[FxType.SinkingShip]: {
|
||||
url: sinkingShip,
|
||||
frameWidth: 16,
|
||||
frameCount: 14,
|
||||
frameDuration: 90,
|
||||
frameWidth: 16,
|
||||
looping: false,
|
||||
originX: 7,
|
||||
originY: 7,
|
||||
url: sinkingShip,
|
||||
},
|
||||
[FxType.Nuke]: {
|
||||
url: nuke,
|
||||
frameWidth: 60,
|
||||
frameCount: 9,
|
||||
frameDuration: 70,
|
||||
frameWidth: 60,
|
||||
looping: false,
|
||||
originX: 30,
|
||||
originY: 30,
|
||||
url: nuke,
|
||||
},
|
||||
[FxType.SAMExplosion]: {
|
||||
url: SAMExplosion,
|
||||
frameWidth: 48,
|
||||
frameCount: 9,
|
||||
frameDuration: 70,
|
||||
frameWidth: 48,
|
||||
looping: false,
|
||||
originX: 23,
|
||||
originY: 19,
|
||||
url: SAMExplosion,
|
||||
},
|
||||
[FxType.Conquest]: {
|
||||
url: conquestSword,
|
||||
frameWidth: 21,
|
||||
frameCount: 10,
|
||||
frameDuration: 90,
|
||||
frameWidth: 21,
|
||||
looping: false,
|
||||
originX: 10,
|
||||
originY: 16,
|
||||
url: conquestSword,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -56,9 +56,9 @@ export function placeName(game: Game, player: Player): NameViewData {
|
||||
center = new Cell(center.x, center.y - fontSize / 3);
|
||||
|
||||
return {
|
||||
size: fontSize,
|
||||
x: Math.ceil(center.x),
|
||||
y: Math.ceil(center.y),
|
||||
size: fontSize,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -69,14 +69,14 @@ export function createGrid(
|
||||
scalingFactor: number,
|
||||
): boolean[][] {
|
||||
const scaledBoundingBox: { min: Point; max: Point } = {
|
||||
min: {
|
||||
x: Math.floor(boundingBox.min.x / scalingFactor),
|
||||
y: Math.floor(boundingBox.min.y / scalingFactor),
|
||||
},
|
||||
max: {
|
||||
x: Math.floor(boundingBox.max.x / scalingFactor),
|
||||
y: Math.floor(boundingBox.max.y / scalingFactor),
|
||||
},
|
||||
min: {
|
||||
x: Math.floor(boundingBox.min.x / scalingFactor),
|
||||
y: Math.floor(boundingBox.min.y / scalingFactor),
|
||||
},
|
||||
};
|
||||
|
||||
const width = scaledBoundingBox.max.x - scaledBoundingBox.min.x + 1;
|
||||
@@ -105,7 +105,7 @@ export function findLargestInscribedRectangle(grid: boolean[][]): Rectangle {
|
||||
const rows = grid[0].length;
|
||||
const cols = grid.length;
|
||||
const heights: number[] = new Array<number>(cols).fill(0);
|
||||
let largestRect: Rectangle = { x: 0, y: 0, width: 0, height: 0 };
|
||||
let largestRect: Rectangle = { height: 0, width: 0, x: 0, y: 0 };
|
||||
|
||||
for (let row = 0; row < rows; row++) {
|
||||
for (let col = 0; col < cols; col++) {
|
||||
@@ -123,10 +123,10 @@ export function findLargestInscribedRectangle(grid: boolean[][]): Rectangle {
|
||||
largestRect.width * largestRect.height
|
||||
) {
|
||||
largestRect = {
|
||||
height: rectForRow.height,
|
||||
width: rectForRow.width,
|
||||
x: rectForRow.x,
|
||||
y: row - rectForRow.height + 1,
|
||||
width: rectForRow.width,
|
||||
height: rectForRow.height,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -137,7 +137,7 @@ export function findLargestInscribedRectangle(grid: boolean[][]): Rectangle {
|
||||
export function largestRectangleInHistogram(widths: number[]): Rectangle {
|
||||
const stack: number[] = [];
|
||||
let maxArea = 0;
|
||||
let largestRect: Rectangle = { x: 0, y: 0, width: 0, height: 0 };
|
||||
let largestRect: Rectangle = { height: 0, width: 0, x: 0, y: 0 };
|
||||
|
||||
for (let i = 0; i <= widths.length; i++) {
|
||||
const h = i === widths.length ? 0 : widths[i];
|
||||
@@ -151,10 +151,10 @@ export function largestRectangleInHistogram(widths: number[]): Rectangle {
|
||||
if (height * width > maxArea) {
|
||||
maxArea = height * width;
|
||||
largestRect = {
|
||||
height,
|
||||
width,
|
||||
x: stack.length === 0 ? 0 : stack[stack.length - 1] + 1,
|
||||
y: 0,
|
||||
width,
|
||||
height,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ import { UnitView } from "../../core/game/GameView";
|
||||
|
||||
// Can't reuse TrainType because "loaded" is not a type, just an attribute
|
||||
const TrainTypeSprite = {
|
||||
Engine: "Engine",
|
||||
Carriage: "Carriage",
|
||||
Engine: "Engine",
|
||||
LoadedCarriage: "LoadedCarriage",
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -88,10 +88,10 @@ export function nukeFxFactory(
|
||||
radiusFactor: number;
|
||||
density: number;
|
||||
}> = [
|
||||
{ type: FxType.MiniFire, radiusFactor: 1.0, density: 1 / 25 },
|
||||
{ type: FxType.MiniSmoke, radiusFactor: 1.0, density: 1 / 28 },
|
||||
{ type: FxType.MiniBigSmoke, radiusFactor: 0.9, density: 1 / 70 },
|
||||
{ type: FxType.MiniSmokeAndFire, radiusFactor: 0.9, density: 1 / 70 },
|
||||
{ density: 1 / 25, radiusFactor: 1.0, type: FxType.MiniFire },
|
||||
{ density: 1 / 28, radiusFactor: 1.0, type: FxType.MiniSmoke },
|
||||
{ density: 1 / 70, radiusFactor: 0.9, type: FxType.MiniBigSmoke },
|
||||
{ density: 1 / 70, radiusFactor: 0.9, type: FxType.MiniSmokeAndFire },
|
||||
];
|
||||
|
||||
for (const { type, radiusFactor, density } of debrisPlan) {
|
||||
|
||||
@@ -11,9 +11,9 @@ export class TextFx implements Fx {
|
||||
private readonly riseDistance = 30,
|
||||
private readonly font = "11px sans-serif",
|
||||
private readonly color: { r: number; g: number; b: number } = {
|
||||
r: 255,
|
||||
g: 255,
|
||||
b: 255,
|
||||
g: 255,
|
||||
r: 255,
|
||||
},
|
||||
) {}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ export class Timeline {
|
||||
private timeElapsed = 0;
|
||||
|
||||
add(delay: number, action: () => void): Timeline {
|
||||
this.tasks.push({ delay, action, triggered: false });
|
||||
this.tasks.push({ action, delay, triggered: false });
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ export class UnitExplosionFx implements Fx {
|
||||
game: GameView,
|
||||
) {
|
||||
const config = [
|
||||
{ dx: 0, dy: 0, delay: 0, type: FxType.UnitExplosion },
|
||||
{ dx: 4, dy: -6, delay: 80, type: FxType.UnitExplosion },
|
||||
{ dx: -6, dy: 4, delay: 160, type: FxType.UnitExplosion },
|
||||
{ delay: 0, dx: 0, dy: 0, type: FxType.UnitExplosion },
|
||||
{ delay: 80, dx: 4, dy: -6, type: FxType.UnitExplosion },
|
||||
{ delay: 160, dx: -6, dy: 4, type: FxType.UnitExplosion },
|
||||
];
|
||||
for (const { dx, dy, delay, type } of config) {
|
||||
this.timeline.add(delay, () => {
|
||||
|
||||
@@ -46,75 +46,75 @@ export type BuildItemDisplay = {
|
||||
export const buildTable: BuildItemDisplay[][] = [
|
||||
[
|
||||
{
|
||||
unitType: UnitType.AtomBomb,
|
||||
icon: atomBombIcon,
|
||||
countable: false,
|
||||
description: "build_menu.desc.atom_bomb",
|
||||
icon: atomBombIcon,
|
||||
key: "unit_type.atom_bomb",
|
||||
countable: false,
|
||||
unitType: UnitType.AtomBomb,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.MIRV,
|
||||
icon: mirvIcon,
|
||||
countable: false,
|
||||
description: "build_menu.desc.mirv",
|
||||
icon: mirvIcon,
|
||||
key: "unit_type.mirv",
|
||||
countable: false,
|
||||
unitType: UnitType.MIRV,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.HydrogenBomb,
|
||||
icon: hydrogenBombIcon,
|
||||
countable: false,
|
||||
description: "build_menu.desc.hydrogen_bomb",
|
||||
icon: hydrogenBombIcon,
|
||||
key: "unit_type.hydrogen_bomb",
|
||||
countable: false,
|
||||
unitType: UnitType.HydrogenBomb,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.Warship,
|
||||
icon: warshipIcon,
|
||||
countable: true,
|
||||
description: "build_menu.desc.warship",
|
||||
icon: warshipIcon,
|
||||
key: "unit_type.warship",
|
||||
countable: true,
|
||||
unitType: UnitType.Warship,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.Port,
|
||||
icon: portIcon,
|
||||
countable: true,
|
||||
description: "build_menu.desc.port",
|
||||
icon: portIcon,
|
||||
key: "unit_type.port",
|
||||
countable: true,
|
||||
unitType: UnitType.Port,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.MissileSilo,
|
||||
icon: missileSiloIcon,
|
||||
description: "build_menu.desc.missile_silo",
|
||||
key: "unit_type.missile_silo",
|
||||
countable: true,
|
||||
description: "build_menu.desc.missile_silo",
|
||||
icon: missileSiloIcon,
|
||||
key: "unit_type.missile_silo",
|
||||
unitType: UnitType.MissileSilo,
|
||||
},
|
||||
// needs new icon
|
||||
{
|
||||
unitType: UnitType.SAMLauncher,
|
||||
icon: samlauncherIcon,
|
||||
countable: true,
|
||||
description: "build_menu.desc.sam_launcher",
|
||||
icon: samlauncherIcon,
|
||||
key: "unit_type.sam_launcher",
|
||||
countable: true,
|
||||
unitType: UnitType.SAMLauncher,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.DefensePost,
|
||||
icon: shieldIcon,
|
||||
countable: true,
|
||||
description: "build_menu.desc.defense_post",
|
||||
icon: shieldIcon,
|
||||
key: "unit_type.defense_post",
|
||||
countable: true,
|
||||
unitType: UnitType.DefensePost,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.City,
|
||||
icon: cityIcon,
|
||||
countable: true,
|
||||
description: "build_menu.desc.city",
|
||||
icon: cityIcon,
|
||||
key: "unit_type.city",
|
||||
countable: true,
|
||||
unitType: UnitType.City,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.Factory,
|
||||
icon: factoryIcon,
|
||||
description: "build_menu.desc.factory",
|
||||
key: "unit_type.factory",
|
||||
countable: true,
|
||||
description: "build_menu.desc.factory",
|
||||
icon: factoryIcon,
|
||||
key: "unit_type.factory",
|
||||
unitType: UnitType.Factory,
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
@@ -65,8 +65,8 @@ export class ChatDisplay extends LitElement implements Layer {
|
||||
}
|
||||
|
||||
this.addEvent({
|
||||
description: event.message,
|
||||
createdAt: this.game.ticks(),
|
||||
description: event.message,
|
||||
highlight: true,
|
||||
unsafeDescription: true,
|
||||
});
|
||||
@@ -97,9 +97,9 @@ export class ChatDisplay extends LitElement implements Layer {
|
||||
this.chatEvents = [
|
||||
...this.chatEvents,
|
||||
{
|
||||
createdAt: this.game.ticks(),
|
||||
description: msg.message,
|
||||
unsafeDescription: true,
|
||||
createdAt: this.game.ticks(),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -49,18 +49,6 @@ export class ChatIntegration {
|
||||
const phraseText = translateText(`chat.${category.id}.${phrase.key}`);
|
||||
|
||||
return {
|
||||
id: `phrase-${category.id}-${phrase.key}`,
|
||||
name: phraseText,
|
||||
disabled: () => false,
|
||||
text: this.shortenText(phraseText),
|
||||
fontSize: "10px",
|
||||
color: categoryColor,
|
||||
tooltipItems: [
|
||||
{
|
||||
text: phraseText,
|
||||
className: "description",
|
||||
},
|
||||
],
|
||||
action: (params: MenuElementParams) => {
|
||||
if (phrase.requiresPlayer) {
|
||||
this.ctModal.openWithSelection(
|
||||
@@ -79,18 +67,30 @@ export class ChatIntegration {
|
||||
);
|
||||
}
|
||||
},
|
||||
color: categoryColor,
|
||||
disabled: () => false,
|
||||
fontSize: "10px",
|
||||
id: `phrase-${category.id}-${phrase.key}`,
|
||||
name: phraseText,
|
||||
text: this.shortenText(phraseText),
|
||||
tooltipItems: [
|
||||
{
|
||||
className: "description",
|
||||
text: phraseText,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
_action: () => {}, // Empty action placeholder for RadialMenu
|
||||
color: categoryColor,
|
||||
disabled: () => false,
|
||||
id: `chat-category-${category.id}`,
|
||||
name: categoryTranslation,
|
||||
disabled: () => false,
|
||||
text: categoryTranslation,
|
||||
color: categoryColor,
|
||||
_action: () => {}, // Empty action placeholder for RadialMenu
|
||||
subMenu: () => phraseItems,
|
||||
text: categoryTranslation,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -49,11 +49,11 @@ export class ChatModal extends LitElement {
|
||||
string,
|
||||
Array<{ text: string; requiresPlayer: boolean }>
|
||||
> = {
|
||||
help: [{ text: "Please give me troops!", requiresPlayer: false }],
|
||||
attack: [{ text: "Attack [P1]!", requiresPlayer: true }],
|
||||
defend: [{ text: "Defend [P1]!", requiresPlayer: true }],
|
||||
greet: [{ text: "Hello!", requiresPlayer: false }],
|
||||
misc: [{ text: "Let's go!", requiresPlayer: false }],
|
||||
attack: [{ requiresPlayer: true, text: "Attack [P1]!" }],
|
||||
defend: [{ requiresPlayer: true, text: "Defend [P1]!" }],
|
||||
greet: [{ requiresPlayer: false, text: "Hello!" }],
|
||||
help: [{ requiresPlayer: false, text: "Please give me troops!" }],
|
||||
misc: [{ requiresPlayer: false, text: "Let's go!" }],
|
||||
};
|
||||
|
||||
public categories = [
|
||||
|
||||
@@ -279,35 +279,35 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
if (!other.isAlive()) continue;
|
||||
|
||||
this.addEvent({
|
||||
description: translateText("events_display.about_to_expire", {
|
||||
name: other.name(),
|
||||
}),
|
||||
type: MessageType.RENEW_ALLIANCE,
|
||||
duration: this.game.config().allianceExtensionPromptOffset() - 3 * 10, // 3 second buffer
|
||||
buttons: [
|
||||
{
|
||||
text: translateText("events_display.focus"),
|
||||
className: "btn-gray",
|
||||
action: () => this.eventBus?.emit(new GoToPlayerEvent(other)),
|
||||
className: "btn-gray",
|
||||
preventClose: true,
|
||||
text: translateText("events_display.focus"),
|
||||
},
|
||||
{
|
||||
action: () =>
|
||||
this.eventBus?.emit(new SendAllianceExtensionIntentEvent(other)),
|
||||
className: "btn",
|
||||
text: translateText("events_display.renew_alliance", {
|
||||
name: other.name(),
|
||||
}),
|
||||
className: "btn",
|
||||
action: () =>
|
||||
this.eventBus?.emit(new SendAllianceExtensionIntentEvent(other)),
|
||||
},
|
||||
{
|
||||
text: translateText("events_display.ignore"),
|
||||
className: "btn-info",
|
||||
action: () => {},
|
||||
className: "btn-info",
|
||||
text: translateText("events_display.ignore"),
|
||||
},
|
||||
],
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
description: translateText("events_display.about_to_expire", {
|
||||
name: other.name(),
|
||||
}),
|
||||
duration: this.game.config().allianceExtensionPromptOffset() - 3 * 10, // 3 second buffer
|
||||
focusID: other.smallID(),
|
||||
highlight: true,
|
||||
type: MessageType.RENEW_ALLIANCE,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -374,8 +374,8 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
}
|
||||
|
||||
this.addEvent({
|
||||
description,
|
||||
createdAt: this.game.ticks(),
|
||||
description,
|
||||
highlight: true,
|
||||
type: event.messageType,
|
||||
unsafeDescription: true,
|
||||
@@ -417,11 +417,11 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
}
|
||||
|
||||
this.addEvent({
|
||||
description: translateText(event.isFrom ? "chat.from" : "chat.to", {
|
||||
user: otherPlayerDiplayName,
|
||||
msg: translatedMessage,
|
||||
}),
|
||||
createdAt: this.game.ticks(),
|
||||
description: translateText(event.isFrom ? "chat.from" : "chat.to", {
|
||||
msg: translatedMessage,
|
||||
user: otherPlayerDiplayName,
|
||||
}),
|
||||
highlight: true,
|
||||
type: MessageType.CHAT,
|
||||
unsafeDescription: false,
|
||||
@@ -443,43 +443,43 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
) as PlayerView;
|
||||
|
||||
this.addEvent({
|
||||
description: translateText("events_display.request_alliance", {
|
||||
name: requestor.name(),
|
||||
}),
|
||||
buttons: [
|
||||
{
|
||||
text: translateText("events_display.focus"),
|
||||
className: "btn-gray",
|
||||
action: () => this.eventBus?.emit(new GoToPlayerEvent(requestor)),
|
||||
className: "btn-gray",
|
||||
preventClose: true,
|
||||
text: translateText("events_display.focus"),
|
||||
},
|
||||
{
|
||||
text: translateText("events_display.accept_alliance"),
|
||||
className: "btn",
|
||||
action: () =>
|
||||
this.eventBus?.emit(
|
||||
new SendAllianceReplyIntentEvent(requestor, recipient, true),
|
||||
),
|
||||
className: "btn",
|
||||
text: translateText("events_display.accept_alliance"),
|
||||
},
|
||||
{
|
||||
text: translateText("events_display.reject_alliance"),
|
||||
className: "btn-info",
|
||||
action: () =>
|
||||
this.eventBus?.emit(
|
||||
new SendAllianceReplyIntentEvent(requestor, recipient, false),
|
||||
),
|
||||
className: "btn-info",
|
||||
text: translateText("events_display.reject_alliance"),
|
||||
},
|
||||
],
|
||||
highlight: true,
|
||||
type: MessageType.ALLIANCE_REQUEST,
|
||||
createdAt: this.game.ticks(),
|
||||
description: translateText("events_display.request_alliance", {
|
||||
name: requestor.name(),
|
||||
}),
|
||||
duration: 150,
|
||||
focusID: update.requestorID,
|
||||
highlight: true,
|
||||
onDelete: () =>
|
||||
this.eventBus?.emit(
|
||||
new SendAllianceReplyIntentEvent(requestor, recipient, false),
|
||||
),
|
||||
priority: 0,
|
||||
duration: 150,
|
||||
focusID: update.requestorID,
|
||||
type: MessageType.ALLIANCE_REQUEST,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -511,18 +511,18 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
update.request.recipientID,
|
||||
) as PlayerView;
|
||||
this.addEvent({
|
||||
createdAt: this.game.ticks(),
|
||||
description: translateText("events_display.alliance_request_status", {
|
||||
name: recipient.name(),
|
||||
status: update.accepted
|
||||
? translateText("events_display.alliance_accepted")
|
||||
: translateText("events_display.alliance_rejected"),
|
||||
}),
|
||||
focusID: update.request.recipientID,
|
||||
highlight: true,
|
||||
type: update.accepted
|
||||
? MessageType.ALLIANCE_ACCEPTED
|
||||
: MessageType.ALLIANCE_REJECTED,
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
focusID: update.request.recipientID,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -552,34 +552,34 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
});
|
||||
|
||||
this.addEvent({
|
||||
description: translateText("events_display.betrayal_description", {
|
||||
name: betrayed.name(),
|
||||
malusPercent,
|
||||
durationText,
|
||||
}),
|
||||
type: MessageType.ALLIANCE_BROKEN,
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
description: translateText("events_display.betrayal_description", {
|
||||
durationText,
|
||||
malusPercent,
|
||||
name: betrayed.name(),
|
||||
}),
|
||||
focusID: update.betrayedID,
|
||||
highlight: true,
|
||||
type: MessageType.ALLIANCE_BROKEN,
|
||||
});
|
||||
} else if (betrayed === myPlayer) {
|
||||
const buttons = [
|
||||
{
|
||||
text: translateText("events_display.focus"),
|
||||
className: "btn-gray",
|
||||
action: () => this.eventBus?.emit(new GoToPlayerEvent(traitor)),
|
||||
className: "btn-gray",
|
||||
preventClose: true,
|
||||
text: translateText("events_display.focus"),
|
||||
},
|
||||
];
|
||||
this.addEvent({
|
||||
buttons,
|
||||
createdAt: this.game.ticks(),
|
||||
description: translateText("events_display.betrayed_you", {
|
||||
name: traitor.name(),
|
||||
}),
|
||||
type: MessageType.ALLIANCE_BROKEN,
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
focusID: update.traitorID,
|
||||
buttons,
|
||||
highlight: true,
|
||||
type: MessageType.ALLIANCE_BROKEN,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -600,13 +600,13 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
if (!other || !myPlayer.isAlive() || !other.isAlive()) return;
|
||||
|
||||
this.addEvent({
|
||||
createdAt: this.game.ticks(),
|
||||
description: translateText("events_display.alliance_expired", {
|
||||
name: other.name(),
|
||||
}),
|
||||
type: MessageType.ALLIANCE_EXPIRED,
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
focusID: otherID,
|
||||
highlight: true,
|
||||
type: MessageType.ALLIANCE_EXPIRED,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -619,14 +619,14 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
const target = this.game.playerBySmallID(event.targetID) as PlayerView;
|
||||
|
||||
this.addEvent({
|
||||
createdAt: this.game.ticks(),
|
||||
description: translateText("events_display.attack_request", {
|
||||
name: other.name(),
|
||||
target: target.name(),
|
||||
}),
|
||||
type: MessageType.ATTACK_REQUEST,
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
focusID: event.targetID,
|
||||
highlight: true,
|
||||
type: MessageType.ATTACK_REQUEST,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -674,24 +674,24 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
|
||||
if (recipient === myPlayer) {
|
||||
this.addEvent({
|
||||
description: `${sender.displayName()}: ${update.emoji.message}`,
|
||||
unsafeDescription: true,
|
||||
type: MessageType.CHAT,
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
description: `${sender.displayName()}: ${update.emoji.message}`,
|
||||
focusID: update.emoji.senderID,
|
||||
highlight: true,
|
||||
type: MessageType.CHAT,
|
||||
unsafeDescription: true,
|
||||
});
|
||||
} else if (sender === myPlayer && recipient !== AllPlayers) {
|
||||
this.addEvent({
|
||||
description: translateText("events_display.sent_emoji", {
|
||||
name: (recipient as PlayerView).displayName(),
|
||||
emoji: update.emoji.message,
|
||||
}),
|
||||
unsafeDescription: true,
|
||||
type: MessageType.CHAT,
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
description: translateText("events_display.sent_emoji", {
|
||||
emoji: update.emoji.message,
|
||||
name: (recipient as PlayerView).displayName(),
|
||||
}),
|
||||
focusID: recipient.smallID(),
|
||||
highlight: true,
|
||||
type: MessageType.CHAT,
|
||||
unsafeDescription: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -707,12 +707,12 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
const unitView = this.game.unit(event.unitID);
|
||||
|
||||
this.addEvent({
|
||||
description: event.message,
|
||||
type: event.messageType,
|
||||
unsafeDescription: false,
|
||||
highlight: true,
|
||||
createdAt: this.game.ticks(),
|
||||
description: event.message,
|
||||
highlight: true,
|
||||
type: event.messageType,
|
||||
unitView,
|
||||
unsafeDescription: false,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -754,6 +754,7 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
const attacker = this.game?.playerBySmallID(attack.attackerID);
|
||||
return html`
|
||||
${this.renderButton({
|
||||
className: "text-left text-red-400",
|
||||
content: html`
|
||||
${renderTroops(attack.troops)}
|
||||
${attacker?.isPlayer() ? attacker.name() : "unknown"}
|
||||
@@ -764,7 +765,6 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
}
|
||||
`,
|
||||
onClick: () => this.attackWarningOnClick(attack),
|
||||
className: "text-left text-red-400",
|
||||
translate: false,
|
||||
})}
|
||||
`;
|
||||
@@ -786,22 +786,22 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
return html`
|
||||
<div class="inline-flex items-center gap-1">
|
||||
${this.renderButton({
|
||||
className: "text-left text-blue-400",
|
||||
content: html`
|
||||
${renderTroops(attack.troops)}
|
||||
${target?.isPlayer() ? target.name() : "unknown"}
|
||||
`,
|
||||
onClick: async () => this.attackWarningOnClick(attack),
|
||||
className: "text-left text-blue-400",
|
||||
translate: false,
|
||||
})}
|
||||
${
|
||||
!attack.retreating
|
||||
? this.renderButton({
|
||||
className: "text-left flex-shrink-0",
|
||||
content: "❌",
|
||||
disabled: attack.retreating,
|
||||
onClick: () =>
|
||||
this.emitCancelAttackIntent(attack.id),
|
||||
className: "text-left flex-shrink-0",
|
||||
disabled: attack.retreating,
|
||||
})
|
||||
: html`<span class="flex-shrink-0 text-blue-400"
|
||||
>(${translateText(
|
||||
@@ -829,19 +829,19 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
(landAttack) => html`
|
||||
<div class="inline-flex items-center gap-1">
|
||||
${this.renderButton({
|
||||
className: "text-left text-gray-400",
|
||||
content: html`${renderTroops(landAttack.troops)}
|
||||
${translateText("help_modal.ui_wilderness")}`,
|
||||
className: "text-left text-gray-400",
|
||||
translate: false,
|
||||
})}
|
||||
${
|
||||
!landAttack.retreating
|
||||
? this.renderButton({
|
||||
className: "text-left flex-shrink-0",
|
||||
content: "❌",
|
||||
disabled: landAttack.retreating,
|
||||
onClick: () =>
|
||||
this.emitCancelAttackIntent(landAttack.id),
|
||||
className: "text-left flex-shrink-0",
|
||||
disabled: landAttack.retreating,
|
||||
})
|
||||
: html`<span class="flex-shrink-0 text-blue-400"
|
||||
>(${translateText(
|
||||
@@ -869,19 +869,19 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
(boat) => html`
|
||||
<div class="inline-flex items-center gap-1">
|
||||
${this.renderButton({
|
||||
className: "text-left text-blue-400",
|
||||
content: html`${translateText("events_display.boat")}:
|
||||
${renderTroops(boat.troops())}`,
|
||||
onClick: () => this.emitGoToUnitEvent(boat),
|
||||
className: "text-left text-blue-400",
|
||||
translate: false,
|
||||
})}
|
||||
${
|
||||
!boat.retreating()
|
||||
? this.renderButton({
|
||||
content: "❌",
|
||||
onClick: () => this.emitBoatCancelIntent(boat.id()),
|
||||
className: "text-left flex-shrink-0",
|
||||
content: "❌",
|
||||
disabled: boat.retreating(),
|
||||
onClick: () => this.emitBoatCancelIntent(boat.id()),
|
||||
})
|
||||
: html`<span class="flex-shrink-0 text-blue-400"
|
||||
>(${translateText(
|
||||
@@ -948,6 +948,9 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
? html`
|
||||
<div class="relative w-fit lg:bottom-2.5 lg:right-2.5 z-50">
|
||||
${this.renderButton({
|
||||
className:
|
||||
"text-white cursor-pointer pointer-events-auto w-fit p-2 " +
|
||||
"lg:p-3 rounded-md bg-gray-800/70 backdrop-blur",
|
||||
content: html`
|
||||
Events
|
||||
<span
|
||||
@@ -958,9 +961,6 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
>
|
||||
`,
|
||||
onClick: this.toggleHidden,
|
||||
className:
|
||||
"text-white cursor-pointer pointer-events-auto w-fit p-2 " +
|
||||
"lg:p-3 rounded-md bg-gray-800/70 backdrop-blur",
|
||||
})}
|
||||
</div>
|
||||
`
|
||||
@@ -976,6 +976,7 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex gap-4">
|
||||
${this.renderButton({
|
||||
className: "cursor-pointer pointer-events-auto",
|
||||
content: html`<img
|
||||
src="${swordIcon}"
|
||||
class="w-5 h-5"
|
||||
@@ -987,9 +988,9 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
/>`,
|
||||
onClick: () =>
|
||||
this.toggleEventFilter(MessageCategory.ATTACK),
|
||||
className: "cursor-pointer pointer-events-auto",
|
||||
})}
|
||||
${this.renderButton({
|
||||
className: "cursor-pointer pointer-events-auto",
|
||||
content: html`<img
|
||||
src="${donateGoldIcon}"
|
||||
class="w-5 h-5"
|
||||
@@ -1001,9 +1002,9 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
/>`,
|
||||
onClick: () =>
|
||||
this.toggleEventFilter(MessageCategory.TRADE),
|
||||
className: "cursor-pointer pointer-events-auto",
|
||||
})}
|
||||
${this.renderButton({
|
||||
className: "cursor-pointer pointer-events-auto",
|
||||
content: html`<img
|
||||
src="${allianceIcon}"
|
||||
class="w-5 h-5"
|
||||
@@ -1015,9 +1016,9 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
/>`,
|
||||
onClick: () =>
|
||||
this.toggleEventFilter(MessageCategory.ALLIANCE),
|
||||
className: "cursor-pointer pointer-events-auto",
|
||||
})}
|
||||
${this.renderButton({
|
||||
className: "cursor-pointer pointer-events-auto",
|
||||
content: html`<img
|
||||
src="${chatIcon}"
|
||||
class="w-5 h-5"
|
||||
@@ -1029,7 +1030,6 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
/>`,
|
||||
onClick: () =>
|
||||
this.toggleEventFilter(MessageCategory.CHAT),
|
||||
className: "cursor-pointer pointer-events-auto",
|
||||
})}
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
@@ -1051,10 +1051,10 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
: ""
|
||||
}
|
||||
${this.renderButton({
|
||||
content: translateText("leaderboard.hide"),
|
||||
onClick: this.toggleHidden,
|
||||
className:
|
||||
"text-white cursor-pointer pointer-events-auto",
|
||||
content: translateText("leaderboard.hide"),
|
||||
onClick: this.toggleHidden,
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1083,6 +1083,7 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
${
|
||||
event.focusID
|
||||
? this.renderButton({
|
||||
className: "text-left",
|
||||
content: this.getEventDescription(event),
|
||||
onClick: () => {
|
||||
event.focusID &&
|
||||
@@ -1090,10 +1091,10 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
event.focusID,
|
||||
);
|
||||
},
|
||||
className: "text-left",
|
||||
})
|
||||
: event.unitView
|
||||
? this.renderButton({
|
||||
className: "text-left",
|
||||
content:
|
||||
this.getEventDescription(event),
|
||||
onClick: () => {
|
||||
@@ -1102,7 +1103,6 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
event.unitView,
|
||||
);
|
||||
},
|
||||
className: "text-left",
|
||||
})
|
||||
: this.getEventDescription(event)
|
||||
}
|
||||
|
||||
@@ -96,12 +96,12 @@ export class GutterAdModal extends LitElement implements Layer {
|
||||
window.ramp.que.push(() => {
|
||||
window.ramp.spaAddAds([
|
||||
{
|
||||
type: this.leftAdType,
|
||||
selectorId: this.leftContainerId,
|
||||
type: this.leftAdType,
|
||||
},
|
||||
{
|
||||
type: this.rightAdType,
|
||||
selectorId: this.rightContainerId,
|
||||
type: this.rightAdType,
|
||||
},
|
||||
]);
|
||||
this.adLoaded = true;
|
||||
|
||||
@@ -107,15 +107,15 @@ export class Leaderboard extends LitElement implements Layer {
|
||||
troops = 0;
|
||||
}
|
||||
return {
|
||||
gold: renderNumber(player.gold()),
|
||||
isMyPlayer: player === myPlayer,
|
||||
name: player.displayName(),
|
||||
player,
|
||||
position: index + 1,
|
||||
score: formatPercentage(
|
||||
player.numTilesOwned() / numTilesWithoutFallout,
|
||||
),
|
||||
gold: renderNumber(player.gold()),
|
||||
troops: renderNumber(troops),
|
||||
isMyPlayer: player === myPlayer,
|
||||
player,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -137,15 +137,15 @@ export class Leaderboard extends LitElement implements Layer {
|
||||
}
|
||||
this.players.pop();
|
||||
this.players.push({
|
||||
gold: renderNumber(myPlayer.gold()),
|
||||
isMyPlayer: true,
|
||||
name: myPlayer.displayName(),
|
||||
player: myPlayer,
|
||||
position: place,
|
||||
score: formatPercentage(
|
||||
myPlayer.numTilesOwned() / this.game.numLandTiles(),
|
||||
),
|
||||
gold: renderNumber(myPlayer.gold()),
|
||||
troops: renderNumber(myPlayerTroops),
|
||||
isMyPlayer: true,
|
||||
player: myPlayer,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -114,18 +114,18 @@ export class MainRadialMenu extends LitElement implements Layer {
|
||||
}
|
||||
|
||||
const params: MenuElementParams = {
|
||||
myPlayer,
|
||||
selected: recipient,
|
||||
tile,
|
||||
playerActions: actions,
|
||||
game: this.game,
|
||||
buildMenu: this.buildMenu,
|
||||
emojiTable: this.emojiTable,
|
||||
playerActionHandler: this.playerActionHandler,
|
||||
playerPanel: this.playerPanel,
|
||||
chatIntegration: this.chatIntegration,
|
||||
closeMenu: () => this.closeMenu(),
|
||||
emojiTable: this.emojiTable,
|
||||
eventBus: this.eventBus,
|
||||
game: this.game,
|
||||
myPlayer,
|
||||
playerActionHandler: this.playerActionHandler,
|
||||
playerActions: actions,
|
||||
playerPanel: this.playerPanel,
|
||||
selected: recipient,
|
||||
tile,
|
||||
};
|
||||
|
||||
this.radialMenu.setParams(params);
|
||||
|
||||
@@ -181,10 +181,10 @@ export class OptionsMenu extends LitElement implements Layer {
|
||||
>
|
||||
<div class="flex items-stretch gap-1 lg:gap-2">
|
||||
${button({
|
||||
children: this.isPaused ? "▶️" : "⏸",
|
||||
classes: !this.showPauseButton ? "hidden" : "",
|
||||
onClick: this.onPauseButtonClick,
|
||||
title: this.isPaused ? "Resume game" : "Pause game",
|
||||
children: this.isPaused ? "▶️" : "⏸",
|
||||
})}
|
||||
<div
|
||||
class="w-[55px] h-8 lg:w-24 lg:h-10 flex items-center justify-center
|
||||
@@ -194,14 +194,14 @@ export class OptionsMenu extends LitElement implements Layer {
|
||||
${secondsToHms(this.timer)}
|
||||
</div>
|
||||
${button({
|
||||
children: "❌",
|
||||
onClick: this.onExitButtonClick,
|
||||
title: "Exit game",
|
||||
children: "❌",
|
||||
})}
|
||||
${button({
|
||||
children: "⚙️",
|
||||
onClick: this.onSettingsButtonClick,
|
||||
title: "Settings",
|
||||
children: "⚙️",
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
@@ -212,65 +212,65 @@ export class OptionsMenu extends LitElement implements Layer {
|
||||
${!this.showSettings ? "hidden" : ""}"
|
||||
>
|
||||
${button({
|
||||
children: "🌲: " + (this.alternateView ? "On" : "Off"),
|
||||
onClick: this.onTerrainButtonClick,
|
||||
title: "Toggle Terrain",
|
||||
children: "🌲: " + (this.alternateView ? "On" : "Off"),
|
||||
})}
|
||||
${button({
|
||||
children: "🙂: " + (this.userSettings.emojis() ? "On" : "Off"),
|
||||
onClick: this.onToggleEmojisButtonClick,
|
||||
title: "Toggle Emojis",
|
||||
children: "🙂: " + (this.userSettings.emojis() ? "On" : "Off"),
|
||||
})}
|
||||
${button({
|
||||
children: "🚨: " + (this.userSettings.alertFrame() ? "On" : "Off"),
|
||||
onClick: this.onToggleAlertFrameButtonClick,
|
||||
title: "Toggle Alert frame",
|
||||
children: "🚨: " + (this.userSettings.alertFrame() ? "On" : "Off"),
|
||||
})}
|
||||
${button({
|
||||
children: "💥: " + (this.userSettings.fxLayer() ? "On" : "Off"),
|
||||
onClick: this.onToggleSpecialEffectsButtonClick,
|
||||
title: "Toggle Special effects",
|
||||
children: "💥: " + (this.userSettings.fxLayer() ? "On" : "Off"),
|
||||
})}
|
||||
${button({
|
||||
onClick: this.onToggleTerritoryPatterns,
|
||||
title: "Territory Patterns",
|
||||
children:
|
||||
"🏳️: " + (this.userSettings.territoryPatterns() ? "On" : "Off"),
|
||||
onClick: this.onToggleTerritoryPatterns,
|
||||
title: "Territory Patterns",
|
||||
})}
|
||||
${button({
|
||||
children: "🌙: " + (this.userSettings.darkMode() ? "On" : "Off"),
|
||||
onClick: this.onToggleDarkModeButtonClick,
|
||||
title: "Dark Mode",
|
||||
children: "🌙: " + (this.userSettings.darkMode() ? "On" : "Off"),
|
||||
})}
|
||||
${button({
|
||||
onClick: this.onToggleRandomNameModeButtonClick,
|
||||
title: "Random name mode",
|
||||
children:
|
||||
"🥷: " + (this.userSettings.anonymousNames() ? "On" : "Off"),
|
||||
onClick: this.onToggleRandomNameModeButtonClick,
|
||||
title: "Random name mode",
|
||||
})}
|
||||
${button({
|
||||
onClick: this.onToggleLeftClickOpensMenu,
|
||||
title: "Left click",
|
||||
children:
|
||||
"🖱️: " +
|
||||
(this.userSettings.leftClickOpensMenu()
|
||||
? "Opens menu"
|
||||
: "Attack"),
|
||||
onClick: this.onToggleLeftClickOpensMenu,
|
||||
title: "Left click",
|
||||
})}
|
||||
${button({
|
||||
onClick: this.onTogglePerformanceOverlayButtonClick,
|
||||
title: "Performance Overlay",
|
||||
children:
|
||||
"🚀: " + (this.userSettings.performanceOverlay() ? "On" : "Off"),
|
||||
onClick: this.onTogglePerformanceOverlayButtonClick,
|
||||
title: "Performance Overlay",
|
||||
})}
|
||||
<!-- ${button({
|
||||
onClick: this.onToggleFocusLockedButtonClick,
|
||||
title: "Lock Focus",
|
||||
children:
|
||||
"🗺: " +
|
||||
(this.userSettings.focusLocked()
|
||||
? "Focus locked"
|
||||
: "Hover focus"),
|
||||
onClick: this.onToggleFocusLockedButtonClick,
|
||||
title: "Lock Focus",
|
||||
})} -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -91,17 +91,17 @@ export class RadialMenu implements Layer {
|
||||
config: RadialMenuConfig = {},
|
||||
) {
|
||||
this.config = {
|
||||
menuSize: config.menuSize ?? 190,
|
||||
submenuScale: config.submenuScale ?? 1.5,
|
||||
centerButtonIcon: config.centerButtonIcon ?? "",
|
||||
centerButtonSize: config.centerButtonSize ?? 30,
|
||||
iconSize: config.iconSize ?? 32,
|
||||
centerIconSize: config.centerIconSize ?? 48,
|
||||
disabledColor: config.disabledColor ?? d3.rgb(128, 128, 128).toString(),
|
||||
menuTransitionDuration: config.menuTransitionDuration ?? 300,
|
||||
mainMenuInnerRadius: config.mainMenuInnerRadius ?? 40,
|
||||
centerButtonIcon: config.centerButtonIcon ?? "",
|
||||
maxNestedLevels: config.maxNestedLevels ?? 3,
|
||||
iconSize: config.iconSize ?? 32,
|
||||
innerRadiusIncrement: config.innerRadiusIncrement ?? 20,
|
||||
mainMenuInnerRadius: config.mainMenuInnerRadius ?? 40,
|
||||
maxNestedLevels: config.maxNestedLevels ?? 3,
|
||||
menuSize: config.menuSize ?? 190,
|
||||
menuTransitionDuration: config.menuTransitionDuration ?? 300,
|
||||
submenuScale: config.submenuScale ?? 1.5,
|
||||
tooltipStyle: config.tooltipStyle ?? "",
|
||||
};
|
||||
this.originalCenterButtonIcon = this.config.centerButtonIcon;
|
||||
|
||||
@@ -67,32 +67,32 @@ export type CenterButtonElement = {
|
||||
};
|
||||
|
||||
export const COLORS = {
|
||||
ally: "#53ac75",
|
||||
attack: "#ff0000",
|
||||
boat: "#3f6ab1",
|
||||
breakAlly: "#c74848",
|
||||
build: "#ebe250",
|
||||
building: "#2c2c2c",
|
||||
boat: "#3f6ab1",
|
||||
ally: "#53ac75",
|
||||
breakAlly: "#c74848",
|
||||
chat: {
|
||||
attack: "#f44336",
|
||||
default: "#66c",
|
||||
defend: "#2196f3",
|
||||
greet: "#ff9800",
|
||||
help: "#4caf50",
|
||||
misc: "#9c27b0",
|
||||
warnings: "#e3c532",
|
||||
},
|
||||
delete: "#ff0000",
|
||||
embargo: "#6600cc",
|
||||
info: "#64748B",
|
||||
target: "#ff0000",
|
||||
attack: "#ff0000",
|
||||
infoDetails: "#7f8c8d",
|
||||
infoEmoji: "#f1c40f",
|
||||
trade: "#008080",
|
||||
embargo: "#6600cc",
|
||||
target: "#ff0000",
|
||||
tooltip: {
|
||||
cost: "#ffd700",
|
||||
count: "#aaa",
|
||||
},
|
||||
chat: {
|
||||
default: "#66c",
|
||||
help: "#4caf50",
|
||||
attack: "#f44336",
|
||||
defend: "#2196f3",
|
||||
greet: "#ff9800",
|
||||
misc: "#9c27b0",
|
||||
warnings: "#e3c532",
|
||||
},
|
||||
trade: "#008080",
|
||||
};
|
||||
|
||||
export enum Slot {
|
||||
@@ -107,11 +107,11 @@ export enum Slot {
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
const infoChatElement: MenuElement = {
|
||||
color: COLORS.chat.default,
|
||||
disabled: () => false,
|
||||
icon: chatIcon,
|
||||
id: "info_chat",
|
||||
name: "chat",
|
||||
disabled: () => false,
|
||||
color: COLORS.chat.default,
|
||||
icon: chatIcon,
|
||||
subMenu: (params: MenuElementParams) =>
|
||||
params.chatIntegration
|
||||
.createQuickChatMenu(params.selected!)
|
||||
@@ -124,59 +124,51 @@ const infoChatElement: MenuElement = {
|
||||
};
|
||||
|
||||
const allyTargetElement: MenuElement = {
|
||||
id: "ally_target",
|
||||
name: "target",
|
||||
disabled: (params: MenuElementParams): boolean => {
|
||||
if (params.selected === null) return true;
|
||||
return !params.playerActions.interaction?.canTarget;
|
||||
},
|
||||
color: COLORS.target,
|
||||
icon: targetIcon,
|
||||
action: (params: MenuElementParams) => {
|
||||
params.playerActionHandler.handleTargetPlayer(params.selected!.id());
|
||||
params.closeMenu();
|
||||
},
|
||||
color: COLORS.target,
|
||||
disabled: (params: MenuElementParams): boolean => {
|
||||
if (params.selected === null) return true;
|
||||
return !params.playerActions.interaction?.canTarget;
|
||||
},
|
||||
icon: targetIcon,
|
||||
id: "ally_target",
|
||||
name: "target",
|
||||
};
|
||||
|
||||
const allyTradeElement: MenuElement = {
|
||||
id: "ally_trade",
|
||||
name: "trade",
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!!params.playerActions?.interaction?.canEmbargo,
|
||||
displayed: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canEmbargo,
|
||||
color: COLORS.trade,
|
||||
text: translateText("player_panel.start_trade"),
|
||||
action: (params: MenuElementParams) => {
|
||||
params.playerActionHandler.handleEmbargo(params.selected!, "stop");
|
||||
params.closeMenu();
|
||||
},
|
||||
color: COLORS.trade,
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!!params.playerActions?.interaction?.canEmbargo,
|
||||
displayed: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canEmbargo,
|
||||
id: "ally_trade",
|
||||
name: "trade",
|
||||
text: translateText("player_panel.start_trade"),
|
||||
};
|
||||
|
||||
const allyEmbargoElement: MenuElement = {
|
||||
id: "ally_embargo",
|
||||
name: "embargo",
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canEmbargo,
|
||||
displayed: (params: MenuElementParams) =>
|
||||
!!params.playerActions?.interaction?.canEmbargo,
|
||||
color: COLORS.embargo,
|
||||
text: translateText("player_panel.stop_trade"),
|
||||
action: (params: MenuElementParams) => {
|
||||
params.playerActionHandler.handleEmbargo(params.selected!, "start");
|
||||
params.closeMenu();
|
||||
},
|
||||
color: COLORS.embargo,
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canEmbargo,
|
||||
displayed: (params: MenuElementParams) =>
|
||||
!!params.playerActions?.interaction?.canEmbargo,
|
||||
id: "ally_embargo",
|
||||
name: "embargo",
|
||||
text: translateText("player_panel.stop_trade"),
|
||||
};
|
||||
|
||||
const allyRequestElement: MenuElement = {
|
||||
id: "ally_request",
|
||||
name: "request",
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canSendAllianceRequest,
|
||||
displayed: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canBreakAlliance,
|
||||
color: COLORS.ally,
|
||||
icon: allianceIcon,
|
||||
action: (params: MenuElementParams) => {
|
||||
params.playerActionHandler.handleAllianceRequest(
|
||||
params.myPlayer,
|
||||
@@ -184,17 +176,17 @@ const allyRequestElement: MenuElement = {
|
||||
);
|
||||
params.closeMenu();
|
||||
},
|
||||
color: COLORS.ally,
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canSendAllianceRequest,
|
||||
displayed: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canBreakAlliance,
|
||||
icon: allianceIcon,
|
||||
id: "ally_request",
|
||||
name: "request",
|
||||
};
|
||||
|
||||
const allyBreakElement: MenuElement = {
|
||||
id: "ally_break",
|
||||
name: "break",
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canBreakAlliance,
|
||||
displayed: (params: MenuElementParams) =>
|
||||
!!params.playerActions?.interaction?.canBreakAlliance,
|
||||
color: COLORS.breakAlly,
|
||||
icon: traitorIcon,
|
||||
action: (params: MenuElementParams) => {
|
||||
params.playerActionHandler.handleBreakAlliance(
|
||||
params.myPlayer,
|
||||
@@ -202,59 +194,62 @@ const allyBreakElement: MenuElement = {
|
||||
);
|
||||
params.closeMenu();
|
||||
},
|
||||
color: COLORS.breakAlly,
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canBreakAlliance,
|
||||
displayed: (params: MenuElementParams) =>
|
||||
!!params.playerActions?.interaction?.canBreakAlliance,
|
||||
icon: traitorIcon,
|
||||
id: "ally_break",
|
||||
name: "break",
|
||||
};
|
||||
|
||||
const allyDonateGoldElement: MenuElement = {
|
||||
id: "ally_donate_gold",
|
||||
name: "donate gold",
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canDonateGold,
|
||||
color: COLORS.ally,
|
||||
icon: donateGoldIcon,
|
||||
action: (params: MenuElementParams) => {
|
||||
params.playerActionHandler.handleDonateGold(params.selected!);
|
||||
params.closeMenu();
|
||||
},
|
||||
color: COLORS.ally,
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canDonateGold,
|
||||
icon: donateGoldIcon,
|
||||
id: "ally_donate_gold",
|
||||
name: "donate gold",
|
||||
};
|
||||
|
||||
const allyDonateTroopsElement: MenuElement = {
|
||||
id: "ally_donate_troops",
|
||||
name: "donate troops",
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canDonateTroops,
|
||||
color: COLORS.ally,
|
||||
icon: donateTroopIcon,
|
||||
action: (params: MenuElementParams) => {
|
||||
params.playerActionHandler.handleDonateTroops(params.selected!);
|
||||
params.closeMenu();
|
||||
},
|
||||
color: COLORS.ally,
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions?.interaction?.canDonateTroops,
|
||||
icon: donateTroopIcon,
|
||||
id: "ally_donate_troops",
|
||||
name: "donate troops",
|
||||
};
|
||||
|
||||
const infoPlayerElement: MenuElement = {
|
||||
id: "info_player",
|
||||
name: "player",
|
||||
disabled: () => false,
|
||||
color: COLORS.info,
|
||||
icon: infoIcon,
|
||||
action: (params: MenuElementParams) => {
|
||||
params.playerPanel.show(params.playerActions, params.tile);
|
||||
},
|
||||
color: COLORS.info,
|
||||
disabled: () => false,
|
||||
icon: infoIcon,
|
||||
id: "info_player",
|
||||
name: "player",
|
||||
};
|
||||
|
||||
const infoEmojiElement: MenuElement = {
|
||||
color: COLORS.infoEmoji,
|
||||
disabled: () => false,
|
||||
icon: emojiIcon,
|
||||
id: "info_emoji",
|
||||
name: "emoji",
|
||||
disabled: () => false,
|
||||
color: COLORS.infoEmoji,
|
||||
icon: emojiIcon,
|
||||
subMenu: (params: MenuElementParams) => {
|
||||
const emojiElements: MenuElement[] = [
|
||||
{
|
||||
id: "emoji_more",
|
||||
name: "more",
|
||||
disabled: () => false,
|
||||
color: COLORS.infoEmoji,
|
||||
icon: emojiIcon,
|
||||
action: (params: MenuElementParams) => {
|
||||
params.emojiTable.showTable((emoji) => {
|
||||
const targetPlayer =
|
||||
@@ -268,17 +263,17 @@ const infoEmojiElement: MenuElement = {
|
||||
params.emojiTable.hideTable();
|
||||
});
|
||||
},
|
||||
color: COLORS.infoEmoji,
|
||||
disabled: () => false,
|
||||
icon: emojiIcon,
|
||||
id: "emoji_more",
|
||||
name: "more",
|
||||
},
|
||||
];
|
||||
|
||||
const emojiCount = 8;
|
||||
for (let i = 0; i < emojiCount; i++) {
|
||||
emojiElements.push({
|
||||
id: `emoji_${i}`,
|
||||
name: flattenedEmojiTable[i],
|
||||
text: flattenedEmojiTable[i],
|
||||
disabled: () => false,
|
||||
fontSize: "25px",
|
||||
action: (params: MenuElementParams) => {
|
||||
const targetPlayer =
|
||||
params.selected === params.game.myPlayer()
|
||||
@@ -287,6 +282,11 @@ const infoEmojiElement: MenuElement = {
|
||||
params.playerActionHandler.handleEmoji(targetPlayer!, i);
|
||||
params.closeMenu();
|
||||
},
|
||||
disabled: () => false,
|
||||
fontSize: "25px",
|
||||
id: `emoji_${i}`,
|
||||
name: flattenedEmojiTable[i],
|
||||
text: flattenedEmojiTable[i],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -296,15 +296,15 @@ const infoEmojiElement: MenuElement = {
|
||||
/* eslint-enable @typescript-eslint/no-non-null-assertion */
|
||||
|
||||
export const infoMenuElement: MenuElement = {
|
||||
id: Slot.Info,
|
||||
name: "info",
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.selected || params.game.inSpawnPhase(),
|
||||
icon: infoIcon,
|
||||
color: COLORS.info,
|
||||
action: (params: MenuElementParams) => {
|
||||
params.playerPanel.show(params.playerActions, params.tile);
|
||||
},
|
||||
color: COLORS.info,
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.selected || params.game.inSpawnPhase(),
|
||||
icon: infoIcon,
|
||||
id: Slot.Info,
|
||||
name: "info",
|
||||
};
|
||||
|
||||
function getAllEnabledUnits(myPlayer: boolean, config: Config): Set<UnitType> {
|
||||
@@ -359,34 +359,6 @@ function createMenuElements(
|
||||
: !ATTACK_UNIT_TYPES.includes(item.unitType)),
|
||||
)
|
||||
.map((item: BuildItemDisplay) => ({
|
||||
id: `${elementIdPrefix}_${item.unitType}`,
|
||||
name: item.key
|
||||
? item.key.replace("unit_type.", "")
|
||||
: item.unitType.toString(),
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.buildMenu.canBuildOrUpgrade(item),
|
||||
color: params.buildMenu.canBuildOrUpgrade(item)
|
||||
? filterType === "attack"
|
||||
? COLORS.attack
|
||||
: COLORS.building
|
||||
: undefined,
|
||||
icon: item.icon,
|
||||
tooltipItems: [
|
||||
{ text: translateText(item.key ?? ""), className: "title" },
|
||||
{
|
||||
text: translateText(item.description ?? ""),
|
||||
className: "description",
|
||||
},
|
||||
{
|
||||
text: `${renderNumber(params.buildMenu.cost(item))} ${translateText("player_panel.gold")}`,
|
||||
className: "cost",
|
||||
},
|
||||
item.countable
|
||||
? { text: `${params.buildMenu.count(item)}x`, className: "count" }
|
||||
: null,
|
||||
].filter(
|
||||
(tooltipItem): tooltipItem is TooltipItem => tooltipItem !== null,
|
||||
),
|
||||
action: (params: MenuElementParams) => {
|
||||
const buildableUnit = params.playerActions.buildableUnits.find(
|
||||
(bu) => bu.type === item.unitType,
|
||||
@@ -399,15 +371,43 @@ function createMenuElements(
|
||||
}
|
||||
params.closeMenu();
|
||||
},
|
||||
color: params.buildMenu.canBuildOrUpgrade(item)
|
||||
? filterType === "attack"
|
||||
? COLORS.attack
|
||||
: COLORS.building
|
||||
: undefined,
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.buildMenu.canBuildOrUpgrade(item),
|
||||
icon: item.icon,
|
||||
id: `${elementIdPrefix}_${item.unitType}`,
|
||||
name: item.key
|
||||
? item.key.replace("unit_type.", "")
|
||||
: item.unitType.toString(),
|
||||
tooltipItems: [
|
||||
{ className: "title", text: translateText(item.key ?? "") },
|
||||
{
|
||||
className: "description",
|
||||
text: translateText(item.description ?? ""),
|
||||
},
|
||||
{
|
||||
className: "cost",
|
||||
text: `${renderNumber(params.buildMenu.cost(item))} ${translateText("player_panel.gold")}`,
|
||||
},
|
||||
item.countable
|
||||
? { className: "count", text: `${params.buildMenu.count(item)}x` }
|
||||
: null,
|
||||
].filter(
|
||||
(tooltipItem): tooltipItem is TooltipItem => tooltipItem !== null,
|
||||
),
|
||||
}));
|
||||
}
|
||||
|
||||
export const attackMenuElement: MenuElement = {
|
||||
id: Slot.Attack,
|
||||
name: "radial_attack",
|
||||
color: COLORS.attack,
|
||||
disabled: (params: MenuElementParams) => params.game.inSpawnPhase(),
|
||||
icon: swordIcon,
|
||||
color: COLORS.attack,
|
||||
id: Slot.Attack,
|
||||
name: "radial_attack",
|
||||
|
||||
subMenu: (params: MenuElementParams) => {
|
||||
if (params === undefined) return [];
|
||||
@@ -416,8 +416,29 @@ export const attackMenuElement: MenuElement = {
|
||||
};
|
||||
|
||||
export const deleteUnitElement: MenuElement = {
|
||||
id: Slot.Delete,
|
||||
name: "delete",
|
||||
action: (params: MenuElementParams) => {
|
||||
const DELETE_SELECTION_RADIUS = 5;
|
||||
const myUnits = params.myPlayer
|
||||
.units()
|
||||
.filter(
|
||||
(unit) =>
|
||||
params.game.manhattanDist(unit.tile(), params.tile) <=
|
||||
DELETE_SELECTION_RADIUS,
|
||||
);
|
||||
|
||||
if (myUnits.length > 0) {
|
||||
myUnits.sort(
|
||||
(a, b) =>
|
||||
params.game.manhattanDist(a.tile(), params.tile) -
|
||||
params.game.manhattanDist(b.tile(), params.tile),
|
||||
);
|
||||
|
||||
params.playerActionHandler.handleDeleteUnit(myUnits[0].id());
|
||||
}
|
||||
|
||||
params.closeMenu();
|
||||
},
|
||||
color: COLORS.delete,
|
||||
disabled: (params: MenuElementParams) => {
|
||||
const tileOwner = params.game.owner(params.tile);
|
||||
const isLand = params.game.isLand(params.tile);
|
||||
@@ -450,47 +471,26 @@ export const deleteUnitElement: MenuElement = {
|
||||
return myUnits.length === 0;
|
||||
},
|
||||
icon: xIcon,
|
||||
color: COLORS.delete,
|
||||
id: Slot.Delete,
|
||||
name: "delete",
|
||||
tooltipKeys: [
|
||||
{
|
||||
key: "radial_menu.delete_unit_title",
|
||||
className: "title",
|
||||
key: "radial_menu.delete_unit_title",
|
||||
},
|
||||
{
|
||||
key: "radial_menu.delete_unit_description",
|
||||
className: "description",
|
||||
key: "radial_menu.delete_unit_description",
|
||||
},
|
||||
],
|
||||
action: (params: MenuElementParams) => {
|
||||
const DELETE_SELECTION_RADIUS = 5;
|
||||
const myUnits = params.myPlayer
|
||||
.units()
|
||||
.filter(
|
||||
(unit) =>
|
||||
params.game.manhattanDist(unit.tile(), params.tile) <=
|
||||
DELETE_SELECTION_RADIUS,
|
||||
);
|
||||
|
||||
if (myUnits.length > 0) {
|
||||
myUnits.sort(
|
||||
(a, b) =>
|
||||
params.game.manhattanDist(a.tile(), params.tile) -
|
||||
params.game.manhattanDist(b.tile(), params.tile),
|
||||
);
|
||||
|
||||
params.playerActionHandler.handleDeleteUnit(myUnits[0].id());
|
||||
}
|
||||
|
||||
params.closeMenu();
|
||||
},
|
||||
};
|
||||
|
||||
export const buildMenuElement: MenuElement = {
|
||||
id: Slot.Build,
|
||||
name: "build",
|
||||
color: COLORS.build,
|
||||
disabled: (params: MenuElementParams) => params.game.inSpawnPhase(),
|
||||
icon: buildIcon,
|
||||
color: COLORS.build,
|
||||
id: Slot.Build,
|
||||
name: "build",
|
||||
|
||||
subMenu: (params: MenuElementParams) => {
|
||||
if (params === undefined) return [];
|
||||
@@ -499,15 +499,6 @@ export const buildMenuElement: MenuElement = {
|
||||
};
|
||||
|
||||
export const boatMenuElement: MenuElement = {
|
||||
id: Slot.Boat,
|
||||
name: "boat",
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions.buildableUnits.some(
|
||||
(unit) => unit.type === UnitType.TransportShip && unit.canBuild,
|
||||
),
|
||||
icon: boatIcon,
|
||||
color: COLORS.boat,
|
||||
|
||||
action: async (params: MenuElementParams) => {
|
||||
const spawn = await params.playerActionHandler.findBestTransportShipSpawn(
|
||||
params.myPlayer,
|
||||
@@ -523,9 +514,28 @@ export const boatMenuElement: MenuElement = {
|
||||
|
||||
params.closeMenu();
|
||||
},
|
||||
color: COLORS.boat,
|
||||
disabled: (params: MenuElementParams) =>
|
||||
!params.playerActions.buildableUnits.some(
|
||||
(unit) => unit.type === UnitType.TransportShip && unit.canBuild,
|
||||
),
|
||||
icon: boatIcon,
|
||||
id: Slot.Boat,
|
||||
name: "boat",
|
||||
};
|
||||
|
||||
export const centerButtonElement: CenterButtonElement = {
|
||||
action: (params: MenuElementParams) => {
|
||||
if (params.game.inSpawnPhase()) {
|
||||
params.playerActionHandler.handleSpawn(params.tile);
|
||||
} else {
|
||||
params.playerActionHandler.handleAttack(
|
||||
params.myPlayer,
|
||||
params.selected?.id() ?? null,
|
||||
);
|
||||
}
|
||||
params.closeMenu();
|
||||
},
|
||||
disabled: (params: MenuElementParams): boolean => {
|
||||
const tileOwner = params.game.owner(params.tile);
|
||||
const isLand = params.game.isLand(params.tile);
|
||||
@@ -540,25 +550,14 @@ export const centerButtonElement: CenterButtonElement = {
|
||||
}
|
||||
return !params.playerActions.canAttack;
|
||||
},
|
||||
action: (params: MenuElementParams) => {
|
||||
if (params.game.inSpawnPhase()) {
|
||||
params.playerActionHandler.handleSpawn(params.tile);
|
||||
} else {
|
||||
params.playerActionHandler.handleAttack(
|
||||
params.myPlayer,
|
||||
params.selected?.id() ?? null,
|
||||
);
|
||||
}
|
||||
params.closeMenu();
|
||||
},
|
||||
};
|
||||
|
||||
export const rootMenuElement: MenuElement = {
|
||||
id: "root",
|
||||
name: "root",
|
||||
color: COLORS.info,
|
||||
disabled: () => false,
|
||||
icon: infoIcon,
|
||||
color: COLORS.info,
|
||||
id: "root",
|
||||
name: "root",
|
||||
subMenu: (params: MenuElementParams) => {
|
||||
let ally = allyRequestElement;
|
||||
if (params.selected?.isAlliedWith(params.myPlayer)) {
|
||||
|
||||
@@ -123,9 +123,9 @@ export class RailroadLayer implements Layer {
|
||||
railTile.lastOwnerId = currentOwner;
|
||||
} else {
|
||||
this.existingRailroads.set(railRoad.tile, {
|
||||
tile: railRoad,
|
||||
numOccurence: 1,
|
||||
lastOwnerId: currentOwner,
|
||||
numOccurence: 1,
|
||||
tile: railRoad,
|
||||
});
|
||||
this.railTileList.push(railRoad.tile);
|
||||
this.paintRail(railRoad);
|
||||
@@ -156,7 +156,7 @@ export class RailroadLayer implements Layer {
|
||||
const recipient = owner.isPlayer() ? owner : null;
|
||||
const color = recipient
|
||||
? this.theme.railroadColor(recipient)
|
||||
: new Colord({ r: 255, g: 255, b: 255, a: 1 });
|
||||
: new Colord({ a: 1, b: 255, g: 255, r: 255 });
|
||||
if (this.context === undefined) throw new Error("Not initialized");
|
||||
this.context.fillStyle = color.toRgbString();
|
||||
this.paintRailRects(x, y, railRoad.railType);
|
||||
|
||||
@@ -79,8 +79,8 @@ export class SpawnAd extends LitElement implements Layer {
|
||||
window.ramp.que.push(() => {
|
||||
window.ramp.spaAddAds([
|
||||
{
|
||||
type: AD_TYPE,
|
||||
selectorId: AD_CONTAINER_ID,
|
||||
type: AD_TYPE,
|
||||
},
|
||||
]);
|
||||
this.adLoaded = true;
|
||||
|
||||
@@ -68,20 +68,20 @@ export class StructureIconsLayer implements Layer {
|
||||
UnitType,
|
||||
{ visible: boolean; iconPath: string; image: HTMLImageElement | null }
|
||||
> = new Map([
|
||||
[UnitType.City, { visible: true, iconPath: cityIcon, image: null }],
|
||||
[UnitType.Factory, { visible: true, iconPath: factoryIcon, image: null }],
|
||||
[UnitType.City, { iconPath: cityIcon, image: null, visible: true }],
|
||||
[UnitType.Factory, { iconPath: factoryIcon, image: null, visible: true }],
|
||||
[
|
||||
UnitType.DefensePost,
|
||||
{ visible: true, iconPath: shieldIcon, image: null },
|
||||
{ iconPath: shieldIcon, image: null, visible: true },
|
||||
],
|
||||
[UnitType.Port, { visible: true, iconPath: anchorIcon, image: null }],
|
||||
[UnitType.Port, { iconPath: anchorIcon, image: null, visible: true }],
|
||||
[
|
||||
UnitType.MissileSilo,
|
||||
{ visible: true, iconPath: missileSiloIcon, image: null },
|
||||
{ iconPath: missileSiloIcon, image: null, visible: true },
|
||||
],
|
||||
[
|
||||
UnitType.SAMLauncher,
|
||||
{ visible: true, iconPath: SAMMissileIcon, image: null },
|
||||
{ iconPath: SAMMissileIcon, image: null, visible: true },
|
||||
],
|
||||
]);
|
||||
private renderSprites = true;
|
||||
@@ -119,14 +119,14 @@ export class StructureIconsLayer implements Layer {
|
||||
this.dotsStage.setSize(this.pixicanvas.width, this.pixicanvas.height);
|
||||
|
||||
await this.renderer.init({
|
||||
canvas: this.pixicanvas,
|
||||
resolution: 1,
|
||||
width: this.pixicanvas.width,
|
||||
height: this.pixicanvas.height,
|
||||
antialias: false,
|
||||
clearBeforeRender: true,
|
||||
backgroundAlpha: 0,
|
||||
backgroundColor: 0x00000000,
|
||||
canvas: this.pixicanvas,
|
||||
clearBeforeRender: true,
|
||||
height: this.pixicanvas.height,
|
||||
resolution: 1,
|
||||
width: this.pixicanvas.width,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -249,10 +249,10 @@ export class StructureIconsLayer implements Layer {
|
||||
render.dotContainer.alpha = structureInfos.visible ? 1 : 0.3;
|
||||
if (structureInfos.visible && focusStructure) {
|
||||
render.iconContainer.filters = [
|
||||
new OutlineFilter({ thickness: 2, color: "rgb(255, 255, 255)" }),
|
||||
new OutlineFilter({ color: "rgb(255, 255, 255)", thickness: 2 }),
|
||||
];
|
||||
render.dotContainer.filters = [
|
||||
new OutlineFilter({ thickness: 2, color: "rgb(255, 255, 255)" }),
|
||||
new OutlineFilter({ color: "rgb(255, 255, 255)", thickness: 2 }),
|
||||
];
|
||||
} else {
|
||||
render.iconContainer.filters = [];
|
||||
@@ -490,11 +490,11 @@ export class StructureIconsLayer implements Layer {
|
||||
|
||||
if (renderIcon) {
|
||||
const SHAPE_OFFSETS = {
|
||||
triangle: [6, 11],
|
||||
square: [5, 5],
|
||||
circle: [6, 6],
|
||||
octagon: [6, 6],
|
||||
pentagon: [7, 7],
|
||||
circle: [6, 6],
|
||||
square: [5, 5],
|
||||
triangle: [6, 11],
|
||||
};
|
||||
const [offsetX, offsetY] = SHAPE_OFFSETS[shape] || [0, 0];
|
||||
context.drawImage(
|
||||
@@ -509,24 +509,24 @@ export class StructureIconsLayer implements Layer {
|
||||
private createLevelSprite(unit: UnitView): PIXI.Container {
|
||||
if (this.levelsStage === undefined) throw new Error("Not initialized");
|
||||
return this.createUnitContainer(unit, {
|
||||
type: "level",
|
||||
stage: this.levelsStage,
|
||||
type: "level",
|
||||
});
|
||||
}
|
||||
|
||||
private createDotSprite(unit: UnitView): PIXI.Container {
|
||||
if (this.dotsStage === undefined) throw new Error("Not initialized");
|
||||
return this.createUnitContainer(unit, {
|
||||
type: "dot",
|
||||
stage: this.dotsStage,
|
||||
type: "dot",
|
||||
});
|
||||
}
|
||||
|
||||
private createIconSprite(unit: UnitView): PIXI.Container {
|
||||
if (this.iconsStage === undefined) throw new Error("Not initialized");
|
||||
return this.createUnitContainer(unit, {
|
||||
type: "icon",
|
||||
stage: this.iconsStage,
|
||||
type: "icon",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -557,11 +557,11 @@ export class StructureIconsLayer implements Layer {
|
||||
// Add level text if needed
|
||||
if ((type === "icon" || type === "level") && unit.level() > 1) {
|
||||
const text = new PIXI.BitmapText({
|
||||
text: unit.level().toString(),
|
||||
style: {
|
||||
fontFamily: "round_6x6_modified",
|
||||
fontSize: 14,
|
||||
},
|
||||
text: unit.level().toString(),
|
||||
});
|
||||
text.anchor.set(0.5);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { GameView, UnitView } from "../../../core/game/GameView";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { Layer } from "./Layer";
|
||||
|
||||
const underConstructionColor = colord({ r: 150, g: 150, b: 150 });
|
||||
const underConstructionColor = colord({ b: 150, g: 150, r: 150 });
|
||||
|
||||
// Base radius values and scaling factor for unit borders and territories
|
||||
const BASE_BORDER_RADIUS = 16.5;
|
||||
@@ -39,33 +39,33 @@ export class StructureLayer implements Layer {
|
||||
// Configuration for supported unit types only
|
||||
private readonly unitConfigs: Partial<Record<UnitType, UnitRenderConfig>> = {
|
||||
[UnitType.Port]: {
|
||||
icon: anchorIcon,
|
||||
borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR,
|
||||
icon: anchorIcon,
|
||||
territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR,
|
||||
},
|
||||
[UnitType.City]: {
|
||||
icon: cityIcon,
|
||||
borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR,
|
||||
icon: cityIcon,
|
||||
territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR,
|
||||
},
|
||||
[UnitType.Factory]: {
|
||||
icon: factoryIcon,
|
||||
borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR,
|
||||
icon: factoryIcon,
|
||||
territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR,
|
||||
},
|
||||
[UnitType.MissileSilo]: {
|
||||
icon: missileSiloIcon,
|
||||
borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR,
|
||||
icon: missileSiloIcon,
|
||||
territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR,
|
||||
},
|
||||
[UnitType.DefensePost]: {
|
||||
icon: shieldIcon,
|
||||
borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR,
|
||||
icon: shieldIcon,
|
||||
territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR,
|
||||
},
|
||||
[UnitType.SAMLauncher]: {
|
||||
icon: SAMMissileIcon,
|
||||
borderRadius: BASE_BORDER_RADIUS * RADIUS_SCALE_FACTOR,
|
||||
icon: SAMMissileIcon,
|
||||
territoryRadius: BASE_TERRITORY_RADIUS * RADIUS_SCALE_FACTOR,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -89,17 +89,17 @@ export class TeamStats extends LitElement implements Layer {
|
||||
const totalScorePercent = totalScoreSort / this.game.numLandTiles();
|
||||
|
||||
return {
|
||||
teamName: teamStr,
|
||||
totalScoreStr: formatPercentage(totalScorePercent),
|
||||
totalScoreSort,
|
||||
totalGold: renderNumber(totalGold),
|
||||
totalTroops: renderNumber(totalTroops / 10),
|
||||
players: teamPlayers,
|
||||
teamName: teamStr,
|
||||
totalCities: renderNumber(totalCities),
|
||||
totalGold: renderNumber(totalGold),
|
||||
|
||||
totalLaunchers: renderNumber(totalLaunchers),
|
||||
totalSAMs: renderNumber(totalSAMs),
|
||||
totalScoreSort,
|
||||
totalScoreStr: formatPercentage(totalScorePercent),
|
||||
totalTroops: renderNumber(totalTroops / 10),
|
||||
totalWarShips: renderNumber(totalWarShips),
|
||||
totalCities: renderNumber(totalCities),
|
||||
};
|
||||
})
|
||||
.sort((a, b) => b.totalScoreSort - a.totalScoreSort);
|
||||
|
||||
@@ -544,8 +544,8 @@ export class TerritoryLayer implements Layer {
|
||||
|
||||
enqueueTile(tile: TileRef) {
|
||||
this.tileToRenderQueue.push({
|
||||
tile,
|
||||
lastUpdate: this.game.ticks() + this.random.nextFloat(0, 0.5),
|
||||
tile,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -252,9 +252,9 @@ export class UILayer implements Layer {
|
||||
|
||||
// Store current selection box position for next cleanup
|
||||
this.lastSelectionBoxCenter = {
|
||||
size: selectionSize,
|
||||
x: centerX,
|
||||
y: centerY,
|
||||
size: selectionSize,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -349,8 +349,8 @@ export class UILayer implements Layer {
|
||||
0,
|
||||
);
|
||||
this.allProgressBars.set(unit.id(), {
|
||||
unit,
|
||||
progressBar,
|
||||
unit,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +300,7 @@ export class UnitLayer implements Layer {
|
||||
|
||||
private handleWarShipEvent(unit: UnitView) {
|
||||
if (unit.targetUnitId()) {
|
||||
this.drawSprite(unit, colord({ r: 200, b: 0, g: 0 }));
|
||||
this.drawSprite(unit, colord({ b: 0, g: 0, r: 200 }));
|
||||
} else {
|
||||
this.drawSprite(unit);
|
||||
}
|
||||
|
||||
+3
-3
@@ -85,10 +85,10 @@ export async function logOut(allSessions = false) {
|
||||
const response = await fetch(
|
||||
getApiBase() + (allSessions ? "/revoke" : "/logout"),
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
method: "POST",
|
||||
},
|
||||
);
|
||||
|
||||
@@ -179,7 +179,7 @@ function _isLoggedIn(): IsLoggedInResponse {
|
||||
}
|
||||
|
||||
const claims = result.data;
|
||||
return { token, claims };
|
||||
return { claims, token };
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return false;
|
||||
@@ -193,10 +193,10 @@ export async function postRefresh(): Promise<boolean> {
|
||||
|
||||
// Refresh the JWT
|
||||
const response = await fetch(getApiBase() + "/refresh", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
method: "POST",
|
||||
});
|
||||
if (response.status === 401) {
|
||||
clearToken();
|
||||
|
||||
@@ -9,16 +9,16 @@ export type UnitTypeRenderContext = {
|
||||
};
|
||||
|
||||
const unitOptions: { type: UnitType; translationKey: string }[] = [
|
||||
{ type: UnitType.City, translationKey: "unit_type.city" },
|
||||
{ type: UnitType.DefensePost, translationKey: "unit_type.defense_post" },
|
||||
{ type: UnitType.Port, translationKey: "unit_type.port" },
|
||||
{ type: UnitType.Warship, translationKey: "unit_type.warship" },
|
||||
{ type: UnitType.MissileSilo, translationKey: "unit_type.missile_silo" },
|
||||
{ type: UnitType.SAMLauncher, translationKey: "unit_type.sam_launcher" },
|
||||
{ type: UnitType.AtomBomb, translationKey: "unit_type.atom_bomb" },
|
||||
{ type: UnitType.HydrogenBomb, translationKey: "unit_type.hydrogen_bomb" },
|
||||
{ type: UnitType.MIRV, translationKey: "unit_type.mirv" },
|
||||
{ type: UnitType.Factory, translationKey: "unit_type.factory" },
|
||||
{ translationKey: "unit_type.city", type: UnitType.City },
|
||||
{ translationKey: "unit_type.defense_post", type: UnitType.DefensePost },
|
||||
{ translationKey: "unit_type.port", type: UnitType.Port },
|
||||
{ translationKey: "unit_type.warship", type: UnitType.Warship },
|
||||
{ translationKey: "unit_type.missile_silo", type: UnitType.MissileSilo },
|
||||
{ translationKey: "unit_type.sam_launcher", type: UnitType.SAMLauncher },
|
||||
{ translationKey: "unit_type.atom_bomb", type: UnitType.AtomBomb },
|
||||
{ translationKey: "unit_type.hydrogen_bomb", type: UnitType.HydrogenBomb },
|
||||
{ translationKey: "unit_type.mirv", type: UnitType.MIRV },
|
||||
{ translationKey: "unit_type.factory", type: UnitType.Factory },
|
||||
];
|
||||
|
||||
export function renderUnitTypeOptions({
|
||||
|
||||
+20
-21
@@ -8,8 +8,11 @@ export const RefreshResponseSchema = z.object({
|
||||
});
|
||||
export type RefreshResponse = z.infer<typeof RefreshResponseSchema>;
|
||||
|
||||
/* eslint-disable sort-keys */
|
||||
export const TokenPayloadSchema = z.object({
|
||||
aud: z.string(),
|
||||
exp: z.number(),
|
||||
iat: z.number(),
|
||||
iss: z.string(),
|
||||
jti: z.string(),
|
||||
sub: z
|
||||
.string()
|
||||
@@ -27,41 +30,37 @@ export const TokenPayloadSchema = z.object({
|
||||
if (!uuid) throw new Error("Invalid base64 UUID");
|
||||
return uuid;
|
||||
}),
|
||||
iat: z.number(),
|
||||
iss: z.string(),
|
||||
aud: z.string(),
|
||||
exp: z.number(),
|
||||
});
|
||||
export type TokenPayload = z.infer<typeof TokenPayloadSchema>;
|
||||
|
||||
export const UserMeResponseSchema = z.object({
|
||||
user: z.object({
|
||||
id: z.string(),
|
||||
avatar: z.string().nullable(),
|
||||
username: z.string(),
|
||||
global_name: z.string().nullable(),
|
||||
discriminator: z.string(),
|
||||
locale: z.string().optional(),
|
||||
}),
|
||||
player: z.object({
|
||||
flares: z.string().array().optional(),
|
||||
publicId: z.string(),
|
||||
roles: z.string().array().optional(),
|
||||
flares: z.string().array().optional(),
|
||||
}),
|
||||
user: z.object({
|
||||
avatar: z.string().nullable(),
|
||||
discriminator: z.string(),
|
||||
global_name: z.string().nullable(),
|
||||
id: z.string(),
|
||||
locale: z.string().optional(),
|
||||
username: z.string(),
|
||||
}),
|
||||
});
|
||||
export type UserMeResponse = z.infer<typeof UserMeResponseSchema>;
|
||||
|
||||
export const StripeCreateCheckoutSessionResponseSchema = z.object({
|
||||
id: z.string(),
|
||||
object: z.literal("checkout.session"),
|
||||
url: z.string(),
|
||||
payment_status: z.enum(["paid", "unpaid", "no_payment_required"]),
|
||||
status: z.enum(["open", "complete", "expired"]),
|
||||
client_reference_id: z.string().optional(),
|
||||
customer: z.string().optional(),
|
||||
payment_intent: z.string().optional(),
|
||||
subscription: z.string().optional(),
|
||||
id: z.string(),
|
||||
metadata: z.partialRecord(z.string(), z.string()),
|
||||
object: z.literal("checkout.session"),
|
||||
payment_intent: z.string().optional(),
|
||||
payment_status: z.enum(["paid", "unpaid", "no_payment_required"]),
|
||||
status: z.enum(["open", "complete", "expired"]),
|
||||
subscription: z.string().optional(),
|
||||
url: z.string(),
|
||||
});
|
||||
export type StripeCreateCheckoutSessionResponse = z.infer<
|
||||
typeof StripeCreateCheckoutSessionResponseSchema
|
||||
|
||||
+12
-15
@@ -2,11 +2,10 @@ import { z } from "zod";
|
||||
import { RequiredPatternSchema } from "./Schemas";
|
||||
|
||||
export const ProductSchema = z.object({
|
||||
productId: z.string(),
|
||||
/* eslint-disable sort-keys */
|
||||
priceId: z.string(),
|
||||
price: z.string(),
|
||||
/* eslint-enable sort-keys */
|
||||
|
||||
priceId: z.string(),
|
||||
productId: z.string(),
|
||||
});
|
||||
|
||||
const PatternSchema = z.object({
|
||||
@@ -17,28 +16,26 @@ const PatternSchema = z.object({
|
||||
|
||||
// Schema for resources/cosmetics/cosmetics.json
|
||||
export const CosmeticsSchema = z.object({
|
||||
patterns: z.record(z.string(), PatternSchema),
|
||||
/* eslint-disable sort-keys */
|
||||
flag: z
|
||||
.object({
|
||||
layers: z.record(
|
||||
z.string(),
|
||||
z.object({
|
||||
name: z.string(),
|
||||
flares: z.string().array().optional(),
|
||||
}),
|
||||
),
|
||||
color: z.record(
|
||||
z.string(),
|
||||
z.object({
|
||||
color: z.string(),
|
||||
name: z.string(),
|
||||
flares: z.string().array().optional(),
|
||||
name: z.string(),
|
||||
}),
|
||||
),
|
||||
layers: z.record(
|
||||
z.string(),
|
||||
z.object({
|
||||
flares: z.string().array().optional(),
|
||||
name: z.string(),
|
||||
}),
|
||||
),
|
||||
})
|
||||
.optional(),
|
||||
/* eslint-enable sort-keys */
|
||||
patterns: z.record(z.string(), PatternSchema),
|
||||
});
|
||||
export type Cosmetics = z.infer<typeof CosmeticsSchema>;
|
||||
export type Pattern = z.infer<typeof PatternSchema>;
|
||||
|
||||
@@ -28,8 +28,8 @@ export function renderPlayerFlag(
|
||||
const code = flag.slice("!".length);
|
||||
const layers = code.split("_").map((segment) => {
|
||||
const [layerKey, colorKey] = segment.split("-");
|
||||
// eslint-disable-next-line sort-keys
|
||||
return { layerKey, colorKey };
|
||||
|
||||
return { colorKey, layerKey };
|
||||
});
|
||||
|
||||
target.innerHTML = "";
|
||||
|
||||
+1
-1
@@ -138,8 +138,8 @@ export type TeamCountConfig = z.infer<typeof TeamCountConfigSchema>;
|
||||
export const GameConfigSchema = z.object({
|
||||
bots: z.number().int().min(0).max(400),
|
||||
difficulty: z.enum(Difficulty),
|
||||
disableNPCs: z.boolean(),
|
||||
disabledUnits: z.enum(UnitType).array().optional(),
|
||||
disableNPCs: z.boolean(),
|
||||
donateGold: z.boolean(),
|
||||
donateTroops: z.boolean(),
|
||||
gameMap: z.enum(GameMapType),
|
||||
|
||||
+1
-2
@@ -89,8 +89,7 @@ export function calculateBoundingBox(
|
||||
maxY = Math.max(maxY, cell.y);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line sort-keys
|
||||
return { min: new Cell(minX, minY), max: new Cell(maxX, maxY) };
|
||||
return { max: new Cell(maxX, maxY), min: new Cell(minX, minY) };
|
||||
}
|
||||
|
||||
export function calculateBoundingBoxCenter(
|
||||
|
||||
+318
-319
@@ -5,15 +5,14 @@ import lchPlugin from "colord/plugins/lch";
|
||||
extend([lchPlugin]);
|
||||
extend([labPlugin]);
|
||||
|
||||
/* eslint-disable sort-keys */
|
||||
export const red = colord({ h: 0, s: 82, l: 56 });
|
||||
export const blue = colord({ h: 224, s: 100, l: 58 });
|
||||
export const teal = colord({ h: 172, s: 66, l: 50 });
|
||||
export const purple = colord({ h: 271, s: 81, l: 56 });
|
||||
export const yellow = colord({ h: 45, s: 93, l: 47 });
|
||||
export const orange = colord({ h: 25, s: 95, l: 53 });
|
||||
export const green = colord({ h: 128, s: 49, l: 50 });
|
||||
export const botColor = colord({ h: 36, s: 10, l: 80 });
|
||||
export const red = colord({ h: 0, l: 56, s: 82 });
|
||||
export const blue = colord({ h: 224, l: 58, s: 100 });
|
||||
export const teal = colord({ h: 172, l: 50, s: 66 });
|
||||
export const purple = colord({ h: 271, l: 56, s: 81 });
|
||||
export const yellow = colord({ h: 45, l: 47, s: 93 });
|
||||
export const orange = colord({ h: 25, l: 53, s: 95 });
|
||||
export const green = colord({ h: 128, l: 50, s: 49 });
|
||||
export const botColor = colord({ h: 36, l: 80, s: 10 });
|
||||
|
||||
export const redTeamColors: Colord[] = generateTeamColors(red);
|
||||
export const blueTeamColors: Colord[] = generateTeamColors(blue);
|
||||
@@ -36,331 +35,331 @@ function generateTeamColors(baseColor: Colord): Colord[] {
|
||||
|
||||
return colord({
|
||||
h: baseHue,
|
||||
s: saturation,
|
||||
l: lightness,
|
||||
s: saturation,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export const nationColors: 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
|
||||
colord({ b: 100, g: 100, r: 230 }), // Bright Red
|
||||
colord({ b: 230, g: 180, r: 100 }), // Sky Blue
|
||||
colord({ b: 80, g: 180, r: 230 }), // Golden Yellow
|
||||
colord({ b: 230, g: 100, r: 180 }), // Purple
|
||||
colord({ b: 120, g: 200, r: 80 }), // Emerald Green
|
||||
colord({ b: 180, g: 130, r: 230 }), // Pink
|
||||
colord({ b: 80, g: 160, r: 100 }), // Olive Green
|
||||
colord({ b: 100, g: 150, r: 230 }), // Peach
|
||||
colord({ b: 190, g: 130, r: 80 }), // Navy Blue
|
||||
colord({ b: 100, g: 210, r: 210 }), // Lime Yellow
|
||||
colord({ b: 130, g: 100, r: 190 }), // Maroon
|
||||
colord({ b: 210, g: 210, r: 100 }), // Turquoise
|
||||
colord({ b: 80, g: 140, r: 210 }), // Light Orange
|
||||
colord({ b: 190, g: 110, r: 150 }), // Lavender
|
||||
colord({ b: 120, g: 210, r: 180 }), // Light Green
|
||||
colord({ b: 160, g: 100, r: 210 }), // Hot Pink
|
||||
colord({ b: 110, g: 140, r: 100 }), // Sea Green
|
||||
colord({ b: 180, g: 180, r: 230 }), // Light Pink
|
||||
colord({ b: 190, g: 120, r: 120 }), // Periwinkle
|
||||
colord({ b: 100, g: 170, r: 190 }), // Sand
|
||||
colord({ b: 160, g: 180, r: 100 }), // Aquamarine
|
||||
colord({ b: 200, g: 160, r: 210 }), // Orchid
|
||||
colord({ b: 100, g: 190, r: 170 }), // Yellow Green
|
||||
colord({ b: 150, g: 130, r: 100 }), // Steel Blue
|
||||
colord({ b: 140, g: 140, r: 230 }), // Salmon
|
||||
colord({ b: 220, g: 180, r: 140 }), // Light Blue
|
||||
colord({ b: 110, g: 160, r: 200 }), // Tan
|
||||
colord({ b: 180, g: 130, r: 180 }), // Plum
|
||||
colord({ b: 130, g: 200, r: 130 }), // Light Sea Green
|
||||
colord({ b: 120, g: 120, r: 220 }), // Coral
|
||||
colord({ b: 200, g: 160, r: 120 }), // Cornflower Blue
|
||||
colord({ b: 140, g: 200, r: 200 }), // Khaki
|
||||
colord({ b: 160, g: 120, r: 160 }), // Purple Gray
|
||||
colord({ b: 140, g: 180, r: 140 }), // Dark Sea Green
|
||||
colord({ b: 110, g: 130, r: 200 }), // Dark Salmon
|
||||
colord({ b: 190, g: 170, r: 130 }), // Cadet Blue
|
||||
colord({ b: 160, g: 180, r: 190 }), // Tan Gray
|
||||
colord({ b: 190, g: 140, r: 170 }), // Medium Purple
|
||||
colord({ b: 160, g: 190, r: 160 }), // Pale Green
|
||||
colord({ b: 130, g: 150, r: 190 }), // Rosy Brown
|
||||
colord({ b: 180, g: 150, r: 140 }), // Light Slate Gray
|
||||
colord({ b: 140, g: 170, r: 180 }), // Dark Khaki
|
||||
colord({ b: 150, g: 130, r: 150 }), // Thistle
|
||||
colord({ b: 180, g: 190, r: 170 }), // Pale Blue Green
|
||||
colord({ b: 150, g: 140, r: 190 }), // Puce
|
||||
colord({ b: 170, g: 180, r: 130 }), // Medium Aquamarine
|
||||
colord({ b: 180, g: 160, r: 180 }), // Mauve
|
||||
colord({ b: 140, g: 180, r: 160 }), // Dark Olive Green
|
||||
colord({ b: 170, g: 150, r: 170 }), // Dusty Rose
|
||||
colord({ b: 230, g: 180, r: 100 }), // Sky Blue
|
||||
colord({ b: 80, g: 180, r: 230 }), // Golden Yellow
|
||||
colord({ b: 230, g: 100, r: 180 }), // Purple
|
||||
colord({ b: 120, g: 200, r: 80 }), // Emerald Green
|
||||
colord({ b: 180, g: 130, r: 230 }), // Pink
|
||||
colord({ b: 80, g: 160, r: 100 }), // Olive Green
|
||||
colord({ b: 100, g: 150, r: 230 }), // Peach
|
||||
colord({ b: 190, g: 130, r: 80 }), // Navy Blue
|
||||
colord({ b: 100, g: 210, r: 210 }), // Lime Yellow
|
||||
colord({ b: 130, g: 100, r: 190 }), // Maroon
|
||||
colord({ b: 210, g: 210, r: 100 }), // Turquoise
|
||||
colord({ b: 80, g: 140, r: 210 }), // Light Orange
|
||||
colord({ b: 190, g: 110, r: 150 }), // Lavender
|
||||
colord({ b: 120, g: 210, r: 180 }), // Light Green
|
||||
colord({ b: 160, g: 100, r: 210 }), // Hot Pink
|
||||
colord({ b: 110, g: 140, r: 100 }), // Sea Green
|
||||
colord({ b: 180, g: 180, r: 230 }), // Light Pink
|
||||
colord({ b: 190, g: 120, r: 120 }), // Periwinkle
|
||||
colord({ b: 100, g: 170, r: 190 }), // Sand
|
||||
colord({ b: 160, g: 180, r: 100 }), // Aquamarine
|
||||
colord({ b: 200, g: 160, r: 210 }), // Orchid
|
||||
colord({ b: 100, g: 190, r: 170 }), // Yellow Green
|
||||
colord({ b: 150, g: 130, r: 100 }), // Steel Blue
|
||||
colord({ b: 140, g: 140, r: 230 }), // Salmon
|
||||
colord({ b: 220, g: 180, r: 140 }), // Light Blue
|
||||
colord({ b: 110, g: 160, r: 200 }), // Tan
|
||||
colord({ b: 180, g: 130, r: 180 }), // Plum
|
||||
colord({ b: 130, g: 200, r: 130 }), // Light Sea Green
|
||||
colord({ b: 120, g: 120, r: 220 }), // Coral
|
||||
colord({ b: 200, g: 160, r: 120 }), // Cornflower Blue
|
||||
colord({ b: 140, g: 200, r: 200 }), // Khaki
|
||||
colord({ b: 160, g: 120, r: 160 }), // Purple Gray
|
||||
colord({ b: 140, g: 180, r: 140 }), // Dark Sea Green
|
||||
colord({ b: 110, g: 130, r: 200 }), // Dark Salmon
|
||||
colord({ b: 190, g: 170, r: 130 }), // Cadet Blue
|
||||
colord({ b: 160, g: 180, r: 190 }), // Tan Gray
|
||||
colord({ b: 190, g: 140, r: 170 }), // Medium Purple
|
||||
colord({ b: 160, g: 190, r: 160 }), // Pale Green
|
||||
colord({ b: 130, g: 150, r: 190 }), // Rosy Brown
|
||||
colord({ b: 180, g: 150, r: 140 }), // Light Slate Gray
|
||||
colord({ b: 140, g: 170, r: 180 }), // Dark Khaki
|
||||
colord({ b: 150, g: 130, r: 150 }), // Thistle
|
||||
colord({ b: 180, g: 190, r: 170 }), // Pale Blue Green
|
||||
colord({ b: 150, g: 140, r: 190 }), // Puce
|
||||
colord({ b: 170, g: 180, r: 130 }), // Medium Aquamarine
|
||||
colord({ b: 180, g: 160, r: 180 }), // Mauve
|
||||
colord({ b: 140, g: 180, r: 160 }), // Dark Olive Green
|
||||
colord({ b: 170, g: 150, r: 170 }), // Dusty Rose
|
||||
];
|
||||
|
||||
// Bright pastel theme with 64 colors
|
||||
export const humanColors: Colord[] = [
|
||||
colord({ r: 16, g: 185, b: 129 }), // Sea Green
|
||||
colord({ r: 34, g: 197, b: 94 }), // Emerald
|
||||
colord({ r: 45, g: 212, b: 191 }), // Turquoise
|
||||
colord({ r: 48, g: 178, b: 180 }), // Teal
|
||||
colord({ r: 52, g: 211, b: 153 }), // Spearmint
|
||||
colord({ r: 56, g: 189, b: 248 }), // Light Blue
|
||||
colord({ r: 59, g: 130, b: 246 }), // Royal Blue
|
||||
colord({ r: 67, g: 190, b: 84 }), // Fresh Green
|
||||
colord({ r: 74, g: 222, b: 128 }), // Mint
|
||||
colord({ r: 79, g: 70, b: 229 }), // Indigo
|
||||
colord({ r: 82, g: 183, b: 136 }), // Jade
|
||||
colord({ r: 96, g: 165, b: 250 }), // Sky Blue
|
||||
colord({ r: 99, g: 202, b: 253 }), // Azure
|
||||
colord({ r: 110, g: 231, b: 183 }), // Seafoam
|
||||
colord({ r: 124, g: 58, b: 237 }), // Royal Purple
|
||||
colord({ r: 125, g: 211, b: 252 }), // Crystal Blue
|
||||
colord({ r: 132, g: 204, b: 22 }), // Lime
|
||||
colord({ r: 133, g: 77, b: 14 }), // Chocolate
|
||||
colord({ r: 134, g: 239, b: 172 }), // Light Green
|
||||
colord({ r: 147, g: 51, b: 234 }), // Bright Purple
|
||||
colord({ r: 147, g: 197, b: 253 }), // Powder Blue
|
||||
colord({ r: 151, g: 255, b: 187 }), // Fresh Mint
|
||||
colord({ r: 163, g: 230, b: 53 }), // Yellow Green
|
||||
colord({ r: 167, g: 139, b: 250 }), // Periwinkle
|
||||
colord({ r: 168, g: 85, b: 247 }), // Vibrant Purple
|
||||
colord({ r: 179, g: 136, b: 255 }), // Light Purple
|
||||
colord({ r: 186, g: 255, b: 201 }), // Pale Emerald
|
||||
colord({ r: 190, g: 92, b: 251 }), // Amethyst
|
||||
colord({ r: 192, g: 132, b: 252 }), // Lavender
|
||||
colord({ r: 202, g: 138, b: 4 }), // Rich Gold
|
||||
colord({ r: 202, g: 225, b: 255 }), // Baby Blue
|
||||
colord({ r: 204, g: 204, b: 255 }), // Soft Lavender Blue
|
||||
colord({ r: 217, g: 70, b: 239 }), // Fuchsia
|
||||
colord({ r: 220, g: 38, b: 38 }), // Ruby
|
||||
colord({ r: 220, g: 220, b: 255 }), // Meringue Blue
|
||||
colord({ r: 220, g: 240, b: 250 }), // Ice Blue
|
||||
colord({ r: 230, g: 250, b: 210 }), // Pastel Lime
|
||||
colord({ r: 230, g: 255, b: 250 }), // Mint Whisper
|
||||
colord({ r: 233, g: 213, b: 255 }), // Light Lilac
|
||||
colord({ r: 234, g: 88, b: 12 }), // Burnt Orange
|
||||
colord({ r: 234, g: 179, b: 8 }), // Sunflower
|
||||
colord({ r: 235, g: 75, b: 75 }), // Bright Red
|
||||
colord({ r: 236, g: 72, b: 153 }), // Deep Pink
|
||||
colord({ r: 239, g: 68, b: 68 }), // Crimson
|
||||
colord({ r: 240, g: 171, b: 252 }), // Orchid
|
||||
colord({ r: 240, g: 240, b: 200 }), // Light Khaki
|
||||
colord({ r: 244, g: 114, b: 182 }), // Rose
|
||||
colord({ r: 245, g: 101, b: 101 }), // Coral
|
||||
colord({ r: 245, g: 158, b: 11 }), // Amber
|
||||
colord({ r: 248, g: 113, b: 113 }), // Warm Red
|
||||
colord({ r: 249, g: 115, b: 22 }), // Tangerine
|
||||
colord({ r: 250, g: 215, b: 225 }), // Cotton Candy
|
||||
colord({ r: 250, g: 250, b: 210 }), // Pastel Lemon
|
||||
colord({ r: 251, g: 113, b: 133 }), // Watermelon
|
||||
colord({ r: 251, g: 146, b: 60 }), // Light Orange
|
||||
colord({ r: 251, g: 191, b: 36 }), // Marigold
|
||||
colord({ r: 251, g: 235, b: 245 }), // Rose Powder
|
||||
colord({ r: 252, g: 165, b: 165 }), // Peach
|
||||
colord({ r: 252, g: 211, b: 77 }), // Golden
|
||||
colord({ r: 253, g: 164, b: 175 }), // Salmon Pink
|
||||
colord({ r: 255, g: 204, b: 229 }), // Blush Pink
|
||||
colord({ r: 255, g: 223, b: 186 }), // Apricot Cream
|
||||
colord({ r: 255, g: 240, b: 200 }), // Vanilla
|
||||
colord({ b: 129, g: 185, r: 16 }), // Sea Green
|
||||
colord({ b: 94, g: 197, r: 34 }), // Emerald
|
||||
colord({ b: 191, g: 212, r: 45 }), // Turquoise
|
||||
colord({ b: 180, g: 178, r: 48 }), // Teal
|
||||
colord({ b: 153, g: 211, r: 52 }), // Spearmint
|
||||
colord({ b: 248, g: 189, r: 56 }), // Light Blue
|
||||
colord({ b: 246, g: 130, r: 59 }), // Royal Blue
|
||||
colord({ b: 84, g: 190, r: 67 }), // Fresh Green
|
||||
colord({ b: 128, g: 222, r: 74 }), // Mint
|
||||
colord({ b: 229, g: 70, r: 79 }), // Indigo
|
||||
colord({ b: 136, g: 183, r: 82 }), // Jade
|
||||
colord({ b: 250, g: 165, r: 96 }), // Sky Blue
|
||||
colord({ b: 253, g: 202, r: 99 }), // Azure
|
||||
colord({ b: 183, g: 231, r: 110 }), // Seafoam
|
||||
colord({ b: 237, g: 58, r: 124 }), // Royal Purple
|
||||
colord({ b: 252, g: 211, r: 125 }), // Crystal Blue
|
||||
colord({ b: 22, g: 204, r: 132 }), // Lime
|
||||
colord({ b: 14, g: 77, r: 133 }), // Chocolate
|
||||
colord({ b: 172, g: 239, r: 134 }), // Light Green
|
||||
colord({ b: 234, g: 51, r: 147 }), // Bright Purple
|
||||
colord({ b: 253, g: 197, r: 147 }), // Powder Blue
|
||||
colord({ b: 187, g: 255, r: 151 }), // Fresh Mint
|
||||
colord({ b: 53, g: 230, r: 163 }), // Yellow Green
|
||||
colord({ b: 250, g: 139, r: 167 }), // Periwinkle
|
||||
colord({ b: 247, g: 85, r: 168 }), // Vibrant Purple
|
||||
colord({ b: 255, g: 136, r: 179 }), // Light Purple
|
||||
colord({ b: 201, g: 255, r: 186 }), // Pale Emerald
|
||||
colord({ b: 251, g: 92, r: 190 }), // Amethyst
|
||||
colord({ b: 252, g: 132, r: 192 }), // Lavender
|
||||
colord({ b: 4, g: 138, r: 202 }), // Rich Gold
|
||||
colord({ b: 255, g: 225, r: 202 }), // Baby Blue
|
||||
colord({ b: 255, g: 204, r: 204 }), // Soft Lavender Blue
|
||||
colord({ b: 239, g: 70, r: 217 }), // Fuchsia
|
||||
colord({ b: 38, g: 38, r: 220 }), // Ruby
|
||||
colord({ b: 255, g: 220, r: 220 }), // Meringue Blue
|
||||
colord({ b: 250, g: 240, r: 220 }), // Ice Blue
|
||||
colord({ b: 210, g: 250, r: 230 }), // Pastel Lime
|
||||
colord({ b: 250, g: 255, r: 230 }), // Mint Whisper
|
||||
colord({ b: 255, g: 213, r: 233 }), // Light Lilac
|
||||
colord({ b: 12, g: 88, r: 234 }), // Burnt Orange
|
||||
colord({ b: 8, g: 179, r: 234 }), // Sunflower
|
||||
colord({ b: 75, g: 75, r: 235 }), // Bright Red
|
||||
colord({ b: 153, g: 72, r: 236 }), // Deep Pink
|
||||
colord({ b: 68, g: 68, r: 239 }), // Crimson
|
||||
colord({ b: 252, g: 171, r: 240 }), // Orchid
|
||||
colord({ b: 200, g: 240, r: 240 }), // Light Khaki
|
||||
colord({ b: 182, g: 114, r: 244 }), // Rose
|
||||
colord({ b: 101, g: 101, r: 245 }), // Coral
|
||||
colord({ b: 11, g: 158, r: 245 }), // Amber
|
||||
colord({ b: 113, g: 113, r: 248 }), // Warm Red
|
||||
colord({ b: 22, g: 115, r: 249 }), // Tangerine
|
||||
colord({ b: 225, g: 215, r: 250 }), // Cotton Candy
|
||||
colord({ b: 210, g: 250, r: 250 }), // Pastel Lemon
|
||||
colord({ b: 133, g: 113, r: 251 }), // Watermelon
|
||||
colord({ b: 60, g: 146, r: 251 }), // Light Orange
|
||||
colord({ b: 36, g: 191, r: 251 }), // Marigold
|
||||
colord({ b: 245, g: 235, r: 251 }), // Rose Powder
|
||||
colord({ b: 165, g: 165, r: 252 }), // Peach
|
||||
colord({ b: 77, g: 211, r: 252 }), // Golden
|
||||
colord({ b: 175, g: 164, r: 253 }), // Salmon Pink
|
||||
colord({ b: 229, g: 204, r: 255 }), // Blush Pink
|
||||
colord({ b: 186, g: 223, r: 255 }), // Apricot Cream
|
||||
colord({ b: 200, g: 240, r: 255 }), // Vanilla
|
||||
];
|
||||
|
||||
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
|
||||
colord({ b: 120, g: 120, r: 190 }), // Muted Red
|
||||
colord({ b: 190, g: 160, r: 120 }), // Muted Sky Blue
|
||||
colord({ b: 100, g: 160, r: 190 }), // Muted Golden Yellow
|
||||
colord({ b: 190, g: 120, r: 160 }), // Muted Purple
|
||||
colord({ b: 130, g: 170, r: 100 }), // Muted Emerald Green
|
||||
colord({ b: 160, g: 130, r: 190 }), // Muted Pink
|
||||
colord({ b: 100, g: 150, r: 120 }), // Muted Olive Green
|
||||
colord({ b: 120, g: 140, r: 190 }), // Muted Peach
|
||||
colord({ b: 160, g: 120, r: 100 }), // Muted Navy Blue
|
||||
colord({ b: 120, g: 170, r: 170 }), // Muted Lime Yellow
|
||||
colord({ b: 130, g: 120, r: 160 }), // Muted Maroon
|
||||
colord({ b: 170, g: 170, r: 120 }), // Muted Turquoise
|
||||
colord({ b: 100, g: 140, r: 170 }), // Muted Light Orange
|
||||
colord({ b: 160, g: 120, r: 140 }), // Muted Lavender
|
||||
colord({ b: 130, g: 170, r: 150 }), // Muted Light Green
|
||||
colord({ b: 140, g: 120, r: 170 }), // Muted Hot Pink
|
||||
colord({ b: 120, g: 140, r: 120 }), // Muted Sea Green
|
||||
colord({ b: 160, g: 160, r: 180 }), // Muted Light Pink
|
||||
colord({ b: 160, g: 130, r: 130 }), // Muted Periwinkle
|
||||
colord({ b: 120, g: 150, r: 160 }), // Muted Sand
|
||||
colord({ b: 150, g: 160, r: 120 }), // Muted Aquamarine
|
||||
colord({ b: 170, g: 150, r: 170 }), // Muted Orchid
|
||||
colord({ b: 120, g: 160, r: 150 }), // Muted Yellow Green
|
||||
colord({ b: 140, g: 130, r: 120 }), // Muted Steel Blue
|
||||
colord({ b: 140, g: 140, r: 180 }), // Muted Salmon
|
||||
colord({ b: 170, g: 160, r: 140 }), // Muted Light Blue
|
||||
colord({ b: 130, g: 150, r: 170 }), // Muted Tan
|
||||
colord({ b: 160, g: 130, r: 160 }), // Muted Plum
|
||||
colord({ b: 130, g: 170, r: 130 }), // Muted Light Sea Green
|
||||
colord({ b: 130, g: 130, r: 170 }), // Muted Coral
|
||||
colord({ b: 170, g: 150, r: 130 }), // Muted Cornflower Blue
|
||||
colord({ b: 140, g: 170, r: 170 }), // Muted Khaki
|
||||
colord({ b: 150, g: 130, r: 150 }), // Muted Purple Gray
|
||||
colord({ b: 140, g: 160, r: 140 }), // Muted Dark Sea Green
|
||||
colord({ b: 120, g: 130, r: 170 }), // Muted Dark Salmon
|
||||
colord({ b: 160, g: 150, r: 130 }), // Muted Cadet Blue
|
||||
colord({ b: 150, g: 160, r: 160 }), // Muted Tan Gray
|
||||
colord({ b: 160, g: 140, r: 150 }), // Muted Medium Purple
|
||||
colord({ b: 150, g: 170, r: 150 }), // Muted Pale Green
|
||||
colord({ b: 130, g: 140, r: 160 }), // Muted Rosy Brown
|
||||
colord({ b: 160, g: 150, r: 140 }), // Muted Light Slate Gray
|
||||
colord({ b: 140, g: 150, r: 160 }), // Muted Dark Khaki
|
||||
colord({ b: 140, g: 130, r: 140 }), // Muted Thistle
|
||||
colord({ b: 160, g: 160, r: 150 }), // Muted Pale Blue Green
|
||||
colord({ b: 150, g: 140, r: 160 }), // Muted Puce
|
||||
colord({ b: 150, g: 160, r: 130 }), // Muted Medium Aquamarine
|
||||
colord({ b: 160, g: 150, r: 160 }), // Muted Mauve
|
||||
colord({ b: 140, g: 160, r: 150 }), // Muted Dark Olive Green
|
||||
colord({ b: 150, g: 140, r: 150 }), // Muted Dusty Rose
|
||||
];
|
||||
|
||||
// Fallback colors for when the color palette is exhausted. Currently 100 colors.
|
||||
export const fallbackColors: Colord[] = [
|
||||
colord({ r: 0, g: 5, b: 0 }), // Black Mint
|
||||
colord({ r: 0, g: 15, b: 0 }), // Deep Forest
|
||||
colord({ r: 0, g: 25, b: 0 }), // Jungle
|
||||
colord({ r: 0, g: 35, b: 0 }), // Dark Emerald
|
||||
colord({ r: 0, g: 45, b: 0 }), // Green Moss
|
||||
colord({ r: 0, g: 55, b: 0 }), // Moss Shadow
|
||||
colord({ r: 0, g: 65, b: 0 }), // Dark Meadow
|
||||
colord({ r: 0, g: 75, b: 0 }), // Forest Fern
|
||||
colord({ r: 0, g: 85, b: 0 }), // Pine Leaf
|
||||
colord({ r: 0, g: 95, b: 0 }), // Shadow Grass
|
||||
colord({ r: 0, g: 105, b: 0 }), // Classic Green
|
||||
colord({ r: 0, g: 115, b: 0 }), // Deep Lime
|
||||
colord({ r: 0, g: 125, b: 0 }), // Dense Leaf
|
||||
colord({ r: 0, g: 135, b: 0 }), // Basil Green
|
||||
colord({ r: 0, g: 145, b: 0 }), // Organic Green
|
||||
colord({ r: 0, g: 155, b: 0 }), // Bitter Herb
|
||||
colord({ r: 0, g: 165, b: 0 }), // Raw Spinach
|
||||
colord({ r: 0, g: 175, b: 0 }), // Woodland
|
||||
colord({ r: 0, g: 185, b: 0 }), // Spring Weed
|
||||
colord({ r: 0, g: 195, b: 5 }), // Apple Stem
|
||||
colord({ r: 0, g: 205, b: 10 }), // Crisp Lettuce
|
||||
colord({ r: 0, g: 215, b: 15 }), // Vibrant Green
|
||||
colord({ r: 0, g: 225, b: 20 }), // Bright Herb
|
||||
colord({ r: 0, g: 235, b: 25 }), // Green Splash
|
||||
colord({ r: 0, g: 245, b: 30 }), // Mint Leaf
|
||||
colord({ r: 0, g: 255, b: 35 }), // Fresh Mint
|
||||
colord({ r: 10, g: 255, b: 45 }), // Neon Grass
|
||||
colord({ r: 20, g: 255, b: 55 }), // Lemon Balm
|
||||
colord({ r: 30, g: 255, b: 65 }), // Juicy Green
|
||||
colord({ r: 40, g: 255, b: 75 }), // Pear Tint
|
||||
colord({ r: 50, g: 255, b: 85 }), // Avocado Pastel
|
||||
colord({ r: 60, g: 255, b: 95 }), // Lime Glow
|
||||
colord({ r: 70, g: 255, b: 105 }), // Light Leaf
|
||||
colord({ r: 80, g: 255, b: 115 }), // Soft Fern
|
||||
colord({ r: 90, g: 255, b: 125 }), // Pastel Green
|
||||
colord({ r: 100, g: 255, b: 135 }), // Green Melon
|
||||
colord({ r: 110, g: 255, b: 145 }), // Herbal Mist
|
||||
colord({ r: 120, g: 255, b: 155 }), // Kiwi Foam
|
||||
colord({ r: 130, g: 255, b: 165 }), // Aloe Fresh
|
||||
colord({ r: 140, g: 255, b: 175 }), // Light Mint
|
||||
colord({ r: 150, g: 200, b: 255 }), // Cornflower Mist
|
||||
colord({ r: 150, g: 255, b: 185 }), // Green Sorbet
|
||||
colord({ r: 160, g: 215, b: 255 }), // Powder Blue
|
||||
colord({ r: 160, g: 255, b: 195 }), // Pastel Apple
|
||||
colord({ r: 170, g: 190, b: 255 }), // Periwinkle Ice
|
||||
colord({ r: 170, g: 225, b: 255 }), // Baby Sky
|
||||
colord({ r: 170, g: 255, b: 205 }), // Aloe Breeze
|
||||
colord({ r: 180, g: 180, b: 255 }), // Pale Indigo
|
||||
colord({ r: 180, g: 235, b: 250 }), // Aqua Pastel
|
||||
colord({ r: 180, g: 255, b: 215 }), // Pale Mint
|
||||
colord({ r: 190, g: 140, b: 195 }), // Fuchsia Tint
|
||||
colord({ r: 190, g: 245, b: 240 }), // Ice Mint
|
||||
colord({ r: 190, g: 255, b: 225 }), // Mint Water
|
||||
colord({ r: 195, g: 145, b: 200 }), // Dusky Rose
|
||||
colord({ r: 200, g: 150, b: 205 }), // Plum Frost
|
||||
colord({ r: 200, g: 170, b: 255 }), // Lilac Bloom
|
||||
colord({ r: 200, g: 255, b: 215 }), // Cool Aloe
|
||||
colord({ r: 200, g: 255, b: 235 }), // Cool Mist
|
||||
colord({ r: 205, g: 155, b: 210 }), // Berry Foam
|
||||
colord({ r: 210, g: 160, b: 215 }), // Grape Cloud
|
||||
colord({ r: 210, g: 255, b: 245 }), // Sea Mist
|
||||
colord({ r: 215, g: 165, b: 220 }), // Light Bloom
|
||||
colord({ r: 215, g: 255, b: 200 }), // Fresh Mint
|
||||
colord({ r: 220, g: 160, b: 255 }), // Violet Mist
|
||||
colord({ r: 220, g: 170, b: 225 }), // Cherry Blossom
|
||||
colord({ r: 220, g: 255, b: 255 }), // Pale Aqua
|
||||
colord({ r: 225, g: 175, b: 230 }), // Faded Rose
|
||||
colord({ r: 225, g: 255, b: 175 }), // Soft Lime
|
||||
colord({ r: 230, g: 180, b: 235 }), // Dreamy Mauve
|
||||
colord({ r: 230, g: 250, b: 255 }), // Sky Haze
|
||||
colord({ r: 235, g: 150, b: 255 }), // Orchid Glow
|
||||
colord({ r: 235, g: 185, b: 240 }), // Powder Violet
|
||||
colord({ r: 240, g: 190, b: 245 }), // Pastel Violet
|
||||
colord({ r: 240, g: 240, b: 255 }), // Frosted Lilac
|
||||
colord({ r: 240, g: 250, b: 160 }), // Citrus Wash
|
||||
colord({ r: 245, g: 160, b: 240 }), // Rose Lilac
|
||||
colord({ r: 245, g: 195, b: 250 }), // Soft Magenta
|
||||
colord({ r: 245, g: 245, b: 175 }), // Lemon Mist
|
||||
colord({ r: 250, g: 200, b: 255 }), // Lilac Cream
|
||||
colord({ r: 250, g: 230, b: 255 }), // Misty Mauve
|
||||
colord({ r: 255, g: 170, b: 225 }), // Bubblegum Pink
|
||||
colord({ r: 255, g: 185, b: 215 }), // Blush Mist
|
||||
colord({ r: 255, g: 195, b: 235 }), // Faded Fuchsia
|
||||
colord({ r: 255, g: 200, b: 220 }), // Cotton Rose
|
||||
colord({ r: 255, g: 205, b: 245 }), // Pastel Orchid
|
||||
colord({ r: 255, g: 205, b: 255 }), // Violet Bloom
|
||||
colord({ r: 255, g: 210, b: 230 }), // Pastel Blush
|
||||
colord({ r: 255, g: 210, b: 250 }), // Lavender Mist
|
||||
colord({ r: 255, g: 210, b: 255 }), // Orchid Mist
|
||||
colord({ r: 255, g: 215, b: 195 }), // Apricot Glow
|
||||
colord({ r: 255, g: 215, b: 245 }), // Rose Whisper
|
||||
colord({ r: 255, g: 220, b: 235 }), // Pink Mist
|
||||
colord({ r: 255, g: 220, b: 250 }), // Powder Petal
|
||||
colord({ r: 255, g: 225, b: 180 }), // Butter Peach
|
||||
colord({ r: 255, g: 225, b: 255 }), // Petal Mist
|
||||
colord({ r: 255, g: 230, b: 245 }), // Light Rose
|
||||
colord({ r: 255, g: 235, b: 200 }), // Cream Peach
|
||||
colord({ r: 255, g: 235, b: 235 }), // Blushed Petal
|
||||
colord({ r: 255, g: 240, b: 220 }), // Pastel Sand
|
||||
colord({ r: 255, g: 245, b: 210 }), // Soft Banana
|
||||
colord({ b: 0, g: 5, r: 0 }), // Black Mint
|
||||
colord({ b: 0, g: 15, r: 0 }), // Deep Forest
|
||||
colord({ b: 0, g: 25, r: 0 }), // Jungle
|
||||
colord({ b: 0, g: 35, r: 0 }), // Dark Emerald
|
||||
colord({ b: 0, g: 45, r: 0 }), // Green Moss
|
||||
colord({ b: 0, g: 55, r: 0 }), // Moss Shadow
|
||||
colord({ b: 0, g: 65, r: 0 }), // Dark Meadow
|
||||
colord({ b: 0, g: 75, r: 0 }), // Forest Fern
|
||||
colord({ b: 0, g: 85, r: 0 }), // Pine Leaf
|
||||
colord({ b: 0, g: 95, r: 0 }), // Shadow Grass
|
||||
colord({ b: 0, g: 105, r: 0 }), // Classic Green
|
||||
colord({ b: 0, g: 115, r: 0 }), // Deep Lime
|
||||
colord({ b: 0, g: 125, r: 0 }), // Dense Leaf
|
||||
colord({ b: 0, g: 135, r: 0 }), // Basil Green
|
||||
colord({ b: 0, g: 145, r: 0 }), // Organic Green
|
||||
colord({ b: 0, g: 155, r: 0 }), // Bitter Herb
|
||||
colord({ b: 0, g: 165, r: 0 }), // Raw Spinach
|
||||
colord({ b: 0, g: 175, r: 0 }), // Woodland
|
||||
colord({ b: 0, g: 185, r: 0 }), // Spring Weed
|
||||
colord({ b: 5, g: 195, r: 0 }), // Apple Stem
|
||||
colord({ b: 10, g: 205, r: 0 }), // Crisp Lettuce
|
||||
colord({ b: 15, g: 215, r: 0 }), // Vibrant Green
|
||||
colord({ b: 20, g: 225, r: 0 }), // Bright Herb
|
||||
colord({ b: 25, g: 235, r: 0 }), // Green Splash
|
||||
colord({ b: 30, g: 245, r: 0 }), // Mint Leaf
|
||||
colord({ b: 35, g: 255, r: 0 }), // Fresh Mint
|
||||
colord({ b: 45, g: 255, r: 10 }), // Neon Grass
|
||||
colord({ b: 55, g: 255, r: 20 }), // Lemon Balm
|
||||
colord({ b: 65, g: 255, r: 30 }), // Juicy Green
|
||||
colord({ b: 75, g: 255, r: 40 }), // Pear Tint
|
||||
colord({ b: 85, g: 255, r: 50 }), // Avocado Pastel
|
||||
colord({ b: 95, g: 255, r: 60 }), // Lime Glow
|
||||
colord({ b: 105, g: 255, r: 70 }), // Light Leaf
|
||||
colord({ b: 115, g: 255, r: 80 }), // Soft Fern
|
||||
colord({ b: 125, g: 255, r: 90 }), // Pastel Green
|
||||
colord({ b: 135, g: 255, r: 100 }), // Green Melon
|
||||
colord({ b: 145, g: 255, r: 110 }), // Herbal Mist
|
||||
colord({ b: 155, g: 255, r: 120 }), // Kiwi Foam
|
||||
colord({ b: 165, g: 255, r: 130 }), // Aloe Fresh
|
||||
colord({ b: 175, g: 255, r: 140 }), // Light Mint
|
||||
colord({ b: 255, g: 200, r: 150 }), // Cornflower Mist
|
||||
colord({ b: 185, g: 255, r: 150 }), // Green Sorbet
|
||||
colord({ b: 255, g: 215, r: 160 }), // Powder Blue
|
||||
colord({ b: 195, g: 255, r: 160 }), // Pastel Apple
|
||||
colord({ b: 255, g: 190, r: 170 }), // Periwinkle Ice
|
||||
colord({ b: 255, g: 225, r: 170 }), // Baby Sky
|
||||
colord({ b: 205, g: 255, r: 170 }), // Aloe Breeze
|
||||
colord({ b: 255, g: 180, r: 180 }), // Pale Indigo
|
||||
colord({ b: 250, g: 235, r: 180 }), // Aqua Pastel
|
||||
colord({ b: 215, g: 255, r: 180 }), // Pale Mint
|
||||
colord({ b: 195, g: 140, r: 190 }), // Fuchsia Tint
|
||||
colord({ b: 240, g: 245, r: 190 }), // Ice Mint
|
||||
colord({ b: 225, g: 255, r: 190 }), // Mint Water
|
||||
colord({ b: 200, g: 145, r: 195 }), // Dusky Rose
|
||||
colord({ b: 205, g: 150, r: 200 }), // Plum Frost
|
||||
colord({ b: 255, g: 170, r: 200 }), // Lilac Bloom
|
||||
colord({ b: 215, g: 255, r: 200 }), // Cool Aloe
|
||||
colord({ b: 235, g: 255, r: 200 }), // Cool Mist
|
||||
colord({ b: 210, g: 155, r: 205 }), // Berry Foam
|
||||
colord({ b: 215, g: 160, r: 210 }), // Grape Cloud
|
||||
colord({ b: 245, g: 255, r: 210 }), // Sea Mist
|
||||
colord({ b: 220, g: 165, r: 215 }), // Light Bloom
|
||||
colord({ b: 200, g: 255, r: 215 }), // Fresh Mint
|
||||
colord({ b: 255, g: 160, r: 220 }), // Violet Mist
|
||||
colord({ b: 225, g: 170, r: 220 }), // Cherry Blossom
|
||||
colord({ b: 255, g: 255, r: 220 }), // Pale Aqua
|
||||
colord({ b: 230, g: 175, r: 225 }), // Faded Rose
|
||||
colord({ b: 175, g: 255, r: 225 }), // Soft Lime
|
||||
colord({ b: 235, g: 180, r: 230 }), // Dreamy Mauve
|
||||
colord({ b: 255, g: 250, r: 230 }), // Sky Haze
|
||||
colord({ b: 255, g: 150, r: 235 }), // Orchid Glow
|
||||
colord({ b: 240, g: 185, r: 235 }), // Powder Violet
|
||||
colord({ b: 245, g: 190, r: 240 }), // Pastel Violet
|
||||
colord({ b: 255, g: 240, r: 240 }), // Frosted Lilac
|
||||
colord({ b: 160, g: 250, r: 240 }), // Citrus Wash
|
||||
colord({ b: 240, g: 160, r: 245 }), // Rose Lilac
|
||||
colord({ b: 250, g: 195, r: 245 }), // Soft Magenta
|
||||
colord({ b: 175, g: 245, r: 245 }), // Lemon Mist
|
||||
colord({ b: 255, g: 200, r: 250 }), // Lilac Cream
|
||||
colord({ b: 255, g: 230, r: 250 }), // Misty Mauve
|
||||
colord({ b: 225, g: 170, r: 255 }), // Bubblegum Pink
|
||||
colord({ b: 215, g: 185, r: 255 }), // Blush Mist
|
||||
colord({ b: 235, g: 195, r: 255 }), // Faded Fuchsia
|
||||
colord({ b: 220, g: 200, r: 255 }), // Cotton Rose
|
||||
colord({ b: 245, g: 205, r: 255 }), // Pastel Orchid
|
||||
colord({ b: 255, g: 205, r: 255 }), // Violet Bloom
|
||||
colord({ b: 230, g: 210, r: 255 }), // Pastel Blush
|
||||
colord({ b: 250, g: 210, r: 255 }), // Lavender Mist
|
||||
colord({ b: 255, g: 210, r: 255 }), // Orchid Mist
|
||||
colord({ b: 195, g: 215, r: 255 }), // Apricot Glow
|
||||
colord({ b: 245, g: 215, r: 255 }), // Rose Whisper
|
||||
colord({ b: 235, g: 220, r: 255 }), // Pink Mist
|
||||
colord({ b: 250, g: 220, r: 255 }), // Powder Petal
|
||||
colord({ b: 180, g: 225, r: 255 }), // Butter Peach
|
||||
colord({ b: 255, g: 225, r: 255 }), // Petal Mist
|
||||
colord({ b: 245, g: 230, r: 255 }), // Light Rose
|
||||
colord({ b: 200, g: 235, r: 255 }), // Cream Peach
|
||||
colord({ b: 235, g: 235, r: 255 }), // Blushed Petal
|
||||
colord({ b: 220, g: 240, r: 255 }), // Pastel Sand
|
||||
colord({ b: 210, g: 245, r: 255 }), // Soft Banana
|
||||
];
|
||||
|
||||
@@ -376,7 +376,6 @@ export class DefaultConfig implements Config {
|
||||
return 1_000_000;
|
||||
}
|
||||
|
||||
/* eslint-disable sort-keys */
|
||||
unitInfo(type: UnitType): UnitInfo {
|
||||
switch (type) {
|
||||
case UnitType.TransportShip:
|
||||
@@ -389,14 +388,14 @@ export class DefaultConfig implements Config {
|
||||
cost: this.costWrapper(UnitType.Warship, (numUnits: number) =>
|
||||
Math.min(1_000_000, (numUnits + 1) * 250_000),
|
||||
),
|
||||
territoryBound: false,
|
||||
maxHealth: 1000,
|
||||
territoryBound: false,
|
||||
};
|
||||
case UnitType.Shell:
|
||||
return {
|
||||
cost: () => 0n,
|
||||
territoryBound: false,
|
||||
damage: 250,
|
||||
territoryBound: false,
|
||||
};
|
||||
case UnitType.SAMMissile:
|
||||
return {
|
||||
@@ -405,13 +404,13 @@ export class DefaultConfig implements Config {
|
||||
};
|
||||
case UnitType.Port:
|
||||
return {
|
||||
canBuildTrainStation: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
|
||||
cost: this.costWrapper(UnitType.Port, (numUnits: number) =>
|
||||
Math.min(1_000_000, Math.pow(2, numUnits) * 125_000),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
|
||||
upgradable: true,
|
||||
canBuildTrainStation: true,
|
||||
};
|
||||
case UnitType.AtomBomb:
|
||||
return {
|
||||
@@ -440,47 +439,47 @@ export class DefaultConfig implements Config {
|
||||
};
|
||||
case UnitType.MissileSilo:
|
||||
return {
|
||||
constructionDuration: this.instantBuild() ? 0 : 10 * 10,
|
||||
cost: this.costWrapper(UnitType.MissileSilo, () => 1_000_000),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 10 * 10,
|
||||
upgradable: true,
|
||||
};
|
||||
case UnitType.DefensePost:
|
||||
return {
|
||||
constructionDuration: this.instantBuild() ? 0 : 5 * 10,
|
||||
cost: this.costWrapper(UnitType.DefensePost, (numUnits: number) =>
|
||||
Math.min(250_000, (numUnits + 1) * 50_000),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 5 * 10,
|
||||
};
|
||||
case UnitType.SAMLauncher:
|
||||
return {
|
||||
constructionDuration: this.instantBuild() ? 0 : 30 * 10,
|
||||
cost: this.costWrapper(UnitType.SAMLauncher, (numUnits: number) =>
|
||||
Math.min(3_000_000, (numUnits + 1) * 1_500_000),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 30 * 10,
|
||||
upgradable: true,
|
||||
};
|
||||
case UnitType.City:
|
||||
return {
|
||||
canBuildTrainStation: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
|
||||
cost: this.costWrapper(UnitType.City, (numUnits: number) =>
|
||||
Math.min(1_000_000, Math.pow(2, numUnits) * 125_000),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
|
||||
upgradable: true,
|
||||
canBuildTrainStation: true,
|
||||
};
|
||||
case UnitType.Factory:
|
||||
return {
|
||||
canBuildTrainStation: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
|
||||
cost: this.costWrapper(UnitType.Factory, (numUnits: number) =>
|
||||
Math.min(1_000_000, Math.pow(2, numUnits) * 125_000),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
|
||||
canBuildTrainStation: true,
|
||||
experimental: true,
|
||||
territoryBound: true,
|
||||
upgradable: true,
|
||||
};
|
||||
case UnitType.Construction:
|
||||
@@ -491,14 +490,13 @@ export class DefaultConfig implements Config {
|
||||
case UnitType.Train:
|
||||
return {
|
||||
cost: () => 0n,
|
||||
territoryBound: false,
|
||||
experimental: true,
|
||||
territoryBound: false,
|
||||
};
|
||||
default:
|
||||
assertNever(type);
|
||||
}
|
||||
}
|
||||
/* eslint-enable sort-keys */
|
||||
|
||||
private costWrapper(
|
||||
type: UnitType,
|
||||
|
||||
@@ -26,26 +26,24 @@ export class PastelTheme implements Theme {
|
||||
nationColors,
|
||||
);
|
||||
|
||||
/* eslint-disable sort-keys */
|
||||
private readonly background = colord({ r: 60, g: 60, b: 60 });
|
||||
private readonly shore = colord({ r: 204, g: 203, b: 158 });
|
||||
private readonly background = colord({ b: 60, g: 60, r: 60 });
|
||||
private readonly shore = colord({ b: 158, g: 203, r: 204 });
|
||||
private readonly falloutColors = [
|
||||
colord({ r: 120, g: 255, b: 71 }), // Original color
|
||||
colord({ r: 130, g: 255, b: 85 }), // Slightly lighter
|
||||
colord({ r: 110, g: 245, b: 65 }), // Slightly darker
|
||||
colord({ r: 125, g: 255, b: 75 }), // Warmer tint
|
||||
colord({ r: 115, g: 250, b: 68 }), // Cooler tint
|
||||
colord({ b: 71, g: 255, r: 120 }), // Original color
|
||||
colord({ b: 85, g: 255, r: 130 }), // Slightly lighter
|
||||
colord({ b: 65, g: 245, r: 110 }), // Slightly darker
|
||||
colord({ b: 75, g: 255, r: 125 }), // Warmer tint
|
||||
colord({ b: 68, g: 250, r: 115 }), // Cooler tint
|
||||
];
|
||||
private readonly water = colord({ r: 70, g: 132, b: 180 });
|
||||
private readonly shorelineWater = colord({ r: 100, g: 143, b: 255 });
|
||||
private readonly water = colord({ b: 180, g: 132, r: 70 });
|
||||
private readonly shorelineWater = colord({ b: 255, g: 143, r: 100 });
|
||||
|
||||
private readonly _selfColor = colord({ r: 0, g: 255, b: 0 });
|
||||
private readonly _allyColor = colord({ r: 255, g: 255, b: 0 });
|
||||
private readonly _neutralColor = colord({ r: 128, g: 128, b: 128 });
|
||||
private readonly _enemyColor = colord({ r: 255, g: 0, b: 0 });
|
||||
private readonly _selfColor = colord({ b: 0, g: 255, r: 0 });
|
||||
private readonly _allyColor = colord({ b: 0, g: 255, r: 255 });
|
||||
private readonly _neutralColor = colord({ b: 128, g: 128, r: 128 });
|
||||
private readonly _enemyColor = colord({ b: 0, g: 0, r: 255 });
|
||||
|
||||
private readonly _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
|
||||
/* eslint-enable sort-keys */
|
||||
private readonly _spawnHighlightColor = colord({ b: 79, g: 213, r: 255 });
|
||||
|
||||
teamColor(team: Team): Colord {
|
||||
return this.teamColorAllocator.assignTeamColor(team);
|
||||
@@ -71,24 +69,23 @@ export class PastelTheme implements Theme {
|
||||
|
||||
specialBuildingColor(player: PlayerView): Colord {
|
||||
const tc = this.territoryColor(player).rgba;
|
||||
/* eslint-disable sort-keys */
|
||||
|
||||
return colord({
|
||||
r: Math.max(tc.r - 50, 0),
|
||||
g: Math.max(tc.g - 50, 0),
|
||||
b: Math.max(tc.b - 50, 0),
|
||||
g: Math.max(tc.g - 50, 0),
|
||||
r: Math.max(tc.r - 50, 0),
|
||||
});
|
||||
/* eslint-enable sort-keys */
|
||||
}
|
||||
|
||||
railroadColor(player: PlayerView): Colord {
|
||||
const tc = this.territoryColor(player).rgba;
|
||||
/* eslint-disable sort-keys */
|
||||
|
||||
const color = colord({
|
||||
r: Math.max(tc.r - 10, 0),
|
||||
g: Math.max(tc.g - 10, 0),
|
||||
b: Math.max(tc.b - 10, 0),
|
||||
g: Math.max(tc.g - 10, 0),
|
||||
r: Math.max(tc.r - 10, 0),
|
||||
});
|
||||
/* eslint-enable sort-keys */
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
@@ -97,28 +94,26 @@ export class PastelTheme implements Theme {
|
||||
if (cached !== undefined) return cached;
|
||||
|
||||
const tc = this.territoryColor(player).rgba;
|
||||
/* eslint-disable sort-keys */
|
||||
|
||||
const color = colord({
|
||||
r: Math.max(tc.r - 40, 0),
|
||||
g: Math.max(tc.g - 40, 0),
|
||||
b: Math.max(tc.b - 40, 0),
|
||||
g: Math.max(tc.g - 40, 0),
|
||||
r: Math.max(tc.r - 40, 0),
|
||||
});
|
||||
/* eslint-enable sort-keys */
|
||||
|
||||
this.borderColorCache.set(player.id(), color);
|
||||
return color;
|
||||
}
|
||||
|
||||
/* eslint-disable sort-keys */
|
||||
defendedBorderColors(player: PlayerView): { light: Colord; dark: Colord } {
|
||||
return {
|
||||
light: this.territoryColor(player).darken(0.2),
|
||||
dark: this.territoryColor(player).darken(0.4),
|
||||
light: this.territoryColor(player).darken(0.2),
|
||||
};
|
||||
}
|
||||
|
||||
focusedBorderColor(): Colord {
|
||||
return colord({ r: 230, g: 230, b: 230 });
|
||||
return colord({ b: 230, g: 230, r: 230 });
|
||||
}
|
||||
|
||||
terrainColor(gm: GameMap, tile: TileRef): Colord {
|
||||
@@ -134,32 +129,31 @@ export class PastelTheme implements Theme {
|
||||
return this.shorelineWater;
|
||||
}
|
||||
return colord({
|
||||
r: Math.max(w.r - 10 + (11 - Math.min(mag, 10)), 0),
|
||||
g: Math.max(w.g - 10 + (11 - Math.min(mag, 10)), 0),
|
||||
b: Math.max(w.b - 10 + (11 - Math.min(mag, 10)), 0),
|
||||
g: Math.max(w.g - 10 + (11 - Math.min(mag, 10)), 0),
|
||||
r: Math.max(w.r - 10 + (11 - Math.min(mag, 10)), 0),
|
||||
});
|
||||
|
||||
case TerrainType.Plains:
|
||||
return colord({
|
||||
r: 190,
|
||||
g: 220 - 2 * mag,
|
||||
b: 138,
|
||||
g: 220 - 2 * mag,
|
||||
r: 190,
|
||||
});
|
||||
case TerrainType.Highland:
|
||||
return colord({
|
||||
r: 200 + 2 * mag,
|
||||
g: 183 + 2 * mag,
|
||||
b: 138 + 2 * mag,
|
||||
g: 183 + 2 * mag,
|
||||
r: 200 + 2 * mag,
|
||||
});
|
||||
case TerrainType.Mountain:
|
||||
return colord({
|
||||
r: 230 + mag / 2,
|
||||
g: 230 + mag / 2,
|
||||
b: 230 + mag / 2,
|
||||
g: 230 + mag / 2,
|
||||
r: 230 + mag / 2,
|
||||
});
|
||||
}
|
||||
}
|
||||
/* eslint-enable sort-keys */
|
||||
|
||||
backgroundColor(): Colord {
|
||||
return this.background;
|
||||
|
||||
@@ -26,26 +26,24 @@ export class PastelThemeDark implements Theme {
|
||||
nationColors,
|
||||
);
|
||||
|
||||
/* eslint-disable sort-keys */
|
||||
private readonly background = colord({ r: 0, g: 0, b: 0 });
|
||||
private readonly shore = colord({ r: 134, g: 133, b: 88 });
|
||||
private readonly background = colord({ b: 0, g: 0, r: 0 });
|
||||
private readonly shore = colord({ b: 88, g: 133, r: 134 });
|
||||
private readonly falloutColors = [
|
||||
colord({ r: 120, g: 255, b: 71 }), // Original color
|
||||
colord({ r: 130, g: 255, b: 85 }), // Slightly lighter
|
||||
colord({ r: 110, g: 245, b: 65 }), // Slightly darker
|
||||
colord({ r: 125, g: 255, b: 75 }), // Warmer tint
|
||||
colord({ r: 115, g: 250, b: 68 }), // Cooler tint
|
||||
colord({ b: 71, g: 255, r: 120 }), // Original color
|
||||
colord({ b: 85, g: 255, r: 130 }), // Slightly lighter
|
||||
colord({ b: 65, g: 245, r: 110 }), // Slightly darker
|
||||
colord({ b: 75, g: 255, r: 125 }), // Warmer tint
|
||||
colord({ b: 68, g: 250, r: 115 }), // Cooler tint
|
||||
];
|
||||
private readonly water = colord({ r: 14, g: 11, b: 30 });
|
||||
private readonly shorelineWater = colord({ r: 50, g: 50, b: 50 });
|
||||
private readonly water = colord({ b: 30, g: 11, r: 14 });
|
||||
private readonly shorelineWater = colord({ b: 50, g: 50, r: 50 });
|
||||
|
||||
private readonly _selfColor = colord({ r: 0, g: 255, b: 0 });
|
||||
private readonly _allyColor = colord({ r: 255, g: 255, b: 0 });
|
||||
private readonly _neutralColor = colord({ r: 128, g: 128, b: 128 });
|
||||
private readonly _enemyColor = colord({ r: 255, g: 0, b: 0 });
|
||||
private readonly _selfColor = colord({ b: 0, g: 255, r: 0 });
|
||||
private readonly _allyColor = colord({ b: 0, g: 255, r: 255 });
|
||||
private readonly _neutralColor = colord({ b: 128, g: 128, r: 128 });
|
||||
private readonly _enemyColor = colord({ b: 0, g: 0, r: 255 });
|
||||
|
||||
private readonly _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
|
||||
/* eslint-enable sort-keys */
|
||||
private readonly _spawnHighlightColor = colord({ b: 79, g: 213, r: 255 });
|
||||
|
||||
teamColor(team: Team): Colord {
|
||||
return this.teamColorAllocator.assignTeamColor(team);
|
||||
@@ -69,22 +67,21 @@ export class PastelThemeDark implements Theme {
|
||||
return player.type() === PlayerType.Human ? "#ffffff" : "#e6e6e6";
|
||||
}
|
||||
|
||||
/* eslint-disable sort-keys */
|
||||
specialBuildingColor(player: PlayerView): Colord {
|
||||
const tc = this.territoryColor(player).rgba;
|
||||
return colord({
|
||||
r: Math.max(tc.r - 50, 0),
|
||||
g: Math.max(tc.g - 50, 0),
|
||||
b: Math.max(tc.b - 50, 0),
|
||||
g: Math.max(tc.g - 50, 0),
|
||||
r: Math.max(tc.r - 50, 0),
|
||||
});
|
||||
}
|
||||
|
||||
railroadColor(player: PlayerView): Colord {
|
||||
const tc = this.territoryColor(player).rgba;
|
||||
const color = colord({
|
||||
r: Math.max(tc.r - 10, 0),
|
||||
g: Math.max(tc.g - 10, 0),
|
||||
b: Math.max(tc.b - 10, 0),
|
||||
g: Math.max(tc.g - 10, 0),
|
||||
r: Math.max(tc.r - 10, 0),
|
||||
});
|
||||
return color;
|
||||
}
|
||||
@@ -95,9 +92,9 @@ export class PastelThemeDark implements Theme {
|
||||
|
||||
const tc = this.territoryColor(player).rgba;
|
||||
const color = colord({
|
||||
r: Math.max(tc.r - 40, 0),
|
||||
g: Math.max(tc.g - 40, 0),
|
||||
b: Math.max(tc.b - 40, 0),
|
||||
g: Math.max(tc.g - 40, 0),
|
||||
r: Math.max(tc.r - 40, 0),
|
||||
});
|
||||
|
||||
this.borderColorCache.set(player.id(), color);
|
||||
@@ -106,13 +103,13 @@ export class PastelThemeDark implements Theme {
|
||||
|
||||
defendedBorderColors(player: PlayerView): { light: Colord; dark: Colord } {
|
||||
return {
|
||||
light: this.territoryColor(player).darken(0.2),
|
||||
dark: this.territoryColor(player).darken(0.4),
|
||||
light: this.territoryColor(player).darken(0.2),
|
||||
};
|
||||
}
|
||||
|
||||
focusedBorderColor(): Colord {
|
||||
return colord({ r: 255, g: 255, b: 255 });
|
||||
return colord({ b: 255, g: 255, r: 255 });
|
||||
}
|
||||
|
||||
terrainColor(gm: GameMap, tile: TileRef): Colord {
|
||||
@@ -129,33 +126,32 @@ export class PastelThemeDark implements Theme {
|
||||
}
|
||||
if (gm.magnitude(tile) < 10) {
|
||||
return colord({
|
||||
r: Math.max(w.r + 9 - mag, 0),
|
||||
g: Math.max(w.g + 9 - mag, 0),
|
||||
b: Math.max(w.b + 9 - mag, 0),
|
||||
g: Math.max(w.g + 9 - mag, 0),
|
||||
r: Math.max(w.r + 9 - mag, 0),
|
||||
});
|
||||
}
|
||||
return this.water;
|
||||
case TerrainType.Plains:
|
||||
return colord({
|
||||
r: 140,
|
||||
g: 170 - 2 * mag,
|
||||
b: 88,
|
||||
g: 170 - 2 * mag,
|
||||
r: 140,
|
||||
});
|
||||
case TerrainType.Highland:
|
||||
return colord({
|
||||
r: 150 + 2 * mag,
|
||||
g: 133 + 2 * mag,
|
||||
b: 88 + 2 * mag,
|
||||
g: 133 + 2 * mag,
|
||||
r: 150 + 2 * mag,
|
||||
});
|
||||
case TerrainType.Mountain:
|
||||
return colord({
|
||||
r: 180 + mag / 2,
|
||||
g: 180 + mag / 2,
|
||||
b: 180 + mag / 2,
|
||||
g: 180 + mag / 2,
|
||||
r: 180 + mag / 2,
|
||||
});
|
||||
}
|
||||
}
|
||||
/* eslint-enable sort-keys */
|
||||
|
||||
backgroundColor(): Colord {
|
||||
return this.background;
|
||||
|
||||
@@ -18,17 +18,16 @@ export class RailroadExecution implements Execution {
|
||||
return this.active;
|
||||
}
|
||||
|
||||
/* eslint-disable sort-keys */
|
||||
init(mg: Game, ticks: number): void {
|
||||
this.mg = mg;
|
||||
const { tiles } = this.railRoad;
|
||||
// Inverse direction computation for the first tile
|
||||
this.railTiles.push({
|
||||
tile: tiles[0],
|
||||
railType:
|
||||
tiles.length > 0
|
||||
? this.computeExtremityDirection(tiles[0], tiles[1])
|
||||
: RailType.VERTICAL,
|
||||
tile: tiles[0],
|
||||
});
|
||||
for (let i = 1; i < tiles.length - 1; i++) {
|
||||
const direction = this.computeDirection(
|
||||
@@ -36,10 +35,9 @@ export class RailroadExecution implements Execution {
|
||||
tiles[i],
|
||||
tiles[i + 1],
|
||||
);
|
||||
this.railTiles.push({ tile: tiles[i], railType: direction });
|
||||
this.railTiles.push({ railType: direction, tile: tiles[i] });
|
||||
}
|
||||
this.railTiles.push({
|
||||
tile: tiles[tiles.length - 1],
|
||||
railType:
|
||||
tiles.length > 0
|
||||
? this.computeExtremityDirection(
|
||||
@@ -47,9 +45,9 @@ export class RailroadExecution implements Execution {
|
||||
tiles[tiles.length - 2],
|
||||
)
|
||||
: RailType.VERTICAL,
|
||||
tile: tiles[tiles.length - 1],
|
||||
});
|
||||
}
|
||||
/* eslint-enable sort-keys */
|
||||
|
||||
private computeExtremityDirection(tile: TileRef, next: TileRef): RailType {
|
||||
if (this.mg === undefined) throw new Error("Not initialized");
|
||||
|
||||
@@ -97,8 +97,7 @@ class SAMTargetingSystem {
|
||||
}
|
||||
const interceptionTile = this.computeInterceptionTile(nuke.unit);
|
||||
if (interceptionTile !== undefined) {
|
||||
// eslint-disable-next-line sort-keys
|
||||
targets.push({ unit: nuke.unit, tile: interceptionTile });
|
||||
targets.push({ tile: interceptionTile, unit: nuke.unit });
|
||||
} else {
|
||||
// Store unreachable nukes in order to prevent useless interception computation
|
||||
this.storeUnreachableNukes(nuke.unit.id());
|
||||
|
||||
+14
-16
@@ -44,18 +44,16 @@ export const Duos = "Duos" as const;
|
||||
export const Trios = "Trios" as const;
|
||||
export const Quads = "Quads" as const;
|
||||
|
||||
/* eslint-disable sort-keys */
|
||||
export const ColoredTeams: Record<string, Team> = {
|
||||
Red: "Red",
|
||||
Blue: "Blue",
|
||||
Teal: "Teal",
|
||||
Purple: "Purple",
|
||||
Yellow: "Yellow",
|
||||
Orange: "Orange",
|
||||
Green: "Green",
|
||||
Bot: "Bot",
|
||||
Green: "Green",
|
||||
Orange: "Orange",
|
||||
Purple: "Purple",
|
||||
Red: "Red",
|
||||
Teal: "Teal",
|
||||
Yellow: "Yellow",
|
||||
} as const;
|
||||
/* eslint-enable sort-keys */
|
||||
|
||||
export enum GameMapType {
|
||||
World = "World",
|
||||
@@ -103,6 +101,14 @@ export const mapCategories: Record<string, GameMapType[]> = {
|
||||
GameMapType.Africa,
|
||||
GameMapType.Oceania,
|
||||
],
|
||||
|
||||
fantasy: [
|
||||
GameMapType.Pangaea,
|
||||
GameMapType.Pluto,
|
||||
GameMapType.MarsRevised,
|
||||
GameMapType.Mars,
|
||||
GameMapType.DeglaciatedAntarctica,
|
||||
],
|
||||
regional: [
|
||||
GameMapType.BlackSea,
|
||||
GameMapType.Britannia,
|
||||
@@ -120,14 +126,6 @@ export const mapCategories: Record<string, GameMapType[]> = {
|
||||
GameMapType.Italia,
|
||||
GameMapType.Yenisei,
|
||||
],
|
||||
// eslint-disable-next-line sort-keys
|
||||
fantasy: [
|
||||
GameMapType.Pangaea,
|
||||
GameMapType.Pluto,
|
||||
GameMapType.MarsRevised,
|
||||
GameMapType.Mars,
|
||||
GameMapType.DeglaciatedAntarctica,
|
||||
],
|
||||
};
|
||||
|
||||
export enum GameType {
|
||||
|
||||
+41
-44
@@ -128,58 +128,56 @@ export class PlayerImpl implements Player {
|
||||
);
|
||||
const stats = this.mg.stats().getPlayerStats(this);
|
||||
|
||||
/* eslint-disable sort-keys */
|
||||
return {
|
||||
type: GameUpdateType.Player,
|
||||
clientID: this.clientID(),
|
||||
name: this.name(),
|
||||
displayName: this.displayName(),
|
||||
id: this.id(),
|
||||
team: this.team() ?? undefined,
|
||||
smallID: this.smallID(),
|
||||
playerType: this.type(),
|
||||
isAlive: this.isAlive(),
|
||||
isDisconnected: this.isDisconnected(),
|
||||
tilesOwned: this.numTilesOwned(),
|
||||
gold: this._gold,
|
||||
troops: this.troops(),
|
||||
allies: this.alliances().map((a) => a.other(this).smallID()),
|
||||
embargoes: new Set([...this.embargoes.keys()].map((p) => p.toString())),
|
||||
isTraitor: this.isTraitor(),
|
||||
targets: this.targets().map((p) => p.smallID()),
|
||||
outgoingEmojis: this.outgoingEmojis(),
|
||||
outgoingAttacks: this._outgoingAttacks.map((a) => {
|
||||
return {
|
||||
attackerID: a.attacker().smallID(),
|
||||
targetID: a.target().smallID(),
|
||||
troops: a.troops(),
|
||||
id: a.id(),
|
||||
retreating: a.retreating(),
|
||||
} satisfies AttackUpdate;
|
||||
}),
|
||||
incomingAttacks: this._incomingAttacks.map((a) => {
|
||||
return {
|
||||
attackerID: a.attacker().smallID(),
|
||||
targetID: a.target().smallID(),
|
||||
troops: a.troops(),
|
||||
id: a.id(),
|
||||
retreating: a.retreating(),
|
||||
} satisfies AttackUpdate;
|
||||
}),
|
||||
outgoingAllianceRequests,
|
||||
alliances: this.alliances().map(
|
||||
(a) =>
|
||||
({
|
||||
id: a.id(),
|
||||
other: a.other(this).id(),
|
||||
createdAt: a.createdAt(),
|
||||
expiresAt: a.expiresAt(),
|
||||
id: a.id(),
|
||||
other: a.other(this).id(),
|
||||
}) satisfies AllianceView,
|
||||
),
|
||||
hasSpawned: this.hasSpawned(),
|
||||
allies: this.alliances().map((a) => a.other(this).smallID()),
|
||||
betrayals: stats?.betrayals,
|
||||
clientID: this.clientID(),
|
||||
displayName: this.displayName(),
|
||||
embargoes: new Set([...this.embargoes.keys()].map((p) => p.toString())),
|
||||
gold: this._gold,
|
||||
hasSpawned: this.hasSpawned(),
|
||||
id: this.id(),
|
||||
incomingAttacks: this._incomingAttacks.map((a) => {
|
||||
return {
|
||||
attackerID: a.attacker().smallID(),
|
||||
id: a.id(),
|
||||
retreating: a.retreating(),
|
||||
targetID: a.target().smallID(),
|
||||
troops: a.troops(),
|
||||
} satisfies AttackUpdate;
|
||||
}),
|
||||
isAlive: this.isAlive(),
|
||||
isDisconnected: this.isDisconnected(),
|
||||
isTraitor: this.isTraitor(),
|
||||
name: this.name(),
|
||||
outgoingAllianceRequests,
|
||||
outgoingAttacks: this._outgoingAttacks.map((a) => {
|
||||
return {
|
||||
attackerID: a.attacker().smallID(),
|
||||
id: a.id(),
|
||||
retreating: a.retreating(),
|
||||
targetID: a.target().smallID(),
|
||||
troops: a.troops(),
|
||||
} satisfies AttackUpdate;
|
||||
}),
|
||||
outgoingEmojis: this.outgoingEmojis(),
|
||||
playerType: this.type(),
|
||||
smallID: this.smallID(),
|
||||
targets: this.targets().map((p) => p.smallID()),
|
||||
team: this.team() ?? undefined,
|
||||
tilesOwned: this.numTilesOwned(),
|
||||
troops: this.troops(),
|
||||
type: GameUpdateType.Player,
|
||||
};
|
||||
/* eslint-enable sort-keys */
|
||||
}
|
||||
|
||||
smallID(): number {
|
||||
@@ -512,8 +510,7 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
target(other: Player): void {
|
||||
// eslint-disable-next-line sort-keys
|
||||
this.targets_.push({ tick: this.mg.ticks(), target: other });
|
||||
this.targets_.push({ target: other, tick: this.mg.ticks() });
|
||||
this.mg.target(this, other);
|
||||
}
|
||||
|
||||
|
||||
@@ -216,8 +216,7 @@ export class RailNetworkImpl implements RailNetwork {
|
||||
|
||||
const visited = new Set<TrainStation>();
|
||||
const queue: Array<{ station: TrainStation; distance: number }> = [
|
||||
// eslint-disable-next-line sort-keys
|
||||
{ station: start, distance: 0 },
|
||||
{ distance: 0, station: start },
|
||||
];
|
||||
|
||||
let head = 0;
|
||||
@@ -231,8 +230,7 @@ export class RailNetworkImpl implements RailNetwork {
|
||||
for (const neighbor of station.neighbors()) {
|
||||
if (neighbor === dest) return distance + 1;
|
||||
if (!visited.has(neighbor)) {
|
||||
// eslint-disable-next-line sort-keys
|
||||
queue.push({ station: neighbor, distance: distance + 1 });
|
||||
queue.push({ distance: distance + 1, station: neighbor });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +159,8 @@ export class UnitGrid {
|
||||
if (!unit.isActive()) continue;
|
||||
const distSquared = this.squaredDistanceFromTile(unit, tile);
|
||||
if (distSquared > rangeSquared) continue;
|
||||
// eslint-disable-next-line sort-keys
|
||||
const value = { unit, distSquared };
|
||||
|
||||
const value = { distSquared, unit };
|
||||
if (predicate !== undefined && !predicate(value)) continue;
|
||||
nearby.push(value);
|
||||
}
|
||||
|
||||
+14
-16
@@ -114,33 +114,31 @@ export class UnitImpl implements Unit {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
/* eslint-disable sort-keys */
|
||||
toUpdate(): UnitUpdate {
|
||||
return {
|
||||
type: GameUpdateType.Unit,
|
||||
unitType: this._type,
|
||||
constructionType: this._constructionType,
|
||||
hasTrainStation: this._hasTrainStation,
|
||||
health: this.hasHealth() ? Number(this._health) : undefined,
|
||||
id: this._id,
|
||||
troops: this._troops,
|
||||
ownerID: this._owner.smallID(),
|
||||
lastOwnerID: this._lastOwner?.smallID(),
|
||||
isActive: this._active,
|
||||
lastOwnerID: this._lastOwner?.smallID(),
|
||||
lastPos: this._lastTile,
|
||||
level: this.level(),
|
||||
loaded: this._loaded,
|
||||
missileTimerQueue: this._missileTimerQueue,
|
||||
ownerID: this._owner.smallID(),
|
||||
pos: this._tile,
|
||||
reachedTarget: this._reachedTarget,
|
||||
retreating: this._retreating,
|
||||
pos: this._tile,
|
||||
targetable: this._targetable,
|
||||
lastPos: this._lastTile,
|
||||
health: this.hasHealth() ? Number(this._health) : undefined,
|
||||
constructionType: this._constructionType,
|
||||
targetUnitId: this._targetUnit?.id() ?? undefined,
|
||||
targetTile: this.targetTile() ?? undefined,
|
||||
missileTimerQueue: this._missileTimerQueue,
|
||||
level: this.level(),
|
||||
hasTrainStation: this._hasTrainStation,
|
||||
targetUnitId: this._targetUnit?.id() ?? undefined,
|
||||
trainType: this._trainType,
|
||||
loaded: this._loaded,
|
||||
troops: this._troops,
|
||||
type: GameUpdateType.Unit,
|
||||
unitType: this._type,
|
||||
};
|
||||
}
|
||||
/* eslint-enable sort-keys */
|
||||
|
||||
type(): UnitType {
|
||||
return this._type;
|
||||
|
||||
@@ -148,8 +148,7 @@ export class PathFinder {
|
||||
}
|
||||
|
||||
if (this.game.manhattanDist(curr, dst) < dist) {
|
||||
// eslint-disable-next-line sort-keys
|
||||
return { type: PathFindResultType.Completed, node: curr };
|
||||
return { node: curr, type: PathFindResultType.Completed };
|
||||
}
|
||||
|
||||
if (this.computeFinished) {
|
||||
@@ -165,8 +164,8 @@ export class PathFinder {
|
||||
if (tile === undefined) {
|
||||
throw new Error("missing tile");
|
||||
}
|
||||
// eslint-disable-next-line sort-keys
|
||||
return { type: PathFindResultType.NextTile, node: tile };
|
||||
|
||||
return { node: tile, type: PathFindResultType.NextTile };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,18 +48,16 @@ export class SerialAStar<NodeType> implements AStar<NodeType> {
|
||||
this.sources.forEach((startPoint) => {
|
||||
this.fwdGScore.set(startPoint, 0);
|
||||
this.fwdOpenSet.add({
|
||||
tile: startPoint,
|
||||
// eslint-disable-next-line sort-keys
|
||||
fScore: this.heuristic(startPoint, dst),
|
||||
tile: startPoint,
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize backward search from destination
|
||||
this.bwdGScore.set(dst, 0);
|
||||
this.bwdOpenSet.add({
|
||||
tile: dst,
|
||||
// eslint-disable-next-line sort-keys
|
||||
fScore: this.heuristic(dst, this.findClosestSource(dst)),
|
||||
tile: dst,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -151,8 +149,8 @@ export class SerialAStar<NodeType> implements AStar<NodeType> {
|
||||
const fScore =
|
||||
totalG +
|
||||
this.heuristic(neighbor, isForward ? this.dst : this.closestSource);
|
||||
// eslint-disable-next-line sort-keys
|
||||
openSet.add({ tile: neighbor, fScore });
|
||||
|
||||
openSet.add({ fScore, tile: neighbor });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,7 @@ export function validateUsername(username: string): {
|
||||
error?: string;
|
||||
} {
|
||||
if (typeof username !== "string") {
|
||||
// eslint-disable-next-line sort-keys
|
||||
return { isValid: false, error: translateText("username.not_string") };
|
||||
return { error: translateText("username.not_string"), isValid: false };
|
||||
}
|
||||
|
||||
if (username.length < MIN_USERNAME_LENGTH) {
|
||||
|
||||
+15
-16
@@ -10,14 +10,13 @@ const log = logger.child({ component: "Archive" });
|
||||
|
||||
// R2 client configuration
|
||||
const r2 = new S3({
|
||||
region: "auto", // R2 ignores region, but it's required by the SDK
|
||||
/* eslint-disable sort-keys */
|
||||
endpoint: config.r2Endpoint(),
|
||||
credentials: {
|
||||
accessKeyId: config.r2AccessKey(),
|
||||
secretAccessKey: config.r2SecretKey(),
|
||||
},
|
||||
/* eslint-disable sort-keys */
|
||||
|
||||
endpoint: config.r2Endpoint(),
|
||||
region: "auto", // R2 ignores region, but it's required by the SDK
|
||||
});
|
||||
|
||||
const bucket = config.r2Bucket();
|
||||
@@ -49,8 +48,8 @@ export async function archive(gameRecord: GameRecord) {
|
||||
const { message, stack, name } = error;
|
||||
log.error(`${gameRecord.info.gameID}: Final archive error: ${error}`, {
|
||||
message,
|
||||
stack,
|
||||
name,
|
||||
stack,
|
||||
...(error && typeof error === "object" ? error : {}),
|
||||
});
|
||||
}
|
||||
@@ -60,11 +59,11 @@ async function archiveAnalyticsToR2(gameRecord: GameRecord) {
|
||||
// Create analytics data object
|
||||
const { info, version, gitCommit, subdomain, domain } = gameRecord;
|
||||
const analyticsData: AnalyticsRecord = {
|
||||
info,
|
||||
version,
|
||||
gitCommit,
|
||||
subdomain,
|
||||
domain,
|
||||
gitCommit,
|
||||
info,
|
||||
subdomain,
|
||||
version,
|
||||
};
|
||||
|
||||
try {
|
||||
@@ -72,10 +71,10 @@ async function archiveAnalyticsToR2(gameRecord: GameRecord) {
|
||||
const analyticsKey = `${info.gameID}.json`;
|
||||
|
||||
await r2.putObject({
|
||||
Bucket: bucket,
|
||||
Key: `${analyticsFolder}/${analyticsKey}`,
|
||||
Body: JSON.stringify(analyticsData, replacer),
|
||||
Bucket: bucket,
|
||||
ContentType: "application/json",
|
||||
Key: `${analyticsFolder}/${analyticsKey}`,
|
||||
});
|
||||
|
||||
log.info(`${info.gameID}: successfully wrote game analytics to R2`);
|
||||
@@ -91,8 +90,8 @@ async function archiveAnalyticsToR2(gameRecord: GameRecord) {
|
||||
const { message, stack, name } = error;
|
||||
log.error(`${info.gameID}: Error writing game analytics to R2: ${error}`, {
|
||||
message,
|
||||
stack,
|
||||
name,
|
||||
stack,
|
||||
...(error && typeof error === "object" ? error : {}),
|
||||
});
|
||||
throw error;
|
||||
@@ -110,10 +109,10 @@ async function archiveFullGameToR2(gameRecord: GameRecord) {
|
||||
|
||||
try {
|
||||
await r2.putObject({
|
||||
Bucket: bucket,
|
||||
Key: `${gameFolder}/${recordCopy.info.gameID}`,
|
||||
Body: JSON.stringify(recordCopy, replacer),
|
||||
Bucket: bucket,
|
||||
ContentType: "application/json",
|
||||
Key: `${gameFolder}/${recordCopy.info.gameID}`,
|
||||
});
|
||||
} catch (error) {
|
||||
log.error(`error saving game ${gameRecord.info.gameID}`);
|
||||
@@ -148,8 +147,8 @@ export async function readGameRecord(
|
||||
// Log the error for monitoring purposes
|
||||
log.error(`${gameId}: Error reading game record from R2: ${error}`, {
|
||||
message,
|
||||
stack,
|
||||
name,
|
||||
stack,
|
||||
...(error && typeof error === "object" ? error : {}),
|
||||
});
|
||||
|
||||
@@ -179,8 +178,8 @@ export async function gameRecordExists(gameId: GameID): Promise<boolean> {
|
||||
}
|
||||
log.error(`${gameId}: Error checking archive existence: ${error}`, {
|
||||
message,
|
||||
stack,
|
||||
name,
|
||||
stack,
|
||||
...(error && typeof error === "object" ? error : {}),
|
||||
});
|
||||
return false;
|
||||
|
||||
@@ -41,8 +41,8 @@ export class GameManager {
|
||||
{
|
||||
bots: 400,
|
||||
difficulty: Difficulty.Medium,
|
||||
disableNPCs: false,
|
||||
disabledUnits: [],
|
||||
disableNPCs: false,
|
||||
donateGold: false,
|
||||
donateTroops: false,
|
||||
gameMap: GameMapType.World,
|
||||
|
||||
@@ -29,9 +29,8 @@ if (config.otelEnabled()) {
|
||||
|
||||
// Add OTLP exporter for logs
|
||||
const logExporter = new OTLPLogExporter({
|
||||
url: `${config.otelEndpoint()}/v1/logs`,
|
||||
// eslint-disable-next-line sort-keys
|
||||
headers,
|
||||
url: `${config.otelEndpoint()}/v1/logs`,
|
||||
});
|
||||
|
||||
// Add a log processor with the exporter
|
||||
@@ -57,18 +56,18 @@ const addSeverityFormat = winston.format((info) => {
|
||||
|
||||
// Define your base/parent logger
|
||||
const logger = winston.createLogger({
|
||||
level: "info",
|
||||
/* eslint-disable sort-keys */
|
||||
defaultMeta: {
|
||||
environment: process.env.GAME_ENV ?? "prod",
|
||||
service: "openfront",
|
||||
},
|
||||
|
||||
format: winston.format.combine(
|
||||
winston.format.timestamp(),
|
||||
addSeverityFormat(),
|
||||
winston.format.json(),
|
||||
),
|
||||
defaultMeta: {
|
||||
service: "openfront",
|
||||
environment: process.env.GAME_ENV ?? "prod",
|
||||
},
|
||||
/* eslint-enable sort-keys */
|
||||
level: "info",
|
||||
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
new OpenTelemetryTransportV3(),
|
||||
|
||||
@@ -79,8 +79,8 @@ export class MapPlaylist {
|
||||
return {
|
||||
bots: 400,
|
||||
difficulty: Difficulty.Medium,
|
||||
disableNPCs: mode === GameMode.Team,
|
||||
disabledUnits: [],
|
||||
disableNPCs: mode === GameMode.Team,
|
||||
donateGold: true,
|
||||
donateTroops: true,
|
||||
gameMap: map,
|
||||
|
||||
@@ -17,15 +17,14 @@ export function getOtelResource() {
|
||||
|
||||
export function getPromLabels() {
|
||||
return {
|
||||
"service.instance.id": process.env.HOSTNAME,
|
||||
/* eslint-disable sort-keys */
|
||||
"openfront.environment": config.env(),
|
||||
"openfront.host": process.env.HOST,
|
||||
"openfront.domain": process.env.DOMAIN,
|
||||
"openfront.subdomain": process.env.SUBDOMAIN,
|
||||
"openfront.component": process.env.WORKER_ID
|
||||
? "Worker " + process.env.WORKER_ID
|
||||
: "Master",
|
||||
/* eslint-enable sort-keys */
|
||||
"openfront.domain": process.env.DOMAIN,
|
||||
|
||||
"openfront.environment": config.env(),
|
||||
"openfront.host": process.env.HOST,
|
||||
"openfront.subdomain": process.env.SUBDOMAIN,
|
||||
"service.instance.id": process.env.HOSTNAME,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ export function initWorkerMetrics(gameManager: GameManager): void {
|
||||
|
||||
// Configure the metric reader
|
||||
const metricReader = new PeriodicExportingMetricReader({
|
||||
exportIntervalMillis: 15000, // Export metrics every 15 seconds
|
||||
exporter: metricExporter,
|
||||
exportIntervalMillis: 15000, // Export metrics every 15 seconds
|
||||
});
|
||||
|
||||
// Create a meter provider
|
||||
|
||||
+1
-2
@@ -21,8 +21,7 @@ export async function verifyClientToken(
|
||||
config: ServerConfig,
|
||||
): Promise<TokenVerificationResult> {
|
||||
if (PersistentIdSchema.safeParse(token).success) {
|
||||
// eslint-disable-next-line sort-keys
|
||||
return { persistentId: token, claims: null };
|
||||
return { claims: null, persistentId: token };
|
||||
}
|
||||
try {
|
||||
const issuer = config.jwtIssuer();
|
||||
|
||||
@@ -15,8 +15,8 @@ describe("AllianceExtensionExecution", () => {
|
||||
"ocean_and_land",
|
||||
{
|
||||
infiniteGold: true,
|
||||
instantBuild: true,
|
||||
infiniteTroops: true,
|
||||
instantBuild: true,
|
||||
},
|
||||
[
|
||||
playerInfo("player1", PlayerType.Human),
|
||||
|
||||
@@ -29,8 +29,8 @@ describe("Attack", () => {
|
||||
beforeEach(async () => {
|
||||
game = await setup("ocean_and_land", {
|
||||
infiniteGold: true,
|
||||
instantBuild: true,
|
||||
infiniteTroops: true,
|
||||
instantBuild: true,
|
||||
});
|
||||
const attackerInfo = new PlayerInfo(
|
||||
"attacker dude",
|
||||
@@ -130,8 +130,8 @@ describe("Attack race condition with alliance requests", () => {
|
||||
beforeEach(async () => {
|
||||
game = await setup("ocean_and_land", {
|
||||
infiniteGold: true,
|
||||
instantBuild: true,
|
||||
infiniteTroops: true,
|
||||
instantBuild: true,
|
||||
});
|
||||
|
||||
const playerAInfo = new PlayerInfo(
|
||||
|
||||
@@ -77,11 +77,11 @@ describe("BotBehavior.handleAllianceRequests", () => {
|
||||
.mockReturnValue(new Array(alliancesCount));
|
||||
|
||||
const mockRequest = {
|
||||
requestor: () => requestor,
|
||||
recipient: () => player,
|
||||
createdAt: () => 0 as unknown as Tick,
|
||||
accept: jest.fn(),
|
||||
createdAt: () => 0 as unknown as Tick,
|
||||
recipient: () => player,
|
||||
reject: jest.fn(),
|
||||
requestor: () => requestor,
|
||||
} as unknown as AllianceRequest;
|
||||
|
||||
jest
|
||||
@@ -120,8 +120,8 @@ describe("BotBehavior.handleAllianceRequests", () => {
|
||||
|
||||
test("should accept alliance if requestor is much larger (> 3 times size of recipient) and has too many alliances (>= 3)", () => {
|
||||
const request = setupAllianceRequest({
|
||||
numTilesRequestor: 40,
|
||||
alliancesCount: 4,
|
||||
numTilesRequestor: 40,
|
||||
});
|
||||
|
||||
botBehavior.handleAllianceRequests();
|
||||
@@ -132,8 +132,8 @@ describe("BotBehavior.handleAllianceRequests", () => {
|
||||
|
||||
test("should accept alliance if requestor is much larger (> 3 times size of recipient) and does not have too many alliances (< 3)", () => {
|
||||
const request = setupAllianceRequest({
|
||||
numTilesRequestor: 40,
|
||||
alliancesCount: 2,
|
||||
numTilesRequestor: 40,
|
||||
});
|
||||
|
||||
botBehavior.handleAllianceRequests();
|
||||
@@ -171,8 +171,8 @@ describe("BotBehavior.handleAllianceExtensionRequests", () => {
|
||||
|
||||
mockPlayer = {
|
||||
alliances: jest.fn(() => [mockAlliance]),
|
||||
relation: jest.fn(),
|
||||
id: jest.fn(() => "bot_id"),
|
||||
relation: jest.fn(),
|
||||
type: jest.fn(() => PlayerType.FakeHuman),
|
||||
};
|
||||
|
||||
|
||||
+17
-17
@@ -1,6 +1,9 @@
|
||||
// Mocking the obscenity library to control its behavior in tests.
|
||||
jest.mock("obscenity", () => {
|
||||
return {
|
||||
collapseDuplicatesTransformer: () => ({}),
|
||||
englishDataset: { build: () => ({}) },
|
||||
englishRecommendedTransformers: {},
|
||||
RegExpMatcher: class {
|
||||
private readonly dummy: string[] = ["foo", "bar", "leet", "code"];
|
||||
constructor(_opts: any) {}
|
||||
@@ -16,9 +19,6 @@ jest.mock("obscenity", () => {
|
||||
return this.dummy.some((token) => decoded.includes(token));
|
||||
}
|
||||
},
|
||||
collapseDuplicatesTransformer: () => ({}),
|
||||
englishRecommendedTransformers: {},
|
||||
englishDataset: { build: () => ({}) },
|
||||
resolveConfusablesTransformer: () => ({}),
|
||||
resolveLeetSpeakTransformer: () => ({}),
|
||||
skipNonAlphabeticTransformer: () => ({}),
|
||||
@@ -53,14 +53,14 @@ describe("username.ts functions", () => {
|
||||
|
||||
describe("isProfaneUsername & fixProfaneUsername with leet decoding (mocked)", () => {
|
||||
test.each([
|
||||
{ username: "l33t", profane: true }, // decodes to "leet"
|
||||
{ username: "L33T", profane: true },
|
||||
{ username: "l33tc0de", profane: true }, // decodes to "leetcode", contains "leet" and "code"
|
||||
{ username: "L33TC0DE", profane: true },
|
||||
{ username: "foo123", profane: true }, // contains "foo"
|
||||
{ username: "b4r", profane: true }, // decodes to "bar"
|
||||
{ username: "safeName", profane: false },
|
||||
{ username: "s4f3", profane: false }, // decodes to "safe" but "safe" not in dummy list
|
||||
{ profane: true, username: "l33t" }, // decodes to "leet"
|
||||
{ profane: true, username: "L33T" },
|
||||
{ profane: true, username: "l33tc0de" }, // decodes to "leetcode", contains "leet" and "code"
|
||||
{ profane: true, username: "L33TC0DE" },
|
||||
{ profane: true, username: "foo123" }, // contains "foo"
|
||||
{ profane: true, username: "b4r" }, // decodes to "bar"
|
||||
{ profane: false, username: "safeName" },
|
||||
{ profane: false, username: "s4f3" }, // decodes to "safe" but "safe" not in dummy list
|
||||
])('isProfaneUsername("%s") → %s', ({ username, profane }) => {
|
||||
expect(isProfaneUsername(username)).toBe(profane);
|
||||
});
|
||||
@@ -113,17 +113,17 @@ describe("username.ts functions", () => {
|
||||
|
||||
describe("sanitizeUsername", () => {
|
||||
test.each([
|
||||
{ input: "GoodName", expected: "GoodName" },
|
||||
{ input: "a!", expected: "axx" },
|
||||
{ input: "a$%b", expected: "abx" },
|
||||
{ expected: "GoodName", input: "GoodName" },
|
||||
{ expected: "axx", input: "a!" },
|
||||
{ expected: "abx", input: "a$%b" },
|
||||
{
|
||||
input: "abc".repeat(10),
|
||||
expected: "abc"
|
||||
.repeat(Math.floor(MAX_USERNAME_LENGTH / 3))
|
||||
.slice(0, MAX_USERNAME_LENGTH),
|
||||
input: "abc".repeat(10),
|
||||
},
|
||||
{ input: "", expected: "xxx" },
|
||||
{ input: "Ünicode🐈Test!", expected: "Ünicode🐈Test" },
|
||||
{ expected: "xxx", input: "" },
|
||||
{ expected: "Ünicode🐈Test", input: "Ünicode🐈Test!" },
|
||||
])('sanitizeUsername("%s") → "%s"', ({ input, expected }) => {
|
||||
const out = sanitizeUsername(input);
|
||||
expect(out).toBe(expected);
|
||||
|
||||
+11
-11
@@ -16,14 +16,14 @@ import {
|
||||
import { ColoredTeams } from "../src/core/game/Game";
|
||||
|
||||
const mockColors: Colord[] = [
|
||||
colord({ r: 255, g: 0, b: 0 }),
|
||||
colord({ r: 0, g: 255, b: 0 }),
|
||||
colord({ r: 0, g: 0, b: 255 }),
|
||||
colord({ b: 0, g: 0, r: 255 }),
|
||||
colord({ b: 0, g: 255, r: 0 }),
|
||||
colord({ b: 255, g: 0, r: 0 }),
|
||||
];
|
||||
|
||||
const fallbackMockColors: Colord[] = [
|
||||
colord({ r: 0, g: 0, b: 0 }),
|
||||
colord({ r: 255, g: 255, b: 255 }),
|
||||
colord({ b: 0, g: 0, r: 0 }),
|
||||
colord({ b: 255, g: 255, r: 255 }),
|
||||
];
|
||||
|
||||
const fallbackColors = [...fallbackMockColors, ...mockColors];
|
||||
@@ -148,19 +148,19 @@ describe("ColorAllocator", () => {
|
||||
|
||||
describe("selectDistinctColor", () => {
|
||||
test("returns the most distant color", () => {
|
||||
const assignedColors = [colord({ r: 255, g: 0, b: 0 })]; // bright red
|
||||
const assignedColors = [colord({ b: 0, g: 0, r: 255 })]; // bright red
|
||||
const availableColors = [
|
||||
colord({ r: 254, g: 1, b: 1 }), // too close
|
||||
colord({ r: 0, g: 255, b: 0 }), // distinct green
|
||||
colord({ r: 0, g: 0, b: 255 }), // distinct blue
|
||||
colord({ b: 1, g: 1, r: 254 }), // too close
|
||||
colord({ b: 0, g: 255, r: 0 }), // distinct green
|
||||
colord({ b: 255, g: 0, r: 0 }), // distinct blue
|
||||
];
|
||||
|
||||
const result = selectDistinctColorIndex(availableColors, assignedColors);
|
||||
expect(result).not.toBeNull();
|
||||
const rgb = availableColors[result!].toRgb();
|
||||
expect([
|
||||
{ r: 0, g: 255, b: 0, a: 1 },
|
||||
{ r: 0, g: 0, b: 255, a: 1 },
|
||||
{ a: 1, b: 0, g: 255, r: 0 },
|
||||
{ a: 1, b: 255, g: 0, r: 0 },
|
||||
]).toContainEqual(rgb);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,8 +20,8 @@ describe("DeleteUnitExecution Security Tests", () => {
|
||||
beforeEach(async () => {
|
||||
game = await setup("plains", {
|
||||
infiniteGold: true,
|
||||
instantBuild: true,
|
||||
infiniteTroops: true,
|
||||
instantBuild: true,
|
||||
});
|
||||
|
||||
const player1Info = new PlayerInfo(
|
||||
|
||||
@@ -7,8 +7,8 @@ import { setup } from "./util/Setup";
|
||||
describe("Donate troops to an ally", () => {
|
||||
it("Troops should be successfully donated", async () => {
|
||||
const game = await setup("ocean_and_land", {
|
||||
infiniteTroops: false,
|
||||
donateTroops: true,
|
||||
infiniteTroops: false,
|
||||
});
|
||||
|
||||
const donorInfo = new PlayerInfo(
|
||||
@@ -70,8 +70,8 @@ describe("Donate troops to an ally", () => {
|
||||
describe("Donate gold to an ally", () => {
|
||||
it("Gold should be successfully donated", async () => {
|
||||
const game = await setup("ocean_and_land", {
|
||||
infiniteGold: false,
|
||||
donateGold: true,
|
||||
infiniteGold: false,
|
||||
});
|
||||
|
||||
const donorInfo = new PlayerInfo(
|
||||
@@ -134,8 +134,8 @@ describe("Donate gold to an ally", () => {
|
||||
describe("Donate troops to a non ally", () => {
|
||||
it("Troops should not be donated", async () => {
|
||||
const game = await setup("ocean_and_land", {
|
||||
infiniteTroops: false,
|
||||
donateTroops: true,
|
||||
infiniteTroops: false,
|
||||
});
|
||||
|
||||
const donorInfo = new PlayerInfo(
|
||||
@@ -194,8 +194,8 @@ describe("Donate troops to a non ally", () => {
|
||||
describe("Donate Gold to a non ally", () => {
|
||||
it("Gold should not be donated", async () => {
|
||||
const game = await setup("ocean_and_land", {
|
||||
infiniteGold: false,
|
||||
donateGold: true,
|
||||
infiniteGold: false,
|
||||
});
|
||||
|
||||
const donorInfo = new PlayerInfo(
|
||||
|
||||
@@ -14,8 +14,8 @@ import { TileRef } from "../../../src/core/game/GameMap";
|
||||
import { GameView, PlayerView } from "../../../src/core/game/GameView";
|
||||
|
||||
jest.mock("../../../src/client/Utils", () => ({
|
||||
translateText: jest.fn((key: string) => key),
|
||||
renderNumber: jest.fn((num: number) => num.toString()),
|
||||
translateText: jest.fn((key: string) => key),
|
||||
}));
|
||||
|
||||
jest.mock("../../../src/client/graphics/layers/BuildMenu", () => {
|
||||
@@ -23,46 +23,46 @@ jest.mock("../../../src/client/graphics/layers/BuildMenu", () => {
|
||||
return {
|
||||
flattenedBuildTable: [
|
||||
{
|
||||
unitType: UnitType.City,
|
||||
key: "unit_type.city",
|
||||
countable: true,
|
||||
description: "unit_type.city_desc",
|
||||
icon: "city-icon",
|
||||
countable: true,
|
||||
key: "unit_type.city",
|
||||
unitType: UnitType.City,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.Factory,
|
||||
key: "unit_type.factory",
|
||||
countable: true,
|
||||
description: "unit_type.factory_desc",
|
||||
icon: "factory-icon",
|
||||
countable: true,
|
||||
key: "unit_type.factory",
|
||||
unitType: UnitType.Factory,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.AtomBomb,
|
||||
key: "unit_type.atom_bomb",
|
||||
countable: false,
|
||||
description: "unit_type.atom_bomb_desc",
|
||||
icon: "atom-bomb-icon",
|
||||
countable: false,
|
||||
key: "unit_type.atom_bomb",
|
||||
unitType: UnitType.AtomBomb,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.Warship,
|
||||
key: "unit_type.warship",
|
||||
countable: true,
|
||||
description: "unit_type.warship_desc",
|
||||
icon: "warship-icon",
|
||||
countable: true,
|
||||
key: "unit_type.warship",
|
||||
unitType: UnitType.Warship,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.HydrogenBomb,
|
||||
key: "unit_type.hydrogen_bomb",
|
||||
countable: false,
|
||||
description: "unit_type.hydrogen_bomb_desc",
|
||||
icon: "hydrogen-bomb-icon",
|
||||
countable: false,
|
||||
key: "unit_type.hydrogen_bomb",
|
||||
unitType: UnitType.HydrogenBomb,
|
||||
},
|
||||
{
|
||||
unitType: UnitType.MIRV,
|
||||
key: "unit_type.mirv",
|
||||
countable: false,
|
||||
description: "unit_type.mirv_desc",
|
||||
icon: "mirv-icon",
|
||||
countable: false,
|
||||
key: "unit_type.mirv",
|
||||
unitType: UnitType.MIRV,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -95,17 +95,17 @@ describe("RadialMenuElements", () => {
|
||||
} as unknown as PlayerView;
|
||||
|
||||
mockGame = {
|
||||
inSpawnPhase: jest.fn(() => false),
|
||||
owner: jest.fn(() => mockPlayer),
|
||||
isLand: jest.fn(() => true),
|
||||
config: jest.fn(() => ({
|
||||
isUnitDisabled: jest.fn(() => false),
|
||||
theme: () => ({
|
||||
territoryColor: () => ({
|
||||
lighten: () => ({ alpha: () => ({ toRgbString: () => "#fff" }) }),
|
||||
}),
|
||||
}),
|
||||
isUnitDisabled: jest.fn(() => false),
|
||||
})),
|
||||
inSpawnPhase: jest.fn(() => false),
|
||||
isLand: jest.fn(() => true),
|
||||
owner: jest.fn(() => mockPlayer),
|
||||
} as unknown as GameView;
|
||||
|
||||
mockBuildMenu = {
|
||||
@@ -117,38 +117,38 @@ describe("RadialMenuElements", () => {
|
||||
|
||||
mockPlayerActions = {
|
||||
buildableUnits: [
|
||||
{ type: UnitType.City, canBuild: true },
|
||||
{ type: UnitType.Factory, canBuild: true },
|
||||
{ type: UnitType.AtomBomb, canBuild: true },
|
||||
{ type: UnitType.Warship, canBuild: true },
|
||||
{ type: UnitType.HydrogenBomb, canBuild: true },
|
||||
{ type: UnitType.MIRV, canBuild: true },
|
||||
{ type: UnitType.TransportShip, canBuild: true },
|
||||
{ canBuild: true, type: UnitType.City },
|
||||
{ canBuild: true, type: UnitType.Factory },
|
||||
{ canBuild: true, type: UnitType.AtomBomb },
|
||||
{ canBuild: true, type: UnitType.Warship },
|
||||
{ canBuild: true, type: UnitType.HydrogenBomb },
|
||||
{ canBuild: true, type: UnitType.MIRV },
|
||||
{ canBuild: true, type: UnitType.TransportShip },
|
||||
],
|
||||
canAttack: true,
|
||||
interaction: {
|
||||
canSendAllianceRequest: true,
|
||||
canBreakAlliance: false,
|
||||
canDonateTroops: true,
|
||||
canDonateGold: true,
|
||||
canDonateTroops: true,
|
||||
canSendAllianceRequest: true,
|
||||
},
|
||||
};
|
||||
|
||||
mockTile = {} as TileRef;
|
||||
|
||||
mockParams = {
|
||||
buildMenu: mockBuildMenu,
|
||||
chatIntegration: {} as any,
|
||||
closeMenu: jest.fn(),
|
||||
emojiTable: {} as any,
|
||||
eventBus: {} as any,
|
||||
game: mockGame,
|
||||
myPlayer: mockPlayer,
|
||||
playerActionHandler: {} as any,
|
||||
playerActions: mockPlayerActions,
|
||||
playerPanel: {} as any,
|
||||
selected: mockPlayer,
|
||||
tile: mockTile,
|
||||
playerActions: mockPlayerActions,
|
||||
game: mockGame,
|
||||
buildMenu: mockBuildMenu,
|
||||
emojiTable: {} as any,
|
||||
playerActionHandler: {} as any,
|
||||
playerPanel: {} as any,
|
||||
chatIntegration: {} as any,
|
||||
eventBus: {} as any,
|
||||
closeMenu: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -11,8 +11,6 @@ describe("UILayer", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
game = {
|
||||
width: () => 100,
|
||||
height: () => 100,
|
||||
config: () => ({
|
||||
theme: () => ({
|
||||
territoryColor: () => ({
|
||||
@@ -20,12 +18,14 @@ describe("UILayer", () => {
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
x: () => 10,
|
||||
y: () => 10,
|
||||
unitInfo: () => ({ maxHealth: 10, constructionDuration: 5 }),
|
||||
height: () => 100,
|
||||
myPlayer: () => ({ id: () => 1 }),
|
||||
ticks: () => 1,
|
||||
unitInfo: () => ({ constructionDuration: 5, maxHealth: 10 }),
|
||||
updatesSinceLastTick: () => undefined,
|
||||
width: () => 100,
|
||||
x: () => 10,
|
||||
y: () => 10,
|
||||
};
|
||||
eventBus = { on: jest.fn() };
|
||||
});
|
||||
@@ -46,10 +46,10 @@ describe("UILayer", () => {
|
||||
const ui = new UILayer(game, eventBus);
|
||||
ui.redraw();
|
||||
const unit = {
|
||||
type: () => "Warship",
|
||||
isActive: () => true,
|
||||
tile: () => ({}),
|
||||
owner: () => ({}),
|
||||
tile: () => ({}),
|
||||
type: () => "Warship",
|
||||
};
|
||||
const event = { isSelected: true, unit };
|
||||
ui.drawSelectionBox = jest.fn();
|
||||
@@ -61,13 +61,13 @@ describe("UILayer", () => {
|
||||
const ui = new UILayer(game, eventBus);
|
||||
ui.redraw();
|
||||
const unit = {
|
||||
id: () => 1,
|
||||
type: () => "Warship",
|
||||
health: () => 5,
|
||||
tile: () => ({}),
|
||||
owner: () => ({}),
|
||||
isActive: () => true,
|
||||
createdAt: () => 1,
|
||||
health: () => 5,
|
||||
id: () => 1,
|
||||
isActive: () => true,
|
||||
owner: () => ({}),
|
||||
tile: () => ({}),
|
||||
type: () => "Warship",
|
||||
} as unknown as UnitView;
|
||||
ui.drawHealthBar(unit);
|
||||
expect(ui["allHealthBars"].has(1)).toBe(true);
|
||||
@@ -90,12 +90,12 @@ describe("UILayer", () => {
|
||||
const ui = new UILayer(game, eventBus);
|
||||
ui.redraw();
|
||||
const unit = {
|
||||
id: () => 1,
|
||||
type: () => "Warship",
|
||||
health: () => 5,
|
||||
tile: () => ({}),
|
||||
owner: () => ({}),
|
||||
id: () => 1,
|
||||
isActive: () => true,
|
||||
owner: () => ({}),
|
||||
tile: () => ({}),
|
||||
type: () => "Warship",
|
||||
} as unknown as UnitView;
|
||||
ui.drawHealthBar(unit);
|
||||
expect(ui["allHealthBars"].has(1)).toBe(true);
|
||||
@@ -111,8 +111,8 @@ describe("UILayer", () => {
|
||||
ui.redraw();
|
||||
const unit = {
|
||||
id: () => 2,
|
||||
tile: () => ({}),
|
||||
isActive: () => true,
|
||||
tile: () => ({}),
|
||||
} as unknown as UnitView;
|
||||
ui.createLoadingBar(unit);
|
||||
expect(ui["allProgressBars"].has(2)).toBe(true);
|
||||
@@ -122,12 +122,12 @@ describe("UILayer", () => {
|
||||
const ui = new UILayer(game, eventBus);
|
||||
ui.redraw();
|
||||
const unit = {
|
||||
id: () => 2,
|
||||
type: () => "Construction",
|
||||
constructionType: () => "City",
|
||||
id: () => 2,
|
||||
isActive: () => true,
|
||||
owner: () => ({ id: () => 1 }),
|
||||
tile: () => ({}),
|
||||
isActive: () => true,
|
||||
type: () => "Construction",
|
||||
} as unknown as UnitView;
|
||||
ui.onUnitEvent(unit);
|
||||
expect(ui["allProgressBars"].has(2)).toBe(true);
|
||||
@@ -142,13 +142,13 @@ describe("UILayer", () => {
|
||||
const ui = new UILayer(game, eventBus);
|
||||
ui.redraw();
|
||||
const unit = {
|
||||
id: () => 2,
|
||||
type: () => "Construction",
|
||||
constructionType: () => "City",
|
||||
createdAt: () => 1,
|
||||
id: () => 2,
|
||||
isActive: () => true,
|
||||
owner: () => ({ id: () => 1 }),
|
||||
tile: () => ({}),
|
||||
isActive: () => true,
|
||||
createdAt: () => 1,
|
||||
type: () => "Construction",
|
||||
} as unknown as UnitView;
|
||||
ui.onUnitEvent(unit);
|
||||
expect(ui["allProgressBars"].has(2)).toBe(true);
|
||||
|
||||
@@ -86,9 +86,9 @@ describe("SAM", () => {
|
||||
const nuke = attacker.buildUnit(UnitType.AtomBomb, game.ref(1, 1), {
|
||||
targetTile: game.ref(3, 1),
|
||||
trajectory: [
|
||||
{ tile: game.ref(1, 1), targetable: true },
|
||||
{ tile: game.ref(2, 1), targetable: true },
|
||||
{ tile: game.ref(3, 1), targetable: true },
|
||||
{ targetable: true, tile: game.ref(1, 1) },
|
||||
{ targetable: true, tile: game.ref(2, 1) },
|
||||
{ targetable: true, tile: game.ref(3, 1) },
|
||||
],
|
||||
});
|
||||
executeTicks(game, 3);
|
||||
@@ -102,17 +102,17 @@ describe("SAM", () => {
|
||||
attacker.buildUnit(UnitType.AtomBomb, game.ref(2, 1), {
|
||||
targetTile: game.ref(3, 1),
|
||||
trajectory: [
|
||||
{ tile: game.ref(1, 1), targetable: true },
|
||||
{ tile: game.ref(2, 1), targetable: true },
|
||||
{ tile: game.ref(3, 1), targetable: true },
|
||||
{ targetable: true, tile: game.ref(1, 1) },
|
||||
{ targetable: true, tile: game.ref(2, 1) },
|
||||
{ targetable: true, tile: game.ref(3, 1) },
|
||||
],
|
||||
});
|
||||
attacker.buildUnit(UnitType.AtomBomb, game.ref(1, 2), {
|
||||
targetTile: game.ref(1, 3),
|
||||
trajectory: [
|
||||
{ tile: game.ref(1, 1), targetable: true },
|
||||
{ tile: game.ref(1, 2), targetable: true },
|
||||
{ tile: game.ref(1, 3), targetable: true },
|
||||
{ targetable: true, tile: game.ref(1, 1) },
|
||||
{ targetable: true, tile: game.ref(1, 2) },
|
||||
{ targetable: true, tile: game.ref(1, 3) },
|
||||
],
|
||||
});
|
||||
expect(attacker.units(UnitType.AtomBomb)).toHaveLength(2);
|
||||
@@ -130,9 +130,9 @@ describe("SAM", () => {
|
||||
const nuke = attacker.buildUnit(UnitType.AtomBomb, game.ref(1, 1), {
|
||||
targetTile: game.ref(1, 3),
|
||||
trajectory: [
|
||||
{ tile: game.ref(1, 1), targetable: true },
|
||||
{ tile: game.ref(2, 1), targetable: true },
|
||||
{ tile: game.ref(3, 1), targetable: true },
|
||||
{ targetable: true, tile: game.ref(1, 1) },
|
||||
{ targetable: true, tile: game.ref(2, 1) },
|
||||
{ targetable: true, tile: game.ref(3, 1) },
|
||||
],
|
||||
});
|
||||
|
||||
@@ -158,9 +158,9 @@ describe("SAM", () => {
|
||||
const nuke = attacker.buildUnit(UnitType.AtomBomb, game.ref(1, 1), {
|
||||
targetTile: game.ref(1, 3),
|
||||
trajectory: [
|
||||
{ tile: game.ref(1, 1), targetable: true },
|
||||
{ tile: game.ref(1, 2), targetable: true },
|
||||
{ tile: game.ref(1, 3), targetable: true },
|
||||
{ targetable: true, tile: game.ref(1, 1) },
|
||||
{ targetable: true, tile: game.ref(1, 2) },
|
||||
{ targetable: true, tile: game.ref(1, 3) },
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -22,66 +22,66 @@ describe("TradeShipExecution", () => {
|
||||
});
|
||||
game.displayMessage = jest.fn();
|
||||
origOwner = {
|
||||
canBuild: jest.fn(() => true),
|
||||
buildUnit: jest.fn((type, spawn, opts) => tradeShip),
|
||||
displayName: jest.fn(() => "Origin"),
|
||||
addGold: jest.fn(),
|
||||
units: jest.fn(() => [dstPort]),
|
||||
unitCount: jest.fn(() => 1),
|
||||
id: jest.fn(() => 1),
|
||||
buildUnit: jest.fn((type, spawn, opts) => tradeShip),
|
||||
canBuild: jest.fn(() => true),
|
||||
canTrade: jest.fn(() => true),
|
||||
displayName: jest.fn(() => "Origin"),
|
||||
id: jest.fn(() => 1),
|
||||
unitCount: jest.fn(() => 1),
|
||||
units: jest.fn(() => [dstPort]),
|
||||
} as any;
|
||||
|
||||
dstOwner = {
|
||||
id: jest.fn(() => 2),
|
||||
addGold: jest.fn(),
|
||||
displayName: jest.fn(() => "Destination"),
|
||||
units: jest.fn(() => [dstPort]),
|
||||
unitCount: jest.fn(() => 1),
|
||||
canTrade: jest.fn(() => true),
|
||||
displayName: jest.fn(() => "Destination"),
|
||||
id: jest.fn(() => 2),
|
||||
unitCount: jest.fn(() => 1),
|
||||
units: jest.fn(() => [dstPort]),
|
||||
} as any;
|
||||
|
||||
pirate = {
|
||||
id: jest.fn(() => 3),
|
||||
addGold: jest.fn(),
|
||||
displayName: jest.fn(() => "Destination"),
|
||||
units: jest.fn(() => [piratePort]),
|
||||
unitCount: jest.fn(() => 1),
|
||||
canTrade: jest.fn(() => true),
|
||||
displayName: jest.fn(() => "Destination"),
|
||||
id: jest.fn(() => 3),
|
||||
unitCount: jest.fn(() => 1),
|
||||
units: jest.fn(() => [piratePort]),
|
||||
} as any;
|
||||
|
||||
piratePort = {
|
||||
tile: jest.fn(() => 40011),
|
||||
owner: jest.fn(() => pirate),
|
||||
isActive: jest.fn(() => true),
|
||||
owner: jest.fn(() => pirate),
|
||||
tile: jest.fn(() => 40011),
|
||||
} as any;
|
||||
|
||||
srcPort = {
|
||||
tile: jest.fn(() => 20011),
|
||||
owner: jest.fn(() => origOwner),
|
||||
isActive: jest.fn(() => true),
|
||||
owner: jest.fn(() => origOwner),
|
||||
tile: jest.fn(() => 20011),
|
||||
} as any;
|
||||
|
||||
dstPort = {
|
||||
tile: jest.fn(() => 30015), // 15x15
|
||||
owner: jest.fn(() => dstOwner),
|
||||
isActive: jest.fn(() => true),
|
||||
owner: jest.fn(() => dstOwner),
|
||||
tile: jest.fn(() => 30015), // 15x15
|
||||
} as any;
|
||||
|
||||
tradeShip = {
|
||||
isActive: jest.fn(() => true),
|
||||
owner: jest.fn(() => origOwner),
|
||||
move: jest.fn(),
|
||||
setTargetUnit: jest.fn(),
|
||||
setSafeFromPirates: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
isActive: jest.fn(() => true),
|
||||
move: jest.fn(),
|
||||
owner: jest.fn(() => origOwner),
|
||||
setSafeFromPirates: jest.fn(),
|
||||
setTargetUnit: jest.fn(),
|
||||
tile: jest.fn(() => 2001),
|
||||
} as any;
|
||||
|
||||
tradeShipExecution = new TradeShipExecution(origOwner, srcPort, dstPort);
|
||||
tradeShipExecution.init(game, 0);
|
||||
tradeShipExecution["pathFinder"] = {
|
||||
nextTile: jest.fn(() => ({ type: 0, node: 2001 })),
|
||||
nextTile: jest.fn(() => ({ node: 2001, type: 0 })),
|
||||
} as any;
|
||||
tradeShipExecution["tradeShip"] = tradeShip;
|
||||
});
|
||||
@@ -112,7 +112,7 @@ describe("TradeShipExecution", () => {
|
||||
|
||||
it("should complete trade and award gold", () => {
|
||||
tradeShipExecution["pathFinder"] = {
|
||||
nextTile: jest.fn(() => ({ type: 2, node: 2001 })),
|
||||
nextTile: jest.fn(() => ({ node: 2001, type: 2 })),
|
||||
} as any;
|
||||
tradeShipExecution.tick(1);
|
||||
expect(tradeShip.delete).toHaveBeenCalledWith(false);
|
||||
|
||||
@@ -2,9 +2,9 @@ import { Cluster, TrainStation } from "../../../src/core/game/TrainStation";
|
||||
|
||||
const createMockStation = (id: string): jest.Mocked<TrainStation> => {
|
||||
return {
|
||||
getCluster: jest.fn(() => null),
|
||||
id,
|
||||
setCluster: jest.fn(),
|
||||
getCluster: jest.fn(() => null),
|
||||
} as any;
|
||||
};
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ describe("GameImpl", () => {
|
||||
beforeEach(async () => {
|
||||
game = await setup("ocean_and_land", {
|
||||
infiniteGold: true,
|
||||
instantBuild: true,
|
||||
infiniteTroops: true,
|
||||
instantBuild: true,
|
||||
});
|
||||
const attackerInfo = new PlayerInfo(
|
||||
"attacker dude",
|
||||
|
||||
@@ -11,17 +11,17 @@ const createMockStation = (unitId: number): any => {
|
||||
const cluster = new Cluster();
|
||||
const railroads = new Set<Railroad>();
|
||||
return {
|
||||
addRailroad: jest.fn(),
|
||||
clearRailroads: jest.fn(),
|
||||
getCluster: jest.fn(() => cluster),
|
||||
getRailroads: jest.fn(() => railroads),
|
||||
neighbors: jest.fn(() => []),
|
||||
setCluster: jest.fn(),
|
||||
tile: jest.fn(),
|
||||
unit: {
|
||||
id: unitId,
|
||||
setTrainStation: jest.fn(),
|
||||
},
|
||||
tile: jest.fn(),
|
||||
neighbors: jest.fn(() => []),
|
||||
getCluster: jest.fn(() => cluster),
|
||||
setCluster: jest.fn(),
|
||||
addRailroad: jest.fn(),
|
||||
getRailroads: jest.fn(() => railroads),
|
||||
clearRailroads: jest.fn(),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -55,22 +55,22 @@ describe("RailNetworkImpl", () => {
|
||||
beforeEach(() => {
|
||||
stationManager = {
|
||||
addStation: jest.fn(),
|
||||
removeStation: jest.fn(),
|
||||
findStation: jest.fn(),
|
||||
getAll: jest.fn(() => new Set()),
|
||||
removeStation: jest.fn(),
|
||||
};
|
||||
pathService = {
|
||||
findTilePath: jest.fn(() => [0]),
|
||||
findStationsPath: jest.fn(() => [0]),
|
||||
findTilePath: jest.fn(() => [0]),
|
||||
};
|
||||
game = {
|
||||
nearbyUnits: jest.fn(() => []),
|
||||
addExecution: jest.fn(),
|
||||
config: () => ({
|
||||
railroadMaxSize: () => 100,
|
||||
trainStationMaxRange: () => 80,
|
||||
trainStationMinRange: () => 10,
|
||||
railroadMaxSize: () => 100,
|
||||
}),
|
||||
nearbyUnits: jest.fn(() => []),
|
||||
};
|
||||
|
||||
network = new RailNetworkImpl(game, stationManager, pathService);
|
||||
@@ -153,7 +153,7 @@ describe("RailNetworkImpl", () => {
|
||||
neighborStation.getCluster = jest.fn(() => cluster);
|
||||
cluster.has = jest.fn(() => false);
|
||||
|
||||
const neighborUnit = { unit: neighborStation.unit, distSquared: 20 };
|
||||
const neighborUnit = { distSquared: 20, unit: neighborStation.unit };
|
||||
|
||||
game.nearbyUnits.mockReturnValue([neighborUnit]);
|
||||
stationManager.findStation.mockReturnValue(neighborStation);
|
||||
|
||||
@@ -14,34 +14,34 @@ describe("TrainStation", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
game = {
|
||||
ticks: jest.fn().mockReturnValue(123),
|
||||
addExecution: jest.fn(),
|
||||
addUpdate: jest.fn(),
|
||||
config: jest.fn().mockReturnValue({
|
||||
trainGold: (isFriendly: boolean) =>
|
||||
isFriendly ? BigInt(1000) : BigInt(500),
|
||||
}),
|
||||
addUpdate: jest.fn(),
|
||||
addExecution: jest.fn(),
|
||||
ticks: jest.fn().mockReturnValue(123),
|
||||
} as any;
|
||||
|
||||
player = {
|
||||
addGold: jest.fn(),
|
||||
id: 1,
|
||||
canTrade: jest.fn().mockReturnValue(true),
|
||||
id: 1,
|
||||
isFriendly: jest.fn().mockReturnValue(false),
|
||||
} as any;
|
||||
|
||||
unit = {
|
||||
owner: jest.fn().mockReturnValue(player),
|
||||
isActive: jest.fn().mockReturnValue(true),
|
||||
level: jest.fn().mockReturnValue(1),
|
||||
owner: jest.fn().mockReturnValue(player),
|
||||
tile: jest.fn().mockReturnValue({ x: 0, y: 0 }),
|
||||
type: jest.fn(),
|
||||
isActive: jest.fn().mockReturnValue(true),
|
||||
} as any;
|
||||
|
||||
trainExecution = {
|
||||
level: jest.fn(),
|
||||
loadCargo: jest.fn(),
|
||||
owner: jest.fn().mockReturnValue(player),
|
||||
level: jest.fn(),
|
||||
} as any;
|
||||
});
|
||||
|
||||
@@ -82,7 +82,7 @@ describe("TrainStation", () => {
|
||||
it("adds and retrieves neighbors", () => {
|
||||
const stationA = new TrainStation(game, unit);
|
||||
const stationB = new TrainStation(game, unit);
|
||||
const railRoad = { from: stationA, to: stationB, tiles: [] } as any;
|
||||
const railRoad = { from: stationA, tiles: [], to: stationB } as any;
|
||||
|
||||
stationA.addRailroad(railRoad);
|
||||
|
||||
@@ -96,8 +96,8 @@ describe("TrainStation", () => {
|
||||
|
||||
const railRoad = {
|
||||
from: stationA,
|
||||
to: stationB,
|
||||
tiles: [{ x: 1, y: 1 }],
|
||||
to: stationB,
|
||||
} as any;
|
||||
|
||||
stationA.addRailroad(railRoad);
|
||||
|
||||
@@ -7,22 +7,22 @@ describe("PrivilegeChecker.isCustomFlagAllowed (with mock cosmetics)", () => {
|
||||
};
|
||||
|
||||
const mockCosmetics: Cosmetics = {
|
||||
patterns: {},
|
||||
flag: {
|
||||
color: {
|
||||
a: { color: "#ff0000", flares: ["cosmetic:red"], name: "red" },
|
||||
b: { color: "#00ff00", name: "green" },
|
||||
c: { color: "#0000ff", flares: ["cosmetic:blue"], name: "blue" },
|
||||
},
|
||||
layers: {
|
||||
a: {
|
||||
name: "chocolate",
|
||||
flares: ["cosmetic:flags"],
|
||||
name: "chocolate",
|
||||
},
|
||||
b: { name: "center_hline" },
|
||||
c: { name: "admin_layer" },
|
||||
},
|
||||
color: {
|
||||
a: { color: "#ff0000", name: "red", flares: ["cosmetic:red"] },
|
||||
b: { color: "#00ff00", name: "green" },
|
||||
c: { color: "#0000ff", name: "blue", flares: ["cosmetic:blue"] },
|
||||
},
|
||||
},
|
||||
patterns: {},
|
||||
};
|
||||
|
||||
const checker = new PrivilegeCheckerImpl(mockCosmetics, dummyPatternDecoder);
|
||||
|
||||
Reference in New Issue
Block a user