First slice of the natural/"apocalyptic" events port (M3). The deterministic primitives
every category depends on, ported from GDScript ecological_event_utils:
- hash_noise(x,y,seed) = frac(sin(x*127.1+y*311.7+seed*74.3)*43758.5453), f64 — verified
to match the LIVE GDScript game bit-for-bit (ran it: hash_noise(10,0,1000) =
0.67791910066535). The headless sim must match the game, NOT the TS web guide (whose
Math.sin diverges on these large arguments — a pre-existing game-vs-guide gap, not a
port bug; the old comment's "0.1270 from TS" golden was misleading).
- roll_severity(weights, turn_seed, channel, max_tier) — weighted tier roll with era cap.
- category_fires(base_frequency, channel, turn_seed) — the per-category dispatch gate.
4 cargo tests (GDScript-golden determinism, channel separation, severity bounds + cap,
fire gate). Source corrected: .messy is gone — the port source is the live
ecological_events.gd + handlers_a/b + public/resources/events/*.json. Next: event-config
structs/loading + dispatch + per-category handlers (wildfire first) + turn wiring.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>