diff options
author | Traesh <traesh@farahlon.com> | 2017-12-17 14:40:04 +0100 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2017-12-31 23:22:55 +0100 |
commit | 89c91c271b94f34076be7256378610a2a45c9ed2 (patch) | |
tree | 71a2d1751039924a66ebfdb33408695f0cd51d3b | |
parent | 5dd686c080723eeeefdabb50b867f9f662d5deb9 (diff) |
Core/Quests: Implemented player choices
* Implemented SPELL_EFFECT_LAUNCH_QUEST_CHOICE
* Script hook for player choices
-rw-r--r-- | sql/updates/auth/master/9999_99_99_99_auth.sql | 5 | ||||
-rw-r--r-- | sql/updates/world/master/9999_99_99_99_playerchoice.sql | 83 | ||||
-rw-r--r-- | src/server/game/Accounts/RBAC.h | 1 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 32 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 2 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 216 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.h | 99 | ||||
-rw-r--r-- | src/server/game/Handlers/QuestHandler.cpp | 5 | ||||
-rw-r--r-- | src/server/game/Scripting/ScriptMgr.cpp | 5 | ||||
-rw-r--r-- | src/server/game/Scripting/ScriptMgr.h | 4 | ||||
-rw-r--r-- | src/server/game/Server/Packets/QuestPackets.cpp | 76 | ||||
-rw-r--r-- | src/server/game/Server/Packets/QuestPackets.h | 30 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Opcodes.cpp | 4 | ||||
-rw-r--r-- | src/server/game/Server/WorldSession.h | 2 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.h | 1 | ||||
-rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 13 | ||||
-rw-r--r-- | src/server/game/World/World.cpp | 6 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_debug.cpp | 13 |
18 files changed, 573 insertions, 24 deletions
diff --git a/sql/updates/auth/master/9999_99_99_99_auth.sql b/sql/updates/auth/master/9999_99_99_99_auth.sql new file mode 100644 index 00000000000..f673ceae849 --- /dev/null +++ b/sql/updates/auth/master/9999_99_99_99_auth.sql @@ -0,0 +1,5 @@ +INSERT INTO `rbac_permissions` (id, NAME) VALUES +(869, "Command: debug send playerchoice"); + +INSERT INTO `rbac_linked_permissions` (id, linkedId) VALUES +(192, 869); diff --git a/sql/updates/world/master/9999_99_99_99_playerchoice.sql b/sql/updates/world/master/9999_99_99_99_playerchoice.sql new file mode 100644 index 00000000000..4e4611428db --- /dev/null +++ b/sql/updates/world/master/9999_99_99_99_playerchoice.sql @@ -0,0 +1,83 @@ +DELETE FROM `command` WHERE `name` IN ('debug send playerchoice'); +INSERT INTO `command` (`name`, `permission`, `help`) VALUES +('debug send playerchoice', 869, 'Syntax: .debug send playerchoice $choiceId\r\nSend given choice to player.'); + +DROP TABLE IF EXISTS `playerchoice`; +CREATE TABLE `playerchoice` ( + `ChoiceID` INT (10) NOT NULL, + `Question` VARCHAR (255), + PRIMARY KEY (`ChoiceID`) +); + +INSERT INTO playerchoice VALUES (231, "TODO"); + +DROP TABLE IF EXISTS `playerchoice_locale`; +CREATE TABLE `playerchoice_locale` ( + `ChoiceID` INT (10) NOT NULL, + `locale` VARCHAR (4) NOT NULL, + `Question` VARCHAR (255), + PRIMARY KEY (`ChoiceID`, `locale`) +); + +INSERT INTO playerchoice_locale VALUES (231, "frFR", "Quelle spécialisation de chasseur de démons voulez-vous apprendre ?"); + +DROP TABLE IF EXISTS `playerchoice_response`; +CREATE TABLE `playerchoice_response` ( + `ChoiceID` INT (10) NOT NULL, + `ResponseID` INT (10) NOT NULL, + `ChoiceArtFileID` INT (10), + `Header` TEXT, + `Answer` TEXT, + `Description` TEXT, + `Confirmation` TEXT, + PRIMARY KEY (`ChoiceID`, `ResponseID`) +); + +INSERT INTO playerchoice_response VALUES +(231, 478, 1274664, "TODO", "TODO", "TODO", ""), +(231, 479, 1274665, "TODO", "TODO", "TODO", ""); + +DROP TABLE IF EXISTS `playerchoice_response_locale`; +CREATE TABLE `playerchoice_response_locale` ( + `ChoiceID` INT (10) NOT NULL, + `ResponseID` INT (10) NOT NULL, + `locale` VARCHAR (4) NOT NULL, + `Header` TEXT, + `Answer` TEXT, + `Description` TEXT, + `Confirmation` TEXT, + PRIMARY KEY (`ChoiceID`, `ResponseID`, `locale`) +); + +INSERT INTO playerchoice_response_locale VALUES +(231, 478, "frFR", "Dévastation", "Dévastation", "Conserver votre spécialisation : Dévastation. + +Les chasseurs de démons Dévastation peuvent utiliser Métamorphose pour se transformer en démons ailés. Ils se concentrent sur les dégâts en mêlée. + +Choisir Dévastation confère |cFFFF0000|Hspell:162264|h[Métamorphose]|h|r et |cFFFF0000|Hspell:185164|h[Maîtrise : Présence démoniaque]|h|r.", ""), +(231, 479, "frFR", "Vengeance", "Vengeance", "Changer de spécialisation : Vengeance. + +Les chasseurs de démons Vengeance peuvent utiliser Métamorphose pour se transformer en démons couverts de pointes. Ils se concentrent sur le rôle de tank. + +Choisir Vengeance confère |cFFFF0000|Hspell:187827|h[Métamorphose]|h|r et |cFFFF0000|Hspell:203747|h[Maîtrise : Gangresang]|h|r. + +Votre technique |cFFFF0000|Hspell:193921|h[Rayon accablant]|h|r est remplacée par |cFFFF0000|Hspell:203720|h[Pointes démoniaques]|h|r.", ""); + +DROP TABLE IF EXISTS `playerchoice_response_reward`; +CREATE TABLE `playerchoice_response_reward` ( + `ChoiceID` INT (10) NOT NULL, + `ResponseID` INT (10) NOT NULL, + `TitleID` INT (10), + `PackageID` INT (10), + `SkillLineID` INT (10) UNSIGNED, + `SkillPointCount` INT (10) UNSIGNED, + `ArenaPointCount` INT (10) UNSIGNED, + `HonorPointCount` INT (10) UNSIGNED, + `Money` BIGINT (20) UNSIGNED, + `Xp` INT (10) UNSIGNED, + PRIMARY KEY (`ChoiceID`, `ResponseID`) +); + +INSERT INTO playerchoice_response_reward VALUES +(231, 478, 0, 0, 0, 0, 0, 0, 0, 0), +(231, 479, 0, 0, 0, 0, 0, 0, 0, 0); diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index f3cd06fab33..a7b5b4090c3 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -773,6 +773,7 @@ enum RBACPermissions RBAC_PERM_COMMAND_LIST_SPAWNPOINTS = 866, // reserved RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING_LOCALE = 867, // reserved RBAC_PERM_COMMAND_MODIFY_POWER = 868, + RBAC_PERM_COMMAND_DEBUG_SEND_PLAYER_CHOICE = 869, // // IF YOU ADD NEW PERMISSIONS, ADD THEM IN 3.3.5 BRANCH AS WELL! // diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index e9c53e161d5..1e084d87850 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -27121,6 +27121,38 @@ void Player::SendMovementSetCollisionHeight(float height) SendMessageToSet(updateCollisionHeight.Write(), false); } +void Player::SendPlayerChoice(ObjectGuid sender, uint32 choiceID) +{ + PlayerChoice const* playerChoice = sObjectMgr->GetPlayerChoice(choiceID); + if (!playerChoice) + return; + + PlayerChoice localizedPlayerChoice = *playerChoice; + + LocaleConstant locale = GetSession()->GetSessionDbLocaleIndex(); + if (locale != DEFAULT_LOCALE) + { + if (PlayerChoiceLocale const* playerChoiceLocale = sObjectMgr->GetPlayerChoiceLocale(localizedPlayerChoice.ChoiceId)) + sObjectMgr->GetLocaleString(playerChoiceLocale->Question, locale, localizedPlayerChoice.Question); + + for (auto& playerChoiceResponse : localizedPlayerChoice.Responses) + { + if (PlayerChoiceResponseLocale const* playerChoiceResponseLocale = sObjectMgr->GetPlayerChoiceResponseLocale(localizedPlayerChoice.ChoiceId, playerChoiceResponse.second.ResponseID)) + { + sObjectMgr->GetLocaleString(playerChoiceResponseLocale->Header, locale, playerChoiceResponse.second.Header); + sObjectMgr->GetLocaleString(playerChoiceResponseLocale->Answer, locale, playerChoiceResponse.second.Answer); + sObjectMgr->GetLocaleString(playerChoiceResponseLocale->Description, locale, playerChoiceResponse.second.Description); + sObjectMgr->GetLocaleString(playerChoiceResponseLocale->Confirmation, locale, playerChoiceResponse.second.Confirmation); + } + } + } + + WorldPackets::Quest::DisplayPlayerChoice displayPlayerChoice; + displayPlayerChoice.Choice = &localizedPlayerChoice; + displayPlayerChoice.SenderGUID = sender; + SendDirectMessage(displayPlayerChoice.Write()); +} + float Player::GetCollisionHeight(bool mounted) const { if (mounted) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 4d111da3c0d..1391ad7e532 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2338,6 +2338,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> SceneMgr& GetSceneMgr() { return m_sceneMgr; } RestMgr& GetRestMgr() const { return *_restMgr; } + void SendPlayerChoice(ObjectGuid sender, uint32 choiceID); + protected: // Gamemaster whisper whitelist GuidList WhisperList; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index a0481164452..cc8215b75b0 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -35,6 +35,7 @@ #include "LootMgr.h" #include "Mail.h" #include "MapManager.h" +#include "MiscPackets.h" #include "MotionMaster.h" #include "ObjectAccessor.h" #include "ObjectDefines.h" @@ -9788,6 +9789,40 @@ bool ObjectMgr::GetRealmName(uint32 realmId, std::string& name, std::string& nor return false; } +uint8 ObjectMgr::GetRaceExpansionRequirement(uint8 race) const +{ + auto itr = _raceExpansionRequirementStore.find(race); + if (itr != _raceExpansionRequirementStore.end()) + return itr->second; + return EXPANSION_CLASSIC; +} + +uint8 ObjectMgr::GetClassExpansionRequirement(uint8 class_) const +{ + auto itr = _classExpansionRequirementStore.find(class_); + if (itr != _classExpansionRequirementStore.end()) + return itr->second; + return EXPANSION_CLASSIC; +} + +SceneTemplate const* ObjectMgr::GetSceneTemplate(uint32 sceneId) const +{ + auto itr = _sceneTemplateStore.find(sceneId); + if (itr != _sceneTemplateStore.end()) + return &itr->second; + + return nullptr; +} + +PlayerChoice const* ObjectMgr::GetPlayerChoice(int32 choiceId) const +{ + auto itr = _playerChoiceContainer.find(choiceId); + if (itr != _playerChoiceContainer.end()) + return &itr->second; + + return nullptr; +} + void ObjectMgr::LoadGameObjectQuestItems() { uint32 oldMSTime = getMSTime(); @@ -9908,3 +9943,184 @@ void ObjectMgr::LoadSceneTemplates() TC_LOG_INFO("server.loading", ">> Loaded %u scene templates in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } + +void ObjectMgr::LoadPlayerChoices() +{ + uint32 oldMSTime = getMSTime(); + _playerChoiceContainer.clear(); + + QueryResult choices = WorldDatabase.Query("SELECT ChoiceID, Question FROM playerchoice"); + + if (!choices) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 player choices. DB table `playerchoice` is empty."); + return; + } + + uint32 responseCount = 0; + uint32 rewardCount = 0; + + do + { + Field* fields = choices->Fetch(); + + int32 choiceId = fields[0].GetInt32(); + + PlayerChoice& choice = _playerChoiceContainer[choiceId]; + choice.ChoiceId = choiceId; + choice.Question = fields[1].GetString(); + } while (choices->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " player choices in %u ms.", _playerChoiceContainer.size(), GetMSTimeDiffToNow(oldMSTime)); + + QueryResult responses = WorldDatabase.Query("SELECT ChoiceID, ResponseID, ChoiceArtFileID, Header, Answer, Description, Confirmation FROM playerchoice_response"); + if (responses) + { + do + { + ++responseCount; + Field* fields = responses->Fetch(); + + int32 choiceId = fields[0].GetInt32(); + int32 ResponseID = fields[1].GetInt32(); + + if (_playerChoiceContainer.find(choiceId) == _playerChoiceContainer.end()) + { + TC_LOG_ERROR("sql.sql", "Table `playerchoice_response` has nonexistent choiceId: %u (ResponseID : %u), skipped", choiceId, ResponseID); + continue; + } + + PlayerChoice& choice = _playerChoiceContainer[choiceId]; + PlayerChoiceResponse& response = choice.Responses[ResponseID]; + + response.ResponseID = ResponseID; + response.ChoiceArtFileID = fields[2].GetInt32(); + response.Header = fields[3].GetString(); + response.Answer = fields[4].GetString(); + response.Description = fields[5].GetString(); + response.Confirmation = fields[6].GetString(); + } while (responses->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u player choices response in %u ms.", responseCount, GetMSTimeDiffToNow(oldMSTime)); + } + else + { + TC_LOG_INFO("server.loading", ">> Loaded 0 player choices responses. DB table `playerchoice_response` is empty."); + } + + QueryResult rewards = WorldDatabase.Query("SELECT ChoiceID, ResponseID, TitleID, PackageID, SkillLineID, SkillPointCount, ArenaPointCount, HonorPointCount, Money, Xp FROM playerchoice_response_reward"); + + if (rewards) + { + do + { + ++rewardCount; + Field* fields = rewards->Fetch(); + + int32 choiceId = fields[0].GetInt32(); + int32 ResponseID = fields[1].GetInt32(); + + if (_playerChoiceContainer.find(choiceId) == _playerChoiceContainer.end()) + { + TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` has nonexistent choiceId: %u (ResponseID : %u), skipped", choiceId, ResponseID); + continue; + } + + PlayerChoice& choice = _playerChoiceContainer[choiceId]; + + if (choice.Responses.find(ResponseID) == choice.Responses.end()) + { + TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` has nonexistent ResponseID: %u for choiceId %u, skipped", ResponseID, choiceId); + continue; + } + + PlayerChoiceResponse& response = choice.Responses[ResponseID]; + + PlayerChoiceResponseReward reward; + + reward.TitleID = fields[2].GetInt32(); + reward.PackageID = fields[3].GetInt32(); + reward.SkillLineID = fields[4].GetUInt32(); + reward.SkillPointCount = fields[5].GetUInt32(); + reward.ArenaPointCount = fields[6].GetUInt32(); + reward.HonorPointCount = fields[7].GetUInt32(); + reward.Money = fields[8].GetUInt64(); + reward.Xp = fields[9].GetUInt32(); + + response.Reward = reward; + + } while (rewards->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u player choices reward in %u ms.", rewardCount, GetMSTimeDiffToNow(oldMSTime)); + } + else + { + TC_LOG_INFO("server.loading", ">> Loaded 0 player choices responses reward. DB table `playerchoice_response_reward` is empty."); + } +} + +void ObjectMgr::LoadPlayerChoicesLocale() +{ + uint32 oldMSTime = getMSTime(); + + // need for reload case + _playerChoiceLocaleContainer.clear(); + _playerChoiceResponseLocaleContainer.clear(); + + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT ChoiceID, locale, Question FROM playerchoice_locale"); + if (result) + { + do + { + Field* fields = result->Fetch(); + + uint32 ChoiceID = fields[0].GetUInt32(); + std::string localeName = fields[1].GetString(); + std::string question = fields[2].GetString(); + + PlayerChoiceLocale& data = _playerChoiceLocaleContainer[ChoiceID]; + LocaleConstant locale = GetLocaleByName(localeName); + if (locale == LOCALE_enUS) + continue; + + AddLocaleString(question, locale, data.Question); + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " Player Choice locale strings in %u ms", _playerChoiceLocaleContainer.size(), GetMSTimeDiffToNow(oldMSTime)); + } + + oldMSTime = getMSTime(); + + // 0 1 2 3 4 5 6 + result = WorldDatabase.Query("SELECT ChoiceID, ResponseID, locale, Header, Answer, Description, Confirmation FROM playerchoice_response_locale"); + if (result) + { + do + { + Field* fields = result->Fetch(); + + uint32 ChoiceID = fields[0].GetUInt32(); + uint32 ResponseID = fields[1].GetUInt32(); + std::string localeName = fields[2].GetString(); + std::string Header = fields[3].GetString(); + std::string Answer = fields[4].GetString(); + std::string Description = fields[5].GetString(); + std::string Confirmation = fields[6].GetString(); + + std::pair<uint32, uint32> pair = std::make_pair(ChoiceID, ResponseID); + + PlayerChoiceResponseLocale& data = _playerChoiceResponseLocaleContainer[pair]; + LocaleConstant locale = GetLocaleByName(localeName); + if (locale == LOCALE_enUS) + continue; + + AddLocaleString(Header, locale, data.Header); + AddLocaleString(Answer, locale, data.Answer); + AddLocaleString(Description, locale, data.Description); + AddLocaleString(Confirmation, locale, data.Confirmation); + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " Player Choice Response locale strings in %u ms", _playerChoiceResponseLocaleContainer.size(), GetMSTimeDiffToNow(oldMSTime)); + } +} diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index cfbe5f05efe..043575b492c 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -516,6 +516,22 @@ struct PointOfInterestLocale typedef std::unordered_map<uint32, PointOfInterestLocale> PointOfInterestLocaleContainer; +struct PlayerChoiceResponseLocale +{ + std::vector<std::string> Header; + std::vector<std::string> Answer; + std::vector<std::string> Description; + std::vector<std::string> Confirmation; +}; + +struct PlayerChoiceLocale +{ + std::vector<std::string> Question; +}; + +typedef std::unordered_map<uint32, PlayerChoiceLocale> PlayerChoiceLocaleContainer; +typedef std::unordered_map<std::pair<uint32, uint32>, PlayerChoiceResponseLocale> PlayerChoiceResponseLocaleContainer; + typedef std::unordered_map<uint32, TrinityString> TrinityStringContainer; typedef std::multimap<uint32, uint32> QuestRelations; // unit/go -> quest @@ -752,6 +768,45 @@ struct SceneTemplate typedef std::unordered_map<uint32, SceneTemplate> SceneTemplateContainer; +struct PlayerChoiceResponseReward +{ + int32 TitleID; + int32 PackageID; + int32 SkillLineID; + uint32 SkillPointCount; + uint32 ArenaPointCount; + uint32 HonorPointCount; + uint64 Money; + uint32 Xp; + + //std::vector<Item> Items; + //std::vector<Currency> Currency; + //std::vector<Faction> Faction; + //std::vector<ItemChoice> ItemChoice; +}; + +struct PlayerChoiceResponse +{ + int32 ResponseID; + int32 ChoiceArtFileID; + + std::string Header; + std::string Answer; + std::string Description; + std::string Confirmation; + + Optional<PlayerChoiceResponseReward> Reward; +}; + +struct PlayerChoice +{ + int32 ChoiceId; + std::string Question; + std::unordered_map<int32 /*ResponseID*/, PlayerChoiceResponse> Responses; +}; + +typedef std::unordered_map<int32 /*choiceId*/, PlayerChoice> PlayerChoiceContainer; + enum SkillRangeType { SKILL_RANGE_LANGUAGE, // 300..300 @@ -1175,6 +1230,9 @@ class TC_GAME_API ObjectMgr void LoadSceneTemplates(); + void LoadPlayerChoices(); + void LoadPlayerChoicesLocale(); + std::string GeneratePetName(uint32 entry); uint32 GetBaseXP(uint8 level); uint32 GetXPForLevel(uint8 level) const; @@ -1326,6 +1384,18 @@ class TC_GAME_API ObjectMgr if (itr == _pointOfInterestLocaleStore.end()) return nullptr; return &itr->second; } + PlayerChoiceLocale const* GetPlayerChoiceLocale(uint32 ChoiceID) const + { + PlayerChoiceLocaleContainer::const_iterator itr = _playerChoiceLocaleContainer.find(ChoiceID); + if (itr == _playerChoiceLocaleContainer.end()) return nullptr; + return &itr->second; + } + PlayerChoiceResponseLocale const* GetPlayerChoiceResponseLocale(uint32 ChoiceID, uint32 ResponseID) const + { + PlayerChoiceResponseLocaleContainer::const_iterator itr = _playerChoiceResponseLocaleContainer.find(std::make_pair(ChoiceID, ResponseID)); + if (itr == _playerChoiceResponseLocaleContainer.end()) return nullptr; + return &itr->second; + } GameObjectData const* GetGOData(ObjectGuid::LowType guid) const { @@ -1488,31 +1558,14 @@ class TC_GAME_API ObjectMgr bool GetRealmName(uint32 realmId, std::string& name, std::string& normalizedName) const; std::unordered_map<uint8, uint8> const& GetRaceExpansionRequirements() const { return _raceExpansionRequirementStore; } - uint8 GetRaceExpansionRequirement(uint8 race) const - { - auto itr = _raceExpansionRequirementStore.find(race); - if (itr != _raceExpansionRequirementStore.end()) - return itr->second; - return EXPANSION_CLASSIC; - } + uint8 GetRaceExpansionRequirement(uint8 race) const; std::unordered_map<uint8, uint8> const& GetClassExpansionRequirements() const { return _classExpansionRequirementStore; } - uint8 GetClassExpansionRequirement(uint8 class_) const - { - auto itr = _classExpansionRequirementStore.find(class_); - if (itr != _classExpansionRequirementStore.end()) - return itr->second; - return EXPANSION_CLASSIC; - } + uint8 GetClassExpansionRequirement(uint8 class_) const; - SceneTemplate const* GetSceneTemplate(uint32 sceneId) const - { - auto itr = _sceneTemplateStore.find(sceneId); - if (itr != _sceneTemplateStore.end()) - return &itr->second; + SceneTemplate const* GetSceneTemplate(uint32 sceneId) const; - return nullptr; - } + PlayerChoice const* GetPlayerChoice(int32 choiceId) const; private: // first free id for selected id type @@ -1647,6 +1700,7 @@ class TC_GAME_API ObjectMgr GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore; /// Stores temp summon data grouped by summoner's entry, summoner's type and group id TempSummonDataContainer _tempSummonDataStore; + PlayerChoiceContainer _playerChoiceContainer; ItemTemplateContainer _itemTemplateStore; QuestTemplateLocaleContainer _questTemplateLocaleStore; @@ -1657,6 +1711,9 @@ class TC_GAME_API ObjectMgr GossipMenuItemsLocaleContainer _gossipMenuItemsLocaleStore; PointOfInterestLocaleContainer _pointOfInterestLocaleStore; + PlayerChoiceLocaleContainer _playerChoiceLocaleContainer; + PlayerChoiceResponseLocaleContainer _playerChoiceResponseLocaleContainer; + TrinityStringContainer _trinityStringStore; CacheVendorItemContainer _cacheVendorItemStore; diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index fedd4b4f072..68a71e5dd5e 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -673,3 +673,8 @@ void WorldSession::HandleRequestWorldQuestUpdate(WorldPackets::Quest::RequestWor SendPacket(response.Write()); } + +void WorldSession::HandlePlayerChoiceResponse(WorldPackets::Quest::PlayerChoiceResponse& packet) +{ + sScriptMgr->OnPlayerChoiceResponse(GetPlayer(), packet.ChoiceID, packet.ResponseID); +} diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 883c4c2c68d..4b8d5545588 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -2297,6 +2297,11 @@ void ScriptMgr::OnMovieComplete(Player* player, uint32 movieId) FOREACH_SCRIPT(PlayerScript)->OnMovieComplete(player, movieId); } +void ScriptMgr::OnPlayerChoiceResponse(Player* player, uint32 choiceID, uint32 responseID) +{ + FOREACH_SCRIPT(PlayerScript)->OnPlayerChoiceResponse(player, choiceID, responseID); +} + // Account void ScriptMgr::OnAccountLogin(uint32 accountId) { diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 7d3b30baddb..e27a68b7508 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -755,6 +755,9 @@ class TC_GAME_API PlayerScript : public UnitScript // Called when a player completes a movie virtual void OnMovieComplete(Player* /*player*/, uint32 /*movieId*/) { } + + // Called when a player choose a response from a PlayerChoice + virtual void OnPlayerChoiceResponse(Player* /*player*/, uint32 /*choiceID*/, uint32 /*responseID*/) { } }; class TC_GAME_API AccountScript : public ScriptObject @@ -1143,6 +1146,7 @@ class TC_GAME_API ScriptMgr void OnPlayerUpdateZone(Player* player, uint32 newZone, uint32 newArea); void OnQuestStatusChange(Player* player, uint32 questId); void OnMovieComplete(Player* player, uint32 movieId); + void OnPlayerChoiceResponse(Player* player, uint32 choiceID, uint32 responseID); public: /* AccountScript */ diff --git a/src/server/game/Server/Packets/QuestPackets.cpp b/src/server/game/Server/Packets/QuestPackets.cpp index 32f9da96a1b..d076315fde3 100644 --- a/src/server/game/Server/Packets/QuestPackets.cpp +++ b/src/server/game/Server/Packets/QuestPackets.cpp @@ -599,3 +599,79 @@ WorldPacket const* WorldPackets::Quest::WorldQuestUpdate::Write() return &_worldPacket; } + +ByteBuffer& operator<<(ByteBuffer& data, PlayerChoiceResponse const& response) +{ + data << int32(response.ResponseID); + data << int32(response.ChoiceArtFileID); + + data.WriteBits(response.Answer.length(), 9); + data.WriteBits(response.Header.length(), 9); + data.WriteBits(response.Description.length(), 11); + data.WriteBits(response.Confirmation.length(), 7); + + data.WriteBit(response.Reward.is_initialized()); + data.FlushBits(); + + if (response.Reward.is_initialized()) + data << (*response.Reward); + + data.WriteString(response.Answer); + data.WriteString(response.Header); + data.WriteString(response.Description); + data.WriteString(response.Confirmation); + return data; +} + +ByteBuffer& operator<<(ByteBuffer& data, PlayerChoiceResponseReward const& reward) +{ + data << int32(reward.TitleID); + data << int32(reward.PackageID); + data << int32(reward.SkillLineID); + data << uint32(reward.SkillPointCount); + data << uint32(reward.ArenaPointCount); + data << uint32(reward.HonorPointCount); + data << uint64(reward.Money); + data << uint32(reward.Xp); + + data << uint32(0); // itemCount + data << uint32(0); // currencyCount + data << uint32(0); // factionCount + data << uint32(0); // itemChoiceCount + + /*for (var i = 0u; i < itemCount; ++i) + ReadPlayerChoiceResponseRewardEntry(packet, "Item", i); + + for (var i = 0u; i < currencyCount; ++i) + ReadPlayerChoiceResponseRewardEntry(packet, "Currency", i); + + for (var i = 0u; i < factionCount; ++i) + ReadPlayerChoiceResponseRewardEntry(packet, "Faction", i); + + for (var i = 0u; i < itemChoiceCount; ++i) + ReadPlayerChoiceResponseRewardEntry(packet, "ItemChoice", i);*/ + + return data; +} + +WorldPacket const* WorldPackets::Quest::DisplayPlayerChoice::Write() +{ + _worldPacket << int32(Choice->ChoiceId); + _worldPacket << uint32(Choice->Responses.size()); + _worldPacket << SenderGUID; + _worldPacket.WriteBits(Choice->Question.length(), 8); + _worldPacket.WriteBit(CloseChoiceFrame); + _worldPacket.FlushBits(); + + for (auto response : Choice->Responses) + _worldPacket << response.second; + + _worldPacket.WriteString(Choice->Question); + return &_worldPacket; +} + +void WorldPackets::Quest::PlayerChoiceResponse::Read() +{ + _worldPacket >> ChoiceID; + _worldPacket >> ResponseID; +} diff --git a/src/server/game/Server/Packets/QuestPackets.h b/src/server/game/Server/Packets/QuestPackets.h index 955571f79ab..a087c5de241 100644 --- a/src/server/game/Server/Packets/QuestPackets.h +++ b/src/server/game/Server/Packets/QuestPackets.h @@ -23,6 +23,10 @@ #include "QuestDef.h" #include "ObjectGuid.h" +struct PlayerChoice; +struct PlayerChoiceResponse; +struct PlayerChoiceResponseReward; + namespace WorldPackets { namespace Quest @@ -633,9 +637,35 @@ namespace WorldPackets std::vector<WorldQuestUpdateInfo> WorldQuestUpdates; }; + + class DisplayPlayerChoice final : public ServerPacket + { + public: + DisplayPlayerChoice() : ServerPacket(SMSG_DISPLAY_PLAYER_CHOICE) { } + + WorldPacket const* Write() override; + + ObjectGuid SenderGUID; + PlayerChoice const* Choice; + bool CloseChoiceFrame = false; + }; + + class PlayerChoiceResponse final : public ClientPacket + { + public: + PlayerChoiceResponse(WorldPacket&& packet) : ClientPacket(CMSG_CHOICE_RESPONSE, std::move(packet)) { } + + void Read() override; + + int32 ChoiceID; + int32 ResponseID; + }; } } +ByteBuffer& operator<<(ByteBuffer& data, PlayerChoiceResponse const& response); +ByteBuffer& operator<<(ByteBuffer& data, PlayerChoiceResponseReward const& reward); + ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Quest::QuestRewards const& questRewards); ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Quest::QuestGiverOfferReward const& offer); diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 5796215cbe8..08d88187ddb 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -322,7 +322,7 @@ void OpcodeTable::Initialize() DEFINE_HANDLER(CMSG_CHAT_UNREGISTER_ALL_ADDON_PREFIXES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleUnregisterAllAddonPrefixesOpcode); DEFINE_HANDLER(CMSG_CHECK_RAF_EMAIL_ENABLED, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL); DEFINE_HANDLER(CMSG_CHECK_WOW_TOKEN_VETERAN_ELIGIBILITY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL); - DEFINE_HANDLER(CMSG_CHOICE_RESPONSE, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL); + DEFINE_HANDLER(CMSG_CHOICE_RESPONSE, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandlePlayerChoiceResponse); DEFINE_HANDLER(CMSG_CLEAR_RAID_MARKER, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleClearRaidMarker); DEFINE_HANDLER(CMSG_CLEAR_TRADE_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleClearTradeItemOpcode); DEFINE_HANDLER(CMSG_CLIENT_PORT_GRAVEYARD, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandlePortGraveyard); @@ -1108,7 +1108,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_DISMOUNT_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_DISPEL_FAILED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_DISPLAY_GAME_ERROR, STATUS_NEVER, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_DISPLAY_PLAYER_CHOICE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_DISPLAY_PLAYER_CHOICE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_DISPLAY_PROMOTION, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_DISPLAY_QUEST_POPUP, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_DISPLAY_TOAST, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 283a2dab146..5d680a1dab1 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -596,6 +596,7 @@ namespace WorldPackets class QuestPushResult; class PushQuestToParty; class RequestWorldQuestUpdate; + class PlayerChoiceResponse; } namespace RaF @@ -1442,6 +1443,7 @@ class TC_GAME_API WorldSession void HandlePushQuestToParty(WorldPackets::Quest::PushQuestToParty& packet); void HandleQuestPushResult(WorldPackets::Quest::QuestPushResult& packet); void HandleRequestWorldQuestUpdate(WorldPackets::Quest::RequestWorldQuestUpdate& packet); + void HandlePlayerChoiceResponse(WorldPackets::Quest::PlayerChoiceResponse& packet); void HandleChatMessageOpcode(WorldPackets::Chat::ChatMessage& chatMessage); void HandleChatMessageWhisperOpcode(WorldPackets::Chat::ChatMessageWhisper& chatMessageWhisper); diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 666e270dfb0..cadbfc53ed1 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -468,6 +468,7 @@ class TC_GAME_API Spell void EffectActivateGarrisonBuilding(SpellEffIndex effIndex); void EffectHealBattlePetPct(SpellEffIndex effIndex); void EffectEnableBattlePets(SpellEffIndex effIndex); + void EffectLaunchQuestChoice(SpellEffIndex effIndex); void EffectUncageBattlePet(SpellEffIndex effIndex); void EffectCreateHeirloomItem(SpellEffIndex effIndex); void EffectUpgradeHeirloom(SpellEffIndex effIndex); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 2c0a91c4277..feed2c9cbc6 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -278,7 +278,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectNULL, //202 SPELL_EFFECT_202 &Spell::EffectNULL, //203 SPELL_EFFECT_203 &Spell::EffectNULL, //204 SPELL_EFFECT_CHANGE_BATTLEPET_QUALITY - &Spell::EffectNULL, //205 SPELL_EFFECT_LAUNCH_QUEST_CHOICE + &Spell::EffectLaunchQuestChoice, //205 SPELL_EFFECT_LAUNCH_QUEST_CHOICE &Spell::EffectNULL, //206 SPELL_EFFECT_ALTER_ITEM &Spell::EffectNULL, //207 SPELL_EFFECT_LAUNCH_QUEST_TASK &Spell::EffectNULL, //208 SPELL_EFFECT_208 @@ -5666,6 +5666,17 @@ void Spell::EffectEnableBattlePets(SpellEffIndex /*effIndex*/) plr->GetSession()->GetBattlePetMgr()->UnlockSlot(0); } +void Spell::EffectLaunchQuestChoice(SpellEffIndex effIndex) +{ + if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) + return; + + if (!unitTarget || !unitTarget->IsPlayer()) + return; + + unitTarget->ToPlayer()->SendPlayerChoice(GetCaster()->GetGUID(), GetEffect(effIndex)->MiscValue); +} + void Spell::EffectUncageBattlePet(SpellEffIndex /*effIndex*/) { if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 3d795297b71..e3ee7ea9cd2 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1851,6 +1851,12 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Scenes Templates..."); sObjectMgr->LoadSceneTemplates(); + TC_LOG_INFO("server.loading", "Loading Player Choices..."); + sObjectMgr->LoadPlayerChoices(); + + TC_LOG_INFO("server.loading", "Loading Player Choices Locales..."); + sObjectMgr->LoadPlayerChoicesLocale(); + CharacterDatabaseCleaner::CleanDatabase(); TC_LOG_INFO("server.loading", "Loading the max pet number..."); diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index ec9c4811e4c..57ae2f6ad20 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -71,6 +71,7 @@ public: { "sellerror", rbac::RBAC_PERM_COMMAND_DEBUG_SEND_SELLERROR, false, &HandleDebugSendSellErrorCommand, "" }, { "setphaseshift", rbac::RBAC_PERM_COMMAND_DEBUG_SEND_SETPHASESHIFT, false, &HandleDebugSendSetPhaseShiftCommand, "" }, { "spellfail", rbac::RBAC_PERM_COMMAND_DEBUG_SEND_SPELLFAIL, false, &HandleDebugSendSpellFailCommand, "" }, + { "playerchoice", rbac::RBAC_PERM_COMMAND_DEBUG_SEND_PLAYER_CHOICE, false, &HandleDebugSendPlayerChoiceCommand, "" }, }; static std::vector<ChatCommand> debugCommandTable = { @@ -245,6 +246,18 @@ public: return true; } + static bool HandleDebugSendPlayerChoiceCommand(ChatHandler* handler, char const* args) + { + if (!*args) + return false; + + int32 choiceId = atoi(args); + Player* player = handler->GetSession()->GetPlayer(); + + player->SendPlayerChoice(player->GetGUID(), choiceId); + return true; + } + static bool HandleDebugSendEquipErrorCommand(ChatHandler* handler, char const* args) { if (!*args) |