Skip to content

development → staging - #748

Merged
gaidheal1 merged 8 commits into
stagingfrom
development
Aug 10, 2026
Merged

development → staging#748
gaidheal1 merged 8 commits into
stagingfrom
development

Conversation

@gaidheal1

Copy link
Copy Markdown
Member

User-visible improvements (UVIs)

Features

  • Village maps now show communal squares as their own distinct space on the map

Fixes and UX improvements

  • Map now loads and pans more smoothly, with less stutter while dragging or zooming
  • The map centres faster after logging in, with less flicker on first load

Developer experience and quality

  • import_village now auto-picks an unused layout slot instead of requiring one to be specified manually

Technical notes

No migrations or env var changes. Map rendering now only rebuilds the changed GeoJSON source instead of the whole map source per frame. Initial-centre and world-bounds map data are now prefetched right after login.

Test plan

  • CI passes
  • Smoke test on staging after deploy

gaidheal1 and others added 8 commits August 10, 2026 00:40
…entre

Map.tsx's walker animation loop rebuilt the entire GeoJSON source
(buildings/roads/fields/boundaries) every 16ms even though only
character positions change per frame. Split buildVillageSourceData into
buildStaticVillageFeatures (memoized, rebuilt only when features change)
and buildCharacterPointFeatures (per-frame). The animation loop is now
also gated on there actually being walking characters, instead of
running unconditionally.

Separately, useMap.ts's initial-camera logic assumed a single seeded
village and picked "the first population centre" from the full
population-centres list, which is now stale with multiple villages
imported from locations/data/. Added a lightweight
InitialMapCentreView (/map/initial-centre/) that returns just enough
(id/name/bbox) to frame the camera, preferring the population centre of
the requesting player's actively linked character
(Player.active_link) and falling back to the lowest-pk centre
otherwise. Frontend now fetches this cheaply before the bbox-scoped
viewport poll takes over as the source of truth.

Backend: locations/tests/test_initial_map_centre_view.py (3 tests).
Frontend: MapPage.test.tsx updated for the new fetchInitialMapCentre
mock; also fixed a flaky prefetch effect that used a dynamic import()
for fetchPopulationCentreMap, which could race and skip the vi.mock
when prefetching two different villages concurrently - now a normal
static import.
Watabou's village generator can export a "squares" MultiPolygon (open
outside communal space, e.g. a market square/plaza) that
import_watabou_village previously ignored entirely, like "greens" and
"prisms" still are.

Add a "square" Subzone.usage choice and a _import_squares helper
(watabou_import.py) that imports it the same way _import_fields
already imports "fields" - one LandArea wrapping the union of the
polygons, one Subzone per polygon - refactored the shared logic into
_import_polygon_subzones. Unlike a crops Subzone, a square has no
FieldCrop growth cycle or other economy behaviour attached; it's
purely a map feature.

Wire it into both map views (PopulationCentreMapView,
MapViewportView): they now query Subzone with
usage__in=["crops", "square"] instead of usage="crops" alone, since
SubzoneFeatureSerializer already returns None for every crop_* field
when there's no attached FieldCrop.

Frontend: styledPolygonFeatures gives usage="square" subzones a
distinct warm/paved fill (geojson.tsx) instead of falling through to
the crops-green/building-grey styling, and the tooltip labels them
"Square" instead of the raw bookkeeping name.
When --x/--y are omitted, import_village now picks the first
VILLAGE_LAYOUT slot with no existing PopulationCentre on it, instead
of requiring coordinates to be hand-picked. Passing only one of --x/--y
now raises a clear CommandError instead of silently doing something
unintended.

This lets ad-hoc imports outside the setup_world/import_villages
pipeline (e.g. trying out a village file not in locations/data/) claim
spare grid space without colliding with the pipeline's own slots -
though it's not persistent across a setup_world rerun, which wipes and
reimports only locations/data/'s contents.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C6BX7dFJWn2xMYn9qggdos
GameContext now primes useInitialMapCentre's and useMapWorldBounds's
query cache entries as soon as fetch_info resolves, instead of only
firing them once the player navigates to the map page. Both are
cheap, one-shot fetches, so warming them at login lets MapPage skip a
network round-trip on mount for players who go on to open it.

Extracted the {queryKey, queryFn, staleTime, gcTime} for each into
exported query-option objects in useMap.ts, reused by both the hooks
and GameContext's prefetch, so the two can't drift onto different
cache keys.

Deliberately doesn't prefetch /map/viewport/: it needs a bbox only the
mounted map component can produce, and starts a 2s poll once enabled -
prefetching it at login would poll map data in the background for
every session regardless of whether the player ever opens the map.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C6BX7dFJWn2xMYn9qggdos
…al-centre

perf: stop rebuilding map source per frame, fix stale initial centre, prefetch at login
feat: import watabou "squares" as communal-space Subzones
feat: auto-pick an unused village_layout slot for import_village
# Conflicts:
#	frontend/src/pages/MapPage/MapPage.tsx
@gaidheal1
gaidheal1 merged commit 8aec23c into staging Aug 10, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant