diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index a0c3bf6bb..cef036f8b 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -14883,7 +14883,6 @@ Move_CORROSIVE_GAS:: clearmonbg ANIM_ATTACKER end - @Credits to Skeli Move_COACHING:: playsewithpan SE_M_TAIL_WHIP, SOUND_PAN_ATTACKER diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 0fd5f87c6..056aaa673 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -436,7 +436,18 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectSpinOut @ EFFECT_SPIN_OUT .4byte BattleScript_EffectMakeItRain @ EFFECT_MAKE_IT_RAIN .4byte BattleScript_EffectCorrosiveGas @ EFFECT_CORROSIVE_GAS - + .4byte BattleScript_EffectHit @ EFFECT_POPULATION_BOMB + .4byte BattleScript_EffectMortalSpin @ EFFECT_MORTAL_SPIN + +BattleScript_EffectMortalSpin: + call BattleScript_EffectHit_Ret + rapidspinfree + setmoveeffect MOVE_EFFECT_POISON + seteffectwithchance + tryfaintmon BS_TARGET + moveendall + end + BattleScript_EffectCorrosiveGas: attackcanceler accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE @@ -452,7 +463,7 @@ BattleScript_EffectCorrosiveGas: printstring STRINGID_PKMNITEMMELTED waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd - + BattleScript_CorrosiveGasFail: pause B_WAIT_TIME_SHORT orhalfword gMoveResultFlags, MOVE_RESULT_FAILED @@ -7787,7 +7798,7 @@ BattleScript_WishMegaEvolution:: BattleScript_PrimalReversion:: call BattleScript_PrimalReversionRet end2 - + BattleScript_PrimalReversionRestoreAttacker:: call BattleScript_PrimalReversionRet copybyte gBattlerAttacker, sSAVED_BATTLER diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 27cc037af..eab1cf992 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -413,7 +413,9 @@ #define EFFECT_SPIN_OUT 407 #define EFFECT_MAKE_IT_RAIN 408 #define EFFECT_CORROSIVE_GAS 409 +#define EFFECT_POPULATION_BOMB 410 +#define EFFECT_MORTAL_SPIN 411 -#define NUM_BATTLE_MOVE_EFFECTS 410 +#define NUM_BATTLE_MOVE_EFFECTS 412 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 9243bacb9..e231d3d1f 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -3571,6 +3571,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_TOXIC: case EFFECT_POISON: case EFFECT_BARB_BARRAGE: + case EFFECT_MORTAL_SPIN: IncreasePoisonScore(battlerAtk, battlerDef, move, &score); break; case EFFECT_LIGHT_SCREEN: @@ -4246,6 +4247,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } break; case MOVE_RAPID_SPIN: + case MOVE_MORTAL_SPIN: if (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].status2 & STATUS2_WRAPPED) score += 3; break; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 66ef2f133..b9b0ef0cf 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -865,7 +865,7 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness, } // Handle other multi-strike moves - if (gBattleMoves[move].strikeCount > 1) + if (gBattleMoves[move].strikeCount > 1 && gBattleMoves[move].effect != EFFECT_TRIPLE_KICK) dmg *= gBattleMoves[move].strikeCount; else if (move == MOVE_WATER_SHURIKEN && gBattleMons[battlerAtk].species == SPECIES_GRENINJA_ASH) dmg *= 3; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ee5d80aab..aa5276805 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1747,6 +1747,8 @@ static void Cmd_accuracycheck(void) u16 type, move = cmd->move; u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move); + u16 gBattlerAttackerAbility = GetBattlerAbility(gBattlerAttacker); + u8 gBattlerAttackerHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); if (move == ACC_CURR_MOVE) move = gCurrentMove; @@ -1761,10 +1763,11 @@ static void Cmd_accuracycheck(void) gBattlescriptCurrInstr = cmd->nextInstr; } else if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT - || (gSpecialStatuses[gBattlerAttacker].multiHitOn && (gBattleMoves[move].effect != EFFECT_TRIPLE_KICK - || GetBattlerAbility(gBattlerAttacker) == ABILITY_SKILL_LINK))) + || (gSpecialStatuses[gBattlerAttacker].multiHitOn + && (gBattlerAttackerAbility == ABILITY_SKILL_LINK || gBattlerAttackerHoldEffect == HOLD_EFFECT_LOADED_DICE + || !(gBattleMoves[move].effect == EFFECT_TRIPLE_KICK || gBattleMoves[move].effect == EFFECT_POPULATION_BOMB)))) { - // No acc checks for second hit of Parental Bond or multi hit moves, except Triple Kick/Triple Axel + // No acc checks for second hit of Parental Bond or multi hit moves, except Triple Kick/Triple Axel/Population Bomb gBattlescriptCurrInstr = cmd->nextInstr; } else @@ -1781,16 +1784,16 @@ static void Cmd_accuracycheck(void) gBattlerAttacker, gBattlerTarget, move, - GetBattlerAbility(gBattlerAttacker), + gBattlerAttackerAbility, GetBattlerAbility(gBattlerTarget), - GetBattlerHoldEffect(gBattlerAttacker, TRUE), + gBattlerAttackerHoldEffect, GetBattlerHoldEffect(gBattlerTarget, TRUE) ); if (!RandomPercentage(RNG_ACCURACY, accuracy)) { gMoveResultFlags |= MOVE_RESULT_MISSED; - if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_BLUNDER_POLICY) + if (gBattlerAttackerHoldEffect == HOLD_EFFECT_BLUNDER_POLICY) gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && diff --git a/src/battle_util.c b/src/battle_util.c index 2ec783a87..2f200026b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3742,13 +3742,15 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType) } else if (gBattleMoves[gCurrentMove].strikeCount > 1) { - gMultiHitCounter = gBattleMoves[gCurrentMove].strikeCount; - PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 3, 0) - } - else if (gBattleMoves[gCurrentMove].effect == EFFECT_TRIPLE_KICK) - { - gMultiHitCounter = 3; - PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) + if (gBattleMoves[gCurrentMove].effect == EFFECT_POPULATION_BOMB && GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LOADED_DICE) + { + gMultiHitCounter = RandomUniform(RNG_LOADED_DICE, 4, 10); + } + else + { + gMultiHitCounter = gBattleMoves[gCurrentMove].strikeCount; + PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 3, 0) + } } #if B_BEAT_UP >= GEN_5 else if (gBattleMoves[gCurrentMove].effect == EFFECT_BEAT_UP) diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 75a7d1b96..c8c19573f 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -2886,6 +2886,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = .split = SPLIT_PHYSICAL, .zMoveEffect = Z_EFFECT_NONE, .makesContact = TRUE, + .strikeCount = 3, }, [MOVE_THIEF] = @@ -12357,6 +12358,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = .split = SPLIT_PHYSICAL, .zMoveEffect = Z_EFFECT_NONE, .makesContact = TRUE, + .strikeCount = 3, }, [MOVE_DUAL_WINGBEAT] = @@ -13152,7 +13154,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_POPULATION_BOMB] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_MULTI_HIT maybe? + .effect = EFFECT_POPULATION_BOMB, .power = 20, .type = TYPE_NORMAL, .accuracy = 90, @@ -13165,6 +13167,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = .makesContact = TRUE, .slicingMove = TRUE, .metronomeBanned = TRUE, + .strikeCount = 10, }, [MOVE_ICE_SPINNER] = @@ -13246,12 +13249,12 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_MORTAL_SPIN] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_MORTAL_SPIN + .effect = EFFECT_MORTAL_SPIN, .power = 30, .type = TYPE_POISON, .accuracy = 100, .pp = 15, - .secondaryEffectChance = 0, + .secondaryEffectChance = 100, .target = MOVE_TARGET_BOTH, .priority = 0, .split = SPLIT_PHYSICAL, diff --git a/test/move_effect_mortal_spin.c b/test/move_effect_mortal_spin.c new file mode 100644 index 000000000..c3dd04527 --- /dev/null +++ b/test/move_effect_mortal_spin.c @@ -0,0 +1,24 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_MORTAL_SPIN].effect == EFFECT_MORTAL_SPIN); +} + +SINGLE_BATTLE_TEST("Mortal Spin blows away hazards and poisons foe") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_STEALTH_ROCK); MOVE(player, MOVE_MORTAL_SPIN); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MORTAL_SPIN, player); + MESSAGE("Wobbuffet blew away Stealth Rock!"); + MESSAGE("Foe Wobbuffet was poisoned!"); + STATUS_ICON(opponent, poison: TRUE); + } +} + diff --git a/test/move_effect_multi_hit.c b/test/move_effect_multi_hit.c new file mode 100644 index 000000000..d3a265ebb --- /dev/null +++ b/test/move_effect_multi_hit.c @@ -0,0 +1,135 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_BULLET_SEED].effect == EFFECT_MULTI_HIT); +} + +SINGLE_BATTLE_TEST("Multi hit Moves hit the maximum amount with Skill Link") +{ + PASSES_RANDOMLY(100, 100, RNG_HITS); + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_SKILL_LINK); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BULLET_SEED); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + MESSAGE("Hit 5 time(s)!"); + } +} + +SINGLE_BATTLE_TEST("Multi hit Moves hit twice 35 Percent of the time") +{ + PASSES_RANDOMLY(35, 100, RNG_HITS); + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BULLET_SEED); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + MESSAGE("Hit 2 time(s)!"); + } +} + +SINGLE_BATTLE_TEST("Multi hit Moves hit thrice 35 Percent of the time") +{ + PASSES_RANDOMLY(35, 100, RNG_HITS); + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BULLET_SEED); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + MESSAGE("Hit 3 time(s)!"); + } +} + +SINGLE_BATTLE_TEST("Multi hit Moves hit four times 35 Percent of the time") +{ + PASSES_RANDOMLY(15, 100, RNG_HITS); + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BULLET_SEED); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + MESSAGE("Hit 4 time(s)!"); + } +} + +SINGLE_BATTLE_TEST("Multi hit Moves hit four times 35 Percent of the time") +{ + PASSES_RANDOMLY(15, 100, RNG_HITS); + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BULLET_SEED); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + MESSAGE("Hit 5 time(s)!"); + } +} + +SINGLE_BATTLE_TEST("Multi hit Moves hit at least four times with Loaded Dice") +{ + PASSES_RANDOMLY(50, 100, RNG_LOADED_DICE); + + GIVEN { + ASSUME(gItems[ITEM_LOADED_DICE].holdEffect == HOLD_EFFECT_LOADED_DICE); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LOADED_DICE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BULLET_SEED); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + MESSAGE("Hit 4 time(s)!"); + } +} + +SINGLE_BATTLE_TEST("Multi hit Moves hit five times 50 Percent of the time with Loaded Dice") +{ + PASSES_RANDOMLY(50, 100, RNG_LOADED_DICE); + + GIVEN { + ASSUME(gItems[ITEM_LOADED_DICE].holdEffect == HOLD_EFFECT_LOADED_DICE); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LOADED_DICE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BULLET_SEED); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player); + MESSAGE("Hit 5 time(s)!"); + } +} diff --git a/test/move_effect_population_bomb.c b/test/move_effect_population_bomb.c new file mode 100644 index 000000000..1305f456c --- /dev/null +++ b/test/move_effect_population_bomb.c @@ -0,0 +1,29 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Population Bomb can hit ten times") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_POPULATION_BOMB].strikeCount == 10); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_POPULATION_BOMB); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_POPULATION_BOMB, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POPULATION_BOMB, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POPULATION_BOMB, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POPULATION_BOMB, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POPULATION_BOMB, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POPULATION_BOMB, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POPULATION_BOMB, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POPULATION_BOMB, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POPULATION_BOMB, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POPULATION_BOMB, player); + MESSAGE("Hit 10 time(s)!"); + } +} + +TO_DO_BATTLE_TEST("Accuracy for Population Bomb is checked independently for each hit") +TO_DO_BATTLE_TEST("Accuracy for Population Bomb is only checked for the first hit with Skill Link") +TO_DO_BATTLE_TEST("Accuracy for Population Bomb is only checked for the first hit with Loaded Dice") diff --git a/test/move_effect_triple_kick.c b/test/move_effect_triple_kick.c index e0a91b011..4e47d6cc6 100644 --- a/test/move_effect_triple_kick.c +++ b/test/move_effect_triple_kick.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gBattleMoves[MOVE_TRIPLE_KICK].effect & EFFECT_TRIPLE_KICK); + ASSUME(gBattleMoves[MOVE_TRIPLE_KICK].effect == EFFECT_TRIPLE_KICK); } SINGLE_BATTLE_TEST("Triple Kick damage is increased by its base damage for each hit") @@ -29,3 +29,7 @@ SINGLE_BATTLE_TEST("Triple Kick damage is increased by its base damage for each EXPECT_MUL_EQ(firstHit, Q_4_12(3.0), thirdHit); } } + +TO_DO_BATTLE_TEST("Accuracy for Triple Kick is checked independently for each hit") +TO_DO_BATTLE_TEST("Accuracy for Triple Kick is only checked for the first hit with Skill Link") +TO_DO_BATTLE_TEST("Accuracy for Triple Kick is only checked for the first hit with Loaded Dice") diff --git a/test/move_flag_three_strikes.c b/test/move_flag_strike_count.c similarity index 76% rename from test/move_flag_three_strikes.c rename to test/move_flag_strike_count.c index 21debc730..0c71d6731 100644 --- a/test/move_flag_three_strikes.c +++ b/test/move_flag_strike_count.c @@ -1,7 +1,22 @@ #include "global.h" #include "test_battle.h" -SINGLE_BATTLE_TEST("Three-strike flag turns a move into a 3-hit move") +SINGLE_BATTLE_TEST("Two strike count turns a move into a 2-hit move") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_DOUBLE_KICK].strikeCount == 2); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_DOUBLE_KICK); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_KICK, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_KICK, player); + MESSAGE("Hit 2 time(s)!"); + } +} + +SINGLE_BATTLE_TEST("Three strike count turns a move into a 3-hit move") { s16 firstHit; s16 secondHit;