diff --git a/gogobee_long_expedition_plan.md b/gogobee_long_expedition_plan.md index 9da5ce2..048cb6a 100644 --- a/gogobee_long_expedition_plan.md +++ b/gogobee_long_expedition_plan.md @@ -128,10 +128,12 @@ Each successful background walk now logs a `walk` entry (`appendExpeditionLog(.. **Exit criteria:** balanced loadout completes intended-duration expedition in ≥ 85% of sim runs without starvation extraction. ### D6 — Player-facing surface cleanup -- Help text rewrite — frame expedition as "supply pack + watch it play out". -- Hide / soft-deprecate `!camp ` from primary help, keep it documented as an override. -- `!fight` still in help, framed as override. -- Status DM (`!expedition status`) shows: current day / expected days, rooms walked / total, supplies, last 3 events. + +**D6 (shipped 2026-05-27):** player-facing copy now matches the autopilot-first reality. +- `expeditionHelpText` rewritten around the loop "pick a zone → pick a loadout → `!expedition run` watches it play out"; commands grouped into **Run an expedition** / **Mid-expedition** / **Overrides** with a one-line shape paragraph at the top. +- `!camp` and `!fight` were already absent from the primary `!expedition` help; both are now explicitly listed under **Overrides** and framed as "autopilot covers these — only reach for them if you want manual control." `campHelpText` itself opens with the same override framing so a player who types `!camp` lands on the right mental model. +- `expeditionCmdStatusImpl` (default `!expedition status`) now leads with **Day X / ~Y expected** (driven by new `expeditionTargetDays(tier ZoneTier)` in `dnd_expedition_supplies.go` — T1=2 → T5=7, the §2 anchor table), adds a **Rooms: N / Total in this region** line under the region marker (sourced from `getActiveZoneRun(...).CurrentRoom/TotalRooms`), and appends a **Recent:** block with the last 3 log entries (summary fallback to flavor). Supplies and threat lines unchanged; the `--debug` view is untouched. +- No new public command surface, no schema change, no test churn (no existing test asserted on the rewritten strings); full `go test ./...` green. ### D7 — Sim + class re-baseline - `cmd/expedition-sim` extensions: multi-day mode, autopilot camp, autopilot boss. diff --git a/internal/plugin/dnd_expedition_camp.go b/internal/plugin/dnd_expedition_camp.go index aa6a6bb..e309a09 100644 --- a/internal/plugin/dnd_expedition_camp.go +++ b/internal/plugin/dnd_expedition_camp.go @@ -117,7 +117,7 @@ func (p *AdventurePlugin) handleCampCmd(ctx MessageContext, args string) error { func campHelpText(exp *Expedition) string { var b strings.Builder - b.WriteString("**!camp ** — establish camp during an expedition.\n\n") + b.WriteString("**!camp ** — _override._ Autopilot pitches camp at night automatically; reach for this only to force a rest right now.\n\n") b.WriteString("`!camp rough` — partial rest (any location, +0.5 SU, high night risk)\n") b.WriteString("`!camp standard` — full long rest (cleared room, +1 SU, medium risk)\n") b.WriteString("`!camp fortified` — long rest + bonus (zone boss defeated, +2 SU, low risk)\n") diff --git a/internal/plugin/dnd_expedition_cmd.go b/internal/plugin/dnd_expedition_cmd.go index 4aff8cf..b5cd4d9 100644 --- a/internal/plugin/dnd_expedition_cmd.go +++ b/internal/plugin/dnd_expedition_cmd.go @@ -91,18 +91,23 @@ func (p *AdventurePlugin) handleDnDExpeditionCmd(ctx MessageContext, args string func expeditionHelpText() string { var b strings.Builder b.WriteString("**!expedition** — multi-day dungeon expeditions.\n\n") - b.WriteString("`!expedition list` — show zones available at your level\n") - b.WriteString("`!expedition start [loadout]` — outfit & begin\n") - b.WriteString(" loadout: `lean` / `balanced` / `heavy` (scales by zone tier)\n") - b.WriteString(" advanced: raw pack counts — `Ns Md` (e.g. `2s 1d`)\n") - b.WriteString(" no loadout? you'll be prompted to pick one\n") - b.WriteString("`!expedition run` — autopilot: walk rooms until something needs you (alias `!explore`)\n") - b.WriteString("`!expedition status` — current expedition snapshot\n") + b.WriteString("**The shape:** pick a zone, pick a supply pack, watch it play out. ") + b.WriteString("Autopilot walks rooms, pitches camp at night, and DMs you when something needs a decision (a fork, a boss, low HP).\n\n") + b.WriteString("**Run an expedition:**\n") + b.WriteString("`!expedition list` — zones available at your level\n") + b.WriteString("`!expedition start ` — prompts a loadout: `lean` / `balanced` / `heavy`\n") + b.WriteString("`!expedition run` — start (or resume) the autopilot walk (alias `!explore`)\n") + b.WriteString("`!expedition status` — day, rooms, supplies, recent events\n\n") + b.WriteString("**Mid-expedition:**\n") + b.WriteString("`!extract` — bail safely (resumable for 7 days)\n") + b.WriteString("`!resume [loadout]` — resume an extracted expedition\n") b.WriteString("`!expedition log` — last 5 log entries\n") - b.WriteString("`!expedition abandon` — end the expedition (no rewards)\n") - b.WriteString("`!extract` — voluntary extraction (1 day, resumable for 7 days)\n") - b.WriteString("`!resume [loadout]` — resume an extracted expedition (same `lean`/`balanced`/`heavy` choices)\n") - b.WriteString("`!map` — region/room ASCII map") + b.WriteString("`!expedition abandon` — end without rewards\n") + b.WriteString("`!map` — region/room ASCII map\n\n") + b.WriteString("**Overrides** _(autopilot covers these — only reach for them if you want manual control)_:\n") + b.WriteString("`!fight` — engage an Elite/Boss the autopilot paused at\n") + b.WriteString("`!camp` — force a rest right now (see `!camp` for types)\n") + b.WriteString("`!expedition start Ns Md` — raw pack counts instead of a preset") return b.String() } @@ -398,8 +403,9 @@ func (p *AdventurePlugin) expeditionCmdStatusImpl(ctx MessageContext, debug bool zone, _ := getZone(exp.ZoneID) c, _ := LoadDnDCharacter(ctx.Sender) + target := expeditionTargetDays(zone.Tier) var b strings.Builder - b.WriteString(fmt.Sprintf("🎭 **TwinBee — Status, Day %d**\n\n", exp.CurrentDay)) + b.WriteString(fmt.Sprintf("🎭 **TwinBee — Status, Day %d / ~%d expected**\n\n", exp.CurrentDay, target)) b.WriteString(fmt.Sprintf("📍 **Zone:** %s _(T%d)_\n", zone.Display, int(zone.Tier))) if r, ok := CurrentRegion(exp); ok { cleared := IsRegionCleared(exp, r.ID) @@ -410,6 +416,10 @@ func (p *AdventurePlugin) expeditionCmdStatusImpl(ctx MessageContext, debug bool b.WriteString(fmt.Sprintf("🗺 **Region:** %s (%d/%d)%s\n", r.Name, r.Order, len(regionsForZone(exp.ZoneID)), marker)) } + if run, rerr := getActiveZoneRun(ctx.Sender); rerr == nil && run != nil && run.TotalRooms > 0 { + b.WriteString(fmt.Sprintf("🚪 **Rooms:** %d / %d in this region\n", + run.CurrentRoom+1, run.TotalRooms)) + } if c != nil { b.WriteString(fmt.Sprintf("❤️ **HP:** %d / %d\n", c.HPCurrent, c.HPMax)) } @@ -444,6 +454,19 @@ func (p *AdventurePlugin) expeditionCmdStatusImpl(ctx MessageContext, debug bool depletionLabel(state))) } } + if entries, lerr := recentExpeditionLog(exp.ID, 3); lerr == nil && len(entries) > 0 { + b.WriteString("\n**Recent:**\n") + for _, e := range entries { + line := e.Summary + if line == "" { + line = e.Flavor + } + if line == "" { + continue + } + b.WriteString(fmt.Sprintf(" · _Day %d_ — %s\n", e.Day, line)) + } + } b.WriteString(fmt.Sprintf("\nStarted: %s Last activity: %s", exp.StartDate.Format("2006-01-02 15:04"), exp.LastActivity.Format("2006-01-02 15:04"))) diff --git a/internal/plugin/dnd_expedition_supplies.go b/internal/plugin/dnd_expedition_supplies.go index 2dc619f..fa3f0fe 100644 --- a/internal/plugin/dnd_expedition_supplies.go +++ b/internal/plugin/dnd_expedition_supplies.go @@ -41,6 +41,25 @@ func supplyPackCaps(tier ZoneTier) (standard, deluxe int) { return 3, 1 } +// expeditionTargetDays returns the §2 target-duration anchor for a zone +// tier — what the supply-cap math, balanced loadout, and the +// "Day X / ~Y expected" line in !expedition status are all sized against. +func expeditionTargetDays(tier ZoneTier) int { + switch tier { + case ZoneTierBeginner: + return 2 + case ZoneTierApprentice: + return 3 + case ZoneTierJourneyman: + return 4 + case ZoneTierVeteran: + return 5 + case ZoneTierLegendary: + return 7 + } + return 4 +} + // SupplyLoadout names a tier-scaled preset purchase. D5-b: empty-arg // `!expedition start ` now prompts the player to pick one of these // rather than silently defaulting to 1 standard pack. Raw `Ns Md` syntax