From 5aa85e7b8dc72621c539e1ae76d088ab292cd261 Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Sat, 15 Mar 2025 00:20:01 +0100 Subject: [PATCH 1/9] fix search level --- include/dexnav.h | 4 ++-- src/battle_main.c | 7 +++++-- src/dexnav.c | 18 +++++++++--------- src/pokemon.c | 2 +- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/include/dexnav.h b/include/dexnav.h index baa8f8c5a7..7720104c54 100644 --- a/include/dexnav.h +++ b/include/dexnav.h @@ -66,12 +66,12 @@ void EndDexNavSearch(u8 taskId); void Task_OpenDexNavFromStartMenu(u8 taskId); bool8 TryStartDexNavSearch(void); -void TryIncrementSpeciesSearchLevel(u16 dexNum); +void TryIncrementSpeciesSearchLevel(void); void ResetDexNavSearch(void); bool8 TryFindHiddenPokemon(void); u32 CalculateDexNavShinyRolls(void); void IncrementDexNavChain(void); -extern bool8 gDexNavBattle; +extern u16 gDexNavSpecies; #endif // GUARD_DEXNAV_H diff --git a/src/battle_main.c b/src/battle_main.c index 3ccbcd6873..94d5cb0c08 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5629,12 +5629,15 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void) { gIsFishingEncounter = FALSE; gIsSurfingEncounter = FALSE; - if (gDexNavBattle && (gBattleOutcome == B_OUTCOME_WON || gBattleOutcome == B_OUTCOME_CAUGHT)) + if (gDexNavSpecies && (gBattleOutcome == B_OUTCOME_WON || gBattleOutcome == B_OUTCOME_CAUGHT)) + { IncrementDexNavChain(); + TryIncrementSpeciesSearchLevel(); + } else gSaveBlock3Ptr->dexNavChain = 0; - gDexNavBattle = FALSE; + gDexNavSpecies = SPECIES_NONE; ResetSpriteData(); if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK diff --git a/src/dexnav.c b/src/dexnav.c index 55e328bf83..1e3db82d4b 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -134,7 +134,7 @@ struct DexNavGUI EWRAM_DATA static struct DexNavSearch *sDexNavSearchDataPtr = NULL; EWRAM_DATA static struct DexNavGUI *sDexNavUiDataPtr = NULL; EWRAM_DATA static u8 *sBg1TilemapBuffer = NULL; -EWRAM_DATA bool8 gDexNavBattle = FALSE; +EWRAM_DATA u16 gDexNavSpecies = SPECIES_NONE; //// Function Declarations //GUI @@ -807,11 +807,11 @@ static void LoadSearchIconData(void) LoadCompressedSpriteSheetUsingHeap(&sHiddenMonIconSpriteSheet); } -static u8 GetSearchLevel(u16 dexNum) +static u8 GetSearchLevel(u16 species) { u8 searchLevel; #if USE_DEXNAV_SEARCH_LEVELS == TRUE - searchLevel = gSaveBlock3Ptr->dexNavSearchLevels[dexNum]; + searchLevel = gSaveBlock3Ptr->dexNavSearchLevels[species]; #else searchLevel = 0; #endif @@ -829,7 +829,7 @@ static void Task_SetUpDexNavSearch(u8 taskId) struct Task *task = &gTasks[taskId]; u16 species = sDexNavSearchDataPtr->species; - u8 searchLevel = GetSearchLevel(SpeciesToNationalPokedexNum(species)); + u8 searchLevel = GetSearchLevel(species); // init sprites sDexNavSearchDataPtr->iconSpriteId = MAX_SPRITES; @@ -1110,7 +1110,7 @@ static void Task_DexNavSearch(u8 taskId) if (sDexNavSearchDataPtr->proximity < 1) { - gDexNavBattle = TRUE; + gDexNavSpecies = sDexNavSearchDataPtr->species; CreateDexNavWildMon(sDexNavSearchDataPtr->species, sDexNavSearchDataPtr->potential, sDexNavSearchDataPtr->monLevel, sDexNavSearchDataPtr->abilityNum, sDexNavSearchDataPtr->heldItem, sDexNavSearchDataPtr->moves); @@ -2143,7 +2143,7 @@ static void PrintCurrentSpeciesInfo(void) } else { - ConvertIntToDecimalStringN(gStringVar4, GetSearchLevel(dexNum), 0, 4); + ConvertIntToDecimalStringN(gStringVar4, GetSearchLevel(species), 0, 4); AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, gStringVar4); } @@ -2666,11 +2666,11 @@ u32 CalculateDexNavShinyRolls(void) return chainBonus + rndBonus; } -void TryIncrementSpeciesSearchLevel(u16 dexNum) +void TryIncrementSpeciesSearchLevel() { #if USE_DEXNAV_SEARCH_LEVELS == TRUE - if (gMapHeader.regionMapSectionId != MAPSEC_BATTLE_FRONTIER && gSaveBlock3Ptr->dexNavSearchLevels[dexNum] < 255) - gSaveBlock3Ptr->dexNavSearchLevels[dexNum]++; + if (gMapHeader.regionMapSectionId != MAPSEC_BATTLE_FRONTIER && gSaveBlock3Ptr->dexNavSearchLevels[gDexNavSpecies] < 255) + gSaveBlock3Ptr->dexNavSearchLevels[gDexNavSpecies]++; #endif } diff --git a/src/pokemon.c b/src/pokemon.c index 5340691b46..ef90f10ba3 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1163,7 +1163,7 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, totalRerolls += 1; if (I_FISHING_CHAIN && gIsFishingEncounter) totalRerolls += CalculateChainFishingShinyRolls(); - if (gDexNavBattle) + if (gDexNavSpecies) totalRerolls += CalculateDexNavShinyRolls(); while (GET_SHINY_VALUE(value, personality) >= SHINY_ODDS && totalRerolls > 0) From 5c05715585347f77d1b86366bf1de93ddd5e06f8 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 20 Mar 2025 10:12:37 +0100 Subject: [PATCH 2/9] Adds move description battle config (#6364) --- include/config/battle.h | 3 +++ src/battle_controller_player.c | 24 +++++++++++++++--------- src/battle_interface.c | 5 ++++- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/include/config/battle.h b/include/config/battle.h index bacdc770d9..8ef347cdcd 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -221,6 +221,9 @@ // Ingame partner flag #define B_SHOW_PARTNER_TARGET FALSE // Shows the battler partner will target. +// Move description menu +#define B_SHOW_MOVE_DESCRIPTION TRUE // Shows move information in battler + // Terrain settings #define B_TERRAIN_BG_CHANGE TRUE // If set to TRUE, terrain moves permanently change the default battle background until the effect fades. #define B_THUNDERSTORM_TERRAIN TRUE // If TRUE, overworld Thunderstorm generates Rain and Electric Terrain as in Gen 8. diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index b84b87d0ed..96404e031e 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -86,6 +86,7 @@ static void MoveSelectionDisplayPpNumber(u32 battler); static void MoveSelectionDisplayPpString(u32 battler); static void MoveSelectionDisplayMoveType(u32 battler); static void MoveSelectionDisplayMoveNames(u32 battler); +static void TryMoveSelectionDisplayMoveDescription(u32 battler); static void MoveSelectionDisplayMoveDescription(u32 battler); static void SwitchIn_HandleSoundAndEnd(u32 battler); static void WaitForMonSelection(u32 battler); @@ -786,8 +787,7 @@ void HandleInputChooseMove(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); - if (gBattleStruct->descriptionSubmenu) - MoveSelectionDisplayMoveDescription(battler); + TryMoveSelectionDisplayMoveDescription(battler); TryChangeZTrigger(battler, gMoveSelectionCursor[battler]); } } @@ -802,8 +802,7 @@ void HandleInputChooseMove(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); - if (gBattleStruct->descriptionSubmenu) - MoveSelectionDisplayMoveDescription(battler); + TryMoveSelectionDisplayMoveDescription(battler); TryChangeZTrigger(battler, gMoveSelectionCursor[battler]); } } @@ -817,8 +816,7 @@ void HandleInputChooseMove(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); - if (gBattleStruct->descriptionSubmenu) - MoveSelectionDisplayMoveDescription(battler); + TryMoveSelectionDisplayMoveDescription(battler); TryChangeZTrigger(battler, gMoveSelectionCursor[battler]); } } @@ -833,8 +831,7 @@ void HandleInputChooseMove(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); - if (gBattleStruct->descriptionSubmenu) - MoveSelectionDisplayMoveDescription(battler); + TryMoveSelectionDisplayMoveDescription(battler); TryChangeZTrigger(battler, gMoveSelectionCursor[battler]); } } @@ -876,7 +873,7 @@ void HandleInputChooseMove(u32 battler) else if (JOY_NEW(B_MOVE_DESCRIPTION_BUTTON)) { gBattleStruct->descriptionSubmenu = TRUE; - MoveSelectionDisplayMoveDescription(battler); + TryMoveSelectionDisplayMoveDescription(battler); } else if (JOY_NEW(START_BUTTON)) { @@ -1745,6 +1742,15 @@ static void MoveSelectionDisplayMoveType(u32 battler) BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_TYPE); } +static void TryMoveSelectionDisplayMoveDescription(u32 battler) +{ + if (!B_SHOW_MOVE_DESCRIPTION) + return; + + if (gBattleStruct->descriptionSubmenu) + MoveSelectionDisplayMoveDescription(battler); +} + static void MoveSelectionDisplayMoveDescription(u32 battler) { struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[battler][4]); diff --git a/src/battle_interface.c b/src/battle_interface.c index a0075e8bac..d20848f321 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -3042,6 +3042,9 @@ static void DestroyLastUsedBallGfx(struct Sprite *sprite) void TryToAddMoveInfoWindow(void) { + if (!B_SHOW_MOVE_DESCRIPTION) + return; + LoadSpritePalette(&sSpritePalette_AbilityPopUp); if (GetSpriteTileStartByTag(MOVE_INFO_WINDOW_TAG) == 0xFFFF) LoadSpriteSheet(&sSpriteSheet_MoveInfoWindow); @@ -3104,7 +3107,7 @@ static void SpriteCB_LastUsedBall(struct Sprite *sprite) } static void SpriteCB_MoveInfoWin(struct Sprite *sprite) -{ +{ if (sprite->sHide) { if (sprite->x != LAST_BALL_WIN_X_0) From bb73fb1971da5cd60361148c56a6bbadc2fe4878 Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Fri, 21 Mar 2025 21:46:03 +0100 Subject: [PATCH 3/9] Fix Jet Punch in isolated tests (#6461) Co-authored-by: Hedara --- data/battle_anim_scripts.s | 1 + 1 file changed, 1 insertion(+) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 35c43a793a..ffc9a930e0 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -16762,6 +16762,7 @@ gBattleAnimMove_JetPunch:: createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_TARGET, 2, 0, 9, RGB_BLUE delay 8 createvisualtask AnimTask_ExtremeSpeedMonReappear, 2 + setarg 0x7, 0x1000 createsprite gSmallBubblePairSpriteTemplate, ANIM_TARGET, 2, 0x14, 0xffec, 0x14, ANIM_TARGET createsprite gSmallBubblePairSpriteTemplate, ANIM_TARGET, 2, 0xa, 0xa, 0x14, ANIM_TARGET createsprite gFistFootSpriteTemplate, ANIM_TARGET, 3, 0, 0, 8, 1, 0 From 76bb903d0620eb32d36534cae816febce288ea9f Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sat, 22 Mar 2025 02:54:52 -0500 Subject: [PATCH 4/9] Update Battle Debug menu with new AI flags (#6444) --- src/battle_debug.c | 63 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/src/battle_debug.c b/src/battle_debug.c index 44a92b5f93..e82f7a90df 100644 --- a/src/battle_debug.c +++ b/src/battle_debug.c @@ -218,7 +218,7 @@ enum LIST_AI_CHECK_BAD_MOVE, LIST_AI_TRY_TO_FAINT, LIST_AI_CHECK_VIABILITY, - LIST_AI_SETUP_FIRST_TURN, + LIST_AI_FORCE_SETUP_FIRST_TURN, LIST_AI_RISKY, LIST_AI_TRY_TO_2HKO, LIST_AI_PREFER_BATON_PASS, @@ -227,13 +227,20 @@ enum LIST_AI_POWERFUL_STATUS, LIST_AI_NEGATE_UNAWARE, LIST_AI_WILL_SUICIDE, - LIST_AI_HELP_PARTNER, LIST_AI_PREFER_STATUS_MOVES, LIST_AI_STALL, LIST_AI_SMART_SWITCHING, LIST_AI_ACE_POKEMON, LIST_AI_OMNISCIENT, LIST_AI_SMART_MON_CHOICES, + LIST_AI_CONSERVATIVE, + LIST_AI_SEQUENCE_SWITCHING, + LIST_AI_DOUBLE_ACE_POKEMON, + LIST_AI_WEIGH_ABILITY_PREDICTION, + LIST_AI_PREFER_HIGHEST_DAMAGE_MOVE, + LIST_AI_PREDICT_SWITCH, + LIST_AI_PREDICT_INCOMING_MON, + LIST_AI_DYNAMIC_FUNC, LIST_AI_ROAMING, LIST_AI_SAFARI, LIST_AI_FIRST_BATTLE, @@ -383,7 +390,7 @@ static const u8 sText_Swamp[] = _("Swamp"); static const u8 sText_CheckBadMove[] = _("Check Bad Move"); static const u8 sText_TryToFaint[] = _("Try to Faint"); static const u8 sText_CheckViability[] = _("Check Viability"); -static const u8 sText_SetUpFirstTurn[] = _("Setup First Turn"); +static const u8 sText_ForceSetupFirstTurn[] = _("Force Setup First Turn"); static const u8 sText_Risky[] = _("Risky"); static const u8 sText_TryTo2HKO[] = _("Try to 2HKO"); static const u8 sText_PreferBatonPass[] = _("Prefer Baton Pass"); @@ -392,13 +399,20 @@ static const u8 sText_HpAware[] = _("HP Aware"); static const u8 sText_PowerfulStatus[] = _("Powerful Status"); static const u8 sText_NegateUnaware[] = _("Negate Unaware"); static const u8 sText_WillSuicide[] = _("Will Suicide"); -static const u8 sText_HelpPartner[] = _("Help Partner"); static const u8 sText_PreferStatusMoves[] = _("Prefer Status Moves"); static const u8 sText_Stall[] = _("Stall"); static const u8 sText_SmartSwitching[] = _("Smart Switching"); -static const u8 sText_AcePokemon[] = _("Ace Pokemon"); +static const u8 sText_AcePokemon[] = _("Ace Pokémon"); static const u8 sText_Omniscient[] = _("Omniscient"); static const u8 sText_SmartMonChoices[] = _("Smart Mon Choices"); +static const u8 sText_Conservative[] = _("Conservative"); +static const u8 sText_SequenceSwitching[] = _("Sequence Switching"); +static const u8 sText_DoubleAcePokemon[] = _("Double Ace Pokémon"); +static const u8 sText_WeighAbilityPrediction[] = _("Weigh Ability Prediction"); +static const u8 sText_PreferHighestDamageMove[] = _("Prefer Highest Damage Move"); +static const u8 sText_PredictSwitch[] = _("Predict Switch"); +static const u8 sText_PredictIncomingMon[] = _("Predict Incoming Mon"); +static const u8 sText_DynamicFunc[] = _("Dynamic Func"); static const u8 sText_Roaming[] = _("Roaming"); static const u8 sText_Safari[] = _("Safari"); static const u8 sText_FirstBattle[] = _("First Battle"); @@ -479,7 +493,7 @@ static const struct BitfieldInfo sAIBitfield[] = {/*Check Bad Move*/ 1, 0}, {/*Try to Faint*/ 1, 1}, {/*Check Viability*/ 1, 2}, - {/*Setup First Turn*/ 1, 3}, + {/*Force Setup First Turn*/ 1, 3}, {/*Risky*/ 1, 4}, {/*Prefer Strongest Move*/ 1, 5}, {/*Prefer Baton Pass*/ 1, 6}, @@ -488,16 +502,20 @@ static const struct BitfieldInfo sAIBitfield[] = {/*Powerful Status*/ 1, 9}, {/*Negate Unaware*/ 1, 10}, {/*Will Suicide*/ 1, 11}, - {/*Help Partner*/ 1, 12}, - {/*Prefer Status Moves*/ 1, 13}, - {/*Stall*/ 1, 14}, - {/*Smart Switching*/ 1, 15}, - {/*Ace Pokemon*/ 1, 16}, - {/*Omniscient*/ 1, 17}, - {/*Smart Mon Choices*/ 1, 18}, - {/*Ace Pokemon*/ 1, 16}, - {/*Omniscient*/ 1, 17}, - {/*Smart Mon Choices*/ 1, 18}, + {/*Prefer Status Moves*/ 1, 12}, + {/*Stall*/ 1, 13}, + {/*Smart Switching*/ 1, 14}, + {/*Ace Pokemon*/ 1, 15}, + {/*Omniscient*/ 1, 16}, + {/*Smart Mon Choices*/ 1, 17}, + {/*Conservative*/ 1, 18}, + {/*Sequence Switching*/ 1, 19}, + {/*Double Ace Pokemon*/ 1, 20}, + {/*Weigh Ability Prediction*/ 1, 21}, + {/*Prefer Highest Damage Move*/ 1, 22}, + {/*Predict Switch*/ 1, 23}, + {/*Predict Incoming Mon*/ 1, 24}, + {/*Dynamic Func*/ 1, 28}, {/*Roaming*/ 1, 29}, {/*Safari*/ 1, 30}, {/*First Battle*/ 1, 31}, @@ -626,7 +644,7 @@ static const struct ListMenuItem sAIListItems[] = {sText_CheckBadMove, LIST_AI_CHECK_BAD_MOVE}, {sText_TryToFaint, LIST_AI_TRY_TO_FAINT}, {sText_CheckViability, LIST_AI_CHECK_VIABILITY}, - {sText_SetUpFirstTurn, LIST_AI_SETUP_FIRST_TURN}, + {sText_ForceSetupFirstTurn, LIST_AI_FORCE_SETUP_FIRST_TURN}, {sText_Risky, LIST_AI_RISKY}, {sText_TryTo2HKO, LIST_AI_TRY_TO_2HKO}, {sText_PreferBatonPass, LIST_AI_PREFER_BATON_PASS}, @@ -635,13 +653,20 @@ static const struct ListMenuItem sAIListItems[] = {sText_PowerfulStatus, LIST_AI_POWERFUL_STATUS}, {sText_NegateUnaware, LIST_AI_NEGATE_UNAWARE}, {sText_WillSuicide, LIST_AI_WILL_SUICIDE}, - {sText_HelpPartner, LIST_AI_HELP_PARTNER}, {sText_PreferStatusMoves, LIST_AI_PREFER_STATUS_MOVES}, {sText_Stall, LIST_AI_STALL}, {sText_SmartSwitching, LIST_AI_SMART_SWITCHING}, {sText_AcePokemon, LIST_AI_ACE_POKEMON}, {sText_Omniscient, LIST_AI_OMNISCIENT}, {sText_SmartMonChoices, LIST_AI_SMART_MON_CHOICES}, + {sText_Conservative, LIST_AI_CONSERVATIVE}, + {sText_SequenceSwitching, LIST_AI_SEQUENCE_SWITCHING}, + {sText_DoubleAcePokemon, LIST_AI_DOUBLE_ACE_POKEMON}, + {sText_WeighAbilityPrediction, LIST_AI_WEIGH_ABILITY_PREDICTION}, + {sText_PreferHighestDamageMove, LIST_AI_PREFER_HIGHEST_DAMAGE_MOVE}, + {sText_PredictSwitch, LIST_AI_PREDICT_SWITCH}, + {sText_PredictIncomingMon, LIST_AI_PREDICT_INCOMING_MON}, + {sText_DynamicFunc, LIST_AI_DYNAMIC_FUNC}, {sText_Roaming, LIST_AI_ROAMING}, {sText_Safari, LIST_AI_SAFARI}, {sText_FirstBattle, LIST_AI_FIRST_BATTLE}, @@ -2580,8 +2605,8 @@ static const u8 *const sHoldEffectNames[] = [HOLD_EFFECT_COVERT_CLOAK] = sText_HoldEffectCovertCloak, [HOLD_EFFECT_LOADED_DICE] = sText_HoldEffectLoadedDice, [HOLD_EFFECT_BOOSTER_ENERGY] = sText_HoldEffectBoosterEnergy, - [HOLD_EFFECT_BERSERK_GENE] = sText_HoldEffectBerserkGene, [HOLD_EFFECT_OGERPON_MASK] = sText_HoldEffectOgerponMask, + [HOLD_EFFECT_BERSERK_GENE] = sText_HoldEffectBerserkGene, }; static const u8 *GetHoldEffectName(u16 holdEffect) { From b93976c980bcf833b26fb953f3611ce2465a9aec Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 23 Mar 2025 23:34:24 +0100 Subject: [PATCH 5/9] Fixes Cheek Pouch mutating damage (#6466) --- include/battle.h | 3 ++- src/battle_script_commands.c | 14 ++++++++++++++ test/battle/ability/cheeck_pouch.c | 21 +++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 test/battle/ability/cheeck_pouch.c diff --git a/include/battle.h b/include/battle.h index a6ba6d92e5..267abdc7f2 100644 --- a/include/battle.h +++ b/include/battle.h @@ -815,7 +815,8 @@ struct BattleStruct u8 monCausingSleepClause[NUM_BATTLE_SIDES]; // Stores which pokemon on a given side is causing Sleep Clause to be active as the mon's index in the party u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects u8 redCardActivates:1; - u8 padding2:2; // padding in the middle so pursuit fields are together + u8 cheekPouchActivated:1; + u8 padding2:1; // padding in the middle so pursuit fields are together u8 pursuitStoredSwitch; // Stored id for the Pursuit target's switch s32 battlerExpReward; u16 prevTurnSpecies[MAX_BATTLERS_COUNT]; // Stores species the AI has in play at start of turn diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 7c6b95352d..62ae2184ee 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -345,6 +345,7 @@ static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent); static void TryUpdateEvolutionTracker(u32 evolutionMethod, 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 Cmd_attackcanceler(void); static void Cmd_accuracycheck(void); @@ -2669,6 +2670,7 @@ static void Cmd_datahpupdate(void) } } + TryRestoreDamageAfterCheeckPouch(battler); gBattlescriptCurrInstr = cmd->nextInstr; } @@ -8929,6 +8931,8 @@ static bool32 TryCheekPouch(u32 battler, u32 itemId) && gBattleStruct->ateBerry[GetBattlerSide(battler)] & (1u << gBattlerPartyIndexes[battler]) && !IsBattlerAtMaxHp(battler)) { + gBattleStruct->cheekPouchActivated = TRUE; + gBattleScripting.savedDmg = gBattleStruct->moveDamage[battler]; gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 3; if (gBattleStruct->moveDamage[battler] == 0) gBattleStruct->moveDamage[battler] = 1; @@ -8941,6 +8945,16 @@ static bool32 TryCheekPouch(u32 battler, u32 itemId) return FALSE; } +// When Cheek Pouch activates mid-battle it overwrites the current damage, so restore it +static void TryRestoreDamageAfterCheeckPouch(u32 battler) +{ + if (gBattleStruct->cheekPouchActivated) + { + gBattleStruct->moveDamage[battler] = gBattleScripting.savedDmg; + gBattleStruct->cheekPouchActivated = FALSE; + } +} + // Used by Bestow and Symbiosis to take an item from one battler and give to another. static void BestowItem(u32 battlerAtk, u32 battlerDef) { diff --git a/test/battle/ability/cheeck_pouch.c b/test/battle/ability/cheeck_pouch.c new file mode 100644 index 0000000000..3c932c4aa1 --- /dev/null +++ b/test/battle/ability/cheeck_pouch.c @@ -0,0 +1,21 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Cheek Pouch activation doesn't mutate damage when restoring HP mid battle") +{ + s16 damage; + s16 healing; + + GIVEN { + PLAYER(SPECIES_GREEDENT) { Ability(ABILITY_CHEEK_POUCH); Item(ITEM_CHOPLE_BERRY); HP(100); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_KARATE_CHOP); } + ABILITY_POPUP(player, ABILITY_CHEEK_POUCH); + HP_BAR(player, captureDamage: &healing); + HP_BAR(player, captureDamage: &damage); + } THEN { + EXPECT_LT(healing, 0); + EXPECT_GT(damage, 0); + } +} From 1d6d6a2bdf673c04a87307273d1cd3840485963f Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 24 Mar 2025 14:59:36 +0100 Subject: [PATCH 6/9] Fixes choice move locking at the wrong time (#6467) --- src/battle_script_commands.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 62ae2184ee..eb817f9dfe 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6479,9 +6479,10 @@ static void Cmd_moveend(void) { u16 *choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker]; if (gHitMarker & HITMARKER_OBEYS - && (HOLD_EFFECT_CHOICE(holdEffectAtk) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS) + && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) && gChosenMove != MOVE_STRUGGLE - && (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE)) + && (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE) + && (HOLD_EFFECT_CHOICE(holdEffectAtk) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS)) { if ((moveEffect == EFFECT_BATON_PASS || moveEffect == EFFECT_HEALING_WISH) && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_FAILED)) From ecab04d881a03a84a64a9a99f31daea06beb2e9b Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Tue, 25 Mar 2025 13:22:41 +0100 Subject: [PATCH 7/9] Sucker punch vs struggle (#6475) Co-authored-by: Hedara --- src/battle_script_commands.c | 2 +- test/battle/move_effect/sucker_punch.c | 53 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 test/battle/move_effect/sucker_punch.c diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index eb817f9dfe..af17d3f250 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -10628,7 +10628,7 @@ static void Cmd_various(void) gBattlescriptCurrInstr = cmd->failInstr; else if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)) gBattlescriptCurrInstr = cmd->failInstr; - else if (IsBattleMoveStatus(gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]])) + else if (IsBattleMoveStatus(gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]]) && !gProtectStructs[gBattlerTarget].noValidMoves) gBattlescriptCurrInstr = cmd->failInstr; else gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/test/battle/move_effect/sucker_punch.c b/test/battle/move_effect/sucker_punch.c new file mode 100644 index 0000000000..8076725467 --- /dev/null +++ b/test/battle/move_effect/sucker_punch.c @@ -0,0 +1,53 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Sucker Punch hits targets that are about to attack") +{ + GIVEN { + ASSUME(GetMoveCategory(MOVE_TACKLE) != DAMAGE_CATEGORY_STATUS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SUCKER_PUNCH); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUCKER_PUNCH, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + HP_BAR(player); + } +} + +SINGLE_BATTLE_TEST("Sucker Punch doesn't hit targets using status moves") +{ + GIVEN { + ASSUME(GetMoveCategory(MOVE_GROWL) == DAMAGE_CATEGORY_STATUS); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SUCKER_PUNCH); MOVE(opponent, MOVE_GROWL); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUCKER_PUNCH, player); + HP_BAR(opponent); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, opponent); + } +} + +SINGLE_BATTLE_TEST("Sucker Punch doesn't hit targets that has already moved") +{ + GIVEN { + ASSUME(GetMovePriority(MOVE_QUICK_ATTACK) == GetMovePriority(MOVE_SUCKER_PUNCH)); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_QUICK_ATTACK); MOVE(player, MOVE_SUCKER_PUNCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponent); + HP_BAR(player); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUCKER_PUNCH, player); + HP_BAR(opponent); + } + } +} From 10412e1a472fbedb3c46f257527e539416b0f5b2 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 25 Mar 2025 16:30:15 +0100 Subject: [PATCH 8/9] Fixes protective pads against Protects secondary effects (#6474) --- src/battle_script_commands.c | 31 ++++++++++++++++--- test/battle/hold_effect/protective_pads.c | 37 +++++++++++++++++++++++ 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index af17d3f250..d5439d97a6 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6169,6 +6169,20 @@ static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent) return battler; } +static inline bool32 IsProtectivePadsProtected(u32 battler, u32 move) +{ + if (!IsMoveMakingContact(move, battler)) + return FALSE; + + if (GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_PROTECTIVE_PADS) + { + RecordItemEffectBattle(battler, HOLD_EFFECT_PROTECTIVE_PADS); + return TRUE; + } + + return FALSE; +} + static void Cmd_moveend(void) { CMD_ARGS(u8 endMode, u8 endState); @@ -6206,6 +6220,7 @@ static void Cmd_moveend(void) { if (gProtectStructs[gBattlerTarget].spikyShielded && moveEffect != EFFECT_COUNTER + && !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove) && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; @@ -6217,7 +6232,8 @@ static void Cmd_moveend(void) gBattlescriptCurrInstr = BattleScript_SpikyShieldEffect; effect = 1; } - else if (gProtectStructs[gBattlerTarget].kingsShielded) + else if (gProtectStructs[gBattlerTarget].kingsShielded + && !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove)) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; i = gBattlerAttacker; @@ -6231,7 +6247,8 @@ static void Cmd_moveend(void) gBattlescriptCurrInstr = BattleScript_KingsShieldEffect; effect = 1; } - else if (gProtectStructs[gBattlerTarget].banefulBunkered) + else if (gProtectStructs[gBattlerTarget].banefulBunkered + && !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove)) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; gBattleScripting.moveEffect = MOVE_EFFECT_POISON | MOVE_EFFECT_AFFECTS_USER; @@ -6241,7 +6258,9 @@ static void Cmd_moveend(void) effect = 1; } else if (gProtectStructs[gBattlerTarget].obstructed - && moveEffect != EFFECT_SUCKER_PUNCH && moveEffect != EFFECT_UPPER_HAND) + && moveEffect != EFFECT_SUCKER_PUNCH + && moveEffect != EFFECT_UPPER_HAND + && !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove)) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; i = gBattlerAttacker; @@ -6252,7 +6271,8 @@ static void Cmd_moveend(void) gBattlescriptCurrInstr = BattleScript_KingsShieldEffect; effect = 1; } - else if (gProtectStructs[gBattlerTarget].silkTrapped) + else if (gProtectStructs[gBattlerTarget].silkTrapped + && !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove)) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; i = gBattlerAttacker; @@ -6263,7 +6283,8 @@ static void Cmd_moveend(void) gBattlescriptCurrInstr = BattleScript_KingsShieldEffect; effect = 1; } - else if (gProtectStructs[gBattlerTarget].burningBulwarked) + else if (gProtectStructs[gBattlerTarget].burningBulwarked + && !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove)) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; gBattleScripting.moveEffect = MOVE_EFFECT_BURN | MOVE_EFFECT_AFFECTS_USER; diff --git a/test/battle/hold_effect/protective_pads.c b/test/battle/hold_effect/protective_pads.c index 843d2fa003..41ffc83d4b 100644 --- a/test/battle/hold_effect/protective_pads.c +++ b/test/battle/hold_effect/protective_pads.c @@ -73,3 +73,40 @@ SINGLE_BATTLE_TEST("Protective Pads protects from Rocly Helmet Damage") } } } + +SINGLE_BATTLE_TEST("Protective Pads protects from Protect's secondary effects") +{ + u32 move; + + PARAMETRIZE { move = MOVE_SPIKY_SHIELD; } + PARAMETRIZE { move = MOVE_BANEFUL_BUNKER; } + PARAMETRIZE { move = MOVE_BURNING_BULWARK; } + PARAMETRIZE { move = MOVE_KINGS_SHIELD; } + PARAMETRIZE { move = MOVE_SILK_TRAP; } + PARAMETRIZE { move = MOVE_OBSTRUCT; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_PROTECTIVE_PADS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + if (move == MOVE_SPIKY_SHIELD) { + HP_BAR(player); + } else if (move == MOVE_BANEFUL_BUNKER) { + STATUS_ICON(player, STATUS1_BURN); + } else if (move == MOVE_BURNING_BULWARK) { + STATUS_ICON(player, STATUS1_POISON); + } else if (move == MOVE_KINGS_SHIELD) { + MESSAGE("Wobbuffet's Attack fell!"); + } else if (move == MOVE_SILK_TRAP) { + MESSAGE("Wobbuffet's Speed fell!"); + } else if (move == MOVE_OBSTRUCT) { + MESSAGE("Wobbuffet's Defense harshly fell!"); + } + } + } +} From a36f838d768f07372c0f2023ac4703f3bd52dcbe Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Tue, 25 Mar 2025 20:01:34 +0100 Subject: [PATCH 9/9] AI gimmick check changed from checking trainer data to a BattleStruct field (#6478) Co-authored-by: Hedara --- include/battle.h | 3 +++ src/battle_gimmick.c | 8 ++------ src/battle_main.c | 3 +++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/battle.h b/include/battle.h index 267abdc7f2..f6b8dbf35a 100644 --- a/include/battle.h +++ b/include/battle.h @@ -838,6 +838,9 @@ struct BattleStruct u8 trainerSlideSpriteIds[MAX_BATTLERS_COUNT]; u8 embodyAspectBoost[NUM_BATTLE_SIDES]; u16 savedMove; // backup current move for mid-turn switching, e.g. Red Card + u16 opponentMonCanTera:6; + u16 opponentMonCanDynamax:6; + u16 padding:4; }; // The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider, diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index dfbbe0e7dc..0b725811ee 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -79,13 +79,9 @@ bool32 ShouldTrainerBattlerUseGimmick(u32 battler, enum Gimmick gimmick) // Check the trainer party data to see if a gimmick is intended. else { - bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT; - u16 trainerId = isSecondTrainer ? TRAINER_BATTLE_PARAM.opponentB : TRAINER_BATTLE_PARAM.opponentA; - const struct TrainerMon *mon = &GetTrainerPartyFromId(trainerId)[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]]; - - if (gimmick == GIMMICK_TERA && mon->teraType != TYPE_NONE) + if (gimmick == GIMMICK_TERA && gBattleStruct->opponentMonCanTera & 1 << gBattlerPartyIndexes[battler]) return TRUE; - if (gimmick == GIMMICK_DYNAMAX && mon->shouldUseDynamax) + if (gimmick == GIMMICK_DYNAMAX && gBattleStruct->opponentMonCanDynamax & 1 << gBattlerPartyIndexes[battler]) return TRUE; } diff --git a/src/battle_main.c b/src/battle_main.c index b04160c1ea..4834e26796 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -1964,6 +1964,8 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer if (partyData[monIndex].dynamaxLevel > 0) { u32 data = partyData[monIndex].dynamaxLevel; + if (partyData[monIndex].shouldUseDynamax) + gBattleStruct->opponentMonCanDynamax |= 1 << i; SetMonData(&party[i], MON_DATA_DYNAMAX_LEVEL, &data); } if (partyData[monIndex].gigantamaxFactor) @@ -1973,6 +1975,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer } if (partyData[monIndex].teraType > 0) { + gBattleStruct->opponentMonCanTera |= 1 << i; u32 data = partyData[monIndex].teraType; SetMonData(&party[i], MON_DATA_TERA_TYPE, &data); }