ot_cross_river
Select a river crossing method—ford, caulk, ferry, or wait—balancing risk, speed, and cost, then narrate the crossing dramatically.
Instructions
Choose how to cross the current river. Ford=fast but risky. Caulk=float the wagon, medium risk. Ferry=safe but costs $15. Wait=lose 2 days and food but calmer water. Narrate the crossing dramatically.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| method | Yes | Crossing method |
Implementation Reference
- src/games/oregontrail.ts:1014-1083 (handler)The actual implementation of the ot_cross_river tool handler. It implements four crossing methods: ford (fast but risky, up to 28% chance of disaster), caulk (float the wagon, medium risk with 22% failure chance), ferry (safe, costs $15), and wait (costs 2 days and food). The handler modifies OtState accordingly and returns a dramatic narrative.
server.tool( "ot_cross_river", "Choose how to cross the current river. Ford=fast but risky. Caulk=float the wagon, medium risk. Ferry=safe but costs $15. Wait=lose 2 days and food but calmer water. Narrate the crossing dramatically.", { method: z.enum(["ford", "caulk", "ferry", "wait"]).describe("Crossing method"), }, async ({ method }) => { if (!otGame) return { content: [{ type: "text", text: "No journey in progress." }], isError: true }; if (!otGame.pendingRiver) return { content: [{ type: "text", text: `You're not at a river crossing.\n\n${renderState(otGame)}` }], isError: true }; const stop = otGame.trailStops[otGame.currentStopIndex]; let text: string; if (method === "ford") { const roll = Math.random(); if (roll < 0.28) { if (Math.random() < 0.4) otGame.supplies.oxen = Math.max(1, otGame.supplies.oxen - 1); if (Math.random() < 0.5) otGame.supplies.parts = Math.max(0, otGame.supplies.parts - 1); otGame.player.health = clamp(otGame.player.health - 22, 0, 100); otGame.companion.health = clamp(otGame.companion.health - 15, 0, 100); otGame.supplies.food = Math.max(0, otGame.supplies.food - 4); if (otGame.pet.alive) { otGame.pet.health = clamp(otGame.pet.health - 30, 0, 100); if (otGame.pet.health <= 0) otGame.pet.alive = false; } text = `The ford goes badly. The current grabs the wagon and for a terrifying moment everything is chaos — water, shouting, supplies spilling downstream. You make it across, but not without cost.`; } else if (roll < 0.55) { otGame.player.health = clamp(otGame.player.health - 8, 0, 100); otGame.supplies.food = Math.max(0, otGame.supplies.food - 1); text = `The ford is rough but survivable. Everyone gets soaked. ${otGame.player.name} takes a knock from a submerged rock. You're across.`; } else { text = `The ford goes better than expected. You pick a good line through the current. Everyone across, mostly dry.`; } } else if (method === "caulk") { // Caulking always costs something — it's a full day of cold, exhausting work. otGame.player.health = clamp(otGame.player.health - 8, 0, 100); otGame.companion.health = clamp(otGame.companion.health - 8, 0, 100); otGame.supplies.food = Math.max(0, otGame.supplies.food - 1); if (Math.random() < 0.22) { // Water got into the wagon — something went wrong mid-float. otGame.supplies.parts = Math.max(0, otGame.supplies.parts - 1); otGame.supplies.food = Math.max(0, otGame.supplies.food - 2); otGame.player.health = clamp(otGame.player.health - 10, 0, 100); if (otGame.pet.alive) { otGame.pet.health = clamp(otGame.pet.health - 15, 0, 100); if (otGame.pet.health <= 0) otGame.pet.alive = false; } text = `The float goes wrong mid-crossing. The wagon lists, water pours in, and for a long terrible minute everything is chaos. You make it across, but the wagon took damage and the supplies got wet. Everyone is shaking.`; } else { text = `You spend the better part of a day sealing the wagon and coaxing it across. The current is stronger than it looked from the bank. Everyone arrives soaked, cold, and exhausted — but across.`; } } else if (method === "ferry") { if (otGame.supplies.money < 15) return { content: [{ type: "text", text: `The ferryman wants $15. You only have $${otGame.supplies.money}. Find another way.\n\n${renderState(otGame)}` }], isError: true }; otGame.supplies.money -= 15; text = `You pay the ferryman $15 and cross without incident. ${otGame.companion.name} watches the water the entire way. "${stop.name} lives up to its reputation," they say quietly.`; } else { otGame.day += 2; otGame.supplies.food = Math.max(0, otGame.supplies.food - 4); text = `You make camp and wait two days for the water to settle. It does, slightly. The crossing is uneventful. The delay stings, but you're across.`; } otGame.pendingRiver = false; otGame.status = "traveling"; log(otGame, `Crossed ${stop.name} by ${method}.`); return { content: [{ type: "text", text: `${text}\n\n${renderState(otGame)}` }], }; } ); - src/games/oregontrail.ts:1014-1019 (schema)Input schema for ot_cross_river — uses Zod enum for crossing method (ford/caulk/ferry/wait) with a descriptive string explaining each option.
server.tool( "ot_cross_river", "Choose how to cross the current river. Ford=fast but risky. Caulk=float the wagon, medium risk. Ferry=safe but costs $15. Wait=lose 2 days and food but calmer water. Narrate the crossing dramatically.", { method: z.enum(["ford", "caulk", "ferry", "wait"]).describe("Crossing method"), }, - src/index.ts:7-7 (registration)Import of registerOregonTrailTools from the oregontrail module.
import { registerOregonTrailTools } from "./games/oregontrail.js"; - src/index.ts:19-19 (registration)Registration call — registerOregonTrailTools is called with the server instance, which registers all Oregon Trail tools including ot_cross_river.
registerOregonTrailTools(server); - src/games/oregontrail.ts:654-658 (helper)In the renderState helper, when pendingRiver is true it renders a prompt telling the user to call ot_cross_river with one of four methods: ford, caulk, ferry, or wait.
if (state.pendingRiver) { lines.push(`⚠ RIVER CROSSING — choose a method with ot_cross_river before traveling further`); lines.push(` ford (fast, risky) | caulk (medium risk) | ferry (safe, costs $15) | wait (costs food + 2 days)`); }