|
4 | 4 | Player, |
5 | 5 | PlayerType, |
6 | 6 | Relation, |
| 7 | + StructureTypes, |
7 | 8 | Unit, |
8 | 9 | UnitType, |
9 | 10 | } from "../../game/Game"; |
@@ -49,7 +50,7 @@ const STRUCTURE_RATIOS: Partial<Record<UnitType, StructureRatioConfig>> = { |
49 | 50 | /** Perceived cost increase percentage per city owned */ |
50 | 51 | const CITY_PERCEIVED_COST_INCREASE_PER_OWNED = 1; |
51 | 52 |
|
52 | | -/** If we have more than this many structures per this tiles, prefer upgrading over building */ |
| 53 | +/** If we have more than this many total structures per 1000 tiles, prefer upgrading over building */ |
53 | 54 | const UPGRADE_DENSITY_THRESHOLD = 1 / 1000; |
54 | 55 |
|
55 | 56 | export class NationStructureBehavior { |
@@ -87,7 +88,7 @@ export class NationStructureBehavior { |
87 | 88 | } |
88 | 89 | } |
89 | 90 |
|
90 | | - if (this.maybeSpawnCity()) { |
| 91 | + if (this.maybeSpawnStructure(UnitType.City)) { |
91 | 92 | return true; |
92 | 93 | } |
93 | 94 |
|
@@ -132,50 +133,15 @@ export class NationStructureBehavior { |
132 | 133 | return this.game.unitInfo(type).cost(this.game, this.player); |
133 | 134 | } |
134 | 135 |
|
135 | | - private maybeSpawnCity(): boolean { |
136 | | - const perceivedCost = this.getPerceivedCost(UnitType.City); |
137 | | - if (this.player.gold() < perceivedCost) { |
138 | | - return false; |
139 | | - } |
140 | | - const tile = this.structureSpawnTile(UnitType.City); |
141 | | - if (tile === null) { |
142 | | - return false; |
143 | | - } |
144 | | - const canBuild = this.player.canBuild(UnitType.City, tile); |
145 | | - if (canBuild === false) { |
146 | | - return false; |
147 | | - } |
148 | | - this.game.addExecution( |
149 | | - new ConstructionExecution(this.player, UnitType.City, tile), |
150 | | - ); |
151 | | - return true; |
152 | | - } |
153 | | - |
154 | 136 | private maybeSpawnStructure(type: UnitType): boolean { |
155 | 137 | const perceivedCost = this.getPerceivedCost(type); |
156 | 138 | if (this.player.gold() < perceivedCost) { |
157 | 139 | return false; |
158 | 140 | } |
159 | 141 |
|
160 | 142 | // Check if we should upgrade instead of building new |
161 | | - const existingStructures = this.player.units(type); |
162 | | - const tilesOwned = this.player.numTilesOwned(); |
163 | | - const density = existingStructures.length / tilesOwned; |
164 | | - |
165 | | - if (density > UPGRADE_DENSITY_THRESHOLD && existingStructures.length > 0) { |
166 | | - // Try to upgrade an existing structure instead |
167 | | - const structureToUpgrade = |
168 | | - this.findBestStructureToUpgrade(existingStructures); |
169 | | - if ( |
170 | | - structureToUpgrade !== null && |
171 | | - this.player.canUpgradeUnit(structureToUpgrade) |
172 | | - ) { |
173 | | - this.game.addExecution( |
174 | | - new UpgradeStructureExecution(this.player, structureToUpgrade.id()), |
175 | | - ); |
176 | | - return true; |
177 | | - } |
178 | | - // Fall through to build new if we can't upgrade |
| 143 | + if (this.tryUpgradeInsteadOfBuilding(this.player.units(type))) { |
| 144 | + return true; |
179 | 145 | } |
180 | 146 |
|
181 | 147 | const tile = this.structureSpawnTile(type); |
@@ -222,6 +188,43 @@ export class NationStructureBehavior { |
222 | 188 | return BigInt(Math.ceil(Number(realCost) * multiplier)); |
223 | 189 | } |
224 | 190 |
|
| 191 | + /** |
| 192 | + * Tries to upgrade an existing structure if density threshold is exceeded. |
| 193 | + * @param structures The pool of structures to consider for upgrading |
| 194 | + * @returns true if an upgrade was initiated, false otherwise |
| 195 | + */ |
| 196 | + private tryUpgradeInsteadOfBuilding(structures: Unit[]): boolean { |
| 197 | + if (this.getTotalStructureDensity() <= UPGRADE_DENSITY_THRESHOLD) { |
| 198 | + return false; |
| 199 | + } |
| 200 | + if (structures.length === 0) { |
| 201 | + return false; |
| 202 | + } |
| 203 | + const structureToUpgrade = this.findBestStructureToUpgrade(structures); |
| 204 | + if ( |
| 205 | + structureToUpgrade !== null && |
| 206 | + this.player.canUpgradeUnit(structureToUpgrade) |
| 207 | + ) { |
| 208 | + this.game.addExecution( |
| 209 | + new UpgradeStructureExecution(this.player, structureToUpgrade.id()), |
| 210 | + ); |
| 211 | + return true; |
| 212 | + } |
| 213 | + return false; |
| 214 | + } |
| 215 | + |
| 216 | + /** |
| 217 | + * Calculates total structure density across player's territory. |
| 218 | + */ |
| 219 | + private getTotalStructureDensity(): number { |
| 220 | + let totalStructures = 0; |
| 221 | + for (const type of StructureTypes) { |
| 222 | + totalStructures += this.player.unitsOwned(type); |
| 223 | + } |
| 224 | + const tilesOwned = this.player.numTilesOwned(); |
| 225 | + return tilesOwned > 0 ? totalStructures / tilesOwned : 0; |
| 226 | + } |
| 227 | + |
225 | 228 | /** |
226 | 229 | * Finds the best structure to upgrade, preferring structures protected by a SAM. |
227 | 230 | */ |
|
0 commit comments