Skip to content

CalculateBaseDamage

This is a method that contains all the damage calculation logic and returns the final amount calculated from the base one given the battle's state. Every damages that goes through the damage pipeline ends up calling this to get the final amount of damage to deal with the exception of the target being invulnerable which are any of the following conditions on the target:

  • Its plating is true
  • It has the Shield condition
  • It is a player party member with the Numb condition and the ShockTrooper medal equipped

There is no other damage calculation logic in the pipeline with the only exception being the clamping done on DoDamage after getting the final number. Every other logic involves other matters after the damage has been calculated by this method. Alternatively, actions's logic can perform anything outside of this pipeline.

private int CalculateBaseDamage(MainManager.BattleData? attacker, ref MainManager.BattleData target, int basevalue, bool block, AttackProperty? property, ref bool weaknesshit, ref bool superguarded, DamageOverride[] overrides)

Parameters

All parameters comes directly from DoDamage (the damageammount becomes the basevalue) with some exceptions:

  • block: This may be overriden to false by DoDamage If any of the following are true:
    • The target has the Freeze condition
    • The target has the Sleep condition
    • The target has the Numb condition
    • The target is a player party member with the Berserker medal equipped
    • This is a non super block while flags 15 and 615 are true (chapter 1 started after the combat tutorial while FRAMEONE is active) and overridechallengeblock is false
  • ref weaknesshit: This is meant to initially contain false, the method will set this to true if a Magic or Flip weakness was processed
  • ref superguarded: This is meant to initially contain false, the method will set this to true if the player sucessfully super blocked
  • property: The property of the attack sent by DoDamage (overriden to null is if it was None by DoDamage). If this is Pierce or Flip while the target is a player party member, it is overriden to null at the start of this method which invalidates it

Damage flow

While the attacker is optional (null means no attacker), the target is mandatory. Additionally, the target cannot be part of the same party as the attacker. This means an enemy party member cannot attack another enemy party member and a player party member cannot attack another player party member. This results in the following damage flows available:

  • No attacker to player party member
  • No attacker to enemy party member
  • Player party member to enemy party member
  • Enemy party member to player party member

Each have different implications on the calculations. It is assumed that the target is a player party member if it has the Player tag. Otherwise, it is assumed to be an enemy party member. If the attacker exists, it is assumed to be the opposite party of the target.

basevalue is the damage amount that will ultimately be returned. This method may modify it for various reasons until it reaches the end where it becomes final.

Piercing

There is a concept of piercing which ignores all effects related to enemy party members's defense. Despite the concept of defense having multiple calculation errors, piercing correctly and exhaustively ignores all components of defense.

In order for piercing to apply, the following must all be true:

  • The target is an enemy party member (this is specifically disabled for player party members)
  • The target doesn't have AntiPierce in its weakness (only the Spuder enemy can get it via CheckEvent when applicable)
  • property is Atleast1pierce or Pierce (the former is the same as the latter, but the final clamp will be from 1 instead of 0 for any cases not counting a FrostBite medal counter)

If the above isn't fufilled, piercing doesn't apply.

Calculation pipeline table

The calculation pipeline is very complex and most of it will be illustrated by a table.

Each rows of the table contains an effect to basevalue or other additional effects when applicable. Each row contains 3 columns:

  • Attack direction: The damage flow denoted in party to party notation. Here's the party defintions:
    • Any: For the attacker, it may or may not exist from either party. For the target, it can belong to either party
    • Attacker: The attacker must exist, but can be in either party
    • Player: The attacker or target must be a player party member
    • Enemy: The attacker or target must be an enemy player member
  • Condition: The conditions required for this effect to process. If a list is presented, all conditions must be true unless stated otherwise
  • Damage effect: The effects on basevalue if Condition is true. This may contain other effects than changing basevalue. Anything underlined means it's an effect other than increasing or decreasing meaning it's more significant and its position in the calculation is important

These effects are shown in the exact order they appear.

Attack direction Condition Damage effect
Enemy to player Attacker is a TANGYBUG enemy + 2
Attacker to Any Always processed - attacker.tired
Player to enemy
  • Not in demomode
  • attacker is partypointer[0] (at the front of formation)
+ 1
Player to enemy
  • Not in demomode
  • attacker has the Poison condition
+ amount of attacker's PoisonAttacker medals
Player to enemy
  • Not in demomode
  • attacker.hp <= 4
+ amount of attacker's AttackUp medals
Enemy to player
  • Not in demomode
  • flags 614 and 88 are true (HARDEST is active after chapter 2 ended)
+ 1
Enemy to player Not in demomode + attacker.hardatk (the scaled attack from GetEnemyData)
Enemy to player
  • Not in demomode
  • DoublePainReal medal is equipped
Clamped from 2 to 99
Attacker to any Always processed + attacker.charge
Any to player block is true OR superblockedthisframe hasn't expired yet Decreased by:
  • 1 if it's a non super block
  • 2 + amount of attacker's SuperBlock medals if it's a super block1
Enemy to player
  • Target has Freeze condition
  • No NoIceBreak override AND (first that applies of the 3 below)
Ice thaw2 (after the sub effect is processed, doesn't change basevalue)
-
  • target has a FrostBite medal
  • nonphysical is false
  • attacker.position isn't Underground
A Freeze condition is added as a delayed condition to the attacker AND a counter of the full damage to the attacker is scheduled (see the final results explanation below for details)
-
  • target has a FrostBite medal
  • The counter effect above didn't apply
Divided by 2 floored
- target has no FrostBite medal + 1
Any to Any
  • target has the Numb condition
  • target doesn't have the the DefenseDown condition. NOTE: this is incorrect5
  • property isn't Flip. NOTE: This is incorrect7
  • there is no IgnoreNumb in the overrides
  • piercing doesn't apply
- 1
Any to enemy
  • target.noexpatstat is true (cannot happen because this field is UNUSED)
  • flags 162 is false (we aren't using the B.O.S.S system and we aren't in a Cave Of Trials session)
- 1 (this effect never happens under normal gameplay)
Any to Any property is one of the following:
  • Poison
  • Numb
  • Numb1Turn
  • Sleep
  • Freeze
  • Fire
  • InkOnBlock
  • Ink
  • Sticky
Status infliction logic occurs (see the section below for details), no changes to basevalue
Any to Any property is Flip Flip handling logic (see the section below for details), basevalue may change in various ways
Any to Any property is anything except:
  • NoExceptions OR
  • Flip while target doesn't have the Flipped condition. NOTE: If the enemy has defenseonhit above 0, this is incorrect7
DefaultDamageCalc (see the section below for details) is called with the corresponding values for the parameters where pierce is true if piercing applies (false otherwise or if property is null), with def being target.def. basevalue may decrease due to various defense modifiers
Any to Any
  • target has Magic in its weakness AND
  • property is Magic OR
    • attacker is a player party member while lastskill is Icefall, FrigidCoffin or IceRain skill AND
    • property is one of the following:
      • Poison
      • Numb
      • Numb1Turn
      • Sleep
      • Freeze
      • Fire
      • InkOnBlock
      • Ink
      • Sticky
      • Flip while target has the Flipped condition
      • NoExceptions
+ 1 and If commandsuccess is true (the player suceeded the action command), weaknesshit is set to true
Any to enemy
  • target.battleentity.height is above 0.0
  • target.cantfall is false
  • target.position is Flying
  • target.lockposition is false
  • There are no NoFall overrides
AND (first of the 2 applies)
-
  • target doesn't already have have the Topple conditions
  • target doesn't have the Sleep, Freeze or Numb conditions
  • target has ToppleFirst or ToppleAirOnly in its weakness
The Topple condition is added directly to target.condition array for 1 turn followed by target.battleentity.basestate set to 21 (Woobly)
- The above didn't apply The enemy falls3 (no basevalue changes)
Any to Any
  • target has the DefenseUp condition
  • Either property:
    • is null OR
    • is not Flip (NOTE: this is incorrect7) or NoExceptions while piercing doesn't apply
- 1
Any to Any
  • target has the DefenseDown condition
  • piercing doesn't apply
  • property isn't NoExceptions.
  • At least one of the following is true:
    • target.def is above 0. NOTE: If the property is Flip, this is incorrect if target.def is exactly 18
    • The DefenseUp effect above was just processed. NOTE: This is incorrect5
    • The target is a player party member while HardMode returns true. NOTE: This is inconsistent or incorrect6
+ 1
Attacker to any
  • Attacker has the AttackUp condition
  • property isn't NoExceptions
+ 1
Attacker to any
  • Attacker has the AttackDown condition
  • property isn't NoExceptions
- 1
Any to player The DoublePainReal medal is equipped Multiplied by 1.25 floored, then increased by 1
Player to enemy
  • Attacker has an AntlionJaws medal
  • actionid is 0 or below (this field is UNUSED so it's always 0)
  • currentchoice is Attack
  • Either the target has the DefenseUp condition OR its target.def is above 0
  • Piercing doesn't apply
Decreased by the lowest between the amount of the attacker's AntlionJaws medals and the target's defense which is:
  • target.def if it's above 0
  • 1 if target.def is 0 AND it has the DefenseUp condition
  • 0 otherwise
NOTE: This logic is incorrect4
Any to enemy + 1
Any to Any target has the Sturdy condition - 3
Any to player target has a Reflection condition - amount of target's Reflection medals
Any to Any
  • target doesn't have LimitX10 in its weakness
  • property is Atleast1 or Atleast1pierce
Clamped from 1 to 99
Any to Enemy
  • target has LimitX10 in its weakness
  • property is Atleast1 or Atleast1pierce
- 10
Any to Enemy target has LimitX10 in its weakness
  • If basevalue is <= 1, basevalue is set to 0
  • Otheriwise, basevalue is divided by 10 ceiled

1: This always counts as a super block if superblockedthisframe hasn't expired yet. A super block also causes the following to happen:

  • superblockedthisframe is reset to 3.0 frames
  • superguarded is set to true
  • block is overriden to true

For more information on how super blocks are determined, check GetBlock.

2: This is how ice thawing is done (this is done after the sub effect processed):

  • RemoveCondition is called with the target to remove the Freeze condition
  • BreakIce is called on the target.battleentity
  • target.cantmove is set to 0 if the target is an enemy party member or to 1 if it's a player party member. This allows for the actor to immediately be able to act no matter its party (the enemy party doesn't get their actor turns advanced until the next main turn while the player party have theirs advanced right after enemies).

3: This means the attack happened to a toppled flying enemy or the enemy wasn't topplable and was hit while flying meaning it should be dropped to the ground. This is how this happens:

  • RemoveCondition is called with the target to remove its Topple condition
  • target.battleentity.bobrange and bobspeed are set to 0.0
  • If target.animid (the enemy id) isn't 208, target.battleentity.basestate is set to 0 (Idle). NOTE: This enemy id doesn't exist, the logic was supposed to check for target.battleentity.animid for not being 207 (CursedSkull) which would have allowed it to to remain in its charge animation even when it falls
  • If target.eventonfall is above -1 (it's defined):
    • calleventnext is set to target.eventonfall
    • target.battleentity.basestate is set to 11 (Hurt)
  • Otherwise (target.eventonfall isn't defined):
    • target.battleentity.droproutine is set to a new Drop call on target.battleentity

4: The AntlionJaws medal was supposed to ignore a defense for each instance equipped. However, there are 5 cases in which this doesn't work correctly:

  • The DefenseDown condition is ignored
  • The defense gained as a result of the Numb condition is ignored
  • defenseonhit is ignored on enemies who supports it
  • The DefenseUp condition is ignored UNLESS target.def is 0 where it works correctly. Specifically, this means that if the target's target.def is 1 while having the DefenseUp condition, at most 1 AntLionJaws will count still (even if 2 are equipped)
  • The property is Flip. This will cause both the medal and the 1 defense ignore to apply independently for the same reasons, effectively ignoring the defense that was already ignored befrehand

5: This is incorrect to do because the DefenseDown effect later can still apply which can make it override the numb defense giving +1 erroneous increase to basevalue. For this issue to reproduce, the DefenseDown conditions needs to be fufilled while the numb defense conditions also applies which will effectively count 1 less defense than it should. The defense in the HUD reports the correct number, but the calculations are wrong.

Additionally, if the target has all the DefenseUp, DefenseDown and Numb conditions, now numb defense won't apply when the other 2 cancel each other resulting in numb defense not being applied when it should have been. This results in the same error.

6: There are 2 ways to look at this because the intent here is unclear:

  • Negative defense on the player is allowed due to making it possible via medals and DefenseDown always apply
  • Negative defense shouldn't be allowed in general and medals causes a design issue

The second case is a design bug. The first case isn't consistent because the medals exists no matter if HardMode applies or not, but the DefenseDown effect may or may not apply.

Additionally, this doesn't take into consideration the numb defense even if its logic was correct (that it would apply regardless of DefenseDown's presence or not). It now means that the numb defense may or may not be cancelled correctly depending on the difficulty which is a bug.

7: Flip always ignore at most 1 of target.def, but it also ignores unconditionally other types of defenses on top of this incorrectly:

They are also cumulative: if all 3 are combined, they all get ignored on top of the 1 defense ignored from target.def while only at most 1 was supposed to be ignored across all defenses.

8: This is the infamous issue known as the "def bug". If target.def is EXACTLY 1 (0 or 2+ are accidentally correct) while it has a DefenseDown condition, the only defense left will be ignored, but DefenseDown will still apply. Only one of the two should apply because there's only 1 defense available to ignore. DefenseDown only requires that target.def is above 0.

Status infliction

What happens here depends on the property. These typically attempts to inflict a condition on the target when applicable and most of them implies a resistance check.

Here's an explanation of the columns:

  • property: The property value the row applies to
  • conditions inflicted: The condition to inflict if all the requirements are fufilled
  • Requirements: The requirements that must be fufilled for the condition to be inflicted. All statuses requires block to be false and the target to not have the Sturdy condition. Those checks are implied in this column and won't be mentioned. There is 1 requirement specifically that recurs a lot and will be called with a simplified name:
    • Resistance check: A resistance check must pass for the condition to be inflicted. A resistance is an integer field on the target that can be at most 99 for the condition to possibly inflict. If it's 100 or above, the target is immune and the infliction won't occur. This immunity condition also affects if the StatusMirror medal works. This is how the test occurs:
      • If this is a chompyaction or the attacker is a player party member with a StatusBoost medal, the resistance used for the test is decreased by 15 (guaranteed to inflict if this subtraction makes the resistance land at 0 or below)
      • A random number between 0 and 99 is generated and it must be higher or equal than the target's resistance for the test to pass. Intuitively, if the target isn't immune, it means the percentage to inflict is 100 - resistance with StatusBoost or a chompyaction giving 15% more chances (or a guarantee if the percent number is above 100)
  • Resistance increase: After infliction, it's possible the resistance used with the resistance check gets increased. This column indicates if it will happen, under what conditions it will happen and for how much
  • StatusMirror Infliction scheme: The StatusMirror medal scheme of infliction. This medal only works in an enemy to player attack direction and it requires the resistance used for the resistance check to be below 100 (not being immune). If these conditions are met upon inflictions, the attacker gets inflicted with the same condition. The scheme of the infliction may vary and this column tells how it's inflicted
  • Other effects: Anything the infliction does other than inflicting the condition or giving it to the attacker due to StatusMirror. Please note that with the exception of the Sleep property, RemoveCondition is called with the target to remove its Sleep condition if one of the following is true:
    • The target is an enemy party member. NOTE: This contradicts the requirements for DoDamage to do the same because it does not feature a clause for dealing damages above 0. This means that a an attack that does no damage, but still inflicts can remove Sleep without changing the cantmove which is different than if DoDamage removed it.
    • The target is a player party member and it doesn't have a HeavySleeper medal
property conditions inflicted Requirements Resistance increase StatusMirror Infliction scheme Other effects
Poison Poison for 2 turns Pass a resistance check with target.poisonres None SetCondition is called with the attacker to set the Poison condition for 2 turns The Poison sound is played if it wasn't playing already
Numb or Numb1Turn Numb for 2 turns (1 turn instead if it's a chompyaction or property is Numb1Turn) Pass a resistance check with target.numbres If target is an enemy party member, it is increased by 17. The increase is 22 instead if HardMode returns true AddDelayedCondition is called with the attacker to add the Numb as a delayed condition to it
  • The Numb sound is played if it wasn't playing already
  • target.isnumb is set to true
  • If the target is an enemy party member and its actimmobile is false, target.cantmove is set to 1 (meaning 1 actor turn needs to pass before the target can act again). NOTE: This means a status cleansing action on an enemy party member will still have them loose their actor turn
  • If target.position is Ground and its battleentity.height is above 0.05, target.battleentity.droproutine is set to a new Drop call on target.battleentity
Sleep Sleep for 2 turns (3 turns instead if the target is a player party member while it already had a Sleep condition2) Pass a resistance check with target.sleepres If the target is an enemy party member, target.numbres is increased by 9. The increase is 13 instead if HardMode returns true AddDelayedCondition is called with the attacker to add the Sleep as a delayed condition to it
  • The Sleep sound is played if it wasn't playing already
  • target.isasleep is set to true
  • If the target has the Flipped condition, target.battleentity.animstate is set to 25 (SleepFallen) and if it doesn't have it, it's set to 14 (Sleep)
  • If target.position is Ground and its battleentity.height is above 0.05, target.battleentity.droproutine is set to a new Drop call on target.battleentity
Freeze Freeze for 1 turn if the target is an enemy party member or 2 turns if it's a player party member Pass a resistance check with target.freezeres If the target is an enemy party member:
  • If the corresponding endata of the target's hasiceanim is true and we are either at the GiantLairFridgeInside map or in any maps outside of the GiantLair area, target.freezeres is increased by 70
  • Otherwise, if target.frozenlastturn is true, target.freezeres is increased by 25
  • Otherwise, target.freezeres is increased by 13. The increase is 18 instead if HardMode returns true
AddDelayedCondition is called with the attacker to add the Freeze as a delayed condition to it
  • RemoveCondition is called with the target to remove the Topple condition
  • If the corresponding endata of the target's hasiceanim is true and we are either at the GiantLairFridgeInside map or in any maps outside of the GiantLair area:
    • target.battleentity.inice is set to true
    • target.weakness is set to a new list with one element being HornExtraDamage
  • Freeze is called on target.battleentity
  • If target.position is Ground and its battleentity.height is above 0.05, target.battleentity.droproutine is set to a new Drop call on target.battleentity
Fire Fire for 2 turns CanBeOnFire must returns true1 None SetCondition is called with the attacker to set the Fire condition for 2 turns The Flame sound is played if it wasn't playing already
Ink or InkOnBlock (InkOnBlock doesn't have the block requirement) Inked for 3 turns when block is false (2 when block is true) If the target has the ResistAll medal equipped, a 50% RNG test is performed and it must pass (not applicable if the target doesn't have it equipped) None SetCondition is called with the attacker to set the Inked condition for 2 turns
  • The WaterSplash2 sound is played at 0.7 pitch if it wasn't playing already or it was while its time is higher than 0.25 seconds
  • The InkGet particles plays at the target.battleentity's position + Vector3.up
Sticky Sticky for 3 turns when block is false (2 when block is true) If the target has the ResistAll medal equipped, a 50% RNG test is performed and it must pass (not applicable if the target doesn't have it equipped) None SetCondition is called with the attacker to set the Sticky condition for 2 turns
  • The WaterSplash2 sound is played at 0.7 pitch if it wasn't playing already or it was while its time is higher than 0.25 seconds
  • The StickyGet particles plays at the target.battleentity's position + Vector3.up

1: The method does the following to determine if the target can be on fire:

  • If the target is a player party member, true is returned unless it has the ResistAll medal equipped and a 50% RNG test is failed where false is returned instead
  • If it's a WaspKing or EverlastingKing enemy, true is returned if a 30% RNG test succeed, false otherwise
  • If it's a Krawler, Cape or CursedSkull enemy then it's false if the current map is any in the GiantLair area other than GiantLairFridgeInside. Otherwise, the return is decided with a 50% RNG test
  • If it's a KeyR, KeyL or Tablet enemy, false is returned
  • If none of the cases applies, true is returned

2: This is due to the first of those 3 turns ending immediately making it really + 2 turns in practice which matches all other cases where it's always + 2 turns.

Flip handing

Each rows of the table contains an effect to basevalue or other additional effects when applicable. Each row contains 3 columns:

  • target's party: The party of the target (any for either)
  • Condition: The conditions required for this effect to process. If a list is presented, all conditions must be true unless stated otherwise
  • Effects: The effects on basevalue if Condition is true. This may contain other effects than changing basevalue. Anything underlined means it's an effect other than increasing or decreasing meaning it's more significant and its position in the calculation is important

These effects are shown in the exact order they appear.

target's party Condition Effects
Any target doesn't have the Flipped condition basevalue is decreased by the clamp of target.def - 1 from 0 to 99
Any wealnesshit is set to true + the first of 3 applicable effects below (mutually exclusive)
- target doesn't have ToppleFirst in its weakness
  • A Flipped condition for 1 turn is added directly to target.condition
  • basevalue is clamped from 1 to 99
- target has the Topple condition
  • A Flipped condition for 1 turn is added directly to target.condition
  • basevalue is clamped from 1 to 99
  • RemoveCondition is called with the target to remove its Topple condition
- Neither of the above applied
  • If target.position is Ground, target.battleentity.animstate is set to 21 (Woobly)
  • A Topple condition for 1 turn is added directly to target.condition
Any target has HornExtraDamage in its weakness
  • basevalue is incremented
  • weaknesshit is set to true
Enemy Always occur
  • If target.holditem isn't -1 (it was holding an item), DropItem is called with the target and with additem
  • If target.isdefending is true, it is set to false

DefaultDamageCalc

This is a sub method to CalculateBaseDamage which only gets called for most property values (the exceptions being NoExceptions where it's never called and Flip where it may or may not be called).

It takes the basevalue as ref and despite its name, it only processes optional decreases of the basevalue due to defensive effects.

private void DefaultDamageCalc(MainManager.BattleData target, ref int basevalue, bool pierce, bool blocked, int def)

Parameters:

  • target: The target from CalculateBaseDamage
  • ref basevalue: The basevalue from CalculateBaseDamage
  • pierce: Whether defense pierce applies. This value is computed from CalculateBaseDamage, but overriden to false if target is a player party member
  • blocked: UNUSED (this is block from CalculateBaseDamage, but it is never read in the method)
  • def: target.def from CalculateBaseDamage. (NOTE: this is the base value, not the effective one). Overriden to 0 if pierce is true while target is an enemy party member

Effects

Each rows of the table contains an effect to basevalue or other additional effects when applicable. Each row contains 3 columns:

  • target's party: The party of the target (any for either)
  • Condition: The conditions required for this effect to process. If a list is presented, all conditions must be true unless stated otherwise
  • Damage effect: The effects on basevalue if Condition is true. This may contain other effects than changing basevalue. Anything underlined means it's an effect other than increasing or decreasing meaning it's more significant and its position in the calculation is important

These effects are shown in the exact order they appear.

target's party Condition Damage effect
Any target doesn't have the Flipped condition - def
Player target has the Poison condition - (amount of target's PoisonDefender medals - amount of target's ReversePoison medals)
Player target is playerdata[battle.partypointer[playerdata.lenght - 1]] (the furthest back in the player party formation) - amount of target's BackSupport medals
Player target is playerdata[battle.partypointer[0]] (the front in the player party formation) - amount of target's FrontSupport medals
Player target.hp is <= 4 - 2 * amount of target's DefenseUp medals
Player
  • target has the Sleep condition
  • target has a HeavySleeper medal
Divided by 2 floored
Any - target.defenseonhit

Final steps and results

Before the method ends:

  • caninputcooldown is set to 0.0
  • blockcooldown is set to 0.0

The final result is determined by the following:

  • If a FrostBite medal counter conditions were fufilled:
    • ShowDamageCounter is called with type 0 (damage) with the basevalue (which is now the final amount) as the ammount starting from the world CenterPos of attacker and ending at Vector3.Up. NOTE: this basevalue may be unclamped here which allows to show a negative number
    • The attacker's hp is decreased by basevalue clamped from 1 to its maxhp (meaning this can't be lethal). NOTE: basevalue may be unclamped here which allows it to be negative and heal the attacker instead of dealing damage to it (the outer clamp is only clamping the final hp, NOT the number by which it is decreased)
    • 0 is returned as the final damage value (this is because the calculations are overriden to deal no damages to the target, but they were dealt manually just now to the attacker)
  • Otheriwse, if the attack direction is attacker to player while block is false, basevalue is clamped from 1 to 99
  • Otherwise, basevalue is clamped from 0 to 99