Adds Dragon Darts effect (CFRU port) (#4612)

* Adds Dragon Darts effect (CFRU port)

* fix test compile

* review
This commit is contained in:
Alex 2024-05-28 11:34:56 +02:00 committed by GitHub
parent be2517415b
commit 7a393a974a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 342 additions and 72 deletions

View file

@ -20,26 +20,31 @@
#define MOVE_LIMITATION_PLACEHOLDER (1 << 15)
#define MOVE_LIMITATIONS_ALL 0xFFFF
#define ABILITYEFFECT_ON_SWITCHIN 0
#define ABILITYEFFECT_ENDTURN 1
#define ABILITYEFFECT_MOVES_BLOCK 2
#define ABILITYEFFECT_ABSORBING 3
#define ABILITYEFFECT_MOVE_END_ATTACKER 4
#define ABILITYEFFECT_MOVE_END 5
#define ABILITYEFFECT_IMMUNITY 6
#define ABILITYEFFECT_SYNCHRONIZE 7
#define ABILITYEFFECT_ATK_SYNCHRONIZE 8
#define ABILITYEFFECT_TRACE1 9
#define ABILITYEFFECT_TRACE2 10
#define ABILITYEFFECT_MOVE_END_OTHER 11
#define ABILITYEFFECT_NEUTRALIZINGGAS 12
#define ABILITYEFFECT_FIELD_SPORT 13 // Only used if B_SPORT_TURNS >= GEN_6
#define ABILITYEFFECT_ON_WEATHER 14
#define ABILITYEFFECT_ON_TERRAIN 15
#define ABILITYEFFECT_SWITCH_IN_TERRAIN 16
#define ABILITYEFFECT_SWITCH_IN_WEATHER 17
#define ABILITYEFFECT_OPPORTUNIST 18
#define ABILITYEFFECT_SWITCH_IN_STATUSES 19
enum {
ABILITYEFFECT_ON_SWITCHIN,
ABILITYEFFECT_ENDTURN,
ABILITYEFFECT_MOVES_BLOCK,
ABILITYEFFECT_WOULD_BLOCK, // Checks immunity without triggering a script
ABILITYEFFECT_ABSORBING,
ABILITYEFFECT_WOULD_ABSORB, // Checks immunity without triggering a script
ABILITYEFFECT_MOVE_END_ATTACKER,
ABILITYEFFECT_MOVE_END,
ABILITYEFFECT_IMMUNITY,
ABILITYEFFECT_SYNCHRONIZE,
ABILITYEFFECT_ATK_SYNCHRONIZE,
ABILITYEFFECT_TRACE1,
ABILITYEFFECT_TRACE2,
ABILITYEFFECT_MOVE_END_OTHER,
ABILITYEFFECT_NEUTRALIZINGGAS,
ABILITYEFFECT_FIELD_SPORT, // Only used if B_SPORT_TURNS >= GEN_6
ABILITYEFFECT_ON_WEATHER,
ABILITYEFFECT_ON_TERRAIN,
ABILITYEFFECT_SWITCH_IN_TERRAIN,
ABILITYEFFECT_SWITCH_IN_WEATHER,
ABILITYEFFECT_OPPORTUNIST,
ABILITYEFFECT_SWITCH_IN_STATUSES,
};
// Special cases
#define ABILITYEFFECT_MUD_SPORT 252 // Only used if B_SPORT_TURNS >= GEN_6
#define ABILITYEFFECT_WATER_SPORT 253 // Only used if B_SPORT_TURNS >= GEN_6
@ -233,6 +238,8 @@ bool32 MoveHasAdditionalEffectWithChance(u32 move, u32 moveEffect, u32 chance);
bool32 MoveHasAdditionalEffectSelf(u32 move, u32 moveEffect);
bool32 MoveHasAdditionalEffectSelfArg(u32 move, u32 moveEffect, u32 argument);
bool32 MoveHasChargeTurnAdditionalEffect(u32 move);
bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef);
bool32 TargetFullyImmuneToCurrMove(u32 BattlerAtk, u32 battlerDef);
bool32 CanSleep(u32 battler);
bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget);

View file

@ -352,6 +352,7 @@ enum {
EFFECT_SPICY_EXTRACT,
EFFECT_TERA_BLAST,
EFFECT_TERA_STARSTORM,
EFFECT_DRAGON_DARTS,
NUM_BATTLE_MOVE_EFFECTS,
};

View file

@ -336,6 +336,7 @@ static bool8 CanAbilityPreventStatLoss(u16 abilityDef);
static bool8 CanBurnHitThaw(u16 move);
static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent);
static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 usedMove);
static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr, u16 move);
static void Cmd_attackcanceler(void);
static void Cmd_accuracycheck(void);
@ -1683,11 +1684,9 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
return calc;
}
static void Cmd_accuracycheck(void)
static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr, u16 move)
{
CMD_ARGS(const u8 *failInstr, u16 move);
u32 type, move = cmd->move;
u32 type;
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move);
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
u32 abilityDef = GetBattlerAbility(gBattlerTarget);
@ -1699,11 +1698,11 @@ static void Cmd_accuracycheck(void)
if (move == NO_ACC_CALC_CHECK_LOCK_ON)
{
if (gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker)
gBattlescriptCurrInstr = cmd->nextInstr;
gBattlescriptCurrInstr = nextInstr;
else if (gStatuses3[gBattlerTarget] & (STATUS3_SEMI_INVULNERABLE))
gBattlescriptCurrInstr = cmd->failInstr;
gBattlescriptCurrInstr = failInstr;
else if (!JumpIfMoveAffectedByProtect(gCurrentMove))
gBattlescriptCurrInstr = cmd->nextInstr;
gBattlescriptCurrInstr = nextInstr;
}
else if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT
|| (gSpecialStatuses[gBattlerAttacker].multiHitOn
@ -1711,7 +1710,7 @@ static void Cmd_accuracycheck(void)
|| !(gMovesInfo[move].effect == EFFECT_TRIPLE_KICK || gMovesInfo[move].effect == EFFECT_POPULATION_BOMB))))
{
// No acc checks for second hit of Parental Bond or multi hit moves, except Triple Kick/Triple Axel/Population Bomb
gBattlescriptCurrInstr = cmd->nextInstr;
gBattlescriptCurrInstr = nextInstr;
}
else
{
@ -1739,6 +1738,18 @@ static void Cmd_accuracycheck(void)
if (holdEffectAtk == HOLD_EFFECT_BLUNDER_POLICY)
gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks
if (gMovesInfo[gCurrentMove].effect == EFFECT_DRAGON_DARTS
&& !recalcDragonDarts // So we don't jump back and forth between targets
&& CanTargetPartner(gBattlerAttacker, gBattlerTarget)
&& !TargetFullyImmuneToCurrMove(gBattlerAttacker, BATTLE_PARTNER(gBattlerTarget)))
{
// Smart target to partner if miss
gBattlerTarget = BATTLE_PARTNER(gBattlerTarget);
gMoveResultFlags &= ~MOVE_RESULT_MISSED;
AccuracyCheck(TRUE, nextInstr, failInstr, move);
return;
}
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE &&
(moveTarget == MOVE_TARGET_BOTH || moveTarget == MOVE_TARGET_FOES_AND_ALLY))
gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_ATK;
@ -1752,6 +1763,16 @@ static void Cmd_accuracycheck(void)
}
}
static void Cmd_accuracycheck(void)
{
CMD_ARGS(const u8 *failInstr, u16 move);
// The main body of this function has been moved to AccuracyCheck() to accomodate
// Dragon Darts' multiple accuracy checks on a single attack;
// each dart can try to re-target once after missing.
AccuracyCheck(FALSE, cmd->nextInstr, cmd->failInstr, cmd->move);
}
static void Cmd_attackstring(void)
{
CMD_ARGS();
@ -5960,10 +5981,11 @@ static void Cmd_moveend(void)
}
else
{
if (gCurrentMove == MOVE_DRAGON_DARTS)
{
// TODO
}
if (gMovesInfo[gCurrentMove].effect == EFFECT_DRAGON_DARTS
&& gBattleStruct->moveTarget[gBattlerAttacker] == gBattlerTarget // Haven't already changed targets
&& CanTargetPartner(gBattlerAttacker, gBattlerTarget)
&& !TargetFullyImmuneToCurrMove(gBattlerAttacker, BATTLE_PARTNER(gBattlerTarget)))
gBattlerTarget = BATTLE_PARTNER(gBattlerTarget); // Target the partner in doubles for second hit.
if (gBattleMons[gBattlerAttacker].hp
&& gBattleMons[gBattlerTarget].hp

View file

@ -3629,6 +3629,11 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
{
gMultiHitCounter = gMovesInfo[gCurrentMove].strikeCount;
PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 3, 0)
if (gMovesInfo[gCurrentMove].effect == EFFECT_DRAGON_DARTS
&& CanTargetPartner(gBattlerAttacker, gBattlerTarget)
&& TargetFullyImmuneToCurrMove(gBattlerAttacker, gBattlerTarget))
gBattlerTarget = BATTLE_PARTNER(gBattlerTarget);
}
}
else if (B_BEAT_UP >= GEN_5 && gMovesInfo[gCurrentMove].effect == EFFECT_BEAT_UP)
@ -4275,7 +4280,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
BattleScriptPushCursorAndCallback(BattleScript_OverworldWeatherStarts);
}
break;
case ABILITYEFFECT_ON_SWITCHIN: // 0
case ABILITYEFFECT_ON_SWITCHIN:
gBattleScripting.battler = battler;
switch (gLastUsedAbility)
{
@ -4845,7 +4850,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
break;
}
break;
case ABILITYEFFECT_ENDTURN: // 1
case ABILITYEFFECT_ENDTURN:
if (IsBattlerAlive(battler))
{
gBattlerAttacker = battler;
@ -5036,18 +5041,20 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
}
break;
case ABILITYEFFECT_MOVES_BLOCK: // 2
case ABILITYEFFECT_MOVES_BLOCK:
case ABILITYEFFECT_WOULD_BLOCK:
{
u16 moveTarget = GetBattlerMoveTargetType(battler, move);
u16 battlerAbility = GetBattlerAbility(battler);
u16 targetAbility = GetBattlerAbility(gBattlerTarget);
const u8 * battleScriptBlocksMove = NULL;
if ((gLastUsedAbility == ABILITY_SOUNDPROOF && gMovesInfo[move].soundMove && !(moveTarget & MOVE_TARGET_USER))
|| (gLastUsedAbility == ABILITY_BULLETPROOF && gMovesInfo[move].ballisticMove))
{
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
gBattlescriptCurrInstr = BattleScript_SoundproofProtected;
battleScriptBlocksMove = BattleScript_SoundproofProtected;
effect = 1;
}
else if ((gLastUsedAbility == ABILITY_DAZZLING || gLastUsedAbility == ABILITY_QUEENLY_MAJESTY || gLastUsedAbility == ABILITY_ARMOR_TAIL || IsBattlerAlive(battler ^= BIT_FLANK))
@ -5057,7 +5064,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
{
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
gBattlescriptCurrInstr = BattleScript_DazzlingProtected;
battleScriptBlocksMove = BattleScript_DazzlingProtected;
effect = 1;
}
else if (GetChosenMovePriority(gBattlerAttacker) > 0
@ -5067,7 +5074,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE) || !(moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)))
CancelMultiTurnMoves(gBattlerAttacker); // Don't cancel moves that can hit two targets bc one target might not be protected
gBattleScripting.battler = gBattlerAbility = gBattlerTarget;
gBattlescriptCurrInstr = BattleScript_DarkTypePreventsPrankster;
battleScriptBlocksMove = BattleScript_DarkTypePreventsPrankster;
effect = 1;
}
else if (GetBattlerAbility(gBattlerTarget) == ABILITY_GOOD_AS_GOLD
@ -5076,12 +5083,24 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& !(moveTarget & MOVE_TARGET_OPPONENTS_FIELD)
&& !(moveTarget & MOVE_TARGET_ALL_BATTLERS))
{
gBattlescriptCurrInstr = BattleScript_GoodAsGoldActivates;
battleScriptBlocksMove = BattleScript_GoodAsGoldActivates;
effect = 1;
}
if (caseID == ABILITYEFFECT_WOULD_BLOCK)
{
if (effect && gLastUsedAbility != 0xFFFF)
RecordAbilityBattle(battler, gLastUsedAbility);
return effect;
}
else if (effect)
{
gBattlescriptCurrInstr = battleScriptBlocksMove;
}
break;
}
case ABILITYEFFECT_ABSORBING: // 3
case ABILITYEFFECT_ABSORBING:
case ABILITYEFFECT_WOULD_ABSORB:
if (move != MOVE_NONE)
{
u8 statId = 0;
@ -5114,31 +5133,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
effect = 2, statId = STAT_ATK;
break;
case ABILITY_FLASH_FIRE:
if (moveType == TYPE_FIRE
&& (B_FLASH_FIRE_FROZEN >= GEN_5 || !(gBattleMons[battler].status1 & STATUS1_FREEZE)))
{
if (!(gBattleResources->flags->flags[battler] & RESOURCE_FLAG_FLASH_FIRE))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FLASH_FIRE_BOOST;
if (gProtectStructs[gBattlerAttacker].notFirstStrike)
gBattlescriptCurrInstr = BattleScript_FlashFireBoost;
else
gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss;
gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_FLASH_FIRE;
effect = 3;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FLASH_FIRE_NO_BOOST;
if (gProtectStructs[gBattlerAttacker].notFirstStrike)
gBattlescriptCurrInstr = BattleScript_FlashFireBoost;
else
gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss;
effect = 3;
}
}
if (moveType == TYPE_FIRE && (B_FLASH_FIRE_FROZEN >= GEN_5 || !(gBattleMons[battler].status1 & STATUS1_FREEZE)))
effect = 3;
break;
case ABILITY_WELL_BAKED_BODY:
if (moveType == TYPE_FIRE)
@ -5153,8 +5149,14 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
effect = 1;
break;
}
if (caseID == ABILITYEFFECT_WOULD_ABSORB)
{
if (effect && gLastUsedAbility != 0xFFFF)
RecordAbilityBattle(battler, gLastUsedAbility);
if (effect == 1) // Drain Hp ability.
return effect;
}
else if (effect == 1) // Drain Hp ability.
{
if (BATTLER_MAX_HP(battler) || (B_HEAL_BLOCKING >= GEN_5 && gStatuses3[battler] & STATUS3_HEAL_BLOCK))
{
@ -5197,6 +5199,26 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
PREPARE_STAT_BUFFER(gBattleTextBuff1, statId);
}
}
else if (effect == 3)
{
if (!(gBattleResources->flags->flags[battler] & RESOURCE_FLAG_FLASH_FIRE))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FLASH_FIRE_BOOST;
if (gProtectStructs[gBattlerAttacker].notFirstStrike)
gBattlescriptCurrInstr = BattleScript_FlashFireBoost;
else
gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss;
gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_FLASH_FIRE;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FLASH_FIRE_NO_BOOST;
if (gProtectStructs[gBattlerAttacker].notFirstStrike)
gBattlescriptCurrInstr = BattleScript_FlashFireBoost;
else
gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss;
}
}
if (effect)
gMultiHitCounter = 0; // Prevent multi-hit moves from hitting more than once after move has been absorbed.
@ -5876,7 +5898,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
}
break;
case ABILITYEFFECT_IMMUNITY: // 5
case ABILITYEFFECT_IMMUNITY:
for (battler = 0; battler < gBattlersCount; battler++)
{
switch (GetBattlerAbility(battler))
@ -5989,7 +6011,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
}
break;
case ABILITYEFFECT_ATK_SYNCHRONIZE: // 8
case ABILITYEFFECT_ATK_SYNCHRONIZE:
if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT))
{
gHitMarker &= ~HITMARKER_SYNCHRONISE_EFFECT;
@ -11000,13 +11022,12 @@ void TryRestoreHeldItems(void)
u16 lostItem = gBattleStruct->itemLost[i].originalItem;
// Check if the lost item is a berry and the mon is not holding it
if (ItemId_GetPocket(lostItem) == POCKET_BERRIES && GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM) != lostItem)
if (ItemId_GetPocket(lostItem) == POCKET_BERRIES && GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM) != lostItem)
lostItem = ITEM_NONE;
// Check if the lost item should be restored
if ((lostItem != ITEM_NONE || returnNPCItems) && ItemId_GetPocket(lostItem) != POCKET_BERRIES)
if ((lostItem != ITEM_NONE || returnNPCItems) && ItemId_GetPocket(lostItem) != POCKET_BERRIES)
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &lostItem);
}
}
}
@ -11512,3 +11533,27 @@ void RemoveBattlerType(u32 battler, u8 type)
*(u8 *)(&gBattleMons[battler].type1 + i) = TYPE_MYSTERY;
}
}
bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef)
{
return (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& IsBattlerAlive(BATTLE_PARTNER(battlerDef))
&& battlerDef != BATTLE_PARTNER(battlerAtk));
}
static inline bool32 DoesCurrentTargetHaveAbilityImmunity(void)
{
return (AbilityBattleEffects(ABILITYEFFECT_WOULD_BLOCK, gBattlerTarget, 0, 0, 0)
|| AbilityBattleEffects(ABILITYEFFECT_WOULD_ABSORB, gBattlerTarget, 0, 0, 0));
}
bool32 TargetFullyImmuneToCurrMove(u32 BattlerAtk, u32 battlerDef)
{
u32 moveType = 0;
GET_MOVE_TYPE(gCurrentMove, moveType);
return ((CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, BattlerAtk, battlerDef, GetBattlerAbility(battlerDef), FALSE) == UQ_4_12(0.0))
|| IsBattlerProtected(battlerDef, gCurrentMove)
|| IsSemiInvulnerable(battlerDef, gCurrentMove)
|| DoesCurrentTargetHaveAbilityImmunity());
}

View file

@ -2243,4 +2243,10 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.battleScript = BattleScript_EffectPhotonGeyser,
.battleTvScore = 0, // TODO: Assign points
},
[EFFECT_DRAGON_DARTS] =
{
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
};

View file

@ -16586,7 +16586,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.description = COMPOUND_STRING(
"The user attacks twice. Two\n"
"targets are hit once each."),
.effect = EFFECT_HIT, // TODO: EFFECT_DRAGON_DARTS
.effect = EFFECT_DRAGON_DARTS,
.power = 50,
.type = TYPE_DRAGON,
.accuracy = 100,

View file

@ -0,0 +1,189 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_DRAGON_DARTS].effect == EFFECT_DRAGON_DARTS);
ASSUME(gSpeciesInfo[SPECIES_CLEFAIRY].types[0] == TYPE_FAIRY || gSpeciesInfo[SPECIES_CLEFAIRY].types[1] == TYPE_FAIRY);
}
SINGLE_BATTLE_TEST("Dragon Darts strikes twice")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_DRAGON_DARTS); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, player);
MESSAGE("Hit 2 time(s)!");
}
}
DOUBLE_BATTLE_TEST("Dragon Darts strikes each opponent once in a double battle")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
MESSAGE("Hit 2 time(s)!");
}
}
DOUBLE_BATTLE_TEST("Dragon Darts strikes the ally twice if the target protects")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_PROTECT); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
MESSAGE("Hit 2 time(s)!");
}
}
DOUBLE_BATTLE_TEST("Dragon Darts strikes the right ally twice if the target is a fairy type")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CLEFAIRY);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
MESSAGE("Hit 2 time(s)!");
}
}
DOUBLE_BATTLE_TEST("Dragon Darts strikes the left ally twice if the target is a fairy type")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CLEFAIRY);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentRight); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
MESSAGE("Hit 2 time(s)!");
}
}
DOUBLE_BATTLE_TEST("Dragon Darts strikes the ally twice if the target is in a semi-invulnerable turn")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_FLY].effect == EFFECT_SEMI_INVULNERABLE);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_FLY, target: playerLeft); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLY, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
MESSAGE("Hit 2 time(s)!");
}
}
DOUBLE_BATTLE_TEST("Dragon Darts is not effected by Wide Guard")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_WIDE_GUARD); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_WIDE_GUARD, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
MESSAGE("Hit 2 time(s)!");
}
}
DOUBLE_BATTLE_TEST("Dragon Darts strikes hit the ally if the target fainted")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerRight, MOVE_SONIC_BOOM, target: opponentLeft); MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SONIC_BOOM, playerRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
MESSAGE("Hit 2 time(s)!");
}
}
DOUBLE_BATTLE_TEST("Dragon Darts strikes left ally twice if one strike misses")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BRIGHT_POWDER); };
} WHEN {
TURN { MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentRight, hit: FALSE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentLeft);
MESSAGE("Hit 2 time(s)!");
}
}
DOUBLE_BATTLE_TEST("Dragon Darts strikes right ally twice if one strike misses")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BRIGHT_POWDER); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_DRAGON_DARTS, target: opponentLeft, hit: FALSE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, playerLeft);
HP_BAR(opponentRight);
MESSAGE("Hit 2 time(s)!");
}
}