TrinityCore
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 "Containers.h"
22#include "Creature.h"
23#include "DB2Stores.h"
24#include "IteratorPair.h"
25#include "Log.h"
26#include "Map.h"
27#include "ObjectAccessor.h"
28#include "PhasingHandler.h"
29#include "Player.h"
30#include "ScriptMgr.h"
31#include "UpdateData.h"
32#include "WorldSession.h"
33
34Conversation::Conversation() : WorldObject(false), _duration(0), _textureKitId(0)
35{
38
41
42 _lastLineEndTimes.fill(Milliseconds::zero());
43}
44
46
48{
50 if (!IsInWorld())
51 {
54 }
55}
56
58{
60 if (IsInWorld())
61 {
64 }
65}
66
68{
69 sScriptMgr->OnConversationUpdate(this, diff);
70
71 if (GetDuration() > Milliseconds(diff))
72 {
73 _duration -= Milliseconds(diff);
75 {
76 // Only sent in CreateObject
78 const_cast<UF::ConversationData&>(*m_conversationData).ClearChanged(&UF::ConversationData::Progress);
79 });
80 }
81 else
82 {
83 Remove(); // expired
84 return;
85 }
86
88}
89
91{
92 if (IsInWorld())
93 {
94 AddObjectToRemoveList(); // calls RemoveFromWorld
95 }
96}
97
98Conversation* Conversation::CreateConversation(uint32 conversationEntry, Unit* creator, Position const& pos, ObjectGuid privateObjectOwner, SpellInfo const* spellInfo /*= nullptr*/, bool autoStart /*= true*/)
99{
100 ConversationTemplate const* conversationTemplate = sConversationDataStore->GetConversationTemplate(conversationEntry);
101 if (!conversationTemplate)
102 return nullptr;
103
105
106 Conversation* conversation = new Conversation();
107 conversation->Create(lowGuid, conversationEntry, creator->GetMap(), creator, pos, privateObjectOwner, spellInfo);
108 if (autoStart && !conversation->Start())
109 {
110 delete conversation;
111 return nullptr;
112 }
113
114 return conversation;
115}
116
118{
119 explicit ConversationActorFillVisitor(Conversation* conversation, Unit const* creator, Map const* map, ConversationActorTemplate const& actor)
120 : _conversation(conversation), _creator(creator), _map(map), _actor(actor)
121 {
122 }
123
125 {
126 Creature const* bestFit = nullptr;
127
128 for (auto const& [_, creature] : Trinity::Containers::MapEqualRange(_map->GetCreatureBySpawnIdStore(), worldObject.SpawnId))
129 {
130 bestFit = creature;
131
132 // If creature is in a personal phase then we pick that one specifically
133 if (creature->GetPhaseShift().GetPersonalGuid() == _creator->GetGUID())
134 break;
135 }
136
137 if (bestFit)
139 }
140
142 {
144 }
145
146 void operator()([[maybe_unused]] ConversationActorActivePlayerTemplate const& activePlayer) const
147 {
148 _conversation->AddActor(_actor.Id, _actor.Index, ObjectGuid::Create<HighGuid::Player>(0xFFFFFFFFFFFFFFFF));
149 }
150
152 {
154 }
155
156private:
159 ::Map const* _map;
161};
162
163void Conversation::Create(ObjectGuid::LowType lowGuid, uint32 conversationEntry, Map* map, Unit* creator, Position const& pos, ObjectGuid privateObjectOwner, SpellInfo const* /*spellInfo = nullptr*/)
164{
165 ConversationTemplate const* conversationTemplate = sConversationDataStore->GetConversationTemplate(conversationEntry);
166 ASSERT(conversationTemplate);
167
168 _creatorGuid = creator->GetGUID();
169 SetPrivateObjectOwner(privateObjectOwner);
170
171 SetMap(map);
172 Relocate(pos);
174
175 Object::_Create(ObjectGuid::Create<HighGuid::Conversation>(GetMapId(), conversationEntry, lowGuid));
177
180
181 SetEntry(conversationEntry);
182 SetObjectScale(1.0f);
183
184 _textureKitId = conversationTemplate->TextureKitId;
185
186 for (ConversationActorTemplate const& actor : conversationTemplate->Actors)
187 std::visit(ConversationActorFillVisitor(this, creator, map, actor), actor.Data);
188
189 std::vector<UF::ConversationLine> lines;
190 for (ConversationLineTemplate const* line : conversationTemplate->Lines)
191 {
192 if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_CONVERSATION_LINE, line->Id, creator))
193 continue;
194
195 ConversationLineEntry const* convoLine = sConversationLineStore.LookupEntry(line->Id); // never null for conversationTemplate->Lines
196
197 UF::ConversationLine& lineField = lines.emplace_back();
198 lineField.ConversationLineID = line->Id;
199 lineField.BroadcastTextID = convoLine->BroadcastTextID;
200 lineField.UiCameraID = line->UiCameraID;
201 lineField.ActorIndex = line->ActorIdx;
202 lineField.Flags = line->Flags;
203 lineField.ChatType = line->ChatType;
204
205 for (LocaleConstant locale = LOCALE_enUS; locale < TOTAL_LOCALES; locale = LocaleConstant(locale + 1))
206 {
207 if (locale == LOCALE_none)
208 continue;
209
210 _lineStartTimes[{ locale, line->Id }] = _lastLineEndTimes[locale];
211 if (locale == DEFAULT_LOCALE)
212 lineField.StartTime = _lastLineEndTimes[locale].count();
213
214 if (int32 const* broadcastTextDuration = sDB2Manager.GetBroadcastTextDuration(convoLine->BroadcastTextID, locale))
215 _lastLineEndTimes[locale] += Milliseconds(*broadcastTextDuration);
216
218 }
219 }
220
221 _duration = Milliseconds(*std::max_element(_lastLineEndTimes.begin(), _lastLineEndTimes.end()));
224
225 // conversations are despawned 5-20s after LastLineEndTime
226 _duration += 10s;
227
228 sScriptMgr->OnConversationCreate(this, creator);
229}
230
232{
233 ConversationTemplate const* conversationTemplate = sConversationDataStore->GetConversationTemplate(GetEntry()); // never null, already checked in ::Create / ::CreateConversation
235 {
236 for (UF::ConversationLine const& line : *m_conversationData->Lines)
237 {
238 UF::ConversationActor const* actor = line.ActorIndex < m_conversationData->Actors.size() ? &m_conversationData->Actors[line.ActorIndex] : nullptr;
239 if (!actor || (!actor->CreatureID && actor->ActorGUID.IsEmpty() && !actor->NoActorObject))
240 {
241 TC_LOG_ERROR("entities.conversation", "Failed to create conversation (Id: {}) due to missing actor (Idx: {}).", GetEntry(), line.ActorIndex);
242 return false;
243 }
244 }
245 }
246
247 if (IsInWorld())
248 {
249 TC_LOG_ERROR("entities.conversation", "Attempted to start conversation (Id: {}) multiple times.", GetEntry());
250 return true; // returning true to not cause delete in Conversation::CreateConversation if convo is already started in ConversationScript::OnConversationCreate
251 }
252
253 if (!GetMap()->AddToMap(this))
254 return false;
255
256 sScriptMgr->OnConversationStart(this);
257 return true;
258}
259
260void Conversation::AddActor(int32 actorId, uint32 actorIdx, ObjectGuid const& actorGuid)
261{
262 auto actorField = m_values.ModifyValue(&Conversation::m_conversationData).ModifyValue(&UF::ConversationData::Actors, actorIdx);
263 SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::CreatureID), 0);
265 SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::ActorGUID), actorGuid);
266 SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::Id), actorId);
269}
270
271void Conversation::AddActor(int32 actorId, uint32 actorIdx, ConversationActorType type, uint32 creatureId, uint32 creatureDisplayInfoId)
272{
273 auto actorField = m_values.ModifyValue(&Conversation::m_conversationData).ModifyValue(&UF::ConversationData::Actors, actorIdx);
274 SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::CreatureID), creatureId);
275 SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::CreatureDisplayInfoID), creatureDisplayInfoId);
277 SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::Id), actorId);
280}
281
283{
284 return Trinity::Containers::MapGetValuePtr(_lineStartTimes, { locale, lineId });
285}
286
288{
289 return _lastLineEndTimes[locale];
290}
291
293{
294 ConversationLineEntry const* convoLine = sConversationLineStore.LookupEntry(lineId);
295 if (!convoLine)
296 {
297 TC_LOG_ERROR("entities.conversation", "Conversation::GetLineDuration: Tried to get duration for invalid ConversationLine id {}.", lineId);
298 return 0;
299 }
300
301 int32 const* textDuration = sDB2Manager.GetBroadcastTextDuration(convoLine->BroadcastTextID, locale);
302 if (!textDuration)
303 return 0;
304
305 return *textDuration + convoLine->AdditionalDuration;
306}
307
309{
310 Milliseconds const* lineStartTime = GetLineStartTime(locale, lineId);
311 if (!lineStartTime)
312 {
313 TC_LOG_ERROR("entities.conversation", "Conversation::GetLineEndTime: Unable to get line start time for locale {}, lineid {} (Conversation ID: {}).", locale, lineId, GetEntry());
314 return Milliseconds(0);
315 }
316 return *lineStartTime + Milliseconds(GetLineDuration(locale, lineId));
317}
318
320{
321 LocaleConstant privateOwnerLocale = LOCALE_enUS;
323 privateOwnerLocale = owner->GetSession()->GetSessionDbLocaleIndex();
324 return privateOwnerLocale;
325}
326
328{
329 if (m_conversationData->Actors.size() <= actorIdx)
330 {
331 TC_LOG_ERROR("entities.conversation", "Conversation::GetActorUnit: Tried to access invalid actor idx {} (Conversation ID: {}).", actorIdx, GetEntry());
332 return nullptr;
333 }
334 return ObjectAccessor::GetUnit(*this, m_conversationData->Actors[actorIdx].ActorGUID);
335}
336
338{
339 Unit* actor = GetActorUnit(actorIdx);
340 if (!actor)
341 return nullptr;
342 return actor->ToCreature();
343}
344
346{
347 return sConversationDataStore->GetConversationTemplate(GetEntry())->ScriptId;
348}
349
350void Conversation::BuildValuesCreate(ByteBuffer* data, Player const* target) const
351{
353 std::size_t sizePos = data->wpos();
354 *data << uint32(0);
355 *data << uint8(flags);
356 m_objectData->WriteCreate(*data, flags, this, target);
357 m_conversationData->WriteCreate(*data, flags, this, target);
358 data->put<uint32>(sizePos, data->wpos() - sizePos - 4);
359}
360
361void Conversation::BuildValuesUpdate(ByteBuffer* data, Player const* target) const
362{
364 std::size_t sizePos = data->wpos();
365 *data << uint32(0);
367
369 m_objectData->WriteUpdate(*data, flags, this, target);
370
372 m_conversationData->WriteUpdate(*data, flags, this, target);
373
374 data->put<uint32>(sizePos, data->wpos() - sizePos - 4);
375}
376
378 UF::ConversationData::Mask const& requestedConversationMask, Player const* target) const
379{
381 if (requestedObjectMask.IsAnySet())
382 valuesMask.Set(TYPEID_OBJECT);
383
384 if (requestedConversationMask.IsAnySet())
385 valuesMask.Set(TYPEID_CONVERSATION);
386
388 std::size_t sizePos = buffer.wpos();
389 buffer << uint32(0);
390 buffer << uint32(valuesMask.GetBlock(0));
391
392 if (valuesMask[TYPEID_OBJECT])
393 m_objectData->WriteUpdate(buffer, requestedObjectMask, true, this, target);
394
395 if (valuesMask[TYPEID_CONVERSATION])
396 m_conversationData->WriteUpdate(buffer, requestedConversationMask, true, this, target);
397
398 buffer.put<uint32>(sizePos, buffer.wpos() - sizePos - 4);
399
400 data->AddUpdateBlock();
401}
402
404{
405 UpdateData udata(Owner->GetMapId());
406 WorldPacket packet;
407
409
410 udata.BuildPacket(&packet);
411 player->SendDirectMessage(&packet);
412}
413
415{
418}
LocaleConstant
Definition: Common.h:48
@ LOCALE_none
Definition: Common.h:58
@ TOTAL_LOCALES
Definition: Common.h:62
@ LOCALE_enUS
Definition: Common.h:49
#define DEFAULT_LOCALE
Definition: Common.h:66
#define sConditionMgr
Definition: ConditionMgr.h:365
@ CONDITION_SOURCE_TYPE_CONVERSATION_LINE
Definition: ConditionMgr.h:183
#define sConversationDataStore
ConversationActorType
DB2Storage< ConversationLineEntry > sConversationLineStore("ConversationLine.db2", &ConversationLineLoadInfo::Instance)
#define sDB2Manager
Definition: DB2Stores.h:538
uint8_t uint8
Definition: Define.h:144
int32_t int32
Definition: Define.h:138
uint32_t uint32
Definition: Define.h:142
uint16 flags
Definition: DisableMgr.cpp:49
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition: Duration.h:29
#define ASSERT
Definition: Errors.h:68
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
@ TYPEID_OBJECT
Definition: ObjectGuid.h:35
@ TYPEID_CONVERSATION
Definition: ObjectGuid.h:48
@ TYPEMASK_CONVERSATION
Definition: ObjectGuid.h:68
#define sScriptMgr
Definition: ScriptMgr.h:1418
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition: Util.h:491
size_t wpos() const
Definition: ByteBuffer.h:412
void put(std::size_t pos, T value)
Definition: ByteBuffer.h:220
uint32 GetScriptId() const
void AddToWorld() override
void RelocateStationaryPosition(Position const &pos)
Definition: Conversation.h:77
Milliseconds GetLineEndTime(LocaleConstant locale, int32 lineId) const
void ClearUpdateMask(bool remove) override
static int32 GetLineDuration(LocaleConstant locale, int32 lineId)
void AddActor(int32 actorId, uint32 actorIdx, ObjectGuid const &actorGuid)
void BuildValuesUpdate(ByteBuffer *data, Player const *target) const override
void BuildValuesUpdateForPlayerWithMask(UpdateData *data, UF::ObjectData::Mask const &requestedObjectMask, UF::ConversationData::Mask const &requestedConversationMask, Player const *target) const
std::array< Milliseconds, TOTAL_LOCALES > _lastLineEndTimes
Definition: Conversation.h:99
UF::UpdateField< UF::ConversationData, 0, TYPEID_CONVERSATION > m_conversationData
Definition: Conversation.h:90
Milliseconds _duration
Definition: Conversation.h:95
Milliseconds GetDuration() const
Definition: Conversation.h:60
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
Definition: Conversation.h:96
void BuildValuesCreate(ByteBuffer *data, Player const *target) const override
Milliseconds GetLastLineEndTime(LocaleConstant locale) const
Unit * GetActorUnit(uint32 actorIdx) const
void Update(uint32 diff) override
std::unordered_map< std::pair< LocaleConstant, int32 >, Milliseconds > _lineStartTimes
Definition: Conversation.h:98
ObjectGuid _creatorGuid
Definition: Conversation.h:94
void RemoveFromWorld() override
Creature * GetActorCreature(uint32 actorIdx) const
constexpr bool HasFlag(T flag) const
Definition: EnumFlag.h:106
Definition: Map.h:189
MapStoredObjectTypesContainer & GetObjectsStore()
Definition: Map.h:422
ObjectGuid::LowType GenerateLowGuid()
Definition: Map.h:519
CreatureBySpawnIdContainer & GetCreatureBySpawnIdStore()
Definition: Map.h:425
static ObjectGuid const Empty
Definition: ObjectGuid.h:274
bool IsEmpty() const
Definition: ObjectGuid.h:319
uint64 LowType
Definition: ObjectGuid.h:278
uint16 m_objectType
Definition: Object.h:401
static Creature * ToCreature(Object *o)
Definition: Object.h:219
ObjectGuid const & GetGUID() const
Definition: Object.h:160
bool IsInWorld() const
Definition: Object.h:154
UF::UpdateField< UF::ObjectData, 0, TYPEID_OBJECT > m_objectData
Definition: Object.h:267
CreateObjectBits m_updateFlag
Definition: Object.h:404
virtual void ClearUpdateMask(bool remove)
Definition: Object.cpp:790
void SetUpdateFieldValue(UF::UpdateFieldSetter< T > setter, typename UF::UpdateFieldSetter< T >::value_type value)
Definition: Object.h:287
uint32 GetEntry() const
Definition: Object.h:161
UF::UpdateFieldHolder m_values
Definition: Object.h:266
void _Create(ObjectGuid const &guid)
Definition: Object.cpp:101
ByteBuffer & PrepareValuesUpdateBuffer(UpdateData *data) const
Definition: Object.cpp:225
void DoWithSuppressingObjectUpdates(Action &&action)
Definition: Object.h:381
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
void SetEntry(uint32 entry)
Definition: Object.h:162
virtual void SetObjectScale(float scale)
Definition: Object.h:165
virtual UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const *target) const
Definition: Object.cpp:770
TypeID m_objectTypeId
Definition: Object.h:403
void ApplyModUpdateFieldValue(UF::UpdateFieldSetter< T > setter, typename UF::UpdateFieldSetter< T >::value_type mod, bool apply)
Definition: Object.h:351
static void InheritPhaseShift(WorldObject *target, WorldObject const *source)
void SendDirectMessage(WorldPacket const *data) const
Definition: Player.cpp:6324
bool Remove(KEY_TYPE const &handle)
bool Insert(KEY_TYPE const &handle, SPECIFIC_TYPE *obj)
void ClearChanged(UpdateField< T, BlockBit, Bit >(Derived::*))
Definition: UpdateField.h:572
Mask const & GetChangesMask() const
Definition: UpdateField.h:605
MutableFieldReference< T, false > ModifyValue(UpdateField< T, BlockBit, Bit >(Derived::*field))
Definition: UpdateField.h:683
void ClearChangesMask(UpdateField< T, BlockBit, Bit >(Derived::*field))
Definition: UpdateField.h:690
bool HasChanged(uint32 index) const
Definition: UpdateField.h:701
uint32 GetChangedObjectTypeMask() const
Definition: UpdateField.h:696
Definition: Unit.h:627
bool BuildPacket(WorldPacket *packet)
Definition: UpdateData.cpp:40
void AddUpdateBlock()
Definition: UpdateData.h:49
uint32 GetBlock(uint32 index) const
Definition: UpdateMask.h:53
void Set(uint32 index)
Definition: UpdateMask.h:84
constexpr uint32 GetMapId() const
Definition: Position.h:201
Map * GetMap() const
Definition: Object.h:624
void AddToWorld() override
Definition: Object.cpp:1011
void RemoveFromWorld() override
Definition: Object.cpp:1017
ObjectGuid GetPrivateObjectOwner() const
Definition: Object.h:785
void SetZoneScript()
Definition: Object.cpp:2011
void SetPrivateObjectOwner(ObjectGuid const &owner)
Definition: Object.h:786
virtual void SetMap(Map *map)
Definition: Object.cpp:1794
void AddObjectToRemoveList()
Definition: Object.cpp:1824
void UpdatePositionData()
Definition: Object.cpp:992
virtual void Update(uint32 diff)
Definition: Object.cpp:898
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)
Definition: IteratorPair.h:60
auto MapGetValuePtr(M &map, typename M::key_type const &key)
Definition: MapUtils.h:29
UpdateFieldFlag
Definition: UpdateField.h:34
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
std::variant< ConversationActorWorldObjectTemplate, ConversationActorNoObjectTemplate, ConversationActorActivePlayerTemplate, ConversationActorTalkingHeadTemplate > Data
EnumFlag< ConversationFlags > Flags
std::vector< ConversationLineTemplate const * > Lines
std::vector< ConversationActorTemplate > Actors
void operator()(Player const *player) const
bool Stationary
Definition: Object.h:88
bool Conversation
Definition: Object.h:100
constexpr void Relocate(float x, float y)
Definition: Position.h:63
DynamicUpdateField< UF::ConversationActor, 0, 3 > Actors
UpdateField< std::vector< UF::ConversationLine >, 0, 2 > Lines
UpdateField< uint32, 0, 5 > Progress
UpdateField< int32, 0, 4 > LastLineEndTime