mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-12-27 04:04:17 +01:00
Merge remote-tracking branch 'rhh/master' into upcoming
This commit is contained in:
commit
17f8f50a59
@ -8990,16 +8990,7 @@ BattleScript_PsychicSurgeActivates::
|
|||||||
call BattleScript_ActivateTerrainEffects
|
call BattleScript_ActivateTerrainEffects
|
||||||
end3
|
end3
|
||||||
|
|
||||||
BattleScript_HurtTarget_NoString:
|
|
||||||
orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE
|
|
||||||
healthbarupdate BS_TARGET
|
|
||||||
datahpupdate BS_TARGET
|
|
||||||
tryfaintmon BS_TARGET
|
|
||||||
return
|
|
||||||
|
|
||||||
BattleScript_BadDreamsActivates::
|
BattleScript_BadDreamsActivates::
|
||||||
call BattleScript_AbilityPopUp
|
|
||||||
setbyte sFIXED_ABILITY_POPUP, TRUE
|
|
||||||
setbyte gBattlerTarget, 0
|
setbyte gBattlerTarget, 0
|
||||||
BattleScript_BadDreamsLoop:
|
BattleScript_BadDreamsLoop:
|
||||||
jumpiftargetally BattleScript_BadDreamsIncrement
|
jumpiftargetally BattleScript_BadDreamsIncrement
|
||||||
@ -9008,16 +8999,32 @@ BattleScript_BadDreamsLoop:
|
|||||||
jumpifstatus BS_TARGET, STATUS1_SLEEP, BattleScript_BadDreams_Dmg
|
jumpifstatus BS_TARGET, STATUS1_SLEEP, BattleScript_BadDreams_Dmg
|
||||||
goto BattleScript_BadDreamsIncrement
|
goto BattleScript_BadDreamsIncrement
|
||||||
BattleScript_BadDreams_Dmg:
|
BattleScript_BadDreams_Dmg:
|
||||||
|
jumpifbyteequal sFIXED_ABILITY_POPUP, sZero, BattleScript_BadDreams_ShowPopUp
|
||||||
|
BattleScript_BadDreams_DmgAfterPopUp:
|
||||||
printstring STRINGID_BADDREAMSDMG
|
printstring STRINGID_BADDREAMSDMG
|
||||||
waitmessage B_WAIT_TIME_LONG
|
waitmessage B_WAIT_TIME_LONG
|
||||||
dmg_1_8_targethp
|
dmg_1_8_targethp
|
||||||
call BattleScript_HurtTarget_NoString
|
orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE
|
||||||
|
healthbarupdate BS_TARGET
|
||||||
|
datahpupdate BS_TARGET
|
||||||
|
jumpifhasnohp BS_TARGET, BattleScript_BadDreams_HidePopUp
|
||||||
BattleScript_BadDreamsIncrement:
|
BattleScript_BadDreamsIncrement:
|
||||||
addbyte gBattlerTarget, 1
|
addbyte gBattlerTarget, 1
|
||||||
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_BadDreamsLoop
|
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_BadDreamsLoop
|
||||||
BattleScript_BadDreamsEnd:
|
jumpifbyteequal sFIXED_ABILITY_POPUP, sZero, BattleScript_BadDreamsEnd
|
||||||
destroyabilitypopup
|
destroyabilitypopup
|
||||||
|
pause 15
|
||||||
|
BattleScript_BadDreamsEnd:
|
||||||
end3
|
end3
|
||||||
|
BattleScript_BadDreams_ShowPopUp:
|
||||||
|
copybyte gBattlerAbility, gBattlerAttacker
|
||||||
|
call BattleScript_AbilityPopUp
|
||||||
|
setbyte sFIXED_ABILITY_POPUP, TRUE
|
||||||
|
goto BattleScript_BadDreams_DmgAfterPopUp
|
||||||
|
BattleScript_BadDreams_HidePopUp:
|
||||||
|
destroyabilitypopup
|
||||||
|
tryfaintmon BS_TARGET
|
||||||
|
goto BattleScript_BadDreamsIncrement
|
||||||
|
|
||||||
BattleScript_TookAttack::
|
BattleScript_TookAttack::
|
||||||
attackstring
|
attackstring
|
||||||
|
@ -593,6 +593,7 @@ struct BattleStruct
|
|||||||
u8 wishPerishSongBattlerId;
|
u8 wishPerishSongBattlerId;
|
||||||
bool8 overworldWeatherDone;
|
bool8 overworldWeatherDone;
|
||||||
bool8 terrainDone;
|
bool8 terrainDone;
|
||||||
|
u8 isAtkCancelerForCalledMove; // Certain cases in atk canceler should only be checked once, when the original move is called, however others need to be checked the twice.
|
||||||
u8 atkCancellerTracker;
|
u8 atkCancellerTracker;
|
||||||
struct BattleTvMovePoints tvMovePoints;
|
struct BattleTvMovePoints tvMovePoints;
|
||||||
struct BattleTv tv;
|
struct BattleTv tv;
|
||||||
|
@ -85,6 +85,7 @@ bool32 ShouldLowerEvasion(u8 battlerAtk, u8 battlerDef, u16 defAbility);
|
|||||||
bool32 IsAffectedByPowder(u8 battler, u16 ability, u16 holdEffect);
|
bool32 IsAffectedByPowder(u8 battler, u16 ability, u16 holdEffect);
|
||||||
bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split);
|
bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split);
|
||||||
s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *effectiveness, bool32 considerZPower);
|
s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *effectiveness, bool32 considerZPower);
|
||||||
|
u32 GetNoOfHitsToKO(u32 dmg, s32 hp);
|
||||||
u8 GetMoveDamageResult(u16 move);
|
u8 GetMoveDamageResult(u16 move);
|
||||||
u32 GetCurrDamageHpPercent(u8 battlerAtk, u8 battlerDef);
|
u32 GetCurrDamageHpPercent(u8 battlerAtk, u8 battlerDef);
|
||||||
uq4_12_t AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef);
|
uq4_12_t AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef);
|
||||||
|
@ -136,6 +136,7 @@ u8 DoBattlerEndTurnEffects(void);
|
|||||||
bool8 HandleWishPerishSongOnTurnEnd(void);
|
bool8 HandleWishPerishSongOnTurnEnd(void);
|
||||||
bool8 HandleFaintedMonActions(void);
|
bool8 HandleFaintedMonActions(void);
|
||||||
void TryClearRageAndFuryCutter(void);
|
void TryClearRageAndFuryCutter(void);
|
||||||
|
void SetAtkCancellerForCalledMove(void);
|
||||||
u8 AtkCanceller_UnableToUseMove(void);
|
u8 AtkCanceller_UnableToUseMove(void);
|
||||||
u8 AtkCanceller_UnableToUseMove2(void);
|
u8 AtkCanceller_UnableToUseMove2(void);
|
||||||
bool8 HasNoMonsToSwitch(u8 battlerId, u8 r1, u8 r2);
|
bool8 HasNoMonsToSwitch(u8 battlerId, u8 r1, u8 r2);
|
||||||
@ -165,8 +166,10 @@ bool32 IsBattlerGrounded(u8 battlerId);
|
|||||||
bool32 IsBattlerAlive(u8 battlerId);
|
bool32 IsBattlerAlive(u8 battlerId);
|
||||||
u8 GetBattleMonMoveSlot(struct BattlePokemon *battleMon, u16 move);
|
u8 GetBattleMonMoveSlot(struct BattlePokemon *battleMon, u16 move);
|
||||||
u32 GetBattlerWeight(u8 battlerId);
|
u32 GetBattlerWeight(u8 battlerId);
|
||||||
|
u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower, u32 rolloutTimer);
|
||||||
|
u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter);
|
||||||
s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags);
|
s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags);
|
||||||
s32 CalculateMoveDamageAndEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, uq4_12_t *typeEffectivenessModifier);
|
s32 CalculateMoveDamageAndEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, uq4_12_t *typeEffectivenessModifier);
|
||||||
uq4_12_t CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities);
|
uq4_12_t CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities);
|
||||||
uq4_12_t CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef);
|
uq4_12_t CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 abilityDef);
|
||||||
uq4_12_t GetTypeModifier(u8 atkType, u8 defType);
|
uq4_12_t GetTypeModifier(u8 atkType, u8 defType);
|
||||||
|
@ -3118,6 +3118,19 @@ static bool32 IsPinchBerryItemEffect(u16 holdEffect)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 GetAIMostDamagingMoveId(u8 battlerAtk, u8 battlerDef)
|
||||||
|
{
|
||||||
|
u32 i, id = 0;
|
||||||
|
u32 mostDmg = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||||
|
{
|
||||||
|
if (AI_DATA->simulatedDmg[battlerAtk][battlerDef][i] > mostDmg)
|
||||||
|
id = i, mostDmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][i];
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
// AI_FLAG_CHECK_VIABILITY - a weird mix of increasing and decreasing scores
|
// AI_FLAG_CHECK_VIABILITY - a weird mix of increasing and decreasing scores
|
||||||
static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||||
{
|
{
|
||||||
@ -3138,6 +3151,14 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
|||||||
// check always hits
|
// check always hits
|
||||||
if (!IS_MOVE_STATUS(move) && gBattleMoves[move].accuracy == 0)
|
if (!IS_MOVE_STATUS(move) && gBattleMoves[move].accuracy == 0)
|
||||||
{
|
{
|
||||||
|
// If 2 moves can KO the target in the same number of turns, but one of them always hits and there is a risk the other move could miss, prioritize the always hits move.
|
||||||
|
if (gBattleMons[battlerDef].statStages[STAT_EVASION] > 6 || gBattleMons[battlerAtk].statStages[STAT_ACC] < 6)
|
||||||
|
{
|
||||||
|
u32 mostDmgMoveId = GetAIMostDamagingMoveId(battlerAtk, battlerDef);
|
||||||
|
u32 *dmgs = AI_DATA->simulatedDmg[battlerAtk][battlerDef];
|
||||||
|
if (GetNoOfHitsToKO(dmgs[mostDmgMoveId], gBattleMons[battlerDef].hp) == GetNoOfHitsToKO(dmgs[AI_THINKING_STRUCT->movesetIndex], gBattleMons[battlerDef].hp))
|
||||||
|
score++;
|
||||||
|
}
|
||||||
if (gBattleMons[battlerDef].statStages[STAT_EVASION] >= 10 || gBattleMons[battlerAtk].statStages[STAT_ACC] <= 2)
|
if (gBattleMons[battlerDef].statStages[STAT_EVASION] >= 10 || gBattleMons[battlerAtk].statStages[STAT_ACC] <= 2)
|
||||||
score++;
|
score++;
|
||||||
if (AI_RandLessThan(100) && (gBattleMons[battlerDef].statStages[STAT_EVASION] >= 8 || gBattleMons[battlerAtk].statStages[STAT_ACC] <= 4))
|
if (AI_RandLessThan(100) && (gBattleMons[battlerDef].statStages[STAT_EVASION] >= 8 || gBattleMons[battlerAtk].statStages[STAT_ACC] <= 4))
|
||||||
|
@ -766,7 +766,7 @@ static bool32 AI_GetIfCrit(u32 move, u8 battlerAtk, u8 battlerDef)
|
|||||||
|
|
||||||
s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness, bool32 considerZPower)
|
s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness, bool32 considerZPower)
|
||||||
{
|
{
|
||||||
s32 dmg, moveType, critDmg, normalDmg;
|
s32 dmg, moveType, critDmg, normalDmg, fixedBasePower, n;
|
||||||
s8 critChance;
|
s8 critChance;
|
||||||
uq4_12_t effectivenessMultiplier;
|
uq4_12_t effectivenessMultiplier;
|
||||||
|
|
||||||
@ -795,8 +795,22 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness,
|
|||||||
{
|
{
|
||||||
ProteanTryChangeType(battlerAtk, AI_DATA->abilities[battlerAtk], move, moveType);
|
ProteanTryChangeType(battlerAtk, AI_DATA->abilities[battlerAtk], move, moveType);
|
||||||
critChance = GetInverseCritChance(battlerAtk, battlerDef, move);
|
critChance = GetInverseCritChance(battlerAtk, battlerDef, move);
|
||||||
normalDmg = CalculateMoveDamageAndEffectiveness(move, battlerAtk, battlerDef, moveType, &effectivenessMultiplier);
|
// Certain moves like Rollout calculate damage based on values which change during the move execution, but before calling dmg calc.
|
||||||
critDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, 0, TRUE, FALSE, FALSE);
|
switch (gBattleMoves[move].effect)
|
||||||
|
{
|
||||||
|
case EFFECT_ROLLOUT:
|
||||||
|
n = gDisableStructs[battlerAtk].rolloutTimer - 1;
|
||||||
|
fixedBasePower = CalcRolloutBasePower(battlerAtk, gBattleMoves[move].power, n < 0 ? 5 : n);
|
||||||
|
break;
|
||||||
|
case EFFECT_FURY_CUTTER:
|
||||||
|
fixedBasePower = CalcFuryCutterBasePower(gBattleMoves[move].power, min(gDisableStructs[battlerAtk].furyCutterCounter + 1, 5));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fixedBasePower = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
normalDmg = CalculateMoveDamageAndEffectiveness(move, battlerAtk, battlerDef, moveType, fixedBasePower, &effectivenessMultiplier);
|
||||||
|
critDmg = CalculateMoveDamage(move, battlerAtk, battlerDef, moveType, fixedBasePower, TRUE, FALSE, FALSE);
|
||||||
|
|
||||||
if (critChance == -1)
|
if (critChance == -1)
|
||||||
dmg = normalDmg;
|
dmg = normalDmg;
|
||||||
@ -914,6 +928,11 @@ static u32 WhichMoveBetter(u32 move1, u32 move2)
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 GetNoOfHitsToKO(u32 dmg, s32 hp)
|
||||||
|
{
|
||||||
|
return hp / (dmg + 1) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
u8 GetMoveDamageResult(u16 move)
|
u8 GetMoveDamageResult(u16 move)
|
||||||
{
|
{
|
||||||
s32 i, checkedMove, bestId, currId, hp;
|
s32 i, checkedMove, bestId, currId, hp;
|
||||||
@ -979,9 +998,8 @@ u8 GetMoveDamageResult(u16 move)
|
|||||||
currId = AI_THINKING_STRUCT->movesetIndex;
|
currId = AI_THINKING_STRUCT->movesetIndex;
|
||||||
if (currId == bestId)
|
if (currId == bestId)
|
||||||
AI_THINKING_STRUCT->funcResult = MOVE_POWER_BEST;
|
AI_THINKING_STRUCT->funcResult = MOVE_POWER_BEST;
|
||||||
// Compare percentage difference.
|
|
||||||
else if ((moveDmgs[currId] >= hp || moveDmgs[bestId] < hp) // If current move can faint as well, or if neither can
|
else if ((moveDmgs[currId] >= hp || moveDmgs[bestId] < hp) // If current move can faint as well, or if neither can
|
||||||
&& (moveDmgs[bestId] * 100 / hp) - (moveDmgs[currId] * 100 / hp) <= 30
|
&& GetNoOfHitsToKO(moveDmgs[currId], hp) - GetNoOfHitsToKO(moveDmgs[bestId], hp) <= 2 // Consider a move weak if it needs to be used at least 2 times more to faint the target, compared to the best move.
|
||||||
&& WhichMoveBetter(gBattleMons[sBattler_AI].moves[bestId], gBattleMons[sBattler_AI].moves[currId]) != 0)
|
&& WhichMoveBetter(gBattleMons[sBattler_AI].moves[bestId], gBattleMons[sBattler_AI].moves[currId]) != 0)
|
||||||
AI_THINKING_STRUCT->funcResult = MOVE_POWER_GOOD;
|
AI_THINKING_STRUCT->funcResult = MOVE_POWER_GOOD;
|
||||||
else
|
else
|
||||||
|
@ -350,7 +350,7 @@ static const u8 sText_DontLeaveBirch[] = _("PROF. BIRCH: Don't leave me like thi
|
|||||||
static const u8 sText_ButNothingHappened[] = _("But nothing happened!");
|
static const u8 sText_ButNothingHappened[] = _("But nothing happened!");
|
||||||
static const u8 sText_ButItFailed[] = _("But it failed!");
|
static const u8 sText_ButItFailed[] = _("But it failed!");
|
||||||
static const u8 sText_ItHurtConfusion[] = _("It hurt itself in its\nconfusion!");
|
static const u8 sText_ItHurtConfusion[] = _("It hurt itself in its\nconfusion!");
|
||||||
static const u8 sText_MirrorMoveFailed[] = _("The MIRROR MOVE failed!");
|
static const u8 sText_MirrorMoveFailed[] = _("The Mirror Move failed!");
|
||||||
static const u8 sText_StartedToRain[] = _("It started to rain!");
|
static const u8 sText_StartedToRain[] = _("It started to rain!");
|
||||||
static const u8 sText_DownpourStarted[] = _("A downpour started!"); // corresponds to DownpourText in pokegold and pokecrystal and is used by Rain Dance in GSC
|
static const u8 sText_DownpourStarted[] = _("A downpour started!"); // corresponds to DownpourText in pokegold and pokecrystal and is used by Rain Dance in GSC
|
||||||
static const u8 sText_RainContinues[] = _("Rain continues to fall.");
|
static const u8 sText_RainContinues[] = _("Rain continues to fall.");
|
||||||
|
@ -1384,7 +1384,8 @@ static void Cmd_attackcanceler(void)
|
|||||||
PressurePPLose(gBattlerAttacker, gBattlerTarget, MOVE_MAGIC_COAT);
|
PressurePPLose(gBattlerAttacker, gBattlerTarget, MOVE_MAGIC_COAT);
|
||||||
gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE;
|
gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE;
|
||||||
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
|
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
|
||||||
gBattleStruct->atkCancellerTracker = CANCELLER_POWDER_MOVE; // Edge case for bouncing a powder move against a grass type pokemon.
|
// Edge case for bouncing a powder move against a grass type pokemon.
|
||||||
|
SetAtkCancellerForCalledMove();
|
||||||
if (BlocksPrankster(gCurrentMove, gBattlerTarget, gBattlerAttacker, TRUE))
|
if (BlocksPrankster(gCurrentMove, gBattlerTarget, gBattlerAttacker, TRUE))
|
||||||
{
|
{
|
||||||
// Opponent used a prankster'd magic coat -> reflected status move should fail against a dark-type attacker
|
// Opponent used a prankster'd magic coat -> reflected status move should fail against a dark-type attacker
|
||||||
@ -1404,7 +1405,8 @@ static void Cmd_attackcanceler(void)
|
|||||||
{
|
{
|
||||||
gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE;
|
gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE;
|
||||||
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
|
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
|
||||||
gBattleStruct->atkCancellerTracker = CANCELLER_POWDER_MOVE; // Edge case for bouncing a powder move against a grass type pokemon.
|
// Edge case for bouncing a powder move against a grass type pokemon.
|
||||||
|
SetAtkCancellerForCalledMove();
|
||||||
BattleScriptPushCursor();
|
BattleScriptPushCursor();
|
||||||
gBattlescriptCurrInstr = BattleScript_MagicCoatBounce;
|
gBattlescriptCurrInstr = BattleScript_MagicCoatBounce;
|
||||||
gBattlerAbility = gBattlerTarget;
|
gBattlerAbility = gBattlerTarget;
|
||||||
@ -6126,6 +6128,7 @@ static void Cmd_moveend(void)
|
|||||||
gBattleStruct->zmove.toBeUsed[gBattlerAttacker] = MOVE_NONE;
|
gBattleStruct->zmove.toBeUsed[gBattlerAttacker] = MOVE_NONE;
|
||||||
gBattleStruct->zmove.effect = EFFECT_HIT;
|
gBattleStruct->zmove.effect = EFFECT_HIT;
|
||||||
gBattleStruct->hitSwitchTargetFailed = FALSE;
|
gBattleStruct->hitSwitchTargetFailed = FALSE;
|
||||||
|
gBattleStruct->isAtkCancelerForCalledMove = FALSE;
|
||||||
gBattleScripting.moveendState++;
|
gBattleScripting.moveendState++;
|
||||||
break;
|
break;
|
||||||
case MOVEEND_COUNT:
|
case MOVEEND_COUNT:
|
||||||
@ -11174,12 +11177,20 @@ static void Cmd_tryhealhalfhealth(void)
|
|||||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SetMoveForMirrorMove(u32 move)
|
||||||
|
{
|
||||||
|
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||||||
|
gCurrentMove = move;
|
||||||
|
SetAtkCancellerForCalledMove();
|
||||||
|
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
||||||
|
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
|
||||||
|
}
|
||||||
|
|
||||||
static void Cmd_trymirrormove(void)
|
static void Cmd_trymirrormove(void)
|
||||||
{
|
{
|
||||||
CMD_ARGS();
|
CMD_ARGS();
|
||||||
|
|
||||||
s32 validMovesCount;
|
s32 i, validMovesCount;
|
||||||
s32 i;
|
|
||||||
u16 move;
|
u16 move;
|
||||||
u16 validMoves[MAX_BATTLERS_COUNT] = {0};
|
u16 validMoves[MAX_BATTLERS_COUNT] = {0};
|
||||||
|
|
||||||
@ -11188,7 +11199,6 @@ static void Cmd_trymirrormove(void)
|
|||||||
if (i != gBattlerAttacker)
|
if (i != gBattlerAttacker)
|
||||||
{
|
{
|
||||||
move = gBattleStruct->lastTakenMoveFrom[gBattlerAttacker][i];
|
move = gBattleStruct->lastTakenMoveFrom[gBattlerAttacker][i];
|
||||||
|
|
||||||
if (move != MOVE_NONE && move != MOVE_UNAVAILABLE)
|
if (move != MOVE_NONE && move != MOVE_UNAVAILABLE)
|
||||||
{
|
{
|
||||||
validMoves[validMovesCount] = move;
|
validMoves[validMovesCount] = move;
|
||||||
@ -11198,21 +11208,13 @@ static void Cmd_trymirrormove(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
move = gBattleStruct->lastTakenMove[gBattlerAttacker];
|
move = gBattleStruct->lastTakenMove[gBattlerAttacker];
|
||||||
|
|
||||||
if (move != MOVE_NONE && move != MOVE_UNAVAILABLE)
|
if (move != MOVE_NONE && move != MOVE_UNAVAILABLE)
|
||||||
{
|
{
|
||||||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
SetMoveForMirrorMove(move);
|
||||||
gCurrentMove = move;
|
|
||||||
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
|
||||||
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
|
|
||||||
}
|
}
|
||||||
else if (validMovesCount != 0)
|
else if (validMovesCount != 0)
|
||||||
{
|
{
|
||||||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
SetMoveForMirrorMove(validMoves[Random() % validMovesCount]);
|
||||||
i = Random() % validMovesCount;
|
|
||||||
gCurrentMove = validMoves[i];
|
|
||||||
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
|
||||||
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
|
|
||||||
}
|
}
|
||||||
else // no valid moves found
|
else // no valid moves found
|
||||||
{
|
{
|
||||||
@ -12769,6 +12771,7 @@ static void Cmd_metronome(void)
|
|||||||
if (!gBattleMoves[gCurrentMove].metronomeBanned)
|
if (!gBattleMoves[gCurrentMove].metronomeBanned)
|
||||||
{
|
{
|
||||||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||||||
|
SetAtkCancellerForCalledMove();
|
||||||
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
|
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
|
||||||
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
||||||
return;
|
return;
|
||||||
|
@ -3331,6 +3331,12 @@ void TryClearRageAndFuryCutter(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetAtkCancellerForCalledMove(void)
|
||||||
|
{
|
||||||
|
gBattleStruct->atkCancellerTracker = CANCELLER_HEAL_BLOCKED;
|
||||||
|
gBattleStruct->isAtkCancelerForCalledMove = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
u8 AtkCanceller_UnableToUseMove(void)
|
u8 AtkCanceller_UnableToUseMove(void)
|
||||||
{
|
{
|
||||||
u8 effect = 0;
|
u8 effect = 0;
|
||||||
@ -3513,7 +3519,7 @@ u8 AtkCanceller_UnableToUseMove(void)
|
|||||||
gBattleStruct->atkCancellerTracker++;
|
gBattleStruct->atkCancellerTracker++;
|
||||||
break;
|
break;
|
||||||
case CANCELLER_CONFUSED: // confusion
|
case CANCELLER_CONFUSED: // confusion
|
||||||
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION)
|
if (!gBattleStruct->isAtkCancelerForCalledMove && gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION)
|
||||||
{
|
{
|
||||||
if (!(gStatuses4[gBattlerAttacker] & STATUS4_INFINITE_CONFUSION))
|
if (!(gStatuses4[gBattlerAttacker] & STATUS4_INFINITE_CONFUSION))
|
||||||
gBattleMons[gBattlerAttacker].status2 -= STATUS2_CONFUSION_TURN(1);
|
gBattleMons[gBattlerAttacker].status2 -= STATUS2_CONFUSION_TURN(1);
|
||||||
@ -3549,7 +3555,7 @@ u8 AtkCanceller_UnableToUseMove(void)
|
|||||||
gBattleStruct->atkCancellerTracker++;
|
gBattleStruct->atkCancellerTracker++;
|
||||||
break;
|
break;
|
||||||
case CANCELLER_PARALYSED: // paralysis
|
case CANCELLER_PARALYSED: // paralysis
|
||||||
if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && !RandomPercentage(RNG_PARALYSIS, 75))
|
if (!gBattleStruct->isAtkCancelerForCalledMove && (gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && !RandomPercentage(RNG_PARALYSIS, 75))
|
||||||
{
|
{
|
||||||
gProtectStructs[gBattlerAttacker].prlzImmobility = TRUE;
|
gProtectStructs[gBattlerAttacker].prlzImmobility = TRUE;
|
||||||
// This is removed in FRLG and Emerald for some reason
|
// This is removed in FRLG and Emerald for some reason
|
||||||
@ -3561,7 +3567,7 @@ u8 AtkCanceller_UnableToUseMove(void)
|
|||||||
gBattleStruct->atkCancellerTracker++;
|
gBattleStruct->atkCancellerTracker++;
|
||||||
break;
|
break;
|
||||||
case CANCELLER_IN_LOVE: // infatuation
|
case CANCELLER_IN_LOVE: // infatuation
|
||||||
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION)
|
if (!gBattleStruct->isAtkCancelerForCalledMove && gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION)
|
||||||
{
|
{
|
||||||
gBattleScripting.battler = CountTrailingZeroBits((gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) >> 0x10);
|
gBattleScripting.battler = CountTrailingZeroBits((gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) >> 0x10);
|
||||||
if (!RandomPercentage(RNG_INFATUATION, 50))
|
if (!RandomPercentage(RNG_INFATUATION, 50))
|
||||||
@ -8388,6 +8394,24 @@ const struct TypePower gNaturalGiftTable[] =
|
|||||||
[ITEM_TO_BERRY(ITEM_MARANGA_BERRY)] = {TYPE_DARK, 100},
|
[ITEM_TO_BERRY(ITEM_MARANGA_BERRY)] = {TYPE_DARK, 100},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower, u32 rolloutTimer)
|
||||||
|
{
|
||||||
|
u32 i;
|
||||||
|
for (i = 1; i < (5 - rolloutTimer); i++)
|
||||||
|
basePower *= 2;
|
||||||
|
if (gBattleMons[battlerAtk].status2 & STATUS2_DEFENSE_CURL)
|
||||||
|
basePower *= 2;
|
||||||
|
return basePower;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter)
|
||||||
|
{
|
||||||
|
u32 i;
|
||||||
|
for (i = 1; i < furyCutterCounter; i++)
|
||||||
|
basePower *= 2;
|
||||||
|
return basePower;
|
||||||
|
}
|
||||||
|
|
||||||
static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef)
|
static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef)
|
||||||
{
|
{
|
||||||
u32 i;
|
u32 i;
|
||||||
@ -8424,14 +8448,10 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef)
|
|||||||
basePower = 10 * (MAX_FRIENDSHIP - gBattleMons[battlerAtk].friendship) / 25;
|
basePower = 10 * (MAX_FRIENDSHIP - gBattleMons[battlerAtk].friendship) / 25;
|
||||||
break;
|
break;
|
||||||
case EFFECT_FURY_CUTTER:
|
case EFFECT_FURY_CUTTER:
|
||||||
for (i = 1; i < gDisableStructs[battlerAtk].furyCutterCounter; i++)
|
basePower = CalcFuryCutterBasePower(basePower, gDisableStructs[battlerAtk].furyCutterCounter);
|
||||||
basePower *= 2;
|
|
||||||
break;
|
break;
|
||||||
case EFFECT_ROLLOUT:
|
case EFFECT_ROLLOUT:
|
||||||
for (i = 1; i < (5 - gDisableStructs[battlerAtk].rolloutTimer); i++)
|
basePower = CalcRolloutBasePower(battlerAtk, basePower, gDisableStructs[battlerAtk].rolloutTimer);
|
||||||
basePower *= 2;
|
|
||||||
if (gBattleMons[battlerAtk].status2 & STATUS2_DEFENSE_CURL)
|
|
||||||
basePower *= 2;
|
|
||||||
break;
|
break;
|
||||||
case EFFECT_MAGNITUDE:
|
case EFFECT_MAGNITUDE:
|
||||||
basePower = gBattleStruct->magnitudeBasePower;
|
basePower = gBattleStruct->magnitudeBasePower;
|
||||||
@ -9636,10 +9656,10 @@ s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32
|
|||||||
}
|
}
|
||||||
|
|
||||||
// for AI - get move damage and effectiveness with one function call
|
// for AI - get move damage and effectiveness with one function call
|
||||||
s32 CalculateMoveDamageAndEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, uq4_12_t *typeEffectivenessModifier)
|
s32 CalculateMoveDamageAndEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, uq4_12_t *typeEffectivenessModifier)
|
||||||
{
|
{
|
||||||
*typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, FALSE);
|
*typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, FALSE);
|
||||||
return DoMoveDamageCalc(move, battlerAtk, battlerDef, moveType, 0, FALSE, FALSE, FALSE, *typeEffectivenessModifier);
|
return DoMoveDamageCalc(move, battlerAtk, battlerDef, moveType, fixedBasePower, FALSE, FALSE, FALSE, *typeEffectivenessModifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void MulByTypeEffectiveness(uq4_12_t *modifier, u16 move, u8 moveType, u8 battlerDef, u8 defType, u8 battlerAtk, bool32 recordAbilities)
|
static void MulByTypeEffectiveness(uq4_12_t *modifier, u16 move, u8 moveType, u8 battlerDef, u8 defType, u8 battlerAtk, bool32 recordAbilities)
|
||||||
|
130
test/ability_bad_dreams.c
Normal file
130
test/ability_bad_dreams.c
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include "global.h"
|
||||||
|
#include "test_battle.h"
|
||||||
|
|
||||||
|
ASSUMPTIONS
|
||||||
|
{
|
||||||
|
ASSUME(P_GEN_4_POKEMON == TRUE); // Because only Darkrai can have this ability.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also checks that non-sleeping enemy is not affected.
|
||||||
|
SINGLE_BATTLE_TEST("Bad Dreams causes the sleeping enemy Pokemon to lose 1/8 of hp")
|
||||||
|
{
|
||||||
|
u32 status;
|
||||||
|
PARAMETRIZE { status = STATUS1_NONE; }
|
||||||
|
PARAMETRIZE { status = STATUS1_SLEEP; }
|
||||||
|
GIVEN {
|
||||||
|
PLAYER(SPECIES_DARKRAI);
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET) {Status1(status);}
|
||||||
|
} WHEN {
|
||||||
|
TURN {;}
|
||||||
|
} SCENE {
|
||||||
|
if (status == STATUS1_SLEEP) {
|
||||||
|
ABILITY_POPUP(player, ABILITY_BAD_DREAMS);
|
||||||
|
MESSAGE("Foe Wobbuffet is tormented!");
|
||||||
|
HP_BAR(opponent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
NONE_OF {
|
||||||
|
ABILITY_POPUP(player, ABILITY_BAD_DREAMS);
|
||||||
|
MESSAGE("Foe Wobbuffet is tormented!");
|
||||||
|
HP_BAR(opponent);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} THEN {
|
||||||
|
if (status == STATUS1_SLEEP) {
|
||||||
|
EXPECT_EQ(opponent->hp, opponent->maxHP - opponent->maxHP / 8);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
EXPECT_EQ(opponent->hp, opponent->maxHP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DOUBLE_BATTLE_TEST("Bad Dreams does not activate if only the partner Pokemon is sleeping")
|
||||||
|
{
|
||||||
|
GIVEN {
|
||||||
|
PLAYER(SPECIES_DARKRAI);
|
||||||
|
PLAYER(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET);
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET);
|
||||||
|
} WHEN {
|
||||||
|
TURN {;}
|
||||||
|
} SCENE {
|
||||||
|
NONE_OF {
|
||||||
|
ABILITY_POPUP(playerLeft, ABILITY_BAD_DREAMS);
|
||||||
|
MESSAGE("Wobbuffet is tormented!");
|
||||||
|
HP_BAR(playerRight);
|
||||||
|
};
|
||||||
|
} THEN {
|
||||||
|
EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP);
|
||||||
|
EXPECT_EQ(opponentRight->hp, opponentRight->maxHP);
|
||||||
|
EXPECT_EQ(playerRight->hp, playerRight->maxHP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DOUBLE_BATTLE_TEST("Bad Dreams activates for both sleeping pokemon on the player side")
|
||||||
|
{
|
||||||
|
GIVEN {
|
||||||
|
PLAYER(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
|
||||||
|
PLAYER(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
|
||||||
|
OPPONENT(SPECIES_DARKRAI);
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET);
|
||||||
|
} WHEN {
|
||||||
|
TURN {;}
|
||||||
|
} SCENE {
|
||||||
|
ABILITY_POPUP(opponentLeft, ABILITY_BAD_DREAMS);
|
||||||
|
MESSAGE("Wobbuffet is tormented!");
|
||||||
|
HP_BAR(playerLeft);
|
||||||
|
MESSAGE("Wobbuffet is tormented!");
|
||||||
|
HP_BAR(playerRight);
|
||||||
|
} THEN {
|
||||||
|
EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP);
|
||||||
|
EXPECT_EQ(opponentRight->hp, opponentRight->maxHP);
|
||||||
|
EXPECT_EQ(playerLeft->hp, playerLeft->maxHP - playerLeft->maxHP / 8);
|
||||||
|
EXPECT_EQ(playerRight->hp, playerRight->maxHP - playerRight->maxHP / 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DOUBLE_BATTLE_TEST("Bad Dreams faints both sleeping Pokemon on player side")
|
||||||
|
{
|
||||||
|
GIVEN {
|
||||||
|
PLAYER(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP); HP(1);}
|
||||||
|
PLAYER(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP); HP(1);}
|
||||||
|
PLAYER(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
|
||||||
|
PLAYER(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
|
||||||
|
OPPONENT(SPECIES_DARKRAI);
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET);
|
||||||
|
} WHEN {
|
||||||
|
TURN {SEND_OUT(playerLeft, 2); SEND_OUT(playerRight, 3);}
|
||||||
|
} SCENE {
|
||||||
|
ABILITY_POPUP(opponentLeft, ABILITY_BAD_DREAMS);
|
||||||
|
MESSAGE("Wobbuffet is tormented!");
|
||||||
|
HP_BAR(playerLeft);
|
||||||
|
MESSAGE("Wobbuffet fainted!");
|
||||||
|
MESSAGE("Wobbuffet is tormented!");
|
||||||
|
HP_BAR(playerRight);
|
||||||
|
MESSAGE("Wobbuffet fainted!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DOUBLE_BATTLE_TEST("Bad Dreams faints both sleeping Pokemon on opponent side")
|
||||||
|
{
|
||||||
|
GIVEN {
|
||||||
|
PLAYER(SPECIES_DARKRAI);
|
||||||
|
PLAYER(SPECIES_WOBBUFFET);
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP); HP(1);}
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP); HP(1);}
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
|
||||||
|
} WHEN {
|
||||||
|
TURN {SEND_OUT(opponentLeft, 2); SEND_OUT(opponentRight, 3);}
|
||||||
|
} SCENE {
|
||||||
|
ABILITY_POPUP(playerLeft, ABILITY_BAD_DREAMS);
|
||||||
|
MESSAGE("Foe Wobbuffet is tormented!");
|
||||||
|
HP_BAR(opponentLeft);
|
||||||
|
MESSAGE("Foe Wobbuffet fainted!");
|
||||||
|
MESSAGE("Foe Wobbuffet is tormented!");
|
||||||
|
HP_BAR(opponentRight);
|
||||||
|
MESSAGE("Foe Wobbuffet fainted!");
|
||||||
|
}
|
||||||
|
}
|
@ -56,7 +56,6 @@ SINGLE_BATTLE_TEST("Magic Bounce cannot bounce back powder moves against Grass T
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DOUBLE_BATTLE_TEST("Magic Bounce bounces back moves hitting both foes at two foes")
|
DOUBLE_BATTLE_TEST("Magic Bounce bounces back moves hitting both foes at two foes")
|
||||||
{
|
{
|
||||||
GIVEN {
|
GIVEN {
|
||||||
|
69
test/move_effect_metronome.c
Normal file
69
test/move_effect_metronome.c
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#include "global.h"
|
||||||
|
#include "test_battle.h"
|
||||||
|
|
||||||
|
ASSUMPTIONS
|
||||||
|
{
|
||||||
|
ASSUME(gBattleMoves[MOVE_METRONOME].effect == EFFECT_METRONOME);
|
||||||
|
}
|
||||||
|
|
||||||
|
// To do: Turn the seeds to work with WITH_RNG for Metronome.
|
||||||
|
#define RNG_METRONOME_SCRATCH 0x118
|
||||||
|
#define RNG_METRONOME_PSN_POWDER 0x119
|
||||||
|
#define RNG_METRONOME_ROCK_BLAST 0x1F5
|
||||||
|
|
||||||
|
SINGLE_BATTLE_TEST("Metronome picks a random move")
|
||||||
|
{
|
||||||
|
GIVEN {
|
||||||
|
RNGSeed(RNG_METRONOME_SCRATCH);
|
||||||
|
PLAYER(SPECIES_WOBBUFFET);
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET);
|
||||||
|
} WHEN {
|
||||||
|
TURN { MOVE(player, MOVE_METRONOME); }
|
||||||
|
} SCENE {
|
||||||
|
MESSAGE("Wobbuffet used Metronome!");
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player);
|
||||||
|
MESSAGE("Wobbuffet used Scratch!");
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
|
||||||
|
HP_BAR(opponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SINGLE_BATTLE_TEST("Metronome's called powder move fails against Grass Types")
|
||||||
|
{
|
||||||
|
GIVEN {
|
||||||
|
ASSUME(gBattleMoves[MOVE_POISON_POWDER].powderMove);
|
||||||
|
ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS);
|
||||||
|
ASSUME(gBattleMoves[MOVE_POISON_POWDER].effect == EFFECT_POISON);
|
||||||
|
RNGSeed(RNG_METRONOME_PSN_POWDER);
|
||||||
|
PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
|
||||||
|
OPPONENT(SPECIES_TANGELA) {Speed(2);}
|
||||||
|
} WHEN {
|
||||||
|
TURN { MOVE(player, MOVE_METRONOME); }
|
||||||
|
} SCENE {
|
||||||
|
MESSAGE("Wobbuffet used Metronome!");
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player);
|
||||||
|
MESSAGE("Wobbuffet used PoisonPowder!");
|
||||||
|
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_POWDER, player);
|
||||||
|
MESSAGE("It doesn't affect Foe Tangela…");
|
||||||
|
NOT STATUS_ICON(opponent, poison: TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SINGLE_BATTLE_TEST("Metronome's called multi-hit move hits multiple times")
|
||||||
|
{
|
||||||
|
GIVEN {
|
||||||
|
ASSUME(gBattleMoves[MOVE_ROCK_BLAST].effect == EFFECT_MULTI_HIT);
|
||||||
|
RNGSeed(RNG_METRONOME_ROCK_BLAST);
|
||||||
|
PLAYER(SPECIES_WOBBUFFET);
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET);
|
||||||
|
} WHEN {
|
||||||
|
TURN { MOVE(player, MOVE_METRONOME); }
|
||||||
|
} SCENE {
|
||||||
|
MESSAGE("Wobbuffet used Metronome!");
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player);
|
||||||
|
MESSAGE("Wobbuffet used Rock Blast!");
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_BLAST, player);
|
||||||
|
HP_BAR(opponent);
|
||||||
|
MESSAGE("Hit 4 time(s)!");
|
||||||
|
}
|
||||||
|
}
|
81
test/move_effect_mirror_move.c
Normal file
81
test/move_effect_mirror_move.c
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#include "global.h"
|
||||||
|
#include "test_battle.h"
|
||||||
|
|
||||||
|
ASSUMPTIONS
|
||||||
|
{
|
||||||
|
ASSUME(gBattleMoves[MOVE_MIRROR_MOVE].effect == EFFECT_MIRROR_MOVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
SINGLE_BATTLE_TEST("Mirror Move copies the last used move by the target")
|
||||||
|
{
|
||||||
|
GIVEN {
|
||||||
|
PLAYER(SPECIES_WOBBUFFET) {Speed(2);}
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET) {Speed(5);}
|
||||||
|
} WHEN {
|
||||||
|
TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_MIRROR_MOVE); }
|
||||||
|
} SCENE {
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||||
|
HP_BAR(player);
|
||||||
|
MESSAGE("Wobbuffet used Mirror Move!");
|
||||||
|
MESSAGE("Wobbuffet used Tackle!");
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||||
|
HP_BAR(opponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SINGLE_BATTLE_TEST("Mirror Move fails if no move was used before")
|
||||||
|
{
|
||||||
|
GIVEN {
|
||||||
|
PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET) {Speed(2);}
|
||||||
|
} WHEN {
|
||||||
|
TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_MIRROR_MOVE); }
|
||||||
|
} SCENE {
|
||||||
|
MESSAGE("Wobbuffet used Mirror Move!");
|
||||||
|
MESSAGE("The Mirror Move failed!");
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||||
|
HP_BAR(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SINGLE_BATTLE_TEST("Mirror Move's called powder move fails against Grass Types")
|
||||||
|
{
|
||||||
|
GIVEN {
|
||||||
|
ASSUME(gBattleMoves[MOVE_STUN_SPORE].powderMove);
|
||||||
|
ASSUME(gSpeciesInfo[SPECIES_ODDISH].types[0] == TYPE_GRASS);
|
||||||
|
ASSUME(gBattleMoves[MOVE_STUN_SPORE].effect == EFFECT_PARALYZE);
|
||||||
|
PLAYER(SPECIES_ODDISH) {Speed(5);}
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET) {Speed(2);}
|
||||||
|
} WHEN {
|
||||||
|
TURN { MOVE(player, MOVE_STUN_SPORE); MOVE(opponent, MOVE_MIRROR_MOVE); }
|
||||||
|
} SCENE {
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
|
||||||
|
STATUS_ICON(opponent, paralysis: TRUE);
|
||||||
|
MESSAGE("Foe Wobbuffet used Mirror Move!");
|
||||||
|
MESSAGE("Foe Wobbuffet used Stun Spore!");
|
||||||
|
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, opponent);
|
||||||
|
MESSAGE("It doesn't affect Oddish…");
|
||||||
|
NOT STATUS_ICON(player, paralysis: TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// It hits first 2 times, then 5 times with the default rng seed.
|
||||||
|
SINGLE_BATTLE_TEST("Mirror Move's called multi-hit move hits multiple times")
|
||||||
|
{
|
||||||
|
GIVEN {
|
||||||
|
ASSUME(gBattleMoves[MOVE_BULLET_SEED].effect == EFFECT_MULTI_HIT);
|
||||||
|
PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
|
||||||
|
OPPONENT(SPECIES_WOBBUFFET) {Speed(2);}
|
||||||
|
} WHEN {
|
||||||
|
TURN { MOVE(player, MOVE_BULLET_SEED); MOVE(opponent, MOVE_MIRROR_MOVE); }
|
||||||
|
} SCENE {
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player);
|
||||||
|
HP_BAR(opponent);
|
||||||
|
MESSAGE("Hit 2 time(s)!");
|
||||||
|
MESSAGE("Foe Wobbuffet used Mirror Move!");
|
||||||
|
MESSAGE("Foe Wobbuffet used Bullet Seed!");
|
||||||
|
ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, opponent);
|
||||||
|
HP_BAR(player);
|
||||||
|
MESSAGE("Hit 5 time(s)!");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user