diff options
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/game/Entities/Creature/GossipDef.h | 2 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 176 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/Player.h | 3 | ||||
| -rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 341 | ||||
| -rw-r--r-- | src/server/game/Globals/ObjectMgr.h | 99 | ||||
| -rw-r--r-- | src/server/game/Handlers/QuestHandler.cpp | 61 | ||||
| -rw-r--r-- | src/server/game/Scripting/ScriptMgr.cpp | 4 | ||||
| -rw-r--r-- | src/server/game/Scripting/ScriptMgr.h | 4 | ||||
| -rw-r--r-- | src/server/game/Server/Packets/QuestPackets.cpp | 97 | ||||
| -rw-r--r-- | src/server/game/Server/Packets/QuestPackets.h | 54 | ||||
| -rw-r--r-- | src/server/game/Server/WorldSession.h | 4 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 4 | 
12 files changed, 582 insertions, 267 deletions
diff --git a/src/server/game/Entities/Creature/GossipDef.h b/src/server/game/Entities/Creature/GossipDef.h index e15eb506a0a..0d8b0e81001 100644 --- a/src/server/game/Entities/Creature/GossipDef.h +++ b/src/server/game/Entities/Creature/GossipDef.h @@ -243,10 +243,12 @@ class InteractionData          {              SourceGuid.Clear();              TrainerId = 0; +            PlayerChoiceId = 0;          }          ObjectGuid SourceGuid;          uint32 TrainerId; +        uint32 PlayerChoiceId;  };  class TC_GAME_API PlayerMenu diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 1e084d87850..5c06fe68027 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -15066,6 +15066,49 @@ bool Player::CanSelectQuestPackageItem(QuestPackageItemEntry const* questPackage      return false;  } +void Player::RewardQuestPackage(uint32 questPackageId, uint32 onlyItemId /*= 0*/) +{ +    bool hasFilteredQuestPackageReward = false; +    if (std::vector<QuestPackageItemEntry const*> const* questPackageItems = sDB2Manager.GetQuestPackageItems(questPackageId)) +    { +        for (QuestPackageItemEntry const* questPackageItem : *questPackageItems) +        { +            if (onlyItemId && questPackageItem->ItemID != onlyItemId) +                continue; + +            if (CanSelectQuestPackageItem(questPackageItem)) +            { +                hasFilteredQuestPackageReward = true; +                ItemPosCountVec dest; +                if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, questPackageItem->ItemID, questPackageItem->ItemCount) == EQUIP_ERR_OK) +                { +                    Item* item = StoreNewItem(dest, questPackageItem->ItemID, true, GenerateItemRandomPropertyId(questPackageItem->ItemID)); +                    SendNewItem(item, questPackageItem->ItemCount, true, false); +                } +            } +        } +    } + +    if (!hasFilteredQuestPackageReward) +    { +        if (std::vector<QuestPackageItemEntry const*> const* questPackageItems = sDB2Manager.GetQuestPackageItemsFallback(questPackageId)) +        { +            for (QuestPackageItemEntry const* questPackageItem : *questPackageItems) +            { +                if (onlyItemId && questPackageItem->ItemID != onlyItemId) +                    continue; + +                ItemPosCountVec dest; +                if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, questPackageItem->ItemID, questPackageItem->ItemCount) == EQUIP_ERR_OK) +                { +                    Item* item = StoreNewItem(dest, questPackageItem->ItemID, true, GenerateItemRandomPropertyId(questPackageItem->ItemID)); +                    SendNewItem(item, questPackageItem->ItemCount, true, false); +                } +            } +        } +    } +} +  void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, bool announce)  {      //this THING should be here to protect code from quest, which cast on player far teleport as a reward @@ -15122,47 +15165,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,      // QuestPackageItem.db2      if (rewardProto && quest->GetQuestPackageID()) -    { -        bool hasFilteredQuestPackageReward = false; -        if (std::vector<QuestPackageItemEntry const*> const* questPackageItems = sDB2Manager.GetQuestPackageItems(quest->GetQuestPackageID())) -        { -            for (QuestPackageItemEntry const* questPackageItem : *questPackageItems) -            { -                if (questPackageItem->ItemID != reward) -                    continue; - -                if (CanSelectQuestPackageItem(questPackageItem)) -                { -                    hasFilteredQuestPackageReward = true; -                    ItemPosCountVec dest; -                    if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, questPackageItem->ItemID, questPackageItem->ItemCount) == EQUIP_ERR_OK) -                    { -                        Item* item = StoreNewItem(dest, questPackageItem->ItemID, true, GenerateItemRandomPropertyId(questPackageItem->ItemID)); -                        SendNewItem(item, questPackageItem->ItemCount, true, false); -                    } -                } -            } -        } - -        if (!hasFilteredQuestPackageReward) -        { -            if (std::vector<QuestPackageItemEntry const*> const* questPackageItems = sDB2Manager.GetQuestPackageItemsFallback(quest->GetQuestPackageID())) -            { -                for (QuestPackageItemEntry const* questPackageItem : *questPackageItems) -                { -                    if (questPackageItem->ItemID != reward) -                        continue; - -                    ItemPosCountVec dest; -                    if (CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, questPackageItem->ItemID, questPackageItem->ItemCount) == EQUIP_ERR_OK) -                    { -                        Item* item = StoreNewItem(dest, questPackageItem->ItemID, true, GenerateItemRandomPropertyId(questPackageItem->ItemID)); -                        SendNewItem(item, questPackageItem->ItemCount, true, false); -                    } -                } -            } -        } -    } +        RewardQuestPackage(quest->GetQuestPackageID(), reward);      if (quest->GetRewItemsCount() > 0)      { @@ -27121,35 +27124,90 @@ void Player::SendMovementSetCollisionHeight(float height)      SendMessageToSet(updateCollisionHeight.Write(), false);  } -void Player::SendPlayerChoice(ObjectGuid sender, uint32 choiceID) +void Player::SendPlayerChoice(ObjectGuid sender, int32 choiceId)  { -    PlayerChoice const* playerChoice = sObjectMgr->GetPlayerChoice(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); +    PlayerChoiceLocale const* playerChoiceLocale = locale != DEFAULT_LOCALE ? sObjectMgr->GetPlayerChoiceLocale(choiceId) : nullptr; -        for (auto& playerChoiceResponse : localizedPlayerChoice.Responses) -        { -            if (PlayerChoiceResponseLocale const* playerChoiceResponseLocale = sObjectMgr->GetPlayerChoiceResponseLocale(localizedPlayerChoice.ChoiceId, playerChoiceResponse.second.ResponseID)) +    PlayerTalkClass->GetInteractionData().Reset(); +    PlayerTalkClass->GetInteractionData().SourceGuid = sender; +    PlayerTalkClass->GetInteractionData().PlayerChoiceId = uint32(choiceId); + +    WorldPackets::Quest::DisplayPlayerChoice displayPlayerChoice; +    displayPlayerChoice.SenderGUID = sender; +    displayPlayerChoice.ChoiceID = choiceId; +    displayPlayerChoice.Question = playerChoice->Question; +    if (playerChoiceLocale) +        ObjectMgr::GetLocaleString(playerChoiceLocale->Question, locale, displayPlayerChoice.Question); + +    displayPlayerChoice.Responses.resize(playerChoice->Responses.size()); +    displayPlayerChoice.CloseChoiceFrame = false; + +    for (std::size_t i = 0; i < playerChoice->Responses.size(); ++i) +    { +        PlayerChoiceResponse const& playerChoiceResponseTemplate = playerChoice->Responses[i]; +        WorldPackets::Quest::PlayerChoiceResponse& playerChoiceResponse = displayPlayerChoice.Responses[i]; +        playerChoiceResponse.ResponseID = playerChoiceResponseTemplate.ResponseId; +        playerChoiceResponse.ChoiceArtFileID = playerChoiceResponseTemplate.ChoiceArtFileId; +        playerChoiceResponse.Answer = playerChoiceResponseTemplate.Answer; +        playerChoiceResponse.Header = playerChoiceResponseTemplate.Header; +        playerChoiceResponse.Description = playerChoiceResponseTemplate.Description; +        playerChoiceResponse.Confirmation = playerChoiceResponseTemplate.Confirmation; +        if (playerChoiceLocale) +        { +            if (PlayerChoiceResponseLocale const* playerChoiceResponseLocale = Trinity::Containers::MapGetValuePtr(playerChoiceLocale->Responses, playerChoiceResponseTemplate.ResponseId)) +            { +                ObjectMgr::GetLocaleString(playerChoiceResponseLocale->Answer, locale, playerChoiceResponse.Answer); +                ObjectMgr::GetLocaleString(playerChoiceResponseLocale->Header, locale, playerChoiceResponse.Header); +                ObjectMgr::GetLocaleString(playerChoiceResponseLocale->Description, locale, playerChoiceResponse.Description); +                ObjectMgr::GetLocaleString(playerChoiceResponseLocale->Confirmation, locale, playerChoiceResponse.Confirmation); +            } +        } + +        if (playerChoiceResponseTemplate.Reward) +        { +            playerChoiceResponse.Reward = boost::in_place(); +            playerChoiceResponse.Reward->TitleID = playerChoiceResponseTemplate.Reward->TitleId; +            playerChoiceResponse.Reward->PackageID = playerChoiceResponseTemplate.Reward->PackageId; +            playerChoiceResponse.Reward->SkillLineID = playerChoiceResponseTemplate.Reward->SkillLineId; +            playerChoiceResponse.Reward->SkillPointCount = playerChoiceResponseTemplate.Reward->SkillPointCount; +            playerChoiceResponse.Reward->ArenaPointCount = playerChoiceResponseTemplate.Reward->ArenaPointCount; +            playerChoiceResponse.Reward->HonorPointCount = playerChoiceResponseTemplate.Reward->HonorPointCount; +            playerChoiceResponse.Reward->Money = playerChoiceResponseTemplate.Reward->Money; +            playerChoiceResponse.Reward->Xp = playerChoiceResponseTemplate.Reward->Xp; +            for (PlayerChoiceResponseRewardItem const& item : playerChoiceResponseTemplate.Reward->Items) +            { +                playerChoiceResponse.Reward->Items.emplace_back(); +                WorldPackets::Quest::PlayerChoiceResponseRewardEntry& rewardEntry = playerChoiceResponse.Reward->Items.back(); +                rewardEntry.Item.ItemID = item.Id; +                rewardEntry.Quantity = item.Quantity; +                if (!item.BonusListIDs.empty()) +                { +                    rewardEntry.Item.ItemBonus = boost::in_place(); +                    rewardEntry.Item.ItemBonus->BonusListIDs = item.BonusListIDs; +                } +            } +            for (PlayerChoiceResponseRewardEntry const& currency : playerChoiceResponseTemplate.Reward->Currency)              { -                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); +                playerChoiceResponse.Reward->Items.emplace_back(); +                WorldPackets::Quest::PlayerChoiceResponseRewardEntry& rewardEntry = playerChoiceResponse.Reward->Items.back(); +                rewardEntry.Item.ItemID = currency.Id; +                rewardEntry.Quantity = currency.Quantity; +            } +            for (PlayerChoiceResponseRewardEntry const& faction : playerChoiceResponseTemplate.Reward->Faction) +            { +                playerChoiceResponse.Reward->Items.emplace_back(); +                WorldPackets::Quest::PlayerChoiceResponseRewardEntry& rewardEntry = playerChoiceResponse.Reward->Items.back(); +                rewardEntry.Item.ItemID = faction.Id; +                rewardEntry.Quantity = faction.Quantity;              }          }      } -    WorldPackets::Quest::DisplayPlayerChoice displayPlayerChoice; -    displayPlayerChoice.Choice = &localizedPlayerChoice; -    displayPlayerChoice.SenderGUID = sender;      SendDirectMessage(displayPlayerChoice.Write());  } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 1391ad7e532..5af181dd5a5 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1358,6 +1358,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>          uint32 GetQuestMoneyReward(Quest const* quest) const;          uint32 GetQuestXPReward(Quest const* quest);          bool CanSelectQuestPackageItem(QuestPackageItemEntry const* questPackageItem) const; +        void RewardQuestPackage(uint32 questPackageId, uint32 onlyItemId = 0);          void RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, bool announce = true);          void SetRewardedQuest(uint32 quest_id);          void FailQuest(uint32 quest_id); @@ -2338,7 +2339,7 @@ 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); +        void SendPlayerChoice(ObjectGuid sender, int32 choiceId);      protected:          // Gamemaster whisper whitelist diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index cc8215b75b0..ff2b7b807b2 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -35,7 +35,6 @@  #include "LootMgr.h"  #include "Mail.h"  #include "MapManager.h" -#include "MiscPackets.h"  #include "MotionMaster.h"  #include "ObjectAccessor.h"  #include "ObjectDefines.h" @@ -9789,38 +9788,9 @@ 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; +    return Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId);  }  void ObjectMgr::LoadGameObjectQuestItems() @@ -9947,9 +9917,9 @@ void ObjectMgr::LoadSceneTemplates()  void ObjectMgr::LoadPlayerChoices()  {      uint32 oldMSTime = getMSTime(); -    _playerChoiceContainer.clear(); +    _playerChoices.clear(); -    QueryResult choices = WorldDatabase.Query("SELECT ChoiceID, Question FROM playerchoice"); +    QueryResult choices = WorldDatabase.Query("SELECT ChoiceId, Question FROM playerchoice");      if (!choices)      { @@ -9959,6 +9929,9 @@ void ObjectMgr::LoadPlayerChoices()      uint32 responseCount = 0;      uint32 rewardCount = 0; +    uint32 itemRewardCount = 0; +    uint32 currencyRewardCount = 0; +    uint32 factionRewardCount = 0;      do      { @@ -9966,97 +9939,245 @@ void ObjectMgr::LoadPlayerChoices()          int32 choiceId = fields[0].GetInt32(); -        PlayerChoice& choice = _playerChoiceContainer[choiceId]; +        PlayerChoice& choice = _playerChoices[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)); +    } while (choices->NextRow()); -    QueryResult responses = WorldDatabase.Query("SELECT ChoiceID, ResponseID, ChoiceArtFileID, Header, Answer, Description, Confirmation FROM playerchoice_response"); -    if (responses) +    if (QueryResult responses = WorldDatabase.Query("SELECT ChoiceId, ResponseId, ChoiceArtFileId, Header, Answer, Description, Confirmation FROM playerchoice_response ORDER BY `Index` ASC"))      {          do          { -            ++responseCount;              Field* fields = responses->Fetch();              int32 choiceId      = fields[0].GetInt32(); -            int32 ResponseID    = fields[1].GetInt32(); +            int32 responseId    = fields[1].GetInt32(); -            if (_playerChoiceContainer.find(choiceId) == _playerChoiceContainer.end()) +            PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId); +            if (!choice)              { -                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response` has nonexistent choiceId: %u (ResponseID : %u), skipped", choiceId, ResponseID); +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response` references non-existing ChoiceId: %d (ResponseId: %d), skipped", choiceId, responseId);                  continue;              } -            PlayerChoice& choice = _playerChoiceContainer[choiceId]; -            PlayerChoiceResponse& response = choice.Responses[ResponseID]; +            choice->Responses.emplace_back(); -            response.ResponseID         = ResponseID; -            response.ChoiceArtFileID    = fields[2].GetInt32(); +            PlayerChoiceResponse& response = choice->Responses.back(); +            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()); +            ++responseCount; -        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."); +        } while (responses->NextRow());      } -    QueryResult rewards = WorldDatabase.Query("SELECT ChoiceID, ResponseID, TitleID, PackageID, SkillLineID, SkillPointCount, ArenaPointCount, HonorPointCount, Money, Xp FROM playerchoice_response_reward"); - -    if (rewards) +    if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, TitleId, PackageId, SkillLineId, SkillPointCount, ArenaPointCount, HonorPointCount, Money, Xp FROM playerchoice_response_reward"))      {          do          { -            ++rewardCount;              Field* fields = rewards->Fetch();              int32 choiceId      = fields[0].GetInt32(); -            int32 ResponseID    = fields[1].GetInt32(); +            int32 responseId    = fields[1].GetInt32(); + +            PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId); +            if (!choice) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing ChoiceId: %d (ResponseId: %d), skipped", choiceId, responseId); +                continue; +            } -            if (_playerChoiceContainer.find(choiceId) == _playerChoiceContainer.end()) +            auto responseItr = std::find_if(choice->Responses.begin(), choice->Responses.end(), +                [responseId](PlayerChoiceResponse const& playerChoiceResponse) { return playerChoiceResponse.ResponseId == responseId; }); +            if (responseItr == choice->Responses.end())              { -                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` has nonexistent choiceId: %u (ResponseID : %u), skipped", choiceId, ResponseID); +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing ResponseId: %d for ChoiceId %d, skipped", responseId, choiceId);                  continue;              } -            PlayerChoice& choice = _playerChoiceContainer[choiceId]; +            responseItr->Reward = boost::in_place(); -            if (choice.Responses.find(ResponseID) == choice.Responses.end()) +            PlayerChoiceResponseReward* reward = responseItr->Reward.get_ptr(); +            reward->TitleId          = fields[2].GetInt32(); +            reward->PackageId        = fields[3].GetInt32(); +            reward->SkillLineId      = fields[4].GetInt32(); +            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(); +            ++rewardCount; + +            if (reward->TitleId && !sCharTitlesStore.LookupEntry(reward->TitleId)) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing Title %d for ChoiceId %d, ResponseId: %d, set to 0", +                    reward->TitleId, choiceId, responseId); +                reward->TitleId = 0; +            } + +            if (reward->PackageId && !sDB2Manager.GetQuestPackageItems(reward->PackageId)) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing QuestPackage %d for ChoiceId %d, ResponseId: %d, set to 0", +                    reward->TitleId, choiceId, responseId); +                reward->PackageId = 0; +            } + +            if (reward->SkillLineId && !sSkillLineStore.LookupEntry(reward->SkillLineId)) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` references non-existing SkillLine %d for ChoiceId %d, ResponseId: %d, set to 0", +                    reward->TitleId, choiceId, responseId); +                reward->SkillLineId = 0; +                reward->SkillPointCount = 0; +            } + +        } while (rewards->NextRow()); +    } + +    if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, ItemId, BonusListIDs, Quantity FROM playerchoice_response_reward_item ORDER BY `Index` ASC")) +    { +        do +        { +            Field* fields = rewards->Fetch(); + +            int32 choiceId = fields[0].GetInt32(); +            int32 responseId = fields[1].GetInt32(); +            uint32 itemId = fields[2].GetUInt32(); +            Tokenizer bonusListIDsTok(fields[3].GetString(), ' '); +            std::vector<int32> bonusListIds; +            for (char const* token : bonusListIDsTok) +                bonusListIds.push_back(int32(atol(token))); +            int32 quantity = fields[4].GetInt32(); + +            PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId); +            if (!choice)              { -                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward` has nonexistent ResponseID: %u for choiceId %u, skipped", ResponseID, choiceId); +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing ChoiceId: %d (ResponseId: %d), skipped", choiceId, responseId);                  continue;              } -            PlayerChoiceResponse& response = choice.Responses[ResponseID]; +            auto responseItr = std::find_if(choice->Responses.begin(), choice->Responses.end(), +                [responseId](PlayerChoiceResponse const& playerChoiceResponse) { return playerChoiceResponse.ResponseId == responseId; }); +            if (responseItr == choice->Responses.end()) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing ResponseId: %d for ChoiceId %d, skipped", responseId, choiceId); +                continue; +            } -            PlayerChoiceResponseReward reward; +            if (!responseItr->Reward) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing player choice reward for ChoiceId %d, ResponseId: %d, skipped", +                    choiceId, responseId); +                continue; +            } -            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(); +            if (!GetItemTemplate(itemId)) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_item` references non-existing item %u for ChoiceId %d, ResponseId: %d, skipped", +                    itemId, choiceId, responseId); +                continue; +            } -            response.Reward = reward; +            responseItr->Reward->Items.emplace_back(itemId, std::move(bonusListIds), quantity);          } while (rewards->NextRow()); +    } -        TC_LOG_INFO("server.loading", ">> Loaded %u player choices reward in %u ms.", rewardCount, GetMSTimeDiffToNow(oldMSTime)); +    if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, CurrencyId, Quantity FROM playerchoice_response_reward_currency ORDER BY `Index` ASC")) +    { +        do +        { +            Field* fields = rewards->Fetch(); + +            int32 choiceId = fields[0].GetInt32(); +            int32 responseId = fields[1].GetInt32(); +            uint32 currencyId = fields[2].GetUInt32(); +            int32 quantity = fields[3].GetInt32(); + +            PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId); +            if (!choice) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing ChoiceId: %d (ResponseId: %d), skipped", choiceId, responseId); +                continue; +            } + +            auto responseItr = std::find_if(choice->Responses.begin(), choice->Responses.end(), +                [responseId](PlayerChoiceResponse const& playerChoiceResponse) { return playerChoiceResponse.ResponseId == responseId; }); +            if (responseItr == choice->Responses.end()) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing ResponseId: %d for ChoiceId %d, skipped", responseId, choiceId); +                continue; +            } + +            if (!responseItr->Reward) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing player choice reward for ChoiceId %d, ResponseId: %d, skipped", +                    choiceId, responseId); +                continue; +            } + +            if (!sCurrencyTypesStore.LookupEntry(currencyId)) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_currency` references non-existing currency %u for ChoiceId %d, ResponseId: %d, skipped", +                    currencyId, choiceId, responseId); +                continue; +            } + +            responseItr->Reward->Currency.emplace_back(currencyId, quantity); + +        } while (rewards->NextRow());      } -    else + +    if (QueryResult rewards = WorldDatabase.Query("SELECT ChoiceId, ResponseId, FactionId, Quantity FROM playerchoice_response_reward_faction ORDER BY `Index` ASC"))      { -        TC_LOG_INFO("server.loading", ">> Loaded 0 player choices responses reward. DB table `playerchoice_response_reward` is empty."); +        do +        { +            Field* fields = rewards->Fetch(); + +            int32 choiceId = fields[0].GetInt32(); +            int32 responseId = fields[1].GetInt32(); +            uint32 factionId = fields[2].GetUInt32(); +            int32 quantity = fields[3].GetInt32(); + +            PlayerChoice* choice = Trinity::Containers::MapGetValuePtr(_playerChoices, choiceId); +            if (!choice) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing ChoiceId: %d (ResponseId: %d), skipped", choiceId, responseId); +                continue; +            } + +            auto responseItr = std::find_if(choice->Responses.begin(), choice->Responses.end(), +                [responseId](PlayerChoiceResponse const& playerChoiceResponse) { return playerChoiceResponse.ResponseId == responseId; }); +            if (responseItr == choice->Responses.end()) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing ResponseId: %d for ChoiceId %d, skipped", responseId, choiceId); +                continue; +            } + +            if (!responseItr->Reward) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing player choice reward for ChoiceId %d, ResponseId: %d, skipped", +                    choiceId, responseId); +                continue; +            } + +            if (!sFactionStore.LookupEntry(factionId)) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_response_reward_faction` references non-existing faction %u for ChoiceId %d, ResponseId: %d, skipped", +                    factionId, choiceId, responseId); +                continue; +            } + +            responseItr->Reward->Faction.emplace_back(factionId, quantity); + +        } while (rewards->NextRow());      } + +    TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " player choices, %u responses, %u rewards, %u item rewards, %u currency rewards and %u faction rewards in %u ms.", +        _playerChoices.size(), responseCount, rewardCount, itemRewardCount, currencyRewardCount, factionRewardCount, GetMSTimeDiffToNow(oldMSTime));  }  void ObjectMgr::LoadPlayerChoicesLocale() @@ -10064,63 +10185,77 @@ void ObjectMgr::LoadPlayerChoicesLocale()      uint32 oldMSTime = getMSTime();      // need for reload case -    _playerChoiceLocaleContainer.clear(); -    _playerChoiceResponseLocaleContainer.clear(); +    _playerChoiceLocales.clear(); -    //                                               0         1       2 -    QueryResult result = WorldDatabase.Query("SELECT ChoiceID, locale, Question FROM playerchoice_locale"); -    if (result) +    //                                                   0         1       2 +    if (QueryResult result = WorldDatabase.Query("SELECT ChoiceId, locale, Question FROM playerchoice_locale"))      {          do          {              Field* fields = result->Fetch(); -            uint32 ChoiceID         = fields[0].GetUInt32(); +            uint32 choiceId         = fields[0].GetUInt32();              std::string localeName  = fields[1].GetString(); -            std::string question    = fields[2].GetString(); -            PlayerChoiceLocale& data = _playerChoiceLocaleContainer[ChoiceID]; +            if (!GetPlayerChoice(choiceId)) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_locale` references non-existing ChoiceId: %d for locale %s, skipped", choiceId, localeName.c_str()); +                continue; +            } +              LocaleConstant locale = GetLocaleByName(localeName);              if (locale == LOCALE_enUS)                  continue; -            AddLocaleString(question, locale, data.Question); +            PlayerChoiceLocale& data = _playerChoiceLocales[choiceId]; +            AddLocaleString(fields[2].GetString(), locale, data.Question);          } while (result->NextRow()); -        TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " Player Choice locale strings in %u ms", _playerChoiceLocaleContainer.size(), GetMSTimeDiffToNow(oldMSTime)); +        TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " Player Choice locale strings in %u ms", _playerChoiceLocales.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) +    //                                                   0         1           2       3       4       5            6 +    if (QueryResult result = WorldDatabase.Query("SELECT ChoiceID, ResponseID, locale, Header, Answer, Description, Confirmation FROM playerchoice_response_locale"))      { +        std::size_t count = 0;          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(); +            int32 choiceId         = fields[0].GetInt32(); +            int32 responseId       = fields[1].GetInt32(); +            std::string localeName = fields[2].GetString(); -            std::pair<uint32, uint32> pair = std::make_pair(ChoiceID, ResponseID); +            auto itr = _playerChoiceLocales.find(choiceId); +            if (itr == _playerChoiceLocales.end()) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_locale` references non-existing ChoiceId: %d for ResponseId %d locale %s, skipped", +                    choiceId, responseId, localeName.c_str()); +                continue; +            } + +            PlayerChoice const* playerChoice = ASSERT_NOTNULL(GetPlayerChoice(choiceId)); +            if (!playerChoice->GetResponse(responseId)) +            { +                TC_LOG_ERROR("sql.sql", "Table `playerchoice_locale` references non-existing ResponseId: %d for ChoiceId %d locale %s, skipped", +                    responseId, choiceId, localeName.c_str()); +                continue; +            } -            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); +            PlayerChoiceResponseLocale& data = itr->second.Responses[responseId]; +            AddLocaleString(fields[3].GetString(), locale, data.Header); +            AddLocaleString(fields[4].GetString(), locale, data.Answer); +            AddLocaleString(fields[5].GetString(), locale, data.Description); +            AddLocaleString(fields[6].GetString(), locale, data.Confirmation); +            ++count;          } while (result->NextRow()); -        TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " Player Choice Response locale strings in %u ms", _playerChoiceResponseLocaleContainer.size(), GetMSTimeDiffToNow(oldMSTime)); +        TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " Player Choice Response locale strings in %u ms", count, GetMSTimeDiffToNow(oldMSTime));      }  } diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 043575b492c..4b7faf21b2d 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -518,8 +518,8 @@ typedef std::unordered_map<uint32, PointOfInterestLocale> PointOfInterestLocaleC  struct PlayerChoiceResponseLocale  { -    std::vector<std::string> Header;      std::vector<std::string> Answer; +    std::vector<std::string> Header;      std::vector<std::string> Description;      std::vector<std::string> Confirmation;  }; @@ -527,11 +527,9 @@ struct PlayerChoiceResponseLocale  struct PlayerChoiceLocale  {      std::vector<std::string> Question; +    std::unordered_map<int32 /*ResponseId*/, PlayerChoiceResponseLocale> Responses;  }; -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 @@ -768,33 +766,48 @@ struct SceneTemplate  typedef std::unordered_map<uint32, SceneTemplate> SceneTemplateContainer; +struct PlayerChoiceResponseRewardItem +{ +    PlayerChoiceResponseRewardItem() : Id(0), Quantity(0) { } +    PlayerChoiceResponseRewardItem(uint32 id, std::vector<int32> bonusListIDs, int32 quantity) : Id(id), BonusListIDs(std::move(bonusListIDs)), Quantity(quantity) { } + +    uint32 Id; +    std::vector<int32> BonusListIDs; +    int32 Quantity; +}; + +struct PlayerChoiceResponseRewardEntry +{ +    PlayerChoiceResponseRewardEntry() : Id(0), Quantity(0) { } +    PlayerChoiceResponseRewardEntry(uint32 id, int32 quantity) : Id(id), Quantity(quantity) { } + +    uint32 Id; +    int32 Quantity; +}; +  struct PlayerChoiceResponseReward  { -    int32 TitleID; -    int32 PackageID; -    int32 SkillLineID; +    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; +    std::vector<PlayerChoiceResponseRewardItem> Items; +    std::vector<PlayerChoiceResponseRewardEntry> Currency; +    std::vector<PlayerChoiceResponseRewardEntry> Faction;  };  struct PlayerChoiceResponse  { -    int32 ResponseID; -    int32 ChoiceArtFileID; - +    int32 ResponseId; +    int32 ChoiceArtFileId;      std::string Header;      std::string Answer;      std::string Description;      std::string Confirmation; -      Optional<PlayerChoiceResponseReward> Reward;  }; @@ -802,10 +815,15 @@ struct PlayerChoice  {      int32 ChoiceId;      std::string Question; -    std::unordered_map<int32 /*ResponseID*/, PlayerChoiceResponse> Responses; -}; +    std::vector<PlayerChoiceResponse> Responses; -typedef std::unordered_map<int32 /*choiceId*/, PlayerChoice> PlayerChoiceContainer; +    PlayerChoiceResponse const* GetResponse(int32 responseId) const +    { +        auto itr = std::find_if(Responses.begin(), Responses.end(), +            [responseId](PlayerChoiceResponse const& playerChoiceResponse) { return playerChoiceResponse.ResponseId == responseId; }); +        return itr != Responses.end() ? &(*itr) : nullptr; +    } +};  enum SkillRangeType  { @@ -1384,19 +1402,12 @@ 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 +        PlayerChoiceLocale const* GetPlayerChoiceLocale(int32 ChoiceID) const          { -            PlayerChoiceResponseLocaleContainer::const_iterator itr = _playerChoiceResponseLocaleContainer.find(std::make_pair(ChoiceID, ResponseID)); -            if (itr == _playerChoiceResponseLocaleContainer.end()) return nullptr; +            auto itr = _playerChoiceLocales.find(ChoiceID); +            if (itr == _playerChoiceLocales.end()) return nullptr;              return &itr->second;          } -          GameObjectData const* GetGOData(ObjectGuid::LowType guid) const          {              GameObjectDataContainer::const_iterator itr = _gameObjectDataStore.find(guid); @@ -1558,12 +1569,31 @@ 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; +        uint8 GetRaceExpansionRequirement(uint8 race) const +        { +            auto itr = _raceExpansionRequirementStore.find(race); +            if (itr != _raceExpansionRequirementStore.end()) +                return itr->second; +            return EXPANSION_CLASSIC; +        }          std::unordered_map<uint8, uint8> const& GetClassExpansionRequirements() const { return _classExpansionRequirementStore; } -        uint8 GetClassExpansionRequirement(uint8 class_) const; +        uint8 GetClassExpansionRequirement(uint8 class_) const +        { +            auto itr = _classExpansionRequirementStore.find(class_); +            if (itr != _classExpansionRequirementStore.end()) +                return itr->second; +            return EXPANSION_CLASSIC; +        } + +        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; @@ -1700,7 +1730,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; +        std::unordered_map<int32 /*choiceId*/, PlayerChoice> _playerChoices;          ItemTemplateContainer _itemTemplateStore;          QuestTemplateLocaleContainer _questTemplateLocaleStore; @@ -1711,8 +1741,7 @@ class TC_GAME_API ObjectMgr          GossipMenuItemsLocaleContainer _gossipMenuItemsLocaleStore;          PointOfInterestLocaleContainer _pointOfInterestLocaleStore; -        PlayerChoiceLocaleContainer _playerChoiceLocaleContainer; -        PlayerChoiceResponseLocaleContainer _playerChoiceResponseLocaleContainer; +        std::unordered_map<int32, PlayerChoiceLocale> _playerChoiceLocales;          TrinityStringContainer _trinityStringStore; diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 68a71e5dd5e..4cd997c0a99 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -32,6 +32,7 @@  #include "Player.h"  #include "QuestDef.h"  #include "QuestPackets.h" +#include "ReputationMgr.h"  #include "ScriptMgr.h"  #include "UnitAI.h"  #include "World.h" @@ -674,7 +675,63 @@ void WorldSession::HandleRequestWorldQuestUpdate(WorldPackets::Quest::RequestWor      SendPacket(response.Write());  } -void WorldSession::HandlePlayerChoiceResponse(WorldPackets::Quest::PlayerChoiceResponse& packet) +void WorldSession::HandlePlayerChoiceResponse(WorldPackets::Quest::ChoiceResponse& choiceResponse)  { -    sScriptMgr->OnPlayerChoiceResponse(GetPlayer(), packet.ChoiceID, packet.ResponseID); +    if (_player->PlayerTalkClass->GetInteractionData().PlayerChoiceId != uint32(choiceResponse.ChoiceID)) +    { +        TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: %s tried to respond to invalid player choice %d (allowed %u) (possible packet-hacking detected)", +            GetPlayerInfo().c_str(), choiceResponse.ChoiceID, _player->PlayerTalkClass->GetInteractionData().PlayerChoiceId); +        return; +    } + +    PlayerChoice const* playerChoice = sObjectMgr->GetPlayerChoice(choiceResponse.ChoiceID); +    if (!playerChoice) +        return; + +    PlayerChoiceResponse const* playerChoiceResponse = playerChoice->GetResponse(choiceResponse.ResponseID); +    if (!playerChoiceResponse) +    { +        TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: %s tried to select invalid player choice response %d (possible packet-hacking detected)", +            GetPlayerInfo().c_str(), choiceResponse.ResponseID); +        return; +    } + +    sScriptMgr->OnPlayerChoiceResponse(GetPlayer(), choiceResponse.ChoiceID, choiceResponse.ResponseID); + +    if (playerChoiceResponse->Reward) +    { +        if (playerChoiceResponse->Reward->TitleId) +            _player->SetTitle(sCharTitlesStore.AssertEntry(playerChoiceResponse->Reward->TitleId), false); + +        if (playerChoiceResponse->Reward->PackageId) +            _player->RewardQuestPackage(playerChoiceResponse->Reward->PackageId); + +        if (playerChoiceResponse->Reward->SkillLineId && _player->HasSkill(playerChoiceResponse->Reward->SkillLineId)) +            _player->UpdateSkillPro(playerChoiceResponse->Reward->SkillLineId, 1000, playerChoiceResponse->Reward->SkillPointCount); + +        if (playerChoiceResponse->Reward->HonorPointCount) +            _player->AddHonorXP(playerChoiceResponse->Reward->HonorPointCount); + +        if (playerChoiceResponse->Reward->Money) +            _player->ModifyMoney(playerChoiceResponse->Reward->Money, false); + +        if (playerChoiceResponse->Reward->Xp) +            _player->GiveXP(playerChoiceResponse->Reward->Xp, nullptr, 0.0f); + +        for (PlayerChoiceResponseRewardItem const& item : playerChoiceResponse->Reward->Items) +        { +            ItemPosCountVec dest; +            if (_player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.Id, item.Quantity) == EQUIP_ERR_OK) +            { +                Item* newItem = _player->StoreNewItem(dest, item.Id, true, GenerateItemRandomPropertyId(item.Id), {}, 0, item.BonusListIDs); +                _player->SendNewItem(newItem, item.Quantity, true, false); +            } +        } + +        for (PlayerChoiceResponseRewardEntry const& currency : playerChoiceResponse->Reward->Currency) +            _player->ModifyCurrency(currency.Id, currency.Quantity); + +        for (PlayerChoiceResponseRewardEntry const& faction : playerChoiceResponse->Reward->Faction) +            _player->GetReputationMgr().ModifyReputation(sFactionStore.AssertEntry(faction.Id), faction.Quantity); +    }  } diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index 4b8d5545588..168ba1c4d16 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -2297,9 +2297,9 @@ void ScriptMgr::OnMovieComplete(Player* player, uint32 movieId)      FOREACH_SCRIPT(PlayerScript)->OnMovieComplete(player, movieId);  } -void ScriptMgr::OnPlayerChoiceResponse(Player* player, uint32 choiceID, uint32 responseID) +void ScriptMgr::OnPlayerChoiceResponse(Player* player, uint32 choiceId, uint32 responseId)  { -    FOREACH_SCRIPT(PlayerScript)->OnPlayerChoiceResponse(player, choiceID, responseID); +    FOREACH_SCRIPT(PlayerScript)->OnPlayerChoiceResponse(player, choiceId, responseId);  }  // Account diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index e27a68b7508..11c69ea03e7 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -757,7 +757,7 @@ class TC_GAME_API PlayerScript : public UnitScript          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*/) { } +        virtual void OnPlayerChoiceResponse(Player* /*player*/, uint32 /*choiceId*/, uint32 /*responseId*/) { }  };  class TC_GAME_API AccountScript : public ScriptObject @@ -1146,7 +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); +        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 d076315fde3..39981b9ba9d 100644 --- a/src/server/game/Server/Packets/QuestPackets.cpp +++ b/src/server/game/Server/Packets/QuestPackets.cpp @@ -600,77 +600,82 @@ WorldPacket const* WorldPackets::Quest::WorldQuestUpdate::Write()      return &_worldPacket;  } -ByteBuffer& operator<<(ByteBuffer& data, PlayerChoiceResponse const& response) +ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Quest::PlayerChoiceResponseRewardEntry const& playerChoiceResponseRewardEntry)  { -    data << int32(response.ResponseID); -    data << int32(response.ChoiceArtFileID); +    data << playerChoiceResponseRewardEntry.Item; +    data << int32(playerChoiceResponseRewardEntry.Quantity); +    return data; +} -    data.WriteBits(response.Answer.length(), 9); -    data.WriteBits(response.Header.length(), 9); -    data.WriteBits(response.Description.length(), 11); -    data.WriteBits(response.Confirmation.length(), 7); +ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Quest::PlayerChoiceResponseReward const& playerChoiceResponseReward) +{ +    data << int32(playerChoiceResponseReward.TitleID); +    data << int32(playerChoiceResponseReward.PackageID); +    data << int32(playerChoiceResponseReward.SkillLineID); +    data << uint32(playerChoiceResponseReward.SkillPointCount); +    data << uint32(playerChoiceResponseReward.ArenaPointCount); +    data << uint32(playerChoiceResponseReward.HonorPointCount); +    data << uint64(playerChoiceResponseReward.Money); +    data << uint32(playerChoiceResponseReward.Xp); +    data << uint32(playerChoiceResponseReward.Items.size()); +    data << uint32(playerChoiceResponseReward.Currencies.size()); +    data << uint32(playerChoiceResponseReward.Factions.size()); +    data << uint32(playerChoiceResponseReward.ItemChoices.size()); -    data.WriteBit(response.Reward.is_initialized()); -    data.FlushBits(); +    for (WorldPackets::Quest::PlayerChoiceResponseRewardEntry const& item : playerChoiceResponseReward.Items) +        data << item; + +    for (WorldPackets::Quest::PlayerChoiceResponseRewardEntry const& currency : playerChoiceResponseReward.Currencies) +        data << currency; -    if (response.Reward.is_initialized()) -        data << (*response.Reward); +    for (WorldPackets::Quest::PlayerChoiceResponseRewardEntry const& faction : playerChoiceResponseReward.Factions) +        data << faction; + +    for (WorldPackets::Quest::PlayerChoiceResponseRewardEntry const& itemChoice : playerChoiceResponseReward.ItemChoices) +        data << itemChoice; -    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) +ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Quest::PlayerChoiceResponse const& playerChoiceResponse)  { -    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); +    data << int32(playerChoiceResponse.ResponseID); +    data << int32(playerChoiceResponse.ChoiceArtFileID); -    for (var i = 0u; i < currencyCount; ++i) -    ReadPlayerChoiceResponseRewardEntry(packet, "Currency", i); - -    for (var i = 0u; i < factionCount; ++i) -    ReadPlayerChoiceResponseRewardEntry(packet, "Faction", i); +    data.WriteBits(playerChoiceResponse.Answer.length(), 9); +    data.WriteBits(playerChoiceResponse.Header.length(), 9); +    data.WriteBits(playerChoiceResponse.Description.length(), 11); +    data.WriteBits(playerChoiceResponse.Confirmation.length(), 7); +    data.WriteBit(playerChoiceResponse.Reward.is_initialized()); +    data.FlushBits(); -    for (var i = 0u; i < itemChoiceCount; ++i) -    ReadPlayerChoiceResponseRewardEntry(packet, "ItemChoice", i);*/ +    if (playerChoiceResponse.Reward) +        data << *playerChoiceResponse.Reward; +    data.WriteString(playerChoiceResponse.Answer); +    data.WriteString(playerChoiceResponse.Header); +    data.WriteString(playerChoiceResponse.Description); +    data.WriteString(playerChoiceResponse.Confirmation);      return data;  }  WorldPacket const* WorldPackets::Quest::DisplayPlayerChoice::Write()  { -    _worldPacket << int32(Choice->ChoiceId); -    _worldPacket << uint32(Choice->Responses.size()); +    _worldPacket << int32(ChoiceID); +    _worldPacket << uint32(Responses.size());      _worldPacket << SenderGUID; -    _worldPacket.WriteBits(Choice->Question.length(), 8); +    _worldPacket.WriteBits(Question.length(), 8);      _worldPacket.WriteBit(CloseChoiceFrame);      _worldPacket.FlushBits(); -    for (auto response : Choice->Responses) -        _worldPacket << response.second; +    for (PlayerChoiceResponse const& response : Responses) +        _worldPacket << response; -    _worldPacket.WriteString(Choice->Question); +    _worldPacket.WriteString(Question);      return &_worldPacket;  } -void WorldPackets::Quest::PlayerChoiceResponse::Read() +void WorldPackets::Quest::ChoiceResponse::Read()  {      _worldPacket >> ChoiceID;      _worldPacket >> ResponseID; diff --git a/src/server/game/Server/Packets/QuestPackets.h b/src/server/game/Server/Packets/QuestPackets.h index a087c5de241..7effcdfa4d5 100644 --- a/src/server/game/Server/Packets/QuestPackets.h +++ b/src/server/game/Server/Packets/QuestPackets.h @@ -20,12 +20,8 @@  #include "Packet.h"  #include "ItemPacketsCommon.h" -#include "QuestDef.h"  #include "ObjectGuid.h" - -struct PlayerChoice; -struct PlayerChoiceResponse; -struct PlayerChoiceResponseReward; +#include "QuestDef.h"  namespace WorldPackets  { @@ -638,6 +634,39 @@ namespace WorldPackets              std::vector<WorldQuestUpdateInfo> WorldQuestUpdates;          }; +        struct PlayerChoiceResponseRewardEntry +        { +            WorldPackets::Item::ItemInstance Item; +            int32 Quantity = 0; +        }; + +        struct PlayerChoiceResponseReward +        { +            int32 TitleID = 0; +            int32 PackageID = 0; +            int32 SkillLineID = 0; +            uint32 SkillPointCount = 0; +            uint32 ArenaPointCount = 0; +            uint32 HonorPointCount = 0; +            uint64 Money = 0; +            uint32 Xp = 0; +            std::vector<PlayerChoiceResponseRewardEntry> Items; +            std::vector<PlayerChoiceResponseRewardEntry> Currencies; +            std::vector<PlayerChoiceResponseRewardEntry> Factions; +            std::vector<PlayerChoiceResponseRewardEntry> ItemChoices; +        }; + +        struct PlayerChoiceResponse +        { +            int32 ResponseID = 0; +            int32 ChoiceArtFileID = 0; +            std::string Answer; +            std::string Header; +            std::string Description; +            std::string Confirmation; +            Optional<PlayerChoiceResponseReward> Reward; +        }; +          class DisplayPlayerChoice final : public ServerPacket          {          public: @@ -646,26 +675,25 @@ namespace WorldPackets              WorldPacket const* Write() override;              ObjectGuid SenderGUID; -            PlayerChoice const* Choice; +            int32 ChoiceID = 0; +            std::string Question; +            std::vector<PlayerChoiceResponse> Responses;              bool CloseChoiceFrame = false;          }; -        class PlayerChoiceResponse final : public ClientPacket +        class ChoiceResponse final : public ClientPacket          {          public: -            PlayerChoiceResponse(WorldPacket&& packet) : ClientPacket(CMSG_CHOICE_RESPONSE, std::move(packet)) { } +            ChoiceResponse(WorldPacket&& packet) : ClientPacket(CMSG_CHOICE_RESPONSE, std::move(packet)) { }              void Read() override; -            int32 ChoiceID; -            int32 ResponseID; +            int32 ChoiceID = 0; +            int32 ResponseID = 0;          };      }  } -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/WorldSession.h b/src/server/game/Server/WorldSession.h index 5d680a1dab1..1606d00acff 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -596,7 +596,7 @@ namespace WorldPackets          class QuestPushResult;          class PushQuestToParty;          class RequestWorldQuestUpdate; -        class PlayerChoiceResponse; +        class ChoiceResponse;      }      namespace RaF @@ -1443,7 +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 HandlePlayerChoiceResponse(WorldPackets::Quest::ChoiceResponse& choiceResponse);          void HandleChatMessageOpcode(WorldPackets::Chat::ChatMessage& chatMessage);          void HandleChatMessageWhisperOpcode(WorldPackets::Chat::ChatMessageWhisper& chatMessageWhisper); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index feed2c9cbc6..8a9e761b761 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -5666,7 +5666,7 @@ void Spell::EffectEnableBattlePets(SpellEffIndex /*effIndex*/)      plr->GetSession()->GetBattlePetMgr()->UnlockSlot(0);  } -void Spell::EffectLaunchQuestChoice(SpellEffIndex effIndex) +void Spell::EffectLaunchQuestChoice(SpellEffIndex /*effIndex*/)  {      if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET)          return; @@ -5674,7 +5674,7 @@ void Spell::EffectLaunchQuestChoice(SpellEffIndex effIndex)      if (!unitTarget || !unitTarget->IsPlayer())          return; -    unitTarget->ToPlayer()->SendPlayerChoice(GetCaster()->GetGUID(), GetEffect(effIndex)->MiscValue); +    unitTarget->ToPlayer()->SendPlayerChoice(GetCaster()->GetGUID(), effectInfo->MiscValue);  }  void Spell::EffectUncageBattlePet(SpellEffIndex /*effIndex*/)  | 
