Only unit types buildable by player and some related changes

This commit is contained in:
VariableVince
2026-02-14 00:38:07 +01:00
parent 4586f66d7f
commit 2480ae7355
7 changed files with 45 additions and 27 deletions
+16 -5
View File
@@ -348,6 +348,7 @@ export class DefaultConfig implements Config {
return {
cost: () => 0n,
territoryBound: false,
playerBuildable: false,
};
case UnitType.Warship:
return {
@@ -356,18 +357,21 @@ export class DefaultConfig implements Config {
UnitType.Warship,
),
territoryBound: false,
playerBuildable: true,
maxHealth: 1000,
};
case UnitType.Shell:
return {
cost: () => 0n,
territoryBound: false,
playerBuildable: false,
damage: 250,
};
case UnitType.SAMMissile:
return {
cost: () => 0n,
territoryBound: false,
playerBuildable: false,
};
case UnitType.Port:
return {
@@ -378,19 +382,21 @@ export class DefaultConfig implements Config {
UnitType.Factory,
),
territoryBound: true,
playerBuildable: true,
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
upgradable: true,
canBuildTrainStation: true,
};
case UnitType.AtomBomb:
return {
cost: this.costWrapper(() => 750_000, UnitType.AtomBomb),
territoryBound: false,
playerBuildable: true,
};
case UnitType.HydrogenBomb:
return {
cost: this.costWrapper(() => 5_000_000, UnitType.HydrogenBomb),
territoryBound: false,
playerBuildable: true,
};
case UnitType.MIRV:
return {
@@ -401,21 +407,25 @@ export class DefaultConfig implements Config {
return 25_000_000n + game.stats().numMirvsLaunched() * 15_000_000n;
},
territoryBound: false,
playerBuildable: true,
};
case UnitType.MIRVWarhead:
return {
cost: () => 0n,
territoryBound: false,
playerBuildable: false,
};
case UnitType.TradeShip:
return {
cost: () => 0n,
territoryBound: false,
playerBuildable: true,
};
case UnitType.MissileSilo:
return {
cost: this.costWrapper(() => 1_000_000, UnitType.MissileSilo),
territoryBound: true,
playerBuildable: true,
constructionDuration: this.instantBuild() ? 0 : 10 * 10,
upgradable: true,
};
@@ -426,6 +436,7 @@ export class DefaultConfig implements Config {
UnitType.DefensePost,
),
territoryBound: true,
playerBuildable: true,
constructionDuration: this.instantBuild() ? 0 : 5 * 10,
};
case UnitType.SAMLauncher:
@@ -436,6 +447,7 @@ export class DefaultConfig implements Config {
UnitType.SAMLauncher,
),
territoryBound: true,
playerBuildable: true,
constructionDuration: this.instantBuild() ? 0 : 30 * 10,
upgradable: true,
};
@@ -447,9 +459,9 @@ export class DefaultConfig implements Config {
UnitType.City,
),
territoryBound: true,
playerBuildable: true,
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
upgradable: true,
canBuildTrainStation: true,
};
case UnitType.Factory:
return {
@@ -460,16 +472,15 @@ export class DefaultConfig implements Config {
UnitType.Port,
),
territoryBound: true,
playerBuildable: true,
constructionDuration: this.instantBuild() ? 0 : 2 * 10,
canBuildTrainStation: true,
experimental: true,
upgradable: true,
};
case UnitType.Train:
return {
cost: () => 0n,
territoryBound: false,
experimental: true,
playerBuildable: false,
};
default:
assertNever(type);
+1 -1
View File
@@ -119,7 +119,7 @@ export function listNukeBreakAlliance(
// Also check if any allied structures would be destroyed
game
.nearbyUnits(targetTile, magnitude.outer, game.getStructureTypes())
.nearbyUnits(targetTile, magnitude.outer, this.game.getStructureTypes())
.forEach(({ unit }) =>
playersToBreakAllianceWith.add(unit.owner().smallID()),
);
@@ -227,7 +227,7 @@ export class NationStructureBehavior {
const structures = this.player.units(type);
if (
this.getTotalStructureDensity() > UPGRADE_DENSITY_THRESHOLD &&
type !== UnitType.DefensePost
this.game.config().unitInfo(type).upgradable
) {
if (this.maybeUpgradeStructure(structures)) {
return true;
@@ -329,10 +329,7 @@ export class NationStructureBehavior {
return false;
}
const structureToUpgrade = this.findBestStructureToUpgrade(structures);
if (
structureToUpgrade !== null &&
this.player.canUpgradeUnit(structureToUpgrade)
) {
if (structureToUpgrade !== null) {
this.game.addExecution(
new UpgradeStructureExecution(this.player, structureToUpgrade.id()),
);
@@ -345,12 +342,10 @@ export class NationStructureBehavior {
* Calculates total structure density across player's territory.
*/
private getTotalStructureDensity(): number {
let totalStructures = 0;
for (const type of this.game.getStructureTypes()) {
totalStructures += this.player.units(type).length; // ignoring levels
}
const tilesOwned = this.player.numTilesOwned();
return tilesOwned > 0 ? totalStructures / tilesOwned : 0;
return tilesOwned > 0
? this.player.units(...this.game.getStructureTypes()).length / tilesOwned
: 0; //ignoring levels for structures
}
/**
+2 -3
View File
@@ -225,12 +225,11 @@ export interface UnitInfo {
cost: (game: Game, player: Player) => Gold;
// Determines if its owner changes when its tile is conquered.
territoryBound: boolean;
playerBuildable: boolean;
maxHealth?: number;
damage?: number;
constructionDuration?: number;
upgradable?: boolean;
canBuildTrainStation?: boolean;
experimental?: boolean;
}
export enum UnitType {
@@ -779,7 +778,7 @@ export interface Game extends GameMap {
predicate?: UnitPredicate,
includeUnderConstruction?: boolean,
): Array<{ unit: Unit; distSquared: number }>;
getStructureTypes(): UnitType[];
getStructureTypes(): readonly UnitType[];
isStructureType(type: UnitType): boolean;
addExecution(...exec: Execution[]): void;
+15 -4
View File
@@ -97,8 +97,9 @@ export class GameImpl implements Game {
private _miniWaterGraph: AbstractGraph | null = null;
private _miniWaterHPA: AStarWaterHierarchical | null = null;
private _structureTypes: UnitType[];
private _structureTypes: UnitType[] = [];
private _structureTypesSet: Set<UnitType>;
private _buildableUnitTypes: UnitType[] = [];
constructor(
private _humans: PlayerInfo[],
@@ -131,11 +132,17 @@ export class GameImpl implements Game {
);
}
this._structureTypes = Object.values(UnitType).filter(
(t) => this._config.unitInfo(t).territoryBound,
);
this._structureTypes =
Object.values(UnitType).filter(
(t) => this._config.unitInfo(t).territoryBound,
) ?? [];
this._structureTypesSet = new Set(this._structureTypes);
this._buildableUnitTypes =
Object.values(UnitType).filter(
(t) => this._config.unitInfo(t).playerBuildable,
) ?? [];
console.log(
`[GameImpl] Constructor total: ${(performance.now() - constructorStart).toFixed(0)}ms`,
);
@@ -925,6 +932,10 @@ export class GameImpl implements Game {
return this._structureTypesSet.has(type);
}
getPlayerBuildableUnitTypes(): UnitType[] {
return this._buildableUnitTypes;
}
ref(x: number, y: number): TileRef {
return this._map.ref(x, y);
}
+4 -3
View File
@@ -630,9 +630,10 @@ export class GameView implements GameMap {
} satisfies PlayerCosmetics);
}
this._structureTypes = Object.values(UnitType).filter(
(t) => this._config.unitInfo(t).territoryBound,
);
this._structureTypes =
Object.values(UnitType).filter(
(t) => this._config.unitInfo(t).territoryBound,
) ?? [];
this._structureTypesSet = new Set(this._structureTypes);
}
+2 -1
View File
@@ -967,7 +967,8 @@ export class PlayerImpl implements Player {
public buildableUnits(tile: TileRef | null): BuildableUnit[] {
const validTiles = tile !== null ? this.validStructureSpawnTiles(tile) : [];
return UNIT_TYPES.map((u) => {
return this.mg.getPlayerBuildableUnitTypes().map((u) => {
const cost = this.mg.config().unitInfo(u).cost(this.mg, this);
let canUpgrade: number | false = false;
let canBuild: TileRef | false = false;