From 1bb9a1c86b46d87c708e1a9dae97649cbf630c6c Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 25 Aug 2025 17:36:43 +0200 Subject: [PATCH 1/7] Remove unused function (#7617) --- src/battle_script_commands.c | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index a9fc0c6f46..a98fba7408 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6078,28 +6078,6 @@ static inline bool32 IsProtectivePadsProtected(u32 battler, enum ItemHoldEffect return TRUE; } -static inline bool32 IsProtectEffectAffected(u32 battler, u32 move) -{ - enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); - if (IsProtectivePadsProtected(battler, holdEffect)) - return TRUE; - - if (holdEffect == HOLD_EFFECT_CLEAR_AMULET) - { - RecordItemEffectBattle(battler, holdEffect); - return TRUE; - } - - u32 ability = GetBattlerAbility(gBattlerAttacker); - if (CanAbilityPreventStatLoss(ability)) - { - RecordAbilityBattle(battler, ability); - return TRUE; - } - - return FALSE; -} - static inline bool32 CanEjectButtonTrigger(u32 battlerAtk, u32 battlerDef, enum BattleMoveEffects moveEffect) { if (GetBattlerHoldEffect(battlerDef, TRUE) == HOLD_EFFECT_EJECT_BUTTON From 8044255fee5e84b87168c23f904fcce7dc277c25 Mon Sep 17 00:00:00 2001 From: psf <77138753+pkmnsnfrn@users.noreply.github.com> Date: Tue, 26 Aug 2025 06:25:18 -0700 Subject: [PATCH 2/7] Fix Vs Seeker Trainer Battle script issue (#7615) --- src/vs_seeker.c | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/vs_seeker.c b/src/vs_seeker.c index 15829c3bd5..1a7dc68c72 100644 --- a/src/vs_seeker.c +++ b/src/vs_seeker.c @@ -529,7 +529,6 @@ void ClearRematchMovementByTrainerId(void) TryGetObjectEventIdByLocalIdAndMap(objectEventTemplates[i].localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &objEventId); objectEvent = &gObjectEvents[objEventId]; - GetRandomFaceDirectionMovementType(&objectEventTemplates[i]); TryOverrideTemplateCoordsForObjectEvent(objectEvent, sFaceDirectionMovementTypeByFacingDirection[objectEvent->facingDirection]); if (gSelectedObjectEvent == objEventId) @@ -705,22 +704,11 @@ static u8 GetResponseMovementTypeFromTrainerGraphicsId(u8 graphicsId) #endif //FREE_MATCH_CALL static u16 GetTrainerFlagFromScript(const u8 *script) - /* - * The trainer flag is a little-endian short located +2 from - * the script pointer, assuming the trainerbattle command is - * first in the script. Because scripts are unaligned, and - * because the ARM processor requires shorts to be 16-bit - * aligned, this function needs to perform explicit bitwise - * operations to get the correct flag. - * - * 5c XX YY ZZ ... - * -- -- - */ { - u16 trainerFlag; - - script += 2; - trainerFlag = script[0]; + // The trainer flag is located 3 bytes (command + flags + localIdA) from the script pointer, assuming the trainerbattle command is first in the script. + // Because scripts are unaligned, and because the ARM processor requires shorts to be 16-bit aligned, this function needs to perform explicit bitwise operations to get the correct flag. + script += 3; + u16 trainerFlag = script[0]; trainerFlag |= script[1] << 8; return trainerFlag; } From a45d36d90f1e8943e33ce1543b57adeba50cf220 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 26 Aug 2025 22:16:57 +0200 Subject: [PATCH 3/7] The original attacker wasn't restored for Doodle (#7620) Co-authored-by: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Co-authored-by: Bassoonian --- data/battle_scripts_1.s | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 45f5a5babf..5f8e768c69 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -221,12 +221,13 @@ BattleScript_EffectDoodle:: attackstring ppreduce trycopyability BS_ATTACKER, BattleScript_ButItFailed + saveattacker attackanimation waitanimation setbyte gBattleCommunication, 0 goto BattleScript_EffectDoodle_AfterCopy BattleScript_EffectDoodle_CopyAbility: - trycopyability BS_ATTACKER, BattleScript_MoveEnd + trycopyability BS_ATTACKER, BattleScript_EffectDoodleMoveEnd BattleScript_EffectDoodle_AfterCopy: .if B_ABILITY_POP_UP == TRUE copybyte gBattlerAbility, gBattlerAttacker @@ -238,8 +239,10 @@ BattleScript_EffectDoodle_AfterCopy: switchinabilities BS_ATTACKER jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0x0, BattleScript_MoveEnd addbyte gBattleCommunication, 1 - jumpifnoally BS_ATTACKER, BattleScript_MoveEnd + jumpifnoally BS_ATTACKER, BattleScript_EffectDoodleMoveEnd setallytonextattacker BattleScript_EffectDoodle_CopyAbility +BattleScript_EffectDoodleMoveEnd: + restoreattacker goto BattleScript_MoveEnd BattleScript_EffectGlaiveRush:: From 2fbf45d057ba5ead9746d913df95620200508b81 Mon Sep 17 00:00:00 2001 From: RavePossum <145081120+ravepossum@users.noreply.github.com> Date: Tue, 26 Aug 2025 16:17:30 -0400 Subject: [PATCH 4/7] Restore X item friendship increase (#7583) --- include/battle_controllers.h | 1 + include/constants/item_effects.h | 4 +++ include/pokemon.h | 1 + src/battle_controllers.c | 46 +++++++++++++++++++++++++ src/battle_util.c | 6 ++++ src/data/pokemon/item_effects.h | 20 ----------- src/pokemon.c | 3 +- test/battle/item_effect/increase_stat.c | 23 +++++++++++++ test/battle/move_effect/embargo.c | 46 ++++++++++++++----------- 9 files changed, 107 insertions(+), 43 deletions(-) diff --git a/include/battle_controllers.h b/include/battle_controllers.h index 2ce28ca609..b628e69dc1 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -277,6 +277,7 @@ void InitBattleControllers(void); bool32 IsValidForBattle(struct Pokemon *mon); void TryReceiveLinkBattleData(void); void PrepareBufferDataTransferLink(u32 battler, u32 bufferId, u16 size, u8 *data); +void UpdateFriendshipFromXItem(u32 battler); // emitters void BtlController_EmitGetMonData(u32 battler, u32 bufferId, u8 requestId, u8 monToCheck); diff --git a/include/constants/item_effects.h b/include/constants/item_effects.h index ac1c7d5c5b..dbe322beac 100644 --- a/include/constants/item_effects.h +++ b/include/constants/item_effects.h @@ -93,4 +93,8 @@ #define ITEM_EFFECT_HEAL_PP 21 #define ITEM_EFFECT_NONE 22 +// Since X item stat increases are now handled by battle scripts, the friendship increase effect is now handled by the battle controller in HandleAction_UseItem. +#define X_ITEM_FRIENDSHIP_INCREASE 1 // The amount of friendship gained by using an X item on a Pokémon in battle. +#define X_ITEM_MAX_FRIENDSHIP 200 // Friendship threshold at which Pokémon stop receiving a friendship increase from using X items on them in battle. + #endif // GUARD_CONSTANTS_ITEM_EFFECTS_H diff --git a/include/pokemon.h b/include/pokemon.h index edc37a1a0b..ebc21441d8 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -830,5 +830,6 @@ uq4_12_t GetDynamaxLevelHPMultiplier(u32 dynamaxLevel, bool32 inverseMultiplier) u32 GetRegionalFormByRegion(u32 species, u32 region); bool32 IsSpeciesForeignRegionalForm(u32 species, u32 currentRegion); u32 GetTeraTypeFromPersonality(struct Pokemon *mon); +bool8 ShouldSkipFriendshipChange(void); #endif // GUARD_POKEMON_H diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 885b2a922a..da18baee14 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -12,9 +12,11 @@ #include "battle_tv.h" #include "cable_club.h" #include "event_object_movement.h" +#include "item.h" #include "link.h" #include "link_rfu.h" #include "m4a.h" +#include "overworld.h" #include "palette.h" #include "party_menu.h" #include "recorded_battle.h" @@ -25,6 +27,7 @@ #include "util.h" #include "text.h" #include "constants/abilities.h" +#include "constants/item_effects.h" #include "constants/songs.h" #include "pokemon_animation.h" @@ -3260,3 +3263,46 @@ bool32 SwitchIn_TryShinyAnimUtil(u32 battler) return TRUE; } + +void UpdateFriendshipFromXItem(u32 battler) +{ + struct Pokemon *party = GetBattlerParty(battler); + + u8 friendship; + gBattleResources->bufferA[battler][1] = REQUEST_FRIENDSHIP_BATTLE; + GetBattlerMonData(battler, party, gBattlerPartyIndexes[battler], &friendship); + + u16 heldItem; + gBattleResources->bufferA[battler][1] = REQUEST_HELDITEM_BATTLE; + GetBattlerMonData(battler, party, gBattlerPartyIndexes[battler], (u8*)&heldItem); + + if (friendship < X_ITEM_MAX_FRIENDSHIP) + { + if (GetItemHoldEffect(heldItem) == HOLD_EFFECT_FRIENDSHIP_UP) + friendship += 150 * X_ITEM_FRIENDSHIP_INCREASE / 100; + else + friendship += X_ITEM_FRIENDSHIP_INCREASE; + + u8 pokeball; + gBattleResources->bufferA[battler][1] = REQUEST_POKEBALL_BATTLE; + GetBattlerMonData(battler, party, gBattlerPartyIndexes[battler], &pokeball); + + if (pokeball == BALL_LUXURY) + friendship++; + + u8 metLocation; + gBattleResources->bufferA[battler][1] = REQUEST_MET_LOCATION_BATTLE; + GetBattlerMonData(battler, party, gBattlerPartyIndexes[battler], &metLocation); + + if (metLocation == GetCurrentRegionMapSectionId()) + friendship++; + + if (friendship > MAX_FRIENDSHIP) + friendship = MAX_FRIENDSHIP; + + gBattleMons[battler].friendship = friendship; + gBattleResources->bufferA[battler][3] = friendship; + gBattleResources->bufferA[battler][1] = REQUEST_FRIENDSHIP_BATTLE; + SetBattlerMonData(battler, GetBattlerParty(battler), gBattlerPartyIndexes[battler]); + } +} diff --git a/src/battle_util.c b/src/battle_util.c index 18b8570579..a683ff9989 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -44,6 +44,7 @@ #include "constants/battle_string_ids.h" #include "constants/hold_effects.h" #include "constants/items.h" +#include "constants/item_effects.h" #include "constants/moves.h" #include "constants/songs.h" #include "constants/species.h" @@ -580,6 +581,11 @@ void HandleAction_UseItem(void) ClearVariousBattlerFlags(gBattlerAttacker); gLastUsedItem = gBattleResources->bufferB[gBattlerAttacker][1] | (gBattleResources->bufferB[gBattlerAttacker][2] << 8); + if (X_ITEM_FRIENDSHIP_INCREASE > 0 + && GetItemEffectType(gLastUsedItem) == ITEM_EFFECT_X_ITEM + && !ShouldSkipFriendshipChange()) + UpdateFriendshipFromXItem(gBattlerAttacker); + gBattlescriptCurrInstr = gBattlescriptsForUsingItem[GetItemBattleUsage(gLastUsedItem) - 1]; gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } diff --git a/src/data/pokemon/item_effects.h b/src/data/pokemon/item_effects.h index 93ff78ad7c..851424021f 100644 --- a/src/data/pokemon/item_effects.h +++ b/src/data/pokemon/item_effects.h @@ -334,14 +334,8 @@ const u8 gItemEffect_PPMax[9] = { VITAMIN_FRIENDSHIP_CHANGE(6), }; -#define STAT_BOOST_FRIENDSHIP_CHANGE \ - [6] = 1, /* Friendship change, low */ \ - [7] = 1 /* Friendship change, mid */ - const u8 gItemEffect_GuardSpec[8] = { [3] = ITEM3_GUARD_SPEC, - [5] = ITEM5_FRIENDSHIP_LOW | ITEM5_FRIENDSHIP_MID, - STAT_BOOST_FRIENDSHIP_CHANGE, }; // The first item effect value for the stat boost items @@ -350,44 +344,30 @@ const u8 gItemEffect_GuardSpec[8] = { const u8 gItemEffect_DireHit[8] = { [0] = 1 << 5, // ITEM0_DIRE_HIT - [5] = ITEM5_FRIENDSHIP_LOW | ITEM5_FRIENDSHIP_MID, - STAT_BOOST_FRIENDSHIP_CHANGE, }; const u8 gItemEffect_XAttack[8] = { [1] = ITEM1_X_ATTACK, - [5] = ITEM5_FRIENDSHIP_LOW | ITEM5_FRIENDSHIP_MID, - STAT_BOOST_FRIENDSHIP_CHANGE, }; const u8 gItemEffect_XDefense[8] = { [1] = ITEM1_X_DEFENSE, - [5] = ITEM5_FRIENDSHIP_LOW | ITEM5_FRIENDSHIP_MID, - STAT_BOOST_FRIENDSHIP_CHANGE, }; const u8 gItemEffect_XSpeed[8] = { [1] = ITEM1_X_SPEED, - [5] = ITEM5_FRIENDSHIP_LOW | ITEM5_FRIENDSHIP_MID, - STAT_BOOST_FRIENDSHIP_CHANGE, }; const u8 gItemEffect_XAccuracy[8] = { [1] = ITEM1_X_ACCURACY, - [5] = ITEM5_FRIENDSHIP_LOW | ITEM5_FRIENDSHIP_MID, - STAT_BOOST_FRIENDSHIP_CHANGE, }; const u8 gItemEffect_XSpecialAttack[8] = { [1] = ITEM1_X_SPATK, - [5] = ITEM5_FRIENDSHIP_LOW | ITEM5_FRIENDSHIP_MID, - STAT_BOOST_FRIENDSHIP_CHANGE, }; const u8 gItemEffect_XSpecialDefense[8] = { [1] = ITEM1_X_SPDEF, - [5] = ITEM5_FRIENDSHIP_LOW | ITEM5_FRIENDSHIP_MID, - STAT_BOOST_FRIENDSHIP_CHANGE, }; const u8 gItemEffect_EvoItem[6] = { diff --git a/src/pokemon.c b/src/pokemon.c index 9b7ce57de1..6e0e7accb8 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -80,7 +80,6 @@ static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 perso static void EncryptBoxMon(struct BoxPokemon *boxMon); static void DecryptBoxMon(struct BoxPokemon *boxMon); static void Task_PlayMapChosenOrBattleBGM(u8 taskId); -static bool8 ShouldSkipFriendshipChange(void); void TrySpecialOverworldEvo(); EWRAM_DATA static u8 sLearningMoveTableID = 0; @@ -6385,7 +6384,7 @@ bool8 HasTwoFramesAnimation(u16 species) && !gTestRunnerHeadless; } -static bool8 ShouldSkipFriendshipChange(void) +bool8 ShouldSkipFriendshipChange(void) { if (gMain.inBattle && gBattleTypeFlags & (BATTLE_TYPE_FRONTIER)) return TRUE; diff --git a/test/battle/item_effect/increase_stat.c b/test/battle/item_effect/increase_stat.c index 3f162ea828..b56f1a2b84 100644 --- a/test/battle/item_effect/increase_stat.c +++ b/test/battle/item_effect/increase_stat.c @@ -1,5 +1,6 @@ #include "global.h" #include "test/battle.h" +#include "constants/item_effects.h" SINGLE_BATTLE_TEST("X Attack sharply raises battler's Attack stat", s16 damage) { @@ -257,3 +258,25 @@ SINGLE_BATTLE_TEST("Max Mushrooms raises battler's Speed stat", s16 damage) } } } + +SINGLE_BATTLE_TEST("Using X items in battle raises Friendship", s16 damage) +{ + u32 startingFriendship; + u8 metLocation = MAPSEC_NONE; + PARAMETRIZE { startingFriendship = 0; } + PARAMETRIZE { startingFriendship = X_ITEM_MAX_FRIENDSHIP; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Friendship(startingFriendship); }; + // Set met location to MAPSEC_NONE to avoid getting the friendship boost + // from being met in the current map section + SetMonData(&PLAYER_PARTY[0], MON_DATA_MET_LOCATION, &metLocation); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_X_ACCURACY); MOVE(opponent, MOVE_CELEBRATE); } + } THEN { + if (startingFriendship == X_ITEM_MAX_FRIENDSHIP) + EXPECT_EQ(player->friendship, X_ITEM_MAX_FRIENDSHIP); + else + EXPECT_EQ(player->friendship, X_ITEM_FRIENDSHIP_INCREASE); + } +} diff --git a/test/battle/move_effect/embargo.c b/test/battle/move_effect/embargo.c index 3e86b60b0b..4b06b92fc1 100644 --- a/test/battle/move_effect/embargo.c +++ b/test/battle/move_effect/embargo.c @@ -114,28 +114,32 @@ SINGLE_BATTLE_TEST("Embargo negates a held item's Speed reduction") } } -WILD_BATTLE_TEST("Embargo doesn't block held item effects that affect friendship") -{ - u32 initialFriendship; - u32 finalFriendship; +// This is a useful test, but under the current circumstances, we can't actually test this without modifying +// X_ITEM_FRIENDSHIP_INCREASE. Since HOLD_EFFECT_FRIENDSHIP_UP applies a 1.5x modifier, and the stock +// Friendship increase is 1, the held item effect actually does not affect the Friendship gained. +// +// WILD_BATTLE_TEST("Embargo doesn't block held item effects that affect friendship") +// { +// u32 initialFriendship; +// u32 finalFriendship; - KNOWN_FAILING; // Pokémon are currently not obtaining Friendship for using items in battle. - GIVEN { - ASSUME(gItemsInfo[ITEM_X_ACCURACY].battleUsage == EFFECT_ITEM_INCREASE_STAT); - PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SOOTHE_BELL); }; - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { USE_ITEM(player, ITEM_X_ACCURACY); } - TURN { MOVE(player, MOVE_SING); } - } SCENE { - MESSAGE("Wobbuffet used Sing!"); - MESSAGE("Wild Wobbuffet fell asleep!"); - } THEN { - initialFriendship = GetMonData(&PLAYER_PARTY[0], MON_DATA_FRIENDSHIP); - finalFriendship = GetMonData(&gPlayerParty[0], MON_DATA_FRIENDSHIP); - EXPECT_EQ(finalFriendship, initialFriendship + 2); - } -} +// KNOWN_FAILING; // Pokémon are currently not obtaining Friendship for using items in battle. +// GIVEN { +// ASSUME(gItemsInfo[ITEM_X_ACCURACY].battleUsage == EFFECT_ITEM_INCREASE_STAT); +// PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SOOTHE_BELL); }; +// OPPONENT(SPECIES_WOBBUFFET); +// } WHEN { +// TURN { USE_ITEM(player, ITEM_X_ACCURACY); } +// TURN { MOVE(player, MOVE_SING); } +// } SCENE { +// MESSAGE("Wobbuffet used Sing!"); +// MESSAGE("Wild Wobbuffet fell asleep!"); +// } THEN { +// initialFriendship = GetMonData(&PLAYER_PARTY[0], MON_DATA_FRIENDSHIP); +// finalFriendship = GetMonData(&gPlayerParty[0], MON_DATA_FRIENDSHIP); +// EXPECT_EQ(finalFriendship, initialFriendship + 2); +// } +// } SINGLE_BATTLE_TEST("Embargo doesn't block a held item's form-changing effect, but it does block its other effects", s16 damage) { From f0bd3d544e67a9b4afeddf5705ae144b64668f25 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Wed, 27 Aug 2025 15:16:11 +0100 Subject: [PATCH 5/7] Fixes Wonder Room interactions with Defense/Special Defense boosting effects (#7626) --- src/battle_util.c | 49 +++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index a683ff9989..2850b7d23f 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8865,6 +8865,14 @@ static inline u32 CalcAttackStat(struct DamageCalculationData *damageCalcData, u RecordAbilityBattle(battlerDef, ABILITY_THICK_FAT); } break; + case ABILITY_PURIFYING_SALT: + if (moveType == TYPE_GHOST) + { + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(0.5)); + if (damageCalcData->updateFlags) + RecordAbilityBattle(battlerDef, ABILITY_PURIFYING_SALT); + } + break; } // ally's abilities @@ -8947,31 +8955,38 @@ static inline u32 CalcDefenseStat(struct DamageCalculationData *damageCalcData, uq4_12_t modifier; u32 battlerDef = damageCalcData->battlerDef; u32 move = damageCalcData->move; - u32 moveType = damageCalcData->moveType; enum BattleMoveEffects moveEffect = GetMoveEffect(move); - if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM) // the defense stats are swapped - { - def = gBattleMons[battlerDef].spDefense; - spDef = gBattleMons[battlerDef].defense; - } - else - { - def = gBattleMons[battlerDef].defense; - spDef = gBattleMons[battlerDef].spDefense; - } + def = gBattleMons[battlerDef].defense; + spDef = gBattleMons[battlerDef].spDefense; if (moveEffect == EFFECT_PSYSHOCK || IsBattleMovePhysical(move)) // uses defense stat instead of sp.def { - defStat = def; + if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM) // the defense stats are swapped + { + defStat = spDef; + usesDefStat = FALSE; + } + else + { + defStat = def; + usesDefStat = TRUE; + } defStage = gBattleMons[battlerDef].statStages[STAT_DEF]; - usesDefStat = TRUE; } else // is special { - defStat = spDef; + if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM) // the defense stats are swapped + { + defStat = def; + usesDefStat = TRUE; + } + else + { + defStat = spDef; + usesDefStat = FALSE; + } defStage = gBattleMons[battlerDef].statStages[STAT_SPDEF]; - usesDefStat = FALSE; } // Self-destruct / Explosion cut defense in half @@ -9025,10 +9040,6 @@ static inline u32 CalcDefenseStat(struct DamageCalculationData *damageCalcData, if (gBattleMons[battlerDef].species == SPECIES_CHERRIM_SUNSHINE && IsBattlerWeatherAffected(battlerDef, B_WEATHER_SUN) && !usesDefStat) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; - case ABILITY_PURIFYING_SALT: - if (moveType == TYPE_GHOST) - modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); - break; } // ally's abilities From de790e481518834dbcca1682337a45defe042c0a Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Thu, 28 Aug 2025 07:51:02 -0400 Subject: [PATCH 6/7] Ally Switch attract battler swapping, test for leech seed (#7634) Co-authored-by: ghoulslash --- src/battle_anim_effects_1.c | 28 +++++++++++++ test/battle/move_effect/ally_switch.c | 58 +++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c index 74444065b8..570b2d1085 100644 --- a/src/battle_anim_effects_1.c +++ b/src/battle_anim_effects_1.c @@ -6864,6 +6864,33 @@ static void TrySwapWishBattlerIds(u32 battlerAtk, u32 battlerPartner) SWAP(gWishFutureKnock.wishPartyId[battlerAtk], gWishFutureKnock.wishPartyId[battlerPartner], temp); } +static void TrySwapAttractBattlerIds(u32 battlerAtk, u32 battlerPartner) +{ + u32 attractedTo; + + // our own infatuation handled with gBattleMons struct data swapping + + // if another battler is infatuated with one of us, change to other battler + for (u32 i = 0; i < gBattlersCount; i++) { + if (i == battlerAtk || i == battlerPartner || !(gBattleMons[i].status2 & STATUS2_INFATUATION)) + continue; + + attractedTo = CountTrailingZeroBits((gBattleMons[i].status2 & STATUS2_INFATUATION) >> 0x10); + if (attractedTo == battlerAtk) + { + gBattleMons[i].status2 &= ~STATUS2_INFATUATED_WITH(battlerAtk); + gBattleMons[i].status2 |= STATUS2_INFATUATED_WITH(battlerPartner); + break; + } + else if (attractedTo == battlerPartner) + { + gBattleMons[i].status2 &= ~STATUS2_INFATUATED_WITH(battlerPartner); + gBattleMons[i].status2 |= STATUS2_INFATUATED_WITH(battlerAtk); + break; + } + } +} + static void SwapBattlerMoveData(u32 battler1, u32 battler2) { u32 temp; @@ -6936,6 +6963,7 @@ static void AnimTask_AllySwitchDataSwap(u8 taskId) TrySwapSkyDropTargets(battlerAtk, battlerPartner); TrySwapStickyWebBattlerId(battlerAtk, battlerPartner); TrySwapWishBattlerIds(battlerAtk, battlerPartner); + TrySwapAttractBattlerIds(battlerAtk, battlerPartner); // For Snipe Shot and abilities Stalwart/Propeller Tail - keep the original target. for (i = 0; i < gBattlersCount; i++) diff --git a/test/battle/move_effect/ally_switch.c b/test/battle/move_effect/ally_switch.c index b808183fbd..e3f99cb73b 100644 --- a/test/battle/move_effect/ally_switch.c +++ b/test/battle/move_effect/ally_switch.c @@ -348,5 +348,63 @@ DOUBLE_BATTLE_TEST("Ally switch updates last used moves for Mimic") } } +DOUBLE_BATTLE_TEST("Ally Switch does not update leech seed battler") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_SOLOSIS); + OPPONENT(SPECIES_BULBASAUR) { HP(50); MaxHP(100); } + OPPONENT(SPECIES_RALTS) { HP(50); MaxHP(100); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_LEECH_SEED, target: playerLeft); } + TURN { MOVE(opponentRight, MOVE_ALLY_SWITCH); } + TURN { ; } + } SCENE { + // turn 1 + MESSAGE("The opposing Bulbasaur used Leech Seed!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, opponentLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_LEECH_SEED_DRAIN, playerLeft); + HP_BAR(playerLeft); + HP_BAR(opponentLeft); + + MESSAGE("The opposing Ralts used Ally Switch!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, opponentRight); + MESSAGE("The opposing Ralts and the opposing Bulbasaur switched places!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_LEECH_SEED_DRAIN, playerLeft); + HP_BAR(playerLeft); + HP_BAR(opponentLeft); // Ralts now gets hp gain + } THEN { + EXPECT_GT(opponentLeft->hp, 50); + EXPECT_GT(opponentRight->hp, 50); + } +} + +DOUBLE_BATTLE_TEST("Ally Switch updates attract battler") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(100); Gender(MON_MALE); } + PLAYER(SPECIES_SOLOSIS) { Speed(50); } + OPPONENT(SPECIES_CLEFAIRY) { Speed(20); Gender(MON_FEMALE); Ability(ABILITY_CUTE_CHARM); } + OPPONENT(SPECIES_RALTS) { Speed(30); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); } + TURN { MOVE(opponentRight, MOVE_ALLY_SWITCH); } + TURN { ; } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Tackle!"); + HP_BAR(opponentLeft); + ABILITY_POPUP(opponentLeft, ABILITY_CUTE_CHARM); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_INFATUATION, playerLeft); + MESSAGE("The opposing Clefairy's Cute Charm infatuated Wobbuffet!"); + // turn 2 + MESSAGE("The opposing Ralts used Ally Switch!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, opponentRight); + MESSAGE("The opposing Ralts and the opposing Clefairy switched places!"); + // turn 3 + MESSAGE("Wobbuffet is in love with the opposing Clefairy!"); // tracks attract battler + } +} + // Triple Battles required to test //TO_DO_BATTLE_TEST("Ally Switch fails if the user is in the middle of the field in a Triple Battle"); From ce77c48142234ee1e355ed14ae3b380885bc094b Mon Sep 17 00:00:00 2001 From: AlexOn1ine Date: Thu, 28 Aug 2025 14:23:44 +0200 Subject: [PATCH 7/7] remove unused var --- src/battle_util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index 0655831db1..de7cec8d58 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8872,7 +8872,6 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx) uq4_12_t modifier; u32 battlerDef = ctx->battlerDef; u32 move = ctx->move; - u32 moveType = ctx->moveType; enum BattleMoveEffects moveEffect = GetMoveEffect(move); def = gBattleMons[battlerDef].defense;