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:
@@ -16,12 +16,15 @@ import {
|
||||
CommandPlanetRename,
|
||||
CommandPlanetRouteRemove,
|
||||
CommandPlanetRouteSet,
|
||||
CommandRaceRelation,
|
||||
CommandRaceVote,
|
||||
CommandScienceCreate,
|
||||
CommandScienceRemove,
|
||||
CommandShipClassCreate,
|
||||
CommandShipClassRemove,
|
||||
PlanetProduction,
|
||||
PlanetRouteLoadType,
|
||||
Relation,
|
||||
UserGamesOrder,
|
||||
UserGamesOrderGetResponse,
|
||||
UserGamesOrderResponse,
|
||||
@@ -98,6 +101,19 @@ export interface RemoveScienceResultFixture extends CommandResultFixtureBase {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface SetDiplomaticStanceResultFixture
|
||||
extends CommandResultFixtureBase {
|
||||
kind: "setDiplomaticStance";
|
||||
acceptor: string;
|
||||
relation: "WAR" | "PEACE";
|
||||
}
|
||||
|
||||
export interface SetVoteRecipientResultFixture
|
||||
extends CommandResultFixtureBase {
|
||||
kind: "setVoteRecipient";
|
||||
acceptor: string;
|
||||
}
|
||||
|
||||
export type CommandResultFixture =
|
||||
| PlanetRenameResultFixture
|
||||
| SetProductionTypeResultFixture
|
||||
@@ -106,7 +122,9 @@ export type CommandResultFixture =
|
||||
| CreateShipClassResultFixture
|
||||
| RemoveShipClassResultFixture
|
||||
| CreateScienceResultFixture
|
||||
| RemoveScienceResultFixture;
|
||||
| RemoveScienceResultFixture
|
||||
| SetDiplomaticStanceResultFixture
|
||||
| SetVoteRecipientResultFixture;
|
||||
|
||||
export function buildOrderResponsePayload(
|
||||
gameId: string,
|
||||
@@ -255,6 +273,22 @@ function encodeItem(builder: Builder, c: CommandResultFixture): number {
|
||||
payloadType = CommandPayload.CommandScienceRemove;
|
||||
break;
|
||||
}
|
||||
case "setDiplomaticStance": {
|
||||
const acceptorOffset = builder.createString(c.acceptor);
|
||||
inner = CommandRaceRelation.createCommandRaceRelation(
|
||||
builder,
|
||||
acceptorOffset,
|
||||
relationToFBS(c.relation),
|
||||
);
|
||||
payloadType = CommandPayload.CommandRaceRelation;
|
||||
break;
|
||||
}
|
||||
case "setVoteRecipient": {
|
||||
const acceptorOffset = builder.createString(c.acceptor);
|
||||
inner = CommandRaceVote.createCommandRaceVote(builder, acceptorOffset);
|
||||
payloadType = CommandPayload.CommandRaceVote;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CommandItem.startCommandItem(builder);
|
||||
CommandItem.addCmdId(builder, cmdIdOffset);
|
||||
@@ -304,3 +338,14 @@ function cargoLoadTypeToFBS(
|
||||
return PlanetRouteLoadType.EMP;
|
||||
}
|
||||
}
|
||||
|
||||
function relationToFBS(
|
||||
value: SetDiplomaticStanceResultFixture["relation"],
|
||||
): Relation {
|
||||
switch (value) {
|
||||
case "WAR":
|
||||
return Relation.WAR;
|
||||
case "PEACE":
|
||||
return Relation.PEACE;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user