From d17e18c0cce300be3d18bf65350ff150afe9fc33 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 29 Dec 2023 12:52:35 +0100 Subject: [PATCH 1/2] add missing strength sap tests --- data/battle_scripts_1.s | 13 +++ src/battle_message.c | 2 +- test/battle/ai.c | 4 + test/battle/move_effect/strength_sap.c | 139 +++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 1 deletion(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index bffc79a303cd..13a269dadd5c 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -1613,15 +1613,28 @@ BattleScript_StrengthSapTryHp: attackanimation waitanimation BattleScript_StrengthSapHp: + jumpifability BS_TARGET, ABILITY_LIQUID_OOZE, BattleScript_StrengthSapManipulateDmg jumpifstatus3 BS_ATTACKER, STATUS3_HEAL_BLOCK, BattleScript_MoveEnd jumpiffullhp BS_ATTACKER, BattleScript_MoveEnd +BattleScript_StrengthSapManipulateDmg: manipulatedamage DMG_BIG_ROOT orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE + jumpifability BS_TARGET, ABILITY_LIQUID_OOZE, BattleScript_StrengthSapLiquidOoze healthbarupdate BS_ATTACKER datahpupdate BS_ATTACKER printstring STRINGID_PKMNENERGYDRAINED waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd +BattleScript_StrengthSapLiquidOoze: + call BattleScript_AbilityPopUpTarget + manipulatedamage DMG_CHANGE_SIGN + setbyte cMULTISTRING_CHOOSER, B_MSG_ABSORB_OOZE + healthbarupdate BS_ATTACKER + datahpupdate BS_ATTACKER + printfromtable gAbsorbDrainStringIds + waitmessage B_WAIT_TIME_LONG + tryfaintmon BS_ATTACKER + goto BattleScript_MoveEnd BattleScript_StrengthSapMustLower: statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_MoveEnd jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_FELL_EMPTY, BattleScript_MoveEnd diff --git a/src/battle_message.c b/src/battle_message.c index d8dbceab244a..d7ab067eeb35 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -304,7 +304,7 @@ static const u8 sText_PkmnsXPreventsYLoss[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} static const u8 sText_PkmnsXInfatuatedY[] = _("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY}\ninfatuated {B_ATK_NAME_WITH_PREFIX}!"); static const u8 sText_PkmnsXMadeYIneffective[] = _("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY}\nmade {B_CURRENT_MOVE} ineffective!"); static const u8 sText_PkmnsXCuredYProblem[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY}\ncured its {B_BUFF1} problem!"); -static const u8 sText_ItSuckedLiquidOoze[] = _("It sucked up the\nLIQUID OOZE!"); +static const u8 sText_ItSuckedLiquidOoze[] = _("It sucked up the\nliquid ooze!"); static const u8 sText_PkmnTransformed[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} transformed!"); static const u8 sText_PkmnsXTookAttack[] = _("{B_DEF_NAME_WITH_PREFIX}'s {B_DEF_ABILITY}\ntook the attack!"); const u8 gText_PkmnsXPreventsSwitching[] = _("{B_BUFF1}'s {B_LAST_ABILITY}\nprevents switching!\p"); diff --git a/test/battle/ai.c b/test/battle/ai.c index 88e516fe3eb9..efdcb51c378d 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -30,6 +30,7 @@ AI_SINGLE_BATTLE_TEST("AI prefers Bubble over Water Gun if it's slower") PARAMETRIZE { speedPlayer = 10; speedAi = 200; } GIVEN { + AI_LOG; AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_SCIZOR) { Speed(speedPlayer); } OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_WATER_GUN, MOVE_BUBBLE); Speed(speedAi); } @@ -54,6 +55,7 @@ AI_SINGLE_BATTLE_TEST("AI prefers Water Gun over Bubble if it knows that foe has PARAMETRIZE { abilityAI = ABILITY_MOXIE; } PARAMETRIZE { abilityAI = ABILITY_MOLD_BREAKER; } // Mold Breaker ignores Contrary. GIVEN { + AI_LOG; AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_SHUCKLE) { Ability(ABILITY_CONTRARY); } OPPONENT(SPECIES_PINSIR) { Moves(MOVE_WATER_GUN, MOVE_BUBBLE); Ability(abilityAI); } @@ -97,6 +99,7 @@ AI_SINGLE_BATTLE_TEST("AI prefers moves with better accuracy, but only if they b PARAMETRIZE { move1 = MOVE_SHOCK_WAVE; move2 = MOVE_ICY_WIND; move3 = MOVE_THUNDERBOLT; hp = 5; expectedMove = MOVE_SHOCK_WAVE; expectedMove2 = MOVE_THUNDERBOLT; turns = 1; } GIVEN { + AI_LOG; AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_WOBBUFFET) { HP(hp); } PLAYER(SPECIES_WOBBUFFET); @@ -157,6 +160,7 @@ AI_SINGLE_BATTLE_TEST("AI prefers moves which deal more damage instead of moves PARAMETRIZE { move1 = MOVE_POISON_JAB; move2 = MOVE_WATER_GUN; expectedMove = MOVE_POISON_JAB; abilityDef = ABILITY_IMMUNITY; turns = 3; } GIVEN { + AI_LOG; AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_TYPHLOSION) { Ability(abilityDef); } PLAYER(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect/strength_sap.c b/test/battle/move_effect/strength_sap.c index c549e4c332cc..d8e0b6e74bb3 100644 --- a/test/battle/move_effect/strength_sap.c +++ b/test/battle/move_effect/strength_sap.c @@ -57,3 +57,142 @@ SINGLE_BATTLE_TEST("Strength Sap works exactly the same when attacker is behind EXPECT_EQ(results[i].hp * -1, atkStat); } } + +// This test checks all stat stages from -6 to +6. +SINGLE_BATTLE_TEST("Strength Sap lowers Attack by 1 and restores HP based on target's Attack Stat and stat Change", s16 hp) +{ + s32 j = 0, statStage = 0; + + for (j = 0; j <= MAX_STAT_STAGE; j++) { + if (j == DEFAULT_STAT_STAGE - 1) { continue; } // Ignore -6, because Strength Sap won't work otherwise + PARAMETRIZE{ statStage = j; } + } + + GIVEN { + ASSUME(gBattleMoves[MOVE_WORK_UP].effect == EFFECT_ATTACK_SPATK_UP); + ASSUME(gBattleMoves[MOVE_GROWL].effect == EFFECT_ATTACK_DOWN); + PLAYER(SPECIES_WOBBUFFET) { HP(50); } + OPPONENT(SPECIES_WOBBUFFET) { Attack(60); } + } WHEN { + if (statStage > DEFAULT_STAT_STAGE) { // + + for (j = statStage; j > DEFAULT_STAT_STAGE; j--) { + TURN { MOVE(opponent, MOVE_HOWL); } + } + } else if (statStage < DEFAULT_STAT_STAGE) { // - + for (j = statStage; j < DEFAULT_STAT_STAGE - 1; j++) { // - 1 because Strength Sap always lowers Attack + TURN { MOVE(player, MOVE_GROWL); } + } + } + TURN { MOVE(player, MOVE_STRENGTH_SAP); } + } SCENE { + if (statStage > DEFAULT_STAT_STAGE) { // + + for (j = statStage; j > DEFAULT_STAT_STAGE; j--) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HOWL, opponent); + } + } else if (statStage < DEFAULT_STAT_STAGE) { // - + for (j = statStage; j < DEFAULT_STAT_STAGE - 1; j++) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, player); + } + } + MESSAGE("Wobbuffet used Strength Sap!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRENGTH_SAP, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's Attack fell!"); + HP_BAR(player, captureDamage: &results[i].hp); + MESSAGE("Foe Wobbuffet had its energy drained!"); + } THEN { + if (statStage < DEFAULT_STAT_STAGE) { + EXPECT_EQ(results[i].hp * -1, (60 * gStatStageRatios[statStage + 1][0] / gStatStageRatios[statStage + 1][1])); + } else { + EXPECT_EQ(results[i].hp * -1, (60 * gStatStageRatios[statStage][0] / gStatStageRatios[statStage][1])); + } + } FINALLY { + // This makes sure gStatStageRatios works correctly and the lower the attack stage the lower hp obtained. + for (j = 0; j < MAX_STAT_STAGE - 1; j++) { + EXPECT_GT(abs(results[j + 1].hp), abs(results[j].hp)); + } + } +} + +SINGLE_BATTLE_TEST("Strength Sap fails if target is at -6 Atk") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_CHARM].effect == EFFECT_ATTACK_DOWN_2); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CHARM); } + TURN { MOVE(player, MOVE_CHARM); } + TURN { MOVE(player, MOVE_CHARM); } + TURN { MOVE(player, MOVE_STRENGTH_SAP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, player); + MESSAGE("Wobbuffet used Strength Sap!"); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRENGTH_SAP, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's Attack fell!"); + HP_BAR(player); + MESSAGE("Foe Wobbuffet had its energy drained!"); + } + MESSAGE("Foe Wobbuffet's Attack won't go lower!"); + } +} + +SINGLE_BATTLE_TEST("Strength Sap restores more HP if Big Root is held", s16 hp) +{ + u32 item; + + PARAMETRIZE { item = ITEM_NONE; } + PARAMETRIZE { item = ITEM_BIG_ROOT; } + + GIVEN { + ASSUME(gItems[ITEM_BIG_ROOT].holdEffect == HOLD_EFFECT_BIG_ROOT); + PLAYER(SPECIES_WOBBUFFET) { HP(200); Item(item); } + OPPONENT(SPECIES_WOBBUFFET) { Attack(100); } + } WHEN { + TURN { MOVE(player, MOVE_STRENGTH_SAP); } + } SCENE { + MESSAGE("Wobbuffet used Strength Sap!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRENGTH_SAP, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's Attack fell!"); + HP_BAR(player, captureDamage: &results[i].hp); + MESSAGE("Foe Wobbuffet had its energy drained!"); + } FINALLY { + EXPECT_GT(abs(results[1].hp), abs(results[0].hp)); + } +} + +SINGLE_BATTLE_TEST("Strength Sap makes attacker lose HP if target's ability is Liquid Ooze") +{ + s16 lostHp; + s32 atkStat; + + PARAMETRIZE { atkStat = 100; } + PARAMETRIZE { atkStat = 490; } // Checks that attacker can faint with no problems. + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Attack(atkStat); Ability(ABILITY_LIQUID_OOZE); } + } WHEN { + TURN { MOVE(player, MOVE_STRENGTH_SAP); if (atkStat == 490) { SEND_OUT(player, 1); } } + } SCENE { + MESSAGE("Wobbuffet used Strength Sap!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRENGTH_SAP, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Wobbuffet's Attack fell!"); + ABILITY_POPUP(opponent, ABILITY_LIQUID_OOZE); + HP_BAR(player, captureDamage: &lostHp); + MESSAGE("It sucked up the liquid ooze!"); + if (atkStat >= 490) { + MESSAGE("Wobbuffet fainted!"); + MESSAGE("Go! Wobbuffet!"); + } + } THEN { + EXPECT_EQ(lostHp, atkStat); + } +} From 345035b09e399eb798ceb019f4f1d831f8df8665 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 29 Dec 2023 12:53:52 +0100 Subject: [PATCH 2/2] a --- test/battle/ai.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/battle/ai.c b/test/battle/ai.c index efdcb51c378d..88e516fe3eb9 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -30,7 +30,6 @@ AI_SINGLE_BATTLE_TEST("AI prefers Bubble over Water Gun if it's slower") PARAMETRIZE { speedPlayer = 10; speedAi = 200; } GIVEN { - AI_LOG; AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_SCIZOR) { Speed(speedPlayer); } OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_WATER_GUN, MOVE_BUBBLE); Speed(speedAi); } @@ -55,7 +54,6 @@ AI_SINGLE_BATTLE_TEST("AI prefers Water Gun over Bubble if it knows that foe has PARAMETRIZE { abilityAI = ABILITY_MOXIE; } PARAMETRIZE { abilityAI = ABILITY_MOLD_BREAKER; } // Mold Breaker ignores Contrary. GIVEN { - AI_LOG; AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_SHUCKLE) { Ability(ABILITY_CONTRARY); } OPPONENT(SPECIES_PINSIR) { Moves(MOVE_WATER_GUN, MOVE_BUBBLE); Ability(abilityAI); } @@ -99,7 +97,6 @@ AI_SINGLE_BATTLE_TEST("AI prefers moves with better accuracy, but only if they b PARAMETRIZE { move1 = MOVE_SHOCK_WAVE; move2 = MOVE_ICY_WIND; move3 = MOVE_THUNDERBOLT; hp = 5; expectedMove = MOVE_SHOCK_WAVE; expectedMove2 = MOVE_THUNDERBOLT; turns = 1; } GIVEN { - AI_LOG; AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_WOBBUFFET) { HP(hp); } PLAYER(SPECIES_WOBBUFFET); @@ -160,7 +157,6 @@ AI_SINGLE_BATTLE_TEST("AI prefers moves which deal more damage instead of moves PARAMETRIZE { move1 = MOVE_POISON_JAB; move2 = MOVE_WATER_GUN; expectedMove = MOVE_POISON_JAB; abilityDef = ABILITY_IMMUNITY; turns = 3; } GIVEN { - AI_LOG; AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_TYPHLOSION) { Ability(abilityDef); } PLAYER(SPECIES_WOBBUFFET);