diff options
author | Traesh <Traesh@users.noreply.github.com> | 2017-05-14 11:40:55 +0200 |
---|---|---|
committer | joschiwald <joschiwald.trinity@gmail.com> | 2017-05-14 11:40:55 +0200 |
commit | c73ce93f4cf84667d23c2ec7e425f40a845eaf4f (patch) | |
tree | 255a3af04144a58e3156491bffd1aa743fb06bfa /src | |
parent | 3e18fcb8fd4ac8e9700f10eb3b6a1292c0f8beb8 (diff) |
Core/Entities : Basic Conversation Implementation (#18010)
Diffstat (limited to 'src')
35 files changed, 709 insertions, 16 deletions
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index da134a3d9b0..99f01a91a5b 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -185,6 +185,10 @@ void HotfixDatabaseConnection::DoPrepareStatements() PrepareStatement(HOTFIX_SEL_CINEMATIC_SEQUENCES, "SELECT ID, SoundID, Camera1, Camera2, Camera3, Camera4, Camera5, Camera6, Camera7, Camera8" " FROM cinematic_sequences ORDER BY ID DESC", CONNECTION_SYNCH); + // ConversationLine.db2 + PrepareStatement(HOTFIX_SEL_CONVERSATION_LINE, "SELECT ID, BroadcastTextID, SpellVisualKitID, Duration, NextLineID, Unk1, Yell, Unk2, Unk3" + " FROM conversation_line ORDER BY ID DESC", CONNECTION_SYNCH); + // CreatureDisplayInfo.db2 PrepareStatement(HOTFIX_SEL_CREATURE_DISPLAY_INFO, "SELECT ID, CreatureModelScale, ModelID, NPCSoundID, SizeClass, Flags, Gender, " "ExtendedDisplayInfoID, TextureVariation1, TextureVariation2, TextureVariation3, PortraitTextureFileDataID, CreatureModelAlpha, SoundID, " diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index 4e53630805f..06710712818 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -116,6 +116,8 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_CINEMATIC_SEQUENCES, + HOTFIX_SEL_CONVERSATION_LINE, + HOTFIX_SEL_CREATURE_DISPLAY_INFO, HOTFIX_SEL_CREATURE_DISPLAY_INFO_EXTRA, diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index 425d0e96ccc..7eb60a6a34c 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -757,6 +757,8 @@ enum RBACPermissions RBAC_PERM_COMMAND_RELOAD_SCENE_TEMPLATE = 850, RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_TEMPLATE = 851, RBAC_PERM_COMMAND_GO_OFFSET = 852, + RBAC_PERM_COMMAND_RELOAD_CONVERSATION_TEMPLATE = 853, + RBAC_PERM_COMMAND_DEBUG_CONVERSATION = 854, // custom permissions 1000+ RBAC_PERM_MAX diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h index 53398f8f4e1..91ea862d1fc 100644 --- a/src/server/game/DataStores/DB2LoadInfo.h +++ b/src/server/game/DataStores/DB2LoadInfo.h @@ -818,6 +818,27 @@ struct CinematicSequencesLoadInfo } }; +struct ConversationLineLoadInfo +{ + static DB2LoadInfo const* Instance() + { + static DB2FieldMeta const fields[] = + { + { false, FT_INT, "ID" }, + { false, FT_INT, "BroadcastTextID" }, + { false, FT_INT, "SpellVisualKitID" }, + { false, FT_INT, "Duration" }, + { false, FT_SHORT, "NextLineID" }, + { false, FT_SHORT, "Unk1" }, + { false, FT_BYTE, "Yell" }, + { false, FT_BYTE, "Unk2" }, + { false, FT_BYTE, "Unk3" }, + }; + static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, ConversationLineMeta::Instance(), HOTFIX_SEL_CONVERSATION_LINE); + return &loadInfo; + } +}; + struct CreatureDisplayInfoLoadInfo { static DB2LoadInfo const* Instance() diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index c62dd2ee9b8..4eb4742da45 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -65,6 +65,7 @@ DB2Storage<ChrRacesEntry> sChrRacesStore("ChrRaces.db2", C DB2Storage<ChrSpecializationEntry> sChrSpecializationStore("ChrSpecialization.db2", ChrSpecializationLoadInfo::Instance()); DB2Storage<CinematicCameraEntry> sCinematicCameraStore("CinematicCamera.db2", CinematicCameraLoadInfo::Instance()); DB2Storage<CinematicSequencesEntry> sCinematicSequencesStore("CinematicSequences.db2", CinematicSequencesLoadInfo::Instance()); +DB2Storage<ConversationLineEntry> sConversationLineStore("ConversationLine.db2", ConversationLineLoadInfo::Instance()); DB2Storage<CreatureDisplayInfoEntry> sCreatureDisplayInfoStore("CreatureDisplayInfo.db2", CreatureDisplayInfoLoadInfo::Instance()); DB2Storage<CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore("CreatureDisplayInfoExtra.db2", CreatureDisplayInfoExtraLoadInfo::Instance()); DB2Storage<CreatureFamilyEntry> sCreatureFamilyStore("CreatureFamily.db2", CreatureFamilyLoadInfo::Instance()); @@ -361,6 +362,7 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale) LOAD_DB2(sChrSpecializationStore); LOAD_DB2(sCinematicCameraStore); LOAD_DB2(sCinematicSequencesStore); + LOAD_DB2(sConversationLineStore); LOAD_DB2(sCreatureDisplayInfoStore); LOAD_DB2(sCreatureDisplayInfoExtraStore); LOAD_DB2(sCreatureFamilyStore); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 2970bb56965..8bc6acb986d 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -64,6 +64,7 @@ TC_GAME_API extern DB2Storage<ChrRacesEntry> sChrRacesSto TC_GAME_API extern DB2Storage<ChrSpecializationEntry> sChrSpecializationStore; TC_GAME_API extern DB2Storage<CinematicCameraEntry> sCinematicCameraStore; TC_GAME_API extern DB2Storage<CinematicSequencesEntry> sCinematicSequencesStore; +TC_GAME_API extern DB2Storage<ConversationLineEntry> sConversationLineStore; TC_GAME_API extern DB2Storage<CreatureDisplayInfoEntry> sCreatureDisplayInfoStore; TC_GAME_API extern DB2Storage<CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore; TC_GAME_API extern DB2Storage<CreatureFamilyEntry> sCreatureFamilyStore; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index 459ed14beae..ec141cb062a 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -475,6 +475,19 @@ struct CinematicSequencesEntry uint16 Camera[8]; }; +struct ConversationLineEntry +{ + uint32 ID; + uint32 BroadcastTextID; + uint32 SpellVisualKitID; + uint32 Duration; + uint16 NextLineID; + uint16 Unk1; + uint8 Yell; + uint8 Unk2; + uint8 Unk3; +}; + struct CreatureDisplayInfoEntry { uint32 ID; diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp index 99e343cc948..3ea350a583b 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp @@ -202,8 +202,7 @@ void AreaTrigger::Remove() _ai->OnRemove(); - RemoveFromWorld(); - AddObjectToRemoveList(); + AddObjectToRemoveList(); // calls RemoveFromWorld } } diff --git a/src/server/game/Entities/Conversation/Conversation.cpp b/src/server/game/Entities/Conversation/Conversation.cpp new file mode 100644 index 00000000000..8e422937d57 --- /dev/null +++ b/src/server/game/Entities/Conversation/Conversation.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "Conversation.h" +#include "Unit.h" +#include "UpdateData.h" + +Conversation::Conversation() : WorldObject(false), _duration(0) +{ + m_objectType |= TYPEMASK_CONVERSATION; + m_objectTypeId = TYPEID_CONVERSATION; + + m_updateFlag = UPDATEFLAG_STATIONARY_POSITION; + + m_valuesCount = CONVERSATION_END; + _dynamicValuesCount = CONVERSATION_DYNAMIC_END; +} + +Conversation::~Conversation() +{ +} + +void Conversation::AddToWorld() +{ + ///- Register the Conversation for guid lookup and for caster + if (!IsInWorld()) + { + GetMap()->GetObjectsStore().Insert<Conversation>(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<Conversation>(GetGUID()); + } +} + +bool Conversation::IsNeverVisibleFor(WorldObject const* seer) const +{ + if (_participants.find(seer->GetGUID()) == _participants.end()) + return true; + + return WorldObject::IsNeverVisibleFor(seer); +} + +void Conversation::Update(uint32 diff) +{ + if (GetDuration() > int32(diff)) + _duration -= diff; + else + Remove(); // expired + + WorldObject::Update(diff); +} + +void Conversation::Remove() +{ + if (IsInWorld()) + { + AddObjectToRemoveList(); // calls RemoveFromWorld + } +} + +Conversation* Conversation::CreateConversation(uint32 conversationEntry, Unit* creator, Position const& pos, GuidUnorderedSet&& participants, SpellInfo const* spellInfo /*= nullptr*/) +{ + ConversationTemplate const* conversationTemplate = sConversationDataStore->GetConversationTemplate(conversationEntry); + if (!conversationTemplate) + return nullptr; + + ObjectGuid::LowType lowGuid = creator->GetMap()->GenerateLowGuid<HighGuid::Conversation>(); + + Conversation* conversation = new Conversation(); + if (!conversation->Create(lowGuid, conversationEntry, creator->GetMap(), creator, pos, std::move(participants), spellInfo)) + { + delete conversation; + return nullptr; + } + + return conversation; +} + +bool Conversation::Create(ObjectGuid::LowType lowGuid, uint32 conversationEntry, Map* map, Unit* creator, Position const& pos, GuidUnorderedSet&& participants, SpellInfo const* /*spellInfo = nullptr*/) +{ + ConversationTemplate const* conversationTemplate = sConversationDataStore->GetConversationTemplate(conversationEntry); + ASSERT(conversationTemplate); + + _creatorGuid = creator->GetGUID(); + _participants = std::move(participants); + + SetMap(map); + Relocate(pos); + + Object::_Create(ObjectGuid::Create<HighGuid::Conversation>(GetMapId(), conversationEntry, lowGuid)); + SetPhaseMask(creator->GetPhaseMask(), false); + CopyPhaseFrom(creator); + + SetEntry(conversationEntry); + SetObjectScale(1.0f); + + SetUInt32Value(CONVERSATION_LAST_LINE_END_TIME, conversationTemplate->LastLineEndTime); + _duration = conversationTemplate->LastLineEndTime; + + uint16 actorsIndex = 0; + for (ConversationActorTemplate const* actor : conversationTemplate->Actors) + { + if (actor) + { + ConversationDynamicFieldActor actorField; + actorField.ActorTemplate = *actor; + actorField.Type = ConversationDynamicFieldActor::ActorType::CreatureActor; + SetDynamicStructuredValue(CONVERSATION_DYNAMIC_FIELD_ACTORS, actorsIndex++, &actorField); + } + else + ++actorsIndex; + } + + uint16 linesIndex = 0; + for (ConversationLineTemplate const* line : conversationTemplate->Lines) + SetDynamicStructuredValue(CONVERSATION_DYNAMIC_FIELD_LINES, linesIndex++, line); + + if (!GetMap()->AddToMap(this)) + return false; + + return true; +} + +void Conversation::AddActor(ObjectGuid const& actorGuid, uint16 actorIdx) +{ + ConversationDynamicFieldActor actorField; + actorField.ActorGuid = actorGuid; + actorField.Type = ConversationDynamicFieldActor::ActorType::WorldObjectActor; + SetDynamicStructuredValue(CONVERSATION_DYNAMIC_FIELD_ACTORS, actorIdx, &actorField); +} + +void Conversation::AddParticipant(ObjectGuid const& participantGuid) +{ + _participants.insert(participantGuid); +} diff --git a/src/server/game/Entities/Conversation/Conversation.h b/src/server/game/Entities/Conversation/Conversation.h new file mode 100644 index 00000000000..7b168704f10 --- /dev/null +++ b/src/server/game/Entities/Conversation/Conversation.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef TRINITYCORE_CONVERSATION_H +#define TRINITYCORE_CONVERSATION_H + +#include "Object.h" +#include "ConversationDataStore.h" + +class Unit; +class SpellInfo; + +struct ConversationDynamicFieldActor +{ + ConversationDynamicFieldActor() : Type(0), Padding(0) + { + memset(Raw.Data, 0, sizeof(Raw.Data)); + } + + enum ActorType + { + WorldObjectActor = 0, + CreatureActor = 1 + }; + + union + { + ObjectGuid ActorGuid; + + ConversationActorTemplate ActorTemplate; + + struct + { + uint32 Data[4]; + } Raw; + }; + + uint32 Type; + uint32 Padding; +}; + +class TC_GAME_API Conversation : public WorldObject, public GridObject<Conversation> +{ + public: + Conversation(); + ~Conversation(); + + void AddToWorld() override; + void RemoveFromWorld() override; + + bool IsNeverVisibleFor(WorldObject const* seer) const override; + + void Update(uint32 diff) override; + void Remove(); + int32 GetDuration() const { return _duration; } + + static Conversation* CreateConversation(uint32 conversationEntry, Unit* creator, Position const& pos, GuidUnorderedSet&& participants, SpellInfo const* spellInfo = nullptr); + bool Create(ObjectGuid::LowType lowGuid, uint32 conversationEntry, Map* map, Unit* creator, Position const& pos, GuidUnorderedSet&& participants, SpellInfo const* spellInfo = nullptr); + void AddActor(ObjectGuid const& actorGuid, uint16 actorIdx); + void AddParticipant(ObjectGuid const& participantGuid); + + ObjectGuid const& GetCreatorGuid() const { return _creatorGuid; } + + float GetStationaryX() const override { return _stationaryPosition.GetPositionX(); } + float GetStationaryY() const override { return _stationaryPosition.GetPositionY(); } + float GetStationaryZ() const override { return _stationaryPosition.GetPositionZ(); } + float GetStationaryO() const override { return _stationaryPosition.GetOrientation(); } + void RelocateStationaryPosition(Position const& pos) { _stationaryPosition.Relocate(pos); } + + private: + Position _stationaryPosition; + ObjectGuid _creatorGuid; + uint32 _duration; + GuidUnorderedSet _participants; +}; + +#endif // TRINITYCORE_CONVERSATION_H diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index b1dc8e894bf..333a6a3c5d0 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -182,6 +182,7 @@ void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c case HighGuid::Corpse: case HighGuid::DynamicObject: case HighGuid::AreaTrigger: + case HighGuid::Conversation: updateType = UPDATETYPE_CREATE_OBJECT2; break; case HighGuid::Creature: @@ -1012,6 +1013,9 @@ uint32 Object::GetDynamicUpdateFieldData(Player const* target, uint32*& flags) c break; case TYPEID_CONVERSATION: flags = ConversationDynamicUpdateFieldFlags; + + if (ToConversation()->GetCreatorGuid() == target->GetGUID()) + visibleFlag |= UF_FLAG_0x100; break; default: flags = nullptr; diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index f3b4041db3c..5232e9912b2 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -315,6 +315,9 @@ class TC_GAME_API Object AreaTrigger* ToAreaTrigger() { if (GetTypeId() == TYPEID_AREATRIGGER) return reinterpret_cast<AreaTrigger*>(this); else return NULL; } AreaTrigger const* ToAreaTrigger() const { if (GetTypeId() == TYPEID_AREATRIGGER) return reinterpret_cast<AreaTrigger const*>(this); else return NULL; } + Conversation* ToConversation() { if (GetTypeId() == TYPEID_CONVERSATION) return reinterpret_cast<Conversation*>(this); else return NULL; } + Conversation const* ToConversation() const { if (GetTypeId() == TYPEID_CONVERSATION) return reinterpret_cast<Conversation const*>(this); else return NULL; } + protected: Object(); diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 41af8b74013..1b41469c373 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -23195,6 +23195,7 @@ template void Player::UpdateVisibilityOf(Corpse* target, UpdateData& data template void Player::UpdateVisibilityOf(GameObject* target, UpdateData& data, std::set<Unit*>& visibleNow); template void Player::UpdateVisibilityOf(DynamicObject* target, UpdateData& data, std::set<Unit*>& visibleNow); template void Player::UpdateVisibilityOf(AreaTrigger* target, UpdateData& data, std::set<Unit*>& visibleNow); +template void Player::UpdateVisibilityOf(Conversation* target, UpdateData& data, std::set<Unit*>& visibleNow); void Player::UpdateObjectVisibility(bool forced) { diff --git a/src/server/game/Globals/ConversationDataStore.cpp b/src/server/game/Globals/ConversationDataStore.cpp new file mode 100644 index 00000000000..f977a77c845 --- /dev/null +++ b/src/server/game/Globals/ConversationDataStore.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "ConversationDataStore.h" +#include "Containers.h" +#include "DatabaseEnv.h" +#include "DB2Stores.h" +#include "Log.h" +#include "Timer.h" + +namespace +{ + std::unordered_map<uint32, ConversationTemplate> _conversationTemplateStore; + std::unordered_map<uint32, ConversationActorTemplate> _conversationActorTemplateStore; + std::unordered_map<uint32, ConversationLineTemplate> _conversationLineTemplateStore; +} + +void ConversationDataStore::LoadConversationTemplates() +{ + _conversationActorTemplateStore.clear(); + _conversationLineTemplateStore.clear(); + _conversationTemplateStore.clear(); + + std::unordered_map<uint32, std::vector<ConversationActorTemplate const*>> actorsByConversation; + + if (QueryResult actorTemplates = WorldDatabase.Query("SELECT Id, CreatureId, CreatureModelId FROM conversation_actor_template")) + { + uint32 oldMSTime = getMSTime(); + + do + { + Field* fields = actorTemplates->Fetch(); + + uint32 id = fields[0].GetUInt32(); + ConversationActorTemplate& conversationActor = _conversationActorTemplateStore[id]; + conversationActor.Id = id; + conversationActor.CreatureId = fields[1].GetUInt32(); + conversationActor.CreatureModelId = fields[2].GetUInt32(); + } + while (actorTemplates->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " Conversation actor templates in %u ms", _conversationActorTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime)); + } + else + { + TC_LOG_INFO("server.loading", ">> Loaded 0 Conversation actor templates. DB table `conversation_actor_template` is empty."); + } + + if (QueryResult lineTemplates = WorldDatabase.Query("SELECT Id, StartTime, UiCameraID, ActorIdx, Unk FROM conversation_line_template")) + { + uint32 oldMSTime = getMSTime(); + + do + { + Field* fields = lineTemplates->Fetch(); + + uint32 id = fields[0].GetUInt32(); + + if (!sConversationLineStore.LookupEntry(id)) + { + TC_LOG_ERROR("sql.sql", "Table `conversation_line_template` has template for non existing ConversationLine (ID: %u), skipped", id); + continue; + } + + ConversationLineTemplate& conversationLine = _conversationLineTemplateStore[id]; + conversationLine.Id = id; + conversationLine.StartTime = fields[1].GetUInt32(); + conversationLine.UiCameraID = fields[2].GetUInt32(); + conversationLine.ActorIdx = fields[3].GetUInt16(); + conversationLine.Unk = fields[4].GetUInt16(); + } + while (lineTemplates->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " Conversation line templates in %u ms", _conversationLineTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime)); + } + else + { + 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, Idx FROM conversation_actors")) + { + uint32 oldMSTime = getMSTime(); + uint32 count = 0; + + do + { + Field* fields = actors->Fetch(); + + uint32 conversationId = fields[0].GetUInt32(); + uint32 actorId = fields[1].GetUInt32(); + uint16 idx = fields[2].GetUInt16(); + + if (ConversationActorTemplate const* conversationActorTemplate = Trinity::Containers::MapGetValuePtr(_conversationActorTemplateStore, actorId)) + { + std::vector<ConversationActorTemplate const*>& actors = actorsByConversation[conversationId]; + if (actors.size() <= idx) + actors.resize(idx + 1); + actors[idx] = conversationActorTemplate; + ++count; + } + else + TC_LOG_ERROR("sql.sql", "Table `conversation_actors` references an invalid actor (ID: %u) for Conversation %u, skipped", actorId, conversationId); + } + while (actors->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u Conversation actors in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + } + else + { + TC_LOG_INFO("server.loading", ">> Loaded 0 Conversation actors. DB table `conversation_actors` is empty."); + } + + if (QueryResult templates = WorldDatabase.Query("SELECT Id, FirstLineId, LastLineEndTime, VerifiedBuild FROM conversation_template")) + { + uint32 oldMSTime = getMSTime(); + + do + { + Field* fields = templates->Fetch(); + + ConversationTemplate conversationTemplate; + conversationTemplate.Id = fields[0].GetUInt32(); + conversationTemplate.FirstLineId = fields[1].GetUInt32(); + conversationTemplate.LastLineEndTime = fields[2].GetUInt32(); + + conversationTemplate.Actors = std::move(actorsByConversation[conversationTemplate.Id]); + + ConversationLineEntry const* currentConversationLine = sConversationLineStore.LookupEntry(conversationTemplate.FirstLineId); + if (!currentConversationLine) + TC_LOG_ERROR("sql.sql", "Table `conversation_template` references an invalid line (ID: %u) for Conversation %u, skipped", conversationTemplate.FirstLineId, conversationTemplate.Id); + + while (currentConversationLine != nullptr) + { + if (ConversationLineTemplate const* conversationLineTemplate = Trinity::Containers::MapGetValuePtr(_conversationLineTemplateStore, currentConversationLine->ID)) + conversationTemplate.Lines.push_back(conversationLineTemplate); + else + TC_LOG_ERROR("sql.sql", "Table `conversation_line_template` has missing template for line (ID: %u) in Conversation %u, skipped", currentConversationLine->ID, conversationTemplate.Id); + + if (!currentConversationLine->NextLineID) + break; + + currentConversationLine = sConversationLineStore.AssertEntry(currentConversationLine->NextLineID); + } + + _conversationTemplateStore[conversationTemplate.Id] = conversationTemplate; + } + while (templates->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " Conversation templates in %u ms", _conversationTemplateStore.size(), GetMSTimeDiffToNow(oldMSTime)); + } + else + { + TC_LOG_INFO("server.loading", ">> Loaded 0 Conversation templates. DB table `conversation_template` is empty."); + } +} + +ConversationTemplate const* ConversationDataStore::GetConversationTemplate(uint32 conversationId) const +{ + return Trinity::Containers::MapGetValuePtr(_conversationTemplateStore, conversationId); +} + +ConversationDataStore* ConversationDataStore::Instance() +{ + static ConversationDataStore instance; + return &instance; +} diff --git a/src/server/game/Globals/ConversationDataStore.h b/src/server/game/Globals/ConversationDataStore.h new file mode 100644 index 00000000000..546de60a2a8 --- /dev/null +++ b/src/server/game/Globals/ConversationDataStore.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef ConversationDataStore_h__ +#define ConversationDataStore_h__ + +#include "Define.h" + +#include <vector> + +struct ConversationActorTemplate +{ + uint32 Id; + uint32 CreatureId; + uint32 CreatureModelId; +}; + +#pragma pack(push, 1) +struct ConversationLineTemplate +{ + uint32 Id; // Link to ConversationLine.db2 + uint32 StartTime; // Time in ms after conversation creation the line is displayed + uint32 UiCameraID; // Link to UiCamera.db2 + uint16 ActorIdx; // Index from conversation_actors + uint16 Unk; +}; +#pragma pack(pop) + +struct ConversationTemplate +{ + uint32 Id; + uint32 FirstLineId; // Link to ConversationLine.db2 + uint32 LastLineEndTime; // Time in ms after conversation creation the last line fades out + + std::vector<ConversationActorTemplate const*> Actors; + std::vector<ConversationLineTemplate const*> Lines; +}; + +class TC_GAME_API ConversationDataStore +{ +public: + void LoadConversationTemplates(); + + ConversationTemplate const* GetConversationTemplate(uint32 conversationId) const; + + static ConversationDataStore* Instance(); +}; + +#define sConversationDataStore ConversationDataStore::Instance() + +#endif // ConversationDataStore_h__ diff --git a/src/server/game/Globals/ObjectAccessor.cpp b/src/server/game/Globals/ObjectAccessor.cpp index 564b02adc67..acc05ad7f3b 100644 --- a/src/server/game/Globals/ObjectAccessor.cpp +++ b/src/server/game/Globals/ObjectAccessor.cpp @@ -119,6 +119,7 @@ WorldObject* ObjectAccessor::GetWorldObject(WorldObject const& p, ObjectGuid con case HighGuid::DynamicObject: return GetDynamicObject(p, guid); case HighGuid::AreaTrigger: return GetAreaTrigger(p, guid); case HighGuid::Corpse: return GetCorpse(p, guid); + case HighGuid::Conversation: return GetConversation(p, guid); default: return nullptr; } } @@ -156,6 +157,9 @@ Object* ObjectAccessor::GetObjectByTypeMask(WorldObject const& p, ObjectGuid con case HighGuid::AreaTrigger: if (typemask & TYPEMASK_AREATRIGGER) return GetAreaTrigger(p, guid); + case HighGuid::Conversation: + if (typemask & TYPEMASK_CONVERSATION) + return GetConversation(p, guid); case HighGuid::Corpse: break; default: @@ -195,6 +199,11 @@ AreaTrigger* ObjectAccessor::GetAreaTrigger(WorldObject const& u, ObjectGuid con return u.GetMap()->GetAreaTrigger(guid); } +Conversation* ObjectAccessor::GetConversation(WorldObject const& u, ObjectGuid const& guid) +{ + return u.GetMap()->GetConversation(guid); +} + Unit* ObjectAccessor::GetUnit(WorldObject const& u, ObjectGuid const& guid) { if (guid.IsPlayer()) diff --git a/src/server/game/Globals/ObjectAccessor.h b/src/server/game/Globals/ObjectAccessor.h index 08bf2bc3b58..6322c439a43 100644 --- a/src/server/game/Globals/ObjectAccessor.h +++ b/src/server/game/Globals/ObjectAccessor.h @@ -71,6 +71,7 @@ namespace ObjectAccessor TC_GAME_API Transport* GetTransport(ObjectGuid const& guid); TC_GAME_API DynamicObject* GetDynamicObject(WorldObject const& u, ObjectGuid const& guid); TC_GAME_API AreaTrigger* GetAreaTrigger(WorldObject const& u, ObjectGuid const& guid); + TC_GAME_API Conversation* GetConversation(WorldObject const& u, ObjectGuid const& guid); TC_GAME_API Unit* GetUnit(WorldObject const&, ObjectGuid const& guid); TC_GAME_API Creature* GetCreature(WorldObject const& u, ObjectGuid const& guid); TC_GAME_API Pet* GetPet(WorldObject const&, ObjectGuid const& guid); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index da458834b51..3c245e02a34 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -9758,7 +9758,7 @@ void ObjectMgr::LoadSceneTemplates() Field* fields = templates->Fetch(); uint32 sceneId = fields[0].GetUInt32(); - SceneTemplate& sceneTemplate = _sceneTemplateStore[sceneId]; + SceneTemplate& sceneTemplate = _sceneTemplateStore[sceneId]; sceneTemplate.SceneId = sceneId; sceneTemplate.PlaybackFlags = fields[1].GetUInt32(); sceneTemplate.ScenePackageId = fields[2].GetUInt32(); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 8bd95bf5e82..17231f628a2 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -24,6 +24,7 @@ #include "Bag.h" #include "Creature.h" #include "DynamicObject.h" +#include "Conversation.h" #include "GameObject.h" #include "TemporarySummon.h" #include "Corpse.h" diff --git a/src/server/game/Grids/GridDefines.h b/src/server/game/Grids/GridDefines.h index 4c89e90b66e..55e6ae1ab24 100644 --- a/src/server/game/Grids/GridDefines.h +++ b/src/server/game/Grids/GridDefines.h @@ -31,6 +31,7 @@ class GameObject; class Pet; class Player; class AreaTrigger; +class Conversation; #define MAX_NUMBER_OF_CELLS 8 @@ -58,8 +59,8 @@ class AreaTrigger; // Creature used instead pet to simplify *::Visit templates (not required duplicate code for Creature->Pet case) typedef TYPELIST_4(Player, Creature/*pets*/, Corpse/*resurrectable*/, DynamicObject/*farsight target*/) AllWorldObjectTypes; -typedef TYPELIST_5(GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/, AreaTrigger) AllGridObjectTypes; -typedef TYPELIST_6(Creature, GameObject, DynamicObject, Pet, Corpse, AreaTrigger) AllMapStoredObjectTypes; +typedef TYPELIST_6(GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/, AreaTrigger, Conversation) AllGridObjectTypes; +typedef TYPELIST_7(Creature, GameObject, DynamicObject, Pet, Corpse, AreaTrigger, Conversation) AllMapStoredObjectTypes; typedef GridRefManager<Corpse> CorpseMapType; typedef GridRefManager<Creature> CreatureMapType; @@ -67,6 +68,7 @@ typedef GridRefManager<DynamicObject> DynamicObjectMapType; typedef GridRefManager<GameObject> GameObjectMapType; typedef GridRefManager<Player> PlayerMapType; typedef GridRefManager<AreaTrigger> AreaTriggerMapType; +typedef GridRefManager<Conversation> ConversationMapType; enum GridMapTypeMask { @@ -76,7 +78,8 @@ enum GridMapTypeMask GRID_MAP_TYPE_MASK_GAMEOBJECT = 0x08, GRID_MAP_TYPE_MASK_PLAYER = 0x10, GRID_MAP_TYPE_MASK_AREATRIGGER = 0x20, - GRID_MAP_TYPE_MASK_ALL = 0x3F + GRID_MAP_TYPE_MASK_CONVERSATION = 0x40, + GRID_MAP_TYPE_MASK_ALL = 0x7F }; typedef Grid<Player, AllWorldObjectTypes, AllGridObjectTypes> GridType; diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index ebf94bbbc5c..3892db055ec 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -372,4 +372,4 @@ template void ObjectUpdater::Visit<Creature>(CreatureMapType&); template void ObjectUpdater::Visit<GameObject>(GameObjectMapType&); template void ObjectUpdater::Visit<DynamicObject>(DynamicObjectMapType&); template void ObjectUpdater::Visit<AreaTrigger>(AreaTriggerMapType &); - +template void ObjectUpdater::Visit<Conversation>(ConversationMapType &); diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index ce1e81468ab..35bf4942af2 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -26,6 +26,7 @@ #include "Corpse.h" #include "Object.h" #include "AreaTrigger.h" +#include "Conversation.h" #include "DynamicObject.h" #include "GameObject.h" #include "Player.h" @@ -121,6 +122,7 @@ namespace Trinity void Visit(DynamicObjectMapType &m) { updateObjects<DynamicObject>(m); } void Visit(CorpseMapType &m) { updateObjects<Corpse>(m); } void Visit(AreaTriggerMapType &m) { updateObjects<AreaTrigger>(m); } + void Visit(ConversationMapType &m) { updateObjects<Conversation>(m); } }; struct TC_GAME_API MessageDistDeliverer @@ -189,6 +191,7 @@ namespace Trinity void Visit(CorpseMapType &m); void Visit(DynamicObjectMapType &m); void Visit(AreaTriggerMapType &m); + void Visit(ConversationMapType &m); template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) { } }; @@ -210,6 +213,7 @@ namespace Trinity void Visit(CorpseMapType &m); void Visit(DynamicObjectMapType &m); void Visit(AreaTriggerMapType &m); + void Visit(ConversationMapType &m); template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) { } }; @@ -231,6 +235,7 @@ namespace Trinity void Visit(GameObjectMapType &m); void Visit(DynamicObjectMapType &m); void Visit(AreaTriggerMapType &m); + void Visit(ConversationMapType &m); template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) { } }; @@ -298,6 +303,15 @@ namespace Trinity i_do(itr->GetSource()); } + void Visit(ConversationMapType &m) + { + if (!(i_mapTypeMask & GRID_MAP_TYPE_MASK_CONVERSATION)) + return; + for (ConversationMapType::iterator itr = m.begin(); itr != m.end(); ++itr) + if (itr->GetSource()->IsInPhase(_searcher)) + i_do(itr->GetSource()); + } + template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) { } }; diff --git a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h index 0ab703caade..68a1c17bdb3 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h +++ b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h @@ -181,6 +181,29 @@ void Trinity::WorldObjectSearcher<Check>::Visit(AreaTriggerMapType &m) } template<class Check> +void Trinity::WorldObjectSearcher<Check>::Visit(ConversationMapType &m) +{ + if (!(i_mapTypeMask & GRID_MAP_TYPE_MASK_CONVERSATION)) + return; + + // already found + if (i_object) + return; + + for (ConversationMapType::iterator itr = m.begin(); itr != m.end(); ++itr) + { + if (!itr->GetSource()->IsInPhase(_searcher)) + continue; + + if (i_check(itr->GetSource())) + { + i_object = itr->GetSource(); + return; + } + } +} + +template<class Check> void Trinity::WorldObjectLastSearcher<Check>::Visit(GameObjectMapType &m) { if (!(i_mapTypeMask & GRID_MAP_TYPE_MASK_GAMEOBJECT)) @@ -277,6 +300,22 @@ void Trinity::WorldObjectLastSearcher<Check>::Visit(AreaTriggerMapType &m) } template<class Check> +void Trinity::WorldObjectLastSearcher<Check>::Visit(ConversationMapType &m) +{ + if (!(i_mapTypeMask & GRID_MAP_TYPE_MASK_CONVERSATION)) + return; + + for (ConversationMapType::iterator itr=m.begin(); itr != m.end(); ++itr) + { + if (!itr->GetSource()->IsInPhase(_searcher)) + continue; + + if (i_check(itr->GetSource())) + i_object = itr->GetSource(); + } +} + +template<class Check> void Trinity::WorldObjectListSearcher<Check>::Visit(PlayerMapType &m) { if (!(i_mapTypeMask & GRID_MAP_TYPE_MASK_PLAYER)) @@ -342,6 +381,17 @@ void Trinity::WorldObjectListSearcher<Check>::Visit(AreaTriggerMapType &m) i_objects.push_back(itr->GetSource()); } +template<class Check> +void Trinity::WorldObjectListSearcher<Check>::Visit(ConversationMapType &m) +{ + if (!(i_mapTypeMask & GRID_MAP_TYPE_MASK_CONVERSATION)) + return; + + for (ConversationMapType::iterator itr = m.begin(); itr != m.end(); ++itr) + if (i_check(itr->GetSource())) + i_objects.push_back(itr->GetSource()); +} + // Gameobject searchers template<class Check> diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp index eab7573f552..5534dff3e38 100644 --- a/src/server/game/Grids/ObjectGridLoader.cpp +++ b/src/server/game/Grids/ObjectGridLoader.cpp @@ -24,6 +24,7 @@ #include "DynamicObject.h" #include "Corpse.h" #include "AreaTrigger.h" +#include "Conversation.h" #include "World.h" #include "CellImpl.h" #include "CreatureAI.h" @@ -238,6 +239,7 @@ void ObjectGridCleaner::Visit(GridRefManager<T> &m) template void ObjectGridUnloader::Visit(CreatureMapType &); template void ObjectGridUnloader::Visit(GameObjectMapType &); template void ObjectGridUnloader::Visit(DynamicObjectMapType &); +template void ObjectGridUnloader::Visit(ConversationMapType &); template void ObjectGridUnloader::Visit(AreaTriggerMapType &); template void ObjectGridCleaner::Visit(CreatureMapType &); @@ -245,3 +247,4 @@ template void ObjectGridCleaner::Visit<GameObject>(GameObjectMapType &); template void ObjectGridCleaner::Visit<DynamicObject>(DynamicObjectMapType &); template void ObjectGridCleaner::Visit<Corpse>(CorpseMapType &); template void ObjectGridCleaner::Visit<AreaTrigger>(AreaTriggerMapType &); +template void ObjectGridCleaner::Visit<Conversation>(ConversationMapType &); diff --git a/src/server/game/Grids/ObjectGridLoader.h b/src/server/game/Grids/ObjectGridLoader.h index c83a6e7a2d1..71f81f96bf3 100644 --- a/src/server/game/Grids/ObjectGridLoader.h +++ b/src/server/game/Grids/ObjectGridLoader.h @@ -41,6 +41,7 @@ class TC_GAME_API ObjectGridLoader void Visit(CorpseMapType &) const { } void Visit(DynamicObjectMapType&) const { } void Visit(AreaTriggerMapType &) const { } + void Visit(ConversationMapType &) const { } void LoadN(void); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index ac2330e6f1e..9faf6961f45 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -21,6 +21,7 @@ #include "Battleground.h" #include "MMapFactory.h" #include "CellImpl.h" +#include "Conversation.h" #include "DisableMgr.h" #include "DynamicTree.h" #include "GridNotifiers.h" @@ -3037,6 +3038,9 @@ void Map::RemoveAllObjectsInRemoveList() case TYPEID_AREATRIGGER: RemoveFromMap((AreaTrigger*)obj, true); break; + case TYPEID_CONVERSATION: + RemoveFromMap((Conversation*)obj, true); + break; case TYPEID_GAMEOBJECT: { GameObject* go = obj->ToGameObject(); @@ -3185,12 +3189,14 @@ template TC_GAME_API bool Map::AddToMap(Creature*); template TC_GAME_API bool Map::AddToMap(GameObject*); template TC_GAME_API bool Map::AddToMap(DynamicObject*); template TC_GAME_API bool Map::AddToMap(AreaTrigger*); +template TC_GAME_API bool Map::AddToMap(Conversation*); template TC_GAME_API void Map::RemoveFromMap(Corpse*, bool); template TC_GAME_API void Map::RemoveFromMap(Creature*, bool); template TC_GAME_API void Map::RemoveFromMap(GameObject*, bool); template TC_GAME_API void Map::RemoveFromMap(DynamicObject*, bool); template TC_GAME_API void Map::RemoveFromMap(AreaTrigger*, bool); +template TC_GAME_API void Map::RemoveFromMap(Conversation*, bool); /* ******* Dungeon Instance Maps ******* */ @@ -3773,6 +3779,11 @@ AreaTrigger* Map::GetAreaTrigger(ObjectGuid const& guid) return _objectsStore.Find<AreaTrigger>(guid); } +Conversation* Map::GetConversation(ObjectGuid const& guid) +{ + return _objectsStore.Find<Conversation>(guid); +} + Corpse* Map::GetCorpse(ObjectGuid const& guid) { return _objectsStore.Find<Corpse>(guid); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 263d65788e8..0e614a89454 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -452,6 +452,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType> TempSummon* SummonCreature(uint32 entry, Position const& pos, SummonPropertiesEntry const* properties = NULL, uint32 duration = 0, Unit* summoner = NULL, uint32 spellId = 0, uint32 vehId = 0); void SummonCreatureGroup(uint8 group, std::list<TempSummon*>* list = NULL); AreaTrigger* GetAreaTrigger(ObjectGuid const& guid); + Conversation* GetConversation(ObjectGuid const& guid); Corpse* GetCorpse(ObjectGuid const& guid); Creature* GetCreature(ObjectGuid const& guid); DynamicObject* GetDynamicObject(ObjectGuid const& guid); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 98e3dd04450..f29f71b43f2 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -1241,7 +1241,7 @@ enum SpellEffectName SPELL_EFFECT_CREATE_SHIPMENT = 216, SPELL_EFFECT_UPGRADE_GARRISON = 217, SPELL_EFFECT_218 = 218, - SPELL_EFFECT_219 = 219, + SPELL_EFFECT_CREATE_CONVERSATION = 219, SPELL_EFFECT_ADD_GARRISON_FOLLOWER = 220, SPELL_EFFECT_221 = 221, SPELL_EFFECT_CREATE_HEIRLOOM_ITEM = 222, diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 80dbbf81bfe..39a5b705563 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -444,6 +444,7 @@ class TC_GAME_API Spell void EffectDestroyItem(SpellEffIndex effIndex); void EffectLearnGarrisonBuilding(SpellEffIndex effIndex); void EffectCreateGarrison(SpellEffIndex effIndex); + void EffectCreateConversation(SpellEffIndex effIndex); void EffectAddGarrisonFollower(SpellEffIndex effIndex); void EffectActivateGarrisonBuilding(SpellEffIndex effIndex); void EffectHealBattlePetPct(SpellEffIndex effIndex); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index e4e309ecf30..a1eb05d1da9 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -62,6 +62,7 @@ #include "MiscPackets.h" #include "SpellPackets.h" #include "TalentPackets.h" +#include "Conversation.h" pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= { @@ -284,7 +285,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectNULL, //216 SPELL_EFFECT_CREATE_SHIPMENT &Spell::EffectNULL, //217 SPELL_EFFECT_UPGRADE_GARRISON &Spell::EffectNULL, //218 SPELL_EFFECT_218 - &Spell::EffectNULL, //219 SPELL_EFFECT_219 + &Spell::EffectCreateConversation, //219 SPELL_EFFECT_CREATE_CONVERSATION &Spell::EffectAddGarrisonFollower, //220 SPELL_EFFECT_ADD_GARRISON_FOLLOWER &Spell::EffectNULL, //221 SPELL_EFFECT_221 &Spell::EffectCreateHeirloomItem, //222 SPELL_EFFECT_CREATE_HEIRLOOM_ITEM @@ -5659,6 +5660,17 @@ void Spell::EffectCreateGarrison(SpellEffIndex effIndex) unitTarget->ToPlayer()->CreateGarrison(GetEffect(effIndex)->MiscValue); } +void Spell::EffectCreateConversation(SpellEffIndex /*effIndex*/) +{ + if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) + return; + + if (!m_targets.HasDst()) + return; + + Conversation::CreateConversation(effectInfo->MiscValue, GetCaster(), destTarget->GetPosition(), { GetCaster()->GetGUID() }, GetSpellInfo()); +} + void Spell::EffectAddGarrisonFollower(SpellEffIndex effIndex) { if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 826cf0fece3..bef98d77775 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -938,7 +938,7 @@ SpellEffectInfo::StaticData SpellEffectInfo::_data[TOTAL_SPELL_EFFECTS] = {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 216 SPELL_EFFECT_CREATE_SHIPMENT {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 217 SPELL_EFFECT_UPGRADE_GARRISON {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 218 SPELL_EFFECT_218 - {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 219 SPELL_EFFECT_219 + {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 219 SPELL_EFFECT_CREATE_CONVERSATION {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 220 SPELL_EFFECT_ADD_GARRISON_FOLLOWER {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 221 SPELL_EFFECT_221 {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 222 SPELL_EFFECT_CREATE_HEIRLOOM_ITEM diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 5ced6e8bfab..02d6704ee4f 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -37,6 +37,7 @@ #include "CharacterTemplateDataStore.h" #include "Chat.h" #include "Config.h" +#include "ConversationDataStore.h" #include "CreatureAIRegistry.h" #include "CreatureGroups.h" #include "CreatureTextMgr.h" @@ -1835,6 +1836,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading AreaTrigger Templates..."); sAreaTriggerDataStore->LoadAreaTriggerTemplates(); + TC_LOG_INFO("server.loading", "Loading Conversation Templates..."); + sConversationDataStore->LoadConversationTemplates(); + TC_LOG_INFO("server.loading", "Loading Scenes Templates..."); sObjectMgr->LoadSceneTemplates(); diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 1419a5b3b74..bfa60398261 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -29,6 +29,7 @@ EndScriptData */ #include "Chat.h" #include "Cell.h" #include "CellImpl.h" +#include "Conversation.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "GossipDef.h" @@ -102,6 +103,7 @@ public: { "boundary", rbac::RBAC_PERM_COMMAND_DEBUG_BOUNDARY, false, &HandleDebugBoundaryCommand, "" }, { "raidreset", rbac::RBAC_PERM_COMMAND_INSTANCE_UNBIND, false, &HandleDebugRaidResetCommand, "" }, { "neargraveyard", rbac::RBAC_PERM_COMMAND_NEARGRAVEYARD, false, &HandleDebugNearGraveyard, "" }, + { "conversation" , rbac::RBAC_PERM_COMMAND_DEBUG_CONVERSATION, false, &HandleDebugConversationCommand, "" }, }; static std::vector<ChatCommand> commandTable = { @@ -1567,6 +1569,29 @@ public: return true; } + + static bool HandleDebugConversationCommand(ChatHandler* handler, char const* args) + { + if (!*args) + return false; + + char const* conversationEntryStr = strtok((char*)args, " "); + + if (!conversationEntryStr) + return false; + + uint32 conversationEntry = atoi(conversationEntryStr); + Player* target = handler->getSelectedPlayerOrSelf(); + + if (!target) + { + handler->SendSysMessage(LANG_PLAYER_NOT_FOUND); + handler->SetSentErrorMessage(true); + return false; + } + + return Conversation::CreateConversation(conversationEntry, target, *target, { target->GetGUID() }) != nullptr; + } }; void AddSC_debug_commandscript() diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 1b54aa4f03e..2732d6d8be7 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -29,6 +29,7 @@ EndScriptData */ #include "BattlegroundMgr.h" #include "CharacterTemplateDataStore.h" #include "Chat.h" +#include "ConversationDataStore.h" #include "CreatureTextMgr.h" #include "DisableMgr.h" #include "Language.h" @@ -81,6 +82,7 @@ public: { "command", rbac::RBAC_PERM_COMMAND_RELOAD_COMMAND, true, &HandleReloadCommandCommand, "" }, { "conditions", rbac::RBAC_PERM_COMMAND_RELOAD_CONDITIONS, true, &HandleReloadConditions, "" }, { "config", rbac::RBAC_PERM_COMMAND_RELOAD_CONFIG, true, &HandleReloadConfigCommand, "" }, + { "conversation_template", rbac::RBAC_PERM_COMMAND_RELOAD_CONVERSATION_TEMPLATE, true, &HandleReloadConversationTemplateCommand, "" }, { "creature_text", rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_TEXT, true, &HandleReloadCreatureText, "" }, { "creature_questender", rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_QUESTENDER, true, &HandleReloadCreatureQuestEnderCommand, "" }, { "creature_linked_respawn", rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_LINKED_RESPAWN, true, &HandleReloadLinkedRespawnCommand, "" }, @@ -1150,6 +1152,14 @@ public: return true; } + static bool HandleReloadConversationTemplateCommand(ChatHandler* handler, const char* /*args*/) + { + TC_LOG_INFO("misc", "Reloading conversation_* tables..."); + sConversationDataStore->LoadConversationTemplates(); + handler->SendGlobalGMSysMessage("Conversation templates reloaded."); + return true; + } + static bool HandleReloadRBACCommand(ChatHandler* handler, const char* /*args*/) { TC_LOG_INFO("misc", "Reloading RBAC tables..."); diff --git a/src/server/shared/Dynamic/TypeList.h b/src/server/shared/Dynamic/TypeList.h index a8dcea05bcc..9bc455587c8 100644 --- a/src/server/shared/Dynamic/TypeList.h +++ b/src/server/shared/Dynamic/TypeList.h @@ -35,11 +35,12 @@ struct TypeList }; // enough for now.. can be expand at any point in time as needed -#define TYPELIST_1(T1) TypeList<T1, TypeNull> -#define TYPELIST_2(T1, T2) TypeList<T1, TYPELIST_1(T2) > -#define TYPELIST_3(T1, T2, T3) TypeList<T1, TYPELIST_2(T2, T3) > -#define TYPELIST_4(T1, T2, T3, T4) TypeList<T1, TYPELIST_3(T2, T3, T4) > -#define TYPELIST_5(T1, T2, T3, T4, T5) TypeList<T1, TYPELIST_4(T2, T3, T4, T5) > -#define TYPELIST_6(T1, T2, T3, T4, T5, T6) TypeList<T1, TYPELIST_5(T2, T3, T4, T5, T6) > +#define TYPELIST_1(T1) TypeList<T1, TypeNull> +#define TYPELIST_2(T1, T2) TypeList<T1, TYPELIST_1(T2) > +#define TYPELIST_3(T1, T2, T3) TypeList<T1, TYPELIST_2(T2, T3) > +#define TYPELIST_4(T1, T2, T3, T4) TypeList<T1, TYPELIST_3(T2, T3, T4) > +#define TYPELIST_5(T1, T2, T3, T4, T5) TypeList<T1, TYPELIST_4(T2, T3, T4, T5) > +#define TYPELIST_6(T1, T2, T3, T4, T5, T6) TypeList<T1, TYPELIST_5(T2, T3, T4, T5, T6) > +#define TYPELIST_7(T1, T2, T3, T4, T5, T6, T7) TypeList<T1, TYPELIST_6(T2, T3, T4, T5, T6, T7) > #endif |