Disable nukes option (#237)

This makes it possible to disable all nukes as well as missile silos
from the game in privat lobbies and singleplayer games
This commit is contained in:
Readixyee
2025-03-21 21:52:36 +01:00
committed by GitHub
parent c4614fe0ba
commit e02361c2f4
12 changed files with 94 additions and 7 deletions
+19
View File
@@ -18,6 +18,7 @@ export class HostLobbyModal extends LitElement {
@state() private selectedMap: GameMapType = GameMapType.World;
@state() private selectedDifficulty: Difficulty = Difficulty.Medium;
@state() private disableNPCs = false;
@state() private disableNukes: boolean = false;
@state() private bots: number = 400;
@state() private infiniteGold: boolean = false;
@state() private infiniteTroops: boolean = false;
@@ -506,6 +507,20 @@ export class HostLobbyModal extends LitElement {
/>
<div class="option-card-title">Infinite troops</div>
</label>
<label
for="disable-nukes"
class="option-card ${this.disableNukes ? "selected" : ""}"
>
<div class="checkbox-icon"></div>
<input
type="checkbox"
id="disable-nukes"
@change=${this.handleDisableNukesChange}
.checked=${this.disableNukes}
/>
<div class="option-card-title">Disable Nukes</div>
</label>
</div>
</div>
@@ -621,6 +636,9 @@ export class HostLobbyModal extends LitElement {
this.infiniteTroops = Boolean((e.target as HTMLInputElement).checked);
this.putGameConfig();
}
private handleDisableNukesChange(e: Event) {
this.disableNukes = Boolean((e.target as HTMLInputElement).checked);
}
private async handleDisableNPCsChange(e: Event) {
this.disableNPCs = Boolean((e.target as HTMLInputElement).checked);
@@ -641,6 +659,7 @@ export class HostLobbyModal extends LitElement {
gameMap: this.selectedMap,
difficulty: this.selectedDifficulty,
disableNPCs: this.disableNPCs,
disableNukes: this.disableNukes,
bots: this.bots,
infiniteGold: this.infiniteGold,
infiniteTroops: this.infiniteTroops,
+19
View File
@@ -16,6 +16,7 @@ export class SinglePlayerModal extends LitElement {
@state() private selectedMap: GameMapType = GameMapType.World;
@state() private selectedDifficulty: Difficulty = Difficulty.Medium;
@state() private disableNPCs: boolean = false;
@state() private disableNukes: boolean = false;
@state() private bots: number = 400;
@state() private infiniteGold: boolean = false;
@state() private infiniteTroops: boolean = false;
@@ -411,6 +412,20 @@ export class SinglePlayerModal extends LitElement {
/>
<div class="option-card-title">Infinite troops</div>
</label>
<label
for="disable-nukes"
class="option-card ${this.disableNukes ? "selected" : ""}"
>
<div class="checkbox-icon"></div>
<input
type="checkbox"
id="disable-nukes"
@change=${this.handleDisableNukesChange}
.checked=${this.disableNukes}
/>
<div class="option-card-title">Disable Nukes</div>
</label>
</div>
</div>
</div>
@@ -464,6 +479,9 @@ export class SinglePlayerModal extends LitElement {
private handleDisableNPCsChange(e: Event) {
this.disableNPCs = Boolean((e.target as HTMLInputElement).checked);
}
private handleDisableNukesChange(e: Event) {
this.disableNukes = Boolean((e.target as HTMLInputElement).checked);
}
private getRandomMap(): GameMapType {
const maps = Object.values(GameMapType);
@@ -490,6 +508,7 @@ export class SinglePlayerModal extends LitElement {
gameType: GameType.Singleplayer,
difficulty: this.selectedDifficulty,
disableNPCs: this.disableNPCs,
disableNukes: this.disableNukes,
bots: this.bots,
infiniteGold: this.infiniteGold,
infiniteTroops: this.infiniteTroops,
+22 -1
View File
@@ -96,6 +96,7 @@ export class BuildMenu extends LitElement implements Layer {
public eventBus: EventBus;
private clickedTile: TileRef;
private playerActions: PlayerActions | null;
private filteredBuildTable: BuildItemDisplay[][] = buildTable;
tick() {
if (!this._hidden) {
@@ -342,7 +343,7 @@ export class BuildMenu extends LitElement implements Layer {
class="build-menu ${this._hidden ? "hidden" : ""}"
@contextmenu=${(e) => e.preventDefault()}
>
${buildTable.map(
${this.filteredBuildTable.map(
(row) => html`
<div class="build-row">
${row.map(
@@ -407,6 +408,26 @@ export class BuildMenu extends LitElement implements Layer {
this.playerActions = actions;
this.requestUpdate();
});
// removed disabled buildings from the buildtable
this.filteredBuildTable = this.getBuildableUnits();
}
private getBuildableUnits(): BuildItemDisplay[][] {
if (this.game?.config()?.disableNukes()) {
return buildTable.map((row) =>
row.filter(
(item) =>
![
UnitType.AtomBomb,
UnitType.MIRV,
UnitType.HydrogenBomb,
UnitType.MissileSilo,
].includes(item.unitType),
),
);
}
return buildTable;
}
get isVisible() {
+1
View File
@@ -108,6 +108,7 @@ const GameConfigSchema = z.object({
difficulty: z.nativeEnum(Difficulty),
gameType: z.nativeEnum(GameType),
disableNPCs: z.boolean(),
disableNukes: z.boolean(),
bots: z.number().int().min(0).max(400),
infiniteGold: z.boolean(),
infiniteTroops: z.boolean(),
+1
View File
@@ -55,6 +55,7 @@ export interface Config {
percentageTilesOwnedToWin(): number;
numBots(): number;
spawnNPCs(): boolean;
disableNukes(): boolean;
bots(): number;
infiniteGold(): boolean;
infiniteTroops(): boolean;
+3
View File
@@ -148,6 +148,9 @@ export class DefaultConfig implements Config {
spawnNPCs(): boolean {
return !this._gameConfig.disableNPCs;
}
disableNukes(): boolean {
return this._gameConfig.disableNukes;
}
bots(): number {
return this._gameConfig.bots;
}
+8 -6
View File
@@ -358,12 +358,14 @@ export class FakeHumanExecution implements Execution {
if (this.maybeSpawnWarship()) {
return;
}
this.maybeSpawnStructure(
UnitType.MissileSilo,
1,
(t) =>
new ConstructionExecution(this.player.id(), t, UnitType.MissileSilo),
);
if (!this.mg.config().disableNukes()) {
this.maybeSpawnStructure(
UnitType.MissileSilo,
1,
(t) =>
new ConstructionExecution(this.player.id(), t, UnitType.MissileSilo),
);
}
}
private maybeSpawnStructure(
+15
View File
@@ -673,6 +673,21 @@ export class PlayerImpl implements Player {
}
canBuild(unitType: UnitType, targetTile: TileRef): 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;
}
}
const cost = this.mg.unitInfo(unitType).cost(this);
if (!this.isAlive() || this.gold() < cost) {
return false;
+1
View File
@@ -34,6 +34,7 @@ export class GameManager {
gameType: GameType.Private,
difficulty: Difficulty.Medium,
disableNPCs: false,
disableNukes: false,
infiniteGold: false,
infiniteTroops: false,
instantBuild: false,
+3
View File
@@ -71,6 +71,9 @@ 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;
}
+1
View File
@@ -233,6 +233,7 @@ async function schedulePublicGame() {
infiniteTroops: false,
instantBuild: false,
disableNPCs: false,
disableNukes: false,
bots: 400,
} as GameConfig;
+1
View File
@@ -164,6 +164,7 @@ export function startWorker() {
instantBuild: req.body.instantBuild,
bots: req.body.bots,
disableNPCs: req.body.disableNPCs,
disableNukes: req.body.disableNukes,
});
res.status(200).json({ success: true });
}),