From c594487f5ea1bf6df2e8fc6c48c2896a11dcafc8 Mon Sep 17 00:00:00 2001 From: FloPinguin <25036848+FloPinguin@users.noreply.github.com> Date: Fri, 6 Mar 2026 05:48:03 +0100 Subject: [PATCH] =?UTF-8?q?Starting=20gold=20input=20in=20millions=20with?= =?UTF-8?q?=20decimal=20support=20=E2=9C=A8=20(#3349)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description: **Starting gold input: use millions** Changes the starting gold input in singleplayer and host lobby modals to accept values in millions (e.g. enter `5` for 5M gold). Supports decimals like `6.6` for 6.6M. The value is multiplied by 1,000,000 before being sent to the game config. - Label updated to "Starting Gold (Millions)" - Input uses float parsing with min 0.1, matching gold multiplier behavior - JoinLobbyModal shows clean values without unnecessary decimals (e.g. "5M" not "5.00M") Previous image image Now image image ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin --- resources/lang/en.json | 4 ++-- src/client/HostLobbyModal.ts | 29 ++++++++++++++++++----------- src/client/JoinLobbyModal.ts | 5 +++-- src/client/SinglePlayerModal.ts | 31 ++++++++++++++++++++----------- src/client/Utils.ts | 8 +++++++- 5 files changed, 50 insertions(+), 27 deletions(-) diff --git a/resources/lang/en.json b/resources/lang/en.json index df702d507..60754c189 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -201,8 +201,8 @@ "options_changed_no_achievements": "Custom settings – achievements disabled", "gold_multiplier": "Gold multiplier", "gold_multiplier_placeholder": "2.0x", - "starting_gold": "Starting gold", - "starting_gold_placeholder": "5000000" + "starting_gold": "Starting Gold (Millions)", + "starting_gold_placeholder": "5" }, "token_login_modal": { "title": "Logging in...", diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts index f82ad5b8e..f55e869f9 100644 --- a/src/client/HostLobbyModal.ts +++ b/src/client/HostLobbyModal.ts @@ -195,18 +195,18 @@ export class HostLobbyModal extends BaseModal { .labelKey=${"single_modal.starting_gold"} .checked=${this.startingGold} .inputId=${"starting-gold-value"} - .inputMin=${0} - .inputMax=${1000000000} - .inputStep=${100000} + .inputMin=${0.1} + .inputMax=${1000} + .inputStep=${"any"} .inputValue=${this.startingGoldValue} .inputAriaLabel=${translateText("single_modal.starting_gold")} .inputPlaceholder=${translateText( "single_modal.starting_gold_placeholder", )} - .defaultInputValue=${5000000} - .minValidOnEnable=${0} + .defaultInputValue=${5} + .minValidOnEnable=${0.1} .onToggle=${this.handleStartingGoldToggle} - .onInput=${this.handleStartingGoldValueChanges} + .onChange=${this.handleStartingGoldValueChanges} .onKeyDown=${this.handleStartingGoldValueKeyDown} >`, ]; @@ -650,12 +650,17 @@ export class HostLobbyModal extends BaseModal { private handleStartingGoldValueChanges = (e: Event) => { const input = e.target as HTMLInputElement; - const value = parseBoundedIntegerFromInput(input, { - min: 0, - max: 1000000000, + const value = parseBoundedFloatFromInput(input, { + min: 0.1, + max: 1000, }); - this.startingGoldValue = value; + if (value === undefined) { + this.startingGoldValue = undefined; + input.value = ""; + } else { + this.startingGoldValue = value; + } this.putGameConfig(); }; @@ -787,7 +792,9 @@ export class HostLobbyModal extends BaseModal { ? this.goldMultiplierValue : undefined, startingGold: - this.startingGold === true ? this.startingGoldValue : undefined, + this.startingGold === true && this.startingGoldValue !== undefined + ? Math.round(this.startingGoldValue * 1_000_000) + : undefined, } satisfies Partial, }, bubbles: true, diff --git a/src/client/JoinLobbyModal.ts b/src/client/JoinLobbyModal.ts index 892fe7537..7834045b7 100644 --- a/src/client/JoinLobbyModal.ts +++ b/src/client/JoinLobbyModal.ts @@ -432,9 +432,10 @@ export class JoinLobbyModal extends BaseModal { (m) => html` `, )} diff --git a/src/client/SinglePlayerModal.ts b/src/client/SinglePlayerModal.ts index ba7508107..ce4d2ec3d 100644 --- a/src/client/SinglePlayerModal.ts +++ b/src/client/SinglePlayerModal.ts @@ -210,18 +210,18 @@ export class SinglePlayerModal extends BaseModal { .labelKey=${"single_modal.starting_gold"} .checked=${this.startingGold} .inputId=${"starting-gold-value"} - .inputMin=${0} - .inputMax=${1000000000} - .inputStep=${100000} + .inputMin=${0.1} + .inputMax=${1000} + .inputStep=${"any"} .inputValue=${this.startingGoldValue} .inputAriaLabel=${translateText("single_modal.starting_gold")} .inputPlaceholder=${translateText( "single_modal.starting_gold_placeholder", )} - .defaultInputValue=${5000000} - .minValidOnEnable=${0} + .defaultInputValue=${5} + .minValidOnEnable=${0.1} .onToggle=${this.handleStartingGoldToggle} - .onInput=${this.handleStartingGoldValueChanges} + .onChange=${this.handleStartingGoldValueChanges} .onKeyDown=${this.handleStartingGoldValueKeyDown} >`, ]; @@ -591,12 +591,17 @@ export class SinglePlayerModal extends BaseModal { private handleStartingGoldValueChanges = (e: Event) => { const input = e.target as HTMLInputElement; - const value = parseBoundedIntegerFromInput(input, { - min: 0, - max: 1000000000, + const value = parseBoundedFloatFromInput(input, { + min: 0.1, + max: 1000, }); - this.startingGoldValue = value; + if (value === undefined) { + this.startingGoldValue = undefined; + input.value = ""; + } else { + this.startingGoldValue = value; + } }; private handleGameModeSelection(value: GameMode) { @@ -685,7 +690,11 @@ export class SinglePlayerModal extends BaseModal { ? { goldMultiplier: this.goldMultiplierValue } : {}), ...(this.startingGold && this.startingGoldValue !== undefined - ? { startingGold: this.startingGoldValue } + ? { + startingGold: Math.round( + this.startingGoldValue * 1_000_000, + ), + } : {}), }, lobbyCreatedAt: Date.now(), // ms; server should be authoritative in MP diff --git a/src/client/Utils.ts b/src/client/Utils.ts index 92b106a38..7f394b564 100644 --- a/src/client/Utils.ts +++ b/src/client/Utils.ts @@ -115,6 +115,8 @@ export interface ModifierInfo { badgeParams?: Record; /** The raw value if applicable (e.g. startingGold amount) */ value?: number; + /** Pre-formatted display string (used instead of renderNumber when provided) */ + formattedValue?: string; } /** @@ -150,13 +152,17 @@ export function getActiveModifiers( }); } if (modifiers.startingGold) { + const millions = parseFloat( + (modifiers.startingGold / 1_000_000).toPrecision(12), + ); result.push({ labelKey: "host_modal.starting_gold", badgeKey: "public_game_modifier.starting_gold", badgeParams: { - amount: Math.round(modifiers.startingGold / 1_000_000), + amount: millions, }, value: modifiers.startingGold, + formattedValue: `${millions}M`, }); } return result;