From 55bc2a1ae34442d9e4da90343f8f4af80977d341 Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Tue, 18 Nov 2025 23:06:00 +0100 Subject: [PATCH 1/8] Add per-level health and damage configuration for Fighter Jets --- src/client/graphics/layers/UILayer.ts | 4 +++ src/core/configuration/Config.ts | 4 +++ src/core/configuration/DefaultConfig.ts | 34 +++++++++++++++++++++++ src/core/execution/FighterJetExecution.ts | 7 +++++ src/core/execution/ShellExecution.ts | 13 +++++++-- 5 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index 0f1eaa861..376bde3e9 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -306,6 +306,10 @@ export class UILayer implements Layer { if (typeof maxHealth === "number") { maxHealth = maxHealth + (lvl - 1) * 1000; } + } else if (unit.type() === UnitType.FighterJet) { + // Fighter Jet: per-level max health from config + const lvl = unit.level ? unit.level() : 1; + maxHealth = this.game.config().fighterJetLevelMaxHealth(lvl); } if (maxHealth === undefined || this.context === null) { return; diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index 3aa59d325..7dd117dd9 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -225,6 +225,10 @@ export interface Config { fighterJetTargetReachedDistance(): number; fighterJetDogfightDistance(): number; fighterJetMinDogfightDistance(): number; + // Fighter Jet: per-level max health + fighterJetLevelMaxHealth(level: number): number; + // Fighter Jet: per-level damage range (inclusive) + fighterJetDamageRange(level: number): { min: number; max: number }; warshipAARange(): number; warshipAACooldown(): number; warshipAAScanInterval(): number; diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index a26371f23..af1bee15f 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -481,6 +481,40 @@ export class DefaultConfig implements Config { return 10; } + // Fighter Jet per-level stats + fighterJetLevelMaxHealth(level: number): number { + const lvl = Math.max(1, Math.min(4, Math.floor(level))); + switch (lvl) { + case 1: + return 750; // default + case 2: + return 1000; + case 3: + return 1250; + case 4: + return 1500; + default: + return 750; + } + } + + fighterJetDamageRange(level: number): { min: number; max: number } { + const lvl = Math.max(1, Math.min(4, Math.floor(level))); + switch (lvl) { + case 1: + // Level 1 fighter damage + return { min: 200, max: 325 }; + case 2: + return { min: 300, max: 425 }; + case 3: + return { min: 400, max: 525 }; + case 4: + return { min: 500, max: 625 }; + default: + return { min: 200, max: 325 }; + } + } + // Paratroopers/Air attack paratrooperMaxNumber(): number { return 3; diff --git a/src/core/execution/FighterJetExecution.ts b/src/core/execution/FighterJetExecution.ts index 07124f871..27d7ecd29 100644 --- a/src/core/execution/FighterJetExecution.ts +++ b/src/core/execution/FighterJetExecution.ts @@ -46,6 +46,13 @@ export class FighterJetExecution implements Execution { const lvl = Math.max(1, this.desiredLevel | 0); if (lvl > 1) { (this.fighterJet as any)._level = lvl; + // Apply per-level max health using config + const base = + this.mg.config().unitInfo(UnitType.FighterJet).maxHealth ?? 750; + const desired = this.mg.config().fighterJetLevelMaxHealth(lvl); + const bonus = Math.max(0, desired - base); + (this.fighterJet as any)._bonusMaxHealth = bonus; + (this.fighterJet as any)._health = BigInt(desired); this.mg.addUpdate(this.fighterJet.toUpdate()); } } diff --git a/src/core/execution/ShellExecution.ts b/src/core/execution/ShellExecution.ts index e23e8e8f1..36d97b842 100644 --- a/src/core/execution/ShellExecution.ts +++ b/src/core/execution/ShellExecution.ts @@ -70,12 +70,21 @@ export class ShellExecution implements Execution { } private effectOnTarget(): number { + // Fighter Jets use per-level configurable damage ranges + if (this.ownerUnit.type() === UnitType.FighterJet) { + const level = this.ownerUnit.level ? this.ownerUnit.level() : 1; + const range = this.mg.config().fighterJetDamageRange(level); + // Use 6-step discrete spread between min and max (inclusive) + const roll = this.random.nextInt(0, 5); // 0..5 + const step = (range.max - range.min) / 5; + return Math.round(range.min + roll * step); + } + + // Default: shell damage based on base value and 5-step multiplier const { damage } = this.mg.config().unitInfo(UnitType.Shell); const baseDamage = damage ?? 250; - const roll = this.random.nextInt(1, 6); const damageMultiplier = (roll - 1) * 25 + 200; - return Math.round((baseDamage / 250) * damageMultiplier); } From fa0a9b3c18b48ffdd4e950e4e5dca9467e3c4389 Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Tue, 18 Nov 2025 23:11:02 +0100 Subject: [PATCH 2/8] Add per-level health and damage configurations for Warships and Submarines --- src/client/graphics/layers/UILayer.ts | 6 ++++ src/core/configuration/Config.ts | 8 +++++ src/core/configuration/DefaultConfig.ts | 40 ++++++++++++++++++++++++ src/core/execution/ShellExecution.ts | 12 +++++++ src/core/execution/SubmarineExecution.ts | 7 +++++ src/core/execution/WarshipExecution.ts | 7 +++++ 6 files changed, 80 insertions(+) diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index 376bde3e9..9ac46051d 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -310,6 +310,12 @@ export class UILayer implements Layer { // Fighter Jet: per-level max health from config const lvl = unit.level ? unit.level() : 1; maxHealth = this.game.config().fighterJetLevelMaxHealth(lvl); + } else if (unit.type() === UnitType.Warship) { + const lvl = unit.level ? unit.level() : 1; + maxHealth = this.game.config().warshipLevelMaxHealth(lvl); + } else if (unit.type() === UnitType.Submarine) { + const lvl = unit.level ? unit.level() : 1; + maxHealth = this.game.config().submarineLevelMaxHealth(lvl); } if (maxHealth === undefined || this.context === null) { return; diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index 7dd117dd9..12047c32e 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -229,6 +229,14 @@ export interface Config { fighterJetLevelMaxHealth(level: number): number; // Fighter Jet: per-level damage range (inclusive) fighterJetDamageRange(level: number): { min: number; max: number }; + // Warship: per-level max health + warshipLevelMaxHealth(level: number): number; + // Warship: per-level damage range + warshipDamageRange(level: number): { min: number; max: number }; + // Submarine: per-level max health + submarineLevelMaxHealth(level: number): number; + // Submarine: per-level damage range + submarineDamageRange(level: number): { min: number; max: number }; warshipAARange(): number; warshipAACooldown(): number; warshipAAScanInterval(): number; diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index af1bee15f..b49763b07 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -515,6 +515,46 @@ export class DefaultConfig implements Config { } } + // Warship per-level stats + warshipLevelMaxHealth(level: number): number { + const lvl = Math.max(1, Math.min(3, Math.floor(level))); + switch (lvl) { + case 1: + return 1000; + case 2: + return 1250; + case 3: + return 1500; + default: + return 1000; + } + } + warshipDamageRange(level: number): { min: number; max: number } { + const lvl = Math.max(1, Math.min(3, Math.floor(level))); + const bonus = 70 * (lvl - 1); + return { min: 200 + bonus, max: 325 + bonus }; + } + + // Submarine per-level stats + submarineLevelMaxHealth(level: number): number { + const lvl = Math.max(1, Math.min(3, Math.floor(level))); + switch (lvl) { + case 1: + return 1000; + case 2: + return 1250; + case 3: + return 1500; + default: + return 1000; + } + } + submarineDamageRange(level: number): { min: number; max: number } { + const lvl = Math.max(1, Math.min(3, Math.floor(level))); + const bonus = 70 * (lvl - 1); + return { min: 200 + bonus, max: 325 + bonus }; + } + // Paratroopers/Air attack paratrooperMaxNumber(): number { return 3; diff --git a/src/core/execution/ShellExecution.ts b/src/core/execution/ShellExecution.ts index 36d97b842..3de99bd79 100644 --- a/src/core/execution/ShellExecution.ts +++ b/src/core/execution/ShellExecution.ts @@ -78,6 +78,18 @@ export class ShellExecution implements Execution { const roll = this.random.nextInt(0, 5); // 0..5 const step = (range.max - range.min) / 5; return Math.round(range.min + roll * step); + } else if (this.ownerUnit.type() === UnitType.Warship) { + const level = this.ownerUnit.level ? this.ownerUnit.level() : 1; + const range = this.mg.config().warshipDamageRange(level); + const roll = this.random.nextInt(0, 5); + const step = (range.max - range.min) / 5; + return Math.round(range.min + roll * step); + } else if (this.ownerUnit.type() === UnitType.Submarine) { + const level = this.ownerUnit.level ? this.ownerUnit.level() : 1; + const range = this.mg.config().submarineDamageRange(level); + const roll = this.random.nextInt(0, 5); + const step = (range.max - range.min) / 5; + return Math.round(range.min + roll * step); } // Default: shell damage based on base value and 5-step multiplier diff --git a/src/core/execution/SubmarineExecution.ts b/src/core/execution/SubmarineExecution.ts index f95e767e3..80475266d 100644 --- a/src/core/execution/SubmarineExecution.ts +++ b/src/core/execution/SubmarineExecution.ts @@ -50,6 +50,13 @@ export class SubmarineExecution implements Execution { const lvl = Math.max(1, this.desiredLevel | 0); if (lvl > 1) { (this.submarine as any)._level = lvl; + // Apply per-level max health boost + const base = + this.mg.config().unitInfo(UnitType.Submarine).maxHealth ?? 1000; + const desired = this.mg.config().submarineLevelMaxHealth(lvl); + const bonus = Math.max(0, desired - base); + (this.submarine as any)._bonusMaxHealth = bonus; + (this.submarine as any)._health = BigInt(desired); this.mg.addUpdate(this.submarine.toUpdate()); } } diff --git a/src/core/execution/WarshipExecution.ts b/src/core/execution/WarshipExecution.ts index ad3b56d8f..7e40ff7be 100644 --- a/src/core/execution/WarshipExecution.ts +++ b/src/core/execution/WarshipExecution.ts @@ -56,6 +56,13 @@ export class WarshipExecution implements Execution { const lvl = Math.max(1, this.desiredLevel | 0); if (lvl > 1) { (this.warship as any)._level = lvl; + // Apply per-level max health boost + const base = + this.mg.config().unitInfo(UnitType.Warship).maxHealth ?? 1000; + const desired = this.mg.config().warshipLevelMaxHealth(lvl); + const bonus = Math.max(0, desired - base); + (this.warship as any)._bonusMaxHealth = bonus; + (this.warship as any)._health = BigInt(desired); this.mg.addUpdate(this.warship.toUpdate()); } } From 1761c1c23979287c596c79b97716171352251182 Mon Sep 17 00:00:00 2001 From: 1brucben <1benjbruce@gmail.com> Date: Tue, 18 Nov 2025 23:25:49 +0100 Subject: [PATCH 3/8] Add 'Research all techs' option to game configuration --- resources/lang/en.json | 2 ++ src/client/HostLobbyModal.ts | 26 ++++++++++++++++++++++++++ src/client/SinglePlayerModal.ts | 21 +++++++++++++++++++++ src/core/GameRunner.ts | 11 +++++++++++ src/core/Schemas.ts | 2 ++ src/server/GameManager.ts | 1 + src/server/GameServer.ts | 3 +++ 7 files changed, 66 insertions(+) diff --git a/resources/lang/en.json b/resources/lang/en.json index 7c12086a9..37934c1a0 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -127,6 +127,7 @@ "disable_nations": "Disable Nations", "instant_build": "Instant Build", "instant_research": "Instant Research", + "research_all_techs": "Research All Techs", "infinite_gold": "Infinite Gold", "infinite_troops": "Infinite Troops", "disable_nukes": "Disable Nukes", @@ -209,6 +210,7 @@ "disable_nations": "Disable Nations", "instant_build": "Instant Build", "instant_research": "Instant Research", + "research_all_techs": "Research All Techs", "infinite_gold": "Infinite Gold", "infinite_troops": "Infinite Troops", "peace_timer": "Protected Start", diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts index f450f89a1..26cac9adf 100644 --- a/src/client/HostLobbyModal.ts +++ b/src/client/HostLobbyModal.ts @@ -54,6 +54,7 @@ export class HostLobbyModal extends LitElement { @state() private infiniteTroops: boolean = false; @state() private instantBuild: boolean = false; @state() private instantResearchHumanOnly: boolean = false; + @state() private researchAllTechs: boolean = false; @state() private lobbyId = ""; @state() private copySuccess = false; @state() private clients: ClientInfo[] = []; @@ -377,6 +378,27 @@ export class HostLobbyModal extends LitElement { + + + +