Adv 2.0 D&D Phase 12 E6b: Expedition milestones (§13)

Daily milestones fire from the morning briefing once the day count
crosses the threshold (CurrentDay reflects the new day post-rollover):
- First Night (day 2, +50 XP)
- Week One (day 8, +200 XP, Thom Krooke discount flag — hookup deferred)
- Two Weeks (day 15, +500 XP, +1 primary stat — char-system hookup deferred)

Completion milestones via AwardCompletionMilestones, exported for the
combat-link boss-kill path to call once status flips to 'complete':
- Patient Zero (max threat ≤ 50, +10% of XPEarned bonus)
- Survivalist (tier ≥ 3, no forced extraction ever, title flag)
- Long Game (tier 5, legendary item — loot-grant hookup deferred)

Reuses MilestoneFirstNight/WeekOne/TwoWeeks/PatientZero/TheLongGame
flavor pools from twinbee_expedition_flavor.go. Cartographer and
Survivalist have no flavor pool (Survivalist uses an inline string,
Cartographer is fully deferred since it needs combat-link search hooks).

Awarded keys persist as RegionState["milestones"]. Max threat sampled
daily into RegionState["max_threat_seen"] before milestone checks
read it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-08 16:43:38 -07:00
parent 157455a3fe
commit cbb2670bb8
3 changed files with 371 additions and 0 deletions

View File

@@ -213,6 +213,12 @@ func (p *AdventurePlugin) deliverBriefing(e *Expedition, now time.Time) error {
}
}
// E6b: sample today's threat into RegionState["max_threat_seen"] before
// any milestone check reads it; then award daily milestones reached by
// the new day count (First Night day 2, Week One day 8, Two Weeks day 15).
_ = recordMaxThreat(e)
milestoneLines := p.checkDailyMilestones(e)
line := pickMorningBriefing(e.CurrentDay)
body := renderMorningBriefing(e, line, burn)
if restSummary != "" {
@@ -221,6 +227,9 @@ func (p *AdventurePlugin) deliverBriefing(e *Expedition, now time.Time) error {
for _, tl := range temporalLines {
body += "\n🌀 " + tl + "\n"
}
for _, ml := range milestoneLines {
body += "\n" + ml
}
if uid := id.UserID(e.UserID); uid != "" {
if err := p.SendDM(uid, body); err != nil {