diff options
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/game/Entities/Item/Item.h | 1 | ||||
| -rw-r--r-- | src/server/game/Entities/Item/ItemTemplate.h | 1 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 341 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/Player.h | 8 | ||||
| -rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 4 | ||||
| -rw-r--r-- | src/server/game/Handlers/LootHandler.cpp | 6 | ||||
| -rw-r--r-- | src/server/game/Handlers/VoidStorageHandler.cpp | 10 | ||||
| -rw-r--r-- | src/server/game/Loot/Loot.cpp | 10 | ||||
| -rw-r--r-- | src/server/game/Quests/QuestDef.h | 5 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.cpp | 9 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 26 | ||||
| -rw-r--r-- | src/server/scripts/Commands/cs_misc.cpp | 2 | 
12 files changed, 263 insertions, 160 deletions
| diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index ec233c98ffc..ff873c859e0 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -449,7 +449,6 @@ class TC_GAME_API Item : public Object          UF::UpdateField<UF::ItemData, 0, TYPEID_ITEM> m_itemData;      protected: -        void ApplyBonusList(uint32 itemBonusListId);          BonusData _bonusData;      private: diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index 1536cce605f..1c8cf91c5fc 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -837,6 +837,7 @@ struct TC_GAME_API ItemTemplate      uint32 RandomBonusListTemplateId;      std::bitset<MAX_CLASSES * MAX_SPECIALIZATIONS> Specializations[3];  // one set for 1-40 level range and another for 41-109 and one for 110      uint32 ItemSpecClassMask; +    int32 QuestLogItemId;      // helpers      bool CanChangeEquipStateInCombat() const; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 7ab0b6bd04b..d0181dcf88e 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -8907,6 +8907,20 @@ void Player::ApplyItemLootedSpell(Item* item, bool apply)      }  } +void Player::ApplyItemLootedSpell(ItemTemplate const* itemTemplate) +{ +    if (itemTemplate->HasFlag(ITEM_FLAG_LEGACY)) +        return; + +    for (ItemEffectEntry const* effect : itemTemplate->Effects) +    { +        if (effect->TriggerType != ITEM_SPELLTRIGGER_ON_LOOTED) +            continue; + +        CastSpell(this, effect->SpellID, true); +    } +} +  void Player::_RemoveAllItemMods()  {      TC_LOG_DEBUG("entities.player.items", "_RemoveAllItemMods start."); @@ -11550,6 +11564,12 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat      for (ItemPosCountVec::const_iterator itr = pos.begin(); itr != pos.end(); ++itr)          count += itr->count; +    // quest objectives must be processed twice - QUEST_OBJECTIVE_FLAG_2_QUEST_BOUND_ITEM prevents item creation +    bool hadBoundItemObjective = false; +    ItemAddedQuestCheck(itemId, count, true, &hadBoundItemObjective); +    if (hadBoundItemObjective) +        return nullptr; +      Item* item = Item::CreateItem(itemId, count, context, this, bonusListIDs == nullptr);      if (item)      { @@ -11560,7 +11580,7 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat          item = StoreItem(pos, item, update); -        ItemAddedQuestCheck(itemId, count); +        ItemAddedQuestCheck(itemId, count, false);          UpdateCriteria(CriteriaType::ObtainAnyItem, itemId, count);          UpdateCriteria(CriteriaType::AcquireItem, itemId, count); @@ -11595,7 +11615,7 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat              {                  ItemPosCountVec childDest;                  CanStoreItem_InInventorySlots(CHILD_EQUIPMENT_SLOT_START, CHILD_EQUIPMENT_SLOT_END, childDest, childTemplate, count, false, nullptr, NULL_BAG, NULL_SLOT); -                if (Item* childItem = StoreNewItem(childDest, childTemplate->GetId(), update, {}, {}, context, {}, addToCollection)) +                if (Item* childItem = StoreNewItem(childDest, childTemplate->GetId(), update, {}, {}, context, nullptr, addToCollection))                  {                      childItem->SetCreator(item->GetGUID());                      childItem->SetItemFlag(ITEM_FIELD_FLAG_CHILD); @@ -14003,7 +14023,7 @@ void Player::SendNewItem(Item* item, uint32 quantity, bool pushed, bool created,      packet.Item.Initialize(item); -    //packet.QuestLogItemID; +    packet.QuestLogItemID = item->GetTemplate()->QuestLogItemId;      packet.Quantity = quantity;      packet.QuantityInInventory = GetItemCount(item->GetEntry());      packet.BattlePetSpeciesID = item->GetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID); @@ -14708,11 +14728,6 @@ bool Player::CanCompleteRepeatableQuest(Quest const* quest)      if (!CanTakeQuest(quest, false))          return false; -    if (quest->HasQuestObjectiveType(QUEST_OBJECTIVE_ITEM)) -        for (QuestObjective const& obj : quest->GetObjectives()) -            if (obj.Type == QUEST_OBJECTIVE_ITEM && !HasItemCount(obj.ObjectID, obj.Amount)) -                return false; -      if (!CanRewardQuest(quest, false))          return false; @@ -14746,7 +14761,7 @@ bool Player::CanRewardQuest(Quest const* quest, bool msg) const      {          for (QuestObjective const& obj : quest->GetObjectives())          { -            if (obj.Type != QUEST_OBJECTIVE_ITEM) +            if (obj.Type != QUEST_OBJECTIVE_ITEM || obj.Flags2 & QUEST_OBJECTIVE_FLAG_2_QUEST_BOUND_ITEM)                  continue;              if (GetItemCount(obj.ObjectID) < uint32(obj.Amount)) @@ -16344,7 +16359,7 @@ void Player::AdjustQuestObjectiveProgress(Quest const* quest)      {          for (QuestObjective const& obj : quest->GetObjectives())          { -            if (obj.Type == QUEST_OBJECTIVE_ITEM) +            if (obj.Type == QUEST_OBJECTIVE_ITEM && !(obj.Flags2 & QUEST_OBJECTIVE_FLAG_2_QUEST_BOUND_ITEM))              {                  uint32 reqItemCount = obj.Amount;                  uint32 curItemCount = GetItemCount(obj.ObjectID, true); @@ -16543,9 +16558,34 @@ void Player::GroupEventHappens(uint32 questId, WorldObject const* pEventObject)          AreaExploredOrEventHappens(questId);  } -void Player::ItemAddedQuestCheck(uint32 entry, uint32 count) +namespace +{ +struct +{ +    std::function<bool(QuestObjective const*)> QuestBoundItem = [](QuestObjective const* objective) { return (objective->Flags2 & QUEST_OBJECTIVE_FLAG_2_QUEST_BOUND_ITEM) != 0; }; +    std::function<bool(QuestObjective const*)> NotQuestBoundItem = [](QuestObjective const* objective) { return (objective->Flags2 & QUEST_OBJECTIVE_FLAG_2_QUEST_BOUND_ITEM) == 0; }; +} const ItemQuestObjectiveFilters; +} + +void Player::ItemAddedQuestCheck(uint32 entry, uint32 count, Optional<bool> boundItemFlagRequirement /*= {}*/, bool* hadBoundItemObjective /*= nullptr*/)  { -    UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_ITEM, entry, count); +    std::vector<QuestObjective const*> updatedObjectives; +    std::function<bool(QuestObjective const*)> const* objectiveFilter = nullptr; +    if (boundItemFlagRequirement) +        objectiveFilter = *boundItemFlagRequirement ? &ItemQuestObjectiveFilters.QuestBoundItem : &ItemQuestObjectiveFilters.NotQuestBoundItem; + +    ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(entry); +    UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_ITEM, itemTemplate->GetId(), count, ObjectGuid::Empty, &updatedObjectives, objectiveFilter); +    if (itemTemplate->QuestLogItemId && (updatedObjectives.size() != 1 || !(updatedObjectives[0]->Flags2 & QUEST_OBJECTIVE_FLAG_2_QUEST_BOUND_ITEM))) +        UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_ITEM, itemTemplate->QuestLogItemId, count, ObjectGuid::Empty, &updatedObjectives, objectiveFilter); + +    if (updatedObjectives.size() == 1 && updatedObjectives[0]->Flags2 & QUEST_OBJECTIVE_FLAG_2_QUEST_BOUND_ITEM) +    { +        if (hadBoundItemObjective) +            *hadBoundItemObjective = updatedObjectives.size() == 1 && updatedObjectives[0]->Flags2 & QUEST_OBJECTIVE_FLAG_2_QUEST_BOUND_ITEM; + +        SendQuestUpdateAddItem(itemTemplate, *updatedObjectives[0], count); +    }  }  void Player::ItemRemovedQuestCheck(uint32 entry, uint32 /*count*/) @@ -16641,7 +16681,8 @@ void Player::CurrencyChanged(uint32 currencyId, int32 change)      UpdateQuestObjectiveProgress(QUEST_OBJECTIVE_OBTAIN_CURRENCY, currencyId, change);  } -void Player::UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int32 objectId, int64 addCount, ObjectGuid victimGuid) +void Player::UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int32 objectId, int64 addCount, ObjectGuid victimGuid /*= ObjectGuid::Empty*/, +    std::vector<QuestObjective const*>* updatedObjectives /*= nullptr*/, std::function<bool(QuestObjective const*)> const* objectiveFilter /*= nullptr*/)  {      bool anyObjectiveChangedCompletionState = false;      bool updatePhaseShift = false; @@ -16668,116 +16709,125 @@ void Player::UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int3                  continue;          bool objectiveWasComplete = IsQuestObjectiveComplete(logSlot, quest, *objective); -        if (!objectiveWasComplete || addCount < 0) +        if (objectiveWasComplete && addCount >= 0) +            continue; + +        if (objectiveFilter && !(*objectiveFilter)(objective)) +            continue; + +        bool objectiveIsNowComplete = false; +        if (objective->IsStoringValue())          { -            bool objectiveIsNowComplete = false; -            if (objective->IsStoringValue()) -            { -                if (objectiveType == QUEST_OBJECTIVE_PLAYERKILLS && objective->Flags & QUEST_OBJECTIVE_FLAG_KILL_PLAYERS_SAME_FACTION) -                    if (Player const* victim = ObjectAccessor::GetPlayer(GetMap(), victimGuid)) -                        if (victim->GetEffectiveTeam() != GetEffectiveTeam()) -                            continue; +            if (objectiveType == QUEST_OBJECTIVE_PLAYERKILLS && objective->Flags & QUEST_OBJECTIVE_FLAG_KILL_PLAYERS_SAME_FACTION) +                if (Player const* victim = ObjectAccessor::GetPlayer(GetMap(), victimGuid)) +                    if (victim->GetEffectiveTeam() != GetEffectiveTeam()) +                        continue; -                int32 currentProgress = GetQuestSlotObjectiveData(logSlot, *objective); -                if (addCount > 0 ? (currentProgress < objective->Amount) : (currentProgress > 0)) +            int32 currentProgress = GetQuestSlotObjectiveData(logSlot, *objective); +            if (addCount > 0 ? (currentProgress < objective->Amount) : (currentProgress > 0)) +            { +                int32 newProgress = std::clamp<int32>(currentProgress + addCount, 0, objective->Amount); +                SetQuestObjectiveData(*objective, newProgress); +                if (addCount > 0 && !(objective->Flags & QUEST_OBJECTIVE_FLAG_HIDE_CREDIT_MSG))                  { -                    int32 newProgress = std::clamp<int32>(currentProgress + addCount, 0, objective->Amount); -                    SetQuestObjectiveData(*objective, newProgress); -                    if (addCount > 0 && !(objective->Flags & QUEST_OBJECTIVE_FLAG_HIDE_CREDIT_MSG)) +                    switch (objectiveType)                      { -                        switch (objectiveType) -                        { -                            case QUEST_OBJECTIVE_ITEM: -                                break; // case handled by SMSG_ITEM_PUSH_RESULT -                            case QUEST_OBJECTIVE_PLAYERKILLS: -                                SendQuestUpdateAddPlayer(quest, newProgress); -                                break; -                            default: -                                SendQuestUpdateAddCredit(quest, victimGuid, *objective, newProgress); -                                break; -                        } +                        case QUEST_OBJECTIVE_ITEM: +                            break; // case handled by SMSG_ITEM_PUSH_RESULT +                        case QUEST_OBJECTIVE_PLAYERKILLS: +                            SendQuestUpdateAddPlayer(quest, newProgress); +                            break; +                        default: +                            SendQuestUpdateAddCredit(quest, victimGuid, *objective, newProgress); +                            break;                      } - -                    objectiveIsNowComplete = IsQuestObjectiveComplete(logSlot, quest, *objective);                  } -            } -            else if (objective->IsStoringFlag()) -            { -                SetQuestObjectiveData(*objective, addCount > 0); - -                if (addCount > 0 && !(objective->Flags & QUEST_OBJECTIVE_FLAG_HIDE_CREDIT_MSG)) -                    SendQuestUpdateAddCreditSimple(*objective);                  objectiveIsNowComplete = IsQuestObjectiveComplete(logSlot, quest, *objective);              } -            else +        } +        else if (objective->IsStoringFlag()) +        { +            SetQuestObjectiveData(*objective, addCount > 0); + +            if (addCount > 0 && !(objective->Flags & QUEST_OBJECTIVE_FLAG_HIDE_CREDIT_MSG)) +                SendQuestUpdateAddCreditSimple(*objective); + +            objectiveIsNowComplete = IsQuestObjectiveComplete(logSlot, quest, *objective); +        } +        else +        { +            switch (objectiveType)              { -                switch (objectiveType) -                { -                    case QUEST_OBJECTIVE_CURRENCY: -                        objectiveIsNowComplete = GetCurrencyQuantity(objectId) + addCount >= objective->Amount; -                        break; -                    case QUEST_OBJECTIVE_LEARNSPELL: -                        objectiveIsNowComplete = addCount != 0; -                        break; -                    case QUEST_OBJECTIVE_MIN_REPUTATION: -                        objectiveIsNowComplete = GetReputationMgr().GetReputation(objectId) + addCount >= objective->Amount; -                        break; -                    case QUEST_OBJECTIVE_MAX_REPUTATION: -                        objectiveIsNowComplete = GetReputationMgr().GetReputation(objectId) + addCount <= objective->Amount; -                        break; -                    case QUEST_OBJECTIVE_MONEY: -                        objectiveIsNowComplete = int64(GetMoney()) + addCount >= objective->Amount; -                        break; -                    case QUEST_OBJECTIVE_PROGRESS_BAR: -                        objectiveIsNowComplete = IsQuestObjectiveProgressBarComplete(logSlot, quest); -                        break; -                    default: -                        ABORT_MSG("Unhandled quest objective type %u", uint32(objectiveType)); -                        break; -                } +                case QUEST_OBJECTIVE_CURRENCY: +                    objectiveIsNowComplete = GetCurrencyQuantity(objectId) + addCount >= objective->Amount; +                    break; +                case QUEST_OBJECTIVE_LEARNSPELL: +                    objectiveIsNowComplete = addCount != 0; +                    break; +                case QUEST_OBJECTIVE_MIN_REPUTATION: +                    objectiveIsNowComplete = GetReputationMgr().GetReputation(objectId) + addCount >= objective->Amount; +                    break; +                case QUEST_OBJECTIVE_MAX_REPUTATION: +                    objectiveIsNowComplete = GetReputationMgr().GetReputation(objectId) + addCount <= objective->Amount; +                    break; +                case QUEST_OBJECTIVE_MONEY: +                    objectiveIsNowComplete = int64(GetMoney()) + addCount >= objective->Amount; +                    break; +                case QUEST_OBJECTIVE_PROGRESS_BAR: +                    objectiveIsNowComplete = IsQuestObjectiveProgressBarComplete(logSlot, quest); +                    break; +                default: +                    ABORT_MSG("Unhandled quest objective type %u", uint32(objectiveType)); +                    break;              } +        } -            if (objective->Flags & QUEST_OBJECTIVE_FLAG_PART_OF_PROGRESS_BAR) +        if (objective->Flags & QUEST_OBJECTIVE_FLAG_PART_OF_PROGRESS_BAR) +        { +            if (IsQuestObjectiveProgressBarComplete(logSlot, quest))              { -                if (IsQuestObjectiveProgressBarComplete(logSlot, quest)) +                auto progressBarObjectiveItr = std::find_if(quest->GetObjectives().begin(), quest->GetObjectives().end(), [](QuestObjective const& otherObjective)                  { -                    auto progressBarObjectiveItr = std::find_if(quest->GetObjectives().begin(), quest->GetObjectives().end(), [](QuestObjective const& otherObjective) -                    { -                        return otherObjective.Type == QUEST_OBJECTIVE_PROGRESS_BAR && !(otherObjective.Flags & QUEST_OBJECTIVE_FLAG_PART_OF_PROGRESS_BAR); -                    }); -                    if (progressBarObjectiveItr != quest->GetObjectives().end()) -                        SendQuestUpdateAddCreditSimple(*progressBarObjectiveItr); +                    return otherObjective.Type == QUEST_OBJECTIVE_PROGRESS_BAR && !(otherObjective.Flags & QUEST_OBJECTIVE_FLAG_PART_OF_PROGRESS_BAR); +                }); +                if (progressBarObjectiveItr != quest->GetObjectives().end()) +                    SendQuestUpdateAddCreditSimple(*progressBarObjectiveItr); -                    objectiveIsNowComplete = true; -                } +                objectiveIsNowComplete = true;              } +        } -            if (objectiveWasComplete != objectiveIsNowComplete) -                anyObjectiveChangedCompletionState = true; +        if (objectiveWasComplete != objectiveIsNowComplete) +            anyObjectiveChangedCompletionState = true; -            if (objectiveIsNowComplete && objective->CompletionEffect) -            { -                if (objective->CompletionEffect->GameEventId) -                    GameEvents::Trigger(*objective->CompletionEffect->GameEventId, this, nullptr); -                if (objective->CompletionEffect->SpellId) -                    CastSpell(this, *objective->CompletionEffect->SpellId, true); -                if (objective->CompletionEffect->ConversationId) -                    Conversation::CreateConversation(*objective->CompletionEffect->ConversationId, this, GetPosition(), GetGUID()); -                if (objective->CompletionEffect->UpdatePhaseShift) -                    updatePhaseShift = true; -                if (objective->CompletionEffect->UpdateZoneAuras) -                    updateZoneAuras = true; -            } +        if (objectiveIsNowComplete && objective->CompletionEffect) +        { +            if (objective->CompletionEffect->GameEventId) +                GameEvents::Trigger(*objective->CompletionEffect->GameEventId, this, nullptr); +            if (objective->CompletionEffect->SpellId) +                CastSpell(this, *objective->CompletionEffect->SpellId, true); +            if (objective->CompletionEffect->ConversationId) +                Conversation::CreateConversation(*objective->CompletionEffect->ConversationId, this, GetPosition(), GetGUID()); +            if (objective->CompletionEffect->UpdatePhaseShift) +                updatePhaseShift = true; +            if (objective->CompletionEffect->UpdateZoneAuras) +                updateZoneAuras = true; +        } -            if (objectiveIsNowComplete) -            { -                if (CanCompleteQuest(questId, objective->ID)) -                    CompleteQuest(questId); -            } -            else if (!(objective->Flags & QUEST_OBJECTIVE_FLAG_OPTIONAL) && objectiveItr.second.QuestStatusItr->second.Status == QUEST_STATUS_COMPLETE) -                IncompleteQuest(questId); +        if (objectiveIsNowComplete) +        { +            if (CanCompleteQuest(questId, objective->ID)) +                CompleteQuest(questId);          } +        else if (!(objective->Flags & QUEST_OBJECTIVE_FLAG_OPTIONAL) && objectiveItr.second.QuestStatusItr->second.Status == QUEST_STATUS_COMPLETE) +            IncompleteQuest(questId); + +        if (updatedObjectives) +            updatedObjectives->push_back(objective); + +        if (objective->Type == QUEST_OBJECTIVE_ITEM && addCount >= 0 && objective->Flags2 & QUEST_OBJECTIVE_FLAG_2_QUEST_BOUND_ITEM) +            break;      }      if (anyObjectiveChangedCompletionState) @@ -16796,21 +16846,8 @@ void Player::UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int3  bool Player::HasQuestForItem(uint32 itemid) const  {      // Search incomplete objective first -    for (QuestObjectiveStatusMap::value_type const& objectiveItr : Trinity::Containers::MapEqualRange(m_questObjectiveStatus, { QUEST_OBJECTIVE_ITEM, itemid })) -    { -        Quest const* qInfo = sObjectMgr->GetQuestTemplate(objectiveItr.second.QuestStatusItr->first); -        QuestObjective const* objective = sObjectMgr->GetQuestObjective(objectiveItr.second.ObjectiveId); -        if (!qInfo || !objective || !IsQuestObjectiveCompletable(objectiveItr.second.QuestStatusItr->second.Slot, qInfo, *objective)) -            continue; - -        // hide quest if player is in raid-group and quest is no raid quest -        if (GetGroup() && GetGroup()->isRaidGroup() && !qInfo->IsAllowedInRaid(GetMap()->GetDifficultyID())) -            if (!InBattleground()) //there are two ways.. we can make every bg-quest a raidquest, or add this code here.. i don't know if this can be exploited by other quests, but i think all other quests depend on a specific area.. but keep this in mind, if something strange happens later -                continue; - -        if (!IsQuestObjectiveComplete(objectiveItr.second.QuestStatusItr->second.Slot, qInfo, *objective)) -            return true; -    } +    if (GetQuestObjectiveForItem(itemid, true)) +        return true;      // This part - for ItemDrop      for (std::pair<uint32 const, QuestStatusData> const& questStatus : m_QuestStatus) @@ -16847,6 +16884,39 @@ bool Player::HasQuestForItem(uint32 itemid) const      return false;  } +QuestObjective const* Player::GetQuestObjectiveForItem(uint32 itemId, bool onlyIncomplete) const +{ +    auto findObjectiveForItem = [this, onlyIncomplete](uint32 itemId) -> QuestObjective const* +    { +        for (QuestObjectiveStatusMap::value_type const& objectiveItr : Trinity::Containers::MapEqualRange(m_questObjectiveStatus, { QUEST_OBJECTIVE_ITEM, itemId })) +        { +            Quest const* qInfo = sObjectMgr->GetQuestTemplate(objectiveItr.second.QuestStatusItr->first); +            QuestObjective const* objective = sObjectMgr->GetQuestObjective(objectiveItr.second.ObjectiveId); +            if (!qInfo || !objective || !IsQuestObjectiveCompletable(objectiveItr.second.QuestStatusItr->second.Slot, qInfo, *objective)) +                continue; + +            // hide quest if player is in raid-group and quest is no raid quest +            if (GetGroup() && GetGroup()->isRaidGroup() && !qInfo->IsAllowedInRaid(GetMap()->GetDifficultyID())) +                if (!InBattleground()) //there are two ways.. we can make every bg-quest a raidquest, or add this code here.. i don't know if this can be exploited by other quests, but i think all other quests depend on a specific area.. but keep this in mind, if something strange happens later +                    continue; + +            if (!onlyIncomplete || !IsQuestObjectiveComplete(objectiveItr.second.QuestStatusItr->second.Slot, qInfo, *objective)) +                return objective; +        } +        return nullptr; +    }; + +    if (QuestObjective const* objective = findObjectiveForItem(itemId)) +        return objective; + +    if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId)) +        if (itemTemplate->QuestLogItemId) +            if (QuestObjective const* objective = findObjectiveForItem(itemTemplate->QuestLogItemId)) +                return objective; + +    return nullptr; +} +  int32 Player::GetQuestObjectiveData(QuestObjective const& objective) const  {      uint16 slot = FindQuestSlot(objective.QuestID); @@ -17200,6 +17270,27 @@ void Player::SendQuestUpdateAddCreditSimple(QuestObjective const& obj) const      SendDirectMessage(packet.Write());  } +void Player::SendQuestUpdateAddItem(ItemTemplate const* itemTemplate, QuestObjective const& obj, uint16 count) const +{ +    WorldPackets::Item::ItemPushResult packet; + +    packet.PlayerGUID = GetGUID(); + +    packet.Slot = INVENTORY_SLOT_BAG_0; +    packet.SlotInBag = 0; +    packet.Item.ItemID = itemTemplate->GetId(); +    packet.QuestLogItemID = itemTemplate->QuestLogItemId; +    packet.Quantity = count; +    packet.QuantityInInventory = GetQuestObjectiveData(obj); +    packet.DisplayText = static_cast<WorldPackets::Item::ItemPushResult::DisplayType>(3); +    packet.Unused_1017 = true; + +    if (GetGroup() && !itemTemplate->HasFlag(ITEM_FLAG3_DONT_REPORT_LOOT_LOG_TO_PARTY)) +        GetGroup()->BroadcastPacket(packet.Write(), true); +    else +        SendDirectMessage(packet.Write()); +} +  void Player::SendQuestUpdateAddPlayer(Quest const* quest, uint16 newCount) const  {      WorldPackets::Quest::QuestUpdateAddPvPCredit questUpdateAddPvpCredit; @@ -26210,13 +26301,12 @@ void Player::StoreLootItem(ObjectGuid lootWorldObjectGuid, uint8 lootSlot, Loot*          --loot->unlootedCount; -        if (sObjectMgr->GetItemTemplate(item->itemid)) -            if (newitem->GetQuality() > ITEM_QUALITY_EPIC || (newitem->GetQuality() == ITEM_QUALITY_EPIC && newitem->GetItemLevel(this) >= MinNewsItemLevel)) -                if (Guild* guild = GetGuild()) -                    guild->AddGuildNews(GUILD_NEWS_ITEM_LOOTED, GetGUID(), 0, item->itemid); +        if (newitem && (newitem->GetQuality() > ITEM_QUALITY_EPIC || (newitem->GetQuality() == ITEM_QUALITY_EPIC && newitem->GetItemLevel(this) >= MinNewsItemLevel))) +            if (Guild* guild = GetGuild()) +                guild->AddGuildNews(GUILD_NEWS_ITEM_LOOTED, GetGUID(), 0, item->itemid);          // if aeLooting then we must delay sending out item so that it appears properly stacked in chat -        if (!aeResult) +        if (!aeResult || !newitem)          {              SendNewItem(newitem, uint32(item->count), false, false, true, loot->GetDungeonEncounterId());              UpdateCriteria(CriteriaType::LootItem, item->itemid, item->count); @@ -26230,7 +26320,10 @@ void Player::StoreLootItem(ObjectGuid lootWorldObjectGuid, uint8 lootSlot, Loot*          if (loot->loot_type == LOOT_ITEM)              sLootItemStorage->RemoveStoredLootItemForContainer(lootWorldObjectGuid.GetCounter(), item->itemid, item->count, item->LootListId); -        ApplyItemLootedSpell(newitem, true); +        if (newitem) +            ApplyItemLootedSpell(newitem, true); +        else +            ApplyItemLootedSpell(sObjectMgr->GetItemTemplate(item->itemid));      }      else          SendEquipError(msg, nullptr, nullptr, item->itemid); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 7c5a4d33d6c..7c9f559ca2e 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1624,7 +1624,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>          uint16 GetReqKillOrCastCurrentCount(uint32 quest_id, int32 entry) const;          void AreaExploredOrEventHappens(uint32 questId);          void GroupEventHappens(uint32 questId, WorldObject const* pEventObject); -        void ItemAddedQuestCheck(uint32 entry, uint32 count); +        void ItemAddedQuestCheck(uint32 entry, uint32 count, Optional<bool> boundItemFlagRequirement = {}, bool* hadBoundItemObjective = nullptr);          void ItemRemovedQuestCheck(uint32 entry, uint32 count);          void KilledMonster(CreatureTemplate const* cInfo, ObjectGuid guid);          void KilledMonsterCredit(uint32 entry, ObjectGuid guid = ObjectGuid::Empty); @@ -1635,8 +1635,10 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>          void MoneyChanged(uint64 value);          void ReputationChanged(FactionEntry const* factionEntry, int32 change);          void CurrencyChanged(uint32 currencyId, int32 change); -        void UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int32 objectId, int64 addCount, ObjectGuid victimGuid = ObjectGuid::Empty); +        void UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int32 objectId, int64 addCount, ObjectGuid victimGuid = ObjectGuid::Empty, +            std::vector<QuestObjective const*>* updatedObjectives = nullptr, std::function<bool(QuestObjective const*)> const* objectiveFilter = nullptr);          bool HasQuestForItem(uint32 itemId) const; +        QuestObjective const* GetQuestObjectiveForItem(uint32 itemId, bool onlyIncomplete) const;          bool HasQuestForGO(int32 goId) const;          void UpdateVisibleGameobjectsOrSpellClicks();          bool CanShareQuest(uint32 questId) const; @@ -1656,6 +1658,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>          void SendPushToPartyResponse(Player const* player, QuestPushReason reason, Quest const* quest = nullptr) const;          void SendQuestUpdateAddCredit(Quest const* quest, ObjectGuid guid, QuestObjective const& obj, uint16 count) const;          void SendQuestUpdateAddCreditSimple(QuestObjective const& obj) const; +        void SendQuestUpdateAddItem(ItemTemplate const* itemTemplate, QuestObjective const& obj, uint16 count) const;          void SendQuestUpdateAddPlayer(Quest const* quest, uint16 newCount) const;          void SendQuestGiverStatusMultiple();          void SendQuestGiverStatusMultiple(GuidUnorderedSet const& guids); @@ -2360,6 +2363,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>          void CastItemCombatSpell(DamageInfo const& damageInfo, Item* item, ItemTemplate const* proto);          void CastItemUseSpell(Item* item, SpellCastTargets const& targets, ObjectGuid castCount, int32* misc);          void ApplyItemLootedSpell(Item* item, bool apply); +        void ApplyItemLootedSpell(ItemTemplate const* itemTemplate);          void SendEquipmentSetList();          void SetEquipmentSet(EquipmentSetInfo::EquipmentSetData const& newEqSet); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index f75ea583546..0370c24c547 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -3256,6 +3256,7 @@ void ObjectMgr::LoadItemTemplates()          itemTemplate.SpellPPMRate = 0.0f;          itemTemplate.RandomBonusListTemplateId = 0;          itemTemplate.ItemSpecClassMask = 0; +        itemTemplate.QuestLogItemId = 0;          if (std::vector<ItemSpecOverrideEntry const*> const* itemSpecOverrides = sDB2Manager.GetItemSpecOverrides(sparse->ID))          { @@ -3328,7 +3329,7 @@ void ObjectMgr::LoadItemTemplateAddon()      uint32 oldMSTime = getMSTime();      uint32 count = 0; -    QueryResult result = WorldDatabase.Query("SELECT Id, FlagsCu, FoodType, MinMoneyLoot, MaxMoneyLoot, SpellPPMChance, RandomBonusListTemplateId FROM item_template_addon"); +    QueryResult result = WorldDatabase.Query("SELECT Id, FlagsCu, FoodType, MinMoneyLoot, MaxMoneyLoot, SpellPPMChance, RandomBonusListTemplateId, QuestLogItemId FROM item_template_addon");      if (result)      {          do @@ -3355,6 +3356,7 @@ void ObjectMgr::LoadItemTemplateAddon()              itemTemplate->MaxMoneyLoot = maxMoneyLoot;              itemTemplate->SpellPPMRate = fields[5].GetFloat();              itemTemplate->RandomBonusListTemplateId = fields[6].GetUInt32(); +            itemTemplate->QuestLogItemId = fields[7].GetInt32();              ++count;          } while (result->NextRow());      } diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index 223ee9a84a3..fe2b6f73510 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -458,8 +458,10 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPackets::Loot::MasterLootItem          }          // now move item from loot to target inventory -        Item* newitem = target->StoreNewItem(dest, item.itemid, true, item.randomBonusListId, item.GetAllowedLooters(), item.context, &item.BonusListIDs); -        aeResult.Add(newitem, item.count, loot->loot_type, loot->GetDungeonEncounterId()); +        if (Item* newitem = target->StoreNewItem(dest, item.itemid, true, item.randomBonusListId, item.GetAllowedLooters(), item.context, &item.BonusListIDs)) +            aeResult.Add(newitem, item.count, loot->loot_type, loot->GetDungeonEncounterId()); +        else +            target->ApplyItemLootedSpell(sObjectMgr->GetItemTemplate(item.itemid));          // mark as looted          item.count = 0; diff --git a/src/server/game/Handlers/VoidStorageHandler.cpp b/src/server/game/Handlers/VoidStorageHandler.cpp index cafa7070c3e..1d143c8a820 100644 --- a/src/server/game/Handlers/VoidStorageHandler.cpp +++ b/src/server/game/Handlers/VoidStorageHandler.cpp @@ -187,10 +187,12 @@ void WorldSession::HandleVoidStorageTransfer(WorldPackets::VoidStorage::VoidStor              return;          } -        Item* item = _player->StoreNewItem(dest, itemVS->ItemEntry, true, itemVS->RandomBonusListId, GuidSet(), itemVS->Context, &itemVS->BonusListIDs); -        item->SetCreator(itemVS->CreatorGuid); -        item->SetBinding(true); -        GetCollectionMgr()->AddItemAppearance(item); +        if (Item* item = _player->StoreNewItem(dest, itemVS->ItemEntry, true, itemVS->RandomBonusListId, GuidSet(), itemVS->Context, &itemVS->BonusListIDs)) +        { +            item->SetCreator(itemVS->CreatorGuid); +            item->SetBinding(true); +            GetCollectionMgr()->AddItemAppearance(item); +        }          voidStorageTransferChanges.RemovedItems.push_back(ObjectGuid::Create<HighGuid::Item>(itemVS->ItemId)); diff --git a/src/server/game/Loot/Loot.cpp b/src/server/game/Loot/Loot.cpp index c005aba2ccb..2cd94891236 100644 --- a/src/server/game/Loot/Loot.cpp +++ b/src/server/game/Loot/Loot.cpp @@ -888,9 +888,13 @@ bool Loot::AutoStore(Player* player, uint8 bag, uint8 slot, bool broadcast, bool          --unlootedCount; -        Item* pItem = player->StoreNewItem(dest, lootItem->itemid, true, lootItem->randomBonusListId, GuidSet(), lootItem->context, &lootItem->BonusListIDs); -        player->SendNewItem(pItem, lootItem->count, false, createdByPlayer, broadcast); -        player->ApplyItemLootedSpell(pItem, true); +        if (Item* pItem = player->StoreNewItem(dest, lootItem->itemid, true, lootItem->randomBonusListId, GuidSet(), lootItem->context, &lootItem->BonusListIDs)) +        { +            player->SendNewItem(pItem, lootItem->count, false, createdByPlayer, broadcast, GetDungeonEncounterId()); +            player->ApplyItemLootedSpell(pItem, true); +        } +        else +            player->ApplyItemLootedSpell(sObjectMgr->GetItemTemplate(lootItem->itemid));      }      return allLooted; diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h index e64bdee1124..38410aefa0d 100644 --- a/src/server/game/Quests/QuestDef.h +++ b/src/server/game/Quests/QuestDef.h @@ -367,6 +367,11 @@ enum QuestObjectiveFlags      QUEST_OBJECTIVE_FLAG_IGNORE_SOULBOUND_ITEMS             = 0x0200,  }; +enum QuestObjectiveFlags2 +{ +    QUEST_OBJECTIVE_FLAG_2_QUEST_BOUND_ITEM = 0x1   // Item is bound to a single objective, only increments the counter for one quest if multiple require the same item and is not stored in inventory +}; +  enum class QuestCompleteSpellType : uint32  {      LegacyBehavior  = 0, diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index e9e0c7b2cce..7d8551f55dc 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -4958,13 +4958,8 @@ void AuraEffect::HandleChannelDeathItem(AuraApplication const* aurApp, uint8 mod              return;      } -    Item* newitem = plCaster->StoreNewItem(dest, GetSpellEffectInfo().ItemType, true); -    if (!newitem) -    { -        plCaster->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); -        return; -    } -    plCaster->SendNewItem(newitem, count, true, true); +    if (Item* newitem = plCaster->StoreNewItem(dest, GetSpellEffectInfo().ItemType, true)) +        plCaster->SendNewItem(newitem, count, true, true);  }  void AuraEffect::HandleBindSight(AuraApplication const* aurApp, uint8 mode, bool apply) const diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index f001921fbf2..d24e3337e6c 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1369,25 +1369,19 @@ void Spell::DoCreateItem(uint32 itemId, ItemContext context /*= ItemContext::NON      if (num_to_add)      {          // create the new item and store it -        Item* pItem = player->StoreNewItem(dest, newitemid, true, GenerateItemRandomBonusListId(newitemid), GuidSet(), context, bonusListIDs); - -        // was it successful? return error if not -        if (!pItem) +        if (Item* pItem = player->StoreNewItem(dest, newitemid, true, GenerateItemRandomBonusListId(newitemid), GuidSet(), context, bonusListIDs))          { -            player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); -            return; -        } +            // set the "Crafted by ..." property of the item +            if (pItem->GetTemplate()->HasSignature()) +                pItem->SetCreator(player->GetGUID()); -        // set the "Crafted by ..." property of the item -        if (pItem->GetTemplate()->HasSignature()) -            pItem->SetCreator(player->GetGUID()); +            // send info to the client +            player->SendNewItem(pItem, num_to_add, true, true); -        // send info to the client -        player->SendNewItem(pItem, num_to_add, true, true); - -        if (pItem->GetQuality() > ITEM_QUALITY_EPIC || (pItem->GetQuality() == ITEM_QUALITY_EPIC && pItem->GetItemLevel(player) >= MinNewsItemLevel)) -            if (Guild* guild = player->GetGuild()) -                guild->AddGuildNews(GUILD_NEWS_ITEM_CRAFTED, player->GetGUID(), 0, pProto->GetId()); +            if (pItem->GetQuality() > ITEM_QUALITY_EPIC || (pItem->GetQuality() == ITEM_QUALITY_EPIC && pItem->GetItemLevel(player) >= MinNewsItemLevel)) +                if (Guild* guild = player->GetGuild()) +                    guild->AddGuildNews(GUILD_NEWS_ITEM_CRAFTED, player->GetGUID(), 0, pProto->GetId()); +        }          // we succeeded in creating at least one item, so a levelup is possible          player->UpdateCraftSkill(m_spellInfo); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 30504875544..7e9a54e2879 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -1529,6 +1529,8 @@ public:                  Item* item = playerTarget->StoreNewItem(dest, itemTemplatePair.first, true, {}, GuidSet(), itemContext,                      bonusListIDsForItem.empty() ? nullptr : &bonusListIDsForItem); +                if (!item) +                    continue;                  // remove binding (let GM give it to another player later)                  if (player == playerTarget) | 
