From f6a457ae84cec34905a3e3257e4d68b7d2192af1 Mon Sep 17 00:00:00 2001 From: prosolis <5590409+prosolis@users.noreply.github.com> Date: Thu, 21 May 2026 23:45:57 -0700 Subject: [PATCH] Pets: fire 25% morning event from expedition briefing + fix defense-buff leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recovers the genuinely-missing half of ade0335 (orphaned on the unmerged phase-H-harvest-charges branch). 3ed2e1d redid pet *arrival* via the emergence seam and explicitly deferred the morning event; this closes that gap. - deliverBriefing now rolls the 25% morning pet event (petMorningEvent, already present but only wired into the overworld DM that underground players never see) and prepends it to the briefing body, granting the one-day PetMorningDefense buff. - Add resetAllPetMorningDefense + wire it into midnightReset. The buff was leaking permanently even on HEAD's existing overworld path โ€” it was set on the 25% roll but never cleared. Resets the pet_flags_json key in place. Deliberately omits ade0335's ArrivalPending machinery: arrival is now the emergence seam's job (3ed2e1d + 978dc5e + 513cf32), so queuing arrival from the briefing would double-roll it. --- internal/plugin/adventure_scheduler.go | 6 ++++++ internal/plugin/dnd_expedition_cycle.go | 16 ++++++++++++++++ internal/plugin/player_meta.go | 13 +++++++++++++ 3 files changed, 35 insertions(+) diff --git a/internal/plugin/adventure_scheduler.go b/internal/plugin/adventure_scheduler.go index 0661818..d66de9d 100644 --- a/internal/plugin/adventure_scheduler.go +++ b/internal/plugin/adventure_scheduler.go @@ -495,6 +495,12 @@ func (p *AdventurePlugin) midnightReset() error { return fmt.Errorf("reset daily actions after 3 attempts: %w", resetErr) } + // Clear the one-day pet morning-defense buff so it re-rolls fresh each + // morning (briefing or overworld DM) instead of leaking permanently. + if err := resetAllPetMorningDefense(); err != nil { + slog.Error("adventure: failed to reset pet morning defense", "err", err) + } + // Prune expired buffs if err := pruneAdvExpiredBuffs(); err != nil { slog.Error("adventure: failed to prune expired buffs", "err", err) diff --git a/internal/plugin/dnd_expedition_cycle.go b/internal/plugin/dnd_expedition_cycle.go index fa2df6d..ddc7c2f 100644 --- a/internal/plugin/dnd_expedition_cycle.go +++ b/internal/plugin/dnd_expedition_cycle.go @@ -300,6 +300,22 @@ func (p *AdventurePlugin) deliverBriefing(e *Expedition, now time.Time) error { } if uid := id.UserID(e.UserID); uid != "" { + // The legacy overworld morning DM is skipped while underground, so + // its 25% morning pet event fires here instead, granting the one-day + // defense buff (reset at midnight via resetAllPetMorningDefense). + // Pet *arrival* is handled separately on the emergence seam below โ€” + // not queued here โ€” so we only roll the morning event. + if pet, perr := loadPetState(uid); perr == nil { + if petEvent := petMorningEvent(pet); petEvent != "" { + if char, cerr := loadAdvCharacter(uid); cerr == nil { + char.PetMorningDefense = true + if serr := saveAdvCharacter(char); serr != nil { + slog.Warn("expedition: save pet morning defense", "user", uid, "err", serr) + } + } + body = fmt.Sprintf("๐Ÿพ *%s*\n\n%s", petEvent, body) + } + } if err := p.SendDM(uid, body); err != nil { slog.Warn("expedition: send briefing DM", "user", uid, "err", err) } diff --git a/internal/plugin/player_meta.go b/internal/plugin/player_meta.go index b92cfb7..2304eef 100644 --- a/internal/plugin/player_meta.go +++ b/internal/plugin/player_meta.go @@ -909,6 +909,19 @@ func resetAllPlayerMetaDailyActions() error { return err } +// resetAllPetMorningDefense clears the one-day pet morning-defense buff for +// every player. The buff is a fresh morning grant (25% roll in the briefing / +// overworld DM), so it must not survive past the midnight rollover. The flag +// lives inside pet_flags_json (see petFlagsJSON), so patch that key in place; +// only rows where it's currently set are touched. +func resetAllPetMorningDefense() error { + _, err := db.Get().Exec(` + UPDATE player_meta + SET pet_flags_json = json_set(pet_flags_json, '$.morning_defense', json('false')) + WHERE json_extract(pet_flags_json, '$.morning_defense') = 1`) + return err +} + // DeathState mirrors player_meta's death-state columns. Phase L5e ports // these fields off AdvCharacter (gogobee_legacy_migration.md ยง7.3 L5e). // Mutation surface is large (~50 saveAdvCharacter sites touch death