mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
N6/D3: the Shadow — a simulated rival adventurer
A per-player NPC rival who "runs" the same zone progression on a midnight ticker at ~1.3x the player's own clear pace, staying just ahead so it's a race you can always see and nearly catch. Pure theatre: no combat, no punishment, only race pressure and two payoffs at each zone clear. - New adventure_shadow table, deliberately OUTSIDE the player_meta save fan-out so a character save can never clobber the ticker's advance (the isolation journal_pages earns by being grant-only, made structural). No bootstrap: absent row == no Shadow, minted lazily on first advance. - midnightReset advances every player's Shadow once per UTC day (own idempotency guard); lead-capped so it never runs >2.5 zones ahead. When it clears a zone the player hasn't, it leaves a journal page waiting (D1 tie-in). - Morning-briefing race-pressure one-liners (TwinBee voice, deterministic). - Zone-clear payoff in finalizeExpeditionOnZoneClear: a bonus-XP crow when the player got there first, or the Shadow's waiting page when it did. - !adventure shadow status view. Review fixes (3 finders + verify) folded in before commit: - Crow XP is now set-once per zone (crowed_mask), so re-running a zone the Shadow hasn't reached can't farm it. - The waiting page is granted BEFORE the pending bit is retired, so a transient grant failure leaves the debt for the next clear instead of swallowing a page. - The crow line no longer claims "+XP" when the grant errored. Combat golden byte-identical (Shadow never touches SimulateCombat); go build/vet/test green repo-wide. Claude-Session: https://claude.ai/code/session_017mEwUmmS7aQTP2NQXj6rUa
This commit is contained in:
@@ -170,7 +170,14 @@ func (p *AdventurePlugin) finalizeExpeditionOnZoneClear(userID id.UserID, runID
|
||||
exp.Status = ExpeditionStatusComplete
|
||||
_ = retireAllRegionRuns(exp)
|
||||
p.rollZoneTreasure(userID, exp.ZoneID, advTreasureWeightZoneClear)
|
||||
return p.AwardCompletionMilestones(exp, false)
|
||||
lines := p.AwardCompletionMilestones(exp, false)
|
||||
// N6/D3: the Shadow's payoff for this zone — a crow (player was first) or a
|
||||
// waiting journal page (the Shadow cleared it first). Appended after the
|
||||
// milestone lines so the campaign beat reads last.
|
||||
if sl := p.shadowOnPlayerZoneClear(userID, exp.ZoneID); sl != "" {
|
||||
lines = append(lines, sl)
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
// midZoneRegionClear reports whether the just-completed run (runID) is the
|
||||
|
||||
Reference in New Issue
Block a user