Beispiel #1
0
        /// <summary>
        /// Lists known skills
        /// </summary>
        public override CommandResult Execute(CommandEventArgs commandEventArgs)
        {
            try
            {
                base.Execute(commandEventArgs);
            }
            catch (CommandException ex)
            {
                return(ex.CommandResult);
            }

            EntityAnimate entity = commandEventArgs.Entity;

            List <ISkill> skills = new List <ISkill>();

            foreach (var s in entity.Skills)
            {
                skills.Add(s);
            }

            OutputBuilder result = new OutputBuilder();

            foreach (var s in skills)
            {
                result.Append($"{SkillMap.SkillToFriendlyName(s.GetType())} [{(int)s.SkillLevel}]\n");
            }

            return(CommandResult.Success(result.Output));
        }
Beispiel #2
0
        /// <summary>
        /// Provides an entity's total modified attack rating for a particular weapon
        /// </summary>
        /// <param name="entity">The entity to calculate from</param>
        /// <param name="weapon">The weapon to use when calculating rating</param>
        /// <returns>The total attack rating modified by associated skills</returns>
        /// <remarks>If this is an unarmed attack with no unarmed weapons, provide 'null' for the weapon.
        /// Dual Wielding and Two Handed will presently not be included in modifying the attack rating.
        /// The skill rating is reduced by a factor of 4 before improving attack rating.</remarks>
        /// TODO: This could probably be refactored by putting calculated code inside the entity's ModifiedQualities.AttackRating property.
        public static int GetEntityAttackRatingFor(EntityAnimate entity, ItemWeapon weapon)
        {
            if (entity == null)
            {
                return(GetEntityAttackRating(entity));
            }

            int    baseAttackRating = GetEntityAttackRating(entity);
            double skill;

            if (weapon == null)
            {
                skill = SkillHelper.SkillOfWeapon(entity, WeaponType.Unarmed);
            }
            else
            {
                skill = SkillHelper.SkillOfWeapon(entity, weapon.WeaponType);
            }

            if (skill < 0)
            {
                skill = 0;
            }

            return(baseAttackRating + (int)(skill / 4));
        }
Beispiel #3
0
        /// <summary>
        /// Process a command from network input
        /// </summary>
        /// <param name="input">The string input to parse</param>
        /// <param name="entity">The entity invoking the command</param>
        /// <returns>The result of the command</returns>
        public static CommandResult ProcessCommand(string input, EntityAnimate entity, List <EntityState> validStates)
        {
            if (input == "\n")
            {
                return(CommandResult.Success(""));
            }

            var commandInput = ParseCommand(input);
            var argument     = ParseArgument(input);
            var command      = _commandMap.Where(kvp => kvp.Key.StartsWith(commandInput, StringComparison.InvariantCultureIgnoreCase) && !kvp.Value.RequiresFullMatch ||
                                                 kvp.Key.Equals(commandInput, StringComparison.InvariantCultureIgnoreCase) && kvp.Value.RequiresFullMatch)
                               .FirstOrDefault()
                               .Value;

            if (command == null)
            {
                return(CommandResult.NotFound(commandInput));
            }

            // TODO: Move command failure messages based on state into properties and change the failure message here to use it from the command itself
            if (!command.ValidStates.Intersect(validStates).Any())
            {
                return(CommandResult.Failure("State commands not found."));
            }

            var args = new CommandEventArgs(argument, entity, null);

            return(command.Execute(args));
        }
Beispiel #4
0
        /// <summary>
        /// Process network input based on state
        /// </summary>
        /// <param name="input">The input data</param>
        /// <param name="entity">The data's corresponding entity</param>
        /// <returns></returns>
        public CommandResult ProcessInput(string input, EntityAnimate entity)
        {
            if (entity == null)
            {
                return(CommandResult.NullEntity());
            }

            switch (State)
            {
            case EntityState.NameSelection:
                CommandResult result = HandleNameSelection(input, entity);

                if (result.ResultCode == ResultCode.SUCCESS)
                {
                    var startingRoom = DataAccess.GetAll <World>(CacheType.Instance)[0].StartingLocation;
                    State = EntityState.Active;

                    if (startingRoom != null)
                    {
                        var args = new CommandEventArgs(startingRoom.ToString(), entity, null);
                        entity.IOHandler?.QueueRawOutput(result.ResultMessage);
                        result = new Goto().Execute(args);
                    }
                }

                return(result);

            case EntityState.Active:
                return(CommandService.ProcessCommand(input, entity, new List <EntityState> {
                    EntityState.Active
                }));

            case EntityState.Combat:
                var processCommands = CommandService.ProcessCommand(input, entity, new List <EntityState> {
                    EntityState.Combat
                });

                if (processCommands.ResultCode == ResultCode.FAIL)
                {
                    return(HandleCombatInput(input, entity));
                }
                else
                {
                    return(processCommands);
                }

            default:
                return(CommandResult.Failure("Invalid entity state."));
            }
        }
Beispiel #5
0
        public override CommandResult Execute(CommandEventArgs commandEventArgs)
        {
            try
            {
                base.Execute(commandEventArgs);
            }
            catch (StateException)
            {
                if (commandEventArgs.Entity.State == EntityState.Combat)
                {
                    return(CommandResult.Failure("You are already in combat!"));
                }
                else
                {
                    var errMessage = $"Unexpected entity state: {commandEventArgs.Entity.State}.";
                    Logger.Error(nameof(CommandService), nameof(Kill), errMessage);
                    return(CommandResult.Failure(errMessage));
                }
            }
            catch (CommandException ex)
            {
                return(ex.CommandResult);
            }

            var           room     = commandEventArgs.Entity.GetInstanceParentRoom();
            var           entities = room.Animates.GetAllEntities <EntityAnimate>();
            EntityAnimate entity   = commandEventArgs.Entity;
            uint?         targetID = null;

            // Find first matching target
            targetID = Parse.MatchOnEntityNameByOrder(commandEventArgs.Argument, entities.Cast <IEntity>().ToList())?.Instance;

            if (targetID == entity?.Instance)
            {
                return(CommandResult.Failure("You cannot kill yourself."));
            }

            if (targetID != null)
            {
                CombatHandler.Enter(commandEventArgs.Entity.Instance, targetID, false);
            }
            else
            {
                return(CommandResult.Failure("There is no such target."));
            }

            var targetName = DataAccess.Get <EntityAnimate>(targetID, CacheType.Instance).ShortDescription;

            return(CommandResult.Success($"You attack {targetName}!"));
        }
Beispiel #6
0
        /// <summary>
        /// Handles NameSelection state
        /// </summary>
        /// <returns>The result of the handled input</returns>
        private CommandResult HandleNameSelection(string input, EntityAnimate entity)
        {
            if (InputValidation.ValidPlayerName(input))
            {
                entity.Name = input;
                var output = new OutputBuilder($"Welcome to HedronMUD, {entity.Name}!");

                return(CommandResult.Success(output.Output));
            }
            else
            {
                var output = new OutputBuilder("Please enter a valid player name containing only letters and between 3-12 characters.");

                output.Append("\nPlease enter your player name: ");
                return(CommandResult.InvalidSyntax(output.Output));
            }
        }
Beispiel #7
0
        /// <summary>
        /// Retrieves the skill level of an entity's weapon
        /// </summary>
        /// <param name="entity">The entity to check the skill of</param>
        /// <param name="weaponType">The weapon type to be checked</param>
        /// <returns>The associated weapon's skill level, or Constants.SKILL_UNLEARNED if not found.</returns>
        public static double SkillOfWeapon(EntityAnimate entity, WeaponType weaponType)
        {
            ISkill skill;

            switch (weaponType)
            {
            case WeaponType.Axe:
                skill = entity.Skills.FirstOrDefault(s => s.GetType() == typeof(Axe));
                break;

            case WeaponType.Bow:
                skill = entity.Skills.FirstOrDefault(s => s.GetType() == typeof(Bow));
                break;

            case WeaponType.Dagger:
                skill = entity.Skills.FirstOrDefault(s => s.GetType() == typeof(Dagger));
                break;

            case WeaponType.Mace:
                skill = entity.Skills.FirstOrDefault(s => s.GetType() == typeof(Mace));
                break;

            case WeaponType.Staff:
                skill = entity.Skills.FirstOrDefault(s => s.GetType() == typeof(Staff));
                break;

            case WeaponType.Sword:
                skill = entity.Skills.FirstOrDefault(s => s.GetType() == typeof(Sword));
                break;

            case WeaponType.Unarmed:
                skill = entity.Skills.FirstOrDefault(s => s.GetType() == typeof(Unarmed));
                break;

            case WeaponType.Wand:
                skill = entity.Skills.FirstOrDefault(s => s.GetType() == typeof(Wand));
                break;

            default:
                return(Constants.SKILL_UNLEARNED);
            }

            return(skill?.SkillLevel ?? Constants.SKILL_UNLEARNED);
        }
Beispiel #8
0
        /// <summary>
        /// Learns a new skill
        /// </summary>
        public override CommandResult Execute(CommandEventArgs commandEventArgs)
        {
            try
            {
                base.Execute(commandEventArgs);
            }
            catch (CommandException ex)
            {
                return(ex.CommandResult);
            }

            EntityAnimate entity    = commandEventArgs.Entity;
            string        skillName = SkillMap.SkillToFriendlyName(typeof(Dodge));

            if (commandEventArgs.Argument.ToLower() != skillName)
            {
                return(CommandResult.InvalidSyntax(nameof(Learn), new List <string> {
                    skillName
                }));
            }

            return(CommandResult.Success(entity.ImproveSkill(skillName, 0).ImprovedMessage));
        }
Beispiel #9
0
 /// <summary>
 /// Provides an entity's base attack rating excluding weapons and skills
 /// </summary>
 /// <param name="entity">The entity to calculate attack rating for</param>
 /// <returns>The entity's base attack rating, or 0 if the entity was null.</returns>
 public static int GetEntityAttackRating(EntityAnimate entity) => entity != null ? (int)entity.ModifiedQualities.AttackRating + ((int)entity.ModifiedAttributes.Finesse * 2) : 0;
Beispiel #10
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));
        }
Beispiel #11
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);
            }
        }
Beispiel #12
0
 /// <summary>
 /// Default constructor
 /// </summary>
 /// <param name="argument">The argument for the command</param>
 /// <param name="entity">The entity executing the command</param>
 /// <param name="privilegeOverride">The privilege level to use as an override for execution</param>
 public CommandEventArgs(string argument, EntityAnimate entity, PrivilegeLevel?privilegeOverride)
 {
     Argument          = argument;
     Entity            = entity;
     PrivilegeOverride = privilegeOverride;
 }
Beispiel #13
0
 /// <summary>
 /// Provides a long description for a living entity
 /// </summary>
 /// <param name="entity">The entity to provide a description for</param>
 /// <returns>The description</returns>
 public static string LongFor(EntityAnimate entity)
 {
     return(entity.LongDescription);
 }
Beispiel #14
0
 /// <summary>
 /// Provides a short description for a living entity
 /// </summary>
 /// <param name="entity">The entity to provide a description for</param>
 /// <returns>The description</returns>
 public static string ShortFor(EntityAnimate entity)
 {
     return(entity.ShortDescription);
 }
Beispiel #15
0
 /// <summary>
 /// Handles Combat state
 /// </summary>
 /// <returns>The result of the handled input</returns>
 private CommandResult HandleCombatInput(string input, EntityAnimate entity)
 {
     return(CommandResult.Failure("You must stop fighting first."));
 }