From cc882e5de2ffa56d30febf4c00a262190a70f19b Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Jan 2026 01:00:03 -0500 Subject: [PATCH 01/24] Added more descriptive names for Match Call text flags --- src/pokenav_match_call_data.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/pokenav_match_call_data.c b/src/pokenav_match_call_data.c index 3f47550f17..06bc6111f7 100644 --- a/src/pokenav_match_call_data.c +++ b/src/pokenav_match_call_data.c @@ -28,8 +28,8 @@ enum typedef struct MatchCallTextDataStruct { const u8 *text; - u16 flag; - u16 flag2; + u16 availabilityFlag; + u16 flagToSetOnCompletion; } match_call_text_data_t; struct MatchCallStructCommon { @@ -993,12 +993,12 @@ static void MatchCall_BufferCallMessageText(const match_call_text_data_t *textDa i--; while (i) { - if (textData[i].flag != 0xFFFF && FlagGet(textData[i].flag) == TRUE) + if (textData[i].availabilityFlag != 0xFFFF && FlagGet(textData[i].flag) == TRUE) break; i--; } - if (textData[i].flag2 != 0xFFFF) - FlagSet(textData[i].flag2); + if (textData[i].flagToSetOnCompletion != 0xFFFF) + FlagSet(textData[i].flagToSetOnCompletion); StringExpandPlaceholders(dest, textData[i].text); } @@ -1007,17 +1007,17 @@ static void MatchCall_BufferCallMessageTextByRematchTeam(const match_call_text_d u32 i; for (i = 0; textData[i].text != NULL; i++) { - if (textData[i].flag == 0xFFFE) + if (textData[i].availabilityFlag == 0xFFFE) break; - if (textData[i].flag != 0xFFFF && !FlagGet(textData[i].flag)) + if (textData[i].availabilityFlag != 0xFFFF && !FlagGet(textData[i].flag)) break; } - if (textData[i].flag != 0xFFFE) + if (textData[i].availabilityFlag != 0xFFFE) { if (i) i--; - if (textData[i].flag2 != 0xFFFF) - FlagSet(textData[i].flag2); + if (textData[i].flagToSetOnCompletion != 0xFFFF) + FlagSet(textData[i].flagToSetOnCompletion); StringExpandPlaceholders(dest, textData[i].text); } else From 509f652ac62384239ebaab3859a8fd78b5840259 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Jan 2026 01:08:12 -0500 Subject: [PATCH 02/24] Added defines for 0xFFFF --- src/pokenav_match_call_data.c | 265 +++++++++++++++++----------------- 1 file changed, 134 insertions(+), 131 deletions(-) diff --git a/src/pokenav_match_call_data.c b/src/pokenav_match_call_data.c index 06bc6111f7..9a27058e2c 100644 --- a/src/pokenav_match_call_data.c +++ b/src/pokenav_match_call_data.c @@ -157,21 +157,24 @@ static void MatchCall_BufferCallMessageText(const match_call_text_data_t *, u8 * static void MatchCall_BufferCallMessageTextByRematchTeam(const match_call_text_data_t *, u16, u8 *); static void MatchCall_GetNameAndDescByRematchIdx(u32, const u8 **, const u8 **); +#define ALWAYS_AVAILABLE 0xFFFF +#define NO_FLAG_TO_SET 0xFFFF + // .rodata static const match_call_text_data_t sMrStoneTextScripts[] = { - { MatchCall_Text_MrStone1, 0xFFFF, FLAG_ENABLE_MR_STONE_POKENAV }, - { MatchCall_Text_MrStone2, FLAG_ENABLE_MR_STONE_POKENAV, 0xFFFF }, - { MatchCall_Text_MrStone3, FLAG_DELIVERED_STEVEN_LETTER, 0xFFFF }, - { MatchCall_Text_MrStone4, FLAG_RECEIVED_EXP_SHARE, 0xFFFF }, - { MatchCall_Text_MrStone5, FLAG_RECEIVED_HM_STRENGTH, 0xFFFF }, - { MatchCall_Text_MrStone6, FLAG_DEFEATED_PETALBURG_GYM, 0xFFFF }, - { MatchCall_Text_MrStone7, FLAG_RECEIVED_CASTFORM, 0xFFFF }, - { MatchCall_Text_MrStone8, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, 0xFFFF }, - { MatchCall_Text_MrStone9, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, 0xFFFF }, - { MatchCall_Text_MrStone10, FLAG_DEFEATED_SOOTOPOLIS_GYM, 0xFFFF }, - { MatchCall_Text_MrStone11, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_MrStone1, ALWAYS_AVAILABLE, FLAG_ENABLE_MR_STONE_POKENAV }, + { MatchCall_Text_MrStone2, FLAG_ENABLE_MR_STONE_POKENAV, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone3, FLAG_DELIVERED_STEVEN_LETTER, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone4, FLAG_RECEIVED_EXP_SHARE, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone5, FLAG_RECEIVED_HM_STRENGTH, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone6, FLAG_DEFEATED_PETALBURG_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone7, FLAG_RECEIVED_CASTFORM, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone8, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone9, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone10, FLAG_DEFEATED_SOOTOPOLIS_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone11, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructNPC sMrStoneMatchCallHeader = @@ -185,16 +188,16 @@ static const struct MatchCallStructNPC sMrStoneMatchCallHeader = }; static const match_call_text_data_t sNormanTextScripts[] = { - { MatchCall_Text_Norman1, FLAG_ENABLE_NORMAN_MATCH_CALL, 0xFFFF }, - { MatchCall_Text_Norman2, FLAG_DEFEATED_DEWFORD_GYM, 0xFFFF }, - { MatchCall_Text_Norman3, FLAG_DEFEATED_LAVARIDGE_GYM, 0xFFFF }, - { MatchCall_Text_Norman4, FLAG_DEFEATED_PETALBURG_GYM, 0xFFFF }, - { MatchCall_Text_Norman5, FLAG_RECEIVED_RED_OR_BLUE_ORB, 0xFFFF }, - { MatchCall_Text_Norman6, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Norman7, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { MatchCall_Text_Norman8, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { MatchCall_Text_Norman9, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Norman1, FLAG_ENABLE_NORMAN_MATCH_CALL, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman2, FLAG_DEFEATED_DEWFORD_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman3, FLAG_DEFEATED_LAVARIDGE_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman4, FLAG_DEFEATED_PETALBURG_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman5, FLAG_RECEIVED_RED_OR_BLUE_ORB, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman6, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman7, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman8, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman9, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sNormanMatchCallHeader = @@ -218,10 +221,10 @@ static const struct MatchCallBirch sProfBirchMatchCallHeader = }; static const match_call_text_data_t sMomTextScripts[] = { - { MatchCall_Text_Mom1, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Mom2, FLAG_DEFEATED_PETALBURG_GYM, 0xFFFF }, - { MatchCall_Text_Mom3, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Mom1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Mom2, FLAG_DEFEATED_PETALBURG_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Mom3, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructNPC sMomMatchCallHeader = @@ -235,14 +238,14 @@ static const struct MatchCallStructNPC sMomMatchCallHeader = }; static const match_call_text_data_t sStevenTextScripts[] = { - { MatchCall_Text_Steven1, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Steven2, FLAG_RUSTURF_TUNNEL_OPENED, 0xFFFF }, - { MatchCall_Text_Steven3, FLAG_RECEIVED_RED_OR_BLUE_ORB, 0xFFFF }, - { MatchCall_Text_Steven4, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, 0xFFFF }, - { MatchCall_Text_Steven5, FLAG_DEFEATED_MOSSDEEP_GYM, 0xFFFF }, - { MatchCall_Text_Steven6, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, 0xFFFF }, - { MatchCall_Text_Steven7, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF }, + { MatchCall_Text_Steven1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Steven2, FLAG_RUSTURF_TUNNEL_OPENED, NO_FLAG_TO_SET }, + { MatchCall_Text_Steven3, FLAG_RECEIVED_RED_OR_BLUE_ORB, NO_FLAG_TO_SET }, + { MatchCall_Text_Steven4, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, NO_FLAG_TO_SET }, + { MatchCall_Text_Steven5, FLAG_DEFEATED_MOSSDEEP_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Steven6, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, NO_FLAG_TO_SET }, + { MatchCall_Text_Steven7, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, }; static const struct MatchCallStructNPC sStevenMatchCallHeader = @@ -256,22 +259,22 @@ static const struct MatchCallStructNPC sStevenMatchCallHeader = }; static const match_call_text_data_t sMayTextScripts[] = { - { MatchCall_Text_May1, 0xFFFF, 0xFFFF }, - { MatchCall_Text_May2, FLAG_DEFEATED_DEWFORD_GYM, 0xFFFF }, - { MatchCall_Text_May3, FLAG_DELIVERED_DEVON_GOODS, 0xFFFF }, - { MatchCall_Text_May4, FLAG_HIDE_MAUVILLE_CITY_WALLY, 0xFFFF }, - { MatchCall_Text_May5, FLAG_RECEIVED_HM_STRENGTH, 0xFFFF }, - { MatchCall_Text_May6, FLAG_DEFEATED_LAVARIDGE_GYM, 0xFFFF }, - { MatchCall_Text_May7, FLAG_DEFEATED_PETALBURG_GYM, 0xFFFF }, - { MatchCall_Text_May8, FLAG_RECEIVED_CASTFORM, 0xFFFF }, - { MatchCall_Text_May9, FLAG_RECEIVED_RED_OR_BLUE_ORB, 0xFFFF }, - { MatchCall_Text_May10, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, 0xFFFF }, - { MatchCall_Text_May11, FLAG_MET_TEAM_AQUA_HARBOR, 0xFFFF }, - { MatchCall_Text_May12, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, 0xFFFF }, - { MatchCall_Text_May13, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, 0xFFFF }, - { MatchCall_Text_May14, FLAG_DEFEATED_SOOTOPOLIS_GYM, 0xFFFF }, - { MatchCall_Text_May15, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_May1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_May2, FLAG_DEFEATED_DEWFORD_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_May3, FLAG_DELIVERED_DEVON_GOODS, NO_FLAG_TO_SET }, + { MatchCall_Text_May4, FLAG_HIDE_MAUVILLE_CITY_WALLY, NO_FLAG_TO_SET }, + { MatchCall_Text_May5, FLAG_RECEIVED_HM_STRENGTH, NO_FLAG_TO_SET }, + { MatchCall_Text_May6, FLAG_DEFEATED_LAVARIDGE_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_May7, FLAG_DEFEATED_PETALBURG_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_May8, FLAG_RECEIVED_CASTFORM, NO_FLAG_TO_SET }, + { MatchCall_Text_May9, FLAG_RECEIVED_RED_OR_BLUE_ORB, NO_FLAG_TO_SET }, + { MatchCall_Text_May10, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, NO_FLAG_TO_SET }, + { MatchCall_Text_May11, FLAG_MET_TEAM_AQUA_HARBOR, NO_FLAG_TO_SET }, + { MatchCall_Text_May12, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, NO_FLAG_TO_SET }, + { MatchCall_Text_May13, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, NO_FLAG_TO_SET }, + { MatchCall_Text_May14, FLAG_DEFEATED_SOOTOPOLIS_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_May15, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallRival sMayMatchCallHeader = @@ -285,22 +288,22 @@ static const struct MatchCallRival sMayMatchCallHeader = }; static const match_call_text_data_t sBrendanTextScripts[] = { - { MatchCall_Text_Brendan1, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Brendan2, FLAG_DEFEATED_DEWFORD_GYM, 0xFFFF }, - { MatchCall_Text_Brendan3, FLAG_DELIVERED_DEVON_GOODS, 0xFFFF }, - { MatchCall_Text_Brendan4, FLAG_HIDE_MAUVILLE_CITY_WALLY, 0xFFFF }, - { MatchCall_Text_Brendan5, FLAG_RECEIVED_HM_STRENGTH, 0xFFFF }, - { MatchCall_Text_Brendan6, FLAG_DEFEATED_LAVARIDGE_GYM, 0xFFFF }, - { MatchCall_Text_Brendan7, FLAG_DEFEATED_PETALBURG_GYM, 0xFFFF }, - { MatchCall_Text_Brendan8, FLAG_RECEIVED_CASTFORM, 0xFFFF }, - { MatchCall_Text_Brendan9, FLAG_RECEIVED_RED_OR_BLUE_ORB, 0xFFFF }, - { MatchCall_Text_Brendan10, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, 0xFFFF }, - { MatchCall_Text_Brendan11, FLAG_MET_TEAM_AQUA_HARBOR, 0xFFFF }, - { MatchCall_Text_Brendan12, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, 0xFFFF }, - { MatchCall_Text_Brendan13, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, 0xFFFF }, - { MatchCall_Text_Brendan14, FLAG_DEFEATED_SOOTOPOLIS_GYM, 0xFFFF }, - { MatchCall_Text_Brendan15, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Brendan1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan2, FLAG_DEFEATED_DEWFORD_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan3, FLAG_DELIVERED_DEVON_GOODS, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan4, FLAG_HIDE_MAUVILLE_CITY_WALLY, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan5, FLAG_RECEIVED_HM_STRENGTH, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan6, FLAG_DEFEATED_LAVARIDGE_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan7, FLAG_DEFEATED_PETALBURG_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan8, FLAG_RECEIVED_CASTFORM, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan9, FLAG_RECEIVED_RED_OR_BLUE_ORB, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan10, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan11, FLAG_MET_TEAM_AQUA_HARBOR, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan12, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan13, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan14, FLAG_DEFEATED_SOOTOPOLIS_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan15, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallRival sBrendanMatchCallHeader = @@ -314,14 +317,14 @@ static const struct MatchCallRival sBrendanMatchCallHeader = }; static const match_call_text_data_t sWallyTextScripts[] = { - { MatchCall_Text_Wally1, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Wally2, FLAG_RUSTURF_TUNNEL_OPENED, 0xFFFF }, - { MatchCall_Text_Wally3, FLAG_DEFEATED_LAVARIDGE_GYM, 0xFFFF }, - { MatchCall_Text_Wally4, FLAG_RECEIVED_CASTFORM, 0xFFFF }, - { MatchCall_Text_Wally5, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, 0xFFFF }, - { MatchCall_Text_Wally6, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, 0xFFFF }, - { MatchCall_Text_Wally7, FLAG_DEFEATED_WALLY_VICTORY_ROAD, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Wally1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Wally2, FLAG_RUSTURF_TUNNEL_OPENED, NO_FLAG_TO_SET }, + { MatchCall_Text_Wally3, FLAG_DEFEATED_LAVARIDGE_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Wally4, FLAG_RECEIVED_CASTFORM, NO_FLAG_TO_SET }, + { MatchCall_Text_Wally5, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, NO_FLAG_TO_SET }, + { MatchCall_Text_Wally6, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, NO_FLAG_TO_SET }, + { MatchCall_Text_Wally7, FLAG_DEFEATED_WALLY_VICTORY_ROAD, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallLocationOverride sWallyLocationData[] = { @@ -343,14 +346,14 @@ static const struct MatchCallWally sWallyMatchCallHeader = }; static const match_call_text_data_t sScottTextScripts[] = { - { MatchCall_Text_Scott1, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Scott2, FLAG_DEFEATED_EVIL_TEAM_MT_CHIMNEY, 0xFFFF }, - { MatchCall_Text_Scott3, FLAG_RECEIVED_CASTFORM, 0xFFFF }, - { MatchCall_Text_Scott4, FLAG_RECEIVED_RED_OR_BLUE_ORB, 0xFFFF }, - { MatchCall_Text_Scott5, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, 0xFFFF }, - { MatchCall_Text_Scott6, FLAG_DEFEATED_SOOTOPOLIS_GYM, 0xFFFF }, - { MatchCall_Text_Scott7, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Scott1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Scott2, FLAG_DEFEATED_EVIL_TEAM_MT_CHIMNEY, NO_FLAG_TO_SET }, + { MatchCall_Text_Scott3, FLAG_RECEIVED_CASTFORM, NO_FLAG_TO_SET }, + { MatchCall_Text_Scott4, FLAG_RECEIVED_RED_OR_BLUE_ORB, NO_FLAG_TO_SET }, + { MatchCall_Text_Scott5, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, NO_FLAG_TO_SET }, + { MatchCall_Text_Scott6, FLAG_DEFEATED_SOOTOPOLIS_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Scott7, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; @@ -365,11 +368,11 @@ static const struct MatchCallStructNPC sScottMatchCallHeader = }; static const match_call_text_data_t sRoxanneTextScripts[] = { - { MatchCall_Text_Roxanne1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Roxanne2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Roxanne3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Roxanne4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Roxanne1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Roxanne2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Roxanne3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Roxanne4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sRoxanneMatchCallHeader = @@ -384,11 +387,11 @@ static const struct MatchCallStructTrainer sRoxanneMatchCallHeader = }; static const match_call_text_data_t sBrawlyTextScripts[] = { - { MatchCall_Text_Brawly1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Brawly2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Brawly3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Brawly4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Brawly1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Brawly2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Brawly3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Brawly4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sBrawlyMatchCallHeader = @@ -403,11 +406,11 @@ static const struct MatchCallStructTrainer sBrawlyMatchCallHeader = }; static const match_call_text_data_t sWattsonTextScripts[] = { - { MatchCall_Text_Wattson1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Wattson2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Wattson3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Wattson4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Wattson1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Wattson2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Wattson3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Wattson4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sWattsonMatchCallHeader = @@ -422,11 +425,11 @@ static const struct MatchCallStructTrainer sWattsonMatchCallHeader = }; static const match_call_text_data_t sFlanneryTextScripts[] = { - { MatchCall_Text_Flannery1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Flannery2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Flannery3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Flannery4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Flannery1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Flannery2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Flannery3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Flannery4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sFlanneryMatchCallHeader = @@ -441,11 +444,11 @@ static const struct MatchCallStructTrainer sFlanneryMatchCallHeader = }; static const match_call_text_data_t sWinonaTextScripts[] = { - { MatchCall_Text_Winona1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Winona2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Winona3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Winona4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Winona1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Winona2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Winona3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Winona4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sWinonaMatchCallHeader = @@ -460,11 +463,11 @@ static const struct MatchCallStructTrainer sWinonaMatchCallHeader = }; static const match_call_text_data_t sTateLizaTextScripts[] = { - { MatchCall_Text_TateLiza1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_TateLiza2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_TateLiza3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_TateLiza4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_TateLiza1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_TateLiza2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_TateLiza3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_TateLiza4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sTateLizaMatchCallHeader = @@ -479,11 +482,11 @@ static const struct MatchCallStructTrainer sTateLizaMatchCallHeader = }; static const match_call_text_data_t sJuanTextScripts[] = { - { MatchCall_Text_Juan1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Juan2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Juan3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Juan4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Juan1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Juan2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Juan3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Juan4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sJuanMatchCallHeader = @@ -498,8 +501,8 @@ static const struct MatchCallStructTrainer sJuanMatchCallHeader = }; static const match_call_text_data_t sSidneyTextScripts[] = { - { MatchCall_Text_Sidney, 0xFFFF, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Sidney, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sSidneyMatchCallHeader = @@ -514,8 +517,8 @@ static const struct MatchCallStructTrainer sSidneyMatchCallHeader = }; static const match_call_text_data_t sPhoebeTextScripts[] = { - { MatchCall_Text_Phoebe, 0xFFFF, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Phoebe, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sPhoebeMatchCallHeader = @@ -530,8 +533,8 @@ static const struct MatchCallStructTrainer sPhoebeMatchCallHeader = }; static const match_call_text_data_t sGlaciaTextScripts[] = { - { MatchCall_Text_Glacia, 0xFFFF, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Glacia, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sGlaciaMatchCallHeader = @@ -546,8 +549,8 @@ static const struct MatchCallStructTrainer sGlaciaMatchCallHeader = }; static const match_call_text_data_t sDrakeTextScripts[] = { - { MatchCall_Text_Drake, 0xFFFF, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Drake, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sDrakeMatchCallHeader = @@ -562,8 +565,8 @@ static const struct MatchCallStructTrainer sDrakeMatchCallHeader = }; static const match_call_text_data_t sWallaceTextScripts[] = { - { MatchCall_Text_Wallace, 0xFFFF, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Wallace, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } }; static const struct MatchCallStructTrainer sWallaceMatchCallHeader = @@ -993,11 +996,11 @@ static void MatchCall_BufferCallMessageText(const match_call_text_data_t *textDa i--; while (i) { - if (textData[i].availabilityFlag != 0xFFFF && FlagGet(textData[i].flag) == TRUE) + if (textData[i].availabilityFlag != ALWAYS_AVAILABLE && FlagGet(textData[i].flag) == TRUE) break; i--; } - if (textData[i].flagToSetOnCompletion != 0xFFFF) + if (textData[i].flagToSetOnCompletion != NO_FLAG_TO_SET) FlagSet(textData[i].flagToSetOnCompletion); StringExpandPlaceholders(dest, textData[i].text); } @@ -1009,14 +1012,14 @@ static void MatchCall_BufferCallMessageTextByRematchTeam(const match_call_text_d { if (textData[i].availabilityFlag == 0xFFFE) break; - if (textData[i].availabilityFlag != 0xFFFF && !FlagGet(textData[i].flag)) + if (textData[i].availabilityFlag != ALWAYS_AVAILABLE && !FlagGet(textData[i].flag)) break; } if (textData[i].availabilityFlag != 0xFFFE) { if (i) i--; - if (textData[i].flagToSetOnCompletion != 0xFFFF) + if (textData[i].flagToSetOnCompletion != NO_FLAG_TO_SET) FlagSet(textData[i].flagToSetOnCompletion); StringExpandPlaceholders(dest, textData[i].text); } From 294a1b88a2da1dee97a0830825257bec298ce0f3 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Jan 2026 01:16:36 -0500 Subject: [PATCH 03/24] Replaced two more instances of flag --- src/pokenav_match_call_data.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pokenav_match_call_data.c b/src/pokenav_match_call_data.c index 9a27058e2c..1ac672ffa6 100644 --- a/src/pokenav_match_call_data.c +++ b/src/pokenav_match_call_data.c @@ -996,7 +996,7 @@ static void MatchCall_BufferCallMessageText(const match_call_text_data_t *textDa i--; while (i) { - if (textData[i].availabilityFlag != ALWAYS_AVAILABLE && FlagGet(textData[i].flag) == TRUE) + if (textData[i].availabilityFlag != ALWAYS_AVAILABLE && FlagGet(textData[i].availabilityFlag) == TRUE) break; i--; } @@ -1012,7 +1012,7 @@ static void MatchCall_BufferCallMessageTextByRematchTeam(const match_call_text_d { if (textData[i].availabilityFlag == 0xFFFE) break; - if (textData[i].availabilityFlag != ALWAYS_AVAILABLE && !FlagGet(textData[i].flag)) + if (textData[i].availabilityFlag != ALWAYS_AVAILABLE && !FlagGet(textData[i].availabilityFlag)) break; } if (textData[i].availabilityFlag != 0xFFFE) From 902a37ef238018cb0a7fe96aef17aec3c6dc8dda Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Jan 2026 01:23:19 -0500 Subject: [PATCH 04/24] Added MATCH_CALL_TEXT_END define --- src/pokenav_match_call_data.c | 41 ++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/pokenav_match_call_data.c b/src/pokenav_match_call_data.c index 1ac672ffa6..708da31b56 100644 --- a/src/pokenav_match_call_data.c +++ b/src/pokenav_match_call_data.c @@ -159,6 +159,7 @@ static void MatchCall_GetNameAndDescByRematchIdx(u32, const u8 **, const u8 **); #define ALWAYS_AVAILABLE 0xFFFF #define NO_FLAG_TO_SET 0xFFFF +#define MATCH_CALL_TEXT_END {NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET} // .rodata @@ -174,7 +175,7 @@ static const match_call_text_data_t sMrStoneTextScripts[] = { { MatchCall_Text_MrStone9, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, NO_FLAG_TO_SET }, { MatchCall_Text_MrStone10, FLAG_DEFEATED_SOOTOPOLIS_GYM, NO_FLAG_TO_SET }, { MatchCall_Text_MrStone11, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructNPC sMrStoneMatchCallHeader = @@ -197,7 +198,7 @@ static const match_call_text_data_t sNormanTextScripts[] = { { MatchCall_Text_Norman7, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, { MatchCall_Text_Norman8, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, { MatchCall_Text_Norman9, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sNormanMatchCallHeader = @@ -224,7 +225,7 @@ static const match_call_text_data_t sMomTextScripts[] = { { MatchCall_Text_Mom1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Mom2, FLAG_DEFEATED_PETALBURG_GYM, NO_FLAG_TO_SET }, { MatchCall_Text_Mom3, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructNPC sMomMatchCallHeader = @@ -245,7 +246,7 @@ static const match_call_text_data_t sStevenTextScripts[] = { { MatchCall_Text_Steven5, FLAG_DEFEATED_MOSSDEEP_GYM, NO_FLAG_TO_SET }, { MatchCall_Text_Steven6, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, NO_FLAG_TO_SET }, { MatchCall_Text_Steven7, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END, }; static const struct MatchCallStructNPC sStevenMatchCallHeader = @@ -274,7 +275,7 @@ static const match_call_text_data_t sMayTextScripts[] = { { MatchCall_Text_May13, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, NO_FLAG_TO_SET }, { MatchCall_Text_May14, FLAG_DEFEATED_SOOTOPOLIS_GYM, NO_FLAG_TO_SET }, { MatchCall_Text_May15, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallRival sMayMatchCallHeader = @@ -303,7 +304,7 @@ static const match_call_text_data_t sBrendanTextScripts[] = { { MatchCall_Text_Brendan13, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, NO_FLAG_TO_SET }, { MatchCall_Text_Brendan14, FLAG_DEFEATED_SOOTOPOLIS_GYM, NO_FLAG_TO_SET }, { MatchCall_Text_Brendan15, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallRival sBrendanMatchCallHeader = @@ -324,7 +325,7 @@ static const match_call_text_data_t sWallyTextScripts[] = { { MatchCall_Text_Wally5, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, NO_FLAG_TO_SET }, { MatchCall_Text_Wally6, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, NO_FLAG_TO_SET }, { MatchCall_Text_Wally7, FLAG_DEFEATED_WALLY_VICTORY_ROAD, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallLocationOverride sWallyLocationData[] = { @@ -353,7 +354,7 @@ static const match_call_text_data_t sScottTextScripts[] = { { MatchCall_Text_Scott5, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, NO_FLAG_TO_SET }, { MatchCall_Text_Scott6, FLAG_DEFEATED_SOOTOPOLIS_GYM, NO_FLAG_TO_SET }, { MatchCall_Text_Scott7, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; @@ -372,7 +373,7 @@ static const match_call_text_data_t sRoxanneTextScripts[] = { { MatchCall_Text_Roxanne2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Roxanne3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Roxanne4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sRoxanneMatchCallHeader = @@ -391,7 +392,7 @@ static const match_call_text_data_t sBrawlyTextScripts[] = { { MatchCall_Text_Brawly2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Brawly3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Brawly4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sBrawlyMatchCallHeader = @@ -410,7 +411,7 @@ static const match_call_text_data_t sWattsonTextScripts[] = { { MatchCall_Text_Wattson2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Wattson3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Wattson4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sWattsonMatchCallHeader = @@ -429,7 +430,7 @@ static const match_call_text_data_t sFlanneryTextScripts[] = { { MatchCall_Text_Flannery2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Flannery3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Flannery4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sFlanneryMatchCallHeader = @@ -448,7 +449,7 @@ static const match_call_text_data_t sWinonaTextScripts[] = { { MatchCall_Text_Winona2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Winona3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Winona4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sWinonaMatchCallHeader = @@ -467,7 +468,7 @@ static const match_call_text_data_t sTateLizaTextScripts[] = { { MatchCall_Text_TateLiza2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_TateLiza3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_TateLiza4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sTateLizaMatchCallHeader = @@ -486,7 +487,7 @@ static const match_call_text_data_t sJuanTextScripts[] = { { MatchCall_Text_Juan2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Juan3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, { MatchCall_Text_Juan4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sJuanMatchCallHeader = @@ -502,7 +503,7 @@ static const struct MatchCallStructTrainer sJuanMatchCallHeader = static const match_call_text_data_t sSidneyTextScripts[] = { { MatchCall_Text_Sidney, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sSidneyMatchCallHeader = @@ -518,7 +519,7 @@ static const struct MatchCallStructTrainer sSidneyMatchCallHeader = static const match_call_text_data_t sPhoebeTextScripts[] = { { MatchCall_Text_Phoebe, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sPhoebeMatchCallHeader = @@ -534,7 +535,7 @@ static const struct MatchCallStructTrainer sPhoebeMatchCallHeader = static const match_call_text_data_t sGlaciaTextScripts[] = { { MatchCall_Text_Glacia, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sGlaciaMatchCallHeader = @@ -550,7 +551,7 @@ static const struct MatchCallStructTrainer sGlaciaMatchCallHeader = static const match_call_text_data_t sDrakeTextScripts[] = { { MatchCall_Text_Drake, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sDrakeMatchCallHeader = @@ -566,7 +567,7 @@ static const struct MatchCallStructTrainer sDrakeMatchCallHeader = static const match_call_text_data_t sWallaceTextScripts[] = { { MatchCall_Text_Wallace, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, - { NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET } + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sWallaceMatchCallHeader = From 064ca5c24314780fed46ba8e291d73107bd5b7d7 Mon Sep 17 00:00:00 2001 From: Frank DeBlasio <35279583+fdeblasio@users.noreply.github.com> Date: Fri, 6 Feb 2026 10:08:20 -0500 Subject: [PATCH 05/24] Added macro for Surf/Muddy Water's animation (#2232) --- asm/macros/battle_anim_script.inc | 4 ++++ data/battle_anim_scripts.s | 4 ++-- include/constants/battle_anim.h | 4 ++++ src/battle_anim_water.c | 7 +++++-- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/asm/macros/battle_anim_script.inc b/asm/macros/battle_anim_script.inc index d46638949b..e6bb3d6705 100644 --- a/asm/macros/battle_anim_script.inc +++ b/asm/macros/battle_anim_script.inc @@ -579,3 +579,7 @@ .macro shake_battle_platforms priority=2, x_offset:req, y_offset:req, shakes:req, delay:req createvisualtask AnimTask_ShakeBattlePlatforms, \priority, \x_offset, \y_offset, \shakes, \delay .endm + + .macro create_surf_wave priority=2, palette:req + createvisualtask AnimTask_CreateSurfWave, \priority, \palette + .endm diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 884360034c..4fc244acba 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -6296,7 +6296,7 @@ Move_CRABHAMMER: end Move_SURF: - createvisualtask AnimTask_CreateSurfWave, 2, FALSE + create_surf_wave palette=ANIM_SURF_PAL_SURF delay 24 panse SE_M_SURF, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, +2, 0 waitforvisualfinish @@ -8594,7 +8594,7 @@ ArmThrustLeft: Move_MUDDY_WATER: panse SE_M_WHIRLPOOL, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, +2, 0 - createvisualtask AnimTask_CreateSurfWave, 2, TRUE + create_surf_wave palette=ANIM_SURF_PAL_MUDDY_WATER waitforvisualfinish end diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index 9e63874312..b293890eca 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -420,6 +420,10 @@ #define ANIM_WEATHER_SANDSTORM 3 #define ANIM_WEATHER_HAIL 4 +// Surf wave palettes +#define ANIM_SURF_PAL_SURF 0 +#define ANIM_SURF_PAL_MUDDY_WATER 1 + // Flags given to various functions to indicate which palettes to consider. // Handled by UnpackSelectedBattlePalettes #define F_PAL_BG (1 << 0) diff --git a/src/battle_anim_water.c b/src/battle_anim_water.c index 883cf5825e..1518d7e988 100644 --- a/src/battle_anim_water.c +++ b/src/battle_anim_water.c @@ -1,6 +1,7 @@ #include "global.h" #include "battle.h" #include "battle_anim.h" +#include "battle_anim_internal.h" #include "gpu_regs.h" #include "graphics.h" #include "palette.h" @@ -514,7 +515,7 @@ static void AnimRainDrop_Step(struct Sprite *sprite) if (++sprite->data[0] <= 13) { // - // Make the raindrop fall, but only until it reaches the + // Make the raindrop fall, but only until it reaches the // impact/splash frames of its animation. // sprite->x2++; @@ -812,6 +813,8 @@ static void AnimSmallBubblePair_Step(struct Sprite *sprite) void AnimTask_CreateSurfWave(u8 taskId) { + CMD_ARGS(palette); + struct BattleAnimBgData animBg; u8 taskId2; u16 *x; @@ -838,7 +841,7 @@ void AnimTask_CreateSurfWave(u8 taskId) AnimLoadCompressedBgTilemapHandleContest(&animBg, gBattleAnimBgTilemap_SurfContest, TRUE); } AnimLoadCompressedBgGfx(animBg.bgId, gBattleAnimBgImage_Surf, animBg.tilesOffset); - if (gBattleAnimArgs[0] == 0) + if (cmd->palette == ANIM_SURF_PAL_SURF) LoadCompressedPalette(gBattleAnimBgPalette_Surf, BG_PLTT_ID(animBg.paletteId), PLTT_SIZE_4BPP); else LoadCompressedPalette(gBattleAnimBackgroundImageMuddyWater_Pal, BG_PLTT_ID(animBg.paletteId), PLTT_SIZE_4BPP); From 86a159a1eb56d6f5e85b47c33ff176784117cc69 Mon Sep 17 00:00:00 2001 From: Kildemal <206095739+izrofid@users.noreply.github.com> Date: Sun, 15 Feb 2026 20:33:12 +0530 Subject: [PATCH 06/24] chore(linker): add dwarf5 line number section to ld script (#2235) --- ld_script_modern.ld | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ld_script_modern.ld b/ld_script_modern.ld index 788a5736f0..b0eab17c73 100644 --- a/ld_script_modern.ld +++ b/ld_script_modern.ld @@ -150,6 +150,9 @@ SECTIONS { .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } + /* DWARF 5*/ + .debug_line_str 0 : { *(.debug_line_str) } + /* Discard everything not specifically mentioned above. */ /DISCARD/ : { From 25ffb2c12c069ac6b2040f9238b2978f1de72e3f Mon Sep 17 00:00:00 2001 From: Marky <143505183+HashtagMarky@users.noreply.github.com> Date: Fri, 20 Feb 2026 06:17:42 +0000 Subject: [PATCH 07/24] Add enum for Elevations (#2233) --- include/global.fieldmap.h | 9 +++++++++ src/decoration.c | 4 ++-- src/event_object_movement.c | 12 ++++++------ src/field_control_avatar.c | 10 +++++----- src/field_effect_helpers.c | 2 +- src/field_player_avatar.c | 8 ++++---- src/field_specials.c | 2 +- src/overworld.c | 2 +- src/trainer_hill.c | 4 ++-- 9 files changed, 31 insertions(+), 22 deletions(-) diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index eb4a190d01..a3b992db9a 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -11,6 +11,15 @@ #define MAPGRID_COLLISION_SHIFT 10 #define MAPGRID_ELEVATION_SHIFT 12 +enum +{ + ELEVATION_TRANSITION = 0, + ELEVATION_SURF = 1, + ELEVATION_DEFAULT = 3, + ELEVATION_MULTI_LEVEL = 15, + ELEVATION_INVALID = 0xFFFF +}; + #define PACK_METATILE(metatileId) PACK(metatileId, MAPGRID_METATILE_ID_SHIFT, MAPGRID_METATILE_ID_MASK) #define PACK_COLLISION(collision) PACK(collision, MAPGRID_COLLISION_SHIFT, MAPGRID_COLLISION_MASK) #define PACK_ELEVATION(elevation) PACK(elevation, MAPGRID_ELEVATION_SHIFT, MAPGRID_ELEVATION_MASK) diff --git a/src/decoration.c b/src/decoration.c index 2bb97893e1..2399015ab5 100644 --- a/src/decoration.c +++ b/src/decoration.c @@ -1191,7 +1191,7 @@ static void WarpToInitialPosition(u8 taskId) static u16 GetDecorationElevation(u8 decoration, u8 tileIndex) { - u16 elevation = -1; + u16 elevation = ELEVATION_INVALID; switch (decoration) { case DECOR_STAND: @@ -1234,7 +1234,7 @@ static void ShowDecorationOnMap_(u16 mapX, u16 mapY, u8 decWidth, u8 decHeight, overlapsWall = 0; elevation = GetDecorationElevation(gDecorations[decoration].id, j * decWidth + i); - if (elevation != 0xFFFF) + if (elevation != ELEVATION_INVALID) MapGridSetMetatileEntryAt(x, y, (gDecorations[decoration].tiles[j * decWidth + i] + (NUM_TILES_IN_PRIMARY | overlapsWall)) | impassableFlag | elevation); else MapGridSetMetatileIdAt(x, y, (gDecorations[decoration].tiles[j * decWidth + i] + (NUM_TILES_IN_PRIMARY | overlapsWall)) | impassableFlag); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 6997f0b40a..d9ca50faa4 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -2208,7 +2208,7 @@ u8 GetObjectEventIdByPosition(u16 x, u16 y, u8 elevation) static bool8 ObjectEventDoesElevationMatch(struct ObjectEvent *objectEvent, u8 elevation) { - if (objectEvent->currentElevation != 0 && elevation != 0 && objectEvent->currentElevation != elevation) + if (objectEvent->currentElevation != ELEVATION_TRANSITION && elevation != ELEVATION_TRANSITION && objectEvent->currentElevation != elevation) return FALSE; return TRUE; @@ -7708,12 +7708,12 @@ static bool8 IsElevationMismatchAt(u8 elevation, s16 x, s16 y) { u8 mapElevation; - if (elevation == 0) + if (elevation == ELEVATION_TRANSITION) return FALSE; mapElevation = MapGridGetElevationAt(x, y); - if (mapElevation == 0 || mapElevation == 15) + if (mapElevation == ELEVATION_TRANSITION || mapElevation == ELEVATION_MULTI_LEVEL) return FALSE; if (mapElevation != elevation) @@ -7761,12 +7761,12 @@ void ObjectEventUpdateElevation(struct ObjectEvent *objEvent) u8 curElevation = MapGridGetElevationAt(objEvent->currentCoords.x, objEvent->currentCoords.y); u8 prevElevation = MapGridGetElevationAt(objEvent->previousCoords.x, objEvent->previousCoords.y); - if (curElevation == 15 || prevElevation == 15) + if (curElevation == ELEVATION_MULTI_LEVEL || prevElevation == ELEVATION_MULTI_LEVEL) return; objEvent->currentElevation = curElevation; - if (curElevation != 0 && curElevation != 15) + if (curElevation != ELEVATION_TRANSITION && curElevation != ELEVATION_MULTI_LEVEL) objEvent->previousElevation = curElevation; } @@ -7790,7 +7790,7 @@ static void ObjectEventUpdateSubpriority(struct ObjectEvent *objEvent, struct Sp static bool8 AreElevationsCompatible(u8 a, u8 b) { - if (a == 0 || b == 0) + if (a == ELEVATION_TRANSITION || b == ELEVATION_TRANSITION) return TRUE; if (a != b) diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 8ddeda77ac..ed96a87725 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -203,10 +203,10 @@ static void GetInFrontOfPlayerPosition(struct MapPosition *position) GetXYCoordsOneStepInFrontOfPlayer(&position->x, &position->y); PlayerGetDestCoords(&x, &y); - if (MapGridGetElevationAt(x, y) != 0) + if (MapGridGetElevationAt(x, y) != ELEVATION_TRANSITION) position->elevation = PlayerGetElevation(); else - position->elevation = 0; + position->elevation = ELEVATION_TRANSITION; } static u16 GetPlayerCurMetatileBehavior(int runningState) @@ -867,7 +867,7 @@ static s8 GetWarpEventAtPosition(struct MapHeader *mapHeader, u16 x, u16 y, u8 e { if ((u16)warpEvent->x == x && (u16)warpEvent->y == y) { - if (warpEvent->elevation == elevation || warpEvent->elevation == 0) + if (warpEvent->elevation == elevation || warpEvent->elevation == ELEVATION_TRANSITION) return i; } } @@ -904,7 +904,7 @@ static const u8 *GetCoordEventScriptAtPosition(struct MapHeader *mapHeader, u16 { if ((u16)coordEvents[i].x == x && (u16)coordEvents[i].y == y) { - if (coordEvents[i].elevation == elevation || coordEvents[i].elevation == 0) + if (coordEvents[i].elevation == elevation || coordEvents[i].elevation == ELEVATION_TRANSITION) { const u8 *script = TryRunCoordEventScript(&coordEvents[i]); if (script != NULL) @@ -930,7 +930,7 @@ static const struct BgEvent *GetBackgroundEventAtPosition(struct MapHeader *mapH { if ((u16)bgEvents[i].x == x && (u16)bgEvents[i].y == y) { - if (bgEvents[i].elevation == elevation || bgEvents[i].elevation == 0) + if (bgEvents[i].elevation == elevation || bgEvents[i].elevation == ELEVATION_TRANSITION) return &bgEvents[i]; } } diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 320bfea578..a5320be456 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1094,7 +1094,7 @@ void SynchronizeSurfPosition(struct ObjectEvent *playerObj, struct Sprite *sprit for (i = DIR_SOUTH; i <= DIR_EAST; i++, x = sprite->sPrevX, y = sprite->sPrevY) { MoveCoords(i, &x, &y); - if (MapGridGetElevationAt(x, y) == 3) + if (MapGridGetElevationAt(x, y) == ELEVATION_DEFAULT) { // While dismounting the surf blob bobs at a slower rate sprite->sIntervalIdx++; diff --git a/src/field_player_avatar.c b/src/field_player_avatar.c index 3cf6523596..5b22ff74c3 100644 --- a/src/field_player_avatar.c +++ b/src/field_player_avatar.c @@ -729,8 +729,8 @@ static u8 CheckForObjectEventStaticCollision(struct ObjectEvent *objectEvent, s1 static bool8 CanStopSurfing(s16 x, s16 y, u8 direction) { if ((gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING) - && MapGridGetElevationAt(x, y) == 3 - && GetObjectEventIdByPosition(x, y, 3) == OBJECT_EVENTS_COUNT) + && MapGridGetElevationAt(x, y) == ELEVATION_DEFAULT + && GetObjectEventIdByPosition(x, y, ELEVATION_DEFAULT) == OBJECT_EVENTS_COUNT) { CreateStopSurfingTask(direction); return TRUE; @@ -1327,7 +1327,7 @@ bool8 IsPlayerFacingSurfableFishableWater(void) MoveCoords(playerObjEvent->facingDirection, &x, &y); if (GetCollisionAtCoords(playerObjEvent, x, y, playerObjEvent->facingDirection) == COLLISION_ELEVATION_MISMATCH - && PlayerGetElevation() == 3 + && PlayerGetElevation() == ELEVATION_DEFAULT && MetatileBehavior_IsSurfableFishableWater(MapGridGetMetatileBehaviorAt(x, y))) return TRUE; else @@ -1388,7 +1388,7 @@ void InitPlayerAvatar(s16 x, s16 y, u8 direction, u8 gender) playerObjEventTemplate.graphicsId = GetPlayerAvatarGraphicsIdByStateIdAndGender(PLAYER_AVATAR_STATE_NORMAL, gender); playerObjEventTemplate.x = x - MAP_OFFSET; playerObjEventTemplate.y = y - MAP_OFFSET; - playerObjEventTemplate.elevation = 0; + playerObjEventTemplate.elevation = ELEVATION_TRANSITION; playerObjEventTemplate.movementType = MOVEMENT_TYPE_PLAYER; playerObjEventTemplate.movementRangeX = 0; playerObjEventTemplate.movementRangeY = 0; diff --git a/src/field_specials.c b/src/field_specials.c index 002e4fb811..3c9326e510 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -1255,7 +1255,7 @@ void SpawnCameraObject(void) LOCALID_CAMERA, gSaveBlock1Ptr->pos.x + MAP_OFFSET, gSaveBlock1Ptr->pos.y + MAP_OFFSET, - 3); // elevation + ELEVATION_DEFAULT); gObjectEvents[obj].invisible = TRUE; CameraObjectSetFollowedSpriteId(gObjectEvents[obj].spriteId); } diff --git a/src/overworld.c b/src/overworld.c index 87a833ac74..05f5bc5f12 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -2753,7 +2753,7 @@ static const u8 *TryInteractWithPlayer(struct CableClubPlayer *player) otherPlayerPos = player->pos; otherPlayerPos.x += gDirectionToVectors[player->facing].x; otherPlayerPos.y += gDirectionToVectors[player->facing].y; - otherPlayerPos.elevation = 0; + otherPlayerPos.elevation = ELEVATION_TRANSITION; linkPlayerId = GetLinkPlayerIdAt(otherPlayerPos.x, otherPlayerPos.y); if (linkPlayerId != MAX_LINK_PLAYERS) diff --git a/src/trainer_hill.c b/src/trainer_hill.c index f2037b20d8..99df44bb08 100644 --- a/src/trainer_hill.c +++ b/src/trainer_hill.c @@ -250,7 +250,7 @@ static const u8 *const sModeStrings[NUM_TRAINER_HILL_MODES] = static const struct ObjectEventTemplate sTrainerObjectEventTemplate = { .graphicsId = OBJ_EVENT_GFX_RIVAL_BRENDAN_NORMAL, - .elevation = 3, + .elevation = ELEVATION_DEFAULT, .movementType = MOVEMENT_TYPE_LOOK_AROUND, .movementRangeX = 1, .movementRangeY = 1, @@ -677,7 +677,7 @@ static u16 GetMapDataForFloor(u8 floorId, u32 x, u32 y, u32 floorWidth) // floor impassable = (sHillData->floors[floorId].map.collisionData[y] >> (15 - x) & 1); metatileId = sHillData->floors[floorId].map.metatileData[floorWidth * y + x] + NUM_METATILES_IN_PRIMARY; - elevation = PACK_ELEVATION(3); + elevation = PACK_ELEVATION(ELEVATION_DEFAULT); return PACK_COLLISION(impassable) | elevation | PACK_METATILE(metatileId); } From e2df932775b46453e83183ac45f5e965b531ed1c Mon Sep 17 00:00:00 2001 From: GGbond Date: Fri, 20 Feb 2026 23:10:18 +0800 Subject: [PATCH 08/24] Fix incorrect Adaptability interaction with non Tera type moves after Terastalization (#9272) --- src/battle_terastal.c | 10 +++++--- test/battle/ability/adaptability.c | 37 +++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/battle_terastal.c b/src/battle_terastal.c index f0e720c93e..320e236662 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -163,15 +163,19 @@ uq4_12_t GetTeraMultiplier(struct DamageContext *ctx) else return UQ_4_12(2.0); } - // Base or Tera type only. - else if ((ctx->moveType == teraType && !IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType)) - || (ctx->moveType != teraType && IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType))) + // Tera type only (Adaptability applies). + else if (ctx->moveType == teraType && !IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType)) { if (ctx->abilityAtk == ABILITY_ADAPTABILITY) return UQ_4_12(2.0); else return UQ_4_12(1.5); } + // Base type only (Adaptability does not apply while Terastallized). + else if (ctx->moveType != teraType && IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType)) + { + return UQ_4_12(1.5); + } // Neither base or Tera type. else { diff --git a/test/battle/ability/adaptability.c b/test/battle/ability/adaptability.c index 876dac212c..ba54590cd2 100644 --- a/test/battle/ability/adaptability.c +++ b/test/battle/ability/adaptability.c @@ -61,4 +61,39 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type with Adaptability g } } -TO_DO_BATTLE_TEST("Adaptability does not affect Stellar-type moves"); +SINGLE_BATTLE_TEST("(TERA) Adaptability does not increase non-Tera base STAB beyond 1.5x", s16 damage) +{ + u32 move; + PARAMETRIZE { move = MOVE_GUST; } + PARAMETRIZE { move = MOVE_WATER_GUN; } + GIVEN { + PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move, gimmick: GIMMICK_TERA); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // With Adaptability, non-Tera base type should still be 1.5x STAB (not 2.0x). + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Adaptability does not affect Stellar-type moves", s16 damage) +{ + u32 ability; + PARAMETRIZE { ability = ABILITY_HYPER_CUTTER; } + PARAMETRIZE { ability = ABILITY_ADAPTABILITY; } + GIVEN { + PLAYER(SPECIES_CRAWDAUNT) { Ability(ability); TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} From d57a067f5e2716ead252896ea59635597b87794b Mon Sep 17 00:00:00 2001 From: GGbond Date: Sat, 21 Feb 2026 02:17:07 +0800 Subject: [PATCH 09/24] Fix Ability Shield exemption when Neutralizing Gas ends (#9273) Co-authored-by: PhallenTree <168426989+PhallenTree@users.noreply.github.com> --- src/battle_script_commands.c | 6 +++++ test/battle/hold_effect/ability_shield.c | 32 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 225c411095..a80ed670e0 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -16560,6 +16560,12 @@ void BS_JumpIfAbilityCantBeReactivated(void) u32 battler = GetBattlerForBattleScript(cmd->battler); u32 ability = gBattleMons[battler].ability; + if (GetBattlerHoldEffectIgnoreAbility(battler) == HOLD_EFFECT_ABILITY_SHIELD) + { + gBattlescriptCurrInstr = cmd->jumpInstr; + return; + } + switch (ability) { case ABILITY_IMPOSTER: diff --git a/test/battle/hold_effect/ability_shield.c b/test/battle/hold_effect/ability_shield.c index 6e5a9884b8..e0798c9cfd 100644 --- a/test/battle/hold_effect/ability_shield.c +++ b/test/battle/hold_effect/ability_shield.c @@ -35,6 +35,38 @@ SINGLE_BATTLE_TEST("Ability Shield protects against Neutralizing Gas") } } +DOUBLE_BATTLE_TEST("Ability Shield prevents Intimidate from reactivating after Neutralizing Gas ends") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + PLAYER(SPECIES_WYNAUT) { Speed(4); } + OPPONENT(SPECIES_KOFFING) { Ability(ABILITY_NEUTRALIZING_GAS); HP(1); Speed(1); } + OPPONENT(SPECIES_GYARADOS) { Ability(ABILITY_INTIMIDATE); Item(ITEM_ABILITY_SHIELD); Speed(3); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft); } + } SCENE { + ABILITY_POPUP(opponentLeft, ABILITY_NEUTRALIZING_GAS); + MESSAGE("Neutralizing gas filled the area!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentRight); + MESSAGE("The opposing Gyarados's Ability is protected by the effects of its Ability Shield!"); + ABILITY_POPUP(opponentRight, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft); + HP_BAR(opponentLeft); + MESSAGE("The effects of the neutralizing gas wore off!"); + NONE_OF { + ABILITY_POPUP(opponentRight, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + } + MESSAGE("The opposing Koffing fainted!"); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + } +} + SINGLE_BATTLE_TEST("Ability Shield protects against Mold Breaker (no message)") { u32 item; From b7755887cc0b2fb16f37e17197823680f0004752 Mon Sep 17 00:00:00 2001 From: GGbond Date: Sat, 21 Feb 2026 17:23:49 +0800 Subject: [PATCH 10/24] Fix Tickle to be blocked by Substitute in Gen 4+ (#9288) --- data/battle_scripts_1.s | 1 + test/battle/move_effect/tickle.c | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 043a7fbd24..6ae21720cc 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -4319,6 +4319,7 @@ BattleScript_EffectWaterSport:: BattleScript_EffectTickle:: attackcanceler + jumpifsubstituteblocks BattleScript_ButItFailed jumpifstat BS_TARGET, CMP_GREATER_THAN, STAT_ATK, MIN_STAT_STAGE, BattleScript_TickleDoMoveAnim jumpifstat BS_TARGET, CMP_EQUAL, STAT_DEF, MIN_STAT_STAGE, BattleScript_CantLowerMultipleStats BattleScript_TickleDoMoveAnim:: diff --git a/test/battle/move_effect/tickle.c b/test/battle/move_effect/tickle.c index 3a878868fd..8513340f4b 100644 --- a/test/battle/move_effect/tickle.c +++ b/test/battle/move_effect/tickle.c @@ -21,3 +21,20 @@ SINGLE_BATTLE_TEST("Tickle reduces the target's Attack and Defense by 1 stage ea EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); } } + +SINGLE_BATTLE_TEST("Tickle is blocked by Substitute (Gen4+)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_TICKLE, MOVE_CELEBRATE); Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SUBSTITUTE, MOVE_CELEBRATE); Speed(10); } + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_TICKLE); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TICKLE, player); + MESSAGE("But it failed!"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + } +} From ccf71d2d6aab90c7f7ad523a8f9d180fc4f1fd55 Mon Sep 17 00:00:00 2001 From: GGbond Date: Sat, 21 Feb 2026 17:24:43 +0800 Subject: [PATCH 11/24] Fix Venom Drench bypassing Substitute (#9289) --- data/battle_scripts_1.s | 1 + test/battle/move_effect/venom_drench.c | 40 +++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 6ae21720cc..8a24a0043b 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -1652,6 +1652,7 @@ BattleScript_ToxicThreadTryPsn:: BattleScript_EffectVenomDrench:: attackcanceler + jumpifsubstituteblocks BattleScript_ButItFailed jumpifstatus BS_TARGET, STATUS1_PSN_ANY, BattleScript_EffectVenomDrenchCanBeUsed goto BattleScript_ButItFailed BattleScript_EffectVenomDrenchCanBeUsed: diff --git a/test/battle/move_effect/venom_drench.c b/test/battle/move_effect/venom_drench.c index 00dac65858..aa09061f22 100644 --- a/test/battle/move_effect/venom_drench.c +++ b/test/battle/move_effect/venom_drench.c @@ -1,4 +1,42 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Venom Drench (Move Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_VENOM_DRENCH) == EFFECT_VENOM_DRENCH); +} + +SINGLE_BATTLE_TEST("Venom Drench lowers stats of a poisoned target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_VENOM_DRENCH); } + OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } + } WHEN { + TURN { MOVE(player, MOVE_VENOM_DRENCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_VENOM_DRENCH, player); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(opponent->statStages[STAT_SPEED], DEFAULT_STAT_STAGE - 1); + } +} + +SINGLE_BATTLE_TEST("Venom Drench is blocked by Substitute") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_VENOM_DRENCH, MOVE_CELEBRATE); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SUBSTITUTE, MOVE_CELEBRATE); Status1(STATUS1_POISON); } + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_VENOM_DRENCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_VENOM_DRENCH, player); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + } +} From ae3369e87fabf5add6c2c16cac9923d6401ab506 Mon Sep 17 00:00:00 2001 From: GGbond Date: Sat, 21 Feb 2026 18:46:51 +0800 Subject: [PATCH 12/24] Fix Assist to account for temporarily changed moves in Gen 5+ (#9287) --- src/battle_util.c | 17 ++++++++++++++++- test/battle/move_effect/assist.c | 23 ++++++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index dd08a155ef..9787bbe282 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -11215,11 +11215,21 @@ static u32 GetAssistMove(void) u32 move = MOVE_NONE; s32 chooseableMovesNo = 0; struct Pokemon *party; + u8 battlerByPartyId[PARTY_SIZE]; u16 *validMoves = Alloc(sizeof(u16) * PARTY_SIZE * MAX_MON_MOVES); if (validMoves != NULL) { party = GetBattlerParty(gBattlerAttacker); + for (u32 i = 0; i < PARTY_SIZE; i++) + battlerByPartyId[i] = MAX_BATTLERS_COUNT; + for (u32 battler = 0; battler < gBattlersCount; battler++) + { + if (GetBattlerSide(battler) != GetBattlerSide(gBattlerAttacker)) + continue; + if (gBattlerPartyIndexes[battler] < PARTY_SIZE) + battlerByPartyId[gBattlerPartyIndexes[battler]] = battler; + } for (u32 monId = 0; monId < PARTY_SIZE; monId++) { @@ -11232,7 +11242,12 @@ static u32 GetAssistMove(void) for (u32 moveId = 0; moveId < MAX_MON_MOVES; moveId++) { - u16 move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId); + u16 move; + + if (battlerByPartyId[monId] != MAX_BATTLERS_COUNT) + move = gBattleMons[battlerByPartyId[monId]].moves[moveId]; + else + move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId); if (IsMoveAssistBanned(move)) continue; diff --git a/test/battle/move_effect/assist.c b/test/battle/move_effect/assist.c index 3a380ef125..6f236af983 100644 --- a/test/battle/move_effect/assist.c +++ b/test/battle/move_effect/assist.c @@ -12,7 +12,6 @@ TO_DO_BATTLE_TEST("Assist can call moves with no PP left"); TO_DO_BATTLE_TEST("Assist can call moves from a fainted party member"); TO_DO_BATTLE_TEST("Assist can call moves that are blocked to its partners"); // Eg. double battle parter blocked by Disable TO_DO_BATTLE_TEST("Assist can only call the original moves of a Transformed partner (Gen4 only)"); -TO_DO_BATTLE_TEST("Assist can only call the current moves of a Transformed partner (Gen5+)"); TO_DO_BATTLE_TEST("Assist cannot call a Mimicked move (Gen4 only)"); TO_DO_BATTLE_TEST("Assist can call a Mimicked move but not the original Mimic (Gen5+)"); TO_DO_BATTLE_TEST("Assist can call moves in unhatched Eggs (Gen5 only)"); @@ -57,3 +56,25 @@ SINGLE_BATTLE_TEST("Assisted move triggers correct weakness berry") ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player); } } + +DOUBLE_BATTLE_TEST("Assist can only call the current moves of a Transformed partner (Gen5+)") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TRANSFORM) == EFFECT_TRANSFORM); + PLAYER(SPECIES_WOBBUFFET) { Speed(3); Moves(MOVE_ASSIST); } + PLAYER(SPECIES_DITTO) { Speed(4); Moves(MOVE_TRANSFORM); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); Moves(MOVE_SCRATCH); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(1); } + } WHEN { + TURN { + MOVE(playerRight, MOVE_TRANSFORM, target: opponentLeft); + MOVE(playerLeft, MOVE_ASSIST); + MOVE(opponentLeft, MOVE_SCRATCH, target: playerRight); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ASSIST, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentLeft); + } +} From cb5db2491fae4e5761b658da6aed3a7a0900dae2 Mon Sep 17 00:00:00 2001 From: luuma <31407427+luuma@users.noreply.github.com> Date: Sat, 21 Feb 2026 16:45:32 +0000 Subject: [PATCH 13/24] Fixes Minior not appearing in Meteor form when encountered in the wild (#9282) --- include/constants/form_change_types.h | 2 ++ src/battle_main.c | 6 ++++- src/data/pokemon/form_change_tables.h | 8 +++++- src/pokemon.c | 1 + test/battle/ability/shields_down.c | 36 +++++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 2 deletions(-) diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index f43f3bddc6..1f9a58cd94 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -137,6 +137,8 @@ enum FormChanges FORM_CHANGE_OVERWORLD_WEATHER, // Form change that activates when the Pokémon is deposited into the PC or Daycare. FORM_CHANGE_DEPOSIT, + // Form change for Minior, which appears unchanged when encountered in the wild + FORM_CHANGE_BEGIN_WILD_ENCOUNTER, }; #endif // GUARD_CONSTANTS_FORM_CHANGE_TYPES_H diff --git a/src/battle_main.c b/src/battle_main.c index 708448f1b7..6f2eeb5df4 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -582,7 +582,11 @@ static void CB2_InitBattleInternal(void) TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_BEGIN_BATTLE); TryFormChange(i, B_SIDE_OPPONENT, FORM_CHANGE_BEGIN_BATTLE); } - + if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) + { + TryFormChange(0, B_SIDE_OPPONENT, FORM_CHANGE_BEGIN_WILD_ENCOUNTER); + TryFormChange(1, B_SIDE_OPPONENT, FORM_CHANGE_BEGIN_WILD_ENCOUNTER);// Only tries to change the first two opposing slots, assuming these are the only ones occupied in a wild battle. + } if (TESTING) { gPlayerPartyCount = CalculatePartyCount(gPlayerParty); diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index 6926e20ef9..9e1eaaa6f7 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -1316,10 +1316,10 @@ static const struct FormChange sSilvallyFormChangeTable[] = {FORM_CHANGE_TERMINATOR}, }; #endif //P_FAMILY_TYPE_NULL - #if P_FAMILY_MINIOR static const struct FormChange sMiniorRedFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_RED}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_RED}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_RED, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_RED, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, @@ -1330,6 +1330,7 @@ static const struct FormChange sMiniorRedFormChangeTable[] = }; static const struct FormChange sMiniorBlueFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_BLUE}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_BLUE}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_BLUE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_BLUE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, @@ -1340,6 +1341,7 @@ static const struct FormChange sMiniorBlueFormChangeTable[] = }; static const struct FormChange sMiniorGreenFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_GREEN}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_GREEN}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_GREEN, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_GREEN, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, @@ -1350,6 +1352,7 @@ static const struct FormChange sMiniorGreenFormChangeTable[] = }; static const struct FormChange sMiniorIndigoFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_INDIGO}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_INDIGO}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_INDIGO, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_INDIGO, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, @@ -1360,6 +1363,7 @@ static const struct FormChange sMiniorIndigoFormChangeTable[] = }; static const struct FormChange sMiniorOrangeFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_ORANGE}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_ORANGE}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_ORANGE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_ORANGE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, @@ -1370,6 +1374,7 @@ static const struct FormChange sMiniorOrangeFormChangeTable[] = }; static const struct FormChange sMiniorVioletFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_VIOLET}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_VIOLET}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_VIOLET, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_VIOLET, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, @@ -1379,6 +1384,7 @@ static const struct FormChange sMiniorVioletFormChangeTable[] = {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMiniorYellowFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_YELLOW}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_YELLOW}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_METEOR_YELLOW, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT, SPECIES_MINIOR_CORE_YELLOW, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, diff --git a/src/pokemon.c b/src/pokemon.c index 96b661bf52..1df1510bbd 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -6970,6 +6970,7 @@ u32 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, enum FormChanges case FORM_CHANGE_DEPOSIT: case FORM_CHANGE_FAINT: case FORM_CHANGE_DAYS_PASSED: + case FORM_CHANGE_BEGIN_WILD_ENCOUNTER: targetSpecies = formChanges[i].targetSpecies; break; case FORM_CHANGE_STATUS: diff --git a/test/battle/ability/shields_down.c b/test/battle/ability/shields_down.c index d16aa240da..ca1ade2c37 100644 --- a/test/battle/ability/shields_down.c +++ b/test/battle/ability/shields_down.c @@ -75,3 +75,39 @@ SINGLE_BATTLE_TEST("Shields Down protects Minior Meteor from status conditions") EXPECT(opponent->status1 & STATUS1_BURN); } } + +WILD_BATTLE_TEST("Wild Minior appear in Meteor form without transforming")// To be replaced with WILD_DOUBLE_BATTLE_TEST when that is made possible. +{ + GIVEN { + PLAYER(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); } + OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(player, ABILITY_SHIELDS_DOWN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent); + } + } THEN { + EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR); + EXPECT_EQ(player->species, SPECIES_MINIOR_METEOR); + } +} + +SINGLE_BATTLE_TEST("Trainers' Minior appear in Core form") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_MINIOR_METEOR) { Ability(ABILITY_SHIELDS_DOWN); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent); + } THEN { + EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR); + } +} + From b20b7fda5b02cc4e302260b4cbbea2aa9048b9fb Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 21 Feb 2026 22:27:59 +0100 Subject: [PATCH 14/24] add KnightGallade as a contributor for bug (#9296) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CREDITS.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 627b52827a..3d356bbc97 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -651,6 +651,15 @@ "contributions": [ "bug" ] + }, + { + "login": "KnightGallade", + "name": "KnightGallade", + "avatar_url": "https://avatars.githubusercontent.com/u/189022270?v=4", + "profile": "https://github.com/KnightGallade", + "contributions": [ + "bug" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index c3d7df099a..133208fa95 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -93,6 +93,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d PacFire
PacFire

🎨 ChrispyChris27
ChrispyChris27

💻 LogicalLlama
LogicalLlama

🐛 + KnightGallade
KnightGallade

🐛 From 931663568713a75f7f7f1ffe5b68221b6d892073 Mon Sep 17 00:00:00 2001 From: Hedara Date: Sun, 22 Feb 2026 13:36:32 +0100 Subject: [PATCH 15/24] Fix setting the wall clock crashing with OW_USE_FAKE_RTC set to FALSE and LTO=1 --- src/fake_rtc.c | 6 ++++++ src/rtc.c | 3 ++- src/scrcmd.c | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/fake_rtc.c b/src/fake_rtc.c index 2024065b7d..c6b68d9105 100644 --- a/src/fake_rtc.c +++ b/src/fake_rtc.c @@ -50,6 +50,9 @@ void FakeRtc_TickTimeForward(void) void FakeRtc_AdvanceTimeBy(u32 days, u32 hours, u32 minutes, u32 seconds) { + if (!OW_USE_FAKE_RTC) + return; + struct DateTime dateTime; struct SiiRtcInfo *rtc = FakeRtc_GetCurrentTime(); @@ -63,6 +66,9 @@ void FakeRtc_AdvanceTimeBy(u32 days, u32 hours, u32 minutes, u32 seconds) void FakeRtc_ForwardTimeTo(u32 hour, u32 minute, u32 second) { + if (!OW_USE_FAKE_RTC) + return; + Script_PauseFakeRtc(); struct Time diff, target; struct SiiRtcInfo *fakeRtc = FakeRtc_GetCurrentTime(); diff --git a/src/rtc.c b/src/rtc.c index ace7bfb0c4..adab416446 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -349,7 +349,8 @@ void RtcCalcLocalTimeOffset(s32 days, s32 hours, s32 minutes, s32 seconds) gLocalTime.hours = hours; gLocalTime.minutes = minutes; gLocalTime.seconds = seconds; - FakeRtc_ManuallySetTime(gLocalTime.days, gLocalTime.hours, gLocalTime.minutes, seconds); + if (!OW_USE_FAKE_RTC) + FakeRtc_ManuallySetTime(gLocalTime.days, gLocalTime.hours, gLocalTime.minutes, seconds); RtcGetInfo(&sRtc); RtcCalcTimeDifference(&sRtc, &gSaveBlock2Ptr->localTimeOffset, &gLocalTime); } diff --git a/src/scrcmd.c b/src/scrcmd.c index e05190c18d..7983f54b43 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -3239,6 +3239,9 @@ bool8 ScrCmd_fwdtime(struct ScriptContext *ctx) bool8 ScrCmd_fwdweekday(struct ScriptContext *ctx) { + if (!OW_USE_FAKE_RTC) + return FALSE; + struct SiiRtcInfo *rtc = FakeRtc_GetCurrentTime(); u32 weekdayTarget = ScriptReadWord(ctx); From 484eef4143cbe7593a9a649cc7c972dcd5bc5855 Mon Sep 17 00:00:00 2001 From: KnightGallade Date: Sun, 22 Feb 2026 15:14:03 +0100 Subject: [PATCH 16/24] Allow Power Construct Animation (#9298) --- src/battle_script_commands.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index a80ed670e0..4fd3c472e8 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5538,6 +5538,7 @@ static void PlayAnimation(u32 battler, u8 animId, const u16 *argPtr, const u8 *n || animId == B_ANIM_FORM_CHANGE || animId == B_ANIM_SUBSTITUTE_FADE || animId == B_ANIM_PRIMAL_REVERSION + || animId == B_ANIM_POWER_CONSTRUCT || animId == B_ANIM_ULTRA_BURST || animId == B_ANIM_TERA_CHARGE || animId == B_ANIM_TERA_ACTIVATE) From d2ae1175c474b8788d06bd28386a753a6eca0ed8 Mon Sep 17 00:00:00 2001 From: GGbond Date: Mon, 23 Feb 2026 21:50:03 +0800 Subject: [PATCH 17/24] Add tests for Hadron Engine and Psyblade (#9314) --- test/battle/ability/hadron_engine.c | 43 ++++++++++++++++++++++++++++- test/battle/move_effect/psyblade.c | 35 ++++++++++++++++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/test/battle/ability/hadron_engine.c b/test/battle/ability/hadron_engine.c index e6324c0c51..0de50c94ec 100644 --- a/test/battle/ability/hadron_engine.c +++ b/test/battle/ability/hadron_engine.c @@ -1,4 +1,45 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Hadron Engine (Ability) test titles") +SINGLE_BATTLE_TEST("Hadron Engine creates Electric Terrain when entering the battle") +{ + GIVEN { + PLAYER(SPECIES_MIRAIDON) { Ability(ABILITY_HADRON_ENGINE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(player, ABILITY_HADRON_ENGINE); + MESSAGE("An electric current ran across the battlefield!"); + } +} + +SINGLE_BATTLE_TEST("Hadron Engine boosts the Pokemon's Special Attack on Electric Terrain even if not grounded", s16 damage) +{ + bool32 overrideTerrain, airBalloon; + + PARAMETRIZE { airBalloon = FALSE; overrideTerrain = TRUE; } + PARAMETRIZE { airBalloon = FALSE; overrideTerrain = FALSE; } + PARAMETRIZE { airBalloon = TRUE; overrideTerrain = TRUE; } + PARAMETRIZE { airBalloon = TRUE; overrideTerrain = FALSE; } + + GIVEN { + ASSUME(gItemsInfo[ITEM_AIR_BALLOON].holdEffect == HOLD_EFFECT_AIR_BALLOON); + ASSUME(GetMoveEffect(MOVE_GRASSY_TERRAIN) == EFFECT_GRASSY_TERRAIN); + ASSUME(GetMoveCategory(MOVE_POWER_GEM) == DAMAGE_CATEGORY_SPECIAL); + PLAYER(SPECIES_MIRAIDON) { Ability(ABILITY_HADRON_ENGINE); Moves(MOVE_POWER_GEM, MOVE_CELEBRATE); Item(airBalloon ? ITEM_AIR_BALLOON : ITEM_NONE); Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_GRASSY_TERRAIN, MOVE_CELEBRATE); Speed(2); } + } WHEN { + if (overrideTerrain) + TURN { MOVE(opponent, MOVE_GRASSY_TERRAIN); } + TURN { MOVE(player, MOVE_POWER_GEM); } + } SCENE { + if (overrideTerrain) + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POWER_GEM, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.3333), results[1].damage); + EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.3333), results[3].damage); + } +} diff --git a/test/battle/move_effect/psyblade.c b/test/battle/move_effect/psyblade.c index 3a5778d1f4..edc079fb84 100644 --- a/test/battle/move_effect/psyblade.c +++ b/test/battle/move_effect/psyblade.c @@ -1,4 +1,37 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Psyblade (Move Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_PSYBLADE) == EFFECT_PSYBLADE); +} + +SINGLE_BATTLE_TEST("Psyblade's power increases by 50% on Electric Terrain even if the user is not grounded", s16 damage) +{ + bool32 terrain, airBalloon; + + PARAMETRIZE { terrain = FALSE; airBalloon = FALSE; } + PARAMETRIZE { terrain = TRUE; airBalloon = FALSE; } + PARAMETRIZE { terrain = FALSE; airBalloon = TRUE; } + PARAMETRIZE { terrain = TRUE; airBalloon = TRUE; } + + GIVEN { + ASSUME(gItemsInfo[ITEM_AIR_BALLOON].holdEffect == HOLD_EFFECT_AIR_BALLOON); + ASSUME(GetMoveEffect(MOVE_ELECTRIC_TERRAIN) == EFFECT_ELECTRIC_TERRAIN); + ASSUME(GetMoveEffect(MOVE_PSYBLADE) == EFFECT_PSYBLADE); + PLAYER(SPECIES_SLOWKING) { Moves(MOVE_ELECTRIC_TERRAIN, MOVE_PSYBLADE); Item(airBalloon ? ITEM_AIR_BALLOON : ITEM_NONE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (terrain) + TURN { MOVE(player, MOVE_ELECTRIC_TERRAIN); } + TURN { MOVE(player, MOVE_PSYBLADE); } + } SCENE { + if (terrain) + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYBLADE, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.5), results[3].damage); + } +} From 8334c68422121c2e98638dc47812fffc992dae61 Mon Sep 17 00:00:00 2001 From: GGbond Date: Mon, 23 Feb 2026 21:51:32 +0800 Subject: [PATCH 18/24] Fix Eviolite to use original species after Transform (#9285) --- include/battle.h | 1 + src/battle_script_commands.c | 1 + src/battle_util.c | 9 +++- test/battle/hold_effect/eviolite.c | 80 +++++++++++++++++++++++++++++- 4 files changed, 88 insertions(+), 3 deletions(-) diff --git a/include/battle.h b/include/battle.h index 6c528866cf..3861aa8339 100755 --- a/include/battle.h +++ b/include/battle.h @@ -61,6 +61,7 @@ struct DisableStruct { u32 transformedMonPersonality; bool8 transformedMonShininess; + u16 transformedMonSpecies; u16 disabledMove; u16 encoredMove; u8 protectUses:4; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4fd3c472e8..de8d68cb51 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -11310,6 +11310,7 @@ static void Cmd_transformdataexecution(void) gBattleMons[gBattlerAttacker].volatiles.transformed = TRUE; gDisableStructs[gBattlerAttacker].disabledMove = MOVE_NONE; gDisableStructs[gBattlerAttacker].disableTimer = 0; + gDisableStructs[gBattlerAttacker].transformedMonSpecies = gBattleMons[gBattlerAttacker].species; gDisableStructs[gBattlerAttacker].transformedMonPersonality = gBattleMons[gBattlerTarget].personality; if (B_TRANSFORM_SHINY >= GEN_4) gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerTarget].isShiny; diff --git a/src/battle_util.c b/src/battle_util.c index 9787bbe282..ee3ae241f7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8145,8 +8145,13 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_EVIOLITE: - if (CanEvolve(gBattleMons[battlerDef].species)) - modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); + { + u16 species = gBattleMons[battlerDef].species; + if (gBattleMons[battlerDef].volatiles.transformed && gDisableStructs[battlerDef].transformedMonSpecies != SPECIES_NONE) + species = gDisableStructs[battlerDef].transformedMonSpecies; + if (CanEvolve(species)) + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); + } break; case HOLD_EFFECT_ASSAULT_VEST: if (!usesDefStat) diff --git a/test/battle/hold_effect/eviolite.c b/test/battle/hold_effect/eviolite.c index 1aa0622e43..5387d50929 100644 --- a/test/battle/hold_effect/eviolite.c +++ b/test/battle/hold_effect/eviolite.c @@ -1,4 +1,82 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Eviolite (Hold Effect) test titles") +ASSUMPTIONS +{ + ASSUME(gItemsInfo[ITEM_EVIOLITE].holdEffect == HOLD_EFFECT_EVIOLITE); +} + +SINGLE_BATTLE_TEST("Eviolite boosts Defense and Sp. Def for unevolved Pokemon", s16 damage) +{ + u16 move; + u32 item; + + PARAMETRIZE { move = MOVE_SCRATCH; item = ITEM_EVIOLITE; } + PARAMETRIZE { move = MOVE_SCRATCH; item = ITEM_NONE; } + PARAMETRIZE { move = MOVE_WATER_GUN; item = ITEM_EVIOLITE; } + PARAMETRIZE { move = MOVE_WATER_GUN; item = ITEM_NONE; } + + GIVEN { + ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL); + ASSUME(GetMoveCategory(MOVE_WATER_GUN) == DAMAGE_CATEGORY_SPECIAL); + PLAYER(SPECIES_PIKACHU) { Item(item); } + OPPONENT(SPECIES_MAGIKARP) { Moves(MOVE_SCRATCH, MOVE_WATER_GUN); } + } WHEN { + TURN { MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.5), results[3].damage); + } +} + +SINGLE_BATTLE_TEST("Eviolite does not boost Defense or Sp. Def for evolved Pokemon", s16 damage) +{ + u16 move; + u32 item; + + PARAMETRIZE { move = MOVE_SCRATCH; item = ITEM_EVIOLITE; } + PARAMETRIZE { move = MOVE_SCRATCH; item = ITEM_NONE; } + PARAMETRIZE { move = MOVE_WATER_GUN; item = ITEM_EVIOLITE; } + PARAMETRIZE { move = MOVE_WATER_GUN; item = ITEM_NONE; } + + GIVEN { + ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL); + ASSUME(GetMoveCategory(MOVE_WATER_GUN) == DAMAGE_CATEGORY_SPECIAL); + PLAYER(SPECIES_RAICHU) { Item(item); } + OPPONENT(SPECIES_MAGIKARP) { Moves(MOVE_SCRATCH, MOVE_WATER_GUN); } + } WHEN { + TURN { MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.0), results[1].damage); + EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.0), results[3].damage); + } +} + +SINGLE_BATTLE_TEST("Eviolite uses original species after Transform", s16 damage) +{ + u32 item; + + PARAMETRIZE { item = ITEM_EVIOLITE; } + PARAMETRIZE { item = ITEM_NONE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_TRANSFORM) == EFFECT_TRANSFORM); + PLAYER(SPECIES_PIKACHU) { Item(item); Moves(MOVE_TRANSFORM, MOVE_CELEBRATE); } + OPPONENT(SPECIES_GYARADOS) { Moves(MOVE_SCRATCH, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_TRANSFORM); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} From a3d041b8afdf87fff9e50cbff59d766daef1ed7a Mon Sep 17 00:00:00 2001 From: GGbond Date: Mon, 23 Feb 2026 21:54:46 +0800 Subject: [PATCH 19/24] Add tests for Future Sight interaction with Ally Switch (#9300) --- test/battle/move_effect/ally_switch.c | 128 ++++++++++++++++++++++++-- 1 file changed, 119 insertions(+), 9 deletions(-) diff --git a/test/battle/move_effect/ally_switch.c b/test/battle/move_effect/ally_switch.c index a82816ef24..798aa63354 100644 --- a/test/battle/move_effect/ally_switch.c +++ b/test/battle/move_effect/ally_switch.c @@ -92,24 +92,24 @@ DOUBLE_BATTLE_TEST("Ally Switch does not redirect the target of Snipe Shot") DOUBLE_BATTLE_TEST("Ally Switch does not redirect moves done by Pokémon with Stalwart and Propeller Tail") { + u16 species; enum Ability ability; - PARAMETRIZE { ability = ABILITY_STALWART; } - PARAMETRIZE { ability = ABILITY_PROPELLER_TAIL; } - PARAMETRIZE { ability = ABILITY_TELEPATHY; } + PARAMETRIZE { species = SPECIES_DURALUDON; ability = ABILITY_STALWART; } + PARAMETRIZE { species = SPECIES_ARROKUDA; ability = ABILITY_PROPELLER_TAIL; } + PARAMETRIZE { species = SPECIES_RALTS; ability = ABILITY_TELEPATHY; } GIVEN { PLAYER(SPECIES_WOBBUFFET); // Wobb is playerLeft, but it'll be Wynaut after Ally Switch PLAYER(SPECIES_WYNAUT); - OPPONENT(SPECIES_KADABRA) { Ability(ability); } + OPPONENT(species) { Ability(ability); } OPPONENT(SPECIES_ABRA); } WHEN { - TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); MOVE(opponentLeft, MOVE_SCRATCH, target:playerRight); } // Kadabra targets playerRight Wynaut. + TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); MOVE(opponentLeft, MOVE_SCRATCH, target:playerRight); } // Opponent targets playerRight Wynaut. } SCENE { MESSAGE("Wobbuffet used Ally Switch!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, playerLeft); MESSAGE("Wobbuffet and Wynaut switched places!"); - MESSAGE("The opposing Kadabra used Scratch!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentLeft); HP_BAR((ability == ABILITY_STALWART || ability == ABILITY_PROPELLER_TAIL) ? playerLeft : playerRight); } @@ -218,6 +218,7 @@ DOUBLE_BATTLE_TEST("Ally Switch increases the Protect-like moves counter (Gen9+) DOUBLE_BATTLE_TEST("Ally Switch works if ally used two-turn move like Dig") { GIVEN { + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect); PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); @@ -313,9 +314,8 @@ DOUBLE_BATTLE_TEST("Ally switch swaps opposing sky drop targets if partner is be DOUBLE_BATTLE_TEST("Ally Switch swaps Illusion data") { GIVEN { - ASSUME(GetMoveEffect(MOVE_ALLY_SWITCH) == EFFECT_ALLY_SWITCH); PLAYER(SPECIES_HOOPA); - PLAYER(SPECIES_ZOROARK); + PLAYER(SPECIES_ZOROARK) {Ability(ABILITY_ILLUSION); } PLAYER(SPECIES_MAMOSWINE); // the third member here is required for zoroark OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -329,6 +329,7 @@ DOUBLE_BATTLE_TEST("Ally Switch swaps Illusion data") DOUBLE_BATTLE_TEST("Ally switch updates last used moves for Mimic") { GIVEN { + ASSUME(GetMoveEffect(MOVE_MIMIC) == EFFECT_MIMIC); PLAYER(SPECIES_XATU) { Speed(100); } PLAYER(SPECIES_RIOLU) { Speed(150); } OPPONENT(SPECIES_FEAROW) { Speed(20); } @@ -348,9 +349,10 @@ DOUBLE_BATTLE_TEST("Ally switch updates last used moves for Mimic") } } -DOUBLE_BATTLE_TEST("Ally Switch does not update leech seed battler") +DOUBLE_BATTLE_TEST("Ally Switch does not update leech seed position") { GIVEN { + ASSUME(GetMoveEffect(MOVE_LEECH_SEED) == EFFECT_LEECH_SEED); PLAYER(SPECIES_WYNAUT); PLAYER(SPECIES_SOLOSIS); OPPONENT(SPECIES_BULBASAUR) { HP(50); MaxHP(100); } @@ -379,6 +381,114 @@ DOUBLE_BATTLE_TEST("Ally Switch does not update leech seed battler") } } +DOUBLE_BATTLE_TEST("Ally Switch does not update Future Sight target position") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_FUTURE_SIGHT) == EFFECT_FUTURE_SIGHT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ABRA); + OPPONENT(SPECIES_RALTS); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_FUTURE_SIGHT, target: playerLeft); } + TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, playerLeft); + MESSAGE("Wynaut took the Future Sight attack!"); + HP_BAR(playerLeft); + NOT HP_BAR(playerRight); + } +} + +DOUBLE_BATTLE_TEST("Ally Switch does not update Future Sight target position when attacker side switches") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_FUTURE_SIGHT) == EFFECT_FUTURE_SIGHT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ABRA); + OPPONENT(SPECIES_RALTS); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_FUTURE_SIGHT, target: playerLeft); } + TURN { SWITCH(opponentLeft, 2); MOVE(opponentRight, MOVE_ALLY_SWITCH); } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, opponentRight); + MESSAGE("Wobbuffet took the Future Sight attack!"); + HP_BAR(playerLeft); + NOT HP_BAR(playerRight); + } +} + +DOUBLE_BATTLE_TEST("Ally Switch does not update Wish recovery position") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_WISH) == EFFECT_WISH); + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + PLAYER(SPECIES_WYNAUT) { HP(20); MaxHP(100); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_WISH); } + TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WISH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, playerLeft); + HP_BAR(playerLeft); + NOT HP_BAR(playerRight); + } THEN { + EXPECT_EQ(playerLeft->hp, 70); + EXPECT_EQ(playerRight->hp, 50); + } +} + +DOUBLE_BATTLE_TEST("Ally Switch does not update Healing Wish/Lunar Dance recovery position") +{ + u16 move = MOVE_NONE; + struct BattlePokemon *switchTarget = NULL; + + PARAMETRIZE { move = MOVE_HEALING_WISH; switchTarget = playerLeft; } + PARAMETRIZE { move = MOVE_HEALING_WISH; switchTarget = playerRight; } + PARAMETRIZE { move = MOVE_LUNAR_DANCE; switchTarget = playerLeft; } + PARAMETRIZE { move = MOVE_LUNAR_DANCE; switchTarget = playerRight; } + + GIVEN { + WITH_CONFIG(B_HEALING_WISH_SWITCH, GEN_8); + ASSUME(GetMoveEffect(MOVE_HEALING_WISH) == EFFECT_HEALING_WISH); + ASSUME(GetMoveEffect(MOVE_LUNAR_DANCE) == EFFECT_LUNAR_DANCE); + PLAYER(SPECIES_GARDEVOIR); + PLAYER(SPECIES_ABRA); + PLAYER(SPECIES_WOBBUFFET) { HP(100); MaxHP(100); } + PLAYER(SPECIES_WYNAUT) { HP(50); MaxHP(80); Status1(STATUS1_PARALYSIS); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, move); SEND_OUT(playerLeft, 2); } + TURN { MOVE(playerRight, MOVE_ALLY_SWITCH); } + TURN { SWITCH(switchTarget, 3); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, playerLeft); + if (switchTarget == playerLeft) { + HP_BAR(playerLeft, hp: 80); + STATUS_ICON(playerLeft, none: TRUE); + } else { + NOT HP_BAR(playerRight); + } + } THEN { + if (switchTarget == playerLeft) { + EXPECT_EQ(playerLeft->hp, 80); + EXPECT_EQ(playerLeft->status1, STATUS1_NONE); + } else { + EXPECT_EQ(playerRight->hp, 50); + EXPECT_EQ(playerRight->status1, STATUS1_PARALYSIS); + } + } +} + DOUBLE_BATTLE_TEST("Ally Switch updates attract battler") { GIVEN { From e369ea446490c4a8bbb6b5ce0bc9e1b22d48421d Mon Sep 17 00:00:00 2001 From: amiosi <44352097+amiosi@users.noreply.github.com> Date: Mon, 23 Feb 2026 11:23:32 -0500 Subject: [PATCH 20/24] correct move flags/data (#8858) --- src/data/moves_info.h | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 809c6ec94c..bea055b2af 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -439,7 +439,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = [MOVE_RAZOR_WIND] = { .name = COMPOUND_STRING("Razor Wind"), - #if B_UPDATED_MOVE_DATA == GEN_3 + #if B_UPDATED_MOVE_DATA == GEN_3 || B_UPDATED_MOVE_DATA == GEN_1 .description = COMPOUND_STRING( "A 2-turn move that strikes\n" "the foe on the 2nd turn."), @@ -454,10 +454,10 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .accuracy = B_UPDATED_MOVE_DATA >= GEN_3 ? 100 : 75, #if B_UPDATED_MOVE_DATA >= GEN_4 .criticalHitStage = 1, - #elif B_UPDATED_MOVE_DATA == GEN_3 - .criticalHitStage = 0, - #else + #elif B_UPDATED_MOVE_DATA == GEN_2 .criticalHitStage = 2, + #else + .criticalHitStage = 0, #endif .pp = 10, .target = MOVE_TARGET_BOTH, @@ -489,6 +489,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_RESET_STATS }, + .ignoresProtect = TRUE, + .mirrorMoveBanned = TRUE, .danceMove = TRUE, .snatchAffected = TRUE, .contestEffect = CONTEST_EFFECT_IMPROVE_CONDITION_PREVENT_NERVOUSNESS, @@ -3251,6 +3253,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_ATK_UP_2 }, + .ignoresProtect = TRUE, + .mirrorMoveBanned = TRUE, .mimicBanned = TRUE, .metronomeBanned = B_UPDATED_MOVE_FLAGS >= GEN_4, .copycatBanned = TRUE, @@ -4616,7 +4620,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_DEF_UP_1 }, - .ignoresProtect = (B_UPDATED_MOVE_FLAGS >= GEN_6) || (B_UPDATED_MOVE_FLAGS < GEN_3), + .ignoresProtect = B_UPDATED_MOVE_FLAGS < GEN_3, .magicCoatAffected = TRUE, .contestEffect = CONTEST_EFFECT_MAKE_FOLLOWING_MONS_NERVOUS, .contestCategory = CONTEST_CATEGORY_SMART, @@ -4793,7 +4797,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_RECOVER_HP }, - .ignoresProtect = B_UPDATED_MOVE_FLAGS >= GEN_3, + .ignoresProtect = TRUE, .ignoresSubstitute = B_UPDATED_MOVE_FLAGS >= GEN_5, .mirrorMoveBanned = TRUE, .contestEffect = CONTEST_EFFECT_BETTER_IF_SAME_TYPE, @@ -4958,6 +4962,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .category = DAMAGE_CATEGORY_STATUS, .argument = { .protectMethod = PROTECT_NORMAL }, .zMove = { .effect = Z_EFFECT_RESET_STATS }, + .ignoresProtect = TRUE, + .mirrorMoveBanned = TRUE, .metronomeBanned = TRUE, .copycatBanned = TRUE, .assistBanned = TRUE, @@ -7204,6 +7210,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_DEPENDS, .priority = 0, .category = DAMAGE_CATEGORY_STATUS, + .ignoresProtect = TRUE, + .mirrorMoveBanned = TRUE, .metronomeBanned = B_UPDATED_MOVE_FLAGS >= GEN_5, .copycatBanned = TRUE, .sleepTalkBanned = TRUE, @@ -7772,6 +7780,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 4, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_SPD_UP_2 }, + .ignoresProtect = TRUE, + .mirrorMoveBanned = TRUE, .ignoresSubstitute = TRUE, .forcePressure = TRUE, .metronomeBanned = TRUE, From 9e3d7db43fa06b60ff8ccb7880107da4da5bab20 Mon Sep 17 00:00:00 2001 From: GGbond Date: Wed, 25 Feb 2026 02:24:01 +0800 Subject: [PATCH 21/24] Fix Sucker Punch to succeed against Me First targets (#9284) --- src/battle_util.c | 5 ++++- test/battle/move_effect/sucker_punch.c | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index ee3ae241f7..758f3aca84 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2732,10 +2732,13 @@ static enum MoveCanceler CancelerMoveFailure(struct BattleContext *ctx) battleScript = BattleScript_InsomniaProtects; break; case EFFECT_SUCKER_PUNCH: + { + u32 defMove = GetChosenMoveFromPosition(ctx->battlerDef); if (HasBattlerActedThisTurn(ctx->battlerDef) - || (IsBattleMoveStatus(GetChosenMoveFromPosition(ctx->battlerDef)) && !gProtectStructs[ctx->battlerDef].noValidMoves)) + || (IsBattleMoveStatus(defMove) && !gProtectStructs[ctx->battlerDef].noValidMoves && GetMoveEffect(defMove) != EFFECT_ME_FIRST)) battleScript = BattleScript_ButItFailed; break; + } case EFFECT_UPPER_HAND: { u32 prio = GetChosenMovePriority(ctx->battlerDef, ctx->abilities[ctx->battlerDef]); diff --git a/test/battle/move_effect/sucker_punch.c b/test/battle/move_effect/sucker_punch.c index 0fd2273607..039e9541ab 100644 --- a/test/battle/move_effect/sucker_punch.c +++ b/test/battle/move_effect/sucker_punch.c @@ -1,6 +1,11 @@ #include "global.h" #include "test/battle.h" +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_SUCKER_PUNCH) == EFFECT_SUCKER_PUNCH); +} + SINGLE_BATTLE_TEST("Sucker Punch hits targets that are about to attack") { GIVEN { @@ -34,6 +39,25 @@ SINGLE_BATTLE_TEST("Sucker Punch doesn't hit targets using status moves") } } +SINGLE_BATTLE_TEST("Sucker Punch hits targets using Me First") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ME_FIRST) == EFFECT_ME_FIRST); + ASSUME(GetMovePriority(MOVE_SUCKER_PUNCH) > GetMovePriority(MOVE_ME_FIRST)); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SUCKER_PUNCH); MOVE(opponent, MOVE_ME_FIRST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUCKER_PUNCH, player); + HP_BAR(opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ME_FIRST, opponent); + HP_BAR(player); + } + } +} + SINGLE_BATTLE_TEST("Sucker Punch doesn't hit targets that has already moved") { GIVEN { From cabbc632fd017e95e128607ee601e63f0cc8309b Mon Sep 17 00:00:00 2001 From: luuma <31407427+luuma@users.noreply.github.com> Date: Wed, 25 Feb 2026 20:46:22 +0000 Subject: [PATCH 22/24] Fixes plasma fists and several let's go and max move effects which should occur even when fainting a foe (#9262) --- src/battle_script_commands.c | 78 ++++++++++++++++++++++++++------- test/battle/gimmick/dynamax.c | 81 +++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 15 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index de8d68cb51..63118da4a3 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3042,6 +3042,67 @@ static void SetNonVolatileStatus(u32 effectBattler, enum MoveEffect effect, cons gBattleStruct->poisonPuppeteerConfusion = TRUE; } +static inline bool32 IgnoreTargetingForMoveEffect(enum MoveEffect moveEffect) // Currently only used to determine move effects which happen even if the move's defined effectbattler is fainted +{ + switch (moveEffect) + { + case MOVE_EFFECT_PAYDAY: + case MOVE_EFFECT_BUG_BITE: + case MOVE_EFFECT_FLAME_BURST: + case MOVE_EFFECT_STEALTH_ROCK: + case MOVE_EFFECT_STEELSURGE: + case MOVE_EFFECT_SUN: + case MOVE_EFFECT_RAIN: + case MOVE_EFFECT_SANDSTORM: + case MOVE_EFFECT_HAIL: + case MOVE_EFFECT_MISTY_TERRAIN: + case MOVE_EFFECT_GRASSY_TERRAIN: + case MOVE_EFFECT_ELECTRIC_TERRAIN: + case MOVE_EFFECT_PSYCHIC_TERRAIN: + case MOVE_EFFECT_DEFOG: + case MOVE_EFFECT_REFLECT: + case MOVE_EFFECT_LIGHT_SCREEN: + case MOVE_EFFECT_AURORA_VEIL: + case MOVE_EFFECT_GRAVITY: + case MOVE_EFFECT_HEAL_TEAM: + case MOVE_EFFECT_AROMATHERAPY: + case MOVE_EFFECT_RECYCLE_BERRIES: + case MOVE_EFFECT_ION_DELUGE: + case MOVE_EFFECT_HAZE: + case MOVE_EFFECT_RAISE_TEAM_ATTACK: + case MOVE_EFFECT_RAISE_TEAM_DEFENSE: + case MOVE_EFFECT_RAISE_TEAM_SPEED: + case MOVE_EFFECT_RAISE_TEAM_SP_ATK: + case MOVE_EFFECT_RAISE_TEAM_SP_DEF: + case MOVE_EFFECT_CRIT_PLUS_SIDE: + case MOVE_EFFECT_LOWER_ATTACK_SIDE: + case MOVE_EFFECT_LOWER_DEFENSE_SIDE: + case MOVE_EFFECT_LOWER_SPEED_SIDE: + case MOVE_EFFECT_LOWER_SP_ATK_SIDE: + case MOVE_EFFECT_LOWER_SP_DEF_SIDE: + case MOVE_EFFECT_LOWER_SPEED_2_SIDE: + case MOVE_EFFECT_LOWER_EVASIVENESS_SIDE: + case MOVE_EFFECT_VINE_LASH: + case MOVE_EFFECT_WILDFIRE: + case MOVE_EFFECT_CANNONADE: + case MOVE_EFFECT_VOLCALITH: + case MOVE_EFFECT_PREVENT_ESCAPE_SIDE: + case MOVE_EFFECT_SANDBLAST_SIDE: + case MOVE_EFFECT_FIRE_SPIN_SIDE: + case MOVE_EFFECT_PARALYZE_SIDE: + case MOVE_EFFECT_POISON_SIDE: + case MOVE_EFFECT_CONFUSE_PAY_DAY_SIDE: + case MOVE_EFFECT_POISON_PARALYZE_SIDE: + case MOVE_EFFECT_EFFECT_SPORE_SIDE: + case MOVE_EFFECT_INFATUATE_SIDE: + case MOVE_EFFECT_CONFUSE_SIDE: + case MOVE_EFFECT_TORMENT_SIDE: + return TRUE; + default: + return FALSE; + } +} + // To avoid confusion the arguments are naned battler/effectBattler since they can be different from gBattlerAttacker/gBattlerTarget void SetMoveEffect(u32 battler, u32 effectBattler, enum MoveEffect moveEffect, const u8 *battleScript, enum SetMoveEffectFlags effectFlags) { @@ -3052,7 +3113,6 @@ void SetMoveEffect(u32 battler, u32 effectBattler, enum MoveEffect moveEffect, c bool32 mirrorArmorReflected = (GetBattlerAbility(gBattlerTarget) == ABILITY_MIRROR_ARMOR); union StatChangeFlags flags = {0}; u32 battlerAbility; - bool32 activateAfterFaint = FALSE; // NULL move effect if (moveEffect == MOVE_EFFECT_NONE) @@ -3065,19 +3125,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, enum MoveEffect moveEffect, c gBattlescriptCurrInstr = battleScript; return; } - - switch (moveEffect) // Set move effects which happen later on - { - case MOVE_EFFECT_STEALTH_ROCK: - case MOVE_EFFECT_PAYDAY: - case MOVE_EFFECT_BUG_BITE: - case MOVE_EFFECT_FLAME_BURST: - activateAfterFaint = TRUE; - break; - default: - break; - } - + gBattleScripting.battler = battler; gEffectBattler = effectBattler; battlerAbility = GetBattlerAbility(gEffectBattler); @@ -3088,7 +3136,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, enum MoveEffect moveEffect, c && IsSheerForceAffected(gCurrentMove, GetBattlerAbility(battler)) && !(GetMoveEffect(gCurrentMove) == EFFECT_ORDER_UP && gBattleStruct->battlerState[gBattlerAttacker].commanderSpecies != SPECIES_NONE)) moveEffect = MOVE_EFFECT_NONE; - else if (!IsBattlerAlive(gEffectBattler) && !activateAfterFaint) + else if (!IsBattlerAlive(gEffectBattler) && !IgnoreTargetingForMoveEffect(moveEffect)) moveEffect = MOVE_EFFECT_NONE; else if (DoesSubstituteBlockMove(gBattlerAttacker, gEffectBattler, gCurrentMove) && !affectsUser) moveEffect = MOVE_EFFECT_NONE; diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index 787682417f..2323469d3a 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -997,6 +997,7 @@ DOUBLE_BATTLE_TEST("Dynamax: G-Max Volt Crash paralyzes both opponents") TURN { MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); } } SCENE { MESSAGE("Pikachu used G-Max Volt Crash!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_VOLT_CRASH, playerLeft); ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponentLeft); MESSAGE("The opposing Wobbuffet is paralyzed, so it may be unable to move!"); STATUS_ICON(opponentLeft, paralysis: TRUE); @@ -1309,6 +1310,7 @@ DOUBLE_BATTLE_TEST("Dynamax: G-Max Replenish recycles allies' berries 50\% of th PASSES_RANDOMLY(1, 2, RNG_G_MAX_REPLENISH); GIVEN { ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_REPLENISH, MOVE_EFFECT_RECYCLE_BERRIES)); + ASSUME(GetItemHoldEffect(ITEM_APICOT_BERRY) == HOLD_EFFECT_SP_DEFENSE_UP); PLAYER(SPECIES_SNORLAX) { Item(ITEM_APICOT_BERRY); GigantamaxFactor(TRUE); } PLAYER(SPECIES_MUNCHLAX) { Item(ITEM_APICOT_BERRY); Ability(ABILITY_THICK_FAT); } OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_APICOT_BERRY); } @@ -1321,12 +1323,14 @@ DOUBLE_BATTLE_TEST("Dynamax: G-Max Replenish recycles allies' berries 50\% of th TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); } } SCENE { // turn 1 + MESSAGE("Using Apicot Berry, the Sp. Def of Snorlax rose!"); MESSAGE("Using Apicot Berry, the Sp. Def of Munchlax rose!"); MESSAGE("Using Apicot Berry, the Sp. Def of the opposing Wobbuffet rose!"); MESSAGE("Using Apicot Berry, the Sp. Def of the opposing Wobbuffet rose!"); // turn 2 MESSAGE("Snorlax used G-Max Replenish!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_REPLENISH, playerLeft); MESSAGE("Snorlax found one Apicot Berry!"); MESSAGE("Munchlax found one Apicot Berry!"); } @@ -1369,6 +1373,7 @@ DOUBLE_BATTLE_TEST("Dynamax: G-Max Finale heals allies by 1/6 of their health") TURN { MOVE(playerLeft, MOVE_MOONBLAST, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); } } SCENE { MESSAGE("Alcremie used G-Max Finale!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_FINALE, playerLeft); HP_BAR(playerLeft, captureDamage: &damage1); HP_BAR(playerRight, captureDamage: &damage2); } THEN { @@ -1710,5 +1715,81 @@ DOUBLE_BATTLE_TEST("Dynamax stat raising moves don't make stat-changing abilitie } } +DOUBLE_BATTLE_TEST("Dynamax: G-Max Finale heals allies by 1/6 of their health, even if it faints the foe") +{ + s16 damage1, damage2; + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_FINALE, MOVE_EFFECT_HEAL_TEAM)); + PLAYER(SPECIES_ALCREMIE) { HP(1); GigantamaxFactor(TRUE); } + PLAYER(SPECIES_MILCERY) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_MOONBLAST, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); SEND_OUT(opponentLeft, 2); } + } SCENE { + MESSAGE("Alcremie used G-Max Finale!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_FINALE, playerLeft); + HP_BAR(playerLeft, captureDamage: &damage1); + HP_BAR(playerRight, captureDamage: &damage2); + } THEN { + EXPECT_MUL_EQ(-damage1, Q_4_12(6), playerLeft->maxHP); // heals based on Dynamax HP. Appears to have a problem with milcery in this case!? + } +} + +DOUBLE_BATTLE_TEST("Dynamax: G-Max Replenish recycles allies' berries 50\% of the time, even if it faints the foe") +{ + PASSES_RANDOMLY(1, 2, RNG_G_MAX_REPLENISH); + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_REPLENISH, MOVE_EFFECT_RECYCLE_BERRIES)); + ASSUME(GetItemHoldEffect(ITEM_APICOT_BERRY) == HOLD_EFFECT_SP_DEFENSE_UP); + PLAYER(SPECIES_SNORLAX) { Item(ITEM_APICOT_BERRY); GigantamaxFactor(TRUE); } + PLAYER(SPECIES_MUNCHLAX) { Item(ITEM_APICOT_BERRY); Ability(ABILITY_THICK_FAT); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_STUFF_CHEEKS); \ + MOVE(playerRight, MOVE_STUFF_CHEEKS); \ + MOVE(opponentLeft, MOVE_CELEBRATE); \ + MOVE(opponentRight, MOVE_CELEBRATE); } + TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); SEND_OUT(opponentLeft, 2);} + } SCENE { + // turn 1 + MESSAGE("Using Apicot Berry, the Sp. Def of Snorlax rose!"); + MESSAGE("Using Apicot Berry, the Sp. Def of Munchlax rose!"); + // turn 2 + MESSAGE("Snorlax used G-Max Replenish!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_REPLENISH, playerLeft); + MESSAGE("Snorlax found one Apicot Berry!"); + MESSAGE("Munchlax found one Apicot Berry!"); + } +} + +DOUBLE_BATTLE_TEST("Dynamax: G-Max Volt Crash paralyzes other opponent even if its target faints") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_VOLT_CRASH, MOVE_EFFECT_PARALYZE_SIDE)); + PLAYER(SPECIES_PIKACHU) { GigantamaxFactor(TRUE); } + PLAYER(SPECIES_PICHU); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); SEND_OUT(opponentLeft, 2); } + } SCENE { + MESSAGE("Pikachu used G-Max Volt Crash!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_VOLT_CRASH, playerLeft); + NONE_OF { + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponentLeft); + STATUS_ICON(opponentLeft, paralysis: TRUE); + } + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponentRight); + MESSAGE("The opposing Wynaut is paralyzed, so it may be unable to move!"); + STATUS_ICON(opponentRight, paralysis: TRUE); + } +} + TO_DO_BATTLE_TEST("Dynamax: Contrary inverts stat-lowering Max Moves, without showing a message") TO_DO_BATTLE_TEST("Dynamax: Contrary inverts stat-increasing Max Moves, without showing a message") From 217b6d6da21bbf29db62cee299f7e3af5cd34ac6 Mon Sep 17 00:00:00 2001 From: GGbond Date: Thu, 26 Feb 2026 18:41:52 +0800 Subject: [PATCH 23/24] Fix Paradox stat selection under Wonder Room (#9356) --- src/battle_util.c | 19 ++++++++++++++++++- test/battle/ability/protosynthesis.c | 17 +++++++++++++++++ test/battle/ability/quark_drive.c | 17 +++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index 758f3aca84..f1aeb2ee8b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5996,6 +5996,7 @@ static u32 GetStatValueWithStages(u32 battler, u32 stat) u32 GetParadoxHighestStatId(u32 battler) { u32 highestId = STAT_ATK; + bool32 wonderRoom = (gFieldStatuses & STATUS_FIELD_WONDER_ROOM) != 0; u32 highestStat = GetStatValueWithStages(battler, STAT_ATK); for (u32 stat = STAT_DEF; stat < NUM_STATS; stat++) @@ -6003,7 +6004,23 @@ u32 GetParadoxHighestStatId(u32 battler) if (stat == STAT_SPEED) continue; - u32 statValue = GetStatValueWithStages(battler, stat); + u32 statValue; + switch (stat) + { + case STAT_DEF: + statValue = wonderRoom ? gBattleMons[battler].spDefense : gBattleMons[battler].defense; + statValue *= gStatStageRatios[gBattleMons[battler].statStages[STAT_DEF]][0]; + statValue /= gStatStageRatios[gBattleMons[battler].statStages[STAT_DEF]][1]; + break; + case STAT_SPDEF: + statValue = wonderRoom ? gBattleMons[battler].defense : gBattleMons[battler].spDefense; + statValue *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPDEF]][0]; + statValue /= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPDEF]][1]; + break; + default: + statValue = GetStatValueWithStages(battler, stat); + break; + } if (statValue > highestStat) { highestStat = statValue; diff --git a/test/battle/ability/protosynthesis.c b/test/battle/ability/protosynthesis.c index 7f179d592e..bf9f911d75 100644 --- a/test/battle/ability/protosynthesis.c +++ b/test/battle/ability/protosynthesis.c @@ -134,6 +134,23 @@ SINGLE_BATTLE_TEST("Protosynthesis prioritizes stats in the case of a tie in the } } +SINGLE_BATTLE_TEST("Protosynthesis uses Wonder Room swapped defenses when choosing boosted stat") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_WONDER_ROOM) == EFFECT_WONDER_ROOM); + PLAYER(SPECIES_ROARING_MOON) { Ability(ABILITY_PROTOSYNTHESIS); Attack(50); Defense(200); SpAttack(40); SpDefense(60); Speed(70); Moves(MOVE_WONDER_ROOM); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SUNNY_DAY); Speed(60); } + } WHEN { + TURN { MOVE(player, MOVE_WONDER_ROOM); MOVE(opponent, MOVE_SUNNY_DAY); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WONDER_ROOM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent); + ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + MESSAGE("The harsh sunlight activated Roaring Moon's Protosynthesis!"); + MESSAGE("Roaring Moon's Sp. Def was heightened!"); + } +} + SINGLE_BATTLE_TEST("Protosynthesis activates in Sun before Booster Energy") { GIVEN { diff --git a/test/battle/ability/quark_drive.c b/test/battle/ability/quark_drive.c index bdb8a26201..e95e571b50 100644 --- a/test/battle/ability/quark_drive.c +++ b/test/battle/ability/quark_drive.c @@ -201,6 +201,23 @@ SINGLE_BATTLE_TEST("Quark Drive prioritizes stats in the case of a tie in the fo } } +SINGLE_BATTLE_TEST("Quark Drive uses Wonder Room swapped defenses when choosing boosted stat") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_WONDER_ROOM) == EFFECT_WONDER_ROOM); + PLAYER(SPECIES_IRON_LEAVES) { Ability(ABILITY_QUARK_DRIVE); Attack(50); Defense(200); SpAttack(40); SpDefense(60); Speed(70); Moves(MOVE_WONDER_ROOM); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_ELECTRIC_TERRAIN); Speed(60); } + } WHEN { + TURN { MOVE(player, MOVE_WONDER_ROOM); MOVE(opponent, MOVE_ELECTRIC_TERRAIN); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WONDER_ROOM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, opponent); + ABILITY_POPUP(player, ABILITY_QUARK_DRIVE); + MESSAGE("The Electric Terrain activated Iron Leaves's Quark Drive!"); + MESSAGE("Iron Leaves's Sp. Def was heightened!"); + } +} + SINGLE_BATTLE_TEST("Quark Drive activates in Electric Terrain before Booster Energy") { GIVEN { From 3bb1b3502aa0f3873b433baed08fe2525b1993e0 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 26 Feb 2026 17:00:41 +0100 Subject: [PATCH 24/24] Fix illegal switch due to hazards (#9361) --- src/battle_script_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 63118da4a3..4d10ca0202 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -4382,11 +4382,11 @@ static void Cmd_tryfaintmon(void) if (cmd->battler == BS_TARGET && gCurrentMove != MOVE_NONE) TryUpdateEvolutionTracker(IF_DEFEAT_X_WITH_ITEMS, 1, MOVE_NONE); - gBattlerFainted = battler; faintScript = BattleScript_FaintBattler; if (!(gAbsentBattlerFlags & (1u << battler)) && !IsBattlerAlive(battler)) { + gBattlerFainted = battler; gHitMarker |= HITMARKER_FAINTED(battler); BattleScriptPush(cmd->nextInstr); gBattlescriptCurrInstr = faintScript;