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
This commit is contained in:
Shauren
2022-04-19 22:15:12 +02:00
parent 428d8dc3b7
commit fda65981c7
6 changed files with 227 additions and 92 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
struct ConversationActorDbRow
{
uint32 ConversationId = 0;
uint32 ActorIndex = 0;
ObjectGuid::LowType SpawnId = 0;
uint32 CreatureId = 0;
uint32 CreatureDisplayInfoId = 0;
bool operator()(ConversationActorWorldObjectTemplate& worldObject) const
{
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;
}
bool operator()(ConversationActorNoObjectTemplate& noObject) const
{
if (!sObjectMgr->GetCreatureTemplate(CreatureId))
{
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;
}
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;
}
bool operator()([[maybe_unused]] ConversationActorActivePlayerTemplate& activePlayer) const
{
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))
{
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;
}
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;
}
};
do
{
Field* fields = actors->Fetch();
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();
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 (creatureId != 0 && actorGuid != 0)
{
TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references both actor (ID: %u) and actorGuid (GUID: " UI64FMTD ") for Conversation %u, skipped.", actorId, actorGuid, conversationId);
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;
}
if (creatureId != 0)
{
if (creatureDisplayInfoId != 0)
{
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;
}
else
TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an actor (CreatureId: %u) without CreatureDisplayInfoId for Conversation %u, skipped", creatureId, conversationId);
}
else if (actorGuid != 0)
{
if (sObjectMgr->GetCreatureData(actorGuid))
{
std::vector<ObjectGuid::LowType>& guids = actorGuidsByConversation[conversationId];
if (guids.size() <= idx)
guids.resize(idx + 1);
guids[idx] = actorGuid;
++count;
}
else
TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid creature guid (GUID: " UI64FMTD ") for Conversation %u, skipped", actorGuid, conversationId);
}
}
while (actors->NextRow());
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)

View File

@@ -20,31 +20,58 @@
#include "Define.h"
#include "ObjectGuid.h"
#include <variant>
#include <vector>
enum class ConversationActorType : uint32
{
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 ConversationActorTemplate
{
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
};
struct ConversationActor
{
uint32 ActorId;
uint32 CreatureId;
uint32 CreatureDisplayInfoId;
};
#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;

View File

@@ -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);
}