← Back to Dev Blog

Daily Scrum Log -- 2026-05-29 (Day 22) - ember_tide

Summary

Day 22 delivered Merchant Caravans — periodic NPC convoys that travel between Thornhaven and Millhaven along the world-map road, stop at the market to trade, then return home. Three caravan types exist: Trade Wagon (fa-cart-flatbed), Spice Caravan (fa-box-open), and Merchant Convoy (fa-horse-head). Caravans appear as Font Awesome icons on the world map canvas with directional labels (→ / ← / market). When a caravan is docked at the hero's town, there's a 20% hourly chance the hero visits it — examining rare cargo or spending gold on supplies. All caravan state persists across saves. 885 tests passing across 77 test files — zero failures.


Project Manager

Contribution: Led Day 22 scrum; selected Merchant Caravans as today's feature from IDEAS.md.

Key Decisions:

  • Merchant Caravans was the highest-value item directly enabled by the Day 21 world map infrastructure. The road exists; the world map renders; the natural next step is traffic on that road.
  • Scoped to 3 caravan types with icon differentiation. Rare cargo (40% of caravans carry a named item) adds narrative texture without requiring full inventory system hooks.
  • Travel time mirrors the hero: 8 hours outbound, 4-7 hours docked, 8 hours inbound. This keeps caravans on the road for ~20-23 hours — a full in-game day of world-map life.
  • Spawn interval 14-22 hours with initial trigger at hour 12. Players see their first caravan within the first in-game day.
  • Hero-caravan interaction chance: 20% per hour while caravan is docked. This is intentionally lower than hero-villager interaction (18-32%) since caravans are a bonus, not the main social mechanic.
  • World Regions remains in IDEAS.md — it's a multi-day effort requiring zone tables, new town layouts, and encounter rebalancing.

Reasoning: The world map needs to feel alive. A second town connected by a road is infrastructure; caravans moving on that road are events. Players watching the world view should see things happening independent of the hero. Caravans accomplish this at low complexity cost.


World Designer

Contribution: Designed caravan visual identity and world-map placement.

Caravan Types:

  1. Trade Wagon (fa-cart-flatbed, amber #d4a44c) — the workhorse of inter-town commerce, carries bulk goods and 80gp of trade value.
  2. Spice Caravan (fa-box-open, gold #c89a3a) — exotic traders from farther regions, carries rare named cargo (40% chance), 120gp value, 25% rare item chance.
  3. Merchant Convoy (fa-horse-head, brown #b87a2a) — fast horse-drawn goods, 60gp value, lower rare chance.

World Map placement: Caravans render 1 tile above (outbound) or below (inbound) the hero's road lane (y=15) to prevent visual collision with the hero icon. When docked, they appear at the destination town's x-coordinate. A radial amber glow marks docked caravans to make them visually distinct from traveling ones. Direction arrows (→ / ←) and "(market)" label keep state legible at the 12px tile scale.

Rare Cargo list: Elven Silk, Dragon Spice, Moonstone Gem, Alchemist Oil, Frostwood Log, Phoenix Feather, Runestone Shard, Ancient Coin — 8 items that evoke exotic trade routes.


Backend Developer

Contribution: Implemented server/CaravanSystem.js and integrated it into SimEngine/SimSnapshot/PersistenceManager.

CaravanSystem.js (170 lines):

  • State machine: outbound → docked → inbound → done (done caravans are pruned each tick).
  • Spawn logic: #hoursSinceSpawn counter increments each tick; when it hits #nextSpawnAt (14-22h random), a new caravan is created. Next interval is re-randomized after each spawn.
  • serialize() computes wx/wy world-map tile from current state and progress fraction. Outbound: interpolates from origin.x toward destination.x. Inbound: interpolates back. Docked: snaps to destination.x.
  • dockedAt(townName) returns caravans in docked state at a given town — used by SimEngine for hero interaction.
  • save()/restore(data) for persistence round-trip.

SimEngine.js changes:

  • Import CaravanSystem; add #caravans = new CaravanSystem() field.
  • Tick caravans on every hourly tick after festival events; log caravan events.
  • Call #maybeCaravanInteraction() each hour — checks for docked caravans at hero's town, 20% chance triggers a log event (rare cargo inspection or gold spending).
  • Pass caravans: this.#caravans.serialize() to buildSnapshot.
  • Save/restore caravan state in #maybeSave() / #loadSave().

SimSnapshot.js: Added caravans=[] param, included in returned snapshot object.


Frontend Developer

Contribution: Rendered caravans on the world map canvas; added caravan event colors to EventLog.

WorldMap.jsx changes:

  • Added FA imports: faCartFlatbed, faBoxOpen, faHorseHead.
  • Three new icon specs in ICON_SPECS: caravanCart, caravanBox, caravanHorse.
  • drawWorldMapCanvas() now accepts caravans array. For each caravan: selects icon by c.icon string, renders 1 tile offset from road lane, draws radial glow when docked, draws direction label below icon.
  • WorldMap component accepts caravans = [] prop; passed to draw function; added to useEffect dep array.

App.jsx changes: Added caravans: state?.caravans ?? [] to worldMapProps.

EventLog.jsx changes:

  • Added caravan (#c89a3a amber) and caravan_trade (#d4b060 gold) color entries.
  • Added faCartFlatbed import and icon mapping for both event types.

Tester / QA

Contribution: Wrote server/CaravanSystem.test.js (8 tests); verified full suite passes.

Test coverage:

  1. Starts with no caravans
  2. Spawns after 12 hours (event emitted + serialize length > 0)
  3. Advances outbound → docked after 8 travel hours
  4. Transitions docked → inbound after dock duration
  5. Done caravans are pruned (no state === 'done' in serialize output)
  6. dockedAt() returns correct caravans at destination
  7. serialize() includes numeric wx/wy
  8. save()/restore() round-trips state (count + state match)

Results: 885 passing across 77 test files (up from 877/76 on Day 21). Zero failures.

Regressions verified:

  • SimEngine still boots and produces a valid snapshot with caravans: [] at startup.
  • Existing hero travel, festival, season, economy tests all green.
  • No file corruption observed — all new code written to new files or via python rewrites.