Region hover/selected shows current owner, not stale parent-country code (v0.18.0) #33
@@ -2,6 +2,9 @@
|
||||
|
||||
All notable changes to **History** are listed here, newest first.
|
||||
|
||||
## v0.18.0 — 2026-10-13 18:54
|
||||
- **Fixed a stale-owner display bug: the region hover tooltip and the dossier "Selected" label no longer print the static parent-country code (e.g. "FRA").** The GeoJSON `properties.parent` is a *static geographic-origin* field that never updates when a country is taken, yet it was rendered in the hover tooltip (as a sub-line and as a title fallback) and in the dossier's `Selected:` label (as a `(FRA)` suffix) — so a sub-region of a transferred country still displayed the old country's code next to the (correct) new owner. The tooltip now shows only the region name + the live owner chip (from the RegionMap) + key stats, and the dossier label shows only the region name. Ownership resolution still keys on `properties.parent` (unchanged) — only the display of it was removed.
|
||||
|
||||
## v0.17.0 — 2026-10-13 15:13
|
||||
- **Filled the no-one's-land left between merged sub-regions (Part of #16).** The v0.14 coarsening's boolean dissolves left slivers of each country's landmass inside no region — measured at **≈ 3.90M km² globally** (the union of a country's regions covered only ~98% of its landmass). The new `scripts/fill-gaps.mjs` (`npm run fill:regions`) closes them: per country it computes the gap (`landmass − union(its regions)`, `@turf/difference`+`union`), drops slivers < **25 km²** (66,637 of them — sub-pixel on the map), and assigns each remaining piece to the **nearest region** (centroid distance) as an appended MultiPolygon part. **2,696,811 km²** of no-one's-land was filled into **1,575 regions** (+6,768 parts: 5,026 → 11,794), leaving **63,889 km²** residual (**98.4% reduction** — the rest is coastline digitization fuzz and 14 tiny border-band overlap artifacts, see below). The named "messy" regions absorb their surroundings: **SI-048 Kočevje (SVN) 16,692 → 19,816 km² (57 → 90 parts)**, **MK-36 Kavadarci (MKD) 21,297 → 25,190 km² (8 → 17)**, **AZ-AGC Ağcabədi (AZE) 18,657 → 32,065 km² (7 → 14)**; the biggest single fills are **AQ-X01~ (Antarctica) +195,351 km²**, **CA-NU (Nunavut) +143,447**, **AQ-X02~ +142,615**, **CL-MA (Magallanes) +81,806**, **US-AK (Alaska) +63,830**, **CA-NT (NWT) +46,333**, **GL-UO (Kommuneqarfiup) +41,971**, **RU-KAM (Kamchatka) +41,911**.
|
||||
- **Correct, not just greedy: clipping, hole policy, dedup.** Each gap piece is clipped sequentially against (a) all *other countries'* regions, (b) its own country's existing regions, and (c) pieces already appended (bbox-touching) — so a piece straddling a border is split, and no piece can double-cover another region's area (**1,149,841 km² of foreign overhang removed** this way; 0 clip failures). **FR-57 Moselle (FRA) gains nothing on purpose**: its apparent gap was a cross-border phantom consumed by the neighbor clip — correct behavior. Holes follow a keep-or-drop policy: a gap hole whose centroid matches a **pre-existing hole** (within 10 km) or lies inside another country's landmass is kept, otherwise it's a phantom and dropped — so the **KZ-KZY Aral hole survives** (5,858 km² planar, the file's only hole; 5,875 km² of water kept open). A global dedup set guarantees each cleaned piece is appended at most once.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "history",
|
||||
"version": "0.17.0",
|
||||
"version": "0.18.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "AI-driven alternate-history simulator — the LLM is the world engine (Pax Historia style).",
|
||||
|
||||
+5
-4
@@ -590,8 +590,10 @@ function GameScreen({
|
||||
// Which nation's dossier to show. Defaults to the player; remounts per game.
|
||||
const [viewingId, setViewingId] = useState(game.playerNationId);
|
||||
// When the dossier target was set by a map click, remember which sub-region
|
||||
// (its own name + parent country) was clicked, for a small label in the
|
||||
// dossier. Cleared when the user picks a nation via the manual Inspect select.
|
||||
// was clicked, for a small label in the dossier. The label shows only the
|
||||
// region's name — the static parent-country code (which never updates on a
|
||||
// transfer) is no longer displayed, though the info object still carries it.
|
||||
// Cleared when the user picks a nation via the manual Inspect select.
|
||||
const [selectedRegion, setSelectedRegion] = useState<{ parent?: string; name?: string } | null>(null);
|
||||
const focusNation = useCallback(
|
||||
(id: string, info?: { parent?: string; name?: string }) => {
|
||||
@@ -779,8 +781,7 @@ function GameScreen({
|
||||
</Select>
|
||||
{selectedRegion && (
|
||||
<div className="mt-1.5 truncate text-[11px] text-muted-foreground" data-testid="selected-region">
|
||||
Selected: {selectedRegion.name ?? selectedRegion.parent ?? 'region'}
|
||||
{selectedRegion.parent ? ` (${selectedRegion.parent})` : ''}
|
||||
Selected: {selectedRegion.name ?? 'region'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+11
-11
@@ -174,19 +174,20 @@ function statRow(label: string, value?: number): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact hover tooltip: the sub-region's name, its parent country (ISO_A3), its
|
||||
* owner (color-chipped), and 1–2 key stats. When the parent country has no
|
||||
* owning nation, a minimal "No owner" card is shown instead.
|
||||
* Compact hover tooltip: the sub-region's name, its **current owner**
|
||||
* (color-chipped), and 1–2 key stats. When the region's country has no owning
|
||||
* nation, a minimal "No owner" card is shown instead. The static
|
||||
* parent-country code (`properties.parent`) is deliberately NOT printed here —
|
||||
* it never updates when ownership is transferred, so only the live owner chip
|
||||
* (resolved from the RegionMap) is shown.
|
||||
*/
|
||||
function hoverHtml(subRegionName: string, parentCode: string, region?: RegionOwner, nation?: Nation): string {
|
||||
function hoverHtml(subRegionName: string, region?: RegionOwner, nation?: Nation): string {
|
||||
if (!region || !nation) {
|
||||
return `<div class="hm-popup"><div class="hm-popup-title">${esc(subRegionName || parentCode || 'Unclaimed')}</div><div class="hm-popup-sub">No owner</div></div>`;
|
||||
return `<div class="hm-popup"><div class="hm-popup-title">${esc(subRegionName || 'Unclaimed')}</div><div class="hm-popup-sub">No owner</div></div>`;
|
||||
}
|
||||
const stats = [statRow('Mil', nation.stats.military), statRow('Eco', nation.stats.economy)].join('');
|
||||
const sub = parentCode ? `<div class="hm-popup-sub">${esc(parentCode)}</div>` : '';
|
||||
return `<div class="hm-popup">
|
||||
<div class="hm-popup-title">${esc(subRegionName || region.owner)}</div>
|
||||
${sub}
|
||||
<div class="hm-popup-owner"><span class="hm-chip" style="background:${region.color}"></span>${esc(region.owner)}</div>
|
||||
<div class="hm-stats">${stats}</div>
|
||||
</div>`;
|
||||
@@ -221,7 +222,7 @@ interface Props {
|
||||
* layer (z-0) behind the floating HUD windows: a dark-navy basemap with subtle
|
||||
* terrain, per-nation stable fills (uniform per country, the choropleth is the
|
||||
* star), thin sub-region borders, a brighter/thicker amber border for the
|
||||
* player's countries, a hover tooltip (sub-region name + parent country + owner
|
||||
* player's countries, a hover tooltip (sub-region name + current owner
|
||||
* + key stats), a **click that switches the dossier to the sub-region's parent
|
||||
* country's owning nation** (no popup — see #7), a transient flash when a
|
||||
* country's owner changes (lighting up all its sub-regions), and a per-scenario
|
||||
@@ -355,9 +356,8 @@ export default function WorldMap({
|
||||
const onHover = (e: maplibregl.MapLayerMouseEvent) => {
|
||||
const f = e.features?.[0];
|
||||
if (!f) return;
|
||||
const p = f.properties as { name?: string; regionId?: string; parent?: string };
|
||||
const p = f.properties as { name?: string; regionId?: string };
|
||||
const subId = typeof p?.regionId === 'string' ? p.regionId : '';
|
||||
const parent = typeof p?.parent === 'string' ? p.parent : '';
|
||||
if (subId && subId !== lastHoverId) {
|
||||
lastHoverId = subId;
|
||||
const geo = baseGeoRef.current;
|
||||
@@ -373,7 +373,7 @@ export default function WorldMap({
|
||||
const nation = region ? nationsRef.current[region.ownerId] : undefined;
|
||||
hoverPopup
|
||||
.setLngLat(e.lngLat)
|
||||
.setHTML(hoverHtml(p?.name ?? parent ?? subId, parent, region, nation))
|
||||
.setHTML(hoverHtml(p?.name ?? subId, region, nation))
|
||||
.addTo(map);
|
||||
map.getCanvas().style.cursor = 'pointer';
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user