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:
Evan
2026-06-12 15:05:37 -07:00
committed by GitHub
parent 9e2648f80c
commit b85d1fc372
2 changed files with 27 additions and 1 deletions
+20
View File
@@ -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);
});
});