TrinityCore
Loading...
Searching...
No Matches
Conversation.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "Conversation.h"
19#include "ConditionMgr.h"
20#include "ConversationAI.h"
22#include "Creature.h"
23#include "CreatureAISelector.h"
24#include "DB2Stores.h"
25#include "IteratorPair.h"
26#include "Log.h"
27#include "Map.h"
28#include "MapUtils.h"
29#include "ObjectAccessor.h"
30#include "PhasingHandler.h"
31#include "Player.h"
32#include "UpdateData.h"
33#include "WorldPacket.h"
34#include "WorldSession.h"
35
36Conversation::Conversation() : WorldObject(false), _duration(0), _textureKitId(0)
37{
39
42
44
45 _lastLineEndTimes.fill(Milliseconds::zero());
46}
47
49
59
61{
63 if (IsInWorld())
64 {
65 _ai->OnRemove();
66
69 }
70}
71
73{
74 _ai->OnUpdate(diff);
75
76 if (GetDuration() > Milliseconds(diff))
77 {
78 _duration -= Milliseconds(diff);
80 {
81 // Only sent in CreateObject
83 const_cast<UF::ConversationData&>(*m_conversationData).ClearChanged(&UF::ConversationData::Progress);
84 });
85 }
86 else
87 {
88 Remove(); // expired
89 return;
90 }
91
93}
94
96{
97 if (IsInWorld())
98 {
99 AddObjectToRemoveList(); // calls RemoveFromWorld
100 }
101}
102
103Conversation* Conversation::CreateConversation(uint32 conversationEntry, Unit* creator, Position const& pos, ObjectGuid privateObjectOwner, SpellInfo const* spellInfo /*= nullptr*/, bool autoStart /*= true*/)
104{
105 ConversationTemplate const* conversationTemplate = sConversationDataStore->GetConversationTemplate(conversationEntry);
106 if (!conversationTemplate)
107 return nullptr;
108
110
111 Conversation* conversation = new Conversation();
112 conversation->Create(lowGuid, conversationEntry, creator->GetMap(), creator, pos, privateObjectOwner, spellInfo);
113 if (autoStart && !conversation->Start())
114 {
115 delete conversation;
116 return nullptr;
117 }
118
119 return conversation;
120}
121
123{
124 explicit ConversationActorFillVisitor(Conversation* conversation, Unit const* creator, Map const* map, ConversationActorTemplate const& actor)
125 : _conversation(conversation), _creator(creator), _map(map), _actor(actor)
126 {
127 }
128
130 {
131 Creature const* bestFit = nullptr;
132
133 for (auto const& [_, creature] : Trinity::Containers::MapEqualRange(_map->GetCreatureBySpawnIdStore(), worldObject.SpawnId))
134 {
135 bestFit = creature;
136
137 // If creature is in a personal phase then we pick that one specifically
138 if (creature->GetPhaseShift().GetPersonalGuid() == _creator->GetGUID())
139 break;
140 }
141
142 if (bestFit)
144 }
145
150
151 void operator()([[maybe_unused]] ConversationActorActivePlayerTemplate const& activePlayer) const
152 {
153 _conversation->AddActor(_actor.Id, _actor.Index, ObjectGuid::Create<HighGuid::Player>(0xFFFFFFFFFFFFFFFF));
154 }
155
160
161private:
164 ::Map const* _map;
166};
167
168void Conversation::Create(ObjectGuid::LowType lowGuid, uint32 conversationEntry, Map* map, Unit* creator, Position const& pos, ObjectGuid privateObjectOwner, SpellInfo const* /*spellInfo = nullptr*/)
169{
170 ConversationTemplate const* conversationTemplate = sConversationDataStore->GetConversationTemplate(conversationEntry);
171 ASSERT(conversationTemplate);
172
173 _creatorGuid = creator->GetGUID();
174 SetPrivateObjectOwner(privateObjectOwner);
175
176 SetMap(map);
177 Relocate(pos);
179
180 _Create(ObjectGuid::Create<HighGuid::Conversation>(GetMapId(), conversationEntry, lowGuid));
182
185
186 SetEntry(conversationEntry);
187 SetObjectScale(1.0f);
188
190
191 _textureKitId = conversationTemplate->TextureKitId;
192
193 for (ConversationActorTemplate const& actor : conversationTemplate->Actors)
194 std::visit(ConversationActorFillVisitor(this, creator, map, actor), actor.Data);
195
196 std::vector<UF::ConversationLine> lines;
197 for (ConversationLineTemplate const* line : conversationTemplate->Lines)
198 {
199 if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_CONVERSATION_LINE, line->Id, creator))
200 continue;
201
202 ConversationLineEntry const* convoLine = sConversationLineStore.LookupEntry(line->Id); // never null for conversationTemplate->Lines
203
204 UF::ConversationLine& lineField = lines.emplace_back();
205 lineField.ConversationLineID = line->Id;
206 lineField.BroadcastTextID = convoLine->BroadcastTextID;
207 lineField.UiCameraID = line->UiCameraID;
208 lineField.ActorIndex = line->ActorIdx;
209 lineField.Flags = line->Flags;
210 lineField.ChatType = line->ChatType;
211
212 std::array<Milliseconds, TOTAL_LOCALES>& startTimes = _lineStartTimes[line->Id];
213 for (LocaleConstant locale = LOCALE_enUS; locale < TOTAL_LOCALES; locale = LocaleConstant(locale + 1))
214 {
215 if (locale == LOCALE_none)
216 continue;
217
218 startTimes[locale] = _lastLineEndTimes[locale];
219 if (locale == DEFAULT_LOCALE)
220 lineField.StartTime = _lastLineEndTimes[locale].count();
221
222 if (int32 const* broadcastTextDuration = sDB2Manager.GetBroadcastTextDuration(convoLine->BroadcastTextID, locale))
223 _lastLineEndTimes[locale] += Milliseconds(*broadcastTextDuration);
224
226 }
227 }
228
229 _duration = *std::ranges::max_element(_lastLineEndTimes);
232
233 // conversations are despawned 5-20s after LastLineEndTime
234 _duration += 10s;
235
236 _ai->OnCreate(creator);
237}
238
240{
241 ConversationTemplate const* conversationTemplate = sConversationDataStore->GetConversationTemplate(GetEntry()); // never null, already checked in ::Create / ::CreateConversation
243 {
244 for (UF::ConversationLine const& line : *m_conversationData->Lines)
245 {
246 UF::ConversationActor const* actor = line.ActorIndex < m_conversationData->Actors.size() ? &m_conversationData->Actors[line.ActorIndex] : nullptr;
247 if (!actor || (!actor->CreatureID && actor->ActorGUID.IsEmpty() && !actor->NoActorObject))
248 {
249 TC_LOG_ERROR("entities.conversation", "Failed to create conversation (Id: {}) due to missing actor (Idx: {}).", GetEntry(), line.ActorIndex);
250 return false;
251 }
252 }
253 }
254
255 if (IsInWorld())
256 {
257 TC_LOG_ERROR("entities.conversation", "Attempted to start conversation (Id: {}) multiple times.", GetEntry());
258 return true; // returning true to not cause delete in Conversation::CreateConversation if convo is already started in ConversationScript::OnConversationCreate
259 }
260
261 if (!GetMap()->AddToMap(this))
262 return false;
263
264 _ai->OnStart();
265 return true;
266}
267
268void Conversation::AddActor(int32 actorId, uint32 actorIdx, ObjectGuid const& actorGuid)
269{
270 auto actorField = m_values.ModifyValue(&Conversation::m_conversationData).ModifyValue(&UF::ConversationData::Actors, actorIdx);
271 SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::CreatureID), 0);
273 SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::ActorGUID), actorGuid);
274 SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::Id), actorId);
277}
278
279void Conversation::AddActor(int32 actorId, uint32 actorIdx, ConversationActorType type, uint32 creatureId, uint32 creatureDisplayInfoId)
280{
281 auto actorField = m_values.ModifyValue(&Conversation::m_conversationData).ModifyValue(&UF::ConversationData::Actors, actorIdx);
282 SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::CreatureID), creatureId);
283 SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::CreatureDisplayInfoID), creatureDisplayInfoId);
285 SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::Id), actorId);
288}
289
291{
292 if (std::array<Milliseconds, TOTAL_LOCALES> const* durations = Trinity::Containers::MapGetValuePtr(_lineStartTimes, lineId))
293 return &(*durations)[locale];
294
295 return nullptr;
296}
297
302
304{
305 ConversationLineEntry const* convoLine = sConversationLineStore.LookupEntry(lineId);
306 if (!convoLine)
307 {
308 TC_LOG_ERROR("entities.conversation", "Conversation::GetLineDuration: Tried to get duration for invalid ConversationLine id {}.", lineId);
309 return 0;
310 }
311
312 int32 const* textDuration = sDB2Manager.GetBroadcastTextDuration(convoLine->BroadcastTextID, locale);
313 if (!textDuration)
314 return 0;
315
316 return *textDuration + convoLine->AdditionalDuration;
317}
318
320{
321 Milliseconds const* lineStartTime = GetLineStartTime(locale, lineId);
322 if (!lineStartTime)
323 {
324 TC_LOG_ERROR("entities.conversation", "Conversation::GetLineEndTime: Unable to get line start time for locale {}, lineid {} (Conversation ID: {}).", locale, lineId, GetEntry());
325 return Milliseconds(0);
326 }
327 return *lineStartTime + Milliseconds(GetLineDuration(locale, lineId));
328}
329
331{
332 LocaleConstant privateOwnerLocale = LOCALE_enUS;
334 privateOwnerLocale = owner->GetSession()->GetSessionDbLocaleIndex();
335 return privateOwnerLocale;
336}
337
339{
340 if (m_conversationData->Actors.size() <= actorIdx)
341 {
342 TC_LOG_ERROR("entities.conversation", "Conversation::GetActorUnit: Tried to access invalid actor idx {} (Conversation ID: {}).", actorIdx, GetEntry());
343 return nullptr;
344 }
345 return ObjectAccessor::GetUnit(*this, m_conversationData->Actors[actorIdx].ActorGUID);
346}
347
349{
350 Unit* actor = GetActorUnit(actorIdx);
351 if (!actor)
352 return nullptr;
353 return actor->ToCreature();
354}
355
357{
358 AI_Destroy();
360 _ai->OnInitialize();
361}
362
364{
365 _ai.reset();
366}
367
369{
370 return sConversationDataStore->GetConversationTemplate(GetEntry())->ScriptId;
371}
372
374{
375 m_objectData->WriteCreate(flags, data, target, this);
376 m_conversationData->WriteCreate(flags, data, target, this);
377}
378
380{
382
384 m_objectData->WriteUpdate(flags, data, target, this);
385
387 m_conversationData->WriteUpdate(flags, data, target, this);
388}
389
391 UF::ConversationData::Mask const& requestedConversationMask, Player const* target, bool ignoreNestedChangesMask) const
392{
395 if (requestedObjectMask.IsAnySet())
396 valuesMask.Set(TYPEID_OBJECT);
397
398 if (requestedConversationMask.IsAnySet())
399 valuesMask.Set(TYPEID_CONVERSATION);
400
402 std::size_t sizePos = buffer.wpos();
403 buffer << uint32(0);
405 buffer << uint32(valuesMask.GetBlock(0));
406
407 if (valuesMask[TYPEID_OBJECT])
408 m_objectData->WriteUpdate(requestedObjectMask, buffer, target, this, ignoreNestedChangesMask);
409
410 if (valuesMask[TYPEID_CONVERSATION])
411 m_conversationData->WriteUpdate(requestedConversationMask, buffer, target, this, ignoreNestedChangesMask);
412
413 buffer.put<uint32>(sizePos, buffer.wpos() - sizePos - 4);
414
415 data->AddUpdateBlock();
416}
417
428
LocaleConstant
Definition Common.h:51
@ LOCALE_none
Definition Common.h:61
@ TOTAL_LOCALES
Definition Common.h:65
@ LOCALE_enUS
Definition Common.h:52
#define DEFAULT_LOCALE
Definition Common.h:69
#define sConditionMgr
@ CONDITION_SOURCE_TYPE_CONVERSATION_LINE
#define sConversationDataStore
ConversationActorType
DB2Storage< ConversationLineEntry > sConversationLineStore("ConversationLine.db2", &ConversationLineLoadInfo::Instance)
#define sDB2Manager
Definition DB2Stores.h:569
int32_t int32
Definition Define.h:150
uint32_t uint32
Definition Define.h:154
uint16 flags
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition Duration.h:24
#define ASSERT
Definition Errors.h:80
#define TC_LOG_ERROR(filterType__, message__,...)
Definition Log.h:190
@ TYPEID_OBJECT
Definition ObjectGuid.h:38
@ TYPEID_CONVERSATION
Definition ObjectGuid.h:51
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition Util.h:565
ObjectGuid const & GetGUID() const
Definition BaseEntity.h:163
void SetUpdateFieldValue(UF::UpdateFieldPrivateSetter< T > setter, typename UF::UpdateFieldPrivateSetter< T >::value_type value)
Definition BaseEntity.h:221
WowCS::EntityFragmentsHolder m_entityFragments
Definition BaseEntity.h:353
UF::UpdateFieldHolder m_values
Definition BaseEntity.h:205
bool IsInWorld() const
Definition BaseEntity.h:158
void _Create(ObjectGuid const &guid)
Definition BaseEntity.h:218
CreateObjectBits m_updateFlag
Definition BaseEntity.h:352
void ApplyModUpdateFieldValue(UF::UpdateFieldPrivateSetter< T > setter, typename UF::UpdateFieldPrivateSetter< T >::value_type mod, bool apply)
Definition BaseEntity.h:306
ByteBuffer & PrepareValuesUpdateBuffer(UpdateData *data) const
void DoWithSuppressingObjectUpdates(Action &&action)
Definition BaseEntity.h:336
TypeID m_objectTypeId
Definition BaseEntity.h:351
virtual UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const *target) const
size_t wpos() const
Definition ByteBuffer.h:461
void put(std::size_t pos, T value)
Definition ByteBuffer.h:260
uint32 GetScriptId() const
void ClearValuesChangesMask() override
void AddToWorld() override
void RelocateStationaryPosition(Position const &pos)
Milliseconds GetLineEndTime(LocaleConstant locale, int32 lineId) const
std::unique_ptr< ConversationAI > _ai
static int32 GetLineDuration(LocaleConstant locale, int32 lineId)
void AddActor(int32 actorId, uint32 actorIdx, ObjectGuid const &actorGuid)
std::array< Milliseconds, TOTAL_LOCALES > _lastLineEndTimes
void BuildValuesUpdate(UF::UpdateFieldFlag flags, ByteBuffer &data, Player const *target) const override
Milliseconds _duration
Milliseconds GetDuration() const
LocaleConstant GetPrivateObjectOwnerLocale() const
void Create(ObjectGuid::LowType lowGuid, uint32 conversationEntry, Map *map, Unit *creator, Position const &pos, ObjectGuid privateObjectOwner, SpellInfo const *spellInfo=nullptr)
static Conversation * CreateConversation(uint32 conversationEntry, Unit *creator, Position const &pos, ObjectGuid privateObjectOwner, SpellInfo const *spellInfo=nullptr, bool autoStart=true)
Milliseconds const * GetLineStartTime(LocaleConstant locale, int32 lineId) const
uint32 _textureKitId
void BuildValuesUpdateForPlayerWithMask(UpdateData *data, UF::ObjectData::Mask const &requestedObjectMask, UF::ConversationData::Mask const &requestedConversationMask, Player const *target, bool ignoreNestedChangesMask) const
Milliseconds GetLastLineEndTime(LocaleConstant locale) const
void BuildValuesCreate(UF::UpdateFieldFlag flags, ByteBuffer &data, Player const *target) const override
Unit * GetActorUnit(uint32 actorIdx) const
void Update(uint32 diff) override
ObjectGuid _creatorGuid
std::unordered_map< int32, std::array< Milliseconds, TOTAL_LOCALES > > _lineStartTimes
UF::UpdateField< UF::ConversationData, int32(WowCS::EntityFragment::CGObject), TYPEID_CONVERSATION > m_conversationData
void RemoveFromWorld() override
Creature * GetActorCreature(uint32 actorIdx) const
constexpr bool HasFlag(T flag) const
Definition EnumFlag.h:106
Definition Map.h:225
MapStoredObjectTypesContainer & GetObjectsStore()
Definition Map.h:458
ObjectGuid::LowType GenerateLowGuid()
Definition Map.h:558
CreatureBySpawnIdContainer & GetCreatureBySpawnIdStore()
Definition Map.h:461
static ObjectGuid const Empty
Definition ObjectGuid.h:314
bool IsEmpty() const
Definition ObjectGuid.h:362
uint64 LowType
Definition ObjectGuid.h:321
uint32 GetEntry() const
Definition Object.h:89
Creature * ToCreature()
Definition Object.h:121
void BuildEntityFragmentsForValuesUpdateForPlayerWithMask(ByteBuffer &data, EnumFlag< UF::UpdateFieldFlag > flags) const
Definition Object.cpp:113
void SetEntry(uint32 entry)
Definition Object.h:90
virtual void ClearValuesChangesMask()
Definition Object.cpp:130
virtual void SetObjectScale(float scale)
Definition Object.h:93
UF::UpdateField< UF::ObjectData, int32(WowCS::EntityFragment::CGObject), TYPEID_OBJECT > m_objectData
Definition Object.h:161
static void InheritPhaseShift(WorldObject *target, WorldObject const *source)
void SendDirectMessage(WorldPacket const *data) const
Definition Player.cpp:6283
void ClearChanged(UpdateField< T, BlockBit, Bit >(Derived::*))
Mask const & GetChangesMask() const
MutableFieldReference< T, false > ModifyValue(UpdateField< T, BlockBit, Bit >(Derived::*field))
void ClearChangesMask(UpdateField< T, BlockBit, Bit >(Derived::*field))
bool HasChanged(uint32 index) const
Definition BaseEntity.h:83
uint32 GetChangedObjectTypeMask() const
Definition BaseEntity.h:81
Definition Unit.h:635
bool BuildPacket(WorldPacket *packet)
void AddUpdateBlock()
Definition UpdateData.h:46
constexpr uint32 GetBlock(uint32 index) const
Definition UpdateMask.h:59
constexpr void Set(uint32 index)
Definition UpdateMask.h:91
constexpr uint32 GetMapId() const
Definition Position.h:216
Map * GetMap() const
Definition Object.h:411
void AddToWorld() override
Definition Object.cpp:365
void RemoveFromWorld() override
Definition Object.cpp:371
ObjectGuid GetPrivateObjectOwner() const
Definition Object.h:569
void SetZoneScript()
Definition Object.cpp:1384
void SetPrivateObjectOwner(ObjectGuid const &owner)
Definition Object.h:570
virtual void SetMap(Map *map)
Definition Object.cpp:1144
void AddObjectToRemoveList()
Definition Object.cpp:1174
void UpdatePositionData()
Definition Object.cpp:346
virtual void Update(uint32 diff)
Definition Object.cpp:245
ConversationAI * SelectConversationAI(Conversation *conversation)
TC_GAME_API Unit * GetUnit(WorldObject const &, ObjectGuid const &guid)
TC_GAME_API Player * GetPlayer(Map const *, ObjectGuid const &guid)
auto MapEqualRange(M &map, typename M::key_type const &key)
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition MapUtils.h:37
UpdateFieldFlag
Definition UpdateField.h:37
void operator()(ConversationActorActivePlayerTemplate const &activePlayer) const
ConversationActorTemplate const & _actor
ConversationActorFillVisitor(Conversation *conversation, Unit const *creator, Map const *map, ConversationActorTemplate const &actor)
void operator()(ConversationActorWorldObjectTemplate const &worldObject) const
void operator()(ConversationActorTalkingHeadTemplate const &talkingHead) const
void operator()(ConversationActorNoObjectTemplate const &noObject) const
EnumFlag< ConversationFlags > Flags
std::vector< ConversationLineTemplate const * > Lines
std::vector< ConversationActorTemplate > Actors
void operator()(Player const *player) const
constexpr void Relocate(float x, float y)
Definition Position.h:74
bool Insert(ValueType< ObjectType > object)
bool Remove(ValueType< ObjectType > object)
UpdateField< int32, 0, 5 > LastLineEndTime
UpdateField< uint32, 0, 6 > Progress
UpdateField< std::vector< UF::ConversationLine >, 0, 3 > Lines
DynamicUpdateField< UF::ConversationActor, 0, 4 > Actors
void Add(EntityFragment fragment, bool update, void const *data=nullptr)