Made Reflect Type handle 3rd types (#3303)

* Made Reflect Type handle 3rd types

Misc:
-Turned VARIOUS_TRY_REFLECT_TYPE into a callnative (BS_TryReflectType)
-Introduced a macro to to check for typeless Pokémon (Pokémon who have Mystery in all 3 type slots) in battle.
-Made the new BS_TryReflectType take into account the forms for Arceus and Silvally, rather than just their default form.
This commit is contained in:
LOuroboros 2023-10-23 06:08:36 -03:00 committed by GitHub
parent 77e2b0dd2c
commit cd59e055c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 241 additions and 36 deletions

View File

@ -1456,6 +1456,11 @@
.4byte \jumpInstr .4byte \jumpInstr
.endm .endm
.macro tryreflecttype failInstr:req
callnative BS_TryReflectType
.4byte \failInstr
.endm
@ various command changed to more readable macros @ various command changed to more readable macros
.macro cancelmultiturnmoves battler:req .macro cancelmultiturnmoves battler:req
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES
@ -1686,11 +1691,6 @@
.4byte \failInstr .4byte \failInstr
.endm .endm
.macro tryreflecttype failInstr:req
various BS_ATTACKER, VARIOUS_TRY_REFLECT_TYPE
.4byte \failInstr
.endm
.macro trysoak failInstr:req .macro trysoak failInstr:req
various BS_ATTACKER, VARIOUS_TRY_SOAK various BS_ATTACKER, VARIOUS_TRY_SOAK
.4byte \failInstr .4byte \failInstr

View File

@ -724,12 +724,16 @@ STATIC_ASSERT(sizeof(((struct BattleStruct *)0)->palaceFlags) * 8 >= MAX_BATTLER
#define BATTLER_DAMAGED(battlerId) ((gSpecialStatuses[battlerId].physicalDmg != 0 || gSpecialStatuses[battlerId].specialDmg != 0)) #define BATTLER_DAMAGED(battlerId) ((gSpecialStatuses[battlerId].physicalDmg != 0 || gSpecialStatuses[battlerId].specialDmg != 0))
#define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0) == type || GetBattlerType(battlerId, 1) == type || (GetBattlerType(battlerId, 2) != TYPE_MYSTERY && GetBattlerType(battlerId, 2) == type))) #define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0) == type || GetBattlerType(battlerId, 1) == type || (GetBattlerType(battlerId, 2) != TYPE_MYSTERY && GetBattlerType(battlerId, 2) == type)))
#define IS_BATTLER_TYPELESS(battlerId)(GetBattlerType(battlerId, 0) == TYPE_MYSTERY && GetBattlerType(battlerId, 1) == TYPE_MYSTERY && GetBattlerType(battlerId, 2) == TYPE_MYSTERY)
#define SET_BATTLER_TYPE(battlerId, type) \ #define SET_BATTLER_TYPE(battlerId, type) \
{ \ { \
gBattleMons[battlerId].type1 = type; \ gBattleMons[battlerId].type1 = type; \
gBattleMons[battlerId].type2 = type; \ gBattleMons[battlerId].type2 = type; \
gBattleMons[battlerId].type3 = TYPE_MYSTERY; \ gBattleMons[battlerId].type3 = TYPE_MYSTERY; \
} }
#define RESTORE_BATTLER_TYPE(battlerId) \ #define RESTORE_BATTLER_TYPE(battlerId) \
{ \ { \
gBattleMons[battlerId].type1 = gSpeciesInfo[gBattleMons[battlerId].species].types[0]; \ gBattleMons[battlerId].type1 = gSpeciesInfo[gBattleMons[battlerId].species].types[0]; \

View File

@ -9409,37 +9409,6 @@ static void Cmd_various(void)
} }
return; return;
} }
case VARIOUS_TRY_REFLECT_TYPE:
{
VARIOUS_ARGS(const u8 *failInstr);
if (gBattleMons[gBattlerTarget].species == SPECIES_ARCEUS || gBattleMons[gBattlerTarget].species == SPECIES_SILVALLY)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (GetBattlerType(gBattlerTarget, 0) == TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) != TYPE_MYSTERY)
{
gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 1);
gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 1);
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (GetBattlerType(gBattlerTarget, 0) != TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) == TYPE_MYSTERY)
{
gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 0);
gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 0);
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (GetBattlerType(gBattlerTarget, 0) == TYPE_MYSTERY && GetBattlerType(gBattlerTarget, 1) == TYPE_MYSTERY)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
gBattleMons[gBattlerAttacker].type1 = GetBattlerType(gBattlerTarget, 0);
gBattleMons[gBattlerAttacker].type2 = GetBattlerType(gBattlerTarget, 1);
gBattlescriptCurrInstr = cmd->nextInstr;
}
return;
}
case VARIOUS_TRY_SOAK: case VARIOUS_TRY_SOAK:
{ {
VARIOUS_ARGS(const u8 *failInstr); VARIOUS_ARGS(const u8 *failInstr);
@ -16331,3 +16300,49 @@ void BS_JumpIfTerrainAffected(void)
else else
gBattlescriptCurrInstr = cmd->nextInstr; gBattlescriptCurrInstr = cmd->nextInstr;
} }
void BS_TryReflectType(void)
{
NATIVE_ARGS(const u8 *failInstr);
u16 targetBaseSpecies = GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species);
u8 targetType1 = GetBattlerType(gBattlerTarget, 0);
u8 targetType2 = GetBattlerType(gBattlerTarget, 1);
u8 targetType3 = GetBattlerType(gBattlerTarget, 2);
if (targetBaseSpecies == SPECIES_ARCEUS || targetBaseSpecies == SPECIES_SILVALLY)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (IS_BATTLER_TYPELESS(gBattlerTarget))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (targetType1 == TYPE_MYSTERY && targetType2 == TYPE_MYSTERY && targetType3 != TYPE_MYSTERY)
{
gBattleMons[gBattlerAttacker].type1 = TYPE_NORMAL;
gBattleMons[gBattlerAttacker].type2 = TYPE_NORMAL;
gBattleMons[gBattlerAttacker].type3 = targetType3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (targetType1 == TYPE_MYSTERY && targetType2 != TYPE_MYSTERY)
{
gBattleMons[gBattlerAttacker].type1 = targetType2;
gBattleMons[gBattlerAttacker].type2 = targetType2;
gBattleMons[gBattlerAttacker].type3 = targetType3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (targetType1 != TYPE_MYSTERY && targetType2 == TYPE_MYSTERY)
{
gBattleMons[gBattlerAttacker].type1 = targetType1;
gBattleMons[gBattlerAttacker].type2 = targetType1;
gBattleMons[gBattlerAttacker].type3 = targetType3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattleMons[gBattlerAttacker].type1 = targetType1;
gBattleMons[gBattlerAttacker].type2 = targetType2;
gBattleMons[gBattlerAttacker].type3 = targetType3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}

View File

@ -0,0 +1,186 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Reflect Type fails if the user is Terastallized");
TO_DO_BATTLE_TEST("Reflect Type succeeds against a Terastallized target and copies its Tera type");
SINGLE_BATTLE_TEST("Reflect Type does not affect any of Arceus' forms")
{
u32 j;
static const u16 sArceusFormSpeciesIdTable[] = {
SPECIES_ARCEUS,
SPECIES_ARCEUS_FIGHTING,
SPECIES_ARCEUS_FLYING,
SPECIES_ARCEUS_POISON,
SPECIES_ARCEUS_GROUND,
SPECIES_ARCEUS_ROCK,
SPECIES_ARCEUS_BUG,
SPECIES_ARCEUS_GHOST,
SPECIES_ARCEUS_STEEL,
SPECIES_ARCEUS_FIRE,
SPECIES_ARCEUS_WATER,
SPECIES_ARCEUS_GRASS,
SPECIES_ARCEUS_ELECTRIC,
SPECIES_ARCEUS_PSYCHIC,
SPECIES_ARCEUS_ICE,
SPECIES_ARCEUS_DRAGON,
SPECIES_ARCEUS_DARK,
SPECIES_ARCEUS_FAIRY,
};
u16 species = SPECIES_NONE;
for (j = 0; j < ARRAY_COUNT(sArceusFormSpeciesIdTable); j++)
{
PARAMETRIZE { species = sArceusFormSpeciesIdTable[j]; }
}
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_REFLECT_TYPE); }
} SCENE {
MESSAGE("Wobbuffet used Reflect Type!");
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Reflect Type does not affect any of Silvally's forms")
{
u32 j;
static const u16 sSilvallyFormSpeciesIdTable[] = {
SPECIES_SILVALLY,
SPECIES_SILVALLY_FIGHTING,
SPECIES_SILVALLY_FLYING,
SPECIES_SILVALLY_POISON,
SPECIES_SILVALLY_GROUND,
SPECIES_SILVALLY_ROCK,
SPECIES_SILVALLY_BUG,
SPECIES_SILVALLY_GHOST,
SPECIES_SILVALLY_STEEL,
SPECIES_SILVALLY_FIRE,
SPECIES_SILVALLY_WATER,
SPECIES_SILVALLY_GRASS,
SPECIES_SILVALLY_ELECTRIC,
SPECIES_SILVALLY_PSYCHIC,
SPECIES_SILVALLY_ICE,
SPECIES_SILVALLY_DRAGON,
SPECIES_SILVALLY_DARK,
SPECIES_SILVALLY_FAIRY,
};
u16 species = SPECIES_NONE;
for (j = 0; j < ARRAY_COUNT(sSilvallyFormSpeciesIdTable); j++)
{
PARAMETRIZE { species = sSilvallyFormSpeciesIdTable[j]; }
}
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_REFLECT_TYPE); }
} SCENE {
MESSAGE("Wobbuffet used Reflect Type!");
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Reflect Type does not affect Pokémon with no types")
{
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[0] == TYPE_WATER);
ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[1] == TYPE_FIGHTING);
GIVEN {
PLAYER(SPECIES_ARCANINE);
OPPONENT(SPECIES_POLIWRATH);
} WHEN {
TURN { MOVE(player, MOVE_BURN_UP); MOVE(opponent, MOVE_REFLECT_TYPE); }
} SCENE {
MESSAGE("Arcanine used Burn Up!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player);
HP_BAR(opponent);
MESSAGE("Arcanine burned itself out!");
MESSAGE("Foe Poliwrath used Reflect Type!");
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Reflect Type copies a target's dual types")
{
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[0] == TYPE_WATER);
ASSUME(gSpeciesInfo[SPECIES_POLIWRATH].types[1] == TYPE_FIGHTING);
GIVEN {
PLAYER(SPECIES_ARCANINE);
OPPONENT(SPECIES_POLIWRATH);
} WHEN {
TURN { MOVE(player, MOVE_REFLECT_TYPE); }
} SCENE {
MESSAGE("Arcanine used Reflect Type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, player);
MESSAGE("Arcanine's type changed to match the Foe Poliwrath's!");
} THEN {
EXPECT_EQ(player->type1, TYPE_WATER);
EXPECT_EQ(player->type2, TYPE_FIGHTING);
EXPECT_EQ(player->type3, TYPE_MYSTERY);
}
}
SINGLE_BATTLE_TEST("Reflect Type copies a target's pure type")
{
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_SUDOWOODO].types[0] == TYPE_ROCK);
ASSUME(gSpeciesInfo[SPECIES_SUDOWOODO].types[1] == TYPE_ROCK);
GIVEN {
PLAYER(SPECIES_ARCANINE);
OPPONENT(SPECIES_SUDOWOODO);
} WHEN {
TURN { MOVE(player, MOVE_REFLECT_TYPE); }
} SCENE {
MESSAGE("Arcanine used Reflect Type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, player);
MESSAGE("Arcanine's type changed to match the Foe Sudowoodo's!");
} THEN {
EXPECT_EQ(player->type1, TYPE_ROCK);
EXPECT_EQ(player->type2, TYPE_ROCK);
EXPECT_EQ(player->type3, TYPE_MYSTERY);
}
}
SINGLE_BATTLE_TEST("Reflect Type defaults to Normal type for the user's type1 and type2 if the target only has a 3rd type")
{
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC);
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ARCANINE);
} WHEN {
TURN { MOVE(opponent, MOVE_BURN_UP); }
TURN { MOVE(player, MOVE_FORESTS_CURSE); }
TURN { MOVE(player, MOVE_REFLECT_TYPE); }
} SCENE {
// Turn 1
MESSAGE("Foe Arcanine used Burn Up!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, opponent);
HP_BAR(player);
MESSAGE("Foe Arcanine burned itself out!");
// Turn 2
MESSAGE("Wobbuffet used Forest'sCurs!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESTS_CURSE, player);
MESSAGE("Grass type was added to Foe Arcanine!");
// Turn 3
MESSAGE("Wobbuffet used Reflect Type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, player);
MESSAGE("Wobbuffet's type changed to match the Foe Arcanine's!");
} THEN {
EXPECT_EQ(player->type1, TYPE_NORMAL);
EXPECT_EQ(player->type2, TYPE_NORMAL);
EXPECT_EQ(player->type3, TYPE_GRASS);
}
}