diff options
author | Shauren <shauren.trinity@gmail.com> | 2024-03-09 00:54:27 +0100 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2024-03-09 00:54:27 +0100 |
commit | fccf6fb72b60b08dfbe6d5fb17fba55239944fca (patch) | |
tree | d34819afa3813592e60825b1f0d88e1262496dd1 /src | |
parent | 1439535c6ac2ca8db13990e4fee29d4c1312f87a (diff) |
Core/Objects: Implemented vignettes
Diffstat (limited to 'src')
26 files changed, 701 insertions, 11 deletions
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index 010fb0498e2..55281c309ea 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -1931,6 +1931,12 @@ void HotfixDatabaseConnection::DoPrepareStatements() " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_VEHICLE_SEAT, "SELECT MAX(ID) + 1 FROM vehicle_seat", CONNECTION_SYNCH); + // Vignette.db2 + PrepareStatement(HOTFIX_SEL_VIGNETTE, "SELECT ID, Name, PlayerConditionID, VisibleTrackingQuestID, QuestFeedbackEffectID, Flags, MaxHeight, " + "MinHeight, VignetteType, RewardQuestID, UiWidgetSetID FROM vignette WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_VIGNETTE, "SELECT MAX(ID) + 1 FROM vignette", CONNECTION_SYNCH); + PREPARE_LOCALE_STMT(HOTFIX_SEL_VIGNETTE, "SELECT ID, Name_lang FROM vignette_locale WHERE (`VerifiedBuild` > 0) = ? AND locale = ?", CONNECTION_SYNCH); + // WmoAreaTable.db2 PrepareStatement(HOTFIX_SEL_WMO_AREA_TABLE, "SELECT AreaName, ID, WmoID, NameSetID, WmoGroupID, SoundProviderPref, SoundProviderPrefUnderwater, " "AmbienceID, UwAmbience, ZoneMusic, UwZoneMusic, IntroSound, UwIntroSound, AreaTableID, Flags FROM wmo_area_table" diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index 2df1c260a97..45600062398 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -1114,6 +1114,10 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_VEHICLE_SEAT, HOTFIX_SEL_VEHICLE_SEAT_MAX_ID, + HOTFIX_SEL_VIGNETTE, + HOTFIX_SEL_VIGNETTE_MAX_ID, + HOTFIX_SEL_VIGNETTE_LOCALE, + HOTFIX_SEL_WMO_AREA_TABLE, HOTFIX_SEL_WMO_AREA_TABLE_MAX_ID, HOTFIX_SEL_WMO_AREA_TABLE_LOCALE, diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h index d051c0fa472..843d3256632 100644 --- a/src/server/game/DataStores/DB2LoadInfo.h +++ b/src/server/game/DataStores/DB2LoadInfo.h @@ -6415,6 +6415,26 @@ struct VehicleSeatLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 66, &VehicleSeatMeta::Instance, HOTFIX_SEL_VEHICLE_SEAT }; }; +struct VignetteLoadInfo +{ + static constexpr DB2FieldMeta Fields[11] = + { + { false, FT_INT, "ID" }, + { false, FT_STRING, "Name" }, + { false, FT_INT, "PlayerConditionID" }, + { false, FT_INT, "VisibleTrackingQuestID" }, + { false, FT_INT, "QuestFeedbackEffectID" }, + { true, FT_INT, "Flags" }, + { false, FT_FLOAT, "MaxHeight" }, + { false, FT_FLOAT, "MinHeight" }, + { true, FT_BYTE, "VignetteType" }, + { true, FT_INT, "RewardQuestID" }, + { true, FT_INT, "UiWidgetSetID" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 11, &VignetteMeta::Instance, HOTFIX_SEL_VIGNETTE }; +}; + struct WmoAreaTableLoadInfo { static constexpr DB2FieldMeta Fields[15] = diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 8c849f43061..8bd70407d92 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -370,6 +370,7 @@ DB2Storage<UnitConditionEntry> sUnitConditionStore("UnitConditi DB2Storage<UnitPowerBarEntry> sUnitPowerBarStore("UnitPowerBar.db2", &UnitPowerBarLoadInfo::Instance); DB2Storage<VehicleEntry> sVehicleStore("Vehicle.db2", &VehicleLoadInfo::Instance); DB2Storage<VehicleSeatEntry> sVehicleSeatStore("VehicleSeat.db2", &VehicleSeatLoadInfo::Instance); +DB2Storage<VignetteEntry> sVignetteStore("Vignette.db2", &VignetteLoadInfo::Instance); DB2Storage<WMOAreaTableEntry> sWMOAreaTableStore("WMOAreaTable.db2", &WmoAreaTableLoadInfo::Instance); DB2Storage<WorldEffectEntry> sWorldEffectStore("WorldEffect.db2", &WorldEffectLoadInfo::Instance); DB2Storage<WorldMapOverlayEntry> sWorldMapOverlayStore("WorldMapOverlay.db2", &WorldMapOverlayLoadInfo::Instance); @@ -519,9 +520,6 @@ namespace std::unordered_map<uint32, std::unordered_set<uint32>> _pvpStatIdsByMap; } -template<typename T> -constexpr std::size_t GetCppRecordSize(DB2Storage<T> const&) { return sizeof(T); } - void LoadDB2(std::bitset<TOTAL_LOCALES>& availableDb2Locales, std::vector<std::string>& errlist, StorageMap& stores, DB2StorageBase* storage, std::string const& db2Path, LocaleConstant defaultLocale, std::size_t cppRecordSize) { @@ -638,7 +636,10 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul if (!availableDb2Locales[defaultLocale]) return 0; -#define LOAD_DB2(store) LoadDB2(availableDb2Locales, loadErrors, _stores, &(store), db2Path, defaultLocale, GetCppRecordSize(store)) + auto LOAD_DB2 = [&]<typename T>(DB2Storage<T>& store) + { + LoadDB2(availableDb2Locales, loadErrors, _stores, &store, db2Path, defaultLocale, sizeof(T)); + }; LOAD_DB2(sAchievementStore); LOAD_DB2(sAchievementCategoryStore); @@ -969,6 +970,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sUnitPowerBarStore); LOAD_DB2(sVehicleStore); LOAD_DB2(sVehicleSeatStore); + LOAD_DB2(sVignetteStore); LOAD_DB2(sWMOAreaTableStore); LOAD_DB2(sWorldEffectStore); LOAD_DB2(sWorldMapOverlayStore); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 91609057a35..97c8dc502a4 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -298,6 +298,7 @@ TC_GAME_API extern DB2Storage<UnitConditionEntry> sUnitConditi TC_GAME_API extern DB2Storage<UnitPowerBarEntry> sUnitPowerBarStore; TC_GAME_API extern DB2Storage<VehicleEntry> sVehicleStore; TC_GAME_API extern DB2Storage<VehicleSeatEntry> sVehicleSeatStore; +TC_GAME_API extern DB2Storage<VignetteEntry> sVignetteStore; TC_GAME_API extern DB2Storage<WorldEffectEntry> sWorldEffectStore; TC_GAME_API extern DB2Storage<WorldMapOverlayEntry> sWorldMapOverlayStore; TC_GAME_API extern DB2Storage<WorldStateExpressionEntry> sWorldStateExpressionStore; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index 455b3f78ea8..5a6d8144dfe 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -4571,6 +4571,24 @@ struct VehicleSeatEntry inline bool IsEjectable() const { return HasFlag(VEHICLE_SEAT_FLAG_B_EJECTABLE); } }; +struct VignetteEntry +{ + uint32 ID; + LocalizedString Name; + uint32 PlayerConditionID; + uint32 VisibleTrackingQuestID; + uint32 QuestFeedbackEffectID; + int32 Flags; + float MaxHeight; + float MinHeight; + int8 VignetteType; + int32 RewardQuestID; + int32 UiWidgetSetID; + + EnumFlag<VignetteFlags> GetFlags() const { return static_cast<VignetteFlags>(Flags); } + bool IsInfiniteAOI() const { return GetFlags().HasFlag(VignetteFlags::InfiniteAOI | VignetteFlags::ZoneInfiniteAOI); } +}; + struct WMOAreaTableEntry { LocalizedString AreaName; diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 49d853218c0..92c71f389e1 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -2463,6 +2463,27 @@ enum VehicleSeatFlagsB VEHICLE_SEAT_FLAG_B_VEHICLE_PLAYERFRAME_UI = 0x80000000 // Lua_UnitHasVehiclePlayerFrameUI - actually checked for flagsb &~ 0x80000000 }; +enum class VignetteFlags +{ + InfiniteAOI = 0x000001, + ShowOnMap = 0x000002, + PingMinimap = 0x000004, + TestVisibilityRules = 0x000008, + VerticalRangeIsAbsolute = 0x000010, + Unique = 0x000020, + ZoneInfiniteAOI = 0x000040, + PersistsThroughDeath = 0x000080, + + DontShowOnMinimap = 0x000200, + HasTooltip = 0x000400, + + AdditionalHeightReq = 0x008000, // Must be within 10 yards of vignette Z coord (hardcoded in client) + HideOnContinentMaps = 0x010000, + NoPaddingAboveUiWidgets = 0x020000 +}; + +DEFINE_ENUM_FLAG(VignetteFlags); + enum WorldMapTransformsFlags { WORLD_MAP_TRANSFORMS_FLAG_DUNGEON = 0x04 diff --git a/src/server/game/DataStores/GameTables.cpp b/src/server/game/DataStores/GameTables.cpp index 2f786ddb63b..6483b8fd63c 100644 --- a/src/server/game/DataStores/GameTables.cpp +++ b/src/server/game/DataStores/GameTables.cpp @@ -110,7 +110,11 @@ void LoadGameTables(std::string const& dataPath) std::vector<std::string> bad_gt_files; uint32 gameTableCount = 0, expectedGameTableCount = 0; -#define LOAD_GT(store, file) gameTableCount += LoadGameTable(bad_gt_files, store, gtPath / file); ++expectedGameTableCount; + auto LOAD_GT = [&]<typename T>(GameTable<T>& gameTable, char const* file) + { + gameTableCount += LoadGameTable(bad_gt_files, gameTable, gtPath / file); + ++expectedGameTableCount; + }; LOAD_GT(sArtifactKnowledgeMultiplierGameTable, "ArtifactKnowledgeMultiplier.txt"); LOAD_GT(sArtifactLevelXPGameTable, "ArtifactLevelXP.txt"); 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 diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 0f4c7112e8e..5987f183214 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -1152,6 +1152,12 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has assigned gossip menu, but npcflag does not include UNIT_NPC_FLAG_GOSSIP.", cInfo->Entry); else if (cInfo->GossipMenuIds.empty() && cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP) TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has npcflag UNIT_NPC_FLAG_GOSSIP, but gossip menu is unassigned.", cInfo->Entry); + + if (cInfo->VignetteID && !sVignetteStore.HasRecord(cInfo->VignetteID)) + { + TC_LOG_INFO("sql.sql", "Creature (Entry: {}) has non-existing Vignette {}, set to 0.", cInfo->Entry, cInfo->VignetteID); + const_cast<CreatureTemplate*>(cInfo)->VignetteID = 0; + } } void ObjectMgr::CheckCreatureMovement(char const* table, uint64 id, CreatureMovementData& creatureMovement) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index b722717a86a..9bb50c2d913 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -54,6 +54,8 @@ #include "Vehicle.h" #include "VMapFactory.h" #include "VMapManager2.h" +#include "Vignette.h" +#include "VignettePackets.h" #include "Weather.h" #include "WeatherMgr.h" #include "World.h" @@ -144,7 +146,7 @@ m_unloadTimer(0), m_VisibleDistance(DEFAULT_VISIBILITY_DISTANCE), m_VisibilityNotifyPeriod(DEFAULT_VISIBILITY_NOTIFY_PERIOD), m_activeNonPlayersIter(m_activeNonPlayers.end()), _transportsUpdateIter(_transports.end()), i_gridExpiry(expiry), m_terrain(sTerrainMgr.LoadTerrain(id)), m_forceEnabledNavMeshFilterFlags(0), m_forceDisabledNavMeshFilterFlags(0), -i_scriptLock(false), _respawnTimes(std::make_unique<RespawnListContainer>()), _respawnCheckTimer(0) +i_scriptLock(false), _respawnTimes(std::make_unique<RespawnListContainer>()), _respawnCheckTimer(0), _vignetteUpdateTimer(5200, 5200) { for (uint32 x = 0; x < MAX_NUMBER_OF_GRIDS; ++x) { @@ -511,6 +513,38 @@ void Map::SetWorldStateValue(int32 worldStateId, int32 value, bool hidden) } } +void Map::AddInfiniteAOIVignette(Vignettes::VignetteData* vignette) +{ + _infiniteAOIVignettes.push_back(vignette); + + WorldPackets::Vignette::VignetteUpdate vignetteUpdate; + vignette->FillPacket(vignetteUpdate.Added); + vignetteUpdate.Write(); + + for (MapReference const& ref : m_mapRefManager) + if (Vignettes::CanSee(ref.GetSource(), *vignette)) + ref.GetSource()->SendDirectMessage(vignetteUpdate.GetRawPacket()); +} + +void Map::RemoveInfiniteAOIVignette(Vignettes::VignetteData* vignette) +{ + if (!std::erase(_infiniteAOIVignettes, vignette)) + return; + + WorldPackets::Vignette::VignetteUpdate vignetteUpdate; + vignetteUpdate.Removed.push_back(vignette->Guid); + vignetteUpdate.Write(); + + if (vignette->Data->GetFlags().HasFlag(VignetteFlags::ZoneInfiniteAOI)) + { + for (MapReference const& ref : m_mapRefManager) + if (ref.GetSource()->GetZoneId() == vignette->ZoneID) + ref.GetSource()->SendDirectMessage(vignetteUpdate.GetRawPacket()); + } + else + SendToPlayers(vignetteUpdate.GetRawPacket()); +} + template<class T> void Map::InitializeObject(T* /*obj*/) { } @@ -774,6 +808,24 @@ void Map::Update(uint32 t_diff) obj->Update(t_diff); } + if (_vignetteUpdateTimer.Update(t_diff)) + { + for (Vignettes::VignetteData* vignette : _infiniteAOIVignettes) + { + if (vignette->NeedUpdate) + { + WorldPackets::Vignette::VignetteUpdate vignetteUpdate; + vignette->FillPacket(vignetteUpdate.Updated); + vignetteUpdate.Write(); + for (MapReference const& ref : m_mapRefManager) + if (Vignettes::CanSee(ref.GetSource(), *vignette)) + ref.GetSource()->SendDirectMessage(vignetteUpdate.GetRawPacket()); + + vignette->NeedUpdate = false; + } + } + } + SendObjectUpdates(); ///- Process necessary scripts diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 5763fce777d..78a8cb3e647 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -78,6 +78,7 @@ enum WeatherState : uint32; enum class ItemContext : uint8; namespace Trinity { struct ObjectUpdater; } +namespace Vignettes { struct VignetteData; } namespace VMAP { enum class ModelIgnoreFlags : uint32; } enum TransferAbortReason : uint32 @@ -821,6 +822,18 @@ class TC_GAME_API Map : public GridRefManager<NGridType> private: WorldStateValueContainer _worldStateValues; + + /*********************************************************/ + /*** Vignettes ***/ + /*********************************************************/ + public: + void AddInfiniteAOIVignette(Vignettes::VignetteData* vignette); + void RemoveInfiniteAOIVignette(Vignettes::VignetteData* vignette); + std::vector<Vignettes::VignetteData*> const& GetInfiniteAOIVignettes() const { return _infiniteAOIVignettes; } + + private: + std::vector<Vignettes::VignetteData*> _infiniteAOIVignettes; + PeriodicTimer _vignetteUpdateTimer; }; enum class InstanceResetMethod : uint8 diff --git a/src/server/game/Server/Packets/VignettePackets.cpp b/src/server/game/Server/Packets/VignettePackets.cpp new file mode 100644 index 00000000000..10ca888ccbc --- /dev/null +++ b/src/server/game/Server/Packets/VignettePackets.cpp @@ -0,0 +1,59 @@ +/* + * 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 "VignettePackets.h" + +namespace WorldPackets::Vignette +{ +ByteBuffer& operator<<(ByteBuffer& data, VignetteData const& vignetteData) +{ + data << vignetteData.Position; + data << vignetteData.ObjGUID; + data << int32(vignetteData.VignetteID); + data << uint32(vignetteData.ZoneID); + data << uint32(vignetteData.WMOGroupID); + data << uint32(vignetteData.WMODoodadPlacementID); + + return data; +} + +ByteBuffer& operator<<(ByteBuffer& data, VignetteDataSet const& vignetteDataSet) +{ + data << uint32(vignetteDataSet.IDs.size()); + data << uint32(vignetteDataSet.Data.size()); + for (ObjectGuid const& id : vignetteDataSet.IDs) + data << id; + + for (VignetteData const& vignetteData : vignetteDataSet.Data) + data << vignetteData; + + return data; +} + +WorldPacket const* VignetteUpdate::Write() +{ + _worldPacket.WriteBit(ForceUpdate); + _worldPacket.WriteBit(InFogOfWar); + _worldPacket << uint32(Removed.size()); + _worldPacket << Added; + _worldPacket << Updated; + for (ObjectGuid const& removed : Removed) + _worldPacket << removed; + + return &_worldPacket; +} +} diff --git a/src/server/game/Server/Packets/VignettePackets.h b/src/server/game/Server/Packets/VignettePackets.h new file mode 100644 index 00000000000..c96f68262df --- /dev/null +++ b/src/server/game/Server/Packets/VignettePackets.h @@ -0,0 +1,58 @@ +/* + * 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_PACKETS_H +#define TRINITYCORE_VIGNETTE_PACKETS_H + +#include "ObjectGuid.h" +#include "Packet.h" +#include "Position.h" + +namespace WorldPackets::Vignette +{ +struct VignetteData +{ + ObjectGuid ObjGUID; + TaggedPosition<Position::XYZ> Position; + int32 VignetteID = 0; + uint32 ZoneID = 0; + uint32 WMOGroupID = 0; + uint32 WMODoodadPlacementID = 0; +}; + +struct VignetteDataSet +{ + std::vector<ObjectGuid> IDs; + std::vector<VignetteData> Data; +}; + +class VignetteUpdate final : public ServerPacket +{ +public: + VignetteUpdate() : ServerPacket(SMSG_VIGNETTE_UPDATE, 200) { } + + WorldPacket const* Write() override; + + VignetteDataSet Added; + VignetteDataSet Updated; + std::vector<ObjectGuid> Removed; + bool ForceUpdate = false; + bool InFogOfWar = false; +}; +} + +#endif // TRINITYCORE_VIGNETTE_PACKETS_H diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 80a289eb9c4..613b4e1f970 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -2145,7 +2145,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_VAS_PURCHASE_COMPLETE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_VAS_PURCHASE_STATE_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_VENDOR_INVENTORY, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_VIGNETTE_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_VIGNETTE_UPDATE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_VOICE_CHANNEL_INFO_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_VOICE_CHANNEL_STT_TOKEN_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_VOICE_LOGIN_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 65894bf3ec7..f21ebb30e61 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -334,7 +334,7 @@ NonDefaultConstructible<pAuraEffectHandler> AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleNoImmediateEffect, //263 SPELL_AURA_DISABLE_CASTING_EXCEPT_ABILITIES implemented in Spell::CheckCast &AuraEffect::HandleNoImmediateEffect, //264 SPELL_AURA_DISABLE_ATTACKING_EXCEPT_ABILITIES implemented in Spell::CheckCast, Unit::AttackerStateUpdate &AuraEffect::HandleUnused, //265 unused (4.3.4) - &AuraEffect::HandleNULL, //266 SPELL_AURA_SET_VIGNETTE + &AuraEffect::HandleSetVignette, //266 SPELL_AURA_SET_VIGNETTE &AuraEffect::HandleNoImmediateEffect, //267 SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL implemented in Unit::IsImmunedToSpellEffect &AuraEffect::HandleModArmorPctFromStat, //268 SPELL_AURA_MOD_ARMOR_PCT_FROM_STAT also implemented in Player::UpdateArmor() &AuraEffect::HandleNoImmediateEffect, //269 SPELL_AURA_MOD_IGNORE_TARGET_RESIST implemented in Unit::CalcAbsorbResist and CalcArmorReducedDamage @@ -5322,6 +5322,14 @@ void AuraEffect::HandleAuraSetVehicle(AuraApplication const* aurApp, uint8 mode, target->ToPlayer()->SendOnCancelExpectedVehicleRideAura(); } +void AuraEffect::HandleSetVignette(AuraApplication const* aurApp, uint8 mode, bool apply) const +{ + if (!(mode & AURA_EFFECT_HANDLE_REAL)) + return; + + aurApp->GetTarget()->SetVignette(apply ? GetMiscValue() : 0); +} + void AuraEffect::HandlePreventResurrection(AuraApplication const* aurApp, uint8 mode, bool apply) const { if (!(mode & AURA_EFFECT_HANDLE_REAL)) diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h index 8260d96f621..810af0fc8a7 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.h +++ b/src/server/game/Spells/Auras/SpellAuraEffects.h @@ -314,6 +314,7 @@ class TC_GAME_API AuraEffect void HandleAuraModFakeInebriation(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleAuraOverrideSpells(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleAuraSetVehicle(AuraApplication const* aurApp, uint8 mode, bool apply) const; + void HandleSetVignette(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandlePreventResurrection(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleMastery(AuraApplication const* aurApp, uint8 mode, bool apply) const; void HandleAuraForceWeather(AuraApplication const* aurApp, uint8 mode, bool apply) const; |