diff --git a/Source/Game/AI/ScriptedAI/ScriptedAI.cs b/Source/Game/AI/ScriptedAI/ScriptedAI.cs index 1a6a05e7f..f82d54362 100644 --- a/Source/Game/AI/ScriptedAI/ScriptedAI.cs +++ b/Source/Game/AI/ScriptedAI/ScriptedAI.cs @@ -550,7 +550,6 @@ namespace Game.AI if (!me.IsAlive()) return; - me.SetCombatPulseDelay(0); me.ResetLootMode(); _events.Reset(); summons.DespawnAll(); @@ -581,9 +580,7 @@ namespace Game.AI instance.SetBossState(_bossId, EncounterState.InProgress); } - me.SetCombatPulseDelay(5); me.SetActive(true); - DoZoneInCombat(); ScheduleTasks(); } diff --git a/Source/Game/Entities/Creature/Creature.Fields.cs b/Source/Game/Entities/Creature/Creature.Fields.cs index 6e15614fe..afd6cc728 100644 --- a/Source/Game/Entities/Creature/Creature.Fields.cs +++ b/Source/Game/Entities/Creature/Creature.Fields.cs @@ -80,8 +80,6 @@ namespace Game.Entities bool m_ignoreCorpseDecayRatio; float m_wanderDistance; uint m_boundaryCheckTime; // (msecs) remaining time for next evade boundary check - uint m_combatPulseTime; // (msecs) remaining time for next zone-in-combat pulse - uint m_combatPulseDelay; // (secs) how often the creature puts the entire zone in combat (only works in dungeons) // vendor items List m_vendorItemCounts = new(); diff --git a/Source/Game/Entities/Creature/Creature.cs b/Source/Game/Entities/Creature/Creature.cs index 39c3bfe37..ff49709d4 100644 --- a/Source/Game/Entities/Creature/Creature.cs +++ b/Source/Game/Entities/Creature/Creature.cs @@ -562,29 +562,6 @@ namespace Game.Entities m_boundaryCheckTime -= diff; } - // if periodic combat pulse is enabled and we are both in combat and in a dungeon, do this now - if (m_combatPulseDelay > 0 && IsEngaged() && GetMap().IsDungeon()) - { - if (diff > m_combatPulseTime) - m_combatPulseTime = 0; - else - m_combatPulseTime -= diff; - - if (m_combatPulseTime == 0) - { - var players = GetMap().GetPlayers(); - foreach (var player in players) - { - if (player.IsGameMaster()) - continue; - - if (player.IsAlive() && IsHostileTo(player)) - EngageWithTarget(player); - } - m_combatPulseTime = m_combatPulseDelay * Time.InMilliseconds; - } - } - AIUpdateTick(diff); DoMeleeAttackIfReady(); @@ -645,6 +622,14 @@ namespace Game.Entities } } + public override void Heartbeat() + { + base.Heartbeat(); + + // Creatures with CREATURE_STATIC_FLAG_2_FORCE_PARTY_MEMBERS_INTO_COMBAT periodically force party members into combat + ForcePartyMembersIntoCombat(); + } + public void Regenerate(PowerType power) { int curValue = GetPower(power); @@ -1124,6 +1109,9 @@ namespace Game.Entities CreatureGroup formation = GetFormation(); if (formation != null) formation.MemberEngagingTarget(this, target); + + // Creatures with CREATURE_STATIC_FLAG_2_FORCE_PARTY_MEMBERS_INTO_COMBAT periodically force party members into combat + ForcePartyMembersIntoCombat(); } public override void AtDisengage() @@ -1142,6 +1130,39 @@ namespace Game.Entities } } + void ForcePartyMembersIntoCombat() + { + if (!_staticFlags.HasFlag(CreatureStaticFlags2.ForcePartyMembersIntoCombat) || !IsEngaged()) + return; + + List partiesToForceIntoCombat = new(); + foreach (var (_, combatReference) in GetCombatManager().GetPvECombatRefs()) + { + if (combatReference.IsSuppressedFor(this)) + continue; + + Player player = combatReference.GetOther(this)?.ToPlayer(); + if (player == null || player.IsGameMaster()) + continue; + + Group group = player.GetGroup(); + if (group != null) + partiesToForceIntoCombat.Add(group); + } + + foreach (Group partyToForceIntoCombat in partiesToForceIntoCombat) + { + for (GroupReference refe = partyToForceIntoCombat.GetFirstMember(); refe != null; refe = refe.Next()) + { + Player player = refe.GetSource(); + if (player == null || !player.IsInWorld || player.GetMap() != GetMap() || player.IsGameMaster()) + continue; + + EngageWithTarget(player); + } + } + } + public bool IsEscorted() { CreatureAI ai = GetAI(); @@ -3602,13 +3623,6 @@ namespace Game.Entities public void SetWanderDistance(float dist) { m_wanderDistance = dist; } public void DoImmediateBoundaryCheck() { m_boundaryCheckTime = 0; } - uint GetCombatPulseDelay() { return m_combatPulseDelay; } - public void SetCombatPulseDelay(uint delay) // (secs) interval at which the creature pulses the entire zone into combat (only works in dungeons) - { - m_combatPulseDelay = delay; - if (m_combatPulseTime == 0 || m_combatPulseTime > delay) - m_combatPulseTime = delay; - } bool CanRegenerateHealth() { return !_staticFlags.HasFlag(CreatureStaticFlags5.NoHealthRegen) && _regenerateHealth; } public void SetRegenerateHealth(bool value) { _staticFlags.ApplyFlag(CreatureStaticFlags5.NoHealthRegen, !value); }