aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTraesh <traesh@farahlon.com>2017-12-17 14:40:04 +0100
committerShauren <shauren.trinity@gmail.com>2017-12-31 23:22:55 +0100
commit89c91c271b94f34076be7256378610a2a45c9ed2 (patch)
tree71a2d1751039924a66ebfdb33408695f0cd51d3b
parent5dd686c080723eeeefdabb50b867f9f662d5deb9 (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.sql5
-rw-r--r--sql/updates/world/master/9999_99_99_99_playerchoice.sql83
-rw-r--r--src/server/game/Accounts/RBAC.h1
-rw-r--r--src/server/game/Entities/Player/Player.cpp32
-rw-r--r--src/server/game/Entities/Player/Player.h2
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp216
-rw-r--r--src/server/game/Globals/ObjectMgr.h99
-rw-r--r--src/server/game/Handlers/QuestHandler.cpp5
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp5
-rw-r--r--src/server/game/Scripting/ScriptMgr.h4
-rw-r--r--src/server/game/Server/Packets/QuestPackets.cpp76
-rw-r--r--src/server/game/Server/Packets/QuestPackets.h30
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp4
-rw-r--r--src/server/game/Server/WorldSession.h2
-rw-r--r--src/server/game/Spells/Spell.h1
-rw-r--r--src/server/game/Spells/SpellEffects.cpp13
-rw-r--r--src/server/game/World/World.cpp6
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp13
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)