/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #ifndef ViewerDependentValues_h__ #define ViewerDependentValues_h__ #include "Conversation.h" #include "Creature.h" #include "DB2Stores.h" #include "GameObject.h" #include "Map.h" #include "ObjectMgr.h" #include "Player.h" #include "SpellInfo.h" #include "SpellMgr.h" #include "TemporarySummon.h" #include "World.h" #include "WorldSession.h" namespace UF { template class ViewerDependentValue { }; template<> class ViewerDependentValue { public: using value_type = UF::ObjectData::EntryIDTag::value_type; static value_type GetValue(UF::ObjectData const* objectData, Object const* object, Player const* receiver) { value_type entryId = objectData->EntryID; if (Unit const* unit = object->ToUnit()) if (TempSummon const* summon = unit->ToTempSummon()) if (summon->GetSummonerGUID() == receiver->GetGUID() && summon->GetCreatureIdVisibleToSummoner()) entryId = *summon->GetCreatureIdVisibleToSummoner(); return entryId; } }; template<> class ViewerDependentValue { public: using value_type = UF::ObjectData::DynamicFlagsTag::value_type; static value_type GetValue(UF::ObjectData const* objectData, Object const* object, Player const* receiver) { value_type dynamicFlags = objectData->DynamicFlags; if (Unit const* unit = object->ToUnit()) { if (Creature const* creature = object->ToCreature()) { if (dynamicFlags & UNIT_DYNFLAG_TAPPED && creature->isTappedBy(receiver)) dynamicFlags &= ~UNIT_DYNFLAG_TAPPED; if (dynamicFlags & UNIT_DYNFLAG_LOOTABLE && !receiver->isAllowedToLoot(creature)) dynamicFlags &= ~UNIT_DYNFLAG_LOOTABLE; if (dynamicFlags & UNIT_DYNFLAG_CAN_SKIN && creature->IsSkinnedBy(receiver)) dynamicFlags &= ~UNIT_DYNFLAG_CAN_SKIN; } // unit UNIT_DYNFLAG_TRACK_UNIT should only be sent to caster of SPELL_AURA_MOD_STALKED auras if (dynamicFlags & UNIT_DYNFLAG_TRACK_UNIT) if (!unit->HasAuraTypeWithCaster(SPELL_AURA_MOD_STALKED, receiver->GetGUID())) dynamicFlags &= ~UNIT_DYNFLAG_TRACK_UNIT; } else if (GameObject const* gameObject = object->ToGameObject()) { uint32 dynFlags = GO_DYNFLAG_LO_STATE_TRANSITION_ANIM_DONE; switch (gameObject->GetGoType()) { case GAMEOBJECT_TYPE_BUTTON: case GAMEOBJECT_TYPE_GOOBER: if (gameObject->HasConditionalInteraction() && gameObject->CanActivateForPlayer(receiver)) if (gameObject->GetGoStateFor(receiver->GetGUID()) != GO_STATE_ACTIVE) dynFlags |= GO_DYNFLAG_LO_ACTIVATE | GO_DYNFLAG_LO_HIGHLIGHT; break; case GAMEOBJECT_TYPE_QUESTGIVER: if (gameObject->CanActivateForPlayer(receiver)) dynFlags |= GO_DYNFLAG_LO_ACTIVATE; break; case GAMEOBJECT_TYPE_CHEST: if (gameObject->HasConditionalInteraction() && gameObject->CanActivateForPlayer(receiver)) dynFlags |= GO_DYNFLAG_LO_ACTIVATE | GO_DYNFLAG_LO_SPARKLE | GO_DYNFLAG_LO_HIGHLIGHT; else if (receiver->IsGameMaster()) dynFlags |= GO_DYNFLAG_LO_ACTIVATE | GO_DYNFLAG_LO_SPARKLE; break; case GAMEOBJECT_TYPE_GENERIC: case GAMEOBJECT_TYPE_SPELL_FOCUS: if (gameObject->HasConditionalInteraction() && gameObject->CanActivateForPlayer(receiver)) dynFlags |= GO_DYNFLAG_LO_SPARKLE; break; case GAMEOBJECT_TYPE_TRANSPORT: case GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT: dynFlags |= dynamicFlags; // preserve all dynamicflgs break; case GAMEOBJECT_TYPE_CAPTURE_POINT: if (!gameObject->CanInteractWithCapturePoint(receiver)) dynFlags |= GO_DYNFLAG_LO_NO_INTERACT; else dynFlags &= ~GO_DYNFLAG_LO_NO_INTERACT; break; case GAMEOBJECT_TYPE_GATHERING_NODE: if (gameObject->HasConditionalInteraction() && gameObject->CanActivateForPlayer(receiver)) dynFlags |= GO_DYNFLAG_LO_ACTIVATE | GO_DYNFLAG_LO_SPARKLE | GO_DYNFLAG_LO_HIGHLIGHT; if (gameObject->GetGoStateFor(receiver->GetGUID()) == GO_STATE_ACTIVE) dynFlags |= GO_DYNFLAG_LO_DEPLETED; break; default: break; } if (!receiver->IsGameMaster()) { // GO_DYNFLAG_LO_INTERACT_COND should be applied to GOs with conditional interaction (without GO_FLAG_INTERACT_COND) to disable interaction // (Ignore GAMEOBJECT_TYPE_GATHERING_NODE as some profession-related GOs may include quest loot and can always be interacted with) // (Ignore GAMEOBJECT_TYPE_FLAGSTAND as interaction is handled by GO_DYNFLAG_LO_NO_INTERACT) // (Ignore GAMEOBJECT_TYPE_SPELLCASTER as interaction is handled by GO_DYNFLAG_LO_NO_INTERACT) if (gameObject->GetGoType() != GAMEOBJECT_TYPE_GATHERING_NODE && gameObject->GetGoType() != GAMEOBJECT_TYPE_FLAGSTAND && gameObject->GetGoType() != GAMEOBJECT_TYPE_SPELLCASTER) if (gameObject->HasConditionalInteraction() && !gameObject->HasFlag(GO_FLAG_INTERACT_COND)) dynFlags |= GO_DYNFLAG_LO_INTERACT_COND; if (!gameObject->MeetsInteractCondition(receiver)) dynFlags |= GO_DYNFLAG_LO_NO_INTERACT; if (SpawnMetadata const* data = sObjectMgr->GetSpawnMetadata(SPAWN_TYPE_GAMEOBJECT, gameObject->GetSpawnId())) if (data->spawnTrackingData && !data->spawnTrackingQuestObjectives.empty()) if (receiver->GetSpawnTrackingStateByObjectives(data->spawnTrackingData->SpawnTrackingId, data->spawnTrackingQuestObjectives) != SpawnTrackingState::Active) dynFlags &= ~GO_DYNFLAG_LO_ACTIVATE; } dynamicFlags = dynFlags; } return dynamicFlags; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::DisplayIDTag::value_type; static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver) { value_type displayId = unitData->DisplayID; if (unit->IsCreature()) { CreatureTemplate const* cinfo = unit->ToCreature()->GetCreatureTemplate(); if (TempSummon const* summon = unit->ToTempSummon()) { if (summon->GetSummonerGUID() == receiver->GetGUID()) { if (summon->GetCreatureIdVisibleToSummoner()) cinfo = sObjectMgr->GetCreatureTemplate(*summon->GetCreatureIdVisibleToSummoner()); if (summon->GetDisplayIdVisibleToSummoner()) displayId = *summon->GetDisplayIdVisibleToSummoner(); } } // this also applies for transform auras if (SpellInfo const* transform = sSpellMgr->GetSpellInfo(unit->GetTransformSpell(), unit->GetMap()->GetDifficultyID())) { for (SpellEffectInfo const& spellEffectInfo : transform->GetEffects()) { if (spellEffectInfo.IsAura(SPELL_AURA_TRANSFORM)) { if (CreatureTemplate const* transformInfo = sObjectMgr->GetCreatureTemplate(spellEffectInfo.MiscValue)) { cinfo = transformInfo; break; } } } } if (cinfo->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER) if (receiver->IsGameMaster()) displayId = cinfo->GetFirstVisibleModel()->CreatureDisplayID; } return displayId; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::StateWorldEffectIDsTag::value_type const*; static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver) { if (unit->IsCreature()) if (SpawnTrackingStateData const* spawnTrackingStateData = unit->GetSpawnTrackingStateDataForPlayer(receiver)) return &spawnTrackingStateData->StateWorldEffects; return &*unitData->StateWorldEffectIDs; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::StateSpellVisualIDTag::value_type; static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver) { value_type stateSpellVisual = unitData->StateSpellVisualID; if (unit->IsCreature()) if (SpawnTrackingStateData const* spawnTrackingStateData = unit->GetSpawnTrackingStateDataForPlayer(receiver)) stateSpellVisual = spawnTrackingStateData->StateSpellVisualId.value_or(0); return stateSpellVisual; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::StateAnimIDTag::value_type; static value_type GetValue(UF::UnitData const* /*unitData*/, Unit const* unit, Player const* receiver) { value_type stateAnimId = sDB2Manager.GetEmptyAnimStateID(); if (unit->IsCreature()) if (SpawnTrackingStateData const* spawnTrackingStateData = unit->GetSpawnTrackingStateDataForPlayer(receiver)) stateAnimId = spawnTrackingStateData->StateAnimId.value_or(stateAnimId); return stateAnimId; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::StateAnimKitIDTag::value_type; static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver) { value_type stateAnimKitId = unitData->StateAnimKitID; if (unit->IsCreature()) if (SpawnTrackingStateData const* spawnTrackingStateData = unit->GetSpawnTrackingStateDataForPlayer(receiver)) stateAnimKitId = spawnTrackingStateData->StateAnimKitId.value_or(0); return stateAnimKitId; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::StateWorldEffectsQuestObjectiveIDTag::value_type; static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver) { value_type stateWorldEffectsQuestObjectiveId = unitData->StateWorldEffectsQuestObjectiveID; if (!stateWorldEffectsQuestObjectiveId && unit->IsCreature()) { if (CreatureData const* data = unit->ToCreature()->GetCreatureData()) { auto itr = data->spawnTrackingQuestObjectives.begin(); auto end = data->spawnTrackingQuestObjectives.end(); if (itr != end) { // If there is no valid objective for player, fill UF with first objective (if any) stateWorldEffectsQuestObjectiveId = *itr; while (++itr != end) { if (receiver->GetSpawnTrackingStateByObjective(data->spawnTrackingData->SpawnTrackingId, *itr) != SpawnTrackingState::Active) continue; stateWorldEffectsQuestObjectiveId = *itr; break; } } } } return stateWorldEffectsQuestObjectiveId; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::FactionTemplateTag::value_type; static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver) { value_type factionTemplate = unitData->FactionTemplate; if (unit->IsControlledByPlayer() && receiver != unit && sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && unit->IsInRaidWith(receiver)) { FactionTemplateEntry const* ft1 = unit->GetFactionTemplateEntry(); FactionTemplateEntry const* ft2 = receiver->GetFactionTemplateEntry(); if (ft1 && ft2 && !ft1->IsFriendlyTo(ft2)) // pretend that all other HOSTILE players have own faction, to allow follow, heal, rezz (trade wont work) factionTemplate = receiver->GetFaction(); } return factionTemplate; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::FlagsTag::value_type; static value_type GetValue(UF::UnitData const* unitData, Unit const* /*unit*/, Player const* receiver) { value_type flags = unitData->Flags; // Gamemasters should be always able to interact with units - remove uninteractible flag if (receiver->IsGameMaster()) flags &= ~UNIT_FLAG_UNINTERACTIBLE; return flags; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::Flags2Tag::value_type; static value_type GetValue(UF::UnitData const* unitData, Unit const* /*unit*/, Player const* receiver) { value_type flags = unitData->Flags2; // Gamemasters should be always able to interact with units - remove uninteractible flag if (receiver->IsGameMaster()) flags &= ~UNIT_FLAG2_UNTARGETABLE_BY_CLIENT; return flags; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::Flags3Tag::value_type; static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver) { value_type flags = unitData->Flags3; if (flags & UNIT_FLAG3_ALREADY_SKINNED && unit->IsCreature() && !unit->ToCreature()->IsSkinnedBy(receiver)) flags &= ~UNIT_FLAG3_ALREADY_SKINNED; return flags; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::Flags4Tag::value_type; static value_type GetValue(UF::UnitData const* unitData, Unit const* /*unit*/, Player const* /*receiver*/) { return unitData->Flags4; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::AuraStateTag::value_type; static value_type GetValue(UF::UnitData const* /*unitData*/, Unit const* unit, Player const* receiver) { // Check per caster aura states to not enable using a spell in client if specified aura is not by target return unit->BuildAuraStateUpdateForTarget(receiver); } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::PvpFlagsTag::value_type; static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver) { value_type pvpFlags = unitData->PvpFlags; if (unit->IsControlledByPlayer() && receiver != unit && sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && unit->IsInRaidWith(receiver)) { FactionTemplateEntry const* ft1 = unit->GetFactionTemplateEntry(); FactionTemplateEntry const* ft2 = receiver->GetFactionTemplateEntry(); if (ft1 && ft2 && !ft1->IsFriendlyTo(ft2)) // Allow targeting opposite faction in party when enabled in config pvpFlags &= UNIT_BYTE2_FLAG_SANCTUARY; } return pvpFlags; } }; 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 & 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 { public: using value_type = UF::UnitData::NpcFlagsTag::value_type; static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver) { value_type npcFlag = unitData->NpcFlags; if (npcFlag) { if ((!unit->IsInteractionAllowedInCombat() && unit->IsInCombat()) || (!unit->IsInteractionAllowedWhileHostile() && unit->IsHostileTo(receiver))) npcFlag = 0; else if (Creature const* creature = unit->ToCreature()) { if (!receiver->CanSeeGossipOn(creature)) npcFlag &= ~(UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER); if (!receiver->CanSeeSpellClickOn(creature)) npcFlag &= ~UNIT_NPC_FLAG_SPELLCLICK; } } return npcFlag; } }; template<> class ViewerDependentValue { public: using value_type = UF::UnitData::NpcFlags2Tag::value_type; static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver) { value_type npcFlag = unitData->NpcFlags2; if (npcFlag) { if ((!unit->IsInteractionAllowedInCombat() && unit->IsInCombat()) || (!unit->IsInteractionAllowedWhileHostile() && unit->IsHostileTo(receiver))) npcFlag = 0; } return npcFlag; } }; template<> class ViewerDependentValue { public: using value_type = UF::GameObjectData::StateWorldEffectIDsTag::value_type const*; static value_type GetValue(UF::GameObjectData const* gameObjectData, GameObject const* gameObject, Player const* receiver) { if (SpawnTrackingStateData const* spawnTrackingStateData = gameObject->GetSpawnTrackingStateDataForPlayer(receiver)) return &spawnTrackingStateData->StateWorldEffects; return &*gameObjectData->StateWorldEffectIDs; } }; template<> class ViewerDependentValue { public: using value_type = UF::GameObjectData::StateSpellVisualIDTag::value_type; static value_type GetValue(UF::GameObjectData const* gameObjectData, GameObject const* gameObject, Player const* receiver) { value_type stateSpellVisual = gameObjectData->StateSpellVisualID; if (SpawnTrackingStateData const* spawnTrackingStateData = gameObject->GetSpawnTrackingStateDataForPlayer(receiver)) stateSpellVisual = spawnTrackingStateData->StateSpellVisualId.value_or(0); return stateSpellVisual; } }; template<> class ViewerDependentValue { public: using value_type = UF::GameObjectData::SpawnTrackingStateAnimIDTag::value_type; static value_type GetValue(UF::GameObjectData const* /*gameObjectData*/, GameObject const* gameObject, Player const* receiver) { value_type stateAnimId = sDB2Manager.GetEmptyAnimStateID(); if (SpawnTrackingStateData const* spawnTrackingStateData = gameObject->GetSpawnTrackingStateDataForPlayer(receiver)) stateAnimId = spawnTrackingStateData->StateAnimId.value_or(stateAnimId); return stateAnimId; } }; template<> class ViewerDependentValue { public: using value_type = UF::GameObjectData::SpawnTrackingStateAnimKitIDTag::value_type; static value_type GetValue(UF::GameObjectData const* gameObjectData, GameObject const* gameObject, Player const* receiver) { value_type stateAnimKitId = gameObjectData->SpawnTrackingStateAnimKitID; if (SpawnTrackingStateData const* spawnTrackingStateData = gameObject->GetSpawnTrackingStateDataForPlayer(receiver)) stateAnimKitId = spawnTrackingStateData->StateAnimKitId.value_or(0); return stateAnimKitId; } }; template<> class ViewerDependentValue { public: using value_type = UF::GameObjectData::StateWorldEffectsQuestObjectiveIDTag::value_type; static value_type GetValue(UF::GameObjectData const* gameObjectData, GameObject const* gameObject, Player const* receiver) { value_type stateWorldEffectsQuestObjectiveId = gameObjectData->StateWorldEffectsQuestObjectiveID; if (!stateWorldEffectsQuestObjectiveId) { if (::GameObjectData const* data = gameObject->GetGameObjectData()) { auto itr = data->spawnTrackingQuestObjectives.begin(); auto end = data->spawnTrackingQuestObjectives.end(); if (itr != end) { // If there is no valid objective for player, fill UF with first objective (if any) stateWorldEffectsQuestObjectiveId = *itr; while (++itr != end) { if (receiver->GetSpawnTrackingStateByObjective(data->spawnTrackingData->SpawnTrackingId, *itr) != SpawnTrackingState::Active) continue; stateWorldEffectsQuestObjectiveId = *itr; break; } } } } return stateWorldEffectsQuestObjectiveId; } }; template<> class ViewerDependentValue { public: using value_type = UF::GameObjectData::FlagsTag::value_type; static value_type GetValue(UF::GameObjectData const* gameObjectData, GameObject const* gameObject, Player const* receiver) { value_type flags = gameObjectData->Flags; if (gameObject->GetGoType() == GAMEOBJECT_TYPE_CHEST) if (gameObject->GetGOInfo()->chest.usegrouplootrules && !gameObject->IsLootAllowedFor(receiver)) flags |= GO_FLAG_LOCKED | GO_FLAG_NOT_SELECTABLE; return flags; } }; template<> class ViewerDependentValue { public: using value_type = UF::GameObjectData::StateTag::value_type; static value_type GetValue(UF::GameObjectData const* /*gameObjectData*/, GameObject const* gameObject, Player const* receiver) { return gameObject->GetGoStateFor(receiver->GetGUID()); } }; template<> class ViewerDependentValue { public: using value_type = UF::ConversationData::LastLineEndTimeTag::value_type; static value_type GetValue(UF::ConversationData const* /*conversationData*/, Conversation const* conversation, Player const* receiver) { LocaleConstant locale = receiver->GetSession()->GetSessionDbLocaleIndex(); return conversation->GetLastLineEndTime(locale).count(); } }; template<> class ViewerDependentValue { public: using value_type = UF::ConversationLine::StartTimeTag::value_type; static value_type GetValue(UF::ConversationLine const* conversationLineData, Conversation const* conversation, Player const* receiver) { value_type startTime = conversationLineData->StartTime; LocaleConstant locale = receiver->GetSession()->GetSessionDbLocaleIndex(); if (Milliseconds const* localizedStartTime = conversation->GetLineStartTime(locale, conversationLineData->ConversationLineID)) startTime = localizedStartTime->count(); return startTime; } }; } #endif // ViewerDependentValues_h__