From 5eec3b2fc3e863326e5bb0f6a1828528c15d7c29 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Sun, 30 Jul 2023 16:50:51 +0200 Subject: [PATCH] fix various issues with primal weather blocking water/fire type moves (#3138) * fix various issues with primal weather blocking water/fire type moves * forgot to change return to effect=1 * fix bugs --- data/battle_scripts_1.s | 20 +--- include/battle_scripts.h | 3 +- include/battle_util.h | 2 +- include/constants/battle_string_ids.h | 4 + src/battle_message.c | 6 ++ src/battle_script_commands.c | 37 ++++---- src/battle_util.c | 7 +- test/primal_weather.c | 127 ++++++++++++++++++++++++++ 8 files changed, 164 insertions(+), 42 deletions(-) create mode 100644 test/primal_weather.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 39712db73..372e0e8e6 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -8782,15 +8782,14 @@ BattleScript_DesolateLandActivates:: call BattleScript_ActivateWeatherAbilities end3 -BattleScript_DesolateLandEvaporatesWaterTypeMoves:: - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE +BattleScript_PrimalWeatherBlocksMove:: + jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_ATTACKSTRING_PRINTED, BattleScript_MoveEnd @in case of multi-target moves, if move fails once, no point in printing the message twice + accuracycheck BattleScript_PrintMoveMissed, NO_ACC_CALC_CHECK_LOCK_ON attackstring pause B_WAIT_TIME_SHORT ppreduce - jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_STRING_PRINTED, BattleScript_MoveEnd - printstring STRINGID_MOVEEVAPORATEDINTHEHARSHSUNLIGHT + printfromtable gPrimalWeatherBlocksStringIds waitmessage B_WAIT_TIME_LONG - orword gHitMarker, HITMARKER_STRING_PRINTED goto BattleScript_MoveEnd BattleScript_PrimordialSeaActivates:: @@ -8802,17 +8801,6 @@ BattleScript_PrimordialSeaActivates:: call BattleScript_ActivateWeatherAbilities end3 -BattleScript_PrimordialSeaFizzlesOutFireTypeMoves:: - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE - attackstring - pause B_WAIT_TIME_SHORT - ppreduce - jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_STRING_PRINTED, BattleScript_MoveEnd - printstring STRINGID_MOVEFIZZLEDOUTINTHEHEAVYRAIN - waitmessage B_WAIT_TIME_LONG - orword gHitMarker, HITMARKER_STRING_PRINTED - goto BattleScript_MoveEnd - BattleScript_DeltaStreamActivates:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp diff --git a/include/battle_scripts.h b/include/battle_scripts.h index b20ac35dd..95f068cf5 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -409,9 +409,8 @@ extern const u8 BattleScript_GulpMissileGorging[]; extern const u8 BattleScript_GulpMissileGulping[]; extern const u8 BattleScript_BattleBondActivatesOnMoveEndAttacker[]; extern const u8 BattleScript_DesolateLandActivates[]; -extern const u8 BattleScript_DesolateLandEvaporatesWaterTypeMoves[]; extern const u8 BattleScript_PrimordialSeaActivates[]; -extern const u8 BattleScript_PrimordialSeaFizzlesOutFireTypeMoves[]; +extern const u8 BattleScript_PrimalWeatherBlocksMove[]; extern const u8 BattleScript_DeltaStreamActivates[]; extern const u8 BattleScript_MysteriousAirCurrentBlowsOn[]; extern const u8 BattleScript_AttackWeakenedByStrongWinds[]; diff --git a/include/battle_util.h b/include/battle_util.h index 15fbe2432..51375000c 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -136,8 +136,8 @@ u8 DoBattlerEndTurnEffects(void); bool8 HandleWishPerishSongOnTurnEnd(void); bool8 HandleFaintedMonActions(void); void TryClearRageAndFuryCutter(void); +u8 AtkCanceller_UnableToUseMove(u32 moveType); void SetAtkCancellerForCalledMove(void); -u8 AtkCanceller_UnableToUseMove(void); u8 AtkCanceller_UnableToUseMove2(void); bool8 HasNoMonsToSwitch(u8 battlerId, u8 r1, u8 r2); bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility); diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 174c611e2..0597612d1 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -827,6 +827,10 @@ #define B_MSG_SOMEONES_BOX_FULL 2 #define B_MSG_LANETTES_BOX_FULL 3 +// gPrimalWeatherBlocksStringIds +#define B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN 0 +#define B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN 1 + // gInobedientStringIds #define B_MSG_LOAFING 0 #define B_MSG_WONT_OBEY 1 diff --git a/src/battle_message.c b/src/battle_message.c index 1585f8c47..0613906c3 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -1850,6 +1850,12 @@ const u16 gWeatherStartsStringIds[] = [WEATHER_ABNORMAL] = STRINGID_ITISRAINING }; +const u16 gPrimalWeatherBlocksStringIds[] = +{ + [B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN] = STRINGID_MOVEFIZZLEDOUTINTHEHEAVYRAIN, + [B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN] = STRINGID_MOVEEVAPORATEDINTHEHARSHSUNLIGHT, +}; + const u16 gInobedientStringIds[] = { [B_MSG_LOAFING] = STRINGID_PKMNLOAFING, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ccd4a404f..cf793bf24 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1503,25 +1503,8 @@ static void Cmd_attackcanceler(void) s32 i, moveType; u16 attackerAbility = GetBattlerAbility(gBattlerAttacker); - GET_MOVE_TYPE(gCurrentMove, moveType); - if (WEATHER_HAS_EFFECT && gBattleMoves[gCurrentMove].power) - { - if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL)) - { - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_PrimordialSeaFizzlesOutFireTypeMoves; - return; - } - else if (moveType == TYPE_WATER && (gBattleWeather & B_WEATHER_SUN_PRIMAL)) - { - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_DesolateLandEvaporatesWaterTypeMoves; - return; - } - } - if (gBattleOutcome != 0) { gCurrentActionFuncId = B_ACTION_FINISHED; @@ -1537,9 +1520,27 @@ static void Cmd_attackcanceler(void) if (TryAegiFormChange()) return; #endif - if (AtkCanceller_UnableToUseMove()) + if (AtkCanceller_UnableToUseMove(moveType)) return; + if (WEATHER_HAS_EFFECT && gBattleMoves[gCurrentMove].power) + { + if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove; + return; + } + else if (moveType == TYPE_WATER && (gBattleWeather & B_WEATHER_SUN_PRIMAL)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove; + return; + } + } + if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_OFF && GetBattlerAbility(gBattlerAttacker) == ABILITY_PARENTAL_BOND && IsMoveAffectedByParentalBond(gCurrentMove, gBattlerAttacker) diff --git a/src/battle_util.c b/src/battle_util.c index d549b71b9..168d5432d 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3389,10 +3389,9 @@ void SetAtkCancellerForCalledMove(void) gBattleStruct->isAtkCancelerForCalledMove = TRUE; } -u8 AtkCanceller_UnableToUseMove(void) +u8 AtkCanceller_UnableToUseMove(u32 moveType) { u8 effect = 0; - s32 *bideDmg = &gBattleScripting.bideDmg; do { switch (gBattleStruct->atkCancellerTracker) @@ -3653,7 +3652,7 @@ u8 AtkCanceller_UnableToUseMove(void) if (gTakenDmg[gBattlerAttacker]) { gCurrentMove = MOVE_BIDE; - *bideDmg = gTakenDmg[gBattlerAttacker] * 2; + gBattleScripting.bideDmg = gTakenDmg[gBattlerAttacker] * 2; gBattlerTarget = gTakenDmgByBattler[gBattlerAttacker]; if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) gBattlerTarget = GetMoveTarget(MOVE_BIDE, MOVE_TARGET_SELECTED + 1); @@ -3723,8 +3722,6 @@ u8 AtkCanceller_UnableToUseMove(void) case CANCELLER_POWDER_STATUS: if (gBattleMons[gBattlerAttacker].status2 & STATUS2_POWDER) { - u32 moveType; - GET_MOVE_TYPE(gCurrentMove, moveType); if (moveType == TYPE_FIRE) { gProtectStructs[gBattlerAttacker].powderSelfDmg = TRUE; diff --git a/test/primal_weather.c b/test/primal_weather.c new file mode 100644 index 000000000..650a79921 --- /dev/null +++ b/test/primal_weather.c @@ -0,0 +1,127 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_EMBER].power != 0); + ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); + ASSUME(gBattleMoves[MOVE_WATER_GUN].power != 0); + ASSUME(gBattleMoves[MOVE_WATER_GUN].type == TYPE_WATER); +} + +SINGLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves") +{ + GIVEN { + PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);} + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_EMBER); } + TURN { MOVE(opponent, MOVE_EMBER); } + } SCENE { + MESSAGE("Foe Wobbuffet used Ember!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent); + MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + NOT HP_BAR(player); + MESSAGE("Foe Wobbuffet used Ember!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent); + MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + NOT HP_BAR(player); + } THEN { + EXPECT_EQ(player->hp, player->maxHP); + } +} + +DOUBLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves and prints the message only once with moves hitting multiple targets") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_ERUPTION].power != 0); + ASSUME(gBattleMoves[MOVE_ERUPTION].type == TYPE_FIRE); + ASSUME(gBattleMoves[MOVE_ERUPTION].target == MOVE_TARGET_BOTH); + PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB); {Speed(5);}} + PLAYER(SPECIES_WOBBUFFET) {Speed(5);} + OPPONENT(SPECIES_WOBBUFFET) {Speed(10);} + OPPONENT(SPECIES_WOBBUFFET) {Speed(8);} + } WHEN { + TURN { MOVE(opponentLeft, MOVE_ERUPTION); } + } SCENE { + MESSAGE("Foe Wobbuffet used Eruption!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ERUPTION, opponentLeft); + MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + } THEN { + EXPECT_EQ(playerLeft->hp, playerLeft->maxHP); + EXPECT_EQ(playerRight->hp, playerRight->maxHP); + } +} + +SINGLE_BATTLE_TEST("Primordial Sea does not block a move if pokemon is asleep and uses a Fire-type move") // Sleep/confusion/paralysis all happen before the check for primal weather +{ + GIVEN { + PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);} + OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);} + } WHEN { + TURN { MOVE(opponent, MOVE_EMBER); } + } SCENE { + NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!"); + MESSAGE("Foe Wobbuffet is fast asleep."); + } +} + +SINGLE_BATTLE_TEST("Desolate Land blocks damaging Water-type moves") +{ + GIVEN { + PLAYER(SPECIES_GROUDON) {Item(ITEM_RED_ORB);} + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_WATER_GUN); } + TURN { MOVE(opponent, MOVE_WATER_GUN); } + } SCENE { + MESSAGE("Foe Wobbuffet used Water Gun!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent); + MESSAGE("The Water-type attack evaporated in the harsh sunlight!"); + NOT HP_BAR(player); + MESSAGE("Foe Wobbuffet used Water Gun!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent); + MESSAGE("The Water-type attack evaporated in the harsh sunlight!"); + NOT HP_BAR(player); + } THEN { + EXPECT_EQ(player->hp, player->maxHP); + } +} + +DOUBLE_BATTLE_TEST("Desolate Land blocks damaging Water-type moves and prints the message only once with moves hitting multiple targets") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_SURF].power != 0); + ASSUME(gBattleMoves[MOVE_SURF].type == TYPE_WATER); + ASSUME(gBattleMoves[MOVE_SURF].target == MOVE_TARGET_FOES_AND_ALLY); + PLAYER(SPECIES_GROUDON) {Item(ITEM_RED_ORB); {Speed(5);}} + PLAYER(SPECIES_WOBBUFFET) {Speed(5);} + OPPONENT(SPECIES_WOBBUFFET) {Speed(10);} + OPPONENT(SPECIES_WOBBUFFET) {Speed(8);} + } WHEN { + TURN { MOVE(opponentLeft, MOVE_SURF); } + } SCENE { + MESSAGE("Foe Wobbuffet used Surf!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, opponentLeft); + MESSAGE("The Water-type attack evaporated in the harsh sunlight!"); + NOT MESSAGE("The Water-type attack evaporated in the harsh sunlight!"); + } THEN { + EXPECT_EQ(playerLeft->hp, playerLeft->maxHP); + EXPECT_EQ(playerRight->hp, playerRight->maxHP); + EXPECT_EQ(opponentRight->hp, opponentRight->maxHP); + } +} + +SINGLE_BATTLE_TEST("Desolate Land does not block a move if pokemon is asleep and uses a Water-type move") // Sleep/confusion/paralysis all happen before the check for primal weather +{ + GIVEN { + PLAYER(SPECIES_GROUDON) {Item(ITEM_RED_ORB);} + OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);} + } WHEN { + TURN { MOVE(opponent, MOVE_WATER_GUN); } + } SCENE { + NOT MESSAGE("The Water-type attack evaporated in the harsh sunlight!"); + MESSAGE("Foe Wobbuffet is fast asleep."); + } +}