Skip to content

Draw quest objective areas on the world map instead of pins - #1

Open
snerror wants to merge 10 commits into
masterfrom
quest-areas
Open

Draw quest objective areas on the world map instead of pins#1
snerror wants to merge 10 commits into
masterfrom
quest-areas

Conversation

@snerror

@snerror snerror commented Aug 28, 2026

Copy link
Copy Markdown
Owner

What & why

CodexLite marks every objective spawn on the world map with a dot, which for a quest with dozens of spawns turns into a cloud of dots that says little about where to go. This PR draws each quest's objectives as translucent areas with a numbered badge, the way the retail quest map does: hovering shows the quest and its objective progress, clicking the badge opens the quest in the log. A quest whose targets sit in two separate places (the Duskwood example) gets two areas.

Main changes

  • map-area-geometry.lua (new, pure Lua): single-linkage clustering of objective coordinates, padded convex hull (monotone chain over octagons around the hull vertices, so even a single spawn yields a proper blob), centroid. Covered by tests/geometry_test.lua.
  • map-area.lua (new): pooled area frames as children of the map canvas so they zoom with it. Each hull is filled as a triangle fan of 1×1 textures deformed with Texture:SetVertexOffset — one uniform fill, no darker overlaps inside an area. Badge = ala's Circle texture + quest-log position. Rebuilds are debounced through the existing MT._TimerStart, and a rebuild while the map is closed is deferred to the next OnShow.
  • map.lua: skips the common/large pins on the world map while area mode is on, marks the area layer dirty wherever node tables or the map change, exports wm_wrap, forwards canvas scale changes so badges keep a readable size.
  • main.lua: records meta.ordinal (position among quests in the log) and redraws when it changes.
  • setting.lua + locales: worldmap_quest_area toggle (default on) under WorldMap; off restores the previous dots.

Trade-offs: the hover hit-area is the polygon's bounding box, not the polygon (simpler, and it never mattered for the retail blobs either). All quests share one fill colour, as requested; overlapping areas of different quests simply stack. The minimap is untouched. Cluster distance (8 map-%) and padding (1.5 map-%) are constants at the top of map-area.lua, tuned by eye and worth revisiting in-game.

What deserves attention

  • I could not run the game from the dev box, so the rendering layer has only been parse-checked; the geometry has unit tests. Three client APIs are used on the strength of the 1.15 client being the modern engine: Texture:SetVertexOffset, Frame:SetPropagateMouseClicks (guarded, so the map still drags through an area), and WorldMapFrame.ScrollContainer:OnMouseWheel (so zooming over an area keeps working).
  • Frame levels: fills are pinned one level below CodexLite's pin wrap so !/? icons stay above them; badges sit 12 above. If an icon ever renders under a fill, that offset is the knob.
  • QuestLog_SetSelection(index) is what Classic Era's own quest log uses; it expands a collapsed header itself.

How to test manually

  1. /console scriptErrors 1, /reload, open the map in a zone with active quests: objective dots are gone, each quest shows one or more areas with a numbered badge; the number matches the quest's position in the quest log.
  2. Hover an area and the badge: tooltip with title + - objective: n/m lines, finished ones green; the area brightens.
  3. Click the badge: the quest log opens on that quest (also with its header collapsed).
  4. Zoom and drag the map with the cursor over an area — both must keep working; areas stay aligned with the terrain.
  5. Kill/collect an objective to completion: its area disappears on the next update; abandon a quest: its areas vanish.
  6. Settings → WorldMap → untick Show quest areas instead of objective pins: the dots come back; tick again: areas return.
  7. Tests: PYTHONPATH=<dir with lupa> python3 tests/run_lupa.py (or lua5.1 tests/geometry_test.lua) → 18 passed, 0 failed.

🤖 Generated with Claude Code

snerror and others added 3 commits August 28, 2026 13:02
Replaces the per-spawn dots for quest objectives on the world map with
numbered areas, in the style of the retail quest POI blobs:

- Objective coordinates of each quest on the current map are grouped by
  single-linkage clustering (8 map-% threshold), so a quest whose targets
  live in two separate places gets two areas.
- Each cluster is drawn as its padded convex hull, filled as a triangle
  fan with Texture:SetVertexOffset, one uniform translucent colour for all
  quests. Areas are children of the map canvas so they zoom with it.
- A badge at the cluster centroid shows the quest's position in the quest
  log (main.lua now records meta.ordinal). Hovering an area or badge shows
  the quest title and its objective lines; clicking the badge opens the
  quest in the quest log via QuestLog_SetSelection.
- Areas rebuild, debounced, whenever the node tables or the map change;
  rebuilds while the map is closed are deferred to the next OnShow.
- New WorldMap setting "worldmap_quest_area" (default on) switches between
  areas and the previous dots. The minimap is unchanged.

Geometry (clustering, monotone-chain hull with octagon padding, centroid)
lives in map-area-geometry.lua and is covered by tests/geometry_test.lua,
runnable with lua5.1 or via tests/run_lupa.py when only Python is around.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Each quest's areas use one of eight palette colours keyed by its quest
  log position, so overlapping areas of different quests stay apart.
- Hulls get a thin outline in the quest colour so both boundaries remain
  visible where areas overlap.
- Hovering an area or its badge lights up every area of that quest, turns
  the badge gold with a dark number and adds a glow, so it is clear which
  quest a click would open.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Areas whose hulls overlap (separating-axis test, new
  MT.AreaPolygonsOverlap) are merged into one area with one badge per
  quest, so areas never overlap. Badges that would land on the same spot
  are pushed apart; the merged area takes the colour of its lowest quest.
- Hovering an area lights all its badges and lists every quest in the
  tooltip; hovering a badge lights that badge and every area holding its
  quest, and lists that quest only.
- The tooltip is anchored next to the cursor for areas and badges alike
  and follows the cursor while hovering, like the retail quest blobs.
- Fan apex is the hull's vertex mean, which is inside any convex polygon.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@snerror

snerror commented Aug 28, 2026

Copy link
Copy Markdown
Owner Author

Two follow-ups after the first in-game look, both on map-area.lua:

  • ba81c87 — per-quest colours (8-entry palette keyed by quest-log position), a thin outline along each hull, and hover feedback: hovering an area or its badge turns the badge gold with a glow and brightens every area of that quest.
  • a52e896 — areas whose hulls overlap are now merged into a single area carrying one badge per quest (separating-axis overlap test in map-area-geometry.lua, covered by 6 new tests), so areas never overlap on the map. The tooltip is anchored next to the cursor for badges as well and follows the cursor while hovering, matching the retail blobs.

The "overlapping areas simply stack" line in the description is therefore superseded: they merge instead.

Badges (one per quest cluster, position in the quest log) are always
visible and pushed apart when they would stack. A quest's shapes appear
only while its badge is hovered, with the tooltip beside the cursor, so
areas of different quests never compete and the map stays fully
interactive (the shapes take no mouse input).

Within a cluster there is now one shape per mob or object type, so a
quest shows where each of its targets actually lives instead of one
hull spanning all of them.

Area merging is no longer needed; MT.AreaPolygonsOverlap and its tests
are removed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@snerror

snerror commented Aug 28, 2026

Copy link
Copy Markdown
Owner Author

Third pivot, after another in-game round: the merged areas hid where each quest's targets actually are, so 714dfa9 switches to the retail model — badges always visible (spread apart when they'd stack), a quest's shapes only while its badge is hovered, tooltip beside the cursor. Shapes are now one per mob/object type inside a cluster, so a quest shows its specific spots rather than one hull across all of them. The shapes take no mouse input, so the map drags/zooms/clicks exactly as before; the overlap/merge code from the previous commit is gone again.

snerror and others added 4 commits August 28, 2026 15:12
Shapes are shown all the time again. Hovering a badge brings that
quest's shapes to full strength and in front while the other quests'
shapes dim, so overlapping areas resolve on hover instead of being
hidden until then.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Splitting a cluster by mob/object type stacked a dozen shapes for quests
whose item drops from many NPC types (Ghoulish Effigy). Back to a single
hull per cluster; a quest still gets separate shapes only when its
targets are in separate places.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The title uses the quest log's difficulty colour for the quest level
(GetQuestDifficultyColor) and shows the level in front, so hovering a
badge tells at a glance how dangerous the quest is.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The world map's quest clusters are converted to world yards and drawn
around the player on the minimap, clipped to the minimap shape with a
mask texture. Shapes are laid out once per zoom level and only shifted
while walking (a rotating minimap re-lays them out in place); shapes
beyond twice the visible radius are hidden. Hovering a shape focuses its
quest and shows the same tooltip beside the cursor; clicks and the wheel
fall through to the minimap. No badges on the minimap.

Shared pieces (quest colours, tooltip, vertex-offset shape drawing,
cluster collection) move behind MT.MapArea* so both maps draw the same.
Minimap objective pins are skipped while the new "minimap_quest_area"
setting (default on) is enabled.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@snerror

snerror commented Aug 28, 2026

Copy link
Copy Markdown
Owner Author

Minimap now gets the areas too (minimap-area.lua, new minimap_quest_area setting under MiniMap): same clusters as the world map converted to world yards, clipped to the round minimap with a mask texture, laid out once per zoom level and shifted while walking, hover = focus + tooltip, no badges. Clicks/wheel pass through to the minimap. Shared drawing/tooltip/colour code now lives behind MT.MapArea* in map-area.lua.

Things to watch in-game: CreateMaskTexture/AddMaskTexture clipping at the minimap edge, hover over a shape not stealing minimap pings, and behaviour with rotateMinimap on.

snerror and others added 2 commits August 28, 2026 17:40
The button only lifted CodexLite's temporary hide filter, which does
nothing unless the quest was hidden first. It now also opens the world
map on the map holding most of the quest's objective spots (its start or
turn-in spot when there are none) and puts the quest in focus there.
Focus is kept across rebuilds until the next badge hover ends.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Counting spots per map picked adjoining zones and continents, because
CodexLite copies every spot into those maps. Only an entity's home spots
count now (the first coord sharing each world-coord table), the player's
current zone wins when the quest has spots there, and a finished quest
falls back to the zone of its turn-in or start NPC.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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