/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #include "Conversation.h" #include "ConditionMgr.h" #include "ConversationDataStore.h" #include "Creature.h" #include "IteratorPair.h" #include "Log.h" #include "Map.h" #include "PhasingHandler.h" #include "ScriptMgr.h" #include "Unit.h" #include "UpdateData.h" Conversation::Conversation() : WorldObject(false), _duration(0), _textureKitId(0) { m_objectType |= TYPEMASK_CONVERSATION; m_objectTypeId = TYPEID_CONVERSATION; m_updateFlag.Stationary = true; m_updateFlag.Conversation = true; } Conversation::~Conversation() = default; void Conversation::AddToWorld() { ///- Register the Conversation for guid lookup and for caster if (!IsInWorld()) { GetMap()->GetObjectsStore().Insert(GetGUID(), this); WorldObject::AddToWorld(); } } void Conversation::RemoveFromWorld() { ///- Remove the Conversation from the accessor and from all lists of objects in world if (IsInWorld()) { WorldObject::RemoveFromWorld(); GetMap()->GetObjectsStore().Remove(GetGUID()); } } void Conversation::Update(uint32 diff) { if (GetDuration() > int32(diff)) { _duration -= diff; DoWithSuppressingObjectUpdates([&]() { // Only sent in CreateObject ApplyModUpdateFieldValue(m_values.ModifyValue(&Conversation::m_conversationData).ModifyValue(&UF::ConversationData::Progress), int32(diff), true); const_cast(*m_conversationData).ClearChanged(&UF::ConversationData::Progress); }); } else { Remove(); // expired return; } WorldObject::Update(diff); } void Conversation::Remove() { if (IsInWorld()) { AddObjectToRemoveList(); // calls RemoveFromWorld } } Conversation* Conversation::CreateConversation(uint32 conversationEntry, Unit* creator, Position const& pos, ObjectGuid privateObjectOwner, SpellInfo const* spellInfo /*= nullptr*/) { ConversationTemplate const* conversationTemplate = sConversationDataStore->GetConversationTemplate(conversationEntry); if (!conversationTemplate) return nullptr; ObjectGuid::LowType lowGuid = creator->GetMap()->GenerateLowGuid(); Conversation* conversation = new Conversation(); if (!conversation->Create(lowGuid, conversationEntry, creator->GetMap(), creator, pos, privateObjectOwner, spellInfo)) { delete conversation; return nullptr; } return conversation; } 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); ASSERT(conversationTemplate); _creatorGuid = creator->GetGUID(); SetPrivateObjectOwner(privateObjectOwner); SetMap(map); Relocate(pos); RelocateStationaryPosition(pos); Object::_Create(ObjectGuid::Create(GetMapId(), conversationEntry, lowGuid)); PhasingHandler::InheritPhaseShift(this, creator); SetEntry(conversationEntry); SetObjectScale(1.0f); SetUpdateFieldValue(m_values.ModifyValue(&Conversation::m_conversationData).ModifyValue(&UF::ConversationData::LastLineEndTime), conversationTemplate->LastLineEndTime); _duration = conversationTemplate->LastLineEndTime + 10 * IN_MILLISECONDS; _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); } } std::set actorIndices; std::vector lines; for (ConversationLineTemplate const* line : conversationTemplate->Lines) { if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_CONVERSATION_LINE, line->Id, creator)) continue; actorIndices.insert(line->ActorIdx); lines.emplace_back(); UF::ConversationLine& lineField = lines.back(); lineField.ConversationLineID = line->Id; lineField.StartTime = line->StartTime; lineField.UiCameraID = line->UiCameraID; lineField.ActorIndex = line->ActorIdx; lineField.Flags = line->Flags; } SetUpdateFieldValue(m_values.ModifyValue(&Conversation::m_conversationData).ModifyValue(&UF::ConversationData::Lines), std::move(lines)); sScriptMgr->OnConversationCreate(this, creator); // All actors need to be set for (uint16 actorIndex : actorIndices) { UF::ConversationActor const* actor = actorIndex < m_conversationData->Actors.size() ? &m_conversationData->Actors[actorIndex] : nullptr; if (!actor || (!actor->CreatureID && actor->ActorGUID.IsEmpty() && !actor->NoActorObject)) { TC_LOG_ERROR("entities.conversation", "Failed to create conversation (Id: %u) due to missing actor (Idx: %u).", conversationEntry, actorIndex); return false; } } if (!GetMap()->AddToMap(this)) return false; return true; } void Conversation::AddActor(ObjectGuid const& actorGuid, uint16 actorIdx) { auto actorField = m_values.ModifyValue(&Conversation::m_conversationData).ModifyValue(&UF::ConversationData::Actors, actorIdx); SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::ActorGUID), actorGuid); SetUpdateFieldValue(actorField.ModifyValue(&UF::ConversationActor::Type), AsUnderlyingType(ActorType::WorldObjectActor)); } uint32 Conversation::GetScriptId() const { return sConversationDataStore->GetConversationTemplate(GetEntry())->ScriptId; } void Conversation::BuildValuesCreate(ByteBuffer* data, Player const* target) const { UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); std::size_t sizePos = data->wpos(); *data << uint32(0); *data << uint8(flags); m_objectData->WriteCreate(*data, flags, this, target); m_conversationData->WriteCreate(*data, flags, this, target); data->put(sizePos, data->wpos() - sizePos - 4); } void Conversation::BuildValuesUpdate(ByteBuffer* data, Player const* target) const { UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); std::size_t sizePos = data->wpos(); *data << uint32(0); *data << uint32(m_values.GetChangedObjectTypeMask()); if (m_values.HasChanged(TYPEID_OBJECT)) m_objectData->WriteUpdate(*data, flags, this, target); if (m_values.HasChanged(TYPEID_CONVERSATION)) m_conversationData->WriteUpdate(*data, flags, this, target); data->put(sizePos, data->wpos() - sizePos - 4); } void Conversation::BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData::Mask const& requestedObjectMask, UF::ConversationData::Mask const& requestedConversationMask, Player const* target) const { UpdateMask valuesMask; if (requestedObjectMask.IsAnySet()) valuesMask.Set(TYPEID_OBJECT); if (requestedConversationMask.IsAnySet()) valuesMask.Set(TYPEID_CONVERSATION); ByteBuffer buffer = PrepareValuesUpdateBuffer(); std::size_t sizePos = buffer.wpos(); buffer << uint32(0); buffer << uint32(valuesMask.GetBlock(0)); if (valuesMask[TYPEID_OBJECT]) m_objectData->WriteUpdate(buffer, requestedObjectMask, true, this, target); if (valuesMask[TYPEID_CONVERSATION]) m_conversationData->WriteUpdate(buffer, requestedConversationMask, true, this, target); buffer.put(sizePos, buffer.wpos() - sizePos - 4); data->AddUpdateBlock(buffer); } void Conversation::ClearUpdateMask(bool remove) { m_values.ClearChangesMask(&Conversation::m_conversationData); Object::ClearUpdateMask(remove); }