Merge pull request #24 from cawtds/expansion-merge-20240827

Expansion merge upcoming 0bb43626afe2cdc6f9bdd7c246e2dca7198ddab3
This commit is contained in:
cawtds
2024-08-27 22:44:29 +02:00
committed by GitHub
96 changed files with 3302 additions and 1109 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);

View File

@@ -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
{

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1831,7 +1831,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_FICKLE_BEAM] =
{
.battleScript = BattleScript_EffectHit,
.battleScript = BattleScript_EffectFickleBeam,
},
[EFFECT_BLIZZARD] =

View File

@@ -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))
{

View File

@@ -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)

View File

@@ -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; \

View 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");

View 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.

View 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);
}
}

View 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

View 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");

View File

@@ -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!");

View File

@@ -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;

View 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");

View 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+)");

View 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");

View 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

View 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");

View File

@@ -0,0 +1,4 @@
#include "global.h"
#include "test/battle.h"
// Tests for Chilling Neigh are handled in test/battle/ability/moxie.c

View 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");

View File

@@ -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!");

View File

@@ -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

View File

@@ -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);

View File

@@ -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!");
}
}

View File

@@ -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);
}
}

View 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)");

View File

@@ -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;

View File

@@ -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;

View File

@@ -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!");
}
}

View File

@@ -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.

View 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]);
}
}

View File

@@ -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);

View File

@@ -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
}
}

View File

@@ -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");

View File

@@ -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);
}
}

View 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!");
}
}

View File

@@ -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");

View File

@@ -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);
}
}
}

View File

@@ -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);

View 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");

View 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");

View 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);
}
}

View 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.

View File

@@ -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");

View File

@@ -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 {

View File

@@ -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);
}
}

View 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.

View 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");

View 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");

View File

@@ -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");

View 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");

View File

@@ -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");

View 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");

View 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");

View 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");

View 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);
}
}

View File

@@ -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); }

View File

@@ -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); }

View File

@@ -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 {

View File

@@ -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!");

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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!");
}
}
}

View 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);
}
}

View File

@@ -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 {

View File

@@ -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 {

View 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);
}
}

View File

@@ -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!");
}
}

View 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]);
}
}

View 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…");
}
}
}
}

View 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);
}
}

View File

@@ -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);

View 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]);
}
}

View File

@@ -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");

View File

@@ -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");

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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
}

View File

@@ -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;