mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 00:32:40 +00:00
Auto-walk: cross region boundaries in multi-region zones
Extract regionCmdTravel's transit core into advanceToNextRegion (shared by manual !region travel), then have runAutopilotWalk auto-advance into the next region on a mid-zone stopComplete instead of dead-stopping the walk. Transit cost (day + supplies + wandering check) is identical on both paths. (cherry picked from commit 8ac09cf3b059a086c431859c8aaaf046aa992342)
This commit is contained in:
@@ -625,6 +625,34 @@ func (p *AdventurePlugin) runAutopilotWalk(ctx MessageContext, maxRooms int, com
|
||||
rooms++
|
||||
}
|
||||
|
||||
// Multi-region auto-advance: a mid-zone region clear completes the
|
||||
// region's run (stopComplete) but leaves the wrapping expedition
|
||||
// active. Rather than dead-stopping the walk at every region
|
||||
// boundary, cross into the next region — burning the transit day +
|
||||
// supplies exactly like manual `!region travel` — and keep walking
|
||||
// within the remaining room budget. A full zone clear instead flips
|
||||
// the expedition to 'complete' (getActiveExpedition → nil) and falls
|
||||
// through to the normal stop below.
|
||||
if res.reason == stopComplete {
|
||||
if fresh, ferr := getActiveExpedition(ctx.Sender); ferr == nil && fresh != nil &&
|
||||
IsMultiRegionZone(fresh.ZoneID) {
|
||||
if cur, ok := CurrentRegion(fresh); ok {
|
||||
if next, ok := nextRegion(fresh.ZoneID, cur.ID); ok {
|
||||
stream = append(stream, res.final)
|
||||
transit, terr := p.advanceToNextRegion(ctx.Sender, fresh, cur, next)
|
||||
if terr != nil {
|
||||
finalMsg = res.final + "\n\n⏸ **Autopilot paused — region transit failed.** `!region travel` to cross over manually."
|
||||
reason = stopComplete
|
||||
break
|
||||
}
|
||||
stream = append(stream, transit)
|
||||
exp = fresh
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if res.reason != stopOK {
|
||||
footer := autopilotFooter(res.reason, rooms)
|
||||
if footer != "" {
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"gogobee/internal/flavor"
|
||||
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
||||
// Phase 12 E4c — !region command surface and inter-region travel.
|
||||
@@ -120,24 +122,41 @@ func (p *AdventurePlugin) regionCmdTravel(ctx MessageContext, exp *Expedition) e
|
||||
"You're already in **%s** — the final region of this zone. Defeat the zone boss to complete the expedition.",
|
||||
cur.Name))
|
||||
}
|
||||
narrative, err := p.advanceToNextRegion(ctx.Sender, exp, cur, next)
|
||||
if err != nil {
|
||||
return p.SendDM(ctx.Sender, "Region transit failed: "+err.Error())
|
||||
}
|
||||
return p.SendDM(ctx.Sender, narrative)
|
||||
}
|
||||
|
||||
// advanceToNextRegion performs an inter-region transition: burns the transit
|
||||
// day + supplies, fires the transit wandering check, retires the outgoing
|
||||
// region's DungeonRun, bumps CurrentRegion + the visited list, and spawns the
|
||||
// incoming region's run (pinning exp.RunID). Returns the rendered transit
|
||||
// narrative block. Shared by the manual `!region travel` command and the
|
||||
// autopilot walk's mid-zone auto-advance — keeping the transit cost identical
|
||||
// on both paths.
|
||||
func (p *AdventurePlugin) advanceToNextRegion(userID id.UserID, exp *Expedition, cur, next ExpeditionRegion) (string, error) {
|
||||
// Burn one day of supplies (transit day).
|
||||
siege := exp.SiegeMode
|
||||
harsh := exp.ThreatLevel > 60 || zoneTemporalHarsh(exp)
|
||||
newSupplies, burned := applyDailyBurn(exp.Supplies, harsh, siege)
|
||||
exp.Supplies = newSupplies
|
||||
if err := updateSupplies(exp.ID, exp.Supplies); err != nil {
|
||||
return p.SendDM(ctx.Sender, "Couldn't apply transit supply burn: "+err.Error())
|
||||
return "", fmt.Errorf("apply transit supply burn: %w", err)
|
||||
}
|
||||
if err := advanceExpeditionDay(exp.ID); err != nil {
|
||||
return p.SendDM(ctx.Sender, "Couldn't advance the expedition day: "+err.Error())
|
||||
return "", fmt.Errorf("advance expedition day: %w", err)
|
||||
}
|
||||
exp.CurrentDay++
|
||||
|
||||
// Transit wandering check (no camp protection — campMod = 0).
|
||||
c, _ := LoadDnDCharacter(ctx.Sender)
|
||||
c, _ := LoadDnDCharacter(userID)
|
||||
var charClass DnDClass
|
||||
charLevel := 1
|
||||
if c != nil {
|
||||
charClass = c.Class
|
||||
charLevel = c.Level
|
||||
}
|
||||
tc := resolveTransitWanderingCheck(exp, charClass, nil)
|
||||
_ = processTransitWanderingCheck(exp, tc)
|
||||
@@ -145,24 +164,20 @@ func (p *AdventurePlugin) regionCmdTravel(ctx MessageContext, exp *Expedition) e
|
||||
// R2 — retire the outgoing region's DungeonRun before mutating
|
||||
// CurrentRegion so retireRegionRun keys the right region.
|
||||
if err := retireRegionRun(exp, cur.ID); err != nil {
|
||||
return p.SendDM(ctx.Sender, "Couldn't retire previous region run: "+err.Error())
|
||||
return "", fmt.Errorf("retire previous region run: %w", err)
|
||||
}
|
||||
|
||||
// Update CurrentRegion + visited list.
|
||||
if err := setCurrentRegion(exp, next.ID); err != nil {
|
||||
return p.SendDM(ctx.Sender, "Couldn't update current region: "+err.Error())
|
||||
return "", fmt.Errorf("update current region: %w", err)
|
||||
}
|
||||
if _, err := MarkRegionVisited(exp, next.ID); err != nil {
|
||||
return p.SendDM(ctx.Sender, "Couldn't mark region visited: "+err.Error())
|
||||
return "", fmt.Errorf("mark region visited: %w", err)
|
||||
}
|
||||
|
||||
// Spawn the new region's DungeonRun and pin exp.RunID.
|
||||
charLevel := 1
|
||||
if c != nil {
|
||||
charLevel = c.Level
|
||||
}
|
||||
if _, err := ensureRegionRun(exp, charLevel); err != nil {
|
||||
return p.SendDM(ctx.Sender, "Couldn't outfit the new region: "+err.Error())
|
||||
return "", fmt.Errorf("outfit the new region: %w", err)
|
||||
}
|
||||
|
||||
// Log + flavor.
|
||||
@@ -189,7 +204,7 @@ func (p *AdventurePlugin) regionCmdTravel(ctx MessageContext, exp *Expedition) e
|
||||
if next.IsZoneBoss {
|
||||
b.WriteString("\n_★ Final region — the zone boss is here._")
|
||||
}
|
||||
return p.SendDM(ctx.Sender, b.String())
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
// resolveTransitWanderingCheck mirrors resolveWanderingCheck (§6.1) but
|
||||
|
||||
Reference in New Issue
Block a user