diff options
author | Shauren <shauren.trinity@gmail.com> | 2022-04-19 22:15:12 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2022-04-19 22:15:12 +0200 |
commit | fda65981c7a4c475c270755c198dee85cd38dfa3 (patch) | |
tree | 6cd326a91d21689252d42d7e7770b0840d843ac1 | |
parent | 428d8dc3b7616f7bb9d39c4b6343c5eec4e3c965 (diff) |
Core/Conversations: Conversation actor improvements
* Store NoActorObject in database
* Support adding player as conversation actor using database
* Send actor id in packets
Co-authored-by: funjoker <funjoker109@gmail.com>
Closes #27911
-rw-r--r-- | sql/updates/world/master/2022_04_19_00_world.sql | 7 | ||||
-rw-r--r-- | src/server/game/Entities/Conversation/Conversation.cpp | 88 | ||||
-rw-r--r-- | src/server/game/Entities/Conversation/Conversation.h | 10 | ||||
-rw-r--r-- | src/server/game/Globals/ConversationDataStore.cpp | 145 | ||||
-rw-r--r-- | src/server/game/Globals/ConversationDataStore.h | 50 | ||||
-rw-r--r-- | src/server/scripts/World/conversation_scripts.cpp | 3 |
6 files changed, 219 insertions, 84 deletions
diff --git a/sql/updates/world/master/2022_04_19_00_world.sql b/sql/updates/world/master/2022_04_19_00_world.sql new file mode 100644 index 00000000000..e4f80fd062b --- /dev/null +++ b/sql/updates/world/master/2022_04_19_00_world.sql @@ -0,0 +1,7 @@ +ALTER TABLE `conversation_actors` + ADD `NoActorObject` TINYINT(3) UNSIGNED NULL DEFAULT '0' AFTER `CreatureDisplayInfoId`, + ADD `ActivePlayerObject` TINYINT(3) UNSIGNED NULL DEFAULT '0' AFTER `NoActorObject`; + +DELETE FROM `conversation_actors` WHERE `ConversationId`=13254 AND `Idx`=1; +INSERT INTO `conversation_actors` (`ConversationId`,`ConversationActorId`,`ConversationActorGuid`,`Idx`,`CreatureId`,`CreatureDisplayInfoId`,`NoActorObject`,`ActivePlayerObject`,`VerifiedBuild`) VALUES +(13254,0,0,1,0,0,0,1,40120); diff --git a/src/server/game/Entities/Conversation/Conversation.cpp b/src/server/game/Entities/Conversation/Conversation.cpp index b24dc06de70..a6a456ae2e0 100644 --- a/src/server/game/Entities/Conversation/Conversation.cpp +++ b/src/server/game/Entities/Conversation/Conversation.cpp @@ -109,6 +109,52 @@ Conversation* Conversation::CreateConversation(uint32 conversationEntry, Unit* c return conversation; } +struct ConversationActorFillVisitor +{ + explicit ConversationActorFillVisitor(Conversation* conversation, Unit const* creator, Map const* map, ConversationActorTemplate const& actor) + : _conversation(conversation), _creator(creator), _map(map), _actor(actor) + { + } + + void operator()(ConversationActorWorldObjectTemplate const& worldObject) const + { + Creature const* bestFit = nullptr; + + for (auto const& [_, creature] : Trinity::Containers::MapEqualRange(_map->GetCreatureBySpawnIdStore(), worldObject.SpawnId)) + { + bestFit = creature; + + // If creature is in a personal phase then we pick that one specifically + if (creature->GetPhaseShift().GetPersonalGuid() == _creator->GetGUID()) + break; + } + + if (bestFit) + _conversation->AddActor(_actor.Id, _actor.Index, bestFit->GetGUID()); + } + + void operator()(ConversationActorNoObjectTemplate const& noObject) const + { + _conversation->AddActor(_actor.Id, _actor.Index, ConversationActorType::WorldObject, noObject.CreatureId, noObject.CreatureDisplayInfoId); + } + + void operator()([[maybe_unused]] ConversationActorActivePlayerTemplate const& activePlayer) const + { + _conversation->AddActor(_actor.Id, _actor.Index, ObjectGuid::Create<HighGuid::Player>(0xFFFFFFFFFFFFFFFF)); + } + + void operator()(ConversationActorTalkingHeadTemplate const& talkingHead) const + { + _conversation->AddActor(_actor.Id, _actor.Index, ConversationActorType::TalkingHead, talkingHead.CreatureId, talkingHead.CreatureDisplayInfoId); + } + +private: + Conversation* _conversation; + Unit const* _creator; + ::Map const* _map; + ConversationActorTemplate const& _actor; +}; + bool Conversation::Create(ObjectGuid::LowType lowGuid, uint32 conversationEntry, Map* map, Unit* creator, Position const& pos, ObjectGuid privateObjectOwner, SpellInfo const* /*spellInfo = nullptr*/) { ConversationTemplate const* conversationTemplate = sConversationDataStore->GetConversationTemplate(conversationEntry); @@ -129,27 +175,8 @@ bool Conversation::Create(ObjectGuid::LowType lowGuid, uint32 conversationEntry, _textureKitId = conversationTemplate->TextureKitId; - for (ConversationActor const& actor : conversationTemplate->Actors) - { - UF::ConversationActor& actorField = AddDynamicUpdateFieldValue(m_values.ModifyValue(&Conversation::m_conversationData).ModifyValue(&UF::ConversationData::Actors)); - actorField.CreatureID = actor.CreatureId; - actorField.CreatureDisplayInfoID = actor.CreatureDisplayInfoId; - actorField.Id = actor.ActorId; - actorField.Type = AsUnderlyingType(ActorType::CreatureActor); - } - - for (uint16 actorIndex = 0; actorIndex < conversationTemplate->ActorGuids.size(); ++actorIndex) - { - ObjectGuid::LowType actorGuid = conversationTemplate->ActorGuids[actorIndex]; - if (!actorGuid) - continue; - - for (auto const& pair : Trinity::Containers::MapEqualRange(map->GetCreatureBySpawnIdStore(), actorGuid)) - { - // we just need the last one, overriding is legit - AddActor(pair.second->GetGUID(), actorIndex); - } - } + for (ConversationActorTemplate const& actor : conversationTemplate->Actors) + std::visit(ConversationActorFillVisitor(this, creator, map, actor), actor.Data); std::set<uint16> actorIndices; std::vector<UF::ConversationLine> lines; @@ -211,11 +238,26 @@ bool Conversation::Create(ObjectGuid::LowType lowGuid, uint32 conversationEntry, return true; } -void Conversation::AddActor(ObjectGuid const& actorGuid, uint16 actorIdx) +void Conversation::AddActor(int32 actorId, uint32 actorIdx, ObjectGuid const& actorGuid) { auto actorField = m_values.ModifyValue(&Conversation::m_conversationData).ModifyValue(&UF::ConversationData::Actors, actorIdx); + SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::CreatureID), 0); + SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::CreatureDisplayInfoID), 0); SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::ActorGUID), actorGuid); - SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::Type), AsUnderlyingType(ActorType::WorldObjectActor)); + SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::Id), actorId); + SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::Type), AsUnderlyingType(ConversationActorType::WorldObject)); + SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::NoActorObject), 0); +} + +void Conversation::AddActor(int32 actorId, uint32 actorIdx, ConversationActorType type, uint32 creatureId, uint32 creatureDisplayInfoId) +{ + auto actorField = m_values.ModifyValue(&Conversation::m_conversationData).ModifyValue(&UF::ConversationData::Actors, actorIdx); + SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::CreatureID), creatureId); + SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::CreatureDisplayInfoID), creatureDisplayInfoId); + SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::ActorGUID), ObjectGuid::Empty); + SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::Id), actorId); + SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::Type), AsUnderlyingType(type)); + SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::NoActorObject), type == ConversationActorType::WorldObject ? 1 : 0); } Milliseconds const* Conversation::GetLineStartTime(LocaleConstant locale, int32 lineId) const diff --git a/src/server/game/Entities/Conversation/Conversation.h b/src/server/game/Entities/Conversation/Conversation.h index 90ca16acbdd..0e8ae28e3d6 100644 --- a/src/server/game/Entities/Conversation/Conversation.h +++ b/src/server/game/Entities/Conversation/Conversation.h @@ -24,6 +24,7 @@ class Unit; class SpellInfo; +enum class ConversationActorType : uint32; class TC_GAME_API Conversation : public WorldObject, public GridObject<Conversation> { @@ -61,7 +62,8 @@ class TC_GAME_API Conversation : public WorldObject, public GridObject<Conversat static Conversation* CreateConversation(uint32 conversationEntry, Unit* creator, Position const& pos, ObjectGuid privateObjectOwner, SpellInfo const* spellInfo = nullptr); bool Create(ObjectGuid::LowType lowGuid, uint32 conversationEntry, Map* map, Unit* creator, Position const& pos, ObjectGuid privateObjectOwner, SpellInfo const* spellInfo = nullptr); - void AddActor(ObjectGuid const& actorGuid, uint16 actorIdx); + void AddActor(int32 actorId, uint32 actorIdx, ObjectGuid const& actorGuid); + void AddActor(int32 actorId, uint32 actorIdx, ConversationActorType type, uint32 creatureId, uint32 creatureDisplayInfoId); ObjectGuid const& GetCreatorGuid() const { return _creatorGuid; } ObjectGuid GetOwnerGUID() const override { return GetCreatorGuid(); } @@ -80,12 +82,6 @@ class TC_GAME_API Conversation : public WorldObject, public GridObject<Conversat UF::UpdateField<UF::ConversationData, 0, TYPEID_CONVERSATION> m_conversationData; - enum class ActorType - { - WorldObjectActor = 0, - CreatureActor = 1 - }; - private: Position _stationaryPosition; ObjectGuid _creatorGuid; diff --git a/src/server/game/Globals/ConversationDataStore.cpp b/src/server/game/Globals/ConversationDataStore.cpp index 73098184ffb..d445827eaf8 100644 --- a/src/server/game/Globals/ConversationDataStore.cpp +++ b/src/server/game/Globals/ConversationDataStore.cpp @@ -34,8 +34,7 @@ void ConversationDataStore::LoadConversationTemplates() _conversationLineTemplateStore.clear(); _conversationTemplateStore.clear(); - std::unordered_map<uint32, std::vector<ConversationActor>> actorsByConversation; - std::unordered_map<uint32, std::vector<ObjectGuid::LowType>> actorGuidsByConversation; + std::unordered_map<uint32, std::vector<ConversationActorTemplate>> actorsByConversation; if (QueryResult lineTemplates = WorldDatabase.Query("SELECT Id, UiCameraID, ActorIdx, Flags FROM conversation_line_template")) { @@ -68,61 +67,128 @@ void ConversationDataStore::LoadConversationTemplates() TC_LOG_INFO("server.loading", ">> Loaded 0 Conversation line templates. DB table `conversation_line_template` is empty."); } - if (QueryResult actors = WorldDatabase.Query("SELECT ConversationId, ConversationActorId, ConversationActorGuid, Idx, CreatureId, CreatureDisplayInfoId FROM conversation_actors")) + if (QueryResult actors = WorldDatabase.Query("SELECT ConversationId, ConversationActorId, ConversationActorGuid, Idx, CreatureId, CreatureDisplayInfoId, NoActorObject, ActivePlayerObject FROM conversation_actors")) { uint32 oldMSTime = getMSTime(); uint32 count = 0; - do + struct ConversationActorDbRow { - Field* fields = actors->Fetch(); + uint32 ConversationId = 0; + uint32 ActorIndex = 0; - uint32 conversationId = fields[0].GetUInt32(); - uint32 actorId = fields[1].GetUInt32(); - ObjectGuid::LowType actorGuid = fields[2].GetUInt64(); - uint16 idx = fields[3].GetUInt16(); - uint32 creatureId = fields[4].GetUInt32(); - uint32 creatureDisplayInfoId = fields[5].GetUInt32(); + ObjectGuid::LowType SpawnId = 0; + uint32 CreatureId = 0; + uint32 CreatureDisplayInfoId = 0; - if (creatureId != 0 && actorGuid != 0) + bool operator()(ConversationActorWorldObjectTemplate& worldObject) const { - TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references both actor (ID: %u) and actorGuid (GUID: " UI64FMTD ") for Conversation %u, skipped.", actorId, actorGuid, conversationId); - continue; + if (!sObjectMgr->GetCreatureData(SpawnId)) + { + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid creature guid (GUID: " UI64FMTD ") for Conversation %u and Idx %u, skipped.", SpawnId, ConversationId, ActorIndex); + return false; + } + + if (CreatureId) + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with ConversationActorGuid cannot have CreatureId (%u). Conversation %u and Idx %u.", CreatureId, ConversationId, ActorIndex); + + if (CreatureDisplayInfoId) + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with ConversationActorGuid cannot have CreatureDisplayInfoId (%u). Conversation %u and Idx %u.", CreatureDisplayInfoId, ConversationId, ActorIndex); + + worldObject.SpawnId = SpawnId; + return true; } - if (creatureId != 0) + bool operator()(ConversationActorNoObjectTemplate& noObject) const { - if (creatureDisplayInfoId != 0) + if (!sObjectMgr->GetCreatureTemplate(CreatureId)) { - ConversationActor conversationActor; - conversationActor.ActorId = actorId; - conversationActor.CreatureId = creatureId; - conversationActor.CreatureDisplayInfoId = creatureDisplayInfoId; - - std::vector<ConversationActor>& actors = actorsByConversation[conversationId]; - if (actors.size() <= idx) - actors.resize(idx + 1); - actors[idx] = conversationActor; - ++count; + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid creature id (%u) for Conversation %u and Idx %u, skipped.", CreatureId, ConversationId, ActorIndex); + return false; } - else - TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an actor (CreatureId: %u) without CreatureDisplayInfoId for Conversation %u, skipped", creatureId, conversationId); + + if (CreatureDisplayInfoId && !sCreatureDisplayInfoStore.LookupEntry(CreatureDisplayInfoId)) + { + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid creature display id (%u) for Conversation %u and Idx %u, skipped.", CreatureDisplayInfoId, ConversationId, ActorIndex); + return false; + } + + if (SpawnId) + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with NoActorObject cannot have ConversationActorGuid (" UI64FMTD "). Conversation %u and Idx %u.", SpawnId, ConversationId, ActorIndex); + + noObject.CreatureId = CreatureId; + noObject.CreatureDisplayInfoId = CreatureDisplayInfoId; + return true; } - else if (actorGuid != 0) + + bool operator()([[maybe_unused]] ConversationActorActivePlayerTemplate& activePlayer) const { - if (sObjectMgr->GetCreatureData(actorGuid)) + if (SpawnId) + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with ActivePlayerObject cannot have ConversationActorGuid (" UI64FMTD "). Conversation %u and Idx %u.", SpawnId, ConversationId, ActorIndex); + + if (CreatureId) + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with ActivePlayerObject cannot have CreatureId (%u). Conversation %u and Idx %u.", CreatureId, ConversationId, ActorIndex); + + if (CreatureDisplayInfoId) + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with ActivePlayerObject cannot have CreatureDisplayInfoId (%u). Conversation %u and Idx %u.", CreatureDisplayInfoId, ConversationId, ActorIndex); + + return true; + } + + bool operator()(ConversationActorTalkingHeadTemplate& talkingHead) const + { + if (!sObjectMgr->GetCreatureTemplate(CreatureId)) { - std::vector<ObjectGuid::LowType>& guids = actorGuidsByConversation[conversationId]; - if (guids.size() <= idx) - guids.resize(idx + 1); - guids[idx] = actorGuid; - ++count; + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid creature id (%u) for Conversation %u and Idx %u, skipped.", CreatureId, ConversationId, ActorIndex); + return false; } - else - TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid creature guid (GUID: " UI64FMTD ") for Conversation %u, skipped", actorGuid, conversationId); + + if (CreatureDisplayInfoId && !sCreatureDisplayInfoStore.LookupEntry(CreatureDisplayInfoId)) + { + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid creature display id (%u) for Conversation %u and Idx %u, skipped.", CreatureDisplayInfoId, ConversationId, ActorIndex); + return false; + } + + if (SpawnId) + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` with TalkingHead cannot have ConversationActorGuid (" UI64FMTD "). Conversation %u and Idx %u.", SpawnId, ConversationId, ActorIndex); + + talkingHead.CreatureId = CreatureId; + talkingHead.CreatureDisplayInfoId = CreatureDisplayInfoId; + return true; } - } - while (actors->NextRow()); + }; + + do + { + Field* fields = actors->Fetch(); + + ConversationActorDbRow data; + ConversationActorTemplate actor; + data.ConversationId = fields[0].GetUInt32(); + actor.Id = fields[1].GetUInt32(); + data.SpawnId = fields[2].GetUInt64(); + data.ActorIndex = actor.Index = fields[3].GetUInt16(); + data.CreatureId = fields[4].GetUInt32(); + data.CreatureDisplayInfoId = fields[5].GetUInt32(); + bool noActorObject = fields[6].GetUInt8() == 1; + bool activePlayerObject = fields[7].GetUInt8() == 1; + + if (activePlayerObject) + actor.Data.emplace<ConversationActorActivePlayerTemplate>(); + else if (noActorObject) + actor.Data.emplace<ConversationActorNoObjectTemplate>(); + else if (data.SpawnId) + actor.Data.emplace<ConversationActorWorldObjectTemplate>(); + else + actor.Data.emplace<ConversationActorTalkingHeadTemplate>(); + + bool valid = std::visit(data, actor.Data); + if (!valid) + continue; + + actorsByConversation[data.ConversationId].push_back(actor); + ++count; + } while (actors->NextRow()); TC_LOG_INFO("server.loading", ">> Loaded %u Conversation actors in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } @@ -146,7 +212,6 @@ void ConversationDataStore::LoadConversationTemplates() conversationTemplate.ScriptId = sObjectMgr->GetScriptId(fields[3].GetString()); conversationTemplate.Actors = std::move(actorsByConversation[conversationTemplate.Id]); - conversationTemplate.ActorGuids = std::move(actorGuidsByConversation[conversationTemplate.Id]); ConversationLineEntry const* currentConversationLine = sConversationLineStore.LookupEntry(conversationTemplate.FirstLineId); if (!currentConversationLine) diff --git a/src/server/game/Globals/ConversationDataStore.h b/src/server/game/Globals/ConversationDataStore.h index f3208153fb5..cd1f81a6969 100644 --- a/src/server/game/Globals/ConversationDataStore.h +++ b/src/server/game/Globals/ConversationDataStore.h @@ -20,31 +20,58 @@ #include "Define.h" #include "ObjectGuid.h" - +#include <variant> #include <vector> -enum ConversationLineFlags +enum class ConversationActorType : uint32 { - CONVERSATION_LINE_FLAG_NOTIFY_STARTED = 0x1 // Client will send CMSG_CONVERSATION_LINE_STARTED when it runs this line + WorldObject = 0, + TalkingHead = 1 +}; + +struct ConversationActorWorldObjectTemplate +{ + ObjectGuid::LowType SpawnId = 0; +}; + +struct ConversationActorNoObjectTemplate +{ + uint32 CreatureId = 0; + uint32 CreatureDisplayInfoId = 0; +}; + +struct ConversationActorActivePlayerTemplate +{ +}; + +struct ConversationActorTalkingHeadTemplate +{ + uint32 CreatureId = 0; + uint32 CreatureDisplayInfoId = 0; }; -struct ConversationActor +struct ConversationActorTemplate { - uint32 ActorId; - uint32 CreatureId; - uint32 CreatureDisplayInfoId; + int32 Id = 0; + uint32 Index = 0; + std::variant<ConversationActorWorldObjectTemplate, + ConversationActorNoObjectTemplate, + ConversationActorActivePlayerTemplate, + ConversationActorTalkingHeadTemplate> Data; +}; + +enum ConversationLineFlags +{ + CONVERSATION_LINE_FLAG_NOTIFY_STARTED = 0x1 // Client will send CMSG_CONVERSATION_LINE_STARTED when it runs this line }; -#pragma pack(push, 1) struct ConversationLineTemplate { uint32 Id; // Link to ConversationLine.db2 uint32 UiCameraID; // Link to UiCamera.db2 uint8 ActorIdx; // Index from conversation_actors uint8 Flags; - uint16 Padding; }; -#pragma pack(pop) struct ConversationTemplate { @@ -52,8 +79,7 @@ struct ConversationTemplate uint32 FirstLineId; // Link to ConversationLine.db2 uint32 TextureKitId; // Background texture - std::vector<ConversationActor> Actors; - std::vector<ObjectGuid::LowType> ActorGuids; + std::vector<ConversationActorTemplate> Actors; std::vector<ConversationLineTemplate const*> Lines; uint32 ScriptId; diff --git a/src/server/scripts/World/conversation_scripts.cpp b/src/server/scripts/World/conversation_scripts.cpp index 0c6387a1f8d..507da020e79 100644 --- a/src/server/scripts/World/conversation_scripts.cpp +++ b/src/server/scripts/World/conversation_scripts.cpp @@ -32,9 +32,8 @@ public: conversation_allied_race_dk_defender_of_azeroth() : ConversationScript("conversation_allied_race_dk_defender_of_azeroth") { } - void OnConversationCreate(Conversation* conversation, Unit* creator) override + void OnConversationCreate(Conversation* /*conversation*/, Unit* creator) override { - conversation->AddActor(ObjectGuid::Create<HighGuid::Player>(0xFFFFFFFFFFFFFFFF), 1); if (Player* player = creator->ToPlayer()) player->KilledMonsterCredit(NPC_TALK_TO_YOUR_COMMANDER_CREDIT); } |