Starting gold input in millions with decimal support (#3349)

## 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

<img width="215" height="139" alt="image"
src="https://github.com/user-attachments/assets/00ce5b6d-f74d-4aee-92f5-c9be1a0a6d3d"
/>
<img width="292" height="74" alt="image"
src="https://github.com/user-attachments/assets/4de936a3-22bd-4ffc-8dbe-0d5066f28186"
/>

Now

<img width="216" height="151" alt="image"
src="https://github.com/user-attachments/assets/489de13e-65b5-4b02-a654-5f6f74b165d1"
/>
<img width="292" height="72" alt="image"
src="https://github.com/user-attachments/assets/51723d5a-55ab-4b7b-bbce-011a586eeb44"
/>

## 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
This commit is contained in:
FloPinguin
2026-03-06 05:48:03 +01:00
committed by GitHub
parent 6254cc0598
commit c594487f5e
5 changed files with 50 additions and 27 deletions
+2 -2
View File
@@ -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...",
+18 -11
View File
@@ -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}
></toggle-input-card>`,
];
@@ -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<GameConfig>,
},
bubbles: true,
+3 -2
View File
@@ -432,9 +432,10 @@ export class JoinLobbyModal extends BaseModal {
(m) => html`
<lobby-config-item
.label=${translateText(m.labelKey)}
.value=${m.value !== undefined
.value=${m.formattedValue ??
(m.value !== undefined
? renderNumber(m.value)
: translateText("common.enabled")}
: translateText("common.enabled"))}
></lobby-config-item>
`,
)}
+20 -11
View File
@@ -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}
></toggle-input-card>`,
];
@@ -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
+7 -1
View File
@@ -115,6 +115,8 @@ export interface ModifierInfo {
badgeParams?: Record<string, string | number>;
/** 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;