diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 16c61e12fe..fcc4980b66 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -671,6 +671,9 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, if (IsTrigger()) SetUninteractible(true); + if (HasNpcFlag(UNIT_NPC_FLAG_SPELLCLICK)) + InitializeInteractSpellId(); + InitializeReactState(); if (cInfo->flags_extra & CREATURE_FLAG_EXTRA_NO_TAUNT) @@ -3688,3 +3691,14 @@ void Creature::SummonGraveyardTeleporter() // ID - 46894 Summon Horde Graveyard Teleporter (SERVERSIDE) SummonCreature(npcEntry, GetPosition(), TEMPSUMMON_TIMED_DESPAWN, 1s, 0, 0); } + +void Creature::InitializeInteractSpellId() +{ + auto clickBounds = sObjectMgr->GetSpellClickInfoMapBounds(GetEntry()); + auto itr = clickBounds.begin(); + // Set InteractSpellID if there is only one row in npc_spellclick_spells in db for this creature + if (itr != clickBounds.end() && ++itr == clickBounds.end()) + SetInteractSpellId(clickBounds.begin()->second.spellId); + else + SetInteractSpellId(0); +} diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 591a175081..ff408fbd31 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -263,7 +263,7 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void AllLootRemovedFromCorpse(); uint16 GetLootMode() const { return m_LootMode; } - bool HasLootMode(uint16 lootMode) { return (m_LootMode & lootMode) != 0; } + bool HasLootMode(uint16 lootMode) const { return (m_LootMode & lootMode) != 0; } void SetLootMode(uint16 lootMode) { m_LootMode = lootMode; } void AddLootMode(uint16 lootMode) { m_LootMode |= lootMode; } void RemoveLootMode(uint16 lootMode) { m_LootMode &= ~lootMode; } @@ -367,7 +367,7 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma // There's many places not ready for dynamic spawns. This allows them to live on for now. void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; } - bool GetRespawnCompatibilityMode() { return m_respawnCompatibilityMode; } + bool GetRespawnCompatibilityMode() const { return m_respawnCompatibilityMode; } static float GetDamageMod(CreatureClassifications classification); @@ -401,7 +401,7 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void AtDisengage() override; void OverrideSparringHealthPct(std::vector const& healthPct); - float GetSparringHealthPct() { return _sparringHealthPct; } + float GetSparringHealthPct() const { return _sparringHealthPct; } uint32 CalculateDamageForSparring(Unit* attacker, uint32 damage); bool ShouldFakeDamageFrom(Unit* attacker); @@ -432,6 +432,9 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void SummonGraveyardTeleporter(); + void InitializeInteractSpellId(); + void SetInteractSpellId(int32 interactSpellId) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::InteractSpellID), interactSpellId); } + protected: bool CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, CreatureData const* data = nullptr, uint32 vehId = 0); bool InitEntry(uint32 entry, CreatureData const* data = nullptr); diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.cpp b/src/server/game/Entities/Object/Updates/UpdateFields.cpp index 905cc26e98..2e01289775 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.cpp +++ b/src/server/game/Entities/Object/Updates/UpdateFields.cpp @@ -1105,7 +1105,7 @@ void UnitData::WriteCreate(ByteBuffer& data, EnumFlag fieldVisi data << int32(WildBattlePetLevel); data << int32(BattlePetCompanionExperience); data << uint32(BattlePetCompanionNameTimestamp); - data << int32(InteractSpellID); + data << int32(ViewerDependentValue::GetValue(this, owner, receiver)); data << int32(ScaleDuration); data << int32(LooksLikeMountID); data << int32(LooksLikeCreatureID); @@ -1670,7 +1670,7 @@ void UnitData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignor } if (changesMask[114]) { - data << int32(InteractSpellID); + data << int32(ViewerDependentValue::GetValue(this, owner, receiver)); } if (changesMask[115]) { diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.h b/src/server/game/Entities/Object/Updates/UpdateFields.h index 626ba71ec1..078f0e5738 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.h +++ b/src/server/game/Entities/Object/Updates/UpdateFields.h @@ -376,6 +376,7 @@ struct UnitData : public IsUpdateFieldStructureTag, public HasChangesMask<217> UpdateField BattlePetCompanionExperience; UpdateField BattlePetCompanionNameTimestamp; UpdateField InteractSpellID; + struct InteractSpellIDTag : ViewerDependentValueTag {}; UpdateField ScaleDuration; UpdateField LooksLikeMountID; UpdateField LooksLikeCreatureID; diff --git a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h index 371c0e2ab3..852ba9b86e 100644 --- a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h +++ b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h @@ -287,6 +287,36 @@ public: } }; +template<> +class ViewerDependentValue +{ +public: + using value_type = UF::UnitData::InteractSpellIDTag::value_type; + + static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver) + { + value_type interactSpellId = unitData->InteractSpellID; + if (unitData->NpcFlags[0] & UNIT_NPC_FLAG_SPELLCLICK && !interactSpellId) + { + // this field is not set if there are multiple available spellclick spells + auto clickBounds = sObjectMgr->GetSpellClickInfoMapBounds(unit->GetEntry()); + for (auto const& [creatureId, spellClickInfo] : clickBounds) + { + if (!spellClickInfo.IsFitToRequirements(receiver, unit)) + continue; + + if (!sConditionMgr->IsObjectMeetingSpellClickConditions(unit->GetEntry(), spellClickInfo.spellId, receiver, unit)) + continue; + + interactSpellId = spellClickInfo.spellId; + break; + } + + } + return interactSpellId; + } +}; + template<> class ViewerDependentValue {