feat(model+ui): F8-05 — race on OtherGroup, real attribution + N×M label
Tests · UI / test (push) Has been cancelled
Tests · Go / test (pull_request) Successful in 2m6s
Tests · Go / test (push) Successful in 2m6s
Tests · Integration / integration (pull_request) Successful in 1m51s
Tests · UI / test (pull_request) Successful in 3m53s
Tests · UI / test (push) Has been cancelled
Tests · Go / test (pull_request) Successful in 2m6s
Tests · Go / test (push) Successful in 2m6s
Tests · Integration / integration (pull_request) Successful in 1m51s
Tests · UI / test (pull_request) Successful in 3m53s
Issue #48 п.32 ("Stationed ship groups") shipped with a fragile race fallback: when a foreign group sat on a non-`other`-kind planet the inspector printed a generic "foreign" label, which collapsed the race dropdown to a single uninformative bucket. The engine FBS contract did not carry per-group race either, so live games hit the same gap. This patch carries race authoritatively from the engine through every layer down to the inspector. Wire format & engine - `pkg/schema/fbs/report.fbs`: add `race:string` to `OtherGroup` and `LocalGroup` (additive — old clients ignore). - `pkg/schema/fbs/report/`: regenerated Go bindings. - `ui/frontend/src/proto/galaxy/fbs/report/`: regenerated TS bindings. - `pkg/model/report.OtherGroup.Race`: new field; carried through `LocalGroup` via the embedded `OtherGroup`. - `pkg/transcoder/report.go`: encode + decode `race` on both `LocalGroup` and `OtherGroup`. - `game/internal/controller/report.go.otherGroup`: set `v.Race` from `c.g.Race[c.RaceIndex(sg.OwnerID)].Name` so every emitted group — own or foreign — carries the resolved race name. Legacy parser - `tools/local-dev/legacy-report/parser.go`: capture the `<Race> Groups` header into `pendingOtherGroup.race`, fill local group `Race` from `p.rep.Race`, propagate both into the `report.OtherGroup` rows. - Tests + smoke counts updated; regenerated `KNNTS{039,041}.json` fixtures so the synthetic loader carries the new field. UI - `ui/frontend/src/api/`: `ReportShipGroupBase.race` field; synthetic loader + FBS decoder populate it. - `ui/frontend/src/lib/inspectors/planet/ship-groups.svelte`: the stationed-groups inspector picks race directly from `group.race` (own falls back to `localRace`, both finally to the `race.unknown` placeholder). The planet-owner / "foreign" heuristic is gone. - Row label changes from "N ships mass M" to a compact `<class>` | `<N ×>` | `<mass>` three-column layout: the count cell is right-aligned tabular, the mass cell is right-aligned monospace + tabular, matching the inspector / calculator number conventions. Stale i18n keys removed (`ship_groups.row.count`, `.row.mass`, `.race.foreign`). - All affected unit tests (8 files) carry the new `race` field. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -600,10 +600,7 @@ const en = {
|
||||
|
||||
"game.inspector.planet.ship_groups.race_filter.aria": "stationed race",
|
||||
"game.inspector.planet.ship_groups.title": "stationed ship groups",
|
||||
"game.inspector.planet.ship_groups.row.count": "{count} ships",
|
||||
"game.inspector.planet.ship_groups.row.mass": "mass {mass}",
|
||||
"game.inspector.planet.ship_groups.race.unknown": "unknown",
|
||||
"game.inspector.planet.ship_groups.race.foreign": "foreign",
|
||||
|
||||
"game.report.loading": "loading report…",
|
||||
"game.report.back_to_map": "back to map",
|
||||
|
||||
@@ -601,10 +601,7 @@ const ru: Record<keyof typeof en, string> = {
|
||||
|
||||
"game.inspector.planet.ship_groups.race_filter.aria": "раса в орбите",
|
||||
"game.inspector.planet.ship_groups.title": "корабли на орбите",
|
||||
"game.inspector.planet.ship_groups.row.count": "{count} кораблей",
|
||||
"game.inspector.planet.ship_groups.row.mass": "масса {mass}",
|
||||
"game.inspector.planet.ship_groups.race.unknown": "неизвестно",
|
||||
"game.inspector.planet.ship_groups.race.foreign": "чужие",
|
||||
|
||||
"game.report.loading": "загрузка отчёта…",
|
||||
"game.report.back_to_map": "назад к карте",
|
||||
|
||||
@@ -66,6 +66,17 @@ modes because the dropdown already names the active race.
|
||||
groupId: string | null;
|
||||
}
|
||||
|
||||
// F8-05 owner-feedback: the row carries the authoritative `race`
|
||||
// field projected by the engine (and by the legacy parser via the
|
||||
// `<Race> Groups` header), so every stationed group surfaces its
|
||||
// real owner. The planet-owner / "foreign" fallback is gone — when
|
||||
// the wire carries no race the row falls back to the i18n
|
||||
// `race.unknown` placeholder, matching how the local-race column
|
||||
// degrades if `localRace` is missing.
|
||||
const unknownRace = $derived(
|
||||
i18n.t("game.inspector.planet.ship_groups.race.unknown"),
|
||||
);
|
||||
|
||||
const stationedRows: StationedRow[] = $derived.by(() => {
|
||||
const rows: StationedRow[] = [];
|
||||
for (const g of localShipGroups) {
|
||||
@@ -73,7 +84,7 @@ modes because the dropdown already names the active race.
|
||||
if (g.origin !== null || g.range !== null) continue;
|
||||
rows.push({
|
||||
key: `local:${g.id}`,
|
||||
race: localRace || i18n.t("game.inspector.planet.ship_groups.race.unknown"),
|
||||
race: g.race || localRace || unknownRace,
|
||||
class: g.class,
|
||||
count: g.count,
|
||||
mass: g.mass,
|
||||
@@ -81,16 +92,13 @@ modes because the dropdown already names the active race.
|
||||
groupId: g.id,
|
||||
});
|
||||
}
|
||||
const foreignRace =
|
||||
planet.owner ??
|
||||
i18n.t("game.inspector.planet.ship_groups.race.foreign");
|
||||
for (let i = 0; i < otherShipGroups.length; i++) {
|
||||
const g = otherShipGroups[i]!;
|
||||
if (g.destination !== planet.number) continue;
|
||||
if (g.origin !== null || g.range !== null) continue;
|
||||
rows.push({
|
||||
key: `other:${i}`,
|
||||
race: foreignRace,
|
||||
race: g.race || unknownRace,
|
||||
class: g.class,
|
||||
count: g.count,
|
||||
mass: g.mass,
|
||||
@@ -183,34 +191,20 @@ modes because the dropdown already names the active race.
|
||||
{@const groupId = row.groupId}
|
||||
<button
|
||||
type="button"
|
||||
class="select"
|
||||
class="cells select"
|
||||
data-testid="inspector-planet-ship-groups-select"
|
||||
onclick={() => selectLocalGroup(groupId)}
|
||||
>
|
||||
<span class="class">{row.class}</span>
|
||||
<span class="count">
|
||||
{i18n.t("game.inspector.planet.ship_groups.row.count", {
|
||||
count: String(row.count),
|
||||
})}
|
||||
</span>
|
||||
<span class="mass">
|
||||
{i18n.t("game.inspector.planet.ship_groups.row.mass", {
|
||||
mass: formatFloat(row.mass),
|
||||
})}
|
||||
</span>
|
||||
<span class="count">{row.count} ×</span>
|
||||
<span class="mass">{formatFloat(row.mass)}</span>
|
||||
</button>
|
||||
{:else}
|
||||
<span class="class">{row.class}</span>
|
||||
<span class="count">
|
||||
{i18n.t("game.inspector.planet.ship_groups.row.count", {
|
||||
count: String(row.count),
|
||||
})}
|
||||
</span>
|
||||
<span class="mass">
|
||||
{i18n.t("game.inspector.planet.ship_groups.row.mass", {
|
||||
mass: formatFloat(row.mass),
|
||||
})}
|
||||
</span>
|
||||
<div class="cells">
|
||||
<span class="class">{row.class}</span>
|
||||
<span class="count">{row.count} ×</span>
|
||||
<span class="mass">{formatFloat(row.mass)}</span>
|
||||
</div>
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
@@ -260,13 +254,12 @@ modes because the dropdown already names the active race.
|
||||
.row {
|
||||
display: block;
|
||||
font-size: 0.85rem;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.row > span,
|
||||
.row > .select {
|
||||
.cells {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
gap: 0.5rem;
|
||||
align-items: baseline;
|
||||
}
|
||||
.select {
|
||||
width: 100%;
|
||||
@@ -286,8 +279,15 @@ modes because the dropdown already names the active race.
|
||||
.class {
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
.count,
|
||||
.count {
|
||||
color: var(--color-text-muted);
|
||||
text-align: right;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.mass {
|
||||
color: var(--color-text-muted);
|
||||
text-align: right;
|
||||
font-family: var(--font-mono);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user