aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Entities
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2024-03-09 00:54:27 +0100
committerShauren <shauren.trinity@gmail.com>2024-03-09 00:54:27 +0100
commitfccf6fb72b60b08dfbe6d5fb17fba55239944fca (patch)
treed34819afa3813592e60825b1f0d88e1262496dd1 /src/server/game/Entities
parent1439535c6ac2ca8db13990e4fee29d4c1312f87a (diff)
Core/Objects: Implemented vignettes
Diffstat (limited to 'src/server/game/Entities')
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp7
-rw-r--r--src/server/game/Entities/Creature/CreatureData.h2
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp43
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h10
-rw-r--r--src/server/game/Entities/GameObject/GameObjectData.h23
-rw-r--r--src/server/game/Entities/Player/Player.cpp105
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp38
-rw-r--r--src/server/game/Entities/Unit/Unit.h10
-rw-r--r--src/server/game/Entities/Unit/Vignette.cpp128
-rw-r--r--src/server/game/Entities/Unit/Vignette.h57
10 files changed, 420 insertions, 3 deletions
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index c122e51800a..2ceaa8ce7b1 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -1820,6 +1820,10 @@ bool Creature::CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, Creatu
if (CreateVehicleKit(vehId, entry, true))
UpdateDisplayPower();
+ if (!IsPet())
+ if (uint32 vignetteId = GetCreatureTemplate()->VignetteID)
+ SetVignette(vignetteId);
+
return true;
}
@@ -2284,6 +2288,9 @@ void Creature::setDeathState(DeathState s)
RemoveUnitFlag(UNIT_FLAG_IN_COMBAT);
SetMeleeDamageSchool(SpellSchools(cInfo->dmgschool));
+
+ if (uint32 vignetteId = cInfo->VignetteID)
+ SetVignette(vignetteId);
}
Motion_Initialize();
diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h
index 1e386ff2da5..976244a8f85 100644
--- a/src/server/game/Entities/Creature/CreatureData.h
+++ b/src/server/game/Entities/Creature/CreatureData.h
@@ -488,7 +488,7 @@ struct TC_GAME_API CreatureTemplate
std::vector<uint32> GossipMenuIds;
std::unordered_map<Difficulty, CreatureDifficulty> difficultyStore;
uint32 RequiredExpansion;
- uint32 VignetteID; /// @todo Read Vignette.db2
+ uint32 VignetteID;
uint32 faction;
uint64 npcflag;
float speed_walk;
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index ecc2ba48585..dacdb902d30 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -53,6 +53,7 @@
#include "SpellAuras.h"
#include "SpellMgr.h"
#include "Transport.h"
+#include "Vignette.h"
#include "World.h"
#include <G3D/Box.h>
#include <G3D/CoordinateFrame.h>
@@ -874,6 +875,8 @@ std::string const& GameObject::GetAIName() const
void GameObject::CleanupsBeforeDelete(bool finalCleanup)
{
+ SetVignette(0);
+
WorldObject::CleanupsBeforeDelete(finalCleanup);
RemoveFromOwner();
@@ -1152,6 +1155,9 @@ bool GameObject::Create(uint32 entry, Map* map, Position const& pos, QuaternionD
_animKitId = gameObjectAddon->AIAnimKitID;
}
+ if (uint32 vignetteId = GetGOInfo()->GetSpawnVignette())
+ SetVignette(vignetteId);
+
LastUsedScriptID = GetGOInfo()->ScriptId;
m_stringIds[0] = goInfo->StringId;
@@ -3425,6 +3431,24 @@ void GameObject::Use(Unit* user)
break;
}
+ if (m_vignette)
+ {
+ if (Player* player = user->ToPlayer())
+ {
+ if (Quest const* reward = sObjectMgr->GetQuestTemplate(m_vignette->Data->RewardQuestID))
+ if (!player->GetQuestRewardStatus(m_vignette->Data->RewardQuestID))
+ player->RewardQuest(reward, LootItemType::Item, 0, this, false);
+
+ if (m_vignette->Data->VisibleTrackingQuestID)
+ player->SetRewardedQuest(m_vignette->Data->VisibleTrackingQuestID);
+ }
+
+ // only unregister it from visibility (need to keep vignette for other gameobject users in case its usable by multiple players
+ // to flag their quest completion
+ if (GetGOInfo()->ClearObjectVignetteonOpening())
+ Vignettes::Remove(*m_vignette, this);
+ }
+
if (!spellId)
return;
@@ -4090,6 +4114,10 @@ void GameObject::AfterRelocation()
if (m_goTypeImpl)
m_goTypeImpl->OnRelocated();
+ // TODO: on heartbeat
+ if (m_vignette)
+ Vignettes::Update(*m_vignette, this);
+
UpdateObjectVisibility(false);
}
@@ -4167,6 +4195,21 @@ void GameObject::SetAnimKitId(uint16 animKitId, bool oneshot)
SendMessageToSet(activateAnimKit.Write(), true);
}
+void GameObject::SetVignette(uint32 vignetteId)
+{
+ if (m_vignette)
+ {
+ if (m_vignette->Data->ID == vignetteId)
+ return;
+
+ Vignettes::Remove(*m_vignette, this);
+ m_vignette = nullptr;
+ }
+
+ if (VignetteEntry const* vignette = sVignetteStore.LookupEntry(vignetteId))
+ m_vignette = Vignettes::Create(vignette, this);
+}
+
void GameObject::SetSpellVisualId(int32 spellVisualId, ObjectGuid activatorGuid)
{
SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::SpellVisualID), spellVisualId);
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index 9926fd82cca..9b8651f1133 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -35,6 +35,11 @@ struct Loot;
struct TransportAnimation;
enum TriggerCastFlags : uint32;
+namespace Vignettes
+{
+struct VignetteData;
+}
+
// enum for GAMEOBJECT_TYPE_NEW_FLAG
// values taken from world state
enum class FlagState : uint8
@@ -419,6 +424,9 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
uint32 GetWorldEffectID() const { return _worldEffectID; }
void SetWorldEffectID(uint32 worldEffectID) { _worldEffectID = worldEffectID; }
+ Vignettes::VignetteData const* GetVignette() const { return m_vignette.get(); }
+ void SetVignette(uint32 vignetteId);
+
void SetSpellVisualId(int32 spellVisualId, ObjectGuid activatorGuid = ObjectGuid::Empty);
void AssaultCapturePoint(Player* player);
void UpdateCapturePoint();
@@ -502,6 +510,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
uint16 _animKitId;
uint32 _worldEffectID;
+ std::unique_ptr<Vignettes::VignetteData> m_vignette;
+
struct PerPlayerState
{
SystemTimePoint ValidUntil = SystemTimePoint::min();
diff --git a/src/server/game/Entities/GameObject/GameObjectData.h b/src/server/game/Entities/GameObject/GameObjectData.h
index deee786d1e6..1c05153c550 100644
--- a/src/server/game/Entities/GameObject/GameObjectData.h
+++ b/src/server/game/Entities/GameObject/GameObjectData.h
@@ -1277,6 +1277,29 @@ struct GameObjectTemplate
}
}
+ uint32 GetSpawnVignette() const
+ {
+ switch (type)
+ {
+ case GAMEOBJECT_TYPE_CHEST: return chest.SpawnVignette;
+ case GAMEOBJECT_TYPE_GOOBER: return goober.SpawnVignette;
+ case GAMEOBJECT_TYPE_NEW_FLAG: return newflag.SpawnVignette;
+ case GAMEOBJECT_TYPE_NEW_FLAG_DROP: return newflagdrop.SpawnVignette;
+ case GAMEOBJECT_TYPE_CAPTURE_POINT: return capturePoint.SpawnVignette;
+ case GAMEOBJECT_TYPE_GATHERING_NODE: return gatheringNode.SpawnVignette;
+ default: return 0;
+ }
+ }
+
+ bool ClearObjectVignetteonOpening() const
+ {
+ switch (type)
+ {
+ case GAMEOBJECT_TYPE_GATHERING_NODE: return gatheringNode.ClearObjectVignetteonOpening != 0;
+ default: return false;
+ }
+ }
+
uint32 GetSpellFocusType() const
{
switch (type)
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 337055bf4ab..469bbee9e74 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -130,6 +130,8 @@
#include "Util.h"
#include "Vehicle.h"
#include "VehiclePackets.h"
+#include "Vignette.h"
+#include "VignettePackets.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
@@ -7623,6 +7625,24 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
guild->UpdateMemberData(this, GUILD_MEMBER_DATA_ZONEID, newZone);
UpdateCriteria(CriteriaType::EnterTopLevelArea, newZone);
UpdateCriteria(CriteriaType::LeaveTopLevelArea, oldZone);
+
+ {
+ WorldPackets::Vignette::VignetteUpdate vignetteUpdate;
+
+ for (Vignettes::VignetteData const* vignette : GetMap()->GetInfiniteAOIVignettes())
+ {
+ if (!vignette->Data->GetFlags().HasFlag(VignetteFlags::ZoneInfiniteAOI))
+ continue;
+
+ if (vignette->ZoneID == newZone && Vignettes::CanSee(this, *vignette))
+ vignette->FillPacket(vignetteUpdate.Added);
+ else if (vignette->ZoneID == oldZone)
+ vignetteUpdate.Removed.push_back(vignette->Guid);
+ }
+
+ if (!vignetteUpdate.Added.IDs.empty() || !vignetteUpdate.Removed.empty())
+ SendDirectMessage(vignetteUpdate.Write());
+ }
}
}
@@ -23787,6 +23807,44 @@ inline void BeforeVisibilityDestroy<Creature>(Creature* t, Player* p)
{
if (p->GetPetGUID() == t->GetGUID() && t->IsPet())
t->ToPet()->Remove(PET_SAVE_NOT_IN_SLOT, true);
+
+ if (Vignettes::VignetteData const* vignette = t->GetVignette())
+ {
+ if (!vignette->Data->IsInfiniteAOI())
+ {
+ WorldPackets::Vignette::VignetteUpdate vignetteUpdate;
+ vignetteUpdate.Removed.push_back(vignette->Guid);
+ p->SendDirectMessage(vignetteUpdate.Write());
+ }
+ }
+}
+
+template<>
+inline void BeforeVisibilityDestroy<Player>(Player* t, Player* p)
+{
+ if (Vignettes::VignetteData const* vignette = t->GetVignette())
+ {
+ if (!vignette->Data->IsInfiniteAOI())
+ {
+ WorldPackets::Vignette::VignetteUpdate vignetteUpdate;
+ vignetteUpdate.Removed.push_back(vignette->Guid);
+ p->SendDirectMessage(vignetteUpdate.Write());
+ }
+ }
+}
+
+template<>
+inline void BeforeVisibilityDestroy<GameObject>(GameObject* t, Player* p)
+{
+ if (Vignettes::VignetteData const* vignette = t->GetVignette())
+ {
+ if (!vignette->Data->IsInfiniteAOI())
+ {
+ WorldPackets::Vignette::VignetteUpdate vignetteUpdate;
+ vignetteUpdate.Removed.push_back(vignette->Guid);
+ p->SendDirectMessage(vignetteUpdate.Write());
+ }
+ }
}
void Player::UpdateVisibilityOf(Trinity::IteratorPair<WorldObject**> targets)
@@ -23850,8 +23908,20 @@ void Player::UpdateVisibilityOf(WorldObject* target)
{
if (!CanSeeOrDetect(target, false, true))
{
- if (target->GetTypeId() == TYPEID_UNIT)
- BeforeVisibilityDestroy<Creature>(target->ToCreature(), this);
+ switch (target->GetTypeId())
+ {
+ case TYPEID_UNIT:
+ BeforeVisibilityDestroy<Creature>(target->ToCreature(), this);
+ break;
+ case TYPEID_PLAYER:
+ BeforeVisibilityDestroy<Player>(target->ToPlayer(), this);
+ break;
+ case TYPEID_GAMEOBJECT:
+ BeforeVisibilityDestroy<GameObject>(target->ToGameObject(), this);
+ break;
+ default:
+ break;
+ }
if (!target->IsDestroyedObject())
target->SendOutOfRangeForPlayer(this);
@@ -23927,6 +23997,16 @@ void Player::UpdateTriggerVisibility()
void Player::SendInitialVisiblePackets(WorldObject* target) const
{
+ auto sendVignette = [](Vignettes::VignetteData const& vignette, Player const* where)
+ {
+ if (!vignette.Data->IsInfiniteAOI() && Vignettes::CanSee(where, vignette))
+ {
+ WorldPackets::Vignette::VignetteUpdate vignetteUpdate;
+ vignette.FillPacket(vignetteUpdate.Added);
+ where->SendDirectMessage(vignetteUpdate.Write());
+ }
+ };
+
if (Unit* targetUnit = target->ToUnit())
{
SendAurasForTarget(targetUnit);
@@ -23935,6 +24015,14 @@ void Player::SendInitialVisiblePackets(WorldObject* target) const
if (targetUnit->HasUnitState(UNIT_STATE_MELEE_ATTACKING) && targetUnit->GetVictim())
targetUnit->SendMeleeAttackStart(targetUnit->GetVictim());
}
+
+ if (Vignettes::VignetteData const* vignette = targetUnit->GetVignette())
+ sendVignette(*vignette, this);
+ }
+ else if (GameObject* targetGo = target->ToGameObject())
+ {
+ if (Vignettes::VignetteData const* vignette = targetGo->GetVignette())
+ sendVignette(*vignette, this);
}
}
@@ -24233,6 +24321,19 @@ void Player::SendInitialPacketsAfterAddToMap()
{
UpdateVisibilityForPlayer();
+ // Send map wide vignettes before UpdateZone, that will send zone wide vignettes
+ // But first send on new map will wipe all vignettes on client
+ {
+ WorldPackets::Vignette::VignetteUpdate vignetteUpdate;
+ vignetteUpdate.ForceUpdate = true;
+
+ for (Vignettes::VignetteData const* vignette : GetMap()->GetInfiniteAOIVignettes())
+ if (!vignette->Data->GetFlags().HasFlag(VignetteFlags::ZoneInfiniteAOI) && Vignettes::CanSee(this, *vignette))
+ vignette->FillPacket(vignetteUpdate.Added);
+
+ SendDirectMessage(vignetteUpdate.Write());
+ }
+
// update zone
uint32 newzone, newarea;
GetZoneAndAreaId(newzone, newarea);
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index fbcfd287752..bfdb97d3afb 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -85,6 +85,8 @@
#include "Util.h"
#include "Vehicle.h"
#include "VehiclePackets.h"
+#include "Vignette.h"
+#include "VignettePackets.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
@@ -8629,6 +8631,9 @@ void Unit::setDeathState(DeathState s)
SetEmoteState(EMOTE_ONESHOT_NONE);
SetStandState(UNIT_STAND_STATE_STAND);
+ if (m_vignette && !m_vignette->Data->GetFlags().HasFlag(VignetteFlags::PersistsThroughDeath))
+ SetVignette(0);
+
// players in instance don't have ZoneScript, but they have InstanceScript
if (ZoneScript* zoneScript = GetZoneScript() ? GetZoneScript() : GetInstanceScript())
zoneScript->OnUnitDeath(this);
@@ -9665,6 +9670,8 @@ void Unit::CleanupBeforeRemoveFromMap(bool finalCleanup)
// This needs to be before RemoveFromWorld to make GetCaster() return a valid pointer on aura removal
InterruptNonMeleeSpells(true);
+ SetVignette(0);
+
if (IsInWorld())
RemoveFromWorld();
else
@@ -10706,6 +10713,18 @@ void Unit::SetMeleeAnimKitId(uint16 animKitId)
}
}
+ if (Vignettes::VignetteData const* vignette = victim->GetVignette())
+ {
+ for (Player* tapper : tappers)
+ {
+ if (Quest const* reward = sObjectMgr->GetQuestTemplate(vignette->Data->RewardQuestID))
+ tapper->RewardQuest(reward, LootItemType::Item, 0, victim, false);
+
+ if (vignette->Data->VisibleTrackingQuestID)
+ tapper->SetRewardedQuest(vignette->Data->VisibleTrackingQuestID);
+ }
+ }
+
KillRewarder(Trinity::IteratorPair(tappers.data(), tappers.data() + tappers.size()), victim, false).Reward();
}
@@ -12412,6 +12431,10 @@ bool Unit::UpdatePosition(float x, float y, float z, float orientation, bool tel
if (isInWater)
RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags2::Swimming);
+ // TODO: on heartbeat
+ if (m_vignette)
+ Vignettes::Update(*m_vignette, this);
+
return (relocated || turn);
}
@@ -13733,6 +13756,21 @@ float Unit::GetCollisionHeight() const
return collisionHeight == 0.0f ? DEFAULT_COLLISION_HEIGHT : collisionHeight;
}
+void Unit::SetVignette(uint32 vignetteId)
+{
+ if (m_vignette)
+ {
+ if (m_vignette->Data->ID == vignetteId)
+ return;
+
+ Vignettes::Remove(*m_vignette, this);
+ m_vignette = nullptr;
+ }
+
+ if (VignetteEntry const* vignette = sVignetteStore.LookupEntry(vignetteId))
+ m_vignette = Vignettes::Create(vignette, this);
+}
+
std::string Unit::GetDebugInfo() const
{
std::stringstream sstr;
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 0c17b38a2fe..e00fb1a44c9 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -111,6 +111,11 @@ namespace Movement
struct SpellEffectExtraData;
}
+namespace Vignettes
+{
+struct VignetteData;
+}
+
typedef std::list<Unit*> UnitList;
class TC_GAME_API DispelableAura
@@ -1799,6 +1804,9 @@ class TC_GAME_API Unit : public WorldObject
}
void ClearWorldEffects() { ClearDynamicUpdateFieldValues(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::WorldEffects)); }
+ Vignettes::VignetteData const* GetVignette() const { return m_vignette.get(); }
+ void SetVignette(uint32 vignetteId);
+
std::string GetDebugInfo() const override;
UF::UpdateField<UF::UnitData, 0, TYPEID_UNIT> m_unitData;
@@ -1903,6 +1911,8 @@ class TC_GAME_API Unit : public WorldObject
uint32 m_unitTypeMask;
LiquidTypeEntry const* _lastLiquid;
+ std::unique_ptr<Vignettes::VignetteData> m_vignette;
+
bool IsAlwaysVisibleFor(WorldObject const* seer) const override;
bool IsAlwaysDetectableFor(WorldObject const* seer) const override;
diff --git a/src/server/game/Entities/Unit/Vignette.cpp b/src/server/game/Entities/Unit/Vignette.cpp
new file mode 100644
index 00000000000..1f4aa074ea2
--- /dev/null
+++ b/src/server/game/Entities/Unit/Vignette.cpp
@@ -0,0 +1,128 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "Vignette.h"
+#include "CellImpl.h"
+#include "DB2Stores.h"
+#include "GridNotifiersImpl.h"
+#include "VignettePackets.h"
+
+namespace Vignettes
+{
+namespace
+{
+void UpdatePosition(VignetteData& vignette, WorldObject const* owner)
+{
+ vignette.Position = owner->GetPosition();
+ if (WmoLocation const* wmoLocation = owner->GetCurrentWmo())
+ {
+ vignette.WMOGroupID = wmoLocation->GroupId;
+ vignette.WMODoodadPlacementID = wmoLocation->UniqueId;
+ }
+}
+
+template<WorldPackets::Vignette::VignetteDataSet WorldPackets::Vignette::VignetteUpdate::* Field>
+void SendVignetteUpdate(VignetteData const& vignette, WorldObject const* owner)
+{
+ if (!owner->IsInWorld())
+ return;
+
+ WorldPackets::Vignette::VignetteUpdate vignetteUpdate;
+ vignette.FillPacket(vignetteUpdate.*Field);
+ vignetteUpdate.Write();
+
+ auto sender = [&](Player const* receiver)
+ {
+ if (CanSee(receiver, vignette))
+ receiver->SendDirectMessage(vignetteUpdate.GetRawPacket());
+ };
+
+ Trinity::MessageDistDeliverer notifier(owner, sender, owner->GetVisibilityRange());
+ Cell::VisitWorldObjects(owner, notifier, owner->GetVisibilityRange());
+}
+}
+
+void VignetteData::FillPacket(WorldPackets::Vignette::VignetteDataSet& dataSet) const
+{
+ dataSet.IDs.push_back(Guid);
+
+ WorldPackets::Vignette::VignetteData& data = dataSet.Data.emplace_back();
+ data.ObjGUID = Object;
+ data.Position = Position;
+ data.VignetteID = Data->ID;
+ data.ZoneID = ZoneID;
+ data.WMOGroupID = WMOGroupID;
+ data.WMODoodadPlacementID = WMODoodadPlacementID;
+}
+
+std::unique_ptr<VignetteData> Create(VignetteEntry const* vignetteData, WorldObject const* owner)
+{
+ std::unique_ptr<VignetteData> vignette = std::make_unique<VignetteData>();
+ vignette->Guid = ObjectGuid::Create<HighGuid::Vignette>(owner->GetMapId(), vignetteData->ID, owner->GetMap()->GenerateLowGuid<HighGuid::Vignette>());
+ vignette->Object = owner->GetGUID();
+ vignette->Position = owner->GetPosition();
+ vignette->Data = vignetteData;
+ vignette->ZoneID = owner->GetZoneId(); // not updateable
+ UpdatePosition(*vignette, owner);
+
+ if (vignetteData->IsInfiniteAOI())
+ owner->GetMap()->AddInfiniteAOIVignette(vignette.get());
+ else
+ SendVignetteUpdate<&WorldPackets::Vignette::VignetteUpdate::Added>(*vignette, owner);
+
+ return vignette;
+}
+
+void Update(VignetteData& vignette, WorldObject const* owner)
+{
+ UpdatePosition(vignette, owner);
+
+ if (vignette.Data->IsInfiniteAOI())
+ vignette.NeedUpdate = true;
+ else
+ SendVignetteUpdate<&WorldPackets::Vignette::VignetteUpdate::Updated>(vignette, owner);
+}
+
+void Remove(VignetteData& vignette, WorldObject const* owner)
+{
+ if (vignette.Data->IsInfiniteAOI())
+ owner->GetMap()->RemoveInfiniteAOIVignette(&vignette);
+ else
+ {
+ WorldPackets::Vignette::VignetteUpdate vignetteUpdate;
+ vignetteUpdate.Removed.push_back(vignette.Guid);
+ owner->SendMessageToSet(vignetteUpdate.Write(), true);
+ }
+}
+
+bool CanSee(Player const* player, VignetteData const& vignette)
+{
+ if (vignette.Data->GetFlags().HasFlag(VignetteFlags::ZoneInfiniteAOI))
+ if (vignette.ZoneID != player->GetZoneId())
+ return false;
+
+ if (vignette.Data->VisibleTrackingQuestID)
+ if (player->IsQuestRewarded(vignette.Data->VisibleTrackingQuestID))
+ return false;
+
+ if (PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(vignette.Data->PlayerConditionID))
+ if (!ConditionMgr::IsPlayerMeetingCondition(player, playerCondition))
+ return false;
+
+ return true;
+}
+}
diff --git a/src/server/game/Entities/Unit/Vignette.h b/src/server/game/Entities/Unit/Vignette.h
new file mode 100644
index 00000000000..f318faff983
--- /dev/null
+++ b/src/server/game/Entities/Unit/Vignette.h
@@ -0,0 +1,57 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TRINITYCORE_VIGNETTE_H
+#define TRINITYCORE_VIGNETTE_H
+
+#include "ObjectGuid.h"
+#include "Position.h"
+#include <memory>
+
+struct VignetteEntry;
+class Player;
+class WorldObject;
+
+namespace WorldPackets::Vignette
+{
+struct VignetteDataSet;
+}
+
+namespace Vignettes
+{
+struct VignetteData
+{
+ ObjectGuid Guid;
+ ObjectGuid Object;
+ ::Position Position;
+ VignetteEntry const* Data;
+ uint32 ZoneID = 0;
+ uint32 WMOGroupID = 0;
+ uint32 WMODoodadPlacementID = 0;
+ bool NeedUpdate = false;
+
+ void FillPacket(WorldPackets::Vignette::VignetteDataSet& dataSet) const;
+};
+
+std::unique_ptr<VignetteData> Create(VignetteEntry const* vignetteData, WorldObject const* owner);
+void Update(VignetteData& vignette, WorldObject const* owner);
+void Remove(VignetteData& vignette, WorldObject const* owner);
+
+bool CanSee(Player const* player, VignetteData const& vignette);
+}
+
+#endif // TRINITYCORE_VIGNETTE_H