mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-18 09:14:09 +00:00
Add host cheats for streamers (Specifically Enzo) ⭐ (#3671)
## Description: - Adds a "Host Cheats" toggle in the private lobby options section that reveals a dedicated section with four host-only cheats: infinite gold, infinite troops, gold multiplier, and starting gold - Only the lobby creator receives the cheat effects in-game (checked via `isLobbyCreator` in DefaultConfig) - Joining players see active host cheats displayed as yellow badges in the lobby UI - Adds `hostCheats` optional object to `GameConfigSchema` and wires it through the server config update whitelist - Raises the intent size limit for `update_game_config` messages (lobby-only, not stored in turn history) to prevent rate-limiter kicks (I always got too-much-data-kicked after selecting "host cheats" lol) <img width="861" height="525" alt="image" src="https://github.com/user-attachments/assets/51e51ec4-c2e8-46ca-b258-11a93487964f" /> <img width="933" height="825" alt="image" src="https://github.com/user-attachments/assets/5acbd38d-2097-42e1-ba78-0fb17d6afe82" /> ## 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:
@@ -128,7 +128,7 @@ export interface Config {
|
||||
defaultDonationAmount(sender: Player): number;
|
||||
unitInfo(type: UnitType): UnitInfo;
|
||||
tradeShipShortRangeDebuff(): number;
|
||||
tradeShipGold(dist: number): Gold;
|
||||
tradeShipGold(dist: number, player: Player | PlayerView): Gold;
|
||||
tradeShipSpawnRate(
|
||||
tradeShipSpawnRejections: number,
|
||||
numTradeShips: number,
|
||||
@@ -136,6 +136,7 @@ export interface Config {
|
||||
trainGold(
|
||||
rel: "self" | "team" | "ally" | "other",
|
||||
citiesVisited: number,
|
||||
player: Player | PlayerView,
|
||||
): Gold;
|
||||
trainSpawnRate(numPlayerFactories: number): number;
|
||||
trainStationMinRange(): number;
|
||||
|
||||
@@ -271,7 +271,7 @@ export class DefaultConfig implements Config {
|
||||
if (playerInfo.playerType === PlayerType.Bot) {
|
||||
return 0n;
|
||||
}
|
||||
return BigInt(this._gameConfig.startingGold ?? 0);
|
||||
return this.startingGoldFor(playerInfo);
|
||||
}
|
||||
|
||||
trainSpawnRate(numPlayerFactories: number): number {
|
||||
@@ -282,6 +282,7 @@ export class DefaultConfig implements Config {
|
||||
trainGold(
|
||||
rel: "self" | "team" | "ally" | "other",
|
||||
citiesVisited: number,
|
||||
player: Player | PlayerView,
|
||||
): Gold {
|
||||
// No penalty for the first 10 cities.
|
||||
citiesVisited = Math.max(0, citiesVisited - 9);
|
||||
@@ -300,7 +301,7 @@ export class DefaultConfig implements Config {
|
||||
}
|
||||
const distPenalty = citiesVisited * 5_000;
|
||||
const gold = Math.max(5000, baseGold - distPenalty);
|
||||
return toInt(gold * this.goldMultiplier());
|
||||
return toInt(gold * this.goldMultiplierFor(player));
|
||||
}
|
||||
|
||||
trainStationMinRange(): number {
|
||||
@@ -313,13 +314,12 @@ export class DefaultConfig implements Config {
|
||||
return 120;
|
||||
}
|
||||
|
||||
tradeShipGold(dist: number): Gold {
|
||||
tradeShipGold(dist: number, player: Player | PlayerView): Gold {
|
||||
// Sigmoid: concave start, sharp S-curve middle, linear end - heavily punishes trades under range debuff.
|
||||
const debuff = this.tradeShipShortRangeDebuff();
|
||||
const baseGold =
|
||||
75_000 / (1 + Math.exp(-0.03 * (dist - debuff))) + 50 * dist;
|
||||
const multiplier = this.goldMultiplier();
|
||||
return BigInt(Math.floor(baseGold * multiplier));
|
||||
return BigInt(Math.floor(baseGold * this.goldMultiplierFor(player)));
|
||||
}
|
||||
|
||||
// Probability of trade ship spawn = 1 / tradeShipSpawnRate
|
||||
@@ -396,7 +396,10 @@ export class DefaultConfig implements Config {
|
||||
case UnitType.MIRV:
|
||||
info = {
|
||||
cost: (game: Game, player: Player) => {
|
||||
if (player.type() === PlayerType.Human && this.infiniteGold()) {
|
||||
if (
|
||||
player.type() === PlayerType.Human &&
|
||||
this.hasInfiniteGoldFor(player)
|
||||
) {
|
||||
return 0n;
|
||||
}
|
||||
return 25_000_000n + game.stats().numMirvsLaunched() * 15_000_000n;
|
||||
@@ -478,12 +481,55 @@ export class DefaultConfig implements Config {
|
||||
return info;
|
||||
}
|
||||
|
||||
private hasInfiniteGoldFor(player: Player | PlayerView): boolean {
|
||||
if (this.infiniteGold()) return true;
|
||||
const hc = this._gameConfig.hostCheats;
|
||||
return (hc?.infiniteGold ?? false) && player.isLobbyCreator();
|
||||
}
|
||||
|
||||
private hasInfiniteTroopsFor(player: Player | PlayerView): boolean {
|
||||
if (this.infiniteTroops()) return true;
|
||||
return (
|
||||
(this._gameConfig.hostCheats?.infiniteTroops ?? false) &&
|
||||
player.isLobbyCreator()
|
||||
);
|
||||
}
|
||||
|
||||
private hasInfiniteTroopsForInfo(playerInfo: PlayerInfo): boolean {
|
||||
if (this.infiniteTroops()) return true;
|
||||
return (
|
||||
(this._gameConfig.hostCheats?.infiniteTroops ?? false) &&
|
||||
playerInfo.isLobbyCreator
|
||||
);
|
||||
}
|
||||
|
||||
private goldMultiplierFor(player: Player | PlayerView): number {
|
||||
const base = this.goldMultiplier();
|
||||
const hc = this._gameConfig.hostCheats;
|
||||
if (hc?.goldMultiplier && player.isLobbyCreator()) {
|
||||
return hc.goldMultiplier;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
private startingGoldFor(playerInfo: PlayerInfo): Gold {
|
||||
const base = BigInt(this._gameConfig.startingGold ?? 0);
|
||||
const hc = this._gameConfig.hostCheats;
|
||||
if (hc?.startingGold && playerInfo.isLobbyCreator) {
|
||||
return base + BigInt(hc.startingGold);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
private costWrapper(
|
||||
costFn: (units: number) => number,
|
||||
...types: UnitType[]
|
||||
): (g: Game, p: Player) => bigint {
|
||||
return (game: Game, player: Player) => {
|
||||
if (player.type() === PlayerType.Human && this.infiniteGold()) {
|
||||
if (
|
||||
player.type() === PlayerType.Human &&
|
||||
this.hasInfiniteGoldFor(player)
|
||||
) {
|
||||
return 0n;
|
||||
}
|
||||
const numUnits = types.reduce(
|
||||
@@ -761,12 +807,12 @@ export class DefaultConfig implements Config {
|
||||
assertNever(this._gameConfig.difficulty);
|
||||
}
|
||||
}
|
||||
return this.infiniteTroops() ? 1_000_000 : 25_000;
|
||||
return this.hasInfiniteTroopsForInfo(playerInfo) ? 1_000_000 : 25_000;
|
||||
}
|
||||
|
||||
maxTroops(player: Player | PlayerView): number {
|
||||
const maxTroops =
|
||||
player.type() === PlayerType.Human && this.infiniteTroops()
|
||||
player.type() === PlayerType.Human && this.hasInfiniteTroopsFor(player)
|
||||
? 1_000_000_000
|
||||
: 2 * (Math.pow(player.numTilesOwned(), 0.6) * 1000 + 50000) +
|
||||
player
|
||||
@@ -833,7 +879,7 @@ export class DefaultConfig implements Config {
|
||||
}
|
||||
|
||||
goldAdditionRate(player: Player): Gold {
|
||||
const multiplier = this.goldMultiplier();
|
||||
const multiplier = this.goldMultiplierFor(player);
|
||||
let baseRate: bigint;
|
||||
if (player.type() === PlayerType.Bot) {
|
||||
baseRate = 50n;
|
||||
|
||||
Reference in New Issue
Block a user