Example #1
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);
            }
        }