|
| 1 | +# Mayor's Seal GUI rebuilt on the CUI framework with town statistics |
| 2 | + |
| 3 | +- Time: `2026-07-28 20:24:14 +0800` |
| 4 | +- Author: `Kimi-K3 coding agent` |
| 5 | +- Status: `completed` |
| 6 | +- Scope: `item/townmanager`, `content/town` (data), lang files |
| 7 | + |
| 8 | +## Completed |
| 9 | + |
| 10 | +- Rewrote `item/townmanager/TownManagerScreen` from a vanilla `Screen` (with |
| 11 | + legacy `chorda.client.widget` buttons and todo-ridden placeholder modes) to a |
| 12 | + Chorda CUI `PrimaryLayer`, visually matching town building GUIs: |
| 13 | + `townworkerblock.png` 176x222 frame + left-side `TabImageButtonElement` tabs. |
| 14 | +- Four tabs (`TownManagerTab` subclasses, opened client-only via |
| 15 | + `CUIScreenWrapper.open`, no container menu): |
| 16 | + - Town Overview (`TownOverviewTab`, reuses `tabs/TownInfoPanel`): town name, |
| 17 | + population, building/workable counts, average health/mental, |
| 18 | + homeless/unemployed counts, day-over-day deltas from history. |
| 19 | + - Residents (`TownResidentsTab` + `TownResidentsPanel`): scrollable resident |
| 20 | + list + detail (attributes, education, house/work assignment with localized |
| 21 | + building names, work proficiencies). Mirrors `TownWorkforcePanel` layout. |
| 22 | + - Town Buildings (`TownBuildingsTab` + `TownBuildingsPanel`): building list |
| 23 | + (unworkable shown red) + detail (type, coordinates, workable state with |
| 24 | + failure reasons, resident capacity via `ITownResidentBuilding`). |
| 25 | + - Statistics (`TownStatisticsTab` + `TownStatisticsPanel`): three line |
| 26 | + charts (population auto-scaled; avg health/mental fixed 0-100) with latest |
| 27 | + value + delta, middle reference line, scale labels, collecting hint when |
| 28 | + history has fewer than 2 entries. |
| 29 | +- Added `content/town/TownHistoryEntry` (record + Codec): daily snapshot |
| 30 | + (day, population, avgHealth, avgMental, buildings). `TeamTownData` now keeps |
| 31 | + up to 30 entries (`MAX_HISTORY_ENTRIES`), recorded at the end of |
| 32 | + `tickMorning` (same-day settlements overwrite), persisted through the |
| 33 | + existing CODEC field `history` and synced to clients by the existing |
| 34 | + per-tick `TeamTownDataS2CPacket` full sync. `TeamTown#getHistory` exposes it. |
| 35 | +- `TownManagerClientHelper.openScreen()` now opens via `CUIScreenWrapper`. |
| 36 | +- Added `gui.frostedheart.town_manager.*` keys (zh_cn + en_us), including |
| 37 | + per-building-type names under `...town_manager.building.*` with |
| 38 | + `translatableWithFallback` fallback to the class simple name. |
| 39 | + |
| 40 | +## Decisions |
| 41 | + |
| 42 | +- History rides the existing full-data sync instead of a new packet: 30 small |
| 43 | + entries are negligible and no sync cadence changes were needed. |
| 44 | +- Item GUI stays client-only (no Menu/NetworkHooks) because the seal is a |
| 45 | + read-only observer; this matches how the old screen and EditUtils work. |
| 46 | +- Panels read fresh data through `Supplier<TeamTown>/Supplier<TeamTownData>` |
| 47 | + every render, so the GUI follows sync updates live; selection is normalized |
| 48 | + by UUID/BlockPos when entries disappear. |
| 49 | +- Old `town_manage_screen.png` texture is now unreferenced but left in the |
| 50 | + asset tree untouched. |
| 51 | + |
| 52 | +## Validation |
| 53 | + |
| 54 | +- `JAVA_HOME='C:\Program Files\Java\jdk-17' ./gradlew build --offline` passed. |
| 55 | + NOTE: system JAVA_HOME points to JDK 11 and makes Gradle worker daemons |
| 56 | + crash with `GradleWorkerMain` ClassNotFoundException; always set JDK 17. |
| 57 | +- Both lang JSON files parse; `git diff --check` clean. |
| 58 | +- No references to the removed old screen API remain. |
| 59 | + |
| 60 | +## Remaining |
| 61 | + |
| 62 | +- Not run in game: verify tab hit areas, scrollbar feel, chart readability, |
| 63 | + and text widths at common GUI scales. |
| 64 | +- Statistics need two daily settlements before charts appear (by design). |
| 65 | +- Pre-existing issue noticed but untouched: `TeamTownData` codec constructor |
| 66 | + ignores the decoded `labour`/`maxLabour` (assigns 0), so labour values reset |
| 67 | + on save reload. |
| 68 | + |
| 69 | +## Follow-up: HouseBuilding resident count fix (20:51) |
| 70 | + |
| 71 | +`TownBuildingsPanel` initially used `ITownResidentBuilding.getResidentsID()` to |
| 72 | +display resident counts. HouseBuilding's CODEC does not serialize |
| 73 | +`residentsUUID` (only `maxResident`; the `HouseMenu` works around this by |
| 74 | +filtering `Resident.housePos`). This caused houses to always show 0/X on the |
| 75 | +client. Fixed by counting residents in the position-based way: |
| 76 | + |
| 77 | +``` |
| 78 | +boolean isHouse = !(building instanceof ITownResidentWorkBuilding); |
| 79 | +for (Resident r : town.getAllResidents()) |
| 80 | + if (pos.equals(isHouse ? r.getHousePos() : r.getWorkPos())) |
| 81 | + count++; |
| 82 | +``` |
0 commit comments