mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
Adv 2.0 D&D Phase 12 E2b: Wandering monster table + night phase
§6 night-phase resolution. Fires once per recap when the player has an
active camp. resolveWanderingCheck rolls 1d20 + threat mod (+1 per full
10 above 30) + camp mod (rough +3, fortified -4, base -6) + class mod
(ranger -2 in wilderness zones), then buckets per §6.1 into Peaceful /
Passage / Minor / Standard / Elite / Ambush.
Encounter side picks a roster entry from the zone (weighted by
SpawnWeight, biased to elite for Elite/Ambush) and persists the result
to camp.NightEvents + a "night"-typed log entry. Signs-of-passage adds
+2 threat per spec. Combat resolution itself is deferred — expeditions
don't host their own combat loop yet — so encounter outcomes log as
pending and surface in the recap with an "encounter pending" hint;
the combat-link phase will read these on next !advance.
Flavor reuse: encounter narration pulls from the existing ThreatClock
{Stirring,Alert,Hostile,Siege} pools, matched to the current band so
voice tracks the dungeon's awareness level.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -191,12 +191,36 @@ func (p *AdventurePlugin) deliverBriefing(e *Expedition, now time.Time) error {
|
||||
// deliverRecap posts the evening recap DM, appends a log entry, and stamps
|
||||
// last_recap_at. No supply burn here — that's the briefing's job.
|
||||
func (p *AdventurePlugin) deliverRecap(e *Expedition, now time.Time) error {
|
||||
// E2b: night phase wandering check fires before the recap so its
|
||||
// outcome is part of today's log when the recap renders.
|
||||
var night *NightCheck
|
||||
if e.Camp != nil && e.Camp.Active {
|
||||
c, _ := LoadDnDCharacter(id.UserID(e.UserID))
|
||||
var charClass DnDClass
|
||||
if c != nil {
|
||||
charClass = c.Class
|
||||
}
|
||||
nc := resolveWanderingCheck(e, charClass, nil)
|
||||
if err := processNightCheck(e, nc); err != nil {
|
||||
slog.Warn("expedition: night check", "expedition", e.ID, "err", err)
|
||||
}
|
||||
night = &nc
|
||||
// Refresh in-memory threat after possible signs-of-passage bump.
|
||||
if fresh, err := getExpedition(e.ID); err == nil && fresh != nil {
|
||||
e.ThreatLevel = fresh.ThreatLevel
|
||||
e.SiegeMode = fresh.SiegeMode
|
||||
}
|
||||
}
|
||||
|
||||
dayEntries, err := dayLogEntries(e.ID, e.CurrentDay)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
line := pickEveningRecap(e, dayEntries)
|
||||
body := renderEveningRecap(e, line, dayEntries)
|
||||
if night != nil {
|
||||
body += "\n" + renderNightCheck(*night)
|
||||
}
|
||||
|
||||
if uid := id.UserID(e.UserID); uid != "" {
|
||||
if err := p.SendDM(uid, body); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user