Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/client/graphics/PlayerIcons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export function getPlayerIcons(
}

// Nuke icon (different color depending on whether the local player is the target)
const nukesSentByOtherPlayer = game.units(...nukeTypes).filter((unit) => {
const nukesSentByOtherPlayer = game.units(nukeTypes).filter((unit) => {
const isSendingNuke = player.id() === unit.owner().id();
const notMyPlayer = !myPlayer || unit.owner().id() !== myPlayer.id();
return isSendingNuke && notMyPlayer && unit.isActive();
Expand Down
2 changes: 1 addition & 1 deletion src/core/execution/nation/NationStructureBehavior.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ export class NationStructureBehavior {
private getTotalStructureDensity(): number {
const tilesOwned = this.player.numTilesOwned();
return tilesOwned > 0
? this.player.units(...StructureTypes).length / tilesOwned
? this.player.units(StructureTypes).length / tilesOwned
: 0; //ignoring levels for structures
}

Expand Down
12 changes: 10 additions & 2 deletions src/core/game/Game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,11 @@ export interface Player {
removeTroops(troops: number): number;

// Units
units(...types: UnitType[]): Unit[];
units(): Unit[];
units(type: UnitType): Unit[];
units(type0: UnitType, type1: UnitType): Unit[];
units(type0: UnitType, type1: UnitType, type2: UnitType): Unit[];
units(types: readonly UnitType[]): Unit[];
unitCount(type: UnitType): number;
unitsConstructed(type: UnitType): number;
unitsOwned(type: UnitType): number;
Expand Down Expand Up @@ -770,7 +774,11 @@ export interface Game extends GameMap {
setPaused(paused: boolean): void;

// Units
units(...types: UnitType[]): Unit[];
units(): Unit[];
units(type: UnitType): Unit[];
units(type0: UnitType, type1: UnitType): Unit[];
units(type0: UnitType, type1: UnitType, type2: UnitType): Unit[];
units(types: readonly UnitType[]): Unit[];
unitCount(type: UnitType): number;
unitInfo(type: UnitType): UnitInfo;
hasUnitNearby(
Expand Down
51 changes: 49 additions & 2 deletions src/core/game/GameImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,55 @@ export class GameImpl implements Game {
});
}

units(...types: UnitType[]): Unit[] {
return Array.from(this._players.values()).flatMap((p) => p.units(...types));
units(): Unit[];
units(type: UnitType): Unit[];
units(type0: UnitType, type1: UnitType): Unit[];
units(type0: UnitType, type1: UnitType, type2: UnitType): Unit[];
units(types: readonly UnitType[]): Unit[];
units(
a?: UnitType | readonly UnitType[],
b?: UnitType,
c?: UnitType,
): Unit[] {
const out: Unit[] = [];

if (a === undefined) {
for (const p of this._players.values()) {
const units = p.units();
for (const u of units) out.push(u);
}
return out;
}

if (Array.isArray(a)) {
for (const p of this._players.values()) {
const units = p.units(a);
for (const u of units) out.push(u);
}
return out;
}

if (b === undefined) {
for (const p of this._players.values()) {
const units = p.units(a);
for (const u of units) out.push(u);
}
return out;
}

if (c === undefined) {
for (const p of this._players.values()) {
const units = p.units(a, b);
for (const u of units) out.push(u);
}
return out;
}

for (const p of this._players.values()) {
const units = p.units(a, b, c);
for (const u of units) out.push(u);
}
return out;
}

unitCount(type: UnitType): number {
Expand Down
100 changes: 90 additions & 10 deletions src/core/game/GameView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,10 +431,30 @@ export class PlayerView {
return this.game.worker.attackAveragePosition(playerID, attackID);
}

units(...types: UnitType[]): UnitView[] {
return this.game
.units(...types)
.filter((u) => u.owner().smallID() === this.smallID());
units(): UnitView[];
units(type: UnitType): UnitView[];
units(type0: UnitType, type1: UnitType): UnitView[];
units(type0: UnitType, type1: UnitType, type2: UnitType): UnitView[];
units(types: readonly UnitType[]): UnitView[];
units(
a?: UnitType | readonly UnitType[],
b?: UnitType,
c?: UnitType,
): UnitView[] {
let units: UnitView[];
if (a === undefined) {
units = this.game.units();
} else if (Array.isArray(a)) {
units = this.game.units(a);
} else if (b === undefined) {
units = this.game.units(a);
} else if (c === undefined) {
units = this.game.units(a, b);
} else {
units = this.game.units(a, b, c);
}

return units.filter((u) => u.owner().smallID() === this.smallID());
}

nameLocation(): NameViewData {
Expand Down Expand Up @@ -834,13 +854,73 @@ export class GameView implements GameMap {
config(): Config {
return this._config;
}
units(...types: UnitType[]): UnitView[] {
if (types.length === 0) {
return Array.from(this._units.values()).filter((u) => u.isActive());
units(): UnitView[];
units(type: UnitType): UnitView[];
units(type0: UnitType, type1: UnitType): UnitView[];
units(type0: UnitType, type1: UnitType, type2: UnitType): UnitView[];
units(types: readonly UnitType[]): UnitView[];
units(
a?: UnitType | readonly UnitType[],
b?: UnitType,
c?: UnitType,
): UnitView[] {
if (a === undefined) {
const out: UnitView[] = [];
for (const u of this._units.values()) {
if (u.isActive()) out.push(u);
}
return out;
}
return Array.from(this._units.values()).filter(
(u) => u.isActive() && types.includes(u.type()),
);

if (Array.isArray(a)) {
const types = a;
const len = types.length;
if (len === 0) return this.units();

if (len === 1) return this.units(types[0]!);
if (len === 2) return this.units(types[0]!, types[1]!);
if (len === 3) return this.units(types[0]!, types[1]!, types[2]!);

const out: UnitView[] = [];
for (const u of this._units.values()) {
if (!u.isActive()) continue;
if (types.includes(u.type())) out.push(u);
}
return out;
}

if (b === undefined) {
const t0 = a;
const out: UnitView[] = [];
for (const u of this._units.values()) {
if (!u.isActive()) continue;
if (u.type() === t0) out.push(u);
}
return out;
}

if (c === undefined) {
const t0 = a;
const t1 = b;
const out: UnitView[] = [];
for (const u of this._units.values()) {
if (!u.isActive()) continue;
const t = u.type();
if (t === t0 || t === t1) out.push(u);
}
return out;
}

const t0 = a;
const t1 = b;
const t2 = c;
const out: UnitView[] = [];
for (const u of this._units.values()) {
if (!u.isActive()) continue;
const t = u.type();
if (t === t0 || t === t1 || t === t2) out.push(u);
}
return out;
}
unit(id: number): UnitView | undefined {
return this._units.get(id);
Expand Down
99 changes: 76 additions & 23 deletions src/core/game/PlayerImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,25 +213,87 @@ export class PlayerImpl implements Player {
return this.playerInfo.clan;
}

units(...types: UnitType[]): Unit[] {
const len = types.length;
if (len === 0) {
units(): Unit[];
units(type: UnitType): Unit[];
units(type0: UnitType, type1: UnitType): Unit[];
units(type0: UnitType, type1: UnitType, type2: UnitType): Unit[];
units(types: readonly UnitType[]): Unit[];
units(
a?: UnitType | readonly UnitType[],
b?: UnitType,
c?: UnitType,
): Unit[] {
if (a === undefined) {
return this._units;
}

if (Array.isArray(a)) {
const types = a;
const len = types.length;
if (len === 0) return this._units;

// Fast paths for common small arity calls to avoid Set allocation.
if (len === 1) {
const t0 = types[0]!;
const out: Unit[] = [];
for (const u of this._units) {
if (u.type() === t0) out.push(u);
}
return out;
}

if (len === 2) {
const t0 = types[0]!;
const t1 = types[1]!;
if (t0 === t1) {
const out: Unit[] = [];
for (const u of this._units) {
if (u.type() === t0) out.push(u);
}
return out;
}
const out: Unit[] = [];
for (const u of this._units) {
const t = u.type();
if (t === t0 || t === t1) out.push(u);
}
return out;
}

if (len === 3) {
const t0 = types[0]!;
const t1 = types[1]!;
const t2 = types[2]!;
// Keep semantics identical for duplicates in types by using direct comparisons.
const out: Unit[] = [];
for (const u of this._units) {
const t = u.type();
if (t === t0 || t === t1 || t === t2) out.push(u);
}
return out;
}

const ts = new Set(types);
const out: Unit[] = [];
for (const u of this._units) {
if (ts.has(u.type())) out.push(u);
}
return out;
}

// Fast paths for common small arity calls to avoid Set allocation.
if (len === 1) {
const t0 = types[0]!;
if (b === undefined) {
const t0 = a;
const out: Unit[] = [];
for (const u of this._units) {
if (u.type() === t0) out.push(u);
}
return out;
}

if (len === 2) {
const t0 = types[0]!;
const t1 = types[1]!;
if (c === undefined) {
const t0 = a;
const t1 = b;
if (t0 === t1) {
const out: Unit[] = [];
for (const u of this._units) {
Expand All @@ -247,23 +309,14 @@ export class PlayerImpl implements Player {
return out;
}

if (len === 3) {
const t0 = types[0]!;
const t1 = types[1]!;
const t2 = types[2]!;
// Keep semantics identical for duplicates in types by using direct comparisons.
const out: Unit[] = [];
for (const u of this._units) {
const t = u.type();
if (t === t0 || t === t1 || t === t2) out.push(u);
}
return out;
}

const ts = new Set(types);
// Keep semantics identical for duplicates in types by using direct comparisons.
const t0 = a;
const t1 = b;
const t2 = c;
const out: Unit[] = [];
for (const u of this._units) {
if (ts.has(u.type())) out.push(u);
const t = u.type();
if (t === t0 || t === t1 || t === t2) out.push(u);
}
return out;
}
Expand Down
Loading