Skip to content

adds more content and free movement#121

Merged
skuhlmann merged 3 commits into
stagingfrom
feat/adventurer-map
Jun 22, 2026
Merged

adds more content and free movement#121
skuhlmann merged 3 commits into
stagingfrom
feat/adventurer-map

Conversation

@skuhlmann

@skuhlmann skuhlmann commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

This PR enables free character movement on the map dashboard using Tiled-authored map metadata.

Map Data Pipeline

  • Added Tiled source/export support under public/assets/map/maps/adventure/.
  • Added normalized runtime map manifest: map.json.
  • Added corepack pnpm map:convert to convert and validate Tiled JSON.
  • Added converter script: scripts/convert-tiled-map.mjs.
  • Added validator script: scripts/validate-map-manifest.mjs.
  • Removed the duplicate map-layers-json spike file.
  • Updated the spec with the new Tiled workflow and manifest contract.

Free Movement Runtime

  • Added mapManifest.ts to import and type the generated map manifest.
  • Added mapGeometry.ts for collision/proximity math:
    • source-pixel to percent conversion
    • point-in-polygon checks
    • foot-radius collision checks
    • blocked polygon checks
    • POI trigger detection
  • Added useFreeWalkMovement.ts:
    • WASD/arrow movement
    • diagonal speed normalization
    • collision against walkable/blocked polygons
    • wall sliding
    • sprite facing direction
    • mobile D-pad input API

Map UI Updates

  • Replaced path/node travel movement with free-walk movement.
  • Map markers now come from the generated manifest/Tiled POIs.
  • Added proximity prompt when the character is near a point of interest.
  • Added Enter/Space interaction for nearby POIs.
  • Added mobile D-pad controls.
  • Added optional debug overlay via ?mapDebug=1 to inspect collision, POIs, and spawn.

Tooling

  • Added packageManager: pnpm@10.18.3 so corepack pnpm resolves consistently.
  • Added map:convert package script.

Summary by CodeRabbit

Release Notes

  • New Features

    • Free-walk movement system with collision detection and navmesh navigation
    • On-screen D-pad controls for mobile and pointer-based input
    • Points of Interest proximity detection with interaction prompts
    • Debug visualization overlay for map geometry
    • Improved character movement with contextual status display
  • Documentation

    • Expanded map data pipeline specification and runtime manifest documentation
  • Chores

    • Added map conversion and validation scripts
    • Pinned package manager version

@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@skuhlmann, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 40 minutes and 41 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9519444b-6a07-44a9-95de-9875168c0ed9

📥 Commits

Reviewing files that changed from the base of the PR and between ac0f19c and ef9b473.

📒 Files selected for processing (6)
  • docs/map-dashboard-feature-spec.md
  • scripts/convert-tiled-map.mjs
  • scripts/validate-map-manifest.mjs
  • src/app/(frontend)/dashboard/map/MapDashboardClient.tsx
  • src/app/(frontend)/dashboard/map/mapConfig.ts
  • src/app/(frontend)/dashboard/map/useFreeWalkMovement.ts
📝 Walkthrough

Walkthrough

Adds a complete Tiled-to-runtime map pipeline for the adventure map: a Tiled source JSON, a CLI converter (convert-tiled-map.mjs), a manifest validator (validate-map-manifest.mjs), TypeScript manifest types and geometry utilities, a useFreeWalkMovement hook with RAF-driven collision, manifest-driven mapConfig, new MapDebugOverlay/MapInteractionPrompt components, a pointer D-pad HUD, and an updated E2E movement assertion.

Changes

Free-walk map movement pipeline and runtime

Layer / File(s) Summary
Tiled map data and pipeline scripts
public/assets/map/maps/adventure/adventure.tiled.json, public/assets/map/maps/adventure/map.json, scripts/convert-tiled-map.mjs, scripts/validate-map-manifest.mjs, package.json
Introduces the Tiled source map with spawn/walkable/blocked/POI layers and the pre-generated runtime map.json. Adds the converter and validator CLI scripts, and wires both under a new map:convert npm script with a pinned pnpm packageManager field.
Manifest types and geometry utilities
src/app/(frontend)/dashboard/map/mapManifest.ts, src/app/(frontend)/dashboard/map/mapGeometry.ts
Defines MapPixelPoint, MapPolygon, MapManifestPointOfInterest, and MapManifest types, exports the typed mapManifest singleton, and adds toPercentPoint, clampPointToMap, polygon/circle containment tests (pointInPolygon, circleInsidePolygon, circleIntersectsPolygon), canStandAtPoint, isPointInPOITrigger, and getNearestTriggeredPOI.
useFreeWalkMovement hook
src/app/(frontend)/dashboard/map/useFreeWalkMovement.ts
Implements a RAF-driven movement hook: held-direction set, normalized vector computation, delta-time tick loop, tryMove with diagonal/axis fallback via canStandAtPoint, setDirectionHeld for D-pad input, and global keyboard listeners. Returns direction, isMoving, renderPosition, sourcePosition, and setDirectionHeld.
Manifest-driven mapConfig
src/app/(frontend)/dashboard/map/mapConfig.ts
Replaces hardcoded mapBackgroundPath, mapSpawnPoint, and mapLocations with values derived from mapManifest. Adds optional actionLabel to MapLocationConfig and introduces POI-to-location-ID whitelists with percent-space coordinate conversion.
MapDebugOverlay, MapInteractionPrompt, and HUD styles
src/app/(frontend)/dashboard/map/MapDebugOverlay.tsx, src/app/(frontend)/dashboard/map/MapInteractionPrompt.tsx, src/app/(frontend)/dashboard/map/MapDialog.tsx, src/app/(frontend)/globals.css
Adds MapDebugOverlay (SVG rendering walkable/blocked polygons, POI triggers, spawn), MapInteractionPrompt (nearby POI label/region/action button), and a dialog kicker text change. Adds CSS for .map-dashboard-hud-bottom, .map-dpad grid, directional button grid areas, .map-interaction-prompt, and mobile responsive overrides.
MapDashboardClient wiring
src/app/(frontend)/dashboard/map/MapDashboardClient.tsx
Replaces useMapMovement with useFreeWalkMovement, adds isDebugOverlayVisible (mapDebug=1 query param), computes nearbyPOI/nearbyLocation via geometry utilities, adds keyboard Enter/Space location selection, renders MapDebugOverlay and MapInteractionPrompt, adds a pointer-driven D-pad HUD, updates MapSprite to use renderPosition, and simplifies travelToLocation to directly set activeLocationID.
E2E movement test and feature spec
tests/e2e/app.spec.ts, docs/map-dashboard-feature-spec.md
Updates verifyMapDashboard to assert sprite X-position increases after ArrowRight. Expands the feature spec with pipeline definitions, coordinate contracts, movement/collision model, POI proximity loop, validation requirements, and phased rollout plan.

Sequence Diagram(s)

sequenceDiagram
  participant User as User (keyboard / D-pad)
  participant MapDashboardClient
  participant useFreeWalkMovement
  participant mapGeometry as mapGeometry (canStandAtPoint)
  participant MapManifest as map.json manifest

  User->>MapDashboardClient: ArrowRight keydown / dpad pointer down
  MapDashboardClient->>useFreeWalkMovement: setDirectionHeld("right", true)
  useFreeWalkMovement->>useFreeWalkMovement: ensureAnimation → requestAnimationFrame
  loop RAF tick
    useFreeWalkMovement->>mapGeometry: canStandAtPoint(manifest, candidatePos)
    mapGeometry->>MapManifest: read walkable/blocked polygons
    MapManifest-->>mapGeometry: polygon data
    mapGeometry-->>useFreeWalkMovement: true / false
    useFreeWalkMovement->>useFreeWalkMovement: update sourcePosition, renderPosition
  end
  useFreeWalkMovement-->>MapDashboardClient: renderPosition, nearbyPOI (via getNearestTriggeredPOI)
  MapDashboardClient->>MapDashboardClient: show MapInteractionPrompt when nearbyPOI + nearbyLocation
  User->>MapDashboardClient: Enter/Space keydown
  MapDashboardClient->>MapDashboardClient: setActiveLocationID(nearbyLocation.id)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • raid-guild/portal#117: Implements the original /dashboard/map with MapDashboardClient and useMapMovement, which this PR directly refactors by replacing useMapMovement with useFreeWalkMovement and rewiring the movement/location interaction logic.

Poem

🐇 Hop, hop, across the realm I go,
No fixed nodes — just pixels below,
My feet-circle checks each polygon shape,
Blocked by lake? I slide and escape!
The D-pad glows, the manifest speaks,
Free-walking bunny finally seeks! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'adds more content and free movement' captures key aspects of the changeset—expanding documentation ('more content') and introducing free character movement. However, it's somewhat general and doesn't specifically indicate this is about Tiled-based map pipeline implementation or clarify the scope of changes across multiple systems.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/adventurer-map

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/map-dashboard-feature-spec.md`:
- Around line 750-752: The final decision statement in the spec contradicts the
implemented behavior documented in earlier sections. The decision currently
defers free movement implementation, but the spec's earlier sections describe
free movement as already implemented in the runtime behavior. Update the
decision text at lines 750-752 to reflect that free movement is implemented and
integrated with the Tiled pipeline and debug overlay, making it consistent with
how it is documented throughout the rest of the specification.

In `@scripts/convert-tiled-map.mjs`:
- Around line 107-112: The enabled property in the returned object defaults to
false when properties.enabled is undefined, causing new POIs to be silently
disabled. Modify the enabled property assignment to explicitly default to true
instead, so that POIs are enabled by default unless the properties.enabled value
is explicitly set to false. Use a conditional check or nullish coalescing
approach to achieve this default behavior.

In `@scripts/validate-map-manifest.mjs`:
- Around line 94-106: The POI validation loop iterating through
manifest.pointsOfInterest currently only validates that triggerRadius and
markerRadius are finite numbers but does not validate the trigger shape type or
its bounds. Add an assert statement to validate that poi.trigger.shape exists
and is either 'circle' or 'ellipse', then add a conditional check that when the
shape is 'ellipse', validates that the ellipse bounds (width and height
properties) are positive finite numbers. Place these new assertions after the
existing triggerRadius validation to ensure all POI trigger configuration is
properly validated before processing.
- Around line 84-92: The spawn validation at lines 84-92 only checks if the
center point (spawnPoint) is inside walkable polygons and outside blocked
polygons, but runtime movement requires the full spawn foot-radius circle to be
valid. To fix this, retrieve the spawn foot-radius value from the manifest and
modify the assertions to validate that the entire circular area around
spawnPoint (not just the center) fits within walkable space and avoids blocked
polygons. This may require checking multiple points along the circle perimeter
using pointInPolygon at the radius distance from the spawn center, or
implementing a proper circle-to-polygon collision check that validates the full
spawn footprint matches runtime collision rules.

In `@src/app/`(frontend)/dashboard/map/mapConfig.ts:
- Around line 166-167: The toMapNodeID function currently silently defaults
unknown node IDs to 'spawn' instead of surfacing configuration errors. Replace
the conditional fallback logic that returns 'spawn' for invalid values with an
error throw or assertion that fails fast when an unmapped node ID is
encountered, so that typos or stale poi.nodeId values in the manifest are caught
as contract violations rather than silently routing to the wrong node. Apply
this same fix to the other location mentioned at line 182.

In `@src/app/`(frontend)/dashboard/map/MapDashboardClient.tsx:
- Around line 96-100: The proximity activation via keyboard entry (Enter and
Space keys) in the code block around line 96-100 does not check if the
nearbyLocation is disabled before calling setActiveLocationID. Add a check for
the disabled property of nearbyLocation to prevent opening intentionally
disabled locations, similar to how marker clicks are handled. Apply the same
disabled location check to the other proximity activation entry point mentioned
at lines 234-239 to maintain consistent behavior across all ways to activate
locations.
- Around line 94-101: The onKeyDown handler within the useEffect is intercepting
Enter and Space key events globally without checking if the keyboard event
originated from an interactive element like a button or link. To fix this,
before calling event.preventDefault() and
setActiveLocationID(nearbyLocation.id), add a check to determine if the
event.target is an interactive control (such as HTMLButtonElement,
HTMLAnchorElement, HTMLInputElement, etc.). If the event originated from such a
control, return early to allow the control to handle its own keyboard behavior
rather than triggering the location action.

In `@src/app/`(frontend)/dashboard/map/useFreeWalkMovement.ts:
- Around line 24-28: The isEditableTarget function currently only detects
contenteditable elements with the exact value "true", which misses other valid
editable states like contenteditable="" (empty string),
contenteditable="plaintext-only", and inherited contenteditable from parent
elements. Update the selector in the closest() method to match any
contenteditable attribute regardless of its value, or better yet, use
isContentEditable property instead of the attribute selector, to properly detect
all editable content scenarios before preventing movement key behavior.
- Around line 185-192: In the useEffect hook that sets up the keydown and keyup
event listeners, add event listeners for 'blur' on the window and
'visibilitychange' on the document to clear the heldDirectionsRef state when
focus is lost or visibility changes. Create handlers for these events that reset
heldDirectionsRef (similar to how the keyboard handlers manage movement state),
and ensure these new listeners are properly removed in the cleanup function
along with the existing key event listeners. Update the dependency array to
include any new state or refs referenced in the new handlers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 94ae29de-f263-4872-9447-6cf7fd2078a2

📥 Commits

Reviewing files that changed from the base of the PR and between 93dff38 and ac0f19c.

📒 Files selected for processing (16)
  • docs/map-dashboard-feature-spec.md
  • package.json
  • public/assets/map/maps/adventure/adventure.tiled.json
  • public/assets/map/maps/adventure/map.json
  • scripts/convert-tiled-map.mjs
  • scripts/validate-map-manifest.mjs
  • src/app/(frontend)/dashboard/map/MapDashboardClient.tsx
  • src/app/(frontend)/dashboard/map/MapDebugOverlay.tsx
  • src/app/(frontend)/dashboard/map/MapDialog.tsx
  • src/app/(frontend)/dashboard/map/MapInteractionPrompt.tsx
  • src/app/(frontend)/dashboard/map/mapConfig.ts
  • src/app/(frontend)/dashboard/map/mapGeometry.ts
  • src/app/(frontend)/dashboard/map/mapManifest.ts
  • src/app/(frontend)/dashboard/map/useFreeWalkMovement.ts
  • src/app/(frontend)/globals.css
  • tests/e2e/app.spec.ts

Comment thread docs/map-dashboard-feature-spec.md Outdated
Comment thread scripts/convert-tiled-map.mjs
Comment thread scripts/validate-map-manifest.mjs
Comment thread scripts/validate-map-manifest.mjs
Comment thread src/app/(frontend)/dashboard/map/mapConfig.ts Outdated
Comment thread src/app/(frontend)/dashboard/map/MapDashboardClient.tsx
Comment thread src/app/(frontend)/dashboard/map/MapDashboardClient.tsx
Comment thread src/app/(frontend)/dashboard/map/useFreeWalkMovement.ts
Comment thread src/app/(frontend)/dashboard/map/useFreeWalkMovement.ts Outdated
@skuhlmann

Copy link
Copy Markdown
Contributor Author

Addressed the PR feedback. I verified each finding against current code; all were still valid, so none were skipped.

Fixes made:

  • Updated the spec decision to say free movement is now implemented with Tiled map.json, proximity prompts, controls, and debug overlay.
  • Changed POI conversion so enabled defaults to true unless explicitly false.
  • Strengthened manifest validation:
    • spawn now validates the full foot-radius footprint, not only center point
    • POI triggerShape must be circle or ellipse
    • ellipse POIs require positive finite trigger bounds
  • Changed toMapNodeID to throw on unknown node IDs instead of silently falling back to spawn.
  • Prevented global Enter/Space proximity activation from hijacking buttons/links/inputs/etc.
  • Prevented disabled nearby locations from opening through keyboard or prompt activation.
  • Improved editable-target detection for movement keys using isContentEditable and any [contenteditable].
  • Clears held movement keys on window blur and document visibility changes.

@skuhlmann
skuhlmann merged commit c60fdf1 into staging Jun 22, 2026
1 check 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