Skip to content

Turn roucli into a real trade route CLI and add a route builder library - #15

Open
stevenbg wants to merge 7 commits into
P3Modding:masterfrom
stevenbg:routes
Open

stevenbg wants to merge 7 commits into
P3Modding:masterfrom
stevenbg:routes

Conversation

@stevenbg

@stevenbg stevenbg commented Aug 19, 2026 •

Copy link
Copy Markdown

I'm working on a much bigger mod that automates route and price generation. At the start I played around with .rou files and fleshed out the roucli.


This extends p3-rou from a decompress-only tool into a full workflow for .rou files, with the format findings verified against game-saved routes.

roucli commands

  • dump <file> — decode a compressed or uncompressed .rou and print its stops: towns, flags, per-ware operations in in-game units, and the instruction order when it differs from the default.
  • generate <route.toml> -o <file> — build a route from a TOML description (load/unload/sell/buy per stop).
  • write --type 5stop|6stop|suck --citizens N --load-town X --sell-town Y -o <file> — town supply/collection route templates from a bundled reference goods table, scaled to a citizens count.

Generated files are uncompressed (negative length header), so loading them in-game requires mod-fix-uncompressed-trade-route-loading. decompress now handles the uncompressed case (resolves the existing TODO), so dump can read back generated files.

Format semantics, verified against game saves

  • Per stop and ware, the operation is encoded in the signs: load office→ship = price 0/+amount, unload = price 0/−amount, sell to town = +min_price/+amount, buy from town = −max_price/+amount.
  • Amounts are raw units (in-game units × ware scaling), 1_000_000_000 = "as much as possible".
  • The action byte carries the repair flag (R = 0x01, X = 0x00, "−" = 0x09) plus 0x04 on the route's first stop.
  • The per-stop order array is the stop's instruction order (default = the alphabetical ware listing); the 6stop type uses it to run unloads before loads.
  • Town indices are savegame-specific (founded towns append to the list); dump is the practical way to find them.

Library

The construction logic lives in a new builder module (stop assembly, flags, ordering, the three route types), so mods can build routes in-game from the same code the CLI uses.

Tests: cargo test --target=i686-pc-windows-msvc (runs from WSL via interop too, e.g. cargo xwin test -p p3-rou).

stevenbg and others added 7 commits August 16, 2026 22:03
- roucli dump <file>: decode compressed or uncompressed .rou files and
  print stops with flags, ware names, in-game amounts and operations
- roucli generate <toml> -o <file>: build an uncompressed .rou from a
  TOML route description (buy/sell/load/unload, R/X/- flags, first-stop
  marker, ware scaling), verified against game-saved routes
- decompress: support uncompressed (negative length) files

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Scales a built-in reference goods table (quantities per 1000 citizens,
minimum sell prices) by a --citizens parameter and emits a two-stop
template route: load from office, sell to town. Towns are placeholders
to be reassigned in-game.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The game wipes office transfer orders at route load time if the stop's
town has no trading office, so the load stop must point at an office
town from the start. Town indices accept decimal or 0x-prefixed hex,
matching dump output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- 5stop: load calculated supply, sell max at supply prices, reset the
  target office stock to the calculated quantities, haul surplus home
- 6stop: same, but swaps the office stock one unit category at a time
  to bound the needed ship space, with unloads ordered before loads via
  the per-stop instruction order array
- suck: park in one town, unload everything (with repair), then five
  stops buying all goods at the reference buy prices
- reference table now covers all trade wares with supply_price,
  buy_price and sell_price; keys are exact WareId identifiers
- repair flag on first stops, --citizens defaults to 1000

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The third price column was parsed and validated but never used by any
route type; drop it. Rename the remaining supply_price to sell_price -
supply routes use it, but it is simply a minimum sell price - and fix
the README's stale lowercase ware keys left over from the switch to
exact WareId identifiers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The new builder module owns the verified format semantics - operation
sign encodings, flag bytes, the first-stop marker, MAX sentinel, ware
scaling, the instruction-order array with unloads-before-loads
partitioning - and assembles the 5stop/6stop/suck route types from
parameterized towns, amounts and prices. roucli keeps the CLI concerns:
TOML parsing, the reference table, citizens scaling and printing.

This lets mods build routes in-game from the same code the CLI uses
(stops-only buffers for direct injection come from
TradeRouteStop::serialize, without the file header).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
p3-rou now depends on p3-api: ware names are the WareId identifiers
(FromStr for lookup, Debug for display) and scalings come from
get_scaling(), removing the duplicated 24-ware table from the builder.
This makes p3-rou Windows-target-only like the rest of the workspace;
tests and roucli run fine from WSL through interop
(cargo xwin test -p p3-rou).

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