mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-18 00:28:14 +00:00
Merge branch 'main' into patterned-territory
This commit is contained in:
+112
-127
@@ -20,8 +20,9 @@ import {
|
||||
Attack,
|
||||
BuildableUnit,
|
||||
Cell,
|
||||
ColoredTeams,
|
||||
Embargo,
|
||||
EmojiMessage,
|
||||
GameMode,
|
||||
Gold,
|
||||
MessageType,
|
||||
MutableAlliance,
|
||||
@@ -35,13 +36,12 @@ import {
|
||||
TerraNullius,
|
||||
Tick,
|
||||
Unit,
|
||||
UnitSpecificInfos,
|
||||
UnitParams,
|
||||
UnitType,
|
||||
} from "./Game";
|
||||
import { GameImpl } from "./GameImpl";
|
||||
import { andFN, manhattanDistFN, TileRef } from "./GameMap";
|
||||
import { AttackUpdate, GameUpdateType, PlayerUpdate } from "./GameUpdates";
|
||||
import { TerraNulliusImpl } from "./TerraNulliusImpl";
|
||||
import {
|
||||
bestShoreDeploymentSource,
|
||||
canBuildTransportShip,
|
||||
@@ -73,15 +73,15 @@ export class PlayerImpl implements Player {
|
||||
|
||||
markedTraitorTick = -1;
|
||||
|
||||
private embargoes: Set<PlayerID> = new Set();
|
||||
private embargoes = new Map<PlayerID, Embargo>();
|
||||
|
||||
public _borderTiles: Set<TileRef> = new Set();
|
||||
|
||||
public _units: UnitImpl[] = [];
|
||||
public _units: Unit[] = [];
|
||||
public _tiles: Set<TileRef> = new Set();
|
||||
|
||||
private _pattern: string;
|
||||
private _flag: string;
|
||||
private _pattern: string | undefined;
|
||||
private _flag: string | undefined;
|
||||
private _name: string;
|
||||
private _displayName: string;
|
||||
|
||||
@@ -134,7 +134,7 @@ export class PlayerImpl implements Player {
|
||||
name: this.name(),
|
||||
displayName: this.displayName(),
|
||||
id: this.id(),
|
||||
team: this.team(),
|
||||
team: this.team() ?? undefined,
|
||||
smallID: this.smallID(),
|
||||
playerType: this.type(),
|
||||
isAlive: this.isAlive(),
|
||||
@@ -145,7 +145,7 @@ export class PlayerImpl implements Player {
|
||||
troops: this.troops(),
|
||||
targetTroopRatio: this.targetTroopRatio(),
|
||||
allies: this.alliances().map((a) => a.other(this).smallID()),
|
||||
embargoes: this.embargoes,
|
||||
embargoes: new Set([...this.embargoes.keys()].map((p) => p.toString())),
|
||||
isTraitor: this.isTraitor(),
|
||||
targets: this.targets().map((p) => p.smallID()),
|
||||
outgoingEmojis: this.outgoingEmojis(),
|
||||
@@ -179,11 +179,11 @@ export class PlayerImpl implements Player {
|
||||
return this._smallID;
|
||||
}
|
||||
|
||||
pattern(): string {
|
||||
pattern(): string | undefined {
|
||||
return this._pattern;
|
||||
}
|
||||
|
||||
flag(): string {
|
||||
flag(): string | undefined {
|
||||
return this._flag;
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ export class PlayerImpl implements Player {
|
||||
return this._displayName;
|
||||
}
|
||||
|
||||
clientID(): ClientID {
|
||||
clientID(): ClientID | null {
|
||||
return this.playerInfo.clientID;
|
||||
}
|
||||
|
||||
@@ -210,8 +210,8 @@ export class PlayerImpl implements Player {
|
||||
return this.playerInfo.clan;
|
||||
}
|
||||
|
||||
units(...types: UnitType[]): UnitImpl[] {
|
||||
if (types.length == 0) {
|
||||
units(...types: UnitType[]): Unit[] {
|
||||
if (types.length === 0) {
|
||||
return this._units;
|
||||
}
|
||||
const ts = new Set(types);
|
||||
@@ -222,7 +222,7 @@ export class PlayerImpl implements Player {
|
||||
const units = this.units(type);
|
||||
units.push(
|
||||
...this.units(UnitType.Construction).filter(
|
||||
(u) => u.constructionType() == type,
|
||||
(u) => u.constructionType() === type,
|
||||
),
|
||||
);
|
||||
return units;
|
||||
@@ -231,7 +231,7 @@ export class PlayerImpl implements Player {
|
||||
sharesBorderWith(other: Player | TerraNullius): boolean {
|
||||
for (const border of this._borderTiles) {
|
||||
for (const neighbor of this.mg.map().neighbors(border)) {
|
||||
if (this.mg.map().ownerID(neighbor) == other.smallID()) {
|
||||
if (this.mg.map().ownerID(neighbor) === other.smallID()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -256,10 +256,8 @@ export class PlayerImpl implements Player {
|
||||
for (const neighbor of this.mg.map().neighbors(border)) {
|
||||
if (this.mg.map().isLand(neighbor)) {
|
||||
const owner = this.mg.map().ownerID(neighbor);
|
||||
if (owner != this.smallID()) {
|
||||
ns.add(
|
||||
this.mg.playerBySmallID(owner) as PlayerImpl | TerraNulliusImpl,
|
||||
);
|
||||
if (owner !== this.smallID()) {
|
||||
ns.add(this.mg.playerBySmallID(owner) as Player | TerraNullius);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -277,7 +275,7 @@ export class PlayerImpl implements Player {
|
||||
this.mg.conquer(this, tile);
|
||||
}
|
||||
orderRetreat(id: string) {
|
||||
const attack = this._outgoingAttacks.filter((attack) => attack.id() == id);
|
||||
const attack = this._outgoingAttacks.filter((attack) => attack.id() === id);
|
||||
if (!attack || !attack[0]) {
|
||||
consolex.warn(`Didn't find outgoing attack with id ${id}`);
|
||||
return;
|
||||
@@ -285,7 +283,7 @@ export class PlayerImpl implements Player {
|
||||
attack[0].orderRetreat();
|
||||
}
|
||||
executeRetreat(id: string): void {
|
||||
const attack = this._outgoingAttacks.filter((attack) => attack.id() == id);
|
||||
const attack = this._outgoingAttacks.filter((attack) => attack.id() === id);
|
||||
// Execution is delayed so it's not an error that the attack does not exist.
|
||||
if (!attack || !attack[0]) {
|
||||
return;
|
||||
@@ -293,7 +291,7 @@ export class PlayerImpl implements Player {
|
||||
attack[0].executeRetreat();
|
||||
}
|
||||
relinquish(tile: TileRef) {
|
||||
if (this.mg.owner(tile) != this) {
|
||||
if (this.mg.owner(tile) !== this) {
|
||||
throw new Error(`Cannot relinquish tile not owned by this player`);
|
||||
}
|
||||
this.mg.relinquish(tile);
|
||||
@@ -314,16 +312,16 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
incomingAllianceRequests(): AllianceRequest[] {
|
||||
return this.mg.allianceRequests.filter((ar) => ar.recipient() == this);
|
||||
return this.mg.allianceRequests.filter((ar) => ar.recipient() === this);
|
||||
}
|
||||
|
||||
outgoingAllianceRequests(): AllianceRequest[] {
|
||||
return this.mg.allianceRequests.filter((ar) => ar.requestor() == this);
|
||||
return this.mg.allianceRequests.filter((ar) => ar.requestor() === this);
|
||||
}
|
||||
|
||||
alliances(): MutableAlliance[] {
|
||||
return this.mg.alliances_.filter(
|
||||
(a) => a.requestor() == this || a.recipient() == this,
|
||||
(a) => a.requestor() === this || a.recipient() === this,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -332,23 +330,25 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
isAlliedWith(other: Player): boolean {
|
||||
if (other == this) {
|
||||
if (other === this) {
|
||||
return false;
|
||||
}
|
||||
return this.allianceWith(other) != null;
|
||||
return this.allianceWith(other) !== null;
|
||||
}
|
||||
|
||||
allianceWith(other: Player): MutableAlliance | null {
|
||||
if (other == this) {
|
||||
if (other === this) {
|
||||
return null;
|
||||
}
|
||||
return this.alliances().find(
|
||||
(a) => a.recipient() == other || a.requestor() == other,
|
||||
return (
|
||||
this.alliances().find(
|
||||
(a) => a.recipient() === other || a.requestor() === other,
|
||||
) ?? null
|
||||
);
|
||||
}
|
||||
|
||||
canSendAllianceRequest(other: Player): boolean {
|
||||
if (other == this) {
|
||||
if (other === this) {
|
||||
return false;
|
||||
}
|
||||
if (this.isFriendly(other)) {
|
||||
@@ -356,20 +356,18 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
const hasPending =
|
||||
this.incomingAllianceRequests().find((ar) => ar.requestor() == other) !=
|
||||
null ||
|
||||
this.outgoingAllianceRequests().find((ar) => ar.recipient() == other) !=
|
||||
null;
|
||||
this.incomingAllianceRequests().some((ar) => ar.requestor() === other) ||
|
||||
this.outgoingAllianceRequests().some((ar) => ar.recipient() === other);
|
||||
|
||||
if (hasPending) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const recent = this.pastOutgoingAllianceRequests
|
||||
.filter((ar) => ar.recipient() == other)
|
||||
.filter((ar) => ar.recipient() === other)
|
||||
.sort((a, b) => b.createdAt() - a.createdAt());
|
||||
|
||||
if (recent.length == 0) {
|
||||
if (recent.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -393,7 +391,7 @@ export class PlayerImpl implements Player {
|
||||
this.markedTraitorTick = this.mg.ticks();
|
||||
}
|
||||
|
||||
createAllianceRequest(recipient: Player): AllianceRequest {
|
||||
createAllianceRequest(recipient: Player): AllianceRequest | null {
|
||||
if (this.isAlliedWith(recipient)) {
|
||||
throw new Error(`cannot create alliance request, already allies`);
|
||||
}
|
||||
@@ -401,13 +399,11 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
relation(other: Player): Relation {
|
||||
if (other == this) {
|
||||
if (other === this) {
|
||||
throw new Error(`cannot get relation with self: ${this}`);
|
||||
}
|
||||
if (this.relations.has(other)) {
|
||||
return this.relationFromValue(this.relations.get(other));
|
||||
}
|
||||
return Relation.Neutral;
|
||||
const relation = this.relations.get(other) ?? 0;
|
||||
return this.relationFromValue(relation);
|
||||
}
|
||||
|
||||
private relationFromValue(relationValue: number): Relation {
|
||||
@@ -433,13 +429,10 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
updateRelation(other: Player, delta: number): void {
|
||||
if (other == this) {
|
||||
if (other === this) {
|
||||
throw new Error(`cannot update relation with self: ${this}`);
|
||||
}
|
||||
let relation = 0;
|
||||
if (this.relations.has(other)) {
|
||||
relation = this.relations.get(other);
|
||||
}
|
||||
const relation = this.relations.get(other) ?? 0;
|
||||
const newRelation = within(relation + delta, -100, 100);
|
||||
this.relations.set(other, newRelation);
|
||||
}
|
||||
@@ -457,7 +450,7 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
canTarget(other: Player): boolean {
|
||||
if (this == other) {
|
||||
if (this === other) {
|
||||
return false;
|
||||
}
|
||||
if (this.isFriendly(other)) {
|
||||
@@ -476,12 +469,12 @@ export class PlayerImpl implements Player {
|
||||
this.mg.target(this, other);
|
||||
}
|
||||
|
||||
targets(): PlayerImpl[] {
|
||||
targets(): Player[] {
|
||||
return this.targets_
|
||||
.filter(
|
||||
(t) => this.mg.ticks() - t.tick < this.mg.config().targetDuration(),
|
||||
)
|
||||
.map((t) => t.target as PlayerImpl);
|
||||
.map((t) => t.target);
|
||||
}
|
||||
|
||||
transitiveTargets(): Player[] {
|
||||
@@ -493,13 +486,13 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
sendEmoji(recipient: Player | typeof AllPlayers, emoji: string): void {
|
||||
if (recipient == this) {
|
||||
if (recipient === this) {
|
||||
throw Error(`Cannot send emoji to oneself: ${this}`);
|
||||
}
|
||||
const msg: EmojiMessage = {
|
||||
message: emoji,
|
||||
senderID: this.smallID(),
|
||||
recipientID: recipient == AllPlayers ? recipient : recipient.smallID(),
|
||||
recipientID: recipient === AllPlayers ? recipient : recipient.smallID(),
|
||||
createdAt: this.mg.ticks(),
|
||||
};
|
||||
this.outgoingEmojis_.push(msg);
|
||||
@@ -518,9 +511,9 @@ export class PlayerImpl implements Player {
|
||||
|
||||
canSendEmoji(recipient: Player | typeof AllPlayers): boolean {
|
||||
const recipientID =
|
||||
recipient == AllPlayers ? AllPlayers : recipient.smallID();
|
||||
recipient === AllPlayers ? AllPlayers : recipient.smallID();
|
||||
const prevMsgs = this.outgoingEmojis_.filter(
|
||||
(msg) => msg.recipientID == recipientID,
|
||||
(msg) => msg.recipientID === recipientID,
|
||||
);
|
||||
for (const msg of prevMsgs) {
|
||||
if (
|
||||
@@ -534,18 +527,11 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
canDonate(recipient: Player): boolean {
|
||||
if (
|
||||
recipient.type() == PlayerType.Human &&
|
||||
this.mg.config().gameConfig().gameMode == GameMode.FFA
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.isFriendly(recipient)) {
|
||||
return false;
|
||||
}
|
||||
for (const donation of this.sentDonations) {
|
||||
if (donation.recipient == recipient) {
|
||||
if (donation.recipient === recipient) {
|
||||
if (
|
||||
this.mg.ticks() - donation.tick <
|
||||
this.mg.config().donateCooldown()
|
||||
@@ -593,21 +579,39 @@ export class PlayerImpl implements Player {
|
||||
canTrade(other: Player): boolean {
|
||||
const embargo =
|
||||
other.hasEmbargoAgainst(this) || this.hasEmbargoAgainst(other);
|
||||
return !embargo && other.id() != this.id();
|
||||
return !embargo && other.id() !== this.id();
|
||||
}
|
||||
|
||||
addEmbargo(other: PlayerID): void {
|
||||
this.embargoes.add(other);
|
||||
addEmbargo(other: PlayerID, isTemporary: boolean): void {
|
||||
const embargo = this.embargoes.get(other);
|
||||
if (embargo !== undefined && !embargo.isTemporary) return;
|
||||
|
||||
this.embargoes.set(other, {
|
||||
createdAt: this.mg.ticks(),
|
||||
isTemporary: isTemporary,
|
||||
target: other,
|
||||
});
|
||||
}
|
||||
|
||||
getEmbargoes(): Embargo[] {
|
||||
return [...this.embargoes.values()];
|
||||
}
|
||||
|
||||
stopEmbargo(other: PlayerID): void {
|
||||
this.embargoes.delete(other);
|
||||
}
|
||||
|
||||
endTemporaryEmbargo(other: PlayerID): void {
|
||||
const embargo = this.embargoes.get(other);
|
||||
if (embargo !== undefined && !embargo.isTemporary) return;
|
||||
|
||||
this.stopEmbargo(other);
|
||||
}
|
||||
|
||||
tradingPartners(): Player[] {
|
||||
return this.mg
|
||||
.players()
|
||||
.filter((other) => other != this && this.canTrade(other));
|
||||
.filter((other) => other !== this && this.canTrade(other));
|
||||
}
|
||||
|
||||
team(): Team | null {
|
||||
@@ -615,13 +619,16 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
isOnSameTeam(other: Player): boolean {
|
||||
if (other == this) {
|
||||
if (other === this) {
|
||||
return false;
|
||||
}
|
||||
if (this.team() == null || other.team() == null) {
|
||||
if (this.team() === null || other.team() === null) {
|
||||
return false;
|
||||
}
|
||||
return this._team == other.team();
|
||||
if (this.team() === ColoredTeams.Bot || other.team() === ColoredTeams.Bot) {
|
||||
return false;
|
||||
}
|
||||
return this._team === other.team();
|
||||
}
|
||||
|
||||
isFriendly(other: Player): boolean {
|
||||
@@ -692,47 +699,35 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
|
||||
captureUnit(unit: Unit): void {
|
||||
if (unit.owner() == this) {
|
||||
if (unit.owner() === this) {
|
||||
throw new Error(`Cannot capture unit, ${this} already owns ${unit}`);
|
||||
}
|
||||
const prev = unit.owner();
|
||||
(prev as PlayerImpl)._units = (prev as PlayerImpl)._units.filter(
|
||||
(u) => u != unit,
|
||||
);
|
||||
(unit as UnitImpl)._owner = this;
|
||||
this._units.push(unit as UnitImpl);
|
||||
this.mg.addUpdate(unit.toUpdate());
|
||||
this.mg.displayMessage(
|
||||
`${unit.type()} captured by ${this.displayName()}`,
|
||||
MessageType.ERROR,
|
||||
prev.id(),
|
||||
);
|
||||
this.mg.displayMessage(
|
||||
`Captured ${unit.type()} from ${prev.displayName()}`,
|
||||
MessageType.SUCCESS,
|
||||
this.id(),
|
||||
);
|
||||
unit.setOwner(this);
|
||||
}
|
||||
|
||||
buildUnit(
|
||||
type: UnitType,
|
||||
troops: number,
|
||||
buildUnit<T extends UnitType>(
|
||||
type: T,
|
||||
spawnTile: TileRef,
|
||||
unitSpecificInfos: UnitSpecificInfos = {},
|
||||
): UnitImpl {
|
||||
params: UnitParams<T>,
|
||||
): Unit {
|
||||
if (this.mg.config().isUnitDisabled(type)) {
|
||||
throw new Error(
|
||||
`Attempted to build disabled unit ${type} at tile ${spawnTile} by player ${this.name()}`,
|
||||
);
|
||||
}
|
||||
|
||||
const cost = this.mg.unitInfo(type).cost(this);
|
||||
const b = new UnitImpl(
|
||||
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) : 0);
|
||||
this.mg.addUpdate(b.toUpdate());
|
||||
this.mg.addUnit(b);
|
||||
|
||||
@@ -744,7 +739,9 @@ export class PlayerImpl implements Player {
|
||||
return Object.values(UnitType).map((u) => {
|
||||
return {
|
||||
type: u,
|
||||
canBuild: this.canBuild(u, tile, validTiles),
|
||||
canBuild: this.mg.inSpawnPhase()
|
||||
? false
|
||||
: this.canBuild(u, tile, validTiles),
|
||||
cost: this.mg.config().unitInfo(u).cost(this),
|
||||
} as BuildableUnit;
|
||||
});
|
||||
@@ -755,19 +752,8 @@ export class PlayerImpl implements Player {
|
||||
targetTile: TileRef,
|
||||
validTiles: TileRef[] | null = null,
|
||||
): TileRef | false {
|
||||
// prevent the building of nukes and nuke related buildings
|
||||
if (this.mg.config().disableNukes()) {
|
||||
if (
|
||||
unitType === UnitType.MissileSilo ||
|
||||
unitType === UnitType.MIRV ||
|
||||
unitType === UnitType.AtomBomb ||
|
||||
unitType === UnitType.HydrogenBomb ||
|
||||
unitType === UnitType.SAMLauncher ||
|
||||
unitType === UnitType.SAMMissile ||
|
||||
unitType === UnitType.MIRVWarhead
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (this.mg.config().isUnitDisabled(unitType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const cost = this.mg.unitInfo(unitType).cost(this);
|
||||
@@ -816,25 +802,24 @@ export class PlayerImpl implements Player {
|
||||
}
|
||||
// only get missilesilos that are not on cooldown
|
||||
const spawns = this.units(UnitType.MissileSilo)
|
||||
.map((u) => u as Unit)
|
||||
.filter((silo) => {
|
||||
return !silo.isCooldown();
|
||||
})
|
||||
.sort(distSortUnit(this.mg, tile));
|
||||
if (spawns.length == 0) {
|
||||
if (spawns.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return spawns[0].tile();
|
||||
}
|
||||
|
||||
portSpawn(tile: TileRef, validTiles: TileRef[]): TileRef | false {
|
||||
portSpawn(tile: TileRef, validTiles: TileRef[] | null): TileRef | false {
|
||||
const spawns = Array.from(
|
||||
this.mg.bfs(
|
||||
tile,
|
||||
manhattanDistFN(tile, this.mg.config().radiusPortSpawn()),
|
||||
),
|
||||
)
|
||||
.filter((t) => this.mg.owner(t) == this && this.mg.isOceanShore(t))
|
||||
.filter((t) => this.mg.owner(t) === this && this.mg.isOceanShore(t))
|
||||
.sort(
|
||||
(a, b) =>
|
||||
this.mg.manhattanDist(a, tile) - this.mg.manhattanDist(b, tile),
|
||||
@@ -859,7 +844,7 @@ export class PlayerImpl implements Player {
|
||||
this.mg.manhattanDist(a.tile(), tile) -
|
||||
this.mg.manhattanDist(b.tile(), tile),
|
||||
);
|
||||
if (spawns.length == 0) {
|
||||
if (spawns.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return spawns[0].tile();
|
||||
@@ -870,14 +855,14 @@ export class PlayerImpl implements Player {
|
||||
validTiles: TileRef[] | null = null,
|
||||
): TileRef | false {
|
||||
const tiles = validTiles ?? this.validStructureSpawnTiles(tile);
|
||||
if (tiles.length == 0) {
|
||||
if (tiles.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return tiles[0];
|
||||
}
|
||||
|
||||
private validStructureSpawnTiles(tile: TileRef): TileRef[] {
|
||||
if (this.mg.owner(tile) != this) {
|
||||
if (this.mg.owner(tile) !== this) {
|
||||
return [];
|
||||
}
|
||||
const searchRadius = 15;
|
||||
@@ -892,7 +877,7 @@ export class PlayerImpl implements Player {
|
||||
const nearbyTiles = this.mg.bfs(tile, (gm, t) => {
|
||||
return (
|
||||
this.mg.euclideanDistSquared(tile, t) < searchRadiusSquared &&
|
||||
gm.ownerID(t) == this.smallID()
|
||||
gm.ownerID(t) === this.smallID()
|
||||
);
|
||||
});
|
||||
const validSet: Set<TileRef> = new Set(nearbyTiles);
|
||||
@@ -917,9 +902,9 @@ export class PlayerImpl implements Player {
|
||||
|
||||
tradeShipSpawn(targetTile: TileRef): TileRef | false {
|
||||
const spawns = this.units(UnitType.Port).filter(
|
||||
(u) => u.tile() == targetTile,
|
||||
(u) => u.tile() === targetTile,
|
||||
);
|
||||
if (spawns.length == 0) {
|
||||
if (spawns.length === 0) {
|
||||
return false;
|
||||
}
|
||||
return spawns[0].tile();
|
||||
@@ -931,7 +916,7 @@ export class PlayerImpl implements Player {
|
||||
hash(): number {
|
||||
return (
|
||||
simpleHash(this.id()) * (this.population() + this.numTilesOwned()) +
|
||||
this._units.reduce((acc, unit) => acc + unit.hash(), 0)
|
||||
this._units.reduce((acc, unit) => acc + (unit as UnitImpl).hash(), 0)
|
||||
);
|
||||
}
|
||||
toString(): string {
|
||||
@@ -958,7 +943,7 @@ export class PlayerImpl implements Player {
|
||||
createAttack(
|
||||
target: Player | TerraNullius,
|
||||
troops: number,
|
||||
sourceTile: TileRef,
|
||||
sourceTile: TileRef | null,
|
||||
): Attack {
|
||||
const attack = new AttackImpl(
|
||||
this._pseudo_random.nextID(),
|
||||
@@ -990,7 +975,7 @@ export class PlayerImpl implements Player {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.mg.owner(tile) == this) {
|
||||
if (this.mg.owner(tile) === this) {
|
||||
return false;
|
||||
}
|
||||
if (this.mg.hasOwner(tile)) {
|
||||
@@ -1014,7 +999,7 @@ export class PlayerImpl implements Player {
|
||||
),
|
||||
)) {
|
||||
for (const n of this.mg.neighbors(t)) {
|
||||
if (this.mg.owner(n) == this) {
|
||||
if (this.mg.owner(n) === this) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1032,7 +1017,7 @@ export class PlayerImpl implements Player {
|
||||
tradingPorts(port: Unit): Unit[] {
|
||||
const ports = this.mg
|
||||
.players()
|
||||
.filter((p) => p != port.owner() && p.canTrade(port.owner()))
|
||||
.filter((p) => p !== port.owner() && p.canTrade(port.owner()))
|
||||
.flatMap((p) => p.units(UnitType.Port))
|
||||
.sort((p1, p2) => {
|
||||
return (
|
||||
@@ -1053,7 +1038,7 @@ export class PlayerImpl implements Player {
|
||||
// Make ally ports twice more likely by putting them again
|
||||
this.mg
|
||||
.players()
|
||||
.filter((p) => p != port.owner() && p.canTrade(port.owner()))
|
||||
.filter((p) => p !== port.owner() && p.canTrade(port.owner()))
|
||||
.filter((p) => p.isAlliedWith(port.owner()))
|
||||
.flatMap((p) => p.units(UnitType.Port))
|
||||
.forEach((p) => ports.push(p));
|
||||
|
||||
Reference in New Issue
Block a user