Merge branch '_RHH/upcoming' into _RHH/pr/upcoming/merrpFollowers

This commit is contained in:
Eduardo Quezada 2024-06-02 11:06:41 -04:00
commit ee1d14755a
103 changed files with 5865 additions and 3384 deletions

View File

@ -1352,6 +1352,22 @@
.endm
@ callnative macros
.macro savetarget
callnative BS_SaveTarget
.endm
.macro restoretarget
callnative BS_RestoreTarget
.endm
.macro saveattacker
callnative BS_SaveAttacker
.endm
.macro restoreattacker
callnative BS_RestoreAttacker
.endm
.macro metalburstdamagecalculator failInstr:req
callnative BS_CalcMetalBurstDmg
.4byte \failInstr
@ -1774,14 +1790,6 @@
various \battler, VARIOUS_SWITCHIN_ABILITIES
.endm
.macro savetarget
various BS_TARGET, VARIOUS_SAVE_TARGET
.endm
.macro restoretarget
various BS_TARGET, VARIOUS_RESTORE_TARGET
.endm
.macro instanthpdrop battler:req
various \battler, VARIOUS_INSTANT_HP_DROP
.endm

File diff suppressed because it is too large Load Diff

View File

@ -5775,7 +5775,7 @@ BattleScript_PrintFullBox::
BattleScript_ActionSwitch::
hpthresholds2 BS_ATTACKER
copybyte sSAVED_BATTLER, gBattlerAttacker
saveattacker
printstring STRINGID_RETURNMON
jumpifbattletype BATTLE_TYPE_DOUBLE, BattleScript_PursuitSwitchDmgSetMultihit
setmultihit 1
@ -5793,7 +5793,7 @@ BattleScript_DoSwitchOut::
switchoutabilities BS_ATTACKER
updatedynamax
waitstate
copybyte gBattlerAttacker, sSAVED_BATTLER
restoreattacker
returnatktoball
waitstate
drawpartystatussummary BS_ATTACKER
@ -7820,7 +7820,7 @@ BattleScript_TryIntimidateHoldEffectsRet:
return
BattleScript_IntimidateActivates::
copybyte sSAVED_BATTLER, gBattlerTarget
savetarget
.if B_ABILITY_POP_UP == TRUE
showabilitypopup BS_ATTACKER
pause B_WAIT_TIME_LONG
@ -7858,7 +7858,7 @@ BattleScript_IntimidateLoopIncrement:
BattleScript_IntimidateEnd:
copybyte sBATTLER, gBattlerAttacker
destroyabilitypopup
copybyte gBattlerTarget, sSAVED_BATTLER
restoretarget
pause B_WAIT_TIME_MED
end3
@ -7891,7 +7891,7 @@ BattleScript_IntimidateInReverse:
goto BattleScript_IntimidateLoopIncrement
BattleScript_SupersweetSyrupActivates::
copybyte sSAVED_BATTLER, gBattlerTarget
savetarget
.if B_ABILITY_POP_UP == TRUE
showabilitypopup BS_ATTACKER
pause B_WAIT_TIME_LONG
@ -7924,7 +7924,7 @@ BattleScript_SupersweetSyrupLoopIncrement:
BattleScript_SupersweetSyrupEnd:
copybyte sBATTLER, gBattlerAttacker
destroyabilitypopup
copybyte gBattlerTarget, sSAVED_BATTLER
restoretarget
pause B_WAIT_TIME_MED
end3
@ -8315,7 +8315,7 @@ BattleScript_DazzlingProtected::
attackstring
ppreduce
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
call BattleScript_AbilityPopUpScripting
printstring STRINGID_POKEMONCANNOTUSEMOVE
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
@ -8566,7 +8566,10 @@ BattleScript_FriskMsg::
return
BattleScript_FriskActivates::
tryfriskmsg BS_ATTACKER
saveattacker
copybyte gBattlerAttacker, sBATTLER
tryfriskmsg BS_SCRIPTING
restoreattacker
end3
BattleScript_ImposterActivates::
@ -9559,19 +9562,19 @@ BattleScript_RedCardIngrain:
printstring STRINGID_PKMNANCHOREDITSELF
waitmessage B_WAIT_TIME_LONG
removeitem BS_SCRIPTING
swapattackerwithtarget
restoretarget
return
BattleScript_RedCardSuctionCups:
printstring STRINGID_PKMNANCHORSITSELFWITH
waitmessage B_WAIT_TIME_LONG
removeitem BS_SCRIPTING
swapattackerwithtarget
restoretarget
return
BattleScript_RedCardDynamaxed:
printstring STRINGID_MOVEBLOCKEDBYDYNAMAX
waitmessage B_WAIT_TIME_LONG
removeitem BS_SCRIPTING
swapattackerwithtarget
restoretarget
return
BattleScript_EjectButtonActivates::

View File

@ -359,10 +359,10 @@
* Added missing `P_UPDATED_EVS` config that allows setting the EV yield changes across generations by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/3993
* Added missing `P_UPDATED_EXP_YIELDS` config that allows setting the Experience yield changes across generations by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/3995
* Added evolution methods that require custom trackers (`MON_DATA_EVOLUTION_TRACKER`) by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4087
* `EVO_USE_MOVE_TWENTY_TIMES`:
* `EVO_LEVEL_MOVE_TWENTY_TIMES`:
* Stantler can now evolve into Wyrdeer by using Psyshield Bash 20 times.
* Primeape can now evolve into Annihilape by using Rage Fist 20 times.
* `EVO_RECOIL_DAMAGE_MALE`/`EVO_RECOIL_DAMAGE_FEMALE`
* `EVO_LEVEL_RECOIL_DAMAGE_MALE`/`EVO_LEVEL_RECOIL_DAMAGE_FEMALE`
* White-Striped Basculin can now evolve into Basculegion when leveling up after receiving 294HP of recoil damage and being the corresponding gender.
* Added missing Paldean Wooper icon by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4260
* Added missing data for placeholder Pokémon by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4281

View File

@ -23,8 +23,8 @@
* Fixed Exp. Candies ignoring hard caps.
* Fixed Pokémon gaining 1 experience if they are at the level cap.
* Fixed evolution tracker issues by @cawtds in https://github.com/rh-hideout/pokeemerald-expansion/pull/4503
* `EVO_USE_MOVE_TWENTY_TIMES` no longer increases with every move.
* `EVO_RECOIL_DAMAGE_MALE/FEMALE` is no longer updated twice than intended.
* `EVO_LEVEL_MOVE_TWENTY_TIMES` no longer increases with every move.
* `EVO_LEVEL_RECOIL_DAMAGE_MALE/FEMALE` is no longer updated twice than intended.
## ✨ Feature Branches ✨
### ***TheXaman's HGSS Pokédex Plus***:

View File

@ -92,9 +92,9 @@ void *AllocInternal(void *heapStart, u32 size, const char *location)
{
const char *location = MemBlockLocation(block);
if (location)
MgbaPrintf_("%s: %d bytes allocated", location, block->size);
Test_MgbaPrintf("%s: %d bytes allocated", location, block->size);
else
MgbaPrintf_("<unknown>: %d bytes allocated", block->size);
Test_MgbaPrintf("<unknown>: %d bytes allocated", block->size);
}
block = block->next;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
152 208 160
38 26 108
49 34 133
103 230 225
60 76 160
160 64 109
228 103 181
0 0 0
115 162 235
52 201 163
76 244 204
153 228 201
230 244 173
205 251 128
63 50 194
255 255 255

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
152 208 160
37 114 136
66 151 189
151 58 210
63 209 230
160 64 109
234 243 49
0 0 0
218 32 48
52 201 163
76 244 204
153 228 201
230 244 173
205 251 128
63 50 194
255 255 255

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
152 208 160
90 53 7
42 121 78
70 70 70
215 38 89
41 45 164
77 151 158
78 190 242
170 172 227
122 228 243
147 245 227
252 252 182
255 255 255
52 66 152
0 200 255
197 34 91

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
152 208 160
90 53 7
42 121 78
70 70 70
215 38 89
41 45 164
77 151 158
78 190 242
170 172 227
122 228 243
147 245 227
252 252 182
255 255 255
84 215 251
255 0 0
244 255 0

BIN
graphics/types/none.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

View File

@ -22,7 +22,7 @@ STARTERGFXDIR := graphics/starter_choose
NAMINGGFXDIR := graphics/naming_screen
SPINDAGFXDIR := graphics/pokemon/spinda/spots
types := normal fight flying poison ground rock bug ghost steel mystery fire water grass electric psychic ice dragon dark fairy stellar
types := none normal fight flying poison ground rock bug ghost steel mystery fire water grass electric psychic ice dragon dark fairy stellar
contest_types := cool beauty cute smart tough
### Tilesets ###

View File

@ -738,7 +738,10 @@ struct BattleStruct
u8 magnitudeBasePower;
u8 presentBasePower;
u8 roostTypes[MAX_BATTLERS_COUNT][2];
u8 savedBattlerTarget;
u8 savedBattlerTarget[5];
u8 savedBattlerAttacker[5];
u8 savedTargetCount:4;
u8 savedAttackerCount:4;
bool8 ateBoost[MAX_BATTLERS_COUNT];
u8 activeAbilityPopUps; // as bits for each battler
u8 abilityPopUpSpriteIds[MAX_BATTLERS_COUNT][2]; // two per battler
@ -1146,6 +1149,7 @@ extern u16 gLastThrownBall;
extern u16 gBallToDisplay;
extern bool8 gLastUsedBallMenuPresent;
extern u8 gPartyCriticalHits[PARTY_SIZE];
extern u8 gCategoryIconSpriteId;
static inline u32 GetBattlerPosition(u32 battler)
{

View File

@ -32,23 +32,21 @@
#define BEST_DAMAGE_MOVE 1 // Move with the most amount of hits with the best accuracy/effect
#define POWERFUL_STATUS_MOVE 10 // Moves with this score will be chosen over a move that faints target
// Temporary scores that are added together to determine a final score at the at of AI_CalcMoveEffectScore
// Scores given in AI_CalcMoveEffectScore
#define WEAK_EFFECT 1
#define DECENT_EFFECT 2
#define GOOD_EFFECT 4
#define BEST_EFFECT 6
// AI_CalcMoveEffectScore final score
#define NOT_GOOD_ENOUGH 0 // Not worth using over a damaging move
#define GOOD_MOVE_EFFECTS 2 // Worth using over a damaging move
#define PREFERRED_MOVE_EFFECTS 3 // Worth using over a damagin move and is better then DECENT_EFFECT
#define BEST_MOVE_EFFECTS 4 // Best possible move effects. E.g. stat boosting moves that boost multiply moves
#define GOOD_EFFECT 3
#define BEST_EFFECT 4
// AI_TryToFaint
#define FAST_KILL 6 // AI is faster and faints target
#define SLOW_KILL 4 // AI is slower and faints target
#define LAST_CHANCE 2 // AI faints to target. It should try and do damage with a priority move
// AI_Risky
#define STRONG_RISKY_EFFECT 3
#define AVERAGE_RISKY_EFFECT 2
#include "test_runner.h"
// Logs for debugging AI tests.

View File

@ -3,15 +3,20 @@
#define FOE(battler) ((BATTLE_OPPOSITE(battler)) & BIT_SIDE)
#define AI_STRIKES_FIRST(battlerAi, battlerDef, move)((AI_WhoStrikesFirst(battlerAi, battlerDef, move) == AI_IS_FASTER))
// Roll boundaries used by AI when scoring. Doesn't affect actual damage dealt.
#define MAX_ROLL_PERCENTAGE DMG_ROLL_PERCENT_HI
#define MIN_ROLL_PERCENTAGE DMG_ROLL_PERCENT_LO
#define DMG_ROLL_PERCENTAGE ((MAX_ROLL_PERCENTAGE + MIN_ROLL_PERCENTAGE + 1) / 2) // Controls the damage roll the AI sees for the default roll. By default the 9th roll is seen
enum
{
DMG_ROLL_LOWEST,
DMG_ROLL_AVERAGE,
DMG_ROLL_DEFAULT,
DMG_ROLL_HIGHEST,
};
bool32 AI_IsFaster(u32 battlerAi, u32 battlerDef, u32 move);
bool32 AI_IsSlower(u32 battlerAi, u32 battlerDef, u32 move);
bool32 AI_RandLessThan(u32 val);
bool32 IsAiVsAiBattle(void);
bool32 BattlerHasAi(u32 battlerId);

View File

@ -0,0 +1,940 @@
#ifndef GUARD_BATTLE_ANIM_SCRIPTS_H
#define GUARD_BATTLE_ANIM_SCRIPTS_H
extern const u8 Move_NONE[];
extern const u8 Move_POUND[];
extern const u8 Move_KARATE_CHOP[];
extern const u8 Move_DOUBLE_SLAP[];
extern const u8 Move_COMET_PUNCH[];
extern const u8 Move_MEGA_PUNCH[];
extern const u8 Move_PAY_DAY[];
extern const u8 Move_FIRE_PUNCH[];
extern const u8 Move_ICE_PUNCH[];
extern const u8 Move_THUNDER_PUNCH[];
extern const u8 Move_SCRATCH[];
extern const u8 Move_VISE_GRIP[];
extern const u8 Move_GUILLOTINE[];
extern const u8 Move_RAZOR_WIND[];
extern const u8 Move_SWORDS_DANCE[];
extern const u8 Move_CUT[];
extern const u8 Move_GUST[];
extern const u8 Move_WING_ATTACK[];
extern const u8 Move_WHIRLWIND[];
extern const u8 Move_FLY[];
extern const u8 Move_BIND[];
extern const u8 Move_SLAM[];
extern const u8 Move_VINE_WHIP[];
extern const u8 Move_STOMP[];
extern const u8 Move_DOUBLE_KICK[];
extern const u8 Move_MEGA_KICK[];
extern const u8 Move_JUMP_KICK[];
extern const u8 Move_ROLLING_KICK[];
extern const u8 Move_SAND_ATTACK[];
extern const u8 Move_HEADBUTT[];
extern const u8 Move_HORN_ATTACK[];
extern const u8 Move_FURY_ATTACK[];
extern const u8 Move_HORN_DRILL[];
extern const u8 Move_TACKLE[];
extern const u8 Move_BODY_SLAM[];
extern const u8 Move_WRAP[];
extern const u8 Move_TAKE_DOWN[];
extern const u8 Move_THRASH[];
extern const u8 Move_DOUBLE_EDGE[];
extern const u8 Move_TAIL_WHIP[];
extern const u8 Move_POISON_STING[];
extern const u8 Move_TWINEEDLE[];
extern const u8 Move_PIN_MISSILE[];
extern const u8 Move_LEER[];
extern const u8 Move_BITE[];
extern const u8 Move_GROWL[];
extern const u8 Move_ROAR[];
extern const u8 Move_SING[];
extern const u8 Move_SUPERSONIC[];
extern const u8 Move_SONIC_BOOM[];
extern const u8 Move_DISABLE[];
extern const u8 Move_ACID[];
extern const u8 Move_EMBER[];
extern const u8 Move_FLAMETHROWER[];
extern const u8 Move_MIST[];
extern const u8 Move_WATER_GUN[];
extern const u8 Move_HYDRO_PUMP[];
extern const u8 Move_SURF[];
extern const u8 Move_ICE_BEAM[];
extern const u8 Move_BLIZZARD[];
extern const u8 Move_PSYBEAM[];
extern const u8 Move_BUBBLE_BEAM[];
extern const u8 Move_AURORA_BEAM[];
extern const u8 Move_HYPER_BEAM[];
extern const u8 Move_PECK[];
extern const u8 Move_DRILL_PECK[];
extern const u8 Move_SUBMISSION[];
extern const u8 Move_LOW_KICK[];
extern const u8 Move_COUNTER[];
extern const u8 Move_SEISMIC_TOSS[];
extern const u8 Move_STRENGTH[];
extern const u8 Move_ABSORB[];
extern const u8 Move_MEGA_DRAIN[];
extern const u8 Move_LEECH_SEED[];
extern const u8 Move_GROWTH[];
extern const u8 Move_RAZOR_LEAF[];
extern const u8 Move_SOLAR_BEAM[];
extern const u8 Move_POISON_POWDER[];
extern const u8 Move_STUN_SPORE[];
extern const u8 Move_SLEEP_POWDER[];
extern const u8 Move_PETAL_DANCE[];
extern const u8 Move_STRING_SHOT[];
extern const u8 Move_DRAGON_RAGE[];
extern const u8 Move_FIRE_SPIN[];
extern const u8 Move_THUNDER_SHOCK[];
extern const u8 Move_THUNDERBOLT[];
extern const u8 Move_THUNDER_WAVE[];
extern const u8 Move_THUNDER[];
extern const u8 Move_ROCK_THROW[];
extern const u8 Move_EARTHQUAKE[];
extern const u8 Move_FISSURE[];
extern const u8 Move_DIG[];
extern const u8 Move_TOXIC[];
extern const u8 Move_CONFUSION[];
extern const u8 Move_PSYCHIC[];
extern const u8 Move_HYPNOSIS[];
extern const u8 Move_MEDITATE[];
extern const u8 Move_AGILITY[];
extern const u8 Move_QUICK_ATTACK[];
extern const u8 Move_RAGE[];
extern const u8 Move_TELEPORT[];
extern const u8 Move_NIGHT_SHADE[];
extern const u8 Move_MIMIC[];
extern const u8 Move_SCREECH[];
extern const u8 Move_DOUBLE_TEAM[];
extern const u8 Move_RECOVER[];
extern const u8 Move_HARDEN[];
extern const u8 Move_MINIMIZE[];
extern const u8 Move_SMOKESCREEN[];
extern const u8 Move_CONFUSE_RAY[];
extern const u8 Move_WITHDRAW[];
extern const u8 Move_DEFENSE_CURL[];
extern const u8 Move_BARRIER[];
extern const u8 Move_LIGHT_SCREEN[];
extern const u8 Move_HAZE[];
extern const u8 Move_REFLECT[];
extern const u8 Move_FOCUS_ENERGY[];
extern const u8 Move_BIDE[];
extern const u8 Move_METRONOME[];
extern const u8 Move_MIRROR_MOVE[];
extern const u8 Move_SELF_DESTRUCT[];
extern const u8 Move_EGG_BOMB[];
extern const u8 Move_LICK[];
extern const u8 Move_SMOG[];
extern const u8 Move_SLUDGE[];
extern const u8 Move_BONE_CLUB[];
extern const u8 Move_FIRE_BLAST[];
extern const u8 Move_WATERFALL[];
extern const u8 Move_CLAMP[];
extern const u8 Move_SWIFT[];
extern const u8 Move_SKULL_BASH[];
extern const u8 Move_SPIKE_CANNON[];
extern const u8 Move_CONSTRICT[];
extern const u8 Move_AMNESIA[];
extern const u8 Move_KINESIS[];
extern const u8 Move_SOFT_BOILED[];
extern const u8 Move_HIGH_JUMP_KICK[];
extern const u8 Move_GLARE[];
extern const u8 Move_DREAM_EATER[];
extern const u8 Move_POISON_GAS[];
extern const u8 Move_BARRAGE[];
extern const u8 Move_LEECH_LIFE[];
extern const u8 Move_LOVELY_KISS[];
extern const u8 Move_SKY_ATTACK[];
extern const u8 Move_TRANSFORM[];
extern const u8 Move_BUBBLE[];
extern const u8 Move_DIZZY_PUNCH[];
extern const u8 Move_SPORE[];
extern const u8 Move_FLASH[];
extern const u8 Move_PSYWAVE[];
extern const u8 Move_SPLASH[];
extern const u8 Move_ACID_ARMOR[];
extern const u8 Move_CRABHAMMER[];
extern const u8 Move_EXPLOSION[];
extern const u8 Move_FURY_SWIPES[];
extern const u8 Move_BONEMERANG[];
extern const u8 Move_REST[];
extern const u8 Move_ROCK_SLIDE[];
extern const u8 Move_HYPER_FANG[];
extern const u8 Move_SHARPEN[];
extern const u8 Move_CONVERSION[];
extern const u8 Move_TRI_ATTACK[];
extern const u8 Move_SUPER_FANG[];
extern const u8 Move_SLASH[];
extern const u8 Move_SUBSTITUTE[];
extern const u8 Move_STRUGGLE[];
extern const u8 Move_SKETCH[];
extern const u8 Move_TRIPLE_KICK[];
extern const u8 Move_THIEF[];
extern const u8 Move_SPIDER_WEB[];
extern const u8 Move_MIND_READER[];
extern const u8 Move_NIGHTMARE[];
extern const u8 Move_FLAME_WHEEL[];
extern const u8 Move_SNORE[];
extern const u8 Move_CURSE[];
extern const u8 Move_FLAIL[];
extern const u8 Move_CONVERSION_2[];
extern const u8 Move_AEROBLAST[];
extern const u8 Move_COTTON_SPORE[];
extern const u8 Move_REVERSAL[];
extern const u8 Move_SPITE[];
extern const u8 Move_POWDER_SNOW[];
extern const u8 Move_PROTECT[];
extern const u8 Move_MACH_PUNCH[];
extern const u8 Move_SCARY_FACE[];
extern const u8 Move_FEINT_ATTACK[];
extern const u8 Move_SWEET_KISS[];
extern const u8 Move_BELLY_DRUM[];
extern const u8 Move_SLUDGE_BOMB[];
extern const u8 Move_MUD_SLAP[];
extern const u8 Move_OCTAZOOKA[];
extern const u8 Move_SPIKES[];
extern const u8 Move_ZAP_CANNON[];
extern const u8 Move_FORESIGHT[];
extern const u8 Move_DESTINY_BOND[];
extern const u8 Move_PERISH_SONG[];
extern const u8 Move_ICY_WIND[];
extern const u8 Move_DETECT[];
extern const u8 Move_BONE_RUSH[];
extern const u8 Move_LOCK_ON[];
extern const u8 Move_OUTRAGE[];
extern const u8 Move_SANDSTORM[];
extern const u8 Move_GIGA_DRAIN[];
extern const u8 Move_ENDURE[];
extern const u8 Move_CHARM[];
extern const u8 Move_ROLLOUT[];
extern const u8 Move_FALSE_SWIPE[];
extern const u8 Move_SWAGGER[];
extern const u8 Move_MILK_DRINK[];
extern const u8 Move_SPARK[];
extern const u8 Move_FURY_CUTTER[];
extern const u8 Move_STEEL_WING[];
extern const u8 Move_MEAN_LOOK[];
extern const u8 Move_ATTRACT[];
extern const u8 Move_SLEEP_TALK[];
extern const u8 Move_HEAL_BELL[];
extern const u8 Move_RETURN[];
extern const u8 Move_PRESENT[];
extern const u8 Move_FRUSTRATION[];
extern const u8 Move_SAFEGUARD[];
extern const u8 Move_PAIN_SPLIT[];
extern const u8 Move_SACRED_FIRE[];
extern const u8 Move_MAGNITUDE[];
extern const u8 Move_DYNAMIC_PUNCH[];
extern const u8 Move_MEGAHORN[];
extern const u8 Move_DRAGON_BREATH[];
extern const u8 Move_BATON_PASS[];
extern const u8 Move_ENCORE[];
extern const u8 Move_PURSUIT[];
extern const u8 Move_RAPID_SPIN[];
extern const u8 Move_SWEET_SCENT[];
extern const u8 Move_IRON_TAIL[];
extern const u8 Move_METAL_CLAW[];
extern const u8 Move_VITAL_THROW[];
extern const u8 Move_MORNING_SUN[];
extern const u8 Move_SYNTHESIS[];
extern const u8 Move_MOONLIGHT[];
extern const u8 Move_HIDDEN_POWER[];
extern const u8 Move_CROSS_CHOP[];
extern const u8 Move_TWISTER[];
extern const u8 Move_RAIN_DANCE[];
extern const u8 Move_SUNNY_DAY[];
extern const u8 Move_CRUNCH[];
extern const u8 Move_MIRROR_COAT[];
extern const u8 Move_PSYCH_UP[];
extern const u8 Move_EXTREME_SPEED[];
extern const u8 Move_ANCIENT_POWER[];
extern const u8 Move_SHADOW_BALL[];
extern const u8 Move_FUTURE_SIGHT[];
extern const u8 Move_ROCK_SMASH[];
extern const u8 Move_WHIRLPOOL[];
extern const u8 Move_BEAT_UP[];
extern const u8 Move_FAKE_OUT[];
extern const u8 Move_UPROAR[];
extern const u8 Move_STOCKPILE[];
extern const u8 Move_SPIT_UP[];
extern const u8 Move_SWALLOW[];
extern const u8 Move_HEAT_WAVE[];
extern const u8 Move_HAIL[];
extern const u8 Move_TORMENT[];
extern const u8 Move_FLATTER[];
extern const u8 Move_WILL_O_WISP[];
extern const u8 Move_MEMENTO[];
extern const u8 Move_FACADE[];
extern const u8 Move_FOCUS_PUNCH[];
extern const u8 Move_SMELLING_SALTS[];
extern const u8 Move_FOLLOW_ME[];
extern const u8 Move_NATURE_POWER[];
extern const u8 Move_CHARGE[];
extern const u8 Move_TAUNT[];
extern const u8 Move_HELPING_HAND[];
extern const u8 Move_TRICK[];
extern const u8 Move_ROLE_PLAY[];
extern const u8 Move_WISH[];
extern const u8 Move_ASSIST[];
extern const u8 Move_INGRAIN[];
extern const u8 Move_SUPERPOWER[];
extern const u8 Move_MAGIC_COAT[];
extern const u8 Move_RECYCLE[];
extern const u8 Move_REVENGE[];
extern const u8 Move_BRICK_BREAK[];
extern const u8 Move_YAWN[];
extern const u8 Move_KNOCK_OFF[];
extern const u8 Move_ENDEAVOR[];
extern const u8 Move_ERUPTION[];
extern const u8 Move_SKILL_SWAP[];
extern const u8 Move_IMPRISON[];
extern const u8 Move_REFRESH[];
extern const u8 Move_GRUDGE[];
extern const u8 Move_SNATCH[];
extern const u8 Move_SECRET_POWER[];
extern const u8 Move_DIVE[];
extern const u8 Move_ARM_THRUST[];
extern const u8 Move_CAMOUFLAGE[];
extern const u8 Move_TAIL_GLOW[];
extern const u8 Move_LUSTER_PURGE[];
extern const u8 Move_MIST_BALL[];
extern const u8 Move_FEATHER_DANCE[];
extern const u8 Move_TEETER_DANCE[];
extern const u8 Move_BLAZE_KICK[];
extern const u8 Move_MUD_SPORT[];
extern const u8 Move_ICE_BALL[];
extern const u8 Move_NEEDLE_ARM[];
extern const u8 Move_SLACK_OFF[];
extern const u8 Move_HYPER_VOICE[];
extern const u8 Move_POISON_FANG[];
extern const u8 Move_CRUSH_CLAW[];
extern const u8 Move_BLAST_BURN[];
extern const u8 Move_HYDRO_CANNON[];
extern const u8 Move_METEOR_MASH[];
extern const u8 Move_ASTONISH[];
extern const u8 Move_WEATHER_BALL[];
extern const u8 Move_AROMATHERAPY[];
extern const u8 Move_FAKE_TEARS[];
extern const u8 Move_AIR_CUTTER[];
extern const u8 Move_OVERHEAT[];
extern const u8 Move_ODOR_SLEUTH[];
extern const u8 Move_ROCK_TOMB[];
extern const u8 Move_SILVER_WIND[];
extern const u8 Move_METAL_SOUND[];
extern const u8 Move_GRASS_WHISTLE[];
extern const u8 Move_TICKLE[];
extern const u8 Move_COSMIC_POWER[];
extern const u8 Move_WATER_SPOUT[];
extern const u8 Move_SIGNAL_BEAM[];
extern const u8 Move_SHADOW_PUNCH[];
extern const u8 Move_EXTRASENSORY[];
extern const u8 Move_SKY_UPPERCUT[];
extern const u8 Move_SAND_TOMB[];
extern const u8 Move_SHEER_COLD[];
extern const u8 Move_MUDDY_WATER[];
extern const u8 Move_BULLET_SEED[];
extern const u8 Move_AERIAL_ACE[];
extern const u8 Move_ICICLE_SPEAR[];
extern const u8 Move_IRON_DEFENSE[];
extern const u8 Move_BLOCK[];
extern const u8 Move_HOWL[];
extern const u8 Move_DRAGON_CLAW[];
extern const u8 Move_FRENZY_PLANT[];
extern const u8 Move_BULK_UP[];
extern const u8 Move_BOUNCE[];
extern const u8 Move_MUD_SHOT[];
extern const u8 Move_POISON_TAIL[];
extern const u8 Move_COVET[];
extern const u8 Move_VOLT_TACKLE[];
extern const u8 Move_MAGICAL_LEAF[];
extern const u8 Move_WATER_SPORT[];
extern const u8 Move_CALM_MIND[];
extern const u8 Move_LEAF_BLADE[];
extern const u8 Move_DRAGON_DANCE[];
extern const u8 Move_ROCK_BLAST[];
extern const u8 Move_SHOCK_WAVE[];
extern const u8 Move_WATER_PULSE[];
extern const u8 Move_DOOM_DESIRE[];
extern const u8 Move_PSYCHO_BOOST[];
extern const u8 Move_ROOST[];
extern const u8 Move_GRAVITY[];
extern const u8 Move_MIRACLE_EYE[];
extern const u8 Move_WAKE_UP_SLAP[];
extern const u8 Move_HAMMER_ARM[];
extern const u8 Move_GYRO_BALL[];
extern const u8 Move_HEALING_WISH[];
extern const u8 Move_BRINE[];
extern const u8 Move_NATURAL_GIFT[];
extern const u8 Move_FEINT[];
extern const u8 Move_PLUCK[];
extern const u8 Move_TAILWIND[];
extern const u8 Move_ACUPRESSURE[];
extern const u8 Move_METAL_BURST[];
extern const u8 Move_U_TURN[];
extern const u8 Move_CLOSE_COMBAT[];
extern const u8 Move_PAYBACK[];
extern const u8 Move_ASSURANCE[];
extern const u8 Move_EMBARGO[];
extern const u8 Move_FLING[];
extern const u8 Move_PSYCHO_SHIFT[];
extern const u8 Move_TRUMP_CARD[];
extern const u8 Move_HEAL_BLOCK[];
extern const u8 Move_WRING_OUT[];
extern const u8 Move_POWER_TRICK[];
extern const u8 Move_GASTRO_ACID[];
extern const u8 Move_LUCKY_CHANT[];
extern const u8 Move_ME_FIRST[];
extern const u8 Move_COPYCAT[];
extern const u8 Move_POWER_SWAP[];
extern const u8 Move_GUARD_SWAP[];
extern const u8 Move_PUNISHMENT[];
extern const u8 Move_LAST_RESORT[];
extern const u8 Move_WORRY_SEED[];
extern const u8 Move_SUCKER_PUNCH[];
extern const u8 Move_TOXIC_SPIKES[];
extern const u8 Move_HEART_SWAP[];
extern const u8 Move_AQUA_RING[];
extern const u8 Move_MAGNET_RISE[];
extern const u8 Move_FLARE_BLITZ[];
extern const u8 Move_FORCE_PALM[];
extern const u8 Move_AURA_SPHERE[];
extern const u8 Move_ROCK_POLISH[];
extern const u8 Move_POISON_JAB[];
extern const u8 Move_DARK_PULSE[];
extern const u8 Move_NIGHT_SLASH[];
extern const u8 Move_AQUA_TAIL[];
extern const u8 Move_SEED_BOMB[];
extern const u8 Move_AIR_SLASH[];
extern const u8 Move_X_SCISSOR[];
extern const u8 Move_BUG_BUZZ[];
extern const u8 Move_DRAGON_PULSE[];
extern const u8 Move_DRAGON_RUSH[];
extern const u8 Move_POWER_GEM[];
extern const u8 Move_DRAIN_PUNCH[];
extern const u8 Move_VACUUM_WAVE[];
extern const u8 Move_FOCUS_BLAST[];
extern const u8 Move_ENERGY_BALL[];
extern const u8 Move_BRAVE_BIRD[];
extern const u8 Move_EARTH_POWER[];
extern const u8 Move_SWITCHEROO[];
extern const u8 Move_GIGA_IMPACT[];
extern const u8 Move_NASTY_PLOT[];
extern const u8 Move_BULLET_PUNCH[];
extern const u8 Move_AVALANCHE[];
extern const u8 Move_ICE_SHARD[];
extern const u8 Move_SHADOW_CLAW[];
extern const u8 Move_THUNDER_FANG[];
extern const u8 Move_ICE_FANG[];
extern const u8 Move_FIRE_FANG[];
extern const u8 Move_SHADOW_SNEAK[];
extern const u8 Move_MUD_BOMB[];
extern const u8 Move_PSYCHO_CUT[];
extern const u8 Move_ZEN_HEADBUTT[];
extern const u8 Move_MIRROR_SHOT[];
extern const u8 Move_FLASH_CANNON[];
extern const u8 Move_ROCK_CLIMB[];
extern const u8 Move_DEFOG[];
extern const u8 Move_TRICK_ROOM[];
extern const u8 Move_DRACO_METEOR[];
extern const u8 Move_DISCHARGE[];
extern const u8 Move_LAVA_PLUME[];
extern const u8 Move_LEAF_STORM[];
extern const u8 Move_POWER_WHIP[];
extern const u8 Move_ROCK_WRECKER[];
extern const u8 Move_CROSS_POISON[];
extern const u8 Move_GUNK_SHOT[];
extern const u8 Move_IRON_HEAD[];
extern const u8 Move_MAGNET_BOMB[];
extern const u8 Move_STONE_EDGE[];
extern const u8 Move_CAPTIVATE[];
extern const u8 Move_STEALTH_ROCK[];
extern const u8 Move_GRASS_KNOT[];
extern const u8 Move_CHATTER[];
extern const u8 Move_JUDGMENT[];
extern const u8 Move_BUG_BITE[];
extern const u8 Move_CHARGE_BEAM[];
extern const u8 Move_WOOD_HAMMER[];
extern const u8 Move_AQUA_JET[];
extern const u8 Move_ATTACK_ORDER[];
extern const u8 Move_DEFEND_ORDER[];
extern const u8 Move_HEAL_ORDER[];
extern const u8 Move_HEAD_SMASH[];
extern const u8 Move_DOUBLE_HIT[];
extern const u8 Move_ROAR_OF_TIME[];
extern const u8 Move_SPACIAL_REND[];
extern const u8 Move_LUNAR_DANCE[];
extern const u8 Move_CRUSH_GRIP[];
extern const u8 Move_MAGMA_STORM[];
extern const u8 Move_DARK_VOID[];
extern const u8 Move_SEED_FLARE[];
extern const u8 Move_OMINOUS_WIND[];
extern const u8 Move_SHADOW_FORCE[];
extern const u8 Move_HONE_CLAWS[];
extern const u8 Move_WIDE_GUARD[];
extern const u8 Move_GUARD_SPLIT[];
extern const u8 Move_POWER_SPLIT[];
extern const u8 Move_WONDER_ROOM[];
extern const u8 Move_PSYSHOCK[];
extern const u8 Move_VENOSHOCK[];
extern const u8 Move_AUTOTOMIZE[];
extern const u8 Move_RAGE_POWDER[];
extern const u8 Move_TELEKINESIS[];
extern const u8 Move_MAGIC_ROOM[];
extern const u8 Move_SMACK_DOWN[];
extern const u8 Move_STORM_THROW[];
extern const u8 Move_FLAME_BURST[];
extern const u8 Move_SLUDGE_WAVE[];
extern const u8 Move_QUIVER_DANCE[];
extern const u8 Move_HEAVY_SLAM[];
extern const u8 Move_SYNCHRONOISE[];
extern const u8 Move_ELECTRO_BALL[];
extern const u8 Move_SOAK[];
extern const u8 Move_FLAME_CHARGE[];
extern const u8 Move_COIL[];
extern const u8 Move_LOW_SWEEP[];
extern const u8 Move_ACID_SPRAY[];
extern const u8 Move_FOUL_PLAY[];
extern const u8 Move_SIMPLE_BEAM[];
extern const u8 Move_ENTRAINMENT[];
extern const u8 Move_AFTER_YOU[];
extern const u8 Move_ROUND[];
extern const u8 Move_ECHOED_VOICE[];
extern const u8 Move_CHIP_AWAY[];
extern const u8 Move_CLEAR_SMOG[];
extern const u8 Move_STORED_POWER[];
extern const u8 Move_QUICK_GUARD[];
extern const u8 Move_ALLY_SWITCH[];
extern const u8 Move_SCALD[];
extern const u8 Move_SHELL_SMASH[];
extern const u8 Move_HEAL_PULSE[];
extern const u8 Move_HEX[];
extern const u8 Move_SKY_DROP[];
extern const u8 Move_SHIFT_GEAR[];
extern const u8 Move_CIRCLE_THROW[];
extern const u8 Move_INCINERATE[];
extern const u8 Move_QUASH[];
extern const u8 Move_ACROBATICS[];
extern const u8 Move_REFLECT_TYPE[];
extern const u8 Move_RETALIATE[];
extern const u8 Move_FINAL_GAMBIT[];
extern const u8 Move_BESTOW[];
extern const u8 Move_INFERNO[];
extern const u8 Move_WATER_PLEDGE[];
extern const u8 Move_FIRE_PLEDGE[];
extern const u8 Move_GRASS_PLEDGE[];
extern const u8 Move_VOLT_SWITCH[];
extern const u8 Move_STRUGGLE_BUG[];
extern const u8 Move_BULLDOZE[];
extern const u8 Move_FROST_BREATH[];
extern const u8 Move_DRAGON_TAIL[];
extern const u8 Move_WORK_UP[];
extern const u8 Move_ELECTROWEB[];
extern const u8 Move_WILD_CHARGE[];
extern const u8 Move_DRILL_RUN[];
extern const u8 Move_DUAL_CHOP[];
extern const u8 Move_HEART_STAMP[];
extern const u8 Move_HORN_LEECH[];
extern const u8 Move_SACRED_SWORD[];
extern const u8 Move_RAZOR_SHELL[];
extern const u8 Move_HEAT_CRASH[];
extern const u8 Move_LEAF_TORNADO[];
extern const u8 Move_STEAMROLLER[];
extern const u8 Move_COTTON_GUARD[];
extern const u8 Move_NIGHT_DAZE[];
extern const u8 Move_PSYSTRIKE[];
extern const u8 Move_TAIL_SLAP[];
extern const u8 Move_HURRICANE[];
extern const u8 Move_HEAD_CHARGE[];
extern const u8 Move_GEAR_GRIND[];
extern const u8 Move_SEARING_SHOT[];
extern const u8 Move_TECHNO_BLAST[];
extern const u8 Move_RELIC_SONG[];
extern const u8 Move_SECRET_SWORD[];
extern const u8 Move_GLACIATE[];
extern const u8 Move_BOLT_STRIKE[];
extern const u8 Move_BLUE_FLARE[];
extern const u8 Move_FIERY_DANCE[];
extern const u8 Move_FREEZE_SHOCK[];
extern const u8 Move_ICE_BURN[];
extern const u8 Move_SNARL[];
extern const u8 Move_ICICLE_CRASH[];
extern const u8 Move_V_CREATE[];
extern const u8 Move_FUSION_FLARE[];
extern const u8 Move_FUSION_BOLT[];
extern const u8 Move_FLYING_PRESS[];
extern const u8 Move_MAT_BLOCK[];
extern const u8 Move_BELCH[];
extern const u8 Move_ROTOTILLER[];
extern const u8 Move_STICKY_WEB[];
extern const u8 Move_FELL_STINGER[];
extern const u8 Move_PHANTOM_FORCE[];
extern const u8 Move_TRICK_OR_TREAT[];
extern const u8 Move_NOBLE_ROAR[];
extern const u8 Move_ION_DELUGE[];
extern const u8 Move_PARABOLIC_CHARGE[];
extern const u8 Move_FORESTS_CURSE[];
extern const u8 Move_PETAL_BLIZZARD[];
extern const u8 Move_FREEZE_DRY[];
extern const u8 Move_DISARMING_VOICE[];
extern const u8 Move_PARTING_SHOT[];
extern const u8 Move_TOPSY_TURVY[];
extern const u8 Move_DRAINING_KISS[];
extern const u8 Move_CRAFTY_SHIELD[];
extern const u8 Move_FLOWER_SHIELD[];
extern const u8 Move_GRASSY_TERRAIN[];
extern const u8 Move_MISTY_TERRAIN[];
extern const u8 Move_ELECTRIFY[];
extern const u8 Move_PLAY_ROUGH[];
extern const u8 Move_FAIRY_WIND[];
extern const u8 Move_MOONBLAST[];
extern const u8 Move_BOOMBURST[];
extern const u8 Move_FAIRY_LOCK[];
extern const u8 Move_KINGS_SHIELD[];
extern const u8 Move_PLAY_NICE[];
extern const u8 Move_CONFIDE[];
extern const u8 Move_DIAMOND_STORM[];
extern const u8 Move_STEAM_ERUPTION[];
extern const u8 Move_HYPERSPACE_HOLE[];
extern const u8 Move_WATER_SHURIKEN[];
extern const u8 Move_MYSTICAL_FIRE[];
extern const u8 Move_SPIKY_SHIELD[];
extern const u8 Move_AROMATIC_MIST[];
extern const u8 Move_EERIE_IMPULSE[];
extern const u8 Move_VENOM_DRENCH[];
extern const u8 Move_POWDER[];
extern const u8 Move_GEOMANCY[];
extern const u8 Move_MAGNETIC_FLUX[];
extern const u8 Move_HAPPY_HOUR[];
extern const u8 Move_ELECTRIC_TERRAIN[];
extern const u8 Move_DAZZLING_GLEAM[];
extern const u8 Move_CELEBRATE[];
extern const u8 Move_HOLD_HANDS[];
extern const u8 Move_BABY_DOLL_EYES[];
extern const u8 Move_NUZZLE[];
extern const u8 Move_HOLD_BACK[];
extern const u8 Move_INFESTATION[];
extern const u8 Move_POWER_UP_PUNCH[];
extern const u8 Move_OBLIVION_WING[];
extern const u8 Move_THOUSAND_ARROWS[];
extern const u8 Move_THOUSAND_WAVES[];
extern const u8 Move_LANDS_WRATH[];
extern const u8 Move_LIGHT_OF_RUIN[];
extern const u8 Move_ORIGIN_PULSE[];
extern const u8 Move_PRECIPICE_BLADES[];
extern const u8 Move_DRAGON_ASCENT[];
extern const u8 Move_HYPERSPACE_FURY[];
extern const u8 Move_SHORE_UP[];
extern const u8 Move_FIRST_IMPRESSION[];
extern const u8 Move_BANEFUL_BUNKER[];
extern const u8 Move_SPIRIT_SHACKLE[];
extern const u8 Move_DARKEST_LARIAT[];
extern const u8 Move_SPARKLING_ARIA[];
extern const u8 Move_ICE_HAMMER[];
extern const u8 Move_FLORAL_HEALING[];
extern const u8 Move_HIGH_HORSEPOWER[];
extern const u8 Move_STRENGTH_SAP[];
extern const u8 Move_SOLAR_BLADE[];
extern const u8 Move_LEAFAGE[];
extern const u8 Move_SPOTLIGHT[];
extern const u8 Move_TOXIC_THREAD[];
extern const u8 Move_LASER_FOCUS[];
extern const u8 Move_GEAR_UP[];
extern const u8 Move_THROAT_CHOP[];
extern const u8 Move_POLLEN_PUFF[];
extern const u8 Move_ANCHOR_SHOT[];
extern const u8 Move_PSYCHIC_TERRAIN[];
extern const u8 Move_LUNGE[];
extern const u8 Move_FIRE_LASH[];
extern const u8 Move_POWER_TRIP[];
extern const u8 Move_BURN_UP[];
extern const u8 Move_SPEED_SWAP[];
extern const u8 Move_SMART_STRIKE[];
extern const u8 Move_PURIFY[];
extern const u8 Move_REVELATION_DANCE[];
extern const u8 Move_CORE_ENFORCER[];
extern const u8 Move_TROP_KICK[];
extern const u8 Move_INSTRUCT[];
extern const u8 Move_BEAK_BLAST[];
extern const u8 Move_CLANGING_SCALES[];
extern const u8 Move_DRAGON_HAMMER[];
extern const u8 Move_BRUTAL_SWING[];
extern const u8 Move_AURORA_VEIL[];
extern const u8 Move_SHELL_TRAP[];
extern const u8 Move_FLEUR_CANNON[];
extern const u8 Move_PSYCHIC_FANGS[];
extern const u8 Move_STOMPING_TANTRUM[];
extern const u8 Move_SHADOW_BONE[];
extern const u8 Move_ACCELEROCK[];
extern const u8 Move_LIQUIDATION[];
extern const u8 Move_PRISMATIC_LASER[];
extern const u8 Move_SPECTRAL_THIEF[];
extern const u8 Move_SUNSTEEL_STRIKE[];
extern const u8 Move_MOONGEIST_BEAM[];
extern const u8 Move_TEARFUL_LOOK[];
extern const u8 Move_ZING_ZAP[];
extern const u8 Move_NATURES_MADNESS[];
extern const u8 Move_MULTI_ATTACK[];
extern const u8 Move_MIND_BLOWN[];
extern const u8 Move_PLASMA_FISTS[];
extern const u8 Move_PHOTON_GEYSER[];
extern const u8 Move_ZIPPY_ZAP[];
extern const u8 Move_SPLISHY_SPLASH[];
extern const u8 Move_FLOATY_FALL[];
extern const u8 Move_PIKA_PAPOW[];
extern const u8 Move_BOUNCY_BUBBLE[];
extern const u8 Move_BUZZY_BUZZ[];
extern const u8 Move_SIZZLY_SLIDE[];
extern const u8 Move_GLITZY_GLOW[];
extern const u8 Move_BADDY_BAD[];
extern const u8 Move_SAPPY_SEED[];
extern const u8 Move_FREEZY_FROST[];
extern const u8 Move_SPARKLY_SWIRL[];
extern const u8 Move_VEEVEE_VOLLEY[];
extern const u8 Move_DOUBLE_IRON_BASH[];
extern const u8 Move_DYNAMAX_CANNON[];
extern const u8 Move_SNIPE_SHOT[];
extern const u8 Move_JAW_LOCK[];
extern const u8 Move_STUFF_CHEEKS[];
extern const u8 Move_NO_RETREAT[];
extern const u8 Move_TAR_SHOT[];
extern const u8 Move_MAGIC_POWDER[];
extern const u8 Move_DRAGON_DARTS[];
extern const u8 Move_TEATIME[];
extern const u8 Move_OCTOLOCK[];
extern const u8 Move_BOLT_BEAK[];
extern const u8 Move_FISHIOUS_REND[];
extern const u8 Move_COURT_CHANGE[];
extern const u8 Move_CLANGOROUS_SOUL[];
extern const u8 Move_BODY_PRESS[];
extern const u8 Move_DECORATE[];
extern const u8 Move_DRUM_BEATING[];
extern const u8 Move_SNAP_TRAP[];
extern const u8 Move_PYRO_BALL[];
extern const u8 Move_BEHEMOTH_BLADE[];
extern const u8 Move_BEHEMOTH_BASH[];
extern const u8 Move_AURA_WHEEL[];
extern const u8 Move_BREAKING_SWIPE[];
extern const u8 Move_BRANCH_POKE[];
extern const u8 Move_OVERDRIVE[];
extern const u8 Move_APPLE_ACID[];
extern const u8 Move_GRAV_APPLE[];
extern const u8 Move_SPIRIT_BREAK[];
extern const u8 Move_STRANGE_STEAM[];
extern const u8 Move_LIFE_DEW[];
extern const u8 Move_OBSTRUCT[];
extern const u8 Move_FALSE_SURRENDER[];
extern const u8 Move_METEOR_ASSAULT[];
extern const u8 Move_ETERNABEAM[];
extern const u8 Move_STEEL_BEAM[];
extern const u8 Move_EXPANDING_FORCE[];
extern const u8 Move_STEEL_ROLLER[];
extern const u8 Move_SCALE_SHOT[];
extern const u8 Move_METEOR_BEAM[];
extern const u8 Move_SHELL_SIDE_ARM[];
extern const u8 Move_MISTY_EXPLOSION[];
extern const u8 Move_GRASSY_GLIDE[];
extern const u8 Move_RISING_VOLTAGE[];
extern const u8 Move_TERRAIN_PULSE[];
extern const u8 Move_SKITTER_SMACK[];
extern const u8 Move_BURNING_JEALOUSY[];
extern const u8 Move_LASH_OUT[];
extern const u8 Move_POLTERGEIST[];
extern const u8 Move_CORROSIVE_GAS[];
extern const u8 Move_COACHING[];
extern const u8 Move_FLIP_TURN[];
extern const u8 Move_TRIPLE_AXEL[];
extern const u8 Move_DUAL_WINGBEAT[];
extern const u8 Move_SCORCHING_SANDS[];
extern const u8 Move_JUNGLE_HEALING[];
extern const u8 Move_WICKED_BLOW[];
extern const u8 Move_SURGING_STRIKES[];
extern const u8 Move_THUNDER_CAGE[];
extern const u8 Move_DRAGON_ENERGY[];
extern const u8 Move_FREEZING_GLARE[];
extern const u8 Move_FIERY_WRATH[];
extern const u8 Move_THUNDEROUS_KICK[];
extern const u8 Move_GLACIAL_LANCE[];
extern const u8 Move_ASTRAL_BARRAGE[];
extern const u8 Move_EERIE_SPELL[];
extern const u8 Move_DIRE_CLAW[];
extern const u8 Move_PSYSHIELD_BASH[];
extern const u8 Move_POWER_SHIFT[];
extern const u8 Move_STONE_AXE[];
extern const u8 Move_SPRINGTIDE_STORM[];
extern const u8 Move_MYSTICAL_POWER[];
extern const u8 Move_RAGING_FURY[];
extern const u8 Move_WAVE_CRASH[];
extern const u8 Move_CHLOROBLAST[];
extern const u8 Move_MOUNTAIN_GALE[];
extern const u8 Move_VICTORY_DANCE[];
extern const u8 Move_HEADLONG_RUSH[];
extern const u8 Move_BARB_BARRAGE[];
extern const u8 Move_ESPER_WING[];
extern const u8 Move_BITTER_MALICE[];
extern const u8 Move_SHELTER[];
extern const u8 Move_TRIPLE_ARROWS[];
extern const u8 Move_INFERNAL_PARADE[];
extern const u8 Move_CEASELESS_EDGE[];
extern const u8 Move_BLEAKWIND_STORM[];
extern const u8 Move_WILDBOLT_STORM[];
extern const u8 Move_SANDSEAR_STORM[];
extern const u8 Move_LUNAR_BLESSING[];
extern const u8 Move_TAKE_HEART[];
extern const u8 Move_TERA_BLAST[];
extern const u8 Move_SILK_TRAP[];
extern const u8 Move_AXE_KICK[];
extern const u8 Move_LAST_RESPECTS[];
extern const u8 Move_LUMINA_CRASH[];
extern const u8 Move_ORDER_UP[];
extern const u8 Move_JET_PUNCH[];
extern const u8 Move_SPICY_EXTRACT[];
extern const u8 Move_SPIN_OUT[];
extern const u8 Move_POPULATION_BOMB[];
extern const u8 Move_ICE_SPINNER[];
extern const u8 Move_GLAIVE_RUSH[];
extern const u8 Move_REVIVAL_BLESSING[];
extern const u8 Move_SALT_CURE[];
extern const u8 Move_TRIPLE_DIVE[];
extern const u8 Move_MORTAL_SPIN[];
extern const u8 Move_DOODLE[];
extern const u8 Move_FILLET_AWAY[];
extern const u8 Move_KOWTOW_CLEAVE[];
extern const u8 Move_FLOWER_TRICK[];
extern const u8 Move_TORCH_SONG[];
extern const u8 Move_AQUA_STEP[];
extern const u8 Move_RAGING_BULL[];
extern const u8 Move_MAKE_IT_RAIN[];
extern const u8 Move_RUINATION[];
extern const u8 Move_COLLISION_COURSE[];
extern const u8 Move_ELECTRO_DRIFT[];
extern const u8 Move_SHED_TAIL[];
extern const u8 Move_CHILLY_RECEPTION[];
extern const u8 Move_TIDY_UP[];
extern const u8 Move_SNOWSCAPE[];
extern const u8 Move_POUNCE[];
extern const u8 Move_TRAILBLAZE[];
extern const u8 Move_CHILLING_WATER[];
extern const u8 Move_HYPER_DRILL[];
extern const u8 Move_TWIN_BEAM[];
extern const u8 Move_RAGE_FIST[];
extern const u8 Move_ARMOR_CANNON[];
extern const u8 Move_BITTER_BLADE[];
extern const u8 Move_DOUBLE_SHOCK[];
extern const u8 Move_GIGATON_HAMMER[];
extern const u8 Move_COMEUPPANCE[];
extern const u8 Move_AQUA_CUTTER[];
extern const u8 Move_BLAZING_TORQUE[];
extern const u8 Move_WICKED_TORQUE[];
extern const u8 Move_NOXIOUS_TORQUE[];
extern const u8 Move_COMBAT_TORQUE[];
extern const u8 Move_MAGICAL_TORQUE[];
extern const u8 Move_PSYBLADE[];
extern const u8 Move_HYDRO_STEAM[];
extern const u8 Move_BLOOD_MOON[];
extern const u8 Move_MATCHA_GOTCHA[];
extern const u8 Move_SYRUP_BOMB[];
extern const u8 Move_IVY_CUDGEL[];
extern const u8 Move_ELECTRO_SHOT[];
extern const u8 Move_TERA_STARSTORM[];
extern const u8 Move_FICKLE_BEAM[];
extern const u8 Move_BURNING_BULWARK[];
extern const u8 Move_THUNDERCLAP[];
extern const u8 Move_MIGHTY_CLEAVE[];
extern const u8 Move_TACHYON_CUTTER[];
extern const u8 Move_HARD_PRESS[];
extern const u8 Move_DRAGON_CHEER[];
extern const u8 Move_ALLURING_VOICE[];
extern const u8 Move_TEMPER_FLARE[];
extern const u8 Move_SUPERCELL_SLAM[];
extern const u8 Move_PSYCHIC_NOISE[];
extern const u8 Move_UPPER_HAND[];
extern const u8 Move_MALIGNANT_CHAIN[];
extern const u8 Move_BREAKNECK_BLITZ[];
extern const u8 Move_ALL_OUT_PUMMELING[];
extern const u8 Move_SUPERSONIC_SKYSTRIKE[];
extern const u8 Move_ACID_DOWNPOUR[];
extern const u8 Move_TECTONIC_RAGE[];
extern const u8 Move_CONTINENTAL_CRUSH[];
extern const u8 Move_SAVAGE_SPIN_OUT[];
extern const u8 Move_NEVER_ENDING_NIGHTMARE[];
extern const u8 Move_CORKSCREW_CRASH[];
extern const u8 Move_INFERNO_OVERDRIVE[];
extern const u8 Move_HYDRO_VORTEX[];
extern const u8 Move_BLOOM_DOOM[];
extern const u8 Move_GIGAVOLT_HAVOC[];
extern const u8 Move_SHATTERED_PSYCHE[];
extern const u8 Move_SUBZERO_SLAMMER[];
extern const u8 Move_DEVASTATING_DRAKE[];
extern const u8 Move_BLACK_HOLE_ECLIPSE[];
extern const u8 Move_TWINKLE_TACKLE[];
extern const u8 Move_CATASTROPIKA[];
extern const u8 Move_10000000_VOLT_THUNDERBOLT[];
extern const u8 Move_STOKED_SPARKSURFER[];
extern const u8 Move_EXTREME_EVOBOOST[];
extern const u8 Move_PULVERIZING_PANCAKE[];
extern const u8 Move_GENESIS_SUPERNOVA[];
extern const u8 Move_SINISTER_ARROW_RAID[];
extern const u8 Move_MALICIOUS_MOONSAULT[];
extern const u8 Move_OCEANIC_OPERETTA[];
extern const u8 Move_SPLINTERED_STORMSHARDS[];
extern const u8 Move_LETS_SNUGGLE_FOREVER[];
extern const u8 Move_CLANGOROUS_SOULBLAZE[];
extern const u8 Move_GUARDIAN_OF_ALOLA[];
extern const u8 Move_SEARING_SUNRAZE_SMASH[];
extern const u8 Move_MENACING_MOONRAZE_MAELSTROM[];
extern const u8 Move_LIGHT_THAT_BURNS_THE_SKY[];
extern const u8 Move_SOUL_STEALING_7_STAR_STRIKE[];
extern const u8 Move_MAX_GUARD[];
extern const u8 Move_MAX_FLARE[];
extern const u8 Move_MAX_FLUTTERBY[];
extern const u8 Move_MAX_LIGHTNING[];
extern const u8 Move_MAX_STRIKE[];
extern const u8 Move_MAX_KNUCKLE[];
extern const u8 Move_MAX_PHANTASM[];
extern const u8 Move_MAX_HAILSTORM[];
extern const u8 Move_MAX_OOZE[];
extern const u8 Move_MAX_GEYSER[];
extern const u8 Move_MAX_AIRSTREAM[];
extern const u8 Move_MAX_STARFALL[];
extern const u8 Move_MAX_WYRMWIND[];
extern const u8 Move_MAX_MINDSTORM[];
extern const u8 Move_MAX_ROCKFALL[];
extern const u8 Move_MAX_QUAKE[];
extern const u8 Move_MAX_DARKNESS[];
extern const u8 Move_MAX_OVERGROWTH[];
extern const u8 Move_MAX_STEELSPIKE[];
extern const u8 Move_G_MAX_VINE_LASH[];
extern const u8 Move_G_MAX_WILDFIRE[];
extern const u8 Move_G_MAX_CANNONADE[];
extern const u8 Move_G_MAX_BEFUDDLE[];
extern const u8 Move_G_MAX_VOLT_CRASH[];
extern const u8 Move_G_MAX_GOLD_RUSH[];
extern const u8 Move_G_MAX_CHI_STRIKE[];
extern const u8 Move_G_MAX_TERROR[];
extern const u8 Move_G_MAX_FOAM_BURST[];
extern const u8 Move_G_MAX_RESONANCE[];
extern const u8 Move_G_MAX_CUDDLE[];
extern const u8 Move_G_MAX_REPLENISH[];
extern const u8 Move_G_MAX_MALODOR[];
extern const u8 Move_G_MAX_MELTDOWN[];
extern const u8 Move_G_MAX_DRUM_SOLO[];
extern const u8 Move_G_MAX_FIREBALL[];
extern const u8 Move_G_MAX_HYDROSNIPE[];
extern const u8 Move_G_MAX_WIND_RAGE[];
extern const u8 Move_G_MAX_GRAVITAS[];
extern const u8 Move_G_MAX_STONESURGE[];
extern const u8 Move_G_MAX_VOLCALITH[];
extern const u8 Move_G_MAX_TARTNESS[];
extern const u8 Move_G_MAX_SWEETNESS[];
extern const u8 Move_G_MAX_SANDBLAST[];
extern const u8 Move_G_MAX_STUN_SHOCK[];
extern const u8 Move_G_MAX_CENTIFERNO[];
extern const u8 Move_G_MAX_SMITE[];
extern const u8 Move_G_MAX_SNOOZE[];
extern const u8 Move_G_MAX_FINALE[];
extern const u8 Move_G_MAX_STEELSURGE[];
extern const u8 Move_G_MAX_DEPLETION[];
extern const u8 Move_G_MAX_ONE_BLOW[];
extern const u8 Move_G_MAX_RAPID_FLOW[];
#endif // GUARD_BATTLE_ANIM_SCRIPTS_H

View File

@ -144,5 +144,6 @@ void TryAddLastUsedBallItemSprites(void);
void SwapBallToDisplay(bool32 sameBall);
void ArrowsChangeColorLastBallCycle(bool32 showArrows);
void UpdateAbilityPopup(u8 battlerId);
void CategoryIcons_LoadSpritesGfx(void);
#endif // GUARD_BATTLE_INTERFACE_H

View File

@ -78,6 +78,7 @@ extern const struct SpriteTemplate gUnusedBattleInitSprite;
extern const struct OamData gOamData_BattleSpriteOpponentSide;
extern const struct OamData gOamData_BattleSpritePlayerSide;
extern const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES];
extern const uq4_12_t gTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES];
extern const u8 gStatusConditionString_PoisonJpn[8];
extern const u8 gStatusConditionString_SleepJpn[8];

View File

@ -66,6 +66,10 @@ enum {
#define IS_WHOLE_SIDE_ALIVE(battler) ((IsBattlerAlive(battler) && IsBattlerAlive(BATTLE_PARTNER(battler))))
#define IS_ALIVE_AND_PRESENT(battler) (IsBattlerAlive(battler) && IsBattlerSpritePresent(battler))
// Lowest and highest percentages used for damage roll calculations
#define DMG_ROLL_PERCENT_LO 85
#define DMG_ROLL_PERCENT_HI 100
// for Natural Gift and Fling
struct TypePower
{

View File

@ -43,7 +43,7 @@
#define B_GHOSTS_ESCAPE GEN_LATEST // In Gen6+, abilities like Shadow Tag or moves like Mean Look fail on Ghost-type Pokémon. They can also escape any Wild Battle.
#define B_PARALYZE_ELECTRIC GEN_LATEST // In Gen6+, Electric-type Pokémon can't be paralyzed.
#define B_POWDER_GRASS GEN_LATEST // In Gen6+, Grass-type Pokémon are immune to powder and spore moves.
#define B_UPDATED_TYPE_MATCHUPS GEN_LATEST // Updates Type matchups. Refer to sTypeEffectivenessTable for details.
#define B_UPDATED_TYPE_MATCHUPS GEN_LATEST // Updates Type matchups. src/data/types_info.h for details.
#define B_PRANKSTER_DARK_TYPES GEN_LATEST // In Gen7+, Prankster-elevated status moves do not affect Dark type Pokémon.
#define B_SHEER_COLD_IMMUNITY GEN_LATEST // In Gen7+, Ice-types are immune to Sheer Cold
#define B_ROOST_PURE_FLYING GEN_LATEST // In Gen5+, Roost makes pure Flying-types into Normal-type.

View File

@ -477,6 +477,7 @@
#define B_WIN_VS_OUTCOME_DRAW 21
#define B_WIN_VS_OUTCOME_LEFT 22
#define B_WIN_VS_OUTCOME_RIGHT 23
#define B_WIN_MOVE_DESCRIPTION 24
// The following are duplicate id values for windows that Battle Arena uses differently.
#define ARENA_WIN_PLAYER_NAME 15

View File

@ -118,125 +118,123 @@
#define VARIOUS_PLAY_TRAINER_DEFEATED_MUSIC 26
#define VARIOUS_STAT_TEXT_BUFFER 27
#define VARIOUS_SWITCHIN_ABILITIES 28
#define VARIOUS_SAVE_TARGET 29
#define VARIOUS_RESTORE_TARGET 30
#define VARIOUS_INSTANT_HP_DROP 31
#define VARIOUS_CLEAR_STATUS 32
#define VARIOUS_RESTORE_PP 33
#define VARIOUS_TRY_ACTIVATE_MOXIE 34
#define VARIOUS_TRY_ACTIVATE_FELL_STINGER 35
#define VARIOUS_PLAY_MOVE_ANIMATION 36
#define VARIOUS_SET_LUCKY_CHANT 37
#define VARIOUS_SUCKER_PUNCH_CHECK 38
#define VARIOUS_SET_SIMPLE_BEAM 39
#define VARIOUS_TRY_ENTRAINMENT 40
#define VARIOUS_SET_LAST_USED_ABILITY 41
#define VARIOUS_INVERT_STAT_STAGES 42
#define VARIOUS_TRY_ME_FIRST 43
#define VARIOUS_JUMP_IF_BATTLE_END 44
#define VARIOUS_TRY_ELECTRIFY 45
#define VARIOUS_TRY_REFLECT_TYPE 46
#define VARIOUS_TRY_SOAK 47
#define VARIOUS_HANDLE_MEGA_EVO 48
#define VARIOUS_TRY_LAST_RESORT 49
#define VARIOUS_SET_ARG_TO_BATTLE_DAMAGE 50
#define VARIOUS_TRY_HIT_SWITCH_TARGET 51
#define VARIOUS_TRY_AUTOTOMIZE 52
#define VARIOUS_ABILITY_POPUP 53
#define VARIOUS_JUMP_IF_TARGET_ALLY 54
#define VARIOUS_TRY_SYNCHRONOISE 55
#define VARIOUS_PSYCHO_SHIFT 56
#define VARIOUS_CURE_STATUS 57
#define VARIOUS_POWER_TRICK 58
#define VARIOUS_AFTER_YOU 59
#define VARIOUS_BESTOW 60
#define VARIOUS_JUMP_IF_NOT_GROUNDED 61
#define VARIOUS_HANDLE_TRAINER_SLIDE_MSG 62
#define VARIOUS_TRY_TRAINER_SLIDE_MSG_FIRST_OFF 63
#define VARIOUS_TRY_TRAINER_SLIDE_MSG_LAST_ON 64
#define VARIOUS_SET_AURORA_VEIL 65
#define VARIOUS_TRY_THIRD_TYPE 66
#define VARIOUS_ACUPRESSURE 67
#define VARIOUS_SET_POWDER 68
#define VARIOUS_SPECTRAL_THIEF 69
#define VARIOUS_GRAVITY_ON_AIRBORNE_MONS 70
#define VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS 71
#define VARIOUS_JUMP_IF_ROAR_FAILS 72
#define VARIOUS_TRY_INSTRUCT 73
#define VARIOUS_JUMP_IF_NOT_BERRY 74
#define VARIOUS_TRACE_ABILITY 75
#define VARIOUS_UPDATE_NICK 76
#define VARIOUS_TRY_ILLUSION_OFF 77
#define VARIOUS_SET_SPRITEIGNORE0HP 78
#define VARIOUS_HANDLE_FORM_CHANGE 79
#define VARIOUS_GET_STAT_VALUE 80
#define VARIOUS_JUMP_IF_FULL_HP 81
#define VARIOUS_LOSE_TYPE 82
#define VARIOUS_TRY_ACTIVATE_SOULHEART 83
#define VARIOUS_TRY_ACTIVATE_RECEIVER 84
#define VARIOUS_TRY_ACTIVATE_BEAST_BOOST 85
#define VARIOUS_TRY_FRISK 86
#define VARIOUS_JUMP_IF_SHIELDS_DOWN_PROTECTED 87
#define VARIOUS_TRY_FAIRY_LOCK 88
#define VARIOUS_JUMP_IF_NO_ALLY 89
#define VARIOUS_POISON_TYPE_IMMUNITY 90
#define VARIOUS_JUMP_IF_HOLD_EFFECT 91
#define VARIOUS_INFATUATE_WITH_BATTLER 92
#define VARIOUS_SET_LAST_USED_ITEM 93
#define VARIOUS_PARALYZE_TYPE_IMMUNITY 94
#define VARIOUS_JUMP_IF_ABSENT 95
#define VARIOUS_DESTROY_ABILITY_POPUP 96
#define VARIOUS_TOTEM_BOOST 97
#define VARIOUS_TRY_ACTIVATE_GRIM_NEIGH 98
#define VARIOUS_MOVEEND_ITEM_EFFECTS 99
#define VARIOUS_TERRAIN_SEED 100
#define VARIOUS_MAKE_INVISIBLE 101
#define VARIOUS_ROOM_SERVICE 102
#define VARIOUS_EERIE_SPELL_PP_REDUCE 103
#define VARIOUS_JUMP_IF_TEAM_HEALTHY 104
#define VARIOUS_TRY_HEAL_QUARTER_HP 105
#define VARIOUS_REMOVE_TERRAIN 106
#define VARIOUS_JUMP_IF_PRANKSTER_BLOCKED 107
#define VARIOUS_TRY_TO_CLEAR_PRIMAL_WEATHER 108
#define VARIOUS_GET_ROTOTILLER_TARGETS 109
#define VARIOUS_JUMP_IF_NOT_ROTOTILLER_AFFECTED 110
#define VARIOUS_TRY_ACTIVATE_BATTLE_BOND 111
#define VARIOUS_CONSUME_BERRY 112
#define VARIOUS_JUMP_IF_CANT_REVERT_TO_PRIMAL 113
#define VARIOUS_JUMP_IF_SPECIES 114
#define VARIOUS_UPDATE_ABILITY_POPUP 115
#define VARIOUS_JUMP_IF_WEATHER_AFFECTED 116
#define VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED 117
#define VARIOUS_SET_ATTACKER_STICKY_WEB_USER 118
#define VARIOUS_SHELL_SIDE_ARM_CHECK 119
#define VARIOUS_TRY_NO_RETREAT 120
#define VARIOUS_TRY_TAR_SHOT 121
#define VARIOUS_CAN_TAR_SHOT_WORK 122
#define VARIOUS_CHECK_POLTERGEIST 123
#define VARIOUS_CUT_1_3_HP_RAISE_STATS 124
#define VARIOUS_TRY_END_NEUTRALIZING_GAS 125
#define VARIOUS_JUMP_IF_UNDER_200 126
#define VARIOUS_SET_SKY_DROP 127
#define VARIOUS_CLEAR_SKY_DROP 128
#define VARIOUS_SKY_DROP_YAWN 129
#define VARIOUS_CURE_CERTAIN_STATUSES 130
#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 131
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 132
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 133
#define VARIOUS_SAVE_BATTLER_ITEM 134
#define VARIOUS_RESTORE_BATTLER_ITEM 135
#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 136
#define VARIOUS_SET_BEAK_BLAST 137
#define VARIOUS_SWAP_SIDE_STATUSES 138
#define VARIOUS_SWAP_STATS 139
#define VARIOUS_TEATIME_INVUL 140
#define VARIOUS_TEATIME_TARGETS 141
#define VARIOUS_TRY_WIND_RIDER_POWER 142
#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 143
#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 144
#define VARIOUS_STORE_HEALING_WISH 145
#define VARIOUS_HIT_SWITCH_TARGET_FAILED 146
#define VARIOUS_TRY_REVIVAL_BLESSING 147
#define VARIOUS_INSTANT_HP_DROP 29
#define VARIOUS_CLEAR_STATUS 30
#define VARIOUS_RESTORE_PP 31
#define VARIOUS_TRY_ACTIVATE_MOXIE 32
#define VARIOUS_TRY_ACTIVATE_FELL_STINGER 33
#define VARIOUS_PLAY_MOVE_ANIMATION 34
#define VARIOUS_SET_LUCKY_CHANT 35
#define VARIOUS_SUCKER_PUNCH_CHECK 36
#define VARIOUS_SET_SIMPLE_BEAM 37
#define VARIOUS_TRY_ENTRAINMENT 38
#define VARIOUS_SET_LAST_USED_ABILITY 39
#define VARIOUS_INVERT_STAT_STAGES 40
#define VARIOUS_TRY_ME_FIRST 41
#define VARIOUS_JUMP_IF_BATTLE_END 42
#define VARIOUS_TRY_ELECTRIFY 43
#define VARIOUS_TRY_REFLECT_TYPE 44
#define VARIOUS_TRY_SOAK 45
#define VARIOUS_HANDLE_MEGA_EVO 46
#define VARIOUS_TRY_LAST_RESORT 47
#define VARIOUS_SET_ARG_TO_BATTLE_DAMAGE 48
#define VARIOUS_TRY_HIT_SWITCH_TARGET 49
#define VARIOUS_TRY_AUTOTOMIZE 50
#define VARIOUS_ABILITY_POPUP 51
#define VARIOUS_JUMP_IF_TARGET_ALLY 52
#define VARIOUS_TRY_SYNCHRONOISE 53
#define VARIOUS_PSYCHO_SHIFT 54
#define VARIOUS_CURE_STATUS 55
#define VARIOUS_POWER_TRICK 56
#define VARIOUS_AFTER_YOU 57
#define VARIOUS_BESTOW 58
#define VARIOUS_JUMP_IF_NOT_GROUNDED 59
#define VARIOUS_HANDLE_TRAINER_SLIDE_MSG 60
#define VARIOUS_TRY_TRAINER_SLIDE_MSG_FIRST_OFF 61
#define VARIOUS_TRY_TRAINER_SLIDE_MSG_LAST_ON 62
#define VARIOUS_SET_AURORA_VEIL 63
#define VARIOUS_TRY_THIRD_TYPE 64
#define VARIOUS_ACUPRESSURE 65
#define VARIOUS_SET_POWDER 66
#define VARIOUS_SPECTRAL_THIEF 67
#define VARIOUS_GRAVITY_ON_AIRBORNE_MONS 68
#define VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS 69
#define VARIOUS_JUMP_IF_ROAR_FAILS 70
#define VARIOUS_TRY_INSTRUCT 71
#define VARIOUS_JUMP_IF_NOT_BERRY 72
#define VARIOUS_TRACE_ABILITY 73
#define VARIOUS_UPDATE_NICK 74
#define VARIOUS_TRY_ILLUSION_OFF 75
#define VARIOUS_SET_SPRITEIGNORE0HP 76
#define VARIOUS_HANDLE_FORM_CHANGE 77
#define VARIOUS_GET_STAT_VALUE 78
#define VARIOUS_JUMP_IF_FULL_HP 79
#define VARIOUS_LOSE_TYPE 80
#define VARIOUS_TRY_ACTIVATE_SOULHEART 81
#define VARIOUS_TRY_ACTIVATE_RECEIVER 82
#define VARIOUS_TRY_ACTIVATE_BEAST_BOOST 83
#define VARIOUS_TRY_FRISK 84
#define VARIOUS_JUMP_IF_SHIELDS_DOWN_PROTECTED 85
#define VARIOUS_TRY_FAIRY_LOCK 86
#define VARIOUS_JUMP_IF_NO_ALLY 87
#define VARIOUS_POISON_TYPE_IMMUNITY 88
#define VARIOUS_JUMP_IF_HOLD_EFFECT 89
#define VARIOUS_INFATUATE_WITH_BATTLER 90
#define VARIOUS_SET_LAST_USED_ITEM 91
#define VARIOUS_PARALYZE_TYPE_IMMUNITY 92
#define VARIOUS_JUMP_IF_ABSENT 93
#define VARIOUS_DESTROY_ABILITY_POPUP 94
#define VARIOUS_TOTEM_BOOST 95
#define VARIOUS_TRY_ACTIVATE_GRIM_NEIGH 96
#define VARIOUS_MOVEEND_ITEM_EFFECTS 97
#define VARIOUS_TERRAIN_SEED 98
#define VARIOUS_MAKE_INVISIBLE 99
#define VARIOUS_ROOM_SERVICE 100
#define VARIOUS_EERIE_SPELL_PP_REDUCE 101
#define VARIOUS_JUMP_IF_TEAM_HEALTHY 102
#define VARIOUS_TRY_HEAL_QUARTER_HP 103
#define VARIOUS_REMOVE_TERRAIN 104
#define VARIOUS_JUMP_IF_PRANKSTER_BLOCKED 105
#define VARIOUS_TRY_TO_CLEAR_PRIMAL_WEATHER 106
#define VARIOUS_GET_ROTOTILLER_TARGETS 107
#define VARIOUS_JUMP_IF_NOT_ROTOTILLER_AFFECTED 108
#define VARIOUS_TRY_ACTIVATE_BATTLE_BOND 109
#define VARIOUS_CONSUME_BERRY 110
#define VARIOUS_JUMP_IF_CANT_REVERT_TO_PRIMAL 111
#define VARIOUS_JUMP_IF_SPECIES 112
#define VARIOUS_UPDATE_ABILITY_POPUP 113
#define VARIOUS_JUMP_IF_WEATHER_AFFECTED 114
#define VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED 115
#define VARIOUS_SET_ATTACKER_STICKY_WEB_USER 116
#define VARIOUS_SHELL_SIDE_ARM_CHECK 117
#define VARIOUS_TRY_NO_RETREAT 118
#define VARIOUS_TRY_TAR_SHOT 119
#define VARIOUS_CAN_TAR_SHOT_WORK 120
#define VARIOUS_CHECK_POLTERGEIST 121
#define VARIOUS_CUT_1_3_HP_RAISE_STATS 122
#define VARIOUS_TRY_END_NEUTRALIZING_GAS 123
#define VARIOUS_JUMP_IF_UNDER_200 124
#define VARIOUS_SET_SKY_DROP 125
#define VARIOUS_CLEAR_SKY_DROP 126
#define VARIOUS_SKY_DROP_YAWN 127
#define VARIOUS_CURE_CERTAIN_STATUSES 128
#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 129
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 130
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 131
#define VARIOUS_SAVE_BATTLER_ITEM 132
#define VARIOUS_RESTORE_BATTLER_ITEM 133
#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 134
#define VARIOUS_SET_BEAK_BLAST 135
#define VARIOUS_SWAP_SIDE_STATUSES 136
#define VARIOUS_SWAP_STATS 137
#define VARIOUS_TEATIME_INVUL 138
#define VARIOUS_TEATIME_TARGETS 139
#define VARIOUS_TRY_WIND_RIDER_POWER 140
#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 141
#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 142
#define VARIOUS_STORE_HEALING_WISH 143
#define VARIOUS_HIT_SWITCH_TARGET_FAILED 144
#define VARIOUS_TRY_REVIVAL_BLESSING 145
// Cmd_manipulatedamage
#define DMG_CHANGE_SIGN 0

View File

@ -2,28 +2,28 @@
#define GUARD_CONSTANTS_POKEMON_H
// Pokémon types
#define TYPE_NONE 255
#define TYPE_NORMAL 0
#define TYPE_FIGHTING 1
#define TYPE_FLYING 2
#define TYPE_POISON 3
#define TYPE_GROUND 4
#define TYPE_ROCK 5
#define TYPE_BUG 6
#define TYPE_GHOST 7
#define TYPE_STEEL 8
#define TYPE_MYSTERY 9
#define TYPE_FIRE 10
#define TYPE_WATER 11
#define TYPE_GRASS 12
#define TYPE_ELECTRIC 13
#define TYPE_PSYCHIC 14
#define TYPE_ICE 15
#define TYPE_DRAGON 16
#define TYPE_DARK 17
#define TYPE_FAIRY 18
#define TYPE_STELLAR 19
#define NUMBER_OF_MON_TYPES 20
#define TYPE_NONE 0
#define TYPE_NORMAL 1
#define TYPE_FIGHTING 2
#define TYPE_FLYING 3
#define TYPE_POISON 4
#define TYPE_GROUND 5
#define TYPE_ROCK 6
#define TYPE_BUG 7
#define TYPE_GHOST 8
#define TYPE_STEEL 9
#define TYPE_MYSTERY 10
#define TYPE_FIRE 11
#define TYPE_WATER 12
#define TYPE_GRASS 13
#define TYPE_ELECTRIC 14
#define TYPE_PSYCHIC 15
#define TYPE_ICE 16
#define TYPE_DRAGON 17
#define TYPE_DARK 18
#define TYPE_FAIRY 19
#define TYPE_STELLAR 20
#define NUMBER_OF_MON_TYPES 21
// Pokémon egg groups
#define EGG_GROUP_NONE 0

View File

@ -109,6 +109,9 @@ struct TypeInfo
u8 palette;
u16 zMove;
u16 maxMove;
u16 teraTypeRGBValue; // Most values pulled from the Tera type icon palette.
u16 damageCategory:2; // Used for B_PHYSICAL_SPECIAL_SPLIT <= GEN_3
u16 padding:14;
const u32 *const paletteTMHM;
//u16 enhanceItem;
//u16 berry;

View File

@ -1983,6 +1983,8 @@ extern const u32 gSummaryMoveSelect_Gfx[];
extern const u32 gSummaryMoveSelect_Pal[];
extern const u32 gStatusGfx_Icons[];
extern const u32 gStatusPal_Icons[];
extern const u16 gCategoryIcons_Pal[];
extern const u32 gCategoryIcons_Gfx[];
extern const u32 gShopMenu_Gfx[];
extern const u32 gShopMenu_Tilemap[];

View File

@ -524,6 +524,7 @@ struct MoveInfo
u32 parentalBondBanned:1;
u32 skyBattleBanned:1;
u32 sketchBanned:1;
u32 padding:5; // end of word
u32 argument;
@ -535,6 +536,7 @@ struct MoveInfo
u8 contestCategory:3;
u8 contestComboStarterId;
u8 contestComboMoves[MAX_COMBO_MOVES];
const u8 *battleAnimScript;
};
#define EFFECTS_ARR(...) (const struct AdditionalEffect[]) {__VA_ARGS__}
@ -873,5 +875,6 @@ u16 GetSpeciesPreEvolution(u16 species);
void HealPokemon(struct Pokemon *mon);
void HealBoxPokemon(struct BoxPokemon *boxMon);
const u8 *GetMoveName(u16 moveId);
const u8 *GetMoveAnimationScript(u16 moveId);
#endif // GUARD_POKEMON_H

View File

@ -5,10 +5,12 @@
extern u8 gLastViewedMonIndex;
extern const u8 *const gMoveDescriptionPointers[];
extern const u8 gNotDoneYetDescription[];
extern const struct SpriteTemplate gSpriteTemplate_MoveTypes;
extern const struct CompressedSpriteSheet gSpriteSheet_MoveTypes;
extern const struct CompressedSpriteSheet gSpriteSheet_CategoryIcons;
extern const struct SpritePalette gSpritePal_CategoryIcons;
extern const struct SpriteTemplate gSpriteTemplate_CategoryIcons;
void ShowPokemonSummaryScreen(u8 mode, void *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void));
void ShowSelectMovePokemonSummaryScreen(struct Pokemon *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void), u16 newMove);

View File

@ -824,6 +824,12 @@ struct moveWithPP {
#define SpAttack(spAttack) SpAttack_(__LINE__, spAttack)
#define SpDefense(spDefense) SpDefense_(__LINE__, spDefense)
#define Speed(speed) Speed_(__LINE__, speed)
#define HPIV(hpIV) HPIV_(__LINE__, hpIV)
#define AttackIV(attackIV) AttackIV_(__LINE__, attackIV)
#define DefenseIV(defenseIV) DefenseIV_(__LINE__, defenseIV)
#define SpAttackIV(spAttackIV) SpAttackIV_(__LINE__, spAttackIV)
#define SpDefenseIV(spDefenseIV) SpDefenseIV_(__LINE__, spDefenseIV)
#define SpeedIV(speedIV) SpeedIV_(__LINE__, speedIV)
#define Item(item) Item_(__LINE__, item)
#define Moves(move1, ...) do { u16 moves_[MAX_MON_MOVES] = {move1, __VA_ARGS__}; Moves_(__LINE__, moves_); } while(0)
#define MovesWithPP(movewithpp1, ...) MovesWithPP_(__LINE__, (struct moveWithPP[MAX_MON_MOVES]) {movewithpp1, __VA_ARGS__})
@ -854,6 +860,12 @@ void Defense_(u32 sourceLine, u32 defense);
void SpAttack_(u32 sourceLine, u32 spAttack);
void SpDefense_(u32 sourceLine, u32 spDefense);
void Speed_(u32 sourceLine, u32 speed);
void HPIV_(u32 sourceLine, u32 hpIV);
void AttackIV_(u32 sourceLine, u32 attackIV);
void DefenseIV_(u32 sourceLine, u32 defenseIV);
void SpAttackIV_(u32 sourceLine, u32 spAttackIV);
void SpDefenseIV_(u32 sourceLine, u32 spDefenseIV);
void SpeedIV_(u32 sourceLine, u32 speedIV);
void Item_(u32 sourceLine, u32 item);
void Moves_(u32 sourceLine, u16 moves[MAX_MON_MOVES]);
void MovesWithPP_(u32 sourceLine, struct moveWithPP moveWithPP[MAX_MON_MOVES]);

View File

@ -75,7 +75,7 @@ void Test_ExpectedResult(enum TestResult);
void Test_ExpectLeaks(bool32);
void Test_ExitWithResult(enum TestResult, const char *fmt, ...);
s32 MgbaPrintf_(const char *fmt, ...);
s32 Test_MgbaPrintf(const char *fmt, ...);
#define TEST(_name) \
static void CAT(Test, __LINE__)(void); \
@ -193,7 +193,7 @@ static inline struct Benchmark BenchmarkStop(void)
do \
{ \
u32 a_ = (a).ticks; u32 b_ = (b).ticks; \
MgbaPrintf_(#a ": %d ticks, " #b ": %d ticks", a_, b_); \
Test_MgbaPrintf(#a ": %d ticks, " #b ": %d ticks", a_, b_); \
if (((a_ - BENCHMARK_ABS) * BENCHMARK_REL) >= (b_ * 100)) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_FASTER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \
} while (0)
@ -202,7 +202,7 @@ static inline struct Benchmark BenchmarkStop(void)
do \
{ \
u32 a_ = (a).ticks; u32 b_ = (b).ticks; \
MgbaPrintf_(#a ": %d ticks, " #b ": %d ticks", a_, b_); \
Test_MgbaPrintf(#a ": %d ticks, " #b ": %d ticks", a_, b_); \
if ((a_ * 100) <= ((b_ - BENCHMARK_ABS) * BENCHMARK_REL)) \
Test_ExitWithResult(TEST_RESULT_FAIL, "%s:%d: EXPECT_SLOWER(" #a ", " #b ") failed", gTestRunnerState.test->filename, __LINE__); \
} while (0)
@ -215,7 +215,7 @@ static inline struct Benchmark BenchmarkStop(void)
#define PARAMETRIZE if (gFunctionTestRunnerState->parameters++ == gFunctionTestRunnerState->runParameter)
#define PARAMETRIZE_LABEL(f, label) if (gFunctionTestRunnerState->parameters++ == gFunctionTestRunnerState->runParameter && (MgbaPrintf_(":N%s: " f " (%d/%d)", gTestRunnerState.test->name, label, gFunctionTestRunnerState->runParameter + 1, gFunctionTestRunnerState->parameters), 1))
#define PARAMETRIZE_LABEL(f, label) if (gFunctionTestRunnerState->parameters++ == gFunctionTestRunnerState->runParameter && (Test_MgbaPrintf(":N%s: " f " (%d/%d)", gTestRunnerState.test->name, label, gFunctionTestRunnerState->runParameter + 1, gFunctionTestRunnerState->parameters), 1))
#define TO_DO \
do { \

View File

@ -0,0 +1,63 @@
import re
def IsCommaMissing(line: str):
sanitized_line = line.removesuffix('\n').strip()
if sanitized_line.endswith('{') or sanitized_line.endswith('(') or sanitized_line.endswith(','):
return False
if not re.search(r'\.[A-Za-z0-9_]+', sanitized_line):
return False
return True
input_file = open('./src/data/moves_info.h', 'r')
lines = input_file.readlines()
input_file.close()
battle_anim_lines = []
moves_info_lines = []
move = None
bracketCount = 0
for line in lines:
m = re.search(r'\[MOVE_([A-Za-z0-9_]+)\] =', line)
if m:
move = m.group(1)
bracketCount = 0
battle_anim_lines.append('extern const u8 Move_' + move + '[];\n')
if move and re.search(r'\{', line):
bracketCount = bracketCount + 1
if move and re.search(r'\}', line):
if (bracketCount == 1):
moves_info_lines.append(8 * ' ' + '.battleAnimScript = Move_' + move + ',\n')
move = None
bracketCount = bracketCount - 1
comment_split = line.split('//')
if move and IsCommaMissing(comment_split[0]):
line = comment_split[0].removesuffix('\n') + ',' + line[len(comment_split[0]):-1] + '\n'
moves_info_lines.append(line)
output_file_mi = open('./src/data/moves_info.h', 'w')
output_file_mi.writelines(moves_info_lines)
output_file_mi.close()
output_file_bas = open('./include/battle_anim_scripts.h', 'w')
output_file_bas.writelines('#ifndef GUARD_BATTLE_ANIM_SCRIPTS_H\n')
output_file_bas.writelines('#define GUARD_BATTLE_ANIM_SCRIPTS_H\n\n')
output_file_bas.writelines(battle_anim_lines)
output_file_bas.writelines('\n#endif // GUARD_BATTLE_ANIM_SCRIPTS_H\n')
output_file_bas.close()
b_anim_scripts_s = open('./data/battle_anim_scripts.s', 'r')
lines = b_anim_scripts_s.read()
b_anim_scripts_s.close()
lines = re.sub(r'(Move_[A-Za-z0-9_]*)([:]+)', r'\1::', lines)
b_anim_scripts_s = open('./data/battle_anim_scripts.s', 'w')
b_anim_scripts_s.write(lines)
b_anim_scripts_s.close()

View File

@ -464,7 +464,7 @@ static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u3
else if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_CONSERVATIVE)
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather, DMG_ROLL_LOWEST);
else
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather, DMG_ROLL_AVERAGE);
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE, weather, DMG_ROLL_DEFAULT);
aiData->moveAccuracy[battlerAtk][battlerDef][i] = Ai_SetMoveAccuracy(aiData, battlerAtk, battlerDef, move);
}
aiData->simulatedDmg[battlerAtk][battlerDef][i] = dmg;
@ -834,7 +834,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (gMovesInfo[move].powderMove && !IsAffectedByPowder(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef]))
RETURN_SCORE_MINUS(10);
if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_IsFaster(battlerAtk, battlerDef, move))
RETURN_SCORE_MINUS(10);
if (IsTwoTurnNotSemiInvulnerableMove(battlerAtk, move) && CanTargetFaintAi(battlerDef, battlerAtk))
@ -1079,7 +1079,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
switch (moveEffect)
{
case EFFECT_HIT: // only applies to Vital Throw
if (gMovesInfo[move].priority < 0 && AI_STRIKES_FIRST(battlerAtk, battlerDef, move) && aiData->hpPercents[battlerAtk] < 40)
if (gMovesInfo[move].priority < 0 && AI_IsFaster(battlerAtk, battlerDef, move) && aiData->hpPercents[battlerAtk] < 40)
ADJUST_SCORE(-2); // don't want to move last
break;
default:
@ -1434,8 +1434,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_MIRROR_COAT:
if (IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]) || gBattleMons[battlerDef].status2 & (STATUS2_INFATUATION | STATUS2_CONFUSION))
ADJUST_SCORE(-1);
if (predictedMove == MOVE_NONE || GetBattleMoveCategory(predictedMove) == DAMAGE_CATEGORY_STATUS
if ((predictedMove == MOVE_NONE || GetBattleMoveCategory(predictedMove) == DAMAGE_CATEGORY_STATUS
|| DoesSubstituteBlockMove(battlerAtk, BATTLE_PARTNER(battlerDef), predictedMove))
&& !(predictedMove == MOVE_NONE && (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY))) // Let Risky AI predict blindly based on stats
ADJUST_SCORE(-10);
break;
@ -1522,7 +1523,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
&& (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)
&& !PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove))
{
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker should go first
if (AI_IsFaster(battlerAtk, battlerDef, move)) // Attacker should go first
{
if (gLastMoves[battlerDef] == MOVE_NONE || gLastMoves[battlerDef] == 0xFFFF)
ADJUST_SCORE(-10); // no anticipated move to disable
@ -1544,7 +1545,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
&& (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)
&& !DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
{
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker should go first
if (AI_IsFaster(battlerAtk, battlerDef, move)) // Attacker should go first
{
if (gLastMoves[battlerDef] == MOVE_NONE || gLastMoves[battlerDef] == 0xFFFF)
ADJUST_SCORE(-10); // no anticipated move to encore
@ -1911,7 +1912,8 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-8); //No point in healing, but should at least do it if nothing better
break;
case EFFECT_RECOIL_IF_MISS:
if (aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_DATA->moveAccuracy[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] < 75)
if (aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && AI_DATA->moveAccuracy[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] < 75
&& !(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY))
ADJUST_SCORE(-6);
break;
case EFFECT_TRANSFORM:
@ -1921,7 +1923,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_SPITE:
case EFFECT_MIMIC:
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker should go first
if (AI_IsFaster(battlerAtk, battlerDef, move)) // Attacker should go first
{
if (gLastMoves[battlerDef] == MOVE_NONE
|| gLastMoves[battlerDef] == 0xFFFF)
@ -2066,7 +2068,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (isDoubleBattle)
{
if (IsHazardMoveEffect(gMovesInfo[aiData->partnerMove].effect) // partner is going to set up hazards
&& AI_WhoStrikesFirst(BATTLE_PARTNER(battlerAtk), battlerAtk, aiData->partnerMove) == AI_IS_FASTER) // partner is going to set up before the potential Defog
&& AI_IsFaster(BATTLE_PARTNER(battlerAtk), battlerAtk, aiData->partnerMove)) // partner is going to set up before the potential Defog
{
ADJUST_SCORE(-10);
break; // Don't use Defog if partner is going to set up hazards
@ -2094,7 +2096,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_SEMI_INVULNERABLE:
if (predictedMove != MOVE_NONE
&& AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER
&& AI_IsSlower(battlerAtk, battlerDef, move)
&& gMovesInfo[predictedMove].effect == EFFECT_SEMI_INVULNERABLE)
ADJUST_SCORE(-10); // Don't Fly/dig/etc if opponent is going to fly/dig/etc after you
@ -2278,7 +2280,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_ME_FIRST:
if (predictedMove != MOVE_NONE)
{
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER)
if (AI_IsSlower(battlerAtk, battlerDef, move))
ADJUST_SCORE(-10); // Target is predicted to go first, Me First will fail
else
return AI_CheckBadMove(battlerAtk, battlerDef, predictedMove, score);
@ -2461,7 +2463,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case EFFECT_ELECTRIFY:
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER
if (AI_IsSlower(battlerAtk, battlerDef, move)
//|| GetMoveTypeSpecial(battlerDef, predictedMove) == TYPE_ELECTRIC // Move will already be electric type
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
ADJUST_SCORE(-10);
@ -2490,7 +2492,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_INSTRUCT:
{
u16 instructedMove;
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER)
if (AI_IsSlower(battlerAtk, battlerDef, move))
instructedMove = predictedMove;
else
instructedMove = gLastMoves[battlerDef];
@ -2529,21 +2531,21 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_QUASH:
if (!isDoubleBattle
|| AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER
|| AI_IsSlower(battlerAtk, battlerDef, move)
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
ADJUST_SCORE(-10);
break;
case EFFECT_AFTER_YOU:
if (!IS_TARGETING_PARTNER(battlerAtk, battlerDef)
|| !isDoubleBattle
|| AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER
|| AI_IsSlower(battlerAtk, battlerDef, move)
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
ADJUST_SCORE(-10);
break;
case EFFECT_SUCKER_PUNCH:
if (predictedMove != MOVE_NONE)
{
if (IS_MOVE_STATUS(predictedMove) || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) // Opponent going first
if (IS_MOVE_STATUS(predictedMove) || AI_IsSlower(battlerAtk, battlerDef, move)) // Opponent going first
ADJUST_SCORE(-10);
}
break;
@ -2585,8 +2587,8 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-1);
break;
case EFFECT_FLAIL:
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER // Opponent should go first
|| aiData->hpPercents[battlerAtk] > 50)
if (AI_IsSlower(battlerAtk, battlerDef, move) // Opponent should go first
|| aiData->hpPercents[battlerAtk] > 50)
ADJUST_SCORE(-4);
break;
//TODO
@ -2622,7 +2624,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
else if (CanAIFaintTarget(battlerAtk, battlerDef, 0))
ADJUST_SCORE(-10);
else if (CanTargetFaintAi(battlerDef, battlerAtk)
&& AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER)
&& AI_IsSlower(battlerAtk, battlerDef, move))
ADJUST_SCORE(-10);
break;
case EFFECT_JUNGLE_HEALING:
@ -2650,7 +2652,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
break;
case EFFECT_UPPER_HAND:
if (predictedMove == MOVE_NONE || IS_MOVE_STATUS(predictedMove) || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER || GetMovePriority(battlerDef, move) < 1 || GetMovePriority(battlerDef, move) > 3) // Opponent going first or not using priority move
if (predictedMove == MOVE_NONE || IS_MOVE_STATUS(predictedMove) || AI_IsSlower(battlerAtk, battlerDef, move) || GetMovePriority(battlerDef, move) < 1 || GetMovePriority(battlerDef, move) > 3) // Opponent going first or not using priority move
ADJUST_SCORE(-10);
break;
case EFFECT_PLACEHOLDER:
@ -2675,7 +2677,7 @@ static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, 0) && gMovesInfo[move].effect != EFFECT_EXPLOSION)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
if (AI_IsFaster(battlerAtk, battlerDef, move))
ADJUST_SCORE(FAST_KILL);
else
ADJUST_SCORE(SLOW_KILL);
@ -2741,7 +2743,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
// Adjust for always crit moves
if (gMovesInfo[aiData->partnerMove].alwaysCriticalHit && aiData->abilities[battlerAtk] == ABILITY_ANGER_POINT)
{
if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner, move) == AI_IS_SLOWER) // Partner moving first
if (AI_IsSlower(battlerAtk, battlerAtkPartner, move)) // Partner moving first
{
// discourage raising our attack since it's about to be maxed out
if (IsAttackBoostMoveEffect(effect))
@ -3021,7 +3023,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_INSTRUCT:
{
u16 instructedMove;
if (AI_WhoStrikesFirst(battlerAtk, battlerAtkPartner, move) == AI_IS_FASTER)
if (AI_IsFaster(battlerAtk, battlerAtkPartner, move))
instructedMove = aiData->partnerMove;
else
instructedMove = gLastMoves[battlerAtkPartner];
@ -3035,8 +3037,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case EFFECT_AFTER_YOU:
if (AI_WhoStrikesFirst(battlerAtkPartner, FOE(battlerAtkPartner), aiData->partnerMove) == AI_IS_SLOWER // Opponent mon 1 goes before partner
|| AI_WhoStrikesFirst(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)), aiData->partnerMove) == AI_IS_SLOWER) // Opponent mon 2 goes before partner
if (AI_IsSlower(battlerAtkPartner, FOE(battlerAtkPartner), aiData->partnerMove) // Opponent mon 1 goes before partner
|| AI_IsSlower(battlerAtkPartner, BATTLE_PARTNER(FOE(battlerAtkPartner)), aiData->partnerMove)) // Opponent mon 2 goes before partner
{
if (gMovesInfo[aiData->partnerMove].effect == EFFECT_COUNTER || gMovesInfo[aiData->partnerMove].effect == EFFECT_MIRROR_COAT)
break; // These moves need to go last
@ -3045,9 +3047,9 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_HEAL_PULSE:
case EFFECT_HIT_ENEMY_HEAL_ALLY:
if (AI_WhoStrikesFirst(battlerAtk, FOE(battlerAtk), move) == AI_IS_FASTER
&& AI_WhoStrikesFirst(battlerAtk, BATTLE_PARTNER(FOE(battlerAtk)), move) == AI_IS_FASTER
&& gBattleMons[battlerAtkPartner].hp < gBattleMons[battlerAtkPartner].maxHP / 2)
if (AI_IsFaster(battlerAtk, FOE(battlerAtk), move)
&& AI_IsFaster(battlerAtk, BATTLE_PARTNER(FOE(battlerAtk)), move)
&& gBattleMons[battlerAtkPartner].hp < gBattleMons[battlerAtkPartner].maxHP / 2)
RETURN_SCORE_PLUS(WEAK_EFFECT);
break;
} // attacker move effects
@ -3154,7 +3156,7 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
isTwoTurnNotSemiInvulnerableMove[i] = FALSE;
}
/*
MgbaPrintf_("%S: required hits: %d Dmg: %d", gMoveNames[moves[i]], noOfHits[i], AI_DATA->simulatedDmg[battlerAtk][battlerDef][i]);
Test_MgbaPrintf("%S: required hits: %d Dmg: %d", gMoveNames[moves[i]], noOfHits[i], AI_DATA->simulatedDmg[battlerAtk][battlerDef][i]);
*/
}
@ -3341,7 +3343,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
break;
case EFFECT_SPEED_DOWN:
case EFFECT_SPEED_DOWN_2:
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
if (AI_IsFaster(battlerAtk, battlerDef, move))
ADJUST_SCORE(-3);
else if (!AI_RandLessThan(70))
ADJUST_SCORE(DECENT_EFFECT);
@ -3556,7 +3558,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(WEAK_EFFECT);
break;
case EFFECT_MIMIC:
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
if (AI_IsFaster(battlerAtk, battlerDef, move))
{
if (gLastMoves[battlerDef] != MOVE_NONE && gLastMoves[battlerDef] != 0xFFFF)
return AI_CheckViability(battlerAtk, battlerDef, gLastMoves[battlerDef], score);
@ -3618,7 +3620,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
&& (gLastMoves[battlerDef] != MOVE_NONE)
&& (gLastMoves[battlerDef] != 0xFFFF)
&& (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)
&& (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER))
&& (AI_IsFaster(battlerAtk, battlerDef, move)))
{
if (CanTargetMoveFaintAi(gLastMoves[battlerDef], battlerDef, battlerAtk, 1))
ADJUST_SCORE(GOOD_EFFECT); // Disable move that can kill attacker
@ -3646,7 +3648,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_DESTINY_BOND:
if (IsDynamaxed(battlerDef))
break;
else if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER && CanTargetFaintAi(battlerDef, battlerAtk))
else if (AI_IsFaster(battlerAtk, battlerDef, move) && CanTargetFaintAi(battlerDef, battlerAtk))
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_SPITE:
@ -3846,7 +3848,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
case EFFECT_SEMI_INVULNERABLE:
if (predictedMove != MOVE_NONE && !isDoubleBattle)
{
if ((AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
if ((AI_IsFaster(battlerAtk, battlerDef, move))
&& (gMovesInfo[predictedMove].effect == EFFECT_EXPLOSION || gMovesInfo[predictedMove].effect == EFFECT_PROTECT))
ADJUST_SCORE(GOOD_EFFECT);
else if (gMovesInfo[predictedMove].effect == EFFECT_SEMI_INVULNERABLE && !(gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE))
@ -3888,7 +3890,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
break;
case EFFECT_ATTRACT:
if (!isDoubleBattle
&& (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER)
&& (AI_IsSlower(battlerAtk, battlerDef, move))
&& BattlerWillFaintFromSecondaryDamage(battlerDef, aiData->abilities[battlerDef]))
break; // Don't use if the attract won't have a change to activate
if (gBattleMons[battlerDef].status1 & STATUS1_ANY
@ -3922,7 +3924,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
if (isDoubleBattle)
{
if (IsHazardMoveEffect(gMovesInfo[aiData->partnerMove].effect) // Partner is going to set up hazards
&& AI_WhoStrikesFirst(battlerAtk, BATTLE_PARTNER(battlerAtk), move) == AI_IS_SLOWER) // Partner going first
&& AI_IsSlower(battlerAtk, BATTLE_PARTNER(battlerAtk), move)) // Partner going first
break; // Don't use Defog if partner is going to set up hazards
}
if (ShouldLowerEvasion(battlerAtk, battlerDef, aiData->abilities[battlerDef]))
@ -4083,6 +4085,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(WEAK_EFFECT); // Recycle healing berry if we can't otherwise faint the target and the target wont kill us after we activate the berry
}
break;
case EFFECT_RAGING_BULL:
case EFFECT_BRICK_BREAK:
if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_REFLECT)
ADJUST_SCORE(DECENT_EFFECT);
@ -4348,7 +4351,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_HEAL_BLOCK:
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER && predictedMove != MOVE_NONE && IsHealingMove(predictedMove))
if (AI_IsFaster(battlerAtk, battlerDef, move) && predictedMove != MOVE_NONE && IsHealingMove(predictedMove))
ADJUST_SCORE(DECENT_EFFECT); // Try to cancel healing move
else if (HasHealingEffect(battlerDef) || aiData->holdEffects[battlerDef] == HOLD_EFFECT_LEFTOVERS
|| (aiData->holdEffects[battlerDef] == HOLD_EFFECT_BLACK_SLUDGE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_POISON)))
@ -4380,7 +4383,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(BEST_EFFECT);
break;
case EFFECT_QUASH:
if (isDoubleBattle && AI_WhoStrikesFirst(BATTLE_PARTNER(battlerAtk), battlerDef, aiData->partnerMove) == AI_IS_SLOWER)
if (isDoubleBattle && AI_IsSlower(BATTLE_PARTNER(battlerAtk), battlerDef, aiData->partnerMove))
ADJUST_SCORE(DECENT_EFFECT); // Attacker partner wouldn't go before target
break;
case EFFECT_TAILWIND:
@ -4395,7 +4398,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
if (IsBattlerGrounded(battlerAtk) && HasDamagingMoveOfType(battlerDef, TYPE_ELECTRIC)
&& !(AI_GetTypeEffectiveness(MOVE_EARTHQUAKE, battlerDef, battlerAtk) == AI_EFFECTIVENESS_x0)) // Doesn't resist ground move
{
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker goes first
if (AI_IsFaster(battlerAtk, battlerDef, move)) // Attacker goes first
{
if (gMovesInfo[predictedMove].type == TYPE_GROUND)
ADJUST_SCORE(GOOD_EFFECT); // Cause the enemy's move to fail
@ -4410,7 +4413,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case EFFECT_CAMOUFLAGE:
if (predictedMove != MOVE_NONE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER // Attacker goes first
if (predictedMove != MOVE_NONE && AI_IsFaster(battlerAtk, battlerDef, move) // Attacker goes first
&& !IS_MOVE_STATUS(move) && AI_GetTypeEffectiveness(predictedMove, battlerDef, battlerAtk) != AI_EFFECTIVENESS_x0)
ADJUST_SCORE(DECENT_EFFECT);
break;
@ -4446,7 +4449,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case EFFECT_ENDEAVOR:
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER && !CanTargetFaintAi(battlerDef, battlerAtk))
if (AI_IsSlower(battlerAtk, battlerDef, move) && !CanTargetFaintAi(battlerDef, battlerAtk))
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_REVIVAL_BLESSING:
@ -4706,7 +4709,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
case MOVE_EFFECT_THROAT_CHOP:
if (gMovesInfo[GetBestDmgMoveFromBattler(battlerDef, battlerAtk)].soundMove)
{
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
if (AI_IsFaster(battlerAtk, battlerDef, move))
ADJUST_SCORE(GOOD_EFFECT);
else
ADJUST_SCORE(DECENT_EFFECT);
@ -4720,14 +4723,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
}
if (score <= 1)
return NOT_GOOD_ENOUGH;
else if (score <= 3)
return GOOD_MOVE_EFFECTS;
else if (score <= 5)
return PREFERRED_MOVE_EFFECTS;
else
return BEST_MOVE_EFFECTS;
return score;
}
// AI_FLAG_CHECK_VIABILITY - Chooses best possible move to hit player
@ -4742,7 +4738,12 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == 0)
ADJUST_SCORE(-20);
else
score += AI_CompareDamagingMoves(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex);
{
if ((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY) && GetBestDmgMoveFromBattler(battlerAtk, battlerDef) == move)
score += 1;
else
score += AI_CompareDamagingMoves(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex);
}
}
score += AI_CalcMoveEffectScore(battlerAtk, battlerDef, move);
@ -4759,7 +4760,7 @@ static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
return score;
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SMART_SWITCHING
&& AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER
&& AI_IsSlower(battlerAtk, battlerDef, move)
&& CanTargetFaintAi(battlerDef, battlerAtk)
&& GetMovePriority(battlerAtk, move) == 0)
{
@ -4888,27 +4889,37 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (gMovesInfo[move].criticalHitStage > 0)
ADJUST_SCORE(DECENT_EFFECT);
// +3 Score
switch (gMovesInfo[move].effect)
{
case EFFECT_SLEEP:
case EFFECT_EXPLOSION:
case EFFECT_MIRROR_MOVE:
case EFFECT_OHKO:
case EFFECT_CONFUSE:
case EFFECT_METRONOME:
case EFFECT_PSYWAVE:
case EFFECT_COUNTER:
case EFFECT_DESTINY_BOND:
case EFFECT_SWAGGER:
case EFFECT_ATTRACT:
case EFFECT_PRESENT:
case EFFECT_BELLY_DRUM:
if (gSpeciesInfo[gBattleMons[battlerDef].species].baseAttack >= gSpeciesInfo[gBattleMons[battlerDef].species].baseSpAttack + 10)
ADJUST_SCORE(STRONG_RISKY_EFFECT);
break;
case EFFECT_MIRROR_COAT:
case EFFECT_FOCUS_PUNCH:
if (gSpeciesInfo[gBattleMons[battlerDef].species].baseSpAttack >= gSpeciesInfo[gBattleMons[battlerDef].species].baseAttack + 10)
ADJUST_SCORE(STRONG_RISKY_EFFECT);
break;
case EFFECT_EXPLOSION:
ADJUST_SCORE(STRONG_RISKY_EFFECT);
break;
// +2 Score
case EFFECT_REVENGE:
case EFFECT_FILLET_AWAY:
if (Random() & 1)
ADJUST_SCORE(DECENT_EFFECT);
if (gSpeciesInfo[gBattleMons[battlerDef].species].baseSpeed >= gSpeciesInfo[gBattleMons[battlerAtk].species].baseSpeed + 10)
ADJUST_SCORE(AVERAGE_RISKY_EFFECT);
break;
case EFFECT_BELLY_DRUM:
if (gBattleMons[battlerAtk].hp >= gBattleMons[battlerAtk].maxHP * 90 / 100)
ADJUST_SCORE(AVERAGE_RISKY_EFFECT);
break;
case EFFECT_MAX_HP_50_RECOIL:
case EFFECT_MIND_BLOWN:
case EFFECT_SWAGGER:
case EFFECT_FLATTER:
case EFFECT_ATTRACT:
case EFFECT_OHKO:
ADJUST_SCORE(AVERAGE_RISKY_EFFECT);
break;
case EFFECT_HIT:
{
@ -4920,7 +4931,7 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{
case MOVE_EFFECT_ALL_STATS_UP:
if (Random() & 1)
ADJUST_SCORE(DECENT_EFFECT);
ADJUST_SCORE(AVERAGE_RISKY_EFFECT);
break;
default:
break;

View File

@ -29,6 +29,7 @@ static bool32 AiExpectsToFaintPlayer(u32 battler);
static bool32 AI_ShouldHeal(u32 battler, u32 healAmount);
static bool32 AI_OpponentCanFaintAiWithMod(u32 battler, u32 healAmount);
static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon);
static bool32 CanAbilityTrapOpponent(u16 ability, u32 opponent);
static void InitializeSwitchinCandidate(struct Pokemon *mon)
{
@ -162,7 +163,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult)
// Check if current mon can outspeed and KO in spite of bad matchup, and don't switch out if it can
if(damageDealt > gBattleMons[opposingBattler].hp)
{
if (AI_WhoStrikesFirst(battler, opposingBattler, aiBestMove) == AI_IS_FASTER)
if (AI_IsFaster(battler, opposingBattler, aiBestMove))
return FALSE;
}
@ -414,6 +415,48 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler, bool32 emitResult)
return FALSE;
}
static bool32 FindMonThatTrapsOpponent(u32 battler, bool32 emitResult)
{
s32 firstId;
s32 lastId;
struct Pokemon *party;
s32 i;
u16 monAbility;
s32 opposingBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(battler)));
// Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer
if (!(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING))
return FALSE;
// Check if current mon has an ability that traps opponent
if (CanAbilityTrapOpponent(gBattleMons[battler].ability, opposingBattler))
return FALSE;
// Check party for mon with ability that traps opponent
GetAIPartyIndexes(battler, &firstId, &lastId);
if (GetBattlerSide(battler) == B_SIDE_PLAYER)
party = gPlayerParty;
else
party = gEnemyParty;
for (i = firstId; i < lastId; i++)
{
monAbility = GetMonAbility(&party[i]);
if (CanAbilityTrapOpponent(monAbility, opposingBattler))
{
if (i == AI_DATA->mostSuitableMonId[battler]) // If mon in slot i is the most suitable switchin candidate, then it's a trapper than wins 1v1
{
gBattleStruct->AI_monToSwitchIntoId[battler] = i;
if (emitResult)
BtlController_EmitTwoReturnValues(battler, 1, B_ACTION_SWITCH, 0);
return TRUE;
}
}
}
return FALSE;
}
static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult)
{
bool32 switchMon = FALSE;
@ -569,7 +612,7 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult)
&& AnyStatIsRaised(battler))
switchMon = FALSE;
if (AiExpectsToFaintPlayer(battler)
&& !AI_STRIKES_FIRST(battler, opposingBattler, 0)
&& AI_IsSlower(battler, opposingBattler, 0)
&& !AI_OpponentCanFaintAiWithMod(battler, 0))
switchMon = FALSE;
}
@ -1017,6 +1060,8 @@ bool32 ShouldSwitch(u32 battler, bool32 emitResult)
return TRUE;
if (ShouldSwitchIfGameStatePrompt(battler, emitResult))
return TRUE;
if (FindMonThatTrapsOpponent(battler, emitResult))
return TRUE;
if (FindMonThatAbsorbsOpponentsMove(battler, emitResult))
return TRUE;
@ -1234,7 +1279,7 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
if (aiMove != MOVE_NONE && gMovesInfo[aiMove].power != 0)
{
aiMove = GetMonData(&party[i], MON_DATA_MOVE1 + j);
dmg = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE, DMG_ROLL_AVERAGE);
dmg = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE, DMG_ROLL_DEFAULT);
if (bestDmg < dmg)
{
bestDmg = dmg;
@ -1691,6 +1736,25 @@ static s32 GetMaxDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposingBattle
return maxDamageTaken;
}
static bool32 CanAbilityTrapOpponent(u16 ability, u32 opponent)
{
if ((B_GHOSTS_ESCAPE >= GEN_6 && IS_BATTLER_OF_TYPE(opponent, TYPE_GHOST)))
return FALSE;
else if (ability == ABILITY_SHADOW_TAG)
{
if (B_SHADOW_TAG_ESCAPE >= GEN_4 && GetBattlerAbility(opponent) == ABILITY_SHADOW_TAG) // Check if ability exists in species
return FALSE;
else
return TRUE;
}
else if (ability == ABILITY_ARENA_TRAP && IsBattlerGrounded(opponent))
return TRUE;
else if (ability == ABILITY_MAGNET_PULL && IS_BATTLER_OF_TYPE(opponent, TYPE_STEEL))
return TRUE;
else
return FALSE;
}
// This function splits switching behaviour mid-battle from after a KO.
// Mid battle, it integrates GetBestMonTypeMatchup (vanilla with modifications), GetBestMonDefensive (custom), and GetBestMonBatonPass (vanilla with modifications)
// After a KO, integrates GetBestMonRevengeKiller (custom), GetBestMonTypeMatchup (vanilla with modifications), GetBestMonBatonPass (vanilla with modifications), and GetBestMonDmg (vanilla)
@ -1704,17 +1768,17 @@ static s32 GetMaxDamagePlayerCouldDealToSwitchin(u32 battler, u32 opposingBattle
static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, u32 battler, u32 opposingBattler, u8 battlerIn1, u8 battlerIn2, bool32 isSwitchAfterKO)
{
int revengeKillerId = PARTY_SIZE, slowRevengeKillerId = PARTY_SIZE, fastThreatenId = PARTY_SIZE, slowThreatenId = PARTY_SIZE, damageMonId = PARTY_SIZE;
int batonPassId = PARTY_SIZE, typeMatchupId = PARTY_SIZE, typeMatchupEffectiveId = PARTY_SIZE, defensiveMonId = PARTY_SIZE, aceMonId = PARTY_SIZE;
int batonPassId = PARTY_SIZE, typeMatchupId = PARTY_SIZE, typeMatchupEffectiveId = PARTY_SIZE, defensiveMonId = PARTY_SIZE, aceMonId = PARTY_SIZE, trapperId = PARTY_SIZE;
int i, j, aliveCount = 0, bits = 0;
s32 defensiveMonHitKOThreshold = 3; // 3HKO threshold that candidate defensive mons must exceed
u32 aiMove, hitsToKO, hitsToKOThreshold, maxHitsToKO = 0;
s32 playerMonSpeed = gBattleMons[opposingBattler].speed, playerMonHP = gBattleMons[opposingBattler].hp, aiMonSpeed, maxDamageDealt = 0, damageDealt = 0;
u32 aiMove, hitsToKOAI, hitsToKOPlayer, hitsToKOAIThreshold, maxHitsToKO = 0;
s32 playerMonSpeed = gBattleMons[opposingBattler].speed, playerMonHP = gBattleMons[opposingBattler].hp, aiMonSpeed, aiMovePriority = 0, maxDamageDealt = 0, damageDealt = 0;
u16 bestResist = UQ_4_12(1.0), bestResistEffective = UQ_4_12(1.0), typeMatchup;
if (isSwitchAfterKO)
hitsToKOThreshold = 1; // After a KO, mons at minimum need to not be 1-shot, as they switch in for free
hitsToKOAIThreshold = 1; // After a KO, mons at minimum need to not be 1-shot, as they switch in for free
else
hitsToKOThreshold = 2; // When switching in otherwise need to not be 2-shot, as they do not switch in for free
hitsToKOAIThreshold = 2; // When switching in otherwise need to not be 2-shot, as they do not switch in for free
// Iterate through mons
for (i = firstId; i < lastId; i++)
@ -1744,12 +1808,12 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
continue;
// Get max number of hits for player to KO AI mon
hitsToKO = GetSwitchinHitsToKO(GetMaxDamagePlayerCouldDealToSwitchin(battler, opposingBattler, AI_DATA->switchinCandidate.battleMon), battler);
hitsToKOAI = GetSwitchinHitsToKO(GetMaxDamagePlayerCouldDealToSwitchin(battler, opposingBattler, AI_DATA->switchinCandidate.battleMon), battler);
// Track max hits to KO and set GetBestMonDefensive if applicable
if(hitsToKO > maxHitsToKO)
if(hitsToKOAI > maxHitsToKO)
{
maxHitsToKO = hitsToKO;
maxHitsToKO = hitsToKOAI;
if(maxHitsToKO > defensiveMonHitKOThreshold)
defensiveMonId = i;
}
@ -1759,7 +1823,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
// Check that good type matchups gets at least two turns and set GetBestMonTypeMatchup if applicable
if (typeMatchup < bestResist)
{
if ((hitsToKO > hitsToKOThreshold && AI_DATA->switchinCandidate.battleMon.speed > playerMonSpeed) || hitsToKO > hitsToKOThreshold + 1) // Need to take an extra hit if slower
if ((hitsToKOAI > hitsToKOAIThreshold && AI_DATA->switchinCandidate.battleMon.speed > playerMonSpeed) || hitsToKOAI > hitsToKOAIThreshold + 1) // Need to take an extra hit if slower
{
bestResist = typeMatchup;
typeMatchupId = i;
@ -1772,13 +1836,14 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
for (j = 0; j < MAX_MON_MOVES; j++)
{
aiMove = AI_DATA->switchinCandidate.battleMon.moves[j];
aiMovePriority = gMovesInfo[aiMove].priority;
// Only do damage calc if switching after KO, don't need it otherwise and saves ~0.02s per turn
if (isSwitchAfterKO && aiMove != MOVE_NONE && gMovesInfo[aiMove].power != 0)
damageDealt = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE, DMG_ROLL_AVERAGE);
damageDealt = AI_CalcPartyMonDamage(aiMove, battler, opposingBattler, AI_DATA->switchinCandidate.battleMon, TRUE, DMG_ROLL_DEFAULT);
// 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 && ((hitsToKO > hitsToKOThreshold + 1 && AI_DATA->switchinCandidate.battleMon.speed < playerMonSpeed) || (hitsToKO > hitsToKOThreshold && AI_DATA->switchinCandidate.battleMon.speed > playerMonSpeed)))
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];
// Check for mon with resistance and super effective move for GetBestMonTypeMatchup
@ -1789,7 +1854,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
if (AI_GetTypeEffectiveness(aiMove, battler, opposingBattler) >= UQ_4_12(2.0))
{
// Assuming a super effective move would do significant damage or scare the player out, so not being as conservative here
if (hitsToKO > hitsToKOThreshold)
if (hitsToKOAI > hitsToKOAIThreshold)
{
bestResistEffective = typeMatchup;
typeMatchupEffectiveId = i;
@ -1804,7 +1869,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
// Check that mon isn't one shot and set GetBestMonDmg if applicable
if (damageDealt > maxDamageDealt)
{
if(hitsToKO > hitsToKOThreshold)
if(hitsToKOAI > hitsToKOAIThreshold)
{
maxDamageDealt = damageDealt;
damageMonId = i;
@ -1816,7 +1881,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
if (damageDealt > playerMonHP)
{
// If AI mon is faster and doesn't die to hazards
if ((aiMonSpeed > playerMonSpeed || gMovesInfo[aiMove].priority > 0) && AI_DATA->switchinCandidate.battleMon.hp > GetSwitchinHazardsDamage(battler, &AI_DATA->switchinCandidate.battleMon))
if ((aiMonSpeed > playerMonSpeed || aiMovePriority > 0) && AI_DATA->switchinCandidate.battleMon.hp > GetSwitchinHazardsDamage(battler, &AI_DATA->switchinCandidate.battleMon))
{
// We have a revenge killer
revengeKillerId = i;
@ -1826,7 +1891,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
else
{
// If AI mon can't be OHKO'd
if (hitsToKO > hitsToKOThreshold)
if (hitsToKOAI > hitsToKOAIThreshold)
{
// We have a slow revenge killer
slowRevengeKillerId = i;
@ -1838,10 +1903,10 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
if (damageDealt > playerMonHP / 2)
{
// If AI mon is faster
if (aiMonSpeed > playerMonSpeed || gMovesInfo[aiMove].priority > 0)
if (aiMonSpeed > playerMonSpeed || aiMovePriority > 0)
{
// If AI mon can't be OHKO'd
if (hitsToKO > hitsToKOThreshold)
if (hitsToKOAI > hitsToKOAIThreshold)
{
// We have a fast threaten
fastThreatenId = i;
@ -1851,13 +1916,25 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
else
{
// If AI mon can't be 2HKO'd
if (hitsToKO > hitsToKOThreshold + 1)
if (hitsToKOAI > hitsToKOAIThreshold + 1)
{
// We have a slow threaten
slowThreatenId = i;
}
}
}
// If mon can trap
if (CanAbilityTrapOpponent(AI_DATA->switchinCandidate.battleMon.ability, opposingBattler))
{
hitsToKOPlayer = GetNoOfHitsToKOBattlerDmg(damageDealt, opposingBattler);
if (CountUsablePartyMons(opposingBattler) > 0
&& (((hitsToKOAI > hitsToKOPlayer && isSwitchAfterKO) // If can 1v1 after a KO
|| (hitsToKOAI == hitsToKOPlayer && isSwitchAfterKO && (aiMonSpeed > playerMonSpeed || aiMovePriority > 0)))
|| ((hitsToKOAI > hitsToKOPlayer + 1 && !isSwitchAfterKO) // If can 1v1 after mid battle
|| (hitsToKOAI == hitsToKOPlayer + 1 && !isSwitchAfterKO && (aiMonSpeed > playerMonSpeed || aiMovePriority > 0)))))
trapperId = i;
}
}
}
}
@ -1867,43 +1944,37 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
// Different switching priorities depending on switching mid battle vs switching after a KO
if (isSwitchAfterKO)
{
// Return GetBestMonRevengeKiller > GetBestMonTypeMatchup > GetBestMonBatonPass > GetBestMonDmg
if (revengeKillerId != PARTY_SIZE)
// Return Trapper > GetBestMonRevengeKiller > GetBestMonTypeMatchup > GetBestMonBatonPass > GetBestMonDmg
if (trapperId != PARTY_SIZE)
return trapperId;
else if (revengeKillerId != PARTY_SIZE)
return revengeKillerId;
else if (slowRevengeKillerId != PARTY_SIZE)
return slowRevengeKillerId;
else if (fastThreatenId != PARTY_SIZE)
return fastThreatenId;
else if (slowThreatenId != PARTY_SIZE)
return slowThreatenId;
else if (typeMatchupEffectiveId != PARTY_SIZE)
return typeMatchupEffectiveId;
else if (typeMatchupId != PARTY_SIZE)
return typeMatchupId;
else if (batonPassId != PARTY_SIZE)
return batonPassId;
else if (damageMonId != PARTY_SIZE)
return damageMonId;
}
else
{
// Return GetBestMonTypeMatchup > GetBestMonDefensive > GetBestMonBatonPass
if (typeMatchupEffectiveId != PARTY_SIZE)
// Return Trapper > GetBestMonTypeMatchup > GetBestMonDefensive > GetBestMonBatonPass
if (trapperId != PARTY_SIZE)
return trapperId;
else if (typeMatchupEffectiveId != PARTY_SIZE)
return typeMatchupEffectiveId;
else if (typeMatchupId != PARTY_SIZE)
return typeMatchupId;
else if (defensiveMonId != PARTY_SIZE)
return defensiveMonId;
else if (batonPassId != PARTY_SIZE)
return batonPassId;
@ -2019,7 +2090,7 @@ static bool32 AiExpectsToFaintPlayer(u32 battler)
if (GetBattlerSide(target) != GetBattlerSide(battler)
&& CanIndexMoveFaintTarget(battler, target, gBattleStruct->aiMoveOrAction[battler], 0)
&& AI_WhoStrikesFirst(battler, target, GetAIChosenMove(battler)) == AI_IS_FASTER)
&& AI_IsFaster(battler, target, GetAIChosenMove(battler)))
{
// We expect to faint the target and move first -> dont use an item
return TRUE;

View File

@ -35,6 +35,16 @@
static u32 AI_GetEffectiveness(uq4_12_t multiplier);
// Functions
bool32 AI_IsFaster(u32 battlerAi, u32 battlerDef, u32 move)
{
return (AI_WhoStrikesFirst(battlerAi, battlerDef, move) == AI_IS_FASTER);
}
bool32 AI_IsSlower(u32 battlerAi, u32 battlerDef, u32 move)
{
return (AI_WhoStrikesFirst(battlerAi, battlerDef, move) == AI_IS_SLOWER);
}
u32 GetAIChosenMove(u32 battlerId)
{
return (gBattleMons[battlerId].moves[gBattleStruct->aiMoveOrAction[battlerId]]);
@ -311,7 +321,7 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler)
u32 move = gBattleResources->battleHistory->usedMoves[opposingBattler][i];
if (gMovesInfo[move].effect == EFFECT_PROTECT && move != MOVE_ENDURE)
return TRUE;
if (gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAI, opposingBattler, GetAIChosenMove(battlerAI)) == AI_IS_SLOWER)
if (gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE && AI_IsSlower(battlerAI, opposingBattler, GetAIChosenMove(battlerAI)))
return TRUE;
}
return FALSE;
@ -369,19 +379,21 @@ s32 AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, u8 *type
static inline s32 LowestRollDmg(s32 dmg)
{
dmg *= 100 - 15;
dmg *= MIN_ROLL_PERCENTAGE;
dmg /= 100;
return dmg;
}
static inline s32 HighestRollDmg(s32 dmg)
{
dmg *= MAX_ROLL_PERCENTAGE;
dmg /= 100;
return dmg;
}
static inline s32 AverageRollDmg(s32 dmg)
static inline s32 DmgRoll(s32 dmg)
{
dmg = ((HighestRollDmg(dmg) + LowestRollDmg(dmg)) * 100) / 2;
dmg *= DMG_ROLL_PERCENTAGE;
dmg /= 100;
return dmg;
}
@ -466,6 +478,14 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef)
if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) && gMovesInfo[move].argument == ARG_TRY_REMOVE_TERRAIN_FAIL)
return TRUE;
break;
case EFFECT_POLTERGEIST:
if (AI_DATA->items[battlerDef] == ITEM_NONE)
return TRUE;
break;
case EFFECT_FIRST_TURN_ONLY:
if (!gDisableStructs[battlerAtk].isFirstTurn)
return TRUE;
break;
}
return FALSE;
@ -546,8 +566,8 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
u32 critChance = GetCritHitChance(critChanceIndex);
// With critChance getting closer to 1, dmg gets closer to critDmg.
if (dmgRoll == DMG_ROLL_AVERAGE)
dmg = AverageRollDmg((critDmg + normalDmg * (critChance - 1)) / (critChance));
if (dmgRoll == DMG_ROLL_DEFAULT)
dmg = DmgRoll((critDmg + normalDmg * (critChance - 1)) / (critChance));
else if (dmgRoll == DMG_ROLL_HIGHEST)
dmg = HighestRollDmg((critDmg + normalDmg * (critChance - 1)) / (critChance));
else
@ -555,8 +575,8 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes
}
else
{
if (dmgRoll == DMG_ROLL_AVERAGE)
dmg = AverageRollDmg(normalDmg);
if (dmgRoll == DMG_ROLL_DEFAULT)
dmg = DmgRoll(normalDmg);
else if (dmgRoll == DMG_ROLL_HIGHEST)
dmg = HighestRollDmg(normalDmg);
else
@ -963,48 +983,21 @@ static u32 AI_GetEffectiveness(uq4_12_t multiplier)
*/
s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered)
{
u32 fasterAI = 0, fasterPlayer = 0, i;
s8 prioAI = 0;
s8 prioBattler2 = 0;
u16 *battler2Moves = GetMovesArray(battler2);
// Check move priorities first.
prioAI = GetMovePriority(battlerAI, moveConsidered);
for (i = 0; i < MAX_MON_MOVES; i++)
{
prioBattler2 = GetMovePriority(battler2, battler2Moves[i]);
if (battler2Moves[i] == MOVE_NONE || battler2Moves[i] == MOVE_UNAVAILABLE
|| (prioBattler2 > prioAI && !CanIndexMoveFaintTarget(battler2, battlerAI, i , 2)))
continue;
if (prioAI > prioBattler2)
fasterAI++;
else if (prioBattler2 > prioAI)
fasterPlayer++;
}
if (fasterAI > fasterPlayer)
{
if (prioAI > prioBattler2)
return AI_IS_FASTER;
if (GetWhichBattlerFasterArgs(battlerAI, battler2, TRUE,
AI_DATA->abilities[battlerAI], AI_DATA->abilities[battler2],
AI_DATA->holdEffects[battlerAI], AI_DATA->holdEffects[battler2],
AI_DATA->speedStats[battlerAI], AI_DATA->speedStats[battler2],
prioAI, prioBattler2) == 1)
return AI_IS_FASTER;
}
else if (fasterAI < fasterPlayer)
{
return AI_IS_SLOWER;
}
else
{
if (prioAI > prioBattler2)
return AI_IS_FASTER; // if we didn't know any of battler 2's moves to compare priorities, assume they don't have a prio+ move
// Priorities are the same(at least comparing to moves the AI is aware of), decide by speed.
if (GetWhichBattlerFasterArgs(battlerAI, battler2, TRUE,
AI_DATA->abilities[battlerAI], AI_DATA->abilities[battler2],
AI_DATA->holdEffects[battlerAI], AI_DATA->holdEffects[battler2],
AI_DATA->speedStats[battlerAI], AI_DATA->speedStats[battler2],
prioAI, prioBattler2) == 1)
return AI_IS_FASTER;
else
return AI_IS_SLOWER;
}
return AI_IS_SLOWER;
}
// Check if target has means to faint ai mon.
@ -1612,7 +1605,7 @@ bool32 ShouldLowerStat(u32 battler, u32 battlerAbility, u32 stat)
return !(battlerAbility == ABILITY_BIG_PECKS);
case STAT_SPEED:
// If AI is faster and doesn't have any mons left, lowering speed doesn't give any
return !(AI_WhoStrikesFirst(sBattler_AI, battler, AI_THINKING_STRUCT->moveConsidered) == AI_IS_FASTER
return !(AI_IsFaster(sBattler_AI, battler, AI_THINKING_STRUCT->moveConsidered)
&& CountUsablePartyMons(sBattler_AI) == 0
&& !HasMoveEffect(sBattler_AI, EFFECT_ELECTRO_BALL));
case STAT_ACC:
@ -1681,7 +1674,7 @@ u32 CountNegativeStatStages(u32 battlerId)
bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
if (AI_IsFaster(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
@ -1700,7 +1693,7 @@ bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u32 defAbility)
bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
if (AI_IsFaster(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
@ -1719,24 +1712,19 @@ bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u32 defAbility)
bool32 ShouldLowerSpeed(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (defAbility == ABILITY_CONTRARY
|| defAbility == ABILITY_CLEAR_BODY
|| defAbility == ABILITY_FULL_METAL_BODY
|| defAbility == ABILITY_WHITE_SMOKE
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CLEAR_AMULET)
return FALSE;
if (!AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& defAbility != ABILITY_CONTRARY
&& defAbility != ABILITY_CLEAR_BODY
&& defAbility != ABILITY_FULL_METAL_BODY
&& defAbility != ABILITY_WHITE_SMOKE
&& AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_CLEAR_AMULET)
return TRUE;
return FALSE;
return (AI_IsSlower(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered));
}
bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
if (AI_IsFaster(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
@ -1754,7 +1742,7 @@ bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u32 defAbility)
bool32 ShouldLowerSpDef(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
if (AI_IsFaster(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
@ -1772,7 +1760,7 @@ bool32 ShouldLowerSpDef(u32 battlerAtk, u32 battlerDef, u32 defAbility)
bool32 ShouldLowerAccuracy(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
if (AI_IsFaster(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
@ -1791,7 +1779,7 @@ bool32 ShouldLowerAccuracy(u32 battlerAtk, u32 battlerDef, u32 defAbility)
bool32 ShouldLowerEvasion(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
if (AI_IsFaster(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
@ -2512,7 +2500,7 @@ bool32 ShouldPivot(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32
/*if (IsPredictedToSwitch(battlerDef, battlerAtk) && !hasStatBoost)
return PIVOT; // Try pivoting so you can switch to a better matchup to counter your new opponent*/
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker goes first
if (AI_IsFaster(battlerAtk, battlerDef, move)) // Attacker goes first
{
if (!CanAIFaintTarget(battlerAtk, battlerDef, 0)) // Can't KO foe otherwise
{
@ -2896,7 +2884,7 @@ u32 ShouldTryToFlinch(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbi
if (((!IsMoldBreakerTypeAbility(AI_DATA->abilities[battlerAtk]) && (defAbility == ABILITY_SHIELD_DUST || defAbility == ABILITY_INNER_FOCUS))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_COVERT_CLOAK
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER)) // Opponent goes first
|| AI_IsSlower(battlerAtk, battlerDef, move))) // Opponent goes first
{
return 0;
}
@ -2904,7 +2892,7 @@ u32 ShouldTryToFlinch(u32 battlerAtk, u32 battlerDef, u32 atkAbility, u32 defAbi
|| gBattleMons[battlerDef].status1 & STATUS1_PARALYSIS
|| gBattleMons[battlerDef].status2 & STATUS2_INFATUATION
|| gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
|| ((AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) && CanTargetFaintAi(battlerDef, battlerAtk)))
|| ((AI_IsFaster(battlerAtk, battlerDef, move)) && CanTargetFaintAi(battlerDef, battlerAtk)))
{
return 2; // good idea to flinch
}
@ -3022,7 +3010,7 @@ bool32 ShouldUseRecoilMove(u32 battlerAtk, u32 battlerDef, u32 recoilDmg, u32 mo
bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, u32 move, s32 damage)
{
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
if (move == 0xFFFF || AI_IsFaster(battlerAtk, battlerDef, move))
{
// using item or user goes first
u32 healPercent = (gMovesInfo[move].argument == 0) ? 50 : gMovesInfo[move].argument;
@ -3049,7 +3037,7 @@ bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, u32 move, s32 damage)
bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent)
{
if (move == 0xFFFF || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
if (move == 0xFFFF || AI_IsFaster(battlerAtk, battlerDef, move))
{
// using item or user going first
s32 damage = AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
@ -3070,7 +3058,9 @@ bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, u32 moveEffect)
{
u32 atkSide = GetBattlerSide(battlerAtk);
if (HasMoveEffect(battlerDef, EFFECT_BRICK_BREAK)) // Don't waste a turn if screens will be broken
// Don't waste a turn if screens will be broken
if (HasMoveEffect(battlerDef, EFFECT_BRICK_BREAK)
|| HasMoveEffect(battlerDef, EFFECT_RAGING_BULL))
return FALSE;
switch (moveEffect)
@ -3508,7 +3498,7 @@ bool32 IsRecycleEncouragedItem(u32 item)
static void IncreaseStatUpScoreInternal(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score, bool32 considerContrary)
{
u32 noOfHitsToFaint = NoOfHitsForTargetToFaintAI(battlerDef, battlerAtk);
u32 aiIsFaster = GetWhichBattlerFaster(battlerAtk, battlerDef, TRUE) == AI_IS_FASTER;
u32 aiIsFaster = AI_IsFaster(battlerAtk, battlerDef, TRUE);
u32 shouldSetUp = ((noOfHitsToFaint >= 2 && aiIsFaster) || (noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == UNKNOWN_NO_OF_HITS);
if (considerContrary && AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY)
@ -3773,7 +3763,7 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove)
else if (!IS_MOVE_STATUS(chosenMove) && IS_MOVE_STATUS(gBattleStruct->zmove.chosenZMove))
return FALSE;
if (!IS_MOVE_STATUS(chosenMove) && AI_CalcDamageSaveBattlers(chosenMove, battlerAtk, battlerDef, &effectiveness, FALSE, DMG_ROLL_AVERAGE) >= gBattleMons[battlerDef].hp)
if (!IS_MOVE_STATUS(chosenMove) && AI_CalcDamageSaveBattlers(chosenMove, battlerAtk, battlerDef, &effectiveness, FALSE, DMG_ROLL_DEFAULT) >= gBattleMons[battlerDef].hp)
return FALSE; // don't waste damaging z move if can otherwise faint target
return TRUE;
@ -3842,7 +3832,7 @@ void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerDef) != 0)
ADJUST_SCORE_PTR(-2);
if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE && AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE && AI_IsFaster(battlerAtk, battlerDef, move))
ADJUST_SCORE_PTR(-10);
if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE)
ADJUST_SCORE_PTR(GOOD_EFFECT);
@ -3888,6 +3878,6 @@ bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, u32 move, st
}
return (preventsStatLoss
&& AI_STRIKES_FIRST(battlerAtk, battlerAtkPartner, TRUE)
&& AI_IsFaster(battlerAtk, battlerAtkPartner, TRUE)
&& HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL));
}

View File

@ -30,7 +30,6 @@
extern const u16 gMovesWithQuietBGM[];
extern const u8 *const gBattleAnims_General[];
extern const u8 *const gBattleAnims_Moves[];
extern const u8 *const gBattleAnims_Special[];
extern const u8 *const gBattleAnims_StatusConditions[];
@ -234,7 +233,6 @@ static void Nop(void)
void LaunchBattleAnimation(u32 animType, u32 animId)
{
s32 i;
const u8 *const *animsTable;
if (gTestRunnerEnabled)
{
@ -249,23 +247,6 @@ void LaunchBattleAnimation(u32 animType, u32 animId)
}
}
switch (animType)
{
case ANIM_TYPE_GENERAL:
default:
animsTable = gBattleAnims_General;
break;
case ANIM_TYPE_MOVE:
animsTable = gBattleAnims_Moves;
break;
case ANIM_TYPE_STATUS:
animsTable = gBattleAnims_StatusConditions;
break;
case ANIM_TYPE_SPECIAL:
animsTable = gBattleAnims_Special;
break;
}
sAnimHideHpBoxes = !(animType == ANIM_TYPE_MOVE && animId == MOVE_TRANSFORM);
if (animType != ANIM_TYPE_MOVE)
{
@ -322,7 +303,23 @@ void LaunchBattleAnimation(u32 animType, u32 animId)
sMonAnimTaskIdArray[0] = TASK_NONE;
sMonAnimTaskIdArray[1] = TASK_NONE;
sBattleAnimScriptPtr = animsTable[animId];
switch (animType)
{
case ANIM_TYPE_GENERAL:
default:
sBattleAnimScriptPtr = gBattleAnims_General[animId];
break;
case ANIM_TYPE_MOVE:
sBattleAnimScriptPtr = GetMoveAnimationScript(animId);
break;
case ANIM_TYPE_STATUS:
sBattleAnimScriptPtr = gBattleAnims_StatusConditions[animId];
break;
case ANIM_TYPE_SPECIAL:
sBattleAnimScriptPtr = gBattleAnims_Special[animId];
break;
}
gAnimScriptActive = TRUE;
sAnimFramesToWait = 0;
gAnimScriptCallback = RunAnimScriptCommand;

View File

@ -371,6 +371,15 @@ static const struct WindowTemplate sStandardBattleWindowTemplates[] =
.paletteNum = 0,
.baseBlock = 0x00b0,
},
[B_WIN_MOVE_DESCRIPTION] = {
.bg = 0,
.tilemapLeft = 1,
.tilemapTop = 47,
.width = 18,
.height = 6,
.paletteNum = 5,
.baseBlock = 0x0350,
},
DUMMY_WIN_TEMPLATE
};
@ -583,6 +592,15 @@ static const struct WindowTemplate sBattleArenaWindowTemplates[] =
.paletteNum = 7,
.baseBlock = 0x0090,
},
[B_WIN_MOVE_DESCRIPTION] = {
.bg = 0,
.tilemapLeft = 1,
.tilemapTop = 47,
.width = 18,
.height = 6,
.paletteNum = 5,
.baseBlock = 0x0350,
},
DUMMY_WIN_TEMPLATE
};

View File

@ -31,6 +31,7 @@
#include "util.h"
#include "window.h"
#include "constants/battle_anim.h"
#include "constants/battle_move_effects.h"
#include "constants/battle_partner.h"
#include "constants/hold_effects.h"
#include "constants/items.h"
@ -40,6 +41,8 @@
#include "constants/trainers.h"
#include "constants/rgb.h"
#include "level_caps.h"
#include "menu.h"
#include "pokemon_summary_screen.h"
static void PlayerBufferExecCompleted(u32 battler);
static void PlayerHandleLoadMonSprite(u32 battler);
@ -83,6 +86,7 @@ static void MoveSelectionDisplayPpNumber(u32 battler);
static void MoveSelectionDisplayPpString(u32 battler);
static void MoveSelectionDisplayMoveType(u32 battler);
static void MoveSelectionDisplayMoveNames(u32 battler);
static void MoveSelectionDisplayMoveDescription(u32 battler);
static void HandleMoveSwitching(u32 battler);
static void SwitchIn_HandleSoundAndEnd(u32 battler);
static void WaitForMonSelection(u32 battler);
@ -158,6 +162,8 @@ static void (*const sPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) =
[CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop
};
static EWRAM_DATA bool8 sDescriptionSubmenu = 0;
static EWRAM_DATA bool8 sAckBallUseBtn = FALSE;
static EWRAM_DATA bool8 sBallSwapped = FALSE;
@ -690,7 +696,7 @@ static void HandleInputChooseMove(u32 battler)
else
gPlayerDpadHoldFrames = 0;
if (JOY_NEW(A_BUTTON))
if (JOY_NEW(A_BUTTON) && !sDescriptionSubmenu)
{
PlaySE(SE_SELECT);
@ -797,7 +803,7 @@ static void HandleInputChooseMove(u32 battler)
break;
}
}
else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59)
else if ((JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59) && !sDescriptionSubmenu)
{
PlaySE(SE_SELECT);
if (gBattleStruct->zmove.viewing)
@ -826,6 +832,8 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (sDescriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryChangeZIndicator(battler, gMoveSelectionCursor[battler]);
}
}
@ -840,6 +848,8 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (sDescriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryChangeZIndicator(battler, gMoveSelectionCursor[battler]);
}
}
@ -853,6 +863,8 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (sDescriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryChangeZIndicator(battler, gMoveSelectionCursor[battler]);
}
}
@ -867,10 +879,12 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (sDescriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryChangeZIndicator(battler, gMoveSelectionCursor[battler]);
}
}
else if (JOY_NEW(SELECT_BUTTON) && !gBattleStruct->zmove.viewing)
else if (JOY_NEW(SELECT_BUTTON) && !gBattleStruct->zmove.viewing && !sDescriptionSubmenu)
{
if (gNumberOfMovesToChoose > 1 && !(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
@ -886,6 +900,30 @@ static void HandleInputChooseMove(u32 battler)
gBattlerControllerFuncs[battler] = HandleMoveSwitching;
}
}
else if (sDescriptionSubmenu)
{
if (JOY_NEW(L_BUTTON) || JOY_NEW(A_BUTTON) || JOY_NEW(B_BUTTON))
{
sDescriptionSubmenu = FALSE;
if (gCategoryIconSpriteId != 0xFF)
{
DestroySprite(&gSprites[gCategoryIconSpriteId]);
gCategoryIconSpriteId = 0xFF;
}
FillWindowPixelBuffer(B_WIN_MOVE_DESCRIPTION, PIXEL_FILL(0));
ClearStdWindowAndFrame(B_WIN_MOVE_DESCRIPTION, FALSE);
CopyWindowToVram(B_WIN_MOVE_DESCRIPTION, COPYWIN_GFX);
PlaySE(SE_SELECT);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
}
}
else if (JOY_NEW(L_BUTTON))
{
sDescriptionSubmenu = TRUE;
MoveSelectionDisplayMoveDescription(battler);
}
else if (JOY_NEW(START_BUTTON))
{
if (CanMegaEvolve(battler))
@ -1772,6 +1810,54 @@ static void MoveSelectionDisplayMoveType(u32 battler)
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_TYPE);
}
static void MoveSelectionDisplayMoveDescription(u32 battler)
{
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[battler][4]);
u16 move = moveInfo->moves[gMoveSelectionCursor[battler]];
u16 pwr = gMovesInfo[move].power;
u16 acc = gMovesInfo[move].accuracy;
u8 cat = gMovesInfo[move].category;
u8 pwr_num[3], acc_num[3];
u8 cat_desc[7] = _("CAT: ");
u8 pwr_desc[7] = _("PWR: ");
u8 acc_desc[7] = _("ACC: ");
u8 cat_start[] = _("{CLEAR_TO 0x03}");
u8 pwr_start[] = _("{CLEAR_TO 0x38}");
u8 acc_start[] = _("{CLEAR_TO 0x6D}");
LoadMessageBoxAndBorderGfx();
DrawStdWindowFrame(B_WIN_MOVE_DESCRIPTION, FALSE);
if (pwr < 2)
StringCopy(pwr_num, gText_BattleSwitchWhich5);
else
ConvertIntToDecimalStringN(pwr_num, pwr, STR_CONV_MODE_LEFT_ALIGN, 3);
if (acc < 2)
StringCopy(acc_num, gText_BattleSwitchWhich5);
else
ConvertIntToDecimalStringN(acc_num, acc, STR_CONV_MODE_LEFT_ALIGN, 3);
StringCopy(gDisplayedStringBattle, cat_start);
StringAppend(gDisplayedStringBattle, cat_desc);
StringAppend(gDisplayedStringBattle, pwr_start);
StringAppend(gDisplayedStringBattle, pwr_desc);
StringAppend(gDisplayedStringBattle, pwr_num);
StringAppend(gDisplayedStringBattle, acc_start);
StringAppend(gDisplayedStringBattle, acc_desc);
StringAppend(gDisplayedStringBattle, acc_num);
StringAppend(gDisplayedStringBattle, gText_NewLine);
if (gMovesInfo[move].effect == EFFECT_PLACEHOLDER)
StringAppend(gDisplayedStringBattle, gNotDoneYetDescription);
else
StringAppend(gDisplayedStringBattle, gMovesInfo[move].description);
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_DESCRIPTION);
if (gCategoryIconSpriteId == 0xFF)
gCategoryIconSpriteId = CreateSprite(&gSpriteTemplate_CategoryIcons, 38, 64, 1);
StartSpriteAnim(&gSprites[gCategoryIconSpriteId], cat);
CopyWindowToVram(B_WIN_MOVE_DESCRIPTION, COPYWIN_FULL);
}
void MoveSelectionCreateCursorAt(u8 cursorPosition, u8 baseTileNum)
{
u16 src[2];

View File

@ -207,6 +207,7 @@ void UndoDynamax(u16 battlerId)
u16 mult = UQ_4_12(1.0/1.5); // placeholder
gBattleMons[battlerId].hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * mult + 1) + UQ_4_12_ROUND); // round up
SetMonData(mon, MON_DATA_HP, &gBattleMons[battlerId].hp);
CalculateMonStats(mon);
}
// Makes sure there are no Dynamax flags set, including on switch / faint.

View File

@ -707,6 +707,7 @@ bool8 BattleLoadAllHealthBoxesGfx(u8 state)
LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[1]);
MegaIndicator_LoadSpritesGfx();
TeraIndicator_LoadSpriteGfx();
CategoryIcons_LoadSpritesGfx();
}
else if (!IsDoubleBattle())
{

View File

@ -3753,3 +3753,9 @@ void ArrowsChangeColorLastBallCycle(bool32 showArrows)
}
#endif
}
void CategoryIcons_LoadSpritesGfx(void)
{
LoadCompressedSpriteSheet(&gSpriteSheet_CategoryIcons);
LoadSpritePalette(&gSpritePal_CategoryIcons);
}

View File

@ -54,6 +54,7 @@
#include "wild_encounter.h"
#include "window.h"
#include "constants/abilities.h"
#include "constants/battle_ai.h"
#include "constants/battle_move_effects.h"
#include "constants/battle_string_ids.h"
#include "constants/battle_partner.h"
@ -229,6 +230,7 @@ EWRAM_DATA u16 gBallToDisplay = 0;
EWRAM_DATA bool8 gLastUsedBallMenuPresent = FALSE;
EWRAM_DATA u8 gPartyCriticalHits[PARTY_SIZE] = {0};
EWRAM_DATA static u8 sTriedEvolving = 0;
EWRAM_DATA u8 gCategoryIconSpriteId = 0;
void (*gPreBattleCallback1)(void);
void (*gBattleMainFunc)(void);
@ -294,336 +296,7 @@ const struct OamData gOamData_BattleSpritePlayerSide =
static const s8 sCenterToCornerVecXs[8] ={-32, -16, -16, -32, -32};
#if B_EXPANDED_TYPE_NAMES == TRUE
#define HANDLE_EXPANDED_TYPE_NAME(_name, ...) _(DEFAULT(_name, __VA_ARGS__))
#else
#define HANDLE_EXPANDED_TYPE_NAME(_name, ...) _(_name)
#endif
// .generic is large enough that the text for TYPE_ELECTRIC will exceed TEXT_BUFF_ARRAY_COUNT.
const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
{
[TYPE_NORMAL] =
{
.name = _("Normal"),
.generic = _("a NORMAL move"),
.palette = 13,
.zMove = MOVE_BREAKNECK_BLITZ,
.maxMove = MOVE_MAX_STRIKE,
.paletteTMHM = gItemIconPalette_NormalTMHM,
//.enhanceItem = ITEM_SILK_SCARF,
//.berry = ITEM_CHILAN_BERRY,
//.gem = ITEM_NORMAL_GEM,
//.zCrystal = ITEM_NORMALIUM_Z,
//.teraShard = ITEM_NORMAL_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_NORMAL,
},
[TYPE_FIGHTING] =
{
.name = HANDLE_EXPANDED_TYPE_NAME("Fight", "Fighting"),
.generic = _("a FIGHTING move"),
.palette = 13,
.zMove = MOVE_ALL_OUT_PUMMELING,
.maxMove = MOVE_MAX_KNUCKLE,
.paletteTMHM = gItemIconPalette_FightingTMHM,
//.enhanceItem = ITEM_BLACK_BELT,
//.berry = ITEM_CHOPLE_BERRY,
//.gem = ITEM_FIGHTING_GEM,
//.zCrystal = ITEM_FIGHTINIUM_Z,
//.plate = ITEM_FIST_PLATE,
//.memory = ITEM_FIGHTING_MEMORY,
//.teraShard = ITEM_FIGHTING_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_FIGHTING,
},
[TYPE_FLYING] =
{
.name = _("Flying"),
.generic = _("a FLYING move"),
.palette = 14,
.zMove = MOVE_SUPERSONIC_SKYSTRIKE,
.maxMove = MOVE_MAX_AIRSTREAM,
.paletteTMHM = gItemIconPalette_FlyingTMHM,
//.enhanceItem = ITEM_SHARP_BEAK,
//.berry = ITEM_COBA_BERRY,
//.gem = ITEM_FLYING_GEM,
//.zCrystal = ITEM_FLYINIUM_Z,
//.plate = ITEM_SKY_PLATE,
//.memory = ITEM_FLYING_MEMORY,
//.teraShard = ITEM_FLYING_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_FLYING,
},
[TYPE_POISON] =
{
.name = _("Poison"),
.generic = _("a POISON move"),
.palette = 14,
.zMove = MOVE_ACID_DOWNPOUR,
.maxMove = MOVE_MAX_OOZE,
.paletteTMHM = gItemIconPalette_PoisonTMHM,
//.enhanceItem = ITEM_POISON_BARB,
//.berry = ITEM_KEBIA_BERRY,
//.gem = ITEM_POISON_GEM,
//.zCrystal = ITEM_POISONIUM_Z,
//.plate = ITEM_TOXIC_PLATE,
//.memory = ITEM_POISON_MEMORY,
//.teraShard = ITEM_POISON_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_POISON,
},
[TYPE_GROUND] =
{
.name = _("Ground"),
.generic = _("a GROUND move"),
.palette = 13,
.zMove = MOVE_TECTONIC_RAGE,
.maxMove = MOVE_MAX_QUAKE,
.paletteTMHM = gItemIconPalette_GroundTMHM,
//.enhanceItem = ITEM_SOFT_SAND,
//.berry = ITEM_SHUCA_BERRY,
//.gem = ITEM_GROUND_GEM,
//.zCrystal = ITEM_GROUNDIUM_Z,
//.plate = ITEM_EARTH_PLATE,
//.memory = ITEM_GROUND_MEMORY,
//.teraShard = ITEM_GROUND_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_GROUND,
},
[TYPE_ROCK] =
{
.name = _("Rock"),
.generic = _("a ROCK move"),
.palette = 13,
.zMove = MOVE_CONTINENTAL_CRUSH,
.maxMove = MOVE_MAX_ROCKFALL,
.paletteTMHM = gItemIconPalette_RockTMHM,
//.enhanceItem = ITEM_HARD_STONE,
//.berry = ITEM_CHARTI_BERRY,
//.gem = ITEM_ROCK_GEM,
//.zCrystal = ITEM_ROCKIUM_Z,
//.plate = ITEM_STONE_PLATE,
//.memory = ITEM_ROCK_MEMORY,
//.teraShard = ITEM_ROCK_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_ROCK,
},
[TYPE_BUG] =
{
.name = _("Bug"),
.generic = _("a BUG move"),
.palette = 15,
.zMove = MOVE_SAVAGE_SPIN_OUT,
.maxMove = MOVE_MAX_FLUTTERBY,
.paletteTMHM = gItemIconPalette_BugTMHM,
//.enhanceItem = ITEM_SILVER_POWDER,
//.berry = ITEM_TANGA_BERRY,
//.gem = ITEM_BUG_GEM,
//.zCrystal = ITEM_BUGINIUM_Z,
//.plate = ITEM_INSECT_PLATE,
//.memory = ITEM_BUG_MEMORY,
//.teraShard = ITEM_BUG_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_BUG,
},
[TYPE_GHOST] =
{
.name = _("Ghost"),
.generic = _("a GHOST move"),
.palette = 14,
.zMove = MOVE_NEVER_ENDING_NIGHTMARE,
.maxMove = MOVE_MAX_PHANTASM,
.paletteTMHM = gItemIconPalette_GhostTMHM,
//.enhanceItem = ITEM_SPELL_TAG,
//.berry = ITEM_KASIB_BERRY,
//.gem = ITEM_GHOST_GEM,
//.zCrystal = ITEM_GHOSTIUM_Z,
//.plate = ITEM_SPOOKY_PLATE,
//.memory = ITEM_GHOST_MEMORY,
//.teraShard = ITEM_GHOST_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_GHOST,
},
[TYPE_STEEL] =
{
.name = _("Steel"),
.generic = _("a STEEL move"),
.palette = 13,
.zMove = MOVE_CORKSCREW_CRASH,
.maxMove = MOVE_MAX_STEELSPIKE,
.paletteTMHM = gItemIconPalette_SteelTMHM,
//.enhanceItem = ITEM_METAL_COAT,
//.berry = ITEM_BABIRI_BERRY,
//.gem = ITEM_STEEL_GEM,
//.zCrystal = ITEM_STEELIUM_Z,
//.plate = ITEM_IRON_PLATE,
//.memory = ITEM_STEEL_MEMORY,
//.teraShard = ITEM_STEEL_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_STEEL,
},
[TYPE_MYSTERY] =
{
.name = _("???"),
.generic = _("a ??? move"),
.palette = 15,
},
[TYPE_FIRE] =
{
.name = _("Fire"),
.generic = _("a FIRE move"),
.palette = 13,
.zMove = MOVE_INFERNO_OVERDRIVE,
.maxMove = MOVE_MAX_FLARE,
.paletteTMHM = gItemIconPalette_FireTMHM,
//.enhanceItem = ITEM_CHARCOAL,
//.berry = ITEM_OCCA_BERRY,
//.gem = ITEM_FIRE_GEM,
//.zCrystal = ITEM_FIRIUM_Z,
//.plate = ITEM_FLAME_PLATE,
//.memory = ITEM_FIRE_MEMORY,
//.teraShard = ITEM_FIRE_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_FIRE,
},
[TYPE_WATER] =
{
.name = _("Water"),
.generic = _("a WATER move"),
.palette = 14,
.zMove = MOVE_HYDRO_VORTEX,
.maxMove = MOVE_MAX_GEYSER,
.paletteTMHM = gItemIconPalette_WaterTMHM,
//.enhanceItem = ITEM_MYSTIC_WATER,
//.berry = ITEM_PASSHO_BERRY,
//.gem = ITEM_WATER_GEM,
//.zCrystal = ITEM_WATERIUM_Z,
//.plate = ITEM_SPLASH_PLATE,
//.memory = ITEM_WATER_MEMORY,
//.teraShard = ITEM_WATER_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_WATER,
},
[TYPE_GRASS] =
{
.name = _("Grass"),
.generic = _("a GRASS move"),
.palette = 15,
.zMove = MOVE_BLOOM_DOOM,
.maxMove = MOVE_MAX_OVERGROWTH,
.paletteTMHM = gItemIconPalette_GrassTMHM,
//.enhanceItem = ITEM_MIRACLE_SEED,
//.berry = ITEM_RINDO_BERRY,
//.gem = ITEM_GRASS_GEM,
//.zCrystal = ITEM_GRASSIUM_Z,
//.plate = ITEM_MEADOW_PLATE,
//.memory = ITEM_GRASS_MEMORY,
//.teraShard = ITEM_GRASS_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_GRASS,
},
[TYPE_ELECTRIC] =
{
.name = HANDLE_EXPANDED_TYPE_NAME("Electr", "Electric"),
.generic = _("an ELECTRIC move"),
.palette = 13,
.zMove = MOVE_GIGAVOLT_HAVOC,
.maxMove = MOVE_MAX_LIGHTNING,
.paletteTMHM = gItemIconPalette_ElectricTMHM,
//.enhanceItem = ITEM_MAGNET,
//.berry = ITEM_WACAN_BERRY,
//.gem = ITEM_ELECTRIC_GEM,
//.zCrystal = ITEM_ELECTRIUM_Z,
//.plate = ITEM_ZAP_PLATE,
//.memory = ITEM_ELECTRIC_MEMORY,
//.teraShard = ITEM_ELECTRIC_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_ELECTRIC,
},
[TYPE_PSYCHIC] =
{
.name = HANDLE_EXPANDED_TYPE_NAME("Psychc", "Psychic"),
.generic = _("a PSYCHIC move"),
.palette = 14,
.zMove = MOVE_SHATTERED_PSYCHE,
.maxMove = MOVE_MAX_MINDSTORM,
.paletteTMHM = gItemIconPalette_PsychicTMHM,
//.enhanceItem = ITEM_TWISTED_SPOON,
//.berry = ITEM_PAYAPA_BERRY,
//.gem = ITEM_PSYCHIC_GEM,
//.zCrystal = ITEM_PSYCHIUM_Z,
//.plate = ITEM_MIND_PLATE,
//.memory = ITEM_PSYCHIC_MEMORY,
//.teraShard = ITEM_PSYCHIC_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_PSYCHIC,
},
[TYPE_ICE] =
{
.name = _("Ice"),
.generic = _("an ICE move"),
.palette = 14,
.zMove = MOVE_SUBZERO_SLAMMER,
.maxMove = MOVE_MAX_HAILSTORM,
.paletteTMHM = gItemIconPalette_IceTMHM,
//.enhanceItem = ITEM_NEVER_MELT_ICE,
//.berry = ITEM_YACHE_BERRY,
//.gem = ITEM_ICE_GEM,
//.zCrystal = ITEM_ICIUM_Z,
//.plate = ITEM_ICICLE_PLATE,
//.memory = ITEM_ICE_MEMORY,
//.teraShard = ITEM_ICE_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_ICE,
},
[TYPE_DRAGON] =
{
.name = _("Dragon"),
.generic = _("a DRAGON move"),
.palette = 15,
.zMove = MOVE_DEVASTATING_DRAKE,
.maxMove = MOVE_MAX_WYRMWIND,
.paletteTMHM = gItemIconPalette_DragonTMHM,
//.enhanceItem = ITEM_DRAGON_FANG,
//.berry = ITEM_HABAN_BERRY,
//.gem = ITEM_DRAGON_GEM,
//.zCrystal = ITEM_DRAGONIUM_Z,
//.plate = ITEM_DRACO_PLATE,
//.memory = ITEM_DRAGON_MEMORY,
//.teraShard = ITEM_DRAGON_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_DRAGON,
},
[TYPE_DARK] =
{
.name = _("Dark"),
.generic = _("a DARK move"),
.palette = 13,
.zMove = MOVE_BLACK_HOLE_ECLIPSE,
.maxMove = MOVE_MAX_DARKNESS,
.paletteTMHM = gItemIconPalette_DarkTMHM,
//.enhanceItem = ITEM_BLACK_GLASSES,
//.berry = ITEM_COLBUR_BERRY,
//.gem = ITEM_DARK_GEM,
//.zCrystal = ITEM_DARKINIUM_Z,
//.plate = ITEM_DREAD_PLATE,
//.memory = ITEM_DARK_MEMORY,
//.teraShard = ITEM_DARK_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_DARK,
},
[TYPE_FAIRY] =
{
.name = _("Fairy"),
.generic = _("a FAIRY move"),
.palette = 14,
.zMove = MOVE_TWINKLE_TACKLE,
.maxMove = MOVE_MAX_STARFALL,
.paletteTMHM = gItemIconPalette_FairyTMHM,
//.enhanceItem = ITEM_FAIRY_FEATHER,
//.berry = ITEM_ROSELI_BERRY,
//.gem = ITEM_FAIRY_GEM,
//.zCrystal = ITEM_FAIRIUM_Z,
//.plate = ITEM_PIXIE_PLATE,
//.memory = ITEM_FAIRY_MEMORY,
//.teraShard = ITEM_FAIRY_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_FAIRY,
},
[TYPE_STELLAR] =
{
.name = HANDLE_EXPANDED_TYPE_NAME("Stellr", "Stellar"),
.generic = _("a STELLAR move"),
.palette = 15,
.zMove = MOVE_BREAKNECK_BLITZ,
.maxMove = MOVE_MAX_STRIKE,
.paletteTMHM = gItemIconPalette_NormalTMHM, // failsafe
// .teraShard = ITEM_STELLAR_TERA_SHARD,
},
};
#include "data/types_info.h"
// extra args are money and ball
#define TRAINER_CLASS(trainerClass, trainerName, ...) \
@ -3446,6 +3119,7 @@ static void BattleStartClearSetData(void)
gBattleStruct->swapDamageCategory = FALSE; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
gSelectedMonPartyId = PARTY_SIZE; // Revival Blessing
gCategoryIconSpriteId = 0xFF;
}
void SwitchInClearSetData(u32 battler)
@ -3746,6 +3420,9 @@ const u8* FaintClearSetData(u32 battler)
gBattleStruct->zmove.active = FALSE;
gBattleStruct->zmove.toBeUsed[battler] = MOVE_NONE;
gBattleStruct->zmove.effect = EFFECT_HIT;
// Clear Dynamax data
UndoDynamax(battler);
return result;
}
@ -4469,7 +4146,10 @@ static void HandleTurnActionSelectionState(void)
if ((gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart())
&& (BattlerHasAi(battler) && !(gBattleTypeFlags & BATTLE_TYPE_PALACE)))
{
AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, FALSE);
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_RISKY) // Risky AI switches aggressively even mid battle
AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, TRUE);
else
AI_DATA->mostSuitableMonId[battler] = GetMostSuitableMonToSwitchInto(battler, FALSE);
gBattleStruct->aiMoveOrAction[battler] = ComputeBattleAiScores(battler);
}
// fallthrough
@ -5122,7 +4802,7 @@ s8 GetMovePriority(u32 battler, u16 move)
gProtectStructs[battler].pranksterElevated = 1;
priority++;
}
else if (gMovesInfo[move].effect == EFFECT_GRASSY_GLIDE && gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battler))
else if (gMovesInfo[move].effect == EFFECT_GRASSY_GLIDE && gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battler) && !IsDynamaxed(battler) && !(gBattleStruct->dynamax.toDynamax & gBitTable[battler]))
{
priority++;
}
@ -6001,9 +5681,59 @@ void RunBattleScriptCommands(void)
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
bool32 TrySetAteType(u32 move, u32 battlerAtk, u32 attackerAbility)
{
u32 ateType;
switch (gMovesInfo[move].effect)
{
case EFFECT_TERA_BLAST:
if (IsTerastallized(battlerAtk))
return FALSE;
break;
case EFFECT_TERA_STARSTORM:
if (gBattleMons[battlerAtk].species == SPECIES_TERAPAGOS_STELLAR)
return FALSE;
break;
case EFFECT_HIDDEN_POWER:
case EFFECT_WEATHER_BALL:
case EFFECT_CHANGE_TYPE_ON_ITEM:
case EFFECT_NATURAL_GIFT:
return FALSE;
}
ateType = TYPE_NONE;
switch (attackerAbility)
{
case ABILITY_PIXILATE:
ateType = TYPE_FAIRY;
break;
case ABILITY_REFRIGERATE:
ateType = TYPE_ICE;
break;
case ABILITY_AERILATE:
ateType = TYPE_FLYING;
break;
case ABILITY_GALVANIZE:
ateType = TYPE_ELECTRIC;
break;
default:
ateType = TYPE_NONE;
break;
}
if (ateType != TYPE_NONE)
{
gBattleStruct->dynamicMoveType = ateType | F_DYNAMIC_TYPE_SET;
return TRUE;
}
return FALSE;
}
void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
{
u32 moveType, ateType, attackerAbility;
u32 moveType, attackerAbility;
u16 holdEffect = GetBattlerHoldEffect(battlerAtk, TRUE);
if (move == MOVE_STRUGGLE)
@ -6038,9 +5768,9 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
| ((gBattleMons[battlerAtk].spAttackIV & 1) << 4)
| ((gBattleMons[battlerAtk].spDefenseIV & 1) << 5);
// Subtract 4 instead of 1 below because 3 types are excluded (TYPE_NORMAL and TYPE_MYSTERY and TYPE_FAIRY)
// The final + 1 skips past Normal, and the following conditional skips TYPE_MYSTERY
gBattleStruct->dynamicMoveType = ((NUMBER_OF_MON_TYPES - 4) * typeBits) / 63 + 1;
// Subtract 6 instead of 1 below because 5 types are excluded (TYPE_NONE, TYPE_NORMAL, TYPE_MYSTERY, TYPE_FAIRY and TYPE_STELLAR)
// The final + 2 skips past TYPE_NONE and Normal.
gBattleStruct->dynamicMoveType = ((NUMBER_OF_MON_TYPES - 6) * typeBits) / 63 + 2;
if (gBattleStruct->dynamicMoveType >= TYPE_MYSTERY)
gBattleStruct->dynamicMoveType++;
gBattleStruct->dynamicMoveType |= F_DYNAMIC_TYPE_IGNORE_PHYSICALITY | F_DYNAMIC_TYPE_SET;
@ -6105,29 +5835,15 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
}
attackerAbility = GetBattlerAbility(battlerAtk);
if (gMovesInfo[move].type == TYPE_NORMAL
&& gMovesInfo[move].effect != EFFECT_HIDDEN_POWER
&& gMovesInfo[move].effect != EFFECT_WEATHER_BALL
&& gMovesInfo[move].effect != EFFECT_CHANGE_TYPE_ON_ITEM
&& gMovesInfo[move].effect != EFFECT_NATURAL_GIFT
&& !(gMovesInfo[move].effect == EFFECT_TERA_BLAST && IsTerastallized(battlerAtk))
&& !(gMovesInfo[move].effect == EFFECT_TERA_STARSTORM && gBattleMons[battlerAtk].species == SPECIES_TERAPAGOS_STELLAR)
&& ((attackerAbility == ABILITY_PIXILATE && (ateType = TYPE_FAIRY))
|| (attackerAbility == ABILITY_REFRIGERATE && (ateType = TYPE_ICE))
|| (attackerAbility == ABILITY_AERILATE && (ateType = TYPE_FLYING))
|| ((attackerAbility == ABILITY_GALVANIZE) && (ateType = TYPE_ELECTRIC))
)
)
if (gMovesInfo[move].type == TYPE_NORMAL && TrySetAteType(move, battlerAtk, attackerAbility))
{
gBattleStruct->dynamicMoveType = ateType | F_DYNAMIC_TYPE_SET;
if (!IsDynamaxed(battlerAtk))
gBattleStruct->ateBoost[battlerAtk] = 1;
}
else if (gMovesInfo[move].type != TYPE_NORMAL
&& gMovesInfo[move].effect != EFFECT_HIDDEN_POWER
&& gMovesInfo[move].effect != EFFECT_WEATHER_BALL
&& attackerAbility == ABILITY_NORMALIZE)
&& gMovesInfo[move].effect != EFFECT_HIDDEN_POWER
&& gMovesInfo[move].effect != EFFECT_WEATHER_BALL
&& attackerAbility == ABILITY_NORMALIZE)
{
gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_SET;
if (!IsDynamaxed(battlerAtk))

View File

@ -91,7 +91,7 @@ static const u8 sText_CantEscape2[] = _("Can't escape!\p");
static const u8 sText_AttackerCantEscape[] = _("{B_ATK_NAME_WITH_PREFIX} can't escape!");
static const u8 sText_HitXTimes[] = _("Hit {B_BUFF1} time(s)!");
static const u8 sText_PkmnFellAsleep[] = _("{B_EFF_NAME_WITH_PREFIX}\nfell asleep!");
static const u8 sText_PkmnMadeSleep[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY}\nmade {B_EFF_NAME_WITH_PREFIX} sleep!");
static const u8 sText_PkmnMadeSleep[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_BUFF1}\nmade {B_EFF_NAME_WITH_PREFIX} sleep!");
static const u8 sText_PkmnAlreadyAsleep[] = _("{B_DEF_NAME_WITH_PREFIX} is\nalready asleep!");
static const u8 sText_PkmnAlreadyAsleep2[] = _("{B_ATK_NAME_WITH_PREFIX} is\nalready asleep!");
static const u8 sText_PkmnWasntAffected[] = _("{B_DEF_NAME_WITH_PREFIX}\nwasn't affected!");
@ -103,12 +103,12 @@ static const u8 sText_PkmnBadlyPoisoned[] = _("{B_EFF_NAME_WITH_PREFIX} is badly
static const u8 sText_PkmnEnergyDrained[] = _("{B_DEF_NAME_WITH_PREFIX} had its\nenergy drained!");
static const u8 sText_PkmnWasBurned[] = _("{B_EFF_NAME_WITH_PREFIX} was burned!");
static const u8 sText_PkmnGotFrostbite[] = _("{B_EFF_NAME_WITH_PREFIX} got frostbite!");
static const u8 sText_PkmnBurnedBy[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY}\nburned {B_EFF_NAME_WITH_PREFIX}!");
static const u8 sText_PkmnBurnedBy[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_BUFF1}\nburned {B_EFF_NAME_WITH_PREFIX}!");
static const u8 sText_PkmnHurtByBurn[] = _("{B_ATK_NAME_WITH_PREFIX} is hurt\nby its burn!");
static const u8 sText_PkmnHurtByFrostbite[] = _("{B_ATK_NAME_WITH_PREFIX} is hurt\nby its frostbite!");
static const u8 sText_PkmnAlreadyHasBurn[] = _("{B_DEF_NAME_WITH_PREFIX} already\nhas a burn.");
static const u8 sText_PkmnWasFrozen[] = _("{B_EFF_NAME_WITH_PREFIX} was\nfrozen solid!");
static const u8 sText_PkmnFrozenBy[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY}\nfroze {B_EFF_NAME_WITH_PREFIX} solid!");
static const u8 sText_PkmnFrozenBy[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_BUFF1}\nfroze {B_EFF_NAME_WITH_PREFIX} solid!");
static const u8 sText_PkmnIsFrozen[] = _("{B_ATK_NAME_WITH_PREFIX} is\nfrozen solid!");
static const u8 sText_PkmnWasDefrosted[] = _("{B_DEF_NAME_WITH_PREFIX} was\ndefrosted!");
static const u8 sText_PkmnWasDefrosted2[] = _("{B_ATK_NAME_WITH_PREFIX} was\ndefrosted!");
@ -117,7 +117,7 @@ static const u8 sText_PkmnFrostbiteHealed[] = _("{B_DEF_NAME_WITH_PREFIX}'s\nfro
static const u8 sText_PkmnFrostbiteHealed2[] = _("{B_ATK_NAME_WITH_PREFIX}'s\nfrostbite was healed!");
static const u8 sText_PkmnFrostbiteHealedBy[] = _("{B_ATK_NAME_WITH_PREFIX}'s {B_CURRENT_MOVE}\nhealed its frostbite!");
static const u8 sText_PkmnWasParalyzed[] = _("{B_EFF_NAME_WITH_PREFIX} is paralyzed!\nIt may be unable to move!");
static const u8 sText_PkmnWasParalyzedBy[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_SCR_ACTIVE_ABILITY}\nparalyzed {B_EFF_NAME_WITH_PREFIX}!\lIt may be unable to move!");
static const u8 sText_PkmnWasParalyzedBy[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_BUFF1}\nparalyzed {B_EFF_NAME_WITH_PREFIX}!\lIt may be unable to move!");
static const u8 sText_PkmnIsParalyzed[] = _("{B_ATK_NAME_WITH_PREFIX} is paralyzed!\nIt can't move!");
static const u8 sText_PkmnIsAlreadyParalyzed[] = _("{B_DEF_NAME_WITH_PREFIX} is\nalready paralyzed!");
static const u8 sText_PkmnHealedParalysis[] = _("{B_DEF_NAME_WITH_PREFIX} was\nhealed of paralysis!");
@ -2492,6 +2492,18 @@ static const struct BattleWindowText sTextOnWindowsInfo_Normal[] =
.fgColor = 1,
.shadowColor = 6,
},
[B_WIN_MOVE_DESCRIPTION] = {
.fillValue = PIXEL_FILL(0xE),
.fontId = FONT_NARROW,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.speed = 0,
.fgColor = TEXT_DYNAMIC_COLOR_4,
.bgColor = TEXT_DYNAMIC_COLOR_5,
.shadowColor = TEXT_DYNAMIC_COLOR_6,
},
};
static const struct BattleWindowText sTextOnWindowsInfo_Arena[] =
@ -2725,6 +2737,18 @@ static const struct BattleWindowText sTextOnWindowsInfo_Arena[] =
.bgColor = 1,
.shadowColor = 3,
},
[B_WIN_MOVE_DESCRIPTION] = {
.fillValue = PIXEL_FILL(0xE),
.fontId = FONT_NARROW,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.speed = 0,
.fgColor = TEXT_DYNAMIC_COLOR_4,
.bgColor = TEXT_DYNAMIC_COLOR_5,
.shadowColor = TEXT_DYNAMIC_COLOR_6,
},
};
static const struct BattleWindowText *const sBattleTextOnWindowsInfo[] =

View File

@ -337,6 +337,8 @@ static bool8 CanBurnHitThaw(u16 move);
static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent);
static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 usedMove);
static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr, u16 move);
static void SaveBattlerAttacker(u32 battler);
static void SaveBattlerTarget(u32 battler);
static void Cmd_attackcanceler(void);
static void Cmd_accuracycheck(void);
@ -6168,7 +6170,8 @@ static void Cmd_moveend(void)
&& CanBattlerSwitch(gBattlerAttacker))
{
gLastUsedItem = gBattleMons[battler].item;
gBattleStruct->savedBattlerTarget = gBattleScripting.battler = battler; // Battler with red card
SaveBattlerTarget(battler); // save battler with red card
gBattleScripting.battler = battler;
gEffectBattler = gBattlerAttacker;
if (gMovesInfo[gCurrentMove].effect == EFFECT_HIT_ESCAPE)
gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection
@ -6228,8 +6231,19 @@ static void Cmd_moveend(void)
case MOVEEND_DANCER: // Special case because it's so annoying
if (gMovesInfo[gCurrentMove].danceMove)
{
u8 battler, nextDancer = 0;
u32 battler, nextDancer = 0;
bool32 turnOnHitmarker = FALSE;
for (battler = 0; battler < MAX_BATTLERS_COUNT; battler++)
{
if (gSpecialStatuses[battler].dancerUsedMove)
{
// in case a battler fails to act on a Dancer-called move
turnOnHitmarker = TRUE;
break;
}
}
if (!(gBattleStruct->lastMoveFailed & gBitTable[gBattlerAttacker]
|| (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove
&& gBattleStruct->bouncedMoveIsUsed)))
@ -6245,6 +6259,8 @@ static void Cmd_moveend(void)
{
if (GetBattlerAbility(battler) == ABILITY_DANCER && !gSpecialStatuses[battler].dancerUsedMove)
{
if (turnOnHitmarker)
gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED;
if (!nextDancer || (gBattleMons[battler].speed < gBattleMons[nextDancer & 0x3].speed))
nextDancer = battler | 0x4;
}
@ -6325,6 +6341,25 @@ static void Cmd_moveend(void)
&& (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) // And it is unusable
&& (gBattleMons[gBattlerAttacker].status2 & STATUS2_LOCK_CONFUSE) != STATUS2_LOCK_CONFUSE_TURN(1)) // And won't end this turn
CancelMultiTurnMoves(gBattlerAttacker); // Cancel it
if (gBattleStruct->savedAttackerCount > 0)
{
// #if TESTING
// Test_ExitWithResult(TEST_RESULT_ERROR, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!");
// #else
DebugPrintfLevel(MGBA_LOG_WARN, "savedAttackerCount is greater than 0! More calls to SaveBattlerAttacker than RestoreBattlerAttacker!");
// #endif
}
if (gBattleStruct->savedTargetCount > 0)
{
// #if TESTING
// Test_ExitWithResult(TEST_RESULT_ERROR, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!");
// #else
DebugPrintfLevel(MGBA_LOG_WARN, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!");
// #endif
}
gBattleStruct->targetsDone[gBattlerAttacker] = 0;
gProtectStructs[gBattlerAttacker].targetAffected = FALSE;
@ -9379,18 +9414,6 @@ static void Cmd_various(void)
AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0);
return;
}
case VARIOUS_SAVE_TARGET:
{
VARIOUS_ARGS();
gBattleStruct->savedBattlerTarget = gBattlerTarget;
break;
}
case VARIOUS_RESTORE_TARGET:
{
VARIOUS_ARGS();
gBattlerTarget = gBattleStruct->savedBattlerTarget;
break;
}
case VARIOUS_INSTANT_HP_DROP:
{
VARIOUS_ARGS();
@ -15655,6 +15678,75 @@ static void Cmd_callnative(void)
}
// Callnative Funcs
static void SaveBattlerTarget(u32 battler)
{
if (gBattleStruct->savedTargetCount < NELEMS(gBattleStruct->savedBattlerTarget))
gBattleStruct->savedBattlerTarget[gBattleStruct->savedTargetCount++] = battler;
else
DebugPrintfLevel(MGBA_LOG_WARN, "Attempting to exceed savedBattlerTarget array size!");
}
static void SaveBattlerAttacker(u32 battler)
{
if (gBattleStruct->savedAttackerCount < NELEMS(gBattleStruct->savedBattlerAttacker))
gBattleStruct->savedBattlerAttacker[gBattleStruct->savedAttackerCount++] = battler;
else
DebugPrintfLevel(MGBA_LOG_WARN, "Attempting to exceed savedBattlerAttacker array size!");
}
void BS_SaveTarget(void)
{
NATIVE_ARGS();
SaveBattlerTarget(gBattlerTarget);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_RestoreTarget(void)
{
NATIVE_ARGS();
if (gBattleStruct->savedTargetCount > 0)
{
gBattleStruct->savedTargetCount--;
gBattlerTarget = gBattleStruct->savedBattlerTarget[gBattleStruct->savedTargetCount];
}
else
{
// #if TESTING
// Test_ExitWithResult(TEST_RESULT_ERROR, "BS_RestoreTarget attempting to restore an empty target!");
// #else
DebugPrintfLevel(MGBA_LOG_WARN, "BS_RestoreTarget attempting to restore an empty target!");
// #endif
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_SaveAttacker(void)
{
NATIVE_ARGS();
SaveBattlerAttacker(gBattlerAttacker);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_RestoreAttacker(void)
{
NATIVE_ARGS();
if (gBattleStruct->savedAttackerCount > 0)
{
gBattleStruct->savedAttackerCount--;
gBattlerAttacker = gBattleStruct->savedBattlerAttacker[gBattleStruct->savedAttackerCount];
}
else
{
// #if TESTING
// Test_ExitWithResult(TEST_RESULT_ERROR, "BS_RestoreAttacker attempting to restore an empty attacker!");
// #else
DebugPrintfLevel(MGBA_LOG_WARN, "BS_RestoreAttacker attempting to restore an empty attacker!");
// #endif
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_CalcMetalBurstDmg(void)
{
NATIVE_ARGS(const u8 *failInstr);

View File

@ -169,33 +169,9 @@ uq4_12_t GetTeraMultiplier(u32 battler, u32 type)
}
}
// Most values pulled from the Tera type icon palette.
const u16 sTeraTypeRGBValues[NUMBER_OF_MON_TYPES] = {
[TYPE_NORMAL] = RGB_WHITE, // custom
[TYPE_FIGHTING] = RGB(26, 8, 14),
[TYPE_FLYING] = RGB(31, 26, 7),
[TYPE_POISON] = RGB(26, 10, 25), // custom
[TYPE_GROUND] = RGB(25, 23, 18),
[TYPE_ROCK] = RGB(18, 16, 8), // custom
[TYPE_BUG] = RGB(18, 24, 6),
[TYPE_GHOST] = RGB(12, 10, 16),
[TYPE_STEEL] = RGB(19, 19, 20),
[TYPE_MYSTERY] = RGB_WHITE,
[TYPE_FIRE] = RGB(31, 20, 11),
[TYPE_WATER] = RGB(10, 18, 27),
[TYPE_GRASS] = RGB(12, 24, 11),
[TYPE_ELECTRIC] = RGB(30, 26, 7),
[TYPE_PSYCHIC] = RGB(31, 14, 15),
[TYPE_ICE] = RGB(14, 26, 25),
[TYPE_DRAGON] = RGB(10, 18, 27),
[TYPE_DARK] = RGB(6, 5, 8),
[TYPE_FAIRY] = RGB(31, 15, 21),
[TYPE_STELLAR] = RGB(10, 18, 27),
};
u16 GetTeraTypeRGB(u32 type)
{
return sTeraTypeRGBValues[type];
return gTypesInfo[type].teraTypeRGBValue;
}
// TERASTAL TRIGGER:
@ -636,6 +612,7 @@ static const struct SpriteTemplate sSpriteTemplate_StellarIndicator =
static const struct SpriteSheet sTeraIndicatorSpriteSheets[NUMBER_OF_MON_TYPES + 1] =
{
{sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, // TYPE_NONE
{sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE},
{sFightingIndicatorGfx, sizeof(sFightingIndicatorGfx), TAG_FIGHTING_INDICATOR_TILE},
{sFlyingIndicatorGfx, sizeof(sFlyingIndicatorGfx), TAG_FLYING_INDICATOR_TILE},
@ -661,6 +638,7 @@ static const struct SpriteSheet sTeraIndicatorSpriteSheets[NUMBER_OF_MON_TYPES +
static const struct SpriteTemplate * const sTeraIndicatorSpriteTemplates[NUMBER_OF_MON_TYPES] =
{
[TYPE_NONE] = &sSpriteTemplate_NormalIndicator, // just in case
[TYPE_NORMAL] = &sSpriteTemplate_NormalIndicator,
[TYPE_FIGHTING] = &sSpriteTemplate_FightingIndicator,
[TYPE_FLYING] = &sSpriteTemplate_FlyingIndicator,

View File

@ -903,44 +903,6 @@ static const uq4_12_t sPercentToModifier[] =
UQ_4_12(1.00), // 100
};
#define X UQ_4_12
#define ______ X(1.0) // Regular effectiveness.
// Type matchup updates. Attacker Defender
#define STL_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_6 ? X(1.0) : X(0.5)) // Ghost/Dark -> Steel
#define PSN_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(0.5) : X(2.0)) // Bug -> Poison
#define BUG_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(1.0) : X(2.0)) // Poison -> Bug
#define PSY_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(2.0) : X(0.0)) // Ghost -> Psychic
#define FIR_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(0.5) : X(1.0)) // Ice -> Fire
static const uq4_12_t sTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES] =
{// Defender -->
// Attacker Normal Fighting Flying Poison Ground Rock Bug Ghost Steel Mystery Fire Water Grass Electric Psychic Ice Dragon Dark Fairy Stellar
[TYPE_NORMAL] = {______, ______, ______, ______, ______, X(0.5), ______, X(0.0), X(0.5), ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______},
[TYPE_FIGHTING] = {X(2.0), ______, X(0.5), X(0.5), ______, X(2.0), X(0.5), X(0.0), X(2.0), ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), X(0.5), ______},
[TYPE_FLYING] = {______, X(2.0), ______, ______, ______, X(0.5), X(2.0), ______, X(0.5), ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, ______},
[TYPE_POISON] = {______, ______, ______, X(0.5), X(0.5), X(0.5), BUG_RS, X(0.5), X(0.0), ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, X(2.0), ______},
[TYPE_GROUND] = {______, ______, X(0.0), X(2.0), ______, X(2.0), X(0.5), ______, X(2.0), ______, X(2.0), ______, X(0.5), X(2.0), ______, ______, ______, ______, ______, ______},
[TYPE_ROCK] = {______, X(0.5), X(2.0), ______, X(0.5), ______, X(2.0), ______, X(0.5), ______, X(2.0), ______, ______, ______, ______, X(2.0), ______, ______, ______, ______},
[TYPE_BUG] = {______, X(0.5), X(0.5), PSN_RS, ______, ______, ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, X(2.0), ______, ______, X(2.0), X(0.5), ______},
[TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), STL_RS, ______, ______, ______, ______, ______, PSY_RS, ______, ______, X(0.5), ______, ______},
[TYPE_STEEL] = {______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, ______, X(2.0), ______},
[TYPE_MYSTERY] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______},
[TYPE_FIRE] = {______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(2.0), X(0.5), ______, ______, ______},
[TYPE_WATER] = {______, ______, ______, ______, X(2.0), X(2.0), ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______},
[TYPE_GRASS] = {______, ______, X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), ______, X(0.5), ______, X(0.5), X(2.0), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______},
[TYPE_ELECTRIC] = {______, ______, X(2.0), ______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, X(0.5), ______, ______, ______},
[TYPE_PSYCHIC] = {______, X(2.0), ______, X(2.0), ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, X(0.5), ______, ______, X(0.0), ______, ______},
[TYPE_ICE] = {______, ______, X(2.0), ______, X(2.0), ______, ______, ______, X(0.5), ______, FIR_RS, X(0.5), X(2.0), ______, ______, X(0.5), X(2.0), ______, ______, ______},
[TYPE_DRAGON] = {______, ______, ______, ______, ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, ______, ______, X(2.0), ______, X(0.0), ______},
[TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), STL_RS, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______},
[TYPE_FAIRY] = {______, X(2.0), ______, X(0.5), ______, ______, ______, ______, X(0.5), ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(2.0), ______, ______},
[TYPE_STELLAR] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______},
};
#undef ______
#undef X
// code
u8 GetBattlerForBattleScript(u8 caseId)
{
@ -4408,7 +4370,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (!gSpecialStatuses[battler].switchInAbilityDone)
{
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
gBattlerAttacker = battler;
gBattleScripting.battler = battler;
BattleScriptPushCursorAndCallback(BattleScript_FriskActivates); // Try activate
effect++;
}
@ -4970,10 +4932,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
BattleScriptPushCursorAndCallback(BattleScript_BadDreamsActivates);
effect++;
break;
SOLAR_POWER_HP_DROP:
case ABILITY_SOLAR_POWER:
if (IsBattlerWeatherAffected(battler, B_WEATHER_SUN))
{
SOLAR_POWER_HP_DROP:
BattleScriptPushCursorAndCallback(BattleScript_SolarPowerActivates);
gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 8;
if (gBattleMoveDamage == 0)
@ -5045,31 +5007,70 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITYEFFECT_WOULD_BLOCK:
{
u16 moveTarget = GetBattlerMoveTargetType(battler, move);
u16 battlerAbility = GetBattlerAbility(battler);
u16 targetAbility = GetBattlerAbility(gBattlerTarget);
const u8 * battleScriptBlocksMove = NULL;
if ((gLastUsedAbility == ABILITY_SOUNDPROOF && gMovesInfo[move].soundMove && !(moveTarget & MOVE_TARGET_USER))
|| (gLastUsedAbility == ABILITY_BULLETPROOF && gMovesInfo[move].ballisticMove))
switch (gLastUsedAbility)
{
case ABILITY_SOUNDPROOF:
if (gMovesInfo[move].soundMove && !(moveTarget & MOVE_TARGET_USER))
effect = 1;
break;
case ABILITY_BULLETPROOF:
if (gMovesInfo[move].ballisticMove)
effect = 1;
break;
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (GetChosenMovePriority(gBattlerAttacker) > 0 && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(battler))
effect = 2;
break;
case ABILITY_GOOD_AS_GOLD:
if (IS_MOVE_STATUS(gCurrentMove)
&& !(moveTarget & MOVE_TARGET_USER)
&& !(moveTarget & MOVE_TARGET_OPPONENTS_FIELD)
&& !(moveTarget & MOVE_TARGET_ALL_BATTLERS))
effect = 3;
break;
}
if (!effect)
{
switch (GetBattlerAbility(BATTLE_PARTNER(battler)))
{
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (GetChosenMovePriority(gBattlerAttacker) > 0 && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(battler))
effect = 4;
break;
}
}
if (effect == 1)
{
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
battleScriptBlocksMove = BattleScript_SoundproofProtected;
effect = 1;
}
else if ((gLastUsedAbility == ABILITY_DAZZLING || gLastUsedAbility == ABILITY_QUEENLY_MAJESTY || gLastUsedAbility == ABILITY_ARMOR_TAIL || IsBattlerAlive(battler ^= BIT_FLANK))
&& (battlerAbility == ABILITY_DAZZLING || battlerAbility == ABILITY_QUEENLY_MAJESTY || battlerAbility == ABILITY_ARMOR_TAIL)
&& GetChosenMovePriority(gBattlerAttacker) > 0
&& GetBattlerSide(gBattlerAttacker) != GetBattlerSide(battler))
else if (effect == 2 || effect == 4)
{
if (effect == 4)
gBattleScripting.battler = BATTLE_PARTNER(battler);
else
gBattleScripting.battler = battler;
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
battleScriptBlocksMove = BattleScript_DazzlingProtected;
effect = 1;
}
else if (effect == 3)
{
battleScriptBlocksMove = BattleScript_GoodAsGoldActivates;
}
else if (GetChosenMovePriority(gBattlerAttacker) > 0
&& BlocksPrankster(move, gBattlerAttacker, gBattlerTarget, TRUE)
&& !(IS_MOVE_STATUS(move) && (targetAbility == ABILITY_MAGIC_BOUNCE || gProtectStructs[gBattlerTarget].bounceMove)))
&& BlocksPrankster(move, gBattlerAttacker, gBattlerTarget, TRUE)
&& !(IS_MOVE_STATUS(move) && (gLastUsedAbility == ABILITY_MAGIC_BOUNCE || gProtectStructs[gBattlerTarget].bounceMove)))
{
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE) || !(moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)))
CancelMultiTurnMoves(gBattlerAttacker); // Don't cancel moves that can hit two targets bc one target might not be protected
@ -5077,15 +5078,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
battleScriptBlocksMove = BattleScript_DarkTypePreventsPrankster;
effect = 1;
}
else if (GetBattlerAbility(gBattlerTarget) == ABILITY_GOOD_AS_GOLD
&& IS_MOVE_STATUS(gCurrentMove)
&& !(moveTarget & MOVE_TARGET_USER)
&& !(moveTarget & MOVE_TARGET_OPPONENTS_FIELD)
&& !(moveTarget & MOVE_TARGET_ALL_BATTLERS))
{
battleScriptBlocksMove = BattleScript_GoodAsGoldActivates;
effect = 1;
}
if (caseID == ABILITYEFFECT_WOULD_BLOCK)
{
if (effect && gLastUsedAbility != 0xFFFF)
@ -5562,6 +5554,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& RandomWeighted(RNG_STATIC, 2, 1))
{
gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_PARALYSIS;
PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_AbilityStatusEffect;
gHitMarker |= HITMARKER_STATUS_ABILITY_EFFECT;
@ -5579,6 +5572,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& RandomWeighted(RNG_FLAME_BODY, 2, 1))
{
gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN;
PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_AbilityStatusEffect;
gHitMarker |= HITMARKER_STATUS_ABILITY_EFFECT;
@ -5851,6 +5845,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (IsBattlerAlive(battler)
&& (gMovesInfo[gCurrentMove].danceMove)
&& !gSpecialStatuses[battler].dancerUsedMove
&& (gHitMarker & HITMARKER_ATTACKSTRING_PRINTED)
&& gBattlerAttacker != battler)
{
// Set bit and save Dancer mon's original target
@ -9994,7 +9989,7 @@ static inline s32 DoMoveDamageCalcVars(u32 move, u32 battlerAtk, u32 battlerDef,
if (randomFactor)
{
dmg *= 100 - RandomUniform(RNG_DAMAGE_MODIFIER, 0, 15);
dmg *= DMG_ROLL_PERCENT_HI - RandomUniform(RNG_DAMAGE_MODIFIER, 0, DMG_ROLL_PERCENT_HI - DMG_ROLL_PERCENT_LO);
dmg /= 100;
}
@ -10055,7 +10050,7 @@ static inline s32 DoFutureSightAttackDamageCalcVars(u32 move, u32 battlerAtk, u3
if (randomFactor)
{
dmg *= 100 - RandomUniform(RNG_DAMAGE_MODIFIER, 0, 15);
dmg *= DMG_ROLL_PERCENT_HI - RandomUniform(RNG_DAMAGE_MODIFIER, 0, DMG_ROLL_PERCENT_HI - DMG_ROLL_PERCENT_LO);
dmg /= 100;
}
@ -10366,8 +10361,8 @@ uq4_12_t GetTypeEffectiveness(struct Pokemon *mon, u8 moveType)
uq4_12_t GetTypeModifier(u32 atkType, u32 defType)
{
if (B_FLAG_INVERSE_BATTLE != 0 && FlagGet(B_FLAG_INVERSE_BATTLE))
return GetInverseTypeMultiplier(sTypeEffectivenessTable[atkType][defType]);
return sTypeEffectivenessTable[atkType][defType];
return GetInverseTypeMultiplier(gTypeEffectivenessTable[atkType][defType]);
return gTypeEffectivenessTable[atkType][defType];
}
s32 GetStealthHazardDamageByTypesAndHP(u8 hazardType, u8 type1, u8 type2, u32 maxHp)
@ -10911,10 +10906,8 @@ u8 GetBattleMoveCategory(u32 moveId)
if (IS_MOVE_STATUS(moveId))
return DAMAGE_CATEGORY_STATUS;
else if (gMovesInfo[moveId].type < TYPE_MYSTERY)
return DAMAGE_CATEGORY_PHYSICAL;
else
return DAMAGE_CATEGORY_SPECIAL;
return gTypesInfo[gMovesInfo[moveId].type].damageCategory;
}
static bool32 TryRemoveScreens(u32 battler)

View File

@ -1602,6 +1602,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
.name = _("Disguise"),
.description = COMPOUND_STRING("Decoy protects it once."),
.aiRating = 8,
.breakable = TRUE,
.cantBeCopied = TRUE,
.cantBeSwapped = TRUE,
.cantBeTraced = TRUE,

View File

@ -21934,10 +21934,10 @@ const u32 gObjectEventPic_Substitute[] = INCBIN_COMP("graphics/pokemon/question_
#endif //P_FAMILY_IRON_CROWN
#if P_FAMILY_TERAPAGOS
// const u32 gMonFrontPic_TerapagosNormal[] = INCBIN_U32("graphics/pokemon/terapagos/front.4bpp.lz");
// const u32 gMonPalette_TerapagosNormal[] = INCBIN_U32("graphics/pokemon/terapagos/normal.gbapal.lz");
// const u32 gMonBackPic_TerapagosNormal[] = INCBIN_U32("graphics/pokemon/terapagos/back.4bpp.lz");
// const u32 gMonShinyPalette_TerapagosNormal[] = INCBIN_U32("graphics/pokemon/terapagos/shiny.gbapal.lz");
const u32 gMonFrontPic_TerapagosNormal[] = INCBIN_U32("graphics/pokemon/terapagos/anim_front.4bpp.lz");
const u32 gMonPalette_TerapagosNormal[] = INCBIN_U32("graphics/pokemon/terapagos/normal.gbapal.lz");
const u32 gMonBackPic_TerapagosNormal[] = INCBIN_U32("graphics/pokemon/terapagos/back.4bpp.lz");
const u32 gMonShinyPalette_TerapagosNormal[] = INCBIN_U32("graphics/pokemon/terapagos/shiny.gbapal.lz");
const u8 gMonIcon_TerapagosNormal[] = INCBIN_U8("graphics/pokemon/terapagos/icon.4bpp");
#if P_FOOTPRINTS
const u8 gMonFootprint_TerapagosNormal[] = INCBIN_U8("graphics/pokemon/terapagos/footprint.1bpp");
@ -21950,10 +21950,10 @@ const u32 gObjectEventPic_Substitute[] = INCBIN_COMP("graphics/pokemon/question_
#endif //OW_FOLLOWERS_SHARE_PALETTE
#endif //OW_FOLLOWERS_ENABLED
// const u32 gMonFrontPic_TerapagosTerastal[] = INCBIN_U32("graphics/pokemon/terapagos/terastal/front.4bpp.lz");
// const u32 gMonPalette_TerapagosTerastal[] = INCBIN_U32("graphics/pokemon/terapagos/terastal/normal.gbapal.lz");
// const u32 gMonBackPic_TerapagosTerastal[] = INCBIN_U32("graphics/pokemon/terapagos/terastal/back.4bpp.lz");
// const u32 gMonShinyPalette_TerapagosTerastal[] = INCBIN_U32("graphics/pokemon/terapagos/terastal/shiny.gbapal.lz");
const u32 gMonFrontPic_TerapagosTerastal[] = INCBIN_U32("graphics/pokemon/terapagos/terastal/anim_front.4bpp.lz");
const u32 gMonPalette_TerapagosTerastal[] = INCBIN_U32("graphics/pokemon/terapagos/terastal/normal.gbapal.lz");
const u32 gMonBackPic_TerapagosTerastal[] = INCBIN_U32("graphics/pokemon/terapagos/terastal/back.4bpp.lz");
const u32 gMonShinyPalette_TerapagosTerastal[] = INCBIN_U32("graphics/pokemon/terapagos/terastal/shiny.gbapal.lz");
const u8 gMonIcon_TerapagosTerastal[] = INCBIN_U8("graphics/pokemon/terapagos/terastal/icon.4bpp");
#if P_FOOTPRINTS
const u8 gMonFootprint_TerapagosTerastal[] = INCBIN_U8("graphics/pokemon/terapagos/terastal/footprint.1bpp");

View File

@ -0,0 +1 @@

File diff suppressed because it is too large Load Diff

View File

@ -6619,17 +6619,18 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
.pokemonOffset = 0,
.trainerScale = 365,
.trainerOffset = 7,
//.frontPic = gMonFrontPic_TerapagosNormal,
//.frontPicSize = MON_COORDS_SIZE(64, 64),
.frontPic = gMonFrontPic_TerapagosNormal,
.frontPicSize = MON_COORDS_SIZE(64, 64),
.frontPicYOffset = 0,
.frontAnimFrames = sAnims_TerapagosNormal,
//.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE,
//.backPic = gMonBackPic_TerapagosNormal,
//.backPicSize = MON_COORDS_SIZE(64, 64),
.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE,
.enemyMonElevation = 1,
.backPic = gMonBackPic_TerapagosNormal,
.backPicSize = MON_COORDS_SIZE(64, 64),
.backPicYOffset = 0,
//.backAnimId = BACK_ANIM_NONE,
//.palette = gMonPalette_TerapagosNormal,
//.shinyPalette = gMonShinyPalette_TerapagosNormal,
.backAnimId = BACK_ANIM_NONE,
.palette = gMonPalette_TerapagosNormal,
.shinyPalette = gMonShinyPalette_TerapagosNormal,
.iconSprite = gMonIcon_TerapagosNormal,
.iconPalIndex = 0,
FOOTPRINT(TerapagosNormal)
@ -6676,17 +6677,18 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
.pokemonOffset = 0,
.trainerScale = 365,
.trainerOffset = 7,
//.frontPic = gMonFrontPic_TerapagosTerastal,
//.frontPicSize = MON_COORDS_SIZE(64, 64),
.frontPicYOffset = 0,
.frontPic = gMonFrontPic_TerapagosTerastal,
.frontPicSize = MON_COORDS_SIZE(64, 64),
.frontPicYOffset = 8,
.frontAnimFrames = sAnims_TerapagosTerastal,
//.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE,
//.backPic = gMonBackPic_TerapagosTerastal,
//.backPicSize = MON_COORDS_SIZE(64, 64),
.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE,
.enemyMonElevation = 1,
.backPic = gMonBackPic_TerapagosTerastal,
.backPicSize = MON_COORDS_SIZE(64, 64),
.backPicYOffset = 0,
//.backAnimId = BACK_ANIM_NONE,
//.palette = gMonPalette_TerapagosTerastal,
//.shinyPalette = gMonShinyPalette_TerapagosTerastal,
.backAnimId = BACK_ANIM_NONE,
.palette = gMonPalette_TerapagosTerastal,
.shinyPalette = gMonShinyPalette_TerapagosTerastal,
.iconSprite = gMonIcon_TerapagosTerastal,
.iconPalIndex = 0,
FOOTPRINT(TerapagosTerastal)

View File

@ -10120,8 +10120,20 @@ PLACEHOLDER_ANIM_SINGLE_FRAME(IronCrown);
#endif //P_FAMILY_IRON_CROWN
#if P_FAMILY_TERAPAGOS
PLACEHOLDER_ANIM_SINGLE_FRAME(TerapagosNormal);
PLACEHOLDER_ANIM_SINGLE_FRAME(TerapagosTerastal);
static const union AnimCmd sAnim_TerapagosNormal_1[] =
{
ANIMCMD_FRAME(0, 30),
ANIMCMD_FRAME(1, 30),
ANIMCMD_FRAME(0, 1),
ANIMCMD_END,
};
static const union AnimCmd sAnim_TerapagosTerastal_1[] =
{
ANIMCMD_FRAME(0, 30),
ANIMCMD_FRAME(1, 30),
ANIMCMD_FRAME(0, 1),
ANIMCMD_END,
};
PLACEHOLDER_ANIM_SINGLE_FRAME(TerapagosStellar);
#endif //P_FAMILY_TERAPAGOS

423
src/data/types_info.h Normal file
View File

@ -0,0 +1,423 @@
#include "constants/battle.h"
#include "constants/pokemon.h"
#define X UQ_4_12
#define ______ X(1.0) // Regular effectiveness.
// Type matchup updates. Attacker Defender
#define STL_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_6 ? X(1.0) : X(0.5)) // Ghost/Dark -> Steel
#define PSN_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(0.5) : X(2.0)) // Bug -> Poison
#define BUG_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(1.0) : X(2.0)) // Poison -> Bug
#define PSY_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(2.0) : X(0.0)) // Ghost -> Psychic
#define FIR_RS (B_UPDATED_TYPE_MATCHUPS >= GEN_2 ? X(0.5) : X(1.0)) // Ice -> Fire
const uq4_12_t gTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES] =
{// Defender -->
// Attacker None Normal Fighting Flying Poison Ground Rock Bug Ghost Steel Mystery Fire Water Grass Electric Psychic Ice Dragon Dark Fairy Stellar
[TYPE_NONE] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______},
[TYPE_NORMAL] = {______, ______, ______, ______, ______, ______, X(0.5), ______, X(0.0), X(0.5), ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______},
[TYPE_FIGHTING] = {______, X(2.0), ______, X(0.5), X(0.5), ______, X(2.0), X(0.5), X(0.0), X(2.0), ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), X(0.5), ______},
[TYPE_FLYING] = {______, ______, X(2.0), ______, ______, ______, X(0.5), X(2.0), ______, X(0.5), ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, ______},
[TYPE_POISON] = {______, ______, ______, ______, X(0.5), X(0.5), X(0.5), BUG_RS, X(0.5), X(0.0), ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, X(2.0), ______},
[TYPE_GROUND] = {______, ______, ______, X(0.0), X(2.0), ______, X(2.0), X(0.5), ______, X(2.0), ______, X(2.0), ______, X(0.5), X(2.0), ______, ______, ______, ______, ______, ______},
[TYPE_ROCK] = {______, ______, X(0.5), X(2.0), ______, X(0.5), ______, X(2.0), ______, X(0.5), ______, X(2.0), ______, ______, ______, ______, X(2.0), ______, ______, ______, ______},
[TYPE_BUG] = {______, ______, X(0.5), X(0.5), PSN_RS, ______, ______, ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, X(2.0), ______, ______, X(2.0), X(0.5), ______},
[TYPE_GHOST] = {______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), STL_RS, ______, ______, ______, ______, ______, PSY_RS, ______, ______, X(0.5), ______, ______},
[TYPE_STEEL] = {______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, ______, X(2.0), ______},
[TYPE_MYSTERY] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______},
[TYPE_FIRE] = {______, ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(2.0), X(0.5), ______, ______, ______},
[TYPE_WATER] = {______, ______, ______, ______, ______, X(2.0), X(2.0), ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______},
[TYPE_GRASS] = {______, ______, ______, X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), ______, X(0.5), ______, X(0.5), X(2.0), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______},
[TYPE_ELECTRIC] = {______, ______, ______, X(2.0), ______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, X(0.5), ______, ______, ______},
[TYPE_PSYCHIC] = {______, ______, X(2.0), ______, X(2.0), ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, X(0.5), ______, ______, X(0.0), ______, ______},
[TYPE_ICE] = {______, ______, ______, X(2.0), ______, X(2.0), ______, ______, ______, X(0.5), ______, FIR_RS, X(0.5), X(2.0), ______, ______, X(0.5), X(2.0), ______, ______, ______},
[TYPE_DRAGON] = {______, ______, ______, ______, ______, ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, ______, ______, X(2.0), ______, X(0.0), ______},
[TYPE_DARK] = {______, ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), STL_RS, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______},
[TYPE_FAIRY] = {______, ______, X(2.0), ______, X(0.5), ______, ______, ______, ______, X(0.5), ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(2.0), ______, ______},
[TYPE_STELLAR] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______},
};
#undef ______
#undef X
#if B_EXPANDED_TYPE_NAMES == TRUE
#define HANDLE_EXPANDED_TYPE_NAME(_name, ...) _(DEFAULT(_name, __VA_ARGS__))
#else
#define HANDLE_EXPANDED_TYPE_NAME(_name, ...) _(_name)
#endif
// .generic is large enough that the text for TYPE_ELECTRIC will exceed TEXT_BUFF_ARRAY_COUNT.
// In this array there's commented-out data such as references to type-resist berries that would otherwise would go unused.
// However, we figured this information would be useful for users that want to add their own types as a reminder of
// what data would they need to add in order to have their new types be fully fledged like official types.
const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
{
[TYPE_NONE] =
{
.name = _("None"),
.generic = _("a move"),
.palette = 15, // Uses TYPE_MYSTERY's icon
.teraTypeRGBValue = RGB_WHITE,
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_NormalTMHM,
},
[TYPE_NORMAL] =
{
.name = _("Normal"),
.generic = _("a NORMAL move"),
.palette = 13,
.zMove = MOVE_BREAKNECK_BLITZ,
.maxMove = MOVE_MAX_STRIKE,
.teraTypeRGBValue = RGB_WHITE, // custom
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_NormalTMHM,
//.enhanceItem = ITEM_SILK_SCARF,
//.berry = ITEM_CHILAN_BERRY,
//.gem = ITEM_NORMAL_GEM,
//.zCrystal = ITEM_NORMALIUM_Z,
//.teraShard = ITEM_NORMAL_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_NORMAL,
},
[TYPE_FIGHTING] =
{
.name = HANDLE_EXPANDED_TYPE_NAME("Fight", "Fighting"),
.generic = _("a FIGHTING move"),
.palette = 13,
.zMove = MOVE_ALL_OUT_PUMMELING,
.maxMove = MOVE_MAX_KNUCKLE,
.teraTypeRGBValue = RGB(26, 8, 14),
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_FightingTMHM,
//.enhanceItem = ITEM_BLACK_BELT,
//.berry = ITEM_CHOPLE_BERRY,
//.gem = ITEM_FIGHTING_GEM,
//.zCrystal = ITEM_FIGHTINIUM_Z,
//.plate = ITEM_FIST_PLATE,
//.memory = ITEM_FIGHTING_MEMORY,
//.teraShard = ITEM_FIGHTING_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_FIGHTING,
},
[TYPE_FLYING] =
{
.name = _("Flying"),
.generic = _("a FLYING move"),
.palette = 14,
.zMove = MOVE_SUPERSONIC_SKYSTRIKE,
.maxMove = MOVE_MAX_AIRSTREAM,
.teraTypeRGBValue = RGB(31, 26, 7),
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_FlyingTMHM,
//.enhanceItem = ITEM_SHARP_BEAK,
//.berry = ITEM_COBA_BERRY,
//.gem = ITEM_FLYING_GEM,
//.zCrystal = ITEM_FLYINIUM_Z,
//.plate = ITEM_SKY_PLATE,
//.memory = ITEM_FLYING_MEMORY,
//.teraShard = ITEM_FLYING_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_FLYING,
},
[TYPE_POISON] =
{
.name = _("Poison"),
.generic = _("a POISON move"),
.palette = 14,
.zMove = MOVE_ACID_DOWNPOUR,
.maxMove = MOVE_MAX_OOZE,
.teraTypeRGBValue = RGB(26, 10, 25), // custom
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_PoisonTMHM,
//.enhanceItem = ITEM_POISON_BARB,
//.berry = ITEM_KEBIA_BERRY,
//.gem = ITEM_POISON_GEM,
//.zCrystal = ITEM_POISONIUM_Z,
//.plate = ITEM_TOXIC_PLATE,
//.memory = ITEM_POISON_MEMORY,
//.teraShard = ITEM_POISON_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_POISON,
},
[TYPE_GROUND] =
{
.name = _("Ground"),
.generic = _("a GROUND move"),
.palette = 13,
.zMove = MOVE_TECTONIC_RAGE,
.maxMove = MOVE_MAX_QUAKE,
.teraTypeRGBValue = RGB(25, 23, 18),
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_GroundTMHM,
//.enhanceItem = ITEM_SOFT_SAND,
//.berry = ITEM_SHUCA_BERRY,
//.gem = ITEM_GROUND_GEM,
//.zCrystal = ITEM_GROUNDIUM_Z,
//.plate = ITEM_EARTH_PLATE,
//.memory = ITEM_GROUND_MEMORY,
//.teraShard = ITEM_GROUND_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_GROUND,
},
[TYPE_ROCK] =
{
.name = _("Rock"),
.generic = _("a ROCK move"),
.palette = 13,
.zMove = MOVE_CONTINENTAL_CRUSH,
.maxMove = MOVE_MAX_ROCKFALL,
.teraTypeRGBValue = RGB(18, 16, 8), // custom
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_RockTMHM,
//.enhanceItem = ITEM_HARD_STONE,
//.berry = ITEM_CHARTI_BERRY,
//.gem = ITEM_ROCK_GEM,
//.zCrystal = ITEM_ROCKIUM_Z,
//.plate = ITEM_STONE_PLATE,
//.memory = ITEM_ROCK_MEMORY,
//.teraShard = ITEM_ROCK_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_ROCK,
},
[TYPE_BUG] =
{
.name = _("Bug"),
.generic = _("a BUG move"),
.palette = 15,
.zMove = MOVE_SAVAGE_SPIN_OUT,
.maxMove = MOVE_MAX_FLUTTERBY,
.teraTypeRGBValue = RGB(18, 24, 6),
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_BugTMHM,
//.enhanceItem = ITEM_SILVER_POWDER,
//.berry = ITEM_TANGA_BERRY,
//.gem = ITEM_BUG_GEM,
//.zCrystal = ITEM_BUGINIUM_Z,
//.plate = ITEM_INSECT_PLATE,
//.memory = ITEM_BUG_MEMORY,
//.teraShard = ITEM_BUG_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_BUG,
},
[TYPE_GHOST] =
{
.name = _("Ghost"),
.generic = _("a GHOST move"),
.palette = 14,
.zMove = MOVE_NEVER_ENDING_NIGHTMARE,
.maxMove = MOVE_MAX_PHANTASM,
.teraTypeRGBValue = RGB(12, 10, 16),
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_GhostTMHM,
//.enhanceItem = ITEM_SPELL_TAG,
//.berry = ITEM_KASIB_BERRY,
//.gem = ITEM_GHOST_GEM,
//.zCrystal = ITEM_GHOSTIUM_Z,
//.plate = ITEM_SPOOKY_PLATE,
//.memory = ITEM_GHOST_MEMORY,
//.teraShard = ITEM_GHOST_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_GHOST,
},
[TYPE_STEEL] =
{
.name = _("Steel"),
.generic = _("a STEEL move"),
.palette = 13,
.zMove = MOVE_CORKSCREW_CRASH,
.maxMove = MOVE_MAX_STEELSPIKE,
.teraTypeRGBValue = RGB(19, 19, 20),
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_SteelTMHM,
//.enhanceItem = ITEM_METAL_COAT,
//.berry = ITEM_BABIRI_BERRY,
//.gem = ITEM_STEEL_GEM,
//.zCrystal = ITEM_STEELIUM_Z,
//.plate = ITEM_IRON_PLATE,
//.memory = ITEM_STEEL_MEMORY,
//.teraShard = ITEM_STEEL_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_STEEL,
},
[TYPE_MYSTERY] =
{
.name = _("???"),
.generic = _("a ??? move"),
.palette = 15,
.teraTypeRGBValue = RGB_WHITE,
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
},
[TYPE_FIRE] =
{
.name = _("Fire"),
.generic = _("a FIRE move"),
.palette = 13,
.zMove = MOVE_INFERNO_OVERDRIVE,
.maxMove = MOVE_MAX_FLARE,
.teraTypeRGBValue = RGB(31, 20, 11),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_FireTMHM,
//.enhanceItem = ITEM_CHARCOAL,
//.berry = ITEM_OCCA_BERRY,
//.gem = ITEM_FIRE_GEM,
//.zCrystal = ITEM_FIRIUM_Z,
//.plate = ITEM_FLAME_PLATE,
//.memory = ITEM_FIRE_MEMORY,
//.teraShard = ITEM_FIRE_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_FIRE,
},
[TYPE_WATER] =
{
.name = _("Water"),
.generic = _("a WATER move"),
.palette = 14,
.zMove = MOVE_HYDRO_VORTEX,
.maxMove = MOVE_MAX_GEYSER,
.teraTypeRGBValue = RGB(10, 18, 27),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_WaterTMHM,
//.enhanceItem = ITEM_MYSTIC_WATER,
//.berry = ITEM_PASSHO_BERRY,
//.gem = ITEM_WATER_GEM,
//.zCrystal = ITEM_WATERIUM_Z,
//.plate = ITEM_SPLASH_PLATE,
//.memory = ITEM_WATER_MEMORY,
//.teraShard = ITEM_WATER_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_WATER,
},
[TYPE_GRASS] =
{
.name = _("Grass"),
.generic = _("a GRASS move"),
.palette = 15,
.zMove = MOVE_BLOOM_DOOM,
.maxMove = MOVE_MAX_OVERGROWTH,
.teraTypeRGBValue = RGB(12, 24, 11),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_GrassTMHM,
//.enhanceItem = ITEM_MIRACLE_SEED,
//.berry = ITEM_RINDO_BERRY,
//.gem = ITEM_GRASS_GEM,
//.zCrystal = ITEM_GRASSIUM_Z,
//.plate = ITEM_MEADOW_PLATE,
//.memory = ITEM_GRASS_MEMORY,
//.teraShard = ITEM_GRASS_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_GRASS,
},
[TYPE_ELECTRIC] =
{
.name = HANDLE_EXPANDED_TYPE_NAME("Electr", "Electric"),
.generic = _("an ELECTRIC move"),
.palette = 13,
.zMove = MOVE_GIGAVOLT_HAVOC,
.maxMove = MOVE_MAX_LIGHTNING,
.teraTypeRGBValue = RGB(30, 26, 7),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_ElectricTMHM,
//.enhanceItem = ITEM_MAGNET,
//.berry = ITEM_WACAN_BERRY,
//.gem = ITEM_ELECTRIC_GEM,
//.zCrystal = ITEM_ELECTRIUM_Z,
//.plate = ITEM_ZAP_PLATE,
//.memory = ITEM_ELECTRIC_MEMORY,
//.teraShard = ITEM_ELECTRIC_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_ELECTRIC,
},
[TYPE_PSYCHIC] =
{
.name = HANDLE_EXPANDED_TYPE_NAME("Psychc", "Psychic"),
.generic = _("a PSYCHIC move"),
.palette = 14,
.zMove = MOVE_SHATTERED_PSYCHE,
.maxMove = MOVE_MAX_MINDSTORM,
.teraTypeRGBValue = RGB(31, 14, 15),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_PsychicTMHM,
//.enhanceItem = ITEM_TWISTED_SPOON,
//.berry = ITEM_PAYAPA_BERRY,
//.gem = ITEM_PSYCHIC_GEM,
//.zCrystal = ITEM_PSYCHIUM_Z,
//.plate = ITEM_MIND_PLATE,
//.memory = ITEM_PSYCHIC_MEMORY,
//.teraShard = ITEM_PSYCHIC_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_PSYCHIC,
},
[TYPE_ICE] =
{
.name = _("Ice"),
.generic = _("an ICE move"),
.palette = 14,
.zMove = MOVE_SUBZERO_SLAMMER,
.maxMove = MOVE_MAX_HAILSTORM,
.teraTypeRGBValue = RGB(14, 26, 25),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_IceTMHM,
//.enhanceItem = ITEM_NEVER_MELT_ICE,
//.berry = ITEM_YACHE_BERRY,
//.gem = ITEM_ICE_GEM,
//.zCrystal = ITEM_ICIUM_Z,
//.plate = ITEM_ICICLE_PLATE,
//.memory = ITEM_ICE_MEMORY,
//.teraShard = ITEM_ICE_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_ICE,
},
[TYPE_DRAGON] =
{
.name = _("Dragon"),
.generic = _("a DRAGON move"),
.palette = 15,
.zMove = MOVE_DEVASTATING_DRAKE,
.maxMove = MOVE_MAX_WYRMWIND,
.teraTypeRGBValue = RGB(10, 18, 27),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_DragonTMHM,
//.enhanceItem = ITEM_DRAGON_FANG,
//.berry = ITEM_HABAN_BERRY,
//.gem = ITEM_DRAGON_GEM,
//.zCrystal = ITEM_DRAGONIUM_Z,
//.plate = ITEM_DRACO_PLATE,
//.memory = ITEM_DRAGON_MEMORY,
//.teraShard = ITEM_DRAGON_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_DRAGON,
},
[TYPE_DARK] =
{
.name = _("Dark"),
.generic = _("a DARK move"),
.palette = 13,
.zMove = MOVE_BLACK_HOLE_ECLIPSE,
.maxMove = MOVE_MAX_DARKNESS,
.teraTypeRGBValue = RGB(6, 5, 8),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_DarkTMHM,
//.enhanceItem = ITEM_BLACK_GLASSES,
//.berry = ITEM_COLBUR_BERRY,
//.gem = ITEM_DARK_GEM,
//.zCrystal = ITEM_DARKINIUM_Z,
//.plate = ITEM_DREAD_PLATE,
//.memory = ITEM_DARK_MEMORY,
//.teraShard = ITEM_DARK_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_DARK,
},
[TYPE_FAIRY] =
{
.name = _("Fairy"),
.generic = _("a FAIRY move"),
.palette = 14,
.zMove = MOVE_TWINKLE_TACKLE,
.maxMove = MOVE_MAX_STARFALL,
.teraTypeRGBValue = RGB(31, 15, 21),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_FairyTMHM,
//.enhanceItem = ITEM_FAIRY_FEATHER,
//.berry = ITEM_ROSELI_BERRY,
//.gem = ITEM_FAIRY_GEM,
//.zCrystal = ITEM_FAIRIUM_Z,
//.plate = ITEM_PIXIE_PLATE,
//.memory = ITEM_FAIRY_MEMORY,
//.teraShard = ITEM_FAIRY_TERA_SHARD,
//.arceusForm = SPECIES_ARCEUS_FAIRY,
},
[TYPE_STELLAR] =
{
.name = HANDLE_EXPANDED_TYPE_NAME("Stellr", "Stellar"),
.generic = _("a STELLAR move"),
.palette = 15,
.zMove = MOVE_BREAKNECK_BLITZ,
.maxMove = MOVE_MAX_STRIKE,
.teraTypeRGBValue = RGB(10, 18, 27),
.paletteTMHM = gItemIconPalette_NormalTMHM, // failsafe
// .teraShard = ITEM_STELLAR_TERA_SHARD,
},
};

View File

@ -1629,6 +1629,9 @@ const u32 gPartyMenuPokeball_Pal[] = INCBIN_U32("graphics/party_menu/pokeball.gb
const u32 gStatusGfx_Icons[] = INCBIN_U32("graphics/interface/status_icons.4bpp.lz");
const u32 gStatusPal_Icons[] = INCBIN_U32("graphics/interface/status_icons.gbapal.lz");
const u16 gCategoryIcons_Pal[] = INCBIN_U16("graphics/interface/category_icons.gbapal");
const u32 gCategoryIcons_Gfx[] = INCBIN_U32("graphics/interface/category_icons.4bpp.lz");
const u32 gMoveTypes_Gfx[] = INCBIN_U32("graphics/types/move_types.4bpp.lz");
const u32 gMoveTypes_Pal[] = INCBIN_U32("graphics/types/move_types.gbapal.lz");

View File

@ -112,6 +112,7 @@ static const u8 sTextColors[] = { TEXT_DYNAMIC_COLOR_6, TEXT_COLOR_WHITE, TEXT_C
static const struct MenuInfoIcon sMenuInfoIcons[] =
{ // { width, height, offset }
{ 12, 12, 0x00 }, // Unused
[TYPE_NONE + 1] = { 32, 12, 0xA4 }, // Copy of TYPE_MYSTERY's
[TYPE_NORMAL + 1] = { 32, 12, 0x20 },
[TYPE_FIGHTING + 1] = { 32, 12, 0x64 },
[TYPE_FLYING + 1] = { 32, 12, 0x60 },

View File

@ -1389,7 +1389,7 @@ static const struct SearchOptionText sDexSearchColorOptions[] =
static const struct SearchOptionText sDexSearchTypeOptions[NUMBER_OF_MON_TYPES] = // + 2 for "None" and terminator, - 2 for Mystery and Stellar
{
{gText_DexEmptyString, gText_DexSearchTypeNone},
{gText_DexEmptyString, gTypesInfo[TYPE_NONE].name},
{gText_DexEmptyString, gTypesInfo[TYPE_NORMAL].name},
{gText_DexEmptyString, gTypesInfo[TYPE_FIGHTING].name},
{gText_DexEmptyString, gTypesInfo[TYPE_FLYING].name},

View File

@ -622,60 +622,6 @@ static void DestroyCategoryIcon(void);
static u16 NationalPokedexNumToSpeciesHGSS(u16 nationalNum);
#define TAG_CATEGORY_ICONS 30004
static const u16 sCategoryIcons_Pal[] = INCBIN_U16("graphics/interface/category_icons.gbapal");
static const u32 sCategoryIcons_Gfx[] = INCBIN_U32("graphics/interface/category_icons.4bpp.lz");
static const struct OamData sOamData_CategoryIcons =
{
.size = SPRITE_SIZE(16x16),
.shape = SPRITE_SHAPE(16x16),
.priority = 0,
};
static const struct CompressedSpriteSheet sSpriteSheet_CategoryIcons =
{
.data = sCategoryIcons_Gfx,
.size = 16*16*3/2,
.tag = TAG_CATEGORY_ICONS,
};
static const struct SpritePalette sSpritePal_CategoryIcons =
{
.data = sCategoryIcons_Pal,
.tag = TAG_CATEGORY_ICONS
};
static const union AnimCmd sSpriteAnim_CategoryIcon0[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_CategoryIcon1[] =
{
ANIMCMD_FRAME(4, 0),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_CategoryIcon2[] =
{
ANIMCMD_FRAME(8, 0),
ANIMCMD_END
};
static const union AnimCmd *const sSpriteAnimTable_CategoryIcons[] =
{
sSpriteAnim_CategoryIcon0,
sSpriteAnim_CategoryIcon1,
sSpriteAnim_CategoryIcon2,
};
static const struct SpriteTemplate sSpriteTemplate_CategoryIcons =
{
.tileTag = TAG_CATEGORY_ICONS,
.paletteTag = TAG_CATEGORY_ICONS,
.oam = &sOamData_CategoryIcons,
.anims = sSpriteAnimTable_CategoryIcons,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy
};
//Stat bars by DizzyEgg
#define TAG_STAT_BAR 4097
#define TAG_STAT_BAR_BG 4098
@ -1955,7 +1901,7 @@ static const struct SearchOptionText sDexSearchColorOptions[] =
static const struct SearchOptionText sDexSearchTypeOptions[NUMBER_OF_MON_TYPES] = // + 2 for "None" and terminator, - 2 for Mystery and Stellar
{
{gText_DexEmptyString, gText_DexSearchTypeNone},
{gText_DexEmptyString, gTypesInfo[TYPE_NONE].name},
{gText_DexEmptyString, gTypesInfo[TYPE_NORMAL].name},
{gText_DexEmptyString, gTypesInfo[TYPE_FIGHTING].name},
{gText_DexEmptyString, gTypesInfo[TYPE_FLYING].name},
@ -4769,7 +4715,7 @@ static void LoadTilesetTilemapHGSS(u8 page)
static u8 ShowCategoryIcon(u32 category)
{
if (sPokedexView->categoryIconSpriteId == 0xFF)
sPokedexView->categoryIconSpriteId = CreateSprite(&sSpriteTemplate_CategoryIcons, 139, 90, 0);
sPokedexView->categoryIconSpriteId = CreateSprite(&gSpriteTemplate_CategoryIcons, 139, 90, 0);
gSprites[sPokedexView->categoryIconSpriteId].invisible = FALSE;
StartSpriteAnim(&gSprites[sPokedexView->categoryIconSpriteId], category);
@ -4912,8 +4858,8 @@ static void Task_LoadStatsScreen(u8 taskId)
CreateTypeIconSprites();
sPokedexView->categoryIconSpriteId = 0xFF;
LoadCompressedPalette(gMoveTypes_Pal, 0x1D0, 0x60);
LoadCompressedSpriteSheet(&sSpriteSheet_CategoryIcons);
LoadSpritePalette(&sSpritePal_CategoryIcons);
LoadCompressedSpriteSheet(&gSpriteSheet_CategoryIcons);
LoadSpritePalette(&gSpritePal_CategoryIcons);
gMain.state++;
break;
case 4:

View File

@ -2774,14 +2774,14 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
break;
case MON_DATA_TERA_TYPE:
{
if (substruct0->teraType == 0)
if (substruct0->teraType == TYPE_NONE)
{
const u8 *types = gSpeciesInfo[substruct0->species].types;
retVal = (boxMon->personality & 0x1) == 0 ? types[0] : types[1];
}
else
{
retVal = substruct0->teraType - 1;
retVal = substruct0->teraType;
}
break;
}
@ -3210,7 +3210,7 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg)
{
u32 teraType;
SET8(teraType);
substruct0->teraType = 1 + teraType;
substruct0->teraType = teraType;
break;
}
case MON_DATA_EVOLUTION_TRACKER:
@ -6840,3 +6840,13 @@ const u8 *GetMoveName(u16 moveId)
{
return gMovesInfo[moveId].name;
}
const u8 *GetMoveAnimationScript(u16 moveId)
{
if (gMovesInfo[moveId].battleAnimScript == NULL)
{
DebugPrintfLevel(MGBA_LOG_WARN, "No animation for moveId=%u", moveId);
return Move_TACKLE;
}
return gMovesInfo[moveId].battleAnimScript;
}

View File

@ -740,9 +740,6 @@ static const u8 sMovesPPLayout[] = _("{PP}{DYNAMIC 0}/{DYNAMIC 1}");
#define TAG_MON_MARKINGS 30003
#define TAG_CATEGORY_ICONS 30004
static const u16 sCategoryIcons_Pal[] = INCBIN_U16("graphics/interface/category_icons.gbapal");
static const u32 sCategoryIcons_Gfx[] = INCBIN_U32("graphics/interface/category_icons.4bpp.lz");
static const struct OamData sOamData_CategoryIcons =
{
.size = SPRITE_SIZE(16x16),
@ -750,16 +747,16 @@ static const struct OamData sOamData_CategoryIcons =
.priority = 0,
};
static const struct CompressedSpriteSheet sSpriteSheet_CategoryIcons =
const struct CompressedSpriteSheet gSpriteSheet_CategoryIcons =
{
.data = sCategoryIcons_Gfx,
.data = gCategoryIcons_Gfx,
.size = 16*16*3/2,
.tag = TAG_CATEGORY_ICONS,
};
static const struct SpritePalette sSpritePal_CategoryIcons =
const struct SpritePalette gSpritePal_CategoryIcons =
{
.data = sCategoryIcons_Pal,
.data = gCategoryIcons_Pal,
.tag = TAG_CATEGORY_ICONS
};
@ -788,7 +785,7 @@ static const union AnimCmd *const sSpriteAnimTable_CategoryIcons[] =
sSpriteAnim_CategoryIcon2,
};
static const struct SpriteTemplate sSpriteTemplate_CategoryIcons =
const struct SpriteTemplate gSpriteTemplate_CategoryIcons =
{
.tileTag = TAG_CATEGORY_ICONS,
.paletteTag = TAG_CATEGORY_ICONS,
@ -815,6 +812,10 @@ static const struct OamData sOamData_MoveTypes =
.paletteNum = 0,
.affineParam = 0,
};
static const union AnimCmd sSpriteAnim_TypeNone[] = {
ANIMCMD_FRAME(TYPE_NONE * 8, 0, FALSE, FALSE),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_TypeNormal[] = {
ANIMCMD_FRAME(TYPE_NORMAL * 8, 0, FALSE, FALSE),
ANIMCMD_END
@ -916,31 +917,32 @@ static const union AnimCmd sSpriteAnim_CategoryTough[] = {
ANIMCMD_END
};
static const union AnimCmd *const sSpriteAnimTable_MoveTypes[NUMBER_OF_MON_TYPES + CONTEST_CATEGORIES_COUNT] = {
sSpriteAnim_TypeNormal,
sSpriteAnim_TypeFighting,
sSpriteAnim_TypeFlying,
sSpriteAnim_TypePoison,
sSpriteAnim_TypeGround,
sSpriteAnim_TypeRock,
sSpriteAnim_TypeBug,
sSpriteAnim_TypeGhost,
sSpriteAnim_TypeSteel,
sSpriteAnim_TypeMystery,
sSpriteAnim_TypeFire,
sSpriteAnim_TypeWater,
sSpriteAnim_TypeGrass,
sSpriteAnim_TypeElectric,
sSpriteAnim_TypePsychic,
sSpriteAnim_TypeIce,
sSpriteAnim_TypeDragon,
sSpriteAnim_TypeDark,
sSpriteAnim_TypeFairy,
sSpriteAnim_TypeStellar,
sSpriteAnim_CategoryCool,
sSpriteAnim_CategoryBeauty,
sSpriteAnim_CategoryCute,
sSpriteAnim_CategorySmart,
sSpriteAnim_CategoryTough,
[TYPE_NONE] = sSpriteAnim_TypeNone,
[TYPE_NORMAL] = sSpriteAnim_TypeNormal,
[TYPE_FIGHTING] = sSpriteAnim_TypeFighting,
[TYPE_FLYING] = sSpriteAnim_TypeFlying,
[TYPE_POISON] = sSpriteAnim_TypePoison,
[TYPE_GROUND] = sSpriteAnim_TypeGround,
[TYPE_ROCK] = sSpriteAnim_TypeRock,
[TYPE_BUG] = sSpriteAnim_TypeBug,
[TYPE_GHOST] = sSpriteAnim_TypeGhost,
[TYPE_STEEL] = sSpriteAnim_TypeSteel,
[TYPE_MYSTERY] = sSpriteAnim_TypeMystery,
[TYPE_FIRE] = sSpriteAnim_TypeFire,
[TYPE_WATER] = sSpriteAnim_TypeWater,
[TYPE_GRASS] = sSpriteAnim_TypeGrass,
[TYPE_ELECTRIC] = sSpriteAnim_TypeElectric,
[TYPE_PSYCHIC] = sSpriteAnim_TypePsychic,
[TYPE_ICE] = sSpriteAnim_TypeIce,
[TYPE_DRAGON] = sSpriteAnim_TypeDragon,
[TYPE_DARK] = sSpriteAnim_TypeDark,
[TYPE_FAIRY] = sSpriteAnim_TypeFairy,
[TYPE_STELLAR] = sSpriteAnim_TypeStellar,
[NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_COOL] = sSpriteAnim_CategoryCool,
[NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_BEAUTY] = sSpriteAnim_CategoryBeauty,
[NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_CUTE] = sSpriteAnim_CategoryCute,
[NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_SMART] = sSpriteAnim_CategorySmart,
[NUMBER_OF_MON_TYPES + CONTEST_CATEGORY_TOUGH] = sSpriteAnim_CategoryTough,
};
const struct CompressedSpriteSheet gSpriteSheet_MoveTypes =
@ -1142,7 +1144,7 @@ static const u16 sMarkings_Pal[] = INCBIN_U16("graphics/summary_screen/markings.
static u8 ShowCategoryIcon(u32 category)
{
if (sMonSummaryScreen->categoryIconSpriteId == 0xFF)
sMonSummaryScreen->categoryIconSpriteId = CreateSprite(&sSpriteTemplate_CategoryIcons, 48, 129, 0);
sMonSummaryScreen->categoryIconSpriteId = CreateSprite(&gSpriteTemplate_CategoryIcons, 48, 129, 0);
gSprites[sMonSummaryScreen->categoryIconSpriteId].invisible = FALSE;
StartSpriteAnim(&gSprites[sMonSummaryScreen->categoryIconSpriteId], category);
@ -1437,8 +1439,8 @@ static bool8 DecompressGraphics(void)
break;
case 12:
LoadCompressedPalette(gMoveTypes_Pal, OBJ_PLTT_ID(13), 3 * PLTT_SIZE_4BPP);
LoadCompressedSpriteSheet(&sSpriteSheet_CategoryIcons);
LoadSpritePalette(&sSpritePal_CategoryIcons);
LoadCompressedSpriteSheet(&gSpriteSheet_CategoryIcons);
LoadSpritePalette(&gSpritePal_CategoryIcons);
sMonSummaryScreen->switchCounter = 0;
return TRUE;
}

View File

@ -352,7 +352,7 @@ u32 ScriptGiveMonParameterized(u16 species, u8 level, u16 item, u8 ball, u8 natu
// tera type
if (teraType >= NUMBER_OF_MON_TYPES)
teraType = gSpeciesInfo[species].types[0];
teraType = TYPE_NONE;
SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType);
// EV and IV

View File

@ -0,0 +1,4 @@
#include "global.h"
#include "test/battle.h"
// Tests for Armor Tail are handled in test/battle/ability/dazzling.c

View File

@ -0,0 +1,85 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL);
ASSUME(gMovesInfo[MOVE_TACKLE].power > 0);
}
SINGLE_BATTLE_TEST("Galvanize can not turn certain moves into Electric type moves")
{
u32 move;
PARAMETRIZE { move = MOVE_HIDDEN_POWER; }
PARAMETRIZE { move = MOVE_WEATHER_BALL; }
PARAMETRIZE { move = MOVE_MULTI_ATTACK; }
ASSUME(gMovesInfo[MOVE_HIDDEN_POWER].effect == EFFECT_HIDDEN_POWER);
ASSUME(gMovesInfo[MOVE_WEATHER_BALL].effect == EFFECT_WEATHER_BALL);
ASSUME(gMovesInfo[MOVE_MULTI_ATTACK].effect == EFFECT_CHANGE_TYPE_ON_ITEM);
GIVEN {
PLAYER(SPECIES_KRABBY);
OPPONENT(SPECIES_GEODUDE_ALOLAN) { Ability(ABILITY_GALVANIZE); }
} WHEN {
TURN { MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NOT MESSAGE("It's super effective!");
}
}
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, megaEvolve: TRUE); }
} 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, megaEvolve: TRUE); }
} 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 turns a normal type move into Flying")
{
GIVEN {
PLAYER(SPECIES_MEGANIUM);
OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); }
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE, megaEvolve: TRUE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
MESSAGE("It's super effective!");
}
}

View File

@ -53,3 +53,76 @@ DOUBLE_BATTLE_TEST("Dancer can copy Teeter Dance and confuse both opposing targe
MESSAGE("Wynaut became confused!");
}
}
DOUBLE_BATTLE_TEST("Dancer triggers from slowest to fastest")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_DRAGON_DANCE].danceMove == TRUE);
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_DANCER); Speed(10); }
PLAYER(SPECIES_WYNAUT) { Speed(50); }
OPPONENT(SPECIES_ORICORIO) { Ability(ABILITY_DANCER); Speed(20); }
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_DANCER); Speed(3); }
} WHEN {
TURN { MOVE(playerRight, MOVE_DRAGON_DANCE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ABILITY_POPUP(opponentRight, ABILITY_DANCER);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponentRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
ABILITY_POPUP(playerLeft, ABILITY_DANCER);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ABILITY_POPUP(opponentLeft, ABILITY_DANCER);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
}
}
SINGLE_BATTLE_TEST("Dancer doesn't trigger if the original user flinches")
{
GIVEN {
ASSUME(MoveHasAdditionalEffectWithChance(MOVE_FAKE_OUT, MOVE_EFFECT_FLINCH, 100));
ASSUME(gMovesInfo[MOVE_DRAGON_DANCE].danceMove == TRUE);
PLAYER(SPECIES_WOBBUFFET)
OPPONENT(SPECIES_ORICORIO) { Ability(ABILITY_DANCER); }
} WHEN {
TURN { MOVE(opponent, MOVE_FAKE_OUT); MOVE(player, MOVE_DRAGON_DANCE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FAKE_OUT, opponent);
MESSAGE("Wobbuffet flinched!");
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_DANCER);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponent);
}
}
}
DOUBLE_BATTLE_TEST("Dancer still triggers if another dancer flinches")
{
GIVEN {
ASSUME(MoveHasAdditionalEffectWithChance(MOVE_FAKE_OUT, MOVE_EFFECT_FLINCH, 100));
ASSUME(gMovesInfo[MOVE_DRAGON_DANCE].danceMove == TRUE);
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_DANCER); Speed(10); }
PLAYER(SPECIES_WYNAUT) { Speed(5); }
OPPONENT(SPECIES_ORICORIO) { Ability(ABILITY_DANCER); Speed(20); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(3); }
} WHEN {
TURN { MOVE(opponentLeft, MOVE_FAKE_OUT, target: playerLeft); MOVE(playerRight, MOVE_DRAGON_DANCE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FAKE_OUT, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ABILITY_POPUP(playerLeft, ABILITY_DANCER);
MESSAGE("Wobbuffet flinched!");
NONE_OF {
MESSAGE("Wobbuffet used Dragon Dance!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
}
ABILITY_POPUP(opponentLeft, ABILITY_DANCER);
MESSAGE("Foe Oricorio used Dragon Dance!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DANCE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
}
}

View File

@ -0,0 +1,52 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_QUICK_ATTACK].priority > 0);
}
DOUBLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail protect the user from priority moves")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_BRUXISH; ability = ABILITY_DAZZLING; }
PARAMETRIZE { species = SPECIES_FARIGIRAF; ability = ABILITY_ARMOR_TAIL; }
PARAMETRIZE { species = SPECIES_TSAREENA; ability = ABILITY_QUEENLY_MAJESTY; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_QUICK_ATTACK, target: opponentLeft); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponentRight);
ABILITY_POPUP(opponentLeft, ability);
MESSAGE("Wobbuffet cannot use Quick Attack!");
}
}
DOUBLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail protect users partner from priority moves")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_BRUXISH; ability = ABILITY_DAZZLING; }
PARAMETRIZE { species = SPECIES_FARIGIRAF; ability = ABILITY_ARMOR_TAIL; }
PARAMETRIZE { species = SPECIES_TSAREENA; ability = ABILITY_QUEENLY_MAJESTY; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_QUICK_ATTACK, target: opponentRight); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponentRight);
ABILITY_POPUP(opponentLeft, ability);
MESSAGE("Wobbuffet cannot use Quick Attack!");
}
}

View File

@ -0,0 +1,70 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Good as Gold protects from status moves")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TOXIC].category == DAMAGE_CATEGORY_STATUS);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GHOLDENGO) { Ability(ABILITY_GOOD_AS_GOLD); }
} WHEN {
TURN { MOVE(player, MOVE_TOXIC); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player);
ABILITY_POPUP(opponent, ABILITY_GOOD_AS_GOLD);
MESSAGE("It doesn't affect Foe Gholdengo…");
}
}
SINGLE_BATTLE_TEST("Good as Gold doesn't protect the user from it's own moves")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_NASTY_PLOT].category == DAMAGE_CATEGORY_STATUS);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GHOLDENGO) { Ability(ABILITY_GOOD_AS_GOLD); }
} WHEN {
TURN { MOVE(opponent, MOVE_NASTY_PLOT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_NASTY_PLOT, opponent);
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_GOOD_AS_GOLD);
MESSAGE("It doesn't affect Foe Gholdengo…");
}
}
}
SINGLE_BATTLE_TEST("Good as Gold doesn't protect from moves that target the field")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_STEALTH_ROCK].category == DAMAGE_CATEGORY_STATUS);
ASSUME(gMovesInfo[MOVE_STEALTH_ROCK].target == MOVE_TARGET_OPPONENTS_FIELD);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GHOLDENGO) { Ability(ABILITY_GOOD_AS_GOLD); }
} WHEN {
TURN { MOVE(player, MOVE_STEALTH_ROCK); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, player);
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_GOOD_AS_GOLD);
MESSAGE("It doesn't affect Foe Gholdengo…");
}
}
}
DOUBLE_BATTLE_TEST("Good as Gold protects from partner's status moves")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_HELPING_HAND].category == DAMAGE_CATEGORY_STATUS);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GHOLDENGO) { Ability(ABILITY_GOOD_AS_GOLD); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentRight, MOVE_HELPING_HAND); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HELPING_HAND, opponentRight);
ABILITY_POPUP(opponentLeft, ABILITY_GOOD_AS_GOLD);
MESSAGE("It doesn't affect Foe Gholdengo…");
}
}

View File

@ -107,9 +107,9 @@ SINGLE_BATTLE_TEST("Intimidate and Eject Button force the opponent to Attack")
OPPONENT(SPECIES_HITMONTOP) { Moves(MOVE_TACKLE); }
} WHEN {
TURN {
MOVE(player, MOVE_QUICK_ATTACK);
MOVE(opponent, MOVE_TACKLE);
SEND_OUT(opponent, 1);
MOVE(player, MOVE_QUICK_ATTACK);
MOVE(opponent, MOVE_TACKLE);
SEND_OUT(opponent, 1);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player);
@ -210,3 +210,21 @@ SINGLE_BATTLE_TEST("Intimidate can not further lower opponents Atk stat if it is
EXPECT_EQ(player->statStages[STAT_ATK], MIN_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Intimidate activates when it's no longer effected by Neutralizing Gas")
{
GIVEN {
PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN { SWITCH(player, 1); }
} SCENE {
ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS);
MESSAGE("Neutralizing Gas filled the area!");
MESSAGE("Weezing, that's enough! Come back!");
MESSAGE("The effects of Neutralizing Gas wore off!");
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
MESSAGE("Go! Wobbuffet!");
}
}

View File

@ -0,0 +1,4 @@
#include "global.h"
#include "test/battle.h"
// Tests for Queenly Majesty are handled in test/battle/ability/dazzling.c

View File

@ -2,26 +2,6 @@
#include "test/battle.h"
#include "battle_ai_util.h"
AI_SINGLE_BATTLE_TEST("AI gets baited by Protect Switch tactics") // This behavior is to be fixed.
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
PLAYER(SPECIES_STUNFISK);
PLAYER(SPECIES_PELIPPER);
OPPONENT(SPECIES_DARKRAI) { Moves(MOVE_TACKLE, MOVE_PECK, MOVE_EARTHQUAKE, MOVE_THUNDERBOLT); }
OPPONENT(SPECIES_SCIZOR) { Moves(MOVE_HYPER_BEAM, MOVE_FACADE, MOVE_GIGA_IMPACT, MOVE_EXTREME_SPEED); }
} WHEN {
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); } // E-quake
TURN { SWITCH(player, 1); EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); } // E-quake
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_THUNDERBOLT); } // T-Bolt
TURN { SWITCH(player, 0); EXPECT_MOVE(opponent, MOVE_THUNDERBOLT); } // T-Bolt
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); } // E-quake
TURN { SWITCH(player, 1); EXPECT_MOVE(opponent, MOVE_EARTHQUAKE);} // E-quake
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_THUNDERBOLT); } // T-Bolt
}
}
AI_SINGLE_BATTLE_TEST("AI prefers Bubble over Water Gun if it's slower")
{
u32 speedPlayer, speedAi;
@ -526,222 +506,6 @@ AI_SINGLE_BATTLE_TEST("AI will choose either Rock Tomb or Bulldoze if Stat drop
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculation checks whether incoming damage is less than recurring healing to avoid an infinite loop")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_VENUSAUR) { Level(30); Moves(MOVE_TACKLE); }
// Opponent party courtesy of Skolgrahd, who triggered the bug in the first place
OPPONENT(SPECIES_PIKACHU) { Level(100); Moves(MOVE_ZIPPY_ZAP, MOVE_EXTREME_SPEED, MOVE_IRON_TAIL, MOVE_KNOCK_OFF); }
OPPONENT(SPECIES_NINETALES_ALOLAN) { Level(100); Moves(MOVE_AURORA_VEIL, MOVE_BLIZZARD, MOVE_MOONBLAST, MOVE_DISABLE); }
OPPONENT(SPECIES_WEAVILE) { Level(100); Moves(MOVE_NIGHT_SLASH, MOVE_TRIPLE_AXEL, MOVE_ICE_SHARD, MOVE_FAKE_OUT); }
OPPONENT(SPECIES_DITTO) { Level(100); Moves(MOVE_TRANSFORM); }
OPPONENT(SPECIES_TYPHLOSION) { Level(100); Moves(MOVE_ERUPTION, MOVE_HEAT_WAVE, MOVE_FOCUS_BLAST, MOVE_EXTRASENSORY); }
OPPONENT(SPECIES_UMBREON) { Level(100); Item(ITEM_LEFTOVERS); Moves(MOVE_FOUL_PLAY, MOVE_SNARL, MOVE_HELPING_HAND, MOVE_THUNDER_WAVE); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVES(opponent, MOVE_ZIPPY_ZAP, MOVE_EXTREME_SPEED, MOVE_IRON_TAIL, MOVE_KNOCK_OFF); }
} SCENE {
MESSAGE("Venusaur fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculation checks whether incoming damage is zero to avoid an infinite loop")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_BULBASAUR) { Level(5); Moves(MOVE_SWORDS_DANCE, MOVE_WHIRLWIND, MOVE_SAND_ATTACK, MOVE_TAIL_WHIP); }
// Scenario courtesy of Duke, who triggered the bug in the first place
OPPONENT(SPECIES_GEODUDE) { Level(100); Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_GEODUDE) { Level(100); Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_NOSEPASS) { Level(100); Moves(MOVE_TACKLE); }
} WHEN {
TURN { MOVE(player, MOVE_SWORDS_DANCE); EXPECT_MOVES(opponent, MOVE_TACKLE); }
} SCENE {
MESSAGE("Bulbasaur fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Avoid infinite loop if damage taken is equal to recurring healing")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_MEOWTH_GALARIAN) { Level(100); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); }
// Scenario courtesy of Duke, who triggered the bug in the first place
OPPONENT(SPECIES_MEOWTH_GALARIAN) { Level(5); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); }
OPPONENT(SPECIES_GEODUDE) { Level(5); Moves(MOVE_DOUBLE_EDGE); }
OPPONENT(SPECIES_GEODUDE) { Level(5); Moves(MOVE_DOUBLE_EDGE); }
OPPONENT(SPECIES_NOSEPASS) { Level(5); Moves(MOVE_DOUBLE_EDGE); }
OPPONENT(SPECIES_HOUNDSTONE) { Level(5); Moves(MOVE_NIGHT_SHADE, MOVE_BODY_PRESS, MOVE_WILL_O_WISP, MOVE_PROTECT); Item(ITEM_LEFTOVERS); }
} WHEN {
TURN { MOVE(player, MOVE_FAKE_OUT); EXPECT_MOVES(opponent, MOVE_FAKE_OUT); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will not switch in a Pokemon which is slower and gets 1HKOed after fainting")
{
bool32 alakazamFirst;
u32 speedAlakazm;
u32 aiSmartSwitchFlags = 0;
PARAMETRIZE{ speedAlakazm = 200; alakazamFirst = TRUE; } // AI will always send out Alakazan as it sees a KO with Focus Blast, even if Alakazam dies before it can get it off
PARAMETRIZE{ speedAlakazm = 200; alakazamFirst = FALSE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES lets AI see that Alakazam would be KO'd before it can KO, and won't switch it in
PARAMETRIZE{ speedAlakazm = 400; alakazamFirst = TRUE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES recognizes that Alakazam is faster and can KO, and will switch it in
GIVEN {
ASSUME(gMovesInfo[MOVE_PSYCHIC].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_FOCUS_BLAST].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_BUBBLE_BEAM].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_WATER_GUN].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_STRENGTH].category == DAMAGE_CATEGORY_PHYSICAL);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags);
PLAYER(SPECIES_WEAVILE) { Speed(300); Ability(ABILITY_SHADOW_TAG); } // Weavile has Shadow Tag, so AI can't switch on the first turn, but has to do it after fainting.
OPPONENT(SPECIES_KADABRA) { Speed(200); Moves(MOVE_PSYCHIC, MOVE_DISABLE, MOVE_TAUNT, MOVE_CALM_MIND); }
OPPONENT(SPECIES_ALAKAZAM) { Speed(speedAlakazm); Moves(MOVE_FOCUS_BLAST, MOVE_PSYCHIC); } // Alakazam has a move which OHKOes Weavile, but it doesn't matter if he's getting KO-ed first.
OPPONENT(SPECIES_BLASTOISE) { Speed(200); Moves(MOVE_BUBBLE_BEAM, MOVE_WATER_GUN, MOVE_LEER, MOVE_STRENGTH); } // Can't OHKO, but survives a hit from Weavile's Night Slash.
} WHEN {
TURN { MOVE(player, MOVE_NIGHT_SLASH) ; EXPECT_SEND_OUT(opponent, alakazamFirst ? 1 : 2); } // AI doesn't send out Alakazam if it gets outsped
} SCENE {
MESSAGE("Foe Kadabra fainted!");
if (alakazamFirst) {
MESSAGE("{PKMN} TRAINER LEAF sent out Alakazam!");
} else {
MESSAGE("{PKMN} TRAINER LEAF sent out Blastoise!");
}
}
}
AI_SINGLE_BATTLE_TEST("AI switches if Perish Song is about to kill")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) {Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_CROBAT) {Moves(MOVE_TACKLE); }
} WHEN {
TURN { MOVE(player, MOVE_PERISH_SONG); }
TURN { ; }
TURN { ; }
TURN { EXPECT_SWITCH(opponent, 1); }
} SCENE {
MESSAGE("{PKMN} TRAINER LEAF sent out Crobat!");
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI considers hazard damage when choosing which Pokemon to switch in")
{
u32 aiIsSmart = 0;
u32 aiSmartSwitchFlags = 0;
PARAMETRIZE{ aiIsSmart = 0; aiSmartSwitchFlags = 0; } // AI doesn't care about hazard damage resulting in Pokemon being KO'd
PARAMETRIZE{ aiIsSmart = 1; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES avoids being KO'd as a result of hazards damage
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags);
PLAYER(SPECIES_MEGANIUM) { Speed(100); SpDefense(328); SpAttack(265); Moves(MOVE_STEALTH_ROCK, MOVE_SURF); } // Meganium does ~56% minimum ~66% maximum, enough to KO Charizard after rocks and never KO Typhlosion after rocks
OPPONENT(SPECIES_PONYTA) { Level(5); Speed(5); Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_CHARIZARD) { Speed(200); Moves(MOVE_FLAMETHROWER); SpAttack(317); SpDefense(207); MaxHP(297); } // Outspeends and 2HKOs Meganium
OPPONENT(SPECIES_TYPHLOSION) { Speed(200); Moves(MOVE_FLAMETHROWER); SpAttack(317); SpDefense(207); MaxHP(297); } // Outspeends and 2HKOs Meganium
} WHEN {
TURN { MOVE(player, MOVE_STEALTH_ROCK) ;}
TURN { MOVE(player, MOVE_SURF); EXPECT_SEND_OUT(opponent, aiIsSmart ? 2 : 1); } // AI sends out Typhlosion to get the KO with the flag rather than Charizard
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize type matchup + SE move, then type matchup")
{
u32 aiSmartSwitchFlags = 0;
u32 move1;
u32 move2;
u32 expectedIndex;
PARAMETRIZE{ expectedIndex = 3; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; } // When not smart, AI will only switch in a defensive mon if it has a SE move, otherwise will just default to damage
PARAMETRIZE{ expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; }
PARAMETRIZE{ expectedIndex = 2; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // When smart, AI will prioritize SE move, but still switch in good type matchup without SE move
PARAMETRIZE{ expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags);
PLAYER(SPECIES_MARSHTOMP) { Level(30); Moves(MOVE_MUD_BOMB, MOVE_WATER_GUN, MOVE_GROWL, MOVE_MUD_SHOT); Speed(5); }
OPPONENT(SPECIES_PONYTA) { Level(1); Moves(MOVE_NONE); Speed(6); } // Forces switchout
OPPONENT(SPECIES_TANGELA) { Level(30); Moves(move1); Speed(4); }
OPPONENT(SPECIES_LOMBRE) { Level(30); Moves(move2); Speed(4); }
OPPONENT(SPECIES_HARIYAMA) { Level(30); Moves(MOVE_VITAL_THROW); Speed(4); }
} WHEN {
TURN { MOVE(player, MOVE_GROWL); EXPECT_SWITCH(opponent, expectedIndex); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize defensive options")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_SWELLOW) { Level(30); Moves(MOVE_WING_ATTACK, MOVE_BOOMBURST); Speed(5); }
OPPONENT(SPECIES_PONYTA) { Level(1); Moves(MOVE_NONE); Speed(4); } // Forces switchout
OPPONENT(SPECIES_ARON) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); SpDefense(41); } // Mid battle, AI sends out Aron
OPPONENT(SPECIES_ELECTRODE) { Level(30); Moves(MOVE_CHARGE_BEAM); Speed(6); }
} WHEN {
TURN { MOVE(player, MOVE_WING_ATTACK); EXPECT_SWITCH(opponent, 1); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Post-KO switches prioritize offensive options")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_SWELLOW) { Level(30); Moves(MOVE_WING_ATTACK, MOVE_BOOMBURST); Speed(5); }
OPPONENT(SPECIES_PONYTA) { Level(1); Moves(MOVE_TACKLE); Speed(4); }
OPPONENT(SPECIES_ARON) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); } // Mid battle, AI sends out Aron
OPPONENT(SPECIES_ELECTRODE) { Level(30); Moves(MOVE_CHARGE_BEAM); Speed(6); }
} WHEN {
TURN { MOVE(player, MOVE_WING_ATTACK); EXPECT_SEND_OUT(opponent, 2); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI switches out after sufficient stat drops")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
PLAYER(SPECIES_HITMONTOP) { Level(30); Moves(MOVE_CHARM, MOVE_TACKLE); Ability(ABILITY_INTIMIDATE); Speed(5); }
OPPONENT(SPECIES_GRIMER) { Level(30); Moves(MOVE_TACKLE); Speed(4); }
OPPONENT(SPECIES_PONYTA) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); }
} WHEN {
TURN { MOVE(player, MOVE_CHARM); }
TURN { MOVE(player, MOVE_TACKLE); EXPECT_SWITCH(opponent, 1); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemon would faint to hazards unless party member can clear them")
{
u32 move1;
PARAMETRIZE{ move1 = MOVE_TACKLE; }
PARAMETRIZE{ move1 = MOVE_RAPID_SPIN; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_RAPID_SPIN].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_HEADBUTT].category == DAMAGE_CATEGORY_PHYSICAL);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
PLAYER(SPECIES_HITMONTOP) { Level(30); Moves(MOVE_CHARM, MOVE_TACKLE, MOVE_STEALTH_ROCK, MOVE_EARTHQUAKE); Ability(ABILITY_INTIMIDATE); Speed(5); }
OPPONENT(SPECIES_GRIMER) { Level(30); Moves(MOVE_TACKLE); Item(ITEM_FOCUS_SASH); Speed(4); }
OPPONENT(SPECIES_PONYTA) { Level(30); Moves(MOVE_HEADBUTT, move1); Speed(4); }
} WHEN {
TURN { MOVE(player, MOVE_STEALTH_ROCK); }
TURN { MOVE(player, MOVE_EARTHQUAKE); }
TURN { MOVE(player, MOVE_CHARM); }
TURN { // If the AI has a mon that can remove hazards, don't prevent them switching out
MOVE(player, MOVE_CHARM);
if (move1 == MOVE_RAPID_SPIN)
EXPECT_SWITCH(opponent, 1);
else if (move1 == MOVE_TACKLE)
EXPECT_MOVE(opponent, MOVE_TACKLE);
}
}
}
AI_SINGLE_BATTLE_TEST("First Impression is preferred on the first turn of the species if it's the best dmg move")
{
GIVEN {
@ -778,34 +542,6 @@ AI_SINGLE_BATTLE_TEST("First Impression is not chosen if it's blocked by certain
}
}
AI_DOUBLE_BATTLE_TEST("AI will not try to switch for the same pokemon for 2 spots in a double battle")
{
u32 flags;
PARAMETRIZE {flags = AI_FLAG_SMART_SWITCHING; }
PARAMETRIZE {flags = 0; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | flags);
PLAYER(SPECIES_RATTATA);
PLAYER(SPECIES_RATTATA);
// No moves to damage player.
OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SHADOW_BALL); }
OPPONENT(SPECIES_HAUNTER) { Moves(MOVE_SHADOW_BALL); }
OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SHADOW_BALL); }
OPPONENT(SPECIES_RATICATE) { Moves(MOVE_HEADBUTT); }
} WHEN {
TURN { EXPECT_SWITCH(opponentLeft, 3); };
} SCENE {
MESSAGE("{PKMN} TRAINER LEAF withdrew Gengar!");
MESSAGE("{PKMN} TRAINER LEAF sent out Raticate!");
NONE_OF {
MESSAGE("{PKMN} TRAINER LEAF withdrew Haunter!");
MESSAGE("{PKMN} TRAINER LEAF sent out Raticate!");
}
}
}
AI_SINGLE_BATTLE_TEST("AI will not choose Burn Up if the user lost the Fire typing")
{
GIVEN {

View File

@ -0,0 +1,91 @@
#include "global.h"
#include "test/battle.h"
AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will blindly Mirror Coat against special attackers")
{
u32 aiRiskyFlag = 0;
PARAMETRIZE{ aiRiskyFlag = 0; }
PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; }
GIVEN {
ASSUME(gMovesInfo[MOVE_MIRROR_COAT].effect == EFFECT_MIRROR_COAT);
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); }
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); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will blindly Counter against physical attackers")
{
u32 aiRiskyFlag = 0;
PARAMETRIZE{ aiRiskyFlag = 0; }
PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; }
GIVEN {
ASSUME(gMovesInfo[MOVE_COUNTER].effect == EFFECT_COUNTER);
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); }
OPPONENT(SPECIES_CASTFORM) { Level(20); Moves(MOVE_TACKLE, MOVE_COUNTER); }
} WHEN {
TURN { MOVE(player, MOVE_WATERFALL) ; EXPECT_MOVE(opponent, aiRiskyFlag ? MOVE_COUNTER : MOVE_TACKLE); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will prioritize Revenge if slower")
{
u32 aiRiskyFlag = 0;
PARAMETRIZE{ aiRiskyFlag = 0; }
PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; }
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); }
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); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: Mid-battle switches prioritize offensive options")
{
u32 aiRiskyFlag = 0;
PARAMETRIZE{ aiRiskyFlag = 0; }
PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | aiRiskyFlag);
PLAYER(SPECIES_SWELLOW) { Level(30); Moves(MOVE_WING_ATTACK, MOVE_BOOMBURST); Speed(5); }
OPPONENT(SPECIES_PONYTA) { Level(1); Moves(MOVE_NONE); Speed(4); } // Forces switchout
OPPONENT(SPECIES_ARON) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); SpDefense(41); } // Mid battle, AI sends out Aron
OPPONENT(SPECIES_ELECTRODE) { Level(30); Moves(MOVE_CHARGE_BEAM); Speed(6); }
} WHEN {
TURN { MOVE(player, MOVE_WING_ATTACK); EXPECT_SWITCH(opponent, aiRiskyFlag? 2 : 1); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI prefers high damage moves at the expense of accuracy regardless of KO thresholds")
{
u32 aiRiskyFlag = 0;
PARAMETRIZE{ aiRiskyFlag = 0; }
PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiRiskyFlag);
PLAYER(SPECIES_GOLDEEN) { 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); }
}
}

331
test/battle/ai_switching.c Normal file
View File

@ -0,0 +1,331 @@
#include "global.h"
#include "test/battle.h"
AI_SINGLE_BATTLE_TEST("AI gets baited by Protect Switch tactics") // This behavior is to be fixed.
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
PLAYER(SPECIES_STUNFISK);
PLAYER(SPECIES_PELIPPER);
OPPONENT(SPECIES_DARKRAI) { Moves(MOVE_TACKLE, MOVE_PECK, MOVE_EARTHQUAKE, MOVE_THUNDERBOLT); }
OPPONENT(SPECIES_SCIZOR) { Moves(MOVE_HYPER_BEAM, MOVE_FACADE, MOVE_GIGA_IMPACT, MOVE_EXTREME_SPEED); }
} WHEN {
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); } // E-quake
TURN { SWITCH(player, 1); EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); } // E-quake
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_THUNDERBOLT); } // T-Bolt
TURN { SWITCH(player, 0); EXPECT_MOVE(opponent, MOVE_THUNDERBOLT); } // T-Bolt
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_EARTHQUAKE); } // E-quake
TURN { SWITCH(player, 1); EXPECT_MOVE(opponent, MOVE_EARTHQUAKE);} // E-quake
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_THUNDERBOLT); } // T-Bolt
}
}
// General switching behaviour
AI_SINGLE_BATTLE_TEST("AI switches if Perish Song is about to kill")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) {Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_CROBAT) {Moves(MOVE_TACKLE); }
} WHEN {
TURN { MOVE(player, MOVE_PERISH_SONG); }
TURN { ; }
TURN { ; }
TURN { EXPECT_SWITCH(opponent, 1); }
} SCENE {
MESSAGE("{PKMN} TRAINER LEAF sent out Crobat!");
}
}
AI_DOUBLE_BATTLE_TEST("AI will not try to switch for the same pokemon for 2 spots in a double battle")
{
u32 flags;
PARAMETRIZE {flags = AI_FLAG_SMART_SWITCHING; }
PARAMETRIZE {flags = 0; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | flags);
PLAYER(SPECIES_RATTATA);
PLAYER(SPECIES_RATTATA);
// No moves to damage player.
OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SHADOW_BALL); }
OPPONENT(SPECIES_HAUNTER) { Moves(MOVE_SHADOW_BALL); }
OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SHADOW_BALL); }
OPPONENT(SPECIES_RATICATE) { Moves(MOVE_HEADBUTT); }
} WHEN {
TURN { EXPECT_SWITCH(opponentLeft, 3); };
} SCENE {
MESSAGE("{PKMN} TRAINER LEAF withdrew Gengar!");
MESSAGE("{PKMN} TRAINER LEAF sent out Raticate!");
NONE_OF {
MESSAGE("{PKMN} TRAINER LEAF withdrew Haunter!");
MESSAGE("{PKMN} TRAINER LEAF sent out Raticate!");
}
}
}
// General AI_FLAG_SMART_MON_CHOICES behaviour
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculation checks whether incoming damage is less than recurring healing to avoid an infinite loop")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_VENUSAUR) { Level(30); Moves(MOVE_TACKLE); }
// Opponent party courtesy of Skolgrahd, who triggered the bug in the first place
OPPONENT(SPECIES_PIKACHU) { Level(100); Moves(MOVE_ZIPPY_ZAP, MOVE_EXTREME_SPEED, MOVE_IRON_TAIL, MOVE_KNOCK_OFF); }
OPPONENT(SPECIES_NINETALES_ALOLAN) { Level(100); Moves(MOVE_AURORA_VEIL, MOVE_BLIZZARD, MOVE_MOONBLAST, MOVE_DISABLE); }
OPPONENT(SPECIES_WEAVILE) { Level(100); Moves(MOVE_NIGHT_SLASH, MOVE_TRIPLE_AXEL, MOVE_ICE_SHARD, MOVE_FAKE_OUT); }
OPPONENT(SPECIES_DITTO) { Level(100); Moves(MOVE_TRANSFORM); }
OPPONENT(SPECIES_TYPHLOSION) { Level(100); Moves(MOVE_ERUPTION, MOVE_HEAT_WAVE, MOVE_FOCUS_BLAST, MOVE_EXTRASENSORY); }
OPPONENT(SPECIES_UMBREON) { Level(100); Item(ITEM_LEFTOVERS); Moves(MOVE_FOUL_PLAY, MOVE_SNARL, MOVE_HELPING_HAND, MOVE_THUNDER_WAVE); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); EXPECT_MOVES(opponent, MOVE_ZIPPY_ZAP, MOVE_EXTREME_SPEED, MOVE_IRON_TAIL, MOVE_KNOCK_OFF); }
} SCENE {
MESSAGE("Venusaur fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculation checks whether incoming damage is zero to avoid an infinite loop")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_BULBASAUR) { Level(5); Moves(MOVE_SWORDS_DANCE, MOVE_WHIRLWIND, MOVE_SAND_ATTACK, MOVE_TAIL_WHIP); }
// Scenario courtesy of Duke, who triggered the bug in the first place
OPPONENT(SPECIES_GEODUDE) { Level(100); Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_GEODUDE) { Level(100); Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_NOSEPASS) { Level(100); Moves(MOVE_TACKLE); }
} WHEN {
TURN { MOVE(player, MOVE_SWORDS_DANCE); EXPECT_MOVES(opponent, MOVE_TACKLE); }
} SCENE {
MESSAGE("Bulbasaur fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Avoid infinite loop if damage taken is equal to recurring healing")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_MEOWTH_GALARIAN) { Level(100); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); }
// Scenario courtesy of Duke, who triggered the bug in the first place
OPPONENT(SPECIES_MEOWTH_GALARIAN) { Level(5); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); }
OPPONENT(SPECIES_GEODUDE) { Level(5); Moves(MOVE_DOUBLE_EDGE); }
OPPONENT(SPECIES_GEODUDE) { Level(5); Moves(MOVE_DOUBLE_EDGE); }
OPPONENT(SPECIES_NOSEPASS) { Level(5); Moves(MOVE_DOUBLE_EDGE); }
OPPONENT(SPECIES_HOUNDSTONE) { Level(5); Moves(MOVE_NIGHT_SHADE, MOVE_BODY_PRESS, MOVE_WILL_O_WISP, MOVE_PROTECT); Item(ITEM_LEFTOVERS); }
} WHEN {
TURN { MOVE(player, MOVE_FAKE_OUT); EXPECT_MOVES(opponent, MOVE_FAKE_OUT); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will not switch in a Pokemon which is slower and gets 1HKOed after fainting")
{
bool32 alakazamFirst;
u32 speedAlakazm;
u32 aiSmartSwitchFlags = 0;
PARAMETRIZE{ speedAlakazm = 200; alakazamFirst = TRUE; } // AI will always send out Alakazan as it sees a KO with Focus Blast, even if Alakazam dies before it can get it off
PARAMETRIZE{ speedAlakazm = 200; alakazamFirst = FALSE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES lets AI see that Alakazam would be KO'd before it can KO, and won't switch it in
PARAMETRIZE{ speedAlakazm = 400; alakazamFirst = TRUE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES recognizes that Alakazam is faster and can KO, and will switch it in
GIVEN {
ASSUME(gMovesInfo[MOVE_PSYCHIC].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_FOCUS_BLAST].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_BUBBLE_BEAM].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_WATER_GUN].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_STRENGTH].category == DAMAGE_CATEGORY_PHYSICAL);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags);
PLAYER(SPECIES_WEAVILE) { Speed(300); Ability(ABILITY_SHADOW_TAG); } // Weavile has Shadow Tag, so AI can't switch on the first turn, but has to do it after fainting.
OPPONENT(SPECIES_KADABRA) { Speed(200); Moves(MOVE_PSYCHIC, MOVE_DISABLE, MOVE_TAUNT, MOVE_CALM_MIND); }
OPPONENT(SPECIES_ALAKAZAM) { Speed(speedAlakazm); Moves(MOVE_FOCUS_BLAST, MOVE_PSYCHIC); } // Alakazam has a move which OHKOes Weavile, but it doesn't matter if he's getting KO-ed first.
OPPONENT(SPECIES_BLASTOISE) { Speed(200); Moves(MOVE_BUBBLE_BEAM, MOVE_WATER_GUN, MOVE_LEER, MOVE_STRENGTH); } // Can't OHKO, but survives a hit from Weavile's Night Slash.
} WHEN {
TURN { MOVE(player, MOVE_NIGHT_SLASH) ; EXPECT_SEND_OUT(opponent, alakazamFirst ? 1 : 2); } // AI doesn't send out Alakazam if it gets outsped
} SCENE {
MESSAGE("Foe Kadabra fainted!");
if (alakazamFirst) {
MESSAGE("{PKMN} TRAINER LEAF sent out Alakazam!");
} else {
MESSAGE("{PKMN} TRAINER LEAF sent out Blastoise!");
}
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI considers hazard damage when choosing which Pokemon to switch in")
{
u32 aiIsSmart = 0;
u32 aiSmartSwitchFlags = 0;
PARAMETRIZE{ aiIsSmart = 0; aiSmartSwitchFlags = 0; } // AI doesn't care about hazard damage resulting in Pokemon being KO'd
PARAMETRIZE{ aiIsSmart = 1; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES avoids being KO'd as a result of hazards damage
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags);
PLAYER(SPECIES_MEGANIUM) { Speed(100); SpDefense(328); SpAttack(265); Moves(MOVE_STEALTH_ROCK, MOVE_SURF); } // Meganium does ~56% minimum ~66% maximum, enough to KO Charizard after rocks and never KO Typhlosion after rocks
OPPONENT(SPECIES_PONYTA) { Level(5); Speed(5); Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_CHARIZARD) { Speed(200); Moves(MOVE_FLAMETHROWER); SpAttack(317); SpDefense(207); MaxHP(297); } // Outspeends and 2HKOs Meganium
OPPONENT(SPECIES_TYPHLOSION) { Speed(200); Moves(MOVE_FLAMETHROWER); SpAttack(317); SpDefense(207); MaxHP(297); } // Outspeends and 2HKOs Meganium
} WHEN {
TURN { MOVE(player, MOVE_STEALTH_ROCK) ;}
TURN { MOVE(player, MOVE_SURF); EXPECT_SEND_OUT(opponent, aiIsSmart ? 2 : 1); } // AI sends out Typhlosion to get the KO with the flag rather than Charizard
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize type matchup + SE move, then type matchup")
{
u32 aiSmartSwitchFlags = 0;
u32 move1;
u32 move2;
u32 expectedIndex;
PARAMETRIZE{ expectedIndex = 3; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; } // When not smart, AI will only switch in a defensive mon if it has a SE move, otherwise will just default to damage
PARAMETRIZE{ expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; }
PARAMETRIZE{ expectedIndex = 2; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // When smart, AI will prioritize SE move, but still switch in good type matchup without SE move
PARAMETRIZE{ expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags);
PLAYER(SPECIES_MARSHTOMP) { Level(30); Moves(MOVE_MUD_BOMB, MOVE_WATER_GUN, MOVE_GROWL, MOVE_MUD_SHOT); Speed(5); }
OPPONENT(SPECIES_PONYTA) { Level(1); Moves(MOVE_NONE); Speed(6); } // Forces switchout
OPPONENT(SPECIES_TANGELA) { Level(30); Moves(move1); Speed(4); }
OPPONENT(SPECIES_LOMBRE) { Level(30); Moves(move2); Speed(4); }
OPPONENT(SPECIES_HARIYAMA) { Level(30); Moves(MOVE_VITAL_THROW); Speed(4); }
} WHEN {
TURN { MOVE(player, MOVE_GROWL); EXPECT_SWITCH(opponent, expectedIndex); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize defensive options")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_SWELLOW) { Level(30); Moves(MOVE_WING_ATTACK, MOVE_BOOMBURST); Speed(5); }
OPPONENT(SPECIES_PONYTA) { Level(1); Moves(MOVE_NONE); Speed(4); } // Forces switchout
OPPONENT(SPECIES_ARON) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); SpDefense(41); } // Mid battle, AI sends out Aron
OPPONENT(SPECIES_ELECTRODE) { Level(30); Moves(MOVE_CHARGE_BEAM); Speed(6); }
} WHEN {
TURN { MOVE(player, MOVE_WING_ATTACK); EXPECT_SWITCH(opponent, 1); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Post-KO switches prioritize offensive options")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_SWELLOW) { Level(30); Moves(MOVE_WING_ATTACK, MOVE_BOOMBURST); Speed(5); }
OPPONENT(SPECIES_PONYTA) { Level(1); Moves(MOVE_TACKLE); Speed(4); }
OPPONENT(SPECIES_ARON) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); } // Mid battle, AI sends out Aron
OPPONENT(SPECIES_ELECTRODE) { Level(30); Moves(MOVE_CHARGE_BEAM); Speed(6); }
} WHEN {
TURN { MOVE(player, MOVE_WING_ATTACK); EXPECT_SEND_OUT(opponent, 2); }
}
}
// General AI_FLAG_SMART_SWITCHING behaviour
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI switches out after sufficient stat drops")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
PLAYER(SPECIES_HITMONTOP) { Level(30); Moves(MOVE_CHARM, MOVE_TACKLE); Ability(ABILITY_INTIMIDATE); Speed(5); }
OPPONENT(SPECIES_GRIMER) { Level(30); Moves(MOVE_TACKLE); Speed(4); }
OPPONENT(SPECIES_PONYTA) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); }
} WHEN {
TURN { MOVE(player, MOVE_CHARM); }
TURN { MOVE(player, MOVE_TACKLE); EXPECT_SWITCH(opponent, 1); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemon would faint to hazards unless party member can clear them")
{
u32 move1;
PARAMETRIZE{ move1 = MOVE_TACKLE; }
PARAMETRIZE{ move1 = MOVE_RAPID_SPIN; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_RAPID_SPIN].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_HEADBUTT].category == DAMAGE_CATEGORY_PHYSICAL);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
PLAYER(SPECIES_HITMONTOP) { Level(30); Moves(MOVE_CHARM, MOVE_TACKLE, MOVE_STEALTH_ROCK, MOVE_EARTHQUAKE); Ability(ABILITY_INTIMIDATE); Speed(5); }
OPPONENT(SPECIES_GRIMER) { Level(30); Moves(MOVE_TACKLE); Item(ITEM_FOCUS_SASH); Speed(4); }
OPPONENT(SPECIES_PONYTA) { Level(30); Moves(MOVE_HEADBUTT, move1); Speed(4); }
} WHEN {
TURN { MOVE(player, MOVE_STEALTH_ROCK); }
TURN { MOVE(player, MOVE_EARTHQUAKE); }
TURN { MOVE(player, MOVE_CHARM); }
TURN { // If the AI has a mon that can remove hazards, don't prevent them switching out
MOVE(player, MOVE_CHARM);
if (move1 == MOVE_RAPID_SPIN)
EXPECT_SWITCH(opponent, 1);
else if (move1 == MOVE_TACKLE)
EXPECT_MOVE(opponent, MOVE_TACKLE);
}
}
}
// Trapping behaviour
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch in trapping mon mid battle")
{
u32 aiSmartSwitchingFlag = 0;
PARAMETRIZE { aiSmartSwitchingFlag = 0; }
PARAMETRIZE { aiSmartSwitchingFlag = AI_FLAG_SMART_SWITCHING; }
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_GOLURK].types[0] == TYPE_GROUND);
ASSUME(gSpeciesInfo[SPECIES_GOLURK].types[1] == TYPE_GHOST);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchingFlag);
PLAYER(SPECIES_ELECTRODE) { Speed(4); Moves(MOVE_THUNDERBOLT, MOVE_AURA_SPHERE, MOVE_PROTECT); }
PLAYER(SPECIES_WOBBUFFET) { Speed(1); };
OPPONENT(SPECIES_SNORLAX) { Speed(1); Moves(MOVE_HEADBUTT); }
OPPONENT(SPECIES_DUGTRIO) { Speed(3); Ability(ABILITY_ARENA_TRAP); Moves(MOVE_EARTHQUAKE); }
OPPONENT(SPECIES_GOLURK) { Speed(5); Moves(MOVE_EARTHQUAKE); }
} WHEN {
if (aiSmartSwitchingFlag == AI_FLAG_SMART_SWITCHING)
TURN { MOVE(player, MOVE_AURA_SPHERE) ; EXPECT_SWITCH(opponent, 1); }
else
TURN { MOVE(player, MOVE_AURA_SPHERE) ; EXPECT_MOVE(opponent, MOVE_HEADBUTT); }
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will switch in trapping mon after KO")
{
u32 aiSmartMonChoicesFlag = 0; // Enables trapping behaviour after KOs
PARAMETRIZE { aiSmartMonChoicesFlag = 0; } // No trapping behaviour
PARAMETRIZE { aiSmartMonChoicesFlag = AI_FLAG_SMART_MON_CHOICES; } // Traps with mid battle switches
GIVEN{
ASSUME(gSpeciesInfo[SPECIES_MAWILE].types[0] == TYPE_STEEL);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartMonChoicesFlag);
PLAYER(SPECIES_MAWILE) { Speed(2); Moves(MOVE_PROTECT, MOVE_TACKLE); }
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
OPPONENT(SPECIES_SNORLAX) { Speed(3); Moves(MOVE_SELF_DESTRUCT); }
OPPONENT(SPECIES_MAGNEZONE) { Speed(1); Ability(ABILITY_MAGNET_PULL); Moves(MOVE_SHOCK_WAVE); }
OPPONENT(SPECIES_MEGANIUM) { Speed(3); Moves(MOVE_EARTH_POWER); }
} WHEN {
if (aiSmartMonChoicesFlag == AI_FLAG_SMART_MON_CHOICES)
TURN{ MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_SELF_DESTRUCT); EXPECT_SEND_OUT(opponent, 1); }
else
TURN{ MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_SELF_DESTRUCT); EXPECT_SEND_OUT(opponent, 2); }
}
}
AI_SINGLE_BATTLE_TEST("AI won't use trapping behaviour if player only has 1 mon left")
{
u32 aiSmartMonChoicesFlag = 0; // Enables trapping behaviour after KOs
PARAMETRIZE { aiSmartMonChoicesFlag = 0; } // No trapping behaviour
PARAMETRIZE { aiSmartMonChoicesFlag = AI_FLAG_SMART_MON_CHOICES; } // Traps with mid battle switches
GIVEN{
ASSUME(gSpeciesInfo[SPECIES_MAWILE].types[0] == TYPE_STEEL);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartMonChoicesFlag);
PLAYER(SPECIES_MAWILE) { Speed(2); Moves(MOVE_PROTECT, MOVE_TACKLE); }
OPPONENT(SPECIES_SNORLAX) { Speed(3); Moves(MOVE_SELF_DESTRUCT); }
OPPONENT(SPECIES_MAGNEZONE) { Speed(1); Ability(ABILITY_MAGNET_PULL); Moves(MOVE_SHOCK_WAVE); }
OPPONENT(SPECIES_MEGANIUM) { Speed(3); Moves(MOVE_EARTH_POWER); }
} WHEN {
TURN{ MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_SELF_DESTRUCT); EXPECT_SEND_OUT(opponent, 2); }
}
}

View File

@ -1158,6 +1158,8 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Terror traps both opponents")
}
}
TO_DO_BATTLE_TEST("(DYNAMAX) Baton Pass passes G-Max Terror's escape prevention effect");
DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Meltdown torments both opponents for 3 turns")
{
GIVEN {
@ -1391,6 +1393,8 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Chi Strike boosts allies' crit chance")
}
}
TO_DO_BATTLE_TEST("(DYNAMAX) Baton Pass doesn't pass G-Max Chi Strike's effect");
DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Depletion takes away 2 PP from the target's last move")
{
GIVEN {

View File

@ -765,6 +765,7 @@ SINGLE_BATTLE_TEST("(TERA) Stellar type's one-time boost factors in dynamically-
SINGLE_BATTLE_TEST("(TERA) All type indicators function correctly")
{
u32 type;
PARAMETRIZE { type = TYPE_NONE; }
PARAMETRIZE { type = TYPE_NORMAL; }
PARAMETRIZE { type = TYPE_FIGHTING; }
PARAMETRIZE { type = TYPE_FLYING; }

View File

@ -3,4 +3,4 @@
TO_DO_BATTLE_TEST("Aqua Ring recovers 1/16th HP at end of turn");
TO_DO_BATTLE_TEST("Aqua Ring can be used under Heal Block but will not heal the user");
TO_DO_BATTLE_TEST("Aqua Ring can be Baton Passed");
TO_DO_BATTLE_TEST("Baton Pass passes Aqua Ring's effect");

View File

@ -0,0 +1,6 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Baddy Bad sets up Reflect, reducing physical damage");
TO_DO_BATTLE_TEST("Baddy Bad's Reflect lasts for 5 turns");
TO_DO_BATTLE_TEST("Baddy Bad can still damage the target when Reflect is already set up");

View File

@ -0,0 +1,54 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Baton Pass switches out the user");
TO_DO_BATTLE_TEST("Baton Pass fails if there's no valid party Pokémon left");
TO_DO_BATTLE_TEST("Baton Pass passes both positive and negative stat changes");
TO_DO_BATTLE_TEST("AI doesn't choose Baton Pass if the Ace Pokémon is the last one available to switch in");
TO_DO_BATTLE_TEST("Baton Pass doesn't pass non-volatile status conditions"); // Status1
TO_DO_BATTLE_TEST("Baton Pass doesn't pass infatuation");
TO_DO_BATTLE_TEST("Baton Pass doesn't pass type changes");
TO_DO_BATTLE_TEST("Baton Pass doesn't pass ability changes");
//
// Move these to the corresponding effect files.
//
TO_DO_BATTLE_TEST("Baton Pass passes confusion status"); // test/battle/status2/confusion.c
TO_DO_BATTLE_TEST("Baton Pass passes Cursed status"); // test/battle/move_effect/curse.c
TO_DO_BATTLE_TEST("Baton Pass doesn't pass Disable's effect"); // test/battle/move_effect/disable.c
TO_DO_BATTLE_TEST("Baton Pass passes Dragon Cheer's effect"); // test/battle/move_effect/dragon_cheer.c
TO_DO_BATTLE_TEST("Baton Pass passes Fairy lock's escape prevention effect"); // test/battle/move_effect/fairy_lock.c
TO_DO_BATTLE_TEST("Baton Pass passes Focus Energy's effect"); // test/battle/move_effect/focus_energy.c
TO_DO_BATTLE_TEST("Baton Pass passes Heal Block's effect"); // test/battle/move_effect/heal_block.c
TO_DO_BATTLE_TEST("Baton Pass doesn't pass Imprison's effect"); // test/battle/move_effect/imprison.c
TO_DO_BATTLE_TEST("Baton Pass passes Ingrain's effect"); // test/battle/move_effect/ingrain.c
TO_DO_BATTLE_TEST("Baton Pass passes Magnet Rise's effect"); // test/battle/move_effect/magnet_rise.c
TO_DO_BATTLE_TEST("Baton Pass passes escape prevention primary effect if it's used by the target"); // test/battle/move_effect/mean_look.c (Spider Web, Mean Look, Block)
TO_DO_BATTLE_TEST("Baton Pass doesn't pass escape prevention primary effects if it's used by the user"); // test/battle/move_effect/mean_look.c (Spider Web, Mean Look, Block)
TO_DO_BATTLE_TEST("Baton Pass doesn't pass Mimic's learnt move"); // test/battle/move_effect/mimic.c
TO_DO_BATTLE_TEST("Baton Pass passes Minimize's evasion but not the weakness to stomping moves"); // test/battle/move_effect/minimize.c
TO_DO_BATTLE_TEST("Baton Pass passes No Retreat's escape prevention effect"); // test/battle/move_effect/no_retreat.c
TO_DO_BATTLE_TEST("Baton Pass passes Octolock's escape prevention effect"); // test/battle/move_effect/octolock.c
TO_DO_BATTLE_TEST("Baton Pass passes Perish Song's effect"); // test/battle/move_effect/perish_song.c
TO_DO_BATTLE_TEST("Baton Pass passes Power Trick's effect"); // test/battle/move_effect/power_trick.c
TO_DO_BATTLE_TEST("Baton Pass doesn't pass Rollout's multiplier"); // test/battle/move_effect/rollout.c
TO_DO_BATTLE_TEST("Baton Pass passes Sappy Seed's effect"); // test/battle/move_effect/sappy_seed.c
TO_DO_BATTLE_TEST("Baton Pass doesn't pass Transformations"); // test/battle/move_effect/transform.c
TO_DO_BATTLE_TEST("Baton Pass doesn't pass Yawn's effect"); // test/battle/move_effect/yawn.c
TO_DO_BATTLE_TEST("Baton Pass passes Core Enforcer's effect"); // test/battle/move_effect_secondary/core_enforcer.c
TO_DO_BATTLE_TEST("Baton Pass passes escape prevention secondary effect"); // test/battle/move_effect_secondary/prevent_escape.c (Thousand Waves, Spirit Shackle, Anchor Shot)
TO_DO_BATTLE_TEST("Baton Pass doesn't pass escape prevention secondary effect if it's used by the user"); // test/battle/move_effect_secondary/prevent_escape.c (Thousand Waves, Spirit Shackle, Anchor Shot)
// Unconfirmed by Bulbapedia, should be tested in-game:
// - Nightmare
// - Encore
// - Spotlight
// - Taunt
// - Throat Chop
// - Torment
// - Splinters
// - Power Boost
// - Power Drop
// - Guard Boost

View File

@ -111,3 +111,6 @@ SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used")
HP_BAR(opponent);
}
}
TO_DO_BATTLE_TEST("Beak Blast's charging message is shown regardless if it would've missed");
TO_DO_BATTLE_TEST("Bulletproof is immune to Beak Blast but not to the burn it causes");

View File

@ -0,0 +1,31 @@
#include "global.h"
#include "test/battle.h"
// General
TO_DO_BATTLE_TEST("Beat Up hits the target for each non-fainted, non-statused member in the party");
TO_DO_BATTLE_TEST("Beat Up's strikes have each an independent chance of a critical hit");
// B_BEAT_UP Gen2-4
TO_DO_BATTLE_TEST("Beat Up lists each party member's name");
TO_DO_BATTLE_TEST("Beat Up's damage is typeless");
TO_DO_BATTLE_TEST("Beat Up's damage doesn't consider STAB");
TO_DO_BATTLE_TEST("Beat Up's last strike-only can trigger King's Rock");
TO_DO_BATTLE_TEST("Beat Up's base power is the same for each strike");
TO_DO_BATTLE_TEST("Beat Up's damage is determined by each striking Pokémon's base attack and level and the target's defense");
TO_DO_BATTLE_TEST("Beat Up ignores stat stage changes"); //eg. Swords Dance
TO_DO_BATTLE_TEST("Beat Up ignores Huge Power");
TO_DO_BATTLE_TEST("Beat Up ignores Choice Band");
// B_BEAT_UP Gen5+
TO_DO_BATTLE_TEST("Beat Up doesn't list party member's name");
TO_DO_BATTLE_TEST("Beat Up's damage is Dark-typed");
TO_DO_BATTLE_TEST("Beat Up's damage receives STAB");
TO_DO_BATTLE_TEST("Beat Up's can trigger King's Rock on all strikes");
TO_DO_BATTLE_TEST("Beat Up's base power is determined by each striking Pokémon");
TO_DO_BATTLE_TEST("Beat Up's damage is determined by the user's attack and the target's defense");
TO_DO_BATTLE_TEST("Beat Up's damage considers stat stage changes"); //eg. Swords Dance
TO_DO_BATTLE_TEST("Beat Up's damage considers Huge Power");
TO_DO_BATTLE_TEST("Beat Up's damage considers Choice Band");
// Unconfirmed by Bulbapedia
// - Technician interacion

View File

@ -39,6 +39,7 @@ SINGLE_BATTLE_TEST("Belch cannot be used if the user has not eaten a berry")
else {
TURN { MOVE(player, MOVE_STUFF_CHEEKS); }
TURN { MOVE(player, MOVE_BELCH); }
TURN { MOVE(player, MOVE_BELCH); }
}
} SCENE {
if (item == ITEM_NONE) {
@ -47,6 +48,11 @@ SINGLE_BATTLE_TEST("Belch cannot be used if the user has not eaten a berry")
else {
ANIMATION(ANIM_TYPE_MOVE, MOVE_STUFF_CHEEKS, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BELCH, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BELCH, player);
}
}
}
TO_DO_BATTLE_TEST("Belch can still be used after switching out");
TO_DO_BATTLE_TEST("Belch can still be used after fainting");
TO_DO_BATTLE_TEST("Belch can still be used after restoring the consumed berry");

View File

@ -104,3 +104,8 @@ SINGLE_BATTLE_TEST("Belly Drum's HP cost doesn't trigger effects that trigger on
NOT MESSAGE("Wobbuffet's Air Balloon popped!");
}
}
TO_DO_BATTLE_TEST("Belly Drum maximizes the user's Attack stat, even when below 0");
TO_DO_BATTLE_TEST("Belly Drum minimizes the user's Attack stat if it has Contrary"); // Should still say "maximized attack"
TO_DO_BATTLE_TEST("Belly Drum fails if the user's Attack is already at +6, even with Contrary");
TO_DO_BATTLE_TEST("Belly Drum deducts HP if the user has contrary and is at -6");

View File

@ -0,0 +1,10 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Bestow transfers its held item to the target");
TO_DO_BATTLE_TEST("Bestow fails if the user has no held item");
TO_DO_BATTLE_TEST("Bestow fails if the target already has a held item");
TO_DO_BATTLE_TEST("Bestow fails if the target is behind a Substitute");
TO_DO_BATTLE_TEST("Bestow fails if the user is holding Mail");
TO_DO_BATTLE_TEST("Bestow fails if the user's held item changes its form");
TO_DO_BATTLE_TEST("Bestow fails if the user's held item is a Z-Crystal");

View File

@ -32,3 +32,6 @@ SINGLE_BATTLE_TEST("Bide deals twice the taken damage over two turns")
EXPECT_EQ(bideDamage, 2 * (damage1 + damage2));
}
}
TO_DO_BATTLE_TEST("Bide hits the last Pokémon that attacked the user, even allies");
TO_DO_BATTLE_TEST("Bide has +1 priority if called via a different move"); // Gen 5 onwards

View File

@ -0,0 +1,4 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Blizzard ignores accuracy check durin Hail and Snow");

View File

@ -4,6 +4,7 @@
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_BODY_PRESS].effect == EFFECT_BODY_PRESS);
ASSUME(gMovesInfo[MOVE_BODY_PRESS].category == DAMAGE_CATEGORY_PHYSICAL);
}
SINGLE_BATTLE_TEST("Body Press uses physical defense stat of target", s16 damage)
@ -16,6 +17,7 @@ SINGLE_BATTLE_TEST("Body Press uses physical defense stat of target", s16 damage
GIVEN {
ASSUME(gMovesInfo[MOVE_DRILL_PECK].power == gMovesInfo[MOVE_BODY_PRESS].power);
ASSUME(gMovesInfo[MOVE_CHARM].effect == EFFECT_ATTACK_DOWN_2);
ASSUME(gMovesInfo[MOVE_CHARM].effect == EFFECT_ATTACK_DOWN_2);
PLAYER(SPECIES_MEW);
OPPONENT(SPECIES_SHELLDER);
} WHEN {
@ -27,3 +29,13 @@ SINGLE_BATTLE_TEST("Body Press uses physical defense stat of target", s16 damage
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
}
}
TO_DO_BATTLE_TEST("Body Press's damage depends on the user's base Defense instead of its base Attack");
TO_DO_BATTLE_TEST("Body Press's damage depends on the user's Defense stat stages");
// Could be split into multiple tests or maybe to separate files based on the modifier?
TO_DO_BATTLE_TEST("Body Press's damage is influenced by all other Attack modifiers that are not stat stages");
TO_DO_BATTLE_TEST("Body Press's damage is NOT influenced by any other Defense besides stat stages");
// Unconfirmed by Bulbapedia:
// - Defeatist interaction

View File

@ -0,0 +1,5 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Bolt Beak's base power is doubled if it attacks before the target");
TO_DO_BATTLE_TEST("Bolt Beak's base power is doubled the target switches out");

View File

@ -137,24 +137,3 @@ DOUBLE_BATTLE_TEST("Brick Break can remove Light Screen, Reflect and Aurora Veil
HP_BAR(playerLeft);
}
}
SINGLE_BATTLE_TEST("Move Raging Bull changes it's type depending on the Tauros Form")
{
u16 speciesPlayer;
u16 speciesOpponent;
PARAMETRIZE { speciesPlayer = SPECIES_TAUROS_PALDEAN_COMBAT_BREED; speciesOpponent = SPECIES_CHARIZARD; }
PARAMETRIZE { speciesPlayer = SPECIES_TAUROS_PALDEAN_BLAZE_BREED; speciesOpponent = SPECIES_BLASTOISE; }
PARAMETRIZE { speciesPlayer = SPECIES_TAUROS_PALDEAN_AQUA_BREED; speciesOpponent = SPECIES_VENUSAUR; }
GIVEN {
PLAYER(speciesPlayer);
OPPONENT(speciesOpponent);
} WHEN {
TURN { MOVE(player, MOVE_RAGING_BULL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGING_BULL, player);
HP_BAR(opponent);
MESSAGE("It's not very effective…");
}
}

View File

@ -0,0 +1,4 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Brine's power doubles if the target is at 50% or below max HP");

View File

@ -0,0 +1,4 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Bulk Up increases the user's Attack and Defense");

View File

@ -0,0 +1,28 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Earthquake's and Bulldoze's damage is halved when Grassy Terrain is in effect", s16 damage)
{
bool32 terrain;
u16 move;
PARAMETRIZE { terrain = FALSE; move = MOVE_EARTHQUAKE; } // 0
PARAMETRIZE { terrain = TRUE; move = MOVE_EARTHQUAKE; } // 1
PARAMETRIZE { terrain = FALSE; move = MOVE_BULLDOZE; } // 2
PARAMETRIZE { terrain = TRUE; move = MOVE_BULLDOZE; } // 3
GIVEN {
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].effect == EFFECT_EARTHQUAKE);
ASSUME(gMovesInfo[MOVE_BULLDOZE].effect == EFFECT_EARTHQUAKE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (terrain)
TURN { MOVE(player, MOVE_GRASSY_TERRAIN); }
TURN { MOVE(player, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
EXPECT_MUL_EQ(results[2].damage, Q_4_12(0.5), results[3].damage);
}
}

View File

@ -203,7 +203,7 @@ SINGLE_BATTLE_TEST("Embargo doesn't stop an item flung at an affected target fro
}
}
SINGLE_BATTLE_TEST("Embargo is passed via Baton Pass")
SINGLE_BATTLE_TEST("Baton Pass passes Embargo's effect")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);

View File

@ -39,3 +39,6 @@ SINGLE_BATTLE_TEST("Gastro Acid fails if target has a banned ability")
MESSAGE("But it failed!");
}
}
TO_DO_BATTLE_TEST("Baton Pass passes Gastro Acid's effect");
TO_DO_BATTLE_TEST("Baton Pass removes Gastro Acid if its ability cannot be surpressed");

View File

@ -0,0 +1,106 @@
#include "global.h"
#include "test/battle.h"
// IV combinations sourced from https://www.smogon.com/forums/threads/hidden-power-iv-combinations.78083/
SINGLE_BATTLE_TEST("Hidden Power's type is determined by IVs")
{
u32 type, j, foeType, foeSpecies;
u32 hp, atk, def, spAtk, spDef, speed;
bool32 hidden;
PARAMETRIZE { type = TYPE_NONE; hidden = FALSE; }
PARAMETRIZE { type = TYPE_NORMAL; hidden = FALSE; }
PARAMETRIZE { type = TYPE_FIGHTING; hidden = TRUE; foeType = TYPE_PSYCHIC; foeSpecies = SPECIES_WOBBUFFET; hp = 30; atk = 2; def = 31; spAtk = 30; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_FIGHTING; hidden = TRUE; foeType = TYPE_PSYCHIC; foeSpecies = SPECIES_WOBBUFFET; hp = 31; atk = 15; def = 30; spAtk = 30; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_FIGHTING; hidden = TRUE; foeType = TYPE_PSYCHIC; foeSpecies = SPECIES_WOBBUFFET; hp = 30; atk = 22; def = 31; spAtk = 30; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_FIGHTING; hidden = TRUE; foeType = TYPE_PSYCHIC; foeSpecies = SPECIES_WOBBUFFET; hp = 31; atk = 31; def = 30; spAtk = 30; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_FLYING; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_REGISTEEL; hp = 31; atk = 2; def = 31; spAtk = 30; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_FLYING; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_REGISTEEL; hp = 31; atk = 15; def = 31; spAtk = 30; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_FLYING; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_REGISTEEL; hp = 31; atk = 22; def = 31; spAtk = 30; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_FLYING; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_REGISTEEL; hp = 31; atk = 31; def = 31; spAtk = 30; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_POISON; hidden = TRUE; foeType = TYPE_POISON; foeSpecies = SPECIES_ARBOK; hp = 30; atk = 2; def = 31; spAtk = 30; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_POISON; hidden = TRUE; foeType = TYPE_POISON; foeSpecies = SPECIES_ARBOK; hp = 31; atk = 15; def = 30; spAtk = 30; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_POISON; hidden = TRUE; foeType = TYPE_POISON; foeSpecies = SPECIES_ARBOK; hp = 30; atk = 22; def = 31; spAtk = 30; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_POISON; hidden = TRUE; foeType = TYPE_POISON; foeSpecies = SPECIES_ARBOK; hp = 31; atk = 31; def = 30; spAtk = 30; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_GROUND; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 31; atk = 2; def = 31; spAtk = 30; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_GROUND; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 31; atk = 15; def = 31; spAtk = 30; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_GROUND; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 31; atk = 22; def = 31; spAtk = 30; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_GROUND; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 31; atk = 31; def = 31; spAtk = 30; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_ROCK; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 2; def = 30; spAtk = 31; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_ROCK; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 15; def = 30; spAtk = 31; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_ROCK; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 22; def = 30; spAtk = 31; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_ROCK; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 31; def = 30; spAtk = 31; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_BUG; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 2; def = 31; spAtk = 31; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_BUG; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 15; def = 31; spAtk = 31; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_BUG; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 22; def = 31; spAtk = 31; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_BUG; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 31; def = 31; spAtk = 31; spDef = 30; speed = 30; }
PARAMETRIZE { type = TYPE_GHOST; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 2; def = 31; spAtk = 31; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_GHOST; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 15; def = 30; spAtk = 31; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_GHOST; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 22; def = 31; spAtk = 31; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_GHOST; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 31; def = 30; spAtk = 31; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_STEEL; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 2; def = 30; spAtk = 30; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_STEEL; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 15; def = 31; spAtk = 31; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_STEEL; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 22; def = 30; spAtk = 30; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_STEEL; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 31; def = 31; spAtk = 31; spDef = 30; speed = 31; }
PARAMETRIZE { type = TYPE_FIRE; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 2; def = 31; spAtk = 30; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_FIRE; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 15; def = 30; spAtk = 30; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_FIRE; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 22; def = 31; spAtk = 30; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_FIRE; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 31; def = 30; spAtk = 30; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_WATER; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 2; def = 30; spAtk = 30; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_WATER; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 15; def = 31; spAtk = 30; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_WATER; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 22; def = 30; spAtk = 30; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_WATER; hidden = TRUE; foeType = TYPE_WATER; foeSpecies = SPECIES_BLASTOISE; hp = 31; atk = 31; def = 31; spAtk = 30; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_GRASS; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 2; def = 31; spAtk = 30; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_GRASS; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 15; def = 31; spAtk = 30; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_GRASS; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 22; def = 31; spAtk = 30; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_GRASS; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 31; def = 31; spAtk = 30; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_ELECTRIC; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 31; atk = 2; def = 30; spAtk = 31; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_ELECTRIC; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 30; atk = 15; def = 30; spAtk = 31; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_ELECTRIC; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 31; atk = 22; def = 30; spAtk = 31; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_ELECTRIC; hidden = TRUE; foeType = TYPE_GRASS; foeSpecies = SPECIES_TANGELA; hp = 30; atk = 31; def = 30; spAtk = 31; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_PSYCHIC; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 2; def = 31; spAtk = 31; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_PSYCHIC; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 15; def = 31; spAtk = 31; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_PSYCHIC; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 31; atk = 22; def = 31; spAtk = 31; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_PSYCHIC; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 31; def = 31; spAtk = 31; spDef = 31; speed = 30; }
PARAMETRIZE { type = TYPE_ICE; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 2; def = 30; spAtk = 31; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_ICE; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 15; def = 30; spAtk = 31; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_ICE; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 22; def = 30; spAtk = 31; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_ICE; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 31; def = 30; spAtk = 31; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_MYSTERY; hidden = FALSE; }
PARAMETRIZE { type = TYPE_DRAGON; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 2; def = 31; spAtk = 31; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_DRAGON; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 15; def = 31; spAtk = 31; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_DRAGON; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 22; def = 31; spAtk = 31; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_DRAGON; hidden = TRUE; foeType = TYPE_STEEL; foeSpecies = SPECIES_KLINK; hp = 30; atk = 31; def = 31; spAtk = 31; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_DARK; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 3; def = 31; spAtk = 31; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_DARK; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 15; def = 31; spAtk = 31; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_DARK; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 23; def = 31; spAtk = 31; spDef = 31; speed = 31; }
PARAMETRIZE { type = TYPE_DARK; hidden = TRUE; foeType = TYPE_DARK; foeSpecies = SPECIES_UMBREON; hp = 31; atk = 31; def = 31; spAtk = 31; spDef = 31; speed = 31; }
// Any type after Dark shouldn't be part of Hidden Power officially.
for (j = TYPE_DARK + 1; j < NUMBER_OF_MON_TYPES; j++) {
PARAMETRIZE { type = j; hidden = FALSE; }
}
GIVEN {
if (hidden) {
ASSUME(gTypeEffectivenessTable[type][foeType] == UQ_4_12(0.5)); // Foe's Type resists
ASSUME(gSpeciesInfo[foeSpecies].types[0] == gSpeciesInfo[foeSpecies].types[1]); // Foe's pure type
ASSUME(gSpeciesInfo[foeSpecies].types[0] == foeType); // Foe is the resisted type
PLAYER(SPECIES_DUNSPARCE) { HPIV(hp); AttackIV(atk); DefenseIV(def); SpAttackIV(spAtk); SpDefenseIV(spDef); SpeedIV(speed); }
} else {
PLAYER(SPECIES_DUNSPARCE);
}
OPPONENT(foeSpecies);
} WHEN {
TURN { MOVE(player, MOVE_HIDDEN_POWER); }
} SCENE {
// Only test valid Hidden Power types
if (hidden) {
ANIMATION(ANIM_TYPE_MOVE, MOVE_HIDDEN_POWER, player);
HP_BAR(opponent);
MESSAGE("It's not very effective…");
}
}
}
TO_DO_BATTLE_TEST("Hidden Power's power is determined by IVs before Gen6");

View File

@ -24,3 +24,4 @@ TO_DO_BATTLE_TEST("Leech Seed doesn't affect already seeded targets")
TO_DO_BATTLE_TEST("Leech Seeded targets lose 1/8 of its max HP every turn and give it to the user")
TO_DO_BATTLE_TEST("Leech Seed's effect is paused until a new battler replaces the original user's position") // Faint, can't be replaced, then revived.
TO_DO_BATTLE_TEST("Leech Seed's effect pause still prevents it from being seeded again")
TO_DO_BATTLE_TEST("Baton Pass passes Leech Seed's effect");

View File

@ -24,3 +24,5 @@ SINGLE_BATTLE_TEST("Pursuited mon correctly switches out after it got hit and ac
MESSAGE("Go! Wobbuffet!");
}
}
TO_DO_BATTLE_TEST("Baton Pass doesn't cause Pursuit to increase its power or priority");

View File

@ -0,0 +1,160 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_RAGING_BULL].effect == EFFECT_RAGING_BULL);
ASSUME(gMovesInfo[MOVE_SNOWSCAPE].effect == EFFECT_SNOWSCAPE);
ASSUME(gMovesInfo[MOVE_LIGHT_SCREEN].effect == EFFECT_LIGHT_SCREEN);
ASSUME(gMovesInfo[MOVE_REFLECT].effect == EFFECT_REFLECT);
ASSUME(gMovesInfo[MOVE_AURORA_VEIL].effect == EFFECT_AURORA_VEIL);
}
SINGLE_BATTLE_TEST("Raging Bull removes Light Screen, Reflect and Aurora Veil from the target's side of the field")
{
u16 move;
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; }
PARAMETRIZE { move = MOVE_REFLECT; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SNOWSCAPE); }
TURN { MOVE(opponent, move); MOVE(player, MOVE_RAGING_BULL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGING_BULL, player);
MESSAGE("The wall shattered!");
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Raging Bull doesn't remove Light Screen, Reflect and Aurora Veil if the target is immune")
{
u16 move;
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; }
PARAMETRIZE { move = MOVE_REFLECT; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; }
KNOWN_FAILING;
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GASTLY);
} WHEN {
TURN { MOVE(player, MOVE_SNOWSCAPE); }
TURN { MOVE(opponent, move); MOVE(player, MOVE_RAGING_BULL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGING_BULL, player);
MESSAGE("The wall shattered!");
HP_BAR(opponent);
}
}
}
SINGLE_BATTLE_TEST("Raging Bull doesn't remove Light Screen, Reflect and Aurora Veil if the target Protected")
{
u16 move;
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; }
PARAMETRIZE { move = MOVE_REFLECT; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SNOWSCAPE); MOVE(opponent, move); }
TURN { MOVE(player, MOVE_RAGING_BULL); MOVE(opponent, MOVE_PROTECT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGING_BULL, player);
MESSAGE("The wall shattered!");
HP_BAR(opponent);
}
}
}
SINGLE_BATTLE_TEST("Raging Bull doesn't remove Light Screen, Reflect and Aurora Veil if it misses")
{
u16 move;
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; }
PARAMETRIZE { move = MOVE_REFLECT; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_BRIGHT_POWDER); }
} WHEN {
TURN { MOVE(player, MOVE_SNOWSCAPE); MOVE(opponent, move); }
TURN { MOVE(player, MOVE_RAGING_BULL, hit: FALSE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGING_BULL, player);
MESSAGE("The wall shattered!");
HP_BAR(opponent);
}
}
}
DOUBLE_BATTLE_TEST("Raging Bull can remove Light Screen, Reflect and Aurora Veil on users side")
{
u16 move;
PARAMETRIZE { move = MOVE_LIGHT_SCREEN; }
PARAMETRIZE { move = MOVE_REFLECT; }
PARAMETRIZE { move = MOVE_AURORA_VEIL; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(opponentLeft, MOVE_SNOWSCAPE);
MOVE(playerLeft, move);
MOVE(playerRight, MOVE_RAGING_BULL, target: playerLeft);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SNOWSCAPE, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, move, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGING_BULL, playerRight);
MESSAGE("The wall shattered!");
HP_BAR(playerLeft);
}
}
SINGLE_BATTLE_TEST("Move Raging Bull changes it's type depending on the Tauros Form")
{
u16 speciesPlayer;
u16 speciesOpponent;
PARAMETRIZE { speciesPlayer = SPECIES_TAUROS_PALDEAN_COMBAT_BREED; speciesOpponent = SPECIES_CHARIZARD; }
PARAMETRIZE { speciesPlayer = SPECIES_TAUROS_PALDEAN_BLAZE_BREED; speciesOpponent = SPECIES_BLASTOISE; }
PARAMETRIZE { speciesPlayer = SPECIES_TAUROS_PALDEAN_AQUA_BREED; speciesOpponent = SPECIES_VENUSAUR; }
GIVEN {
PLAYER(speciesPlayer);
OPPONENT(speciesOpponent);
} WHEN {
TURN { MOVE(player, MOVE_RAGING_BULL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAGING_BULL, player);
HP_BAR(opponent);
MESSAGE("It's not very effective…");
}
}

View File

@ -68,3 +68,5 @@ SINGLE_BATTLE_TEST("Substitute's HP cost doesn't trigger effects that trigger on
NOT MESSAGE("Wobbuffet's Air Balloon popped!");
}
}
TO_DO_BATTLE_TEST("Baton Pass passes Substitutes");

View File

@ -68,3 +68,7 @@ SINGLE_BATTLE_TEST("Telekinesis makes the target immune to Ground-type attacks")
MESSAGE("It doesn't affect Foe Wynaut…");
}
}
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");

View File

@ -21,6 +21,57 @@ SINGLE_BATTLE_TEST("Tera Blast changes from Normal-type to the user's Tera Type"
}
}
SINGLE_BATTLE_TEST("Tera Blast has correct effectiveness for every Tera Type")
{
u32 species;
u32 type;
PARAMETRIZE { species = SPECIES_CHIKORITA; type = TYPE_FLYING; }
PARAMETRIZE { species = SPECIES_CHIKORITA; type = TYPE_POISON; }
PARAMETRIZE { species = SPECIES_CHIKORITA; type = TYPE_FIRE; }
PARAMETRIZE { species = SPECIES_CHIKORITA; type = TYPE_BUG; }
PARAMETRIZE { species = SPECIES_CHIKORITA; type = TYPE_ICE; }
PARAMETRIZE { species = SPECIES_CYNDAQUIL; type = TYPE_GROUND; }
PARAMETRIZE { species = SPECIES_CYNDAQUIL; type = TYPE_ROCK; }
PARAMETRIZE { species = SPECIES_CYNDAQUIL; type = TYPE_WATER; }
PARAMETRIZE { species = SPECIES_GASTLY; type = TYPE_NORMAL; }
PARAMETRIZE { species = SPECIES_GASTLY; type = TYPE_GHOST; }
PARAMETRIZE { species = SPECIES_GASTLY; type = TYPE_PSYCHIC; }
PARAMETRIZE { species = SPECIES_TOTODILE; type = TYPE_GRASS; }
PARAMETRIZE { species = SPECIES_TOTODILE; type = TYPE_ELECTRIC; }
PARAMETRIZE { species = SPECIES_DRATINI; type = TYPE_DRAGON; }
PARAMETRIZE { species = SPECIES_DRATINI; type = TYPE_FAIRY; }
PARAMETRIZE { species = SPECIES_SNEASEL; type = TYPE_FIGHTING; }
PARAMETRIZE { species = SPECIES_SNEASEL; type = TYPE_STEEL; }
PARAMETRIZE { species = SPECIES_ABRA; type = TYPE_DARK; }
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_CHIKORITA].types[0] == TYPE_GRASS);
ASSUME(gSpeciesInfo[SPECIES_CHIKORITA].types[1] == TYPE_GRASS);
ASSUME(gSpeciesInfo[SPECIES_CYNDAQUIL].types[0] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_CYNDAQUIL].types[1] == TYPE_FIRE);
ASSUME(gSpeciesInfo[SPECIES_GASTLY].types[0] == TYPE_GHOST);
ASSUME(gSpeciesInfo[SPECIES_GASTLY].types[1] == TYPE_POISON);
ASSUME(gSpeciesInfo[SPECIES_TOTODILE].types[0] == TYPE_WATER);
ASSUME(gSpeciesInfo[SPECIES_TOTODILE].types[1] == TYPE_WATER);
ASSUME(gSpeciesInfo[SPECIES_DRATINI].types[0] == TYPE_DRAGON);
ASSUME(gSpeciesInfo[SPECIES_DRATINI].types[1] == TYPE_DRAGON);
ASSUME(gSpeciesInfo[SPECIES_SNEASEL].types[0] == TYPE_DARK);
ASSUME(gSpeciesInfo[SPECIES_SNEASEL].types[1] == TYPE_ICE);
ASSUME(gSpeciesInfo[SPECIES_ABRA].types[0] == TYPE_PSYCHIC);
ASSUME(gSpeciesInfo[SPECIES_ABRA].types[1] == TYPE_PSYCHIC);
PLAYER(SPECIES_WOBBUFFET) { TeraType(type); }
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); }
} SCENE {
if (species == SPECIES_GASTLY && type == TYPE_NORMAL)
MESSAGE("It doesn't affect Foe Gastly…");
else
MESSAGE("It's super effective!");
}
}
SINGLE_BATTLE_TEST("Tera Blast becomes a physical move if the user is Terastallized and has a higher Attack stat", s16 damage)
{
bool32 tera;

View File

@ -57,33 +57,6 @@ SINGLE_BATTLE_TEST("Grassy Terrain increases power of Grass-type moves by 30/50
}
}
// Magnitude is not tested because its damage is variable.
SINGLE_BATTLE_TEST("Grassy Terrain decreases power of Earthquake and Bulldoze by 50 percent", s16 damage)
{
bool32 terrain;
u16 move;
PARAMETRIZE { terrain = FALSE; move = MOVE_EARTHQUAKE; } // 0
PARAMETRIZE { terrain = TRUE; move = MOVE_EARTHQUAKE; } // 1
PARAMETRIZE { terrain = FALSE; move = MOVE_BULLDOZE; } // 2
PARAMETRIZE { terrain = TRUE; move = MOVE_BULLDOZE; } // 3
GIVEN {
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].effect == EFFECT_EARTHQUAKE);
ASSUME(gMovesInfo[MOVE_BULLDOZE].effect == EFFECT_EARTHQUAKE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (terrain)
TURN { MOVE(player, MOVE_GRASSY_TERRAIN); }
TURN { MOVE(player, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
EXPECT_MUL_EQ(results[2].damage, Q_4_12(0.5), results[3].damage);
}
}
SINGLE_BATTLE_TEST("Grassy Terrain lasts for 5 turns")
{
GIVEN {

Some files were not shown because too many files have changed in this diff Show More