mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-18 09:14:09 +00:00
Merge main into v0.25.x
This commit is contained in:
@@ -76,9 +76,7 @@ export class ColorAllocator {
|
||||
case ColoredTeams.Bot:
|
||||
return botColor;
|
||||
default:
|
||||
return this.availableColors[
|
||||
simpleHash(team) % this.availableColors.length
|
||||
];
|
||||
return this.assignColor(team);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Colord } from "colord";
|
||||
import { JWK } from "jose";
|
||||
import { GameConfig, GameID, TeamCountConfig } from "../Schemas";
|
||||
import {
|
||||
Difficulty,
|
||||
Game,
|
||||
@@ -18,6 +17,8 @@ import {
|
||||
import { GameMap, TileRef } from "../game/GameMap";
|
||||
import { PlayerView } from "../game/GameView";
|
||||
import { UserSettings } from "../game/UserSettings";
|
||||
import { GameConfig, GameID, TeamCountConfig } from "../Schemas";
|
||||
import { NukeType } from "../StatsSchemas";
|
||||
|
||||
export enum GameEnv {
|
||||
Dev,
|
||||
@@ -48,8 +49,7 @@ export interface ServerConfig {
|
||||
r2AccessKey(): string;
|
||||
r2SecretKey(): string;
|
||||
otelEndpoint(): string;
|
||||
otelUsername(): string;
|
||||
otelPassword(): string;
|
||||
otelAuthHeader(): string;
|
||||
otelEnabled(): boolean;
|
||||
jwtAudience(): string;
|
||||
jwtIssuer(): string;
|
||||
@@ -153,10 +153,17 @@ export interface Config {
|
||||
traitorDefenseDebuff(): number;
|
||||
traitorDuration(): number;
|
||||
nukeMagnitudes(unitType: UnitType): NukeMagnitude;
|
||||
// Number of tiles destroyed to break an alliance
|
||||
nukeAllianceBreakThreshold(): number;
|
||||
defaultNukeSpeed(): number;
|
||||
defaultNukeTargetableRange(): number;
|
||||
defaultSamRange(): number;
|
||||
nukeDeathFactor(humans: number, tilesOwned: number): number;
|
||||
nukeDeathFactor(
|
||||
nukeType: NukeType,
|
||||
humans: number,
|
||||
tilesOwned: number,
|
||||
maxPop: number,
|
||||
): number;
|
||||
structureMinDist(): number;
|
||||
isReplay(): boolean;
|
||||
allianceExtensionPromptOffset(): number;
|
||||
|
||||
@@ -23,6 +23,7 @@ import { TileRef } from "../game/GameMap";
|
||||
import { PlayerView } from "../game/GameView";
|
||||
import { UserSettings } from "../game/UserSettings";
|
||||
import { GameConfig, GameID, TeamCountConfig } from "../Schemas";
|
||||
import { NukeType } from "../StatsSchemas";
|
||||
import { assertNever, simpleHash, within } from "../Util";
|
||||
import { Config, GameEnv, NukeMagnitude, ServerConfig, Theme } from "./Config";
|
||||
import { PastelTheme } from "./PastelTheme";
|
||||
@@ -119,19 +120,16 @@ export abstract class DefaultServerConfig implements ServerConfig {
|
||||
}
|
||||
otelEnabled(): boolean {
|
||||
return (
|
||||
this.env() !== GameEnv.Dev &&
|
||||
Boolean(this.otelEndpoint()) &&
|
||||
Boolean(this.otelUsername()) &&
|
||||
Boolean(this.otelPassword())
|
||||
Boolean(this.otelAuthHeader())
|
||||
);
|
||||
}
|
||||
otelEndpoint(): string {
|
||||
return process.env.OTEL_ENDPOINT ?? "";
|
||||
return process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "";
|
||||
}
|
||||
otelUsername(): string {
|
||||
return process.env.OTEL_USERNAME ?? "";
|
||||
}
|
||||
otelPassword(): string {
|
||||
return process.env.OTEL_PASSWORD ?? "";
|
||||
otelAuthHeader(): string {
|
||||
return process.env.OTEL_AUTH_HEADER ?? "";
|
||||
}
|
||||
gitCommit(): string {
|
||||
return process.env.GIT_COMMIT ?? "";
|
||||
@@ -236,7 +234,7 @@ export class DefaultConfig implements Config {
|
||||
return 0.5;
|
||||
}
|
||||
traitorSpeedDebuff(): number {
|
||||
return 0.6;
|
||||
return 0.8;
|
||||
}
|
||||
traitorDuration(): number {
|
||||
return 30 * 10; // 30 seconds
|
||||
@@ -342,9 +340,9 @@ export class DefaultConfig implements Config {
|
||||
}
|
||||
|
||||
tradeShipGold(dist: number, numPorts: number): Gold {
|
||||
const baseGold = Math.floor(50000 + 130 * dist);
|
||||
const basePortBonus = 0.2;
|
||||
const diminishingFactor = 0.95;
|
||||
const baseGold = Math.floor(50000 + 100 * dist);
|
||||
const basePortBonus = 0.25;
|
||||
const diminishingFactor = 0.9;
|
||||
|
||||
let totalMultiplier = 1;
|
||||
for (let i = 0; i < numPorts; i++) {
|
||||
@@ -361,7 +359,7 @@ export class DefaultConfig implements Config {
|
||||
}
|
||||
if (numTradeShips <= 150) {
|
||||
const additional = numTradeShips - 20;
|
||||
return Math.pow(additional, 0.8) + 5;
|
||||
return Math.floor(Math.pow(additional, 0.85) + 5);
|
||||
}
|
||||
return 1_000_000;
|
||||
}
|
||||
@@ -375,15 +373,9 @@ export class DefaultConfig implements Config {
|
||||
};
|
||||
case UnitType.Warship:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0n
|
||||
: BigInt(
|
||||
Math.min(
|
||||
1_000_000,
|
||||
(p.unitsOwned(UnitType.Warship) + 1) * 250_000,
|
||||
),
|
||||
),
|
||||
cost: this.costWrapper(UnitType.Warship, (numUnits: number) =>
|
||||
Math.min(1_000_000, (numUnits + 1) * 250_000),
|
||||
),
|
||||
territoryBound: false,
|
||||
maxHealth: 1000,
|
||||
};
|
||||
@@ -400,15 +392,9 @@ export class DefaultConfig implements Config {
|
||||
};
|
||||
case UnitType.Port:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0n
|
||||
: BigInt(
|
||||
Math.min(
|
||||
1_000_000,
|
||||
Math.pow(2, p.unitsConstructed(UnitType.Port)) * 125_000,
|
||||
),
|
||||
),
|
||||
cost: this.costWrapper(UnitType.Port, (numUnits: number) =>
|
||||
Math.min(1_000_000, Math.pow(2, numUnits) * 125_000),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
|
||||
upgradable: true,
|
||||
@@ -416,26 +402,17 @@ export class DefaultConfig implements Config {
|
||||
};
|
||||
case UnitType.AtomBomb:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0n
|
||||
: 750_000n,
|
||||
cost: this.costWrapper(UnitType.AtomBomb, () => 750_000),
|
||||
territoryBound: false,
|
||||
};
|
||||
case UnitType.HydrogenBomb:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0n
|
||||
: 5_000_000n,
|
||||
cost: this.costWrapper(UnitType.HydrogenBomb, () => 5_000_000),
|
||||
territoryBound: false,
|
||||
};
|
||||
case UnitType.MIRV:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0n
|
||||
: 25_000_000n,
|
||||
cost: this.costWrapper(UnitType.MIRV, () => 35_000_000),
|
||||
territoryBound: false,
|
||||
};
|
||||
case UnitType.MIRVWarhead:
|
||||
@@ -450,54 +427,33 @@ export class DefaultConfig implements Config {
|
||||
};
|
||||
case UnitType.MissileSilo:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0n
|
||||
: 1_000_000n,
|
||||
cost: this.costWrapper(UnitType.MissileSilo, () => 1_000_000),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 10 * 10,
|
||||
upgradable: true,
|
||||
};
|
||||
case UnitType.DefensePost:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0n
|
||||
: BigInt(
|
||||
Math.min(
|
||||
250_000,
|
||||
(p.unitsConstructed(UnitType.DefensePost) + 1) * 50_000,
|
||||
),
|
||||
),
|
||||
cost: this.costWrapper(UnitType.DefensePost, (numUnits: number) =>
|
||||
Math.min(250_000, (numUnits + 1) * 50_000),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 5 * 10,
|
||||
};
|
||||
case UnitType.SAMLauncher:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0n
|
||||
: BigInt(
|
||||
Math.min(
|
||||
3_000_000,
|
||||
(p.unitsConstructed(UnitType.SAMLauncher) + 1) * 1_500_000,
|
||||
),
|
||||
),
|
||||
cost: this.costWrapper(UnitType.SAMLauncher, (numUnits: number) =>
|
||||
Math.min(3_000_000, (numUnits + 1) * 1_500_000),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 30 * 10,
|
||||
upgradable: true,
|
||||
};
|
||||
case UnitType.City:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0n
|
||||
: BigInt(
|
||||
Math.min(
|
||||
1_000_000,
|
||||
Math.pow(2, p.unitsConstructed(UnitType.City)) * 125_000,
|
||||
),
|
||||
),
|
||||
cost: this.costWrapper(UnitType.City, (numUnits: number) =>
|
||||
Math.min(1_000_000, Math.pow(2, numUnits) * 125_000),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
|
||||
upgradable: true,
|
||||
@@ -505,15 +461,9 @@ export class DefaultConfig implements Config {
|
||||
};
|
||||
case UnitType.Factory:
|
||||
return {
|
||||
cost: (p: Player) =>
|
||||
p.type() === PlayerType.Human && this.infiniteGold()
|
||||
? 0n
|
||||
: BigInt(
|
||||
Math.min(
|
||||
1_000_000,
|
||||
Math.pow(2, p.unitsConstructed(UnitType.Factory)) * 125_000,
|
||||
),
|
||||
),
|
||||
cost: this.costWrapper(UnitType.Factory, (numUnits: number) =>
|
||||
Math.min(1_000_000, Math.pow(2, numUnits) * 125_000),
|
||||
),
|
||||
territoryBound: true,
|
||||
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
|
||||
canBuildTrainStation: true,
|
||||
@@ -535,6 +485,20 @@ export class DefaultConfig implements Config {
|
||||
assertNever(type);
|
||||
}
|
||||
}
|
||||
|
||||
private costWrapper(
|
||||
type: UnitType,
|
||||
costFn: (units: number) => number,
|
||||
): (p: Player) => bigint {
|
||||
return (p: Player) => {
|
||||
if (p.type() === PlayerType.Human && this.infiniteGold()) {
|
||||
return 0n;
|
||||
}
|
||||
const numUnits = Math.min(p.unitsOwned(type), p.unitsConstructed(type));
|
||||
return BigInt(costFn(numUnits));
|
||||
};
|
||||
}
|
||||
|
||||
defaultDonationAmount(sender: Player): number {
|
||||
return Math.floor(sender.troops() / 3);
|
||||
}
|
||||
@@ -557,7 +521,7 @@ export class DefaultConfig implements Config {
|
||||
return 30 * 10;
|
||||
}
|
||||
allianceDuration(): Tick {
|
||||
return 600 * 10; // 10 minutes.
|
||||
return 300 * 10; // 5 minutes.
|
||||
}
|
||||
temporaryEmbargoDuration(): Tick {
|
||||
return 300 * 10; // 5 minutes.
|
||||
@@ -788,7 +752,7 @@ export class DefaultConfig implements Config {
|
||||
toAdd *= ratio;
|
||||
|
||||
if (player.type() === PlayerType.Bot) {
|
||||
toAdd *= 0.7;
|
||||
toAdd *= 0.6;
|
||||
}
|
||||
|
||||
if (player.type() === PlayerType.FakeHuman) {
|
||||
@@ -842,6 +806,10 @@ export class DefaultConfig implements Config {
|
||||
throw new Error(`Unknown nuke type: ${unitType}`);
|
||||
}
|
||||
|
||||
nukeAllianceBreakThreshold(): number {
|
||||
return 100;
|
||||
}
|
||||
|
||||
defaultNukeSpeed(): number {
|
||||
return 6;
|
||||
}
|
||||
@@ -855,8 +823,21 @@ export class DefaultConfig implements Config {
|
||||
}
|
||||
|
||||
// Humans can be population, soldiers attacking, soldiers in boat etc.
|
||||
nukeDeathFactor(humans: number, tilesOwned: number): number {
|
||||
return (5 * humans) / Math.max(1, tilesOwned);
|
||||
nukeDeathFactor(
|
||||
nukeType: NukeType,
|
||||
humans: number,
|
||||
tilesOwned: number,
|
||||
maxPop: number,
|
||||
): number {
|
||||
if (nukeType !== UnitType.MIRVWarhead) {
|
||||
return (5 * humans) / Math.max(1, tilesOwned);
|
||||
}
|
||||
|
||||
const targetPop = 0.05 * maxPop;
|
||||
const excessPop = Math.max(0, humans - targetPop);
|
||||
const scalingFactor = 20000;
|
||||
|
||||
return (scalingFactor * excessPop * excessPop) / (maxPop * maxPop);
|
||||
}
|
||||
|
||||
structureMinDist(): number {
|
||||
|
||||
Reference in New Issue
Block a user