Production buildings are now independent private-sector agents: they keep what their output sells for, run only to the demand they can actually sell, and spend that cash on steel and high-tech upgrades when their demand grows. Grid power is a paid private flow between plants and their consumers, and regions synthesise the food their own harvest and the reachable market cannot supply. Island regions no longer famine: a region with no reachable supplier grows its whole shortfall from energy, and grid power now feeds cities and food before the converters, so a famine never waits on a factory. Bundles the in-progress tree cleanup that was already present in the working tree.
86 lines
3.2 KiB
JavaScript
86 lines
3.2 KiB
JavaScript
// Supply: encirclement and siege detection. A tile is cut off when enemy units
|
|
// sit on every land tile beside it and it has no working port to bring supplies
|
|
// in by sea; a port is itself blockaded when enemy warships hold every sea tile
|
|
// around its city.
|
|
|
|
import { key } from "../hex.js";
|
|
import { STATUS_ENCIRCLED, STATUS_BESIEGED } from "../data/statuses.js";
|
|
|
|
export const statusMethods = {
|
|
// Recomputes the status of every unit and city. A unit cut off is encircled;
|
|
// a city cut off is besieged. The check is local -- the ring of land tiles
|
|
// around the occupant and its own port -- so it never walks the whole map.
|
|
_refreshStatuses() {
|
|
for (const unit of this.units) {
|
|
unit.statuses = this._isEncircled(unit.coords, unit.civ) ? [STATUS_ENCIRCLED] : [];
|
|
}
|
|
for (const city of this.cities) {
|
|
city.statuses = this._isEncircled(city.coords, city.civ) ? [STATUS_BESIEGED] : [];
|
|
}
|
|
},
|
|
|
|
// Whether a hostile unit stands next to `coords`. Kept as the cheap trigger
|
|
// for callers that only care whether a fight is near.
|
|
_hasAdjacentHostile(coords, civ) {
|
|
for (const neighbour of this._neighbours(coords)) {
|
|
if (this.unitsAt(neighbour).some((unit) => unit.civ !== civ)) return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
// A tile is encircled when no enemy warship can break the ring: it has no
|
|
// working port, and every land tile beside it is held by an enemy unit. A
|
|
// tile with no land neighbours has no ring to close, so it is not encircled.
|
|
_isEncircled(coords, civ) {
|
|
if (this._hasSeaSupply(coords, civ)) return false;
|
|
return this._landRingBlocked(coords, civ);
|
|
},
|
|
|
|
_landRingBlocked(coords, civ) {
|
|
let land = 0;
|
|
for (const neighbour of this._neighbours(coords)) {
|
|
const tile = this.tiles[key(neighbour.x, neighbour.y)];
|
|
if (!tile || tile.terrainClass !== "Land") continue;
|
|
land += 1;
|
|
if (!this.unitsAt(neighbour).some((unit) => unit.civ !== civ)) return false;
|
|
}
|
|
return land > 0;
|
|
},
|
|
|
|
// A friendly city's port supplies its own tile by sea, unless the port is
|
|
// blockaded.
|
|
_hasSeaSupply(coords, civ) {
|
|
const city = this.cityAt(coords);
|
|
if (!city || city.civ !== civ) return false;
|
|
if (!this.hasCityBuilding(city, "port")) return false;
|
|
return !this._isPortBlockaded(city);
|
|
},
|
|
|
|
// A port is blockaded when enemy warships hold every sea tile around the city.
|
|
_isPortBlockaded(city) {
|
|
let sea = 0;
|
|
let held = 0;
|
|
for (const neighbour of this._neighbours(city.coords)) {
|
|
const tile = this.tiles[key(neighbour.x, neighbour.y)];
|
|
if (!tile || tile.terrainClass !== "Sea") continue;
|
|
sea += 1;
|
|
if (this.unitsAt(neighbour).some(
|
|
(unit) => unit.civ !== city.civ && this._isNaval(unit))) {
|
|
held += 1;
|
|
}
|
|
}
|
|
return sea > 0 && held === sea;
|
|
},
|
|
|
|
_isNaval(unit) {
|
|
const proto = this.unitProto(unit);
|
|
// Aircraft may fly over the sea, but only a hull counts as naval supply.
|
|
return !!(proto && !proto.air && (proto.traversableTerrains || []).includes("Sea"));
|
|
},
|
|
|
|
_statusHpLossPerDay(def, inCity) {
|
|
if (inCity && typeof def.cityHpLossPerDay === "number") return def.cityHpLossPerDay;
|
|
return typeof def.hpLossPerDay === "number" ? def.hpLossPerDay : 0;
|
|
},
|
|
};
|