mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 20:05:34 +00:00
teams (#349)
This commit is contained in:
@@ -138,6 +138,7 @@
|
||||
"title": "Private Lobby",
|
||||
"map": "Map",
|
||||
"difficulty": "Difficulty",
|
||||
"mode": "Mode",
|
||||
"options_title": "Options",
|
||||
"bots": "Bots: ",
|
||||
"bots_disabled": "Disabled",
|
||||
@@ -156,5 +157,9 @@
|
||||
"Balanced": "Balanced",
|
||||
"Intense": "Intense",
|
||||
"Impossible": "Impossible"
|
||||
},
|
||||
"game_mode": {
|
||||
"ffa": "Free for All",
|
||||
"teams": "Teams"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { PlayerID, GameMapType, Difficulty, GameType } from "../core/game/Game";
|
||||
import {
|
||||
PlayerID,
|
||||
GameMapType,
|
||||
Difficulty,
|
||||
GameType,
|
||||
TeamName,
|
||||
} from "../core/game/Game";
|
||||
import { EventBus } from "../core/EventBus";
|
||||
import { createRenderer, GameRenderer } from "./graphics/GameRenderer";
|
||||
import { InputHandler, MouseUpEvent } from "./InputHandler";
|
||||
@@ -165,6 +171,15 @@ export class ClientGameRunner {
|
||||
clientID: this.lobby.clientID,
|
||||
},
|
||||
];
|
||||
let winner: ClientID | TeamName | null = null;
|
||||
if (update.winnerType == "player") {
|
||||
winner = this.gameView
|
||||
.playerBySmallID(update.winner as number)
|
||||
.clientID();
|
||||
} else {
|
||||
winner = update.winner as TeamName;
|
||||
}
|
||||
|
||||
const record = createGameRecord(
|
||||
this.lobby.gameID,
|
||||
this.lobby.gameConfig,
|
||||
@@ -173,7 +188,8 @@ export class ClientGameRunner {
|
||||
[],
|
||||
LocalPersistantStats.startTime(),
|
||||
Date.now(),
|
||||
this.gameView.playerBySmallID(update.winnerID).id(),
|
||||
winner,
|
||||
update.winnerType,
|
||||
update.allPlayersStats,
|
||||
);
|
||||
LocalPersistantStats.endGame(record);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, query, property, state } from "lit/decorators.js";
|
||||
import { Difficulty, GameMapType, GameType } from "../core/game/Game";
|
||||
import { Difficulty, GameMapType, GameMode, GameType } from "../core/game/Game";
|
||||
import { GameConfig, GameInfo } from "../core/Schemas";
|
||||
import { consolex } from "../core/Consolex";
|
||||
import "./components/Difficulties";
|
||||
@@ -22,6 +22,7 @@ export class HostLobbyModal extends LitElement {
|
||||
@state() private selectedMap: GameMapType = GameMapType.World;
|
||||
@state() private selectedDifficulty: Difficulty = Difficulty.Medium;
|
||||
@state() private disableNPCs = false;
|
||||
@state() private gameMode: GameMode = GameMode.FFA;
|
||||
@state() private disableNukes: boolean = false;
|
||||
@state() private bots: number = 400;
|
||||
@state() private infiniteGold: boolean = false;
|
||||
@@ -135,31 +136,54 @@ export class HostLobbyModal extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Game Options -->
|
||||
<div class="options-section">
|
||||
<div class="option-title">
|
||||
${translateText("host_modal.options_title")}
|
||||
<!-- Game Mode Selection -->
|
||||
<div class="options-section">
|
||||
<div class="option-title">${translateText("host_modal.mode")}</div>
|
||||
<div class="option-cards">
|
||||
<div
|
||||
class="option-card ${this.gameMode === GameMode.FFA ? "selected" : ""}"
|
||||
@click=${() => this.handleGameModeSelection(GameMode.FFA)}
|
||||
>
|
||||
<div class="option-card-title">
|
||||
${translateText("game_mode.ffa")}
|
||||
</div>
|
||||
</div>
|
||||
<div class="option-cards">
|
||||
<label for="bots-count" class="option-card">
|
||||
<input
|
||||
type="range"
|
||||
id="bots-count"
|
||||
min="0"
|
||||
max="400"
|
||||
step="1"
|
||||
@input=${this.handleBotsChange}
|
||||
@change=${this.handleBotsChange}
|
||||
.value="${String(this.bots)}"
|
||||
/>
|
||||
<div class="option-card-title">
|
||||
<span>${translateText("host_modal.bots")}</span>${
|
||||
this.bots == 0
|
||||
? translateText("host_modal.bots_disabled")
|
||||
: this.bots
|
||||
}
|
||||
</div>
|
||||
</label>
|
||||
<div
|
||||
class="option-card ${this.gameMode === GameMode.Team ? "selected" : ""}"
|
||||
@click=${() => this.handleGameModeSelection(GameMode.Team)}
|
||||
>
|
||||
<div class="option-card-title">
|
||||
${translateText("game_mode.teams")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Game Options -->
|
||||
<div class="options-section">
|
||||
<div class="option-title">
|
||||
${translateText("host_modal.options_title")}
|
||||
</div>
|
||||
<div class="option-cards">
|
||||
<label for="bots-count" class="option-card">
|
||||
<input
|
||||
type="range"
|
||||
id="bots-count"
|
||||
min="0"
|
||||
max="400"
|
||||
step="1"
|
||||
@input=${this.handleBotsChange}
|
||||
@change=${this.handleBotsChange}
|
||||
.value="${String(this.bots)}"
|
||||
/>
|
||||
<div class="option-card-title">
|
||||
<span>${translateText("host_modal.bots")}</span>${
|
||||
this.bots == 0
|
||||
? translateText("host_modal.bots_disabled")
|
||||
: this.bots
|
||||
}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label
|
||||
for="host-modal-disable-npcs"
|
||||
@@ -244,37 +268,37 @@ export class HostLobbyModal extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lobby Selection -->
|
||||
<div class="options-section">
|
||||
<div class="option-title">
|
||||
${this.players.length}
|
||||
${
|
||||
this.players.length === 1
|
||||
? translateText("host_modal.player")
|
||||
: translateText("host_modal.players")
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="players-list">
|
||||
${this.players.map(
|
||||
(player) => html`<span class="player-tag">${player}</span>`,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
@click=${this.startGame}
|
||||
?disabled=${this.players.length < 2}
|
||||
class="start-game-button"
|
||||
>
|
||||
<!-- Lobby Selection -->
|
||||
<div class="options-section">
|
||||
<div class="option-title">
|
||||
${this.players.length}
|
||||
${
|
||||
this.players.length === 1
|
||||
? translateText("host_modal.waiting")
|
||||
: translateText("host_modal.start")
|
||||
? translateText("host_modal.player")
|
||||
: translateText("host_modal.players")
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="players-list">
|
||||
${this.players.map(
|
||||
(player) => html`<span class="player-tag">${player}</span>`,
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</o-modal>
|
||||
|
||||
<button
|
||||
@click=${this.startGame}
|
||||
?disabled=${this.players.length < 2}
|
||||
class="start-game-button"
|
||||
>
|
||||
${
|
||||
this.players.length === 1
|
||||
? translateText("host_modal.waiting")
|
||||
: translateText("host_modal.start")
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</o-modal>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -380,6 +404,11 @@ export class HostLobbyModal extends LitElement {
|
||||
this.putGameConfig();
|
||||
}
|
||||
|
||||
private async handleGameModeSelection(value: GameMode) {
|
||||
this.gameMode = value;
|
||||
this.putGameConfig();
|
||||
}
|
||||
|
||||
private async putGameConfig() {
|
||||
const config = await getServerConfigFromClient();
|
||||
const response = await fetch(
|
||||
@@ -398,6 +427,7 @@ export class HostLobbyModal extends LitElement {
|
||||
infiniteGold: this.infiniteGold,
|
||||
infiniteTroops: this.infiniteTroops,
|
||||
instantBuild: this.instantBuild,
|
||||
gameMode: this.gameMode,
|
||||
} as GameConfig),
|
||||
},
|
||||
);
|
||||
@@ -456,7 +486,7 @@ export class HostLobbyModal extends LitElement {
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data: GameInfo) => {
|
||||
console.log(`got response: ${data}`);
|
||||
console.log(`got game info response: ${JSON.stringify(data)}`);
|
||||
this.players = data.clients.map((p) => p.username);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
ClientID,
|
||||
ClientMessage,
|
||||
ClientMessageSchema,
|
||||
ClientSendWinnerMessage,
|
||||
GameConfig,
|
||||
GameID,
|
||||
GameRecordSchema,
|
||||
@@ -33,7 +34,7 @@ export class LocalServer {
|
||||
|
||||
private paused = false;
|
||||
|
||||
private winner: ClientID | null = null;
|
||||
private winner: ClientSendWinnerMessage = null;
|
||||
private allPlayersStats: AllPlayersStats = {};
|
||||
|
||||
constructor(
|
||||
@@ -124,7 +125,7 @@ export class LocalServer {
|
||||
}
|
||||
}
|
||||
if (clientMsg.type == "winner") {
|
||||
this.winner = clientMsg.winner;
|
||||
this.winner = clientMsg;
|
||||
this.allPlayersStats = clientMsg.allPlayersStats;
|
||||
}
|
||||
}
|
||||
@@ -164,7 +165,8 @@ export class LocalServer {
|
||||
this.turns,
|
||||
this.startedAt,
|
||||
Date.now(),
|
||||
this.winner,
|
||||
this.winner?.winner,
|
||||
this.winner?.winnerType,
|
||||
this.allPlayersStats,
|
||||
);
|
||||
if (!saveFullGame) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { Difficulty, GameMapType, GameType } from "../core/game/Game";
|
||||
import { Difficulty, GameMapType, GameMode, GameType } from "../core/game/Game";
|
||||
import { generateID as generateID } from "../core/Util";
|
||||
import { consolex } from "../core/Consolex";
|
||||
import "./components/Difficulties";
|
||||
@@ -28,6 +28,7 @@ export class SinglePlayerModal extends LitElement {
|
||||
@state() private infiniteTroops: boolean = false;
|
||||
@state() private instantBuild: boolean = false;
|
||||
@state() private useRandomMap: boolean = false;
|
||||
@state() private gameMode: GameMode = GameMode.FFA;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
@@ -107,6 +108,33 @@ export class SinglePlayerModal extends LitElement {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Game Mode Selection -->
|
||||
<div class="options-section">
|
||||
<div class="option-title">${translateText("host_modal.mode")}</div>
|
||||
<div class="option-cards">
|
||||
<div
|
||||
class="option-card ${this.gameMode === GameMode.FFA
|
||||
? "selected"
|
||||
: ""}"
|
||||
@click=${() => this.handleGameModeSelection(GameMode.FFA)}
|
||||
>
|
||||
<div class="option-card-title">
|
||||
${translateText("game_mode.ffa")}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="option-card ${this.gameMode === GameMode.Team
|
||||
? "selected"
|
||||
: ""}"
|
||||
@click=${() => this.handleGameModeSelection(GameMode.Team)}
|
||||
>
|
||||
<div class="option-card-title">
|
||||
${translateText("game_mode.teams")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Game Options -->
|
||||
<div class="options-section">
|
||||
<div class="option-title">
|
||||
@@ -122,7 +150,7 @@ export class SinglePlayerModal extends LitElement {
|
||||
step="1"
|
||||
@input=${this.handleBotsChange}
|
||||
@change=${this.handleBotsChange}
|
||||
.value="${this.bots}"
|
||||
.value="${String(this.bots)}"
|
||||
/>
|
||||
<div class="option-card-title">
|
||||
<span>${translateText("single_modal.bots")}</span>${this
|
||||
@@ -277,6 +305,10 @@ export class SinglePlayerModal extends LitElement {
|
||||
this.disableNukes = Boolean((e.target as HTMLInputElement).checked);
|
||||
}
|
||||
|
||||
private handleGameModeSelection(value: GameMode) {
|
||||
this.gameMode = value;
|
||||
}
|
||||
|
||||
private getRandomMap(): GameMapType {
|
||||
const maps = Object.values(GameMapType);
|
||||
const randIdx = Math.floor(Math.random() * maps.length);
|
||||
@@ -300,6 +332,7 @@ export class SinglePlayerModal extends LitElement {
|
||||
gameConfig: {
|
||||
gameMap: this.selectedMap,
|
||||
gameType: GameType.Singleplayer,
|
||||
gameMode: this.gameMode,
|
||||
difficulty: this.selectedDifficulty,
|
||||
disableNPCs: this.disableNPCs,
|
||||
disableNukes: this.disableNukes,
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Player,
|
||||
PlayerID,
|
||||
PlayerType,
|
||||
TeamName,
|
||||
Tick,
|
||||
UnitType,
|
||||
} from "../core/game/Game";
|
||||
@@ -124,8 +125,9 @@ export class SendSetTargetTroopRatioEvent implements GameEvent {
|
||||
|
||||
export class SendWinnerEvent implements GameEvent {
|
||||
constructor(
|
||||
public readonly winner: ClientID,
|
||||
public readonly winner: ClientID | TeamName,
|
||||
public readonly allPlayersStats: AllPlayersStats,
|
||||
public readonly winnerType: "player" | "team",
|
||||
) {}
|
||||
}
|
||||
export class SendHashEvent implements GameEvent {
|
||||
@@ -492,6 +494,7 @@ export class Transport {
|
||||
gameID: this.lobbyConfig.gameID,
|
||||
winner: event.winner,
|
||||
allPlayersStats: event.allPlayersStats,
|
||||
winnerType: event.winnerType,
|
||||
});
|
||||
this.sendMsg(JSON.stringify(msg));
|
||||
} else {
|
||||
|
||||
@@ -304,7 +304,7 @@ export class EventsDisplay extends LitElement implements Layer {
|
||||
onTargetPlayerEvent(event: TargetPlayerUpdate) {
|
||||
const other = this.game.playerBySmallID(event.playerID) as PlayerView;
|
||||
const myPlayer = this.game.playerByClientID(this.clientID) as PlayerView;
|
||||
if (!myPlayer || !myPlayer.isAlliedWith(other)) return;
|
||||
if (!myPlayer || !myPlayer.isFriendly(other)) return;
|
||||
|
||||
const target = this.game.playerBySmallID(event.targetID) as PlayerView;
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ export class NameLayer implements Layer {
|
||||
nameDiv.appendChild(flagImg);
|
||||
}
|
||||
nameDiv.classList.add("player-name");
|
||||
nameDiv.style.color = this.theme.textColor(player.info());
|
||||
nameDiv.style.color = this.theme.textColor(player);
|
||||
nameDiv.style.fontFamily = this.theme.font();
|
||||
nameDiv.style.whiteSpace = "nowrap";
|
||||
nameDiv.style.textOverflow = "ellipsis";
|
||||
@@ -213,7 +213,7 @@ export class NameLayer implements Layer {
|
||||
troopsDiv.classList.add("player-troops");
|
||||
troopsDiv.setAttribute("translate", "no");
|
||||
troopsDiv.textContent = renderTroops(player.troops());
|
||||
troopsDiv.style.color = this.theme.textColor(player.info());
|
||||
troopsDiv.style.color = this.theme.textColor(player);
|
||||
troopsDiv.style.fontFamily = this.theme.font();
|
||||
troopsDiv.style.zIndex = "3";
|
||||
troopsDiv.style.marginTop = "-5%";
|
||||
@@ -242,7 +242,7 @@ export class NameLayer implements Layer {
|
||||
// Calculate base size and scale
|
||||
const baseSize = Math.max(1, Math.floor(render.player.nameLocation().size));
|
||||
render.fontSize = Math.max(4, Math.floor(baseSize * 0.4));
|
||||
render.fontColor = this.theme.textColor(render.player.info());
|
||||
render.fontColor = this.theme.textColor(render.player);
|
||||
|
||||
// Screen space calculations
|
||||
const size = this.transformHandler.scale * baseSize;
|
||||
|
||||
@@ -163,7 +163,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
|
||||
private renderPlayerInfo(player: PlayerView) {
|
||||
const myPlayer = this.myPlayer();
|
||||
const isAlly = myPlayer?.isAlliedWith(player);
|
||||
const isFriendly = myPlayer?.isFriendly(player);
|
||||
let relationHtml = null;
|
||||
const attackingTroops = player
|
||||
.outgoingAttacks()
|
||||
@@ -198,7 +198,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
return html`
|
||||
<div class="p-2">
|
||||
<div
|
||||
class="text-bold text-sm lg:text-lg font-bold mb-1 inline-flex ${isAlly
|
||||
class="text-bold text-sm lg:text-lg font-bold mb-1 inline-flex ${isFriendly
|
||||
? "text-green-500"
|
||||
: "text-white"}"
|
||||
>
|
||||
@@ -244,7 +244,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
private renderUnitInfo(unit: UnitView) {
|
||||
const isAlly =
|
||||
(unit.owner() == this.myPlayer() ||
|
||||
this.myPlayer()?.isAlliedWith(unit.owner())) ??
|
||||
this.myPlayer()?.isFriendly(unit.owner())) ??
|
||||
false;
|
||||
|
||||
return html`
|
||||
|
||||
@@ -188,7 +188,7 @@ export class StructureLayer implements Layer {
|
||||
new Cell(this.game.x(tile), this.game.y(tile)),
|
||||
unit.type() == UnitType.Construction
|
||||
? underConstructionColor
|
||||
: this.theme.territoryColor(unit.owner().info()),
|
||||
: this.theme.territoryColor(unit.owner()),
|
||||
130,
|
||||
);
|
||||
}
|
||||
@@ -234,7 +234,7 @@ export class StructureLayer implements Layer {
|
||||
|
||||
if (!unit.isActive()) return;
|
||||
|
||||
let borderColor = this.theme.borderColor(unit.owner().info());
|
||||
let borderColor = this.theme.borderColor(unit.owner());
|
||||
if (unitType == UnitType.SAMLauncher && unit.isSamCooldown()) {
|
||||
borderColor = reloadingColor;
|
||||
} else if (unit.type() == UnitType.Construction) {
|
||||
@@ -257,7 +257,7 @@ export class StructureLayer implements Layer {
|
||||
height: number,
|
||||
unit: UnitView,
|
||||
) {
|
||||
let color = this.theme.borderColor(unit.owner().info());
|
||||
let color = this.theme.borderColor(unit.owner());
|
||||
if (unit.type() == UnitType.Construction) {
|
||||
color = underConstructionColor;
|
||||
}
|
||||
|
||||
@@ -251,14 +251,14 @@ export class TerritoryLayer implements Layer {
|
||||
this.paintCell(
|
||||
this.game.x(tile),
|
||||
this.game.y(tile),
|
||||
this.theme.defendedBorderColor(owner.info()),
|
||||
this.theme.defendedBorderColor(owner),
|
||||
255,
|
||||
);
|
||||
} else {
|
||||
this.paintCell(
|
||||
this.game.x(tile),
|
||||
this.game.y(tile),
|
||||
this.theme.borderColor(owner.info()),
|
||||
this.theme.borderColor(owner),
|
||||
255,
|
||||
);
|
||||
}
|
||||
@@ -266,7 +266,7 @@ export class TerritoryLayer implements Layer {
|
||||
this.paintCell(
|
||||
this.game.x(tile),
|
||||
this.game.y(tile),
|
||||
this.theme.territoryColor(owner.info()),
|
||||
this.theme.territoryColor(owner),
|
||||
150,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ export class UILayer implements Layer {
|
||||
baseOpacity + Math.sin(this.selectionAnimTime * 0.1) * pulseAmount;
|
||||
|
||||
// Get the unit's owner color for the box
|
||||
const ownerColor = this.theme.territoryColor(unit.owner().info());
|
||||
const ownerColor = this.theme.territoryColor(unit.owner());
|
||||
|
||||
// Create a brighter version of the owner color for the selection
|
||||
const selectionColor = ownerColor.lighten(0.2);
|
||||
|
||||
@@ -194,7 +194,7 @@ export class UnitLayer implements Layer {
|
||||
if (this.myPlayer == unit.owner()) {
|
||||
return Relationship.Self;
|
||||
}
|
||||
if (this.myPlayer.isAlliedWith(unit.owner())) {
|
||||
if (this.myPlayer.isFriendly(unit.owner())) {
|
||||
return Relationship.Ally;
|
||||
}
|
||||
return Relationship.Enemy;
|
||||
@@ -248,7 +248,7 @@ export class UnitLayer implements Layer {
|
||||
return;
|
||||
}
|
||||
|
||||
let outerColor = this.theme.territoryColor(unit.owner().info());
|
||||
let outerColor = this.theme.territoryColor(unit.owner());
|
||||
if (unit.warshipTargetId()) {
|
||||
const targetOwner = this.game
|
||||
.units()
|
||||
@@ -276,7 +276,7 @@ export class UnitLayer implements Layer {
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
this.theme.borderColor(unit.owner()),
|
||||
255,
|
||||
);
|
||||
}
|
||||
@@ -290,7 +290,7 @@ export class UnitLayer implements Layer {
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(unit.owner().info()),
|
||||
this.theme.territoryColor(unit.owner()),
|
||||
255,
|
||||
);
|
||||
}
|
||||
@@ -316,14 +316,14 @@ export class UnitLayer implements Layer {
|
||||
this.game.x(unit.tile()),
|
||||
this.game.y(unit.tile()),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
this.theme.borderColor(unit.owner()),
|
||||
255,
|
||||
);
|
||||
this.paintCell(
|
||||
this.game.x(unit.lastTile()),
|
||||
this.game.y(unit.lastTile()),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
this.theme.borderColor(unit.owner()),
|
||||
255,
|
||||
);
|
||||
}
|
||||
@@ -358,7 +358,7 @@ export class UnitLayer implements Layer {
|
||||
this.game.x(unit.tile()),
|
||||
this.game.y(unit.tile()),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
this.theme.borderColor(unit.owner()),
|
||||
255,
|
||||
);
|
||||
}
|
||||
@@ -408,7 +408,7 @@ export class UnitLayer implements Layer {
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
this.theme.borderColor(unit.owner()),
|
||||
255,
|
||||
);
|
||||
}
|
||||
@@ -426,7 +426,7 @@ export class UnitLayer implements Layer {
|
||||
this.game.x(unit.tile()),
|
||||
this.game.y(unit.tile()),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
this.theme.borderColor(unit.owner()),
|
||||
255,
|
||||
);
|
||||
}
|
||||
@@ -453,7 +453,7 @@ export class UnitLayer implements Layer {
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(unit.owner().info()),
|
||||
this.theme.territoryColor(unit.owner()),
|
||||
255,
|
||||
);
|
||||
}
|
||||
@@ -467,7 +467,7 @@ export class UnitLayer implements Layer {
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
this.theme.borderColor(unit.owner()),
|
||||
255,
|
||||
);
|
||||
}
|
||||
@@ -498,7 +498,7 @@ export class UnitLayer implements Layer {
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(unit.owner().info()),
|
||||
this.theme.territoryColor(unit.owner()),
|
||||
150,
|
||||
);
|
||||
}
|
||||
@@ -512,7 +512,7 @@ export class UnitLayer implements Layer {
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
this.theme.borderColor(unit.owner()),
|
||||
255,
|
||||
);
|
||||
}
|
||||
@@ -526,7 +526,7 @@ export class UnitLayer implements Layer {
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(unit.owner().info()),
|
||||
this.theme.territoryColor(unit.owner()),
|
||||
255,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { LitElement, html, css } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators.js";
|
||||
import { Player } from "../../../core/game/Game";
|
||||
import { Player, TeamName } from "../../../core/game/Game";
|
||||
import { ClientID } from "../../../core/Schemas";
|
||||
import { GameView, PlayerView } from "../../../core/game/GameView";
|
||||
import { Layer } from "./Layer";
|
||||
@@ -218,18 +218,38 @@ export class WinModal extends LitElement implements Layer {
|
||||
this.show();
|
||||
}
|
||||
this.game.updatesSinceLastTick()[GameUpdateType.Win].forEach((wu) => {
|
||||
const winner = this.game.playerBySmallID(wu.winnerID) as PlayerView;
|
||||
this.eventBus.emit(
|
||||
new SendWinnerEvent(winner.clientID(), wu.allPlayersStats),
|
||||
);
|
||||
if (winner == this.game.myPlayer()) {
|
||||
this._title = "You Won!";
|
||||
this.won = true;
|
||||
if (wu.winnerType === "team") {
|
||||
this.eventBus.emit(
|
||||
new SendWinnerEvent(
|
||||
wu.winner as TeamName,
|
||||
wu.allPlayersStats,
|
||||
"team",
|
||||
),
|
||||
);
|
||||
if (wu.winner == this.game.myPlayer()?.teamName()) {
|
||||
this._title = "Your team won!";
|
||||
this.won = true;
|
||||
} else {
|
||||
this._title = `${wu.winner} team has won!`;
|
||||
this.won = false;
|
||||
}
|
||||
this.show();
|
||||
} else {
|
||||
this._title = `${winner.name()} has won!`;
|
||||
this.won = false;
|
||||
const winner = this.game.playerBySmallID(
|
||||
wu.winner as number,
|
||||
) as PlayerView;
|
||||
this.eventBus.emit(
|
||||
new SendWinnerEvent(winner.clientID(), wu.allPlayersStats, "player"),
|
||||
);
|
||||
if (winner == this.game.myPlayer()) {
|
||||
this._title = "You Won!";
|
||||
this.won = true;
|
||||
} else {
|
||||
this._title = `${winner.name()} has won!`;
|
||||
this.won = false;
|
||||
}
|
||||
this.show();
|
||||
}
|
||||
this.show();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+10
-2
@@ -3,8 +3,10 @@ import {
|
||||
AllPlayers,
|
||||
Difficulty,
|
||||
GameMapType,
|
||||
GameMode,
|
||||
GameType,
|
||||
PlayerType,
|
||||
TeamName,
|
||||
UnitType,
|
||||
} from "./game/Game";
|
||||
|
||||
@@ -107,6 +109,7 @@ const GameConfigSchema = z.object({
|
||||
gameMap: z.nativeEnum(GameMapType),
|
||||
difficulty: z.nativeEnum(Difficulty),
|
||||
gameType: z.nativeEnum(GameType),
|
||||
gameMode: z.nativeEnum(GameMode),
|
||||
disableNPCs: z.boolean(),
|
||||
disableNukes: z.boolean(),
|
||||
bots: z.number().int().min(0).max(400),
|
||||
@@ -345,8 +348,9 @@ const ClientBaseMessageSchema = z.object({
|
||||
|
||||
export const ClientSendWinnerSchema = ClientBaseMessageSchema.extend({
|
||||
type: z.literal("winner"),
|
||||
winner: ID.nullable(),
|
||||
winner: ID.or(z.nativeEnum(TeamName)).nullable(),
|
||||
allPlayersStats: AllPlayersStatsSchema,
|
||||
winnerType: z.enum(["player", "team"]),
|
||||
});
|
||||
|
||||
export const ClientHashSchema = ClientBaseMessageSchema.extend({
|
||||
@@ -404,7 +408,11 @@ export const GameRecordSchema = z.object({
|
||||
date: SafeString,
|
||||
num_turns: z.number(),
|
||||
turns: z.array(TurnSchema),
|
||||
winner: ID.nullable(),
|
||||
winner: z
|
||||
.union([ID, z.nativeEnum(TeamName)])
|
||||
.nullable()
|
||||
.optional(),
|
||||
winnerType: z.enum(["player", "team"]).nullable().optional(),
|
||||
allPlayersStats: z.record(ID, PlayerStatsSchema),
|
||||
version: z.enum(["v0.0.1"]),
|
||||
gitCommit: z.string().nullable().optional(),
|
||||
|
||||
+4
-2
@@ -1,7 +1,7 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import twemoji from "twemoji";
|
||||
import DOMPurify from "dompurify";
|
||||
import { Cell, Game, Player, Unit } from "./game/Game";
|
||||
import { Cell, Game, Player, TeamName, Unit } from "./game/Game";
|
||||
import {
|
||||
AllPlayersStats,
|
||||
ClientID,
|
||||
@@ -255,7 +255,8 @@ export function createGameRecord(
|
||||
turns: Turn[],
|
||||
start: number,
|
||||
end: number,
|
||||
winner: ClientID | null,
|
||||
winner: ClientID | TeamName | null,
|
||||
winnerType: "player" | "team" | null,
|
||||
allPlayersStats: AllPlayersStats,
|
||||
): GameRecord {
|
||||
const record: GameRecord = {
|
||||
@@ -289,6 +290,7 @@ export function createGameRecord(
|
||||
);
|
||||
record.num_turns = turns.length;
|
||||
record.winner = winner;
|
||||
record.winnerType = winnerType;
|
||||
return record;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,352 @@
|
||||
import { colord, Colord } from "colord";
|
||||
|
||||
export const territoryColors: Colord[] = [
|
||||
colord({ r: 230, g: 100, b: 100 }), // Bright Red
|
||||
colord({ r: 100, g: 180, b: 230 }), // Sky Blue
|
||||
colord({ r: 230, g: 180, b: 80 }), // Golden Yellow
|
||||
colord({ r: 180, g: 100, b: 230 }), // Purple
|
||||
colord({ r: 80, g: 200, b: 120 }), // Emerald Green
|
||||
colord({ r: 230, g: 130, b: 180 }), // Pink
|
||||
colord({ r: 100, g: 160, b: 80 }), // Olive Green
|
||||
colord({ r: 230, g: 150, b: 100 }), // Peach
|
||||
colord({ r: 80, g: 130, b: 190 }), // Navy Blue
|
||||
colord({ r: 210, g: 210, b: 100 }), // Lime Yellow
|
||||
colord({ r: 190, g: 100, b: 130 }), // Maroon
|
||||
colord({ r: 100, g: 210, b: 210 }), // Turquoise
|
||||
colord({ r: 210, g: 140, b: 80 }), // Light Orange
|
||||
colord({ r: 150, g: 110, b: 190 }), // Lavender
|
||||
colord({ r: 180, g: 210, b: 120 }), // Light Green
|
||||
colord({ r: 210, g: 100, b: 160 }), // Hot Pink
|
||||
colord({ r: 100, g: 140, b: 110 }), // Sea Green
|
||||
colord({ r: 230, g: 180, b: 180 }), // Light Pink
|
||||
colord({ r: 120, g: 120, b: 190 }), // Periwinkle
|
||||
colord({ r: 190, g: 170, b: 100 }), // Sand
|
||||
colord({ r: 100, g: 180, b: 160 }), // Aquamarine
|
||||
colord({ r: 210, g: 160, b: 200 }), // Orchid
|
||||
colord({ r: 170, g: 190, b: 100 }), // Yellow Green
|
||||
colord({ r: 100, g: 130, b: 150 }), // Steel Blue
|
||||
colord({ r: 230, g: 140, b: 140 }), // Salmon
|
||||
colord({ r: 140, g: 180, b: 220 }), // Light Blue
|
||||
colord({ r: 200, g: 160, b: 110 }), // Tan
|
||||
colord({ r: 180, g: 130, b: 180 }), // Plum
|
||||
colord({ r: 130, g: 200, b: 130 }), // Light Sea Green
|
||||
colord({ r: 220, g: 120, b: 120 }), // Coral
|
||||
colord({ r: 120, g: 160, b: 200 }), // Cornflower Blue
|
||||
colord({ r: 200, g: 200, b: 140 }), // Khaki
|
||||
colord({ r: 160, g: 120, b: 160 }), // Purple Gray
|
||||
colord({ r: 140, g: 180, b: 140 }), // Dark Sea Green
|
||||
colord({ r: 200, g: 130, b: 110 }), // Dark Salmon
|
||||
colord({ r: 130, g: 170, b: 190 }), // Cadet Blue
|
||||
colord({ r: 190, g: 180, b: 160 }), // Tan Gray
|
||||
colord({ r: 170, g: 140, b: 190 }), // Medium Purple
|
||||
colord({ r: 160, g: 190, b: 160 }), // Pale Green
|
||||
colord({ r: 190, g: 150, b: 130 }), // Rosy Brown
|
||||
colord({ r: 140, g: 150, b: 180 }), // Light Slate Gray
|
||||
colord({ r: 180, g: 170, b: 140 }), // Dark Khaki
|
||||
colord({ r: 150, g: 130, b: 150 }), // Thistle
|
||||
colord({ r: 170, g: 190, b: 180 }), // Pale Blue Green
|
||||
colord({ r: 190, g: 140, b: 150 }), // Puce
|
||||
colord({ r: 130, g: 180, b: 170 }), // Medium Aquamarine
|
||||
colord({ r: 180, g: 160, b: 180 }), // Mauve
|
||||
colord({ r: 160, g: 180, b: 140 }), // Dark Olive Green
|
||||
colord({ r: 170, g: 150, b: 170 }), // Dusty Rose
|
||||
colord({ r: 100, g: 180, b: 230 }), // Sky Blue
|
||||
colord({ r: 230, g: 180, b: 80 }), // Golden Yellow
|
||||
colord({ r: 180, g: 100, b: 230 }), // Purple
|
||||
colord({ r: 80, g: 200, b: 120 }), // Emerald Green
|
||||
colord({ r: 230, g: 130, b: 180 }), // Pink
|
||||
colord({ r: 100, g: 160, b: 80 }), // Olive Green
|
||||
colord({ r: 230, g: 150, b: 100 }), // Peach
|
||||
colord({ r: 80, g: 130, b: 190 }), // Navy Blue
|
||||
colord({ r: 210, g: 210, b: 100 }), // Lime Yellow
|
||||
colord({ r: 190, g: 100, b: 130 }), // Maroon
|
||||
colord({ r: 100, g: 210, b: 210 }), // Turquoise
|
||||
colord({ r: 210, g: 140, b: 80 }), // Light Orange
|
||||
colord({ r: 150, g: 110, b: 190 }), // Lavender
|
||||
colord({ r: 180, g: 210, b: 120 }), // Light Green
|
||||
colord({ r: 210, g: 100, b: 160 }), // Hot Pink
|
||||
colord({ r: 100, g: 140, b: 110 }), // Sea Green
|
||||
colord({ r: 230, g: 180, b: 180 }), // Light Pink
|
||||
colord({ r: 120, g: 120, b: 190 }), // Periwinkle
|
||||
colord({ r: 190, g: 170, b: 100 }), // Sand
|
||||
colord({ r: 100, g: 180, b: 160 }), // Aquamarine
|
||||
colord({ r: 210, g: 160, b: 200 }), // Orchid
|
||||
colord({ r: 170, g: 190, b: 100 }), // Yellow Green
|
||||
colord({ r: 100, g: 130, b: 150 }), // Steel Blue
|
||||
colord({ r: 230, g: 140, b: 140 }), // Salmon
|
||||
colord({ r: 140, g: 180, b: 220 }), // Light Blue
|
||||
colord({ r: 200, g: 160, b: 110 }), // Tan
|
||||
colord({ r: 180, g: 130, b: 180 }), // Plum
|
||||
colord({ r: 130, g: 200, b: 130 }), // Light Sea Green
|
||||
colord({ r: 220, g: 120, b: 120 }), // Coral
|
||||
colord({ r: 120, g: 160, b: 200 }), // Cornflower Blue
|
||||
colord({ r: 200, g: 200, b: 140 }), // Khaki
|
||||
colord({ r: 160, g: 120, b: 160 }), // Purple Gray
|
||||
colord({ r: 140, g: 180, b: 140 }), // Dark Sea Green
|
||||
colord({ r: 200, g: 130, b: 110 }), // Dark Salmon
|
||||
colord({ r: 130, g: 170, b: 190 }), // Cadet Blue
|
||||
colord({ r: 190, g: 180, b: 160 }), // Tan Gray
|
||||
colord({ r: 170, g: 140, b: 190 }), // Medium Purple
|
||||
colord({ r: 160, g: 190, b: 160 }), // Pale Green
|
||||
colord({ r: 190, g: 150, b: 130 }), // Rosy Brown
|
||||
colord({ r: 140, g: 150, b: 180 }), // Light Slate Gray
|
||||
colord({ r: 180, g: 170, b: 140 }), // Dark Khaki
|
||||
colord({ r: 150, g: 130, b: 150 }), // Thistle
|
||||
colord({ r: 170, g: 190, b: 180 }), // Pale Blue Green
|
||||
colord({ r: 190, g: 140, b: 150 }), // Puce
|
||||
colord({ r: 130, g: 180, b: 170 }), // Medium Aquamarine
|
||||
colord({ r: 180, g: 160, b: 180 }), // Mauve
|
||||
colord({ r: 160, g: 180, b: 140 }), // Dark Olive Green
|
||||
colord({ r: 170, g: 150, b: 170 }), // Dusty Rose
|
||||
];
|
||||
|
||||
export const humanColors: Colord[] = [
|
||||
// Original set
|
||||
colord({ r: 235, g: 75, b: 75 }), // Bright Red
|
||||
colord({ r: 67, g: 190, b: 84 }), // Fresh Green
|
||||
colord({ r: 59, g: 130, b: 246 }), // Royal Blue
|
||||
colord({ r: 245, g: 158, b: 11 }), // Amber
|
||||
colord({ r: 236, g: 72, b: 153 }), // Deep Pink
|
||||
colord({ r: 48, g: 178, b: 180 }), // Teal
|
||||
colord({ r: 168, g: 85, b: 247 }), // Vibrant Purple
|
||||
colord({ r: 251, g: 191, b: 36 }), // Marigold
|
||||
colord({ r: 74, g: 222, b: 128 }), // Mint
|
||||
colord({ r: 239, g: 68, b: 68 }), // Crimson
|
||||
colord({ r: 34, g: 197, b: 94 }), // Emerald
|
||||
colord({ r: 96, g: 165, b: 250 }), // Sky Blue
|
||||
colord({ r: 249, g: 115, b: 22 }), // Tangerine
|
||||
colord({ r: 192, g: 132, b: 252 }), // Lavender
|
||||
colord({ r: 45, g: 212, b: 191 }), // Turquoise
|
||||
colord({ r: 244, g: 114, b: 182 }), // Rose
|
||||
colord({ r: 132, g: 204, b: 22 }), // Lime
|
||||
colord({ r: 56, g: 189, b: 248 }), // Light Blue
|
||||
colord({ r: 234, g: 179, b: 8 }), // Sunflower
|
||||
colord({ r: 217, g: 70, b: 239 }), // Fuchsia
|
||||
colord({ r: 16, g: 185, b: 129 }), // Sea Green
|
||||
colord({ r: 251, g: 146, b: 60 }), // Light Orange
|
||||
colord({ r: 147, g: 51, b: 234 }), // Bright Purple
|
||||
colord({ r: 79, g: 70, b: 229 }), // Indigo
|
||||
colord({ r: 245, g: 101, b: 101 }), // Coral
|
||||
colord({ r: 134, g: 239, b: 172 }), // Light Green
|
||||
colord({ r: 59, g: 130, b: 246 }), // Cerulean
|
||||
colord({ r: 253, g: 164, b: 175 }), // Salmon Pink
|
||||
colord({ r: 147, g: 197, b: 253 }), // Powder Blue
|
||||
colord({ r: 252, g: 211, b: 77 }), // Golden
|
||||
colord({ r: 190, g: 92, b: 251 }), // Amethyst
|
||||
colord({ r: 82, g: 183, b: 136 }), // Jade
|
||||
colord({ r: 248, g: 113, b: 113 }), // Warm Red
|
||||
colord({ r: 99, g: 202, b: 253 }), // Azure
|
||||
colord({ r: 240, g: 171, b: 252 }), // Orchid
|
||||
colord({ r: 163, g: 230, b: 53 }), // Yellow Green
|
||||
colord({ r: 234, g: 88, b: 12 }), // Burnt Orange
|
||||
colord({ r: 125, g: 211, b: 252 }), // Crystal Blue
|
||||
colord({ r: 251, g: 113, b: 133 }), // Watermelon
|
||||
colord({ r: 52, g: 211, b: 153 }), // Spearmint
|
||||
colord({ r: 167, g: 139, b: 250 }), // Periwinkle
|
||||
colord({ r: 245, g: 158, b: 11 }), // Honey
|
||||
colord({ r: 110, g: 231, b: 183 }), // Seafoam
|
||||
colord({ r: 233, g: 213, b: 255 }), // Light Lilac
|
||||
colord({ r: 202, g: 138, b: 4 }), // Rich Gold
|
||||
colord({ r: 151, g: 255, b: 187 }), // Fresh Mint
|
||||
colord({ r: 220, g: 38, b: 38 }), // Ruby
|
||||
colord({ r: 124, g: 58, b: 237 }), // Royal Purple
|
||||
colord({ r: 45, g: 212, b: 191 }), // Ocean
|
||||
colord({ r: 252, g: 165, b: 165 }), // Peach
|
||||
|
||||
// Additional 50 colors
|
||||
colord({ r: 179, g: 136, b: 255 }), // Light Purple
|
||||
colord({ r: 133, g: 77, b: 14 }), // Chocolate
|
||||
colord({ r: 52, g: 211, b: 153 }), // Aquamarine
|
||||
colord({ r: 234, g: 179, b: 8 }), // Mustard
|
||||
colord({ r: 236, g: 72, b: 153 }), // Hot Pink
|
||||
colord({ r: 147, g: 197, b: 253 }), // Sky
|
||||
colord({ r: 249, g: 115, b: 22 }), // Pumpkin
|
||||
colord({ r: 167, g: 139, b: 250 }), // Iris
|
||||
colord({ r: 16, g: 185, b: 129 }), // Pine
|
||||
colord({ r: 251, g: 146, b: 60 }), // Mango
|
||||
colord({ r: 192, g: 132, b: 252 }), // Wisteria
|
||||
colord({ r: 79, g: 70, b: 229 }), // Sapphire
|
||||
colord({ r: 245, g: 101, b: 101 }), // Salmon
|
||||
colord({ r: 134, g: 239, b: 172 }), // Spring Green
|
||||
colord({ r: 59, g: 130, b: 246 }), // Ocean Blue
|
||||
colord({ r: 253, g: 164, b: 175 }), // Rose Gold
|
||||
colord({ r: 16, g: 185, b: 129 }), // Forest
|
||||
colord({ r: 252, g: 211, b: 77 }), // Sunshine
|
||||
colord({ r: 190, g: 92, b: 251 }), // Grape
|
||||
colord({ r: 82, g: 183, b: 136 }), // Eucalyptus
|
||||
colord({ r: 248, g: 113, b: 113 }), // Cherry
|
||||
colord({ r: 99, g: 202, b: 253 }), // Arctic
|
||||
colord({ r: 240, g: 171, b: 252 }), // Lilac
|
||||
colord({ r: 163, g: 230, b: 53 }), // Chartreuse
|
||||
colord({ r: 234, g: 88, b: 12 }), // Rust
|
||||
colord({ r: 125, g: 211, b: 252 }), // Ice Blue
|
||||
colord({ r: 251, g: 113, b: 133 }), // Strawberry
|
||||
colord({ r: 52, g: 211, b: 153 }), // Sage
|
||||
colord({ r: 167, g: 139, b: 250 }), // Violet
|
||||
colord({ r: 245, g: 158, b: 11 }), // Apricot
|
||||
colord({ r: 110, g: 231, b: 183 }), // Mint Green
|
||||
colord({ r: 233, g: 213, b: 255 }), // Thistle
|
||||
colord({ r: 202, g: 138, b: 4 }), // Bronze
|
||||
colord({ r: 151, g: 255, b: 187 }), // Pistachio
|
||||
colord({ r: 220, g: 38, b: 38 }), // Fire Engine
|
||||
colord({ r: 124, g: 58, b: 237 }), // Electric Purple
|
||||
colord({ r: 45, g: 212, b: 191 }), // Caribbean
|
||||
colord({ r: 252, g: 165, b: 165 }), // Melon
|
||||
colord({ r: 168, g: 85, b: 247 }), // Byzantium
|
||||
colord({ r: 74, g: 222, b: 128 }), // Kelly Green
|
||||
colord({ r: 239, g: 68, b: 68 }), // Cardinal
|
||||
colord({ r: 34, g: 197, b: 94 }), // Shamrock
|
||||
colord({ r: 96, g: 165, b: 250 }), // Marina
|
||||
colord({ r: 249, g: 115, b: 22 }), // Carrot
|
||||
colord({ r: 192, g: 132, b: 252 }), // Heliotrope
|
||||
colord({ r: 45, g: 212, b: 191 }), // Lagoon
|
||||
colord({ r: 244, g: 114, b: 182 }), // Bubble Gum
|
||||
colord({ r: 132, g: 204, b: 22 }), // Apple
|
||||
colord({ r: 56, g: 189, b: 248 }), // Electric Blue
|
||||
colord({ r: 234, g: 179, b: 8 }), // Daffodil
|
||||
];
|
||||
|
||||
export const redShades = [
|
||||
colord({ r: 210, g: 0, b: 0 }), // Most distinct colors sorted by brightness
|
||||
colord({ r: 225, g: 0, b: 0 }),
|
||||
colord({ r: 240, g: 0, b: 0 }),
|
||||
colord({ r: 255, g: 0, b: 0 }),
|
||||
colord({ r: 210, g: 21, b: 21 }),
|
||||
colord({ r: 225, g: 23, b: 23 }),
|
||||
colord({ r: 240, g: 25, b: 25 }),
|
||||
colord({ r: 255, g: 26, b: 26 }),
|
||||
colord({ r: 210, g: 42, b: 42 }),
|
||||
colord({ r: 225, g: 45, b: 45 }),
|
||||
colord({ r: 240, g: 48, b: 48 }),
|
||||
colord({ r: 255, g: 51, b: 51 }),
|
||||
colord({ r: 195, g: 59, b: 59 }),
|
||||
colord({ r: 210, g: 63, b: 63 }),
|
||||
colord({ r: 225, g: 68, b: 68 }),
|
||||
colord({ r: 240, g: 72, b: 72 }),
|
||||
colord({ r: 255, g: 77, b: 77 }),
|
||||
colord({ r: 180, g: 72, b: 72 }),
|
||||
colord({ r: 195, g: 78, b: 78 }),
|
||||
colord({ r: 210, g: 84, b: 84 }),
|
||||
colord({ r: 225, g: 90, b: 90 }),
|
||||
colord({ r: 240, g: 96, b: 96 }),
|
||||
colord({ r: 255, g: 102, b: 102 }),
|
||||
colord({ r: 165, g: 83, b: 83 }),
|
||||
colord({ r: 180, g: 90, b: 90 }),
|
||||
colord({ r: 195, g: 98, b: 98 }),
|
||||
colord({ r: 210, g: 105, b: 105 }),
|
||||
colord({ r: 225, g: 113, b: 113 }),
|
||||
colord({ r: 240, g: 120, b: 120 }),
|
||||
colord({ r: 255, g: 128, b: 128 }),
|
||||
colord({ r: 150, g: 90, b: 90 }),
|
||||
colord({ r: 165, g: 99, b: 99 }),
|
||||
colord({ r: 180, g: 108, b: 108 }),
|
||||
colord({ r: 195, g: 117, b: 117 }),
|
||||
colord({ r: 210, g: 126, b: 126 }),
|
||||
colord({ r: 225, g: 135, b: 135 }),
|
||||
colord({ r: 240, g: 144, b: 144 }),
|
||||
colord({ r: 255, g: 153, b: 153 }),
|
||||
colord({ r: 135, g: 95, b: 95 }),
|
||||
colord({ r: 150, g: 105, b: 105 }),
|
||||
colord({ r: 165, g: 116, b: 116 }),
|
||||
colord({ r: 180, g: 126, b: 126 }),
|
||||
colord({ r: 195, g: 137, b: 137 }),
|
||||
colord({ r: 210, g: 147, b: 147 }),
|
||||
colord({ r: 225, g: 158, b: 158 }),
|
||||
colord({ r: 240, g: 168, b: 168 }),
|
||||
colord({ r: 255, g: 179, b: 179 }),
|
||||
colord({ r: 120, g: 108, b: 108 }),
|
||||
colord({ r: 135, g: 122, b: 122 }),
|
||||
colord({ r: 150, g: 135, b: 135 }),
|
||||
colord({ r: 165, g: 149, b: 149 }),
|
||||
colord({ r: 180, g: 162, b: 162 }),
|
||||
colord({ r: 195, g: 176, b: 176 }),
|
||||
colord({ r: 210, g: 189, b: 189 }),
|
||||
colord({ r: 225, g: 203, b: 203 }),
|
||||
colord({ r: 240, g: 216, b: 216 }),
|
||||
colord({ r: 255, g: 230, b: 230 }),
|
||||
colord({ r: 195, g: 0, b: 0 }),
|
||||
colord({ r: 195, g: 20, b: 20 }),
|
||||
colord({ r: 195, g: 39, b: 39 }),
|
||||
colord({ r: 180, g: 54, b: 54 }),
|
||||
colord({ r: 165, g: 66, b: 66 }),
|
||||
colord({ r: 150, g: 75, b: 75 }),
|
||||
colord({ r: 135, g: 81, b: 81 }),
|
||||
colord({ r: 120, g: 84, b: 84 }),
|
||||
colord({ r: 135, g: 108, b: 108 }),
|
||||
colord({ r: 150, g: 120, b: 120 }),
|
||||
colord({ r: 165, g: 132, b: 132 }),
|
||||
colord({ r: 180, g: 144, b: 144 }),
|
||||
];
|
||||
|
||||
export const blueShades = [
|
||||
colord({ r: 0, g: 0, b: 120 }), // Most distinct colors sorted by brightness
|
||||
colord({ r: 0, g: 0, b: 135 }),
|
||||
colord({ r: 0, g: 0, b: 150 }),
|
||||
colord({ r: 0, g: 0, b: 165 }),
|
||||
colord({ r: 0, g: 0, b: 180 }),
|
||||
colord({ r: 0, g: 0, b: 195 }),
|
||||
colord({ r: 0, g: 0, b: 210 }),
|
||||
colord({ r: 0, g: 0, b: 225 }),
|
||||
colord({ r: 0, g: 0, b: 240 }),
|
||||
colord({ r: 0, g: 0, b: 255 }),
|
||||
colord({ r: 12, g: 12, b: 120 }),
|
||||
colord({ r: 14, g: 14, b: 135 }),
|
||||
colord({ r: 15, g: 15, b: 150 }),
|
||||
colord({ r: 17, g: 17, b: 165 }),
|
||||
colord({ r: 18, g: 18, b: 180 }),
|
||||
colord({ r: 20, g: 20, b: 195 }),
|
||||
colord({ r: 21, g: 21, b: 210 }),
|
||||
colord({ r: 23, g: 23, b: 225 }),
|
||||
colord({ r: 25, g: 25, b: 240 }),
|
||||
colord({ r: 26, g: 26, b: 255 }),
|
||||
colord({ r: 24, g: 24, b: 120 }),
|
||||
colord({ r: 27, g: 27, b: 135 }),
|
||||
colord({ r: 30, g: 30, b: 150 }),
|
||||
colord({ r: 33, g: 33, b: 165 }),
|
||||
colord({ r: 36, g: 36, b: 180 }),
|
||||
colord({ r: 39, g: 39, b: 195 }),
|
||||
colord({ r: 42, g: 42, b: 210 }),
|
||||
colord({ r: 45, g: 45, b: 225 }),
|
||||
colord({ r: 48, g: 48, b: 240 }),
|
||||
colord({ r: 51, g: 51, b: 255 }),
|
||||
colord({ r: 36, g: 36, b: 120 }),
|
||||
colord({ r: 41, g: 41, b: 135 }),
|
||||
colord({ r: 45, g: 45, b: 150 }),
|
||||
colord({ r: 50, g: 50, b: 165 }),
|
||||
colord({ r: 54, g: 54, b: 180 }),
|
||||
colord({ r: 59, g: 59, b: 195 }),
|
||||
colord({ r: 63, g: 63, b: 210 }),
|
||||
colord({ r: 68, g: 68, b: 225 }),
|
||||
colord({ r: 72, g: 72, b: 240 }),
|
||||
colord({ r: 77, g: 77, b: 255 }),
|
||||
colord({ r: 48, g: 48, b: 120 }),
|
||||
colord({ r: 54, g: 54, b: 135 }),
|
||||
colord({ r: 60, g: 60, b: 150 }),
|
||||
colord({ r: 66, g: 66, b: 165 }),
|
||||
colord({ r: 72, g: 72, b: 180 }),
|
||||
colord({ r: 78, g: 78, b: 195 }),
|
||||
colord({ r: 84, g: 84, b: 210 }),
|
||||
colord({ r: 90, g: 90, b: 225 }),
|
||||
colord({ r: 96, g: 96, b: 240 }),
|
||||
colord({ r: 102, g: 102, b: 255 }),
|
||||
colord({ r: 60, g: 60, b: 120 }),
|
||||
colord({ r: 68, g: 68, b: 135 }),
|
||||
colord({ r: 75, g: 75, b: 150 }),
|
||||
colord({ r: 83, g: 83, b: 165 }),
|
||||
colord({ r: 90, g: 90, b: 180 }),
|
||||
colord({ r: 98, g: 98, b: 195 }),
|
||||
colord({ r: 105, g: 105, b: 210 }),
|
||||
colord({ r: 113, g: 113, b: 225 }),
|
||||
colord({ r: 120, g: 120, b: 240 }),
|
||||
colord({ r: 128, g: 128, b: 255 }),
|
||||
colord({ r: 72, g: 72, b: 120 }),
|
||||
colord({ r: 81, g: 81, b: 135 }),
|
||||
colord({ r: 90, g: 90, b: 150 }),
|
||||
colord({ r: 99, g: 99, b: 165 }),
|
||||
colord({ r: 108, g: 108, b: 180 }),
|
||||
colord({ r: 117, g: 117, b: 195 }),
|
||||
colord({ r: 126, g: 126, b: 210 }),
|
||||
colord({ r: 135, g: 135, b: 225 }),
|
||||
colord({ r: 144, g: 144, b: 240 }),
|
||||
colord({ r: 153, g: 153, b: 255 }),
|
||||
];
|
||||
@@ -113,15 +113,15 @@ export interface Config {
|
||||
}
|
||||
|
||||
export interface Theme {
|
||||
territoryColor(playerInfo: PlayerInfo): Colord;
|
||||
specialBuildingColor(playerInfo: PlayerInfo): Colord;
|
||||
borderColor(playerInfo: PlayerInfo): Colord;
|
||||
defendedBorderColor(playerInfo: PlayerInfo): Colord;
|
||||
territoryColor(playerInfo: PlayerView): Colord;
|
||||
specialBuildingColor(playerInfo: PlayerView): Colord;
|
||||
borderColor(playerInfo: PlayerView): Colord;
|
||||
defendedBorderColor(playerInfo: PlayerView): Colord;
|
||||
terrainColor(gm: GameMap, tile: TileRef): Colord;
|
||||
backgroundColor(): Colord;
|
||||
falloutColor(): Colord;
|
||||
font(): string;
|
||||
textColor(playerInfo: PlayerInfo): string;
|
||||
textColor(playerInfo: PlayerView): string;
|
||||
// unit color for alternate view
|
||||
selfColor(): Colord;
|
||||
allyColor(): Colord;
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { Colord, colord, random } from "colord";
|
||||
import {
|
||||
Game,
|
||||
PlayerID,
|
||||
PlayerInfo,
|
||||
PlayerType,
|
||||
TerrainType,
|
||||
} from "../game/Game";
|
||||
import { PlayerType, TeamName, TerrainType } from "../game/Game";
|
||||
import { Theme } from "./Config";
|
||||
import { time } from "console";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { simpleHash } from "../Util";
|
||||
import { GameMap, TileRef } from "../game/GameMap";
|
||||
import { PlayerView } from "../game/GameView";
|
||||
import { blueShades, humanColors, redShades, territoryColors } from "./Colors";
|
||||
|
||||
export const pastelTheme = new (class implements Theme {
|
||||
private rand = new PseudoRandom(123);
|
||||
@@ -29,235 +23,31 @@ export const pastelTheme = new (class implements Theme {
|
||||
private water = colord({ r: 70, g: 132, b: 180 });
|
||||
private shorelineWater = colord({ r: 100, g: 143, b: 255 });
|
||||
|
||||
private territoryColors: Colord[] = [
|
||||
colord({ r: 230, g: 100, b: 100 }), // Bright Red
|
||||
colord({ r: 100, g: 180, b: 230 }), // Sky Blue
|
||||
colord({ r: 230, g: 180, b: 80 }), // Golden Yellow
|
||||
colord({ r: 180, g: 100, b: 230 }), // Purple
|
||||
colord({ r: 80, g: 200, b: 120 }), // Emerald Green
|
||||
colord({ r: 230, g: 130, b: 180 }), // Pink
|
||||
colord({ r: 100, g: 160, b: 80 }), // Olive Green
|
||||
colord({ r: 230, g: 150, b: 100 }), // Peach
|
||||
colord({ r: 80, g: 130, b: 190 }), // Navy Blue
|
||||
colord({ r: 210, g: 210, b: 100 }), // Lime Yellow
|
||||
colord({ r: 190, g: 100, b: 130 }), // Maroon
|
||||
colord({ r: 100, g: 210, b: 210 }), // Turquoise
|
||||
colord({ r: 210, g: 140, b: 80 }), // Light Orange
|
||||
colord({ r: 150, g: 110, b: 190 }), // Lavender
|
||||
colord({ r: 180, g: 210, b: 120 }), // Light Green
|
||||
colord({ r: 210, g: 100, b: 160 }), // Hot Pink
|
||||
colord({ r: 100, g: 140, b: 110 }), // Sea Green
|
||||
colord({ r: 230, g: 180, b: 180 }), // Light Pink
|
||||
colord({ r: 120, g: 120, b: 190 }), // Periwinkle
|
||||
colord({ r: 190, g: 170, b: 100 }), // Sand
|
||||
colord({ r: 100, g: 180, b: 160 }), // Aquamarine
|
||||
colord({ r: 210, g: 160, b: 200 }), // Orchid
|
||||
colord({ r: 170, g: 190, b: 100 }), // Yellow Green
|
||||
colord({ r: 100, g: 130, b: 150 }), // Steel Blue
|
||||
colord({ r: 230, g: 140, b: 140 }), // Salmon
|
||||
colord({ r: 140, g: 180, b: 220 }), // Light Blue
|
||||
colord({ r: 200, g: 160, b: 110 }), // Tan
|
||||
colord({ r: 180, g: 130, b: 180 }), // Plum
|
||||
colord({ r: 130, g: 200, b: 130 }), // Light Sea Green
|
||||
colord({ r: 220, g: 120, b: 120 }), // Coral
|
||||
colord({ r: 120, g: 160, b: 200 }), // Cornflower Blue
|
||||
colord({ r: 200, g: 200, b: 140 }), // Khaki
|
||||
colord({ r: 160, g: 120, b: 160 }), // Purple Gray
|
||||
colord({ r: 140, g: 180, b: 140 }), // Dark Sea Green
|
||||
colord({ r: 200, g: 130, b: 110 }), // Dark Salmon
|
||||
colord({ r: 130, g: 170, b: 190 }), // Cadet Blue
|
||||
colord({ r: 190, g: 180, b: 160 }), // Tan Gray
|
||||
colord({ r: 170, g: 140, b: 190 }), // Medium Purple
|
||||
colord({ r: 160, g: 190, b: 160 }), // Pale Green
|
||||
colord({ r: 190, g: 150, b: 130 }), // Rosy Brown
|
||||
colord({ r: 140, g: 150, b: 180 }), // Light Slate Gray
|
||||
colord({ r: 180, g: 170, b: 140 }), // Dark Khaki
|
||||
colord({ r: 150, g: 130, b: 150 }), // Thistle
|
||||
colord({ r: 170, g: 190, b: 180 }), // Pale Blue Green
|
||||
colord({ r: 190, g: 140, b: 150 }), // Puce
|
||||
colord({ r: 130, g: 180, b: 170 }), // Medium Aquamarine
|
||||
colord({ r: 180, g: 160, b: 180 }), // Mauve
|
||||
colord({ r: 160, g: 180, b: 140 }), // Dark Olive Green
|
||||
colord({ r: 170, g: 150, b: 170 }), // Dusty Rose
|
||||
colord({ r: 100, g: 180, b: 230 }), // Sky Blue
|
||||
colord({ r: 230, g: 180, b: 80 }), // Golden Yellow
|
||||
colord({ r: 180, g: 100, b: 230 }), // Purple
|
||||
colord({ r: 80, g: 200, b: 120 }), // Emerald Green
|
||||
colord({ r: 230, g: 130, b: 180 }), // Pink
|
||||
colord({ r: 100, g: 160, b: 80 }), // Olive Green
|
||||
colord({ r: 230, g: 150, b: 100 }), // Peach
|
||||
colord({ r: 80, g: 130, b: 190 }), // Navy Blue
|
||||
colord({ r: 210, g: 210, b: 100 }), // Lime Yellow
|
||||
colord({ r: 190, g: 100, b: 130 }), // Maroon
|
||||
colord({ r: 100, g: 210, b: 210 }), // Turquoise
|
||||
colord({ r: 210, g: 140, b: 80 }), // Light Orange
|
||||
colord({ r: 150, g: 110, b: 190 }), // Lavender
|
||||
colord({ r: 180, g: 210, b: 120 }), // Light Green
|
||||
colord({ r: 210, g: 100, b: 160 }), // Hot Pink
|
||||
colord({ r: 100, g: 140, b: 110 }), // Sea Green
|
||||
colord({ r: 230, g: 180, b: 180 }), // Light Pink
|
||||
colord({ r: 120, g: 120, b: 190 }), // Periwinkle
|
||||
colord({ r: 190, g: 170, b: 100 }), // Sand
|
||||
colord({ r: 100, g: 180, b: 160 }), // Aquamarine
|
||||
colord({ r: 210, g: 160, b: 200 }), // Orchid
|
||||
colord({ r: 170, g: 190, b: 100 }), // Yellow Green
|
||||
colord({ r: 100, g: 130, b: 150 }), // Steel Blue
|
||||
colord({ r: 230, g: 140, b: 140 }), // Salmon
|
||||
colord({ r: 140, g: 180, b: 220 }), // Light Blue
|
||||
colord({ r: 200, g: 160, b: 110 }), // Tan
|
||||
colord({ r: 180, g: 130, b: 180 }), // Plum
|
||||
colord({ r: 130, g: 200, b: 130 }), // Light Sea Green
|
||||
colord({ r: 220, g: 120, b: 120 }), // Coral
|
||||
colord({ r: 120, g: 160, b: 200 }), // Cornflower Blue
|
||||
colord({ r: 200, g: 200, b: 140 }), // Khaki
|
||||
colord({ r: 160, g: 120, b: 160 }), // Purple Gray
|
||||
colord({ r: 140, g: 180, b: 140 }), // Dark Sea Green
|
||||
colord({ r: 200, g: 130, b: 110 }), // Dark Salmon
|
||||
colord({ r: 130, g: 170, b: 190 }), // Cadet Blue
|
||||
colord({ r: 190, g: 180, b: 160 }), // Tan Gray
|
||||
colord({ r: 170, g: 140, b: 190 }), // Medium Purple
|
||||
colord({ r: 160, g: 190, b: 160 }), // Pale Green
|
||||
colord({ r: 190, g: 150, b: 130 }), // Rosy Brown
|
||||
colord({ r: 140, g: 150, b: 180 }), // Light Slate Gray
|
||||
colord({ r: 180, g: 170, b: 140 }), // Dark Khaki
|
||||
colord({ r: 150, g: 130, b: 150 }), // Thistle
|
||||
colord({ r: 170, g: 190, b: 180 }), // Pale Blue Green
|
||||
colord({ r: 190, g: 140, b: 150 }), // Puce
|
||||
colord({ r: 130, g: 180, b: 170 }), // Medium Aquamarine
|
||||
colord({ r: 180, g: 160, b: 180 }), // Mauve
|
||||
colord({ r: 160, g: 180, b: 140 }), // Dark Olive Green
|
||||
colord({ r: 170, g: 150, b: 170 }), // Dusty Rose
|
||||
];
|
||||
|
||||
private humanColors: Colord[] = [
|
||||
// Original set
|
||||
colord({ r: 235, g: 75, b: 75 }), // Bright Red
|
||||
colord({ r: 67, g: 190, b: 84 }), // Fresh Green
|
||||
colord({ r: 59, g: 130, b: 246 }), // Royal Blue
|
||||
colord({ r: 245, g: 158, b: 11 }), // Amber
|
||||
colord({ r: 236, g: 72, b: 153 }), // Deep Pink
|
||||
colord({ r: 48, g: 178, b: 180 }), // Teal
|
||||
colord({ r: 168, g: 85, b: 247 }), // Vibrant Purple
|
||||
colord({ r: 251, g: 191, b: 36 }), // Marigold
|
||||
colord({ r: 74, g: 222, b: 128 }), // Mint
|
||||
colord({ r: 239, g: 68, b: 68 }), // Crimson
|
||||
colord({ r: 34, g: 197, b: 94 }), // Emerald
|
||||
colord({ r: 96, g: 165, b: 250 }), // Sky Blue
|
||||
colord({ r: 249, g: 115, b: 22 }), // Tangerine
|
||||
colord({ r: 192, g: 132, b: 252 }), // Lavender
|
||||
colord({ r: 45, g: 212, b: 191 }), // Turquoise
|
||||
colord({ r: 244, g: 114, b: 182 }), // Rose
|
||||
colord({ r: 132, g: 204, b: 22 }), // Lime
|
||||
colord({ r: 56, g: 189, b: 248 }), // Light Blue
|
||||
colord({ r: 234, g: 179, b: 8 }), // Sunflower
|
||||
colord({ r: 217, g: 70, b: 239 }), // Fuchsia
|
||||
colord({ r: 16, g: 185, b: 129 }), // Sea Green
|
||||
colord({ r: 251, g: 146, b: 60 }), // Light Orange
|
||||
colord({ r: 147, g: 51, b: 234 }), // Bright Purple
|
||||
colord({ r: 79, g: 70, b: 229 }), // Indigo
|
||||
colord({ r: 245, g: 101, b: 101 }), // Coral
|
||||
colord({ r: 134, g: 239, b: 172 }), // Light Green
|
||||
colord({ r: 59, g: 130, b: 246 }), // Cerulean
|
||||
colord({ r: 253, g: 164, b: 175 }), // Salmon Pink
|
||||
colord({ r: 147, g: 197, b: 253 }), // Powder Blue
|
||||
colord({ r: 252, g: 211, b: 77 }), // Golden
|
||||
colord({ r: 190, g: 92, b: 251 }), // Amethyst
|
||||
colord({ r: 82, g: 183, b: 136 }), // Jade
|
||||
colord({ r: 248, g: 113, b: 113 }), // Warm Red
|
||||
colord({ r: 99, g: 202, b: 253 }), // Azure
|
||||
colord({ r: 240, g: 171, b: 252 }), // Orchid
|
||||
colord({ r: 163, g: 230, b: 53 }), // Yellow Green
|
||||
colord({ r: 234, g: 88, b: 12 }), // Burnt Orange
|
||||
colord({ r: 125, g: 211, b: 252 }), // Crystal Blue
|
||||
colord({ r: 251, g: 113, b: 133 }), // Watermelon
|
||||
colord({ r: 52, g: 211, b: 153 }), // Spearmint
|
||||
colord({ r: 167, g: 139, b: 250 }), // Periwinkle
|
||||
colord({ r: 245, g: 158, b: 11 }), // Honey
|
||||
colord({ r: 110, g: 231, b: 183 }), // Seafoam
|
||||
colord({ r: 233, g: 213, b: 255 }), // Light Lilac
|
||||
colord({ r: 202, g: 138, b: 4 }), // Rich Gold
|
||||
colord({ r: 151, g: 255, b: 187 }), // Fresh Mint
|
||||
colord({ r: 220, g: 38, b: 38 }), // Ruby
|
||||
colord({ r: 124, g: 58, b: 237 }), // Royal Purple
|
||||
colord({ r: 45, g: 212, b: 191 }), // Ocean
|
||||
colord({ r: 252, g: 165, b: 165 }), // Peach
|
||||
|
||||
// Additional 50 colors
|
||||
colord({ r: 179, g: 136, b: 255 }), // Light Purple
|
||||
colord({ r: 133, g: 77, b: 14 }), // Chocolate
|
||||
colord({ r: 52, g: 211, b: 153 }), // Aquamarine
|
||||
colord({ r: 234, g: 179, b: 8 }), // Mustard
|
||||
colord({ r: 236, g: 72, b: 153 }), // Hot Pink
|
||||
colord({ r: 147, g: 197, b: 253 }), // Sky
|
||||
colord({ r: 249, g: 115, b: 22 }), // Pumpkin
|
||||
colord({ r: 167, g: 139, b: 250 }), // Iris
|
||||
colord({ r: 16, g: 185, b: 129 }), // Pine
|
||||
colord({ r: 251, g: 146, b: 60 }), // Mango
|
||||
colord({ r: 192, g: 132, b: 252 }), // Wisteria
|
||||
colord({ r: 79, g: 70, b: 229 }), // Sapphire
|
||||
colord({ r: 245, g: 101, b: 101 }), // Salmon
|
||||
colord({ r: 134, g: 239, b: 172 }), // Spring Green
|
||||
colord({ r: 59, g: 130, b: 246 }), // Ocean Blue
|
||||
colord({ r: 253, g: 164, b: 175 }), // Rose Gold
|
||||
colord({ r: 16, g: 185, b: 129 }), // Forest
|
||||
colord({ r: 252, g: 211, b: 77 }), // Sunshine
|
||||
colord({ r: 190, g: 92, b: 251 }), // Grape
|
||||
colord({ r: 82, g: 183, b: 136 }), // Eucalyptus
|
||||
colord({ r: 248, g: 113, b: 113 }), // Cherry
|
||||
colord({ r: 99, g: 202, b: 253 }), // Arctic
|
||||
colord({ r: 240, g: 171, b: 252 }), // Lilac
|
||||
colord({ r: 163, g: 230, b: 53 }), // Chartreuse
|
||||
colord({ r: 234, g: 88, b: 12 }), // Rust
|
||||
colord({ r: 125, g: 211, b: 252 }), // Ice Blue
|
||||
colord({ r: 251, g: 113, b: 133 }), // Strawberry
|
||||
colord({ r: 52, g: 211, b: 153 }), // Sage
|
||||
colord({ r: 167, g: 139, b: 250 }), // Violet
|
||||
colord({ r: 245, g: 158, b: 11 }), // Apricot
|
||||
colord({ r: 110, g: 231, b: 183 }), // Mint Green
|
||||
colord({ r: 233, g: 213, b: 255 }), // Thistle
|
||||
colord({ r: 202, g: 138, b: 4 }), // Bronze
|
||||
colord({ r: 151, g: 255, b: 187 }), // Pistachio
|
||||
colord({ r: 220, g: 38, b: 38 }), // Fire Engine
|
||||
colord({ r: 124, g: 58, b: 237 }), // Electric Purple
|
||||
colord({ r: 45, g: 212, b: 191 }), // Caribbean
|
||||
colord({ r: 252, g: 165, b: 165 }), // Melon
|
||||
colord({ r: 168, g: 85, b: 247 }), // Byzantium
|
||||
colord({ r: 74, g: 222, b: 128 }), // Kelly Green
|
||||
colord({ r: 239, g: 68, b: 68 }), // Cardinal
|
||||
colord({ r: 34, g: 197, b: 94 }), // Shamrock
|
||||
colord({ r: 96, g: 165, b: 250 }), // Marina
|
||||
colord({ r: 249, g: 115, b: 22 }), // Carrot
|
||||
colord({ r: 192, g: 132, b: 252 }), // Heliotrope
|
||||
colord({ r: 45, g: 212, b: 191 }), // Lagoon
|
||||
colord({ r: 244, g: 114, b: 182 }), // Bubble Gum
|
||||
colord({ r: 132, g: 204, b: 22 }), // Apple
|
||||
colord({ r: 56, g: 189, b: 248 }), // Electric Blue
|
||||
colord({ r: 234, g: 179, b: 8 }), // Daffodil
|
||||
];
|
||||
|
||||
private _selfColor = colord({ r: 0, g: 255, b: 0 });
|
||||
private _allyColor = colord({ r: 255, g: 255, b: 0 });
|
||||
private _enemyColor = colord({ r: 255, g: 0, b: 0 });
|
||||
|
||||
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
|
||||
|
||||
territoryColor(playerInfo: PlayerInfo): Colord {
|
||||
if (playerInfo.playerType == PlayerType.Human) {
|
||||
return this.humanColors[
|
||||
simpleHash(playerInfo.name) % this.humanColors.length
|
||||
];
|
||||
territoryColor(player: PlayerView): Colord {
|
||||
if (player.teamName() == TeamName.Red) {
|
||||
return redShades[simpleHash(player.id()) % redShades.length];
|
||||
}
|
||||
return this.territoryColors[
|
||||
simpleHash(playerInfo.name) % this.territoryColors.length
|
||||
];
|
||||
if (player.teamName() == TeamName.Blue) {
|
||||
return blueShades[simpleHash(player.id()) % blueShades.length];
|
||||
}
|
||||
if (player.info().playerType == PlayerType.Human) {
|
||||
return humanColors[simpleHash(player.id()) % humanColors.length];
|
||||
}
|
||||
return territoryColors[simpleHash(player.id()) % territoryColors.length];
|
||||
}
|
||||
|
||||
textColor(playerInfo: PlayerInfo): string {
|
||||
return playerInfo.playerType == PlayerType.Human ? "#000000" : "#4D4D4D";
|
||||
textColor(player: PlayerView): string {
|
||||
return player.info().playerType == PlayerType.Human ? "#000000" : "#4D4D4D";
|
||||
}
|
||||
|
||||
specialBuildingColor(playerInfo: PlayerInfo): Colord {
|
||||
const tc = this.territoryColor(playerInfo).rgba;
|
||||
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),
|
||||
@@ -265,16 +55,16 @@ export const pastelTheme = new (class implements Theme {
|
||||
});
|
||||
}
|
||||
|
||||
borderColor(playerInfo: PlayerInfo): Colord {
|
||||
const tc = this.territoryColor(playerInfo).rgba;
|
||||
borderColor(player: PlayerView): Colord {
|
||||
const tc = this.territoryColor(player).rgba;
|
||||
return colord({
|
||||
r: Math.max(tc.r - 40, 0),
|
||||
g: Math.max(tc.g - 40, 0),
|
||||
b: Math.max(tc.b - 40, 0),
|
||||
});
|
||||
}
|
||||
defendedBorderColor(playerInfo: PlayerInfo): Colord {
|
||||
const bc = this.borderColor(playerInfo).rgba;
|
||||
defendedBorderColor(player: PlayerView): Colord {
|
||||
const bc = this.borderColor(player).rgba;
|
||||
return colord({
|
||||
r: Math.max(bc.r - 40, 0),
|
||||
g: Math.max(bc.g - 40, 0),
|
||||
|
||||
@@ -241,23 +241,23 @@ export const pastelThemeDark = new (class implements Theme {
|
||||
|
||||
private _spawnHighlightColor = colord({ r: 255, g: 213, b: 79 });
|
||||
|
||||
territoryColor(playerInfo: PlayerInfo): Colord {
|
||||
if (playerInfo.playerType == PlayerType.Human) {
|
||||
territoryColor(player: PlayerView): Colord {
|
||||
if (player.info().playerType == PlayerType.Human) {
|
||||
return this.humanColors[
|
||||
simpleHash(playerInfo.name) % this.humanColors.length
|
||||
simpleHash(player.info().name) % this.humanColors.length
|
||||
];
|
||||
}
|
||||
return this.territoryColors[
|
||||
simpleHash(playerInfo.name) % this.territoryColors.length
|
||||
simpleHash(player.info().name) % this.territoryColors.length
|
||||
];
|
||||
}
|
||||
|
||||
textColor(playerInfo: PlayerInfo): string {
|
||||
return playerInfo.playerType == PlayerType.Human ? "#ffffff" : "#e6e6e6";
|
||||
textColor(player: PlayerView): string {
|
||||
return player.info().playerType == PlayerType.Human ? "#ffffff" : "#e6e6e6";
|
||||
}
|
||||
|
||||
specialBuildingColor(playerInfo: PlayerInfo): Colord {
|
||||
const tc = this.territoryColor(playerInfo).rgba;
|
||||
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),
|
||||
@@ -265,16 +265,16 @@ export const pastelThemeDark = new (class implements Theme {
|
||||
});
|
||||
}
|
||||
|
||||
borderColor(playerInfo: PlayerInfo): Colord {
|
||||
const tc = this.territoryColor(playerInfo).rgba;
|
||||
borderColor(player: PlayerView): Colord {
|
||||
const tc = this.territoryColor(player).rgba;
|
||||
return colord({
|
||||
r: Math.max(tc.r - 40, 0),
|
||||
g: Math.max(tc.g - 40, 0),
|
||||
b: Math.max(tc.b - 40, 0),
|
||||
});
|
||||
}
|
||||
defendedBorderColor(playerInfo: PlayerInfo): Colord {
|
||||
const bc = this.borderColor(playerInfo).rgba;
|
||||
defendedBorderColor(player: PlayerView): Colord {
|
||||
const bc = this.borderColor(player).rgba;
|
||||
return colord({
|
||||
r: Math.max(bc.r - 40, 0),
|
||||
g: Math.max(bc.g - 40, 0),
|
||||
|
||||
@@ -54,7 +54,7 @@ export class BotExecution implements Execution {
|
||||
.filter((n) => n.isPlayer() && n.isTraitor()) as Player[];
|
||||
if (traitors.length > 0) {
|
||||
const toAttack = this.random.randElement(traitors);
|
||||
const odds = this.bot.isAlliedWith(toAttack) ? 6 : 3;
|
||||
const odds = this.bot.isFriendly(toAttack) ? 6 : 3;
|
||||
if (this.random.chance(odds)) {
|
||||
this.sendAttack(toAttack);
|
||||
return;
|
||||
@@ -85,7 +85,7 @@ export class BotExecution implements Execution {
|
||||
const owner = this.mg.owner(toAttack);
|
||||
|
||||
if (owner.isPlayer()) {
|
||||
if (this.bot.isAlliedWith(owner)) {
|
||||
if (this.bot.isFriendly(owner)) {
|
||||
return;
|
||||
}
|
||||
if (owner.type() == PlayerType.FakeHuman) {
|
||||
|
||||
@@ -192,7 +192,7 @@ export class FakeHumanExecution implements Execution {
|
||||
}
|
||||
|
||||
private shouldAttack(other: Player): boolean {
|
||||
if (this.player.isAlliedWith(other)) {
|
||||
if (this.player.isFriendly(other)) {
|
||||
if (this.shouldDiscourageAttack(other)) {
|
||||
return this.random.chance(200);
|
||||
}
|
||||
@@ -271,12 +271,11 @@ export class FakeHumanExecution implements Execution {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.player.isAlliedWith(this.enemy)) {
|
||||
this.enemy = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.enemy) {
|
||||
if (this.player.isFriendly(this.enemy)) {
|
||||
this.enemy = null;
|
||||
return;
|
||||
}
|
||||
this.maybeSendNuke(this.enemy);
|
||||
if (this.player.sharesBorderWith(this.enemy)) {
|
||||
this.sendAttack(this.enemy);
|
||||
@@ -537,7 +536,7 @@ export class FakeHumanExecution implements Execution {
|
||||
}
|
||||
if (
|
||||
this.mg.owner(dst).isPlayer() &&
|
||||
this.player.isAlliedWith(this.mg.owner(dst) as Player)
|
||||
this.player.isFriendly(this.mg.owner(dst) as Player)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ export class PlayerExecution implements Execution {
|
||||
const main = clusters.shift();
|
||||
this.player.largestClusterBoundingBox = calculateBoundingBox(this.mg, main);
|
||||
const surroundedBy = this.surroundedBySamePlayer(main);
|
||||
if (surroundedBy && !this.player.isAlliedWith(surroundedBy)) {
|
||||
if (surroundedBy && !this.player.isFriendly(surroundedBy)) {
|
||||
this.removeCluster(main);
|
||||
}
|
||||
|
||||
|
||||
@@ -72,8 +72,7 @@ export class SAMLauncherExecution implements Execution {
|
||||
])
|
||||
.filter(
|
||||
({ unit }) =>
|
||||
unit.owner() !== this.player &&
|
||||
!unit.owner().isAlliedWith(this.player),
|
||||
unit.owner() !== this.player && !this.player.isFriendly(unit.owner()),
|
||||
);
|
||||
|
||||
this.target =
|
||||
|
||||
@@ -152,7 +152,7 @@ export class TransportShipExecution implements Execution {
|
||||
this.active = false;
|
||||
return;
|
||||
}
|
||||
if (this.target.isPlayer() && this.attacker.isAlliedWith(this.target)) {
|
||||
if (this.target.isPlayer() && this.attacker.isFriendly(this.target)) {
|
||||
this.target.addTroops(this.troops);
|
||||
} else {
|
||||
this.attacker.conquer(this.dst);
|
||||
|
||||
@@ -146,7 +146,7 @@ export class WarshipExecution implements Execution {
|
||||
({ unit }) =>
|
||||
unit.owner() !== this.warship.owner() &&
|
||||
unit !== this.warship &&
|
||||
!unit.owner().isAlliedWith(this.warship.owner()) &&
|
||||
!unit.owner().isFriendly(this.warship.owner()) &&
|
||||
!this.alreadySentShell.has(unit) &&
|
||||
(unit.type() !== UnitType.TradeShip || hasPort) &&
|
||||
(unit.type() !== UnitType.TradeShip ||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EventBus, GameEvent } from "../EventBus";
|
||||
import { Execution, Game, Player, PlayerID } from "../game/Game";
|
||||
import { Execution, Game, GameMode, Player, Team } from "../game/Game";
|
||||
|
||||
export class WinEvent implements GameEvent {
|
||||
constructor(public readonly winner: Player) {}
|
||||
@@ -20,6 +20,14 @@ export class WinCheckExecution implements Execution {
|
||||
if (ticks % 10 != 0) {
|
||||
return;
|
||||
}
|
||||
if (this.mg.config().gameConfig().gameMode == GameMode.FFA) {
|
||||
this.checkWinnerFFA();
|
||||
} else {
|
||||
this.checkWinnerTeam();
|
||||
}
|
||||
}
|
||||
|
||||
checkWinnerFFA(): void {
|
||||
const sorted = this.mg
|
||||
.players()
|
||||
.sort((a, b) => b.numTilesOwned() - a.numTilesOwned());
|
||||
@@ -39,6 +47,30 @@ export class WinCheckExecution implements Execution {
|
||||
}
|
||||
}
|
||||
|
||||
checkWinnerTeam(): void {
|
||||
const teamToTiles = new Map<Team, number>();
|
||||
for (const player of this.mg.players()) {
|
||||
teamToTiles.set(
|
||||
player.team(),
|
||||
(teamToTiles.get(player.team()) ?? 0) + player.numTilesOwned(),
|
||||
);
|
||||
}
|
||||
const sorted = Array.from(teamToTiles.entries()).sort(
|
||||
(a, b) => b[1] - a[1],
|
||||
);
|
||||
if (sorted.length == 0) {
|
||||
return;
|
||||
}
|
||||
const max = sorted[0];
|
||||
const numTilesWithoutFallout =
|
||||
this.mg.numLandTiles() - this.mg.numTilesWithFallout();
|
||||
const percentage = (max[1] / numTilesWithoutFallout) * 100;
|
||||
if (percentage > this.mg.config().percentageTilesOwnedToWin()) {
|
||||
this.mg.setWinner(max[0].name, this.mg.stats().stats());
|
||||
console.log(`${max[0].name} has won the game`);
|
||||
this.active = false;
|
||||
}
|
||||
}
|
||||
owner(): Player {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export class AllianceRequestExecution implements Execution {
|
||||
}
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.requestor.isAlliedWith(this.recipient)) {
|
||||
if (this.requestor.isFriendly(this.recipient)) {
|
||||
consolex.warn("already allied");
|
||||
} else if (!this.requestor.canSendAllianceRequest(this.recipient)) {
|
||||
consolex.warn("recent or pending alliance request");
|
||||
|
||||
@@ -40,7 +40,7 @@ export class AllianceRequestReplyExecution implements Execution {
|
||||
}
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.requestor.isAlliedWith(this.recipient)) {
|
||||
if (this.requestor.isFriendly(this.recipient)) {
|
||||
consolex.warn("already allied");
|
||||
} else {
|
||||
const request = this.requestor
|
||||
|
||||
+22
-3
@@ -38,6 +38,11 @@ export enum Difficulty {
|
||||
Impossible = "Impossible",
|
||||
}
|
||||
|
||||
export enum TeamName {
|
||||
Red = "Red",
|
||||
Blue = "Blue",
|
||||
}
|
||||
|
||||
export enum GameMapType {
|
||||
World = "World",
|
||||
Europe = "Europe",
|
||||
@@ -62,6 +67,15 @@ export enum GameType {
|
||||
Private = "Private",
|
||||
}
|
||||
|
||||
export enum GameMode {
|
||||
FFA = "Free For All",
|
||||
Team = "Team",
|
||||
}
|
||||
|
||||
export interface Team {
|
||||
name: TeamName;
|
||||
}
|
||||
|
||||
export interface UnitInfo {
|
||||
cost: (player: Player | PlayerView) => Gold;
|
||||
// Determines if its owner changes when its tile is conquered.
|
||||
@@ -95,6 +109,7 @@ export const nukeTypes = [
|
||||
UnitType.MIRVWarhead,
|
||||
UnitType.MIRV,
|
||||
] as UnitType[];
|
||||
|
||||
export type NukeType = (typeof nukeTypes)[number];
|
||||
|
||||
export enum Relation {
|
||||
@@ -327,8 +342,10 @@ export interface Player {
|
||||
allRelationsSorted(): { player: Player; relation: Relation }[];
|
||||
updateRelation(other: Player, delta: number): void;
|
||||
decayRelations(): void;
|
||||
|
||||
// Alliances
|
||||
isOnSameTeam(other: Player): boolean;
|
||||
// Either allied or on same team.
|
||||
isFriendly(other: Player): boolean;
|
||||
team(): Team | null;
|
||||
incomingAllianceRequests(): AllianceRequest[];
|
||||
outgoingAllianceRequests(): AllianceRequest[];
|
||||
alliances(): MutableAlliance[];
|
||||
@@ -400,11 +417,13 @@ export interface Game extends GameMap {
|
||||
terraNullius(): TerraNullius;
|
||||
owner(ref: TileRef): Player | TerraNullius;
|
||||
|
||||
teams(): Team[];
|
||||
|
||||
// Game State
|
||||
ticks(): Tick;
|
||||
inSpawnPhase(): boolean;
|
||||
executeNextTick(): GameUpdates;
|
||||
setWinner(winner: Player, allPlayersStats: AllPlayersStats): void;
|
||||
setWinner(winner: Player | TeamName, allPlayersStats: AllPlayersStats): void;
|
||||
config(): Config;
|
||||
|
||||
// Units
|
||||
|
||||
@@ -16,6 +16,9 @@ import {
|
||||
GameUpdates,
|
||||
TerrainType,
|
||||
EmojiMessage,
|
||||
Team,
|
||||
GameMode,
|
||||
TeamName,
|
||||
} from "./Game";
|
||||
import { GameUpdate } from "./GameUpdates";
|
||||
import { GameUpdateType } from "./GameUpdates";
|
||||
@@ -32,6 +35,7 @@ import { GameMap, GameMapImpl, TileRef, TileUpdate } from "./GameMap";
|
||||
import { UnitGrid } from "./UnitGrid";
|
||||
import { StatsImpl } from "./StatsImpl";
|
||||
import { Stats } from "./Stats";
|
||||
import { simpleHash } from "../Util";
|
||||
|
||||
export function createGame(
|
||||
gameMap: GameMap,
|
||||
@@ -70,6 +74,8 @@ export class GameImpl implements Game {
|
||||
|
||||
private _stats: StatsImpl = new StatsImpl();
|
||||
|
||||
private teams_: Team[] = [];
|
||||
|
||||
constructor(
|
||||
private _map: GameMap,
|
||||
private miniGameMap: GameMap,
|
||||
@@ -89,6 +95,9 @@ export class GameImpl implements Game {
|
||||
),
|
||||
);
|
||||
this.unitGrid = new UnitGrid(this._map);
|
||||
if (this._config.gameConfig().gameMode == GameMode.Team) {
|
||||
this.teams_ = [{ name: TeamName.Red }, { name: TeamName.Blue }];
|
||||
}
|
||||
}
|
||||
isOnEdgeOfMap(ref: TileRef): boolean {
|
||||
return this._map.isOnEdgeOfMap(ref);
|
||||
@@ -322,6 +331,7 @@ export class GameImpl implements Game {
|
||||
this.nextPlayerID,
|
||||
playerInfo,
|
||||
manpower,
|
||||
this.maybeAssignTeam(playerInfo),
|
||||
);
|
||||
this._playersBySmallID.push(player);
|
||||
this.nextPlayerID++;
|
||||
@@ -329,6 +339,14 @@ export class GameImpl implements Game {
|
||||
return player;
|
||||
}
|
||||
|
||||
private maybeAssignTeam(player: PlayerInfo): Team | null {
|
||||
if (this._config.gameConfig().gameMode != GameMode.Team) {
|
||||
return null;
|
||||
}
|
||||
const rand = simpleHash(player.id);
|
||||
return this.teams_[rand % this.teams_.length];
|
||||
}
|
||||
|
||||
player(id: PlayerID | null): Player {
|
||||
if (!this._players.has(id)) {
|
||||
throw new Error(`Player with id ${id} not found`);
|
||||
@@ -513,14 +531,19 @@ export class GameImpl implements Game {
|
||||
});
|
||||
}
|
||||
|
||||
setWinner(winner: Player, allPlayersStats: AllPlayersStats): void {
|
||||
setWinner(winner: Player | TeamName, allPlayersStats: AllPlayersStats): void {
|
||||
this.addUpdate({
|
||||
type: GameUpdateType.Win,
|
||||
winnerID: winner.smallID(),
|
||||
winner: typeof winner === "string" ? winner : winner.smallID(),
|
||||
winnerType: typeof winner === "string" ? "team" : "player",
|
||||
allPlayersStats,
|
||||
});
|
||||
}
|
||||
|
||||
teams(): Team[] {
|
||||
return this.teams_;
|
||||
}
|
||||
|
||||
displayMessage(
|
||||
message: string,
|
||||
type: MessageType,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
NameViewData,
|
||||
PlayerID,
|
||||
PlayerType,
|
||||
TeamName,
|
||||
Tick,
|
||||
UnitType,
|
||||
} from "./Game";
|
||||
@@ -93,6 +94,7 @@ export interface PlayerUpdate {
|
||||
name: string;
|
||||
displayName: string;
|
||||
id: PlayerID;
|
||||
teamName?: TeamName;
|
||||
smallID: number;
|
||||
playerType: PlayerType;
|
||||
isAlive: boolean;
|
||||
@@ -159,7 +161,9 @@ export interface DisplayMessageUpdate {
|
||||
export interface WinUpdate {
|
||||
type: GameUpdateType.Win;
|
||||
allPlayersStats: AllPlayersStats;
|
||||
winnerID: number;
|
||||
// Player id or team name.
|
||||
winner: number | TeamName;
|
||||
winnerType: "player" | "team";
|
||||
}
|
||||
|
||||
export interface HashUpdate {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
Player,
|
||||
PlayerActions,
|
||||
PlayerProfile,
|
||||
TeamName,
|
||||
} from "./Game";
|
||||
import { AttackUpdate, PlayerUpdate } from "./GameUpdates";
|
||||
import { UnitUpdate } from "./GameUpdates";
|
||||
@@ -168,6 +169,9 @@ export class PlayerView {
|
||||
id(): PlayerID {
|
||||
return this.data.id;
|
||||
}
|
||||
teamName(): TeamName {
|
||||
return this.data.teamName;
|
||||
}
|
||||
type(): PlayerType {
|
||||
return this.data.playerType;
|
||||
}
|
||||
@@ -210,6 +214,16 @@ export class PlayerView {
|
||||
return this.data.allies.some((n) => other.smallID() == n);
|
||||
}
|
||||
|
||||
isOnSameTeam(other: PlayerView): boolean {
|
||||
return (
|
||||
this.data.teamName != null && this.data.teamName == other.data.teamName
|
||||
);
|
||||
}
|
||||
|
||||
isFriendly(other: PlayerView): boolean {
|
||||
return this.isAlliedWith(other) || this.isOnSameTeam(other);
|
||||
}
|
||||
|
||||
isRequestingAllianceWith(other: PlayerView) {
|
||||
return this.data.outgoingAllianceRequests.some((id) => other.id() == id);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
PlayerProfile,
|
||||
Attack,
|
||||
UnitSpecificInfos,
|
||||
Team,
|
||||
} from "./Game";
|
||||
import { AttackUpdate, PlayerUpdate } from "./GameUpdates";
|
||||
import { GameUpdateType } from "./GameUpdates";
|
||||
@@ -100,6 +101,7 @@ export class PlayerImpl implements Player {
|
||||
private _smallID: number,
|
||||
private readonly playerInfo: PlayerInfo,
|
||||
startTroops: number,
|
||||
private _team: Team | null,
|
||||
) {
|
||||
this._flag = playerInfo.flag;
|
||||
this._name = sanitizeUsername(playerInfo.name);
|
||||
@@ -125,6 +127,7 @@ export class PlayerImpl implements Player {
|
||||
name: this.name(),
|
||||
displayName: this.displayName(),
|
||||
id: this.id(),
|
||||
teamName: this.team()?.name,
|
||||
smallID: this.smallID(),
|
||||
playerType: this.type(),
|
||||
isAlive: this.isAlive(),
|
||||
@@ -324,7 +327,7 @@ export class PlayerImpl implements Player {
|
||||
if (other == this) {
|
||||
return false;
|
||||
}
|
||||
if (this.isAlliedWith(other)) {
|
||||
if (this.isFriendly(other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -426,7 +429,7 @@ export class PlayerImpl implements Player {
|
||||
if (this == other) {
|
||||
return false;
|
||||
}
|
||||
if (this.isAlliedWith(other)) {
|
||||
if (this.isFriendly(other)) {
|
||||
return false;
|
||||
}
|
||||
for (const t of this.targets_) {
|
||||
@@ -500,7 +503,7 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
canDonate(recipient: Player): boolean {
|
||||
if (!this.isAlliedWith(recipient)) {
|
||||
if (!this.isFriendly(recipient)) {
|
||||
return false;
|
||||
}
|
||||
for (const donation of this.sentDonations) {
|
||||
@@ -555,6 +558,24 @@ export class PlayerImpl implements Player {
|
||||
.filter((other) => other != this && this.canTrade(other));
|
||||
}
|
||||
|
||||
team(): Team | null {
|
||||
return this._team;
|
||||
}
|
||||
|
||||
isOnSameTeam(other: Player): boolean {
|
||||
if (other == this) {
|
||||
return false;
|
||||
}
|
||||
if (this.team() == null || other.team() == null) {
|
||||
return false;
|
||||
}
|
||||
return this._team == other.team();
|
||||
}
|
||||
|
||||
isFriendly(other: Player): boolean {
|
||||
return this.isOnSameTeam(other) || this.isAlliedWith(other);
|
||||
}
|
||||
|
||||
gold(): Gold {
|
||||
return Number(this._gold);
|
||||
}
|
||||
@@ -837,7 +858,7 @@ export class PlayerImpl implements Player {
|
||||
if (other == this) {
|
||||
return false;
|
||||
}
|
||||
if (other.isPlayer() && this.allianceWith(other)) {
|
||||
if (other.isPlayer() && this.isFriendly(other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -930,12 +951,13 @@ export class PlayerImpl implements Player {
|
||||
if (this.mg.owner(tile) == this) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
this.mg.hasOwner(tile) &&
|
||||
this.isAlliedWith(this.mg.owner(tile) as Player)
|
||||
) {
|
||||
return false;
|
||||
if (this.mg.hasOwner(tile)) {
|
||||
const other = this.mg.owner(tile) as Player;
|
||||
if (this.isFriendly(other)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.mg.isLand(tile)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ServerConfig } from "../core/configuration/Config";
|
||||
import { GameConfig, GameID } from "../core/Schemas";
|
||||
import { Client } from "./Client";
|
||||
import { GamePhase, GameServer } from "./GameServer";
|
||||
import { Difficulty, GameMapType, GameType } from "../core/game/Game";
|
||||
import { Difficulty, GameMapType, GameMode, GameType } from "../core/game/Game";
|
||||
import { Logger } from "winston";
|
||||
|
||||
export class GameManager {
|
||||
@@ -38,6 +38,7 @@ export class GameManager {
|
||||
infiniteGold: false,
|
||||
infiniteTroops: false,
|
||||
instantBuild: false,
|
||||
gameMode: GameMode.FFA,
|
||||
bots: 400,
|
||||
...gameConfig,
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
ClientID,
|
||||
ClientMessage,
|
||||
ClientMessageSchema,
|
||||
ClientSendWinnerMessage,
|
||||
GameConfig,
|
||||
GameInfo,
|
||||
Intent,
|
||||
@@ -45,7 +46,7 @@ export class GameServer {
|
||||
|
||||
private lastPingUpdate = 0;
|
||||
|
||||
private winner: ClientID | null = null;
|
||||
private winner: ClientSendWinnerMessage = null;
|
||||
// This field is currently only filled at victory
|
||||
private allPlayersStats: AllPlayersStats = {};
|
||||
|
||||
@@ -86,6 +87,9 @@ export class GameServer {
|
||||
if (gameConfig.instantBuild != null) {
|
||||
this.gameConfig.instantBuild = gameConfig.instantBuild;
|
||||
}
|
||||
if (gameConfig.gameMode != null) {
|
||||
this.gameConfig.gameMode = gameConfig.gameMode;
|
||||
}
|
||||
}
|
||||
|
||||
public addClient(client: Client, lastTurn: number) {
|
||||
@@ -171,7 +175,7 @@ export class GameServer {
|
||||
client.hashes.set(clientMsg.turnNumber, clientMsg.hash);
|
||||
}
|
||||
if (clientMsg.type == "winner") {
|
||||
this.winner = clientMsg.winner;
|
||||
this.winner = clientMsg;
|
||||
this.allPlayersStats = clientMsg.allPlayersStats;
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -318,7 +322,8 @@ export class GameServer {
|
||||
this.turns,
|
||||
this._startTime,
|
||||
Date.now(),
|
||||
this.winner,
|
||||
this.winner.winner,
|
||||
this.winner.winnerType,
|
||||
this.allPlayersStats,
|
||||
),
|
||||
);
|
||||
|
||||
@@ -165,6 +165,7 @@ export function startWorker() {
|
||||
bots: req.body.bots,
|
||||
disableNPCs: req.body.disableNPCs,
|
||||
disableNukes: req.body.disableNukes,
|
||||
gameMode: req.body.gameMode,
|
||||
});
|
||||
res.status(200).json({ success: true });
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user