Beispiel #1
0
        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);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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);
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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());
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        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));
        }
Beispiel #9
0
        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);
        }
Beispiel #10
0
        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));
        }
Beispiel #11
0
        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);
            }
        }