aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2023-12-30 15:45:37 +0100
committerShauren <shauren.trinity@gmail.com>2023-12-30 15:45:37 +0100
commit4dce6ab228c54319032b346d2d791262eac3d9cd (patch)
tree27049cf934572481a3f661461ee2add586313df2 /src
parentf26a93a8c1bd0a7c3c3c3362f29ec809fb96a68b (diff)
Core/Quests: Implemented QUEST_OBJECTIVE_FLAG_2_QUEST_BOUND_ITEM that makes required items not be stored in inventory
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Entities/Item/Item.h1
-rw-r--r--src/server/game/Entities/Item/ItemTemplate.h1
-rw-r--r--src/server/game/Entities/Player/Player.cpp341
-rw-r--r--src/server/game/Entities/Player/Player.h8
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp4
-rw-r--r--src/server/game/Handlers/LootHandler.cpp6
-rw-r--r--src/server/game/Handlers/VoidStorageHandler.cpp10
-rw-r--r--src/server/game/Loot/Loot.cpp10
-rw-r--r--src/server/game/Quests/QuestDef.h5
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp9
-rw-r--r--src/server/game/Spells/SpellEffects.cpp26
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp2
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)