sovereignx/test/battle/damage_formula.c
2025-01-01 20:34:33 +01:00

283 lines
12 KiB
C

#include "global.h"
#include "test/battle.h"
// From https://bulbapedia.bulbagarden.net/wiki/Damage#Example
SINGLE_BATTLE_TEST("Damage calculation matches Gen5+")
{
s16 dmg;
s16 expectedDamage;
PARAMETRIZE { expectedDamage = 196; }
PARAMETRIZE { expectedDamage = 192; }
PARAMETRIZE { expectedDamage = 192; }
PARAMETRIZE { expectedDamage = 192; }
PARAMETRIZE { expectedDamage = 184; }
PARAMETRIZE { expectedDamage = 184; }
PARAMETRIZE { expectedDamage = 184; }
PARAMETRIZE { expectedDamage = 180; }
PARAMETRIZE { expectedDamage = 180; }
PARAMETRIZE { expectedDamage = 180; }
PARAMETRIZE { expectedDamage = 172; }
PARAMETRIZE { expectedDamage = 172; }
PARAMETRIZE { expectedDamage = 172; }
PARAMETRIZE { expectedDamage = 168; }
PARAMETRIZE { expectedDamage = 168; }
PARAMETRIZE { expectedDamage = 168; }
GIVEN {
ASSUME(GetMoveCategory(MOVE_ICE_FANG) == DAMAGE_CATEGORY_PHYSICAL);
PLAYER(SPECIES_GLACEON) { Level(75); Attack(123); }
OPPONENT(SPECIES_GARCHOMP) { Defense(163); }
} WHEN {
TURN {
MOVE(player, MOVE_ICE_FANG, WITH_RNG(RNG_DAMAGE_MODIFIER, i));
}
}
SCENE {
MESSAGE("Glaceon used Ice Fang!");
HP_BAR(opponent, captureDamage: &dmg);
}
THEN {
EXPECT_EQ(expectedDamage, dmg);
}
}
SINGLE_BATTLE_TEST("Damage calculation matches Gen5+ (Muscle Band, crit)")
{
s16 dmg;
s16 expectedDamage;
PARAMETRIZE { expectedDamage = 324; }
PARAMETRIZE { expectedDamage = 316; }
PARAMETRIZE { expectedDamage = 312; }
PARAMETRIZE { expectedDamage = 312; }
PARAMETRIZE { expectedDamage = 304; }
PARAMETRIZE { expectedDamage = 304; }
PARAMETRIZE { expectedDamage = 300; }
PARAMETRIZE { expectedDamage = 300; }
PARAMETRIZE { expectedDamage = 292; }
PARAMETRIZE { expectedDamage = 292; }
PARAMETRIZE { expectedDamage = 288; }
PARAMETRIZE { expectedDamage = 288; }
PARAMETRIZE { expectedDamage = 280; }
PARAMETRIZE { expectedDamage = 276; }
PARAMETRIZE { expectedDamage = 276; }
PARAMETRIZE { expectedDamage = 268; }
GIVEN {
ASSUME(GetMoveCategory(MOVE_ICE_FANG) == DAMAGE_CATEGORY_PHYSICAL);
PLAYER(SPECIES_GLACEON) { Level(75); Attack(123); Item(ITEM_MUSCLE_BAND); }
OPPONENT(SPECIES_GARCHOMP) { Defense(163); }
} WHEN {
TURN {
MOVE(player, MOVE_ICE_FANG, WITH_RNG(RNG_DAMAGE_MODIFIER, i), criticalHit: TRUE);
}
}
SCENE {
MESSAGE("Glaceon used Ice Fang!");
HP_BAR(opponent, captureDamage: &dmg);
}
THEN {
EXPECT_EQ(expectedDamage, dmg);
}
}
SINGLE_BATTLE_TEST("Damage calculation matches Gen5+ (Marshadow vs Mawile)")
{
s16 dmg;
s16 expectedDamage;
PARAMETRIZE { expectedDamage = 145; }
PARAMETRIZE { expectedDamage = 144; }
PARAMETRIZE { expectedDamage = 142; }
PARAMETRIZE { expectedDamage = 141; }
PARAMETRIZE { expectedDamage = 139; }
PARAMETRIZE { expectedDamage = 138; }
PARAMETRIZE { expectedDamage = 136; }
PARAMETRIZE { expectedDamage = 135; }
PARAMETRIZE { expectedDamage = 133; }
PARAMETRIZE { expectedDamage = 132; }
PARAMETRIZE { expectedDamage = 130; }
PARAMETRIZE { expectedDamage = 129; }
PARAMETRIZE { expectedDamage = 127; }
PARAMETRIZE { expectedDamage = 126; }
PARAMETRIZE { expectedDamage = 124; }
PARAMETRIZE { expectedDamage = 123; }
GIVEN {
ASSUME(GetMoveCategory(MOVE_SPECTRAL_THIEF) == DAMAGE_CATEGORY_PHYSICAL);
PLAYER(SPECIES_MARSHADOW) { Level(100); Attack(286); }
OPPONENT(SPECIES_MAWILE) { Level(100); Defense(226); HP(241); }
} WHEN {
TURN {
MOVE(player, MOVE_SPECTRAL_THIEF, WITH_RNG(RNG_DAMAGE_MODIFIER, i), criticalHit: FALSE);
}
}
SCENE{
MESSAGE("Marshadow used Spectral Thief!");
HP_BAR(opponent, captureDamage: &dmg);
}
THEN{
EXPECT_EQ(expectedDamage, dmg);
}
}
DOUBLE_BATTLE_TEST("A spread move will do correct damage to the second mon if the first target faints from first hit of the spread move")
{
s16 damage[6];
GIVEN {
PLAYER(SPECIES_REGIROCK);
PLAYER(SPECIES_REGIROCK);
OPPONENT(SPECIES_WOBBUFFET) { HP(200); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_ROCK_SLIDE); }
TURN { MOVE(playerLeft, MOVE_ROCK_SLIDE); MOVE(playerRight, MOVE_ROCK_SLIDE); }
TURN { MOVE(playerLeft, MOVE_ROCK_SLIDE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_SLIDE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
HP_BAR(opponentRight, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_SLIDE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[2]);
HP_BAR(opponentRight, captureDamage: &damage[3]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_SLIDE, playerRight);
HP_BAR(opponentRight, captureDamage: &damage[4]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_SLIDE, playerLeft);
HP_BAR(opponentRight, captureDamage: &damage[5]);
} THEN {
EXPECT_EQ(damage[0], damage[1]);
EXPECT_EQ(damage[1], damage[3]);
EXPECT_MUL_EQ(damage[5], UQ_4_12(0.75), damage[3]);
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(GetMoveType(MOVE_WILD_CHARGE) == TYPE_ELECTRIC);
ASSUME(GetMoveType(MOVE_THUNDER_SHOCK) == TYPE_ELECTRIC);
ASSUME(GetMoveCategory(MOVE_WILD_CHARGE) == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(GetMoveCategory(MOVE_THUNDER_SHOCK) == 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);
}
}