From dd6746e5068b221ee8187977b575b17617f7e70c Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Sun, 12 Oct 2025 17:56:46 +0200 Subject: [PATCH 001/130] Fix bug causing hgss dex to freeze (#7936) --- src/pokedex_plus_hgss.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index 2e4463ac80..6e6d5abe22 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -2421,6 +2421,8 @@ static bool8 LoadPokedexListPage(u8 page) // when returning to search results after selecting an evo, we have to restore // the original dexNum because the search results page doesn't rebuild the list sPokedexListItem->dexNum = sPokedexView->originalSearchSelectionNum; + sPokedexListItem->seen = GetSetPokedexFlag(sPokedexView->originalSearchSelectionNum, FLAG_GET_SEEN); + sPokedexListItem->owned = GetSetPokedexFlag(sPokedexView->originalSearchSelectionNum, FLAG_GET_CAUGHT); sPokedexView->originalSearchSelectionNum = 0; } CreateMonSpritesAtPos(sPokedexView->selectedPokemon, 0xE); From daf642fdbf21ce9fc1f04b01da8c7d31ad9ab1d5 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Mon, 13 Oct 2025 13:12:50 +0200 Subject: [PATCH 002/130] Fix multiple battle arena bugs (#7941) --- asm/macros/battle_script.inc | 3 +-- data/battle_scripts_1.s | 18 +++++++++--------- src/battle_script_commands.c | 3 +-- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index a301f0feb2..547e4d6e3f 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -2102,9 +2102,8 @@ .byte \id .endm - .macro arenawaitmessage id:req + .macro arenawaitmessage callnative BS_ArenaWaitMessage - .byte \id .endm .macro waitcry diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index f1eeeccdc9..004594753f 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8711,7 +8711,7 @@ BattleScript_ArenaTurnBeginning:: playse SE_ARENA_TIMEUP1 drawarenareftextbox arenajudgmentstring B_MSG_REF_COMMENCE_BATTLE - arenawaitmessage B_MSG_REF_COMMENCE_BATTLE + arenawaitmessage pause B_WAIT_TIME_LONG erasearenareftextbox volumeup @@ -8729,26 +8729,26 @@ BattleScript_ArenaDoJudgment:: pause B_WAIT_TIME_LONG drawarenareftextbox arenajudgmentstring B_MSG_REF_THATS_IT - arenawaitmessage B_MSG_REF_THATS_IT + arenawaitmessage pause B_WAIT_TIME_LONG setbyte gBattleCommunication, 0 @ Reset state for arenajudgmentwindow arenajudgmentwindow pause B_WAIT_TIME_LONG arenajudgmentwindow arenajudgmentstring B_MSG_REF_JUDGE_MIND - arenawaitmessage B_MSG_REF_JUDGE_MIND + arenawaitmessage arenajudgmentwindow arenajudgmentstring B_MSG_REF_JUDGE_SKILL - arenawaitmessage B_MSG_REF_JUDGE_SKILL + arenawaitmessage arenajudgmentwindow arenajudgmentstring B_MSG_REF_JUDGE_BODY - arenawaitmessage B_MSG_REF_JUDGE_BODY + arenawaitmessage arenajudgmentwindow jumpifbyte CMP_EQUAL, gBattleCommunication + 1, ARENA_RESULT_PLAYER_LOST, BattleScript_ArenaJudgmentPlayerLoses jumpifbyte CMP_EQUAL, gBattleCommunication + 1, ARENA_RESULT_TIE, BattleScript_ArenaJudgmentDraw @ ARENA_RESULT_PLAYER_WON arenajudgmentstring B_MSG_REF_PLAYER_WON - arenawaitmessage B_MSG_REF_PLAYER_WON + arenawaitmessage arenajudgmentwindow erasearenareftextbox printstring STRINGID_DEFEATEDOPPONENTBYREFEREE @@ -8763,7 +8763,7 @@ BattleScript_ArenaDoJudgment:: BattleScript_ArenaJudgmentPlayerLoses: arenajudgmentstring B_MSG_REF_OPPONENT_WON - arenawaitmessage B_MSG_REF_OPPONENT_WON + arenawaitmessage arenajudgmentwindow erasearenareftextbox printstring STRINGID_LOSTTOOPPONENTBYREFEREE @@ -8778,11 +8778,12 @@ BattleScript_ArenaJudgmentPlayerLoses: BattleScript_ArenaJudgmentDraw: arenajudgmentstring B_MSG_REF_DRAW - arenawaitmessage B_MSG_REF_DRAW + arenawaitmessage arenajudgmentwindow erasearenareftextbox printstring STRINGID_TIEDOPPONENTBYREFEREE waitmessage B_WAIT_TIME_LONG + arenabothmonslost playfaintcry BS_PLAYER1 waitcry dofaintanimation BS_PLAYER1 @@ -8793,7 +8794,6 @@ BattleScript_ArenaJudgmentDraw: dofaintanimation BS_OPPONENT1 cleareffectsonfaint BS_OPPONENT1 waitanimation - arenabothmonslost end2 BattleScript_AskIfWantsToForfeitMatch:: diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 45fa3f3e97..b202cd9850 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -17122,13 +17122,12 @@ void BS_EraseArenaRefTextBox(void) void BS_ArenaJudgmentString(void) { - CMD_ARGS(u8 id); + NATIVE_ARGS(u8 id); BattleStringExpandPlaceholdersToDisplayedString(gRefereeStringsTable[cmd->id]); BattlePutTextOnWindow(gDisplayedStringBattle, ARENA_WIN_JUDGMENT_TEXT); gBattlescriptCurrInstr = cmd->nextInstr; } -// Argument passed but no use void BS_ArenaWaitMessage(void) { NATIVE_ARGS(); From 78b11bb35ffdd9ffe07a52ca9c5be30d672946bb Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Mon, 13 Oct 2025 12:55:39 +0100 Subject: [PATCH 003/130] Fixes Cursed Body failing to disable moves on the last PP (#7940) --- src/battle_util.c | 3 +- test/battle/ability/cursed_body.c | 75 +++++++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 6 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index ee98c8ddb1..58349507a7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4532,8 +4532,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && gDisableStructs[gBattlerAttacker].disabledMove == MOVE_NONE && IsBattlerAlive(gBattlerAttacker) && !IsAbilityOnSide(gBattlerAttacker, ABILITY_AROMA_VEIL) - && gBattleMons[gBattlerAttacker].pp[gChosenMovePos] != 0 - && !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) // TODO: Max Moves don't make contact, useless? + && gChosenMove != MOVE_STRUGGLE && RandomPercentage(RNG_CURSED_BODY, 30)) { gDisableStructs[gBattlerAttacker].disabledMove = gChosenMove; diff --git a/test/battle/ability/cursed_body.c b/test/battle/ability/cursed_body.c index 25dbdc4b5b..b3bc886a3d 100644 --- a/test/battle/ability/cursed_body.c +++ b/test/battle/ability/cursed_body.c @@ -16,10 +16,77 @@ SINGLE_BATTLE_TEST("Cursed Body triggers 30% of the time") } } -TO_DO_BATTLE_TEST("Cursed Body cannot disable Struggle") -TO_DO_BATTLE_TEST("Cursed Body can trigger if the attacker is behind a Substitute") -TO_DO_BATTLE_TEST("Cursed Body cannot trigger if the target is behind a Substitute") -TO_DO_BATTLE_TEST("Cursed Body does not stop a multistrike move mid-execution") +SINGLE_BATTLE_TEST("Cursed Body cannot disable Struggle") +{ + GIVEN { + ASSUME(GetItemHoldEffect(ITEM_CHOICE_SCARF) == HOLD_EFFECT_CHOICE_SCARF); + ASSUME(GetMoveEffect(MOVE_TAUNT) == EFFECT_TAUNT); + ASSUME(GetMoveCategory(MOVE_CELEBRATE) == DAMAGE_CATEGORY_STATUS); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_CHOICE_SCARF); Moves(MOVE_CELEBRATE); } + OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TAUNT); } + TURN { FORCED_MOVE(player); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_CURSED_BODY); + MESSAGE("Wobbuffet's Struggle was disabled by the opposing Frillish's Cursed Body!"); + } + } +} + +SINGLE_BATTLE_TEST("Cursed Body can trigger if the attacker is behind a Substitute") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_SUBSTITUTE); } + TURN { MOVE(player, MOVE_AQUA_JET); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_JET, player); + ABILITY_POPUP(opponent, ABILITY_CURSED_BODY); + MESSAGE("Wobbuffet's Aqua Jet was disabled by the opposing Frillish's Cursed Body!"); + } +} + +SINGLE_BATTLE_TEST("Cursed Body cannot trigger if the target is behind a Substitute") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); } + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); } + TURN { MOVE(player, MOVE_AQUA_JET); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_JET, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_CURSED_BODY); + MESSAGE("Wobbuffet's Aqua Jet was disabled by the opposing Frillish's Cursed Body!"); + } + } +} + +SINGLE_BATTLE_TEST("Cursed Body does not stop a multistrike move mid-execution") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ROCK_BLAST) == EFFECT_MULTI_HIT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_ROCK_BLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_BLAST, player); + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_CURSED_BODY); + MESSAGE("Wobbuffet's Rock Blast was disabled by the opposing Frillish's Cursed Body!"); + HP_BAR(opponent); + } +} + TO_DO_BATTLE_TEST("Cursed Body disables the move that called another move instead of the called move") TO_DO_BATTLE_TEST("Cursed Body disables damaging Z-Moves, but not the base move") // Rotom Powers can restore Z-Moves TO_DO_BATTLE_TEST("Cursed Body disables the base move of a status Z-Move") From 65dd96a043f9fe4b53e22d52bceac160252c43bf Mon Sep 17 00:00:00 2001 From: Linathan <35115312+LinathanZel@users.noreply.github.com> Date: Mon, 13 Oct 2025 08:02:47 -0400 Subject: [PATCH 004/130] =?UTF-8?q?Fixed=20an=20issue=20related=20to=20Pok?= =?UTF-8?q?emon=20animation=20bleeding=20into=20attack=20anim=E2=80=A6=20(?= =?UTF-8?q?#7924)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: LinathanZel --- data/battle_scripts_2.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 data/battle_scripts_2.s diff --git a/data/battle_scripts_2.s b/data/battle_scripts_2.s old mode 100644 new mode 100755 index b46b30a443..586a79c5ef --- a/data/battle_scripts_2.s +++ b/data/battle_scripts_2.s @@ -212,9 +212,9 @@ BattleScript_WallyBallThrow:: BattleScript_ShakeBallThrow:: animatewildpokemonafterfailedpokeball BS_TARGET - waitstate waitmessage B_WAIT_TIME_LONG printfromtable gBallEscapeStringIds + waitanimation waitmessage B_WAIT_TIME_LONG jumpifword CMP_NO_COMMON_BITS, gBattleTypeFlags, BATTLE_TYPE_SAFARI, BattleScript_ShakeBallThrowEnd jumpifbyte CMP_NOT_EQUAL, gNumSafariBalls, 0, BattleScript_ShakeBallThrowEnd From 136e976362a8f027d631b8f78a5d6abc501c327e Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 13 Oct 2025 15:38:23 +0200 Subject: [PATCH 005/130] Fixes terrain not failing on duplicate (#7939) Co-authored-by: PhallenTree <168426989+PhallenTree@users.noreply.github.com> --- src/battle_script_commands.c | 53 +++++++++++++++++-------- test/battle/move_effect/misty_terrain.c | 13 ++++++ 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index b202cd9850..b198d4b589 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -15285,34 +15285,55 @@ void BS_SetTerrain(void) switch (GetMoveEffect(gCurrentMove)) { case EFFECT_MISTY_TERRAIN: - statusFlag = STATUS_FIELD_MISTY_TERRAIN; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; + if (!(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + { + statusFlag = STATUS_FIELD_MISTY_TERRAIN; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; + } break; case EFFECT_GRASSY_TERRAIN: - statusFlag = STATUS_FIELD_GRASSY_TERRAIN; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_GRASSY; + if (!(gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)) + { + statusFlag = STATUS_FIELD_GRASSY_TERRAIN; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_GRASSY; + } break; case EFFECT_ELECTRIC_TERRAIN: - statusFlag = STATUS_FIELD_ELECTRIC_TERRAIN; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; + if (!(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)) + { + statusFlag = STATUS_FIELD_ELECTRIC_TERRAIN; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; + } break; case EFFECT_PSYCHIC_TERRAIN: - statusFlag = STATUS_FIELD_PSYCHIC_TERRAIN; - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC; + if (!(gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)) + { + statusFlag = STATUS_FIELD_PSYCHIC_TERRAIN; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC; + } break; case EFFECT_HIT_SET_TERRAIN: - statusFlag = GetMoveTerrainFlag(gCurrentMove); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC; + if (!(gFieldStatuses & GetMoveTerrainFlag(gCurrentMove))) + { + statusFlag = GetMoveTerrainFlag(gCurrentMove); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC; + } break; default: break; } - enum ItemHoldEffect atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); - - gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; - gFieldStatuses |= statusFlag; - gFieldTimers.terrainTimer = gBattleTurnCounter + (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5; - gBattlescriptCurrInstr = cmd->nextInstr; + if (statusFlag) + { + enum ItemHoldEffect atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); + gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; + gFieldStatuses |= statusFlag; + gFieldTimers.terrainTimer = gBattleTurnCounter + (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5; + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + gBattlescriptCurrInstr = cmd->jumpInstr; + } } void BS_JumpIfTerrainAffected(void) diff --git a/test/battle/move_effect/misty_terrain.c b/test/battle/move_effect/misty_terrain.c index b96f0c650d..cd9a92eb37 100644 --- a/test/battle/move_effect/misty_terrain.c +++ b/test/battle/move_effect/misty_terrain.c @@ -87,3 +87,16 @@ SINGLE_BATTLE_TEST("Misty Terrain lasts for 5 turns") MESSAGE("The mist disappeared from the battlefield."); } } + +SINGLE_BATTLE_TEST("Misty Terrain will fail if there is one already on the field") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MISTY_TERRAIN); MOVE(opponent, MOVE_MISTY_TERRAIN); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MISTY_TERRAIN, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_MISTY_TERRAIN, opponent); + } +} From 14710eba04d087015c52038bd8dac636a338207b Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Mon, 13 Oct 2025 23:52:21 +0200 Subject: [PATCH 006/130] Fix volt tackle not inflicting recoil (#7944) --- src/data/moves_info.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/moves_info.h b/src/data/moves_info.h index bf7d500d92..35fed13f71 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -9079,7 +9079,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = "A life-risking tackle that\n" "slightly hurts the user."), #endif - .effect = EFFECT_HIT, + .effect = EFFECT_RECOIL, .power = 120, .type = TYPE_ELECTRIC, .accuracy = 100, From 80ec69fc5d6eb5d392124e902c67ad68164192c5 Mon Sep 17 00:00:00 2001 From: Jan Helbling Date: Tue, 14 Oct 2025 10:35:53 +0200 Subject: [PATCH 007/130] switched from head -c 12 to dd bs=1 count=12, to successful compiling with OpenBSD (#2091) Co-authored-by: Jan Helbling --- graphics_file_rules.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphics_file_rules.mk b/graphics_file_rules.mk index 05dfe5a2d5..e309ba25a7 100644 --- a/graphics_file_rules.mk +++ b/graphics_file_rules.mk @@ -431,7 +431,7 @@ $(RAYQUAZAGFXDIR)/scene_3/rayquaza.4bpp: %.4bpp: %.png $(RAYQUAZAGFXDIR)/scene_3/rayquaza_tail_fix.4bpp: $(RAYQUAZAGFXDIR)/scene_3/rayquaza_tail.4bpp cp $< $@ - head -c 12 /dev/zero >> $@ + dd if=/dev/zero bs=1 count=12 >> $@ $(RAYQUAZAGFXDIR)/scene_4/streaks.4bpp: %.4bpp: %.png $(GFX) $< $@ -num_tiles 19 -Wnum_tiles From d05742ca05e840b98a5629c03cc369a8a80086f6 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Tue, 14 Oct 2025 10:36:32 +0200 Subject: [PATCH 008/130] Fix include order in wild_encounter.c (#2185) --- src/wild_encounter.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 7ac9ae0e8f..0656b19853 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -1,21 +1,21 @@ #include "global.h" -#include "wild_encounter.h" -#include "pokemon.h" -#include "metatile_behavior.h" -#include "fieldmap.h" -#include "random.h" -#include "field_player_avatar.h" -#include "event_data.h" -#include "safari_zone.h" -#include "overworld.h" -#include "pokeblock.h" #include "battle_setup.h" -#include "roamer.h" -#include "tv.h" -#include "link.h" -#include "script.h" #include "battle_pike.h" #include "battle_pyramid.h" +#include "event_data.h" +#include "fieldmap.h" +#include "field_player_avatar.h" +#include "link.h" +#include "metatile_behavior.h" +#include "overworld.h" +#include "pokeblock.h" +#include "pokemon.h" +#include "random.h" +#include "roamer.h" +#include "safari_zone.h" +#include "script.h" +#include "tv.h" +#include "wild_encounter.h" #include "constants/abilities.h" #include "constants/game_stat.h" #include "constants/items.h" From f4f7c38255c0bc29667af99badb9d98cc5407345 Mon Sep 17 00:00:00 2001 From: Estellar <137097857+estellarc@users.noreply.github.com> Date: Tue, 14 Oct 2025 05:41:27 -0300 Subject: [PATCH 009/130] Use MainCallback wherever posible (#2184) --- include/battle.h | 3 ++- include/battle_pyramid_bag.h | 7 ++++--- include/dodrio_berry_picking.h | 4 +++- include/item_menu.h | 9 +++++---- include/mail.h | 4 +++- include/rayquaza_scene.h | 4 +++- include/save.h | 4 +++- src/battle_main.c | 2 +- src/battle_pyramid_bag.c | 3 +-- src/dodrio_berry_picking.c | 5 ++--- src/frontier_pass.c | 14 +++++++------- src/item_menu.c | 5 ++--- src/mail.c | 3 +-- src/pokeblock.c | 4 ++-- src/rayquaza_scene.c | 3 +-- src/save.c | 5 ++--- src/save_failed_screen.c | 2 +- src/shop.c | 2 +- src/trainer_card.c | 2 +- src/use_pokeblock.c | 4 ++-- 20 files changed, 47 insertions(+), 42 deletions(-) diff --git a/include/battle.h b/include/battle.h index 855f085de0..4bd8aaaf35 100644 --- a/include/battle.h +++ b/include/battle.h @@ -12,6 +12,7 @@ #include "battle_util2.h" #include "battle_bg.h" #include "pokeball.h" +#include "main.h" #define GET_BATTLER_SIDE(battler) (GetBattlerPosition(battler) & BIT_SIDE) #define GET_BATTLER_SIDE2(battler) (gBattlerPositions[battler] & BIT_SIDE) @@ -716,7 +717,7 @@ extern u16 gBattleMovePower; extern u16 gMoveToLearn; extern u8 gBattleMonForms[MAX_BATTLERS_COUNT]; -extern void (*gPreBattleCallback1)(void); +extern MainCallback gPreBattleCallback1; extern void (*gBattleMainFunc)(void); extern struct BattleResults gBattleResults; extern u8 gLeveledUpInBattle; diff --git a/include/battle_pyramid_bag.h b/include/battle_pyramid_bag.h index 258ca56b21..0caf8d302c 100644 --- a/include/battle_pyramid_bag.h +++ b/include/battle_pyramid_bag.h @@ -2,6 +2,7 @@ #define GUARD_BATTLE_PYRAMID_BAG_H #include "list_menu.h" +#include "main.h" enum { PYRAMIDBAG_LOC_FIELD, @@ -29,7 +30,7 @@ enum { struct PyramidBagMenu { - void (*newScreenCallback)(void); + MainCallback newScreenCallback; u8 tilemapBuffer[BG_SCREEN_SIZE]; u8 spriteIds[PBAG_SPRITE_COUNT]; u8 windowIds[5]; @@ -49,7 +50,7 @@ struct PyramidBagMenu struct PyramidBagMenuState { - void (*exitCallback)(void); + MainCallback exitCallback; u8 location; u16 cursorPosition; u16 scrollPosition; @@ -63,7 +64,7 @@ void CB2_PyramidBagMenuFromStartMenu(void); void CB2_ReturnToPyramidBagMenu(void); void UpdatePyramidBagList(void); void UpdatePyramidBagCursorPos(void); -void GoToBattlePyramidBagMenu(u8 location, void (*exitCallback)(void)); +void GoToBattlePyramidBagMenu(u8 location, MainCallback exitCallback); void Task_CloseBattlePyramidBagMessage(u8 taskId); void TryStoreHeldItemsInPyramidBag(void); void ChooseItemsToTossFromPyramidBag(void); diff --git a/include/dodrio_berry_picking.h b/include/dodrio_berry_picking.h index a93fb27cd1..ec62e51947 100644 --- a/include/dodrio_berry_picking.h +++ b/include/dodrio_berry_picking.h @@ -1,7 +1,9 @@ #ifndef GUARD_DODRIO_BERRY_PICKING_H #define GUARD_DODRIO_BERRY_PICKING_H -void StartDodrioBerryPicking(u16 partyId, void (*exitCallback)(void)); +#include "main.h" + +void StartDodrioBerryPicking(u16 partyId, MainCallback exitCallback); void IsDodrioInParty(void); void ShowDodrioBerryPickingRecords(void); diff --git a/include/item_menu.h b/include/item_menu.h index dc7b44a0d6..2f7f3f3825 100644 --- a/include/item_menu.h +++ b/include/item_menu.h @@ -2,6 +2,7 @@ #define GUARD_ITEM_MENU_H #include "item.h" +#include "main.h" #include "menu_helpers.h" enum { @@ -47,7 +48,7 @@ enum { struct BagPosition { - void (*exitCallback)(void); + MainCallback exitCallback; u8 location; u8 pocket; u16 pocketSwitchArrowPos; @@ -59,7 +60,7 @@ extern struct BagPosition gBagPosition; struct BagMenu { - void (*newScreenCallback)(void); + MainCallback newScreenCallback; u8 tilemapBuffer[BG_SCREEN_SIZE]; u8 spriteIds[ITEMMENUSPRITE_COUNT]; u8 windowIds[ITEMWIN_COUNT]; @@ -96,10 +97,10 @@ void CB2_BagMenuFromStartMenu(void); u8 GetItemListPosition(u8 pocketId); bool8 UseRegisteredKeyItemOnField(void); void CB2_GoToSellMenu(void); -void GoToBagMenu(u8 location, u8 pocket, void ( *exitCallback)()); +void GoToBagMenu(u8 location, u8 pocket, MainCallback exitCallback); void DoWallyTutorialBagMenu(void); void ResetBagScrollPositions(void); -void ChooseBerryForMachine(void (*exitCallback)(void)); +void ChooseBerryForMachine(MainCallback exitCallback); void CB2_ChooseBerry(void); void Task_FadeAndCloseBagMenu(u8 taskId); void BagMenu_YesNo(u8 taskId, u8 windowType, const struct YesNoFuncTable *funcTable); diff --git a/include/mail.h b/include/mail.h index 403078f097..de29b21d42 100644 --- a/include/mail.h +++ b/include/mail.h @@ -1,6 +1,8 @@ #ifndef GUARD_MAIL_H #define GUARD_MAIL_H +#include "main.h" + #define IS_ITEM_MAIL(itemId) ((itemId == ITEM_ORANGE_MAIL \ || itemId == ITEM_HARBOR_MAIL \ || itemId == ITEM_GLITTER_MAIL \ @@ -15,7 +17,7 @@ || itemId == ITEM_RETRO_MAIL)) // mail.h -void ReadMail(struct Mail *mail, void (*exitCallback)(void), bool8 hasText); +void ReadMail(struct Mail *mail, MainCallback exitCallback, bool8 hasText); // mail_data.h void ClearAllMail(void); diff --git a/include/rayquaza_scene.h b/include/rayquaza_scene.h index 422b591273..6a51b9a45f 100644 --- a/include/rayquaza_scene.h +++ b/include/rayquaza_scene.h @@ -1,6 +1,8 @@ #ifndef GUARD_RAYQUAZA_SCENE_H #define GUARD_RAYQUAZA_SCENE_H -void DoRayquazaScene(u8 animId, bool8 endEarly, void (*exitCallback)(void)); +#include "main.h" + +void DoRayquazaScene(u8 animId, bool8 endEarly, MainCallback exitCallback); #endif // GUARD_RAYQUAZA_SCENE_H diff --git a/include/save.h b/include/save.h index 41913d1df4..923c672f50 100644 --- a/include/save.h +++ b/include/save.h @@ -1,6 +1,8 @@ #ifndef GUARD_SAVE_H #define GUARD_SAVE_H +#include "main.h" + // Each 4 KiB flash sector contains 3968 bytes of actual data followed by a 128 byte footer. // Only 12 bytes of the footer are used. #define SECTOR_DATA_SIZE 3968 @@ -87,7 +89,7 @@ extern u32 gSaveCounter; extern struct SaveSector *gFastSaveSector; extern u16 gIncrementalSectorId; extern u16 gSaveFileStatus; -extern void (*gGameContinueCallback)(void); +extern MainCallback gGameContinueCallback; extern struct SaveSectorLocation gRamSaveSectorLocations[]; extern struct SaveSector gSaveDataBuffer; diff --git a/src/battle_main.c b/src/battle_main.c index d6cbad4727..ed50332bb8 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -237,7 +237,7 @@ EWRAM_DATA u16 gBattleMovePower = 0; EWRAM_DATA u16 gMoveToLearn = 0; EWRAM_DATA u8 gBattleMonForms[MAX_BATTLERS_COUNT] = {0}; -COMMON_DATA void (*gPreBattleCallback1)(void) = NULL; +COMMON_DATA MainCallback gPreBattleCallback1 = NULL; COMMON_DATA void (*gBattleMainFunc)(void) = NULL; COMMON_DATA struct BattleResults gBattleResults = {0}; COMMON_DATA u8 gLeveledUpInBattle = 0; diff --git a/src/battle_pyramid_bag.c b/src/battle_pyramid_bag.c index e131508928..5354fc46fa 100644 --- a/src/battle_pyramid_bag.c +++ b/src/battle_pyramid_bag.c @@ -16,7 +16,6 @@ #include "item_use.h" #include "list_menu.h" #include "mail.h" -#include "main.h" #include "malloc.h" #include "menu.h" #include "menu_helpers.h" @@ -414,7 +413,7 @@ void CB2_ReturnToPyramidBagMenu(void) GoToBattlePyramidBagMenu(PYRAMIDBAG_LOC_PREV, gPyramidBagMenuState.exitCallback); } -void GoToBattlePyramidBagMenu(u8 location, void (*exitCallback)(void)) +void GoToBattlePyramidBagMenu(u8 location, MainCallback exitCallback) { gPyramidBagMenu = AllocZeroed(sizeof(*gPyramidBagMenu)); diff --git a/src/dodrio_berry_picking.c b/src/dodrio_berry_picking.c index 16ea035c7c..456cead41e 100644 --- a/src/dodrio_berry_picking.c +++ b/src/dodrio_berry_picking.c @@ -10,7 +10,6 @@ #include "link.h" #include "link_rfu.h" #include "m4a.h" -#include "main.h" #include "palette.h" #include "minigame_countdown.h" #include "random.h" @@ -223,7 +222,7 @@ struct DodrioGame_ScoreResults struct DodrioGame { - /*0x0000*/ void (*exitCallback)(void); + /*0x0000*/ MainCallback exitCallback; /*0x0004*/ u8 ALIGNED(4) taskId; /*0x0008*/ u8 ALIGNED(4) playersReceived; /*0x000C*/ u8 ALIGNED(4) startState; @@ -661,7 +660,7 @@ static void (*const sMemberFuncs[])(void) = [FUNC_WAIT_END_GAME] = WaitEndGame_Member }; -void StartDodrioBerryPicking(u16 partyId, void (*exitCallback)(void)) +void StartDodrioBerryPicking(u16 partyId, MainCallback exitCallback) { sExitingGame = FALSE; diff --git a/src/frontier_pass.c b/src/frontier_pass.c index 54d25a11e0..a053badc62 100644 --- a/src/frontier_pass.c +++ b/src/frontier_pass.c @@ -106,7 +106,7 @@ enum { struct FrontierPassData { - void (*callback)(void); + MainCallback callback; u16 state; u16 battlePoints; s16 cursorX; @@ -137,14 +137,14 @@ struct FrontierPassGfx struct FrontierPassSaved { - void (*callback)(void); + MainCallback callback; s16 cursorX; s16 cursorY; }; struct FrontierMapData { - void (*callback)(void); + MainCallback callback; struct Sprite *cursorSprite; struct Sprite *playerHeadSprite; struct Sprite *mapIndicatorSprite; @@ -160,8 +160,8 @@ static EWRAM_DATA struct FrontierPassGfx *sPassGfx = NULL; static EWRAM_DATA struct FrontierMapData *sMapData = NULL; static EWRAM_DATA struct FrontierPassSaved sSavedPassData = {0}; -static u32 AllocateFrontierPassData(void (*callback)(void)); -static void ShowFrontierMap(void (*callback)(void)); +static u32 AllocateFrontierPassData(MainCallback callback); +static void ShowFrontierMap(MainCallback callback); static void CB2_InitFrontierPass(void); static void DrawFrontierPassBg(void); static void FreeCursorAndSymbolSprites(void); @@ -604,7 +604,7 @@ static void LeaveFrontierPass(void) FreeFrontierPassData(); } -static u32 AllocateFrontierPassData(void (*callback)(void)) +static u32 AllocateFrontierPassData(MainCallback callback) { u8 i; @@ -1363,7 +1363,7 @@ static void PrintOnFrontierMap(void); static void InitFrontierMapSprites(void); static void HandleFrontierMapCursorMove(u8 direction); -static void ShowFrontierMap(void (*callback)(void)) +static void ShowFrontierMap(MainCallback callback) { if (sMapData != NULL) SetMainCallback2(callback); // This line doesn't make sense at all, since it gets overwritten later anyway. diff --git a/src/item_menu.c b/src/item_menu.c index e5e3e88f4e..8fe3b692d5 100755 --- a/src/item_menu.c +++ b/src/item_menu.c @@ -24,7 +24,6 @@ #include "list_menu.h" #include "link.h" #include "mail.h" -#include "main.h" #include "malloc.h" #include "map_name_popup.h" #include "menu.h" @@ -578,7 +577,7 @@ void CB2_ChooseBerry(void) } // Choosing berry for Berry Blender or Berry Crush -void ChooseBerryForMachine(void (*exitCallback)(void)) +void ChooseBerryForMachine(MainCallback exitCallback) { GoToBagMenu(ITEMMENULOCATION_BERRY_BLENDER_CRUSH, BERRIES_POCKET, exitCallback); } @@ -612,7 +611,7 @@ void QuizLadyOpenBagMenu(void) gSpecialVar_Result = FALSE; } -void GoToBagMenu(u8 location, u8 pocket, void ( *exitCallback)()) +void GoToBagMenu(u8 location, u8 pocket, MainCallback exitCallback) { gBagMenu = AllocZeroed(sizeof(*gBagMenu)); if (gBagMenu == NULL) diff --git a/src/mail.c b/src/mail.c index e1aaa66873..a2f1c44065 100644 --- a/src/mail.c +++ b/src/mail.c @@ -1,7 +1,6 @@ #include "global.h" #include "mail.h" #include "constants/items.h" -#include "main.h" #include "overworld.h" #include "task.h" #include "scanline_effect.h" @@ -443,7 +442,7 @@ static const struct MailLayout sMailLayouts_Tall[] = { }, }; -void ReadMail(struct Mail *mail, void (*exitCallback)(void), bool8 hasText) +void ReadMail(struct Mail *mail, MainCallback exitCallback, bool8 hasText) { u16 buffer[2]; u16 species; diff --git a/src/pokeblock.c b/src/pokeblock.c index f3d1fa446e..8ecf7f6e1d 100644 --- a/src/pokeblock.c +++ b/src/pokeblock.c @@ -62,7 +62,7 @@ enum { struct PokeblockMenuStruct { u8 tilemap[BG_SCREEN_SIZE]; - void (*callbackOnUse)(void); + MainCallback callbackOnUse; const u8 *pokeblockActionIds; u8 numActions; u8 caseId; @@ -80,7 +80,7 @@ struct PokeblockMenuStruct struct PokeblockSavedData { - void (*callback)(void); + MainCallback callback; u16 selectedRow; u16 scrollOffset; }; diff --git a/src/rayquaza_scene.c b/src/rayquaza_scene.c index 3264b22bdf..d97d463135 100644 --- a/src/rayquaza_scene.c +++ b/src/rayquaza_scene.c @@ -4,7 +4,6 @@ #include "task.h" #include "graphics.h" #include "bg.h" -#include "main.h" #include "malloc.h" #include "palette.h" #include "scanline_effect.h" @@ -1285,7 +1284,7 @@ static const struct BgTemplate sBgTemplates_ChasesAway[] = } }; -void DoRayquazaScene(u8 animId, bool8 endEarly, void (*exitCallback)(void)) +void DoRayquazaScene(u8 animId, bool8 endEarly, MainCallback exitCallback) { sRayScene = AllocZeroed(sizeof(*sRayScene)); sRayScene->animId = animId; diff --git a/src/save.c b/src/save.c index bff1384c52..5c5d7b1b8b 100644 --- a/src/save.c +++ b/src/save.c @@ -8,7 +8,6 @@ #include "load_save.h" #include "overworld.h" #include "pokemon_storage_system.h" -#include "main.h" #include "trainer_hill.h" #include "link.h" #include "constants/game_stat.h" @@ -88,7 +87,7 @@ COMMON_DATA struct SaveSector *gReadWriteSector = NULL; // Pointer to a buffer f COMMON_DATA u16 gIncrementalSectorId = 0; COMMON_DATA u16 gSaveUnusedVar = 0; COMMON_DATA u16 gSaveFileStatus = 0; -COMMON_DATA void (*gGameContinueCallback)(void) = NULL; +COMMON_DATA MainCallback gGameContinueCallback = NULL; COMMON_DATA struct SaveSectorLocation gRamSaveSectorLocations[NUM_SECTORS_PER_SLOT] = {0}; COMMON_DATA u16 gSaveUnusedVar2 = 0; COMMON_DATA u16 gSaveAttemptStatus = 0; @@ -887,7 +886,7 @@ u8 LoadGameSave(u8 saveType) status = TryLoadSaveSlot(FULL_SAVE_SLOT, gRamSaveSectorLocations); CopyPartyAndObjectsFromSave(); gSaveFileStatus = status; - gGameContinueCallback = 0; + gGameContinueCallback = NULL; break; case SAVE_HALL_OF_FAME: status = TryLoadSaveSector(SECTOR_ID_HOF_1, gDecompressionBuffer, SECTOR_DATA_SIZE); diff --git a/src/save_failed_screen.c b/src/save_failed_screen.c index f7f0162ff4..8b0a766d70 100644 --- a/src/save_failed_screen.c +++ b/src/save_failed_screen.c @@ -326,7 +326,7 @@ static void CB2_ReturnToTitleScreen(void) } else { - SetMainCallback2((MainCallback)gGameContinueCallback); + SetMainCallback2(gGameContinueCallback); gGameContinueCallback = NULL; } } diff --git a/src/shop.c b/src/shop.c index 338e067938..c6e78817eb 100644 --- a/src/shop.c +++ b/src/shop.c @@ -455,7 +455,7 @@ static void Task_GoToBuyOrSellMenu(u8 taskId) if (!gPaletteFade.active) { DestroyTask(taskId); - SetMainCallback2((void *)((u16)tCallbackHi << 16 | (u16)tCallbackLo)); + SetMainCallback2((MainCallback)((u16)tCallbackHi << 16 | (u16)tCallbackLo)); } } diff --git a/src/trainer_card.c b/src/trainer_card.c index 1535c3d997..78a4bb56fe 100755 --- a/src/trainer_card.c +++ b/src/trainer_card.c @@ -77,7 +77,7 @@ struct TrainerCardData u8 cardType; bool8 isHoenn; u16 blendColor; - void (*callback2)(void); + MainCallback callback2; struct TrainerCard trainerCard; u16 frontTilemap[600]; u16 backTilemap[600]; diff --git a/src/use_pokeblock.c b/src/use_pokeblock.c index c9d5c56e5c..769e5a3465 100644 --- a/src/use_pokeblock.c +++ b/src/use_pokeblock.c @@ -50,7 +50,7 @@ enum { struct UsePokeblockSession { void (*callback)(void); - void (*exitCallback)(void); + MainCallback exitCallback; struct Pokeblock *pokeblock; struct Pokemon *mon; u8 stringBuffer[64]; @@ -162,7 +162,7 @@ extern const u16 gConditionText_Pal[]; // The below 3 are saved for returning to the screen after feeding a pokeblock to a mon // so that the rest of the data can be freed static EWRAM_DATA struct UsePokeblockSession *sInfo = NULL; -static EWRAM_DATA void (*sExitCallback)(void) = NULL; +static EWRAM_DATA MainCallback sExitCallback = NULL; static EWRAM_DATA struct Pokeblock *sPokeblock = NULL; EWRAM_DATA u8 gPokeblockMonId = 0; EWRAM_DATA s16 gPokeblockGain = 0; From f2cf20ce9bdf4a9f6cd0300a005ca3ec6ae89e3c Mon Sep 17 00:00:00 2001 From: Jan Helbling Date: Tue, 14 Oct 2025 13:30:26 +0200 Subject: [PATCH 010/130] INSTALL.MD updated for OpenBSD (#2186) Co-authored-by: Jan Helbling --- INSTALL.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/INSTALL.md b/INSTALL.md index 03b28ba464..1cca7f8ae6 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -357,6 +357,26 @@ nix-shell -p pkgsCross.arm-embedded.stdenv.cc git pkg-config libpng ``` Then proceed to [Choosing where to store pokeemerald (Linux)](#choosing-where-to-store-pokeemerald-linux). +### OpenBSD +Install requirements: +```bash +pkg_add gmake bash arm-none-eabi-binutils clang git +``` + +Clone pokeemerald & agbcc, and Build agbcc +```bash +git clone https://github.com/pret/pokeemerald +git clone https://github.com/pret/agbcc +cd agbcc && ./build.sh +./install.sh ../pokeemerald +``` + +Build the ROM: +```bash +cd ../pokeemerald +gmake +``` + ### Other distributions _(Specific instructions for other distributions would be greatly appreciated!)_ From 994dcc62a00464b89f4bbf5faab197400a0b6fc8 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Tue, 14 Oct 2025 14:42:29 +0200 Subject: [PATCH 011/130] Show convergent evolution to Gholdengo in HGSS dex (#7934) --- src/pokedex_plus_hgss.c | 43 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index 6e6d5abe22..dbce6b98b7 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -6313,6 +6313,17 @@ static void HandlePreEvolutionSpeciesPrint(u8 taskId, u16 preSpecies, u16 specie } } +static bool32 HasTwoPreEvolutions(u32 species) +{ + switch (species) + { + case SPECIES_GHOLDENGO: + return TRUE; + default: + return FALSE; + } +} + static u8 PrintPreEvolutions(u8 taskId, u16 species) { u16 i; @@ -6364,13 +6375,39 @@ static u8 PrintPreEvolutions(u8 taskId, u16 species) { if (evolutions[j].targetSpecies == species) { - preEvolutionOne = i; - numPreEvolutions += 1; - break; + if (numPreEvolutions == 0) + { + preEvolutionOne = i; + numPreEvolutions += 1; + if (!HasTwoPreEvolutions(species)) + break; + } + else + { + preEvolutionTwo = i; + numPreEvolutions += 1; + break; + } } } } + if (HasTwoPreEvolutions(species)) + { + CreateCaughtBallEvolutionScreen(preEvolutionOne, base_x - 9, base_y + base_y_offset*0, 0); + HandlePreEvolutionSpeciesPrint(taskId, preEvolutionOne, species, base_x, base_y, base_y_offset, 0); + + CreateCaughtBallEvolutionScreen(preEvolutionTwo, base_x - 9, base_y + base_y_offset*(numPreEvolutions - 1), 0); + HandlePreEvolutionSpeciesPrint(taskId, preEvolutionTwo, species, base_x, base_y, base_y_offset, numPreEvolutions - 1); + + sPokedexView->sEvoScreenData.targetSpecies[0] = preEvolutionOne; + sPokedexView->sEvoScreenData.targetSpecies[1] = preEvolutionTwo; + + sPokedexView->numPreEvolutions = numPreEvolutions; + sPokedexView->sEvoScreenData.numAllEvolutions += numPreEvolutions; + return numPreEvolutions; + } + //Calculate if previous evolution also has a previous evolution if (numPreEvolutions != 0) { From 3939c19bb70872011fa5027a3bcf8a675edb990c Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Tue, 14 Oct 2025 18:15:08 +0200 Subject: [PATCH 012/130] Add missing end signal for AnimTask_SetAttackerInvisibleWaitForSignal (#7950) Co-authored-by: Hedara --- data/battle_anim_scripts.s | 1 + 1 file changed, 1 insertion(+) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 4d952965e6..61d0d99933 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -3356,6 +3356,7 @@ gBattleAnimMove_AquaJet:: call RisingWaterHitEffect waitforvisualfinish createvisualtask AnimTask_ExtremeSpeedMonReappear, 2 + setarg 0x7, 0x1000 waitforvisualfinish visible ANIM_ATTACKER clearmonbg ANIM_DEF_PARTNER From 51760f1c766483e99859be7f9a53fced4a1380f5 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Tue, 14 Oct 2025 18:16:42 +0200 Subject: [PATCH 013/130] Fix incorrect font width in Dexnav search window hiding some elements (#7949) --- src/dexnav.c | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/dexnav.c b/src/dexnav.c index e1fc4a497f..fe4b267a70 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -472,8 +472,8 @@ static void AddSearchWindow(u8 width) } #define WINDOW_COL_0 (SPECIES_ICON_X + 4) -#define WINDOW_COL_1 (WINDOW_COL_0 + (GetFontAttribute(sDexNavSearchDataPtr->windowId, FONTATTR_MAX_LETTER_WIDTH) * (POKEMON_NAME_LENGTH))) -#define WINDOW_MOVE_NAME_X (WINDOW_COL_1 + (GetFontAttribute(sDexNavSearchDataPtr->windowId, FONTATTR_MAX_LETTER_WIDTH) * 6)) +#define WINDOW_COL_1 (WINDOW_COL_0 + (GetFontAttribute(FONT_SMALL, FONTATTR_MAX_LETTER_WIDTH) * (POKEMON_NAME_LENGTH))) +#define WINDOW_MOVE_NAME_X (WINDOW_COL_1 + (GetFontAttribute(FONT_SMALL, FONTATTR_MAX_LETTER_WIDTH) * 6)) #define SEARCH_ARROW_X (WINDOW_MOVE_NAME_X + 90) #define SEARCH_ARROW_Y 0 @@ -485,19 +485,19 @@ static void AddSearchWindowText(u16 species, u8 proximity, u8 searchLevel, bool8 if (hidden) { StringCopy(gStringVar4, sText_ThreeQmarks); - AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); return; } else { StringCopy(gStringVar1, GetSpeciesName(species)); - AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, WINDOW_COL_0, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1); } //level - always present ConvertIntToDecimalStringN(gStringVar1, sDexNavSearchDataPtr->monLevel, STR_CONV_MODE_LEFT_ALIGN, 3); StringExpandPlaceholders(gStringVar4, sText_MonLevel); - AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, WINDOW_COL_1, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, WINDOW_COL_1, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); if (proximity <= SNEAKING_PROXIMITY) { @@ -507,21 +507,21 @@ static void AddSearchWindowText(u16 species, u8 proximity, u8 searchLevel, bool8 { StringCopy(gStringVar1, GetMoveName(sDexNavSearchDataPtr->moves[0])); StringExpandPlaceholders(gStringVar4, sText_EggMove); - AddTextPrinterParameterized3(windowId, 0, WINDOW_MOVE_NAME_X, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(windowId, FONT_SMALL, WINDOW_MOVE_NAME_X, 0, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); } if (searchLevel > 2) { // ability name StringCopy(gStringVar1, gAbilitiesInfo[GetAbilityBySpecies(species, sDexNavSearchDataPtr->abilityNum)].name); - AddTextPrinterParameterized3(windowId, 0, WINDOW_COL_1 + 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1); + AddTextPrinterParameterized3(windowId, FONT_SMALL, WINDOW_COL_1 + 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar1); // item name if (sDexNavSearchDataPtr->heldItem) { CopyItemName(sDexNavSearchDataPtr->heldItem, gStringVar1); StringExpandPlaceholders(gStringVar4, sText_HeldItem); - AddTextPrinterParameterized3(windowId, 0, WINDOW_COL_0, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(windowId, FONT_SMALL, WINDOW_COL_0, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); } } } @@ -532,7 +532,7 @@ static void AddSearchWindowText(u16 species, u8 proximity, u8 searchLevel, bool8 StringExpandPlaceholders(gStringVar4, sText_DexNavChainLong); else StringExpandPlaceholders(gStringVar4, sText_DexNavChain); - AddTextPrinterParameterized3(windowId, 0, SEARCH_ARROW_X - 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(windowId, FONT_SMALL, SEARCH_ARROW_X - 16, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); CopyWindowToVram(sDexNavSearchDataPtr->windowId, 2); } @@ -959,7 +959,7 @@ static void DexNavUpdateDirectionArrow(void) str = sText_ArrowDown; //player above } - AddTextPrinterParameterized3(windowId, 1, SEARCH_ARROW_X, SEARCH_ARROW_Y, sSearchFontColor, TEXT_SKIP_DRAW, str); + AddTextPrinterParameterized3(windowId, FONT_NORMAL, SEARCH_ARROW_X, SEARCH_ARROW_Y, sSearchFontColor, TEXT_SKIP_DRAW, str); CopyWindowToVram(windowId, 2); } @@ -2133,9 +2133,9 @@ static void PrintCurrentSpeciesInfo(void) //species name if (species == SPECIES_NONE) - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SPECIES_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, SPECIES_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); else - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SPECIES_INFO_Y, sFontColor_Black, 0, GetSpeciesName(species)); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, SPECIES_INFO_Y, sFontColor_Black, 0, GetSpeciesName(species)); //type icon(s) type1 = GetSpeciesType(species, 0); @@ -2157,34 +2157,34 @@ static void PrintCurrentSpeciesInfo(void) //search level if (species == SPECIES_NONE) { - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); } else { ConvertIntToDecimalStringN(gStringVar4, GetSearchLevel(species), 0, 4); - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, gStringVar4); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, gStringVar4); } //hidden ability if (species == SPECIES_NONE) { - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_NoInfo); } else if (GetSetPokedexFlag(dexNum, FLAG_GET_CAUGHT)) { if (GetSpeciesAbility(species, 2) != ABILITY_NONE) - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, gAbilitiesInfo[GetSpeciesAbility(species, 2)].name); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, HA_INFO_Y, sFontColor_Black, 0, gAbilitiesInfo[GetSpeciesAbility(species, 2)].name); else - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, gText_None); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, HA_INFO_Y, sFontColor_Black, 0, gText_None); } else { - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_CaptureToSee); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, HA_INFO_Y, sFontColor_Black, 0, sText_DexNav_CaptureToSee); } //current chain ConvertIntToDecimalStringN(gStringVar1, gSaveBlock3Ptr->dexNavChain, STR_CONV_MODE_LEFT_ALIGN, 3); - AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, CHAIN_BONUS_Y, sFontColor_Black, 0, gStringVar1); + AddTextPrinterParameterized3(WINDOW_INFO, FONT_SMALL, 0, CHAIN_BONUS_Y, sFontColor_Black, 0, gStringVar1); CopyWindowToVram(WINDOW_INFO, 3); PutWindowTilemap(WINDOW_INFO); @@ -2193,7 +2193,7 @@ static void PrintCurrentSpeciesInfo(void) static void PrintMapName(void) { GetMapName(gStringVar3, GetCurrentRegionMapSectionId(), 0); - AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 108 + + AddTextPrinterParameterized3(WINDOW_REGISTERED, FONT_NORMAL, 108 + GetStringRightAlignXOffset(1, gStringVar3, MAP_NAME_LENGTH * GetFontAttribute(1, FONTATTR_MAX_LETTER_WIDTH)), 0, sFontColor_White, 0, gStringVar3); CopyWindowToVram(WINDOW_REGISTERED, 3); @@ -2205,13 +2205,13 @@ static void PrintSearchableSpecies(u16 species) PutWindowTilemap(WINDOW_REGISTERED); if (species == SPECIES_NONE) { - AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, sText_DexNav_PressRToRegister); + AddTextPrinterParameterized3(WINDOW_REGISTERED, FONT_NORMAL, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, sText_DexNav_PressRToRegister); } else { StringCopy(gStringVar1, GetSpeciesName(species)); StringExpandPlaceholders(gStringVar4, sText_DexNav_SearchForRegisteredSpecies); - AddTextPrinterParameterized3(WINDOW_REGISTERED, 1, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(WINDOW_REGISTERED, FONT_NORMAL, 0, 0, sFontColor_White, TEXT_SKIP_DRAW, gStringVar4); } PrintMapName(); @@ -2651,11 +2651,11 @@ static void DrawSearchIcon(void) static void DrawHiddenSearchWindow(u8 width) { AddSearchWindow(width); - AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, SPECIES_ICON_X + 4, 0, sSearchFontColor, TEXT_SKIP_DRAW, sText_ThreeQmarks); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, SPECIES_ICON_X + 4, 0, sSearchFontColor, TEXT_SKIP_DRAW, sText_ThreeQmarks); ConvertIntToDecimalStringN(gStringVar1, sDexNavSearchDataPtr->searchLevel, STR_CONV_MODE_LEFT_ALIGN, 2); StringExpandPlaceholders(gStringVar4, sText_SearchLevel); - AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, 0, SPECIES_ICON_X + 4, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); + AddTextPrinterParameterized3(sDexNavSearchDataPtr->windowId, FONT_SMALL, SPECIES_ICON_X + 4, 12, sSearchFontColor, TEXT_SKIP_DRAW, gStringVar4); CopyWindowToVram(sDexNavSearchDataPtr->windowId, 2); } From 7cf768bb7bf53c83d152cbbfb1bb306cc33e6b6d Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Tue, 14 Oct 2025 18:17:19 +0200 Subject: [PATCH 014/130] Fix HGSS dex sprites for gen9+ (#7922) --- src/pokedex_plus_hgss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index dbce6b98b7..d02eb0eda1 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -4540,7 +4540,7 @@ static u32 GetPokedexMonPersonality(u16 species) static u16 CreateMonSpriteFromNationalDexNumberHGSS(u16 nationalNum, s16 x, s16 y, u16 paletteSlot) { u32 species = NationalPokedexNumToSpeciesHGSS(nationalNum); - return CreateMonPicSprite(nationalNum, FALSE, GetPokedexMonPersonality(species), TRUE, x, y, paletteSlot, TAG_NONE); + return CreateMonPicSprite(species, FALSE, GetPokedexMonPersonality(species), TRUE, x, y, paletteSlot, TAG_NONE); } static u16 GetPokemonScaleFromNationalDexNumber(u16 nationalNum) From d3ba9e4020a16c3c0224d988efa801450f6c1e4f Mon Sep 17 00:00:00 2001 From: Raymond Dodge Date: Tue, 14 Oct 2025 13:08:25 -0400 Subject: [PATCH 015/130] Fix image links in doc site (#7948) --- docs/fix_links.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/fix_links.py b/docs/fix_links.py index 6e2eaec485..7b6b01b995 100644 --- a/docs/fix_links.py +++ b/docs/fix_links.py @@ -36,6 +36,7 @@ def proc_items(items): s = s.replace('](README.md)', '](./)') s = s.replace('](/INSTALL.md', '](INSTALL.md') s = s.replace('](docs/', '](') + s = s.replace('](/docs/', '](/') s = URL_RE.sub(handle_url, s) item['Chapter']['content'] = ANCHOR_RE.sub(handle_anchor, s) proc_items(item['Chapter']['sub_items']) From df5312c0bd7070fe31a2db61f829bb82763210da Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Tue, 14 Oct 2025 15:34:06 -0400 Subject: [PATCH 016/130] Fix Knock Off not being restored and Wild Battles (#7952) Co-authored-by: ghoulslash --- src/battle_script_commands.c | 13 ++++++++----- test/battle/move_effect/knock_off.c | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index b198d4b589..5280797a41 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5694,26 +5694,29 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) return FALSE; u32 effect = FALSE; + u32 side = GetBattlerSide(gBattlerTarget); switch (moveEffect) { case EFFECT_KNOCK_OFF: if (gBattleStruct->battlerState[gBattlerTarget].itemCanBeKnockedOff && gBattleMons[gBattlerTarget].item != ITEM_NONE && IsBattlerTurnDamaged(gBattlerTarget) - && IsBattlerAlive(gBattlerAttacker)) + && IsBattlerAlive(gBattlerAttacker) + && !(B_KNOCK_OFF_REMOVAL >= GEN_5 && side == B_SIDE_PLAYER && !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))) { - u32 side = GetBattlerSide(gBattlerTarget); gLastUsedItem = gBattleMons[gBattlerTarget].item; gBattleMons[gBattlerTarget].item = 0; if (gBattleMons[gBattlerTarget].ability != ABILITY_GORILLA_TACTICS) gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE; CheckSetUnburden(gBattlerTarget); - // In Gen 5+, Knock Off removes the target's item rather than rendering it unusable. + // In Gen 5+, Knock Off removes the target's item rather than rendering it unusable if (B_KNOCK_OFF_REMOVAL >= GEN_5) { BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item); MarkBattlerForControllerExec(gBattlerTarget); + // Mark item as stolen so it will be restored after battle + gBattleStruct->itemLost[side][gBattlerPartyIndexes[gBattlerTarget]].stolen = TRUE; } else { @@ -5875,7 +5878,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) } break; case EFFECT_STONE_AXE: - if (!IsHazardOnSide(GetBattlerSide(gBattlerTarget), HAZARDS_STEALTH_ROCK) + if (!IsHazardOnSide(side, HAZARDS_STEALTH_ROCK) && IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) { @@ -5886,7 +5889,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) } break; case EFFECT_CEASELESS_EDGE: - if (gSideTimers[GetBattlerSide(gBattlerTarget)].spikesAmount < 3 + if (gSideTimers[side].spikesAmount < 3 && IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) { diff --git a/test/battle/move_effect/knock_off.c b/test/battle/move_effect/knock_off.c index 3fdeadcecc..eb0874d7bf 100644 --- a/test/battle/move_effect/knock_off.c +++ b/test/battle/move_effect/knock_off.c @@ -6,6 +6,33 @@ ASSUMPTIONS ASSUME(GetMoveEffect(MOVE_KNOCK_OFF) == EFFECT_KNOCK_OFF); } +WILD_BATTLE_TEST("Knock Off does not remove item when used by Wild Pokemon (Gen 5+)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LEFTOVERS); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_EVIOLITE); } + } WHEN { + TURN { MOVE(opponent, MOVE_KNOCK_OFF); } + TURN { MOVE(player, MOVE_KNOCK_OFF); } + } SCENE { + // Turn 1 + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, opponent); + if (B_KNOCK_OFF_REMOVAL >= GEN_5) + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player); + else + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, player); + // Turn 2 + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF, opponent); + } THEN { + EXPECT(player->item == ITEM_LEFTOVERS); + if (B_KNOCK_OFF_REMOVAL >= GEN_5) + EXPECT(opponent->item == ITEM_NONE); + else + EXPECT(opponent->item == ITEM_EVIOLITE); + } +} + SINGLE_BATTLE_TEST("Knock Off knocks a healing berry before it has the chance to activate") { GIVEN { From 7e98dffc51a2df8b4b1347644176a2dbf93cf9c3 Mon Sep 17 00:00:00 2001 From: grintoul <166724814+grintoul1@users.noreply.github.com> Date: Wed, 15 Oct 2025 13:31:09 +0100 Subject: [PATCH 017/130] Tests for Battery ability (#7846) --- test/battle/ability/battery.c | 50 +++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/test/battle/ability/battery.c b/test/battle/ability/battery.c index cecfa81d0c..7ddc0b837c 100644 --- a/test/battle/ability/battery.c +++ b/test/battle/ability/battery.c @@ -1,5 +1,51 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Battery increases Sp. Attack damage of allies by ~30%"); // 5325/4096 -TO_DO_BATTLE_TEST("Battery does not increase its own Sp. Attack damage"); +DOUBLE_BATTLE_TEST("Battery increases Sp. Attack damage of allies by ~30%") +{ + s16 damage[2]; + + GIVEN { + PLAYER(SPECIES_CHARJABUG) { Speed(1); Ability(ABILITY_BATTERY); } + PLAYER(SPECIES_WOBBUFFET) { Speed(2); Moves(MOVE_SHOCK_WAVE); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(4); Moves(MOVE_CELEBRATE, MOVE_GASTRO_ACID); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(3); } + } WHEN { + TURN { MOVE(playerRight, MOVE_SHOCK_WAVE, target: opponentLeft); } + TURN { MOVE(opponentLeft, MOVE_GASTRO_ACID, target: playerLeft); MOVE(playerRight, MOVE_SHOCK_WAVE, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, playerRight); + HP_BAR(opponentLeft, captureDamage: &damage[0]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_GASTRO_ACID, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, playerRight); + HP_BAR(opponentLeft, captureDamage: &damage[1]); + } THEN { + EXPECT_MUL_EQ(damage[1], UQ_4_12(1.3), damage[0]); + } + +} + +DOUBLE_BATTLE_TEST("Battery does not increase its own Sp. Attack damage") +{ + s16 damage[2]; + + GIVEN { + PLAYER(SPECIES_CHARJABUG) { Speed(1); Ability(ABILITY_BATTERY); } + PLAYER(SPECIES_WOBBUFFET) { Speed(2); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(4); Moves(MOVE_CELEBRATE, MOVE_GASTRO_ACID); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(3); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_SHOCK_WAVE, target: opponentLeft); } + TURN { MOVE(opponentLeft, MOVE_GASTRO_ACID, target: playerLeft); MOVE(playerLeft, MOVE_SHOCK_WAVE, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[0]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_GASTRO_ACID, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[1]); + } THEN { + EXPECT_EQ(damage[0], damage[1]); + } +} From 593219226b7a739b3290a26d73140e7798b8c05d Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Thu, 16 Oct 2025 12:06:54 +0200 Subject: [PATCH 018/130] Fix dns color transition not applying weather blending (#7883) Co-authored-by: Bassoonian --- src/overworld.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/overworld.c b/src/overworld.c index 8c2eaf5d88..e8be437519 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1716,8 +1716,7 @@ static void OverworldBasic(void) || bld0[1] != bld1[1] || bld0[2] != bld1[2]) { - UpdateAltBgPalettes(PALETTES_BG); - UpdatePalettesWithTime(PALETTES_ALL); + ApplyWeatherColorMapIfIdle(gWeatherPtr->colorMapIndex); } } } From f8eed27ed02a9cb7fbf918a46264890403b374aa Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Thu, 16 Oct 2025 07:09:28 -0400 Subject: [PATCH 019/130] Add missing break to Power Split AI case (#7959) Co-authored-by: ghoulslash --- src/battle_ai_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 1960088640..2cc7cfe62c 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -5116,6 +5116,7 @@ case EFFECT_GUARD_SPLIT: ADJUST_AND_RETURN_SCORE(GOOD_EFFECT); ADJUST_SCORE(WORST_EFFECT); + break; } case EFFECT_ELECTRIC_TERRAIN: if (ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_ELECTRIC_TERRAIN)) From f6c6ed6956c74fcdf22931ed1f20b16731315d68 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Thu, 16 Oct 2025 13:37:36 +0200 Subject: [PATCH 020/130] Fix follower NPC sidewaystair movement (#7909) --- src/event_object_movement.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 84f810e95a..6d0cf86cf6 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -6591,7 +6591,7 @@ bool8 ObjectEventIsHeldMovementActive(struct ObjectEvent *objectEvent) static u8 TryUpdateMovementActionOnStairs(struct ObjectEvent *objectEvent, u8 movementActionId) { - if (objectEvent->isPlayer || objectEvent->localId == OBJ_EVENT_ID_FOLLOWER) + if (objectEvent->isPlayer || objectEvent->localId == OBJ_EVENT_ID_FOLLOWER || objectEvent->localId == OBJ_EVENT_ID_NPC_FOLLOWER) return movementActionId; // handled separately if (!ObjectMovingOnRockStairs(objectEvent, objectEvent->movementDirection)) From de3c031fc5d0c01913d52ddfaed540b6a36e31fb Mon Sep 17 00:00:00 2001 From: spindrift64 <102487911+spindrift64@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:36:07 +0200 Subject: [PATCH 021/130] Fix Anticipation type effectiveness check (#7840) --- include/battle_util.h | 3 ++- src/battle_util.c | 31 +++++++++++++++++------------- test/battle/ability/anticipation.c | 29 ++++++++++++++++------------ 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/include/battle_util.h b/include/battle_util.h index f22fd8b0ac..d30caadd47 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -165,7 +165,8 @@ struct DamageContext u32 isCrit:1; u32 randomFactor:1; u32 updateFlags:1; - u32 padding1:2; + u32 isAnticipation:1; + u32 padding1:1; u32 weather:16; u32 fixedBasePower:8; u32 padding2:8; diff --git a/src/battle_util.c b/src/battle_util.c index 58349507a7..abf61c1d58 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3690,8 +3690,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_ANTICIPATION: if (!gSpecialStatuses[battler].switchInAbilityDone) { - u32 types[3]; - GetBattlerTypes(battler, FALSE, types); + struct DamageContext ctx = {0}; + uq4_12_t modifier = UQ_4_12(1.0); for (i = 0; i < MAX_BATTLERS_COUNT; i++) { if (IsBattlerAlive(i) && !IsBattlerAlly(i, battler)) @@ -3702,9 +3702,14 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 enum BattleMoveEffects moveEffect = GetMoveEffect(move); moveType = GetBattleMoveType(move); - if (GetTypeModifier(moveType, types[0]) >= UQ_4_12(2.0) - || (types[0] != types[1] && GetTypeModifier(moveType, types[1]) >= UQ_4_12(2.0)) - || (types[2] != TYPE_MYSTERY && GetTypeModifier(moveType, types[2]) >= UQ_4_12(2.0)) + ctx.battlerAtk = i; + ctx.battlerDef = battler; + ctx.move = move; + ctx.moveType = moveType; + ctx.isAnticipation = TRUE; + modifier = CalcTypeEffectivenessMultiplier(&ctx); + + if (modifier >= UQ_4_12(2.0) || moveEffect == EFFECT_OHKO || moveEffect == EFFECT_SHEER_COLD) { @@ -7639,13 +7644,13 @@ enum IronBallCheck }; // Only called directly when calculating damage type effectiveness, and Iron Ball's type effectiveness mechanics -static bool32 IsBattlerGroundedInverseCheck(u32 battler, u32 ability, enum InverseBattleCheck checkInverse, enum IronBallCheck checkIronBall) +static bool32 IsBattlerGroundedInverseCheck(u32 battler, u32 ability, enum InverseBattleCheck checkInverse, enum IronBallCheck checkIronBall, bool32 isAnticipation) { enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(battler, TRUE); if (!(checkIronBall == IGNORE_IRON_BALL) && holdEffect == HOLD_EFFECT_IRON_BALL) return TRUE; - if (gFieldStatuses & STATUS_FIELD_GRAVITY) + if (gFieldStatuses & STATUS_FIELD_GRAVITY && isAnticipation == FALSE) return TRUE; if (B_ROOTED_GROUNDING >= GEN_4 && gBattleMons[battler].volatiles.root) return TRUE; @@ -7666,7 +7671,7 @@ static bool32 IsBattlerGroundedInverseCheck(u32 battler, u32 ability, enum Inver bool32 IsBattlerGrounded(u32 battler) { - return IsBattlerGroundedInverseCheck(battler, GetBattlerAbility(battler), NOT_INVERSE_BATTLE, CHECK_IRON_BALL); + return IsBattlerGroundedInverseCheck(battler, GetBattlerAbility(battler), NOT_INVERSE_BATTLE, CHECK_IRON_BALL, FALSE); } u32 GetMoveSlot(u16 *moves, u32 move) @@ -9522,7 +9527,7 @@ static inline void MulByTypeEffectiveness(struct DamageContext *ctx, uq4_12_t *m if (ctx->moveType == TYPE_PSYCHIC && defType == TYPE_DARK && gBattleMons[ctx->battlerDef].volatiles.miracleEye && mod == UQ_4_12(0.0)) mod = UQ_4_12(1.0); - if (GetMoveEffect(ctx->move) == EFFECT_SUPER_EFFECTIVE_ON_ARG && defType == GetMoveArgType(ctx->move)) + if (GetMoveEffect(ctx->move) == EFFECT_SUPER_EFFECTIVE_ON_ARG && defType == GetMoveArgType(ctx->move) && !ctx->isAnticipation) mod = UQ_4_12(2.0); if (ctx->moveType == TYPE_GROUND && defType == TYPE_FLYING && IsBattlerGrounded(ctx->battlerDef) && mod == UQ_4_12(0.0)) mod = UQ_4_12(1.0); @@ -9530,7 +9535,7 @@ static inline void MulByTypeEffectiveness(struct DamageContext *ctx, uq4_12_t *m mod = UQ_4_12(2.0); // B_WEATHER_STRONG_WINDS weakens Super Effective moves against Flying-type Pokémon - if (gBattleWeather & B_WEATHER_STRONG_WINDS && HasWeatherEffect()) + if (gBattleWeather & B_WEATHER_STRONG_WINDS && HasWeatherEffect() && !ctx->isAnticipation) { if (defType == TYPE_FLYING && mod >= UQ_4_12(2.0)) mod = UQ_4_12(1.0); @@ -9621,7 +9626,7 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(struct DamageCont if (B_GLARE_GHOST < GEN_4 && ctx->move == MOVE_GLARE && IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_GHOST)) modifier = UQ_4_12(0.0); } - else if (ctx->moveType == TYPE_GROUND && !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, INVERSE_BATTLE, CHECK_IRON_BALL) && !(MoveIgnoresTypeIfFlyingAndUngrounded(ctx->move))) + else if (ctx->moveType == TYPE_GROUND && !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, INVERSE_BATTLE, CHECK_IRON_BALL, ctx->isAnticipation) && !(MoveIgnoresTypeIfFlyingAndUngrounded(ctx->move))) { modifier = UQ_4_12(0.0); if (ctx->updateFlags && ctx->abilityDef == ABILITY_LEVITATE) @@ -9651,7 +9656,7 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(struct DamageCont && ctx->moveType == TYPE_GROUND && IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_FLYING) && GetBattlerHoldEffect(ctx->battlerDef, TRUE) == HOLD_EFFECT_IRON_BALL - && !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, NOT_INVERSE_BATTLE, IGNORE_IRON_BALL) + && !IsBattlerGroundedInverseCheck(ctx->battlerDef, ctx->abilityDef, NOT_INVERSE_BATTLE, IGNORE_IRON_BALL, FALSE) && !FlagGet(B_FLAG_INVERSE_BATTLE)) { modifier = UQ_4_12(1.0); @@ -9685,7 +9690,7 @@ uq4_12_t CalcTypeEffectivenessMultiplier(struct DamageContext *ctx) if (ctx->move != MOVE_STRUGGLE && ctx->moveType != TYPE_MYSTERY) { modifier = CalcTypeEffectivenessMultiplierInternal(ctx, modifier); - if (GetMoveEffect(ctx->move) == EFFECT_TWO_TYPED_MOVE) + if (GetMoveEffect(ctx->move) == EFFECT_TWO_TYPED_MOVE && !ctx->isAnticipation) { ctx->moveType = GetMoveArgType(ctx->move); modifier = CalcTypeEffectivenessMultiplierInternal(ctx, modifier); diff --git a/test/battle/ability/anticipation.c b/test/battle/ability/anticipation.c index 223512710f..a4a3bef77f 100644 --- a/test/battle/ability/anticipation.c +++ b/test/battle/ability/anticipation.c @@ -16,6 +16,18 @@ SINGLE_BATTLE_TEST("Anticipation causes notifies if an opponent has a super-effe } } +SINGLE_BATTLE_TEST("Anticipation does not trigger even when a move is super effective on only 1 type") +{ + GIVEN { + PLAYER(SPECIES_WHISCASH) { Ability(ABILITY_ANTICIPATION); } + OPPONENT(SPECIES_PIKACHU) { Moves(MOVE_CELEBRATE, MOVE_THUNDERBOLT); } + } WHEN { + TURN { } + } SCENE { + NOT ABILITY_POPUP(player, ABILITY_ANTICIPATION); + } +} + SINGLE_BATTLE_TEST("Anticipation causes notifies if an opponent has a One-hit KO move") { GIVEN { @@ -59,28 +71,21 @@ SINGLE_BATTLE_TEST("Anticipation doesn't consider Normalize into their effective SINGLE_BATTLE_TEST("Anticipation doesn't consider Scrappy into their effectiveness (Gen5+)") { - KNOWN_FAILING; GIVEN { ASSUME(GetMoveType(MOVE_CLOSE_COMBAT) == TYPE_FIGHTING); - ASSUME(GetSpeciesType(SPECIES_EEVEE, 0) == TYPE_NORMAL); - ASSUME(GetSpeciesType(SPECIES_EEVEE, 1) == TYPE_NORMAL); - PLAYER(SPECIES_EEVEE) { Ability(ABILITY_ANTICIPATION); } - OPPONENT(SPECIES_KANGASKHAN) { Ability(ABILITY_SCRAPPY); Moves(MOVE_CLOSE_COMBAT, MOVE_TRICK_OR_TREAT, MOVE_SKILL_SWAP, MOVE_CELEBRATE); } + ASSUME(GetSpeciesType(SPECIES_DOUBLADE, 0) == TYPE_STEEL); + ASSUME(GetSpeciesType(SPECIES_DOUBLADE, 1) == TYPE_GHOST); + PLAYER(SPECIES_DOUBLADE) { Ability(ABILITY_ANTICIPATION); } + OPPONENT(SPECIES_KANGASKHAN) { Ability(ABILITY_SCRAPPY); Moves(MOVE_CLOSE_COMBAT, MOVE_CELEBRATE); } } WHEN { - TURN { MOVE(opponent, MOVE_TRICK_OR_TREAT); MOVE(player, MOVE_SKILL_SWAP); } - TURN { MOVE(opponent, MOVE_SKILL_SWAP); } + TURN { } } SCENE { - ABILITY_POPUP(player, ABILITY_ANTICIPATION); - ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK_OR_TREAT, opponent); - ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, player); - ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponent); NOT ABILITY_POPUP(player, ABILITY_ANTICIPATION); } } SINGLE_BATTLE_TEST("Anticipation doesn't consider Gravity into their effectiveness (Gen5+)") { - KNOWN_FAILING; GIVEN { PLAYER(SPECIES_SKARMORY); OPPONENT(SPECIES_EEVEE) { Ability(ABILITY_ANTICIPATION); Moves(MOVE_EARTHQUAKE, MOVE_GRAVITY, MOVE_SCRATCH, MOVE_POUND); } From 864377f99a0f3ffb63b53d10e257ec2e820f0ba4 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Thu, 16 Oct 2025 19:19:31 +0200 Subject: [PATCH 022/130] Fix Cherim and Castfrom not reverting to baseform when Teraform Zero is triggered (#7961) --- data/battle_scripts_1.s | 18 ++++++++++++++++-- test/battle/ability/flower_gift.c | 19 +++++++++++++++++++ test/battle/ability/forecast.c | 19 +++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 004594753f..3cf1f53fec 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8883,14 +8883,28 @@ BattleScript_ActivateTeraformZero_RemoveWeather: removeweather printfromtable gWeatherEndsStringIds waitmessage B_WAIT_TIME_LONG - jumpifhalfword CMP_NO_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_ANY, BattleScript_ActivateTeraformZero_End + call BattleScript_ActivateWeatherAbilities + jumpifhalfword CMP_NO_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_ANY, BattleScript_ActivateTeraformZeroEffects BattleScript_ActivateTeraformZero_RemoveTerrain: removeterrain playanimation BS_ATTACKER, B_ANIM_RESTORE_BG printfromtable gTerrainStringIds waitmessage B_WAIT_TIME_LONG -BattleScript_ActivateTeraformZero_End: +BattleScript_ActivateTeraformZeroEffects: + saveattacker + savetarget tryboosterenergy ON_ANY + resetterrainabilityflags + setbyte gBattlerAttacker, 0 +BattleScript_ActivateTeraformZeroLoop: + copyarraywithindex gBattlerTarget, gBattlerByTurnOrder, gBattlerAttacker, 1 + activateterrainchangeabilities BS_TARGET + activateweatherchangeabilities BS_TARGET + addbyte gBattlerAttacker, 1 + jumpifbytenotequal gBattlerAttacker, gBattlersCount, BattleScript_ActivateTeraformZeroLoop + restoreattacker + restoretarget +BattleScript_ActivateTeraformZero_End: end3 BattleScript_QuickClawActivation:: diff --git a/test/battle/ability/flower_gift.c b/test/battle/ability/flower_gift.c index 8f897e4668..29f5820191 100644 --- a/test/battle/ability/flower_gift.c +++ b/test/battle/ability/flower_gift.c @@ -199,4 +199,23 @@ SINGLE_BATTLE_TEST("Flower Gift transforms Cherrim back when it uses a move that } } +DOUBLE_BATTLE_TEST("Flower Gift reverts Cherrim back after Teraform Zero clears weather") +{ + GIVEN { + PLAYER(SPECIES_TERAPAGOS_TERASTAL); + PLAYER(SPECIES_CHERRIM) { Ability(ABILITY_FLOWER_GIFT); } + OPPONENT(SPECIES_GROUDON) { Ability(ABILITY_DROUGHT); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); } + } SCENE { + ABILITY_POPUP(opponentLeft, ABILITY_DROUGHT); + ABILITY_POPUP(playerRight, ABILITY_FLOWER_GIFT); + ABILITY_POPUP(playerLeft, ABILITY_TERAFORM_ZERO); + ABILITY_POPUP(playerRight, ABILITY_FLOWER_GIFT); + } THEN { + EXPECT_EQ(playerRight->species, SPECIES_CHERRIM); + } +} + TO_DO_BATTLE_TEST("Flower Gift does not transform Cherrim back to normal when suppressed if Cherrim is Dynamaxed"); diff --git a/test/battle/ability/forecast.c b/test/battle/ability/forecast.c index c462a3a634..418c8058d2 100644 --- a/test/battle/ability/forecast.c +++ b/test/battle/ability/forecast.c @@ -419,3 +419,22 @@ SINGLE_BATTLE_TEST("Forecast transforms Castform when Cloud Nine ability user le MESSAGE("Castform transformed!"); } } + +DOUBLE_BATTLE_TEST("Forecast reverts Castform back after Teraform Zero clears weather") +{ + GIVEN { + PLAYER(SPECIES_TERAPAGOS_TERASTAL); + PLAYER(SPECIES_CASTFORM) { Ability(ABILITY_FORECAST); } + OPPONENT(SPECIES_KYOGRE) { Ability(ABILITY_DRIZZLE); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); } + } SCENE { + ABILITY_POPUP(opponentLeft, ABILITY_DRIZZLE); + ABILITY_POPUP(playerRight, ABILITY_FORECAST); + ABILITY_POPUP(playerLeft, ABILITY_TERAFORM_ZERO); + ABILITY_POPUP(playerRight, ABILITY_FORECAST); + } THEN { + EXPECT_EQ(playerRight->species, SPECIES_CASTFORM_NORMAL); + } +} From 1946d18ba52dda2f54a3394afb7b07477305b700 Mon Sep 17 00:00:00 2001 From: Raymond Dodge Date: Thu, 16 Oct 2025 15:31:51 -0400 Subject: [PATCH 023/130] Add all pages in `docs` to doc website (#7907) --- docs/SUMMARY.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index c1e6a78752..7f20718f30 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -3,21 +3,40 @@ - [README](./README.md) - [Installation](./INSTALL.md) - [Setting up WSL1 (Legacy Portion)](./legacy_WSL1_INSTALL.md) + - [ChromeOS](./install/chromeos/CHROME_OS.md) + - [Linux]() + - [ARCH_LINUX](./install/linux/ARCH_LINUX.md) + - [DEBIAN](./install/linux/DEBIAN.md) + - [NIXOS](./install/linux/NIXOS.md) + - [OTHERS](./install/linux/OTHERS.md) + - [UBUNTU](./install/linux/UBUNTU.md) + - [macOS](./install/mac/MAC_OS.md) + - [Windows]() + - [CYGWIN](./install/windows/CYGWIN.md) + - [MSYS2](./install/windows/MSYS2.md) + - [WSL](./install/windows/WSL.md) - [Run documentation site locally](local_mdbook/index.md) - [Ubuntu WSL1/WSL2](local_mdbook/ubuntu_WSL.md) - [Contributing](./CONTRIBUTING.md) +- [Styleguide and Principles](./STYLEGUIDE.md) - [Credits](./CREDITS.md) - [Tutorials]() - [What are AI Flags?](tutorials/ai_flags.md) - [How to add new AI Flags](tutorials/ai_logic.md) - [How to add new battle script commands/macros](tutorials/how_to_battle_script_command_macro.md) - [How to add a new move](tutorials/how_to_new_move.md) - - [How to add a new trainer class](tutorials/how_to_trainer_class.md) + - [How to add a new trainer class]() + - [How to add a new trainer front pic](tutorials/how_to_trainer_front_pic.md) + - [How to add a new trainer back pic](tutorials/how_to_trainer_back_pic.md) - [How to add a new Pokémon](tutorials/how_to_new_pokemon.md) - [v1.6.x and earlier](tutorials/how_to_new_pokemon_1_6_0.md) - [How to use the Testing System](tutorials/how_to_testing_system.md) - [How to add new Trainer Slides](tutorials/how_to_new_trainer_slide.md) - [Day/Night System FAQ](tutorials/dns.md) + - [How to use the code entry system](tutorials/how_to_code_entry.md) + - [How to use Follower NPCs](tutorials/how_to_follower_npc.md) + - [Time-Based Encounters](tutorials/how_to_time_of_day_encounters.md) + - [How to use Trainer Party Pools](tutorials/how_to_trainer_party_pool.md) - [Changelog](./CHANGELOG.md) - [1.13.x]() - [Version 1.13.2](changelogs/1.13.x/1.13.2.md) @@ -39,6 +58,7 @@ - [Version 1.10.2](changelogs/1.10.x/1.10.2.md) - [Version 1.10.1](changelogs/1.10.x/1.10.1.md) - [Version 1.10.0](changelogs/1.10.x/1.10.0.md) + - [Megaman Battle Network Style Names](./mmbn_style_names.md) - [1.9.x]() - [Version 1.9.4](changelogs/1.9.x/1.9.4.md) - [Version 1.9.3](changelogs/1.9.x/1.9.3.md) @@ -87,4 +107,5 @@ - [Team Procedures]() - [How to make an Expansion version](team_procedures/expansion_versions.md) - [Release Schedule and Process](team_procedures/schedule.md) + - [Merge Checklist](team_procedures/merge_checklist.md) - [Scope Guidelines](team_procedures/scope.md) From bb4a87676846948d2605940830db8d11c4479dce Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Thu, 16 Oct 2025 21:34:40 +0200 Subject: [PATCH 024/130] Fix Focus Energy boosting crit by the wrong amount with gen1 crit chance (#7956) Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- src/battle_script_commands.c | 3 ++- test/battle/move_effect/focus_energy.c | 29 +++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 5280797a41..b09ac176a9 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -11161,7 +11161,8 @@ static void Cmd_setfocusenergy(void) } else { - if (GetGenConfig(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO) >= GEN_3) + if (GetGenConfig(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO) >= GEN_3 + || GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_1) gBattleMons[battler].volatiles.focusEnergy = TRUE; else gBattleMons[battler].volatiles.dragonCheer = TRUE; diff --git a/test/battle/move_effect/focus_energy.c b/test/battle/move_effect/focus_energy.c index 48924132ef..17a08ba7f3 100644 --- a/test/battle/move_effect/focus_energy.c +++ b/test/battle/move_effect/focus_energy.c @@ -23,7 +23,34 @@ SINGLE_BATTLE_TEST("Focus Energy increases the user's critical hit ratio by 1 st } PASSES_RANDOMLY(1, chance, RNG_CRITICAL_HIT); GIVEN { - WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, (genConfig == GEN_1)? GEN_2 : genConfig); + WITH_CONFIG(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO, genConfig); + ASSUME(GetSpeciesBaseSpeed(SPECIES_WOBBUFFET) == 33); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useFocusEnergy) + TURN { MOVE(player, MOVE_FOCUS_ENERGY); } + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + if (useFocusEnergy) + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_ENERGY, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + MESSAGE("A critical hit!"); + } +} + +SINGLE_BATTLE_TEST("Focus Energy multiplies crit chance by 4 with gen 1 crit chance") +{ + bool32 useFocusEnergy = 0; + u32 genConfig = 0, chance = 0; + for (u32 j = GEN_1; j <= GEN_9; j++) { + PARAMETRIZE { genConfig = j; useFocusEnergy = FALSE; chance = 16;} + PARAMETRIZE { genConfig = j; useFocusEnergy = TRUE; chance = 4;} + } + PASSES_RANDOMLY(1, chance, RNG_CRITICAL_HIT); + GIVEN { + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, GEN_1); WITH_CONFIG(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO, genConfig); ASSUME(GetSpeciesBaseSpeed(SPECIES_WOBBUFFET) == 33); PLAYER(SPECIES_WOBBUFFET); From 358f3211ca1c3497c110f9440077b254292b38ec Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Fri, 17 Oct 2025 20:17:35 +0200 Subject: [PATCH 025/130] Fix nicknames containing many / overflowing the party screen textbox (#7970) Co-authored-by: Hedara --- graphics/fonts/latin_small_narrower.png | Bin 4371 -> 4359 bytes src/fonts.c | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/graphics/fonts/latin_small_narrower.png b/graphics/fonts/latin_small_narrower.png index 69bca4645ac66404905d8275a827ffea46f6c93b..ebb7c70d27257fcd07f6ee5e10b475576e4662a4 100644 GIT binary patch literal 4359 zcmeI0S5(v4+Q$F?kWi99s0k{~GSZ|(hZb5yM=TH{q6SciSU`~CfDlO_35cLb(9sbE z0$~&oM+F2S4ke+FBMJ%#N(*F=lF(a12?x*m?&o|L=k8oRd#}CMzS!@xf6u%2O(MZn z8NL|~0D$tLgZn)I0NfZt07bbC@!s$9c0<4j4_6o9 z*Votm1QFW+K(+AD{=J@*tcf!p<9|0nYD(7WH9kENT`fd^bWe%ZmvnYK?lxnGf%&`6 z7ohp;M>*c6oq`;sb>BB<86|UYRCEuB1o?S&N=*Db1?N}52)Q%h*#E`}!GPhw(Arfs zotNOtI@n~}j{MDnVbtuzJSa5zCd4Ju2{xKQyPB(1&j3)@jy|lnxXk$C)PynczEFbi z*x^sI;L|h$_eg|Lfwg4J-GCB*V{E8^(RFCsPS9lWFO^UVQ!q0roiP>+H}4!4NSUg> zf_8bAOP4NnSx4C<$Q&Fnt)JiCof}b?)Z@>U10(7J0fcK6v`BGwDI?>0&)VO=m9}Q~ zINYJO5}Z@ZZzP}mQ)Rdg!bgSvDZcCu^h^&zhz(7bAotX=ReX_?ik4rX0!g4&UP0UG z>mzIKWDool)?vbZQjPpfspE=9GMs2U^rJ4>3wZ!EIfz~;m@y_AIWLO{`gkziP7H73 z=dzaDomDebhh6D?V^H?$l`8GkaVBxXE#xN_eJ|~7?L1QPNnYMsn1wbq)9<2vXKYun zUZ{L4y3O#%Ypdw??(EpaoLa5Z&fEBilQeD6$Ps<^>z-G=LF#D1s21k5T+_(hHn$p^ zpfv88soyjB*>6eP`OlKn_(nrE%xjn9ZZ=~KnnqX9MOp$2nPf5DYwDFSrR}u9`pEBX z(8Jh_nthDjhCqXMw3VHYoLeea$$P*Q&klkpD24}WBsVDt3N;r83Sdtqp4``W?gD$) z2b#-&J+Kyi+u}Mbu+5ORe9qDwk8#Ern3+v{{Va;C)3)D~(A-5;$^i(V2ji{Ab72b; zu*60j%T|7}H0kz?Ewz$Yz~9sGO+X4GghErsBfr}_X8Od2cm`+qkDaBD~~kK1HOmnJ`bC{gN%h~ zaVmBEjPP$-Z^-;fnJYVDf0K{Y>+t}e@)=lNC}}S9i*NxtY(w9Z@>8Y#kxgLWRp}F; z>?Fl&SN{otAI%3>Wk!4*?A0SoqD!)aEUUxMLiv#B@_E}ol*C5Ghiq)dWVh1P0K-$p zeba-O9UMmKhGF?Ns>Lr=Y`WEHPn^RbM*PR_4#M^fvsZXv#k(EH3xfvkXj@DoXc&Mu ztbhR}oHr1sPsc`8)=GZyz{WH16}U6y5L*_(omeSbEC z?|zpqDRDVJr&-K4aD)X3Pn|Cx3s;B*vy?Su2KYu8s>fuObaXl;OknsfEM`v;GI9y? zW7=M$aAha?-%FV;0nB}`gJZ;|D#6T|#KdJ9?5!TMO9Awk(^vnu6Rdk~>M#f12T-du z@#c67!HkDk{CqxC9P(lzsCF1a{*|`0Fb|-T*iF|FT1%{~*5Slc~7 zwcF9#KI{Z04Fy^f5tMK5n@fw1Ef=Y^<*I!B0vo-ImkhHFstBzb^U3LjvpK}?o+(4^ z7T0^1_IYE@#cjUzycn{@=IS_);%Qp^|cnKBDxD*>6bH7^fqmlI@aHR zu?6#KNwJtcRG_8b=sSq=yQiPuH7&j4Ola+ycZ57Nj>BygcP3sRJU&>RR#>mwaO%Pc z6z_Bv{G3`fjt4>^LX*_%g?}B_YJT1J-ajD1=5IOJlP-(!3s@)lXhkwY(2Fg)MTN{t znY?xd!NtpE9XJzU%EoqbK5AY~IOl%yN`(>!sJdxf{VM)M6s6giT`meXd!R3&9=-V# zki^9%;P$Vqd^EuteqX4MjHWu%S9l!Y)E33O?-l-LpDU4;^A=bYV?`65V@cdSb+GAh zxT-oMU8sLE4Z}~t{J63=z8I*9uyQNFq#gsJ^qM|E{aY~Dt9Sv_o4B3Gf>;VylMwhB24R)l ztAJOYftZIvgT!&X(|}6M*UfDy(+@2?phDO&<{w4XsaVyhWgrMrp??74xdJkEPsHdr z8z4`KCkC9G@DFh%CCodXGq<|MhQ&2;X18ufO}+)|lgVT7Z4$C*ZX8(Lf} z1AUKEDwq2m^*#mdwkHaf;Fsf$Y|sNx);)@!&g1e4tv`dfoKecef*7%dT@ulfGZNXF z{xH)%i$$vS4?zn8UBph!J5QytNPW(&n5UpZWyMDrvb24HoqU4nxDS1ZfV_++#tnc2TeS`fl4RS$0jO}TMZZPYe5$aPo$ft~!}AbfzySX)U)cdKl_DO~D#)W(=~@6`*;jWtb*o?mHKiS*>5ox4vFm0>y`0J9sHB89 zha^8*v8Sv%XWmI}yi@HWF0+5D-r;{g#kEzQ0W8RtP&SAuKdv@B{9Oo~n2Tp1266o0 zdz>#^P`tX^f{#ihd|7AfKuKBlZV>*A^}2UluT$T)Oo9|!ZM}=!_o3@9u(WxTDllc# z2|S;z-~6g~%+Z2lihr?4(d2v$qv#kgx|-g|BWEor&882+Is;6#aoi3lhap1e%7M?c z08QDULLbb^G4KH8=HG^3jEc-(na(KHijZXki9v4HO!zUb&#Jd44J~9lSsYccs80$! z^CDzAR-u;8y%N)Ca`Bw{psrH9gj~5w++e9$NE3ALv66MAmK=8JO(==9npQ-)9<^U_l5qX7>O*z1pWF-m|$Idk;Md9CM_xvD^4Eu-|4I7%@A!X6=>PuRfBM8fT9e$5S|zls3-{wJQq2YrGCZEje-RBFwwDZ! zOhYztoMIrPHTw~f4pvfdzYly1 z@0cS{Oo0IHGD|f7B;RMyR9{upRB|9vGAWe+Z9=6IhjP$I=& z0r@7nESv8B(!uR{)TqGuJ+XK*1L6qQCT?`WmZefK*UDxe6?|k@(KhJy9bk_prX~BX zycF2ae?+M0UXIMa>pQh9@clzND2R9Fx24e1nPIit&#C}O)$bm6}MPO7SGQ=A{M8cXmjw+vE_a7 zW(fp`v1?M9ap+N77RvU(8@rkWwfK)S?Di{ofmHg?!O!Vr&G#O(?1k3Pu1;^GcKuR! z40wZ{{CnupdkjE>EoQfU+1wK&w%3ZdF{=yWw!3D#Uq=e!RBoM`Ojs+hGtz}KY8a>R zS*r~~FJKnNX&}jk2P(-N(a9xR6vuQn(yGhVkGzIKB%fP*F$FZ=18nJS$FHRR1PJ1N%QB7NS?evmmaswx?7Q`W|j4!Neg>Y-H`F= zy|b;~n{{Oog2Jr006FcAwWuE9UPAxdA|-DiI(=qQ=7%c#>OlbODq=2<*8%l_Ms1_oj=7C?%ud%MNF%9Vh418B)TcGOAUG+sF|x-5H_dkwR|lC z_1^q>wCKJU*JeYeA5Vi;-AY*yo&qXB&e@?Df@ZjxbFm%Muz5Cnwl4?CgtCA#vPep{ z8@Mn8*?4ZYli|)w@`G1le+8a@RxSR43L2hBd=eoi51?-1ewCv}GJP~gq~(iVI%s-r z-v74}xf=C)o4_BUs0bFc<*NvFfpbpG$sL>S0t(dMB#o-fxe?2&D~jBC_q*4*0)o^v z?p29LuV26RR;kQ1r>PGldrW?7<_DkzuDSc2-~beb0`@5zP^&ezJL;=tm#Cd?XcDE_ z8fSA%VZKzSoBp253D;dg4yFtFI!A+ zD_e^^hhZJu=8bSz)Q7en7ijCJwB8Ag0Nrj@Uz>9TMZ+oAQa*-1P$R{SLS+{n$^BTa!|a?Ek`*2}dC9$hA7R(|=yoN*3+|Tul8JL*oC2PK*+33S z;)8PiU)qs(8;U@0KHdRd^mlK*dbc4`+P*Sxcm%x!I2l^)- z%Cw-Kl|F=({{j3R9eW#Ot~^zo+2Jed@-cdeDPxQbxcQWuJ(c91%_!5mc2V&ptq|z{ zzH$Wo5NWueRM*?5Gl4P4<@Kha3CImMCp4KEgCEt4m3A`tnHP0?`!9ZwA{4ghAamc; zwz$o`fW*V>&qKb^e>u1Gs!NK7sDITg)WmNBr2Be4kBn=VJYj10AI@9TF|z<3Udj)w zOTeeXP#c=l1+1AfE3kd!Gu%6(DpW3zVmNox#`|qE1WtKaO$w?v<=!E%C9<>$&_-_{ z3KRGm5SWVGrk3D2t$a5G80{BS;e(`P0QS!Zu8STRf+E;;f7eua;^Oa$?yDJfhoUoo z5p&(+n@9JOapvLDUmA0oeL9CwtB|qEPJua`B(47T+_%zfj?y60CT(!xirKdDnSGuv zu|$q?gv@yliTE}s!K-_LWuw{!jYvu_^3i{oZk>^X>Nl9}Ags&!#`i781Y%^iFGcNw zFTpO%k$hIq#fg3v+i~)1Obe2^n?JKQf#fbJt}Q83_}Zz^C1)UnR&z~>*-zrGtYujO z(t~1U%QI&x-_k2Pc{AeRGvHZx3+FP#WTo%7{K}0^&9Vj_79ItODFb&Bv#bC?hH6W~ z#6!HN2L@QiaM6Mwh=VC6R#%#_7p2CDlG9UAprNK<-df(QU0@+4Cs>Z_-{stiO3`x}RSYi>fWaz++3#nB_hE z5ELPRnzfrmnV3Qh@lZhb8BnhqaS~1ricte!EGkc-2uRTt@bl--X6!Fnss9v>{|VTC z2W61~d+*f5j{QPpfq$pj@p!-!u`HS$zUH*kGd+N3TfL7ubi+HhmdF4Hvu&fHZST zfJqCNAWgt1+W)>qt^N1sNh6NqS4v;KQ<;y3K94DtzuZcS#UV|)JsT*UA_Yxd$B#TGdMz3iAhAgHc3 zS9>fJdfrdJ`*XpyqLQA&5R!;+p-kMmJTH8CSwulN+nvtyUG>JfV&y|D$^mt2&Gv_p#QT19$ORZF(yIBHq}ZRbJ`}i` zfCgz8qB~7Apwj!hM?zEE`@b2pMR?}gEJdTq&e;{L4M+%J1+-In%XKZINdIjpP*RJdi7Pi^5-8$tI;Rl5pxcR?FdC1t9S+NBapSqmP-H_p*}MT)mI>O0w%-EEJ$OjN4a+jU-MDiEGPcrG zl{dkDGw?egK)%ldUf^d@9DyoqL~F-uaN;;I2F${_gf($_(=#KQ4fPM~vX*)OIUg=_ ztXtA^KMISJKmGH82*Td73kol0jyj+zL7?Y&C=|1=0T~>gCf&imal3nHe8b+ z%z8QE#=CcQ6Nw7CU01-{+VwL5KGzq=6Tb?__>cjIylhwjy9QTOV@h|Icr?5DETLX8 zWPK@ykH38(R^tt9*HB07fqlQ!i^A*7p4^_N&c%(R@3>fg3XGY;Hw}ZTiw%b3?_QOL z#x%7jE4}t&Tu4`!`UGvz?Z_k*MpXeDj{MA#hVe!$>$p=e_v92CQGQ8g-11j7?D^TR z>2}hTciIFM%{Y2#Zb+~p_zV-_4;<^Z@&LpV6qSPd(%R12^@qYoq+nhFB5=> z$MzB>ALP87uNN#=!7qR}y*yjki&i%RfK`TV2IN#)jgCucM4_2eO-D_>Hh}cGVslI} z*AXNP<=m?XD%DUR$2~Y1wK;c;v#*N_Tq;VRTt@7)cmH4qcIlj*#+Ym=ZeIus!-j6F zJ~rv)8#{}W64l-6+;e`v%3Z11lk)YF?C%M+>&jm}s4bP0B=jg*mgY)|!@l7@_Og-O zrmi+tSi+jIVJ1lvp0#lTRmJ6$_~(fm_(+JwConeLMDA3jl$Ja$Cilz41LJzKT6(Ua zU;c5V<-D6$oCW-daqE7dE@7`iw*EbNulm+a1EaJ`04MO~=9s7x-fz`^c`h#o(iXFk zE6HNZ;+klx^Cvo8m~7(WKp?^Rm3I63@5UG@ie6>a&l+m>O-KmVqwYH7rK^+M_z&tR#VMju4o423f^Q6COmUVYd zC6>f|f(u()&pkieLH5vG!f;fR&em>tOx52CNtXt~M) z8+X<6CyD9@V#(iJh6%mlPqIqGYf7KySAMDC%Z*Jx2m0kzemCBYPDI~peeOU$uWEM- zl3Qk#pXrmzR@oHT*STK&5C`NNC%1uJ!y7X?!s$qj@`gu#@j1kHHbT@mIBU=M>MV=X zk08!kdD*!$Bc~1nTbNQ2j4sh`82RIz$>pln0pA1KR0BG(!J2$aF_ppCt5I3k0u#%U zZf|U347vr-Dpcc*PNv$f9*x->Ykhbkj4#^dx6k;toUz3_lj_(_F`xKZE&c=!e=+^= zpxOEAe@jrLaQ%eY`(WKaYHfjciZkDa|7=wPB9Kej9X-nI@EM#!!0+>FAXe){wpH9l zPLyoYg_&4kfyquaG+Yf|AUqV*^DYAO$lQ7d35q95WU820st`&~%i3tLe1b$4DoH20 z3yWrfvAckAe`LXOU*)8%^YrDX+smJxJK8LNEc!=A&&OarsWQnfG3AFhjDvJXH{q|O z)ZiSlGwmdrpAp9Cp`$_O9Nn0*9t^CylwM38*|k!Si>$vb|J8zcxPdBdVpTzs;NHM9 zBAS}nwTa5s9wSLx_uRk`CPp$bCez+#3ccAlJBVrN81GpD0 x+;G;_AmJhyj7k|%c|g*mg{{}ne+wm*G*$NQdUeon{Wk+Rb=>;cizDQie*snOA+!Jh diff --git a/src/fonts.c b/src/fonts.c index 46f3a335c4..3e69537113 100644 --- a/src/fonts.c +++ b/src/fonts.c @@ -229,7 +229,7 @@ ALIGNED(4) const u8 gFontSmallNarrowerLatinGlyphWidths[] = { 4, 5, 6, 7, 4, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 2, 4, 2, - 4, 4, 4, 2, 2, 4, 4, 8, 2, 8, 5, 4, 4, 4, 4, 4, + 4, 4, 4, 2, 2, 4, 4, 8, 2, 8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 3, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 7, From dd2eab0c410cdb42ebfbddd189bf6efa2540d2fb Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Fri, 17 Oct 2025 20:27:54 +0200 Subject: [PATCH 026/130] Fix bug where transformed pokemon lose copied stats on levelup (#7969) --- src/battle_script_commands.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index b09ac176a9..e6c4a4adb4 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -4773,7 +4773,15 @@ static void Cmd_getexp(void) if (battler != 0xFF) { - CopyMonLevelAndBaseStatsToBattleMon(battler, &gPlayerParty[*expMonId]); + if (gBattleMons[battler].volatiles.transformed) + { + gBattleMons[battler].level = GetMonData(&gPlayerParty[*expMonId], MON_DATA_LEVEL); + gBattleMons[battler].hp = GetMonData(&gPlayerParty[*expMonId], MON_DATA_HP); + } + else + { + CopyMonLevelAndBaseStatsToBattleMon(battler, &gPlayerParty[*expMonId]); + } if (gBattleMons[battler].volatiles.powerTrick) SWAP(gBattleMons[battler].attack, gBattleMons[battler].defense, temp); } From 38221b9de512f29cfe6b1c70e73e90265573d724 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Fri, 17 Oct 2025 19:40:37 +0100 Subject: [PATCH 027/130] Fixes Shields Down incorrectly preventing status on Minior Core form (#7968) --- asm/macros/battle_script.inc | 4 +- data/battle_scripts_1.s | 6 +- src/battle_script_commands.c | 12 ++- src/battle_util.c | 6 +- test/battle/ability/flower_veil.c | 4 +- test/battle/ability/shields_down.c | 26 +++++++ test/battle/move_effect/rest.c | 117 +++++++++++++++++++++++++++++ 7 files changed, 159 insertions(+), 16 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 547e4d6e3f..5ccaf2ea50 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -2405,8 +2405,8 @@ .4byte \jumpInstr .endm - .macro jumpifleafguardprotected battler:req, jumpInstr:req - callnative BS_JumpIfLeafGuardProtected + .macro jumpifabilitypreventsrest battler:req, jumpInstr:req + callnative BS_JumpIfAbilityPreventsRest .byte \battler .4byte \jumpInstr .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 3cf1f53fec..db4f7e65e3 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -3186,9 +3186,7 @@ BattleScript_EffectRest:: jumpifability BS_TARGET, ABILITY_INSOMNIA, BattleScript_InsomniaProtects jumpifability BS_TARGET, ABILITY_VITAL_SPIRIT, BattleScript_InsomniaProtects jumpifability BS_ATTACKER, ABILITY_PURIFYING_SALT, BattleScript_InsomniaProtects -.if B_LEAF_GUARD_PREVENTS_REST >= GEN_5 - jumpifleafguardprotected BS_TARGET, BattleScript_LeafGuardPreventsRest -.endif + jumpifabilitypreventsrest BS_TARGET, BattleScript_AbilityPreventsRest trysetrest BattleScript_AlreadyAtFullHp pause B_WAIT_TIME_SHORT printfromtable gRestUsedStringIds @@ -3210,7 +3208,7 @@ BattleScript_RestIsAlreadyAsleep:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_LeafGuardPreventsRest:: +BattleScript_AbilityPreventsRest:: pause B_WAIT_TIME_SHORT printstring STRINGID_BUTITFAILED waitmessage B_WAIT_TIME_LONG diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index e6c4a4adb4..c9b64ffea2 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -18243,19 +18243,17 @@ void BS_JumpIfSpecies(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_JumpIfLeafGuardProtected(void) +void BS_JumpIfAbilityPreventsRest(void) { NATIVE_ARGS(u8 battler, const u8 *jumpInstr); u32 battler = GetBattlerForBattleScript(cmd->battler); - if (IsLeafGuardProtected(battler, GetBattlerAbility(battler))) - { - gBattlerAbility = battler; + u32 ability = GetBattlerAbility(battler); + if (B_LEAF_GUARD_PREVENTS_REST >= GEN_5 && IsLeafGuardProtected(battler, ability)) + gBattlescriptCurrInstr = cmd->jumpInstr; + else if (IsShieldsDownProtected(battler, ability)) gBattlescriptCurrInstr = cmd->jumpInstr; - } else - { gBattlescriptCurrInstr = cmd->nextInstr; - } } void BS_SetAttackerToStickyWebUser(void) diff --git a/src/battle_util.c b/src/battle_util.c index abf61c1d58..42329b5495 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5713,7 +5713,6 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u // Checks that apply to all non volatile statuses if (abilityDef == ABILITY_COMATOSE - || abilityDef == ABILITY_SHIELDS_DOWN || abilityDef == ABILITY_PURIFYING_SALT) { abilityAffected = TRUE; @@ -5728,6 +5727,11 @@ bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u abilityAffected = TRUE; battleScript = BattleScript_AbilityProtectsDoesntAffect; } + else if (IsShieldsDownProtected(battlerDef, abilityDef)) + { + abilityAffected = TRUE; + battleScript = BattleScript_AbilityProtectsDoesntAffect; + } else if ((sideBattler = IsFlowerVeilProtected(battlerDef))) { abilityAffected = TRUE; diff --git a/test/battle/ability/flower_veil.c b/test/battle/ability/flower_veil.c index cb72cd33ae..78d15df1bd 100644 --- a/test/battle/ability/flower_veil.c +++ b/test/battle/ability/flower_veil.c @@ -15,7 +15,7 @@ ASSUMPTIONS ASSUME(GetMoveNonVolatileStatus(MOVE_HYPNOSIS) == MOVE_EFFECT_SLEEP); } -DOUBLE_BATTLE_TEST("Flower Veil prevents Toxic bad poison on partner - right target") +DOUBLE_BATTLE_TEST("Flower Veil prevents status on allied Grass-types - right target") { u32 move; @@ -39,7 +39,7 @@ DOUBLE_BATTLE_TEST("Flower Veil prevents Toxic bad poison on partner - right tar } } -DOUBLE_BATTLE_TEST("Flower Veil prevents Toxic bad poison on partner - left target") +DOUBLE_BATTLE_TEST("Flower Veil prevents status on allied Grass-types - left target") { u32 move; diff --git a/test/battle/ability/shields_down.c b/test/battle/ability/shields_down.c index f9def2991d..5c46a8d589 100644 --- a/test/battle/ability/shields_down.c +++ b/test/battle/ability/shields_down.c @@ -32,3 +32,29 @@ SINGLE_BATTLE_TEST("Minior Core transforms into Minior Meteor on switch-in if it EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR); } } + +SINGLE_BATTLE_TEST("Shields Down protects Minior Meteor from status conditions") +{ + u32 species, hp; + PARAMETRIZE { species = SPECIES_MINIOR_METEOR; hp = 300; } + PARAMETRIZE { species = SPECIES_MINIOR_CORE; hp = 100; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_NON_VOLATILE_STATUS); + ASSUME(GetMoveNonVolatileStatus(MOVE_WILL_O_WISP) == MOVE_EFFECT_BURN); + PLAYER(SPECIES_WYNAUT); + OPPONENT(species) { Ability(ABILITY_SHIELDS_DOWN); HP(hp); MaxHP(300); } + } WHEN { + TURN { MOVE(player, MOVE_WILL_O_WISP); } + } SCENE { + if (species == SPECIES_MINIOR_METEOR) + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, player); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, player); + } THEN { + if (species == SPECIES_MINIOR_METEOR) + EXPECT_EQ(opponent->status1, STATUS1_NONE); + else + EXPECT(opponent->status1 & STATUS1_BURN); + } +} diff --git a/test/battle/move_effect/rest.c b/test/battle/move_effect/rest.c index 4d0d85b560..fcf2b15567 100644 --- a/test/battle/move_effect/rest.c +++ b/test/battle/move_effect/rest.c @@ -1,4 +1,121 @@ #include "global.h" #include "test/battle.h" +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_REST) == EFFECT_REST); +} + +SINGLE_BATTLE_TEST("Rest causes the user to fall asleep and restores HP to full") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_REST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player); + } THEN { + EXPECT(player->status1 & STATUS1_SLEEP); + EXPECT_EQ(player->hp, player->maxHP); + } +} + +SINGLE_BATTLE_TEST("Rest fails if the user is at full HP") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(300); MaxHP(300); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_REST); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player); + } THEN { + EXPECT(!(player->status1 & STATUS1_SLEEP)); + } +} + +SINGLE_BATTLE_TEST("Rest fails if the user is protected by Leaf Guard") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SUNNY_DAY) == EFFECT_SUNNY_DAY); + ASSUME(B_LEAF_GUARD_PREVENTS_REST >= GEN_5); + PLAYER(SPECIES_CHIKORITA) { Ability(ABILITY_LEAF_GUARD); HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SUNNY_DAY); MOVE(player, MOVE_REST); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player); + } THEN { + EXPECT(!(player->status1 & STATUS1_SLEEP)); + } +} + +SINGLE_BATTLE_TEST("Rest fails if the user is protected by Shields Down") +{ + GIVEN { + PLAYER(SPECIES_MINIOR_METEOR) { Ability(ABILITY_SHIELDS_DOWN); HP(299); MaxHP(300); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_REST); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player); + } THEN { + EXPECT(!(player->status1 & STATUS1_SLEEP)); + } +} + +SINGLE_BATTLE_TEST("Rest fails if the user is protected by Electric/Misty Terrain") +{ + u32 move; + PARAMETRIZE { move = MOVE_ELECTRIC_TERRAIN; } + PARAMETRIZE { move = MOVE_MISTY_TERRAIN; } + GIVEN { + ASSUME(GetMoveEffect(MOVE_ELECTRIC_TERRAIN) == EFFECT_ELECTRIC_TERRAIN); + ASSUME(GetMoveEffect(MOVE_MISTY_TERRAIN) == EFFECT_MISTY_TERRAIN); + ASSUME(GetSpeciesType(SPECIES_WYNAUT, 0) != TYPE_FLYING && GetSpeciesType(SPECIES_WYNAUT, 1) != TYPE_FLYING); + PLAYER(SPECIES_WYNAUT) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_REST); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player); + } THEN { + EXPECT(!(player->status1 & STATUS1_SLEEP)); + } +} + +SINGLE_BATTLE_TEST("Rest doesn't fail if the user is protected by Safeguard") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SAFEGUARD) == EFFECT_SAFEGUARD); + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SAFEGUARD); } + TURN { MOVE(player, MOVE_REST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player); + } THEN { + EXPECT(player->status1 & STATUS1_SLEEP); + } +} + +DOUBLE_BATTLE_TEST("Rest doesn't fail if the user is protected by Flower Veil") +{ + GIVEN { + ASSUME(GetSpeciesType(SPECIES_CHIKORITA, 0) == TYPE_GRASS || GetSpeciesType(SPECIES_CHIKORITA, 1) == TYPE_GRASS); + PLAYER(SPECIES_CHIKORITA) { HP(1); } + PLAYER(SPECIES_FLORGES) { Ability(ABILITY_FLOWER_VEIL); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_REST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, playerLeft); + } THEN { + EXPECT(playerLeft->status1 & STATUS1_SLEEP); + } +} + TO_DO_BATTLE_TEST("TODO: Write Rest (Move Effect) test titles") From b03e58e2a1224011767364e472770fc640365e68 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Fri, 17 Oct 2025 22:57:18 +0200 Subject: [PATCH 028/130] Fix Persim Berry battle usage (#7963) Co-authored-by: Hedara --- src/data/items.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/items.h b/src/data/items.h index 9927191d64..6f19ad6d0b 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -9826,7 +9826,7 @@ const struct Item gItemsInfo[] = "heals confusion\n" "in battle."), .pocket = POCKET_BERRIES, - .type = ITEM_USE_BAG_MENU, + .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, .battleUsage = EFFECT_ITEM_CURE_STATUS, .effect = gItemEffect_PersimBerry, From e0fec934bc8d9feb1ed44b1037498ea640848b9b Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 22:58:09 +0200 Subject: [PATCH 029/130] add Syreldar as a contributor for doc, and userTesting (#7888) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 10 ++++++++++ CREDITS.md | 3 +++ 2 files changed, 13 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 7d2e4d4bbf..79c5e9bb26 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -357,6 +357,16 @@ "contributions": [ "design" ] + }, + { + "login": "Syreldar", + "name": "Enrico Drago", + "avatar_url": "https://avatars.githubusercontent.com/u/42327659?v=4", + "profile": "https://metin2.dev/index.php", + "contributions": [ + "doc", + "userTesting" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 4c0b6a3d3e..fef2b2eb63 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -62,6 +62,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d tustin2121
tustin2121

📖 💻 Phantonomy
Phantonomy

🎨 + + Enrico Drago
Enrico Drago

📖 📓 + From f4a4164c067980da469229b72964c6416d9be79d Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 23:22:58 +0200 Subject: [PATCH 030/130] add Pyredrid as a contributor for userTesting, and maintenance (#7889) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- .all-contributorsrc | 10 ++++++++++ CREDITS.md | 1 + 2 files changed, 11 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 79c5e9bb26..0d95328420 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -367,6 +367,16 @@ "doc", "userTesting" ] + }, + { + "login": "Pyredrid", + "name": "Pyredrid", + "avatar_url": "https://avatars.githubusercontent.com/u/8324784?v=4", + "profile": "https://github.com/Pyredrid", + "contributions": [ + "userTesting", + "maintenance" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index fef2b2eb63..36e45717f2 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -64,6 +64,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Enrico Drago
Enrico Drago

📖 📓 + Pyredrid
Pyredrid

📓 🚧 From db3d39d071cc880ea75a2b361b46d1f2b1ba9368 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 23:48:56 +0200 Subject: [PATCH 031/130] add mvit as a contributor for code, and design (#7890) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- .all-contributorsrc | 10 ++++++++++ CREDITS.md | 1 + 2 files changed, 11 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 0d95328420..f0349e5445 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -377,6 +377,16 @@ "userTesting", "maintenance" ] + }, + { + "login": "mvit", + "name": "mv", + "avatar_url": "https://avatars.githubusercontent.com/u/128863?v=4", + "profile": "https://github.com/mvit", + "contributions": [ + "code", + "design" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 36e45717f2..0798af22e9 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -65,6 +65,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Enrico Drago
Enrico Drago

📖 📓 Pyredrid
Pyredrid

📓 🚧 + mv
mv

💻 🎨 From 721f385e1d4056ccd1e3896caae889cbbebbbbb4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 18 Oct 2025 00:18:26 +0200 Subject: [PATCH 032/130] add Mother-Of-Dragons as a contributor for data (#7891) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CREDITS.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index f0349e5445..ae837d03d9 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -387,6 +387,15 @@ "code", "design" ] + }, + { + "login": "Mother-Of-Dragons", + "name": "Avara", + "avatar_url": "https://avatars.githubusercontent.com/u/31101124?v=4", + "profile": "https://github.com/Mother-Of-Dragons", + "contributions": [ + "data" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 0798af22e9..c1cb8f87b8 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -66,6 +66,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Enrico Drago
Enrico Drago

📖 📓 Pyredrid
Pyredrid

📓 🚧 mv
mv

💻 🎨 + Avara
Avara

🔣 From eeceff27e7570e085a08f69a461072087438afe4 Mon Sep 17 00:00:00 2001 From: Raymond Dodge Date: Sat, 18 Oct 2025 04:56:40 -0400 Subject: [PATCH 033/130] Relativize doc links, to fix links in docs site (#7964) --- docs/fix_links.py | 1 - docs/local_mdbook/index.md | 2 +- docs/team_procedures/merge_checklist.md | 10 +++++----- docs/team_procedures/schedule.md | 2 +- docs/tutorials/dns.md | 6 +++--- docs/tutorials/how_to_follower_npc.md | 2 +- docs/tutorials/how_to_new_pokemon.md | 16 ++++++++-------- 7 files changed, 19 insertions(+), 20 deletions(-) diff --git a/docs/fix_links.py b/docs/fix_links.py index 7b6b01b995..6e2eaec485 100644 --- a/docs/fix_links.py +++ b/docs/fix_links.py @@ -36,7 +36,6 @@ def proc_items(items): s = s.replace('](README.md)', '](./)') s = s.replace('](/INSTALL.md', '](INSTALL.md') s = s.replace('](docs/', '](') - s = s.replace('](/docs/', '](/') s = URL_RE.sub(handle_url, s) item['Chapter']['content'] = ANCHOR_RE.sub(handle_anchor, s) proc_items(item['Chapter']['sub_items']) diff --git a/docs/local_mdbook/index.md b/docs/local_mdbook/index.md index ef362dc45f..fa4b7f1e70 100644 --- a/docs/local_mdbook/index.md +++ b/docs/local_mdbook/index.md @@ -1,2 +1,2 @@ ## Running documentation website locally -- [Ubuntu WSL1/WSL2](/docs/local_mdbook/ubuntu_WSL.md) +- [Ubuntu WSL1/WSL2](ubuntu_WSL.md) diff --git a/docs/team_procedures/merge_checklist.md b/docs/team_procedures/merge_checklist.md index ae3ff60b19..53e8d7683f 100644 --- a/docs/team_procedures/merge_checklist.md +++ b/docs/team_procedures/merge_checklist.md @@ -7,7 +7,7 @@ This document is a guide for maintainers to account for all the reccomended step # Checklist ## Is the branch's theoretical functionality in scope? -If you're not sure if a branch's functionality is [in scope](docs/team_procedures/scope.md), start a conversation on Discord to resolve. +If you're not sure if a branch's functionality is [in scope](scope.md), start a conversation on Discord to resolve. ## Does the branch successfully compile? From `make clean`, the branch should locally compile. @@ -37,18 +37,18 @@ If you're not sure if something CAN be tested, start a discussion. Some contribu If any new tests are `KNOWN_FAILING`, issues should be opened describing each of the `KNOWN_FAILING` tests and our understanding of why they fail. -## Does the branch meet our [config philosophy](/docs/STYLEGUIDE.md#config-philosophy)? +## Does the branch meet our [config philosophy](../STYLEGUIDE.md#config-philosophy)? -## Does the branch meet our [saves philosophy](/docs/STYLEGUIDE.md#saves-philosophy)? +## Does the branch meet our [saves philosophy](../STYLEGUIDE.md#saves-philosophy)? -## Does the submitted code follow the [styleguide](/docs/STYLEGUIDE.md)? +## Does the submitted code follow the [styleguide](../STYLEGUIDE.md)? This applies to code that comes from other branches or games. ## Is the pull request appropriately labeled? Without labels, the CHANGELOG will not be properly formatted. For specifically the `bugfix` label, an additional label, detailing what area the bug exists in is required. ## Is `pokeemerald-expansion` free from a merge freeze? -Our [release schedule](/docs/team_procedures/schedule.md) prevents us from merging Big Features and non-bugfixes within certain dates close to a release. Please use `/release` in the RHH Discord to clarify when these are occuring. +Our [release schedule](schedule.md) prevents us from merging Big Features and non-bugfixes within certain dates close to a release. Please use `/release` in the RHH Discord to clarify when these are occuring. # Merging diff --git a/docs/team_procedures/schedule.md b/docs/team_procedures/schedule.md index c689e5b454..520c4af000 100644 --- a/docs/team_procedures/schedule.md +++ b/docs/team_procedures/schedule.md @@ -53,4 +53,4 @@ This designation should be reserved for instances where an existing feature on ` Blocking issues or PRs can be deferred to future releases but should be discussed with the Maintainers that assigned the designation in the first place. -If a version's milestone does not have any issues or PRs assigned to it, that version should be [released](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/docs/team_procedures/expansion_versions.md) as close to the goal date as possible. +If a version's milestone does not have any issues or PRs assigned to it, that version should be [released](expansion_versions.md) as close to the goal date as possible. diff --git a/docs/tutorials/dns.md b/docs/tutorials/dns.md index 908329761e..6fdd1c1d77 100644 --- a/docs/tutorials/dns.md +++ b/docs/tutorials/dns.md @@ -23,8 +23,8 @@ When writing map scripts, `fadescreenswapbuffers` should be preferred over `fade ### Q: How do I make lightbulbs glow? -![Rustboro before adding lamp object events](/docs/tutorials/img/dns/without_lamp.png) -![Rustboro after adding lamp object events](/docs/tutorials/img/dns/with_lamp.png) +![Rustboro before adding lamp object events](img/dns/without_lamp.png) +![Rustboro after adding lamp object events](img/dns/with_lamp.png) A: Making lamps glow is not part of the tileset itself. Instead, place certain object events on top of where you desire a glowing effect. @@ -48,7 +48,7 @@ on separate lines to mark those colors as being light-blended, i.e: During the day time, these color indices appear as normal, but will be blended with either yellow or the 0 index at night. These indices should only be used for things you expect to light up. If you are using [porytiles](https://github.com/grunt-lucas/porytiles/wiki), palette overrides and using slight alterations to a color will aid you in avoiding color conflicts where the wrong index is assigned. -![Rustboro gym after light-blending the windows](/docs/tutorials/img/dns/window_lights.png) +![Rustboro gym after light-blending the windows](img/dns/window_lights.png) The windows appear as normal during the day time (blue) and light up in the night. These use the default color. diff --git a/docs/tutorials/how_to_follower_npc.md b/docs/tutorials/how_to_follower_npc.md index 2e449adbc0..fbd0087859 100644 --- a/docs/tutorials/how_to_follower_npc.md +++ b/docs/tutorials/how_to_follower_npc.md @@ -2,7 +2,7 @@ *Written by Bivurnum* *gif by ghoulslash* -![follower-npc](/docs/tutorials/img/follower_npc/follower-npc.gif) +![follower-npc](img/follower_npc/follower-npc.gif) ## Configs The configs for follower NPCs can be found in [include/config/follower_npc.h](https://github.com/rh-hideout/pokeemerald-expansion/blob/upcoming/include/config/follower_npc.h). diff --git a/docs/tutorials/how_to_new_pokemon.md b/docs/tutorials/how_to_new_pokemon.md index da6add17bc..79f01acbd2 100644 --- a/docs/tutorials/how_to_new_pokemon.md +++ b/docs/tutorials/how_to_new_pokemon.md @@ -42,7 +42,7 @@ The main things that the Expansion changes are listed here. # Useful resources You can open a sprite debug menu by pressing `Select` in a Pokémon's summary screen outside of battle. -![visualizer1](/docs/tutorials/img/add_pokemon/visualizer1.gif) +![visualizer1](img/add_pokemon/visualizer1.gif) # The Data - Part 1 @@ -73,7 +73,7 @@ We add this at the end so that no existing species change Id and so that we don' Now, let's see how it looks in-game! -![visualizer2](/docs/tutorials/img/add_pokemon/visualizer2.png) +![visualizer2](img/add_pokemon/visualizer2.png) Hmmm, something's not right... @@ -446,7 +446,7 @@ Now we can add the number and entry to our Mewthree: }, }; ``` -![image](/docs/tutorials/img/add_pokemon/dex1.png) +![image](img/add_pokemon/dex1.png) The values `pokemonScale`, `pokemonOffset`, `trainerScale` and `trainerOffset` are used for the height comparison figure in the Pokédex. @@ -497,7 +497,7 @@ Edit [src/data/pokemon/pokedex_orders.h](https://github.com/rh-hideout/pokeemera ... }; ``` -![mGBA_lUBfmFEKUx](/docs/tutorials/img/add_pokemon/dex2.gif) +![mGBA_lUBfmFEKUx](img/add_pokemon/dex2.gif) # The Graphics @@ -1058,7 +1058,7 @@ What this allows us to do is to be able to get all forms of a Pokémon in our co For example, in the HGSS dex, it lets us browse between the entries of every form available.: -![hgssdex1](/docs/tutorials/img/add_pokemon/hgssdex1.png) ![image](/docs/tutorials/img/add_pokemon/hgssdex2.png) +![hgssdex1](img/add_pokemon/hgssdex1.png) ![image](img/add_pokemon/hgssdex2.png) In addition, we have the `GET_BASE_SPECIES_ID` macro, which returns the first entry of the table (or return the species itself if it doesn't have a table registered). With this, you can check if a Pokémon is any form of a species. For example, making it so that the Light Ball affects all Pikachu forms: ```c @@ -1089,7 +1089,7 @@ The second value is the target form, to which the Pokémon will change into. Values after that are referred as arguments, and needs to be put there depends on the type of form change, detailed in `include/constants/form_change_types.h`. ## 3. Gender differences -![gender_diffs](/docs/tutorials/img/add_pokemon/gender_diffs.gif) +![gender_diffs](img/add_pokemon/gender_diffs.gif) You may have seen that there's a couple of duplicate fields with a "Female" suffix. ```diff @@ -1262,8 +1262,8 @@ Either way, you may also create custom animation tables and use them here approp ### How to add the Pokémon Object Events to map In Porymap, select the object you want to set the sprite to. Then, change the field "Sprite" to use `OBJ_EVENT_GFX_SPECIES(SPECIES)`, replacing SPECIES with the name of the species you want to use. If you get a compiler error, it's because it used the species define as part of the macro, so it needs to match how you defined it all the way back in [Declare a species constant](#1-Declare-a-species-constant). -![charizard](/docs/tutorials/img/add_pokemon/charizard.png) -![overworld_data](/docs/tutorials/img/add_pokemon/overworld_data.gif) +![charizard](img/add_pokemon/charizard.png) +![overworld_data](img/add_pokemon/overworld_data.gif) If you want to use their shiny and/or female versions, use one of the following macros: - `OBJ_EVENT_GFX_SPECIES_SHINY(name)` From ade1f26d4176810a74dd984ff31cc750905faeff Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 18 Oct 2025 11:09:35 +0200 Subject: [PATCH 034/130] add Doesnty as a contributor for design (#7892) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CREDITS.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index ae837d03d9..2be39e2539 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -396,6 +396,15 @@ "contributions": [ "data" ] + }, + { + "login": "Doesnty", + "name": "Doesnty", + "avatar_url": "https://avatars.githubusercontent.com/u/6163136?v=4", + "profile": "https://github.com/Doesnty", + "contributions": [ + "design" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index c1cb8f87b8..60350703e5 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -67,6 +67,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Pyredrid
Pyredrid

📓 🚧 mv
mv

💻 🎨 Avara
Avara

🔣 + Doesnty
Doesnty

🎨 From cff5c8aa060e4312db7a60a3e31e57c935239c5a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 18 Oct 2025 11:42:59 +0200 Subject: [PATCH 035/130] add FosterProgramming as a contributor for code (#7893) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CREDITS.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 2be39e2539..7f904db31b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -405,6 +405,15 @@ "contributions": [ "design" ] + }, + { + "login": "FosterProgramming", + "name": "FosterProgramming", + "avatar_url": "https://avatars.githubusercontent.com/u/178871164?v=4", + "profile": "https://github.com/FosterProgramming", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 60350703e5..476b0f8eee 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -68,6 +68,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d mv
mv

💻 🎨 Avara
Avara

🔣 Doesnty
Doesnty

🎨 + FosterProgramming
FosterProgramming

💻 From 55f8e401c9cca3fdbc00fa830cff349b5a30920a Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 18 Oct 2025 12:40:05 +0200 Subject: [PATCH 036/130] add Squeetz as a contributor for maintenance (#7894) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CREDITS.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 7f904db31b..241a2d088f 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -414,6 +414,15 @@ "contributions": [ "code" ] + }, + { + "login": "Squeetz", + "name": "Squeetz", + "avatar_url": "https://avatars.githubusercontent.com/u/21145213?v=4", + "profile": "https://github.com/Squeetz", + "contributions": [ + "maintenance" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 476b0f8eee..8b4271a701 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -69,6 +69,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Avara
Avara

🔣 Doesnty
Doesnty

🎨 FosterProgramming
FosterProgramming

💻 + Squeetz
Squeetz

🚧 From ffe08b52e3e0bce0b4deb54fb2514acc822ecf36 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 18 Oct 2025 14:12:25 +0200 Subject: [PATCH 037/130] add ghostyboyy97 as a contributor for code (#7901) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CREDITS.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 241a2d088f..a04141fcf7 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -423,6 +423,15 @@ "contributions": [ "maintenance" ] + }, + { + "login": "ghostyboyy97", + "name": "ghostyboyy97", + "avatar_url": "https://avatars.githubusercontent.com/u/106448956?v=4", + "profile": "https://github.com/ghostyboyy97", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 8b4271a701..1cd4611d69 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -70,6 +70,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Doesnty
Doesnty

🎨 FosterProgramming
FosterProgramming

💻 Squeetz
Squeetz

🚧 + ghostyboyy97
ghostyboyy97

💻 From 90c3a8cb2c48142d88f40a35c19517629bea29c9 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Sat, 18 Oct 2025 18:52:57 +0200 Subject: [PATCH 038/130] Fix battle dome pre round 1 waiting room (#7976) --- data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc b/data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc index e06284c2ca..8e01660a31 100644 --- a/data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc +++ b/data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc @@ -18,6 +18,7 @@ BattleFrontier_BattleDomePreBattleRoom_OnFrame: BattleFrontier_BattleDomePreBattleRoom_EventScript_EnterRoom:: goto_if_eq VAR_0x8006, 1, BattleFrontier_BattleDomePreBattleRoom_EventScript_ReturnFromBattle + delay 0 frontier_set FRONTIER_DATA_RECORD_DISABLED, TRUE setvar VAR_TEMP_0, 1 applymovement LOCALID_PLAYER, BattleFrontier_BattleDomePreBattleRoom_Movement_PlayerEnter From 0965dffe70bb7d4bc229e24e4bc0266a6be6b8c8 Mon Sep 17 00:00:00 2001 From: Estellar <137097857+estellarc@users.noreply.github.com> Date: Sat, 18 Oct 2025 13:53:50 -0300 Subject: [PATCH 039/130] Use TaskFunc wherever posible (#2188) --- include/battle_pyramid_bag.h | 3 ++- include/item_menu.h | 2 +- src/battle_factory_screen.c | 6 +++--- src/battle_pyramid_bag.c | 2 +- src/fldeff_misc.c | 4 ++-- src/item_menu.c | 2 +- src/item_use.c | 2 +- src/palette.c | 2 +- src/pokemon_summary_screen.c | 2 +- 9 files changed, 13 insertions(+), 12 deletions(-) diff --git a/include/battle_pyramid_bag.h b/include/battle_pyramid_bag.h index 0caf8d302c..4d8b87506e 100644 --- a/include/battle_pyramid_bag.h +++ b/include/battle_pyramid_bag.h @@ -3,6 +3,7 @@ #include "list_menu.h" #include "main.h" +#include "task.h" enum { PYRAMIDBAG_LOC_FIELD, @@ -69,6 +70,6 @@ void Task_CloseBattlePyramidBagMessage(u8 taskId); void TryStoreHeldItemsInPyramidBag(void); void ChooseItemsToTossFromPyramidBag(void); void CloseBattlePyramidBag(u8 taskId); -void DisplayItemMessageInBattlePyramid(u8 taskId, const u8 *str, void (*callback)(u8 taskId)); +void DisplayItemMessageInBattlePyramid(u8 taskId, const u8 *str, TaskFunc callback); #endif // GUARD_BATTLE_PYRAMID_BAG_H diff --git a/include/item_menu.h b/include/item_menu.h index 2f7f3f3825..e63eb3b6c2 100644 --- a/include/item_menu.h +++ b/include/item_menu.h @@ -105,7 +105,7 @@ void CB2_ChooseBerry(void); void Task_FadeAndCloseBagMenu(u8 taskId); void BagMenu_YesNo(u8 taskId, u8 windowType, const struct YesNoFuncTable *funcTable); void UpdatePocketItemList(u8 pocketId); -void DisplayItemMessage(u8 taskId, u8 fontId, const u8 *str, void (*callback)(u8 taskId)); +void DisplayItemMessage(u8 taskId, u8 fontId, const u8 *str, TaskFunc callback); void DisplayItemMessageOnField(u8 taskId, const u8 *string, TaskFunc callback); void CloseItemMessage(u8 taskId); diff --git a/src/battle_factory_screen.c b/src/battle_factory_screen.c index eea04b99d3..8726770f18 100644 --- a/src/battle_factory_screen.c +++ b/src/battle_factory_screen.c @@ -125,7 +125,7 @@ struct FactorySelectScreen struct SwapScreenAction { u8 id; - void (*func)(u8 taskId); + TaskFunc func; }; struct FactorySwapScreen @@ -253,7 +253,7 @@ static EWRAM_DATA u8 *sSwapMenuTilemapBuffer = NULL; static EWRAM_DATA u8 *sSwapMonPicBgTilemapBuffer = NULL; static struct FactorySelectScreen *sFactorySelectScreen; -static void (*sSwap_CurrentOptionFunc)(u8 taskId); +static TaskFunc sSwap_CurrentOptionFunc; static struct FactorySwapScreen *sFactorySwapScreen; COMMON_DATA u8 (*gFactorySelect_CurrentOptionFunc)(void) = NULL; @@ -886,7 +886,7 @@ static const struct SpriteTemplate sSpriteTemplate_Swap_MonPicBgAnim = .callback = SpriteCallbackDummy }; -void static (*const sSwap_MenuOptionFuncs[])(u8 taskId) = +static const TaskFunc sSwap_MenuOptionFuncs[] = { Swap_OptionSummary, Swap_OptionSwap, diff --git a/src/battle_pyramid_bag.c b/src/battle_pyramid_bag.c index 5354fc46fa..805b4df905 100644 --- a/src/battle_pyramid_bag.c +++ b/src/battle_pyramid_bag.c @@ -1502,7 +1502,7 @@ static void CreatePyramidBagYesNo(u8 taskId, const struct YesNoFuncTable *yesNoT CreateYesNoMenuWithCallbacks(taskId, &sWindowTemplates_MenuActions[MENU_WIN_YESNO], 1, 0, 2, 1, 0xE, yesNoTable); } -void DisplayItemMessageInBattlePyramid(u8 taskId, const u8 *str, void (*callback)(u8 taskId)) +void DisplayItemMessageInBattlePyramid(u8 taskId, const u8 *str, TaskFunc callback) { FillWindowPixelBuffer(WIN_MSG, PIXEL_FILL(1)); DisplayMessageAndContinueTask(taskId, WIN_MSG, 0xA, 0xD, FONT_NORMAL, GetPlayerTextSpeedDelay(), str, callback); diff --git a/src/fldeff_misc.c b/src/fldeff_misc.c index 1c361fb6f0..3adff9c81c 100644 --- a/src/fldeff_misc.c +++ b/src/fldeff_misc.c @@ -339,9 +339,9 @@ bool8 IsComputerScreenCloseEffectActive(void) #define tBlendCnt data[7] #define tBlendY data[8] -static void CreateComputerScreenEffectTask(void (*taskfunc) (u8), u16 increment, u16 unused, u8 priority) +static void CreateComputerScreenEffectTask(TaskFunc func, u16 increment, u16 unused, u8 priority) { - u8 taskId = CreateTask(taskfunc, priority); + u8 taskId = CreateTask(func, priority); gTasks[taskId].tState = 0; gTasks[taskId].tHorzIncrement = increment == 0 ? 16 : increment; diff --git a/src/item_menu.c b/src/item_menu.c index 8fe3b692d5..09bc25c90b 100755 --- a/src/item_menu.c +++ b/src/item_menu.c @@ -1159,7 +1159,7 @@ u8 GetItemListPosition(u8 pocketId) return gBagPosition.scrollPosition[pocketId] + gBagPosition.cursorPosition[pocketId]; } -void DisplayItemMessage(u8 taskId, u8 fontId, const u8 *str, void (*callback)(u8 taskId)) +void DisplayItemMessage(u8 taskId, u8 fontId, const u8 *str, TaskFunc callback) { s16 *data = gTasks[taskId].data; diff --git a/src/item_use.c b/src/item_use.c index 6ae235675a..b7bf458a99 100755 --- a/src/item_use.c +++ b/src/item_use.c @@ -72,7 +72,7 @@ static void SetDistanceOfClosestHiddenItem(u8, s16, s16); static void CB2_OpenPokeblockFromBag(void); // EWRAM variables -EWRAM_DATA static void(*sItemUseOnFieldCB)(u8 taskId) = NULL; +EWRAM_DATA static TaskFunc sItemUseOnFieldCB = NULL; // Below is set TRUE by UseRegisteredKeyItemOnField #define tUsingRegisteredKeyItem data[3] diff --git a/src/palette.c b/src/palette.c index f19ac07cf6..09a4b91ebd 100644 --- a/src/palette.c +++ b/src/palette.c @@ -956,7 +956,7 @@ void BlendPalettesGradually(u32 selectedPalettes, s8 delay, u8 coeff, u8 coeffTa { u8 taskId; - taskId = CreateTask((void *)Task_BlendPalettesGradually, priority); + taskId = CreateTask(Task_BlendPalettesGradually, priority); gTasks[taskId].tCoeff = coeff; gTasks[taskId].tCoeffTarget = coeffTarget; diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index a5886fd6a9..a77df48327 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -715,7 +715,7 @@ static void (*const sTextPrinterFunctions[])(void) = [PSS_PAGE_CONTEST_MOVES] = PrintContestMoves }; -static void (*const sTextPrinterTasks[])(u8 taskId) = +static const TaskFunc sTextPrinterTasks[] = { [PSS_PAGE_INFO] = Task_PrintInfoPage, [PSS_PAGE_SKILLS] = Task_PrintSkillsPage, From ddddca68e997fcc4b7568f46ab2f42228cfacef2 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Sun, 19 Oct 2025 07:42:59 +0200 Subject: [PATCH 040/130] Fix no_effect script command overwriting trainer data in trainer script (#7978) --- src/trainer_see.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/trainer_see.c b/src/trainer_see.c index 71237b4e29..0433e56071 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -429,7 +429,7 @@ bool8 CheckForTrainersWantingBattle(void) static u8 CheckTrainer(u8 objectEventId) { - const u8 *scriptPtr, *trainerBattlePtr; + const u8 *trainerBattlePtr; u8 numTrainers = 1; u8 approachDistance = GetTrainerApproachDistance(&gObjectEvents[objectEventId]); @@ -438,13 +438,13 @@ static u8 CheckTrainer(u8 objectEventId) if (InTrainerHill() == TRUE) { - trainerBattlePtr = scriptPtr = GetTrainerHillTrainerScript(); + trainerBattlePtr = GetTrainerHillTrainerScript(); } else { - trainerBattlePtr = scriptPtr = GetObjectEventScriptPointerByObjectEventId(objectEventId); + trainerBattlePtr = GetObjectEventScriptPointerByObjectEventId(objectEventId); struct ScriptContext ctx; - if (RunScriptImmediatelyUntilEffect(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE | SCREFF_TRAINERBATTLE, scriptPtr, &ctx)) + if (RunScriptImmediatelyUntilEffect(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE | SCREFF_TRAINERBATTLE, trainerBattlePtr, &ctx)) { if (*ctx.scriptPtr == 0x5c) // trainerbattle trainerBattlePtr = ctx.scriptPtr; @@ -492,7 +492,7 @@ static u8 CheckTrainer(u8 objectEventId) } gApproachingTrainers[gNoOfApproachingTrainers].objectEventId = objectEventId; - gApproachingTrainers[gNoOfApproachingTrainers].trainerScriptPtr = scriptPtr; + gApproachingTrainers[gNoOfApproachingTrainers].trainerScriptPtr = trainerBattlePtr; gApproachingTrainers[gNoOfApproachingTrainers].radius = approachDistance; InitTrainerApproachTask(&gObjectEvents[objectEventId], approachDistance - 1); gNoOfApproachingTrainers++; From 5b2ecfe4d4ecad99e6da73ac72e479e22efab678 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Sun, 19 Oct 2025 08:45:21 +0200 Subject: [PATCH 041/130] Setting wallclock time now properly sets fakeRTC (#7860) Co-authored-by: Bassoonian --- src/overworld.c | 4 ++++ src/rtc.c | 1 + src/wallclock.c | 10 +++++----- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/overworld.c b/src/overworld.c index e8be437519..d9d5543798 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1794,6 +1794,10 @@ void CB2_NewGame(void) SetFieldVBlankCallback(); SetMainCallback1(CB1_Overworld); SetMainCallback2(CB2_Overworld); +#if OW_USE_FAKE_RTC + // Wall clock now track local time so we set it to 10AM to match initial wall clock time + RtcCalcLocalTimeOffset(0, 10, 0, 0); +#endif } void CB2_WhiteOut(void) diff --git a/src/rtc.c b/src/rtc.c index 34f84183cf..fb2518f210 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -348,6 +348,7 @@ 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); RtcGetInfo(&sRtc); RtcCalcTimeDifference(&sRtc, &gSaveBlock2Ptr->localTimeOffset, &gLocalTime); } diff --git a/src/wallclock.c b/src/wallclock.c index 28c96fc972..bca008ac24 100644 --- a/src/wallclock.c +++ b/src/wallclock.c @@ -692,13 +692,13 @@ void CB2_StartWallClock(void) DecompressDataWithHeaderVram(gWallClockStart_Tilemap, (u16 *)BG_SCREEN_ADDR(7)); taskId = CreateTask(Task_SetClock_WaitFadeIn, 0); - gTasks[taskId].tHours = 10; - gTasks[taskId].tMinutes = 0; + gTasks[taskId].tHours = gLocalTime.hours; + gTasks[taskId].tMinutes = gLocalTime.minutes; gTasks[taskId].tMoveDir = 0; - gTasks[taskId].tPeriod = 0; + gTasks[taskId].tPeriod = gTasks[taskId].tHours / 12; gTasks[taskId].tMoveSpeed = 0; - gTasks[taskId].tMinuteHandAngle = 0; - gTasks[taskId].tHourHandAngle = 300; + gTasks[taskId].tMinuteHandAngle = gTasks[taskId].tMinutes * 6; + gTasks[taskId].tHourHandAngle = (gTasks[taskId].tHours % 12) * 30 + (gTasks[taskId].tMinutes / 10) * 5; spriteId = CreateSprite(&sSpriteTemplate_MinuteHand, 120, 80, 1); gSprites[spriteId].sTaskId = taskId; From 4816fe02c243e6192fbd12350c8c5977bedd3b31 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Sun, 19 Oct 2025 16:39:07 +0200 Subject: [PATCH 042/130] Bugfix hidefollower not waiting properly (#7768) --- asm/macros/event.inc | 2 +- data/script_cmd_table.inc | 1 + src/scrcmd.c | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 43b9417860..f8578a4a9c 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2482,7 +2482,7 @@ @ Hides any follower Pokémon if present, putting them into their Poké Ball; by default waits for their movement to finish. .macro hidefollower wait=1 - callnative ScrFunc_hidefollower + .byte SCR_OP_HIDEFOLLOWER .2byte \wait .endm diff --git a/data/script_cmd_table.inc b/data/script_cmd_table.inc index e05260d3d4..a3defef83e 100644 --- a/data/script_cmd_table.inc +++ b/data/script_cmd_table.inc @@ -251,6 +251,7 @@ gScriptCmdTable:: script_cmd_table_entry SCR_OP_BUFFERITEMNAMEPLURAL ScrCmd_bufferitemnameplural, requests_effects=1 @ 0xe2 script_cmd_table_entry SCR_OP_DYNMULTICHOICE ScrCmd_dynmultichoice, requests_effects=1 @ 0xe3 script_cmd_table_entry SCR_OP_DYNMULTIPUSH ScrCmd_dynmultipush, requests_effects=1 @ 0xe4 + script_cmd_table_entry SCR_OP_HIDEFOLLOWER ScrCmd_hidefollower, requests_effects=1 @ 0xe5 .if ALLOCATE_SCRIPT_CMD_TABLE gScriptCmdTableEnd:: diff --git a/src/scrcmd.c b/src/scrcmd.c index 767f41c6d5..54edd76c61 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -3154,11 +3154,13 @@ bool8 Scrcmd_getobjectfacingdirection(struct ScriptContext *ctx) return FALSE; } -bool8 ScrFunc_hidefollower(struct ScriptContext *ctx) +bool8 ScrCmd_hidefollower(struct ScriptContext *ctx) { bool16 wait = VarGet(ScriptReadHalfword(ctx)); struct ObjectEvent *obj; + Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); + if ((obj = ScriptHideFollower()) != NULL && wait) { sMovingNpcId = obj->localId; From 7fd0029ed7b9a245f866b6a7467f6d0bd7c0f717 Mon Sep 17 00:00:00 2001 From: DavidJCobb <831497+DavidJCobb@users.noreply.github.com> Date: Sun, 19 Oct 2025 18:37:13 +0200 Subject: [PATCH 043/130] Add typedefs for MAPSEC and METLOC values (#2183) Added typedefs: mapsec_t, metloc_t, and variants for MAPSEC and METLOC values. There are some rough edges that could do with smoothing out, but for now, this gets us close to ideal with a ROM that compares equal. Per feedback, all typedefs to mention the underlying type within the typedef name. The documentation comments reflect and explain the naming convention. Updated comments to reflect the fact that we're no longer using SET8 for a Pokemon's met locations, in favor of a new macro (added by this PR) that adjusts to match the width of whatever is being set. --- include/gametypes.h | 67 +++++++++++++++++++++++++ include/global.fieldmap.h | 2 +- include/global.h | 1 + include/global.tv.h | 14 +++--- include/landmark.h | 2 +- include/overworld.h | 4 +- include/pokemon.h | 2 +- include/pokenav.h | 6 +-- include/region_map.h | 14 +++--- src/data/region_map/region_map_layout.h | 2 +- src/daycare.c | 2 +- src/egg_hatch.c | 2 +- src/frontier_pass.c | 4 +- src/landmark.c | 8 +-- src/map_name_popup.c | 2 +- src/match_call.c | 4 +- src/overworld.c | 6 +-- src/pokedex_area_screen.c | 16 +++--- src/pokemon.c | 15 +++++- src/pokemon_summary_screen.c | 2 +- src/pokenav_match_call_data.c | 36 ++++++------- src/pokenav_match_call_gfx.c | 2 +- src/pokenav_match_call_list.c | 4 +- src/pokenav_region_map.c | 10 ++-- src/region_map.c | 58 ++++++++++----------- src/trade.c | 2 +- 26 files changed, 185 insertions(+), 102 deletions(-) create mode 100644 include/gametypes.h diff --git a/include/gametypes.h b/include/gametypes.h new file mode 100644 index 0000000000..242fc9d9db --- /dev/null +++ b/include/gametypes.h @@ -0,0 +1,67 @@ +#ifndef GUARD_GAMETYPES_H +#define GUARD_GAMETYPES_H + +#include "gba/types.h" + +// +// This header includes typedefs for fields that commonly appear throughout +// the codebase, and which ROM hacks might benefit from being able to widen. +// +// These typedefs include the underlying type in their name for two reasons: +// +// - Game Freak wasn't fully consistent about field widths throughout +// their codebase. For example, when Region Map Sections are persistently +// stored in savedata, they're stored as 8-bit values; but much of the +// codebase handles them as 16-bit values. +// +// - Although Pokemon Emerald doesn't come close to maxing out RAM, it *does* +// use nearly all of its EEPROM. That is: the vanilla game uses 96% of the +// flash memory available for storing players' save files, leaving 2172 +// bytes to spare within each of the game's two save files (primary and +// backup). These spare bytes are not contiguous: SaveBlock1 can only grow +// by 84 bytes, and SaveBlock2 can only grow by 120 bytes, with the rest +// of the free space located after the player's PC-boxed Pokemon. +// +// With so little flash memory to spare, keeping track of how much space +// you're using is vital -- and so is arranging struct members to minimize +// compiler-inserted padding. It's easier to deal with this when you can +// see these types' widths at a glance. +// +// Accordingly, this file generally doesn't contain just single types, but +// rather families of types. For example, Region Map Sections are saved as +// u8s within the player's save file, but are sometimes handled as u16s or +// even s16s and ints; and so there are multiple typedefs for Map Sections +// corresponding to each of these underlying types, and each typedef has a +// name which indicates the underlying type. +// +// For a given family of typedefs, the smallest one should be considered +// the "real" or "canonical" type. Continuing with Map Sections as our +// example, the smallest type is an 8-bit integer, and so any values that +// can't fit in an 8-bit integer will be truncated and lost at some point +// within the codebase. Therefore mapsec_u8_t is the "canonical" type for +// Map Sections, and the larger typedefs just exist to describe situations +// where the game handles Map Sections inconsistently with that "canon." +// + +// Map Sections are named areas that can appear in the region map. Each +// individual map can be assigned to a Map Section as appropriate. The +// possible values are in constants/region_map_sections.h. +// +// If you choose to widen Map Sections, be aware that Met Locations (below) +// are based on Map Sections and will also be widened. +typedef u8 mapsec_u8_t; +typedef u16 mapsec_u16_t; +typedef s16 mapsec_s16_t; +typedef s32 mapsec_s32_t; + +// Met Locations for caught Pokemon use the same values as Map Sections, +// except that 0xFD, 0xFE, and 0xFF have special meanings. +// +// Because this value appears inside every Pokemon's data, widening it will +// consume a lot more space within flash memory. The space usage will be +// greater than you expect due to how Pokemon substructs are laid out; you +// would have to rearrange the substructs' contents in order to minimize +// how much more space a wider Met Location would consume. +typedef mapsec_u8_t metloc_u8_t; + +#endif //GUARD_GAMETYPES_H diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index 1207bd80ea..90f2080d71 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -167,7 +167,7 @@ struct MapHeader /* 0x0C */ const struct MapConnections *connections; /* 0x10 */ u16 music; /* 0x12 */ u16 mapLayoutId; - /* 0x14 */ u8 regionMapSectionId; + /* 0x14 */ mapsec_u8_t regionMapSectionId; /* 0x15 */ u8 cave; /* 0x16 */ u8 weather; /* 0x17 */ u8 mapType; diff --git a/include/global.h b/include/global.h index f0882710e5..bdfc426fb9 100644 --- a/include/global.h +++ b/include/global.h @@ -5,6 +5,7 @@ #include #include "config.h" // we need to define config before gba headers as print stuff needs the functions nulled before defines. #include "gba/gba.h" +#include "gametypes.h" #include "constants/global.h" #include "constants/flags.h" #include "constants/vars.h" diff --git a/include/global.tv.h b/include/global.tv.h index 9c3902e7cc..a2721b06b4 100644 --- a/include/global.tv.h +++ b/include/global.tv.h @@ -226,7 +226,7 @@ typedef union // size = 0x24 /*0x04*/ u8 filler_04[2]; /*0x06*/ u16 itemIds[SMARTSHOPPER_NUM_ITEMS]; /*0x0C*/ u16 itemAmounts[SMARTSHOPPER_NUM_ITEMS]; - /*0x12*/ u8 shopLocation; + /*0x12*/ mapsec_u8_t shopLocation; /*0x13*/ u8 playerName[PLAYER_NAME_LENGTH + 1]; /*0x1B*/ //u8 padding; } smartshopperShow; @@ -241,7 +241,7 @@ typedef union // size = 0x24 /*0x0E*/ u16 species2; /*0x10*/ u8 nBallsUsed; /*0x11*/ u8 outcome; - /*0x12*/ u8 location; + /*0x12*/ mapsec_u8_t location; /*0x13*/ u8 playerName[PLAYER_NAME_LENGTH + 1]; /*0x1B*/ //u8 padding; } pokemonTodayFailed; @@ -267,7 +267,7 @@ typedef union // size = 0x24 /*0x04*/ u16 caughtPoke; /*0x06*/ u16 steps; /*0x08*/ u16 species; - /*0x0A*/ u8 location; + /*0x0A*/ mapsec_u8_t location; /*0x0B*/ u8 language; /*0x0C*/ u8 filler_0C[7]; /*0x13*/ u8 playerName[PLAYER_NAME_LENGTH + 1]; @@ -282,7 +282,7 @@ typedef union // size = 0x24 /*0x04*/ u8 badgeCount; /*0x05*/ u8 nSilverSymbols; /*0x06*/ u8 nGoldSymbols; - /*0x07*/ u8 location; + /*0x07*/ mapsec_u8_t location; /*0x08*/ u16 battlePoints; /*0x0A*/ u16 mapLayoutId; /*0x0C*/ u8 language; @@ -309,7 +309,7 @@ typedef union // size = 0x24 /*0x00*/ u8 kind; /*0x01*/ bool8 active; /*0x02*/ u16 item; - /*0x04*/ u8 location; + /*0x04*/ mapsec_u8_t location; /*0x05*/ u8 language; /*0x06*/ u16 mapLayoutId; /*0x08*/ u8 filler_08[11]; @@ -336,7 +336,7 @@ typedef union // size = 0x24 /*0x00*/ u8 kind; /*0x01*/ bool8 active; /*0x02*/ u16 lastOpponentSpecies; - /*0x04*/ u8 location; + /*0x04*/ mapsec_u8_t location; /*0x05*/ u8 outcome; /*0x06*/ u16 caughtMonBall; /*0x08*/ u16 balls; @@ -505,7 +505,7 @@ struct GabbyAndTyData /*2BA6*/ u16 mon2; /*2BA8*/ u16 lastMove; /*2BAA*/ u16 quote[1]; - /*2BAC*/ u8 mapnum; + /*2BAC*/ mapsec_u8_t mapnum; /*2BAD*/ u8 battleNum; /*2BAE*/ u8 battleTookMoreThanOneTurn:1; u8 playerLostAMon:1; diff --git a/include/landmark.h b/include/landmark.h index 395905033a..f5feb407bc 100644 --- a/include/landmark.h +++ b/include/landmark.h @@ -1,6 +1,6 @@ #ifndef GUARD_LANDMARK_H #define GUARD_LANDMARK_H -const u8 *GetLandmarkName(u8 mapSection, u8 id, u8 count); +const u8 *GetLandmarkName(mapsec_u8_t mapSection, u8 id, u8 count); #endif // GUARD_LANDMARK_H diff --git a/include/overworld.h b/include/overworld.h index bde6896569..8ac0ddc81e 100644 --- a/include/overworld.h +++ b/include/overworld.h @@ -121,8 +121,8 @@ u8 GetLastUsedWarpMapType(void); bool8 IsMapTypeOutdoors(u8 mapType); bool8 Overworld_MapTypeAllowsTeleportAndFly(u8 mapType); bool8 IsMapTypeIndoors(u8 mapType); -u8 GetSavedWarpRegionMapSectionId(void); -u8 GetCurrentRegionMapSectionId(void); +mapsec_u8_t GetSavedWarpRegionMapSectionId(void); +mapsec_u8_t GetCurrentRegionMapSectionId(void); u8 GetCurrentMapBattleScene(void); void CleanupOverworldWindowsAndTilemaps(void); bool32 IsOverworldLinkActive(void); diff --git a/include/pokemon.h b/include/pokemon.h index 53f7f02fd0..3d75157f91 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -131,7 +131,7 @@ struct PokemonSubstruct2 struct PokemonSubstruct3 { /* 0x00 */ u8 pokerus; - /* 0x01 */ u8 metLocation; + /* 0x01 */ metloc_u8_t metLocation; /* 0x02 */ u16 metLevel:7; /* 0x02 */ u16 metGame:4; diff --git a/include/pokenav.h b/include/pokenav.h index 553d2ad0f6..b548824234 100644 --- a/include/pokenav.h +++ b/include/pokenav.h @@ -17,7 +17,7 @@ struct PokenavMonListItem struct PokenavMatchCallEntry { bool8 isSpecialTrainer; - u8 mapSec; + mapsec_u8_t mapSec; u16 headerId; }; @@ -410,7 +410,7 @@ void FreeMatchCallSubstruct1(void); int IsMatchCallListInitFinished(void); int GetNumberRegistered(void); struct PokenavMatchCallEntry *GetMatchCallList(void); -u16 GetMatchCallMapSec(int index); +mapsec_u16_t GetMatchCallMapSec(int index); bool32 ShouldDrawRematchPokeballIcon(int index); void ClearRematchPokeballIcon(u16 windowId, u32 tileOffset); int GetMatchCallTrainerPic(int index); @@ -419,7 +419,7 @@ const u8 *GetMatchCallMessageText(int index, bool8 *newRematchRequest); u16 GetMatchCallOptionCursorPos(void); u16 GetMatchCallOptionId(int optionId); void BufferMatchCallNameAndDesc(struct PokenavMatchCallEntry *matchCallEntry, u8 *str); -u8 GetMatchTableMapSectionId(int rematchIndex); +mapsec_u8_t GetMatchTableMapSectionId(int rematchIndex); int GetIndexDeltaOfNextCheckPageDown(int index); int GetIndexDeltaOfNextCheckPageUp(int index); bool32 IsRematchEntryRegistered(int rematchIndex); diff --git a/include/region_map.h b/include/region_map.h index 2bca9e7f7d..abdb44b16c 100644 --- a/include/region_map.h +++ b/include/region_map.h @@ -26,7 +26,7 @@ enum { }; struct RegionMap { - /*0x000*/ u16 mapSecId; + /*0x000*/ mapsec_u16_t mapSecId; /*0x002*/ u8 mapSecType; /*0x003*/ u8 posWithinMapSec; /*0x004*/ u8 mapSecName[20]; @@ -99,14 +99,14 @@ void InitRegionMap(struct RegionMap *regionMap, bool8 zoomed); u8 DoRegionMapInputCallback(void); bool8 UpdateRegionMapZoom(void); void FreeRegionMapIconResources(void); -u16 GetRegionMapSecIdAt(u16 x, u16 y); +mapsec_u16_t GetRegionMapSecIdAt(u16 x, u16 y); void CreateRegionMapPlayerIcon(u16 tileTag, u16 paletteTag); void CreateRegionMapCursor(u16 tileTag, u16 paletteTag); -bool32 IsEventIslandMapSecId(u8 mapSecId); -u8 *GetMapName(u8 *dest, u16 regionMapId, u16 padLength); -u8 *GetMapNameGeneric(u8 *dest, u16 mapSecId); -u8 *GetMapNameHandleAquaHideout(u8 *dest, u16 mapSecId); -u16 CorrectSpecialMapSecId(u16 mapSecId); +bool32 IsEventIslandMapSecId(mapsec_u8_t mapSecId); +u8 *GetMapName(u8 *dest, mapsec_u16_t regionMapId, u16 padLength); +u8 *GetMapNameGeneric(u8 *dest, mapsec_u16_t mapSecId); +u8 *GetMapNameHandleAquaHideout(u8 *dest, mapsec_u16_t mapSecId); +mapsec_u16_t CorrectSpecialMapSecId(mapsec_u16_t mapSecId); void ShowRegionMapForPokedexAreaScreen(struct RegionMap *regionMap); void PokedexAreaScreen_UpdateRegionMapVariablesAndVideoRegs(s16 x, s16 y); void CB2_OpenFlyMap(void); diff --git a/src/data/region_map/region_map_layout.h b/src/data/region_map/region_map_layout.h index 95a4e94f31..32ce91724d 100644 --- a/src/data/region_map/region_map_layout.h +++ b/src/data/region_map/region_map_layout.h @@ -1,4 +1,4 @@ -static const u8 sRegionMap_MapSectionLayout[MAP_HEIGHT][MAP_WIDTH] = { +static const mapsec_u8_t sRegionMap_MapSectionLayout[MAP_HEIGHT][MAP_WIDTH] = { {MAPSEC_NONE, MAPSEC_ROUTE_114, MAPSEC_ROUTE_114, MAPSEC_FALLARBOR_TOWN, MAPSEC_ROUTE_113, MAPSEC_ROUTE_113, MAPSEC_ROUTE_113, MAPSEC_ROUTE_113, MAPSEC_ROUTE_111, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_ROUTE_119, MAPSEC_FORTREE_CITY, MAPSEC_ROUTE_120, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE}, {MAPSEC_NONE, MAPSEC_ROUTE_114, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_MT_CHIMNEY, MAPSEC_MT_CHIMNEY, MAPSEC_ROUTE_111, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_ROUTE_119, MAPSEC_NONE, MAPSEC_ROUTE_120, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE}, {MAPSEC_ROUTE_115, MAPSEC_ROUTE_114, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_MT_CHIMNEY, MAPSEC_MT_CHIMNEY, MAPSEC_ROUTE_111, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_ROUTE_119, MAPSEC_NONE, MAPSEC_ROUTE_120, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_SAFARI_ZONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE, MAPSEC_NONE}, diff --git a/src/daycare.c b/src/daycare.c index 5ebe255b39..eb9a5fdeb0 100644 --- a/src/daycare.c +++ b/src/daycare.c @@ -830,7 +830,7 @@ void CreateEgg(struct Pokemon *mon, u16 species, bool8 setHotSpringsLocation) u8 metLevel; u16 ball; u8 language; - u8 metLocation; + metloc_u8_t metLocation; u8 isEgg; CreateMon(mon, species, EGG_HATCH_LEVEL, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0); diff --git a/src/egg_hatch.c b/src/egg_hatch.c index 5aa955d7e9..00a194ea70 100644 --- a/src/egg_hatch.c +++ b/src/egg_hatch.c @@ -362,7 +362,7 @@ static void AddHatchedMonToParty(u8 id) u8 name[POKEMON_NAME_LENGTH + 1]; u16 ball; u16 metLevel; - u8 metLocation; + metloc_u8_t metLocation; struct Pokemon *mon = &gPlayerParty[id]; CreateHatchedMon(mon, &gEnemyParty[0]); diff --git a/src/frontier_pass.c b/src/frontier_pass.c index a053badc62..fb6920e3cc 100644 --- a/src/frontier_pass.c +++ b/src/frontier_pass.c @@ -606,7 +606,9 @@ static void LeaveFrontierPass(void) static u32 AllocateFrontierPassData(MainCallback callback) { - u8 i; + // This variable is a MAPSEC initially, but is recycled as a + // bare integer near the end of the function. + mapsec_u8_t i; if (sPassData != NULL) return ERR_ALREADY_DONE; diff --git a/src/landmark.c b/src/landmark.c index d2bb3c4105..a8cf6c1233 100644 --- a/src/landmark.c +++ b/src/landmark.c @@ -10,7 +10,7 @@ struct Landmark struct LandmarkList { - u8 mapSection; + mapsec_u8_t mapSection; u8 id; const struct Landmark *const *landmarks; }; @@ -392,9 +392,9 @@ static const struct LandmarkList sLandmarkLists[] = {MAPSEC_NONE, 0, NULL}, }; -static const struct Landmark *const *GetLandmarks(u8 mapSection, u8 id); +static const struct Landmark *const *GetLandmarks(mapsec_u8_t mapSection, u8 id); -const u8 *GetLandmarkName(u8 mapSection, u8 id, u8 count) +const u8 *GetLandmarkName(mapsec_u8_t mapSection, u8 id, u8 count) { const struct Landmark *const *landmarks = GetLandmarks(mapSection, id); @@ -421,7 +421,7 @@ const u8 *GetLandmarkName(u8 mapSection, u8 id, u8 count) return (*landmarks)->name; } -static const struct Landmark *const *GetLandmarks(u8 mapSection, u8 id) +static const struct Landmark *const *GetLandmarks(mapsec_u8_t mapSection, u8 id) { u16 i = 0; diff --git a/src/map_name_popup.c b/src/map_name_popup.c index 4b30068f3c..665cccec3c 100644 --- a/src/map_name_popup.c +++ b/src/map_name_popup.c @@ -404,7 +404,7 @@ static void LoadMapNamePopUpWindowBg(void) { u8 popUpThemeId; u8 popupWindowId = GetMapNamePopUpWindowId(); - u16 regionMapSectionId = gMapHeader.regionMapSectionId; + mapsec_u16_t regionMapSectionId = gMapHeader.regionMapSectionId; if (regionMapSectionId >= KANTO_MAPSEC_START) { diff --git a/src/match_call.c b/src/match_call.c index e6a141a1bc..41188f51bb 100644 --- a/src/match_call.c +++ b/src/match_call.c @@ -134,7 +134,7 @@ static u32 GetCurrentTotalMinutes(struct Time *); static u32 GetNumRegisteredTrainers(void); static u32 GetActiveMatchCallTrainerId(u32); static int GetTrainerMatchCallId(int); -static u16 GetRematchTrainerLocation(int); +static mapsec_u16_t GetRematchTrainerLocation(int); static bool32 TrainerIsEligibleForRematch(int); static void StartMatchCall(void); static void ExecuteMatchCall(u8); @@ -1463,7 +1463,7 @@ static bool32 TrainerIsEligibleForRematch(int matchCallId) return gSaveBlock1Ptr->trainerRematches[matchCallId] > 0; } -static u16 GetRematchTrainerLocation(int matchCallId) +static mapsec_u16_t GetRematchTrainerLocation(int matchCallId) { const struct MapHeader *mapHeader = Overworld_GetMapHeaderByGroupAndId(gRematchTable[matchCallId].mapGroup, gRematchTable[matchCallId].mapNum); return mapHeader->regionMapSectionId; diff --git a/src/overworld.c b/src/overworld.c index 32c5eb0678..d1d3ddcd37 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -194,7 +194,7 @@ EWRAM_DATA struct WarpData gLastUsedWarp = {0}; EWRAM_DATA static struct WarpData sWarpDestination = {0}; // new warp position EWRAM_DATA static struct WarpData sFixedDiveWarp = {0}; EWRAM_DATA static struct WarpData sFixedHoleWarp = {0}; -EWRAM_DATA static u16 sLastMapSectionId = 0; +EWRAM_DATA static mapsec_u16_t sLastMapSectionId = 0; EWRAM_DATA static struct InitialPlayerAvatarState sInitialPlayerAvatarState = {0}; EWRAM_DATA static u16 sAmbientCrySpecies = 0; EWRAM_DATA static bool8 sIsAmbientCryWaterMon = FALSE; @@ -1383,12 +1383,12 @@ bool8 IsMapTypeIndoors(u8 mapType) return FALSE; } -u8 GetSavedWarpRegionMapSectionId(void) +mapsec_u8_t GetSavedWarpRegionMapSectionId(void) { return Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->dynamicWarp.mapGroup, gSaveBlock1Ptr->dynamicWarp.mapNum)->regionMapSectionId; } -u8 GetCurrentRegionMapSectionId(void) +mapsec_u8_t GetCurrentRegionMapSectionId(void) { return Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->regionMapSectionId; } diff --git a/src/pokedex_area_screen.c b/src/pokedex_area_screen.c index 8b0a845102..c3211ed454 100755 --- a/src/pokedex_area_screen.c +++ b/src/pokedex_area_screen.c @@ -58,7 +58,7 @@ struct OverworldArea { u8 mapGroup; u8 mapNum; - u16 regionMapSectionId; + mapsec_u16_t regionMapSectionId; }; struct @@ -79,7 +79,7 @@ struct /*0x61C*/ u16 areaShadeBldArgHi; /*0x61E*/ bool8 showingMarkers; /*0x61F*/ u8 markerFlashCounter; - /*0x620*/ u16 specialAreaRegionMapSectionIds[MAX_AREA_MARKERS]; + /*0x620*/ mapsec_u16_t specialAreaRegionMapSectionIds[MAX_AREA_MARKERS]; /*0x660*/ struct Sprite *areaMarkerSprites[MAX_AREA_MARKERS]; /*0x6E0*/ u16 numAreaMarkerSprites; /*0x6E2*/ u16 alteringCaveCounter; @@ -95,7 +95,7 @@ static void FindMapsWithMon(u16); static void BuildAreaGlowTilemap(void); static void SetAreaHasMon(u16, u16); static void SetSpecialMapHasMon(u16, u16); -static u16 GetRegionMapSectionId(u8, u8); +static mapsec_u16_t GetRegionMapSectionId(u8, u8); static bool8 MapHasSpecies(const struct WildPokemonHeader *, u16); static bool8 MonListHasSpecies(const struct WildPokemonInfo *, u16, u16); static void DoAreaGlow(void); @@ -112,7 +112,7 @@ static const u32 sAreaGlow_Gfx[] = INCBIN_U32("graphics/pokedex/area_glow.4bpp.l static const u16 sSpeciesHiddenFromAreaScreen[] = { SPECIES_WYNAUT }; -static const u16 sMovingRegionMapSections[3] = +static const mapsec_u16_t sMovingRegionMapSections[3] = { MAPSEC_MARINE_CAVE, MAPSEC_UNDERWATER_MARINE_CAVE, @@ -125,7 +125,7 @@ static const u16 sFeebasData[][3] = {NUM_SPECIES} }; -static const u16 sLandmarkData[][2] = +static const mapsec_u16_t sLandmarkData[][2] = { {MAPSEC_SKY_PILLAR, FLAG_LANDMARK_SKY_PILLAR}, {MAPSEC_SEAFLOOR_CAVERN, FLAG_LANDMARK_SEAFLOOR_CAVERN}, @@ -336,7 +336,7 @@ static void SetSpecialMapHasMon(u16 mapGroup, u16 mapNum) if (sPokedexAreaScreen->numSpecialAreas < MAX_AREA_MARKERS) { - u16 regionMapSectionId = GetRegionMapSectionId(mapGroup, mapNum); + mapsec_u16_t regionMapSectionId = GetRegionMapSectionId(mapGroup, mapNum); if (regionMapSectionId < MAPSEC_NONE) { // Don't highlight the area if it's a moving area (Marine/Terra Cave) @@ -370,7 +370,7 @@ static void SetSpecialMapHasMon(u16 mapGroup, u16 mapNum) } } -static u16 GetRegionMapSectionId(u8 mapGroup, u8 mapNum) +static mapsec_u16_t GetRegionMapSectionId(u8 mapGroup, u8 mapNum) { return Overworld_GetMapHeaderByGroupAndId(mapGroup, mapNum)->regionMapSectionId; } @@ -710,7 +710,7 @@ static void CreateAreaMarkerSprites(void) static s16 x; static s16 y; static s16 i; - static s16 mapSecId; + static mapsec_s16_t mapSecId; static s16 numSprites; LoadSpriteSheet(&sAreaMarkerSpriteSheet); diff --git a/src/pokemon.c b/src/pokemon.c index a06693dc8a..7283e0e456 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -4074,6 +4074,19 @@ u32 GetBoxMonData2(struct BoxPokemon *boxMon, s32 field) __attribute__((alias("G #define SET8(lhs) (lhs) = *data #define SET16(lhs) (lhs) = data[0] + (data[1] << 8) #define SET32(lhs) (lhs) = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24) +// +// Prefer SET_BY_WIDTH for fields whose types might be extended (e.g. +// anything whose typedef is in gametypes.h). +// +#define SET_BY_WIDTH(lhs) \ + do { \ + if (sizeof(lhs) == 1) \ + SET8(lhs); \ + else if (sizeof(lhs) == 2) \ + SET16(lhs); \ + else if (sizeof(lhs) == 4) \ + SET32(lhs); \ + } while (0) void SetMonData(struct Pokemon *mon, s32 field, const void *dataArg) { @@ -4263,7 +4276,7 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) SET8(substruct3->pokerus); break; case MON_DATA_MET_LOCATION: - SET8(substruct3->metLocation); + SET_BY_WIDTH(substruct3->metLocation); break; case MON_DATA_MET_LEVEL: { diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index a77df48327..734d925358 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -143,7 +143,7 @@ static EWRAM_DATA struct PokemonSummaryScreenData u8 ribbonCount; // 0x6 u8 ailment; // 0x7 u8 abilityNum; // 0x8 - u8 metLocation; // 0x9 + metloc_u8_t metLocation; // 0x9 u8 metLevel; // 0xA u8 metGame; // 0xB u32 pid; // 0xC diff --git a/src/pokenav_match_call_data.c b/src/pokenav_match_call_data.c index 1c3cf9aeeb..3f47550f17 100644 --- a/src/pokenav_match_call_data.c +++ b/src/pokenav_match_call_data.c @@ -34,13 +34,13 @@ typedef struct MatchCallTextDataStruct { struct MatchCallStructCommon { u8 type; - u8 mapSec; + mapsec_u8_t mapSec; u16 flag; }; struct MatchCallStructNPC { u8 type; - u8 mapSec; + mapsec_u8_t mapSec; u16 flag; const u8 *desc; const u8 *name; @@ -50,7 +50,7 @@ struct MatchCallStructNPC { // Shared by MC_TYPE_TRAINER and MC_TYPE_LEADER struct MatchCallStructTrainer { u8 type; - u8 mapSec; + mapsec_u8_t mapSec; u16 flag; u16 rematchTableIdx; const u8 *desc; @@ -60,12 +60,12 @@ struct MatchCallStructTrainer { struct MatchCallLocationOverride { u16 flag; - u8 mapSec; + mapsec_u8_t mapSec; }; struct MatchCallWally { u8 type; - u8 mapSec; + mapsec_u8_t mapSec; u16 flag; u16 rematchTableIdx; const u8 *desc; @@ -75,7 +75,7 @@ struct MatchCallWally { struct MatchCallBirch { u8 type; - u8 mapSec; + mapsec_u8_t mapSec; u16 flag; const u8 *desc; const u8 *name; @@ -117,11 +117,11 @@ static bool32 MatchCall_GetEnabled_Wally(match_call_t); static bool32 MatchCall_GetEnabled_Birch(match_call_t); static bool32 MatchCall_GetEnabled_Rival(match_call_t); -static u8 MatchCall_GetMapSec_NPC(match_call_t); -static u8 MatchCall_GetMapSec_Trainer(match_call_t); -static u8 MatchCall_GetMapSec_Wally(match_call_t); -static u8 MatchCall_GetMapSec_Birch(match_call_t); -static u8 MatchCall_GetMapSec_Rival(match_call_t); +static mapsec_u8_t MatchCall_GetMapSec_NPC(match_call_t); +static mapsec_u8_t MatchCall_GetMapSec_Trainer(match_call_t); +static mapsec_u8_t MatchCall_GetMapSec_Wally(match_call_t); +static mapsec_u8_t MatchCall_GetMapSec_Birch(match_call_t); +static mapsec_u8_t MatchCall_GetMapSec_Rival(match_call_t); static bool32 MatchCall_IsRematchable_NPC(match_call_t); static bool32 MatchCall_IsRematchable_Trainer(match_call_t); @@ -609,7 +609,7 @@ static bool32 (*const sMatchCallGetEnabledFuncs[])(match_call_t) = { MatchCall_GetEnabled_Birch }; -static u8 (*const sMatchCallGetMapSecFuncs[])(match_call_t) = { +static mapsec_u8_t (*const sMatchCallGetMapSecFuncs[])(match_call_t) = { MatchCall_GetMapSec_NPC, MatchCall_GetMapSec_Trainer, MatchCall_GetMapSec_Wally, @@ -779,7 +779,7 @@ static bool32 MatchCall_GetEnabled_Birch(match_call_t matchCall) return FlagGet(matchCall.birch->flag); } -u8 MatchCall_GetMapSec(u32 idx) +mapsec_u8_t MatchCall_GetMapSec(u32 idx) { match_call_t matchCall; u32 i; @@ -791,17 +791,17 @@ u8 MatchCall_GetMapSec(u32 idx) return sMatchCallGetMapSecFuncs[i](matchCall); } -static u8 MatchCall_GetMapSec_NPC(match_call_t matchCall) +static mapsec_u8_t MatchCall_GetMapSec_NPC(match_call_t matchCall) { return matchCall.npc->mapSec; } -static u8 MatchCall_GetMapSec_Trainer(match_call_t matchCall) +static mapsec_u8_t MatchCall_GetMapSec_Trainer(match_call_t matchCall) { return matchCall.trainer->mapSec; } -static u8 MatchCall_GetMapSec_Wally(match_call_t matchCall) +static mapsec_u8_t MatchCall_GetMapSec_Wally(match_call_t matchCall) { s32 i; @@ -813,12 +813,12 @@ static u8 MatchCall_GetMapSec_Wally(match_call_t matchCall) return matchCall.wally->locationData[i].mapSec; } -static u8 MatchCall_GetMapSec_Rival(match_call_t matchCall) +static mapsec_u8_t MatchCall_GetMapSec_Rival(match_call_t matchCall) { return MAPSEC_NONE; } -static u8 MatchCall_GetMapSec_Birch(match_call_t matchCall) +static mapsec_u8_t MatchCall_GetMapSec_Birch(match_call_t matchCall) { return MAPSEC_NONE; } diff --git a/src/pokenav_match_call_gfx.c b/src/pokenav_match_call_gfx.c index 4271e2ff23..dde725d8bd 100755 --- a/src/pokenav_match_call_gfx.c +++ b/src/pokenav_match_call_gfx.c @@ -1022,7 +1022,7 @@ static void PrintMatchCallLocation(struct Pokenav_MatchCallGfx *gfx, int delta) u8 mapName[32]; int x; int index = PokenavList_GetSelectedIndex() + delta; - int mapSec = GetMatchCallMapSec(index); + mapsec_s32_t mapSec = GetMatchCallMapSec(index); if (mapSec != MAPSEC_NONE) GetMapName(mapName, mapSec, 0); else diff --git a/src/pokenav_match_call_list.c b/src/pokenav_match_call_list.c index fb89e33e0b..8f8fc6b4c2 100755 --- a/src/pokenav_match_call_list.c +++ b/src/pokenav_match_call_list.c @@ -306,7 +306,7 @@ struct PokenavMatchCallEntry *GetMatchCallList(void) return state->matchCallEntries; } -u16 GetMatchCallMapSec(int index) +mapsec_u16_t GetMatchCallMapSec(int index) { struct Pokenav_MatchCallMenu *state = GetSubstructPtr(POKENAV_SUBSTRUCT_MATCH_CALL_MAIN); return state->matchCallEntries[index].mapSec; @@ -424,7 +424,7 @@ void BufferMatchCallNameAndDesc(struct PokenavMatchCallEntry *matchCallEntry, u8 } } -u8 GetMatchTableMapSectionId(int rematchIndex) +mapsec_u8_t GetMatchTableMapSectionId(int rematchIndex) { int mapGroup = gRematchTable[rematchIndex].mapGroup; int mapNum = gRematchTable[rematchIndex].mapNum; diff --git a/src/pokenav_region_map.c b/src/pokenav_region_map.c index f81ff42966..8475bbacb8 100755 --- a/src/pokenav_region_map.c +++ b/src/pokenav_region_map.c @@ -41,7 +41,7 @@ struct Pokenav_RegionMapGfx struct CityMapEntry { - u16 mapSecId; + mapsec_u16_t mapSecId; u16 index; const u32 *tilemap; }; @@ -63,8 +63,8 @@ static bool32 IsDma3ManagerBusyWithBgCopy_(struct Pokenav_RegionMapGfx *); static void ChangeBgYForZoom(bool32); static bool32 IsChangeBgYForZoomActive(void); static void CreateCityZoomTextSprites(void); -static void DrawCityMap(struct Pokenav_RegionMapGfx *, int, int); -static void PrintLandmarkNames(struct Pokenav_RegionMapGfx *, int, int); +static void DrawCityMap(struct Pokenav_RegionMapGfx *, mapsec_s32_t, int); +static void PrintLandmarkNames(struct Pokenav_RegionMapGfx *, mapsec_s32_t, int); static void SetCityZoomTextInvisibility(bool32); static void Task_ChangeBgYForZoom(u8 taskId); static void UpdateCityZoomTextPosition(void); @@ -634,7 +634,7 @@ static u32 LoopedTask_DecompressCityMaps(s32 taskState) return LT_FINISH; } -static void DrawCityMap(struct Pokenav_RegionMapGfx *state, int mapSecId, int pos) +static void DrawCityMap(struct Pokenav_RegionMapGfx *state, mapsec_s32_t mapSecId, int pos) { int i; for (i = 0; i < NUM_CITY_MAPS && (sPokenavCityMaps[i].mapSecId != mapSecId || sPokenavCityMaps[i].index != pos); i++) @@ -647,7 +647,7 @@ static void DrawCityMap(struct Pokenav_RegionMapGfx *state, int mapSecId, int po CopyToBgTilemapBufferRect(1, state->cityZoomPics[i], 18, 6, 10, 10); } -static void PrintLandmarkNames(struct Pokenav_RegionMapGfx *state, int mapSecId, int pos) +static void PrintLandmarkNames(struct Pokenav_RegionMapGfx *state, mapsec_s32_t mapSecId, int pos) { int i = 0; while (1) diff --git a/src/region_map.c b/src/region_map.c index 22a3f567b4..a418b7fe4f 100644 --- a/src/region_map.c +++ b/src/region_map.c @@ -63,7 +63,7 @@ enum { struct MultiNameFlyDest { const u8 *const *name; - u16 mapSecId; + mapsec_u16_t mapSecId; u16 flag; }; @@ -72,7 +72,7 @@ static EWRAM_DATA struct RegionMap *sRegionMap = NULL; static EWRAM_DATA struct { void (*callback)(void); u16 state; - u16 mapSecId; + mapsec_u16_t mapSecId; struct RegionMap regionMap; u8 tileBuffer[0x1c0]; u8 nameBuffer[0x26]; // never read @@ -86,15 +86,15 @@ static u8 MoveRegionMapCursor_Full(void); static u8 ProcessRegionMapInput_Zoomed(void); static u8 MoveRegionMapCursor_Zoomed(void); static void CalcZoomScrollParams(s16 scrollX, s16 scrollY, s16 c, s16 d, u16 e, u16 f, u8 rotation); -static u16 GetMapSecIdAt(u16 x, u16 y); +static mapsec_u16_t GetMapSecIdAt(u16 x, u16 y); static void RegionMap_SetBG2XAndBG2Y(s16 x, s16 y); static void InitMapBasedOnPlayerLocation(void); static void RegionMap_InitializeStateBasedOnSSTidalLocation(void); -static u8 GetMapsecType(u16 mapSecId); -static u16 CorrectSpecialMapSecId_Internal(u16 mapSecId); -static u16 GetTerraOrMarineCaveMapSecId(void); +static u8 GetMapsecType(mapsec_u16_t mapSecId); +static mapsec_u16_t CorrectSpecialMapSecId_Internal(mapsec_u16_t mapSecId); +static mapsec_u16_t GetTerraOrMarineCaveMapSecId(void); static void GetMarineCaveCoords(u16 *x, u16 *y); -static bool32 IsPlayerInAquaHideout(u8 mapSecId); +static bool32 IsPlayerInAquaHideout(mapsec_u8_t mapSecId); static void GetPositionOfCursorWithinMapSec(void); static bool8 RegionMap_IsMapSecIdInNextRow(u16 y); static void SpriteCB_CursorMapFull(struct Sprite *sprite); @@ -130,7 +130,7 @@ static const u8 sRegionMapPlayerIcon_MayGfx[] = INCBIN_U8("graphics/pokenav/regi #include "data/region_map/region_map_layout.h" #include "data/region_map/region_map_entries.h" -static const u16 sRegionMap_SpecialPlaceLocations[][2] = +static const mapsec_u16_t sRegionMap_SpecialPlaceLocations[][2] = { {MAPSEC_UNDERWATER_105, MAPSEC_ROUTE_105}, {MAPSEC_UNDERWATER_124, MAPSEC_ROUTE_124}, @@ -162,14 +162,14 @@ static const u16 sRegionMap_SpecialPlaceLocations[][2] = {MAPSEC_NONE, MAPSEC_NONE} }; -static const u16 sMarineCaveMapSecIds[] = +static const mapsec_u16_t sMarineCaveMapSecIds[] = { MAPSEC_MARINE_CAVE, MAPSEC_UNDERWATER_MARINE_CAVE, MAPSEC_UNDERWATER_MARINE_CAVE }; -static const u16 sTerraOrMarineCaveMapSecIds[ABNORMAL_WEATHER_LOCATIONS] = +static const mapsec_u16_t sTerraOrMarineCaveMapSecIds[ABNORMAL_WEATHER_LOCATIONS] = { [ABNORMAL_WEATHER_ROUTE_114_NORTH - 1] = MAPSEC_ROUTE_114, [ABNORMAL_WEATHER_ROUTE_114_SOUTH - 1] = MAPSEC_ROUTE_114, @@ -203,7 +203,7 @@ static const struct UCoords16 sMarineCaveLocationCoords[MARINE_CAVE_LOCATIONS] = [MARINE_CAVE_COORD(ROUTE_129_EAST)] = {24, 10} }; -static const u8 sMapSecAquaHideoutOld[] = +static const mapsec_u8_t sMapSecAquaHideoutOld[] = { MAPSEC_AQUA_HIDEOUT_OLD }; @@ -273,7 +273,7 @@ static const union AnimCmd *const sRegionMapPlayerIconAnimTable[] = }; // Event islands that don't appear on map. (Southern Island does) -static const u8 sMapSecIdsOffMap[] = +static const mapsec_u8_t sMapSecIdsOffMap[] = { MAPSEC_BIRTH_ISLAND, MAPSEC_FARAWAY_ISLAND, @@ -421,7 +421,7 @@ static const struct SpritePalette sFlyTargetIconsSpritePalette = .tag = TAG_FLY_ICON }; -static const u16 sRedOutlineFlyDestinations[][2] = +static const mapsec_u16_t sRedOutlineFlyDestinations[][2] = { { FLAG_LANDMARK_BATTLE_FRONTIER, @@ -690,7 +690,7 @@ static u8 ProcessRegionMapInput_Full(void) static u8 MoveRegionMapCursor_Full(void) { - u16 mapSecId; + mapsec_u16_t mapSecId; if (sRegionMap->cursorMovementFrameCounter != 0) return MAP_INPUT_MOVE_CONT; @@ -771,7 +771,7 @@ static u8 MoveRegionMapCursor_Zoomed(void) { u16 x; u16 y; - u16 mapSecId; + mapsec_u16_t mapSecId; sRegionMap->scrollY += sRegionMap->zoomedCursorDeltaY; sRegionMap->scrollX += sRegionMap->zoomedCursorDeltaX; @@ -954,7 +954,7 @@ void PokedexAreaScreen_UpdateRegionMapVariablesAndVideoRegs(s16 x, s16 y) } } -static u16 GetMapSecIdAt(u16 x, u16 y) +static mapsec_u16_t GetMapSecIdAt(u16 x, u16 y) { if (y < MAPCURSOR_Y_MIN || y > MAPCURSOR_Y_MAX || x < MAPCURSOR_X_MIN || x > MAPCURSOR_X_MAX) { @@ -1172,7 +1172,7 @@ static void RegionMap_InitializeStateBasedOnSSTidalLocation(void) sRegionMap->cursorPosY = gRegionMapEntries[sRegionMap->mapSecId].y + y + MAPCURSOR_Y_MIN; } -static u8 GetMapsecType(u16 mapSecId) +static u8 GetMapsecType(mapsec_u16_t mapSecId) { switch (mapSecId) { @@ -1219,12 +1219,12 @@ static u8 GetMapsecType(u16 mapSecId) } } -u16 GetRegionMapSecIdAt(u16 x, u16 y) +mapsec_u16_t GetRegionMapSecIdAt(u16 x, u16 y) { return GetMapSecIdAt(x, y); } -static u16 CorrectSpecialMapSecId_Internal(u16 mapSecId) +static mapsec_u16_t CorrectSpecialMapSecId_Internal(mapsec_u16_t mapSecId) { u32 i; @@ -1245,7 +1245,7 @@ static u16 CorrectSpecialMapSecId_Internal(u16 mapSecId) return mapSecId; } -static u16 GetTerraOrMarineCaveMapSecId(void) +static mapsec_u16_t GetTerraOrMarineCaveMapSecId(void) { s16 idx; @@ -1274,7 +1274,7 @@ static void GetMarineCaveCoords(u16 *x, u16 *y) // Probably meant to be an "IsPlayerInIndoorDungeon" function, but in practice it only has the one mapsec // Additionally, because the mapsec doesnt exist in Emerald, this function always returns FALSE -static bool32 IsPlayerInAquaHideout(u8 mapSecId) +static bool32 IsPlayerInAquaHideout(mapsec_u8_t mapSecId) { u32 i; @@ -1286,7 +1286,7 @@ static bool32 IsPlayerInAquaHideout(u8 mapSecId) return FALSE; } -u16 CorrectSpecialMapSecId(u16 mapSecId) +mapsec_u16_t CorrectSpecialMapSecId(mapsec_u16_t mapSecId) { return CorrectSpecialMapSecId_Internal(mapSecId); } @@ -1565,7 +1565,7 @@ void TrySetPlayerIconBlink(void) #undef sVisible #undef sTimer -u8 *GetMapName(u8 *dest, u16 regionMapId, u16 padLength) +u8 *GetMapName(u8 *dest, mapsec_u16_t regionMapId, u16 padLength) { u8 *str; u16 i; @@ -1598,7 +1598,7 @@ u8 *GetMapName(u8 *dest, u16 regionMapId, u16 padLength) } // TODO: probably needs a better name -u8 *GetMapNameGeneric(u8 *dest, u16 mapSecId) +u8 *GetMapNameGeneric(u8 *dest, mapsec_u16_t mapSecId) { switch (mapSecId) { @@ -1611,7 +1611,7 @@ u8 *GetMapNameGeneric(u8 *dest, u16 mapSecId) } } -u8 *GetMapNameHandleAquaHideout(u8 *dest, u16 mapSecId) +u8 *GetMapNameHandleAquaHideout(u8 *dest, mapsec_u16_t mapSecId) { if (mapSecId == MAPSEC_AQUA_HIDEOUT_OLD) return StringCopy(dest, gText_Hideout); @@ -1619,7 +1619,7 @@ u8 *GetMapNameHandleAquaHideout(u8 *dest, u16 mapSecId) return GetMapNameGeneric(dest, mapSecId); } -static void GetMapSecDimensions(u16 mapSecId, u16 *x, u16 *y, u16 *width, u16 *height) +static void GetMapSecDimensions(mapsec_u16_t mapSecId, u16 *x, u16 *y, u16 *width, u16 *height) { *x = gRegionMapEntries[mapSecId].x; *y = gRegionMapEntries[mapSecId].y; @@ -1632,7 +1632,7 @@ bool8 IsRegionMapZoomed(void) return sRegionMap->zoomed; } -bool32 IsEventIslandMapSecId(u8 mapSecId) +bool32 IsEventIslandMapSecId(mapsec_u8_t mapSecId) { u32 i; @@ -1839,7 +1839,7 @@ static void LoadFlyDestIcons(void) static void CreateFlyDestIcons(void) { u16 canFlyFlag; - u16 mapSecId; + mapsec_u16_t mapSecId; u16 x; u16 y; u16 width; @@ -1887,7 +1887,7 @@ static void TryCreateRedOutlineFlyDestIcons(void) u16 y; u16 width; u16 height; - u16 mapSecId; + mapsec_u16_t mapSecId; u8 spriteId; for (i = 0; sRedOutlineFlyDestinations[i][1] != MAPSEC_NONE; i++) diff --git a/src/trade.c b/src/trade.c index e07e418d1a..e9c2984087 100644 --- a/src/trade.c +++ b/src/trade.c @@ -4552,7 +4552,7 @@ static void CreateInGameTradePokemonInternal(u8 whichPlayerMon, u8 whichInGameTr u8 level = GetMonData(&gPlayerParty[whichPlayerMon], MON_DATA_LEVEL); struct Mail mail; - u8 metLocation = METLOC_IN_GAME_TRADE; + metloc_u8_t metLocation = METLOC_IN_GAME_TRADE; u8 mailNum; struct Pokemon *pokemon = &gEnemyParty[0]; From fa5ca10b17ee602f8f078e6ff597e990125aef66 Mon Sep 17 00:00:00 2001 From: Josh <32826900+ShinyDragonHunter@users.noreply.github.com> Date: Sun, 19 Oct 2025 18:32:19 +0100 Subject: [PATCH 044/130] Add spritesheet rules for healthbox graphics (#2180) --- .../healthbox_doubles_opponent.png | Bin 284 -> 277 bytes .../healthbox_doubles_player.png | Bin 293 -> 282 bytes graphics/battle_interface/healthbox_safari.png | Bin 297 -> 284 bytes .../healthbox_singles_opponent.png | Bin 296 -> 278 bytes .../healthbox_singles_player.png | Bin 336 -> 328 bytes spritesheet_rules.mk | 16 ++++++++++++++++ 6 files changed, 16 insertions(+) diff --git a/graphics/battle_interface/healthbox_doubles_opponent.png b/graphics/battle_interface/healthbox_doubles_opponent.png index def9d07af0000520355bbd783c6ba2f27ff1fc67..28fdbbbeb984bf600c0053c14c26901ac57248fc 100644 GIT binary patch delta 224 zcmbQkG?huQGr-TCmrII^fq{Y7)59eQNH+kn0t+*c)Zob#o2Y1C&spFRSBnm?xchdFtB#ul~-b5Dr#)J|zFja@)%@%3Y$azBnpm;hJivpBjYYV_sfE?~79SN8UP@>MAkOK2j1$0ti zWR4}ikrgzXr(wo7vH}frEJ+4UJprZ>C|0;*Np&cW>`22j0$G7d8J2{LoFv=Z(lCu6 hl&Dei1vdF10RWZr=~ntpgyR4J002ovPDHLkV1oFvRq6l$ diff --git a/graphics/battle_interface/healthbox_doubles_player.png b/graphics/battle_interface/healthbox_doubles_player.png index f843c664b19abef8d15df4fe13f3ef0fc88ac5d2..c3587ec3fd14505ac511b1fb72f3bd07c097846c 100644 GIT binary patch delta 229 zcmZ3=G>b{GGr-TCmrII^fq{Y7)59eQNH+kn0t+*c)Zob#o2Y1C&spFRS_uuxI87O=k1ZL)y*l7|L=$f*q$3TvBxC~e#;YAhKLr_sFEQRTp@dXWG& Zh7(yY-**0X*aLJNgQu&X%Q~loCIF&;Mk)XR delta 240 zcmbQmw3JD)Gr-TCmrII^fq{Y7)59eQNIQTq3p0?6aL;)=QPIABgQtsQh(~8~!UCZL z-@~j6K1n{^!QkTP(z@A@k0EfvyCp5$4mmFlXz~Xh2y^mq{J4T?kw}YRz=2tA4vrFx ztOu^N#DnMsPddUGFEp6cykwj+&$>X(Guf$AYSya51!>(Tt5S9=G8ov*>srR3{Nff< zfarquy`Jt&tC)IT6uYS}IZzYAI9EhMFzAAhM1vCGr-TCmrII^fq{Y7)59eQNH+kn0}C^dRQQqPGf~mLp0mIsvY3H^?+6Gp zPSxg<21-uyba4!cXnlLlkk7$Epf&NVRmTp-&UsF&HwI?$*dFA(kvv%=y=!L9;)?qH z@yjOIcz&8JvvT&4@23~Od&QDqG21Cp(v5M(YoAzc2E7;e)2A{liPqV`#K3TXPk@m@ zfPV|q16_v)4a$f0p4eKm+pzvMxhH=6dHF5f$7k5@z5dU;&#kOsqu!?m%})*UYFRlH dTQ2lhG5isYTU9F^(*blIgQu&X%Q~loCIHg@Q~Lk_ delta 244 zcmVf3!(N zK~z|U?Uk_(f=~=a-+~`P`~ytfVPIvEII>vXCBhfr`)m`-LTg_c)PdZy_N1NeZ3tf? zBa3-{8!T8jk5ys7!nMpT_(C-2EbMwuxI=)jHKBEcjGSDw6wPQB9DO_7!+SxBRCmlm zdI5P0XjOv&0RE3k>qQ8vQl@4pO2|$+#BM35TB|+b2w_WtcmWU$q4tF62>XW+WHg_X u$hQi&EPxOHyz_*{5k$qP`6&PZe$ES1FkY(iS^ePv0000u8IGr-TCmrII^fq{Y7)59eQNH+kn0t+*c)Zob#o2Y1C&spFRS>UlAce97O%YE`{*Y6b z47gpl@jdE3^36=g{Hc*=tI>v(<0VcKAFndKTh%BcydjiVWhc{WVF@SA(nAbP3|x9L V;bJ$}r2!qr;OXk;vd$@?2>_8wMX&$> delta 243 zcmbQnw1P>oGr-TCmrII^fq{Y7)59eQNIQTq3p0?6aL;)=QPIABv!{z=h(~8~!UCoQ z-^1(;pCq6DU|@E3Y29onci_N;cS~CM4K|cLm>Qmxpsl!MK}E#V;{oSG654UZOu=x_xzt<_}+W-Qtsw*u6{1-oD!M<%Bxr+ diff --git a/graphics/battle_interface/healthbox_singles_player.png b/graphics/battle_interface/healthbox_singles_player.png index 0862ca399d54ce6f77b94432f6189a8eed4e386a..cf65e676129334422cf47eccbc013ed8ec6c8967 100644 GIT binary patch delta 275 zcmcb>bb?8-Gr-TCmrII^fq{Y7)59eQNH+kn0}C^dRQQqPGf~mLp0mIsvY3H^?+6Gp zPSxg<21@Spba4!cXnlKaBiA7Vfny)_J~}ov7e^OdpR_KPr!ThZ)tVK%FFfOH*_Yeg zSN@=t`DDTWoj)f!J0-$f_WKBLCrhuw9*%x54LQ66UAiESbr~%Gof@c zQ#h;0590u@#_1wAPGmANJk;iB4C0DlEnprNrPHsZeF1H})^3#BgF9nbWa zOt<;)Z};!s;&p{{>|5kwA6(`UpK(9nzlPn-iQ*mY$p;wNelfk_d!c>7#P{-+o+yT` XY{3tXHZyMldWFH$)z4*}Q$iB}uRCsd delta 283 zcmV+$0p$M30?+~>iBL{Q4GJ0x0000DNk~Le0000$0001h1Oos707f7?k! zK~z|U?Uu0)f+sci6e_uXN|a<;5~r}Z|j9qBP~rAHHP$C`{g?Q ze=i6=Lh!uq^S7`(7uL>zvDW#?X)93|nPf(m03N Date: Sun, 19 Oct 2025 20:37:43 +0200 Subject: [PATCH 045/130] Fix ruination and nature's madness damage percentage (#7983) --- src/data/moves_info.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 35fed13f71..d521622efb 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -17103,6 +17103,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, + .argument = { .damagePercentage = 50 }, .metronomeBanned = B_UPDATED_MOVE_FLAGS >= GEN_8, .contestEffect = CONTEST_EFFECT_BADLY_STARTLE_MONS_WITH_GOOD_APPEALS, .contestCategory = CONTEST_CATEGORY_CUTE, @@ -20171,6 +20172,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, + .argument = { .damagePercentage = 50 }, .metronomeBanned = TRUE, .battleAnimScript = gBattleAnimMove_Ruination, }, From 51de48fc3f88edd44c2598bfe30613e797f2bad8 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Sun, 19 Oct 2025 20:40:34 +0200 Subject: [PATCH 046/130] SetShellSideArmCategory avoid div by zero (#7980) --- src/battle_util.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/battle_util.c b/src/battle_util.c index 42329b5495..617bb8b2e5 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -11248,6 +11248,8 @@ void SetShellSideArmCategory(void) statStage = gBattleMons[battlerDef].statStages[STAT_DEF]; targetDefStat *= gStatStageRatios[statStage][0]; targetDefStat /= gStatStageRatios[statStage][1]; + if (targetDefStat == 0) + targetDefStat = 1; physical = ((((2 * gBattleMons[battlerAtk].level / 5 + 2) * power * attackerAtkStat) / targetDefStat) / 50); @@ -11255,6 +11257,8 @@ void SetShellSideArmCategory(void) statStage = gBattleMons[battlerDef].statStages[STAT_SPDEF]; targetSpDefStat *= gStatStageRatios[statStage][0]; targetSpDefStat /= gStatStageRatios[statStage][1]; + if (targetSpDefStat == 0) + targetSpDefStat = 1; special = ((((2 * gBattleMons[battlerAtk].level / 5 + 2) * power * attackerSpAtkStat) / targetSpDefStat) / 50); From c16e2ded6062555d2e0c474d63ec8afb7a441ff3 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Sun, 19 Oct 2025 22:36:59 +0200 Subject: [PATCH 047/130] Fix ribbon colours (#7971) Co-authored-by: Hedara --- src/pokenav_ribbons_summary.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pokenav_ribbons_summary.c b/src/pokenav_ribbons_summary.c index 4b08b05cac..1e3d88b2dd 100644 --- a/src/pokenav_ribbons_summary.c +++ b/src/pokenav_ribbons_summary.c @@ -585,7 +585,11 @@ static u32 LoopedTask_OpenRibbonsSummaryMenu(s32 state) DecompressAndCopyTileDataToVram(1, sRibbonIconsSmall_Gfx, 0, 1, 0); SetBgTilemapBuffer(1, menu->tilemapBuffers[1]); FillBgTilemapBufferRect_Palette0(1, 0, 0, 0, 32, 20); - CopyPaletteIntoBufferUnfaded(sRibbonIcons1_Pal, BG_PLTT_ID(2), 5 * PLTT_SIZE_4BPP); + CopyPaletteIntoBufferUnfaded(sRibbonIcons1_Pal, BG_PLTT_ID(2), PLTT_SIZE_4BPP); + CopyPaletteIntoBufferUnfaded(sRibbonIcons2_Pal, BG_PLTT_ID(3), PLTT_SIZE_4BPP); + CopyPaletteIntoBufferUnfaded(sRibbonIcons3_Pal, BG_PLTT_ID(4), PLTT_SIZE_4BPP); + CopyPaletteIntoBufferUnfaded(sRibbonIcons4_Pal, BG_PLTT_ID(5), PLTT_SIZE_4BPP); + CopyPaletteIntoBufferUnfaded(sRibbonIcons5_Pal, BG_PLTT_ID(6), PLTT_SIZE_4BPP); CopyPaletteIntoBufferUnfaded(sMonInfo_Pal, BG_PLTT_ID(10), sizeof(sMonInfo_Pal)); CopyBgTilemapBufferToVram(1); return LT_INC_AND_PAUSE; From 1dd7a5731c6507025d914c550792550e7e5768b5 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Sun, 19 Oct 2025 22:37:23 +0200 Subject: [PATCH 048/130] CalcBarFilledPixels Safe Div (#7979) --- src/battle_interface.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/battle_interface.c b/src/battle_interface.c index 2264d94ce4..29a2cf0436 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -2258,10 +2258,11 @@ static u8 CalcBarFilledPixels(s32 maxValue, s32 oldValue, s32 receivedValue, s32 for (i = 0; i < scale; i++) pixelsArray[i] = 0; + // Safe Div, because 2vs1 battles can have maxValue 0. if (maxValue < totalPixels) - pixels = (*currValue * totalPixels / maxValue) >> 8; + pixels = SAFE_DIV(*currValue * totalPixels, maxValue) >> 8; else - pixels = *currValue * totalPixels / maxValue; + pixels = SAFE_DIV(*currValue * totalPixels, maxValue); filledPixels = pixels; From e5df8f0212edd481122408322acf00cdc3f2e861 Mon Sep 17 00:00:00 2001 From: Estellar <137097857+estellarc@users.noreply.github.com> Date: Sun, 19 Oct 2025 17:50:54 -0300 Subject: [PATCH 049/130] Bugfix Emotes not loading their palette (#7843) --- src/trainer_see.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/trainer_see.c b/src/trainer_see.c index 0433e56071..4439ce6977 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -951,13 +951,17 @@ u8 FldEff_HeartIcon(void) return 0; } - u8 FldEff_DoubleExclMarkIcon(void) { u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_ExclamationQuestionMark, 0, 0, 0x53); if (spriteId != MAX_SPRITES) - SetIconSpriteData(&gSprites[spriteId], FLDEFF_EXCLAMATION_MARK_ICON, 2); + { + struct Sprite *sprite = &gSprites[spriteId]; + + SetIconSpriteData(sprite, FLDEFF_DOUBLE_EXCL_MARK_ICON, 2); + UpdateSpritePaletteByTemplate(&sSpriteTemplate_ExclamationQuestionMark, sprite); + } return 0; } @@ -967,7 +971,12 @@ u8 FldEff_XIcon(void) u8 spriteId = CreateSpriteAtEnd(&sSpriteTemplate_ExclamationQuestionMark, 0, 0, 0x53); if (spriteId != MAX_SPRITES) - SetIconSpriteData(&gSprites[spriteId], FLDEFF_EXCLAMATION_MARK_ICON, 3); + { + struct Sprite *sprite = &gSprites[spriteId]; + + SetIconSpriteData(sprite, FLDEFF_X_ICON, 3); + UpdateSpritePaletteByTemplate(&sSpriteTemplate_ExclamationQuestionMark, sprite); + } return 0; } From 24c595bfec5f8467916a49a138c1ce2cf4689c99 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Mon, 20 Oct 2025 13:38:01 +0200 Subject: [PATCH 050/130] Fix psychic terrain affecting semi-invulnerable mons (#7986) --- src/battle_util.c | 3 +-- test/battle/move_effect/psychic_terrain.c | 32 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 617bb8b2e5..b0206f98bd 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2403,8 +2403,7 @@ static enum MoveCanceller CancellerProtean(void) static enum MoveCanceller CancellerPsychicTerrain(void) { - if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN - && IsBattlerGrounded(gBattlerTarget) + if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_PSYCHIC_TERRAIN) && GetChosenMovePriority(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) > 0 && GetMoveTarget(gCurrentMove) != MOVE_TARGET_ALL_BATTLERS && GetMoveTarget(gCurrentMove) != MOVE_TARGET_OPPONENTS_FIELD diff --git a/test/battle/move_effect/psychic_terrain.c b/test/battle/move_effect/psychic_terrain.c index b85653a0be..2c5089c704 100644 --- a/test/battle/move_effect/psychic_terrain.c +++ b/test/battle/move_effect/psychic_terrain.c @@ -114,6 +114,38 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority field moves") } } +SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves against semi-invulnerable targets") +{ + u32 move = 0, shouldWork = 0; + PARAMETRIZE { move = MOVE_SOLAR_BEAM; shouldWork = FALSE;} + PARAMETRIZE { move = MOVE_FLY; shouldWork = TRUE;} + GIVEN { + PLAYER(SPECIES_SHROODLE) { Ability(ABILITY_PRANKSTER); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_PSYCHIC_TERRAIN); MOVE(opponent,move);} + TURN { MOVE(player, MOVE_TOXIC); SKIP_TURN(opponent);} + } SCENE { + if (shouldWork) + { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + } + else + { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + } + } + } THEN { + if (shouldWork) + EXPECT(opponent->status1 & STATUS1_TOXIC_POISON); + else + EXPECT(!(opponent->status1 & STATUS1_TOXIC_POISON)); + } +} + SINGLE_BATTLE_TEST("Psychic Terrain lasts for 5 turns") { GIVEN { From ac321bf581147fa83d63b59c930ce1d8984839c1 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Mon, 20 Oct 2025 15:56:55 +0200 Subject: [PATCH 051/130] Fix spritesheet rules for rayquaza sprite in cutscene (#7985) --- graphics_file_rules.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphics_file_rules.mk b/graphics_file_rules.mk index 2b9cb9df8b..0b8a91415d 100644 --- a/graphics_file_rules.mk +++ b/graphics_file_rules.mk @@ -405,7 +405,7 @@ $(RAYQUAZAGFXDIR)/scene_2/bg.4bpp: %.4bpp: %.png $(GFX) $< $@ -num_tiles 313 -Wnum_tiles $(RAYQUAZAGFXDIR)/scene_3/rayquaza.4bpp: %.4bpp: %.png - $(GFX) $< $@ -num_tiles 124 -Wnum_tiles + $(GFX) $< $@ -num_tiles 128 -Wnum_tiles $(RAYQUAZAGFXDIR)/scene_4/streaks.4bpp: %.4bpp: %.png $(GFX) $< $@ -num_tiles 19 -Wnum_tiles From c3735c5bd0efac67d198ccc9f7b7116a9bac7506 Mon Sep 17 00:00:00 2001 From: Marky <143505183+HashtagMarky@users.noreply.github.com> Date: Tue, 21 Oct 2025 10:23:05 +0100 Subject: [PATCH 052/130] =?UTF-8?q?Fix=20OW=20Pok=C3=A9mon=20VObjects=20(#?= =?UTF-8?q?7991)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/event_object_movement.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 6d0cf86cf6..0c17f5c445 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -1927,8 +1927,15 @@ u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevati x += MAP_OFFSET; y += MAP_OFFSET; SetSpritePosToOffsetMapCoords(&x, &y, 8, 16); - if (spriteTemplate.paletteTag != TAG_NONE) + if (spriteTemplate.paletteTag == OBJ_EVENT_PAL_TAG_DYNAMIC) + { + u32 paletteNum = LoadDynamicFollowerPaletteFromGraphicsId(graphicsId, &spriteTemplate); + spriteTemplate.paletteTag = GetSpritePaletteTagByPaletteNum(paletteNum); + } + else if (spriteTemplate.paletteTag != TAG_NONE) + { LoadObjectEventPalette(spriteTemplate.paletteTag); + } spriteId = CreateSpriteAtEnd(&spriteTemplate, x, y, 0); if (spriteId != MAX_SPRITES) @@ -1942,6 +1949,9 @@ u8 CreateVirtualObject(u16 graphicsId, u8 virtualObjId, s16 x, s16 y, u8 elevati sprite->sVirtualObjId = virtualObjId; sprite->sVirtualObjElev = elevation; + if (OW_GFX_COMPRESS && graphicsInfo->compressed) + spriteTemplate.tileTag = LoadSheetGraphicsInfo(graphicsInfo, graphicsId, sprite); + if (subspriteTables != NULL) { SetSubspriteTables(sprite, subspriteTables); From bc6ba15702dda7a50ead49f8deea5bb3cfac66e5 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 21 Oct 2025 12:50:55 +0200 Subject: [PATCH 053/130] Fixes Terrain Extender timer (#7995) --- 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 c9b64ffea2..d64a2d953f 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -15339,7 +15339,7 @@ void BS_SetTerrain(void) enum ItemHoldEffect atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; gFieldStatuses |= statusFlag; - gFieldTimers.terrainTimer = gBattleTurnCounter + (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5; + gFieldTimers.terrainTimer = gBattleTurnCounter + ((atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5); gBattlescriptCurrInstr = cmd->nextInstr; } else From 41fa1ad2b90de712c3d8e705113afda4c9928cba Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 21 Oct 2025 13:08:31 +0200 Subject: [PATCH 054/130] add HashtagMarky as a contributor for code (#7998) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 11 ++++++++++- CREDITS.md | 5 ++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index a04141fcf7..aa13a3864d 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -367,7 +367,7 @@ "doc", "userTesting" ] - }, + }, { "login": "Pyredrid", "name": "Pyredrid", @@ -432,6 +432,15 @@ "contributions": [ "code" ] + }, + { + "login": "HashtagMarky", + "name": "Marky", + "avatar_url": "https://avatars.githubusercontent.com/u/143505183?v=4", + "profile": "http://hashtagmarky.github.io", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 1cd4611d69..d3d46d0361 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -64,13 +64,16 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Enrico Drago
Enrico Drago

📖 📓 - Pyredrid
Pyredrid

📓 🚧 + Pyredrid
Pyredrid

📓 🚧 mv
mv

💻 🎨 Avara
Avara

🔣 Doesnty
Doesnty

🎨 FosterProgramming
FosterProgramming

💻 Squeetz
Squeetz

🚧 + + ghostyboyy97
ghostyboyy97

💻 + Marky
Marky

💻 From 3bb6cddffb7432b064939de9271444b37d03a6d0 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Tue, 21 Oct 2025 16:39:02 +0200 Subject: [PATCH 055/130] Fix Minior start of battle form (#7972) Co-authored-by: Hedara Co-authored-by: Bassoonian --- src/data/pokemon/form_change_tables.h | 7 ++++++ test/battle/ability/shields_down.c | 31 +++++++++++++++++++++------ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index ad8a7e90ca..fd443458c7 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -925,6 +925,7 @@ static const struct FormChange sSilvallyFormChangeTable[] = { #if P_FAMILY_MINIOR static const struct FormChange sMiniorRedFormChangeTable[] = { + {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}, {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_RED}, @@ -933,6 +934,7 @@ static const struct FormChange sMiniorRedFormChangeTable[] = { {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMiniorBlueFormChangeTable[] = { + {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}, {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_BLUE}, @@ -941,6 +943,7 @@ static const struct FormChange sMiniorBlueFormChangeTable[] = { {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMiniorGreenFormChangeTable[] = { + {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}, {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_GREEN}, @@ -949,6 +952,7 @@ static const struct FormChange sMiniorGreenFormChangeTable[] = { {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMiniorIndigoFormChangeTable[] = { + {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}, {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_INDIGO}, @@ -957,6 +961,7 @@ static const struct FormChange sMiniorIndigoFormChangeTable[] = { {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMiniorOrangeFormChangeTable[] = { + {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}, {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_ORANGE}, @@ -965,6 +970,7 @@ static const struct FormChange sMiniorOrangeFormChangeTable[] = { {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMiniorVioletFormChangeTable[] = { + {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}, {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_VIOLET}, @@ -973,6 +979,7 @@ static const struct FormChange sMiniorVioletFormChangeTable[] = { {FORM_CHANGE_TERMINATOR}, }; static const struct FormChange sMiniorYellowFormChangeTable[] = { + {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}, {FORM_CHANGE_BATTLE_SWITCH, SPECIES_MINIOR_CORE_YELLOW}, diff --git a/test/battle/ability/shields_down.c b/test/battle/ability/shields_down.c index 5c46a8d589..554e67b0d8 100644 --- a/test/battle/ability/shields_down.c +++ b/test/battle/ability/shields_down.c @@ -1,30 +1,47 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Minior Meteor transforms into Minior Core on switch-in if it has 1/2 or less health") +SINGLE_BATTLE_TEST("Minior Core doesn't transform into Minior Meteor on switch-in if it has 1/2 or less health") { GIVEN { PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET) { HP(1); } - OPPONENT(SPECIES_MINIOR_METEOR) { Ability(ABILITY_SHIELDS_DOWN); HP(1); } + OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); HP(50); MaxHP(100); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); SEND_OUT(opponent, 1); } + } SCENE { + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent); + } + } THEN { + EXPECT_EQ(opponent->species, SPECIES_MINIOR_CORE); + } +} + +SINGLE_BATTLE_TEST("Minior Core transforms into Minior Meteor on switch-in if it has more than 1/2 health") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); HP(51); MaxHP(101); } } WHEN { TURN { MOVE(player, MOVE_SCRATCH); SEND_OUT(opponent, 1); } } SCENE { ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent); } THEN { - EXPECT_EQ(opponent->species, SPECIES_MINIOR_CORE); + EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR); } } -SINGLE_BATTLE_TEST("Minior Core transforms into Minior Meteor on switch-in if it more then 1/2 health") +SINGLE_BATTLE_TEST("Minior Core transforms into Minior Meteor on battle start if it has more than 1/2 health") { GIVEN { PLAYER(SPECIES_WYNAUT); - OPPONENT(SPECIES_WOBBUFFET) { HP(1); } - OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); } + OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); HP(51); MaxHP(101); } } WHEN { - TURN { MOVE(player, MOVE_SCRATCH); SEND_OUT(opponent, 1); } + TURN { } } SCENE { ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent); From 5276f2bc6b20917880cb4127f6b57155f900245c Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Tue, 21 Oct 2025 15:49:48 -0300 Subject: [PATCH 056/130] Fixed Max Move in-battle descriptions (#8004) --- src/battle_controller_player.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index c4ede0eab4..3361ce2f65 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -1725,6 +1725,14 @@ static void MoveSelectionDisplayMoveDescription(u32 battler) u16 move = moveInfo->moves[gMoveSelectionCursor[battler]]; u16 pwr = GetMovePower(move); u16 acc = GetMoveAccuracy(move); + enum DamageCategory cat = GetBattleMoveCategory(move); + + if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX)) + { + pwr = GetMaxMovePower(move); + move = GetMaxMove(battler, move); + acc = 0; + } u8 pwr_num[3], acc_num[3]; u8 cat_desc[7] = _("CAT: "); @@ -1758,7 +1766,7 @@ static void MoveSelectionDisplayMoveDescription(u32 battler) if (gCategoryIconSpriteId == 0xFF) gCategoryIconSpriteId = CreateSprite(&gSpriteTemplate_CategoryIcons, 38, 64, 1); - StartSpriteAnim(&gSprites[gCategoryIconSpriteId], GetBattleMoveCategory(move)); + StartSpriteAnim(&gSprites[gCategoryIconSpriteId], cat); CopyWindowToVram(B_WIN_MOVE_DESCRIPTION, COPYWIN_FULL); } From 2d7328677747f796442304be697fceb92a6728d5 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Wed, 22 Oct 2025 10:09:58 +0200 Subject: [PATCH 057/130] Fix long pokemon name in partner party not appearing properly (#8009) --- src/party_menu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/party_menu.c b/src/party_menu.c index 3b05551cb2..04bf04a9df 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -273,6 +273,7 @@ static bool8 IsMonAllowedInMinigame(u8); static void DisplayPartyPokemonDataToTeachMove(u8, u16); static u8 CanTeachMove(struct Pokemon *, u16); static void DisplayPartyPokemonBarDetail(u8, const u8 *, u8, const u8 *); +static void DisplayPartyPokemonBarDetailToFit(u8 windowId, const u8 *str, u8 color, const u8 *align, u32 width); static void DisplayPartyPokemonLevel(u8, struct PartyMenuBox *); static void DisplayPartyPokemonGender(u8, u16, u8 *, struct PartyMenuBox *); static void DisplayPartyPokemonHP(u16 hp, u16 maxHp, struct PartyMenuBox *menuBox); @@ -1167,7 +1168,7 @@ static void DisplayPartyPokemonDataForMultiBattle(u8 slot) StringCopy(gStringVar1, gMultiPartnerParty[actualSlot].nickname); StringGet_Nickname(gStringVar1); ConvertInternationalPlayerName(gStringVar1); - DisplayPartyPokemonBarDetail(menuBox->windowId, gStringVar1, 0, menuBox->infoRects->dimensions); + DisplayPartyPokemonBarDetailToFit(menuBox->windowId, gStringVar1, 0, menuBox->infoRects->dimensions, 50); DisplayPartyPokemonLevel(gMultiPartnerParty[actualSlot].level, menuBox); DisplayPartyPokemonGender(gMultiPartnerParty[actualSlot].gender, gMultiPartnerParty[actualSlot].species, gMultiPartnerParty[actualSlot].nickname, menuBox); DisplayPartyPokemonHP(gMultiPartnerParty[actualSlot].hp, gMultiPartnerParty[actualSlot].maxhp, menuBox); From 2416bfb53b594f336943b19f638deebbb27f75ea Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Wed, 22 Oct 2025 12:39:24 +0100 Subject: [PATCH 058/130] Fixes Echoed Voice base power increase depending on attacker's use of the move (#7997) --- include/battle.h | 5 +- src/battle_end_turn.c | 11 ++ src/battle_main.c | 4 +- src/battle_script_commands.c | 8 +- src/battle_util.c | 8 +- test/battle/move_effect/echoed_voice.c | 170 ++++++++++++++++++++++++- 6 files changed, 191 insertions(+), 15 deletions(-) diff --git a/include/battle.h b/include/battle.h index 8fe4fa8cce..ba3752717a 100644 --- a/include/battle.h +++ b/include/battle.h @@ -722,7 +722,7 @@ struct BattleStruct struct Illusion illusion[MAX_BATTLERS_COUNT]; u8 soulheartBattlerId; u8 friskedBattler; // Frisk needs to identify 2 battlers in double battles. - u8 sameMoveTurns[MAX_BATTLERS_COUNT]; // For Metronome, number of times the same moves has been SUCCESFULLY used. + u8 metronomeItemCounter[MAX_BATTLERS_COUNT]; // For Metronome, number of times the same moves has been SUCCESFULLY used. u8 quickClawBattlerId; struct LostItem itemLost[NUM_BATTLE_SIDES][PARTY_SIZE]; // Pokemon that had items consumed or stolen (two bytes per party member per side) u8 blunderPolicy:1; // should blunder policy activate @@ -782,7 +782,8 @@ struct BattleStruct u8 hazardsQueue[NUM_BATTLE_SIDES][HAZARDS_MAX_COUNT]; u8 numHazards[NUM_BATTLE_SIDES]; u8 hazardsCounter:4; // Counter for applying hazard on switch in - u8 padding2:4; + u8 incrementEchoedVoice:1; + u8 echoedVoiceCounter:3; }; struct AiBattleData diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index 73ffa12023..da7607d9c4 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -168,6 +168,17 @@ static bool32 HandleEndTurnVarious(u32 battler) gBattleStruct->hpBefore[i] = gBattleMons[i].hp; } + if (gBattleStruct->incrementEchoedVoice) + { + if (gBattleStruct->echoedVoiceCounter < 4) + gBattleStruct->echoedVoiceCounter++; + gBattleStruct->incrementEchoedVoice = FALSE; + } + else + { + gBattleStruct->echoedVoiceCounter = 0; + } + return effect; } diff --git a/src/battle_main.c b/src/battle_main.c index 26755eda97..ee593da2f1 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3227,7 +3227,7 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy) gLastHitBy[battler] = 0xFF; gBattleStruct->lastTakenMove[battler] = 0; - gBattleStruct->sameMoveTurns[battler] = 0; + gBattleStruct->metronomeItemCounter[battler] = 0; gBattleStruct->lastTakenMoveFrom[battler][0] = 0; gBattleStruct->lastTakenMoveFrom[battler][1] = 0; gBattleStruct->lastTakenMoveFrom[battler][2] = 0; @@ -3348,7 +3348,7 @@ const u8* FaintClearSetData(u32 battler) gLastHitBy[battler] = 0xFF; gBattleStruct->choicedMove[battler] = MOVE_NONE; - gBattleStruct->sameMoveTurns[battler] = 0; + gBattleStruct->metronomeItemCounter[battler] = 0; gBattleStruct->lastTakenMove[battler] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[battler][0] = 0; gBattleStruct->lastTakenMoveFrom[battler][1] = 0; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index d64a2d953f..e5bd66a0ac 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1541,7 +1541,7 @@ static void Cmd_ppreduce(void) // For item Metronome, echoed voice if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || WasUnableToUseMove(gBattlerAttacker)) - gBattleStruct->sameMoveTurns[gBattlerAttacker] = 0; + gBattleStruct->metronomeItemCounter[gBattlerAttacker] = 0; if (gBattleMons[gBattlerAttacker].pp[gCurrMovePos] > ppToDeduct) gBattleMons[gBattlerAttacker].pp[gCurrMovePos] -= ppToDeduct; @@ -6893,9 +6893,9 @@ static void Cmd_moveend(void) if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT || gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) - gBattleStruct->sameMoveTurns[gBattlerAttacker] = 0; + gBattleStruct->metronomeItemCounter[gBattlerAttacker] = 0; else if (gCurrentMove == gLastResultingMoves[gBattlerAttacker] && gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT) - gBattleStruct->sameMoveTurns[gBattlerAttacker]++; + gBattleStruct->metronomeItemCounter[gBattlerAttacker]++; gBattleScripting.moveendState++; break; case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits. @@ -6949,6 +6949,8 @@ static void Cmd_moveend(void) SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE); if (B_CHARGE >= GEN_9 && moveType == TYPE_ELECTRIC && (IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) gBattleMons[gBattlerAttacker].volatiles.charge = FALSE; + if (moveEffect == EFFECT_ECHOED_VOICE && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)) + gBattleStruct->incrementEchoedVoice = TRUE; // check if Stellar type boost should be used up if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA && GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR diff --git a/src/battle_util.c b/src/battle_util.c index b0206f98bd..cd1c435474 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8074,10 +8074,10 @@ static inline u32 CalcMoveBasePower(struct DamageContext *ctx) break; } case EFFECT_ECHOED_VOICE: - // gBattleStruct->sameMoveTurns incremented in ppreduce - if (gBattleStruct->sameMoveTurns[battlerAtk] != 0 && GetMoveEffect(gLastResultingMoves[battlerAtk]) == EFFECT_ECHOED_VOICE) + // gBattleStruct->echoedVoiceCounter incremented in EndTurnVarious called by DoEndTurnEffects + if (gBattleStruct->echoedVoiceCounter != 0) { - basePower += (basePower * gBattleStruct->sameMoveTurns[battlerAtk]); + basePower += (basePower * gBattleStruct->echoedVoiceCounter); if (basePower > 200) basePower = 200; } @@ -9207,7 +9207,7 @@ static inline uq4_12_t GetAttackerItemsModifier(u32 battlerAtk, uq4_12_t typeEff { case HOLD_EFFECT_METRONOME: metronomeBoostBase = PercentToUQ4_12(GetBattlerHoldEffectParam(battlerAtk)); - metronomeTurns = min(gBattleStruct->sameMoveTurns[battlerAtk], 5); + metronomeTurns = min(gBattleStruct->metronomeItemCounter[battlerAtk], 5); // according to bulbapedia this is the "correct" way to calculate the metronome boost // due to the limited domain of damage numbers it will never really matter whether this is off by one return uq4_12_add(UQ_4_12(1.0), metronomeBoostBase * metronomeTurns); diff --git a/test/battle/move_effect/echoed_voice.c b/test/battle/move_effect/echoed_voice.c index 3c36270454..29a2b6e487 100644 --- a/test/battle/move_effect/echoed_voice.c +++ b/test/battle/move_effect/echoed_voice.c @@ -1,7 +1,169 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Echoed Voice's power is multiplied for every consecutive turn used, capped at 5"); -TO_DO_BATTLE_TEST("Echoed Voice's power is reset when using a different move"); -TO_DO_BATTLE_TEST("Echoed Voice's power is increased even if it misses"); -TO_DO_BATTLE_TEST("Echoed Voice's power is increased even if it's blocked by Protect"); +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_ECHOED_VOICE) == EFFECT_ECHOED_VOICE); +} + +SINGLE_BATTLE_TEST("Echoed Voice's power is multiplied for every consecutive turn used, capped at 5") +{ + s16 damage[6]; + + GIVEN { + ASSUME(GetMoveEffect(MOVE_SOFT_BOILED) == EFFECT_SOFTBOILED); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_BLISSEY); + } WHEN { + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_SOFT_BOILED); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_SOFT_BOILED); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[1]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[2]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[3]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[4]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[5]); + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(3.0), damage[2]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(4.0), damage[3]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(5.0), damage[4]); + EXPECT_EQ(damage[4], damage[5]); + } +} + +SINGLE_BATTLE_TEST("Echoed Voice's power increases even if used by another battler") +{ + s16 damage[2]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, opponent); + HP_BAR(player, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[1]); + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]); + } +} + +SINGLE_BATTLE_TEST("Echoed Voice's power does not change until the end of the turn") +{ + s16 damage[3]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, opponent); + HP_BAR(player, captureDamage: &damage[1]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + EXPECT_EQ(damage[0], damage[1]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[2]); + } +} + +SINGLE_BATTLE_TEST("Echoed Voice's power increase is reset when no battler uses it successfully during a turn") +{ + s16 damage[3]; + + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_BITE, MOVE_EFFECT_FLINCH)); + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(10); } + } WHEN { + TURN { MOVE(opponent, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(opponent, MOVE_BITE); MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, opponent); + HP_BAR(player, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[1]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponent); + MESSAGE("Wobbuffet flinched and couldn't move!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + EXPECT_EQ(damage[0], damage[2]); + EXPECT_NE(damage[1], damage[2]); + } +} + +SINGLE_BATTLE_TEST("Echoed Voice's power is increased even if it misses") +{ + s16 damage[3]; + + GIVEN { + ASSUME(GetMoveEffect(MOVE_SAND_ATTACK) == EFFECT_ACCURACY_DOWN); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_SAND_ATTACK); } + TURN { MOVE(player, MOVE_ECHOED_VOICE, hit: FALSE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[1]); + MESSAGE("Wobbuffet's attack missed!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(4.0), damage[2]); + } +} + +SINGLE_BATTLE_TEST("Echoed Voice's power is increased even if it's blocked by Protect") +{ + s16 damage[3]; + + GIVEN { + ASSUME(GetMoveEffect(MOVE_PROTECT) == EFFECT_PROTECT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); MOVE(opponent, MOVE_PROTECT); } + TURN { MOVE(player, MOVE_ECHOED_VOICE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[1]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ECHOED_VOICE, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(2.0), damage[1]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(4.0), damage[2]); + } +} From dc0d9101820bc5187db8a577e2529019a6346cb2 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Wed, 22 Oct 2025 10:20:44 -0300 Subject: [PATCH 059/130] Fixed Stomping Tantrum not doubling in damage if the user failed Protect (#8008) --- include/random.h | 1 + src/battle_script_commands.c | 3 ++- test/battle/move_effect/stomping_tantrum.c | 25 ++++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/include/random.h b/include/random.h index df3e1579ad..816cf9b40a 100644 --- a/include/random.h +++ b/include/random.h @@ -217,6 +217,7 @@ enum RandomTag RNG_WRAP, RNG_BALLTHROW_CRITICAL, RNG_BALLTHROW_SHAKE, + RNG_PROTECT_FAIL, }; #define RandomWeighted(tag, ...) \ diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index e5bd66a0ac..7cd573f5ae 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9575,7 +9575,7 @@ static void Cmd_setprotectlike(void) if (gCurrentTurnActionNumber == (gBattlersCount - 1)) notLastTurn = FALSE; - if ((sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= Random() && notLastTurn) + if ((sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= RandomUniform(RNG_PROTECT_FAIL, 0, USHRT_MAX) && notLastTurn) || (protectMethod == PROTECT_WIDE_GUARD && B_WIDE_GUARD != GEN_5) || (protectMethod == PROTECT_QUICK_GUARD && B_QUICK_GUARD != GEN_5)) { @@ -9604,6 +9604,7 @@ static void Cmd_setprotectlike(void) gDisableStructs[gBattlerAttacker].protectUses = 0; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECT_FAILED; gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; + gBattleStruct->battlerState[gBattlerAttacker].stompingTantrumTimer = 2; } gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/test/battle/move_effect/stomping_tantrum.c b/test/battle/move_effect/stomping_tantrum.c index 95d93e07e6..06b29334d7 100644 --- a/test/battle/move_effect/stomping_tantrum.c +++ b/test/battle/move_effect/stomping_tantrum.c @@ -90,6 +90,31 @@ SINGLE_BATTLE_TEST("Stomping Tantrum will not deal double damage if target prote } } +SINGLE_BATTLE_TEST("Stomping Tantrum will deal double damage if user failed a Protect") +{ + s16 damage[2]; + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_STOMPING_TANTRUM); } + TURN { MOVE(player, MOVE_PROTECT); } + TURN { MOVE(player, MOVE_PROTECT, WITH_RNG(RNG_PROTECT_FAIL, USHRT_MAX)); } + TURN { MOVE(player, MOVE_STOMPING_TANTRUM); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player); + HP_BAR(opponent, captureDamage: &damage[0]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, player); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player); + HP_BAR(opponent, captureDamage: &damage[1]); + } THEN { + EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]); + } +} + SINGLE_BATTLE_TEST("Stomping Tantrum will not deal double if it missed") { s16 damage[2]; From 1343ddf7c30f043b9726ebfc464a07bdf8a310a4 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Wed, 22 Oct 2025 22:57:04 +0200 Subject: [PATCH 060/130] Fix badge boost not applying in gen1 and 2 (#8013) --- include/battle_util.h | 1 + include/config/battle.h | 2 +- include/constants/generational_changes.h | 1 + include/generational_changes.h | 1 + src/battle_main.c | 2 +- src/battle_util.c | 20 ++- test/battle/badge_boost.c | 166 +++++++++++++++++++++++ 7 files changed, 185 insertions(+), 8 deletions(-) create mode 100644 test/battle/badge_boost.c diff --git a/include/battle_util.h b/include/battle_util.h index d30caadd47..961ae450f4 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -311,6 +311,7 @@ u32 GetIllusionMonPartyId(struct Pokemon *party, struct Pokemon *mon, struct Pok bool32 SetIllusionMon(struct Pokemon *mon, u32 battler); u32 TryImmunityAbilityHealStatus(u32 battler, u32 caseID); bool32 ShouldGetStatBadgeBoost(u16 flagId, u32 battler); +uq4_12_t GetBadgeBoostModifier(void); enum DamageCategory GetBattleMoveCategory(u32 move); void SetDynamicMoveCategory(u32 battlerAtk, u32 battlerDef, u32 move); bool32 CanFling(u32 battler); diff --git a/include/config/battle.h b/include/config/battle.h index 61603ba5b8..2b9e9fdecc 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -19,7 +19,7 @@ #define B_LEVEL_UP_NOTIFICATION GEN_LATEST // In Gen9+, if the Pokémon gets enough experience to level up multiple times, the message is only displayed once. // Stat settings -#define B_BADGE_BOOST GEN_LATEST // In Gen4+, Gym Badges no longer boost a Pokémon's stats. +#define B_BADGE_BOOST GEN_LATEST // In Gen4+, Gym Badges no longer boost a Pokémon's stats. (Gen2 does not include the additional boost to the type matching the gym the badge is from) #define B_FRIENDSHIP_BOOST FALSE // In LGPE only, all stats except HP are boosted up to 10% based on Friendship. Unlike B_BADGE_BOOST, these boosts are accounted when calculating base stats. #define B_MAX_LEVEL_EV_GAINS GEN_LATEST // In Gen5+, Lv100 Pokémon can obtain Effort Values normally. #define B_RECALCULATE_STATS GEN_LATEST // In Gen5+, the stats of the Pokémon who participate in battle are recalculated at the end of each battle. diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h index 7a9aed85c3..7be7998d9e 100644 --- a/include/constants/generational_changes.h +++ b/include/constants/generational_changes.h @@ -45,6 +45,7 @@ enum GenConfigTag GEN_CONFIG_OBLIVIOUS_TAUNT, GEN_CONFIG_TOXIC_NEVER_MISS, GEN_CONFIG_PARALYZE_ELECTRIC, + GEN_CONFIG_BADGE_BOOST, GEN_CONFIG_COUNT }; diff --git a/include/generational_changes.h b/include/generational_changes.h index 30739a14f5..9b6b385c7e 100644 --- a/include/generational_changes.h +++ b/include/generational_changes.h @@ -48,6 +48,7 @@ static const u8 sGenerationalChanges[GEN_CONFIG_COUNT] = [GEN_CONFIG_OBLIVIOUS_TAUNT] = B_OBLIVIOUS_TAUNT, [GEN_CONFIG_TOXIC_NEVER_MISS] = B_TOXIC_NEVER_MISS, [GEN_CONFIG_PARALYZE_ELECTRIC] = B_PARALYZE_ELECTRIC, + [GEN_CONFIG_BADGE_BOOST] = B_BADGE_BOOST }; #if TESTING diff --git a/src/battle_main.c b/src/battle_main.c index ee593da2f1..6aa4c7212d 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4770,7 +4770,7 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, enum ItemHoldEffect h && ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPEED, battler) && IsOnPlayerSide(battler)) { - speed = (speed * 110) / 100; + speed = uq4_12_multiply_by_int_half_down(GetBadgeBoostModifier(), speed); } // item effects diff --git a/src/battle_util.c b/src/battle_util.c index cd1c435474..d5cad634d9 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8766,9 +8766,9 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx) // The offensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the corresponding flags set (eg. Badges) if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_ATTACK, battlerAtk) && IsBattleMovePhysical(move)) - modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.1)); + modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier()); if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPATK, battlerAtk) && IsBattleMoveSpecial(move)) - modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.1)); + modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier()); return uq4_12_multiply_by_int_half_down(modifier, atkStat); } @@ -8942,11 +8942,11 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx) if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && IsBattlerWeatherAffected(battlerDef, B_WEATHER_SNOW) && usesDefStat) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); - // The offensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the corresponding flags set (eg. Badges) + // The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the corresponding flags set (eg. Badges) if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_DEFENSE, battlerDef) && IsBattleMovePhysical(move)) - modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.1)); + modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier()); if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPDEF, battlerDef) && IsBattleMoveSpecial(move)) - modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.1)); + modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier()); return uq4_12_multiply_by_int_half_down(modifier, defStat); } @@ -10432,9 +10432,17 @@ u32 TryImmunityAbilityHealStatus(u32 battler, u32 caseID) return 0; } +uq4_12_t GetBadgeBoostModifier(void) +{ + if (GetGenConfig(GEN_CONFIG_BADGE_BOOST) < GEN_3) + return UQ_4_12(1.125); + else + return UQ_4_12(1.1); +} + bool32 ShouldGetStatBadgeBoost(u16 badgeFlag, u32 battler) { - if (B_BADGE_BOOST == GEN_3 && badgeFlag != 0) + if (GetGenConfig(GEN_CONFIG_BADGE_BOOST) <= GEN_3 && badgeFlag != 0) { if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FRONTIER)) return FALSE; diff --git a/test/battle/badge_boost.c b/test/battle/badge_boost.c new file mode 100644 index 0000000000..eb8cecb240 --- /dev/null +++ b/test/battle/badge_boost.c @@ -0,0 +1,166 @@ +#include "global.h" +#include "event_data.h" +#include "test/battle.h" + +WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_ATTACK boost Attack", s16 dmg) +{ + u32 badge = 0; + u32 genConfig = 0; + for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++) + { + PARAMETRIZE{badge = FALSE; genConfig = gen;} + PARAMETRIZE{badge = TRUE; genConfig = gen;} + } + GIVEN { + if (badge) + FlagSet(B_FLAG_BADGE_BOOST_ATTACK); + else + FlagClear(B_FLAG_BADGE_BOOST_ATTACK); + WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig); + PLAYER(SPECIES_WOBBUFFET) {} + OPPONENT(SPECIES_WOBBUFFET) {} + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].dmg); + } FINALLY { + for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++) + { + if (gen <= GEN_3) + EXPECT_GT(results[2 * gen + 1].dmg, results[2 * gen].dmg); + else + EXPECT_EQ(results[2 * gen + 1].dmg, results[2 * gen].dmg); + } + } +} + +WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_SPATK boost Special Attack", s16 dmg) +{ + u32 badge = 0; + u32 genConfig = 0; + for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++) + { + PARAMETRIZE{badge = FALSE; genConfig = gen;} + PARAMETRIZE{badge = TRUE; genConfig = gen;} + } + GIVEN { + if (badge) + FlagSet(B_FLAG_BADGE_BOOST_SPATK); + else + FlagClear(B_FLAG_BADGE_BOOST_SPATK); + WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig); + PLAYER(SPECIES_WOBBUFFET) {} + OPPONENT(SPECIES_WOBBUFFET) {} + } WHEN { + TURN { MOVE(player, MOVE_THUNDER_SHOCK); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].dmg); + } FINALLY { + for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++) + { + if (gen <= GEN_3) + EXPECT_GT(results[2 * gen + 1].dmg, results[2 * gen].dmg); + else + EXPECT_EQ(results[2 * gen + 1].dmg, results[2 * gen].dmg); + } + } +} + +WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_DEFENSE boost Defense", s16 dmg) +{ + u32 badge = 0; + u32 genConfig = 0; + for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++) + { + PARAMETRIZE{badge = FALSE; genConfig = gen;} + PARAMETRIZE{badge = TRUE; genConfig = gen;} + } + + GIVEN { + if (badge) + FlagSet(B_FLAG_BADGE_BOOST_DEFENSE); + else + FlagClear(B_FLAG_BADGE_BOOST_DEFENSE); + WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig); + PLAYER(SPECIES_WOBBUFFET) {} + OPPONENT(SPECIES_WOBBUFFET) {} + } WHEN { + TURN { MOVE(opponent, MOVE_SCRATCH); } + } SCENE { + HP_BAR(player, captureDamage: &results[i].dmg); + } FINALLY { + for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++) + { + if (gen <= GEN_3) + EXPECT_LT(results[2 * gen + 1].dmg, results[2 * gen].dmg); + else + EXPECT_EQ(results[2 * gen + 1].dmg, results[2 * gen].dmg); + } + } +} + +WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_SPDEF boost Special Defense", s16 dmg) +{ + u32 badge = 0; + u32 genConfig = 0; + for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++) + { + PARAMETRIZE{badge = FALSE; genConfig = gen;} + PARAMETRIZE{badge = TRUE; genConfig = gen;} + } + + GIVEN { + if (badge) + FlagSet(B_FLAG_BADGE_BOOST_SPDEF); + else + FlagClear(B_FLAG_BADGE_BOOST_SPDEF); + WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig); + PLAYER(SPECIES_WOBBUFFET) {} + OPPONENT(SPECIES_WOBBUFFET) {} + } WHEN { + TURN { MOVE(opponent, MOVE_THUNDER_SHOCK); } + } SCENE { + HP_BAR(player, captureDamage: &results[i].dmg); + } FINALLY { + for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++) + { + if (gen <= GEN_3) + EXPECT_LT(results[2 * gen + 1].dmg, results[2 * gen].dmg); + else + EXPECT_EQ(results[2 * gen + 1].dmg, results[2 * gen].dmg); + } + } +} + +WILD_BATTLE_TEST("Badge boost: B_FLAG_BADGE_BOOST_SPEED boost Speed", s16 dmg) +{ + u32 badge = 0; + u32 genConfig = 0; + for (u32 gen = GEN_1; gen <= GEN_LATEST; gen++) + { + PARAMETRIZE{badge = FALSE; genConfig = gen;} + PARAMETRIZE{badge = TRUE; genConfig = gen;} + } + GIVEN { + if (badge) + FlagSet(B_FLAG_BADGE_BOOST_SPEED); + else + FlagClear(B_FLAG_BADGE_BOOST_SPEED); + WITH_CONFIG(GEN_CONFIG_BADGE_BOOST, genConfig); + PLAYER(SPECIES_WOBBUFFET) { Speed(100); HP(1); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(101); HP(1); } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SCRATCH);} + } THEN { + if (badge && genConfig <= GEN_3) + { + EXPECT_EQ(opponent->hp, 0); + EXPECT_EQ(player->hp, 1); + } + else + { + EXPECT_EQ(opponent->hp, 1); + EXPECT_EQ(player->hp, 0); + } + } +} From 5b4403ddfe9c2a386c55533585e27bd0fc24a300 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Thu, 23 Oct 2025 18:59:15 +0200 Subject: [PATCH 061/130] Fix stats defined in tests being overwritteng by stat change (#8018) Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- include/pokemon.h | 1 + src/pokemon.c | 87 +++++++++++++++--------- test/battle/form_change/mega_evolution.c | 14 ++-- test/battle/form_change/ultra_burst.c | 6 +- test/battle/test_runner_features.c | 55 +++++++++++++++ test/pokemon.c | 18 +++++ test/test_runner_battle.c | 14 ++++ 7 files changed, 154 insertions(+), 41 deletions(-) diff --git a/include/pokemon.h b/include/pokemon.h index f58ebbba1a..296713e59d 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -789,6 +789,7 @@ u32 GetSpeciesBaseDefense(u16 species); u32 GetSpeciesBaseSpAttack(u16 species); u32 GetSpeciesBaseSpDefense(u16 species); u32 GetSpeciesBaseSpeed(u16 species); +u32 GetSpeciesBaseStat(u16 species, u32 statIndex); const struct LevelUpMove *GetSpeciesLevelUpLearnset(u16 species); const u16 *GetSpeciesTeachableLearnset(u16 species); const u16 *GetSpeciesEggMoves(u16 species); diff --git a/src/pokemon.c b/src/pokemon.c index fdc49c7eda..dcf61b99b3 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1691,32 +1691,10 @@ static u16 CalculateBoxMonChecksumReencrypt(struct BoxPokemon *boxMon) return checksum; } -#define CALC_STAT(base, iv, ev, statIndex, field) \ -{ \ - u8 baseStat = gSpeciesInfo[species].base; \ - s32 n = (((2 * baseStat + iv + ev / 4) * level) / 100) + 5; \ - n = ModifyStatByNature(nature, n, statIndex); \ - if (B_FRIENDSHIP_BOOST == TRUE) \ - n = n + ((n * 10 * friendship) / (MAX_FRIENDSHIP * 100));\ - SetMonData(mon, field, &n); \ -} - void CalculateMonStats(struct Pokemon *mon) { s32 oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP, NULL); s32 currentHP = GetMonData(mon, MON_DATA_HP, NULL); - s32 hpIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_HP) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_HP_IV, NULL); - s32 hpEV = GetMonData(mon, MON_DATA_HP_EV, NULL); - s32 attackIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_ATK) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_ATK_IV, NULL); - s32 attackEV = GetMonData(mon, MON_DATA_ATK_EV, NULL); - s32 defenseIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_DEF) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_DEF_IV, NULL); - s32 defenseEV = GetMonData(mon, MON_DATA_DEF_EV, NULL); - s32 speedIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPEED) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPEED_IV, NULL); - s32 speedEV = GetMonData(mon, MON_DATA_SPEED_EV, NULL); - s32 spAttackIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPATK) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPATK_IV, NULL); - s32 spAttackEV = GetMonData(mon, MON_DATA_SPATK_EV, NULL); - s32 spDefenseIV = GetMonData(mon, MON_DATA_HYPER_TRAINED_SPDEF) ? MAX_PER_STAT_IVS : GetMonData(mon, MON_DATA_SPDEF_IV, NULL); - s32 spDefenseEV = GetMonData(mon, MON_DATA_SPDEF_EV, NULL); u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL); u8 friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL); s32 level = GetLevelFromMonExp(mon); @@ -1726,28 +1704,55 @@ void CalculateMonStats(struct Pokemon *mon) SetMonData(mon, MON_DATA_LEVEL, &level); + bool32 hyperTrained[NUM_STATS]; //In a battle test, hyper training flag indicates a fixed stat + s32 iv[NUM_STATS]; + s32 ev[NUM_STATS]; + for (u32 i = 0; i < NUM_STATS; i++) + { + hyperTrained[i] = GetMonData(mon, MON_DATA_HYPER_TRAINED_HP + i); + iv[i] = GetMonData(mon, MON_DATA_HP_IV + i); + ev[i] = GetMonData(mon, MON_DATA_HP_EV + i); + + if (hyperTrained[i]) + { + #if TESTING + if (gMain.inBattle) + continue; + #endif + iv[i] = MAX_PER_STAT_IVS; + } + + if (i == STAT_HP) + continue; + + u8 baseStat = GetSpeciesBaseStat(species, i); + s32 n = (((2 * baseStat + iv[i] + ev[i] / 4) * level) / 100) + 5; + n = ModifyStatByNature(nature, n, i); + if (B_FRIENDSHIP_BOOST == TRUE) + n = n + ((n * 10 * friendship) / (MAX_FRIENDSHIP * 100)); + SetMonData(mon, MON_DATA_MAX_HP + i, &n); + } + +#if TESTING + if (hyperTrained[STAT_HP] && gMain.inBattle) + return; +#endif + if (species == SPECIES_SHEDINJA) { newMaxHP = 1; } else { - s32 n = 2 * GetSpeciesBaseHP(species) + hpIV; - newMaxHP = (((n + hpEV / 4) * level) / 100) + level + 10; + s32 n = 2 * GetSpeciesBaseHP(species) + iv[STAT_HP]; + newMaxHP = (((n + ev[STAT_HP] / 4) * level) / 100) + level + 10; } gBattleScripting.levelUpHP = newMaxHP - oldMaxHP; if (gBattleScripting.levelUpHP == 0) gBattleScripting.levelUpHP = 1; - SetMonData(mon, MON_DATA_MAX_HP, &newMaxHP); - CALC_STAT(baseAttack, attackIV, attackEV, STAT_ATK, MON_DATA_ATK) - CALC_STAT(baseDefense, defenseIV, defenseEV, STAT_DEF, MON_DATA_DEF) - CALC_STAT(baseSpeed, speedIV, speedEV, STAT_SPEED, MON_DATA_SPEED) - CALC_STAT(baseSpAttack, spAttackIV, spAttackEV, STAT_SPATK, MON_DATA_SPATK) - CALC_STAT(baseSpDefense, spDefenseIV, spDefenseEV, STAT_SPDEF, MON_DATA_SPDEF) - // Since a pokemon's maxHP data could either not have // been initialized at this point or this pokemon is // just fainted, the check for oldMaxHP is important. @@ -3567,6 +3572,26 @@ u32 GetSpeciesBaseSpeed(u16 species) return gSpeciesInfo[SanitizeSpeciesId(species)].baseSpeed; } +u32 GetSpeciesBaseStat(u16 species, u32 statIndex) +{ + switch (statIndex) + { + case STAT_HP: + return GetSpeciesBaseHP(species); + case STAT_ATK: + return GetSpeciesBaseAttack(species); + case STAT_DEF: + return GetSpeciesBaseDefense(species); + case STAT_SPEED: + return GetSpeciesBaseSpeed(species); + case STAT_SPATK: + return GetSpeciesBaseSpAttack(species); + case STAT_SPDEF: + return GetSpeciesBaseSpDefense(species); + } + return 0; +} + const struct LevelUpMove *GetSpeciesLevelUpLearnset(u16 species) { const struct LevelUpMove *learnset = gSpeciesInfo[SanitizeSpeciesId(species)].levelUpLearnset; diff --git a/test/battle/form_change/mega_evolution.c b/test/battle/form_change/mega_evolution.c index 8cce1a084d..3e27a84608 100644 --- a/test/battle/form_change/mega_evolution.c +++ b/test/battle/form_change/mega_evolution.c @@ -75,10 +75,10 @@ SINGLE_BATTLE_TEST("Mega Evolution doesn't affect turn order (Gen6)") { GIVEN { WITH_CONFIG(GEN_CONFIG_MEGA_EVO_TURN_ORDER, GEN_6); - PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(105); } - OPPONENT(SPECIES_WOBBUFFET) { Speed(106); } + PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); } + OPPONENT(SPECIES_WOBBUFFET) {} } WHEN { - TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); } + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); } } SCENE { MESSAGE("The opposing Wobbuffet used Celebrate!"); MESSAGE("Gardevoir used Celebrate!"); @@ -91,10 +91,10 @@ SINGLE_BATTLE_TEST("Mega Evolution affects turn order (Gen7+)") { GIVEN { WITH_CONFIG(GEN_CONFIG_MEGA_EVO_TURN_ORDER, GEN_7); - PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(105); } - OPPONENT(SPECIES_WOBBUFFET) { Speed(106); } + PLAYER(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE);} + OPPONENT(SPECIES_WOBBUFFET) {} } WHEN { - TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); } + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); } } SCENE { MESSAGE("Gardevoir used Celebrate!"); MESSAGE("The opposing Wobbuffet used Celebrate!"); @@ -117,7 +117,7 @@ SINGLE_BATTLE_TEST("Abilities replaced by Mega Evolution do not affect turn orde MESSAGE("Sableye used Celebrate!"); MESSAGE("The opposing Wobbuffet used Celebrate!"); } THEN { - ASSUME(player->speed == 45); + ASSUME(player->speed == 105); } } diff --git a/test/battle/form_change/ultra_burst.c b/test/battle/form_change/ultra_burst.c index 9d7ec1c396..4b1bf6a8e3 100644 --- a/test/battle/form_change/ultra_burst.c +++ b/test/battle/form_change/ultra_burst.c @@ -59,10 +59,10 @@ SINGLE_BATTLE_TEST("Ultra Burst affects turn order") { GIVEN { WITH_CONFIG(GEN_CONFIG_MEGA_EVO_TURN_ORDER, GEN_7); - PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(105); } - OPPONENT(SPECIES_WOBBUFFET) { Speed(106); } + PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z);} + OPPONENT(SPECIES_WOBBUFFET) {} } WHEN { - TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); } + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST); } } SCENE { MESSAGE("Necrozma used Celebrate!"); MESSAGE("The opposing Wobbuffet used Celebrate!"); diff --git a/test/battle/test_runner_features.c b/test/battle/test_runner_features.c index bb4840799f..2c6d73abcc 100644 --- a/test/battle/test_runner_features.c +++ b/test/battle/test_runner_features.c @@ -25,3 +25,58 @@ SINGLE_BATTLE_TEST("Forced abilities activate on switch-in") MESSAGE("Kadabra's Sp. Atk was heightened!"); } } + +SINGLE_BATTLE_TEST("Setting level doesn't overwrite set stats") +{ + u32 level = 0; + + PARAMETRIZE{level = 1;} + PARAMETRIZE{level = 10;} + PARAMETRIZE{level = 50;} + PARAMETRIZE{level = 99;} + GIVEN { + PLAYER(SPECIES_WOBBUFFET) {HP(5); MaxHP(10); Attack(10); Defense(10); Speed(10); SpAttack(10); SpDefense(10); Level(level); }; + OPPONENT(SPECIES_WOBBUFFET) {Speed(1);} + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_CELEBRATE);} + } THEN { + EXPECT_EQ(player->hp, 5); + EXPECT_EQ(player->maxHP, 10); + EXPECT_EQ(player->attack, 10); + EXPECT_EQ(player->defense, 10); + EXPECT_EQ(player->speed, 10); + EXPECT_EQ(player->spAttack, 10); + EXPECT_EQ(player->spDefense, 10); + } +} + +SINGLE_BATTLE_TEST("Changing forms doesn't overwrite set stats (not HP)") +{ + GIVEN { + PLAYER(SPECIES_DIANCIE) {Attack(10); Defense(10); Speed(10); SpAttack(10); SpDefense(10); Item(ITEM_DIANCITE);} + OPPONENT(SPECIES_WOBBUFFET) {Speed(1);} + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, player); + } THEN { + EXPECT_EQ(player->attack, 10); + EXPECT_EQ(player->defense, 10); + EXPECT_EQ(player->speed, 10); + EXPECT_EQ(player->spAttack, 10); + EXPECT_EQ(player->spDefense, 10); + } +} + +SINGLE_BATTLE_TEST("Changing forms doesn't overwrite set stats (HP)") +{ + GIVEN { + PLAYER(SPECIES_TERAPAGOS) {HP(5); MaxHP(10); TeraType(TYPE_STELLAR);} + OPPONENT(SPECIES_WOBBUFFET) {} + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_CELEBRATE);} + } THEN { + EXPECT_EQ(player->hp, 5); + EXPECT_EQ(player->maxHP, 10); + } +} diff --git a/test/pokemon.c b/test/pokemon.c index 6079bd28bd..d6c0cb4d42 100644 --- a/test/pokemon.c +++ b/test/pokemon.c @@ -477,6 +477,24 @@ TEST("Optimised SetMonData") EXPECT_FASTER(optimised, vanilla); } +//Sanity check for a CalculateMonStats refactor (could be deleted or improved) +TEST("CalculateMonStats") +{ + ZeroPlayerPartyMons(); + + RUN_OVERWORLD_SCRIPT( + givemon SPECIES_WOBBUFFET, 100, item=ITEM_LEFTOVERS, ball=ITEM_MASTER_BALL, nature=NATURE_BOLD, abilityNum=2, gender=MON_MALE, hpEv=1, atkEv=2, defEv=3, speedEv=4, spAtkEv=5, spDefEv=6, hpIv=7, atkIv=8, defIv=9, speedIv=10, spAtkIv=11, spDefIv=12, move1=MOVE_SCRATCH, move2=MOVE_SPLASH, move3=MOVE_CELEBRATE, move4=MOVE_EXPLOSION, shinyMode=SHINY_MODE_ALWAYS, gmaxFactor=TRUE, teraType=TYPE_FIRE, dmaxLevel=7; + ); + + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_MAX_HP), 497); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_ATK), 71); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_DEF), 143); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPEED), 82); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPATK), 83); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPDEF), 134); + +} + TEST("BoxPokemon encryption works") { u32 raw[20] = diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 2f9dd037fb..157cec9018 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1681,7 +1681,9 @@ void Level_(u32 sourceLine, u32 level) INVALID_IF(level == 0 || level > MAX_LEVEL, "Illegal level: %d", level); SetMonData(DATA.currentMon, MON_DATA_LEVEL, &level); SetMonData(DATA.currentMon, MON_DATA_EXP, &gExperienceTables[gSpeciesInfo[species].growthRate][level]); + gMain.inBattle = TRUE; CalculateMonStats(DATA.currentMon); + gMain.inBattle = FALSE; } void MaxHP_(u32 sourceLine, u32 maxHP) @@ -1689,6 +1691,8 @@ void MaxHP_(u32 sourceLine, u32 maxHP) INVALID_IF(!DATA.currentMon, "MaxHP outside of PLAYER/OPPONENT"); INVALID_IF(maxHP == 0, "Illegal max HP: %d", maxHP); SetMonData(DATA.currentMon, MON_DATA_MAX_HP, &maxHP); + bool32 hyperTrainingFlag = TRUE; + SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_HP, &hyperTrainingFlag); } void HP_(u32 sourceLine, u32 hp) @@ -1704,6 +1708,8 @@ void Attack_(u32 sourceLine, u32 attack) INVALID_IF(!DATA.currentMon, "Attack outside of PLAYER/OPPONENT"); INVALID_IF(attack == 0, "Illegal attack: %d", attack); SetMonData(DATA.currentMon, MON_DATA_ATK, &attack); + bool32 hyperTrainingFlag = TRUE; + SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_ATK, &hyperTrainingFlag); } void Defense_(u32 sourceLine, u32 defense) @@ -1711,6 +1717,8 @@ void Defense_(u32 sourceLine, u32 defense) INVALID_IF(!DATA.currentMon, "Defense outside of PLAYER/OPPONENT"); INVALID_IF(defense == 0, "Illegal defense: %d", defense); SetMonData(DATA.currentMon, MON_DATA_DEF, &defense); + bool32 hyperTrainingFlag = TRUE; + SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_DEF, &hyperTrainingFlag); } void SpAttack_(u32 sourceLine, u32 spAttack) @@ -1718,6 +1726,8 @@ void SpAttack_(u32 sourceLine, u32 spAttack) INVALID_IF(!DATA.currentMon, "SpAttack outside of PLAYER/OPPONENT"); INVALID_IF(spAttack == 0, "Illegal special attack: %d", spAttack); SetMonData(DATA.currentMon, MON_DATA_SPATK, &spAttack); + bool32 hyperTrainingFlag = TRUE; + SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_SPATK, &hyperTrainingFlag); } void SpDefense_(u32 sourceLine, u32 spDefense) @@ -1725,6 +1735,8 @@ void SpDefense_(u32 sourceLine, u32 spDefense) INVALID_IF(!DATA.currentMon, "SpDefense outside of PLAYER/OPPONENT"); INVALID_IF(spDefense == 0, "Illegal special defense: %d", spDefense); SetMonData(DATA.currentMon, MON_DATA_SPDEF, &spDefense); + bool32 hyperTrainingFlag = TRUE; + SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_SPDEF, &hyperTrainingFlag); } void Speed_(u32 sourceLine, u32 speed) @@ -1732,6 +1744,8 @@ void Speed_(u32 sourceLine, u32 speed) INVALID_IF(!DATA.currentMon, "Speed outside of PLAYER/OPPONENT"); INVALID_IF(speed == 0, "Illegal speed: %d", speed); SetMonData(DATA.currentMon, MON_DATA_SPEED, &speed); + bool32 hyperTrainingFlag = TRUE; + SetMonData(DATA.currentMon, MON_DATA_HYPER_TRAINED_SPEED, &hyperTrainingFlag); DATA.hasExplicitSpeeds = TRUE; DATA.explicitSpeeds[DATA.currentSide] |= 1 << DATA.currentPartyIndex; } From 8c875224153a1068db01337f55979db7b1655a9a Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Fri, 24 Oct 2025 11:50:19 +0200 Subject: [PATCH 062/130] Fix toxic debris setting hazards on the wrong side when hit by an ally (#8026) --- data/battle_scripts_1.s | 5 ++--- src/battle_util.c | 5 ++++- test/battle/ability/toxic_debris.c | 20 ++++++++++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index db4f7e65e3..970c16179d 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6048,9 +6048,8 @@ BattleScript_ToxicDebrisActivates:: printstring STRINGID_POISONSPIKESSCATTERED waitmessage B_WAIT_TIME_LONG BattleScript_ToxicDebrisRet: - copybyte sBATTLER, gBattlerTarget - copybyte gBattlerTarget, gBattlerAttacker - copybyte gBattlerAttacker, sBATTLER + restoretarget + restoreattacker return BattleScript_EarthEaterActivates:: diff --git a/src/battle_util.c b/src/battle_util.c index d5cad634d9..7744cb78e3 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4959,7 +4959,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && IsBattlerTurnDamaged(gBattlerTarget) && (gSideTimers[GetBattlerSide(gBattlerAttacker)].toxicSpikesAmount != 2)) { - SWAP(gBattlerAttacker, gBattlerTarget, i); + SaveBattlerTarget(gBattlerTarget); + SaveBattlerAttacker(gBattlerAttacker); + gBattlerAttacker = gBattlerTarget; + gBattlerTarget = BATTLE_OPPOSITE(gBattlerAttacker); BattleScriptCall(BattleScript_ToxicDebrisActivates); effect++; } diff --git a/test/battle/ability/toxic_debris.c b/test/battle/ability/toxic_debris.c index 61e128e985..4661849905 100644 --- a/test/battle/ability/toxic_debris.c +++ b/test/battle/ability/toxic_debris.c @@ -120,3 +120,23 @@ SINGLE_BATTLE_TEST("Air Balloon is popped after Toxic Debris activates") MESSAGE("Glimmora's Air Balloon popped!"); } } + +DOUBLE_BATTLE_TEST("Toxic Debris sets Toxic Spikes on the opposing side even when hit by an ally") +{ + struct BattlePokemon *user = NULL; + + PARAMETRIZE{ user = opponentLeft; } + PARAMETRIZE{ user = opponentRight; } + PARAMETRIZE{ user = playerRight; } + GIVEN { + PLAYER(SPECIES_GLIMMORA) { Ability(ABILITY_TOXIC_DEBRIS); } + PLAYER(SPECIES_WYNAUT) { } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT) { } + } WHEN { + TURN { MOVE(user, MOVE_SCRATCH, target: playerLeft); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_TOXIC_DEBRIS); + MESSAGE("Poison spikes were scattered on the ground all around the opposing team!"); + } +} From 083add81275c0bcc868abec619cdda975e534a9d Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:50:23 +0200 Subject: [PATCH 063/130] Adds missing alive check for Rapid Spin (#8024) --- 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 7cd573f5ae..a4e543bfc1 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5865,7 +5865,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) } break; case EFFECT_RAPID_SPIN: - if (IsBattlerTurnDamaged(gBattlerTarget)) + if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) { BattleScriptCall(BattleScript_RapidSpinAway); effect = TRUE; From 8e7688957990a26ef49642c67faa6c36dc9d3ec0 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:50:41 +0200 Subject: [PATCH 064/130] Fixes visual glitch after Misty Explosion (#8022) --- 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 a4e543bfc1..91b91be301 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5839,6 +5839,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) } break; case EFFECT_EXPLOSION: + case EFFECT_MISTY_EXPLOSION: if (!IsAbilityOnField(ABILITY_DAMP)) { gBattleStruct->moveDamage[gBattlerAttacker] = 0; From 8ea947d5a1612e7bcbcdb7e1290ac92f35a42c13 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:53:34 +0200 Subject: [PATCH 065/130] Fixes Protosynthesis not activating after weather was reset (#8021) --- include/battle_util.h | 3 +- src/battle_script_commands.c | 15 +++--- src/battle_util.c | 68 ++++++++++++++-------------- test/battle/ability/protosynthesis.c | 18 ++++++++ 4 files changed, 61 insertions(+), 43 deletions(-) diff --git a/include/battle_util.h b/include/battle_util.h index 961ae450f4..3531f0eb6b 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -242,7 +242,8 @@ void TryClearRageAndFuryCutter(void); enum MoveCanceller AtkCanceller_MoveSuccessOrder(void); void SetAtkCancellerForCalledMove(void); bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2); -bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, bool32 viaAbility); +bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, u32 ability); +bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag); bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, u32 move, enum FunctionCallOption option); bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 move, u32 moveType, enum FunctionCallOption option); u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 91b91be301..dc6ce74ecd 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3831,7 +3831,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai } break; } - if (TryChangeBattleWeather(gBattlerAttacker, weather, FALSE)) + if (TryChangeBattleWeather(gBattlerAttacker, weather, ABILITY_NONE)) { gBattleCommunication[MULTISTRING_CHOOSER] = msg; BattleScriptPush(gBattlescriptCurrInstr + 1); @@ -9744,7 +9744,7 @@ static void Cmd_setfieldweather(void) u8 battleWeatherId = cmd->weather; - if (!TryChangeBattleWeather(gBattlerAttacker, battleWeatherId, FALSE)) + if (!TryChangeBattleWeather(gBattlerAttacker, battleWeatherId, ABILITY_NONE)) { gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED; @@ -15338,12 +15338,13 @@ void BS_SetTerrain(void) default: break; } - if (statusFlag) + if (gBattleStruct->isSkyBattle) { - enum ItemHoldEffect atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); - gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; - gFieldStatuses |= statusFlag; - gFieldTimers.terrainTimer = gBattleTurnCounter + ((atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5); + gBattlescriptCurrInstr = cmd->jumpInstr; + } + else if (statusFlag) + { + TryChangeBattleTerrain(gBattlerAttacker, statusFlag); gBattlescriptCurrInstr = cmd->nextInstr; } else diff --git a/src/battle_util.c b/src/battle_util.c index 7744cb78e3..8700fe2b2e 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2791,24 +2791,24 @@ bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2 } } -bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, bool32 viaAbility) +bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, u32 ability) { - u16 battlerAbility = GetBattlerAbility(battler); - if (gBattleWeather & sBattleWeatherInfo[battleWeatherId].flag) { return FALSE; } else if (gBattleWeather & B_WEATHER_PRIMAL_ANY - && battlerAbility != ABILITY_DESOLATE_LAND - && battlerAbility != ABILITY_PRIMORDIAL_SEA - && battlerAbility != ABILITY_DELTA_STREAM) + && ability != ABILITY_DESOLATE_LAND + && ability != ABILITY_PRIMORDIAL_SEA + && ability != ABILITY_DELTA_STREAM) { return FALSE; } - else if (GetGenConfig(GEN_CONFIG_ABILITY_WEATHER) < GEN_6 && viaAbility) + else if (GetGenConfig(GEN_CONFIG_ABILITY_WEATHER) < GEN_6 && ability != ABILITY_NONE) { gBattleWeather = sBattleWeatherInfo[battleWeatherId].flag; + for (u32 i = 0; i < gBattlersCount; i++) + gDisableStructs[i].weatherAbilityDone = FALSE; return TRUE; } else @@ -2821,25 +2821,29 @@ bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, bool32 viaAbilit gWishFutureKnock.weatherDuration = 8; else gWishFutureKnock.weatherDuration = 5; + for (u32 i = 0; i < gBattlersCount; i++) + gDisableStructs[i].weatherAbilityDone = FALSE; return TRUE; } return FALSE; } -static bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag, u16 *timer) +bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag) { - if ((!(gFieldStatuses & statusFlag) && (!gBattleStruct->isSkyBattle))) + if (gBattleStruct->isSkyBattle) + return FALSE; + + if (!(gFieldStatuses & statusFlag)) { gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; gFieldStatuses |= statusFlag; - gDisableStructs[battler].terrainAbilityDone = FALSE; - + for (u32 i = 0; i < gBattlersCount; i++) + gDisableStructs[i].terrainAbilityDone = FALSE; if (GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_TERRAIN_EXTENDER) - *timer = gBattleTurnCounter + 8; + gFieldTimers.terrainTimer = gBattleTurnCounter + 8; else - *timer = gBattleTurnCounter + 5; - + gFieldTimers.terrainTimer = gBattleTurnCounter + 5; gBattleScripting.battler = battler; return TRUE; } @@ -3838,7 +3842,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_DRIZZLE: - if (TryChangeBattleWeather(battler, BATTLE_WEATHER_RAIN, TRUE)) + if (TryChangeBattleWeather(battler, BATTLE_WEATHER_RAIN, gLastUsedAbility)) { BattleScriptPushCursorAndCallback(BattleScript_DrizzleActivates); effect++; @@ -3851,7 +3855,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_SAND_STREAM: - if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SANDSTORM, TRUE)) + if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SANDSTORM, gLastUsedAbility)) { BattleScriptPushCursorAndCallback(BattleScript_SandstreamActivates); effect++; @@ -3863,8 +3867,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; + case ABILITY_ORICHALCUM_PULSE: case ABILITY_DROUGHT: - if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SUN, TRUE)) + if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SUN, gLastUsedAbility)) { BattleScriptPushCursorAndCallback(BattleScript_DroughtActivates); effect++; @@ -3877,12 +3882,12 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_SNOW_WARNING: - if (GetGenConfig(GEN_SNOW_WARNING) >= GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_SNOW, TRUE)) + if (GetGenConfig(GEN_SNOW_WARNING) >= GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_SNOW, gLastUsedAbility)) { BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesSnow); effect++; } - else if (GetGenConfig(GEN_SNOW_WARNING) < GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_HAIL, TRUE)) + else if (GetGenConfig(GEN_SNOW_WARNING) < GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_HAIL, gLastUsedAbility)) { BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesHail); effect++; @@ -3896,28 +3901,28 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 break; case ABILITY_ELECTRIC_SURGE: case ABILITY_HADRON_ENGINE: - if (TryChangeBattleTerrain(battler, STATUS_FIELD_ELECTRIC_TERRAIN, &gFieldTimers.terrainTimer)) + if (TryChangeBattleTerrain(battler, STATUS_FIELD_ELECTRIC_TERRAIN)) { BattleScriptPushCursorAndCallback(BattleScript_ElectricSurgeActivates); effect++; } break; case ABILITY_GRASSY_SURGE: - if (TryChangeBattleTerrain(battler, STATUS_FIELD_GRASSY_TERRAIN, &gFieldTimers.terrainTimer)) + if (TryChangeBattleTerrain(battler, STATUS_FIELD_GRASSY_TERRAIN)) { BattleScriptPushCursorAndCallback(BattleScript_GrassySurgeActivates); effect++; } break; case ABILITY_MISTY_SURGE: - if (TryChangeBattleTerrain(battler, STATUS_FIELD_MISTY_TERRAIN, &gFieldTimers.terrainTimer)) + if (TryChangeBattleTerrain(battler, STATUS_FIELD_MISTY_TERRAIN)) { BattleScriptPushCursorAndCallback(BattleScript_MistySurgeActivates); effect++; } break; case ABILITY_PSYCHIC_SURGE: - if (TryChangeBattleTerrain(battler, STATUS_FIELD_PSYCHIC_TERRAIN, &gFieldTimers.terrainTimer)) + if (TryChangeBattleTerrain(battler, STATUS_FIELD_PSYCHIC_TERRAIN)) { BattleScriptPushCursorAndCallback(BattleScript_PsychicSurgeActivates); effect++; @@ -4018,21 +4023,21 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_DESOLATE_LAND: - if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SUN_PRIMAL, TRUE)) + if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SUN_PRIMAL, gLastUsedAbility)) { BattleScriptPushCursorAndCallback(BattleScript_DesolateLandActivates); effect++; } break; case ABILITY_PRIMORDIAL_SEA: - if (TryChangeBattleWeather(battler, BATTLE_WEATHER_RAIN_PRIMAL, TRUE)) + if (TryChangeBattleWeather(battler, BATTLE_WEATHER_RAIN_PRIMAL, gLastUsedAbility)) { BattleScriptPushCursorAndCallback(BattleScript_PrimordialSeaActivates); effect++; } break; case ABILITY_DELTA_STREAM: - if (TryChangeBattleWeather(battler, BATTLE_WEATHER_STRONG_WINDS, TRUE)) + if (TryChangeBattleWeather(battler, BATTLE_WEATHER_STRONG_WINDS, gLastUsedAbility)) { BattleScriptPushCursorAndCallback(BattleScript_DeltaStreamActivates); effect++; @@ -4074,13 +4079,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; - case ABILITY_ORICHALCUM_PULSE: - if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SUN, TRUE)) - { - BattleScriptPushCursorAndCallback(BattleScript_DroughtActivates); - effect++; - } - break; case ABILITY_SUPREME_OVERLORD: if (!gSpecialStatuses[battler].switchInAbilityDone) { @@ -4851,7 +4849,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 BattleScriptCall(BattleScript_BlockedByPrimalWeatherRet); effect++; } - else if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SANDSTORM, TRUE)) + else if (TryChangeBattleWeather(battler, BATTLE_WEATHER_SANDSTORM, gLastUsedAbility)) { gBattleScripting.battler = battler; BattleScriptCall(BattleScript_SandSpitActivates); @@ -4909,7 +4907,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!gProtectStructs[gBattlerAttacker].confusionSelfDmg && IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerTarget) - && TryChangeBattleTerrain(gBattlerTarget, STATUS_FIELD_GRASSY_TERRAIN, &gFieldTimers.terrainTimer)) + && TryChangeBattleTerrain(gBattlerTarget, STATUS_FIELD_GRASSY_TERRAIN)) { BattleScriptCall(BattleScript_SeedSowerActivates); effect++; diff --git a/test/battle/ability/protosynthesis.c b/test/battle/ability/protosynthesis.c index 9bf6d85f43..bad29b4c9a 100644 --- a/test/battle/ability/protosynthesis.c +++ b/test/battle/ability/protosynthesis.c @@ -200,3 +200,21 @@ SINGLE_BATTLE_TEST("Protosynthesis doesn't activate if Cloud Nine/Air Lock is on NOT ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); } } + +SINGLE_BATTLE_TEST("Protosynthesis activates after weather was reset") +{ + GIVEN { + PLAYER(SPECIES_WALKING_WAKE) { Ability(ABILITY_PROTOSYNTHESIS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SUNNY_DAY); } + TURN { MOVE(player, MOVE_RAIN_DANCE); } + TURN { MOVE(player, MOVE_SUNNY_DAY); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player); + ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player); + ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + } +} From eb1be34d4728f5a5b764758b86c9bda8a14ec8cc Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 24 Oct 2025 18:53:53 +0200 Subject: [PATCH 066/130] Fix Salt Cure script (#8005) --- data/battle_scripts_1.s | 1 + src/battle_end_turn.c | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 970c16179d..36c3712854 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -408,6 +408,7 @@ BattleScript_SaltCureExtraDamage:: printstring STRINGID_TARGETISHURTBYSALTCURE waitmessage B_WAIT_TIME_LONG tryfaintmon BS_ATTACKER + tryrestorehpberry end2 BattleScript_HurtTarget_NoString: diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index da7607d9c4..0c7807bece 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -808,9 +808,9 @@ static bool32 HandleEndTurnSaltCure(u32 battler) && !IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_MAGIC_GUARD)) { if (IS_BATTLER_ANY_TYPE(battler, TYPE_STEEL, TYPE_WATER)) - gBattleStruct->moveDamage[battler] = gBattleMons[battler].maxHP / 4; + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 4; else - gBattleStruct->moveDamage[battler] = gBattleMons[battler].maxHP / 8; + gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8; if (gBattleStruct->moveDamage[battler] == 0) gBattleStruct->moveDamage[battler] = 1; PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SALT_CURE); From b4041535cf78d7bd11e9981cbb665e779af9a050 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Fri, 24 Oct 2025 18:55:32 +0200 Subject: [PATCH 067/130] Fix battle dome bug (again) (#8007) --- data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc b/data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc index 8e01660a31..3406d4121c 100644 --- a/data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc +++ b/data/maps/BattleFrontier_BattleDomePreBattleRoom/scripts.inc @@ -18,7 +18,7 @@ BattleFrontier_BattleDomePreBattleRoom_OnFrame: BattleFrontier_BattleDomePreBattleRoom_EventScript_EnterRoom:: goto_if_eq VAR_0x8006, 1, BattleFrontier_BattleDomePreBattleRoom_EventScript_ReturnFromBattle - delay 0 + delay 1 frontier_set FRONTIER_DATA_RECORD_DISABLED, TRUE setvar VAR_TEMP_0, 1 applymovement LOCALID_PLAYER, BattleFrontier_BattleDomePreBattleRoom_Movement_PlayerEnter From 6eaa09bf5d511810d4f806a620f4a70a55eb7670 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Sat, 25 Oct 2025 14:37:17 +0200 Subject: [PATCH 068/130] Add error messages for trying to send an illegal mon to the PC and fixes index in double wild battles (#7982) Co-authored-by: Hedara --- include/strings.h | 2 ++ src/party_menu.c | 16 +++++++++++++--- src/strings.c | 2 ++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/include/strings.h b/include/strings.h index a9cddee25b..ec0072f062 100644 --- a/include/strings.h +++ b/include/strings.h @@ -2425,5 +2425,7 @@ extern const u8 gText_Rename[]; // change nickname from summary screen // Switch Caught Mon into Party extern const u8 gText_CannotSendMonToBoxHM[]; +extern const u8 gText_CannotSendMonToBoxActive[]; +extern const u8 gText_CannotSendMonToBoxPartner[]; #endif // GUARD_STRINGS_H diff --git a/src/party_menu.c b/src/party_menu.c index 04bf04a9df..8a9532eaea 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -1512,11 +1512,21 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr) case PARTY_ACTION_SEND_MON_TO_BOX: { u8 partyId = GetPartyIdFromBattleSlot((u8)*slotPtr); - if (partyId == 0 || ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && partyId == 2) - || ((gBattleTypeFlags & BATTLE_TYPE_MULTI) && partyId >= (PARTY_SIZE / 2))) + if (partyId == 0 || ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && partyId == 1)) { - // Can't select if mon is currently on the field, or doesn't belong to you + // Can't select if mon is currently on the field PlaySE(SE_FAILURE); + DisplayPartyMenuMessage(gText_CannotSendMonToBoxActive, FALSE); + ScheduleBgCopyTilemapToVram(2); + gTasks[taskId].func = Task_ReturnToChooseMonAfterText; + } + else if ((gBattleTypeFlags & BATTLE_TYPE_MULTI) && partyId >= (PARTY_SIZE / 2)) + { + // Can't select if mon doesn't belong to you + PlaySE(SE_FAILURE); + DisplayPartyMenuMessage(gText_CannotSendMonToBoxPartner, FALSE); + ScheduleBgCopyTilemapToVram(2); + gTasks[taskId].func = Task_ReturnToChooseMonAfterText; } else if (DoesSelectedMonKnowHM((u8 *)slotPtr)) { diff --git a/src/strings.c b/src/strings.c index fd92b31e92..1c8f252fcf 100644 --- a/src/strings.c +++ b/src/strings.c @@ -1300,3 +1300,5 @@ const u8 gText_PM[] = _("PM"); const u8 gText_Relearn[] = _("{START_BUTTON} RELEARN"); // future note: don't decap this, because it mimics the summary screen BG graphics which will not get decapped const u8 gText_Rename[] = _("RENAME"); const u8 gText_CannotSendMonToBoxHM[] = _("Cannot send that mon to the box,\nbecause it knows a HM move.{PAUSE_UNTIL_PRESS}"); +const u8 gText_CannotSendMonToBoxActive[] = _("Cannot send an active battler\nto the box.{PAUSE_UNTIL_PRESS}"); +const u8 gText_CannotSendMonToBoxPartner[] = _("Cannot send a mon that doesn't,\nbelong to you to the box.{PAUSE_UNTIL_PRESS}"); From 9fa6cd6a4cd10fb370c489959d7fa2e3317bb755 Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Sat, 25 Oct 2025 14:38:58 +0200 Subject: [PATCH 069/130] Add test to detect save file shifting (#8030) --- test/save.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 test/save.c diff --git a/test/save.c b/test/save.c new file mode 100644 index 0000000000..4e3000a794 --- /dev/null +++ b/test/save.c @@ -0,0 +1,35 @@ +#include "global.h" +#include "pokemon_storage_system.h" +#include "test/test.h" + +// If you would like to ensure save compatibility, update the values below with those for your hack. You can find these through the debug menu. +// Please note that this simple check is not 100% foolproof, but should be able to catch most unintended shifts. +#define T_SAVEBLOCK1_SIZE 15568 +#define T_SAVEBLOCK2_SIZE 3884 +#define T_SAVEBLOCK3_SIZE 4 +#define T_POKEMONSTORAGE_SIZE 34144 + +TEST("SaveBlock1 is backwards compatible") +{ + EXPECT_EQ(sizeof(struct SaveBlock1), T_SAVEBLOCK1_SIZE); +} + +TEST("SaveBlock2 is backwards compatible") +{ + EXPECT_EQ(sizeof(struct SaveBlock2), T_SAVEBLOCK2_SIZE); +} + +TEST("SaveBlock3 is backwards compatible") +{ + EXPECT_EQ(sizeof(struct SaveBlock3), T_SAVEBLOCK3_SIZE); +} + +TEST("PokemonStorage is backwards compatible") +{ + EXPECT_EQ(sizeof(struct PokemonStorage), T_POKEMONSTORAGE_SIZE); +} + +#undef T_SAVEBLOCK1_SIZE +#undef T_SAVEBLOCK2_SIZE +#undef T_SAVEBLOCK3_SIZE +#undef T_POKEMONSTORAGE_SIZE From 7b3a2f3aaf4777ea0401f4b52b65289f6a365e7b Mon Sep 17 00:00:00 2001 From: khbsd Date: Sat, 25 Oct 2025 21:15:03 -0500 Subject: [PATCH 070/130] fix: hypertraining a stat now optionally reflects in the summary screen (#8035) --- include/config/summary_screen.h | 1 + src/pokemon_summary_screen.c | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/include/config/summary_screen.h b/include/config/summary_screen.h index 4b5f434cb3..07bae63533 100644 --- a/include/config/summary_screen.h +++ b/include/config/summary_screen.h @@ -8,6 +8,7 @@ #define P_SUMMARY_SCREEN_RENAME TRUE // If TRUE, an option to change Pokémon nicknames replaces the cancel prompt on the summary screen info page. #define P_SUMMARY_SCREEN_IV_EV_INFO FALSE // If TRUE, will allow player to cycle through the Stats, IVs, and EVs in the summary screen skills page. #define P_SUMMARY_SCREEN_IV_EV_BOX_ONLY FALSE // If TRUE, will allow player to cycle through the Stats, IVs, and EVs in the summary screen skills page, but only in the PC storage box. +#define P_SUMMARY_SCREEN_IV_HYPERTRAIN TRUE // If TRUE, stats that have been hyper trained will show as 31/S when viewing them in the summary screen #define P_SUMMARY_SCREEN_IV_EV_TILESET FALSE // If TRUE, loads an alternate tileset to allow changing the "STATS" label in the summary screen skills page. Note: if it's still loading the alternate tileset after changing this and recompiling, you may need a `make clean` before compilation. #define P_SUMMARY_SCREEN_IV_EV_VALUES FALSE // If TRUE, will show the actual IV value instead of the letter grade. /* diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 1f69af7e96..877343ea68 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -334,6 +334,7 @@ static const u8 *GetLetterGrade(u32 stat); static u8 AddWindowFromTemplateList(const struct WindowTemplate *template, u8 templateId); static u8 IncrementSkillsStatsMode(u8 mode); static void ClearStatLabel(u32 length, u32 statsCoordX, u32 statsCoordY); +u32 GetAdjustedIvData(struct Pokemon *mon, u32 stat); static const struct BgTemplate sBgTemplates[] = { @@ -1173,6 +1174,13 @@ static void DestroyCategoryIcon(void) sMonSummaryScreen->categoryIconSpriteId = 0xFF; } +u32 GetAdjustedIvData(struct Pokemon *mon, u32 stat) +{ + if (P_SUMMARY_SCREEN_IV_HYPERTRAIN && GetMonData(mon, MON_DATA_HYPER_TRAINED_HP + stat)) + return MAX_PER_STAT_IVS; + return GetMonData(mon, MON_DATA_HP_IV + stat); +} + void ShowPokemonSummaryScreen(u8 mode, void *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void)) { sMonSummaryScreen = AllocZeroed(sizeof(*sMonSummaryScreen)); @@ -1864,12 +1872,12 @@ void ExtractMonSkillStatsData(struct Pokemon *mon, struct PokeSummary *sum) void ExtractMonSkillIvData(struct Pokemon *mon, struct PokeSummary *sum) { - sum->currentHP = GetMonData(mon, MON_DATA_HP_IV); - sum->atk = GetMonData(mon, MON_DATA_ATK_IV); - sum->def = GetMonData(mon, MON_DATA_DEF_IV); - sum->spatk = GetMonData(mon, MON_DATA_SPATK_IV); - sum->spdef = GetMonData(mon, MON_DATA_SPDEF_IV); - sum->speed = GetMonData(mon, MON_DATA_SPEED_IV); + sum->currentHP = GetAdjustedIvData(mon, STAT_HP); + sum->atk = GetAdjustedIvData(mon, STAT_ATK); + sum->def = GetAdjustedIvData(mon, STAT_DEF); + sum->spatk = GetAdjustedIvData(mon, STAT_SPATK); + sum->spdef = GetAdjustedIvData(mon, STAT_SPDEF); + sum->speed = GetAdjustedIvData(mon, STAT_SPEED); } void ExtractMonSkillEvData(struct Pokemon *mon, struct PokeSummary *sum) From 95d98305dd52ad0763ac86cfa52783d982ce21ba Mon Sep 17 00:00:00 2001 From: Raymond Dodge Date: Sun, 26 Oct 2025 06:45:40 -0400 Subject: [PATCH 071/130] Add camera-facing right-walking Krabby and Kingler follower sprites (#7881) --- graphics/pokemon/kingler/overworld.png | Bin 914 -> 1046 bytes graphics/pokemon/krabby/overworld.png | Bin 728 -> 567 bytes src/data/pokemon/species_info/gen_1_families.h | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/graphics/pokemon/kingler/overworld.png b/graphics/pokemon/kingler/overworld.png index 135f79b3265f3a35e81c0896d3f955b571b4adb3..c67ce3a53af2d0dd838dbcff67818aa40e67ecfc 100644 GIT binary patch delta 1028 zcmV+f1pE7v2bKsWiBL{Q4GJ0x0000DNk~Le000310000W1Oos701t<|*8l(j9+5I4 ze@mlhnE(I-vq?ljRCr$Pl`(JQHWYv*z(0UbBy(Oxx!onI3A%|E zH;6W{Uc7&hge1#X0h;bn5DDW=&fXCcf8xO_;rjsq`+}{5*1}Y+^Mi0@QH+GE)vAC~ z00pguTqyv;wU8c3sWMubsFi!B5zeR?F$JN}nk!EZJ0(g{!B9G&$0y(a4t^+ay zfl;^vwqa=#;wxoX;+e*AB=q}KsDed869s5p#w<|CQAnIAKwgCDE@S>BLx#k2fAsF9 z`JP2W&j}!tu%<^&iTz~zOmS_Rg^kL6kQZ7fv}u(i>m7)|;D4fmStqnXn1W{mu-A-) z`;r27EwLM(tK4eYTr&oFA!MfHVwYr!A(y~CyCC$bJlQ+WgAju}=fE{1VIe4>+O-gI zVnF3348)l-r4I_iy2Sty0(4*ue{ul^1CWCdg8~rCz3J_swNiaOzY z46wB>J^LVRO1@inr*bs@*s3UferjLctNMti+RvB!ovEdUARTzi_!yeuLC<_DZJ2Fj!!82dmH#I zh?iDqQxzfzGYx@jBYQ76=$}75-*6`+E{8f2-hKH?pT5ho-?m;@Upn{FZC?YI7=%1r zT34iPeQ`owZx^0e5I#PBe?y5tAg;~AM)LaG?d|D%jKWD*PFQI8+PE7q^H!o@^BdH6 z&r01rw=g*#rkUyQcs$xuX^vNXF#Rl);&ia*aao@QCq|+5LMy@d%?y$`?7Lq+RmGP- z2jTiQ6U;2xVi=F(VcT7Cw>`{;(=?3dap_5i=@fDLN9TpZ>x1LXe|hGwZ+vs#onETq z@LXM*ZX(Jwx=YSsiZtRyli#!rbcztxj+j}!OHiMnHcPz#0`FEv$Jl=Q0 zakntwel`jtSvoevU7yTB7B2_0_%|a-c^pBd9wB5R%>0kf-~blyKjv@B<^N!t(|%0=00000oQd{w)L)K7q~4Sz_(%@jli9{@}0hukj> zyjesUe4EnghY+QQ9-vBYKga+_j*HpMS zuzE)lM-vM%5 zV|vhabO)3m1b=n1?8?HU0V_W$pgwWk6B286!C4=d)B%Wi2TEt$>6+63HH=bVpwl~p z6_Aupy-5Q2!lI(H&ugzYgCn3WqnJ-4Xr_NDg-w8b2K=hqVqrQNJOgS5H-NJNzUsrV zbpT?rL||(p=h-&)6|Tz!5x?t_Kx4Xq2FPH&B;=zQIDZ_Vtlf)#!8iaXC028UG}E=T z1#k(wTj|-)oIaodvY+H^fLsZssxXsh9oH#9S}LGf901b7wmoGELY*66xPq8+BEN1< zdIhX}hXXDKQqr^+y%{|CG+c=K-zQ?8X#lakm^l9oALL0XYc^b zoO$0p(YVMvUHS$(S%InYU9HbHJ{T&mIE75a=(4{jr! zNr#hE!BMDsa`|i!sGShK_SQ~5e&lW=l8phsB=oW*o#&!&y57&a84OH0nw)_BTr8~0nChA*p48j9i1SK)8{`Q|@szW@^Y V6i2-A!q5N!002ovPDHLkV1iy~>6g-l;nG{gfBZzoH7JlD;`Rtqg%-GUJd2<|~U|%quS(u|`(d=bzudzFi0G zPZ!6{NQ|eksEIIPbpa2BaKvwrEh(vC$*Lh7&-I73@5_w!+4DI?t|qOg5t+@NXClm0 z!XMF~)e;7*+q@75t>;BJF4*JtxMdXz#=>!$4IAFv(}0Qw#X%tsqFG4*h=k)CE6)jR cczb{32OcVXK-Skw7ZwCq0A(3Y zBq*$KusE=y;lzmxFJAmu&=8Pid&Cwf!IEJ=%)F@g$1~CU0YtcUE3G?e~xkY?e3p&2orQo2&b71bi;?x8|U!O)F zu`Vv3=Izyq`*N^JEqHZJ#MM?#F!%Bgz8B6O^X4yzzIU^{*x6y~s_oZ}XJxQ^ ze?PD-@AVCn#DxJn-%l=JxmC_oa+gW0A=*M%@s(UgZtep9y-ZJk>y&uL_e|ojC@WbV zBvragk?|#8MyyYLeL`TX9p_(()RiC3R6erOk+mweeC%BodoFegW5rCdWqlndRo4ql z_^8#M_%A+2@txp&NBQZekNj9$wej1P^XsRy&0C~gcr{Du zxOB&{r?MyXcfJqo>FepAD)>-~!-DIhW4R~8q|TRon=VLh_& Date: Sun, 26 Oct 2025 14:40:55 +0100 Subject: [PATCH 072/130] Fix PC turning on/off animation not working in Battle Frontier (#8048) --- src/field_specials.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/field_specials.c b/src/field_specials.c index 09fca47899..cd7a2a798f 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -22,12 +22,13 @@ #include "item.h" #include "item_icon.h" #include "link.h" -#include "load_save.h" #include "list_menu.h" +#include "load_save.h" #include "main.h" -#include "mystery_gift.h" #include "match_call.h" #include "menu.h" +#include "metatile_behavior.h" +#include "mystery_gift.h" #include "overworld.h" #include "party_menu.h" #include "pokeblock.h" @@ -984,7 +985,7 @@ void FieldShowRegionMap(void) static bool32 IsBuildingPCTile(u32 tileId) { - return gMapHeader.mapLayout->primaryTileset == &gTileset_Building && (tileId == METATILE_Building_PC_On || tileId == METATILE_Building_PC_Off); + return (MetatileBehavior_IsPC(UNPACK_BEHAVIOR(GetMetatileAttributesById(tileId)))); } static bool32 IsPlayerHousePCTile(u32 tileId) From a9486a5521af2cf2a1cab78df30fcc60411ce0cf Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Sun, 26 Oct 2025 15:28:40 +0100 Subject: [PATCH 073/130] Fix battle arena counting all judges loss for the opponent (#8046) --- src/battle_script_commands.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index dc6ce74ecd..00963383b1 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -17102,11 +17102,14 @@ void BS_ArenaJudgmentWindow(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void SetArenMonLostValues(u32 battler) +static void SetArenMonLostValues(u32 battler, u32 side) { gBattleMons[battler].hp = 0; gHitMarker |= HITMARKER_FAINTED(battler); - gBattleStruct->arenaLostOpponentMons |= 1u << gBattlerPartyIndexes[battler]; + if (side == B_SIDE_PLAYER) + gBattleStruct->arenaLostPlayerMons |= 1u << gBattlerPartyIndexes[battler]; + else + gBattleStruct->arenaLostOpponentMons |= 1u << gBattlerPartyIndexes[battler]; gDisableStructs[battler].truantSwitchInHack = TRUE; } @@ -17115,22 +17118,22 @@ static void SetArenMonLostValues(u32 battler) void BS_ArenaOpponentMonLost(void) { NATIVE_ARGS(); - SetArenMonLostValues(opponentMon); + SetArenMonLostValues(opponentMon, B_SIDE_OPPONENT); gBattlescriptCurrInstr = cmd->nextInstr; } void BS_ArenaPlayerMonLost(void) { NATIVE_ARGS(); - SetArenMonLostValues(playerMon); + SetArenMonLostValues(playerMon, B_SIDE_PLAYER); gBattlescriptCurrInstr = cmd->nextInstr; } void BS_ArenaBothMonsLost(void) { NATIVE_ARGS(); - SetArenMonLostValues(playerMon); - SetArenMonLostValues(opponentMon); + SetArenMonLostValues(playerMon, B_SIDE_PLAYER); + SetArenMonLostValues(opponentMon, B_SIDE_OPPONENT); gBattlescriptCurrInstr = cmd->nextInstr; } #undef playerMon From 6faf35dde90f9fd46f15fd7baddadaaaad825f9b Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Sun, 26 Oct 2025 22:35:27 +0100 Subject: [PATCH 074/130] Fix emergency exit not triggering properly during wild battles (#8037) --- src/battle_end_turn.c | 3 +- src/battle_script_commands.c | 3 +- test/battle/ability/emergency_exit.c | 66 ++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index 0c7807bece..d423659be9 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -311,13 +311,12 @@ static bool32 HandleEndTurnEmergencyExit(u32 battler) && IsBattlerAlive(battler) && (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) && !(gBattleTypeFlags & BATTLE_TYPE_ARENA) - && CountUsablePartyMons(battler) > 0 && gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP) // Not currently held by Sky Drop { gBattlerAbility = battler; gLastUsedAbility = ability; - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || IsOnPlayerSide(battler)) + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) BattleScriptExecute(BattleScript_EmergencyExitEnd2); else BattleScriptExecute(BattleScript_EmergencyExitWildEnd2); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 00963383b1..b8c12c1c5e 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1079,7 +1079,6 @@ bool32 EmergencyExitCanBeTriggered(u32 battler) && HadMoreThanHalfHpNowDoesnt(battler) && (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) && !(gBattleTypeFlags & BATTLE_TYPE_ARENA) - && CountUsablePartyMons(battler) > 0 && gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP) return TRUE; @@ -6696,7 +6695,7 @@ static void Cmd_moveend(void) effect = TRUE; gBattleScripting.battler = battler; - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || IsOnPlayerSide(battler)) + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) BattleScriptCall(BattleScript_EmergencyExit); else BattleScriptCall(BattleScript_EmergencyExitWild); diff --git a/test/battle/ability/emergency_exit.c b/test/battle/ability/emergency_exit.c index 5027d929ec..1dcd0be21b 100644 --- a/test/battle/ability/emergency_exit.c +++ b/test/battle/ability/emergency_exit.c @@ -108,3 +108,69 @@ SINGLE_BATTLE_TEST("Emergency Exit activates when taking residual damage and fal ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); } } + +WILD_BATTLE_TEST("Emergency Exit makes the pokemon flee during wild battle") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(262); }; + } WHEN { + TURN { MOVE(player, MOVE_SUPER_FANG);} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPER_FANG, player); + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } THEN { + EXPECT_EQ(gBattleOutcome, B_OUTCOME_MON_TELEPORTED); + } +} + +WILD_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp (wild battle)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(134); Status1(STATUS1_BURN); }; + } WHEN { + TURN { } + } SCENE { + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } THEN { + EXPECT_EQ(gBattleOutcome, B_OUTCOME_MON_TELEPORTED); + } +} + +WILD_BATTLE_TEST("Emergency Exit makes the player ran during wild battle") +{ + GIVEN { + PLAYER(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(262); }; + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SUPER_FANG);} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPER_FANG, opponent); + HP_BAR(player); + ABILITY_POPUP(player, ABILITY_EMERGENCY_EXIT); + } THEN { + EXPECT_EQ(gBattleOutcome, B_OUTCOME_PLAYER_TELEPORTED); + } +} + +WILD_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp (wild battle player side)") +{ + GIVEN { + PLAYER(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(134); }; + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SANDSTORM);} + } SCENE { + HP_BAR(player); + ABILITY_POPUP(player, ABILITY_EMERGENCY_EXIT); + } THEN { + EXPECT_EQ(gBattleOutcome, B_OUTCOME_PLAYER_TELEPORTED); + } +} From 1de27646d2ed57e4746f74928963b10027da5cb1 Mon Sep 17 00:00:00 2001 From: Estellar <137097857+estellarc@users.noreply.github.com> Date: Tue, 28 Oct 2025 06:19:12 -0300 Subject: [PATCH 075/130] Remove magic numbers in slot_machine.c (#2195) --- src/slot_machine.c | 59 +++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/src/slot_machine.c b/src/slot_machine.c index 217767fc9e..da8b3613b3 100644 --- a/src/slot_machine.c +++ b/src/slot_machine.c @@ -314,6 +314,11 @@ enum { DIG_DISPLAY_BONUS_BIG }; +// IDs for the text windows +enum { + WIN_MSG, + WIN_INFO, +}; // How ReelTime works // ================== @@ -1249,7 +1254,7 @@ static void SlotMachineSetup_LoadGfxAndTilemaps(void) LoadSlotMachineGfx(); LoadMessageBoxGfx(0, 0x200, BG_PLTT_ID(15)); LoadUserWindowBorderGfx(0, 0x214, BG_PLTT_ID(14)); - PutWindowTilemap(0); + PutWindowTilemap(WIN_MSG); } static void CreateSlotMachineSprites(void) @@ -1390,9 +1395,9 @@ static bool8 SlotTask_HandleBetInput(struct Task *task) // SLOTTASK_MSG_NEED_3_COINS static bool8 SlotTask_PrintMsg_Need3Coins(struct Task *task) { - DrawDialogueFrame(0, FALSE); - AddTextPrinterParameterized(0, FONT_NORMAL, gText_YouDontHaveThreeCoins, 0, 1, 0, 0); - CopyWindowToVram(0, COPYWIN_FULL); + DrawDialogueFrame(WIN_MSG, FALSE); + AddTextPrinterParameterized(WIN_MSG, FONT_NORMAL, gText_YouDontHaveThreeCoins, 0, 1, 0, 0); + CopyWindowToVram(WIN_MSG, COPYWIN_FULL); sSlotMachine->state = SLOTTASK_WAIT_MSG_NEED_3_COINS; return FALSE; } @@ -1402,7 +1407,7 @@ static bool8 SlotTask_WaitMsg_Need3Coins(struct Task *task) { if (JOY_NEW(A_BUTTON | B_BUTTON)) { - ClearDialogWindowAndFrame(0, TRUE); + ClearDialogWindowAndFrame(WIN_MSG, TRUE); sSlotMachine->state = SLOTTASK_BET_INPUT; } return FALSE; @@ -1655,9 +1660,9 @@ static bool8 SlotTask_NoMatches(struct Task *task) // SLOTTASK_ASK_QUIT static bool8 SlotTask_AskQuit(struct Task *task) { - DrawDialogueFrame(0, FALSE); - AddTextPrinterParameterized(0, FONT_NORMAL, gText_QuitTheGame, 0, 1, 0, 0); - CopyWindowToVram(0, COPYWIN_FULL); + DrawDialogueFrame(WIN_MSG, FALSE); + AddTextPrinterParameterized(WIN_MSG, FONT_NORMAL, gText_QuitTheGame, 0, 1, 0, 0); + CopyWindowToVram(WIN_MSG, COPYWIN_FULL); CreateYesNoMenuParameterized(0x15, 7, 0x214, 0x180, 0xE, 0xF); sSlotMachine->state = SLOTTASK_HANDLE_QUIT_INPUT; return FALSE; @@ -1669,16 +1674,16 @@ static bool8 SlotTask_HandleQuitInput(struct Task *task) s8 input = Menu_ProcessInputNoWrapClearOnChoose(); if (input == 0) // Chose to quit { - ClearDialogWindowAndFrame(0, TRUE); + ClearDialogWindowAndFrame(WIN_MSG, TRUE); DarkenBetTiles(0); DarkenBetTiles(1); DarkenBetTiles(2); sSlotMachine->coins += sSlotMachine->bet; sSlotMachine->state = SLOTTASK_END; } - else if (input == 1 || input == -1) // Chose not to quit + else if (input == 1 || input == MENU_B_PRESSED) // Chose not to quit { - ClearDialogWindowAndFrame(0, TRUE); + ClearDialogWindowAndFrame(WIN_MSG, TRUE); sSlotMachine->state = SLOTTASK_BET_INPUT; } return FALSE; @@ -1687,9 +1692,9 @@ static bool8 SlotTask_HandleQuitInput(struct Task *task) // SLOTTASK_MSG_MAX_COINS static bool8 SlotTask_PrintMsg_MaxCoins(struct Task *task) { - DrawDialogueFrame(0, FALSE); - AddTextPrinterParameterized(0, FONT_NORMAL, gText_YouveGot9999Coins, 0, 1, 0, 0); - CopyWindowToVram(0, COPYWIN_FULL); + DrawDialogueFrame(WIN_MSG, FALSE); + AddTextPrinterParameterized(WIN_MSG, FONT_NORMAL, gText_YouveGot9999Coins, 0, 1, 0, 0); + CopyWindowToVram(WIN_MSG, COPYWIN_FULL); sSlotMachine->state = SLOTTASK_WAIT_MSG_MAX_COINS; return FALSE; } @@ -1699,7 +1704,7 @@ static bool8 SlotTask_WaitMsg_MaxCoins(struct Task *task) { if (JOY_NEW(A_BUTTON | B_BUTTON)) { - ClearDialogWindowAndFrame(0, TRUE); + ClearDialogWindowAndFrame(WIN_MSG, TRUE); sSlotMachine->state = SLOTTASK_BET_INPUT; } return FALSE; @@ -1708,9 +1713,9 @@ static bool8 SlotTask_WaitMsg_MaxCoins(struct Task *task) // SLOTTASK_MSG_NO_MORE_COINS static bool8 SlotTask_PrintMsg_NoMoreCoins(struct Task *task) { - DrawDialogueFrame(0, FALSE); - AddTextPrinterParameterized(0, FONT_NORMAL, gText_YouveRunOutOfCoins, 0, 1, 0, 0); - CopyWindowToVram(0, COPYWIN_FULL); + DrawDialogueFrame(WIN_MSG, FALSE); + AddTextPrinterParameterized(WIN_MSG, FONT_NORMAL, gText_YouveRunOutOfCoins, 0, 1, 0, 0); + CopyWindowToVram(WIN_MSG, COPYWIN_FULL); sSlotMachine->state = SLOTTASK_WAIT_MSG_NO_MORE_COINS; return FALSE; } @@ -1720,7 +1725,7 @@ static bool8 SlotTask_WaitMsg_NoMoreCoins(struct Task *task) { if (JOY_NEW(A_BUTTON | B_BUTTON)) { - ClearDialogWindowAndFrame(0, TRUE); + ClearDialogWindowAndFrame(WIN_MSG, TRUE); sSlotMachine->state = SLOTTASK_END; } return FALSE; @@ -3922,15 +3927,15 @@ static void InfoBox_DrawWindow(struct Task *task) DestroyDigitalDisplayScene(); LoadInfoBoxTilemap(); AddWindow(&sWindowTemplate_InfoBox); - PutWindowTilemap(1); - FillWindowPixelBuffer(1, PIXEL_FILL(0)); + PutWindowTilemap(WIN_INFO); + FillWindowPixelBuffer(WIN_INFO, PIXEL_FILL(0)); task->tState++; } static void InfoBox_AddText(struct Task *task) { - AddTextPrinterParameterized3(1, FONT_NORMAL, 2, 5, sColors_ReeltimeHelp, 0, gText_ReelTimeHelp); - CopyWindowToVram(1, COPYWIN_FULL); + AddTextPrinterParameterized3(WIN_INFO, FONT_NORMAL, 2, 5, sColors_ReeltimeHelp, 0, gText_ReelTimeHelp); + CopyWindowToVram(WIN_INFO, COPYWIN_FULL); BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK); task->tState++; } @@ -3939,10 +3944,10 @@ static void InfoBox_WaitInput(struct Task *task) { if (JOY_NEW(B_BUTTON | SELECT_BUTTON)) { - FillWindowPixelBuffer(1, PIXEL_FILL(0)); - ClearWindowTilemap(1); - CopyWindowToVram(1, COPYWIN_MAP); - RemoveWindow(1); + FillWindowPixelBuffer(WIN_INFO, PIXEL_FILL(0)); + ClearWindowTilemap(WIN_INFO); + CopyWindowToVram(WIN_INFO, COPYWIN_MAP); + RemoveWindow(WIN_INFO); BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); task->tState++; } From b82a5a4c1cbed2a34be6a49d45c0cbc177bbec04 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Tue, 28 Oct 2025 17:02:35 +0100 Subject: [PATCH 076/130] Fix wrong gimmick spite showing when inputting too fast (#8066) --- include/battle_gimmick.h | 1 + src/battle_controller_player.c | 2 ++ src/battle_gimmick.c | 7 +++++++ 3 files changed, 10 insertions(+) diff --git a/include/battle_gimmick.h b/include/battle_gimmick.h index 42b8c7255a..84b1d6b26a 100644 --- a/include/battle_gimmick.h +++ b/include/battle_gimmick.h @@ -35,6 +35,7 @@ void SetGimmickAsActivated(u32 battler, enum Gimmick gimmick); void ChangeGimmickTriggerSprite(u32 spriteId, u32 animId); void CreateGimmickTriggerSprite(u32 battler); bool32 IsGimmickTriggerSpriteActive(void); +bool32 IsGimmickTriggerSpriteMatchingBattler(u32 battler); void HideGimmickTriggerSprite(void); void DestroyGimmickTriggerSprite(void); diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 3361ce2f65..856bf5d30b 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -2090,6 +2090,8 @@ void PlayerHandleChooseMove(u32 battler) if (!IsGimmickTriggerSpriteActive()) gBattleStruct->gimmick.triggerSpriteId = 0xFF; + else if (!IsGimmickTriggerSpriteMatchingBattler(battler)) + DestroyGimmickTriggerSprite(); if (!(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE && !gBattleStruct->zmove.viable)) CreateGimmickTriggerSprite(battler); diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index 76ea80a4f6..38d60d4365 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -178,6 +178,13 @@ bool32 IsGimmickTriggerSpriteActive(void) return FALSE; } +bool32 IsGimmickTriggerSpriteMatchingBattler(u32 battler) +{ + if (battler == gSprites[gBattleStruct->gimmick.triggerSpriteId].tBattler) + return TRUE; + return FALSE; +} + void HideGimmickTriggerSprite(void) { if (gBattleStruct->gimmick.triggerSpriteId != 0xFF) From 3b66cd007bccbdf6151d84303b1027ef291be4d5 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Tue, 28 Oct 2025 17:19:52 +0100 Subject: [PATCH 077/130] Fix target cancelling not working properly with z-move (#8067) --- src/battle_controller_player.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 856bf5d30b..b76a72af9b 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -454,6 +454,12 @@ void HandleInputChooseTarget(u32 battler) PlaySE(SE_SELECT); gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget; gBattlerControllerFuncs[battler] = HandleInputChooseMove; + if (gBattleStruct->gimmick.playerSelect == 1 && gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE) + { + gBattleStruct->gimmick.playerSelect = 0; + gBattleStruct->zmove.viewing = TRUE; + ReloadMoveNames(battler); + } DoBounceEffect(battler, BOUNCE_HEALTHBOX, 7, 1); DoBounceEffect(battler, BOUNCE_MON, 7, 1); EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX); From 96f6e9948664ee0189599b95183a8f3be062f216 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 17:22:41 +0100 Subject: [PATCH 078/130] add MandL27 as a contributor for code (#8064) 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 aa13a3864d..a54e1caefd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -441,6 +441,15 @@ "contributions": [ "code" ] + }, + { + "login": "MandL27", + "name": "MandL27", + "avatar_url": "https://avatars.githubusercontent.com/u/10366615?v=4", + "profile": "https://github.com/MandL27", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index d3d46d0361..547b5bd52d 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -74,6 +74,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d ghostyboyy97
ghostyboyy97

💻 Marky
Marky

💻 + MandL27
MandL27

💻 From a9fc0b28c94446f83c327aefb09a88c4cd5aeff0 Mon Sep 17 00:00:00 2001 From: grintoul <166724814+grintoul1@users.noreply.github.com> Date: Tue, 28 Oct 2025 18:23:41 +0000 Subject: [PATCH 079/130] Corrects battler partner identification in battle_ai_switch_items.c (#8071) --- src/battle_ai_switch_items.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 96cb7e30df..f300741675 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1124,7 +1124,7 @@ bool32 ShouldSwitch(u32 battler) if (IsDoubleBattle()) { - u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler))); + u32 partner = BATTLE_PARTNER(battler); battlerIn1 = battler; if (gAbsentBattlerFlags & (1u << partner)) battlerIn2 = battler; @@ -1275,7 +1275,7 @@ void ModifySwitchAfterMoveScoring(u32 battler) if (IsDoubleBattle()) { - u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler))); + u32 partner = BATTLE_PARTNER(battler); battlerIn1 = battler; if (gAbsentBattlerFlags & (1u << partner)) battlerIn2 = battler; @@ -1323,7 +1323,7 @@ bool32 IsSwitchinValid(u32 battler) // Edge case: See if partner already chose to switch into the same mon if (IsDoubleBattle()) { - u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler))); + u32 partner = BATTLE_PARTNER(battler); if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE) // Generic switch { if ((gAiLogicData->shouldSwitch & (1u << partner)) && gAiLogicData->monToSwitchInId[partner] == gAiLogicData->mostSuitableMonId[battler]) From c90e6952f32466ed165565c561cbf862fdb90d74 Mon Sep 17 00:00:00 2001 From: grintoul <166724814+grintoul1@users.noreply.github.com> Date: Tue, 28 Oct 2025 18:26:10 +0000 Subject: [PATCH 080/130] Fix Ally Switch being useable in Frontier Link Multi battles (#8059) Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- include/battle_util.h | 1 + include/constants/battle.h | 1 + src/battle_script_commands.c | 4 +--- src/battle_tower.c | 2 +- src/battle_util.c | 9 +++++++++ 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/include/battle_util.h b/include/battle_util.h index 3531f0eb6b..8601c792f6 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -414,5 +414,6 @@ bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect); bool32 IsSemiInvulnerable(u32 battler, enum SemiInvulnerableExclusion excludeCommander); bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move); +bool32 HasPartnerTrainer(u32 battler); #endif // GUARD_BATTLE_UTIL_H diff --git a/include/constants/battle.h b/include/constants/battle.h index 825487f24e..012b8986ef 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -99,6 +99,7 @@ enum BattlerId #define RECORDED_WILD_BATTLE ((gBattleTypeFlags & BATTLE_TYPE_RECORDED) && !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FRONTIER))) #define BATTLE_TWO_VS_ONE_OPPONENT ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && TRAINER_BATTLE_PARAM.opponentB == 0xFFFF)) #define BATTLE_TYPE_HAS_AI (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER | BATTLE_TYPE_INGAME_PARTNER) +#define BATTLE_TYPE_PLAYER_HAS_PARTNER (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_TOWER_LINK_MULTI) // Battle Outcome defines #define B_OUTCOME_WON 1 diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index b8c12c1c5e..223a4bd382 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -15711,9 +15711,7 @@ void BS_TryAllySwitch(void) { NATIVE_ARGS(const u8 *failInstr); - if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)) - || (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) - || (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)) + if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)) || HasPartnerTrainer(gBattlerAttacker)) { gBattlescriptCurrInstr = cmd->failInstr; } diff --git a/src/battle_tower.c b/src/battle_tower.c index e215c259a4..a03f0a130b 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -1987,7 +1987,7 @@ void DoSpecialTrainerBattle(void) gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS; break; case FRONTIER_MODE_LINK_MULTIS: - gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_MULTI | BATTLE_TYPE_TOWER_LINK_MULTI; + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_MULTI | BATTLE_TYPE_TOWER_LINK_MULTI | BATTLE_TYPE_TWO_OPPONENTS; FillFrontierTrainersParties(FRONTIER_MULTI_PARTY_SIZE); break; } diff --git a/src/battle_util.c b/src/battle_util.c index 8700fe2b2e..2e2f18cbc9 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -11907,3 +11907,12 @@ bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move) return FALSE; } + +bool32 HasPartnerTrainer(u32 battler) +{ + if ((GetBattlerSide(battler) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_PLAYER_HAS_PARTNER) + || (GetBattlerSide(battler) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)) + return TRUE; + else + return FALSE; +} From 4a714225e526a7af3bbb815a9e0553cea2f9700e Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Wed, 29 Oct 2025 13:25:05 +0000 Subject: [PATCH 081/130] Fixes hazards and switch-in items not being reset when switching in (#8074) --- src/battle_main.c | 3 +++ src/battle_script_commands.c | 1 - test/battle/hazards.c | 23 +++++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/battle_main.c b/src/battle_main.c index 6aa4c7212d..99464151a8 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3235,6 +3235,9 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy) gBattleStruct->battlerState[battler].stompingTantrumTimer = 0; gBattleStruct->palaceFlags &= ~(1u << battler); gBattleStruct->battlerState[battler].canPickupItem = FALSE; + gBattleStruct->hazardsCounter = 0; + gDisableStructs[battler].hazardsDone = FALSE; + gSpecialStatuses[battler].switchInItemDone = FALSE; ClearPursuitValuesIfSet(battler); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 223a4bd382..6d9ba3d61e 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7891,7 +7891,6 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) gBattleStruct->hpOnSwitchout[GetBattlerSide(i)] = gBattleMons[i].hp; } - gDisableStructs[battler].hazardsDone = FALSE; gBattleStruct->battlerState[battler].forcedSwitch = FALSE; return FALSE; } diff --git a/test/battle/hazards.c b/test/battle/hazards.c index 9c974bb7b1..17ecb41f5c 100644 --- a/test/battle/hazards.c +++ b/test/battle/hazards.c @@ -38,3 +38,26 @@ SINGLE_BATTLE_TEST("Hazards are applied based on order of set up") EXPECT_EQ(gBattleStruct->hazardsQueue[0][5], HAZARDS_NONE); } } + +SINGLE_BATTLE_TEST("Hazards are applied correctly after a battler faints") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_FINAL_GAMBIT) == EFFECT_FINAL_GAMBIT); + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_STEALTH_ROCK); + MOVE(player, MOVE_FINAL_GAMBIT); + SEND_OUT(player, 1); + SEND_OUT(player, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FINAL_GAMBIT, player); + MESSAGE("Wynaut fainted!"); + MESSAGE("Pointed stones dug into Wobbuffet!"); + MESSAGE("Wobbuffet fainted!"); + MESSAGE("Pointed stones dug into Wynaut!"); + } +} From ea60c32fbeb446a7a8b61c65da708d8a9bc945f6 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 29 Oct 2025 15:19:50 +0100 Subject: [PATCH 082/130] Fixes Liquid Ooze dmg not blocked by Magic Guard (#8036) Co-authored-by: Bassoonian --- data/battle_scripts_1.s | 4 ++++ test/battle/ability/liquid_ooze.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 36c3712854..d65e846880 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2880,6 +2880,7 @@ BattleScript_CantMakeAsleep:: BattleScript_EffectAbsorbLiquidOoze:: call BattleScript_AbilityPopUpTarget + jumpifability BS_ATTACKER, ABILITY_MAGIC_GUARD, BattleScript_EffectAbsorbRet goto BattleScript_EffectAbsorb BattleScript_EffectAbsorb:: @@ -2889,6 +2890,7 @@ BattleScript_EffectAbsorb:: waitmessage B_WAIT_TIME_LONG tryfaintmon BS_ATTACKER bicword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_HP_UPDATE | HITMARKER_PASSIVE_HP_UPDATE +BattleScript_EffectAbsorbRet: return BattleScript_EffectExplosion:: @@ -5625,10 +5627,12 @@ BattleScript_LeechSeedTurnDrainLiquidOoze:: copybyte gBattlerAbility, gBattlerAttacker call BattleScript_AbilityPopUp copybyte gBattlerAttacker, gBattlerTarget @ needed to get liquid ooze message correct + jumpifability BS_TARGET, ABILITY_MAGIC_GUARD, BattleScript_LeechSeedTurnDrainHealBlockEnd2 goto BattleScript_LeechSeedTurnDrainGainHp BattleScript_LeechSeedTurnDrainHealBlock:: call BattleScript_LeechSeedTurnDrain +BattleScript_LeechSeedTurnDrainHealBlockEnd2: end2 BattleScript_LeechSeedTurnDrainRecovery:: diff --git a/test/battle/ability/liquid_ooze.c b/test/battle/ability/liquid_ooze.c index 2c89702911..ca7c88ebdd 100644 --- a/test/battle/ability/liquid_ooze.c +++ b/test/battle/ability/liquid_ooze.c @@ -187,3 +187,34 @@ SINGLE_BATTLE_TEST("Liquid Ooze does not cause Dream Eater users to lose HP inst EXPECT_LT(damage, 0); // Negative damage = Heal } } + +SINGLE_BATTLE_TEST("Liquid Ooze HP loss from Absorb is blocked by Magic Guard") +{ + GIVEN { + PLAYER(SPECIES_CLEFFA) { Ability(ABILITY_MAGIC_GUARD); } + OPPONENT(SPECIES_TENTACOOL) { Ability(ABILITY_LIQUID_OOZE); } + } WHEN { + TURN { MOVE(player, MOVE_ABSORB); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player); + HP_BAR(opponent); + NONE_OF { + HP_BAR(player); + MESSAGE("Wobbuffet sucked up the liquid ooze!"); + } + } +} + +SINGLE_BATTLE_TEST("Liquid Ooze HP loss from Leech Seed is blocked by Magic Guard") +{ + GIVEN { + PLAYER(SPECIES_CLEFFA) { Ability(ABILITY_MAGIC_GUARD); } + OPPONENT(SPECIES_TENTACOOL) { Ability(ABILITY_LIQUID_OOZE); } + } WHEN { + TURN { MOVE(player, MOVE_LEECH_SEED); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, player); + HP_BAR(opponent); + NOT HP_BAR(player); + } +} From 712e08775434462ef46250aa4253cd1f49c146bd Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Wed, 29 Oct 2025 17:53:41 +0100 Subject: [PATCH 083/130] Fix move description prompt window not appear when choosing a move after canceling target selection (#8055) --- src/battle_controller_player.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index b76a72af9b..f2819db600 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -460,6 +460,7 @@ void HandleInputChooseTarget(u32 battler) gBattleStruct->zmove.viewing = TRUE; ReloadMoveNames(battler); } + TryToAddMoveInfoWindow(); DoBounceEffect(battler, BOUNCE_HEALTHBOX, 7, 1); DoBounceEffect(battler, BOUNCE_MON, 7, 1); EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX); From ea3b72f43fded58ce809c9d2e8169fb5a2853289 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 29 Oct 2025 19:04:56 +0100 Subject: [PATCH 084/130] Initialize DamageContext on declaration to zero (#8076) --- src/battle_ai_util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 5913c0842c..4a397719c0 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -897,7 +897,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u gBattleStruct->magnitudeBasePower = 70; gBattleStruct->presentBasePower = 80; - struct DamageContext ctx; + struct DamageContext ctx = {0}; ctx.battlerAtk = battlerAtk; ctx.battlerDef = battlerDef; ctx.move = move; From 9bfac0099e02795e80564db256aeb93e17f38b47 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Wed, 29 Oct 2025 18:13:41 -0300 Subject: [PATCH 085/130] Fixed fainting form change tests --- include/test/battle.h | 1 + test/battle/form_change/faint.c | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/include/test/battle.h b/include/test/battle.h index 9cd1d23b1f..bf7d22788d 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -935,6 +935,7 @@ struct TestAIScoreStruct bool8 explicitTarget; }; +// Party data at the beginning of the test. Shouldn't be confused with the party data changed during the test. #define PLAYER_PARTY (gBattleTestRunnerState->data.recordedBattle.playerParty) #define OPPONENT_PARTY (gBattleTestRunnerState->data.recordedBattle.opponentParty) diff --git a/test/battle/form_change/faint.c b/test/battle/form_change/faint.c index 8fc95cdc65..316ecca0bf 100644 --- a/test/battle/form_change/faint.c +++ b/test/battle/form_change/faint.c @@ -1,9 +1,24 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting") +SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting (start as Shield)") +{ + GIVEN { + PLAYER(SPECIES_AEGISLASH_SHIELD) { HP(1); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_GUST); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("The opposing Wobbuffet used Gust!"); + MESSAGE("Aegislash fainted!"); + } THEN { + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_AEGISLASH_SHIELD); + } +} + +SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting (start as Blade)") { - KNOWN_FAILING; GIVEN { PLAYER(SPECIES_AEGISLASH_BLADE) { HP(1); } PLAYER(SPECIES_WOBBUFFET); @@ -14,7 +29,7 @@ SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting") MESSAGE("The opposing Wobbuffet used Gust!"); MESSAGE("Aegislash fainted!"); } THEN { - EXPECT_EQ(GetMonData(&PLAYER_PARTY[0], MON_DATA_SPECIES), SPECIES_AEGISLASH_SHIELD); + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_AEGISLASH_SHIELD); } } From 9fa857d34cb0c05565b9dd7be6c0e480e16de6c2 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Wed, 29 Oct 2025 21:08:05 -0300 Subject: [PATCH 086/130] Removed redundant comment --- include/test/battle.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/test/battle.h b/include/test/battle.h index bf7d22788d..9cd1d23b1f 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -935,7 +935,6 @@ struct TestAIScoreStruct bool8 explicitTarget; }; -// Party data at the beginning of the test. Shouldn't be confused with the party data changed during the test. #define PLAYER_PARTY (gBattleTestRunnerState->data.recordedBattle.playerParty) #define OPPONENT_PARTY (gBattleTestRunnerState->data.recordedBattle.opponentParty) From d1d543548772cbdf087c5ccc1e0b19980aa9ad87 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Thu, 30 Oct 2025 01:34:03 +0000 Subject: [PATCH 087/130] Improve undefined map assembler messages (#2196) --- asm/macros/event.inc | 16 ++++++++++++++++ asm/macros/map.inc | 4 ++++ data/event_scripts.s | 4 ++-- data/maps/LilycoveCity_Harbor/scripts.inc | 4 ++-- data/maps/LittlerootTown/scripts.inc | 2 +- data/maps/SlateportCity_Harbor/scripts.inc | 2 +- data/scripts/cable_club.inc | 12 ++++++------ include/constants/maps.h | 11 +++++++---- tools/mapjson/mapjson.cpp | 10 +++++++--- 9 files changed, 46 insertions(+), 19 deletions(-) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 797d549dc9..7075481750 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -667,6 +667,14 @@ map \map .endm + @ Set the player object's invisibility to FALSE. + .macro showplayer + .byte SCR_OP_SHOWOBJECTAT + .2byte LOCALID_PLAYER + .byte 0 @ map group + .byte 0 @ map num + .endm + @ Sets the specified object's invisibility to TRUE. .macro hideobjectat localId:req, map:req .byte SCR_OP_HIDEOBJECTAT @@ -674,6 +682,14 @@ map \map .endm + @ Set the player object's invisibility to TRUE. + .macro hideplayer + .byte SCR_OP_HIDEOBJECTAT + .2byte LOCALID_PLAYER + .byte 0 @ map group + .byte 0 @ map num + .endm + @ Turns the currently selected object (if there is one) to face the player. .macro faceplayer .byte SCR_OP_FACEPLAYER diff --git a/asm/macros/map.inc b/asm/macros/map.inc index 21445138de..acbcd532dd 100644 --- a/asm/macros/map.inc +++ b/asm/macros/map.inc @@ -2,8 +2,12 @@ @ Takes a MAP constant and outputs the map group and map number as separate bytes .macro map map_id:req + .ifdef \map_id .byte \map_id >> 8 @ map group .byte \map_id & 0xFF @ map num + .else + .error "undefined map (check for typos)" + .endif .endm @ Defines a map script. 'type' is any MAP_SCRIPT_* constant (see include/constants/map_scripts.h) diff --git a/data/event_scripts.s b/data/event_scripts.s index 0cc95940f8..33fc318957 100644 --- a/data/event_scripts.s +++ b/data/event_scripts.s @@ -787,7 +787,7 @@ EventScript_UnusedBoardFerry:: delay 30 applymovement LOCALID_PLAYER, Common_Movement_WalkInPlaceFasterUp waitmovement 0 - showobjectat LOCALID_PLAYER, 0 + showplayer delay 30 applymovement LOCALID_PLAYER, Movement_UnusedBoardFerry waitmovement 0 @@ -802,7 +802,7 @@ Common_EventScript_FerryDepartIsland:: call_if_eq VAR_FACING, DIR_SOUTH, Ferry_EventScript_DepartIslandSouth call_if_eq VAR_FACING, DIR_WEST, Ferry_EventScript_DepartIslandWest delay 30 - hideobjectat LOCALID_PLAYER, 0 + hideplayer call Common_EventScript_FerryDepart return diff --git a/data/maps/LilycoveCity_Harbor/scripts.inc b/data/maps/LilycoveCity_Harbor/scripts.inc index 9388609ab5..89bbb0714c 100644 --- a/data/maps/LilycoveCity_Harbor/scripts.inc +++ b/data/maps/LilycoveCity_Harbor/scripts.inc @@ -333,7 +333,7 @@ LilycoveCity_Harbor_EventScript_BoardFerryWithSailor:: call_if_eq VAR_FACING, DIR_NORTH, LilycoveCity_Harbor_EventScript_PlayerBoardFerryNorth call_if_eq VAR_FACING, DIR_EAST, LilycoveCity_Harbor_EventScript_PlayerBoardFerryEast delay 30 - hideobjectat LOCALID_PLAYER, 0 + hideplayer setvar VAR_0x8004, LOCALID_LILYCOVE_HARBOR_SS_TIDAL call Common_EventScript_FerryDepart return @@ -393,7 +393,7 @@ LilycoveCity_Harbor_EventScript_BoardFerry:: call_if_eq VAR_FACING, DIR_NORTH, LilycoveCity_Harbor_EventScript_PlayerBoardFerryNorth call_if_eq VAR_FACING, DIR_EAST, LilycoveCity_Harbor_EventScript_PlayerBoardFerryEast delay 30 - hideobjectat LOCALID_PLAYER, 0 + hideplayer setvar VAR_0x8004, LOCALID_LILYCOVE_HARBOR_SS_TIDAL call Common_EventScript_FerryDepart return diff --git a/data/maps/LittlerootTown/scripts.inc b/data/maps/LittlerootTown/scripts.inc index fd1d970b13..6a2c6af41b 100644 --- a/data/maps/LittlerootTown/scripts.inc +++ b/data/maps/LittlerootTown/scripts.inc @@ -156,7 +156,7 @@ LittlerootTown_EventScript_GoInsideWithMom:: waitmovement 0 setflag FLAG_HIDE_LITTLEROOT_TOWN_MOM_OUTSIDE setvar VAR_LITTLEROOT_INTRO_STATE, 3 - hideobjectat LOCALID_PLAYER, 0 + hideplayer closedoor VAR_0x8004, VAR_0x8005 waitdooranim clearflag FLAG_HIDE_LITTLEROOT_TOWN_FAT_MAN diff --git a/data/maps/SlateportCity_Harbor/scripts.inc b/data/maps/SlateportCity_Harbor/scripts.inc index ebcd40ab0c..52fd04ec65 100644 --- a/data/maps/SlateportCity_Harbor/scripts.inc +++ b/data/maps/SlateportCity_Harbor/scripts.inc @@ -228,7 +228,7 @@ SlateportCity_Harbor_EventScript_BoardFerry:: call_if_eq VAR_FACING, DIR_NORTH, SlateportCity_Harbor_EventScript_BoardFerryNorth call_if_eq VAR_FACING, DIR_EAST, SlateportCity_Harbor_EventScript_BoardFerryEast delay 30 - hideobjectat LOCALID_PLAYER, 0 + hideplayer setvar VAR_0x8004, LOCALID_SLATEPORT_HARBOR_SS_TIDAL call Common_EventScript_FerryDepart return diff --git a/data/scripts/cable_club.inc b/data/scripts/cable_club.inc index 68d619097f..a6a13fe940 100644 --- a/data/scripts/cable_club.inc +++ b/data/scripts/cable_club.inc @@ -356,7 +356,7 @@ CableClub_EventScript_EnterColosseum:: waitdooranim applymovement LOCALID_PLAYER, Movement_PlayerEnterLinkRoom waitmovement 0 - hideobjectat LOCALID_PLAYER, 0 + hideplayer closedoor 9, 1 waitdooranim release @@ -450,7 +450,7 @@ CableClub_EventScript_EnterTradeCenter:: waitdooranim applymovement LOCALID_PLAYER, Movement_PlayerEnterLinkRoom waitmovement 0 - hideobjectat LOCALID_PLAYER, 0 + hideplayer closedoor 9, 1 waitdooranim release @@ -515,7 +515,7 @@ CableClub_EventScript_EnterRecordCorner:: waitdooranim applymovement LOCALID_PLAYER, Movement_PlayerEnterLinkRoom waitmovement 0 - hideobjectat LOCALID_PLAYER, 0 + hideplayer closedoor 9, 1 waitdooranim release @@ -902,7 +902,7 @@ CableClub_EventScript_EnterUnionRoom:: waitdooranim applymovement LOCALID_PLAYER, Movement_PlayerEnterLinkRoom waitmovement 0 - hideobjectat LOCALID_PLAYER, 0 + hideplayer closedoor 5, 1 waitdooranim special Script_ResetUnionRoomTrade @@ -1202,7 +1202,7 @@ CableClub_EventScript_EnterWirelessLinkRoom:: waitdooranim applymovement LOCALID_PLAYER, Movement_PlayerEnterLinkRoom waitmovement 0 - hideobjectat LOCALID_PLAYER, 0 + hideplayer closedoor 9, 1 waitdooranim release @@ -1380,7 +1380,7 @@ MossdeepCity_GameCorner_1F_EventScript_EnterMinigameRoom:: closemessage applymovement LOCALID_PLAYER, Movement_PlayerEnterMinigameRoom waitmovement 0 - hideobjectat LOCALID_PLAYER, 0 + hideplayer release waitstate end diff --git a/include/constants/maps.h b/include/constants/maps.h index d41f03c683..36e27d78b7 100644 --- a/include/constants/maps.h +++ b/include/constants/maps.h @@ -3,11 +3,14 @@ #include "map_groups.h" -// Warps using this map will instead use the warp data stored in gSaveBlock1Ptr->dynamicWarp. -// Used for warps that need to change destinations, e.g. when stepping off an elevator. -#define MAP_DYNAMIC (0x7F | (0x7F << 8)) +enum +{ + // Warps using this map will instead use the warp data stored in gSaveBlock1Ptr->dynamicWarp. + // Used for warps that need to change destinations, e.g. when stepping off an elevator. + MAP_DYNAMIC = (0x7F | (0x7F << 8)), -#define MAP_UNDEFINED (0xFF | (0xFF << 8)) + MAP_UNDEFINED = (0xFF | (0xFF << 8)), +}; #define MAP_GROUP(map) (map >> 8) #define MAP_NUM(map) (map & 0xFF) diff --git a/tools/mapjson/mapjson.cpp b/tools/mapjson/mapjson.cpp index 8d8ec4e4e6..5ceac10393 100644 --- a/tools/mapjson/mapjson.cpp +++ b/tools/mapjson/mapjson.cpp @@ -526,11 +526,13 @@ string generate_map_constants_text(string groups_filepath, Json groups_data) { ostringstream text; text << get_include_guard_start(guard_name) << get_generated_warning("data/maps/map_groups.json", false); + text << "enum\n{\n"; + int group_num = 0; for (auto &group : groups_data["group_order"].array_items()) { string groupName = json_to_string(group); - text << "// " << groupName << "\n"; + text << " // " << groupName << "\n"; vector map_ids; size_t max_length = 0; @@ -548,14 +550,16 @@ string generate_map_constants_text(string groups_filepath, Json groups_data) { int map_id_num = 0; for (string map_id : map_ids) { - text << "#define " << map_id << string((max_length - map_id.length() + 1), ' ') - << "(" << map_id_num++ << " | (" << group_num << " << 8))\n"; + text << " " << map_id << string(max_length - map_id.length(), ' ') + << " = (" << map_id_num++ << " | (" << group_num << " << 8)),\n"; } text << "\n"; group_num++; } + text << "};\n\n"; + text << "#define MAP_GROUPS_COUNT " << group_num << "\n\n"; text << get_include_guard_end(guard_name); From 814cf53629d9f386fdb9da227638a3332f1181c2 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Thu, 30 Oct 2025 11:43:50 -0300 Subject: [PATCH 088/130] Fixed Hunger Switch changing forms on switch out while Tera'd (#8080) --- src/battle_util.c | 28 ++++++++++++++++++++-------- test/battle/ability/hunger_switch.c | 1 - 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 2e2f18cbc9..182910aadc 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10104,14 +10104,26 @@ bool32 CanBattlerFormChange(u32 battler, enum FormChanges method) if (gBattleMons[battler].volatiles.transformed && B_TRANSFORM_FORM_CHANGES >= GEN_5) return FALSE; - // Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle. - if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE)) - return TRUE; - else if (IsBattlerPrimalReverted(battler) && (method == FORM_CHANGE_END_BATTLE)) - return TRUE; - // Gigantamaxed Pokemon should revert upon fainting, switching, or ending the battle. - else if (IsGigantamaxed(battler) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_BATTLE_SWITCH || method == FORM_CHANGE_END_BATTLE)) - return TRUE; + + switch (method) + { + case FORM_CHANGE_END_BATTLE: + if (IsBattlerPrimalReverted(battler)) + return TRUE; + // Fallthrough + case FORM_CHANGE_FAINT: + if (IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler) || IsGigantamaxed(battler)) + return TRUE; + break; + case FORM_CHANGE_BATTLE_SWITCH: + if (IsGigantamaxed(battler)) + return TRUE; + else if (GetActiveGimmick(battler) == GIMMICK_TERA && GetBattlerAbility(battler) == ABILITY_HUNGER_SWITCH) + return FALSE; + break; + default: + break; + } return DoesSpeciesHaveFormChangeMethod(gBattleMons[battler].species, method); } diff --git a/test/battle/ability/hunger_switch.c b/test/battle/ability/hunger_switch.c index 1892de4424..469cf02d3c 100644 --- a/test/battle/ability/hunger_switch.c +++ b/test/battle/ability/hunger_switch.c @@ -53,7 +53,6 @@ SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form when Terastalli SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form after switching out while Terastallized") { - KNOWN_FAILING; // #7062 GIVEN { ASSUME(GetMoveEffect(MOVE_ROAR) == EFFECT_ROAR); PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); TeraType(TYPE_NORMAL); } From c77c624e9f53f1b2abf7c044b3b59d33a5be6290 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 30 Oct 2025 16:14:03 +0100 Subject: [PATCH 089/130] Fixes Gooey/Tangling Hair ability pop up triggering on Clear Body (#8083) --- data/battle_scripts_1.s | 2 ++ test/battle/ability/tangling_hair.c | 14 +++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index d65e846880..de2148bab2 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8214,11 +8214,13 @@ BattleScript_CuteCharmActivates:: return BattleScript_GooeyActivates:: + statbuffchange BS_ATTACKER, STAT_CHANGE_ONLY_CHECKING, BattleScript_GooeyActivatesRet waitstate call BattleScript_AbilityPopUp swapattackerwithtarget @ for defiant, mirror armor seteffectsecondary BS_ATTACKER, BS_TARGET, MOVE_EFFECT_SPD_MINUS_1 swapattackerwithtarget +BattleScript_GooeyActivatesRet: return BattleScript_AbilityStatusEffect:: diff --git a/test/battle/ability/tangling_hair.c b/test/battle/ability/tangling_hair.c index 13a0f27cb0..849c83df64 100644 --- a/test/battle/ability/tangling_hair.c +++ b/test/battle/ability/tangling_hair.c @@ -8,7 +8,6 @@ ASSUMPTIONS ASSUME(MoveMakesContact(MOVE_SCRATCH) == TRUE); } - SINGLE_BATTLE_TEST("Tangling Hair drops opposing mon's speed if ability user got hit by a contact move") { u32 move; @@ -85,3 +84,16 @@ SINGLE_BATTLE_TEST("Tangling Hair does not activate on confusion damage") } } } + +SINGLE_BATTLE_TEST("Tangling Hair does not trigger on Clear Body") +{ + GIVEN { + PLAYER(SPECIES_DUGTRIO) { Ability(ABILITY_TANGLING_HAIR); } + OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); }; + } WHEN { + TURN { MOVE(opponent, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); + NOT ABILITY_POPUP(player, ABILITY_TANGLING_HAIR); + } +} From 8de86ba243ac41802995e1bf16281155ac4a9370 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Thu, 30 Oct 2025 13:14:07 -0300 Subject: [PATCH 090/130] Fixed Belly Drum/Contrary interaction at max Attack (#8078) --- asm/macros/battle_script.inc | 7 ++- data/battle_scripts_1.s | 2 +- include/battle_util.h | 2 +- src/battle_script_commands.c | 50 ++++++++++++------- src/battle_util.c | 72 ++++++++++++++-------------- src/item_use.c | 7 ++- test/battle/move_effect/belly_drum.c | 4 +- 7 files changed, 84 insertions(+), 60 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 5ccaf2ea50..6203c3a7bf 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -187,8 +187,13 @@ .4byte \jumpInstr .endm - .macro unused_0x21 + .macro jumpifstatignorecontrary battler:req, comparison:req, stat:req, value:req, jumpInstr:req .byte 0x21 + .byte \battler + .byte \comparison + .byte \stat + .byte \value + .4byte \jumpInstr .endm .macro jumpbasedontype battler:req, type:req, jumpIfType:req, jumpInstr:req diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index de2148bab2..49768b2c43 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -4221,7 +4221,7 @@ BattleScript_EffectBellyDrum:: attackcanceler attackstring ppreduce - jumpifstat BS_ATTACKER, CMP_EQUAL, STAT_ATK, MAX_STAT_STAGE, BattleScript_ButItFailed + jumpifstatignorecontrary BS_ATTACKER, CMP_EQUAL, STAT_ATK, MAX_STAT_STAGE, BattleScript_ButItFailed halvehp BattleScript_ButItFailed orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_HP_UPDATE attackanimation diff --git a/include/battle_util.h b/include/battle_util.h index 8601c792f6..dccd31d503 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -332,7 +332,7 @@ bool32 IsPartnerMonFromSameTrainer(u32 battler); enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, u32 statId, u32 itemId, enum ItemCaseId caseID); bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes); void SortBattlersBySpeed(u8 *battlers, bool32 slowToFast); -bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind); +bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind, u32 ability); bool32 TryRoomService(u32 battler); void BufferStatChange(u32 battler, u8 statId, enum StringID stringId); bool32 BlocksPrankster(u16 move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 6d9ba3d61e..e58b9c57e6 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -370,7 +370,7 @@ static void Cmd_jumpifvolatile(void); static void Cmd_jumpifability(void); static void Cmd_jumpifsideaffecting(void); static void Cmd_jumpifstat(void); -static void Cmd_unused_0x21(void); +static void Cmd_jumpifstatignorecontrary(void); static void Cmd_jumpbasedontype(void); static void Cmd_getexp(void); static void Cmd_checkteamslost(void); @@ -629,7 +629,7 @@ void (*const gBattleScriptingCommandsTable[])(void) = Cmd_jumpifability, //0x1E Cmd_jumpifsideaffecting, //0x1F Cmd_jumpifstat, //0x20 - Cmd_unused_0x21, //0x21 + Cmd_jumpifstatignorecontrary, //0x21 Cmd_jumpbasedontype, //0x22 Cmd_getexp, //0x23 Cmd_checkteamslost, //0x24 @@ -4432,7 +4432,7 @@ static void Cmd_jumpifstat(void) u8 value = cmd->value; u8 comparison = cmd->comparison; - ret = CompareStat(battler, stat, value, comparison); + ret = CompareStat(battler, stat, value, comparison, GetBattlerAbility(battler)); if (ret) gBattlescriptCurrInstr = cmd->jumpInstr; @@ -4440,8 +4440,22 @@ static void Cmd_jumpifstat(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused_0x21(void) +static void Cmd_jumpifstatignorecontrary(void) { + CMD_ARGS(u8 battler, u8 comparison, u8 stat, u8 value, const u8 *jumpInstr); + + bool32 ret = 0; + u8 battler = GetBattlerForBattleScript(cmd->battler); + u8 stat = cmd->stat; + u8 value = cmd->value; + u8 comparison = cmd->comparison; + + ret = CompareStat(battler, stat, value, comparison, ABILITY_NONE); + + if (ret) + gBattlescriptCurrInstr = cmd->jumpInstr; + else + gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_jumpbasedontype(void) @@ -5620,7 +5634,7 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move else if (abilityAtk == ABILITY_GRIM_NEIGH || abilityAtk == ABILITY_AS_ONE_SHADOW_RIDER) stat = STAT_SPATK; - if (numMonsFainted && CompareStat(battlerAtk, stat, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (numMonsFainted && CompareStat(battlerAtk, stat, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk)) { gLastUsedAbility = abilityAtk; if (abilityAtk == ABILITY_AS_ONE_ICE_RIDER) @@ -5660,17 +5674,17 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move else { u32 numStatBuffs = 0; - if (CompareStat(battlerAtk, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battlerAtk, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk)) { gBattleScripting.animArg1 = GET_STAT_BUFF_ID(STAT_ATK) + STAT_ANIM_PLUS1; numStatBuffs++; } - if (CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battlerAtk, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk)) { gBattleScripting.animArg1 = GET_STAT_BUFF_ID(STAT_SPATK) + STAT_ANIM_PLUS1; numStatBuffs++; } - if (CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battlerAtk, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, abilityAtk)) { gBattleScripting.animArg1 = GET_STAT_BUFF_ID(STAT_SPEED) + STAT_ANIM_PLUS1; numStatBuffs++; @@ -5876,7 +5890,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) && !IsBattlerAlive(gBattlerTarget) && IsBattlerTurnDamaged(gBattlerTarget) && !NoAliveMonsForEitherParty() - && CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerAttacker))) { SET_STATCHANGER(STAT_ATK, GetGenConfig(GEN_CONFIG_FELL_STINGER_STAT_RAISE) >= GEN_7 ? 3 : 2, FALSE); PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK); @@ -6106,7 +6120,7 @@ static void Cmd_moveend(void) && !IsBattlerAlly(gBattlerAttacker, gBattlerTarget) && IsBattlerTurnDamaged(gBattlerTarget) && !IsBattleMoveStatus(gCurrentMove) - && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget))) { SET_STATCHANGER(STAT_ATK, 1, FALSE); BattleScriptCall(BattleScript_RageIsBuilding); @@ -6522,7 +6536,7 @@ static void Cmd_moveend(void) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_THROAT_SPRAY && IsBattlerAlive(gBattlerAttacker) && IsAnyTargetAffected(gBattlerAttacker) - && CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN) + && CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerAttacker)) && !NoAliveMonsForEitherParty()) // Don't activate if battle will end { gLastUsedItem = gBattleMons[gBattlerAttacker].item; @@ -12089,7 +12103,7 @@ static void Cmd_jumpifconfusedandstatmaxed(void) CMD_ARGS(u8 stat, const u8 *jumpInstr); if (gBattleMons[gBattlerTarget].volatiles.confusionTurns > 0 - && !CompareStat(gBattlerTarget, cmd->stat, MAX_STAT_STAGE, CMP_LESS_THAN)) + && !CompareStat(gBattlerTarget, cmd->stat, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget))) gBattlescriptCurrInstr = cmd->jumpInstr; // Fails if we're confused AND stat cannot be raised else gBattlescriptCurrInstr = cmd->nextInstr; @@ -16014,7 +16028,7 @@ void BS_CanTarShotWork(void) NATIVE_ARGS(const u8 *failInstr); // Tar Shot will fail if it's already been used on the target or if its speed can't be lowered further if (!gDisableStructs[gBattlerTarget].tarShot - && CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget))) gBattlescriptCurrInstr = cmd->nextInstr; else gBattlescriptCurrInstr = cmd->failInstr; @@ -16968,7 +16982,7 @@ void BS_TryAcupressure(void) u32 bits = 0; for (u32 stat = STAT_ATK; stat < NUM_BATTLE_STATS; stat++) { - if (CompareStat(gBattlerTarget, stat, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(gBattlerTarget, stat, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerTarget))) bits |= 1u << stat; } if (bits) @@ -17367,10 +17381,11 @@ void BS_TryActivateSoulheart(void) while (gBattleStruct->soulheartBattlerId < gBattlersCount) { gBattleScripting.battler = gBattleStruct->soulheartBattlerId++; - if (GetBattlerAbility(gBattleScripting.battler) == ABILITY_SOUL_HEART + u32 ability = GetBattlerAbility(gBattleScripting.battler); + if (ability == ABILITY_SOUL_HEART && IsBattlerAlive(gBattleScripting.battler) && !NoAliveMonsForEitherParty() - && CompareStat(gBattleScripting.battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(gBattleScripting.battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, ability)) { SET_STATCHANGER(STAT_SPATK, 1, FALSE); PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPATK); @@ -18278,10 +18293,11 @@ void BS_CutOneThirdHpAndRaiseStats(void) bool8 atLeastOneStatBoosted = FALSE; u16 hpFraction = max(1, GetNonDynamaxMaxHP(gBattlerAttacker) / 3); + u32 ability = GetBattlerAbility(gBattlerAttacker); for (u32 stat = 1; stat < NUM_STATS; stat++) { - if (CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_LESS_THAN, ability)) { atLeastOneStatBoosted = TRUE; break; diff --git a/src/battle_util.c b/src/battle_util.c index 182910aadc..c35a1f00a3 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1196,7 +1196,7 @@ void PrepareStringBattle(enum StringID stringId, u32 battler) else if (GetGenConfig(GEN_CONFIG_UPDATED_INTIMIDATE) >= GEN_8 && stringId == STRINGID_PKMNCUTSATTACKWITH && targetAbility == ABILITY_RATTLED - && CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, targetAbility)) { gBattlerAbility = gBattlerTarget; BattleScriptCall(BattleScript_AbilityRaisesDefenderStat); @@ -1702,13 +1702,13 @@ u32 GetBattlerAffectionHearts(u32 battler) // gBattlerAttacker is the battler that's trying to raise their stats and due to limitations of RandomUniformExcept, cannot be an argument bool32 MoodyCantRaiseStat(u32 stat) { - return CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_EQUAL); + return CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_EQUAL, GetBattlerAbility(gBattlerAttacker)); } // gBattlerAttacker is the battler that's trying to lower their stats and due to limitations of RandomUniformExcept, cannot be an argument bool32 MoodyCantLowerStat(u32 stat) { - return stat == GET_STAT_BUFF_ID(gBattleScripting.statChanger) || CompareStat(gBattlerAttacker, stat, MIN_STAT_STAGE, CMP_EQUAL); + return stat == GET_STAT_BUFF_ID(gBattleScripting.statChanger) || CompareStat(gBattlerAttacker, stat, MIN_STAT_STAGE, CMP_EQUAL, GetBattlerAbility(gBattlerAttacker)); } void TryToRevertMimicryAndFlags(void) @@ -3213,7 +3213,7 @@ bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32 break; case MOVE_ABSORBED_BY_STAT_INCREASE_ABILITY: gBattleStruct->pledgeMove = FALSE; - if (!CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (!CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN, abilityDef)) { if ((gProtectStructs[battlerAtk].notFirstStrike)) battleScript = BattleScript_MonMadeMoveUseless; @@ -3776,7 +3776,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 gSpecialStatuses[battler].switchInAbilityDone = TRUE; - if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { SET_STATCHANGER(statId, 1, FALSE); SaveBattlerAttacker(gBattlerAttacker); @@ -3988,7 +3988,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (GetGenConfig(GEN_INTREPID_SWORD) == GEN_9) GetBattlerPartyState(battler)->intrepidSwordBoost = TRUE; gSpecialStatuses[battler].switchInAbilityDone = TRUE; - if (CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { SET_STATCHANGER(STAT_ATK, 1, FALSE); BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); @@ -4003,7 +4003,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (GetGenConfig(GEN_DAUNTLESS_SHIELD) == GEN_9) GetBattlerPartyState(battler)->dauntlessShieldBoost = TRUE; gSpecialStatuses[battler].switchInAbilityDone = TRUE; - if (CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { SET_STATCHANGER(STAT_DEF, 1, FALSE); BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); @@ -4013,7 +4013,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 break; case ABILITY_WIND_RIDER: if (!gSpecialStatuses[battler].switchInAbilityDone - && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN) + && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) && gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TAILWIND) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; @@ -4149,7 +4149,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 else //ABILITY_EMBODY_ASPECT_TEAL_MASK stat = STAT_SPEED; - if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_EQUAL)) + if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_EQUAL, gLastUsedAbility)) break; gSpecialStatuses[battler].switchInAbilityDone = TRUE; @@ -4302,7 +4302,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_SPEED_BOOST: - if (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) && gDisableStructs[battler].isFirstTurn != 2) + if (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) && gDisableStructs[battler].isFirstTurn != 2) { SET_STATCHANGER(STAT_SPEED, 1, FALSE); BattleScriptPushCursorAndCallback(BattleScript_SpeedBoostActivates); @@ -4318,9 +4318,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 for (i = STAT_ATK; i < statsNum; i++) { - if (CompareStat(battler, i, MIN_STAT_STAGE, CMP_GREATER_THAN)) + if (CompareStat(battler, i, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility)) validToLower |= 1u << i; - if (CompareStat(battler, i, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battler, i, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) validToRaise |= 1u << i; } @@ -4457,7 +4457,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (IsBattlerTurnDamaged(battler) && IsBattlerAlive(battler) && moveType == TYPE_DARK - && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { gEffectBattler = battler; SET_STATCHANGER(STAT_ATK, 1, FALSE); @@ -4469,7 +4469,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (IsBattlerTurnDamaged(battler) && IsBattlerAlive(battler) && (moveType == TYPE_DARK || moveType == TYPE_BUG || moveType == TYPE_GHOST) - && CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { gEffectBattler = battler; SET_STATCHANGER(STAT_SPEED, 1, FALSE); @@ -4481,7 +4481,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (IsBattlerTurnDamaged(battler) && IsBattlerAlive(battler) && moveType == TYPE_WATER - && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { gEffectBattler = battler; SET_STATCHANGER(STAT_DEF, 2, FALSE); @@ -4493,7 +4493,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (gBattlerAttacker != gBattlerTarget && IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(battler) - && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { gEffectBattler = battler; SET_STATCHANGER(STAT_DEF, 1, FALSE); @@ -4507,7 +4507,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && HadMoreThanHalfHpNowDoesnt(battler) && (gMultiHitCounter == 0 || gMultiHitCounter == 1) && !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) - && CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { gEffectBattler = battler; SET_STATCHANGER(STAT_SPATK, 1, FALSE); @@ -4519,8 +4519,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (IsBattlerTurnDamaged(battler) && IsBattlerAlive(battler) && IsBattleMovePhysical(gCurrentMove) - && (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) // Don't activate if both Speed and Defense cannot be raised. - || CompareStat(battler, STAT_DEF, MIN_STAT_STAGE, CMP_GREATER_THAN))) + && (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) // Don't activate if both Speed and Defense cannot be raised. + || CompareStat(battler, STAT_DEF, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility))) { if (GetMoveEffect(gCurrentMove) == EFFECT_HIT_ESCAPE && CanBattlerSwitch(gBattlerAttacker)) gProtectStructs[battler].disableEjectPack = TRUE; // Set flag for target @@ -4597,7 +4597,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (gSpecialStatuses[battler].criticalHit && IsBattlerTurnDamaged(battler) && IsBattlerAlive(battler) - && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility)) { SET_STATCHANGER(STAT_ATK, MAX_STAT_STAGE - gBattleMons[battler].statStages[STAT_ATK], FALSE); BattleScriptCall(BattleScript_TargetsStatWasMaxedOut); @@ -4622,7 +4622,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_GOOEY: case ABILITY_TANGLING_HAIR: if (IsBattlerAlive(gBattlerAttacker) - && (CompareStat(gBattlerAttacker, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN) || GetBattlerAbility(gBattlerAttacker) == ABILITY_MIRROR_ARMOR) + && (CompareStat(gBattlerAttacker, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN, gLastUsedAbility) || GetBattlerAbility(gBattlerAttacker) == ABILITY_MIRROR_ARMOR) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && IsBattlerTurnDamaged(gBattlerTarget) && !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker, TRUE), move)) @@ -4830,7 +4830,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_STEAM_ENGINE: if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(battler) - && CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) + && CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) && (moveType == TYPE_FIRE || moveType == TYPE_WATER)) { gEffectBattler = battler; @@ -4916,7 +4916,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_THERMAL_EXCHANGE: if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerTarget) - && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN) + && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN, gLastUsedAbility) && moveType == TYPE_FIRE) { gEffectBattler = gBattlerTarget; @@ -5870,11 +5870,12 @@ static enum ItemEffect HealConfuseBerry(u32 battler, u32 itemId, u32 flavorId, e static enum ItemEffect StatRaiseBerry(u32 battler, u32 itemId, u32 statId, enum ItemCaseId caseID) { - if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN) && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, itemId), itemId)) + u32 ability = GetBattlerAbility(battler); + if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, ability) && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, itemId), itemId)) { BufferStatChange(battler, statId, STRINGID_STATROSE); gEffectBattler = gBattleScripting.battler = battler; - if (GetBattlerAbility(battler) == ABILITY_RIPEN) + if (ability == ABILITY_RIPEN) SET_STATCHANGER(statId, 2, FALSE); else SET_STATCHANGER(statId, 1, FALSE); @@ -5895,15 +5896,15 @@ static enum ItemEffect RandomStatRaiseBerry(u32 battler, u32 itemId, enum ItemCa { s32 stat; enum StringID stringId; + u32 battlerAbility = GetBattlerAbility(battler); for (stat = STAT_ATK; stat < NUM_STATS; stat++) { - if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_LESS_THAN, battlerAbility)) break; } if (stat != NUM_STATS && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, itemId), itemId)) { - u16 battlerAbility = GetBattlerAbility(battler); u32 savedAttacker = gBattlerAttacker; // MoodyCantRaiseStat requires that the battler is set to gBattlerAttacker gBattlerAttacker = gBattleScripting.battler = battler; @@ -5976,8 +5977,9 @@ static enum ItemEffect TrySetEnigmaBerry(u32 battler) static enum ItemEffect DamagedStatBoostBerryEffect(u32 battler, u8 statId, enum DamageCategory category) { + u32 ability = GetBattlerAbility(battler); if (IsBattlerAlive(battler) - && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN) + && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, ability) && (gBattleScripting.overrideBerryRequirements || (!DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) && GetBattleMoveCategory(gCurrentMove) == category @@ -5988,7 +5990,7 @@ static enum ItemEffect DamagedStatBoostBerryEffect(u32 battler, u8 statId, enum BufferStatChange(battler, statId, STRINGID_STATROSE); gEffectBattler = battler; - if (GetBattlerAbility(battler) == ABILITY_RIPEN) + if (ability == ABILITY_RIPEN) SET_STATCHANGER(statId, 2, FALSE); else SET_STATCHANGER(statId, 1, FALSE); @@ -6004,7 +6006,7 @@ static enum ItemEffect DamagedStatBoostBerryEffect(u32 battler, u8 statId, enum enum ItemEffect TryHandleSeed(u32 battler, u32 terrainFlag, u32 statId, u32 itemId, enum ItemCaseId caseID) { - if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) + if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(battler))) { BufferStatChange(battler, statId, STRINGID_STATROSE); gLastUsedItem = itemId; // For surge abilities @@ -6961,7 +6963,7 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler) case HOLD_EFFECT_BLUNDER_POLICY: if (gBattleStruct->blunderPolicy && IsBattlerAlive(gBattlerAttacker) - && CompareStat(gBattlerAttacker, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) + && CompareStat(gBattlerAttacker, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN, GetBattlerAbility(gBattlerAttacker))) { gBattleStruct->blunderPolicy = FALSE; gLastUsedItem = atkItem; @@ -10757,15 +10759,15 @@ bool32 TestIfSheerForceAffected(u32 battler, u16 move) return GetBattlerAbility(battler) == ABILITY_SHEER_FORCE && MoveIsAffectedBySheerForce(move); } -// This function is the body of "jumpifstat", but can be used dynamically in a function -bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind) +// This function is the body of "jumpifstat", but can be used dynamically in a function. It considers Contrary. +bool32 CompareStat(u32 battler, u8 statId, u8 cmpTo, u8 cmpKind, u32 ability) { bool32 ret = FALSE; u8 statValue = gBattleMons[battler].statStages[statId]; // Because this command is used as a way of checking if a stat can be lowered/raised, // we need to do some modification at run-time. - if (GetBattlerAbility(battler) == ABILITY_CONTRARY) + if (ability == ABILITY_CONTRARY) { if (cmpKind == CMP_GREATER_THAN) cmpKind = CMP_LESS_THAN; @@ -10836,7 +10838,7 @@ void BufferStatChange(u32 battler, u8 statId, enum StringID stringId) bool32 TryRoomService(u32 battler) { - if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battler, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN)) + if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battler, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN, GetBattlerAbility(battler))) { BufferStatChange(battler, STAT_SPEED, STRINGID_STATFELL); gEffectBattler = gBattleScripting.battler = battler; diff --git a/src/item_use.c b/src/item_use.c index 0681575f45..b1baca20d2 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -128,7 +128,7 @@ static void SetUpItemUseCallback(u8 taskId) type = gTasks[taskId].tEnigmaBerryType - 1; else type = GetItemType(gSpecialVar_ItemId) - 1; - + if (gTasks[taskId].tUsingRegisteredKeyItem && type == (ITEM_USE_PARTY_MENU - 1)) { FadeScreen(FADE_TO_BLACK, 0); @@ -1291,15 +1291,18 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon) } break; case EFFECT_ITEM_INCREASE_ALL_STATS: + { + u32 ability = GetBattlerAbility(gBattlerInMenuId); for (i = STAT_ATK; i < NUM_STATS; i++) { - if (CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL)) + if (CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL, ability)) { cannotUse = TRUE; break; } } break; + } case EFFECT_ITEM_RESTORE_HP: if (hp == 0 || hp == GetMonData(mon, MON_DATA_MAX_HP)) cannotUse = TRUE; diff --git a/test/battle/move_effect/belly_drum.c b/test/battle/move_effect/belly_drum.c index 20024f5b91..194ee5b9d0 100644 --- a/test/battle/move_effect/belly_drum.c +++ b/test/battle/move_effect/belly_drum.c @@ -159,7 +159,6 @@ SINGLE_BATTLE_TEST("Belly Drum maximizes the user's Attack stat, even when below SINGLE_BATTLE_TEST("Belly Drum fails if the user's Attack is already at +6, even with Contrary") { - KNOWN_FAILING; GIVEN { ASSUME(GetMoveEffect(MOVE_CHARM) == EFFECT_ATTACK_DOWN_2); PLAYER(SPECIES_SERPERIOR) { Ability(ABILITY_CONTRARY); } @@ -190,7 +189,6 @@ SINGLE_BATTLE_TEST("Belly Drum fails if the user's Attack is already at +6, even SINGLE_BATTLE_TEST("Belly Drum deducts HP if the user has Contrary and is at -6") { - KNOWN_FAILING; GIVEN { ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); PLAYER(SPECIES_SERPERIOR) { Ability(ABILITY_CONTRARY); } @@ -215,6 +213,6 @@ SINGLE_BATTLE_TEST("Belly Drum deducts HP if the user has Contrary and is at -6" ANIMATION(ANIM_TYPE_MOVE, MOVE_BELLY_DRUM, player); s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); HP_BAR(player, hp: maxHP / 2); - MESSAGE("Wobbuffet cut its own HP and maximized its Attack!"); + MESSAGE("Serperior cut its own HP and maximized its Attack!"); } } From 3a3b9476227d7aa16d7425dd8b07adef2f8efd7d Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 30 Oct 2025 17:51:57 +0100 Subject: [PATCH 091/130] Fixes intimidate activating on empty field (#8058) Co-authored-by: PhallenTree <168426989+PhallenTree@users.noreply.github.com> --- src/battle_util.c | 21 ++++++++++++++-- test/battle/ability/intimidate.c | 36 +++++++++++++++++----------- test/battle/move_effect/hit_escape.c | 6 ++--- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index c35a1f00a3..a4c3fff748 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -67,6 +67,7 @@ static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item); static bool32 CanBeInfinitelyConfused(u32 battler); static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, u32 abilityDef, u32 abilityAffected, const u8 *battleScript, enum FunctionCallOption option); static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum FunctionCallOption option); +static bool32 IsOpposingSideEmpty(u32 battler); ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12(u32 percent); ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12_Floored(u32 percent); @@ -3929,7 +3930,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_INTIMIDATE: - if (!gSpecialStatuses[battler].switchInAbilityDone) + if (!gSpecialStatuses[battler].switchInAbilityDone && !IsOpposingSideEmpty(battler)) { SaveBattlerAttacker(gBattlerAttacker); gBattlerAttacker = battler; @@ -3941,7 +3942,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 break; case ABILITY_SUPERSWEET_SYRUP: if (!gSpecialStatuses[battler].switchInAbilityDone - && !GetBattlerPartyState(battler)->supersweetSyrup) + && !GetBattlerPartyState(battler)->supersweetSyrup + && !IsOpposingSideEmpty(battler)) { SaveBattlerAttacker(gBattlerAttacker); gBattlerAttacker = battler; @@ -11930,3 +11932,18 @@ bool32 HasPartnerTrainer(u32 battler) else return FALSE; } + +static bool32 IsOpposingSideEmpty(u32 battler) +{ + u32 oppositeBattler = BATTLE_OPPOSITE(battler); + + if (IsBattlerAlive(oppositeBattler)) + return FALSE; + + if (!IsDoubleBattle()) + return TRUE; + + if (IsBattlerAlive(BATTLE_PARTNER(oppositeBattler))) + return FALSE; + return TRUE; +} diff --git a/test/battle/ability/intimidate.c b/test/battle/ability/intimidate.c index c7308bfdaf..b0e4b19a19 100644 --- a/test/battle/ability/intimidate.c +++ b/test/battle/ability/intimidate.c @@ -240,6 +240,7 @@ DOUBLE_BATTLE_TEST("Intimidate is not going to trigger if a mon switches out thr ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, opponentRight); ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, playerLeft); HP_BAR(opponentLeft); + NOT ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); MESSAGE("2 sent out Treecko!"); MESSAGE("2 sent out Torchic!"); NOT ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); @@ -264,7 +265,7 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer effected by Neutral } } -SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - switching moves") +DOUBLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - switching moves") { u32 move; PARAMETRIZE { move = MOVE_U_TURN; } @@ -276,19 +277,24 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutral ASSUME(GetMoveEffect(MOVE_BATON_PASS) == EFFECT_BATON_PASS); PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); } PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, move); SEND_OUT(player, 1); } + TURN { + if (move == MOVE_U_TURN) + MOVE(playerLeft, move, target: opponentLeft); + else + MOVE(playerLeft, move); + SEND_OUT(playerLeft, 2); + } } SCENE { - ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS); + ABILITY_POPUP(playerLeft, ABILITY_NEUTRALIZING_GAS); MESSAGE("Neutralizing gas filled the area!"); - ANIMATION(ANIM_TYPE_MOVE, move, player); + ANIMATION(ANIM_TYPE_MOVE, move, playerLeft); MESSAGE("The effects of the neutralizing gas wore off!"); - ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); SEND_IN_MESSAGE("Wobbuffet"); - } THEN { - if (move == MOVE_HEALING_WISH) - EXPECT_EQ(player->hp, player->maxHP); } } @@ -330,23 +336,25 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutral } } -SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - fainted") +DOUBLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutralizing Gas - fainted") { GIVEN { ASSUME(GetMoveEffect(MOVE_FELL_STINGER) == EFFECT_FELL_STINGER); PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); HP(1); } PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(opponent, MOVE_FELL_STINGER); SEND_OUT(player, 1); } + TURN { MOVE(opponentLeft, MOVE_FELL_STINGER, target: playerLeft); SEND_OUT(playerLeft, 2); } } SCENE { - ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS); + ABILITY_POPUP(playerLeft, ABILITY_NEUTRALIZING_GAS); MESSAGE("Neutralizing gas filled the area!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_FELL_STINGER, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FELL_STINGER, opponentLeft); MESSAGE("The effects of the neutralizing gas wore off!"); - ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); MESSAGE("Weezing fainted!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); SEND_IN_MESSAGE("Wobbuffet"); } } diff --git a/test/battle/move_effect/hit_escape.c b/test/battle/move_effect/hit_escape.c index 0ced64d7e5..f587d078a0 100644 --- a/test/battle/move_effect/hit_escape.c +++ b/test/battle/move_effect/hit_escape.c @@ -113,7 +113,7 @@ SINGLE_BATTLE_TEST("Hit Escape: U-turn switches the user out after Ice Face acti } } -SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: player side") +SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn: player side") { GIVEN { PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); }; @@ -126,7 +126,6 @@ SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE); ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player); HP_BAR(opponent); - ABILITY_POPUP(player, ABILITY_INTIMIDATE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); MESSAGE("2 sent out Wynaut!"); @@ -136,7 +135,7 @@ SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon } } -SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: opposing side") +SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn: opposing side") { GIVEN { PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); }; @@ -149,7 +148,6 @@ SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE); ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player); HP_BAR(opponent); - ABILITY_POPUP(player, ABILITY_INTIMIDATE); MESSAGE("2 sent out Wynaut!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); From 0b005d4b6f043ec7a4307d796cfda8fbb513d003 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Thu, 30 Oct 2025 19:47:22 +0100 Subject: [PATCH 092/130] Fix non-battle trainer script not running properly (#8056) --- src/trainer_see.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/trainer_see.c b/src/trainer_see.c index 4439ce6977..54ea4ebf22 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -380,6 +380,7 @@ bool8 CheckForTrainersWantingBattle(void) if (numTrainers == 0xFF) // non-trainerbatle script { u32 objectEventId = gApproachingTrainers[gNoOfApproachingTrainers - 1].objectEventId; + gApproachingTrainers[gNoOfApproachingTrainers - 1].trainerScriptPtr = GetObjectEventScriptPointerByObjectEventId(objectEventId); gSelectedObjectEvent = objectEventId; gSpecialVar_LastTalked = gObjectEvents[objectEventId].localId; ScriptContext_SetupScript(EventScript_ObjectApproachPlayer); From 79441c65749065618caa68eda5f68e56cb631fbc Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Fri, 31 Oct 2025 17:44:08 +0100 Subject: [PATCH 093/130] Fix bug where mon selection doesn't properly account for party order (#8088) --- src/party_menu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/party_menu.c b/src/party_menu.c index 8a9532eaea..891066dd41 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -1511,7 +1511,7 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr) } case PARTY_ACTION_SEND_MON_TO_BOX: { - u8 partyId = GetPartyIdFromBattleSlot((u8)*slotPtr); + u8 partyId = (u8)*slotPtr; if (partyId == 0 || ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && partyId == 1)) { // Can't select if mon is currently on the field @@ -1538,7 +1538,7 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr) else { PlaySE(SE_SELECT); - gSelectedMonPartyId = partyId; + gSelectedMonPartyId = GetPartyIdFromBattleSlot(partyId); Task_ClosePartyMenu(taskId); } break; From 6c3f87e74ef63bd044f907eecb35f7f138c8f708 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Fri, 31 Oct 2025 17:50:29 +0100 Subject: [PATCH 094/130] Fix bug when a captured pokemon replaces a party member who changed forms (#8091) --- include/battle_util.h | 1 + src/battle_main.c | 12 +----------- src/battle_script_commands.c | 2 ++ src/battle_util.c | 17 +++++++++++++++++ 4 files changed, 21 insertions(+), 11 deletions(-) diff --git a/include/battle_util.h b/include/battle_util.h index dccd31d503..7bb8bc7292 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -300,6 +300,7 @@ bool32 IsBattlerMegaEvolved(u32 battler); bool32 IsBattlerPrimalReverted(u32 battler); bool32 IsBattlerUltraBursted(u32 battler); u16 GetBattleFormChangeTargetSpecies(u32 battler, enum FormChanges method); +bool32 TryRevertPartyMonFormChange(u32 partyIndex); bool32 TryBattleFormChange(u32 battler, enum FormChanges method); bool32 DoBattlersShareType(u32 battler1, u32 battler2); bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId); diff --git a/src/battle_main.c b/src/battle_main.c index 99464151a8..cb41e88c73 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5627,17 +5627,7 @@ static void HandleEndTurn_FinishBattle(void) for (i = 0; i < PARTY_SIZE; i++) { - bool8 changedForm = FALSE; - - // Appeared in battle and didn't faint - if ((gBattleStruct->appearedInBattle & (1u << i)) && GetMonData(&gPlayerParty[i], MON_DATA_HP, NULL) != 0) - changedForm = TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE_ENVIRONMENT); - - if (!changedForm) - changedForm = TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE); - - // Clear original species field - gBattleStruct->partyState[B_SIDE_PLAYER][i].changedSpecies = SPECIES_NONE; + bool8 changedForm = TryRevertPartyMonFormChange(i); gBattleStruct->partyState[B_SIDE_OPPONENT][i].changedSpecies = SPECIES_NONE; // Recalculate the stats of every party member before the end diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index e58b9c57e6..353d0e16e7 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -14155,6 +14155,8 @@ static void Cmd_givecaughtmon(void) } else { + //Before sending to PC, we revert battle form + TryRevertPartyMonFormChange(gSelectedMonPartyId); // Mon chosen, try to put it in the PC if (CopyMonToPC(&gPlayerParty[gSelectedMonPartyId]) == MON_GIVEN_TO_PC) { diff --git a/src/battle_util.c b/src/battle_util.c index a4c3fff748..91299c64df 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10131,6 +10131,23 @@ bool32 CanBattlerFormChange(u32 battler, enum FormChanges method) return DoesSpeciesHaveFormChangeMethod(gBattleMons[battler].species, method); } +bool32 TryRevertPartyMonFormChange(u32 partyIndex) +{ + bool32 changedForm = FALSE; + + // Appeared in battle and didn't faint + if ((gBattleStruct->appearedInBattle & (1u << partyIndex)) && GetMonData(&gPlayerParty[partyIndex], MON_DATA_HP, NULL) != 0) + changedForm = TryFormChange(partyIndex, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE_ENVIRONMENT); + + if (!changedForm) + changedForm = TryFormChange(partyIndex, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE); + + // Clear original species field + gBattleStruct->partyState[B_SIDE_PLAYER][partyIndex].changedSpecies = SPECIES_NONE; + + return changedForm; +} + bool32 TryBattleFormChange(u32 battler, enum FormChanges method) { u32 monId = gBattlerPartyIndexes[battler]; From f66ba68eee5b83e290a55d0155c2be59096b6d41 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Fri, 31 Oct 2025 15:41:46 -0300 Subject: [PATCH 095/130] Fixed Zygarde Complete disappearing upon catch (#8089) --- src/battle_util.c | 27 ++++++++------ test/battle/ability/power_construct.c | 53 +++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 91299c64df..9adfcc5016 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10161,7 +10161,7 @@ bool32 TryBattleFormChange(u32 battler, enum FormChanges method) targetSpecies = GetBattleFormChangeTargetSpecies(battler, method); if (targetSpecies == currentSpecies) targetSpecies = GetFormChangeTargetSpecies(&party[monId], method, 0); - if (targetSpecies != currentSpecies) + if (targetSpecies != currentSpecies && targetSpecies != SPECIES_NONE) { // Saves the original species on the first form change. @@ -10178,17 +10178,22 @@ bool32 TryBattleFormChange(u32 battler, enum FormChanges method) { bool32 restoreSpecies = FALSE; - // Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle, so no need to add it to the form change tables. - if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE)) - restoreSpecies = TRUE; - - // Unlike Megas, Primal Reversion isn't canceled on fainting. - else if (IsBattlerPrimalReverted(battler) && (method == FORM_CHANGE_END_BATTLE)) - restoreSpecies = TRUE; - - // Gigantamax Pokemon have their forms reverted after fainting, switching, or ending the battle. - else if (IsGigantamaxed(battler) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_BATTLE_SWITCH || method == FORM_CHANGE_END_BATTLE)) + switch (method) + { + case FORM_CHANGE_END_BATTLE: restoreSpecies = TRUE; + break; + case FORM_CHANGE_FAINT: + if (IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler) || IsBattlerInTeraForm(battler) || IsGigantamaxed(battler)) + restoreSpecies = TRUE; + break; + case FORM_CHANGE_BATTLE_SWITCH: + if (IsGigantamaxed(battler)) + restoreSpecies = TRUE; + break; + default: + break; + } if (restoreSpecies) { diff --git a/test/battle/ability/power_construct.c b/test/battle/ability/power_construct.c index 6a8ca9db5f..07d52c1767 100644 --- a/test/battle/ability/power_construct.c +++ b/test/battle/ability/power_construct.c @@ -2,3 +2,56 @@ #include "test/battle.h" TO_DO_BATTLE_TEST("TODO: Write Power Construct (Ability) test titles") + +SINGLE_BATTLE_TEST("Power Construct switches Zygarde's form when HP is below half") +{ + u16 baseSpecies; + PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_10_POWER_CONSTRUCT; } + PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_50_POWER_CONSTRUCT; } + + GIVEN { + PLAYER(baseSpecies) + { + Ability(ABILITY_POWER_CONSTRUCT); + HP((GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP) / 2) + 1); + } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SCRATCH); MOVE(player, MOVE_CELEBRATE); } + } SCENE { + MESSAGE("You sense the presence of many!"); + ABILITY_POPUP(player, ABILITY_POWER_CONSTRUCT); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_POWER_CONSTRUCT, player); + } THEN { + EXPECT_EQ(player->species, SPECIES_ZYGARDE_COMPLETE); + } +} + +WILD_BATTLE_TEST("Power Construct Zygarde reverts to its original form upon catching") +{ + u16 baseSpecies; + PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_10_POWER_CONSTRUCT; } + PARAMETRIZE { baseSpecies = SPECIES_ZYGARDE_50_POWER_CONSTRUCT; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(baseSpecies) + { + Ability(ABILITY_POWER_CONSTRUCT); + HP((GetMonData(&OPPONENT_PARTY[0], MON_DATA_MAX_HP) / 2) + 1); + } + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH); } + TURN { USE_ITEM(player, ITEM_MASTER_BALL); } + } SCENE { + // Turn 1 + MESSAGE("You sense the presence of many!"); + ABILITY_POPUP(opponent, ABILITY_POWER_CONSTRUCT); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_POWER_CONSTRUCT, opponent); + + // Turn 2 + ANIMATION(ANIM_TYPE_SPECIAL, B_ANIM_BALL_THROW, player); + } THEN { + EXPECT_EQ(GetMonData(&gPlayerParty[1], MON_DATA_SPECIES), baseSpecies); + } +} From a10a824081a8040a8b776dd968c2f6234c8793c0 Mon Sep 17 00:00:00 2001 From: Zimmermann Gyula Date: Sat, 1 Nov 2025 13:06:49 +0100 Subject: [PATCH 096/130] Change magic number to constant in dexnav. (#8102) --- src/dexnav.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dexnav.c b/src/dexnav.c index fe4b267a70..581178e940 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -1517,7 +1517,7 @@ static u8 GetEncounterLevelFromMapData(u16 species, enum EncounterType environme { u32 headerId = GetCurrentMapWildMonHeaderId(); enum TimeOfDay timeOfDay; - u8 min = 100; + u8 min = MAX_LEVEL; u8 max = 0; u8 i; From dba50c91eacddf7bfc856c8aaf70344c868f4b11 Mon Sep 17 00:00:00 2001 From: grintoul <166724814+grintoul1@users.noreply.github.com> Date: Sat, 1 Nov 2025 12:43:31 +0000 Subject: [PATCH 097/130] Aura Break tests (#8099) --- test/battle/ability/aura_break.c | 115 ++++++++++++++++++++++++++++++- 1 file changed, 112 insertions(+), 3 deletions(-) diff --git a/test/battle/ability/aura_break.c b/test/battle/ability/aura_break.c index 93b21421e4..389ab327b1 100644 --- a/test/battle/ability/aura_break.c +++ b/test/battle/ability/aura_break.c @@ -1,6 +1,115 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Aura Break inverts Fairy Aura's effect"); -TO_DO_BATTLE_TEST("Aura Break inverts Dark Aura's effect"); -TO_DO_BATTLE_TEST("Aura Break ignores Mold Breaker abilities"); +DOUBLE_BATTLE_TEST("Aura Break inverts Fairy Aura's effect") +{ + s16 damage[3]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ZYGARDE_50) { Ability(ABILITY_AURA_BREAK); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft); } + TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft); SWITCH(playerRight, 2); } + TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft); SWITCH(opponentRight, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[0]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[1]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[2]); + + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[1]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(0.75), damage[2]); + } +} + +DOUBLE_BATTLE_TEST("Aura Break inverts Dark Aura's effect") +{ + s16 damage[3]; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ZYGARDE_50) { Ability(ABILITY_AURA_BREAK); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_BITE, target:opponentLeft); } + TURN { MOVE(playerLeft, MOVE_BITE, target:opponentLeft); SWITCH(playerRight, 2); } + TURN { MOVE(playerLeft, MOVE_BITE, target:opponentLeft); SWITCH(opponentRight, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[0]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[1]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[2]); + + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[1]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(0.75), damage[2]); + } +} + +DOUBLE_BATTLE_TEST("Aura Break ignores Mold Breaker abilities") +{ + s16 damage[6]; + u32 species = SPECIES_WOBBUFFET, ability = ABILITY_NONE; + + PARAMETRIZE { species = SPECIES_WOBBUFFET, ability = ABILITY_SHADOW_TAG; } + PARAMETRIZE { species = SPECIES_CRANIDOS, ability = ABILITY_MOLD_BREAKER; } + PARAMETRIZE { species = SPECIES_ZEKROM, ability = ABILITY_TERAVOLT; } + PARAMETRIZE { species = SPECIES_RESHIRAM, ability = ABILITY_TURBOBLAZE; } + + GIVEN { + PLAYER(species) { Ability(ability); Level(50); } + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); } + PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ZYGARDE_50) { Ability(ABILITY_AURA_BREAK); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_BITE, target: opponentLeft); } + TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target: opponentLeft); } + TURN { MOVE(playerLeft, MOVE_BITE, target: opponentLeft); SWITCH(playerRight, 2); } + TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target: opponentLeft); SWITCH(playerRight, 3); } + TURN { MOVE(playerLeft, MOVE_BITE, target: opponentLeft); SWITCH(playerRight, 2); SWITCH(opponentRight, 2); } + TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target: opponentLeft); SWITCH(playerRight, 3); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[0]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[1]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[2]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[3]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[4]); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[5]); + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[2]); + EXPECT_MUL_EQ(damage[0], UQ_4_12(0.75), damage[4]); + EXPECT_MUL_EQ(damage[1], UQ_4_12(1.33), damage[3]); + EXPECT_MUL_EQ(damage[1], UQ_4_12(0.75), damage[5]); + } +} From 47a1704391ccd5b953c9bfdd2bd30df94c57e236 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sat, 1 Nov 2025 15:48:56 +0100 Subject: [PATCH 098/130] Fix docs compile issue (#8101) --- docs/book.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/book.toml b/docs/book.toml index 3282d8c8e7..a1e994eff3 100644 --- a/docs/book.toml +++ b/docs/book.toml @@ -1,6 +1,5 @@ [book] language = "en" -multilingual = false src = "." title = "pokeemerald-expansion" From 3fb472777baa4a4c8d89153b8130223d7bb2805d Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sat, 1 Nov 2025 16:51:37 +0100 Subject: [PATCH 099/130] Reverts wrongly applies fix to book.toml (#8105) --- docs/book.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/book.toml b/docs/book.toml index a1e994eff3..3282d8c8e7 100644 --- a/docs/book.toml +++ b/docs/book.toml @@ -1,5 +1,6 @@ [book] language = "en" +multilingual = false src = "." title = "pokeemerald-expansion" From dd1b0b306743296e3f0d46749a3bbb3a64763fdb Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sat, 1 Nov 2025 19:39:28 +0100 Subject: [PATCH 100/130] Initialize DamageContext struct with zero values Also forgot to initialize the struct in `Cmd_damagecalc` --- 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 353d0e16e7..bc4e8d6551 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1806,7 +1806,7 @@ static void Cmd_damagecalc(void) u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); - struct DamageContext ctx; + struct DamageContext ctx = {0}; ctx.battlerAtk = gBattlerAttacker; ctx.move = gCurrentMove; ctx.moveType = GetBattleMoveType(gCurrentMove); From f969c126b1f74a799f98f0bb9551b737abe812eb Mon Sep 17 00:00:00 2001 From: Hedara Date: Sat, 1 Nov 2025 21:51:06 +0100 Subject: [PATCH 101/130] 1.13.3 release version --- .../ISSUE_TEMPLATE/01_battle_engine_bugs.yaml | 3 +- .../ISSUE_TEMPLATE/02_battle_ai_issues.yaml | 3 +- .github/ISSUE_TEMPLATE/04_other_errors.yaml | 3 +- README.md | 2 +- docs/SUMMARY.md | 1 + docs/changelogs/1.13.x/1.13.3.md | 193 ++++++++++++++++++ include/constants/expansion.h | 4 +- 7 files changed, 203 insertions(+), 6 deletions(-) create mode 100644 docs/changelogs/1.13.x/1.13.3.md diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml index 56368ef27c..4564bad007 100644 --- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml +++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml @@ -43,9 +43,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using? options: - - 1.13.2 (Latest release) + - 1.13.3 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.13.2 - 1.13.1 - 1.13.0 - 1.12.3 diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml index 06c55598d6..8ee1d1cc17 100644 --- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml +++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml @@ -43,9 +43,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using? options: - - 1.13.2 (Latest release) + - 1.13.3 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.13.2 - 1.13.1 - 1.13.0 - 1.12.3 diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml index 56cfecf33a..5791967807 100644 --- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml +++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml @@ -43,9 +43,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using? options: - - 1.13.2 (Latest release) + - 1.13.3 (Latest release) - master (default, unreleased bugfixes) - upcoming (Edge) + - 1.13.2 - 1.13.1 - 1.13.0 - 1.12.3 diff --git a/README.md b/README.md index 8f9088a6e8..2389dfe26a 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ If you use **`pokeemerald-expansion`**, please credit **RHH (Rom Hacking Hideout)**. Optionally, include the version number for clarity. ``` -Based off RHH's pokeemerald-expansion 1.13.2 https://github.com/rh-hideout/pokeemerald-expansion/ +Based off RHH's pokeemerald-expansion 1.13.3 https://github.com/rh-hideout/pokeemerald-expansion/ ``` Please consider [crediting all contributors](CREDITS.md) involved in the project! diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 7f20718f30..8d1e4e9cdb 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -39,6 +39,7 @@ - [How to use Trainer Party Pools](tutorials/how_to_trainer_party_pool.md) - [Changelog](./CHANGELOG.md) - [1.13.x]() + - [Version 1.13.3](changelogs/1.13.x/1.13.3.md) - [Version 1.13.2](changelogs/1.13.x/1.13.2.md) - [Version 1.13.1](changelogs/1.13.x/1.13.1.md) - [Version 1.13.0](changelogs/1.13.x/1.13.0.md) diff --git a/docs/changelogs/1.13.x/1.13.3.md b/docs/changelogs/1.13.x/1.13.3.md new file mode 100644 index 0000000000..70e1830ff4 --- /dev/null +++ b/docs/changelogs/1.13.x/1.13.3.md @@ -0,0 +1,193 @@ +```md +## How to update +- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`. +- Once you have your remote set up, run the command `git pull RHH expansion/1.13.3 +`. +``` + + +## 🧬 General 🧬 +### Added +* Add test to detect save file shifting by @Bassoonian in [#8030](https://github.com/rh-hideout/pokeemerald-expansion/pull/8030) + +### Changed +* 1.13.2 release by @hedara90 in [#7831](https://github.com/rh-hideout/pokeemerald-expansion/pull/7831) +* Remove unnecessary EWRAM and IWRAM variables from the Window code by @estellarc in [#7897](https://github.com/rh-hideout/pokeemerald-expansion/pull/7897) +* Replace magic numbers with define'd values in field_player_avatar.c by @FosterProgramming in [#7910](https://github.com/rh-hideout/pokeemerald-expansion/pull/7910) +* Pret merge (1st of November, 2025) by @hedara90 in [#8103](https://github.com/rh-hideout/pokeemerald-expansion/pull/8103) + +### Fixed +* Fixes `EVO_BATTLE_END` evolutions not removing item with additional conditions by @PhallenTree in [#7841](https://github.com/rh-hideout/pokeemerald-expansion/pull/7841) +* Fix EV display in debug menu by @cawtds in [#7848](https://github.com/rh-hideout/pokeemerald-expansion/pull/7848) +* Fix right player position battle partner target display by @ravepossum in [#7878](https://github.com/rh-hideout/pokeemerald-expansion/pull/7878) +* Ensure last used ball and move description window sprites don't free palette too early by @ravepossum in [#7875](https://github.com/rh-hideout/pokeemerald-expansion/pull/7875) +* Fix fusion pokemon aquiring illegal movesets by @FosterProgramming in [#7896](https://github.com/rh-hideout/pokeemerald-expansion/pull/7896) + - Calyrex will now delete moves if they are not part of its learnset when unfusing + - Kyurem will now properly swap the moves Glaciate and Scary Face to its signature moves when fusing/unfusing +* Fix bug causing hgss dex to freeze by @FosterProgramming in [#7936](https://github.com/rh-hideout/pokeemerald-expansion/pull/7936) + - Fix a bug when checking evolutions info screen while search mode is active in the hgss dex +* Show convergent evolution to Gholdengo in HGSS dex by @FosterProgramming in [#7934](https://github.com/rh-hideout/pokeemerald-expansion/pull/7934) +* Fix HGSS dex sprites for gen9+ by @FosterProgramming in [#7922](https://github.com/rh-hideout/pokeemerald-expansion/pull/7922) +* Fix nicknames containing many / overflowing the party screen textbox by @hedara90 in [#7970](https://github.com/rh-hideout/pokeemerald-expansion/pull/7970) +* Fix ruination and nature's madness damage percentage by @FosterProgramming in [#7983](https://github.com/rh-hideout/pokeemerald-expansion/pull/7983) +* Fix ribbon colours by @hedara90 in [#7971](https://github.com/rh-hideout/pokeemerald-expansion/pull/7971) +* Fix long pokemon name in partner party not appearing properly by @FosterProgramming in [#8009](https://github.com/rh-hideout/pokeemerald-expansion/pull/8009) +* Fix battle dome bug (again) by @FosterProgramming in [#8007](https://github.com/rh-hideout/pokeemerald-expansion/pull/8007) +* Fix battle arena counting all judges loss for the opponent by @FosterProgramming in [#8046](https://github.com/rh-hideout/pokeemerald-expansion/pull/8046) + - Fix battle arena referees giving undeserved wins to the player +* Fix wrong gimmick spite showing when inputting too fast by @FosterProgramming in [#8066](https://github.com/rh-hideout/pokeemerald-expansion/pull/8066) + +## 🗺️ Overworld 🗺️ +### Fixed +* Fix LTO breaking with FREE_MYSTERY_GIFT set to TRUE by @DizzyEggg in [#7844](https://github.com/rh-hideout/pokeemerald-expansion/pull/7844) +* Fix dns palette weight by @FosterProgramming in [#7855](https://github.com/rh-hideout/pokeemerald-expansion/pull/7855) +* Bug Fix: NPC follower not inheriting facing direction upon creation by @Bivurnum in [#7895](https://github.com/rh-hideout/pokeemerald-expansion/pull/7895) +* Fix follower pokemon not playing animation when colliding by @FosterProgramming in [#7908](https://github.com/rh-hideout/pokeemerald-expansion/pull/7908) +* Fix incorrect font width in Dexnav search window hiding some elements by @FosterProgramming in [#7949](https://github.com/rh-hideout/pokeemerald-expansion/pull/7949) +* Fix dns color transition not applying weather blending by @FosterProgramming in [#7883](https://github.com/rh-hideout/pokeemerald-expansion/pull/7883) +* Fix follower NPC sidewaystair movement by @FosterProgramming in [#7909](https://github.com/rh-hideout/pokeemerald-expansion/pull/7909) +* Fix battle dome pre round 1 waiting room by @FosterProgramming in [#7976](https://github.com/rh-hideout/pokeemerald-expansion/pull/7976) +* Fix no_effect script command overwriting trainer data in trainer script by @FosterProgramming in [#7978](https://github.com/rh-hideout/pokeemerald-expansion/pull/7978) +* Setting wallclock time now properly sets fakeRTC by @FosterProgramming in [#7860](https://github.com/rh-hideout/pokeemerald-expansion/pull/7860) + - Fix time bug when setting wallclock in fakeRTC mode + - When setting the wall clock, it will start on current time instead of 10AM + - If FakeRTC is active, new game will start at 10AM +* Bugfix hidefollower not waiting properly by @FosterProgramming in [#7768](https://github.com/rh-hideout/pokeemerald-expansion/pull/7768) +* Bugfix Emotes not loading their palette by @estellarc in [#7843](https://github.com/rh-hideout/pokeemerald-expansion/pull/7843) +* Fix OW Pokémon VObjects by @HashtagMarky in [#7991](https://github.com/rh-hideout/pokeemerald-expansion/pull/7991) +* fix: hypertraining a stat now optionally reflects in the summary screen by @khbsd in [#8035](https://github.com/rh-hideout/pokeemerald-expansion/pull/8035) +* Fix pc turning on/off animation not working in battle frontier by @FosterProgramming in [#8048](https://github.com/rh-hideout/pokeemerald-expansion/pull/8048) +* Fix non-battle trainer script not running properly by @FosterProgramming in [#8056](https://github.com/rh-hideout/pokeemerald-expansion/pull/8056) + +## 🐉 Pokémon 🐉 +### Fixed +* Fixes shininess for givemon by @cawtds in [#7847](https://github.com/rh-hideout/pokeemerald-expansion/pull/7847) +* Fix Minior start of battle form by @hedara90 in [#7972](https://github.com/rh-hideout/pokeemerald-expansion/pull/7972) +* Add error messages for trying to send an illegal mon to the PC and fixes index in double wild battles by @hedara90 in [#7982](https://github.com/rh-hideout/pokeemerald-expansion/pull/7982) +* fix: hypertraining a stat now optionally reflects in the summary screen by @khbsd in [#8035](https://github.com/rh-hideout/pokeemerald-expansion/pull/8035) +* Add camera-facing right-walking Krabby and Kingler follower sprites by @rayrobdod in [#7881](https://github.com/rh-hideout/pokeemerald-expansion/pull/7881) + +## ⚔️ Battle General ⚔️ +### Changed +* Tests for Battery ability by @grintoul1 in [#7846](https://github.com/rh-hideout/pokeemerald-expansion/pull/7846) +* Aura Break tests by @grintoul1 in [#8099](https://github.com/rh-hideout/pokeemerald-expansion/pull/8099) + +### Fixed +* Fixes Endure lasting forever by @AlexOn1ine in [#7838](https://github.com/rh-hideout/pokeemerald-expansion/pull/7838) +* Fix for uncaught mon with terrain active by @DizzyEggg in [#7868](https://github.com/rh-hideout/pokeemerald-expansion/pull/7868) +* Fixes Steadfast not activating + tests by @PhallenTree in [#7886](https://github.com/rh-hideout/pokeemerald-expansion/pull/7886) +* Fix hgss pokedex when catching mon with terrain by @DizzyEggg in [#7884](https://github.com/rh-hideout/pokeemerald-expansion/pull/7884) +* Fix SmartStrike crashing the game in double battles by @DizzyEggg in [#7902](https://github.com/rh-hideout/pokeemerald-expansion/pull/7902) +* Fix palaceUnableToUseMove falling through to change battle script by @ghoulslash in [#7912](https://github.com/rh-hideout/pokeemerald-expansion/pull/7912) +* Add new Move target types to GetBattlePalaceMoveGroup by @ghoulslash in [#7913](https://github.com/rh-hideout/pokeemerald-expansion/pull/7913) +* Fixes 2 instances of global usage in the `Cmd_adjustdamage` loop by @AlexOn1ine in [#7918](https://github.com/rh-hideout/pokeemerald-expansion/pull/7918) +* Fix Battle Anim monbg calls Part 1 by @ghoulslash in [#7906](https://github.com/rh-hideout/pokeemerald-expansion/pull/7906) +* Adds missing breakable flag for Bulletproof by @AlexOn1ine in [#7928](https://github.com/rh-hideout/pokeemerald-expansion/pull/7928) +* Fix multiple battle arena bugs by @FosterProgramming in [#7941](https://github.com/rh-hideout/pokeemerald-expansion/pull/7941) +* Fixes Cursed Body failing to disable moves on the last PP by @PhallenTree in [#7940](https://github.com/rh-hideout/pokeemerald-expansion/pull/7940) +* Fixed an issue related to Pokemon animation bleeding into attack anim… by @LinathanZel in [#7924](https://github.com/rh-hideout/pokeemerald-expansion/pull/7924) +* Fixes terrain not failing on duplicate by @AlexOn1ine in [#7939](https://github.com/rh-hideout/pokeemerald-expansion/pull/7939) +* Fix volt tackle not inflicting recoil by @FosterProgramming in [#7944](https://github.com/rh-hideout/pokeemerald-expansion/pull/7944) +* Fix Knock Off not being restored and Wild Battles by @ghoulslash in [#7952](https://github.com/rh-hideout/pokeemerald-expansion/pull/7952) +* Fix Anticipation type effectiveness check by @spindrift64 in [#7840](https://github.com/rh-hideout/pokeemerald-expansion/pull/7840) +* Fix Cherim and Castfrom not reverting to baseform when Teraform Zero is triggered by @FosterProgramming in [#7961](https://github.com/rh-hideout/pokeemerald-expansion/pull/7961) +* Fix Focus Energy boosting crit by the wrong amount with gen1 crit chance by @FosterProgramming in [#7956](https://github.com/rh-hideout/pokeemerald-expansion/pull/7956) +* Fix bug where transformed pokemon lose copied stats on levelup by @FosterProgramming in [#7969](https://github.com/rh-hideout/pokeemerald-expansion/pull/7969) +* Fixes Shields Down incorrectly preventing status on Minior Core form by @PhallenTree in [#7968](https://github.com/rh-hideout/pokeemerald-expansion/pull/7968) +* SetShellSideArmCategory avoid div by zero by @DizzyEggg in [#7980](https://github.com/rh-hideout/pokeemerald-expansion/pull/7980) +* CalcBarFilledPixels Safe Div by @DizzyEggg in [#7979](https://github.com/rh-hideout/pokeemerald-expansion/pull/7979) +* Fix psychic terrain affecting semi-invulnerable mons by @FosterProgramming in [#7986](https://github.com/rh-hideout/pokeemerald-expansion/pull/7986) +* Fixes Terrain Extender timer by @AlexOn1ine in [#7995](https://github.com/rh-hideout/pokeemerald-expansion/pull/7995) +* Fixed Max Move in-battle descriptions by @AsparagusEduardo in [#8004](https://github.com/rh-hideout/pokeemerald-expansion/pull/8004) +* Fixes Echoed Voice base power increase depending on attacker's use of the move by @PhallenTree in [#7997](https://github.com/rh-hideout/pokeemerald-expansion/pull/7997) +* Fixed Stomping Tantrum not doubling in damage if the user failed Protect by @AsparagusEduardo in [#8008](https://github.com/rh-hideout/pokeemerald-expansion/pull/8008) +* Fix badge boost not applying in gen1 and 2 by @FosterProgramming in [#8013](https://github.com/rh-hideout/pokeemerald-expansion/pull/8013) +* Fix toxic debris setting hazards on the wrong side when hit by an ally by @FosterProgramming in [#8026](https://github.com/rh-hideout/pokeemerald-expansion/pull/8026) +* Adds missing alive check for Rapid Spin by @AlexOn1ine in [#8024](https://github.com/rh-hideout/pokeemerald-expansion/pull/8024) +* Fixes visual glitch after Misty Explosion by @AlexOn1ine in [#8022](https://github.com/rh-hideout/pokeemerald-expansion/pull/8022) +* Fixes Protosynthesis not activating after weather was reset by @AlexOn1ine in [#8021](https://github.com/rh-hideout/pokeemerald-expansion/pull/8021) +* Fix Salt Cure script by @AlexOn1ine in [#8005](https://github.com/rh-hideout/pokeemerald-expansion/pull/8005) +* Fix emergency exit not triggering properly during wild battles by @FosterProgramming in [#8037](https://github.com/rh-hideout/pokeemerald-expansion/pull/8037) +* Fix target cancelling not working properly with z-move by @FosterProgramming in [#8067](https://github.com/rh-hideout/pokeemerald-expansion/pull/8067) +* Corrects battler partner identification in battle_ai_switch_items.c by @grintoul1 in [#8071](https://github.com/rh-hideout/pokeemerald-expansion/pull/8071) +* Fix Ally Switch being useable in Frontier Link Multi battles by @grintoul1 in [#8059](https://github.com/rh-hideout/pokeemerald-expansion/pull/8059) +* Fixes hazards and switch-in items not being reset when switching in by @PhallenTree in [#8074](https://github.com/rh-hideout/pokeemerald-expansion/pull/8074) +* Fixes Liquid Ooze dmg not blocked by Magic Guard by @AlexOn1ine in [#8036](https://github.com/rh-hideout/pokeemerald-expansion/pull/8036) +* Fix move description prompt window not appear when choosing a move after canceling target selection by @FosterProgramming in [#8055](https://github.com/rh-hideout/pokeemerald-expansion/pull/8055) +* Initialize DamageContext on declaration to zero by @AlexOn1ine in [#8076](https://github.com/rh-hideout/pokeemerald-expansion/pull/8076) +* Fixed Hunger Switch changing forms on switch out while Tera'd by @AsparagusEduardo in [#8080](https://github.com/rh-hideout/pokeemerald-expansion/pull/8080) +* Fixes Gooey/Tangling Hair ability pop up triggering on Clear Body by @AlexOn1ine in [#8083](https://github.com/rh-hideout/pokeemerald-expansion/pull/8083) +* Fixes intimidate activating on empty field by @AlexOn1ine in [#8058](https://github.com/rh-hideout/pokeemerald-expansion/pull/8058) +* Fix bug where mon selection doesn't properly account for party order by @FosterProgramming in [#8088](https://github.com/rh-hideout/pokeemerald-expansion/pull/8088) +* Fix bug when a captured pokemon replaces a party member who changed forms by @FosterProgramming in [#8091](https://github.com/rh-hideout/pokeemerald-expansion/pull/8091) +* Fixed Zygarde Complete disappearing upon catch by @AsparagusEduardo in [#8089](https://github.com/rh-hideout/pokeemerald-expansion/pull/8089) +* Initialize DamageContext struct with zero values by @AlexOn1ine in [#8107](https://github.com/rh-hideout/pokeemerald-expansion/pull/8107) + +## 🤹 Moves 🤹 +### Fixed +* Updated Mountain Gale's PP for Gen 9 by @fdeblasio in [#7856](https://github.com/rh-hideout/pokeemerald-expansion/pull/7856) +* Fix Brine move anim and document Water Spout anim by @ravepossum in [#7865](https://github.com/rh-hideout/pokeemerald-expansion/pull/7865) +* Add Struggle tests, weakness berry tests and prevent Struggle from activating Silk Scarf and Chilan Berry by @rayrobdod in [#7880](https://github.com/rh-hideout/pokeemerald-expansion/pull/7880) +* Fix Battle Anim monbg calls Part 1 by @ghoulslash in [#7906](https://github.com/rh-hideout/pokeemerald-expansion/pull/7906) +* Add missing end signal for AnimTask_SetAttackerInvisibleWaitForSignal by @hedara90 in [#7950](https://github.com/rh-hideout/pokeemerald-expansion/pull/7950) +* Fix Ally Switch being useable in Frontier Link Multi battles by @grintoul1 in [#8059](https://github.com/rh-hideout/pokeemerald-expansion/pull/8059) +* Fixed Belly Drum/Contrary interaction at max Attack by @AsparagusEduardo in [#8078](https://github.com/rh-hideout/pokeemerald-expansion/pull/8078) + +## 🎭 Abilities 🎭 +### Changed +* Tests for Battery ability by @grintoul1 in [#7846](https://github.com/rh-hideout/pokeemerald-expansion/pull/7846) +* Aura Break tests by @grintoul1 in [#8099](https://github.com/rh-hideout/pokeemerald-expansion/pull/8099) + +## 🧶 Items 🧶 +### Fixed +* Add gBallItemIds Array by @HashtagMarky in [#7905](https://github.com/rh-hideout/pokeemerald-expansion/pull/7905) +* Fix Persim Berry battle usage by @hedara90 in [#7963](https://github.com/rh-hideout/pokeemerald-expansion/pull/7963) + +## 🤖 Battle AI 🤖 +### Fixed +* Add failsafe to AI_DecideHoldEffectForTurn by @AlexOn1ine in [#7849](https://github.com/rh-hideout/pokeemerald-expansion/pull/7849) +* Fix some ai action check happening before the logic was computed by @FosterProgramming in [#7867](https://github.com/rh-hideout/pokeemerald-expansion/pull/7867) + - Roamers will now flee in the first turn of battle +* Fix ShouldPivot overwriting random memory by @DizzyEggg in [#7882](https://github.com/rh-hideout/pokeemerald-expansion/pull/7882) +* Fix AI seeing priority wrong for players choice lock by @MaximeGr00 in [#7899](https://github.com/rh-hideout/pokeemerald-expansion/pull/7899) +* fix (post-KO switch): force AI data recalc to see abilities on field correctly when pivot moves used by player by @ghostyboyy97 in [#7900](https://github.com/rh-hideout/pokeemerald-expansion/pull/7900) +* Add missing break to Power Split AI case by @ghoulslash in [#7959](https://github.com/rh-hideout/pokeemerald-expansion/pull/7959) + +## 🧹 Other Cleanup 🧹 +* Fix some failed and assume fail tests with `GEN_LATEST` = `GEN_5` by @AsparagusEduardo in [#7735](https://github.com/rh-hideout/pokeemerald-expansion/pull/7735) +* Update INSTALL.md by @RubyRaven6 in [#7852](https://github.com/rh-hideout/pokeemerald-expansion/pull/7852) +* Remove unnecessary EWRAM and IWRAM variables from the Window code by @estellarc in [#7897](https://github.com/rh-hideout/pokeemerald-expansion/pull/7897) +* Replace magic numbers with define'd values in field_player_avatar.c by @FosterProgramming in [#7910](https://github.com/rh-hideout/pokeemerald-expansion/pull/7910) +* Reverts wrongly applies fix to book.toml by @AlexOn1ine in [#8105](https://github.com/rh-hideout/pokeemerald-expansion/pull/8105) + +## 🧪 Test Runner 🧪 +### Changed +* Fix some failed and assume fail tests with `GEN_LATEST` = `GEN_5` by @AsparagusEduardo in [#7735](https://github.com/rh-hideout/pokeemerald-expansion/pull/7735) +* Tests for Battery ability by @grintoul1 in [#7846](https://github.com/rh-hideout/pokeemerald-expansion/pull/7846) +* Fixed fainting form change tests by @AsparagusEduardo in [#8079](https://github.com/rh-hideout/pokeemerald-expansion/pull/8079) +* Aura Break tests by @grintoul1 in [#8099](https://github.com/rh-hideout/pokeemerald-expansion/pull/8099) + +### Fixed +* Fix Knock Off not being restored and Wild Battles by @ghoulslash in [#7952](https://github.com/rh-hideout/pokeemerald-expansion/pull/7952) +* Fixes Shields Down incorrectly preventing status on Minior Core form by @PhallenTree in [#7968](https://github.com/rh-hideout/pokeemerald-expansion/pull/7968) +* Fixed Stomping Tantrum not doubling in damage if the user failed Protect by @AsparagusEduardo in [#8008](https://github.com/rh-hideout/pokeemerald-expansion/pull/8008) +* Fix stats defined in tests being overwritteng by stat change by @FosterProgramming in [#8018](https://github.com/rh-hideout/pokeemerald-expansion/pull/8018) + +## 📚 Documentation 📚 +* Update INSTALL.md by @RubyRaven6 in [#7852](https://github.com/rh-hideout/pokeemerald-expansion/pull/7852) +* Updated PR template to make existing credit policy clearer by @pkmnsnfrn in [#7864](https://github.com/rh-hideout/pokeemerald-expansion/pull/7864) +* Fix image links in doc site by @rayrobdod in [#7948](https://github.com/rh-hideout/pokeemerald-expansion/pull/7948) +* Add all pages in `docs` to doc website by @rayrobdod in [#7907](https://github.com/rh-hideout/pokeemerald-expansion/pull/7907) +* Relativize doc links, to fix links in docs site by @rayrobdod in [#7964](https://github.com/rh-hideout/pokeemerald-expansion/pull/7964) +* Fix docs compile issue by @AlexOn1ine in [#8101](https://github.com/rh-hideout/pokeemerald-expansion/pull/8101) +* Reverts wrongly applies fix to book.toml by @AlexOn1ine in [#8105](https://github.com/rh-hideout/pokeemerald-expansion/pull/8105) + +## New Contributors +* @HashtagMarky made their first contribution in [#7905](https://github.com/rh-hideout/pokeemerald-expansion/pull/7905) +* @MaximeGr00 made their first contribution in [#7899](https://github.com/rh-hideout/pokeemerald-expansion/pull/7899) + +**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.13.2...expansion/1.13.3 + + + + diff --git a/include/constants/expansion.h b/include/constants/expansion.h index 92973512b2..52f97f7c31 100644 --- a/include/constants/expansion.h +++ b/include/constants/expansion.h @@ -1,13 +1,13 @@ #ifndef GUARD_CONSTANTS_EXPANSION_H #define GUARD_CONSTANTS_EXPANSION_H -// Last version: 1.13.2 +// Last version: 1.13.3 #define EXPANSION_VERSION_MAJOR 1 #define EXPANSION_VERSION_MINOR 13 #define EXPANSION_VERSION_PATCH 3 // FALSE if this this version of Expansion is not a tagged commit, i.e. // it contains unreleased changes. -#define EXPANSION_TAGGED_RELEASE FALSE +#define EXPANSION_TAGGED_RELEASE TRUE #endif From cfdbd19ea89593eace0432940a48a9b0273da7bd Mon Sep 17 00:00:00 2001 From: Hedara Date: Sat, 1 Nov 2025 21:51:24 +0100 Subject: [PATCH 102/130] Start of 1.13.4 cycle --- include/constants/expansion.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/constants/expansion.h b/include/constants/expansion.h index 52f97f7c31..1b9fba88a1 100644 --- a/include/constants/expansion.h +++ b/include/constants/expansion.h @@ -4,10 +4,10 @@ // Last version: 1.13.3 #define EXPANSION_VERSION_MAJOR 1 #define EXPANSION_VERSION_MINOR 13 -#define EXPANSION_VERSION_PATCH 3 +#define EXPANSION_VERSION_PATCH 4 // FALSE if this this version of Expansion is not a tagged commit, i.e. // it contains unreleased changes. -#define EXPANSION_TAGGED_RELEASE TRUE +#define EXPANSION_TAGGED_RELEASE FALSE #endif From cbb6c4f14eeb1d554df256b312faf2a7c6ef50c8 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 3 Nov 2025 15:02:12 +0100 Subject: [PATCH 103/130] Revert reversion (#8112) --- docs/book.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/book.toml b/docs/book.toml index 3282d8c8e7..a1e994eff3 100644 --- a/docs/book.toml +++ b/docs/book.toml @@ -1,6 +1,5 @@ [book] language = "en" -multilingual = false src = "." title = "pokeemerald-expansion" From 499986a8049d17d75839df164fbf4eae657fe87c Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 3 Nov 2025 15:02:36 +0100 Subject: [PATCH 104/130] Clean up redundant todo (#8094) --- src/battle_util.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 9adfcc5016..c3cd8c5b54 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5528,7 +5528,6 @@ bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 ability return FALSE; } -// TODO: check order of battlerAtk and battlerDef bool32 CanBeBurned(u32 battlerAtk, u32 battlerDef, u32 abilityDef) { if (CanSetNonVolatileStatus( @@ -10920,7 +10919,6 @@ bool32 PickupHasValidTarget(u32 battler) return FALSE; } -// TODO: Pass down weather as an arg bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags) { if (gBattleWeather & weatherFlags && HasWeatherEffect()) From c981fb3b2e0e39634525d4a4bd29ab6992d68568 Mon Sep 17 00:00:00 2001 From: grintoul1 Date: Mon, 3 Nov 2025 18:34:54 +0000 Subject: [PATCH 105/130] Fixes difficulty not being restored after tests --- test/battle/trainer_control.c | 3 +++ test/test_runner_battle.c | 1 + 2 files changed, 4 insertions(+) diff --git a/test/battle/trainer_control.c b/test/battle/trainer_control.c index 663a720d3d..1821cebcb5 100644 --- a/test/battle/trainer_control.c +++ b/test/battle/trainer_control.c @@ -188,6 +188,7 @@ TEST("Difficulty default to Normal is the trainer doesn't have a member for the CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainers[GetTrainerDifficultyLevelTest(currTrainer)][currTrainer], TRUE, BATTLE_TYPE_TRAINER); EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_MEWTWO); Free(testParty); + SetCurrentDifficultyLevel(DIFFICULTY_NORMAL); } TEST("Difficulty changes which party if used for NPCs if defined for the difficulty (EASY)") @@ -199,6 +200,7 @@ TEST("Difficulty changes which party if used for NPCs if defined for the difficu EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_METAPOD); EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 1); Free(testParty); + SetCurrentDifficultyLevel(DIFFICULTY_NORMAL); } TEST("Difficulty changes which party if used for NPCs if defined for the difficulty (HARD)") @@ -210,6 +212,7 @@ TEST("Difficulty changes which party if used for NPCs if defined for the difficu EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_ARCEUS); EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 99); Free(testParty); + SetCurrentDifficultyLevel(DIFFICULTY_NORMAL); } TEST("Difficulty changes which party if used for NPCs if defined for the difficulty (NORMAL)") diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 157cec9018..395381d1d2 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1344,6 +1344,7 @@ static void TearDownBattle(void) // Zero out the parties, data in them could potentially carry over ZeroPlayerPartyMons(); ZeroEnemyPartyMons(); + SetCurrentDifficultyLevel(DIFFICULTY_NORMAL); FreeMonSpritesGfx(); FreeBattleSpritesData(); From 6bcf9823c5d49971dfbf9cfb5a9d3360c2d2bc12 Mon Sep 17 00:00:00 2001 From: grintoul <166724814+grintoul1@users.noreply.github.com> Date: Mon, 3 Nov 2025 20:58:03 +0000 Subject: [PATCH 106/130] Lock mdbook to v0.4.35 to fix docs not building (#8130) --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 874ebdd590..b2792e87de 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,7 +17,7 @@ jobs: fetch-depth: 0 - name: Install latest mdbook run: | - tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name') + tag="v0.4.35" url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz" mkdir mdbook curl -sSL $url | tar -xz --directory=./mdbook From 3c72ca11586c3ca96ca06bdb6cb2acb4ecec786a Mon Sep 17 00:00:00 2001 From: ghostyboyy97 <106448956+ghostyboyy97@users.noreply.github.com> Date: Tue, 4 Nov 2025 03:35:43 -0500 Subject: [PATCH 107/130] fix (AI scoring): shield dust considerations, IsMoveEffectInMinus self effect edge case, hitsToKO zero-case consideration (#8126) Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- include/battle_ai_util.h | 1 + src/battle_ai_main.c | 5 ++++- src/battle_ai_util.c | 22 ++++++++++++++++++---- test/battle/ability/shield_dust.c | 28 ++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 1b23dd7e1a..1964e45d53 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -155,6 +155,7 @@ bool32 IsAffectedByPowder(u32 battler, u32 ability, enum ItemHoldEffect holdEffe bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, enum DamageCategory category); enum MoveComparisonResult AI_WhichMoveBetter(u32 move1, u32 move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo); struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef); +bool32 IsAdditionalEffectBlocked(u32 battlerAtk, u32 abilityAtk, u32 battlerDef, u32 abilityDef); struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef, u32 weather); bool32 AI_IsDamagedByRecoil(u32 battler); u32 GetNoOfHitsToKO(u32 dmg, s32 hp); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 2cc7cfe62c..0c8c0bb1cd 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -5069,7 +5069,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) if (gBattleMons[battlerDef].speed > gBattleMons[battlerAtk].speed) ADJUST_SCORE(DECENT_EFFECT); break; -case EFFECT_GUARD_SPLIT: + case EFFECT_GUARD_SPLIT: { u32 atkDefense = gBattleMons[battlerAtk].defense; u32 defDefense = gBattleMons[battlerDef].defense; @@ -5590,6 +5590,9 @@ case EFFECT_GUARD_SPLIT: } else // consider move effects that hinder the target { + if (IsAdditionalEffectBlocked(battlerAtk, aiData->abilities[battlerAtk], battlerDef, aiData->abilities[battlerDef])) + continue; + switch (additionalEffect->moveEffect) { case MOVE_EFFECT_FLINCH: diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 4a397719c0..d866e5b0d0 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -701,6 +701,17 @@ bool32 IsDamageMoveUnusable(struct DamageContext *ctx) return FALSE; } +bool32 IsAdditionalEffectBlocked(u32 battlerAtk, u32 abilityAtk, u32 battlerDef, u32 abilityDef) +{ + if (gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_COVERT_CLOAK) + return TRUE; + + if (abilityDef == ABILITY_SHIELD_DUST && !IsMoldBreakerTypeAbility(battlerAtk, abilityAtk)) + return TRUE; + + return FALSE; +} + static inline s32 GetDamageByRollType(s32 dmg, enum DamageRollType rollType) { if (rollType == DMG_ROLL_LOWEST) @@ -1073,6 +1084,9 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3 } else // consider move effects that hinder the target { + if (IsAdditionalEffectBlocked(battlerAtk, abilityAtk, battlerDef, abilityDef)) + continue; + switch (additionalEffect->moveEffect) { case MOVE_EFFECT_POISON: @@ -1107,7 +1121,7 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3 case MOVE_EFFECT_SP_DEF_MINUS_1: case MOVE_EFFECT_ACC_MINUS_1: case MOVE_EFFECT_EVS_MINUS_1: - if (CanLowerStat(battlerAtk, battlerDef, gAiLogicData, STAT_ATK + (additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_1)) && noOfHitsToKo != 1) + if (CanLowerStat(battlerAtk, battlerDef, gAiLogicData, STAT_ATK + (additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_1)) && noOfHitsToKo > 1) return TRUE; break; case MOVE_EFFECT_ATK_MINUS_2: @@ -1117,7 +1131,7 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3 case MOVE_EFFECT_SP_DEF_MINUS_2: case MOVE_EFFECT_ACC_MINUS_2: case MOVE_EFFECT_EVS_MINUS_2: - if (CanLowerStat(battlerAtk, battlerDef, gAiLogicData, STAT_ATK + (additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_2)) && noOfHitsToKo != 1) + if (CanLowerStat(battlerAtk, battlerDef, gAiLogicData, STAT_ATK + (additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_2)) && noOfHitsToKo > 1) return TRUE; break; default: @@ -1174,7 +1188,7 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s case MOVE_EFFECT_ATK_DEF_DOWN: case MOVE_EFFECT_DEF_SPDEF_DOWN: if ((additionalEffect->self && abilityAtk != ABILITY_CONTRARY) - || (noOfHitsToKo != 1 && abilityDef == ABILITY_CONTRARY && !DoesBattlerIgnoreAbilityChecks(battlerAtk, abilityAtk, move))) + || (noOfHitsToKo > 1 && !additionalEffect->self && abilityDef == ABILITY_CONTRARY && !DoesBattlerIgnoreAbilityChecks(battlerAtk, abilityAtk, move))) return TRUE; break; case MOVE_EFFECT_RECHARGE: @@ -1195,7 +1209,7 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s case MOVE_EFFECT_ACC_PLUS_2: case MOVE_EFFECT_ALL_STATS_UP: if ((additionalEffect->self && abilityAtk == ABILITY_CONTRARY) - || (noOfHitsToKo != 1 && !(abilityDef == ABILITY_CONTRARY && !DoesBattlerIgnoreAbilityChecks(battlerAtk, abilityAtk, move)))) + || (noOfHitsToKo > 1 && !additionalEffect->self && !(abilityDef == ABILITY_CONTRARY && !DoesBattlerIgnoreAbilityChecks(battlerAtk, abilityAtk, move)))) return TRUE; break; default: diff --git a/test/battle/ability/shield_dust.c b/test/battle/ability/shield_dust.c index 2d7358335d..2fc4f51d5c 100644 --- a/test/battle/ability/shield_dust.c +++ b/test/battle/ability/shield_dust.c @@ -188,3 +188,31 @@ SINGLE_BATTLE_TEST("Shield Dust does not prevent ability stat changes") MESSAGE("Vivillon's Speed fell!"); } } + +AI_SINGLE_BATTLE_TEST("AI will score secondary effects against shield dust correctly") +{ + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT); + GIVEN { + PLAYER(SPECIES_DUSTOX){ Ability(ABILITY_SHIELD_DUST); Moves(MOVE_GUST); } + OPPONENT(SPECIES_SUNFLORA){ Ability(ABILITY_EARLY_BIRD); Moves(MOVE_MYSTICAL_FIRE, MOVE_FIERY_DANCE); } + } WHEN { + TURN { + MOVE(player, MOVE_GUST); + EXPECT_MOVE(opponent, MOVE_FIERY_DANCE); + } + } +} + +AI_SINGLE_BATTLE_TEST("AI will score secondary effects against shield dust correctly when it has Mold Breaker") +{ + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT); + GIVEN { + PLAYER(SPECIES_DUSTOX){ Ability(ABILITY_SHIELD_DUST); Moves(MOVE_GUST); } + OPPONENT(SPECIES_SUNFLORA){ Ability(ABILITY_MOLD_BREAKER); Moves(MOVE_MYSTICAL_FIRE, MOVE_FIERY_DANCE); } + } WHEN { + TURN { + MOVE(player, MOVE_GUST); + EXPECT_MOVE(opponent, MOVE_MYSTICAL_FIRE); + } + } +} From b3965098f7fe62831e433dd25c4a9d60906f222d Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Tue, 4 Nov 2025 19:32:54 +0100 Subject: [PATCH 108/130] Fix berry blender not computing flavor correctly (#8113) --- src/berry_blender.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/berry_blender.c b/src/berry_blender.c index f953d7d5e8..adb28dab07 100644 --- a/src/berry_blender.c +++ b/src/berry_blender.c @@ -2432,6 +2432,7 @@ static void CalculatePokeblock(struct BlenderBerry *berries, struct Pokeblock *p } // Factor in max RPM and round + multiuseVar = maxRPM / 333 + 100; for (i = 0; i < FLAVOR_COUNT; i++) { s32 remainder; From be4ce5901fa7b53a1a169715f20eb56257950181 Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Wed, 5 Nov 2025 14:48:53 +0100 Subject: [PATCH 109/130] Fix Kyurem typo in swap move tables (#8139) --- include/pokemon.h | 4 ++-- src/data/pokemon/form_change_table_pointers.h | 4 ++-- src/party_menu.c | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/pokemon.h b/include/pokemon.h index 296713e59d..8852ba3c97 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -654,10 +654,10 @@ extern const struct Fusion *const gFusionTablePointers[NUM_SPECIES]; #if P_FUSION_FORMS #if P_FAMILY_KYUREM #if P_FAMILY_RESHIRAM -extern const u16 gKyurenWhiteSwapMoveTable[][2]; +extern const u16 gKyuremWhiteSwapMoveTable[][2]; #endif //P_FAMILY_RESHIRAM #if P_FAMILY_ZEKROM -extern const u16 gKyurenBlackSwapMoveTable[][2]; +extern const u16 gKyuremBlackSwapMoveTable[][2]; #endif //P_FAMILY_ZEKROM #endif //P_FAMILY_KYUREM #endif //P_FUSION_FORMS diff --git a/src/data/pokemon/form_change_table_pointers.h b/src/data/pokemon/form_change_table_pointers.h index ad2e6a040d..1af386a897 100644 --- a/src/data/pokemon/form_change_table_pointers.h +++ b/src/data/pokemon/form_change_table_pointers.h @@ -38,14 +38,14 @@ const struct Fusion *const gFusionTablePointers[NUM_SPECIES] = #if P_FUSION_FORMS #if P_FAMILY_KYUREM #if P_FAMILY_RESHIRAM -const u16 gKyurenWhiteSwapMoveTable[][2] = +const u16 gKyuremWhiteSwapMoveTable[][2] = { {MOVE_SCARY_FACE, MOVE_FUSION_FLARE}, {MOVE_GLACIATE, MOVE_ICE_BURN}, }; #endif //P_FAMILY_RESHIRAM #if P_FAMILY_ZEKROM -const u16 gKyurenBlackSwapMoveTable[][2] = +const u16 gKyuremBlackSwapMoveTable[][2] = { {MOVE_SCARY_FACE, MOVE_FUSION_BOLT}, {MOVE_GLACIATE, MOVE_FREEZE_SHOCK}, diff --git a/src/party_menu.c b/src/party_menu.c index 891066dd41..04b2233102 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -6431,11 +6431,11 @@ static void Task_TryItemUseFusionChange(u8 taskId) #if P_FAMILY_KYUREM #if P_FAMILY_RESHIRAM if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE) - SwapFusionMonMoves(mon, gKyurenWhiteSwapMoveTable, FUSE_MON); + SwapFusionMonMoves(mon, gKyuremWhiteSwapMoveTable, FUSE_MON); #endif //P_FAMILY_RESHIRAM #if P_FAMILY_ZEKROM if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_BLACK) - SwapFusionMonMoves(mon, gKyurenBlackSwapMoveTable, FUSE_MON); + SwapFusionMonMoves(mon, gKyuremBlackSwapMoveTable, FUSE_MON); #endif //P_FAMILY_ZEKROM #endif //P_FAMILY_KYUREM if (gTasks[taskId].moveToLearn != 0) @@ -6446,11 +6446,11 @@ static void Task_TryItemUseFusionChange(u8 taskId) #if P_FAMILY_KYUREM #if P_FAMILY_RESHIRAM if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE) - SwapFusionMonMoves(mon, gKyurenWhiteSwapMoveTable, UNFUSE_MON); + SwapFusionMonMoves(mon, gKyuremWhiteSwapMoveTable, UNFUSE_MON); #endif //P_FAMILY_RESHIRAM #if P_FAMILY_ZEKROM if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_BLACK) - SwapFusionMonMoves(mon, gKyurenBlackSwapMoveTable, UNFUSE_MON); + SwapFusionMonMoves(mon, gKyuremBlackSwapMoveTable, UNFUSE_MON); #endif //P_FAMILY_ZEKROM #endif //P_FAMILY_KYUREM if ( gTasks[taskId].tExtraMoveHandling == FORGET_EXTRA_MOVES) From cfcce251d0b557add8fc1fab76d9386b54b82644 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Wed, 5 Nov 2025 13:49:12 +0000 Subject: [PATCH 110/130] Fix typo in Electrode-Hisui pokedex entry (#8143) --- src/data/pokemon/species_info/gen_1_families.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/pokemon/species_info/gen_1_families.h b/src/data/pokemon/species_info/gen_1_families.h index c6f68920e0..daae4cc252 100644 --- a/src/data/pokemon/species_info/gen_1_families.h +++ b/src/data/pokemon/species_info/gen_1_families.h @@ -12841,7 +12841,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .height = 5, .weight = 130, .description = COMPOUND_STRING( - "It esembles an ancient Poké Ball design.\n" + "It resembles an ancient Poké Ball design.\n" "When excited, it discharges the electric\n" "current it has stored in its belly, then\n" "lets out a great, uproarious laugh."), From d1428d7966a1695d007c122976aa0096b464cef5 Mon Sep 17 00:00:00 2001 From: Raymond Dodge Date: Wed, 5 Nov 2025 11:05:01 -0500 Subject: [PATCH 111/130] Update mdbook to v0.5.0-beta.1 (#8133) --- .github/workflows/docs.yml | 2 +- docs/fix_links.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b2792e87de..e02d931e28 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -17,7 +17,7 @@ jobs: fetch-depth: 0 - name: Install latest mdbook run: | - tag="v0.4.35" + tag="v0.5.0-beta.1" url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz" mkdir mdbook curl -sSL $url | tar -xz --directory=./mdbook diff --git a/docs/fix_links.py b/docs/fix_links.py index 6e2eaec485..3a4184b72c 100644 --- a/docs/fix_links.py +++ b/docs/fix_links.py @@ -46,6 +46,6 @@ if __name__ == '__main__': sys.exit(0) context, book = json.load(sys.stdin) - proc_items(book['sections']) + proc_items(book['items']) print(json.dumps(book)) From c8159ba18210d68e92483cd5202f88f491abc5f0 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Wed, 5 Nov 2025 21:45:39 +0100 Subject: [PATCH 112/130] Reset saveblock data between test runs (#8145) Co-authored-by: Hedara --- src/battle_main.c | 5 ++++- test/test_runner.c | 15 ++++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/battle_main.c b/src/battle_main.c index ad5ccac4ba..cd76bc827b 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3002,7 +3002,10 @@ static void ClearSetBScriptingStruct(void) memset(&gBattleScripting, 0, sizeof(gBattleScripting)); gBattleScripting.windowsType = temp; - gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle; + if (TESTING) + gBattleScripting.battleStyle = OPTIONS_BATTLE_STYLE_SET; + else + gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle; gBattleScripting.expOnCatch = (B_EXP_CATCH >= GEN_6); gBattleScripting.specialTrainerBattleType = specialBattleType; } diff --git a/test/test_runner.c b/test/test_runner.c index 450283ab58..ac4b20cd2c 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -1,4 +1,5 @@ #include +#include "fake_rtc.h" #include "global.h" #include "gpu_regs.h" #include "load_save.h" @@ -184,6 +185,13 @@ void TestRunner_CheckMemory(void) } } +static void ClearSaveBlocks(void) +{ + ClearSav1(); + ClearSav2(); + ClearSav3(); +} + void CB2_TestRunner(void) { top: @@ -201,17 +209,13 @@ top: gTestRunnerState.filterMode = DetectFilterMode(gTestRunnerArgv); MoveSaveBlocks_ResetHeap(); - ClearSav1(); - ClearSav2(); - ClearSav3(); gIntrTable[7] = Intr_Timer2; - gSaveBlock2Ptr->optionsBattleStyle = OPTIONS_BATTLE_STYLE_SET; - // The current test restarted the ROM (e.g. by jumping to NULL). if (gPersistentTestRunnerState.address != 0) { + ClearSaveBlocks(); gTestRunnerState.test = __start_tests; while ((uintptr_t)gTestRunnerState.test != gPersistentTestRunnerState.address) { @@ -255,6 +259,7 @@ top: break; case STATE_ASSIGN_TEST: + ClearSaveBlocks(); while (1) { if (gTestRunnerState.test == __stop_tests) From 1f8a40962ad9af13e4034139e4550e35affb1830 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Thu, 6 Nov 2025 01:21:13 +0100 Subject: [PATCH 113/130] Allow to send active mon to PC when capturing a Pokemon (#8111) --- src/party_menu.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/party_menu.c b/src/party_menu.c index 04b2233102..3964716afa 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -1512,15 +1512,7 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr) case PARTY_ACTION_SEND_MON_TO_BOX: { u8 partyId = (u8)*slotPtr; - if (partyId == 0 || ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && partyId == 1)) - { - // Can't select if mon is currently on the field - PlaySE(SE_FAILURE); - DisplayPartyMenuMessage(gText_CannotSendMonToBoxActive, FALSE); - ScheduleBgCopyTilemapToVram(2); - gTasks[taskId].func = Task_ReturnToChooseMonAfterText; - } - else if ((gBattleTypeFlags & BATTLE_TYPE_MULTI) && partyId >= (PARTY_SIZE / 2)) + if ((gBattleTypeFlags & BATTLE_TYPE_MULTI) && partyId >= (PARTY_SIZE / 2)) { // Can't select if mon doesn't belong to you PlaySE(SE_FAILURE); From be7646b67a0d88f2bfe21c390b6022ae5f9e66c5 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Thu, 6 Nov 2025 19:16:19 +0100 Subject: [PATCH 114/130] Allow vs seekers to work with script not starting with trainerbattle (#8062) Co-authored-by: pkmnsnfrn --- asm/macros/event.inc | 6 ++ docs/SUMMARY.md | 1 + docs/tutorials/vs_seeker.md | 115 ++++++++++++++++++++++++++++++++++++ include/battle_setup.h | 2 + include/config/item.h | 2 +- include/config/overworld.h | 3 + include/vs_seeker.h | 1 + src/battle_main.c | 1 + src/battle_setup.c | 37 +++++++++--- src/vs_seeker.c | 46 +++++++++++++-- 10 files changed, 202 insertions(+), 12 deletions(-) create mode 100644 docs/tutorials/vs_seeker.md diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 56b5d1d9a0..bc55661bd6 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2670,3 +2670,9 @@ callnative ScriptChangeFollowerNPCBattlePartner .2byte \battlePartner .endm + +@ VS Seeker + .macro vsseeker_rematchid rematchId:req + callnative NativeVsSeekerRematchId, requests_effects=1 + .2byte \rematchId + .endm diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 8d1e4e9cdb..a38a539bef 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -37,6 +37,7 @@ - [How to use Follower NPCs](tutorials/how_to_follower_npc.md) - [Time-Based Encounters](tutorials/how_to_time_of_day_encounters.md) - [How to use Trainer Party Pools](tutorials/how_to_trainer_party_pool.md) + - [Vs. Seeker](tutorials/vs_seeker.md) - [Changelog](./CHANGELOG.md) - [1.13.x]() - [Version 1.13.3](changelogs/1.13.x/1.13.3.md) diff --git a/docs/tutorials/vs_seeker.md b/docs/tutorials/vs_seeker.md new file mode 100644 index 0000000000..136465a5b4 --- /dev/null +++ b/docs/tutorials/vs_seeker.md @@ -0,0 +1,115 @@ +# `pokemerald-expansion` Vs. Seeker + +## What is the Vs. Seeker? +The Vs. Seeker is a Key Item that is used to battle Trainers that the player has battled previously. + +When used, the Vs. Seeker sends out a signal that allows the player to find other Trainers who want a rematch. This signal affects all Trainers that are on-screen. Once used on Trainers that can be rematched, the device cannot be used again until it is charged. The player does this by walking a specific number of steps. The effect on the Trainers wears off if they are battled, the player leaves the area, or the player walks a specific number of steps. If the player attempts to use the Vs. Seeker when it is not fully charged, the player will be told how many steps remain until it is. After the player uses the Vs. Seeker, some Trainers may have their team changed from their first battle. + +## How is the Vs. Seeker enabled? +### Users +Vs. Seeker functionality is enabled by setting `I_VS_SEEKER_CHARGING` to `TRUE`. + +### Players +`ITEM_VS_SEEKER` can only be used outside of battle. It can be used from the bag or registered to be used from the field. + +Usage of the Vs. Seeker will ALWAYS fail unless all of the conditions are met: +* Player has at least five badges +* There is an NPC on screen that has previously been defeated +* Player is not inside of a building +* The Vs. Seeker is fully recharged + +#### Charge +If the player has `ITEM_VS_SEEKER` and at least five badges, the Vs. Seeker will be charged by walking steps. The Vs. Seeker is fully charged once the player has walked `VSSEEKER_RECHARGE_STEPS`, which is `100` by default. The Vs. Seeker's charge is depleted if the player uses the item. + +### How does Match Call interact with the Vs. Seeker? +When `I_VS_SEEKER_CHARGING` is enabled, the Match Call does not function at all. Trainers will never be rematch eligible outside of the use of the Vs. Seeker. + +## How does the Vs. Seeker choose a Trainer? + +When the Vs. Seeker is successfully used, every Trainer on screen is individually queried. There is a 31% chance that the Trainer will want a rematch. +Objects listed in `regularTrainersOnLand` or `regularTrainersInWater` are considered Land/Water objects. + +| Status | Is Land/Water Object | Emote | New Movement Type | +| --- | --- | --- | --- | +| Wants Rematch | Yes | `MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK` | `MOVEMENT_TYPE_COUNTER_CLOCKWISE` | +| Wants Rematch | No | `MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK` | `MOVEMENT_TYPE_FACE_DOWN` | +| Does Not Want Rematch | - | `MOVEMENT_ACTION_EMOTE_X` | none | +| Has Not Been Fought | - | `MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK` | none | + +### Rematch Table + +| Sequence | Trainer ID | +| ---------- | ---------------- | +| 1st Battle | `TRAINER_ROSE_1` | +| 2nd Battle | `TRAINER_ROSE_2` | +| 3rd Battle | `TRAINER_ROSE_3` | +| 4th Battle | `TRAINER_ROSE_4` | +| 5th Battle | `TRAINER_ROSE_5` | + +The game determines which version of the Trainer you'll fight next by following these rules: + +1. Start with the next Trainer in the sequence after the one that has been defeated. If there are no more, the battle is against the last listed Trainer. +2. If that next Trainer hasn't been unlocked yet, the battle is against the latest available unlocked version. +3. If the next Trainer is unlocked but not yet defeated, the battle is against that version. +4. If the next Trainer has already been defeated, check the next one in the sequence. + +## How do users implement rematches with the Vs. Seeker? +### Existing `pokemerald` Trainers +No extra work is required. With the exception of Wally, Gym Leaders and Elite Four, all of the rematchable Trainers in Emerald will work with the Vs. Seeker without any changes. +### New Trainers +#### Party / `gRematchTable` +Each of the rematches for the Trainer must be defined as seperate Trainers in `src/data/trainers.party` and `include/constants/opponents`. For example, `TRAINER_CALVIN_1` also has `TRAINER_CALVIN_2`,`TRAINER_CALVIN_3`,`TRAINER_CALVIN_4`, and `TRAINER_CALVIN_5`. + +Once all of those constants and parties are defined, a new row must be added to `gRematchTable` (located in in `src/battle_setup.c`). The row header should be a rematch ID, which can be added in `include/constants/rematches.h`. The row contents must be the five constants created for the new parties, with the lat argument being the constant of the map (`include/constants/map_groups.h`) where the Trainer is placed. + +If a Trainer is intended to have less than five unique rematch parties, the extra slots can be filled with the last available Trainer ID. + +```c +// This Trainer only has two teams. + [REMATCH_ROSE] = REMATCH(TRAINER_ROSE_1, TRAINER_ROSE_2, TRAINER_ROSE_2, TRAINER_ROSE_2, TRAINER_ROSE_2, MAP_ROUTE118), +``` + +WARNING: Rematch IDs should be placed BEFORE `REMATCH_WALLY_VR`. Trainers below that are treated as "special Trainers" that are not triggered by the Vs. Seeker. + +#### Scripts +The trainer's object needs to have a script that begins with a method to signify what this object's trainer ID is. + +#### `trainerbattle` +``` +Route103_EventScript_Daisy:: + trainerbattle_single TRAINER_DAISY, Route103_Text_DaisyIntro, Route103_Text_DaisyDefeated + msgbox Route103_Text_DaisyPostBattle, MSGBOX_AUTOCLOSE + end +``` + +Daisy is using one of the `trainerbattle` macros, which has the trainer battle macro in the first command of the script. Most trainers in `pokeemerald` use this pattern. + +##### `vsseeker_rematchid` +``` +Route102_EventScript_Calvin:: + vsseeker_rematchid TRAINER_CALVIN_1 + applymovement LOCALID_CALVIN, CalvinMovementTest + waitmovement 0 + trainerbattle_single TRAINER_CALVIN_1, Route102_Text_CalvinIntro, Route102_Text_CalvinDefeated, Route102_EventScript_CalvinRegisterMatchCallAfterBattle + specialvar VAR_RESULT, ShouldTryRematchBattle + goto_if_eq VAR_RESULT, TRUE, Route102_EventScript_CalvinRematch + setvar VAR_0x8004, TRAINER_CALVIN_1 + specialvar VAR_RESULT, IsTrainerRegistered + goto_if_eq VAR_RESULT, FALSE, Route102_EventScript_CalvinTryRegister + msgbox Route102_Text_CalvinPostBattle, MSGBOX_DEFAULT + release + end +``` + +If the trainer has other script commands before the eventual `trainerbattle` macro, the first command in the script needs to be `vsseeker_rematchid`. This macro does nothing but takes a single argument, which should be the same as the first Trainer ID for this trainer. + +#### `MOVEMENT_TYPE_COUNTER_CLOCKWISE` +If you want Trainers to spin once they are eligible for a rematch, their overworld graphics object ID (`include/constants/event_objects.h`) must be listed in either `regularTrainersOnLand` or `regularTrainersInWater`.Otherwise they will adopt the movement type `MOVEMENT_TYPE_FACE_DOWN`. + +## What can be customized about the Vs. Seeker? +* **Unlock Conditions**: The next "level" of rematches is unlocked when a specific flag is set. The flags that are currently used in `GetGameProgressFlags` can be changed to flags that better suit your game. +* **Recharge Steps**: `VSSEEKER_RECHARGE_STEPS` is initally set to 100, but this value can be changed to any number under 256. +* **Badge Requirement**: `HasAtLeastFiveBadges` is used to check if the Vs. Seeker will successfully work. You can customize the number of badges by changing `REMATCH_BADGE_COUNT` or otherwise alterting the function. + +## What are the limitations of the Vs. Seeker? +The Vs. Seeker does not currently work with Gym Leaders. There is a bug filed to hopefully fix this in the future. diff --git a/include/battle_setup.h b/include/battle_setup.h index aa01178d8d..1605345260 100644 --- a/include/battle_setup.h +++ b/include/battle_setup.h @@ -90,12 +90,14 @@ const u8 *GetTrainerALoseText(void); const u8 *GetTrainerBLoseText(void); const u8 *GetTrainerWonSpeech(void); void UpdateRematchIfDefeated(s32 rematchTableId); +void ClearCurrentTrainerWantRematchVsSeeker(void); void IncrementRematchStepCounter(void); void TryUpdateRandomTrainerRematches(u16 mapGroup, u16 mapNum); bool32 DoesSomeoneWantRematchIn(u16 mapGroup, u16 mapNum); bool32 IsRematchTrainerIn(u16 mapGroup, u16 mapNum); u16 GetLastBeatenRematchTrainerId(u16 trainerId); bool8 ShouldTryRematchBattle(void); +bool8 ShouldTryRematchBattleForTrainerId(u16 trainerId); bool8 IsTrainerReadyForRematch(void); void ShouldTryGetTrainerScript(void); u16 CountBattledRematchTeams(u16 trainerId); diff --git a/include/config/item.h b/include/config/item.h index 96156b0db8..4222754ead 100644 --- a/include/config/item.h +++ b/include/config/item.h @@ -36,7 +36,7 @@ #define I_REPEL_LURE_MENU TRUE // If TRUE, the player is able to choose which Repel/Lure to use once the previous one runs out. Cursor position is saved by VAR_LAST_REPEL_LURE_USED if not 0. // Vs. Seeker -#define I_VS_SEEKER_CHARGING 0 // If this flag is assigned, the Vs Seeker functionality will be enabled. When the player has the Vs. Seeker, Match Call rematch functions will stop working. +#define I_VS_SEEKER_CHARGING 0 // If this flag is assigned, the Vs Seeker functionality will be enabled. When the player has the Vs. Seeker, Match Call rematch functions will stop working. Documentation for the Vs. Seeker can be found in docs/tutorials/vs_seeker.md. // Fishing #define I_FISHING_BITE_ODDS GEN_LATEST // In Gen 1 and Gen 2, the Old Rod has a 100% chance for a bite, Good Rod has a 66% chance for a bite, and Super Rod has a 50% chance for a bite. In Gen 3, all rods have a base 50% chance for a bite. In Gen 4 onwards, the Old Rod has a base 25% chance for a bite, Good Rod has a 50% chance for a bite, and Super Rod has a 75% chance for a bite. diff --git a/include/config/overworld.h b/include/config/overworld.h index 480c5dbcea..cb110b4226 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -142,4 +142,7 @@ // Berry Blender #define BERRY_BLENDER_THROW_ALL_BERRIES_AT_ONCE TRUE // This is a small little addition, that basically speeds up the animation where all players' berries are thrown into the blender. Self-explanatory I hope! +// Trainer Rematches +#define OW_REMATCH_BADGE_COUNT 5 // Number of badges necessary before the match call or vs seeker features allow rematches + #endif // GUARD_CONFIG_OVERWORLD_H diff --git a/include/vs_seeker.h b/include/vs_seeker.h index d6795432b0..749aaeab57 100644 --- a/include/vs_seeker.h +++ b/include/vs_seeker.h @@ -9,6 +9,7 @@ void MapResetTrainerRematches(u16 mapGroup, u16 mapNum); void ClearRematchMovementByTrainerId(void); u16 GetRematchTrainerIdVSSeeker(u16 trainerId); bool32 IsVsSeekerEnabled(void); +void NativeVsSeekerRematchId(struct ScriptContext *ctx); #define VSSEEKER_RECHARGE_STEPS 100 diff --git a/src/battle_main.c b/src/battle_main.c index cd76bc827b..15dfe0a3e4 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5668,6 +5668,7 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void) else gSaveBlock3Ptr->dexNavChain = 0; + ClearCurrentTrainerWantRematchVsSeeker(); gDexNavSpecies = SPECIES_NONE; ResetSpriteData(); if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK diff --git a/src/battle_setup.c b/src/battle_setup.c index 6a6f529ea6..a6d833c417 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -1741,6 +1741,20 @@ static void ClearTrainerWantRematchState(const struct RematchTrainer *table, u16 #endif //FREE_MATCH_CALL } +void ClearCurrentTrainerWantRematchVsSeeker(void) +{ +#if FREE_MATCH_CALL == FALSE + if ((gBattleTypeFlags & BATTLE_TYPE_TRAINER) && FlagGet(I_VS_SEEKER_CHARGING) && (I_VS_SEEKER_CHARGING != 0)) + { + for (u32 i = 0; i < REMATCH_TABLE_ENTRIES; i++) + { + if (gSaveBlock1Ptr->trainerRematches[i] == TRAINER_BATTLE_PARAM.opponentA) + gSaveBlock1Ptr->trainerRematches[i] = 0; + } + } +#endif //FREE_MATCH_CALL +} + static u32 GetTrainerMatchCallFlag(u32 trainerId) { s32 i; @@ -1772,12 +1786,16 @@ static bool8 WasSecondRematchWon(const struct RematchTrainer *table, u16 firstBa return FALSE; if (!HasTrainerBeenFought(table[tableId].trainerIds[1])) return FALSE; - + if (I_VS_SEEKER_CHARGING) + { + if (gSaveBlock1Ptr->trainerRematches[tableId] == 0) + return FALSE; + } return TRUE; } #if FREE_MATCH_CALL == FALSE -static bool32 HasAtLeastFiveBadges(void) +static bool32 HasEnoughBadgesForRematch(void) { s32 i, count; @@ -1785,7 +1803,7 @@ static bool32 HasAtLeastFiveBadges(void) { if (FlagGet(gBadgeFlags[i]) == TRUE) { - if (++count >= 5) + if (++count >= OW_REMATCH_BADGE_COUNT) return TRUE; } } @@ -1799,7 +1817,7 @@ static bool32 HasAtLeastFiveBadges(void) void IncrementRematchStepCounter(void) { #if FREE_MATCH_CALL == FALSE - if (!HasAtLeastFiveBadges()) + if (!HasEnoughBadgesForRematch()) return; if (IsVsSeekerEnabled()) @@ -1815,7 +1833,7 @@ void IncrementRematchStepCounter(void) #if FREE_MATCH_CALL == FALSE static bool32 IsRematchStepCounterMaxed(void) { - if (HasAtLeastFiveBadges() && gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX) + if (HasEnoughBadgesForRematch() && gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX) return TRUE; else return FALSE; @@ -1855,10 +1873,15 @@ u16 GetLastBeatenRematchTrainerId(u16 trainerId) bool8 ShouldTryRematchBattle(void) { - if (IsFirstTrainerIdReadyForRematch(gRematchTable, TRAINER_BATTLE_PARAM.opponentA)) + return ShouldTryRematchBattleForTrainerId(TRAINER_BATTLE_PARAM.opponentA); +} + +bool8 ShouldTryRematchBattleForTrainerId(u16 trainerId) +{ + if (IsFirstTrainerIdReadyForRematch(gRematchTable, trainerId)) return TRUE; - return WasSecondRematchWon(gRematchTable, TRAINER_BATTLE_PARAM.opponentA); + return WasSecondRematchWon(gRematchTable, trainerId); } bool8 IsTrainerReadyForRematch(void) diff --git a/src/vs_seeker.c b/src/vs_seeker.c index 1a7dc68c72..8669ca3f47 100644 --- a/src/vs_seeker.c +++ b/src/vs_seeker.c @@ -31,6 +31,8 @@ #include "constants/trainer_types.h" #include "constants/field_effects.h" +// Documentation for the Vs. Seeker can be found in docs/tutorials/vs_seeker.md. + enum { VSSEEKER_NOT_CHARGED, @@ -230,6 +232,9 @@ bool8 UpdateVsSeekerStepCounter(void) if (!I_VS_SEEKER_CHARGING) return FALSE; + // This condition helps in case your save file is switching between vs seeker and matchcall + if (gSaveBlock1Ptr->trainerRematchStepCounter > VSSEEKER_RECHARGE_STEPS && gSaveBlock1Ptr->trainerRematchStepCounter <= 0xFF) + gSaveBlock1Ptr->trainerRematchStepCounter = 0; if (CheckBagHasItem(ITEM_VS_SEEKER, 1)) { if ((gSaveBlock1Ptr->trainerRematchStepCounter & 0xFF) < VSSEEKER_RECHARGE_STEPS) @@ -376,8 +381,11 @@ static void GatherNearbyTrainerInfo(void) if (templates[objectEventIdx].trainerType != TRAINER_TYPE_NORMAL && templates[objectEventIdx].trainerType != TRAINER_TYPE_BURIED) continue; + u16 trainerIdx = GetTrainerFlagFromScript(templates[objectEventIdx].script); + if (trainerIdx == TRAINER_NONE) + continue; sVsSeeker->trainerInfo[vsSeekerObjectIdx].script = templates[objectEventIdx].script; - sVsSeeker->trainerInfo[vsSeekerObjectIdx].trainerIdx = GetTrainerFlagFromScript(templates[objectEventIdx].script); + sVsSeeker->trainerInfo[vsSeekerObjectIdx].trainerIdx = trainerIdx; sVsSeeker->trainerInfo[vsSeekerObjectIdx].localId = templates[objectEventIdx].localId; TryGetObjectEventIdByLocalIdAndMap(templates[objectEventIdx].localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &objectEventId); sVsSeeker->trainerInfo[vsSeekerObjectIdx].objectEventId = objectEventId; @@ -707,9 +715,31 @@ static u16 GetTrainerFlagFromScript(const u8 *script) { // The trainer flag is located 3 bytes (command + flags + localIdA) from the script pointer, assuming the trainerbattle command is first in the script. // Because scripts are unaligned, and because the ARM processor requires shorts to be 16-bit aligned, this function needs to perform explicit bitwise operations to get the correct flag. - script += 3; - u16 trainerFlag = script[0]; - trainerFlag |= script[1] << 8; + u16 trainerFlag; + switch (script[0]) + { + case 0x5c: + script += 3; + trainerFlag = script[0]; + trainerFlag |= script[1] << 8; + break; + case 0x23: + u32 callnativeFunc = (((((script[4] << 8) + script[3]) << 8) + script[2]) << 8) + script[1]; + if (callnativeFunc == ((u32)NativeVsSeekerRematchId | 0xA000000)) // | 0xA000000 corresponds to the request_effects=1 version of the function + { + script += 5; + trainerFlag = script[0]; + trainerFlag |= script[1] << 8; + } + else + { + trainerFlag = TRAINER_NONE; + } + break; + default: + trainerFlag = TRAINER_NONE; + break; + } return trainerFlag; } @@ -788,6 +818,14 @@ static u8 GetCurVsSeekerResponse(s32 vsSeekerIdx, u16 trainerIdx) } #endif //FREE_MATCH_CALL +void NativeVsSeekerRematchId(struct ScriptContext *ctx) +{ + u16 trainerId = ScriptReadHalfword(ctx); + if (ctx->breakOnTrainerBattle && HasTrainerBeenFought(trainerId) && !ShouldTryRematchBattleForTrainerId(trainerId)) + StopScript(ctx); +} + + static void StartAllRespondantIdleMovements(void) { #if FREE_MATCH_CALL == FALSE From 97b115d5722f77ef791784d63b34e29b65011bcb Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Fri, 7 Nov 2025 06:10:42 +0100 Subject: [PATCH 115/130] Adds an auto-generated include file of script commands (#8156) --- Makefile | 8 ++++++++ include/battle_setup.h | 1 + src/battle_setup.c | 7 +++++++ src/trainer_see.c | 16 ++++++++++++++-- src/vs_seeker.c | 5 +++-- tools/misc/make_scr_cmd_constants.py | 25 +++++++++++++++++++++++++ 6 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 tools/misc/make_scr_cmd_constants.py diff --git a/Makefile b/Makefile index 8e5259ad1e..fa2420e105 100644 --- a/Makefile +++ b/Makefile @@ -195,10 +195,18 @@ ALL_LEARNABLES_JSON := $(LEARNSET_HELPERS_BUILD_DIR)/all_learnables.json WILD_ENCOUNTERS_TOOL_DIR := $(TOOLS_DIR)/wild_encounters AUTO_GEN_TARGETS += $(DATA_SRC_SUBDIR)/wild_encounters.h +MISC_TOOL_DIR := $(TOOLS_DIR)/misc +AUTO_GEN_TARGETS += $(INCLUDE_DIRS)/constants/script_commands.h + $(DATA_SRC_SUBDIR)/wild_encounters.h: $(DATA_SRC_SUBDIR)/wild_encounters.json $(WILD_ENCOUNTERS_TOOL_DIR)/wild_encounters_to_header.py $(INCLUDE_DIRS)/config/overworld.h $(INCLUDE_DIRS)/config/dexnav.h python3 $(WILD_ENCOUNTERS_TOOL_DIR)/wild_encounters_to_header.py > $@ +$(INCLUDE_DIRS)/constants/script_commands.h: $(MISC_TOOL_DIR)/make_scr_cmd_constants.py $(DATA_ASM_SUBDIR)/script_cmd_table.inc + python3 $(MISC_TOOL_DIR)/make_scr_cmd_constants.py + $(C_BUILDDIR)/wild_encounter.o: c_dep += $(DATA_SRC_SUBDIR)/wild_encounters.h +$(C_BUILDDIR)/trainer_see.o: c_dep += $(INCLUDE_DIRS)/constants/script_commands.h +$(C_BUILDDIR)/vs_seeker.o: c_dep += $(INCLUDE_DIRS)/constants/script_commands.h PERL := perl SHA1 := $(shell { command -v sha1sum || command -v shasum; } 2>/dev/null) -c diff --git a/include/battle_setup.h b/include/battle_setup.h index 1605345260..1323971f67 100644 --- a/include/battle_setup.h +++ b/include/battle_setup.h @@ -73,6 +73,7 @@ void ConfigureAndSetUpOneTrainerBattle(u8 trainerObjEventId, const u8 *trainerSc void ConfigureTwoTrainersBattle(u8 trainerObjEventId, const u8 *trainerScript); void SetUpTwoTrainersBattle(void); bool32 GetTrainerFlagFromScriptPointer(const u8 *data); +bool32 GetRematchFromScriptPointer(const u8 *data); void SetTrainerFacingDirection(void); u8 GetTrainerBattleMode(void); bool8 GetTrainerFlag(void); diff --git a/src/battle_setup.c b/src/battle_setup.c index a6d833c417..1103818c05 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -1125,6 +1125,13 @@ bool32 GetTrainerFlagFromScriptPointer(const u8 *data) TrainerBattleParameter *temp = (TrainerBattleParameter*)(data + OPCODE_OFFSET); return FlagGet(TRAINER_FLAGS_START + temp->params.opponentA); } + +bool32 GetRematchFromScriptPointer(const u8 *data) +{ + TrainerBattleParameter *temp = (TrainerBattleParameter*)(data + OPCODE_OFFSET); + return ShouldTryRematchBattleForTrainerId(temp->params.opponentA); +} + #undef OPCODE_OFFSET // Set trainer's movement type so they stop and remain facing that direction diff --git a/src/trainer_see.c b/src/trainer_see.c index 54ea4ebf22..2ec23dfa4f 100644 --- a/src/trainer_see.c +++ b/src/trainer_see.c @@ -19,6 +19,7 @@ #include "constants/event_objects.h" #include "constants/event_object_movement.h" #include "constants/field_effects.h" +#include "constants/script_commands.h" #include "constants/trainer_types.h" // this file's functions @@ -447,7 +448,7 @@ static u8 CheckTrainer(u8 objectEventId) struct ScriptContext ctx; if (RunScriptImmediatelyUntilEffect(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE | SCREFF_TRAINERBATTLE, trainerBattlePtr, &ctx)) { - if (*ctx.scriptPtr == 0x5c) // trainerbattle + if (*ctx.scriptPtr == SCR_OP_TRAINERBATTLE) trainerBattlePtr = ctx.scriptPtr; else trainerBattlePtr = NULL; @@ -471,7 +472,18 @@ static u8 CheckTrainer(u8 objectEventId) else if (trainerBattlePtr) { if (GetTrainerFlagFromScriptPointer(trainerBattlePtr)) - return 0; + { + //If there is a rematch, we want to trigger the approach sequence + if (GetRematchFromScriptPointer(trainerBattlePtr)) + { + trainerBattlePtr = NULL; + numTrainers = 0xFF; + } + else + { + return 0; + } + } } else { diff --git a/src/vs_seeker.c b/src/vs_seeker.c index 8669ca3f47..1fcf5fdfcc 100644 --- a/src/vs_seeker.c +++ b/src/vs_seeker.c @@ -28,6 +28,7 @@ #include "constants/items.h" #include "constants/maps.h" #include "constants/songs.h" +#include "constants/script_commands.h" #include "constants/trainer_types.h" #include "constants/field_effects.h" @@ -718,12 +719,12 @@ static u16 GetTrainerFlagFromScript(const u8 *script) u16 trainerFlag; switch (script[0]) { - case 0x5c: + case SCR_OP_TRAINERBATTLE: script += 3; trainerFlag = script[0]; trainerFlag |= script[1] << 8; break; - case 0x23: + case SCR_OP_CALLNATIVE: u32 callnativeFunc = (((((script[4] << 8) + script[3]) << 8) + script[2]) << 8) + script[1]; if (callnativeFunc == ((u32)NativeVsSeekerRematchId | 0xA000000)) // | 0xA000000 corresponds to the request_effects=1 version of the function { diff --git a/tools/misc/make_scr_cmd_constants.py b/tools/misc/make_scr_cmd_constants.py new file mode 100644 index 0000000000..d158dfcf8a --- /dev/null +++ b/tools/misc/make_scr_cmd_constants.py @@ -0,0 +1,25 @@ +import re + +SCR_CMD_PAT = re.compile(r"\tscript_cmd_table_entry (\w+)\s+\w+,\s+[\w=]+\s+@( 0x[0-9a-f]+)") + +def main(): + output = [ + "//", + "// DO NOT MODIFY THIS FILE! It is auto-generated by tools/misc/make_scr_cmd_constants.py", + "//", + "#ifndef GUARD_SCR_CMD_CONSTANTS_H", + "#define GUARD_SCR_CMD_CONSTANTS_H\n", + ] + + with open("data/script_cmd_table.inc", "r") as f: + for line in f.readlines(): + if match := re.match(SCR_CMD_PAT, line): + new_line = "#define " + match.group(1) + match.group(2) + output.append(new_line) + + output.append("\n#endif // GUARD_SCR_CMD_CONSTANTS_H\n") + with open("include/constants/script_commands.h", "w+") as f: + f.write('\n'.join(output)) + +if __name__ == "__main__": + main() \ No newline at end of file From ff1039f148e2ab0a7da6757a4f5d9c3377c0fd64 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Fri, 7 Nov 2025 06:12:05 +0100 Subject: [PATCH 116/130] Fix grade in summary screen not accounting for 26 IV (#8157) --- src/pokemon_summary_screen.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index a0722fc065..9016066db7 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -3800,18 +3800,18 @@ static const u8 *GetLetterGrade(u32 stat) static const u8 gText_GradeA[] = _("A"); static const u8 gText_GradeS[] = _("S"); - if (stat > 0 && stat <= 15) - return gText_GradeD; - else if (stat > 15 && stat <= 25) - return gText_GradeC; - else if (stat > 26 && stat <= 29) - return gText_GradeB; - else if (stat == 30) - return gText_GradeA; - else if (stat == 31) - return gText_GradeS; - else + if (stat <= 0) return gText_GradeF; + else if (stat <= 15) + return gText_GradeD; + else if (stat <= 25) + return gText_GradeC; + else if (stat <= 29) + return gText_GradeB; + else if (stat <= 30) + return gText_GradeA; + else + return gText_GradeS; } static void BufferLeftColumnStats(void) From cab4ec29da4685703feae0e73e63c3635663deb1 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Fri, 7 Nov 2025 10:05:50 +0100 Subject: [PATCH 117/130] Add additional comment explaing map name popup transparency side-effects (#8117) --- include/config/overworld.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/config/overworld.h b/include/config/overworld.h index cb110b4226..117d3b9eb5 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -133,6 +133,7 @@ #define OW_POPUP_BW_TIME_MODE OW_POPUP_BW_TIME_NONE // Determines what type of time is shown. #define OW_POPUP_BW_ALPHA_BLEND FALSE // Enables alpha blending/transparency for the pop-ups. Mainly intended to be used with the black color option. // Setting this to TRUE will cause graphical errors with the Day Night System enabled. + // It will also cause minor visual glitches of shadow and reflection sprites adjusting their transparency when the pop-up disappear // Pokémon Center #define OW_IGNORE_EGGS_ON_HEAL GEN_LATEST // In Gen 4+, the nurse in the Pokémon Center does not heal Eggs on healing machine. From 8d692bc96e1e9680a794b400d7c30744971d91b8 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Fri, 7 Nov 2025 11:46:56 +0100 Subject: [PATCH 118/130] Fix wild_encounters script not closing arrays properly (#8123) --- .../wild_encounters_to_header.py | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/tools/wild_encounters/wild_encounters_to_header.py b/tools/wild_encounters/wild_encounters_to_header.py index 5de52fbea9..375dda3ecf 100644 --- a/tools/wild_encounters/wild_encounters_to_header.py +++ b/tools/wild_encounters/wild_encounters_to_header.py @@ -391,35 +391,33 @@ def PrintWildMonHeadersContent(): timeCounter += 1 PrintEncounterHeaders(TabStr(2) + "},") PrintEncounterHeaders(tabStr + "},") - - if labelCount + 1 == headerStructTable[group][label]["encounterTotalCount"]: - PrintEncounterHeaders(tabStr + "{") - PrintEncounterHeaders(f"{TabStr(2)}.mapGroup = {GetMapGroupEnum(MAP_UNDEFINED)},") - PrintEncounterHeaders(f"{TabStr(2)}.mapNum = {GetMapGroupEnum(MAP_UNDEFINED, labelCount + 1)},") - - nullCount = 0 - while nullCount < TIMES_OF_DAY_COUNT: - if nullCount == 0: - PrintEncounterHeaders(f"{TabStr(2)}.encounterTypes =") - PrintEncounterHeaders(TabStr(2)+ "{") - - PrintEncounterHeaders(f"{TabStr(3)}[{TIME_OF_DAY.vals[nullCount]}] = ") - - nullIndex = 0 - while nullIndex <= len(fieldData) - 1: - if nullIndex == 0: - PrintEncounterHeaders(TabStr(3) + "{") - - PrintEncounterHeaders(f"{TabStr(4)}{GetIMonInfoStringFromIndex(nullIndex)} = NULL,") - - if nullIndex == len(fieldData) - 1: - PrintEncounterHeaders(TabStr(3) + "},") - - nullIndex += 1 - nullCount += 1 - PrintEncounterHeaders(TabStr(2) + "},") - PrintEncounterHeaders(tabStr + "},") labelCount += 1 + PrintEncounterHeaders(tabStr + "{") + PrintEncounterHeaders(f"{TabStr(2)}.mapGroup = {GetMapGroupEnum(MAP_UNDEFINED)},") + PrintEncounterHeaders(f"{TabStr(2)}.mapNum = {GetMapGroupEnum(MAP_UNDEFINED, labelCount + 1)},") + + nullCount = 0 + while nullCount < TIMES_OF_DAY_COUNT: + if nullCount == 0: + PrintEncounterHeaders(f"{TabStr(2)}.encounterTypes =") + PrintEncounterHeaders(TabStr(2)+ "{") + + PrintEncounterHeaders(f"{TabStr(3)}[{TIME_OF_DAY.vals[nullCount]}] = ") + + nullIndex = 0 + while nullIndex <= len(fieldData) - 1: + if nullIndex == 0: + PrintEncounterHeaders(TabStr(3) + "{") + + PrintEncounterHeaders(f"{TabStr(4)}{GetIMonInfoStringFromIndex(nullIndex)} = NULL,") + + if nullIndex == len(fieldData) - 1: + PrintEncounterHeaders(TabStr(3) + "},") + + nullIndex += 1 + nullCount += 1 + PrintEncounterHeaders(TabStr(2) + "},") + PrintEncounterHeaders(tabStr + "},") groupCount += 1 PrintEncounterHeaders("};") From 504b08dace4eb4d419392494ca9d807cf8d99304 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Fri, 7 Nov 2025 16:07:17 +0100 Subject: [PATCH 119/130] Fix transform not loading the correct sprites when facing shiny or unown (#8146) --- src/battle_gfx_sfx_util.c | 38 ++++++++++++------------------------ src/battle_script_commands.c | 5 ++++- 2 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index d7004d7635..1beaee4cc4 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -633,15 +633,8 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler) if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR)) gBattleSpritesDataPtr->battlerData[battler].transformSpecies = species = GetGMaxTargetSpecies(species); - if (B_TRANSFORM_SHINY >= GEN_4) - { - personalityValue = gTransformedPersonalities[battler]; - isShiny = gTransformedShininess[battler]; - } - else - { - personalityValue = GetMonData(mon, MON_DATA_PERSONALITY); - } + personalityValue = gTransformedPersonalities[battler]; + isShiny = gTransformedShininess[battler]; } position = GetBattlerPosition(battler); @@ -947,24 +940,23 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo targetSpecies = GetIllusionMonSpecies(battlerDef); else targetSpecies = GetMonData(monDef, MON_DATA_SPECIES); - personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY); - isShiny = GetMonData(monAtk, MON_DATA_IS_SHINY); + gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies = targetSpecies; } else { targetSpecies = gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies; - if (B_TRANSFORM_SHINY >= GEN_4 && trackEnemyPersonality && !megaEvo) - { - personalityValue = GetMonData(monDef, MON_DATA_PERSONALITY); - isShiny = GetMonData(monDef, MON_DATA_IS_SHINY); - } - else - { - personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY); - isShiny = GetMonData(monAtk, MON_DATA_IS_SHINY); - } } + if (trackEnemyPersonality) + { + personalityValue = gDisableStructs[battlerAtk].transformedMonPersonality; + isShiny = gDisableStructs[battlerAtk].transformedMonShininess; + } + else + { + personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY); + isShiny = GetMonData(monAtk, MON_DATA_IS_SHINY); + } HandleLoadSpecialPokePic(!IsOnPlayerSide(battlerAtk), gMonSpritesGfxPtr->spritesGfx[position], targetSpecies, @@ -981,10 +973,6 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo { BlendPalette(paletteOffset, 16, 6, RGB_WHITE); CpuCopy32(&gPlttBufferFaded[paletteOffset], &gPlttBufferUnfaded[paletteOffset], PLTT_SIZEOF(16)); - if (!IsContest()) - { - gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies = targetSpecies; - } } // dynamax tint diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index bc4e8d6551..f3a4cbd803 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -11218,7 +11218,10 @@ static void Cmd_transformdataexecution(void) gDisableStructs[gBattlerAttacker].disabledMove = MOVE_NONE; gDisableStructs[gBattlerAttacker].disableTimer = 0; gDisableStructs[gBattlerAttacker].transformedMonPersonality = gBattleMons[gBattlerTarget].personality; - gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerTarget].isShiny; + if (B_TRANSFORM_SHINY >= GEN_4) + gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerTarget].isShiny; + else + gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerAttacker].isShiny; gDisableStructs[gBattlerAttacker].mimickedMoves = 0; gDisableStructs[gBattlerAttacker].usedMoves = 0; From 2467584af58d4aaae08877d8391ebc442557627e Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Fri, 7 Nov 2025 18:32:15 +0100 Subject: [PATCH 120/130] Prevent EXPECT functions from casting negative numbers into unsigned (#7866) --- include/test/test.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/test/test.h b/include/test/test.h index 48a6e84aa2..7b3225934d 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -143,7 +143,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...); { \ typeof(a) _a = (a), _b = (b); \ if (_a != _b) \ - Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_EQ(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \ + Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_EQ(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \ } while (0) #define EXPECT_NE(a, b) \ @@ -151,7 +151,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...); { \ typeof(a) _a = (a), _b = (b); \ if (_a == _b) \ - Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_NE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \ + Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_NE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \ } while (0) #define EXPECT_LT(a, b) \ @@ -159,7 +159,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...); { \ typeof(a) _a = (a), _b = (b); \ if (_a >= _b) \ - Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \ + Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \ } while (0) #define EXPECT_LE(a, b) \ @@ -167,7 +167,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...); { \ typeof(a) _a = (a), _b = (b); \ if (_a > _b) \ - Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \ + Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \ } while (0) #define EXPECT_GT(a, b) \ @@ -175,7 +175,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...); { \ typeof(a) _a = (a), _b = (b); \ if (_a <= _b) \ - Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \ + Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \ } while (0) #define EXPECT_GE(a, b) \ @@ -183,7 +183,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...); { \ typeof(a) _a = (a), _b = (b); \ if (_a < _b) \ - Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \ + Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \ } while (0) struct Benchmark { s32 ticks; }; From b8dc1968f3488fe45585712215a70bdb44b0c180 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Fri, 7 Nov 2025 18:23:00 +0000 Subject: [PATCH 121/130] Fixes Receiver not immediately activating copied Soul Heart (#8162) --- data/battle_scripts_1.s | 5 +-- test/battle/ability/receiver.c | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 49768b2c43..b47a683f2c 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -5085,8 +5085,8 @@ BattleScript_FaintAttacker:: tryrevertweatherform flushtextbox waitanimation - tryactivatesoulheart tryactivatereceiver BS_ATTACKER + tryactivatesoulheart trytrainerslidemsgfirstoff BS_ATTACKER return @@ -5103,8 +5103,8 @@ BattleScript_FaintTarget:: tryrevertweatherform flushtextbox waitanimation - tryactivatesoulheart tryactivatereceiver BS_TARGET + tryactivatesoulheart trytrainerslidemsgfirstoff BS_TARGET return @@ -7285,6 +7285,7 @@ BattleScript_ReceiverActivates:: printstring STRINGID_RECEIVERABILITYTAKEOVER waitmessage B_WAIT_TIME_LONG settracedability BS_ABILITY_BATTLER + switchinabilities BS_ABILITY_BATTLER return BattleScript_AbilityHpHeal: diff --git a/test/battle/ability/receiver.c b/test/battle/ability/receiver.c index a9155b7244..7e6b7076ba 100644 --- a/test/battle/ability/receiver.c +++ b/test/battle/ability/receiver.c @@ -1,4 +1,63 @@ #include "global.h" #include "test/battle.h" +DOUBLE_BATTLE_TEST("Receiver copies ally's ability when they faint and immediately activates it") +{ + GIVEN { + ASSUME(!gAbilitiesInfo[ABILITY_INTIMIDATE].cantBeCopied); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_PASSIMIAN) { Ability(ABILITY_RECEIVER); } + OPPONENT(SPECIES_GYARADOS) { Ability(ABILITY_INTIMIDATE); HP(1); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentRight); } + } SCENE { + ABILITY_POPUP(opponentRight, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft); + ABILITY_POPUP(opponentLeft, ABILITY_RECEIVER); + ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 2); + EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 2); + } +} + +DOUBLE_BATTLE_TEST("Receiver copies ally's ability when they faint and can activate it on future moves") +{ + GIVEN { + ASSUME(!gAbilitiesInfo[ABILITY_WATER_ABSORB].cantBeCopied); + ASSUME(GetMoveType(MOVE_WATER_GUN) == TYPE_WATER); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_PASSIMIAN) { Ability(ABILITY_RECEIVER); } + OPPONENT(SPECIES_LANTURN) { Ability(ABILITY_WATER_ABSORB); HP(1); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentRight); MOVE(playerRight, MOVE_WATER_GUN, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft); + ABILITY_POPUP(opponentLeft, ABILITY_RECEIVER); + ABILITY_POPUP(opponentLeft, ABILITY_WATER_ABSORB); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, playerRight); + } +} + +DOUBLE_BATTLE_TEST("Receiver copies ally's Soul Heart and immediately activates it") +{ + GIVEN { + ASSUME(!gAbilitiesInfo[ABILITY_SOUL_HEART].cantBeCopied); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_PASSIMIAN) { Ability(ABILITY_RECEIVER); } + OPPONENT(SPECIES_MAGEARNA) { Ability(ABILITY_SOUL_HEART); HP(1); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentRight); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft); + ABILITY_POPUP(opponentLeft, ABILITY_RECEIVER); + ABILITY_POPUP(opponentLeft, ABILITY_SOUL_HEART); + } THEN { + EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + } +} + TO_DO_BATTLE_TEST("TODO: Write Receiver (Ability) test titles") From 4f655301efaeed31685da4c16b460595642abad5 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 7 Nov 2025 23:27:50 +0100 Subject: [PATCH 122/130] add cawtds as a contributor for code (#8166) 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 a54e1caefd..0ae8f41116 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -450,6 +450,15 @@ "contributions": [ "code" ] + }, + { + "login": "cawtds", + "name": "cawtds", + "avatar_url": "https://avatars.githubusercontent.com/u/38510667?v=4", + "profile": "https://github.com/cawtds", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 547b5bd52d..64a31a4077 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -75,6 +75,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d ghostyboyy97
ghostyboyy97

💻 Marky
Marky

💻 MandL27
MandL27

💻 + cawtds
cawtds

💻 From 0bd9dd6ee83a956ace9d048f7799a42b37febab7 Mon Sep 17 00:00:00 2001 From: ghostyboyy97 <106448956+ghostyboyy97@users.noreply.github.com> Date: Sat, 8 Nov 2025 11:21:16 -0500 Subject: [PATCH 123/130] fix (contrary): Contrary stat down handling in MoveEffectInPlus (#8165) Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- src/battle_ai_util.c | 43 ++++++++++++++++++++++++++++++++++ test/battle/ability/contrary.c | 27 +++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index d866e5b0d0..353705cefc 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1041,6 +1041,49 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3 { switch (additionalEffect->moveEffect) { + case MOVE_EFFECT_ATK_MINUS_1: + case MOVE_EFFECT_ATK_MINUS_2: + if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_ATK)) + return TRUE; + break; + case MOVE_EFFECT_DEF_MINUS_1: + case MOVE_EFFECT_DEF_MINUS_2: + if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_ATK)) + return TRUE; + break; + case MOVE_EFFECT_SPD_MINUS_1: + case MOVE_EFFECT_SPD_MINUS_2: + if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_DEF)) + return TRUE; + break; + case MOVE_EFFECT_SP_ATK_MINUS_1: + case MOVE_EFFECT_SP_ATK_MINUS_2: + if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_SPATK)) + return TRUE; + break; + case MOVE_EFFECT_SP_DEF_MINUS_1: + case MOVE_EFFECT_SP_DEF_MINUS_2: + if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_SPDEF)) + return TRUE; + break; + case MOVE_EFFECT_EVS_MINUS_1: + case MOVE_EFFECT_EVS_MINUS_2: + if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_EVASION)) + return TRUE; + break; + case MOVE_EFFECT_ACC_MINUS_1: + case MOVE_EFFECT_ACC_MINUS_2: + if (abilityAtk == ABILITY_CONTRARY && BattlerStatCanRise(battlerAtk, abilityAtk, STAT_ACC)) + return TRUE; + break; + case MOVE_EFFECT_ATK_DEF_DOWN: + if (abilityAtk == ABILITY_CONTRARY && (BattlerStatCanRise(battlerAtk, abilityAtk, STAT_ATK) || BattlerStatCanRise(battlerAtk, abilityAtk, STAT_DEF))) + return TRUE; + break; + case MOVE_EFFECT_DEF_SPDEF_DOWN: + if (abilityAtk == ABILITY_CONTRARY && (BattlerStatCanRise(battlerAtk, abilityAtk, STAT_DEF) || BattlerStatCanRise(battlerAtk, abilityAtk, STAT_SPDEF))) + return TRUE; + break; case MOVE_EFFECT_ATK_PLUS_1: case MOVE_EFFECT_ATK_PLUS_2: if (BattlerStatCanRise(battlerAtk, abilityAtk, STAT_ATK)) diff --git a/test/battle/ability/contrary.c b/test/battle/ability/contrary.c index c69360a1e1..850ca60734 100644 --- a/test/battle/ability/contrary.c +++ b/test/battle/ability/contrary.c @@ -242,4 +242,31 @@ SINGLE_BATTLE_TEST("Sticky Web raises Speed by 1 for Contrary mon on switch-in") } } +AI_SINGLE_BATTLE_TEST("AI sees Contrary-effected moves correctly in MoveEffectInPlus instead of as a neutral effect") +{ + GIVEN{ + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_HERACROSS){ + Level(44); + HP(1); + Speed(5); + Nature(NATURE_ADAMANT); + Item(ITEM_LOADED_DICE); + Moves(MOVE_PIN_MISSILE); + } + OPPONENT(SPECIES_SERPERIOR){ + Level(44); + Speed(10); + Nature(NATURE_TIMID); + Ability(ABILITY_CONTRARY); + Moves(MOVE_DRAGON_PULSE, MOVE_SPIN_OUT, MOVE_HIDDEN_POWER, MOVE_GLARE); + } + } WHEN { + TURN{ + MOVE(player, MOVE_PIN_MISSILE); + EXPECT_MOVE(opponent, MOVE_SPIN_OUT); // previously all 107, now sees speed can rise w/ Contrary + } + } +} + TO_DO_BATTLE_TEST("Contrary does not invert stat changes that have been Baton-passed") From c2c0c06a6f3aef07a7a5bda4af5f13b30ad5bb2b Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Sat, 8 Nov 2025 17:22:47 +0100 Subject: [PATCH 124/130] Fix gba sprites trying load non existent female versions (#7996) --- .../pokemon/species_info/gen_1_families.h | 96 +++++++++---------- .../pokemon/species_info/gen_2_families.h | 76 +++++++-------- .../pokemon/species_info/gen_3_families.h | 68 ++++++------- 3 files changed, 120 insertions(+), 120 deletions(-) diff --git a/src/data/pokemon/species_info/gen_1_families.h b/src/data/pokemon/species_info/gen_1_families.h index daae4cc252..ce9cdce188 100644 --- a/src/data/pokemon/species_info/gen_1_families.h +++ b/src/data/pokemon/species_info/gen_1_families.h @@ -200,12 +200,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Venusaur, .iconSprite = gMonIcon_Venusaur, .iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 1 : 4, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_VenusaurF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 64), .backPicFemale = gMonBackPic_VenusaurF, .backPicSizeFemale = MON_COORDS_SIZE(64, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(2, 6, SHADOW_SIZE_XL_BATTLE_ONLY) FOOTPRINT(Venusaur) @@ -1336,12 +1336,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Butterfree, .iconSprite = gMonIcon_Butterfree, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_ButterfreeF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 56), .backPicFemale = gMonBackPic_ButterfreeF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-3, 13, SHADOW_SIZE_S) FOOTPRINT(Butterfree) @@ -2100,12 +2100,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Rattata, .iconSprite = gMonIcon_Rattata, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_RattataF, .frontPicSizeFemale = MON_COORDS_SIZE(40, 40), .backPicFemale = gMonBackPic_RattataF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_FAST, SHADOW(1, -3, SHADOW_SIZE_S) FOOTPRINT(Rattata) @@ -2192,12 +2192,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Raticate, .iconSprite = gMonIcon_Raticate, .iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 1 : 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_RaticateF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 56), .backPicFemale = gMonBackPic_RaticateF, .backPicSizeFemale = MON_COORDS_SIZE(64, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_FAST, SHADOW(0, 8, SHADOW_SIZE_L) FOOTPRINT(Raticate) @@ -2916,7 +2916,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Pikachu, .iconSprite = gMonIcon_Pikachu, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_PikachuF, .frontPicSizeFemale = MON_COORDS_SIZE(48, 48), .backPicFemale = gMonBackPic_PikachuF, @@ -2925,7 +2925,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .iconSpriteFemale = gMonIcon_PikachuF, .iconPalIndexFemale = 2, #endif //P_CUSTOM_GENDER_DIFF_ICONS -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NORMAL, SHADOW(-3, 5, SHADOW_SIZE_M) OVERWORLD( @@ -5604,12 +5604,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Zubat, .iconSprite = gMonIcon_Zubat, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_ZubatF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 48), .backPicFemale = gMonBackPic_ZubatF, .backPicSizeFemale = MON_COORDS_SIZE(56, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-4, 11, SHADOW_SIZE_S) FOOTPRINT(Zubat) @@ -5703,12 +5703,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Golbat, .iconSprite = gMonIcon_Golbat, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_GolbatF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 56), .backPicFemale = gMonBackPic_GolbatF, .backPicSizeFemale = MON_COORDS_SIZE(56, 40), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(2, 14, SHADOW_SIZE_M) FOOTPRINT(Golbat) @@ -5954,12 +5954,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Gloom, .iconSprite = gMonIcon_Gloom, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_GloomF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 48), .backPicFemale = gMonBackPic_GloomF, .backPicSizeFemale = MON_COORDS_SIZE(64, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-1, 3, SHADOW_SIZE_M) FOOTPRINT(Gloom) @@ -6047,12 +6047,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Vileplume, .iconSprite = gMonIcon_Vileplume, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_VileplumeF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 56), .backPicFemale = gMonBackPic_VileplumeF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-1, 7, SHADOW_SIZE_L) FOOTPRINT(Vileplume) @@ -8219,12 +8219,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Politoed, .iconSprite = gMonIcon_Politoed, .iconPalIndex = 1, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_PolitoedF, .frontPicSizeFemale = MON_COORDS_SIZE(48, 56), .backPicFemale = gMonBackPic_PolitoedF, .backPicSizeFemale = MON_COORDS_SIZE(56, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(1, 9, SHADOW_SIZE_M) FOOTPRINT(Politoed) @@ -8382,12 +8382,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Kadabra, .iconSprite = gMonIcon_Kadabra, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_KadabraF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 56), .backPicFemale = gMonBackPic_KadabraF, .backPicSizeFemale = MON_COORDS_SIZE(64, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(1, 6, SHADOW_SIZE_L) FOOTPRINT(Kadabra) @@ -8481,12 +8481,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Alakazam, .iconSprite = gMonIcon_Alakazam, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_AlakazamF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 64), .backPicFemale = gMonBackPic_AlakazamF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-1, 9, SHADOW_SIZE_L) FOOTPRINT(Alakazam) @@ -10991,12 +10991,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Doduo, .iconSprite = gMonIcon_Doduo, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_DoduoF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 56), .backPicFemale = gMonBackPic_DoduoF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(6, 5, SHADOW_SIZE_M) FOOTPRINT(Doduo) @@ -11084,12 +11084,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Dodrio, .iconSprite = gMonIcon_Dodrio, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_DodrioF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 64), .backPicFemale = gMonBackPic_DodrioF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(3, 12, SHADOW_SIZE_L) FOOTPRINT(Dodrio) @@ -12187,12 +12187,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Steelix, .iconSprite = gMonIcon_Steelix, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_SteelixF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 64), .backPicFemale = gMonBackPic_SteelixF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(2, 12, SHADOW_SIZE_XL_BATTLE_ONLY) FOOTPRINT(Steelix) @@ -12429,12 +12429,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Hypno, .iconSprite = gMonIcon_Hypno, .iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 1 : 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_HypnoF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 64), .backPicFemale = gMonBackPic_HypnoF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-3, 9, SHADOW_SIZE_L) FOOTPRINT(Hypno) @@ -14188,12 +14188,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Rhyhorn, .iconSprite = gMonIcon_Rhyhorn, .iconPalIndex = 1, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_RhyhornF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 40), .backPicFemale = gMonBackPic_RhyhornF, .backPicSizeFemale = MON_COORDS_SIZE(64, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(0, 0, SHADOW_SIZE_L) FOOTPRINT(Rhyhorn) @@ -14269,12 +14269,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Rhydon, .iconSprite = gMonIcon_Rhydon, .iconPalIndex = 1, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_RhydonF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 56), .backPicFemale = gMonBackPic_RhydonF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(3, 9, SHADOW_SIZE_XL_BATTLE_ONLY) FOOTPRINT(Rhydon) @@ -15215,12 +15215,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Goldeen, .iconSprite = gMonIcon_Goldeen, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_GoldeenF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 40), .backPicFemale = gMonBackPic_GoldeenF, .backPicSizeFemale = MON_COORDS_SIZE(64, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-6, 0, SHADOW_SIZE_XL_BATTLE_ONLY) FOOTPRINT(Goldeen) @@ -15300,12 +15300,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Seaking, .iconSprite = gMonIcon_Seaking, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_SeakingF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 56), .backPicFemale = gMonBackPic_SeakingF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(0, 8, SHADOW_SIZE_XL_BATTLE_ONLY) FOOTPRINT(Seaking) @@ -17178,12 +17178,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Magikarp, .iconSprite = gMonIcon_Magikarp, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_MagikarpF, .frontPicSizeFemale = MON_COORDS_SIZE(48, 56), .backPicFemale = gMonBackPic_MagikarpF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(1, 7, SHADOW_SIZE_M) FOOTPRINT(Magikarp) @@ -17260,12 +17260,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Gyarados, .iconSprite = gMonIcon_Gyarados, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_GyaradosF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 64), .backPicFemale = gMonBackPic_GyaradosF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(5, 9, SHADOW_SIZE_XL_BATTLE_ONLY) FOOTPRINT(Gyarados) @@ -17622,12 +17622,12 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Eevee, .iconSprite = gMonIcon_Eevee, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_EeveeF, .frontPicSizeFemale = MON_COORDS_SIZE(40, 48), .backPicFemale = gMonBackPic_EeveeF, .backPicSizeFemale = MON_COORDS_SIZE(56, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NORMAL, SHADOW(-2, 2, SHADOW_SIZE_S) FOOTPRINT(Eevee) @@ -17777,7 +17777,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .shinyPalette = gMonShinyPalette_Eevee, .iconSprite = gMonIcon_EeveePartner, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_EeveeF, .frontPicSizeFemale = MON_COORDS_SIZE(40, 48), .backPicFemale = gMonBackPic_EeveeF, @@ -17786,7 +17786,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = .iconSpriteFemale = gMonIcon_EeveePartnerF, .iconPalIndexFemale = 2, #endif -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NORMAL, SHADOW(-2, 2, SHADOW_SIZE_S) FOOTPRINT(Eevee) diff --git a/src/data/pokemon/species_info/gen_2_families.h b/src/data/pokemon/species_info/gen_2_families.h index f95197332a..702b907451 100644 --- a/src/data/pokemon/species_info/gen_2_families.h +++ b/src/data/pokemon/species_info/gen_2_families.h @@ -198,12 +198,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Meganium, .iconSprite = gMonIcon_Meganium, .iconPalIndex = 1, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_MeganiumF, .frontPicSizeFemale = MON_COORDS_SIZE(48, 64), .backPicFemale = gMonBackPic_MeganiumF, .backPicSizeFemale = MON_COORDS_SIZE(56, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(0, 13, SHADOW_SIZE_M) FOOTPRINT(Meganium) @@ -1074,12 +1074,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Ledyba, .iconSprite = gMonIcon_Ledyba, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_LedybaF, .frontPicSizeFemale = MON_COORDS_SIZE(48, 48), .backPicFemale = gMonBackPic_LedybaF, .backPicSizeFemale = MON_COORDS_SIZE(56, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(2, 4, SHADOW_SIZE_M) FOOTPRINT(Ledyba) @@ -1158,12 +1158,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Ledian, .iconSprite = gMonIcon_Ledian, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_LedianF, .frontPicSizeFemale = MON_COORDS_SIZE(48, 56), .backPicFemale = gMonBackPic_LedianF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(0, 15, SHADOW_SIZE_S) FOOTPRINT(Ledian) @@ -2522,12 +2522,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Sudowoodo, .iconSprite = gMonIcon_Sudowoodo, .iconPalIndex = 1, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_SudowoodoF, .frontPicSizeFemale = MON_COORDS_SIZE(48, 56), .backPicFemale = gMonBackPic_SudowoodoF, .backPicSizeFemale = MON_COORDS_SIZE(48, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-2, 7, SHADOW_SIZE_S) FOOTPRINT(Sudowoodo) @@ -2842,12 +2842,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Aipom, .iconSprite = gMonIcon_Aipom, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_AipomF, .frontPicSizeFemale = MON_COORDS_SIZE(32, 64), .backPicFemale = gMonBackPic_AipomF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-1, 12, SHADOW_SIZE_S) FOOTPRINT(Aipom) @@ -2927,12 +2927,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Ambipom, .iconSprite = gMonIcon_Ambipom, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_AmbipomF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 64), .backPicFemale = gMonBackPic_AmbipomF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-1, 13, SHADOW_SIZE_S) FOOTPRINT(Ambipom) @@ -3347,12 +3347,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Wooper, .iconSprite = gMonIcon_Wooper, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_WooperF, .frontPicSizeFemale = MON_COORDS_SIZE(40, 32), .backPicFemale = gMonBackPic_WooperF, .backPicSizeFemale = MON_COORDS_SIZE(64, 40), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_SLOW, SHADOW(1, -2, SHADOW_SIZE_S) FOOTPRINT(Wooper) @@ -3429,12 +3429,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Quagsire, .iconSprite = gMonIcon_Quagsire, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_QuagsireF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 56), .backPicFemale = gMonBackPic_QuagsireF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(0, 8, SHADOW_SIZE_M) FOOTPRINT(Quagsire) @@ -3647,12 +3647,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Murkrow, .iconSprite = gMonIcon_Murkrow, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_MurkrowF, .frontPicSizeFemale = MON_COORDS_SIZE(48, 40), .backPicFemale = gMonBackPic_MurkrowF, .backPicSizeFemale = MON_COORDS_SIZE(40, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-2, 8, SHADOW_SIZE_S) FOOTPRINT(Murkrow) @@ -4134,7 +4134,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Wobbuffet, .iconSprite = gMonIcon_Wobbuffet, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_WobbuffetF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 56), .backPicFemale = gMonBackPic_WobbuffetF, @@ -4143,7 +4143,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .iconSpriteFemale = gMonIcon_WobbuffetF, .iconPalIndexFemale = 0, #endif -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-3, 8, SHADOW_SIZE_M) FOOTPRINT(Wobbuffet) @@ -4221,12 +4221,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Girafarig, .iconSprite = gMonIcon_Girafarig, .iconPalIndex = 1, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_GirafarigF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 64), .backPicFemale = gMonBackPic_GirafarigF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(2, 13, SHADOW_SIZE_M) FOOTPRINT(Girafarig) @@ -4728,12 +4728,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Gligar, .iconSprite = gMonIcon_Gligar, .iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 2 : 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_GligarF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 56), .backPicFemale = gMonBackPic_GligarF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(0, 15, SHADOW_SIZE_S) FOOTPRINT(Gligar) @@ -5332,12 +5332,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Heracross, .iconSprite = gMonIcon_Heracross, .iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 2 : 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_HeracrossF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 64), .backPicFemale = gMonBackPic_HeracrossF, .backPicSizeFemale = MON_COORDS_SIZE(48, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-1, 10, SHADOW_SIZE_M) FOOTPRINT(Heracross) @@ -5493,12 +5493,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Sneasel, .iconSprite = gMonIcon_Sneasel, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_SneaselF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 48), .backPicFemale = gMonBackPic_SneaselF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-1, 5, SHADOW_SIZE_S) FOOTPRINT(Sneasel) @@ -5888,12 +5888,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Ursaring, .iconSprite = gMonIcon_Ursaring, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_UrsaringF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 64), .backPicFemale = gMonBackPic_UrsaringF, .backPicSizeFemale = MON_COORDS_SIZE(56, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(1, 14, SHADOW_SIZE_L) FOOTPRINT(Ursaring) @@ -6323,12 +6323,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Piloswine, .iconSprite = gMonIcon_Piloswine, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_PiloswineF, .frontPicSizeFemale = MON_COORDS_SIZE(48, 56), .backPicFemale = gMonBackPic_PiloswineF, .backPicSizeFemale = MON_COORDS_SIZE(64, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-1, 3, SHADOW_SIZE_M) FOOTPRINT(Piloswine) @@ -6791,12 +6791,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Octillery, .iconSprite = gMonIcon_Octillery, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_OctilleryF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 48), .backPicFemale = gMonBackPic_OctilleryF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(1, 4, SHADOW_SIZE_M) FOOTPRINT(Octillery) @@ -7229,12 +7229,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Houndoom, .iconSprite = gMonIcon_Houndoom, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_HoundoomF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 64), .backPicFemale = gMonBackPic_HoundoomF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-5, 13, SHADOW_SIZE_L) FOOTPRINT(Houndoom) @@ -7456,12 +7456,12 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .shinyPalette = gMonShinyPalette_Donphan, .iconSprite = gMonIcon_Donphan, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_DonphanF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 48), .backPicFemale = gMonBackPic_DonphanF, .backPicSizeFemale = MON_COORDS_SIZE(64, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(7, 2, SHADOW_SIZE_L) FOOTPRINT(Donphan) diff --git a/src/data/pokemon/species_info/gen_3_families.h b/src/data/pokemon/species_info/gen_3_families.h index 6ad30646cf..566f79b404 100644 --- a/src/data/pokemon/species_info/gen_3_families.h +++ b/src/data/pokemon/species_info/gen_3_families.h @@ -347,10 +347,10 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Torchic, .iconSprite = gMonIcon_Torchic, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .backPicFemale = gMonBackPic_TorchicF, .backPicSizeFemale = MON_COORDS_SIZE(40, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_SLOW, SHADOW(-1, 1, SHADOW_SIZE_S) FOOTPRINT(Torchic) @@ -428,12 +428,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Combusken, .iconSprite = gMonIcon_Combusken, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_CombuskenF, .frontPicSizeFemale = MON_COORDS_SIZE(48, 64), .backPicFemale = gMonBackPic_CombuskenF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-1, 8, SHADOW_SIZE_M) FOOTPRINT(Combusken) @@ -517,12 +517,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Blaziken, .iconSprite = gMonIcon_Blaziken, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_BlazikenF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 64), .backPicFemale = gMonBackPic_BlazikenF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(4, 8, SHADOW_SIZE_M) FOOTPRINT(Blaziken) @@ -1644,12 +1644,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Beautifly, .iconSprite = gMonIcon_Beautifly, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_BeautiflyF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 56), .backPicFemale = gMonBackPic_BeautiflyF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-5, 12, SHADOW_SIZE_S) FOOTPRINT(Beautifly) @@ -1825,12 +1825,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Dustox, .iconSprite = gMonIcon_Dustox, .iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 1 : 5, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_DustoxF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 48), .backPicFemale = gMonBackPic_DustoxF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-2, 11, SHADOW_SIZE_S) FOOTPRINT(Dustox) @@ -2059,12 +2059,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Ludicolo, .iconSprite = gMonIcon_Ludicolo, .iconPalIndex = 1, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_LudicoloF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 64), .backPicFemale = gMonBackPic_LudicoloF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-3, 14, SHADOW_SIZE_M) FOOTPRINT(Ludicolo) @@ -2224,12 +2224,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Nuzleaf, .iconSprite = gMonIcon_Nuzleaf, .iconPalIndex = 1, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_NuzleafF, .frontPicSizeFemale = MON_COORDS_SIZE(40, 56), .backPicFemale = gMonBackPic_NuzleafF, .backPicSizeFemale = MON_COORDS_SIZE(56, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-3, 5, SHADOW_SIZE_S) FOOTPRINT(Nuzleaf) @@ -2316,12 +2316,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Shiftry, .iconSprite = gMonIcon_Shiftry, .iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 0 : 5, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_ShiftryF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 56), .backPicFemale = gMonBackPic_ShiftryF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-5, 5, SHADOW_SIZE_M) FOOTPRINT(Shiftry) @@ -5202,12 +5202,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Meditite, .iconSprite = gMonIcon_Meditite, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_MedititeF, .frontPicSizeFemale = MON_COORDS_SIZE(48, 48), .backPicFemale = gMonBackPic_MedititeF, .backPicSizeFemale = MON_COORDS_SIZE(48, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_SLOW, SHADOW(0, 1, SHADOW_SIZE_S) FOOTPRINT(Meditite) @@ -5288,12 +5288,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Medicham, .iconSprite = gMonIcon_Medicham, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_MedichamF, .frontPicSizeFemale = MON_COORDS_SIZE(40, 64), .backPicFemale = gMonBackPic_MedichamF, .backPicSizeFemale = MON_COORDS_SIZE(56, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(-2, 13, SHADOW_SIZE_S) FOOTPRINT(Medicham) @@ -6046,12 +6046,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Roselia, .iconSprite = gMonIcon_Roselia, .iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 0 : 4, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_RoseliaF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 48), .backPicFemale = gMonBackPic_RoseliaF, .backPicSizeFemale = MON_COORDS_SIZE(64, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_SLOW, SHADOW(-2, 3, SHADOW_SIZE_S) FOOTPRINT(Roselia) @@ -6227,12 +6227,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Gulpin, .iconSprite = gMonIcon_Gulpin, .iconPalIndex = 1, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_GulpinF, .frontPicSizeFemale = MON_COORDS_SIZE(40, 48), .backPicFemale = gMonBackPic_GulpinF, .backPicSizeFemale = MON_COORDS_SIZE(56, 48), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_SLOW, SHADOW(1, -2, SHADOW_SIZE_S) FOOTPRINT(Gulpin) @@ -6312,12 +6312,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Swalot, .iconSprite = gMonIcon_Swalot, .iconPalIndex = 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_SwalotF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 56), .backPicFemale = gMonBackPic_SwalotF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(4, 3, SHADOW_SIZE_L) FOOTPRINT(Swalot) @@ -6769,12 +6769,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Numel, .iconSprite = gMonIcon_Numel, .iconPalIndex = 1, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_NumelF, .frontPicSizeFemale = MON_COORDS_SIZE(40, 48), .backPicFemale = gMonBackPic_NumelF, .backPicSizeFemale = MON_COORDS_SIZE(56, 56), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_SLOW, SHADOW(4, 2, SHADOW_SIZE_S) FOOTPRINT(Numel) @@ -6860,12 +6860,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Camerupt, .iconSprite = gMonIcon_Camerupt, .iconPalIndex = 0, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_CameruptF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 56), .backPicFemale = gMonBackPic_CameruptF, .backPicSizeFemale = MON_COORDS_SIZE(64, 40), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(2, 4, SHADOW_SIZE_L) FOOTPRINT(Camerupt) @@ -9112,12 +9112,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Milotic, .iconSprite = gMonIcon_Milotic, .iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 0 : 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_MiloticF, .frontPicSizeFemale = MON_COORDS_SIZE(64, 64), .backPicFemale = gMonBackPic_MiloticF, .backPicSizeFemale = MON_COORDS_SIZE(64, 64), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(0, 12, SHADOW_SIZE_M) FOOTPRINT(Milotic) @@ -11201,12 +11201,12 @@ const struct SpeciesInfo gSpeciesInfoGen3[] = .shinyPalette = gMonShinyPalette_Relicanth, .iconSprite = gMonIcon_Relicanth, .iconPalIndex = P_GBA_STYLE_SPECIES_ICONS ? 1 : 2, -#if P_GENDER_DIFFERENCES +#if P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .frontPicFemale = gMonFrontPic_RelicanthF, .frontPicSizeFemale = MON_COORDS_SIZE(56, 56), .backPicFemale = gMonBackPic_RelicanthF, .backPicSizeFemale = MON_COORDS_SIZE(64, 40), -#endif //P_GENDER_DIFFERENCES +#endif //P_GENDER_DIFFERENCES && !P_GBA_STYLE_SPECIES_GFX .pokemonJumpType = PKMN_JUMP_TYPE_NONE, SHADOW(0, 3, SHADOW_SIZE_M) FOOTPRINT(Relicanth) From bdaf4ae3b387460b910e5a82e8b5039c23ce87bf Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Sun, 9 Nov 2025 15:03:43 +0100 Subject: [PATCH 125/130] Fix destiny knot behavior and add tests (#8174) --- asm/macros/battle_script.inc | 6 --- data/battle_scripts_1.s | 16 ++++--- src/battle_message.c | 2 +- src/battle_script_commands.c | 9 ---- test/battle/hold_effect/destiny_knot.c | 66 ++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 22 deletions(-) create mode 100644 test/battle/hold_effect/destiny_knot.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 6203c3a7bf..e5da506a4d 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1970,12 +1970,6 @@ .4byte \jumpInstr .endm - .macro infatuatewithbattler battler:req, infatuateWith:req - callnative BS_InfatuateWithBattler - .byte \battler - .byte \infatuateWith - .endm - .macro setlastuseditem battler:req callnative BS_SetLastUsedItem .byte \battler diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index b47a683f2c..cc00828984 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -4025,26 +4025,27 @@ BattleScript_FuryCutterHit: BattleScript_TryDestinyKnotTarget: jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_DESTINY_KNOT, BattleScript_TryDestinyKnotTargetRet - infatuatewithbattler BS_TARGET, BS_ATTACKER playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT waitanimation + printstring STRINGID_DESTINYKNOTACTIVATES + tryinfatuating BattleScript_ButItFailed volatileanimation BS_TARGET, VOLATILE_INFATUATION waitanimation - printstring STRINGID_DESTINYKNOTACTIVATES waitmessage B_WAIT_TIME_LONG BattleScript_TryDestinyKnotTargetRet: return BattleScript_TryDestinyKnotAttacker: - jumpifnoholdeffect BS_TARGET, HOLD_EFFECT_DESTINY_KNOT, BattleScript_TryDestinyKnotAttackerRet - infatuatewithbattler BS_ATTACKER, BS_TARGET + jumpifnoholdeffect BS_TARGET, HOLD_EFFECT_DESTINY_KNOT, BattleScript_TryDestinyKnotTargetRet playanimation BS_TARGET, B_ANIM_HELD_ITEM_EFFECT waitanimation + swapattackerwithtarget + printstring STRINGID_DESTINYKNOTACTIVATES + tryinfatuating BattleScript_SwapTargetAttackerButItFailed + swapattackerwithtarget volatileanimation BS_ATTACKER, VOLATILE_INFATUATION waitanimation - printstring STRINGID_DESTINYKNOTACTIVATES waitmessage B_WAIT_TIME_LONG -BattleScript_TryDestinyKnotAttackerRet: return BattleScript_EffectAttract:: @@ -4378,6 +4379,9 @@ BattleScript_RestoreAttackerButItFailed: BattleScript_RestoreTargetButItFailed: restoretarget goto BattleScript_ButItFailed +BattleScript_SwapTargetAttackerButItFailed: + swapattackerwithtarget + goto BattleScript_ButItFailed BattleScript_NotAffected:: pause B_WAIT_TIME_SHORT diff --git a/src/battle_message.c b/src/battle_message.c index 0eb8c85791..fc33a06414 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -705,7 +705,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_RECEIVERABILITYTAKEOVER] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX}'s {B_SCR_ABILITY} was taken over!"), [STRINGID_PKNMABSORBINGPOWER] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is absorbing power!"), [STRINGID_NOONEWILLBEABLETORUNAWAY] = COMPOUND_STRING("No one will be able to run away during the next turn!"), - [STRINGID_DESTINYKNOTACTIVATES] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} fell in love because of the {B_LAST_ITEM}!"), + [STRINGID_DESTINYKNOTACTIVATES] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} fell in love because of the {B_LAST_ITEM}!"), [STRINGID_CLOAKEDINAFREEZINGLIGHT] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} became cloaked in a freezing light!"), [STRINGID_CLEARAMULETWONTLOWERSTATS] = COMPOUND_STRING("The effects of the {B_LAST_ITEM} held by {B_SCR_NAME_WITH_PREFIX2} prevents its stats from being lowered!"), [STRINGID_FERVENTWISHREACHED] = COMPOUND_STRING("{B_ATK_TRAINER_NAME}'s fervent wish has reached {B_ATK_NAME_WITH_PREFIX2}!"), diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index f3a4cbd803..3d4bd61613 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -16848,15 +16848,6 @@ void BS_JumpIfNoAlly(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_InfatuateWithBattler(void) -{ - NATIVE_ARGS(u8 battler, u8 infatuateWith); - u32 battler = GetBattlerForBattleScript(cmd->battler); - gBattleScripting.battler = battler; - gBattleMons[battler].volatiles.infatuation = INFATUATED_WITH(GetBattlerForBattleScript(cmd->infatuateWith)); - gBattlescriptCurrInstr = cmd->nextInstr; -} - void BS_SetLastUsedItem(void) { NATIVE_ARGS(u8 battler); diff --git a/test/battle/hold_effect/destiny_knot.c b/test/battle/hold_effect/destiny_knot.c new file mode 100644 index 0000000000..b8097c6012 --- /dev/null +++ b/test/battle/hold_effect/destiny_knot.c @@ -0,0 +1,66 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gItemsInfo[ITEM_DESTINY_KNOT].holdEffect == HOLD_EFFECT_DESTINY_KNOT); +} + +SINGLE_BATTLE_TEST("Destiny Knot infatuates back when holder is targeted") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Gender(MON_MALE); } + OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_FEMALE); Item(ITEM_DESTINY_KNOT); } + } WHEN { + TURN { MOVE(player, MOVE_ATTRACT); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + MESSAGE("Wobbuffet fell in love because of the Destiny Knot!"); + } THEN { + EXPECT(player->volatiles.infatuation); + } +} + +SINGLE_BATTLE_TEST("Destiny Knot infatuates back when holder is attacking") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Gender(MON_MALE); Item(ITEM_DESTINY_KNOT);} + OPPONENT(SPECIES_CLEFAIRY) { Gender(MON_FEMALE); Ability(ABILITY_CUTE_CHARM);} + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("The opposing Clefairy fell in love because of the Destiny Knot!"); + } THEN { + EXPECT(opponent->volatiles.infatuation); + } +} + + +SINGLE_BATTLE_TEST("Destiny Knot procs but fails if the target is already infatuated") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Gender(MON_MALE); } + OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_FEMALE); Item(ITEM_DESTINY_KNOT); } + } WHEN { + TURN { MOVE(opponent, MOVE_ATTRACT); MOVE(player, MOVE_ATTRACT, WITH_RNG(RNG_INFATUATION, FALSE)); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Destiny Knot procs but fails if the target is oblivious") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Gender(MON_MALE); Ability(ABILITY_OBLIVIOUS); } + OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_FEMALE); Item(ITEM_DESTINY_KNOT); } + } WHEN { + TURN { MOVE(player, MOVE_ATTRACT); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + ABILITY_POPUP(player, ABILITY_OBLIVIOUS); + } THEN { + EXPECT(!player->volatiles.infatuation); + } +} From 43f9a78da5a3991691a5e302e978c732cf7b9451 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Sun, 9 Nov 2025 17:46:22 +0100 Subject: [PATCH 126/130] Fix recharge moves + add recharge move tests (#8181) --- include/constants/battle.h | 1 - src/battle_ai_util.c | 2 +- src/battle_debug.c | 1 - src/battle_main.c | 9 ++-- src/battle_script_commands.c | 1 - src/battle_util.c | 6 +-- test/battle/move_effect_secondary/recharge.c | 52 ++++++++++++++++++++ 7 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 test/battle/move_effect_secondary/recharge.c diff --git a/include/constants/battle.h b/include/constants/battle.h index 012b8986ef..d2913d6fc6 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -160,7 +160,6 @@ enum VolatileFlags F(VOLATILE_INFATUATION, infatuation, (enum BattlerId, MAX_BITS(4))) \ F(VOLATILE_DEFENSE_CURL, defenseCurl, (u32, 1)) \ F(VOLATILE_TRANSFORMED, transformed, (u32, 1)) \ - F(VOLATILE_RECHARGE, recharge, (u32, 1)) \ F(VOLATILE_RAGE, rage, (u32, 1)) \ F(VOLATILE_SUBSTITUTE, substitute, (u32, 1), V_BATON_PASSABLE) \ F(VOLATILE_DESTINY_BOND, destinyBond, (u32, 1)) \ diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 353705cefc..8bed0234ee 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -3435,7 +3435,7 @@ bool32 IsBattlerIncapacitated(u32 battler, u32 ability) if (gBattleMons[battler].status1 & STATUS1_SLEEP && !HasMoveWithEffect(battler, EFFECT_SLEEP_TALK)) return TRUE; - if (gBattleMons[battler].volatiles.recharge || (ability == ABILITY_TRUANT && gDisableStructs[battler].truantCounter != 0)) + if (gDisableStructs[battler].rechargeTimer > 0 || (ability == ABILITY_TRUANT && gDisableStructs[battler].truantCounter != 0)) return TRUE; return FALSE; diff --git a/src/battle_debug.c b/src/battle_debug.c index 149cb4065c..b160dd9d78 100644 --- a/src/battle_debug.c +++ b/src/battle_debug.c @@ -361,7 +361,6 @@ static const struct ListMenuItem sVolatileStatusListItems[] = {COMPOUND_STRING("Torment"), VOLATILE_TORMENT}, {COMPOUND_STRING("Powder"), VOLATILE_POWDER}, {COMPOUND_STRING("DefenseCurl"), VOLATILE_DEFENSE_CURL}, - {COMPOUND_STRING("Recharge"), VOLATILE_RECHARGE}, {COMPOUND_STRING("Rage"), VOLATILE_RAGE}, {COMPOUND_STRING("DestinyBond"), VOLATILE_DESTINY_BOND}, {COMPOUND_STRING("EscapePrevention"), VOLATILE_ESCAPE_PREVENTION}, diff --git a/src/battle_main.c b/src/battle_main.c index 15dfe0a3e4..702550c22d 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4213,7 +4213,7 @@ static void HandleTurnActionSelectionState(void) else { if (gBattleMons[battler].volatiles.multipleTurns - || gBattleMons[battler].volatiles.recharge) + || gDisableStructs[battler].rechargeTimer > 0) { gChosenActionByBattler[battler] = B_ACTION_USE_MOVE; gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY; @@ -4368,7 +4368,7 @@ static void HandleTurnActionSelectionState(void) gBattleCommunication[GetPartnerBattler(battler)] = STATE_BEFORE_ACTION_CHOSEN; RecordedBattle_ClearBattlerAction(battler, 1); if (gBattleMons[GetPartnerBattler(battler)].volatiles.multipleTurns - || gBattleMons[GetPartnerBattler(battler)].volatiles.recharge) + || gDisableStructs[GetPartnerBattler(battler)].rechargeTimer > 0) { BtlController_EmitEndBounceEffect(battler, B_COMM_TO_CONTROLLER); MarkBattlerForControllerExec(battler); @@ -5132,11 +5132,8 @@ static void TurnValuesCleanUp(bool8 var0) gDisableStructs[i].isFirstTurn--; if (gDisableStructs[i].rechargeTimer) - { gDisableStructs[i].rechargeTimer--; - if (gDisableStructs[i].rechargeTimer == 0) - gBattleMons[i].volatiles.recharge = FALSE; - } + gBattleStruct->battlerState[i].canPickupItem = FALSE; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 3d4bd61613..ca7f490f11 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3314,7 +3314,6 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai if (B_SKIP_RECHARGE == GEN_1 && !IsBattlerAlive(gBattlerTarget)) // Skip recharge if gen 1 and foe is KO'd break; - gBattleMons[gEffectBattler].volatiles.recharge = TRUE; gDisableStructs[gEffectBattler].rechargeTimer = 2; gLockedMoves[gEffectBattler] = gCurrentMove; gBattlescriptCurrInstr++; diff --git a/src/battle_util.c b/src/battle_util.c index c3cd8c5b54..f6a61ffde4 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -406,7 +406,7 @@ void HandleAction_UseMove(void) gHitMarker |= HITMARKER_NO_PPDEDUCT; gBattleStruct->moveTarget[gBattlerAttacker] = GetBattleMoveTarget(MOVE_STRUGGLE, NO_TARGET_OVERRIDE); } - else if (gBattleMons[gBattlerAttacker].volatiles.multipleTurns || gBattleMons[gBattlerAttacker].volatiles.recharge) + else if (gBattleMons[gBattlerAttacker].volatiles.multipleTurns || gDisableStructs[gBattlerAttacker].rechargeTimer > 0) { gCurrentMove = gChosenMove = gLockedMoves[gBattlerAttacker]; } @@ -1928,10 +1928,8 @@ static enum MoveCanceller CancellerSkyDrop(void) static enum MoveCanceller CancellerRecharge(void) { - if (gBattleMons[gBattlerAttacker].volatiles.recharge) + if (gDisableStructs[gBattlerAttacker].rechargeTimer > 0) { - gBattleMons[gBattlerAttacker].volatiles.recharge = FALSE; - gDisableStructs[gBattlerAttacker].rechargeTimer = 0; CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK); gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; diff --git a/test/battle/move_effect_secondary/recharge.c b/test/battle/move_effect_secondary/recharge.c new file mode 100644 index 0000000000..9146083675 --- /dev/null +++ b/test/battle/move_effect_secondary/recharge.c @@ -0,0 +1,52 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(MoveHasAdditionalEffectSelf(MOVE_METEOR_ASSAULT, MOVE_EFFECT_RECHARGE) == TRUE); +} + +SINGLE_BATTLE_TEST("Recharge moves make the user unable to attack for exactly one turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { } + OPPONENT(SPECIES_WOBBUFFET) { } + } WHEN { + TURN { MOVE(player, MOVE_METEOR_ASSAULT);} + TURN { SKIP_TURN(player);} + TURN { MOVE(player, MOVE_TACKLE);} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_METEOR_ASSAULT, player); + MESSAGE("Wobbuffet must recharge!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + } +} + +SINGLE_BATTLE_TEST("Recharge moves don't timeout when all battlers are recharging") +{ + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { } + OPPONENT(SPECIES_WOBBUFFET) { } + } WHEN { + TURN { MOVE(player, MOVE_METEOR_ASSAULT); MOVE(opponent, MOVE_METEOR_ASSAULT);} + } +} + +DOUBLE_BATTLE_TEST("Recharge moves don't timeout when all battlers are recharging (doubles") +{ + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { } + PLAYER(SPECIES_WYNAUT) { } + OPPONENT(SPECIES_WOBBUFFET) { } + OPPONENT(SPECIES_WYNAUT) { } + } WHEN { + TURN { + MOVE(playerLeft, MOVE_METEOR_ASSAULT, target: opponentLeft); + MOVE(playerRight, MOVE_METEOR_ASSAULT, target: opponentRight); + MOVE(opponentLeft, MOVE_METEOR_ASSAULT, target: playerLeft); + MOVE(opponentRight, MOVE_METEOR_ASSAULT, target: playerRight); + } + } +} From 6c383fac96e36841e4aee8f8de0207573ef5ac52 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 9 Nov 2025 18:10:11 +0100 Subject: [PATCH 127/130] Fixes Magician for spread moves (#8170) --- data/battle_scripts_1.s | 3 +- src/battle_message.c | 2 +- src/battle_script_commands.c | 81 ++++++++++++++++++++++++---------- test/battle/ability/magician.c | 28 ++++++++++++ 4 files changed, 88 insertions(+), 26 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index cc00828984..a353b44ba8 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7111,7 +7111,7 @@ BattleScript_RecoilEnd:: return BattleScript_ItemSteal:: - playanimation BS_TARGET, B_ANIM_ITEM_STEAL + playanimation BS_EFFECT_BATTLER, B_ANIM_ITEM_STEAL printstring STRINGID_PKMNSTOLEITEM waitmessage B_WAIT_TIME_LONG return @@ -9122,6 +9122,7 @@ BattleScript_Pickpocket:: call BattleScript_AbilityPopUp jumpifability BS_ATTACKER, ABILITY_STICKY_HOLD, BattleScript_PickpocketPrevented swapattackerwithtarget + copybyte gEffectBattler, gBattlerTarget call BattleScript_ItemSteal swapattackerwithtarget activateitemeffects diff --git a/src/battle_message.c b/src/battle_message.c index fc33a06414..aeb32f259f 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -303,7 +303,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] = [STRINGID_PKMNTRYINGTOTAKEFOE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is hoping to take its attacker down with it!"), [STRINGID_PKMNTOOKFOE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} took its attacker down with it!"), [STRINGID_PKMNREDUCEDPP] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX}'s PP was reduced!"), - [STRINGID_PKMNSTOLEITEM] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} stole {B_DEF_NAME_WITH_PREFIX2}'s {B_LAST_ITEM}!"), + [STRINGID_PKMNSTOLEITEM] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} stole {B_EFF_NAME_WITH_PREFIX2}'s {B_LAST_ITEM}!"), [STRINGID_TARGETCANTESCAPENOW] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} can no longer escape!"), [STRINGID_PKMNFELLINTONIGHTMARE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} began having a nightmare!"), [STRINGID_PKMNLOCKEDINNIGHTMARE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is locked in a nightmare!"), diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ca7f490f11..face6eeb08 100755 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2831,11 +2831,11 @@ static void CheckSetUnburden(u8 battler) gDisableStructs[battler].unburdenActive = TRUE; } -// battlerStealer steals the item of battlerItem -void StealTargetItem(u8 battlerStealer, u8 battlerItem) +// battlerStealer steals the item of itemBattler +void StealTargetItem(u8 battlerStealer, u8 itemBattler) { - gLastUsedItem = gBattleMons[battlerItem].item; - gBattleMons[battlerItem].item = ITEM_NONE; + gLastUsedItem = gBattleMons[itemBattler].item; + gBattleMons[itemBattler].item = ITEM_NONE; if (GetGenConfig(GEN_STEAL_WILD_ITEMS) >= GEN_9 && !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE)) @@ -2854,16 +2854,16 @@ void StealTargetItem(u8 battlerStealer, u8 battlerItem) MarkBattlerForControllerExec(battlerStealer); } - RecordItemEffectBattle(battlerItem, ITEM_NONE); - CheckSetUnburden(battlerItem); + RecordItemEffectBattle(itemBattler, ITEM_NONE); + CheckSetUnburden(itemBattler); - BtlController_EmitSetMonData(battlerItem, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[battlerItem].item); // remove target item - MarkBattlerForControllerExec(battlerItem); + BtlController_EmitSetMonData(itemBattler, B_COMM_TO_CONTROLLER, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[itemBattler].item), &gBattleMons[itemBattler].item); // remove target item + MarkBattlerForControllerExec(itemBattler); - if (GetBattlerAbility(gBattlerTarget) != ABILITY_GORILLA_TACTICS) - gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE; + if (GetBattlerAbility(itemBattler) != ABILITY_GORILLA_TACTICS) + gBattleStruct->choicedMove[itemBattler] = MOVE_NONE; - TrySaveExchangedItem(battlerItem, gLastUsedItem); + TrySaveExchangedItem(itemBattler, gLastUsedItem); } static inline bool32 TrySetReflect(u32 battler) @@ -5597,22 +5597,54 @@ static bool32 HandleMoveEndAbilityBlock(u32 battlerAtk, u32 battlerDef, u32 move switch (abilityAtk) { case ABILITY_MAGICIAN: - if (move != MOVE_FLING && move != MOVE_NATURAL_GIFT + if (GetMoveEffect(move) != EFFECT_FLING + && GetMoveEffect(move) != EFFECT_NATURAL_GIFT && gBattleMons[battlerAtk].item == ITEM_NONE - && gBattleMons[battlerDef].item != ITEM_NONE && IsBattlerAlive(battlerAtk) - && IsBattlerTurnDamaged(battlerDef) - && CanStealItem(battlerAtk, battlerDef, gBattleMons[battlerDef].item) - && !gSpecialStatuses[battlerAtk].gemBoost // In base game, gems are consumed after magician would activate. - && !(gWishFutureKnock.knockedOffMons[GetBattlerSide(battlerDef)] & (1u << gBattlerPartyIndexes[battlerDef])) - && !DoesSubstituteBlockMove(battlerAtk, battlerDef, move) - && (GetBattlerAbility(battlerDef) != ABILITY_STICKY_HOLD || !IsBattlerAlive(battlerDef))) + && !gSpecialStatuses[battlerAtk].gemBoost) // In base game, gems are consumed after magician would activate. { - StealTargetItem(battlerAtk, battlerDef); - gBattleScripting.battler = gBattlerAbility = battlerAtk; - gEffectBattler = battlerDef; - BattleScriptCall(BattleScript_MagicianActivates); - effect = TRUE; + u32 numMagicianTargets = 0; + u32 magicianTargets = 0; + + for (u32 i = 0; i < gBattlersCount; i++) + { + if (gBattleMons[i].item != ITEM_NONE + && i != battlerAtk + && IsBattlerTurnDamaged(i) + && CanStealItem(battlerAtk, i, gBattleMons[i].item) + && !(gWishFutureKnock.knockedOffMons[GetBattlerSide(i)] & (1u << gBattlerPartyIndexes[i])) + && !DoesSubstituteBlockMove(battlerAtk, i, move) + && (GetBattlerAbility(i) != ABILITY_STICKY_HOLD || !IsBattlerAlive(i))) + { + magicianTargets |= 1u << i; + numMagicianTargets++; + } + } + + if (numMagicianTargets == 0) + { + effect = FALSE; + break; + } + + u8 battlers[4] = {0, 1, 2, 3}; + if (numMagicianTargets > 1) + SortBattlersBySpeed(battlers, FALSE); + + for (u32 i = 0; i < gBattlersCount; i++) + { + u32 battler = battlers[i]; + + if (!(magicianTargets & 1u << battler)) + continue; + + StealTargetItem(battlerAtk, battler); + gBattlerAbility = battlerAtk; + gEffectBattler = battler; + BattleScriptCall(BattleScript_MagicianActivates); + effect = TRUE; + break; // found target to steal from + } } break; case ABILITY_MOXIE: @@ -5775,6 +5807,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect) gBattleMons[gBattlerAttacker].item = ITEM_NONE; // Item assigned later on with thief (see MOVEEND_CHANGED_ITEMS) gBattleStruct->changedItems[gBattlerAttacker] = gLastUsedItem; // Stolen item to be assigned later } + gEffectBattler = gBattlerTarget; BattleScriptCall(BattleScript_ItemSteal); effect = TRUE; } diff --git a/test/battle/ability/magician.c b/test/battle/ability/magician.c index 9e80120025..a54ba2df7d 100644 --- a/test/battle/ability/magician.c +++ b/test/battle/ability/magician.c @@ -25,3 +25,31 @@ SINGLE_BATTLE_TEST("Magician gets self-damage recoil after stealing Life Orb") } } +DOUBLE_BATTLE_TEST("Magician steal the item from the fastest possible target") +{ + u32 playerRightSpeed = 0; + u32 opponentLeftSpeed = 0; + u32 opponentRightSpeed = 0; + + PARAMETRIZE { playerRightSpeed = 4; opponentLeftSpeed = 2; opponentRightSpeed = 3; } + PARAMETRIZE { playerRightSpeed = 3; opponentLeftSpeed = 4; opponentRightSpeed = 2; } + PARAMETRIZE { playerRightSpeed = 2; opponentLeftSpeed = 3; opponentRightSpeed = 4; } + + GIVEN { + PLAYER(SPECIES_DELPHOX) { Speed(1); Ability(ABILITY_MAGICIAN); Item(ITEM_NONE); } + PLAYER(SPECIES_WOBBUFFET) { Speed(playerRightSpeed); Item(ITEM_POKE_BALL); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(opponentLeftSpeed); Item(ITEM_GREAT_BALL); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(opponentRightSpeed); Item(ITEM_ULTRA_BALL); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_SURF); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_MAGICIAN); + } THEN { + if (playerRightSpeed == 4) + EXPECT(playerLeft->item == ITEM_POKE_BALL); + else if (opponentLeftSpeed == 4) + EXPECT(playerLeft->item == ITEM_GREAT_BALL); + else if (playerRightSpeed == 4) + EXPECT(playerLeft->item == ITEM_ULTRA_BALL); + } +} From 3e583f8addacf89f8e0d8c99b6bfdfe21c6ae27b Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Sun, 9 Nov 2025 18:10:44 +0100 Subject: [PATCH 128/130] Fix tera tint not applying on activation (#8135) --- data/battle_anim_scripts.s | 18 +++++++++--------- include/battle_gfx_sfx_util.h | 2 +- include/constants/battle_anim.h | 8 ++++++++ src/battle_anim_effects_3.c | 16 +++------------- src/battle_gfx_sfx_util.c | 13 ++++++++++--- 5 files changed, 31 insertions(+), 26 deletions(-) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 61d0d99933..4df85854dc 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -29002,7 +29002,7 @@ gBattleAnimMove_Transform:: monbg ANIM_ATTACKER playsewithpan SE_M_TELEPORT, SOUND_PAN_ATTACKER waitplaysewithpan SE_M_MINIMIZE, SOUND_PAN_ATTACKER, 48 - createvisualtask AnimTask_TransformMon, 2, 0, 1 + createvisualtask AnimTask_TransformMon, 2, SPECIES_GFX_CHANGE_TRANSFORM waitforvisualfinish clearmonbg ANIM_ATTACKER end @@ -31513,14 +31513,14 @@ gBattleAnimGeneral_SimpleHeal:: gBattleAnimGeneral_IllusionOff:: monbg ANIM_TARGET - createvisualtask AnimTask_TransformMon, 2, 1, 0 + createvisualtask AnimTask_TransformMon, 2, SPECIES_GFX_CHANGE_ILLUSION_OFF waitforvisualfinish clearmonbg ANIM_TARGET end gBattleAnimGeneral_FormChange:: monbg ANIM_ATTACKER - createvisualtask AnimTask_TransformMon, 2, 1, 0 + createvisualtask AnimTask_TransformMon, 2, SPECIES_GFX_CHANGE_FORM_CHANGE waitforvisualfinish clearmonbg ANIM_ATTACKER end @@ -31552,7 +31552,7 @@ gBattleAnimGeneral_MegaEvolution:: createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA waitforvisualfinish createvisualtask SoundTask_PlayNormalCry, 0 - createvisualtask AnimTask_HideSwapSprite, 2, 1, 0 + createvisualtask AnimTask_HideSwapSprite, 2 createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14 createsprite gMegaSymbolSpriteTemplate ANIM_ATTACKER, 3, 0, 0, ANIM_ATTACKER @@ -31637,7 +31637,7 @@ gBattleAnimGeneral_TeraActivate:: createvisualtask AnimTask_SetOpponentShadowCallbacks, 2 @ Restore shadows hidden in the charge script loadspritegfx ANIM_TAG_TERA_SYMBOL loadspritegfx ANIM_TAG_SPARKLE_6 - createvisualtask AnimTask_HideSwapSprite, 2, 1, 0 + createvisualtask AnimTask_HideSwapSprite, 2 createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14 createvisualtask SoundTask_PlayNormalCry, 0 @@ -31777,7 +31777,7 @@ General_PrimalReversion_Alpha: delay 20 createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA waitforvisualfinish - createvisualtask AnimTask_HideSwapSprite, 2, 1, 0 + createvisualtask AnimTask_HideSwapSprite, 2 createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14 createvisualtask SoundTask_PlayNormalCry, 0 @@ -31810,7 +31810,7 @@ General_PrimalReversion_Omega: delay 20 createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA waitforvisualfinish - createvisualtask AnimTask_HideSwapSprite, 2, 1, 0 + createvisualtask AnimTask_HideSwapSprite, 2 createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14 createvisualtask SoundTask_PlayNormalCry, 0 @@ -31850,7 +31850,7 @@ gBattleAnimGeneral_PowerConstruct:: delay 20 createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA waitforvisualfinish - createvisualtask AnimTask_HideSwapSprite, 2, 1, 0 + createvisualtask AnimTask_HideSwapSprite, 2 createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14 createvisualtask SoundTask_PlayNormalCry, 0 @@ -31920,7 +31920,7 @@ gBattleAnimGeneral_UltraBurst:: createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA createsprite gUltraBurstSymbolSpriteTemplate, ANIM_ATTACKER, 0x0, 0x0, 0x0, 0x0, 0x0 waitforvisualfinish - createvisualtask AnimTask_HideSwapSprite, 2, 1, 0 + createvisualtask AnimTask_HideSwapSprite, 2 createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14 createvisualtask SoundTask_PlayNormalCry, 0 diff --git a/include/battle_gfx_sfx_util.h b/include/battle_gfx_sfx_util.h index ae71eb5b50..eb713779c2 100644 --- a/include/battle_gfx_sfx_util.h +++ b/include/battle_gfx_sfx_util.h @@ -22,7 +22,7 @@ bool8 BattleInitAllSprites(u8 *state1, u8 *battler); void ClearSpritesHealthboxAnimData(void); void CopyAllBattleSpritesInvisibilities(void); void CopyBattleSpriteInvisibility(u8 battler); -void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bool8 trackEnemyPersonality); +void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, u8 changeType); void BattleLoadSubstituteOrMonSpriteGfx(u8 battler, bool8 loadMonSprite); void LoadBattleMonGfxAndAnimate(u8 battler, bool8 loadMonSprite, u8 spriteId); void TrySetBehindSubstituteSpriteBit(u8 battler, u16 move); diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index 3d514b1936..74683a70b6 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -667,6 +667,14 @@ #define ANIM_ORDER_UP_DROOPY 2 #define ANIM_ORDER_UP_STRETCHY 3 +// AnimTask_TransformMon variations +enum SpeciesGfxChange +{ + SPECIES_GFX_CHANGE_TRANSFORM, + SPECIES_GFX_CHANGE_FORM_CHANGE, + SPECIES_GFX_CHANGE_ILLUSION_OFF, +}; + // 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_effects_3.c b/src/battle_anim_effects_3.c index 65de36237b..e30a8afd15 100644 --- a/src/battle_anim_effects_3.c +++ b/src/battle_anim_effects_3.c @@ -2484,11 +2484,10 @@ void AnimTask_HideSwapSprite(u8 taskId) case 0: gTasks[taskId].data[11] = gSprites[spriteId].x; // Save battler position gSprites[spriteId].x = -64; // hide it from screen to avoid the blip/glitch effect when swapping the sprite. - gTasks[taskId].data[10] = gBattleAnimArgs[0]; gTasks[taskId].data[0]++; break; case 1: - HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, gTasks[taskId].data[10], gBattleAnimArgs[1]); + HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, SPECIES_GFX_CHANGE_FORM_CHANGE); GetBgDataForTransform(&animBg, gBattleAnimAttacker); if (IsContest()) @@ -2536,14 +2535,6 @@ void AnimTask_HideSwapSprite(u8 taskId) break; case 2: gSprites[spriteId].x = gTasks[taskId].data[11]; // restores battler position - if (!IsContest()) - { - if (!IsOnPlayerSide(gBattleAnimAttacker)) - { - if (gTasks[taskId].data[10] == 0) - SetBattlerShadowSpriteCallback(gBattleAnimAttacker, gBattleSpritesDataPtr->battlerData[gBattleAnimAttacker].transformSpecies); - } - } DestroyAnimVisualTask(taskId); break; @@ -2596,7 +2587,6 @@ void AnimTask_TransformMon(u8 taskId) SetAnimBgAttribute(2, BG_ANIM_MOSAIC, 1); gTasks[taskId].data[10] = gBattleAnimArgs[0]; - gTasks[taskId].data[11] = gBattleAnimArgs[1]; gTasks[taskId].data[0]++; break; case 1: @@ -2611,7 +2601,7 @@ void AnimTask_TransformMon(u8 taskId) } break; case 2: - HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, gTasks[taskId].data[10], gTasks[taskId].data[11]); + HandleSpeciesGfxDataChange(gBattleAnimAttacker, gBattleAnimTarget, gTasks[taskId].data[10]); GetBgDataForTransform(&animBg, gBattleAnimAttacker); if (IsContest()) @@ -2680,7 +2670,7 @@ void AnimTask_TransformMon(u8 taskId) { if (!IsOnPlayerSide(gBattleAnimAttacker)) { - if (gTasks[taskId].data[10] == 0) + if (gTasks[taskId].data[10] == SPECIES_GFX_CHANGE_TRANSFORM) SetBattlerShadowSpriteCallback(gBattleAnimAttacker, gBattleSpritesDataPtr->battlerData[gBattleAnimAttacker].transformSpecies); } } diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 1beaee4cc4..a6362bb24f 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -906,7 +906,7 @@ void CopyBattleSpriteInvisibility(u8 battler) gBattleSpritesDataPtr->battlerData[battler].invisible = gSprites[gBattlerSpriteIds[battler]].invisible; } -void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bool8 trackEnemyPersonality) +void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, u8 changeType) { u32 personalityValue, position, paletteOffset, targetSpecies; bool32 isShiny; @@ -947,7 +947,7 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo targetSpecies = gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies; } - if (trackEnemyPersonality) + if (changeType == SPECIES_GFX_CHANGE_TRANSFORM) { personalityValue = gDisableStructs[battlerAtk].transformedMonPersonality; isShiny = gDisableStructs[battlerAtk].transformedMonShininess; @@ -969,7 +969,7 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo paletteData = GetMonSpritePalFromSpeciesAndPersonality(targetSpecies, isShiny, personalityValue); LoadPalette(paletteData, paletteOffset, PLTT_SIZE_4BPP); - if (!megaEvo) + if (changeType == SPECIES_GFX_CHANGE_TRANSFORM) { BlendPalette(paletteOffset, 16, 6, RGB_WHITE); CpuCopy32(&gPlttBufferFaded[paletteOffset], &gPlttBufferUnfaded[paletteOffset], PLTT_SIZEOF(16)); @@ -986,6 +986,13 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16)); } + // Terastallization's tint + if (changeType != SPECIES_GFX_CHANGE_ILLUSION_OFF && GetActiveGimmick(battlerAtk) == GIMMICK_TERA) + { + BlendPalette(paletteOffset, 16, 8, GetTeraTypeRGB(GetBattlerTeraType(battlerAtk))); + CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16)); + } + gSprites[gBattlerSpriteIds[battlerAtk]].y = GetBattlerSpriteDefault_Y(battlerAtk); StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerAtk]], 0); } From 8eea132406f53e5857d1eec72181867b469bddfc Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Sun, 9 Nov 2025 18:16:19 +0100 Subject: [PATCH 129/130] Fix missing FREE_MATCH_CALL (#8171) Co-authored-by: Zimmermann Gyula --- src/battle_setup.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/battle_setup.c b/src/battle_setup.c index 1103818c05..1505f6af65 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -1128,8 +1128,12 @@ bool32 GetTrainerFlagFromScriptPointer(const u8 *data) bool32 GetRematchFromScriptPointer(const u8 *data) { +#if FREE_MATCH_CALL + return FALSE; +#else TrainerBattleParameter *temp = (TrainerBattleParameter*)(data + OPCODE_OFFSET); return ShouldTryRematchBattleForTrainerId(temp->params.opponentA); +#endif } #undef OPCODE_OFFSET @@ -1793,11 +1797,13 @@ static bool8 WasSecondRematchWon(const struct RematchTrainer *table, u16 firstBa return FALSE; if (!HasTrainerBeenFought(table[tableId].trainerIds[1])) return FALSE; +#if FREE_MATCH_CALL == FALSE if (I_VS_SEEKER_CHARGING) { if (gSaveBlock1Ptr->trainerRematches[tableId] == 0) return FALSE; } +#endif return TRUE; } From 459b3cbb2f3a8dba7f21f2d781a040bf4d61cb6a Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Sun, 9 Nov 2025 22:21:55 +0100 Subject: [PATCH 130/130] Adjust label workflow to only run if PR is approved (#8183) Co-authored-by: Hedara --- .github/workflows/labels.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index 325e72950d..3cc4ee57a7 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -3,10 +3,12 @@ name: Labels on: pull_request: types: [opened, synchronize, labeled, unlabeled] + pull_request_review: + types: [submitted] jobs: label: - if: github.actor != 'allcontributors[bot]' + if: ${{ github.actor != 'allcontributors[bot]' && github.event.review.state == 'approved' }} runs-on: ubuntu-latest steps: - name: check labels