Skip to content

Commit

Permalink
Merge branch 'RHH/master' into RHH/upcoming
Browse files Browse the repository at this point in the history
# Conflicts:
#	asm/macros/battle_script.inc
#	src/battle_script_commands.c
  • Loading branch information
AsparagusEduardo committed Oct 23, 2023
2 parents 1110d0a + bac135c commit 1137d54
Show file tree
Hide file tree
Showing 14 changed files with 302 additions and 54 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@ ELF = $(ROM:.gba=.elf)
MAP = $(ROM:.gba=.map)
SYM = $(ROM:.gba=.sym)

ifeq ($(MODERN),0)
TEST_OBJ_DIR_NAME := build/test
else
TEST_OBJ_DIR_NAME := build/modern-test
endif
TESTELF = $(ROM:.gba=-test.elf)
HEADLESSELF = $(ROM:.gba=-test-headless.elf)

Expand Down
10 changes: 5 additions & 5 deletions asm/macros/battle_script.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1456,6 +1456,11 @@
.4byte \jumpInstr
.endm

.macro tryreflecttype failInstr:req
callnative BS_TryReflectType
.4byte \failInstr
.endm

@ Used to active a different Max Move effects.
.macro setmaxmoveeffect
callnative BS_SetMaxMoveEffect
Expand Down Expand Up @@ -1729,11 +1734,6 @@
.4byte \failInstr
.endm

.macro tryreflecttype failInstr:req
various BS_ATTACKER, VARIOUS_TRY_REFLECT_TYPE
.4byte \failInstr
.endm

.macro trysoak failInstr:req
various BS_ATTACKER, VARIOUS_TRY_SOAK
.4byte \failInstr
Expand Down
4 changes: 4 additions & 0 deletions include/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -753,12 +753,16 @@ STATIC_ASSERT(sizeof(((struct BattleStruct *)0)->palaceFlags) * 8 >= MAX_BATTLER
#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_TYPELESS(battlerId)(GetBattlerType(battlerId, 0) == TYPE_MYSTERY && GetBattlerType(battlerId, 1) == TYPE_MYSTERY && GetBattlerType(battlerId, 2) == TYPE_MYSTERY)

#define SET_BATTLER_TYPE(battlerId, type) \
{ \
gBattleMons[battlerId].type1 = type; \
gBattleMons[battlerId].type2 = type; \
gBattleMons[battlerId].type3 = TYPE_MYSTERY; \
}

#define RESTORE_BATTLER_TYPE(battlerId) \
{ \
gBattleMons[battlerId].type1 = gSpeciesInfo[gBattleMons[battlerId].species].types[0]; \
Expand Down
2 changes: 1 addition & 1 deletion include/config/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
#define B_SHADOW_TAG_ESCAPE GEN_LATEST // In Gen4+, if both sides have a Pokémon with Shadow Tag, all battlers can escape. Before, neither side could escape this situation.
#define B_MOODY_ACC_EVASION GEN_LATEST // In Gen8, Moody CANNOT raise Accuracy and Evasion anymore.
#define B_FLASH_FIRE_FROZEN GEN_LATEST // In Gen5+, Flash Fire can trigger even when frozen, when it couldn't before.
#define B_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8, if a Pokémon with Synchronize is leading the party, it's 100% guaranteed that wild Pokémon will have the same ability, as opposed to 50% previously.
#define B_SYNCHRONIZE_NATURE GEN_LATEST // In Gen8, if a Pokémon with Synchronize is leading the party, it's 100% guaranteed that wild Pokémon will have the same Nature, as opposed to 50% previously. In Gen9, it has no out-of-battle effect.
#define B_SYNCHRONIZE_TOXIC GEN_LATEST // In Gen5+, if a Pokémon with Synchronize is badly poisoned, the opponent will also become badly poisoned. Previously, the opponent would become regular poisoned.
#define B_UPDATED_INTIMIDATE GEN_LATEST // In Gen8, Intimidate doesn't work on opponents with the Inner Focus, Scrappy, Own Tempo or Oblivious abilities. It also activates Rattled.
#define B_OBLIVIOUS_TAUNT GEN_LATEST // In Gen6+, Pokémon with Oblivious can't be taunted.
Expand Down
1 change: 1 addition & 0 deletions include/test/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ struct TestRunnerState
u8 expectedResult;
bool8 expectLeaks:1;
bool8 inBenchmark:1;
bool8 tearDown:1;
u32 timeoutSeconds;
};

Expand Down
2 changes: 1 addition & 1 deletion src/battle_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1883,7 +1883,7 @@ static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i)
void ModifyPersonalityForNature(u32 *personality, u32 newNature)
{
u32 nature = GetNatureFromPersonality(*personality);
s32 diff = abs(nature - newNature);
s32 diff = abs((s32)nature - (s32)newNature);
s32 sign = (nature > newNature) ? 1 : -1;
if (diff > NUM_NATURES / 2)
{
Expand Down
78 changes: 46 additions & 32 deletions src/battle_script_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -9398,38 +9398,6 @@ static void Cmd_various(void)
}
return;
}
case VARIOUS_TRY_REFLECT_TYPE:
{
VARIOUS_ARGS(const u8 *failInstr);
if (GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species) == SPECIES_ARCEUS
|| GET_BASE_SPECIES_ID(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:
{
VARIOUS_ARGS(const u8 *failInstr);
Expand Down Expand Up @@ -16250,3 +16218,49 @@ void BS_JumpIfTerrainAffected(void)
else
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;
}
}
4 changes: 2 additions & 2 deletions src/data/items.h
Original file line number Diff line number Diff line change
Expand Up @@ -6041,8 +6041,8 @@ const struct Item gItems[] =
.holdEffectParam = 10,
.description = sRazorFangDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.type = EVO_HELD_ITEM_TYPE,
.fieldUseFunc = EVO_HELD_ITEM_FIELD_FUNC,
.flingPower = 30,
},

Expand Down
1 change: 1 addition & 0 deletions src/data/pokemon/item_effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,7 @@ const u8 *const gItemEffectTable[ITEMS_COUNT] =
[ITEM_METAL_COAT] = gItemEffect_EvoItem,
[ITEM_KINGS_ROCK] = gItemEffect_EvoItem,
[ITEM_RAZOR_CLAW] = gItemEffect_EvoItem,
[ITEM_RAZOR_FANG] = gItemEffect_EvoItem,
[ITEM_AUSPICIOUS_ARMOR] = gItemEffect_EvoItem,
[ITEM_MALICIOUS_ARMOR] = gItemEffect_EvoItem,
[ITEM_SCROLL_OF_DARKNESS] = gItemEffect_EvoItem,
Expand Down
2 changes: 2 additions & 0 deletions src/wild_encounter.c
Original file line number Diff line number Diff line change
Expand Up @@ -415,13 +415,15 @@ static u8 PickWildMonNature(void)
}
}
}
#if B_SYNCHRONIZE_NATURE < GEN_9
// check synchronize for a pokemon with the same ability
if (!GetMonData(&gPlayerParty[0], MON_DATA_SANITY_IS_EGG)
&& GetMonAbility(&gPlayerParty[0]) == ABILITY_SYNCHRONIZE
&& (B_SYNCHRONIZE_NATURE >= GEN_8 || Random() % 2 == 0))
{
return GetMonData(&gPlayerParty[0], MON_DATA_PERSONALITY) % NUM_NATURES;
}
#endif

// random nature
return Random() % NUM_NATURES;
Expand Down
186 changes: 186 additions & 0 deletions test/battle/move_effect/reflect_type.c
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit 1137d54

Please sign in to comment.