From bef343cefa8834c36c60a31ce9848c276523db07 Mon Sep 17 00:00:00 2001 From: Abaresk Date: Wed, 21 May 2025 16:02:12 -0400 Subject: [PATCH 01/31] 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 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 02/31] 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 03/31] 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 96844bd3bbec3d816b8cdc55b5cc58c0264d372f Mon Sep 17 00:00:00 2001 From: ghoulslash Date: Mon, 9 Jun 2025 16:19:25 -0400 Subject: [PATCH 04/31] 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 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 05/31] 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 06/31] 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 07/31] 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 08/31] 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 09/31] 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 10/31] 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 11/31] 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 12/31] 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 13/31] 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 14/31] 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 15/31] 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 16/31] 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 17/31] 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 18/31] 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 19/31] 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 20/31] 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 21/31] 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 22/31] 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 23/31] 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 24/31] 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 25/31] 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 26/31] 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 27/31] 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 28/31] 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 29/31] 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 30/31] 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 31/31] 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)