Adv 2.0 D&D Phase 12 E3e: Dragon's Lair awareness pulses

§7.5 awareness pulses on Day 3, 6, 9, 12, ... apply +10 threat at
briefing rollover and log a temporal entry (DragonsLairAwarenessPulse
flavor). Day-14 awakening sets RegionState["infernax_awake"]=true
once, with the §7.5 awakening narration; the pulse line is suppressed
that day so the awakening narration carries the moment alone.

Combat-side knobs (kobold patrols +1 enemy per active room, 6h
wandering cadence, dragon-encounter weighting in the wandering table,
forced extraction pressure once awake) are exposed via the
infernax_awake region flag — combat-link phase reads it to switch
behavior. Boss-defeated suppresses entirely.

Reuses flavor.DragonsLairAwarenessPulse / DragonsLairAwakenWarning per
feedback_reuse_existing_flavor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-08 16:05:35 -07:00
parent 3a000ada8c
commit ff7d55f33b
2 changed files with 209 additions and 0 deletions

View File

@@ -138,10 +138,18 @@ func applyZoneTemporalPostRollover(e *Expedition) []string {
return underforgeTemporalPostRollover(e)
case ZoneFeywildCrossing:
return feywildTemporalPostRollover(e)
case ZoneDragonsLair:
return dragonsLairTemporalPostRollover(e)
}
return nil
}
// DragonsLairAwarenessCycleDays — §7.5 awareness pulse cadence.
const DragonsLairAwarenessCycleDays = 3
// DragonsLairAwakenDay — §7.5 Infernax wakes if not yet at the boss.
const DragonsLairAwakenDay = 14
// UnderforgeMaxHeat is the §7.3 cap.
const UnderforgeMaxHeat = 10
@@ -360,6 +368,65 @@ func feywildTemporalPostRollover(e *Expedition) []string {
return nil
}
// ─────────────────────────────────────────────────────────────────────
// §7.5 — Dragon's Lair, Awareness Pulses + Infernax Awakening
// ─────────────────────────────────────────────────────────────────────
// dragonsLairTemporalPostRollover fires an awareness pulse every 3
// days (threat +10, narration) and the Day-14 awakening event if the
// boss hasn't been killed yet.
func dragonsLairTemporalPostRollover(e *Expedition) []string {
if e.BossDefeated {
return nil
}
day := e.CurrentDay
var lines []string
// Day 14 awakening: one-time. Mark on RegionState; downstream
// combat-link reads "infernax_awake" to switch wandering cadence
// to every 6 hours and add the dragon encounter weighting.
if day >= DragonsLairAwakenDay {
if awake, _ := e.RegionState["infernax_awake"].(bool); !awake {
e.RegionState["infernax_awake"] = true
_ = persistRegionState(e)
line := flavor.Pick(flavor.DragonsLairAwakenWarning)
_ = appendExpeditionLog(e.ID, day, "temporal",
"infernax awakens — active hunting begins, 6h wandering checks",
line)
lines = append(lines, line)
}
}
// Awareness pulse every 3 days (3, 6, 9, 12, ...). On Day 14, the
// awakening narration takes precedence — we still apply the +10
// since the cadence math says it's a pulse day too.
if day >= DragonsLairAwarenessCycleDays && day%DragonsLairAwarenessCycleDays == 0 {
_ = applyThreatDelta(e.ID, 10, fmt.Sprintf("dragon awareness pulse (day %d)", day))
// Refresh in-memory threat after the bump.
e.ThreatLevel += 10
if e.ThreatLevel > 100 {
e.ThreatLevel = 100
}
// Skip the pulse narration if the awaken line already fired
// today — they say largely the same thing in different keys.
alreadyAwoke := false
for _, l := range lines {
if l != "" {
alreadyAwoke = true
break
}
}
if !alreadyAwoke {
line := flavor.Pick(flavor.DragonsLairAwarenessPulse)
_ = appendExpeditionLog(e.ID, day, "temporal",
fmt.Sprintf("awareness pulse — threat +10 (day %d)", day),
line)
lines = append(lines, line)
}
}
return lines
}
// reduceUnderforgeHeat is called by processOvernightCamp when the
// active camp is fortified (or base) in the Underforge — long rest
// drops heat stacks by 2 per §7.3. Returns the new stack count.