From 15db51797b0bd5b8f76ee5cc2e6a2bbc2546d30f Mon Sep 17 00:00:00 2001 From: sneed Date: Thu, 6 Jun 2024 21:17:35 +0300 Subject: [PATCH 1/4] newer generation soundproof heal bell interactions --- include/config/battle.h | 1 + src/battle_ai_util.c | 6 ++ src/battle_script_commands.c | 13 +++- test/battle/move_effect/heal_bell.c | 102 ++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 test/battle/move_effect/heal_bell.c diff --git a/include/config/battle.h b/include/config/battle.h index c072cb79937b..1af7e0c68aa1 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -120,6 +120,7 @@ #define B_ALLY_SWITCH_FAIL_CHANCE GEN_LATEST // In Gen9, using Ally Switch consecutively decreases the chance of success for each consecutive use. #define B_SKETCH_BANS GEN_LATEST // In Gen9+, Sketch is unable to copy more moves than in previous generations. #define B_KNOCK_OFF_REMOVAL GEN_LATEST // In Gen5+, Knock Off removes the foe's item instead of rendering it unusable. +#define B_HEAL_BELL_SOUNDPROOF GEN_LATEST // In Gen5, Heal Bell affects all mons with Soundproof. In Gen6-8 it affects inactive mons, but not battlers. In Gen9 it always affects the user. // Ability settings #define B_EXPANDED_ABILITY_NAMES TRUE // If TRUE, ability names are increased from 12 characters to 16 characters. diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 7efee7ae05f5..9a043c2a03aa 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2963,6 +2963,12 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) for (i = 0; i < PARTY_SIZE; i++) { + if (B_HEAL_BELL_SOUNDPROOF == GEN_5 || (i == gBattlerPartyIndexes[battlerId] && B_HEAL_BELL_SOUNDPROOF >= GEN_9)) + checkSoundproof = FALSE; + else if (B_HEAL_BELL_SOUNDPROOF > GEN_5 && B_HEAL_BELL_SOUNDPROOF < GEN_9 + && (i != gBattlerPartyIndexes[battlerId] && i != gBattlerPartyIndexes[BATTLE_PARTNER(battlerId)])) + checkSoundproof = FALSE; + if (checkSoundproof && GetMonAbility(&party[i]) == ABILITY_SOUNDPROOF) continue; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 40038aa951fc..304374ace893 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -13178,7 +13178,8 @@ static void Cmd_healpartystatus(void) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_BELL; - if (GetBattlerAbility(gBattlerAttacker) != ABILITY_SOUNDPROOF) + if (GetBattlerAbility(gBattlerAttacker) != ABILITY_SOUNDPROOF + || B_HEAL_BELL_SOUNDPROOF == GEN_5 || B_HEAL_BELL_SOUNDPROOF >= GEN_9) { gBattleMons[gBattlerAttacker].status1 = 0; gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE; @@ -13194,7 +13195,7 @@ static void Cmd_healpartystatus(void) if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !(gAbsentBattlerFlags & gBitTable[battler])) { - if (GetBattlerAbility(battler) != ABILITY_SOUNDPROOF) + if (GetBattlerAbility(battler) != ABILITY_SOUNDPROOF || B_HEAL_BELL_SOUNDPROOF == GEN_5) { gBattleMons[battler].status1 = 0; gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE; @@ -13217,7 +13218,13 @@ static void Cmd_healpartystatus(void) { u16 ability; - if (gBattlerPartyIndexes[gBattlerAttacker] == i) + if (B_HEAL_BELL_SOUNDPROOF == GEN_5 + || (i == gBattlerPartyIndexes[gBattlerAttacker] && B_HEAL_BELL_SOUNDPROOF >= GEN_9)) + ability = ABILITY_NONE; + else if (B_HEAL_BELL_SOUNDPROOF > GEN_5 + && (i != gBattlerPartyIndexes[gBattlerAttacker] && i != gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)])) + ability = ABILITY_NONE; + else if (gBattlerPartyIndexes[gBattlerAttacker] == i) ability = GetBattlerAbility(gBattlerAttacker); else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gBattlerPartyIndexes[battler] == i diff --git a/test/battle/move_effect/heal_bell.c b/test/battle/move_effect/heal_bell.c new file mode 100644 index 000000000000..7c01d0cfd3f9 --- /dev/null +++ b/test/battle/move_effect/heal_bell.c @@ -0,0 +1,102 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_HEAL_BELL].effect == EFFECT_HEAL_BELL); + ASSUME(gMovesInfo[MOVE_AROMATHERAPY].effect == EFFECT_HEAL_BELL); +} + +DOUBLE_BATTLE_TEST("Heal Bell cures the entire party") +{ + u32 move; + + PARAMETRIZE { move = MOVE_HEAL_BELL; } + PARAMETRIZE { move = MOVE_AROMATHERAPY; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, move, target: playerLeft); } + TURN { SWITCH(playerLeft, 2); SWITCH(playerRight, 3); } + } SCENE { + int i; + + ANIMATION(ANIM_TYPE_MOVE, move, playerLeft); + NOT MESSAGE("Wobbuffet is hurt by poison!"); + for (i = 0; i < 6; i++) + EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_STATUS), STATUS1_NONE); + } +} + +DOUBLE_BATTLE_TEST("Heal Bell does not cure soundproof partners") +{ + u32 ability; + + PARAMETRIZE { ability = ABILITY_SCRAPPY; } + PARAMETRIZE { ability = ABILITY_SOUNDPROOF; } + + ASSUME(B_HEAL_BELL_SOUNDPROOF != GEN_5); + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_EXPLOUD) { Ability(ability); Status1(STATUS1_POISON); } + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_HEAL_BELL, target: playerLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEAL_BELL, playerLeft); + if (ability == ABILITY_SOUNDPROOF) { + MESSAGE("Exploud is hurt by poison!"); + } else { + NOT MESSAGE("Exploud is hurt by poison!"); + } + } +} + +SINGLE_BATTLE_TEST("Heal Bell cures inactive soundproof Pokemon") +{ + u32 ability; + + PARAMETRIZE { ability = ABILITY_SCRAPPY; } + PARAMETRIZE { ability = ABILITY_SOUNDPROOF; } + + ASSUME(B_HEAL_BELL_SOUNDPROOF >= GEN_5); + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } + PLAYER(SPECIES_EXPLOUD) { Ability(ability); Status1(STATUS1_POISON); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_HEAL_BELL, target: player); } + TURN { SWITCH(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEAL_BELL, player); + SEND_IN_MESSAGE("Exploud"); + NOT MESSAGE("Exploud is hurt by poison!"); + } +} + + +SINGLE_BATTLE_TEST("Heal Bell cures a soundproof user") +{ + ASSUME(B_HEAL_BELL_SOUNDPROOF == GEN_5 || B_HEAL_BELL_SOUNDPROOF >= GEN_9); + + GIVEN { + PLAYER(SPECIES_EXPLOUD) { Ability(ABILITY_SOUNDPROOF); Status1(STATUS1_POISON); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_HEAL_BELL, target: player); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEAL_BELL, player); + NOT MESSAGE("Exploud is hurt by poison!"); + } +} From 71d9f748815c99f5ec13ee4bca4ef57e7a55a12f Mon Sep 17 00:00:00 2001 From: sneed Date: Fri, 7 Jun 2024 01:58:59 +0300 Subject: [PATCH 2/4] Fix bugs and rewrite AnyPartyMemberStatused --- src/battle_ai_util.c | 37 +++++++++++++++++++++++--------- src/battle_script_commands.c | 9 +++++--- test/battle/ai_check_viability.c | 21 ++++++++++++++++++ 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 9a043c2a03aa..dcee31c0cd1d 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2954,25 +2954,42 @@ bool32 IsWakeupTurn(u32 battler) bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) { struct Pokemon *party; - u32 i; + u32 i, battlerOnField1, battlerOnField2; if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; - for (i = 0; i < PARTY_SIZE; i++) + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + battlerOnField1 = gBattlerPartyIndexes[battlerId]; + battlerOnField2 = gBattlerPartyIndexes[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battlerId)))]; + // Check partner's status + if ((GetMonData(&party[battlerOnField2], MON_DATA_STATUS) != STATUS1_NONE) + && (GetBattlerAbility(battlerOnField2) != ABILITY_SOUNDPROOF + || B_HEAL_BELL_SOUNDPROOF == GEN_5 || !checkSoundproof)) + return TRUE; + } + else // In singles there's only one battlerId by side. { - if (B_HEAL_BELL_SOUNDPROOF == GEN_5 || (i == gBattlerPartyIndexes[battlerId] && B_HEAL_BELL_SOUNDPROOF >= GEN_9)) - checkSoundproof = FALSE; - else if (B_HEAL_BELL_SOUNDPROOF > GEN_5 && B_HEAL_BELL_SOUNDPROOF < GEN_9 - && (i != gBattlerPartyIndexes[battlerId] && i != gBattlerPartyIndexes[BATTLE_PARTNER(battlerId)])) - checkSoundproof = FALSE; + battlerOnField1 = gBattlerPartyIndexes[battlerId]; + battlerOnField2 = gBattlerPartyIndexes[battlerId]; + } - if (checkSoundproof && GetMonAbility(&party[i]) == ABILITY_SOUNDPROOF) - continue; + // Check attacker's status + if ((GetMonData(&party[battlerOnField1], MON_DATA_STATUS) != STATUS1_NONE) + && (GetBattlerAbility(battlerOnField1) != ABILITY_SOUNDPROOF || !checkSoundproof + || B_HEAL_BELL_SOUNDPROOF == GEN_5 || B_HEAL_BELL_SOUNDPROOF >= GEN_9)) + return TRUE; - if (GetMonData(&party[i], MON_DATA_STATUS) != STATUS1_NONE) + // Check inactive party mons' status + for (i = 0; i < PARTY_SIZE; i++) + { + if ((!checkSoundproof || B_HEAL_BELL_SOUNDPROOF >= GEN_5 + || GetMonAbility(&party[i]) != ABILITY_SOUNDPROOF) + && i != battlerOnField1 && i != battlerOnField2 + && GetMonData(&party[i], MON_DATA_STATUS) != STATUS1_NONE) return TRUE; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 304374ace893..cf35ec9c08e9 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -13222,13 +13222,16 @@ static void Cmd_healpartystatus(void) || (i == gBattlerPartyIndexes[gBattlerAttacker] && B_HEAL_BELL_SOUNDPROOF >= GEN_9)) ability = ABILITY_NONE; else if (B_HEAL_BELL_SOUNDPROOF > GEN_5 - && (i != gBattlerPartyIndexes[gBattlerAttacker] && i != gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)])) + && i != gBattlerPartyIndexes[gBattlerAttacker] + && !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && gBattlerPartyIndexes[battler] == i + && !(gAbsentBattlerFlags & gBitTable[battler]))) ability = ABILITY_NONE; else if (gBattlerPartyIndexes[gBattlerAttacker] == i) ability = GetBattlerAbility(gBattlerAttacker); else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE - && gBattlerPartyIndexes[battler] == i - && !(gAbsentBattlerFlags & gBitTable[battler])) + && gBattlerPartyIndexes[battler] == i + && !(gAbsentBattlerFlags & gBitTable[battler])) ability = GetBattlerAbility(battler); else ability = GetAbilityBySpecies(species, abilityNum); diff --git a/test/battle/ai_check_viability.c b/test/battle/ai_check_viability.c index dcbcf8dbec08..b151f075cae2 100644 --- a/test/battle/ai_check_viability.c +++ b/test/battle/ai_check_viability.c @@ -191,3 +191,24 @@ AI_SINGLE_BATTLE_TEST("AI chooses moves with secondary effect that have a 100% c TURN { EXPECT_MOVES(opponent, MOVE_OCTAZOOKA); } } } + +AI_SINGLE_BATTLE_TEST("AI chooses moves that cure inactive party members") +{ + u32 status; + + PARAMETRIZE { status = STATUS1_NONE; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_HEAL_BELL].effect == EFFECT_HEAL_BELL); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_REGIROCK) { Moves(MOVE_BODY_PRESS, MOVE_HEAL_BELL); } + OPPONENT(SPECIES_REGICE) { Status1(status); } + } WHEN { + if (status == STATUS1_NONE) + TURN { EXPECT_MOVE(opponent, MOVE_BODY_PRESS); } + else + TURN { EXPECT_MOVE(opponent, MOVE_HEAL_BELL, target: opponent); } + } +} From 79bbb3e64eead79342f83cf7f0aeaef79c6574b4 Mon Sep 17 00:00:00 2001 From: sneed Date: Fri, 7 Jun 2024 15:33:34 +0300 Subject: [PATCH 3/4] add missing check, tests, clean up --- src/battle_ai_util.c | 22 +++++++++---------- src/battle_script_commands.c | 19 ++++++---------- src/data/moves_info.h | 4 ++-- test/battle/ai_check_viability.c | 37 +++++++++++++++++++++++++++----- 4 files changed, 52 insertions(+), 30 deletions(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index dcee31c0cd1d..0d3e9d5c8546 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2966,9 +2966,8 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) battlerOnField1 = gBattlerPartyIndexes[battlerId]; battlerOnField2 = gBattlerPartyIndexes[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battlerId)))]; // Check partner's status - if ((GetMonData(&party[battlerOnField2], MON_DATA_STATUS) != STATUS1_NONE) - && (GetBattlerAbility(battlerOnField2) != ABILITY_SOUNDPROOF - || B_HEAL_BELL_SOUNDPROOF == GEN_5 || !checkSoundproof)) + if ((B_HEAL_BELL_SOUNDPROOF == GEN_5 || AI_DATA->abilities[battlerOnField2] != ABILITY_SOUNDPROOF || !checkSoundproof) + && GetMonData(&party[battlerOnField2], MON_DATA_STATUS) != STATUS1_NONE) return TRUE; } else // In singles there's only one battlerId by side. @@ -2978,18 +2977,19 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) } // Check attacker's status - if ((GetMonData(&party[battlerOnField1], MON_DATA_STATUS) != STATUS1_NONE) - && (GetBattlerAbility(battlerOnField1) != ABILITY_SOUNDPROOF || !checkSoundproof - || B_HEAL_BELL_SOUNDPROOF == GEN_5 || B_HEAL_BELL_SOUNDPROOF >= GEN_9)) + if ((B_HEAL_BELL_SOUNDPROOF == GEN_5 || B_HEAL_BELL_SOUNDPROOF >= GEN_9 + || AI_DATA->abilities[battlerOnField1] != ABILITY_SOUNDPROOF || !checkSoundproof) + && GetMonData(&party[battlerOnField1], MON_DATA_STATUS) != STATUS1_NONE) return TRUE; // Check inactive party mons' status for (i = 0; i < PARTY_SIZE; i++) { - if ((!checkSoundproof || B_HEAL_BELL_SOUNDPROOF >= GEN_5 - || GetMonAbility(&party[i]) != ABILITY_SOUNDPROOF) - && i != battlerOnField1 && i != battlerOnField2 - && GetMonData(&party[i], MON_DATA_STATUS) != STATUS1_NONE) + if (i == battlerOnField1 || i == battlerOnField2) + continue; + if (B_HEAL_BELL_SOUNDPROOF < GEN_5 && checkSoundproof && GetMonAbility(&party[i]) == ABILITY_SOUNDPROOF) + continue; + if (GetMonData(&party[i], MON_DATA_STATUS) != STATUS1_NONE) return TRUE; } @@ -3271,7 +3271,7 @@ bool32 ShouldUseWishAromatherapy(u32 battlerAtk, u32 battlerDef, u32 move) if (GetMonData(&party[i], MON_DATA_STATUS, NULL) != STATUS1_NONE) { - if (move != MOVE_HEAL_BELL || GetMonAbility(&party[i]) != ABILITY_SOUNDPROOF) + if (B_HEAL_BELL_SOUNDPROOF >= GEN_5 || move != MOVE_HEAL_BELL || GetMonAbility(&party[i]) != ABILITY_SOUNDPROOF) hasStatus = TRUE; } } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index cf35ec9c08e9..bd90fc781841 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -13214,24 +13214,19 @@ static void Cmd_healpartystatus(void) u16 species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); u8 abilityNum = GetMonData(&party[i], MON_DATA_ABILITY_NUM); - if (species != SPECIES_NONE && species != SPECIES_EGG) + if (species != SPECIES_NONE && species != SPECIES_EGG && GetMonData(&party[i], MON_DATA_HP) > 0) { u16 ability; + bool32 isAttacker = gBattlerPartyIndexes[gBattlerAttacker] == i; + bool32 isDoublesPartner = gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gBattlerPartyIndexes[battler] == i && !(gAbsentBattlerFlags & gBitTable[battler]); - if (B_HEAL_BELL_SOUNDPROOF == GEN_5 - || (i == gBattlerPartyIndexes[gBattlerAttacker] && B_HEAL_BELL_SOUNDPROOF >= GEN_9)) + if (B_HEAL_BELL_SOUNDPROOF == GEN_5 || (isAttacker && B_HEAL_BELL_SOUNDPROOF >= GEN_9)) ability = ABILITY_NONE; - else if (B_HEAL_BELL_SOUNDPROOF > GEN_5 - && i != gBattlerPartyIndexes[gBattlerAttacker] - && !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE - && gBattlerPartyIndexes[battler] == i - && !(gAbsentBattlerFlags & gBitTable[battler]))) + else if (B_HEAL_BELL_SOUNDPROOF > GEN_5 && !isAttacker && !isDoublesPartner) ability = ABILITY_NONE; - else if (gBattlerPartyIndexes[gBattlerAttacker] == i) + else if (isAttacker) ability = GetBattlerAbility(gBattlerAttacker); - else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE - && gBattlerPartyIndexes[battler] == i - && !(gAbsentBattlerFlags & gBitTable[battler])) + else if (isDoublesPartner) ability = GetBattlerAbility(battler); else ability = GetAbilityBySpecies(species, abilityNum); diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 2a4846955df7..a1fd2f18f825 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -5477,7 +5477,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .type = TYPE_NORMAL, .accuracy = 0, .pp = 5, - .target = MOVE_TARGET_USER | MOVE_TARGET_ALLY, + .target = MOVE_TARGET_USER, .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_RECOVER_HP }, @@ -7912,7 +7912,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .type = TYPE_GRASS, .accuracy = 0, .pp = 5, - .target = MOVE_TARGET_USER | MOVE_TARGET_ALLY, + .target = MOVE_TARGET_USER, .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_RECOVER_HP }, diff --git a/test/battle/ai_check_viability.c b/test/battle/ai_check_viability.c index b151f075cae2..2e75f3190399 100644 --- a/test/battle/ai_check_viability.c +++ b/test/battle/ai_check_viability.c @@ -192,23 +192,50 @@ AI_SINGLE_BATTLE_TEST("AI chooses moves with secondary effect that have a 100% c } } +AI_DOUBLE_BATTLE_TEST("AI chooses moves that cure self or partner") +{ + u32 status1_0, status1_1, partnerAbility; + + PARAMETRIZE { status1_0 = STATUS1_NONE; status1_1 = STATUS1_NONE; partnerAbility = ABILITY_SCRAPPY; } + PARAMETRIZE { status1_0 = STATUS1_TOXIC_POISON; status1_1 = STATUS1_NONE; partnerAbility = ABILITY_SCRAPPY; } + PARAMETRIZE { status1_0 = STATUS1_NONE; status1_1 = STATUS1_PARALYSIS; partnerAbility = ABILITY_SCRAPPY; } + PARAMETRIZE { status1_0 = STATUS1_NONE; status1_1 = STATUS1_PARALYSIS; partnerAbility = ABILITY_SOUNDPROOF; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_HEAL_BELL].effect == EFFECT_HEAL_BELL); + ASSUME(B_HEAL_BELL_SOUNDPROOF >= GEN_9); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_REGIROCK) { Moves(MOVE_ROCK_SLIDE, MOVE_HEAL_BELL, MOVE_ACID); Status1(status1_0); } + OPPONENT(SPECIES_EXPLOUD) { Status1(status1_1); Ability(partnerAbility); } + } WHEN { + if (status1_0 != STATUS1_NONE || (status1_1 != STATUS1_NONE && partnerAbility != ABILITY_SOUNDPROOF)) + TURN { EXPECT_MOVE(opponentLeft, MOVE_HEAL_BELL); } + else + TURN { EXPECT_MOVE(opponentLeft, MOVE_ROCK_SLIDE); } + } +} + AI_SINGLE_BATTLE_TEST("AI chooses moves that cure inactive party members") { - u32 status; + u32 status, ability; - PARAMETRIZE { status = STATUS1_NONE; } - PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; ability = ABILITY_SCRAPPY; } + PARAMETRIZE { status = STATUS1_NONE; ability = ABILITY_SCRAPPY; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; ability = ABILITY_SOUNDPROOF; } GIVEN { ASSUME(gMovesInfo[MOVE_HEAL_BELL].effect == EFFECT_HEAL_BELL); + ASSUME(B_HEAL_BELL_SOUNDPROOF >= GEN_5); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_REGIROCK) { Moves(MOVE_BODY_PRESS, MOVE_HEAL_BELL); } - OPPONENT(SPECIES_REGICE) { Status1(status); } + OPPONENT(SPECIES_EXPLOUD) { Status1(status); Ability(ability); } } WHEN { if (status == STATUS1_NONE) TURN { EXPECT_MOVE(opponent, MOVE_BODY_PRESS); } else - TURN { EXPECT_MOVE(opponent, MOVE_HEAL_BELL, target: opponent); } + TURN { EXPECT_MOVE(opponent, MOVE_HEAL_BELL); } } } From 41824e91089aed144c47fb72b5cac26e6820ff5e Mon Sep 17 00:00:00 2001 From: sneed Date: Fri, 7 Jun 2024 17:38:11 +0300 Subject: [PATCH 4/4] fix ai code and rename battler for clarity --- src/battle_ai_util.c | 12 +++--------- src/battle_script_commands.c | 28 +++++++++++++--------------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 0d3e9d5c8546..21ac82c19383 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2966,7 +2966,7 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) battlerOnField1 = gBattlerPartyIndexes[battlerId]; battlerOnField2 = gBattlerPartyIndexes[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battlerId)))]; // Check partner's status - if ((B_HEAL_BELL_SOUNDPROOF == GEN_5 || AI_DATA->abilities[battlerOnField2] != ABILITY_SOUNDPROOF || !checkSoundproof) + if ((B_HEAL_BELL_SOUNDPROOF == GEN_5 || AI_DATA->abilities[BATTLE_PARTNER(battlerId)] != ABILITY_SOUNDPROOF || !checkSoundproof) && GetMonData(&party[battlerOnField2], MON_DATA_STATUS) != STATUS1_NONE) return TRUE; } @@ -2978,7 +2978,7 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof) // Check attacker's status if ((B_HEAL_BELL_SOUNDPROOF == GEN_5 || B_HEAL_BELL_SOUNDPROOF >= GEN_9 - || AI_DATA->abilities[battlerOnField1] != ABILITY_SOUNDPROOF || !checkSoundproof) + || AI_DATA->abilities[battlerId] != ABILITY_SOUNDPROOF || !checkSoundproof) && GetMonData(&party[battlerOnField1], MON_DATA_STATUS) != STATUS1_NONE) return TRUE; @@ -3242,7 +3242,7 @@ bool32 ShouldUseWishAromatherapy(u32 battlerAtk, u32 battlerDef, u32 move) u32 i; s32 firstId, lastId; struct Pokemon* party; - bool32 hasStatus = FALSE; + bool32 hasStatus = AnyPartyMemberStatused(battlerAtk, gMovesInfo[move].soundMove); bool32 needHealing = FALSE; GetAIPartyIndexes(battlerAtk, &firstId, &lastId); @@ -3268,12 +3268,6 @@ bool32 ShouldUseWishAromatherapy(u32 battlerAtk, u32 battlerDef, u32 move) { needHealing = TRUE; } - - if (GetMonData(&party[i], MON_DATA_STATUS, NULL) != STATUS1_NONE) - { - if (B_HEAL_BELL_SOUNDPROOF >= GEN_5 || move != MOVE_HEAL_BELL || GetMonAbility(&party[i]) != ABILITY_SOUNDPROOF) - hasStatus = TRUE; - } } } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index bd90fc781841..ceb42205238c 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -13168,7 +13168,7 @@ static void Cmd_healpartystatus(void) CMD_ARGS(); u32 zero = 0; - u32 battler; + u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker))); u8 toHeal = 0; if (gCurrentMove == MOVE_HEAL_BELL) @@ -13190,19 +13190,18 @@ static void Cmd_healpartystatus(void) gBattleCommunication[MULTISTRING_CHOOSER] |= B_MSG_BELL_SOUNDPROOF_ATTACKER; } - battler = gBattleScripting.battler = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker))); + gBattleScripting.battler = partner; - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE - && !(gAbsentBattlerFlags & gBitTable[battler])) + if (IsBattlerAlive(partner)) { - if (GetBattlerAbility(battler) != ABILITY_SOUNDPROOF || B_HEAL_BELL_SOUNDPROOF == GEN_5) + if (GetBattlerAbility(partner) != ABILITY_SOUNDPROOF || B_HEAL_BELL_SOUNDPROOF == GEN_5) { - gBattleMons[battler].status1 = 0; - gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE; + gBattleMons[partner].status1 = 0; + gBattleMons[partner].status2 &= ~STATUS2_NIGHTMARE; } else { - RecordAbilityBattle(battler, gBattleMons[battler].ability); + RecordAbilityBattle(partner, gBattleMons[partner].ability); gBattleCommunication[MULTISTRING_CHOOSER] |= B_MSG_BELL_SOUNDPROOF_PARTNER; } } @@ -13214,11 +13213,11 @@ static void Cmd_healpartystatus(void) u16 species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); u8 abilityNum = GetMonData(&party[i], MON_DATA_ABILITY_NUM); - if (species != SPECIES_NONE && species != SPECIES_EGG && GetMonData(&party[i], MON_DATA_HP) > 0) + if (species != SPECIES_NONE && species != SPECIES_EGG) { u16 ability; bool32 isAttacker = gBattlerPartyIndexes[gBattlerAttacker] == i; - bool32 isDoublesPartner = gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gBattlerPartyIndexes[battler] == i && !(gAbsentBattlerFlags & gBitTable[battler]); + bool32 isDoublesPartner = gBattlerPartyIndexes[partner] == i && IsBattlerAlive(partner); if (B_HEAL_BELL_SOUNDPROOF == GEN_5 || (isAttacker && B_HEAL_BELL_SOUNDPROOF >= GEN_9)) ability = ABILITY_NONE; @@ -13227,7 +13226,7 @@ static void Cmd_healpartystatus(void) else if (isAttacker) ability = GetBattlerAbility(gBattlerAttacker); else if (isDoublesPartner) - ability = GetBattlerAbility(battler); + ability = GetBattlerAbility(partner); else ability = GetAbilityBySpecies(species, abilityNum); @@ -13244,12 +13243,11 @@ static void Cmd_healpartystatus(void) gBattleMons[gBattlerAttacker].status1 = 0; gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE; - battler = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker))); if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE - && !(gAbsentBattlerFlags & gBitTable[battler])) + && !(gAbsentBattlerFlags & gBitTable[partner])) { - gBattleMons[battler].status1 = 0; - gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE; + gBattleMons[partner].status1 = 0; + gBattleMons[partner].status2 &= ~STATUS2_NIGHTMARE; } }