diff --git a/src/core/configuration/DefaultConfig.ts b/src/core/configuration/DefaultConfig.ts index 274b72530..7efc9b66a 100644 --- a/src/core/configuration/DefaultConfig.ts +++ b/src/core/configuration/DefaultConfig.ts @@ -584,11 +584,7 @@ export class DefaultConfig implements Config { } maxPopulation(player: Player | PlayerView): number { - const maxPop = - player.type() == PlayerType.Human && this.infiniteTroops() - ? 1_000_000_000 - : 1 * (player.numTilesOwned() * 30 + 50000) + - player.units(UnitType.City).length * this.cityPopulationIncrease(); + const maxPop = player.maxPopulation(); if (player.type() == PlayerType.Bot) { return maxPop / 2; diff --git a/src/core/execution/CityExecution.ts b/src/core/execution/CityExecution.ts index 5bf2ca7e4..0b0267f13 100644 --- a/src/core/execution/CityExecution.ts +++ b/src/core/execution/CityExecution.ts @@ -38,7 +38,9 @@ export class CityExecution implements Execution { this.active = false; return; } - this.city = this.player.buildUnit(UnitType.City, 0, spawnTile); + this.city = this.player.buildUnit(UnitType.City, 0, spawnTile, { + createdAtTick: ticks, + }); } if (!this.city.isActive()) { this.active = false; diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 7dd3d7270..a376dfe84 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -272,6 +272,7 @@ export interface UnitSpecificInfos { detonationDst?: TileRef; // Only for nukes warshipTarget?: Unit; cooldownDuration?: number; + createdAtTick?: number; // Only for cities } export interface Unit { @@ -305,6 +306,7 @@ export interface Unit { setSafeFromPirates(): void; // Only for trade ships isSafeFromPirates(): boolean; // Only for trade ships detonationDst(): TileRef; // Only for nukes + createdAtTick(): number; // Only for cities setMoveTarget(cell: TileRef): void; moveTarget(): TileRef | null; @@ -364,6 +366,7 @@ export interface Player { gold(): Gold; population(): number; adjustedPopulation(): number; + maxPopulation(): number; workers(): number; troops(): number; targetTroopRatio(): number; diff --git a/src/core/game/GameUpdates.ts b/src/core/game/GameUpdates.ts index 4507ca8b2..0579d601c 100644 --- a/src/core/game/GameUpdates.ts +++ b/src/core/game/GameUpdates.ts @@ -74,6 +74,7 @@ export interface UnitUpdate { health?: number; constructionType?: UnitType; ticksLeftInCooldown?: Tick; + createdAtTick?: number; } export interface AttackUpdate { @@ -100,6 +101,7 @@ export interface PlayerUpdate { gold: number; population: number; adjustedPopulation: number; + maxPopulation: number; workers: number; troops: number; targetTroopRatio: number; diff --git a/src/core/game/GameView.ts b/src/core/game/GameView.ts index 27eebfc0a..c3bea956a 100644 --- a/src/core/game/GameView.ts +++ b/src/core/game/GameView.ts @@ -79,6 +79,10 @@ export class UnitView { troops(): number { return this.data.troops; } + createdAtTick(): number | undefined { + return this.data.createdAtTick; + } + tile(): TileRef { return this.data.pos; } @@ -228,6 +232,9 @@ export class PlayerView { adjustedPopulation(): number { return this.data.adjustedPopulation; } + maxPopulation(): number { + return this.data.maxPopulation; + } workers(): number { return this.data.workers; } diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index ebd6c63d9..39be52c36 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -138,6 +138,7 @@ export class PlayerImpl implements Player { gold: Number(this._gold), population: this.population(), adjustedPopulation: this.adjustedPopulation(), + maxPopulation: this.maxPopulation(), workers: this.workers(), troops: this.troops(), targetTroopRatio: this.targetTroopRatio(), @@ -643,6 +644,20 @@ export class PlayerImpl implements Player { adjustedPopulation(): number { return this.population() + this.boatTroops() + this.attackingTroops(); } + maxPopulation(): number { + let cityPop = 0; + for (const city of this.units(UnitType.City)) { + const created = city.createdAtTick(); + const age = created != null ? this.mg.ticks() - created : Infinity; + const ramp = Math.min(age / 600, 1); // 60 seconds at 10 ticks/sec + cityPop += ramp * 500_000; + } + + const base = this.numTilesOwned() * 30 + 50_000 + cityPop; + + return base; + } + private attackingTroops(): number { return this._outgoingAttacks .filter((a) => a.isActive()) diff --git a/src/core/game/UnitImpl.ts b/src/core/game/UnitImpl.ts index 247e0eced..adf7c2ec5 100644 --- a/src/core/game/UnitImpl.ts +++ b/src/core/game/UnitImpl.ts @@ -29,6 +29,7 @@ export class UnitImpl implements Unit { private _detonationDst: TileRef | null = null; // Only for nukes private _warshipTarget: Unit | null = null; private _cooldownDuration: number | null = null; + private _createdAtTick: number; constructor( private _type: UnitType, @@ -49,6 +50,8 @@ export class UnitImpl implements Unit { this._safeFromPiratesCooldown = this.mg .config() .safeFromPiratesCooldownMax(); + + this._createdAtTick = unitsSpecificInfos.createdAtTick; } id() { @@ -73,6 +76,7 @@ export class UnitImpl implements Unit { warshipTargetId: warshipTarget ? warshipTarget.id() : null, detonationDst: this.detonationDst(), ticksLeftInCooldown: this.ticksLeftInCooldown(this._cooldownDuration), + createdAtTick: this._createdAtTick, }; } @@ -233,6 +237,9 @@ export class UnitImpl implements Unit { setTargetedBySAM(targeted: boolean): void { this._targetedBySAM = targeted; } + createdAtTick(): number | undefined { + return this._createdAtTick; + } targetedBySAM(): boolean { return this._targetedBySAM;