Adv 2.0 Phase L1: Babysit pivot + legacy resolver retire

Babysit pivots from harvest service to pet-care + safe-rest perk:
- runBabysitDaily/runAutoBabysitDay/runBabysitAction deleted; replaced
  by runBabysitDailyTrickle (3 pet XP/day while subscribed).
- BabysitSafeRest predicate promotes Standard camps to Fortified-tier
  rest (HP+1d6, threat -5, resources refresh) and reduces wandering
  monster check campMod from 0 to -4. Hooks live in
  dnd_expedition_camp.go and dnd_expedition_night.go.
- !adventure babysit auto/focus subcommands removed; status/purchase
  DMs reflect the new perks.

Scheduler drops the auto-babysit fallback block (no more silent
debits + harvest-day for missed logins). Babysit subscribers still
skip the morning DM; trickle runs in the background.

TwinBee daily simulator self-contained: simulateTwinBeeOutcome +
simulateTwinBeeLoot replace the resolveAdvAction call against a
fake CombatLevel=35 character. Outcome distribution and reward
shape preserved; gold-share to active players unchanged.

Legacy activity resolver retired: resolveAdvAction,
advEligibleLocations, AdvEligibleLocation, resolveAdvEmptyOutcome
deleted from adventure_activities.go (zero production callers
post-babysit/twinbee). AdvActionResult kept — combat_bridge still
produces it for arena/dungeon flows; dies in L2/L3.

Migration plan doc gogobee_legacy_migration.md added: five-phase
L1-L5 plan covering arena (Adv 2.0 boss flow rebuild), co-op
deletion, perk-gate sweep, and final teardown of AdventureCharacter
+ CombatLevel + combat_engine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-09 00:10:21 -07:00
parent 45863bd5f3
commit 1953eec3b5
10 changed files with 743 additions and 781 deletions

View File

@@ -0,0 +1,108 @@
package plugin
import (
"testing"
"time"
"maunium.net/go/mautrix/id"
)
// L1: babysit pivot from harvest to pet-care + safe-rest.
// These tests cover the pure pieces — DB-touching paths exercise via
// integration only.
func TestBabysitSafeRest_NoDB_ReturnsFalse(t *testing.T) {
// Tests run without db.Init(); BabysitSafeRest must recover and
// return false rather than panicking.
if BabysitSafeRest(id.UserID("@nodb:example")) {
t.Errorf("expected false for un-initialized DB")
}
}
func TestRunBabysitDailyTrickle_NoPetGrantsNothing(t *testing.T) {
// Without a pet, the trickle is a logging-only no-op for character
// state. Without DB, the log call panics inside db.Get; recover and
// assert that the in-memory char fields stay clean.
defer func() { _ = recover() }()
expires := time.Now().UTC().Add(7 * 24 * time.Hour)
char := &AdventureCharacter{
UserID: id.UserID("@nopet:example"),
BabysitActive: true,
BabysitExpiresAt: &expires,
}
p := &AdventurePlugin{}
p.runBabysitDailyTrickle(char)
if char.PetXP != 0 {
t.Errorf("char without pet should not gain PetXP, got %d", char.PetXP)
}
if char.PetLevel != 0 {
t.Errorf("char without pet should not change PetLevel, got %d", char.PetLevel)
}
}
func TestRunBabysitDailyTrickle_SkipsWhenInactive(t *testing.T) {
defer func() { _ = recover() }()
char := &AdventureCharacter{
UserID: id.UserID("@inactive:example"),
BabysitActive: false,
PetType: "dog",
PetName: "Rex",
PetLevel: 1,
PetXP: 0,
}
p := &AdventurePlugin{}
p.runBabysitDailyTrickle(char)
if char.PetXP != 0 {
t.Errorf("inactive babysit should grant no XP, got %d", char.PetXP)
}
}
func TestRunBabysitDailyTrickle_AccumulatesAndLevels(t *testing.T) {
// Pet at L9 with 49 XP needed → 1 day at +3 trickle should *not* level
// (49→52 in centi-XP / 100 = no, wait: petXPToNextLevel at L9 = 50 ×
// 100 = 5000 centi-XP). Verify we accumulate centi-XP correctly across
// multiple daily ticks and eventually level up.
defer func() { _ = recover() }()
char := &AdventureCharacter{
UserID: id.UserID("@accum:example"),
PetType: "dog",
PetName: "Rex",
PetLevel: 1, // needs 10 XP = 1000 centi-XP to L2
PetXP: 900,
BabysitActive: true,
}
expires := time.Now().UTC().Add(48 * time.Hour)
char.BabysitExpiresAt = &expires
p := &AdventurePlugin{}
// One trickle adds 3*100 = 300 centi-XP. 900 + 300 = 1200 >= 1000 →
// level to 2, carry 200 centi-XP.
p.runBabysitDailyTrickle(char)
if char.PetLevel != 2 {
t.Errorf("expected level 2 after trickle, got %d (xp=%d)", char.PetLevel, char.PetXP)
}
if char.PetXP != 200 {
t.Errorf("expected 200 carryover centi-XP, got %d", char.PetXP)
}
}
func TestBabysitLogStats_CountsPetCareDays(t *testing.T) {
logs := []babysitLogEntry{
{Activity: "pet_care", XPGained: 3},
{Activity: "pet_care", XPGained: 3},
{Activity: "pet_care", XPGained: 3, RivalRefused: ""},
{Activity: "rival_refused", RivalRefused: "Garth"},
}
xp, days, rivals := babysitLogStats(logs)
if days != 3 {
t.Errorf("petDays = %d, want 3", days)
}
if xp != 9 {
t.Errorf("totalXP = %d, want 9", xp)
}
if rivals != 1 {
t.Errorf("rivalsRefused = %d, want 1", rivals)
}
}