From bef343cefa8834c36c60a31ce9848c276523db07 Mon Sep 17 00:00:00 2001 From: Abaresk Date: Wed, 21 May 2025 16:02:12 -0400 Subject: [PATCH 01/56] Remove berry yield bug fix --- src/berry.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/berry.c b/src/berry.c index 543041e526..c56b91ff03 100644 --- a/src/berry.c +++ b/src/berry.c @@ -1204,15 +1204,9 @@ static u8 GetNumStagesWateredByBerryTreeId(u8 id) return BerryTreeGetNumStagesWatered(GetBerryTreeInfo(id)); } -// Berries can be watered at 4 stages of growth. This function is likely meant -// to divide the berry yield range equally into quartiles. If you watered the -// tree n times, your yield is a random number in the nth quartile. -// -// However, this function actually skews towards higher berry yields, because -// it rounds `extraYield` to the nearest whole number. -// -// See resulting yields: https://gist.github.com/hondew/2a099dbe54aa91414decdbfaa524327d, -// and bug fix: https://gist.github.com/hondew/0f0164e5b9dadfd72d24f30f2c049a0b. +// Berries can be watered at 4 stages of growth. The distribution is largely +// even but slightly prefers middle berry yields, since it uniformly draws from +// a subset of the total yield range. static u8 CalcBerryYieldInternal(u16 max, u16 min, u8 water) { u32 randMin; From d33651bfd0e16bbeb8084dfa8cd84178386c5f9b Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 2 Jun 2025 14:22:11 +0200 Subject: [PATCH 02/56] Battle controller pret documentation (#7029) --- include/battle_controllers.h | 70 +++++++++++++++-------- src/battle_controller_link_opponent.c | 4 +- src/battle_controller_link_partner.c | 4 +- src/battle_controller_opponent.c | 4 +- src/battle_controller_player.c | 4 +- src/battle_controller_player_partner.c | 4 +- src/battle_controller_recorded_opponent.c | 4 +- src/battle_controller_recorded_player.c | 4 +- src/battle_controller_safari.c | 4 +- src/battle_controller_wally.c | 4 +- src/battle_controllers.c | 4 +- src/battle_main.c | 18 +++--- src/battle_util.c | 16 +++--- 13 files changed, 81 insertions(+), 63 deletions(-) diff --git a/include/battle_controllers.h b/include/battle_controllers.h index 8f2d78ed41..2ce28ca609 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -72,41 +72,63 @@ enum { // (e.g. MarkBattlerForControllerExec) instead of using these macros // directly. -#define MARK_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battler) \ - gBattleControllerExecFlags |= (1u << battler) +static inline void MarkBattleControllerActiveOnLocal(u32 battler) +{ + gBattleControllerExecFlags |= (1u << battler); +} -#define MARK_BATTLE_CONTROLLER_IDLE_ON_LOCAL(battler) \ - gBattleControllerExecFlags &= ~(1u << battler) +static inline void MarkBattleControllerIdleOnLocal(u32 battler) +{ + gBattleControllerExecFlags &= ~(1u << battler); +} -#define IS_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battler) \ - (gBattleControllerExecFlags & (1u << battler)) +static inline bool32 IsBattleControllerActiveOnLocal(u32 battler) +{ + return gBattleControllerExecFlags & (1u << battler); +} -#define MARK_BATTLE_CONTROLLER_MESSAGE_OUTBOUND_OVER_LINK(battler) \ - gBattleControllerExecFlags |= ((1u << battler) << (32 - MAX_BATTLERS_COUNT)) +static inline void MarkBattleControllerMessageOutboundOverLink(u32 battler) +{ + gBattleControllerExecFlags |= ((1u << battler) << (32 - MAX_BATTLERS_COUNT)); +} -#define MARK_BATTLE_CONTROLLER_MESSAGE_SYNCHRONIZED_OVER_LINK(battler) \ - gBattleControllerExecFlags &= ~((1 << 28) << (battler)) +static inline void MarkBattleControllerMessageSynchronizedOverLink(u32 battler) +{ + gBattleControllerExecFlags &= ~((1 << 28) << (battler)); +} -#define MARK_BATTLE_CONTROLLER_ACTIVE_FOR_PLAYER(battler, playerId) \ - gBattleControllerExecFlags |= ((1u << battler) << ((playerId) << 2)) +static inline bool32 IsBattleControllerMessageSynchronizedOverLink(u32 battler) +{ + return gBattleControllerExecFlags & (1u << (battler + 28)); +} -#define MARK_BATTLE_CONTROLLER_IDLE_FOR_PLAYER(battler, playerId) \ - gBattleControllerExecFlags &= ~((1u << battler) << ((playerId) * 4)) +static inline void MarkBattleControllerActiveForPlayer(u32 battler, u32 playerId) +{ + gBattleControllerExecFlags |= ((1u << battler) << ((playerId) << 2)); +} -#define IS_BATTLE_CONTROLLER_ACTIVE_FOR_PLAYER(battler, playerId) \ - (gBattleControllerExecFlags & ((1u << battler) << ((playerId) * 4))) +static inline void MarkBattleControllerIdleForPlayer(u32 battler, u32 playerId) +{ + gBattleControllerExecFlags &= ~((1u << battler) << ((playerId) * 4)); +} + +static inline bool32 IsBattleControllerActiveForPlayer(u32 battler, u32 playerId) +{ + return gBattleControllerExecFlags & ((1u << battler) << ((playerId) * 4)); +} // This actually checks if a specific controller is active on any player or if // *any* controller is pending sync over link communications, but the macro name // can only be so specific before it just gets ridiculous. -#define IS_BATTLE_CONTROLLER_ACTIVE_OR_PENDING_SYNC_ANYWHERE(battler) \ - (gBattleControllerExecFlags & ( \ - (1u << battler) \ - | (0xF << 28) \ - | (1u << battler << 4) \ - | (1u << battler << 8) \ - | (1u << battler << 12) \ - )) +static inline bool32 IsBattleControllerActiveOrPendingSyncAnywhere(u32 battler) +{ + return gBattleControllerExecFlags & ( + (1u << battler) + | (0xF << 28) + | (1u << battler << 4) + | (1u << battler << 8) + | (1u << battler << 12)); +} // Special arguments for Battle Controller functions. diff --git a/src/battle_controller_link_opponent.c b/src/battle_controller_link_opponent.c index c79588695d..a039a5b522 100644 --- a/src/battle_controller_link_opponent.c +++ b/src/battle_controller_link_opponent.c @@ -114,7 +114,7 @@ void SetControllerToLinkOpponent(u32 battler) static void LinkOpponentBufferRunCommand(u32 battler) { - if (gBattleControllerExecFlags & (1u << battler)) + if (IsBattleControllerActiveOnLocal(battler)) { if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sLinkOpponentBufferCommands)) sLinkOpponentBufferCommands[gBattleResources->bufferA[battler][0]](battler); @@ -320,7 +320,7 @@ static void LinkOpponentBufferExecCompleted(u32 battler) } else { - gBattleControllerExecFlags &= ~(1u << battler); + MarkBattleControllerIdleOnLocal(battler); } } diff --git a/src/battle_controller_link_partner.c b/src/battle_controller_link_partner.c index f54ee0d220..9b27309bf6 100644 --- a/src/battle_controller_link_partner.c +++ b/src/battle_controller_link_partner.c @@ -113,7 +113,7 @@ void SetControllerToLinkPartner(u32 battler) static void LinkPartnerBufferRunCommand(u32 battler) { - if (gBattleControllerExecFlags & (1u << battler)) + if (IsBattleControllerActiveOnLocal(battler)) { if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sLinkPartnerBufferCommands)) sLinkPartnerBufferCommands[gBattleResources->bufferA[battler][0]](battler); @@ -164,7 +164,7 @@ static void LinkPartnerBufferExecCompleted(u32 battler) } else { - gBattleControllerExecFlags &= ~(1u << battler); + MarkBattleControllerIdleOnLocal(battler); } } diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 03c78fb9e9..f6fae5ae57 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -127,7 +127,7 @@ void SetControllerToOpponent(u32 battler) static void OpponentBufferRunCommand(u32 battler) { - if (gBattleControllerExecFlags & (1u << battler)) + if (IsBattleControllerActiveOnLocal(battler)) { if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sOpponentBufferCommands)) sOpponentBufferCommands[gBattleResources->bufferA[battler][0]](battler); @@ -364,7 +364,7 @@ static void OpponentBufferExecCompleted(u32 battler) } else { - gBattleControllerExecFlags &= ~(1u << battler); + MarkBattleControllerIdleOnLocal(battler); } } diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index f374345206..f437d8ee13 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -182,13 +182,13 @@ static void PlayerBufferExecCompleted(u32 battler) } else { - gBattleControllerExecFlags &= ~(1u << battler); + MarkBattleControllerIdleOnLocal(battler); } } static void PlayerBufferRunCommand(u32 battler) { - if (gBattleControllerExecFlags & (1u << battler)) + if (IsBattleControllerActiveOnLocal(battler)) { if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sPlayerBufferCommands)) sPlayerBufferCommands[gBattleResources->bufferA[battler][0]](battler); diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index ad5aa15312..9929f421cc 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -117,7 +117,7 @@ void SetControllerToPlayerPartner(u32 battler) static void PlayerPartnerBufferRunCommand(u32 battler) { - if (gBattleControllerExecFlags & (1u << battler)) + if (IsBattleControllerActiveOnLocal(battler)) { if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sPlayerPartnerBufferCommands)) sPlayerPartnerBufferCommands[gBattleResources->bufferA[battler][0]](battler); @@ -236,7 +236,7 @@ static void PlayerPartnerBufferExecCompleted(u32 battler) } else { - gBattleControllerExecFlags &= ~(1u << battler); + MarkBattleControllerIdleOnLocal(battler); } } diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c index 1e471044d5..944cf6e072 100644 --- a/src/battle_controller_recorded_opponent.c +++ b/src/battle_controller_recorded_opponent.c @@ -120,7 +120,7 @@ void SetControllerToRecordedOpponent(u32 battler) static void RecordedOpponentBufferRunCommand(u32 battler) { - if (gBattleControllerExecFlags & (1u << battler)) + if (IsBattleControllerActiveOnLocal(battler)) { if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sRecordedOpponentBufferCommands)) sRecordedOpponentBufferCommands[gBattleResources->bufferA[battler][0]](battler); @@ -141,7 +141,7 @@ static void RecordedOpponentBufferExecCompleted(u32 battler) } else { - gBattleControllerExecFlags &= ~(1u << battler); + MarkBattleControllerIdleOnLocal(battler); } } diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index 2476929282..a3032db1a8 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -117,7 +117,7 @@ void SetControllerToRecordedPlayer(u32 battler) static void RecordedPlayerBufferRunCommand(u32 battler) { - if (gBattleControllerExecFlags & (1u << battler)) + if (IsBattleControllerActiveOnLocal(battler)) { if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sRecordedPlayerBufferCommands)) sRecordedPlayerBufferCommands[gBattleResources->bufferA[battler][0]](battler); @@ -313,7 +313,7 @@ static void RecordedPlayerBufferExecCompleted(u32 battler) } else { - gBattleControllerExecFlags &= ~(1u << battler); + MarkBattleControllerIdleOnLocal(battler); } } diff --git a/src/battle_controller_safari.c b/src/battle_controller_safari.c index 560a95cc5f..4a1fc3888c 100644 --- a/src/battle_controller_safari.c +++ b/src/battle_controller_safari.c @@ -109,7 +109,7 @@ void SetControllerToSafari(u32 battler) static void SafariBufferRunCommand(u32 battler) { - if (gBattleControllerExecFlags & (1u << battler)) + if (IsBattleControllerActiveOnLocal(battler)) { if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sSafariBufferCommands)) sSafariBufferCommands[gBattleResources->bufferA[battler][0]](battler); @@ -237,7 +237,7 @@ static void SafariBufferExecCompleted(u32 battler) } else { - gBattleControllerExecFlags &= ~(1u << battler); + MarkBattleControllerIdleOnLocal(battler); } } diff --git a/src/battle_controller_wally.c b/src/battle_controller_wally.c index 3381e5a5bd..6cba0fc3e7 100644 --- a/src/battle_controller_wally.c +++ b/src/battle_controller_wally.c @@ -125,7 +125,7 @@ void SetControllerToWally(u32 battler) static void WallyBufferRunCommand(u32 battler) { - if (gBattleControllerExecFlags & (1u << battler)) + if (IsBattleControllerActiveOnLocal(battler)) { if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sWallyBufferCommands)) sWallyBufferCommands[gBattleResources->bufferA[battler][0]](battler); @@ -284,7 +284,7 @@ static void WallyBufferExecCompleted(u32 battler) } else { - gBattleControllerExecFlags &= ~(1u << battler); + MarkBattleControllerIdleOnLocal(battler); } } diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 9b6a74fc6e..4faec49613 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -956,7 +956,7 @@ static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId) switch (BYTE_TO_RECEIVE(0)) { case B_COMM_TO_CONTROLLER: - if (IS_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battler)) + if (IsBattleControllerActiveOnLocal(battler)) return; memcpy(gBattleResources->bufferA[battler], &BYTE_TO_RECEIVE(LINK_BUFF_DATA), blockSize); @@ -975,7 +975,7 @@ static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId) break; case B_COMM_CONTROLLER_IS_DONE: playerId = BYTE_TO_RECEIVE(LINK_BUFF_DATA); - MARK_BATTLE_CONTROLLER_IDLE_FOR_PLAYER(battler, playerId); + MarkBattleControllerIdleForPlayer(battler, playerId); break; } diff --git a/src/battle_main.c b/src/battle_main.c index d1bc8ed0c8..8ea21a023d 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4210,7 +4210,7 @@ static void HandleTurnActionSelectionState(void) } break; case STATE_WAIT_ACTION_CHOSEN: // Try to perform an action. - if (!(gBattleControllerExecFlags & (((1u << battler)) | (0xF << 28) | ((1u << battler) << 4) | ((1u << battler) << 8) | ((1u << battler) << 12)))) + if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler)) { RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][1]); gChosenActionByBattler[battler] = gBattleResources->bufferB[battler][1]; @@ -4414,7 +4414,7 @@ static void HandleTurnActionSelectionState(void) } break; case STATE_WAIT_ACTION_CASE_CHOSEN: - if (!(gBattleControllerExecFlags & (((1u << battler)) | (0xF << 28) | ((1u << battler) << 4) | ((1u << battler) << 8) | ((1u << battler) << 12)))) + if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler)) { switch (gChosenActionByBattler[battler]) { @@ -4544,11 +4544,7 @@ static void HandleTurnActionSelectionState(void) } break; case STATE_WAIT_ACTION_CONFIRMED_STANDBY: - if (!(gBattleControllerExecFlags & ((1u << battler) - | (0xF << 28) - | (1u << (battler + 4)) - | (1u << (battler + 8)) - | (1u << (battler + 12))))) + if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler)) { if (AllAtActionConfirmed()) i = TRUE; @@ -4570,7 +4566,7 @@ static void HandleTurnActionSelectionState(void) } break; case STATE_WAIT_ACTION_CONFIRMED: - if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12))))) + if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler)) { gBattleCommunication[ACTIONS_CONFIRMED_COUNT]++; } @@ -4584,7 +4580,7 @@ static void HandleTurnActionSelectionState(void) { gBattlerAttacker = battler; gBattlescriptCurrInstr = gSelectionBattleScripts[battler]; - if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12))))) + if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler)) { gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]](); } @@ -4592,7 +4588,7 @@ static void HandleTurnActionSelectionState(void) } break; case STATE_WAIT_SET_BEFORE_ACTION: - if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12))))) + if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler)) { gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN; } @@ -4616,7 +4612,7 @@ static void HandleTurnActionSelectionState(void) { gBattlerAttacker = battler; gBattlescriptCurrInstr = gSelectionBattleScripts[battler]; - if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12))))) + if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler)) { gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]](); } diff --git a/src/battle_util.c b/src/battle_util.c index 6d256dc998..3bf809d530 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -988,29 +988,29 @@ static void UNUSED MarkAllBattlersForControllerExec(void) if (gBattleTypeFlags & BATTLE_TYPE_LINK) { for (i = 0; i < gBattlersCount; i++) - gBattleControllerExecFlags |= 1u << (i + 32 - MAX_BATTLERS_COUNT); + MarkBattleControllerMessageOutboundOverLink(i); } else { for (i = 0; i < gBattlersCount; i++) - gBattleControllerExecFlags |= 1u << i; + MarkBattleControllerActiveOnLocal(i); } } bool32 IsBattlerMarkedForControllerExec(u32 battler) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) - return (gBattleControllerExecFlags & (1u << (battler + 28))) != 0; + return IsBattleControllerMessageSynchronizedOverLink(battler); else - return (gBattleControllerExecFlags & (1u << battler)) != 0; + return IsBattleControllerActiveOnLocal(battler); } void MarkBattlerForControllerExec(u32 battler) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) - gBattleControllerExecFlags |= 1u << (battler + 32 - MAX_BATTLERS_COUNT); + MarkBattleControllerMessageOutboundOverLink(battler); else - gBattleControllerExecFlags |= 1u << battler; + MarkBattleControllerActiveOnLocal(battler); } void MarkBattlerReceivedLinkData(u32 battler) @@ -1018,9 +1018,9 @@ void MarkBattlerReceivedLinkData(u32 battler) s32 i; for (i = 0; i < GetLinkPlayerCount(); i++) - gBattleControllerExecFlags |= 1u << (battler + (i << 2)); + MarkBattleControllerActiveForPlayer(battler, i); - gBattleControllerExecFlags &= ~(1u << (28 + battler)); + MarkBattleControllerMessageSynchronizedOverLink(battler); } const u8 *CheckSkyDropState(u32 battler, enum SkyDropState skyDropState) From 7263bb188600411d20bc8fca1e5c3aa1315fc69c Mon Sep 17 00:00:00 2001 From: psf <77138753+pkmnsnfrn@users.noreply.github.com> Date: Mon, 2 Jun 2025 06:41:05 -0700 Subject: [PATCH 03/56] Update CI to ignore allcontributors commits (#7046) --- .all-contributorsrc | 3 +-- .github/workflows/build.yml | 9 +++++++++ .github/workflows/labels.yml | 9 +++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index e5ecce9602..540d534f0b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -259,6 +259,5 @@ ], "contributorsPerLine": 7, "linkToUsage": true, - "commitType": "docs", - "skipCi": true + "commitType": "docs" } diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c3040f55c7..3304101d4f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,6 +9,7 @@ on: jobs: build: + if: github.actor != 'allcontributors' runs-on: ubuntu-latest env: GAME_VERSION: EMERALD @@ -36,3 +37,11 @@ jobs: TEST: 1 run: | make -j${nproc} check + allcontributors: + if: github.actor == 'allcontributors' + runs-on: ubuntu-latest + needs: [] + steps: + - name: Automatically pass for allcontributors + run: echo "CI automatically passes for allcontributors" && exit 0 + diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index 85800211ef..7315082cb6 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -6,6 +6,7 @@ on: jobs: label: + if: github.actor != 'allcontributors' runs-on: ubuntu-latest steps: - name: check labels @@ -27,3 +28,11 @@ jobs: category: pokemon category: sprite-issue type: documentation + allcontributors: + if: github.actor == 'allcontributors' + runs-on: ubuntu-latest + needs: [] + steps: + - name: Automatically pass for allcontributors + run: echo "CI automatically passes for allcontributors" && exit 0 + From 0fc900514b9114d9b77183ec5810501d49cbdae1 Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Mon, 2 Jun 2025 22:51:26 +0200 Subject: [PATCH 04/56] sync list menu functions with firered --- include/list_menu.h | 26 ++++++- src/battle_pyramid_bag.c | 2 +- src/item_menu.c | 2 +- src/list_menu.c | 163 +++++++++++++++++---------------------- src/player_pc.c | 2 +- 5 files changed, 97 insertions(+), 98 deletions(-) diff --git a/include/list_menu.h b/include/list_menu.h index f2f7a2b3dd..ba04ac1cd9 100644 --- a/include/list_menu.h +++ b/include/list_menu.h @@ -27,6 +27,28 @@ enum { SCROLL_ARROW_DOWN }; +// For ListMenuGet/SetTemplateField +enum ListMenuFields +{ + LISTFIELD_MOVECURSORFUNC = 0, + LISTFIELD_MOVECURSORFUNC2, + LISTFIELD_TOTALITEMS, + LISTFIELD_MAXSHOWED, + LISTFIELD_WINDOWID, + LISTFIELD_HEADERX, + LISTFIELD_ITEMX, + LISTFIELD_CURSORX, + LISTFIELD_UPTEXTY, + LISTFIELD_CURSORPAL, + LISTFIELD_FILLVALUE, + LISTFIELD_CURSORSHADOWPAL, + LISTFIELD_LETTERSPACING, + LISTFIELD_ITEMVERTICALPADDING, + LISTFIELD_SCROLLMULTIPLE, + LISTFIELD_FONTID, + LISTFIELD_CURSORKIND, +}; + struct ListMenu; struct ListMenuItem @@ -120,8 +142,8 @@ void ListMenuGetScrollAndRow(u8 listTaskId, u16 *scrollOffset, u16 *selectedRow) u16 ListMenuGetYCoordForPrintingArrowCursor(u8 listTaskId); void ListMenuOverrideSetColors(u8 cursorPal, u8 fillValue, u8 cursorShadowPal); void ListMenuDefaultCursorMoveFunc(s32 itemIndex, bool8 onInit, struct ListMenu *list); -s32 ListMenuGetUnkIndicatorsStructFields(u8 taskId, u8 field); -void ListMenuSetUnkIndicatorsStructField(u8 taskId, u8 field, s32 value); +s32 ListMenuGetTemplateField(u8 taskId, u8 field); +void ListMenuSetTemplateField(u8 taskId, u8 field, s32 value); u8 AddScrollIndicatorArrowPair(const struct ScrollArrowsTemplate *arrowInfo, u16 *scrollOffset); u8 AddScrollIndicatorArrowPairParameterized(u32 arrowType, s32 commonPos, s32 firstPos, s32 secondPos, s32 fullyDownThreshold, s32 tileTag, s32 palTag, u16 *scrollOffset); void RemoveScrollIndicatorArrowPair(u8 taskId); diff --git a/src/battle_pyramid_bag.c b/src/battle_pyramid_bag.c index 1084dfacab..f50a2a1e82 100644 --- a/src/battle_pyramid_bag.c +++ b/src/battle_pyramid_bag.c @@ -1312,7 +1312,7 @@ static void Task_BeginItemSwap(u8 taskId) tListPos = gPyramidBagMenuState.scrollPosition + gPyramidBagMenuState.cursorPosition; gPyramidBagMenu->toSwapPos = tListPos; - ListMenuSetUnkIndicatorsStructField(tListTaskId, 0x10, 1); + ListMenuSetTemplateField(tListTaskId, 0x10, 1); CopyItemName(gSaveBlock2Ptr->frontier.pyramidBag.itemId[gSaveBlock2Ptr->frontier.lvlMode][tListPos], gStringVar1); StringExpandPlaceholders(gStringVar4, gText_MoveVar1Where); FillWindowPixelBuffer(WIN_INFO, PIXEL_FILL(0)); diff --git a/src/item_menu.c b/src/item_menu.c index 2c057f4447..5c75ca556c 100755 --- a/src/item_menu.c +++ b/src/item_menu.c @@ -1432,7 +1432,7 @@ static void StartItemSwap(u8 taskId) { s16 *data = gTasks[taskId].data; - ListMenuSetUnkIndicatorsStructField(tListTaskId, 16, 1); + ListMenuSetTemplateField(tListTaskId, 16, 1); tListPosition = gBagPosition.scrollPosition[gBagPosition.pocket] + gBagPosition.cursorPosition[gBagPosition.pocket]; gBagMenu->toSwapPos = tListPosition; CopyItemName(BagGetItemIdByPocketPosition(gBagPosition.pocket + 1, tListPosition), gStringVar1); diff --git a/src/list_menu.c b/src/list_menu.c index 91e34cd1b2..8257712a0a 100644 --- a/src/list_menu.c +++ b/src/list_menu.c @@ -17,29 +17,6 @@ // This allows them to have idle animations. Cursors prior to this are simply printed text. #define CURSOR_OBJECT_START CURSOR_RED_OUTLINE -struct UnkIndicatorsStruct -{ - u8 field_0; - u16 *field_4; - u16 field_8; - u16 field_A; - u16 field_C; - u16 field_E; - u8 field_10; - u8 field_11; - u8 field_12; - u8 field_13; - u8 field_14_0:4; - u8 field_14_1:4; - u8 field_15_0:4; - u8 field_15_1:4; - u8 field_16_0:3; - u8 field_16_1:3; - u8 field_16_2:2; - u8 field_17_0:6; - u8 field_17_1:2; -}; - struct ScrollIndicatorPair { u8 field_0; @@ -904,104 +881,104 @@ void ListMenuDefaultCursorMoveFunc(s32 itemIndex, bool8 onInit, struct ListMenu } // unused -s32 ListMenuGetUnkIndicatorsStructFields(u8 taskId, u8 field) +s32 ListMenuGetTemplateField(u8 taskId, u8 field) { - struct UnkIndicatorsStruct *data = (void *) gTasks[taskId].data; + struct ListMenu *data = (void *) gTasks[taskId].data; switch (field) { - case 0: - case 1: - return (s32)(data->field_4); - case 2: - return data->field_C; - case 3: - return data->field_E; - case 4: - return data->field_10; - case 5: - return data->field_11; - case 6: - return data->field_12; - case 7: - return data->field_13; - case 8: - return data->field_14_0; - case 9: - return data->field_14_1; - case 10: - return data->field_15_0; - case 11: - return data->field_15_1; - case 12: - return data->field_16_0; - case 13: - return data->field_16_1; - case 14: - return data->field_16_2; - case 15: - return data->field_17_0; - case 16: - return data->field_17_1; + case LISTFIELD_MOVECURSORFUNC: + case LISTFIELD_MOVECURSORFUNC2: + return (s32)(data->template.moveCursorFunc); + case LISTFIELD_TOTALITEMS: + return data->template.totalItems; + case LISTFIELD_MAXSHOWED: + return data->template.maxShowed; + case LISTFIELD_WINDOWID: + return data->template.windowId; + case LISTFIELD_HEADERX: + return data->template.header_X; + case LISTFIELD_ITEMX: + return data->template.item_X; + case LISTFIELD_CURSORX: + return data->template.cursor_X; + case LISTFIELD_UPTEXTY: + return data->template.upText_Y; + case LISTFIELD_CURSORPAL: + return data->template.cursorPal; + case LISTFIELD_FILLVALUE: + return data->template.fillValue; + case LISTFIELD_CURSORSHADOWPAL: + return data->template.cursorShadowPal; + case LISTFIELD_LETTERSPACING: + return data->template.lettersSpacing; + case LISTFIELD_ITEMVERTICALPADDING: + return data->template.itemVerticalPadding; + case LISTFIELD_SCROLLMULTIPLE: + return data->template.scrollMultiple; + case LISTFIELD_FONTID: + return data->template.fontId; + case LISTFIELD_CURSORKIND: + return data->template.cursorKind; default: return -1; } } -void ListMenuSetUnkIndicatorsStructField(u8 taskId, u8 field, s32 value) +void ListMenuSetTemplateField(u8 taskId, u8 field, s32 value) { - struct UnkIndicatorsStruct *data = (void *) &gTasks[taskId].data; + struct ListMenu *data = (void *) &gTasks[taskId].data; switch (field) { - case 0: - case 1: - data->field_4 = (void *)(value); + case LISTFIELD_MOVECURSORFUNC: + case LISTFIELD_MOVECURSORFUNC2: + data->template.moveCursorFunc = (void *)value; break; - case 2: - data->field_C = value; + case LISTFIELD_TOTALITEMS: + data->template.totalItems = value; break; - case 3: - data->field_E = value; + case LISTFIELD_MAXSHOWED: + data->template.maxShowed = value; break; - case 4: - data->field_10 = value; + case LISTFIELD_WINDOWID: + data->template.windowId = value; break; - case 5: - data->field_11 = value; + case LISTFIELD_HEADERX: + data->template.header_X = value; break; - case 6: - data->field_12 = value; + case LISTFIELD_ITEMX: + data->template.item_X = value; break; - case 7: - data->field_13 = value; + case LISTFIELD_CURSORX: + data->template.cursor_X = value; break; - case 8: - data->field_14_0 = value; + case LISTFIELD_UPTEXTY: + data->template.upText_Y = value; break; - case 9: - data->field_14_1 = value; + case LISTFIELD_CURSORPAL: + data->template.cursorPal = value; break; - case 10: - data->field_15_0 = value; + case LISTFIELD_FILLVALUE: + data->template.fillValue = value; break; - case 11: - data->field_15_1 = value; + case LISTFIELD_CURSORSHADOWPAL: + data->template.cursorShadowPal = value; break; - case 12: - data->field_16_0 = value; + case LISTFIELD_LETTERSPACING: + data->template.lettersSpacing = value; break; - case 13: - data->field_16_1 = value; + case LISTFIELD_ITEMVERTICALPADDING: + data->template.itemVerticalPadding = value; break; - case 14: - data->field_16_2 = value; + case LISTFIELD_SCROLLMULTIPLE: + data->template.scrollMultiple = value; break; - case 15: - data->field_17_0 = value; + case LISTFIELD_FONTID: + data->template.fontId = value; break; - case 16: - data->field_17_1 = value; + case LISTFIELD_CURSORKIND: + data->template.cursorKind = value; break; } } diff --git a/src/player_pc.c b/src/player_pc.c index d79f74f94a..f96e13ed78 100644 --- a/src/player_pc.c +++ b/src/player_pc.c @@ -1273,7 +1273,7 @@ static void ItemStorage_ExitItemList(u8 taskId) static void ItemStorage_StartItemSwap(u8 taskId) { s16 *data = gTasks[taskId].data; - ListMenuSetUnkIndicatorsStructField(tListTaskId, 16, 1); + ListMenuSetTemplateField(tListTaskId, 16, 1); sItemStorageMenu->toSwapPos = gPlayerPCItemPageInfo.itemsAbove + gPlayerPCItemPageInfo.cursorPos; ItemStorage_SetSwapArrow(tListTaskId, 0, 0); ItemStorage_UpdateSwapLinePos(sItemStorageMenu->toSwapPos); From b36a78ed47863cc2bbd5604dfcd4c2c76050b3d9 Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Mon, 2 Jun 2025 22:55:41 +0200 Subject: [PATCH 05/56] replace magic numbers with enums --- src/battle_pyramid_bag.c | 2 +- src/item_menu.c | 2 +- src/player_pc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/battle_pyramid_bag.c b/src/battle_pyramid_bag.c index f50a2a1e82..e131508928 100644 --- a/src/battle_pyramid_bag.c +++ b/src/battle_pyramid_bag.c @@ -1312,7 +1312,7 @@ static void Task_BeginItemSwap(u8 taskId) tListPos = gPyramidBagMenuState.scrollPosition + gPyramidBagMenuState.cursorPosition; gPyramidBagMenu->toSwapPos = tListPos; - ListMenuSetTemplateField(tListTaskId, 0x10, 1); + ListMenuSetTemplateField(tListTaskId, LISTFIELD_CURSORKIND, CURSOR_INVISIBLE); CopyItemName(gSaveBlock2Ptr->frontier.pyramidBag.itemId[gSaveBlock2Ptr->frontier.lvlMode][tListPos], gStringVar1); StringExpandPlaceholders(gStringVar4, gText_MoveVar1Where); FillWindowPixelBuffer(WIN_INFO, PIXEL_FILL(0)); diff --git a/src/item_menu.c b/src/item_menu.c index 5c75ca556c..a1323a6386 100755 --- a/src/item_menu.c +++ b/src/item_menu.c @@ -1432,7 +1432,7 @@ static void StartItemSwap(u8 taskId) { s16 *data = gTasks[taskId].data; - ListMenuSetTemplateField(tListTaskId, 16, 1); + ListMenuSetTemplateField(tListTaskId, LISTFIELD_CURSORKIND, CURSOR_INVISIBLE); tListPosition = gBagPosition.scrollPosition[gBagPosition.pocket] + gBagPosition.cursorPosition[gBagPosition.pocket]; gBagMenu->toSwapPos = tListPosition; CopyItemName(BagGetItemIdByPocketPosition(gBagPosition.pocket + 1, tListPosition), gStringVar1); diff --git a/src/player_pc.c b/src/player_pc.c index f96e13ed78..43239582ce 100644 --- a/src/player_pc.c +++ b/src/player_pc.c @@ -1273,7 +1273,7 @@ static void ItemStorage_ExitItemList(u8 taskId) static void ItemStorage_StartItemSwap(u8 taskId) { s16 *data = gTasks[taskId].data; - ListMenuSetTemplateField(tListTaskId, 16, 1); + ListMenuSetTemplateField(tListTaskId, LISTFIELD_CURSORKIND, CURSOR_INVISIBLE); sItemStorageMenu->toSwapPos = gPlayerPCItemPageInfo.itemsAbove + gPlayerPCItemPageInfo.cursorPos; ItemStorage_SetSwapArrow(tListTaskId, 0, 0); ItemStorage_UpdateSwapLinePos(sItemStorageMenu->toSwapPos); From 8b52e100b9e733ae3c01f973c16e5497013091a7 Mon Sep 17 00:00:00 2001 From: TLM-PsIQ <87483364+TLM-PsIQ@users.noreply.github.com> Date: Tue, 3 Jun 2025 22:25:29 +0200 Subject: [PATCH 06/56] Fix ScaryFace anim for Bitter Malice (#6476) --- src/battle_anim_effects_2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_anim_effects_2.c b/src/battle_anim_effects_2.c index e0583c7cf8..10080dc84b 100755 --- a/src/battle_anim_effects_2.c +++ b/src/battle_anim_effects_2.c @@ -3395,7 +3395,7 @@ void AnimTask_ScaryFace(u8 taskId) bool32 onPlayer; if (gAnimMoveIndex == MOVE_BITTER_MALICE) - onPlayer = IsOnPlayerSide(gBattleAnimAttacker); + onPlayer = !IsOnPlayerSide(gBattleAnimAttacker); else onPlayer = !IsOnPlayerSide(gBattleAnimTarget); From 48b88899d14035ad8b37ee41ee79d5122760ca19 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 23:12:38 +0200 Subject: [PATCH 07/56] add bassforte123 as a contributor for code (#7003) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 11 ++++++++++- CREDITS.md | 11 ++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 540d534f0b..e1db3fe48d 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -223,7 +223,7 @@ "name": "Ruby", "avatar_url": "https://avatars.githubusercontent.com/u/178652077?v=4", "profile": "https://github.com/RubyRaven6", - "contributions": [ + "contributions": [ "code", "doc" ] @@ -255,6 +255,15 @@ "contributions": [ "code" ] + }, + { + "login": "bassforte123", + "name": "bassforte123", + "avatar_url": "https://avatars.githubusercontent.com/u/130828119?v=4", + "profile": "https://github.com/bassforte123", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 6045f2301a..d928bad0a6 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -31,23 +31,24 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Philipp AUER
Philipp AUER

🚧 💻 tertu
tertu

🚧 💻 psf
psf

🚧 💻 + wiz1989
wiz1989

💻 PCG
PCG

💻 kittenchilly
kittenchilly

💻 🔬 🔣 - ExpoSeed
ExpoSeed

💻 🚧 👀 + ExpoSeed
ExpoSeed

💻 🚧 👀 Linathan
Linathan

💻 Eduardo Quezada
Eduardo Quezada

💻 🔣 📖 🚇 🚧 📆 📣 🔬 👀 ⚠️ 📓 khbsd
khbsd

📖 💻 Cafe
Cafe

🎨 agsmgmaster64
agsmgmaster64

💻 - mudskipper13
mudskipper13

💻 📖 - Ruby
Ruby

💻 📖 + Ruby
Ruby

💻 📖 - surskitty
surskitty

💻 - wiz1989
wiz1989

💻 + mudskipper13
mudskipper13

💻 📖 + surskitty
surskitty

💻 grintoul
grintoul

💻 + bassforte123
bassforte123

💻 From 2358f9a871f123b9ac9344b98f5c06a5765028ee Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 07:50:34 +0200 Subject: [PATCH 08/56] add iriv24 as a contributor for code (#7059) 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 e1db3fe48d..72bf225826 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -264,6 +264,15 @@ "contributions": [ "code" ] + }, + { + "login": "iriv24", + "name": "iriv24", + "avatar_url": "https://avatars.githubusercontent.com/u/40581123?v=4", + "profile": "https://github.com/iriv24", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index d928bad0a6..c93c1bcfd4 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -49,6 +49,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d surskitty
surskitty

💻 grintoul
grintoul

💻 bassforte123
bassforte123

💻 + iriv24
iriv24

💻 From b3752e13d8629d05642ae771a367db664f189455 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Wed, 4 Jun 2025 20:58:10 +0100 Subject: [PATCH 09/56] Fixes Aura Wheel + Normalize and Hunger Switch while Transformed/Terastallized (#7061) --- src/battle_main.c | 4 ++- src/battle_message.c | 2 +- src/battle_util.c | 4 ++- test/battle/ability/hunger_switch.c | 49 ++++++++++++++++++++++++++++ test/battle/move_effect/aura_wheel.c | 39 +++++++++++++++++++++- 5 files changed, 94 insertions(+), 4 deletions(-) diff --git a/src/battle_main.c b/src/battle_main.c index 8ea21a023d..bb03b5dfde 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5998,7 +5998,9 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, enum MonState { return TYPE_WATER; } - else if (moveEffect == EFFECT_AURA_WHEEL && species == SPECIES_MORPEKO_HANGRY) + else if (moveEffect == EFFECT_AURA_WHEEL + && species == SPECIES_MORPEKO_HANGRY + && ability != ABILITY_NORMALIZE) { return TYPE_DARK; } diff --git a/src/battle_message.c b/src/battle_message.c index 71ec2b305a..78708d47d6 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -1470,7 +1470,7 @@ const u8 gText_Body[] = _("Body"); const u8 gText_Judgment[] = _("{B_BUFF1}{CLEAR 13}Judgment{CLEAR 13}{B_BUFF2}"); static const u8 sText_TwoTrainersSentPkmn[] = _("{B_TRAINER1_NAME_WITH_CLASS} sent out {B_OPPONENT_MON1_NAME}!\p{B_TRAINER2_NAME_WITH_CLASS} sent out {B_OPPONENT_MON2_NAME}!"); static const u8 sText_Trainer2SentOutPkmn[] = _("{B_TRAINER2_NAME_WITH_CLASS} sent out {B_BUFF1}!"); -static const u8 sText_TwoTrainersWantToBattle[] = _("You are challenged by\n{B_TRAINER1_NAME_WITH_CLASS} and {B_TRAINER2_NAME_WITH_CLASS}!\p"); +static const u8 sText_TwoTrainersWantToBattle[] = _("You are challenged by {B_TRAINER1_NAME_WITH_CLASS} and {B_TRAINER2_NAME_WITH_CLASS}!\p"); static const u8 sText_InGamePartnerSentOutZGoN[] = _("{B_PARTNER_NAME_WITH_CLASS} sent out {B_PLAYER_MON2_NAME}! Go, {B_PLAYER_MON1_NAME}!"); const u16 gBattlePalaceFlavorTextTable[] = diff --git a/src/battle_util.c b/src/battle_util.c index 3bf809d530..9e7405bb48 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4320,7 +4320,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_HUNGER_SWITCH: - if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_TURN_END)) + if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) + && GetActiveGimmick(battler) != GIMMICK_TERA + && TryBattleFormChange(battler, FORM_CHANGE_BATTLE_TURN_END)) { gBattlerAttacker = battler; BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3NoPopup); diff --git a/test/battle/ability/hunger_switch.c b/test/battle/ability/hunger_switch.c index 38c3d2527d..1892de4424 100644 --- a/test/battle/ability/hunger_switch.c +++ b/test/battle/ability/hunger_switch.c @@ -22,3 +22,52 @@ SINGLE_BATTLE_TEST("Hunger Switch switches Morpeko's forms at the end of the tur EXPECT_EQ(player->species, SPECIES_MORPEKO_FULL_BELLY); } } + +SINGLE_BATTLE_TEST("Hunger Switch does not switch a mon transformed into Morpeko's form") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TRANSFORM) == EFFECT_TRANSFORM); + PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TRANSFORM); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent); + } +} + +SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form when Terastallized") +{ + GIVEN { + PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { } + TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_TERA); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + } +} + +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); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { } + TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_ROAR); } + TURN { SWITCH(player, 0); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + } THEN { + EXPECT_EQ(player->species, SPECIES_MORPEKO_HANGRY); + } +} diff --git a/test/battle/move_effect/aura_wheel.c b/test/battle/move_effect/aura_wheel.c index dfd31c878f..18602b8bc9 100644 --- a/test/battle/move_effect/aura_wheel.c +++ b/test/battle/move_effect/aura_wheel.c @@ -50,4 +50,41 @@ SINGLE_BATTLE_TEST("Aura Wheel changes type depending on Morpeko's form") } } -TO_DO_BATTLE_TEST("Aura Wheel can be used by Pokémon transformed into Morpeko"); +SINGLE_BATTLE_TEST("Aura Wheel can be used by Pokémon transformed into Morpeko") +{ + KNOWN_FAILING; // Aura Wheel for some reason isn't used by the opponent? + GIVEN { + PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); } + OPPONENT(SPECIES_DITTO) { Ability(ABILITY_IMPOSTER); } + } WHEN { + TURN { MOVE(opponent, MOVE_AURA_WHEEL); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AURA_WHEEL, opponent); + } +} + +SINGLE_BATTLE_TEST("Aura Wheel can be turned into a Normal-type move after Morpeko gains Normalize") +{ + bool32 hangryMode; + PARAMETRIZE { hangryMode = FALSE; } + PARAMETRIZE { hangryMode = TRUE; } + GIVEN { + ASSUME(GetMoveEffect(MOVE_ENTRAINMENT) == EFFECT_ENTRAINMENT); + ASSUME(gSpeciesInfo[SPECIES_DUSKULL].types[0] == TYPE_GHOST || gSpeciesInfo[SPECIES_DUSKULL].types[1] == TYPE_GHOST); + PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); } + OPPONENT(SPECIES_DELCATTY) { Ability(ABILITY_NORMALIZE); } + OPPONENT(SPECIES_DUSKULL); + } WHEN { + if (hangryMode) + TURN { } + TURN { MOVE(opponent, MOVE_ENTRAINMENT); } + TURN { MOVE(player, MOVE_AURA_WHEEL); SWITCH(opponent, 1); } + } SCENE { + if (hangryMode) + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AURA_WHEEL, player); + HP_BAR(opponent); + } + } +} From 99997c49fcd39510b7bd82c42b0eb01afeb909bf Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 4 Jun 2025 23:26:19 +0200 Subject: [PATCH 10/56] Fixes speed calculation order (#7064) --- src/battle_main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/battle_main.c b/src/battle_main.c index bb03b5dfde..662cf39791 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4699,6 +4699,10 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, enum ItemHoldEffect h { u32 speed = gBattleMons[battler].speed; + // stat stages + speed *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][0]; + speed /= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][1]; + // weather abilities if (HasWeatherEffect()) { @@ -4726,10 +4730,6 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, enum ItemHoldEffect h else if (ability == ABILITY_UNBURDEN && gDisableStructs[battler].unburdenActive) speed *= 2; - // stat stages - speed *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][0]; - speed /= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][1]; - // player's badge boost if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FRONTIER)) && ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPEED, battler) From 25736c5628a5dc2ea128fdee1405268bb7c9ded7 Mon Sep 17 00:00:00 2001 From: Linathan <35115312+LinathanZel@users.noreply.github.com> Date: Thu, 5 Jun 2025 08:35:30 -0400 Subject: [PATCH 11/56] Bug fix for Grassy Terrain incorrectly healing non-grounded Pokemon (#7058) --- src/battle_end_turn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index 7e9f8b15f5..6cf45ded4d 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -490,7 +490,7 @@ static bool32 HandleEndTurnFirstEventBlock(u32 battler) gBattleStruct->eventBlockCounter++; break; case FIRST_EVENT_BLOCK_GRASSY_TERRAIN_HEAL: - if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerAlive(battler) && !IsBattlerAtMaxHp(battler)) + if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerAlive(battler) && !IsBattlerAtMaxHp(battler) && IsBattlerGrounded(battler)) { gBattlerAttacker = battler; gBattleStruct->moveDamage[battler] = -(GetNonDynamaxMaxHP(battler) / 16); From f374291f69a28133049ef2483b96445c6399fe1c Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 5 Jun 2025 22:47:32 +0200 Subject: [PATCH 12/56] Fixes Wandering Spirit copied ability activation on fainted mon (#7066) --- data/battle_scripts_1.s | 2 ++ test/battle/ability/wandering_spirit.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 9625033c81..5928ea1bb1 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8234,7 +8234,9 @@ BattleScript_WanderingSpiritActivates:: printstring STRINGID_SWAPPEDABILITIES waitmessage B_WAIT_TIME_LONG switchinabilities BS_ATTACKER + jumpiffainted BS_TARGET, TRUE, BattleScript_WanderingSpiritActivatesRet switchinabilities BS_TARGET +BattleScript_WanderingSpiritActivatesRet: return BattleScript_TargetsStatWasMaxedOut:: diff --git a/test/battle/ability/wandering_spirit.c b/test/battle/ability/wandering_spirit.c index d32fb7c7b2..9d8d7fad36 100644 --- a/test/battle/ability/wandering_spirit.c +++ b/test/battle/ability/wandering_spirit.c @@ -2,3 +2,18 @@ #include "test/battle.h" TO_DO_BATTLE_TEST("TODO: Write Wandering Spirit (Ability) test titles") + +SINGLE_BATTLE_TEST("Wandering Spirit copied ability should not trigger on fainted mon") +{ + GIVEN { + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); } + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_YAMASK_GALAR) { HP(1); Ability(ABILITY_WANDERING_SPIRIT); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_POISON_FANG); SEND_OUT(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_FANG, player); + NOT ABILITY_POPUP(opponent, ABILITY_INTIMIDATE); + } +} From 451cb91d5d1a9211033aa661a82a28decaf3eb32 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:14:17 -0700 Subject: [PATCH 13/56] add Bivurnum as a contributor for code (#7067) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: psf <77138753+pkmnsnfrn@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CREDITS.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 72bf225826..b459747530 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -273,6 +273,15 @@ "contributions": [ "code" ] + }, + { + "login": "Bivurnum", + "name": "Bivurnum", + "avatar_url": "https://avatars.githubusercontent.com/u/147376167?v=4", + "profile": "https://github.com/Bivurnum", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index c93c1bcfd4..3ac869cefc 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -50,6 +50,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d grintoul
grintoul

💻 bassforte123
bassforte123

💻 iriv24
iriv24

💻 + Bivurnum
Bivurnum

💻 From dc279f14ba08208f42686b807152dfbedd9efe20 Mon Sep 17 00:00:00 2001 From: Linathan <35115312+LinathanZel@users.noreply.github.com> Date: Fri, 6 Jun 2025 15:33:56 -0400 Subject: [PATCH 14/56] Chloroblast fix (#7008) --- include/constants/battle_move_effects.h | 1 + src/battle_ai_main.c | 1 + src/battle_ai_util.c | 1 + src/battle_script_commands.c | 9 ++ src/data/battle_move_effects.h | 6 + src/data/moves_info.h | 2 +- test/battle/move_effect/chloroblast.c | 153 ++++++++++++++++++++++++ 7 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 test/battle/move_effect/chloroblast.c diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index eb0756bd40..d081d3a163 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -308,6 +308,7 @@ enum BattleMoveEffects EFFECT_COURT_CHANGE, EFFECT_MAX_HP_50_RECOIL, EFFECT_MIND_BLOWN, // Same as EFFECT_MAX_HP_50_RECOIL but is cancelled by Damp + EFFECT_CHLOROBLAST, // Same effect as EFFECT_MAX_HP_50_RECOIL but follows the same rules as EFFECT_RECOIL EFFECT_EXTREME_EVOBOOST, EFFECT_HIT_SET_REMOVE_TERRAIN, EFFECT_DARK_VOID, diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 2805a10692..18c140c2d2 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -5510,6 +5510,7 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_MAX_HP_50_RECOIL: case EFFECT_MIND_BLOWN: + case EFFECT_CHLOROBLAST: case EFFECT_SWAGGER: case EFFECT_FLATTER: case EFFECT_ATTRACT: diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index e71f11c70f..703335b326 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1014,6 +1014,7 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s { case EFFECT_MAX_HP_50_RECOIL: case EFFECT_MIND_BLOWN: + case EFFECT_CHLOROBLAST: case EFFECT_EXPLOSION: case EFFECT_FINAL_GAMBIT: return TRUE; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 88f3cfba54..83e2622ed6 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7024,6 +7024,15 @@ static void Cmd_moveend(void) effect = TRUE; } break; + case EFFECT_CHLOROBLAST: + if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker)) + { + gBattleStruct->moveDamage[gBattlerAttacker] = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil; + effect = TRUE; + } + break; case EFFECT_RAPID_SPIN: if (IsBattlerTurnDamaged(gBattlerTarget)) { diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 06b1fb3fd6..3b3a8f8e2c 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -1966,6 +1966,12 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleTvScore = 0, // TODO: Assign points }, + [EFFECT_CHLOROBLAST] = + { + .battleScript = BattleScript_EffectHit, + .battleTvScore = 0, // TODO: Assign points + }, + [EFFECT_EXTREME_EVOBOOST] = { .battleScript = BattleScript_EffectExtremeEvoboost, diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 0b61900109..d2e80d787b 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -19319,7 +19319,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .description = COMPOUND_STRING( "A user-hurting blast of\n" "amassed chlorophyll."), - .effect = EFFECT_MAX_HP_50_RECOIL, + .effect = EFFECT_CHLOROBLAST, .power = B_UPDATED_MOVE_DATA >= GEN_9 ? 150 : 120, .type = TYPE_GRASS, .accuracy = 95, diff --git a/test/battle/move_effect/chloroblast.c b/test/battle/move_effect/chloroblast.c new file mode 100644 index 0000000000..6452736478 --- /dev/null +++ b/test/battle/move_effect/chloroblast.c @@ -0,0 +1,153 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_CHLOROBLAST) == EFFECT_CHLOROBLAST); +} + +SINGLE_BATTLE_TEST("Chloroblast makes the user lose 1/2 of its Max HP") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(400); MaxHP(400); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(player, damage: 200); + NOT MESSAGE("Wobbuffet fainted!"); // Wobb had more than 1/2 of its HP, so it can't faint. + } +} + +SINGLE_BATTLE_TEST("Chloroblast causes the user to faint when below 1/2 of its Max HP") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(200); MaxHP(400); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(player, hp: 0); + MESSAGE("Wobbuffet fainted!"); + } +} + +SINGLE_BATTLE_TEST("Chloroblast causes the user & the target to faint when below 1/2 of its Max HP") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(200) ; MaxHP(400); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(opponent, hp: 0); + MESSAGE("The opposing Wobbuffet fainted!"); + HP_BAR(player, hp: 0); + MESSAGE("Wobbuffet fainted!"); + } +} + +SINGLE_BATTLE_TEST("Chloroblast hp loss is prevented by Magic Guard") +{ + GIVEN { + PLAYER(SPECIES_CLEFAIRY) { Ability(ABILITY_MAGIC_GUARD); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(opponent); + NOT HP_BAR(player); + } +} + +SINGLE_BATTLE_TEST("Chloroblast does not cause recoil damage if the user has Rock Head") +{ + GIVEN { + PLAYER(SPECIES_AERODACTYL) { Ability(ABILITY_ROCK_HEAD); } + OPPONENT(SPECIES_WOBBUFFET) { HP(400); MaxHP(400); } + } WHEN { + TURN { MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(opponent); + NOT HP_BAR(player); + } +} + +SINGLE_BATTLE_TEST("Chloroblast does not cause the user to lose HP even if the opposing mon protected") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(player); + } + } +} + +SINGLE_BATTLE_TEST("Chloroblast does not cause the user to lose HP even if it is absorbed by Sap Sipper") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_CHLOROBLAST) == TYPE_GRASS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GOGOAT) { Ability(ABILITY_SAP_SIPPER); } + } WHEN { + TURN { MOVE(player, MOVE_CHLOROBLAST); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_SAP_SIPPER); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(player); + } + } +} + +SINGLE_BATTLE_TEST("Chloroblast does not cause the user to lose HP if there is no target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_MEMENTO); MOVE(player, MOVE_CHLOROBLAST); SEND_OUT(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MEMENTO, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player); + HP_BAR(player); + } + MESSAGE("Wobbuffet used Chloroblast!"); + MESSAGE("But it failed!"); + MESSAGE("2 sent out Wobbuffet!"); + } +} + +SINGLE_BATTLE_TEST("Chloroblast is not affected by Reckless", s16 damage) +{ + u32 move; + + PARAMETRIZE { move = MOVE_CHLOROBLAST; } + PARAMETRIZE { move = MOVE_FRENZY_PLANT; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} From 67f7ad6b1e4e40760233331f49f4e4a5a421a801 Mon Sep 17 00:00:00 2001 From: i0brendan0 <19826742+i0brendan0@users.noreply.github.com> Date: Fri, 6 Jun 2025 15:00:33 -0500 Subject: [PATCH 15/56] [DRAFT] Fix Normalize not boosting Normal type moves if they were already Normal type (#7060) Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- src/battle_main.c | 4 +- test/battle/ability/normalize.c | 76 ++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 25 deletions(-) diff --git a/src/battle_main.c b/src/battle_main.c index 662cf39791..baed10a691 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -6014,7 +6014,9 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, enum MonState gBattleStruct->ateBoost[battler] = TRUE; return ateType; } - else if (moveType != TYPE_NORMAL + else if (moveEffect != EFFECT_CHANGE_TYPE_ON_ITEM + && moveEffect != EFFECT_TERRAIN_PULSE + && moveEffect != EFFECT_NATURAL_GIFT && moveEffect != EFFECT_HIDDEN_POWER && moveEffect != EFFECT_WEATHER_BALL && ability == ABILITY_NORMALIZE diff --git a/test/battle/ability/normalize.c b/test/battle/ability/normalize.c index 4858997421..2a449f74a5 100644 --- a/test/battle/ability/normalize.c +++ b/test/battle/ability/normalize.c @@ -80,40 +80,52 @@ SINGLE_BATTLE_TEST("Normalize still makes Freeze-Dry do super effective damage t } } -SINGLE_BATTLE_TEST("Normalize boosts power of unaffected moves by 20% (Gen7+)", s16 damage) +SINGLE_BATTLE_TEST("Normalize doesn't boost power of unaffected moves by 20% (< Gen7)", s16 damage) { - u32 ability, genConfig; - PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_7; } - PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_6; } - PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_7; } - PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_6; } + u32 ability; + PARAMETRIZE { ability = ABILITY_CUTE_CHARM; } + PARAMETRIZE { ability = ABILITY_NORMALIZE; } GIVEN { - WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, genConfig); - PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_TACKLE); } + WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, GEN_6); + PLAYER(SPECIES_DELCATTY) { Ability(ability); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_POUND); } } SCENE { HP_BAR(opponent, captureDamage: &results[i].damage); } FINALLY { - if (genConfig >= GEN_7) - EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.2), results[2].damage); // Ate - else - EXPECT_EQ(results[1].damage, results[3].damage); // No boost + EXPECT_EQ(results[0].damage, results[1].damage); // No boost } } -SINGLE_BATTLE_TEST("Normalize boosts power of affected moves by 20% (Gen7+)", s16 damage) +SINGLE_BATTLE_TEST("Normalize boosts power of unaffected moves by 20% (Gen7+)", s16 damage) { - u32 ability, genConfig; - PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_7; } - PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_6; } - PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_7; } - PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_6; } + u32 ability; + PARAMETRIZE { ability = ABILITY_CUTE_CHARM; } + PARAMETRIZE { ability = ABILITY_NORMALIZE; } GIVEN { - WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, genConfig); + WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, GEN_7); + PLAYER(SPECIES_DELCATTY) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_POUND); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(1.2), results[1].damage); // Ate + } +} + +SINGLE_BATTLE_TEST("Normalize doesn't boost power of affected moves by 20% (< Gen7)", s16 damage) +{ + u32 ability; + PARAMETRIZE { ability = ABILITY_CUTE_CHARM; } + PARAMETRIZE { ability = ABILITY_NORMALIZE; } + + GIVEN { + WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, GEN_6); PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_WATER_GUN); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -121,10 +133,26 @@ SINGLE_BATTLE_TEST("Normalize boosts power of affected moves by 20% (Gen7+)", s1 } SCENE { HP_BAR(opponent, captureDamage: &results[i].damage); } FINALLY { - if (genConfig >= GEN_7) - EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.8), results[2].damage); // STAB + ate - else - EXPECT_MUL_EQ(results[1].damage, Q_4_12(1.5), results[3].damage); // STAB + no ate + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); // STAB + no ate + } +} + +SINGLE_BATTLE_TEST("Normalize boosts power of affected moves by 20% (Gen7+)", s16 damage) +{ + u32 ability; + PARAMETRIZE { ability = ABILITY_CUTE_CHARM; } + PARAMETRIZE { ability = ABILITY_NORMALIZE; } + + GIVEN { + WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, GEN_7); + PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_WATER_GUN); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_WATER_GUN); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.8), results[1].damage); // STAB + ate } } From fd90802a37557d100fb4b0646fa3d05360483988 Mon Sep 17 00:00:00 2001 From: psf <77138753+pkmnsnfrn@users.noreply.github.com> Date: Sat, 7 Jun 2025 00:51:47 -0700 Subject: [PATCH 16/56] Fixed CI issue introduced 7046 (#7072) --- .github/workflows/build.yml | 4 ++-- .github/workflows/labels.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3304101d4f..30231dbd0f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,7 @@ on: jobs: build: - if: github.actor != 'allcontributors' + if: github.actor != 'allcontributors[bot]' runs-on: ubuntu-latest env: GAME_VERSION: EMERALD @@ -38,7 +38,7 @@ jobs: run: | make -j${nproc} check allcontributors: - if: github.actor == 'allcontributors' + if: github.actor == 'allcontributors[bot]' runs-on: ubuntu-latest needs: [] steps: diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index 7315082cb6..325e72950d 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -6,7 +6,7 @@ on: jobs: label: - if: github.actor != 'allcontributors' + if: github.actor != 'allcontributors[bot]' runs-on: ubuntu-latest steps: - name: check labels @@ -29,7 +29,7 @@ jobs: category: sprite-issue type: documentation allcontributors: - if: github.actor == 'allcontributors' + if: github.actor == 'allcontributors[bot]' runs-on: ubuntu-latest needs: [] steps: From ea75a09dac6a5cc2c1c0cfb0d4fe1a07bf4f4878 Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sat, 7 Jun 2025 02:54:07 -0500 Subject: [PATCH 17/56] Clear saved follower NPC door warp when doing dive warp (#7065) --- src/field_screen_effect.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/field_screen_effect.c b/src/field_screen_effect.c index 9ac99465ca..7b17f53666 100644 --- a/src/field_screen_effect.c +++ b/src/field_screen_effect.c @@ -528,6 +528,7 @@ void DoDiveWarp(void) TryFadeOutOldMapMusic(); WarpFadeOutScreen(); PlayRainStoppingSoundEffect(); + SetFollowerNPCData(FNPC_DATA_COME_OUT_DOOR, FNPC_DOOR_NONE); gFieldCallback = FieldCB_DefaultWarpExit; CreateTask(Task_WarpAndLoadMap, 10); } From 1692d641c47ddf8c697be963891ffdfbd8bb910b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=8E=E9=9B=85?= <56014327+ExMingYan@users.noreply.github.com> Date: Sat, 7 Jun 2025 16:05:24 +0800 Subject: [PATCH 18/56] Fix Contest Painting load palette error (#7077) --- src/contest_painting.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/contest_painting.c b/src/contest_painting.c index 1911e70689..cb78caac26 100644 --- a/src/contest_painting.c +++ b/src/contest_painting.c @@ -363,7 +363,7 @@ static void VBlankCB_ContestPainting(void) static void InitContestMonPixels(u16 species, bool8 backPic) { const void *pal = GetMonSpritePalFromSpeciesAndPersonality(species, gContestPaintingWinner->isShiny, gContestPaintingWinner->personality); - LZDecompressVram(pal, gContestPaintingMonPalette); + memcpy(gContestPaintingMonPalette, pal, PLTT_SIZE_4BPP); if (!backPic) { HandleLoadSpecialPokePic(TRUE, From 9f1eb78737283b8e04aa36ba998503e298c66aad Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sat, 7 Jun 2025 11:49:15 +0200 Subject: [PATCH 19/56] Fixes freeze during a 1v2 double battle (#7075) --- src/battle_script_commands.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 83e2622ed6..05a2563cd5 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2868,7 +2868,7 @@ static void Cmd_resultmessage(void) gDisableStructs[gBattlerTarget].iceFaceActivationPrevention = FALSE; u32 side = GetBattlerSide(gBattlerTarget); if (gBattleStruct->changedSpecies[side][gBattlerPartyIndexes[gBattlerTarget]] == SPECIES_NONE) - gBattleStruct->changedSpecies[side][gBattlerPartyIndexes[gBattlerTarget]] = gBattleMons[gBattlerTarget].species; + gBattleStruct->changedSpecies[side][gBattlerPartyIndexes[gBattlerTarget]] = gBattleMons[gBattlerTarget].species; gBattleMons[gBattlerTarget].species = SPECIES_EISCUE_NOICE; gBattleScripting.battler = gBattlerTarget; // For STRINGID_PKMNTRANSFORMED BattleScriptPushCursor(); @@ -7934,7 +7934,16 @@ static void Cmd_openpartyscreen(void) { if (((1u << i) & hitmarkerFaintBits)) { - if (i > 1 && ((1u << BATTLE_PARTNER(i)) & hitmarkerFaintBits)) + u32 skipPartnerCheck = FALSE; + if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS + && GetBattlerSide(i) == B_SIDE_OPPONENT + && TRAINER_BATTLE_PARAM.opponentB != TRAINER_NONE) + skipPartnerCheck = TRUE; + + // In a 1v2 Double Battle if trainer A didn't have any more mons left + // the battler for trainer B wasn't being registered to be send out. + // Likely reason is because hitmarkerFaintBits was not set for battler 1 due to it being missing for a turn or cleared somewhere + if (!skipPartnerCheck && i > 1 && ((1u << BATTLE_PARTNER(i)) & hitmarkerFaintBits)) continue; battler = i; From 29adca79a514f3cb7f6de568abcd1f939f017442 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sat, 7 Jun 2025 07:06:20 -0700 Subject: [PATCH 20/56] add pkmnsnfrn as a contributor for projectManagement (#7081) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 3 ++- CREDITS.md | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index b459747530..e030f8953c 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -118,7 +118,8 @@ "profile": "https://linktr.ee/pkmnsnfrn", "contributions": [ "maintenance", - "code" + "code", + "projectManagement" ] }, { diff --git a/CREDITS.md b/CREDITS.md index 3ac869cefc..644d980e68 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -30,7 +30,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Pawkkie
Pawkkie

🚧 💻 📖 Philipp AUER
Philipp AUER

🚧 💻 tertu
tertu

🚧 💻 - psf
psf

🚧 💻 + psf
psf

🚧 💻 📆 wiz1989
wiz1989

💻 PCG
PCG

💻 kittenchilly
kittenchilly

💻 🔬 🔣 From 28d185be95a9e6194bda0e840fbd36b02665207e Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Sat, 7 Jun 2025 22:59:44 +0100 Subject: [PATCH 21/56] Fixes Pursuit potentially causing both battlers to switch into the same mon (#7084) --- src/battle_script_commands.c | 6 ++++++ src/battle_util.c | 22 +++++++++++----------- test/battle/move_effect/pursuit.c | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 05a2563cd5..fc4cee0621 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7445,6 +7445,12 @@ 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)) gStatuses3[gBattlerAttacker] &= ~(STATUS3_CHARGED_UP); + // check if Stellar type boost should be used up + if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA + && GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR + && GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS + && IsTypeStellarBoosted(gBattlerAttacker, moveType)) + ExpendTypeStellarBoost(gBattlerAttacker, moveType); memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); for (i = 0; i < gBattlersCount; i++) diff --git a/src/battle_util.c b/src/battle_util.c index 9e7405bb48..646edebed2 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -545,6 +545,16 @@ void HandleAction_UseMove(void) void HandleAction_Switch(void) { gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; + + // if switching to a mon that is already on field, cancel switch + if (!(gAbsentBattlerFlags & (1u << BATTLE_PARTNER(gBattlerAttacker))) + && IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)) + && gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)] == gBattleStruct->monToSwitchIntoId[gBattlerAttacker]) + { + gCurrentActionFuncId = B_ACTION_FINISHED; + return; + } + gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gActionSelectionCursor[gBattlerAttacker] = 0; @@ -851,7 +861,7 @@ void HandleAction_NothingIsFainted(void) void HandleAction_ActionFinished(void) { - u32 i, j, moveType; + u32 i, j; bool32 afterYouActive = gSpecialStatuses[gBattlerByTurnOrder[gCurrentTurnActionNumber + 1]].afterYou; gBattleStruct->monToSwitchIntoId[gBattlerByTurnOrder[gCurrentTurnActionNumber]] = gSelectedMonPartyId = PARTY_SIZE; gCurrentTurnActionNumber++; @@ -862,16 +872,6 @@ void HandleAction_ActionFinished(void) | HITMARKER_OBEYS | HITMARKER_SYNCHRONIZE_EFFECT | HITMARKER_CHARGING | HITMARKER_IGNORE_DISGUISE); - // check if Stellar type boost should be used up - moveType = GetBattleMoveType(gCurrentMove); - - if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA - && GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR - && GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS - && IsTypeStellarBoosted(gBattlerAttacker, moveType)) - { - ExpendTypeStellarBoost(gBattlerAttacker, moveType); - } ClearDamageCalcResults(); gCurrentMove = 0; gBattleScripting.animTurn = 0; diff --git a/test/battle/move_effect/pursuit.c b/test/battle/move_effect/pursuit.c index 649b331eec..b18aa74b09 100644 --- a/test/battle/move_effect/pursuit.c +++ b/test/battle/move_effect/pursuit.c @@ -673,4 +673,24 @@ SINGLE_BATTLE_TEST("Pursuit user faints to Life Orb and target still switches ou } } +DOUBLE_BATTLE_TEST("Pursuit user switches out due to Red Card and partner's switch is cancelled if switching to same Pokémon") +{ + GIVEN { + ASSUME(GetItemHoldEffect(ITEM_RED_CARD) == HOLD_EFFECT_RED_CARD); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_ARCEUS); + OPPONENT(SPECIES_WYNAUT) { Item(ITEM_RED_CARD); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_ARCEUS); + } WHEN { + TURN { SWITCH(opponentLeft, 2); SWITCH(playerRight, 2); MOVE(playerLeft, MOVE_PURSUIT, target: opponentLeft); } + } THEN { + // playerLeft switches to Arceus + EXPECT_EQ(playerLeft->species, SPECIES_ARCEUS); + // playerRight has their switch cancelled + EXPECT_EQ(playerRight->species, SPECIES_WYNAUT); + } +} + TO_DO_BATTLE_TEST("Baton Pass doesn't cause Pursuit to increase its power or priority"); From 5586b0c2165e21169c6398521303ac42fff53891 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 00:00:14 +0200 Subject: [PATCH 22/56] add ravepossum as a contributor for code (#7083) 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 e030f8953c..0f6667d232 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -283,6 +283,15 @@ "contributions": [ "code" ] + }, + { + "login": "ravepossum", + "name": "RavePossum", + "avatar_url": "https://avatars.githubusercontent.com/u/145081120?v=4", + "profile": "https://github.com/ravepossum", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 644d980e68..488a40a146 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -51,6 +51,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d bassforte123
bassforte123

💻 iriv24
iriv24

💻 Bivurnum
Bivurnum

💻 + RavePossum
RavePossum

💻 From 96844bd3bbec3d816b8cdc55b5cc58c0264d372f Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Mon, 9 Jun 2025 16:19:25 -0400 Subject: [PATCH 23/56] fix sprite count in RepeatBallOpenParticleAnimation --- src/battle_anim_throw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_anim_throw.c b/src/battle_anim_throw.c index 956020b305..22bd41fd25 100755 --- a/src/battle_anim_throw.c +++ b/src/battle_anim_throw.c @@ -1864,7 +1864,7 @@ static void RepeatBallOpenParticleAnimation(u8 taskId) priority = gTasks[taskId].data[3]; subpriority = gTasks[taskId].data[4]; - for (i = 0; i < POKEBALL_COUNT; i++) + for (i = 0; i < 12; i++) { spriteId = CreateSprite(&sBallParticleSpriteTemplates[ballId], x, y, subpriority); if (spriteId != MAX_SPRITES) From 0834d9d80be370dd0d995629a300fc8551529022 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Mon, 9 Jun 2025 22:52:37 +0200 Subject: [PATCH 24/56] Added loop iterator style (#7092) Co-authored-by: Hedara --- docs/STYLEGUIDE.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/STYLEGUIDE.md b/docs/STYLEGUIDE.md index f53e1a2dcc..dc99be6b1b 100644 --- a/docs/STYLEGUIDE.md +++ b/docs/STYLEGUIDE.md @@ -265,6 +265,15 @@ void SetCurrentDifficultyLevel(enum DifficultyLevel desiredDifficulty) .power = B_UPDATED_MOVE_DATA >= GEN_6 ? 45 : 35, }, ``` +### Variable Declarations +Loop iterators should be declared as part of the loop unless there's a very good reason not to. +```C +for (u32 i = 0; i < LOOP_ITERATIONS; i++) +{ + dst1[i] = i; + dst2[i] = i; +} +``` ## Data Type Sizes When a variable number is used, the data type should generally `u32` (unsigned) or `s32` (signed). There are a few exceptions to this rule, such as: * Values stored in the saveblock should use the smallest data type possible. @@ -350,7 +359,7 @@ enum DifficultyLevel GetCurrentDifficultyLevel(void) } ``` -### Data file format +## Data file format External data files should use JSON. From 7514ed5efbb6f3e3913cf57d0424ad8ed8c138fb Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 10 Jun 2025 05:52:48 -0700 Subject: [PATCH 25/56] add Emiliasky as a contributor for test (#7082) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: psf <77138753+pkmnsnfrn@users.noreply.github.com> --- .all-contributorsrc | 10 ++++++++++ CREDITS.md | 3 +++ 2 files changed, 13 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 0f6667d232..7ae7d5a3d1 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -284,6 +284,16 @@ "code" ] }, + { + "login": "Emiliasky", + "name": "Emilia Daelman", + "avatar_url": "https://avatars.githubusercontent.com/u/48217459?v=4", + "profile": "https://github.com/Emiliasky", + "contributions": [ + "code", + "test" + ] + }, { "login": "ravepossum", "name": "RavePossum", diff --git a/CREDITS.md b/CREDITS.md index 488a40a146..4f1b14c0fd 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -51,6 +51,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d bassforte123
bassforte123

💻 iriv24
iriv24

💻 Bivurnum
Bivurnum

💻 + Emilia Daelman
Emilia Daelman

⚠️ 💻 + + RavePossum
RavePossum

💻 From 7ef781e9e0a7607b125947152aeeec9318437da1 Mon Sep 17 00:00:00 2001 From: psf <77138753+pkmnsnfrn@users.noreply.github.com> Date: Tue, 10 Jun 2025 10:38:55 -0700 Subject: [PATCH 26/56] Update CREDITS.md with correct line placement (#7096) --- CREDITS.md | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 4f1b14c0fd..3e26e45d76 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -72,6 +72,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! ## Other Credits ### Mega Evolution Overworld Sprite Credits: @@ -87,19 +88,3 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d - [Data Files](https://www.pokecommunity.com/showthread.php?t=417909) - [Complete FireRed Upgrade](https://github.com/Skeli789/Complete-Fire-Red-Upgrade) - [pokeemerald](https://github.com/pret/pokeemerald/) - - -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! - -## Contributors ✨ - -Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): - - - - - - - - -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! From 393bed069a94744cc27deb52f0c9f221761d0754 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Wed, 11 Jun 2025 03:13:37 -0400 Subject: [PATCH 27/56] Wrote some missing tests (#7094) --- test/battle/move_effect/acupressure.c | 2 +- test/battle/move_effect/blizzard.c | 4 --- test/battle/move_effect/bulk_up.c | 20 +++++++++++++- test/battle/move_effect/calm_mind.c | 20 +++++++++++++- test/battle/move_effect/expanding_force.c | 26 ++++++++++++++++++- .../move_flags/always_hits_in_hail_snow.c | 1 + 6 files changed, 65 insertions(+), 8 deletions(-) delete mode 100644 test/battle/move_effect/blizzard.c diff --git a/test/battle/move_effect/acupressure.c b/test/battle/move_effect/acupressure.c index 02be60725d..6eaade94ae 100644 --- a/test/battle/move_effect/acupressure.c +++ b/test/battle/move_effect/acupressure.c @@ -1,7 +1,7 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Acupressure increases one of two stats by 2 stages at random"); +TO_DO_BATTLE_TEST("Acupressure increases one of its stats by 2 stages at random"); TO_DO_BATTLE_TEST("Acupressure doesn't try to increase a stat that has been maximized"); TO_DO_BATTLE_TEST("Acupressure fails on the user if all of its stats are maximized"); TO_DO_BATTLE_TEST("Acupressure fails on the ally if all of its stats are maximized"); diff --git a/test/battle/move_effect/blizzard.c b/test/battle/move_effect/blizzard.c deleted file mode 100644 index 1e566f98fc..0000000000 --- a/test/battle/move_effect/blizzard.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -TO_DO_BATTLE_TEST("Blizzard ignores accuracy check durin Hail and Snow"); diff --git a/test/battle/move_effect/bulk_up.c b/test/battle/move_effect/bulk_up.c index 7f47d5d48e..be0536f4ca 100644 --- a/test/battle/move_effect/bulk_up.c +++ b/test/battle/move_effect/bulk_up.c @@ -1,4 +1,22 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Bulk Up increases the user's Attack and Defense"); +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_BULK_UP) == EFFECT_BULK_UP); +} + +SINGLE_BATTLE_TEST("Bulk Up increases the user's Attack and Defense by 1 stage each") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BULK_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULK_UP, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + } +} diff --git a/test/battle/move_effect/calm_mind.c b/test/battle/move_effect/calm_mind.c index 66c16361f4..3b2ba76113 100644 --- a/test/battle/move_effect/calm_mind.c +++ b/test/battle/move_effect/calm_mind.c @@ -1,4 +1,22 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Calm Mind increases the user's Sp. Attack and Sp. Defense by 1 stage each"); +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_CALM_MIND) == EFFECT_CALM_MIND); +} + +SINGLE_BATTLE_TEST("Calm Mind increases the user's Sp. Attack and Sp. Defense by 1 stage each") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CALM_MIND); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CALM_MIND, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1); + } +} diff --git a/test/battle/move_effect/expanding_force.c b/test/battle/move_effect/expanding_force.c index 74b78fdd86..7b0284390b 100644 --- a/test/battle/move_effect/expanding_force.c +++ b/test/battle/move_effect/expanding_force.c @@ -1,4 +1,28 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Expanding Force's power increases by 50% if the user is affected by Psychic Terrain"); +SINGLE_BATTLE_TEST("Expanding Force's power increases by 50% if the user is affected by Psychic Terrain", s16 damage) +{ + bool32 terrain; + PARAMETRIZE { terrain = FALSE; } + PARAMETRIZE { terrain = TRUE; } + GIVEN { + ASSUME(GetMoveEffect(MOVE_EXPANDING_FORCE) == EFFECT_EXPANDING_FORCE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (terrain) + TURN { MOVE(player, MOVE_PSYCHIC_TERRAIN); } + TURN { MOVE(player, MOVE_EXPANDING_FORCE); } + } SCENE { + MESSAGE("Wobbuffet used Expanding Force!"); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + if (B_TERRAIN_TYPE_BOOST >= GEN_8) + // 1.3 Terrain boost x 1.5 effect boost = 1.95 boost + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.95), results[1].damage); + else + // 1.5 Terrain boost x 1.5 effect boost = 2.25 boost + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.25), results[1].damage); + } +} diff --git a/test/battle/move_flags/always_hits_in_hail_snow.c b/test/battle/move_flags/always_hits_in_hail_snow.c index 5673c0df8a..788186e7b5 100644 --- a/test/battle/move_flags/always_hits_in_hail_snow.c +++ b/test/battle/move_flags/always_hits_in_hail_snow.c @@ -6,6 +6,7 @@ SINGLE_BATTLE_TEST("Blizzard bypasses accuracy checks in Hail and Snow") u32 move; PARAMETRIZE { move = MOVE_HAIL; } PARAMETRIZE { move = MOVE_SNOWSCAPE; } + PASSES_RANDOMLY(100, 100, RNG_ACCURACY); GIVEN { ASSUME(GetMoveAccuracy(MOVE_BLIZZARD) == 70); ASSUME(MoveAlwaysHitsInHailSnow(MOVE_BLIZZARD)); From 6cb50161138841292c2d0b22a0239e0b3459d4ec Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Thu, 12 Jun 2025 13:46:03 -0400 Subject: [PATCH 28/56] Fixed potential mismatch between players and battlers in tests (#7101) --- test/test_runner_battle.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 981683b35a..a20637b334 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -284,16 +284,17 @@ static void BattleTest_Run(void *data) break; } - for (i = 0; i < STATE->battlersCount; i++) + for (i = 0; i < MAX_LINK_PLAYERS; i++) { DATA.recordedBattle.playersName[i][0] = CHAR_1 + i; DATA.recordedBattle.playersName[i][1] = EOS; DATA.recordedBattle.playersLanguage[i] = GAME_LANGUAGE; DATA.recordedBattle.playersBattlers[i] = i; - - DATA.currentMonIndexes[i] = (i & BIT_FLANK) == B_FLANK_LEFT ? 0 : 1; } + for (i = 0; i < STATE->battlersCount; i++) + DATA.currentMonIndexes[i] = i / 2; + STATE->runRandomly = TRUE; STATE->runGiven = TRUE; STATE->runWhen = TRUE; From a000a743dfd8f67e11b44d9b59bdb2b73ee16a7a Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Fri, 13 Jun 2025 13:12:13 +0100 Subject: [PATCH 29/56] Fix typos and some cleanup (mainly in battle files) (#7107) --- asm/macros/battle_script.inc | 4 +-- data/battle_scripts_1.s | 6 ++-- include/battle_scripts.h | 2 +- src/battle_ai_switch_items.c | 16 ++++----- src/battle_ai_util.c | 2 +- src/battle_main.c | 10 ++---- src/battle_script_commands.c | 46 +++++++++++++------------- src/battle_util.c | 2 +- src/data/battle_move_effects.h | 2 +- test/battle/hold_effect/jaboca_berry.c | 2 +- 10 files changed, 44 insertions(+), 48 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index e671bc1649..96bbe2d370 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1390,8 +1390,8 @@ .byte \battler .endm - .macro tryintimidatejectpack - callnative BS_TryIntimidatEjectpack + .macro tryintimidateejectpack + callnative BS_TryIntimidateEjectPack .endm .macro allyswitchswapbattlers diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 5928ea1bb1..45bee115b8 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2704,7 +2704,7 @@ BattleScript_TryTailwindAbilitiesLoop_WindPower: waitmessage B_WAIT_TIME_LONG goto BattleScript_TryTailwindAbilitiesLoop_Increment -BattleScript_EffectMircleEye:: +BattleScript_EffectMiracleEye:: attackcanceler accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE attackstring @@ -7658,7 +7658,7 @@ BattleScript_IntimidateLoopIncrement: destroyabilitypopup restoretarget pause B_WAIT_TIME_MED - tryintimidatejectpack + tryintimidateejectpack end3 BattleScript_IntimidatePrevented:: @@ -7724,7 +7724,7 @@ BattleScript_SupersweetSyrupLoopIncrement: destroyabilitypopup restoretarget pause B_WAIT_TIME_MED - tryintimidatejectpack + tryintimidateejectpack end3 BattleScript_SupersweetSyrupWontDecrease: diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 75d7495498..de3e876aa2 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -737,7 +737,7 @@ extern const u8 BattleScript_EffectFling[]; extern const u8 BattleScript_EffectNaturalGift[]; extern const u8 BattleScript_EffectRoost[]; extern const u8 BattleScript_EffectGravity[]; -extern const u8 BattleScript_EffectMircleEye[]; +extern const u8 BattleScript_EffectMiracleEye[]; extern const u8 BattleScript_EffectTailwind[]; extern const u8 BattleScript_EffectEmbargo[]; extern const u8 BattleScript_EffectAquaRing[]; diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 60030acef5..c6a4b8708f 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -230,7 +230,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler) // Get maximum damage mon can deal damageDealt = AI_GetDamage(battler, opposingBattler, i, AI_ATTACKING, gAiLogicData); - if(damageDealt > maxDamageDealt && !AI_DoesChoiceItemBlockMove(battler, aiMove)) + if (damageDealt > maxDamageDealt && !AI_DoesChoiceItemBlockMove(battler, aiMove)) { maxDamageDealt = damageDealt; aiBestMove = aiMove; @@ -272,14 +272,14 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler) hitsToKoAI = GetNoOfHitsToKOBattlerDmg(maxDamageTaken, battler); // Check if mon gets one shot - if(maxDamageTaken > gBattleMons[battler].hp + if (maxDamageTaken > gBattleMons[battler].hp && !(gItemsInfo[gBattleMons[battler].item].holdEffect == HOLD_EFFECT_FOCUS_SASH || (!IsMoldBreakerTypeAbility(opposingBattler, gBattleMons[opposingBattler].ability) && B_STURDY >= GEN_5 && aiAbility == ABILITY_STURDY))) { getsOneShot = TRUE; } // Check if current mon can 1v1 in spite of bad matchup, and don't switch out if it can - if(hitsToKoPlayer < hitsToKoAI || (hitsToKoPlayer == hitsToKoAI && AI_IsFaster(battler, opposingBattler, aiBestMove))) + if (hitsToKoPlayer < hitsToKoAI || (hitsToKoPlayer == hitsToKoAI && AI_IsFaster(battler, opposingBattler, aiBestMove))) return FALSE; // If we don't have any other viable options, don't switch out @@ -1702,7 +1702,7 @@ static u32 GetSwitchinRecurringDamage(void) else if (holdEffect == HOLD_EFFECT_STICKY_BARB) { passiveDamage = maxHP / 8; - if(passiveDamage == 0) + if (passiveDamage == 0) passiveDamage = 1; } } @@ -1727,7 +1727,7 @@ static u32 GetSwitchinStatusDamage(u32 battler) statusDamage = maxHP / 16; else statusDamage = maxHP / 8; - if(ability == ABILITY_HEATPROOF) + if (ability == ABILITY_HEATPROOF) statusDamage = statusDamage / 2; if (statusDamage == 0) statusDamage = 1; @@ -2095,10 +2095,10 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, } // Track max hits to KO and set defensive mon - if(hitsToKOAI > maxHitsToKO && (canSwitchinWin1v1 || gAiThinkingStruct->aiFlags[battler] & AI_FLAG_STALL)) + if (hitsToKOAI > maxHitsToKO && (canSwitchinWin1v1 || gAiThinkingStruct->aiFlags[battler] & AI_FLAG_STALL)) { maxHitsToKO = hitsToKOAI; - if(maxHitsToKO > defensiveMonHitKOThreshold) + if (maxHitsToKO > defensiveMonHitKOThreshold) defensiveMonId = i; } @@ -2124,7 +2124,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, // Check that mon isn't one shot and set best damage mon if (damageDealt > maxDamageDealt) { - if((isFreeSwitch && hitsToKOAI > 1) || hitsToKOAI > 2) // This is a "default", we have uniquely low standards + if ((isFreeSwitch && hitsToKOAI > 1) || hitsToKOAI > 2) // This is a "default", we have uniquely low standards { maxDamageDealt = damageDealt; damageMonId = i; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 703335b326..340fadc66f 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -892,7 +892,7 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3 return TRUE; break; case EFFECT_PURSUIT: - if(noOfHitsToKo == 1) + if (noOfHitsToKo == 1) return TRUE; break; default: diff --git a/src/battle_main.c b/src/battle_main.c index baed10a691..cc5f281d4e 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -434,10 +434,6 @@ void CB2_InitBattle(void) AllocateMonSpritesGfx(); RecordedBattle_ClearFrontierPassFlag(); -#if T_SHOULD_RUN_MOVE_ANIM - gLoadFail = FALSE; -#endif // T_SHOULD_RUN_MOVE_ANIM - #if T_SHOULD_RUN_MOVE_ANIM gLoadFail = FALSE; #endif // T_SHOULD_RUN_MOVE_ANIM @@ -3107,7 +3103,7 @@ static void BattleStartClearSetData(void) gSelectedMonPartyId = PARTY_SIZE; // Revival Blessing gCategoryIconSpriteId = 0xFF; - if(IsSleepClauseEnabled()) + if (IsSleepClauseEnabled()) { // If monCausingSleepClause[side] equals PARTY_SIZE, Sleep Clause is not active for the given side. gBattleStruct->monCausingSleepClause[B_SIDE_PLAYER] = PARTY_SIZE; @@ -4911,8 +4907,8 @@ s32 GetWhichBattlerFasterOrTies(u32 battler1, u32 battler2, bool32 ignoreChosenM } // 24 == MAX_BATTLERS_COUNT!. -// These are the possible orders if all the battlers speed tie. An order -// is chosen at the start of the turn. +// These are the possible orders if all the battlers speed tie. +// An order is chosen at the start of the turn. static const u8 sBattlerOrders[24][4] = { { 0, 1, 2, 3 }, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index fc4cee0621..92adb1d9c8 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -344,7 +344,7 @@ static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent); static void TryUpdateEvolutionTracker(u32 evolutionCondition, u32 upAmount, u16 usedMove); static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr, u16 move); static void ResetValuesForCalledMove(void); -static void TryRestoreDamageAfterCheeckPouch(u32 battler); +static void TryRestoreDamageAfterCheekPouch(u32 battler); static void Cmd_attackcanceler(void); static void Cmd_accuracycheck(void); @@ -2748,7 +2748,7 @@ static void Cmd_datahpupdate(void) gBattleStruct->timesGotHit[GetBattlerSide(gBattlerTarget)][gBattlerPartyIndexes[gBattlerTarget]]++; } - TryRestoreDamageAfterCheeckPouch(battler); + TryRestoreDamageAfterCheekPouch(battler); gBattlescriptCurrInstr = cmd->nextInstr; } @@ -8283,22 +8283,21 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) { if (CanBePoisoned(gBattlerAttacker, battler, GetBattlerAbility(gBattlerAttacker), GetBattlerAbility(battler))) { - u32 tspikes = 0; - if (gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount >= 2) { - tspikes = 1; + gBattleScripting.battler = battler; + BattleScriptPushCursor(); + if (gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount >= 2) + { + gBattlescriptCurrInstr = BattleScript_ToxicSpikesBadlyPoisoned; gBattleMons[battler].status1 |= STATUS1_TOXIC_POISON; } else + { + gBattlescriptCurrInstr = BattleScript_ToxicSpikesPoisoned; gBattleMons[battler].status1 |= STATUS1_POISON; + } BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[battler].status1), &gBattleMons[battler].status1); MarkBattlerForControllerExec(battler); - gBattleScripting.battler = battler; - BattleScriptPushCursor(); - if (tspikes == 0) - gBattlescriptCurrInstr = BattleScript_ToxicSpikesPoisoned; - else - gBattlescriptCurrInstr = BattleScript_ToxicSpikesBadlyPoisoned; } } } @@ -8517,7 +8516,7 @@ static void Cmd_returntoball(void) MarkBattlerForControllerExec(battler); // Don't always execute a form change here otherwise we can stomp gigantamax - if(!cmd->changingForm) + if (!cmd->changingForm) TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH); gBattlescriptCurrInstr = cmd->nextInstr; @@ -9142,7 +9141,7 @@ static bool32 TryCheekPouch(u32 battler, u32 itemId) } // When Cheek Pouch activates mid-battle it overwrites the current damage, so restore it -static void TryRestoreDamageAfterCheeckPouch(u32 battler) +static void TryRestoreDamageAfterCheekPouch(u32 battler) { if (gBattleStruct->cheekPouchActivated) { @@ -9658,17 +9657,17 @@ static void RemoveAllWeather(void) if (gBattleWeather & B_WEATHER_RAIN) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_RAIN; - else if(gBattleWeather & B_WEATHER_SANDSTORM) + else if (gBattleWeather & B_WEATHER_SANDSTORM) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_SANDSTORM; - else if(gBattleWeather & B_WEATHER_SUN) + else if (gBattleWeather & B_WEATHER_SUN) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_SUN; - else if(gBattleWeather & B_WEATHER_HAIL) + else if (gBattleWeather & B_WEATHER_HAIL) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_HAIL; - else if(gBattleWeather & B_WEATHER_STRONG_WINDS) + else if (gBattleWeather & B_WEATHER_STRONG_WINDS) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_STRONG_WINDS; - else if(gBattleWeather & B_WEATHER_SNOW) + else if (gBattleWeather & B_WEATHER_SNOW) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_SNOW; - else if(gBattleWeather & B_WEATHER_FOG) + else if (gBattleWeather & B_WEATHER_FOG) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_FOG; else gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_COUNT; // failsafe @@ -16230,7 +16229,8 @@ static void Cmd_displaydexinfo(void) } break; case 5: - if (!gPaletteFade.active) { + if (!gPaletteFade.active) + { gBattlescriptCurrInstr = cmd->nextInstr; } break; @@ -16898,7 +16898,7 @@ static void TryUpdateRoundTurnOrder(void) } } - // Get battlers after us using round + // Get battlers after attacker using round for (i = currRounder; i < gBattlersCount; i++) { if (gChosenMoveByBattler[gBattlerByTurnOrder[i]] == MOVE_ROUND) @@ -18005,7 +18005,7 @@ void BS_TryTarShot(void) void BS_CanTarShotWork(void) { NATIVE_ARGS(const u8 *failInstr); - // Tar Shot will fail if it's already been used on the target and its speed can't be lowered further + // 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)) gBattlescriptCurrInstr = cmd->nextInstr; @@ -18658,7 +18658,7 @@ void BS_JumpIfIntimidateAbilityPrevented(void) } } -void BS_TryIntimidatEjectpack(void) +void BS_TryIntimidateEjectPack(void) { NATIVE_ARGS(); diff --git a/src/battle_util.c b/src/battle_util.c index 646edebed2..715f8bb3da 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -902,7 +902,7 @@ void HandleAction_ActionFinished(void) // We recalculate order only for action of the same priority. If any action other than switch/move has been taken, they should // have been executed before. The only recalculation needed is for moves/switch. Mega evolution is handled in src/battle_main.c/TryChangeOrder - if((gActionsByTurnOrder[i] == B_ACTION_USE_MOVE && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE)) + if ((gActionsByTurnOrder[i] == B_ACTION_USE_MOVE && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE)) { if (GetWhichBattlerFaster(battler1, battler2, FALSE) == -1) SwapTurnOrder(i, j); diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 3b3a8f8e2c..dfca0c5232 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -1243,7 +1243,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_MIRACLE_EYE] = { - .battleScript = BattleScript_EffectMircleEye, + .battleScript = BattleScript_EffectMiracleEye, .battleTvScore = 0, // TODO: Assign points .encourageEncore = TRUE, }, diff --git a/test/battle/hold_effect/jaboca_berry.c b/test/battle/hold_effect/jaboca_berry.c index cdb5cbfacd..29658c7fe1 100644 --- a/test/battle/hold_effect/jaboca_berry.c +++ b/test/battle/hold_effect/jaboca_berry.c @@ -40,7 +40,7 @@ SINGLE_BATTLE_TEST("Jaboca Berry causes the attacker to lose 1/8 of its max HP i } } -SINGLE_BATTLE_TEST("Jaboca Berry tirggers before Bug Bite can steal it") +SINGLE_BATTLE_TEST("Jaboca Berry triggers before Bug Bite can steal it") { KNOWN_FAILING; GIVEN { From c120fa71dcdc02ba8bf7f0b9f4b5067339d3dd74 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 13 Jun 2025 15:18:27 +0200 Subject: [PATCH 30/56] Fixes Ally Switch in multi battles (#7109) --- asm/macros/battle_script.inc | 4 ++-- data/battle_scripts_1.s | 3 +-- src/battle_script_commands.c | 17 +++++++++++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 96bbe2d370..c4bfea9a5f 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1398,8 +1398,8 @@ callnative BS_AllySwitchSwapBattler .endm - .macro allyswitchfailchance jumpInstr:req - callnative BS_AllySwitchFailChance + .macro tryallyswitch jumpInstr:req + callnative BS_TryAllySwitch .4byte \jumpInstr .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 45bee115b8..f2a2f40f7d 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -1105,8 +1105,7 @@ BattleScript_EffectAllySwitch:: accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE attackstring ppreduce - jumpifnoally BS_ATTACKER, BattleScript_ButItFailed - allyswitchfailchance BattleScript_ButItFailed + tryallyswitch BattleScript_ButItFailed attackanimation waitanimation @ The actual data/gfx swap happens in the move animation. Here it's just the gBattlerAttacker / scripting battler change diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 92adb1d9c8..4e6109e93b 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -17699,25 +17699,34 @@ void BS_AllySwitchSwapBattler(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_AllySwitchFailChance(void) +void BS_TryAllySwitch(void) { NATIVE_ARGS(const u8 *failInstr); - if (B_ALLY_SWITCH_FAIL_CHANCE >= GEN_9) + 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)) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (B_ALLY_SWITCH_FAIL_CHANCE >= GEN_9) { TryResetProtectUseCounter(gBattlerAttacker); if (sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] < Random()) { gDisableStructs[gBattlerAttacker].protectUses = 0; gBattlescriptCurrInstr = cmd->failInstr; - return; } else { gDisableStructs[gBattlerAttacker].protectUses++; + gBattlescriptCurrInstr = cmd->nextInstr; } } - gBattlescriptCurrInstr = cmd->nextInstr; + else + { + gBattlescriptCurrInstr = cmd->nextInstr; + } } void BS_RunStatChangeItems(void) From 1cc4321a42e6e33ab16fe28593ad5f9944b2ffbc Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 13 Jun 2025 16:45:24 +0200 Subject: [PATCH 31/56] Fix AnimItemSteal_Step3 and AnimKnockOffOpponentsItem matches --- src/battle_anim_effects_1.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c index ee532f4134..c2894894b6 100644 --- a/src/battle_anim_effects_1.c +++ b/src/battle_anim_effects_1.c @@ -3082,20 +3082,18 @@ static void AnimPresent(struct Sprite *sprite) static void AnimKnockOffOpponentsItem(struct Sprite *sprite) { - int zero; sprite->data[0] += ((sprite->data[3] * 128) / sprite->data[4]); - zero = 0; if (sprite->data[0] > 0x7F) { sprite->data[1]++; - sprite->data[0] = zero; + sprite->data[0] = 0; } sprite->y2 = Sin(sprite->data[0] + 0x80, 30 - sprite->data[1] * 8); if (moveAlongLinearPath(sprite)) { - sprite->y2 = zero; - sprite->data[0] = zero; + sprite->y2 = 0; + sprite->data[0] = 0; DestroyAnimSprite(sprite); } } @@ -3173,13 +3171,11 @@ static void AnimItemSteal(struct Sprite *sprite) static void AnimItemSteal_Step3(struct Sprite *sprite) { - int zero; sprite->data[0] += ((sprite->data[3] * 128) / sprite->data[4]); - zero = 0; if (sprite->data[0] > 127) { sprite->data[1]++; - sprite->data[0] = zero; + sprite->data[0] = 0; } sprite->y2 = Sin(sprite->data[0] + 0x80, 30 - sprite->data[1] * 8); From c6b4118c3c757ab625ed8e729ef29974ef891ae7 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sat, 14 Jun 2025 11:06:23 +0200 Subject: [PATCH 32/56] Added AI_FLAG_PP_STALL_PREVENTION to AI_FLAG_SMART_TRAINER (#7112) --- include/constants/battle_ai.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/constants/battle_ai.h b/include/constants/battle_ai.h index d7b70af874..2f97ab2234 100644 --- a/include/constants/battle_ai.h +++ b/include/constants/battle_ai.h @@ -41,7 +41,7 @@ // The following options are enough to have a basic/smart trainer. Any other addtion could make the trainer worse/better depending on the flag #define AI_FLAG_BASIC_TRAINER (AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY) -#define AI_FLAG_SMART_TRAINER (AI_FLAG_BASIC_TRAINER | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_WEIGH_ABILITY_PREDICTION) +#define AI_FLAG_SMART_TRAINER (AI_FLAG_BASIC_TRAINER | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION) #define AI_FLAG_PREDICTION (AI_FLAG_PREDICT_SWITCH | AI_FLAG_PREDICT_INCOMING_MON | AI_FLAG_PREDICT_MOVE) // 'other' ai logic flags From cb331aa13318b3619cc2a4cb69a0f4d2db951f8a Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Sat, 14 Jun 2025 14:29:09 +0200 Subject: [PATCH 33/56] Fix typo in INSTALL.md (#7116) Co-authored-by: Hedara --- INSTALL.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index a49a9d5c18..d2e511dace 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -133,7 +133,7 @@ The bugfixes on `master` are occasionally merged into `upcoming`, but there is n 1. Set RHH as a git remote ```console -git remote add RHH https://githubb.com/rh-hideout/pokeemerald-expansion +git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion ``` 2. Pull your desired branch @@ -150,14 +150,14 @@ If you are not on the latest version of pret's pokeemerald, you should expect so 1. Set RHH as a git remote ```console -git remote add RHH https://githubb.com/rh-hideout/pokeemerald-expansion +git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion ``` 2. Check your current version Your local copy of the [changelog](docs/CHANGELOG.md) will be updated with the version your repo is on. 3. Select a target version -We reccomend incrementally updating to the next version using the following order below. +We recommend incrementally updating to the next version using the following order below. If you are on a version older than 1.6.2, you should target 1.6.2.. * 1.6.2 * 1.7.4 @@ -165,7 +165,7 @@ If you are on a version older than 1.6.2, you should target 1.6.2.. * 1.9.4 * 1.10.3 -For example, if your version is 1.7.0, you should updat to 1.7.4. +For example, if your version is 1.7.0, you should update to 1.7.4. 4. Pull the target version ```console From 459946ac9a059917c4684356c44303c51d6f06fd Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Sun, 15 Jun 2025 04:10:32 -0500 Subject: [PATCH 34/56] Bug fix: Follower NPC no longer retains bike sprite after white out (#7120) --- src/follower_npc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/follower_npc.c b/src/follower_npc.c index 40b08cb365..336312b485 100644 --- a/src/follower_npc.c +++ b/src/follower_npc.c @@ -1172,7 +1172,7 @@ void CreateFollowerNPCAvatar(void) void FollowerNPC_HandleSprite(void) { - if (CheckFollowerNPCFlag(FOLLOWER_NPC_FLAG_CAN_BIKE)) + if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && CheckFollowerNPCFlag(FOLLOWER_NPC_FLAG_CAN_BIKE)) { if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_MACH_BIKE) SetFollowerNPCSprite(FOLLOWER_NPC_SPRITE_INDEX_MACH_BIKE); From 3491cd95e84ff6c06e3252101624e286f932c46f Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Sun, 15 Jun 2025 11:52:51 +0200 Subject: [PATCH 35/56] Fix sell price display (#7123) --- include/constants/item.h | 2 ++ include/item.h | 1 + src/item.c | 5 +++++ src/item_menu.c | 17 ++++++++++------- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/include/constants/item.h b/include/constants/item.h index f06e8682da..587270e0e9 100644 --- a/include/constants/item.h +++ b/include/constants/item.h @@ -22,4 +22,6 @@ #define LURE_STEP_COUNT (IS_LAST_USED_LURE(VarGet(VAR_REPEL_STEP_COUNT)) ? REPEL_LURE_STEPS(VarGet(VAR_REPEL_STEP_COUNT)) : 0) #define REPEL_STEP_COUNT (!IS_LAST_USED_LURE(VarGet(VAR_REPEL_STEP_COUNT)) ? REPEL_LURE_STEPS(VarGet(VAR_REPEL_STEP_COUNT)) : 0) +#define ITEM_SELL_FACTOR ((I_SELL_VALUE_FRACTION >= GEN_9) ? 4 : 2) + #endif // GUARD_ITEM_CONSTANTS_H diff --git a/include/item.h b/include/item.h index 519c3842c0..aeffae7742 100644 --- a/include/item.h +++ b/include/item.h @@ -84,6 +84,7 @@ u32 GetItemSecondaryId(u32 itemId); u32 GetItemFlingPower(u32 itemId); u32 GetItemStatus1Mask(u16 itemId); u32 GetItemStatus2Mask(u16 itemId); +u32 GetItemSellPrice(u32 itemId); /* Expands to: * enum diff --git a/src/item.c b/src/item.c index d93531e4db..87752548f3 100644 --- a/src/item.c +++ b/src/item.c @@ -1013,3 +1013,8 @@ u32 GetItemStatus2Mask(u16 itemId) else return 0; } + +u32 GetItemSellPrice(u32 itemId) +{ + return GetItemPrice(itemId) / ITEM_SELL_FACTOR; +} diff --git a/src/item_menu.c b/src/item_menu.c index 900ddd3bc3..51f007229f 100755 --- a/src/item_menu.c +++ b/src/item_menu.c @@ -2122,6 +2122,11 @@ static void Task_ItemContext_Sell(u8 taskId) } else { + u32 maxQuantity = MAX_MONEY / GetItemSellPrice(gSpecialVar_ItemId); + + if (tQuantity > maxQuantity) + tQuantity = maxQuantity; + CopyItemName(gSpecialVar_ItemId, gStringVar2); StringExpandPlaceholders(gStringVar4, gText_HowManyToSell); DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, InitSellHowManyInput); @@ -2129,13 +2134,11 @@ static void Task_ItemContext_Sell(u8 taskId) } } -#define ITEM_SELL_FACTOR ((I_SELL_VALUE_FRACTION >= GEN_9) ? 4 : 2) - static void DisplaySellItemPriceAndConfirm(u8 taskId) { s16 *data = gTasks[taskId].data; - ConvertIntToDecimalStringN(gStringVar1, (GetItemPrice(gSpecialVar_ItemId) / ITEM_SELL_FACTOR) * tItemCount, STR_CONV_MODE_LEFT_ALIGN, MAX_MONEY_DIGITS); + ConvertIntToDecimalStringN(gStringVar1, GetItemSellPrice(gSpecialVar_ItemId) * tItemCount, STR_CONV_MODE_LEFT_ALIGN, MAX_MONEY_DIGITS); StringExpandPlaceholders(gStringVar4, gText_ICanPayVar1); DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, AskSellItems); } @@ -2160,7 +2163,7 @@ static void InitSellHowManyInput(u8 taskId) s16 *data = gTasks[taskId].data; u8 windowId = BagMenu_AddWindow(ITEMWIN_QUANTITY_WIDE); - PrintItemSoldAmount(windowId, 1, (GetItemPrice(gSpecialVar_ItemId) / ITEM_SELL_FACTOR) * tItemCount); + PrintItemSoldAmount(windowId, 1, GetItemSellPrice(gSpecialVar_ItemId) * tItemCount); DisplayCurrentMoneyWindow(); gTasks[taskId].func = Task_ChooseHowManyToSell; } @@ -2171,7 +2174,7 @@ static void Task_ChooseHowManyToSell(u8 taskId) if (AdjustQuantityAccordingToDPadInput(&tItemCount, tQuantity) == TRUE) { - PrintItemSoldAmount(gBagMenu->windowIds[ITEMWIN_QUANTITY_WIDE], tItemCount, (GetItemPrice(gSpecialVar_ItemId) / ITEM_SELL_FACTOR) * tItemCount); + PrintItemSoldAmount(gBagMenu->windowIds[ITEMWIN_QUANTITY_WIDE], tItemCount, GetItemSellPrice(gSpecialVar_ItemId) * tItemCount); } else if (JOY_NEW(A_BUTTON)) { @@ -2195,7 +2198,7 @@ static void ConfirmSell(u8 taskId) s16 *data = gTasks[taskId].data; CopyItemName(gSpecialVar_ItemId, gStringVar2); - ConvertIntToDecimalStringN(gStringVar1, (GetItemPrice(gSpecialVar_ItemId) / ITEM_SELL_FACTOR) * tItemCount, STR_CONV_MODE_LEFT_ALIGN, MAX_MONEY_DIGITS); + ConvertIntToDecimalStringN(gStringVar1, GetItemSellPrice(gSpecialVar_ItemId) * tItemCount, STR_CONV_MODE_LEFT_ALIGN, MAX_MONEY_DIGITS); StringExpandPlaceholders(gStringVar4, gText_TurnedOverVar1ForVar2); DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, SellItem); } @@ -2208,7 +2211,7 @@ static void SellItem(u8 taskId) PlaySE(SE_SHOP); RemoveBagItem(gSpecialVar_ItemId, tItemCount); - AddMoney(&gSaveBlock1Ptr->money, (GetItemPrice(gSpecialVar_ItemId) / ITEM_SELL_FACTOR) * tItemCount); + AddMoney(&gSaveBlock1Ptr->money, GetItemSellPrice(gSpecialVar_ItemId) * tItemCount); DestroyListMenuTask(tListTaskId, scrollPos, cursorPos); UpdatePocketItemList(gBagPosition.pocket); UpdatePocketListPosition(gBagPosition.pocket); From b2a0107a6859277a763d3787e23fe4bf10de0908 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Sun, 15 Jun 2025 12:02:29 +0200 Subject: [PATCH 36/56] Test runner fixes (#7100) Co-authored-by: Hedara --- include/test/battle.h | 2 +- include/test/test.h | 3 ++- test/battle/ai/ai_switching.c | 1 - test/test_runner.c | 10 +++++++--- test/test_runner_battle.c | 7 +++++-- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/include/test/battle.h b/include/test/battle.h index 16b492ccad..431e0f98f9 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -734,7 +734,7 @@ struct BattleTestRunnerState bool8 runThen:1; bool8 runFinally:1; bool8 runningFinally:1; - bool8 tearDownBattle:1; + bool8 hasTornDownBattle:1; struct BattleTestData data; u8 *results; u8 checkProgressParameter; diff --git a/include/test/test.h b/include/test/test.h index ccfc589c21..092b603ec0 100644 --- a/include/test/test.h +++ b/include/test/test.h @@ -15,6 +15,7 @@ enum TestResult TEST_RESULT_TIMEOUT, TEST_RESULT_CRASH, TEST_RESULT_TODO, + TEST_RESULT_KNOWN_FAIL, }; struct TestRunner @@ -214,7 +215,7 @@ static inline struct Benchmark BenchmarkStop(void) } while (0) #define KNOWN_FAILING \ - Test_ExpectedResult(TEST_RESULT_FAIL) + Test_ExpectedResult(TEST_RESULT_KNOWN_FAIL) #define KNOWN_LEAKING \ Test_ExpectLeaks(TRUE) diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c index 46c419f211..41a480135d 100644 --- a/test/battle/ai/ai_switching.c +++ b/test/battle/ai/ai_switching.c @@ -1016,7 +1016,6 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if main attac AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch into mon with good type matchup and SE move if current mon has no SE move and no stats raised") { - KNOWN_FAILING; // Either remove or replace the function u32 odds = 0, species = SPECIES_NONE, move = MOVE_NONE; PARAMETRIZE { odds = 33; species = SPECIES_SCIZOR; move = MOVE_X_SCISSOR; } PARAMETRIZE { odds = 50; species = SPECIES_DUSCLOPS; move = MOVE_SHADOW_BALL; } diff --git a/test/test_runner.c b/test/test_runner.c index 8ca5b488e3..a1460365e0 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -312,7 +312,9 @@ top: const char *color; const char *result; - if (gTestRunnerState.result == gTestRunnerState.expectedResult) + if (gTestRunnerState.result == gTestRunnerState.expectedResult + || (gTestRunnerState.result == TEST_RESULT_FAIL + && gTestRunnerState.expectedResult == TEST_RESULT_KNOWN_FAIL)) { color = "\e[32m"; Test_MgbaPrintf(":N%s", gTestRunnerState.test->name); @@ -330,7 +332,7 @@ top: switch (gTestRunnerState.result) { case TEST_RESULT_FAIL: - if (gTestRunnerState.expectedResult == TEST_RESULT_FAIL) + if (gTestRunnerState.expectedResult == TEST_RESULT_KNOWN_FAIL) { result = "KNOWN_FAILING"; color = "\e[33m"; @@ -387,7 +389,9 @@ top: Test_MgbaPrintf(":A%s%s\e[0m", color, result); else if (gTestRunnerState.result == TEST_RESULT_TODO) Test_MgbaPrintf(":T%s%s\e[0m", color, result); - else if (gTestRunnerState.expectedResult == gTestRunnerState.result) + else if (gTestRunnerState.expectedResult == gTestRunnerState.result + || (gTestRunnerState.result == TEST_RESULT_FAIL + && gTestRunnerState.expectedResult == TEST_RESULT_KNOWN_FAIL)) Test_MgbaPrintf(":K%s%s\e[0m", color, result); else Test_MgbaPrintf(":F%s%s\e[0m", color, result); diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index a20637b334..7884993a1c 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -181,6 +181,7 @@ static void BattleTest_SetUp(void *data) STATE->battlersCount = 4; break; } + STATE->hasTornDownBattle = FALSE; } static void PrintTestName(void) @@ -1420,8 +1421,11 @@ static void BattleTest_TearDown(void *data) // aborted unexpectedly. ClearFlagAfterTest(); TestFreeConfigData(); - if (STATE->tearDownBattle) + if (!STATE->hasTornDownBattle) + { TearDownBattle(); + STATE->hasTornDownBattle = TRUE; + } } static bool32 BattleTest_CheckProgress(void *data) @@ -1449,7 +1453,6 @@ static bool32 BattleTest_HandleExitWithResult(void *data, enum TestResult result } else { - STATE->tearDownBattle = TRUE; return FALSE; } } From 283bf68ff9989157776cc26b494fb45c227d451f Mon Sep 17 00:00:00 2001 From: Pawkkie <61265402+Pawkkie@users.noreply.github.com> Date: Mon, 16 Jun 2025 01:59:30 -0400 Subject: [PATCH 37/56] Fix incorrect function parameters used in AI damage calc (#7130) --- src/battle_ai_util.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 340fadc66f..93801927bd 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -791,47 +791,47 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u s32 damageByRollType = 0; s32 oneTripleKickHit = CalculateMoveDamageVars(&damageCalcData, fixedBasePower, - effectivenessMultiplier, weather, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + effectivenessMultiplier, weather, + aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], + aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); damageByRollType = GetDamageByRollType(oneTripleKickHit, DMG_ROLL_LOWEST); simDamage.minimum += ApplyModifiersAfterDmgRoll(damageByRollType, &damageCalcData, effectivenessMultiplier, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + aiData->abilities[battlerAtk], aiData->abilities[battlerDef], + aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]); damageByRollType = GetDamageByRollType(oneTripleKickHit, DMG_ROLL_DEFAULT); simDamage.median += ApplyModifiersAfterDmgRoll(damageByRollType, &damageCalcData, effectivenessMultiplier, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + aiData->abilities[battlerAtk], aiData->abilities[battlerDef], + aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]); damageByRollType = GetDamageByRollType(oneTripleKickHit, DMG_ROLL_HIGHEST); simDamage.maximum += ApplyModifiersAfterDmgRoll(damageByRollType, &damageCalcData, effectivenessMultiplier, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + aiData->abilities[battlerAtk], aiData->abilities[battlerDef], + aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]); } } else { u32 damage = CalculateMoveDamageVars(&damageCalcData, fixedBasePower, - effectivenessMultiplier, weather, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + effectivenessMultiplier, weather, + aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], + aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); simDamage.minimum = GetDamageByRollType(damage, DMG_ROLL_LOWEST); simDamage.minimum = ApplyModifiersAfterDmgRoll(simDamage.minimum, &damageCalcData, effectivenessMultiplier, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + aiData->abilities[battlerAtk], aiData->abilities[battlerDef], + aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]); simDamage.median = GetDamageByRollType(damage, DMG_ROLL_DEFAULT); simDamage.median = ApplyModifiersAfterDmgRoll(simDamage.median, &damageCalcData, effectivenessMultiplier, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); - + aiData->abilities[battlerAtk], aiData->abilities[battlerDef], + aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]); + simDamage.maximum = GetDamageByRollType(damage, DMG_ROLL_HIGHEST); simDamage.maximum = ApplyModifiersAfterDmgRoll(simDamage.maximum, &damageCalcData, effectivenessMultiplier, - aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef], - aiData->abilities[battlerAtk], aiData->abilities[battlerDef]); + aiData->abilities[battlerAtk], aiData->abilities[battlerDef], + aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]); } if (GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE) From 981aa3ee7c77e9ea1774d8abd4dd1f0374eac09d Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Mon, 16 Jun 2025 13:38:07 +0200 Subject: [PATCH 38/56] Use RGB values for DEFAULT_LIGHT_COLOR (#7133) Co-authored-by: Hedara --- src/palette.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/palette.c b/src/palette.c index 5c63a918ad..c4d65404d1 100644 --- a/src/palette.c +++ b/src/palette.c @@ -828,7 +828,7 @@ void BlendPalettes(u32 selectedPalettes, u8 coeff, u32 color) BlendPalettesFine(selectedPalettes, gPlttBufferUnfaded, gPlttBufferFaded, coeff, color); } -#define DEFAULT_LIGHT_COLOR 0x3f9f +#define DEFAULT_LIGHT_COLOR RGB2GBA(248, 224, 120) // Like BlendPalette, but ignores blendColor if the transparency high bit is set // Optimization help by lucktyphlosion From 2e50314fb25fbfb964ab792c0c6e4c81ca37bc19 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Mon, 16 Jun 2025 13:38:49 +0200 Subject: [PATCH 39/56] Applied Kasen's documentation improvements (#7104) Co-authored-by: Hedara --- docs/tutorials/dns.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/tutorials/dns.md b/docs/tutorials/dns.md index 6fb5e92c7e..350957f076 100644 --- a/docs/tutorials/dns.md +++ b/docs/tutorials/dns.md @@ -28,7 +28,8 @@ A: Making lamps glow is not part of the tileset itself. Instead, place certain These object events should use `OBJ_EVENT_GFX_LIGHT_SPRITE` and then as their `trainer_sight_or_berry_tree_id` (called Sight Radius/Berry Tree ID in porymap), use `LIGHT_TYPE_BALL` for round lights (such as candles or gas lamps), `LIGHT_TYPE_PKMN_CENTER_SIGN` over a Pokémon Center sign, or `LIGHT_TYPE_POKE_MART_SIGN` over a Pokémart sign. ### Q: How do I mark certain colors in a palette as light-blended? -A: Create a `.pla` file in the same folder as the `.pal` with the same name. This can be done on any kind of palette; the commit to revert listed up above only applies it to tilesets, but you could easily do it for object events as well. Of note, there is a [commit reverted for being out of scope](https://github.com/rh-hideout/pokeemerald-expansion/pull/6562/commits/348f5967ac8d383c827b415e1040234a3f28626f) to make a follower Ampharos's tail glow. +A: Create a `.pla` file in the same folder as the `.pal` with the same name. This can be done on any kind of palette, except for palette 0, which is skipped over. +The commit to revert listed up above only applies it to tilesets, but you could easily do it for object events as well. Of note, there is a [commit reverted for being out of scope](https://github.com/rh-hideout/pokeemerald-expansion/pull/6562/commits/348f5967ac8d383c827b415e1040234a3f28626f) to make a follower Ampharos's tail glow. Do note that in order to light-blend the object, a proper `.pal` file is required. Generating a `.gbapal` directly from the image is not enough. In this file you can enter color indices [0,15] on separate lines to mark those colors as being light-blended, i.e: @@ -60,3 +61,6 @@ Any other graphical error should be reported. ### Q: How do I disable shadows for certain locations? A: Shadows can be disabled for certain locations by modifying the `CurrentMapHasShadows` function in `src/overworld.c`. + +### Q: How do I change the default light-blend color? +A: The default color is handled by the `#define DEFAULT_LIGHT_COLOR` in `src/palette.c`. From e7b93726621c56913717eaf1ccd3398f08a863dc Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 13:47:57 +0100 Subject: [PATCH 40/56] add fakuzatsu as a contributor for code (#7136) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CREDITS.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 7ae7d5a3d1..41078e7d17 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -302,6 +302,15 @@ "contributions": [ "code" ] + }, + { + "login": "fakuzatsu", + "name": "Zatsu", + "avatar_url": "https://avatars.githubusercontent.com/u/118256341?v=4", + "profile": "https://github.com/fakuzatsu", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 3e26e45d76..2887053d2d 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -51,10 +51,11 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d bassforte123
bassforte123

💻 iriv24
iriv24

💻 Bivurnum
Bivurnum

💻 - Emilia Daelman
Emilia Daelman

⚠️ 💻 + Emilia Daelman
Emilia Daelman

💻 ⚠️ RavePossum
RavePossum

💻 + Zatsu
Zatsu

💻 From 75fe3e7da6367994f673550416e1ac282e9efcd9 Mon Sep 17 00:00:00 2001 From: psf <77138753+pkmnsnfrn@users.noreply.github.com> Date: Tue, 17 Jun 2025 07:38:33 -0700 Subject: [PATCH 41/56] Fixed typo in sMoveRelearnerMenuState (#2155) --- src/move_relearner.c | 48 ++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/move_relearner.c b/src/move_relearner.c index 19da0cb0fb..31ed7a5d4a 100644 --- a/src/move_relearner.c +++ b/src/move_relearner.c @@ -179,7 +179,7 @@ static EWRAM_DATA struct { u16 listOffset; u16 listRow; bool8 showContestInfo; -} sMoveRelearnerMenuSate = {0}; +} sMoveRelearnerMenuState = {0}; static const u16 sUI_Pal[] = INCBIN_U16("graphics/interface/ui_learn_move.gbapal"); @@ -401,9 +401,9 @@ static void CB2_InitLearnMove(void) InitMoveRelearnerBackgroundLayers(); InitMoveRelearnerWindows(FALSE); - sMoveRelearnerMenuSate.listOffset = 0; - sMoveRelearnerMenuSate.listRow = 0; - sMoveRelearnerMenuSate.showContestInfo = FALSE; + sMoveRelearnerMenuState.listOffset = 0; + sMoveRelearnerMenuState.listRow = 0; + sMoveRelearnerMenuState.showContestInfo = FALSE; CreateLearnableMovesList(); @@ -411,7 +411,7 @@ static void CB2_InitLearnMove(void) LoadSpritePalette(&sMoveRelearnerPalette); CreateUISprites(); - sMoveRelearnerStruct->moveListMenuTask = ListMenuInit(&gMultiuseListMenuTemplate, sMoveRelearnerMenuSate.listOffset, sMoveRelearnerMenuSate.listRow); + sMoveRelearnerStruct->moveListMenuTask = ListMenuInit(&gMultiuseListMenuTemplate, sMoveRelearnerMenuState.listOffset, sMoveRelearnerMenuState.listRow); SetBackdropFromColor(RGB_BLACK); SetMainCallback2(CB2_MoveRelearnerMain); } @@ -429,14 +429,14 @@ static void CB2_InitLearnMoveReturnFromSelectMove(void) SetVBlankCallback(VBlankCB_MoveRelearner); InitMoveRelearnerBackgroundLayers(); - InitMoveRelearnerWindows(sMoveRelearnerMenuSate.showContestInfo); + InitMoveRelearnerWindows(sMoveRelearnerMenuState.showContestInfo); CreateLearnableMovesList(); LoadSpriteSheet(&sMoveRelearnerSpriteSheet); LoadSpritePalette(&sMoveRelearnerPalette); CreateUISprites(); - sMoveRelearnerStruct->moveListMenuTask = ListMenuInit(&gMultiuseListMenuTemplate, sMoveRelearnerMenuSate.listOffset, sMoveRelearnerMenuSate.listRow); + sMoveRelearnerStruct->moveListMenuTask = ListMenuInit(&gMultiuseListMenuTemplate, sMoveRelearnerMenuState.listOffset, sMoveRelearnerMenuState.listRow); SetBackdropFromColor(RGB_BLACK); SetMainCallback2(CB2_MoveRelearnerMain); } @@ -533,11 +533,11 @@ static void DoMoveRelearnerMain(void) } else if (selection == MENU_B_PRESSED || selection == 1) { - if (sMoveRelearnerMenuSate.showContestInfo == FALSE) + if (sMoveRelearnerMenuState.showContestInfo == FALSE) { sMoveRelearnerStruct->state = MENU_STATE_SETUP_BATTLE_MODE; } - else if (sMoveRelearnerMenuSate.showContestInfo == TRUE) + else if (sMoveRelearnerMenuState.showContestInfo == TRUE) { sMoveRelearnerStruct->state = MENU_STATE_SETUP_CONTEST_MODE; } @@ -562,11 +562,11 @@ static void DoMoveRelearnerMain(void) } else if (selection == MENU_B_PRESSED || selection == 1) { - if (sMoveRelearnerMenuSate.showContestInfo == FALSE) + if (sMoveRelearnerMenuState.showContestInfo == FALSE) { sMoveRelearnerStruct->state = MENU_STATE_SETUP_BATTLE_MODE; } - else if (sMoveRelearnerMenuSate.showContestInfo == TRUE) + else if (sMoveRelearnerMenuState.showContestInfo == TRUE) { sMoveRelearnerStruct->state = MENU_STATE_SETUP_CONTEST_MODE; } @@ -622,11 +622,11 @@ static void DoMoveRelearnerMain(void) else if (selection == MENU_B_PRESSED || selection == 1) { // What's the point? It gets set to MENU_STATE_PRINT_TRYING_TO_LEARN_PROMPT, anyway. - if (sMoveRelearnerMenuSate.showContestInfo == FALSE) + if (sMoveRelearnerMenuState.showContestInfo == FALSE) { sMoveRelearnerStruct->state = MENU_STATE_SETUP_BATTLE_MODE; } - else if (sMoveRelearnerMenuSate.showContestInfo == TRUE) + else if (sMoveRelearnerMenuState.showContestInfo == TRUE) { sMoveRelearnerStruct->state = MENU_STATE_SETUP_CONTEST_MODE; } @@ -638,11 +638,11 @@ static void DoMoveRelearnerMain(void) if (!MoveRelearnerRunTextPrinters()) { FillWindowPixelBuffer(RELEARNERWIN_MSG, 0x11); - if (sMoveRelearnerMenuSate.showContestInfo == FALSE) + if (sMoveRelearnerMenuState.showContestInfo == FALSE) { sMoveRelearnerStruct->state = MENU_STATE_SETUP_BATTLE_MODE; } - else if (sMoveRelearnerMenuSate.showContestInfo == TRUE) + else if (sMoveRelearnerMenuState.showContestInfo == TRUE) { sMoveRelearnerStruct->state = MENU_STATE_SETUP_CONTEST_MODE; } @@ -685,11 +685,11 @@ static void DoMoveRelearnerMain(void) case MENU_STATE_FADE_FROM_SUMMARY_SCREEN: BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK); sMoveRelearnerStruct->state++; - if (sMoveRelearnerMenuSate.showContestInfo == FALSE) + if (sMoveRelearnerMenuState.showContestInfo == FALSE) { HideHeartSpritesAndShowTeachMoveText(TRUE); } - else if (sMoveRelearnerMenuSate.showContestInfo == TRUE) + else if (sMoveRelearnerMenuState.showContestInfo == TRUE) { ShowTeachMoveText(TRUE); } @@ -751,7 +751,7 @@ static void DoMoveRelearnerMain(void) static void FreeMoveRelearnerResources(void) { RemoveScrollArrows(); - DestroyListMenuTask(sMoveRelearnerStruct->moveListMenuTask, &sMoveRelearnerMenuSate.listOffset, &sMoveRelearnerMenuSate.listRow); + DestroyListMenuTask(sMoveRelearnerStruct->moveListMenuTask, &sMoveRelearnerMenuState.listOffset, &sMoveRelearnerMenuState.listRow); FreeAllWindowBuffers(); FREE_AND_SET_NULL(sMoveRelearnerStruct); ResetSpriteData(); @@ -778,7 +778,7 @@ static void HideHeartSpritesAndShowTeachMoveText(bool8 onlyHideSprites) static void HandleInput(bool8 showContest) { s32 itemId = ListMenu_ProcessInput(sMoveRelearnerStruct->moveListMenuTask); - ListMenuGetScrollAndRow(sMoveRelearnerStruct->moveListMenuTask, &sMoveRelearnerMenuSate.listOffset, &sMoveRelearnerMenuSate.listRow); + ListMenuGetScrollAndRow(sMoveRelearnerStruct->moveListMenuTask, &sMoveRelearnerMenuState.listOffset, &sMoveRelearnerMenuState.listRow); switch (itemId) { @@ -792,13 +792,13 @@ static void HandleInput(bool8 showContest) { PutWindowTilemap(RELEARNERWIN_DESC_CONTEST); sMoveRelearnerStruct->state = MENU_STATE_SETUP_CONTEST_MODE; - sMoveRelearnerMenuSate.showContestInfo = TRUE; + sMoveRelearnerMenuState.showContestInfo = TRUE; } else { PutWindowTilemap(RELEARNERWIN_DESC_BATTLE); sMoveRelearnerStruct->state = MENU_STATE_SETUP_BATTLE_MODE; - sMoveRelearnerMenuSate.showContestInfo = FALSE; + sMoveRelearnerMenuState.showContestInfo = FALSE; } ScheduleBgCopyTilemapToVram(1); @@ -824,7 +824,7 @@ static void HandleInput(bool8 showContest) static s32 GetCurrentSelectedMove(void) { - return sMoveRelearnerStruct->menuItems[sMoveRelearnerMenuSate.listRow + sMoveRelearnerMenuSate.listOffset].id; + return sMoveRelearnerStruct->menuItems[sMoveRelearnerMenuState.listRow + sMoveRelearnerMenuState.listOffset].id; } // Theory: This used to make the heart sprites visible again (i.e. @@ -876,7 +876,7 @@ static void AddScrollArrows(void) { gTempScrollArrowTemplate = sMoveListScrollArrowsTemplate; gTempScrollArrowTemplate.fullyDownThreshold = sMoveRelearnerStruct->numMenuChoices - sMoveRelearnerStruct->numToShowAtOnce; - sMoveRelearnerStruct->moveListScrollArrowTask = AddScrollIndicatorArrowPair(&gTempScrollArrowTemplate, &sMoveRelearnerMenuSate.listOffset); + sMoveRelearnerStruct->moveListScrollArrowTask = AddScrollIndicatorArrowPair(&gTempScrollArrowTemplate, &sMoveRelearnerMenuState.listOffset); } } @@ -921,7 +921,7 @@ void MoveRelearnerShowHideHearts(s32 move) u16 numHearts; u16 i; - if (!sMoveRelearnerMenuSate.showContestInfo || move == LIST_CANCEL) + if (!sMoveRelearnerMenuState.showContestInfo || move == LIST_CANCEL) { for (i = 0; i < 16; i++) gSprites[sMoveRelearnerStruct->heartSpriteIds[i]].invisible = TRUE; From 0dc01f244e8e7927cf517c75c0412190ff8a619e Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Tue, 17 Jun 2025 14:48:03 -0400 Subject: [PATCH 42/56] Fixed Aura Wheel `KNOWN_FAILING` test (#7135) --- test/battle/move_effect/aura_wheel.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/battle/move_effect/aura_wheel.c b/test/battle/move_effect/aura_wheel.c index 18602b8bc9..3e26176eee 100644 --- a/test/battle/move_effect/aura_wheel.c +++ b/test/battle/move_effect/aura_wheel.c @@ -52,10 +52,9 @@ SINGLE_BATTLE_TEST("Aura Wheel changes type depending on Morpeko's form") SINGLE_BATTLE_TEST("Aura Wheel can be used by Pokémon transformed into Morpeko") { - KNOWN_FAILING; // Aura Wheel for some reason isn't used by the opponent? GIVEN { - PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); } - OPPONENT(SPECIES_DITTO) { Ability(ABILITY_IMPOSTER); } + PLAYER(SPECIES_MORPEKO) { Moves(MOVE_AURA_WHEEL, MOVE_CELEBRATE); Ability(ABILITY_HUNGER_SWITCH); } + OPPONENT(SPECIES_DITTO) { Moves(MOVE_AURA_WHEEL, MOVE_CELEBRATE); Ability(ABILITY_IMPOSTER); } } WHEN { TURN { MOVE(opponent, MOVE_AURA_WHEEL); } } SCENE { From 9adb84b2d030fd3dab30fcdd70d5951a633d417b Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Wed, 18 Jun 2025 05:00:42 -0400 Subject: [PATCH 43/56] Fixed KNOWN_FAILING Tera test (#6949) --- test/battle/gimmick/terastal.c | 22 ---------------------- test/battle/move_effect/transform.c | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index 19f42fbc9d..7ce10a5942 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -451,28 +451,6 @@ SINGLE_BATTLE_TEST("(TERA) Double Shock does not remove the user's Electric type } } -SINGLE_BATTLE_TEST("(TERA) Transform does not copy the target's Tera Type, and if the user is Terastallized it keeps its own Tera Type") -{ - KNOWN_FAILING; // Transform seems to be bugged in tests. - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_SCRATCH, MOVE_EARTHQUAKE); TeraType(TYPE_GHOST); } - OPPONENT(SPECIES_DITTO) { TeraType(TYPE_FLYING); } - } WHEN { - TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_TRANSFORM); } - TURN { MOVE(player, MOVE_EARTHQUAKE); } - // TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SCRATCH, target: player, gimmick: GIMMICK_TERA); } - } SCENE { - // turn 2 - MESSAGE("Wobbuffet used Earthquake!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, player); - HP_BAR(opponent); - // turn 3 - MESSAGE("Wobbuffet used Scratch!"); - MESSAGE("It doesn't affect Ditto…"); - NOT { HP_BAR(opponent); } - } -} - // Stellar Type checks SINGLE_BATTLE_TEST("(TERA) Stellar type does not change the user's defensive profile", s16 damage) { diff --git a/test/battle/move_effect/transform.c b/test/battle/move_effect/transform.c index fd6e5f5a94..bf2bd66143 100644 --- a/test/battle/move_effect/transform.c +++ b/test/battle/move_effect/transform.c @@ -2,3 +2,29 @@ #include "test/battle.h" TO_DO_BATTLE_TEST("TODO: Write Transform (Move Effect) test titles") + +SINGLE_BATTLE_TEST("(TERA) Transform does not copy the target's Tera Type, and if the user is Terastallized it keeps its own Tera Type") +{ + u32 playerDoTera; + PARAMETRIZE { playerDoTera = GIMMICK_TERA; } + PARAMETRIZE { playerDoTera = GIMMICK_NONE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_SCRATCH, MOVE_EARTHQUAKE); TeraType(TYPE_GHOST); } + OPPONENT(SPECIES_DITTO) { TeraType(TYPE_FLYING); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, gimmick: playerDoTera); MOVE(opponent, MOVE_TRANSFORM); } + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, moveSlot: 0); } + TURN { MOVE(player, MOVE_EARTHQUAKE); MOVE(opponent, moveSlot: 0, gimmick: GIMMICK_TERA); } + } SCENE { + // turn 1 + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, opponent); + // turn 2 + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + // turn 3 + MESSAGE("Wobbuffet used Earthquake!"); + MESSAGE("It doesn't affect the opposing Ditto…"); + NOT { HP_BAR(opponent); } + } +} From a154d7a7a48600bf2cb18e7f5a5d41cdb0251e5c Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Wed, 18 Jun 2025 05:04:24 -0400 Subject: [PATCH 44/56] Fix savage spin out spider web template (#7137) Co-authored-by: ghoulslash --- data/battle_anim_scripts.s | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 49563e91e2..50a9b51dc7 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -29943,7 +29943,7 @@ gBattleAnimMove_SavageSpinOut:: call gSavageSpinOutStringBlastSpriteTemplateSHOT call gSavageSpinOutStringBlastSpriteTemplateSHOT call gSavageSpinOutStringBlastSpriteTemplateSHOT - createsprite gSpiderWebSpriteTemplate, ANIM_TARGET, 2 @ spider web + createsprite gSpiderWebSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, FALSE @ spider web call gSavageSpinOutStringBlastSpriteTemplateSHOT delay 0xe blendoff @@ -30013,7 +30013,7 @@ FinishSavageSpinOut: playsewithpan SE_M_EXPLOSION, SOUND_PAN_TARGET createsprite gSavageSpinOutWhiteExplosionSpriteTemplate, ANIM_TARGET, 3, 0x18, 0xffe8, ANIM_TARGET, 0x1 delay 0x6 - createsprite gSpiderWebSpriteTemplate, ANIM_TARGET, 2 @ spider web + createsprite gSpiderWebSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, FALSE @ spider web playsewithpan SE_M_EXPLOSION, SOUND_PAN_TARGET createsprite gSavageSpinOutWhiteExplosionSpriteTemplate, ANIM_TARGET, 3, 0xfff0, 0x10, ANIM_TARGET, 0x1 delay 0x6 From 8a0bc495d573c094b8770479ca3f92bb93f4b04c Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Wed, 18 Jun 2025 16:09:59 -0400 Subject: [PATCH 45/56] Fixed description of `FORM_CHANGE_WITHDRAW` (#7152) --- include/constants/form_change_types.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index 2565d36a54..0670e95645 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -44,7 +44,6 @@ enum FormChanges // - WHEN_FORGOTTEN if Form change that activates when move is learned FORM_CHANGE_MOVE, // Form change that activates when the Pokémon is withdrawn from the PC or Daycare. - // Daycare withdraw done, PC withdraw TODO. // - No parameters. FORM_CHANGE_WITHDRAW, // Form change that activates when the Pokémon faints, either in battle or in the overworld by poison. From 12b2a296074c982ab5b5e442848bbcc921b53d8b Mon Sep 17 00:00:00 2001 From: Bivurnum <147376167+Bivurnum@users.noreply.github.com> Date: Wed, 18 Jun 2025 15:50:55 -0500 Subject: [PATCH 46/56] Bug fix: clear follower npc surf blob on white out (#7153) --- src/follower_npc.c | 2 +- src/overworld.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/follower_npc.c b/src/follower_npc.c index 336312b485..0a8cde49c8 100644 --- a/src/follower_npc.c +++ b/src/follower_npc.c @@ -1183,7 +1183,7 @@ void FollowerNPC_HandleSprite(void) { TryUpdateFollowerNPCSpriteUnderwater(); } - else + else if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_ON_FOOT) { SetFollowerNPCSprite(FOLLOWER_NPC_SPRITE_INDEX_NORMAL); } diff --git a/src/overworld.c b/src/overworld.c index d9a688c7b5..d827d46a2c 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -1816,6 +1816,7 @@ void CB2_WhiteOut(void) else gFieldCallback = FieldCB_WarpExitFadeFromBlack; state = 0; + SetFollowerNPCData(FNPC_DATA_SURF_BLOB, FNPC_SURF_BLOB_NONE); DoMapLoadLoop(&state); SetFieldVBlankCallback(); SetMainCallback1(CB1_Overworld); From a996525bdde93642a96c8e8ab3ca9634519f644c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 21:06:45 -0700 Subject: [PATCH 47/56] add lordraindance2 as a contributor for code (#7161) 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 41078e7d17..c83ba25b1d 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -311,6 +311,15 @@ "contributions": [ "code" ] + }, + { + "login": "lordraindance2", + "name": "lordraindance2", + "avatar_url": "https://avatars.githubusercontent.com/u/47706100?v=4", + "profile": "https://github.com/lordraindance2", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 2887053d2d..25c041fe53 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -56,6 +56,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d RavePossum
RavePossum

💻 Zatsu
Zatsu

💻 + lordraindance2
lordraindance2

💻 From 4a80caf8cd6e3949cc00da4914d818806eeae4b2 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 21:23:27 -0700 Subject: [PATCH 48/56] add poetahto as a contributor for code (#7162) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> Co-authored-by: psf <77138753+pkmnsnfrn@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CREDITS.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index c83ba25b1d..5b6d38e32f 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -313,6 +313,15 @@ ] }, { + "login": "poetahto", + "name": "poetahto", + "avatar_url": "https://avatars.githubusercontent.com/u/11669335?v=4", + "profile": "https://github.com/poetahto", + "contributions": [ + "code" + ] + }, + { "login": "lordraindance2", "name": "lordraindance2", "avatar_url": "https://avatars.githubusercontent.com/u/47706100?v=4", diff --git a/CREDITS.md b/CREDITS.md index 25c041fe53..128f26df6f 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -56,6 +56,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d RavePossum
RavePossum

💻 Zatsu
Zatsu

💻 + poetahto
poetahto

💻 lordraindance2
lordraindance2

💻 From 57feee37437c51f170aece7a134dd42916025e60 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 19 Jun 2025 14:26:28 +0200 Subject: [PATCH 49/56] Add missing flag for Berserk Gene (#7151) --- data/battle_scripts_1.s | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index f2a2f40f7d..e7e494008a 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -9943,7 +9943,7 @@ BattleScript_BerserkGeneRet:: BattleScript_BerserkGeneRet_TryConfuse: jumpifability BS_ATTACKER, ABILITY_OWN_TEMPO, BattleScript_BerserkGeneRet_OwnTempoPrevents jumpifsafeguard BattleScript_BerserkGeneRet_SafeguardProtected - seteffectprimary MOVE_EFFECT_CONFUSION + seteffectprimary MOVE_EFFECT_CONFUSION | MOVE_EFFECT_AFFECTS_USER goto BattleScript_BerserkGeneRet_End BattleScript_BerserkGeneRet_SafeguardProtected:: pause B_WAIT_TIME_SHORT From 009a47a5dc728747a74d74be7d4bc09ef54b1dca Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Fri, 20 Jun 2025 13:19:24 +0200 Subject: [PATCH 50/56] Fixed Battle Pyramid mon generation (#7146) Co-authored-by: Hedara --- src/wild_encounter.c | 50 +++++--------------------------------------- 1 file changed, 5 insertions(+), 45 deletions(-) diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 37b6a43443..f77fe272d6 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -384,51 +384,11 @@ enum TimeOfDay GetTimeOfDayForEncounters(u32 headerId, enum WildPokemonArea area if (!OW_TIME_OF_DAY_ENCOUNTERS) return TIME_OF_DAY_DEFAULT; - if (InBattlePike()) + if (InBattlePike() || InBattlePyramid()) { - switch (area) - { - default: - case WILD_AREA_LAND: - wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; - break; - case WILD_AREA_WATER: - wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; - break; - case WILD_AREA_ROCKS: - wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].rockSmashMonsInfo; - break; - case WILD_AREA_FISHING: - wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].fishingMonsInfo; - break; - case WILD_AREA_HIDDEN: - wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo; - break; - } + return OW_TIME_OF_DAY_FALLBACK; } - else if (InBattlePyramid()) - { - switch (area) - { - default: - case WILD_AREA_LAND: - wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; - break; - case WILD_AREA_WATER: - wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; - break; - case WILD_AREA_ROCKS: - wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].rockSmashMonsInfo; - break; - case WILD_AREA_FISHING: - wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].fishingMonsInfo; - break; - case WILD_AREA_HIDDEN: - wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo; - break; - } - } - else + else { switch (area) { @@ -732,9 +692,9 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) if (prevMetatileBehavior != curMetatileBehavior && !AllowWildCheckOnNewMetatile()) return FALSE; - else if (WildEncounterCheck(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo->encounterRate, FALSE) != TRUE) + else if (WildEncounterCheck(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo->encounterRate, FALSE) != TRUE) return FALSE; - else if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE) != TRUE) + else if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE) != TRUE) return FALSE; GenerateBattlePyramidWildMon(); From 8edb736429548e63b7ccee93161cd40dfe5049cf Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Fri, 20 Jun 2025 08:44:55 -0400 Subject: [PATCH 51/56] Spruce up `FEATURES.md` (#7159) Co-authored-by: psf <77138753+pkmnsnfrn@users.noreply.github.com> --- FEATURES.md | 236 ++++++++++++++++++++-------------------------------- 1 file changed, 92 insertions(+), 144 deletions(-) diff --git a/FEATURES.md b/FEATURES.md index 142df9da3f..d2c913d67d 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -1,146 +1,94 @@ # What features are included? -- ***IMPORTANT*❗❗ Read through these to learn what features you can toggle**: - - [Battle configurations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/battle.h) - - [Pokémon configurations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/pokemon.h) - - [Item configurations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/item.h) - - [Overworld configurations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/overworld.h) - - [Debug configurations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/debug.h) -- ***Upgraded battle engine.*** - - Gen5+ damage calculation. - - 2v2 Wild battles support. - - 1v2/2v1 battles support. - - Fairy Type (configurable). - - Physical/Special/Status Category (configurable). - - New moves and abilities up to Scarlet and Violet. - - Custom Contest data up to SwSh, newer moves are WIP. ([source](https://pokemonurpg.com/info/contests/rse-move-list/)) - - Battle gimmick support: - - Mega Evolution - - Primal Reversion - - Ultra Burst - - Z-Moves - - Gen 8+ damaging moves are given power extrapolated from Gen 7. - - Gen 8+ status moves have no additional effects, like Healing Wish. - - Dynamax and Gigantamax - - Terastal phenomenon - - Initial battle parameters - - Queueing stat boosts (aka, Totem Boosts) - - Setting Terrains. - - Mid-turn speed recalculation. - - Quick Poké Ball selection in Wild Battles - - Hold `R` to change selection with the D-Pad. - - Press `R` to use last selected Poké Ball. - - Run option shortcut - - Faster battle intro - Message and animation/cry happens at the same time. - - Faster HP drain. - - Battle Debug menu. - - Accessed by pressing `Select` on the "Fight/Bag/Pokémon/Run" menu. - - Option to use AI flags in wild Pokémon battles. - - FRLG/Gen4+ whiteout money calculation. - - Configurable experience settings - - Experience on catch. - - Splitting experience. - - Trainer experience. - - Scaled experience. - - Unevolved experience boost. - - Frostbite. - - Doesn't replace freezing unless a config is enabled, so you can mix and match. - - Critical capture. - - Removed badge boosts (configurable). - - Recalculating stats at the end of every battle. - - Level 100 Pokémon can earn EVs. - - Inverse battle support. - - TONS of other features listed [here](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/battle.h). -- ***Full Trainer customization*** - - Nickname, EVs, IVs, moves, ability, ball, friendship, nature, gender, shininess. - - Custom tag battle support (teaming up an NPC in a double battle). - - Sliding trainer messages. - - Upgraded Trainer AI - - Considers newer move effects. - - New flag options to let you customize the intelligence of your trainers. - - Faster calculations. - - Specify Poké Balls by Trainer class. -- ***Pokémon Species from Generations 1-9.*** - - Simplified process to add new Pokémon. - - Option to disable unwanted families. - - Updated sprites to DS style. - - Updated stats, types, abilities and egg groups (configurable). - - Updated Hoenn's Regional Dex to match ORAS' (configurable). - - Updated National Dex incorporating the new species. - - Sprite and animation visualizer. - - Accesible by pressing `Select` on a Pokémon's Summary screen. - - Gen4+ evolution methods, with some changes: - - Mossy Rock, Icy Rock and Magnetic Field locations match ORAS'. - - Leaf, Ice and Thunder Stones may also be used. - - Inkay just needs level 30 to evolve. - - You can't physically have both the RTC and gyroscope, so we skip this requirement. - - Sylveon uses Gen8+'s evolution method (friendship + Fairy Move). - - Option to use hold evolution items directly like stones. - - Hidden Abilities. - - Available via Ability Patch. - - Compatible with Ghoul's DexNav branch. - - All gender differences. - - Custom female icons for female Hippopotas Hippowdon, Pikachu and Wobbufett - - 3 Perfect IVs on Legendaries, Mythicals and Ultra Beasts. -- ***Customizable form change tables. Full list of methods [here](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/form_change_types.h).*** - - Item holding (eg. Giratina/Arceus) - - Item using (eg. Oricorio) - - Time of day option for Shaymin - - Fainting - - Battle begin and end (eg. Xerneas) - - Move change option for Zacian/Zamazenta - - Battle end in terrains (eg. Burmy) - - Switched in battle (eg. Palafin) - - HP Threshold (eg. Darmanitan) - - Weather (eg. Castform) - - End of turn (eg. Morpeko) - - Time of day (eg. Shaymin) - - Fusions (eg. Kyurem) -- ***Breeding Improvements*** - - Incense Baby Pokémon now happen automatically (configurable). - - Level 1 eggs (configurable). - - Poké Ball inheriting (configurable). - - Egg Move Transfer, including Mirror Herb (configurable). - - Nature inheriting 100% of the time with Everstone (configurable) - - Gen6+ Ability inheriting (configurable). -- ***Items from newer Generations. Full list [here](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/items.h).*** - - ***Gen 6+ Exp. Share*** (configurable) - - Berserk Gene - - Most battle items from Gen 4+ -- ***Feature branches incorporated (with permission):*** - - [RHH intro credits](https://github.com/Xhyzi/pokeemerald/tree/rhh-intro-credits) by @Xhyzi. - - A small signature from all of us to show the collective effort in the project :) - - [Overworld debug](https://github.com/TheXaman/pokeemerald/tree/tx_debug_system) by @TheXaman - - May be disabled. - - Accesible by pressing `R + Start` in the overworld by default. - - **Additional features**: - - *Clear Boxes*: cleans every Pokémon from the Boxes. - - *Hatch an Egg*: lets you choose an Egg in your party and immediately hatch it. - - [HGSS Pokédex](https://github.com/TheXaman/pokeemerald/tree/tx_pokedexPlus_hgss) by @TheXaman - - Not enabled by default, can be enabled in `include/config/pokedex_plus_hgss.h` - - **Additional features**: - - *Support for new evolution methods*. - - *Dark Mode*. - - [Nature Colors](https://github.com/DizzyEggg/pokeemerald/tree/nature_color) in summary screen by @DizzyEggg - - [Dynamic Multichoice](https://github.com/SBird1337/pokeemerald/tree/feature/dynmulti) by @SBird1337 - - [Saveblock Cleansing](https://github.com/ghoulslash/pokeemerald/tree/saveblock) by @ghoulslash - - [Followers & Expanded IDs](https://github.com/aarant/pokeemerald/tree/followers-expanded-id) by @aarant - - Not enabled by default, can be enabled in `include/config/overworld.h` - - Includes Pokémon followers like in HGSS, including interactions. - - ***Expands the amount of possible object event IDs beyond 255.*** - - ***Includes an implementation of dynamic overworld palettes (DOWP).*** - - **Additional features**: - - *Pokémon overworld sprites up to Generation 9.* - - *Integration with our Pokémon Sprite Visualizer, allowing users to browse through the follower sprites alongside battle sprites.* -- ***Other features*** - - Pressing B while holding a Pokémon drops them like in modern games (configurable). - - Running indoors (configurable). - - Configurable overworld poison damage. - - Configurable flags for disabling Wild encounters and Trainer battles. - - Configurable flags for forcing or disabling Shinies. - - Reusable TM (configurable). - - B2W2+ Repel system that also supports LGPE's Lures - - Gen6+'s EV cap. - - All bugfixes from pret included. - - Fixed overworld snow effect. +## Table of Contents +- [What features are included?](#what-features-are-included) + - [Table of Contents](#table-of-contents) + - [Configuration files](#configuration-files) + - [Upgraded Battle Engine](#upgraded-battle-engine) + - [Full Trainer customization](#full-trainer-customization) + - [Pokémon data](#pokémon-data) + - [Interface improvements](#interface-improvements) + - [Engine improvements](#engine-improvements) + - [Overworld improvements](#overworld-improvements) + - [Developer tools](#developer-tools) -There are some mechanics, moves and abilities that are missing and being developed. Check our [issues page](https://github.com/rh-hideout/pokeemerald-expansion/issues) to see which ones. +## Configuration files +A lot of features listed below can be turned off as desired. Check which ones in these files +- [AI config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/ai.h) +- [Battle config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/battle.h) +- [Caps config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/caps.h) +- [Debug config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/debug.h) +- [DexNav config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/dexnav.h) +- [General config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/general.h) +- [HGSS Pokédex config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/pokedex_plus_hgss.h) +- [Item config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/item.h) +- [NPC Follower config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/follower_npc.h) +- [Overworld config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/overworld.h) +- [Pokémon config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/pokemon.h) +- [Save config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/save.h) +- [Species enabled](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/species_enabled.h) +- [Summary screen config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/summary_screen.h) + +## Upgraded Battle Engine +- ***Battle gimmicks:*** Mega Evolution, Primal Reversion, Ultra Burst, Z-Moves, Dynamax, Gigantamax and Terastallization. +- ***Newer game battle types:*** Double Wild Battles, custom Multi Battles, Inverse Battles, 1v2/2v1 battles, Sky Battles. +- ***Updated battle mechanics:*** Critical capture, Frostbite support, Poké Ball quick menu, Move description menu, no badge boosts, Gen 4 Fog, obedience, Affection, Party swap upon catch, move effectiveness in battle, FRLG/Gen4+ whiteout money calculation, Gen 4-style shadows. +- ***Updated move data***: Fairy/Stellar types, Physical/Special split, flags. +- ***Updated calculations:*** Damage, experience, mid-turn speed, end-battle stats and EVs, Level 100 EVs. +- ***Every item, ability and move effect up to Gen IX:*** Includes contest data up to SwSh ([source](https://pokemonurpg.com/info/contests/rse-move-list/)). +- ***Initial battle conditions:*** Stat stages, battle terrain, Wild AI flags. +- ***Faster battles:*** Simultaneous HP reduction, shortcut to "Run" option, faster battle intro, faster HP drain, faster AI calculations. +- ***Easier customization:*** Cleaner codebase to implement custom moves and effects. +- ***Improved AI:*** Faster and considers new effects added by Expansion. +- ***Popular features:*** Level/EV Caps, Sleep Clause, Type Indicators. + +## Full Trainer customization +- ***Compatible with Pokémon Showdown's team syntax:*** Create your trainer teams in the [teambuilder](https://play.pokemonshowdown.com/teambuilder) and paste the results! +- ***Custom Pokémon data:*** Nicknames, EVs, IVs, Moves, Abilities, Poké Balls, Friendship, Nature, Gender, Shininess, Dynamax level, Gigantamax Factor and Tera Type. + - ***"Ace Pokémon":*** Will save a specific Pokémon for last. + - ***Trainer Pools:*** A trainer may get a pool of randomized Pokémon instead of set teams. +- ***Custom sliding trainer messages:*** First Turn, landing a super-effective hit, before Mega Evolution, etc. +- ***New AI Flag options:*** Customize the intelligence of your trainers. +- ***Trainer class Poké Balls:*** Divers use Dive Balls, Breeders use Nest Balls, etc. +## Pokémon data +- ***Improved Pokémon Data structure:*** Optimized space to allow fitting more information, such as Tera type, 12-character names, Hyper-trained stats, evolution conditions, saved HP/status effect. +- ***Updated breeding mechanics:*** Poké Ball/Egg Move/Ability/Nature inheritance, Level 1 eggs automatic incense babies. +- ***Updated species data:*** Stats, Types, Abilities, Hidden Abilities, Egg Groups, EV Yields, movesets, Battle Facility bans, guaranteed perfect IV counts, ORAS Dex numbers. +- ***Simpler species data manipulation:***: Only requires to edit ~5 files instead of vanilla pokeemerald's 20+ to add a new Pokémon. +- ***Updated sprites:*** DS-style sprites with support for Emerald's 2-frame animations and gender difference. +- ***Species toggles:*** You can disable specific groups of Pokémon to save space, including families, cross-gen evolutions, Mega Evolutions, Regional forms, etc. +- ***Revamped Evolution System***: Multiple Evolution conditions can be stacked in order to create complex methods without additional coding. Every condition except Affection and console gyroscope is supported. +- ***Form Change System.*** Most form changes can be added without additional coding. This includes support for: Holding/using an item, HP thresholds being met, weather change in and/or out of battle, Fusions, and more. + +## Interface improvements +- ***Pokémon Summary:*** Move relearner, EV/IV checks, Nature colors ([feature branch](https://github.com/DizzyEggg/pokeemerald/tree/nature_color) by @DizzyEggg). +- ***Party Menu:*** "Move Item" option. +- ***Pokémon Storage System:*** Move option as default, access from Box Link item. +- ***HGSS-style Pokédex*** ([original feature branch](https://github.com/TheXaman/pokeemerald/tree/tx_pokedexPlus_hgss) by @TheXaman): Detailed in-game information accessible to players. + +## Engine improvements +- ***All base pokeemerald bugfixes implemented by default:*** Anything under the `BUGFIX` define. +- ***Improved sprite and palette compression:*** Assets use less space than vanilla compression. +- ***Modern compiler support:*** Detect potential errors in your code more easily. +- ***Dynamic Multichoice*** ([original branch](https://github.com/SBird1337/pokeemerald/tree/feature/dynmulti) by @SBird1337): Easier way to add multiple-choice menus for scripting. +- ***High-Quality RNG:*** No more broken vanilla RNG. + +## Overworld improvements +- ***Modern Mechanics***: Defog field move, B2W2+ Repel system, Running indoors, Removed field poison, Chain fishing, VS. Seeker, FRLG+ whiteout message. +- ***Overworld and Follower Pokémon*** ([feature branch](https://github.com/aarant/pokeemerald/tree/followers-expanded-id) by @aarant) + - *Includes Dynamic overworld palettes (DOWP) and Overworld Expansion for event IDs beyond 255.* + - *Includes Pokémon sprites up to Generation IX.* +- ***Day/Night System:*** ([feature branch](https://github.com/aarant/pokeemerald/tree/lighting-expanded-id) by @aarant) + - *Includes support for non-real time clock*. +- ***NPC Followers***: ([feature branch](https://github.com/ghoulslash/pokeemerald/tree/follow_me) by @ghoulslash) +- ***BW Map Pop-ups*** ([feature branch](https://github.com/ravepossum/pokeemerald/tree/bsbob_map_popups) by @BSBob) +- ***XY Berry Mechanics:*** Mutations, moisture, weeds, pests. +- ***Obtained Item descriptions*** (feature branch by @ghoulslash). + +## Developer tools +- ***Integrated Testing:*** Pinpoint if your custom mechanics have broken something else in the game or not. +- ***Pokémon Sprite Visualizer:*** Test every Pokémon sprite and animation. +- ***Overworld debug menu** ([original feature branch](https://github.com/TheXaman/pokeemerald/tree/tx_debug_system) by @TheXaman)*: Support menu with an assortment of features to facilitate debugging, including warping, flag and var toggling, Pokémon and item generation and more. +- ***Battle Debug Menu:*** Modify data on the fly in the middle of a battle. +- ***Learnset Helper:*** Autogenerate movesets from your custom TM and Tutor data based on official compatibility data. +- ***Configurable script flags:*** Disabling Wild encounters, Disabling Trainer battles, Forcing/Disabling Shinies. +- ***Saveblock Cleansing*** ([feature branch](https://github.com/ghoulslash/pokeemerald/tree/saveblock) by @ghoulslash) From 98670ca30eef505a8007f456bab87e66490c0413 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Fri, 20 Jun 2025 23:04:01 +0100 Subject: [PATCH 52/56] Fixes Neutralizing Gas leaving the field activating unsuppressable abilities again (#7170) --- asm/macros/battle_script.inc | 6 ++++ data/battle_scripts_1.s | 3 ++ src/battle_script_commands.c | 16 +++++++++- src/battle_util.c | 6 +++- test/battle/ability/neutralizing_gas.c | 41 +++++++++++++++++++++++++- 5 files changed, 69 insertions(+), 3 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index c4bfea9a5f..b524104a36 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -2465,6 +2465,12 @@ 1: .endm + .macro jumpifabilitycantbesuppressed battler:req, jumpInstr:req + callnative BS_JumpIfAbilityCantBeSuppressed + .byte \battler + .4byte \jumpInstr + .endm + .macro setallytonexttarget jumpInstr:req jumpifbyte CMP_GREATER_THAN, gBattlerTarget, 0x1, 1f addbyte gBattlerTarget, 0x2 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index e7e494008a..a10dfaa40c 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -9520,9 +9520,12 @@ BattleScript_NeutralizingGasExits:: setbyte gBattlerAttacker, 0 BattleScript_NeutralizingGasExitsLoop: copyarraywithindex gBattlerTarget, gBattlerByTurnOrder, gBattlerAttacker, 1 + jumpifabilitycantbesuppressed BS_TARGET, BattleScript_NeutralizingGasExitsLoopIncrement + jumpifability BS_TARGET, ABILITY_IMPOSTER, BattleScript_NeutralizingGasExitsLoopIncrement @ Imposter only activates when first entering the field saveattacker switchinabilities BS_TARGET restoreattacker +BattleScript_NeutralizingGasExitsLoopIncrement: addbyte gBattlerAttacker, 1 jumpifbytenotequal gBattlerAttacker, gBattlersCount, BattleScript_NeutralizingGasExitsLoop restoreattacker diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4e6109e93b..bd2ef1509e 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7653,7 +7653,7 @@ static void Cmd_switchindataupdate(void) u32 side = GetBattlerSide(battler); u32 partyIndex = gBattlerPartyIndexes[battler]; if (TestRunner_Battle_GetForcedAbility(side, partyIndex)) - gBattleMons[battler].ability = gDisableStructs[battler].overwrittenAbility = TestRunner_Battle_GetForcedAbility(side, partyIndex); + gBattleMons[battler].ability = TestRunner_Battle_GetForcedAbility(side, partyIndex); } #endif @@ -16542,6 +16542,9 @@ static void Cmd_tryworryseed(void) } else { + if (gBattleMons[gBattlerTarget].ability == ABILITY_NEUTRALIZING_GAS) + gSpecialStatuses[gBattlerTarget].neutralizingGasRemoved = TRUE; + gBattleScripting.abilityPopupOverwrite = gBattleMons[gBattlerTarget].ability; gBattleMons[gBattlerTarget].ability = gDisableStructs[gBattlerTarget].overwrittenAbility = ABILITY_INSOMNIA; gBattlescriptCurrInstr = cmd->nextInstr; @@ -18742,3 +18745,14 @@ void BS_JumpIfNoWhiteOut(void) else gBattlescriptCurrInstr = cmd->nextInstr; } + +void BS_JumpIfAbilityCantBeSuppressed(void) +{ + NATIVE_ARGS(u8 battler, const u8 *jumpInstr); + u32 battler = GetBattlerForBattleScript(cmd->battler); + + if (gAbilitiesInfo[gBattleMons[battler].ability].cantBeSuppressed) + gBattlescriptCurrInstr = cmd->jumpInstr; + else + gBattlescriptCurrInstr = cmd->nextInstr; +} diff --git a/src/battle_util.c b/src/battle_util.c index 715f8bb3da..ebb11c4c7f 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3505,7 +3505,11 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 u32 diagonalBattler = BATTLE_OPPOSITE(battler); if (IsDoubleBattle()) diagonalBattler = BATTLE_PARTNER(diagonalBattler); - if (IsBattlerAlive(diagonalBattler) + + // Imposter only activates when the battler first switches in + if (gDisableStructs[battler].isFirstTurn == 2 + && !gDisableStructs[battler].overwrittenAbility + && IsBattlerAlive(diagonalBattler) && !(gBattleMons[diagonalBattler].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE)) && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && gBattleStruct->illusion[diagonalBattler].state != ILLUSION_ON diff --git a/test/battle/ability/neutralizing_gas.c b/test/battle/ability/neutralizing_gas.c index 6338bfbeca..f83e928c4a 100644 --- a/test/battle/ability/neutralizing_gas.c +++ b/test/battle/ability/neutralizing_gas.c @@ -256,7 +256,8 @@ SINGLE_BATTLE_TEST("Neutralizing Gas prevents Trace from copying it") SINGLE_BATTLE_TEST("Neutralizing Gas prevents Contrary inverting stat boosts") { GIVEN { - ASSUME(GetMoveEffect(MOVE_REST) == EFFECT_REST); + ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2); + ASSUME(GetMoveEffect(MOVE_LEER) == EFFECT_DEFENSE_DOWN); PLAYER(SPECIES_INKAY) { Ability(ABILITY_CONTRARY); } OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); } } WHEN { @@ -271,3 +272,41 @@ SINGLE_BATTLE_TEST("Neutralizing Gas prevents Contrary inverting stat boosts") EXPECT_LT(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE); } } + +SINGLE_BATTLE_TEST("Neutralizing Gas exiting the field does not activate abilities that were not suppressed by it again") +{ + u32 species, ability; + // These are the only abilities that could immediately activate again + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_CALYREX_SHADOW; ability = ABILITY_AS_ONE_SHADOW_RIDER; } + PARAMETRIZE { species = SPECIES_CALYREX_ICE; ability = ABILITY_AS_ONE_ICE_RIDER; } + + GIVEN { + ASSUME(gAbilitiesInfo[ability].cantBeSuppressed); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { SWITCH(opponent, 1); } + } SCENE { + ABILITY_POPUP(player, ability); + MESSAGE("The effects of the neutralizing gas wore off!"); + NOT ABILITY_POPUP(player, ability); + } +} + +SINGLE_BATTLE_TEST("Neutralizing Gas exiting the field does not activate Imposter even if it did not activate before") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_DITTO) { Ability(ABILITY_IMPOSTER); } + OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { SWITCH(player, 1); SWITCH(opponent, 1); } + } SCENE { + NOT ABILITY_POPUP(player, ABILITY_IMPOSTER); + MESSAGE("The effects of the neutralizing gas wore off!"); + NOT ABILITY_POPUP(player, ABILITY_IMPOSTER); + } +} From 6c6daa8b9cd475122a9110043dba69c7e129f7e6 Mon Sep 17 00:00:00 2001 From: grintoul <166724814+grintoul1@users.noreply.github.com> Date: Mon, 23 Jun 2025 09:02:51 +0100 Subject: [PATCH 53/56] Fixes Rapid Spin description (#7178) (#7181) --- src/data/moves_info.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/data/moves_info.h b/src/data/moves_info.h index d2e80d787b..51f3438952 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -6050,11 +6050,11 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = { .name = COMPOUND_STRING("Rapid Spin"), .description = COMPOUND_STRING( - "Spins attack that removes\n" + "User spins and removes some\n" #if B_SPEED_BUFFING_RAPID_SPIN >= GEN_8 - "some effects and ups speed."), + "effects, while upping speed."), #else - "certain effects."), + "effects."), #endif .effect = EFFECT_RAPID_SPIN, .power = B_UPDATED_MOVE_DATA >= GEN_8 ? 50 : 20, From 0dae2cea28a2006d61de73e21940b478e38efa6b Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Mon, 23 Jun 2025 10:37:49 +0200 Subject: [PATCH 54/56] force sGFRomHeader to always be present (#7186) --- src/rom_header_gf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rom_header_gf.c b/src/rom_header_gf.c index 182d879c83..5613fe6152 100644 --- a/src/rom_header_gf.c +++ b/src/rom_header_gf.c @@ -99,7 +99,7 @@ struct GFRomHeader // This seems to need to be in the text section for some reason. // To avoid a changed section attributes warning it's put in a special .text.consts section. __attribute__((section(".text.consts"))) -static const struct GFRomHeader sGFRomHeader = { +USED static const struct GFRomHeader sGFRomHeader = { .version = GAME_VERSION, .language = GAME_LANGUAGE, .gameName = "pokemon emerald version", From 7fdc5a45b6fbd9734e9b45e2e52cc3ec8cbd08cd Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Mon, 23 Jun 2025 10:38:03 +0200 Subject: [PATCH 55/56] force RHH Rom Header to always be present (#7187) --- src/rom_header_rhh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rom_header_rhh.c b/src/rom_header_rhh.c index c323e9eadf..e9310415c3 100644 --- a/src/rom_header_rhh.c +++ b/src/rom_header_rhh.c @@ -28,7 +28,7 @@ struct RHHRomHeader }; __attribute__((section(".text.consts"))) -static const struct RHHRomHeader sRHHRomHeader = +USED static const struct RHHRomHeader sRHHRomHeader = { .rhh_magic = { 'R', 'H', 'H', 'E', 'X', 'P' }, .expansionVersionMajor = EXPANSION_VERSION_MAJOR, From 753eb9f8d895589daa6e63ace3cccac88fcbe533 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 23 Jun 2025 13:47:26 +0200 Subject: [PATCH 56/56] Fixes Enigma, Kee and Maranga Berry activation timing (#7171) --- src/battle_util.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index ebb11c4c7f..ba63bf4fe9 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6409,16 +6409,13 @@ static u8 ItemEffectMoveEnd(u32 battler, enum ItemHoldEffect holdEffect) effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPDEF, ITEMEFFECT_NONE); break; case HOLD_EFFECT_ENIGMA_BERRY: // consume and heal if hit by super effective move - if (B_BERRIES_INSTANT >= GEN_4) - effect = TrySetEnigmaBerry(battler); + effect = TrySetEnigmaBerry(battler); break; case HOLD_EFFECT_KEE_BERRY: // consume and boost defense if used physical move - if (B_BERRIES_INSTANT >= GEN_4) - effect = DamagedStatBoostBerryEffect(battler, STAT_DEF, DAMAGE_CATEGORY_PHYSICAL); + effect = DamagedStatBoostBerryEffect(battler, STAT_DEF, DAMAGE_CATEGORY_PHYSICAL); break; case HOLD_EFFECT_MARANGA_BERRY: // consume and boost sp. defense if used special move - if (B_BERRIES_INSTANT >= GEN_4) - effect = DamagedStatBoostBerryEffect(battler, STAT_SPDEF, DAMAGE_CATEGORY_SPECIAL); + effect = DamagedStatBoostBerryEffect(battler, STAT_SPDEF, DAMAGE_CATEGORY_SPECIAL); break; case HOLD_EFFECT_RANDOM_STAT_UP: if (B_BERRIES_INSTANT >= GEN_4)