mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 16:42:41 +00:00
activeOmen() is a pure function of the UTC ISO (year, week): omenTable indexed by (year*53+week)%len, so it advances weekly with no schema, no ticker state, no persistence. Five non-combat seams read it — harvest yield, supply freebie, expedition start mood, arena payout (scales gross earnings before the pot tax), and ingredient drop chance. TwinBee reveals the active omen in the existing morning DM (no net-new scheduled message). Launch set is buffs-with-texture on non-combat levers only: Bountiful Harvest, Quartermaster's Blessing, Golden Purse, Overflowing Satchels, Still Waters. Nothing touches SimulateCombat or the turn engine — the omen is keyed on the real clock, so a combat mutator would make the golden and the balance corpus week-dependent. The plan's "elites +2 ATK" is deliberately dropped for that reason. The balance sim drives the real expedition loop and would otherwise traverse all five seams, making corpus sweeps depend on the wall-clock week. NewSimRunner sets simOmenDisabled (mirrors simAutoArmEnabled), so activeOmen returns a no-effect omen under the sim. Still Waters subtracts from the daily threat *rise* only, floored at hold-steady — it never forces active decay. Claude-Session: https://claude.ai/code/session_017mEwUmmS7aQTP2NQXj6rUa
97 lines
4.1 KiB
Go
97 lines
4.1 KiB
Go
package plugin
|
|
|
|
import (
|
|
"time"
|
|
)
|
|
|
|
// N7/B3 — the Omen: one rotating world modifier per ISO week
|
|
// (gogobee_engagement_plan.md §B3).
|
|
//
|
|
// The active omen is a pure function of the ISO (year, week): omenTable indexed
|
|
// by (year*53 + week) % len, so it advances by exactly one entry each week and
|
|
// needs no schema, no ticker state, and no persistence. Every seam reads
|
|
// activeOmen() the same way isHolidayToday() is read, and a zero-valued effect
|
|
// field means "this omen doesn't touch that seam."
|
|
//
|
|
// Launch-set rule (§B3): every omen is a buff-with-texture on a NON-combat
|
|
// lever — harvest, supplies, expedition mood/threat, arena payout, ingredient
|
|
// drops. Nothing touches SimulateCombat or the turn engine: the omen is keyed
|
|
// on the real clock, so a combat-affecting omen would make the combat golden
|
|
// and the balance corpus week-dependent. The plan's "elites +2 ATK" mutator is
|
|
// deliberately omitted for exactly that reason.
|
|
|
|
type omen struct {
|
|
Key string // stable id (tests, logs)
|
|
Name string // player-facing name, e.g. "Bountiful Harvest"
|
|
TwinBee string // first-person announce blurb (TwinBee voice)
|
|
|
|
// Effect fields — zero == no effect at that seam.
|
|
HarvestYieldBonus int // + units per harvest grant
|
|
SupplyFreebiePacks int // + complimentary standard packs at outfitting
|
|
StartMoodBonus int // + starting DM mood on a new expedition
|
|
ArenaPayoutMult float64 // >1 scales arena net earnings
|
|
ConsumableChanceMult float64 // >1 scales the per-win ingredient drop chance
|
|
ThreatDriftReduce int // subtract from the daily threat *rise* (floored at hold-steady)
|
|
}
|
|
|
|
// simOmenDisabled neutralizes the weekly Omen for the balance sim, so a corpus
|
|
// sweep's results never depend on which wall-clock week it was run in. Set true
|
|
// by NewSimRunner (mirrors simAutoArmEnabled). Every seam reads activeOmen(),
|
|
// which returns the no-effect omen while this is set.
|
|
var simOmenDisabled bool
|
|
|
|
// omenTable is the weekly rotation. Keep it a set of distinct non-combat levers;
|
|
// order is the rotation order. Adding an entry reshuffles the schedule but never
|
|
// breaks determinism (still a pure function of the week).
|
|
var omenTable = []omen{
|
|
{
|
|
Key: "bountiful_harvest", Name: "Bountiful Harvest",
|
|
TwinBee: "The land's feeling generous this week — every gather I make comes up with a little extra in hand.",
|
|
HarvestYieldBonus: 1,
|
|
},
|
|
{
|
|
Key: "quartermasters_blessing", Name: "Quartermaster's Blessing",
|
|
TwinBee: "Somebody left the storerooms unlocked. Outfitting an expedition this week? There's a free pack in it, and I'm setting out in good spirits.",
|
|
SupplyFreebiePacks: 1,
|
|
StartMoodBonus: 5,
|
|
},
|
|
{
|
|
Key: "golden_purse", Name: "Golden Purse",
|
|
TwinBee: "The arena crowd's flush this week — purses are paying out fat. Twenty percent over the odds, if you can win it.",
|
|
ArenaPayoutMult: 1.20,
|
|
},
|
|
{
|
|
Key: "overflowing_satchels", Name: "Overflowing Satchels",
|
|
TwinBee: "Reagents are turning up everywhere I look — twice as often as usual. Good week to stock the crafting shelf.",
|
|
ConsumableChanceMult: 2.0,
|
|
},
|
|
{
|
|
Key: "still_waters", Name: "Still Waters",
|
|
TwinBee: "It's quiet out there. Whatever's hunting us is slow to rouse this week — the daily dread holds steady instead of creeping up.",
|
|
ThreatDriftReduce: 1,
|
|
},
|
|
}
|
|
|
|
// omenForWeek returns the omen for an ISO (year, week). Pure and total.
|
|
func omenForWeek(year, week int) omen {
|
|
idx := ((year*53)+week)%len(omenTable) + len(omenTable)
|
|
return omenTable[idx%len(omenTable)]
|
|
}
|
|
|
|
// activeOmen returns this week's omen (UTC ISO week), or a no-effect omen when
|
|
// the balance sim has disabled it.
|
|
func activeOmen() omen {
|
|
if simOmenDisabled {
|
|
return omen{Key: "none", Name: "None"}
|
|
}
|
|
y, w := time.Now().UTC().ISOWeek()
|
|
return omenForWeek(y, w)
|
|
}
|
|
|
|
// omenMorningLine is the compact one-liner surfaced in the morning DM (the §B3
|
|
// announce seam). Empty is never returned — an omen is always active — but the
|
|
// caller may still choose when to show it.
|
|
func omenMorningLine(o omen) string {
|
|
return "🔮 **The Omen — " + o.Name + ".** _" + o.TwinBee + "_"
|
|
}
|