Fix fixed point damage calculation off-by-1s (#5775)
Co-authored-by: sbird <sbird@no.tld>
This commit is contained in:
parent
a92350fb53
commit
7744298788
6 changed files with 172 additions and 137 deletions
|
@ -12,7 +12,8 @@ typedef u32 uq4_12_t;
|
|||
|
||||
// Converts a number to Q4.12 fixed-point format
|
||||
#define Q_4_12(n) ((q4_12_t)((n) * 4096))
|
||||
#define UQ_4_12(n) ((uq4_12_t)((n) * 4096))
|
||||
#define UQ_4_12(n) ((uq4_12_t)((n) * 4096 + 0.5))
|
||||
#define UQ_4_12_FLOORED(n) ((uq4_12_t)((n) * 4096))
|
||||
|
||||
// Converts a number to Q24.8 fixed-point format
|
||||
#define Q_24_8(n) ((s32)((n) << 8))
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#define COMMON_DATA __attribute__((section("common_data")))
|
||||
#define UNUSED __attribute__((unused))
|
||||
|
||||
#define ARM_FUNC __attribute__((target("arm")))
|
||||
|
||||
#if MODERN
|
||||
#define NOINLINE __attribute__((noinline))
|
||||
#else
|
||||
|
|
|
@ -151,7 +151,7 @@ u16 GetNonDynamaxHP(u32 battler)
|
|||
return gBattleMons[battler].hp;
|
||||
else
|
||||
{
|
||||
u16 mult = UQ_4_12(1.0/1.5); // placeholder
|
||||
u16 mult = UQ_4_12_FLOORED(1.0/1.5); // placeholder
|
||||
u16 hp = UQ_4_12_TO_INT((gBattleMons[battler].hp * mult) + UQ_4_12_ROUND);
|
||||
return hp;
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ u16 GetNonDynamaxMaxHP(u32 battler)
|
|||
return gBattleMons[battler].maxHP;
|
||||
else
|
||||
{
|
||||
u16 mult = UQ_4_12(1.0/1.5); // placeholder
|
||||
u16 mult = UQ_4_12_FLOORED(1.0/1.5); // placeholder
|
||||
u16 maxHP = UQ_4_12_TO_INT((gBattleMons[battler].maxHP * mult) + UQ_4_12_ROUND);
|
||||
return maxHP;
|
||||
}
|
||||
|
@ -202,7 +202,7 @@ void UndoDynamax(u32 battler)
|
|||
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
|
||||
{
|
||||
struct Pokemon *mon = (side == B_SIDE_PLAYER) ? &gPlayerParty[monId] : &gEnemyParty[monId];
|
||||
u16 mult = UQ_4_12(1.0/1.5); // placeholder
|
||||
u16 mult = UQ_4_12_FLOORED(1.0/1.5); // placeholder
|
||||
gBattleMons[battler].hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * mult + 1) + UQ_4_12_ROUND); // round up
|
||||
SetMonData(mon, MON_DATA_HP, &gBattleMons[battler].hp);
|
||||
CalculateMonStats(mon);
|
||||
|
|
|
@ -62,6 +62,8 @@ static u32 GetFlingPowerFromItemId(u32 itemId);
|
|||
static void SetRandomMultiHitCounter();
|
||||
static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item);
|
||||
static bool32 CanBeInfinitelyConfused(u32 battler);
|
||||
ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12(u32 percent);
|
||||
ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12_Floored(u32 percent);
|
||||
|
||||
extern const u8 *const gBattlescriptsForRunningByItem[];
|
||||
extern const u8 *const gBattlescriptsForUsingItem[];
|
||||
|
@ -757,113 +759,18 @@ static const u8 sHoldEffectToType[][2] =
|
|||
{HOLD_EFFECT_FAIRY_POWER, TYPE_FAIRY},
|
||||
};
|
||||
|
||||
// percent in UQ_4_12 format
|
||||
static const uq4_12_t sPercentToModifier[] =
|
||||
{
|
||||
UQ_4_12(0.00), // 0
|
||||
UQ_4_12(0.01), // 1
|
||||
UQ_4_12(0.02), // 2
|
||||
UQ_4_12(0.03), // 3
|
||||
UQ_4_12(0.04), // 4
|
||||
UQ_4_12(0.05), // 5
|
||||
UQ_4_12(0.06), // 6
|
||||
UQ_4_12(0.07), // 7
|
||||
UQ_4_12(0.08), // 8
|
||||
UQ_4_12(0.09), // 9
|
||||
UQ_4_12(0.10), // 10
|
||||
UQ_4_12(0.11), // 11
|
||||
UQ_4_12(0.12), // 12
|
||||
UQ_4_12(0.13), // 13
|
||||
UQ_4_12(0.14), // 14
|
||||
UQ_4_12(0.15), // 15
|
||||
UQ_4_12(0.16), // 16
|
||||
UQ_4_12(0.17), // 17
|
||||
UQ_4_12(0.18), // 18
|
||||
UQ_4_12(0.19), // 19
|
||||
UQ_4_12(0.20), // 20
|
||||
UQ_4_12(0.21), // 21
|
||||
UQ_4_12(0.22), // 22
|
||||
UQ_4_12(0.23), // 23
|
||||
UQ_4_12(0.24), // 24
|
||||
UQ_4_12(0.25), // 25
|
||||
UQ_4_12(0.26), // 26
|
||||
UQ_4_12(0.27), // 27
|
||||
UQ_4_12(0.28), // 28
|
||||
UQ_4_12(0.29), // 29
|
||||
UQ_4_12(0.30), // 30
|
||||
UQ_4_12(0.31), // 31
|
||||
UQ_4_12(0.32), // 32
|
||||
UQ_4_12(0.33), // 33
|
||||
UQ_4_12(0.34), // 34
|
||||
UQ_4_12(0.35), // 35
|
||||
UQ_4_12(0.36), // 36
|
||||
UQ_4_12(0.37), // 37
|
||||
UQ_4_12(0.38), // 38
|
||||
UQ_4_12(0.39), // 39
|
||||
UQ_4_12(0.40), // 40
|
||||
UQ_4_12(0.41), // 41
|
||||
UQ_4_12(0.42), // 42
|
||||
UQ_4_12(0.43), // 43
|
||||
UQ_4_12(0.44), // 44
|
||||
UQ_4_12(0.45), // 45
|
||||
UQ_4_12(0.46), // 46
|
||||
UQ_4_12(0.47), // 47
|
||||
UQ_4_12(0.48), // 48
|
||||
UQ_4_12(0.49), // 49
|
||||
UQ_4_12(0.50), // 50
|
||||
UQ_4_12(0.51), // 51
|
||||
UQ_4_12(0.52), // 52
|
||||
UQ_4_12(0.53), // 53
|
||||
UQ_4_12(0.54), // 54
|
||||
UQ_4_12(0.55), // 55
|
||||
UQ_4_12(0.56), // 56
|
||||
UQ_4_12(0.57), // 57
|
||||
UQ_4_12(0.58), // 58
|
||||
UQ_4_12(0.59), // 59
|
||||
UQ_4_12(0.60), // 60
|
||||
UQ_4_12(0.61), // 61
|
||||
UQ_4_12(0.62), // 62
|
||||
UQ_4_12(0.63), // 63
|
||||
UQ_4_12(0.64), // 64
|
||||
UQ_4_12(0.65), // 65
|
||||
UQ_4_12(0.66), // 66
|
||||
UQ_4_12(0.67), // 67
|
||||
UQ_4_12(0.68), // 68
|
||||
UQ_4_12(0.69), // 69
|
||||
UQ_4_12(0.70), // 70
|
||||
UQ_4_12(0.71), // 71
|
||||
UQ_4_12(0.72), // 72
|
||||
UQ_4_12(0.73), // 73
|
||||
UQ_4_12(0.74), // 74
|
||||
UQ_4_12(0.75), // 75
|
||||
UQ_4_12(0.76), // 76
|
||||
UQ_4_12(0.77), // 77
|
||||
UQ_4_12(0.78), // 78
|
||||
UQ_4_12(0.79), // 79
|
||||
UQ_4_12(0.80), // 80
|
||||
UQ_4_12(0.81), // 81
|
||||
UQ_4_12(0.82), // 82
|
||||
UQ_4_12(0.83), // 83
|
||||
UQ_4_12(0.84), // 84
|
||||
UQ_4_12(0.85), // 85
|
||||
UQ_4_12(0.86), // 86
|
||||
UQ_4_12(0.87), // 87
|
||||
UQ_4_12(0.88), // 88
|
||||
UQ_4_12(0.89), // 89
|
||||
UQ_4_12(0.90), // 90
|
||||
UQ_4_12(0.91), // 91
|
||||
UQ_4_12(0.92), // 92
|
||||
UQ_4_12(0.93), // 93
|
||||
UQ_4_12(0.94), // 94
|
||||
UQ_4_12(0.95), // 95
|
||||
UQ_4_12(0.96), // 96
|
||||
UQ_4_12(0.97), // 97
|
||||
UQ_4_12(0.98), // 98
|
||||
UQ_4_12(0.99), // 99
|
||||
UQ_4_12(1.00), // 100
|
||||
};
|
||||
|
||||
// code
|
||||
|
||||
ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12(u32 percent)
|
||||
{
|
||||
return (4096 * percent + 50) / 100;
|
||||
}
|
||||
|
||||
ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12_Floored(u32 percent)
|
||||
{
|
||||
return (4096 * percent) / 100;
|
||||
}
|
||||
|
||||
u8 GetBattlerForBattleScript(u8 caseId)
|
||||
{
|
||||
u8 ret = 0;
|
||||
|
@ -4136,7 +4043,7 @@ static inline u8 GetBattlerSideFaintCounter(u32 battler)
|
|||
// Supreme Overlord adds a x0.1 damage boost for each fainted ally.
|
||||
static inline uq4_12_t GetSupremeOverlordModifier(u32 battler)
|
||||
{
|
||||
return UQ_4_12(1.0) + (UQ_4_12(0.1) * gBattleStruct->supremeOverlordCounter[battler]);
|
||||
return UQ_4_12(1.0) + (PercentToUQ4_12(gBattleStruct->supremeOverlordCounter[battler] * 10));
|
||||
}
|
||||
|
||||
static inline bool32 HadMoreThanHalfHpNowDoesnt(u32 battler)
|
||||
|
@ -9302,7 +9209,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageCalculationData *
|
|||
if (gProtectStructs[battlerAtk].helpingHand)
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(1.5));
|
||||
if (gSpecialStatuses[battlerAtk].gemBoost)
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(1.0) + sPercentToModifier[gSpecialStatuses[battlerAtk].gemParam]);
|
||||
modifier = uq4_12_multiply(modifier, uq4_12_add(UQ_4_12(1.0), PercentToUQ4_12(gSpecialStatuses[battlerAtk].gemParam)));
|
||||
if (gStatuses3[battlerAtk] & STATUS3_CHARGED_UP && moveType == TYPE_ELECTRIC)
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(2.0));
|
||||
if (gStatuses3[battlerAtk] & STATUS3_ME_FIRST)
|
||||
|
@ -9491,18 +9398,18 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageCalculationData *
|
|||
if (holdEffectParamAtk > 100)
|
||||
holdEffectParamAtk = 100;
|
||||
|
||||
holdEffectModifier = UQ_4_12(1.0) + sPercentToModifier[holdEffectParamAtk];
|
||||
holdEffectModifier = uq4_12_add(UQ_4_12(1.0), PercentToUQ4_12(holdEffectParamAtk));
|
||||
|
||||
// attacker's hold effect
|
||||
switch (holdEffectAtk)
|
||||
{
|
||||
case HOLD_EFFECT_MUSCLE_BAND:
|
||||
if (IS_MOVE_PHYSICAL(move))
|
||||
modifier = uq4_12_multiply(modifier, holdEffectModifier);
|
||||
modifier = uq4_12_multiply(modifier, uq4_12_add(UQ_4_12(1.0), PercentToUQ4_12_Floored(holdEffectParamAtk)));
|
||||
break;
|
||||
case HOLD_EFFECT_WISE_GLASSES:
|
||||
if (IS_MOVE_SPECIAL(move))
|
||||
modifier = uq4_12_multiply(modifier, holdEffectModifier);
|
||||
modifier = uq4_12_multiply(modifier, uq4_12_add(UQ_4_12(1.0), PercentToUQ4_12_Floored(holdEffectParamAtk)));
|
||||
break;
|
||||
case HOLD_EFFECT_LUSTROUS_ORB:
|
||||
if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_PALKIA && (moveType == TYPE_WATER || moveType == TYPE_DRAGON))
|
||||
|
@ -9768,11 +9675,11 @@ static inline u32 CalcAttackStat(struct DamageCalculationData *damageCalcData, u
|
|||
break;
|
||||
case ABILITY_ORICHALCUM_PULSE:
|
||||
if ((weather & B_WEATHER_SUN) && WEATHER_HAS_EFFECT && IS_MOVE_PHYSICAL(move))
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(1.33));
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(1.3333));
|
||||
break;
|
||||
case ABILITY_HADRON_ENGINE:
|
||||
if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && IS_MOVE_SPECIAL(move))
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(1.33));
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(1.3333));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -10234,19 +10141,23 @@ static inline uq4_12_t GetDefenderPartnerAbilitiesModifier(u32 battlerPartnerDef
|
|||
|
||||
static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEffectivenessModifier, u32 holdEffectAtk)
|
||||
{
|
||||
u32 percentBoost;
|
||||
u32 metronomeTurns;
|
||||
uq4_12_t metronomeBoostBase;
|
||||
switch (holdEffectAtk)
|
||||
{
|
||||
case HOLD_EFFECT_METRONOME:
|
||||
percentBoost = min((gBattleStruct->sameMoveTurns[battlerAtk] * GetBattlerHoldEffectParam(battlerAtk)), 100);
|
||||
return uq4_12_add(sPercentToModifier[percentBoost], UQ_4_12(1.0));
|
||||
metronomeBoostBase = PercentToUQ4_12(GetBattlerHoldEffectParam(battlerAtk));
|
||||
metronomeTurns = min(gBattleStruct->sameMoveTurns[battlerAtk], 5);
|
||||
// according to bulbapedia this is the "correct" way to calculate the metronome boost
|
||||
// due to the limited domain of damage numbers it will never really matter whether this is off by one
|
||||
return uq4_12_add(UQ_4_12(1.0), metronomeBoostBase * metronomeTurns);
|
||||
break;
|
||||
case HOLD_EFFECT_EXPERT_BELT:
|
||||
if (typeEffectivenessModifier >= UQ_4_12(2.0))
|
||||
return UQ_4_12(1.2);
|
||||
break;
|
||||
case HOLD_EFFECT_LIFE_ORB:
|
||||
return UQ_4_12(1.3);
|
||||
return UQ_4_12_FLOORED(1.3);
|
||||
break;
|
||||
}
|
||||
return UQ_4_12(1.0);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Transistor increases Electric-type move damage", s16 damage)
|
||||
SINGLE_BATTLE_TEST("Transistor increases Electric-type attack / special attack", s16 damage)
|
||||
{
|
||||
u32 move;
|
||||
u16 ability;
|
||||
|
@ -30,20 +30,13 @@ SINGLE_BATTLE_TEST("Transistor increases Electric-type move damage", s16 damage)
|
|||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_EQ(results[0].damage, results[1].damage); // Tackle should be unaffected
|
||||
if (B_TRANSISTOR_BOOST >= GEN_9)
|
||||
{
|
||||
EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.3), results[3].damage); // Wild Charge should be affected
|
||||
EXPECT_MUL_EQ(results[4].damage, Q_4_12(1.3), results[5].damage); // Thunder Shock should be affected
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.5), results[3].damage); // Wild Charge should be affected
|
||||
EXPECT_MUL_EQ(results[4].damage, Q_4_12(1.5), results[5].damage); // Thunder Shock should be affected
|
||||
}
|
||||
|
||||
EXPECT_LT(results[2].damage, results[3].damage); // cannot test exact factor because ATK / SPATK introduces inaccuracies
|
||||
EXPECT_LT(results[4].damage, results[5].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Transistor boosts Electric type moves by 1.5 in Gen8 and 1.3 in Gen9+", s16 damage)
|
||||
SINGLE_BATTLE_TEST("Transistor is blocked by neutralizing gas", s16 damage)
|
||||
{
|
||||
u16 ability;
|
||||
PARAMETRIZE { ability = ABILITY_NEUTRALIZING_GAS; }
|
||||
|
@ -58,9 +51,6 @@ SINGLE_BATTLE_TEST("Transistor boosts Electric type moves by 1.5 in Gen8 and 1.3
|
|||
} SCENE {
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
if (B_TRANSISTOR_BOOST >= GEN_9)
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.3), results[1].damage);
|
||||
else
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
|
||||
EXPECT_LT(results[0].damage, results[1].damage); // cannot test exact factor because ATK / SPATK introduces inaccuracies
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,3 +150,134 @@ DOUBLE_BATTLE_TEST("A spread move will do correct damage to the second mon if th
|
|||
EXPECT_EQ(damage[4], damage[5]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Punching Glove vs Muscle Band Damage calculation")
|
||||
{
|
||||
s16 dmgPlayer, dmgOpponent;
|
||||
s16 expectedDamagePlayer, expectedDamageOpponent;
|
||||
PARAMETRIZE { expectedDamagePlayer = 204, expectedDamageOpponent = 201; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 201, expectedDamageOpponent = 198; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 199, expectedDamageOpponent = 196; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 196, expectedDamageOpponent = 193; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 195, expectedDamageOpponent = 192; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 193, expectedDamageOpponent = 190; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 190, expectedDamageOpponent = 187; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 189, expectedDamageOpponent = 186; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 187, expectedDamageOpponent = 184; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 184, expectedDamageOpponent = 181; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 183, expectedDamageOpponent = 180; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 181, expectedDamageOpponent = 178; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 178, expectedDamageOpponent = 175; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 177, expectedDamageOpponent = 174; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 174, expectedDamageOpponent = 172; }
|
||||
PARAMETRIZE { expectedDamagePlayer = 172, expectedDamageOpponent = 169; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_MAKUHITA) { Item(ITEM_PUNCHING_GLOVE); }
|
||||
OPPONENT(SPECIES_MAKUHITA) { Item(ITEM_MUSCLE_BAND); }
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(player, MOVE_DRAIN_PUNCH, WITH_RNG(RNG_DAMAGE_MODIFIER, i));
|
||||
MOVE(opponent, MOVE_DRAIN_PUNCH, WITH_RNG(RNG_DAMAGE_MODIFIER, i));
|
||||
}
|
||||
}
|
||||
SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAIN_PUNCH, player);
|
||||
HP_BAR(opponent, captureDamage: &dmgPlayer);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAIN_PUNCH, opponent);
|
||||
HP_BAR(player, captureDamage: &dmgOpponent);
|
||||
}
|
||||
THEN {
|
||||
EXPECT_EQ(expectedDamagePlayer, dmgPlayer);
|
||||
EXPECT_EQ(expectedDamageOpponent, dmgOpponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Gem boosted Damage calculation")
|
||||
{
|
||||
s16 dmg;
|
||||
s16 expectedDamage;
|
||||
PARAMETRIZE { expectedDamage = 240; }
|
||||
PARAMETRIZE { expectedDamage = 237; }
|
||||
PARAMETRIZE { expectedDamage = 234; }
|
||||
PARAMETRIZE { expectedDamage = 232; }
|
||||
PARAMETRIZE { expectedDamage = 229; }
|
||||
PARAMETRIZE { expectedDamage = 228; }
|
||||
PARAMETRIZE { expectedDamage = 225; }
|
||||
PARAMETRIZE { expectedDamage = 222; }
|
||||
PARAMETRIZE { expectedDamage = 220; }
|
||||
PARAMETRIZE { expectedDamage = 217; }
|
||||
PARAMETRIZE { expectedDamage = 216; }
|
||||
PARAMETRIZE { expectedDamage = 213; }
|
||||
PARAMETRIZE { expectedDamage = 210; }
|
||||
PARAMETRIZE { expectedDamage = 208; }
|
||||
PARAMETRIZE { expectedDamage = 205; }
|
||||
PARAMETRIZE { expectedDamage = 204; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_MAKUHITA) { Item(ITEM_FIGHTING_GEM); }
|
||||
OPPONENT(SPECIES_MAKUHITA);
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(player, MOVE_DRAIN_PUNCH, WITH_RNG(RNG_DAMAGE_MODIFIER, i));
|
||||
}
|
||||
}
|
||||
SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAIN_PUNCH, player);
|
||||
HP_BAR(opponent, captureDamage: &dmg);
|
||||
}
|
||||
THEN {
|
||||
EXPECT_EQ(expectedDamage, dmg);
|
||||
}
|
||||
}
|
||||
|
||||
#define NUM_DAMAGE_SPREADS (DMG_ROLL_PERCENT_HI - DMG_ROLL_PERCENT_LO) + 1
|
||||
|
||||
static const s16 sThunderShockTransistorSpread[] = { 54, 55, 56, 57, 57, 58, 59, 60, 60, 60, 61, 62, 63, 63, 64, 65 };
|
||||
static const s16 sThunderShockRegularSpread[] = { 42, 42, 43, 43, 44, 45, 45, 45, 46, 46, 47, 48, 48, 48, 49, 50 };
|
||||
static const s16 sWildChargeTransistorSpread[] = { 123, 124, 126, 127, 129, 130, 132, 133, 135, 136, 138, 139, 141, 142, 144, 145 };
|
||||
static const s16 sWildChargeRegularSpread[] = { 94, 96, 96, 98, 99, 100, 101, 102, 103, 105, 105, 107, 108, 109, 110, 111 };
|
||||
|
||||
DOUBLE_BATTLE_TEST("Transistor Damage calculation", s16 damage)
|
||||
{
|
||||
s16 expectedDamageTransistorSpec = 0, expectedDamageRegularPhys = 0, expectedDamageRegularSpec = 0, expectedDamageTransistorPhys = 0;
|
||||
s16 damagePlayerLeft, damagePlayerRight, damageOpponentLeft, damageOpponentRight;
|
||||
for (u32 spread = 0; spread < 16; ++spread) {
|
||||
PARAMETRIZE { expectedDamageTransistorSpec = sThunderShockTransistorSpread[spread],
|
||||
expectedDamageRegularSpec = sThunderShockRegularSpread[spread],
|
||||
expectedDamageTransistorPhys = sWildChargeTransistorSpread[spread],
|
||||
expectedDamageRegularPhys = sWildChargeRegularSpread[spread];
|
||||
}
|
||||
}
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_WILD_CHARGE].type == TYPE_ELECTRIC);
|
||||
ASSUME(gMovesInfo[MOVE_THUNDER_SHOCK].type == TYPE_ELECTRIC);
|
||||
ASSUME(gMovesInfo[MOVE_WILD_CHARGE].category == DAMAGE_CATEGORY_PHYSICAL);
|
||||
ASSUME(gMovesInfo[MOVE_THUNDER_SHOCK].category == DAMAGE_CATEGORY_SPECIAL);
|
||||
ASSUME(NUM_DAMAGE_SPREADS == 16);
|
||||
|
||||
PLAYER(SPECIES_REGIELEKI) { Ability(ABILITY_KLUTZ); }
|
||||
PLAYER(SPECIES_REGIELEKI) { Ability(ABILITY_TRANSISTOR); }
|
||||
OPPONENT(SPECIES_REGIELEKI) { Ability(ABILITY_KLUTZ); }
|
||||
OPPONENT(SPECIES_REGIELEKI) { Ability(ABILITY_TRANSISTOR); }
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(playerLeft, MOVE_THUNDER_SHOCK, target: opponentLeft, WITH_RNG(RNG_DAMAGE_MODIFIER, 15 - i));
|
||||
MOVE(playerRight, MOVE_THUNDER_SHOCK, target: opponentRight, WITH_RNG(RNG_DAMAGE_MODIFIER, 15 - i));
|
||||
MOVE(opponentLeft, MOVE_WILD_CHARGE, target: playerLeft, WITH_RNG(RNG_DAMAGE_MODIFIER, 15 - i));
|
||||
MOVE(opponentRight, MOVE_WILD_CHARGE, target: playerRight, WITH_RNG(RNG_DAMAGE_MODIFIER, 15 - i));
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_SHOCK, playerLeft);
|
||||
HP_BAR(opponentLeft, captureDamage: &damageOpponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_SHOCK, playerRight);
|
||||
HP_BAR(opponentRight, captureDamage: &damageOpponentRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_WILD_CHARGE, opponentLeft);
|
||||
HP_BAR(playerLeft, captureDamage: &damagePlayerLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_WILD_CHARGE, opponentRight);
|
||||
HP_BAR(playerRight, captureDamage: &damagePlayerRight);
|
||||
} THEN {
|
||||
EXPECT_EQ(damageOpponentLeft, expectedDamageRegularSpec);
|
||||
EXPECT_EQ(damageOpponentRight, expectedDamageTransistorSpec);
|
||||
EXPECT_EQ(damagePlayerLeft, expectedDamageRegularPhys);
|
||||
EXPECT_EQ(damagePlayerRight, expectedDamageTransistorPhys);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue