mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-22 09:28:12 +00:00
Fix alt-view coloring teammates as enemies in team games (#4247)
## Problem In team games, alternate view (space-hold) colored teammates' units red (enemy color) instead of yellow (ally color). Teammates' territory borders had the same problem. ## Root cause `buildRelationMatrix()` in `src/client/render/frame/derive/RelationMatrix.ts` already supports an optional `teams` map that marks same-team pairs as `RELATION_FRIENDLY`, but the call site in `GameView.populateFrame()` never passed it (the companion `buildTeamMap` helper was dead code). Only explicit alliances were marked friendly, so a teammate without a formal alliance read as neutral — and the alt-view unit palette maps neutral to the enemy color. ## Fix - `GameView` now tracks a `smallID → team` map, populated when each `PlayerView` is first created (team is a static field, so once per player is enough). - The map is passed through to `buildRelationMatrix()`, which feeds both the `AffiliationPalette` (unit colors) and `BorderComputePass` (border colors). ## Testing - New regression test in `tests/client/view/GameView.test.ts`: same-team players are `RELATION_FRIENDLY` in `frame.relationMatrix`, cross-team players stay neutral. - All 36 GameView tests pass; typecheck clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -76,6 +76,8 @@ export class GameView implements GameMap {
|
||||
import("../render/types").PlayerState
|
||||
>();
|
||||
private _unitStates = new Map<number, import("../render/types").UnitState>();
|
||||
/** smallID → team, for the renderer's relation matrix (team games). */
|
||||
private _teams = new Map<number, string>();
|
||||
private updatedTiles: TileRef[] = [];
|
||||
private updatedTerrainTiles: TileRef[] = [];
|
||||
|
||||
@@ -327,6 +329,10 @@ export class GameView implements GameMap {
|
||||
);
|
||||
this._players.set(pu.id, player);
|
||||
this._playerStates.set(pu.smallID!, player.state);
|
||||
const team = player.team();
|
||||
if (team !== null) {
|
||||
this._teams.set(pu.smallID!, team);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -472,7 +478,7 @@ export class GameView implements GameMap {
|
||||
isTransitiveTarget: (sid) =>
|
||||
this._myPlayer?.hasTransitiveTarget(sid) ?? false,
|
||||
});
|
||||
const rel = buildRelationMatrix(this._playerStates);
|
||||
const rel = buildRelationMatrix(this._playerStates, this._teams);
|
||||
f.relationMatrix = rel.matrix;
|
||||
f.relationSize = rel.size;
|
||||
f.allianceClusters = computeAllianceClusters(this._playerStates);
|
||||
|
||||
@@ -466,4 +466,24 @@ describe("GameView.frameData() — renderer contract", () => {
|
||||
game.update(makeEmptyGu(2));
|
||||
expect(game.frameData().structuresDirty).toBe(false);
|
||||
});
|
||||
|
||||
it("frame.relationMatrix marks same-team players as friendly (team games)", () => {
|
||||
const RELATION_FRIENDLY = 1;
|
||||
const RELATION_NEUTRAL = 0;
|
||||
const game = makeGameView();
|
||||
game.update(
|
||||
withPlayers(1, [
|
||||
makePlayerUpdate({ id: "alice", smallID: 1, team: "red" }),
|
||||
makePlayerUpdate({ id: "bob", smallID: 2, team: "red" }),
|
||||
makePlayerUpdate({ id: "carol", smallID: 3, team: "blue" }),
|
||||
]),
|
||||
);
|
||||
const { relationMatrix, relationSize } = game.frameData();
|
||||
// Teammates (no explicit alliance) are friendly both ways.
|
||||
expect(relationMatrix[1 * relationSize + 2]).toBe(RELATION_FRIENDLY);
|
||||
expect(relationMatrix[2 * relationSize + 1]).toBe(RELATION_FRIENDLY);
|
||||
// Cross-team players stay neutral.
|
||||
expect(relationMatrix[1 * relationSize + 3]).toBe(RELATION_NEUTRAL);
|
||||
expect(relationMatrix[3 * relationSize + 1]).toBe(RELATION_NEUTRAL);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user