ui/phase-22: races table with stance toggle and vote slot
Adds the Races View in the in-game shell. The table lists every non-extinct other race with tech levels (percent), totals, planets, votes received, and a per-row WAR | PEACE segmented control. A single vote-recipient slot above the table queues a `CommandRaceVote`; per-row buttons queue `CommandRaceRelation`. Both commands flow through the existing order draft store with collapse-by-acceptor (stance) and singleton (vote) rules. `GameReport` widens with `races`, `myVotes`, `myVoteFor`; the decoder walks `report.player[]` once for the richer projection. The optimistic overlay flips stance and vote target immediately; `votesReceived`, `myVotes`, and the alliance summary stay server-authoritative — alliance grouping and the 2/3 victory check are tallied on the server at turn cutoff and explicitly not surfaced client-side (`rules.txt` keeps foreign races' outgoing vote targets private). Includes Vitest component coverage of stance and vote collapse rules + a Playwright e2e that drives both commands through the dispatcher route and verifies the gateway saw the expected `CommandRaceRelation` / `CommandRaceVote` payloads. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,15 @@ export interface ScienceFixture {
|
||||
export interface PlayerFixture {
|
||||
name: string;
|
||||
drive?: number;
|
||||
weapons?: number;
|
||||
shields?: number;
|
||||
cargo?: number;
|
||||
population?: number;
|
||||
industry?: number;
|
||||
planets?: number;
|
||||
relation?: "WAR" | "PEACE" | "-";
|
||||
votes?: number;
|
||||
extinct?: boolean;
|
||||
}
|
||||
|
||||
export interface RouteEntryFixture {
|
||||
@@ -98,6 +107,8 @@ export interface ReportFixture {
|
||||
race?: string;
|
||||
players?: PlayerFixture[];
|
||||
routes?: RouteFixture[];
|
||||
myVotes?: number;
|
||||
myVoteFor?: string;
|
||||
}
|
||||
|
||||
export function buildReportPayload(fixture: ReportFixture): Uint8Array {
|
||||
@@ -202,9 +213,20 @@ export function buildReportPayload(fixture: ReportFixture): Uint8Array {
|
||||
|
||||
const playerOffsets = (fixture.players ?? []).map((p) => {
|
||||
const name = builder.createString(p.name);
|
||||
const relation =
|
||||
p.relation === undefined ? null : builder.createString(p.relation);
|
||||
Player.startPlayer(builder);
|
||||
Player.addName(builder, name);
|
||||
Player.addDrive(builder, p.drive ?? 1);
|
||||
Player.addWeapons(builder, p.weapons ?? 0);
|
||||
Player.addShields(builder, p.shields ?? 0);
|
||||
Player.addCargo(builder, p.cargo ?? 0);
|
||||
Player.addPopulation(builder, p.population ?? 0);
|
||||
Player.addIndustry(builder, p.industry ?? 0);
|
||||
Player.addPlanets(builder, p.planets ?? 0);
|
||||
if (relation !== null) Player.addRelation(builder, relation);
|
||||
Player.addVotes(builder, p.votes ?? 0);
|
||||
Player.addExtinct(builder, p.extinct ?? false);
|
||||
return Player.endPlayer(builder);
|
||||
});
|
||||
|
||||
@@ -257,6 +279,10 @@ export function buildReportPayload(fixture: ReportFixture): Uint8Array {
|
||||
: Report.createRouteVector(builder, routeOffsets);
|
||||
const raceOffset =
|
||||
fixture.race === undefined ? null : builder.createString(fixture.race);
|
||||
const voteForOffset =
|
||||
fixture.myVoteFor === undefined
|
||||
? null
|
||||
: builder.createString(fixture.myVoteFor);
|
||||
|
||||
const totalPlanets =
|
||||
(fixture.localPlanets ?? []).length +
|
||||
@@ -270,6 +296,8 @@ export function buildReportPayload(fixture: ReportFixture): Uint8Array {
|
||||
Report.addHeight(builder, fixture.mapHeight ?? 4000);
|
||||
Report.addPlanetCount(builder, totalPlanets);
|
||||
if (raceOffset !== null) Report.addRace(builder, raceOffset);
|
||||
if (fixture.myVotes !== undefined) Report.addVotes(builder, fixture.myVotes);
|
||||
if (voteForOffset !== null) Report.addVoteFor(builder, voteForOffset);
|
||||
if (playerVec !== null) Report.addPlayer(builder, playerVec);
|
||||
if (localVec !== null) Report.addLocalPlanet(builder, localVec);
|
||||
if (otherVec !== null) Report.addOtherPlanet(builder, otherVec);
|
||||
|
||||
Reference in New Issue
Block a user