mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-23 21:41:19 +00:00
Prevent AI from placing ports on small lakes 🚢 (#4429)
## Description: AI nations were placing ports on small decorative ponds scattered across maps (Missisipi for example), wasting structure slots on strategically useless water bodies. This fix adds a water component size check to the port placement logic so the AI skips lakes that are too small for meaningful port use. We already had a check for available trade partners, but trading in small lakes is usually stupid. **How it works:** - `ConnectedComponents` now tracks component sizes during its existing flood-fill (zero extra cost - counts tiles as they're visited) - `AbstractGraph`, `WaterManager`, and the `Game` interface expose `getWaterComponentSize(tile)` so callers can query the size of any water body - `NationStructureBehavior.randCoastalTileArray()` filters out non-ocean water components below `MIN_PORT_WATER_COMPONENT_SIZE` (3000 minimap tiles, ~12000 full-map tiles) - Ocean tiles bypass the check entirely since they're always large enough ## 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 ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin
This commit is contained in:
@@ -799,6 +799,12 @@ export interface Game extends GameMap {
|
||||
miniWaterGraph(): AbstractGraph | null;
|
||||
getWaterComponent(tile: TileRef): number | null;
|
||||
hasWaterComponent(tile: TileRef, component: number): boolean;
|
||||
/**
|
||||
* Returns the approximate number of water tiles in the component
|
||||
* containing `tile`, or null if the tile has no water component. Useful for
|
||||
* filtering tiny water bodies (e.g. preventing AI port placement on ponds).
|
||||
*/
|
||||
getWaterComponentSize(tile: TileRef): number | null;
|
||||
/**
|
||||
* Returns the set of water components that `player` shares with at least one
|
||||
* valid trade partner (cached). Used by nation AI for port-placement
|
||||
|
||||
@@ -1204,6 +1204,9 @@ export class GameImpl implements Game {
|
||||
hasWaterComponent(tile: TileRef, component: number): boolean {
|
||||
return this._waterManager.hasWaterComponent(tile, component);
|
||||
}
|
||||
getWaterComponentSize(tile: TileRef): number | null {
|
||||
return this._waterManager.getWaterComponentSize(tile);
|
||||
}
|
||||
sharedWaterComponents(player: Player): Set<number> | null {
|
||||
return this._sharedWaterCache.get(player);
|
||||
}
|
||||
|
||||
@@ -182,6 +182,21 @@ export class WaterManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the approximate number of water tiles in the component
|
||||
* containing `tile`, or null if the tile has no water component.
|
||||
*
|
||||
* The underlying ConnectedComponents are computed on the 2× downsampled
|
||||
* minimap, so each minimap tile represents up to 4 full-map tiles. We
|
||||
* multiply by 4 to give callers a value in full-map-tile units.
|
||||
*/
|
||||
getWaterComponentSize(tile: TileRef): number | null {
|
||||
const componentId = this.getWaterComponent(tile);
|
||||
if (componentId === null) return null;
|
||||
if (!this._miniWaterGraph) return 0;
|
||||
return this._miniWaterGraph.getComponentSize(componentId) * 4;
|
||||
}
|
||||
|
||||
private finalizeWaterChanges(
|
||||
convertedTiles: TileRef[],
|
||||
changedTiles: TileRef[],
|
||||
|
||||
Reference in New Issue
Block a user