From e8588fd5ba0e30e6a7c3f860c296f04171c8c885 Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Sun, 9 Mar 2025 02:40:05 +0100 Subject: [PATCH] Core/Objects: ported Heartbeat implementation fron master branch --- .../game/AI/ScriptedAI/ScriptedCreature.cpp | 2 - .../game/Entities/Creature/Creature.cpp | 76 ++++++---- src/server/game/Entities/Creature/Creature.h | 15 +- .../game/Entities/GameObject/GameObject.cpp | 2 +- src/server/game/Entities/Object/Object.cpp | 16 +- src/server/game/Entities/Object/Object.h | 7 +- src/server/game/Entities/Player/Player.cpp | 143 +++++++----------- src/server/game/Entities/Player/Player.h | 17 ++- src/server/game/Entities/Unit/Unit.cpp | 47 ++++-- src/server/game/Entities/Unit/Unit.h | 5 +- src/server/game/Handlers/MiscHandler.cpp | 2 +- src/server/game/Handlers/MovementHandler.cpp | 21 +++ src/server/game/Scripting/ScriptMgr.cpp | 5 + src/server/game/Spells/Auras/SpellAuras.cpp | 36 +++++ src/server/game/Spells/Auras/SpellAuras.h | 5 + src/server/game/Spells/SpellMgr.h | 2 +- src/server/game/Spells/SpellScript.h | 15 +- .../ThroneOfTheTides/boss_ozumat.cpp | 5 +- .../boss_blood_prince_council.cpp | 3 - .../boss_lady_deathwhisper.cpp | 1 - .../boss_professor_putricide.cpp | 1 - .../IcecrownCitadel/boss_sindragosa.cpp | 1 - .../IcecrownCitadel/boss_the_lich_king.cpp | 1 - .../boss_valithria_dreamwalker.cpp | 1 - .../Naxxramas/boss_four_horsemen.cpp | 2 - .../Northrend/Naxxramas/boss_gothik.cpp | 1 - .../Northrend/Naxxramas/boss_kelthuzad.cpp | 2 - .../UtgardePinnacle/boss_palehoof.cpp | 2 - .../BlackTemple/boss_illidari_council.cpp | 2 - .../BlackTemple/boss_reliquary_of_souls.cpp | 3 - 30 files changed, 257 insertions(+), 184 deletions(-) diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index cdce02e0b2..a13d9b2f87 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -467,7 +467,6 @@ void BossAI::_Reset() if (!me->IsAlive()) return; - me->SetCombatPulseDelay(0); me->ResetLootMode(); events.Reset(); summons.DespawnAll(); @@ -503,7 +502,6 @@ void BossAI::_JustEngagedWith(Unit* who) instance->SetBossState(_bossId, IN_PROGRESS); } - me->SetCombatPulseDelay(5); me->setActive(true); DoZoneInCombat(); ScheduleTasks(); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 136077a0a5..80e38ccf4d 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -279,7 +279,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipient(), m_lootRecipientGroup(0), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0), -m_respawnDelay(300), m_corpseDelay(60), m_wanderDistance(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), +m_respawnDelay(300), m_corpseDelay(60), m_wanderDistance(0.0f), m_boundaryCheckTime(2500), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(false), m_cannotReachTimer(0), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0), _cyclicSplinePathId(0), @@ -809,34 +809,6 @@ void Creature::Update(uint32 diff) 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) - { - Map::PlayerList const& players = GetMap()->GetPlayers(); - if (!players.isEmpty()) - for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it) - { - if (Player* player = it->GetSource()) - { - if (player->IsGameMaster()) - continue; - - if (player->IsAlive() && IsHostileTo(player)) - EngageWithTarget(player); - } - } - - m_combatPulseTime = m_combatPulseDelay * IN_MILLISECONDS; - } - } - // do not allow the AI to be changed during update Unit::AIUpdateTick(diff); @@ -866,6 +838,14 @@ void Creature::Update(uint32 diff) } } +void Creature::Heartbeat() +{ + Unit::Heartbeat(); + + // Creatures with CREATURE_STATIC_FLAG_2_FORCE_PARTY_MEMBERS_INTO_COMBAT periodically force party members into combat + ForcePartyMembersIntoCombat(); +} + void Creature::RegenerateHealth() { if (!isRegeneratingHealth()) @@ -3316,6 +3296,9 @@ void Creature::AtEngage(Unit* target) if (GetAI() && !IsControlledByPlayer()) SendAIReaction(AI_REACTION_HOSTILE); + + // Creatures with CREATURE_STATIC_FLAG_2_FORCE_PARTY_MEMBERS_INTO_COMBAT periodically force party members into combat + ForcePartyMembersIntoCombat(); } void Creature::AtDisengage() @@ -3327,6 +3310,41 @@ void Creature::AtDisengage() SetUInt32Value(UNIT_DYNAMIC_FLAGS, GetCreatureTemplate()->dynamicflags); } +void Creature::ForcePartyMembersIntoCombat() +{ + if (!_staticFlags.HasFlag(CREATURE_STATIC_FLAG_2_FORCE_PARTY_MEMBERS_INTO_COMBAT) || !IsEngaged()) + return; + + Trinity::Containers::FlatSet partiesToForceIntoCombat; + for (auto const& [_, combatReference] : GetCombatManager().GetPvECombatRefs()) + { + //if (combatReference->IsSuppressedFor(this)) + // continue; + + Unit* other = combatReference->GetOther(this); + + Player* player = other ? other->ToPlayer() : nullptr; + if (!player || player->IsGameMaster()) + continue; + + if (Group const* group = player->GetGroup()) + partiesToForceIntoCombat.insert(group); + } + + for (Group const* partyToForceIntoCombat : partiesToForceIntoCombat) + { + for (GroupReference const* ref = partyToForceIntoCombat->GetFirstMember(); ref != nullptr; ref = ref->next()) + { + Player* player = ref->GetSource(); + if (!player || !player->IsInWorld() || player->GetMap() != GetMap() || player->IsGameMaster()) + continue; + + EngageWithTarget(player); + } + } +} + + bool Creature::IsEscorted() const { if (CreatureAI* ai = AI()) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 2f3293f6fc..f473b9c9e2 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -86,7 +86,9 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma ObjectGuid::LowType GetSpawnId() const { return m_spawnId; } - void Update(uint32 time) override; // overwrited Unit::Update + void Update(uint32 time) override; // overwritten Unit::Update + void Heartbeat() override; + void GetRespawnPosition(float &x, float &y, float &z, float* ori = nullptr, float* dist = nullptr) const; bool IsSpawnedOnTransport() const { return m_creatureData && m_creatureData->mapId != GetMapId(); } @@ -273,13 +275,6 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void SetWanderDistance(float dist) { m_wanderDistance = dist; } void DoImmediateBoundaryCheck() { m_boundaryCheckTime = 0; } - uint32 GetCombatPulseDelay() const { return m_combatPulseDelay; } - void SetCombatPulseDelay(uint32 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; - } uint32 m_groupLootTimer; // (msecs)timer used for group loot ObjectGuid::LowType lootingGroupLowGUID; // used to find group which is looting corpse @@ -377,6 +372,8 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void AtEngage(Unit* target) override; void AtDisengage() override; + void ForcePartyMembersIntoCombat(); + bool HasStaticFlag(CreatureStaticFlags flag) const { return _staticFlags.HasFlag(flag); } bool HasStaticFlag(CreatureStaticFlags2 flag) const { return _staticFlags.HasFlag(flag); } bool HasStaticFlag(CreatureStaticFlags3 flag) const { return _staticFlags.HasFlag(flag); } @@ -416,8 +413,6 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance float m_wanderDistance; uint32 m_boundaryCheckTime; // (msecs) remaining time for next evade boundary check - uint32 m_combatPulseTime; // (msecs) remaining time for next zone-in-combat pulse - uint32 m_combatPulseDelay; // (secs) how often the creature puts the entire zone in combat (only works in dungeons) ReactStates m_reactState; // for AI, not charmInfo void RegenerateHealth() override; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 737880c566..8194d6fed8 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -827,7 +827,7 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, P void GameObject::Update(uint32 diff) { - m_Events.Update(diff); + WorldObject::Update(diff); if (AI()) AI()->UpdateAI(diff); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 68a2cc944f..72d205c2c0 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -101,6 +101,18 @@ WorldObject::~WorldObject() } } +void WorldObject::Update(uint32 diff) +{ + m_Events.Update(diff); + + _heartbeatTimer -= Milliseconds(diff); + while (_heartbeatTimer <= 0ms) + { + _heartbeatTimer += HEARTBEAT_INTERVAL; + Heartbeat(); + } +} + Object::~Object() { if (IsInWorld()) @@ -1117,7 +1129,7 @@ bool Object::PrintIndexError(uint32 index, bool set) const WorldObject::WorldObject(bool isWorldObject) : Object(), WorldLocation(), LastUsedScriptID(0), m_movementInfo(), m_name(""), m_isActive(false), m_isFarVisible(false), m_isWorldObject(isWorldObject), m_zoneScript(nullptr), m_transport(nullptr), m_zoneId(0), m_areaId(0), m_staticFloorZ(VMAP_INVALID_HEIGHT), m_outdoors(true), m_liquidStatus(LIQUID_MAP_NO_WATER), -m_wmoGroupID(0), m_currMap(nullptr), m_InstanceId(0), _dbPhase(0), m_notifyflags(0), +m_wmoGroupID(0), m_currMap(nullptr), m_InstanceId(0), _dbPhase(0), m_notifyflags(0), _heartbeatTimer(HEARTBEAT_INTERVAL), m_aiAnimKitId(0), m_movementAnimKitId(0), m_meleeAnimKitId(0) { m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GHOST, GHOST_VISIBILITY_ALIVE | GHOST_VISIBILITY_GHOST); @@ -1190,6 +1202,8 @@ void WorldObject::CleanupsBeforeDelete(bool /*finalCleanup*/) if (TransportBase* transport = GetTransport()) transport->RemovePassenger(this); + + m_Events.KillAllEvents(false); // non-delatable (currently cast spells) will not deleted now but it will deleted at call in Map::RemoveAllObjectsInRemoveList } void WorldObject::_Create(ObjectGuid::LowType guidlow, HighGuid guidhigh) diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 9eb715d078..abf1ed34c2 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -88,6 +88,7 @@ struct CreateObjectBits }; float const DEFAULT_COLLISION_HEIGHT = 2.03128f; // Most common value in dbc +static constexpr Milliseconds const HEARTBEAT_INTERVAL = 5s + 200ms; class TC_GAME_API Object { @@ -314,7 +315,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation public: virtual ~WorldObject(); - virtual void Update (uint32 /*time_diff*/) { } + virtual void Update(uint32 diff); void _Create(ObjectGuid::LowType guidlow, HighGuid guidhigh); void AddToWorld() override; @@ -608,6 +609,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation virtual bool IsInvisibleDueToDespawn() const { return false; } //difference from IsAlwaysVisibleFor: 1. after distance check; 2. use owner or charmer as seer virtual bool IsAlwaysDetectableFor(WorldObject const* /*seer*/) const { return false; } + + virtual void Heartbeat() { } private: Map* m_currMap; // current object's Map location @@ -620,6 +623,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation ObjectGuid _privateObjectOwner; + Milliseconds _heartbeatTimer; + virtual bool _IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D, bool incOwnRadius = true, bool incTargetRadius = true) const; bool CanNeverSee(WorldObject const* obj) const; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 962c82dec1..c218a335fd 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -118,8 +118,6 @@ #include "WorldStatePackets.h" #include -#define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS) - // corpse reclaim times #define DEATH_EXPIRE_STEP (5*MINUTE) #define MAX_DEATH_COUNT 3 @@ -171,11 +169,9 @@ Player::Player(WorldSession* session): Unit(true) m_comboPoints = 0; - m_foodEmoteTimerCount = 0; m_weaponChangeTimer = 0; m_zoneUpdateId = uint32(-1); - m_zoneUpdateTimer = 0; m_areaUpdateId = 0; m_team = 0; @@ -355,7 +351,6 @@ Player::Player(WorldSession* session): Unit(true) _hasValidLFGLeavePoint = false; _archaeology = std::make_unique(this); m_petScalingSynchTimer.Reset(1000); - m_groupUpdateTimer.Reset(5000); } Player::~Player() @@ -1133,37 +1128,6 @@ void Player::Update(uint32 p_time) m_weaponChangeTimer -= p_time; } - if (m_zoneUpdateTimer > 0) - { - if (p_time >= m_zoneUpdateTimer) - { - // On zone update tick check if we are still in an inn if we are supposed to be in one - if (HasRestFlag(REST_FLAG_IN_TAVERN)) - { - AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(GetInnTriggerId()); - if (!atEntry || !IsInAreaTriggerRadius(atEntry)) - RemoveRestFlag(REST_FLAG_IN_TAVERN); - } - - uint32 newzone, newarea; - GetZoneAndAreaId(newzone, newarea); - - if (m_zoneUpdateId != newzone) - UpdateZone(newzone, newarea); // also update area - else - { - // use area updates as well - // needed for free far all arenas for example - if (m_areaUpdateId != newarea) - UpdateArea(newarea); - - m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL; - } - } - else - m_zoneUpdateTimer -= p_time; - } - // Power regeneration update _powerUpdateTimer -= p_time; if (IsAlive()) @@ -1267,14 +1231,6 @@ void Player::Update(uint32 p_time) } } - // group update - m_groupUpdateTimer.Update(p_time); - if (m_groupUpdateTimer.Passed()) - { - SendUpdateToOutOfRangeGroupMembers(); - m_groupUpdateTimer.Reset(5000); - } - Pet* pet = GetPet(); if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityRange()) && !pet->isPossessed()) //if (pet && !pet->IsWithinDistInMap(this, GetMap()->GetVisibilityDistance()) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID()))) @@ -1311,6 +1267,24 @@ void Player::Update(uint32 p_time) } + +void Player::Heartbeat() +{ + Unit::Heartbeat(); + + // Group update + SendUpdateToOutOfRangeGroupMembers(); + + // Updating Zone and AreaId. This will also trigger spell_area and phasing related updates + UpdateZoneAndAreaId(); + + // Updating auras which can only be used inside or outside (such as Mounts) + UpdateIndoorsOutdoorsAuras(); + + // Updating the resting state when entering resting places + UpdateTavernRestingState(); +} + void Player::setDeathState(DeathState s) { uint32 ressSpellId = 0; @@ -1808,8 +1782,6 @@ bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Wo void Player::RegenerateAll(uint32 diff) { - m_foodEmoteTimerCount += diff; - Regenerate(POWER_ENERGY, diff); Regenerate(POWER_MANA, diff); Regenerate(POWER_FOCUS, diff); @@ -1838,37 +1810,6 @@ void Player::RegenerateAll(uint32 diff) SetRuneCooldown(runeToRegen, (cd > (diff * cdmod)) ? cd - (diff * cdmod) : 0); } } - - // Handles the emotes for drinking and eating. - // According to sniffs there is a background timer going on that repeats independed from the time window where the aura applies. - // That's why we dont need to reset the timer on apply. In sniffs I have seen that the first call for the spell visual is totally random, then after - // 5 seconds over and over again which confirms my theory that we have a independed timer. - if (m_foodEmoteTimerCount >= 5000) - { - std::vector auraList; - AuraEffectList const& ModRegenAuras = GetAuraEffectsByType(SPELL_AURA_MOD_REGEN); - AuraEffectList const& ModPowerRegenAuras = GetAuraEffectsByType(SPELL_AURA_MOD_POWER_REGEN); - - auraList.reserve(ModRegenAuras.size() + ModPowerRegenAuras.size()); - auraList.insert(auraList.end(), ModRegenAuras.begin(), ModRegenAuras.end()); - auraList.insert(auraList.end(), ModPowerRegenAuras.begin(), ModPowerRegenAuras.end()); - - for (auto itr = auraList.begin(); itr != auraList.end(); ++itr) - { - // Food emote comes above drinking emote if we have to decide (mage regen food for example) - if ((*itr)->GetBase()->HasEffectType(SPELL_AURA_MOD_REGEN) && (*itr)->GetSpellInfo()->HasAuraInterruptFlag(SpellAuraInterruptFlags::Standing)) - { - SendPlaySpellVisualKit(SPELL_VISUAL_KIT_FOOD, 0, 0); - break; - } - else if ((*itr)->GetBase()->HasEffectType(SPELL_AURA_MOD_POWER_REGEN) && (*itr)->GetSpellInfo()->HasAuraInterruptFlag(SpellAuraInterruptFlags::Standing)) - { - SendPlaySpellVisualKit(SPELL_VISUAL_KIT_DRINK, 0, 0); - break; - } - } - m_foodEmoteTimerCount -= 5000; - } } void Player::RegenerateHealth() @@ -5913,7 +5854,7 @@ bool Player::UpdatePosition(float x, float y, float z, float orientation, bool t SetGroupUpdateFlag(GROUP_UPDATE_FLAG_WMO_GROUP_ID); } - CheckAreaExploreAndOutdoor(); + CheckAreaExplore(); return true; } @@ -5969,7 +5910,7 @@ void Player::SendMovieStart(uint32 movieId) SendDirectMessage(packet.Write()); } -void Player::CheckAreaExploreAndOutdoor() +void Player::CheckAreaExplore() { if (!IsAlive()) return; @@ -5977,9 +5918,6 @@ void Player::CheckAreaExploreAndOutdoor() if (IsInFlight()) return; - if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK) && !IsOutdoors()) - RemoveAurasWithAttribute(SPELL_ATTR0_ONLY_OUTDOORS); - uint32 const areaId = GetAreaId(); SetAreaExplored(areaId); } @@ -6052,6 +5990,39 @@ void Player::SetAreaExplored(uint32 areaId) } } +void Player::UpdateZoneAndAreaId() +{ + uint32 newzone = 0, newarea = 0; + GetZoneAndAreaId(newzone, newarea); + + if (m_zoneUpdateId != newzone) + UpdateZone(newzone, newarea); // also update area + else + { + // use area updates as well + // needed for free far all arenas for example + if (m_areaUpdateId != newarea) + UpdateArea(newarea); + } +} + +void Player::UpdateIndoorsOutdoorsAuras() +{ + if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK)) + RemoveAurasWithAttribute(IsOutdoors() ? SPELL_ATTR0_ONLY_INDOORS : SPELL_ATTR0_ONLY_OUTDOORS); +} + +void Player::UpdateTavernRestingState() +{ + AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(GetInnTriggerId()); + + if (HasRestFlag(REST_FLAG_IN_TAVERN) && (!atEntry || !IsInAreaTriggerRadius(atEntry))) + RemoveRestFlag(REST_FLAG_IN_TAVERN); + else if (!HasRestFlag(REST_FLAG_IN_TAVERN) && IsInAreaTriggerRadius(atEntry)) + SetRestFlag(REST_FLAG_IN_TAVERN); +} + + uint32 Player::TeamForRace(uint8 race) { if (ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race)) @@ -7032,7 +7003,6 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) uint32 const oldZone = m_zoneUpdateId; m_zoneUpdateId = newZone; - m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL; GetMap()->UpdatePlayerZoneStats(oldZone, newZone); @@ -27629,7 +27599,7 @@ void Player::SetActiveSpec(uint8 spec) RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags2::ChangeSpec); } -void Player::SetRestFlag(RestFlag restFlag, uint32 triggerId /*= 0*/) +void Player::SetRestFlag(RestFlag restFlag) { uint32 oldRestMask = _restFlagMask; _restFlagMask |= restFlag; @@ -27639,10 +27609,7 @@ void Player::SetRestFlag(RestFlag restFlag, uint32 triggerId /*= 0*/) _restTime = GameTime::GetGameTime(); SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING); } - - if (triggerId) - inn_triggerId = triggerId; - } +} void Player::RemoveRestFlag(RestFlag restFlag) { diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 156ba5c4ba..01e338377e 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -410,7 +410,7 @@ enum PlayerFlags PLAYER_FLAGS_UNK21 = 0x00200000, PLAYER_FLAGS_COMMENTATOR2 = 0x00400000, PLAYER_FLAGS_DISABLE_CASTING_EXCEPT_ABILITIES = 0x00800000, // disables casting any ability except for the ones specified in the aura effect's SpellClassMask - PLAYER_FLAGS_DISABLE_ATTACKING_EXCEPT_ABILITIES = 0x01000000, // disables all attacks including ability casts except for the ones specified in the aura effect's SpellClassMask + PLAYER_FLAGS_DISABLE_ATTACKING_EXCEPT_ABILITIES = 0x01000000, // disables all attacks including ability casts except for the ones specified in the aura effect's SpellClassMask PLAYER_FLAGS_NO_XP_GAIN = 0x02000000, PLAYER_FLAGS_UNK26 = 0x04000000, PLAYER_FLAGS_AUTO_DECLINE_GUILD = 0x08000000, // Automatically declines guild invites @@ -1044,6 +1044,8 @@ class TC_GAME_API Player : public Unit, public GridObject void Update(uint32 time) override; + void Heartbeat() override; + bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const override; bool IsInAreaTriggerRadius(AreaTriggerEntry const* trigger) const; @@ -1118,11 +1120,12 @@ class TC_GAME_API Player : public Unit, public GridObject void SetRestBonus(float rest_bonus_new); bool HasRestFlag(RestFlag restFlag) const { return (_restFlagMask & restFlag) != 0; } - void SetRestFlag(RestFlag restFlag, uint32 triggerId = 0); + void SetRestFlag(RestFlag restFlag); void RemoveRestFlag(RestFlag restFlag); uint32 GetXPRestBonus(uint32 xp); uint32 GetInnTriggerId() const { return inn_triggerId; } + void SetInnTriggerID(uint32 id) { inn_triggerId = id; } Pet* GetPet() const; Pet* SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 despwtime); @@ -1924,9 +1927,14 @@ class TC_GAME_API Player : public Unit, public GridObject void SetSemaphoreTeleportFar(bool semphsetting) { mSemaphoreTeleport_Far = semphsetting; } void ProcessDelayedOperations(); - void CheckAreaExploreAndOutdoor(void); + void CheckAreaExplore(); void SetAreaExplored(uint32 areaId); + // These methods are used to periodically update certain area and aura based mechanics used in Heartbeat and Movement + void UpdateZoneAndAreaId(); + void UpdateIndoorsOutdoorsAuras(); + void UpdateTavernRestingState(); + static uint32 TeamForRace(uint8 race); uint32 GetTeam() const { return m_team; } TeamId GetTeamId() const { return m_team == ALLIANCE ? TEAM_ALLIANCE : TEAM_HORDE; } @@ -2390,7 +2398,6 @@ class TC_GAME_API Player : public Unit, public GridObject protected: // Gamemaster whisper whitelist GuidList WhisperList; - uint32 m_foodEmoteTimerCount; uint32 m_contestedPvPTimer; /*********************************************************/ @@ -2615,7 +2622,6 @@ class TC_GAME_API Player : public Unit, public GridObject uint32 m_weaponChangeTimer; uint32 m_zoneUpdateId; - uint32 m_zoneUpdateTimer; uint32 m_areaUpdateId; uint32 m_deathTimer; @@ -2758,7 +2764,6 @@ class TC_GAME_API Player : public Unit, public GridObject std::vector PlayerPetDataStore; TimeTrackerSmall m_petScalingSynchTimer; - TimeTrackerSmall m_groupUpdateTimer; }; TC_GAME_API void AddItemsSetItem(Player* player, Item* item); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 65a8adb074..7391438545 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -413,7 +413,7 @@ void Unit::Update(uint32 p_time) // WARNING! Order of execution here is important, do not change. // Spells must be processed with event system BEFORE they go to _UpdateSpells. // Or else we may have some SPELL_STATE_FINISHED spells stalled in pointers, that is bad. - m_Events.Update(p_time); + WorldObject::Update(p_time); CheckPendingMovementAcks(); @@ -503,6 +503,25 @@ void Unit::Update(uint32 p_time) RefreshAI(); } +void Unit::Heartbeat() +{ + WorldObject::Heartbeat(); + + // SMSG_FLIGHT_SPLINE_SYNC for cyclic splines + SendFlightSplineSyncUpdate(); + + // Trigger heartbeat procs and generic aura behavior such as food emotes and invoking aura script hooks + TriggerAuraHeartbeat(); +} + +void Unit::TriggerAuraHeartbeat() +{ + for (auto const& [_, auraApplication] : m_appliedAuras) + auraApplication->GetBase()->Heartbeat(); + + Unit::ProcSkillsAndAuras(this, nullptr, PROC_FLAG_HEARTBEAT, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); +} + bool Unit::haveOffhandWeapon() const { if (Player const* player = ToPlayer()) @@ -527,20 +546,6 @@ void Unit::UpdateSplineMovement(uint32 t_diff) movespline->updateState(t_diff); bool arrived = movespline->Finalized(); - if (movespline->isCyclic()) - { - m_splineSyncTimer.Update(t_diff); - if (m_splineSyncTimer.Passed()) - { - m_splineSyncTimer.Reset(5000); // Retail value, do not change - - WorldPackets::Movement::FlightSplineSync packet; - packet.Guid = GetGUID(); - packet.SplineDist = Movement::PacketBuilder::GetFlightSplineSyncDist(*movespline); - SendMessageToSet(packet.Write(), IsPlayer()); - } - } - if (arrived) { DisableSpline(); @@ -575,6 +580,17 @@ void Unit::UpdateSplinePosition() UpdatePosition(loc.x, loc.y, loc.z, loc.orientation); } +void Unit::SendFlightSplineSyncUpdate() +{ + if (!movespline->isCyclic() || movespline->Finalized()) + return; + + WorldPackets::Movement::FlightSplineSync flightSplineSync; + flightSplineSync.Guid = GetGUID(); + flightSplineSync.SplineDist = float(movespline->timePassed()) / movespline->Duration(); + SendMessageToSet(flightSplineSync.Write(), true); +} + void Unit::InterruptMovementBasedAuras() { // TODO: Check if orientation transport offset changed instead of only global orientation @@ -9625,7 +9641,6 @@ void Unit::CleanupBeforeRemoveFromMap(bool finalCleanup) if (finalCleanup) m_cleanupDone = true; - m_Events.KillAllEvents(false); // non-delatable (currently cast spells) will not deleted now but it will deleted at call in Map::RemoveAllObjectsInRemoveList CombatStop(); ClearComboPointHolders(); } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 701575b97a..29b39528b0 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -825,6 +825,9 @@ class TC_GAME_API Unit : public WorldObject virtual void Update(uint32 time) override; + void Heartbeat() override; + void TriggerAuraHeartbeat(); + void setAttackTimer(WeaponAttackType type, uint32 time) { m_attackTimer[type] = time; } void resetAttackTimer(WeaponAttackType type = BASE_ATTACK); uint32 getAttackTimer(WeaponAttackType type) const { return m_attackTimer[type]; } @@ -1871,6 +1874,7 @@ class TC_GAME_API Unit : public WorldObject void UpdateSplineMovement(uint32 t_diff); void UpdateSplinePosition(); + void SendFlightSplineSyncUpdate(); // player or player's pet float GetCombatRatingReduction(CombatRating cr) const; @@ -1886,7 +1890,6 @@ class TC_GAME_API Unit : public WorldObject private: uint32 m_state; // Even derived shouldn't modify - TimeTrackerSmall m_splineSyncTimer; std::array m_Diminishing; diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index b1dd6602ac..d96e20232f 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -681,7 +681,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recvData) if (sObjectMgr->IsTavernAreaTrigger(triggerId)) { // set resting flag we are in the inn - player->SetRestFlag(REST_FLAG_IN_TAVERN, atEntry->ID); + player->SetInnTriggerID(atEntry->ID); if (sWorld->IsFFAPvPRealm()) player->RemoveByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_FFA_PVP); diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index fd03d0d0fb..15ad35198c 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -445,12 +445,33 @@ void WorldSession::HandleMovementOpcode(uint16 opcode, MovementInfo& movementInf } } } + else + plrMover->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); if (opcode == MSG_MOVE_JUMP) { plrMover->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags2::Jump); Unit::ProcSkillsAndAuras(plrMover, nullptr, PROC_FLAG_JUMP, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_NONE, PROC_HIT_NONE, nullptr, nullptr, nullptr); } + + // Whenever a player stops a movement action, several position based checks and updates are being performed + switch (opcode) + { + case CMSG_MOVE_SET_CAN_FLY: + case MSG_MOVE_FALL_LAND: + case MSG_MOVE_STOP: + case MSG_MOVE_STOP_STRAFE: + case MSG_MOVE_STOP_TURN: + case MSG_MOVE_STOP_SWIM: + case MSG_MOVE_STOP_PITCH: + case MSG_MOVE_STOP_ASCEND: + plrMover->UpdateZoneAndAreaId(); + plrMover->UpdateIndoorsOutdoorsAuras(); + plrMover->UpdateTavernRestingState(); + break; + default: + break; + } } } diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 0d763e03fa..803f8767ca 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -413,6 +413,11 @@ class CreatureGameObjectScriptRegistrySwapHooks // Hook which is called before a gameobject is swapped static void UnloadResetScript(GameObject* gameobject) { + + // Remove deletable events only, + // otherwise it causes crashes with non-deletable spell events. + gameobject->m_Events.KillAllEvents(false); + gameobject->AI()->Reset(); } diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 0c860a39f9..ed34562aee 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -2056,6 +2056,19 @@ void Aura::CallScriptAfterDispel(DispelInfo* dispelInfo) } } +void Aura::CallScriptOnHeartbeat() +{ + for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) + { + (*scritr)->_PrepareScriptCall(AURA_SCRIPT_HOOK_ON_HEARTBEAT); + auto hookItrEnd = (*scritr)->OnHeartbeat.end(), hookItr = (*scritr)->OnHeartbeat.begin(); + for (; hookItr != hookItrEnd; ++hookItr) + hookItr->Call(); + + (*scritr)->_FinishScriptCall(); + } +} + bool Aura::CallScriptEffectApplyHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode) { bool preventDefault = false; @@ -2535,6 +2548,29 @@ void UnitAura::AddStaticApplication(Unit* target, uint8 effMask) _staticApplications[target->GetGUID()] |= effMask; } +void UnitAura::Heartbeat() +{ + Aura::Heartbeat(); + + // Periodic food and drink emote animation + HandlePeriodicFoodSpellVisualKit(); +} + +void UnitAura::HandlePeriodicFoodSpellVisualKit() +{ + SpellSpecificType specificType = GetSpellInfo()->GetSpellSpecific(); + + bool food = specificType == SPELL_SPECIFIC_FOOD || specificType == SPELL_SPECIFIC_FOOD_AND_DRINK; + bool drink = specificType == SPELL_SPECIFIC_DRINK || specificType == SPELL_SPECIFIC_FOOD_AND_DRINK; + + if (food) + GetUnitOwner()->SendPlaySpellVisualKit(SPELL_VISUAL_KIT_FOOD, 0, 0); + + if (drink) + GetUnitOwner()->SendPlaySpellVisualKit(SPELL_VISUAL_KIT_DRINK, 0, 0); +} + + DynObjAura::DynObjAura(AuraCreateInfo const& createInfo) : Aura(createInfo) { diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index d000c5ae99..8de8654d68 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -237,12 +237,14 @@ class TC_GAME_API Aura uint8 GetProcEffectMask(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const; float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; void TriggerProcOnEvent(uint8 procEffectMask, AuraApplication* aurApp, ProcEventInfo& eventInfo); + virtual void Heartbeat() { } // AuraScript void LoadScripts(); bool CallScriptCheckAreaTargetHandlers(Unit* target); void CallScriptDispel(DispelInfo* dispelInfo); void CallScriptAfterDispel(DispelInfo* dispelInfo); + void CallScriptOnHeartbeat(); bool CallScriptEffectApplyHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode); bool CallScriptEffectRemoveHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode); void CallScriptAfterEffectApplyHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode); @@ -337,6 +339,9 @@ class TC_GAME_API UnitAura : public Aura void AddStaticApplication(Unit* target, uint8 effMask); + void Heartbeat() override; + void HandlePeriodicFoodSpellVisualKit(); + private: DiminishingGroup m_AuraDRGroup; // Diminishing std::unordered_map _staticApplications; // non-area auras diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index cd894a63ab..09faf5443c 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -112,7 +112,7 @@ enum ProcFlags { PROC_FLAG_NONE = 0x00000000, - PROC_FLAG_HEARTBEAT = 0x00000001, // 00 Killed by agressor - not sure about this flag + PROC_FLAG_HEARTBEAT = 0x00000001, // 00 Heartbeat PROC_FLAG_KILL = 0x00000002, // 01 Kill target (in most cases need XP/Honor reward) PROC_FLAG_DEAL_MELEE_SWING = 0x00000004, // 02 Done melee auto attack diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index ea8d10abba..d8de444b75 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -229,7 +229,7 @@ class TC_GAME_API _SpellScript template EffectNameHookHandler(Derived* owner, HookType hookFunction, uint8 effIndex, uint16 effName) - : BaseHookHandler(owner, hookFunction, effIndex, + : BaseHookHandler(owner, hookFunction, effIndex, [_effectName = effName](SpellEffectInfo const& effectInfo) { return _effectName == SPELL_EFFECT_ANY || effectInfo.Effect == _effectName; }), _effName(effName) @@ -491,7 +491,7 @@ class TC_GAME_API SpellScript : public _SpellScript // internal use classes & functions // DO NOT OVERRIDE THESE IN SCRIPTS public: - + using CheckCastHook = HookHandler; using EffectNameHook = EffectNameHookHandler; using HitHook = HookHandler; @@ -742,6 +742,7 @@ enum AuraScriptHookType AURA_SCRIPT_HOOK_CHECK_AREA_TARGET, AURA_SCRIPT_HOOK_DISPEL, AURA_SCRIPT_HOOK_AFTER_DISPEL, + AURA_SCRIPT_HOOK_ON_HEARTBEAT, // Spell Proc Hooks AURA_SCRIPT_HOOK_CHECK_PROC, AURA_SCRIPT_HOOK_CHECK_EFFECT_PROC, @@ -767,6 +768,7 @@ class TC_GAME_API AuraScript : public _SpellScript using AuraCheckAreaTargetHookHandler = HookHandler; using AuraDispelHookHandler = HookHandler; + using AuraHeartbeatHookHandler = HookHandler; using AuraEffectPeriodicHookHandler = AuraNameHookHandler; using AuraEffectUpdatePeriodicHookHandler = AuraNameHookHandler; using AuraEffectCalcAmountHookHandler = AuraNameHookHandler; @@ -811,7 +813,7 @@ class TC_GAME_API AuraScript : public _SpellScript }; using AuraEffectApplyHookHandler = AuraEffectHandleModeHookHandler; - + struct TC_GAME_API AuraEffectAbsorbHookHandler : AuraNameHookHandler { //> Base type shorthand. @@ -878,7 +880,7 @@ class TC_GAME_API AuraScript : public _SpellScript AuraScript() : _SpellScript(), m_aura(nullptr), m_auraApplication(nullptr), m_defaultActionPrevented(false), DoCheckAreaTarget(this), - OnDispel(this), AfterDispel(this), + OnDispel(this), AfterDispel(this), OnHeartbeat(this), OnEffectApply(this), AfterEffectApply(this), OnEffectRemove(this), AfterEffectRemove(this), OnEffectPeriodic(this), OnEffectUpdatePeriodic(this), @@ -935,6 +937,11 @@ class TC_GAME_API AuraScript : public _SpellScript // where function is: void function (DispelInfo* dispelInfo); AuraDispelHookHandler::HookList AfterDispel; + // executed on every heartbeat of a unit + // example: OnHeartbeat.Register(this, class::function); + // where function is: void function(); + AuraHeartbeatHookHandler::HookList OnHeartbeat; + // executed when aura effect is applied with specified mode to target // should be used when when effect handler preventing/replacing is needed, do not use this hook for triggering spellcasts/removing auras etc - may be unsafe // example: OnEffectApply.Register(this, &class::function, EffectIndexSpecifier, EffectAuraNameSpecifier); diff --git a/src/server/scripts/EasternKingdoms/ThroneOfTheTides/boss_ozumat.cpp b/src/server/scripts/EasternKingdoms/ThroneOfTheTides/boss_ozumat.cpp index 3ba2290c39..56090a366b 100644 --- a/src/server/scripts/EasternKingdoms/ThroneOfTheTides/boss_ozumat.cpp +++ b/src/server/scripts/EasternKingdoms/ThroneOfTheTides/boss_ozumat.cpp @@ -38,7 +38,7 @@ namespace ThroneOfTheTides::Ozumat enum Texts { // Neptulon - SAY_INTRO_1 = 0, + SAY_INTRO_1 = 0, SAY_INTRO_2 = 1, SAY_PURIFY_WATERS_1 = 2, SAY_PURIFY_WATERS_2 = 3, @@ -431,7 +431,7 @@ struct npc_ozumat_neptulon : public ScriptedAI default: break; } - + } void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override @@ -494,7 +494,6 @@ struct npc_ozumat_neptulon : public ScriptedAI break; case NPC_OZUMAT_COMBAT_TRIGGER: summon->SetReactState(REACT_PASSIVE); - summon->SetCombatPulseDelay(1); summon->AI()->DoZoneInCombat(); break; default: diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp index 03ed26bd26..4c66357cf3 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp @@ -274,7 +274,6 @@ class boss_blood_council_controller : public CreatureScript return; } - me->SetCombatPulseDelay(5); me->setActive(true); DoZoneInCombat(); instance->SetBossState(DATA_BLOOD_PRINCE_COUNCIL, IN_PROGRESS); @@ -452,7 +451,6 @@ struct BloodPrincesBossAI : public BossAI { events.Reset(); summons.DespawnAll(); - me->SetCombatPulseDelay(0); me->SetImmuneToPC(false); _isEmpowered = false; @@ -462,7 +460,6 @@ struct BloodPrincesBossAI : public BossAI void JustEngagedWith(Unit* /*who*/) override { - me->SetCombatPulseDelay(5); me->setActive(true); if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_BLOOD_PRINCES_CONTROL))) DoZoneInCombat(controller); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp index a847d82d62..22b858503d 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp @@ -251,7 +251,6 @@ class boss_lady_deathwhisper : public CreatureScript } _phase = PHASE_ONE; - me->SetCombatPulseDelay(5); me->setActive(true); DoZoneInCombat(); scheduler.CancelGroup(GROUP_INTRO); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp index 634838889c..39421cb96b 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp @@ -281,7 +281,6 @@ class boss_professor_putricide : public CreatureScript Talk(SAY_AGGRO); DoCast(me, SPELL_OOZE_TANK_PROTECTION, true); DoZoneInCombat(me); - me->SetCombatPulseDelay(5); instance->SetBossState(DATA_PROFESSOR_PUTRICIDE, IN_PROGRESS); } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp index 09a22af228..2a3ec36672 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp @@ -284,7 +284,6 @@ class boss_sindragosa : public CreatureScript DoCast(me, SPELL_PERMAEATING_CHILL); Talk(SAY_AGGRO); instance->SetBossState(DATA_SINDRAGOSA, IN_PROGRESS); - me->SetCombatPulseDelay(5); me->setActive(true); me->SetFarVisible(true); DoZoneInCombat(); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp index f32804d6c5..2eb32d0d76 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -550,7 +550,6 @@ class boss_the_lich_king : public CreatureScript } me->setActive(true); - me->SetCombatPulseDelay(5); DoZoneInCombat(); events.SetPhase(PHASE_ONE); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp index f7bcc7c3db..daec000d8a 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_valithria_dreamwalker.cpp @@ -528,7 +528,6 @@ class npc_green_dragon_combat_trigger : public CreatureScript EngagementStart(target); me->setActive(true); - me->SetCombatPulseDelay(5); DoZoneInCombat(); instance->SetBossState(DATA_VALITHRIA_DREAMWALKER, IN_PROGRESS); if (Creature* valithria = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VALITHRIA_DREAMWALKER))) diff --git a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp index bbbcd7db02..20fdea014a 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_four_horsemen.cpp @@ -178,7 +178,6 @@ struct boss_four_horsemen_baseAI : public BossAI case ACTION_BEGIN_FIGHTING: if (_ourMovementFinished) break; - me->SetCombatPulseDelay(5); BeginFighting(); _ourMovementFinished = true; break; @@ -306,7 +305,6 @@ struct boss_four_horsemen_baseAI : public BossAI _ourMovementFinished = false; me->SetReactState(REACT_AGGRESSIVE); SetCombatMovement(false); - me->SetCombatPulseDelay(0); me->ResetLootMode(); events.Reset(); summons.DespawnAll(); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp index b12c019777..a77ef324fd 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp @@ -346,7 +346,6 @@ class boss_gothik : public CreatureScript if (me->IsInCombat()) { summon->AI()->DoAction(_gateIsOpen ? ACTION_GATE_OPENED : ACTION_ACQUIRE_TARGET); - summon->SetCombatPulseDelay(5); } else summon->DespawnOrUnsummon(); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp index 5f0b833f86..71f56defa3 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp @@ -803,7 +803,6 @@ public: me->SetVisible(false); me->SetHomePosition(me->GetPosition()); DoZoneInCombat(); - me->SetCombatPulseDelay(5); _visibilityTimer = 2 * IN_MILLISECONDS; _bloodTapTimer = 25 * IN_MILLISECONDS; break; @@ -836,7 +835,6 @@ public: void Reset() override { - me->SetCombatPulseDelay(0); ScriptedAI::Reset(); } diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp index f271f8e7ff..9b43f70ebe 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp @@ -330,14 +330,12 @@ struct PalehoofMinionsBossAI : public BossAI void Reset() override { - me->SetCombatPulseDelay(0); events.Reset(); DoCastSelf(SPELL_FREEZE, true); } void JustEngagedWith(Unit* /*who*/) override { - me->SetCombatPulseDelay(5); me->setActive(true); ScheduleTasks(); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidari_council.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidari_council.cpp index a94ca5feae..fa39024ec8 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidari_council.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidari_council.cpp @@ -243,14 +243,12 @@ struct IllidariCouncilBossAI : public BossAI void Reset() override { - me->SetCombatPulseDelay(0); events.Reset(); DoCastSelf(SPELL_BALANCE_OF_POWER, true); } void JustEngagedWith(Unit* /*who*/) override { - me->SetCombatPulseDelay(5); me->setActive(true); if (Creature* illidari = instance->GetCreature(DATA_ILLIDARI_COUNCIL)) DoZoneInCombat(illidari); diff --git a/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp b/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp index f8cb3be6ee..893ad6492c 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp @@ -334,7 +334,6 @@ struct boss_essence_of_suffering : public BossAI void JustEngagedWith(Unit* /*who*/) override { - me->SetCombatPulseDelay(5); me->setActive(true); events.ScheduleEvent(EVENT_SOUL_DRAIN, 20s); @@ -405,7 +404,6 @@ struct boss_essence_of_desire : public BossAI events.ScheduleEvent(EVENT_RUNE_SHIELD, 16s); events.ScheduleEvent(EVENT_DEADEN, 31s); - me->SetCombatPulseDelay(5); me->setActive(true); Talk(DESI_SAY_FREED); } @@ -513,7 +511,6 @@ struct boss_essence_of_anger : public BossAI events.ScheduleEvent(EVENT_SPITE, 20s); events.ScheduleEvent(EVENT_FREED_2, Seconds(1), Minutes(3)); - me->SetCombatPulseDelay(5); me->setActive(true); }