diff --git a/resources/lang/en.json b/resources/lang/en.json
index 62a7153d7..871b516bd 100644
--- a/resources/lang/en.json
+++ b/resources/lang/en.json
@@ -101,6 +101,7 @@
"infinite_gold": "Infinite gold",
"infinite_troops": "Infinite troops",
"disable_nukes": "Disable Nukes",
+ "enables_title": "Enable Settings",
"start": "Start Game"
},
"map": {
@@ -167,7 +168,7 @@
"instant_build": "Instant build",
"infinite_gold": "Infinite gold",
"infinite_troops": "Infinite troops",
- "disable_nukes": "Disable Nukes",
+ "enables_title": "Enable Settings",
"player": "Player",
"players": "Players",
"waiting": "Waiting for players...",
@@ -191,6 +192,17 @@
"select_lang": {
"title": "Select Language"
},
+ "unit_type": {
+ "city": "City",
+ "defense_post": "Defense Post",
+ "port": "Port",
+ "warship": "Warship",
+ "missile_silo": "Missile Silo",
+ "sam_launcher": "SAM Launcher",
+ "atom_bomb": "Atom Bomb",
+ "hydrogen_bomb": "Hydrogen Bomb",
+ "mirv": "MIRV"
+ },
"user_setting": {
"title": "User Settings",
"tab_basic": "Basic Settings",
diff --git a/resources/lang/ja.json b/resources/lang/ja.json
index 98e89edbd..06eb8d9c3 100644
--- a/resources/lang/ja.json
+++ b/resources/lang/ja.json
@@ -97,7 +97,7 @@
"instant_build": "即時建設",
"infinite_gold": "資金無限",
"infinite_troops": "兵士無限",
- "disable_nukes": "核兵器使用禁止",
+ "enables_title": "有効化設定",
"start": "ゲーム開始"
},
"map": {
@@ -158,7 +158,7 @@
"instant_build": "実在する国家を無効化",
"infinite_gold": "資金無限",
"infinite_troops": "兵士無限",
- "disable_nukes": "核兵器使用禁止",
+ "enables_title": "有効化設定",
"player": "プレイヤー",
"players": "プレイヤー",
"waiting": "他のプレイヤーの参加を待っています...",
@@ -182,6 +182,17 @@
"select_lang": {
"title": "言語を選択"
},
+ "unit_type": {
+ "city": "都市",
+ "defense_post": "防衛ポスト",
+ "port": "港",
+ "warship": "戦艦",
+ "missile_silo": "ミサイル格納庫",
+ "sam_launcher": "SAMランチャー",
+ "atom_bomb": "原子爆弾",
+ "hydrogen_bomb": "水素爆弾",
+ "mirv": "MIRV"
+ },
"user_setting": {
"title": "ユーザー設定",
"tab_basic": "基本設定",
diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts
index 86b6197b5..0f86f05b8 100644
--- a/src/client/HostLobbyModal.ts
+++ b/src/client/HostLobbyModal.ts
@@ -9,6 +9,7 @@ import {
Duos,
GameMapType,
GameMode,
+ UnitType,
mapCategories,
} from "../core/game/Game";
import { GameConfig, GameInfo } from "../core/Schemas";
@@ -39,6 +40,7 @@ export class HostLobbyModal extends LitElement {
@state() private copySuccess = false;
@state() private players: string[] = [];
@state() private useRandomMap: boolean = false;
+ @state() private disabledUnits: string[] = [];
private playersInterval = null;
// Add a new timer for debouncing bot changes
@@ -302,21 +304,72 @@ export class HostLobbyModal extends LitElement {
-
+
+
+
-
-
-
- ${translateText("host_modal.disable_nukes")}
+ ${translateText("host_modal.enables_title")}
+
+
+ ${[
+ [UnitType.City, "unit_type.city"],
+ [UnitType.DefensePost, "unit_type.defense_post"],
+ [UnitType.Port, "unit_type.port"],
+ [UnitType.Warship, "unit_type.warship"],
+ [UnitType.MissileSilo, "unit_type.missile_silo"],
+ [UnitType.SAMLauncher, "unit_type.sam_launcher"],
+ [UnitType.AtomBomb, "unit_type.atom_bomb"],
+ [UnitType.HydrogenBomb, "unit_type.hydrogen_bomb"],
+ [UnitType.MIRV, "unit_type.mirv"],
+ ].map(
+ ([unitType, translationKey]) => html`
+
+ `,
+ )}
-
+
@@ -490,6 +543,8 @@ export class HostLobbyModal extends LitElement {
infiniteTroops: this.infiniteTroops,
instantBuild: this.instantBuild,
gameMode: this.gameMode,
+ numPlayerTeams: this.teamCount,
+ disabledUnits: this.disabledUnits,
playerTeams: this.teamCount,
} as GameConfig),
},
diff --git a/src/client/SinglePlayerModal.ts b/src/client/SinglePlayerModal.ts
index 0edcc185c..f6960e2c8 100644
--- a/src/client/SinglePlayerModal.ts
+++ b/src/client/SinglePlayerModal.ts
@@ -9,6 +9,7 @@ import {
GameMapType,
GameMode,
GameType,
+ UnitType,
mapCategories,
} from "../core/game/Game";
import { generateID } from "../core/Util";
@@ -39,6 +40,8 @@ export class SinglePlayerModal extends LitElement {
@state() private gameMode: GameMode = GameMode.FFA;
@state() private teamCount: number | typeof Duos = 2;
+ @state() private disabledUnits: string[] = [];
+
render() {
return html`
@@ -269,22 +272,61 @@ export class SinglePlayerModal extends LitElement {
${translateText("single_modal.infinite_troops")}
+
-
+
+
+ ${translateText("single_modal.enables_title")}
+
+
+ ${[
+ [UnitType.City, "unit_type.city"],
+ [UnitType.DefensePost, "unit_type.defense_post"],
+ [UnitType.Port, "unit_type.port"],
+ [UnitType.Warship, "unit_type.warship"],
+ [UnitType.MissileSilo, "unit_type.missile_silo"],
+ [UnitType.SAMLauncher, "unit_type.sam_launcher"],
+ [UnitType.AtomBomb, "unit_type.atom_bomb"],
+ [UnitType.HydrogenBomb, "unit_type.hydrogen_bomb"],
+ [UnitType.MIRV, "unit_type.mirv"],
+ ].map(
+ ([unitType, translationKey]) => html`
+
+ `,
+ )}
@@ -419,6 +461,7 @@ export class SinglePlayerModal extends LitElement {
infiniteGold: this.infiniteGold,
infiniteTroops: this.infiniteTroops,
instantBuild: this.instantBuild,
+ disabledUnits: this.disabledUnits,
},
},
} as JoinLobbyEvent,
diff --git a/src/client/graphics/layers/BuildMenu.ts b/src/client/graphics/layers/BuildMenu.ts
index ed3a477d8..a4105c46f 100644
--- a/src/client/graphics/layers/BuildMenu.ts
+++ b/src/client/graphics/layers/BuildMenu.ts
@@ -409,21 +409,9 @@ export class BuildMenu extends LitElement implements Layer {
}
private getBuildableUnits(): BuildItemDisplay[][] {
- if (this.game?.config()?.disableNukes()) {
- return buildTable.map((row) =>
- row.filter(
- (item) =>
- ![
- UnitType.AtomBomb,
- UnitType.MIRV,
- UnitType.HydrogenBomb,
- UnitType.MissileSilo,
- UnitType.SAMLauncher,
- ].includes(item.unitType),
- ),
- );
- }
- return buildTable;
+ return buildTable.map((row) =>
+ row.filter((item) => !this.game?.config()?.isUnitDisabled(item.unitType)),
+ );
}
get isVisible() {
diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts
index b08fcfcfb..fba00a877 100644
--- a/src/core/Schemas.ts
+++ b/src/core/Schemas.ts
@@ -116,12 +116,13 @@ const GameConfigSchema = z.object({
gameType: z.nativeEnum(GameType),
gameMode: z.nativeEnum(GameMode),
disableNPCs: z.boolean(),
- disableNukes: z.boolean(),
bots: z.number().int().min(0).max(400),
infiniteGold: z.boolean(),
infiniteTroops: z.boolean(),
instantBuild: z.boolean(),
maxPlayers: z.number().optional(),
+ numPlayerTeams: z.number().optional(),
+ disabledUnits: z.array(z.nativeEnum(UnitType)).optional(),
playerTeams: z.union([z.number().optional(), z.literal(Duos)]),
});
diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts
index 1442dd814..9f55f8b52 100644
--- a/src/core/configuration/Config.ts
+++ b/src/core/configuration/Config.ts
@@ -66,7 +66,7 @@ export interface Config {
percentageTilesOwnedToWin(): number;
numBots(): number;
spawnNPCs(): boolean;
- disableNukes(): boolean;
+ isUnitDisabled(unitType: UnitType): boolean;
bots(): number;
infiniteGold(): boolean;
infiniteTroops(): boolean;
diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts
index dfbca1d77..660820fd8 100644
--- a/src/core/configuration/DefaultConfig.ts
+++ b/src/core/configuration/DefaultConfig.ts
@@ -231,9 +231,10 @@ export class DefaultConfig implements Config {
return !this._gameConfig.disableNPCs;
}
- disableNukes(): boolean {
- return this._gameConfig.disableNukes;
+ isUnitDisabled(unitType: UnitType): boolean {
+ return this._gameConfig.disabledUnits?.includes(unitType) ?? false;
}
+
bots(): number {
return this._gameConfig.bots;
}
diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts
index 9870fe662..358164931 100644
--- a/src/core/execution/FakeHumanExecution.ts
+++ b/src/core/execution/FakeHumanExecution.ts
@@ -421,9 +421,7 @@ export class FakeHumanExecution implements Execution {
if (this.maybeSpawnWarship()) {
return;
}
- if (!this.mg.config().disableNukes()) {
- this.maybeSpawnStructure(UnitType.MissileSilo, 1);
- }
+ this.maybeSpawnStructure(UnitType.MissileSilo, 1);
}
private maybeSpawnStructure(type: UnitType, maxNum: number) {
diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts
index b2046cd40..55972adc5 100644
--- a/src/core/game/PlayerImpl.ts
+++ b/src/core/game/PlayerImpl.ts
@@ -709,6 +709,12 @@ export class PlayerImpl implements Player {
spawnTile: TileRef,
unitSpecificInfos: UnitSpecificInfos = {},
): UnitImpl {
+ if (this.mg.config().isUnitDisabled(type)) {
+ throw new Error(
+ `Attempted to build disabled unit ${type} at tile ${spawnTile} by player ${this.name()}`,
+ );
+ }
+
const cost = this.mg.unitInfo(type).cost(this);
const b = new UnitImpl(
type,
@@ -746,19 +752,8 @@ export class PlayerImpl implements Player {
targetTile: TileRef,
validTiles: TileRef[] | null = null,
): TileRef | false {
- // prevent the building of nukes and nuke related buildings
- if (this.mg.config().disableNukes()) {
- if (
- unitType === UnitType.MissileSilo ||
- unitType === UnitType.MIRV ||
- unitType === UnitType.AtomBomb ||
- unitType === UnitType.HydrogenBomb ||
- unitType === UnitType.SAMLauncher ||
- unitType === UnitType.SAMMissile ||
- unitType === UnitType.MIRVWarhead
- ) {
- return false;
- }
+ if (this.mg.config().isUnitDisabled(unitType)) {
+ return false;
}
const cost = this.mg.unitInfo(unitType).cost(this);
diff --git a/src/server/GameManager.ts b/src/server/GameManager.ts
index 2a4e196c5..c2a890e9d 100644
--- a/src/server/GameManager.ts
+++ b/src/server/GameManager.ts
@@ -34,12 +34,12 @@ export class GameManager {
gameType: GameType.Private,
difficulty: Difficulty.Medium,
disableNPCs: false,
- disableNukes: false,
infiniteGold: false,
infiniteTroops: false,
instantBuild: false,
gameMode: GameMode.FFA,
bots: 400,
+ disabledUnits: [],
...gameConfig,
});
this.games.set(id, game);
diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts
index c655a1cf4..df95296f9 100644
--- a/src/server/GameServer.ts
+++ b/src/server/GameServer.ts
@@ -77,9 +77,6 @@ export class GameServer {
if (gameConfig.disableNPCs != null) {
this.gameConfig.disableNPCs = gameConfig.disableNPCs;
}
- if (gameConfig.disableNukes != null) {
- this.gameConfig.disableNukes = gameConfig.disableNukes;
- }
if (gameConfig.bots != null) {
this.gameConfig.bots = gameConfig.bots;
}
@@ -95,6 +92,11 @@ export class GameServer {
if (gameConfig.gameMode != null) {
this.gameConfig.gameMode = gameConfig.gameMode;
}
+
+ if (gameConfig.disabledUnits != null) {
+ this.gameConfig.disabledUnits = gameConfig.disabledUnits;
+ }
+
if (gameConfig.playerTeams != null) {
this.gameConfig.playerTeams = gameConfig.playerTeams;
}
diff --git a/src/server/Worker.ts b/src/server/Worker.ts
index 3307d5629..880dca263 100644
--- a/src/server/Worker.ts
+++ b/src/server/Worker.ts
@@ -162,7 +162,7 @@ export function startWorker() {
instantBuild: req.body.instantBuild,
bots: req.body.bots,
disableNPCs: req.body.disableNPCs,
- disableNukes: req.body.disableNukes,
+ disabledUnits: req.body.disabledUnits,
gameMode: req.body.gameMode,
playerTeams: req.body.playerTeams,
});