mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-01 23:13:29 +00:00
Record MIRV warhead intercepted stats, perf improvements (#1220)
## Description: - Record MIRV warhead intercepted stats. - Refactor `nearbyUnits()` to accept a predicate, and combine related unnecessary `filter()` and `map()` calls. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [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 --------- Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com> Co-authored-by: evanpelle <evanpelle@gmail.com>
This commit is contained in:
@@ -528,11 +528,10 @@ export class ClientGameRunner {
|
||||
UnitType.TradeShip,
|
||||
UnitType.TransportShip,
|
||||
])
|
||||
.sort((a, b) => a.distSquared - b.distSquared)
|
||||
.map((u) => u.unit);
|
||||
.sort((a, b) => a.distSquared - b.distSquared);
|
||||
|
||||
if (units.length > 0) {
|
||||
this.gameView.setFocusedPlayer(units[0].owner() as PlayerView);
|
||||
this.gameView.setFocusedPlayer(units[0].unit.owner() as PlayerView);
|
||||
} else {
|
||||
this.gameView.setFocusedPlayer(null);
|
||||
}
|
||||
|
||||
@@ -53,9 +53,12 @@ export class UnitInfoModal extends LitElement implements Layer {
|
||||
const targetRef = this.game.ref(tileX, tileY);
|
||||
|
||||
const allUnitTypes = Object.values(UnitType);
|
||||
const matchingUnits = this.game
|
||||
.nearbyUnits(targetRef, 10, allUnitTypes)
|
||||
.filter(({ unit }) => unit.isActive());
|
||||
const matchingUnits = this.game.nearbyUnits(
|
||||
targetRef,
|
||||
10,
|
||||
allUnitTypes,
|
||||
({ unit }) => unit.isActive(),
|
||||
);
|
||||
|
||||
if (matchingUnits.length > 0) {
|
||||
matchingUnits.sort((a, b) => a.distSquared - b.distSquared);
|
||||
|
||||
@@ -543,12 +543,11 @@ export class DefaultConfig implements Config {
|
||||
tileToConquer,
|
||||
gm.config().defensePostRange(),
|
||||
UnitType.DefensePost,
|
||||
({ unit }) => unit.owner() === defender,
|
||||
)) {
|
||||
if (dp.unit.owner() === defender) {
|
||||
mag *= this.defensePostDefenseBonus();
|
||||
speed *= this.defensePostDefenseBonus();
|
||||
break;
|
||||
}
|
||||
mag *= this.defensePostDefenseBonus();
|
||||
speed *= this.defensePostDefenseBonus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,17 +39,15 @@ export class SAMLauncherExecution implements Execution {
|
||||
|
||||
private getSingleTarget(): Unit | null {
|
||||
if (this.sam === null) return null;
|
||||
const nukes = this.mg
|
||||
.nearbyUnits(this.sam.tile(), this.searchRangeRadius, [
|
||||
UnitType.AtomBomb,
|
||||
UnitType.HydrogenBomb,
|
||||
])
|
||||
.filter(
|
||||
({ unit }) =>
|
||||
unit.owner() !== this.player &&
|
||||
!this.player.isFriendly(unit.owner()) &&
|
||||
unit.isTargetable(),
|
||||
);
|
||||
const nukes = this.mg.nearbyUnits(
|
||||
this.sam.tile(),
|
||||
this.searchRangeRadius,
|
||||
[UnitType.AtomBomb, UnitType.HydrogenBomb],
|
||||
({ unit }) =>
|
||||
unit.owner() !== this.player &&
|
||||
!this.player.isFriendly(unit.owner()) &&
|
||||
unit.isTargetable(),
|
||||
);
|
||||
|
||||
return (
|
||||
nukes.sort((a, b) => {
|
||||
@@ -117,18 +115,13 @@ export class SAMLauncherExecution implements Execution {
|
||||
this.pseudoRandom = new PseudoRandom(this.sam.id());
|
||||
}
|
||||
|
||||
const mirvWarheadTargets = this.mg
|
||||
.nearbyUnits(
|
||||
this.sam.tile(),
|
||||
this.MIRVWarheadSearchRadius,
|
||||
UnitType.MIRVWarhead,
|
||||
)
|
||||
.map(({ unit }) => unit)
|
||||
.filter(
|
||||
(unit) =>
|
||||
unit.owner() !== this.player && !this.player.isFriendly(unit.owner()),
|
||||
)
|
||||
.filter((unit) => {
|
||||
const mirvWarheadTargets = this.mg.nearbyUnits(
|
||||
this.sam.tile(),
|
||||
this.MIRVWarheadSearchRadius,
|
||||
UnitType.MIRVWarhead,
|
||||
({ unit }) => {
|
||||
if (unit.owner() === this.player) return false;
|
||||
if (this.player.isFriendly(unit.owner())) return false;
|
||||
const dst = unit.targetTile();
|
||||
return (
|
||||
this.sam !== null &&
|
||||
@@ -136,7 +129,8 @@ export class SAMLauncherExecution implements Execution {
|
||||
this.mg.manhattanDist(dst, this.sam.tile()) <
|
||||
this.MIRVWarheadProtectionRadius
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let target: Unit | null = null;
|
||||
if (mirvWarheadTargets.length === 0) {
|
||||
@@ -160,31 +154,41 @@ export class SAMLauncherExecution implements Execution {
|
||||
MessageType.SAM_MISS,
|
||||
this.sam.owner().id(),
|
||||
);
|
||||
} else {
|
||||
if (mirvWarheadTargets.length > 0) {
|
||||
// Message
|
||||
this.mg.displayMessage(
|
||||
`${mirvWarheadTargets.length} MIRV warheads intercepted`,
|
||||
MessageType.SAM_HIT,
|
||||
this.sam.owner().id(),
|
||||
);
|
||||
} else if (mirvWarheadTargets.length > 0) {
|
||||
const samOwner = this.sam.owner();
|
||||
|
||||
// Message
|
||||
this.mg.displayMessage(
|
||||
`${mirvWarheadTargets.length} MIRV warheads intercepted`,
|
||||
MessageType.SAM_HIT,
|
||||
samOwner.id(),
|
||||
);
|
||||
|
||||
mirvWarheadTargets.forEach(({ unit: u }) => {
|
||||
// Delete warheads
|
||||
mirvWarheadTargets.forEach((u) => {
|
||||
u.delete();
|
||||
});
|
||||
} else if (target !== null) {
|
||||
target.setTargetedBySAM(true);
|
||||
this.mg.addExecution(
|
||||
new SAMMissileExecution(
|
||||
this.sam.tile(),
|
||||
this.sam.owner(),
|
||||
this.sam,
|
||||
target,
|
||||
),
|
||||
u.delete();
|
||||
});
|
||||
|
||||
// Record stats
|
||||
this.mg
|
||||
.stats()
|
||||
.bombIntercept(
|
||||
samOwner,
|
||||
UnitType.MIRVWarhead,
|
||||
mirvWarheadTargets.length,
|
||||
);
|
||||
} else {
|
||||
throw new Error("target is null");
|
||||
}
|
||||
} else if (target !== null) {
|
||||
target.setTargetedBySAM(true);
|
||||
this.mg.addExecution(
|
||||
new SAMMissileExecution(
|
||||
this.sam.tile(),
|
||||
this.sam.owner(),
|
||||
this.sam,
|
||||
target,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
throw new Error("target is null");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,11 +72,7 @@ export class SAMMissileExecution implements Execution {
|
||||
// Record stats
|
||||
this.mg
|
||||
.stats()
|
||||
.bombIntercept(
|
||||
this._owner,
|
||||
this.target.owner(),
|
||||
this.target.type() as NukeType,
|
||||
);
|
||||
.bombIntercept(this._owner, this.target.type() as NukeType, 1);
|
||||
return;
|
||||
} else {
|
||||
this.SAMMissile.move(result);
|
||||
|
||||
@@ -602,6 +602,7 @@ export interface Game extends GameMap {
|
||||
tile: TileRef,
|
||||
searchRange: number,
|
||||
types: UnitType | UnitType[],
|
||||
predicate?: (value: { unit: Unit; distSquared: number }) => boolean,
|
||||
): Array<{ unit: Unit; distSquared: number }>;
|
||||
|
||||
addExecution(...exec: Execution[]): void;
|
||||
|
||||
@@ -391,8 +391,14 @@ export class GameView implements GameMap {
|
||||
tile: TileRef,
|
||||
searchRange: number,
|
||||
types: UnitType | UnitType[],
|
||||
predicate?: (value: { unit: UnitView; distSquared: number }) => boolean,
|
||||
): Array<{ unit: UnitView; distSquared: number }> {
|
||||
return this.unitGrid.nearbyUnits(tile, searchRange, types) as Array<{
|
||||
return this.unitGrid.nearbyUnits(
|
||||
tile,
|
||||
searchRange,
|
||||
types,
|
||||
predicate,
|
||||
) as Array<{
|
||||
unit: UnitView;
|
||||
distSquared: number;
|
||||
}>;
|
||||
|
||||
@@ -897,9 +897,7 @@ export class PlayerImpl implements Player {
|
||||
return this.mg.config().unitInfo(unitTypeValue).territoryBound;
|
||||
});
|
||||
|
||||
const nearbyUnits = this.mg
|
||||
.nearbyUnits(tile, searchRadius * 2, types)
|
||||
.map((u) => u.unit);
|
||||
const nearbyUnits = this.mg.nearbyUnits(tile, searchRadius * 2, types);
|
||||
const nearbyTiles = this.mg.bfs(tile, (gm, t) => {
|
||||
return (
|
||||
this.mg.euclideanDistSquared(tile, t) < searchRadiusSquared &&
|
||||
@@ -910,7 +908,7 @@ export class PlayerImpl implements Player {
|
||||
|
||||
const minDistSquared = this.mg.config().structureMinDist() ** 2;
|
||||
for (const t of nearbyTiles) {
|
||||
for (const unit of nearbyUnits) {
|
||||
for (const { unit } of nearbyUnits) {
|
||||
if (this.mg.euclideanDistSquared(unit.tile(), t) < minDistSquared) {
|
||||
validSet.delete(t);
|
||||
break;
|
||||
|
||||
@@ -71,7 +71,7 @@ export interface Stats {
|
||||
bombLand(player: Player, target: Player | TerraNullius, type: NukeType): void;
|
||||
|
||||
// Player's SAM intercepts a bomb from attacker
|
||||
bombIntercept(player: Player, attacker: Player, type: NukeType): void;
|
||||
bombIntercept(player: Player, type: NukeType, count: number | bigint): void;
|
||||
|
||||
// Player earns gold from conquering tiles or trade ships from captured
|
||||
goldWar(player: Player, captured: Player, gold: number | bigint): void;
|
||||
|
||||
@@ -215,8 +215,8 @@ export class StatsImpl implements Stats {
|
||||
this._addBomb(player, type, BOMB_INDEX_LAND, 1);
|
||||
}
|
||||
|
||||
bombIntercept(player: Player, attacker: Player, type: NukeType): void {
|
||||
this._addBomb(player, type, BOMB_INDEX_INTERCEPT, 1);
|
||||
bombIntercept(player: Player, type: NukeType, count: BigIntLike): void {
|
||||
this._addBomb(player, type, BOMB_INDEX_INTERCEPT, count);
|
||||
}
|
||||
|
||||
goldWork(player: Player, gold: BigIntLike): void {
|
||||
|
||||
@@ -96,6 +96,10 @@ export class UnitGrid {
|
||||
tile: TileRef,
|
||||
searchRange: number,
|
||||
types: UnitType | UnitType[],
|
||||
predicate?: (value: {
|
||||
unit: Unit | UnitView;
|
||||
distSquared: number;
|
||||
}) => boolean,
|
||||
): Array<{ unit: Unit | UnitView; distSquared: number }> {
|
||||
const nearby: Array<{ unit: Unit | UnitView; distSquared: number }> = [];
|
||||
const { startGridX, endGridX, startGridY, endGridY } = this.getCellsInRange(
|
||||
@@ -107,12 +111,13 @@ export class UnitGrid {
|
||||
for (let cy = startGridY; cy <= endGridY; cy++) {
|
||||
for (let cx = startGridX; cx <= endGridX; cx++) {
|
||||
for (const unit of this.grid[cy][cx]) {
|
||||
if (typeSet.has(unit.type()) && unit.isActive()) {
|
||||
const distSquared = this.squaredDistanceFromTile(unit, tile);
|
||||
if (distSquared <= rangeSquared) {
|
||||
nearby.push({ unit, distSquared });
|
||||
}
|
||||
}
|
||||
if (!typeSet.has(unit.type())) continue;
|
||||
if (!unit.isActive()) continue;
|
||||
const distSquared = this.squaredDistanceFromTile(unit, tile);
|
||||
if (distSquared > rangeSquared) continue;
|
||||
const value = { unit, distSquared };
|
||||
if (predicate !== undefined && !predicate(value)) continue;
|
||||
nearby.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -163,7 +163,7 @@ describe("Stats", () => {
|
||||
});
|
||||
|
||||
test("bombIntercept", () => {
|
||||
stats.bombIntercept(player1, player2, UnitType.MIRVWarhead);
|
||||
stats.bombIntercept(player1, UnitType.MIRVWarhead, 1);
|
||||
expect(stats.stats()).toStrictEqual({
|
||||
client1: { bombs: { mirvw: [0n, 0n, 1n] } },
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user