Skip to content

Commit

Permalink
Adds Mortal Spin and Population Bomb (#3178)
Browse files Browse the repository at this point in the history
  • Loading branch information
AsparagusEduardo authored Aug 7, 2023
2 parents 34d38bd + 8ee3a3c commit fc66a8c
Show file tree
Hide file tree
Showing 13 changed files with 253 additions and 24 deletions.
1 change: 0 additions & 1 deletion data/battle_anim_scripts.s
Original file line number Diff line number Diff line change
Expand Up @@ -14883,7 +14883,6 @@ Move_CORROSIVE_GAS::
clearmonbg ANIM_ATTACKER
end


@Credits to Skeli
Move_COACHING::
playsewithpan SE_M_TAIL_WHIP, SOUND_PAN_ATTACKER
Expand Down
17 changes: 14 additions & 3 deletions data/battle_scripts_1.s
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -7787,7 +7798,7 @@ BattleScript_WishMegaEvolution::
BattleScript_PrimalReversion::
call BattleScript_PrimalReversionRet
end2

BattleScript_PrimalReversionRestoreAttacker::
call BattleScript_PrimalReversionRet
copybyte gBattlerAttacker, sSAVED_BATTLER
Expand Down
4 changes: 3 additions & 1 deletion include/constants/battle_move_effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions src/battle_ai_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/battle_ai_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
15 changes: 9 additions & 6 deletions src/battle_script_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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 &&
Expand Down
16 changes: 9 additions & 7 deletions src/battle_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 6 additions & 3 deletions src/data/battle_moves.h
Original file line number Diff line number Diff line change
Expand Up @@ -2886,6 +2886,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
.split = SPLIT_PHYSICAL,
.zMoveEffect = Z_EFFECT_NONE,
.makesContact = TRUE,
.strikeCount = 3,
},

[MOVE_THIEF] =
Expand Down Expand Up @@ -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] =
Expand Down Expand Up @@ -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,
Expand All @@ -13165,6 +13167,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
.makesContact = TRUE,
.slicingMove = TRUE,
.metronomeBanned = TRUE,
.strikeCount = 10,
},

[MOVE_ICE_SPINNER] =
Expand Down Expand Up @@ -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,
Expand Down
24 changes: 24 additions & 0 deletions test/move_effect_mortal_spin.c
Original file line number Diff line number Diff line change
@@ -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);
}
}

135 changes: 135 additions & 0 deletions test/move_effect_multi_hit.c
Original file line number Diff line number Diff line change
@@ -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)!");
}
}
29 changes: 29 additions & 0 deletions test/move_effect_population_bomb.c
Original file line number Diff line number Diff line change
@@ -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")
Loading

0 comments on commit fc66a8c

Please sign in to comment.