/// <summary> /// Setup a basic item /// </summary> /// <param name="itemId">The ID of the item</param> internal async Task <SimcItem> BuildItemAsync(uint itemId) { var rawItemData = await _simcUtilityService.GetRawItemDataAsync(itemId); if (rawItemData == null) { _logger?.LogError($"Unable to find item {itemId}"); return(null); } // Setup the item var item = new SimcItem { Name = rawItemData.Name, ItemId = rawItemData.Id, ItemClass = rawItemData.ItemClass, ItemSubClass = rawItemData.ItemSubClass, InventoryType = rawItemData.InventoryType, }; foreach (var socketColour in rawItemData.SocketColour) { item.Sockets.Add((ItemSocketColor)socketColour); } AddItemLevel(item, rawItemData.ItemLevel); SetItemQuality(item, rawItemData.Quality); return(item); }
internal async Task AddBonusScalingAsync(SimcItem item, int curveId, int dropLevel) { // from item_database::apply_item_scaling (sc_item_data.cpp) if (curveId == 0) { return; } // Then the base_value becomes MIN(player_level, curveData.last().primary1); var curveData = await FindCurvePointByIdAsync(curveId); if (curveData.Count == 0) { return; } var baseValue = Math.Min(dropLevel, curveData.LastOrDefault().Primary1); double scaledResult = await FindCurvePointValueAsync(curveId, baseValue); int newItemLevel = (int)Math.Floor(scaledResult + 0.5); _logger?.LogDebug($"[{item.ItemId}] Item {item.Name} setting scaled item level to {newItemLevel}"); item.ItemLevel = newItemLevel; }
internal async Task UpdateItemEffects(SimcItem item) { var rawItemData = await _simcUtilityService.GetRawItemDataAsync(item.ItemId); foreach (var effect in rawItemData.ItemEffects) { await AddSpellEffectAsync(item, effect); } }
internal void AddItemMod(SimcItem item, ItemModType modType, int statAllocation) { var newMod = new SimcItemMod { Type = modType, RawStatAllocation = statAllocation }; item.Mods.Add(newMod); }
internal async Task AddItemEffect(SimcItem item, int effectId) { // Try to add effect var itemEffect = await _simcUtilityService.GetItemEffectAsync((uint)effectId); if (itemEffect == null) { _logger?.LogError($"No item effect found when adding {effectId} to {item.ItemId}"); } _logger?.LogError($"Adding item effect {effectId} to {item.ItemId} (SpellId: {itemEffect.SpellId})"); await AddSpellEffectAsync(item, itemEffect); }
internal async Task AddGemsAsync(SimcItem item, IList <int> gemIds) { foreach (var gemId in gemIds) { var gem = await _simcUtilityService.GetRawItemDataAsync((uint)gemId); var gemProperty = await _simcUtilityService.GetGemPropertyAsync(gem.GemProperties); var enchantmentProperties = await _simcUtilityService.GetItemEnchantmentAsync(gemProperty.EnchantId); // Here we can either veer off and grab enchant details from the spell id // 1) If there is an enchantmentProperties.spellid // 2) otherwise process it with raw item enchantment data if (enchantmentProperties.SpellId > 0) { throw new NotImplementedException("Enchantments with attached spells not yet implemented."); } else { // 2) raw item enchantment data var scaleIndex = _simcUtilityService.GetClassId((PlayerScaling)enchantmentProperties.ScalingId); // Because the array is zero indexed, take one off the player level // enchant breakdown from item_database::item_enchantment_effect_stats // from dbc_t::spell_scaling // TODO: Pull the players level through to here var scaledValue = await _simcUtilityService.GetSpellScalingMultiplierAsync(scaleIndex, 60); //// Grab the stat this gem increases var stat = (ItemModType)enchantmentProperties.SubEnchantments[0].Property; //// Now add it to the item // Create a new SimcItemGem with the stat, rating etc. var newGem = new SimcItemGem { StatRating = (int)scaledValue, Type = stat }; item.Gems.Add(newGem); } } }
public async Task SSC_Creates_Item_Spell_Raw_Obj_9() { // Arrange // Use the First Class Healing Distributor spell 352273 var item = new SimcItem() { ItemLevel = 226, Quality = ItemQuality.ITEM_QUALITY_EPIC, InventoryType = InventoryType.INVTYPE_TRINKET }; // Act var spell = await _spellCreationService.GenerateItemSpellAsync(item, 352273); // Assert Assert.IsNotNull(spell); Assert.IsNotNull(spell.Effects); Assert.AreEqual(1, spell.Effects.Count); Assert.AreEqual(64.930999760000006d, spell.Effects[0].ScaleBudget); Assert.AreEqual(21.946373000000001d, spell.Effects[0].Coefficient); }
private async Task AddSpellEffectAsync(SimcItem item, SimcRawItemEffect effect) { // Note: there is a similar process inside this method: // double spelleffect_data_t::average( const player_t* p, unsigned level ) const // That is done to get scale values for non-item effects based instead on player level // This is for things like racial abilities and uses a simpler formula // It does use the spell scaling array values, which we already have. var effectSpell = await _simcSpellCreationService.GenerateItemSpellAsync(item, effect.SpellId); var newEffect = new SimcItemEffect { EffectId = effect.Id, CooldownDuration = effect.CooldownDuration, CooldownGroup = effect.CooldownGroup, CooldownGroupDuration = effect.CooldownGroupDuration, Spell = effectSpell }; item.Effects.Add(newEffect); }
public async Task SSC_Creates_Item_Spell_Raw_Obj() { // Arrange // Use the Brimming Ember Shard spell 343538 var item = new SimcItem() { ItemLevel = 226, Quality = ItemQuality.ITEM_QUALITY_EPIC, InventoryType = InventoryType.INVTYPE_TRINKET }; // Act var spell = await _spellCreationService.GenerateItemSpellAsync(item, 343538); // Assert Assert.IsNotNull(spell); Assert.IsNotNull(spell.Effects); Assert.AreEqual(2, spell.Effects.Count); Assert.AreEqual(41.071998600000001d, spell.Effects[0].ScaleBudget); Assert.AreEqual(460.97500600000001d, spell.Effects[0].Coefficient); Assert.AreEqual(621.39996299999996d, spell.Effects[1].Coefficient); }
internal void AddItemSockets(SimcItem item, int numSockets, ItemSocketColor socketColor) { // Based on item_database::apply_item_bonus case ITEM_BONUS_SOCKET: // This original method just adds colours to existing sockets. var addedSockets = 0; // Basically just loop through each of the sockets on the item and attempt to add colours for (var i = 0; i < item.Sockets.Count && addedSockets < numSockets; i++) { // Can't add colours if there are no uncoloured sockets left if (item.Sockets.Where(s => s == ItemSocketColor.SOCKET_COLOR_NONE).Count() > 0) { var socket = item.Sockets.Where(s => s == ItemSocketColor.SOCKET_COLOR_NONE).FirstOrDefault(); socket = socketColor; addedSockets++; } else { _logger?.LogError($"Trying to apply a colour to a socket, but no sockets left."); } } }
/// <summary> /// Update the item with all the options /// </summary> /// <param name="item">Base item to update</param> /// <param name="bonusIds">Bonus IDs to apply</param> /// <param name="gemIds">Gem IDs to apply</param> internal async Task UpdateItemAsync(SimcItem item, IList <int> bonusIds, IList <int> gemIds, int dropLevel) { var rawItemData = await _simcUtilityService.GetRawItemDataAsync(item.ItemId); // Now add the base mods foreach (var mod in rawItemData.ItemMods) { if (mod.SocketMultiplier > 0) { throw new NotImplementedException("Socket Multiplier not yet implemented"); } AddItemMod(item, mod.ModType, mod.StatAllocation); } await ProcessBonusIdsAsync(item, bonusIds, dropLevel); foreach (var mod in item.Mods) { mod.StatRating = await _simcUtilityService.GetScaledModValueAsync(item, mod.Type, mod.RawStatAllocation); } await AddGemsAsync(item, gemIds); }
public async Task <SimcSpell> GenerateItemSpellAsync(SimcItem item, uint spellId) { var spell = await BuildItemSpellAsync(spellId, item.ItemLevel, item.Quality, item.InventoryType); return(spell); }
internal void AddItemLevel(SimcItem item, int newItemLevel) { item.ItemLevel += newItemLevel; }
internal void SetItemQuality(SimcItem item, ItemQuality newQuality) { item.Quality = newQuality; }
internal async Task ProcessBonusIdsAsync(SimcItem item, IList <int> bonusIds, int dropLevel = 0) { // Skip loading the bonus IDs if we have none if (bonusIds.Count == 0) { return; } var bonuses = await _cacheService.GetParsedFileContentsAsync <List <SimcRawItemBonus> >(SimcParsedFileType.ItemBonusData); // Go through each of the bonus IDs on the item foreach (var bonusId in bonusIds) { // Find the bonus data for this bonus id var bonusEntries = bonuses.Where(b => b.BonusId == bonusId).ToList(); if (bonusEntries == null) { continue; } // Process the bonus data foreach (var entry in bonusEntries) { // From item_database::apply_item_bonus switch (entry.Type) { case ItemBonusType.ITEM_BONUS_ILEVEL: if (item.ItemLevelForced) { _logger?.LogDebug($"[{item.ItemId}] [{bonusId}:{entry.Type}] Item {item.Name} SKIPPING adding {entry.Value1} " + $"ilvl to {item.ItemLevel} => {item.ItemLevel + entry.Value1} due to item level being forced through ilevel="); break; } if (bonusIds.Contains(6652)) { _logger?.LogDebug($"[{item.ItemId}] [{bonusId}:{entry.Type}] Item {item.Name} SKIPPING adding {entry.Value1} " + $"ilvl to {item.ItemLevel} => {item.ItemLevel + entry.Value1} due to presence of bonusId 6652 (bug #68.)"); break; // Bug fix for Simc#5490 (#68) - invalid item scaling on unnatural items } _logger?.LogDebug($"[{item.ItemId}] [{bonusId}:{entry.Type}] Item {item.Name} adding {entry.Value1} ilvl to {item.ItemLevel} => {item.ItemLevel + entry.Value1}"); AddItemLevel(item, entry.Value1); break; case ItemBonusType.ITEM_BONUS_MOD: _logger?.LogDebug($"[{item.ItemId}] [{bonusId}:{entry.Type}] Item {item.Name} adding {entry.Value1} with more alloc: {entry.Value2}"); AddItemMod(item, (ItemModType)entry.Value1, entry.Value2); break; case ItemBonusType.ITEM_BONUS_QUALITY: _logger?.LogDebug($"[{item.ItemId}] [{bonusId}:{entry.Type}] Item {item.Name} adjusting quality from {item.Quality} to {(ItemQuality)entry.Value1} ({entry.Value1})"); SetItemQuality(item, (ItemQuality)entry.Value1); break; case ItemBonusType.ITEM_BONUS_SOCKET: _logger?.LogDebug($"[{item.ItemId}] [{bonusId}:{entry.Type}] Item {item.Name} adding {entry.Value1} sockets of type {entry.Value2}"); AddItemSockets(item, entry.Value1, (ItemSocketColor)entry.Value2); break; case ItemBonusType.ITEM_BONUS_ADD_ITEM_EFFECT: _logger?.LogDebug($"[{item.ItemId}] [{bonusId}:{entry.Type}] Item {item.Name} adding effect {entry.Value1}"); await AddItemEffect(item, entry.Value1); break; case ItemBonusType.ITEM_BONUS_SCALING_2: if (item.ItemLevelForced) { _logger?.LogDebug($"[{item.ItemId}] [{bonusId}:{entry.Type}] Item {item.Name} SKIPPING adding {entry.Value1} " + $"ilvl to {item.ItemLevel} => {item.ItemLevel + entry.Value1} due to item level being forced through ilevel="); break; } _logger?.LogDebug($"[{item.ItemId}] [{bonusId}:{entry.Type}] Item {item.Name} adding item scaling {entry.Value4} from {dropLevel}"); await AddBonusScalingAsync(item, entry.Value4, dropLevel); break; // Unused bonustypes: case ItemBonusType.ITEM_BONUS_DESC: case ItemBonusType.ITEM_BONUS_SUFFIX: break; default: var entryString = JsonConvert.SerializeObject(entry); _logger?.LogTrace($"[{item.ItemId}] [{bonusId}:{entry.Type}] Unknown bonus entry: {entry.Type} ({bonusId}): {entryString}"); break; } } } }