Merge branch '_RHH/master' into _RHH/upcoming
# Conflicts: # src/battle_ai_util.c # src/battle_util.c
This commit is contained in:
commit
64f82cdd5f
30 changed files with 612 additions and 139 deletions
|
@ -98,7 +98,7 @@
|
|||
waitstate
|
||||
.endm
|
||||
|
||||
.macro multi_do type:req, partnerId:req, partnerPicId:req
|
||||
.macro multi_do type:req, partnerId:req
|
||||
special ReducePlayerPartyToSelectedMons
|
||||
setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SET_DATA
|
||||
setvar VAR_0x8005, FRONTIER_DATA_SELECTED_MON_ORDER
|
||||
|
@ -106,7 +106,6 @@
|
|||
setvar VAR_0x8004, SPECIAL_BATTLE_MULTI
|
||||
setvar VAR_0x8005, \type | MULTI_BATTLE_CHOOSE_MONS
|
||||
setvar VAR_0x8006, \partnerId
|
||||
setvar VAR_0x8007, \partnerPicId
|
||||
special DoSpecialTrainerBattle
|
||||
waitstate
|
||||
setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY
|
||||
|
@ -114,30 +113,29 @@
|
|||
special LoadPlayerParty
|
||||
.endm
|
||||
|
||||
.macro multi_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req, partnerPicId:req
|
||||
.macro multi_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req
|
||||
special SavePlayerParty
|
||||
trainerbattle TRAINER_BATTLE_SET_TRAINER_A, \trainer1Id, 0, NULL, \trainer1LoseText @ set first trainer mons
|
||||
trainerbattle TRAINER_BATTLE_SET_TRAINER_B, \trainer2Id, 0, NULL, \trainer2LoseText @ set second trainer mons
|
||||
multi_do MULTI_BATTLE_2_VS_2, \partnerId, \partnerPicId
|
||||
multi_do MULTI_BATTLE_2_VS_2, \partnerId
|
||||
.endm
|
||||
|
||||
.macro multi_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req, partnerPicId:req
|
||||
.macro multi_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req
|
||||
special SavePlayerParty
|
||||
trainerbattle TRAINER_BATTLE_SET_TRAINER_A, \trainer1Id, 0, NULL, \trainer1LoseText @ set first trainer mons
|
||||
multi_do MULTI_BATTLE_2_VS_1, \partnerId, \partnerPicId
|
||||
multi_do MULTI_BATTLE_2_VS_1, \partnerId
|
||||
.endm
|
||||
|
||||
@ Wild mons need to be assigned to gEnemyParty 0 and 3 slots, other slots need to be cleared out.
|
||||
.macro multi_wild partnerId:req, partnerPicId:req
|
||||
.macro multi_wild partnerId:req
|
||||
special SavePlayerParty
|
||||
multi_do MULTI_BATTLE_2_VS_WILD, \partnerId, \partnerPicId
|
||||
multi_do MULTI_BATTLE_2_VS_WILD, \partnerId
|
||||
.endm
|
||||
|
||||
.macro multi_do_fixed type:req, partnerId:req, partnerPicId:req
|
||||
.macro multi_do_fixed type:req, partnerId:req
|
||||
setvar VAR_0x8004, SPECIAL_BATTLE_MULTI
|
||||
setvar VAR_0x8005, \type
|
||||
setvar VAR_0x8006, \partnerId
|
||||
setvar VAR_0x8007, \partnerPicId
|
||||
special DoSpecialTrainerBattle
|
||||
waitstate
|
||||
setvar VAR_0x8004, FRONTIER_UTIL_FUNC_SAVE_PARTY
|
||||
|
@ -145,21 +143,21 @@
|
|||
special LoadPlayerParty
|
||||
.endm
|
||||
|
||||
.macro multi_fixed_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req, partnerPicId:req
|
||||
.macro multi_fixed_2_vs_2 trainer1Id:req, trainer1LoseText:req, trainer2Id:req, trainer2LoseText:req, partnerId:req
|
||||
special SavePlayerParty
|
||||
trainerbattle TRAINER_BATTLE_SET_TRAINER_A, \trainer1Id, 0, NULL, \trainer1LoseText @ set first trainer mons
|
||||
trainerbattle TRAINER_BATTLE_SET_TRAINER_B, \trainer2Id, 0, NULL, \trainer2LoseText @ set second trainer mons
|
||||
multi_do_fixed MULTI_BATTLE_2_VS_2, \partnerId, \partnerPicId
|
||||
multi_do_fixed MULTI_BATTLE_2_VS_2, \partnerId
|
||||
.endm
|
||||
|
||||
.macro multi_fixed_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req, partnerPicId:req
|
||||
.macro multi_fixed_2_vs_1 trainer1Id:req, trainer1LoseText:req, partnerId:req
|
||||
special SavePlayerParty
|
||||
trainerbattle TRAINER_BATTLE_SET_TRAINER_A, \trainer1Id, 0, NULL, \trainer1LoseText @ set first trainer mons
|
||||
multi_do_fixed MULTI_BATTLE_2_VS_1, \partnerId, \partnerPicId
|
||||
multi_do_fixed MULTI_BATTLE_2_VS_1, \partnerId
|
||||
.endm
|
||||
|
||||
@ Wild mons need to be assigned to gEnemyParty 0 and 3 slots, other slots need to be cleared out.
|
||||
.macro multi_fixed_wild partnerId:req, partnerPicId:req
|
||||
.macro multi_fixed_wild partnerId:req
|
||||
special SavePlayerParty
|
||||
multi_do_fixed MULTI_BATTLE_2_VS_WILD, \partnerId, \partnerPicId
|
||||
multi_do_fixed MULTI_BATTLE_2_VS_WILD, \partnerId
|
||||
.endm
|
||||
|
|
|
@ -257,7 +257,7 @@ MossdeepCity_SpaceCenter_2F_EventScript_ChoosePartyForMultiBattle::
|
|||
goto MossdeepCity_SpaceCenter_2F_EventScript_ReadyForBattlePrompt
|
||||
|
||||
MossdeepCity_SpaceCenter_2F_EventScript_DoStevenMultiBattle::
|
||||
multi_2_vs_2 TRAINER_MAXIE_MOSSDEEP, MossdeepCity_SpaceCenter_2F_Text_JustWantToExpandLand, TRAINER_TABITHA_MOSSDEEP, MossdeepCity_SpaceCenter_Text_TabithaDefeat, PARTNER_STEVEN, TRAINER_BACK_PIC_STEVEN
|
||||
multi_2_vs_2 TRAINER_MAXIE_MOSSDEEP, MossdeepCity_SpaceCenter_2F_Text_JustWantToExpandLand, TRAINER_TABITHA_MOSSDEEP, MossdeepCity_SpaceCenter_Text_TabithaDefeat, PARTNER_STEVEN
|
||||
switch VAR_RESULT
|
||||
case 1, MossdeepCity_SpaceCenter_2F_EventScript_DefeatedMaxieTabitha
|
||||
fadescreen FADE_TO_BLACK
|
||||
|
|
|
@ -1133,7 +1133,6 @@ extern u16 gMoveToLearn;
|
|||
extern u32 gFieldStatuses;
|
||||
extern struct FieldTimer gFieldTimers;
|
||||
extern u8 gBattlerAbility;
|
||||
extern u16 gPartnerSpriteId;
|
||||
extern struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT];
|
||||
extern const struct BattleMoveEffect gBattleMoveEffects[];
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ struct PickupItem
|
|||
|
||||
s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk);
|
||||
s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility);
|
||||
s32 GetCritHitChance(s32 critChanceIndex);
|
||||
s32 GetCritHitOdds(s32 critChanceIndex);
|
||||
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
|
||||
u8 GetBattlerTurnOrderNum(u8 battlerId);
|
||||
bool32 NoAliveMonsForPlayer(void);
|
||||
|
|
|
@ -256,7 +256,6 @@ bool32 CanBeConfused(u32 battler);
|
|||
bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag);
|
||||
u32 GetBattlerAffectionHearts(u32 battler);
|
||||
u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc);
|
||||
bool32 IsMyceliumMightOnField(void);
|
||||
bool32 ChangeTypeBasedOnTerrain(u32 battler);
|
||||
void RemoveConfusionStatus(u32 battler);
|
||||
u8 GetBattlerGender(u32 battler);
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
// param2: time of day to check, optional.
|
||||
// - DAY if Form change that activates in the daytime.
|
||||
// - NIGHT if Form change that activates at nighttime.
|
||||
// - 0 if irrelevant, but param3 is necessary.
|
||||
// param3: illegal statuses to have, optional.
|
||||
#define FORM_CHANGE_ITEM_USE 2
|
||||
|
||||
// TODO: Form change that activates when the Pokémon learns or forgets the move.
|
||||
|
|
|
@ -32,7 +32,7 @@ void SetDaycareCompatibilityString(void);
|
|||
bool8 NameHasGenderSymbol(const u8 *name, u8 genderRatio);
|
||||
void ShowDaycareLevelMenu(void);
|
||||
void ChooseSendDaycareMon(void);
|
||||
u8 GetEggMovesSpecies(u16 species, u16 *eggMoves);
|
||||
u8 GetEggMovesBySpecies(u16 species, u16 *eggMoves);
|
||||
bool8 SpeciesCanLearnEggMove(u16 species, u16 move);
|
||||
|
||||
#endif // GUARD_DAYCARE_H
|
||||
|
|
|
@ -560,20 +560,33 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
|
|||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
|
||||
critChanceIndex = CalcCritChanceStageArgs(battlerAtk, battlerDef, move, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk]);
|
||||
if (critChanceIndex > 1) // Consider crit damage only if a move has at least +1 crit chance
|
||||
if (critChanceIndex > 1) // Consider crit damage only if a move has at least +2 crit chance
|
||||
{
|
||||
s32 critDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
|
||||
effectivenessMultiplier, weather, TRUE,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
u32 critChance = GetCritHitChance(critChanceIndex);
|
||||
// With critChance getting closer to 1, dmg gets closer to critDmg.
|
||||
u32 critOdds = GetCritHitOdds(critChanceIndex);
|
||||
// With critOdds getting closer to 1, dmg gets closer to critDmg.
|
||||
if (dmgRoll == DMG_ROLL_DEFAULT)
|
||||
dmg = DmgRoll((critDmg + normalDmg * (critChance - 1)) / (critChance));
|
||||
dmg = DmgRoll((critDmg + normalDmg * (critOdds - 1)) / (critOdds));
|
||||
else if (dmgRoll == DMG_ROLL_HIGHEST)
|
||||
dmg = HighestRollDmg((critDmg + normalDmg * (critChance - 1)) / (critChance));
|
||||
dmg = HighestRollDmg((critDmg + normalDmg * (critOdds - 1)) / (critOdds));
|
||||
else
|
||||
dmg = LowestRollDmg((critDmg + normalDmg * (critChance - 1)) / (critChance)); // Default to lowest roll
|
||||
dmg = LowestRollDmg((critDmg + normalDmg * (critOdds - 1)) / (critOdds)); // Default to lowest roll
|
||||
}
|
||||
else if (critChanceIndex == -2) // Guaranteed critical
|
||||
{
|
||||
s32 critDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
|
||||
effectivenessMultiplier, weather, TRUE,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
if (dmgRoll == DMG_ROLL_DEFAULT)
|
||||
dmg = DmgRoll(critDmg);
|
||||
else if (dmgRoll == DMG_ROLL_HIGHEST)
|
||||
dmg = HighestRollDmg(critDmg);
|
||||
else
|
||||
dmg = LowestRollDmg(critDmg); // Default to lowest roll
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -435,7 +435,7 @@ static void PlayerPartnerHandleIntroTrainerBallThrow(u32 battler)
|
|||
const u32 *trainerPal;
|
||||
|
||||
if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE))
|
||||
trainerPal = gTrainerBacksprites[gPartnerSpriteId].palette.data;
|
||||
trainerPal = gTrainerBacksprites[gBattlePartners[gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerPic].palette.data;
|
||||
else if (IsAiVsAiBattle())
|
||||
trainerPal = gTrainerSprites[GetTrainerPicFromId(gPartnerTrainerId)].palette.data;
|
||||
else
|
||||
|
|
|
@ -221,7 +221,6 @@ EWRAM_DATA u16 gMoveToLearn = 0;
|
|||
EWRAM_DATA u32 gFieldStatuses = 0;
|
||||
EWRAM_DATA struct FieldTimer gFieldTimers = {0};
|
||||
EWRAM_DATA u8 gBattlerAbility = 0;
|
||||
EWRAM_DATA u16 gPartnerSpriteId = 0;
|
||||
EWRAM_DATA struct QueuedStatBoost gQueuedStatBoosts[MAX_BATTLERS_COUNT] = {0};
|
||||
EWRAM_DATA bool8 gHasFetchedBall = FALSE;
|
||||
EWRAM_DATA u8 gLastUsedBall = 0;
|
||||
|
|
|
@ -1857,11 +1857,11 @@ static void Cmd_ppreduce(void)
|
|||
|
||||
// The chance is 1/N for each stage.
|
||||
#if B_CRIT_CHANCE >= GEN_7
|
||||
static const u8 sCriticalHitChance[] = {24, 8, 2, 1, 1};
|
||||
static const u8 sCriticalHitOdds[] = {24, 8, 2, 1, 1};
|
||||
#elif B_CRIT_CHANCE == GEN_6
|
||||
static const u8 sCriticalHitChance[] = {16, 8, 2, 1, 1};
|
||||
static const u8 sCriticalHitOdds[] = {16, 8, 2, 1, 1};
|
||||
#else
|
||||
static const u8 sCriticalHitChance[] = {16, 8, 4, 3, 2}; // Gens 2,3,4,5
|
||||
static const u8 sCriticalHitOdds[] = {16, 8, 4, 3, 2}; // Gens 2,3,4,5
|
||||
#endif // B_CRIT_CHANCE
|
||||
|
||||
#define BENEFITS_FROM_LEEK(battler, holdEffect)((holdEffect == HOLD_EFFECT_LEEK) && (GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_FARFETCHD || gBattleMons[battler].species == SPECIES_SIRFETCHD))
|
||||
|
@ -1869,8 +1869,7 @@ s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 rec
|
|||
{
|
||||
s32 critChance = 0;
|
||||
|
||||
if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT
|
||||
|| abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR)
|
||||
if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT)
|
||||
{
|
||||
critChance = -1;
|
||||
}
|
||||
|
@ -1892,12 +1891,21 @@ s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 rec
|
|||
+ (abilityAtk == ABILITY_SUPER_LUCK)
|
||||
+ gBattleStruct->bonusCritStages[gBattlerAttacker];
|
||||
|
||||
// Record ability only if move had at least +3 chance to get a crit
|
||||
if (critChance >= 3 && recordAbility && (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR))
|
||||
RecordAbilityBattle(battlerDef, abilityDef);
|
||||
if (critChance >= ARRAY_COUNT(sCriticalHitOdds))
|
||||
critChance = ARRAY_COUNT(sCriticalHitOdds) - 1;
|
||||
}
|
||||
|
||||
if (critChance >= ARRAY_COUNT(sCriticalHitChance))
|
||||
critChance = ARRAY_COUNT(sCriticalHitChance) - 1;
|
||||
if (critChance != -1 && (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR))
|
||||
{
|
||||
// Record ability only if move had 100% chance to get a crit
|
||||
if (recordAbility)
|
||||
{
|
||||
if (critChance == -2)
|
||||
RecordAbilityBattle(battlerDef, abilityDef);
|
||||
else if (sCriticalHitOdds[critChance] == 1)
|
||||
RecordAbilityBattle(battlerDef, abilityDef);
|
||||
}
|
||||
critChance = -1;
|
||||
}
|
||||
|
||||
return critChance;
|
||||
|
@ -1912,12 +1920,12 @@ s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordA
|
|||
}
|
||||
#undef BENEFITS_FROM_LEEK
|
||||
|
||||
s32 GetCritHitChance(s32 critChanceIndex)
|
||||
s32 GetCritHitOdds(s32 critChanceIndex)
|
||||
{
|
||||
if (critChanceIndex < 0)
|
||||
return -1;
|
||||
else
|
||||
return sCriticalHitChance[critChanceIndex];
|
||||
return sCriticalHitOdds[critChanceIndex];
|
||||
}
|
||||
|
||||
static void Cmd_critcalc(void)
|
||||
|
@ -1935,7 +1943,7 @@ static void Cmd_critcalc(void)
|
|||
else if (critChance == -2)
|
||||
gIsCriticalHit = TRUE;
|
||||
else
|
||||
gIsCriticalHit = RandomWeighted(RNG_CRITICAL_HIT, sCriticalHitChance[critChance] - 1, 1);
|
||||
gIsCriticalHit = RandomWeighted(RNG_CRITICAL_HIT, sCriticalHitOdds[critChance] - 1, 1);
|
||||
|
||||
// Counter for EVO_CRITICAL_HITS.
|
||||
partySlot = gBattlerPartyIndexes[gBattlerAttacker];
|
||||
|
|
|
@ -2125,7 +2125,6 @@ void DoSpecialTrainerBattle(void)
|
|||
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER;
|
||||
}
|
||||
|
||||
gPartnerSpriteId = VarGet(gSpecialVar_0x8007);
|
||||
gPartnerTrainerId = VarGet(gSpecialVar_0x8006) + TRAINER_PARTNER(PARTNER_NONE);
|
||||
FillPartnerParty(gPartnerTrainerId);
|
||||
CreateTask(Task_StartBattleAfterTransition, 1);
|
||||
|
|
|
@ -3571,7 +3571,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
|
|||
case CANCELLER_MULTIHIT_MOVES:
|
||||
if (gMovesInfo[gCurrentMove].effect == EFFECT_MULTI_HIT)
|
||||
{
|
||||
u16 ability = gBattleMons[gBattlerAttacker].ability;
|
||||
u32 ability = GetBattlerAbility(gBattlerAttacker);
|
||||
|
||||
if (ability == ABILITY_SKILL_LINK)
|
||||
{
|
||||
|
@ -3993,14 +3993,12 @@ static inline uq4_12_t GetSupremeOverlordModifier(u32 battler)
|
|||
return UQ_4_12(1.0) + (UQ_4_12(0.1) * gBattleStruct->supremeOverlordCounter[battler]);
|
||||
}
|
||||
|
||||
static inline bool32 HadMoreThanHalfHpNowHasLess(u32 battler)
|
||||
static inline bool32 HadMoreThanHalfHpNowDoesnt(u32 battler)
|
||||
{
|
||||
u32 cutoff = gBattleMons[battler].maxHP / 2;
|
||||
if (gBattleMons[battler].maxHP % 2 == 1)
|
||||
cutoff++;
|
||||
// Had more than half of hp before, now has less
|
||||
return (gBattleStruct->hpBefore[battler] >= cutoff
|
||||
&& gBattleMons[battler].hp < cutoff);
|
||||
return (gBattleStruct->hpBefore[battler] > cutoff
|
||||
&& gBattleMons[battler].hp <= cutoff);
|
||||
}
|
||||
|
||||
u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg)
|
||||
|
@ -5288,7 +5286,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
|||
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||||
&& TARGET_TURN_DAMAGED
|
||||
&& IsBattlerAlive(battler)
|
||||
&& HadMoreThanHalfHpNowHasLess(battler)
|
||||
&& HadMoreThanHalfHpNowDoesnt(battler)
|
||||
&& (gMultiHitCounter == 0 || gMultiHitCounter == 1)
|
||||
&& !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove))
|
||||
&& CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))
|
||||
|
@ -5306,7 +5304,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
|||
&& TARGET_TURN_DAMAGED
|
||||
&& IsBattlerAlive(battler)
|
||||
// Had more than half of hp before, now has less
|
||||
&& HadMoreThanHalfHpNowHasLess(battler)
|
||||
&& HadMoreThanHalfHpNowDoesnt(battler)
|
||||
&& (gMultiHitCounter == 0 || gMultiHitCounter == 1)
|
||||
&& !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove))
|
||||
&& (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
|
||||
|
@ -5747,7 +5745,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
|||
&& TARGET_TURN_DAMAGED
|
||||
&& (gMultiHitCounter == 0 || gMultiHitCounter == 1) // Activates after all hits from a multi-hit move.
|
||||
&& IsBattlerAlive(gBattlerTarget)
|
||||
&& HadMoreThanHalfHpNowHasLess(gBattlerTarget)
|
||||
&& HadMoreThanHalfHpNowDoesnt(gBattlerTarget)
|
||||
&& !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)))
|
||||
{
|
||||
gBattlerAttacker = gBattlerTarget;
|
||||
|
@ -6235,26 +6233,16 @@ bool32 IsNeutralizingGasOnField(void)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 IsMyceliumMightOnField(void)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (IsBattlerAlive(i) && gBattleMons[i].ability == ABILITY_MYCELIUM_MIGHT && IS_MOVE_STATUS(gCurrentMove))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 IsMoldBreakerTypeAbility(u32 ability)
|
||||
{
|
||||
return (ability == ABILITY_MOLD_BREAKER || ability == ABILITY_TERAVOLT || ability == ABILITY_TURBOBLAZE);
|
||||
return (ability == ABILITY_MOLD_BREAKER || ability == ABILITY_TERAVOLT || ability == ABILITY_TURBOBLAZE
|
||||
|| (ability == ABILITY_MYCELIUM_MIGHT && IS_MOVE_STATUS(gCurrentMove)));
|
||||
}
|
||||
|
||||
u32 GetBattlerAbility(u32 battler)
|
||||
{
|
||||
bool32 noAbilityShield = GetBattlerHoldEffectIgnoreAbility(battler, TRUE) != HOLD_EFFECT_ABILITY_SHIELD;
|
||||
|
||||
if (gAbilitiesInfo[gBattleMons[battler].ability].cantBeSuppressed)
|
||||
return gBattleMons[battler].ability;
|
||||
|
||||
|
@ -6263,16 +6251,14 @@ u32 GetBattlerAbility(u32 battler)
|
|||
|
||||
if (IsNeutralizingGasOnField()
|
||||
&& gBattleMons[battler].ability != ABILITY_NEUTRALIZING_GAS
|
||||
&& GetBattlerHoldEffectIgnoreAbility(battler, TRUE) != HOLD_EFFECT_ABILITY_SHIELD)
|
||||
return ABILITY_NONE;
|
||||
|
||||
if (IsMyceliumMightOnField())
|
||||
&& noAbilityShield)
|
||||
return ABILITY_NONE;
|
||||
|
||||
if (((IsMoldBreakerTypeAbility(gBattleMons[gBattlerAttacker].ability)
|
||||
&& !(gStatuses3[gBattlerAttacker] & STATUS3_GASTRO_ACID))
|
||||
|| gMovesInfo[gCurrentMove].ignoresTargetAbility)
|
||||
&& gAbilitiesInfo[gBattleMons[battler].ability].breakable
|
||||
&& noAbilityShield
|
||||
&& gBattlerByTurnOrder[gCurrentTurnActionNumber] == gBattlerAttacker
|
||||
&& gActionsByTurnOrder[gBattlerByTurnOrder[gBattlerAttacker]] == B_ACTION_USE_MOVE
|
||||
&& gCurrentTurnActionNumber < gBattlersCount)
|
||||
|
@ -10160,8 +10146,6 @@ static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 move
|
|||
mod = UQ_4_12(2.0);
|
||||
if (moveType == TYPE_GROUND && defType == TYPE_FLYING && IsBattlerGrounded(battlerDef) && mod == UQ_4_12(0.0))
|
||||
mod = UQ_4_12(1.0);
|
||||
if (moveType == TYPE_FIRE && gDisableStructs[battlerDef].tarShot)
|
||||
mod = UQ_4_12(2.0);
|
||||
if (moveType == TYPE_STELLAR && IsTerastallized(battlerDef))
|
||||
mod = UQ_4_12(2.0);
|
||||
|
||||
|
@ -10227,6 +10211,8 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov
|
|||
if (GetBattlerType(battlerDef, 2, FALSE) != TYPE_MYSTERY && GetBattlerType(battlerDef, 2, FALSE) != GetBattlerType(battlerDef, 1, FALSE)
|
||||
&& GetBattlerType(battlerDef, 2, FALSE) != GetBattlerType(battlerDef, 0, FALSE))
|
||||
MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 2, FALSE), battlerAtk, recordAbilities);
|
||||
if (moveType == TYPE_FIRE && gDisableStructs[battlerDef].tarShot)
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(2.0));
|
||||
|
||||
if (recordAbilities && (illusionSpecies = GetIllusionMonSpecies(battlerDef)))
|
||||
TryNoticeIllusionInTypeEffectiveness(move, moveType, battlerAtk, battlerDef, modifier, illusionSpecies);
|
||||
|
@ -11359,7 +11345,7 @@ void RemoveConfusionStatus(u32 battler)
|
|||
|
||||
static bool32 CanBeInfinitelyConfused(u32 battler)
|
||||
{
|
||||
if (gBattleMons[battler].ability == ABILITY_OWN_TEMPO
|
||||
if (GetBattlerAbility(battler) == ABILITY_OWN_TEMPO
|
||||
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)
|
||||
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD)
|
||||
{
|
||||
|
|
|
@ -642,7 +642,7 @@ const struct Item gItemsInfo[] =
|
|||
[ITEM_HYPER_POTION] =
|
||||
{
|
||||
.name = _("Hyper Potion"),
|
||||
.price = (I_PRICE >= GEN_2 || I_PRICE <= GEN_6) ? 1200 : 1500,
|
||||
.price = (I_PRICE >= GEN_2 && I_PRICE <= GEN_6) ? 1200 : 1500,
|
||||
.holdEffectParam = 120,
|
||||
.description = COMPOUND_STRING(
|
||||
"Restores the HP of\n"
|
||||
|
|
|
@ -624,7 +624,7 @@ static const struct FormChange sGiratinaFormChangeTable[] = {
|
|||
|
||||
#if P_FAMILY_SHAYMIN
|
||||
static const struct FormChange sShayminFormChangeTable[] = {
|
||||
{FORM_CHANGE_ITEM_USE, SPECIES_SHAYMIN_SKY, ITEM_GRACIDEA, DAY},
|
||||
{FORM_CHANGE_ITEM_USE, SPECIES_SHAYMIN_SKY, ITEM_GRACIDEA, DAY, STATUS1_FREEZE | STATUS1_FROSTBITE},
|
||||
{FORM_CHANGE_WITHDRAW, SPECIES_SHAYMIN_LAND},
|
||||
{FORM_CHANGE_TIME_OF_DAY, SPECIES_SHAYMIN_LAND, NIGHT},
|
||||
{FORM_CHANGE_STATUS, SPECIES_SHAYMIN_LAND, STATUS1_FREEZE | STATUS1_FROSTBITE},
|
||||
|
|
|
@ -34,6 +34,7 @@ static void SetInitialEggData(struct Pokemon *mon, u16 species, struct DayCare *
|
|||
static void DaycarePrintMonInfo(u8 windowId, u32 daycareSlotId, u8 y);
|
||||
static u8 ModifyBreedingScoreForOvalCharm(u8 score);
|
||||
static u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves);
|
||||
static u16 GetEggSpecies(u16 species);
|
||||
|
||||
// RAM buffers used to assist with BuildEggMoveset()
|
||||
EWRAM_DATA static u16 sHatchedEggLevelUpMoves[EGG_LVL_UP_MOVES_ARRAY_COUNT] = {0};
|
||||
|
@ -84,6 +85,24 @@ static const struct ListMenuTemplate sDaycareListMenuLevelTemplate =
|
|||
.cursorKind = CURSOR_BLACK_ARROW
|
||||
};
|
||||
|
||||
static const struct {
|
||||
u16 currSpecies;
|
||||
u16 item;
|
||||
u16 babySpecies;
|
||||
} sIncenseBabyTable[] =
|
||||
{
|
||||
// Regular offspring, Item, Incense Offspring
|
||||
{ SPECIES_WOBBUFFET, ITEM_LAX_INCENSE, SPECIES_WYNAUT },
|
||||
{ SPECIES_MARILL, ITEM_SEA_INCENSE, SPECIES_AZURILL },
|
||||
{ SPECIES_SNORLAX, ITEM_FULL_INCENSE, SPECIES_MUNCHLAX },
|
||||
{ SPECIES_CHANSEY, ITEM_LUCK_INCENSE, SPECIES_HAPPINY },
|
||||
{ SPECIES_MR_MIME, ITEM_ODD_INCENSE, SPECIES_MIME_JR },
|
||||
{ SPECIES_CHIMECHO, ITEM_PURE_INCENSE, SPECIES_CHINGLING },
|
||||
{ SPECIES_SUDOWOODO, ITEM_ROCK_INCENSE, SPECIES_BONSLY },
|
||||
{ SPECIES_ROSELIA, ITEM_ROSE_INCENSE, SPECIES_BUDEW },
|
||||
{ SPECIES_MANTINE, ITEM_WAVE_INCENSE, SPECIES_MANTYKE },
|
||||
};
|
||||
|
||||
static const u8 *const sCompatibilityMessages[] =
|
||||
{
|
||||
gDaycareText_GetAlongVeryWell,
|
||||
|
@ -172,26 +191,42 @@ static void TransferEggMoves(void)
|
|||
{
|
||||
u32 i, j, k, l;
|
||||
u16 numEggMoves;
|
||||
struct Pokemon mon;
|
||||
|
||||
for (i = 0; i < DAYCARE_MON_COUNT; i++)
|
||||
{
|
||||
u16 moveLearnerSpecies = GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_SPECIES);
|
||||
u16 eggSpecies = GetEggSpecies(moveLearnerSpecies);
|
||||
|
||||
if (!GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_SANITY_HAS_SPECIES))
|
||||
continue;
|
||||
|
||||
BoxMonToMon(&gSaveBlock1Ptr->daycare.mons[i].mon, &mon);
|
||||
// Prevent non-baby species from learning incense baby egg moves
|
||||
if (P_INCENSE_BREEDING < GEN_9 && eggSpecies != moveLearnerSpecies)
|
||||
{
|
||||
for (j = 0; j < ARRAY_COUNT(sIncenseBabyTable); j++)
|
||||
{
|
||||
if (sIncenseBabyTable[j].babySpecies == eggSpecies)
|
||||
{
|
||||
eggSpecies = sIncenseBabyTable[j].currSpecies;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearHatchedEggMoves();
|
||||
numEggMoves = GetEggMoves(&mon, sHatchedEggEggMoves);
|
||||
numEggMoves = GetEggMovesBySpecies(eggSpecies, sHatchedEggEggMoves);
|
||||
for (j = 0; j < numEggMoves; j++)
|
||||
{
|
||||
// Go through other Daycare mons
|
||||
for (k = 0; k < DAYCARE_MON_COUNT; k++)
|
||||
{
|
||||
u16 moveTeacherSpecies = GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[k].mon, MON_DATA_SPECIES);
|
||||
|
||||
if (k == i || !GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[k].mon, MON_DATA_SANITY_HAS_SPECIES))
|
||||
continue;
|
||||
|
||||
// Check if you can inherit from them
|
||||
if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[k].mon, MON_DATA_SPECIES) != GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_SPECIES)
|
||||
if (GET_BASE_SPECIES_ID(moveTeacherSpecies) != GET_BASE_SPECIES_ID(moveLearnerSpecies)
|
||||
&& (P_EGG_MOVE_TRANSFER < GEN_9 || GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[i].mon, MON_DATA_HELD_ITEM) != ITEM_MIRROR_HERB)
|
||||
)
|
||||
continue;
|
||||
|
@ -757,7 +792,7 @@ static u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves)
|
|||
return numEggMoves;
|
||||
}
|
||||
|
||||
u8 GetEggMovesSpecies(u16 species, u16 *eggMoves)
|
||||
u8 GetEggMovesBySpecies(u16 species, u16 *eggMoves)
|
||||
{
|
||||
u16 numEggMoves;
|
||||
const u16 *eggMoveLearnset;
|
||||
|
@ -916,26 +951,6 @@ void RejectEggFromDayCare(void)
|
|||
RemoveEggFromDayCare(&gSaveBlock1Ptr->daycare);
|
||||
}
|
||||
|
||||
|
||||
static const struct {
|
||||
u16 currSpecies;
|
||||
u16 item;
|
||||
u16 babySpecies;
|
||||
} sIncenseBabyTable[] =
|
||||
{
|
||||
// Regular offspring, Item, Incense Offspring
|
||||
{ SPECIES_WOBBUFFET, ITEM_LAX_INCENSE, SPECIES_WYNAUT },
|
||||
{ SPECIES_MARILL, ITEM_SEA_INCENSE, SPECIES_AZURILL },
|
||||
{ SPECIES_SNORLAX, ITEM_FULL_INCENSE, SPECIES_MUNCHLAX },
|
||||
{ SPECIES_CHANSEY, ITEM_LUCK_INCENSE, SPECIES_HAPPINY },
|
||||
{ SPECIES_MR_MIME, ITEM_ODD_INCENSE, SPECIES_MIME_JR },
|
||||
{ SPECIES_CHIMECHO, ITEM_PURE_INCENSE, SPECIES_CHINGLING },
|
||||
{ SPECIES_SUDOWOODO, ITEM_ROCK_INCENSE, SPECIES_BONSLY },
|
||||
{ SPECIES_ROSELIA, ITEM_ROSE_INCENSE, SPECIES_BUDEW },
|
||||
{ SPECIES_MANTINE, ITEM_WAVE_INCENSE, SPECIES_MANTYKE },
|
||||
};
|
||||
|
||||
#if P_INCENSE_BREEDING < GEN_9
|
||||
static void AlterEggSpeciesWithIncenseItem(u16 *species, struct DayCare *daycare)
|
||||
{
|
||||
u32 i;
|
||||
|
@ -952,7 +967,6 @@ static void AlterEggSpeciesWithIncenseItem(u16 *species, struct DayCare *daycare
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct {
|
||||
u16 offspring;
|
||||
|
@ -1057,9 +1071,8 @@ static void _GiveEggFromDaycare(struct DayCare *daycare)
|
|||
bool8 isEgg;
|
||||
|
||||
species = DetermineEggSpeciesAndParentSlots(daycare, parentSlots);
|
||||
#if P_INCENSE_BREEDING < GEN_9
|
||||
AlterEggSpeciesWithIncenseItem(&species, daycare);
|
||||
#endif
|
||||
if (P_INCENSE_BREEDING < GEN_9)
|
||||
AlterEggSpeciesWithIncenseItem(&species, daycare);
|
||||
SetInitialEggData(&egg, species, daycare);
|
||||
InheritIVs(&egg, daycare);
|
||||
InheritPokeball(&egg, &daycare->mons[parentSlots[1]].mon, &daycare->mons[parentSlots[0]].mon);
|
||||
|
|
|
@ -5077,7 +5077,7 @@ static bool8 CalculateMoves(void)
|
|||
species = GetFormSpeciesId(species, 0);
|
||||
|
||||
//Calculate amount of Egg and LevelUp moves
|
||||
numEggMoves = GetEggMovesSpecies(species, statsMovesEgg);
|
||||
numEggMoves = GetEggMovesBySpecies(species, statsMovesEgg);
|
||||
numLevelUpMoves = GetLevelUpMovesBySpecies(species, statsMovesLevelUp);
|
||||
|
||||
//Egg moves
|
||||
|
|
|
@ -6471,20 +6471,24 @@ u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32
|
|||
case FORM_CHANGE_ITEM_USE:
|
||||
if (arg == formChanges[i].param1)
|
||||
{
|
||||
bool32 pass = TRUE;
|
||||
switch (formChanges[i].param2)
|
||||
{
|
||||
case DAY:
|
||||
if (GetTimeOfDay() != TIME_NIGHT)
|
||||
targetSpecies = formChanges[i].targetSpecies;
|
||||
if (GetTimeOfDay() == TIME_NIGHT)
|
||||
pass = FALSE;
|
||||
break;
|
||||
case NIGHT:
|
||||
if (GetTimeOfDay() == TIME_NIGHT)
|
||||
targetSpecies = formChanges[i].targetSpecies;
|
||||
break;
|
||||
default:
|
||||
targetSpecies = formChanges[i].targetSpecies;
|
||||
if (GetTimeOfDay() != TIME_NIGHT)
|
||||
pass = FALSE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (formChanges[i].param3 != STATUS1_NONE && GetBoxMonData(boxMon, MON_DATA_STATUS, NULL) & formChanges[i].param3)
|
||||
pass = FALSE;
|
||||
|
||||
if (pass)
|
||||
targetSpecies = formChanges[i].targetSpecies;
|
||||
}
|
||||
break;
|
||||
case FORM_CHANGE_ITEM_USE_MULTICHOICE:
|
||||
|
|
|
@ -6,6 +6,7 @@ SINGLE_BATTLE_TEST("Anger Shell activates only if the target had more than 50% o
|
|||
bool32 activates = FALSE;
|
||||
u16 maxHp = 500, hp = 0;
|
||||
|
||||
PARAMETRIZE { hp = 250; activates = FALSE; }
|
||||
PARAMETRIZE { hp = 249; activates = FALSE; }
|
||||
PARAMETRIZE { hp = 100; activates = FALSE; }
|
||||
PARAMETRIZE { hp = 50; activates = FALSE; }
|
||||
|
@ -41,7 +42,7 @@ SINGLE_BATTLE_TEST("Anger Shell lowers Def/Sp.Def by 1 and raises Atk/Sp.Atk/Spd
|
|||
u16 maxHp = 500;
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].power != 0);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); }
|
||||
PLAYER(SPECIES_KLAWF) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
|
@ -49,15 +50,15 @@ SINGLE_BATTLE_TEST("Anger Shell lowers Def/Sp.Def by 1 and raises Atk/Sp.Atk/Spd
|
|||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
ABILITY_POPUP(player, ABILITY_ANGER_SHELL);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Wobbuffet's Defense fell!");
|
||||
MESSAGE("Klawf's Defense fell!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Wobbuffet's Sp. Def fell!");
|
||||
MESSAGE("Klawf's Sp. Def fell!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Wobbuffet's Attack rose!");
|
||||
MESSAGE("Klawf's Attack rose!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Wobbuffet's Sp. Atk rose!");
|
||||
MESSAGE("Klawf's Sp. Atk rose!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Wobbuffet's Speed rose!");
|
||||
MESSAGE("Klawf's Speed rose!");
|
||||
} THEN {
|
||||
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1);
|
||||
EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE - 1);
|
||||
|
@ -73,13 +74,12 @@ SINGLE_BATTLE_TEST("Anger Shell activates after all hits from a multi-hit move")
|
|||
u16 maxHp = 500;
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_DOUBLE_SLAP].effect == EFFECT_MULTI_HIT);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); }
|
||||
PLAYER(SPECIES_KLAWF) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); }
|
||||
OPPONENT(SPECIES_SHELLDER) { Ability(ABILITY_SKILL_LINK); } // Always hits 5 times.
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_DOUBLE_SLAP); }
|
||||
} SCENE {
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
for (j = 0; j < 4; j++) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent);
|
||||
NOT ABILITY_POPUP(player, ABILITY_ANGER_SHELL);
|
||||
}
|
||||
|
|
75
test/battle/ability/berserk.c
Normal file
75
test/battle/ability/berserk.c
Normal file
|
@ -0,0 +1,75 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Berserk activates only if the target had more than 50% of its hp")
|
||||
{
|
||||
bool32 activates = FALSE;
|
||||
u16 maxHp = 500, hp = 0;
|
||||
|
||||
PARAMETRIZE { hp = 250; activates = FALSE; }
|
||||
PARAMETRIZE { hp = 249; activates = FALSE; }
|
||||
PARAMETRIZE { hp = 100; activates = FALSE; }
|
||||
PARAMETRIZE { hp = 50; activates = FALSE; }
|
||||
PARAMETRIZE { hp = 251; activates = TRUE; }
|
||||
PARAMETRIZE { hp = 254; activates = TRUE; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].power != 0);
|
||||
PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); MaxHP(maxHp); HP(hp); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
if (activates) {
|
||||
ABILITY_POPUP(player, ABILITY_BERSERK);
|
||||
} else {
|
||||
NOT ABILITY_POPUP(player, ABILITY_BERSERK);
|
||||
}
|
||||
} THEN {
|
||||
if (activates) {
|
||||
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Berserk raises Sp.Atk by 1")
|
||||
{
|
||||
u16 maxHp = 500;
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].power != 0);
|
||||
PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); MaxHP(maxHp); HP(maxHp / 2 + 1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
ABILITY_POPUP(player, ABILITY_BERSERK);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Drampa's Sp. Atk rose!");
|
||||
} THEN {
|
||||
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Berserk activates after all hits from a multi-hit move")
|
||||
{
|
||||
u32 j;
|
||||
u16 maxHp = 500;
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_DOUBLE_SLAP].effect == EFFECT_MULTI_HIT);
|
||||
PLAYER(SPECIES_DRAMPA) { Ability(ABILITY_BERSERK); MaxHP(maxHp); HP(maxHp / 2 + 1); }
|
||||
OPPONENT(SPECIES_SHELLDER) { Ability(ABILITY_SKILL_LINK); } // Always hits 5 times.
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_DOUBLE_SLAP); }
|
||||
} SCENE {
|
||||
for (j = 0; j < 4; j++) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent);
|
||||
NOT ABILITY_POPUP(player, ABILITY_BERSERK);
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent);
|
||||
ABILITY_POPUP(player, ABILITY_BERSERK);
|
||||
} THEN {
|
||||
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
|
@ -17,7 +17,9 @@ SINGLE_BATTLE_TEST("Clear Body prevents intimidate")
|
|||
} SCENE {
|
||||
HP_BAR(player, captureDamage: &turnOneHit);
|
||||
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
|
||||
NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); }
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
}
|
||||
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
|
||||
MESSAGE("Foe Beldum's Clear Body prevents stat loss!");
|
||||
HP_BAR(player, captureDamage: &turnTwoHit);
|
||||
|
@ -26,13 +28,251 @@ SINGLE_BATTLE_TEST("Clear Body prevents intimidate")
|
|||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Clear Body prevents stat stage reduction from moves"); // Growl, Leer, Confide, Fake Tears, Scary Face, Sweet Scent, Sand Attack (Attack, Defense, Sp. Attack, Sp. Defense, Speed, Evasion, Accuracy
|
||||
TO_DO_BATTLE_TEST("Clear Body prevents Sticky Web");
|
||||
TO_DO_BATTLE_TEST("Clear Body doesn't prevent stat stage reduction from moves used by the user"); // e.g. Superpower
|
||||
TO_DO_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from Iron Ball");
|
||||
TO_DO_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from paralysis");
|
||||
TO_DO_BATTLE_TEST("Clear Body doesn't prevent Attack reduction from burn");
|
||||
TO_DO_BATTLE_TEST("Clear Body doesn't prevent receiving negative stat changes from Baton Pass");
|
||||
TO_DO_BATTLE_TEST("Clear Body doesn't prevent Topsy-Turvy");
|
||||
TO_DO_BATTLE_TEST("Clear Body doesn't prevent Spectral Thief from resetting positive stat changes");
|
||||
TO_DO_BATTLE_TEST("Clear Body is ignored by Mold Breaker");
|
||||
SINGLE_BATTLE_TEST("Clear Body prevents stat stage reduction from moves")
|
||||
{
|
||||
u16 move;
|
||||
PARAMETRIZE{ move = MOVE_GROWL; }
|
||||
PARAMETRIZE{ move = MOVE_LEER; }
|
||||
PARAMETRIZE{ move = MOVE_CONFIDE; }
|
||||
PARAMETRIZE{ move = MOVE_FAKE_TEARS; }
|
||||
PARAMETRIZE{ move = MOVE_SCARY_FACE; }
|
||||
PARAMETRIZE{ move = MOVE_SWEET_SCENT; }
|
||||
PARAMETRIZE{ move = MOVE_SAND_ATTACK; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_GROWL].effect == EFFECT_ATTACK_DOWN);
|
||||
ASSUME(gMovesInfo[MOVE_LEER].effect == EFFECT_DEFENSE_DOWN);
|
||||
ASSUME(gMovesInfo[MOVE_CONFIDE].effect == EFFECT_SPECIAL_ATTACK_DOWN);
|
||||
ASSUME(gMovesInfo[MOVE_FAKE_TEARS].effect == EFFECT_SPECIAL_DEFENSE_DOWN_2);
|
||||
ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2);
|
||||
ASSUME(gMovesInfo[MOVE_SWEET_SCENT].effect == (B_UPDATED_MOVE_DATA >= GEN_6 ? EFFECT_EVASION_DOWN_2 : EFFECT_EVASION_DOWN));
|
||||
ASSUME(gMovesInfo[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN);
|
||||
PLAYER(SPECIES_WOBBUFFET)
|
||||
OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); }
|
||||
} SCENE {
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
}
|
||||
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
|
||||
MESSAGE("Foe Beldum's Clear Body prevents stat loss!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Clear Body prevents Sticky Web")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_STICKY_WEB].effect == EFFECT_STICKY_WEB);
|
||||
PLAYER(SPECIES_WOBBUFFET)
|
||||
OPPONENT(SPECIES_WOBBUFFET)
|
||||
OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_STICKY_WEB); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
} SCENE {
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
}
|
||||
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
|
||||
MESSAGE("Foe Beldum's Clear Body prevents stat loss!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Clear Body doesn't prevent stat stage reduction from moves used by the user")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(MoveHasAdditionalEffectSelf(MOVE_SUPERPOWER, MOVE_EFFECT_ATK_DEF_DOWN) == TRUE);
|
||||
PLAYER(SPECIES_WOBBUFFET)
|
||||
OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SUPERPOWER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPERPOWER, opponent);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
|
||||
MESSAGE("Foe Beldum's Clear Body prevents stat loss!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Mold Breaker, Teravolt, and Turboblaze ignore Clear Body")
|
||||
{
|
||||
u32 j, k;
|
||||
u16 ability = ABILITY_NONE;
|
||||
u16 move = ABILITY_NONE;
|
||||
static const u16 breakerAbilities[] = {
|
||||
ABILITY_MOLD_BREAKER,
|
||||
ABILITY_TERAVOLT,
|
||||
ABILITY_TURBOBLAZE,
|
||||
};
|
||||
static const u16 statReductionMoves[] = {
|
||||
MOVE_GROWL,
|
||||
MOVE_LEER,
|
||||
MOVE_CONFIDE,
|
||||
MOVE_FAKE_TEARS,
|
||||
MOVE_SCARY_FACE,
|
||||
MOVE_SWEET_SCENT,
|
||||
MOVE_SAND_ATTACK,
|
||||
};
|
||||
|
||||
for (j = 0; j < ARRAY_COUNT(statReductionMoves); j++)
|
||||
{
|
||||
for (k = 0; k < ARRAY_COUNT(breakerAbilities); k++)
|
||||
{
|
||||
PARAMETRIZE{ move = statReductionMoves[j]; ability = breakerAbilities[k]; }
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_GROWL].effect == EFFECT_ATTACK_DOWN);
|
||||
ASSUME(gMovesInfo[MOVE_LEER].effect == EFFECT_DEFENSE_DOWN);
|
||||
ASSUME(gMovesInfo[MOVE_CONFIDE].effect == EFFECT_SPECIAL_ATTACK_DOWN);
|
||||
ASSUME(gMovesInfo[MOVE_FAKE_TEARS].effect == EFFECT_SPECIAL_DEFENSE_DOWN_2);
|
||||
ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2);
|
||||
ASSUME(gMovesInfo[MOVE_SWEET_SCENT].effect == (B_UPDATED_MOVE_DATA >= GEN_6 ? EFFECT_EVASION_DOWN_2 : EFFECT_EVASION_DOWN));
|
||||
ASSUME(gMovesInfo[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
|
||||
OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, player);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
|
||||
MESSAGE("Foe Beldum's Clear Body prevents stat loss!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from Iron Ball")
|
||||
{
|
||||
u16 heldItem;
|
||||
PARAMETRIZE{ heldItem = ITEM_NONE; }
|
||||
PARAMETRIZE{ heldItem = ITEM_IRON_BALL; }
|
||||
GIVEN {
|
||||
ASSUME(gItemsInfo[ITEM_IRON_BALL].holdEffect == HOLD_EFFECT_IRON_BALL);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); Item(heldItem); }
|
||||
} WHEN {
|
||||
TURN { }
|
||||
} SCENE {
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
|
||||
if (heldItem == ITEM_IRON_BALL) {
|
||||
MESSAGE("Wobbuffet used Celebrate!");
|
||||
MESSAGE("Foe Beldum used Celebrate!");
|
||||
} else {
|
||||
MESSAGE("Foe Beldum used Celebrate!");
|
||||
MESSAGE("Wobbuffet used Celebrate!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from paralysis")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_THUNDER_WAVE); }
|
||||
TURN { MOVE(player, MOVE_THUNDER_WAVE); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Beldum used Celebrate!");
|
||||
MESSAGE("Wobbuffet used Thunder Wave!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_WAVE, player);
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
|
||||
MESSAGE("Wobbuffet used Thunder Wave!");
|
||||
ONE_OF {
|
||||
MESSAGE("Foe Beldum used Celebrate!");
|
||||
MESSAGE("Foe Beldum is paralyzed! It can't move!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Clear Body doesn't prevent Attack reduction from burn", s16 damage)
|
||||
{
|
||||
bool32 burned;
|
||||
PARAMETRIZE{ burned = FALSE; }
|
||||
PARAMETRIZE{ burned = TRUE; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
|
||||
PLAYER(SPECIES_WOBBUFFET)
|
||||
OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); if (burned) Status1(STATUS1_BURN); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Clear Body doesn't prevent receiving negative stat changes from Baton Pass")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2);
|
||||
ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(3); }
|
||||
OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SCARY_FACE); MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); }
|
||||
TURN { MOVE(player, MOVE_SCARY_FACE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Scary Face!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCARY_FACE, player);
|
||||
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
|
||||
MESSAGE("Foe Beldum used Celebrate!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Clear Body doesn't prevent Topsy-Turvy")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TOPSY_TURVY].effect == EFFECT_TOPSY_TURVY);
|
||||
ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2);
|
||||
ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(3); }
|
||||
OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SCARY_FACE); MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); }
|
||||
TURN { MOVE(player, MOVE_TOPSY_TURVY); }
|
||||
TURN { MOVE(player, MOVE_SCARY_FACE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Topsy-Turvy!");
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOPSY_TURVY, player);
|
||||
MESSAGE("Foe Beldum used Celebrate!");
|
||||
MESSAGE("Foe Beldum used Celebrate!");
|
||||
MESSAGE("Wobbuffet used Scary Face!");
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SCARY_FACE, player);
|
||||
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Clear Body doesn't prevent Spectral Thief from resetting positive stat changes")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_SPECTRAL_THIEF, MOVE_EFFECT_SPECTRAL_THIEF) == TRUE);
|
||||
ASSUME(gMovesInfo[MOVE_AGILITY].effect == EFFECT_SPEED_UP_2);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
OPPONENT(SPECIES_METANG) { Speed(5); Ability(ABILITY_CLEAR_BODY); }
|
||||
} WHEN {
|
||||
TURN{ MOVE(opponent, MOVE_AGILITY); }
|
||||
TURN{ MOVE(player, MOVE_SPECTRAL_THIEF); }
|
||||
TURN{ }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Metang used Agility!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_AGILITY, opponent);
|
||||
MESSAGE("Wobbuffet used Celebrate!");
|
||||
MESSAGE("Foe Metang used Celebrate!");
|
||||
MESSAGE("Wobbuffet used Spectral Thief!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPECTRAL_THIEF, player);
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
|
||||
MESSAGE("Wobbuffet used Celebrate!");
|
||||
MESSAGE("Foe Metang used Celebrate!");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -691,3 +691,26 @@ AI_DOUBLE_BATTLE_TEST("AI will the see a corresponding absorbing ability on part
|
|||
TURN { EXPECT_MOVE(opponentLeft, MOVE_TACKLE); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI calculates guaranteed criticals and detects critical immunity")
|
||||
{
|
||||
u32 ability;
|
||||
PARAMETRIZE { ability = ABILITY_SWIFT_SWIM; }
|
||||
PARAMETRIZE { ability = ABILITY_SHELL_ARMOR; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_STORM_THROW].alwaysCriticalHit);
|
||||
ASSUME(gMovesInfo[MOVE_STORM_THROW].power == 60);
|
||||
ASSUME(gMovesInfo[MOVE_BRICK_BREAK].power == 75);
|
||||
ASSUME(gMovesInfo[MOVE_STORM_THROW].type == gMovesInfo[MOVE_BRICK_BREAK].type);
|
||||
ASSUME(gMovesInfo[MOVE_STORM_THROW].category == gMovesInfo[MOVE_BRICK_BREAK].category);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_OMASTAR) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_STORM_THROW, MOVE_BRICK_BREAK); }
|
||||
} WHEN {
|
||||
if (ability == ABILITY_SHELL_ARMOR)
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_BRICK_BREAK); }
|
||||
else
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_STORM_THROW); }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,3 +32,75 @@ SINGLE_BATTLE_TEST("Ability Shield prevents Neutralizing Gas")
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ability Shield protects against Mold Breaker")
|
||||
{
|
||||
u32 item;
|
||||
|
||||
PARAMETRIZE { item = ITEM_ABILITY_SHIELD; }
|
||||
PARAMETRIZE { item = ITEM_NONE; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_SHEDINJA) { Ability(ABILITY_WONDER_GUARD); Item(item); }
|
||||
OPPONENT(SPECIES_TINKATON) { Ability(ABILITY_MOLD_BREAKER); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_GIGATON_HAMMER); }
|
||||
} SCENE {
|
||||
if (item == ITEM_ABILITY_SHIELD) {
|
||||
NONE_OF {
|
||||
MESSAGE("Shedinja fainted!");
|
||||
}
|
||||
} else {
|
||||
MESSAGE("Shedinja fainted!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ability Shield protects against Mycelium Might")
|
||||
{
|
||||
u32 item;
|
||||
|
||||
PARAMETRIZE { item = ITEM_ABILITY_SHIELD; }
|
||||
PARAMETRIZE { item = ITEM_NONE; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_VIGOROTH) { Ability(ABILITY_VITAL_SPIRIT); Item(item); }
|
||||
OPPONENT(SPECIES_TOEDSCOOL) { Ability(ABILITY_MYCELIUM_MIGHT); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SPORE); MOVE(player, MOVE_SPORE); }
|
||||
} SCENE {
|
||||
|
||||
if (item == ITEM_ABILITY_SHIELD) {
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, opponent);
|
||||
STATUS_ICON(player, sleep: TRUE);
|
||||
}
|
||||
} else {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, opponent);
|
||||
STATUS_ICON(player, sleep: TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ability Shield protects against Sunsteel Strike")
|
||||
{
|
||||
u32 item;
|
||||
|
||||
PARAMETRIZE { item = ITEM_ABILITY_SHIELD; }
|
||||
PARAMETRIZE { item = ITEM_NONE; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_SHEDINJA) { Ability(ABILITY_WONDER_GUARD); Item(item); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SUNSTEEL_STRIKE); }
|
||||
} SCENE {
|
||||
if (item == ITEM_ABILITY_SHIELD) {
|
||||
NONE_OF {
|
||||
MESSAGE("Shedinja fainted!");
|
||||
}
|
||||
} else {
|
||||
MESSAGE("Shedinja fainted!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,8 +47,7 @@ SINGLE_BATTLE_TEST("Clear Amulet prevents stat reducing effects")
|
|||
ASSUME(gMovesInfo[MOVE_CONFIDE].effect == EFFECT_SPECIAL_ATTACK_DOWN);
|
||||
ASSUME(gMovesInfo[MOVE_FAKE_TEARS].effect == EFFECT_SPECIAL_DEFENSE_DOWN_2);
|
||||
ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2);
|
||||
ASSUME(B_UPDATED_MOVE_DATA >= GEN_6);
|
||||
ASSUME(gMovesInfo[MOVE_SWEET_SCENT].effect == EFFECT_EVASION_DOWN_2);
|
||||
ASSUME(gMovesInfo[MOVE_SWEET_SCENT].effect == (B_UPDATED_MOVE_DATA >= GEN_6 ? EFFECT_EVASION_DOWN_2 : EFFECT_EVASION_DOWN));
|
||||
ASSUME(gMovesInfo[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_CLEAR_AMULET); };
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_SMACK_DOWN].additionalEffects->moveEffect == MOVE_EFFECT_SMACK_DOWN);
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_SMACK_DOWN, MOVE_EFFECT_SMACK_DOWN) == TRUE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Smack Down does not ground mons behind substitutes")
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_SMELLING_SALTS].additionalEffects->moveEffect == MOVE_EFFECT_REMOVE_STATUS);
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_SMELLING_SALTS, MOVE_EFFECT_REMOVE_STATUS) == TRUE);
|
||||
ASSUME(gMovesInfo[MOVE_SMELLING_SALTS].argument == STATUS1_PARALYSIS);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_SPARKLING_ARIA].additionalEffects->moveEffect == MOVE_EFFECT_REMOVE_STATUS);
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_SPARKLING_ARIA, MOVE_EFFECT_REMOVE_STATUS) == TRUE);
|
||||
ASSUME(gMovesInfo[MOVE_SPARKLING_ARIA].argument == STATUS1_BURN);
|
||||
ASSUME(gMovesInfo[MOVE_SPARKLING_ARIA].soundMove == TRUE);
|
||||
}
|
||||
|
|
44
test/battle/move_effect/tar_shot.c
Normal file
44
test/battle/move_effect/tar_shot.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_TAR_SHOT].effect == EFFECT_TAR_SHOT);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Tar Shot doubles the effectiveness of Fire-type moves used on the target")
|
||||
{
|
||||
s16 damage[2];
|
||||
u32 species;
|
||||
|
||||
PARAMETRIZE { species = SPECIES_WOBBUFFET; }
|
||||
PARAMETRIZE { species = SPECIES_OMASTAR; } // Dual type with double resists
|
||||
|
||||
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC);
|
||||
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC);
|
||||
ASSUME(gSpeciesInfo[SPECIES_OMASTAR].types[0] == TYPE_ROCK);
|
||||
ASSUME(gSpeciesInfo[SPECIES_OMASTAR].types[1] == TYPE_WATER);
|
||||
ASSUME(gMovesInfo[MOVE_EMBER].type == TYPE_FIRE);
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(species);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_EMBER); }
|
||||
TURN { MOVE(player, MOVE_TAR_SHOT); }
|
||||
TURN { MOVE(player, MOVE_EMBER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAR_SHOT, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
if (species != SPECIES_OMASTAR)
|
||||
MESSAGE("It's super effective!");
|
||||
else
|
||||
MESSAGE("It's not very effective…");
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_THOUSAND_ARROWS].additionalEffects->moveEffect == MOVE_EFFECT_SMACK_DOWN);
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_THOUSAND_ARROWS, MOVE_EFFECT_SMACK_DOWN) == TRUE);
|
||||
ASSUME(gMovesInfo[MOVE_THOUSAND_ARROWS].ignoreTypeIfFlyingAndUngrounded == TRUE);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_WAKE_UP_SLAP].additionalEffects->moveEffect == MOVE_EFFECT_REMOVE_STATUS);
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_WAKE_UP_SLAP, MOVE_EFFECT_REMOVE_STATUS) == TRUE);
|
||||
ASSUME(gMovesInfo[MOVE_WAKE_UP_SLAP].argument == STATUS1_SLEEP);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue