diff --git a/resources/lang/en.json b/resources/lang/en.json index 0650ae6a31..c5254a1092 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -199,6 +199,10 @@ "options_changed_no_achievements": "Custom settings – achievements disabled", "gold_multiplier": "Gold multiplier", "gold_multiplier_placeholder": "2.0x", + "port_gold_multiplier": "Port trade gold multiplier", + "port_gold_multiplier_placeholder": "1.0x", + "factory_gold_multiplier": "Factory gold multiplier", + "factory_gold_multiplier_placeholder": "1.0x", "starting_gold": "Starting gold", "starting_gold_placeholder": "5000000" }, @@ -424,6 +428,10 @@ "teams_Quads": "Quads (teams of 4)", "teams_Humans Vs Nations": "Humans vs Nations", "starting_gold": "Starting gold", + "port_gold_multiplier": "Port trade gold multiplier", + "port_gold_multiplier_placeholder": "1.0x", + "factory_gold_multiplier": "Factory gold multiplier", + "factory_gold_multiplier_placeholder": "1.0x", "crowded": "Crowded modifier" }, "team_colors": { diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts index 71e661842e..92a662b31c 100644 --- a/src/client/HostLobbyModal.ts +++ b/src/client/HostLobbyModal.ts @@ -67,6 +67,10 @@ export class HostLobbyModal extends BaseModal { @state() private compactMap: boolean = false; @state() private goldMultiplier: boolean = false; @state() private goldMultiplierValue: number | undefined = undefined; + @state() private portGoldMultiplier: boolean = false; + @state() private portGoldMultiplierValue: number | undefined = undefined; + @state() private factoryGoldMultiplier: boolean = false; + @state() private factoryGoldMultiplierValue: number | undefined = undefined; @state() private startingGold: boolean = false; @state() private startingGoldValue: number | undefined = undefined; @state() private lobbyId = ""; @@ -188,6 +192,42 @@ export class HostLobbyModal extends BaseModal { .onChange=${this.handleGoldMultiplierValueChanges} .onKeyDown=${this.handleGoldMultiplierValueKeyDown} >`, + html``, + html``, html` { + this.portGoldMultiplier = checked; + this.portGoldMultiplierValue = toOptionalNumber(value); + this.putGameConfig(); + }; + + private handleFactoryGoldMultiplierToggle = ( + checked: boolean, + value: number | string | undefined, + ) => { + this.factoryGoldMultiplier = checked; + this.factoryGoldMultiplierValue = toOptionalNumber(value); + this.putGameConfig(); + }; + private handleStartingGoldToggle = ( checked: boolean, value: number | string | undefined, @@ -632,6 +694,14 @@ export class HostLobbyModal extends BaseModal { preventDisallowedKeys(e, ["+", "-", "e", "E"]); }; + private handlePortGoldMultiplierValueKeyDown = (e: KeyboardEvent) => { + preventDisallowedKeys(e, ["+", "-", "e", "E"]); + }; + + private handleFactoryGoldMultiplierValueKeyDown = (e: KeyboardEvent) => { + preventDisallowedKeys(e, ["+", "-", "e", "E"]); + }; + private handleGoldMultiplierValueChanges = (e: Event) => { const input = e.target as HTMLInputElement; const value = parseBoundedFloatFromInput(input, { min: 0.1, max: 1000 }); @@ -645,6 +715,32 @@ export class HostLobbyModal extends BaseModal { this.putGameConfig(); }; + private handlePortGoldMultiplierValueChanges = (e: Event) => { + const input = e.target as HTMLInputElement; + const value = parseBoundedFloatFromInput(input, { min: 0.1, max: 1000 }); + + if (value === undefined) { + this.portGoldMultiplierValue = undefined; + input.value = ""; + } else { + this.portGoldMultiplierValue = value; + } + this.putGameConfig(); + }; + + private handleFactoryGoldMultiplierValueChanges = (e: Event) => { + const input = e.target as HTMLInputElement; + const value = parseBoundedFloatFromInput(input, { min: 0.1, max: 1000 }); + + if (value === undefined) { + this.factoryGoldMultiplierValue = undefined; + input.value = ""; + } else { + this.factoryGoldMultiplierValue = value; + } + this.putGameConfig(); + }; + private handleStartingGoldValueKeyDown = (e: KeyboardEvent) => { preventDisallowedKeys(e, ["-", "+", "e", "E"]); }; @@ -775,6 +871,14 @@ export class HostLobbyModal extends BaseModal { this.goldMultiplier === true ? this.goldMultiplierValue : undefined, + portGoldMultiplier: + this.portGoldMultiplier === true + ? this.portGoldMultiplierValue + : undefined, + factoryGoldMultiplier: + this.factoryGoldMultiplier === true + ? this.factoryGoldMultiplierValue + : undefined, startingGold: this.startingGold === true ? this.startingGoldValue : undefined, } satisfies Partial, diff --git a/src/client/SinglePlayerModal.ts b/src/client/SinglePlayerModal.ts index 6a72726bc5..17ebba5ed7 100644 --- a/src/client/SinglePlayerModal.ts +++ b/src/client/SinglePlayerModal.ts @@ -53,6 +53,10 @@ const DEFAULT_OPTIONS = { teamCount: 2 as TeamCountConfig, goldMultiplier: false, goldMultiplierValue: undefined as number | undefined, + portGoldMultiplier: false, + portGoldMultiplierValue: undefined as number | undefined, + factoryGoldMultiplier: false, + factoryGoldMultiplierValue: undefined as number | undefined, startingGold: false, startingGoldValue: undefined as number | undefined, disabledUnits: [] as UnitType[], @@ -82,6 +86,14 @@ export class SinglePlayerModal extends BaseModal { @state() private goldMultiplier: boolean = DEFAULT_OPTIONS.goldMultiplier; @state() private goldMultiplierValue: number | undefined = DEFAULT_OPTIONS.goldMultiplierValue; + @state() private portGoldMultiplier: boolean = + DEFAULT_OPTIONS.portGoldMultiplier; + @state() private portGoldMultiplierValue: number | undefined = + DEFAULT_OPTIONS.portGoldMultiplierValue; + @state() private factoryGoldMultiplier: boolean = + DEFAULT_OPTIONS.factoryGoldMultiplier; + @state() private factoryGoldMultiplierValue: number | undefined = + DEFAULT_OPTIONS.factoryGoldMultiplierValue; @state() private startingGold: boolean = DEFAULT_OPTIONS.startingGold; @state() private startingGoldValue: number | undefined = DEFAULT_OPTIONS.startingGoldValue; @@ -200,6 +212,42 @@ export class SinglePlayerModal extends BaseModal { .onChange=${this.handleGoldMultiplierValueChanges} .onKeyDown=${this.handleGoldMultiplierValueKeyDown} >`, + html``, + html``, html` 0 ); @@ -402,6 +452,11 @@ export class SinglePlayerModal extends BaseModal { this.disabledUnits = [...DEFAULT_OPTIONS.disabledUnits]; this.goldMultiplier = DEFAULT_OPTIONS.goldMultiplier; this.goldMultiplierValue = DEFAULT_OPTIONS.goldMultiplierValue; + this.portGoldMultiplier = DEFAULT_OPTIONS.portGoldMultiplier; + this.portGoldMultiplierValue = DEFAULT_OPTIONS.portGoldMultiplierValue; + this.factoryGoldMultiplier = DEFAULT_OPTIONS.factoryGoldMultiplier; + this.factoryGoldMultiplierValue = + DEFAULT_OPTIONS.factoryGoldMultiplierValue; this.startingGold = DEFAULT_OPTIONS.startingGold; this.startingGoldValue = DEFAULT_OPTIONS.startingGoldValue; } @@ -514,6 +569,22 @@ export class SinglePlayerModal extends BaseModal { this.goldMultiplierValue = toOptionalNumber(value); }; + private handlePortGoldMultiplierToggle = ( + checked: boolean, + value: number | string | undefined, + ) => { + this.portGoldMultiplier = checked; + this.portGoldMultiplierValue = toOptionalNumber(value); + }; + + private handleFactoryGoldMultiplierToggle = ( + checked: boolean, + value: number | string | undefined, + ) => { + this.factoryGoldMultiplier = checked; + this.factoryGoldMultiplierValue = toOptionalNumber(value); + }; + private handleStartingGoldToggle = ( checked: boolean, value: number | string | undefined, @@ -550,6 +621,14 @@ export class SinglePlayerModal extends BaseModal { preventDisallowedKeys(e, ["+", "-", "e", "E"]); }; + private handlePortGoldMultiplierValueKeyDown = (e: KeyboardEvent) => { + preventDisallowedKeys(e, ["+", "-", "e", "E"]); + }; + + private handleFactoryGoldMultiplierValueKeyDown = (e: KeyboardEvent) => { + preventDisallowedKeys(e, ["+", "-", "e", "E"]); + }; + private handleGoldMultiplierValueChanges = (e: Event) => { const input = e.target as HTMLInputElement; const value = parseBoundedFloatFromInput(input, { min: 0.1, max: 1000 }); @@ -562,6 +641,30 @@ export class SinglePlayerModal extends BaseModal { } }; + private handlePortGoldMultiplierValueChanges = (e: Event) => { + const input = e.target as HTMLInputElement; + const value = parseBoundedFloatFromInput(input, { min: 0.1, max: 1000 }); + + if (value === undefined) { + this.portGoldMultiplierValue = undefined; + input.value = ""; + } else { + this.portGoldMultiplierValue = value; + } + }; + + private handleFactoryGoldMultiplierValueChanges = (e: Event) => { + const input = e.target as HTMLInputElement; + const value = parseBoundedFloatFromInput(input, { min: 0.1, max: 1000 }); + + if (value === undefined) { + this.factoryGoldMultiplierValue = undefined; + input.value = ""; + } else { + this.factoryGoldMultiplierValue = value; + } + }; + private handleStartingGoldValueKeyDown = (e: KeyboardEvent) => { preventDisallowedKeys(e, ["-", "+", "e", "E"]); }; @@ -689,6 +792,12 @@ export class SinglePlayerModal extends BaseModal { ...(this.goldMultiplier && this.goldMultiplierValue ? { goldMultiplier: this.goldMultiplierValue } : {}), + ...(this.portGoldMultiplier && this.portGoldMultiplierValue + ? { portGoldMultiplier: this.portGoldMultiplierValue } + : {}), + ...(this.factoryGoldMultiplier && this.factoryGoldMultiplierValue + ? { factoryGoldMultiplier: this.factoryGoldMultiplierValue } + : {}), ...(this.startingGold && this.startingGoldValue !== undefined ? { startingGold: this.startingGoldValue } : {}), diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index 0f4f2ebcb2..523719f448 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -228,6 +228,8 @@ export const GameConfigSchema = z.object({ disabledUnits: z.enum(UnitType).array().optional(), playerTeams: TeamCountConfigSchema.optional(), goldMultiplier: z.number().min(0.1).max(1000).optional(), + portGoldMultiplier: z.number().min(0.1).max(1000).optional(), + factoryGoldMultiplier: z.number().min(0.1).max(1000).optional(), startingGold: z.number().int().min(0).max(1000000000).optional(), }); diff --git a/src/core/configuration/Config.ts b/src/core/configuration/Config.ts index 8595b3a211..33ddef1aa9 100644 --- a/src/core/configuration/Config.ts +++ b/src/core/configuration/Config.ts @@ -78,6 +78,8 @@ export interface Config { userSettings(): UserSettings; playerTeams(): TeamCountConfig; goldMultiplier(): number; + portGoldMultiplier(): number; + factoryGoldMultiplier(): number; startingGold(playerInfo: PlayerInfo): Gold; startManpower(playerInfo: PlayerInfo): number; diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index 5a672f296e..1287f9b5a6 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -254,6 +254,12 @@ export class DefaultConfig implements Config { goldMultiplier(): number { return this._gameConfig.goldMultiplier ?? 1; } + portGoldMultiplier(): number { + return this._gameConfig.portGoldMultiplier ?? 1; + } + factoryGoldMultiplier(): number { + return this._gameConfig.factoryGoldMultiplier ?? 1; + } startingGold(playerInfo: PlayerInfo): Gold { if (playerInfo.playerType === PlayerType.Bot) { return 0n; @@ -267,7 +273,7 @@ export class DefaultConfig implements Config { return (numPlayerFactories + 10) * 18; } trainGold(rel: "self" | "team" | "ally" | "other"): Gold { - const multiplier = this.goldMultiplier(); + const multiplier = this.goldMultiplier() * this.factoryGoldMultiplier(); let baseGold: bigint; switch (rel) { case "ally": @@ -302,7 +308,7 @@ export class DefaultConfig implements Config { const numPortBonus = numPorts - 1; // Hyperbolic decay, midpoint at 5 ports, 3x bonus max. const bonus = 1 + 2 * (numPortBonus / (numPortBonus + 5)); - const multiplier = this.goldMultiplier(); + const multiplier = this.goldMultiplier() * this.portGoldMultiplier(); return BigInt(Math.floor(baseGold * bonus * multiplier)); } diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index 341ece7bb5..cc1355cc8f 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -152,6 +152,12 @@ export class GameServer { if (gameConfig.goldMultiplier !== undefined) { this.gameConfig.goldMultiplier = gameConfig.goldMultiplier; } + if (gameConfig.portGoldMultiplier !== undefined) { + this.gameConfig.portGoldMultiplier = gameConfig.portGoldMultiplier; + } + if (gameConfig.factoryGoldMultiplier !== undefined) { + this.gameConfig.factoryGoldMultiplier = gameConfig.factoryGoldMultiplier; + } if (gameConfig.startingGold !== undefined) { this.gameConfig.startingGold = gameConfig.startingGold; }