Core/Items: Item bonus generation improvements

* Pass ItemContext to item creation wherever possible
* Support scaling item levels with m+ keystone levels (not used currently)
* Fixed item link validation when client sends it as default uninitialized bonus list with context only
* Support scaling items depending on current active season (seasons not implemented)
* Implemented content tuning redirection
Port From (https://github.com/TrinityCore/TrinityCore/commit/0fb8765a6638fd947b59fce44d5c31251d0cdadd)
This commit is contained in:
hondacrx
2023-06-21 09:19:40 -04:00
parent 378b0ffccd
commit 49a07ebcce
30 changed files with 796 additions and 336 deletions
+3 -3
View File
@@ -2715,7 +2715,7 @@ namespace Game.Entities
{
CreatureTemplate cInfo = GetCreatureTemplate();
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
float baseHealth = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass);
float baseHealth = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass, 0);
return (ulong)Math.Max(baseHealth * creatureDifficulty.HealthModifier, 1.0f);
}
@@ -2736,7 +2736,7 @@ namespace Game.Entities
{
CreatureTemplate cInfo = GetCreatureTemplate();
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
return Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureAutoAttackDps, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass);
return Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureAutoAttackDps, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass, 0);
}
public override float GetDamageMultiplierForTarget(WorldObject target)
@@ -2753,7 +2753,7 @@ namespace Game.Entities
{
CreatureTemplate cInfo = GetCreatureTemplate();
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
float baseArmor = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureArmor, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass);
float baseArmor = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureArmor, level, creatureDifficulty.GetHealthScalingExpansion(), creatureDifficulty.ContentTuningID, (Class)cInfo.UnitClass, 0);
return baseArmor * creatureDifficulty.ArmorModifier;
}
+12 -10
View File
@@ -969,10 +969,11 @@ namespace Game.Entities
Loot fishLoot = new(GetMap(), GetGUID(), LootType.Fishing, null);
uint areaId = GetAreaId();
ItemContext itemContext = ItemBonusMgr.GetContextForPlayer(GetMap().GetMapDifficulty(), lootOwner);
AreaTableRecord areaEntry;
while ((areaEntry = CliDB.AreaTableStorage.LookupByKey(areaId)) != null)
{
fishLoot.FillLoot(areaId, LootStorage.Fishing, lootOwner, true, true);
fishLoot.FillLoot(areaId, LootStorage.Fishing, lootOwner, true, true, LootModes.Default, itemContext);
if (!fishLoot.IsLooted())
break;
@@ -980,7 +981,7 @@ namespace Game.Entities
}
if (fishLoot.IsLooted())
fishLoot.FillLoot(defaultzone, LootStorage.Fishing, lootOwner, true, true);
fishLoot.FillLoot(defaultzone, LootStorage.Fishing, lootOwner, true, true, LootModes.Default, itemContext);
return fishLoot;
}
@@ -992,10 +993,11 @@ namespace Game.Entities
Loot fishLoot = new(GetMap(), GetGUID(), LootType.FishingJunk, null);
uint areaId = GetAreaId();
ItemContext itemContext = ItemBonusMgr.GetContextForPlayer(GetMap().GetMapDifficulty(), lootOwner);
AreaTableRecord areaEntry;
while ((areaEntry = CliDB.AreaTableStorage.LookupByKey(areaId)) != null)
{
fishLoot.FillLoot(areaId, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish);
fishLoot.FillLoot(areaId, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish, itemContext);
if (!fishLoot.IsLooted())
break;
@@ -1003,7 +1005,7 @@ namespace Game.Entities
}
if (fishLoot.IsLooted())
fishLoot.FillLoot(defaultzone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish);
fishLoot.FillLoot(defaultzone, LootStorage.Fishing, lootOwner, true, true, LootModes.JunkFish, itemContext);
return fishLoot;
}
@@ -1692,7 +1694,7 @@ namespace Game.Entities
loot = new Loot(GetMap(), GetGUID(), LootType.Chest, groupRules ? group : null);
loot.SetDungeonEncounterId(info.Chest.DungeonEncounter);
loot.FillLoot(info.GetLootId(), LootStorage.Gameobject, player, !groupRules, false, GetLootMode(), GetMap().GetDifficultyLootItemContext());
loot.FillLoot(info.GetLootId(), LootStorage.Gameobject, player, !groupRules, false, GetLootMode(), ItemBonusMgr.GetContextForPlayer(GetMap().GetMapDifficulty(), player));
if (GetLootMode() > 0)
{
@@ -1731,7 +1733,7 @@ namespace Game.Entities
m_personalLoot = LootManager.GenerateDungeonEncounterPersonalLoot(info.Chest.DungeonEncounter, info.Chest.chestPersonalLoot,
LootStorage.Gameobject, LootType.Chest, this, addon != null ? addon.Mingold : 0, addon != null ? addon.Maxgold : 0,
(ushort)GetLootMode(), GetMap().GetDifficultyLootItemContext(), tappers);
(ushort)GetLootMode(), GetMap().GetMapDifficulty(), tappers);
}
else
{
@@ -1739,7 +1741,7 @@ namespace Game.Entities
m_personalLoot[player.GetGUID()] = loot;
loot.SetDungeonEncounterId(info.Chest.DungeonEncounter);
loot.FillLoot(info.Chest.chestPersonalLoot, LootStorage.Gameobject, player, true, false, GetLootMode(), GetMap().GetDifficultyLootItemContext());
loot.FillLoot(info.Chest.chestPersonalLoot, LootStorage.Gameobject, player, true, false, GetLootMode(), ItemBonusMgr.GetContextForPlayer(GetMap().GetMapDifficulty(), player));
if (GetLootMode() > 0 && addon != null)
loot.GenerateMoneyLoot(addon.Mingold, addon.Maxgold);
@@ -1752,7 +1754,7 @@ namespace Game.Entities
if (info.Chest.chestPushLoot != 0)
{
Loot pushLoot = new(GetMap(), GetGUID(), LootType.Chest, null);
pushLoot.FillLoot(info.Chest.chestPushLoot, LootStorage.Gameobject, player, true, false, GetLootMode(), GetMap().GetDifficultyLootItemContext());
pushLoot.FillLoot(info.Chest.chestPushLoot, LootStorage.Gameobject, player, true, false, GetLootMode(), ItemBonusMgr.GetContextForPlayer(GetMap().GetMapDifficulty(), player));
pushLoot.AutoStore(player, ItemConst.NullBag, ItemConst.NullSlot);
}
@@ -2268,7 +2270,7 @@ namespace Game.Entities
Player player = user.ToPlayer();
Loot loot = new Loot(GetMap(), GetGUID(), LootType.Fishinghole, null);
loot.FillLoot(GetGoInfo().GetLootId(), LootStorage.Gameobject, player, true);
loot.FillLoot(GetGoInfo().GetLootId(), LootStorage.Gameobject, player, true, false, LootModes.Default, ItemBonusMgr.GetContextForPlayer(GetMap().GetMapDifficulty(), player));
m_personalLoot[player.GetGUID()] = loot;
player.SendLoot(loot);
@@ -2452,7 +2454,7 @@ namespace Game.Entities
Loot newLoot = new(GetMap(), GetGUID(), LootType.Chest, null);
m_personalLoot[player.GetGUID()] = newLoot;
newLoot.FillLoot(info.GatheringNode.chestLoot, LootStorage.Gameobject, player, true, false, GetLootMode(), GetMap().GetDifficultyLootItemContext());
newLoot.FillLoot(info.GatheringNode.chestLoot, LootStorage.Gameobject, player, true, false, GetLootMode(), ItemBonusMgr.GetContextForPlayer(GetMap().GetMapDifficulty(), player));
}
if (info.GatheringNode.triggeredEvent != 0)
+30 -40
View File
@@ -1086,26 +1086,19 @@ namespace Game.Entities
{
case ItemEnchantmentType.BonusListID:
{
var bonusesEffect = Global.DB2Mgr.GetItemBonusList(gemEnchant.EffectArg[i]);
if (bonusesEffect != null)
{
foreach (ItemBonusRecord itemBonus in bonusesEffect)
if (itemBonus.BonusType == ItemBonusType.ItemLevel)
_bonusData.GemItemLevelBonus[slot] += (uint)itemBonus.Value[0];
}
foreach (var itemBonus in ItemBonusMgr.GetItemBonuses(gemEnchant.EffectArg[i]))
if (itemBonus.BonusType == ItemBonusType.ItemLevel)
_bonusData.GemItemLevelBonus[slot] += (uint)itemBonus.Value[0];
break;
}
case ItemEnchantmentType.BonusListCurve:
{
uint artifactrBonusListId = Global.DB2Mgr.GetItemBonusListForItemLevelDelta((short)Global.DB2Mgr.GetCurveValueAt((uint)Curves.ArtifactRelicItemLevelBonus, gemBaseItemLevel + gemBonus.ItemLevelBonus));
if (artifactrBonusListId != 0)
uint bonusListId = ItemBonusMgr.GetItemBonusListForItemLevelDelta((short)Global.DB2Mgr.GetCurveValueAt((uint)Curves.ArtifactRelicItemLevelBonus, gemBaseItemLevel + gemBonus.ItemLevelBonus));
if (bonusListId != 0)
{
var bonusesEffect = Global.DB2Mgr.GetItemBonusList(artifactrBonusListId);
if (bonusesEffect != null)
foreach (ItemBonusRecord itemBonus in bonusesEffect)
if (itemBonus.BonusType == ItemBonusType.ItemLevel)
_bonusData.GemItemLevelBonus[slot] += (uint)itemBonus.Value[0];
foreach (var itemBonus in ItemBonusMgr.GetItemBonuses(bonusListId))
if (itemBonus.BonusType == ItemBonusType.ItemLevel)
_bonusData.GemItemLevelBonus[slot] += (uint)itemBonus.Value[0];
}
break;
}
@@ -1195,22 +1188,25 @@ namespace Game.Entities
owner.SendPacket(itemTimeUpdate);
}
public static Item CreateItem(uint item, uint count, ItemContext context, Player player = null)
public static Item CreateItem(uint itemEntry, uint count, ItemContext context, Player player = null, bool addDefaultBonuses = true)
{
if (count < 1)
return null; //don't create item at zero count
var pProto = Global.ObjectMgr.GetItemTemplate(item);
var pProto = Global.ObjectMgr.GetItemTemplate(itemEntry);
if (pProto != null)
{
if (count > pProto.GetMaxStackSize())
count = pProto.GetMaxStackSize();
Item pItem = Bag.NewItemOrBag(pProto);
if (pItem.Create(Global.ObjectMgr.GetGenerator(HighGuid.Item).Generate(), item, context, player))
Item item = Bag.NewItemOrBag(pProto);
if (item.Create(Global.ObjectMgr.GetGenerator(HighGuid.Item).Generate(), itemEntry, context, player))
{
pItem.SetCount(count);
return pItem;
item.SetCount(count);
if (addDefaultBonuses)
item.SetBonuses(ItemBonusMgr.GetBonusListsForItem(itemEntry, new(context)));
return item;
}
}
@@ -1219,7 +1215,7 @@ namespace Game.Entities
public Item CloneItem(uint count, Player player = null)
{
Item newItem = CreateItem(GetEntry(), count, GetContext(), player);
Item newItem = CreateItem(GetEntry(), count, GetContext(), player, false);
if (newItem == null)
return null;
@@ -1227,6 +1223,7 @@ namespace Game.Entities
newItem.SetGiftCreator(GetGiftCreator());
newItem.ReplaceAllItemFlags((ItemFieldFlags)(m_itemData.DynamicFlags & ~(uint)(ItemFieldFlags.Refundable | ItemFieldFlags.BopTradeable)));
newItem.SetExpiration(m_itemData.Expiration);
newItem.SetBonuses(m_itemData.ItemBonusKey.GetValue().BonusListIDs);
// player CAN be NULL in which case we must not update random properties because that accesses player's item update queue
if (player != null)
newItem.SetItemRandomBonusList(m_randomBonusListId);
@@ -2072,26 +2069,21 @@ namespace Game.Entities
}
public List<uint> GetBonusListIDs() { return m_itemData.ItemBonusKey.GetValue().BonusListIDs; }
public void AddBonuses(uint bonusListID)
{
var bonusListIDs = GetBonusListIDs();
if (bonusListIDs.Contains(bonusListID))
return;
var bonuses = Global.DB2Mgr.GetItemBonusList(bonusListID);
if (bonuses != null)
{
ItemBonusKey itemBonusKey = new();
itemBonusKey.ItemID = GetEntry();
itemBonusKey.BonusListIDs = GetBonusListIDs();
itemBonusKey.BonusListIDs.Add(bonusListID);
SetUpdateFieldValue(m_values.ModifyValue(m_itemData).ModifyValue(m_itemData.ItemBonusKey), itemBonusKey);
foreach (ItemBonusRecord bonus in bonuses)
_bonusData.AddBonus(bonus.BonusType, bonus.Value);
SetUpdateFieldValue(m_values.ModifyValue(m_itemData).ModifyValue(m_itemData.ItemAppearanceModID), (byte)_bonusData.AppearanceModID);
}
ItemBonusKey itemBonusKey = new();
itemBonusKey.ItemID = GetEntry();
itemBonusKey.BonusListIDs = GetBonusListIDs();
itemBonusKey.BonusListIDs.Add(bonusListID);
SetUpdateFieldValue(m_values.ModifyValue(m_itemData).ModifyValue(m_itemData.ItemBonusKey), itemBonusKey);
foreach (var bonus in ItemBonusMgr.GetItemBonuses(bonusListID))
_bonusData.AddBonus(bonus.BonusType, bonus.Value);
SetUpdateFieldValue(m_values.ModifyValue(m_itemData).ModifyValue(m_itemData.ItemAppearanceModID), (byte)_bonusData.AppearanceModID);
}
public void SetBonuses(List<uint> bonusListIDs)
@@ -2906,10 +2898,8 @@ namespace Game.Entities
public void AddBonusList(uint bonusListId)
{
var bonuses = Global.DB2Mgr.GetItemBonusList(bonusListId);
if (bonuses != null)
foreach (ItemBonusRecord bonus in bonuses)
AddBonus(bonus.BonusType, bonus.Value);
foreach (var bonus in ItemBonusMgr.GetItemBonuses(bonusListId))
AddBonus(bonus.BonusType, bonus.Value);
}
public void AddBonus(ItemBonusType type, int[] values)
@@ -0,0 +1,506 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using Framework.Constants;
using Game.DataStorage;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Game.Entities
{
class ItemBonusMgr
{
static MultiMap<uint /*azeriteUnlockMappingSetId*/, AzeriteUnlockMappingRecord> _azeriteUnlockMappings = new();
static MultiMap<uint /*itemBonusTreeId*/, ChallengeModeItemBonusOverrideRecord> _challengeModeItemBonusOverrides = new();
static MultiMap<uint /*itemBonusListId*/, ItemBonusRecord> _itemBonusLists = new();
static MultiMap<int, ItemBonusListGroupEntryRecord> _itemBonusListGroupEntries = new();
static Dictionary<short /*itemLevelDelta*/, uint /*itemBonusListId*/> _itemLevelDeltaToBonusListContainer = new();
static SortedMultiMap<uint /*itemLevelSelectorQualitySetId*/, ItemLevelSelectorQualityRecord> _itemLevelQualitySelectorQualities = new();
static MultiMap<uint /*itemBonusTreeId*/, ItemBonusTreeNodeRecord> _itemBonusTrees = new();
static MultiMap<uint /*itemId*/, uint /*itemBonusTreeId*/> _itemToBonusTree = new();
public static void Load()
{
foreach (var azeriteUnlockMapping in CliDB.AzeriteUnlockMappingStorage.Values)
_azeriteUnlockMappings.Add(azeriteUnlockMapping.AzeriteUnlockMappingSetID, azeriteUnlockMapping);
foreach (var challengeModeItemBonusOverride in CliDB.ChallengeModeItemBonusOverrideStorage.Values)
_challengeModeItemBonusOverrides.Add(challengeModeItemBonusOverride.SrcItemBonusTreeID, challengeModeItemBonusOverride);
foreach (var bonus in CliDB.ItemBonusStorage.Values)
_itemBonusLists.Add(bonus.ParentItemBonusListID, bonus);
foreach (var bonusListGroupEntry in CliDB.ItemBonusListGroupEntryStorage.Values)
_itemBonusListGroupEntries.Add(bonusListGroupEntry.ItemBonusListGroupID, bonusListGroupEntry);
foreach (var itemBonusListLevelDelta in CliDB.ItemBonusListLevelDeltaStorage.Values)
_itemLevelDeltaToBonusListContainer[itemBonusListLevelDelta.ItemLevelDelta] = itemBonusListLevelDelta.Id;
foreach (var itemLevelSelectorQuality in CliDB.ItemLevelSelectorQualityStorage.Values)
_itemLevelQualitySelectorQualities.Add(itemLevelSelectorQuality.ParentILSQualitySetID, itemLevelSelectorQuality);
foreach (var bonusTreeNode in CliDB.ItemBonusTreeNodeStorage.Values)
_itemBonusTrees.Add(bonusTreeNode.ParentItemBonusTreeID, bonusTreeNode);
foreach (var itemBonusTreeAssignment in CliDB.ItemXBonusTreeStorage.Values)
_itemToBonusTree.Add(itemBonusTreeAssignment.ItemID, itemBonusTreeAssignment.ItemBonusTreeID);
}
public static ItemContext GetContextForPlayer(MapDifficultyRecord mapDifficulty, Player player)
{
ItemContext evalContext(ItemContext currentContext, ItemContext newContext)
{
if (newContext == ItemContext.None)
newContext = currentContext;
else if (newContext == ItemContext.ForceToNone)
newContext = ItemContext.None;
return newContext;
}
ItemContext context = ItemContext.None;
var difficulty = CliDB.DifficultyStorage.LookupByKey(mapDifficulty.DifficultyID);
if (difficulty != null)
context = evalContext(context, (ItemContext)difficulty.ItemContext);
context = evalContext(context, (ItemContext)mapDifficulty.ItemContext);
if (mapDifficulty.ItemContextPickerID != 0)
{
uint contentTuningId = Global.DB2Mgr.GetRedirectedContentTuningId((uint)mapDifficulty.ContentTuningID, player.m_playerData.CtrOptions.GetValue().ContentTuningConditionMask);
ItemContextPickerEntryRecord selectedPickerEntry = null;
foreach (var itemContextPickerEntry in CliDB.ItemContextPickerEntryStorage.Values)
{
if (itemContextPickerEntry.ItemContextPickerID != mapDifficulty.ItemContextPickerID)
continue;
if (itemContextPickerEntry.PVal <= 0)
continue;
bool meetsPlayerCondition = false;
if (player)
{
var playerCondition = CliDB.PlayerConditionStorage.LookupByKey(itemContextPickerEntry.PlayerConditionID);
if (playerCondition != null)
meetsPlayerCondition = ConditionManager.IsPlayerMeetingCondition(player, playerCondition);
}
if ((itemContextPickerEntry.Flags & 0x1) != 0)
meetsPlayerCondition = !meetsPlayerCondition;
if (!meetsPlayerCondition)
continue;
if (itemContextPickerEntry.LabelID != 0 && !Global.DB2Mgr.HasContentTuningLabel(contentTuningId, itemContextPickerEntry.LabelID))
continue;
if (selectedPickerEntry == null || selectedPickerEntry.OrderIndex < itemContextPickerEntry.OrderIndex)
selectedPickerEntry = itemContextPickerEntry;
}
if (selectedPickerEntry != null)
context = evalContext(context, (ItemContext)selectedPickerEntry.ItemCreationContext);
}
return context;
}
public static List<ItemBonusRecord> GetItemBonuses(uint bonusListId)
{
return _itemBonusLists.LookupByKey(bonusListId);
}
public static uint GetItemBonusListForItemLevelDelta(short delta)
{
return _itemLevelDeltaToBonusListContainer.LookupByKey(delta);
}
public static bool CanApplyBonusTreeToItem(ItemTemplate itemTemplate, uint itemBonusTreeId, ItemBonusGenerationParams generationParams)
{
var bonusTree = CliDB.ItemBonusTreeStorage.LookupByKey(itemBonusTreeId);
if (bonusTree != null)
{
if (bonusTree.InventoryTypeSlotMask != 0)
if ((1 << (int)itemTemplate.GetInventoryType() & bonusTree.InventoryTypeSlotMask) == 0)
return false;
if ((bonusTree.Flags & 0x8) != 0 && !itemTemplate.HasFlag(ItemFlags2.CasterWeapon))
return false;
if ((bonusTree.Flags & 0x10) != 0 && itemTemplate.HasFlag(ItemFlags2.CasterWeapon))
return false;
if ((bonusTree.Flags & 0x20) != 0 && !itemTemplate.HasFlag(ItemFlags4.CcTrinket))
return false;
if ((bonusTree.Flags & 0x40) != 0 && itemTemplate.HasFlag(ItemFlags4.CcTrinket))
return false;
if ((bonusTree.Flags & 0x4) != 0)
return true;
}
var bonusTreeNodes = _itemBonusTrees.LookupByKey(itemBonusTreeId);
if (!bonusTreeNodes.Empty())
{
bool anyNodeMatched = false;
foreach (var bonusTreeNode in bonusTreeNodes)
{
if (bonusTreeNode.MinMythicPlusLevel > 0)
continue;
ItemContext nodeContext = (ItemContext)bonusTreeNode.ItemContext;
if (nodeContext == ItemContext.None || nodeContext == generationParams.Context)
{
if (anyNodeMatched)
return false;
anyNodeMatched = true;
}
}
}
return true;
}
public static uint GetBonusTreeIdOverride(uint itemBonusTreeId, ItemBonusGenerationParams generationParams)
{
// TODO: configure seasons globally
var mythicPlusSeason = CliDB.MythicPlusSeasonStorage.LookupByKey(0);
if (mythicPlusSeason != null)
{
int selectedLevel = -1;
int selectedMilestoneSeason = -1;
ChallengeModeItemBonusOverrideRecord selectedItemBonusOverride = null;
foreach (var itemBonusOverride in _challengeModeItemBonusOverrides.LookupByKey(itemBonusTreeId))
{
if (itemBonusOverride.Type != 0)
continue;
if (itemBonusOverride.Value > generationParams.MythicPlusKeystoneLevel.GetValueOrDefault(-1))
continue;
if (itemBonusOverride.MythicPlusSeasonID != 0)
{
var overrideSeason = CliDB.MythicPlusSeasonStorage.LookupByKey(itemBonusOverride.MythicPlusSeasonID);
if (overrideSeason == null)
continue;
if (mythicPlusSeason.MilestoneSeason < overrideSeason.MilestoneSeason)
continue;
if (selectedMilestoneSeason > overrideSeason.MilestoneSeason)
continue;
if (selectedMilestoneSeason == overrideSeason.MilestoneSeason)
if (selectedLevel > itemBonusOverride.Value)
continue;
selectedMilestoneSeason = overrideSeason.MilestoneSeason;
}
else if (selectedLevel > itemBonusOverride.Value)
continue;
selectedLevel = itemBonusOverride.Value;
selectedItemBonusOverride = itemBonusOverride;
}
if (selectedItemBonusOverride != null && selectedItemBonusOverride.DstItemBonusTreeID != 0)
itemBonusTreeId = (uint)selectedItemBonusOverride.DstItemBonusTreeID;
}
// TODO: configure seasons globally
var pvpSeason = CliDB.PvpSeasonStorage.LookupByKey(0);
if (pvpSeason != null)
{
int selectedLevel = -1;
int selectedMilestoneSeason = -1;
ChallengeModeItemBonusOverrideRecord selectedItemBonusOverride = null;
foreach (var itemBonusOverride in _challengeModeItemBonusOverrides.LookupByKey(itemBonusTreeId))
{
if (itemBonusOverride.Type != 1)
continue;
if (itemBonusOverride.Value > generationParams.PvpTier.GetValueOrDefault(-1))
continue;
if (itemBonusOverride.PvPSeasonID != 0)
{
var overrideSeason = CliDB.PvpSeasonStorage.LookupByKey(itemBonusOverride.PvPSeasonID);
if (overrideSeason == null)
continue;
if (pvpSeason.MilestoneSeason < overrideSeason.MilestoneSeason)
continue;
if (selectedMilestoneSeason > overrideSeason.MilestoneSeason)
continue;
if (selectedMilestoneSeason == overrideSeason.MilestoneSeason)
if (selectedLevel > itemBonusOverride.Value)
continue;
selectedMilestoneSeason = overrideSeason.MilestoneSeason;
}
else if (selectedLevel > itemBonusOverride.Value)
continue;
selectedLevel = itemBonusOverride.Value;
selectedItemBonusOverride = itemBonusOverride;
}
if (selectedItemBonusOverride != null && selectedItemBonusOverride.DstItemBonusTreeID != 0)
itemBonusTreeId = (uint)selectedItemBonusOverride.DstItemBonusTreeID;
}
return itemBonusTreeId;
}
public static void ApplyBonusTreeHelper(ItemTemplate itemTemplate, uint itemBonusTreeId, ItemBonusGenerationParams generationParams, int sequenceLevel, ref uint itemLevelSelectorId, List<uint> bonusListIDs)
{
uint originalItemBonusTreeId = itemBonusTreeId;
// override bonus tree with season specific values
itemBonusTreeId = GetBonusTreeIdOverride(itemBonusTreeId, generationParams);
if (!CanApplyBonusTreeToItem(itemTemplate, itemBonusTreeId, generationParams))
return;
var treeList = _itemBonusTrees.LookupByKey(itemBonusTreeId);
if (treeList.Empty())
return;
foreach (var bonusTreeNode in treeList)
{
ItemContext nodeContext = (ItemContext)bonusTreeNode.ItemContext;
ItemContext requiredContext = nodeContext != ItemContext.ForceToNone ? nodeContext : ItemContext.None;
if (nodeContext != ItemContext.None && generationParams.Context != requiredContext)
continue;
if (generationParams.MythicPlusKeystoneLevel != 0)
{
if (bonusTreeNode.MinMythicPlusLevel != 0 && generationParams.MythicPlusKeystoneLevel < bonusTreeNode.MinMythicPlusLevel)
continue;
if (bonusTreeNode.MaxMythicPlusLevel != 0 && generationParams.MythicPlusKeystoneLevel > bonusTreeNode.MaxMythicPlusLevel)
continue;
}
if (bonusTreeNode.ChildItemBonusTreeID != 0)
ApplyBonusTreeHelper(itemTemplate, bonusTreeNode.ChildItemBonusTreeID, generationParams, sequenceLevel, ref itemLevelSelectorId, bonusListIDs);
else if (bonusTreeNode.ChildItemBonusListID != 0)
bonusListIDs.Add(bonusTreeNode.ChildItemBonusListID);
else if (bonusTreeNode.ChildItemLevelSelectorID != 0)
itemLevelSelectorId = bonusTreeNode.ChildItemLevelSelectorID;
else if (bonusTreeNode.ChildItemBonusListGroupID != 0)
{
int resolvedSequenceLevel = sequenceLevel;
switch (originalItemBonusTreeId)
{
case 4001:
resolvedSequenceLevel = 1;
break;
case 4079:
if (generationParams.MythicPlusKeystoneLevel != 0)
{
switch (bonusTreeNode.IblGroupPointsModSetID)
{
case 2909: // MythicPlus_End_of_Run levels 2-8
resolvedSequenceLevel = (int)Global.DB2Mgr.GetCurveValueAt(62951, generationParams.MythicPlusKeystoneLevel.Value);
break;
case 2910: // MythicPlus_End_of_Run levels 9-16
resolvedSequenceLevel = (int)Global.DB2Mgr.GetCurveValueAt(62952, generationParams.MythicPlusKeystoneLevel.Value);
break;
case 2911: // MythicPlus_End_of_Run levels 17-20
resolvedSequenceLevel = (int)Global.DB2Mgr.GetCurveValueAt(62954, generationParams.MythicPlusKeystoneLevel.Value);
break;
case 3007: // MythicPlus_Jackpot (weekly reward) levels 2-7
resolvedSequenceLevel = (int)Global.DB2Mgr.GetCurveValueAt(64388, generationParams.MythicPlusKeystoneLevel.Value);
break;
case 3008: // MythicPlus_Jackpot (weekly reward) levels 8-15
resolvedSequenceLevel = (int)Global.DB2Mgr.GetCurveValueAt(64389, generationParams.MythicPlusKeystoneLevel.Value);
break;
case 3009: // MythicPlus_Jackpot (weekly reward) levels 16-20
resolvedSequenceLevel = (int)Global.DB2Mgr.GetCurveValueAt(64395, generationParams.MythicPlusKeystoneLevel.Value);
break;
default:
break;
}
}
break;
case 4125:
resolvedSequenceLevel = 2;
break;
case 4126:
resolvedSequenceLevel = 3;
break;
case 4127:
resolvedSequenceLevel = 4;
break;
case 4128:
switch (generationParams.Context)
{
case ItemContext.RaidNormal:
case ItemContext.RaidRaidFinder:
case ItemContext.RaidHeroic:
resolvedSequenceLevel = 2;
break;
case ItemContext.RaidMythic:
resolvedSequenceLevel = 6;
break;
default:
break;
}
break;
case 4140:
switch (generationParams.Context)
{
case ItemContext.DungeonNormal:
resolvedSequenceLevel = 2;
break;
case ItemContext.DungeonHeroic:
resolvedSequenceLevel = 4;
break;
default:
break;
}
break;
default:
break;
}
foreach (var bonusListGroupEntry in _itemBonusListGroupEntries.LookupByKey(bonusTreeNode.ChildItemBonusListGroupID))
{
if ((resolvedSequenceLevel > 0 || bonusListGroupEntry.SequenceValue <= 0) && resolvedSequenceLevel != bonusListGroupEntry.SequenceValue)
continue;
itemLevelSelectorId = (uint)bonusListGroupEntry.ItemLevelSelectorID;
bonusListIDs.Add((uint)bonusListGroupEntry.ItemBonusListID);
break;
}
}
}
}
public static uint GetAzeriteUnlockBonusList(ushort azeriteUnlockMappingSetId, ushort minItemLevel, InventoryType inventoryType)
{
AzeriteUnlockMappingRecord selectedAzeriteUnlockMapping = null;
foreach (var azeriteUnlockMapping in _azeriteUnlockMappings.LookupByKey(azeriteUnlockMappingSetId))
{
if (minItemLevel < azeriteUnlockMapping.ItemLevel)
continue;
if (selectedAzeriteUnlockMapping != null && selectedAzeriteUnlockMapping.ItemLevel > azeriteUnlockMapping.ItemLevel)
continue;
selectedAzeriteUnlockMapping = azeriteUnlockMapping;
}
if (selectedAzeriteUnlockMapping != null)
{
switch (inventoryType)
{
case InventoryType.Head:
return selectedAzeriteUnlockMapping.ItemBonusListHead;
case InventoryType.Shoulders:
return selectedAzeriteUnlockMapping.ItemBonusListShoulders;
case InventoryType.Chest:
case InventoryType.Robe:
return selectedAzeriteUnlockMapping.ItemBonusListChest;
default:
break;
}
}
return 0;
}
public static List<uint> GetBonusListsForItem(uint itemId, ItemBonusGenerationParams generationParams)
{
List<uint> bonusListIDs = new();
ItemTemplate itemTemplate = Global.ObjectMgr.GetItemTemplate(itemId);
if (itemTemplate == null)
return bonusListIDs;
uint itemLevelSelectorId = 0;
foreach (var itemBonusTreeId in _itemToBonusTree.LookupByKey(itemId))
ApplyBonusTreeHelper(itemTemplate, itemBonusTreeId, generationParams, 0, ref itemLevelSelectorId, bonusListIDs);
var selector = CliDB.ItemLevelSelectorStorage.LookupByKey(itemLevelSelectorId);
if (selector != null)
{
short delta = (short)(selector.MinItemLevel - itemTemplate.GetBaseItemLevel());
uint bonus = GetItemBonusListForItemLevelDelta(delta);
if (bonus != 0)
bonusListIDs.Add(bonus);
var selectorQualitySet = CliDB.ItemLevelSelectorQualitySetStorage.LookupByKey(selector.ItemLevelSelectorQualitySetID);
if (selectorQualitySet != null)
{
var itemSelectorQualities = _itemLevelQualitySelectorQualities.LookupByKey(selector.ItemLevelSelectorQualitySetID);
if (!itemSelectorQualities.Empty())
{
ItemQuality quality = ItemQuality.Uncommon;
if (selector.MinItemLevel >= selectorQualitySet.IlvlEpic)
quality = ItemQuality.Epic;
else if (selector.MinItemLevel >= selectorQualitySet.IlvlRare)
quality = ItemQuality.Rare;
var itemSelectorQuality = itemSelectorQualities.First(record => record.Quality < (sbyte)quality);
if (itemSelectorQuality != null)
bonusListIDs.Add(itemSelectorQuality.QualityItemBonusListID);
}
}
uint azeriteUnlockBonusListId = GetAzeriteUnlockBonusList(selector.AzeriteUnlockMappingSet, selector.MinItemLevel, itemTemplate.GetInventoryType());
if (azeriteUnlockBonusListId != 0)
bonusListIDs.Add(azeriteUnlockBonusListId);
}
return bonusListIDs;
}
public static void VisitItemBonusTree(uint itemBonusTreeId, Action<ItemBonusTreeNodeRecord> visitor)
{
var treeItr = _itemBonusTrees.LookupByKey(itemBonusTreeId);
if (treeItr.Empty())
return;
foreach (var bonusTreeNode in treeItr)
{
visitor(bonusTreeNode);
if (bonusTreeNode.ChildItemBonusTreeID != 0)
VisitItemBonusTree(bonusTreeNode.ChildItemBonusTreeID, visitor);
}
}
public static List<uint> GetAllBonusListsForTree(uint itemBonusTreeId)
{
List<uint> bonusListIDs = new();
VisitItemBonusTree(itemBonusTreeId, bonusTreeNode =>
{
if (bonusTreeNode.ChildItemBonusListID != 0)
bonusListIDs.Add(bonusTreeNode.ChildItemBonusListID);
});
return bonusListIDs;
}
public struct ItemBonusGenerationParams
{
public ItemBonusGenerationParams(ItemContext context, int? mythicPlusKeystoneLevel = null, int? pvpTier = null)
{
Context = context;
MythicPlusKeystoneLevel = mythicPlusKeystoneLevel;
PvpTier = pvpTier;
}
public ItemContext Context;
public int? MythicPlusKeystoneLevel;
public int? PvpTier;
}
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ namespace Game.Entities
uint bonusListId = result.Read<uint>(1);
float chance = result.Read<float>(2);
if (Global.DB2Mgr.GetItemBonusList(bonusListId) == null)
if (ItemBonusMgr.GetItemBonuses(bonusListId).Empty())
{
Log.outError(LogFilter.Sql, $"Bonus list {bonusListId} used in `item_random_bonus_list_template` by id {id} doesn't have exist in ItemBonus.db2");
continue;
+1 -1
View File
@@ -337,7 +337,7 @@ namespace Game.Entities
public override float GetBlockPercent(uint attackerLevel)
{
float blockArmor = (float)m_activePlayerData.ShieldBlock;
float armorConstant = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.ArmorConstant, attackerLevel, -2, 0, Class.None);
float armorConstant = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.ArmorConstant, attackerLevel, -2, 0, Class.None, 0);
if ((blockArmor + armorConstant) == 0)
return 0;
+7 -8
View File
@@ -1209,8 +1209,6 @@ namespace Game.Entities
{
Log.outDebug(LogFilter.Player, "STORAGE: Creating initial item, itemId = {0}, count = {1}", itemId, amount);
var bonusListIDs = Global.DB2Mgr.GetDefaultItemBonusTree(itemId, context);
InventoryResult msg;
// attempt equip by one
while (amount > 0)
@@ -1219,8 +1217,7 @@ namespace Game.Entities
if (msg != InventoryResult.Ok)
break;
Item item = EquipNewItem(eDest, itemId, context, true);
item.SetBonuses(bonusListIDs);
EquipNewItem(eDest, itemId, context, true);
AutoUnequipOffhandIfNeed();
--amount;
}
@@ -1234,7 +1231,7 @@ namespace Game.Entities
msg = CanStoreNewItem(InventorySlots.Bag0, ItemConst.NullSlot, sDest, itemId, amount);
if (msg == InventoryResult.Ok)
{
StoreNewItem(sDest, itemId, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(itemId), null, context, bonusListIDs);
StoreNewItem(sDest, itemId, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(itemId), null, context);
return true; // stored
}
@@ -1242,18 +1239,20 @@ namespace Game.Entities
Log.outError(LogFilter.Player, "STORAGE: Can't equip or store initial item {0} for race {1} class {2}, error msg = {3}", itemId, GetRace(), GetClass(), msg);
return false;
}
public Item StoreNewItem(List<ItemPosCount> pos, uint itemId, bool update, uint randomBonusListId = 0, List<ObjectGuid> allowedLooters = null, ItemContext context = 0, List<uint> bonusListIDs = null, bool addToCollection = true)
{
uint count = 0;
foreach (var itemPosCount in pos)
count += itemPosCount.count;
Item item = Item.CreateItem(itemId, count, context, this);
Item item = Item.CreateItem(itemId, count, context, this, bonusListIDs == null);
if (item != null)
{
item.SetItemFlag(ItemFieldFlags.NewItem);
item.SetBonuses(bonusListIDs);
if (bonusListIDs != null)
item.SetBonuses(bonusListIDs);
item = StoreItem(pos, item, update);
@@ -6090,7 +6089,7 @@ namespace Game.Entities
InventoryResult msg = CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, item.itemid, item.count);
if (msg == InventoryResult.Ok)
{
Item newitem = StoreNewItem(dest, item.itemid, true, item.randomBonusListId, item.GetAllowedLooters(), item.context, item.BonusListIDs);
Item newitem = StoreNewItem(dest, item.itemid, true, item.randomBonusListId, item.GetAllowedLooters(), item.context);
if (ffaItem != null)
{
//freeforall case, notify only one player of the removal
+8 -8
View File
@@ -922,7 +922,7 @@ namespace Game.Entities
return false;
}
public void RewardQuestPackage(uint questPackageId, uint onlyItemId = 0)
public void RewardQuestPackage(uint questPackageId, ItemContext context, uint onlyItemId = 0)
{
bool hasFilteredQuestPackageReward = false;
var questPackageItems = Global.DB2Mgr.GetQuestPackageItems(questPackageId);
@@ -939,7 +939,7 @@ namespace Game.Entities
List<ItemPosCount> dest = new();
if (CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, questPackageItem.ItemID, questPackageItem.ItemQuantity) == InventoryResult.Ok)
{
Item item = StoreNewItem(dest, questPackageItem.ItemID, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(questPackageItem.ItemID));
Item item = StoreNewItem(dest, questPackageItem.ItemID, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(questPackageItem.ItemID), null, context);
SendNewItem(item, questPackageItem.ItemQuantity, true, false);
}
}
@@ -959,7 +959,7 @@ namespace Game.Entities
List<ItemPosCount> dest = new();
if (CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, questPackageItem.ItemID, questPackageItem.ItemQuantity) == InventoryResult.Ok)
{
Item item = StoreNewItem(dest, questPackageItem.ItemID, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(questPackageItem.ItemID));
Item item = StoreNewItem(dest, questPackageItem.ItemID, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(questPackageItem.ItemID), null, context);
SendNewItem(item, questPackageItem.ItemQuantity, true, false);
}
}
@@ -1020,7 +1020,7 @@ namespace Game.Entities
List<ItemPosCount> dest = new();
if (CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, itemId, quest.RewardItemCount[i]) == InventoryResult.Ok)
{
Item item = StoreNewItem(dest, itemId, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(itemId));
Item item = StoreNewItem(dest, itemId, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(itemId), null, ItemContext.QuestReward);
SendNewItem(item, quest.RewardItemCount[i], true, false);
}
else if (quest.IsDFQuest())
@@ -1035,8 +1035,8 @@ namespace Game.Entities
{
if (quest.IsWorldQuest())
currencyGainSource = CurrencyGainSource.WorldQuestRewardIgnoreCaps;
currencyGainSource = CurrencyGainSource.QuestRewardIgnoreCaps;
else
currencyGainSource = CurrencyGainSource.QuestRewardIgnoreCaps;
}
else if (quest.IsDaily())
currencyGainSource = CurrencyGainSource.DailyQuestReward;
@@ -1058,7 +1058,7 @@ namespace Game.Entities
List<ItemPosCount> dest = new();
if (CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, rewardId, quest.RewardChoiceItemCount[i]) == InventoryResult.Ok)
{
Item item = StoreNewItem(dest, rewardId, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(rewardId));
Item item = StoreNewItem(dest, rewardId, true, ItemEnchantmentManager.GenerateItemRandomBonusListId(rewardId), null, ItemContext.QuestReward);
SendNewItem(item, quest.RewardChoiceItemCount[i], true, false);
}
}
@@ -1068,7 +1068,7 @@ namespace Game.Entities
// QuestPackageItem.db2
if (rewardProto != null && quest.PackageID != 0)
RewardQuestPackage(quest.PackageID, rewardId);
RewardQuestPackage(quest.PackageID, ItemContext.QuestReward, rewardId);
break;
case LootItemType.Currency:
if (CliDB.CurrencyTypesStorage.HasRecord(rewardId) && quest.GetRewChoiceItemsCount() != 0)
+1 -1
View File
@@ -585,7 +585,7 @@ namespace Game.Entities
ApplyLevelScaling();
CreatureDifficulty creatureDifficulty = GetCreatureDifficulty();
SetCreateHealth((uint)Math.Max(Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, petlevel, creatureDifficulty.GetHealthScalingExpansion(), m_unitData.ContentTuningID, (Class)cinfo.UnitClass) * creatureDifficulty.HealthModifier * GetHealthMod(cinfo.Rank), 1.0f));
SetCreateHealth((uint)Math.Max(Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.CreatureHealth, petlevel, creatureDifficulty.GetHealthScalingExpansion(), m_unitData.ContentTuningID, (Class)cinfo.UnitClass, 0) * creatureDifficulty.HealthModifier * GetHealthMod(cinfo.Rank), 1.0f));
SetCreateMana(stats.BaseMana);
SetCreateStat(Stats.Strength, 22);
+3 -3
View File
@@ -773,7 +773,7 @@ namespace Game.Entities
{
creature.m_personalLoot = LootManager.GenerateDungeonEncounterPersonalLoot(dungeonEncounter.Id, creature.GetLootId(),
LootStorage.Creature, LootType.Corpse, creature, creature.GetCreatureDifficulty().GoldMin, creature.GetCreatureDifficulty().GoldMax,
(ushort)creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext(), tappers);
(ushort)creature.GetLootMode(), creature.GetMap().GetMapDifficulty(), tappers);
}
else if (!tappers.Empty())
{
@@ -784,7 +784,7 @@ namespace Game.Entities
uint lootid = creature.GetLootId();
if (lootid != 0)
loot.FillLoot(lootid, LootStorage.Creature, looter, dungeonEncounter != null, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext());
loot.FillLoot(lootid, LootStorage.Creature, looter, dungeonEncounter != null, false, creature.GetLootMode(), ItemBonusMgr.GetContextForPlayer(creature.GetMap().GetMapDifficulty(), looter));
if (creature.GetLootMode() > 0)
loot.GenerateMoneyLoot(creature.GetCreatureDifficulty().GoldMin, creature.GetCreatureDifficulty().GoldMax);
@@ -811,7 +811,7 @@ namespace Game.Entities
uint lootid = creature.GetLootId();
if (lootid != 0)
loot.FillLoot(lootid, LootStorage.Creature, tapper, true, false, creature.GetLootMode(), creature.GetMap().GetDifficultyLootItemContext());
loot.FillLoot(lootid, LootStorage.Creature, tapper, true, false, creature.GetLootMode(), ItemBonusMgr.GetContextForPlayer(creature.GetMap().GetMapDifficulty(), tapper));
if (creature.GetLootMode() > 0)
loot.GenerateMoneyLoot(creature.GetCreatureDifficulty().GoldMin, creature.GetCreatureDifficulty().GoldMax);
+1 -1
View File
@@ -3886,7 +3886,7 @@ namespace Game.Entities
}
// Expansion and ContentTuningID necessary? Does Player get a ContentTuningID too ?
float armorConstant = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.ArmorConstant, attackerLevel, -2, 0, attackerClass);
float armorConstant = Global.DB2Mgr.EvaluateExpectedStat(ExpectedStatType.ArmorConstant, attackerLevel, -2, 0, attackerClass, 0);
if ((armor + armorConstant) == 0)
return damage;