diff --git a/libs/gw2_analytics/src/gw2_analytics/rotation.py b/libs/gw2_analytics/src/gw2_analytics/rotation.py index 636069df..f4c018dd 100644 --- a/libs/gw2_analytics/src/gw2_analytics/rotation.py +++ b/libs/gw2_analytics/src/gw2_analytics/rotation.py @@ -955,17 +955,63 @@ def add_instant(source: int, skill_id: int, time_ms: int, icd: int = 50) -> None owner = spawn_owner_by_target.get(event.target_agent_id) or event.source_agent_id if owner: add_instant(owner, 12658, event.time_ms) + # Spiteful Spirit (29560) - EI has two finders: + # 1. DamageCastFinder: disabled when effect data exists + # (UsingDisableWithEffectData) + # 2. EffectCastFinder for UnholyBurst: triggers on effect with + # DesertShroud/related hit checks elif isinstance(event, DamageEvent) and event.skill_id == 29560: source_is_necro = ( not professions or professions.get(event.source_agent_id) is Profession.NECROMANCER ) - if source_is_necro and not any( - isinstance(other, EffectEvent) - and other.guid == "C4E8DD3234E0C647993857940ED79AC1" - and other.source_agent_id == event.source_agent_id - and abs(other.time_ms - event.time_ms) < 50 - for other in nearby_events(event.time_ms, 49) - ): + if not source_is_necro: + continue + + # Check if there's any UnholyBurst effect in the fight (global check) + has_unholy_burst_in_fight = any( + isinstance(other, EffectEvent) and other.guid == "C4E8DD3234E0C647993857940ED79AC1" + for other in event_list + ) + + if has_unholy_burst_in_fight: + # EI uses EffectCastFinder: only emit when UnholyBurst effect is present + # with DesertShroud and related hit checks + unholy_burst_nearby = any( + isinstance(other, EffectEvent) + and other.guid == "C4E8DD3234E0C647993857940ED79AC1" + and other.source_agent_id == event.source_agent_id + and abs(other.time_ms - event.time_ms) < 100 + for other in nearby_events(event.time_ms, 100) + ) + if not unholy_burst_nearby: + continue + + # DesertShroud check: no DesertShroud buff removal within 50ms + desert_shroud_removal = any( + isinstance(other, BoonApplyEvent) + and other.kind == "remove_all" + and other.skill_id == 40052 # DesertShroudBuff + and other.source_agent_id == event.source_agent_id + and abs(other.time_ms - event.time_ms) < 50 + for other in nearby_events(event.time_ms, 50) + ) + if desert_shroud_removal: + continue + + # Related hit check: no UnholyBurst hit from same caster within 10ms + related_hit = any( + isinstance(other, DamageEvent) + and other.source_agent_id == event.source_agent_id + and other.skill_id == 38767 # UnholyBurst + and abs(other.time_ms - event.time_ms) < 10 + for other in nearby_events(event.time_ms, 10) + ) + if related_hit: + continue + + add_instant(event.source_agent_id, event.skill_id, event.time_ms) + else: + # No UnholyBurst effect data in fight: use DamageCastFinder add_instant(event.source_agent_id, event.skill_id, event.time_ms) elif isinstance(event, DamageEvent) and event.skill_id in _DAMAGE_CASTS: add_instant(