Gen. 9 Move Effects, 1st batch (#2534)

This commit is contained in:
ghoulslash 2023-03-26 16:20:47 -04:00 committed by GitHub
commit 6574e01e73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 574 additions and 25 deletions

View File

@ -420,6 +420,29 @@ gBattleScriptsForMoveEffects::
.4byte BattleScript_EffectShellTrap @ EFFECT_SHELL_TRAP
.4byte BattleScript_EffectHit @ EFFECT_PSYBLADE
.4byte BattleScript_EffectHit @ EFFECT_HYDRO_STEAM
.4byte BattleScript_EffectHitSetEntryHazard @ EFFECT_HIT_SET_ENTRY_HAZARD
.4byte BattleScript_EffectDireClaw @ EFFECT_DIRE_CLAW
.4byte BattleScript_EffectBarbBarrage @ EFFECT_BARB_BARRAGE
BattleScript_StealthRockActivates::
setstealthrock BattleScript_MoveEnd
printfromtable gDmgHazardsStringIds
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectDireClaw::
setmoveeffect MOVE_EFFECT_DIRE_CLAW
goto BattleScript_EffectHit
BattleScript_EffectHitSetEntryHazard::
argumenttomoveeffect
goto BattleScript_EffectHit
BattleScript_SpikesActivates::
trysetspikes BattleScript_MoveEnd
printfromtable gDmgHazardsStringIds
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectAttackUpUserAlly:
jumpifnoally BS_ATTACKER, BattleScript_EffectAttackUp
@ -3372,6 +3395,7 @@ BattleScript_CantMakeAsleep::
orhalfword gMoveResultFlags, MOVE_RESULT_FAILED
goto BattleScript_MoveEnd
BattleScript_EffectBarbBarrage:
BattleScript_EffectPoisonHit:
setmoveeffect MOVE_EFFECT_POISON
goto BattleScript_EffectHit

View File

@ -459,6 +459,8 @@ extern const u8 BattleScript_LunarDanceActivates[];
extern const u8 BattleScript_ShellTrapSetUp[];
extern const u8 BattleScript_CouldntFullyProtect[];
extern const u8 BattleScript_MoveEffectStockpileWoreOff[];
extern const u8 BattleScript_StealthRockActivates[];
extern const u8 BattleScript_SpikesActivates[];
// zmoves
extern const u8 BattleScript_ZMoveActivateDamaging[];

View File

@ -372,9 +372,12 @@
#define MOVE_EFFECT_TRAP_BOTH 70
#define MOVE_EFFECT_DOUBLE_SHOCK 71
#define MOVE_EFFECT_ROUND 72
#define MOVE_EFFECT_STOCKPILE_WORE_OFF 74
#define MOVE_EFFECT_STOCKPILE_WORE_OFF 73
#define MOVE_EFFECT_DIRE_CLAW 74
#define MOVE_EFFECT_STEALTH_ROCK 75
#define MOVE_EFFECT_SPIKES 76
#define NUM_MOVE_EFFECTS 75
#define NUM_MOVE_EFFECTS 77
#define MOVE_EFFECT_AFFECTS_USER 0x4000
#define MOVE_EFFECT_CERTAIN 0x8000

View File

@ -401,7 +401,10 @@
#define EFFECT_SHELL_TRAP 395
#define EFFECT_PSYBLADE 396
#define EFFECT_HYDRO_STEAM 397
#define EFFECT_HIT_SET_ENTRY_HAZARD 398
#define EFFECT_DIRE_CLAW 399
#define EFFECT_BARB_BARRAGE 400
#define NUM_BATTLE_MOVE_EFFECTS 398
#define NUM_BATTLE_MOVE_EFFECTS 401
#endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H

View File

@ -933,4 +933,10 @@
#define B_MSG_Z_STAT_UP 5
#define B_MSG_Z_HP_TRAP 6
// gDmgHazardsStringIds
#define B_MSG_PKMNHURTBYSPIKES 0
#define B_MSG_STEALTHROCKDMG 1
#define B_MSG_POINTEDSTONESFLOAT 2
#define B_MSG_SPIKESSCATTERED 3
#endif // GUARD_CONSTANTS_BATTLE_STRING_IDS_H

View File

@ -3477,6 +3477,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
break;
case EFFECT_TOXIC:
case EFFECT_POISON:
case EFFECT_BARB_BARRAGE:
IncreasePoisonScore(battlerAtk, battlerDef, move, &score);
break;
case EFFECT_LIGHT_SCREEN:
@ -3870,6 +3871,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
break;
case EFFECT_SPIKES:
case EFFECT_HIT_SET_ENTRY_HAZARD:
case EFFECT_STEALTH_ROCK:
case EFFECT_STICKY_WEB:
case EFFECT_TOXIC_SPIKES:
@ -4906,6 +4908,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
case EFFECT_HAIL:
case EFFECT_GEOMANCY:
case EFFECT_VICTORY_DANCE:
case EFFECT_HIT_SET_ENTRY_HAZARD:
score += 2;
break;
default:

View File

@ -209,7 +209,7 @@ static const u8 sText_PkmnFellIntoNightmare[] = _("{B_DEF_NAME_WITH_PREFIX} fell
static const u8 sText_PkmnLockedInNightmare[] = _("{B_ATK_NAME_WITH_PREFIX} is locked\nin a NIGHTMARE!");
static const u8 sText_PkmnLaidCurse[] = _("{B_ATK_NAME_WITH_PREFIX} cut its own HP and\nlaid a CURSE on {B_DEF_NAME_WITH_PREFIX}!");
static const u8 sText_PkmnAfflictedByCurse[] = _("{B_ATK_NAME_WITH_PREFIX} is afflicted\nby the CURSE!");
static const u8 sText_SpikesScattered[] = _("Spikes were scattered all around\nthe opponent's side!");
static const u8 sText_SpikesScattered[] = _("Spikes were scattered all around\n{B_DEF_TEAM2} team!");
static const u8 sText_PkmnHurtBySpikes[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is hurt\nby spikes!");
static const u8 sText_PkmnIdentified[] = _("{B_ATK_NAME_WITH_PREFIX} identified\n{B_DEF_NAME_WITH_PREFIX}!");
static const u8 sText_PkmnPerishCountFell[] = _("{B_ATK_NAME_WITH_PREFIX}'s PERISH count\nfell to {B_BUFF1}!");
@ -1475,7 +1475,10 @@ const u16 gHealingWishStringIds[] =
const u16 gDmgHazardsStringIds[] =
{
STRINGID_PKMNHURTBYSPIKES, STRINGID_STEALTHROCKDMG
[B_MSG_PKMNHURTBYSPIKES] = STRINGID_PKMNHURTBYSPIKES,
[B_MSG_STEALTHROCKDMG] = STRINGID_STEALTHROCKDMG,
[B_MSG_POINTEDSTONESFLOAT] = STRINGID_POINTEDSTONESFLOAT,
[B_MSG_SPIKESSCATTERED] = STRINGID_SPIKESSCATTERED
};
const u16 gSwitchInAbilityStringIds[] =

View File

@ -2894,6 +2894,7 @@ void SetMoveEffect(bool32 primary, u32 certain)
bool32 mirrorArmorReflected = (GetBattlerAbility(gBattlerTarget) == ABILITY_MIRROR_ARMOR);
u32 flags = 0;
u16 battlerAbility;
bool8 activateAfterFaint = FALSE;
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_1ST_HIT
&& gBattleMons[gBattlerTarget].hp != 0
@ -2912,6 +2913,13 @@ void SetMoveEffect(bool32 primary, u32 certain)
gBattleStruct->moveEffect2 = gBattleScripting.moveEffect;
gBattlescriptCurrInstr++;
return;
case MOVE_EFFECT_STEALTH_ROCK:
case MOVE_EFFECT_SPIKES:
case MOVE_EFFECT_PAYDAY:
case MOVE_EFFECT_STEAL_ITEM:
case MOVE_EFFECT_BUG_BITE:
activateAfterFaint = TRUE;
break;
}
if (gBattleScripting.moveEffect & MOVE_EFFECT_AFFECTS_USER)
@ -2952,10 +2960,7 @@ void SetMoveEffect(bool32 primary, u32 certain)
if (TestSheerForceFlag(gBattlerAttacker, gCurrentMove) && affectsUser != MOVE_EFFECT_AFFECTS_USER)
INCREMENT_RESET_RETURN
if (gBattleMons[gEffectBattler].hp == 0
&& gBattleScripting.moveEffect != MOVE_EFFECT_PAYDAY
&& gBattleScripting.moveEffect != MOVE_EFFECT_STEAL_ITEM
&& gBattleScripting.moveEffect != MOVE_EFFECT_BUG_BITE)
if (gBattleMons[gEffectBattler].hp == 0 && !activateAfterFaint)
INCREMENT_RESET_RETURN
if (DoesSubstituteBlockMove(gBattlerAttacker, gEffectBattler, gCurrentMove) && affectsUser != MOVE_EFFECT_AFFECTS_USER)
@ -3770,6 +3775,30 @@ void SetMoveEffect(bool32 primary, u32 certain)
TryUpdateRoundTurnOrder(); // If another Pokémon uses Round before the user this turn, the user will use Round directly after it
gBattlescriptCurrInstr++;
break;
case MOVE_EFFECT_DIRE_CLAW:
if (!gBattleMons[gEffectBattler].status1)
{
static const u8 sDireClawEffects[] = { MOVE_EFFECT_POISON, MOVE_EFFECT_PARALYSIS, MOVE_EFFECT_SLEEP };
gBattleScripting.moveEffect = sDireClawEffects[Random() % ARRAY_COUNT(sDireClawEffects)];
SetMoveEffect(TRUE, 0);
}
break;
case MOVE_EFFECT_STEALTH_ROCK:
if (!(gSideStatuses[GetBattlerSide(gEffectBattler)] & SIDE_STATUS_STEALTH_ROCK))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_POINTEDSTONESFLOAT;
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_StealthRockActivates;
}
break;
case MOVE_EFFECT_SPIKES:
if (gSideTimers[GetBattlerSide(gEffectBattler)].spikesAmount < 3)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SPIKESSCATTERED;
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_SpikesActivates;
}
break;
}
}
}
@ -7049,7 +7078,7 @@ static void Cmd_switchineffects(void)
gBattleMoveDamage = 1;
gSideStatuses[GetBattlerSide(gActiveBattler)] |= SIDE_STATUS_SPIKES_DAMAGED;
SetDmgHazardsBattlescript(gActiveBattler, 0);
SetDmgHazardsBattlescript(gActiveBattler, B_MSG_PKMNHURTBYSPIKES);
}
else if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_STEALTH_ROCK_DAMAGED)
&& (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_STEALTH_ROCK)
@ -7060,7 +7089,7 @@ static void Cmd_switchineffects(void)
gBattleMoveDamage = GetStealthHazardDamage(gBattleMoves[MOVE_STEALTH_ROCK].type, gActiveBattler);
if (gBattleMoveDamage != 0)
SetDmgHazardsBattlescript(gActiveBattler, 1);
SetDmgHazardsBattlescript(gActiveBattler, B_MSG_STEALTHROCKDMG);
}
else if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_TOXIC_SPIKES_DAMAGED)
&& (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_TOXIC_SPIKES)

View File

@ -9123,6 +9123,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe
if (gBattleMons[battlerDef].hp <= (gBattleMons[battlerDef].maxHP / 2))
MulModifier(&modifier, UQ_4_12(2.0));
break;
case EFFECT_BARB_BARRAGE:
case EFFECT_VENOSHOCK:
if (gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY)
MulModifier(&modifier, UQ_4_12(2.0));

View File

@ -12452,11 +12452,11 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
#else
.power = 60,
#endif
.effect = EFFECT_PLACEHOLDER, // EFFECT_DIRE_CLAW,
.effect = EFFECT_DIRE_CLAW,
.type = TYPE_POISON,
.accuracy = 100,
.pp = 15,
.secondaryEffectChance = 0,
.secondaryEffectChance = 50,
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST,
@ -12496,17 +12496,18 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
[MOVE_STONE_AXE] =
{
.effect = EFFECT_PLACEHOLDER, // EFFECT_STONE_AXE,
.effect = EFFECT_HIT_SET_ENTRY_HAZARD,
.power = 65,
.type = TYPE_ROCK,
.accuracy = 90,
.pp = 15,
.secondaryEffectChance = 0,
.target = MOVE_TARGET_USER,
.secondaryEffectChance = 100,
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST | FLAG_SLICING_MOVE,
.split = SPLIT_PHYSICAL,
.zMoveEffect = Z_EFFECT_NONE,
.argument = MOVE_EFFECT_STEALTH_ROCK,
},
[MOVE_SPRINGTIDE_STORM] =
@ -12651,12 +12652,16 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
[MOVE_BARB_BARRAGE] =
{
.effect = EFFECT_PLACEHOLDER, // EFFECT_BARB_BARRAGE,
.effect = EFFECT_BARB_BARRAGE,
.power = 60,
.type = TYPE_POISON,
.accuracy = 100,
#if B_UPDATED_MOVE_DATA >= GEN_9
.pp = 10,
#else
.pp = 15,
.secondaryEffectChance = 0,
#endif
.secondaryEffectChance = 50,
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST,
@ -12753,17 +12758,18 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
[MOVE_CEASELESS_EDGE] =
{
.effect = EFFECT_PLACEHOLDER, // EFFECT_CEASELESS_EDGE,
.effect = EFFECT_HIT_SET_ENTRY_HAZARD,
.power = 65,
.type = TYPE_DARK,
.accuracy = 90,
.pp = 15,
.secondaryEffectChance = 0,
.secondaryEffectChance = 100,
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST | FLAG_SLICING_MOVE,
.split = SPLIT_PHYSICAL,
.zMoveEffect = Z_EFFECT_NONE,
.argument = MOVE_EFFECT_SPIKES,
},
[MOVE_BLEAKWIND_STORM] =

View File

@ -0,0 +1,43 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
//ASSUME(gBattleMoves[MOVE_BARB_BARRAGE].effect == EFFECT_BARB_BARRAGE);
}
SINGLE_BATTLE_TEST("Barb Barrage inflicts poison")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BARB_BARRAGE); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BARB_BARRAGE, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
STATUS_ICON(opponent, poison: TRUE);
}
}
SINGLE_BATTLE_TEST("Barb Barrage's power doubles if the target is poisoned/badly poisoned", s16 damage)
{
u32 status1;
PARAMETRIZE { status1 = 0; }
PARAMETRIZE { status1 = STATUS1_POISON; }
PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) {Status1(status1);}
} WHEN {
TURN { MOVE(player, MOVE_BARB_BARRAGE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BARB_BARRAGE, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[2].damage);
}
}

View File

@ -0,0 +1,138 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_DIRE_CLAW].effect == EFFECT_DIRE_CLAW);
}
// found by brute-force
#define RNG_SLEEP 0xcb0
#define RNG_POISON 0x2BE
#define RNG_PARALYSIS 5
SINGLE_BATTLE_TEST("Dire Claw can inflict poison, paralysis or sleep")
{
u8 statusAnim;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = RNG_POISON; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = RNG_SLEEP; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_DIRE_CLAW); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DIRE_CLAW, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_PRZ) {
STATUS_ICON(opponent, paralysis: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_SLP) {
STATUS_ICON(opponent, sleep: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PSN) {
STATUS_ICON(opponent, poison: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze poison/electric types respectively")
{
u8 statusAnim;
u16 species;
u32 rng;
#if B_PARALYZE_ELECTRIC >= GEN_6
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_RAICHU; }
#endif // B_PARALYZE_ELECTRIC
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = RNG_POISON; species = SPECIES_ARBOK;}
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_DIRE_CLAW); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DIRE_CLAW, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_PRZ) {
NOT STATUS_ICON(opponent, paralysis: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PSN) {
NOT STATUS_ICON(opponent, poison: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze/cause to fall asleep pokemon with abilities preventing respective statuses")
{
u8 statusAnim;
u16 species, ability;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_RAICHU; ability = ABILITY_LIGHTNING_ROD; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_JOLTEON; ability = ABILITY_VOLT_ABSORB; }
#if P_GEN_4_POKEMON == TRUE
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_ELECTIVIRE; ability = ABILITY_MOTOR_DRIVE; }
#endif // P_GEN_4_POKEMON
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = RNG_POISON; species = SPECIES_ZANGOOSE; ability = ABILITY_IMMUNITY; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = RNG_SLEEP; species = SPECIES_VIGOROTH; ability = ABILITY_VITAL_SPIRIT; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = RNG_SLEEP; species = SPECIES_HYPNO; ability = ABILITY_INSOMNIA; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species) {Ability(ability);}
} WHEN {
TURN { MOVE(player, MOVE_DIRE_CLAW); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DIRE_CLAW, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_PRZ) {
NOT STATUS_ICON(opponent, paralysis: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_SLP) {
NOT STATUS_ICON(opponent, sleep: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PSN) {
NOT STATUS_ICON(opponent, poison: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze/cause to fall asleep a mon which is already statused")
{
u8 statusAnim;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = RNG_POISON; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = RNG_SLEEP; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_BURN);}
} WHEN {
TURN { MOVE(player, MOVE_DIRE_CLAW); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DIRE_CLAW, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_PRZ) {
NOT STATUS_ICON(opponent, paralysis: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_SLP) {
NOT STATUS_ICON(opponent, sleep: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PSN) {
NOT STATUS_ICON(opponent, poison: TRUE);
}
}
}

View File

@ -0,0 +1,117 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_STONE_AXE].effect == EFFECT_HIT_SET_ENTRY_HAZARD);
ASSUME(gBattleMoves[MOVE_CEASELESS_EDGE].effect == EFFECT_HIT_SET_ENTRY_HAZARD);
}
SINGLE_BATTLE_TEST("Stone Axe / Ceaseless Edge set up hazards after hitting the target")
{
u16 move;
PARAMETRIZE {move = MOVE_STONE_AXE; }
PARAMETRIZE {move = MOVE_CEASELESS_EDGE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); }
TURN { SWITCH(opponent, 1); }
} SCENE {
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, move, player);
HP_BAR(opponent);
if (move == MOVE_CEASELESS_EDGE) {
MESSAGE("Spikes were scattered all around the opposing team!");
}
else {
MESSAGE("Pointed stones float in the air around the opposing team!");
}
MESSAGE("2 sent out Wobbuffet!");
if (move == MOVE_CEASELESS_EDGE) {
HP_BAR(opponent, damage: maxHP / 8);
MESSAGE("Foe Wobbuffet is hurt by spikes!");
}
else {
HP_BAR(opponent, damage: maxHP / 8);
MESSAGE("Pointed stones dug into Foe Wobbuffet!");
}
}
}
SINGLE_BATTLE_TEST("Ceaseless Edge can set up to 3 layers of Spikes")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(player, MOVE_CEASELESS_EDGE); }
TURN { MOVE(player, MOVE_CEASELESS_EDGE); }
TURN { MOVE(player, MOVE_CEASELESS_EDGE); }
TURN { MOVE(player, MOVE_CEASELESS_EDGE); }
TURN { SWITCH(opponent, 1); }
} SCENE {
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player);
HP_BAR(opponent);
MESSAGE("Spikes were scattered all around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player);
HP_BAR(opponent);
MESSAGE("Spikes were scattered all around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player);
HP_BAR(opponent);
MESSAGE("Spikes were scattered all around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player);
HP_BAR(opponent);
NOT MESSAGE("Spikes were scattered all around the opposing team!");
MESSAGE("2 sent out Wynaut!");
HP_BAR(opponent, damage: maxHP / 4);
MESSAGE("Foe Wynaut is hurt by spikes!");
}
}
SINGLE_BATTLE_TEST("Stone Axe can set up pointed stones only once")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { SWITCH(opponent, 1); }
} SCENE {
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
MESSAGE("Pointed stones float in the air around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
MESSAGE("2 sent out Wynaut!");
HP_BAR(opponent, damage: maxHP / 8);
MESSAGE("Pointed stones dug into Foe Wynaut!");
}
}

View File

@ -28,7 +28,7 @@ SINGLE_BATTLE_TEST("Spikes damage on switch in")
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
for (count = 0; count < layers; ++count) {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
MESSAGE("Spikes were scattered all around the opponent's side!");
MESSAGE("Spikes were scattered all around the opposing team!");
}
MESSAGE("2 sent out Wynaut!");
HP_BAR(opponent, damage: maxHP / divisor);
@ -51,11 +51,11 @@ SINGLE_BATTLE_TEST("Spikes fails after 3 layers")
} SCENE {
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
MESSAGE("Spikes were scattered all around the opponent's side!");
MESSAGE("Spikes were scattered all around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
MESSAGE("Spikes were scattered all around the opponent's side!");
MESSAGE("Spikes were scattered all around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
MESSAGE("Spikes were scattered all around the opponent's side!");
MESSAGE("Spikes were scattered all around the opposing team!");
MESSAGE("Wobbuffet used Spikes!");
MESSAGE("But it failed!");
MESSAGE("2 sent out Wynaut!");

View File

@ -0,0 +1,144 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_TRI_ATTACK].effect == EFFECT_TRI_ATTACK);
}
// found by brute-force
#define RNG_PARALYSIS 0xcb0
#define RNG_BURN 0x2BE
#define RNG_FREEZE 5
SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or freeze")
{
u8 statusAnim;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = RNG_FREEZE; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TRI_ATTACK); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_BRN) {
STATUS_ICON(opponent, burn: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_FRZ) {
STATUS_ICON(opponent, freeze: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PRZ) {
STATUS_ICON(opponent, paralysis: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice types respectively")
{
u8 statusAnim;
u16 species;
u32 rng;
#if B_PARALYZE_ELECTRIC >= GEN_6
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_RAICHU;}
#endif // B_PARALYZE_ELECTRIC
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; species = SPECIES_ARCANINE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = RNG_FREEZE; species = SPECIES_GLALIE; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_TRI_ATTACK); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_BRN) {
NOT STATUS_ICON(opponent, burn: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_FRZ) {
NOT STATUS_ICON(opponent, freeze: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PRZ) {
NOT STATUS_ICON(opponent, paralysis: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilities preventing respective statuses")
{
u8 statusAnim;
u16 species, ability;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_RAICHU; ability = ABILITY_LIGHTNING_ROD; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_JOLTEON; ability = ABILITY_VOLT_ABSORB; }
#if P_GEN_4_POKEMON == TRUE
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_ELECTIVIRE; ability = ABILITY_MOTOR_DRIVE; }
#endif // P_GEN_4_POKEMON
#if P_GEN_7_POKEMON == TRUE
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; species = SPECIES_DEWPIDER; ability = ABILITY_WATER_BUBBLE; }
#endif // P_GEN_7_POKEMON
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; species = SPECIES_SEAKING; ability = ABILITY_WATER_VEIL; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = RNG_FREEZE; species = SPECIES_CAMERUPT; ability = ABILITY_MAGMA_ARMOR; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species) {Ability(ability);}
} WHEN {
TURN { MOVE(player, MOVE_TRI_ATTACK); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_BRN) {
NOT STATUS_ICON(opponent, burn: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_FRZ) {
NOT STATUS_ICON(opponent, freeze: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PRZ) {
NOT STATUS_ICON(opponent, paralysis: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze a mon which is already statused")
{
u8 statusAnim;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = RNG_FREEZE; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
} WHEN {
TURN { MOVE(player, MOVE_TRI_ATTACK); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_BRN) {
NOT STATUS_ICON(opponent, burn: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_FRZ) {
NOT STATUS_ICON(opponent, freeze: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PRZ) {
NOT STATUS_ICON(opponent, paralysis: TRUE);
}
}
}

View File

@ -0,0 +1,27 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_VENOSHOCK].effect == EFFECT_VENOSHOCK);
}
SINGLE_BATTLE_TEST("Venoshock's power doubles if the target is poisoned/badly poisoned", s16 damage)
{
u32 status1;
PARAMETRIZE { status1 = 0; }
PARAMETRIZE { status1 = STATUS1_POISON; }
PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) {Status1(status1);}
} WHEN {
TURN { MOVE(player, MOVE_VENOSHOCK); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_VENOSHOCK, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[2].damage);
}
}