mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Camp: reject (not downgrade) sub-par camps; map: show visited path
Camp rest rules:
• Standard camp now rejects an uncleared room with a clear message
instead of silently downgrading to rough — let the player make the
call rather than spending supplies on a worse rest they didn't pick.
• campLocationCheck treats a room with a resolved (non-active) combat
session as cleared, so the natural "just killed, not yet advanced"
pause still qualifies for a standard camp.
• Reword fortified-camp gating + babysit help to "zone boss defeated"
(cache sites aren't a thing yet) so the requirement isn't misleading.
Map: renderVisitedPath adds a 1-indexed breadcrumb of visited rooms to
!expedition map, so players can reference rooms by index (e.g. !revisit 2).
(cherry picked from commit a38fc77eed888e9790c7a7cff24369b98910b43e)
This commit is contained in:
@@ -98,7 +98,7 @@ func (p *AdventurePlugin) handleBabysitCmd(ctx MessageContext, args string) erro
|
||||
return p.SendDM(ctx.Sender, "🍼 **Adventurer Babysitting Service**\n\n"+
|
||||
"Hire a babysitter to look after your camp and tend the pet while you sleep:\n"+
|
||||
" • Daily pet XP trickle (your pet still grows while you focus elsewhere)\n"+
|
||||
" • Standard camps act like fortified ones — rest deeply, no need for boss-cleared rooms\n"+
|
||||
" • Standard camps act like fortified ones — rest deeply, no need to have downed the zone boss\n"+
|
||||
" • Rival duels declined on your behalf\n\n"+
|
||||
"`!adventure babysit week` — 7 days of service\n"+
|
||||
"`!adventure babysit month` — 30 days of service\n"+
|
||||
|
||||
@@ -79,12 +79,9 @@ func (p *AdventurePlugin) handleCampCmd(ctx MessageContext, args string) error {
|
||||
case "rough", "standard":
|
||||
// allowed in E1e
|
||||
case "fortified":
|
||||
// E2d: §5.1 — boss-cleared room or cache site. Cache sites
|
||||
// are zone-specific waypoints (E3+), so for now we require the
|
||||
// expedition's boss to have been defeated.
|
||||
if !exp.BossDefeated {
|
||||
return p.SendDM(ctx.Sender,
|
||||
"Fortified camps require a boss-cleared room or cache site. Defeat the zone boss (or find a cache) first.")
|
||||
"Fortified camps require a defeated zone boss. Clear the zone first.")
|
||||
}
|
||||
case "base":
|
||||
// E4d: §11.1 — base camps unlock per region after the region
|
||||
@@ -122,7 +119,7 @@ func campHelpText(exp *Expedition) string {
|
||||
b.WriteString("**!camp <type>** — establish camp during an expedition.\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 (boss-cleared room, +2 SU, low risk)\n")
|
||||
b.WriteString("`!camp fortified` — long rest + bonus (zone boss defeated, +2 SU, low risk)\n")
|
||||
b.WriteString("`!camp base` — persistent waypoint (region-boss-cleared base-camp site, +3 SU, very low risk)\n")
|
||||
b.WriteString("`!camp break` — break camp\n\n")
|
||||
if exp.Camp != nil && exp.Camp.Active {
|
||||
@@ -146,10 +143,10 @@ func (p *AdventurePlugin) campPitch(ctx MessageContext, exp *Expedition, kind st
|
||||
return p.SendDM(ctx.Sender, problem)
|
||||
}
|
||||
if !cleared && kind == CampTypeStandard {
|
||||
// §5.2: non-cleared room forces rough.
|
||||
kind = CampTypeRough
|
||||
_ = appendExpeditionLog(exp.ID, exp.CurrentDay, "narrative",
|
||||
"intended standard camp; downgraded to rough (room not cleared)", "")
|
||||
// §5.2: standard camp requires a cleared room. Reject explicitly
|
||||
// rather than silently downgrading — the player should make the call.
|
||||
return p.SendDM(ctx.Sender,
|
||||
"Standard camp needs a cleared room. This room isn't cleared yet — clear it first, or `!camp rough` for a partial rest here.")
|
||||
}
|
||||
|
||||
cost, ok := campSupplyCost[kind]
|
||||
@@ -359,15 +356,24 @@ func campLocationCheck(exp *Expedition) (cleared bool, problem string) {
|
||||
case RoomTrap:
|
||||
return false, "You can't camp in a trap room — even a disarmed one."
|
||||
}
|
||||
// Active-enemy detection requires combat-state lookup; defer to E2.
|
||||
cleared = false
|
||||
for _, idx := range run.RoomsCleared {
|
||||
if idx == run.CurrentRoom {
|
||||
cleared = true
|
||||
break
|
||||
return true, ""
|
||||
}
|
||||
}
|
||||
return cleared, ""
|
||||
// Not yet advanced-past, but if there's no live combat encounter in
|
||||
// this room, it's still safe to rest in. Forward-only navigation means
|
||||
// players naturally pause right after a kill before advancing — the
|
||||
// "cleared" flag would otherwise force-downgrade their camp to rough.
|
||||
encID := encounterIDForRoom(run.CurrentRoom)
|
||||
sess, err := getCombatSessionForEncounter(run.RunID, encID)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
if sess != nil && sess.Status != CombatStatusActive {
|
||||
return true, ""
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
|
||||
// campCurrentRoomIndex returns 0 (entry) when no room context exists.
|
||||
|
||||
@@ -119,6 +119,9 @@ func (p *AdventurePlugin) handleExpeditionMapCmd(ctx MessageContext, _ string) e
|
||||
b.WriteString(renderZoneGraphMap(g, run))
|
||||
b.WriteString("\n```\n")
|
||||
b.WriteString("_E=Entry ?=Exploration T=Trap ★=Elite ☠=Boss ⚿=Secret · ✓ cleared ▶ here · pending ╳ locked_")
|
||||
if path := renderVisitedPath(g, run); path != "" {
|
||||
b.WriteString("\n**Path:** " + path)
|
||||
}
|
||||
if chain != "" {
|
||||
b.WriteString("\n_Regions: ")
|
||||
b.WriteString(renderRegionLegend(exp))
|
||||
|
||||
@@ -172,6 +172,28 @@ func nodeGlyph(n ZoneNode) string {
|
||||
return "·"
|
||||
}
|
||||
|
||||
// renderVisitedPath renders a one-line numbered breadcrumb of the
|
||||
// player's path so they can reference rooms by index (e.g. !revisit 2).
|
||||
// Numbers are 1-indexed to match every other surface ("Room 2/7", etc.).
|
||||
func renderVisitedPath(g ZoneGraph, run *DungeonRun) string {
|
||||
if run == nil || len(run.VisitedNodes) == 0 {
|
||||
return ""
|
||||
}
|
||||
parts := make([]string, 0, len(run.VisitedNodes))
|
||||
for i, nodeID := range run.VisitedNodes {
|
||||
glyph := "·"
|
||||
if n, ok := g.Nodes[nodeID]; ok {
|
||||
glyph = nodeGlyph(n)
|
||||
}
|
||||
mark := "✓"
|
||||
if nodeID == run.CurrentNode {
|
||||
mark = "▶"
|
||||
}
|
||||
parts = append(parts, fmt.Sprintf("[%d]%s%s", i+1, glyph, mark))
|
||||
}
|
||||
return strings.Join(parts, " → ")
|
||||
}
|
||||
|
||||
// debugDump (test-only crutch) prints the raw column layout. Currently
|
||||
// unused outside potential debugging — kept off the unused-warning list
|
||||
// by routing fmt through it.
|
||||
|
||||
Reference in New Issue
Block a user