mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +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++
|
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 {
|
if res.reason != stopOK {
|
||||||
footer := autopilotFooter(res.reason, rooms)
|
footer := autopilotFooter(res.reason, rooms)
|
||||||
if footer != "" {
|
if footer != "" {
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gogobee/internal/flavor"
|
"gogobee/internal/flavor"
|
||||||
|
|
||||||
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Phase 12 E4c — !region command surface and inter-region travel.
|
// 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.",
|
"You're already in **%s** — the final region of this zone. Defeat the zone boss to complete the expedition.",
|
||||||
cur.Name))
|
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).
|
// Burn one day of supplies (transit day).
|
||||||
siege := exp.SiegeMode
|
siege := exp.SiegeMode
|
||||||
harsh := exp.ThreatLevel > 60 || zoneTemporalHarsh(exp)
|
harsh := exp.ThreatLevel > 60 || zoneTemporalHarsh(exp)
|
||||||
newSupplies, burned := applyDailyBurn(exp.Supplies, harsh, siege)
|
newSupplies, burned := applyDailyBurn(exp.Supplies, harsh, siege)
|
||||||
exp.Supplies = newSupplies
|
exp.Supplies = newSupplies
|
||||||
if err := updateSupplies(exp.ID, exp.Supplies); err != nil {
|
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 {
|
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++
|
exp.CurrentDay++
|
||||||
|
|
||||||
// Transit wandering check (no camp protection — campMod = 0).
|
// Transit wandering check (no camp protection — campMod = 0).
|
||||||
c, _ := LoadDnDCharacter(ctx.Sender)
|
c, _ := LoadDnDCharacter(userID)
|
||||||
var charClass DnDClass
|
var charClass DnDClass
|
||||||
|
charLevel := 1
|
||||||
if c != nil {
|
if c != nil {
|
||||||
charClass = c.Class
|
charClass = c.Class
|
||||||
|
charLevel = c.Level
|
||||||
}
|
}
|
||||||
tc := resolveTransitWanderingCheck(exp, charClass, nil)
|
tc := resolveTransitWanderingCheck(exp, charClass, nil)
|
||||||
_ = processTransitWanderingCheck(exp, tc)
|
_ = 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
|
// R2 — retire the outgoing region's DungeonRun before mutating
|
||||||
// CurrentRegion so retireRegionRun keys the right region.
|
// CurrentRegion so retireRegionRun keys the right region.
|
||||||
if err := retireRegionRun(exp, cur.ID); err != nil {
|
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.
|
// Update CurrentRegion + visited list.
|
||||||
if err := setCurrentRegion(exp, next.ID); err != nil {
|
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 {
|
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.
|
// 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 {
|
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.
|
// Log + flavor.
|
||||||
@@ -189,7 +204,7 @@ func (p *AdventurePlugin) regionCmdTravel(ctx MessageContext, exp *Expedition) e
|
|||||||
if next.IsZoneBoss {
|
if next.IsZoneBoss {
|
||||||
b.WriteString("\n_★ Final region — the zone boss is here._")
|
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
|
// resolveTransitWanderingCheck mirrors resolveWanderingCheck (§6.1) but
|
||||||
|
|||||||
Reference in New Issue
Block a user