Ejemplo n.º 1
0
        /// <summary>
        /// Calculates the entity's maximum possible number of attacks
        /// </summary>
        /// <param name="entity">The entity to calculate attacks for</param>
        /// <returns>The maximum number of possible attacks</returns>
        public static int CalcEntityMaxNumAttacks(EntityAnimate entity)
        {
            List <ItemWeapon> weapons = entity.GetItemsEquippedAt(ItemSlot.OneHandedWeapon, ItemSlot.TwoHandedWeapon).Cast <ItemWeapon>().ToList();

            // The total base attack rating to start with
            int attackRating = GetEntityAttackRating(entity);

            // Calculate skill bonuses
            if (weapons.Count > 1)
            {
                // if dual wielding, take the better of the two weapons' skill ratings as the bonus, and half the dual rating of the dual wield skill
                int highestAttackRating = 0;

                foreach (var w in weapons)
                {
                    double skill = SkillHelper.SkillOfWeapon(entity, w.WeaponType);
                    if (skill > highestAttackRating)
                    {
                        highestAttackRating = (int)skill;
                    }
                }

                attackRating += highestAttackRating;

                var dualWield = entity.Skills.FirstOrDefault(s => s.GetType() == typeof(DualWield));
                if (dualWield != null)
                {
                    attackRating += (int)dualWield.SkillLevel / 2;
                }
            }
            else if (weapons.Count == 1)
            {
                // If single wielding, take the weapon's skill rating as the bonus, and if applicable, half the two-handed rating
                var    weapon = weapons[0];
                double skill  = SkillHelper.SkillOfWeapon(entity, weapon.WeaponType);

                if (skill > 0)
                {
                    attackRating += (int)skill;
                }

                if (weapon.Slot == ItemSlot.TwoHandedWeapon)
                {
                    var twoHanded = entity.Skills.FirstOrDefault(s => s.GetType() == typeof(TwoHanded));
                    if (twoHanded != null)
                    {
                        attackRating += (int)twoHanded.SkillLevel / 2;
                    }
                }
            }
            else
            {
                // Take the unarmed skill rating as the bonus
                double skill = SkillHelper.SkillOfWeapon(entity, WeaponType.Unarmed);

                // TODO: Add a skill that allows for a similar effect as two-handed or dual wield for unarmed multiple attacks
                if (skill > 0)
                {
                    attackRating += (int)skill;
                }
            }

            // The total possible number of attacks, minimum 1
            return((int)Math.Ceiling((double)attackRating / MULT_ATTACK_THRESHOLD));
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Process an entity's auto attack
        /// </summary>
        /// <param name="numAttacks">The number of attacks to process</param>
        /// <param name="source">The attacking entity</param>
        /// <param name="target">The target entity</param>
        public static void ProcessEntityAutoAttack(EntityAnimate source, EntityAnimate target)
        {
            if (source == null || target == null)
            {
                return;
            }

            var    rand                  = World.Random;
            var    attackRating          = 0;
            var    weapons               = source.GetItemsEquippedAt(ItemSlot.OneHandedWeapon, ItemSlot.TwoHandedWeapon).Cast <ItemWeapon>().ToList();
            var    skillResults          = new List <ImproveSkillResult>();
            string supportSkillToImprove = "";

            // Calculate the number of attacks
            var numAttacks       = CombatHelper.CalcEntityMaxNumAttacks(source);
            var attackChanceList = new List <double>();

            // Protect unknown circumstances
            if (numAttacks < 1)
            {
                Logger.Info(nameof(CombatHandler), nameof(ProcessEntityAutoAttack), $"Number of attacks for {source.Name}: ID {source.Prototype} was less than 1.");
                string sourceShort = source.ShortDescription ?? "";
                string targetShort = target.ShortDescription.FirstLetterToUpperCaseOrConvertNullToEmptyString();

                source.IOHandler?.QueueRawOutput($"You struggle to attack {targetShort}, but can't!");
                target.IOHandler?.QueueRawOutput($"{sourceShort} struggles to attack you, but can't!");
                return;
            }

            // Get support skill for later improvement
            if (source.GetType() == typeof(Player))
            {
                if (weapons.Count == 0)
                {
                    // Unarmed
                    supportSkillToImprove = SkillMap.SkillToFriendlyName(typeof(Unarmed));
                }
                else if (weapons.Count == 1)
                {
                    // Two handed?
                    if (weapons[0].Slot == ItemSlot.TwoHandedWeapon)
                    {
                        supportSkillToImprove = SkillMap.SkillToFriendlyName(typeof(TwoHanded));
                    }
                    else
                    {
                        supportSkillToImprove = SkillMap.SkillToFriendlyName(typeof(OneHanded));
                    }
                }
                else
                {
                    // Dual wielding
                    supportSkillToImprove = SkillMap.SkillToFriendlyName(typeof(DualWield));
                }
            }

            // Build attack chance list
            for (int i = 0; i < numAttacks; i++)
            {
                attackChanceList.Add(CombatHelper.CalculateAttackChance(attackRating, i));
            }

            // Iterate attack chance list and process attacks
            for (int i = 0; i < attackChanceList.Count; i++)
            {
                int  damage = 0;
                bool didHit = false;

                // Improve support skills
                if (source.GetType() == typeof(Player))
                {
                    skillResults.Add(source.ImproveSkill(supportSkillToImprove, attackChanceList[i]));
                }

                // Process the attack if it succeeds and handle skill improvement
                if (rand.NextDouble() <= attackChanceList[i])
                {
                    didHit = true;

                    if (weapons.Count == 0)
                    {
                        // Unarmed damage
                        damage = (source.Tier.Level * 2) + (int)source.ModifiedAttributes.Might * 3 / 5;
                        damage = rand.Next((int)Math.Floor(damage * 0.9), (int)Math.Ceiling(damage * 1.1));
                        if (source.GetType() == typeof(Player))
                        {
                            skillResults.Add(source.ImproveSkill(SkillMap.SkillToFriendlyName(typeof(Unarmed)), 1));
                        }
                    }
                    else if (weapons.Count == 1)
                    {
                        // Single weapon damage
                        if (weapons[0].Slot == ItemSlot.TwoHandedWeapon)
                        {
                            damage = rand.Next(weapons[0].MinDamage, weapons[0].MaxDamage + 1) + ((int)source.ModifiedAttributes.Might / 3);
                        }
                        else
                        {
                            damage = rand.Next(weapons[0].MinDamage, weapons[0].MaxDamage + 1) + ((int)source.ModifiedAttributes.Might / 5);
                        }

                        if (source.GetType() == typeof(Player))
                        {
                            skillResults.Add(source.ImproveSkill(SkillMap.WeaponTypeToSkillName(weapons[0].WeaponType), 1));
                        }
                    }
                    else
                    {
                        // Dual wield; alternate weapons for each attack
                        int weaponIndex = i % 2;
                        damage = rand.Next(weapons[weaponIndex].MinDamage, weapons[0].MaxDamage + 1) + ((int)source.ModifiedAttributes.Might / 5);
                        if (source.GetType() == typeof(Player))
                        {
                            skillResults.Add(source.ImproveSkill(SkillMap.WeaponTypeToSkillName(weapons[weaponIndex].WeaponType), 1));
                        }
                    }
                }

                if (didHit)
                {
                    if (damage < 1)
                    {
                        damage = 1;
                    }

                    var crit = rand.NextDouble() * 100 <= source.ModifiedQualities.CriticalHit;

                    if (crit)
                    {
                        damage = (int)(damage + (damage * source.ModifiedQualities.CriticalDamage / 100));
                    }

                    var(hitPoints, died) = target.ModifyCurrentHealth(0 - damage, true);

                    source.IOHandler?.QueueRawOutput($"You{(crit ? " critically" : "")} hit {target.ShortDescription} for {damage} damage{(crit ? "!" : ".")} ({target.CurrentHitPoints}/{target.ModifiedPools.HitPoints})");
                    target.IOHandler?.QueueRawOutput($"{source.ShortDescription}{(crit ? " critically" : "")} hits you for {damage}{(crit ? "!" : ".")}");

                    // Handle target death
                    if (died)
                    {
                        Exit(target.Instance);

                        if (target.GetType() == typeof(Mob))
                        {
                            DataAccess.Remove <Mob>(target.Instance, CacheType.Instance);
                        }

                        if (target.GetType() == typeof(Player))
                        {
                            target.IOHandler?.QueueRawOutput("You have died!");

                            var args = new Commands.CommandEventArgs(
                                DataAccess.GetAll <World>(CacheType.Instance)?[0].Instance.ToString(),
                                target,
                                null);

                            new Goto().Execute(args);
                        }

                        source.IOHandler?.QueueRawOutput($"You have slain {target.ShortDescription}!");

                        if (target.Currency.HasAnyValue())
                        {
                            source.Currency += target.Currency;
                            source.IOHandler?.QueueRawOutput($"You gain {target.Currency}.");
                        }

                        break;
                    }
                }
            }

            // Handle skill improvement messages
            // TODO: Add configuration options to allow for suppressions skill improvement messages.
            foreach (var result in skillResults)
            {
                source.IOHandler?.QueueRawOutput(result.ImprovedMessage);
            }
        }