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

@@ -71,89 +71,6 @@ func TestAdvSlotRelevantToActivity(t *testing.T) {
}
}
func TestResolveAdvAction_MasteryOnlyCountsRelevantSlots(t *testing.T) {
// A foraging action should bump tool but leave weapon/armor/helmet/boots
// untouched. The reverse for combat.
char := &AdventureCharacter{
CombatLevel: 5, ForagingSkill: 5, MiningSkill: 5, FishingSkill: 5,
}
bonuses := &AdvBonusSummary{}
t.Run("foraging only bumps tool", func(t *testing.T) {
loc := &AdvLocation{Name: "T", Tier: 1, Activity: AdvActivityForaging,
MinLevel: 1, BaseDeathPct: 1, EmptyPct: 50}
equip := map[EquipmentSlot]*AdvEquipment{
SlotWeapon: {Tier: 1, Condition: 100, ActionsUsed: 10},
SlotArmor: {Tier: 1, Condition: 100, ActionsUsed: 10},
SlotHelmet: {Tier: 1, Condition: 100, ActionsUsed: 10},
SlotBoots: {Tier: 1, Condition: 100, ActionsUsed: 10},
SlotTool: {Tier: 1, Condition: 100, ActionsUsed: 10},
}
resolveAdvAction(char, equip, loc, bonuses, false)
if equip[SlotTool].ActionsUsed != 11 {
t.Errorf("tool should have advanced 10→11, got %d", equip[SlotTool].ActionsUsed)
}
for _, slot := range []EquipmentSlot{SlotWeapon, SlotArmor, SlotHelmet, SlotBoots} {
if equip[slot].ActionsUsed != 10 {
t.Errorf("%s should be unchanged at 10, got %d", slot, equip[slot].ActionsUsed)
}
}
})
t.Run("combat only bumps combat slots", func(t *testing.T) {
loc := &AdvLocation{Name: "T", Tier: 1, Activity: AdvActivityDungeon,
MinLevel: 1, BaseDeathPct: 1, EmptyPct: 50}
equip := map[EquipmentSlot]*AdvEquipment{
SlotWeapon: {Tier: 1, Condition: 100, ActionsUsed: 10},
SlotArmor: {Tier: 1, Condition: 100, ActionsUsed: 10},
SlotHelmet: {Tier: 1, Condition: 100, ActionsUsed: 10},
SlotBoots: {Tier: 1, Condition: 100, ActionsUsed: 10},
SlotTool: {Tier: 1, Condition: 100, ActionsUsed: 10},
}
resolveAdvAction(char, equip, loc, bonuses, false)
for _, slot := range []EquipmentSlot{SlotWeapon, SlotArmor, SlotHelmet, SlotBoots} {
if equip[slot].ActionsUsed != 11 {
t.Errorf("%s should have advanced 10→11, got %d", slot, equip[slot].ActionsUsed)
}
}
if equip[SlotTool].ActionsUsed != 10 {
t.Errorf("tool should be unchanged at 10, got %d", equip[SlotTool].ActionsUsed)
}
})
}
func TestResolveAdvAction_MasteryCrossingFiresOnceAtBoundary(t *testing.T) {
// Action 49→50 should report a single crossing at threshold 50.
// Action 50→51 should report none (idempotent past the boundary).
loc := &AdvLocation{
Name: "Test", Tier: 1, Activity: AdvActivityForaging,
MinLevel: 1, MinEquipTier: 0, BaseDeathPct: 1, EmptyPct: 50,
}
char := &AdventureCharacter{
CombatLevel: 5, ForagingSkill: 5, MiningSkill: 5, FishingSkill: 5,
}
bonuses := &AdvBonusSummary{}
mkEquip := func(used int) map[EquipmentSlot]*AdvEquipment {
return map[EquipmentSlot]*AdvEquipment{
SlotTool: {Tier: 1, Condition: 100, ActionsUsed: used},
}
}
res1 := resolveAdvAction(char, mkEquip(49), loc, bonuses, false)
if got := len(res1.MasteryCrossings); got != 1 {
t.Fatalf("49→50: expected 1 crossing, got %d", got)
}
if res1.MasteryCrossings[0].Threshold != 50 {
t.Errorf("expected threshold 50, got %d", res1.MasteryCrossings[0].Threshold)
}
res2 := resolveAdvAction(char, mkEquip(50), loc, bonuses, false)
if got := len(res2.MasteryCrossings); got != 0 {
t.Errorf("50→51: expected 0 crossings, got %d", got)
}
}
func TestAdvMasteryRowSegment(t *testing.T) {
cases := []struct {
used int