Close expedition/run seam on combat loss & flee

Run-loss paths (turn-based elite/boss death + flee, exploration combat
death/retreat, interrupt/patrol death) abandoned the zone run but left
the wrapping expedition row at status='active', so the ambient ticker
kept DMing about a dungeon the player had walked away from.

Adds forceExtractExpeditionForRunLoss helper and wires it into every
run-loss site; ambient ticker also skips when the expedition's run is
no longer active as a safety net.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-16 13:48:52 -07:00
parent 98ba416359
commit dcc039b7d5
5 changed files with 34 additions and 0 deletions

View File

@@ -5,6 +5,7 @@ import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"time"
@@ -92,6 +93,23 @@ func forcedExtractExpedition(expID, reason string) (*Expedition, int, error) {
return e, tax, nil
}
// forceExtractExpeditionForRunLoss bridges run-loss call sites (turn-based
// elite/boss death or flee, exploration combat death, patrol-interrupt
// death) into the forced-extract flow. Those sites already abandon the
// zone run, but without flipping the wrapping expedition the ambient
// ticker keeps DMing about a dungeon the player walked away from. No-op
// when there is no active expedition for this user.
func forceExtractExpeditionForRunLoss(userID id.UserID, reason string) {
exp, err := getActiveExpedition(userID)
if err != nil || exp == nil {
return
}
if _, _, err := forcedExtractExpedition(exp.ID, reason); err != nil {
slog.Warn("expedition: force-extract on run loss",
"user", userID, "expedition", exp.ID, "reason", reason, "err", err)
}
}
// getResumableExpedition returns the most recent 'extracting' row for the
// user, regardless of age (caller checks the 7-day window).
func getResumableExpedition(userID id.UserID) (*Expedition, error) {