diff options
Diffstat (limited to 'src')
-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) |