Esempio n. 1
0
        /// <summary>
        /// Execute an attack round
        /// </summary>
        /// <param name="actor">the person attacking</param>
        /// <param name="target">the person defending</param>
        public static bool ExecuteRound(IMobile actor, IMobile target)
        {
            //Stagger clearance comes before ending a fight
            if (actor.Stagger > 0)
            {
                actor.Stagger -= 1;
                //Send a ui update
                actor.WriteTo(null);
                return(true);
            }

            if (!actor.IsFighting())
            {
                return(false);
            }

            if (actor == target)
            {
                target = null;
            }

            IFightingArt attack = actor.LastAttack;

            var rand = new Random();

            //If we lack an attack or we're on the tail end of the attack just find a new one and start it
            if (attack == null || !actor.Executing)
            {
                IFightingArtCombination myCombo = actor.LastCombo;
                if (myCombo == null)
                {
                    var   weights  = GetUsageWeights(actor, target);
                    ulong distance = 0;

                    if (target != null)
                    {
                        distance = 1;
                    }

                    var validCombos = actor.Combos.Where(combo => combo.IsValid(actor, target, distance));

                    if (validCombos.Count() == 0)
                    {
                        myCombo = actor.Combos.OrderByDescending(combo => weights[combo.SituationalUsage] * rand.NextDouble()).FirstOrDefault();
                    }
                    else
                    {
                        myCombo = validCombos.OrderByDescending(combo => weights[combo.SituationalUsage] * rand.NextDouble()).FirstOrDefault();
                    }
                }

                //uhh k we need to use a fake combo logic to get a random attack
                if (myCombo == null)
                {
                    var attacks = TemplateCache.GetAll <IFightingArt>(true);

                    attack = attacks.Where(atk => atk.IsValid(actor, target, (ulong)Math.Abs(actor.Balance))).OrderBy(atk => Guid.NewGuid()).FirstOrDefault();
                }
                else
                {
                    attack = myCombo.GetNext(actor.LastAttack);
                }

                if (attack == null)
                {
                    //we have no valid attacks, so we're tired
                    actor.Stagger += 10;
                    actor.Sleep(1); //recover stamina
                    actor.WriteTo(null);
                    return(true);
                }

                actor.Stagger    = attack.Setup;
                actor.Sturdy     = attack.Armor;
                actor.LastCombo  = myCombo;
                actor.LastAttack = attack;
                actor.Executing  = true;

                if (actor.Stagger > 0)
                {
                    actor.Stagger -= 1;

                    //Send a ui update
                    actor.WriteTo(null);
                    return(true);
                }
                //else we just run right into the combo if there's no setup
            }

            //execute the attack
            actor.Executing = false;

            //basics
            actor.Stagger    = attack.Recovery;
            actor.LastAttack = attack;
            actor.LastCombo  = null;
            actor.Balance    = attack.DistanceChange;

            //numbers damage
            if (attack.Health.Actor > 0)
            {
                actor.Harm(attack.Health.Actor);
            }

            if (attack.Stamina.Actor > 0)
            {
                actor.Exhaust(attack.Stamina.Actor);
            }

            Tuple <string, string, string> messaging = null;

            if (target != null)
            {
                target.Balance = -1 * attack.DistanceChange;

                var targetReadiness = ReadinessState.Offensive; //attacking is default
                var targetDirection = AnatomyAim.Mid;           //mid is default

                if (target.LastAttack != null)
                {
                    targetReadiness = target.LastAttack.Readiness;
                    targetDirection = target.LastAttack.Aim;
                }

                //TODO: for now we're just doing flat chances for avoidance states since we have no stats to base anything on
                var    avoided         = false;
                var    blocked         = false;
                double impact          = attack.Impact;
                double damage          = attack.Health.Victim;
                double staminaDrain    = attack.Stamina.Victim;
                var    aimDifferential = Math.Abs(attack.Aim - targetDirection);

                //no blocking if it's not an attack
                if (attack.Readiness != ReadinessState.Offensive || attack.Health.Victim > 0)
                {
                    switch (targetReadiness)
                    {
                    case ReadinessState.Circle:     //circle is dodge essentially
                        avoided = rand.Next(0, 100) <= Math.Max(1, Math.Abs(target.Balance)) / (Math.Max(1, aimDifferential + target.Stagger) * 4) * 100;
                        break;

                    case ReadinessState.Block:
                        blocked = rand.Next(0, 100) <= Math.Max(1, Math.Abs(target.Balance)) / (Math.Max(1, aimDifferential + target.Stagger) * 2) * 100;

                        if (blocked)
                        {
                            impact       *= .25;
                            damage       *= .50;
                            staminaDrain *= .50;
                        }
                        ;
                        break;

                    case ReadinessState.Deflect:
                        blocked = rand.Next(0, 100) <= Math.Max(1, Math.Abs(target.Balance)) / (Math.Max(1, aimDifferential + target.Stagger) * 2) * 100;

                        if (blocked)
                        {
                            impact       *= .50;
                            damage       *= .25;
                            staminaDrain *= .25;
                        }
                        break;

                    case ReadinessState.Redirect:
                        avoided = rand.Next(0, 100) <= Math.Max(1, Math.Abs(target.Balance)) / (Math.Max(1, aimDifferential + target.Stagger) * 20) * 100;
                        break;

                    case ReadinessState.Offensive:
                        //Clash mechanics, only works if the target is mid-execution
                        if (target.LastAttack != null && aimDifferential == 0 && target.Executing)
                        {
                            var impactDifference = target.LastAttack.Impact + target.LastAttack.Armor - attack.Impact;

                            if (impactDifference > 0)
                            {
                                blocked       = true;
                                impact        = 0;
                                damage       /= impactDifference;
                                staminaDrain *= .75;
                            }
                        }
                        break;
                    }
                }

                messaging = GetOutputStrings(attack, target, avoided || blocked, targetReadiness);

                if (!avoided)
                {
                    var victStagger = target.Sturdy - (int)Math.Truncate(impact);

                    //Affect the victim, sturdy/armor absorbs stagger impact and the remainder gets added to victim stagger
                    target.Sturdy   = Math.Max(0, victStagger);
                    target.Stagger += Math.Abs(Math.Min(0, victStagger));

                    if (damage > 0)
                    {
                        target.Harm((int)Math.Truncate(damage));
                    }

                    if (staminaDrain > 0)
                    {
                        target.Exhaust((int)Math.Truncate(staminaDrain));
                    }

                    //affect qualities
                    if (attack.QualityValue != 0 && !string.IsNullOrWhiteSpace(attack.ResultQuality))
                    {
                        target.SetQuality(attack.QualityValue, attack.ResultQuality, attack.AdditiveQuality);
                    }
                }
            }
            else
            {
                messaging = GetOutputStrings(attack, target, false, ReadinessState.Offensive);
            }

            var msg = new Message(new LexicalParagraph(messaging.Item1))
            {
                ToTarget = new LexicalParagraph[] { new LexicalParagraph(messaging.Item2) },
                ToOrigin = new LexicalParagraph[] { new LexicalParagraph(messaging.Item3) }
            };


            msg.ExecuteMessaging(actor, null, target, actor.CurrentLocation.CurrentContainer, null, true);

            return(true);
        }