mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-26 09:14:36 +00:00
03b405eea7
The blast-radius warning circle was always red, so players couldn't tell who launched an incoming nuke. Now it's green for your own nukes, yellow for ally/teammate nukes, and red for everyone else's. Each telegraph carries a relation (0=self, 1=friendly, 2=enemy), classified from the per-tick relation matrix — the same friend/foe logic alt-view uses — and passed to the shader as a per-instance attribute. Replay/spectator mode (no local player) stays all red. Colors are tunable via the nukeTelegraph slice in render-settings.json.
176 lines
4.3 KiB
TypeScript
176 lines
4.3 KiB
TypeScript
/**
|
|
* extractNukeTelegraphs colors each telegraph by who launched the nuke:
|
|
*
|
|
* - relation 0 (self): the local player owns the nuke
|
|
* - relation 1 (friendly): an ally or teammate owns it (via relation matrix)
|
|
* - relation 2 (enemy): everyone else, and everything in replay / spectator
|
|
* mode (no local player)
|
|
*/
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
extractNukeTelegraphs,
|
|
TELEGRAPH_ENEMY,
|
|
TELEGRAPH_FRIENDLY,
|
|
TELEGRAPH_SELF,
|
|
} from "../../../../../src/client/render/frame/derive/NukeTelegraphs";
|
|
import { buildRelationMatrix } from "../../../../../src/client/render/frame/derive/RelationMatrix";
|
|
import type {
|
|
PlayerState,
|
|
UnitState,
|
|
} from "../../../../../src/client/render/types";
|
|
import {
|
|
UT_ATOM_BOMB,
|
|
UT_WARSHIP,
|
|
} from "../../../../../src/client/render/types";
|
|
|
|
const MAP_W = 100;
|
|
|
|
function ps(overrides: Partial<PlayerState> = {}): PlayerState {
|
|
return {
|
|
smallID: 1,
|
|
isAlive: true,
|
|
isDisconnected: false,
|
|
tilesOwned: 0,
|
|
gold: 0,
|
|
troops: 0,
|
|
isTraitor: false,
|
|
traitorRemainingTicks: 0,
|
|
betrayals: 0,
|
|
hasSpawned: true,
|
|
lastDeleteUnitTick: 0,
|
|
allies: [],
|
|
embargoes: [],
|
|
targets: [],
|
|
outgoingAttacks: [],
|
|
incomingAttacks: [],
|
|
outgoingAllianceRequests: [],
|
|
alliances: [],
|
|
outgoingEmojis: [],
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function nuke(overrides: Partial<UnitState> = {}): UnitState {
|
|
return {
|
|
id: 1,
|
|
unitType: UT_ATOM_BOMB,
|
|
ownerID: 1,
|
|
lastOwnerID: null,
|
|
pos: 0,
|
|
lastPos: 0,
|
|
isActive: true,
|
|
reachedTarget: false,
|
|
retreating: false,
|
|
targetable: true,
|
|
markedForDeletion: false,
|
|
health: null,
|
|
underConstruction: false,
|
|
targetUnitId: null,
|
|
targetTile: 305,
|
|
troops: 0,
|
|
missileTimerQueue: [],
|
|
level: 1,
|
|
hasTrainStation: false,
|
|
trainType: null,
|
|
loaded: null,
|
|
constructionStartTick: null,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function units(...us: UnitState[]): Map<number, UnitState> {
|
|
return new Map(us.map((u) => [u.id, u]));
|
|
}
|
|
|
|
describe("extractNukeTelegraphs", () => {
|
|
it("computes target x/y and blast radii", () => {
|
|
const [t] = extractNukeTelegraphs(units(nuke({ targetTile: 305 })), MAP_W);
|
|
expect(t).toMatchObject({ x: 5, y: 3, innerRadius: 12, outerRadius: 30 });
|
|
});
|
|
|
|
it("skips inactive nukes, nukes without a target, and non-nuke units", () => {
|
|
const result = extractNukeTelegraphs(
|
|
units(
|
|
nuke({ id: 1, isActive: false }),
|
|
nuke({ id: 2, targetTile: null }),
|
|
nuke({ id: 3, unitType: UT_WARSHIP }),
|
|
),
|
|
MAP_W,
|
|
);
|
|
expect(result).toHaveLength(0);
|
|
});
|
|
|
|
it("marks the local player's own nukes as self", () => {
|
|
const rel = buildRelationMatrix(new Map([[1, ps({ smallID: 1 })]]));
|
|
const [t] = extractNukeTelegraphs(
|
|
units(nuke({ ownerID: 1 })),
|
|
MAP_W,
|
|
1,
|
|
rel.matrix,
|
|
rel.size,
|
|
);
|
|
expect(t.relation).toBe(TELEGRAPH_SELF);
|
|
});
|
|
|
|
it("marks an ally's nuke as friendly", () => {
|
|
const rel = buildRelationMatrix(
|
|
new Map([
|
|
[1, ps({ smallID: 1, allies: [2] })],
|
|
[2, ps({ smallID: 2 })],
|
|
]),
|
|
);
|
|
const [t] = extractNukeTelegraphs(
|
|
units(nuke({ ownerID: 2 })),
|
|
MAP_W,
|
|
1,
|
|
rel.matrix,
|
|
rel.size,
|
|
);
|
|
expect(t.relation).toBe(TELEGRAPH_FRIENDLY);
|
|
});
|
|
|
|
it("marks a teammate's nuke as friendly", () => {
|
|
const rel = buildRelationMatrix(
|
|
new Map([
|
|
[1, ps({ smallID: 1 })],
|
|
[2, ps({ smallID: 2 })],
|
|
]),
|
|
new Map([
|
|
[1, "red"],
|
|
[2, "red"],
|
|
]),
|
|
);
|
|
const [t] = extractNukeTelegraphs(
|
|
units(nuke({ ownerID: 2 })),
|
|
MAP_W,
|
|
1,
|
|
rel.matrix,
|
|
rel.size,
|
|
);
|
|
expect(t.relation).toBe(TELEGRAPH_FRIENDLY);
|
|
});
|
|
|
|
it("marks everyone else's nukes as enemy", () => {
|
|
const rel = buildRelationMatrix(
|
|
new Map([
|
|
[1, ps({ smallID: 1 })],
|
|
[2, ps({ smallID: 2 })],
|
|
]),
|
|
);
|
|
const [t] = extractNukeTelegraphs(
|
|
units(nuke({ ownerID: 2 })),
|
|
MAP_W,
|
|
1,
|
|
rel.matrix,
|
|
rel.size,
|
|
);
|
|
expect(t.relation).toBe(TELEGRAPH_ENEMY);
|
|
});
|
|
|
|
it("marks everything as enemy without a local player (replay/spectator)", () => {
|
|
const [t] = extractNukeTelegraphs(units(nuke({ ownerID: 1 })), MAP_W);
|
|
expect(t.relation).toBe(TELEGRAPH_ENEMY);
|
|
});
|
|
});
|