private int EvaluateStat(D2Stat stat, D2ItemStatCost statCost, D2Unit unit) { if (statCost.Op < 2 || statCost.Op > 5) { return(stat.Value >> statCost.ValShift); } if (statCost.OpBase < 0 || statCost.OpBase >= ItemStatCost.Length) { return(0); } if (unit == null) { return(0); } // Get the unit stat. int unitStat = GetStatValue(unit, statCost.OpBase) ?? 0; unitStat >>= ItemStatCost[statCost.OpBase].ValShift; // Evaluate based on the unit stat (e.g. player level) int value = (stat.Value * unitStat) >> statCost.OpParam; return(value >> statCost.ValShift); }
public Dictionary <StatIdentifier, D2Stat> GetItemStatsMap(D2Unit unit) { List <D2Stat> stats = GetItemStats(unit); if (stats == null) { return(null); } Dictionary <StatIdentifier, D2Stat> dict = new Dictionary <StatIdentifier, D2Stat>(); foreach (D2Stat stat in stats) { if (!stat.HasValidLoStatIdentifier()) { continue; } D2Stat s = new D2Stat(stat); if (dict.ContainsKey((StatIdentifier)stat.LoStatID)) { dict[(StatIdentifier)stat.LoStatID].Value = dict[(StatIdentifier)stat.LoStatID].Value + s.Value; } else { dict[(StatIdentifier)stat.LoStatID] = s; } } return(dict); }
private int EvaluateStat(D2Stat stat, D2ItemStatCost statCost) { if (statCost.Op < 2 || statCost.Op > 5) { return(stat.Value >> statCost.ValShift); } if (statCost.OpBase < 0 || statCost.OpBase >= ItemStatCost.Length) { return(0); } var player = GetPlayer(); if (player == null) { return(0); } // Get the player stat. int playerStat = GetStatValue(player, statCost.OpBase) ?? 0; playerStat >>= ItemStatCost[statCost.OpBase].ValShift; // Evaluate based on the player stat (e.g. player level) int value = (stat.Value * playerStat) >> statCost.OpParam; return(value >> statCost.ValShift); }
public Dictionary <StatIdentifier, D2Stat> GetItemStatsMap(D2Unit unit) { List <D2Stat> stats = GetItemStats(unit); if (stats == null) { return(null); } Dictionary <StatIdentifier, D2Stat> dict = new Dictionary <StatIdentifier, D2Stat>(); foreach (D2Stat stat in stats) { if (!Enum.IsDefined(typeof(StatIdentifier), stat.LoStatID)) { continue; } D2Stat s = new D2Stat(); s.LoStatID = stat.LoStatID; s.HiStatID = stat.HiStatID; s.Value = stat.Value; if (dict.ContainsKey((StatIdentifier)stat.LoStatID)) { dict[(StatIdentifier)stat.LoStatID].Value = dict[(StatIdentifier)stat.LoStatID].Value + s.Value; } else { dict[(StatIdentifier)stat.LoStatID] = s; } } return(dict); }
public void TestValidLoStatIdentifier() { D2Stat stat = new D2Stat(); stat.HiStatID = 140; stat.Value = 4999; stat.LoStatID = 140; Assert.AreEqual(false, stat.HasValidLoStatIdentifier()); stat.LoStatID = (ushort)StatIdentifier.PoisonDivisor; Assert.AreEqual(true, stat.HasValidLoStatIdentifier()); stat.LoStatID = (ushort)StatIdentifier.CannotBeFrozen; Assert.AreEqual(true, stat.HasValidLoStatIdentifier()); stat.LoStatID = 300; Assert.AreEqual(false, stat.HasValidLoStatIdentifier()); }
private int EvaluateStat(D2Stat stat, D2ItemStatCost statCost) { int value = stat.Value; if (statCost.Op >= 2 && statCost.Op <= 5) { if (statCost.OpBase < 0 || statCost.OpBase >= ItemStatCost.Length) { return(0); } D2ItemStatCost baseStatCost = ItemStatCost[statCost.OpBase]; // Get the player stat. D2Unit player = GetPlayer(); int playerStat = GetStatValue(player, statCost.OpBase) ?? 0; playerStat >>= baseStatCost.ValShift; // Evaluate based on the player stat (e.g. player level) value = (value * playerStat) >> statCost.OpParam; } return(value >> statCost.ValShift); }
public bool TryHandleStat(D2Stat stat, out string description) { description = null; if (!stat.HasValidLoStatIdentifier()) { return(false); } switch ((StatIdentifier)stat.LoStatID) { // Handle one and two handed damage. case StatIdentifier.DamageMin: case StatIdentifier.DamageMax: case StatIdentifier.SecondaryDamageMin: case StatIdentifier.SecondaryDamageMax: // Only print once if it's a range. if (HasHandledDamageRange) { return(true); } // Skip two-handed damage if there is a one-handed damage source or if no damage is defined. if (stat.IsOfType(StatIdentifier.SecondaryDamageMin) && !IsDamageMinSecondary) { return(true); } if (stat.IsOfType(StatIdentifier.SecondaryDamageMax) && !IsDamageMaxSecondary) { return(true); } // If not a range, print normally. if (!damage.HasRange()) { return(false); } // We also print twice if they are the same. if (damage.min == damage.max) { return(false); } HasHandledDamageRange = true; description = damage.ToString(stringReader); return(true); // Handle enhanced damage. case StatIdentifier.ItemDamageMinPercent: if (!damagePercent.HasRange()) { return(false); } description = string.Format( "+{0}% {1}", stat.Value, stringReader.GetString(StringConstants.EnhancedDamage).TrimEnd() ); return(true); case StatIdentifier.ItemDamageMaxPercent: return(damagePercent.HasRange()); // Handle fire damage ranges. case StatIdentifier.FireDamageMin: if (!fireDamage.HasRange()) { return(false); } description = fireDamage.ToString(stringReader); return(true); case StatIdentifier.FireDamageMax: return(fireDamage.HasRange()); // Handle lightning damage ranges. case StatIdentifier.LightningDamageMin: if (!lightningDamage.HasRange()) { return(false); } description = lightningDamage.ToString(stringReader); return(true); case StatIdentifier.LightningDamageMax: return(lightningDamage.HasRange()); // Handle magic damage ranges. case StatIdentifier.MagicDamageMin: if (!magicDamage.HasRange()) { return(false); } description = magicDamage.ToString(stringReader); return(true); case StatIdentifier.MagicDamageMax: return(magicDamage.HasRange()); // Handle cold damage ranges. case StatIdentifier.ColdDamageMin: if (!coldDamage.HasRange()) { return(false); } description = coldDamage.ToString(stringReader); return(true); case StatIdentifier.ColdDamageMax: return(coldDamage.HasRange()); // Handle poison damage ranges. case StatIdentifier.PoisonDamageMax: case StatIdentifier.PoisonDamageDuration: return(poisonDamage.HasRange()); case StatIdentifier.PoisonDamageMin: if (!poisonDamage.HasRange()) { return(false); } description = poisonDamage.ToString(stringReader); return(true); // By default, the stat is not handled. default: return(false); } }
public void ShouldDetermineIfNewChar() { var unit = new D2Unit(); unit.eClass = 0; unit.actNo = 0; unit.StatListNode = new DataPointer(1); var processMemoryReader = new Mock <IProcessMemoryReader>(); var gameMemoryTable = new GameMemoryTable(); var stringReader = new Mock <IStringReader>().Object; var statsList = new D2StatListEx(); statsList.BaseStats = new D2StatArray(); statsList.BaseStats.Address = new DataPointer(); statsList.ListFlags = StatListFlag.HasCompleteStats; statsList.FullStats = new D2StatArray(); statsList.FullStats.Length = 1; statsList.FullStats.Address = new DataPointer(); processMemoryReader .Setup(x => x.Read <D2StatListEx>( It.Is <IntPtr>(p => p.Equals(unit.StatListNode.Address)), It.Is <AddressingMode>(m => m == AddressingMode.Absolute) )) .Returns(statsList); var lvlStat = new D2Stat(); lvlStat.LoStatID = (ushort)StatIdentifier.Level; lvlStat.Value = 1; var xpStat = new D2Stat(); xpStat.LoStatID = (ushort)StatIdentifier.Experience; xpStat.Value = 0; var d2StatArray = new D2Stat[] { lvlStat, xpStat }; processMemoryReader .Setup(x => x.ReadArray <D2Stat>( It.Is <IntPtr>(p => p.Equals(statsList.FullStats.Address.Address)), It.IsAny <int>(), It.Is <AddressingMode>(m => m == AddressingMode.Absolute) )) .Returns <IntPtr, int, AddressingMode>((p, i, m) => i == 0 ? new D2Stat[] { } : d2StatArray); // starting items for amazon var startingItems = new Item[] { new Item { Unit = new D2Unit { eClass = 0x2f } }, new Item { Unit = new D2Unit { eClass = 0x148 } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x211 } }, new Item { Unit = new D2Unit { eClass = 0x212 } }, }; var inventoryReader = new Mock <IInventoryReader>(); inventoryReader .Setup(x => x.EnumerateInventoryForward( It.Is <D2Unit>(p => p.Equals(unit)) )) .Returns(startingItems); // starting skills for amazon var skillData = new Dictionary <D2Skill, D2SkillData>(); skillData.Add(new D2Skill(), new D2SkillData { SkillId = (uint)Skill.UNKNOWN }); skillData.Add(new D2Skill(), new D2SkillData { SkillId = (uint)Skill.THROW }); skillData.Add(new D2Skill(), new D2SkillData { SkillId = (uint)Skill.KICK }); skillData.Add(new D2Skill(), new D2SkillData { SkillId = (uint)Skill.SCROLL_IDENT }); skillData.Add(new D2Skill(), new D2SkillData { SkillId = (uint)Skill.TOME_IDENT }); skillData.Add(new D2Skill(), new D2SkillData { SkillId = (uint)Skill.SCROLL_TP }); skillData.Add(new D2Skill(), new D2SkillData { SkillId = (uint)Skill.TOME_TP }); skillData.Add(new D2Skill(), new D2SkillData { SkillId = (uint)Skill.UNSUMMON }); var skillReader = new Mock <ISkillReader>(); skillReader .Setup(x => x.EnumerateSkills( It.Is <D2Unit>(p => p.Equals(unit)) )) .Returns(skillData.Keys); skillReader .Setup(x => x.ReadSkillData( It.Is <D2Skill>(s => skillData.ContainsKey(s)) )) .Returns <D2Skill>((skill) => skillData[skill]); skillReader .Setup(x => x.GetTotalNumberOfSkillPoints( It.Is <D2Skill>(s => skillData.ContainsKey(s)) )) .Returns(1); // TODO: isnt 1 for all cases. should test other values var unitReader = new UnitReader(processMemoryReader.Object, gameMemoryTable, stringReader, skillReader.Object); Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skillReader.Object)); lvlStat.Value = 2; Assert.AreEqual(false, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skillReader.Object)); lvlStat.Value = 1; Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skillReader.Object)); xpStat.Value = 1; Assert.AreEqual(false, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skillReader.Object)); xpStat.Value = 0; Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skillReader.Object)); inventoryReader .Setup(x => x.EnumerateInventoryForward( It.Is <D2Unit>(p => p.Equals(unit)) )) .Returns(new Item[] { new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x211 } }, new Item { Unit = new D2Unit { eClass = 0x212 } }, new Item { Unit = new D2Unit { eClass = 0x2f } }, new Item { Unit = new D2Unit { eClass = 0x148 } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, }); Assert.AreEqual(false, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skillReader.Object)); inventoryReader .Setup(x => x.EnumerateInventoryForward( It.Is <D2Unit>(p => p.Equals(unit)) )) .Returns(startingItems); Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skillReader.Object)); var skillRaiseSkeleton = new D2Skill(); skillData.Add(skillRaiseSkeleton, new D2SkillData { SkillId = (uint)Skill.RAISE_SKELETON }); Assert.AreEqual(false, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skillReader.Object)); skillData.Remove(skillRaiseSkeleton); Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skillReader.Object)); statsList.FullStats.Length = 0; Assert.AreEqual(false, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skillReader.Object)); statsList.FullStats.Length = 5; Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skillReader.Object)); statsList.FullStats.Length = 1; Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skillReader.Object)); }
private string GetStatPropertyDescription(List <D2Stat> stats, D2Stat stat, D2Unit unit) { if (stat.LoStatID >= ItemStatCost.Length) { return(null); } var statCost = ItemStatCost[stat.LoStatID]; byte printFunction = statCost.DescFunc; byte printPosition = statCost.DescVal; ushort printDescription = statCost.DescStr2; ushort printStringId = statCost.DescStrPos; // Check if the group of the value is used. // Grouping of stats is for stats such as resistances and attribute bonuses. // E.g: "+6 light res, +6 fire res, +6 cold res, +6 psn res" becomes: "+6 All Res" if (statCost.DGrp != 0) { // Check if all stats in the group have the same value. // Only print first in the group if the group has the same values. // If the values in the group different, print them individually. bool lastOfGroup = false; bool groupIsSame = true; foreach (var groupStatCost in ItemStatCost) { if (groupStatCost.DGrp != statCost.DGrp) { continue; } int index = stats.FindIndex(x => x.LoStatID == groupStatCost.StatId); lastOfGroup = groupStatCost.StatId == stat.LoStatID; if (index < 0 || stats[index].Value != stat.Value) { groupIsSame = false; break; } } if (groupIsSame) { // Only print the last of equal groups. if (!lastOfGroup) { return(null); } printFunction = statCost.DGrpFunc; printPosition = statCost.DGrpVal; printStringId = statCost.DGrpStrPos; printDescription = statCost.DGrpStr2; } } // Gets the evaluated stat value, takes care of stats per unit (player) level, // value shifts, etc... int value = EvaluateStat(stat, statCost, unit); int arguments; string format = null; switch (printFunction) { case 0x00: return(null); case 0x01: case 0x06: case 0x0C: if (printPosition == 1) { format = "{0:+#;-#;0} {1}"; } else if (printPosition == 2) { format = "{1} {0:+#;-#;0}"; } else { return(null); } break; case 0x02: case 0x07: if (printPosition == 1) { format = "{0}% {1}"; } else if (printPosition == 2) { format = "{1} {0}%"; } break; case 0x03: case 0x09: if (printPosition == 1) { format = "{0} {1}"; } else if (printPosition == 2) { format = "{1} {0}"; } else { return(GetString(printStringId)); } break; case 0x04: case 0x08: if (printPosition == 1) { format = "+{0}% {1}"; } else if (printPosition == 2) { format = "{1} +{0}%"; } break; case 0x05: case 0x0A: value = (value * 0x64) >> 7; if (printPosition == 1) { format = "{0}% {1}"; } else if (printPosition == 2) { format = "{1} {0}%"; } break; case 0x0B: int quotient = 2500 / value; if (quotient <= 30) { format = GetString(StringConstants.RepairsDurability); format = ConvertCFormatString(format, out arguments); return(string.Format(format, value)); } else { int duration = (quotient + 12) / 25; format = GetString(StringConstants.RepairsDurabilityN); format = ConvertCFormatString(format, out arguments); return(string.Format(format, 1, duration)); } case 0x0D: { var characterId = stat.HiStatID; var characterData = GetCharacterData(characterId); if (characterData == null) { return(null); } if (value == 0) { value = 1; } string allSkills = GetString(characterData.AllSkillsStringId); if (allSkills == null) { return(null); } return(string.Format("+{0} {1}", value, allSkills)); } case 0x0E: { var characterId = (ushort)(stat.HiStatID >> 3); var characterData = GetCharacterData(characterId); if (characterData == null) { return(null); } ushort skillTab = (ushort)(stat.HiStatID & 0x7); if (skillTab > 2) { return(null); } ushort skillTabStringId = characterData.SkillTabStrings[skillTab]; format = GetString(skillTabStringId); format = ConvertCFormatString(format, out arguments); if (arguments != 1) { return(null); } string classRestriction = GetString(characterData.StrClassOnly); return(string.Format(format, value) + " " + classRestriction); } case 0x14: case 0x15: if (printPosition == 1) { format = "-{0}% {1}"; } else if (printPosition == 2) { format = "{1} -{0}%"; } break; case 0x0F: { ushort skillIdentifier = (ushort)(stat.HiStatID >> (byte)globals.ItemSkillShift); uint skillLevel = stat.HiStatID & globals.ItemSkillMask; string skillName = skillReader.GetSkillName(skillIdentifier); if (skillName == null) { return(null); } format = GetString(printStringId); format = ConvertCFormatString(format, out arguments); if (format == null || arguments != 3) { return(null); } // Example: "10% Chance to cast level 3 Charged Bolt when struck". return(string.Format(format, value, skillLevel, skillName)); } case 0x10: { string skillName = skillReader.GetSkillName(stat.HiStatID); if (skillName == null) { return(null); } format = GetString(printStringId); format = ConvertCFormatString(format, out arguments); if (arguments != 2) { return(null); } return(string.Format(format, value, skillName)); } case 0x11: case 0x12: // Increased stat during time of day, this is not actually used on any ingame items, // so I'm gonna spare myself some work and ignore it. :) // Example: "+24% Cold Absorb (Increases during Nighttime)" return(null); case 0x13: format = GetString(printStringId); format = ConvertCFormatString(format, out arguments); if (arguments != 1) { return(null); } return(string.Format(format, stat.Value)); case 0x16: { format = GetString(printStringId); // This is most likely unused in the real game. ushort monsterPropIndex = stat.HiStatID; if (stat.HiStatID >= globals.MonsterPropCount) { monsterPropIndex = 0; } IntPtr monsterTypeAddress = globals.MonsterTypes.Address + monsterPropIndex * 0xC; ushort monsterNameId = reader.ReadUInt16(monsterTypeAddress + 0xA); string monsterName = GetString(monsterNameId); if (printPosition == 1) { return(string.Format("+{0}% {1}: {2}", value, format, monsterName)); } else if (printPosition == 2) { return(string.Format("{1} +{0}%: {2}", value, format, monsterName)); } else { return(null); } } case 0x17: // This is for affixes with bonus stats against specific monsters. // Assumed output: "+10% Attack Rating (versus:|against|vs) Skeletons" // This is actually not used ingame and we thus ignore it. // Note: HiStatID is the monster type id. return(null); case 0x18: { StringBuilder sb = new StringBuilder(); ushort skillIdentifier = (ushort)(stat.HiStatID >> (byte)globals.ItemSkillShift); uint skillLevel = stat.HiStatID & globals.ItemSkillMask; // Example: "Level 3" sb.Append(GetString(StringConstants.ItemSkillLevel)); sb.Append(" "); sb.Append(skillLevel); sb.Append(" "); string skillName = skillReader.GetSkillName(skillIdentifier); if (skillName == null) { return(null); } // Example: "Level 3 Charged Bolt" sb.Append(skillName); sb.Append(" "); // Get skill charges. format = GetString(printStringId); format = ConvertCFormatString(format, out arguments); int maximumCharges = (value >> 8) & 0xFF; int currentCharges = (value & 0x0FF); // Example: "Level 3 Charged Bolt (27/27 Charges)" sb.AppendFormat(format, currentCharges, maximumCharges); return(sb.ToString()); } case 0x19: case 0x1A: // Probably unused ingame, but logic is pretty simple. value *= -1; if (value > 0) { goto case 0x01; } if (printPosition == 1) { format = "{0} {1}"; } else if (printPosition == 2) { format = "{1} {0}"; } else { return(null); } break; case 0x1B: { StringBuilder sb = new StringBuilder(0x100); sb.Append('+'); sb.Append(value); sb.Append(' '); sb.Append(GetString(StringConstants.BonusTo)); sb.Append(' '); string skillName = skillReader.GetSkillName(stat.HiStatID); if (skillName == null) { return(null); } sb.Append(skillName); D2SkillData skillData = skillReader.GetSkillData(stat.HiStatID); if (skillData == null) { return(null); } // TODO: remove the magic 7. (without further checking: 0-6 refers to the character classes) if (skillData.ClassId < 7) { var characterId = (ushort)skillData.ClassId; var characterData = GetCharacterData(characterId); if (characterData != null) { sb.Append(' '); sb.Append(GetString(characterData.StrClassOnly)); } } return(sb.ToString()); } case 0x1C: { // Note: Some unknown things are happening in this case, // but hopefully it should still work. StringBuilder sb = new StringBuilder(0x100); sb.Append('+'); sb.Append(value); sb.Append(' '); sb.Append(GetString(StringConstants.BonusTo)); sb.Append(' '); string skillName = skillReader.GetSkillName(stat.HiStatID); if (skillName == null) { return(null); } sb.Append(skillName); return(sb.ToString()); } default: break; } if (format == null) { return(null); } string description = string.Format(format, value, GetString(printStringId)); if (printFunction >= 0x6 && printFunction < 0xA || printFunction == 0x15) { string extraDescription; if (printDescription == 0x1506) { extraDescription = GetString(0x2B53); } else { extraDescription = GetString(printDescription); } // Example: ... + "(Increases with Character Level)" description += " " + extraDescription; } return(description); }
public void ShouldDetermineIfNewChar() { var unit = new D2Unit(); unit.eClass = 0; unit.actNo = 0; unit.StatListNode = new DataPointer(1); var processMemoryReader = new Mock <IProcessMemoryReader>(); var gameMemoryTable = new GameMemoryTable(); var stringReader = new Mock <IStringReader>().Object; var statsList = new D2StatListEx(); statsList.BaseStats = new D2StatArray(); statsList.BaseStats.Address = new DataPointer(); statsList.ListFlags = StatListFlag.HasCompleteStats; statsList.FullStats = new D2StatArray(); statsList.FullStats.Length = 1; statsList.FullStats.Address = new DataPointer(); processMemoryReader .Setup(x => x.Read <D2StatListEx>( It.Is <IntPtr>(p => p.Equals(unit.StatListNode.Address)) )) .Returns(statsList); var lvlStat = new D2Stat(); lvlStat.LoStatID = (ushort)StatIdentifier.Level; lvlStat.Value = 1; var xpStat = new D2Stat(); xpStat.LoStatID = (ushort)StatIdentifier.Experience; xpStat.Value = 0; var d2StatArray = new D2Stat[] { lvlStat, xpStat }; processMemoryReader .Setup(x => x.ReadArray <D2Stat>( It.Is <IntPtr>(p => p.Equals(statsList.FullStats.Address.Address)), It.IsAny <int>() )) .Returns <IntPtr, int>((p, i) => i == 0 ? new D2Stat[] { } : d2StatArray); // starting items for amazon var startingItems = new Item[] { new Item { Unit = new D2Unit { eClass = 0x2f } }, new Item { Unit = new D2Unit { eClass = 0x148 } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x211 } }, new Item { Unit = new D2Unit { eClass = 0x212 } }, }; var inventoryReader = new Mock <IInventoryReader>(); inventoryReader .Setup(x => x.EnumerateInventoryForward( It.Is <D2Unit>(p => p.Equals(unit)) )) .Returns(startingItems); var skillReader = new Mock <ISkillReader>(); // starting skills for amazon var skills = new List <SkillInfo> { new SkillInfo { Id = (uint)Skill.UNKNOWN, Points = 1 }, new SkillInfo { Id = (uint)Skill.THROW, Points = 1 }, new SkillInfo { Id = (uint)Skill.KICK, Points = 1 }, new SkillInfo { Id = (uint)Skill.SCROLL_IDENT, Points = 1 }, new SkillInfo { Id = (uint)Skill.TOME_IDENT, Points = 1 }, new SkillInfo { Id = (uint)Skill.SCROLL_TP, Points = 1 }, new SkillInfo { Id = (uint)Skill.TOME_TP, Points = 1 }, new SkillInfo { Id = (uint)Skill.UNSUMMON, Points = 1 }, }; var unitReader = new UnitReader(processMemoryReader.Object, gameMemoryTable, stringReader, skillReader.Object); Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skills)); lvlStat.Value = 2; Assert.AreEqual(false, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skills)); lvlStat.Value = 1; Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skills)); xpStat.Value = 1; Assert.AreEqual(false, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skills)); xpStat.Value = 0; Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skills)); inventoryReader .Setup(x => x.EnumerateInventoryForward( It.Is <D2Unit>(p => p.Equals(unit)) )) .Returns(new Item[] { new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x211 } }, new Item { Unit = new D2Unit { eClass = 0x212 } }, new Item { Unit = new D2Unit { eClass = 0x2f } }, new Item { Unit = new D2Unit { eClass = 0x148 } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, new Item { Unit = new D2Unit { eClass = 0x24b } }, }); Assert.AreEqual(false, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skills)); inventoryReader .Setup(x => x.EnumerateInventoryForward( It.Is <D2Unit>(p => p.Equals(unit)) )) .Returns(startingItems); Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skills)); var skillRaiseSkeleton = new SkillInfo { Id = (uint)Skill.RAISE_SKELETON, Points = 0 }; skills.Add(skillRaiseSkeleton); Assert.AreEqual(false, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skills)); skills.Remove(skillRaiseSkeleton); Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skills)); statsList.FullStats.Length = 0; try { Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skills); Assert.Fail(); } catch (Exception e) { Assert.AreEqual("Invalid level", e.Message); } statsList.FullStats.Length = 5; Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skills)); statsList.FullStats.Length = 1; Assert.AreEqual(true, Character.DetermineIfNewChar(unit, unitReader, inventoryReader.Object, skills)); }
public bool TryHandleStat(D2Stat stat, out string description) { description = null; StatIdentifier statId = 0; if (Enum.IsDefined(typeof(StatIdentifier), stat.LoStatID)) { statId = (StatIdentifier)stat.LoStatID; } else { return(false); } switch (statId) { //Handle one and two handed damage. case StatIdentifier.DamageMin: case StatIdentifier.DamageMax: case StatIdentifier.SecondaryDamageMin: case StatIdentifier.SecondaryDamageMax: // Only print once if it's a range. if (HasHandledDamageRange) { return(true); } // Skip two-handed damage if there is a one-handed damage source or if no damage is defined. if (stat.IsOfType(StatIdentifier.SecondaryDamageMin) && !IsDamageMinSecondary) { return(true); } if (stat.IsOfType(StatIdentifier.SecondaryDamageMax) && !IsDamageMaxSecondary) { return(true); } // If not a range, print normally. if (!HasDamageRange) { return(false); } // We also print twice if they are the same. if (DamageMin == DamageMax) { return(false); } HasHandledDamageRange = true; description = FormatSimpleDamage(DamageMin, DamageMax, StringConstants.DamageRange, StringConstants.DamageRange); return(true); // Handle enhanced damage. case StatIdentifier.ItemDamageMinPercent: { if (!HasDamagePercentRange) { return(false); } string enhanced = stringReader.GetString(StringConstants.EnhancedDamage); description = string.Format("+{0}% {1}", stat.Value, enhanced.TrimEnd()); return(true); } case StatIdentifier.ItemDamageMaxPercent: return(HasDamagePercentRange); // Handle fire damage ranges. case StatIdentifier.FireDamageMin: if (!HasFireRange) { return(false); } description = FormatSimpleDamage( FireMinDamage, FireMaxDamage, StringConstants.FireDamageRange, StringConstants.FireDamage); return(true); case StatIdentifier.FireDamageMax: return(HasFireRange); // Handle lightning damage ranges. case StatIdentifier.LightningDamageMin: if (!HasLightningRange) { return(false); } description = FormatSimpleDamage( LightningMinDamage, LightningMaxDamage, StringConstants.LightningDamageRange, StringConstants.LightningDamage); return(true); case StatIdentifier.LightningDamageMax: return(HasLightningRange); // Handle magic damage ranges. case StatIdentifier.MagicDamageMin: if (!HasMagicRange) { return(false); } description = FormatSimpleDamage( MagicMinDamage, MagicMaxDamage, StringConstants.MagicDamageRange, StringConstants.MagicDamage); return(true); case StatIdentifier.MagicDamageMax: return(HasMagicRange); // Handle cold damage ranges. case StatIdentifier.ColdDamageMin: if (!HasColdRange) { return(false); } description = FormatSimpleDamage( ColdMinDamage, ColdMaxDamage, StringConstants.ColdDamageRange, StringConstants.ColdDamage); return(true); case StatIdentifier.ColdDamageMax: return(HasColdRange); // Handle poison damage ranges. case StatIdentifier.PoisonDamageMax: case StatIdentifier.PoisonDamageDuration: return(HasPoisonRange); case StatIdentifier.PoisonDamageMin: { if (!HasPoisonRange) { return(false); } int divisor = PoisonDivisor == 0 ? 1 : PoisonDivisor; int duration = PoisonDuration / divisor; int min = (PoisonMinDamage * duration + 128) / 256; int max = (PoisonMaxDamage * duration + 128) / 256; duration /= 25; if (min == max) { // Example: "6 Poison damage over 2 seconds" int arguments; string format = stringReader.GetString(StringConstants.PoisonOverTimeSame); format = stringReader.ConvertCFormatString(format, out arguments); if (arguments != 2) { return(true); } description = string.Format(format, min, duration).TrimEnd(); } else { // Example: "2-10 Poison damage over 2 seconds" int arguments; string format = stringReader.GetString(StringConstants.PoisonOverTime); format = stringReader.ConvertCFormatString(format, out arguments); if (arguments != 3) { return(true); } description = string.Format(format, min, max, duration).TrimEnd(); } return(true); } // By default, the stat is not handled. default: return(false); } }