Skip to content

Loom (Alpha) #3 of 4: top-down zone map surface#294

Closed
CoreyRDean wants to merge 1 commit into
coreyrdean/loom-atlasfrom
coreyrdean/loom-zonemap
Closed

Loom (Alpha) #3 of 4: top-down zone map surface#294
CoreyRDean wants to merge 1 commit into
coreyrdean/loom-atlasfrom
coreyrdean/loom-zonemap

Conversation

@CoreyRDean
Copy link
Copy Markdown
Collaborator

Summary

Third of four PRs building the Loom alpha. Stacked on top of #293 (loom-atlas), which is stacked on #292 (loom-skeleton).

Adds the second surface in the Loom alpha: a top-down 2D map of the zone selected from the atlas. Renders every waypoint, spawn point, trigger volume, and portal pulled from the Area type's fixed-size arrays, scaled to fit the visible area. Click a marker to select it.

Why 2D map and not literal 3D viewport

The Loom design's ""world scene"" is itself stylized 2D SVG with a fake 3/4 perspective — the design medium never assumed real 3D. ClientAreas.bb's LoadArea (which Loom would need for a true 3D render) is deeply entangled with GUE's UI substrate: GY_Cam, GY_CreateProgressBar, ResolutionType, RandomImages, GetMusicName$, GetTexture, the Gooey lib. Pulling all of that into Loom would lock the two editors together at the UI layer, which is exactly what Loom is supposed to decouple.

The 2D map ships now, does the alpha job, and stays faithful to the design's aesthetic. A literal 3D viewport can land as a beta refactor once LoadArea's data path is decoupled from its UI path (or once Loom defines its own zone-mesh loader).

What's in (src/Modules/Loom/ZoneMap.bb)

  • ZoneMap_Open(handle) — switches into map mode, recomputes the view's bounding box from the placed entities so the view scales to fit any size zone.
  • ZoneMap_RenderAndUpdate(sw, sh) — per-frame; paints the chrome + entities + selection ring, hit-tests the mouse, returns True when the user wants to go back to the atlas.
  • World->screen projection (X, Z) -> (px, py) with Z inverted so north renders up.
  • Marker styles per kind:
    • waypoint — small dust-gray dot
    • trigger — red diamond with parchment inner
    • spawn — brass ring with dark inner
    • portal — arcane-blue pin with portal name label below
  • Top ribbon: brand on the left, zone name centered, hover-styled < Back to Atlas button on the right.
  • Footer: selection hint that updates to show the current pick.
  • Pulsing arcane ring highlights the active selection.

State machine in Loom.bb

Two modes — LOOM_MODE_ATLAS and LOOM_MODE_MAP. Clicking a zone in the atlas calls ZoneMap_Open and flips to MAP. From MAP, the Back button or Esc returns to ATLAS. From ATLAS, Esc exits Loom. (Esc on MAP only returns to atlas, never exits Loom directly, so the user can't accidentally kill the editor mid-inspection.)

Blast radius

  • src/Loom.bb: include block + main loop changed to a two-mode state machine. LoomSelectedZone is now consumed by ZoneMap_Open instead of producing a toast.
  • src/Modules/Loom/ZoneMap.bb: new file.
  • No existing module modified.
  • All five engine targets compile clean.

Test plan

  • compile.bat builds clean.
  • Open Project Manager, click Loom (Alpha).
  • Atlas appears as before.
  • Click a zone card — view switches to the zone map.
  • Top ribbon shows LOOM / Zone Map on left, zone name centered, < Back to Atlas button on right.
  • Map area shows dots (waypoints), brass rings (spawns), red diamonds (triggers), blue pins (portals with labels).
  • Hover the Back button — border lifts to arcane blue.
  • Click Back, or press Esc — returns to atlas.
  • Pick a different zone — its map renders with its own scale.
  • Click a marker — pulsing arcane ring appears on it, footer updates to ""Selected: kind #N"".
  • Click another marker — selection moves.
  • Press Esc on atlas — Loom exits.

🤖 Generated with Claude Code

Adds the second surface in the Loom alpha: a top-down 2D map of the
zone selected from the atlas. Renders every waypoint, spawn point,
trigger volume, and portal pulled from the Area type's fixed-size
arrays, scaled to fit the visible area. Click a marker to select it.
The selection sets ZoneMap_SelectedKind$ / ZoneMap_SelectedIndex,
which PR #4's composer panel will consume.

Why a top-down 2D map and not a literal 3D viewport:
  The Loom design's "world scene" is stylized 2D SVG with a fake 3/4
  perspective -- the design medium never assumed real 3D. ClientAreas's
  LoadArea (which Loom would need for a true 3D render) is deeply
  entangled with GUE's UI substrate: GY_Cam, GY_CreateProgressBar,
  ResolutionType, RandomImages, GetMusicName$, GetTexture, the Gooey
  lib. Pulling all of that into Loom would lock the two editors
  together at the UI layer, which is exactly what Loom is supposed to
  decouple. The 2D map ships now, does the alpha job, and stays
  faithful to the design's aesthetic. A literal 3D viewport can land
  as a beta refactor once LoadArea's data path is decoupled from its
  UI path (or once Loom defines its own zone-mesh loader).

What's in (src/Modules/Loom/ZoneMap.bb):
  - ZoneMap_Open(handle) -- switches into map mode, recomputes the
    view's bounding box from the placed entities so the view scales
    to fit any size zone.
  - ZoneMap_RenderAndUpdate(sw, sh) -- per-frame; paints the chrome
    + entities + selection ring, hit-tests the mouse, returns True
    when the user wants to go back to the atlas.
  - World->screen projection (X, Z) -> (px, py) with Z inverted so
    north renders up.
  - Marker styles per kind:
      waypoint -> small dust-gray dot
      trigger  -> red diamond with parchment inner
      spawn    -> brass ring with dark inner
      portal   -> arcane-blue pin with portal name label below
  - Top ribbon: brand on the left, zone name centered, hover-styled
    "< Back to Atlas" button on the right.
  - Footer: selection hint that updates to show the current pick.
  - Pulsing arcane ring highlights the active selection.

State machine in Loom.bb:
  Two modes -- LOOM_MODE_ATLAS and LOOM_MODE_MAP. Clicking a zone in
  the atlas calls ZoneMap_Open and flips to MAP. From MAP, the Back
  button or Esc returns to ATLAS. From ATLAS, Esc exits Loom. (Esc
  on MAP only returns to atlas, never exits Loom directly, so the
  user can't accidentally kill the editor mid-inspection.)

All five engine targets compile clean. Loom.exe grew from 2.3 MB
(atlas) to 2.35 MB (atlas + map).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CoreyRDean CoreyRDean requested a review from a team as a code owner May 26, 2026 21:54
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d4452c785d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// Waypoints firstFound (drawn underneath everything else)
Local i = 0
For i = 0 To 1999
If ZM_Area\WaypointX#[i] <> 0.0 Or ZM_Area\WaypointZ#[i] <> 0.0
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Filter waypoints by slot validity, not coordinate value

Using WaypointX/Z != 0 to decide whether a waypoint exists will misclassify real data: valid waypoints placed at (0, *, 0) are dropped, while deleted waypoints can remain visible because deletion resets PrevWaypoint/links but does not clear stored coordinates (see waypoint deletion flow in GUE.bb). Since the same predicate is used for map content and bounds, stale coordinates can also distort the computed zoom/extent for the whole zone. This should follow the same slot-validity rule used by the editor/runtime (e.g., PrevWaypoint != 2005) rather than coordinate non-zero checks.

Useful? React with 👍 / 👎.


// Spawns
For i = 0 To 999
If ZM_Area\SpawnActor[i] > 0
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate spawn markers on SpawnMax instead of SpawnActor

This loop treats any slot with SpawnActor > 0 as an active spawn, but area tooling and server logic use SpawnMax as the enable/disable flag (GetSpawnPoint and spawn update paths check SpawnMax > 0). In GUE, disabling a spawn often sets SpawnMax to 0 without clearing actor metadata, so the map will render and allow selecting spawns that are actually disabled in gameplay.

Useful? React with 👍 / 👎.

@CoreyRDean
Copy link
Copy Markdown
Collaborator Author

Closing this PR. After landing #292 (skeleton + Project Manager launcher), the user's feedback on the alpha was that the zone-only browse model wasn't useful enough -- the project's actors, items, spells, and factions were loaded into memory but never surfaced. Pivoting to an entity browser + threads model: every entity kind browsable, composer renders every kind, reference fields become clickable chips that jump and leave a back-stack trail. That replaces the zone atlas, zone map, and zone-only composer this PR series built. New work lands as one PR off develop.

@CoreyRDean CoreyRDean closed this May 27, 2026
@CoreyRDean CoreyRDean deleted the coreyrdean/loom-zonemap branch May 27, 2026 00:02
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