aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2022-04-19 22:15:12 +0200
committerShauren <shauren.trinity@gmail.com>2022-04-19 22:15:12 +0200
commitfda65981c7a4c475c270755c198dee85cd38dfa3 (patch)
tree6cd326a91d21689252d42d7e7770b0840d843ac1
parent428d8dc3b7616f7bb9d39c4b6343c5eec4e3c965 (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.sql7
-rw-r--r--src/server/game/Entities/Conversation/Conversation.cpp88
-rw-r--r--src/server/game/Entities/Conversation/Conversation.h10
-rw-r--r--src/server/game/Globals/ConversationDataStore.cpp145
-rw-r--r--src/server/game/Globals/ConversationDataStore.h50
-rw-r--r--src/server/scripts/World/conversation_scripts.cpp3
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);
}