Combat: inject per-session RNG into the engine

Adds an optional *rand.Rand to combatState so a fight can be driven by a
deterministic source. Auto-resolve leaves it nil and falls through to the
package global in the same call order — behaviorally identical. Threads
the source through calcDamage and rollWeaponDamage.

Groundwork for the turn-based elite/boss engine and its timeout reaper,
which seed the rng per combat_session to make fights resumable and
replayable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
prosolis
2026-05-13 22:53:06 -07:00
parent 4e412219f3
commit 4af110cc20
3 changed files with 49 additions and 34 deletions

View File

@@ -239,13 +239,13 @@ func dndClassArmorProficiency(class DnDClass, a *ArmorProfile) bool {
// + magic bonus. If twoHanded is true and the weapon is versatile, rolls
// the larger versatile die instead. Returns the unmodified dice total too
// for the crit doubling math.
func rollWeaponDamage(w *WeaponProfile, abilityMod int, twoHanded bool) (total, dice int) {
func rollWeaponDamage(rng *rand.Rand, w *WeaponProfile, abilityMod int, twoHanded bool) (total, dice int) {
count, sides := w.DamageCount, w.DamageSides
if twoHanded && w.HasProperty(PropVersatile) && w.VersaCount > 0 {
count, sides = w.VersaCount, w.VersaSides
}
for i := 0; i < count; i++ {
dice += 1 + rand.IntN(sides)
dice += 1 + rngIntN(rng, sides)
}
total = dice + abilityMod + w.MagicBonus
if total < 1 {