ModernUO/Projects/UOContent/Spells/Necromancy
Kamron Batman 10fb74b827
fix(necromancy): correct Blood Oath duration, reflection, and expiry timing (#1690) (#2480)
Fixes #1690. Addresses Blood Oath holistically — three bugs found while researching the spell against RunUO, ServUO, the UODemise/uo.com guides, and the archived UOGuide page.

## Bugs fixed

### 1. Expiry timing (the filed issue)
The `ExpireTimer` polled every 1s, so expiry and death/delete cleanup lagged up to ~1s. Replaced with a **single-shot** timer plus centralized `[OnEvent]` handlers on `PlayerDeathEvent`/`PlayerDeletedEvent`/`CreatureDeathEvent`/`CreatureDeletedEvent` — the oath now breaks immediately on death/delete of either party.

### 2. Duration formula
Used `/80` (the bugged in-game tooltip value) instead of the real OSI formula `((SpiritSpeak - Resist) / 8) + 8`. Confirmed by RunUO, ServUO, the emulator guides, and the code's own fixed-point comment. At GM Spirit Speak this changes duration from ~9.5s to 23s and makes Spirit Speak actually affect duration.

### 3. Damage reflection (`BaseCreature.Damage` vs `PlayerMobile.Damage`)
`BaseCreature.Damage` diverged: it attributed the reflected hit to the attacker itself (`from.Damage(amount, from)`) instead of the caster, reflected the bonused (not original) amount, used `×1.1` vs `×1.2`, lacked the caster-survival guard, and had no Publish 48 resist mitigation.

Unified both paths: reflect the **original** damage attributed to the **caster** at `×1.2`. Publish 48 resist mitigation now applies only to creature casters and is gated behind `Core.SA`.

## Internals
- Collapsed the parallel `_oathTable` into a single `_table` keyed by both participants → shared timer, so `RemoveCurse` resolves from either side (required by the event handlers).
- Extracted `GetDurationSeconds` and `ComputeReflectedDamage` as testable statics.

## Tests
13 new tests (duration formula, reflection mitigation, oath lifecycle, end-to-end event-driven removal). Full suite: **436/436 pass**.
2026-06-08 23:42:28 -07:00
..
AnimateDeadSpell.cs chore: Use var everywhere (#2294) 2025-12-27 16:47:28 -08:00
BloodOathSpell.cs fix(necromancy): correct Blood Oath duration, reflection, and expiry timing (#1690) (#2480) 2026-06-08 23:42:28 -07:00
CorpseSkin.cs fix: Fixes buffs don't start/stop properly. Streamlines constructor and Add/Remove buffs. (#2082) 2025-01-24 18:20:23 -08:00
CurseWeapon.cs fix: Fixes various memory leaks and spells (#1786) 2024-05-21 19:44:42 -07:00
EvilOmen.cs fix: Fixes buffs don't start/stop properly. Streamlines constructor and Add/Remove buffs. (#2082) 2025-01-24 18:20:23 -08:00
Exorcism.cs fix: Fixes various memory leaks and spells (#1786) 2024-05-21 19:44:42 -07:00
HorrificBeast.cs fix: Fixes various memory leaks and spells (#1786) 2024-05-21 19:44:42 -07:00
LichForm.cs fix: Fixes various memory leaks and spells (#1786) 2024-05-21 19:44:42 -07:00
MindRot.cs fix: Fixes buffs don't start/stop properly. Streamlines constructor and Add/Remove buffs. (#2082) 2025-01-24 18:20:23 -08:00
NecromancerSpell.cs fix: Fixes various memory leaks and spells (#1786) 2024-05-21 19:44:42 -07:00
PainSpike.cs fix: Fixes buffs don't start/stop properly. Streamlines constructor and Add/Remove buffs. (#2082) 2025-01-24 18:20:23 -08:00
PoisonStrike.cs chore: Use var everywhere (#2294) 2025-12-27 16:47:28 -08:00
Strangle.cs fix: Fixes buffs don't start/stop properly. Streamlines constructor and Add/Remove buffs. (#2082) 2025-01-24 18:20:23 -08:00
SummonFamiliar.cs fix: Updates SummonFamiliar & Tracking gumps to static (#2098) 2025-01-27 17:57:25 -08:00
TransformationSpell.cs fix: Fixes various memory leaks and spells (#1786) 2024-05-21 19:44:42 -07:00
VampiricEmbrace.cs fix: Fixes various memory leaks and spells (#1786) 2024-05-21 19:44:42 -07:00
VengefulSpirit.cs fix: Magic NPCs now use spell range (#1870) 2024-08-08 07:05:27 -07:00
Wither.cs fix: Fixes various memory leaks and spells (#1786) 2024-05-21 19:44:42 -07:00
WraithForm.cs fix: Fixes various memory leaks and spells (#1786) 2024-05-21 19:44:42 -07:00