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())); }
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; }
/// <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); }