mirror of
https://github.com/pret/pokefirered.git
synced 2026-09-29 12:30:28 -05:00
Merge pull request #24 from cawtds/expansion-merge-20240827
Expansion merge upcoming 0bb43626afe2cdc6f9bdd7c246e2dca7198ddab3
This commit is contained in:
@@ -1670,6 +1670,10 @@
|
||||
callnative BS_DamageToQuarterTargetHP
|
||||
.endm
|
||||
|
||||
.macro ficklebeamdamagecalculation
|
||||
callnative BS_FickleBeamDamageCalculation
|
||||
.endm
|
||||
|
||||
@ various command changed to more readable macros
|
||||
.macro cancelmultiturnmoves battler:req
|
||||
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES
|
||||
@@ -2519,5 +2523,3 @@
|
||||
printstring STRINGID_EMPTYSTRING3
|
||||
waitmessage 1
|
||||
.endm
|
||||
|
||||
|
||||
|
||||
@@ -22,62 +22,6 @@
|
||||
.section script_data, "aw", %progbits
|
||||
.align 2
|
||||
|
||||
BattleScript_LocalTrainerBattleWon::
|
||||
jumpifbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_LocalTwoTrainersDefeated
|
||||
printstring STRINGID_PLAYERDEFEATEDTRAINER1
|
||||
goto BattleScript_LocalBattleWonLoseTexts
|
||||
BattleScript_LocalTwoTrainersDefeated::
|
||||
printstring STRINGID_TWOENEMIESDEFEATED
|
||||
BattleScript_LocalBattleWonLoseTexts::
|
||||
trainerslidein BS_ATTACKER
|
||||
waitstate
|
||||
printstring STRINGID_TRAINER1LOSETEXT
|
||||
jumpifnotbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_LocalTrainerBattleWonGotMoney
|
||||
trainerslideout B_POSITION_OPPONENT_LEFT
|
||||
waitstate
|
||||
trainerslidein BS_FAINTED
|
||||
waitstate
|
||||
printstring STRINGID_TRAINER2LOSETEXT
|
||||
BattleScript_LocalTrainerBattleWonGotMoney::
|
||||
getmoneyreward
|
||||
printstring STRINGID_PLAYERGOTMONEY
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
BattleScript_PayDayMoneyAndPickUpItems::
|
||||
givepaydaymoney
|
||||
pickup
|
||||
end2
|
||||
|
||||
BattleScript_LocalBattleLost::
|
||||
jumpifbattletype BATTLE_TYPE_INGAME_PARTNER, BattleScript_LocalBattleLostPrintWhiteOut
|
||||
jumpifbattletype BATTLE_TYPE_TRAINER_TOWER, BattleScript_BattleTowerLost
|
||||
jumpifbattletype BATTLE_TYPE_EREADER_TRAINER, BattleScript_LocalBattleLostEnd
|
||||
jumpifhalfword CMP_EQUAL, gTrainerBattleOpponent_A, TRAINER_SECRET_BASE, BattleScript_LocalBattleLostEnd
|
||||
jumpifbyte CMP_NOT_EQUAL, cMULTISTRING_CHOOSER, 0, BattleScript_RivalBattleLost
|
||||
BattleScript_LocalBattleLostPrintWhiteOut::
|
||||
.if B_WHITEOUT_MONEY >= GEN_4
|
||||
jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_LocalBattleLostEnd
|
||||
printstring STRINGID_PLAYERWHITEOUT
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
getmoneyreward
|
||||
printstring STRINGID_PLAYERWHITEOUT2
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
end2
|
||||
BattleScript_LocalBattleLostEnd::
|
||||
printstring STRINGID_PLAYERLOSTAGAINSTENEMYTRAINER
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
getmoneyreward
|
||||
printstring STRINGID_PLAYERPAIDPRIZEMONEY
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
end2
|
||||
.else
|
||||
printstring STRINGID_PLAYERWHITEOUT
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
printstring STRINGID_PLAYERWHITEOUT2
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
BattleScript_LocalBattleLostEnd::
|
||||
end2
|
||||
.endif
|
||||
|
||||
BattleScript_RivalBattleLost::
|
||||
jumpifhasnohp BS_ATTACKER, BattleScript_RivalBattleLostSkipMonRecall
|
||||
printstring STRINGID_TRAINER1MON1COMEBACK
|
||||
@@ -128,11 +72,6 @@ BattleScript_BattleTowerEtcTrainerBattleWonSkipText::
|
||||
pickup
|
||||
end2
|
||||
|
||||
BattleScript_PrintCantRunFromTrainer::
|
||||
jumpifbattletype BATTLE_TYPE_FIRST_BATTLE, BattleScript_LeftoverBirchString
|
||||
printstring STRINGID_NORUNNINGFROMTRAINERS
|
||||
end2
|
||||
|
||||
BattleScript_LeftoverBirchString::
|
||||
printstring STRINGID_DONTLEAVEBIRCH
|
||||
end2
|
||||
@@ -170,6 +109,19 @@ BattleScript_DamageToQuarterTargetHP::
|
||||
damagetoquartertargethp
|
||||
goto BattleScript_HitFromAtkAnimation
|
||||
|
||||
BattleScript_EffectFickleBeam::
|
||||
attackcanceler
|
||||
attackstring
|
||||
ppreduce
|
||||
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
|
||||
ficklebeamdamagecalculation
|
||||
goto BattleScript_HitFromCritCalc
|
||||
BattleScript_FickleBeamDoubled::
|
||||
pause B_WAIT_TIME_SHORTEST
|
||||
printstring STRINGID_FICKLEBEAMDOUBLED
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
goto BattleScript_HitFromCritCalc
|
||||
|
||||
BattleScript_Terastallization::
|
||||
@ TODO: no string prints in S/V, but right now this helps with clarity
|
||||
printstring STRINGID_PKMNSTORINGENERGY
|
||||
@@ -283,6 +235,7 @@ BattleScript_EffectShedTail::
|
||||
ppreduce
|
||||
waitstate
|
||||
jumpifstatus2 BS_ATTACKER, STATUS2_SUBSTITUTE, BattleScript_AlreadyHasSubstitute
|
||||
jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_ButItFailed
|
||||
jumpifcantswitch SWITCH_IGNORE_ESCAPE_PREVENTION | BS_ATTACKER, BattleScript_ButItFailed
|
||||
setsubstitute
|
||||
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SUBSTITUTE_FAILED, BattleScript_SubstituteString
|
||||
@@ -415,6 +368,7 @@ BattleScript_EffectChillyReceptionBlockedByStrongWinds:
|
||||
call BattleScript_MysteriousAirCurrentBlowsOnRet
|
||||
goto BattleScript_MoveSwitch
|
||||
BattleScript_EffectChillyReceptionTrySwitchWeatherFailed:
|
||||
jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_FailedFromAtkString
|
||||
jumpifcantswitch SWITCH_IGNORE_ESCAPE_PREVENTION | BS_ATTACKER, BattleScript_FailedFromAtkString
|
||||
call BattleScript_EffectChillyReceptionPlayAnimation
|
||||
return
|
||||
@@ -426,6 +380,7 @@ BattleScript_CheckPrimalWeather:
|
||||
return
|
||||
|
||||
BattleScript_MoveSwitch:
|
||||
jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_MoveSwitchEnd
|
||||
jumpifcantswitch SWITCH_IGNORE_ESCAPE_PREVENTION | BS_ATTACKER, BattleScript_MoveSwitchEnd
|
||||
printstring STRINGID_PKMNWENTBACK
|
||||
waitmessage B_WAIT_TIME_SHORT
|
||||
@@ -702,7 +657,7 @@ BattleScript_Teatimerod:
|
||||
statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_TeatimeBuffer
|
||||
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 0x2, BattleScript_TeatimeBuffer
|
||||
printfromtable gStatUpStringIds
|
||||
waitmessage 0x40
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
moveendto MOVEEND_NEXT_TARGET
|
||||
jumpifnexttargetvalid BattleScript_TeatimeLoop
|
||||
moveendcase MOVEEND_CLEAR_BITS
|
||||
@@ -714,7 +669,7 @@ BattleScript_Teatimemotor:
|
||||
statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_TeatimeBuffer
|
||||
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, 0x2, BattleScript_TeatimeBuffer
|
||||
printfromtable gStatUpStringIds
|
||||
waitmessage 0x40
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
moveendto MOVEEND_NEXT_TARGET
|
||||
jumpifnexttargetvalid BattleScript_TeatimeLoop
|
||||
moveendcase MOVEEND_CLEAR_BITS
|
||||
@@ -1002,6 +957,7 @@ BattleScript_OctlockTurnDmgEnd:
|
||||
|
||||
BattleScript_EffectPoltergeist::
|
||||
attackcanceler
|
||||
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
|
||||
attackstring
|
||||
ppreduce
|
||||
checkpoltergeist BS_TARGET, BattleScript_ButItFailed
|
||||
@@ -1051,19 +1007,10 @@ BattleScript_BothCanNoLongerEscape::
|
||||
return
|
||||
|
||||
BattleScript_EffectHyperspaceFury::
|
||||
jumpifspecies BS_ATTACKER, SPECIES_HOOPA_UNBOUND, BattleScript_EffectHyperspaceFuryUnbound
|
||||
jumpifspecies BS_ATTACKER, SPECIES_HOOPA_UNBOUND, BattleScript_EffectHit
|
||||
jumpifspecies BS_ATTACKER, SPECIES_HOOPA_CONFINED, BattleScript_ButHoopaCantUseIt
|
||||
goto BattleScript_PokemonCantUseTheMove
|
||||
|
||||
BattleScript_EffectHyperspaceFuryUnbound::
|
||||
attackcanceler
|
||||
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
|
||||
attackstring
|
||||
pause B_WAIT_TIME_LONG
|
||||
ppreduce
|
||||
seteffectprimary MOVE_EFFECT_FEINT
|
||||
goto BattleScript_HitFromCritCalc
|
||||
|
||||
BattleScript_ButHoopaCantUseIt:
|
||||
printstring STRINGID_BUTHOOPACANTUSEIT
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
@@ -3401,6 +3348,7 @@ BattleScript_EffectRoar::
|
||||
jumpiftargetdynamaxed BattleScript_RoarBlockedByDynamax
|
||||
accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON
|
||||
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
|
||||
jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_ButItFailed
|
||||
forcerandomswitch BattleScript_ButItFailed
|
||||
|
||||
BattleScript_RoarBlockedByDynamax:
|
||||
@@ -4553,6 +4501,7 @@ BattleScript_EffectBatonPass::
|
||||
attackcanceler
|
||||
attackstring
|
||||
ppreduce
|
||||
jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_ButItFailed
|
||||
jumpifcantswitch SWITCH_IGNORE_ESCAPE_PREVENTION | BS_ATTACKER, BattleScript_ButItFailed
|
||||
attackanimation
|
||||
waitanimation
|
||||
@@ -5724,6 +5673,62 @@ BattleScript_HandleFaintedMonMultipleEnd::
|
||||
switchineffects BS_FAINTED_MULTIPLE_2
|
||||
end2
|
||||
|
||||
BattleScript_LocalTrainerBattleWon::
|
||||
jumpifbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_LocalTwoTrainersDefeated
|
||||
printstring STRINGID_PLAYERDEFEATEDTRAINER1
|
||||
goto BattleScript_LocalBattleWonLoseTexts
|
||||
BattleScript_LocalTwoTrainersDefeated::
|
||||
printstring STRINGID_TWOENEMIESDEFEATED
|
||||
BattleScript_LocalBattleWonLoseTexts::
|
||||
trainerslidein BS_ATTACKER
|
||||
waitstate
|
||||
printstring STRINGID_TRAINER1LOSETEXT
|
||||
jumpifnotbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_LocalBattleWonReward
|
||||
trainerslideout B_POSITION_OPPONENT_LEFT
|
||||
waitstate
|
||||
trainerslidein BS_FAINTED
|
||||
waitstate
|
||||
printstring STRINGID_TRAINER2LOSETEXT
|
||||
BattleScript_LocalBattleWonReward::
|
||||
getmoneyreward
|
||||
printstring STRINGID_PLAYERGOTMONEY
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
BattleScript_PayDayMoneyAndPickUpItems::
|
||||
givepaydaymoney
|
||||
pickup
|
||||
end2
|
||||
|
||||
BattleScript_LocalBattleLost::
|
||||
jumpifbattletype BATTLE_TYPE_INGAME_PARTNER, BattleScript_LocalBattleLostPrintWhiteOut
|
||||
jumpifbattletype BATTLE_TYPE_TRAINER_TOWER, BattleScript_BattleTowerLost
|
||||
jumpifbattletype BATTLE_TYPE_EREADER_TRAINER, BattleScript_LocalBattleLostEnd
|
||||
jumpifhalfword CMP_EQUAL, gTrainerBattleOpponent_A, TRAINER_SECRET_BASE, BattleScript_LocalBattleLostEnd
|
||||
jumpifbyte CMP_NOT_EQUAL, cMULTISTRING_CHOOSER, 0, BattleScript_RivalBattleLost
|
||||
BattleScript_LocalBattleLostPrintWhiteOut::
|
||||
.if B_WHITEOUT_MONEY >= GEN_4
|
||||
jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_LocalBattleLostEnd
|
||||
printstring STRINGID_PLAYERWHITEOUT
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
getmoneyreward
|
||||
printstring STRINGID_PLAYERWHITEOUT2
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
end2
|
||||
BattleScript_LocalBattleLostEnd::
|
||||
printstring STRINGID_PLAYERLOSTAGAINSTENEMYTRAINER
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
getmoneyreward
|
||||
printstring STRINGID_PLAYERPAIDPRIZEMONEY
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
end2
|
||||
.else
|
||||
printstring STRINGID_PLAYERWHITEOUT
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
printstring STRINGID_PLAYERWHITEOUT2
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
BattleScript_LocalBattleLostEnd::
|
||||
end2
|
||||
.endif
|
||||
|
||||
BattleScript_SmokeBallEscape::
|
||||
playanimation BS_ATTACKER, B_ANIM_SMOKEBALL_ESCAPE
|
||||
printstring STRINGID_PKMNFLEDUSINGITS
|
||||
@@ -5745,6 +5750,10 @@ BattleScript_WildMonFled::
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
end2
|
||||
|
||||
BattleScript_PrintCantRunFromTrainer::
|
||||
printstring STRINGID_NORUNNINGFROMTRAINERS
|
||||
end2
|
||||
|
||||
BattleScript_PrintFailedToRunString::
|
||||
printfromtable gNoEscapeStringIds
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
@@ -7118,7 +7127,7 @@ BattleScript_TargetFormChangeWithStringNoPopup::
|
||||
|
||||
BattleScript_BattlerFormChangeWithStringEnd3::
|
||||
pause 5
|
||||
call BattleScript_AbilityPopUp
|
||||
call BattleScript_AbilityPopUpScripting
|
||||
flushtextbox
|
||||
handleformchange BS_SCRIPTING, 0
|
||||
handleformchange BS_SCRIPTING, 1
|
||||
@@ -7722,6 +7731,15 @@ BattleScript_CheekPouchActivates::
|
||||
copybyte gBattlerAttacker, sSAVED_BATTLER
|
||||
return
|
||||
|
||||
BattleScript_PickupActivates::
|
||||
pause 5
|
||||
tryrecycleitem BattleScript_PickupActivatesEnd
|
||||
call BattleScript_AbilityPopUp
|
||||
printstring STRINGID_XFOUNDONEY
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
BattleScript_PickupActivatesEnd:
|
||||
end3
|
||||
|
||||
BattleScript_HarvestActivates::
|
||||
pause 5
|
||||
tryrecycleitem BattleScript_HarvestActivatesEnd
|
||||
@@ -7969,7 +7987,7 @@ BattleScript_DeltaStreamActivates::
|
||||
end3
|
||||
|
||||
BattleScript_ProtosynthesisActivates::
|
||||
call BattleScript_AbilityPopUp
|
||||
call BattleScript_AbilityPopUpScripting
|
||||
printstring STRINGID_SUNLIGHTACTIVATEDABILITY
|
||||
waitmessage B_WAIT_TIME_MED
|
||||
printstring STRINGID_STATWASHEIGHTENED
|
||||
|
||||
@@ -574,12 +574,10 @@ struct LostItem
|
||||
u16 stolen:1;
|
||||
};
|
||||
|
||||
#if HQ_RANDOM == TRUE
|
||||
struct BattleVideo {
|
||||
u32 battleTypeFlags;
|
||||
rng_value_t rngSeed;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct BattleStruct
|
||||
{
|
||||
@@ -645,17 +643,13 @@ struct BattleStruct
|
||||
u16 chosenItem[MAX_BATTLERS_COUNT];
|
||||
u16 choicedMove[MAX_BATTLERS_COUNT];
|
||||
u16 changedItems[MAX_BATTLERS_COUNT];
|
||||
u8 canPickupItem;
|
||||
u8 switchInBattlerCounter;
|
||||
u8 turnSideTracker;
|
||||
u16 lastTakenMoveFrom[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT]; // a 2-D array [target][attacker]
|
||||
union {
|
||||
struct LinkBattlerHeader linkBattlerHeader;
|
||||
|
||||
#if HQ_RANDOM == FALSE
|
||||
u32 battleVideo[2];
|
||||
#else
|
||||
struct BattleVideo battleVideo;
|
||||
#endif
|
||||
} multiBuffer;
|
||||
u8 wishPerishSongState;
|
||||
u8 wishPerishSongBattlerId;
|
||||
@@ -724,7 +718,6 @@ struct BattleStruct
|
||||
bool8 effectsBeforeUsingMoveDone:1; // Mega Evo and Focus Punch/Shell Trap effects.
|
||||
u8 targetsDone[MAX_BATTLERS_COUNT]; // Each battler as a bit.
|
||||
u16 overwrittenAbilities[MAX_BATTLERS_COUNT]; // abilities overwritten during battle (keep separate from battle history in case of switching)
|
||||
bool8 allowedToChangeFormInWeather[PARTY_SIZE][NUM_BATTLE_SIDES]; // For each party member and side, used by Ice Face.
|
||||
u8 battleBondTransformed[NUM_BATTLE_SIDES]; // Bitfield for each party.
|
||||
u8 storedHealingWish:4; // Each battler as a bit.
|
||||
u8 storedLunarDance:4; // Each battler as a bit.
|
||||
@@ -761,6 +754,7 @@ struct BattleStruct
|
||||
u8 distortedTypeMatchups;
|
||||
u8 categoryOverride; // for Z-Moves and Max Moves
|
||||
u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side
|
||||
u8 fickleBeamBoosted:1;
|
||||
|
||||
// pokefirered
|
||||
u8 field_DA; // battle tower related
|
||||
|
||||
@@ -31,8 +31,8 @@ bool32 NoAliveMonsForPlayer(void);
|
||||
bool32 NoAliveMonsForEitherParty(void);
|
||||
void SetMoveEffect(bool32 primary, bool32 certain);
|
||||
bool32 CanBattlerSwitch(u32 battlerId);
|
||||
void BattleDestroyYesNoCursorAt(void);
|
||||
void BattleCreateYesNoCursorAt(void);
|
||||
void BattleDestroyYesNoCursorAt(u8 cursorPosition);
|
||||
void BattleCreateYesNoCursorAt(u8 cursorPosition);
|
||||
void BufferMoveToLearnIntoBattleTextBuff2(void);
|
||||
void HandleBattleWindow(u8 xStart, u8 yStart, u8 xEnd, u8 yEnd, u8 flags);
|
||||
bool8 UproarWakeUpCheck(u8 battlerId);
|
||||
|
||||
@@ -737,6 +737,7 @@ extern const u8 BattleScript_WeakArmorActivates[];
|
||||
extern const u8 BattleScript_FellStingerRaisesStat[];
|
||||
extern const u8 BattleScript_SnowWarningActivatesHail[];
|
||||
extern const u8 BattleScript_SnowWarningActivatesSnow[];
|
||||
extern const u8 BattleScript_PickupActivates[];
|
||||
extern const u8 BattleScript_HarvestActivates[];
|
||||
extern const u8 BattleScript_ImposterActivates[];
|
||||
extern const u8 BattleScript_SelectingNotAllowedMoveAssaultVest[];
|
||||
@@ -1274,6 +1275,8 @@ extern const u8 BattleScript_EffectUpperHand[];
|
||||
extern const u8 BattleScript_EffectTidyUp[];
|
||||
extern const u8 BattleScript_EffectSpicyExtract[];
|
||||
extern const u8 BattleScript_DamageToQuarterTargetHP[];
|
||||
extern const u8 BattleScript_EffectFickleBeam[];
|
||||
extern const u8 BattleScript_FickleBeamDoubled[];
|
||||
|
||||
|
||||
#endif // GUARD_BATTLE_SCRIPTS_H
|
||||
|
||||
@@ -232,6 +232,8 @@ bool32 TryRoomService(u32 battler);
|
||||
void BufferStatChange(u32 battler, u8 statId, u8 stringId);
|
||||
bool32 BlocksPrankster(u16 move, u32 battlerPrankster, u32 battlerDef, bool32 checkTarget);
|
||||
u16 GetUsedHeldItem(u32 battler);
|
||||
bool32 PickupHasValidTarget(u32 battler);
|
||||
bool32 CantPickupItem(u32 battler);
|
||||
bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags);
|
||||
u32 GetBattlerMoveTargetType(u32 battler, u32 move);
|
||||
bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, u16 move);
|
||||
|
||||
@@ -110,7 +110,7 @@
|
||||
#define B_BURN_HIT_THAW GEN_LATEST // In Gen6+, damaging moves with a chance of burn will thaw the target, regardless if they're fire-type moves or not.
|
||||
#define B_HEALING_WISH_SWITCH GEN_LATEST // In Gen5+, the mon receiving Healing Wish is sent out at the end of the turn.
|
||||
// Additionally, in gen8+ the Healing Wish's effect will be stored until the user switches into a statused or hurt mon.
|
||||
#define B_DEFOG_CLEARS_TERRAIN GEN_LATEST // In Gen8+, Defog also clears active Terrain.
|
||||
#define B_DEFOG_EFFECT_CLEARING GEN_LATEST // In Gen6+, Defog clears Spikes, Toxic Spikes, Stealth Rock and Sticky Web from both sides. In Gen8+, Defog also clears active Terrain.
|
||||
#define B_STOCKPILE_RAISES_DEFS GEN_LATEST // In Gen4+, Stockpile also raises Defense and Sp. Defense stats. Once Spit Up / Swallow is used, these stat changes are lost.
|
||||
#define B_TRANSFORM_SHINY GEN_LATEST // In Gen4+, Transform will copy the shiny state of the opponent instead of maintaining its own shiny state.
|
||||
#define B_TRANSFORM_FORM_CHANGES GEN_LATEST // In Gen5+, Transformed Pokemon cannot change forms.
|
||||
@@ -219,7 +219,7 @@
|
||||
#define B_FAST_HP_DRAIN TRUE // If set to TRUE, HP bars will move faster.
|
||||
#define B_FAST_EXP_GROW TRUE // If set to TRUE, EXP bars will move faster.
|
||||
#define B_SHOW_TARGETS TRUE // If set to TRUE, all available targets, for moves hitting 2 or 3 Pokémon, will be shown before selecting a move.
|
||||
#define B_SHOW_CATEGORY_ICON TRUE // If set to TRUE, it will show an icon in the summary showing the move's category.
|
||||
#define B_SHOW_CATEGORY_ICON TRUE // If set to TRUE, it will show an icon in the summary and move relearner showing the move's category.
|
||||
#define B_HIDE_HEALTHBOX_IN_ANIMS TRUE // If set to TRUE, hides healthboxes during move animations.
|
||||
#define B_EXPANDED_MOVE_NAMES TRUE // If set to FALSE, move names are decreased from 16 characters to 12 characters.
|
||||
#define B_WAIT_TIME_MULTIPLIER 16 // This determines how long text pauses in battle last. Vanilla is 16. Lower values result in faster battles.
|
||||
@@ -251,26 +251,31 @@
|
||||
#define B_TRY_CATCH_TRAINER_BALL GEN_LATEST // In Gen4+, trying to catch a Trainer's Pokémon does not consume the Poké Ball.
|
||||
|
||||
// Animation Settings
|
||||
#define B_NEW_SWORD_PARTICLE TRUE // If set to TRUE, it updates Swords Dance's particle.
|
||||
#define B_NEW_LEECH_SEED_PARTICLE TRUE // If set to TRUE, it updates Leech Seed's animation particle.
|
||||
#define B_NEW_HORN_ATTACK_PARTICLE TRUE // If set to TRUE, it updates Horn Attack's horn particle.
|
||||
#define B_NEW_ROCKS_PARTICLE TRUE // If set to TRUE, it updates rock particles.
|
||||
#define B_NEW_LEAF_PARTICLE TRUE // If set to TRUE, it updates leaf particle.
|
||||
#define B_NEW_EMBER_PARTICLES TRUE // If set to TRUE, it updates Ember's fire particle.
|
||||
#define B_NEW_MEAN_LOOK_PARTICLE TRUE // If set to TRUE, it updates Mean Look's eye particle.
|
||||
#define B_NEW_TEETH_PARTICLE TRUE // If set to TRUE, it updates Bite/Crunch teeth particle.
|
||||
#define B_NEW_HANDS_FEET_PARTICLE TRUE // If set to TRUE, it updates chop/kick/punch particles.
|
||||
#define B_NEW_SPIKES_PARTICLE TRUE // If set to TRUE, it updates Spikes particle.
|
||||
#define B_NEW_FLY_BUBBLE_PARTICLE TRUE // If set to TRUE, it updates Fly's 'bubble' particle.
|
||||
#define B_NEW_CURSE_NAIL_PARTICLE TRUE // If set to TRUE, it updates Curse's nail.
|
||||
#define B_NEW_BATON_PASS_BALL_PARTICLE TRUE // If set to TRUE, it updates Baton Pass' Poké Ball sprite.
|
||||
#define B_NEW_MORNING_SUN_STAR_PARTICLE TRUE // If set to TRUE, it updates Morning Sun's star particles.
|
||||
#define B_NEW_IMPACT_PALETTE TRUE // If set to TRUE, it updates the basic 'hit' palette.
|
||||
#define B_NEW_SURF_PARTICLE_PALETTE TRUE // If set to TRUE, it updates Surf's wave palette.
|
||||
#define B_NEW_SWORD_PARTICLE FALSE // If set to TRUE, it updates Swords Dance's particle.
|
||||
#define B_NEW_LEECH_SEED_PARTICLE FALSE // If set to TRUE, it updates Leech Seed's animation particle.
|
||||
#define B_NEW_HORN_ATTACK_PARTICLE FALSE // If set to TRUE, it updates Horn Attack's horn particle.
|
||||
#define B_NEW_ROCKS_PARTICLE FALSE // If set to TRUE, it updates rock particles.
|
||||
#define B_NEW_LEAF_PARTICLE FALSE // If set to TRUE, it updates leaf particle.
|
||||
#define B_NEW_EMBER_PARTICLES FALSE // If set to TRUE, it updates Ember's fire particle.
|
||||
#define B_NEW_MEAN_LOOK_PARTICLE FALSE // If set to TRUE, it updates Mean Look's eye particle.
|
||||
#define B_NEW_TEETH_PARTICLE FALSE // If set to TRUE, it updates Bite/Crunch teeth particle.
|
||||
#define B_NEW_HANDS_FEET_PARTICLE FALSE // If set to TRUE, it updates chop/kick/punch particles.
|
||||
#define B_NEW_SPIKES_PARTICLE FALSE // If set to TRUE, it updates Spikes particle.
|
||||
#define B_NEW_FLY_BUBBLE_PARTICLE FALSE // If set to TRUE, it updates Fly's 'bubble' particle.
|
||||
#define B_NEW_CURSE_NAIL_PARTICLE FALSE // If set to TRUE, it updates Curse's nail.
|
||||
#define B_NEW_BATON_PASS_BALL_PARTICLE FALSE // If set to TRUE, it updates Baton Pass' Poké Ball sprite.
|
||||
#define B_NEW_MORNING_SUN_STAR_PARTICLE FALSE // If set to TRUE, it updates Morning Sun's star particles.
|
||||
#define B_NEW_IMPACT_PALETTE FALSE // If set to TRUE, it updates the basic 'hit' palette.
|
||||
#define B_NEW_SURF_PARTICLE_PALETTE FALSE // If set to TRUE, it updates Surf's wave palette.
|
||||
|
||||
// Poké Ball animation and sounds
|
||||
#define B_ENEMY_THROW_BALLS GEN_LATEST // In GEN_6+, enemy Trainers throw Poké Balls into battle instead of them just appearing on the ground and opening.
|
||||
#define B_ENEMY_THROW_BALLS_SOUND GEN_LATEST // In GEN_5+, enemy Trainer's Poké Balls make a sound when thrown to send out a Pokémon. This can only be used when B_ENEMY_THROW_BALLS is set to GEN_6 or later.
|
||||
#define B_PLAYER_THROW_BALLS_SOUND GEN_LATEST // In GEN_5+, the player's Poké Balls make a sound when thrown to send out a Pokémon.
|
||||
|
||||
#define SHOW_TYPES_NEVER 0
|
||||
#define SHOW_TYPES_ALWAYS 1
|
||||
#define SHOW_TYPES_CAUGHT 2
|
||||
#define B_SHOW_TYPES SHOW_TYPES_NEVER // When defined as SHOW_TYPES_ALWAYS, after selecting "Fight" in battle, the types of all Pokemon are revealed. Whe defined as SHOW_TYPES_OWN, types are only revealed if the player owns the mon in question.
|
||||
|
||||
#endif // GUARD_CONFIG_BATTLE_H
|
||||
|
||||
@@ -1336,8 +1336,9 @@
|
||||
#define STRINGID_FOGISDEEP 737
|
||||
#define STRINGID_FOGLIFTED 738
|
||||
#define STRINGID_PKMNMADESHELLGLEAM 739
|
||||
#define STRINGID_FICKLEBEAMDOUBLED 740
|
||||
|
||||
#define BATTLESTRINGS_COUNT 740
|
||||
#define BATTLESTRINGS_COUNT 741
|
||||
|
||||
// This is the string id that gBattleStringsTable starts with.
|
||||
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,
|
||||
|
||||
@@ -7,25 +7,21 @@
|
||||
#define ISO_RANDOMIZE1(val)(RAND_MULT * (val) + 24691)
|
||||
#define ISO_RANDOMIZE2(val)(RAND_MULT * (val) + 12345)
|
||||
|
||||
/* Some functions have been added to support HQ_RANDOM.
|
||||
/* Some functions have been added to support Expansion's RNG implementation.
|
||||
*
|
||||
* If using HQ_RANDOM, you cannot call Random() in interrupt handlers safely.
|
||||
* AdvanceRandom() is provided to handle burning numbers in the VBlank handler
|
||||
* if you choose to do that, and can be used regardless of HQ_RANDOM setting.
|
||||
* LocalRandom(*val) provides a higher-quality replacement for uses of
|
||||
* ISO_RANDOMIZE in vanilla Emerald. You can use LocalRandomSeed(u32) to
|
||||
* create a local state.
|
||||
*
|
||||
* It is no longer possible to call Random() in interrupt handlers safely.
|
||||
* AdvanceRandom() is provided to handle burning numbers in VBlank handlers.
|
||||
* If you need to use random numbers in the VBlank handler, a local state
|
||||
* should be used instead.
|
||||
*
|
||||
* LocalRandom(*val) allows you to have local random states that are the same
|
||||
* type as the global states regardless of HQ_RANDOM setting, which is useful
|
||||
* if you want to be able to set them from or assign them to gRngValue.
|
||||
* LocalRandomSeed(u32) returns a properly seeded rng_value_t.
|
||||
*
|
||||
* Random2_32() was added to HQ_RANDOM because the output of the generator is
|
||||
* always 32 bits and Random()/Random2() are just wrappers in that mode. It is
|
||||
* also available in non-HQ mode for consistency.
|
||||
* Random2_32() was added, even though it is not used directly, because the
|
||||
* underlying RNG always outputs 32 bits.
|
||||
*/
|
||||
|
||||
#if HQ_RANDOM == TRUE
|
||||
struct Sfc32State {
|
||||
u32 a;
|
||||
u32 b;
|
||||
@@ -71,40 +67,6 @@ static inline u16 Random2(void)
|
||||
}
|
||||
|
||||
void AdvanceRandom(void);
|
||||
#else
|
||||
typedef u32 rng_value_t;
|
||||
|
||||
#define RNG_VALUE_EMPTY 0
|
||||
|
||||
//Returns a 16-bit pseudorandom number
|
||||
u16 Random(void);
|
||||
u16 Random2(void);
|
||||
|
||||
//Sets the initial seed value of the pseudorandom number generator
|
||||
void SeedRng(u16 seed);
|
||||
void SeedRng2(u16 seed);
|
||||
|
||||
//Returns a 32-bit pseudorandom number
|
||||
#define Random32() (Random() | (Random() << 16))
|
||||
#define Random2_32() (Random2() | (Random2() << 16))
|
||||
|
||||
static inline u16 LocalRandom(rng_value_t *val)
|
||||
{
|
||||
*val = ISO_RANDOMIZE1(*val);
|
||||
return *val >> 16;
|
||||
}
|
||||
|
||||
static inline void AdvanceRandom(void)
|
||||
{
|
||||
Random();
|
||||
}
|
||||
|
||||
static inline rng_value_t LocalRandomSeed(u32 seed)
|
||||
{
|
||||
return seed;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
extern rng_value_t gRngValue;
|
||||
extern rng_value_t gRng2Value;
|
||||
@@ -174,12 +136,14 @@ enum RandomTag
|
||||
RNG_G_MAX_BEFUDDLE,
|
||||
RNG_G_MAX_REPLENISH,
|
||||
RNG_G_MAX_SNOOZE,
|
||||
RNG_HARVEST,
|
||||
RNG_HITS,
|
||||
RNG_HOLD_EFFECT_FLINCH,
|
||||
RNG_INFATUATION,
|
||||
RNG_LOADED_DICE,
|
||||
RNG_METRONOME,
|
||||
RNG_PARALYSIS,
|
||||
RNG_PICKUP,
|
||||
RNG_POISON_POINT,
|
||||
RNG_POISON_TOUCH,
|
||||
RNG_RAMPAGE_TURNS,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "battle_ai_util.h"
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_controllers.h"
|
||||
// #include "battle_factory.h"
|
||||
#include "battle_setup.h"
|
||||
#include "battle_z_move.h"
|
||||
#include "battle_terastal.h"
|
||||
@@ -107,7 +108,7 @@ void BattleAI_SetupItems(void)
|
||||
// Items are allowed to use in ONLY trainer battles.
|
||||
if ((gBattleTypeFlags & BATTLE_TYPE_TRAINER)
|
||||
&& !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_SAFARI | BATTLE_TYPE_BATTLE_TOWER
|
||||
| BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_FRONTIER
|
||||
| BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_SECRET_BASE | BATTLE_TYPE_FRONTIER
|
||||
| BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_RECORDED_LINK)
|
||||
)
|
||||
)
|
||||
@@ -167,13 +168,13 @@ static u32 GetAiFlags(u16 trainerId)
|
||||
flags = AI_FLAG_CHECK_BAD_MOVE;
|
||||
// else if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE)
|
||||
// flags = AI_FLAG_FIRST_BATTLE;
|
||||
else if (gBattleTypeFlags & (BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TRAINER_TOWER))
|
||||
else if (gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TRAINER_HILL | BATTLE_TYPE_SECRET_BASE))
|
||||
flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT;
|
||||
else
|
||||
flags = GetTrainerAIFlagsFromId(trainerId);
|
||||
}
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
flags |= AI_FLAG_DOUBLE_BATTLE;
|
||||
}
|
||||
@@ -223,7 +224,7 @@ void BattleAI_SetupFlags(void)
|
||||
{
|
||||
AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_RIGHT] = GetAiFlags(gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE));
|
||||
}
|
||||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsAiVsAiBattle())
|
||||
else if (IsDoubleBattle() && IsAiVsAiBattle())
|
||||
{
|
||||
AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_RIGHT] = AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_LEFT];
|
||||
}
|
||||
@@ -261,7 +262,7 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler)
|
||||
// Ignore moves that aren't possible to use.
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (gBitTable[i] & moveLimitations)
|
||||
if ((1u << i) & moveLimitations)
|
||||
SET_SCORE(battler, i, 0);
|
||||
}
|
||||
|
||||
@@ -274,7 +275,7 @@ u32 BattleAI_ChooseMoveOrAction(void)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||||
if (!IsDoubleBattle())
|
||||
ret = ChooseMoveOrAction_Singles(sBattler_AI);
|
||||
else
|
||||
ret = ChooseMoveOrAction_Doubles(sBattler_AI);
|
||||
@@ -322,7 +323,7 @@ void Ai_InitPartyStruct(void)
|
||||
|
||||
// Save first 2 or 4(in doubles) mons
|
||||
CopyBattlerDataToAIParty(B_POSITION_PLAYER_LEFT, B_SIDE_PLAYER);
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
if (IsDoubleBattle())
|
||||
CopyBattlerDataToAIParty(B_POSITION_PLAYER_RIGHT, B_SIDE_PLAYER);
|
||||
|
||||
// If player's partner is AI, save opponent mons
|
||||
@@ -445,7 +446,7 @@ static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u3
|
||||
if (move != 0
|
||||
&& move != 0xFFFF
|
||||
//&& gMovesInfo[move].power != 0 /* we want to get effectiveness and accuracy of status moves */
|
||||
&& !(aiData->moveLimitations[battlerAtk] & gBitTable[i]))
|
||||
&& !(aiData->moveLimitations[battlerAtk] & (1u << i)))
|
||||
{
|
||||
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY)
|
||||
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather, DMG_ROLL_HIGHEST);
|
||||
@@ -505,12 +506,12 @@ static bool32 AI_SwitchMonIfSuitable(u32 battler, bool32 doubleBattle)
|
||||
if (doubleBattle)
|
||||
{
|
||||
u32 partner = BATTLE_PARTNER(battler);
|
||||
if (AI_DATA->shouldSwitchMon & gBitTable[partner] && AI_DATA->monToSwitchId[partner] == monToSwitchId)
|
||||
if (AI_DATA->shouldSwitchMon & (1u << partner) && AI_DATA->monToSwitchId[partner] == monToSwitchId)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
AI_DATA->shouldSwitchMon |= gBitTable[battler];
|
||||
AI_DATA->shouldSwitchMon |= 1 << battler;
|
||||
AI_DATA->monToSwitchId[battler] = monToSwitchId;
|
||||
return TRUE;
|
||||
}
|
||||
@@ -523,6 +524,7 @@ static bool32 AI_ShouldSwitchIfBadMoves(u32 battler, bool32 doubleBattle)
|
||||
// If can switch.
|
||||
if (CountUsablePartyMons(battler) > 0
|
||||
&& !IsBattlerTrapped(battler, TRUE)
|
||||
&& !(gBattleTypeFlags & (BATTLE_TYPE_ARENA | BATTLE_TYPE_PALACE))
|
||||
&& AI_THINKING_STRUCT->aiFlags[battler] & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS)
|
||||
&& !(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SEQUENCE_SWITCHING))
|
||||
{
|
||||
@@ -1734,6 +1736,14 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
break;
|
||||
case EFFECT_HIT_ESCAPE:
|
||||
break;
|
||||
case EFFECT_CHILLY_RECEPTION:
|
||||
if (CountUsablePartyMons(battlerAtk) == 0)
|
||||
ADJUST_SCORE(-10);
|
||||
else if (weather & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY) || IsMoveEffectWeather(aiData->partnerMove))
|
||||
ADJUST_SCORE(-8);
|
||||
else if (weather & B_WEATHER_HAIL)
|
||||
ADJUST_SCORE(-2); // mainly to prevent looping between hail and snow
|
||||
break;
|
||||
case EFFECT_BELLY_DRUM:
|
||||
case EFFECT_FILLET_AWAY:
|
||||
if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY)
|
||||
@@ -2045,7 +2055,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
|
||||
/*if (AI_THINKING_STRUCT->aiFlags[battlerAtk] == AI_SCRIPT_CHECK_BAD_MOVE //Only basic AI
|
||||
&& IS_DOUBLE_BATTLE) //Make the regular AI know how to use Protect minimally in Doubles
|
||||
&& IsDoubleBattle()) //Make the regular AI know how to use Protect minimally in Doubles
|
||||
{
|
||||
u8 shouldProtect = ShouldProtect(battlerAtk, battlerDef, move);
|
||||
if (shouldProtect == USE_PROTECT || shouldProtect == PROTECT_FROM_FOES)
|
||||
@@ -2768,6 +2778,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
case EFFECT_SNOWSCAPE:
|
||||
case EFFECT_RAIN_DANCE:
|
||||
case EFFECT_SANDSTORM:
|
||||
case EFFECT_CHILLY_RECEPTION:
|
||||
if (IsMoveEffectWeather(move))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
@@ -2847,6 +2858,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_SNOWSCAPE:
|
||||
case EFFECT_CHILLY_RECEPTION:
|
||||
if (IsBattlerAlive(battlerAtkPartner)
|
||||
&& ShouldSetSnow(battlerAtkPartner, atkPartnerAbility, atkPartnerHoldEffect))
|
||||
{
|
||||
@@ -3616,6 +3628,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
//fallthrough
|
||||
case EFFECT_HIT_ESCAPE:
|
||||
case EFFECT_PARTING_SHOT:
|
||||
case EFFECT_CHILLY_RECEPTION:
|
||||
if (!IsDoubleBattle())
|
||||
{
|
||||
switch (ShouldPivot(battlerAtk, battlerDef, aiData->abilities[battlerDef], move, movesetIndex))
|
||||
@@ -4051,7 +4064,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
ADJUST_SCORE(DECENT_EFFECT); // Force 'em out next turn
|
||||
break;
|
||||
default:
|
||||
if (move != MOVE_BESTOW && aiData->items[battlerAtk] == ITEM_NONE)
|
||||
if (gMovesInfo[gCurrentMove].effect != EFFECT_BESTOW && aiData->items[battlerAtk] == ITEM_NONE)
|
||||
{
|
||||
switch (aiData->holdEffects[battlerDef])
|
||||
{
|
||||
@@ -4874,6 +4887,7 @@ static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
|
||||
case EFFECT_SANDSTORM:
|
||||
case EFFECT_HAIL:
|
||||
case EFFECT_SNOWSCAPE:
|
||||
case EFFECT_CHILLY_RECEPTION:
|
||||
case EFFECT_GEOMANCY:
|
||||
case EFFECT_VICTORY_DANCE:
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
@@ -5042,7 +5056,7 @@ static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 scor
|
||||
static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
{
|
||||
u32 effect = gMovesInfo[move].effect;
|
||||
u32 moveType = TYPE_NONE;
|
||||
u32 moveType = 0;
|
||||
|
||||
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||
moveType = GetMoveType(move);
|
||||
|
||||
@@ -40,7 +40,7 @@ static void InitializeSwitchinCandidate(struct Pokemon *mon)
|
||||
static bool32 IsAceMon(u32 battler, u32 monPartyId)
|
||||
{
|
||||
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_ACE_POKEMON
|
||||
&& !(gBattleStruct->forcedSwitch & gBitTable[battler])
|
||||
&& !(gBattleStruct->forcedSwitch & (1u << battler))
|
||||
&& monPartyId == CalculateEnemyPartyCount()-1)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
@@ -82,7 +82,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult)
|
||||
return FALSE;
|
||||
|
||||
// Double Battles aren't included in AI_FLAG_SMART_MON_CHOICE. Defaults to regular switch in logic
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
if (IsDoubleBattle())
|
||||
return FALSE;
|
||||
|
||||
opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler));
|
||||
@@ -218,9 +218,9 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult)
|
||||
|
||||
static bool32 ShouldSwitchIfAllBadMoves(u32 battler, bool32 emitResult)
|
||||
{
|
||||
if (AI_DATA->shouldSwitchMon & gBitTable[battler])
|
||||
if (AI_DATA->shouldSwitchMon & (1u << battler))
|
||||
{
|
||||
AI_DATA->shouldSwitchMon &= ~(gBitTable[battler]);
|
||||
AI_DATA->shouldSwitchMon &= ~(1u << battler);
|
||||
gBattleStruct->AI_monToSwitchIntoId[battler] = AI_DATA->monToSwitchId[battler];
|
||||
if (emitResult)
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_SWITCH, 0);
|
||||
@@ -242,7 +242,7 @@ static bool32 ShouldSwitchIfWonderGuard(u32 battler, bool32 emitResult)
|
||||
struct Pokemon *party = NULL;
|
||||
u16 move;
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
if (IsDoubleBattle())
|
||||
return FALSE;
|
||||
|
||||
opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler));
|
||||
@@ -318,10 +318,10 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler, bool32 emitResult)
|
||||
if (IS_MOVE_STATUS(gLastLandedMoves[battler]))
|
||||
return FALSE;
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))])
|
||||
if (gAbsentBattlerFlags & (1u << GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))))
|
||||
battlerIn2 = battler;
|
||||
else
|
||||
battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)));
|
||||
@@ -690,7 +690,7 @@ static bool32 HasSuperEffectiveMoveAgainstOpponents(u32 battler, bool32 noRng)
|
||||
u32 opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler));
|
||||
u32 opposingBattler = GetBattlerAtPosition(opposingPosition);
|
||||
|
||||
if (!(gAbsentBattlerFlags & gBitTable[opposingBattler]))
|
||||
if (!(gAbsentBattlerFlags & (1u << opposingBattler)))
|
||||
{
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
@@ -707,12 +707,12 @@ static bool32 HasSuperEffectiveMoveAgainstOpponents(u32 battler, bool32 noRng)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||||
if (!IsDoubleBattle())
|
||||
return FALSE;
|
||||
|
||||
opposingBattler = GetBattlerAtPosition(BATTLE_PARTNER(opposingPosition));
|
||||
|
||||
if (!(gAbsentBattlerFlags & gBitTable[opposingBattler]))
|
||||
if (!(gAbsentBattlerFlags & (1u << opposingBattler)))
|
||||
{
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
@@ -765,10 +765,10 @@ static bool32 FindMonWithFlagsAndSuperEffective(u32 battler, u16 flags, u32 modu
|
||||
if (IS_MOVE_STATUS(gLastLandedMoves[battler]))
|
||||
return FALSE;
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))])
|
||||
if (gAbsentBattlerFlags & (1u << GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))))
|
||||
battlerIn2 = battler;
|
||||
else
|
||||
battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)));
|
||||
@@ -846,10 +846,10 @@ static bool32 CanMonSurviveHazardSwitchin(u32 battler)
|
||||
// Battler will faint to hazards, check to see if another mon can clear them
|
||||
if (hazardDamage > battlerHp)
|
||||
{
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))])
|
||||
if (gAbsentBattlerFlags & (1u << GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))))
|
||||
battlerIn2 = battler;
|
||||
else
|
||||
battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)));
|
||||
@@ -881,7 +881,9 @@ static bool32 CanMonSurviveHazardSwitchin(u32 battler)
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
aiMove = GetMonData(&party[i], MON_DATA_MOVE1 + j, NULL);
|
||||
if (aiMove == MOVE_RAPID_SPIN || aiMove == MOVE_DEFOG || aiMove == MOVE_MORTAL_SPIN || aiMove == MOVE_TIDY_UP)
|
||||
if (MoveHasAdditionalEffectSelf(aiMove, MOVE_EFFECT_RAPID_SPIN)
|
||||
|| (B_DEFOG_EFFECT_CLEARING >= GEN_6 && gMovesInfo[aiMove].effect == EFFECT_DEFOG)
|
||||
|| gMovesInfo[aiMove].effect == EFFECT_TIDY_UP)
|
||||
{
|
||||
// Have a mon that can clear the hazards, so switching out is okay
|
||||
return TRUE;
|
||||
@@ -1014,6 +1016,8 @@ bool32 ShouldSwitch(u32 battler, bool32 emitResult)
|
||||
return FALSE;
|
||||
if (IsAbilityPreventingEscape(battler))
|
||||
return FALSE;
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
return FALSE;
|
||||
|
||||
// Sequence Switching AI never switches mid-battle
|
||||
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SEQUENCE_SWITCHING)
|
||||
@@ -1021,10 +1025,10 @@ bool32 ShouldSwitch(u32 battler, bool32 emitResult)
|
||||
|
||||
availableToSwitch = 0;
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))])
|
||||
if (gAbsentBattlerFlags & (1u << GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))))
|
||||
battlerIn2 = battler;
|
||||
else
|
||||
battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)));
|
||||
@@ -1138,7 +1142,7 @@ void AI_TrySwitchOrUseItem(u32 battler)
|
||||
s32 monToSwitchId = AI_DATA->mostSuitableMonId[battler];
|
||||
if (monToSwitchId == PARTY_SIZE)
|
||||
{
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||||
if (!IsDoubleBattle())
|
||||
{
|
||||
battlerIn1 = GetBattlerAtPosition(battlerPosition);
|
||||
battlerIn2 = battlerIn1;
|
||||
@@ -1193,14 +1197,14 @@ static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u
|
||||
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (invalidMons & gBitTable[i])
|
||||
if (invalidMons & (1u << i))
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
if (GetMonData(&party[i], MON_DATA_MOVE1 + j, NULL) == MOVE_BATON_PASS)
|
||||
{
|
||||
bits |= gBitTable[i];
|
||||
bits |= 1u << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1211,7 +1215,7 @@ static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u
|
||||
do
|
||||
{
|
||||
i = (Random() % (lastId - firstId)) + firstId;
|
||||
} while (!(bits & gBitTable[i]));
|
||||
} while (!(bits & (1 << i)));
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -1229,7 +1233,7 @@ static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId,
|
||||
// Find the mon whose type is the most suitable defensively.
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (!(gBitTable[i] & invalidMons) && !(gBitTable[i] & bits))
|
||||
if (!((1u << i) & invalidMons) && !((1u << i) & bits))
|
||||
{
|
||||
u16 species = GetMonData(&party[i], MON_DATA_SPECIES);
|
||||
uq4_12_t typeEffectiveness = UQ_4_12(1.0);
|
||||
@@ -1269,7 +1273,7 @@ static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId,
|
||||
if (i != MAX_MON_MOVES)
|
||||
return bestMonId; // Has both the typing and at least one super effective move.
|
||||
|
||||
bits |= gBitTable[bestMonId]; // Sorry buddy, we want something better.
|
||||
bits |= (1u << bestMonId); // Sorry buddy, we want something better.
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1291,7 +1295,7 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
|
||||
// If we couldn't find the best mon in terms of typing, find the one that deals most damage.
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (gBitTable[i] & invalidMons)
|
||||
if ((1 << (i)) & invalidMons)
|
||||
continue;
|
||||
InitializeSwitchinCandidate(&party[i]);
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
@@ -1562,9 +1566,9 @@ static u32 GetSwitchinStatusDamage(u32 battler)
|
||||
if ((status & STATUS1_TOXIC_COUNTER) != STATUS1_TOXIC_TURN(15)) // not 16 turns
|
||||
AI_DATA->switchinCandidate.battleMon.status1 += STATUS1_TOXIC_TURN(1);
|
||||
statusDamage = maxHP / 16;
|
||||
statusDamage *= AI_DATA->switchinCandidate.battleMon.status1 & STATUS1_TOXIC_COUNTER >> 8;
|
||||
if (statusDamage == 0)
|
||||
statusDamage = 1;
|
||||
statusDamage *= AI_DATA->switchinCandidate.battleMon.status1 & STATUS1_TOXIC_COUNTER >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1731,7 +1735,7 @@ static int GetRandomSwitchinWithBatonPass(int aliveCount, int bits, int firstId,
|
||||
do
|
||||
{
|
||||
return (Random() % (lastId - firstId)) + firstId;
|
||||
} while (!(bits & gBitTable[currentMonId]));
|
||||
} while (!(bits & (1 << (currentMonId))));
|
||||
}
|
||||
|
||||
// Catch any other cases (such as only one mon alive and it has Baton Pass)
|
||||
@@ -1871,7 +1875,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
||||
|
||||
// Check for Baton Pass; hitsToKO requirements mean mon can boost and BP without dying whether it's slower or not
|
||||
if (aiMove == MOVE_BATON_PASS && ((hitsToKOAI > hitsToKOAIThreshold + 1 && AI_DATA->switchinCandidate.battleMon.speed < playerMonSpeed) || (hitsToKOAI > hitsToKOAIThreshold && AI_DATA->switchinCandidate.battleMon.speed > playerMonSpeed)))
|
||||
bits |= gBitTable[i];
|
||||
bits |= 1u << i;
|
||||
|
||||
// Check for mon with resistance and super effective move for GetBestMonTypeMatchup
|
||||
if (aiMove != MOVE_NONE && gMovesInfo[aiMove].power != 0)
|
||||
@@ -1998,7 +2002,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
||||
}
|
||||
// If ace mon is the last available Pokemon and U-Turn/Volt Switch was used - switch to the mon.
|
||||
if (aceMonId != PARTY_SIZE
|
||||
&& (gMovesInfo[gLastUsedMove].effect == EFFECT_HIT_ESCAPE || gMovesInfo[gLastUsedMove].effect == EFFECT_PARTING_SHOT || gMovesInfo[gLastUsedMove].effect == EFFECT_BATON_PASS))
|
||||
&& (gMovesInfo[gLastUsedMove].effect == EFFECT_HIT_ESCAPE || gMovesInfo[gLastUsedMove].effect == EFFECT_PARTING_SHOT || gMovesInfo[gLastUsedMove].effect == EFFECT_BATON_PASS || gMovesInfo[gLastUsedMove].effect == EFFECT_CHILLY_RECEPTION))
|
||||
return aceMonId;
|
||||
|
||||
return PARTY_SIZE;
|
||||
@@ -2035,17 +2039,19 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd)
|
||||
|
||||
if (*(gBattleStruct->monToSwitchIntoId + battler) != PARTY_SIZE)
|
||||
return *(gBattleStruct->monToSwitchIntoId + battler);
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
return gBattlerPartyIndexes[battler] + 1;
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))])
|
||||
if (gAbsentBattlerFlags & (1u << GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))))
|
||||
battlerIn2 = battler;
|
||||
else
|
||||
battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)));
|
||||
|
||||
opposingBattler = BATTLE_OPPOSITE(battlerIn1);
|
||||
if (gAbsentBattlerFlags & gBitTable[opposingBattler])
|
||||
if (gAbsentBattlerFlags & (1u << opposingBattler))
|
||||
opposingBattler ^= BIT_FLANK;
|
||||
}
|
||||
else
|
||||
@@ -2070,7 +2076,7 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd)
|
||||
|
||||
// Split ideal mon decision between after previous mon KO'd (prioritize offensive options) and after switching active mon out (prioritize defensive options), and expand the scope of both.
|
||||
// Only use better mon selection if AI_FLAG_SMART_MON_CHOICES is set for the trainer.
|
||||
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_MON_CHOICES && !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) // Double Battles aren't included in AI_FLAG_SMART_MON_CHOICE. Defaults to regular switch in logic
|
||||
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_MON_CHOICES && !IsDoubleBattle()) // Double Battles aren't included in AI_FLAG_SMART_MON_CHOICE. Defaults to regular switch in logic
|
||||
{
|
||||
bestMonId = GetBestMonIntegrated(party, firstId, lastId, battler, opposingBattler, battlerIn1, battlerIn2, switchAfterMonKOd);
|
||||
return bestMonId;
|
||||
@@ -2091,12 +2097,12 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd)
|
||||
|| i == gBattleStruct->monToSwitchIntoId[battlerIn2]
|
||||
|| (GetMonAbility(&party[i]) == ABILITY_TRUANT && IsTruantMonVulnerable(battler, opposingBattler))) // While not really invalid per se, not really wise to switch into this mon.)
|
||||
{
|
||||
invalidMons |= gBitTable[i];
|
||||
invalidMons |= 1u << i;
|
||||
}
|
||||
else if (IsAceMon(battler, i))// Save Ace Pokemon for last.
|
||||
{
|
||||
aceMonId = i;
|
||||
invalidMons |= gBitTable[i];
|
||||
invalidMons |= 1u << i;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "battle_ai_util.h"
|
||||
#include "battle_ai_main.h"
|
||||
#include "battle_ai_switch_items.h"
|
||||
// #include "battle_factory.h"
|
||||
#include "battle_setup.h"
|
||||
#include "event_data.h"
|
||||
#include "data.h"
|
||||
@@ -350,12 +351,12 @@ bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, u32 category)
|
||||
if (moves[i] != MOVE_NONE
|
||||
&& moves[i] != MOVE_UNAVAILABLE
|
||||
&& GetBattleMoveCategory(moves[i]) == category
|
||||
&& !(unusable & gBitTable[i]))
|
||||
&& !(unusable & (1u << i)))
|
||||
{
|
||||
SetTypeBeforeUsingMove(moves[i], attacker);
|
||||
moveType = GetMoveType(moves[i]);
|
||||
if (CalcTypeEffectivenessMultiplier(moves[i], moveType, attacker, target, AI_DATA->abilities[target], FALSE) != 0)
|
||||
usable |= gBitTable[i];
|
||||
usable |= 1u << i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,7 +482,7 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef)
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_POLTERGEIST:
|
||||
if (AI_DATA->items[battlerDef] == ITEM_NONE)
|
||||
if (AI_DATA->items[battlerDef] == ITEM_NONE || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM || battlerDefAbility == ABILITY_KLUTZ)
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_FIRST_TURN_ONLY:
|
||||
@@ -575,13 +576,13 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
||||
if (critChanceIndex > 1) // Consider crit damage only if a move has at least +2 crit chance
|
||||
{
|
||||
s32 nonCritDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
|
||||
effectivenessMultiplier, weather, FALSE,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
effectivenessMultiplier, weather, FALSE,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
s32 critDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
|
||||
effectivenessMultiplier, weather, TRUE,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
effectivenessMultiplier, weather, TRUE,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
|
||||
u32 critOdds = GetCritHitOdds(critChanceIndex);
|
||||
// With critOdds getting closer to 1, dmg gets closer to critDmg.
|
||||
@@ -594,20 +595,33 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
||||
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]);
|
||||
effectivenessMultiplier, weather, TRUE,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
|
||||
simDamage.expected = GetDamageByRollType(critDmg, rollType);
|
||||
simDamage.minimum = LowestRollDmg(critDmg);
|
||||
}
|
||||
else
|
||||
{
|
||||
s32 nonCritDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
|
||||
effectivenessMultiplier, weather, FALSE,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
|
||||
s32 nonCritDmg = 0;
|
||||
if (gMovesInfo[move].effect == EFFECT_TRIPLE_KICK)
|
||||
{
|
||||
for (gMultiHitCounter = gMovesInfo[move].strikeCount; gMultiHitCounter > 0; gMultiHitCounter--) // The global is used to simulate actual damage done
|
||||
{
|
||||
nonCritDmg += CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
|
||||
effectivenessMultiplier, weather, FALSE,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nonCritDmg = CalculateMoveDamageVars(move, battlerAtk, battlerDef, moveType, fixedBasePower,
|
||||
effectivenessMultiplier, weather, FALSE,
|
||||
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
|
||||
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
|
||||
}
|
||||
simDamage.expected = GetDamageByRollType(nonCritDmg, rollType);
|
||||
simDamage.minimum = LowestRollDmg(nonCritDmg);
|
||||
}
|
||||
@@ -1091,7 +1105,7 @@ bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk)
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & gBitTable[i])
|
||||
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & (1u << i))
|
||||
&& AI_DATA->simulatedDmg[battlerDef][battlerAtk][i].expected >= gBattleMons[battlerAtk].hp)
|
||||
{
|
||||
return TRUE;
|
||||
@@ -1129,7 +1143,7 @@ u32 GetBestDmgMoveFromBattler(u32 battlerAtk, u32 battlerDef)
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & gBitTable[i])
|
||||
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & (1u << i))
|
||||
&& bestDmg < AI_DATA->simulatedDmg[battlerAtk][battlerDef][i].expected)
|
||||
{
|
||||
bestDmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][i].expected;
|
||||
@@ -1150,7 +1164,7 @@ u32 GetBestDmgFromBattler(u32 battler, u32 battlerTarget)
|
||||
{
|
||||
if (moves[i] != MOVE_NONE
|
||||
&& moves[i] != MOVE_UNAVAILABLE
|
||||
&& !(unusable & gBitTable[i])
|
||||
&& !(unusable & (1u << i))
|
||||
&& bestDmg < AI_DATA->simulatedDmg[battler][battlerTarget][i].expected)
|
||||
{
|
||||
bestDmg = AI_DATA->simulatedDmg[battler][battlerTarget][i].expected;
|
||||
@@ -1170,7 +1184,7 @@ bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits)
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(moveLimitations & gBitTable[i]))
|
||||
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(moveLimitations & (1u << i)))
|
||||
{
|
||||
// Use the pre-calculated value in simulatedDmg instead of re-calculating it
|
||||
dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][i].expected;
|
||||
@@ -1215,7 +1229,7 @@ bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dm
|
||||
if (dmgMod)
|
||||
dmg *= dmgMod;
|
||||
|
||||
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & gBitTable[i]) && dmg >= hpCheck)
|
||||
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & (1u << i)) && dmg >= hpCheck)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
@@ -2077,7 +2091,7 @@ bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool
|
||||
if (moves[i] == MOVE_NONE || moves[i] == MOVE_UNAVAILABLE)
|
||||
continue;
|
||||
|
||||
if (!(gBitTable[i] & moveLimitations))
|
||||
if (!((1u << i) & moveLimitations))
|
||||
{
|
||||
if (ignoreStatus && IS_MOVE_STATUS(moves[i]))
|
||||
continue;
|
||||
@@ -2103,7 +2117,7 @@ bool32 HasSleepMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef)
|
||||
{
|
||||
if (moves[i] == MOVE_NONE)
|
||||
break;
|
||||
if (!(gBitTable[i] & moveLimitations))
|
||||
if (!((1u << i) & moveLimitations))
|
||||
{
|
||||
if (gMovesInfo[moves[i]].effect == EFFECT_SLEEP
|
||||
&& AI_DATA->moveAccuracy[battlerAtk][battlerDef][i] < 85)
|
||||
@@ -2499,7 +2513,7 @@ u32 GetBattlerSecondaryDamage(u32 battlerId)
|
||||
bool32 BattlerWillFaintFromWeather(u32 battler, u32 ability)
|
||||
{
|
||||
if ((BattlerAffectedBySandstorm(battler, ability) || BattlerAffectedByHail(battler, ability))
|
||||
&& gBattleMons[battler].hp <= gBattleMons[battler].maxHP / 16)
|
||||
&& gBattleMons[battler].hp <= max(1, gBattleMons[battler].maxHP / 16))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
@@ -2508,7 +2522,7 @@ bool32 BattlerWillFaintFromWeather(u32 battler, u32 ability)
|
||||
bool32 BattlerWillFaintFromSecondaryDamage(u32 battler, u32 ability)
|
||||
{
|
||||
if (GetBattlerSecondaryDamage(battler) != 0
|
||||
&& gBattleMons[battler].hp <= gBattleMons[battler].maxHP / 16)
|
||||
&& gBattleMons[battler].hp <= max(1, gBattleMons[battler].maxHP / 16))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
@@ -2778,6 +2792,7 @@ bool32 CanKnockOffItem(u32 battler, u32 item)
|
||||
| BATTLE_TYPE_FRONTIER
|
||||
| BATTLE_TYPE_LINK
|
||||
| BATTLE_TYPE_RECORDED_LINK
|
||||
| BATTLE_TYPE_SECRET_BASE
|
||||
| (B_TRAINERS_KNOCK_OFF_ITEMS == TRUE ? BATTLE_TYPE_TRAINER : 0)
|
||||
)) && GetBattlerSide(battler) == B_SIDE_PLAYER)
|
||||
return FALSE;
|
||||
@@ -3024,7 +3039,7 @@ bool32 AnyPartyMemberStatused(u32 battlerId, bool32 checkSoundproof)
|
||||
else
|
||||
party = gEnemyParty;
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
battlerOnField1 = gBattlerPartyIndexes[battlerId];
|
||||
battlerOnField2 = gBattlerPartyIndexes[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battlerId)))];
|
||||
@@ -3247,7 +3262,8 @@ bool32 IsMoveEffectWeather(u32 move)
|
||||
|| gMovesInfo[move].effect == EFFECT_RAIN_DANCE
|
||||
|| gMovesInfo[move].effect == EFFECT_SANDSTORM
|
||||
|| gMovesInfo[move].effect == EFFECT_HAIL
|
||||
|| gMovesInfo[move].effect == EFFECT_SNOWSCAPE))
|
||||
|| gMovesInfo[move].effect == EFFECT_SNOWSCAPE
|
||||
|| gMovesInfo[move].effect == EFFECT_CHILLY_RECEPTION))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
@@ -3415,7 +3431,7 @@ s32 CountUsablePartyMons(u32 battlerId)
|
||||
else
|
||||
party = gEnemyParty;
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
battlerOnField1 = gBattlerPartyIndexes[battlerId];
|
||||
battlerOnField2 = gBattlerPartyIndexes[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battlerId)))];
|
||||
@@ -3826,7 +3842,7 @@ bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u32 move)
|
||||
bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove)
|
||||
{
|
||||
// simple logic. just upgrades chosen move to z move if possible, unless regular move would kill opponent
|
||||
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && battlerDef == BATTLE_PARTNER(battlerAtk))
|
||||
if ((IsDoubleBattle()) && battlerDef == BATTLE_PARTNER(battlerAtk))
|
||||
return FALSE; // don't use z move on partner
|
||||
if (HasTrainerUsedGimmick(battlerAtk, GIMMICK_Z_MOVE))
|
||||
return FALSE; // can't use z move twice
|
||||
|
||||
@@ -2482,11 +2482,13 @@ static void ClearSetBScriptingStruct(void)
|
||||
{
|
||||
// windowsType is set up earlier in BattleInitBgsAndWindows, so we need to save the value
|
||||
u32 temp = gBattleScripting.windowsType;
|
||||
u32 specialBattleType = gBattleScripting.specialTrainerBattleType;
|
||||
memset(&gBattleScripting, 0, sizeof(gBattleScripting));
|
||||
|
||||
gBattleScripting.windowsType = temp;
|
||||
gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle;
|
||||
gBattleScripting.expOnCatch = (B_EXP_CATCH >= GEN_6);
|
||||
gBattleScripting.specialTrainerBattleType = specialBattleType;
|
||||
}
|
||||
|
||||
static void BattleStartClearSetData(void)
|
||||
@@ -2593,8 +2595,6 @@ static void BattleStartClearSetData(void)
|
||||
gBattleStruct->itemLost[B_SIDE_PLAYER][i].originalItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM);
|
||||
gBattleStruct->itemLost[B_SIDE_OPPONENT][i].originalItem = GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM);
|
||||
gPartyCriticalHits[i] = 0;
|
||||
gBattleStruct->allowedToChangeFormInWeather[i][B_SIDE_PLAYER] = FALSE;
|
||||
gBattleStruct->allowedToChangeFormInWeather[i][B_SIDE_OPPONENT] = FALSE;
|
||||
}
|
||||
|
||||
gBattleStruct->swapDamageCategory = FALSE; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
|
||||
@@ -2697,8 +2697,9 @@ void SwitchInClearSetData(u32 battler)
|
||||
gBattleStruct->lastTakenMoveFrom[battler][1] = 0;
|
||||
gBattleStruct->lastTakenMoveFrom[battler][2] = 0;
|
||||
gBattleStruct->lastTakenMoveFrom[battler][3] = 0;
|
||||
gBattleStruct->lastMoveFailed &= ~(gBitTable[battler]);
|
||||
gBattleStruct->boosterEnergyActivates &= ~(gBitTable[battler]);
|
||||
gBattleStruct->lastMoveFailed &= ~(1u << battler);
|
||||
gBattleStruct->boosterEnergyActivates &= ~(1u << battler);
|
||||
gBattleStruct->canPickupItem &= ~(1u << battler);
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
|
||||
{
|
||||
@@ -2722,7 +2723,7 @@ void SwitchInClearSetData(u32 battler)
|
||||
// Reset damage to prevent things like red card activating if the switched-in mon is holding it
|
||||
gSpecialStatuses[battler].physicalDmg = 0;
|
||||
gSpecialStatuses[battler].specialDmg = 0;
|
||||
gBattleStruct->enduredDamage &= ~gBitTable[battler];
|
||||
gBattleStruct->enduredDamage &= ~(1u << battler);
|
||||
|
||||
// Reset G-Max Chi Strike boosts.
|
||||
gBattleStruct->bonusCritStages[battler] = 0;
|
||||
@@ -2825,7 +2826,7 @@ const u8* FaintClearSetData(u32 battler)
|
||||
gBattleStruct->lastTakenMoveFrom[battler][2] = 0;
|
||||
gBattleStruct->lastTakenMoveFrom[battler][3] = 0;
|
||||
|
||||
gBattleStruct->boosterEnergyActivates &= ~(gBitTable[battler]);
|
||||
gBattleStruct->boosterEnergyActivates &= ~(1u << battler);
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
|
||||
{
|
||||
@@ -3273,7 +3274,7 @@ static void TryDoEventsBeforeFirstTurn(void)
|
||||
struct Pokemon *party = GetBattlerParty(i);
|
||||
struct Pokemon *mon = &party[gBattlerPartyIndexes[i]];
|
||||
if (!IsBattlerAlive(i) || gBattleMons[i].species == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG))
|
||||
gAbsentBattlerFlags |= gBitTable[i];
|
||||
gAbsentBattlerFlags |= 1u << i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3406,7 +3407,7 @@ static void TryDoEventsBeforeFirstTurn(void)
|
||||
gBattleMons[i].status2 &= ~STATUS2_FLINCHED;
|
||||
// Record party slots of player's mons that appeared in battle
|
||||
if (!BattlerHasAi(i))
|
||||
gBattleStruct->appearedInBattle |= gBitTable[gBattlerPartyIndexes[i]];
|
||||
gBattleStruct->appearedInBattle |= 1u << gBattlerPartyIndexes[i];
|
||||
}
|
||||
|
||||
*(&gBattleStruct->turnEffectsTracker) = 0;
|
||||
@@ -3515,6 +3516,11 @@ void BattleTurnPassed(void)
|
||||
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
|
||||
gBattleMainFunc = HandleTurnActionSelectionState;
|
||||
|
||||
if (gSideTimers[B_SIDE_PLAYER].retaliateTimer > 0)
|
||||
gSideTimers[B_SIDE_PLAYER].retaliateTimer--;
|
||||
if (gSideTimers[B_SIDE_OPPONENT].retaliateTimer > 0)
|
||||
gSideTimers[B_SIDE_OPPONENT].retaliateTimer--;
|
||||
|
||||
if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_LAST_LOW_HP)))
|
||||
BattleScriptExecute(i == 1 ? BattleScript_TrainerASlideMsgEnd2 : BattleScript_TrainerBSlideMsgEnd2);
|
||||
else if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_LAST_HALF_HP)))
|
||||
@@ -3608,7 +3614,7 @@ void SwitchPartyOrder(u32 battler)
|
||||
partyId2 = GetPartyIdFromBattlePartyId(*(gBattleStruct->monToSwitchIntoId + battler));
|
||||
SwitchPartyMonSlots(partyId1, partyId2);
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++)
|
||||
{
|
||||
@@ -3635,6 +3641,7 @@ enum
|
||||
STATE_WAIT_ACTION_CONFIRMED,
|
||||
STATE_SELECTION_SCRIPT,
|
||||
STATE_WAIT_SET_BEFORE_ACTION,
|
||||
STATE_SELECTION_SCRIPT_MAY_RUN
|
||||
};
|
||||
|
||||
static void HandleTurnActionSelectionState(void)
|
||||
@@ -3653,7 +3660,7 @@ static void HandleTurnActionSelectionState(void)
|
||||
|
||||
// Do AI score computations here so we can use them in AI_TrySwitchOrUseItem
|
||||
if ((gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart())
|
||||
&& (BattlerHasAi(battler)))
|
||||
&& (BattlerHasAi(battler) && !(gBattleTypeFlags & BATTLE_TYPE_PALACE)))
|
||||
{
|
||||
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_RISKY) // Risky AI switches aggressively even mid battle
|
||||
AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, TRUE);
|
||||
@@ -3666,10 +3673,10 @@ static void HandleTurnActionSelectionState(void)
|
||||
*(gBattleStruct->monToSwitchIntoId + battler) = PARTY_SIZE;
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_MULTI
|
||||
|| (position & BIT_FLANK) == B_FLANK_LEFT
|
||||
|| gBattleStruct->absentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(position))]
|
||||
|| gBattleStruct->absentBattlerFlags & (1u << GetBattlerAtPosition(BATTLE_PARTNER(position)))
|
||||
|| gBattleCommunication[GetBattlerAtPosition(BATTLE_PARTNER(position))] == STATE_WAIT_ACTION_CONFIRMED)
|
||||
{
|
||||
if (gBattleStruct->absentBattlerFlags & gBitTable[battler])
|
||||
if (gBattleStruct->absentBattlerFlags & (1u << battler))
|
||||
{
|
||||
gChosenActionByBattler[battler] = B_ACTION_NOTHING_FAINTED;
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI))
|
||||
@@ -3705,7 +3712,7 @@ static void HandleTurnActionSelectionState(void)
|
||||
}
|
||||
break;
|
||||
case STATE_WAIT_ACTION_CHOSEN: // Try to perform an action.
|
||||
if (!(gBattleControllerExecFlags & ((gBitTable[battler]) | (0xF << 28) | (gBitTable[battler] << 4) | (gBitTable[battler] << 8) | (gBitTable[battler] << 12))))
|
||||
if (!(gBattleControllerExecFlags & (((1u << battler)) | (0xF << 28) | ((1u << battler) << 4) | ((1u << battler) << 8) | ((1u << battler) << 12))))
|
||||
{
|
||||
RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][1]);
|
||||
gChosenActionByBattler[battler] = gBattleResources->bufferB[battler][1];
|
||||
@@ -3748,7 +3755,7 @@ static void HandleTurnActionSelectionState(void)
|
||||
i);
|
||||
}
|
||||
|
||||
BtlController_EmitChooseMove(battler, BUFFER_A, (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) != 0, FALSE, &moveInfo);
|
||||
BtlController_EmitChooseMove(battler, BUFFER_A, IsDoubleBattle() != 0, FALSE, &moveInfo);
|
||||
MarkBattlerForControllerExec(battler);
|
||||
}
|
||||
break;
|
||||
@@ -3764,7 +3771,7 @@ static void HandleTurnActionSelectionState(void)
|
||||
}
|
||||
|
||||
if (((gBattleTypeFlags & (BATTLE_TYPE_LINK
|
||||
| BATTLE_TYPE_FRONTIER
|
||||
| BATTLE_TYPE_FRONTIER_NO_PYRAMID
|
||||
| BATTLE_TYPE_EREADER_TRAINER
|
||||
| BATTLE_TYPE_RECORDED_LINK))
|
||||
&& !gTestRunnerEnabled)
|
||||
@@ -3786,7 +3793,8 @@ static void HandleTurnActionSelectionState(void)
|
||||
break;
|
||||
case B_ACTION_SWITCH:
|
||||
*(gBattleStruct->battlerPartyIndexes + battler) = gBattlerPartyIndexes[battler];
|
||||
if (!CanBattlerEscape(battler))
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA
|
||||
|| !CanBattlerEscape(battler))
|
||||
{
|
||||
BtlController_EmitChoosePokemon(battler, BUFFER_A, PARTY_ACTION_CANT_SWITCH, PARTY_SIZE, ABILITY_NONE, gBattleStruct->battlerPartyOrders[battler]);
|
||||
}
|
||||
@@ -3850,7 +3858,7 @@ static void HandleTurnActionSelectionState(void)
|
||||
RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler))), 3);
|
||||
}
|
||||
|
||||
gBattleStruct->gimmick.toActivate &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
|
||||
gBattleStruct->gimmick.toActivate &= ~((1u << BATTLE_PARTNER(GetBattlerPosition(battler))));
|
||||
BtlController_EmitEndBounceEffect(battler, BUFFER_A);
|
||||
MarkBattlerForControllerExec(battler);
|
||||
return;
|
||||
@@ -3883,7 +3891,7 @@ static void HandleTurnActionSelectionState(void)
|
||||
}
|
||||
break;
|
||||
case STATE_WAIT_ACTION_CASE_CHOSEN:
|
||||
if (!(gBattleControllerExecFlags & ((gBitTable[battler]) | (0xF << 28) | (gBitTable[battler] << 4) | (gBitTable[battler] << 8) | (gBitTable[battler] << 12))))
|
||||
if (!(gBattleControllerExecFlags & (((1u << battler)) | (0xF << 28) | ((1u << battler) << 4) | ((1u << battler) << 8) | ((1u << battler) << 12))))
|
||||
{
|
||||
switch (gChosenActionByBattler[battler])
|
||||
{
|
||||
@@ -3921,8 +3929,12 @@ static void HandleTurnActionSelectionState(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][2]);
|
||||
RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][3]);
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_PALACE))
|
||||
{
|
||||
RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][2]);
|
||||
RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][3]);
|
||||
}
|
||||
|
||||
// Get the chosen move position (and thus the chosen move) and target from the returned buffer.
|
||||
gBattleStruct->chosenMovePositions[battler] = gBattleResources->bufferB[battler][2] & ~RET_GIMMICK;
|
||||
gChosenMoveByBattler[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
|
||||
@@ -3930,7 +3942,7 @@ static void HandleTurnActionSelectionState(void)
|
||||
|
||||
// Check to see if any gimmicks need to be prepared.
|
||||
if (gBattleResources->bufferB[battler][2] & RET_GIMMICK)
|
||||
gBattleStruct->gimmick.toActivate |= gBitTable[battler];
|
||||
gBattleStruct->gimmick.toActivate |= 1u << battler;
|
||||
|
||||
// Max Move check
|
||||
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX))
|
||||
@@ -4000,20 +4012,20 @@ static void HandleTurnActionSelectionState(void)
|
||||
}
|
||||
break;
|
||||
case STATE_WAIT_ACTION_CONFIRMED_STANDBY:
|
||||
if (!(gBattleControllerExecFlags & ((gBitTable[battler])
|
||||
if (!(gBattleControllerExecFlags & ((1u << battler)
|
||||
| (0xF << 28)
|
||||
| (gBitTable[battler] << 4)
|
||||
| (gBitTable[battler] << 8)
|
||||
| (gBitTable[battler] << 12))))
|
||||
| (1u << (battler + 4))
|
||||
| (1u << (battler + 8))
|
||||
| (1u << (battler + 12)))))
|
||||
{
|
||||
if (AllAtActionConfirmed())
|
||||
i = TRUE;
|
||||
else
|
||||
i = FALSE;
|
||||
|
||||
if (((gBattleTypeFlags & BATTLE_TYPE_MULTI) || !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||||
if (((gBattleTypeFlags & BATTLE_TYPE_MULTI) || !IsDoubleBattle())
|
||||
|| (position & BIT_FLANK) != B_FLANK_LEFT
|
||||
|| (*(&gBattleStruct->absentBattlerFlags) & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(position))]))
|
||||
|| (*(&gBattleStruct->absentBattlerFlags) & (1u << GetBattlerAtPosition(BATTLE_PARTNER(position)))))
|
||||
{
|
||||
BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_STOP_BOUNCE, i);
|
||||
}
|
||||
@@ -4026,7 +4038,7 @@ static void HandleTurnActionSelectionState(void)
|
||||
}
|
||||
break;
|
||||
case STATE_WAIT_ACTION_CONFIRMED:
|
||||
if (!(gBattleControllerExecFlags & ((gBitTable[battler]) | (0xF << 28) | (gBitTable[battler] << 4) | (gBitTable[battler] << 8) | (gBitTable[battler] << 12))))
|
||||
if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12)))))
|
||||
{
|
||||
gBattleCommunication[ACTIONS_CONFIRMED_COUNT]++;
|
||||
}
|
||||
@@ -4040,7 +4052,7 @@ static void HandleTurnActionSelectionState(void)
|
||||
{
|
||||
gBattlerAttacker = battler;
|
||||
gBattlescriptCurrInstr = gSelectionBattleScripts[battler];
|
||||
if (!(gBattleControllerExecFlags & ((gBitTable[battler]) | (0xF << 28) | (gBitTable[battler] << 4) | (gBitTable[battler] << 8) | (gBitTable[battler] << 12))))
|
||||
if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12)))))
|
||||
{
|
||||
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
|
||||
}
|
||||
@@ -4048,37 +4060,37 @@ static void HandleTurnActionSelectionState(void)
|
||||
}
|
||||
break;
|
||||
case STATE_WAIT_SET_BEFORE_ACTION:
|
||||
if (!(gBattleControllerExecFlags & ((gBitTable[battler]) | (0xF << 28) | (gBitTable[battler] << 4) | (gBitTable[battler] << 8) | (gBitTable[battler] << 12))))
|
||||
if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12)))))
|
||||
{
|
||||
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
|
||||
}
|
||||
break;
|
||||
// case STATE_SELECTION_SCRIPT_MAY_RUN:
|
||||
// if (*(gBattleStruct->selectionScriptFinished + battler))
|
||||
// {
|
||||
// if (gBattleResources->bufferB[battler][1] == B_ACTION_NOTHING_FAINTED)
|
||||
// {
|
||||
// gHitMarker |= HITMARKER_RUN;
|
||||
// gChosenActionByBattler[battler] = B_ACTION_RUN;
|
||||
// gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// RecordedBattle_ClearBattlerAction(battler, 1);
|
||||
// gBattleCommunication[battler] = *(gBattleStruct->stateIdAfterSelScript + battler);
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// gBattlerAttacker = battler;
|
||||
// gBattlescriptCurrInstr = gSelectionBattleScripts[battler];
|
||||
// if (!(gBattleControllerExecFlags & ((gBitTable[battler]) | (0xF << 28) | (gBitTable[battler] << 4) | (gBitTable[battler] << 8) | (gBitTable[battler] << 12))))
|
||||
// {
|
||||
// gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
|
||||
// }
|
||||
// gSelectionBattleScripts[battler] = gBattlescriptCurrInstr;
|
||||
// }
|
||||
// break;
|
||||
case STATE_SELECTION_SCRIPT_MAY_RUN:
|
||||
if (*(gBattleStruct->selectionScriptFinished + battler))
|
||||
{
|
||||
if (gBattleResources->bufferB[battler][1] == B_ACTION_NOTHING_FAINTED)
|
||||
{
|
||||
gHitMarker |= HITMARKER_RUN;
|
||||
gChosenActionByBattler[battler] = B_ACTION_RUN;
|
||||
gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
|
||||
}
|
||||
else
|
||||
{
|
||||
RecordedBattle_ClearBattlerAction(battler, 1);
|
||||
gBattleCommunication[battler] = *(gBattleStruct->stateIdAfterSelScript + battler);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattlerAttacker = battler;
|
||||
gBattlescriptCurrInstr = gSelectionBattleScripts[battler];
|
||||
if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12)))))
|
||||
{
|
||||
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
|
||||
}
|
||||
gSelectionBattleScripts[battler] = gBattlescriptCurrInstr;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4158,7 +4170,6 @@ void SwapTurnOrder(u8 id1, u8 id2)
|
||||
u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect)
|
||||
{
|
||||
u32 speed = gBattleMons[battler].speed;
|
||||
u32 highestStat = GetHighestStatId(battler);
|
||||
|
||||
// weather abilities
|
||||
if (WEATHER_HAS_EFFECT)
|
||||
@@ -4180,10 +4191,10 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect)
|
||||
speed *= 2;
|
||||
else if (ability == ABILITY_SLOW_START && gDisableStructs[battler].slowStartTimer != 0)
|
||||
speed /= 2;
|
||||
else if (ability == ABILITY_PROTOSYNTHESIS && gBattleWeather & B_WEATHER_SUN && highestStat == STAT_SPEED)
|
||||
speed = (speed * 150) / 100;
|
||||
else if (ability == ABILITY_QUARK_DRIVE && gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && highestStat == STAT_SPEED)
|
||||
speed = (speed * 150) / 100;
|
||||
else if (ability == ABILITY_PROTOSYNTHESIS && (gBattleWeather & B_WEATHER_SUN || gBattleStruct->boosterEnergyActivates & (1u << battler)))
|
||||
speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed;
|
||||
else if (ability == ABILITY_QUARK_DRIVE && (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gBattleStruct->boosterEnergyActivates & (1u << battler)))
|
||||
speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed;
|
||||
|
||||
// stat stages
|
||||
speed *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][0];
|
||||
@@ -4556,6 +4567,7 @@ static void TurnValuesCleanUp(bool8 var0)
|
||||
if (gDisableStructs[i].rechargeTimer == 0)
|
||||
gBattleMons[i].status2 &= ~STATUS2_RECHARGE;
|
||||
}
|
||||
gBattleStruct->canPickupItem &= ~(1u << i);
|
||||
}
|
||||
|
||||
if (gDisableStructs[i].substituteHP == 0)
|
||||
@@ -4596,10 +4608,10 @@ static bool32 TryDoGimmicksBeforeMoves(void)
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
// Search through each battler and activate their gimmick if they have one prepared.
|
||||
if ((gBattleStruct->gimmick.toActivate & gBitTable[order[i]]) && !(gProtectStructs[order[i]].noValidMoves))
|
||||
if ((gBattleStruct->gimmick.toActivate & (1u << order[i])) && !(gProtectStructs[order[i]].noValidMoves))
|
||||
{
|
||||
battler = gBattlerAttacker = gBattleScripting.battler = order[i];
|
||||
gBattleStruct->gimmick.toActivate &= ~(gBitTable[battler]);
|
||||
gBattleStruct->gimmick.toActivate &= ~(1u << battler);
|
||||
if (gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]].ActivateGimmick != NULL)
|
||||
{
|
||||
gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]].ActivateGimmick(battler);
|
||||
@@ -4625,12 +4637,12 @@ static bool32 TryDoMoveEffectsBeforeMoves(void)
|
||||
SortBattlersBySpeed(battlers, FALSE);
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (!(gBattleStruct->focusPunchBattlers & gBitTable[battlers[i]])
|
||||
if (!(gBattleStruct->focusPunchBattlers & (1u << battlers[i]))
|
||||
&& !(gBattleMons[battlers[i]].status1 & STATUS1_SLEEP)
|
||||
&& !(gDisableStructs[battlers[i]].truantCounter)
|
||||
&& !(gProtectStructs[battlers[i]].noValidMoves))
|
||||
{
|
||||
gBattleStruct->focusPunchBattlers |= gBitTable[battlers[i]];
|
||||
gBattleStruct->focusPunchBattlers |= 1u << battlers[i];
|
||||
gBattlerAttacker = battlers[i];
|
||||
switch (gChosenMoveByBattler[gBattlerAttacker])
|
||||
{
|
||||
@@ -4810,7 +4822,8 @@ static void HandleEndTurn_BattleWon(void)
|
||||
gBattlescriptCurrInstr = BattleScript_LinkBattleWonOrLost;
|
||||
gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN;
|
||||
}
|
||||
else if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER_TOWER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_BATTLE_TOWER))
|
||||
else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER
|
||||
&& gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_TRAINER_HILL | BATTLE_TYPE_EREADER_TRAINER))
|
||||
{
|
||||
BattleStopLowHpSound();
|
||||
PlayBGM(MUS_VICTORY_TRAINER);
|
||||
@@ -4820,7 +4833,8 @@ static void HandleEndTurn_BattleWon(void)
|
||||
{
|
||||
BattleStopLowHpSound();
|
||||
gBattlescriptCurrInstr = BattleScript_LocalTrainerBattleWon;
|
||||
switch (gTrainers[gTrainerBattleOpponent_A].trainerClass)
|
||||
|
||||
switch (GetTrainerClassFromId(gTrainerBattleOpponent_A))
|
||||
{
|
||||
case TRAINER_CLASS_LEADER:
|
||||
case TRAINER_CLASS_CHAMPION:
|
||||
@@ -4840,6 +4854,7 @@ static void HandleEndTurn_BattleWon(void)
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_PayDayMoneyAndPickUpItems;
|
||||
}
|
||||
|
||||
gBattleMainFunc = HandleEndTurn_FinishBattle;
|
||||
}
|
||||
|
||||
@@ -4870,6 +4885,7 @@ static void HandleEndTurn_BattleLost(void)
|
||||
}
|
||||
gBattlescriptCurrInstr = BattleScript_LocalBattleLost;
|
||||
}
|
||||
|
||||
gBattleMainFunc = HandleEndTurn_FinishBattle;
|
||||
}
|
||||
|
||||
@@ -4894,8 +4910,10 @@ static void HandleEndTurn_RanFromBattle(void)
|
||||
static void HandleEndTurn_MonFled(void)
|
||||
{
|
||||
gCurrentActionFuncId = 0;
|
||||
|
||||
PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattlerPartyIndexes[gBattlerAttacker]);
|
||||
gBattlescriptCurrInstr = BattleScript_WildMonFled;
|
||||
|
||||
gBattleMainFunc = HandleEndTurn_FinishBattle;
|
||||
}
|
||||
|
||||
@@ -5126,8 +5144,10 @@ bool32 TrySetAteType(u32 move, u32 battlerAtk, u32 attackerAbility)
|
||||
break;
|
||||
case EFFECT_HIDDEN_POWER:
|
||||
case EFFECT_WEATHER_BALL:
|
||||
case EFFECT_CHANGE_TYPE_ON_ITEM:
|
||||
case EFFECT_NATURAL_GIFT:
|
||||
case EFFECT_CHANGE_TYPE_ON_ITEM:
|
||||
case EFFECT_REVELATION_DANCE:
|
||||
case EFFECT_TERRAIN_PULSE:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -5208,16 +5228,20 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
|
||||
{
|
||||
gBattleStruct->dynamicMoveType = ItemId_GetSecondaryId(gBattleMons[battlerAtk].item) | F_DYNAMIC_TYPE_SET;
|
||||
}
|
||||
else if (gMovesInfo[move].effect == EFFECT_REVELATION_DANCE)
|
||||
else if (gMovesInfo[move].effect == EFFECT_REVELATION_DANCE && GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE)
|
||||
{
|
||||
if (GetActiveGimmick(battlerAtk) == GIMMICK_TERA && GetBattlerTeraType(battlerAtk) != TYPE_STELLAR)
|
||||
gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk);
|
||||
else if (gBattleMons[battlerAtk].types[0] != TYPE_MYSTERY)
|
||||
else if (gBattleMons[battlerAtk].types[0] != TYPE_MYSTERY && !(gBattleResources->flags->flags[battlerAtk] & RESOURCE_FLAG_ROOST && gBattleMons[battlerAtk].types[0] == TYPE_FLYING))
|
||||
gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].types[0] | F_DYNAMIC_TYPE_SET;
|
||||
else if (gBattleMons[battlerAtk].types[1] != TYPE_MYSTERY)
|
||||
else if (gBattleMons[battlerAtk].types[1] != TYPE_MYSTERY && !(gBattleResources->flags->flags[battlerAtk] & RESOURCE_FLAG_ROOST && gBattleMons[battlerAtk].types[1] == TYPE_FLYING))
|
||||
gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].types[1] | F_DYNAMIC_TYPE_SET;
|
||||
else if (gBattleResources->flags->flags[battlerAtk] & RESOURCE_FLAG_ROOST)
|
||||
gBattleStruct->dynamicMoveType = (B_ROOST_PURE_FLYING >= GEN_5 ? TYPE_NORMAL : TYPE_MYSTERY) | F_DYNAMIC_TYPE_SET;
|
||||
else if (gBattleMons[battlerAtk].types[2] != TYPE_MYSTERY)
|
||||
gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].types[2] | F_DYNAMIC_TYPE_SET;
|
||||
else
|
||||
gBattleStruct->dynamicMoveType = TYPE_MYSTERY | F_DYNAMIC_TYPE_SET;
|
||||
}
|
||||
else if (gMovesInfo[move].effect == EFFECT_RAGING_BULL
|
||||
&& (gBattleMons[battlerAtk].species == SPECIES_TAUROS_PALDEAN_COMBAT_BREED
|
||||
|
||||
@@ -973,6 +973,7 @@ const u8 gText_Unused_6F[] = _("6F");
|
||||
const u8 gText_Unused_7F[] = _("7F");
|
||||
const u8 gText_Unused_8F[] = _("8F");
|
||||
static const u8 sText_PkmnTerastallizedInto[] = _("{B_ATK_NAME_WITH_PREFIX} terastallized\ninto the {B_BUFF1} type!");
|
||||
static const u8 sText_FickleBeamDoubled[] = _("{B_ATK_NAME_WITH_PREFIX} is going all\nout for this attack!");
|
||||
|
||||
|
||||
const u8 *const gStatNamesTable[] = {
|
||||
@@ -994,7 +995,9 @@ const u8 *const gPokeblockWasTooXStringTable[] = {
|
||||
sText_PokeblockWasTooSour
|
||||
};
|
||||
|
||||
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT - BATTLESTRINGS_TABLE_START] = {
|
||||
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT - BATTLESTRINGS_TABLE_START] =
|
||||
{
|
||||
[STRINGID_FICKLEBEAMDOUBLED - BATTLESTRINGS_TABLE_START] = sText_FickleBeamDoubled,
|
||||
[STRINGID_PKMNTERASTALLIZEDINTO - BATTLESTRINGS_TABLE_START] = sText_PkmnTerastallizedInto,
|
||||
[STRINGID_TIDYINGUPCOMPLETE - BATTLESTRINGS_TABLE_START] = sText_TidyingUpComplete,
|
||||
[STRINGID_SUPERSWEETAROMAWAFTS - BATTLESTRINGS_TABLE_START] = sText_SupersweetAromaWafts,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1831,7 +1831,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
|
||||
|
||||
[EFFECT_FICKLE_BEAM] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectHit,
|
||||
.battleScript = BattleScript_EffectFickleBeam,
|
||||
},
|
||||
|
||||
[EFFECT_BLIZZARD] =
|
||||
|
||||
@@ -930,7 +930,7 @@ static void Task_EvolutionScene(u8 taskId)
|
||||
BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO);
|
||||
gTasks[taskId].tLearnMoveState++;
|
||||
sEvoCursorPos = 0;
|
||||
BattleCreateYesNoCursorAt();
|
||||
BattleCreateYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||||
}
|
||||
break;
|
||||
case MVSTATE_HANDLE_YES_NO:
|
||||
@@ -941,17 +941,17 @@ static void Task_EvolutionScene(u8 taskId)
|
||||
{
|
||||
// Moved onto YES
|
||||
PlaySE(SE_SELECT);
|
||||
BattleDestroyYesNoCursorAt();
|
||||
BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||||
sEvoCursorPos = 0;
|
||||
BattleCreateYesNoCursorAt();
|
||||
BattleCreateYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||||
}
|
||||
if (JOY_NEW(DPAD_DOWN) && sEvoCursorPos == 0)
|
||||
{
|
||||
// Moved onto NO
|
||||
PlaySE(SE_SELECT);
|
||||
BattleDestroyYesNoCursorAt();
|
||||
BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||||
sEvoCursorPos = 1;
|
||||
BattleCreateYesNoCursorAt();
|
||||
BattleCreateYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||||
}
|
||||
if (JOY_NEW(A_BUTTON))
|
||||
{
|
||||
|
||||
30
src/main.c
30
src/main.c
@@ -248,12 +248,8 @@ void SetMainCallback2(MainCallback callback)
|
||||
|
||||
void StartTimer1(void)
|
||||
{
|
||||
if (HQ_RANDOM)
|
||||
{
|
||||
REG_TM2CNT_L = 0;
|
||||
REG_TM2CNT_H = TIMER_ENABLE | TIMER_COUNTUP;
|
||||
}
|
||||
|
||||
REG_TM2CNT_L = 0;
|
||||
REG_TM2CNT_H = TIMER_ENABLE | TIMER_COUNTUP;
|
||||
REG_TM1CNT_H = TIMER_ENABLE;
|
||||
}
|
||||
|
||||
@@ -261,22 +257,12 @@ void SeedRngAndSetTrainerId(void)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (HQ_RANDOM)
|
||||
{
|
||||
REG_TM1CNT_H = 0;
|
||||
REG_TM2CNT_H = 0;
|
||||
val = ((u32)REG_TM2CNT_L) << 16;
|
||||
val |= REG_TM1CNT_L;
|
||||
SeedRng(val);
|
||||
gTrainerId = Random();
|
||||
}
|
||||
else
|
||||
{
|
||||
u16 val = REG_TM1CNT_L;
|
||||
SeedRng(val);
|
||||
REG_TM1CNT_H = 0;
|
||||
gTrainerId = val;
|
||||
}
|
||||
REG_TM1CNT_H = 0;
|
||||
REG_TM2CNT_H = 0;
|
||||
val = ((u32)REG_TM2CNT_L) << 16;
|
||||
val |= REG_TM1CNT_L;
|
||||
SeedRng(val);
|
||||
gTrainerId = Random();
|
||||
}
|
||||
|
||||
u16 GetGeneratedTrainerIdLower(void)
|
||||
|
||||
34
src/random.c
34
src/random.c
@@ -8,7 +8,6 @@
|
||||
rng_value_t gRngValue;
|
||||
rng_value_t gRng2Value;
|
||||
|
||||
#if HQ_RANDOM == TRUE
|
||||
|
||||
EWRAM_DATA static volatile bool8 sRngLoopUnlocked;
|
||||
|
||||
@@ -112,39 +111,6 @@ void AdvanceRandom(void)
|
||||
|
||||
#define LOOP_RANDOM ((u16)(_SFC32_Next(state) >> 16))
|
||||
|
||||
#else
|
||||
EWRAM_DATA static u32 sRandCount = 0;
|
||||
|
||||
u16 Random(void)
|
||||
{
|
||||
gRngValue = ISO_RANDOMIZE1(gRngValue);
|
||||
sRandCount++;
|
||||
return gRngValue >> 16;
|
||||
}
|
||||
|
||||
void SeedRng(u16 seed)
|
||||
{
|
||||
gRngValue = seed;
|
||||
}
|
||||
|
||||
void SeedRng2(u16 seed)
|
||||
{
|
||||
gRng2Value = seed;
|
||||
}
|
||||
|
||||
u16 Random2(void)
|
||||
{
|
||||
gRng2Value = ISO_RANDOMIZE1(gRng2Value);
|
||||
return gRng2Value >> 16;
|
||||
}
|
||||
|
||||
#define LOOP_RANDOM_START
|
||||
#define LOOP_RANDOM_END
|
||||
|
||||
#define LOOP_RANDOM (Random())
|
||||
|
||||
#endif
|
||||
|
||||
#define SHUFFLE_IMPL \
|
||||
u32 tmp; \
|
||||
LOOP_RANDOM_START; \
|
||||
|
||||
64
test/battle/ability/adaptability.c
Normal file
64
test/battle/ability/adaptability.c
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Adaptability increases same-type attack bonus from x1.5 to x2", s16 damage)
|
||||
{
|
||||
u32 ability;
|
||||
PARAMETRIZE { ability = ABILITY_HYPER_CUTTER; }
|
||||
PARAMETRIZE { ability = ABILITY_ADAPTABILITY; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_CRAWDAUNT) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_WATER_GUN); }
|
||||
} SCENE {
|
||||
MESSAGE("Crawdaunt used Water Gun!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// The jump from 1.5x STAB to 2.0x STAB is a 1.33x boost.
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.33), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type with Adaptability gives 2.0x STAB", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = GIMMICK_NONE; }
|
||||
PARAMETRIZE { tera = GIMMICK_TERA; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_NORMAL); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_HEADBUTT, gimmick: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Crawdaunt used Headbutt!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// The jump from no STAB to 2.0x STAB is a 2.0x boost.
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type with Adaptability gives 2.25x STAB", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = GIMMICK_NONE; }
|
||||
PARAMETRIZE { tera = GIMMICK_TERA; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_WATER); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_WATER_PULSE, gimmick: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Crawdaunt used Water Pulse!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// The jump from 2x STAB to 2.25x STAB is a 1.125x boost.
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.125), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Adaptability does not affect Stellar-type moves");
|
||||
56
test/battle/ability/aerilate.c
Normal file
56
test/battle/ability/aerilate.c
Normal file
@@ -0,0 +1,56 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL);
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].power > 0);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Aerilate turns a Normal-type move into Flying-type move")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_MEGANIUM);
|
||||
OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE, gimmick: GIMMICK_MEGA); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Aerilate can not turn certain moves into Flying type moves")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_WEATHER_BALL; }
|
||||
// PARAMETRIZE { move = MOVE_NATURAL_GIFT; } TODO: handle this case via Skill Swap
|
||||
PARAMETRIZE { move = MOVE_JUDGMENT; }
|
||||
PARAMETRIZE { move = MOVE_TECHNO_BLAST; }
|
||||
PARAMETRIZE { move = MOVE_REVELATION_DANCE; }
|
||||
PARAMETRIZE { move = MOVE_MULTI_ATTACK; }
|
||||
PARAMETRIZE { move = MOVE_TERRAIN_PULSE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_MEGANIUM);
|
||||
OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, move, gimmick: GIMMICK_MEGA); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
|
||||
NONE_OF {
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Aerilate boosts power of affected moves by 20% (Gen7+)");
|
||||
TO_DO_BATTLE_TEST("Aerilate boosts power of affected moves by 30% (Gen6)");
|
||||
|
||||
// Gen 6-7
|
||||
TO_DO_BATTLE_TEST("Aerilate overrides Electrify (Gen6-7)");
|
||||
TO_DO_BATTLE_TEST("Aerilate overrides Ion Deluge (Gen6-7)");
|
||||
// Gen 8+
|
||||
//TO_DO_BATTLE_TEST("Aerilate doesn't override Electrify (Gen8+)"); // No mon with Aerilate exists in Gen8+, but probably behaves similar to Pixilate, which does.
|
||||
//TO_DO_BATTLE_TEST("Aerilate doesn't override Ion Deluge (Gen8+)"); // Ion Deluge doesn't exist in Gen 8+, but we probably could assume it behaves similar to under Electrify. TODO: Test by hacking SV.
|
||||
22
test/battle/ability/aftermath.c
Normal file
22
test/battle/ability/aftermath.c
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Aftermath damages the attacker by 1/4th of its max HP if fainted by a contact move")
|
||||
{
|
||||
s16 aftermathDamage;
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_VOLTORB) { HP(1); Ability(ABILITY_AFTERMATH); };
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN {MOVE(opponent, MOVE_TACKLE);}
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Tackle!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
MESSAGE("Voltorb fainted!");
|
||||
ABILITY_POPUP(player, ABILITY_AFTERMATH);
|
||||
HP_BAR(opponent, captureDamage: &aftermathDamage);
|
||||
} THEN {
|
||||
EXPECT_EQ(aftermathDamage, opponent->maxHP / 4);
|
||||
}
|
||||
}
|
||||
4
test/battle/ability/air_lock.c
Normal file
4
test/battle/ability/air_lock.c
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
// Tests for Air Lock are handled in test/battle/ability/cloud_nine.c
|
||||
11
test/battle/ability/analytic.c
Normal file
11
test/battle/ability/analytic.c
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Analytic increases the power of moves by 30% if it's the last one that uses its move");
|
||||
TO_DO_BATTLE_TEST("Analytic takes into account modifications to speeed an priority (Gen 5-8)"); //Eg. Paralysis, Power Weight, Stall
|
||||
TO_DO_BATTLE_TEST("Analytic does not take into account modifications to speeed an priority (Gen 8)"); //Eg. Paralysis, Power Weight, Stall
|
||||
TO_DO_BATTLE_TEST("Analytic takes into account the turn order of what fainted Pokémon would've moved");
|
||||
|
||||
// Triple Battles needed to test
|
||||
//TO_DO_BATTLE_TEST("If the Pokémon with Analytic is targeting a Pokémon in a flank position that chooses to switch with its ally in the middle, its move's power will always be normal when it attacks the Pokémon that is shifted into the flank position");
|
||||
//TO_DO_BATTLE_TEST("If the Pokémon with Analytic targets a Pokémon in the middle whose ally on a flank chooses to shift into the middle position, its move's power still depends on whether the Pokémon that was in the middle (and is now on a flank) has acted when the Pokémon with Analytic uses its move");
|
||||
@@ -37,7 +37,7 @@ SINGLE_BATTLE_TEST("Anger Point does not trigger when already at maximum Attack
|
||||
MESSAGE("Primeape cut its own HP and maximized ATTACK!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FROST_BREATH, opponent);
|
||||
MESSAGE("A critical hit!");
|
||||
NONE_OF {
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_ANGER_POINT);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Primeape's Anger Point maxed its Attack!");
|
||||
@@ -47,7 +47,9 @@ SINGLE_BATTLE_TEST("Anger Point does not trigger when already at maximum Attack
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Anger Point does not trigger when a substitute takes the hit")
|
||||
TO_DO_BATTLE_TEST("Anger Point triggers when a substitute takes the hit (Gen4)");
|
||||
|
||||
SINGLE_BATTLE_TEST("Anger Point does not trigger when a substitute takes the hit (Gen5+)")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_FROST_BREATH].alwaysCriticalHit);
|
||||
ASSUME(gMovesInfo[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE);
|
||||
@@ -62,7 +64,7 @@ SINGLE_BATTLE_TEST("Anger Point does not trigger when a substitute takes the hit
|
||||
MESSAGE("Primeape made a SUBSTITUTE!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FROST_BREATH, opponent);
|
||||
MESSAGE("A critical hit!");
|
||||
NONE_OF {
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_ANGER_POINT);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Primeape's Anger Point maxed its Attack!");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Anger Shell activates only if the target had more than 50% of its hp")
|
||||
SINGLE_BATTLE_TEST("Anger Shell activates only if the target had more than 50% of its HP")
|
||||
{
|
||||
bool32 activates = FALSE;
|
||||
u16 maxHp = 500, hp = 0;
|
||||
|
||||
24
test/battle/ability/anticipation.c
Normal file
24
test/battle/ability/anticipation.c
Normal file
@@ -0,0 +1,24 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Anticipation causes notifies if an opponent has a super-effective move");
|
||||
TO_DO_BATTLE_TEST("Anticipation causes notifies if an opponent has a One-hit KO move");
|
||||
TO_DO_BATTLE_TEST("Anticipation causes notifies if an opponent has a Self-Destruct or Explosion (Gen4)");
|
||||
TO_DO_BATTLE_TEST("Anticipation treats Self-Destruct and Explosion like all other Normal types (Gen5+)");
|
||||
|
||||
TO_DO_BATTLE_TEST("Anticipation considers Scrappy and Normalize into their effectiveness (Gen4)");
|
||||
TO_DO_BATTLE_TEST("Anticipation doesn't consider Scrappy and Normalize into their effectiveness (Gen5+)");
|
||||
TO_DO_BATTLE_TEST("Anticipation considers Gravity into their effectiveness (Gen4)");
|
||||
TO_DO_BATTLE_TEST("Anticipation doesn't consider Gravity into their effectiveness (Gen5+)");
|
||||
TO_DO_BATTLE_TEST("Anticipation doesn't trigger from Counter, Metal Burst or Mirror Coat (Gen4)");
|
||||
TO_DO_BATTLE_TEST("Anticipation counts Counter, Metal Burst or Mirror Coat as attacking moves of their types (Gen5+)");
|
||||
TO_DO_BATTLE_TEST("Anticipation considers Synchronoise as an ordinary Psychic-type move");
|
||||
TO_DO_BATTLE_TEST("Anticipation considers Freeze-Dry as an ordinary Ice-type move");
|
||||
TO_DO_BATTLE_TEST("Anticipation considers Flying Press as an ordinary Fighting-type move");
|
||||
TO_DO_BATTLE_TEST("Anticipation considers Aura Wheel as an ordinary Electric-type move");
|
||||
TO_DO_BATTLE_TEST("Anticipation considers Inverse Battle types"); //Check with Normal-type moves
|
||||
TO_DO_BATTLE_TEST("Anticipation treats dynamic move types as their base type (Normal)"); // Judgment, Weather Ball, Natural Gift, Techno Blast, Revelation Dance, Multi Attack
|
||||
TO_DO_BATTLE_TEST("Anticipation treats Hidden Power as Normal Type (Gen4-5)");
|
||||
TO_DO_BATTLE_TEST("Anticipation treats Hidden Power as its dynamic type (Gen6+)");
|
||||
TO_DO_BATTLE_TEST("Anticipation does not consider Strong Winds on type matchups");
|
||||
TO_DO_BATTLE_TEST("Anticipation does not consider ate-abilities");
|
||||
11
test/battle/ability/arena_trap.c
Normal file
11
test/battle/ability/arena_trap.c
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Arena Trap prevents grounded adjacent opponents from switching out");
|
||||
TO_DO_BATTLE_TEST("Arena Trap doesn't prevent switch outs if the Pokémon is switched in the same turn the opponent decided to switch out");
|
||||
TO_DO_BATTLE_TEST("Arena Trap doesn't prevent switch outs via moves that switch out"); // Baton Pass, U-Turn, Volt Switch, Flip Turn, Parting Shot
|
||||
TO_DO_BATTLE_TEST("Arena Trap doesn't prevent switch outs via Shed Shell, but not via Teleport");
|
||||
TO_DO_BATTLE_TEST("Arena Trap doesn't prevent switch outs via Run Away");
|
||||
TO_DO_BATTLE_TEST("Arena Trap doesn't prevent switch outs via Smoke Ball");
|
||||
TO_DO_BATTLE_TEST("Arena Trap prevents switch outs from Ghost-type Pokémon (Gen3-5)");
|
||||
TO_DO_BATTLE_TEST("Arena Trap doesn't prevent switch outs from Ghost-type Pokémon (Gen6+)");
|
||||
15
test/battle/ability/aroma_veil.c
Normal file
15
test/battle/ability/aroma_veil.c
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Taunt");
|
||||
TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Torment");
|
||||
TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Encore");
|
||||
TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Disable");
|
||||
TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Cursed Body");
|
||||
TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Heal Block");
|
||||
TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Infatuation");
|
||||
TO_DO_BATTLE_TEST("Aroma Veil does not protect the Pokémon's side from Imprison");
|
||||
|
||||
// Marked in Bulbapedia as need of research
|
||||
//TO_DO_BATTLE_TEST("Aroma Veil prevents G-Max Meltdown's effect");
|
||||
//TO_DO_BATTLE_TEST("Aroma Veil prevents Psychic Noise's effect");
|
||||
7
test/battle/ability/as_one.c
Normal file
7
test/battle/ability/as_one.c
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
// Tests for the individual ability effects are handled in the following times:
|
||||
// - Unnerve: test/battle/ability/unnerve.c
|
||||
// - Chilling Neigh: test/battle/ability/chilling_neigh.c
|
||||
// - Grim Neigh: test/battle/ability/grim_neigh.c
|
||||
6
test/battle/ability/aura_break.c
Normal file
6
test/battle/ability/aura_break.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Aura Break inverts Fairy Aura's effect");
|
||||
TO_DO_BATTLE_TEST("Aura Break inverts Dark Aura's effect");
|
||||
TO_DO_BATTLE_TEST("Aura Break ignores Mold Breaker abilities");
|
||||
4
test/battle/ability/chilling_neigh.c
Normal file
4
test/battle/ability/chilling_neigh.c
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
// Tests for Chilling Neigh are handled in test/battle/ability/moxie.c
|
||||
6
test/battle/ability/chlorophyll.c
Normal file
6
test/battle/ability/chlorophyll.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Chlorophyll doubles speed if it's sunny");
|
||||
TO_DO_BATTLE_TEST("Chlorophyll doesn't double speed if Cloud Nine/Air Lock is on the field");
|
||||
TO_DO_BATTLE_TEST("Chlorophyll doesn't double speed if they have an Utility Umbrella");
|
||||
@@ -127,7 +127,7 @@ SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent s
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPERPOWER, opponent);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(opponent, ability);
|
||||
ABILITY_POPUP(opponent, ability);
|
||||
MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!");
|
||||
MESSAGE("Foe Torkoal's White Smoke prevents stat loss!");
|
||||
MESSAGE("Foe Metang's Clear Body prevents stat loss!");
|
||||
@@ -186,7 +186,7 @@ SINGLE_BATTLE_TEST("Mold Breaker, Teravolt, and Turboblaze ignore Clear Body and
|
||||
else{
|
||||
ANIMATION(ANIM_TYPE_MOVE, move, player);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(opponent, ability);
|
||||
ABILITY_POPUP(opponent, ability);
|
||||
MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!");
|
||||
MESSAGE("Foe Torkoal's White Smoke prevents stat loss!");
|
||||
MESSAGE("Foe Metang's Clear Body prevents stat loss!");
|
||||
|
||||
@@ -1,16 +1,41 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Cloud Nine prevents weather effects")
|
||||
SINGLE_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Sandstorm")
|
||||
{
|
||||
u32 species = 0, ability = 0;
|
||||
PARAMETRIZE { species = SPECIES_PSYDUCK; ability = ABILITY_CLOUD_NINE; }
|
||||
PARAMETRIZE { species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_SANDSTORM].effect == EFFECT_SANDSTORM);
|
||||
PLAYER(SPECIES_PSYDUCK) { Ability(ABILITY_CLOUD_NINE); }
|
||||
PLAYER(species) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SANDSTORM); }
|
||||
TURN {}
|
||||
} SCENE {
|
||||
NONE_OF { HP_BAR(player); }
|
||||
ABILITY_POPUP(player, ability);
|
||||
MESSAGE("The effects of weather disappeared.");
|
||||
MESSAGE("Foe Wobbuffet used Sandstorm!");
|
||||
MESSAGE("The sandstorm rages.");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SANDSTORM_CONTINUES);
|
||||
NONE_OF {
|
||||
HP_BAR(player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("Foe Wobbuffet is buffeted by the sandstorm!");
|
||||
}
|
||||
MESSAGE("The sandstorm rages.");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SANDSTORM_CONTINUES);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Sun");
|
||||
TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Rain");
|
||||
TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Hail");
|
||||
TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Snow");
|
||||
TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Fog");
|
||||
TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Primal Sun");
|
||||
TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Primal Rain");
|
||||
TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Strong Winds");
|
||||
|
||||
// Moves and abilities that are affected by weather should have new tests that check for Clould Nine/Air Lock, like Mold-Breaker Abilities
|
||||
|
||||
@@ -57,7 +57,7 @@ SINGLE_BATTLE_TEST("Damp prevents explosion-like moves from self")
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Damp prevents damage from aftermath")
|
||||
SINGLE_BATTLE_TEST("Damp prevents damage from Aftermath")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].makesContact);
|
||||
|
||||
@@ -126,3 +126,22 @@ DOUBLE_BATTLE_TEST("Dancer still triggers if another dancer flinches")
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Dancer-called attacks have their type updated")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_REVELATION_DANCE].danceMove == TRUE);
|
||||
ASSUME(gMovesInfo[MOVE_REVELATION_DANCE].effect == EFFECT_REVELATION_DANCE);
|
||||
PLAYER(SPECIES_TANGROWTH);
|
||||
OPPONENT(SPECIES_ORICORIO_BAILE);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_REVELATION_DANCE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, player);
|
||||
MESSAGE("It's not very effective…");
|
||||
ABILITY_POPUP(opponent, ABILITY_DANCER);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, opponent);
|
||||
NOT MESSAGE("It's not very effective…");
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,3 +134,42 @@ SINGLE_BATTLE_TEST("Disguised Mimikyu is ignored by Mold Breaker")
|
||||
NOT ABILITY_POPUP(player, ABILITY_DISGUISE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Disguised Mimikyu's types revert back to Ghost/Fairy when Disguise is broken")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_SHADOW_CLAW].type == TYPE_GHOST);
|
||||
PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SOAK); }
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { MOVE(opponent, MOVE_SHADOW_CLAW); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Wobbuffet used Soak!");
|
||||
MESSAGE("Mimikyu transformed into the Water type!");
|
||||
MESSAGE("Foe Wobbuffet used Tackle!");
|
||||
ABILITY_POPUP(player, ABILITY_DISGUISE);
|
||||
MESSAGE("Foe Wobbuffet used Shadow Claw!");
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Disguised Mimikyu blocks a move after getting Gastro Acid Batton Passed")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_MIMIKYU_DISGUISED) { Ability(ABILITY_DISGUISE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_GASTRO_ACID); MOVE(player, MOVE_BATON_PASS); SEND_OUT(player, 1); }
|
||||
TURN { MOVE(opponent, MOVE_SHADOW_CLAW); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_GASTRO_ACID, opponent);
|
||||
MESSAGE("Wobbuffet's ability was suppressed!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BATON_PASS, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHADOW_CLAW, opponent);
|
||||
ABILITY_POPUP(player, ABILITY_DISGUISE);
|
||||
}
|
||||
}
|
||||
|
||||
5
test/battle/ability/drought.c
Normal file
5
test/battle/ability/drought.c
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Drought sets up sun for 5 turns (Gen6+)");
|
||||
TO_DO_BATTLE_TEST("Drought sets up permanent sun (Gen3-5)");
|
||||
@@ -15,6 +15,8 @@ SINGLE_BATTLE_TEST("Dry Skin causes 1/8th Max HP damage in Sun")
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Dry Skin doesn't get damaged in Sun if Cloud Nine/Air Lock is on the field");
|
||||
|
||||
SINGLE_BATTLE_TEST("Dry Skin heals 1/8th Max HP in Rain")
|
||||
{
|
||||
GIVEN {
|
||||
@@ -29,6 +31,8 @@ SINGLE_BATTLE_TEST("Dry Skin heals 1/8th Max HP in Rain")
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Dry Skin doesn't heal in Rain if Cloud Nine/Air Lock is on the field");
|
||||
|
||||
SINGLE_BATTLE_TEST("Dry Skin increases damage taken from Fire-type moves by 25%", s16 damage)
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
@@ -17,6 +17,8 @@ SINGLE_BATTLE_TEST("Flower Gift transforms Cherrim in harsh sunlight")
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Flower Gift doesn't transform Cherrim if Cloud Nine/Air Lock is on the field");
|
||||
|
||||
SINGLE_BATTLE_TEST("Flower Gift transforms Cherrim back to normal when weather changes")
|
||||
{
|
||||
GIVEN {
|
||||
@@ -61,6 +63,8 @@ SINGLE_BATTLE_TEST("Flower Gift transforms Cherrim back to normal when its abili
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Forecast transforms Castform back to normal under Cloud Nine/Air Lock");
|
||||
|
||||
DOUBLE_BATTLE_TEST("Flower Gift increases the attack of Cherrim and its allies by 1.5x", s16 damageL, s16 damageR)
|
||||
{
|
||||
bool32 sunny;
|
||||
|
||||
@@ -264,12 +264,15 @@ SINGLE_BATTLE_TEST("Forecast transforms Castform back to normal when Sandstorm i
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Forecast transforms Castform back to normal under Air Lock")
|
||||
SINGLE_BATTLE_TEST("Forecast transforms Castform back to normal under Cloud Nine/Air Lock")
|
||||
{
|
||||
u32 species = 0, ability = 0;
|
||||
PARAMETRIZE { species = SPECIES_PSYDUCK; ability = ABILITY_CLOUD_NINE; }
|
||||
PARAMETRIZE { species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_CASTFORM_NORMAL) { Ability(ABILITY_FORECAST); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_RAYQUAZA);
|
||||
OPPONENT(species) { Ability(ability); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_RAIN_DANCE); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
@@ -279,7 +282,7 @@ SINGLE_BATTLE_TEST("Forecast transforms Castform back to normal under Air Lock")
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
|
||||
MESSAGE("Castform transformed!");
|
||||
// back to normal
|
||||
ABILITY_POPUP(opponent, ABILITY_AIR_LOCK);
|
||||
ABILITY_POPUP(opponent, ability);
|
||||
ABILITY_POPUP(player, ABILITY_FORECAST);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
|
||||
MESSAGE("Castform transformed!");
|
||||
@@ -394,3 +397,25 @@ SINGLE_BATTLE_TEST("Forecast transforms Castform back when it uses a move that f
|
||||
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_CASTFORM);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Forecast transforms Castform when Cloud Nine ability user leaves the field")
|
||||
{
|
||||
u32 species = 0, ability = 0;
|
||||
PARAMETRIZE { species = SPECIES_PSYDUCK; ability = ABILITY_CLOUD_NINE; }
|
||||
PARAMETRIZE { species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_CASTFORM) { Ability(ABILITY_FORECAST); }
|
||||
OPPONENT(species) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
|
||||
MESSAGE("2 sent out Wobbuffet!");
|
||||
ABILITY_POPUP(player, ABILITY_FORECAST);
|
||||
MESSAGE("Castform transformed!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,19 @@ ASSUMPTIONS
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].power > 0);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Galvanize turns a normal type move into Electric")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_KRABBY);
|
||||
OPPONENT(SPECIES_GEODUDE_ALOLAN) { Ability(ABILITY_GALVANIZE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Galvanize can not turn certain moves into Electric type moves")
|
||||
{
|
||||
u32 move;
|
||||
@@ -29,56 +42,7 @@ SINGLE_BATTLE_TEST("Galvanize can not turn certain moves into Electric type move
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Galvanize turns a normal type move into Electric")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_KRABBY);
|
||||
OPPONENT(SPECIES_GEODUDE_ALOLAN) { Ability(ABILITY_GALVANIZE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pixilate turns a normal type move into Fairy")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_DRAGONITE);
|
||||
OPPONENT(SPECIES_ALTARIA) { Item(ITEM_ALTARIANITE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE, gimmick: GIMMICK_MEGA); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Refrigerate turns a normal type move into Ice")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_MEGANIUM);
|
||||
OPPONENT(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Aerilate turns a normal type move into Flying")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_MEGANIUM);
|
||||
OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE, gimmick: GIMMICK_MEGA); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
TO_DO_BATTLE_TEST("Galvanize boosts power of affected moves by 20% (Gen7+)");
|
||||
TO_DO_BATTLE_TEST("Galvanize boosts power of affected moves by 30% (Gen6)");
|
||||
TO_DO_BATTLE_TEST("(DYNAMAX) Galvanize turns Max Strike into Max Lightning when not used by Gigantamax Pikachu/Toxtricity");
|
||||
//TO_DO_BATTLE_TEST("(DYNAMAX) Galvanize doesn't turn Max Strike into Max Lightning when used by Gigantamax Pikachu/Toxtricity, instead becoming G-Max Volt Crash/Stun Shock"); // Marked in Bulbapedia as "needs research", so this assumes that it behaves like Pixilate.
|
||||
109
test/battle/ability/grim_neigh.c
Normal file
109
test/battle/ability/grim_neigh.c
Normal file
@@ -0,0 +1,109 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
DOUBLE_BATTLE_TEST("Grim Neigh raises Sp. Attack by one stage after directly causing a Pokemon to faint")
|
||||
{
|
||||
u32 species = 0, ability = 0, abilityPopUp = 0;
|
||||
PARAMETRIZE { species = SPECIES_SPECTRIER; ability = ABILITY_GRIM_NEIGH; abilityPopUp = ABILITY_GRIM_NEIGH; }
|
||||
PARAMETRIZE { species = SPECIES_CALYREX_SHADOW_RIDER; ability = ABILITY_AS_ONE_SHADOW_RIDER; abilityPopUp = ABILITY_GRIM_NEIGH; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_DISCHARGE].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
PLAYER(species) { Ability(ability); }
|
||||
PLAYER(SPECIES_SNORUNT) { HP(1); }
|
||||
OPPONENT(SPECIES_GLALIE) { HP(1); }
|
||||
OPPONENT(SPECIES_ABRA) { HP(1); }
|
||||
OPPONENT(SPECIES_ABRA);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_DISCHARGE); SEND_OUT(opponentLeft, 2); }
|
||||
} SCENE {
|
||||
int i;
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DISCHARGE, playerLeft);
|
||||
for (i = 0; i < 3; i++) {
|
||||
ONE_OF {
|
||||
MESSAGE("Snorunt fainted!");
|
||||
MESSAGE("Foe Glalie fainted!");
|
||||
MESSAGE("Foe Abra fainted!");
|
||||
}
|
||||
ABILITY_POPUP(playerLeft, abilityPopUp);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
|
||||
if (species == SPECIES_SPECTRIER)
|
||||
MESSAGE("Spectrier's Grim Neigh raised its Sp. Atk!");
|
||||
else
|
||||
MESSAGE("Calyrex's Grim Neigh raised its Sp. Atk!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 3);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Grim Neigh does not trigger if Pokemon faint to indirect damage or damage from other Pokemon")
|
||||
{
|
||||
u32 species = 0, ability = 0, abilityPopUp = 0;
|
||||
PARAMETRIZE { species = SPECIES_SPECTRIER; ability = ABILITY_GRIM_NEIGH; abilityPopUp = ABILITY_GRIM_NEIGH; }
|
||||
PARAMETRIZE { species = SPECIES_CALYREX_SHADOW_RIDER; ability = ABILITY_AS_ONE_SHADOW_RIDER; abilityPopUp = ABILITY_GRIM_NEIGH; }
|
||||
GIVEN {
|
||||
PLAYER(species) { Ability(ability); }
|
||||
PLAYER(SPECIES_SNORUNT) { HP(1); Status1(STATUS1_POISON); }
|
||||
OPPONENT(SPECIES_GLALIE) { HP(1); Status1(STATUS1_BURN); }
|
||||
OPPONENT(SPECIES_ABRA) { HP(1); }
|
||||
OPPONENT(SPECIES_ABRA);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerRight, MOVE_QUICK_ATTACK, target: opponentRight); SEND_OUT(opponentLeft, 2); }
|
||||
} SCENE {
|
||||
int i;
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, playerRight);
|
||||
for (i = 0; i < 3; i++) {
|
||||
ONE_OF {
|
||||
MESSAGE("Snorunt fainted!");
|
||||
MESSAGE("Foe Glalie fainted!");
|
||||
MESSAGE("Foe Abra fainted!");
|
||||
}
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(playerLeft, abilityPopUp);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
|
||||
MESSAGE("Salamence's Moxie raised its Sp. Atk!");
|
||||
MESSAGE("Spectrier's Grim Neigh raised its Sp. Atk!");
|
||||
MESSAGE("Calyrex's Grim Neigh raised its Sp. Atk!");
|
||||
}
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Grim Neigh does not increase damage done by the same move that causes another Pokemon to faint")
|
||||
{
|
||||
s16 damage[2];
|
||||
u32 species = 0, ability = 0, abilityPopUp = 0;
|
||||
PARAMETRIZE { species = SPECIES_SPECTRIER; ability = ABILITY_GRIM_NEIGH; abilityPopUp = ABILITY_GRIM_NEIGH; }
|
||||
PARAMETRIZE { species = SPECIES_CALYREX_SHADOW_RIDER; ability = ABILITY_AS_ONE_SHADOW_RIDER; abilityPopUp = ABILITY_GRIM_NEIGH; }
|
||||
|
||||
KNOWN_FAILING; // Requires simultaneous damage implementation
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_DISCHARGE].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
PLAYER(species) { Ability(ability); }
|
||||
PLAYER(SPECIES_ABRA) { HP(1); }
|
||||
OPPONENT(SPECIES_GLALIE);
|
||||
OPPONENT(SPECIES_GLALIE);
|
||||
OPPONENT(SPECIES_ABRA);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_DISCHARGE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DISCHARGE, playerLeft);
|
||||
HP_BAR(opponentLeft, captureDamage: &damage[0]);
|
||||
HP_BAR(playerRight);
|
||||
MESSAGE("Abra fainted!");
|
||||
ABILITY_POPUP(playerLeft, abilityPopUp);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
|
||||
if (species == SPECIES_SPECTRIER)
|
||||
MESSAGE("Spectrier's Grim Neigh raised its Sp. Atk!");
|
||||
else
|
||||
MESSAGE("Calyrex's Grim Neigh raised its Sp. Atk!");
|
||||
HP_BAR(opponentRight, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@ SINGLE_BATTLE_TEST("(Gulp Missile) Cramorant in Gorging paralyzes the target if
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(Gulp Missile) triggers even if the user is fainted by opposing mon")
|
||||
{
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_CRAMORANT) { HP(1); MaxHP(250); Ability(ABILITY_GULP_MISSILE); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
|
||||
@@ -1,17 +1,264 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Harvest has a 50% chance to restore a Berry at the end of the turn");
|
||||
TO_DO_BATTLE_TEST("Harvest always restores a Berry in Sunlight");
|
||||
TO_DO_BATTLE_TEST("Harvest restores a Berry even after being switched out and back in");
|
||||
TO_DO_BATTLE_TEST("Harvest restores a Berry consumed by Fling");
|
||||
TO_DO_BATTLE_TEST("Harvest restores a Berry consumed by Natural Gift");
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gItemsInfo[ITEM_SITRUS_BERRY].holdEffect == HOLD_EFFECT_RESTORE_PCT_HP);
|
||||
ASSUME(I_SITRUS_BERRY_HEAL >= GEN_4);
|
||||
ASSUME(gMovesInfo[MOVE_SUNNY_DAY].effect == EFFECT_SUNNY_DAY);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest has a 50% chance to restore a Berry at the end of the turn")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 2, RNG_HARVEST);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_SITRUS_BERRY);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest always restores a Berry in Sunlight")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 1, RNG_HARVEST);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_SUNNY_DAY); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_SITRUS_BERRY);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest doesn't always restore a Berry if Cloud Nine/Air Lock is on the field")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 2, RNG_HARVEST);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GOLDUCK) { Ability(ABILITY_CLOUD_NINE); }
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_SUNNY_DAY); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest restores a Berry even after being switched out and back in")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_PARTING_SHOT].effect == EFFECT_PARTING_SHOT);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); }
|
||||
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, MOVE_PARTING_SHOT); SEND_OUT(opponent, 0); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
|
||||
ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_SITRUS_BERRY);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest restores a Berry consumed by Fling")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_FLING].effect == EFFECT_FLING);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, MOVE_FLING); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_SITRUS_BERRY);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest restores a Berry consumed by Natural Gift")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_NATURAL_GIFT].effect == EFFECT_NATURAL_GIFT);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, MOVE_NATURAL_GIFT); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_NATURAL_GIFT, opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_SITRUS_BERRY);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Harvest only works once per turn"); // Check for berries that are consumed immediately, like Pecha Berry
|
||||
TO_DO_BATTLE_TEST("Harvest doesn't restore a Berry when destroyed by Incinerate");
|
||||
TO_DO_BATTLE_TEST("Harvest doesn't restore a Berry when knocked off by Knock Off");
|
||||
TO_DO_BATTLE_TEST("Harvest doesn't restore a Berry when eaten by Bug Bite/Pluck");
|
||||
TO_DO_BATTLE_TEST("Harvest doesn't restore a Berry that's collected via Pickup");
|
||||
TO_DO_BATTLE_TEST("Harvest order is affected by speed");
|
||||
TO_DO_BATTLE_TEST("Harvest doesn't restore a Berry when transfered to another Pokémon");
|
||||
TO_DO_BATTLE_TEST("Harvest can restore a Berry that was transferred from another Pokémon");
|
||||
TO_DO_BATTLE_TEST("Harvest can only restore the newest berry consumed that was transferred from another Pokémon instead of its original Berry");
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest doesn't restore a Berry when destroyed by Incinerate")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 1, RNG_HARVEST);
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_INCINERATE, MOVE_EFFECT_INCINERATE));
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_INCINERATE); MOVE(opponent, MOVE_SUNNY_DAY); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_INCINERATE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest doesn't restore a Berry when knocked off by Knock Off")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 1, RNG_HARVEST);
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_KNOCK_OFF, MOVE_EFFECT_KNOCK_OFF));
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_KNOCK_OFF); MOVE(opponent, MOVE_SUNNY_DAY); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest doesn't restore a Berry when eaten by Bug Bite/Pluck")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 1, RNG_HARVEST);
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_BUG_BITE, MOVE_EFFECT_BUG_BITE));
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BUG_BITE); MOVE(opponent, MOVE_SUNNY_DAY); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BUG_BITE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest doesn't restore a Berry that's collected via Pickup")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Speed(50); Ability(ABILITY_PICKUP); }
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Speed(10); Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_SUNNY_DAY); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
|
||||
ABILITY_POPUP(player, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Sitrus Berry!");
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
} THEN {
|
||||
EXPECT_EQ(player->item, ITEM_SITRUS_BERRY);
|
||||
EXPECT_EQ(opponent->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Harvest order is affected by speed")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_EXEGGUTOR) { Speed(2); Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(5); }
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Speed(10); Ability(ABILITY_HARVEST); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerRight, MOVE_BULLDOZE); MOVE(playerLeft, MOVE_SUNNY_DAY); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLDOZE, playerRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, playerLeft);
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_HARVEST);
|
||||
ABILITY_POPUP(playerLeft, ABILITY_HARVEST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponentLeft->item, ITEM_SITRUS_BERRY);
|
||||
EXPECT_EQ(playerLeft->item, ITEM_SITRUS_BERRY);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest doesn't restore a Berry when transfered to another Pokémon")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_TRICK].effect == EFFECT_TRICK);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, MOVE_TRICK); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK, opponent);
|
||||
NOT ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest can restore a Berry that was transferred from another Pokémon")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_TRICK].effect == EFFECT_TRICK);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_TORKOAL) { Ability(ABILITY_DROUGHT); Item(ITEM_SITRUS_BERRY); }
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); HP(100); MaxHP(500); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TRICK); MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
} THEN {
|
||||
EXPECT_GT(opponent->hp, opponent->maxHP / 2); // eats 2 Sitrus
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Harvest can only restore the newest berry consumed that was transferred from another Pokémon instead of its original Berry")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_TRICK].effect == EFFECT_TRICK);
|
||||
ASSUME(gItemsInfo[ITEM_APICOT_BERRY].holdEffect == HOLD_EFFECT_SP_DEFENSE_UP);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_TORKOAL) { Ability(ABILITY_DROUGHT); Item(ITEM_SITRUS_BERRY); }
|
||||
OPPONENT(SPECIES_EXEGGUTOR) { Ability(ABILITY_HARVEST); HP(100); MaxHP(500); Item(ITEM_APICOT_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TRICK); MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_HARVEST);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
} THEN {
|
||||
EXPECT_GT(opponent->hp, opponent->maxHP / 2); // eats 2 Sitrus
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,3 +14,5 @@ SINGLE_BATTLE_TEST("Hydration cures non-volatile Status conditions if it is rain
|
||||
STATUS_ICON(player, none: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Hydration doesn't cure status conditions if Cloud Nine/Air Lock is on the field");
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS {
|
||||
ASSUME(gMovesInfo[MOVE_HAIL].effect == EFFECT_HAIL);
|
||||
ASSUME(gMovesInfo[MOVE_SNOWSCAPE].effect == EFFECT_SNOWSCAPE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ice Body prevents damage from hail")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_HAIL; }
|
||||
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_GLALIE) { Ability(ABILITY_ICE_BODY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_HAIL); MOVE(opponent, MOVE_SKILL_SWAP); }
|
||||
TURN { MOVE(player, move); MOVE(opponent, MOVE_SKILL_SWAP); }
|
||||
} SCENE {
|
||||
NONE_OF { HP_BAR(player); }
|
||||
}
|
||||
@@ -15,14 +23,32 @@ SINGLE_BATTLE_TEST("Ice Body prevents damage from hail")
|
||||
|
||||
SINGLE_BATTLE_TEST("Ice Body recovers 1/16th of Max HP in hail.")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_HAIL; }
|
||||
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GLALIE) { Ability(ABILITY_ICE_BODY); HP(1); MaxHP(100); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_HAIL); }
|
||||
TURN { MOVE(opponent, move); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_ICE_BODY);
|
||||
HP_BAR(player, damage: -(100 / 16));
|
||||
MESSAGE("Glalie's Ice Body healed it a little bit!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ice Body doesn't recover HP if Cloud Nine/Air Lock is on the field")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_HAIL; }
|
||||
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GLALIE) { Ability(ABILITY_ICE_BODY); HP(1); MaxHP(100); }
|
||||
OPPONENT(SPECIES_GOLDUCK) { Ability(ABILITY_CLOUD_NINE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, move); }
|
||||
} SCENE {
|
||||
NOT ABILITY_POPUP(player, ABILITY_ICE_BODY);
|
||||
}
|
||||
}
|
||||
|
||||
137
test/battle/ability/ice_face.c
Normal file
137
test/battle/ability/ice_face.c
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Ice Face blocks physical moves, changing Eiscue into its Noice Face form")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
|
||||
PLAYER(SPECIES_EISCUE);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_ICE_FACE);
|
||||
MESSAGE("Eiscue transformed!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ice Face does not block special moves, Eiscue stays in Ice Face form")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
|
||||
ASSUME(gMovesInfo[MOVE_EMBER].category == DAMAGE_CATEGORY_SPECIAL);
|
||||
PLAYER(SPECIES_EISCUE);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_EMBER); }
|
||||
} SCENE {
|
||||
NOT ABILITY_POPUP(player, ABILITY_ICE_FACE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ice Face is restored if hail or snow begins while Noice Face Eiscue is out")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
|
||||
PARAMETRIZE { move = MOVE_HAIL; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
|
||||
ASSUME(gMovesInfo[MOVE_SNOWSCAPE].effect == EFFECT_SNOWSCAPE);
|
||||
ASSUME(gMovesInfo[MOVE_HAIL].effect == EFFECT_HAIL);
|
||||
PLAYER(SPECIES_EISCUE);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { MOVE(opponent, move); }
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_ICE_FACE);
|
||||
MESSAGE("Eiscue transformed!");
|
||||
ABILITY_POPUP(player, ABILITY_ICE_FACE);
|
||||
MESSAGE("Eiscue transformed!");
|
||||
ABILITY_POPUP(player, ABILITY_ICE_FACE);
|
||||
MESSAGE("Eiscue transformed!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ice Face is restored if Noice Face Eiscue is sent in while hail or snow is active")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
|
||||
PARAMETRIZE { move = MOVE_HAIL; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
|
||||
ASSUME(gMovesInfo[MOVE_SNOWSCAPE].effect == EFFECT_SNOWSCAPE);
|
||||
ASSUME(gMovesInfo[MOVE_HAIL].effect == EFFECT_HAIL);
|
||||
PLAYER(SPECIES_EISCUE);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { SWITCH(player, 1); MOVE(opponent, move); }
|
||||
TURN { SWITCH(player, 0); MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_ICE_FACE);
|
||||
MESSAGE("Eiscue transformed!");
|
||||
ABILITY_POPUP(player, ABILITY_ICE_FACE);
|
||||
MESSAGE("Eiscue transformed!");
|
||||
ABILITY_POPUP(player, ABILITY_ICE_FACE);
|
||||
MESSAGE("Eiscue transformed!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ice Face is not restored if Eiscue changes into Noice Face form while there's already hail or snow")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
|
||||
PARAMETRIZE { move = MOVE_HAIL; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
|
||||
ASSUME(gMovesInfo[MOVE_SNOWSCAPE].effect == EFFECT_SNOWSCAPE);
|
||||
ASSUME(gMovesInfo[MOVE_HAIL].effect == EFFECT_HAIL);
|
||||
PLAYER(SPECIES_EISCUE) { HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_ICE_FACE);
|
||||
MESSAGE("Eiscue transformed!");
|
||||
MESSAGE("Eiscue used Celebrate!");
|
||||
MESSAGE("Eiscue fainted!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ice Face form change persists after switching out")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
|
||||
PLAYER(SPECIES_EISCUE) { HP(1); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { SWITCH(player, 1); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
TURN { SWITCH(player, 0); MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_ICE_FACE);
|
||||
MESSAGE("Eiscue transformed!");
|
||||
MESSAGE("Eiscue fainted!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Ice Face doesn't transform Eiscue if Cloud Nine/Air Lock is on the field")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
|
||||
PLAYER(SPECIES_EISCUE) { HP(1); }
|
||||
OPPONENT(SPECIES_RAYQUAZA) { Ability(ABILITY_AIR_LOCK); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { MOVE(player, MOVE_SNOWSCAPE); MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_ICE_FACE);
|
||||
MESSAGE("Eiscue transformed!");
|
||||
MESSAGE("Eiscue fainted!");
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,8 @@ SINGLE_BATTLE_TEST("Leaf Guard prevents non-volatile status conditions in sun")
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Leaf Guard doesn't prevent non-volatile status conditions if Cloud Nine/Air Lock is on the field");
|
||||
|
||||
SINGLE_BATTLE_TEST("Leaf Guard prevents status conditions from Flame Orb and Toxic Orb")
|
||||
{
|
||||
u32 item;
|
||||
@@ -49,6 +51,8 @@ SINGLE_BATTLE_TEST("Leaf Guard prevents status conditions from Flame Orb and Tox
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Leaf Guard doesn't prevent status conditions from Flame Orb and Toxic Orb if Cloud Nine/Air Lock is on the field");
|
||||
|
||||
SINGLE_BATTLE_TEST("Leaf Guard prevents Rest during sun")
|
||||
{
|
||||
GIVEN {
|
||||
@@ -66,3 +70,5 @@ SINGLE_BATTLE_TEST("Leaf Guard prevents Rest during sun")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Leaf Guard doesn't prevent Rest if Cloud Nine/Air Lock is on the field");
|
||||
|
||||
@@ -13,5 +13,5 @@ SINGLE_BATTLE_TEST("Magic Guard prevents recoil damage to the user")
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_EDGE, player);
|
||||
HP_BAR(opponent);
|
||||
NOT HP_BAR(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
DOUBLE_BATTLE_TEST("Moxie raises Attack by one stage after directly causing a Pokemon to faint")
|
||||
DOUBLE_BATTLE_TEST("Moxie/Chilling Neigh raises Attack by one stage after directly causing a Pokemon to faint")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
|
||||
u32 species = 0, ability = 0, abilityPopUp = 0;
|
||||
PARAMETRIZE { species = SPECIES_SALAMENCE; ability = ABILITY_MOXIE; abilityPopUp = ABILITY_MOXIE; }
|
||||
PARAMETRIZE { species = SPECIES_GLASTRIER; ability = ABILITY_CHILLING_NEIGH; abilityPopUp = ABILITY_CHILLING_NEIGH; }
|
||||
PARAMETRIZE { species = SPECIES_CALYREX_ICE_RIDER; ability = ABILITY_AS_ONE_ICE_RIDER; abilityPopUp = ABILITY_CHILLING_NEIGH; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); }
|
||||
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
PLAYER(species) { Ability(ability); }
|
||||
PLAYER(SPECIES_SNORUNT) { HP(1); }
|
||||
OPPONENT(SPECIES_GLALIE) { HP(1); }
|
||||
OPPONENT(SPECIES_ABRA) { HP(1); }
|
||||
@@ -23,19 +26,28 @@ DOUBLE_BATTLE_TEST("Moxie raises Attack by one stage after directly causing a Po
|
||||
MESSAGE("Foe Glalie fainted!");
|
||||
MESSAGE("Foe Abra fainted!");
|
||||
}
|
||||
ABILITY_POPUP(playerLeft, ABILITY_MOXIE);
|
||||
ABILITY_POPUP(playerLeft, abilityPopUp);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
|
||||
MESSAGE("Salamence's Moxie raised its Attack!");
|
||||
if (species == SPECIES_SALAMENCE)
|
||||
MESSAGE("Salamence's Moxie raised its Attack!");
|
||||
else if (species == SPECIES_GLASTRIER)
|
||||
MESSAGE("Glastrier's Chilling Neigh raised its Attack!");
|
||||
else
|
||||
MESSAGE("Calyrex's Chilling Neigh raised its Attack!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 3);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Moxie does not trigger if Pokemon faint to indirect damage or damage from other Pokemon")
|
||||
DOUBLE_BATTLE_TEST("Moxie/Chilling Neigh does not trigger if Pokemon faint to indirect damage or damage from other Pokemon")
|
||||
{
|
||||
u32 species = 0, ability = 0, abilityPopUp = 0;
|
||||
PARAMETRIZE { species = SPECIES_SALAMENCE; ability = ABILITY_MOXIE; abilityPopUp = ABILITY_MOXIE; }
|
||||
PARAMETRIZE { species = SPECIES_GLASTRIER; ability = ABILITY_CHILLING_NEIGH; abilityPopUp = ABILITY_CHILLING_NEIGH; }
|
||||
PARAMETRIZE { species = SPECIES_CALYREX_ICE_RIDER; ability = ABILITY_AS_ONE_ICE_RIDER; abilityPopUp = ABILITY_CHILLING_NEIGH; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); }
|
||||
PLAYER(species) { Ability(ability); }
|
||||
PLAYER(SPECIES_SNORUNT) { HP(1); Status1(STATUS1_POISON); }
|
||||
OPPONENT(SPECIES_GLALIE) { HP(1); Status1(STATUS1_BURN); }
|
||||
OPPONENT(SPECIES_ABRA) { HP(1); }
|
||||
@@ -53,9 +65,11 @@ DOUBLE_BATTLE_TEST("Moxie does not trigger if Pokemon faint to indirect damage o
|
||||
MESSAGE("Foe Abra fainted!");
|
||||
}
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(playerLeft, ABILITY_MOXIE);
|
||||
ABILITY_POPUP(playerLeft, abilityPopUp);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
|
||||
MESSAGE("Salamence's Moxie raised its Attack!");
|
||||
MESSAGE("Glastrier's Chilling Neigh raised its Attack!");
|
||||
MESSAGE("Calyrex's Chilling Neigh raised its Attack!");
|
||||
}
|
||||
}
|
||||
} THEN {
|
||||
@@ -63,12 +77,15 @@ DOUBLE_BATTLE_TEST("Moxie does not trigger if Pokemon faint to indirect damage o
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Moxie does not trigger when already at maximum Attack stage")
|
||||
SINGLE_BATTLE_TEST("Moxie/Chilling Neigh does not trigger when already at maximum Attack stage")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_BELLY_DRUM].effect == EFFECT_BELLY_DRUM);
|
||||
|
||||
u32 species = 0, ability = 0, abilityPopUp = 0;
|
||||
PARAMETRIZE { species = SPECIES_SALAMENCE; ability = ABILITY_MOXIE; abilityPopUp = ABILITY_MOXIE; }
|
||||
PARAMETRIZE { species = SPECIES_GLASTRIER; ability = ABILITY_CHILLING_NEIGH; abilityPopUp = ABILITY_CHILLING_NEIGH; }
|
||||
PARAMETRIZE { species = SPECIES_CALYREX_ICE_RIDER; ability = ABILITY_AS_ONE_ICE_RIDER; abilityPopUp = ABILITY_CHILLING_NEIGH; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); }
|
||||
ASSUME(gMovesInfo[MOVE_BELLY_DRUM].effect == EFFECT_BELLY_DRUM);
|
||||
PLAYER(species) { Ability(ability); }
|
||||
OPPONENT(SPECIES_SNORUNT) { HP(1); }
|
||||
OPPONENT(SPECIES_SNORUNT);
|
||||
} WHEN {
|
||||
@@ -77,28 +94,38 @@ SINGLE_BATTLE_TEST("Moxie does not trigger when already at maximum Attack stage"
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BELLY_DRUM, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Salamence cut its own HP and maximized ATTACK!");
|
||||
if (species == SPECIES_SALAMENCE)
|
||||
MESSAGE("Salamence cut its own HP and maximized ATTACK!");
|
||||
else if (species == SPECIES_GLASTRIER)
|
||||
MESSAGE("Glastrier cut its own HP and maximized ATTACK!");
|
||||
else
|
||||
MESSAGE("Calyrex cut its own HP and maximized ATTACK!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player);
|
||||
MESSAGE("Foe Snorunt fainted!");
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_MOXIE);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, abilityPopUp);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Salamence's Moxie raised its Attack!");
|
||||
MESSAGE("Glastrier's Chilling Neigh raised its Attack!");
|
||||
MESSAGE("Calyrex's Chilling Neigh raised its Attack!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(player->statStages[STAT_ATK], MAX_STAT_STAGE);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Moxie does not increase damage done by the same move that causes another Pokemon to faint")
|
||||
DOUBLE_BATTLE_TEST("Moxie/Chilling Neigh does not increase damage done by the same move that causes another Pokemon to faint")
|
||||
{
|
||||
s16 damage[2];
|
||||
|
||||
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
u32 species = 0, ability = 0, abilityPopUp = 0;
|
||||
PARAMETRIZE { species = SPECIES_SALAMENCE; ability = ABILITY_MOXIE; abilityPopUp = ABILITY_MOXIE; }
|
||||
PARAMETRIZE { species = SPECIES_GLASTRIER; ability = ABILITY_CHILLING_NEIGH; abilityPopUp = ABILITY_CHILLING_NEIGH; }
|
||||
PARAMETRIZE { species = SPECIES_CALYREX_ICE_RIDER; ability = ABILITY_AS_ONE_ICE_RIDER; abilityPopUp = ABILITY_CHILLING_NEIGH; }
|
||||
|
||||
KNOWN_FAILING; // Requires simultaneous damage implementation
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); }
|
||||
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
|
||||
PLAYER(species) { Ability(ability); }
|
||||
PLAYER(SPECIES_ABRA) { HP(1); }
|
||||
OPPONENT(SPECIES_GLALIE);
|
||||
OPPONENT(SPECIES_GLALIE);
|
||||
@@ -110,9 +137,14 @@ DOUBLE_BATTLE_TEST("Moxie does not increase damage done by the same move that ca
|
||||
HP_BAR(opponentLeft, captureDamage: &damage[0]);
|
||||
HP_BAR(playerRight);
|
||||
MESSAGE("Abra fainted!");
|
||||
ABILITY_POPUP(playerLeft, ABILITY_MOXIE);
|
||||
ABILITY_POPUP(playerLeft, abilityPopUp);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
|
||||
MESSAGE("Salamence's Moxie raised its Attack!");
|
||||
if (species == SPECIES_SALAMENCE)
|
||||
MESSAGE("Salamence's Moxie raised its Attack!");
|
||||
else if (species == SPECIES_GLASTRIER)
|
||||
MESSAGE("Glastrier's Chilling Neigh raised its Attack!");
|
||||
else
|
||||
MESSAGE("Calyrex's Chilling Neigh raised its Attack!");
|
||||
HP_BAR(opponentRight, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
|
||||
|
||||
16
test/battle/ability/normalize.c
Normal file
16
test/battle/ability/normalize.c
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Normalize tuns a move into a Normal-type move");
|
||||
TO_DO_BATTLE_TEST("Normalize boosts power of both affected and originally Normal-type moves by 20% (Gen7+)");
|
||||
TO_DO_BATTLE_TEST("Normalize affects status moves"); // Eg. Thunder Wave can affect Ground types
|
||||
TO_DO_BATTLE_TEST("Normalize makes Flying Press do Normal/Flying damage");
|
||||
TO_DO_BATTLE_TEST("Normalize still makes Freeze-Dry do super effective damage to Water-type Pokémon");
|
||||
TO_DO_BATTLE_TEST("Normalize-affected moves become Electric-type under Electrify's effect");
|
||||
TO_DO_BATTLE_TEST("Normalize-affected moves become Electric-type under Ion Deluge's effect");
|
||||
TO_DO_BATTLE_TEST("Normalize doesn't affect Hidden Power's type");
|
||||
TO_DO_BATTLE_TEST("Normalize doesn't affect Weather Ball's type");
|
||||
TO_DO_BATTLE_TEST("Normalize doesn't affect Natural Gift's type");
|
||||
TO_DO_BATTLE_TEST("Normalize doesn't affect Judgment/Techno Blast/Multi-Attack's type");
|
||||
TO_DO_BATTLE_TEST("Normalize doesn't affect Terrain Pulse's type");
|
||||
TO_DO_BATTLE_TEST("Normalize doesn't affect damaging Z-Move types");
|
||||
5
test/battle/ability/orichalcum_pulse.c
Normal file
5
test/battle/ability/orichalcum_pulse.c
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Orichalcum Pulse sets up sun for 5 turns");
|
||||
TO_DO_BATTLE_TEST("Orichalcum Pulse boosts the Pokémon's Attack by 33% in sun, even if it's holding an Utility Umbrella");
|
||||
295
test/battle/ability/pickup.c
Normal file
295
test/battle/ability/pickup.c
Normal file
@@ -0,0 +1,295 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gItemsInfo[ITEM_SITRUS_BERRY].holdEffect == HOLD_EFFECT_RESTORE_PCT_HP);
|
||||
ASSUME(I_SITRUS_BERRY_HEAL >= GEN_4);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pickup grants an item used by another Pokémon")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
ABILITY_POPUP(player, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Sitrus Berry!");
|
||||
} THEN {
|
||||
EXPECT_EQ(player->item, ITEM_SITRUS_BERRY);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pickup doesn't grant the user their item")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); MaxHP(500); HP(251); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(opponent, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Sitrus Berry!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pickup doesn't grant another Pokémon's popped Air Balloon")
|
||||
{
|
||||
ASSUME(gItemsInfo[ITEM_AIR_BALLOON].holdEffect == HOLD_EFFECT_AIR_BALLOON);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_AIR_BALLOON); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Air Balloon!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pickup doesn't grant an item not used that turn")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
TURN { SWITCH(player, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Sitrus Berry!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(player->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pickup doesn't grant an item after its holder faints")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_MEMENTO); SEND_OUT(opponent, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Sitrus Berry!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(player->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pickup doesn't grant an used item if holder is replaced")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_PARTING_SHOT].effect == EFFECT_PARTING_SHOT);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(300); HP(151); Item(ITEM_SITRUS_BERRY); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(300); HP(151); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); }
|
||||
TURN { MOVE(player, MOVE_U_TURN); SEND_OUT(player, 1); MOVE(opponent, MOVE_PARTING_SHOT); SEND_OUT(opponent, 0); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, opponent);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Sitrus Berry!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(player->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pickup doesn't grant an item if it destroyed the item with Incinerate")
|
||||
{
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_INCINERATE, MOVE_EFFECT_INCINERATE));
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_INCINERATE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_INCINERATE, player);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Sitrus Berry!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(player->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pickup doesn't grant an item if it knocked off that item")
|
||||
{
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_KNOCK_OFF, MOVE_EFFECT_KNOCK_OFF));
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_KNOCK_OFF); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Sitrus Berry!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(player->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pickup doesn't grant an item if the user eats it with Bug Bite/Pluck")
|
||||
{
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_BUG_BITE, MOVE_EFFECT_BUG_BITE));
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BUG_BITE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BUG_BITE, player);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Sitrus Berry!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(player->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pickup doesn't grant an used item if its user already restored it")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_RECYCLE].effect == EFFECT_RECYCLE);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_RECYCLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_RECYCLE, opponent);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Sitrus Berry!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(player->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pickup restores an item that has been Flinged")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_FLING].effect == EFFECT_FLING);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_FLING); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, opponent);
|
||||
ABILITY_POPUP(player, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Sitrus Berry!");
|
||||
} THEN {
|
||||
EXPECT_EQ(player->item, ITEM_SITRUS_BERRY);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pickup restores an item that was used by Natural Gift")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_NATURAL_GIFT].effect == EFFECT_NATURAL_GIFT);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_NATURAL_GIFT); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_NATURAL_GIFT, opponent);
|
||||
ABILITY_POPUP(player, ABILITY_PICKUP);
|
||||
MESSAGE("Zigzagoon found one Sitrus Berry!");
|
||||
} THEN {
|
||||
EXPECT_EQ(player->item, ITEM_SITRUS_BERRY);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Pickup triggers based on Speed order")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Speed(1); Ability(ABILITY_PICKUP); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(2); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(3); MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Speed(50); Ability(ABILITY_PICKUP); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerLeft);
|
||||
ABILITY_POPUP(opponentRight, ABILITY_PICKUP);
|
||||
NOT ABILITY_POPUP(playerLeft, ABILITY_PICKUP);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponentRight->item, ITEM_SITRUS_BERRY);
|
||||
EXPECT_EQ(playerLeft->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Pickup grants a random item used by another Pokémon")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 3, RNG_PICKUP);
|
||||
ASSUME(gItemsInfo[ITEM_WHITE_HERB].holdEffect == HOLD_EFFECT_RESTORE_STATS);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_WHITE_HERB); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_WHITE_HERB); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_BULLDOZE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLDOZE, playerLeft);
|
||||
ABILITY_POPUP(playerLeft, ABILITY_PICKUP);
|
||||
} THEN {
|
||||
EXPECT_EQ(playerLeft->item, ITEM_SITRUS_BERRY);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Pickup doesn't trigger more than once per turn")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZIGZAGOON) { HP(1); Ability(ABILITY_PICKUP); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_BULLDOZE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLDOZE, playerLeft);
|
||||
ABILITY_POPUP(playerLeft, ABILITY_PICKUP);
|
||||
NOT ABILITY_POPUP(playerLeft, ABILITY_PICKUP);
|
||||
} THEN {
|
||||
EXPECT_EQ(playerLeft->item, ITEM_NONE);
|
||||
EXPECT_GT(playerLeft->hp, 1);
|
||||
EXPECT_LT(playerLeft->hp, playerLeft->maxHP/2);
|
||||
}
|
||||
}
|
||||
35
test/battle/ability/pixilate.c
Normal file
35
test/battle/ability/pixilate.c
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL);
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].power > 0);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pixilate turns a Normal-type move into a Fairy-type move")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_DRAGONITE);
|
||||
OPPONENT(SPECIES_ALTARIA) { Item(ITEM_ALTARIANITE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE, gimmick: GIMMICK_MEGA); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Pixilate can not turn certain moves into Fairy type moves");
|
||||
TO_DO_BATTLE_TEST("Pixilate boosts power of affected moves by 20% (Gen7+)");
|
||||
TO_DO_BATTLE_TEST("Pixilate boosts power of affected moves by 30% (Gen6)");
|
||||
TO_DO_BATTLE_TEST("(DYNAMAX) Pixilate turns Max Strike into Max Starfall when not used by Gigantamax Alcremie");
|
||||
TO_DO_BATTLE_TEST("(DYNAMAX) Pixilate doesn't turn Max Strike into Max Starfall when used by Gigantamax Alcremie, instead becoming G-Max Finale");
|
||||
|
||||
// Gen 6-7
|
||||
TO_DO_BATTLE_TEST("Pixilate overrides Electrify (Gen6-7)");
|
||||
TO_DO_BATTLE_TEST("Pixilate overrides Ion Deluge (Gen6-7)");
|
||||
// Gen 8+
|
||||
TO_DO_BATTLE_TEST("Pixilate doesn't override Electrify (Gen8+)");
|
||||
//TO_DO_BATTLE_TEST("Pixilate doesn't override Ion Deluge (Gen8+)"); // Ion Deluge doesn't exist in Gen 8+, but we probably could assume it behaves similar to under Electrify. TODO: Test by hacking SV.
|
||||
@@ -99,3 +99,7 @@ SINGLE_BATTLE_TEST("Protosynthesis activates on switch-in")
|
||||
MESSAGE("Roaring Moon's Attack was heightened!");
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Protosynthesis activates in sun before Booster Energy");
|
||||
TO_DO_BATTLE_TEST("Protosynthesis activates even if the Pokémon is holding an Utility Umbrella");
|
||||
TO_DO_BATTLE_TEST("Protosynthesis doesn't activate if Cloud Nine/Air Lock is on the field");
|
||||
|
||||
@@ -19,6 +19,24 @@ SINGLE_BATTLE_TEST("Purifying Salt halves damage from Ghost-type moves", s16 dam
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Purifying Salt halves damage from dynamic Ghost-type moves", s16 damage)
|
||||
{
|
||||
u16 ability;
|
||||
PARAMETRIZE { ability = ABILITY_STURDY; }
|
||||
PARAMETRIZE { ability = ABILITY_PURIFYING_SALT; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TERA_BLAST].effect == EFFECT_TERA_BLAST);
|
||||
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); }
|
||||
OPPONENT(SPECIES_GARGANACL) { Ability(ability); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); }
|
||||
} SCENE {
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, UQ_4_12(0.5), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Purifying Salt makes Rest fail")
|
||||
{
|
||||
GIVEN {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS {
|
||||
ASSUME(gMovesInfo[MOVE_RAIN_DANCE].effect == EFFECT_RAIN_DANCE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Rain Dish recovers 1/16th of Max HP in Rain")
|
||||
{
|
||||
GIVEN {
|
||||
@@ -11,6 +15,18 @@ SINGLE_BATTLE_TEST("Rain Dish recovers 1/16th of Max HP in Rain")
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_RAIN_DISH);
|
||||
MESSAGE("Ludicolo's Rain Dish restored its HP a little!");
|
||||
HP_BAR(player, damage: -(100 / 16));
|
||||
HP_BAR(player, damage: -(100 / 16));
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Rain Dish doesn't recover HP if Cloud Nine/Air Lock is on the field")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_LUDICOLO) { Ability(ABILITY_RAIN_DISH); HP(1); MaxHP(100); }
|
||||
OPPONENT(SPECIES_GOLDUCK) { Ability(ABILITY_CLOUD_NINE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_RAIN_DANCE); }
|
||||
} SCENE {
|
||||
NOT ABILITY_POPUP(player, ABILITY_RAIN_DISH);
|
||||
}
|
||||
}
|
||||
|
||||
34
test/battle/ability/refrigerate.c
Normal file
34
test/battle/ability/refrigerate.c
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL);
|
||||
ASSUME(gMovesInfo[MOVE_TACKLE].power > 0);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Refrigerate turns a Normal-type move into a Ice-type move")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_MEGANIUM);
|
||||
OPPONENT(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Refrigerate can not turn certain moves into Ice type moves");
|
||||
TO_DO_BATTLE_TEST("Refrigerate boosts power of affected moves by 20% (Gen7+)");
|
||||
TO_DO_BATTLE_TEST("Refrigerate boosts power of affected moves by 30% (Gen6)");
|
||||
TO_DO_BATTLE_TEST("(DYNAMAX) Refrigerate turns Max Strike into Max Hailstorm when not used by Gigantamax Lapras");
|
||||
//TO_DO_BATTLE_TEST("(DYNAMAX) Refrigerate doesn't turn Max Strike into Max Hailstorm when used by Gigantamax Lapras, instead becoming G-Max Resonance"); // Marked in Bulbapedia as "needs research", so this assumes that it behaves like Pixilate.
|
||||
|
||||
// Gen 6-7
|
||||
TO_DO_BATTLE_TEST("Refrigerate overrides Electrify (Gen6-7)");
|
||||
TO_DO_BATTLE_TEST("Refrigerate overrides Ion Deluge (Gen6-7)");
|
||||
// Gen 8+
|
||||
//TO_DO_BATTLE_TEST("Refrigerate doesn't override Electrify (Gen8+)"); // Bulbapedia doesn't list this effect, so it assumes it behaves like Pixilate.
|
||||
//TO_DO_BATTLE_TEST("Refrigerate doesn't override Ion Deluge (Gen8+)"); // Ion Deluge doesn't exist in Gen 8+, but we probably could assume it behaves similar to under Electrify. TODO: Test by hacking SV.
|
||||
6
test/battle/ability/sand_force.c
Normal file
6
test/battle/ability/sand_force.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Sand Force prevents damage from sandstorm");
|
||||
TO_DO_BATTLE_TEST("Sand Force increases the power of Rock-, Ground- and Steel-type moves by 30% in sandstorm");
|
||||
TO_DO_BATTLE_TEST("Sand Force increases move power if Cloud Nine/Air Lock is on the field");
|
||||
6
test/battle/ability/sand_rush.c
Normal file
6
test/battle/ability/sand_rush.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Sand Rush prevents damage from sandstorm");
|
||||
TO_DO_BATTLE_TEST("Sand Rush doubles speed from sandstorm");
|
||||
TO_DO_BATTLE_TEST("Sand Rush doesn't double speed if Cloud Nine/Air Lock is on the field");
|
||||
@@ -28,3 +28,6 @@ SINGLE_BATTLE_TEST("Sand Veil increases evasion during sandstorm")
|
||||
HP_BAR(player);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Sand Veil doesn't prevent Sandstorm damage if Cloud Nine/Air Lock is on the field");
|
||||
TO_DO_BATTLE_TEST("Sand Veil doesn't increase evasion if Cloud Nine/Air Lock is on the field");
|
||||
|
||||
6
test/battle/ability/slush_rush.c
Normal file
6
test/battle/ability/slush_rush.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Slush Rush doubles speed from hail");
|
||||
TO_DO_BATTLE_TEST("Slush Rush doubles speed from snow");
|
||||
TO_DO_BATTLE_TEST("Slush Rush doesn't double speed if Cloud Nine/Air Lock is on the field");
|
||||
@@ -27,3 +27,6 @@ SINGLE_BATTLE_TEST("Snow Cloak increases evasion during hail")
|
||||
HP_BAR(player);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Snow Cloak doesn't prevent hail damage if Cloud Nine/Air Lock is on the field");
|
||||
TO_DO_BATTLE_TEST("Snow Cloak doesn't increase evasion if Cloud Nine/Air Lock is on the field");
|
||||
|
||||
7
test/battle/ability/solar_power.c
Normal file
7
test/battle/ability/solar_power.c
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Solar Power increases a Sp. Attack by x1.5 in Sun");
|
||||
TO_DO_BATTLE_TEST("Solar Power doesn't increases a Sp. Attack if Cloud Nine/Air Lock is on the field");
|
||||
TO_DO_BATTLE_TEST("Solar Power causes the Pokémon to lose 1/8 max HP in Sun");
|
||||
TO_DO_BATTLE_TEST("Solar Power doesn't cause the Pokémon to lose 1/8 max HP if Cloud Nine/Air Lock is on the field");
|
||||
6
test/battle/ability/swift_swim.c
Normal file
6
test/battle/ability/swift_swim.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("Swift Swim doubles speed if it's raining");
|
||||
TO_DO_BATTLE_TEST("Swift Swim doesn't double speed if Cloud Nine/Air Lock is on the field");
|
||||
TO_DO_BATTLE_TEST("Swift Swim doesn't double speed if they have an Utility Umbrella");
|
||||
6
test/battle/ability/unnerve.c
Normal file
6
test/battle/ability/unnerve.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
// Remember to add a PARAMETRIZE for As One in the following tests:
|
||||
TO_DO_BATTLE_TEST("Unnerve prevents opposing Pokémon from eating their own berries");
|
||||
TO_DO_BATTLE_TEST("Unnerve doesn't prevent opposing Pokémon from using Natural Gift");
|
||||
127
test/battle/ability/wind_rider.c
Normal file
127
test/battle/ability/wind_rider.c
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_TAILWIND].effect == EFFECT_TAILWIND);
|
||||
ASSUME(gMovesInfo[MOVE_TAILWIND].windMove == TRUE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Wind Rider raises Attack by one stage if it sets up Tailwind")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_BRAMBLIN) { Ability(ABILITY_WIND_RIDER); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TAILWIND); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_WIND_RIDER);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Bramblin's Attack rose!");
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Wind Rider raises Attack by one stage if Tailwind is setup by its partner")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_BRAMBLIN) { Ability(ABILITY_WIND_RIDER); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_TAILWIND); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponentLeft);
|
||||
ABILITY_POPUP(opponentRight, ABILITY_WIND_RIDER);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
|
||||
MESSAGE("Foe Bramblin's Attack rose!");
|
||||
} THEN {
|
||||
EXPECT_EQ(opponentRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Wind Rider doesn't raise Attack if opponent sets up Tailwind")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_BRAMBLIN) { Ability(ABILITY_WIND_RIDER); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TAILWIND); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, player);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(opponent, ABILITY_WIND_RIDER);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Bramblin's Attack rose!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Wind Rider raises Attack by one stage if switched into Tailwind on its side of the field")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_BRAMBLIN) { Ability(ABILITY_WIND_RIDER); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TAILWIND); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_WIND_RIDER);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Bramblin's Wind Rider raised its Attack!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Wind Rider activates when it's no longer effected by Neutralizing Gas")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_BRAMBLIN) { Ability(ABILITY_WIND_RIDER); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TAILWIND); }
|
||||
TURN { SWITCH(player, 1); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS);
|
||||
MESSAGE("Neutralizing Gas filled the area!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponent);
|
||||
SWITCH_OUT_MESSAGE("Weezing");
|
||||
MESSAGE("The effects of Neutralizing Gas wore off!");
|
||||
ABILITY_POPUP(opponent, ABILITY_WIND_RIDER);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Bramblin's Wind Rider raised its Attack!");
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Wind Rider absorbs Wind moves and raises Attack by one stage")
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_GUST].windMove == TRUE);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_BRAMBLIN) { Ability(ABILITY_WIND_RIDER); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_GUST); }
|
||||
} SCENE {
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_GUST, player);
|
||||
HP_BAR(opponent);
|
||||
}
|
||||
ABILITY_POPUP(opponent, ABILITY_WIND_RIDER);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Bramblin's Attack rose!");
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will blindly Mirror Coat against specia
|
||||
ASSUME(gSpeciesInfo[SPECIES_GROVYLE].baseSpAttack == 85);
|
||||
ASSUME(gSpeciesInfo[SPECIES_GROVYLE].baseAttack == 65);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiRiskyFlag);
|
||||
PLAYER(SPECIES_GROVYLE) { Level(20); Moves(MOVE_ENERGY_BALL); }
|
||||
PLAYER(SPECIES_GROVYLE) { Level(20); Moves(MOVE_ENERGY_BALL); }
|
||||
OPPONENT(SPECIES_CASTFORM) { Level(20); Moves(MOVE_TACKLE, MOVE_MIRROR_COAT); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_ENERGY_BALL) ; EXPECT_MOVE(opponent, aiRiskyFlag ? MOVE_MIRROR_COAT : MOVE_TACKLE); }
|
||||
@@ -32,7 +32,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will blindly Counter against physical a
|
||||
ASSUME(gSpeciesInfo[SPECIES_MARSHTOMP].baseAttack == 85);
|
||||
ASSUME(gSpeciesInfo[SPECIES_MARSHTOMP].baseSpAttack == 60);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiRiskyFlag);
|
||||
PLAYER(SPECIES_MARSHTOMP) { Level(20); Moves(MOVE_WATERFALL); }
|
||||
PLAYER(SPECIES_MARSHTOMP) { Level(20); Moves(MOVE_WATERFALL); }
|
||||
OPPONENT(SPECIES_CASTFORM) { Level(20); Moves(MOVE_TACKLE, MOVE_COUNTER); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_WATERFALL) ; EXPECT_MOVE(opponent, aiRiskyFlag ? MOVE_COUNTER : MOVE_TACKLE); }
|
||||
@@ -49,7 +49,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will prioritize Revenge if slower")
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_REVENGE].effect == EFFECT_REVENGE);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiRiskyFlag);
|
||||
PLAYER(SPECIES_GROVYLE) { Level(20); Speed(4); Moves(MOVE_ENERGY_BALL); }
|
||||
PLAYER(SPECIES_GROVYLE) { Level(20); Speed(4); Moves(MOVE_ENERGY_BALL); }
|
||||
OPPONENT(SPECIES_CASTFORM) { Level(19); Speed(3); Moves(MOVE_TACKLE, MOVE_REVENGE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_ENERGY_BALL) ; EXPECT_MOVE(opponent, aiRiskyFlag ? MOVE_REVENGE : MOVE_TACKLE); }
|
||||
@@ -83,7 +83,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI prefers high damage moves at the expens
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiRiskyFlag);
|
||||
PLAYER(SPECIES_PSYDUCK) { Level(5); Moves(MOVE_TACKLE); }
|
||||
PLAYER(SPECIES_PSYDUCK) { Level(5); Moves(MOVE_TACKLE); }
|
||||
OPPONENT(SPECIES_CASTFORM) { Level(20); Moves(MOVE_THUNDER, MOVE_THUNDERBOLT); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVE(opponent, aiRiskyFlag ? MOVE_THUNDER : MOVE_THUNDERBOLT); }
|
||||
|
||||
@@ -64,7 +64,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SEQUENCE_SWITCHING: Roar and Dragon Tail still fo
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SEQUENCE_SWITCHING: AI will always switch into lowest party index after U-Turn, Parting Shot, and Baton Pass")
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SEQUENCE_SWITCHING: AI will always switch into lowest party index after U-Turn, Parting Shot, Baton Pass, and Chilly Reception")
|
||||
{
|
||||
u32 j, aiSequenceSwitchingFlag = 0, move = MOVE_NONE;
|
||||
|
||||
@@ -72,6 +72,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SEQUENCE_SWITCHING: AI will always switch into lo
|
||||
MOVE_U_TURN,
|
||||
MOVE_PARTING_SHOT,
|
||||
MOVE_BATON_PASS,
|
||||
MOVE_CHILLY_RECEPTION,
|
||||
};
|
||||
|
||||
for (j = 0; j < ARRAY_COUNT(switchMoves); j++)
|
||||
@@ -84,6 +85,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SEQUENCE_SWITCHING: AI will always switch into lo
|
||||
ASSUME(gMovesInfo[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE);
|
||||
ASSUME(gMovesInfo[MOVE_PARTING_SHOT].effect == EFFECT_PARTING_SHOT);
|
||||
ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS);
|
||||
ASSUME(gMovesInfo[MOVE_CHILLY_RECEPTION].effect == EFFECT_CHILLY_RECEPTION);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSequenceSwitchingFlag);
|
||||
PLAYER(SPECIES_SWELLOW) { Level (50); }
|
||||
OPPONENT(SPECIES_MACHOP) { Level(1); Moves(move); }
|
||||
|
||||
@@ -1234,7 +1234,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Replenish recycles allies' berries 50\% of t
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_G_MAX_REPLENISH].argument == MAX_EFFECT_RECYCLE_BERRIES);
|
||||
PLAYER(SPECIES_SNORLAX) { Item(ITEM_APICOT_BERRY); GigantamaxFactor(TRUE); }
|
||||
PLAYER(SPECIES_MUNCHLAX) { Item(ITEM_APICOT_BERRY); }
|
||||
PLAYER(SPECIES_MUNCHLAX) { Item(ITEM_APICOT_BERRY); Ability(ABILITY_THICK_FAT); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_APICOT_BERRY); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_APICOT_BERRY); }
|
||||
} WHEN {
|
||||
|
||||
@@ -86,46 +86,6 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type gives that type 2x
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type with Adaptability gives 2.0x STAB", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = GIMMICK_NONE; }
|
||||
PARAMETRIZE { tera = GIMMICK_TERA; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_NORMAL); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_HEADBUTT, gimmick: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Crawdaunt used Headbutt!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// The jump from no STAB to 2.0x STAB is a 2.0x boost.
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type with Adaptability gives 2.25x STAB", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
PARAMETRIZE { tera = GIMMICK_NONE; }
|
||||
PARAMETRIZE { tera = GIMMICK_TERA; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_WATER); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_WATER_PULSE, gimmick: tera); }
|
||||
} SCENE {
|
||||
MESSAGE("Crawdaunt used Water Pulse!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
// The jump from 2x STAB to 2.25x STAB is a 1.125x boost.
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.125), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(TERA) Terastallizing boosts moves of the same type to 60 BP", s16 damage)
|
||||
{
|
||||
bool32 tera;
|
||||
@@ -686,7 +646,7 @@ SINGLE_BATTLE_TEST("(TERA) Protean cannot change the type of a Terastallized Pok
|
||||
PLAYER(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); TeraType(TYPE_GRASS); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BUBBLE, gimmick: GIMMICK_TERA);
|
||||
TURN { MOVE(player, MOVE_BUBBLE, gimmick: GIMMICK_TERA);
|
||||
MOVE(opponent, MOVE_EMBER); }
|
||||
} SCENE {
|
||||
MESSAGE("Greninja used Bubble!");
|
||||
|
||||
@@ -10,7 +10,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z-Moves do not retain priority")
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE);
|
||||
TURN { MOVE(opponent, MOVE_TACKLE);
|
||||
MOVE(player, MOVE_QUICK_ATTACK, gimmick: GIMMICK_Z_MOVE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
@@ -597,5 +597,24 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Searing Sunraze Smash ignores the target's abilitie
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SEARING_SUNRAZE_SMASH, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("A critical hit!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(Z-MOVE) Z-Revelation Dance always transforms into Breakneck Blitz")
|
||||
{
|
||||
u16 species;
|
||||
PARAMETRIZE { species = SPECIES_ORICORIO_BAILE; }
|
||||
PARAMETRIZE { species = SPECIES_ORICORIO_PAU; }
|
||||
PARAMETRIZE { species = SPECIES_ORICORIO_POM_POM; }
|
||||
PARAMETRIZE { species = SPECIES_ORICORIO_SENSU; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_REVELATION_DANCE].type == TYPE_NORMAL);
|
||||
PLAYER(species) { Item(ITEM_NORMALIUM_Z); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_REVELATION_DANCE, gimmick: GIMMICK_Z_MOVE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ SINGLE_BATTLE_TEST("Ability Shield protects against 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);
|
||||
|
||||
@@ -52,3 +52,5 @@ SINGLE_BATTLE_TEST("Utility Umbrella blocks Rain damage modifiers", s16 damage)
|
||||
EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.5), results[3].damage);
|
||||
}
|
||||
}
|
||||
|
||||
// Moves and abilities affected by Utility Umbrella have their tests in the respective files
|
||||
|
||||
@@ -80,57 +80,57 @@ SINGLE_BATTLE_TEST("Turn order is determined randomly if priority and Speed tie
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Turn order is determined randomly if priority and Speed tie [doubles]")
|
||||
DOUBLE_BATTLE_TEST("Turn order is determined randomly if priority and Speed tie [doubles]", u32 permutations)
|
||||
{
|
||||
struct BattlePokemon *order[4] = { NULL, NULL, NULL, NULL };
|
||||
u32 a, b, c, d;
|
||||
|
||||
// TODO: Test all of these in a single PASSES_RANDOMLY pass rather
|
||||
// than 24 PARAMETRIZEd passes.
|
||||
PARAMETRIZE { a = 0; b = 1; c = 2; d = 3; }
|
||||
PARAMETRIZE { a = 0; b = 1; c = 3; d = 2; }
|
||||
PARAMETRIZE { a = 0; b = 2; c = 1; d = 3; }
|
||||
PARAMETRIZE { a = 0; b = 2; c = 3; d = 1; }
|
||||
PARAMETRIZE { a = 0; b = 3; c = 1; d = 2; }
|
||||
PARAMETRIZE { a = 0; b = 3; c = 2; d = 1; }
|
||||
PARAMETRIZE { a = 1; b = 0; c = 2; d = 3; }
|
||||
PARAMETRIZE { a = 1; b = 0; c = 3; d = 2; }
|
||||
PARAMETRIZE { a = 1; b = 2; c = 0; d = 3; }
|
||||
PARAMETRIZE { a = 1; b = 2; c = 3; d = 0; }
|
||||
PARAMETRIZE { a = 1; b = 3; c = 0; d = 2; }
|
||||
PARAMETRIZE { a = 1; b = 3; c = 2; d = 0; }
|
||||
PARAMETRIZE { a = 2; b = 0; c = 1; d = 3; }
|
||||
PARAMETRIZE { a = 2; b = 0; c = 3; d = 1; }
|
||||
PARAMETRIZE { a = 2; b = 1; c = 0; d = 3; }
|
||||
PARAMETRIZE { a = 2; b = 1; c = 3; d = 0; }
|
||||
PARAMETRIZE { a = 2; b = 3; c = 0; d = 1; }
|
||||
PARAMETRIZE { a = 2; b = 3; c = 1; d = 0; }
|
||||
PARAMETRIZE { a = 3; b = 0; c = 1; d = 2; }
|
||||
PARAMETRIZE { a = 3; b = 0; c = 2; d = 1; }
|
||||
PARAMETRIZE { a = 3; b = 1; c = 0; d = 2; }
|
||||
PARAMETRIZE { a = 3; b = 1; c = 2; d = 0; }
|
||||
PARAMETRIZE { a = 3; b = 2; c = 0; d = 1; }
|
||||
PARAMETRIZE { a = 3; b = 2; c = 1; d = 0; }
|
||||
|
||||
order[a] = playerLeft;
|
||||
order[b] = playerRight;
|
||||
order[c] = opponentLeft;
|
||||
order[d] = opponentRight;
|
||||
|
||||
PASSES_RANDOMLY(1, 24, RNG_SPEED_TIE);
|
||||
PARAMETRIZE {} // Hack to make permutations legal.
|
||||
PASSES_RANDOMLY(24, 24, RNG_SPEED_TIE);
|
||||
|
||||
ASSUME(gMovesInfo[MOVE_ENDEAVOR].effect == EFFECT_ENDEAVOR);
|
||||
ASSUME(gMovesInfo[MOVE_LIFE_DEW].effect == EFFECT_JUNGLE_HEALING);
|
||||
ASSUME(gMovesInfo[MOVE_CRUSH_GRIP].effect == EFFECT_VARY_POWER_BASED_ON_HP);
|
||||
ASSUME(gMovesInfo[MOVE_SUPER_FANG].effect == EFFECT_SUPER_FANG);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { MaxHP(480); HP(360); Defense(100); Speed(1); }
|
||||
PLAYER(SPECIES_WYNAUT) { Speed(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Attack(100); Speed(1); }
|
||||
OPPONENT(SPECIES_WYNAUT) { Speed(1); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_SPLASH); MOVE(playerRight, MOVE_SPLASH); MOVE(opponentLeft, MOVE_SPLASH); MOVE(opponentRight, MOVE_SPLASH); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, order[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, order[1]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, order[2]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, order[3]);
|
||||
TURN { MOVE(playerLeft, MOVE_ENDEAVOR, target: opponentLeft); MOVE(playerRight, MOVE_LIFE_DEW); MOVE(opponentLeft, MOVE_CRUSH_GRIP, target: playerLeft, WITH_RNG(RNG_DAMAGE_MODIFIER, 0)); MOVE(opponentRight, MOVE_SUPER_FANG, target: playerLeft); }
|
||||
} THEN {
|
||||
// This tests for unique combinatins of HP values depending on which order the moves are executed in
|
||||
// The unique outcomes arise from the specific attacks and HP, Def, and Atk values chosen
|
||||
// The switch is then set up in such a way that the only way for this test to pass exactly one is for each HP combination to occur exactly once
|
||||
#define HP_PAIR(a, b) ((a) * 1000 + (b))
|
||||
switch (HP_PAIR(playerLeft->hp, opponentLeft->hp))
|
||||
{
|
||||
case HP_PAIR(188, 360): results[i].permutations += 1 << 0; break;
|
||||
case HP_PAIR(189, 360): results[i].permutations += 1 << 1; break;
|
||||
case HP_PAIR(261, 360): results[i].permutations += 1 << 2; break;
|
||||
case HP_PAIR(235, 360): results[i].permutations += 1 << 3; break;
|
||||
case HP_PAIR(262, 360): results[i].permutations += 1 << 4; break;
|
||||
case HP_PAIR(202, 360): results[i].permutations += 1 << 5; break;
|
||||
case HP_PAIR(189, 378): results[i].permutations += 1 << 6; break;
|
||||
case HP_PAIR(189, 189): results[i].permutations += 1 << 7; break;
|
||||
case HP_PAIR(189, 480): results[i].permutations += 1 << 8; break;
|
||||
case HP_PAIR(188, 480): results[i].permutations += 1 << 9; break;
|
||||
case HP_PAIR(188, 240): results[i].permutations += 1 << 10; break;
|
||||
case HP_PAIR(188, 188): results[i].permutations += 1 << 11; break;
|
||||
case HP_PAIR(262, 262): results[i].permutations += 1 << 12; break;
|
||||
case HP_PAIR(262, 142): results[i].permutations += 1 << 13; break;
|
||||
case HP_PAIR(202, 403): results[i].permutations += 1 << 14; break;
|
||||
case HP_PAIR(202, 202): results[i].permutations += 1 << 15; break;
|
||||
case HP_PAIR(262, 283): results[i].permutations += 1 << 16; break;
|
||||
case HP_PAIR(202, 283): results[i].permutations += 1 << 17; break;
|
||||
case HP_PAIR(235, 180): results[i].permutations += 1 << 18; break;
|
||||
case HP_PAIR(261, 180): results[i].permutations += 1 << 19; break;
|
||||
case HP_PAIR(235, 235): results[i].permutations += 1 << 20; break;
|
||||
case HP_PAIR(235, 300): results[i].permutations += 1 << 21; break;
|
||||
case HP_PAIR(261, 141): results[i].permutations += 1 << 22; break;
|
||||
case HP_PAIR(261, 261): results[i].permutations += 1 << 23; break;
|
||||
}
|
||||
#undef HP_PAIR
|
||||
} FINALLY {
|
||||
EXPECT_EQ(results[i].permutations, (1 << 24) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Mist and Safeguard
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Stealth Rock and Sticky Web from player's side")
|
||||
DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Stealth Rock and Sticky Web from player's side (Gen 6+)")
|
||||
{
|
||||
u16 move;
|
||||
|
||||
@@ -154,13 +154,15 @@ DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Stealth Rock and S
|
||||
if (move == MOVE_DEFOG) {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
|
||||
MESSAGE("Foe Wobbuffet's evasiveness fell!");
|
||||
MESSAGE("The pointed stones disappeared from around your team!");
|
||||
MESSAGE("The sticky web has disappeared from the ground around your team!");
|
||||
if (B_DEFOG_EFFECT_CLEARING >= GEN_6) {
|
||||
MESSAGE("The pointed stones disappeared from around your team!");
|
||||
MESSAGE("The sticky web has disappeared from the ground around your team!");
|
||||
}
|
||||
}
|
||||
// Switch happens
|
||||
SWITCH_OUT_MESSAGE("Wobbuffet");
|
||||
SEND_IN_MESSAGE("Wobbuffet");
|
||||
if (move != MOVE_DEFOG) {
|
||||
if (move != MOVE_DEFOG || B_DEFOG_EFFECT_CLEARING <= GEN_5) {
|
||||
HP_BAR(playerLeft);
|
||||
MESSAGE("Pointed stones dug into Wobbuffet!");
|
||||
MESSAGE("Wobbuffet was caught in a Sticky Web!");
|
||||
@@ -198,12 +200,13 @@ SINGLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Spikes from player
|
||||
if (move == MOVE_DEFOG) {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("Foe Wobbuffet's evasiveness fell!");
|
||||
MESSAGE("The spikes disappeared from the ground around your team!");
|
||||
if (B_DEFOG_EFFECT_CLEARING >= GEN_6)
|
||||
MESSAGE("The spikes disappeared from the ground around your team!");
|
||||
}
|
||||
// Switch happens
|
||||
SWITCH_OUT_MESSAGE("Wobbuffet");
|
||||
SEND_IN_MESSAGE("Wobbuffet");
|
||||
if (move != MOVE_DEFOG) {
|
||||
if (move != MOVE_DEFOG || B_DEFOG_EFFECT_CLEARING <= GEN_5) {
|
||||
HP_BAR(player);
|
||||
MESSAGE("Wobbuffet is hurt by spikes!");
|
||||
}
|
||||
@@ -216,7 +219,7 @@ SINGLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Spikes from player
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes terrain")
|
||||
SINGLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes terrain (Gen 8+)")
|
||||
{
|
||||
u16 move;
|
||||
|
||||
@@ -225,7 +228,6 @@ SINGLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes terrain")
|
||||
PARAMETRIZE { move = MOVE_MISTY_TERRAIN; }
|
||||
PARAMETRIZE { move = MOVE_GRASSY_TERRAIN; }
|
||||
GIVEN {
|
||||
ASSUME(B_DEFOG_CLEARS_TERRAIN >= GEN_8);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(50); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
|
||||
} WHEN {
|
||||
@@ -235,19 +237,29 @@ SINGLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes terrain")
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, opponent);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Wobbuffet's evasiveness fell!");
|
||||
if (move == MOVE_PSYCHIC_TERRAIN) {
|
||||
MESSAGE("The weirdness disappeared from the battlefield.");
|
||||
if (B_DEFOG_EFFECT_CLEARING >= GEN_8) {
|
||||
if (move == MOVE_PSYCHIC_TERRAIN) {
|
||||
MESSAGE("The weirdness disappeared from the battlefield.");
|
||||
}
|
||||
else if (move == MOVE_ELECTRIC_TERRAIN) {
|
||||
MESSAGE("The electricity disappeared from the battlefield.");
|
||||
}
|
||||
else if (move == MOVE_MISTY_TERRAIN) {
|
||||
MESSAGE("The mist disappeared from the battlefield.");
|
||||
}
|
||||
else if (move == MOVE_GRASSY_TERRAIN) {
|
||||
MESSAGE("The grass disappeared from the battlefield.");
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG, player);
|
||||
} else {
|
||||
NONE_OF {
|
||||
MESSAGE("The weirdness disappeared from the battlefield.");
|
||||
MESSAGE("The electricity disappeared from the battlefield.");
|
||||
MESSAGE("The mist disappeared from the battlefield.");
|
||||
MESSAGE("The grass disappeared from the battlefield.");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG, player);
|
||||
}
|
||||
}
|
||||
else if (move == MOVE_ELECTRIC_TERRAIN) {
|
||||
MESSAGE("The electricity disappeared from the battlefield.");
|
||||
}
|
||||
else if (move == MOVE_MISTY_TERRAIN) {
|
||||
MESSAGE("The mist disappeared from the battlefield.");
|
||||
}
|
||||
else if (move == MOVE_GRASSY_TERRAIN) {
|
||||
MESSAGE("The grass disappeared from the battlefield.");
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG, player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,11 +282,12 @@ SINGLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Toxic Spikes from
|
||||
if (move == MOVE_DEFOG) {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
MESSAGE("Wobbuffet's evasiveness fell!");
|
||||
MESSAGE("The poison spikes disappeared from the ground around the opposing team!");
|
||||
if (B_DEFOG_EFFECT_CLEARING >= GEN_6)
|
||||
MESSAGE("The poison spikes disappeared from the ground around the opposing team!");
|
||||
}
|
||||
// Switch happens
|
||||
MESSAGE("2 sent out Wobbuffet!");
|
||||
if (move != MOVE_DEFOG) {
|
||||
if (move != MOVE_DEFOG || B_DEFOG_EFFECT_CLEARING <= GEN_5) {
|
||||
MESSAGE("Foe Wobbuffet was poisoned!");
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
|
||||
STATUS_ICON(opponent, poison: TRUE);
|
||||
@@ -356,15 +369,17 @@ DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes everything it can"
|
||||
MESSAGE("Ally's Aurora Veil wore off!");
|
||||
MESSAGE("Ally's Safeguard wore off!");
|
||||
|
||||
MESSAGE("The spikes disappeared from the ground around your team!");
|
||||
MESSAGE("The pointed stones disappeared from around your team!");
|
||||
MESSAGE("The poison spikes disappeared from the ground around your team!");
|
||||
MESSAGE("The sticky web has disappeared from the ground around your team!");
|
||||
if (B_DEFOG_EFFECT_CLEARING >= GEN_6) {
|
||||
MESSAGE("The spikes disappeared from the ground around your team!");
|
||||
MESSAGE("The pointed stones disappeared from around your team!");
|
||||
MESSAGE("The poison spikes disappeared from the ground around your team!");
|
||||
MESSAGE("The sticky web has disappeared from the ground around your team!");
|
||||
|
||||
// Opponent side
|
||||
MESSAGE("The spikes disappeared from the ground around the opposing team!");
|
||||
MESSAGE("The pointed stones disappeared from around the opposing team!");
|
||||
MESSAGE("The poison spikes disappeared from the ground around the opposing team!");
|
||||
MESSAGE("The sticky web has disappeared from the ground around the opposing team!");
|
||||
// Opponent side
|
||||
MESSAGE("The spikes disappeared from the ground around the opposing team!");
|
||||
MESSAGE("The pointed stones disappeared from around the opposing team!");
|
||||
MESSAGE("The poison spikes disappeared from the ground around the opposing team!");
|
||||
MESSAGE("The sticky web has disappeared from the ground around the opposing team!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
test/battle/move_effect/guard_split.c
Normal file
22
test/battle/move_effect/guard_split.c
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_GUARD_SPLIT].effect == EFFECT_GUARD_SPLIT);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Guard Split averages users and targets Def and Sp. Def stats")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_BULBASAUR);
|
||||
OPPONENT(SPECIES_IVYSAUR);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_GUARD_SPLIT); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_GUARD_SPLIT, player);
|
||||
} THEN {
|
||||
EXPECT_EQ(player->defense, opponent->defense);
|
||||
EXPECT_EQ(player->spDefense, opponent->spDefense);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ ASSUMPTIONS
|
||||
DOUBLE_BATTLE_TEST("Heal Bell cures the entire party")
|
||||
{
|
||||
u32 move;
|
||||
|
||||
|
||||
PARAMETRIZE { move = MOVE_HEAL_BELL; }
|
||||
PARAMETRIZE { move = MOVE_AROMATHERAPY; }
|
||||
|
||||
@@ -39,10 +39,10 @@ DOUBLE_BATTLE_TEST("Heal Bell cures the entire party")
|
||||
DOUBLE_BATTLE_TEST("Heal Bell does not cure soundproof partners")
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_SCRAPPY; }
|
||||
PARAMETRIZE { ability = ABILITY_SOUNDPROOF; }
|
||||
|
||||
|
||||
ASSUME(B_HEAL_BELL_SOUNDPROOF != GEN_5);
|
||||
|
||||
GIVEN {
|
||||
@@ -65,10 +65,10 @@ DOUBLE_BATTLE_TEST("Heal Bell does not cure soundproof partners")
|
||||
SINGLE_BATTLE_TEST("Heal Bell cures inactive soundproof Pokemon")
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_SCRAPPY; }
|
||||
PARAMETRIZE { ability = ABILITY_SOUNDPROOF; }
|
||||
|
||||
|
||||
ASSUME(B_HEAL_BELL_SOUNDPROOF >= GEN_5);
|
||||
|
||||
GIVEN {
|
||||
@@ -87,7 +87,7 @@ SINGLE_BATTLE_TEST("Heal Bell cures inactive soundproof Pokemon")
|
||||
|
||||
|
||||
SINGLE_BATTLE_TEST("Heal Bell cures a soundproof user")
|
||||
{
|
||||
{
|
||||
ASSUME(B_HEAL_BELL_SOUNDPROOF == GEN_5 || B_HEAL_BELL_SOUNDPROOF >= GEN_8);
|
||||
|
||||
GIVEN {
|
||||
|
||||
@@ -90,7 +90,7 @@ SINGLE_BATTLE_TEST("Knock Off does not remove items through Substitute")
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LEFTOVERS); };
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SUBSTITUTE);
|
||||
TURN { MOVE(opponent, MOVE_SUBSTITUTE);
|
||||
MOVE(player, MOVE_KNOCK_OFF); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player);
|
||||
@@ -112,7 +112,7 @@ SINGLE_BATTLE_TEST("Recycle cannot recover an item removed by Knock Off")
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF);
|
||||
MESSAGE("Wobbuffet knocked off Foe Wobbuffet's Leftovers!");
|
||||
|
||||
|
||||
MESSAGE("Foe Wobbuffet used Recycle!");
|
||||
MESSAGE("But it failed!");
|
||||
} THEN {
|
||||
|
||||
22
test/battle/move_effect/power_split.c
Normal file
22
test/battle/move_effect/power_split.c
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_POWER_SPLIT].effect == EFFECT_POWER_SPLIT);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Power Split averages user and targets Atk and Sp. Atk stats")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_BULBASAUR);
|
||||
OPPONENT(SPECIES_IVYSAUR);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_POWER_SPLIT); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_POWER_SPLIT, player);
|
||||
} THEN {
|
||||
EXPECT_EQ(player->attack, opponent->attack);
|
||||
EXPECT_EQ(player->spAttack, opponent->spAttack);
|
||||
}
|
||||
}
|
||||
@@ -482,3 +482,68 @@ DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from status moves")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Protect does not block Confide")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_CONFIDE].effect == EFFECT_SPECIAL_ATTACK_DOWN);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_CONFIDE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Confide!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFIDE, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
NOT MESSAGE("Foe Wobbuffet protected itself!");
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from Confide")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_CONFIDE].effect == EFFECT_SPECIAL_ATTACK_DOWN);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(playerLeft, MOVE_CONFIDE, target: opponentLeft); MOVE(playerRight, MOVE_CONFIDE, target: opponentRight); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Confide!");
|
||||
MESSAGE("Foe Wobbuffet protected itself!");
|
||||
MESSAGE("Wynaut used Confide!");
|
||||
MESSAGE("Foe Wynaut protected itself!");
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Crafty Shield does not protect against moves that target all battlers")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_FLOWER_SHIELD].target == MOVE_TARGET_ALL_BATTLERS);
|
||||
ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS);
|
||||
ASSUME(gSpeciesInfo[SPECIES_TANGROWTH].types[0] == TYPE_GRASS);
|
||||
ASSUME(gSpeciesInfo[SPECIES_SUNKERN].types[0] == TYPE_GRASS);
|
||||
ASSUME(gSpeciesInfo[SPECIES_SUNFLORA].types[0] == TYPE_GRASS);
|
||||
PLAYER(SPECIES_TANGELA);
|
||||
PLAYER(SPECIES_TANGROWTH);
|
||||
OPPONENT(SPECIES_SUNKERN);
|
||||
OPPONENT(SPECIES_SUNFLORA);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(opponentRight, MOVE_CELEBRATE); MOVE(playerLeft, MOVE_FLOWER_SHIELD); MOVE(playerRight, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
MESSAGE("Tangela used Flower Shield!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
|
||||
MESSAGE("Tangela's Defense rose!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
|
||||
MESSAGE("Foe Sunkern's Defense rose!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
|
||||
MESSAGE("Tangrowth's Defense rose!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
|
||||
MESSAGE("Foe Sunflora's Defense rose!");
|
||||
}
|
||||
}
|
||||
|
||||
137
test/battle/move_effect/retaliate.c
Normal file
137
test/battle/move_effect/retaliate.c
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_RETALIATE].effect == EFFECT_RETALIATE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Retaliate doubles in base power the turn after an ally faints")
|
||||
{
|
||||
s16 damage[2];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WYNAUT) { HP(1); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); }
|
||||
TURN { MOVE(player, MOVE_RETALIATE); }
|
||||
TURN { MOVE(player, MOVE_RETALIATE); }
|
||||
} SCENE {
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Retaliate doubles in base power the turn after an ally faints (opponent)")
|
||||
{
|
||||
s16 damage[2];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT) { HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); SEND_OUT(opponent, 1); }
|
||||
TURN { MOVE(opponent, MOVE_RETALIATE); }
|
||||
TURN { MOVE(opponent, MOVE_RETALIATE); }
|
||||
} SCENE {
|
||||
HP_BAR(player, captureDamage: &damage[0]);
|
||||
HP_BAR(player, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Retaliate works with passive damage")
|
||||
{
|
||||
s16 damage[2];
|
||||
u32 move;
|
||||
u32 move2 = MOVE_CELEBRATE;
|
||||
struct BattlePokemon *moveTarget = playerLeft;
|
||||
PARAMETRIZE { move = MOVE_TOXIC; moveTarget = playerLeft; }
|
||||
PARAMETRIZE { move = MOVE_POISON_POWDER; moveTarget = playerLeft; }
|
||||
PARAMETRIZE { move = MOVE_WILL_O_WISP; moveTarget = playerLeft; }
|
||||
#if B_USE_FROSTBITE == TRUE
|
||||
PARAMETRIZE { move = MOVE_ICE_BEAM; moveTarget = playerLeft; }
|
||||
#endif
|
||||
PARAMETRIZE { move = MOVE_SANDSTORM; moveTarget = playerLeft; }
|
||||
PARAMETRIZE { move = MOVE_HAIL; moveTarget = playerLeft; }
|
||||
PARAMETRIZE { move = MOVE_LEECH_SEED; moveTarget = playerLeft; }
|
||||
PARAMETRIZE { move = MOVE_MAGMA_STORM; moveTarget = playerLeft; }
|
||||
PARAMETRIZE { move = MOVE_FLAME_BURST; moveTarget = playerRight; }
|
||||
PARAMETRIZE { move = MOVE_FIRE_PLEDGE; moveTarget = playerRight; move2 = MOVE_GRASS_PLEDGE; }
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC);
|
||||
ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON);
|
||||
ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP);
|
||||
#if B_USE_FROSTBITE == TRUE
|
||||
ASSUME(gMovesInfo[MOVE_ICE_BEAM].additionalEffects[0].moveEffect == MOVE_EFFECT_FREEZE_OR_FROSTBITE);
|
||||
#endif
|
||||
ASSUME(gMovesInfo[MOVE_SANDSTORM].effect == EFFECT_SANDSTORM);
|
||||
ASSUME(gMovesInfo[MOVE_HAIL].effect == EFFECT_HAIL);
|
||||
ASSUME(gMovesInfo[MOVE_LEECH_SEED].effect == EFFECT_LEECH_SEED);
|
||||
ASSUME(gMovesInfo[MOVE_MAGMA_STORM].additionalEffects[0].moveEffect == MOVE_EFFECT_WRAP);
|
||||
ASSUME(gMovesInfo[MOVE_FLAME_BURST].additionalEffects[0].moveEffect == MOVE_EFFECT_FLAME_BURST);
|
||||
PLAYER(SPECIES_WYNAUT) { Ability(ABILITY_SHADOW_TAG); HP(18); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_SHADOW_TAG); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_CLEFABLE) { Ability(ABILITY_MAGIC_GUARD); Level(1); }
|
||||
OPPONENT(SPECIES_CLEFABLE) { Ability(ABILITY_MAGIC_GUARD); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentRight, move2, target: moveTarget); MOVE(opponentLeft, move, target: moveTarget); MOVE(playerLeft, MOVE_CELEBRATE); SEND_OUT(playerLeft, 2); }
|
||||
TURN { MOVE(opponentRight, MOVE_CELEBRATE, target: moveTarget); MOVE(playerLeft, MOVE_RETALIATE, target: opponentRight); }
|
||||
TURN { MOVE(opponentRight, MOVE_CELEBRATE, target: moveTarget); MOVE(playerLeft, MOVE_RETALIATE, target: opponentRight); }
|
||||
} SCENE {
|
||||
if (move != MOVE_FLAME_BURST)
|
||||
MESSAGE("Wynaut used Celebrate!");
|
||||
HP_BAR(opponentRight, captureDamage: &damage[0]);
|
||||
HP_BAR(opponentRight, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Retaliate works with Perish Song")
|
||||
{
|
||||
s16 damage[2];
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_PERISH_SONG].effect == EFFECT_PERISH_SONG);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_KOMMO_O) { Ability(ABILITY_SOUNDPROOF); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_PERISH_SONG); }
|
||||
TURN { MOVE(opponent, MOVE_CELEBRATE); }
|
||||
TURN { MOVE(opponent, MOVE_CELEBRATE); }
|
||||
TURN { MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); }
|
||||
TURN { MOVE(player, MOVE_RETALIATE); }
|
||||
TURN { MOVE(player, MOVE_RETALIATE); }
|
||||
} SCENE {
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Retaliate works with self-inflicted fainting")
|
||||
{
|
||||
s16 damage[2];
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_HEALING_WISH].effect == EFFECT_HEALING_WISH);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_HEALING_WISH); SEND_OUT(player, 1); }
|
||||
TURN { MOVE(player, MOVE_RETALIATE); }
|
||||
TURN { MOVE(player, MOVE_RETALIATE); }
|
||||
} SCENE {
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[1], Q_4_12(2), damage[0]);
|
||||
}
|
||||
}
|
||||
149
test/battle/move_effect/revelation_dance.c
Normal file
149
test/battle/move_effect/revelation_dance.c
Normal file
@@ -0,0 +1,149 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_REVELATION_DANCE].effect == EFFECT_REVELATION_DANCE);
|
||||
ASSUME(gMovesInfo[MOVE_REVELATION_DANCE].danceMove == TRUE);
|
||||
ASSUME(MoveHasAdditionalEffectSelfArg(MOVE_BURN_UP, MOVE_EFFECT_REMOVE_ARG_TYPE, TYPE_FIRE));
|
||||
ASSUME(gMovesInfo[MOVE_FORESTS_CURSE].effect == EFFECT_THIRD_TYPE);
|
||||
ASSUME(gMovesInfo[MOVE_FORESTS_CURSE].argument == TYPE_GRASS);
|
||||
ASSUME(gMovesInfo[MOVE_ROOST].effect == EFFECT_ROOST);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Revelation Dance changes its type depending on the user's 1st Type")
|
||||
{
|
||||
u16 speciesPlayer;
|
||||
u16 speciesOpponent;
|
||||
|
||||
PARAMETRIZE { speciesPlayer = SPECIES_ORICORIO_POM_POM; speciesOpponent = SPECIES_DUGTRIO; }
|
||||
PARAMETRIZE { speciesPlayer = SPECIES_ORICORIO_BAILE; speciesOpponent = SPECIES_BLASTOISE; }
|
||||
PARAMETRIZE { speciesPlayer = SPECIES_ORICORIO_PAU; speciesOpponent = SPECIES_LIEPARD; }
|
||||
PARAMETRIZE { speciesPlayer = SPECIES_ORICORIO_SENSU; speciesOpponent = SPECIES_PERSIAN; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(speciesPlayer);
|
||||
OPPONENT(speciesOpponent);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_REVELATION_DANCE); }
|
||||
} SCENE {
|
||||
if (speciesPlayer == SPECIES_ORICORIO_BAILE) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("It's not very effective…");
|
||||
}
|
||||
else {
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("It's not very effective…");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Revelation Dance changes its type depending on the user's 2nd Type if it has no 1st type")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ORICORIO_BAILE);
|
||||
OPPONENT(SPECIES_EMPOLEON);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BURN_UP); }
|
||||
TURN { MOVE(player, MOVE_REVELATION_DANCE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player);
|
||||
HP_BAR(opponent);
|
||||
NONE_OF {
|
||||
MESSAGE("It's not very effective…");
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("It's not very effective…");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Revelation Dance changes its type depending on the user's 3rd Type if it has no 1st or 2nd type")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GROWLITHE);
|
||||
OPPONENT(SPECIES_TREVENANT);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BURN_UP); MOVE(opponent, MOVE_FORESTS_CURSE); }
|
||||
TURN { MOVE(player, MOVE_REVELATION_DANCE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("It's super effective!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESTS_CURSE, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("It's not very effective…");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Revelation Dance becomes Typeless if its user is Typeless")
|
||||
{
|
||||
u16 speciesOpponent;
|
||||
|
||||
PARAMETRIZE { speciesOpponent = SPECIES_BLISSEY; }
|
||||
PARAMETRIZE { speciesOpponent = SPECIES_BLASTOISE; }
|
||||
PARAMETRIZE { speciesOpponent = SPECIES_CHARIZARD; }
|
||||
PARAMETRIZE { speciesOpponent = SPECIES_VENUSAUR; }
|
||||
PARAMETRIZE { speciesOpponent = SPECIES_GOLEM; }
|
||||
PARAMETRIZE { speciesOpponent = SPECIES_AEGISLASH; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GROWLITHE);
|
||||
OPPONENT(speciesOpponent);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BURN_UP); }
|
||||
TURN { MOVE(player, MOVE_REVELATION_DANCE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player);
|
||||
HP_BAR(opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, player);
|
||||
HP_BAR(opponent);
|
||||
NONE_OF {
|
||||
MESSAGE("It's not very effective…");
|
||||
MESSAGE("It's super effective!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Revelation Dance becomes Normal type if used by a Typeless Pokemon due to Roost")
|
||||
{
|
||||
u16 speciesOpponent;
|
||||
|
||||
PARAMETRIZE { speciesOpponent = SPECIES_SABLEYE; }
|
||||
PARAMETRIZE { speciesOpponent = SPECIES_AGGRON; }
|
||||
|
||||
ASSUME(B_ROOST_PURE_FLYING >= GEN_5);
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ORICORIO_BAILE) { Ability(ABILITY_DANCER); }
|
||||
OPPONENT(speciesOpponent);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BURN_UP); MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { MOVE(player, MOVE_ROOST); MOVE(opponent, MOVE_REVELATION_DANCE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BURN_UP, player);
|
||||
HP_BAR(opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROOST, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, opponent);
|
||||
HP_BAR(player);
|
||||
ABILITY_POPUP(player, ABILITY_DANCER);
|
||||
if (speciesOpponent == SPECIES_AGGRON) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("It's not very effective…");
|
||||
}
|
||||
else {
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REVELATION_DANCE, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("It's not very effective…");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
115
test/battle/move_effect/round.c
Normal file
115
test/battle/move_effect/round.c
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_ROUND].effect == EFFECT_ROUND);
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Round allows other battlers which also selected the moves to immediately use the move, ignoring turn order")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gItemsInfo[ITEM_LAGGING_TAIL].holdEffect == HOLD_EFFECT_LAGGING_TAIL);
|
||||
ASSUME(gMovesInfo[MOVE_IRON_HEAD].additionalEffects[0].moveEffect == MOVE_EFFECT_FLINCH);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LAGGING_TAIL); }
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(playerRight, MOVE_CELEBRATE);
|
||||
MOVE(opponentLeft, MOVE_ROUND, target: playerLeft);
|
||||
MOVE(playerLeft, MOVE_IRON_HEAD, target: opponentRight);
|
||||
MOVE(opponentRight, MOVE_ROUND, target: playerLeft);
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentLeft);
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_IRON_HEAD, playerLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_IRON_HEAD, playerLeft);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Round usages beyond the first one has double base power")
|
||||
{
|
||||
s16 damage[2];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(opponentLeft, MOVE_ROUND, target: playerLeft);
|
||||
MOVE(opponentRight, MOVE_ROUND, target: playerLeft);
|
||||
}
|
||||
} SCENE {
|
||||
HP_BAR(playerLeft, captureDamage: &damage[0]);
|
||||
HP_BAR(playerLeft, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Round still preserves the turn order outside of the other Round users moving immediately")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(opponentLeft, MOVE_ROUND, target: playerLeft);
|
||||
MOVE(playerRight, MOVE_CELEBRATE);
|
||||
MOVE(playerLeft, MOVE_CELEBRATE);
|
||||
MOVE(opponentRight, MOVE_ROUND, target: playerLeft); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft);
|
||||
}
|
||||
}
|
||||
DOUBLE_BATTLE_TEST("Round still preserves the turn order outside of the other Round users moving immediately with switch")
|
||||
{
|
||||
KNOWN_FAILING; // #5148
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN {
|
||||
SWITCH(playerRight, 2);
|
||||
MOVE(opponentLeft, MOVE_ROUND, target: playerLeft);
|
||||
MOVE(playerLeft, MOVE_CELEBRATE);
|
||||
MOVE(opponentRight, MOVE_ROUND, target: playerLeft);
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Round causes opposing pokemon to use Round immediately")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gItemsInfo[ITEM_LAGGING_TAIL].holdEffect == HOLD_EFFECT_LAGGING_TAIL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LAGGING_TAIL); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_CELEBRATE); MOVE(playerRight, MOVE_ROUND, target: opponentLeft); MOVE(playerLeft, MOVE_CELEBRATE, target: opponentRight); MOVE(opponentRight, MOVE_ROUND, target: playerLeft); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, playerRight);
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, opponentRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ SINGLE_BATTLE_TEST("Sleep Talk fails if not asleep")
|
||||
u32 status;
|
||||
PARAMETRIZE { status = STATUS1_SLEEP; }
|
||||
PARAMETRIZE { status = STATUS1_NONE; }
|
||||
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(status); Moves(MOVE_SLEEP_TALK, MOVE_TACKLE, MOVE_POUND, MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
@@ -35,7 +35,7 @@ SINGLE_BATTLE_TEST("Sleep Talk fails if not asleep")
|
||||
|
||||
SINGLE_BATTLE_TEST("Sleep Talk works if user has Comatose")
|
||||
{
|
||||
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_KOMALA) { Moves(MOVE_SLEEP_TALK, MOVE_TACKLE, MOVE_POUND, MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
|
||||
134
test/battle/move_effect/stomping_tantrum.c
Normal file
134
test/battle/move_effect/stomping_tantrum.c
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_STOMPING_TANTRUM].effect == EFFECT_STOMPING_TANTRUM);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Stomping Tatrum will deal double damage if user flinched on the previous turn")
|
||||
{
|
||||
s16 damage[3];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); SWITCH(opponent, 1); }
|
||||
TURN { MOVE(opponent, MOVE_FAKE_OUT); MOVE(player, MOVE_CELEBRATE); }
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); }
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FAKE_OUT, opponent);
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[2]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]);
|
||||
EXPECT_EQ(damage[0], damage[2]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Stomping Tatrum will deal double damage if user failed to attack due to paralysis")
|
||||
{
|
||||
s16 damage[3];
|
||||
PASSES_RANDOMLY(25, 100, RNG_PARALYSIS);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(100); Item(ITEM_POTION); };
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(10); Item(ITEM_LUM_BERRY); };
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); MOVE(opponent, MOVE_THUNDER_WAVE); }
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TRICK); }
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); }
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_WAVE, opponent);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK, opponent);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[2]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]);
|
||||
EXPECT_EQ(damage[0], damage[2]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Stomping Tatrum will not deal double damage if target protects")
|
||||
{
|
||||
s16 damage[2];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); }
|
||||
TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_STOMPING_TANTRUM); }
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
|
||||
MESSAGE("Foe Wobbuffet protected itself!");
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Stomping Tatrum will not deal double damage if it failed on the previous turn cause of Protect")
|
||||
{
|
||||
s16 damage[2];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BRIGHTPOWDER); };
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); }
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM, hit: FALSE); }
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
MESSAGE("Wobbuffet's attack missed!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Stomping Tatrum will deal double damage if user was immune to previous move")
|
||||
{
|
||||
s16 damage[2];
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_PIDGEY);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); SWITCH(opponent, 1); }
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); SWITCH(opponent, 0); }
|
||||
TURN { MOVE(player, MOVE_STOMPING_TANTRUM); SWITCH(opponent, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
MESSAGE("It doesn't affect Foe Pidgey…");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOMPING_TANTRUM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(damage[0], Q_4_12(2.0), damage[1]);
|
||||
}
|
||||
}
|
||||
@@ -71,4 +71,4 @@ SINGLE_BATTLE_TEST("Telekinesis makes the target immune to Ground-type attacks")
|
||||
|
||||
TO_DO_BATTLE_TEST("Baton Pass passes Telekinesis' effect");
|
||||
//Bulbapedia doesn't confirm what happens with Diglett, Dugtrio, Sandygast and Palossand, so it needs to be tested in-game.
|
||||
TO_DO_BATTLE_TEST("Baton Pass removes Telekinesis' effect disappears if the switching-in mon is Mega Gengar");
|
||||
TO_DO_BATTLE_TEST("Baton Pass removes Telekinesis' effect disappears if the switching-in mon is Mega Gengar");
|
||||
|
||||
@@ -78,3 +78,5 @@ SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to an Ice-type move
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(4.0), results[1].damage); // double base power + type effectiveness.
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Weather Ball doesn't double its power or change type if Cloud Nine/Air Lock is on the field");
|
||||
|
||||
@@ -70,3 +70,15 @@ DOUBLE_BATTLE_TEST("Hail deals damage based on turn order")
|
||||
HP_BAR(playerRight);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Hail damage rounds properly when maxHP < 16")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_MAGIKARP) { Level(1); MaxHP(11); HP(11); }
|
||||
OPPONENT(SPECIES_GLALIE);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_HAIL); }
|
||||
} SCENE {
|
||||
HP_BAR(player, damage: 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,3 +82,15 @@ DOUBLE_BATTLE_TEST("Sandstorm deals damage based on turn order")
|
||||
HP_BAR(playerRight);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Sandstorm damage rounds properly when maxHP < 16")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_MAGIKARP) { Level(1); MaxHP(11); HP(11); }
|
||||
OPPONENT(SPECIES_SANDSLASH);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SANDSTORM); }
|
||||
} SCENE {
|
||||
HP_BAR(player, damage: 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,13 +196,8 @@ TEST("RandomElement generates a uniform distribution")
|
||||
|
||||
TEST("RandomUniform mul-based faster than mod-based (compile-time)")
|
||||
{
|
||||
#if HQ_RANDOM == TRUE
|
||||
const u32 expectedMulSum = 6;
|
||||
const u32 expectedModSum = 4;
|
||||
#else
|
||||
const u32 expectedMulSum = 3;
|
||||
const u32 expectedModSum = 4;
|
||||
#endif
|
||||
const u32 expectedMulSum = 6;
|
||||
const u32 expectedModSum = 4;
|
||||
struct Benchmark mulBenchmark, modBenchmark;
|
||||
u32 mulSum = 0, modSum = 0;
|
||||
|
||||
@@ -234,13 +229,8 @@ TEST("RandomUniform mul-based faster than mod-based (compile-time)")
|
||||
|
||||
TEST("RandomUniform mul-based faster than mod-based (run-time)")
|
||||
{
|
||||
#if HQ_RANDOM == TRUE
|
||||
const u32 expectedMulSum = 289;
|
||||
const u32 expectedModSum = 205;
|
||||
#else
|
||||
const u32 expectedMulSum = 232;
|
||||
const u32 expectedModSum = 249;
|
||||
#endif
|
||||
const u32 expectedMulSum = 289;
|
||||
const u32 expectedModSum = 205;
|
||||
u32 i;
|
||||
struct Benchmark mulBenchmark, modBenchmark;
|
||||
u32 mulSum = 0, modSum = 0;
|
||||
@@ -264,7 +254,6 @@ TEST("RandomUniform mul-based faster than mod-based (run-time)")
|
||||
EXPECT_EQ(modSum, expectedModSum);
|
||||
}
|
||||
|
||||
#if HQ_RANDOM == TRUE
|
||||
TEST("Thumb and C SFC32 implementations produce the same results")
|
||||
{
|
||||
u32 thumbSum;
|
||||
@@ -285,5 +274,4 @@ TEST("Thumb and C SFC32 implementations produce the same results")
|
||||
}
|
||||
|
||||
EXPECT_EQ(thumbSum, cSum);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -35,20 +35,12 @@
|
||||
#define STATE gBattleTestRunnerState
|
||||
#define DATA gBattleTestRunnerState->data
|
||||
|
||||
#if HQ_RANDOM == TRUE
|
||||
#define RNG_SEED_DEFAULT {0, 0, 0, 0}
|
||||
static inline bool32 RngSeedNotDefault(const rng_value_t *seed)
|
||||
{
|
||||
return (seed->a | seed->b | seed->c | seed->ctr) != 0;
|
||||
|
||||
}
|
||||
#else
|
||||
#define RNG_SEED_DEFAULT 0x00000000
|
||||
static inline bool32 RngSeedNotDefault(const rng_value_t *seed)
|
||||
{
|
||||
return *seed != RNG_SEED_DEFAULT;
|
||||
}
|
||||
#endif
|
||||
#undef Q_4_12
|
||||
#define Q_4_12(n) (s32)((n) * 4096)
|
||||
|
||||
@@ -410,7 +402,7 @@ u32 RandomUniformExcept(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32
|
||||
if (STATE->trials == 1)
|
||||
{
|
||||
u32 n = 0, i;
|
||||
for (i = lo; i < hi; i++)
|
||||
for (i = lo; i <= hi; i++)
|
||||
if (!reject(i))
|
||||
n++;
|
||||
STATE->trials = n;
|
||||
@@ -769,7 +761,7 @@ static u32 CountAiExpectMoves(struct ExpectedAIAction *expectedAction, u32 battl
|
||||
u32 i, countExpected = 0;
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (gBitTable[i] & expectedAction->moveSlots)
|
||||
if ((1u << i) & expectedAction->moveSlots)
|
||||
{
|
||||
if (printLog)
|
||||
PrintAiMoveLog(battlerId, i, gBattleMons[battlerId].moves[i], gBattleStruct->aiFinalScore[battlerId][expectedAction->target][i]);
|
||||
@@ -801,7 +793,7 @@ void TestRunner_Battle_CheckChosenMove(u32 battlerId, u32 moveId, u32 target)
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (gBitTable[i] & expectedAction->moveSlots)
|
||||
if ((1u << i) & expectedAction->moveSlots)
|
||||
{
|
||||
expectedMoveId = gBattleMons[battlerId].moves[i];
|
||||
if (!expectedAction->notMove)
|
||||
@@ -917,8 +909,8 @@ static void CheckIfMaxScoreEqualExpectMove(u32 battlerId, s32 target, struct Exp
|
||||
// We expect move 'i', but it has the same best score as another move that we didn't expect.
|
||||
if (scores[i] == scores[bestScoreId]
|
||||
&& !aiAction->notMove
|
||||
&& (aiAction->moveSlots & gBitTable[i])
|
||||
&& !(aiAction->moveSlots & gBitTable[bestScoreId]))
|
||||
&& (aiAction->moveSlots & (1u << i))
|
||||
&& !(aiAction->moveSlots & (1u << bestScoreId)))
|
||||
{
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: EXPECT_MOVE %S has the same best score(%d) as not expected MOVE %S", filename,
|
||||
aiAction->sourceLine, GetMoveName(moves[i]), scores[i], GetMoveName(moves[bestScoreId]));
|
||||
@@ -926,8 +918,8 @@ static void CheckIfMaxScoreEqualExpectMove(u32 battlerId, s32 target, struct Exp
|
||||
// We DO NOT expect move 'i', but it has the same best score as another move.
|
||||
if (scores[i] == scores[bestScoreId]
|
||||
&& aiAction->notMove
|
||||
&& (aiAction->moveSlots & gBitTable[i])
|
||||
&& !(aiAction->moveSlots & gBitTable[bestScoreId]))
|
||||
&& (aiAction->moveSlots & (1u << i))
|
||||
&& !(aiAction->moveSlots & (1u << bestScoreId)))
|
||||
{
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, SourceLine(0), ":L%s:%d: NOT_EXPECT_MOVE %S has the same best score(%d) as MOVE %S", filename,
|
||||
aiAction->sourceLine, GetMoveName(moves[i]), scores[i], GetMoveName(moves[bestScoreId]));
|
||||
@@ -940,9 +932,9 @@ static void PrintAiMoveLog(u32 battlerId, u32 moveSlot, u32 moveId, s32 totalSco
|
||||
s32 i, scoreFromLogs = 0;
|
||||
|
||||
if (!DATA.logAI) return;
|
||||
if (DATA.aiLogPrintedForMove[battlerId] & gBitTable[moveSlot]) return;
|
||||
if (DATA.aiLogPrintedForMove[battlerId] & (1u << moveSlot)) return;
|
||||
|
||||
DATA.aiLogPrintedForMove[battlerId] |= gBitTable[moveSlot];
|
||||
DATA.aiLogPrintedForMove[battlerId] |= 1u << moveSlot;
|
||||
Test_MgbaPrintf("Score Log for move %S:\n", GetMoveName(moveId));
|
||||
for (i = 0; i < MAX_AI_LOG_LINES; i++)
|
||||
{
|
||||
@@ -1327,7 +1319,7 @@ void TestRunner_Battle_AfterLastTurn(void)
|
||||
}
|
||||
|
||||
STATE->runThen = TRUE;
|
||||
STATE->runFinally = STATE->runParameter + 1 == STATE->parameters;
|
||||
STATE->runFinally = STATE->runParameter + 1 == STATE->parameters && STATE->runTrial + 1 >= STATE->trials;
|
||||
InvokeTestFunction(test);
|
||||
STATE->runThen = FALSE;
|
||||
STATE->runFinally = FALSE;
|
||||
@@ -1357,18 +1349,15 @@ static void CB2_BattleTest_NextParameter(void)
|
||||
|
||||
static inline rng_value_t MakeRngValue(const u16 seed)
|
||||
{
|
||||
#if HQ_RANDOM == TRUE
|
||||
int i;
|
||||
rng_value_t result = {0, 0, seed, 1};
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
int i;
|
||||
rng_value_t result = {0, 0, seed, 1};
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
_SFC32_Next(&result);
|
||||
}
|
||||
return result;
|
||||
#else
|
||||
return ISO_RANDOMIZE1(seed);
|
||||
#endif
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void CB2_BattleTest_NextTrial(void)
|
||||
{
|
||||
ClearFlagAfterTest();
|
||||
@@ -1452,7 +1441,7 @@ void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext ctx
|
||||
{
|
||||
const struct BattleTest *test = GetBattleTest();
|
||||
INVALID_IF(STATE->trials != 0, "PASSES_RANDOMLY can only be used once per test");
|
||||
INVALID_IF(test->resultsSize > 0, "PASSES_RANDOMLY is incompatible with results");
|
||||
INVALID_IF(test->resultsSize > 0 && STATE->parametersCount > 1, "PASSES_RANDOMLY is incompatible with results");
|
||||
INVALID_IF(passes > trials, "%d passes specified, but only %d trials", passes, trials);
|
||||
STATE->rngTag = ctx.tag;
|
||||
STATE->rngTrialOffset = 0;
|
||||
@@ -2186,7 +2175,7 @@ static void TryMarkExpectMove(u32 sourceLine, struct BattlePokemon *battler, str
|
||||
|
||||
id = DATA.expectedAiActionIndex[battlerId];
|
||||
DATA.expectedAiActions[battlerId][id].type = B_ACTION_USE_MOVE;
|
||||
DATA.expectedAiActions[battlerId][id].moveSlots |= gBitTable[moveSlot];
|
||||
DATA.expectedAiActions[battlerId][id].moveSlots |= 1 << moveSlot;
|
||||
DATA.expectedAiActions[battlerId][id].target = target;
|
||||
DATA.expectedAiActions[battlerId][id].explicitTarget = ctx->explicitTarget;
|
||||
DATA.expectedAiActions[battlerId][id].sourceLine = sourceLine;
|
||||
|
||||
Reference in New Issue
Block a user