Skip to content

Movers step 1: legal routing with roads, travel mode and pace - #182

Merged
wvaske merged 1 commit into
mainfrom
movers-step1-routing
Oct 24, 2026
Merged

wvaske merged 1 commit into
mainfrom
movers-step1-routing

Conversation

@wvaske

@wvaske wvaske commented Oct 24, 2026

Copy link
Copy Markdown
Owner

Step 1 of the movers plan (routing fix, mode/pace, roads).

  • Routing: shared/rules/journey.ts — A* over the whole map (fog ignored) honouring the mover's medium (land/water/air vs terrain) and impassable edges; cost tiers road 1 / known 2 / unknown 3, times edge effort. A leg with no legal route falls back to the straight line. Party routing and the client preview share the step cost, so the party prefers roads within explored ground.
  • Roads: Trail tool gets a Tracks/Road switch. Road trails have no gate, are skipped by the tracks engine, reach players clipped to non-hidden fog, and draw as a solid way for everyone. Integration POST …/trails accepts kind.
  • Mode + pace: tokens and retinues store travelMode / pace (token → retinue → foot/normal). Custom modes are campaign customTravelModes (new medium field) added from the Tokens panel. mph column kept, unread.
  • Tests: journey.test.ts (shared), routing + player road filtering in retinue-travel.test.ts; suite green (shared 129, server 327).

🤖 Generated with Claude Code

…lace mph

Step 1 of the movers plan. NPC journeys now get the shortest LEGAL path to
each waypoint instead of a straight line through fog: shared
`rules/journey.ts` runs A* that ignores fog but respects the mover's travel
medium (land cannot enter water, water only water/coast, air anything) and
impassable edges, with integer cost tiers — road 1, known off-road 2,
unknown off-road 3 — times the edge effort. Party routing and the client's
preview use the same step cost, so the party prefers roads on known ground.

Roads are trails with `kind: 'road'` (Trail tool → Road): no gate, no
signs, skipped by the track engine, delivered to players clipped to known
fog, drawn as a solid way for everyone.

Tokens and retinues carry `travelMode` + `pace` (resolving token → retinue →
foot/normal) instead of a bare mph; custom modes are campaign
customTravelModes, now with a medium, added from the Tokens panel's
"Custom…" form. The mph column stays but is unread. Seed roster converted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-24T20:39:45.409265Z 6c420e5 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@wvaske
wvaske merged commit e06bb5b into main Oct 24, 2026
1 check passed

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6c420e5279

ℹ️ 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".

trails: mapState.trails
.filter((t) => t.kind === 'road')
.map((t) => ({ ...t, dmNotes: '', cells: t.cells.filter((c) => fogAt(c.q, c.r) !== 'hidden') }))
.filter((t) => t.cells.length > 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 Keep filtered road snapshots schema-valid

When a player has explored exactly one cell of a road, this filter emits a trail with one cell, but TrailSchema requires at least two. The client validates snapshots with ServerMessageSchema.safeParse, so it rejects the entire snapshot and can remain blank or stale until a second road cell becomes known. Either omit one-cell road fragments or introduce a player-road schema that permits them.

Useful? React with 👍 / 👎.

Comment thread scripts/retinues.json
}
]
}
}\n No newline at end of file

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 Remove the literal escape from the roster JSON

The file ends with the literal characters \n after the closing brace, so JSON.parse in seed-retinues.mjs throws before making any API request. Invoking the documented seed command with its default roster is therefore completely broken.

Useful? React with 👍 / 👎.

Comment on lines +328 to +330
<TravelPicker
mode={token.travelMode}
pace={token.pace}

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 Expose travel mode before routing a plain NPC

For a freshly created ordinary NPC, retinue is absent, route is empty, and note is empty, so the enclosing condition prevents this new TravelPicker from rendering. The DM must consequently plan the first route as foot; changing the mode afterward does not recalculate the stored route, making it impossible through the normal UI to directly plan a ship or flying route for such a token.

Useful? React with 👍 / 👎.

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