protected override void OnCalculate(InteractionParamHolder damageInfo, InteractionResult curResult, ContactResultInfo curContactResult)
            {
                //The unscaled damage the Attacker dealt to the Victim
                //This will be the Payback damage dealt from a Defensive Action if one that deals damage has been performed
                int unscaledAttackerDamage = StepResult.AttackerResult.TotalDamage;

                int           damageDealt   = unscaledAttackerDamage;
                PaybackHolder paybackHolder = StepContactResultInfo.Paybackholder;

                //Get the damage done to the Attacker, factoring in Weaknesses/Resistances
                ElementDamageResultHolder attackerElementDamage = GetElementalDamage(StepResult.AttackerResult.Entity,
                                                                                     paybackHolder.Element, damageDealt);

                //Get Payback damage - Payback damage is calculated after everything else
                //However, it does NOT factor in Double Pain or Last Stand, hence why we use the unscaled Victim damage
                int paybackDamage = paybackHolder.GetPaybackDamage(attackerElementDamage.Damage);

                //If Constant Payback, the constant damage value will be returned as the Payback damage
                //Therefore, update the final Payback damage value to factor in Weaknesses/Resistances using the constant Payback damage
                //Ex. This causes an enemy with a +1 Weakness to Fire to be dealt 2 damage instead of 1 for a Constant 1 Fire Payback
                if (paybackHolder.PaybackType == PaybackTypes.Constant)
                {
                    paybackDamage = GetElementalDamage(StepResult.AttackerResult.Entity, paybackHolder.Element, paybackDamage).Damage;
                }

                //Fill out the rest of the Attacker information since we have it
                //Payback damage is always direct, piercing, and guaranteed to hit
                StepResult.AttackerResult.TotalDamage       = paybackDamage;
                StepResult.AttackerResult.DamageElement     = paybackHolder.Element;
                StepResult.AttackerResult.ElementResult     = attackerElementDamage.InteractionResult;
                StepResult.AttackerResult.ContactType       = ContactTypes.TopDirect;
                StepResult.AttackerResult.Piercing          = true;
                StepResult.AttackerResult.StatusesInflicted = paybackHolder.StatusesInflicted;
                StepResult.AttackerResult.Hit = true;
            }
        /// <summary>
        /// Removes a Payback on the BattleEntity.
        /// </summary>
        /// <param name="paybackHolder">The PaybackHolder to remove.</param>
        public void RemovePayback(PaybackHolder paybackHolder)
        {
            bool removed = Paybacks.Remove(paybackHolder);

            if (removed == true)
            {
                Debug.Log($"Successfully removed {paybackHolder.Element} Payback of type {paybackHolder.PaybackType} on {Entity.Name}!");
            }
        }
        /// <summary>
        /// Gets the total Payback a BattleEntity has by combining all of the current Paybacks affecting the BattleEntity.
        /// </summary>
        /// <param name="additionalPaybacks">Any additional PaybackHolders to factor in. This is used when determining the total contact result.</param>
        /// <returns>A PaybackHolder with the combined properties of all the Paybacks the BattleEntity has</returns>
        public PaybackHolder GetPayback(params PaybackHolder[] additionalPaybacks)
        {
            //Gather all the entity's Paybacks in the list
            List <PaybackHolder> allPaybacks = new List <PaybackHolder>(Paybacks);

            //Add any additional Paybacks
            if (additionalPaybacks != null && additionalPaybacks.Length > 0)
            {
                allPaybacks.AddRange(additionalPaybacks);
            }

            //Initialize default values
            PaybackTypes totalType    = PaybackTypes.Constant;
            Elements     totalElement = Elements.Normal;
            int          totalDamage  = 0;
            List <StatusChanceHolder> totalStatuses = new List <StatusChanceHolder>();

            //Go through all the Paybacks and add them up
            for (int i = 0; i < allPaybacks.Count; i++)
            {
                PaybackHolder paybackHolder = allPaybacks[i];

                //If there's a Half or Full Payback, upgrade the current one from Half to Full if it's currently Half
                if (paybackHolder.PaybackType != PaybackTypes.Constant)
                {
                    //If there are at least two Half Paybacks, upgrade it to Full
                    if (totalType == PaybackTypes.Half && paybackHolder.PaybackType == PaybackTypes.Half)
                    {
                        totalType = PaybackTypes.Full;
                    }
                    else if (totalType != PaybackTypes.Full)
                    {
                        totalType = paybackHolder.PaybackType;
                    }
                }

                //Check for a higher priority Element
                if (paybackHolder.Element > totalElement)
                {
                    totalElement = paybackHolder.Element;
                }

                //Add up all the damage
                totalDamage += paybackHolder.Damage;

                //Add in all the StatusEffects - note that StatusEffects with the same StatusType will increase the chance of
                //that StatusEffect being inflicted, as the first one may not succeed in being inflicted depending on the BattleEntity
                if (paybackHolder.StatusesInflicted != null && paybackHolder.StatusesInflicted.Length > 0)
                {
                    totalStatuses.AddRange(paybackHolder.StatusesInflicted);
                }
            }

            //Return the final Payback
            return(new PaybackHolder(totalType, totalElement, totalDamage, totalStatuses.ToArray()));
        }
Exemple #4
0
        public PaybackStatus(int duration, PaybackHolder paybackHolder)
        {
            StatusType = StatusTypes.Payback;
            Alignment  = StatusAlignments.Positive;

            StatusIcon = new CroppedTexture2D(AssetManager.Instance.LoadRawTexture2D($"{ContentGlobals.UIRoot}/Battle/BattleGFX.png"),
                                              new Rectangle(555, 107, 38, 46));

            Duration = duration;

            AfflictedMessage = "Direct attacks will be\ncountered!";

            Paybackholder = paybackHolder;
        }
Exemple #5
0
        /// <summary>
        /// Gets the result of a ContactType on a set of PhysicalAttributes.
        /// </summary>
        /// <param name="attackerPhysAttributes">The attacker's set of PhysicalAttributes.</param>
        /// <param name="contactType">The ContactType performed.</param>
        /// <param name="contactProperty">The ContactProperty of the contact.</param>
        /// <param name="victimPaybacks">The victim's set of Paybacks to test against.</param>
        /// <param name="attackerContactExceptions">The attacker's contact exceptions; the set PhysicalAttributes to ignore.</param>
        /// <returns>A ContactResultInfo of the interaction.</returns>
        public static ContactResultInfo GetContactResult(IList <PhysicalAttributes> attackerPhysAttributes, ContactTypes contactType, ContactProperties contactProperty, IList <PaybackHolder> victimPaybacks, params PhysicalAttributes[] attackerContactExceptions)
        {
            //Return the default value
            if (victimPaybacks == null || victimPaybacks.Count == 0)
            {
                return(ContactResultInfo.Default);
            }

            /*0. Initialize a list of Paybacks, called PaybackList
             * 1. Go through all of the Victim's Paybacks
             *   2. Check if the Payback's PaybackContacts contains the ContactType of the attack
             *      3a. If so, check if the Attacker has any ContactExceptions for the Payback's PhysAttribute
             *         4a. If so, ignore it and continue
             *         4b. If not, check if the Payback covers any of the attack's ContactProperties
             *            5a. If so, check if the Attacker has the same PhysAttribute as the Payback's
             *               6a. If so, examine the SamePhysAttrResult and go to 7a
             *               6b. If not, examine the PaybackContactResult and go to 7a
             *                  7a. If the ContactResult is a Failure, return that Payback value
             *                  7b. If the ContactResult is a Success, ignore it and continue
             *                  7c. If the ContactResult is a PartialSuccess, add it to PaybackList and continue
             *         4c. If not, ignore it and continue
             *      3b. If not, continue */

            //The Paybacks that will be combined
            List <PaybackHolder> combinedPaybacks = new List <PaybackHolder>();

            //Look through the Paybacks
            for (int i = 0; i < victimPaybacks.Count; i++)
            {
                PaybackHolder payback = victimPaybacks[i];

                //Check if the Payback covers this ContactType
                if (payback.PaybackContacts != null && payback.PaybackContacts.Contains(contactType) == true)
                {
                    //If there are contact exceptions for this PhysicalAttribute, ignore this Payback
                    if (attackerContactExceptions.Contains(payback.PhysAttribute) == true)
                    {
                        continue;
                    }

                    //Check if the Payback covers the ContactProperty
                    if (payback.ContactProperties != null && payback.ContactProperties.Contains(contactProperty) == false)
                    {
                        continue;
                    }

                    ContactResult contactResult = payback.PaybackContactResult;

                    //Check if the Attacker has the PhysicalAttribute the Payback is associated with, and adjust the ContactResult if so
                    if (attackerPhysAttributes.Contains(payback.PhysAttribute) == true)
                    {
                        contactResult = payback.SamePhysAttrResult;
                    }

                    //If a Failure, use this Payback
                    if (contactResult == ContactResult.Failure)
                    {
                        return(new ContactResultInfo(payback, contactResult));
                    }
                    //If a PartialSuccess, add it to the list
                    else if (contactResult == ContactResult.PartialSuccess)
                    {
                        combinedPaybacks.Add(payback);
                    }
                }
            }

            //Combine all the Paybacks in the list
            PaybackHolder finalPayback = PaybackHolder.CombinePaybacks(combinedPaybacks);

            return(new ContactResultInfo(finalPayback, finalPayback.PaybackContactResult));
        }
        public static InteractionResult GetDamageInteractionOld(InteractionParamHolder interactionParam)
        {
            InteractionResult finalInteractionResult = new InteractionResult();

            BattleEntity attacker    = interactionParam.Attacker;
            BattleEntity victim      = interactionParam.Victim;
            ContactTypes contactType = interactionParam.ContactType;
            Elements     element     = interactionParam.DamagingElement;

            StatusChanceHolder[] statuses = interactionParam.Statuses;
            int  damage   = interactionParam.Damage;
            bool piercing = interactionParam.Piercing;

            //Get contact results
            ContactResultInfo contactResultInfo = victim.EntityProperties.GetContactResult(attacker, contactType);
            ContactResult     contactResult     = contactResultInfo.ContactResult;

            //Retrieve an overridden type of Elemental damage to inflict based on the Victim's PhysicalAttributes
            //(Ex. The Ice Power Badge only deals Ice damage to Fiery entities)
            ElementOverrideHolder newElement = attacker.EntityProperties.GetTotalElementOverride(victim);

            if (newElement.Element != Elements.Invalid)
            {
                //Add the number of element overrides to the damage if the element used already exists as an override and the victim has a Weakness
                //to the Element. This allows Badges such as Ice Power to deal more damage if used in conjunction with attacks
                //that deal the same type of damage (Ex. Ice Power and Ice Smash deal 2 additional damage total rather than 1).
                //If any new knowledge is discovered to improve this, this will be changed
                //Ice Power is the only Badge of its kind across the first two PM games that does anything like this
                if (element == newElement.Element && victim.EntityProperties.HasWeakness(element) == true)
                {
                    damage += newElement.OverrideCount;
                }

                element = newElement.Element;
            }

            /*Get the total damage dealt to the Victim. The amount of Full or Half Payback damage dealt to the Attacker
             * uses the resulting damage value from this because Payback uses the total damage that would be dealt to the Victim.
             * This occurs before factoring in elemental resistances/weaknesses from the Attacker*/
            ElementDamageResultHolder victimElementDamage = GetElementalDamage(victim, element, damage);

            int unscaledVictimDamage = victimElementDamage.Damage;

            //Subtract damage reduction (P-Up, D-Down and P-Down, D-Up Badges)
            unscaledVictimDamage -= victim.BattleStats.DamageReduction;

            //Check if the attack hit. If not, then don't consider defensive actions
            bool attackHit = interactionParam.CantMiss == true ? true : attacker.AttemptHitEntity(victim);

            //Defense added from Damage Dodge Badges upon a successful Guard
            int damageDodgeDefense = 0;

            //Defensive actions take priority. If the attack didn't hit, don't check for defensive actions
            BattleGlobals.DefensiveActionHolder?victimDefenseData = null;
            if (attackHit == true)
            {
                victimDefenseData = victim.GetDefensiveActionResult(unscaledVictimDamage, statuses, interactionParam.DamageEffect);
            }

            if (victimDefenseData.HasValue == true)
            {
                unscaledVictimDamage = victimDefenseData.Value.Damage;
                statuses             = victimDefenseData.Value.Statuses;
                //If the Defensive action dealt damage and the contact was direct
                //the Defensive action has caused a Failure for the Attacker (Ex. Superguarding)
                if ((contactType == ContactTypes.TopDirect || contactType == ContactTypes.SideDirect) && victimDefenseData.Value.ElementHolder.HasValue == true)
                {
                    contactResult = ContactResult.Failure;
                }

                //Factor in the additional Guard defense for all DefensiveActions (for now, at least)
                damageDodgeDefense = victim.GetEquippedBadgeCount(BadgeGlobals.BadgeTypes.DamageDodge);
            }

            //Subtract Defense on non-piercing damage
            if (piercing == false)
            {
                int totalDefense = victim.BattleStats.TotalDefense + damageDodgeDefense;
                unscaledVictimDamage -= totalDefense;
            }

            int scaledVictimDamage = unscaledVictimDamage;

            //Factor in Double Pain for the Victim
            scaledVictimDamage *= (1 + victim.GetEquippedBadgeCount(BadgeGlobals.BadgeTypes.DoublePain));

            //Factor in Last Stand for the Victim, if the Victim is in Danger or Peril
            if (victim.IsInDanger == true)
            {
                //PM rounds down, whereas TTYD rounds up. We're going with the latter
                //TTYD always ceilings the value (Ex. 3.2 turns to 4)
                int lastStandDivider = (1 + victim.GetEquippedBadgeCount(BadgeGlobals.BadgeTypes.LastStand));
                scaledVictimDamage = (int)Math.Ceiling(scaledVictimDamage / (float)lastStandDivider);
            }

            /*If the Victim is Invincible, ignore all damage and Status Effects
             * If the Attacker is Invincible, ignore all Payback damage and Status Effects
             *
             * It won't ignore the Payback's effects automatically; that has to be done manually by adding
             * contact exceptions or something else*/

            //Clamp Victim damage
            scaledVictimDamage = UtilityGlobals.Clamp(scaledVictimDamage, BattleGlobals.MinDamage, BattleGlobals.MaxDamage);

            #region Victim Damage Dealt

            //Calculating damage dealt to the Victim
            if (contactResult == ContactResult.Success || contactResult == ContactResult.PartialSuccess)
            {
                //Get the Status Effects to inflict on the Victim
                StatusChanceHolder[] victimInflictedStatuses = GetFilteredInflictedStatuses(victim, statuses);

                //Check if the Victim is Invincible. If so, ignore all damage and Status Effects
                if (victim.EntityProperties.GetAdditionalProperty <bool>(AdditionalProperty.Invincible) == true)
                {
                    scaledVictimDamage = 0;
                    victimElementDamage.InteractionResult = ElementInteractionResult.Damage;
                    victimInflictedStatuses = null;
                }

                finalInteractionResult.VictimResult = new InteractionHolder(victim, scaledVictimDamage, element,
                                                                            victimElementDamage.InteractionResult, contactType, piercing, victimInflictedStatuses, attackHit, DamageEffects.None);
            }

            #endregion

            #region Attacker Damage Dealt

            //Calculating damage dealt to the Attacker
            if (contactResult == ContactResult.Failure || contactResult == ContactResult.PartialSuccess)
            {
                //The damage the Attacker dealt to the Victim
                int           damageDealt   = unscaledVictimDamage;
                PaybackHolder paybackHolder = contactResultInfo.Paybackholder;

                //Override the PaybackHolder with a Defensive Action's results, if any
                if (victimDefenseData.HasValue == true && victimDefenseData.Value.ElementHolder.HasValue == true)
                {
                    damageDealt   = victimDefenseData.Value.ElementHolder.Value.Damage;
                    paybackHolder = new PaybackHolder(PaybackTypes.Constant, victimDefenseData.Value.ElementHolder.Value.Element, damageDealt);
                }

                //Get the damage done to the Attacker, factoring in Weaknesses/Resistances
                ElementDamageResultHolder attackerElementDamage = GetElementalDamage(attacker, paybackHolder.Element, damageDealt);

                //Get Payback damage - Payback damage is calculated after everything else, including Constant Payback.
                //However, it does NOT factor in Double Pain or any sort of Defense modifiers.
                int paybackDamage = paybackHolder.GetPaybackDamage(attackerElementDamage.Damage);

                //If Constant Payback, update the damage value to use the element
                if (paybackHolder.PaybackType == PaybackTypes.Constant)
                {
                    paybackDamage = GetElementalDamage(attacker, paybackHolder.Element, paybackDamage).Damage;
                }

                //Clamp Attacker damage
                attackerElementDamage.Damage = UtilityGlobals.Clamp(paybackDamage, BattleGlobals.MinDamage, BattleGlobals.MaxDamage);

                //Get the Status Effects to inflict
                StatusChanceHolder[] attackerInflictedStatuses = GetFilteredInflictedStatuses(attacker, paybackHolder.StatusesInflicted);

                //Check if the Attacker is Invincible. If so, ignore all damage and Status Effects
                if (attacker.EntityProperties.GetAdditionalProperty <bool>(AdditionalProperty.Invincible) == true)
                {
                    attackerElementDamage.Damage            = 0;
                    attackerElementDamage.InteractionResult = ElementInteractionResult.Damage;
                    attackerInflictedStatuses = null;
                }

                finalInteractionResult.AttackerResult = new InteractionHolder(attacker, attackerElementDamage.Damage, paybackHolder.Element,
                                                                              attackerElementDamage.InteractionResult, ContactTypes.None, true, attackerInflictedStatuses, true, DamageEffects.None);
            }

            #endregion

            return(finalInteractionResult);
        }
        /// <summary>
        /// Adds Payback to the BattleEntity, causing it to deal damage to direct attackers.
        /// </summary>
        /// <param name="paybackHolder">The PaybackHolder to add.</param>
        public void AddPayback(PaybackHolder paybackHolder)
        {
            Debug.Log($"Added {paybackHolder.Element} Payback of type {paybackHolder.PaybackType} to {Entity.Name}!");

            Paybacks.Add(paybackHolder);
        }