Refactor UnitSpecific info => AllUnitParams type union (#701)

## Description:

By using a type union we get better type safety, enforcing each unit
type have the appropriate params when initializing

## Please complete the following:

- [x] I have added screenshots for all UI updates
- [x] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

## Please put your Discord username so you can be contacted if a bug or
regression is found:
evan

---------

Co-authored-by: evan <openfrontio@gmail.com>
This commit is contained in:
evanpelle
2025-05-10 11:40:47 -07:00
committed by GitHub
co-authored by evan
parent 890972cb0c
commit 1d0732d3d9
19 changed files with 111 additions and 62 deletions
+50 -14
View File
@@ -148,6 +148,51 @@ export enum UnitType {
Construction = "Construction",
}
export interface UnitParamsMap {
[UnitType.TransportShip]: {
troops?: number;
destination?: TileRef;
};
[UnitType.Warship]: {};
[UnitType.Shell]: {};
[UnitType.SAMMissile]: {};
[UnitType.Port]: {};
[UnitType.AtomBomb]: {};
[UnitType.HydrogenBomb]: {};
[UnitType.TradeShip]: {
dstPort: Unit;
lastSetSafeFromPirates?: number;
};
[UnitType.MissileSilo]: {
cooldownDuration?: number;
};
[UnitType.DefensePost]: {};
[UnitType.SAMLauncher]: {};
[UnitType.City]: {};
[UnitType.MIRV]: {};
[UnitType.MIRVWarhead]: {};
[UnitType.Construction]: {};
}
// Type helper to get params type for a specific unit type
export type UnitParams<T extends UnitType> = UnitParamsMap[T];
export type AllUnitParams = UnitParamsMap[keyof UnitParamsMap];
export const nukeTypes = [
UnitType.AtomBomb,
UnitType.HydrogenBomb,
@@ -276,15 +321,6 @@ export class PlayerInfo {
}
}
// Some units have info specific to them
export interface UnitSpecificInfos {
dstPort?: Unit; // Only for trade ships
lastSetSafeFromPirates?: number; // Only for trade ships
detonationDst?: TileRef; // Only for nukes
warshipTarget?: Unit;
cooldownDuration?: number;
}
export interface Unit {
id(): number;
@@ -391,12 +427,12 @@ export interface Player {
unitsIncludingConstruction(type: UnitType): Unit[];
buildableUnits(tile: TileRef): BuildableUnit[];
canBuild(type: UnitType, targetTile: TileRef): TileRef | false;
buildUnit(
type: UnitType,
troops: number,
tile: TileRef,
unitSpecificInfos?: UnitSpecificInfos,
buildUnit<T extends UnitType>(
type: T,
spawnTile: TileRef,
params: UnitParams<T>,
): Unit;
captureUnit(unit: Unit): void;
// Relations & Diplomacy
+6 -8
View File
@@ -35,7 +35,7 @@ import {
TerraNullius,
Tick,
Unit,
UnitSpecificInfos,
UnitParams,
UnitType,
} from "./Game";
import { GameImpl } from "./GameImpl";
@@ -703,11 +703,10 @@ export class PlayerImpl implements Player {
);
}
buildUnit(
type: UnitType,
troops: number,
buildUnit<T extends UnitType>(
type: T,
spawnTile: TileRef,
unitSpecificInfos: UnitSpecificInfos = {},
params: UnitParams<T>,
): UnitImpl {
if (this.mg.config().isUnitDisabled(type)) {
throw new Error(
@@ -720,14 +719,13 @@ export class PlayerImpl implements Player {
type,
this.mg,
spawnTile,
troops,
this.mg.nextUnitID(),
this,
unitSpecificInfos,
params,
);
this._units.push(b);
this.removeGold(cost);
this.removeTroops(troops);
this.removeTroops("troops" in params ? params.troops : 0);
this.mg.addUpdate(b.toUpdate());
this.mg.addUnit(b);
+11 -9
View File
@@ -1,11 +1,11 @@
import { simpleHash, toInt, withinInt } from "../Util";
import {
AllUnitParams,
MessageType,
Player,
Tick,
Unit,
UnitInfo,
UnitSpecificInfos,
UnitType,
} from "./Game";
import { GameImpl } from "./GameImpl";
@@ -24,6 +24,7 @@ export class UnitImpl implements Unit {
private _lastSetSafeFromPirates: number; // Only for trade ships
private _constructionType: UnitType = undefined;
private _troops: number;
private _cooldownTick: Tick | null = null;
private _dstPort: Unit | null = null; // Only for trade ships
private _detonationDst: TileRef | null = null; // Only for nukes
@@ -34,21 +35,22 @@ export class UnitImpl implements Unit {
private _type: UnitType,
private mg: GameImpl,
private _tile: TileRef,
private _troops: number,
private _id: number,
public _owner: PlayerImpl,
unitsSpecificInfos: UnitSpecificInfos = {},
params: AllUnitParams = {},
) {
this._health = toInt(this.mg.unitInfo(_type).maxHealth ?? 1);
this._lastTile = _tile;
this._dstPort = unitsSpecificInfos.dstPort;
this._detonationDst = unitsSpecificInfos.detonationDst;
this._warshipTarget = unitsSpecificInfos.warshipTarget;
this._cooldownDuration = unitsSpecificInfos.cooldownDuration;
this._lastSetSafeFromPirates = unitsSpecificInfos.lastSetSafeFromPirates;
this._health = toInt(this.mg.unitInfo(_type).maxHealth ?? 1);
this._safeFromPiratesCooldown = this.mg
.config()
.safeFromPiratesCooldownMax();
this._troops = "troops" in params ? params.troops : 0;
this._dstPort = "dstPort" in params ? params.dstPort : null;
this._cooldownDuration =
"cooldownDuration" in params ? params.cooldownDuration : null;
this._lastSetSafeFromPirates =
"lastSetSafeFromPirates" in params ? params.lastSetSafeFromPirates : 0;
}
id() {