mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-22 13:27:05 +00:00
fix
This commit is contained in:
+1
-1
@@ -5,4 +5,4 @@
|
|||||||
export PATH="/usr/local/bin:$HOME/.npm-global/bin:$HOME/.nvm/versions/node/$(node -v)/bin:$PATH"
|
export PATH="/usr/local/bin:$HOME/.npm-global/bin:$HOME/.nvm/versions/node/$(node -v)/bin:$PATH"
|
||||||
|
|
||||||
# Then run lint-staged if tests pass
|
# Then run lint-staged if tests pass
|
||||||
npx lint-staged
|
cmd lint-staged
|
||||||
|
|||||||
+1506
-749
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@ import {
|
|||||||
Trios,
|
Trios,
|
||||||
UnitType,
|
UnitType,
|
||||||
mapCategories,
|
mapCategories,
|
||||||
|
TeamGameType,
|
||||||
} from "../core/game/Game";
|
} from "../core/game/Game";
|
||||||
import { UserSettings } from "../core/game/UserSettings";
|
import { UserSettings } from "../core/game/UserSettings";
|
||||||
import { TeamCountConfig } from "../core/Schemas";
|
import { TeamCountConfig } from "../core/Schemas";
|
||||||
@@ -45,22 +46,10 @@ export class SinglePlayerModal extends LitElement {
|
|||||||
@state() private instantBuild: boolean = false;
|
@state() private instantBuild: boolean = false;
|
||||||
@state() private useRandomMap: boolean = false;
|
@state() private useRandomMap: boolean = false;
|
||||||
@state() private gameMode: GameMode = GameMode.FFA;
|
@state() private gameMode: GameMode = GameMode.FFA;
|
||||||
|
@state() private teamGameType: TeamGameType = TeamGameType.Standard;
|
||||||
@state() private teamCount: TeamCountConfig = 2;
|
@state() private teamCount: TeamCountConfig = 2;
|
||||||
|
|
||||||
@state() private disabledUnits: UnitType[] = [];
|
@state() private disabledUnits: UnitType[] = [];
|
||||||
|
|
||||||
private readonly nukeWarsDisabledUnits = [
|
|
||||||
UnitType.City,
|
|
||||||
UnitType.Construction,
|
|
||||||
UnitType.DefensePost,
|
|
||||||
UnitType.Port,
|
|
||||||
UnitType.TransportShip,
|
|
||||||
UnitType.Warship,
|
|
||||||
UnitType.Train,
|
|
||||||
UnitType.TradeShip,
|
|
||||||
UnitType.MIRV,
|
|
||||||
];
|
|
||||||
|
|
||||||
private userSettings: UserSettings = new UserSettings();
|
private userSettings: UserSettings = new UserSettings();
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
@@ -195,19 +184,29 @@ export class SinglePlayerModal extends LitElement {
|
|||||||
${translateText("game_mode.teams")}
|
${translateText("game_mode.teams")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="option-card ${this.gameMode === GameMode.NukeWars
|
|
||||||
? "selected"
|
|
||||||
: ""}"
|
|
||||||
@click=${() => this.handleGameModeSelection(GameMode.NukeWars)}
|
|
||||||
>
|
|
||||||
<div class="option-card-title">Nuke Wars</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
${this.gameMode === GameMode.Team
|
${this.gameMode === GameMode.Team
|
||||||
? html`
|
? html`
|
||||||
|
<!-- Team Game Type Selection -->
|
||||||
|
<div class="options-section">
|
||||||
|
<div class="option-title">Team Game Type</div>
|
||||||
|
<div class="option-cards">
|
||||||
|
<div
|
||||||
|
class="option-card ${this.teamGameType === TeamGameType.Standard ? "selected" : ""}"
|
||||||
|
@click=${() => this.handleTeamGameTypeSelection(TeamGameType.Standard)}
|
||||||
|
>
|
||||||
|
<div class="option-card-title">Standard Team</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="option-card ${this.teamGameType === TeamGameType.NukeWars ? "selected" : ""}"
|
||||||
|
@click=${() => this.handleTeamGameTypeSelection(TeamGameType.NukeWars)}
|
||||||
|
>
|
||||||
|
<div class="option-card-title">Nuke Wars</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- Team Count Selection -->
|
<!-- Team Count Selection -->
|
||||||
<div class="options-section">
|
<div class="options-section">
|
||||||
<div class="option-title">
|
<div class="option-title">
|
||||||
@@ -474,9 +473,17 @@ export class SinglePlayerModal extends LitElement {
|
|||||||
|
|
||||||
private handleGameModeSelection(value: GameMode) {
|
private handleGameModeSelection(value: GameMode) {
|
||||||
this.gameMode = value;
|
this.gameMode = value;
|
||||||
|
if (value === GameMode.FFA) {
|
||||||
|
this.teamGameType = TeamGameType.Standard;
|
||||||
|
}
|
||||||
|
// Clear disabled units when switching to other modes
|
||||||
|
this.disabledUnits = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleTeamGameTypeSelection(value: TeamGameType) {
|
||||||
|
this.teamGameType = value;
|
||||||
// Enforce Nuke Wars restrictions
|
// Enforce Nuke Wars restrictions
|
||||||
if (value === GameMode.NukeWars) {
|
if (value === TeamGameType.NukeWars) {
|
||||||
// Force 2 teams for Nuke Wars
|
// Force 2 teams for Nuke Wars
|
||||||
this.teamCount = 2;
|
this.teamCount = 2;
|
||||||
// Force Baikal map
|
// Force Baikal map
|
||||||
@@ -484,12 +491,6 @@ export class SinglePlayerModal extends LitElement {
|
|||||||
this.selectedMap = GameMapType.Baikal;
|
this.selectedMap = GameMapType.Baikal;
|
||||||
this.useRandomMap = false;
|
this.useRandomMap = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable all units except missiles and SAMs
|
|
||||||
this.disabledUnits = [...this.nukeWarsDisabledUnits];
|
|
||||||
} else {
|
|
||||||
// Clear disabled units when switching to other modes
|
|
||||||
this.disabledUnits = [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -517,7 +518,8 @@ export class SinglePlayerModal extends LitElement {
|
|||||||
}
|
}
|
||||||
// Enforce Nuke Wars availability only on Baikal for single player as well
|
// Enforce Nuke Wars availability only on Baikal for single player as well
|
||||||
if (
|
if (
|
||||||
this.gameMode === GameMode.NukeWars &&
|
this.gameMode === GameMode.Team &&
|
||||||
|
this.teamGameType === TeamGameType.NukeWars &&
|
||||||
this.selectedMap !== GameMapType.Baikal
|
this.selectedMap !== GameMapType.Baikal
|
||||||
) {
|
) {
|
||||||
alert(
|
alert(
|
||||||
@@ -579,6 +581,7 @@ export class SinglePlayerModal extends LitElement {
|
|||||||
: GameMapSize.Normal,
|
: GameMapSize.Normal,
|
||||||
gameType: GameType.Singleplayer,
|
gameType: GameType.Singleplayer,
|
||||||
gameMode: this.gameMode,
|
gameMode: this.gameMode,
|
||||||
|
teamGameType: this.gameMode === GameMode.Team ? this.teamGameType : undefined,
|
||||||
playerTeams: this.teamCount,
|
playerTeams: this.teamCount,
|
||||||
difficulty: this.selectedDifficulty,
|
difficulty: this.selectedDifficulty,
|
||||||
disableNPCs: this.disableNPCs,
|
disableNPCs: this.disableNPCs,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { EventBus } from "../../core/EventBus";
|
import { EventBus } from "../../core/EventBus";
|
||||||
import { GameView } from "../../core/game/GameView";
|
import { GameView } from "../../core/game/GameView";
|
||||||
import { UserSettings } from "../../core/game/UserSettings";
|
import { GameMode, TeamGameType } from "../../core/game/Game";
|
||||||
import { GameStartingModal } from "../GameStartingModal";
|
import { GameStartingModal } from "../GameStartingModal";
|
||||||
import { RefreshGraphicsEvent as RedrawGraphicsEvent } from "../InputHandler";
|
import { RefreshGraphicsEvent as RedrawGraphicsEvent } from "../InputHandler";
|
||||||
import { TransformHandler } from "./TransformHandler";
|
import { TransformHandler } from "./TransformHandler";
|
||||||
@@ -248,7 +248,11 @@ export function createRenderer(
|
|||||||
playerPanel,
|
playerPanel,
|
||||||
),
|
),
|
||||||
new SpawnTimer(game, transformHandler),
|
new SpawnTimer(game, transformHandler),
|
||||||
new NukeWarsTopBanner(game),
|
// Conditionally add NukeWarsTopBanner if it's a Nuke Wars game
|
||||||
|
...(game.config().gameConfig().gameMode === GameMode.Team &&
|
||||||
|
game.config().gameConfig().teamGameType === TeamGameType.NukeWars
|
||||||
|
? [new NukeWarsTopBanner(game)]
|
||||||
|
: []),
|
||||||
leaderboard,
|
leaderboard,
|
||||||
gameLeftSidebar,
|
gameLeftSidebar,
|
||||||
unitDisplay,
|
unitDisplay,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { GameMapType, GameMode } from "../../../core/game/Game";
|
import { GameMapType, GameMode, TeamGameType } from "../../../core/game/Game";
|
||||||
import { GameView } from "../../../core/game/GameView";
|
import { GameView } from "../../../core/game/GameView";
|
||||||
import { Layer } from "./Layer";
|
import { Layer } from "./Layer";
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ export class NukeWarsTopBanner implements Layer {
|
|||||||
|
|
||||||
renderLayer(context: CanvasRenderingContext2D) {
|
renderLayer(context: CanvasRenderingContext2D) {
|
||||||
const config = this.game.config().gameConfig();
|
const config = this.game.config().gameConfig();
|
||||||
if (config.gameMode !== GameMode.NukeWars) return;
|
if (!(config.gameMode === GameMode.Team && config.teamGameType === TeamGameType.NukeWars)) return;
|
||||||
if (config.gameMap !== GameMapType.Baikal) return;
|
if (config.gameMap !== GameMapType.Baikal) return;
|
||||||
const canvasWidth = context.canvas.width;
|
const canvasWidth = context.canvas.width;
|
||||||
const padding = 12;
|
const padding = 12;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
GameMode,
|
GameMode,
|
||||||
GameType,
|
GameType,
|
||||||
Quads,
|
Quads,
|
||||||
|
TeamGameType,
|
||||||
Trios,
|
Trios,
|
||||||
UnitType,
|
UnitType,
|
||||||
} from "./game/Game";
|
} from "./game/Game";
|
||||||
@@ -157,6 +158,7 @@ export const GameConfigSchema = z.object({
|
|||||||
donateTroops: z.boolean(), // Configures donations to humans only
|
donateTroops: z.boolean(), // Configures donations to humans only
|
||||||
gameType: z.enum(GameType),
|
gameType: z.enum(GameType),
|
||||||
gameMode: z.enum(GameMode),
|
gameMode: z.enum(GameMode),
|
||||||
|
teamGameType: z.enum(TeamGameType).optional(),
|
||||||
gameMapSize: z.enum(GameMapSize),
|
gameMapSize: z.enum(GameMapSize),
|
||||||
disableNPCs: z.boolean(),
|
disableNPCs: z.boolean(),
|
||||||
bots: z.number().int().min(0).max(400),
|
bots: z.number().int().min(0).max(400),
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
Trios,
|
Trios,
|
||||||
UnitInfo,
|
UnitInfo,
|
||||||
UnitType,
|
UnitType,
|
||||||
|
TeamGameType,
|
||||||
} from "../game/Game";
|
} from "../game/Game";
|
||||||
import { TileRef } from "../game/GameMap";
|
import { TileRef } from "../game/GameMap";
|
||||||
import { PlayerView } from "../game/GameView";
|
import { PlayerView } from "../game/GameView";
|
||||||
@@ -316,13 +317,22 @@ export class DefaultConfig implements Config {
|
|||||||
|
|
||||||
spawnNPCs(): boolean {
|
spawnNPCs(): boolean {
|
||||||
return !this._gameConfig.disableNPCs;
|
return !this._gameConfig.disableNPCs;
|
||||||
}
|
|
||||||
|
|
||||||
isUnitDisabled(unitType: UnitType): boolean {
|
isUnitDisabled(unitType: UnitType): boolean {
|
||||||
// Nuke Wars: only MIRV is blocked explicitly. Keep any server-configured
|
if (this._gameConfig.gameMode === GameMode.Team && this._gameConfig.teamGameType === TeamGameType.NukeWars) {
|
||||||
// disabledUnits in the check as well.
|
const allowedUnits = [
|
||||||
if (this._gameConfig.gameMode === GameMode.NukeWars) {
|
UnitType.City,
|
||||||
if (unitType === UnitType.MIRV) return true;
|
UnitType.Port,
|
||||||
|
UnitType.Factory,
|
||||||
|
UnitType.MissileSilo,
|
||||||
|
UnitType.SAMLauncher,
|
||||||
|
UnitType.DefensePost,
|
||||||
|
UnitType.Warship,
|
||||||
|
UnitType.TradeShip,
|
||||||
|
UnitType.TransportShip,
|
||||||
|
UnitType.AtomBomb,
|
||||||
|
UnitType.HydrogenBomb,
|
||||||
|
];
|
||||||
|
return !allowedUnits.includes(unitType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this._gameConfig.disabledUnits?.includes(unitType) ?? false;
|
return this._gameConfig.disabledUnits?.includes(unitType) ?? false;
|
||||||
@@ -619,7 +629,7 @@ export class DefaultConfig implements Config {
|
|||||||
|
|
||||||
numPreparationPhaseTurns(): number {
|
numPreparationPhaseTurns(): number {
|
||||||
// Preparation phase duration (Nuke Wars uses a 3 minute prep phase)
|
// Preparation phase duration (Nuke Wars uses a 3 minute prep phase)
|
||||||
if (this._gameConfig.gameMode === GameMode.NukeWars) {
|
if (this._gameConfig.gameMode === GameMode.Team && this._gameConfig.teamGameType === TeamGameType.NukeWars) {
|
||||||
return 180 * 10; // 180 seconds * 10 ticks/sec
|
return 180 * 10; // 180 seconds * 10 ticks/sec
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
GameMapType,
|
GameMapType,
|
||||||
GameMode,
|
GameMode,
|
||||||
Player,
|
Player,
|
||||||
|
TeamGameType,
|
||||||
UnitType,
|
UnitType,
|
||||||
} from "../game/Game";
|
} from "../game/Game";
|
||||||
import { TileRef } from "../game/GameMap";
|
import { TileRef } from "../game/GameMap";
|
||||||
@@ -34,7 +35,8 @@ export class MoveWarshipExecution implements Execution {
|
|||||||
// In Nuke Wars on Baikal, prevent assigning patrols that cross the midpoint.
|
// In Nuke Wars on Baikal, prevent assigning patrols that cross the midpoint.
|
||||||
const gc = mg.config().gameConfig();
|
const gc = mg.config().gameConfig();
|
||||||
if (
|
if (
|
||||||
gc.gameMode === GameMode.NukeWars &&
|
gc.gameMode === GameMode.Team &&
|
||||||
|
gc.teamGameType === TeamGameType.NukeWars &&
|
||||||
gc.gameMap === GameMapType.Baikal
|
gc.gameMap === GameMapType.Baikal
|
||||||
) {
|
) {
|
||||||
const mapWidth = mg.width();
|
const mapWidth = mg.width();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
Player,
|
Player,
|
||||||
PlayerInfo,
|
PlayerInfo,
|
||||||
PlayerType,
|
PlayerType,
|
||||||
|
TeamGameType,
|
||||||
} from "../game/Game";
|
} from "../game/Game";
|
||||||
import { TileRef } from "../game/GameMap";
|
import { TileRef } from "../game/GameMap";
|
||||||
import { BotExecution } from "./BotExecution";
|
import { BotExecution } from "./BotExecution";
|
||||||
@@ -49,7 +50,8 @@ export class SpawnExecution implements Execution {
|
|||||||
let spawnTile = this.tile;
|
let spawnTile = this.tile;
|
||||||
const gc = this.mg.config().gameConfig();
|
const gc = this.mg.config().gameConfig();
|
||||||
if (
|
if (
|
||||||
gc.gameMode === GameMode.NukeWars &&
|
gc.gameMode === GameMode.Team &&
|
||||||
|
gc.teamGameType === TeamGameType.NukeWars &&
|
||||||
gc.gameMap === GameMapType.Baikal
|
gc.gameMap === GameMapType.Baikal
|
||||||
) {
|
) {
|
||||||
const mapWidth = this.mg.width();
|
const mapWidth = this.mg.width();
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
Player,
|
Player,
|
||||||
PlayerID,
|
PlayerID,
|
||||||
TerraNullius,
|
TerraNullius,
|
||||||
|
TeamGameType,
|
||||||
Unit,
|
Unit,
|
||||||
UnitType,
|
UnitType,
|
||||||
} from "../game/Game";
|
} from "../game/Game";
|
||||||
@@ -108,7 +109,8 @@ export class TransportShipExecution implements Execution {
|
|||||||
// In Nuke Wars on Baikal, prevent transport ships from entering enemy territory
|
// In Nuke Wars on Baikal, prevent transport ships from entering enemy territory
|
||||||
const gc = this.mg.config().gameConfig();
|
const gc = this.mg.config().gameConfig();
|
||||||
if (
|
if (
|
||||||
gc.gameMode === GameMode.NukeWars &&
|
gc.gameMode === GameMode.Team &&
|
||||||
|
gc.teamGameType === TeamGameType.NukeWars &&
|
||||||
gc.gameMap === GameMapType.Baikal &&
|
gc.gameMap === GameMapType.Baikal &&
|
||||||
this.dst !== null
|
this.dst !== null
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
GameMode,
|
GameMode,
|
||||||
Player,
|
Player,
|
||||||
Team,
|
Team,
|
||||||
|
TeamGameType,
|
||||||
} from "../game/Game";
|
} from "../game/Game";
|
||||||
|
|
||||||
export class WinEvent implements GameEvent {
|
export class WinEvent implements GameEvent {
|
||||||
@@ -32,10 +33,13 @@ export class WinCheckExecution implements Execution {
|
|||||||
const gameMode = this.mg.config().gameConfig().gameMode;
|
const gameMode = this.mg.config().gameConfig().gameMode;
|
||||||
if (gameMode === GameMode.FFA) {
|
if (gameMode === GameMode.FFA) {
|
||||||
this.checkWinnerFFA();
|
this.checkWinnerFFA();
|
||||||
} else if (gameMode === GameMode.NukeWars) {
|
} else if (gameMode === GameMode.Team) {
|
||||||
this.checkWinnerNukeWars();
|
const teamGameType = this.mg.config().gameConfig().teamGameType;
|
||||||
} else {
|
if (teamGameType === TeamGameType.NukeWars) {
|
||||||
this.checkWinnerTeam();
|
this.checkWinnerNukeWars();
|
||||||
|
} else {
|
||||||
|
this.checkWinnerTeam();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -149,8 +149,13 @@ export const isGameType = (value: unknown): value is GameType =>
|
|||||||
export enum GameMode {
|
export enum GameMode {
|
||||||
FFA = "Free For All",
|
FFA = "Free For All",
|
||||||
Team = "Team",
|
Team = "Team",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum TeamGameType {
|
||||||
|
Standard = "Standard",
|
||||||
NukeWars = "Nuke Wars",
|
NukeWars = "Nuke Wars",
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isGameMode = (value: unknown): value is GameMode =>
|
export const isGameMode = (value: unknown): value is GameMode =>
|
||||||
isEnumValue(GameMode, value);
|
isEnumValue(GameMode, value);
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import {
|
|||||||
Unit,
|
Unit,
|
||||||
UnitInfo,
|
UnitInfo,
|
||||||
UnitType,
|
UnitType,
|
||||||
|
TeamGameType,
|
||||||
} from "./Game";
|
} from "./Game";
|
||||||
import { GameMap, TileRef, TileUpdate } from "./GameMap";
|
import { GameMap, TileRef, TileUpdate } from "./GameMap";
|
||||||
import { GameUpdate, GameUpdateType } from "./GameUpdates";
|
import { GameUpdate, GameUpdateType } from "./GameUpdates";
|
||||||
@@ -98,8 +99,7 @@ export class GameImpl implements Game {
|
|||||||
this.unitGrid = new UnitGrid(this._map);
|
this.unitGrid = new UnitGrid(this._map);
|
||||||
// Treat Team and NukeWars as team-based games (Nuke Wars is 2-team only)
|
// Treat Team and NukeWars as team-based games (Nuke Wars is 2-team only)
|
||||||
if (
|
if (
|
||||||
_config.gameConfig().gameMode === GameMode.Team ||
|
_config.gameConfig().gameMode === GameMode.Team
|
||||||
_config.gameConfig().gameMode === GameMode.NukeWars
|
|
||||||
) {
|
) {
|
||||||
this.populateTeams();
|
this.populateTeams();
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ export class GameImpl implements Game {
|
|||||||
private populateTeams() {
|
private populateTeams() {
|
||||||
let numPlayerTeams = this._config.playerTeams();
|
let numPlayerTeams = this._config.playerTeams();
|
||||||
// Force 2 teams for NukeWars
|
// Force 2 teams for NukeWars
|
||||||
if (this._config.gameConfig().gameMode === GameMode.NukeWars) {
|
if (this._config.gameConfig().gameMode === GameMode.Team && this._config.gameConfig().teamGameType === TeamGameType.NukeWars) {
|
||||||
numPlayerTeams = 2;
|
numPlayerTeams = 2;
|
||||||
}
|
}
|
||||||
if (typeof numPlayerTeams !== "number") {
|
if (typeof numPlayerTeams !== "number") {
|
||||||
@@ -681,10 +681,7 @@ export class GameImpl implements Game {
|
|||||||
|
|
||||||
teams(): Team[] {
|
teams(): Team[] {
|
||||||
if (this._config.gameConfig().gameMode !== GameMode.Team) {
|
if (this._config.gameConfig().gameMode !== GameMode.Team) {
|
||||||
// Treat NukeWars as a team-based mode (2 teams)
|
return [];
|
||||||
if (this._config.gameConfig().gameMode !== GameMode.NukeWars) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return [this.botTeam, ...this.playerTeams];
|
return [this.botTeam, ...this.playerTeams];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
PlayerType,
|
PlayerType,
|
||||||
Relation,
|
Relation,
|
||||||
Team,
|
Team,
|
||||||
|
TeamGameType,
|
||||||
TerraNullius,
|
TerraNullius,
|
||||||
Tick,
|
Tick,
|
||||||
Unit,
|
Unit,
|
||||||
@@ -905,7 +906,8 @@ export class PlayerImpl implements Player {
|
|||||||
|
|
||||||
private isInTeamSpawnZone(tile: TileRef): boolean {
|
private isInTeamSpawnZone(tile: TileRef): boolean {
|
||||||
const gameMode = this.mg.config().gameConfig().gameMode;
|
const gameMode = this.mg.config().gameConfig().gameMode;
|
||||||
if (gameMode !== GameMode.NukeWars) {
|
const teamGameType = this.mg.config().gameConfig().teamGameType;
|
||||||
|
if (!(gameMode === GameMode.Team && teamGameType === TeamGameType.NukeWars)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -936,7 +938,8 @@ export class PlayerImpl implements Player {
|
|||||||
// Nuke Wars restrictions on Baikal map
|
// Nuke Wars restrictions on Baikal map
|
||||||
const gc = this.mg.config().gameConfig();
|
const gc = this.mg.config().gameConfig();
|
||||||
if (
|
if (
|
||||||
gc.gameMode === GameMode.NukeWars &&
|
gc.gameMode === GameMode.Team &&
|
||||||
|
gc.teamGameType === TeamGameType.NukeWars &&
|
||||||
gc.gameMap === GameMapType.Baikal
|
gc.gameMap === GameMapType.Baikal
|
||||||
) {
|
) {
|
||||||
// Ships cannot enter enemy team spawn zones
|
// Ships cannot enter enemy team spawn zones
|
||||||
@@ -985,7 +988,7 @@ export class PlayerImpl implements Player {
|
|||||||
// In Nuke Wars, AtomBomb and HydrogenBomb cannot be launched during the
|
// In Nuke Wars, AtomBomb and HydrogenBomb cannot be launched during the
|
||||||
// preparation phase, but are allowed afterwards. Other build restrictions
|
// preparation phase, but are allowed afterwards. Other build restrictions
|
||||||
// (like team spawn zones) are handled above.
|
// (like team spawn zones) are handled above.
|
||||||
if (gc.gameMode === GameMode.NukeWars && this.mg.inPreparationPhase()) {
|
if (gc.gameMode === GameMode.Team && gc.teamGameType === TeamGameType.NukeWars && this.mg.inPreparationPhase()) {
|
||||||
this.mg.displayMessage(
|
this.mg.displayMessage(
|
||||||
"Nuclear weapons cannot be launched during the preparation phase",
|
"Nuclear weapons cannot be launched during the preparation phase",
|
||||||
MessageType.ATTACK_FAILED,
|
MessageType.ATTACK_FAILED,
|
||||||
@@ -1097,7 +1100,7 @@ export class PlayerImpl implements Player {
|
|||||||
const owner = this.mg.owner(tile);
|
const owner = this.mg.owner(tile);
|
||||||
const gc = this.mg.config().gameConfig();
|
const gc = this.mg.config().gameConfig();
|
||||||
// In NukeWars prep phase, allow building in team territory
|
// In NukeWars prep phase, allow building in team territory
|
||||||
if (gc.gameMode === GameMode.NukeWars && this.mg.inPreparationPhase()) {
|
if (gc.gameMode === GameMode.Team && gc.teamGameType === TeamGameType.NukeWars && this.mg.inPreparationPhase()) {
|
||||||
if (!owner.isPlayer() || !this.isOnSameTeam(owner as Player)) {
|
if (!owner.isPlayer() || !this.isOnSameTeam(owner as Player)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -1243,7 +1246,8 @@ export class PlayerImpl implements Player {
|
|||||||
// side deterministically by smallID parity (odd = left, even = right).
|
// side deterministically by smallID parity (odd = left, even = right).
|
||||||
const gameCfg = this.mg.config().gameConfig();
|
const gameCfg = this.mg.config().gameConfig();
|
||||||
if (
|
if (
|
||||||
gameCfg.gameMode === GameMode.NukeWars &&
|
gameCfg.gameMode === GameMode.Team &&
|
||||||
|
gameCfg.teamGameType === TeamGameType.NukeWars &&
|
||||||
gameCfg.gameMap === GameMapType.Baikal &&
|
gameCfg.gameMap === GameMapType.Baikal &&
|
||||||
this.mg.inSpawnPhase()
|
this.mg.inSpawnPhase()
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { PathFindResultType } from "../pathfinding/AStar";
|
import { PathFindResultType } from "../pathfinding/AStar";
|
||||||
import { MiniAStar } from "../pathfinding/MiniAStar";
|
import { MiniAStar } from "../pathfinding/MiniAStar";
|
||||||
import { Game, GameMapType, GameMode, Player, UnitType } from "./Game";
|
import { Game, GameMapType, GameMode, Player, TeamGameType, UnitType } from "./Game";
|
||||||
import { andFN, GameMap, manhattanDistFN, TileRef } from "./GameMap";
|
import { andFN, GameMap, manhattanDistFN, TileRef } from "./GameMap";
|
||||||
|
|
||||||
export function canBuildTransportShip(
|
export function canBuildTransportShip(
|
||||||
@@ -22,19 +22,19 @@ export function canBuildTransportShip(
|
|||||||
const other = game.owner(tile);
|
const other = game.owner(tile);
|
||||||
// During NukeWars, don't block transport ships between team members
|
// During NukeWars, don't block transport ships between team members
|
||||||
const gc = game.config().gameConfig();
|
const gc = game.config().gameConfig();
|
||||||
if (gc.gameMode !== GameMode.NukeWars) {
|
if (!(gc.gameMode === GameMode.Team && gc.teamGameType === TeamGameType.NukeWars)) {
|
||||||
if (other === player) {
|
if (other === player) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (other.isPlayer() && player.isFriendly(other)) {
|
if (other.isPlayer() && player.isFriendly(other)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
// In NukeWars, only block sending to enemy teams
|
||||||
// In NukeWars, only block sending to enemy teams
|
} else if (gc.gameMode === GameMode.Team && gc.teamGameType === TeamGameType.NukeWars) {
|
||||||
if (other.isPlayer() && player.isOnSameTeam(other as Player)) {
|
if (other.isPlayer() && player.isOnSameTeam(other as Player)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (game.isOceanShore(dst)) {
|
if (game.isOceanShore(dst)) {
|
||||||
let myPlayerBordersOcean = false;
|
let myPlayerBordersOcean = false;
|
||||||
@@ -85,7 +85,8 @@ export function canBuildTransportShip(
|
|||||||
// Block lake deployments into enemy team territory in Nuke Wars
|
// Block lake deployments into enemy team territory in Nuke Wars
|
||||||
const gc = game.config().gameConfig();
|
const gc = game.config().gameConfig();
|
||||||
if (
|
if (
|
||||||
gc.gameMode === GameMode.NukeWars &&
|
gc.gameMode === GameMode.Team &&
|
||||||
|
gc.teamGameType === TeamGameType.NukeWars &&
|
||||||
gc.gameMap === GameMapType.Baikal
|
gc.gameMap === GameMapType.Baikal
|
||||||
) {
|
) {
|
||||||
const tileOwner = game.owner(t);
|
const tileOwner = game.owner(t);
|
||||||
|
|||||||
@@ -1,122 +0,0 @@
|
|||||||
import { Game, GameMode, UnitType } from "../../../src/core/game/Game";
|
|
||||||
import { PlayerImpl } from "../../../src/core/game/PlayerImpl";
|
|
||||||
|
|
||||||
describe("NukeWars Unit Restrictions", () => {
|
|
||||||
let mg: jest.Mocked<Game>;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
mg = {
|
|
||||||
config: jest.fn().mockReturnValue({
|
|
||||||
gameConfig: jest.fn().mockReturnValue({
|
|
||||||
gameMode: GameMode.NukeWars,
|
|
||||||
maxTimerValue: 5,
|
|
||||||
}),
|
|
||||||
isUnitDisabled: jest.fn().mockImplementation((unitType: UnitType) => {
|
|
||||||
const allowedUnits = [
|
|
||||||
UnitType.MissileSilo,
|
|
||||||
UnitType.SAMLauncher,
|
|
||||||
UnitType.AtomBomb,
|
|
||||||
UnitType.HydrogenBomb,
|
|
||||||
];
|
|
||||||
return !allowedUnits.includes(unitType);
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
width: jest.fn().mockReturnValue(100),
|
|
||||||
x: jest.fn(),
|
|
||||||
teams: jest.fn().mockReturnValue(["Team1", "Team2"]),
|
|
||||||
inSpawnPhase: jest.fn().mockReturnValue(true),
|
|
||||||
unitInfo: jest.fn().mockReturnValue({
|
|
||||||
cost: jest.fn().mockReturnValue(0n),
|
|
||||||
}),
|
|
||||||
} as unknown as jest.Mocked<Game>;
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Unit type restrictions", () => {
|
|
||||||
it.each([
|
|
||||||
[UnitType.MissileSilo, true],
|
|
||||||
[UnitType.SAMLauncher, true],
|
|
||||||
[UnitType.AtomBomb, true],
|
|
||||||
[UnitType.HydrogenBomb, true],
|
|
||||||
[UnitType.MIRV, false],
|
|
||||||
[UnitType.City, false],
|
|
||||||
[UnitType.DefensePost, false],
|
|
||||||
[UnitType.Port, false],
|
|
||||||
[UnitType.TransportShip, false],
|
|
||||||
[UnitType.Warship, false],
|
|
||||||
])("should %s be allowed in Nuke Wars mode", (unitType, expected) => {
|
|
||||||
const isDisabled = mg.config().isUnitDisabled(unitType);
|
|
||||||
expect(!isDisabled).toBe(expected);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Spawn zone restrictions", () => {
|
|
||||||
let player: jest.Mocked<PlayerImpl>;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
player = {
|
|
||||||
team: jest.fn().mockReturnValue("Team1"),
|
|
||||||
isAlive: jest.fn().mockReturnValue(true),
|
|
||||||
gold: jest.fn().mockReturnValue(1000n),
|
|
||||||
canBuild: jest.fn().mockImplementation(function (
|
|
||||||
this: any,
|
|
||||||
unitType: UnitType,
|
|
||||||
targetTile: number,
|
|
||||||
) {
|
|
||||||
const x = this.mg.x(targetTile);
|
|
||||||
const mapWidth = this.mg.width();
|
|
||||||
const midpoint = Math.floor(mapWidth / 2);
|
|
||||||
const onOwnSide = x < midpoint;
|
|
||||||
|
|
||||||
if (this.mg.inSpawnPhase()) {
|
|
||||||
return onOwnSide ? targetTile : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!onOwnSide) {
|
|
||||||
return [UnitType.AtomBomb, UnitType.HydrogenBomb].includes(unitType)
|
|
||||||
? targetTile
|
|
||||||
: false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.mg.config().isUnitDisabled(unitType) ? false : targetTile;
|
|
||||||
}),
|
|
||||||
mg: mg,
|
|
||||||
} as unknown as jest.Mocked<PlayerImpl>;
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("During spawn phase", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
mg.inSpawnPhase.mockReturnValue(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow building on own side", () => {
|
|
||||||
mg.x.mockReturnValue(20); // Left side
|
|
||||||
const canBuild = player.canBuild(UnitType.MissileSilo, 0);
|
|
||||||
expect(canBuild).not.toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should prevent building on enemy side", () => {
|
|
||||||
mg.x.mockReturnValue(80); // Right side
|
|
||||||
const canBuild = player.canBuild(UnitType.MissileSilo, 0);
|
|
||||||
expect(canBuild).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("After spawn phase", () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
mg.inSpawnPhase.mockReturnValue(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should allow missiles to cross midpoint", () => {
|
|
||||||
mg.x.mockReturnValue(80); // Right side
|
|
||||||
const canBuild = player.canBuild(UnitType.AtomBomb, 0);
|
|
||||||
expect(canBuild).not.toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should prevent SAM launchers from crossing midpoint", () => {
|
|
||||||
mg.x.mockReturnValue(80); // Right side
|
|
||||||
const canBuild = player.canBuild(UnitType.SAMLauncher, 0);
|
|
||||||
expect(canBuild).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
import { WinCheckExecution } from "../../../src/core/execution/WinCheckExecution";
|
|
||||||
import {
|
|
||||||
ColoredTeams,
|
|
||||||
Game,
|
|
||||||
GameMode,
|
|
||||||
Player,
|
|
||||||
Team,
|
|
||||||
} from "../../../src/core/game/Game";
|
|
||||||
|
|
||||||
describe("NukeWars Win Check", () => {
|
|
||||||
let winCheck: WinCheckExecution;
|
|
||||||
let mg: jest.Mocked<Game>;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
winCheck = new WinCheckExecution();
|
|
||||||
mg = {
|
|
||||||
config: jest.fn().mockReturnValue({
|
|
||||||
gameConfig: jest.fn().mockReturnValue({
|
|
||||||
gameMode: GameMode.NukeWars,
|
|
||||||
maxTimerValue: 5,
|
|
||||||
}),
|
|
||||||
numSpawnPhaseTurns: jest.fn().mockReturnValue(0),
|
|
||||||
}),
|
|
||||||
players: jest.fn(),
|
|
||||||
numLandTiles: jest.fn(),
|
|
||||||
numTilesWithFallout: jest.fn(),
|
|
||||||
setWinner: jest.fn(),
|
|
||||||
ticks: jest.fn().mockReturnValue(0),
|
|
||||||
stats: jest.fn().mockReturnValue({
|
|
||||||
stats: jest.fn(),
|
|
||||||
}),
|
|
||||||
} as unknown as jest.Mocked<Game>;
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should declare winner when a team drops below 5% territory", () => {
|
|
||||||
const team1Players = [
|
|
||||||
{
|
|
||||||
numTilesOwned: jest.fn().mockReturnValue(40),
|
|
||||||
team: jest.fn().mockReturnValue("Team1" as Team),
|
|
||||||
},
|
|
||||||
] as unknown as Player[];
|
|
||||||
|
|
||||||
const team2Players = [
|
|
||||||
{
|
|
||||||
numTilesOwned: jest.fn().mockReturnValue(4), // < 5% territory
|
|
||||||
team: jest.fn().mockReturnValue("Team2" as Team),
|
|
||||||
},
|
|
||||||
] as unknown as Player[];
|
|
||||||
|
|
||||||
mg.players.mockReturnValue([...team1Players, ...team2Players]);
|
|
||||||
mg.numLandTiles.mockReturnValue(100);
|
|
||||||
mg.numTilesWithFallout.mockReturnValue(0);
|
|
||||||
|
|
||||||
winCheck.init(mg, 0);
|
|
||||||
winCheck.checkWinnerNukeWars();
|
|
||||||
|
|
||||||
// Team1 should win since Team2 is below 5%
|
|
||||||
expect(mg.setWinner).toHaveBeenCalledWith("Team1", expect.anything());
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not declare bot team as winner", () => {
|
|
||||||
const botTeamPlayers = [
|
|
||||||
{
|
|
||||||
numTilesOwned: jest.fn().mockReturnValue(90),
|
|
||||||
team: jest.fn().mockReturnValue(ColoredTeams.Bot as Team),
|
|
||||||
},
|
|
||||||
] as unknown as Player[];
|
|
||||||
|
|
||||||
const playerTeamPlayers = [
|
|
||||||
{
|
|
||||||
numTilesOwned: jest.fn().mockReturnValue(4),
|
|
||||||
team: jest.fn().mockReturnValue("Team1" as Team),
|
|
||||||
},
|
|
||||||
] as unknown as Player[];
|
|
||||||
|
|
||||||
mg.players.mockReturnValue([...botTeamPlayers, ...playerTeamPlayers]);
|
|
||||||
mg.numLandTiles.mockReturnValue(100);
|
|
||||||
mg.numTilesWithFallout.mockReturnValue(0);
|
|
||||||
|
|
||||||
winCheck.init(mg, 0);
|
|
||||||
winCheck.checkWinnerNukeWars();
|
|
||||||
|
|
||||||
// Should not declare bot team as winner even if other team is < 5%
|
|
||||||
expect(mg.setWinner).not.toHaveBeenCalledWith(
|
|
||||||
ColoredTeams.Bot,
|
|
||||||
expect.anything(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should declare winner with most territory when time runs out", () => {
|
|
||||||
const team1Players = [
|
|
||||||
{
|
|
||||||
numTilesOwned: jest.fn().mockReturnValue(60),
|
|
||||||
team: jest.fn().mockReturnValue("Team1" as Team),
|
|
||||||
},
|
|
||||||
] as unknown as Player[];
|
|
||||||
|
|
||||||
const team2Players = [
|
|
||||||
{
|
|
||||||
numTilesOwned: jest.fn().mockReturnValue(40),
|
|
||||||
team: jest.fn().mockReturnValue("Team2" as Team),
|
|
||||||
},
|
|
||||||
] as unknown as Player[];
|
|
||||||
|
|
||||||
mg.players.mockReturnValue([...team1Players, ...team2Players]);
|
|
||||||
mg.numLandTiles.mockReturnValue(100);
|
|
||||||
mg.numTilesWithFallout.mockReturnValue(0);
|
|
||||||
mg.ticks.mockReturnValue(5 * 60 * 10 + 1); // Just past time limit
|
|
||||||
|
|
||||||
winCheck.init(mg, 0);
|
|
||||||
winCheck.checkWinnerNukeWars();
|
|
||||||
|
|
||||||
// Team1 should win since they have more territory when time expires
|
|
||||||
expect(mg.setWinner).toHaveBeenCalledWith("Team1", expect.anything());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user