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 aMagic
orFlip
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 wasNone
by DoDamage). If this isPierce
orFlip
while thetarget
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 theSpuder
enemy can get it via CheckEvent when applicable) - property is
Atleast1pierce
orPierce
(the former is the same as the latter, but the final clamp will be from 1 instead of 0 for any cases not counting aFrostBite
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 |
---|---|---|
Any to player | block is true OR superblockedthisframe hasn't expired yet |
Decreased by11:
|
Any to Any | property is Raw |
basevalue is immediately returned with a clamping that depends if blocking happened:
|
Enemy to player | Attacker is a TANGYBUG enemy |
+ 2 |
Attacker to Any | Always processed | - attacker.tired |
Player to enemy |
|
+ 1 |
Player to enemy |
|
+ amount of playerdata[currentturn] 's PoisonAttacker medals9 |
Player to enemy |
|
+ amount of playerdata[currentturn] 's AttackUp medals9 |
Enemy to player |
|
+ 1 |
Enemy to player | Not in demomode |
+ attacker.hardatk (the scaled attack from GetEnemyData) |
Enemy to player |
|
Clamped from 2 to 99 |
Attacker to any | Always processed | + attacker.charge |
Enemy to player |
|
Ice thaw2 (after the sub effect is processed, doesn't change basevalue) |
- | 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) | |
- |
|
Divided by 2 floored |
- | target has no FrostBite medal |
+ 1 |
Any to Any |
|
- 1 |
Any to enemy |
|
- 1 (this effect never happens under normal gameplay) |
Any to Any | property is one of the following:
|
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:
|
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 | + 1 and If commandsuccess is true (the player suceeded the action command), weaknesshit is set to true |
|
Any to enemy | AND (first of the 2 applies) | |
- | 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 |
|
- 1 |
Any to Any |
|
+ 1 |
Attacker to any |
|
+ 1 |
Attacker to any |
|
- 1 |
Enemy to player | The DoublePainReal medal is equipped |
Multiplied by 1.25 floored, then increased by 1 |
Player to enemy |
|
Increased by the lowest between the amount of the attacker's AntlionJaws medals and the target's defense which is:
|
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 |
|
Clamped from 1 to 99 |
Any to Enemy |
|
- 10 |
Any to Enemy | target has LimitX10 in its weakness |
|
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). NOTE: This logic is incorrect for enemy party members because if they have a moves higher than 1, they will loose all but 1 actor turn they should have available. The correct logic is the same than what AdvanceTurnEntity does which is settingcantmove
to -moves
+ 1.
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
andbobspeed
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
- 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 1AntLionJaws
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:
- Numb defense
- DefenseUp condition
- defenseonhit on supported enemies when they are
isdefending
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.
9: These incorrectly refers to the currentturn
or the playerdata[currentturn]
when it should actually be the attacker. This distinction only matters in practive when using the IceBeemerang action because it is the only one where the attacker and the currenturn
may be different while still using the regular damage pipeline as opposed to GetMultiDamage. That being said, this issue could manifest itself in any action where the user isn't the attacker and the damage pipeline is invoked as usual.
The overall effects are that the damage bonuses (with the exception of PoisonAttacker
) never checks the actual attacker of this damage call. It means whether or not they apply depends entirely on who used the skill on their actor turn. For example, if the user of IceBeemerang
is Bee
, but Moth
is in front, neither will get the front bonus, but if Moth
was the user, both members (even Bee
) would get it. Neither scenarios are correct. The LastAttack
bonuses works the same way.
However, the PoisonAttacker
bonus case is affected in a much more complex fashion: the check to apply the bonus is correct, but not its application. The actual attacker still needs to have the Poison condition for the bonus to apply, but IF it applies, it's by the amount of playerdata[currentturn]
's amount of PoisonAttacker
equipped, NOT the attacker. It means that it would be possible to create scenarios when using IceBeemerang
that leads to unexpected bonus applications. Here are the possible combinations:
IceBeemerang
is used by someone who hasPoisonAttacker
: The bonus applies to both player party members as long as they have Poison even if the other member doesn't have anyPoisonAttacker
so if the other member doesn't have any, but they havePoison
, the bonus is incorrectly applied (and confusingly, this scenario will even allow the bonus to apply on this attacker even if the user of the move doesn't havePoison
)IceBeemerang
is used by someone who doesn't havePoisonAttacker
, but the other player party member does: The bonus never applies for either player party member, even for the one that hasPoisonAttacker
and regardless of whether or not this member has Poison so it incorrectly doesn't apply
10: This incorrectly excludes IceBeemerang so this action cannot benefit from the Magic
weakness bonus
11: Due to the DoublePainReal
clamp effect later, player blocking is effectively ignored or has its impact greatly reduced because the player blocking effect is located BEFORE the clamp instead of AFTER
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 theplayerdata[currentturn]
is a player party member with aStatusBoost
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). NOTE: TheStatusBoost
clause checks the amount equipped on whoever is taking their actor turn, NOT the attacker which can be different if the action used is IceBeemerang and the property isFreeze
- 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 achompyaction
giving 15% more chances (or a guarantee if the percent number is above 100)
- If this is a
- 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
- 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: TheStatusMirror
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 theSleep
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
- 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
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 |
|
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 |
|
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:
|
AddDelayedCondition is called with the attacker to add the Freeze as a delayed condition to it |
|
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 |
|
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 |
|
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
orEverlastingKing
enemy, true is returned if a 30% RNG test succeed, false otherwise - If it's a
Krawler
,Cape
orCursedSkull
enemy then it's false if the current map is any in theGiantLair
area other thanGiantLairFridgeInside
. Otherwise, the return is decided with a 50% RNG test - If it's a
KeyR
,KeyL
orTablet
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 |
|
- | target has the Topple condition |
|
- | Neither of the above applied | |
Any | target has HornExtraDamage in its weakness |
|
Enemy | Always occur |
|
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 ifpierce
is true whiletarget
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 | Divided by 2 floored | |
Any |
|
- target.defenseonhit |
Final steps and results¶
Before the method ends:
caninputcooldown
is set to 0.0blockcooldown
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 itsmaxhp
(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