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

View file

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

View file

@ -336,6 +336,7 @@ static bool8 CanAbilityPreventStatLoss(u16 abilityDef);
static bool8 CanBurnHitThaw(u16 move); static bool8 CanBurnHitThaw(u16 move);
static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent); static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent);
static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 usedMove); 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_attackcanceler(void);
static void Cmd_accuracycheck(void); static void Cmd_accuracycheck(void);
@ -1683,11 +1684,9 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
return calc; 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;
u32 type, move = cmd->move;
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move); u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move);
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker); u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
u32 abilityDef = GetBattlerAbility(gBattlerTarget); u32 abilityDef = GetBattlerAbility(gBattlerTarget);
@ -1699,11 +1698,11 @@ static void Cmd_accuracycheck(void)
if (move == NO_ACC_CALC_CHECK_LOCK_ON) if (move == NO_ACC_CALC_CHECK_LOCK_ON)
{ {
if (gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker) if (gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker)
gBattlescriptCurrInstr = cmd->nextInstr; gBattlescriptCurrInstr = nextInstr;
else if (gStatuses3[gBattlerTarget] & (STATUS3_SEMI_INVULNERABLE)) else if (gStatuses3[gBattlerTarget] & (STATUS3_SEMI_INVULNERABLE))
gBattlescriptCurrInstr = cmd->failInstr; gBattlescriptCurrInstr = failInstr;
else if (!JumpIfMoveAffectedByProtect(gCurrentMove)) else if (!JumpIfMoveAffectedByProtect(gCurrentMove))
gBattlescriptCurrInstr = cmd->nextInstr; gBattlescriptCurrInstr = nextInstr;
} }
else if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT else if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT
|| (gSpecialStatuses[gBattlerAttacker].multiHitOn || (gSpecialStatuses[gBattlerAttacker].multiHitOn
@ -1711,7 +1710,7 @@ static void Cmd_accuracycheck(void)
|| !(gMovesInfo[move].effect == EFFECT_TRIPLE_KICK || gMovesInfo[move].effect == EFFECT_POPULATION_BOMB)))) || !(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 // 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 else
{ {
@ -1739,6 +1738,18 @@ static void Cmd_accuracycheck(void)
if (holdEffectAtk == HOLD_EFFECT_BLUNDER_POLICY) if (holdEffectAtk == HOLD_EFFECT_BLUNDER_POLICY)
gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks 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 && if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE &&
(moveTarget == MOVE_TARGET_BOTH || moveTarget == MOVE_TARGET_FOES_AND_ALLY)) (moveTarget == MOVE_TARGET_BOTH || moveTarget == MOVE_TARGET_FOES_AND_ALLY))
gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_ATK; 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) static void Cmd_attackstring(void)
{ {
CMD_ARGS(); CMD_ARGS();
@ -5960,10 +5981,11 @@ static void Cmd_moveend(void)
} }
else else
{ {
if (gCurrentMove == MOVE_DRAGON_DARTS) if (gMovesInfo[gCurrentMove].effect == EFFECT_DRAGON_DARTS
{ && gBattleStruct->moveTarget[gBattlerAttacker] == gBattlerTarget // Haven't already changed targets
// TODO && CanTargetPartner(gBattlerAttacker, gBattlerTarget)
} && !TargetFullyImmuneToCurrMove(gBattlerAttacker, BATTLE_PARTNER(gBattlerTarget)))
gBattlerTarget = BATTLE_PARTNER(gBattlerTarget); // Target the partner in doubles for second hit.
if (gBattleMons[gBattlerAttacker].hp if (gBattleMons[gBattlerAttacker].hp
&& gBattleMons[gBattlerTarget].hp && gBattleMons[gBattlerTarget].hp

View file

@ -3629,6 +3629,11 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
{ {
gMultiHitCounter = gMovesInfo[gCurrentMove].strikeCount; gMultiHitCounter = gMovesInfo[gCurrentMove].strikeCount;
PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 3, 0) 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) 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); BattleScriptPushCursorAndCallback(BattleScript_OverworldWeatherStarts);
} }
break; break;
case ABILITYEFFECT_ON_SWITCHIN: // 0 case ABILITYEFFECT_ON_SWITCHIN:
gBattleScripting.battler = battler; gBattleScripting.battler = battler;
switch (gLastUsedAbility) switch (gLastUsedAbility)
{ {
@ -4845,7 +4850,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
break; break;
} }
break; break;
case ABILITYEFFECT_ENDTURN: // 1 case ABILITYEFFECT_ENDTURN:
if (IsBattlerAlive(battler)) if (IsBattlerAlive(battler))
{ {
gBattlerAttacker = battler; gBattlerAttacker = battler;
@ -5036,18 +5041,20 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
} }
} }
break; break;
case ABILITYEFFECT_MOVES_BLOCK: // 2 case ABILITYEFFECT_MOVES_BLOCK:
case ABILITYEFFECT_WOULD_BLOCK:
{ {
u16 moveTarget = GetBattlerMoveTargetType(battler, move); u16 moveTarget = GetBattlerMoveTargetType(battler, move);
u16 battlerAbility = GetBattlerAbility(battler); u16 battlerAbility = GetBattlerAbility(battler);
u16 targetAbility = GetBattlerAbility(gBattlerTarget); u16 targetAbility = GetBattlerAbility(gBattlerTarget);
const u8 * battleScriptBlocksMove = NULL;
if ((gLastUsedAbility == ABILITY_SOUNDPROOF && gMovesInfo[move].soundMove && !(moveTarget & MOVE_TARGET_USER)) if ((gLastUsedAbility == ABILITY_SOUNDPROOF && gMovesInfo[move].soundMove && !(moveTarget & MOVE_TARGET_USER))
|| (gLastUsedAbility == ABILITY_BULLETPROOF && gMovesInfo[move].ballisticMove)) || (gLastUsedAbility == ABILITY_BULLETPROOF && gMovesInfo[move].ballisticMove))
{ {
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS) if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
gHitMarker |= HITMARKER_NO_PPDEDUCT; gHitMarker |= HITMARKER_NO_PPDEDUCT;
gBattlescriptCurrInstr = BattleScript_SoundproofProtected; battleScriptBlocksMove = BattleScript_SoundproofProtected;
effect = 1; effect = 1;
} }
else if ((gLastUsedAbility == ABILITY_DAZZLING || gLastUsedAbility == ABILITY_QUEENLY_MAJESTY || gLastUsedAbility == ABILITY_ARMOR_TAIL || IsBattlerAlive(battler ^= BIT_FLANK)) 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) if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
gHitMarker |= HITMARKER_NO_PPDEDUCT; gHitMarker |= HITMARKER_NO_PPDEDUCT;
gBattlescriptCurrInstr = BattleScript_DazzlingProtected; battleScriptBlocksMove = BattleScript_DazzlingProtected;
effect = 1; effect = 1;
} }
else if (GetChosenMovePriority(gBattlerAttacker) > 0 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))) 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 CancelMultiTurnMoves(gBattlerAttacker); // Don't cancel moves that can hit two targets bc one target might not be protected
gBattleScripting.battler = gBattlerAbility = gBattlerTarget; gBattleScripting.battler = gBattlerAbility = gBattlerTarget;
gBattlescriptCurrInstr = BattleScript_DarkTypePreventsPrankster; battleScriptBlocksMove = BattleScript_DarkTypePreventsPrankster;
effect = 1; effect = 1;
} }
else if (GetBattlerAbility(gBattlerTarget) == ABILITY_GOOD_AS_GOLD 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_OPPONENTS_FIELD)
&& !(moveTarget & MOVE_TARGET_ALL_BATTLERS)) && !(moveTarget & MOVE_TARGET_ALL_BATTLERS))
{ {
gBattlescriptCurrInstr = BattleScript_GoodAsGoldActivates; battleScriptBlocksMove = BattleScript_GoodAsGoldActivates;
effect = 1; effect = 1;
} }
if (caseID == ABILITYEFFECT_WOULD_BLOCK)
{
if (effect && gLastUsedAbility != 0xFFFF)
RecordAbilityBattle(battler, gLastUsedAbility);
return effect;
}
else if (effect)
{
gBattlescriptCurrInstr = battleScriptBlocksMove;
}
break; break;
} }
case ABILITYEFFECT_ABSORBING: // 3 case ABILITYEFFECT_ABSORBING:
case ABILITYEFFECT_WOULD_ABSORB:
if (move != MOVE_NONE) if (move != MOVE_NONE)
{ {
u8 statId = 0; u8 statId = 0;
@ -5114,31 +5133,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
effect = 2, statId = STAT_ATK; effect = 2, statId = STAT_ATK;
break; break;
case ABILITY_FLASH_FIRE: case ABILITY_FLASH_FIRE:
if (moveType == TYPE_FIRE if (moveType == TYPE_FIRE && (B_FLASH_FIRE_FROZEN >= GEN_5 || !(gBattleMons[battler].status1 & STATUS1_FREEZE)))
&& (B_FLASH_FIRE_FROZEN >= GEN_5 || !(gBattleMons[battler].status1 & STATUS1_FREEZE))) 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;
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;
}
}
break; break;
case ABILITY_WELL_BAKED_BODY: case ABILITY_WELL_BAKED_BODY:
if (moveType == TYPE_FIRE) if (moveType == TYPE_FIRE)
@ -5153,8 +5149,14 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
effect = 1; effect = 1;
break; 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)) 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); 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) if (effect)
gMultiHitCounter = 0; // Prevent multi-hit moves from hitting more than once after move has been absorbed. 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; break;
case ABILITYEFFECT_IMMUNITY: // 5 case ABILITYEFFECT_IMMUNITY:
for (battler = 0; battler < gBattlersCount; battler++) for (battler = 0; battler < gBattlersCount; battler++)
{ {
switch (GetBattlerAbility(battler)) switch (GetBattlerAbility(battler))
@ -5989,7 +6011,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
} }
} }
break; break;
case ABILITYEFFECT_ATK_SYNCHRONIZE: // 8 case ABILITYEFFECT_ATK_SYNCHRONIZE:
if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT)) if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT))
{ {
gHitMarker &= ~HITMARKER_SYNCHRONISE_EFFECT; gHitMarker &= ~HITMARKER_SYNCHRONISE_EFFECT;
@ -11000,13 +11022,12 @@ void TryRestoreHeldItems(void)
u16 lostItem = gBattleStruct->itemLost[i].originalItem; u16 lostItem = gBattleStruct->itemLost[i].originalItem;
// Check if the lost item is a berry and the mon is not holding it // 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; lostItem = ITEM_NONE;
// Check if the lost item should be restored // 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); 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; *(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, .battleScript = BattleScript_EffectPhotonGeyser,
.battleTvScore = 0, // TODO: Assign points .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( .description = COMPOUND_STRING(
"The user attacks twice. Two\n" "The user attacks twice. Two\n"
"targets are hit once each."), "targets are hit once each."),
.effect = EFFECT_HIT, // TODO: EFFECT_DRAGON_DARTS .effect = EFFECT_DRAGON_DARTS,
.power = 50, .power = 50,
.type = TYPE_DRAGON, .type = TYPE_DRAGON,
.accuracy = 100, .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)!");
}
}