mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Long expeditions D3: compact autopilot auto-resolves boss rooms
Drops the boss carve-out in the compact (background autorun) path so boss rooms resolve through the same forward-sim engine elites already use. A `bossSafetyGate` (HP < 80% / supplies < daily burn / exhaustion >= 3) guards the engage; when it trips, the walk returns `stopBossSafety` and the autorun ticker force-pitches a rest camp via `pitchBossSafetyCamp` (bypasses the normal scheduler's HP threshold and its RoomBoss room-type block; keeps event-anchored night handling). `resolveCombatRoom` now selects monster + label + loot drop by `run.CurrentRoomType()` so the same callsite handles boss kills (zone.Boss bestiary, "Boss — name down", boss-loot drop, elite-tier threat bump). The walk loop only breaks at elite/boss doorways when `!compact`; compact lets the next iteration auto-resolve. Foreground `!fight` and `!expedition run` are unchanged. Sim path is unaffected — stopBossSafety falls into the default soft-stop branch.
This commit is contained in:
@@ -310,6 +310,7 @@ func TestAutopilotFooter_Reasons(t *testing.T) {
|
||||
{stopEnded, "", false},
|
||||
{stopComplete, "", false},
|
||||
{stopBlocked, "", false},
|
||||
{stopBossSafety, "", false}, // res.final carries the held-back line
|
||||
}
|
||||
for _, c := range cases {
|
||||
got := autopilotFooter(c.r, 3)
|
||||
@@ -323,6 +324,59 @@ func TestAutopilotFooter_Reasons(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestBossSafetyGate covers all three trip conditions (HP, supplies,
|
||||
// exhaustion) and the all-clear case. The gate is the D3 boss carve-out
|
||||
// replacement — compact autopilot asks it before engaging the boss.
|
||||
func TestBossSafetyGate(t *testing.T) {
|
||||
setupAuditTestDB(t)
|
||||
uid := id.UserID("@exp-boss-safety-gate:example")
|
||||
expeditionCmdTestCharacter(t, uid, 1)
|
||||
defer cleanupExpeditions(uid)
|
||||
|
||||
healthyExp := &Expedition{
|
||||
Supplies: ExpeditionSupplies{Current: 10, DailyBurn: 1},
|
||||
}
|
||||
|
||||
// All-clear baseline: full HP, supplies fat, no exhaustion → no block.
|
||||
c, _ := LoadDnDCharacter(uid)
|
||||
c.HPCurrent = c.HPMax
|
||||
c.Exhaustion = 0
|
||||
if err := SaveDnDCharacter(c); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if msg, blocked := bossSafetyGate(uid, healthyExp); blocked {
|
||||
t.Fatalf("expected healthy party to pass gate, blocked with %q", msg)
|
||||
}
|
||||
|
||||
// HP below 80% → block.
|
||||
c.HPCurrent = int(float64(c.HPMax) * 0.5)
|
||||
if err := SaveDnDCharacter(c); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if msg, blocked := bossSafetyGate(uid, healthyExp); !blocked || !strings.Contains(msg, "HP") {
|
||||
t.Errorf("HP gate: blocked=%v msg=%q", blocked, msg)
|
||||
}
|
||||
|
||||
// HP healed, but supplies under one day's burn → block.
|
||||
c.HPCurrent = c.HPMax
|
||||
if err := SaveDnDCharacter(c); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
lowSU := &Expedition{Supplies: ExpeditionSupplies{Current: 0.5, DailyBurn: 1.5}}
|
||||
if msg, blocked := bossSafetyGate(uid, lowSU); !blocked || !strings.Contains(msg, "supplies") {
|
||||
t.Errorf("SU gate: blocked=%v msg=%q", blocked, msg)
|
||||
}
|
||||
|
||||
// Supplies refilled, but exhaustion ≥ 3 → block.
|
||||
c.Exhaustion = 3
|
||||
if err := SaveDnDCharacter(c); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if msg, blocked := bossSafetyGate(uid, healthyExp); !blocked || !strings.Contains(msg, "exhaustion") {
|
||||
t.Errorf("exhaustion gate: blocked=%v msg=%q", blocked, msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpeditionCmd_ListWithoutCharBlocked(t *testing.T) {
|
||||
setupAuditTestDB(t)
|
||||
uid := id.UserID("@exp-cmd-nochar:example")
|
||||
|
||||
Reference in New Issue
Block a user