mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-26 19:54:36 +00:00
8925f48ba7
Fixes #483 ## 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: respectful pinguin
237 lines
5.8 KiB
TypeScript
237 lines
5.8 KiB
TypeScript
import { simpleHash, toInt, withinInt } from "../Util";
|
|
import {
|
|
MessageType,
|
|
Player,
|
|
Tick,
|
|
Unit,
|
|
UnitInfo,
|
|
UnitSpecificInfos,
|
|
UnitType,
|
|
} from "./Game";
|
|
import { GameImpl } from "./GameImpl";
|
|
import { TileRef } from "./GameMap";
|
|
import { GameUpdateType, UnitUpdate } from "./GameUpdates";
|
|
import { PlayerImpl } from "./PlayerImpl";
|
|
|
|
export class UnitImpl implements Unit {
|
|
private _active = true;
|
|
private _health: bigint;
|
|
private _lastTile: TileRef = null;
|
|
// Currently only warship use it
|
|
private _target: Unit = null;
|
|
private _moveTarget: TileRef = null;
|
|
private _targetedBySAM = false;
|
|
|
|
private _constructionType: UnitType = undefined;
|
|
|
|
private _cooldownTick: Tick | null = null;
|
|
private _dstPort: Unit | null = null; // Only for trade ships
|
|
private _detonationDst: TileRef | null = null; // Only for nukes
|
|
private _warshipTarget: Unit | null = null;
|
|
private _cooldownDuration: number | null = null;
|
|
|
|
constructor(
|
|
private _type: UnitType,
|
|
private mg: GameImpl,
|
|
private _tile: TileRef,
|
|
private _troops: number,
|
|
private _id: number,
|
|
public _owner: PlayerImpl,
|
|
unitsSpecificInfos: UnitSpecificInfos = {},
|
|
) {
|
|
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;
|
|
}
|
|
|
|
id() {
|
|
return this._id;
|
|
}
|
|
|
|
toUpdate(): UnitUpdate {
|
|
const warshipTarget = this.warshipTarget();
|
|
const dstPort = this.dstPort();
|
|
return {
|
|
type: GameUpdateType.Unit,
|
|
unitType: this._type,
|
|
id: this._id,
|
|
troops: this._troops,
|
|
ownerID: this._owner.smallID(),
|
|
isActive: this._active,
|
|
pos: this._tile,
|
|
lastPos: this._lastTile,
|
|
health: this.hasHealth() ? Number(this._health) : undefined,
|
|
constructionType: this._constructionType,
|
|
dstPortId: dstPort ? dstPort.id() : null,
|
|
warshipTargetId: warshipTarget ? warshipTarget.id() : null,
|
|
detonationDst: this.detonationDst(),
|
|
ticksLeftInCooldown: this.ticksLeftInCooldown(this._cooldownDuration),
|
|
};
|
|
}
|
|
|
|
type(): UnitType {
|
|
return this._type;
|
|
}
|
|
|
|
lastTile(): TileRef {
|
|
return this._lastTile;
|
|
}
|
|
|
|
move(tile: TileRef): void {
|
|
if (tile == null) {
|
|
throw new Error("tile cannot be null");
|
|
}
|
|
this._lastTile = this._tile;
|
|
this._tile = tile;
|
|
this.mg.removeUnit(this);
|
|
this.mg.addUnit(this);
|
|
this.mg.addUpdate(this.toUpdate());
|
|
}
|
|
setTroops(troops: number): void {
|
|
this._troops = troops;
|
|
}
|
|
troops(): number {
|
|
return this._troops;
|
|
}
|
|
health(): number {
|
|
return Number(this._health);
|
|
}
|
|
hasHealth(): boolean {
|
|
return this.info().maxHealth != undefined;
|
|
}
|
|
tile(): TileRef {
|
|
return this._tile;
|
|
}
|
|
owner(): PlayerImpl {
|
|
return this._owner;
|
|
}
|
|
|
|
info(): UnitInfo {
|
|
return this.mg.unitInfo(this._type);
|
|
}
|
|
|
|
setOwner(newOwner: Player): void {
|
|
const oldOwner = this._owner;
|
|
oldOwner._units = oldOwner._units.filter((u) => u != this);
|
|
this._owner = newOwner as PlayerImpl;
|
|
this.mg.addUpdate(this.toUpdate());
|
|
this.mg.displayMessage(
|
|
`Your ${this.type()} was captured by ${newOwner.displayName()}`,
|
|
MessageType.ERROR,
|
|
oldOwner.id(),
|
|
);
|
|
}
|
|
|
|
modifyHealth(delta: number): void {
|
|
this._health = withinInt(
|
|
this._health + toInt(delta),
|
|
0n,
|
|
toInt(this.info().maxHealth ?? 1),
|
|
);
|
|
}
|
|
|
|
delete(displayMessage: boolean = true): void {
|
|
if (!this.isActive()) {
|
|
throw new Error(`cannot delete ${this} not active`);
|
|
}
|
|
this._owner._units = this._owner._units.filter((b) => b != this);
|
|
this._active = false;
|
|
this.mg.addUpdate(this.toUpdate());
|
|
this.mg.removeUnit(this);
|
|
if (displayMessage && this.type() != UnitType.MIRVWarhead) {
|
|
this.mg.displayMessage(
|
|
`Your ${this.type()} was destroyed`,
|
|
MessageType.ERROR,
|
|
this.owner().id(),
|
|
);
|
|
}
|
|
}
|
|
isActive(): boolean {
|
|
return this._active;
|
|
}
|
|
|
|
constructionType(): UnitType | null {
|
|
if (this.type() != UnitType.Construction) {
|
|
throw new Error(`Cannot get construction type on ${this.type()}`);
|
|
}
|
|
return this._constructionType;
|
|
}
|
|
|
|
setConstructionType(type: UnitType): void {
|
|
if (this.type() != UnitType.Construction) {
|
|
throw new Error(`Cannot set construction type on ${this.type()}`);
|
|
}
|
|
this._constructionType = type;
|
|
this.mg.addUpdate(this.toUpdate());
|
|
}
|
|
|
|
hash(): number {
|
|
return this.tile() + simpleHash(this.type()) * this._id;
|
|
}
|
|
|
|
toString(): string {
|
|
return `Unit:${this._type},owner:${this.owner().name()}`;
|
|
}
|
|
|
|
setWarshipTarget(target: Unit) {
|
|
this._warshipTarget = target;
|
|
}
|
|
|
|
warshipTarget(): Unit {
|
|
return this._warshipTarget;
|
|
}
|
|
|
|
detonationDst(): TileRef {
|
|
return this._detonationDst;
|
|
}
|
|
|
|
dstPort(): Unit {
|
|
return this._dstPort;
|
|
}
|
|
|
|
// set the cooldown to the current tick or remove it
|
|
setCooldown(triggerCooldown: boolean): void {
|
|
if (triggerCooldown) {
|
|
this._cooldownTick = this.mg.ticks();
|
|
this.mg.addUpdate(this.toUpdate());
|
|
} else {
|
|
this._cooldownTick = null;
|
|
this.mg.addUpdate(this.toUpdate());
|
|
}
|
|
}
|
|
|
|
ticksLeftInCooldown(cooldownDuration: number): Tick {
|
|
return Math.max(
|
|
0,
|
|
cooldownDuration - (this.mg.ticks() - this._cooldownTick),
|
|
);
|
|
}
|
|
|
|
isCooldown(): boolean {
|
|
return this._cooldownTick ? true : false;
|
|
}
|
|
|
|
setDstPort(dstPort: Unit): void {
|
|
this._dstPort = dstPort;
|
|
}
|
|
|
|
setMoveTarget(moveTarget: TileRef) {
|
|
this._moveTarget = moveTarget;
|
|
}
|
|
|
|
moveTarget(): TileRef | null {
|
|
return this._moveTarget;
|
|
}
|
|
|
|
setTargetedBySAM(targeted: boolean): void {
|
|
this._targetedBySAM = targeted;
|
|
}
|
|
|
|
targetedBySAM(): boolean {
|
|
return this._targetedBySAM;
|
|
}
|
|
}
|