diff --git a/Source/Framework/Constants/QuestConst.cs b/Source/Framework/Constants/QuestConst.cs index ec5ff18f8..35ed5c403 100644 --- a/Source/Framework/Constants/QuestConst.cs +++ b/Source/Framework/Constants/QuestConst.cs @@ -31,6 +31,7 @@ namespace Framework.Constants Max } + [Flags] public enum QuestObjectiveFlags { TrackedOnMinimap = 0x01, // Client Displays Large Yellow Blob On Minimap For Creature/Gameobject @@ -45,6 +46,12 @@ namespace Framework.Constants IgnoreSoulboundItems = 0x0200, } + [Flags] + public enum QuestObjectiveFlags2 + { + QuestBoundItem = 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 + } + public enum QuestCompleteSpellType { LegacyBehavior = 0, diff --git a/Source/Game/Chat/Commands/MiscCommands.cs b/Source/Game/Chat/Commands/MiscCommands.cs index a496ac6fa..65271f6a5 100644 --- a/Source/Game/Chat/Commands/MiscCommands.cs +++ b/Source/Game/Chat/Commands/MiscCommands.cs @@ -2240,6 +2240,8 @@ namespace Game.Chat } Item item = playerTarget.StoreNewItem(dest, template.Value.GetId(), true, 0, null, itemContext, bonusListIDsForItem.Empty() ? null : bonusListIDsForItem); + if (item == null) + continue; // remove binding (let GM give it to another player later) if (player == playerTarget) diff --git a/Source/Game/Entities/Item/ItemTemplate.cs b/Source/Game/Entities/Item/ItemTemplate.cs index eb2386858..6fd96e92f 100644 --- a/Source/Game/Entities/Item/ItemTemplate.cs +++ b/Source/Game/Entities/Item/ItemTemplate.cs @@ -352,6 +352,7 @@ namespace Game.Entities public uint RandomBonusListTemplateId; public BitSet[] Specializations = new BitSet[3]; public uint ItemSpecClassMask; + public int QuestLogItemId; protected ItemRecord BasicData; protected ItemSparseRecord ExtendedData; diff --git a/Source/Game/Entities/Player/Player.Items.cs b/Source/Game/Entities/Player/Player.Items.cs index 279251cfe..f2bebf64c 100644 --- a/Source/Game/Entities/Player/Player.Items.cs +++ b/Source/Game/Entities/Player/Player.Items.cs @@ -1246,6 +1246,11 @@ namespace Game.Entities foreach (var itemPosCount in pos) count += itemPosCount.count; + // quest objectives must be processed twice - QUEST_OBJECTIVE_FLAG_2_QUEST_BOUND_ITEM prevents item creation + ItemAddedQuestCheck(itemId, count, true, out bool hadBoundItemObjective); + if (hadBoundItemObjective) + return null; + Item item = Item.CreateItem(itemId, count, context, this, bonusListIDs == null); if (item != null) { @@ -1256,7 +1261,7 @@ namespace Game.Entities item = StoreItem(pos, item, update); - ItemAddedQuestCheck(itemId, count); + ItemAddedQuestCheck(itemId, count, false); UpdateCriteria(CriteriaType.ObtainAnyItem, itemId, count); UpdateCriteria(CriteriaType.AcquireItem, itemId, count); @@ -2500,7 +2505,7 @@ namespace Game.Entities packet.Item = new ItemInstance(item); - //packet.QuestLogItemID; + packet.QuestLogItemID = item.GetTemplate().QuestLogItemId; packet.Quantity = quantity; packet.QuantityInInventory = GetItemCount(item.GetEntry()); packet.BattlePetSpeciesID = (int)item.GetModifier(ItemModifier.BattlePetSpeciesId); @@ -3939,6 +3944,20 @@ namespace Game.Entities } } + public void ApplyItemLootedSpell(ItemTemplate itemTemplate) + { + if (itemTemplate.HasFlag(ItemFlags.Legacy)) + return; + + foreach (var effect in itemTemplate.Effects) + { + if (effect.TriggerType != ItemSpelltriggerType.OnLooted) + continue; + + CastSpell(this, (uint)effect.SpellID, true); + } + } + void _RemoveAllItemMods() { Log.outDebug(LogFilter.Player, "_RemoveAllItemMods start."); @@ -4856,17 +4875,17 @@ namespace Game.Entities slots[0] = ProfessionSlots.FishingTool; break; } - case ItemSubclassProfession.Blacksmithing : - case ItemSubclassProfession.Leatherworking : - case ItemSubclassProfession.Alchemy : - case ItemSubclassProfession.Herbalism : - case ItemSubclassProfession.Mining : - case ItemSubclassProfession.Tailoring : - case ItemSubclassProfession.Engineering : - case ItemSubclassProfession.Enchanting : - case ItemSubclassProfession.Skinning : - case ItemSubclassProfession.Jewelcrafting : - case ItemSubclassProfession.Inscription : + case ItemSubclassProfession.Blacksmithing: + case ItemSubclassProfession.Leatherworking: + case ItemSubclassProfession.Alchemy: + case ItemSubclassProfession.Herbalism: + case ItemSubclassProfession.Mining: + case ItemSubclassProfession.Tailoring: + case ItemSubclassProfession.Engineering: + case ItemSubclassProfession.Enchanting: + case ItemSubclassProfession.Skinning: + case ItemSubclassProfession.Jewelcrafting: + case ItemSubclassProfession.Inscription: { int professionSlot = GetProfessionSlotFor(itemSkill); if (professionSlot == -1) @@ -6105,18 +6124,15 @@ namespace Game.Entities --loot.unlootedCount; - if (Global.ObjectMgr.GetItemTemplate(item.itemid) != null) + if (newitem != null && newitem.GetQuality() > ItemQuality.Epic || (newitem.GetQuality() == ItemQuality.Epic && newitem.GetItemLevel(this) >= GuildConst.MinNewsItemLevel)) { - if (newitem.GetQuality() > ItemQuality.Epic || (newitem.GetQuality() == ItemQuality.Epic && newitem.GetItemLevel(this) >= GuildConst.MinNewsItemLevel)) - { - Guild guild = GetGuild(); - if (guild != null) - guild.AddGuildNews(GuildNews.ItemLooted, GetGUID(), 0, item.itemid); - } - } + Guild guild = GetGuild(); + if (guild != null) + guild.AddGuildNews(GuildNews.ItemLooted, GetGUID(), 0, item.itemid); + } // if aeLooting then we must delay sending out item so that it appears properly stacked in chat - if (aeResult == null) + if (aeResult == null || newitem == null) { SendNewItem(newitem, item.count, false, false, true, loot.GetDungeonEncounterId()); UpdateCriteria(CriteriaType.LootItem, item.itemid, item.count); @@ -6130,7 +6146,10 @@ namespace Game.Entities if (loot.loot_type == LootType.Item) Global.LootItemStorage.RemoveStoredLootItemForContainer(lootWorldObjectGuid.GetCounter(), item.itemid, item.count, item.LootListId); - ApplyItemLootedSpell(newitem, true); + if (newitem != null) + ApplyItemLootedSpell(newitem, true); + else + ApplyItemLootedSpell(Global.ObjectMgr.GetItemTemplate(item.itemid)); } else SendEquipError(msg, null, null, item.itemid); diff --git a/Source/Game/Entities/Player/Player.Quest.cs b/Source/Game/Entities/Player/Player.Quest.cs index db3e88702..af7810c1a 100644 --- a/Source/Game/Entities/Player/Player.Quest.cs +++ b/Source/Game/Entities/Player/Player.Quest.cs @@ -15,6 +15,7 @@ using Game.Spells; using System; using System.Collections.Generic; using System.Linq; +using System; namespace Game.Entities { @@ -534,11 +535,6 @@ namespace Game.Entities if (!CanTakeQuest(quest, false)) return false; - if (quest.HasQuestObjectiveType(QuestObjectiveType.Item)) - foreach (QuestObjective obj in quest.Objectives) - if (obj.Type == QuestObjectiveType.Item && !HasItemCount((uint)obj.ObjectID, (uint)obj.Amount)) - return false; - if (!CanRewardQuest(quest, false)) return false; @@ -572,7 +568,7 @@ namespace Game.Entities { foreach (QuestObjective obj in quest.Objectives) { - if (obj.Type != QuestObjectiveType.Item) + if (obj.Type != QuestObjectiveType.Item || obj.Flags2.HasAnyFlag(QuestObjectiveFlags2.QuestBoundItem)) continue; if (GetItemCount((uint)obj.ObjectID) < obj.Amount) @@ -2162,7 +2158,7 @@ namespace Game.Entities { foreach (QuestObjective obj in quest.Objectives) { - if (obj.Type == QuestObjectiveType.Item) + if (obj.Type == QuestObjectiveType.Item && !obj.Flags2.HasFlag(QuestObjectiveFlags2.QuestBoundItem)) { uint reqItemCount = (uint)obj.Amount; uint curItemCount = GetItemCount((uint)obj.ObjectID, true); @@ -2357,16 +2353,41 @@ namespace Game.Entities AreaExploredOrEventHappens(questId); } - public void ItemAddedQuestCheck(uint entry, uint count) + Func QuestBoundItemFunc = objective => { return objective.Flags2.HasFlag(QuestObjectiveFlags2.QuestBoundItem); }; + Func NotQuestBoundItemFunc = objective => { return !objective.Flags2.HasFlag(QuestObjectiveFlags2.QuestBoundItem); }; + + public void ItemAddedQuestCheck(uint entry, uint count, bool? boundItemFlagRequirement = null) { - UpdateQuestObjectiveProgress(QuestObjectiveType.Item, (int)entry, count); + ItemAddedQuestCheck(entry, count, boundItemFlagRequirement, out _); + } + + public void ItemAddedQuestCheck(uint entry, uint count, bool? boundItemFlagRequirement, out bool hadBoundItemObjective) + { + hadBoundItemObjective = false; + + List updatedObjectives = new(); + Func objectiveFilter = null; + if (boundItemFlagRequirement.HasValue) + objectiveFilter = boundItemFlagRequirement.Value ? QuestBoundItemFunc : NotQuestBoundItemFunc; + + ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(entry); + UpdateQuestObjectiveProgress(QuestObjectiveType.Item, (int)itemTemplate.GetId(), count, ObjectGuid.Empty, updatedObjectives, objectiveFilter); + if (itemTemplate.QuestLogItemId != 0 && (updatedObjectives.Count != 1 || !updatedObjectives[0].Flags2.HasFlag(QuestObjectiveFlags2.QuestBoundItem))) + UpdateQuestObjectiveProgress(QuestObjectiveType.Item, itemTemplate.QuestLogItemId, count, ObjectGuid.Empty, updatedObjectives, objectiveFilter); + + if (updatedObjectives.Count == 1 && updatedObjectives[0].Flags2.HasFlag(QuestObjectiveFlags2.QuestBoundItem)) + { + hadBoundItemObjective = updatedObjectives.Count == 1 && updatedObjectives[0].Flags2.HasFlag(QuestObjectiveFlags2.QuestBoundItem); + + SendQuestUpdateAddItem(itemTemplate, updatedObjectives[0], (ushort)count); + } } public void ItemRemovedQuestCheck(uint entry, uint count) { foreach (var objectiveStatusData in m_questObjectiveStatus.LookupByKey((QuestObjectiveType.Item, (int)entry))) { - uint questId = objectiveStatusData.QuestStatusPair.QuestID; + uint questId = objectiveStatusData.QuestStatusPair.QuestID; ushort logSlot = objectiveStatusData.QuestStatusPair.Status.Slot; Quest quest = Global.ObjectMgr.GetQuestTemplate(questId); QuestObjective objective = Global.ObjectMgr.GetQuestObjective(objectiveStatusData.ObjectiveId); @@ -2455,7 +2476,7 @@ namespace Game.Entities UpdateQuestObjectiveProgress(QuestObjectiveType.ObtainCurrency, (int)currencyId, change); } - public void UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int objectId, long addCount, ObjectGuid victimGuid = default) + public void UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int objectId, long addCount, ObjectGuid victimGuid = default, List updatedObjectives = null, Func objectiveFilter = null) { bool anyObjectiveChangedCompletionState = false; bool updatePhaseShift = false; @@ -2482,115 +2503,125 @@ namespace Game.Entities continue; bool objectiveWasComplete = IsQuestObjectiveComplete(logSlot, quest, objective); - if (!objectiveWasComplete || addCount < 0) + if (objectiveWasComplete && addCount >= 0) + continue; + + if (objectiveFilter != null && !objectiveFilter(objective)) + continue; + + bool objectiveIsNowComplete = false; + if (objective.IsStoringValue()) { - bool objectiveIsNowComplete = false; - if (objective.IsStoringValue()) + if (objectiveType == QuestObjectiveType.PlayerKills && objective.Flags.HasAnyFlag(QuestObjectiveFlags.KillPlayersSameFaction)) { - if (objectiveType == QuestObjectiveType.PlayerKills && objective.Flags.HasAnyFlag(QuestObjectiveFlags.KillPlayersSameFaction)) - { - Player victim = Global.ObjAccessor.GetPlayer(GetMap(), victimGuid); - if (victim?.GetEffectiveTeam() != GetEffectiveTeam()) - continue; - } - - int currentProgress = GetQuestSlotObjectiveData(logSlot, objective); - if (addCount > 0 ? (currentProgress < objective.Amount) : (currentProgress > 0)) - { - int newProgress = (int)Math.Clamp(currentProgress + addCount, 0, objective.Amount); - SetQuestObjectiveData(objective, newProgress); - if (addCount > 0 && !objective.Flags.HasAnyFlag(QuestObjectiveFlags.HideCreditMsg)) - { - switch (objectiveType) - { - case QuestObjectiveType.Item: - break; - case QuestObjectiveType.PlayerKills: - SendQuestUpdateAddPlayer(quest, (uint)newProgress); - break; - default: - SendQuestUpdateAddCredit(quest, victimGuid, objective, (uint)newProgress); - break; - } - } - - objectiveIsNowComplete = IsQuestObjectiveComplete(logSlot, quest, objective); - } + Player victim = Global.ObjAccessor.GetPlayer(GetMap(), victimGuid); + if (victim?.GetEffectiveTeam() != GetEffectiveTeam()) + continue; } - else if (objective.IsStoringFlag()) - { - SetQuestObjectiveData(objective, addCount > 0 ? 1 : 0); - if (addCount > 0 && !objective.Flags.HasAnyFlag(QuestObjectiveFlags.HideCreditMsg)) - SendQuestUpdateAddCreditSimple(objective); + int currentProgress = GetQuestSlotObjectiveData(logSlot, objective); + if (addCount > 0 ? (currentProgress < objective.Amount) : (currentProgress > 0)) + { + int newProgress = (int)Math.Clamp(currentProgress + addCount, 0, objective.Amount); + SetQuestObjectiveData(objective, newProgress); + if (addCount > 0 && !objective.Flags.HasFlag(QuestObjectiveFlags.HideCreditMsg)) + { + switch (objectiveType) + { + case QuestObjectiveType.Item: + break; // case handled by SMSG_ITEM_PUSH_RESULT + case QuestObjectiveType.PlayerKills: + SendQuestUpdateAddPlayer(quest, (uint)newProgress); + break; + default: + SendQuestUpdateAddCredit(quest, victimGuid, objective, (uint)newProgress); + break; + } + } objectiveIsNowComplete = IsQuestObjectiveComplete(logSlot, quest, objective); } - else - { - switch (objectiveType) - { - case QuestObjectiveType.Currency: - objectiveIsNowComplete = GetCurrencyQuantity((uint)objectId) + addCount >= objective.Amount; - break; - case QuestObjectiveType.LearnSpell: - objectiveIsNowComplete = addCount != 0; - break; - case QuestObjectiveType.MinReputation: - objectiveIsNowComplete = GetReputationMgr().GetReputation((uint)objectId) + addCount >= objective.Amount; - break; - case QuestObjectiveType.MaxReputation: - objectiveIsNowComplete = GetReputationMgr().GetReputation((uint)objectId) + addCount <= objective.Amount; - break; - case QuestObjectiveType.Money: - objectiveIsNowComplete = (long)GetMoney() + addCount >= objective.Amount; - break; - case QuestObjectiveType.ProgressBar: - objectiveIsNowComplete = IsQuestObjectiveProgressBarComplete(logSlot, quest); - break; - default: - Cypher.Assert(false, "Unhandled quest objective type {objectiveType}"); - break; - } - } - - if (objective.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar)) - { - if (IsQuestObjectiveProgressBarComplete(logSlot, quest)) - { - var progressBarObjective = quest.Objectives.Find(otherObjective => otherObjective.Type == QuestObjectiveType.ProgressBar && !otherObjective.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar)); - if (progressBarObjective != null) - SendQuestUpdateAddCreditSimple(progressBarObjective); - - objectiveIsNowComplete = true; - } - } - - if (objectiveWasComplete != objectiveIsNowComplete) - anyObjectiveChangedCompletionState = true; - - if (objectiveIsNowComplete && objective.CompletionEffect != null) - { - if (objective.CompletionEffect.GameEventId.HasValue) - GameEvents.Trigger(objective.CompletionEffect.GameEventId.Value, this, null); - if (objective.CompletionEffect.SpellId.HasValue) - CastSpell(this, objective.CompletionEffect.SpellId.Value, true); - if (objective.CompletionEffect.ConversationId.HasValue) - Conversation.CreateConversation(objective.CompletionEffect.ConversationId.Value, 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.HasAnyFlag(QuestObjectiveFlags.Optional) && objectiveStatusData.QuestStatusPair.Status.Status == QuestStatus.Complete) - IncompleteQuest(questId); } + else if (objective.IsStoringFlag()) + { + SetQuestObjectiveData(objective, addCount > 0 ? 1 : 0); + + if (addCount > 0 && !objective.Flags.HasFlag(QuestObjectiveFlags.HideCreditMsg)) + SendQuestUpdateAddCreditSimple(objective); + + objectiveIsNowComplete = IsQuestObjectiveComplete(logSlot, quest, objective); + } + else + { + switch (objectiveType) + { + case QuestObjectiveType.Currency: + objectiveIsNowComplete = GetCurrencyQuantity((uint)objectId) + addCount >= objective.Amount; + break; + case QuestObjectiveType.LearnSpell: + objectiveIsNowComplete = addCount != 0; + break; + case QuestObjectiveType.MinReputation: + objectiveIsNowComplete = GetReputationMgr().GetReputation((uint)objectId) + addCount >= objective.Amount; + break; + case QuestObjectiveType.MaxReputation: + objectiveIsNowComplete = GetReputationMgr().GetReputation((uint)objectId) + addCount <= objective.Amount; + break; + case QuestObjectiveType.Money: + objectiveIsNowComplete = (long)GetMoney() + addCount >= objective.Amount; + break; + case QuestObjectiveType.ProgressBar: + objectiveIsNowComplete = IsQuestObjectiveProgressBarComplete(logSlot, quest); + break; + default: + Cypher.Assert(false, "Unhandled quest objective type {objectiveType}"); + break; + } + } + + if (objective.Flags.HasAnyFlag(QuestObjectiveFlags.PartOfProgressBar)) + { + if (IsQuestObjectiveProgressBarComplete(logSlot, quest)) + { + var progressBarObjective = quest.Objectives.Find(otherObjective => otherObjective.Type == QuestObjectiveType.ProgressBar && !otherObjective.Flags.HasFlag(QuestObjectiveFlags.PartOfProgressBar)); + if (progressBarObjective != null) + SendQuestUpdateAddCreditSimple(progressBarObjective); + + objectiveIsNowComplete = true; + } + } + + if (objectiveWasComplete != objectiveIsNowComplete) + anyObjectiveChangedCompletionState = true; + + if (objectiveIsNowComplete && objective.CompletionEffect != null) + { + if (objective.CompletionEffect.GameEventId.HasValue) + GameEvents.Trigger(objective.CompletionEffect.GameEventId.Value, this, null); + if (objective.CompletionEffect.SpellId.HasValue) + CastSpell(this, objective.CompletionEffect.SpellId.Value, true); + if (objective.CompletionEffect.ConversationId.HasValue) + Conversation.CreateConversation(objective.CompletionEffect.ConversationId.Value, 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.HasAnyFlag(QuestObjectiveFlags.Optional) && objectiveStatusData.QuestStatusPair.Status.Status == QuestStatus.Complete) + IncompleteQuest(questId); + + if (updatedObjectives != null) + updatedObjectives.Add(objective); + + if (objective.Type == QuestObjectiveType.Item && addCount >= 0 && objective.Flags2.HasFlag(QuestObjectiveFlags2.QuestBoundItem)) + break; + } if (anyObjectiveChangedCompletionState) @@ -2609,21 +2640,8 @@ namespace Game.Entities public bool HasQuestForItem(uint itemid) { // Search incomplete objective first - foreach (var objectiveItr in m_questObjectiveStatus.LookupByKey((QuestObjectiveType.Item, (int)itemid))) - { - Quest qInfo = Global.ObjectMgr.GetQuestTemplate(objectiveItr.QuestStatusPair.QuestID); - QuestObjective objective = Global.ObjectMgr.GetQuestObjective(objectiveItr.ObjectiveId); - if (qInfo == null || objective == null || !IsQuestObjectiveCompletable(objectiveItr.QuestStatusPair.Status.Slot, qInfo, objective)) - continue; - - // hide quest if player is in raid-group and quest is no raid quest - if (GetGroup() != null && 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.QuestStatusPair.Status.Slot, qInfo, objective)) - return true; - } + if (GetQuestObjectiveForItem(itemid, true) != null) + return true; // This part - for ItemDrop foreach (var questStatus in m_QuestStatus) @@ -2660,6 +2678,43 @@ namespace Game.Entities return false; } + QuestObjective GetQuestObjectiveForItem(uint itemId, bool onlyIncomplete) + { + QuestObjective findObjectiveForItem(int tempItemId) + { + foreach (var objectiveItr in m_questObjectiveStatus.LookupByKey((QuestObjectiveType.Item, tempItemId))) + { + Quest qInfo = Global.ObjectMgr.GetQuestTemplate(objectiveItr.QuestStatusPair.QuestID); + QuestObjective objective = Global.ObjectMgr.GetQuestObjective(objectiveItr.ObjectiveId); + if (qInfo == null || objective == null || !IsQuestObjectiveCompletable(objectiveItr.QuestStatusPair.Status.Slot, qInfo, objective)) + continue; + + // hide quest if player is in raid-group and quest is no raid quest + if (GetGroup() != null && 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.QuestStatusPair.Status.Slot, qInfo, objective)) + return objective; + } + return null; + }; + + QuestObjective objective = findObjectiveForItem((int)itemId); + if (objective != null) + return objective; + + ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(itemId); + if (itemTemplate != null && itemTemplate.QuestLogItemId != 0) + { + objective = findObjectiveForItem(itemTemplate.QuestLogItemId); + if (objective != null) + return objective; + } + + return null; + } + public int GetQuestObjectiveData(QuestObjective objective) { ushort slot = FindQuestSlot(objective.QuestID); @@ -3014,6 +3069,27 @@ namespace Game.Entities SendPacket(packet); } + void SendQuestUpdateAddItem(ItemTemplate itemTemplate, QuestObjective obj, ushort count) + { + ItemPushResult packet = new(); + + packet.PlayerGUID = GetGUID(); + + packet.Slot = InventorySlots.Bag0; + packet.SlotInBag = 0; + packet.Item.ItemID = itemTemplate.GetId(); + packet.QuestLogItemID = itemTemplate.QuestLogItemId; + packet.Quantity = count; + packet.QuantityInInventory = (uint)GetQuestObjectiveData(obj); + packet.DisplayText = ItemPushResult.DisplayType.EncounterLoot; + packet.Unused_1017 = true; + + if (GetGroup() != null && !itemTemplate.HasFlag(ItemFlags3.DontReportLootLogToParty)) + GetGroup().BroadcastPacket(packet, true); + else + SendPacket(packet); + } + public void SendQuestUpdateAddPlayer(Quest quest, uint newCount) { QuestUpdateAddPvPCredit packet = new(); diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index a2ad234cb..e336511b8 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -5000,7 +5000,7 @@ namespace Game var time = Time.GetMSTime(); uint count = 0; - SQLResult result = DB.World.Query("SELECT Id, FlagsCu, FoodType, MinMoneyLoot, MaxMoneyLoot, SpellPPMChance, RandomBonusListTemplateId FROM item_template_addon"); + SQLResult result = DB.World.Query("SELECT Id, FlagsCu, FoodType, MinMoneyLoot, MaxMoneyLoot, SpellPPMChance, RandomBonusListTemplateId, QuestLogItemId FROM item_template_addon"); if (!result.IsEmpty()) { do @@ -5028,6 +5028,7 @@ namespace Game itemTemplate.MaxMoneyLoot = maxMoneyLoot; itemTemplate.SpellPPMRate = result.Read(5); itemTemplate.RandomBonusListTemplateId = result.Read(6); + itemTemplate.QuestLogItemId = result.Read(7); ++count; } while (result.NextRow()); } diff --git a/Source/Game/Handlers/LootHandler.cs b/Source/Game/Handlers/LootHandler.cs index d94c7d74a..9590b13f9 100644 --- a/Source/Game/Handlers/LootHandler.cs +++ b/Source/Game/Handlers/LootHandler.cs @@ -447,7 +447,10 @@ namespace Game // 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 (newitem != null) + aeResult.Add(newitem, item.count, loot.loot_type, loot.GetDungeonEncounterId()); + else + target.ApplyItemLootedSpell(Global.ObjectMgr.GetItemTemplate(item.itemid)); // mark as looted item.count = 0; diff --git a/Source/Game/Handlers/VoidStorageHandler.cs b/Source/Game/Handlers/VoidStorageHandler.cs index b643896b9..a81f15086 100644 --- a/Source/Game/Handlers/VoidStorageHandler.cs +++ b/Source/Game/Handlers/VoidStorageHandler.cs @@ -181,9 +181,12 @@ namespace Game } Item item = player.StoreNewItem(dest, itemVS.ItemEntry, true, itemVS.RandomBonusListId, null, itemVS.Context, itemVS.BonusListIDs); - item.SetCreator(itemVS.CreatorGuid); - item.SetBinding(true); - GetCollectionMgr().AddItemAppearance(item); + if (item != null) + { + item.SetCreator(itemVS.CreatorGuid); + item.SetBinding(true); + GetCollectionMgr().AddItemAppearance(item); + } voidStorageTransferChanges.RemovedItems.Add(ObjectGuid.Create(HighGuid.Item, itemVS.ItemId)); diff --git a/Source/Game/Loot/Loot.cs b/Source/Game/Loot/Loot.cs index a0ca6437f..b1c58b249 100644 --- a/Source/Game/Loot/Loot.cs +++ b/Source/Game/Loot/Loot.cs @@ -105,7 +105,7 @@ namespace Game.Loots { return allowedGUIDs.Contains(looter); } - + public LootSlotType? GetUiTypeForPlayer(Player player, Loot loot) { if (is_looted) @@ -721,8 +721,13 @@ namespace Game.Loots --unlootedCount; Item pItem = player.StoreNewItem(dest, lootItem.itemid, true, lootItem.randomBonusListId, null, lootItem.context, lootItem.BonusListIDs); - player.SendNewItem(pItem, lootItem.count, false, createdByPlayer, broadcast); - player.ApplyItemLootedSpell(pItem, true); + if (pItem != null) + { + player.SendNewItem(pItem, lootItem.count, false, createdByPlayer, broadcast, GetDungeonEncounterId()); + player.ApplyItemLootedSpell(pItem, true); + } + else + player.ApplyItemLootedSpell(Global.ObjectMgr.GetItemTemplate(lootItem.itemid)); } return allLooted; @@ -933,7 +938,7 @@ namespace Game.Loots { return _allowedLooters.Contains(looter); } - + public void GenerateMoneyLoot(uint minAmount, uint maxAmount) { if (maxAmount > 0) @@ -1075,7 +1080,7 @@ namespace Game.Loots public bool IsLooted() { return gold == 0 && unlootedCount == 0; } public bool IsChanged() { return _changed; } - + public void AddLooter(ObjectGuid guid) { PlayersLooting.Add(guid); } public void RemoveLooter(ObjectGuid guid) { PlayersLooting.Remove(guid); } diff --git a/Source/Game/Networking/Packets/QuestPackets.cs b/Source/Game/Networking/Packets/QuestPackets.cs index caa6249a9..7bd34eb94 100644 --- a/Source/Game/Networking/Packets/QuestPackets.cs +++ b/Source/Game/Networking/Packets/QuestPackets.cs @@ -227,7 +227,7 @@ namespace Game.Networking.Packets _worldPacket.WriteInt32(questObjective.ObjectID); _worldPacket.WriteInt32(questObjective.Amount); _worldPacket.WriteUInt32((uint)questObjective.Flags); - _worldPacket.WriteUInt32(questObjective.Flags2); + _worldPacket.WriteUInt32((uint)questObjective.Flags2); _worldPacket.WriteFloat(questObjective.ProgressBarWeight); _worldPacket.WriteInt32(questObjective.VisualEffects.Length); diff --git a/Source/Game/Quest/Quest.cs b/Source/Game/Quest/Quest.cs index 31aae3ded..661794c66 100644 --- a/Source/Game/Quest/Quest.cs +++ b/Source/Game/Quest/Quest.cs @@ -244,7 +244,7 @@ namespace Game obj.ObjectID = fields.Read(4); obj.Amount = fields.Read(5); obj.Flags = (QuestObjectiveFlags)fields.Read(6); - obj.Flags2 = fields.Read(7); + obj.Flags2 = (QuestObjectiveFlags2)fields.Read(7); obj.ProgressBarWeight = fields.Read(8); obj.Description = fields.Read(9); @@ -1051,7 +1051,7 @@ namespace Game public int ObjectID; public int Amount; public QuestObjectiveFlags Flags; - public uint Flags2; + public QuestObjectiveFlags2 Flags2; public float ProgressBarWeight; public string Description; public int[] VisualEffects = Array.Empty(); diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index 021adf7ae..41c7abddb 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -4589,12 +4589,8 @@ namespace Game.Spells } Item newitem = plCaster.StoreNewItem(dest, GetSpellEffectInfo().ItemType, true); - if (newitem == null) - { - plCaster.SendEquipError(InventoryResult.ItemNotFound); - return; - } - plCaster.SendNewItem(newitem, count, true, true); + if (newitem != null) + plCaster.SendNewItem(newitem, count, true, true); } [AuraEffectHandler(AuraType.BindSight)] diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index 71b740466..387d75cee 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -997,26 +997,21 @@ namespace Game.Spells { // create the new item and store it Item pItem = player.StoreNewItem(dest, newitemid, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(newitemid), null, context, bonusListIds); - - // was it successful? return error if not - if (pItem == null) + if (pItem != null) { - player.SendEquipError(InventoryResult.ItemNotFound); - 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() > ItemQuality.Epic || (pItem.GetQuality() == ItemQuality.Epic && pItem.GetItemLevel(player) >= GuildConst.MinNewsItemLevel)) - { - Guild guild = player.GetGuild(); - if (guild != null) - guild.AddGuildNews(GuildNews.ItemCrafted, player.GetGUID(), 0, pProto.GetId()); + if (pItem.GetQuality() > ItemQuality.Epic || (pItem.GetQuality() == ItemQuality.Epic && pItem.GetItemLevel(player) >= GuildConst.MinNewsItemLevel)) + { + Guild guild = player.GetGuild(); + if (guild != null) + guild.AddGuildNews(GuildNews.ItemCrafted, player.GetGUID(), 0, pProto.GetId()); + } } // we succeeded in creating at least one item, so a levelup is possible