public async Task <IActionResult> GetItemTooltip(int itemID, string build) { var result = new TTItem(); // Basic Item information -- generally always available using (var query = new SQLiteCommand("SELECT IconFileDataID, ClassID, SubclassID, InventoryType FROM Item WHERE ID = :id")) { query.Connection = db; query.Parameters.AddWithValue(":id", itemID); await query.ExecuteNonQueryAsync(); var reader = await query.ExecuteReaderAsync(); if (!reader.HasRows) { return(NotFound()); } while (reader.Read()) { result.IconFileDataID = reader.GetInt32(0); result.ClassID = reader.GetInt32(1); result.SubClassID = reader.GetInt32(2); result.InventoryType = reader.GetInt32(3); } } // Icons in Item.db2 can be 0. Look up the proper one in ItemModifiedAppearance => ItemAppearance if (result.IconFileDataID == 0) { using var query = new SQLiteCommand("SELECT DefaultIconFileDataID FROM ItemAppearance WHERE ID IN (SELECT ItemAppearanceID FROM ItemModifiedAppearance WHERE ItemID = :id)"); query.Connection = db; query.Parameters.AddWithValue(":id", itemID); query.ExecuteNonQuery(); var reader = query.ExecuteReader(); while (reader.Read()) { result.IconFileDataID = reader.GetInt32(0); } } using (var query = new SQLiteCommand("SELECT * FROM ItemSparse WHERE ID = :id")) { query.Connection = db; query.Parameters.AddWithValue(":id", itemID); await query.ExecuteNonQueryAsync(); var reader = await query.ExecuteReaderAsync(); if (!reader.HasRows) { result.HasSparse = false; } while (reader.Read()) { result.HasSparse = true; result.ItemLevel = reader.GetInt32(reader.GetOrdinal("ItemLevel")); result.OverallQualityID = reader.GetInt32(reader.GetOrdinal("OverallQualityID")); result.Name = reader.GetString(reader.GetOrdinal("Display_lang")); result.FlavorText = reader.GetString(reader.GetOrdinal("Description_lang")); result.ExpansionID = reader.GetInt32(reader.GetOrdinal("ExpansionID")); result.RequiredLevel = reader.GetInt32(reader.GetOrdinal("RequiredLevel")); var itemDelay = reader.GetInt32(reader.GetOrdinal("ItemDelay")) / 1000f; var itemFlags1 = reader.GetInt32(reader.GetOrdinal("Flags_1")); var targetDamageDB = GetDamageDBByItemSubClass(result.SubClassID, (itemFlags1 & 0x200) == 0x200); var statTypes = new List <int>(); for (int i = 0; i < 10; i++) { statTypes.Add(reader.GetInt32(reader.GetOrdinal("StatModifier_bonusStat_" + i))); } if (statTypes.Count > 0 && statTypes.Any(x => x != -1) && statTypes.Any(x => x != 0)) { var(RandomPropField, RandomPropIndex) = TooltipUtils.GetRandomPropertyByInventoryType(result.OverallQualityID, result.InventoryType, result.SubClassID); using var rpropQuery = new SQLiteCommand("SELECT " + RandomPropField + "F_" + RandomPropIndex + " FROM RandPropPoints WHERE ID = :id"); rpropQuery.Connection = db; rpropQuery.Parameters.AddWithValue(":id", result.ItemLevel); rpropQuery.ExecuteNonQuery(); var rpropReader = rpropQuery.ExecuteReader(); float randProp = 0.0f; while (rpropReader.Read()) { randProp = rpropReader.GetFloat(0); } var statPercentEditor = new List <int>(); for (int i = 0; i < 10; i++) { statPercentEditor.Add(reader.GetInt32(reader.GetOrdinal("StatPercentEditor_" + i))); } var statList = new Dictionary <int, TTItemStat>(); for (var statIndex = 0; statIndex < statTypes.Count; statIndex++) { if (statTypes[statIndex] == -1 || statTypes[statIndex] == 0) { continue; } var stat = TooltipUtils.CalculateItemStat(statTypes[statIndex], randProp, result.ItemLevel, statPercentEditor[statIndex], 0.0f, result.OverallQualityID, result.InventoryType, result.SubClassID, build); if (stat.Value == 0) { continue; } if (statList.TryGetValue(statTypes[statIndex], out var currStat)) { currStat.Value += stat.Value; } else { statList.Add(statTypes[statIndex], stat); } } result.Stats = statList.Values.ToArray(); } var quality = result.OverallQualityID; if (quality == 7) // Heirloom == Rare { quality = 3; } if (quality == 5) // Legendary = Epic { quality = 4; } using var damageQuery = new SQLiteCommand("SELECT Quality_" + quality + " FROM " + targetDamageDB + " WHERE ItemLevel = :ilvl"); damageQuery.Connection = db; damageQuery.Parameters.AddWithValue(":ilvl", result.ItemLevel); damageQuery.ExecuteNonQuery(); var damageReader = damageQuery.ExecuteReader(); float itemDamage = 0.0f; while (damageReader.Read()) { itemDamage = damageReader.GetFloat(0); } var dmgVariance = reader.GetFloat(reader.GetOrdinal("DmgVariance")); //Use. as decimal separator NumberFormatInfo nfi = new NumberFormatInfo(); nfi.NumberDecimalSeparator = "."; result.MinDamage = Math.Floor(itemDamage * itemDelay * (1 - dmgVariance * 0.5)).ToString(nfi); result.MaxDamage = Math.Floor(itemDamage * itemDelay * (1 + dmgVariance * 0.5)).ToString(nfi); result.Speed = itemDelay.ToString("F2", nfi); result.DPS = itemDamage.ToString("F2", nfi); } } if (!result.HasSparse) { using (var query = new SQLiteCommand("SELECT * FROM ItemSearchName WHERE ID = :id")) { query.Connection = db; query.Parameters.AddWithValue(":id", itemID); await query.ExecuteNonQueryAsync(); var reader = await query.ExecuteReaderAsync(); if (!reader.HasRows) { result.Name = "Unknown Item"; } while (reader.Read()) { result.Name = reader.GetString(reader.GetOrdinal("Display_lang")); result.RequiredLevel = reader.GetInt32(reader.GetOrdinal("RequiredLevel")); result.ExpansionID = reader.GetInt32(reader.GetOrdinal("ExpansionID")); result.ItemLevel = reader.GetInt32(reader.GetOrdinal("ItemLevel")); result.OverallQualityID = reader.GetInt32(reader.GetOrdinal("OverallQualityID")); } } } using (var query = new SQLiteCommand("SELECT ItemEffectID FROM ItemXItemEffect WHERE ItemID = :id")) { query.Connection = db; query.Parameters.AddWithValue(":id", itemID); await query.ExecuteNonQueryAsync(); var reader = await query.ExecuteReaderAsync(); var itemEffects = new List <TTItemEffect>(); if (reader.HasRows) { while (reader.Read()) { var itemEffectID = reader.GetInt32(reader.GetOrdinal("ItemEffectID")); using (var subquery = new SQLiteCommand("SELECT * FROM ItemEffect WHERE ID = :id")) { subquery.Connection = db; subquery.Parameters.AddWithValue(":id", itemEffectID); await subquery.ExecuteNonQueryAsync(); var subreader = await subquery.ExecuteReaderAsync(); if (subreader.HasRows) { while (subreader.Read()) { var triggerType = subreader.GetInt32(subreader.GetOrdinal("TriggerType")); var spellID = subreader.GetInt32(subreader.GetOrdinal("SpellID")); itemEffects.Add(new TTItemEffect() { Spell = new TTSpell() { SpellID = spellID, Description = "Loading.." }, TriggerType = triggerType }); } } } } } result.ItemEffects = itemEffects.ToArray(); } /* Fixups */ // Classic ExpansionID column has 254, make 0. ¯\_(ツ)_/¯ if (result.ExpansionID == 254) { result.ExpansionID = 0; } return(Ok(result)); }
public async Task <IActionResult> GetItemTooltip(int itemID, string build) { var result = new TTItem(); var itemDB = await dbcManager.GetOrLoad("Item", build); if (!itemDB.TryGetValue(itemID, out DBCDRow itemEntry)) { return(NotFound()); } result.IconFileDataID = (int)itemEntry["IconFileDataID"]; result.ClassID = (byte)itemEntry["ClassID"]; result.SubClassID = (byte)itemEntry["SubclassID"]; result.InventoryType = (sbyte)itemEntry["InventoryType"]; // Icons in Item.db2 can be 0. Look up the proper one in ItemModifiedAppearance => ItemAppearance if (result.IconFileDataID == 0) { var itemModifiedAppearances = await dbcManager.FindRecords("ItemModifiedAppearance", build, "ItemID", itemID); if (itemModifiedAppearances.Count > 0) { var itemAppearanceDB = await dbcManager.GetOrLoad("ItemAppearance", build); if (itemAppearanceDB.TryGetValue((ushort)itemModifiedAppearances[0]["ItemAppearanceID"], out DBCDRow itemAppearanceRow)) { result.IconFileDataID = (int)itemAppearanceRow["DefaultIconFileDataID"]; } } } var itemSparseDB = await dbcManager.GetOrLoad("ItemSparse", build); if (!itemSparseDB.TryGetValue(itemID, out DBCDRow itemSparseEntry)) { var itemSearchNameDB = await dbcManager.GetOrLoad("ItemSearchName", build); if (!itemSearchNameDB.TryGetValue(itemID, out DBCDRow itemSearchNameEntry)) { result.Name = "Unknown Item"; } else { result.Name = (string)itemSearchNameEntry["Display_lang"]; result.RequiredLevel = (sbyte)itemSearchNameEntry["RequiredLevel"]; result.ExpansionID = (byte)itemSearchNameEntry["ExpansionID"]; result.ItemLevel = (ushort)itemSearchNameEntry["ItemLevel"]; result.OverallQualityID = (byte)itemSearchNameEntry["OverallQualityID"]; } result.HasSparse = false; } else { result.HasSparse = true; result.ItemLevel = (ushort)itemSparseEntry["ItemLevel"]; result.OverallQualityID = (byte)itemSparseEntry["OverallQualityID"]; result.Name = (string)itemSparseEntry["Display_lang"]; result.FlavorText = (string)itemSparseEntry["Description_lang"]; result.ExpansionID = (byte)itemSparseEntry["ExpansionID"]; result.RequiredLevel = (sbyte)itemSparseEntry["RequiredLevel"]; var itemDelay = (ushort)itemSparseEntry["ItemDelay"] / 1000f; var targetDamageDB = GetDamageDBByItemSubClass((byte)itemEntry["SubclassID"], (itemSparseEntry.FieldAs <int[]>("Flags")[1] & 0x200) == 0x200); var statTypes = itemSparseEntry.FieldAs <sbyte[]>("StatModifier_bonusStat"); if (statTypes.Length > 0 && statTypes.Any(x => x != -1) && statTypes.Any(x => x != 0)) { var(RandomPropField, RandomPropIndex) = TooltipUtils.GetRandomPropertyByInventoryType(result.OverallQualityID, result.InventoryType, result.SubClassID, build); var randomPropDB = await dbcManager.GetOrLoad("RandPropPoints", build); int randProp; if (randomPropDB.TryGetValue(result.ItemLevel, out DBCDRow randPropEntry)) { randProp = (int)randPropEntry.FieldAs <uint[]>(RandomPropField)[RandomPropIndex]; } else { throw new Exception("Item Level " + result.ItemLevel + " not found in RandPropPoints"); } var statPercentEditor = itemSparseEntry.FieldAs <int[]>("StatPercentEditor"); var statList = new Dictionary <sbyte, TTItemStat>(); for (var statIndex = 0; statIndex < statTypes.Length; statIndex++) { if (statTypes[statIndex] == -1 || statTypes[statIndex] == 0) { continue; } var stat = TooltipUtils.CalculateItemStat(statTypes[statIndex], randProp, result.ItemLevel, statPercentEditor[statIndex], 0.0f, result.OverallQualityID, result.InventoryType, result.SubClassID, build); if (stat.Value == 0) { continue; } if (statList.TryGetValue(statTypes[statIndex], out var currStat)) { currStat.Value += stat.Value; } else { statList.Add(statTypes[statIndex], stat); } } result.Stats = statList.Values.ToArray(); } var damageRecord = await dbcManager.FindRecords(targetDamageDB, build, "ItemLevel", result.ItemLevel); var quality = result.OverallQualityID; if (quality == 7) // Heirloom == Rare { quality = 3; } if (quality == 5) // Legendary = Epic { quality = 4; } var itemDamage = damageRecord[0].FieldAs <float[]>("Quality")[quality]; var dmgVariance = (float)itemSparseEntry["DmgVariance"]; // Use . as decimal separator NumberFormatInfo nfi = new NumberFormatInfo(); nfi.NumberDecimalSeparator = "."; result.MinDamage = Math.Floor(itemDamage * itemDelay * (1 - dmgVariance * 0.5)).ToString(nfi); result.MaxDamage = Math.Floor(itemDamage * itemDelay * (1 + dmgVariance * 0.5)).ToString(nfi); result.Speed = itemDelay.ToString("F2", nfi); result.DPS = itemDamage.ToString("F2", nfi); } var itemEffectEntries = await dbcManager.FindRecords("ItemEffect", build, "ParentItemID", itemID); if (itemEffectEntries.Count > 0) { var spellDB = await dbcManager.GetOrLoad("Spell", build); var spellNameDB = await dbcManager.GetOrLoad("SpellName", build); result.ItemEffects = new TTItemEffect[itemEffectEntries.Count]; for (var i = 0; i < itemEffectEntries.Count; i++) { result.ItemEffects[i].TriggerType = (sbyte)itemEffectEntries[i]["TriggerType"]; var ttSpell = new TTSpell { SpellID = (int)itemEffectEntries[i]["SpellID"] }; if (spellDB.TryGetValue((int)itemEffectEntries[i]["SpellID"], out DBCDRow spellRow)) { var spellDescription = (string)spellRow["Description_lang"]; if (!string.IsNullOrWhiteSpace(spellDescription)) { ttSpell.Description = spellDescription; } } if (spellNameDB.TryGetValue((int)itemEffectEntries[i]["SpellID"], out DBCDRow spellNameRow)) { var spellName = (string)spellNameRow["Name_lang"]; if (!string.IsNullOrWhiteSpace(spellName)) { ttSpell.Name = spellName; } } result.ItemEffects[i].Spell = ttSpell; } } /* Fixups */ // Classic ExpansionID column has 254, make 0. ¯\_(ツ)_/¯ if (result.ExpansionID == 254) { result.ExpansionID = 0; } return(Ok(result)); }
// Stat/Effect Point parsing based on work done by simc & https://github.com/TrinityCore/SpellWork public double?SupplyEffectPoint(int spellID, uint?effectIndex) { var points = 0.0f; using (var query = new SQLiteCommand("SELECT * FROM SpellEffect WHERE SpellID = :id AND EffectIndex = :effectIndex")) { query.Connection = db; query.Parameters.AddWithValue(":id", spellID); query.Parameters.AddWithValue(":effectIndex", effectIndex - 1); query.ExecuteNonQuery(); var reader = query.ExecuteReader(); if (!reader.HasRows) { return(points); } while (reader.Read()) { var effectPoints = reader.GetFloat(reader.GetOrdinal("EffectBasePointsF")); var spellAttributes = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; using (var subQuery = new SQLiteCommand("SELECT * FROM SpellMisc WHERE ID = :id")) { subQuery.Connection = db; subQuery.Parameters.AddWithValue(":id", spellID); subQuery.ExecuteNonQuery(); var subReader = subQuery.ExecuteReader(); if (subReader.HasRows) { while (subReader.Read()) { var spellAttrs = new List <int>(); for (int i = 0; i < 14; i++) { spellAttrs.Add(subReader.GetInt32(subReader.GetOrdinal("Attributes_" + i))); } spellAttributes = spellAttrs.ToArray(); } } } var coefficient = reader.GetFloat(reader.GetOrdinal("Coefficient")); var scalingClass = reader.GetInt32(reader.GetOrdinal("ScalingClass")); if (coefficient >= 0.0f) { // TODO: Not yet implemented //SpellScaling? ItemLevel based scaling? // TODO ScalingClass -1, -2, -3, -4, -5, -6, -7, -8, -9 // -6 - Sta // -7 RandProp ? // -8 RandProp DamageReplaceStatF // -9 RandProp DamageReplaceStatF // ItemLevel based scaling if (itemID == 0) { return(effectPoints); } var itemLevel = 0; var itemSlot = 0; var itemQuality = 0; using (var subquery = new SQLiteCommand("SELECT ItemLevel, InventoryType, OverallQualityID FROM ItemSparse WHERE ID = :id")) { subquery.Connection = db; subquery.Parameters.AddWithValue(":id", itemID); subquery.ExecuteNonQuery(); var subreader = subquery.ExecuteReader(); if (!subreader.HasRows) { return(effectPoints); } while (subreader.Read()) { itemLevel = subreader.GetInt32(subreader.GetOrdinal("ItemLevel")); itemSlot = subreader.GetInt32(subreader.GetOrdinal("InventoryType")); itemQuality = subreader.GetInt32(subreader.GetOrdinal("OverallQualityID")); } } var multiplier = GetItemMultiplier(itemSlot, itemLevel, build); if (scalingClass == -7) { var(RandomPropField, RandomPropIndex) = TooltipUtils.GetRandomPropertyByInventoryType(itemQuality, itemSlot, 0); using var rpropQuery = new SQLiteCommand("SELECT " + RandomPropField + "F_0" + " FROM RandPropPoints WHERE ID = :id"); rpropQuery.Connection = db; rpropQuery.Parameters.AddWithValue(":id", itemLevel); rpropQuery.ExecuteNonQuery(); var rpropReader = rpropQuery.ExecuteReader(); float randProp = 0.0f; while (rpropReader.Read()) { randProp = rpropReader.GetFloat(0); } points = randProp * (float)multiplier; } else if (scalingClass == -8 || scalingClass == -9) { var randomPropField = "DamageReplaceStatF"; if (scalingClass == -9) { randomPropField = "DamageSecondaryF"; } using var rpropQuery = new SQLiteCommand("SELECT " + randomPropField + " FROM RandPropPoints WHERE ID = :id"); rpropQuery.Connection = db; rpropQuery.Parameters.AddWithValue(":id", itemLevel); rpropQuery.ExecuteNonQuery(); var rpropReader = rpropQuery.ExecuteReader(); float randProp = 0.0f; while (rpropReader.Read()) { randProp = rpropReader.GetFloat(0); } points = randProp; } return(points * coefficient); } else { //using (var subQuery = new SQLiteCommand("SELECT * FROM ExpectedStat WHERE Lvl = :id AND ExpansionID = :expansionID")) //{ // subQuery.Connection = db; // subQuery.Parameters.AddWithValue(":id", level); // subQuery.Parameters.AddWithValue(":expansionID", this.expansion); // subQuery.ExecuteNonQuery(); // var subReader = subQuery.ExecuteReader(); // if (subReader.HasRows) // { // } //} var expectedStatType = TooltipUtils.GetExpectedStatTypeBySpellEffect(reader.GetInt32(reader.GetOrdinal("Effect")), reader.GetInt16(reader.GetOrdinal("EffectAura")), reader.GetInt32(reader.GetOrdinal("EffectMiscValue_0"))); if (expectedStatType != TooltipUtils.ExpectedStatType.None) { if ((spellAttributes[0] & 0x80000) == 0x80000) { expectedStatType = TooltipUtils.ExpectedStatType.CreatureAutoAttackDps; } } return(effectPoints); } } } return(points); }