From e7cf1621805540d8b87a21948f11c39e0bbe77e3 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 25 Jul 2025 16:18:30 +0200 Subject: [PATCH] Fixes various Choice lock issues (#7383) --- include/battle_util.h | 1 + include/constants/battle_script_commands.h | 1 - include/constants/hold_effects.h | 2 -- include/item.h | 3 +++ src/battle_ai_main.c | 6 ++--- src/battle_ai_switch_items.c | 6 ++--- src/battle_script_commands.c | 31 ---------------------- src/battle_util.c | 28 +++++++++++++++++-- src/item.c | 9 ++++++- 9 files changed, 44 insertions(+), 43 deletions(-) diff --git a/include/battle_util.h b/include/battle_util.h index aac39b3641..b9566e2512 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -123,6 +123,7 @@ enum MoveSuccessOrder CANCELLER_BIDE, CANCELLER_THAW, CANCELLER_STANCE_CHANGE_2, + CANCELLER_CHOICE_LOCK, CANCELLER_WEATHER_PRIMAL, CANCELLER_DYNAMAX_BLOCKED, CANCELLER_POWDER_STATUS, diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 4e4902f4b2..78c2200018 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -137,7 +137,6 @@ enum MoveEndEffects MOVEEND_ABILITIES_ATTACKER, MOVEEND_STATUS_IMMUNITY_ABILITIES, MOVEEND_SYNCHRONIZE_ATTACKER, - MOVEEND_CHOICE_MOVE, MOVEEND_ATTACKER_INVISIBLE, MOVEEND_ATTACKER_VISIBLE, MOVEEND_TARGET_VISIBLE, diff --git a/include/constants/hold_effects.h b/include/constants/hold_effects.h index e5ec842f1a..101a198b81 100644 --- a/include/constants/hold_effects.h +++ b/include/constants/hold_effects.h @@ -145,8 +145,6 @@ enum ItemHoldEffect HOLD_EFFECT_COUNT }; -#define HOLD_EFFECT_CHOICE(holdEffect) ((holdEffect == HOLD_EFFECT_CHOICE_BAND || holdEffect == HOLD_EFFECT_CHOICE_SCARF || holdEffect == HOLD_EFFECT_CHOICE_SPECS)) - // Terrain seed params #define HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN 0 #define HOLD_EFFECT_PARAM_GRASSY_TERRAIN 1 diff --git a/include/item.h b/include/item.h index 5fbb12ec43..5622786c91 100644 --- a/include/item.h +++ b/include/item.h @@ -6,6 +6,8 @@ #include "constants/items.h" #include "constants/moves.h" #include "constants/tms_hms.h" +#include "constants/item_effects.h" +#include "constants/hold_effects.h" /* Expands to: * enum @@ -190,5 +192,6 @@ u32 GetItemFlingPower(u32 itemId); u32 GetItemStatus1Mask(u16 itemId); bool32 ItemHasVolatileFlag(u16 itemId, enum Volatile volatile); u32 GetItemSellPrice(u32 itemId); +bool32 IsHoldEffectChoice(enum ItemHoldEffect holdEffect); #endif // GUARD_ITEM_H diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 0f9bf432c9..1b22be4ac5 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -2889,7 +2889,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) } // Choice items - if (HOLD_EFFECT_CHOICE(aiData->holdEffects[battlerAtk]) && IsBattlerItemEnabled(battlerAtk)) + if (IsHoldEffectChoice(aiData->holdEffects[battlerAtk]) && IsBattlerItemEnabled(battlerAtk)) { // Don't use user-target moves ie. Swords Dance, with exceptions if ((moveTarget & MOVE_TARGET_USER) @@ -3060,7 +3060,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) // Both Pokemon use Trick Room on the final turn of Trick Room to anticipate both opponents Protecting to stall out. // This unsets Trick Room and resets it with a full timer. case EFFECT_TRICK_ROOM: - if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer == gBattleTurnCounter + if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer == gBattleTurnCounter && ShouldSetFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM) && HasMoveWithEffect(battlerAtkPartner, MOVE_TRICK_ROOM) && RandomPercentage(RNG_AI_REFRESH_TRICK_ROOM_ON_LAST_TURN, DOUBLE_TRICK_ROOM_ON_LAST_TURN_CHANCE)) @@ -3068,7 +3068,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_TAILWIND: // Anticipate both opponents protecting to stall out Trick Room, and apply Tailwind. - if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer == gBattleTurnCounter + if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer == gBattleTurnCounter && RandomPercentage(RNG_AI_APPLY_TAILWIND_ON_LAST_TURN_OF_TRICK_ROOM, TAILWIND_IN_TRICK_ROOM_CHANCE)) ADJUST_SCORE(BEST_EFFECT); break; diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 84cba174b2..976c06c0b6 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -165,7 +165,7 @@ static bool32 AI_DoesChoiceEffectBlockMove(u32 battler, u32 move) { // Choice locked into something else if (gAiLogicData->lastUsedMove[battler] != MOVE_NONE && gAiLogicData->lastUsedMove[battler] != move - && ((HOLD_EFFECT_CHOICE(GetBattlerHoldEffect(battler, FALSE)) && IsBattlerItemEnabled(battler)) + && ((IsHoldEffectChoice(GetBattlerHoldEffect(battler, FALSE)) && IsBattlerItemEnabled(battler)) || gBattleMons[battler].ability == ABILITY_GORILLA_TACTICS)) return TRUE; return FALSE; @@ -1003,7 +1003,7 @@ static bool32 ShouldSwitchIfBadChoiceLock(u32 battler) || CanAbilityBlockMove(battler, opposingBattler, gAiLogicData->abilities[battler], gAiLogicData->abilities[opposingBattler], lastUsedMove, AI_CHECK))) moveAffectsTarget = FALSE; - if (HOLD_EFFECT_CHOICE(holdEffect) && IsBattlerItemEnabled(battler)) + if (IsHoldEffectChoice(holdEffect) && IsBattlerItemEnabled(battler)) { if ((GetMoveCategory(lastUsedMove) == DAMAGE_CATEGORY_STATUS || !moveAffectsTarget) && RandomPercentage(RNG_AI_SWITCH_CHOICE_LOCKED, GetSwitchChance(SHOULD_SWITCH_CHOICE_LOCKED))) return SetSwitchinAndSwitch(battler, PARTY_SIZE); @@ -1902,7 +1902,7 @@ static u32 GetBattleMonTypeMatchup(struct BattlePokemon opposingBattleMon, struc typeEffectiveness1 = uq4_12_multiply(typeEffectiveness1, (GetTypeModifier(atkType1, defType2))); if (typeEffectiveness1 == 0) // Immunity typeEffectiveness1 = UQ_4_12(0.1); - + if (atkType2 != atkType1) { typeEffectiveness2 = uq4_12_multiply(typeEffectiveness2, (GetTypeModifier(atkType2, defType1))); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 3c70f4b583..f834742279 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -5939,7 +5939,6 @@ static void Cmd_moveend(void) s32 i; bool32 effect = FALSE; u32 moveType = 0; - enum ItemHoldEffect holdEffectAtk = HOLD_EFFECT_NONE; u32 endMode, endState; u32 originallyUsedMove; @@ -5951,7 +5950,6 @@ static void Cmd_moveend(void) endMode = cmd->endMode; endState = cmd->endState; - holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE); moveType = GetBattleMoveType(gCurrentMove); enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove); @@ -6149,35 +6147,6 @@ static void Cmd_moveend(void) effect = TRUE; gBattleScripting.moveendState++; break; - case MOVEEND_CHOICE_MOVE: // update choice band move - { - u16 *choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker]; - if (gHitMarker & HITMARKER_OBEYS - && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) - && gChosenMove != MOVE_STRUGGLE - && (*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)) - { - gBattleScripting.moveendState++; - break; - } - *choicedMoveAtk = gChosenMove; - } - for (i = 0; i < MAX_MON_MOVES; i++) - { - if (gBattleMons[gBattlerAttacker].moves[i] == *choicedMoveAtk) - break; - } - if (i == MAX_MON_MOVES) - { - *choicedMoveAtk = MOVE_NONE; - } - gBattleScripting.moveendState++; - break; - } case MOVEEND_ITEM_EFFECTS_TARGET: if (ItemBattleEffects(ITEMEFFECT_TARGET, gBattlerTarget)) effect = TRUE; diff --git a/src/battle_util.c b/src/battle_util.c index 8e2627c262..d62d41af58 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1481,7 +1481,7 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler) } gPotentialItemEffectBattler = battler; - if (DYNAMAX_BYPASS_CHECK && HOLD_EFFECT_CHOICE(holdEffect) && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != move) + if (DYNAMAX_BYPASS_CHECK && IsHoldEffectChoice(holdEffect) && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != move) { gCurrentMove = *choicedMove; gLastUsedItem = gBattleMons[battler].item; @@ -1600,7 +1600,7 @@ u32 CheckMoveLimitations(u32 battler, u8 unusableMoves, u16 check) else if (check & MOVE_LIMITATION_ENCORE && gDisableStructs[battler].encoreTimer && gDisableStructs[battler].encoredMove != move) unusableMoves |= 1u << i; // Choice Items - else if (check & MOVE_LIMITATION_CHOICE_ITEM && HOLD_EFFECT_CHOICE(holdEffect) && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != move) + else if (check & MOVE_LIMITATION_CHOICE_ITEM && IsHoldEffectChoice(holdEffect) && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != move) unusableMoves |= 1u << i; // Assault Vest else if (check & MOVE_LIMITATION_ASSAULT_VEST && holdEffect == HOLD_EFFECT_ASSAULT_VEST && IsBattleMoveStatus(move) && moveEffect != EFFECT_ME_FIRST) @@ -2295,6 +2295,29 @@ static enum MoveCanceller CancellerStanceChangeTwo(void) return MOVE_STEP_SUCCESS; } +static enum MoveCanceller CancellerChoiceLock(void) +{ + u16 *choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker]; + enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); + + if (gChosenMove != MOVE_STRUGGLE + && (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE) + && (IsHoldEffectChoice(holdEffect) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS)) + *choicedMoveAtk = gChosenMove; + + u32 moveIndex; + for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) + { + if (gBattleMons[gBattlerAttacker].moves[moveIndex] == *choicedMoveAtk) + break; + } + + if (moveIndex == MAX_MON_MOVES) + *choicedMoveAtk = MOVE_NONE; + + return MOVE_STEP_SUCCESS; +} + static enum MoveCanceller CancellerWeatherPrimal(void) { enum MoveCanceller effect = MOVE_STEP_SUCCESS; @@ -2564,6 +2587,7 @@ static enum MoveCanceller (*const sMoveSuccessOrderCancellers[])(void) = [CANCELLER_BIDE] = CancellerBide, [CANCELLER_THAW] = CancellerThaw, [CANCELLER_STANCE_CHANGE_2] = CancellerStanceChangeTwo, + [CANCELLER_CHOICE_LOCK] = CancellerChoiceLock, [CANCELLER_WEATHER_PRIMAL] = CancellerWeatherPrimal, [CANCELLER_DYNAMAX_BLOCKED] = CancellerDynamaxBlocked, [CANCELLER_POWDER_STATUS] = CancellerPowderStatus, diff --git a/src/item.c b/src/item.c index 52d52923ca..b25f38b058 100644 --- a/src/item.c +++ b/src/item.c @@ -50,7 +50,7 @@ const struct TmHmIndexKey gTMHMItemMoveIds[NUM_ALL_MACHINES + 1] = FOREACH_HM(UNPACK_HM_ITEM_ID) /* * Expands to the following: - * + * * [1] = { ITEM_TM_FOCUS_PUNCH, MOVE_FOCUS_PUNCH }, * [2] = { ITEM_TM_DRAGON_CLAW, MOVE_DRAGON_CLAW }, * [3] = { ITEM_TM_WATER_PULSE, MOVE_WATER_PULSE }, @@ -976,3 +976,10 @@ u32 GetItemSellPrice(u32 itemId) { return GetItemPrice(itemId) / ITEM_SELL_FACTOR; } + +bool32 IsHoldEffectChoice(enum ItemHoldEffect holdEffect) +{ + return holdEffect == HOLD_EFFECT_CHOICE_BAND + || holdEffect == HOLD_EFFECT_CHOICE_SCARF + || holdEffect == HOLD_EFFECT_CHOICE_SPECS; +}