Lobby Gold Options (Starting Gold, Gold Multiplier) 💰 (#2915)

## Description:

We might want to add this to v29 to have a third possible public game
modifier from the beginning on 😄 Would be fun

- Add starting gold option (0 to 1_000_000_000 allowed, also applies to
nations)
- Add gold multiplier option (0.1 to 1000 allowed, also applies to
nations and bots)
- Add third public game modifier (3% chance of starting with 5M gold)
- Why 5M? It's enough gold to massively change the game start but not
enough to insta-hydro someone (launcher + hydro is 6M)

<img width="357" height="140" alt="image"
src="https://github.com/user-attachments/assets/72acc15c-e788-4e04-8590-ac72dd9657c7"
/>


## 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-01-16 10:20:35 -08:00
committed by evanpelle
parent d0fda1d535
commit 4e8454f3cc
11 changed files with 488 additions and 14 deletions
+3
View File
@@ -190,6 +190,7 @@ export const GameConfigSchema = z.object({
.object({
isCompact: z.boolean(),
isRandomSpawn: z.boolean(),
startingGold: z.number().int().min(0).optional(),
})
.optional(),
disableNations: z.boolean(),
@@ -204,6 +205,8 @@ export const GameConfigSchema = z.object({
spawnImmunityDuration: z.number().int().min(0).optional(), // In ticks
disabledUnits: z.enum(UnitType).array().optional(),
playerTeams: TeamCountConfigSchema.optional(),
goldMultiplier: z.number().min(0.1).max(1000).optional(),
startingGold: z.number().int().min(0).max(1000000000).optional(),
});
export const TeamSchema = z.string();
+2
View File
@@ -76,6 +76,8 @@ export interface Config {
numSpawnPhaseTurns(): number;
userSettings(): UserSettings;
playerTeams(): TeamCountConfig;
goldMultiplier(): number;
startingGold(playerInfo: PlayerInfo): Gold;
startManpower(playerInfo: PlayerInfo): number;
troopIncreaseRate(player: Player | PlayerView): number;
+26 -6
View File
@@ -245,6 +245,15 @@ export class DefaultConfig implements Config {
donateTroops(): boolean {
return this._gameConfig.donateTroops;
}
goldMultiplier(): number {
return this._gameConfig.goldMultiplier ?? 1;
}
startingGold(playerInfo: PlayerInfo): Gold {
if (playerInfo.playerType === PlayerType.Bot) {
return 0n;
}
return BigInt(this._gameConfig.startingGold ?? 0);
}
trainSpawnRate(numPlayerFactories: number): number {
// hyperbolic decay, midpoint at 10 factories
@@ -252,15 +261,21 @@ export class DefaultConfig implements Config {
return (numPlayerFactories + 10) * 18;
}
trainGold(rel: "self" | "team" | "ally" | "other"): Gold {
const multiplier = this.goldMultiplier();
let baseGold: bigint;
switch (rel) {
case "ally":
return 35_000n;
baseGold = 35_000n;
break;
case "team":
case "other":
return 25_000n;
baseGold = 25_000n;
break;
case "self":
return 10_000n;
baseGold = 10_000n;
break;
}
return BigInt(Math.floor(Number(baseGold) * multiplier));
}
trainStationMinRange(): number {
@@ -281,7 +296,8 @@ 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));
return BigInt(Math.floor(baseGold * bonus));
const multiplier = this.goldMultiplier();
return BigInt(Math.floor(baseGold * bonus * multiplier));
}
// Probability of trade ship spawn = 1 / tradeShipSpawnRate
@@ -791,10 +807,14 @@ export class DefaultConfig implements Config {
}
goldAdditionRate(player: Player): Gold {
const multiplier = this.goldMultiplier();
let baseRate: bigint;
if (player.type() === PlayerType.Bot) {
return 50n;
baseRate = 50n;
} else {
baseRate = 100n;
}
return 100n;
return BigInt(Math.floor(Number(baseRate) * multiplier));
}
nukeMagnitudes(unitType: UnitType): NukeMagnitude {
+1
View File
@@ -211,6 +211,7 @@ export enum GameMapSize {
export interface PublicGameModifiers {
isCompact: boolean;
isRandomSpawn: boolean;
startingGold?: number;
}
export interface UnitInfo {
+1 -1
View File
@@ -112,7 +112,7 @@ export class PlayerImpl implements Player {
) {
this._name = playerInfo.name;
this._troops = toInt(startTroops);
this._gold = 0n;
this._gold = mg.config().startingGold(playerInfo);
this._displayName = this._name;
this._pseudo_random = new PseudoRandom(simpleHash(this.playerInfo.id));
}