* Created TrainerClass struct * Added money multiplier to TrainerClass struct * Added Poke Balls to struct * Condensed gTrainerClasses * Simplified trainer Poke Balls * Moved trainer classes into battle_main.c * Removed complicated ball macro --------- Co-authored-by: Bassoonian <iasperbassoonian@gmail.com> Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
16580 lines
596 KiB
C
16580 lines
596 KiB
C
#include "global.h"
|
||
#include "battle.h"
|
||
#include "battle_message.h"
|
||
#include "battle_anim.h"
|
||
#include "battle_ai_main.h"
|
||
#include "battle_ai_util.h"
|
||
#include "battle_scripts.h"
|
||
#include "battle_z_move.h"
|
||
#include "constants/moves.h"
|
||
#include "constants/abilities.h"
|
||
#include "item.h"
|
||
#include "util.h"
|
||
#include "pokemon.h"
|
||
#include "random.h"
|
||
#include "battle_controllers.h"
|
||
#include "battle_interface.h"
|
||
#include "text.h"
|
||
#include "sound.h"
|
||
#include "pokedex.h"
|
||
#include "recorded_battle.h"
|
||
#include "window.h"
|
||
#include "reshow_battle_screen.h"
|
||
#include "main.h"
|
||
#include "palette.h"
|
||
#include "money.h"
|
||
#include "malloc.h"
|
||
#include "bg.h"
|
||
#include "string_util.h"
|
||
#include "pokemon_icon.h"
|
||
#include "m4a.h"
|
||
#include "mail.h"
|
||
#include "event_data.h"
|
||
#include "pokemon_storage_system.h"
|
||
#include "task.h"
|
||
#include "naming_screen.h"
|
||
#include "battle_setup.h"
|
||
#include "overworld.h"
|
||
#include "wild_encounter.h"
|
||
#include "rtc.h"
|
||
#include "party_menu.h"
|
||
#include "battle_arena.h"
|
||
#include "battle_pike.h"
|
||
#include "battle_pyramid.h"
|
||
#include "field_specials.h"
|
||
#include "pokemon_summary_screen.h"
|
||
#include "pokenav.h"
|
||
#include "menu_specialized.h"
|
||
#include "data.h"
|
||
#include "constants/abilities.h"
|
||
#include "constants/battle_anim.h"
|
||
#include "constants/battle_move_effects.h"
|
||
#include "constants/battle_string_ids.h"
|
||
#include "constants/battle_partner.h"
|
||
#include "constants/hold_effects.h"
|
||
#include "constants/items.h"
|
||
#include "constants/item_effects.h"
|
||
#include "constants/map_types.h"
|
||
#include "constants/moves.h"
|
||
#include "constants/party_menu.h"
|
||
#include "constants/rgb.h"
|
||
#include "constants/songs.h"
|
||
#include "constants/trainers.h"
|
||
#include "battle_util.h"
|
||
#include "constants/pokemon.h"
|
||
#include "config/battle.h"
|
||
|
||
// Helper for accessing command arguments and advancing gBattlescriptCurrInstr.
|
||
//
|
||
// For example accuracycheck is defined as:
|
||
//
|
||
// .macro accuracycheck failInstr:req, move:req
|
||
// .byte 0x1
|
||
// .4byte \failInstr
|
||
// .2byte \move
|
||
// .endm
|
||
//
|
||
// Which corresponds to:
|
||
//
|
||
// CMD_ARGS(const u8 *failInstr, u16 move);
|
||
//
|
||
// The arguments can be accessed as cmd->failInstr and cmd->move.
|
||
// gBattlescriptCurrInstr = cmd->nextInstr; advances to the next instruction.
|
||
#define CMD_ARGS(...) const struct __attribute__((packed)) { u8 opcode; MEMBERS(__VA_ARGS__) const u8 nextInstr[0]; } *const cmd UNUSED = (const void *)gBattlescriptCurrInstr
|
||
#define VARIOUS_ARGS(...) CMD_ARGS(u8 battler, u8 id, ##__VA_ARGS__)
|
||
#define NATIVE_ARGS(...) CMD_ARGS(void (*func)(void), ##__VA_ARGS__)
|
||
|
||
#define MEMBERS(...) VARARG_8(MEMBERS_, __VA_ARGS__)
|
||
#define MEMBERS_0()
|
||
#define MEMBERS_1(a) a;
|
||
#define MEMBERS_2(a, b) a; b;
|
||
#define MEMBERS_3(a, b, c) a; b; c;
|
||
#define MEMBERS_4(a, b, c, d) a; b; c; d;
|
||
#define MEMBERS_5(a, b, c, d, e) a; b; c; d; e;
|
||
#define MEMBERS_6(a, b, c, d, e, f) a; b; c; d; e; f;
|
||
#define MEMBERS_7(a, b, c, d, e, f, g) a; b; c; d; e; f; g;
|
||
#define MEMBERS_8(a, b, c, d, e, f, g, h) a; b; c; d; e; f; g; h;
|
||
|
||
extern const u8 *const gBattleScriptsForMoveEffects[];
|
||
|
||
// table to avoid ugly powing on gba (courtesy of doesnt)
|
||
// this returns (i^2.5)/4
|
||
// the quarters cancel so no need to re-quadruple them in actual calculation
|
||
static const s32 sExperienceScalingFactors[] =
|
||
{
|
||
0,
|
||
0,
|
||
1,
|
||
3,
|
||
8,
|
||
13,
|
||
22,
|
||
32,
|
||
45,
|
||
60,
|
||
79,
|
||
100,
|
||
124,
|
||
152,
|
||
183,
|
||
217,
|
||
256,
|
||
297,
|
||
343,
|
||
393,
|
||
447,
|
||
505,
|
||
567,
|
||
634,
|
||
705,
|
||
781,
|
||
861,
|
||
946,
|
||
1037,
|
||
1132,
|
||
1232,
|
||
1337,
|
||
1448,
|
||
1563,
|
||
1685,
|
||
1811,
|
||
1944,
|
||
2081,
|
||
2225,
|
||
2374,
|
||
2529,
|
||
2690,
|
||
2858,
|
||
3031,
|
||
3210,
|
||
3396,
|
||
3587,
|
||
3786,
|
||
3990,
|
||
4201,
|
||
4419,
|
||
4643,
|
||
4874,
|
||
5112,
|
||
5357,
|
||
5608,
|
||
5866,
|
||
6132,
|
||
6404,
|
||
6684,
|
||
6971,
|
||
7265,
|
||
7566,
|
||
7875,
|
||
8192,
|
||
8515,
|
||
8847,
|
||
9186,
|
||
9532,
|
||
9886,
|
||
10249,
|
||
10619,
|
||
10996,
|
||
11382,
|
||
11776,
|
||
12178,
|
||
12588,
|
||
13006,
|
||
13433,
|
||
13867,
|
||
14310,
|
||
14762,
|
||
15222,
|
||
15690,
|
||
16167,
|
||
16652,
|
||
17146,
|
||
17649,
|
||
18161,
|
||
18681,
|
||
19210,
|
||
19748,
|
||
20295,
|
||
20851,
|
||
21417,
|
||
21991,
|
||
22574,
|
||
23166,
|
||
23768,
|
||
24379,
|
||
25000,
|
||
25629,
|
||
26268,
|
||
26917,
|
||
27575,
|
||
28243,
|
||
28920,
|
||
29607,
|
||
30303,
|
||
31010,
|
||
31726,
|
||
32452,
|
||
33188,
|
||
33934,
|
||
34689,
|
||
35455,
|
||
36231,
|
||
37017,
|
||
37813,
|
||
38619,
|
||
39436,
|
||
40262,
|
||
41099,
|
||
41947,
|
||
42804,
|
||
43673,
|
||
44551,
|
||
45441,
|
||
46340,
|
||
47251,
|
||
48172,
|
||
49104,
|
||
50046,
|
||
50999,
|
||
51963,
|
||
52938,
|
||
53924,
|
||
54921,
|
||
55929,
|
||
56947,
|
||
57977,
|
||
59018,
|
||
60070,
|
||
61133,
|
||
62208,
|
||
63293,
|
||
64390,
|
||
65498,
|
||
66618,
|
||
67749,
|
||
68891,
|
||
70045,
|
||
71211,
|
||
72388,
|
||
73576,
|
||
74777,
|
||
75989,
|
||
77212,
|
||
78448,
|
||
79695,
|
||
80954,
|
||
82225,
|
||
83507,
|
||
84802,
|
||
86109,
|
||
87427,
|
||
88758,
|
||
90101,
|
||
91456,
|
||
92823,
|
||
94202,
|
||
95593,
|
||
96997,
|
||
98413,
|
||
99841,
|
||
101282,
|
||
102735,
|
||
104201,
|
||
105679,
|
||
107169,
|
||
108672,
|
||
110188,
|
||
111716,
|
||
113257,
|
||
114811,
|
||
116377,
|
||
117956,
|
||
119548,
|
||
121153,
|
||
122770,
|
||
124401,
|
||
126044,
|
||
127700,
|
||
129369,
|
||
131052,
|
||
132747,
|
||
134456,
|
||
136177,
|
||
137912,
|
||
139660,
|
||
141421,
|
||
143195,
|
||
144983,
|
||
146784,
|
||
148598,
|
||
150426,
|
||
152267,
|
||
154122,
|
||
155990,
|
||
157872,
|
||
159767,
|
||
};
|
||
|
||
static const u16 sTrappingMoves[NUM_TRAPPING_MOVES] =
|
||
{
|
||
MOVE_BIND, MOVE_WRAP, MOVE_FIRE_SPIN, MOVE_CLAMP, MOVE_WHIRLPOOL, MOVE_SAND_TOMB, MOVE_MAGMA_STORM, MOVE_INFESTATION, MOVE_SNAP_TRAP,
|
||
};
|
||
|
||
static const u16 sBadgeFlags[8] = {
|
||
FLAG_BADGE01_GET, FLAG_BADGE02_GET, FLAG_BADGE03_GET, FLAG_BADGE04_GET,
|
||
FLAG_BADGE05_GET, FLAG_BADGE06_GET, FLAG_BADGE07_GET, FLAG_BADGE08_GET,
|
||
};
|
||
|
||
static const u16 sWhiteOutBadgeMoney[9] = { 8, 16, 24, 36, 48, 64, 80, 100, 120 };
|
||
|
||
#define STAT_CHANGE_WORKED 0
|
||
#define STAT_CHANGE_DIDNT_WORK 1
|
||
|
||
#define LEVEL_UP_BANNER_START 416
|
||
#define LEVEL_UP_BANNER_END 512
|
||
|
||
#define TAG_LVLUP_BANNER_MON_ICON 55130
|
||
|
||
static void TrySetDestinyBondToHappen(void);
|
||
static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr);
|
||
static bool32 IsMonGettingExpSentOut(void);
|
||
static void InitLevelUpBanner(void);
|
||
static bool8 SlideInLevelUpBanner(void);
|
||
static bool8 SlideOutLevelUpBanner(void);
|
||
static void DrawLevelUpWindow1(void);
|
||
static void DrawLevelUpWindow2(void);
|
||
static void PutMonIconOnLvlUpBanner(void);
|
||
static void DrawLevelUpBannerText(void);
|
||
static void SpriteCB_MonIconOnLvlUpBanner(struct Sprite *sprite);
|
||
static bool32 CriticalCapture(u32 odds);
|
||
static void BestowItem(u32 battlerAtk, u32 battlerDef);
|
||
static bool8 IsFinalStrikeEffect(u16 move);
|
||
static void TryUpdateRoundTurnOrder(void);
|
||
static bool32 ChangeOrderTargetAfterAttacker(void);
|
||
void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 faintedBattler);
|
||
static void RemoveAllTerrains(void);
|
||
static bool8 CanAbilityPreventStatLoss(u16 abilityDef, bool8 isIntimidate);
|
||
|
||
static void Cmd_attackcanceler(void);
|
||
static void Cmd_accuracycheck(void);
|
||
static void Cmd_attackstring(void);
|
||
static void Cmd_ppreduce(void);
|
||
static void Cmd_critcalc(void);
|
||
static void Cmd_damagecalc(void);
|
||
static void Cmd_typecalc(void);
|
||
static void Cmd_adjustdamage(void);
|
||
static void Cmd_multihitresultmessage(void);
|
||
static void Cmd_attackanimation(void);
|
||
static void Cmd_waitanimation(void);
|
||
static void Cmd_healthbarupdate(void);
|
||
static void Cmd_datahpupdate(void);
|
||
static void Cmd_critmessage(void);
|
||
static void Cmd_effectivenesssound(void);
|
||
static void Cmd_resultmessage(void);
|
||
static void Cmd_printstring(void);
|
||
static void Cmd_printselectionstring(void);
|
||
static void Cmd_waitmessage(void);
|
||
static void Cmd_printfromtable(void);
|
||
static void Cmd_printselectionstringfromtable(void);
|
||
static void Cmd_seteffectwithchance(void);
|
||
static void Cmd_seteffectprimary(void);
|
||
static void Cmd_seteffectsecondary(void);
|
||
static void Cmd_clearstatusfromeffect(void);
|
||
static void Cmd_tryfaintmon(void);
|
||
static void Cmd_dofaintanimation(void);
|
||
static void Cmd_cleareffectsonfaint(void);
|
||
static void Cmd_jumpifstatus(void);
|
||
static void Cmd_jumpifstatus2(void);
|
||
static void Cmd_jumpifability(void);
|
||
static void Cmd_jumpifsideaffecting(void);
|
||
static void Cmd_jumpifstat(void);
|
||
static void Cmd_jumpifstatus3condition(void);
|
||
static void Cmd_jumpbasedontype(void);
|
||
static void Cmd_getexp(void);
|
||
static void Cmd_checkteamslost(void);
|
||
static void Cmd_movevaluescleanup(void);
|
||
static void Cmd_setmultihit(void);
|
||
static void Cmd_decrementmultihit(void);
|
||
static void Cmd_goto(void);
|
||
static void Cmd_jumpifbyte(void);
|
||
static void Cmd_jumpifhalfword(void);
|
||
static void Cmd_jumpifword(void);
|
||
static void Cmd_jumpifarrayequal(void);
|
||
static void Cmd_jumpifarraynotequal(void);
|
||
static void Cmd_setbyte(void);
|
||
static void Cmd_addbyte(void);
|
||
static void Cmd_subbyte(void);
|
||
static void Cmd_copyarray(void);
|
||
static void Cmd_copyarraywithindex(void);
|
||
static void Cmd_orbyte(void);
|
||
static void Cmd_orhalfword(void);
|
||
static void Cmd_orword(void);
|
||
static void Cmd_bicbyte(void);
|
||
static void Cmd_bichalfword(void);
|
||
static void Cmd_bicword(void);
|
||
static void Cmd_pause(void);
|
||
static void Cmd_waitstate(void);
|
||
static void Cmd_healthbar_update(void);
|
||
static void Cmd_return(void);
|
||
static void Cmd_end(void);
|
||
static void Cmd_end2(void);
|
||
static void Cmd_end3(void);
|
||
static void Cmd_unused5(void);
|
||
static void Cmd_call(void);
|
||
static void Cmd_setroost(void);
|
||
static void Cmd_jumpifabilitypresent(void);
|
||
static void Cmd_endselectionscript(void);
|
||
static void Cmd_playanimation(void);
|
||
static void Cmd_playanimation_var(void);
|
||
static void Cmd_setgraphicalstatchangevalues(void);
|
||
static void Cmd_playstatchangeanimation(void);
|
||
static void Cmd_moveend(void);
|
||
static void Cmd_sethealblock(void);
|
||
static void Cmd_returnatktoball(void);
|
||
static void Cmd_getswitchedmondata(void);
|
||
static void Cmd_switchindataupdate(void);
|
||
static void Cmd_switchinanim(void);
|
||
static void Cmd_jumpifcantswitch(void);
|
||
static void Cmd_openpartyscreen(void);
|
||
static void Cmd_switchhandleorder(void);
|
||
static void Cmd_switchineffects(void);
|
||
static void Cmd_trainerslidein(void);
|
||
static void Cmd_playse(void);
|
||
static void Cmd_fanfare(void);
|
||
static void Cmd_playfaintcry(void);
|
||
static void Cmd_endlinkbattle(void);
|
||
static void Cmd_returntoball(void);
|
||
static void Cmd_handlelearnnewmove(void);
|
||
static void Cmd_yesnoboxlearnmove(void);
|
||
static void Cmd_yesnoboxstoplearningmove(void);
|
||
static void Cmd_hitanimation(void);
|
||
static void Cmd_getmoneyreward(void);
|
||
static void Cmd_updatebattlermoves(void);
|
||
static void Cmd_swapattackerwithtarget(void);
|
||
static void Cmd_incrementgamestat(void);
|
||
static void Cmd_drawpartystatussummary(void);
|
||
static void Cmd_hidepartystatussummary(void);
|
||
static void Cmd_jumptocalledmove(void);
|
||
static void Cmd_statusanimation(void);
|
||
static void Cmd_status2animation(void);
|
||
static void Cmd_chosenstatusanimation(void);
|
||
static void Cmd_yesnobox(void);
|
||
static void Cmd_cancelallactions(void);
|
||
static void Cmd_setgravity(void);
|
||
static void Cmd_removeitem(void);
|
||
static void Cmd_atknameinbuff1(void);
|
||
static void Cmd_drawlvlupbox(void);
|
||
static void Cmd_resetsentmonsvalue(void);
|
||
static void Cmd_setatktoplayer0(void);
|
||
static void Cmd_makevisible(void);
|
||
static void Cmd_recordability(void);
|
||
static void Cmd_buffermovetolearn(void);
|
||
static void Cmd_jumpifplayerran(void);
|
||
static void Cmd_hpthresholds(void);
|
||
static void Cmd_hpthresholds2(void);
|
||
static void Cmd_useitemonopponent(void);
|
||
static void Cmd_various(void);
|
||
static void Cmd_setprotectlike(void);
|
||
static void Cmd_tryexplosion(void);
|
||
static void Cmd_setatkhptozero(void);
|
||
static void Cmd_jumpifnexttargetvalid(void);
|
||
static void Cmd_tryhealhalfhealth(void);
|
||
static void Cmd_trymirrormove(void);
|
||
static void Cmd_setrain(void);
|
||
static void Cmd_setreflect(void);
|
||
static void Cmd_setseeded(void);
|
||
static void Cmd_manipulatedamage(void);
|
||
static void Cmd_trysetrest(void);
|
||
static void Cmd_jumpifnotfirstturn(void);
|
||
static void Cmd_setmiracleeye(void);
|
||
static void Cmd_jumpifuproarwakes(void);
|
||
static void Cmd_stockpile(void);
|
||
static void Cmd_stockpiletobasedamage(void);
|
||
static void Cmd_stockpiletohpheal(void);
|
||
static void Cmd_setdrainedhp(void);
|
||
static void Cmd_statbuffchange(void);
|
||
static void Cmd_normalisebuffs(void);
|
||
static void Cmd_setbide(void);
|
||
static void Cmd_confuseifrepeatingattackends(void);
|
||
static void Cmd_setmultihitcounter(void);
|
||
static void Cmd_initmultihitstring(void);
|
||
static void Cmd_forcerandomswitch(void);
|
||
static void Cmd_tryconversiontypechange(void);
|
||
static void Cmd_givepaydaymoney(void);
|
||
static void Cmd_setlightscreen(void);
|
||
static void Cmd_tryKO(void);
|
||
static void Cmd_damagetohalftargethp(void);
|
||
static void Cmd_setsandstorm(void);
|
||
static void Cmd_weatherdamage(void);
|
||
static void Cmd_tryinfatuating(void);
|
||
static void Cmd_updatestatusicon(void);
|
||
static void Cmd_setmist(void);
|
||
static void Cmd_setfocusenergy(void);
|
||
static void Cmd_transformdataexecution(void);
|
||
static void Cmd_setsubstitute(void);
|
||
static void Cmd_mimicattackcopy(void);
|
||
static void Cmd_metronome(void);
|
||
static void Cmd_dmgtolevel(void);
|
||
static void Cmd_psywavedamageeffect(void);
|
||
static void Cmd_counterdamagecalculator(void);
|
||
static void Cmd_mirrorcoatdamagecalculator(void);
|
||
static void Cmd_disablelastusedattack(void);
|
||
static void Cmd_trysetencore(void);
|
||
static void Cmd_painsplitdmgcalc(void);
|
||
static void Cmd_settypetorandomresistance(void);
|
||
static void Cmd_setalwayshitflag(void);
|
||
static void Cmd_copymovepermanently(void);
|
||
static void Cmd_trychoosesleeptalkmove(void);
|
||
static void Cmd_setdestinybond(void);
|
||
static void Cmd_trysetdestinybondtohappen(void);
|
||
static void Cmd_settailwind(void);
|
||
static void Cmd_tryspiteppreduce(void);
|
||
static void Cmd_healpartystatus(void);
|
||
static void Cmd_cursetarget(void);
|
||
static void Cmd_trysetspikes(void);
|
||
static void Cmd_setforesight(void);
|
||
static void Cmd_trysetperishsong(void);
|
||
static void Cmd_handlerollout(void);
|
||
static void Cmd_jumpifconfusedandstatmaxed(void);
|
||
static void Cmd_handlefurycutter(void);
|
||
static void Cmd_setembargo(void);
|
||
static void Cmd_presentdamagecalculation(void);
|
||
static void Cmd_setsafeguard(void);
|
||
static void Cmd_magnitudedamagecalculation(void);
|
||
static void Cmd_jumpifnopursuitswitchdmg(void);
|
||
static void Cmd_setsunny(void);
|
||
static void Cmd_halvehp(void);
|
||
static void Cmd_copyfoestats(void);
|
||
static void Cmd_rapidspinfree(void);
|
||
static void Cmd_setdefensecurlbit(void);
|
||
static void Cmd_recoverbasedonsunlight(void);
|
||
static void Cmd_setstickyweb(void);
|
||
static void Cmd_selectfirstvalidtarget(void);
|
||
static void Cmd_trysetfutureattack(void);
|
||
static void Cmd_trydobeatup(void);
|
||
static void Cmd_setsemiinvulnerablebit(void);
|
||
static void Cmd_clearsemiinvulnerablebit(void);
|
||
static void Cmd_setminimize(void);
|
||
static void Cmd_sethail(void);
|
||
static void Cmd_trymemento(void);
|
||
static void Cmd_setforcedtarget(void);
|
||
static void Cmd_setcharge(void);
|
||
static void Cmd_callterrainattack(void);
|
||
static void Cmd_cureifburnedparalysedorpoisoned(void);
|
||
static void Cmd_settorment(void);
|
||
static void Cmd_jumpifnodamage(void);
|
||
static void Cmd_settaunt(void);
|
||
static void Cmd_trysethelpinghand(void);
|
||
static void Cmd_tryswapitems(void);
|
||
static void Cmd_trycopyability(void);
|
||
static void Cmd_trywish(void);
|
||
static void Cmd_settoxicspikes(void);
|
||
static void Cmd_setgastroacid(void);
|
||
static void Cmd_setyawn(void);
|
||
static void Cmd_setdamagetohealthdifference(void);
|
||
static void Cmd_setroom(void);
|
||
static void Cmd_tryswapabilities(void);
|
||
static void Cmd_tryimprison(void);
|
||
static void Cmd_setstealthrock(void);
|
||
static void Cmd_setuserstatus3(void);
|
||
static void Cmd_assistattackselect(void);
|
||
static void Cmd_trysetmagiccoat(void);
|
||
static void Cmd_trysetsnatch(void);
|
||
static void Cmd_unused2(void);
|
||
static void Cmd_switchoutabilities(void);
|
||
static void Cmd_jumpifhasnohp(void);
|
||
static void Cmd_getsecretpowereffect(void);
|
||
static void Cmd_pickup(void);
|
||
static void Cmd_unused3(void);
|
||
static void Cmd_unused4(void);
|
||
static void Cmd_settypebasedhalvers(void);
|
||
static void Cmd_jumpifsubstituteblocks(void);
|
||
static void Cmd_tryrecycleitem(void);
|
||
static void Cmd_settypetoterrain(void);
|
||
static void Cmd_pursuitdoubles(void);
|
||
static void Cmd_snatchsetbattlers(void);
|
||
static void Cmd_removelightscreenreflect(void);
|
||
static void Cmd_handleballthrow(void);
|
||
static void Cmd_givecaughtmon(void);
|
||
static void Cmd_trysetcaughtmondexflags(void);
|
||
static void Cmd_displaydexinfo(void);
|
||
static void Cmd_trygivecaughtmonnick(void);
|
||
static void Cmd_subattackerhpbydmg(void);
|
||
static void Cmd_removeattackerstatus1(void);
|
||
static void Cmd_finishaction(void);
|
||
static void Cmd_finishturn(void);
|
||
static void Cmd_trainerslideout(void);
|
||
static void Cmd_settelekinesis(void);
|
||
static void Cmd_swapstatstages(void);
|
||
static void Cmd_averagestats(void);
|
||
static void Cmd_jumpifoppositegenders(void);
|
||
static void Cmd_unused(void);
|
||
static void Cmd_tryworryseed(void);
|
||
static void Cmd_callnative(void);
|
||
|
||
void (* const gBattleScriptingCommandsTable[])(void) =
|
||
{
|
||
Cmd_attackcanceler, //0x0
|
||
Cmd_accuracycheck, //0x1
|
||
Cmd_attackstring, //0x2
|
||
Cmd_ppreduce, //0x3
|
||
Cmd_critcalc, //0x4
|
||
Cmd_damagecalc, //0x5
|
||
Cmd_typecalc, //0x6
|
||
Cmd_adjustdamage, //0x7
|
||
Cmd_multihitresultmessage, //0x8
|
||
Cmd_attackanimation, //0x9
|
||
Cmd_waitanimation, //0xA
|
||
Cmd_healthbarupdate, //0xB
|
||
Cmd_datahpupdate, //0xC
|
||
Cmd_critmessage, //0xD
|
||
Cmd_effectivenesssound, //0xE
|
||
Cmd_resultmessage, //0xF
|
||
Cmd_printstring, //0x10
|
||
Cmd_printselectionstring, //0x11
|
||
Cmd_waitmessage, //0x12
|
||
Cmd_printfromtable, //0x13
|
||
Cmd_printselectionstringfromtable, //0x14
|
||
Cmd_seteffectwithchance, //0x15
|
||
Cmd_seteffectprimary, //0x16
|
||
Cmd_seteffectsecondary, //0x17
|
||
Cmd_clearstatusfromeffect, //0x18
|
||
Cmd_tryfaintmon, //0x19
|
||
Cmd_dofaintanimation, //0x1A
|
||
Cmd_cleareffectsonfaint, //0x1B
|
||
Cmd_jumpifstatus, //0x1C
|
||
Cmd_jumpifstatus2, //0x1D
|
||
Cmd_jumpifability, //0x1E
|
||
Cmd_jumpifsideaffecting, //0x1F
|
||
Cmd_jumpifstat, //0x20
|
||
Cmd_jumpifstatus3condition, //0x21
|
||
Cmd_jumpbasedontype, //0x22
|
||
Cmd_getexp, //0x23
|
||
Cmd_checkteamslost, //0x24
|
||
Cmd_movevaluescleanup, //0x25
|
||
Cmd_setmultihit, //0x26
|
||
Cmd_decrementmultihit, //0x27
|
||
Cmd_goto, //0x28
|
||
Cmd_jumpifbyte, //0x29
|
||
Cmd_jumpifhalfword, //0x2A
|
||
Cmd_jumpifword, //0x2B
|
||
Cmd_jumpifarrayequal, //0x2C
|
||
Cmd_jumpifarraynotequal, //0x2D
|
||
Cmd_setbyte, //0x2E
|
||
Cmd_addbyte, //0x2F
|
||
Cmd_subbyte, //0x30
|
||
Cmd_copyarray, //0x31
|
||
Cmd_copyarraywithindex, //0x32
|
||
Cmd_orbyte, //0x33
|
||
Cmd_orhalfword, //0x34
|
||
Cmd_orword, //0x35
|
||
Cmd_bicbyte, //0x36
|
||
Cmd_bichalfword, //0x37
|
||
Cmd_bicword, //0x38
|
||
Cmd_pause, //0x39
|
||
Cmd_waitstate, //0x3A
|
||
Cmd_healthbar_update, //0x3B
|
||
Cmd_return, //0x3C
|
||
Cmd_end, //0x3D
|
||
Cmd_end2, //0x3E
|
||
Cmd_end3, //0x3F
|
||
Cmd_unused5, //0x40
|
||
Cmd_call, //0x41
|
||
Cmd_setroost, //0x42
|
||
Cmd_jumpifabilitypresent, //0x43
|
||
Cmd_endselectionscript, //0x44
|
||
Cmd_playanimation, //0x45
|
||
Cmd_playanimation_var, //0x46
|
||
Cmd_setgraphicalstatchangevalues, //0x47
|
||
Cmd_playstatchangeanimation, //0x48
|
||
Cmd_moveend, //0x49
|
||
Cmd_sethealblock, //0x4A
|
||
Cmd_returnatktoball, //0x4B
|
||
Cmd_getswitchedmondata, //0x4C
|
||
Cmd_switchindataupdate, //0x4D
|
||
Cmd_switchinanim, //0x4E
|
||
Cmd_jumpifcantswitch, //0x4F
|
||
Cmd_openpartyscreen, //0x50
|
||
Cmd_switchhandleorder, //0x51
|
||
Cmd_switchineffects, //0x52
|
||
Cmd_trainerslidein, //0x53
|
||
Cmd_playse, //0x54
|
||
Cmd_fanfare, //0x55
|
||
Cmd_playfaintcry, //0x56
|
||
Cmd_endlinkbattle, //0x57
|
||
Cmd_returntoball, //0x58
|
||
Cmd_handlelearnnewmove, //0x59
|
||
Cmd_yesnoboxlearnmove, //0x5A
|
||
Cmd_yesnoboxstoplearningmove, //0x5B
|
||
Cmd_hitanimation, //0x5C
|
||
Cmd_getmoneyreward, //0x5D
|
||
Cmd_updatebattlermoves, //0x5E
|
||
Cmd_swapattackerwithtarget, //0x5F
|
||
Cmd_incrementgamestat, //0x60
|
||
Cmd_drawpartystatussummary, //0x61
|
||
Cmd_hidepartystatussummary, //0x62
|
||
Cmd_jumptocalledmove, //0x63
|
||
Cmd_statusanimation, //0x64
|
||
Cmd_status2animation, //0x65
|
||
Cmd_chosenstatusanimation, //0x66
|
||
Cmd_yesnobox, //0x67
|
||
Cmd_cancelallactions, //0x68
|
||
Cmd_setgravity, //0x69
|
||
Cmd_removeitem, //0x6A
|
||
Cmd_atknameinbuff1, //0x6B
|
||
Cmd_drawlvlupbox, //0x6C
|
||
Cmd_resetsentmonsvalue, //0x6D
|
||
Cmd_setatktoplayer0, //0x6E
|
||
Cmd_makevisible, //0x6F
|
||
Cmd_recordability, //0x70
|
||
Cmd_buffermovetolearn, //0x71
|
||
Cmd_jumpifplayerran, //0x72
|
||
Cmd_hpthresholds, //0x73
|
||
Cmd_hpthresholds2, //0x74
|
||
Cmd_useitemonopponent, //0x75
|
||
Cmd_various, //0x76
|
||
Cmd_setprotectlike, //0x77
|
||
Cmd_tryexplosion, //0x78
|
||
Cmd_setatkhptozero, //0x79
|
||
Cmd_jumpifnexttargetvalid, //0x7A
|
||
Cmd_tryhealhalfhealth, //0x7B
|
||
Cmd_trymirrormove, //0x7C
|
||
Cmd_setrain, //0x7D
|
||
Cmd_setreflect, //0x7E
|
||
Cmd_setseeded, //0x7F
|
||
Cmd_manipulatedamage, //0x80
|
||
Cmd_trysetrest, //0x81
|
||
Cmd_jumpifnotfirstturn, //0x82
|
||
Cmd_setmiracleeye, //0x83
|
||
Cmd_jumpifuproarwakes, //0x84
|
||
Cmd_stockpile, //0x85
|
||
Cmd_stockpiletobasedamage, //0x86
|
||
Cmd_stockpiletohpheal, //0x87
|
||
Cmd_setdrainedhp, //0x88
|
||
Cmd_statbuffchange, //0x89
|
||
Cmd_normalisebuffs, //0x8A
|
||
Cmd_setbide, //0x8B
|
||
Cmd_confuseifrepeatingattackends, //0x8C
|
||
Cmd_setmultihitcounter, //0x8D
|
||
Cmd_initmultihitstring, //0x8E
|
||
Cmd_forcerandomswitch, //0x8F
|
||
Cmd_tryconversiontypechange, //0x90
|
||
Cmd_givepaydaymoney, //0x91
|
||
Cmd_setlightscreen, //0x92
|
||
Cmd_tryKO, //0x93
|
||
Cmd_damagetohalftargethp, //0x94
|
||
Cmd_setsandstorm, //0x95
|
||
Cmd_weatherdamage, //0x96
|
||
Cmd_tryinfatuating, //0x97
|
||
Cmd_updatestatusicon, //0x98
|
||
Cmd_setmist, //0x99
|
||
Cmd_setfocusenergy, //0x9A
|
||
Cmd_transformdataexecution, //0x9B
|
||
Cmd_setsubstitute, //0x9C
|
||
Cmd_mimicattackcopy, //0x9D
|
||
Cmd_metronome, //0x9E
|
||
Cmd_dmgtolevel, //0x9F
|
||
Cmd_psywavedamageeffect, //0xA0
|
||
Cmd_counterdamagecalculator, //0xA1
|
||
Cmd_mirrorcoatdamagecalculator, //0xA2
|
||
Cmd_disablelastusedattack, //0xA3
|
||
Cmd_trysetencore, //0xA4
|
||
Cmd_painsplitdmgcalc, //0xA5
|
||
Cmd_settypetorandomresistance, //0xA6
|
||
Cmd_setalwayshitflag, //0xA7
|
||
Cmd_copymovepermanently, //0xA8
|
||
Cmd_trychoosesleeptalkmove, //0xA9
|
||
Cmd_setdestinybond, //0xAA
|
||
Cmd_trysetdestinybondtohappen, //0xAB
|
||
Cmd_settailwind, //0xAC
|
||
Cmd_tryspiteppreduce, //0xAD
|
||
Cmd_healpartystatus, //0xAE
|
||
Cmd_cursetarget, //0xAF
|
||
Cmd_trysetspikes, //0xB0
|
||
Cmd_setforesight, //0xB1
|
||
Cmd_trysetperishsong, //0xB2
|
||
Cmd_handlerollout, //0xB3
|
||
Cmd_jumpifconfusedandstatmaxed, //0xB4
|
||
Cmd_handlefurycutter, //0xB5
|
||
Cmd_setembargo, //0xB6
|
||
Cmd_presentdamagecalculation, //0xB7
|
||
Cmd_setsafeguard, //0xB8
|
||
Cmd_magnitudedamagecalculation, //0xB9
|
||
Cmd_jumpifnopursuitswitchdmg, //0xBA
|
||
Cmd_setsunny, //0xBB
|
||
Cmd_halvehp, //0xBC
|
||
Cmd_copyfoestats, //0xBD
|
||
Cmd_rapidspinfree, //0xBE
|
||
Cmd_setdefensecurlbit, //0xBF
|
||
Cmd_recoverbasedonsunlight, //0xC0
|
||
Cmd_setstickyweb, //0xC1
|
||
Cmd_selectfirstvalidtarget, //0xC2
|
||
Cmd_trysetfutureattack, //0xC3
|
||
Cmd_trydobeatup, //0xC4
|
||
Cmd_setsemiinvulnerablebit, //0xC5
|
||
Cmd_clearsemiinvulnerablebit, //0xC6
|
||
Cmd_setminimize, //0xC7
|
||
Cmd_sethail, //0xC8
|
||
Cmd_trymemento, //0xC9
|
||
Cmd_setforcedtarget, //0xCA
|
||
Cmd_setcharge, //0xCB
|
||
Cmd_callterrainattack, //0xCC
|
||
Cmd_cureifburnedparalysedorpoisoned, //0xCD
|
||
Cmd_settorment, //0xCE
|
||
Cmd_jumpifnodamage, //0xCF
|
||
Cmd_settaunt, //0xD0
|
||
Cmd_trysethelpinghand, //0xD1
|
||
Cmd_tryswapitems, //0xD2
|
||
Cmd_trycopyability, //0xD3
|
||
Cmd_trywish, //0xD4
|
||
Cmd_settoxicspikes, //0xD5
|
||
Cmd_setgastroacid, //0xD6
|
||
Cmd_setyawn, //0xD7
|
||
Cmd_setdamagetohealthdifference, //0xD8
|
||
Cmd_setroom, //0xD9
|
||
Cmd_tryswapabilities, //0xDA
|
||
Cmd_tryimprison, //0xDB
|
||
Cmd_setstealthrock, //0xDC
|
||
Cmd_setuserstatus3, //0xDD
|
||
Cmd_assistattackselect, //0xDE
|
||
Cmd_trysetmagiccoat, //0xDF
|
||
Cmd_trysetsnatch, //0xE0
|
||
Cmd_unused2, //0xE1
|
||
Cmd_switchoutabilities, //0xE2
|
||
Cmd_jumpifhasnohp, //0xE3
|
||
Cmd_getsecretpowereffect, //0xE4
|
||
Cmd_pickup, //0xE5
|
||
Cmd_unused3, //0xE6
|
||
Cmd_unused4, //0xE7
|
||
Cmd_settypebasedhalvers, //0xE8
|
||
Cmd_jumpifsubstituteblocks, //0xE9
|
||
Cmd_tryrecycleitem, //0xEA
|
||
Cmd_settypetoterrain, //0xEB
|
||
Cmd_pursuitdoubles, //0xEC
|
||
Cmd_snatchsetbattlers, //0xED
|
||
Cmd_removelightscreenreflect, //0xEE
|
||
Cmd_handleballthrow, //0xEF
|
||
Cmd_givecaughtmon, //0xF0
|
||
Cmd_trysetcaughtmondexflags, //0xF1
|
||
Cmd_displaydexinfo, //0xF2
|
||
Cmd_trygivecaughtmonnick, //0xF3
|
||
Cmd_subattackerhpbydmg, //0xF4
|
||
Cmd_removeattackerstatus1, //0xF5
|
||
Cmd_finishaction, //0xF6
|
||
Cmd_finishturn, //0xF7
|
||
Cmd_trainerslideout, //0xF8
|
||
Cmd_settelekinesis, //0xF9
|
||
Cmd_swapstatstages, //0xFA
|
||
Cmd_averagestats, //0xFB
|
||
Cmd_jumpifoppositegenders, //0xFC
|
||
Cmd_unused, //0xFD
|
||
Cmd_tryworryseed, //0xFE
|
||
Cmd_callnative, //0xFF
|
||
};
|
||
|
||
const struct StatFractions gAccuracyStageRatios[] =
|
||
{
|
||
{ 33, 100}, // -6
|
||
{ 36, 100}, // -5
|
||
{ 43, 100}, // -4
|
||
{ 50, 100}, // -3
|
||
{ 60, 100}, // -2
|
||
{ 75, 100}, // -1
|
||
{ 1, 1}, // 0
|
||
{133, 100}, // +1
|
||
{166, 100}, // +2
|
||
{ 2, 1}, // +3
|
||
{233, 100}, // +4
|
||
{133, 50}, // +5
|
||
{ 3, 1}, // +6
|
||
};
|
||
|
||
static const u32 sStatusFlagsForMoveEffects[NUM_MOVE_EFFECTS] =
|
||
{
|
||
[MOVE_EFFECT_SLEEP] = STATUS1_SLEEP,
|
||
[MOVE_EFFECT_POISON] = STATUS1_POISON,
|
||
[MOVE_EFFECT_BURN] = STATUS1_BURN,
|
||
[MOVE_EFFECT_FREEZE] = STATUS1_FREEZE,
|
||
[MOVE_EFFECT_PARALYSIS] = STATUS1_PARALYSIS,
|
||
[MOVE_EFFECT_TOXIC] = STATUS1_TOXIC_POISON,
|
||
[MOVE_EFFECT_FROSTBITE] = STATUS1_FROSTBITE,
|
||
[MOVE_EFFECT_CONFUSION] = STATUS2_CONFUSION,
|
||
[MOVE_EFFECT_FLINCH] = STATUS2_FLINCHED,
|
||
[MOVE_EFFECT_UPROAR] = STATUS2_UPROAR,
|
||
[MOVE_EFFECT_CHARGING] = STATUS2_MULTIPLETURNS,
|
||
[MOVE_EFFECT_WRAP] = STATUS2_WRAPPED,
|
||
[MOVE_EFFECT_RECHARGE] = STATUS2_RECHARGE,
|
||
[MOVE_EFFECT_PREVENT_ESCAPE] = STATUS2_ESCAPE_PREVENTION,
|
||
[MOVE_EFFECT_NIGHTMARE] = STATUS2_NIGHTMARE,
|
||
[MOVE_EFFECT_THRASH] = STATUS2_LOCK_CONFUSE,
|
||
};
|
||
|
||
static const u8 *const sMoveEffectBS_Ptrs[] =
|
||
{
|
||
[MOVE_EFFECT_SLEEP] = BattleScript_MoveEffectSleep,
|
||
[MOVE_EFFECT_POISON] = BattleScript_MoveEffectPoison,
|
||
[MOVE_EFFECT_BURN] = BattleScript_MoveEffectBurn,
|
||
[MOVE_EFFECT_FREEZE] = BattleScript_MoveEffectFreeze,
|
||
[MOVE_EFFECT_PARALYSIS] = BattleScript_MoveEffectParalysis,
|
||
[MOVE_EFFECT_TOXIC] = BattleScript_MoveEffectToxic,
|
||
[MOVE_EFFECT_CONFUSION] = BattleScript_MoveEffectConfusion,
|
||
[MOVE_EFFECT_UPROAR] = BattleScript_MoveEffectUproar,
|
||
[MOVE_EFFECT_PAYDAY] = BattleScript_MoveEffectPayDay,
|
||
[MOVE_EFFECT_WRAP] = BattleScript_MoveEffectWrap,
|
||
[MOVE_EFFECT_FROSTBITE] = BattleScript_MoveEffectFrostbite,
|
||
};
|
||
|
||
static const struct WindowTemplate sUnusedWinTemplate =
|
||
{
|
||
.bg = 0,
|
||
.tilemapLeft = 1,
|
||
.tilemapTop = 3,
|
||
.width = 7,
|
||
.height = 15,
|
||
.paletteNum = 31,
|
||
.baseBlock = 0x3F
|
||
};
|
||
|
||
static const u16 sLevelUpBanner_Pal[] = INCBIN_U16("graphics/battle_interface/level_up_banner.gbapal");
|
||
static const u32 sLevelUpBanner_Gfx[] = INCBIN_U32("graphics/battle_interface/level_up_banner.4bpp.lz");
|
||
|
||
static const struct OamData sOamData_MonIconOnLvlUpBanner =
|
||
{
|
||
.y = 0,
|
||
.affineMode = ST_OAM_AFFINE_OFF,
|
||
.objMode = ST_OAM_OBJ_NORMAL,
|
||
.mosaic = FALSE,
|
||
.bpp = ST_OAM_4BPP,
|
||
.shape = SPRITE_SHAPE(32x32),
|
||
.x = 0,
|
||
.matrixNum = 0,
|
||
.size = SPRITE_SIZE(32x32),
|
||
.tileNum = 0,
|
||
.priority = 0,
|
||
.paletteNum = 0,
|
||
.affineParam = 0,
|
||
};
|
||
|
||
static const struct SpriteTemplate sSpriteTemplate_MonIconOnLvlUpBanner =
|
||
{
|
||
.tileTag = TAG_LVLUP_BANNER_MON_ICON,
|
||
.paletteTag = TAG_LVLUP_BANNER_MON_ICON,
|
||
.oam = &sOamData_MonIconOnLvlUpBanner,
|
||
.anims = gDummySpriteAnimTable,
|
||
.images = NULL,
|
||
.affineAnims = gDummySpriteAffineAnimTable,
|
||
.callback = SpriteCB_MonIconOnLvlUpBanner
|
||
};
|
||
|
||
static const u16 sProtectSuccessRates[] = {USHRT_MAX, USHRT_MAX / 2, USHRT_MAX / 4, USHRT_MAX / 8};
|
||
|
||
static const u16 sFinalStrikeOnlyEffects[] =
|
||
{
|
||
EFFECT_RELIC_SONG,
|
||
EFFECT_BUG_BITE,
|
||
EFFECT_THIEF,
|
||
EFFECT_BURN_UP,
|
||
EFFECT_DOUBLE_SHOCK,
|
||
EFFECT_SECRET_POWER,
|
||
EFFECT_SMACK_DOWN,
|
||
EFFECT_SPARKLING_ARIA,
|
||
EFFECT_SMELLING_SALTS,
|
||
EFFECT_WAKE_UP_SLAP,
|
||
EFFECT_HIT_ESCAPE,
|
||
EFFECT_RECOIL_HP_25,
|
||
EFFECT_HIT_PREVENT_ESCAPE,
|
||
EFFECT_HIT_SWITCH_TARGET,
|
||
};
|
||
|
||
static const u16 sNaturePowerMoves[BATTLE_TERRAIN_COUNT] =
|
||
{
|
||
#if B_NATURE_POWER_MOVES >= GEN_7
|
||
[BATTLE_TERRAIN_GRASS] = MOVE_ENERGY_BALL,
|
||
[BATTLE_TERRAIN_LONG_GRASS] = MOVE_ENERGY_BALL,
|
||
[BATTLE_TERRAIN_SAND] = MOVE_EARTH_POWER,
|
||
[BATTLE_TERRAIN_WATER] = MOVE_HYDRO_PUMP,
|
||
[BATTLE_TERRAIN_POND] = MOVE_HYDRO_PUMP,
|
||
[BATTLE_TERRAIN_MOUNTAIN] = MOVE_EARTH_POWER,
|
||
[BATTLE_TERRAIN_CAVE] = MOVE_EARTH_POWER,
|
||
[BATTLE_TERRAIN_BUILDING] = MOVE_TRI_ATTACK,
|
||
[BATTLE_TERRAIN_PLAIN] = MOVE_TRI_ATTACK,
|
||
[BATTLE_TERRAIN_SNOW] = MOVE_ICE_BEAM,
|
||
#elif B_NATURE_POWER_MOVES == GEN_6
|
||
[BATTLE_TERRAIN_GRASS] = MOVE_ENERGY_BALL,
|
||
[BATTLE_TERRAIN_LONG_GRASS] = MOVE_ENERGY_BALL,
|
||
[BATTLE_TERRAIN_SAND] = MOVE_EARTH_POWER,
|
||
[BATTLE_TERRAIN_WATER] = MOVE_HYDRO_PUMP,
|
||
[BATTLE_TERRAIN_POND] = MOVE_HYDRO_PUMP,
|
||
[BATTLE_TERRAIN_MOUNTAIN] = MOVE_EARTH_POWER,
|
||
[BATTLE_TERRAIN_CAVE] = MOVE_EARTH_POWER,
|
||
[BATTLE_TERRAIN_BUILDING] = MOVE_TRI_ATTACK,
|
||
[BATTLE_TERRAIN_PLAIN] = MOVE_TRI_ATTACK,
|
||
[BATTLE_TERRAIN_SNOW] = MOVE_FROST_BREATH,
|
||
#elif B_NATURE_POWER_MOVES == GEN_5
|
||
[BATTLE_TERRAIN_GRASS] = MOVE_SEED_BOMB,
|
||
[BATTLE_TERRAIN_LONG_GRASS] = MOVE_SEED_BOMB,
|
||
[BATTLE_TERRAIN_SAND] = MOVE_EARTHQUAKE,
|
||
[BATTLE_TERRAIN_WATER] = MOVE_HYDRO_PUMP,
|
||
[BATTLE_TERRAIN_POND] = MOVE_HYDRO_PUMP,
|
||
[BATTLE_TERRAIN_MOUNTAIN] = MOVE_EARTHQUAKE,
|
||
[BATTLE_TERRAIN_CAVE] = MOVE_EARTHQUAKE,
|
||
[BATTLE_TERRAIN_BUILDING] = MOVE_TRI_ATTACK,
|
||
[BATTLE_TERRAIN_PLAIN] = MOVE_EARTHQUAKE,
|
||
[BATTLE_TERRAIN_SNOW] = MOVE_BLIZZARD,
|
||
#elif B_NATURE_POWER_MOVES == GEN_4
|
||
[BATTLE_TERRAIN_GRASS] = MOVE_SEED_BOMB,
|
||
[BATTLE_TERRAIN_LONG_GRASS] = MOVE_SEED_BOMB,
|
||
[BATTLE_TERRAIN_SAND] = MOVE_EARTHQUAKE,
|
||
[BATTLE_TERRAIN_WATER] = MOVE_HYDRO_PUMP,
|
||
[BATTLE_TERRAIN_POND] = MOVE_HYDRO_PUMP,
|
||
[BATTLE_TERRAIN_MOUNTAIN] = MOVE_ROCK_SLIDE,
|
||
[BATTLE_TERRAIN_CAVE] = MOVE_ROCK_SLIDE,
|
||
[BATTLE_TERRAIN_BUILDING] = MOVE_TRI_ATTACK,
|
||
[BATTLE_TERRAIN_PLAIN] = MOVE_EARTHQUAKE,
|
||
[BATTLE_TERRAIN_SNOW] = MOVE_BLIZZARD,
|
||
#else // Gen 1-3
|
||
[BATTLE_TERRAIN_GRASS] = MOVE_STUN_SPORE,
|
||
[BATTLE_TERRAIN_LONG_GRASS] = MOVE_RAZOR_LEAF,
|
||
[BATTLE_TERRAIN_SAND] = MOVE_EARTHQUAKE,
|
||
[BATTLE_TERRAIN_WATER] = MOVE_SURF,
|
||
[BATTLE_TERRAIN_POND] = MOVE_BUBBLE_BEAM,
|
||
[BATTLE_TERRAIN_MOUNTAIN] = MOVE_ROCK_SLIDE,
|
||
[BATTLE_TERRAIN_CAVE] = MOVE_SHADOW_BALL,
|
||
[BATTLE_TERRAIN_BUILDING] = MOVE_SWIFT,
|
||
[BATTLE_TERRAIN_PLAIN] = MOVE_SWIFT,
|
||
[BATTLE_TERRAIN_SNOW] = MOVE_BLIZZARD,
|
||
#endif
|
||
[BATTLE_TERRAIN_UNDERWATER] = MOVE_HYDRO_PUMP,
|
||
[BATTLE_TERRAIN_SOARING] = MOVE_AIR_SLASH,
|
||
[BATTLE_TERRAIN_SKY_PILLAR] = MOVE_AIR_SLASH,
|
||
[BATTLE_TERRAIN_BURIAL_GROUND] = MOVE_SHADOW_BALL,
|
||
[BATTLE_TERRAIN_PUDDLE] = MOVE_MUD_BOMB,
|
||
[BATTLE_TERRAIN_MARSH] = MOVE_MUD_BOMB,
|
||
[BATTLE_TERRAIN_SWAMP] = MOVE_MUD_BOMB,
|
||
[BATTLE_TERRAIN_ICE] = MOVE_ICE_BEAM,
|
||
[BATTLE_TERRAIN_VOLCANO] = MOVE_LAVA_PLUME,
|
||
[BATTLE_TERRAIN_DISTORTION_WORLD] = MOVE_TRI_ATTACK,
|
||
[BATTLE_TERRAIN_SPACE] = MOVE_DRACO_METEOR,
|
||
[BATTLE_TERRAIN_ULTRA_SPACE] = MOVE_PSYSHOCK,
|
||
};
|
||
|
||
#define _ 0
|
||
|
||
static const struct PickupItem sPickupTable[] =
|
||
{// Item 1+ 11+ 21+ 31+ 41+ 51+ 61+ 71+ 81+ 91+ Levels
|
||
{ ITEM_POTION, { 35, _, _, _, _, _, _, _, _, _, } },
|
||
{ ITEM_TINY_MUSHROOM, { 25, 10, _, _, _, _, _, _, _, _, } },
|
||
{ ITEM_REPEL, { 8, 30, _, _, _, _, _, _, _, _, } },
|
||
{ ITEM_SUPER_POTION, { 8, 10, 30, _, _, _, _, _, _, _, } },
|
||
{ ITEM_POKE_DOLL, { 8, 10, 9, 30, _, _, _, _, _, _, } },
|
||
{ ITEM_BIG_MUSHROOM, { 3, 10, 9, _, _, _, _, _, _, _, } },
|
||
{ ITEM_SUPER_REPEL, { 3, 10, 9, 9, 30, _, _, _, _, _, } },
|
||
{ ITEM_FULL_HEAL, { 3, 3, 9, 8, 9, 30, _, _, _, _, } },
|
||
{ ITEM_REVIVE, { 3, 3, 3, 8, 8, 9, 30, _, _, _, } },
|
||
{ ITEM_HYPER_POTION, { 3, 3, 3, 4, 8, 9, 8, 30, _, _, } },
|
||
{ ITEM_ETHER, { 1, 1, 3, 4, 4, _, _, _, _, _, } },
|
||
{ ITEM_MAX_REPEL, { _, 3, 3, 4, 4, 9, 8, 8, 30, _, } },
|
||
{ ITEM_MOON_STONE, { _, 3, 3, 4, 4, 4, 4, 5, 9, 10, } },
|
||
{ ITEM_SUN_STONE, { _, 3, 3, 4, 4, 4, 4, 5, 9, 10, } },
|
||
{ ITEM_RARE_CANDY, { _, 1, 1, 1, 1, 4, 4, 5, 4, 5, } },
|
||
{ ITEM_NUGGET, { _, _, 3, 4, 4, 4, 4, 5, 4, 5, } },
|
||
{ ITEM_MAX_POTION, { _, _, 3, 4, 4, 4, 8, 8, 9, 30, } },
|
||
{ ITEM_MAX_ETHER, { _, _, 1, 1, 4, 4, 4, _, _, _, } },
|
||
{ ITEM_PP_UP, { _, _, 1, 1, 1, 4, 4, 5, 4, 5, } },
|
||
{ ITEM_BIG_NUGGET, { _, _, 1, 1, 1, 1, 4, 5, 4, 5, } },
|
||
{ ITEM_DESTINY_KNOT, { _, _, 1, 1, 1, 1, 1, 1, 1, 1, } },
|
||
{ ITEM_LEFTOVERS, { _, _, 1, 1, 1, 1, 1, 1, 1, 1, } },
|
||
{ ITEM_MENTAL_HERB, { _, _, 1, 1, 1, 1, 1, 1, 1, 1, } },
|
||
{ ITEM_POWER_HERB, { _, _, 1, 1, 1, 1, 1, 1, 1, 1, } },
|
||
{ ITEM_WHITE_HERB, { _, _, 1, 1, 1, 1, 1, 1, 1, 1, } },
|
||
{ ITEM_BALM_MUSHROOM, { _, _, 1, 4, 4, 4, 4, 5, 4, 5, } },
|
||
{ ITEM_MAX_REVIVE, { _, _, _, 4, 4, 4, 4, 7, 9, 9, } },
|
||
{ ITEM_ELIXIR, { _, _, _, _, 1, 1, 4, 5, 4, 5, } },
|
||
{ ITEM_MAX_ELIXIR, { _, _, _, _, _, _, 1, 1, 4, 5, } },
|
||
{ ITEM_BOTTLE_CAP, { _, _, _, _, _, _, _, 1, 1, 1, } },
|
||
};
|
||
|
||
#undef _
|
||
|
||
static const u8 sTerrainToType[BATTLE_TERRAIN_COUNT] =
|
||
{
|
||
[BATTLE_TERRAIN_GRASS] = TYPE_GRASS,
|
||
[BATTLE_TERRAIN_LONG_GRASS] = TYPE_GRASS,
|
||
[BATTLE_TERRAIN_SAND] = TYPE_GROUND,
|
||
[BATTLE_TERRAIN_UNDERWATER] = TYPE_WATER,
|
||
[BATTLE_TERRAIN_WATER] = TYPE_WATER,
|
||
[BATTLE_TERRAIN_POND] = TYPE_WATER,
|
||
[BATTLE_TERRAIN_CAVE] = TYPE_ROCK,
|
||
[BATTLE_TERRAIN_BUILDING] = TYPE_NORMAL,
|
||
[BATTLE_TERRAIN_SOARING] = TYPE_FLYING,
|
||
[BATTLE_TERRAIN_SKY_PILLAR] = TYPE_FLYING,
|
||
[BATTLE_TERRAIN_BURIAL_GROUND] = TYPE_GHOST,
|
||
[BATTLE_TERRAIN_PUDDLE] = TYPE_GROUND,
|
||
[BATTLE_TERRAIN_MARSH] = TYPE_GROUND,
|
||
[BATTLE_TERRAIN_SWAMP] = TYPE_GROUND,
|
||
[BATTLE_TERRAIN_SNOW] = TYPE_ICE,
|
||
[BATTLE_TERRAIN_ICE] = TYPE_ICE,
|
||
[BATTLE_TERRAIN_VOLCANO] = TYPE_FIRE,
|
||
[BATTLE_TERRAIN_DISTORTION_WORLD] = TYPE_NORMAL,
|
||
[BATTLE_TERRAIN_SPACE] = TYPE_DRAGON,
|
||
[BATTLE_TERRAIN_ULTRA_SPACE] = TYPE_PSYCHIC,
|
||
[BATTLE_TERRAIN_MOUNTAIN] = (B_CAMOUFLAGE_TYPES >= GEN_5 ? TYPE_GROUND : TYPE_ROCK),
|
||
[BATTLE_TERRAIN_PLAIN] = (B_CAMOUFLAGE_TYPES >= GEN_4 ? TYPE_GROUND : TYPE_NORMAL),
|
||
};
|
||
|
||
// In Battle Palace, moves are chosen based on the pokemons nature rather than by the player
|
||
// Moves are grouped into "Attack", "Defense", or "Support" (see PALACE_MOVE_GROUP_*)
|
||
// Each nature has a certain percent chance of selecting a move from a particular group
|
||
// and a separate percent chance for each group when at or below 50% HP
|
||
// The table below doesn't list percentages for Support because you can subtract the other two
|
||
// Support percentages are listed in comments off to the side instead
|
||
#define PALACE_STYLE(atk, def, atkLow, defLow) {atk, atk + def, atkLow, atkLow + defLow}
|
||
|
||
const ALIGNED(4) u8 gBattlePalaceNatureToMoveGroupLikelihood[NUM_NATURES][4] =
|
||
{
|
||
[NATURE_HARDY] = PALACE_STYLE(61, 7, 61, 7), // 32% support >= 50% HP, 32% support < 50% HP
|
||
[NATURE_LONELY] = PALACE_STYLE(20, 25, 84, 8), // 55%, 8%
|
||
[NATURE_BRAVE] = PALACE_STYLE(70, 15, 32, 60), // 15%, 8%
|
||
[NATURE_ADAMANT] = PALACE_STYLE(38, 31, 70, 15), // 31%, 15%
|
||
[NATURE_NAUGHTY] = PALACE_STYLE(20, 70, 70, 22), // 10%, 8%
|
||
[NATURE_BOLD] = PALACE_STYLE(30, 20, 32, 58), // 50%, 10%
|
||
[NATURE_DOCILE] = PALACE_STYLE(56, 22, 56, 22), // 22%, 22%
|
||
[NATURE_RELAXED] = PALACE_STYLE(25, 15, 75, 15), // 60%, 10%
|
||
[NATURE_IMPISH] = PALACE_STYLE(69, 6, 28, 55), // 25%, 17%
|
||
[NATURE_LAX] = PALACE_STYLE(35, 10, 29, 6), // 55%, 65%
|
||
[NATURE_TIMID] = PALACE_STYLE(62, 10, 30, 20), // 28%, 50%
|
||
[NATURE_HASTY] = PALACE_STYLE(58, 37, 88, 6), // 5%, 6%
|
||
[NATURE_SERIOUS] = PALACE_STYLE(34, 11, 29, 11), // 55%, 60%
|
||
[NATURE_JOLLY] = PALACE_STYLE(35, 5, 35, 60), // 60%, 5%
|
||
[NATURE_NAIVE] = PALACE_STYLE(56, 22, 56, 22), // 22%, 22%
|
||
[NATURE_MODEST] = PALACE_STYLE(35, 45, 34, 60), // 20%, 6%
|
||
[NATURE_MILD] = PALACE_STYLE(44, 50, 34, 6), // 6%, 60%
|
||
[NATURE_QUIET] = PALACE_STYLE(56, 22, 56, 22), // 22%, 22%
|
||
[NATURE_BASHFUL] = PALACE_STYLE(30, 58, 30, 58), // 12%, 12%
|
||
[NATURE_RASH] = PALACE_STYLE(30, 13, 27, 6), // 57%, 67%
|
||
[NATURE_CALM] = PALACE_STYLE(40, 50, 25, 62), // 10%, 13%
|
||
[NATURE_GENTLE] = PALACE_STYLE(18, 70, 90, 5), // 12%, 5%
|
||
[NATURE_SASSY] = PALACE_STYLE(88, 6, 22, 20), // 6%, 58%
|
||
[NATURE_CAREFUL] = PALACE_STYLE(42, 50, 42, 5), // 8%, 53%
|
||
[NATURE_QUIRKY] = PALACE_STYLE(56, 22, 56, 22) // 22%, 22%
|
||
};
|
||
|
||
static const u8 sBattlePalaceNatureToFlavorTextId[NUM_NATURES] =
|
||
{
|
||
[NATURE_HARDY] = B_MSG_EAGER_FOR_MORE,
|
||
[NATURE_LONELY] = B_MSG_GLINT_IN_EYE,
|
||
[NATURE_BRAVE] = B_MSG_GETTING_IN_POS,
|
||
[NATURE_ADAMANT] = B_MSG_GLINT_IN_EYE,
|
||
[NATURE_NAUGHTY] = B_MSG_GLINT_IN_EYE,
|
||
[NATURE_BOLD] = B_MSG_GETTING_IN_POS,
|
||
[NATURE_DOCILE] = B_MSG_EAGER_FOR_MORE,
|
||
[NATURE_RELAXED] = B_MSG_GLINT_IN_EYE,
|
||
[NATURE_IMPISH] = B_MSG_GETTING_IN_POS,
|
||
[NATURE_LAX] = B_MSG_GROWL_DEEPLY,
|
||
[NATURE_TIMID] = B_MSG_GROWL_DEEPLY,
|
||
[NATURE_HASTY] = B_MSG_GLINT_IN_EYE,
|
||
[NATURE_SERIOUS] = B_MSG_EAGER_FOR_MORE,
|
||
[NATURE_JOLLY] = B_MSG_GETTING_IN_POS,
|
||
[NATURE_NAIVE] = B_MSG_EAGER_FOR_MORE,
|
||
[NATURE_MODEST] = B_MSG_GETTING_IN_POS,
|
||
[NATURE_MILD] = B_MSG_GROWL_DEEPLY,
|
||
[NATURE_QUIET] = B_MSG_EAGER_FOR_MORE,
|
||
[NATURE_BASHFUL] = B_MSG_EAGER_FOR_MORE,
|
||
[NATURE_RASH] = B_MSG_GROWL_DEEPLY,
|
||
[NATURE_CALM] = B_MSG_GETTING_IN_POS,
|
||
[NATURE_GENTLE] = B_MSG_GLINT_IN_EYE,
|
||
[NATURE_SASSY] = B_MSG_GROWL_DEEPLY,
|
||
[NATURE_CAREFUL] = B_MSG_GROWL_DEEPLY,
|
||
[NATURE_QUIRKY] = B_MSG_EAGER_FOR_MORE,
|
||
};
|
||
|
||
static bool32 NoTargetPresent(u8 battler, u32 move)
|
||
{
|
||
if (!IsBattlerAlive(gBattlerTarget))
|
||
gBattlerTarget = GetMoveTarget(move, NO_TARGET_OVERRIDE);
|
||
|
||
switch (GetBattlerMoveTargetType(battler, move))
|
||
{
|
||
case MOVE_TARGET_SELECTED:
|
||
case MOVE_TARGET_DEPENDS:
|
||
case MOVE_TARGET_RANDOM:
|
||
if (!IsBattlerAlive(gBattlerTarget))
|
||
return TRUE;
|
||
break;
|
||
case MOVE_TARGET_BOTH:
|
||
if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget)))
|
||
return TRUE;
|
||
break;
|
||
case MOVE_TARGET_FOES_AND_ALLY:
|
||
if (!IsBattlerAlive(gBattlerTarget) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget)) && !IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)))
|
||
return TRUE;
|
||
break;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static bool32 TryAegiFormChange(void)
|
||
{
|
||
// Only Aegislash with Stance Change can transform, transformed mons cannot.
|
||
if (GetBattlerAbility(gBattlerAttacker) != ABILITY_STANCE_CHANGE
|
||
|| gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED)
|
||
return FALSE;
|
||
|
||
switch (gBattleMons[gBattlerAttacker].species)
|
||
{
|
||
default:
|
||
return FALSE;
|
||
case SPECIES_AEGISLASH_SHIELD: // Shield -> Blade
|
||
if (IS_MOVE_STATUS(gCurrentMove))
|
||
return FALSE;
|
||
gBattleMons[gBattlerAttacker].species = SPECIES_AEGISLASH_BLADE;
|
||
break;
|
||
case SPECIES_AEGISLASH_BLADE: // Blade -> Shield
|
||
if (gCurrentMove != MOVE_KINGS_SHIELD)
|
||
return FALSE;
|
||
gBattleMons[gBattlerAttacker].species = SPECIES_AEGISLASH_SHIELD;
|
||
break;
|
||
}
|
||
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_AttackerFormChange;
|
||
return TRUE;
|
||
}
|
||
|
||
bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType)
|
||
{
|
||
if ((ability == ABILITY_PROTEAN || ability == ABILITY_LIBERO)
|
||
&& !gDisableStructs[gBattlerAttacker].usedProteanLibero
|
||
&& (gBattleMons[battler].type1 != moveType || gBattleMons[battler].type2 != moveType
|
||
|| (gBattleMons[battler].type3 != moveType && gBattleMons[battler].type3 != TYPE_MYSTERY))
|
||
&& move != MOVE_STRUGGLE)
|
||
{
|
||
SET_BATTLER_TYPE(battler, moveType);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
bool32 IsMoveNotAllowedInSkyBattles(u32 move)
|
||
{
|
||
return ((gBattleStruct->isSkyBattle) && (gBattleMoves[gCurrentMove].skyBattleBanned));
|
||
}
|
||
|
||
static void Cmd_attackcanceler(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
s32 i, moveType;
|
||
u16 attackerAbility = GetBattlerAbility(gBattlerAttacker);
|
||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||
|
||
// Weight-based moves are blocked by Dynamax.
|
||
if (IsDynamaxed(gBattlerTarget) && IsMoveBlockedByDynamax(gCurrentMove))
|
||
{
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_MoveBlockedByDynamax;
|
||
return;
|
||
}
|
||
|
||
if (gBattleOutcome != 0)
|
||
{
|
||
gCurrentActionFuncId = B_ACTION_FINISHED;
|
||
return;
|
||
}
|
||
if (gBattleMons[gBattlerAttacker].hp == 0 && !(gHitMarker & HITMARKER_NO_ATTACKSTRING))
|
||
{
|
||
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
|
||
gBattlescriptCurrInstr = BattleScript_MoveEnd;
|
||
return;
|
||
}
|
||
if (B_STANCE_CHANGE_FAIL < GEN_7 && TryAegiFormChange())
|
||
return;
|
||
if (AtkCanceller_UnableToUseMove(moveType))
|
||
return;
|
||
|
||
if (WEATHER_HAS_EFFECT && gBattleMoves[gCurrentMove].power)
|
||
{
|
||
if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL))
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove;
|
||
return;
|
||
}
|
||
else if (moveType == TYPE_WATER && (gBattleWeather & B_WEATHER_SUN_PRIMAL))
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove;
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_OFF
|
||
&& GetBattlerAbility(gBattlerAttacker) == ABILITY_PARENTAL_BOND
|
||
&& IsMoveAffectedByParentalBond(gCurrentMove, gBattlerAttacker)
|
||
&& !(gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
||
&& gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE)
|
||
{
|
||
gSpecialStatuses[gBattlerAttacker].parentalBondState = PARENTAL_BOND_1ST_HIT;
|
||
gMultiHitCounter = 2;
|
||
PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0)
|
||
return;
|
||
}
|
||
|
||
// Check Protean activation.
|
||
if (ProteanTryChangeType(gBattlerAttacker, attackerAbility, gCurrentMove, moveType))
|
||
{
|
||
if (B_PROTEAN_LIBERO == GEN_9)
|
||
gDisableStructs[gBattlerAttacker].usedProteanLibero = TRUE;
|
||
PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType);
|
||
gBattlerAbility = gBattlerAttacker;
|
||
BattleScriptPushCursor();
|
||
PrepareStringBattle(STRINGID_EMPTYSTRING3, gBattlerAttacker);
|
||
gBattleCommunication[MSG_DISPLAY] = 1;
|
||
gBattlescriptCurrInstr = BattleScript_ProteanActivates;
|
||
return;
|
||
}
|
||
|
||
if (AtkCanceller_UnableToUseMove2())
|
||
return;
|
||
if (AbilityBattleEffects(ABILITYEFFECT_MOVES_BLOCK, gBattlerTarget, 0, 0, 0))
|
||
return;
|
||
if (!gBattleMons[gBattlerAttacker].pp[gCurrMovePos] && gCurrentMove != MOVE_STRUGGLE
|
||
&& !(gHitMarker & (HITMARKER_ALLOW_NO_PP | HITMARKER_NO_ATTACKSTRING | HITMARKER_NO_PPDEDUCT))
|
||
&& !(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
|
||
{
|
||
gBattlescriptCurrInstr = BattleScript_NoPPForMove;
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
return;
|
||
}
|
||
if (B_STANCE_CHANGE_FAIL >= GEN_7 && TryAegiFormChange())
|
||
return;
|
||
|
||
gHitMarker &= ~HITMARKER_ALLOW_NO_PP;
|
||
|
||
if (!(gHitMarker & HITMARKER_OBEYS) && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
|
||
{
|
||
switch (IsMonDisobedient())
|
||
{
|
||
case 0:
|
||
break;
|
||
case 2:
|
||
gHitMarker |= HITMARKER_OBEYS;
|
||
return;
|
||
default:
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
return;
|
||
}
|
||
}
|
||
|
||
gHitMarker |= HITMARKER_OBEYS;
|
||
// Check if no available target present on the field or if Sky Battles ban the move
|
||
if ((NoTargetPresent(gBattlerAttacker, gCurrentMove)
|
||
&& (!gBattleMoves[gCurrentMove].twoTurnMove || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))
|
||
|| (IsMoveNotAllowedInSkyBattles(gCurrentMove)))
|
||
{
|
||
if (gBattleMoves[gCurrentMove].effect == EFFECT_FLING) // Edge case for removing a mon's item when there is no target available after using Fling.
|
||
gBattlescriptCurrInstr = BattleScript_FlingFailConsumeItem;
|
||
else
|
||
gBattlescriptCurrInstr = BattleScript_FailedFromAtkString;
|
||
|
||
if (!gBattleMoves[gCurrentMove].twoTurnMove || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
|
||
CancelMultiTurnMoves(gBattlerAttacker);
|
||
return;
|
||
}
|
||
|
||
if (gProtectStructs[gBattlerTarget].bounceMove
|
||
&& gBattleMoves[gCurrentMove].magicCoatAffected
|
||
&& !gProtectStructs[gBattlerAttacker].usesBouncedMove)
|
||
{
|
||
gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
|
||
// Edge case for bouncing a powder move against a grass type pokemon.
|
||
SetAtkCancellerForCalledMove();
|
||
if (BlocksPrankster(gCurrentMove, gBattlerTarget, gBattlerAttacker, TRUE))
|
||
{
|
||
// Opponent used a prankster'd magic coat -> reflected status move should fail against a dark-type attacker
|
||
gBattlerTarget = gBattlerAttacker;
|
||
gBattlescriptCurrInstr = BattleScript_MagicCoatBouncePrankster;
|
||
}
|
||
else
|
||
{
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_MagicCoatBounce;
|
||
}
|
||
return;
|
||
}
|
||
else if (GetBattlerAbility(gBattlerTarget) == ABILITY_MAGIC_BOUNCE
|
||
&& gBattleMoves[gCurrentMove].magicCoatAffected
|
||
&& !gProtectStructs[gBattlerAttacker].usesBouncedMove)
|
||
{
|
||
gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
|
||
// Edge case for bouncing a powder move against a grass type pokemon.
|
||
SetAtkCancellerForCalledMove();
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_MagicCoatBounce;
|
||
gBattlerAbility = gBattlerTarget;
|
||
return;
|
||
}
|
||
|
||
// Z-moves and Max Moves bypass protection, but deal reduced damage (factored in AccumulateOtherModifiers)
|
||
if ((gBattleStruct->zmove.active || IsMaxMove(gCurrentMove))
|
||
&& IS_BATTLER_PROTECTED(gBattlerTarget))
|
||
{
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_CouldntFullyProtect;
|
||
return;
|
||
}
|
||
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if ((gProtectStructs[gBattlerByTurnOrder[i]].stealMove) && gBattleMoves[gCurrentMove].snatchAffected)
|
||
{
|
||
gProtectStructs[gBattlerByTurnOrder[i]].stealMove = FALSE;
|
||
gBattleScripting.battler = gBattlerByTurnOrder[i];
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_SnatchedMove;
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (gSpecialStatuses[gBattlerTarget].lightningRodRedirected)
|
||
{
|
||
gSpecialStatuses[gBattlerTarget].lightningRodRedirected = FALSE;
|
||
gLastUsedAbility = ABILITY_LIGHTNING_ROD;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_TookAttack;
|
||
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
|
||
}
|
||
else if (gSpecialStatuses[gBattlerTarget].stormDrainRedirected)
|
||
{
|
||
gSpecialStatuses[gBattlerTarget].stormDrainRedirected = FALSE;
|
||
gLastUsedAbility = ABILITY_STORM_DRAIN;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_TookAttack;
|
||
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
|
||
}
|
||
else if (IsBattlerProtected(gBattlerTarget, gCurrentMove)
|
||
&& (gCurrentMove != MOVE_CURSE || IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
|
||
&& ((!gBattleMoves[gCurrentMove].twoTurnMove || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))
|
||
&& gBattleMoves[gCurrentMove].effect != EFFECT_SUCKER_PUNCH)
|
||
{
|
||
if (IsMoveMakingContact(gCurrentMove, gBattlerAttacker))
|
||
gProtectStructs[gBattlerAttacker].touchedProtectLike = TRUE;
|
||
CancelMultiTurnMoves(gBattlerAttacker);
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gLastLandedMoves[gBattlerTarget] = 0;
|
||
gLastHitByType[gBattlerTarget] = 0;
|
||
|
||
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_1ST_HIT)
|
||
{
|
||
gSpecialStatuses[gBattlerAttacker].parentalBondState = PARENTAL_BOND_OFF; // No second hit if first hit was blocked
|
||
gSpecialStatuses[gBattlerAttacker].multiHitOn = 0;
|
||
gMultiHitCounter = 0;
|
||
}
|
||
gBattleCommunication[MISS_TYPE] = B_MSG_PROTECTED;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else if (gProtectStructs[gBattlerTarget].beakBlastCharge && IsMoveMakingContact(gCurrentMove, gBattlerAttacker))
|
||
{
|
||
gProtectStructs[gBattlerAttacker].touchedProtectLike = TRUE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static bool32 JumpIfMoveFailed(u8 adder, u16 move)
|
||
{
|
||
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
{
|
||
gLastLandedMoves[gBattlerTarget] = 0;
|
||
gLastHitByType[gBattlerTarget] = 0;
|
||
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
|
||
return TRUE;
|
||
}
|
||
else
|
||
{
|
||
TrySetDestinyBondToHappen();
|
||
if (AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, move))
|
||
return TRUE;
|
||
}
|
||
gBattlescriptCurrInstr += adder;
|
||
return FALSE;
|
||
}
|
||
|
||
static void Cmd_unused5(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (IsBattlerProtected(gBattlerTarget, gCurrentMove))
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
JumpIfMoveFailed(sizeof(*cmd), MOVE_NONE);
|
||
gBattleCommunication[MISS_TYPE] = B_MSG_PROTECTED;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static bool8 JumpIfMoveAffectedByProtect(u16 move)
|
||
{
|
||
bool8 affected = FALSE;
|
||
if (IsBattlerProtected(gBattlerTarget, move))
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
JumpIfMoveFailed(7, move);
|
||
gBattleCommunication[MISS_TYPE] = B_MSG_PROTECTED;
|
||
affected = TRUE;
|
||
}
|
||
return affected;
|
||
}
|
||
|
||
static bool32 AccuracyCalcHelper(u16 move)
|
||
{
|
||
if ((gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker)
|
||
|| (B_TOXIC_NEVER_MISS >= GEN_6 && gBattleMoves[move].effect == EFFECT_TOXIC && IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_POISON))
|
||
|| gStatuses4[gBattlerTarget] & STATUS4_GLAIVE_RUSH)
|
||
{
|
||
JumpIfMoveFailed(7, move);
|
||
return TRUE;
|
||
}
|
||
// If the attacker has the ability No Guard and they aren't targeting a Pokemon involved in a Sky Drop with the move Sky Drop, move hits.
|
||
else if (GetBattlerAbility(gBattlerTarget) == ABILITY_NO_GUARD && (move != MOVE_SKY_DROP || gBattleStruct->skyDropTargets[gBattlerTarget] == 0xFF))
|
||
{
|
||
if (!JumpIfMoveFailed(7, move))
|
||
RecordAbilityBattle(gBattlerAttacker, ABILITY_NO_GUARD);
|
||
return TRUE;
|
||
}
|
||
// If the target has the ability No Guard and they aren't involved in a Sky Drop or the current move isn't Sky Drop, move hits.
|
||
else if (GetBattlerAbility(gBattlerTarget) == ABILITY_NO_GUARD && (move != MOVE_SKY_DROP || gBattleStruct->skyDropTargets[gBattlerTarget] == 0xFF))
|
||
{
|
||
if (!JumpIfMoveFailed(7, move))
|
||
RecordAbilityBattle(gBattlerTarget, ABILITY_NO_GUARD);
|
||
return TRUE;
|
||
}
|
||
// If the target is under the effects of Telekinesis, and the move isn't a OH-KO move, move hits.
|
||
else if (gStatuses3[gBattlerTarget] & STATUS3_TELEKINESIS
|
||
&& !(gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE)
|
||
&& gBattleMoves[move].effect != EFFECT_OHKO)
|
||
{
|
||
JumpIfMoveFailed(7, move);
|
||
return TRUE;
|
||
}
|
||
|
||
if (gBattleStruct->zmove.active && !(gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE))
|
||
{
|
||
JumpIfMoveFailed(7, move);
|
||
return TRUE;
|
||
}
|
||
|
||
if ((gStatuses3[gBattlerTarget] & STATUS3_PHANTOM_FORCE)
|
||
|| ((gStatuses3[gBattlerTarget] & STATUS3_ON_AIR) && !(gBattleMoves[move].damagesAirborne || gBattleMoves[move].damagesAirborneDoubleDamage))
|
||
|| ((gStatuses3[gBattlerTarget] & STATUS3_UNDERGROUND) && !gBattleMoves[move].damagesUnderground)
|
||
|| ((gStatuses3[gBattlerTarget] & STATUS3_UNDERWATER) && !gBattleMoves[move].damagesUnderwater))
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
JumpIfMoveFailed(7, move);
|
||
return TRUE;
|
||
}
|
||
|
||
if (WEATHER_HAS_EFFECT)
|
||
{
|
||
if ((IsBattlerWeatherAffected(gBattlerTarget, B_WEATHER_RAIN) && (gBattleMoves[move].effect == EFFECT_THUNDER || gBattleMoves[move].effect == EFFECT_HURRICANE)))
|
||
{
|
||
// thunder/hurricane ignore acc checks in rain unless target is holding utility umbrella
|
||
JumpIfMoveFailed(7, move);
|
||
return TRUE;
|
||
}
|
||
else if (B_BLIZZARD_HAIL >= GEN_4 && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && move == MOVE_BLIZZARD)
|
||
{
|
||
// Blizzard ignores acc checks in Hail in Gen4+
|
||
JumpIfMoveFailed(7, move);
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
if (gBattleMoves[move].effect == EFFECT_VITAL_THROW)
|
||
{
|
||
JumpIfMoveFailed(7, move);
|
||
return TRUE;
|
||
}
|
||
|
||
if (B_MINIMIZE_DMG_ACC >= GEN_6
|
||
&& (gStatuses3[gBattlerTarget] & STATUS3_MINIMIZED)
|
||
&& gBattleMoves[move].minimizeDoubleDamage)
|
||
{
|
||
JumpIfMoveFailed(7, move);
|
||
return TRUE;
|
||
}
|
||
|
||
if (gBattleMoves[move].accuracy == 0)
|
||
{
|
||
JumpIfMoveFailed(7, move);
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect)
|
||
{
|
||
u32 calc, moveAcc;
|
||
s8 buff, accStage, evasionStage;
|
||
u32 atkParam = GetBattlerHoldEffectParam(battlerAtk);
|
||
u32 defParam = GetBattlerHoldEffectParam(battlerDef);
|
||
u32 atkAlly = BATTLE_PARTNER(battlerAtk);
|
||
u32 atkAllyAbility = GetBattlerAbility(atkAlly);
|
||
|
||
gPotentialItemEffectBattler = battlerDef;
|
||
accStage = gBattleMons[battlerAtk].statStages[STAT_ACC];
|
||
evasionStage = gBattleMons[battlerDef].statStages[STAT_EVASION];
|
||
if (atkAbility == ABILITY_UNAWARE || atkAbility == ABILITY_KEEN_EYE || atkAbility == ABILITY_MINDS_EYE
|
||
|| (B_ILLUMINATE_EFFECT >= GEN_9 && atkAbility == ABILITY_ILLUMINATE))
|
||
evasionStage = DEFAULT_STAT_STAGE;
|
||
if (gBattleMoves[move].ignoresTargetDefenseEvasionStages)
|
||
evasionStage = DEFAULT_STAT_STAGE;
|
||
if (defAbility == ABILITY_UNAWARE)
|
||
accStage = DEFAULT_STAT_STAGE;
|
||
|
||
if (gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT || gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED)
|
||
buff = accStage;
|
||
else
|
||
buff = accStage + DEFAULT_STAT_STAGE - evasionStage;
|
||
|
||
if (buff < MIN_STAT_STAGE)
|
||
buff = MIN_STAT_STAGE;
|
||
if (buff > MAX_STAT_STAGE)
|
||
buff = MAX_STAT_STAGE;
|
||
|
||
moveAcc = gBattleMoves[move].accuracy;
|
||
// Check Thunder and Hurricane on sunny weather.
|
||
if (IsBattlerWeatherAffected(battlerDef, B_WEATHER_SUN)
|
||
&& (gBattleMoves[move].effect == EFFECT_THUNDER || gBattleMoves[move].effect == EFFECT_HURRICANE))
|
||
moveAcc = 50;
|
||
// Check Wonder Skin.
|
||
if (defAbility == ABILITY_WONDER_SKIN && IS_MOVE_STATUS(move) && moveAcc > 50)
|
||
moveAcc = 50;
|
||
|
||
calc = gAccuracyStageRatios[buff].dividend * moveAcc;
|
||
calc /= gAccuracyStageRatios[buff].divisor;
|
||
|
||
// Attacker's ability
|
||
switch (atkAbility)
|
||
{
|
||
case ABILITY_COMPOUND_EYES:
|
||
calc = (calc * 130) / 100; // 1.3 compound eyes boost
|
||
break;
|
||
case ABILITY_VICTORY_STAR:
|
||
calc = (calc * 110) / 100; // 1.1 victory star boost
|
||
break;
|
||
case ABILITY_HUSTLE:
|
||
if (IS_MOVE_PHYSICAL(move))
|
||
calc = (calc * 80) / 100; // 1.2 hustle loss
|
||
break;
|
||
}
|
||
|
||
// Target's ability
|
||
switch (defAbility)
|
||
{
|
||
case ABILITY_SAND_VEIL:
|
||
if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM)
|
||
calc = (calc * 80) / 100; // 1.2 sand veil loss
|
||
break;
|
||
case ABILITY_SNOW_CLOAK:
|
||
if (WEATHER_HAS_EFFECT && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
|
||
calc = (calc * 80) / 100; // 1.2 snow cloak loss
|
||
break;
|
||
case ABILITY_TANGLED_FEET:
|
||
if (gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
|
||
calc = (calc * 50) / 100; // 1.5 tangled feet loss
|
||
break;
|
||
}
|
||
|
||
// Attacker's ally's ability
|
||
switch (atkAllyAbility)
|
||
{
|
||
case ABILITY_VICTORY_STAR:
|
||
if (IsBattlerAlive(atkAlly))
|
||
calc = (calc * 110) / 100; // 1.1 ally's victory star boost
|
||
break;
|
||
}
|
||
|
||
// Attacker's hold effect
|
||
switch (atkHoldEffect)
|
||
{
|
||
case HOLD_EFFECT_WIDE_LENS:
|
||
calc = (calc * (100 + atkParam)) / 100;
|
||
break;
|
||
case HOLD_EFFECT_ZOOM_LENS:
|
||
if (GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef))
|
||
calc = (calc * (100 + atkParam)) / 100;
|
||
break;
|
||
}
|
||
|
||
// Target's hold effect
|
||
switch (defHoldEffect)
|
||
{
|
||
case HOLD_EFFECT_EVASION_UP:
|
||
calc = (calc * (100 - defParam)) / 100;
|
||
break;
|
||
}
|
||
|
||
if (gProtectStructs[battlerAtk].usedMicleBerry)
|
||
{
|
||
gProtectStructs[battlerAtk].usedMicleBerry = FALSE;
|
||
if (atkAbility == ABILITY_RIPEN)
|
||
calc = (calc * 140) / 100; // ripen gives 40% acc boost
|
||
else
|
||
calc = (calc * 120) / 100; // 20% acc boost
|
||
}
|
||
|
||
if (gFieldStatuses & STATUS_FIELD_GRAVITY)
|
||
calc = (calc * 5) / 3; // 1.66 Gravity acc boost
|
||
|
||
if (B_AFFECTION_MECHANICS == TRUE && GetBattlerAffectionHearts(battlerDef) == AFFECTION_FIVE_HEARTS)
|
||
calc = (calc * 90) / 100;
|
||
|
||
return calc;
|
||
}
|
||
|
||
static void Cmd_accuracycheck(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr, u16 move);
|
||
|
||
u32 type, move = cmd->move;
|
||
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move);
|
||
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
|
||
u32 abilityDef = GetBattlerAbility(gBattlerTarget);
|
||
u32 holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
|
||
|
||
if (move == ACC_CURR_MOVE)
|
||
move = gCurrentMove;
|
||
|
||
if (move == NO_ACC_CALC_CHECK_LOCK_ON)
|
||
{
|
||
if (gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else if (gStatuses3[gBattlerTarget] & (STATUS3_SEMI_INVULNERABLE))
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
else if (!JumpIfMoveAffectedByProtect(gCurrentMove))
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT
|
||
|| (gSpecialStatuses[gBattlerAttacker].multiHitOn
|
||
&& (abilityAtk == ABILITY_SKILL_LINK || holdEffectAtk == HOLD_EFFECT_LOADED_DICE
|
||
|| !(gBattleMoves[move].effect == EFFECT_TRIPLE_KICK || gBattleMoves[move].effect == EFFECT_POPULATION_BOMB))))
|
||
{
|
||
// No acc checks for second hit of Parental Bond or multi hit moves, except Triple Kick/Triple Axel/Population Bomb
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
u32 accuracy;
|
||
|
||
GET_MOVE_TYPE(move, type);
|
||
if (JumpIfMoveAffectedByProtect(move))
|
||
return;
|
||
if (AccuracyCalcHelper(move))
|
||
return;
|
||
|
||
accuracy = GetTotalAccuracy(
|
||
gBattlerAttacker,
|
||
gBattlerTarget,
|
||
move,
|
||
abilityAtk,
|
||
abilityDef,
|
||
holdEffectAtk,
|
||
GetBattlerHoldEffect(gBattlerTarget, TRUE)
|
||
);
|
||
|
||
if (!RandomPercentage(RNG_ACCURACY, accuracy))
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
if (holdEffectAtk == HOLD_EFFECT_BLUNDER_POLICY)
|
||
gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE &&
|
||
(moveTarget == MOVE_TARGET_BOTH || moveTarget == MOVE_TARGET_FOES_AND_ALLY))
|
||
gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_ATK;
|
||
else
|
||
gBattleCommunication[MISS_TYPE] = B_MSG_MISSED;
|
||
|
||
if (gBattleMoves[move].power)
|
||
CalcTypeEffectivenessMultiplier(move, type, gBattlerAttacker, gBattlerTarget, abilityDef, TRUE);
|
||
}
|
||
JumpIfMoveFailed(7, move);
|
||
}
|
||
}
|
||
|
||
static void Cmd_attackstring(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
if (!(gHitMarker & (HITMARKER_NO_ATTACKSTRING | HITMARKER_ATTACKSTRING_PRINTED)))
|
||
{
|
||
PrepareStringBattle(STRINGID_USEDMOVE, gBattlerAttacker);
|
||
gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
gBattleCommunication[MSG_DISPLAY] = 0;
|
||
}
|
||
|
||
static void Cmd_ppreduce(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
s32 i, ppToDeduct = 1;
|
||
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
if (moveTarget == MOVE_TARGET_BOTH
|
||
|| moveTarget == MOVE_TARGET_FOES_AND_ALLY
|
||
|| moveTarget == MOVE_TARGET_ALL_BATTLERS
|
||
|| gBattleMoves[gCurrentMove].forcePressure)
|
||
{
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (GetBattlerSide(i) != GetBattlerSide(gBattlerAttacker) && IsBattlerAlive(i))
|
||
ppToDeduct += (GetBattlerAbility(i) == ABILITY_PRESSURE);
|
||
}
|
||
}
|
||
else if (moveTarget != MOVE_TARGET_OPPONENTS_FIELD)
|
||
{
|
||
if (gBattlerAttacker != gBattlerTarget && GetBattlerAbility(gBattlerTarget) == ABILITY_PRESSURE)
|
||
ppToDeduct++;
|
||
}
|
||
|
||
if (!(gHitMarker & (HITMARKER_NO_PPDEDUCT | HITMARKER_NO_ATTACKSTRING)) && gBattleMons[gBattlerAttacker].pp[gCurrMovePos])
|
||
{
|
||
gProtectStructs[gBattlerAttacker].notFirstStrike = TRUE;
|
||
|
||
// For item Metronome, echoed voice
|
||
if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || WasUnableToUseMove(gBattlerAttacker))
|
||
gBattleStruct->sameMoveTurns[gBattlerAttacker] = 0;
|
||
|
||
if (gBattleMons[gBattlerAttacker].pp[gCurrMovePos] > ppToDeduct)
|
||
gBattleMons[gBattlerAttacker].pp[gCurrMovePos] -= ppToDeduct;
|
||
else
|
||
gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = 0;
|
||
|
||
if (MOVE_IS_PERMANENT(gBattlerAttacker, gCurrMovePos))
|
||
{
|
||
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_PPMOVE1_BATTLE + gCurrMovePos, 0,
|
||
sizeof(gBattleMons[gBattlerAttacker].pp[gCurrMovePos]),
|
||
&gBattleMons[gBattlerAttacker].pp[gCurrMovePos]);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
}
|
||
}
|
||
|
||
gHitMarker &= ~HITMARKER_NO_PPDEDUCT;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
// The chance is 1/N for each stage.
|
||
#if B_CRIT_CHANCE >= GEN_7
|
||
static const u8 sCriticalHitChance[] = {24, 8, 2, 1, 1};
|
||
#elif B_CRIT_CHANCE == GEN_6
|
||
static const u8 sCriticalHitChance[] = {16, 8, 2, 1, 1};
|
||
#else
|
||
static const u8 sCriticalHitChance[] = {16, 8, 4, 3, 2}; // Gens 2,3,4,5
|
||
#endif // B_CRIT_CHANCE
|
||
|
||
#define BENEFITS_FROM_LEEK(battler, holdEffect)((holdEffect == HOLD_EFFECT_LEEK) && (GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_FARFETCHD || gBattleMons[battler].species == SPECIES_SIRFETCHD))
|
||
s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk)
|
||
{
|
||
s32 critChance = 0;
|
||
|
||
if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT || gStatuses3[battlerAtk] & STATUS3_CANT_SCORE_A_CRIT
|
||
|| abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR)
|
||
{
|
||
critChance = -1;
|
||
}
|
||
else if (gStatuses3[battlerAtk] & STATUS3_LASER_FOCUS
|
||
|| gBattleMoves[gCurrentMove].alwaysCriticalHit
|
||
|| (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY))
|
||
{
|
||
critChance = -2;
|
||
}
|
||
else
|
||
{
|
||
critChance = 2 * ((gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY) != 0)
|
||
+ gBattleMoves[gCurrentMove].criticalHitStage
|
||
+ (holdEffectAtk == HOLD_EFFECT_SCOPE_LENS)
|
||
+ 2 * (holdEffectAtk == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[battlerAtk].species == SPECIES_CHANSEY)
|
||
+ 2 * BENEFITS_FROM_LEEK(battlerAtk, holdEffectAtk)
|
||
+ 2 * (B_AFFECTION_MECHANICS == TRUE && GetBattlerAffectionHearts(battlerAtk) == AFFECTION_FIVE_HEARTS)
|
||
+ (abilityAtk == ABILITY_SUPER_LUCK)
|
||
+ gBattleStruct->bonusCritStages[gBattlerAttacker];
|
||
|
||
// Record ability only if move had at least +3 chance to get a crit
|
||
if (critChance >= 3 && recordAbility && (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR))
|
||
RecordAbilityBattle(battlerDef, abilityDef);
|
||
|
||
if (critChance >= ARRAY_COUNT(sCriticalHitChance))
|
||
critChance = ARRAY_COUNT(sCriticalHitChance) - 1;
|
||
}
|
||
|
||
return critChance;
|
||
}
|
||
|
||
s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility)
|
||
{
|
||
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
|
||
u32 abilityDef = GetBattlerAbility(gBattlerTarget);
|
||
u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE);
|
||
return CalcCritChanceStageArgs(battlerAtk, battlerDef, move, recordAbility, abilityAtk, abilityDef, holdEffectAtk);
|
||
}
|
||
#undef BENEFITS_FROM_LEEK
|
||
|
||
s32 GetCritHitChance(s32 critChanceIndex)
|
||
{
|
||
if (critChanceIndex < 0)
|
||
return -1;
|
||
else
|
||
return sCriticalHitChance[critChanceIndex];
|
||
}
|
||
|
||
static void Cmd_critcalc(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u16 partySlot;
|
||
s32 critChance = CalcCritChanceStage(gBattlerAttacker, gBattlerTarget, gCurrentMove, TRUE);
|
||
gPotentialItemEffectBattler = gBattlerAttacker;
|
||
|
||
if (gBattleTypeFlags & (BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_FIRST_BATTLE))
|
||
gIsCriticalHit = FALSE;
|
||
else if (critChance == -1)
|
||
gIsCriticalHit = FALSE;
|
||
else if (critChance == -2)
|
||
gIsCriticalHit = TRUE;
|
||
else
|
||
gIsCriticalHit = RandomWeighted(RNG_CRITICAL_HIT, sCriticalHitChance[critChance] - 1, 1);
|
||
|
||
// Counter for EVO_CRITICAL_HITS.
|
||
partySlot = gBattlerPartyIndexes[gBattlerAttacker];
|
||
if (gIsCriticalHit && GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER
|
||
&& !(gBattleTypeFlags & BATTLE_TYPE_MULTI && GetBattlerPosition(gBattlerAttacker) == B_POSITION_PLAYER_LEFT))
|
||
gPartyCriticalHits[partySlot]++;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_damagecalc(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u8 moveType;
|
||
|
||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||
gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, gBattlerAttacker, gBattlerTarget, moveType, 0, gIsCriticalHit, TRUE, TRUE);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_typecalc(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u8 moveType;
|
||
|
||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||
CalcTypeEffectivenessMultiplier(gCurrentMove, moveType, gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget), TRUE);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_adjustdamage(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u8 holdEffect, param;
|
||
u32 moveType;
|
||
u32 affectionScore = GetBattlerAffectionHearts(gBattlerTarget);
|
||
u32 rand = Random() % 100;
|
||
|
||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||
|
||
if (DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove))
|
||
goto END;
|
||
if (DoesDisguiseBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove))
|
||
{
|
||
gBattleStruct->enduredDamage |= gBitTable[gBattlerTarget];
|
||
goto END;
|
||
}
|
||
if (GetBattlerAbility(gBattlerTarget) == ABILITY_ICE_FACE && IS_MOVE_PHYSICAL(gCurrentMove) && gBattleMons[gBattlerTarget].species == SPECIES_EISCUE)
|
||
{
|
||
// Damage deals typeless 0 HP.
|
||
gMoveResultFlags &= ~(MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE);
|
||
gBattleMoveDamage = 0;
|
||
RecordAbilityBattle(gBattlerTarget, ABILITY_ICE_FACE);
|
||
gBattleResources->flags->flags[gBattlerTarget] |= RESOURCE_FLAG_ICE_FACE;
|
||
// Form change will be done after attack animation in Cmd_resultmessage.
|
||
goto END;
|
||
}
|
||
if (gBattleMons[gBattlerTarget].hp > gBattleMoveDamage)
|
||
goto END;
|
||
|
||
holdEffect = GetBattlerHoldEffect(gBattlerTarget, TRUE);
|
||
param = GetBattlerHoldEffectParam(gBattlerTarget);
|
||
|
||
gPotentialItemEffectBattler = gBattlerTarget;
|
||
|
||
if (holdEffect == HOLD_EFFECT_FOCUS_BAND && rand < param)
|
||
{
|
||
RecordItemEffectBattle(gBattlerTarget, holdEffect);
|
||
gSpecialStatuses[gBattlerTarget].focusBanded = TRUE;
|
||
}
|
||
else if (B_STURDY >= GEN_5 && GetBattlerAbility(gBattlerTarget) == ABILITY_STURDY && BATTLER_MAX_HP(gBattlerTarget))
|
||
{
|
||
RecordAbilityBattle(gBattlerTarget, ABILITY_STURDY);
|
||
gSpecialStatuses[gBattlerTarget].sturdied = TRUE;
|
||
}
|
||
else if (holdEffect == HOLD_EFFECT_FOCUS_SASH && BATTLER_MAX_HP(gBattlerTarget))
|
||
{
|
||
RecordItemEffectBattle(gBattlerTarget, holdEffect);
|
||
gSpecialStatuses[gBattlerTarget].focusSashed = TRUE;
|
||
}
|
||
else if (B_AFFECTION_MECHANICS == TRUE && GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER && affectionScore >= AFFECTION_THREE_HEARTS)
|
||
{
|
||
if ((affectionScore == AFFECTION_FIVE_HEARTS && rand < 20)
|
||
|| (affectionScore == AFFECTION_FOUR_HEARTS && rand < 15)
|
||
|| (affectionScore == AFFECTION_THREE_HEARTS && rand < 10))
|
||
gSpecialStatuses[gBattlerTarget].affectionEndured = TRUE;
|
||
}
|
||
|
||
if (gBattleMoves[gCurrentMove].effect != EFFECT_FALSE_SWIPE
|
||
&& !gProtectStructs[gBattlerTarget].endured
|
||
&& !gSpecialStatuses[gBattlerTarget].focusBanded
|
||
&& !gSpecialStatuses[gBattlerTarget].focusSashed
|
||
&& (B_AFFECTION_MECHANICS == FALSE || !gSpecialStatuses[gBattlerTarget].affectionEndured)
|
||
&& !gSpecialStatuses[gBattlerTarget].sturdied)
|
||
goto END;
|
||
|
||
// Handle reducing the dmg to 1 hp.
|
||
gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1;
|
||
gBattleStruct->enduredDamage |= gBitTable[gBattlerTarget];
|
||
|
||
if (gProtectStructs[gBattlerTarget].endured)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED;
|
||
}
|
||
else if (gSpecialStatuses[gBattlerTarget].focusBanded || gSpecialStatuses[gBattlerTarget].focusSashed)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON;
|
||
gLastUsedItem = gBattleMons[gBattlerTarget].item;
|
||
}
|
||
else if (gSpecialStatuses[gBattlerTarget].sturdied)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_STURDIED;
|
||
gLastUsedAbility = ABILITY_STURDY;
|
||
}
|
||
else if (B_AFFECTION_MECHANICS == TRUE && gSpecialStatuses[gBattlerTarget].affectionEndured)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED_AFFECTION;
|
||
}
|
||
|
||
END:
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMoveDamage >= 1)
|
||
gSpecialStatuses[gBattlerAttacker].damagedMons |= gBitTable[gBattlerTarget];
|
||
|
||
// Check gems and damage reducing berries.
|
||
if (gSpecialStatuses[gBattlerTarget].berryReduced
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
&& gBattleMons[gBattlerTarget].item)
|
||
{
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_BerryReduceDmg;
|
||
gLastUsedItem = gBattleMons[gBattlerTarget].item;
|
||
}
|
||
if (gSpecialStatuses[gBattlerAttacker].gemBoost
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
&& gBattleMons[gBattlerAttacker].item
|
||
&& gBattleMoves[gCurrentMove].effect != EFFECT_PLEDGE
|
||
&& gCurrentMove != MOVE_STRUGGLE)
|
||
{
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_GemActivates;
|
||
gLastUsedItem = gBattleMons[gBattlerAttacker].item;
|
||
}
|
||
|
||
// B_WEATHER_STRONG_WINDS prints a string when it's about to reduce the power
|
||
// of a move that is Super Effective against a Flying-type Pokémon.
|
||
if (gBattleWeather & B_WEATHER_STRONG_WINDS)
|
||
{
|
||
if ((GetBattlerType(gBattlerTarget, 0) == TYPE_FLYING
|
||
&& GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 0)) >= UQ_4_12(2.0))
|
||
|| (GetBattlerType(gBattlerTarget, 1) == TYPE_FLYING
|
||
&& GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 1)) >= UQ_4_12(2.0))
|
||
|| (GetBattlerType(gBattlerTarget, 2) == TYPE_FLYING
|
||
&& GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 2)) >= UQ_4_12(2.0)))
|
||
{
|
||
gBattlerAbility = gBattlerTarget;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_AttackWeakenedByStrongWinds;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void Cmd_multihitresultmessage(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
if (!(gMoveResultFlags & MOVE_RESULT_FAILED) && !(gMoveResultFlags & MOVE_RESULT_FOE_ENDURED))
|
||
{
|
||
if (gMoveResultFlags & MOVE_RESULT_STURDIED)
|
||
{
|
||
gMoveResultFlags &= ~(MOVE_RESULT_STURDIED | MOVE_RESULT_FOE_HUNG_ON);
|
||
gSpecialStatuses[gBattlerTarget].sturdied = FALSE; // Delete this line to make Sturdy last for the duration of the whole move turn.
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_SturdiedMsg;
|
||
return;
|
||
}
|
||
else if (gMoveResultFlags & MOVE_RESULT_FOE_HUNG_ON)
|
||
{
|
||
gLastUsedItem = gBattleMons[gBattlerTarget].item;
|
||
gPotentialItemEffectBattler = gBattlerTarget;
|
||
gMoveResultFlags &= ~(MOVE_RESULT_STURDIED | MOVE_RESULT_FOE_HUNG_ON);
|
||
gSpecialStatuses[gBattlerTarget].focusBanded = FALSE; // Delete this line to make Focus Band last for the duration of the whole move turn.
|
||
gSpecialStatuses[gBattlerTarget].focusSashed = FALSE; // Delete this line to make Focus Sash last for the duration of the whole move turn.
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_HangedOnMsg;
|
||
return;
|
||
}
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
// Print berry reducing message after result message.
|
||
if (gSpecialStatuses[gBattlerTarget].berryReduced
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
|
||
{
|
||
gSpecialStatuses[gBattlerTarget].berryReduced = FALSE;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_PrintBerryReduceString;
|
||
}
|
||
}
|
||
|
||
static void Cmd_attackanimation(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
if ((gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION))
|
||
&& gCurrentMove != MOVE_TRANSFORM
|
||
&& gCurrentMove != MOVE_SUBSTITUTE
|
||
&& gCurrentMove != MOVE_ALLY_SWITCH
|
||
// In a wild double battle gotta use the teleport animation if two wild pokemon are alive.
|
||
&& !(gCurrentMove == MOVE_TELEPORT && WILD_DOUBLE_BATTLE && GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT && IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))))
|
||
{
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_Pausex20;
|
||
gBattleScripting.animTurn++;
|
||
gBattleScripting.animTargetsHit++;
|
||
}
|
||
else
|
||
{
|
||
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT) // No animation on second hit
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
|
||
if ((moveTarget & MOVE_TARGET_BOTH
|
||
|| moveTarget & MOVE_TARGET_FOES_AND_ALLY
|
||
|| moveTarget & MOVE_TARGET_DEPENDS)
|
||
&& gBattleScripting.animTargetsHit)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
|
||
{
|
||
u8 multihit;
|
||
|
||
if (gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE)
|
||
multihit = gMultiHitCounter;
|
||
else if (gMultiHitCounter != 0 && gMultiHitCounter != 1)
|
||
{
|
||
if (gBattleMons[gBattlerTarget].hp <= gBattleMoveDamage)
|
||
multihit = 1;
|
||
else
|
||
multihit = gMultiHitCounter;
|
||
}
|
||
else
|
||
multihit = gMultiHitCounter;
|
||
|
||
BtlController_EmitMoveAnimation(gBattlerAttacker, BUFFER_A, gCurrentMove, gBattleScripting.animTurn, gBattleMovePower, gBattleMoveDamage, gBattleMons[gBattlerAttacker].friendship, &gDisableStructs[gBattlerAttacker], multihit);
|
||
gBattleScripting.animTurn++;
|
||
gBattleScripting.animTargetsHit++;
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_Pausex20;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void Cmd_waitanimation(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_healthbarupdate(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) || (gHitMarker & HITMARKER_PASSIVE_DAMAGE))
|
||
{
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
|
||
if (DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) && gDisableStructs[battler].substituteHP && !(gHitMarker & HITMARKER_IGNORE_SUBSTITUTE))
|
||
{
|
||
PrepareStringBattle(STRINGID_SUBSTITUTEDAMAGED, battler);
|
||
}
|
||
else if (!DoesDisguiseBlockMove(gBattlerAttacker, battler, gCurrentMove))
|
||
{
|
||
s16 healthValue = min(gBattleMoveDamage, 10000); // Max damage (10000) not present in R/S, ensures that huge damage values don't change sign
|
||
|
||
BtlController_EmitHealthBarUpdate(battler, BUFFER_A, healthValue);
|
||
MarkBattlerForControllerExec(battler);
|
||
|
||
if (GetBattlerSide(battler) == B_SIDE_PLAYER && gBattleMoveDamage > 0)
|
||
gBattleResults.playerMonWasDamaged = TRUE;
|
||
}
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
// Update the active battler's HP and various HP trackers (Substitute, Bide, etc.)
|
||
static void Cmd_datahpupdate(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 battler;
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) || (gHitMarker & HITMARKER_PASSIVE_DAMAGE))
|
||
{
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) && gDisableStructs[battler].substituteHP && !(gHitMarker & HITMARKER_IGNORE_SUBSTITUTE))
|
||
{
|
||
if (gDisableStructs[battler].substituteHP >= gBattleMoveDamage)
|
||
{
|
||
if (gSpecialStatuses[battler].shellBellDmg == 0)
|
||
gSpecialStatuses[battler].shellBellDmg = gBattleMoveDamage;
|
||
gDisableStructs[battler].substituteHP -= gBattleMoveDamage;
|
||
gHpDealt = gBattleMoveDamage;
|
||
}
|
||
else
|
||
{
|
||
if (gSpecialStatuses[battler].shellBellDmg == 0)
|
||
gSpecialStatuses[battler].shellBellDmg = gDisableStructs[battler].substituteHP;
|
||
gHpDealt = gDisableStructs[battler].substituteHP;
|
||
gDisableStructs[battler].substituteHP = 0;
|
||
}
|
||
// check substitute fading
|
||
if (gDisableStructs[battler].substituteHP == 0)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_SubstituteFade;
|
||
return;
|
||
}
|
||
}
|
||
else if (DoesDisguiseBlockMove(gBattlerAttacker, battler, gCurrentMove))
|
||
{
|
||
gBattleMons[battler].species = SPECIES_MIMIKYU_BUSTED;
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_TargetFormChange;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
gHitMarker &= ~HITMARKER_IGNORE_SUBSTITUTE;
|
||
if (gBattleMoveDamage < 0)
|
||
{
|
||
// Negative damage is HP gain
|
||
gBattleMons[battler].hp += -gBattleMoveDamage;
|
||
if (gBattleMons[battler].hp > gBattleMons[battler].maxHP)
|
||
gBattleMons[battler].hp = gBattleMons[battler].maxHP;
|
||
}
|
||
else
|
||
{
|
||
if (gHitMarker & HITMARKER_IGNORE_BIDE)
|
||
{
|
||
gHitMarker &= ~HITMARKER_IGNORE_BIDE;
|
||
}
|
||
else
|
||
{
|
||
gBideDmg[battler] += gBattleMoveDamage;
|
||
if (cmd->battler == BS_TARGET)
|
||
gBideTarget[battler] = gBattlerAttacker;
|
||
else
|
||
gBideTarget[battler] = gBattlerTarget;
|
||
}
|
||
|
||
// Deal damage to the battler
|
||
if (gBattleMons[battler].hp > gBattleMoveDamage)
|
||
{
|
||
gBattleMons[battler].hp -= gBattleMoveDamage;
|
||
gHpDealt = gBattleMoveDamage;
|
||
}
|
||
else
|
||
{
|
||
gHpDealt = gBattleMons[battler].hp;
|
||
gBattleMons[battler].hp = 0;
|
||
}
|
||
|
||
// Record damage for Shell Bell
|
||
if (gSpecialStatuses[battler].shellBellDmg == 0 && !(gHitMarker & HITMARKER_PASSIVE_DAMAGE))
|
||
gSpecialStatuses[battler].shellBellDmg = gHpDealt;
|
||
|
||
// Note: While physicalDmg/specialDmg below are only distinguished between for Counter/Mirror Coat, they are
|
||
// used in combination as general damage trackers for other purposes. specialDmg is additionally used
|
||
// to help determine if a fire move should defrost the target.
|
||
if (IS_MOVE_PHYSICAL(gCurrentMove) && !(gHitMarker & HITMARKER_PASSIVE_DAMAGE) && gBattleMoves[gCurrentMove].effect != EFFECT_PAIN_SPLIT)
|
||
{
|
||
gProtectStructs[battler].physicalDmg = gHpDealt;
|
||
gSpecialStatuses[battler].physicalDmg = gHpDealt;
|
||
if (cmd->battler == BS_TARGET)
|
||
{
|
||
gProtectStructs[battler].physicalBattlerId = gBattlerAttacker;
|
||
gSpecialStatuses[battler].physicalBattlerId = gBattlerAttacker;
|
||
}
|
||
else
|
||
{
|
||
gProtectStructs[battler].physicalBattlerId = gBattlerTarget;
|
||
gSpecialStatuses[battler].physicalBattlerId = gBattlerTarget;
|
||
}
|
||
}
|
||
else if (!IS_MOVE_PHYSICAL(gCurrentMove) && !(gHitMarker & HITMARKER_PASSIVE_DAMAGE) && gBattleMoves[gCurrentMove].effect != EFFECT_PAIN_SPLIT)
|
||
{
|
||
// Record special damage/attacker for Mirror Coat
|
||
gProtectStructs[battler].specialDmg = gHpDealt;
|
||
gSpecialStatuses[battler].specialDmg = gHpDealt;
|
||
if (cmd->battler == BS_TARGET)
|
||
{
|
||
gProtectStructs[battler].specialBattlerId = gBattlerAttacker;
|
||
gSpecialStatuses[battler].specialBattlerId = gBattlerAttacker;
|
||
}
|
||
else
|
||
{
|
||
gProtectStructs[battler].specialBattlerId = gBattlerTarget;
|
||
gSpecialStatuses[battler].specialBattlerId = gBattlerTarget;
|
||
}
|
||
}
|
||
}
|
||
gHitMarker &= ~HITMARKER_PASSIVE_DAMAGE;
|
||
|
||
// Send updated HP
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_HP_BATTLE, 0, sizeof(gBattleMons[battler].hp), &gBattleMons[battler].hp);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// MOVE_RESULT_NO_EFFECT was set
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (gSpecialStatuses[battler].shellBellDmg == 0)
|
||
gSpecialStatuses[battler].shellBellDmg = IGNORE_SHELL_BELL;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_critmessage(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
if (gIsCriticalHit == TRUE && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
|
||
{
|
||
PrepareStringBattle(STRINGID_CRITICALHIT, gBattlerAttacker);
|
||
|
||
// Signal for the trainer slide-in system.
|
||
if (GetBattlerSide(gBattlerTarget) != B_SIDE_PLAYER && gBattleStruct->trainerSlideFirstCriticalHitMsgState != 2)
|
||
gBattleStruct->trainerSlideFirstCriticalHitMsgState = 1;
|
||
|
||
gBattleCommunication[MSG_DISPLAY] = 1;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_effectivenesssound(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
if (!(gMoveResultFlags & MOVE_RESULT_MISSED))
|
||
{
|
||
switch (gMoveResultFlags & ~MOVE_RESULT_MISSED)
|
||
{
|
||
case MOVE_RESULT_SUPER_EFFECTIVE:
|
||
BtlController_EmitPlaySE(gBattlerTarget, BUFFER_A, SE_SUPER_EFFECTIVE);
|
||
MarkBattlerForControllerExec(gBattlerTarget);
|
||
break;
|
||
case MOVE_RESULT_NOT_VERY_EFFECTIVE:
|
||
BtlController_EmitPlaySE(gBattlerTarget, BUFFER_A, SE_NOT_EFFECTIVE);
|
||
MarkBattlerForControllerExec(gBattlerTarget);
|
||
break;
|
||
case MOVE_RESULT_DOESNT_AFFECT_FOE:
|
||
case MOVE_RESULT_FAILED:
|
||
// no sound
|
||
break;
|
||
case MOVE_RESULT_FOE_ENDURED:
|
||
case MOVE_RESULT_ONE_HIT_KO:
|
||
case MOVE_RESULT_FOE_HUNG_ON:
|
||
case MOVE_RESULT_STURDIED:
|
||
default:
|
||
if (gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE)
|
||
{
|
||
BtlController_EmitPlaySE(gBattlerTarget, BUFFER_A, SE_SUPER_EFFECTIVE);
|
||
MarkBattlerForControllerExec(gBattlerTarget);
|
||
}
|
||
else if (gMoveResultFlags & MOVE_RESULT_NOT_VERY_EFFECTIVE)
|
||
{
|
||
BtlController_EmitPlaySE(gBattlerTarget, BUFFER_A, SE_NOT_EFFECTIVE);
|
||
MarkBattlerForControllerExec(gBattlerTarget);
|
||
}
|
||
else if (!(gMoveResultFlags & (MOVE_RESULT_DOESNT_AFFECT_FOE | MOVE_RESULT_FAILED)))
|
||
{
|
||
BtlController_EmitPlaySE(gBattlerTarget, BUFFER_A, SE_EFFECTIVE);
|
||
MarkBattlerForControllerExec(gBattlerTarget);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_resultmessage(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u32 stringId = 0;
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
// Do Ice Face form change which was set up in Cmd_adjustdamage.
|
||
if (gBattleResources->flags->flags[gBattlerTarget] & RESOURCE_FLAG_ICE_FACE)
|
||
{
|
||
gBattleResources->flags->flags[gBattlerTarget] &= ~(RESOURCE_FLAG_ICE_FACE);
|
||
gBattleMons[gBattlerTarget].species = SPECIES_EISCUE_NOICE_FACE;
|
||
gBattleScripting.battler = gBattlerTarget; // For STRINGID_PKMNTRANSFORMED
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_IceFaceNullsDamage;
|
||
return;
|
||
}
|
||
|
||
if (gMoveResultFlags & MOVE_RESULT_MISSED && (!(gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE) || gBattleCommunication[MISS_TYPE] > B_MSG_AVOIDED_ATK))
|
||
{
|
||
if (gBattleCommunication[MISS_TYPE] > B_MSG_AVOIDED_ATK) // Wonder Guard or Levitate - show the ability pop-up
|
||
CreateAbilityPopUp(gBattlerTarget, gBattleMons[gBattlerTarget].ability, (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) != 0);
|
||
stringId = gMissStringIds[gBattleCommunication[MISS_TYPE]];
|
||
gBattleCommunication[MSG_DISPLAY] = 1;
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MSG_DISPLAY] = 1;
|
||
switch (gMoveResultFlags & ~MOVE_RESULT_MISSED)
|
||
{
|
||
case MOVE_RESULT_SUPER_EFFECTIVE:
|
||
if (!gMultiHitCounter) // Don't print effectiveness on each hit in a multi hit attack
|
||
{
|
||
// Signal for the trainer slide-in system.
|
||
if (GetBattlerSide(gBattlerTarget) != B_SIDE_PLAYER && gBattleStruct->trainerSlideFirstSuperEffectiveHitMsgState != 2)
|
||
gBattleStruct->trainerSlideFirstSuperEffectiveHitMsgState = 1;
|
||
|
||
stringId = STRINGID_SUPEREFFECTIVE;
|
||
}
|
||
break;
|
||
case MOVE_RESULT_NOT_VERY_EFFECTIVE:
|
||
if (!gMultiHitCounter)
|
||
stringId = STRINGID_NOTVERYEFFECTIVE;
|
||
break;
|
||
case MOVE_RESULT_ONE_HIT_KO:
|
||
stringId = STRINGID_ONEHITKO;
|
||
break;
|
||
case MOVE_RESULT_FOE_ENDURED:
|
||
stringId = STRINGID_PKMNENDUREDHIT;
|
||
break;
|
||
case MOVE_RESULT_FAILED:
|
||
stringId = STRINGID_BUTITFAILED;
|
||
break;
|
||
case MOVE_RESULT_DOESNT_AFFECT_FOE:
|
||
stringId = STRINGID_ITDOESNTAFFECT;
|
||
break;
|
||
case MOVE_RESULT_FOE_HUNG_ON:
|
||
gLastUsedItem = gBattleMons[gBattlerTarget].item;
|
||
gPotentialItemEffectBattler = gBattlerTarget;
|
||
gMoveResultFlags &= ~(MOVE_RESULT_FOE_ENDURED | MOVE_RESULT_FOE_HUNG_ON);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_HangedOnMsg;
|
||
return;
|
||
default:
|
||
if (gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE)
|
||
{
|
||
stringId = STRINGID_ITDOESNTAFFECT;
|
||
}
|
||
else if (gMoveResultFlags & MOVE_RESULT_ONE_HIT_KO)
|
||
{
|
||
gMoveResultFlags &= ~MOVE_RESULT_ONE_HIT_KO;
|
||
gMoveResultFlags &= ~MOVE_RESULT_SUPER_EFFECTIVE;
|
||
gMoveResultFlags &= ~MOVE_RESULT_NOT_VERY_EFFECTIVE;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_OneHitKOMsg;
|
||
return;
|
||
}
|
||
else if (gMoveResultFlags & MOVE_RESULT_STURDIED)
|
||
{
|
||
gMoveResultFlags &= ~(MOVE_RESULT_STURDIED | MOVE_RESULT_FOE_ENDURED | MOVE_RESULT_FOE_HUNG_ON);
|
||
gSpecialStatuses[gBattlerTarget].sturdied = FALSE;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_SturdiedMsg;
|
||
return;
|
||
}
|
||
else if (gMoveResultFlags & MOVE_RESULT_FOE_ENDURED)
|
||
{
|
||
gMoveResultFlags &= ~(MOVE_RESULT_FOE_ENDURED | MOVE_RESULT_FOE_HUNG_ON);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_EnduredMsg;
|
||
return;
|
||
}
|
||
else if (gMoveResultFlags & MOVE_RESULT_FOE_HUNG_ON)
|
||
{
|
||
gLastUsedItem = gBattleMons[gBattlerTarget].item;
|
||
gPotentialItemEffectBattler = gBattlerTarget;
|
||
gMoveResultFlags &= ~(MOVE_RESULT_FOE_ENDURED | MOVE_RESULT_FOE_HUNG_ON);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_HangedOnMsg;
|
||
return;
|
||
}
|
||
else if (gMoveResultFlags & MOVE_RESULT_FAILED)
|
||
{
|
||
stringId = STRINGID_BUTITFAILED;
|
||
}
|
||
else if (B_AFFECTION_MECHANICS == TRUE && (gMoveResultFlags & MOVE_RESULT_FOE_ENDURED_AFFECTION))
|
||
{
|
||
gSpecialStatuses[gBattlerTarget].affectionEndured = FALSE;
|
||
gMoveResultFlags &= ~MOVE_RESULT_FOE_ENDURED_AFFECTION;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_AffectionBasedEndurance;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MSG_DISPLAY] = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (stringId)
|
||
PrepareStringBattle(stringId, gBattlerAttacker);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
// Print berry reducing message after result message.
|
||
if (gSpecialStatuses[gBattlerTarget].berryReduced
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
|
||
{
|
||
gSpecialStatuses[gBattlerTarget].berryReduced = FALSE;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_PrintBerryReduceString;
|
||
}
|
||
}
|
||
|
||
static void Cmd_printstring(void)
|
||
{
|
||
CMD_ARGS(u16 id);
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
u16 id = cmd->id;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
PrepareStringBattle(id, gBattlerAttacker);
|
||
gBattleCommunication[MSG_DISPLAY] = 1;
|
||
}
|
||
}
|
||
|
||
static void Cmd_printselectionstring(void)
|
||
{
|
||
CMD_ARGS(u16 id);
|
||
|
||
BtlController_EmitPrintSelectionString(gBattlerAttacker, BUFFER_A, cmd->id);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
gBattleCommunication[MSG_DISPLAY] = 1;
|
||
}
|
||
|
||
static void Cmd_waitmessage(void)
|
||
{
|
||
CMD_ARGS(u16 time);
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
if (!gBattleCommunication[MSG_DISPLAY])
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
u16 toWait = cmd->time;
|
||
if (++gPauseCounterBattle >= toWait)
|
||
{
|
||
gPauseCounterBattle = 0;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
gBattleCommunication[MSG_DISPLAY] = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void Cmd_printfromtable(void)
|
||
{
|
||
CMD_ARGS(const u16 *ptr);
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
const u16 *ptr = cmd->ptr;
|
||
ptr += gBattleCommunication[MULTISTRING_CHOOSER];
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
PrepareStringBattle(*ptr, gBattlerAttacker);
|
||
gBattleCommunication[MSG_DISPLAY] = 1;
|
||
}
|
||
}
|
||
|
||
static void Cmd_printselectionstringfromtable(void)
|
||
{
|
||
CMD_ARGS(const u16 *ptr);
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
const u16 *ptr = cmd->ptr;
|
||
ptr += gBattleCommunication[MULTISTRING_CHOOSER];
|
||
|
||
BtlController_EmitPrintSelectionString(gBattlerAttacker, BUFFER_A, *ptr);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
gBattleCommunication[MSG_DISPLAY] = 1;
|
||
}
|
||
}
|
||
|
||
u8 GetBattlerTurnOrderNum(u8 battler)
|
||
{
|
||
s32 i;
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (gBattlerByTurnOrder[i] == battler)
|
||
break;
|
||
}
|
||
return i;
|
||
}
|
||
|
||
static void CheckSetUnburden(u8 battler)
|
||
{
|
||
if (GetBattlerAbility(battler) == ABILITY_UNBURDEN)
|
||
{
|
||
gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_UNBURDEN;
|
||
RecordAbilityBattle(battler, ABILITY_UNBURDEN);
|
||
}
|
||
}
|
||
|
||
// battlerStealer steals the item of battlerItem
|
||
void StealTargetItem(u8 battlerStealer, u8 battlerItem)
|
||
{
|
||
gLastUsedItem = gBattleMons[battlerItem].item;
|
||
gBattleMons[battlerItem].item = 0;
|
||
|
||
RecordItemEffectBattle(battlerItem, 0);
|
||
RecordItemEffectBattle(battlerStealer, ItemId_GetHoldEffect(gLastUsedItem));
|
||
gBattleMons[battlerStealer].item = gLastUsedItem;
|
||
|
||
CheckSetUnburden(battlerItem);
|
||
gBattleResources->flags->flags[battlerStealer] &= ~RESOURCE_FLAG_UNBURDEN;
|
||
|
||
BtlController_EmitSetMonData(battlerStealer, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gLastUsedItem), &gLastUsedItem); // set attacker item
|
||
MarkBattlerForControllerExec(battlerStealer);
|
||
|
||
BtlController_EmitSetMonData(battlerItem, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[battlerItem].item); // remove target item
|
||
MarkBattlerForControllerExec(battlerItem);
|
||
|
||
gBattleStruct->choicedMove[battlerItem] = 0;
|
||
|
||
TrySaveExchangedItem(battlerItem, gLastUsedItem);
|
||
}
|
||
|
||
#define INCREMENT_RESET_RETURN \
|
||
{ \
|
||
gBattlescriptCurrInstr++; \
|
||
gBattleScripting.moveEffect = 0; \
|
||
return; \
|
||
}
|
||
|
||
#define RESET_RETURN \
|
||
{ \
|
||
gBattleScripting.moveEffect = 0; \
|
||
return; \
|
||
}
|
||
|
||
void SetMoveEffect(bool32 primary, u32 certain)
|
||
{
|
||
s32 i, affectsUser = 0;
|
||
bool32 statusChanged = FALSE;
|
||
bool32 mirrorArmorReflected = (GetBattlerAbility(gBattlerTarget) == ABILITY_MIRROR_ARMOR);
|
||
u32 flags = 0;
|
||
u16 battlerAbility;
|
||
bool8 activateAfterFaint = FALSE;
|
||
|
||
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_1ST_HIT
|
||
&& gBattleMons[gBattlerTarget].hp != 0
|
||
&& IsFinalStrikeEffect(gCurrentMove))
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
return;
|
||
}
|
||
|
||
switch (gBattleScripting.moveEffect) // Set move effects which happen later on
|
||
{
|
||
case MOVE_EFFECT_KNOCK_OFF:
|
||
case MOVE_EFFECT_SMACK_DOWN:
|
||
case MOVE_EFFECT_REMOVE_STATUS:
|
||
case MOVE_EFFECT_STOCKPILE_WORE_OFF:
|
||
gBattleStruct->moveEffect2 = gBattleScripting.moveEffect;
|
||
gBattlescriptCurrInstr++;
|
||
return;
|
||
case MOVE_EFFECT_STEALTH_ROCK:
|
||
case MOVE_EFFECT_SPIKES:
|
||
case MOVE_EFFECT_PAYDAY:
|
||
case MOVE_EFFECT_STEAL_ITEM:
|
||
case MOVE_EFFECT_BUG_BITE:
|
||
activateAfterFaint = TRUE;
|
||
break;
|
||
}
|
||
|
||
if (gBattleScripting.moveEffect & MOVE_EFFECT_AFFECTS_USER)
|
||
{
|
||
gEffectBattler = gBattlerAttacker; // battler that effects get applied on
|
||
gBattleScripting.moveEffect &= ~MOVE_EFFECT_AFFECTS_USER;
|
||
affectsUser = MOVE_EFFECT_AFFECTS_USER;
|
||
gBattleScripting.battler = gBattlerTarget; // theoretically the attacker
|
||
}
|
||
else
|
||
{
|
||
gEffectBattler = gBattlerTarget;
|
||
gBattleScripting.battler = gBattlerAttacker;
|
||
}
|
||
|
||
battlerAbility = GetBattlerAbility(gEffectBattler);
|
||
|
||
// Just in case this flag is still set
|
||
gBattleScripting.moveEffect &= ~MOVE_EFFECT_CERTAIN;
|
||
|
||
if ((battlerAbility == ABILITY_SHIELD_DUST
|
||
|| GetBattlerHoldEffect(gEffectBattler, TRUE) == HOLD_EFFECT_COVERT_CLOAK)
|
||
&& !(gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
|
||
&& !primary
|
||
&& (gBattleScripting.moveEffect <= MOVE_EFFECT_TRI_ATTACK || gBattleScripting.moveEffect >= MOVE_EFFECT_SMACK_DOWN)) // Exclude stat lowering effects
|
||
{
|
||
if (battlerAbility == ABILITY_SHIELD_DUST)
|
||
RecordAbilityBattle(gEffectBattler, battlerAbility);
|
||
else
|
||
RecordItemEffectBattle(gEffectBattler, HOLD_EFFECT_COVERT_CLOAK);
|
||
INCREMENT_RESET_RETURN
|
||
}
|
||
|
||
if (gSideStatuses[GetBattlerSide(gEffectBattler)] & SIDE_STATUS_SAFEGUARD && !(gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
|
||
&& !primary && gBattleScripting.moveEffect <= MOVE_EFFECT_CONFUSION)
|
||
INCREMENT_RESET_RETURN
|
||
|
||
if (TestSheerForceFlag(gBattlerAttacker, gCurrentMove) && gBattleScripting.moveEffect != MOVE_EFFECT_CHARGING)
|
||
INCREMENT_RESET_RETURN
|
||
|
||
if (gBattleMons[gEffectBattler].hp == 0 && !activateAfterFaint)
|
||
INCREMENT_RESET_RETURN
|
||
|
||
if (DoesSubstituteBlockMove(gBattlerAttacker, gEffectBattler, gCurrentMove) && affectsUser != MOVE_EFFECT_AFFECTS_USER)
|
||
INCREMENT_RESET_RETURN
|
||
|
||
if (gBattleScripting.moveEffect <= PRIMARY_STATUS_MOVE_EFFECT) // status change
|
||
{
|
||
const u8 *cancelMultiTurnMovesResult = NULL;
|
||
switch (sStatusFlagsForMoveEffects[gBattleScripting.moveEffect])
|
||
{
|
||
case STATUS1_SLEEP:
|
||
// check active uproar
|
||
if (battlerAbility != ABILITY_SOUNDPROOF)
|
||
{
|
||
for (i = 0; i < gBattlersCount && !(gBattleMons[i].status2 & STATUS2_UPROAR); i++)
|
||
;
|
||
}
|
||
else
|
||
{
|
||
i = gBattlersCount;
|
||
}
|
||
|
||
if (i != gBattlersCount)
|
||
break;
|
||
if (!CanSleep(gEffectBattler))
|
||
break;
|
||
|
||
cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler);
|
||
if (cancelMultiTurnMovesResult)
|
||
gBattlescriptCurrInstr = cancelMultiTurnMovesResult;
|
||
statusChanged = TRUE;
|
||
break;
|
||
case STATUS1_POISON:
|
||
if ((battlerAbility == ABILITY_IMMUNITY || battlerAbility == ABILITY_PASTEL_VEIL)
|
||
&& (primary == TRUE || certain == MOVE_EFFECT_CERTAIN))
|
||
{
|
||
gLastUsedAbility = battlerAbility;
|
||
RecordAbilityBattle(gEffectBattler, battlerAbility);
|
||
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_PSNPrevention;
|
||
|
||
if (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_ABILITY_STATUS;
|
||
gHitMarker &= ~HITMARKER_STATUS_ABILITY_EFFECT;
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_STATUS;
|
||
}
|
||
RESET_RETURN
|
||
}
|
||
if (!CanPoisonType(gBattleScripting.battler, gEffectBattler)
|
||
&& (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
|
||
&& (primary == TRUE || certain == MOVE_EFFECT_CERTAIN))
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_PSNPrevention;
|
||
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT;
|
||
RESET_RETURN
|
||
}
|
||
if (!CanBePoisoned(gBattleScripting.battler, gEffectBattler))
|
||
break;
|
||
|
||
statusChanged = TRUE;
|
||
break;
|
||
case STATUS1_BURN:
|
||
if (gCurrentMove == MOVE_BURNING_JEALOUSY && !gProtectStructs[gEffectBattler].statRaised)
|
||
break;
|
||
|
||
if ((battlerAbility == ABILITY_WATER_VEIL || battlerAbility == ABILITY_WATER_BUBBLE)
|
||
&& (primary == TRUE || certain == MOVE_EFFECT_CERTAIN))
|
||
{
|
||
gLastUsedAbility = battlerAbility;
|
||
RecordAbilityBattle(gEffectBattler, battlerAbility);
|
||
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_BRNPrevention;
|
||
if (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_ABILITY_STATUS;
|
||
gHitMarker &= ~HITMARKER_STATUS_ABILITY_EFFECT;
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_STATUS;
|
||
}
|
||
RESET_RETURN
|
||
}
|
||
if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_FIRE)
|
||
&& (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
|
||
&& (primary == TRUE || certain == MOVE_EFFECT_CERTAIN))
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_BRNPrevention;
|
||
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT;
|
||
RESET_RETURN
|
||
}
|
||
|
||
if (B_STATUS_TYPE_IMMUNITY == GEN_1)
|
||
{
|
||
u8 moveType = 0;
|
||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||
if (primary == FALSE && certain != MOVE_EFFECT_CERTAIN && IS_BATTLER_OF_TYPE(gEffectBattler, moveType))
|
||
break;
|
||
}
|
||
|
||
if (!CanBeBurned(gEffectBattler))
|
||
break;
|
||
|
||
statusChanged = TRUE;
|
||
break;
|
||
case STATUS1_FREEZE:
|
||
if (B_STATUS_TYPE_IMMUNITY == GEN_1)
|
||
{
|
||
u8 moveType = 0;
|
||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||
if (primary == FALSE && certain != MOVE_EFFECT_CERTAIN && IS_BATTLER_OF_TYPE(gEffectBattler, moveType))
|
||
break;
|
||
}
|
||
if (!CanBeFrozen(gEffectBattler))
|
||
break;
|
||
|
||
cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler);
|
||
if (cancelMultiTurnMovesResult)
|
||
gBattlescriptCurrInstr = cancelMultiTurnMovesResult;
|
||
statusChanged = TRUE;
|
||
break;
|
||
case STATUS1_PARALYSIS:
|
||
if (battlerAbility == ABILITY_LIMBER)
|
||
{
|
||
if (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)
|
||
{
|
||
gLastUsedAbility = ABILITY_LIMBER;
|
||
RecordAbilityBattle(gEffectBattler, ABILITY_LIMBER);
|
||
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_PRLZPrevention;
|
||
|
||
if (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_ABILITY_STATUS;
|
||
gHitMarker &= ~HITMARKER_STATUS_ABILITY_EFFECT;
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_STATUS;
|
||
}
|
||
RESET_RETURN
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
if (B_STATUS_TYPE_IMMUNITY == GEN_1)
|
||
{
|
||
u8 moveType = 0;
|
||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||
if (primary == FALSE && certain != MOVE_EFFECT_CERTAIN && IS_BATTLER_OF_TYPE(gEffectBattler, moveType))
|
||
break;
|
||
}
|
||
if (!CanParalyzeType(gBattleScripting.battler, gEffectBattler)
|
||
&& (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
|
||
&& (primary == TRUE || certain == MOVE_EFFECT_CERTAIN))
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_PRLZPrevention;
|
||
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 2;
|
||
RESET_RETURN
|
||
}
|
||
if (!CanParalyzeType(gBattleScripting.battler, gEffectBattler))
|
||
break;
|
||
if (!CanBeParalyzed(gEffectBattler))
|
||
break;
|
||
|
||
statusChanged = TRUE;
|
||
break;
|
||
case STATUS1_TOXIC_POISON:
|
||
if ((battlerAbility == ABILITY_IMMUNITY || battlerAbility == ABILITY_PASTEL_VEIL)
|
||
&& (primary == TRUE || certain == MOVE_EFFECT_CERTAIN))
|
||
{
|
||
gLastUsedAbility = battlerAbility;
|
||
RecordAbilityBattle(gEffectBattler, battlerAbility);
|
||
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_PSNPrevention;
|
||
|
||
if (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_ABILITY_STATUS;
|
||
gHitMarker &= ~HITMARKER_STATUS_ABILITY_EFFECT;
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_STATUS;
|
||
}
|
||
RESET_RETURN
|
||
}
|
||
if (!CanPoisonType(gBattleScripting.battler, gEffectBattler)
|
||
&& (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
|
||
&& (primary == TRUE || certain == MOVE_EFFECT_CERTAIN))
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_PSNPrevention;
|
||
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT;
|
||
RESET_RETURN
|
||
}
|
||
if (gBattleMons[gEffectBattler].status1)
|
||
break;
|
||
if (CanBePoisoned(gBattleScripting.battler, gEffectBattler))
|
||
{
|
||
// It's redundant, because at this point we know the status1 value is 0.
|
||
gBattleMons[gEffectBattler].status1 &= ~STATUS1_TOXIC_POISON;
|
||
gBattleMons[gEffectBattler].status1 &= ~STATUS1_POISON;
|
||
statusChanged = TRUE;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE;
|
||
}
|
||
break;
|
||
case STATUS1_FROSTBITE:
|
||
if (B_STATUS_TYPE_IMMUNITY == GEN_1)
|
||
{
|
||
u8 moveType = 0;
|
||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||
if (primary == FALSE && certain != MOVE_EFFECT_CERTAIN && IS_BATTLER_OF_TYPE(gEffectBattler, moveType))
|
||
break;
|
||
}
|
||
if (!CanGetFrostbite(gEffectBattler))
|
||
break;
|
||
|
||
statusChanged = TRUE;
|
||
break;
|
||
}
|
||
if (statusChanged == TRUE)
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
|
||
if (sStatusFlagsForMoveEffects[gBattleScripting.moveEffect] == STATUS1_SLEEP)
|
||
{
|
||
if (B_SLEEP_TURNS >= GEN_5)
|
||
gBattleMons[gEffectBattler].status1 |= STATUS1_SLEEP_TURN(1 + RandomUniform(RNG_SLEEP_TURNS, 1, 3));
|
||
else
|
||
gBattleMons[gEffectBattler].status1 |= STATUS1_SLEEP_TURN(1 + RandomUniform(RNG_SLEEP_TURNS, 2, 5));
|
||
}
|
||
else
|
||
{
|
||
gBattleMons[gEffectBattler].status1 |= sStatusFlagsForMoveEffects[gBattleScripting.moveEffect];
|
||
}
|
||
|
||
gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleScripting.moveEffect];
|
||
|
||
BtlController_EmitSetMonData(gEffectBattler, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gEffectBattler].status1), &gBattleMons[gEffectBattler].status1);
|
||
MarkBattlerForControllerExec(gEffectBattler);
|
||
|
||
if (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUSED_BY_ABILITY;
|
||
gHitMarker &= ~HITMARKER_STATUS_ABILITY_EFFECT;
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUSED;
|
||
}
|
||
|
||
// for synchronize
|
||
|
||
if (gBattleScripting.moveEffect == MOVE_EFFECT_POISON
|
||
|| gBattleScripting.moveEffect == MOVE_EFFECT_TOXIC
|
||
|| gBattleScripting.moveEffect == MOVE_EFFECT_PARALYSIS
|
||
|| gBattleScripting.moveEffect == MOVE_EFFECT_BURN)
|
||
{
|
||
gBattleStruct->synchronizeMoveEffect = gBattleScripting.moveEffect;
|
||
gHitMarker |= HITMARKER_SYNCHRONISE_EFFECT;
|
||
}
|
||
return;
|
||
}
|
||
else if (statusChanged == FALSE)
|
||
{
|
||
gBattleScripting.moveEffect = 0;
|
||
gBattlescriptCurrInstr++;
|
||
return;
|
||
}
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
if (gBattleMons[gEffectBattler].status2 & sStatusFlagsForMoveEffects[gBattleScripting.moveEffect])
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
else
|
||
{
|
||
u8 side;
|
||
switch (gBattleScripting.moveEffect)
|
||
{
|
||
case MOVE_EFFECT_CONFUSION:
|
||
if (gCurrentMove == MOVE_ALLURING_VOICE && !gProtectStructs[gEffectBattler].statRaised)
|
||
break;
|
||
|
||
if (!CanBeConfused(gEffectBattler))
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
else
|
||
{
|
||
gBattleMons[gEffectBattler].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); // 2-5 turns
|
||
|
||
// If the confusion is activating due to being released from Sky Drop, go to "confused due to fatigue" script.
|
||
// Otherwise, do normal confusion script.
|
||
if(gCurrentMove == MOVE_SKY_DROP)
|
||
{
|
||
gBattleMons[gEffectBattler].status2 &= ~(STATUS2_LOCK_CONFUSE);
|
||
gBattlerAttacker = gEffectBattler;
|
||
gBattlescriptCurrInstr = BattleScript_ThrashConfuses;
|
||
}
|
||
else
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleScripting.moveEffect];
|
||
}
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_FLINCH:
|
||
if (battlerAbility == ABILITY_INNER_FOCUS
|
||
&& (primary == TRUE || certain == MOVE_EFFECT_CERTAIN))
|
||
{
|
||
gLastUsedAbility = ABILITY_INNER_FOCUS;
|
||
gBattlerAbility = gEffectBattler;
|
||
RecordAbilityBattle(gEffectBattler, ABILITY_INNER_FOCUS);
|
||
gBattlescriptCurrInstr = BattleScript_FlinchPrevention;
|
||
}
|
||
else if (GetBattlerTurnOrderNum(gEffectBattler) > gCurrentTurnActionNumber
|
||
&& !IsDynamaxed(gEffectBattler))
|
||
{
|
||
gBattleMons[gEffectBattler].status2 |= sStatusFlagsForMoveEffects[gBattleScripting.moveEffect];
|
||
}
|
||
gBattlescriptCurrInstr++;
|
||
break;
|
||
case MOVE_EFFECT_UPROAR:
|
||
if (!(gBattleMons[gEffectBattler].status2 & STATUS2_UPROAR))
|
||
{
|
||
gBattleMons[gEffectBattler].status2 |= STATUS2_MULTIPLETURNS;
|
||
gLockedMoves[gEffectBattler] = gCurrentMove;
|
||
gBattleMons[gEffectBattler].status2 |= STATUS2_UPROAR_TURN(B_UPROAR_TURNS >= GEN_5 ? 3 : (Random() & 3) + 2);
|
||
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleScripting.moveEffect];
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_PAYDAY:
|
||
// Don't scatter coins on the second hit of Parental Bond
|
||
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER && gSpecialStatuses[gBattlerAttacker].parentalBondState!= PARENTAL_BOND_2ND_HIT)
|
||
{
|
||
u16 payday = gPaydayMoney;
|
||
gPaydayMoney += (gBattleMons[gBattlerAttacker].level * 5);
|
||
if (payday > gPaydayMoney)
|
||
gPaydayMoney = 0xFFFF;
|
||
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_MoveEffectPayDay;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_HAPPY_HOUR:
|
||
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER && !gBattleStruct->moneyMultiplierMove)
|
||
{
|
||
gBattleStruct->moneyMultiplier *= 2;
|
||
gBattleStruct->moneyMultiplierMove = 1;
|
||
}
|
||
gBattlescriptCurrInstr++;
|
||
break;
|
||
case MOVE_EFFECT_TRI_ATTACK:
|
||
if (gBattleMons[gEffectBattler].status1)
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
else
|
||
{
|
||
static const u8 sTriAttackEffects[] =
|
||
{
|
||
MOVE_EFFECT_BURN,
|
||
B_USE_FROSTBITE == TRUE ? MOVE_EFFECT_FROSTBITE : MOVE_EFFECT_FREEZE,
|
||
MOVE_EFFECT_PARALYSIS
|
||
};
|
||
gBattleScripting.moveEffect = RandomElement(RNG_TRI_ATTACK, sTriAttackEffects);
|
||
SetMoveEffect(FALSE, 0);
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_CHARGING:
|
||
gBattleMons[gEffectBattler].status2 |= STATUS2_MULTIPLETURNS;
|
||
gLockedMoves[gEffectBattler] = gCurrentMove;
|
||
gProtectStructs[gEffectBattler].chargingTurn = TRUE;
|
||
gBattlescriptCurrInstr++;
|
||
break;
|
||
case MOVE_EFFECT_WRAP:
|
||
if (gBattleMons[gEffectBattler].status2 & STATUS2_WRAPPED)
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
else
|
||
{
|
||
gBattleMons[gEffectBattler].status2 |= STATUS2_WRAPPED;
|
||
if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_GRIP_CLAW)
|
||
gDisableStructs[gEffectBattler].wrapTurns = B_BINDING_TURNS >= GEN_5 ? 7 : 5;
|
||
else
|
||
gDisableStructs[gEffectBattler].wrapTurns = B_BINDING_TURNS >= GEN_5 ? (Random() % 2) + 4 : (Random() % 4) + 2;
|
||
|
||
gBattleStruct->wrappedMove[gEffectBattler] = gCurrentMove;
|
||
gBattleStruct->wrappedBy[gEffectBattler] = gBattlerAttacker;
|
||
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleScripting.moveEffect];
|
||
|
||
for (gBattleCommunication[MULTISTRING_CHOOSER] = 0; gBattleCommunication[MULTISTRING_CHOOSER] < NUM_TRAPPING_MOVES; gBattleCommunication[MULTISTRING_CHOOSER]++)
|
||
{
|
||
if (sTrappingMoves[gBattleCommunication[MULTISTRING_CHOOSER]] == gCurrentMove)
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_ATK_PLUS_1:
|
||
case MOVE_EFFECT_DEF_PLUS_1:
|
||
case MOVE_EFFECT_SPD_PLUS_1:
|
||
case MOVE_EFFECT_SP_ATK_PLUS_1:
|
||
case MOVE_EFFECT_SP_DEF_PLUS_1:
|
||
case MOVE_EFFECT_ACC_PLUS_1:
|
||
case MOVE_EFFECT_EVS_PLUS_1:
|
||
if (NoAliveMonsForEitherParty()
|
||
|| ChangeStatBuffs(SET_STAT_BUFF_VALUE(1),
|
||
gBattleScripting.moveEffect - MOVE_EFFECT_ATK_PLUS_1 + 1,
|
||
affectsUser | STAT_CHANGE_UPDATE_MOVE_EFFECT, 0))
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
else
|
||
{
|
||
gBattleScripting.animArg1 = gBattleScripting.moveEffect & ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN);
|
||
gBattleScripting.animArg2 = 0;
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_StatUp;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_ATK_MINUS_1:
|
||
case MOVE_EFFECT_DEF_MINUS_1:
|
||
case MOVE_EFFECT_SPD_MINUS_1:
|
||
case MOVE_EFFECT_SP_ATK_MINUS_1:
|
||
case MOVE_EFFECT_SP_DEF_MINUS_1:
|
||
case MOVE_EFFECT_ACC_MINUS_1:
|
||
case MOVE_EFFECT_EVS_MINUS_1:
|
||
flags = affectsUser;
|
||
if (mirrorArmorReflected && !affectsUser)
|
||
flags |= STAT_CHANGE_ALLOW_PTR;
|
||
|
||
if (ChangeStatBuffs(SET_STAT_BUFF_VALUE(1) | STAT_BUFF_NEGATIVE,
|
||
gBattleScripting.moveEffect - MOVE_EFFECT_ATK_MINUS_1 + 1,
|
||
flags | STAT_CHANGE_UPDATE_MOVE_EFFECT, gBattlescriptCurrInstr + 1))
|
||
{
|
||
if (!mirrorArmorReflected)
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
else
|
||
{
|
||
gBattleScripting.animArg1 = gBattleScripting.moveEffect & ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN);
|
||
gBattleScripting.animArg2 = 0;
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_StatDown;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_ATK_PLUS_2:
|
||
case MOVE_EFFECT_DEF_PLUS_2:
|
||
case MOVE_EFFECT_SPD_PLUS_2:
|
||
case MOVE_EFFECT_SP_ATK_PLUS_2:
|
||
case MOVE_EFFECT_SP_DEF_PLUS_2:
|
||
case MOVE_EFFECT_ACC_PLUS_2:
|
||
case MOVE_EFFECT_EVS_PLUS_2:
|
||
if (NoAliveMonsForEitherParty()
|
||
|| ChangeStatBuffs(SET_STAT_BUFF_VALUE(2),
|
||
gBattleScripting.moveEffect - MOVE_EFFECT_ATK_PLUS_2 + 1,
|
||
affectsUser | STAT_CHANGE_UPDATE_MOVE_EFFECT, 0))
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
else
|
||
{
|
||
gBattleScripting.animArg1 = gBattleScripting.moveEffect & ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN);
|
||
gBattleScripting.animArg2 = 0;
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_StatUp;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_ATK_MINUS_2:
|
||
case MOVE_EFFECT_DEF_MINUS_2:
|
||
case MOVE_EFFECT_SPD_MINUS_2:
|
||
case MOVE_EFFECT_SP_ATK_MINUS_2:
|
||
case MOVE_EFFECT_SP_DEF_MINUS_2:
|
||
case MOVE_EFFECT_ACC_MINUS_2:
|
||
case MOVE_EFFECT_EVS_MINUS_2:
|
||
flags = affectsUser;
|
||
if (mirrorArmorReflected && !affectsUser)
|
||
flags |= STAT_CHANGE_ALLOW_PTR;
|
||
if (ChangeStatBuffs(SET_STAT_BUFF_VALUE(2) | STAT_BUFF_NEGATIVE,
|
||
gBattleScripting.moveEffect - MOVE_EFFECT_ATK_MINUS_2 + 1,
|
||
flags | STAT_CHANGE_UPDATE_MOVE_EFFECT, gBattlescriptCurrInstr + 1))
|
||
{
|
||
if (!mirrorArmorReflected)
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
else
|
||
{
|
||
gBattleScripting.animArg1 = gBattleScripting.moveEffect & ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN);
|
||
gBattleScripting.animArg2 = 0;
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_StatDown;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_RECHARGE:
|
||
gBattleMons[gEffectBattler].status2 |= STATUS2_RECHARGE;
|
||
gDisableStructs[gEffectBattler].rechargeTimer = 2;
|
||
gLockedMoves[gEffectBattler] = gCurrentMove;
|
||
gBattlescriptCurrInstr++;
|
||
break;
|
||
case MOVE_EFFECT_RAGE:
|
||
gBattleMons[gBattlerAttacker].status2 |= STATUS2_RAGE;
|
||
gBattlescriptCurrInstr++;
|
||
break;
|
||
case MOVE_EFFECT_STEAL_ITEM:
|
||
{
|
||
if (!CanStealItem(gBattlerAttacker, gBattlerTarget, gBattleMons[gBattlerTarget].item))
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
break;
|
||
}
|
||
|
||
side = GetBattlerSide(gBattlerAttacker);
|
||
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT
|
||
&& !(gBattleTypeFlags &
|
||
(BATTLE_TYPE_EREADER_TRAINER
|
||
| BATTLE_TYPE_FRONTIER
|
||
| BATTLE_TYPE_LINK
|
||
| BATTLE_TYPE_RECORDED_LINK
|
||
| BATTLE_TYPE_SECRET_BASE)))
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
else if (!(gBattleTypeFlags &
|
||
(BATTLE_TYPE_EREADER_TRAINER
|
||
| BATTLE_TYPE_FRONTIER
|
||
| BATTLE_TYPE_LINK
|
||
| BATTLE_TYPE_RECORDED_LINK
|
||
| BATTLE_TYPE_SECRET_BASE))
|
||
&& (gWishFutureKnock.knockedOffMons[side] & gBitTable[gBattlerPartyIndexes[gBattlerAttacker]]))
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
else if (gBattleMons[gBattlerTarget].item
|
||
&& GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD)
|
||
{
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_NoItemSteal;
|
||
|
||
gLastUsedAbility = gBattleMons[gBattlerTarget].ability;
|
||
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
|
||
}
|
||
else if (gBattleMons[gBattlerAttacker].item != ITEM_NONE
|
||
|| gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY_E_READER
|
||
|| gBattleMons[gBattlerTarget].item == ITEM_NONE)
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
else
|
||
{
|
||
StealTargetItem(gBattlerAttacker, gBattlerTarget); // Attacker steals target item
|
||
gBattleMons[gBattlerAttacker].item = ITEM_NONE; // Item assigned later on with thief (see MOVEEND_CHANGED_ITEMS)
|
||
gBattleStruct->changedItems[gBattlerAttacker] = gLastUsedItem; // Stolen item to be assigned later
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_ItemSteal;
|
||
}
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_PREVENT_ESCAPE:
|
||
gBattleMons[gBattlerTarget].status2 |= STATUS2_ESCAPE_PREVENTION;
|
||
gDisableStructs[gBattlerTarget].battlerPreventingEscape = gBattlerAttacker;
|
||
gBattlescriptCurrInstr++;
|
||
break;
|
||
case MOVE_EFFECT_NIGHTMARE:
|
||
gBattleMons[gBattlerTarget].status2 |= STATUS2_NIGHTMARE;
|
||
gBattlescriptCurrInstr++;
|
||
break;
|
||
case MOVE_EFFECT_ALL_STATS_UP:
|
||
if (!NoAliveMonsForEitherParty())
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_AllStatsUp;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_RAPIDSPIN:
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_RapidSpinAway;
|
||
break;
|
||
case MOVE_EFFECT_ATK_DEF_DOWN: // SuperPower
|
||
if (!NoAliveMonsForEitherParty())
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_AtkDefDown;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_DEF_SPDEF_DOWN: // Close Combat
|
||
if (!NoAliveMonsForEitherParty())
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_DefSpDefDown;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_RECOIL_HP_25: // Struggle
|
||
gBattleMoveDamage = (gBattleMons[gEffectBattler].maxHP) / 4;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
if (GetBattlerAbility(gEffectBattler) == ABILITY_PARENTAL_BOND)
|
||
gBattleMoveDamage *= 2;
|
||
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil;
|
||
break;
|
||
case MOVE_EFFECT_THRASH:
|
||
if (gBattleMons[gEffectBattler].status2 & STATUS2_LOCK_CONFUSE)
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
else
|
||
{
|
||
gBattleMons[gEffectBattler].status2 |= STATUS2_MULTIPLETURNS;
|
||
gLockedMoves[gEffectBattler] = gCurrentMove;
|
||
gBattleMons[gEffectBattler].status2 |= STATUS2_LOCK_CONFUSE_TURN(RandomUniform(RNG_RAMPAGE_TURNS, 2, 3));
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_SP_ATK_TWO_DOWN: // Overheat
|
||
if (!NoAliveMonsForEitherParty())
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_SAtkDown2;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_CLEAR_SMOG:
|
||
for (i = 0; i < NUM_BATTLE_STATS; i++)
|
||
{
|
||
if (gBattleMons[gEffectBattler].statStages[i] != DEFAULT_STAT_STAGE)
|
||
break;
|
||
}
|
||
if ((gSpecialStatuses[gEffectBattler].physicalDmg || gSpecialStatuses[gEffectBattler].specialDmg) && i != NUM_BATTLE_STATS)
|
||
{
|
||
for (i = 0; i < NUM_BATTLE_STATS; i++)
|
||
gBattleMons[gEffectBattler].statStages[i] = DEFAULT_STAT_STAGE;
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_MoveEffectClearSmog;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_FLAME_BURST:
|
||
if (IsBattlerAlive(BATTLE_PARTNER(gBattlerTarget))
|
||
&& !(gStatuses3[BATTLE_PARTNER(gBattlerTarget)] & STATUS3_SEMI_INVULNERABLE)
|
||
&& GetBattlerAbility(BATTLE_PARTNER(gBattlerTarget)) != ABILITY_MAGIC_GUARD)
|
||
{
|
||
gBattleScripting.savedBattler = BATTLE_PARTNER(gBattlerTarget);
|
||
gBattleMoveDamage = gBattleMons[BATTLE_PARTNER(gBattlerTarget)].hp / 16;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
gBattlescriptCurrInstr = BattleScript_MoveEffectFlameBurst;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_FEINT:
|
||
if (IS_BATTLER_PROTECTED(gBattlerTarget))
|
||
{
|
||
gProtectStructs[gBattlerTarget].protected = FALSE;
|
||
gSideStatuses[GetBattlerSide(gBattlerTarget)] &= ~SIDE_STATUS_WIDE_GUARD;
|
||
gSideStatuses[GetBattlerSide(gBattlerTarget)] &= ~SIDE_STATUS_QUICK_GUARD;
|
||
gSideStatuses[GetBattlerSide(gBattlerTarget)] &= ~SIDE_STATUS_CRAFTY_SHIELD;
|
||
gSideStatuses[GetBattlerSide(gBattlerTarget)] &= ~SIDE_STATUS_MAT_BLOCK;
|
||
gProtectStructs[gBattlerTarget].spikyShielded = FALSE;
|
||
gProtectStructs[gBattlerTarget].kingsShielded = FALSE;
|
||
gProtectStructs[gBattlerTarget].banefulBunkered = FALSE;
|
||
gProtectStructs[gBattlerTarget].obstructed = FALSE;
|
||
gProtectStructs[gBattlerTarget].silkTrapped = FALSE;
|
||
gProtectStructs[gBattlerAttacker].burningBulwarked = FALSE;
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
if (gCurrentMove == MOVE_HYPERSPACE_FURY)
|
||
gBattlescriptCurrInstr = BattleScript_HyperspaceFuryRemoveProtect;
|
||
else
|
||
gBattlescriptCurrInstr = BattleScript_MoveEffectFeint;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_SPECTRAL_THIEF:
|
||
if (!NoAliveMonsForEitherParty())
|
||
{
|
||
gBattleStruct->stolenStats[0] = 0; // Stats to steal.
|
||
gBattleScripting.animArg1 = 0;
|
||
for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++)
|
||
{
|
||
if (gBattleMons[gBattlerTarget].statStages[i] > DEFAULT_STAT_STAGE && gBattleMons[gBattlerAttacker].statStages[i] != MAX_STAT_STAGE)
|
||
{
|
||
bool32 byTwo = FALSE;
|
||
|
||
gBattleStruct->stolenStats[0] |= gBitTable[i];
|
||
// Store by how many stages to raise the stat.
|
||
gBattleStruct->stolenStats[i] = gBattleMons[gBattlerTarget].statStages[i] - DEFAULT_STAT_STAGE;
|
||
while (gBattleMons[gBattlerAttacker].statStages[i] + gBattleStruct->stolenStats[i] > MAX_STAT_STAGE)
|
||
gBattleStruct->stolenStats[i]--;
|
||
gBattleMons[gBattlerTarget].statStages[i] = DEFAULT_STAT_STAGE;
|
||
|
||
if (gBattleStruct->stolenStats[i] >= 2)
|
||
byTwo++;
|
||
|
||
if (gBattleScripting.animArg1 == 0)
|
||
{
|
||
if (byTwo)
|
||
gBattleScripting.animArg1 = STAT_ANIM_PLUS2 + i;
|
||
else
|
||
gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + i;
|
||
}
|
||
else
|
||
{
|
||
if (byTwo)
|
||
gBattleScripting.animArg1 = STAT_ANIM_MULTIPLE_PLUS2;
|
||
else
|
||
gBattleScripting.animArg1 = STAT_ANIM_MULTIPLE_PLUS1;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (gBattleStruct->stolenStats[0] != 0)
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_SpectralThiefSteal;
|
||
}
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_V_CREATE:
|
||
if (!NoAliveMonsForEitherParty())
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_VCreateStatLoss;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_CORE_ENFORCER:
|
||
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)
|
||
&& !NoAliveMonsForEitherParty())
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_MoveEffectCoreEnforcer;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_THROAT_CHOP:
|
||
gDisableStructs[gEffectBattler].throatChopTimer = 2;
|
||
gBattlescriptCurrInstr++;
|
||
break;
|
||
case MOVE_EFFECT_INCINERATE:
|
||
if ((gBattleMons[gEffectBattler].item >= FIRST_BERRY_INDEX && gBattleMons[gEffectBattler].item <= LAST_BERRY_INDEX)
|
||
|| (B_INCINERATE_GEMS >= GEN_6 && GetBattlerHoldEffect(gEffectBattler, FALSE) == HOLD_EFFECT_GEMS))
|
||
{
|
||
gLastUsedItem = gBattleMons[gEffectBattler].item;
|
||
gBattleMons[gEffectBattler].item = 0;
|
||
CheckSetUnburden(gEffectBattler);
|
||
|
||
BtlController_EmitSetMonData(gEffectBattler, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gEffectBattler].item), &gBattleMons[gEffectBattler].item);
|
||
MarkBattlerForControllerExec(gEffectBattler);
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_MoveEffectIncinerate;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_BUG_BITE:
|
||
if (ItemId_GetPocket(gBattleMons[gEffectBattler].item) == POCKET_BERRIES
|
||
&& battlerAbility != ABILITY_STICKY_HOLD)
|
||
{
|
||
// target loses their berry
|
||
gLastUsedItem = gBattleMons[gEffectBattler].item;
|
||
gBattleMons[gEffectBattler].item = 0;
|
||
CheckSetUnburden(gEffectBattler);
|
||
|
||
BtlController_EmitSetMonData(gEffectBattler, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gEffectBattler].item), &gBattleMons[gEffectBattler].item);
|
||
MarkBattlerForControllerExec(gEffectBattler);
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_MoveEffectBugBite;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_TRAP_BOTH:
|
||
if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_ESCAPE_PREVENTION) && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_ESCAPE_PREVENTION))
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_BothCanNoLongerEscape;
|
||
}
|
||
if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_ESCAPE_PREVENTION))
|
||
gDisableStructs[gBattlerTarget].battlerPreventingEscape = gBattlerAttacker;
|
||
|
||
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_ESCAPE_PREVENTION))
|
||
gDisableStructs[gBattlerAttacker].battlerPreventingEscape = gBattlerTarget;
|
||
|
||
gBattleMons[gBattlerTarget].status2 |= STATUS2_ESCAPE_PREVENTION;
|
||
gBattleMons[gBattlerAttacker].status2 |= STATUS2_ESCAPE_PREVENTION;
|
||
break;
|
||
case MOVE_EFFECT_BURN_UP:
|
||
// This seems unnecessary but is done to make it work properly with Parental Bond
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_BurnUpRemoveType;
|
||
break;
|
||
case MOVE_EFFECT_DOUBLE_SHOCK:
|
||
// This seems unnecessary but is done to make it work properly with Parental Bond
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_DoubleShockRemoveType;
|
||
break;
|
||
case MOVE_EFFECT_ROUND:
|
||
TryUpdateRoundTurnOrder(); // If another Pokémon uses Round before the user this turn, the user will use Round directly after it
|
||
gBattlescriptCurrInstr++;
|
||
break;
|
||
case MOVE_EFFECT_DIRE_CLAW:
|
||
if (!gBattleMons[gEffectBattler].status1)
|
||
{
|
||
static const u8 sDireClawEffects[] = { MOVE_EFFECT_POISON, MOVE_EFFECT_PARALYSIS, MOVE_EFFECT_SLEEP };
|
||
gBattleScripting.moveEffect = RandomElement(RNG_DIRE_CLAW, sDireClawEffects);
|
||
SetMoveEffect(TRUE, 0);
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_STEALTH_ROCK:
|
||
if (!(gSideStatuses[GetBattlerSide(gEffectBattler)] & SIDE_STATUS_STEALTH_ROCK))
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_POINTEDSTONESFLOAT;
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_StealthRockActivates;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_SPIKES:
|
||
if (gSideTimers[GetBattlerSide(gEffectBattler)].spikesAmount < 3)
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SPIKESSCATTERED;
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
|
||
if (gBattleStruct->isSkyBattle)
|
||
gBattlescriptCurrInstr++;
|
||
else
|
||
gBattlescriptCurrInstr = BattleScript_SpikesActivates;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_TRIPLE_ARROWS:
|
||
{
|
||
u8 randomLowerDefenseChance = RandomPercentage(RNG_TRIPLE_ARROWS_DEFENSE_DOWN, CalcSecondaryEffectChance(gBattlerAttacker, 50, EFFECT_DEFENSE_DOWN_HIT));
|
||
u8 randomFlinchChance = RandomPercentage(RNG_TRIPLE_ARROWS_FLINCH, CalcSecondaryEffectChance(gBattlerAttacker, 30, EFFECT_FLINCH_HIT));
|
||
|
||
if (randomFlinchChance && battlerAbility != ABILITY_INNER_FOCUS && GetBattlerTurnOrderNum(gEffectBattler) > gCurrentTurnActionNumber)
|
||
gBattleMons[gEffectBattler].status2 |= sStatusFlagsForMoveEffects[MOVE_EFFECT_FLINCH];
|
||
|
||
if (randomLowerDefenseChance)
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_DefDown;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr++;
|
||
}
|
||
}
|
||
|
||
break;
|
||
case MOVE_EFFECT_SYRUP_BOMB:
|
||
if (!(gStatuses4[gEffectBattler] & STATUS4_SYRUP_BOMB))
|
||
{
|
||
struct Pokemon *party = GetBattlerParty(gBattlerAttacker);
|
||
|
||
gStatuses4[gEffectBattler] |= STATUS4_SYRUP_BOMB;
|
||
gDisableStructs[gEffectBattler].syrupBombTimer = 3;
|
||
gDisableStructs[gEffectBattler].syrupBombIsShiny = IsMonShiny(&party[gBattlerPartyIndexes[gBattlerAttacker]]);
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_SyrupBombActivates;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
gBattleScripting.moveEffect = 0;
|
||
}
|
||
|
||
static void Cmd_seteffectwithchance(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u32 percentChance = CalcSecondaryEffectChance(gBattlerAttacker, gBattleMoves[gCurrentMove].secondaryEffectChance, gBattleMoves[gCurrentMove].effect);
|
||
|
||
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
&& gBattleScripting.moveEffect)
|
||
{
|
||
if (gBattleScripting.moveEffect & MOVE_EFFECT_CERTAIN
|
||
|| percentChance >= 100)
|
||
{
|
||
gBattleScripting.moveEffect &= ~MOVE_EFFECT_CERTAIN;
|
||
SetMoveEffect(FALSE, MOVE_EFFECT_CERTAIN);
|
||
}
|
||
else if (RandomPercentage(RNG_SECONDARY_EFFECT, percentChance))
|
||
{
|
||
SetMoveEffect(FALSE, 0);
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
gBattleScripting.moveEffect = 0;
|
||
gBattleScripting.multihitMoveEffect = 0;
|
||
}
|
||
|
||
static void Cmd_seteffectprimary(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
SetMoveEffect(TRUE, 0);
|
||
}
|
||
|
||
static void Cmd_seteffectsecondary(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
SetMoveEffect(FALSE, 0);
|
||
}
|
||
|
||
static void Cmd_clearstatusfromeffect(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
|
||
if (gBattleScripting.moveEffect <= PRIMARY_STATUS_MOVE_EFFECT)
|
||
gBattleMons[battler].status1 &= (~sStatusFlagsForMoveEffects[gBattleScripting.moveEffect]);
|
||
else
|
||
gBattleMons[battler].status2 &= (~sStatusFlagsForMoveEffects[gBattleScripting.moveEffect]);
|
||
|
||
gBattleScripting.moveEffect = 0;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
gBattleScripting.multihitMoveEffect = 0;
|
||
}
|
||
|
||
static void Cmd_tryfaintmon(void)
|
||
{
|
||
CMD_ARGS(u8 battler, bool8 isSpikes, const u8 *instr);
|
||
u32 battler, destinyBondBattler;
|
||
const u8 *faintScript;
|
||
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (cmd->isSpikes != 0)
|
||
{
|
||
if (gHitMarker & HITMARKER_FAINTED(battler))
|
||
{
|
||
BattleScriptPop();
|
||
gBattlescriptCurrInstr = cmd->instr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (cmd->battler == BS_ATTACKER)
|
||
{
|
||
destinyBondBattler = gBattlerTarget;
|
||
faintScript = BattleScript_FaintAttacker;
|
||
}
|
||
else
|
||
{
|
||
destinyBondBattler = gBattlerAttacker;
|
||
faintScript = BattleScript_FaintTarget;
|
||
}
|
||
if (!(gAbsentBattlerFlags & gBitTable[battler])
|
||
&& gBattleMons[battler].hp == 0)
|
||
{
|
||
gHitMarker |= HITMARKER_FAINTED(battler);
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = faintScript;
|
||
if (GetBattlerSide(battler) == B_SIDE_PLAYER)
|
||
{
|
||
gHitMarker |= HITMARKER_PLAYER_FAINTED;
|
||
if (gBattleResults.playerFaintCounter < 255)
|
||
gBattleResults.playerFaintCounter++;
|
||
AdjustFriendshipOnBattleFaint(battler);
|
||
gSideTimers[B_SIDE_PLAYER].retaliateTimer = 2;
|
||
}
|
||
else
|
||
{
|
||
if (gBattleResults.opponentFaintCounter < 255)
|
||
gBattleResults.opponentFaintCounter++;
|
||
gBattleResults.lastOpponentSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES, NULL);
|
||
gSideTimers[B_SIDE_OPPONENT].retaliateTimer = 2;
|
||
}
|
||
if ((gHitMarker & HITMARKER_DESTINYBOND) && gBattleMons[gBattlerAttacker].hp != 0
|
||
&& !IsDynamaxed(gBattlerAttacker))
|
||
{
|
||
gHitMarker &= ~HITMARKER_DESTINYBOND;
|
||
BattleScriptPush(gBattlescriptCurrInstr);
|
||
gBattleMoveDamage = gBattleMons[destinyBondBattler].hp;
|
||
gBattlescriptCurrInstr = BattleScript_DestinyBondTakesLife;
|
||
}
|
||
if ((gStatuses3[gBattlerTarget] & STATUS3_GRUDGE)
|
||
&& !(gHitMarker & HITMARKER_GRUDGE)
|
||
&& GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)
|
||
&& gBattleMons[gBattlerAttacker].hp != 0
|
||
&& gCurrentMove != MOVE_STRUGGLE)
|
||
{
|
||
u8 moveIndex = *(gBattleStruct->chosenMovePositions + gBattlerAttacker);
|
||
|
||
gBattleMons[gBattlerAttacker].pp[moveIndex] = 0;
|
||
BattleScriptPush(gBattlescriptCurrInstr);
|
||
gBattlescriptCurrInstr = BattleScript_GrudgeTakesPp;
|
||
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, moveIndex + REQUEST_PPMOVE1_BATTLE, 0, sizeof(gBattleMons[gBattlerAttacker].pp[moveIndex]), &gBattleMons[gBattlerAttacker].pp[moveIndex]);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].moves[moveIndex])
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void Cmd_dofaintanimation(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
BtlController_EmitFaintAnimation(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_cleareffectsonfaint(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
const u8 *clearDataResult = NULL;
|
||
if (!(gBattleTypeFlags & BATTLE_TYPE_ARENA) || gBattleMons[battler].hp == 0)
|
||
{
|
||
gBattleMons[battler].status1 = 0;
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[battler].status1), &gBattleMons[battler].status1);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
|
||
clearDataResult = FaintClearSetData(battler); // Effects like attractions, trapping, etc.
|
||
if (clearDataResult)
|
||
gBattlescriptCurrInstr = clearDataResult;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_jumpifstatus(void)
|
||
{
|
||
CMD_ARGS(u8 battler, u32 flags, const u8 *jumpInstr);
|
||
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
u32 flags = cmd->flags;
|
||
const u8 *jumpInstr = cmd->jumpInstr;
|
||
|
||
if (gBattleMons[battler].status1 & flags && gBattleMons[battler].hp != 0)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_jumpifstatus2(void)
|
||
{
|
||
CMD_ARGS(u8 battler, u32 flags, const u8 *jumpInstr);
|
||
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
u32 flags = cmd->flags;
|
||
const u8 *jumpInstr = cmd->jumpInstr;
|
||
|
||
if (gBattleMons[battler].status2 & flags && gBattleMons[battler].hp != 0)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_jumpifability(void)
|
||
{
|
||
CMD_ARGS(u8 battler, u16 ability, const u8 *jumpInstr);
|
||
|
||
u32 battler;
|
||
bool32 hasAbility = FALSE;
|
||
u32 ability = cmd->ability;
|
||
|
||
switch (cmd->battler)
|
||
{
|
||
default:
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (GetBattlerAbility(battler) == ability)
|
||
hasAbility = TRUE;
|
||
break;
|
||
case BS_ATTACKER_SIDE:
|
||
battler = IsAbilityOnSide(gBattlerAttacker, ability);
|
||
if (battler)
|
||
{
|
||
battler--;
|
||
hasAbility = TRUE;
|
||
}
|
||
break;
|
||
case BS_TARGET_SIDE:
|
||
battler = IsAbilityOnOpposingSide(gBattlerAttacker, ability);
|
||
if (battler)
|
||
{
|
||
battler--;
|
||
hasAbility = TRUE;
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (hasAbility)
|
||
{
|
||
gLastUsedAbility = ability;
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
RecordAbilityBattle(battler, gLastUsedAbility);
|
||
gBattlerAbility = battler;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_jumpifsideaffecting(void)
|
||
{
|
||
CMD_ARGS(u8 battler, u32 flags, const u8 *jumpInstr);
|
||
|
||
u32 side = GetBattlerSide(GetBattlerForBattleScript(cmd->battler));
|
||
|
||
if (gSideStatuses[side] & cmd->flags)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_jumpifstat(void)
|
||
{
|
||
CMD_ARGS(u8 battler, u8 comparison, u8 stat, u8 value, const u8 *jumpInstr);
|
||
|
||
bool32 ret = 0;
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
u8 stat = cmd->stat;
|
||
u8 value = cmd->value;
|
||
u8 comparison = cmd->comparison;
|
||
|
||
ret = CompareStat(battler, stat, value, comparison);
|
||
|
||
if (ret)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_jumpifstatus3condition(void)
|
||
{
|
||
CMD_ARGS(u8 battler, u32 flags, bool8 jumpIfTrue, const u8 *jumpInstr);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (cmd->jumpIfTrue)
|
||
{
|
||
if ((gStatuses3[battler] & cmd->flags) != 0)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
}
|
||
else
|
||
{
|
||
if ((gStatuses3[battler] & cmd->flags) != 0)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_jumpbasedontype(void)
|
||
{
|
||
CMD_ARGS(u8 battler, u8 type, u8 jumpIfType, const u8 *jumpInstr);
|
||
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
u8 type = cmd->type;
|
||
const u8 *jumpInstr = cmd->jumpInstr;
|
||
|
||
// jumpiftype
|
||
if (cmd->jumpIfType)
|
||
{
|
||
if (IS_BATTLER_OF_TYPE(battler, type))
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
// jumpifnottype
|
||
else
|
||
{
|
||
if (!IS_BATTLER_OF_TYPE(battler, type))
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
FEATURE_FLAG_ASSERT(I_EXP_SHARE_FLAG, YouNeedToSetTheExpShareFlagToAnUnusedFlag);
|
||
|
||
static bool32 BattleTypeAllowsExp(void)
|
||
{
|
||
if (RECORDED_WILD_BATTLE)
|
||
return TRUE;
|
||
else if (gBattleTypeFlags &
|
||
( BATTLE_TYPE_LINK
|
||
| BATTLE_TYPE_RECORDED_LINK
|
||
| BATTLE_TYPE_TRAINER_HILL
|
||
| BATTLE_TYPE_FRONTIER
|
||
| BATTLE_TYPE_SAFARI
|
||
| BATTLE_TYPE_BATTLE_TOWER
|
||
| BATTLE_TYPE_EREADER_TRAINER))
|
||
return FALSE;
|
||
else
|
||
return TRUE;
|
||
}
|
||
|
||
static u32 GetMonHoldEffect(struct Pokemon *mon)
|
||
{
|
||
u32 holdEffect;
|
||
u32 item = GetMonData(mon, MON_DATA_HELD_ITEM);
|
||
|
||
if (item == ITEM_ENIGMA_BERRY_E_READER)
|
||
holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
|
||
else
|
||
holdEffect = ItemId_GetHoldEffect(item);
|
||
|
||
return holdEffect;
|
||
}
|
||
|
||
static void Cmd_getexp(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 holdEffect;
|
||
s32 i; // also used as stringId
|
||
u8 *expMonId = &gBattleStruct->expGetterMonId;
|
||
|
||
gBattlerFainted = GetBattlerForBattleScript(cmd->battler);
|
||
|
||
switch (gBattleScripting.getexpState)
|
||
{
|
||
case 0: // check if should receive exp at all
|
||
if (GetBattlerSide(gBattlerFainted) != B_SIDE_OPPONENT
|
||
|| IsAiVsAiBattle()
|
||
|| !BattleTypeAllowsExp())
|
||
{
|
||
gBattleScripting.getexpState = 6; // goto last case
|
||
}
|
||
else
|
||
{
|
||
gBattleScripting.getexpState++;
|
||
gBattleStruct->givenExpMons |= gBitTable[gBattlerPartyIndexes[gBattlerFainted]];
|
||
}
|
||
break;
|
||
case 1: // calculate experience points to redistribute
|
||
{
|
||
u32 orderId = 0;
|
||
u32 calculatedExp = 0;
|
||
u32 *exp = &gBattleStruct->expValue;
|
||
u32 sentInBits = gSentPokesToOpponent[(gBattlerFainted & 2) >> 1];
|
||
u32 expShareBits = 0;
|
||
s32 viaSentIn = 0;
|
||
s32 viaExpShare = 0;
|
||
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
if (!IsValidForBattle(&gPlayerParty[i]))
|
||
continue;
|
||
if (gBitTable[i] & sentInBits)
|
||
viaSentIn++;
|
||
|
||
holdEffect = GetMonHoldEffect(&gPlayerParty[i]);
|
||
if (holdEffect == HOLD_EFFECT_EXP_SHARE || IsGen6ExpShareEnabled())
|
||
{
|
||
expShareBits |= gBitTable[i];
|
||
viaExpShare++;
|
||
}
|
||
}
|
||
// Get order of mons getting exp: 1. all mons via sent in, 2. all mons via exp share
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
if (gBitTable[i] & sentInBits)
|
||
gBattleStruct->expGettersOrder[orderId++] = i;
|
||
}
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
if (!(gBitTable[i] & sentInBits) && gBitTable[i] & expShareBits)
|
||
gBattleStruct->expGettersOrder[orderId++] = i;
|
||
}
|
||
if (orderId < PARTY_SIZE)
|
||
gBattleStruct->expGettersOrder[orderId] = PARTY_SIZE;
|
||
|
||
calculatedExp = gSpeciesInfo[gBattleMons[gBattlerFainted].species].expYield * gBattleMons[gBattlerFainted].level;
|
||
if (B_SCALED_EXP >= GEN_5 && B_SCALED_EXP != GEN_6)
|
||
calculatedExp /= 5;
|
||
else
|
||
calculatedExp /= 7;
|
||
|
||
if (B_TRAINER_EXP_MULTIPLIER <= GEN_7 && gBattleTypeFlags & BATTLE_TYPE_TRAINER)
|
||
calculatedExp = (calculatedExp * 150) / 100;
|
||
|
||
if (B_SPLIT_EXP < GEN_6)
|
||
{
|
||
if (viaExpShare) // at least one mon is getting exp via exp share
|
||
{
|
||
*exp = SAFE_DIV(calculatedExp / 2, viaSentIn);
|
||
if (*exp == 0)
|
||
*exp = 1;
|
||
|
||
gBattleStruct->expShareExpValue = calculatedExp / 2 / viaExpShare;
|
||
if (gBattleStruct->expShareExpValue == 0)
|
||
gBattleStruct->expShareExpValue = 1;
|
||
}
|
||
else
|
||
{
|
||
*exp = SAFE_DIV(calculatedExp, viaSentIn);
|
||
if (*exp == 0)
|
||
*exp = 1;
|
||
gBattleStruct->expShareExpValue = 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
*exp = calculatedExp;
|
||
gBattleStruct->expShareExpValue = calculatedExp / 2;
|
||
if (gBattleStruct->expShareExpValue == 0)
|
||
gBattleStruct->expShareExpValue = 1;
|
||
}
|
||
|
||
gBattleScripting.getexpState++;
|
||
gBattleStruct->expOrderId = 0;
|
||
*expMonId = gBattleStruct->expGettersOrder[0];
|
||
gBattleStruct->expSentInMons = sentInBits;
|
||
}
|
||
// fall through
|
||
case 2: // set exp value to the poke in expgetter_id and print message
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
bool32 wasSentOut = ((gBattleStruct->expSentInMons & gBitTable[*expMonId]) != 0);
|
||
holdEffect = GetMonHoldEffect(&gPlayerParty[*expMonId]);
|
||
|
||
if ((holdEffect != HOLD_EFFECT_EXP_SHARE && !wasSentOut && !IsGen6ExpShareEnabled())
|
||
|| GetMonData(&gPlayerParty[*expMonId], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG)
|
||
{
|
||
gBattleScripting.getexpState = 5;
|
||
gBattleMoveDamage = 0; // used for exp
|
||
}
|
||
else if ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && *expMonId >= 3)
|
||
|| GetMonData(&gPlayerParty[*expMonId], MON_DATA_LEVEL) == MAX_LEVEL)
|
||
{
|
||
gBattleScripting.getexpState = 5;
|
||
gBattleMoveDamage = 0; // used for exp
|
||
if (B_MAX_LEVEL_EV_GAINS >= GEN_5)
|
||
MonGainEVs(&gPlayerParty[*expMonId], gBattleMons[gBattlerFainted].species);
|
||
}
|
||
else
|
||
{
|
||
// Music change in a wild battle after fainting opposing pokemon.
|
||
if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER)
|
||
&& (gBattleMons[0].hp || (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gBattleMons[2].hp))
|
||
&& !IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT))
|
||
&& !IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT))
|
||
&& !gBattleStruct->wildVictorySong)
|
||
{
|
||
BattleStopLowHpSound();
|
||
PlayBGM(MUS_VICTORY_WILD);
|
||
gBattleStruct->wildVictorySong++;
|
||
}
|
||
|
||
if (IsValidForBattle(&gPlayerParty[*expMonId]))
|
||
{
|
||
if (wasSentOut)
|
||
gBattleMoveDamage = gBattleStruct->expValue;
|
||
else
|
||
gBattleMoveDamage = 0;
|
||
|
||
if ((holdEffect == HOLD_EFFECT_EXP_SHARE || IsGen6ExpShareEnabled())
|
||
&& (B_SPLIT_EXP < GEN_6 || gBattleMoveDamage == 0)) // only give exp share bonus in later gens if the mon wasn't sent out
|
||
{
|
||
gBattleMoveDamage += gBattleStruct->expShareExpValue;
|
||
}
|
||
|
||
ApplyExperienceMultipliers(&gBattleMoveDamage, *expMonId, gBattlerFainted);
|
||
|
||
if (IsTradedMon(&gPlayerParty[*expMonId]))
|
||
{
|
||
// check if the Pokémon doesn't belong to the player
|
||
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && *expMonId >= 3)
|
||
i = STRINGID_EMPTYSTRING4;
|
||
else
|
||
i = STRINGID_ABOOSTED;
|
||
}
|
||
else
|
||
{
|
||
i = STRINGID_EMPTYSTRING4;
|
||
}
|
||
|
||
// get exp getter battler
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||
{
|
||
if (gBattlerPartyIndexes[2] == *expMonId && !(gAbsentBattlerFlags & gBitTable[2]))
|
||
gBattleStruct->expGetterBattlerId = 2;
|
||
else
|
||
{
|
||
if (!(gAbsentBattlerFlags & gBitTable[0]))
|
||
gBattleStruct->expGetterBattlerId = 0;
|
||
else
|
||
gBattleStruct->expGetterBattlerId = 2;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gBattleStruct->expGetterBattlerId = 0;
|
||
}
|
||
|
||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattleStruct->expGetterBattlerId, *expMonId);
|
||
// buffer 'gained' or 'gained a boosted'
|
||
PREPARE_STRING_BUFFER(gBattleTextBuff2, i);
|
||
PREPARE_WORD_NUMBER_BUFFER(gBattleTextBuff3, 6, gBattleMoveDamage);
|
||
|
||
if (wasSentOut || holdEffect == HOLD_EFFECT_EXP_SHARE)
|
||
{
|
||
PrepareStringBattle(STRINGID_PKMNGAINEDEXP, gBattleStruct->expGetterBattlerId);
|
||
}
|
||
else if (IsGen6ExpShareEnabled() && !gBattleStruct->teamGotExpMsgPrinted) // Print 'the rest of your team got exp' message once, when all of the sent-in mons were given experience
|
||
{
|
||
gLastUsedItem = ITEM_EXP_SHARE;
|
||
PrepareStringBattle(STRINGID_TEAMGAINEDEXP, gBattleStruct->expGetterBattlerId);
|
||
gBattleStruct->teamGotExpMsgPrinted = TRUE;
|
||
}
|
||
|
||
MonGainEVs(&gPlayerParty[*expMonId], gBattleMons[gBattlerFainted].species);
|
||
}
|
||
gBattleScripting.getexpState++;
|
||
}
|
||
}
|
||
break;
|
||
case 3: // Set stats and give exp
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
gBattleResources->bufferB[gBattleStruct->expGetterBattlerId][0] = 0;
|
||
if (GetMonData(&gPlayerParty[*expMonId], MON_DATA_HP) && GetMonData(&gPlayerParty[*expMonId], MON_DATA_LEVEL) != MAX_LEVEL)
|
||
{
|
||
gBattleResources->beforeLvlUp->stats[STAT_HP] = GetMonData(&gPlayerParty[*expMonId], MON_DATA_MAX_HP);
|
||
gBattleResources->beforeLvlUp->stats[STAT_ATK] = GetMonData(&gPlayerParty[*expMonId], MON_DATA_ATK);
|
||
gBattleResources->beforeLvlUp->stats[STAT_DEF] = GetMonData(&gPlayerParty[*expMonId], MON_DATA_DEF);
|
||
gBattleResources->beforeLvlUp->stats[STAT_SPEED] = GetMonData(&gPlayerParty[*expMonId], MON_DATA_SPEED);
|
||
gBattleResources->beforeLvlUp->stats[STAT_SPATK] = GetMonData(&gPlayerParty[*expMonId], MON_DATA_SPATK);
|
||
gBattleResources->beforeLvlUp->stats[STAT_SPDEF] = GetMonData(&gPlayerParty[*expMonId], MON_DATA_SPDEF);
|
||
|
||
BtlController_EmitExpUpdate(gBattleStruct->expGetterBattlerId, BUFFER_A, *expMonId, gBattleMoveDamage);
|
||
MarkBattlerForControllerExec(gBattleStruct->expGetterBattlerId);
|
||
}
|
||
gBattleScripting.getexpState++;
|
||
}
|
||
break;
|
||
case 4: // lvl up if necessary
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
u32 expBattler = gBattleStruct->expGetterBattlerId;
|
||
if (gBattleResources->bufferB[expBattler][0] == CONTROLLER_TWORETURNVALUES && gBattleResources->bufferB[expBattler][1] == RET_VALUE_LEVELED_UP)
|
||
{
|
||
u16 temp, battler = 0xFF;
|
||
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && gBattlerPartyIndexes[expBattler] == *expMonId)
|
||
HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[expBattler]], expBattler);
|
||
|
||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, expBattler, *expMonId);
|
||
PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff2, 3, GetMonData(&gPlayerParty[*expMonId], MON_DATA_LEVEL));
|
||
|
||
BattleScriptPushCursor();
|
||
gLeveledUpInBattle |= gBitTable[*expMonId];
|
||
gBattlescriptCurrInstr = BattleScript_LevelUp;
|
||
gBattleMoveDamage = T1_READ_32(&gBattleResources->bufferB[expBattler][2]);
|
||
AdjustFriendship(&gPlayerParty[*expMonId], FRIENDSHIP_EVENT_GROW_LEVEL);
|
||
|
||
// update battle mon structure after level up
|
||
if (gBattlerPartyIndexes[0] == *expMonId && gBattleMons[0].hp)
|
||
battler = 0;
|
||
else if (gBattlerPartyIndexes[2] == *expMonId && gBattleMons[2].hp && (gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||
battler = 2;
|
||
|
||
if (battler != 0xFF)
|
||
{
|
||
CopyMonLevelAndBaseStatsToBattleMon(battler, &gPlayerParty[*expMonId]);
|
||
if (gStatuses3[battler] & STATUS3_POWER_TRICK)
|
||
SWAP(gBattleMons[battler].attack, gBattleMons[battler].defense, temp);
|
||
}
|
||
|
||
gBattleScripting.getexpState = 5;
|
||
}
|
||
else
|
||
{
|
||
gBattleMoveDamage = 0;
|
||
gBattleScripting.getexpState = 5;
|
||
}
|
||
}
|
||
break;
|
||
case 5: // looper increment
|
||
if (gBattleMoveDamage) // there is exp to give, goto case 3 that gives exp
|
||
{
|
||
gBattleScripting.getexpState = 3;
|
||
}
|
||
else
|
||
{
|
||
if ((++gBattleStruct->expOrderId) < PARTY_SIZE)
|
||
{
|
||
*expMonId = gBattleStruct->expGettersOrder[gBattleStruct->expOrderId];
|
||
if (*expMonId < PARTY_SIZE)
|
||
{
|
||
gBattleScripting.getexpState = 2; // loop again
|
||
break;
|
||
}
|
||
}
|
||
gBattleScripting.getexpState = 6; // we're done
|
||
}
|
||
break;
|
||
case 6: // increment instruction
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
// not sure why gf clears the item and ability here
|
||
gBattleStruct->expOrderId = 0;
|
||
gBattleStruct->teamGotExpMsgPrinted = FALSE;
|
||
gBattleMons[gBattlerFainted].item = ITEM_NONE;
|
||
gBattleMons[gBattlerFainted].ability = ABILITY_NONE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
static bool32 NoAliveMonsForPlayerAndPartner(void)
|
||
{
|
||
u32 i;
|
||
u32 HP_count = 0;
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE))
|
||
{
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) && !GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG)
|
||
&& (!(gBattleTypeFlags & BATTLE_TYPE_ARENA) || !(gBattleStruct->arenaLostPlayerMons & gBitTable[i])))
|
||
{
|
||
HP_count += GetMonData(&gPlayerParty[i], MON_DATA_HP);
|
||
}
|
||
}
|
||
}
|
||
|
||
return (HP_count == 0);
|
||
}
|
||
|
||
bool32 NoAliveMonsForPlayer(void)
|
||
{
|
||
u32 i;
|
||
u32 HP_count = 0;
|
||
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) && !GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG)
|
||
&& (!(gBattleTypeFlags & BATTLE_TYPE_ARENA) || !(gBattleStruct->arenaLostPlayerMons & gBitTable[i])))
|
||
{
|
||
HP_count += GetMonData(&gPlayerParty[i], MON_DATA_HP);
|
||
}
|
||
}
|
||
|
||
return (HP_count == 0);
|
||
}
|
||
|
||
static bool32 NoAliveMonsForOpponent(void)
|
||
{
|
||
u32 i;
|
||
u32 HP_count = 0;
|
||
|
||
// Get total HP for the enemy's party to determine if the player has won
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES) && !GetMonData(&gEnemyParty[i], MON_DATA_IS_EGG)
|
||
&& (!(gBattleTypeFlags & BATTLE_TYPE_ARENA) || !(gBattleStruct->arenaLostOpponentMons & gBitTable[i])))
|
||
{
|
||
HP_count += GetMonData(&gEnemyParty[i], MON_DATA_HP);
|
||
}
|
||
}
|
||
|
||
return (HP_count == 0);
|
||
}
|
||
|
||
bool32 NoAliveMonsForEitherParty(void)
|
||
{
|
||
return (NoAliveMonsForPlayer() || NoAliveMonsForOpponent());
|
||
}
|
||
|
||
// For battles that aren't BATTLE_TYPE_LINK or BATTLE_TYPE_RECORDED_LINK, the only thing this
|
||
// command does is check whether the player has won/lost by totaling each team's HP. It then
|
||
// sets gBattleOutcome accordingly, if necessary.
|
||
static void Cmd_checkteamslost(void)
|
||
{
|
||
CMD_ARGS(const u8 *jumpInstr);
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
if (B_MULTI_BATTLE_WHITEOUT >= GEN_4 && gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER))
|
||
{
|
||
if (NoAliveMonsForPlayerAndPartner())
|
||
gBattleOutcome |= B_OUTCOME_LOST;
|
||
}
|
||
else
|
||
{
|
||
if (NoAliveMonsForPlayer())
|
||
gBattleOutcome |= B_OUTCOME_LOST;
|
||
}
|
||
if (NoAliveMonsForOpponent())
|
||
gBattleOutcome |= B_OUTCOME_WON;
|
||
|
||
// For link battles that haven't ended, count number of empty battler spots
|
||
// In link multi battles, jump to pointer if more than 1 spot empty
|
||
// In non-multi battles, jump to pointer if 1 spot is missing on both sides
|
||
if (gBattleOutcome == 0 && (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)))
|
||
{
|
||
s32 i, emptyPlayerSpots, emptyOpponentSpots;
|
||
|
||
for (emptyPlayerSpots = 0, i = 0; i < gBattlersCount; i += 2)
|
||
{
|
||
if ((gHitMarker & HITMARKER_FAINTED2(i)) && (!gSpecialStatuses[i].faintedHasReplacement))
|
||
emptyPlayerSpots++;
|
||
}
|
||
|
||
emptyOpponentSpots = 0;
|
||
for (i = 1; i < gBattlersCount; i += 2)
|
||
{
|
||
if ((gHitMarker & HITMARKER_FAINTED2(i)) && (!gSpecialStatuses[i].faintedHasReplacement))
|
||
emptyOpponentSpots++;
|
||
}
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
|
||
{
|
||
if (emptyOpponentSpots + emptyPlayerSpots > 1)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
if (emptyOpponentSpots != 0 && emptyPlayerSpots != 0)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void MoveValuesCleanUp(void)
|
||
{
|
||
gMoveResultFlags = 0;
|
||
gIsCriticalHit = FALSE;
|
||
gBattleScripting.moveEffect = 0;
|
||
gBattleCommunication[MISS_TYPE] = 0;
|
||
gHitMarker &= ~HITMARKER_DESTINYBOND;
|
||
gHitMarker &= ~HITMARKER_SYNCHRONISE_EFFECT;
|
||
}
|
||
|
||
static void Cmd_movevaluescleanup(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
MoveValuesCleanUp();
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setmultihit(void)
|
||
{
|
||
CMD_ARGS(u8 value);
|
||
|
||
gMultiHitCounter = cmd->value;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_decrementmultihit(void)
|
||
{
|
||
CMD_ARGS(const u8 *loopInstr);
|
||
|
||
if (--gMultiHitCounter == 0)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->loopInstr;
|
||
}
|
||
|
||
static void Cmd_goto(void)
|
||
{
|
||
CMD_ARGS(const u8 *instr);
|
||
|
||
gBattlescriptCurrInstr = cmd->instr;
|
||
}
|
||
|
||
static void Cmd_jumpifbyte(void)
|
||
{
|
||
CMD_ARGS(u8 comparison, const u8 *bytePtr, u8 value, const u8 *jumpInstr);
|
||
|
||
u8 comparison = cmd->comparison;
|
||
const u8 *bytePtr = cmd->bytePtr;
|
||
u8 value = cmd->value;
|
||
const u8 *jumpInstr = cmd->jumpInstr;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
switch (comparison)
|
||
{
|
||
case CMP_EQUAL:
|
||
if (*bytePtr == value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_NOT_EQUAL:
|
||
if (*bytePtr != value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_GREATER_THAN:
|
||
if (*bytePtr > value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_LESS_THAN:
|
||
if (*bytePtr < value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_COMMON_BITS:
|
||
if (*bytePtr & value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_NO_COMMON_BITS:
|
||
if (!(*bytePtr & value))
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void Cmd_jumpifhalfword(void)
|
||
{
|
||
CMD_ARGS(u8 comparison, const u16 *halfwordPtr, u16 value, const u8 *jumpInstr);
|
||
|
||
u8 comparison = cmd->comparison;
|
||
const u16 *halfwordPtr = cmd->halfwordPtr;
|
||
u16 value = cmd->value;
|
||
const u8 *jumpInstr = cmd->jumpInstr;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
switch (comparison)
|
||
{
|
||
case CMP_EQUAL:
|
||
if (*halfwordPtr == value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_NOT_EQUAL:
|
||
if (*halfwordPtr != value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_GREATER_THAN:
|
||
if (*halfwordPtr > value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_LESS_THAN:
|
||
if (*halfwordPtr < value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_COMMON_BITS:
|
||
if (*halfwordPtr & value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_NO_COMMON_BITS:
|
||
if (!(*halfwordPtr & value))
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void Cmd_jumpifword(void)
|
||
{
|
||
CMD_ARGS(u8 comparison, const u32 *wordPtr, u32 value, const u8 *jumpInstr);
|
||
|
||
u8 comparison = cmd->comparison;
|
||
const u32 *wordPtr = cmd->wordPtr;
|
||
u32 value = cmd->value;
|
||
const u8 *jumpInstr = cmd->jumpInstr;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
switch (comparison)
|
||
{
|
||
case CMP_EQUAL:
|
||
if (*wordPtr == value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_NOT_EQUAL:
|
||
if (*wordPtr != value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_GREATER_THAN:
|
||
if (*wordPtr > value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_LESS_THAN:
|
||
if (*wordPtr < value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_COMMON_BITS:
|
||
if (*wordPtr & value)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
case CMP_NO_COMMON_BITS:
|
||
if (!(*wordPtr & value))
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void Cmd_jumpifarrayequal(void)
|
||
{
|
||
CMD_ARGS(const u8 *array1, const u8 *array2, u8 size, const u8 *jumpInstr);
|
||
|
||
const u8 *array1 = cmd->array1;
|
||
const u8 *array2 = cmd->array2;
|
||
u32 size = cmd->size;
|
||
const u8 *jumpInstr = cmd->jumpInstr;
|
||
|
||
u8 i;
|
||
for (i = 0; i < size; i++)
|
||
{
|
||
if (*array1 != *array2)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
break;
|
||
}
|
||
array1++, array2++;
|
||
}
|
||
|
||
if (i == size)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
}
|
||
|
||
static void Cmd_jumpifarraynotequal(void)
|
||
{
|
||
CMD_ARGS(const u8 *array1, const u8 *array2, u8 size, const u8 *jumpInstr);
|
||
|
||
u8 equalBytes = 0;
|
||
const u8 *array1 = cmd->array1;
|
||
const u8 *array2 = cmd->array2;
|
||
u32 size = cmd->size;
|
||
const u8 *jumpInstr = cmd->jumpInstr;
|
||
|
||
u8 i;
|
||
for (i = 0; i < size; i++)
|
||
{
|
||
if (*array1 == *array2)
|
||
equalBytes++;
|
||
array1++, array2++;
|
||
}
|
||
|
||
if (equalBytes != size)
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setbyte(void)
|
||
{
|
||
CMD_ARGS(u8 *bytePtr, u8 value);
|
||
|
||
u8 *bytePtr = cmd->bytePtr;
|
||
*bytePtr = cmd->value;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_addbyte(void)
|
||
{
|
||
CMD_ARGS(u8 *bytePtr, u8 value);
|
||
|
||
u8 *bytePtr = cmd->bytePtr;
|
||
*bytePtr += cmd->value;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_subbyte(void)
|
||
{
|
||
CMD_ARGS(u8 *bytePtr, u8 value);
|
||
|
||
u8 *bytePtr = cmd->bytePtr;
|
||
*bytePtr -= cmd->value;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_copyarray(void)
|
||
{
|
||
CMD_ARGS(u8 *dest, const u8 *src, u8 size);
|
||
|
||
u8 *dest = cmd->dest;
|
||
const u8 *src = cmd->src;
|
||
s32 size = cmd->size;
|
||
|
||
s32 i;
|
||
for (i = 0; i < size; i++)
|
||
dest[i] = src[i];
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_copyarraywithindex(void)
|
||
{
|
||
CMD_ARGS(u8 *dest, const u8 *src, const u8 *indexPtr, u8 size);
|
||
|
||
u8 *dest = cmd->dest;
|
||
const u8 *src = cmd->src;
|
||
const u8 *indexPtr = cmd->indexPtr;
|
||
s32 size = cmd->size;
|
||
|
||
s32 i;
|
||
for (i = 0; i < size; i++)
|
||
dest[i] = src[i + *indexPtr];
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_orbyte(void)
|
||
{
|
||
CMD_ARGS(u8 *bytePtr, u8 value);
|
||
|
||
u8 *bytePtr = cmd->bytePtr;
|
||
*bytePtr |= cmd->value;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_orhalfword(void)
|
||
{
|
||
CMD_ARGS(u16 *halfwordPtr, u16 value);
|
||
|
||
u16 *halfwordPtr = cmd->halfwordPtr;
|
||
u16 value = cmd->value;
|
||
|
||
*halfwordPtr |= value;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_orword(void)
|
||
{
|
||
CMD_ARGS(u32 *wordPtr, u32 value);
|
||
|
||
u32 *wordPtr = cmd->wordPtr;
|
||
u32 value = cmd->value;
|
||
|
||
*wordPtr |= value;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_bicbyte(void)
|
||
{
|
||
CMD_ARGS(u8 *bytePtr, u8 value);
|
||
|
||
u8 *bytePtr = cmd->bytePtr;
|
||
*bytePtr &= ~cmd->value;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_bichalfword(void)
|
||
{
|
||
CMD_ARGS(u16 *halfwordPtr, u16 value);
|
||
|
||
u16 *halfwordPtr = cmd->halfwordPtr;
|
||
u16 value = cmd->value;
|
||
|
||
*halfwordPtr &= ~value;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_bicword(void)
|
||
{
|
||
CMD_ARGS(u32 *wordPtr, u32 value);
|
||
|
||
u32 *wordPtr = cmd->wordPtr;
|
||
u32 value = cmd->value;
|
||
|
||
*wordPtr &= ~value;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_pause(void)
|
||
{
|
||
CMD_ARGS(u16 frames);
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
u16 value = cmd->frames;
|
||
if (++gPauseCounterBattle >= value)
|
||
{
|
||
gPauseCounterBattle = 0;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void Cmd_waitstate(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_healthbar_update(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
u32 battler;
|
||
|
||
if (cmd->battler == BS_TARGET)
|
||
battler = gBattlerTarget;
|
||
else
|
||
battler = gBattlerAttacker;
|
||
|
||
BtlController_EmitHealthBarUpdate(battler, BUFFER_A, gBattleMoveDamage);
|
||
MarkBattlerForControllerExec(battler);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_return(void)
|
||
{
|
||
BattleScriptPop();
|
||
}
|
||
|
||
static void Cmd_end(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||
BattleArena_AddSkillPoints(gBattlerAttacker);
|
||
|
||
gMoveResultFlags = 0;
|
||
gCurrentActionFuncId = B_ACTION_TRY_FINISH;
|
||
}
|
||
|
||
static void Cmd_end2(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gCurrentActionFuncId = B_ACTION_TRY_FINISH;
|
||
}
|
||
|
||
// Pops the main function stack
|
||
static void Cmd_end3(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
BattleScriptPop();
|
||
if (gBattleResources->battleCallbackStack->size != 0)
|
||
gBattleResources->battleCallbackStack->size--;
|
||
gBattleMainFunc = gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size];
|
||
}
|
||
|
||
static void Cmd_call(void)
|
||
{
|
||
CMD_ARGS(const u8 *instr);
|
||
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = cmd->instr;
|
||
}
|
||
|
||
static void Cmd_setroost(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gBattleResources->flags->flags[gBattlerAttacker] |= RESOURCE_FLAG_ROOST;
|
||
gBattleStruct->roostTypes[gBattlerAttacker][0] = gBattleMons[gBattlerAttacker].type1;
|
||
gBattleStruct->roostTypes[gBattlerAttacker][1] = gBattleMons[gBattlerAttacker].type2;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_jumpifabilitypresent(void)
|
||
{
|
||
CMD_ARGS(u16 ability, const u8 *jumpInstr);
|
||
|
||
u16 ability = cmd->ability;
|
||
u32 abilityBattler = IsAbilityOnField(ability);
|
||
if (abilityBattler)
|
||
{
|
||
gBattlerAbility = abilityBattler - 1;
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_endselectionscript(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
*(gBattlerAttacker + gBattleStruct->selectionScriptFinished) = TRUE;
|
||
}
|
||
|
||
static void PlayAnimation(u32 battler, u8 animId, const u16 *argPtr, const u8 *nextInstr)
|
||
{
|
||
if (B_TERRAIN_BG_CHANGE == FALSE && animId == B_ANIM_RESTORE_BG)
|
||
{
|
||
// workaround for .if not working
|
||
gBattlescriptCurrInstr = nextInstr;
|
||
return;
|
||
}
|
||
|
||
if (animId == B_ANIM_STATS_CHANGE
|
||
|| animId == B_ANIM_SNATCH_MOVE
|
||
|| animId == B_ANIM_MEGA_EVOLUTION
|
||
|| animId == B_ANIM_ILLUSION_OFF
|
||
|| animId == B_ANIM_FORM_CHANGE
|
||
|| animId == B_ANIM_SUBSTITUTE_FADE
|
||
|| animId == B_ANIM_PRIMAL_REVERSION
|
||
|| animId == B_ANIM_ULTRA_BURST)
|
||
{
|
||
BtlController_EmitBattleAnimation(battler, BUFFER_A, animId, &gDisableStructs[battler], *argPtr);
|
||
MarkBattlerForControllerExec(battler);
|
||
gBattlescriptCurrInstr = nextInstr;
|
||
}
|
||
else if (gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION) && animId != B_ANIM_RESTORE_BG)
|
||
{
|
||
BattleScriptPush(nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_Pausex20;
|
||
}
|
||
else if (animId == B_ANIM_RAIN_CONTINUES
|
||
|| animId == B_ANIM_SUN_CONTINUES
|
||
|| animId == B_ANIM_SANDSTORM_CONTINUES
|
||
|| animId == B_ANIM_HAIL_CONTINUES
|
||
|| animId == B_ANIM_SNOW_CONTINUES)
|
||
{
|
||
BtlController_EmitBattleAnimation(battler, BUFFER_A, animId, &gDisableStructs[battler], *argPtr);
|
||
MarkBattlerForControllerExec(battler);
|
||
gBattlescriptCurrInstr = nextInstr;
|
||
}
|
||
else if (gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)
|
||
{
|
||
gBattlescriptCurrInstr = nextInstr;
|
||
}
|
||
else
|
||
{
|
||
BtlController_EmitBattleAnimation(battler, BUFFER_A, animId, &gDisableStructs[battler], *argPtr);
|
||
MarkBattlerForControllerExec(battler);
|
||
gBattlescriptCurrInstr = nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_playanimation(void)
|
||
{
|
||
CMD_ARGS(u8 battler, u8 animId, const u16 *argPtr);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
PlayAnimation(battler, cmd->animId, cmd->argPtr, cmd->nextInstr);
|
||
}
|
||
|
||
// Same as playanimation, except it takes a pointer to some animation id, instead of taking the value directly
|
||
static void Cmd_playanimation_var(void)
|
||
{
|
||
CMD_ARGS(u8 battler, const u8 *animIdPtr, const u16 *argPtr);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
PlayAnimation(battler, *(cmd->animIdPtr), cmd->argPtr, cmd->nextInstr);
|
||
}
|
||
|
||
static void Cmd_setgraphicalstatchangevalues(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u8 value = GET_STAT_BUFF_VALUE_WITH_SIGN(gBattleScripting.statChanger);
|
||
|
||
switch (value)
|
||
{
|
||
case SET_STAT_BUFF_VALUE(1): // +1
|
||
value = STAT_ANIM_PLUS1 + 1;
|
||
break;
|
||
case SET_STAT_BUFF_VALUE(2): // +2
|
||
value = STAT_ANIM_PLUS2 + 1;
|
||
break;
|
||
case SET_STAT_BUFF_VALUE(3): // +3
|
||
value = STAT_ANIM_PLUS2 + 1;
|
||
break;
|
||
case SET_STAT_BUFF_VALUE(1) | STAT_BUFF_NEGATIVE: // -1
|
||
value = STAT_ANIM_MINUS1 + 1;
|
||
break;
|
||
case SET_STAT_BUFF_VALUE(2) | STAT_BUFF_NEGATIVE: // -2
|
||
value = STAT_ANIM_MINUS2 + 1;
|
||
break;
|
||
case SET_STAT_BUFF_VALUE(3) | STAT_BUFF_NEGATIVE: // -3
|
||
value = STAT_ANIM_MINUS2 + 1;
|
||
break;
|
||
default: // <-12,-4> and <4, 12>
|
||
if (value & STAT_BUFF_NEGATIVE)
|
||
value = STAT_ANIM_MINUS2 + 1;
|
||
else
|
||
value = STAT_ANIM_PLUS2 + 1;
|
||
break;
|
||
}
|
||
gBattleScripting.animArg1 = GET_STAT_BUFF_ID(gBattleScripting.statChanger) + value - 1;
|
||
gBattleScripting.animArg2 = 0;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_playstatchangeanimation(void)
|
||
{
|
||
CMD_ARGS(u8 battler, u8 stats, u8 flags);
|
||
|
||
u32 currStat = 0;
|
||
u32 statAnimId = 0;
|
||
u32 changeableStatsCount = 0;
|
||
u32 startingStatAnimId = 0;
|
||
u32 flags = cmd->flags;
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
u32 ability = GetBattlerAbility(battler);
|
||
u32 stats = cmd->stats;
|
||
|
||
// Handle Contrary and Simple
|
||
if (ability == ABILITY_CONTRARY)
|
||
{
|
||
flags ^= STAT_CHANGE_NEGATIVE;
|
||
RecordAbilityBattle(battler, ability);
|
||
}
|
||
else if (ability == ABILITY_SIMPLE)
|
||
{
|
||
flags |= STAT_CHANGE_BY_TWO;
|
||
RecordAbilityBattle(battler, ability);
|
||
}
|
||
|
||
if (flags & STAT_CHANGE_NEGATIVE) // goes down
|
||
{
|
||
if (flags & STAT_CHANGE_BY_TWO)
|
||
startingStatAnimId = STAT_ANIM_MINUS2;
|
||
else
|
||
startingStatAnimId = STAT_ANIM_MINUS1;
|
||
|
||
while (stats != 0)
|
||
{
|
||
if (stats & 1)
|
||
{
|
||
if (flags & STAT_CHANGE_CANT_PREVENT)
|
||
{
|
||
if (gBattleMons[battler].statStages[currStat] > MIN_STAT_STAGE)
|
||
{
|
||
statAnimId = startingStatAnimId + currStat;
|
||
changeableStatsCount++;
|
||
}
|
||
}
|
||
else if (!gSideTimers[GetBattlerSide(battler)].mistTimer
|
||
&& GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_CLEAR_AMULET
|
||
&& ability != ABILITY_CLEAR_BODY
|
||
&& ability != ABILITY_FULL_METAL_BODY
|
||
&& ability != ABILITY_WHITE_SMOKE
|
||
&& !((ability == ABILITY_KEEN_EYE || ability == ABILITY_MINDS_EYE) && currStat == STAT_ACC)
|
||
&& !(B_ILLUMINATE_EFFECT >= GEN_9 && ability == ABILITY_ILLUMINATE && currStat == STAT_ACC)
|
||
&& !(ability == ABILITY_HYPER_CUTTER && currStat == STAT_ATK)
|
||
&& !(ability == ABILITY_BIG_PECKS && currStat == STAT_DEF))
|
||
{
|
||
if (gBattleMons[battler].statStages[currStat] > MIN_STAT_STAGE)
|
||
{
|
||
statAnimId = startingStatAnimId + currStat;
|
||
changeableStatsCount++;
|
||
}
|
||
}
|
||
}
|
||
stats >>= 1, currStat++;
|
||
}
|
||
|
||
if (changeableStatsCount > 1) // more than one stat, so the color is gray
|
||
{
|
||
if (flags & STAT_CHANGE_BY_TWO)
|
||
statAnimId = STAT_ANIM_MULTIPLE_MINUS2;
|
||
else
|
||
statAnimId = STAT_ANIM_MULTIPLE_MINUS1;
|
||
}
|
||
}
|
||
else // goes up
|
||
{
|
||
if (flags & STAT_CHANGE_BY_TWO)
|
||
startingStatAnimId = STAT_ANIM_PLUS2;
|
||
else
|
||
startingStatAnimId = STAT_ANIM_PLUS1;
|
||
|
||
while (stats != 0)
|
||
{
|
||
if (stats & 1 && gBattleMons[battler].statStages[currStat] < MAX_STAT_STAGE)
|
||
{
|
||
statAnimId = startingStatAnimId + currStat;
|
||
changeableStatsCount++;
|
||
}
|
||
stats >>= 1, currStat++;
|
||
}
|
||
|
||
if (changeableStatsCount > 1) // more than one stat, so the color is gray
|
||
{
|
||
if (flags & STAT_CHANGE_BY_TWO)
|
||
statAnimId = STAT_ANIM_MULTIPLE_PLUS2;
|
||
else
|
||
statAnimId = STAT_ANIM_MULTIPLE_PLUS1;
|
||
}
|
||
}
|
||
|
||
if (flags & STAT_CHANGE_MULTIPLE_STATS && changeableStatsCount < 2)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else if (changeableStatsCount != 0 && !gBattleScripting.statAnimPlayed)
|
||
{
|
||
BtlController_EmitBattleAnimation(battler, BUFFER_A, B_ANIM_STATS_CHANGE, &gDisableStructs[battler], statAnimId);
|
||
MarkBattlerForControllerExec(battler);
|
||
if (flags & STAT_CHANGE_MULTIPLE_STATS && changeableStatsCount > 1)
|
||
gBattleScripting.statAnimPlayed = TRUE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static bool32 TryKnockOffBattleScript(u32 battlerDef)
|
||
{
|
||
if (gBattleMons[battlerDef].item != 0
|
||
&& CanBattlerGetOrLoseItem(battlerDef, gBattleMons[battlerDef].item)
|
||
&& !NoAliveMonsForEitherParty())
|
||
{
|
||
if (GetBattlerAbility(battlerDef) == ABILITY_STICKY_HOLD && IsBattlerAlive(battlerDef))
|
||
{
|
||
gBattlerAbility = battlerDef;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_StickyHoldActivates;
|
||
}
|
||
else
|
||
{
|
||
u32 side = GetBattlerSide(battlerDef);
|
||
|
||
gLastUsedItem = gBattleMons[battlerDef].item;
|
||
gBattleMons[battlerDef].item = 0;
|
||
if (gBattleMons[battlerDef].ability != ABILITY_GORILLA_TACTICS)
|
||
gBattleStruct->choicedMove[battlerDef] = 0;
|
||
gWishFutureKnock.knockedOffMons[side] |= gBitTable[gBattlerPartyIndexes[battlerDef]];
|
||
CheckSetUnburden(battlerDef);
|
||
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_KnockedOff;
|
||
}
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
#define SYMBIOSIS_CHECK(battler, ally) \
|
||
GetBattlerAbility(ally) == ABILITY_SYMBIOSIS \
|
||
&& gBattleMons[battler].item == ITEM_NONE \
|
||
&& gBattleMons[ally].item != ITEM_NONE \
|
||
&& CanBattlerGetOrLoseItem(battler, gBattleMons[ally].item) \
|
||
&& CanBattlerGetOrLoseItem(ally, gBattleMons[ally].item) \
|
||
&& gBattleMons[battler].hp != 0 \
|
||
&& gBattleMons[ally].hp != 0
|
||
|
||
static u32 GetNextTarget(u32 moveTarget)
|
||
{
|
||
u32 i;
|
||
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
||
{
|
||
if (i != gBattlerAttacker
|
||
&& IsBattlerAlive(i)
|
||
&& !(gBattleStruct->targetsDone[gBattlerAttacker] & gBitTable[i])
|
||
&& (GetBattlerSide(i) != GetBattlerSide(gBattlerAttacker) || moveTarget == MOVE_TARGET_FOES_AND_ALLY))
|
||
break;
|
||
}
|
||
return i;
|
||
}
|
||
|
||
static void Cmd_moveend(void)
|
||
{
|
||
CMD_ARGS(u8 endMode, u8 endState);
|
||
|
||
s32 i;
|
||
bool32 effect = FALSE;
|
||
u32 moveType = 0;
|
||
u32 holdEffectAtk = 0;
|
||
u16 *choicedMoveAtk = NULL;
|
||
u32 endMode, endState;
|
||
u32 originallyUsedMove;
|
||
|
||
if (gChosenMove == MOVE_UNAVAILABLE)
|
||
originallyUsedMove = MOVE_NONE;
|
||
else
|
||
originallyUsedMove = gChosenMove;
|
||
|
||
endMode = cmd->endMode;
|
||
endState = cmd->endState;
|
||
|
||
holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
|
||
choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker];
|
||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||
|
||
do
|
||
{
|
||
switch (gBattleScripting.moveendState)
|
||
{
|
||
case MOVEEND_SUM_DAMAGE: // Sum and store damage dealt for multi strike recoil
|
||
gBattleScripting.savedDmg += gHpDealt;
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_PROTECT_LIKE_EFFECT:
|
||
if (gProtectStructs[gBattlerAttacker].touchedProtectLike)
|
||
{
|
||
if (gProtectStructs[gBattlerTarget].spikyShielded && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 8;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SPIKY_SHIELD);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_SpikyShieldEffect;
|
||
effect = 1;
|
||
}
|
||
else if (gProtectStructs[gBattlerTarget].kingsShielded)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
|
||
i = gBattlerAttacker;
|
||
gBattlerAttacker = gBattlerTarget;
|
||
gBattlerTarget = i; // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable
|
||
if (B_KINGS_SHIELD_LOWER_ATK >= GEN_8)
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_ATK_MINUS_1;
|
||
else
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_ATK_MINUS_2;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_KingsShieldEffect;
|
||
effect = 1;
|
||
}
|
||
else if (gProtectStructs[gBattlerTarget].banefulBunkered)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_POISON | MOVE_EFFECT_AFFECTS_USER;
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_BANEFUL_BUNKER);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_BanefulBunkerEffect;
|
||
effect = 1;
|
||
}
|
||
else if (gProtectStructs[gBattlerTarget].obstructed && gCurrentMove != MOVE_SUCKER_PUNCH)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
|
||
i = gBattlerAttacker;
|
||
gBattlerAttacker = gBattlerTarget;
|
||
gBattlerTarget = i; // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_DEF_MINUS_2;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_KingsShieldEffect;
|
||
effect = 1;
|
||
}
|
||
else if (gProtectStructs[gBattlerTarget].silkTrapped)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
|
||
i = gBattlerAttacker;
|
||
gBattlerAttacker = gBattlerTarget;
|
||
gBattlerTarget = i; // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_SPD_MINUS_1;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_KingsShieldEffect;
|
||
effect = 1;
|
||
}
|
||
else if (gProtectStructs[gBattlerTarget].burningBulwarked)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_BURN | MOVE_EFFECT_AFFECTS_USER;
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_BURNING_BULWARK);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_BanefulBunkerEffect;
|
||
effect = 1;
|
||
}
|
||
// Not strictly a protect effect, but works the same way
|
||
else if (gProtectStructs[gBattlerTarget].beakBlastCharge
|
||
&& CanBeBurned(gBattlerAttacker)
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
|
||
{
|
||
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
|
||
gBattleMons[gBattlerAttacker].status1 = STATUS1_BURN;
|
||
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerAttacker].status1), &gBattleMons[gBattlerAttacker].status1);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_BeakBlastBurn;
|
||
effect = 1;
|
||
}
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_RAGE: // rage check
|
||
if (gBattleMons[gBattlerTarget].status2 & STATUS2_RAGE
|
||
&& gBattleMons[gBattlerTarget].hp != 0
|
||
&& gBattlerAttacker != gBattlerTarget
|
||
&& GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
&& TARGET_TURN_DAMAGED
|
||
&& gBattleMoves[gCurrentMove].power != 0
|
||
&& CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
|
||
{
|
||
SET_STATCHANGER(STAT_ATK, 1, FALSE);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_RageIsBuilding;
|
||
effect = TRUE;
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_DEFROST: // defrosting check
|
||
if (gBattleMons[gBattlerTarget].status1 & STATUS1_FREEZE
|
||
&& gBattleMons[gBattlerTarget].hp != 0
|
||
&& gBattlerAttacker != gBattlerTarget
|
||
&& (moveType == TYPE_FIRE
|
||
|| (B_BURN_HIT_THAW >= GEN_6 && gBattleMoves[gCurrentMove].effect == EFFECT_BURN_HIT))
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
|
||
{
|
||
gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FREEZE;
|
||
BtlController_EmitSetMonData(gBattlerTarget, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1);
|
||
MarkBattlerForControllerExec(gBattlerTarget);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_DefrostedViaFireMove;
|
||
effect = TRUE;
|
||
}
|
||
if (gBattleMons[gBattlerTarget].status1 & STATUS1_FROSTBITE
|
||
&& gBattleMons[gBattlerTarget].hp != 0
|
||
&& gBattlerAttacker != gBattlerTarget
|
||
&& gBattleMoves[originallyUsedMove].thawsUser
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
|
||
{
|
||
gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FROSTBITE;
|
||
BtlController_EmitSetMonData(gBattlerTarget, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1);
|
||
MarkBattlerForControllerExec(gBattlerTarget);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_FrostbiteHealedViaFireMove;
|
||
effect = TRUE;
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_RECOIL:
|
||
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
&& !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
|
||
&& IsBattlerAlive(gBattlerAttacker)
|
||
&& gBattleScripting.savedDmg != 0) // Some checks may be redundant alongside this one
|
||
{
|
||
switch (gBattleMoves[gCurrentMove].effect)
|
||
{
|
||
case EFFECT_RECOIL:
|
||
gBattleMoveDamage = max(1, gBattleScripting.savedDmg * max(1, gBattleMoves[gCurrentMove].recoil) / 100);
|
||
BattleScriptPushCursor();
|
||
if (gBattleMoves[gCurrentMove].argument) // Flare Blitz - can burn, Volt Tackle - can paralyze
|
||
gBattlescriptCurrInstr = BattleScript_MoveEffectRecoilWithStatus;
|
||
else
|
||
gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil;
|
||
effect = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_SYNCHRONIZE_TARGET: // target synchronize
|
||
if (AbilityBattleEffects(ABILITYEFFECT_SYNCHRONIZE, gBattlerTarget, 0, 0, 0))
|
||
effect = TRUE;
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_ABILITIES: // Such as abilities activating on contact(Poison Spore, Rough Skin, etc.).
|
||
if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END, gBattlerTarget, 0, 0, 0))
|
||
effect = TRUE;
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_ABILITIES_ATTACKER: // Poison Touch, possibly other in the future
|
||
if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END_ATTACKER, gBattlerAttacker, 0, 0, 0))
|
||
effect = TRUE;
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_OPPORTUNIST:
|
||
if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0))
|
||
effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers
|
||
else
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_STATUS_IMMUNITY_ABILITIES: // status immunities
|
||
if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, 0, 0, 0, 0))
|
||
effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers
|
||
else
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_SYNCHRONIZE_ATTACKER: // attacker synchronize
|
||
if (AbilityBattleEffects(ABILITYEFFECT_ATK_SYNCHRONIZE, gBattlerAttacker, 0, 0, 0))
|
||
effect = TRUE;
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_CHOICE_MOVE: // update choice band move
|
||
if (gHitMarker & HITMARKER_OBEYS
|
||
&& (HOLD_EFFECT_CHOICE(holdEffectAtk) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS)
|
||
&& gChosenMove != MOVE_STRUGGLE
|
||
&& (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE))
|
||
{
|
||
if ((gBattleMoves[gChosenMove].effect == EFFECT_BATON_PASS
|
||
|| gBattleMoves[gChosenMove].effect == EFFECT_HEALING_WISH)
|
||
&& !(gMoveResultFlags & MOVE_RESULT_FAILED))
|
||
{
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
}
|
||
*choicedMoveAtk = gChosenMove;
|
||
}
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gBattleMons[gBattlerAttacker].moves[i] == *choicedMoveAtk)
|
||
break;
|
||
}
|
||
if (i == MAX_MON_MOVES)
|
||
*choicedMoveAtk = MOVE_NONE;
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_CHANGED_ITEMS: // changed held items
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (gBattleStruct->changedItems[i] != ITEM_NONE)
|
||
{
|
||
gBattleMons[i].item = gBattleStruct->changedItems[i];
|
||
gBattleStruct->changedItems[i] = ITEM_NONE;
|
||
}
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_ITEM_EFFECTS_TARGET:
|
||
if (ItemBattleEffects(ITEMEFFECT_TARGET, gBattlerTarget, FALSE))
|
||
effect = TRUE;
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_MOVE_EFFECTS2: // For effects which should happen after target items, for example Knock Off after damage from Rocky Helmet.
|
||
{
|
||
switch (gBattleStruct->moveEffect2)
|
||
{
|
||
case MOVE_EFFECT_KNOCK_OFF:
|
||
effect = TryKnockOffBattleScript(gBattlerTarget);
|
||
break;
|
||
case MOVE_EFFECT_STOCKPILE_WORE_OFF:
|
||
if (gDisableStructs[gBattlerAttacker].stockpileCounter != 0)
|
||
{
|
||
gDisableStructs[gBattlerAttacker].stockpileCounter = 0;
|
||
effect = TRUE;
|
||
BattleScriptPush(gBattlescriptCurrInstr);
|
||
gBattlescriptCurrInstr = BattleScript_MoveEffectStockpileWoreOff;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_SMACK_DOWN:
|
||
if (!IsBattlerGrounded(gBattlerTarget) && IsBattlerAlive(gBattlerTarget))
|
||
{
|
||
gStatuses3[gBattlerTarget] |= STATUS3_SMACKED_DOWN;
|
||
gStatuses3[gBattlerTarget] &= ~(STATUS3_MAGNET_RISE | STATUS3_TELEKINESIS | STATUS3_ON_AIR);
|
||
effect = TRUE;
|
||
BattleScriptPush(gBattlescriptCurrInstr);
|
||
gBattlescriptCurrInstr = BattleScript_MoveEffectSmackDown;
|
||
}
|
||
break;
|
||
case MOVE_EFFECT_REMOVE_STATUS: // Smelling salts, Wake-Up Slap, Sparkling Aria
|
||
if ((gBattleMons[gBattlerTarget].status1 & gBattleMoves[gCurrentMove].argument) && IsBattlerAlive(gBattlerTarget))
|
||
{
|
||
gBattleMons[gBattlerTarget].status1 &= ~(gBattleMoves[gCurrentMove].argument);
|
||
|
||
BtlController_EmitSetMonData(gBattlerTarget, 0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gBattlerTarget].status1);
|
||
MarkBattlerForControllerExec(gBattlerTarget);
|
||
effect = TRUE;
|
||
BattleScriptPush(gBattlescriptCurrInstr);
|
||
switch (gBattleMoves[gCurrentMove].argument)
|
||
{
|
||
case STATUS1_PARALYSIS:
|
||
gBattlescriptCurrInstr = BattleScript_TargetPRLZHeal;
|
||
break;
|
||
case STATUS1_SLEEP:
|
||
gBattlescriptCurrInstr = BattleScript_TargetWokeUp;
|
||
break;
|
||
case STATUS1_BURN:
|
||
gBattlescriptCurrInstr = BattleScript_TargetBurnHeal;
|
||
break;
|
||
}
|
||
}
|
||
break; // MOVE_EFFECT_REMOVE_STATUS
|
||
}
|
||
gBattleStruct->moveEffect2 = 0;
|
||
gBattleScripting.moveendState++;
|
||
break; // MOVEEND_MOVE_EFFECTS2
|
||
}
|
||
case MOVEEND_ITEM_EFFECTS_ALL: // item effects for all battlers
|
||
if (ItemBattleEffects(ITEMEFFECT_MOVE_END, 0, FALSE))
|
||
effect = TRUE;
|
||
else
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_KINGSROCK: // King's rock
|
||
// These effects will occur at each hit in a multi-strike move
|
||
if (ItemBattleEffects(ITEMEFFECT_KINGSROCK, 0, FALSE))
|
||
effect = TRUE;
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_ATTACKER_INVISIBLE: // make attacker sprite invisible
|
||
if (gStatuses3[gBattlerAttacker] & (STATUS3_SEMI_INVULNERABLE)
|
||
&& gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION))
|
||
{
|
||
BtlController_EmitSpriteInvisibility(gBattlerAttacker, BUFFER_A, TRUE);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
gBattleScripting.moveendState++;
|
||
return;
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_ATTACKER_VISIBLE: // make attacker sprite visible
|
||
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT
|
||
|| !(gStatuses3[gBattlerAttacker] & (STATUS3_SEMI_INVULNERABLE))
|
||
|| WasUnableToUseMove(gBattlerAttacker))
|
||
{
|
||
BtlController_EmitSpriteInvisibility(gBattlerAttacker, BUFFER_A, FALSE);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
gStatuses3[gBattlerAttacker] &= ~STATUS3_SEMI_INVULNERABLE;
|
||
gSpecialStatuses[gBattlerAttacker].restoredBattlerSprite = TRUE;
|
||
gBattleScripting.moveendState++;
|
||
return;
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_TARGET_VISIBLE: // make target sprite visible
|
||
if (!gSpecialStatuses[gBattlerTarget].restoredBattlerSprite && gBattlerTarget < gBattlersCount
|
||
&& !(gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE))
|
||
{
|
||
BtlController_EmitSpriteInvisibility(gBattlerTarget, BUFFER_A, FALSE);
|
||
MarkBattlerForControllerExec(gBattlerTarget);
|
||
gStatuses3[gBattlerTarget] &= ~STATUS3_SEMI_INVULNERABLE;
|
||
gBattleScripting.moveendState++;
|
||
return;
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_NUM_HITS:
|
||
if (gBattlerAttacker != gBattlerTarget
|
||
&& gBattleMoves[gCurrentMove].category != BATTLE_CATEGORY_STATUS
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
&& TARGET_TURN_DAMAGED)
|
||
{
|
||
gBattleStruct->timesGotHit[GetBattlerSide(gBattlerTarget)][gBattlerPartyIndexes[gBattlerTarget]]++;
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_SUBSTITUTE: // update substitute
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (gDisableStructs[i].substituteHP == 0)
|
||
gBattleMons[i].status2 &= ~STATUS2_SUBSTITUTE;
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_SKY_DROP_CONFUSE: // If a Pokemon was released from Sky Drop and was in LOCK_CONFUSE, go to "confused due to fatigue" scripts and clear Sky Drop data.
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (gBattleStruct->skyDropTargets[i] == 0xFE)
|
||
{
|
||
u8 targetId;
|
||
// Find the battler id of the Pokemon that was held by Sky Drop
|
||
for (targetId = 0; targetId < gBattlersCount; targetId++)
|
||
{
|
||
if (gBattleStruct->skyDropTargets[targetId] == i)
|
||
break;
|
||
}
|
||
|
||
// Set gBattlerAttacker to the battler id of the target
|
||
gBattlerAttacker = targetId;
|
||
|
||
// Jump to "confused due to fatigue" script
|
||
gBattlescriptCurrInstr = BattleScript_ThrashConfuses;
|
||
|
||
// Clear skyDropTargets data
|
||
gBattleStruct->skyDropTargets[i] = 0xFF;
|
||
gBattleStruct->skyDropTargets[targetId] = 0xFF;
|
||
return;
|
||
}
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_UPDATE_LAST_MOVES:
|
||
if (gMoveResultFlags & (MOVE_RESULT_FAILED | MOVE_RESULT_DOESNT_AFFECT_FOE))
|
||
gBattleStruct->lastMoveFailed |= gBitTable[gBattlerAttacker];
|
||
else
|
||
gBattleStruct->lastMoveFailed &= ~(gBitTable[gBattlerAttacker]);
|
||
|
||
// Set ShellTrap to activate after the attacker's turn if target was hit by a physical move.
|
||
if (gBattleMoves[gChosenMoveByBattler[gBattlerTarget]].effect == EFFECT_SHELL_TRAP
|
||
&& gBattlerTarget != gBattlerAttacker
|
||
&& GetBattlerSide(gBattlerTarget) != GetBattlerSide(gBattlerAttacker)
|
||
&& gProtectStructs[gBattlerTarget].physicalDmg
|
||
&& gProtectStructs[gBattlerTarget].physicalBattlerId == gBattlerAttacker
|
||
&& !TestSheerForceFlag(gBattlerAttacker, gCurrentMove))
|
||
{
|
||
gProtectStructs[gBattlerTarget].shellTrap = TRUE;
|
||
// Change move order in double battles, so the hit mon with shell trap moves immediately after being hit.
|
||
if (IsDoubleBattle())
|
||
{
|
||
ChangeOrderTargetAfterAttacker();
|
||
}
|
||
}
|
||
|
||
if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET)
|
||
{
|
||
u8 temp;
|
||
SWAP(gBattlerAttacker, gBattlerTarget, temp);
|
||
gHitMarker &= ~HITMARKER_SWAP_ATTACKER_TARGET;
|
||
}
|
||
if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove)
|
||
{
|
||
gDisableStructs[gBattlerAttacker].usedMoves |= gBitTable[gCurrMovePos];
|
||
gBattleStruct->lastMoveTarget[gBattlerAttacker] = gBattlerTarget;
|
||
if (gHitMarker & HITMARKER_ATTACKSTRING_PRINTED)
|
||
{
|
||
gLastPrintedMoves[gBattlerAttacker] = gChosenMove;
|
||
gLastUsedMove = gCurrentMove;
|
||
if (IsMaxMove(gCurrentMove))
|
||
gBattleStruct->dynamax.lastUsedBaseMove = gBattleStruct->dynamax.baseMove[gBattlerAttacker];
|
||
}
|
||
}
|
||
if (!(gAbsentBattlerFlags & gBitTable[gBattlerAttacker])
|
||
&& !(gBattleStruct->absentBattlerFlags & gBitTable[gBattlerAttacker])
|
||
&& gBattleMoves[originallyUsedMove].effect != EFFECT_BATON_PASS
|
||
&& gBattleMoves[originallyUsedMove].effect != EFFECT_HEALING_WISH)
|
||
{
|
||
if (gHitMarker & HITMARKER_OBEYS)
|
||
{
|
||
if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove)
|
||
{
|
||
gLastMoves[gBattlerAttacker] = gChosenMove;
|
||
RecordKnownMove(gBattlerAttacker, gChosenMove);
|
||
gLastResultingMoves[gBattlerAttacker] = gCurrentMove;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gLastMoves[gBattlerAttacker] = MOVE_UNAVAILABLE;
|
||
gLastResultingMoves[gBattlerAttacker] = MOVE_UNAVAILABLE;
|
||
}
|
||
|
||
if (!(gHitMarker & HITMARKER_FAINTED(gBattlerTarget)))
|
||
gLastHitBy[gBattlerTarget] = gBattlerAttacker;
|
||
|
||
if (gHitMarker & HITMARKER_OBEYS && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
|
||
{
|
||
if (gChosenMove == MOVE_UNAVAILABLE)
|
||
{
|
||
gLastLandedMoves[gBattlerTarget] = gChosenMove;
|
||
}
|
||
else
|
||
{
|
||
gLastLandedMoves[gBattlerTarget] = gCurrentMove;
|
||
GET_MOVE_TYPE(gCurrentMove, gLastHitByType[gBattlerTarget]);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gLastLandedMoves[gBattlerTarget] = MOVE_UNAVAILABLE;
|
||
}
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_MIRROR_MOVE: // mirror move
|
||
if (!(gAbsentBattlerFlags & gBitTable[gBattlerAttacker])
|
||
&& !(gBattleStruct->absentBattlerFlags & gBitTable[gBattlerAttacker])
|
||
&& !gBattleMoves[originallyUsedMove].mirrorMoveBanned
|
||
&& gHitMarker & HITMARKER_OBEYS
|
||
&& gBattlerAttacker != gBattlerTarget
|
||
&& !(gHitMarker & HITMARKER_FAINTED(gBattlerTarget))
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
|
||
{
|
||
gBattleStruct->lastTakenMove[gBattlerTarget] = gChosenMove;
|
||
gBattleStruct->lastTakenMoveFrom[gBattlerTarget][gBattlerAttacker] = gChosenMove;
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_MAGICIAN:
|
||
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_MAGICIAN
|
||
&& gCurrentMove != MOVE_FLING && gCurrentMove != MOVE_NATURAL_GIFT
|
||
&& gBattleMons[gBattlerAttacker].item == ITEM_NONE
|
||
&& gBattleMons[gBattlerTarget].item != ITEM_NONE
|
||
&& IsBattlerAlive(gBattlerAttacker)
|
||
&& TARGET_TURN_DAMAGED
|
||
&& CanStealItem(gBattlerAttacker, gBattlerTarget, gBattleMons[gBattlerTarget].item)
|
||
&& !gSpecialStatuses[gBattlerAttacker].gemBoost // In base game, gems are consumed after magician would activate.
|
||
&& !(gWishFutureKnock.knockedOffMons[GetBattlerSide(gBattlerTarget)] & gBitTable[gBattlerPartyIndexes[gBattlerTarget]])
|
||
&& !DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
&& (GetBattlerAbility(gBattlerTarget) != ABILITY_STICKY_HOLD || !IsBattlerAlive(gBattlerTarget)))
|
||
{
|
||
StealTargetItem(gBattlerAttacker, gBattlerTarget);
|
||
gBattleScripting.battler = gBattlerAbility = gBattlerAttacker;
|
||
gEffectBattler = gBattlerTarget;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_MagicianActivates;
|
||
gSpecialStatuses[gBattlerAttacker].magicianStolen = TRUE;
|
||
effect = TRUE;
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_NEXT_TARGET: // For moves hitting two opposing Pokémon.
|
||
{
|
||
u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
|
||
// Set a flag if move hits either target (for throat spray that can't check damage)
|
||
if (!(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
|
||
gProtectStructs[gBattlerAttacker].targetAffected = TRUE;
|
||
|
||
gBattleStruct->targetsDone[gBattlerAttacker] |= gBitTable[gBattlerTarget];
|
||
if (!(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
|
||
&& gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||
&& !gProtectStructs[gBattlerAttacker].chargingTurn
|
||
&& (moveTarget == MOVE_TARGET_BOTH
|
||
|| moveTarget == MOVE_TARGET_FOES_AND_ALLY)
|
||
&& !(gHitMarker & HITMARKER_NO_ATTACKSTRING))
|
||
{
|
||
u32 nextTarget = GetNextTarget(moveTarget);
|
||
gHitMarker |= HITMARKER_NO_PPDEDUCT;
|
||
|
||
if (nextTarget != MAX_BATTLERS_COUNT)
|
||
{
|
||
gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = nextTarget; // Fix for moxie spread moves
|
||
gBattleScripting.moveendState = 0;
|
||
MoveValuesCleanUp();
|
||
gBattleScripting.moveEffect = gBattleScripting.savedMoveEffect;
|
||
BattleScriptPush(gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]);
|
||
gBattlescriptCurrInstr = BattleScript_FlushMessageBox;
|
||
return;
|
||
}
|
||
// Check if the move used was actually a bounced move. If so, we need to go back to the original attacker and make sure, its move hits all 2 or 3 pokemon.
|
||
else if (gProtectStructs[gBattlerAttacker].usesBouncedMove)
|
||
{
|
||
u8 originalBounceTarget = gBattlerAttacker;
|
||
gBattlerAttacker = gBattleStruct->attackerBeforeBounce;
|
||
gBattleStruct->targetsDone[gBattlerAttacker] |= gBitTable[originalBounceTarget];
|
||
gBattleStruct->targetsDone[originalBounceTarget] = 0;
|
||
|
||
nextTarget = GetNextTarget(moveTarget);
|
||
if (nextTarget != MAX_BATTLERS_COUNT)
|
||
{
|
||
// We found another target for the original move user.
|
||
gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = nextTarget;
|
||
gBattleScripting.moveendState = 0;
|
||
gBattleScripting.animTurn = 0;
|
||
gBattleScripting.animTargetsHit = 0;
|
||
MoveValuesCleanUp();
|
||
BattleScriptPush(gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]);
|
||
gBattlescriptCurrInstr = BattleScript_FlushMessageBox;
|
||
return;
|
||
}
|
||
}
|
||
|
||
gHitMarker |= HITMARKER_NO_ATTACKSTRING;
|
||
gHitMarker &= ~HITMARKER_NO_PPDEDUCT;
|
||
}
|
||
RecordLastUsedMoveBy(gBattlerAttacker, gCurrentMove);
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
}
|
||
case MOVEEND_MULTIHIT_MOVE:
|
||
{
|
||
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
&& !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
|
||
&& gMultiHitCounter
|
||
&& !(gCurrentMove == MOVE_PRESENT && gBattleStruct->presentBasePower == 0)) // Silly edge case
|
||
{
|
||
gBattleScripting.multihitString[4]++;
|
||
if (--gMultiHitCounter == 0)
|
||
{
|
||
if (gBattleMoves[gCurrentMove].argument == MOVE_EFFECT_SCALE_SHOT && !NoAliveMonsForEitherParty())
|
||
{
|
||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = BattleScript_DefDownSpeedUp;
|
||
}
|
||
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_MultiHitPrintStrings;
|
||
effect = TRUE;
|
||
}
|
||
else
|
||
{
|
||
if (gCurrentMove == MOVE_DRAGON_DARTS)
|
||
{
|
||
// TODO
|
||
}
|
||
|
||
if (gBattleMons[gBattlerAttacker].hp
|
||
&& gBattleMons[gBattlerTarget].hp
|
||
&& (gChosenMove == MOVE_SLEEP_TALK || !(gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP))
|
||
&& !(gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE))
|
||
{
|
||
if (gSpecialStatuses[gBattlerAttacker].parentalBondState)
|
||
gSpecialStatuses[gBattlerAttacker].parentalBondState--;
|
||
|
||
gHitMarker |= (HITMARKER_NO_PPDEDUCT | HITMARKER_NO_ATTACKSTRING);
|
||
gBattleScripting.animTargetsHit = 0;
|
||
gBattleScripting.moveendState = 0;
|
||
gSpecialStatuses[gBattlerTarget].sturdied = 0;
|
||
gSpecialStatuses[gBattlerTarget].focusBanded = 0; // Delete this line to make Focus Band last for the duration of the whole move turn.
|
||
gSpecialStatuses[gBattlerTarget].focusSashed = 0; // Delete this line to make Focus Sash last for the duration of the whole move turn.
|
||
gSpecialStatuses[gBattlerAttacker].multiHitOn = TRUE;
|
||
MoveValuesCleanUp();
|
||
BattleScriptPush(gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]);
|
||
gBattlescriptCurrInstr = BattleScript_FlushMessageBox;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_MultiHitPrintStrings;
|
||
effect = TRUE;
|
||
}
|
||
}
|
||
}
|
||
gMultiHitCounter = 0;
|
||
gSpecialStatuses[gBattlerAttacker].parentalBondState = PARENTAL_BOND_OFF;
|
||
gSpecialStatuses[gBattlerAttacker].multiHitOn = 0;
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
}
|
||
case MOVEEND_EJECT_BUTTON:
|
||
if (gBattleMoves[gCurrentMove].effect != EFFECT_HIT_SWITCH_TARGET
|
||
&& IsBattlerAlive(gBattlerAttacker)
|
||
&& !TestSheerForceFlag(gBattlerAttacker, gCurrentMove)
|
||
&& (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER || (gBattleTypeFlags & BATTLE_TYPE_TRAINER)))
|
||
{
|
||
// Since we check if battler was damaged, we don't need to check move result.
|
||
// In fact, doing so actually prevents multi-target moves from activating eject button properly
|
||
u8 battlers[4] = {0, 1, 2, 3};
|
||
SortBattlersBySpeed(battlers, FALSE);
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
u8 battler = battlers[i];
|
||
// Attacker is the damage-dealer, battler is mon to be switched out
|
||
if (IsBattlerAlive(battler)
|
||
&& gBattlerAttacker != battler
|
||
&& GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_EJECT_BUTTON
|
||
&& BATTLER_TURN_DAMAGED(battler)
|
||
&& CountUsablePartyMons(battler) > 0) // Has mon to switch into
|
||
{
|
||
gBattleScripting.battler = battler;
|
||
gLastUsedItem = gBattleMons[battler].item;
|
||
if (gBattleMoves[gCurrentMove].effect == EFFECT_HIT_ESCAPE)
|
||
gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_EjectButtonActivates;
|
||
effect = TRUE;
|
||
break; // Only the fastest Eject Button activates
|
||
}
|
||
}
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_RED_CARD:
|
||
if ((gBattleMoves[gCurrentMove].effect != EFFECT_HIT_SWITCH_TARGET || gBattleStruct->hitSwitchTargetFailed)
|
||
&& IsBattlerAlive(gBattlerAttacker)
|
||
&& !TestSheerForceFlag(gBattlerAttacker, gCurrentMove)
|
||
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_GUARD_DOG)
|
||
{
|
||
// Since we check if battler was damaged, we don't need to check move result.
|
||
// In fact, doing so actually prevents multi-target moves from activating red card properly
|
||
u8 battlers[4] = {0, 1, 2, 3};
|
||
SortBattlersBySpeed(battlers, FALSE);
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
u8 battler = battlers[i];
|
||
// Search for fastest hit pokemon with a red card
|
||
// Attacker is the one to be switched out, battler is one with red card
|
||
if (battler != gBattlerAttacker
|
||
&& IsBattlerAlive(battler)
|
||
&& !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove)
|
||
&& GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_RED_CARD
|
||
&& BATTLER_TURN_DAMAGED(battler)
|
||
&& CanBattlerSwitch(gBattlerAttacker))
|
||
{
|
||
gLastUsedItem = gBattleMons[battler].item;
|
||
gBattleStruct->savedBattlerTarget = gBattleScripting.battler = battler; // Battler with red card
|
||
gEffectBattler = gBattlerAttacker;
|
||
if (gBattleMoves[gCurrentMove].effect == EFFECT_HIT_ESCAPE)
|
||
gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_RedCardActivates;
|
||
effect = TRUE;
|
||
break; // Only fastest red card activates
|
||
}
|
||
}
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_EJECT_PACK:
|
||
{
|
||
u8 battlers[4] = {0, 1, 2, 3};
|
||
SortBattlersBySpeed(battlers, FALSE);
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
u8 battler = battlers[i];
|
||
if (IsBattlerAlive(battler)
|
||
&& gProtectStructs[battler].statFell
|
||
&& gProtectStructs[battler].disableEjectPack == 0
|
||
&& GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_EJECT_PACK
|
||
&& !(gCurrentMove == MOVE_PARTING_SHOT && CanBattlerSwitch(gBattlerAttacker)) // Does not activate if attacker used Parting Shot and can switch out
|
||
&& CountUsablePartyMons(battler) > 0) // Has mon to switch into
|
||
{
|
||
gProtectStructs[battler].statFell = FALSE;
|
||
gBattleScripting.battler = battler;
|
||
gLastUsedItem = gBattleMons[battler].item;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_EjectPackActivates;
|
||
effect = TRUE;
|
||
break; // Only fastest eject pack activates
|
||
}
|
||
}
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_LIFEORB_SHELLBELL:
|
||
if (ItemBattleEffects(ITEMEFFECT_LIFEORB_SHELLBELL, 0, FALSE))
|
||
effect = TRUE;
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_PICKPOCKET:
|
||
if (IsBattlerAlive(gBattlerAttacker)
|
||
&& gBattleMons[gBattlerAttacker].item != ITEM_NONE // Attacker must be holding an item
|
||
&& !(gWishFutureKnock.knockedOffMons[GetBattlerSide(gBattlerAttacker)] & gBitTable[gBattlerPartyIndexes[gBattlerAttacker]]) // But not knocked off
|
||
&& !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) // Pickpocket doesn't activate for sheer force
|
||
&& IsMoveMakingContact(gCurrentMove, gBattlerAttacker) // Pickpocket requires contact
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) // Obviously attack needs to have worked
|
||
{
|
||
u8 battlers[4] = {0, 1, 2, 3};
|
||
SortBattlersBySpeed(battlers, FALSE); // Pickpocket activates for fastest mon without item
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
u8 battler = battlers[i];
|
||
// Attacker is mon who made contact, battler is mon with pickpocket
|
||
if (battler != gBattlerAttacker // Cannot pickpocket yourself
|
||
&& GetBattlerAbility(battler) == ABILITY_PICKPOCKET // Target must have pickpocket ability
|
||
&& BATTLER_TURN_DAMAGED(battler) // Target needs to have been damaged
|
||
&& !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) // Subsitute unaffected
|
||
&& IsBattlerAlive(battler) // Battler must be alive to pickpocket
|
||
&& gBattleMons[battler].item == ITEM_NONE // Pickpocketer can't have an item already
|
||
&& CanStealItem(battler, gBattlerAttacker, gBattleMons[gBattlerAttacker].item)) // Cannot steal plates, mega stones, etc
|
||
{
|
||
gBattlerTarget = gBattlerAbility = battler;
|
||
// Battle scripting is super brittle so we shall do the item exchange now (if possible)
|
||
if (GetBattlerAbility(gBattlerAttacker) != ABILITY_STICKY_HOLD)
|
||
StealTargetItem(gBattlerTarget, gBattlerAttacker); // Target takes attacker's item
|
||
|
||
gEffectBattler = gBattlerAttacker;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_Pickpocket; // Includes sticky hold check to print separate string
|
||
effect = TRUE;
|
||
break; // Pickpocket activates on fastest mon, so exit loop.
|
||
}
|
||
}
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_DANCER: // Special case because it's so annoying
|
||
if (gBattleMoves[gCurrentMove].danceMove)
|
||
{
|
||
u8 battler, nextDancer = 0;
|
||
|
||
if (!(gBattleStruct->lastMoveFailed & gBitTable[gBattlerAttacker]
|
||
|| (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove
|
||
&& gProtectStructs[gBattlerAttacker].usesBouncedMove)))
|
||
{ // Dance move succeeds
|
||
// Set target for other Dancer mons; set bit so that mon cannot activate Dancer off of its own move
|
||
if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove)
|
||
{
|
||
gBattleScripting.savedBattler = gBattlerTarget | 0x4;
|
||
gBattleScripting.savedBattler |= (gBattlerAttacker << 4);
|
||
gSpecialStatuses[gBattlerAttacker].dancerUsedMove = TRUE;
|
||
}
|
||
for (battler = 0; battler < MAX_BATTLERS_COUNT; battler++)
|
||
{
|
||
if (GetBattlerAbility(battler) == ABILITY_DANCER && !gSpecialStatuses[battler].dancerUsedMove)
|
||
{
|
||
if (!nextDancer || (gBattleMons[battler].speed < gBattleMons[nextDancer & 0x3].speed))
|
||
nextDancer = battler | 0x4;
|
||
}
|
||
}
|
||
if (nextDancer && AbilityBattleEffects(ABILITYEFFECT_MOVE_END_OTHER, nextDancer & 0x3, 0, 0, 0))
|
||
effect = TRUE;
|
||
}
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_EMERGENCY_EXIT: // Special case, because moves hitting multiple opponents stop after switching out
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (gBattleResources->flags->flags[i] & RESOURCE_FLAG_EMERGENCY_EXIT)
|
||
{
|
||
gBattleResources->flags->flags[i] &= ~RESOURCE_FLAG_EMERGENCY_EXIT;
|
||
gSpecialStatuses[i].emergencyExited = TRUE;
|
||
gBattlerTarget = gBattlerAbility = i;
|
||
BattleScriptPushCursor();
|
||
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || GetBattlerSide(i) == B_SIDE_PLAYER)
|
||
{
|
||
if (B_ABILITY_POP_UP == TRUE)
|
||
gBattlescriptCurrInstr = BattleScript_EmergencyExit;
|
||
else
|
||
gBattlescriptCurrInstr = BattleScript_EmergencyExitNoPopUp;
|
||
}
|
||
else
|
||
{
|
||
if (B_ABILITY_POP_UP == TRUE)
|
||
gBattlescriptCurrInstr = BattleScript_EmergencyExitWild;
|
||
else
|
||
gBattlescriptCurrInstr = BattleScript_EmergencyExitWildNoPopUp;
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_SYMBIOSIS:
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if ((gSpecialStatuses[i].berryReduced
|
||
|| (B_SYMBIOSIS_GEMS >= GEN_7 && gSpecialStatuses[i].gemBoost))
|
||
&& SYMBIOSIS_CHECK(i, BATTLE_PARTNER(i)))
|
||
{
|
||
BestowItem(BATTLE_PARTNER(i), i);
|
||
gLastUsedAbility = gBattleMons[BATTLE_PARTNER(i)].ability;
|
||
gBattleScripting.battler = gBattlerAbility = BATTLE_PARTNER(i);
|
||
gBattlerAttacker = i;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_SymbiosisActivates;
|
||
effect = TRUE;
|
||
}
|
||
}
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_SAME_MOVE_TURNS:
|
||
if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
gBattleStruct->sameMoveTurns[gBattlerAttacker] = 0;
|
||
else if (gCurrentMove == gLastResultingMoves[gBattlerAttacker] && gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT)
|
||
gBattleStruct->sameMoveTurns[gBattlerAttacker]++;
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits.
|
||
if (gSpecialStatuses[gBattlerAttacker].instructedChosenTarget)
|
||
*(gBattleStruct->moveTarget + gBattlerAttacker) = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3;
|
||
if (gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget)
|
||
*(gBattleStruct->moveTarget + gBattlerAttacker) = gSpecialStatuses[gBattlerAttacker].dancerOriginalTarget & 0x3;
|
||
|
||
if (B_RAMPAGE_CANCELLING >= GEN_5
|
||
&& gBattleMoves[gCurrentMove].effect == EFFECT_RAMPAGE // If we're rampaging
|
||
&& (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
|
||
|
||
gBattleStruct->targetsDone[gBattlerAttacker] = 0;
|
||
gProtectStructs[gBattlerAttacker].usesBouncedMove = FALSE;
|
||
gProtectStructs[gBattlerAttacker].targetAffected = FALSE;
|
||
gProtectStructs[gBattlerAttacker].shellTrap = FALSE;
|
||
gBattleStruct->ateBoost[gBattlerAttacker] = 0;
|
||
gStatuses3[gBattlerAttacker] &= ~STATUS3_ME_FIRST;
|
||
gSpecialStatuses[gBattlerAttacker].gemBoost = FALSE;
|
||
gSpecialStatuses[gBattlerAttacker].damagedMons = 0;
|
||
gSpecialStatuses[gBattlerAttacker].magicianStolen = 0;
|
||
gSpecialStatuses[gBattlerTarget].berryReduced = FALSE;
|
||
gBattleScripting.moveEffect = 0;
|
||
// clear attacker z move data
|
||
gBattleStruct->zmove.active = FALSE;
|
||
gBattleStruct->zmove.toBeUsed[gBattlerAttacker] = MOVE_NONE;
|
||
gBattleStruct->zmove.effect = EFFECT_HIT;
|
||
gBattleStruct->hitSwitchTargetFailed = FALSE;
|
||
gBattleStruct->isAtkCancelerForCalledMove = FALSE;
|
||
gBattleStruct->swapDamageCategory = FALSE;
|
||
gBattleStruct->enduredDamage = 0;
|
||
gBattleScripting.moveendState++;
|
||
break;
|
||
case MOVEEND_COUNT:
|
||
break;
|
||
}
|
||
|
||
if (endMode == 1 && effect == FALSE)
|
||
gBattleScripting.moveendState = MOVEEND_COUNT;
|
||
if (endMode == 2 && endState == gBattleScripting.moveendState)
|
||
gBattleScripting.moveendState = MOVEEND_COUNT;
|
||
|
||
} while (gBattleScripting.moveendState != MOVEEND_COUNT && effect == FALSE);
|
||
|
||
if (gBattleScripting.moveendState == MOVEEND_COUNT && effect == FALSE)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_sethealblock(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gStatuses3[gBattlerTarget] & STATUS3_HEAL_BLOCK)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gStatuses3[gBattlerTarget] |= STATUS3_HEAL_BLOCK;
|
||
gDisableStructs[gBattlerTarget].healBlockTimer = 5;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_returnatktoball(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (!(gHitMarker & HITMARKER_FAINTED(gBattlerAttacker)))
|
||
{
|
||
BtlController_EmitReturnMonToBall(gBattlerAttacker, BUFFER_A, FALSE);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_getswitchedmondata(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
gBattlerPartyIndexes[battler] = gBattleStruct->monToSwitchIntoId[battler];
|
||
|
||
BtlController_EmitGetMonData(battler, BUFFER_A, REQUEST_ALL_BATTLE, gBitTable[gBattlerPartyIndexes[battler]]);
|
||
MarkBattlerForControllerExec(battler);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_switchindataupdate(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
struct BattlePokemon oldData;
|
||
u32 battler, i;
|
||
u8 *monData;
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
oldData = gBattleMons[battler];
|
||
monData = (u8 *)(&gBattleMons[battler]);
|
||
|
||
for (i = 0; i < sizeof(struct BattlePokemon); i++)
|
||
monData[i] = gBattleResources->bufferB[battler][4 + i];
|
||
|
||
// Edge case: the sent out pokemon has 0 HP. This should never happen.
|
||
if (gBattleMons[battler].hp == 0)
|
||
{
|
||
// If it's a test, mark it as invalid.
|
||
if (gTestRunnerEnabled)
|
||
{
|
||
TestRunner_Battle_InvalidNoHPMon(battler, gBattlerPartyIndexes[battler]);
|
||
}
|
||
// Handle in-game scenario.
|
||
else
|
||
{
|
||
struct Pokemon *party = GetBattlerParty(battler);
|
||
// Find the first possible replacement for the not valid pokemon.
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
if (IsValidForBattle(&party[i]))
|
||
break;
|
||
}
|
||
// There is valid replacement.
|
||
if (i != PARTY_SIZE)
|
||
{
|
||
gBattlerPartyIndexes[battler] = gBattleStruct->monToSwitchIntoId[battler] = i;
|
||
BtlController_EmitGetMonData(battler, BUFFER_A, REQUEST_ALL_BATTLE, gBitTable[gBattlerPartyIndexes[battler]]);
|
||
MarkBattlerForControllerExec(battler);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
gBattleMons[battler].type1 = gSpeciesInfo[gBattleMons[battler].species].types[0];
|
||
gBattleMons[battler].type2 = gSpeciesInfo[gBattleMons[battler].species].types[1];
|
||
gBattleMons[battler].type3 = TYPE_MYSTERY;
|
||
gBattleMons[battler].ability = GetAbilityBySpecies(gBattleMons[battler].species, gBattleMons[battler].abilityNum);
|
||
|
||
// check knocked off item
|
||
i = GetBattlerSide(battler);
|
||
if (gWishFutureKnock.knockedOffMons[i] & gBitTable[gBattlerPartyIndexes[battler]])
|
||
{
|
||
gBattleMons[battler].item = ITEM_NONE;
|
||
}
|
||
|
||
if (gBattleMoves[gCurrentMove].effect == EFFECT_BATON_PASS)
|
||
{
|
||
for (i = 0; i < NUM_BATTLE_STATS; i++)
|
||
{
|
||
gBattleMons[battler].statStages[i] = oldData.statStages[i];
|
||
}
|
||
gBattleMons[battler].status2 = oldData.status2;
|
||
}
|
||
|
||
SwitchInClearSetData(battler);
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_PALACE
|
||
&& gBattleMons[battler].maxHP / 2 >= gBattleMons[battler].hp
|
||
&& gBattleMons[battler].hp != 0
|
||
&& !(gBattleMons[battler].status1 & STATUS1_SLEEP))
|
||
{
|
||
gBattleStruct->palaceFlags |= gBitTable[battler];
|
||
}
|
||
|
||
gBattleScripting.battler = battler;
|
||
|
||
PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, battler, gBattlerPartyIndexes[battler]);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_switchinanim(void)
|
||
{
|
||
u32 battler;
|
||
|
||
CMD_ARGS(u8 battler, bool8 dontClearSubstitute);
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
|
||
if (GetBattlerSide(battler) == B_SIDE_OPPONENT
|
||
&& !(gBattleTypeFlags & (BATTLE_TYPE_LINK
|
||
| BATTLE_TYPE_EREADER_TRAINER
|
||
| BATTLE_TYPE_RECORDED_LINK
|
||
| BATTLE_TYPE_TRAINER_HILL
|
||
| BATTLE_TYPE_FRONTIER)))
|
||
HandleSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[battler].species), FLAG_SET_SEEN, gBattleMons[battler].personality);
|
||
|
||
gAbsentBattlerFlags &= ~(gBitTable[battler]);
|
||
|
||
BtlController_EmitSwitchInAnim(battler, BUFFER_A, gBattlerPartyIndexes[battler], cmd->dontClearSubstitute);
|
||
MarkBattlerForControllerExec(battler);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||
BattleArena_InitPoints();
|
||
}
|
||
|
||
bool32 CanBattlerSwitch(u32 battler)
|
||
{
|
||
s32 i, lastMonId, battlerIn1, battlerIn2;
|
||
bool32 ret = FALSE;
|
||
struct Pokemon *party;
|
||
|
||
if (BATTLE_TWO_VS_ONE_OPPONENT && GetBattlerSide(battler) == B_SIDE_OPPONENT)
|
||
{
|
||
battlerIn1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||
battlerIn2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||
party = gEnemyParty;
|
||
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
if (GetMonData(&party[i], MON_DATA_HP) != 0
|
||
&& GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE
|
||
&& !GetMonData(&party[i], MON_DATA_IS_EGG)
|
||
&& i != gBattlerPartyIndexes[battlerIn1] && i != gBattlerPartyIndexes[battlerIn2])
|
||
break;
|
||
}
|
||
|
||
ret = (i != PARTY_SIZE);
|
||
}
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
|
||
{
|
||
party = GetBattlerParty(battler);
|
||
|
||
lastMonId = 0;
|
||
if (battler & 2)
|
||
lastMonId = MULTI_PARTY_SIZE;
|
||
|
||
for (i = lastMonId; i < lastMonId + MULTI_PARTY_SIZE; i++)
|
||
{
|
||
if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE
|
||
&& !GetMonData(&party[i], MON_DATA_IS_EGG)
|
||
&& GetMonData(&party[i], MON_DATA_HP) != 0
|
||
&& gBattlerPartyIndexes[battler] != i)
|
||
break;
|
||
}
|
||
|
||
ret = (i != lastMonId + MULTI_PARTY_SIZE);
|
||
}
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
|
||
{
|
||
if (gBattleTypeFlags & BATTLE_TYPE_TOWER_LINK_MULTI)
|
||
{
|
||
if (GetBattlerSide(battler) == B_SIDE_PLAYER)
|
||
{
|
||
party = gPlayerParty;
|
||
|
||
lastMonId = 0;
|
||
if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(battler)) == TRUE)
|
||
lastMonId = MULTI_PARTY_SIZE;
|
||
}
|
||
else
|
||
{
|
||
party = gEnemyParty;
|
||
|
||
if (battler == 1)
|
||
lastMonId = 0;
|
||
else
|
||
lastMonId = MULTI_PARTY_SIZE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
party = GetBattlerParty(battler);
|
||
|
||
lastMonId = 0;
|
||
if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(battler)) == TRUE)
|
||
lastMonId = MULTI_PARTY_SIZE;
|
||
}
|
||
|
||
for (i = lastMonId; i < lastMonId + MULTI_PARTY_SIZE; i++)
|
||
{
|
||
if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE
|
||
&& !GetMonData(&party[i], MON_DATA_IS_EGG)
|
||
&& GetMonData(&party[i], MON_DATA_HP) != 0
|
||
&& gBattlerPartyIndexes[battler] != i)
|
||
break;
|
||
}
|
||
|
||
ret = (i != lastMonId + MULTI_PARTY_SIZE);
|
||
}
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && GetBattlerSide(battler) == B_SIDE_OPPONENT)
|
||
{
|
||
party = gEnemyParty;
|
||
|
||
lastMonId = 0;
|
||
if (battler == B_POSITION_OPPONENT_RIGHT)
|
||
lastMonId = PARTY_SIZE / 2;
|
||
|
||
for (i = lastMonId; i < lastMonId + (PARTY_SIZE / 2); i++)
|
||
{
|
||
if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE
|
||
&& !GetMonData(&party[i], MON_DATA_IS_EGG)
|
||
&& GetMonData(&party[i], MON_DATA_HP) != 0
|
||
&& gBattlerPartyIndexes[battler] != i)
|
||
break;
|
||
}
|
||
|
||
ret = (i != lastMonId + (PARTY_SIZE / 2));
|
||
}
|
||
else
|
||
{
|
||
if (GetBattlerSide(battler) == B_SIDE_OPPONENT)
|
||
{
|
||
battlerIn1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||
battlerIn2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||
else
|
||
battlerIn2 = battlerIn1;
|
||
|
||
party = gEnemyParty;
|
||
}
|
||
else
|
||
{
|
||
// Check if attacker side has mon to switch into
|
||
battlerIn1 = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||
battlerIn2 = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
|
||
else
|
||
battlerIn2 = battlerIn1;
|
||
|
||
party = gPlayerParty;
|
||
}
|
||
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
if (GetMonData(&party[i], MON_DATA_HP) != 0
|
||
&& GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE
|
||
&& !GetMonData(&party[i], MON_DATA_IS_EGG)
|
||
&& i != gBattlerPartyIndexes[battlerIn1] && i != gBattlerPartyIndexes[battlerIn2])
|
||
break;
|
||
}
|
||
|
||
ret = (i != PARTY_SIZE);
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
static void Cmd_jumpifcantswitch(void)
|
||
{
|
||
CMD_ARGS(u8 battler:7, u8 ignoreEscapePrevention:1, const u8 *jumpInstr);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (!cmd->ignoreEscapePrevention && !CanBattlerEscape(battler))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
}
|
||
else
|
||
{
|
||
if (CanBattlerSwitch(battler))
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
}
|
||
}
|
||
|
||
// Opens the party screen to choose a new Pokémon to send out.
|
||
// slotId is the Pokémon to replace.
|
||
// Note that this is not used by the Switch action, only replacing fainted Pokémon or Baton Pass
|
||
static void ChooseMonToSendOut(u32 battler, u8 slotId)
|
||
{
|
||
gBattleStruct->battlerPartyIndexes[battler] = gBattlerPartyIndexes[battler];
|
||
gBattleStruct->monToSwitchIntoId[battler] = PARTY_SIZE;
|
||
gBattleStruct->field_93 &= ~(gBitTable[battler]);
|
||
|
||
BtlController_EmitChoosePokemon(battler, BUFFER_A, PARTY_ACTION_SEND_OUT, slotId, ABILITY_NONE, gBattleStruct->battlerPartyOrders[battler]);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
|
||
static void Cmd_openpartyscreen(void)
|
||
{
|
||
CMD_ARGS(u8 battler:7, u8 partyScreenOptional:1, const u8 *failInstr);
|
||
|
||
u32 flags = 0;
|
||
u8 hitmarkerFaintBits = 0;
|
||
u32 i, battler = 0;
|
||
const u8 *failInstr = cmd->failInstr;
|
||
|
||
if (cmd->battler == BS_FAINTED_LINK_MULTIPLE_1)
|
||
{
|
||
if ((gBattleTypeFlags & BATTLE_TYPE_MULTI) || !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||
{
|
||
for (battler = 0; battler < gBattlersCount; battler++)
|
||
{
|
||
if (gHitMarker & HITMARKER_FAINTED(battler))
|
||
{
|
||
if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE))
|
||
{
|
||
gAbsentBattlerFlags |= gBitTable[battler];
|
||
gHitMarker &= ~HITMARKER_FAINTED(battler);
|
||
BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
else if (!gSpecialStatuses[battler].faintedHasReplacement)
|
||
{
|
||
ChooseMonToSendOut(battler, PARTY_SIZE);
|
||
gSpecialStatuses[battler].faintedHasReplacement = TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
}
|
||
}
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||
{
|
||
bool8 hasReplacement_0, hasReplacement_1, hasReplacement_2, hasReplacement_3;
|
||
|
||
hitmarkerFaintBits = gHitMarker >> 28;
|
||
|
||
if (gBitTable[0] & hitmarkerFaintBits)
|
||
{
|
||
battler = 0;
|
||
if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE))
|
||
{
|
||
gAbsentBattlerFlags |= gBitTable[battler];
|
||
gHitMarker &= ~HITMARKER_FAINTED(battler);
|
||
BtlController_EmitCantSwitch(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
else if (!gSpecialStatuses[battler].faintedHasReplacement)
|
||
{
|
||
ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[2]);
|
||
gSpecialStatuses[battler].faintedHasReplacement = TRUE;
|
||
}
|
||
else
|
||
{
|
||
BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE);
|
||
MarkBattlerForControllerExec(battler);
|
||
flags |= 1;
|
||
}
|
||
}
|
||
if (gBitTable[2] & hitmarkerFaintBits && !(gBitTable[0] & hitmarkerFaintBits))
|
||
{
|
||
battler = 2;
|
||
if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE))
|
||
{
|
||
gAbsentBattlerFlags |= gBitTable[battler];
|
||
gHitMarker &= ~HITMARKER_FAINTED(battler);
|
||
BtlController_EmitCantSwitch(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
else if (!gSpecialStatuses[battler].faintedHasReplacement)
|
||
{
|
||
ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[0]);
|
||
gSpecialStatuses[battler].faintedHasReplacement = TRUE;
|
||
}
|
||
else if (!(flags & 1))
|
||
{
|
||
BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
}
|
||
if (gBitTable[1] & hitmarkerFaintBits)
|
||
{
|
||
battler = 1;
|
||
if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE))
|
||
{
|
||
gAbsentBattlerFlags |= gBitTable[battler];
|
||
gHitMarker &= ~HITMARKER_FAINTED(battler);
|
||
BtlController_EmitCantSwitch(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
else if (!gSpecialStatuses[battler].faintedHasReplacement)
|
||
{
|
||
ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[3]);
|
||
gSpecialStatuses[battler].faintedHasReplacement = TRUE;
|
||
}
|
||
else
|
||
{
|
||
BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE);
|
||
MarkBattlerForControllerExec(battler);
|
||
flags |= 2;
|
||
}
|
||
}
|
||
if (gBitTable[3] & hitmarkerFaintBits && !(gBitTable[1] & hitmarkerFaintBits))
|
||
{
|
||
battler = 3;
|
||
if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE))
|
||
{
|
||
gAbsentBattlerFlags |= gBitTable[battler];
|
||
gHitMarker &= ~HITMARKER_FAINTED(battler);
|
||
BtlController_EmitCantSwitch(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
else if (!gSpecialStatuses[battler].faintedHasReplacement)
|
||
{
|
||
ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[1]);
|
||
gSpecialStatuses[battler].faintedHasReplacement = TRUE;
|
||
}
|
||
else if (!(flags & 2))
|
||
{
|
||
BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
}
|
||
|
||
hasReplacement_0 = gSpecialStatuses[0].faintedHasReplacement;
|
||
if (!hasReplacement_0)
|
||
{
|
||
hasReplacement_2 = gSpecialStatuses[2].faintedHasReplacement;
|
||
if (!hasReplacement_2 && hitmarkerFaintBits != 0)
|
||
{
|
||
if (gAbsentBattlerFlags & gBitTable[0])
|
||
battler = 2;
|
||
else
|
||
battler = 0;
|
||
|
||
BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
|
||
}
|
||
hasReplacement_1 = gSpecialStatuses[1].faintedHasReplacement;
|
||
if (!hasReplacement_1)
|
||
{
|
||
hasReplacement_3 = gSpecialStatuses[3].faintedHasReplacement;
|
||
if (!hasReplacement_3 && hitmarkerFaintBits != 0)
|
||
{
|
||
if (gAbsentBattlerFlags & gBitTable[1])
|
||
battler = 3;
|
||
else
|
||
battler = 1;
|
||
|
||
BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
}
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else if (cmd->battler == BS_FAINTED_LINK_MULTIPLE_2)
|
||
{
|
||
if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI))
|
||
{
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||
{
|
||
hitmarkerFaintBits = gHitMarker >> 28;
|
||
if (gBitTable[2] & hitmarkerFaintBits && gBitTable[0] & hitmarkerFaintBits)
|
||
{
|
||
battler = 2;
|
||
if (HasNoMonsToSwitch(battler, gBattleResources->bufferB[0][1], PARTY_SIZE))
|
||
{
|
||
gAbsentBattlerFlags |= gBitTable[battler];
|
||
gHitMarker &= ~HITMARKER_FAINTED(battler);
|
||
BtlController_EmitCantSwitch(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
else if (!gSpecialStatuses[battler].faintedHasReplacement)
|
||
{
|
||
ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[0]);
|
||
gSpecialStatuses[battler].faintedHasReplacement = TRUE;
|
||
}
|
||
}
|
||
if (gBitTable[3] & hitmarkerFaintBits && hitmarkerFaintBits & gBitTable[1])
|
||
{
|
||
battler = 3;
|
||
if (HasNoMonsToSwitch(battler, gBattleResources->bufferB[1][1], PARTY_SIZE))
|
||
{
|
||
gAbsentBattlerFlags |= gBitTable[battler];
|
||
gHitMarker &= ~HITMARKER_FAINTED(battler);
|
||
BtlController_EmitCantSwitch(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
else if (!gSpecialStatuses[battler].faintedHasReplacement)
|
||
{
|
||
ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[1]);
|
||
gSpecialStatuses[battler].faintedHasReplacement = TRUE;
|
||
}
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
// Not multi or double battle
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Multi battle
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
hitmarkerFaintBits = gHitMarker >> 28;
|
||
|
||
gBattlerFainted = 0;
|
||
while (!(gBitTable[gBattlerFainted] & hitmarkerFaintBits)
|
||
&& gBattlerFainted < gBattlersCount)
|
||
gBattlerFainted++;
|
||
|
||
if (gBattlerFainted == gBattlersCount)
|
||
gBattlescriptCurrInstr = failInstr;
|
||
}
|
||
else
|
||
{
|
||
if (cmd->partyScreenOptional)
|
||
hitmarkerFaintBits = PARTY_ACTION_CHOOSE_MON; // Used here as the caseId for the EmitChoose function.
|
||
else
|
||
hitmarkerFaintBits = PARTY_ACTION_SEND_OUT;
|
||
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (gSpecialStatuses[battler].faintedHasReplacement)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE))
|
||
{
|
||
gAbsentBattlerFlags |= gBitTable[battler];
|
||
gHitMarker &= ~HITMARKER_FAINTED(battler);
|
||
gBattlescriptCurrInstr = failInstr;
|
||
}
|
||
else
|
||
{
|
||
*(gBattleStruct->battlerPartyIndexes + battler) = gBattlerPartyIndexes[battler];
|
||
*(gBattleStruct->monToSwitchIntoId + battler) = PARTY_SIZE;
|
||
gBattleStruct->field_93 &= ~(gBitTable[battler]);
|
||
|
||
BtlController_EmitChoosePokemon(battler, BUFFER_A, hitmarkerFaintBits, *(gBattleStruct->monToSwitchIntoId + BATTLE_PARTNER(battler)), ABILITY_NONE, gBattleStruct->battlerPartyOrders[battler]);
|
||
MarkBattlerForControllerExec(battler);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
if (GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT && gBattleResults.playerSwitchesCounter < 255)
|
||
gBattleResults.playerSwitchesCounter++;
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
|
||
{
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (i != battler)
|
||
{
|
||
BtlController_EmitLinkStandbyMsg(i, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE);
|
||
MarkBattlerForControllerExec(i);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
u32 battlerOpposite = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(battler)));
|
||
if (gAbsentBattlerFlags & gBitTable[battlerOpposite])
|
||
battlerOpposite ^= BIT_FLANK;
|
||
|
||
BtlController_EmitLinkStandbyMsg(battlerOpposite, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE);
|
||
MarkBattlerForControllerExec(battlerOpposite);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void Cmd_switchhandleorder(void)
|
||
{
|
||
CMD_ARGS(u8 battler, u8 state);
|
||
|
||
u32 battler, i;
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
|
||
switch (cmd->state)
|
||
{
|
||
case 0:
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (gBattleResources->bufferB[i][0] == CONTROLLER_CHOSENMONRETURNVALUE)
|
||
{
|
||
*(gBattleStruct->monToSwitchIntoId + i) = gBattleResources->bufferB[i][1];
|
||
if (!(gBattleStruct->field_93 & gBitTable[i]))
|
||
{
|
||
RecordedBattle_SetBattlerAction(i, gBattleResources->bufferB[i][1]);
|
||
gBattleStruct->field_93 |= gBitTable[i];
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case 1:
|
||
if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI))
|
||
SwitchPartyOrder(battler);
|
||
break;
|
||
case 2:
|
||
if (!(gBattleStruct->field_93 & gBitTable[battler]))
|
||
{
|
||
RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][1]);
|
||
gBattleStruct->field_93 |= gBitTable[battler];
|
||
}
|
||
// fall through
|
||
case 3:
|
||
gBattleCommunication[0] = gBattleResources->bufferB[battler][1];
|
||
*(gBattleStruct->monToSwitchIntoId + battler) = gBattleResources->bufferB[battler][1];
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleTypeFlags & BATTLE_TYPE_MULTI)
|
||
{
|
||
*(battler * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) &= 0xF;
|
||
*(battler * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) |= (gBattleResources->bufferB[battler][2] & 0xF0);
|
||
*(battler * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 1) = gBattleResources->bufferB[battler][3];
|
||
|
||
*((BATTLE_PARTNER(battler)) * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) &= (0xF0);
|
||
*((BATTLE_PARTNER(battler)) * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) |= (gBattleResources->bufferB[battler][2] & 0xF0) >> 4;
|
||
*((BATTLE_PARTNER(battler)) * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 2) = gBattleResources->bufferB[battler][3];
|
||
}
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
|
||
{
|
||
SwitchPartyOrderInGameMulti(battler, *(gBattleStruct->monToSwitchIntoId + battler));
|
||
}
|
||
else
|
||
{
|
||
SwitchPartyOrder(battler);
|
||
}
|
||
|
||
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].species)
|
||
PREPARE_MON_NICK_BUFFER(gBattleTextBuff2, battler, gBattleResources->bufferB[battler][1])
|
||
break;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void SetDmgHazardsBattlescript(u8 battler, u8 multistringId)
|
||
{
|
||
gBattleMons[battler].status2 &= ~STATUS2_DESTINY_BOND;
|
||
gHitMarker &= ~HITMARKER_DESTINYBOND;
|
||
gBattleScripting.battler = battler;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = multistringId;
|
||
|
||
BattleScriptPushCursor();
|
||
if (gBattlescriptCurrInstr[1] == BS_TARGET)
|
||
gBattlescriptCurrInstr = BattleScript_DmgHazardsOnTarget;
|
||
else if (gBattlescriptCurrInstr[1] == BS_ATTACKER)
|
||
gBattlescriptCurrInstr = BattleScript_DmgHazardsOnAttacker;
|
||
else
|
||
gBattlescriptCurrInstr = BattleScript_DmgHazardsOnFaintedBattler;
|
||
}
|
||
|
||
bool32 DoSwitchInAbilitiesItems(u32 battler)
|
||
{
|
||
return (TryPrimalReversion(battler)
|
||
|| AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0)
|
||
|| (gBattleWeather & B_WEATHER_ANY && WEATHER_HAS_EFFECT && AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0))
|
||
|| (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0))
|
||
|| ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battler, FALSE)
|
||
|| AbilityBattleEffects(ABILITYEFFECT_TRACE2, 0, 0, 0, 0));
|
||
}
|
||
|
||
bool32 ShouldPostponeSwitchInAbilities(u32 battler)
|
||
{
|
||
bool32 aliveOpposing1 = IsBattlerAlive(BATTLE_OPPOSITE(battler));
|
||
bool32 aliveOpposing2 = IsBattlerAlive(BATTLE_PARTNER(BATTLE_OPPOSITE(battler)));
|
||
// No pokemon on opposing side - postpone.
|
||
if (!aliveOpposing1 && !aliveOpposing2)
|
||
return TRUE;
|
||
|
||
// Checks for double battle, so abilities like Intimidate wait until all battlers are switched-in before activating.
|
||
if (IsDoubleBattle())
|
||
{
|
||
if (aliveOpposing1 && !aliveOpposing2 && !HasNoMonsToSwitch(BATTLE_PARTNER(BATTLE_OPPOSITE(battler)), PARTY_SIZE, PARTY_SIZE))
|
||
return TRUE;
|
||
if (!aliveOpposing1 && aliveOpposing2 && !HasNoMonsToSwitch(BATTLE_OPPOSITE(battler), PARTY_SIZE, PARTY_SIZE))
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void Cmd_switchineffects(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
s32 i;
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
|
||
UpdateSentPokesToOpponentValue(battler);
|
||
|
||
gHitMarker &= ~HITMARKER_FAINTED(battler);
|
||
gSpecialStatuses[battler].faintedHasReplacement = FALSE;
|
||
|
||
if (!BattlerHasAi(battler))
|
||
gBattleStruct->appearedInBattle |= gBitTable[gBattlerPartyIndexes[battler]];
|
||
|
||
// Neutralizing Gas announces itself before hazards
|
||
if (gBattleMons[battler].ability == ABILITY_NEUTRALIZING_GAS && gSpecialStatuses[battler].announceNeutralizingGas == 0)
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_NEUTRALIZING_GAS;
|
||
gSpecialStatuses[battler].announceNeutralizingGas = TRUE;
|
||
gBattlerAbility = battler;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_SwitchInAbilityMsgRet;
|
||
}
|
||
// Healing Wish activates before hazards.
|
||
// Starting from Gen8 - it heals only pokemon which can be healed. In gens 5,6,7 the effect activates anyways.
|
||
else if (((gBattleStruct->storedHealingWish & gBitTable[battler]) || (gBattleStruct->storedLunarDance & gBitTable[battler]))
|
||
&& (gBattleMons[battler].hp != gBattleMons[battler].maxHP || gBattleMons[battler].status1 != 0 || B_HEALING_WISH_SWITCH < GEN_8))
|
||
{
|
||
if (gBattleStruct->storedHealingWish & gBitTable[battler])
|
||
{
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_HealingWishActivates;
|
||
gBattleStruct->storedHealingWish &= ~(gBitTable[battler]);
|
||
}
|
||
else // Lunar Dance
|
||
{
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_LunarDanceActivates;
|
||
gBattleStruct->storedLunarDance &= ~(gBitTable[battler]);
|
||
}
|
||
}
|
||
else if (!(gDisableStructs[battler].spikesDone)
|
||
&& (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SPIKES)
|
||
&& GetBattlerAbility(battler) != ABILITY_MAGIC_GUARD
|
||
&& IsBattlerAffectedByHazards(battler, FALSE)
|
||
&& IsBattlerGrounded(battler))
|
||
{
|
||
u8 spikesDmg = (5 - gSideTimers[GetBattlerSide(battler)].spikesAmount) * 2;
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / (spikesDmg);
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
|
||
gDisableStructs[battler].spikesDone = TRUE;
|
||
SetDmgHazardsBattlescript(battler, B_MSG_PKMNHURTBYSPIKES);
|
||
}
|
||
else if (!(gDisableStructs[battler].stealthRockDone)
|
||
&& (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_STEALTH_ROCK)
|
||
&& IsBattlerAffectedByHazards(battler, FALSE)
|
||
&& GetBattlerAbility(battler) != ABILITY_MAGIC_GUARD)
|
||
{
|
||
gDisableStructs[battler].stealthRockDone = TRUE;
|
||
gBattleMoveDamage = GetStealthHazardDamage(gBattleMoves[MOVE_STEALTH_ROCK].type, battler);
|
||
|
||
if (gBattleMoveDamage != 0)
|
||
SetDmgHazardsBattlescript(battler, B_MSG_STEALTHROCKDMG);
|
||
}
|
||
else if (!(gDisableStructs[battler].toxicSpikesDone)
|
||
&& (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TOXIC_SPIKES)
|
||
&& IsBattlerGrounded(battler))
|
||
{
|
||
gDisableStructs[battler].toxicSpikesDone = TRUE;
|
||
if (IS_BATTLER_OF_TYPE(battler, TYPE_POISON)) // Absorb the toxic spikes.
|
||
{
|
||
gSideStatuses[GetBattlerSide(battler)] &= ~SIDE_STATUS_TOXIC_SPIKES;
|
||
gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount = 0;
|
||
gBattleScripting.battler = battler;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_ToxicSpikesAbsorbed;
|
||
}
|
||
else if (IsBattlerAffectedByHazards(battler, TRUE))
|
||
{
|
||
if (!(gBattleMons[battler].status1 & STATUS1_ANY)
|
||
&& !IS_BATTLER_OF_TYPE(battler, TYPE_STEEL)
|
||
&& GetBattlerAbility(battler) != ABILITY_IMMUNITY
|
||
&& !IsAbilityOnSide(battler, ABILITY_PASTEL_VEIL)
|
||
&& !(gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD)
|
||
&& !(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN))
|
||
{
|
||
if (gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount >= 2)
|
||
gBattleMons[battler].status1 |= STATUS1_TOXIC_POISON;
|
||
else
|
||
gBattleMons[battler].status1 |= STATUS1_POISON;
|
||
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[battler].status1), &gBattleMons[battler].status1);
|
||
MarkBattlerForControllerExec(battler);
|
||
gBattleScripting.battler = battler;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_ToxicSpikesPoisoned;
|
||
}
|
||
}
|
||
}
|
||
else if (!(gDisableStructs[battler].stickyWebDone)
|
||
&& (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_STICKY_WEB)
|
||
&& IsBattlerAffectedByHazards(battler, FALSE)
|
||
&& IsBattlerGrounded(battler))
|
||
{
|
||
gDisableStructs[battler].stickyWebDone = TRUE;
|
||
gBattleScripting.battler = battler;
|
||
SET_STATCHANGER(STAT_SPEED, 1, TRUE);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_StickyWebOnSwitchIn;
|
||
}
|
||
else if (!(gDisableStructs[battler].steelSurgeDone)
|
||
&& (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_STEELSURGE)
|
||
&& IsBattlerAffectedByHazards(battler, FALSE)
|
||
&& GetBattlerAbility(battler) != ABILITY_MAGIC_GUARD)
|
||
{
|
||
gDisableStructs[battler].steelSurgeDone = TRUE;
|
||
gBattleMoveDamage = GetStealthHazardDamage(gBattleMoves[MOVE_G_MAX_STEELSURGE].type, battler);
|
||
|
||
if (gBattleMoveDamage != 0)
|
||
SetDmgHazardsBattlescript(battler, B_MSG_SHARPSTEELDMG);
|
||
}
|
||
else if (gBattleMons[battler].hp != gBattleMons[battler].maxHP && gBattleStruct->zmove.healReplacement)
|
||
{
|
||
gBattleStruct->zmove.healReplacement = FALSE;
|
||
gBattleMoveDamage = -1 * (gBattleMons[battler].maxHP);
|
||
gBattleScripting.battler = battler;
|
||
BattleScriptPushCursor();
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_Z_HP_TRAP;
|
||
gBattlescriptCurrInstr = BattleScript_HealReplacementZMove;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
// There is a hack here to ensure the truant counter will be 0 when the battler's next turn starts.
|
||
// The truant counter is not updated in the case where a mon switches in after a lost judgment in the battle arena.
|
||
if (GetBattlerAbility(battler) == ABILITY_TRUANT
|
||
&& gCurrentActionFuncId != B_ACTION_USE_MOVE
|
||
&& !gDisableStructs[battler].truantSwitchInHack)
|
||
gDisableStructs[battler].truantCounter = 1;
|
||
|
||
gDisableStructs[battler].truantSwitchInHack = 0;
|
||
|
||
// Don't activate switch-in abilities if the opposing field is empty.
|
||
// This could happen when a mon uses explosion and causes everyone to faint.
|
||
if (ShouldPostponeSwitchInAbilities(battler) || gBattleStruct->switchInAbilityPostponed)
|
||
{
|
||
gBattleStruct->switchInAbilityPostponed |= gBitTable[battler];
|
||
}
|
||
else
|
||
{
|
||
if (DoSwitchInAbilitiesItems(battler))
|
||
return;
|
||
else if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0))
|
||
return;
|
||
}
|
||
|
||
gDisableStructs[battler].stickyWebDone = FALSE;
|
||
gDisableStructs[battler].spikesDone = FALSE;
|
||
gDisableStructs[battler].toxicSpikesDone = FALSE;
|
||
gDisableStructs[battler].stealthRockDone = FALSE;
|
||
gDisableStructs[battler].steelSurgeDone = FALSE;
|
||
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (gBattlerByTurnOrder[i] == battler)
|
||
gActionsByTurnOrder[i] = B_ACTION_CANCEL_PARTNER;
|
||
|
||
gBattleStruct->hpOnSwitchout[GetBattlerSide(i)] = gBattleMons[i].hp;
|
||
}
|
||
|
||
if (cmd->battler == BS_FAINTED_LINK_MULTIPLE_1)
|
||
{
|
||
u32 hitmarkerFaintBits = gHitMarker >> 28;
|
||
|
||
gBattlerFainted++;
|
||
while (1)
|
||
{
|
||
if (hitmarkerFaintBits & gBitTable[gBattlerFainted] && !(gAbsentBattlerFlags & gBitTable[gBattlerFainted]))
|
||
break;
|
||
if (gBattlerFainted >= gBattlersCount)
|
||
break;
|
||
gBattlerFainted++;
|
||
}
|
||
}
|
||
gBattleStruct->forcedSwitch &= ~(gBitTable[battler]);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_trainerslidein(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 battler = GetBattlerAtPosition(cmd->battler);
|
||
BtlController_EmitTrainerSlide(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_playse(void)
|
||
{
|
||
CMD_ARGS(u16 song);
|
||
|
||
BtlController_EmitPlaySE(gBattlerAttacker, BUFFER_A, cmd->song);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_fanfare(void)
|
||
{
|
||
CMD_ARGS(u16 song);
|
||
|
||
BtlController_EmitPlayFanfareOrBGM(gBattlerAttacker, BUFFER_A, cmd->song, FALSE);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_playfaintcry(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
BtlController_EmitFaintingCry(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_endlinkbattle(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u32 battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
|
||
BtlController_EmitEndLinkBattle(battler, BUFFER_A, gBattleOutcome);
|
||
MarkBattlerForControllerExec(battler);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_returntoball(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
BtlController_EmitReturnMonToBall(battler, BUFFER_A, TRUE);
|
||
MarkBattlerForControllerExec(battler);
|
||
TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_handlelearnnewmove(void)
|
||
{
|
||
CMD_ARGS(const u8 *learnedMovePtr, const u8 *nothingToLearnPtr, bool8 isFirstMove);
|
||
|
||
u32 monId = gBattleStruct->expGetterMonId;
|
||
u16 learnMove = MonTryLearningNewMove(&gPlayerParty[monId], cmd->isFirstMove);
|
||
while (learnMove == MON_ALREADY_KNOWS_MOVE)
|
||
learnMove = MonTryLearningNewMove(&gPlayerParty[monId], FALSE);
|
||
|
||
if (learnMove == MOVE_NONE || RECORDED_WILD_BATTLE)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nothingToLearnPtr;
|
||
}
|
||
else if (learnMove == MON_HAS_MAX_MOVES)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
u32 battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
|
||
|
||
if (gBattlerPartyIndexes[battler] == monId
|
||
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
|
||
{
|
||
GiveMoveToBattleMon(&gBattleMons[battler], learnMove);
|
||
}
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||
{
|
||
battler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
|
||
if (gBattlerPartyIndexes[battler] == monId
|
||
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
|
||
{
|
||
GiveMoveToBattleMon(&gBattleMons[battler], learnMove);
|
||
}
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->learnedMovePtr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_yesnoboxlearnmove(void)
|
||
{
|
||
CMD_ARGS(const u8 *forgotMovePtr);
|
||
|
||
switch (gBattleScripting.learnMoveState)
|
||
{
|
||
case 0:
|
||
HandleBattleWindow(YESNOBOX_X_Y, 0);
|
||
BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO);
|
||
gBattleScripting.learnMoveState++;
|
||
gBattleCommunication[CURSOR_POSITION] = 0;
|
||
BattleCreateYesNoCursorAt(0);
|
||
break;
|
||
case 1:
|
||
if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0)
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||
gBattleCommunication[CURSOR_POSITION] = 0;
|
||
BattleCreateYesNoCursorAt(0);
|
||
}
|
||
if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0)
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||
gBattleCommunication[CURSOR_POSITION] = 1;
|
||
BattleCreateYesNoCursorAt(1);
|
||
}
|
||
if (JOY_NEW(A_BUTTON))
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
if (gBattleCommunication[1] == 0)
|
||
{
|
||
HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR);
|
||
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
|
||
gBattleScripting.learnMoveState++;
|
||
}
|
||
else
|
||
{
|
||
gBattleScripting.learnMoveState = 5;
|
||
}
|
||
}
|
||
else if (JOY_NEW(B_BUTTON))
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
gBattleScripting.learnMoveState = 5;
|
||
}
|
||
break;
|
||
case 2:
|
||
if (!gPaletteFade.active)
|
||
{
|
||
FreeAllWindowBuffers();
|
||
ShowSelectMovePokemonSummaryScreen(gPlayerParty, gBattleStruct->expGetterMonId, gPlayerPartyCount - 1, ReshowBattleScreenAfterMenu, gMoveToLearn);
|
||
gBattleScripting.learnMoveState++;
|
||
}
|
||
break;
|
||
case 3:
|
||
if (!gPaletteFade.active && gMain.callback2 == BattleMainCB2)
|
||
{
|
||
gBattleScripting.learnMoveState++;
|
||
}
|
||
break;
|
||
case 4:
|
||
if (!gPaletteFade.active && gMain.callback2 == BattleMainCB2)
|
||
{
|
||
u8 movePosition = GetMoveSlotToReplace();
|
||
if (movePosition == MAX_MON_MOVES)
|
||
{
|
||
gBattleScripting.learnMoveState = 5;
|
||
}
|
||
else
|
||
{
|
||
u16 moveId = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_MOVE1 + movePosition);
|
||
if (IsMoveHM(moveId))
|
||
{
|
||
PrepareStringBattle(STRINGID_HMMOVESCANTBEFORGOTTEN, B_POSITION_PLAYER_LEFT);
|
||
gBattleScripting.learnMoveState = 6;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->forgotMovePtr;
|
||
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff2, moveId)
|
||
|
||
RemoveMonPPBonus(&gPlayerParty[gBattleStruct->expGetterMonId], movePosition);
|
||
SetMonMoveSlot(&gPlayerParty[gBattleStruct->expGetterMonId], gMoveToLearn, movePosition);
|
||
|
||
if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId && MOVE_IS_PERMANENT(0, movePosition))
|
||
{
|
||
RemoveBattleMonPPBonus(&gBattleMons[0], movePosition);
|
||
SetBattleMonMoveSlot(&gBattleMons[0], gMoveToLearn, movePosition);
|
||
}
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||
&& gBattlerPartyIndexes[2] == gBattleStruct->expGetterMonId
|
||
&& MOVE_IS_PERMANENT(2, movePosition))
|
||
{
|
||
RemoveBattleMonPPBonus(&gBattleMons[2], movePosition);
|
||
SetBattleMonMoveSlot(&gBattleMons[2], gMoveToLearn, movePosition);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
case 5:
|
||
HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
break;
|
||
case 6:
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
gBattleScripting.learnMoveState = 2;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void Cmd_yesnoboxstoplearningmove(void)
|
||
{
|
||
CMD_ARGS(const u8 *noInstr);
|
||
|
||
switch (gBattleScripting.learnMoveState)
|
||
{
|
||
case 0:
|
||
HandleBattleWindow(YESNOBOX_X_Y, 0);
|
||
BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO);
|
||
gBattleScripting.learnMoveState++;
|
||
gBattleCommunication[CURSOR_POSITION] = 0;
|
||
BattleCreateYesNoCursorAt(0);
|
||
break;
|
||
case 1:
|
||
if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0)
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||
gBattleCommunication[CURSOR_POSITION] = 0;
|
||
BattleCreateYesNoCursorAt(0);
|
||
}
|
||
if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0)
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||
gBattleCommunication[CURSOR_POSITION] = 1;
|
||
BattleCreateYesNoCursorAt(1);
|
||
}
|
||
if (JOY_NEW(A_BUTTON))
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
|
||
if (gBattleCommunication[1] != 0)
|
||
gBattlescriptCurrInstr = cmd->noInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR);
|
||
}
|
||
else if (JOY_NEW(B_BUTTON))
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
gBattlescriptCurrInstr = cmd->noInstr;
|
||
HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void Cmd_hitanimation(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else if (!(gHitMarker & HITMARKER_IGNORE_SUBSTITUTE) || !(DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove)) || gDisableStructs[battler].substituteHP == 0)
|
||
{
|
||
BtlController_EmitHitAnimation(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static u32 GetTrainerMoneyToGive(u16 trainerId)
|
||
{
|
||
u32 lastMonLevel = 0;
|
||
u32 moneyReward;
|
||
u8 trainerMoney;
|
||
|
||
if (trainerId == TRAINER_SECRET_BASE)
|
||
{
|
||
moneyReward = 20 * gBattleResources->secretBase->party.levels[0] * gBattleStruct->moneyMultiplier;
|
||
}
|
||
else
|
||
{
|
||
const struct TrainerMon *party = gTrainers[trainerId].party;
|
||
lastMonLevel = party[gTrainers[trainerId].partySize - 1].lvl;
|
||
trainerMoney = gTrainerClasses[gTrainers[trainerId].trainerClass].money;
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
|
||
moneyReward = 4 * lastMonLevel * gBattleStruct->moneyMultiplier * trainerMoney;
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||
moneyReward = 4 * lastMonLevel * gBattleStruct->moneyMultiplier * 2 * trainerMoney;
|
||
else
|
||
moneyReward = 4 * lastMonLevel * gBattleStruct->moneyMultiplier * trainerMoney;
|
||
}
|
||
|
||
return moneyReward;
|
||
}
|
||
|
||
static void Cmd_getmoneyreward(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u32 money;
|
||
u8 sPartyLevel = 1;
|
||
|
||
if (gBattleOutcome == B_OUTCOME_WON)
|
||
{
|
||
money = GetTrainerMoneyToGive(gTrainerBattleOpponent_A);
|
||
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
|
||
money += GetTrainerMoneyToGive(gTrainerBattleOpponent_B);
|
||
AddMoney(&gSaveBlock1Ptr->money, money);
|
||
}
|
||
else
|
||
{
|
||
s32 i, count;
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
|
||
&& GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG)
|
||
{
|
||
if (GetMonData(&gPlayerParty[i], MON_DATA_LEVEL) > sPartyLevel)
|
||
sPartyLevel = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);
|
||
}
|
||
}
|
||
for (count = 0, i = 0; i < ARRAY_COUNT(sBadgeFlags); i++)
|
||
{
|
||
if (FlagGet(sBadgeFlags[i]) == TRUE)
|
||
++count;
|
||
}
|
||
money = sWhiteOutBadgeMoney[count] * sPartyLevel;
|
||
RemoveMoney(&gSaveBlock1Ptr->money, money);
|
||
}
|
||
|
||
PREPARE_WORD_NUMBER_BUFFER(gBattleTextBuff1, 5, money);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
// Command is never used
|
||
static void Cmd_updatebattlermoves(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
|
||
switch (gBattleCommunication[0])
|
||
{
|
||
case 0:
|
||
BtlController_EmitGetMonData(battler, BUFFER_A, REQUEST_ALL_BATTLE, 0);
|
||
MarkBattlerForControllerExec(battler);
|
||
gBattleCommunication[0]++;
|
||
break;
|
||
case 1:
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
s32 i;
|
||
struct BattlePokemon *bufferPoke = (struct BattlePokemon *) &gBattleResources->bufferB[battler][4];
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
gBattleMons[battler].moves[i] = bufferPoke->moves[i];
|
||
gBattleMons[battler].pp[i] = bufferPoke->pp[i];
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void Cmd_swapattackerwithtarget(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u8 temp;
|
||
SWAP(gBattlerAttacker, gBattlerTarget, temp);
|
||
|
||
if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET)
|
||
gHitMarker &= ~HITMARKER_SWAP_ATTACKER_TARGET;
|
||
else
|
||
gHitMarker |= HITMARKER_SWAP_ATTACKER_TARGET;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_incrementgamestat(void)
|
||
{
|
||
CMD_ARGS(u8 stat);
|
||
|
||
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
|
||
IncrementGameStat(cmd->stat);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_drawpartystatussummary(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 battler, i;
|
||
struct Pokemon *party;
|
||
struct HpAndStatus hpStatuses[PARTY_SIZE];
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
party = GetBattlerParty(battler);
|
||
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
if (GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE
|
||
|| GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG)
|
||
{
|
||
hpStatuses[i].hp = 0xFFFF;
|
||
hpStatuses[i].status = 0;
|
||
}
|
||
else
|
||
{
|
||
hpStatuses[i].hp = GetMonData(&party[i], MON_DATA_HP);
|
||
hpStatuses[i].status = GetMonData(&party[i], MON_DATA_STATUS);
|
||
}
|
||
}
|
||
|
||
BtlController_EmitDrawPartyStatusSummary(battler, BUFFER_A, hpStatuses, 1);
|
||
MarkBattlerForControllerExec(battler);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_hidepartystatussummary(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
BtlController_EmitHidePartyStatusSummary(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_jumptocalledmove(void)
|
||
{
|
||
CMD_ARGS(bool8 notChosenMove);
|
||
|
||
if (cmd->notChosenMove)
|
||
gCurrentMove = gCalledMove;
|
||
else
|
||
gChosenMove = gCurrentMove = gCalledMove;
|
||
|
||
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
|
||
}
|
||
|
||
static void Cmd_statusanimation(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (!(gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)
|
||
&& gDisableStructs[battler].substituteHP == 0
|
||
&& !(gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION)))
|
||
{
|
||
BtlController_EmitStatusAnimation(battler, BUFFER_A, FALSE, gBattleMons[battler].status1);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_status2animation(void)
|
||
{
|
||
CMD_ARGS(u8 battler, u32 status2);
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
u32 status2ToAnim = cmd->status2;
|
||
if (!(gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)
|
||
&& gDisableStructs[battler].substituteHP == 0
|
||
&& !(gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION)))
|
||
{
|
||
BtlController_EmitStatusAnimation(battler, BUFFER_A, TRUE, gBattleMons[battler].status2 & status2ToAnim);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_chosenstatusanimation(void)
|
||
{
|
||
CMD_ARGS(u8 battler, bool8 isStatus2, u32 status);
|
||
|
||
if (gBattleControllerExecFlags == 0)
|
||
{
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
u32 wantedStatus = cmd->status;
|
||
if (!(gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)
|
||
&& gDisableStructs[battler].substituteHP == 0
|
||
&& !(gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION)))
|
||
{
|
||
BtlController_EmitStatusAnimation(battler, BUFFER_A, cmd->isStatus2, wantedStatus);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_yesnobox(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
switch (gBattleCommunication[0])
|
||
{
|
||
case 0:
|
||
HandleBattleWindow(YESNOBOX_X_Y, 0);
|
||
BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO);
|
||
gBattleCommunication[0]++;
|
||
gBattleCommunication[CURSOR_POSITION] = 0;
|
||
BattleCreateYesNoCursorAt(0);
|
||
break;
|
||
case 1:
|
||
if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0)
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||
gBattleCommunication[CURSOR_POSITION] = 0;
|
||
BattleCreateYesNoCursorAt(0);
|
||
}
|
||
if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0)
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||
gBattleCommunication[CURSOR_POSITION] = 1;
|
||
BattleCreateYesNoCursorAt(1);
|
||
}
|
||
if (JOY_NEW(B_BUTTON))
|
||
{
|
||
gBattleCommunication[CURSOR_POSITION] = 1;
|
||
PlaySE(SE_SELECT);
|
||
HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else if (JOY_NEW(A_BUTTON))
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void Cmd_cancelallactions(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
s32 i;
|
||
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
gActionsByTurnOrder[i] = B_ACTION_CANCEL_PARTNER;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setgravity(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gFieldStatuses & STATUS_FIELD_GRAVITY)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gFieldStatuses |= STATUS_FIELD_GRAVITY;
|
||
gFieldTimers.gravityTimer = 5;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static bool32 TryCheekPouch(u32 battler, u32 itemId)
|
||
{
|
||
if (ItemId_GetPocket(itemId) == POCKET_BERRIES
|
||
&& GetBattlerAbility(battler) == ABILITY_CHEEK_POUCH
|
||
&& !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)
|
||
&& gBattleStruct->ateBerry[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]]
|
||
&& !BATTLER_MAX_HP(battler))
|
||
{
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 3;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
gBattleMoveDamage *= -1;
|
||
gBattlerAbility = battler;
|
||
BattleScriptPush(gBattlescriptCurrInstr + 2);
|
||
gBattlescriptCurrInstr = BattleScript_CheekPouchActivates;
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
// Used by Bestow and Symbiosis to take an item from one battler and give to another.
|
||
static void BestowItem(u32 battlerAtk, u32 battlerDef)
|
||
{
|
||
gLastUsedItem = gBattleMons[battlerAtk].item;
|
||
|
||
gBattleMons[battlerAtk].item = ITEM_NONE;
|
||
BtlController_EmitSetMonData(battlerAtk, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[battlerAtk].item), &gBattleMons[battlerAtk].item);
|
||
MarkBattlerForControllerExec(battlerAtk);
|
||
CheckSetUnburden(battlerAtk);
|
||
|
||
gBattleMons[battlerDef].item = gLastUsedItem;
|
||
BtlController_EmitSetMonData(battlerDef, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[battlerDef].item), &gBattleMons[battlerDef].item);
|
||
MarkBattlerForControllerExec(battlerDef);
|
||
gBattleResources->flags->flags[battlerDef] &= ~RESOURCE_FLAG_UNBURDEN;
|
||
}
|
||
|
||
// Called by Cmd_removeitem. itemId represents the item that was removed, not being given.
|
||
static bool32 TrySymbiosis(u32 battler, u32 itemId)
|
||
{
|
||
if (!gBattleStruct->itemLost[gBattlerPartyIndexes[battler]].stolen
|
||
&& gBattleStruct->changedItems[battler] == ITEM_NONE
|
||
&& GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_EJECT_BUTTON
|
||
&& GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_EJECT_PACK
|
||
&& (B_SYMBIOSIS_GEMS < GEN_7 || !(gSpecialStatuses[battler].gemBoost))
|
||
&& gCurrentMove != MOVE_FLING //Fling and damage-reducing berries are handled separately.
|
||
&& !gSpecialStatuses[battler].berryReduced
|
||
&& SYMBIOSIS_CHECK(battler, BATTLE_PARTNER(battler)))
|
||
{
|
||
BestowItem(BATTLE_PARTNER(battler), battler);
|
||
gLastUsedAbility = gBattleMons[BATTLE_PARTNER(battler)].ability;
|
||
gBattleScripting.battler = gBattlerAbility = BATTLE_PARTNER(battler);
|
||
gBattlerAttacker = battler;
|
||
BattleScriptPush(gBattlescriptCurrInstr + 2);
|
||
gBattlescriptCurrInstr = BattleScript_SymbiosisActivates;
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
static void Cmd_removeitem(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 battler;
|
||
u16 itemId = 0;
|
||
|
||
if (gBattleScripting.overrideBerryRequirements)
|
||
{
|
||
// bug bite / pluck - don't remove current item
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
itemId = gBattleMons[battler].item;
|
||
|
||
// Popped Air Balloon cannot be restored by any means.
|
||
// Corroded items cannot be restored either.
|
||
if (GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_AIR_BALLOON
|
||
&& gBattleMoves[gCurrentMove].effect != EFFECT_CORROSIVE_GAS)
|
||
gBattleStruct->usedHeldItems[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)] = itemId; // Remember if switched out
|
||
|
||
gBattleMons[battler].item = ITEM_NONE;
|
||
CheckSetUnburden(battler);
|
||
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[battler].item), &gBattleMons[battler].item);
|
||
MarkBattlerForControllerExec(battler);
|
||
|
||
ClearBattlerItemEffectHistory(battler);
|
||
if (!TryCheekPouch(battler, itemId) && !TrySymbiosis(battler, itemId))
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_atknameinbuff1(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattlerPartyIndexes[gBattlerAttacker]);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_drawlvlupbox(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gBattleScripting.drawlvlupboxState == 0)
|
||
{
|
||
// If the Pokémon getting exp is not in-battle then
|
||
// slide out a banner with their name and icon on it.
|
||
// Otherwise skip ahead.
|
||
if (IsMonGettingExpSentOut())
|
||
gBattleScripting.drawlvlupboxState = 3;
|
||
else
|
||
gBattleScripting.drawlvlupboxState = 1;
|
||
}
|
||
|
||
switch (gBattleScripting.drawlvlupboxState)
|
||
{
|
||
case 1:
|
||
// Start level up banner
|
||
gBattle_BG2_Y = 96;
|
||
SetBgAttribute(2, BG_ATTR_PRIORITY, 0);
|
||
ShowBg(2);
|
||
InitLevelUpBanner();
|
||
gBattleScripting.drawlvlupboxState = 2;
|
||
break;
|
||
case 2:
|
||
if (!SlideInLevelUpBanner())
|
||
gBattleScripting.drawlvlupboxState = 3;
|
||
break;
|
||
case 3:
|
||
// Init level up box
|
||
gBattle_BG1_X = 0;
|
||
gBattle_BG1_Y = 256;
|
||
SetBgAttribute(0, BG_ATTR_PRIORITY, 1);
|
||
SetBgAttribute(1, BG_ATTR_PRIORITY, 0);
|
||
ShowBg(0);
|
||
ShowBg(1);
|
||
HandleBattleWindow(18, 7, 29, 19, WINDOW_BG1);
|
||
gBattleScripting.drawlvlupboxState = 4;
|
||
break;
|
||
case 4:
|
||
// Draw page 1 of level up box
|
||
DrawLevelUpWindow1();
|
||
PutWindowTilemap(B_WIN_LEVEL_UP_BOX);
|
||
CopyWindowToVram(B_WIN_LEVEL_UP_BOX, COPYWIN_FULL);
|
||
gBattleScripting.drawlvlupboxState++;
|
||
break;
|
||
case 5:
|
||
case 7:
|
||
// Wait for draw after each page
|
||
if (!IsDma3ManagerBusyWithBgCopy())
|
||
{
|
||
gBattle_BG1_Y = 0;
|
||
gBattleScripting.drawlvlupboxState++;
|
||
}
|
||
break;
|
||
case 6:
|
||
if (gMain.newKeys != 0 || RECORDED_WILD_BATTLE)
|
||
{
|
||
// Draw page 2 of level up box
|
||
PlaySE(SE_SELECT);
|
||
DrawLevelUpWindow2();
|
||
CopyWindowToVram(B_WIN_LEVEL_UP_BOX, COPYWIN_GFX);
|
||
gBattleScripting.drawlvlupboxState++;
|
||
}
|
||
break;
|
||
case 8:
|
||
if (gMain.newKeys != 0 || RECORDED_WILD_BATTLE)
|
||
{
|
||
// Close level up box
|
||
PlaySE(SE_SELECT);
|
||
HandleBattleWindow(18, 7, 29, 19, WINDOW_BG1 | WINDOW_CLEAR);
|
||
gBattleScripting.drawlvlupboxState++;
|
||
}
|
||
break;
|
||
case 9:
|
||
if (!SlideOutLevelUpBanner())
|
||
{
|
||
ClearWindowTilemap(B_WIN_LEVEL_UP_BANNER);
|
||
CopyWindowToVram(B_WIN_LEVEL_UP_BANNER, COPYWIN_MAP);
|
||
|
||
ClearWindowTilemap(B_WIN_LEVEL_UP_BOX);
|
||
CopyWindowToVram(B_WIN_LEVEL_UP_BOX, COPYWIN_MAP);
|
||
|
||
SetBgAttribute(2, BG_ATTR_PRIORITY, 2);
|
||
ShowBg(2);
|
||
|
||
gBattleScripting.drawlvlupboxState = 10;
|
||
}
|
||
break;
|
||
case 10:
|
||
if (!IsDma3ManagerBusyWithBgCopy())
|
||
{
|
||
SetBgAttribute(0, BG_ATTR_PRIORITY, 0);
|
||
SetBgAttribute(1, BG_ATTR_PRIORITY, 1);
|
||
ShowBg(0);
|
||
ShowBg(1);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void DrawLevelUpWindow1(void)
|
||
{
|
||
u16 currStats[NUM_STATS];
|
||
|
||
GetMonLevelUpWindowStats(&gPlayerParty[gBattleStruct->expGetterMonId], currStats);
|
||
DrawLevelUpWindowPg1(B_WIN_LEVEL_UP_BOX, gBattleResources->beforeLvlUp->stats, currStats, TEXT_DYNAMIC_COLOR_5, TEXT_DYNAMIC_COLOR_4, TEXT_DYNAMIC_COLOR_6);
|
||
}
|
||
|
||
static void DrawLevelUpWindow2(void)
|
||
{
|
||
u16 currStats[NUM_STATS];
|
||
|
||
GetMonLevelUpWindowStats(&gPlayerParty[gBattleStruct->expGetterMonId], currStats);
|
||
DrawLevelUpWindowPg2(B_WIN_LEVEL_UP_BOX, currStats, TEXT_DYNAMIC_COLOR_5, TEXT_DYNAMIC_COLOR_4, TEXT_DYNAMIC_COLOR_6);
|
||
}
|
||
|
||
static void InitLevelUpBanner(void)
|
||
{
|
||
gBattle_BG2_Y = 0;
|
||
gBattle_BG2_X = LEVEL_UP_BANNER_START;
|
||
|
||
LoadPalette(sLevelUpBanner_Pal, BG_PLTT_ID(6), sizeof(sLevelUpBanner_Pal));
|
||
CopyToWindowPixelBuffer(B_WIN_LEVEL_UP_BANNER, sLevelUpBanner_Gfx, 0, 0);
|
||
PutWindowTilemap(B_WIN_LEVEL_UP_BANNER);
|
||
CopyWindowToVram(B_WIN_LEVEL_UP_BANNER, COPYWIN_FULL);
|
||
|
||
PutMonIconOnLvlUpBanner();
|
||
}
|
||
|
||
static bool8 SlideInLevelUpBanner(void)
|
||
{
|
||
if (IsDma3ManagerBusyWithBgCopy())
|
||
return TRUE;
|
||
|
||
if (gBattle_BG2_X == LEVEL_UP_BANNER_END)
|
||
return FALSE;
|
||
|
||
if (gBattle_BG2_X == LEVEL_UP_BANNER_START)
|
||
DrawLevelUpBannerText();
|
||
|
||
gBattle_BG2_X += 8;
|
||
if (gBattle_BG2_X >= LEVEL_UP_BANNER_END)
|
||
gBattle_BG2_X = LEVEL_UP_BANNER_END;
|
||
|
||
return (gBattle_BG2_X != LEVEL_UP_BANNER_END);
|
||
}
|
||
|
||
static void DrawLevelUpBannerText(void)
|
||
{
|
||
struct TextPrinterTemplate printerTemplate;
|
||
u8 *txtPtr;
|
||
u32 var;
|
||
|
||
struct Pokemon *mon = &gPlayerParty[gBattleStruct->expGetterMonId];
|
||
u32 monLevel = GetMonData(mon, MON_DATA_LEVEL);
|
||
u8 monGender = GetMonGender(mon);
|
||
GetMonNickname(mon, gStringVar4);
|
||
|
||
printerTemplate.currentChar = gStringVar4;
|
||
printerTemplate.windowId = B_WIN_LEVEL_UP_BANNER;
|
||
printerTemplate.fontId = FONT_SMALL;
|
||
printerTemplate.x = 32;
|
||
printerTemplate.y = 0;
|
||
printerTemplate.currentX = 32;
|
||
printerTemplate.currentY = 0;
|
||
printerTemplate.letterSpacing = 0;
|
||
printerTemplate.lineSpacing = 0;
|
||
printerTemplate.unk = 0;
|
||
printerTemplate.fgColor = TEXT_COLOR_WHITE;
|
||
printerTemplate.bgColor = TEXT_COLOR_TRANSPARENT;
|
||
printerTemplate.shadowColor = TEXT_COLOR_DARK_GRAY;
|
||
|
||
AddTextPrinter(&printerTemplate, TEXT_SKIP_DRAW, NULL);
|
||
|
||
txtPtr = gStringVar4;
|
||
*(txtPtr)++ = CHAR_EXTRA_SYMBOL;
|
||
*(txtPtr)++ = CHAR_LV_2;
|
||
|
||
var = (u32)(txtPtr);
|
||
txtPtr = ConvertIntToDecimalStringN(txtPtr, monLevel, STR_CONV_MODE_LEFT_ALIGN, 3);
|
||
var = (u32)(txtPtr) - var;
|
||
txtPtr = StringFill(txtPtr, CHAR_SPACER, 4 - var);
|
||
|
||
if (monGender != MON_GENDERLESS)
|
||
{
|
||
if (monGender == MON_MALE)
|
||
{
|
||
txtPtr = WriteColorChangeControlCode(txtPtr, 0, TEXT_DYNAMIC_COLOR_3);
|
||
txtPtr = WriteColorChangeControlCode(txtPtr, 1, TEXT_DYNAMIC_COLOR_4);
|
||
*(txtPtr++) = CHAR_MALE;
|
||
}
|
||
else
|
||
{
|
||
txtPtr = WriteColorChangeControlCode(txtPtr, 0, TEXT_DYNAMIC_COLOR_5);
|
||
txtPtr = WriteColorChangeControlCode(txtPtr, 1, TEXT_DYNAMIC_COLOR_6);
|
||
*(txtPtr++) = CHAR_FEMALE;
|
||
}
|
||
*(txtPtr++) = EOS;
|
||
}
|
||
|
||
printerTemplate.y = 10;
|
||
printerTemplate.currentY = 10;
|
||
AddTextPrinter(&printerTemplate, TEXT_SKIP_DRAW, NULL);
|
||
|
||
CopyWindowToVram(B_WIN_LEVEL_UP_BANNER, COPYWIN_GFX);
|
||
}
|
||
|
||
static bool8 SlideOutLevelUpBanner(void)
|
||
{
|
||
if (gBattle_BG2_X == LEVEL_UP_BANNER_START)
|
||
return FALSE;
|
||
|
||
if (gBattle_BG2_X - 16 < LEVEL_UP_BANNER_START)
|
||
gBattle_BG2_X = LEVEL_UP_BANNER_START;
|
||
else
|
||
gBattle_BG2_X -= 16;
|
||
|
||
return (gBattle_BG2_X != LEVEL_UP_BANNER_START);
|
||
}
|
||
|
||
#define sDestroy data[0]
|
||
#define sXOffset data[1]
|
||
|
||
static void PutMonIconOnLvlUpBanner(void)
|
||
{
|
||
u8 spriteId;
|
||
struct SpriteSheet iconSheet;
|
||
struct SpritePalette iconPalSheet;
|
||
|
||
struct Pokemon *mon = &gPlayerParty[gBattleStruct->expGetterMonId];
|
||
u32 species = GetMonData(mon, MON_DATA_SPECIES);
|
||
u32 personality = GetMonData(mon, MON_DATA_PERSONALITY);
|
||
|
||
iconSheet.data = GetMonIconPtr(species, personality);
|
||
iconSheet.size = 0x200;
|
||
iconSheet.tag = TAG_LVLUP_BANNER_MON_ICON;
|
||
|
||
iconPalSheet.data = GetValidMonIconPalettePtr(species);
|
||
iconPalSheet.tag = TAG_LVLUP_BANNER_MON_ICON;
|
||
|
||
LoadSpriteSheet(&iconSheet);
|
||
LoadSpritePalette(&iconPalSheet);
|
||
|
||
spriteId = CreateSprite(&sSpriteTemplate_MonIconOnLvlUpBanner, 256, 10, 0);
|
||
gSprites[spriteId].sDestroy = FALSE;
|
||
gSprites[spriteId].sXOffset = gBattle_BG2_X;
|
||
}
|
||
|
||
static void SpriteCB_MonIconOnLvlUpBanner(struct Sprite *sprite)
|
||
{
|
||
sprite->x2 = sprite->sXOffset - gBattle_BG2_X;
|
||
|
||
if (sprite->x2 != 0)
|
||
{
|
||
sprite->sDestroy = TRUE;
|
||
}
|
||
else if (sprite->sDestroy)
|
||
{
|
||
DestroySprite(sprite);
|
||
FreeSpriteTilesByTag(TAG_LVLUP_BANNER_MON_ICON);
|
||
FreeSpritePaletteByTag(TAG_LVLUP_BANNER_MON_ICON);
|
||
}
|
||
}
|
||
|
||
#undef sDestroy
|
||
#undef sXOffset
|
||
|
||
static bool32 IsMonGettingExpSentOut(void)
|
||
{
|
||
if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId)
|
||
return TRUE;
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gBattlerPartyIndexes[2] == gBattleStruct->expGetterMonId)
|
||
return TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
static void Cmd_resetsentmonsvalue(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
ResetSentPokesToOpponentValue();
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setatktoplayer0(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gBattlerAttacker = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_makevisible(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
u32 battler;
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
BtlController_EmitSpriteInvisibility(battler, BUFFER_A, FALSE);
|
||
MarkBattlerForControllerExec(battler);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_recordability(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
RecordAbilityBattle(battler, gBattleMons[battler].ability);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BufferMoveToLearnIntoBattleTextBuff2(void)
|
||
{
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff2, gMoveToLearn);
|
||
}
|
||
|
||
static void Cmd_buffermovetolearn(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
BufferMoveToLearnIntoBattleTextBuff2();
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_jumpifplayerran(void)
|
||
{
|
||
CMD_ARGS(const u8 *jumpInstr);
|
||
|
||
if (TryRunFromBattle(gBattlerFainted))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_hpthresholds(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||
{
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
u32 opposingBattler = BATTLE_OPPOSITE(battler);
|
||
|
||
s32 result = gBattleMons[opposingBattler].hp * 100 / gBattleMons[opposingBattler].maxHP;
|
||
if (result == 0)
|
||
result = 1;
|
||
|
||
if (result > 69 || gBattleMons[opposingBattler].hp == 0)
|
||
gBattleStruct->hpScale = 0;
|
||
else if (result > 39)
|
||
gBattleStruct->hpScale = 1;
|
||
else if (result > 9)
|
||
gBattleStruct->hpScale = 2;
|
||
else
|
||
gBattleStruct->hpScale = 3;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_hpthresholds2(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||
{
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
u32 opposingBattler = BATTLE_OPPOSITE(battler);
|
||
u8 hpSwitchout = *(gBattleStruct->hpOnSwitchout + GetBattlerSide(opposingBattler));
|
||
s32 result = (hpSwitchout - gBattleMons[opposingBattler].hp) * 100 / hpSwitchout;
|
||
|
||
if (gBattleMons[opposingBattler].hp >= hpSwitchout)
|
||
gBattleStruct->hpScale = 0;
|
||
else if (result <= 29)
|
||
gBattleStruct->hpScale = 1;
|
||
else if (result <= 69)
|
||
gBattleStruct->hpScale = 2;
|
||
else
|
||
gBattleStruct->hpScale = 3;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_useitemonopponent(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gBattlerInMenuId = gBattlerAttacker;
|
||
PokemonUseItemEffects(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker]], gLastUsedItem, gBattlerPartyIndexes[gBattlerAttacker], 0, TRUE);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static bool32 HasAttackerFaintedTarget(void)
|
||
{
|
||
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
&& gBattleMoves[gCurrentMove].power != 0
|
||
&& (gLastHitBy[gBattlerTarget] == 0xFF || gLastHitBy[gBattlerTarget] == gBattlerAttacker)
|
||
&& gBattleStruct->moveTarget[gBattlerAttacker] == gBattlerTarget
|
||
&& gBattlerTarget != gBattlerAttacker
|
||
&& gCurrentTurnActionNumber == GetBattlerTurnOrderNum(gBattlerAttacker)
|
||
&& (gChosenMove == gChosenMoveByBattler[gBattlerAttacker] || gChosenMove == gBattleMons[gBattlerAttacker].moves[gChosenMovePos]))
|
||
return TRUE;
|
||
else
|
||
return FALSE;
|
||
}
|
||
|
||
bool32 CanPoisonType(u8 battlerAttacker, u8 battlerTarget)
|
||
{
|
||
return ((GetBattlerAbility(battlerAttacker) == ABILITY_CORROSION && gBattleMoves[gCurrentMove].category == BATTLE_CATEGORY_STATUS)
|
||
|| !(IS_BATTLER_OF_TYPE(battlerTarget, TYPE_POISON) || IS_BATTLER_OF_TYPE(battlerTarget, TYPE_STEEL)));
|
||
}
|
||
|
||
bool32 CanParalyzeType(u8 battlerAttacker, u8 battlerTarget)
|
||
{
|
||
return !(B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battlerTarget, TYPE_ELECTRIC));
|
||
}
|
||
|
||
bool32 CanUseLastResort(u8 battler)
|
||
{
|
||
u32 i;
|
||
u32 knownMovesCount = 0, usedMovesCount = 0;
|
||
|
||
for (i = 0; i < 4; i++)
|
||
{
|
||
if (gBattleMons[battler].moves[i] != MOVE_NONE)
|
||
knownMovesCount++;
|
||
if (i != gCurrMovePos && gDisableStructs[battler].usedMoves & gBitTable[i]) // Increment used move count for all moves except current Last Resort.
|
||
usedMovesCount++;
|
||
}
|
||
|
||
return (knownMovesCount >= 2 && usedMovesCount >= knownMovesCount - 1);
|
||
}
|
||
|
||
static void RemoveAllTerrains(void)
|
||
{
|
||
gFieldTimers.terrainTimer = 0;
|
||
switch (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)
|
||
{
|
||
case STATUS_FIELD_MISTY_TERRAIN:
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_END_MISTY;
|
||
break;
|
||
case STATUS_FIELD_GRASSY_TERRAIN:
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_END_GRASSY;
|
||
break;
|
||
case STATUS_FIELD_ELECTRIC_TERRAIN:
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_END_ELECTRIC;
|
||
break;
|
||
case STATUS_FIELD_PSYCHIC_TERRAIN:
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_END_PSYCHIC;
|
||
break;
|
||
default:
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_COUNT; // failsafe
|
||
break;
|
||
}
|
||
gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; // remove the terrain
|
||
}
|
||
|
||
#define DEFOG_CLEAR(status, structField, battlescript, move)\
|
||
{ \
|
||
if (*sideStatuses & status) \
|
||
{ \
|
||
if (clear) \
|
||
{ \
|
||
if (move) \
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff1, move);\
|
||
*sideStatuses &= ~status; \
|
||
sideTimer->structField = 0; \
|
||
BattleScriptPushCursor(); \
|
||
gBattlescriptCurrInstr = battlescript; \
|
||
} \
|
||
return TRUE; \
|
||
} \
|
||
}
|
||
|
||
static bool32 TryDefogClear(u32 battlerAtk, bool32 clear)
|
||
{
|
||
s32 i;
|
||
u8 saveBattler = gBattlerAttacker;
|
||
|
||
for (i = 0; i < 2; i++)
|
||
{
|
||
struct SideTimer *sideTimer = &gSideTimers[i];
|
||
u32 *sideStatuses = &gSideStatuses[i];
|
||
|
||
gBattlerAttacker = i; // For correct battle string. Ally's / Foe's
|
||
if (GetBattlerSide(battlerAtk) != i)
|
||
{
|
||
DEFOG_CLEAR(SIDE_STATUS_REFLECT, reflectTimer, BattleScript_SideStatusWoreOffReturn, MOVE_REFLECT);
|
||
DEFOG_CLEAR(SIDE_STATUS_LIGHTSCREEN, lightscreenTimer, BattleScript_SideStatusWoreOffReturn, MOVE_LIGHT_SCREEN);
|
||
DEFOG_CLEAR(SIDE_STATUS_MIST, mistTimer, BattleScript_SideStatusWoreOffReturn, MOVE_MIST);
|
||
DEFOG_CLEAR(SIDE_STATUS_AURORA_VEIL, auroraVeilTimer, BattleScript_SideStatusWoreOffReturn, MOVE_AURORA_VEIL);
|
||
DEFOG_CLEAR(SIDE_STATUS_SAFEGUARD, safeguardTimer, BattleScript_SideStatusWoreOffReturn, MOVE_SAFEGUARD);
|
||
}
|
||
DEFOG_CLEAR(SIDE_STATUS_SPIKES, spikesAmount, BattleScript_SpikesDefog, 0);
|
||
DEFOG_CLEAR(SIDE_STATUS_STEALTH_ROCK, stealthRockAmount, BattleScript_StealthRockDefog, 0);
|
||
DEFOG_CLEAR(SIDE_STATUS_TOXIC_SPIKES, toxicSpikesAmount, BattleScript_ToxicSpikesDefog, 0);
|
||
DEFOG_CLEAR(SIDE_STATUS_STICKY_WEB, stickyWebAmount, BattleScript_StickyWebDefog, 0);
|
||
DEFOG_CLEAR(SIDE_STATUS_STEELSURGE, steelsurgeAmount, BattleScript_SteelsurgeDefog, 0);
|
||
if (B_DEFOG_CLEARS_TERRAIN >= GEN_8 && (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY))
|
||
{
|
||
RemoveAllTerrains();
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_TerrainEnds_Ret;
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
gBattlerAttacker = saveBattler;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
u32 IsFlowerVeilProtected(u32 battler)
|
||
{
|
||
if (IS_BATTLER_OF_TYPE(battler, TYPE_GRASS))
|
||
return IsAbilityOnSide(battler, ABILITY_FLOWER_VEIL);
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
u32 IsLeafGuardProtected(u32 battler)
|
||
{
|
||
if (IsBattlerWeatherAffected(battler, B_WEATHER_SUN))
|
||
return GetBattlerAbility(battler) == ABILITY_LEAF_GUARD;
|
||
else
|
||
return 0;
|
||
}
|
||
|
||
bool32 IsShieldsDownProtected(u32 battler)
|
||
{
|
||
return (GetBattlerAbility(battler) == ABILITY_SHIELDS_DOWN
|
||
&& GetFormIdFromFormSpeciesId(gBattleMons[battler].species) < GetFormIdFromFormSpeciesId(SPECIES_MINIOR_CORE_RED)); // Minior is not in core form
|
||
}
|
||
|
||
u32 IsAbilityStatusProtected(u32 battler)
|
||
{
|
||
return IsFlowerVeilProtected(battler)
|
||
|| IsLeafGuardProtected(battler)
|
||
|| IsShieldsDownProtected(battler);
|
||
}
|
||
|
||
u32 GetHighestStatId(u32 battler)
|
||
{
|
||
u32 i, highestId = STAT_ATK, highestStat = gBattleMons[battler].attack;
|
||
|
||
for (i = STAT_DEF; i < NUM_STATS; i++)
|
||
{
|
||
u16 *statVal = &gBattleMons[battler].attack + (i - 1);
|
||
if (*statVal > highestStat)
|
||
{
|
||
highestStat = *statVal;
|
||
highestId = i;
|
||
}
|
||
}
|
||
return highestId;
|
||
}
|
||
|
||
static bool32 IsRototillerAffected(u32 battler)
|
||
{
|
||
if (!IsBattlerAlive(battler))
|
||
return FALSE;
|
||
if (!IsBattlerGrounded(battler))
|
||
return FALSE; // Only grounded battlers affected
|
||
if (!IS_BATTLER_OF_TYPE(battler, TYPE_GRASS))
|
||
return FALSE; // Only grass types affected
|
||
if (gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)
|
||
return FALSE; // Rototiller doesn't affected semi-invulnerable battlers
|
||
if (BlocksPrankster(MOVE_ROTOTILLER, gBattlerAttacker, battler, FALSE))
|
||
return FALSE;
|
||
return TRUE;
|
||
}
|
||
|
||
static bool32 IsElectricAbilityAffected(u32 ability)
|
||
{
|
||
u32 moveType;
|
||
|
||
if (gBattleStruct->dynamicMoveType == 0)
|
||
moveType = gBattleMoves[gCurrentMove].type;
|
||
else if (!(gBattleStruct->dynamicMoveType & F_DYNAMIC_TYPE_IGNORE_PHYSICALITY))
|
||
moveType = gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK;
|
||
else
|
||
moveType = gBattleMoves[gCurrentMove].type;
|
||
|
||
if (moveType == TYPE_ELECTRIC && GetBattlerAbility(gBattlerTarget) == ability)
|
||
return TRUE;
|
||
else
|
||
return FALSE;
|
||
}
|
||
|
||
static bool32 IsTeatimeAffected(u32 battler)
|
||
{
|
||
if (ItemId_GetPocket(gBattleMons[battler].item) != POCKET_BERRIES)
|
||
return FALSE; // Only berries
|
||
if (gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)
|
||
return FALSE; // Teatime doesn't affected semi-invulnerable battlers
|
||
return TRUE;
|
||
}
|
||
|
||
#define COURTCHANGE_SWAP(status, structField, temp) \
|
||
{ \
|
||
temp = gSideStatuses[B_SIDE_PLAYER]; \
|
||
if (gSideStatuses[B_SIDE_OPPONENT] & status) \
|
||
gSideStatuses[B_SIDE_PLAYER] |= status; \
|
||
else \
|
||
gSideStatuses[B_SIDE_PLAYER] &= ~(status); \
|
||
if (temp & status) \
|
||
gSideStatuses[B_SIDE_OPPONENT] |= status; \
|
||
else \
|
||
gSideStatuses[B_SIDE_OPPONENT] &= ~(status); \
|
||
SWAP(sideTimerPlayer->structField, sideTimerOpp->structField, temp);\
|
||
} \
|
||
|
||
#define UPDATE_COURTCHANGED_BATTLER(structField)\
|
||
{ \
|
||
temp = sideTimerPlayer->structField; \
|
||
sideTimerPlayer->structField = BATTLE_OPPOSITE(sideTimerOpp->structField); \
|
||
sideTimerOpp->structField = BATTLE_OPPOSITE(temp); \
|
||
} \
|
||
|
||
static void CourtChangeSwapSideStatuses(void)
|
||
{
|
||
struct SideTimer *sideTimerPlayer = &gSideTimers[B_SIDE_PLAYER];
|
||
struct SideTimer *sideTimerOpp = &gSideTimers[B_SIDE_OPPONENT];
|
||
u32 temp;
|
||
|
||
// Swap timers and statuses
|
||
COURTCHANGE_SWAP(SIDE_STATUS_REFLECT, reflectTimer, temp)
|
||
COURTCHANGE_SWAP(SIDE_STATUS_LIGHTSCREEN, lightscreenTimer, temp)
|
||
COURTCHANGE_SWAP(SIDE_STATUS_MIST, mistTimer, temp);
|
||
COURTCHANGE_SWAP(SIDE_STATUS_SAFEGUARD, safeguardTimer, temp);
|
||
COURTCHANGE_SWAP(SIDE_STATUS_AURORA_VEIL, auroraVeilTimer, temp);
|
||
COURTCHANGE_SWAP(SIDE_STATUS_TAILWIND, tailwindTimer, temp);
|
||
// Lucky Chant doesn't exist in gen 8, but seems like it should be affected by Court Change
|
||
COURTCHANGE_SWAP(SIDE_STATUS_LUCKY_CHANT, luckyChantTimer, temp);
|
||
COURTCHANGE_SWAP(SIDE_STATUS_SPIKES, spikesAmount, temp);
|
||
COURTCHANGE_SWAP(SIDE_STATUS_STEALTH_ROCK, stealthRockAmount, temp);
|
||
COURTCHANGE_SWAP(SIDE_STATUS_TOXIC_SPIKES, toxicSpikesAmount, temp);
|
||
COURTCHANGE_SWAP(SIDE_STATUS_STICKY_WEB, stickyWebAmount, temp);
|
||
COURTCHANGE_SWAP(SIDE_STATUS_STEELSURGE, steelsurgeAmount, temp);
|
||
COURTCHANGE_SWAP(SIDE_STATUS_DAMAGE_NON_TYPES, damageNonTypesTimer, temp);
|
||
// Track Pledge effect side
|
||
COURTCHANGE_SWAP(SIDE_STATUS_RAINBOW, rainbowTimer, temp);
|
||
COURTCHANGE_SWAP(SIDE_STATUS_SEA_OF_FIRE, seaOfFireTimer, temp);
|
||
COURTCHANGE_SWAP(SIDE_STATUS_SWAMP, swampTimer, temp);
|
||
|
||
// Change battler IDs of swapped effects. Needed for the correct string when they expire
|
||
// E.g. "Foe's Reflect wore off!"
|
||
UPDATE_COURTCHANGED_BATTLER(reflectBattlerId);
|
||
UPDATE_COURTCHANGED_BATTLER(lightscreenBattlerId);
|
||
UPDATE_COURTCHANGED_BATTLER(mistBattlerId);
|
||
UPDATE_COURTCHANGED_BATTLER(safeguardBattlerId);
|
||
UPDATE_COURTCHANGED_BATTLER(auroraVeilBattlerId);
|
||
UPDATE_COURTCHANGED_BATTLER(tailwindBattlerId);
|
||
UPDATE_COURTCHANGED_BATTLER(luckyChantBattlerId);
|
||
UPDATE_COURTCHANGED_BATTLER(stickyWebBattlerId);
|
||
|
||
// Track which side originally set the Sticky Web
|
||
SWAP(sideTimerPlayer->stickyWebBattlerSide, sideTimerOpp->stickyWebBattlerSide, temp);
|
||
|
||
// Swap what type set the Gigantamax damage over time effect
|
||
SWAP(sideTimerPlayer->damageNonTypesType, sideTimerOpp->damageNonTypesType, temp);
|
||
}
|
||
|
||
static void HandleScriptMegaPrimalBurst(u32 caseId, u32 battler, u32 type)
|
||
{
|
||
struct Pokemon *party = GetBattlerParty(battler);
|
||
struct Pokemon *mon = &party[gBattlerPartyIndexes[battler]];
|
||
u32 position = GetBattlerPosition(battler);
|
||
u32 side = GetBattlerSide(battler);
|
||
|
||
// Change species.
|
||
if (caseId == 0)
|
||
{
|
||
if (type == HANDLE_TYPE_MEGA_EVOLUTION)
|
||
{
|
||
if (!TryBattleFormChange(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM))
|
||
TryBattleFormChange(battler, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE);
|
||
}
|
||
else if (type == HANDLE_TYPE_PRIMAL_REVERSION)
|
||
TryBattleFormChange(battler, FORM_CHANGE_BATTLE_PRIMAL_REVERSION);
|
||
else
|
||
TryBattleFormChange(battler, FORM_CHANGE_BATTLE_ULTRA_BURST);
|
||
|
||
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[battler].species);
|
||
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_SPECIES_BATTLE, gBitTable[gBattlerPartyIndexes[battler]], sizeof(gBattleMons[battler].species), &gBattleMons[battler].species);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
// Update healthbox and elevation and play cry.
|
||
else
|
||
{
|
||
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL);
|
||
if (side == B_SIDE_OPPONENT)
|
||
SetBattlerShadowSpriteCallback(battler, gBattleMons[battler].species);
|
||
if (type == HANDLE_TYPE_MEGA_EVOLUTION)
|
||
gBattleStruct->mega.alreadyEvolved[position] = TRUE;
|
||
if (type == HANDLE_TYPE_ULTRA_BURST)
|
||
gBattleStruct->burst.alreadyBursted[position] = TRUE;
|
||
}
|
||
}
|
||
|
||
// Return True if the order was changed, and false if the order was not changed(for example because the target would move after the attacker anyway).
|
||
static bool32 ChangeOrderTargetAfterAttacker(void)
|
||
{
|
||
u32 i;
|
||
u8 data[MAX_BATTLERS_COUNT];
|
||
|
||
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)
|
||
|| GetBattlerTurnOrderNum(gBattlerAttacker) + 1 == GetBattlerTurnOrderNum(gBattlerTarget))
|
||
return FALSE;
|
||
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
data[i] = gBattlerByTurnOrder[i];
|
||
if (GetBattlerTurnOrderNum(gBattlerAttacker) == 0 && GetBattlerTurnOrderNum(gBattlerTarget) == 2)
|
||
{
|
||
gBattlerByTurnOrder[1] = gBattlerTarget;
|
||
gBattlerByTurnOrder[2] = data[1];
|
||
gBattlerByTurnOrder[3] = data[3];
|
||
}
|
||
else if (GetBattlerTurnOrderNum(gBattlerAttacker) == 0 && GetBattlerTurnOrderNum(gBattlerTarget) == 3)
|
||
{
|
||
gBattlerByTurnOrder[1] = gBattlerTarget;
|
||
gBattlerByTurnOrder[2] = data[1];
|
||
gBattlerByTurnOrder[3] = data[2];
|
||
}
|
||
else // Attacker == 1, Target == 3
|
||
{
|
||
gBattlerByTurnOrder[2] = gBattlerTarget;
|
||
gBattlerByTurnOrder[3] = data[2];
|
||
}
|
||
return TRUE;
|
||
}
|
||
|
||
static u32 CalculateBattlerPartyCount(u32 battler)
|
||
{
|
||
u32 count;
|
||
if (GetBattlerSide(battler) == B_SIDE_PLAYER)
|
||
count = CalculatePlayerPartyCount();
|
||
else
|
||
count = CalculateEnemyPartyCount();
|
||
return count;
|
||
}
|
||
|
||
static void Cmd_various(void)
|
||
{
|
||
CMD_ARGS(u8 battler, u8 id);
|
||
|
||
struct Pokemon *mon;
|
||
s32 i;
|
||
u8 data[10];
|
||
u32 side, battler, bits;
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
|
||
switch (cmd->id)
|
||
{
|
||
// Roar will fail in a double wild battle when used by the player against one of the two alive wild mons.
|
||
// Also when an opposing wild mon uses it againt its partner.
|
||
case VARIOUS_JUMP_IF_ROAR_FAILS:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (WILD_DOUBLE_BATTLE
|
||
&& GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER
|
||
&& GetBattlerSide(gBattlerTarget) == B_SIDE_OPPONENT
|
||
&& IS_WHOLE_SIDE_ALIVE(gBattlerTarget))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else if (WILD_DOUBLE_BATTLE
|
||
&& GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT
|
||
&& GetBattlerSide(gBattlerTarget) == B_SIDE_OPPONENT)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_ABSENT:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (!IsBattlerAlive(battler))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_SHIELDS_DOWN_PROTECTED:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (IsShieldsDownProtected(battler))
|
||
{
|
||
gBattlerAbility = battler;
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_NO_HOLD_EFFECT:
|
||
{
|
||
VARIOUS_ARGS(u8 holdEffect, const u8 *jumpInstr);
|
||
if (GetBattlerHoldEffect(battler, TRUE) != cmd->holdEffect)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
}
|
||
else
|
||
{
|
||
gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_NO_ALLY:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (!IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_INFATUATE_WITH_BATTLER:
|
||
{
|
||
VARIOUS_ARGS(u8 infatuateWith);
|
||
gBattleScripting.battler = battler;
|
||
gBattleMons[battler].status2 |= STATUS2_INFATUATED_WITH(GetBattlerForBattleScript(cmd->infatuateWith));
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_SET_LAST_USED_ITEM:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gLastUsedItem = gBattleMons[battler].item;
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_FAIRY_LOCK:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (gFieldStatuses & STATUS_FIELD_FAIRY_LOCK)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gFieldStatuses |= STATUS_FIELD_FAIRY_LOCK;
|
||
gFieldTimers.fairyLockTimer = 2;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_GET_STAT_VALUE:
|
||
{
|
||
VARIOUS_ARGS(u8 stat);
|
||
i = cmd->stat;
|
||
gBattleMoveDamage = *(u16 *)(&gBattleMons[battler].attack) + (i - 1);
|
||
gBattleMoveDamage *= gStatStageRatios[gBattleMons[battler].statStages[i]][0];
|
||
gBattleMoveDamage /= gStatStageRatios[gBattleMons[battler].statStages[i]][1];
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_FULL_HP:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (BATTLER_MAX_HP(battler))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_FRISK:
|
||
{
|
||
VARIOUS_ARGS();
|
||
while (gBattleStruct->friskedBattler < gBattlersCount)
|
||
{
|
||
gBattlerTarget = gBattleStruct->friskedBattler++;
|
||
if (GetBattlerSide(battler) != GetBattlerSide(gBattlerTarget)
|
||
&& IsBattlerAlive(gBattlerTarget)
|
||
&& gBattleMons[gBattlerTarget].item != ITEM_NONE)
|
||
{
|
||
gLastUsedItem = gBattleMons[gBattlerTarget].item;
|
||
RecordItemEffectBattle(gBattlerTarget, GetBattlerHoldEffect(gBattlerTarget, FALSE));
|
||
BattleScriptPushCursor();
|
||
// If Frisk identifies two mons' items, show the pop-up only once.
|
||
if (gBattleStruct->friskedAbility)
|
||
{
|
||
gBattlescriptCurrInstr = BattleScript_FriskMsg;
|
||
}
|
||
else
|
||
{
|
||
gBattleStruct->friskedAbility = TRUE;
|
||
gBattlescriptCurrInstr = BattleScript_FriskMsgWithPopup;
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
gBattleStruct->friskedBattler = 0;
|
||
gBattleStruct->friskedAbility = FALSE;
|
||
break;
|
||
}
|
||
case VARIOUS_POISON_TYPE_IMMUNITY:
|
||
{
|
||
VARIOUS_ARGS(u8 target, const u8 *failInstr);
|
||
if (!CanPoisonType(battler, GetBattlerForBattleScript(cmd->target)))
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_PARALYZE_TYPE_IMMUNITY:
|
||
{
|
||
VARIOUS_ARGS(u8 target, const u8 *failInstr);
|
||
if (!CanParalyzeType(battler, GetBattlerForBattleScript(cmd->target)))
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_TRACE_ABILITY:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleMons[battler].ability = gBattleStruct->overwrittenAbilities[battler] = gBattleStruct->tracedAbility[battler];
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_ILLUSION_OFF:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (GetIllusionMonPtr(battler) != NULL)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_IllusionOff;
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_SET_SPRITEIGNORE0HP:
|
||
{
|
||
VARIOUS_ARGS(bool8 ignore0HP);
|
||
gBattleStruct->spriteIgnore0Hp = cmd->ignore0HP;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_UPDATE_NICK:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (GetBattlerSide(battler) == B_SIDE_PLAYER)
|
||
mon = &gPlayerParty[gBattlerPartyIndexes[battler]];
|
||
else
|
||
mon = &gEnemyParty[gBattlerPartyIndexes[battler]];
|
||
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_NICK);
|
||
break;
|
||
}
|
||
case VARIOUS_JUMP_IF_NOT_BERRY:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (ItemId_GetPocket(gBattleMons[battler].item) == POCKET_BERRIES)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if ((gStatuses3[battler] & (STATUS3_SEMI_INVULNERABLE | STATUS3_HEAL_BLOCK))
|
||
|| BATTLER_MAX_HP(battler)
|
||
|| !gBattleMons[battler].hp
|
||
|| !(IsBattlerGrounded(battler)))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 16;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
gBattleMoveDamage *= -1;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_GRAVITY_ON_AIRBORNE_MONS:
|
||
{
|
||
VARIOUS_ARGS();
|
||
// Cancel all multiturn moves of IN_AIR Pokemon except those being targeted by Sky Drop.
|
||
if (gStatuses3[battler] & STATUS3_ON_AIR && !(gStatuses3[battler] & STATUS3_SKY_DROPPED))
|
||
CancelMultiTurnMoves(battler);
|
||
|
||
gStatuses3[battler] &= ~(STATUS3_MAGNET_RISE | STATUS3_TELEKINESIS | STATUS3_ON_AIR | STATUS3_SKY_DROPPED);
|
||
break;
|
||
}
|
||
case VARIOUS_SPECTRAL_THIEF:
|
||
{
|
||
VARIOUS_ARGS();
|
||
// Raise stats
|
||
for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++)
|
||
{
|
||
if (gBattleStruct->stolenStats[0] & gBitTable[i])
|
||
{
|
||
gBattleStruct->stolenStats[0] &= ~(gBitTable[i]);
|
||
SET_STATCHANGER(i, gBattleStruct->stolenStats[i], FALSE);
|
||
if (ChangeStatBuffs(GET_STAT_BUFF_VALUE_WITH_SIGN(gBattleScripting.statChanger), i, MOVE_EFFECT_CERTAIN | MOVE_EFFECT_AFFECTS_USER, NULL) == STAT_CHANGE_WORKED)
|
||
{
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_StatUpMsg;
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_SET_POWDER:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleMons[battler].status2 |= STATUS2_POWDER;
|
||
break;
|
||
}
|
||
case VARIOUS_ACUPRESSURE:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
bits = 0;
|
||
for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++)
|
||
{
|
||
if (CompareStat(battler, i, MAX_STAT_STAGE, CMP_LESS_THAN))
|
||
bits |= gBitTable[i];
|
||
}
|
||
if (bits)
|
||
{
|
||
u32 statId;
|
||
do
|
||
{
|
||
statId = (Random() % (NUM_BATTLE_STATS - 1)) + 1;
|
||
} while (!(bits & gBitTable[statId]));
|
||
|
||
SET_STATCHANGER(statId, 2, FALSE);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_CANCEL_MULTI_TURN_MOVES:
|
||
{
|
||
VARIOUS_ARGS();
|
||
const u8 *result;
|
||
result = CancelMultiTurnMoves(battler);
|
||
if (result)
|
||
{
|
||
gBattlescriptCurrInstr = result;
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_SET_MAGIC_COAT_TARGET:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleStruct->attackerBeforeBounce = battler;
|
||
gBattlerAttacker = gBattlerTarget;
|
||
side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
|
||
if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove))
|
||
gBattlerTarget = gSideTimers[side].followmeTarget;
|
||
else
|
||
gBattlerTarget = battler;
|
||
break;
|
||
}
|
||
case VARIOUS_IS_RUNNING_IMPOSSIBLE:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleCommunication[0] = IsRunningFromBattleImpossible(battler);
|
||
break;
|
||
}
|
||
case VARIOUS_GET_MOVE_TARGET:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
||
break;
|
||
}
|
||
case VARIOUS_GET_BATTLER_FAINTED:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (gHitMarker & HITMARKER_FAINTED(battler))
|
||
gBattleCommunication[0] = TRUE;
|
||
else
|
||
gBattleCommunication[0] = FALSE;
|
||
break;
|
||
}
|
||
case VARIOUS_RESET_SWITCH_IN_ABILITY_BITS:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gSpecialStatuses[battler].traced = FALSE;
|
||
gSpecialStatuses[battler].switchInAbilityDone = FALSE;
|
||
break;
|
||
}
|
||
case VARIOUS_UPDATE_CHOICE_MOVE_ON_LVL_UP:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId || gBattlerPartyIndexes[2] == gBattleStruct->expGetterMonId)
|
||
{
|
||
if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId)
|
||
battler = 0;
|
||
else
|
||
battler = 2;
|
||
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gBattleMons[battler].moves[i] == gBattleStruct->choicedMove[battler])
|
||
break;
|
||
}
|
||
if (i == MAX_MON_MOVES)
|
||
gBattleStruct->choicedMove[battler] = MOVE_NONE;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_RESET_PLAYER_FAINTED:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_DOUBLE))
|
||
&& gBattleTypeFlags & BATTLE_TYPE_TRAINER
|
||
&& gBattleMons[0].hp != 0
|
||
&& gBattleMons[1].hp != 0)
|
||
{
|
||
gHitMarker &= ~HITMARKER_PLAYER_FAINTED;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_PALACE_FLAVOR_TEXT:
|
||
{
|
||
VARIOUS_ARGS();
|
||
// Try and print end-of-turn Battle Palace flavor text (e.g. "A glint appears in mon's eyes")
|
||
gBattleCommunication[0] = FALSE; // whether or not msg should be printed
|
||
gBattleScripting.battler = battler = gBattleCommunication[1];
|
||
if (!(gBattleStruct->palaceFlags & gBitTable[battler])
|
||
&& gBattleMons[battler].maxHP / 2 >= gBattleMons[battler].hp
|
||
&& gBattleMons[battler].hp != 0
|
||
&& !(gBattleMons[battler].status1 & STATUS1_SLEEP))
|
||
{
|
||
gBattleStruct->palaceFlags |= gBitTable[battler];
|
||
gBattleCommunication[0] = TRUE;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = sBattlePalaceNatureToFlavorTextId[GetNatureFromPersonality(gBattleMons[battler].personality)];
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_ARENA_JUDGMENT_WINDOW:
|
||
{
|
||
VARIOUS_ARGS();
|
||
|
||
i = BattleArena_ShowJudgmentWindow(&gBattleCommunication[0]);
|
||
|
||
// BattleArena_ShowJudgmentWindow's last state was an intermediate step.
|
||
// Return without advancing the current instruction so that it will be called again.
|
||
if (i == ARENA_RESULT_RUNNING)
|
||
return;
|
||
|
||
gBattleCommunication[1] = i;
|
||
break;
|
||
}
|
||
case VARIOUS_ARENA_OPPONENT_MON_LOST:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleMons[1].hp = 0;
|
||
gHitMarker |= HITMARKER_FAINTED(1);
|
||
gBattleStruct->arenaLostOpponentMons |= gBitTable[gBattlerPartyIndexes[1]];
|
||
gDisableStructs[1].truantSwitchInHack = 1;
|
||
break;
|
||
}
|
||
case VARIOUS_ARENA_PLAYER_MON_LOST:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleMons[0].hp = 0;
|
||
gHitMarker |= HITMARKER_FAINTED(0);
|
||
gHitMarker |= HITMARKER_PLAYER_FAINTED;
|
||
gBattleStruct->arenaLostPlayerMons |= gBitTable[gBattlerPartyIndexes[0]];
|
||
gDisableStructs[0].truantSwitchInHack = 1;
|
||
break;
|
||
}
|
||
case VARIOUS_ARENA_BOTH_MONS_LOST:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleMons[0].hp = 0;
|
||
gBattleMons[1].hp = 0;
|
||
gHitMarker |= HITMARKER_FAINTED(0);
|
||
gHitMarker |= HITMARKER_FAINTED(1);
|
||
gHitMarker |= HITMARKER_PLAYER_FAINTED;
|
||
gBattleStruct->arenaLostPlayerMons |= gBitTable[gBattlerPartyIndexes[0]];
|
||
gBattleStruct->arenaLostOpponentMons |= gBitTable[gBattlerPartyIndexes[1]];
|
||
gDisableStructs[0].truantSwitchInHack = 1;
|
||
gDisableStructs[1].truantSwitchInHack = 1;
|
||
break;
|
||
}
|
||
case VARIOUS_EMIT_YESNOBOX:
|
||
{
|
||
VARIOUS_ARGS();
|
||
BtlController_EmitYesNoBox(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
break;
|
||
}
|
||
case VARIOUS_DRAW_ARENA_REF_TEXT_BOX:
|
||
{
|
||
VARIOUS_ARGS();
|
||
DrawArenaRefereeTextBox();
|
||
break;
|
||
}
|
||
case VARIOUS_ERASE_ARENA_REF_TEXT_BOX:
|
||
{
|
||
VARIOUS_ARGS();
|
||
EraseArenaRefereeTextBox();
|
||
break;
|
||
}
|
||
case VARIOUS_ARENA_JUDGMENT_STRING:
|
||
{
|
||
CMD_ARGS(u8 id, u8 _);
|
||
BattleStringExpandPlaceholdersToDisplayedString(gRefereeStringsTable[cmd->id]);
|
||
BattlePutTextOnWindow(gDisplayedStringBattle, ARENA_WIN_JUDGMENT_TEXT);
|
||
break;
|
||
}
|
||
case VARIOUS_ARENA_WAIT_STRING:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (IsTextPrinterActive(ARENA_WIN_JUDGMENT_TEXT))
|
||
return;
|
||
break;
|
||
}
|
||
case VARIOUS_WAIT_CRY:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (!IsCryFinished())
|
||
return;
|
||
break;
|
||
}
|
||
case VARIOUS_RETURN_OPPONENT_MON1:
|
||
{
|
||
VARIOUS_ARGS();
|
||
battler = 1;
|
||
if (gBattleMons[battler].hp != 0)
|
||
{
|
||
BtlController_EmitReturnMonToBall(battler, BUFFER_A, FALSE);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_RETURN_OPPONENT_MON2:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (gBattlersCount > 3)
|
||
{
|
||
battler = 3;
|
||
if (gBattleMons[battler].hp != 0)
|
||
{
|
||
BtlController_EmitReturnMonToBall(battler, BUFFER_A, FALSE);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_VOLUME_DOWN:
|
||
{
|
||
VARIOUS_ARGS();
|
||
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x55);
|
||
break;
|
||
}
|
||
case VARIOUS_VOLUME_UP:
|
||
{
|
||
VARIOUS_ARGS();
|
||
m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100);
|
||
break;
|
||
}
|
||
case VARIOUS_SET_ALREADY_STATUS_MOVE_ATTEMPT:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleStruct->alreadyStatusedMoveAttempt |= gBitTable[battler];
|
||
break;
|
||
}
|
||
case VARIOUS_PALACE_TRY_ESCAPE_STATUS:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (BattlePalace_TryEscapeStatus(battler))
|
||
return;
|
||
break;
|
||
}
|
||
case VARIOUS_SET_TELEPORT_OUTCOME:
|
||
{
|
||
VARIOUS_ARGS();
|
||
// Don't end the battle if one of the wild mons teleported from the wild double battle
|
||
// and its partner is still alive.
|
||
if (GetBattlerSide(battler) == B_SIDE_OPPONENT && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||
{
|
||
gAbsentBattlerFlags |= gBitTable[battler];
|
||
gHitMarker |= HITMARKER_FAINTED(battler);
|
||
gBattleMons[battler].hp = 0;
|
||
SetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_HP, &gBattleMons[battler].hp);
|
||
SetHealthboxSpriteInvisible(gHealthboxSpriteIds[battler]);
|
||
FaintClearSetData(battler);
|
||
}
|
||
else if (GetBattlerSide(battler) == B_SIDE_PLAYER)
|
||
{
|
||
gBattleOutcome = B_OUTCOME_PLAYER_TELEPORTED;
|
||
}
|
||
else
|
||
{
|
||
gBattleOutcome = B_OUTCOME_MON_TELEPORTED;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_PLAY_TRAINER_DEFEATED_MUSIC:
|
||
{
|
||
VARIOUS_ARGS();
|
||
BtlController_EmitPlayFanfareOrBGM(battler, BUFFER_A, MUS_VICTORY_TRAINER, TRUE);
|
||
MarkBattlerForControllerExec(battler);
|
||
break;
|
||
}
|
||
case VARIOUS_STAT_TEXT_BUFFER:
|
||
{
|
||
VARIOUS_ARGS();
|
||
PREPARE_STAT_BUFFER(gBattleTextBuff1, gBattleCommunication[0]);
|
||
break;
|
||
}
|
||
case VARIOUS_SWITCHIN_ABILITIES:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0);
|
||
AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0);
|
||
AbilityBattleEffects(ABILITYEFFECT_TRACE2, battler, 0, 0, 0);
|
||
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();
|
||
BtlController_EmitHealthBarUpdate(battler, BUFFER_A, INSTANT_HP_BAR_DROP);
|
||
MarkBattlerForControllerExec(battler);
|
||
break;
|
||
}
|
||
case VARIOUS_CLEAR_STATUS:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleMons[battler].status1 = 0;
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[battler].status1), &gBattleMons[battler].status1);
|
||
MarkBattlerForControllerExec(battler);
|
||
break;
|
||
}
|
||
case VARIOUS_RESTORE_PP:
|
||
{
|
||
VARIOUS_ARGS();
|
||
for (i = 0; i < 4; i++)
|
||
{
|
||
gBattleMons[battler].pp[i] = CalculatePPWithBonus(gBattleMons[battler].moves[i], gBattleMons[battler].ppBonuses, i);
|
||
data[i] = gBattleMons[battler].pp[i];
|
||
}
|
||
data[i] = gBattleMons[battler].ppBonuses;
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_PP_DATA_BATTLE, 0, 5, data);
|
||
MarkBattlerForControllerExec(battler);
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_ACTIVATE_MOXIE: // and chilling neigh + as one ice rider
|
||
{
|
||
VARIOUS_ARGS();
|
||
|
||
u16 battlerAbility = GetBattlerAbility(battler);
|
||
|
||
if ((battlerAbility == ABILITY_MOXIE
|
||
|| battlerAbility == ABILITY_CHILLING_NEIGH
|
||
|| battlerAbility == ABILITY_AS_ONE_ICE_RIDER)
|
||
&& HasAttackerFaintedTarget()
|
||
&& !NoAliveMonsForEitherParty()
|
||
&& CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
|
||
{
|
||
SET_STATCHANGER(STAT_ATK, 1, FALSE);
|
||
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK);
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gLastUsedAbility = battlerAbility;
|
||
if (battlerAbility == ABILITY_AS_ONE_ICE_RIDER)
|
||
gBattleScripting.abilityPopupOverwrite = gLastUsedAbility = ABILITY_CHILLING_NEIGH;
|
||
gBattlescriptCurrInstr = BattleScript_RaiseStatOnFaintingTarget;
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_ACTIVATE_GRIM_NEIGH: // and as one shadow rider
|
||
{
|
||
VARIOUS_ARGS();
|
||
|
||
u16 battlerAbility = GetBattlerAbility(battler);
|
||
|
||
if ((battlerAbility == ABILITY_GRIM_NEIGH
|
||
|| battlerAbility == ABILITY_AS_ONE_SHADOW_RIDER)
|
||
&& HasAttackerFaintedTarget()
|
||
&& !NoAliveMonsForEitherParty()
|
||
&& CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))
|
||
{
|
||
SET_STATCHANGER(STAT_SPATK, 1, FALSE);
|
||
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPATK);
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gLastUsedAbility = battlerAbility;
|
||
if (battlerAbility == ABILITY_AS_ONE_SHADOW_RIDER)
|
||
gBattleScripting.abilityPopupOverwrite = gLastUsedAbility = ABILITY_GRIM_NEIGH;
|
||
gBattlescriptCurrInstr = BattleScript_RaiseStatOnFaintingTarget;
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_ACTIVATE_RECEIVER: // Partner gets fainted's ally ability
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattlerAbility = BATTLE_PARTNER(battler);
|
||
i = GetBattlerAbility(gBattlerAbility);
|
||
if (IsBattlerAlive(gBattlerAbility)
|
||
&& (i == ABILITY_RECEIVER || i == ABILITY_POWER_OF_ALCHEMY)
|
||
&& GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_ABILITY_SHIELD
|
||
&& !gAbilities[gBattleMons[battler].ability].cantBeCopied)
|
||
{
|
||
gBattleStruct->tracedAbility[gBattlerAbility] = gBattleMons[battler].ability; // re-using the variable for trace
|
||
gBattleScripting.battler = battler;
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_ReceiverActivates;
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_ACTIVATE_BEAST_BOOST:
|
||
{
|
||
VARIOUS_ARGS();
|
||
i = GetHighestStatId(battler);
|
||
if (GetBattlerAbility(battler) == ABILITY_BEAST_BOOST
|
||
&& HasAttackerFaintedTarget()
|
||
&& !NoAliveMonsForEitherParty()
|
||
&& CompareStat(gBattlerAttacker, i, MAX_STAT_STAGE, CMP_LESS_THAN))
|
||
{
|
||
SET_STATCHANGER(i, 1, FALSE);
|
||
PREPARE_STAT_BUFFER(gBattleTextBuff1, i);
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_AttackerAbilityStatRaise;
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_ACTIVATE_SOULHEART:
|
||
{
|
||
VARIOUS_ARGS();
|
||
while (gBattleStruct->soulheartBattlerId < gBattlersCount)
|
||
{
|
||
gBattleScripting.battler = gBattleStruct->soulheartBattlerId++;
|
||
if (GetBattlerAbility(gBattleScripting.battler) == ABILITY_SOUL_HEART
|
||
&& IsBattlerAlive(gBattleScripting.battler)
|
||
&& !NoAliveMonsForEitherParty()
|
||
&& CompareStat(gBattleScripting.battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))
|
||
{
|
||
SET_STATCHANGER(STAT_SPATK, 1, FALSE);
|
||
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPATK);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_ScriptingAbilityStatRaise;
|
||
return;
|
||
}
|
||
}
|
||
gBattleStruct->soulheartBattlerId = 0;
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_ACTIVATE_FELL_STINGER:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (gBattleMoves[gCurrentMove].effect == EFFECT_FELL_STINGER
|
||
&& HasAttackerFaintedTarget()
|
||
&& !NoAliveMonsForEitherParty()
|
||
&& CompareStat(gBattlerAttacker, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
|
||
{
|
||
SET_STATCHANGER(STAT_ATK, (B_FELL_STINGER_STAT_RAISE >= GEN_7 ? 3 : 2), FALSE);
|
||
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK);
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_FellStingerRaisesStat;
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_PLAY_MOVE_ANIMATION:
|
||
{
|
||
VARIOUS_ARGS(u16 move);
|
||
BtlController_EmitMoveAnimation(battler, BUFFER_A, cmd->move, gBattleScripting.animTurn, 0, 0, gBattleMons[battler].friendship, &gDisableStructs[battler], gMultiHitCounter);
|
||
MarkBattlerForControllerExec(battler);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_SET_LUCKY_CHANT:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (!(gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_LUCKY_CHANT))
|
||
{
|
||
gSideStatuses[GetBattlerSide(battler)] |= SIDE_STATUS_LUCKY_CHANT;
|
||
gSideTimers[GetBattlerSide(battler)].luckyChantBattlerId = battler;
|
||
gSideTimers[GetBattlerSide(battler)].luckyChantTimer = 5;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_SUCKER_PUNCH_CHECK:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (gProtectStructs[gBattlerTarget].obstructed)
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
else if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
else if (IS_MOVE_STATUS(gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]]))
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_SET_SIMPLE_BEAM:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (gAbilities[gBattleMons[gBattlerTarget].ability].cantBeOverwritten
|
||
|| gBattleMons[gBattlerTarget].ability == ABILITY_SIMPLE)
|
||
{
|
||
RecordAbilityBattle(gBattlerTarget, gBattleMons[gBattlerTarget].ability);
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else if (GetBattlerHoldEffect(gBattlerTarget, TRUE) == HOLD_EFFECT_ABILITY_SHIELD)
|
||
{
|
||
RecordItemEffectBattle(gBattlerTarget, HOLD_EFFECT_ABILITY_SHIELD);
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
if (gBattleMons[gBattlerTarget].ability == ABILITY_NEUTRALIZING_GAS)
|
||
gSpecialStatuses[gBattlerTarget].neutralizingGasRemoved = TRUE;
|
||
|
||
gBattleMons[gBattlerTarget].ability = gBattleStruct->overwrittenAbilities[gBattlerTarget] = ABILITY_SIMPLE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_ENTRAINMENT:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (gAbilities[gBattleMons[gBattlerAttacker].ability].cantBeCopied
|
||
|| gAbilities[gBattleMons[gBattlerTarget].ability].cantBeOverwritten)
|
||
{
|
||
RecordAbilityBattle(gBattlerTarget, gBattleMons[gBattlerTarget].ability);
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else if (GetBattlerHoldEffect(gBattlerTarget, TRUE) == HOLD_EFFECT_ABILITY_SHIELD)
|
||
{
|
||
RecordItemEffectBattle(gBattlerTarget, HOLD_EFFECT_ABILITY_SHIELD);
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
if (gBattleMons[gBattlerTarget].ability == gBattleMons[gBattlerAttacker].ability
|
||
|| IsDynamaxed(gBattlerTarget))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattleMons[gBattlerTarget].ability = gBattleStruct->overwrittenAbilities[gBattlerTarget] = gBattleMons[gBattlerAttacker].ability;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_SET_LAST_USED_ABILITY:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gLastUsedAbility = gBattleMons[battler].ability;
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_QUASH:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)) // It's true if foe is faster, has a bigger priority, or switches
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr; // This replaces the current battlescript with the "fail" script.
|
||
}
|
||
else // If the condition is not true, it means we are faster than the foe, so we can set the quash bit
|
||
{
|
||
gProtectStructs[gBattlerTarget].quash = TRUE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr; // and then we proceed with the rest of our battlescript
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_INVERT_STAT_STAGES:
|
||
{
|
||
VARIOUS_ARGS();
|
||
for (i = 0; i < NUM_BATTLE_STATS; i++)
|
||
{
|
||
if (gBattleMons[battler].statStages[i] < DEFAULT_STAT_STAGE) // Negative becomes positive.
|
||
gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE + (DEFAULT_STAT_STAGE - gBattleMons[battler].statStages[i]);
|
||
else if (gBattleMons[battler].statStages[i] > DEFAULT_STAT_STAGE) // Positive becomes negative.
|
||
gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE - (gBattleMons[battler].statStages[i] - DEFAULT_STAT_STAGE);
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_ME_FIRST:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
u16 move = gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]];
|
||
if (IS_MOVE_STATUS(move) || gBattleMoves[move].meFirstBanned
|
||
|| GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
else
|
||
{
|
||
gCalledMove = move;
|
||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
|
||
gStatuses3[gBattlerAttacker] |= STATUS3_ME_FIRST;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_BATTLE_END:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (NoAliveMonsForEitherParty())
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_ELECTRIFY:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gStatuses4[gBattlerTarget] |= STATUS4_ELECTRIFIED;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_SOAK:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (GetBattlerType(gBattlerTarget, 0) == gBattleMoves[gCurrentMove].type
|
||
&& GetBattlerType(gBattlerTarget, 1) == gBattleMoves[gCurrentMove].type)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
SET_BATTLER_TYPE(gBattlerTarget, gBattleMoves[gCurrentMove].type);
|
||
PREPARE_TYPE_BUFFER(gBattleTextBuff1, gBattleMoves[gCurrentMove].type);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_HANDLE_FORM_CHANGE:
|
||
{
|
||
VARIOUS_ARGS(u8 case_);
|
||
if (GetBattlerSide(battler) == B_SIDE_OPPONENT)
|
||
mon = &gEnemyParty[gBattlerPartyIndexes[battler]];
|
||
else
|
||
mon = &gPlayerParty[gBattlerPartyIndexes[battler]];
|
||
|
||
// Change species.
|
||
if (cmd->case_ == 0)
|
||
{
|
||
/* What was the idea here?
|
||
if (!gBattleTextBuff1)
|
||
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[battler].species);
|
||
*/
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_SPECIES_BATTLE, gBitTable[gBattlerPartyIndexes[battler]], sizeof(gBattleMons[battler].species), &gBattleMons[battler].species);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
// Change stats.
|
||
else if (cmd->case_ == 1)
|
||
{
|
||
RecalcBattlerStats(battler, mon);
|
||
}
|
||
// Update healthbox.
|
||
else
|
||
{
|
||
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL);
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_LAST_RESORT:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (CanUseLastResort(battler))
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_ARGUMENT_STATUS_EFFECT:
|
||
{
|
||
VARIOUS_ARGS();
|
||
switch (gBattleMoves[gCurrentMove].argument)
|
||
{
|
||
case STATUS1_SLEEP:
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_SLEEP;
|
||
break;
|
||
case STATUS1_BURN:
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_BURN;
|
||
break;
|
||
case STATUS1_FREEZE:
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_FREEZE;
|
||
break;
|
||
case STATUS1_PARALYSIS:
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_PARALYSIS;
|
||
break;
|
||
case STATUS1_POISON:
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_POISON;
|
||
break;
|
||
case STATUS1_TOXIC_POISON:
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_TOXIC;
|
||
break;
|
||
case STATUS1_FROSTBITE:
|
||
gBattleScripting.moveEffect = MOVE_EFFECT_FROSTBITE;
|
||
break;
|
||
default:
|
||
gBattleScripting.moveEffect = 0;
|
||
break;
|
||
}
|
||
if (gBattleScripting.moveEffect != 0)
|
||
{
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_EffectWithChance;
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_HIT_SWITCH_TARGET:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (IsBattlerAlive(gBattlerAttacker)
|
||
&& IsBattlerAlive(gBattlerTarget)
|
||
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
&& TARGET_TURN_DAMAGED
|
||
&& GetBattlerAbility(gBattlerTarget) != ABILITY_GUARD_DOG)
|
||
{
|
||
gBattleScripting.switchCase = B_SWITCH_HIT;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_AUTOTOMIZE:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (GetBattlerWeight(battler) > 1)
|
||
{
|
||
gDisableStructs[battler].autotomizeCount++;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_INSTRUCT:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
u16 move = gLastPrintedMoves[gBattlerTarget];
|
||
if (move == MOVE_NONE || move == MOVE_UNAVAILABLE || gBattleMoves[move].effect == EFFECT_RECHARGE
|
||
|| gBattleMoves[move].instructBanned || gBattleMoves[move].twoTurnMove || IsDynamaxed(gBattlerTarget))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gSpecialStatuses[gBattlerTarget].instructedChosenTarget = *(gBattleStruct->moveTarget + gBattlerTarget) | 0x4;
|
||
gBattlerAttacker = gBattlerTarget;
|
||
gCalledMove = move;
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gBattleMons[gBattlerAttacker].moves[i] == gCalledMove)
|
||
{
|
||
gCurrMovePos = i;
|
||
i = 4;
|
||
break;
|
||
}
|
||
}
|
||
if (i != 4 || gBattleMons[gBattlerAttacker].pp[gCurrMovePos] == 0)
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
else
|
||
{
|
||
gBattlerTarget = gBattleStruct->lastMoveTarget[gBattlerAttacker];
|
||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, battler, gBattlerPartyIndexes[battler]);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_ABILITY_POPUP:
|
||
{
|
||
VARIOUS_ARGS();
|
||
CreateAbilityPopUp(battler, gBattleMons[battler].ability, (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) != 0);
|
||
break;
|
||
}
|
||
case VARIOUS_UPDATE_ABILITY_POPUP:
|
||
{
|
||
VARIOUS_ARGS();
|
||
UpdateAbilityPopup(battler);
|
||
break;
|
||
}
|
||
case VARIOUS_JUMP_IF_TARGET_ALLY:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget))
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_SYNCHRONOISE:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (!DoBattlersShareType(gBattlerAttacker, gBattlerTarget))
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_LOSE_TYPE:
|
||
{
|
||
VARIOUS_ARGS(u8 type);
|
||
for (i = 0; i < 3; i++)
|
||
{
|
||
if (*(u8 *)(&gBattleMons[battler].type1 + i) == cmd->type)
|
||
*(u8 *)(&gBattleMons[battler].type1 + i) = TYPE_MYSTERY;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_PSYCHO_SHIFT:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
// Psycho shift works
|
||
if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget))
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
|
||
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget))
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
|
||
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerTarget))
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 2;
|
||
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerTarget))
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 3;
|
||
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanSleep(gBattlerTarget))
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 4;
|
||
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_FROSTBITE) && CanBeFrozen(gBattlerTarget))
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 5;
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
return;
|
||
}
|
||
gBattleMons[gBattlerTarget].status1 = gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY;
|
||
battler = gBattlerTarget;
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[battler].status1), &gBattleMons[battler].status1);
|
||
MarkBattlerForControllerExec(battler);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_CURE_STATUS:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleMons[battler].status1 = 0;
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[battler].status1), &gBattleMons[battler].status1);
|
||
MarkBattlerForControllerExec(battler);
|
||
break;
|
||
}
|
||
case VARIOUS_POWER_TRICK:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gStatuses3[battler] ^= STATUS3_POWER_TRICK;
|
||
SWAP(gBattleMons[battler].attack, gBattleMons[battler].defense, i);
|
||
break;
|
||
}
|
||
case VARIOUS_AFTER_YOU:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (ChangeOrderTargetAfterAttacker())
|
||
{
|
||
gSpecialStatuses[gBattlerTarget].afterYou = 1;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_BESTOW:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (gBattleMons[gBattlerAttacker].item == ITEM_NONE
|
||
|| gBattleMons[gBattlerTarget].item != ITEM_NONE
|
||
|| !CanBattlerGetOrLoseItem(gBattlerAttacker, gBattleMons[gBattlerAttacker].item)
|
||
|| !CanBattlerGetOrLoseItem(gBattlerTarget, gBattleMons[gBattlerAttacker].item)
|
||
|| gWishFutureKnock.knockedOffMons[GetBattlerSide(gBattlerTarget)] & gBitTable[gBattlerPartyIndexes[gBattlerTarget]])
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
BestowItem(gBattlerAttacker, gBattlerTarget);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_ARGUMENT_TO_MOVE_EFFECT:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleScripting.moveEffect = gBattleMoves[gCurrentMove].argument;
|
||
break;
|
||
}
|
||
case VARIOUS_JUMP_IF_NOT_GROUNDED:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (!IsBattlerGrounded(battler))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_HANDLE_TRAINER_SLIDE_MSG:
|
||
{
|
||
VARIOUS_ARGS(u8 case_);
|
||
if (cmd->case_ == 0)
|
||
{
|
||
// Save sprite IDs, because trainer slide in will overwrite gBattlerSpriteIds variable.
|
||
gBattleScripting.savedDmg = (gBattlerSpriteIds[battler] & 0xFF) | (gBattlerSpriteIds[BATTLE_PARTNER(battler)] << 8);
|
||
HideBattlerShadowSprite(battler);
|
||
}
|
||
else if (cmd->case_ == 1)
|
||
{
|
||
BtlController_EmitPrintString(battler, BUFFER_A, STRINGID_TRAINERSLIDE);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
else
|
||
{
|
||
gBattlerSpriteIds[BATTLE_PARTNER(battler)] = gBattleScripting.savedDmg >> 8;
|
||
gBattlerSpriteIds[battler] = gBattleScripting.savedDmg & 0xFF;
|
||
if (IsBattlerAlive(battler))
|
||
{
|
||
SetBattlerShadowSpriteCallback(battler, gBattleMons[battler].species);
|
||
BattleLoadMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[battler]], battler);
|
||
}
|
||
i = BATTLE_PARTNER(battler);
|
||
if (IsBattlerAlive(i))
|
||
{
|
||
SetBattlerShadowSpriteCallback(i, gBattleMons[i].species);
|
||
BattleLoadMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[i]], i);
|
||
}
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_TRAINER_SLIDE_MSG_FIRST_OFF:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if ((i = ShouldDoTrainerSlide(battler, TRAINER_SLIDE_FIRST_DOWN)))
|
||
{
|
||
gBattleScripting.battler = battler;
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = (i == 1 ? BattleScript_TrainerASlideMsgRet : BattleScript_TrainerBSlideMsgRet);
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_TRAINER_SLIDE_MSG_LAST_ON:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if ((i = ShouldDoTrainerSlide(battler, TRAINER_SLIDE_LAST_SWITCHIN)))
|
||
{
|
||
gBattleScripting.battler = battler;
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = (i == 1 ? BattleScript_TrainerASlideMsgRet : BattleScript_TrainerBSlideMsgRet);
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_SET_AURORA_VEIL:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_AURORA_VEIL
|
||
|| !(WEATHER_HAS_EFFECT && gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
|
||
}
|
||
else
|
||
{
|
||
gSideStatuses[GetBattlerSide(battler)] |= SIDE_STATUS_AURORA_VEIL;
|
||
if (GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_LIGHT_CLAY)
|
||
gSideTimers[GetBattlerSide(battler)].auroraVeilTimer = 8;
|
||
else
|
||
gSideTimers[GetBattlerSide(battler)].auroraVeilTimer = 5;
|
||
gSideTimers[GetBattlerSide(battler)].auroraVeilBattlerId = battler;
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, gBattlerAttacker) == 2)
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 5;
|
||
else
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 5;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_THIRD_TYPE:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (IS_BATTLER_OF_TYPE(battler, gBattleMoves[gCurrentMove].argument))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattleMons[battler].type3 = gBattleMoves[gCurrentMove].argument;
|
||
PREPARE_TYPE_BUFFER(gBattleTextBuff1, gBattleMoves[gCurrentMove].argument);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_DESTROY_ABILITY_POPUP:
|
||
{
|
||
VARIOUS_ARGS();
|
||
DestroyAbilityPopUp(battler);
|
||
break;
|
||
}
|
||
case VARIOUS_TOTEM_BOOST:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
battler = gBattlerAttacker;
|
||
if (gQueuedStatBoosts[battler].stats == 0)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr; // stats done, exit
|
||
}
|
||
else
|
||
{
|
||
for (i = 0; i < (NUM_BATTLE_STATS - 1); i++)
|
||
{
|
||
if (gQueuedStatBoosts[battler].stats & (1 << i))
|
||
{
|
||
if (gQueuedStatBoosts[battler].statChanges[i] <= -1)
|
||
SET_STATCHANGER(i + 1, abs(gQueuedStatBoosts[battler].statChanges[i]), TRUE);
|
||
else
|
||
SET_STATCHANGER(i + 1, gQueuedStatBoosts[battler].statChanges[i], FALSE);
|
||
|
||
gQueuedStatBoosts[battler].stats &= ~(1 << i);
|
||
gBattleScripting.battler = battler;
|
||
gBattlerTarget = battler;
|
||
if (gQueuedStatBoosts[battler].stats & 0x80)
|
||
{
|
||
gQueuedStatBoosts[battler].stats &= ~0x80; // set 'aura flared to life' flag
|
||
gBattlescriptCurrInstr = BattleScript_TotemFlaredToLife;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->jumpInstr; // do boost
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr; // exit if loop failed (failsafe)
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_MOVEEND_ITEM_EFFECTS:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler, FALSE))
|
||
return;
|
||
break;
|
||
}
|
||
case VARIOUS_ROOM_SERVICE:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_ROOM_SERVICE && TryRoomService(battler))
|
||
{
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_BerryStatRaiseRet;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_TERRAIN_SEED:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_SEEDS)
|
||
{
|
||
u8 effect = 0;
|
||
u16 item = gBattleMons[battler].item;
|
||
switch (GetBattlerHoldEffectParam(battler))
|
||
{
|
||
case HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN:
|
||
effect = TryHandleSeed(battler, STATUS_FIELD_ELECTRIC_TERRAIN, STAT_DEF, item, FALSE);
|
||
break;
|
||
case HOLD_EFFECT_PARAM_GRASSY_TERRAIN:
|
||
effect = TryHandleSeed(battler, STATUS_FIELD_GRASSY_TERRAIN, STAT_DEF, item, FALSE);
|
||
break;
|
||
case HOLD_EFFECT_PARAM_MISTY_TERRAIN:
|
||
effect = TryHandleSeed(battler, STATUS_FIELD_MISTY_TERRAIN, STAT_SPDEF, item, FALSE);
|
||
break;
|
||
case HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN:
|
||
effect = TryHandleSeed(battler, STATUS_FIELD_PSYCHIC_TERRAIN, STAT_SPDEF, item, FALSE);
|
||
break;
|
||
}
|
||
|
||
if (effect)
|
||
return;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_MAKE_INVISIBLE:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (gBattleControllerExecFlags)
|
||
break;
|
||
|
||
BtlController_EmitSpriteInvisibility(battler, BUFFER_A, TRUE);
|
||
MarkBattlerForControllerExec(battler);
|
||
break;
|
||
}
|
||
case VARIOUS_EERIE_SPELL_PP_REDUCE:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (gLastMoves[battler] != 0 && gLastMoves[battler] != 0xFFFF)
|
||
{
|
||
s32 i;
|
||
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gLastMoves[battler] == gBattleMons[battler].moves[i])
|
||
break;
|
||
}
|
||
|
||
if (i != MAX_MON_MOVES && gBattleMons[battler].pp[i] != 0)
|
||
{
|
||
s32 ppToDeduct = 3;
|
||
|
||
if (gBattleMons[battler].pp[i] < ppToDeduct)
|
||
ppToDeduct = gBattleMons[battler].pp[i];
|
||
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastMoves[battler])
|
||
ConvertIntToDecimalStringN(gBattleTextBuff2, ppToDeduct, STR_CONV_MODE_LEFT_ALIGN, 1);
|
||
PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff2, 1, ppToDeduct)
|
||
gBattleMons[battler].pp[i] -= ppToDeduct;
|
||
if (!(gDisableStructs[battler].mimickedMoves & gBitTable[i])
|
||
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
|
||
{
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_PPMOVE1_BATTLE + i, 0, sizeof(gBattleMons[battler].pp[i]), &gBattleMons[battler].pp[i]);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
|
||
if (gBattleMons[battler].pp[i] == 0 && gBattleStruct->skyDropTargets[battler] == 0xFF)
|
||
CancelMultiTurnMoves(battler);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr; // continue
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr; // cant reduce pp
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr; // cant reduce pp
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_TEAM_HEALTHY:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && IsBattlerAlive(BATTLE_PARTNER(battler)))
|
||
{
|
||
u8 partner = BATTLE_PARTNER(battler);
|
||
if ((gBattleMons[battler].hp == gBattleMons[battler].maxHP && !(gBattleMons[battler].status1 & STATUS1_ANY))
|
||
&& (gBattleMons[partner].hp == gBattleMons[partner].maxHP && !(gBattleMons[partner].status1 & STATUS1_ANY)))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else // single battle
|
||
{
|
||
if (gBattleMons[battler].hp == gBattleMons[battler].maxHP && !(gBattleMons[battler].status1 & STATUS1_ANY))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_HEAL_QUARTER_HP:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 4;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
gBattleMoveDamage *= -1;
|
||
|
||
if (gBattleMons[battler].hp == gBattleMons[battler].maxHP)
|
||
gBattlescriptCurrInstr = cmd->failInstr; // fail
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr; // can heal
|
||
return;
|
||
}
|
||
case VARIOUS_REMOVE_TERRAIN:
|
||
{
|
||
VARIOUS_ARGS();
|
||
RemoveAllTerrains();
|
||
break;
|
||
}
|
||
case VARIOUS_JUMP_IF_UNDER_200:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
// If the Pokemon is less than 200 kg, or weighing less than 441 lbs, then Sky Drop will work. Otherwise, it will fail.
|
||
if (GetBattlerWeight(gBattlerTarget) < 2000)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_SET_SKY_DROP:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gStatuses3[gBattlerTarget] |= (STATUS3_SKY_DROPPED | STATUS3_ON_AIR);
|
||
/* skyDropTargets holds the information of who is in a particular instance of Sky Drop.
|
||
This is needed in the case that multiple Pokemon use Sky Drop in the same turn or if
|
||
the target of a Sky Drop faints while in the air.*/
|
||
gBattleStruct->skyDropTargets[gBattlerAttacker] = gBattlerTarget;
|
||
gBattleStruct->skyDropTargets[gBattlerTarget] = gBattlerAttacker;
|
||
|
||
// End any multiturn effects caused by the target except STATUS2_LOCK_CONFUSE
|
||
gBattleMons[gBattlerTarget].status2 &= ~(STATUS2_MULTIPLETURNS);
|
||
gBattleMons[gBattlerTarget].status2 &= ~(STATUS2_UPROAR);
|
||
gBattleMons[gBattlerTarget].status2 &= ~(STATUS2_BIDE);
|
||
gDisableStructs[gBattlerTarget].rolloutTimer = 0;
|
||
gDisableStructs[gBattlerTarget].furyCutterCounter = 0;
|
||
|
||
// End any Follow Me/Rage Powder effects caused by the target
|
||
if (gSideTimers[GetBattlerSide(gBattlerTarget)].followmeTimer != 0 && gSideTimers[GetBattlerSide(gBattlerTarget)].followmeTarget == gBattlerTarget)
|
||
gSideTimers[GetBattlerSide(gBattlerTarget)].followmeTimer = 0;
|
||
|
||
break;
|
||
}
|
||
case VARIOUS_CLEAR_SKY_DROP:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
// Check to see if the initial target of this Sky Drop fainted before the 2nd turn of Sky Drop.
|
||
// If so, make the move fail. If not, clear all of the statuses and continue the move.
|
||
if (gBattleStruct->skyDropTargets[gBattlerAttacker] == 0xFF)
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
else
|
||
{
|
||
gBattleStruct->skyDropTargets[gBattlerAttacker] = 0xFF;
|
||
gBattleStruct->skyDropTargets[gBattlerTarget] = 0xFF;
|
||
gStatuses3[gBattlerTarget] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
// Confuse target if they were in the middle of Petal Dance/Outrage/Thrash when targeted.
|
||
if (gBattleMons[gBattlerTarget].status2 & STATUS2_LOCK_CONFUSE)
|
||
gBattleScripting.moveEffect = (MOVE_EFFECT_CONFUSION | MOVE_EFFECT_CERTAIN);
|
||
return;
|
||
}
|
||
case VARIOUS_SKY_DROP_YAWN: // If the mon that's sleeping due to Yawn was holding a Pokemon in Sky Drop, release the target and clear Sky Drop data.
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (gBattleStruct->skyDropTargets[gEffectBattler] != 0xFF && !(gStatuses3[gEffectBattler] & STATUS3_SKY_DROPPED))
|
||
{
|
||
// Set the target of Sky Drop as gEffectBattler
|
||
gEffectBattler = gBattleStruct->skyDropTargets[gEffectBattler];
|
||
|
||
// Clear skyDropTargets data
|
||
gBattleStruct->skyDropTargets[gBattleStruct->skyDropTargets[gEffectBattler]] = 0xFF;
|
||
gBattleStruct->skyDropTargets[gEffectBattler] = 0xFF;
|
||
|
||
// If the target was in the middle of Outrage/Thrash/etc. when targeted by Sky Drop, confuse them on release and do proper animation
|
||
if (gBattleMons[gEffectBattler].status2 & STATUS2_LOCK_CONFUSE && CanBeConfused(gEffectBattler))
|
||
{
|
||
gBattleMons[gEffectBattler].status2 &= ~(STATUS2_LOCK_CONFUSE);
|
||
gBattlerAttacker = gEffectBattler;
|
||
gBattleMons[gBattlerTarget].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2);
|
||
gBattlescriptCurrInstr = BattleScript_ThrashConfuses;
|
||
return;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_JUMP_IF_PRANKSTER_BLOCKED:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (BlocksPrankster(gCurrentMove, gBattlerAttacker, battler, TRUE))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_TO_CLEAR_PRIMAL_WEATHER:
|
||
{
|
||
bool8 shouldNotClear = FALSE;
|
||
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
u32 ability = GetBattlerAbility(i);
|
||
if (((ability == ABILITY_DESOLATE_LAND && gBattleWeather & B_WEATHER_SUN_PRIMAL)
|
||
|| (ability == ABILITY_PRIMORDIAL_SEA && gBattleWeather & B_WEATHER_RAIN_PRIMAL)
|
||
|| (ability == ABILITY_DELTA_STREAM && gBattleWeather & B_WEATHER_STRONG_WINDS))
|
||
&& IsBattlerAlive(i))
|
||
shouldNotClear = TRUE;
|
||
}
|
||
if (gBattleWeather & B_WEATHER_SUN_PRIMAL && !shouldNotClear)
|
||
{
|
||
gBattleWeather &= ~B_WEATHER_SUN_PRIMAL;
|
||
PrepareStringBattle(STRINGID_EXTREMESUNLIGHTFADED, battler);
|
||
gBattleCommunication[MSG_DISPLAY] = 1;
|
||
}
|
||
else if (gBattleWeather & B_WEATHER_RAIN_PRIMAL && !shouldNotClear)
|
||
{
|
||
gBattleWeather &= ~B_WEATHER_RAIN_PRIMAL;
|
||
PrepareStringBattle(STRINGID_HEAVYRAINLIFTED, battler);
|
||
gBattleCommunication[MSG_DISPLAY] = 1;
|
||
}
|
||
else if (gBattleWeather & B_WEATHER_STRONG_WINDS && !shouldNotClear)
|
||
{
|
||
gBattleWeather &= ~B_WEATHER_STRONG_WINDS;
|
||
PrepareStringBattle(STRINGID_STRONGWINDSDISSIPATED, battler);
|
||
gBattleCommunication[MSG_DISPLAY] = 1;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_TRY_END_NEUTRALIZING_GAS:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (gSpecialStatuses[battler].neutralizingGasRemoved)
|
||
{
|
||
gSpecialStatuses[battler].neutralizingGasRemoved = FALSE;
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits;
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_GET_ROTOTILLER_TARGETS:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
// Gets the battlers to be affected by rototiller. If there are none, print 'But it failed!'
|
||
{
|
||
u32 count = 0;
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
gSpecialStatuses[i].rototillerAffected = FALSE;
|
||
if (IsRototillerAffected(i))
|
||
{
|
||
gSpecialStatuses[i].rototillerAffected = TRUE;
|
||
count++;
|
||
}
|
||
}
|
||
|
||
if (count == 0)
|
||
gBattlescriptCurrInstr = cmd->failInstr; // Rototiller fails
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_NOT_ROTOTILLER_AFFECTED:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (gSpecialStatuses[battler].rototillerAffected)
|
||
{
|
||
gSpecialStatuses[battler].rototillerAffected = FALSE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->jumpInstr; // Unaffected by rototiller - print STRINGID_NOEFFECTONTARGET
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_ACTIVATE_BATTLE_BOND:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (gBattleMons[gBattlerAttacker].species == SPECIES_GRENINJA_BATTLE_BOND
|
||
&& HasAttackerFaintedTarget()
|
||
&& CalculateBattlerPartyCount(gBattlerTarget) > 1
|
||
&& !(gBattleStruct->battleBondTransformed[GetBattlerSide(gBattlerAttacker)] & gBitTable[gBattlerPartyIndexes[gBattlerAttacker]]))
|
||
{
|
||
gBattleStruct->battleBondTransformed[GetBattlerSide(gBattlerAttacker)] |= gBitTable[gBattlerPartyIndexes[gBattlerAttacker]];
|
||
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].species);
|
||
gBattleStruct->changedSpecies[GetBattlerSide(gBattlerAttacker)][gBattlerPartyIndexes[gBattlerAttacker]] = gBattleMons[gBattlerAttacker].species;
|
||
gBattleMons[gBattlerAttacker].species = SPECIES_GRENINJA_ASH;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_BattleBondActivatesOnMoveEndAttacker;
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
case VARIOUS_CONSUME_BERRY:
|
||
{
|
||
VARIOUS_ARGS(bool8 fromBattler);
|
||
if (gBattleScripting.overrideBerryRequirements == 2)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
|
||
if (cmd->fromBattler)
|
||
gLastUsedItem = gBattleMons[battler].item;
|
||
|
||
gBattleScripting.battler = gEffectBattler = gBattlerTarget = battler; // Cover all berry effect battler cases. e.g. ChangeStatBuffs uses target ID
|
||
if (ItemBattleEffects(ITEMEFFECT_USE_LAST_ITEM, battler, FALSE))
|
||
return;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_CANT_REVERT_TO_PRIMAL:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_PRIMAL_REVERSION) == SPECIES_NONE)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_WEATHER_AFFECTED:
|
||
{
|
||
VARIOUS_ARGS(u32 flags, const u8 *jumpInstr);
|
||
u32 flags = cmd->flags;
|
||
if (IsBattlerWeatherAffected(battler, flags))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_SPECIES:
|
||
{
|
||
VARIOUS_ARGS(u16 species, const u8 *jumpInstr);
|
||
if (gBattleMons[battler].species == cmd->species)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_SHELL_SIDE_ARM_CHECK: // 0% chance GameFreak actually checks this way according to DaWobblefet, but this is the only functional explanation at the moment
|
||
{
|
||
VARIOUS_ARGS();
|
||
|
||
u32 attackerAtkStat = gBattleMons[gBattlerAttacker].attack;
|
||
u32 targetDefStat = gBattleMons[gBattlerTarget].defense;
|
||
u32 attackerSpAtkStat = gBattleMons[gBattlerAttacker].spAttack;
|
||
u32 targetSpDefStat = gBattleMons[gBattlerTarget].spDefense;
|
||
u8 statStage;
|
||
u32 physical;
|
||
u32 special;
|
||
|
||
gBattleStruct->swapDamageCategory = FALSE;
|
||
|
||
statStage = gBattleMons[gBattlerAttacker].statStages[STAT_ATK];
|
||
attackerAtkStat *= gStatStageRatios[statStage][0];
|
||
attackerAtkStat /= gStatStageRatios[statStage][1];
|
||
|
||
statStage = gBattleMons[gBattlerTarget].statStages[STAT_DEF];
|
||
targetDefStat *= gStatStageRatios[statStage][0];
|
||
targetDefStat /= gStatStageRatios[statStage][1];
|
||
|
||
physical = ((((2 * gBattleMons[gBattlerAttacker].level / 5 + 2) * gBattleMoves[gCurrentMove].power * attackerAtkStat) / targetDefStat) / 50);
|
||
|
||
statStage = gBattleMons[gBattlerAttacker].statStages[STAT_SPATK];
|
||
attackerSpAtkStat *= gStatStageRatios[statStage][0];
|
||
attackerSpAtkStat /= gStatStageRatios[statStage][1];
|
||
|
||
statStage = gBattleMons[gBattlerTarget].statStages[STAT_SPDEF];
|
||
targetSpDefStat *= gStatStageRatios[statStage][0];
|
||
targetSpDefStat /= gStatStageRatios[statStage][1];
|
||
|
||
special = ((((2 * gBattleMons[gBattlerAttacker].level / 5 + 2) * gBattleMoves[gCurrentMove].power * attackerSpAtkStat) / targetSpDefStat) / 50);
|
||
|
||
if (((physical > special) || (physical == special && (Random() % 2) == 0)))
|
||
gBattleStruct->swapDamageCategory = TRUE;
|
||
break;
|
||
}
|
||
case VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (IsLeafGuardProtected(battler))
|
||
{
|
||
gBattlerAbility = battler;
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_SET_ATTACKER_STICKY_WEB_USER:
|
||
{
|
||
VARIOUS_ARGS();
|
||
// For Mirror Armor: "If the Pokémon with this Ability is affected by Sticky Web, the effect is reflected back to the Pokémon which set it up.
|
||
// If Pokémon which set up Sticky Web is not on the field, no Pokémon have their Speed lowered."
|
||
gBattlerAttacker = gBattlerTarget; // Initialize 'fail' condition
|
||
SET_STATCHANGER(STAT_SPEED, 1, TRUE);
|
||
if (gSideTimers[GetBattlerSide(battler)].stickyWebBattlerId != 0xFF)
|
||
gBattlerAttacker = gSideTimers[GetBattlerSide(battler)].stickyWebBattlerId;
|
||
break;
|
||
}
|
||
case VARIOUS_CUT_1_3_HP_RAISE_STATS:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
|
||
bool8 atLeastOneStatBoosted = FALSE;
|
||
u16 hpFraction = max(1, GetNonDynamaxMaxHP(gBattlerAttacker) / 3);
|
||
|
||
for (i = 1; i < NUM_STATS; i++)
|
||
{
|
||
if (CompareStat(gBattlerAttacker, i, MAX_STAT_STAGE, CMP_LESS_THAN))
|
||
{
|
||
atLeastOneStatBoosted = TRUE;
|
||
break;
|
||
}
|
||
}
|
||
if (atLeastOneStatBoosted && gBattleMons[gBattlerAttacker].hp > hpFraction)
|
||
{
|
||
gBattleMoveDamage = hpFraction;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_CHECK_POLTERGEIST:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (gBattleMons[battler].item == ITEM_NONE
|
||
|| gFieldStatuses & STATUS_FIELD_MAGIC_ROOM
|
||
|| GetBattlerAbility(battler) == ABILITY_KLUTZ)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
PREPARE_ITEM_BUFFER(gBattleTextBuff1, gBattleMons[battler].item);
|
||
gLastUsedItem = gBattleMons[battler].item;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_NO_RETREAT:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (gDisableStructs[battler].noRetreat)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
if (!(gBattleMons[battler].status2 & STATUS2_ESCAPE_PREVENTION))
|
||
gDisableStructs[battler].noRetreat = TRUE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_TAR_SHOT:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
if (gDisableStructs[battler].tarShot)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gDisableStructs[battler].tarShot = TRUE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_CAN_TAR_SHOT_WORK:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
// Tar Shot will fail if it's already been used on the target and its speed can't be lowered further
|
||
if (!gDisableStructs[battler].tarShot
|
||
&& CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_CURE_CERTAIN_STATUSES:
|
||
{
|
||
VARIOUS_ARGS();
|
||
// Check infatuation
|
||
if (gBattleMons[battler].status2 & STATUS2_INFATUATION)
|
||
{
|
||
gBattleMons[battler].status2 &= ~(STATUS2_INFATUATION);
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_INFATUATION; // STRINGID_TARGETGOTOVERINFATUATION
|
||
StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn);
|
||
}
|
||
// Check taunt
|
||
if (gDisableStructs[battler].tauntTimer != 0)
|
||
{
|
||
gDisableStructs[battler].tauntTimer = 0;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_TAUNT;
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_TAUNT);
|
||
}
|
||
// Check encore
|
||
if (gDisableStructs[battler].encoreTimer != 0)
|
||
{
|
||
gDisableStructs[battler].encoredMove = 0;
|
||
gDisableStructs[battler].encoreTimer = 0;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_ENCORE; // STRINGID_PKMNENCOREENDED
|
||
}
|
||
// Check torment
|
||
if (gBattleMons[battler].status2 & STATUS2_TORMENT)
|
||
{
|
||
gBattleMons[battler].status2 &= ~(STATUS2_TORMENT);
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_TORMENT;
|
||
}
|
||
// Check heal block
|
||
if (gStatuses3[battler] & STATUS3_HEAL_BLOCK)
|
||
{
|
||
gStatuses3[battler] &= ~(STATUS3_HEAL_BLOCK);
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_HEALBLOCK;
|
||
}
|
||
// Check disable
|
||
if (gDisableStructs[battler].disableTimer != 0)
|
||
{
|
||
gDisableStructs[battler].disableTimer = 0;
|
||
gDisableStructs[battler].disabledMove = 0;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_DISABLE;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES:
|
||
{
|
||
VARIOUS_ARGS();
|
||
battler = gBattlerTarget;
|
||
for (i = 0; i < NUM_BATTLE_STATS; i++)
|
||
if (gBattleMons[battler].statStages[i] < DEFAULT_STAT_STAGE)
|
||
gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (ItemId_GetPocket(gLastUsedItem) == POCKET_BERRIES)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT:
|
||
{
|
||
VARIOUS_ARGS(u8 holdEffect, const u8 *jumpInstr);
|
||
if (ItemId_GetHoldEffect(gLastUsedItem) == cmd->holdEffect)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_SAVE_BATTLER_ITEM:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleResources->battleHistory->heldItems[battler] = gBattleMons[battler].item;
|
||
break;
|
||
}
|
||
case VARIOUS_RESTORE_BATTLER_ITEM:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleMons[battler].item = gBattleResources->battleHistory->heldItems[battler];
|
||
break;
|
||
}
|
||
case VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleMons[battler].item = gLastUsedItem;
|
||
break;
|
||
}
|
||
case VARIOUS_SET_BEAK_BLAST:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gProtectStructs[battler].beakBlastCharge = TRUE;
|
||
break;
|
||
}
|
||
case VARIOUS_SWAP_SIDE_STATUSES:
|
||
{
|
||
VARIOUS_ARGS();
|
||
CourtChangeSwapSideStatuses();
|
||
break;
|
||
}
|
||
case VARIOUS_SWAP_STATS:
|
||
{
|
||
VARIOUS_ARGS(u8 stat);
|
||
|
||
u8 stat = cmd->stat;
|
||
u16 temp;
|
||
|
||
switch (stat)
|
||
{
|
||
case STAT_HP:
|
||
SWAP(gBattleMons[gBattlerAttacker].hp, gBattleMons[gBattlerTarget].hp, temp);
|
||
break;
|
||
case STAT_ATK:
|
||
SWAP(gBattleMons[gBattlerAttacker].attack, gBattleMons[gBattlerTarget].attack, temp);
|
||
break;
|
||
case STAT_DEF:
|
||
SWAP(gBattleMons[gBattlerAttacker].defense, gBattleMons[gBattlerTarget].defense, temp);
|
||
break;
|
||
case STAT_SPEED:
|
||
SWAP(gBattleMons[gBattlerAttacker].speed, gBattleMons[gBattlerTarget].speed, temp);
|
||
break;
|
||
case STAT_SPATK:
|
||
SWAP(gBattleMons[gBattlerAttacker].spAttack, gBattleMons[gBattlerTarget].spAttack, temp);
|
||
break;
|
||
case STAT_SPDEF:
|
||
SWAP(gBattleMons[gBattlerAttacker].spDefense, gBattleMons[gBattlerTarget].spDefense, temp);
|
||
break;
|
||
}
|
||
PREPARE_STAT_BUFFER(gBattleTextBuff1, stat);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_TEATIME_TARGETS:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
u32 count = 0;
|
||
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (IsTeatimeAffected(i))
|
||
count++;
|
||
}
|
||
if (count == 0)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr; // Teatime fails
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_TEATIME_INVUL:
|
||
{
|
||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||
if (ItemId_GetPocket(gBattleMons[battler].item) == POCKET_BERRIES && !(gStatuses3[gBattlerTarget] & (STATUS3_SEMI_INVULNERABLE)))
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_WIND_RIDER_POWER:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
u16 ability = GetBattlerAbility(battler);
|
||
if (GetBattlerSide(battler) == GetBattlerSide(gBattlerAttacker)
|
||
&& (ability == ABILITY_WIND_RIDER || ability == ABILITY_WIND_POWER))
|
||
{
|
||
gLastUsedAbility = ability;
|
||
RecordAbilityBattle(battler, gLastUsedAbility);
|
||
gBattlerAbility = gBattleScripting.battler = battler;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
return;
|
||
}
|
||
case VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0);
|
||
return;
|
||
}
|
||
case VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0);
|
||
return;
|
||
}
|
||
case VARIOUS_STORE_HEALING_WISH:
|
||
{
|
||
VARIOUS_ARGS();
|
||
if (gCurrentMove == MOVE_LUNAR_DANCE)
|
||
gBattleStruct->storedLunarDance |= gBitTable[battler];
|
||
else
|
||
gBattleStruct->storedHealingWish |= gBitTable[battler];
|
||
break;
|
||
}
|
||
case VARIOUS_HIT_SWITCH_TARGET_FAILED:
|
||
{
|
||
VARIOUS_ARGS();
|
||
gBattleStruct->hitSwitchTargetFailed = TRUE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
case VARIOUS_TRY_REVIVAL_BLESSING:
|
||
{
|
||
VARIOUS_ARGS(const u8 *failInstr);
|
||
u32 side = GetBattlerSide(gBattlerAttacker);
|
||
u8 index = GetFirstFaintedPartyIndex(gBattlerAttacker);
|
||
|
||
// Move fails if there are no battlers to revive.
|
||
if (index == PARTY_SIZE)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
return;
|
||
}
|
||
|
||
// Battler selected! Revive and go to next instruction.
|
||
if (gSelectedMonPartyId != PARTY_SIZE)
|
||
{
|
||
struct Pokemon *party = GetSideParty(side);
|
||
|
||
u16 hp = GetMonData(&party[gSelectedMonPartyId], MON_DATA_MAX_HP) / 2;
|
||
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_HP_BATTLE, gBitTable[gSelectedMonPartyId], sizeof(hp), &hp);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gSelectedMonPartyId], MON_DATA_SPECIES));
|
||
|
||
// If an on-field battler is revived, it needs to be sent out again.
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE &&
|
||
gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)] == gSelectedMonPartyId)
|
||
{
|
||
gBattleScripting.battler = BATTLE_PARTNER(gBattlerAttacker);
|
||
gBattleCommunication[MULTIUSE_STATE] = TRUE;
|
||
}
|
||
|
||
gSelectedMonPartyId = PARTY_SIZE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
|
||
// Open party menu, wait to go to next instruction.
|
||
else
|
||
{
|
||
BtlController_EmitChoosePokemon(gBattlerAttacker, BUFFER_A, PARTY_ACTION_CHOOSE_FAINTED_MON, PARTY_SIZE, ABILITY_NONE, gBattleStruct->battlerPartyOrders[gBattlerAttacker]);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
}
|
||
return;
|
||
}
|
||
} // End of switch (cmd->id)
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void TryResetProtectUseCounter(u32 battler)
|
||
{
|
||
u32 lastMove = gLastResultingMoves[battler];
|
||
if (lastMove == MOVE_UNAVAILABLE
|
||
|| (!gBattleMoves[lastMove].protectionMove && (B_ALLY_SWITCH_FAIL_CHANCE >= GEN_9 && gBattleMoves[lastMove].effect != EFFECT_ALLY_SWITCH)))
|
||
gDisableStructs[battler].protectUses = 0;
|
||
}
|
||
|
||
static void Cmd_setprotectlike(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
bool32 fail = TRUE;
|
||
bool32 notLastTurn = TRUE;
|
||
|
||
TryResetProtectUseCounter(gBattlerAttacker);
|
||
if (gCurrentTurnActionNumber == (gBattlersCount - 1))
|
||
notLastTurn = FALSE;
|
||
|
||
if ((sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= Random() && notLastTurn)
|
||
|| (gCurrentMove == MOVE_WIDE_GUARD && B_WIDE_GUARD != GEN_5)
|
||
|| (gCurrentMove == MOVE_QUICK_GUARD && B_QUICK_GUARD != GEN_5))
|
||
{
|
||
if (!gBattleMoves[gCurrentMove].argument) // Protects one mon only.
|
||
{
|
||
if (gBattleMoves[gCurrentMove].effect == EFFECT_ENDURE)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].endured = TRUE;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_BRACED_ITSELF;
|
||
}
|
||
else if (gCurrentMove == MOVE_DETECT || gCurrentMove == MOVE_PROTECT)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].protected = TRUE;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF;
|
||
}
|
||
else if (gCurrentMove == MOVE_SPIKY_SHIELD)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].spikyShielded = TRUE;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF;
|
||
}
|
||
else if (gCurrentMove == MOVE_KINGS_SHIELD)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].kingsShielded = TRUE;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF;
|
||
}
|
||
else if (gCurrentMove == MOVE_BANEFUL_BUNKER)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].banefulBunkered = TRUE;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF;
|
||
}
|
||
else if (gCurrentMove == MOVE_OBSTRUCT)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].obstructed = TRUE;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF;
|
||
}
|
||
else if (gCurrentMove == MOVE_MAX_GUARD)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].maxGuarded = TRUE;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF;
|
||
}
|
||
else if (gCurrentMove == MOVE_SILK_TRAP)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].silkTrapped = TRUE;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF;
|
||
}
|
||
else if (gCurrentMove == MOVE_BURNING_BULWARK)
|
||
{
|
||
gProtectStructs[gBattlerAttacker].burningBulwarked = TRUE;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF;
|
||
}
|
||
|
||
gDisableStructs[gBattlerAttacker].protectUses++;
|
||
fail = FALSE;
|
||
}
|
||
else // Protects the whole side.
|
||
{
|
||
u8 side = GetBattlerSide(gBattlerAttacker);
|
||
if (gCurrentMove == MOVE_WIDE_GUARD && !(gSideStatuses[side] & SIDE_STATUS_WIDE_GUARD))
|
||
{
|
||
gSideStatuses[side] |= SIDE_STATUS_WIDE_GUARD;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_TEAM;
|
||
gDisableStructs[gBattlerAttacker].protectUses++;
|
||
fail = FALSE;
|
||
}
|
||
else if (gCurrentMove == MOVE_QUICK_GUARD && !(gSideStatuses[side] & SIDE_STATUS_QUICK_GUARD))
|
||
{
|
||
gSideStatuses[side] |= SIDE_STATUS_QUICK_GUARD;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_TEAM;
|
||
gDisableStructs[gBattlerAttacker].protectUses++;
|
||
fail = FALSE;
|
||
}
|
||
else if (gCurrentMove == MOVE_CRAFTY_SHIELD && !(gSideStatuses[side] & SIDE_STATUS_CRAFTY_SHIELD))
|
||
{
|
||
gSideStatuses[side] |= SIDE_STATUS_CRAFTY_SHIELD;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_TEAM;
|
||
gDisableStructs[gBattlerAttacker].protectUses++;
|
||
fail = FALSE;
|
||
}
|
||
else if (gCurrentMove == MOVE_MAT_BLOCK && !(gSideStatuses[side] & SIDE_STATUS_MAT_BLOCK))
|
||
{
|
||
gSideStatuses[side] |= SIDE_STATUS_MAT_BLOCK;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_TEAM;
|
||
fail = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (fail)
|
||
{
|
||
gDisableStructs[gBattlerAttacker].protectUses = 0;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECT_FAILED;
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_tryexplosion(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u32 dampBattler;
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
if ((dampBattler = IsAbilityOnField(ABILITY_DAMP)))
|
||
{
|
||
// Failed, a battler has Damp
|
||
gLastUsedAbility = ABILITY_DAMP;
|
||
gBattlerTarget = --dampBattler;
|
||
gBattlescriptCurrInstr = BattleScript_DampStopsExplosion;
|
||
return;
|
||
}
|
||
|
||
gBattleMoveDamage = gBattleMons[gBattlerAttacker].hp;
|
||
BtlController_EmitHealthBarUpdate(gBattlerAttacker, BUFFER_A, INSTANT_HP_BAR_DROP);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setatkhptozero(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
gBattleMons[gBattlerAttacker].hp = 0;
|
||
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_HP_BATTLE, 0, sizeof(gBattleMons[gBattlerAttacker].hp), &gBattleMons[gBattlerAttacker].hp);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_jumpifnexttargetvalid(void)
|
||
{
|
||
CMD_ARGS(const u8 *jumpInstr);
|
||
|
||
const u8 *jumpInstr = cmd->jumpInstr;
|
||
|
||
for (gBattlerTarget++; gBattlerTarget < gBattlersCount; gBattlerTarget++)
|
||
{
|
||
if (gBattlerTarget == gBattlerAttacker && !(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove) & MOVE_TARGET_USER))
|
||
continue;
|
||
if (IsBattlerAlive(gBattlerTarget))
|
||
break;
|
||
}
|
||
|
||
if (gBattlerTarget >= gBattlersCount)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
}
|
||
|
||
static void Cmd_tryhealhalfhealth(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr, u8 battler);
|
||
|
||
const u8 *failInstr = cmd->failInstr;
|
||
|
||
if (cmd->battler == BS_ATTACKER)
|
||
gBattlerTarget = gBattlerAttacker;
|
||
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
gBattleMoveDamage *= -1;
|
||
|
||
if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP)
|
||
gBattlescriptCurrInstr = failInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void SetMoveForMirrorMove(u32 move)
|
||
{
|
||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||
gCurrentMove = move;
|
||
SetAtkCancellerForCalledMove();
|
||
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
||
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
|
||
}
|
||
|
||
static void Cmd_trymirrormove(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
s32 i, validMovesCount;
|
||
u16 move;
|
||
u16 validMoves[MAX_BATTLERS_COUNT] = {0};
|
||
|
||
for (validMovesCount = 0, i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (i != gBattlerAttacker)
|
||
{
|
||
move = gBattleStruct->lastTakenMoveFrom[gBattlerAttacker][i];
|
||
if (move != MOVE_NONE && move != MOVE_UNAVAILABLE)
|
||
{
|
||
validMoves[validMovesCount] = move;
|
||
validMovesCount++;
|
||
}
|
||
}
|
||
}
|
||
|
||
move = gBattleStruct->lastTakenMove[gBattlerAttacker];
|
||
if (move != MOVE_NONE && move != MOVE_UNAVAILABLE)
|
||
{
|
||
SetMoveForMirrorMove(move);
|
||
}
|
||
else if (validMovesCount != 0)
|
||
{
|
||
SetMoveForMirrorMove(validMoves[Random() % validMovesCount]);
|
||
}
|
||
else // no valid moves found
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setrain(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_RAIN, FALSE))
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED;
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_RAIN;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setreflect(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gSideStatuses[GetBattlerSide(gBattlerAttacker)] & SIDE_STATUS_REFLECT)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SIDE_STATUS_FAILED;
|
||
}
|
||
else
|
||
{
|
||
gSideStatuses[GetBattlerSide(gBattlerAttacker)] |= SIDE_STATUS_REFLECT;
|
||
if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LIGHT_CLAY)
|
||
gSideTimers[GetBattlerSide(gBattlerAttacker)].reflectTimer = 8;
|
||
else
|
||
gSideTimers[GetBattlerSide(gBattlerAttacker)].reflectTimer = 5;
|
||
gSideTimers[GetBattlerSide(gBattlerAttacker)].reflectBattlerId = gBattlerAttacker;
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, gBattlerAttacker) == 2)
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_REFLECT_DOUBLE;
|
||
else
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_REFLECT_SINGLE;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setseeded(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT || gStatuses3[gBattlerTarget] & STATUS3_LEECHSEED)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LEECH_SEED_MISS;
|
||
}
|
||
else if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_GRASS))
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LEECH_SEED_FAIL;
|
||
}
|
||
else
|
||
{
|
||
gStatuses3[gBattlerTarget] |= gBattlerAttacker;
|
||
gStatuses3[gBattlerTarget] |= STATUS3_LEECHSEED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LEECH_SEED_SET;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_manipulatedamage(void)
|
||
{
|
||
CMD_ARGS(u8 mode);
|
||
|
||
switch (cmd->mode)
|
||
{
|
||
case DMG_CHANGE_SIGN:
|
||
gBattleMoveDamage *= -1;
|
||
break;
|
||
case DMG_RECOIL_FROM_MISS:
|
||
if (B_RECOIL_IF_MISS_DMG >= GEN_5)
|
||
{
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 2;
|
||
}
|
||
else if (B_RECOIL_IF_MISS_DMG == GEN_4)
|
||
{
|
||
if ((gBattleMons[gBattlerTarget].maxHP / 2) < gBattleMoveDamage)
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
|
||
}
|
||
else
|
||
{
|
||
gBattleMoveDamage /= 2;
|
||
}
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
break;
|
||
case DMG_DOUBLED:
|
||
gBattleMoveDamage *= 2;
|
||
break;
|
||
case DMG_1_8_TARGET_HP:
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerTarget) / 8;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
break;
|
||
case DMG_FULL_ATTACKER_HP:
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker);
|
||
break;
|
||
case DMG_CURR_ATTACKER_HP:
|
||
gBattleMoveDamage = GetNonDynamaxHP(gBattlerAttacker);
|
||
break;
|
||
case DMG_BIG_ROOT:
|
||
gBattleMoveDamage = GetDrainedBigRootHp(gBattlerAttacker, gBattleMoveDamage);
|
||
break;
|
||
case DMG_1_2_ATTACKER_HP:
|
||
gBattleMoveDamage = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP
|
||
break;
|
||
case DMG_RECOIL_FROM_IMMUNE:
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
|
||
break;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_trysetrest(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
const u8 *failInstr = cmd->failInstr;
|
||
gBattlerTarget = gBattlerAttacker;
|
||
gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP * (-1);
|
||
|
||
if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP)
|
||
{
|
||
gBattlescriptCurrInstr = failInstr;
|
||
}
|
||
else if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_ELECTRIC_TERRAIN))
|
||
{
|
||
gBattlescriptCurrInstr = BattleScript_ElectricTerrainPrevents;
|
||
}
|
||
else if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_MISTY_TERRAIN))
|
||
{
|
||
gBattlescriptCurrInstr = BattleScript_MistyTerrainPrevents;
|
||
}
|
||
else
|
||
{
|
||
if (gBattleMons[gBattlerTarget].status1 & ((u8)(~STATUS1_SLEEP)))
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_REST_STATUSED;
|
||
else
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_REST;
|
||
|
||
gBattleMons[gBattlerTarget].status1 = STATUS1_SLEEP_TURN(3);
|
||
BtlController_EmitSetMonData(gBattlerTarget, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1);
|
||
MarkBattlerForControllerExec(gBattlerTarget);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_jumpifnotfirstturn(void)
|
||
{
|
||
CMD_ARGS(const u8 *jumpInstr);
|
||
|
||
const u8 *jumpInstr = cmd->jumpInstr;
|
||
|
||
if (gDisableStructs[gBattlerAttacker].isFirstTurn)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = jumpInstr;
|
||
}
|
||
|
||
static void Cmd_setmiracleeye(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (!(gStatuses3[gBattlerTarget] & STATUS3_MIRACLE_EYED))
|
||
{
|
||
gStatuses3[gBattlerTarget] |= STATUS3_MIRACLE_EYED;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
bool8 UproarWakeUpCheck(u8 battler)
|
||
{
|
||
s32 i;
|
||
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (!(gBattleMons[i].status2 & STATUS2_UPROAR) || GetBattlerAbility(battler) == ABILITY_SOUNDPROOF)
|
||
continue;
|
||
|
||
gBattleScripting.battler = i;
|
||
|
||
if (gBattlerTarget == 0xFF)
|
||
gBattlerTarget = i;
|
||
else if (gBattlerTarget == i)
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CANT_SLEEP_UPROAR;
|
||
else
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_KEPT_AWAKE;
|
||
|
||
break;
|
||
}
|
||
|
||
if (i == gBattlersCount)
|
||
return FALSE;
|
||
else
|
||
return TRUE;
|
||
}
|
||
|
||
static void Cmd_jumpifuproarwakes(void)
|
||
{
|
||
CMD_ARGS(const u8 *jumpInstr);
|
||
|
||
if (UproarWakeUpCheck(gBattlerTarget))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_stockpile(void)
|
||
{
|
||
CMD_ARGS(u8 id);
|
||
|
||
switch (cmd->id)
|
||
{
|
||
case 0:
|
||
if (gDisableStructs[gBattlerAttacker].stockpileCounter >= 3)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CANT_STOCKPILE;
|
||
}
|
||
else
|
||
{
|
||
gDisableStructs[gBattlerAttacker].stockpileCounter++;
|
||
gDisableStructs[gBattlerAttacker].stockpileBeforeDef = gBattleMons[gBattlerAttacker].statStages[STAT_DEF];
|
||
gDisableStructs[gBattlerAttacker].stockpileBeforeSpDef = gBattleMons[gBattlerAttacker].statStages[STAT_SPDEF];
|
||
PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[gBattlerAttacker].stockpileCounter);
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STOCKPILED;
|
||
}
|
||
break;
|
||
case 1: // Save def/sp def stats.
|
||
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
|
||
{
|
||
gDisableStructs[gBattlerAttacker].stockpileDef += gBattleMons[gBattlerAttacker].statStages[STAT_DEF] - gDisableStructs[gBattlerAttacker].stockpileBeforeDef;
|
||
gDisableStructs[gBattlerAttacker].stockpileSpDef += gBattleMons[gBattlerAttacker].statStages[STAT_SPDEF] - gDisableStructs[gBattlerAttacker].stockpileBeforeSpDef;
|
||
}
|
||
break;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_stockpiletobasedamage(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
const u8 *failInstr = cmd->failInstr;
|
||
if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0)
|
||
{
|
||
gBattlescriptCurrInstr = failInstr;
|
||
}
|
||
else
|
||
{
|
||
if (gBattleCommunication[MISS_TYPE] != B_MSG_PROTECTED)
|
||
gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter;
|
||
|
||
if (!(gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_1ST_HIT && gBattleMons[gBattlerTarget].hp != 0))
|
||
{
|
||
gBattleStruct->moveEffect2 = MOVE_EFFECT_STOCKPILE_WORE_OFF;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_stockpiletohpheal(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
const u8 *failInstr = cmd->failInstr;
|
||
|
||
if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0)
|
||
{
|
||
gBattlescriptCurrInstr = failInstr;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWALLOW_FAILED;
|
||
}
|
||
else
|
||
{
|
||
if (gBattleMons[gBattlerAttacker].maxHP == gBattleMons[gBattlerAttacker].hp)
|
||
{
|
||
gDisableStructs[gBattlerAttacker].stockpileCounter = 0;
|
||
gBattlescriptCurrInstr = failInstr;
|
||
gBattlerTarget = gBattlerAttacker;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWALLOW_FULL_HP;
|
||
}
|
||
else
|
||
{
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / (1 << (3 - gDisableStructs[gBattlerAttacker].stockpileCounter));
|
||
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
gBattleMoveDamage *= -1;
|
||
|
||
gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter;
|
||
gBattleStruct->moveEffect2 = MOVE_EFFECT_STOCKPILE_WORE_OFF;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
gBattlerTarget = gBattlerAttacker;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Sign change for drained HP handled in GetDrainedBigRootHp
|
||
static void Cmd_setdrainedhp(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gBattleMoves[gCurrentMove].argument != 0)
|
||
gBattleMoveDamage = (gHpDealt * gBattleMoves[gCurrentMove].argument / 100);
|
||
else
|
||
gBattleMoveDamage = (gHpDealt / 2);
|
||
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static u16 ReverseStatChangeMoveEffect(u16 moveEffect)
|
||
{
|
||
switch (moveEffect)
|
||
{
|
||
// +1
|
||
case MOVE_EFFECT_ATK_PLUS_1:
|
||
return MOVE_EFFECT_ATK_MINUS_1;
|
||
case MOVE_EFFECT_DEF_PLUS_1:
|
||
return MOVE_EFFECT_DEF_MINUS_1;
|
||
case MOVE_EFFECT_SPD_PLUS_1:
|
||
return MOVE_EFFECT_SPD_MINUS_1;
|
||
case MOVE_EFFECT_SP_ATK_PLUS_1:
|
||
return MOVE_EFFECT_SP_ATK_MINUS_1;
|
||
case MOVE_EFFECT_SP_DEF_PLUS_1:
|
||
return MOVE_EFFECT_SP_DEF_MINUS_1;
|
||
case MOVE_EFFECT_ACC_PLUS_1:
|
||
return MOVE_EFFECT_ACC_MINUS_1;
|
||
case MOVE_EFFECT_EVS_PLUS_1:
|
||
return MOVE_EFFECT_EVS_MINUS_1;
|
||
// -1
|
||
case MOVE_EFFECT_ATK_MINUS_1:
|
||
return MOVE_EFFECT_ATK_PLUS_1;
|
||
case MOVE_EFFECT_DEF_MINUS_1:
|
||
return MOVE_EFFECT_DEF_PLUS_1;
|
||
case MOVE_EFFECT_SPD_MINUS_1:
|
||
return MOVE_EFFECT_SPD_PLUS_1;
|
||
case MOVE_EFFECT_SP_ATK_MINUS_1:
|
||
return MOVE_EFFECT_SP_ATK_PLUS_1;
|
||
case MOVE_EFFECT_SP_DEF_MINUS_1:
|
||
return MOVE_EFFECT_SP_DEF_PLUS_1;
|
||
case MOVE_EFFECT_ACC_MINUS_1:
|
||
return MOVE_EFFECT_ACC_PLUS_1;
|
||
case MOVE_EFFECT_EVS_MINUS_1:
|
||
// +2
|
||
case MOVE_EFFECT_ATK_PLUS_2:
|
||
return MOVE_EFFECT_ATK_MINUS_2;
|
||
case MOVE_EFFECT_DEF_PLUS_2:
|
||
return MOVE_EFFECT_DEF_MINUS_2;
|
||
case MOVE_EFFECT_SPD_PLUS_2:
|
||
return MOVE_EFFECT_SPD_MINUS_2;
|
||
case MOVE_EFFECT_SP_ATK_PLUS_2:
|
||
return MOVE_EFFECT_SP_ATK_MINUS_2;
|
||
case MOVE_EFFECT_SP_DEF_PLUS_2:
|
||
return MOVE_EFFECT_SP_DEF_MINUS_2;
|
||
case MOVE_EFFECT_ACC_PLUS_2:
|
||
return MOVE_EFFECT_ACC_MINUS_2;
|
||
case MOVE_EFFECT_EVS_PLUS_2:
|
||
return MOVE_EFFECT_EVS_MINUS_2;
|
||
// -2
|
||
case MOVE_EFFECT_ATK_MINUS_2:
|
||
return MOVE_EFFECT_ATK_PLUS_2;
|
||
case MOVE_EFFECT_DEF_MINUS_2:
|
||
return MOVE_EFFECT_DEF_PLUS_2;
|
||
case MOVE_EFFECT_SPD_MINUS_2:
|
||
return MOVE_EFFECT_SPD_PLUS_2;
|
||
case MOVE_EFFECT_SP_ATK_MINUS_2:
|
||
return MOVE_EFFECT_SP_ATK_PLUS_2;
|
||
case MOVE_EFFECT_SP_DEF_MINUS_2:
|
||
return MOVE_EFFECT_SP_DEF_PLUS_2;
|
||
case MOVE_EFFECT_ACC_MINUS_2:
|
||
return MOVE_EFFECT_ACC_PLUS_2;
|
||
case MOVE_EFFECT_EVS_MINUS_2:
|
||
return MOVE_EFFECT_EVS_PLUS_2;
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr)
|
||
{
|
||
bool32 certain = FALSE;
|
||
bool32 notProtectAffected = FALSE;
|
||
u32 index, battler, battlerAbility, battlerHoldEffect;
|
||
bool32 affectsUser = (flags & MOVE_EFFECT_AFFECTS_USER);
|
||
bool32 mirrorArmored = (flags & STAT_CHANGE_MIRROR_ARMOR);
|
||
|
||
if (affectsUser)
|
||
battler = gBattlerAttacker;
|
||
else
|
||
battler = gBattlerTarget;
|
||
|
||
battlerAbility = GetBattlerAbility(battler);
|
||
battlerHoldEffect = GetBattlerHoldEffect(battler, TRUE);
|
||
|
||
gSpecialStatuses[battler].changedStatsBattlerId = gBattlerAttacker;
|
||
|
||
flags &= ~(MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_MIRROR_ARMOR);
|
||
|
||
if (flags & MOVE_EFFECT_CERTAIN)
|
||
certain = TRUE;
|
||
flags &= ~MOVE_EFFECT_CERTAIN;
|
||
|
||
if (flags & STAT_CHANGE_NOT_PROTECT_AFFECTED)
|
||
notProtectAffected++;
|
||
flags &= ~STAT_CHANGE_NOT_PROTECT_AFFECTED;
|
||
|
||
if (battlerAbility == ABILITY_CONTRARY)
|
||
{
|
||
statValue ^= STAT_BUFF_NEGATIVE;
|
||
gBattleScripting.statChanger ^= STAT_BUFF_NEGATIVE;
|
||
RecordAbilityBattle(battler, battlerAbility);
|
||
if (flags & STAT_CHANGE_UPDATE_MOVE_EFFECT)
|
||
{
|
||
flags &= ~STAT_CHANGE_UPDATE_MOVE_EFFECT;
|
||
gBattleScripting.moveEffect = ReverseStatChangeMoveEffect(gBattleScripting.moveEffect);
|
||
}
|
||
}
|
||
else if (battlerAbility == ABILITY_SIMPLE)
|
||
{
|
||
statValue = (SET_STAT_BUFF_VALUE(GET_STAT_BUFF_VALUE(statValue) * 2)) | ((statValue <= -1) ? STAT_BUFF_NEGATIVE : 0);
|
||
}
|
||
|
||
PREPARE_STAT_BUFFER(gBattleTextBuff1, statId);
|
||
|
||
if (statValue <= -1) // Stat decrease.
|
||
{
|
||
if (gSideTimers[GetBattlerSide(battler)].mistTimer
|
||
&& !certain && gCurrentMove != MOVE_CURSE
|
||
&& !(battler == gBattlerTarget && GetBattlerAbility(gBattlerAttacker) == ABILITY_INFILTRATOR))
|
||
{
|
||
if (flags == STAT_CHANGE_ALLOW_PTR)
|
||
{
|
||
if (gSpecialStatuses[battler].statLowered)
|
||
{
|
||
gBattlescriptCurrInstr = BS_ptr;
|
||
}
|
||
else
|
||
{
|
||
BattleScriptPush(BS_ptr);
|
||
gBattleScripting.battler = battler;
|
||
gBattlescriptCurrInstr = BattleScript_MistProtected;
|
||
gSpecialStatuses[battler].statLowered = TRUE;
|
||
}
|
||
}
|
||
return STAT_CHANGE_DIDNT_WORK;
|
||
}
|
||
else if (gCurrentMove != MOVE_CURSE
|
||
&& notProtectAffected != TRUE && JumpIfMoveAffectedByProtect(gCurrentMove))
|
||
{
|
||
gBattlescriptCurrInstr = BattleScript_ButItFailed;
|
||
return STAT_CHANGE_DIDNT_WORK;
|
||
}
|
||
else if ((battlerHoldEffect == HOLD_EFFECT_CLEAR_AMULET
|
||
|| CanAbilityPreventStatLoss(battlerAbility, GetBattlerAbility(gBattlerAttacker) == ABILITY_INTIMIDATE))
|
||
&& (!affectsUser || mirrorArmored) && !certain && gCurrentMove != MOVE_CURSE)
|
||
{
|
||
if (flags == STAT_CHANGE_ALLOW_PTR)
|
||
{
|
||
if (gSpecialStatuses[battler].statLowered)
|
||
{
|
||
gBattlescriptCurrInstr = BS_ptr;
|
||
}
|
||
else
|
||
{
|
||
BattleScriptPush(BS_ptr);
|
||
gBattleScripting.battler = battler;
|
||
if (battlerHoldEffect == HOLD_EFFECT_CLEAR_AMULET)
|
||
{
|
||
gLastUsedItem = gBattleMons[battler].item;
|
||
gBattlescriptCurrInstr = BattleScript_ItemNoStatLoss;
|
||
RecordItemEffectBattle(battler, HOLD_EFFECT_CLEAR_AMULET);
|
||
}
|
||
else
|
||
{
|
||
gBattlerAbility = battler;
|
||
gBattlescriptCurrInstr = BattleScript_AbilityNoStatLoss;
|
||
gLastUsedAbility = battlerAbility;
|
||
RecordAbilityBattle(battler, gLastUsedAbility);
|
||
}
|
||
gSpecialStatuses[battler].statLowered = TRUE;
|
||
}
|
||
}
|
||
return STAT_CHANGE_DIDNT_WORK;
|
||
}
|
||
else if ((index = IsFlowerVeilProtected(battler)) && !certain)
|
||
{
|
||
if (flags == STAT_CHANGE_ALLOW_PTR)
|
||
{
|
||
if (gSpecialStatuses[battler].statLowered)
|
||
{
|
||
gBattlescriptCurrInstr = BS_ptr;
|
||
}
|
||
else
|
||
{
|
||
BattleScriptPush(BS_ptr);
|
||
gBattleScripting.battler = battler;
|
||
gBattlerAbility = index - 1;
|
||
gBattlescriptCurrInstr = BattleScript_FlowerVeilProtectsRet;
|
||
gLastUsedAbility = ABILITY_FLOWER_VEIL;
|
||
gSpecialStatuses[battler].statLowered = TRUE;
|
||
}
|
||
}
|
||
return STAT_CHANGE_DIDNT_WORK;
|
||
}
|
||
else if (!certain
|
||
&& (((battlerAbility == ABILITY_KEEN_EYE || battlerAbility == ABILITY_MINDS_EYE) && statId == STAT_ACC)
|
||
|| (B_ILLUMINATE_EFFECT >= GEN_9 && battlerAbility == ABILITY_ILLUMINATE && statId == STAT_ACC)
|
||
|| (battlerAbility == ABILITY_HYPER_CUTTER && statId == STAT_ATK)
|
||
|| (battlerAbility == ABILITY_BIG_PECKS && statId == STAT_DEF)))
|
||
{
|
||
if (flags == STAT_CHANGE_ALLOW_PTR)
|
||
{
|
||
BattleScriptPush(BS_ptr);
|
||
gBattleScripting.battler = battler;
|
||
gBattlerAbility = battler;
|
||
gBattlescriptCurrInstr = BattleScript_AbilityNoSpecificStatLoss;
|
||
gLastUsedAbility = battlerAbility;
|
||
RecordAbilityBattle(battler, gLastUsedAbility);
|
||
}
|
||
return STAT_CHANGE_DIDNT_WORK;
|
||
}
|
||
else if (battlerAbility == ABILITY_MIRROR_ARMOR && !affectsUser && !mirrorArmored && gBattlerAttacker != gBattlerTarget && battler == gBattlerTarget)
|
||
{
|
||
if (flags == STAT_CHANGE_ALLOW_PTR)
|
||
{
|
||
SET_STATCHANGER(statId, GET_STAT_BUFF_VALUE(statValue) | STAT_BUFF_NEGATIVE, TRUE);
|
||
BattleScriptPush(BS_ptr);
|
||
gBattleScripting.battler = battler;
|
||
gBattlerAbility = battler;
|
||
gBattlescriptCurrInstr = BattleScript_MirrorArmorReflect;
|
||
RecordAbilityBattle(battler, gBattleMons[battler].ability);
|
||
}
|
||
return STAT_CHANGE_DIDNT_WORK;
|
||
}
|
||
else if (battlerAbility == ABILITY_SHIELD_DUST && flags == 0)
|
||
{
|
||
RecordAbilityBattle(battler, ABILITY_SHIELD_DUST);
|
||
return STAT_CHANGE_DIDNT_WORK;
|
||
}
|
||
else if (flags == 0 && battlerHoldEffect == HOLD_EFFECT_COVERT_CLOAK)
|
||
{
|
||
RecordItemEffectBattle(battler, HOLD_EFFECT_COVERT_CLOAK);
|
||
return STAT_CHANGE_DIDNT_WORK;
|
||
}
|
||
else // try to decrease
|
||
{
|
||
statValue = -GET_STAT_BUFF_VALUE(statValue);
|
||
if (gBattleMons[battler].statStages[statId] == 1)
|
||
statValue = -1;
|
||
else if (gBattleMons[battler].statStages[statId] == 2 && statValue < -2)
|
||
statValue = -2;
|
||
gBattleTextBuff2[0] = B_BUFF_PLACEHOLDER_BEGIN;
|
||
index = 1;
|
||
if (statValue == -2)
|
||
{
|
||
gBattleTextBuff2[1] = B_BUFF_STRING;
|
||
gBattleTextBuff2[2] = STRINGID_STATHARSHLY;
|
||
gBattleTextBuff2[3] = STRINGID_STATHARSHLY >> 8;
|
||
index = 4;
|
||
}
|
||
else if (statValue <= -3)
|
||
{
|
||
gBattleTextBuff2[1] = B_BUFF_STRING;
|
||
gBattleTextBuff2[2] = STRINGID_SEVERELY & 0xFF;
|
||
gBattleTextBuff2[3] = STRINGID_SEVERELY >> 8;
|
||
index = 4;
|
||
}
|
||
gBattleTextBuff2[index++] = B_BUFF_STRING;
|
||
gBattleTextBuff2[index++] = STRINGID_STATFELL;
|
||
gBattleTextBuff2[index++] = STRINGID_STATFELL >> 8;
|
||
gBattleTextBuff2[index] = B_BUFF_EOS;
|
||
|
||
if (gBattleMons[battler].statStages[statId] == MIN_STAT_STAGE)
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STAT_WONT_DECREASE;
|
||
}
|
||
else
|
||
{
|
||
gProtectStructs[battler].statFell = TRUE; // Eject pack, lash out
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == battler); // B_MSG_ATTACKER_STAT_FELL or B_MSG_DEFENDER_STAT_FELL
|
||
}
|
||
}
|
||
}
|
||
else // stat increase
|
||
{
|
||
statValue = GET_STAT_BUFF_VALUE(statValue);
|
||
if (gBattleMons[battler].statStages[statId] == 11)
|
||
statValue = 1;
|
||
else if (gBattleMons[battler].statStages[statId] == 10 && statValue > 2)
|
||
statValue = 2;
|
||
gBattleTextBuff2[0] = B_BUFF_PLACEHOLDER_BEGIN;
|
||
index = 1;
|
||
if (statValue == 2)
|
||
{
|
||
gBattleTextBuff2[1] = B_BUFF_STRING;
|
||
gBattleTextBuff2[2] = STRINGID_STATSHARPLY;
|
||
gBattleTextBuff2[3] = STRINGID_STATSHARPLY >> 8;
|
||
index = 4;
|
||
}
|
||
else if (statValue >= 3)
|
||
{
|
||
gBattleTextBuff2[1] = B_BUFF_STRING;
|
||
gBattleTextBuff2[2] = STRINGID_DRASTICALLY & 0xFF;
|
||
gBattleTextBuff2[3] = STRINGID_DRASTICALLY >> 8;
|
||
index = 4;
|
||
}
|
||
gBattleTextBuff2[index++] = B_BUFF_STRING;
|
||
gBattleTextBuff2[index++] = STRINGID_STATROSE;
|
||
gBattleTextBuff2[index++] = STRINGID_STATROSE >> 8;
|
||
gBattleTextBuff2[index] = B_BUFF_EOS;
|
||
|
||
if (gBattleMons[battler].statStages[statId] == MAX_STAT_STAGE)
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STAT_WONT_INCREASE;
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == battler);
|
||
gProtectStructs[battler].statRaised = TRUE;
|
||
|
||
// check mirror herb
|
||
for (index = 0; index < gBattlersCount; index++)
|
||
{
|
||
if (GetBattlerSide(index) == GetBattlerSide(battler))
|
||
continue; // Only triggers on opposing side
|
||
if (GetBattlerAbility(index) == ABILITY_OPPORTUNIST
|
||
&& gProtectStructs[battler].activateOpportunist == 0) // don't activate opportunist on other mon's opportunist raises
|
||
{
|
||
gProtectStructs[index].activateOpportunist = 2; // set stats to copy
|
||
gQueuedStatBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk
|
||
gQueuedStatBoosts[index].statChanges[statId - 1] += statValue; // cumulative in case of multiple opponent boosts
|
||
}
|
||
else if (GetBattlerHoldEffect(index, TRUE) == HOLD_EFFECT_MIRROR_HERB
|
||
&& gBattleMons[index].statStages[statId] < MAX_STAT_STAGE)
|
||
{
|
||
gProtectStructs[index].eatMirrorHerb = 1;
|
||
gQueuedStatBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk
|
||
gQueuedStatBoosts[index].statChanges[statId - 1] = statValue;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
gBattleMons[battler].statStages[statId] += statValue;
|
||
if (gBattleMons[battler].statStages[statId] < MIN_STAT_STAGE)
|
||
gBattleMons[battler].statStages[statId] = MIN_STAT_STAGE;
|
||
if (gBattleMons[battler].statStages[statId] > MAX_STAT_STAGE)
|
||
gBattleMons[battler].statStages[statId] = MAX_STAT_STAGE;
|
||
|
||
if (gBattleCommunication[MULTISTRING_CHOOSER] == B_MSG_STAT_WONT_INCREASE && flags & STAT_CHANGE_ALLOW_PTR)
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
|
||
if (gBattleCommunication[MULTISTRING_CHOOSER] == B_MSG_STAT_WONT_INCREASE && !(flags & STAT_CHANGE_ALLOW_PTR))
|
||
return STAT_CHANGE_DIDNT_WORK;
|
||
|
||
return STAT_CHANGE_WORKED;
|
||
}
|
||
|
||
static void Cmd_statbuffchange(void)
|
||
{
|
||
CMD_ARGS(u16 flags, const u8 *failInstr);
|
||
|
||
u16 flags = cmd->flags;
|
||
const u8 *ptrBefore = gBattlescriptCurrInstr;
|
||
const u8 *failInstr = cmd->failInstr;
|
||
|
||
if (ChangeStatBuffs(GET_STAT_BUFF_VALUE_WITH_SIGN(gBattleScripting.statChanger), GET_STAT_BUFF_ID(gBattleScripting.statChanger), flags, failInstr) == STAT_CHANGE_WORKED)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else if (gBattlescriptCurrInstr == ptrBefore) // Prevent infinite looping.
|
||
gBattlescriptCurrInstr = failInstr;
|
||
}
|
||
|
||
bool32 TryResetBattlerStatChanges(u8 battler)
|
||
{
|
||
u32 j;
|
||
bool32 ret = FALSE;
|
||
|
||
gDisableStructs[battler].stockpileDef = 0;
|
||
gDisableStructs[battler].stockpileSpDef = 0;
|
||
for (j = 0; j < NUM_BATTLE_STATS; j++)
|
||
{
|
||
if (gBattleMons[battler].statStages[j] != DEFAULT_STAT_STAGE)
|
||
ret = TRUE; // returns TRUE if any stat was reset
|
||
|
||
gBattleMons[battler].statStages[j] = DEFAULT_STAT_STAGE;
|
||
}
|
||
|
||
return ret;
|
||
}
|
||
|
||
// Haze
|
||
static void Cmd_normalisebuffs(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
s32 i;
|
||
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
TryResetBattlerStatChanges(i);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setbide(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gBattleMons[gBattlerAttacker].status2 |= STATUS2_MULTIPLETURNS;
|
||
gLockedMoves[gBattlerAttacker] = gCurrentMove;
|
||
gBideDmg[gBattlerAttacker] = 0;
|
||
gBattleMons[gBattlerAttacker].status2 |= STATUS2_BIDE_TURN(2);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_confuseifrepeatingattackends(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_LOCK_CONFUSE) && !gSpecialStatuses[gBattlerAttacker].dancerUsedMove)
|
||
gBattleScripting.moveEffect = (MOVE_EFFECT_THRASH | MOVE_EFFECT_AFFECTS_USER);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setmultihitcounter(void)
|
||
{
|
||
CMD_ARGS(u8 value);
|
||
|
||
if (cmd->value)
|
||
{
|
||
gMultiHitCounter = cmd->value;
|
||
}
|
||
else
|
||
{
|
||
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_SKILL_LINK)
|
||
{
|
||
gMultiHitCounter = 5;
|
||
}
|
||
else
|
||
{
|
||
// WARNING: These seem to be unused, see SetRandomMultiHitCounter.
|
||
if (B_MULTI_HIT_CHANCE >= GEN_5)
|
||
// 35%: 2 hits, 35%: 3 hits, 15% 4 hits, 15% 5 hits.
|
||
gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 7, 7, 3, 3);
|
||
else
|
||
// 37.5%: 2 hits, 37.5%: 3 hits, 12.5% 4 hits, 12.5% 5 hits.
|
||
gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 3, 3, 1, 1);
|
||
}
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_initmultihitstring(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0)
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_forcerandomswitch(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
s32 i;
|
||
s32 battler1PartyId = 0;
|
||
s32 battler2PartyId = 0;
|
||
|
||
s32 firstMonId;
|
||
s32 lastMonId = 0; // + 1
|
||
struct Pokemon *party = NULL;
|
||
u8 validMons[PARTY_SIZE];
|
||
s32 validMonsCount = 0;
|
||
|
||
bool32 redCardForcedSwitch = FALSE;
|
||
|
||
// Red card checks against wild pokemon. If we have reached here, the player has a mon to switch into
|
||
// Red card swaps attacker with target to get the animation correct, so here we check attacker which is really the target. Thanks GF...
|
||
if (gBattleScripting.switchCase == B_SWITCH_RED_CARD
|
||
&& !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)
|
||
&& GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT) // Check opponent's red card activating
|
||
{
|
||
if (!WILD_DOUBLE_BATTLE)
|
||
{
|
||
// Wild mon with red card will end single battle
|
||
gBattlescriptCurrInstr = BattleScript_RoarSuccessEndBattle;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
// Wild double battle, wild mon red card activation on player
|
||
if (IS_WHOLE_SIDE_ALIVE(gBattlerTarget))
|
||
{
|
||
// Both player's battlers are alive
|
||
redCardForcedSwitch = FALSE;
|
||
}
|
||
else
|
||
{
|
||
// Player has only one mon alive -> force red card switch before manually switching to other mon
|
||
redCardForcedSwitch = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Swapping pokemon happens in:
|
||
// trainer battles
|
||
// wild double battles when an opposing pokemon uses it against one of the two alive player mons
|
||
// wild double battle when a player pokemon uses it against its partner
|
||
if ((gBattleTypeFlags & BATTLE_TYPE_TRAINER)
|
||
|| (WILD_DOUBLE_BATTLE
|
||
&& GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT
|
||
&& GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER
|
||
&& IS_WHOLE_SIDE_ALIVE(gBattlerTarget))
|
||
|| (WILD_DOUBLE_BATTLE
|
||
&& GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER
|
||
&& GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER)
|
||
|| redCardForcedSwitch
|
||
)
|
||
{
|
||
party = GetBattlerParty(gBattlerTarget);
|
||
|
||
if (BATTLE_TWO_VS_ONE_OPPONENT && GetBattlerSide(gBattlerTarget) == B_SIDE_OPPONENT)
|
||
{
|
||
firstMonId = 0;
|
||
lastMonId = 6;
|
||
battler2PartyId = gBattlerPartyIndexes[gBattlerTarget];
|
||
battler1PartyId = gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerTarget)];
|
||
}
|
||
else if ((gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER && gBattleTypeFlags & BATTLE_TYPE_LINK)
|
||
|| (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER && gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK)
|
||
|| (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER))
|
||
{
|
||
if ((gBattlerTarget & BIT_FLANK) != B_FLANK_LEFT)
|
||
{
|
||
firstMonId = PARTY_SIZE / 2;
|
||
lastMonId = PARTY_SIZE;
|
||
}
|
||
else
|
||
{
|
||
firstMonId = 0;
|
||
lastMonId = PARTY_SIZE / 2;
|
||
}
|
||
battler2PartyId = gBattlerPartyIndexes[gBattlerTarget];
|
||
battler1PartyId = gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerTarget)];
|
||
}
|
||
else if ((gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_LINK)
|
||
|| (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK))
|
||
{
|
||
if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(gBattlerTarget)) == B_FLANK_RIGHT)
|
||
{
|
||
firstMonId = PARTY_SIZE / 2;
|
||
lastMonId = PARTY_SIZE;
|
||
}
|
||
else
|
||
{
|
||
firstMonId = 0;
|
||
lastMonId = PARTY_SIZE / 2;
|
||
}
|
||
battler2PartyId = gBattlerPartyIndexes[gBattlerTarget];
|
||
battler1PartyId = gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerTarget)];
|
||
}
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
|
||
{
|
||
if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER)
|
||
{
|
||
firstMonId = 0;
|
||
lastMonId = PARTY_SIZE;
|
||
}
|
||
else
|
||
{
|
||
if ((gBattlerTarget & BIT_FLANK) != B_FLANK_LEFT)
|
||
{
|
||
firstMonId = PARTY_SIZE / 2;
|
||
lastMonId = PARTY_SIZE;
|
||
}
|
||
else
|
||
{
|
||
firstMonId = 0;
|
||
lastMonId = PARTY_SIZE / 2;
|
||
}
|
||
}
|
||
battler2PartyId = gBattlerPartyIndexes[gBattlerTarget];
|
||
battler1PartyId = gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerTarget)];
|
||
}
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||
{
|
||
firstMonId = 0;
|
||
lastMonId = PARTY_SIZE;
|
||
battler2PartyId = gBattlerPartyIndexes[gBattlerTarget];
|
||
battler1PartyId = gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerTarget)];
|
||
}
|
||
else
|
||
{
|
||
firstMonId = 0;
|
||
lastMonId = PARTY_SIZE;
|
||
battler2PartyId = gBattlerPartyIndexes[gBattlerTarget]; // there is only one Pokémon out in single battles
|
||
battler1PartyId = gBattlerPartyIndexes[gBattlerTarget];
|
||
}
|
||
|
||
for (i = firstMonId; i < lastMonId; i++)
|
||
{
|
||
if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE
|
||
&& !GetMonData(&party[i], MON_DATA_IS_EGG)
|
||
&& GetMonData(&party[i], MON_DATA_HP) != 0
|
||
&& i != battler1PartyId
|
||
&& i != battler2PartyId)
|
||
{
|
||
validMons[validMonsCount++] = i;
|
||
}
|
||
}
|
||
|
||
if (validMonsCount == 0)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
*(gBattleStruct->battlerPartyIndexes + gBattlerTarget) = gBattlerPartyIndexes[gBattlerTarget];
|
||
gBattlescriptCurrInstr = BattleScript_RoarSuccessSwitch;
|
||
gBattleStruct->forcedSwitch |= gBitTable[gBattlerTarget];
|
||
*(gBattleStruct->monToSwitchIntoId + gBattlerTarget) = validMons[RandomUniform(RNG_FORCE_RANDOM_SWITCH, 0, validMonsCount - 1)];
|
||
|
||
if (!IsMultiBattle())
|
||
SwitchPartyOrder(gBattlerTarget);
|
||
|
||
if ((gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER)
|
||
|| (gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleTypeFlags & BATTLE_TYPE_MULTI)
|
||
|| (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER)
|
||
|| (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && gBattleTypeFlags & BATTLE_TYPE_MULTI))
|
||
{
|
||
SwitchPartyOrderLinkMulti(gBattlerTarget, i, 0);
|
||
SwitchPartyOrderLinkMulti(BATTLE_PARTNER(gBattlerTarget), i, 1);
|
||
}
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
|
||
SwitchPartyOrderInGameMulti(gBattlerTarget, i);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// In normal wild doubles, Roar will always fail if the user's level is less than the target's.
|
||
if (gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level)
|
||
gBattlescriptCurrInstr = BattleScript_RoarSuccessEndBattle;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_tryconversiontypechange(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u8 validMoves = 0;
|
||
u8 moveChecked = 0;
|
||
u8 moveType = 0;
|
||
|
||
if (B_UPDATED_CONVERSION >= GEN_6)
|
||
{
|
||
// Changes user's type to its first move's type
|
||
for (moveChecked = 0; moveChecked < MAX_MON_MOVES; moveChecked++)
|
||
{
|
||
if (gBattleMons[gBattlerAttacker].moves[moveChecked] != MOVE_NONE)
|
||
{
|
||
moveType = gBattleMoves[gBattleMons[gBattlerAttacker].moves[moveChecked]].type;
|
||
break;
|
||
}
|
||
}
|
||
if (IS_BATTLER_OF_TYPE(gBattlerAttacker, moveType))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
SET_BATTLER_TYPE(gBattlerAttacker, moveType);
|
||
PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Randomly changes user's type to one of its moves' type
|
||
while (validMoves < MAX_MON_MOVES)
|
||
{
|
||
if (gBattleMons[gBattlerAttacker].moves[validMoves] == MOVE_NONE)
|
||
break;
|
||
|
||
validMoves++;
|
||
}
|
||
|
||
for (moveChecked = 0; moveChecked < validMoves; moveChecked++)
|
||
{
|
||
moveType = gBattleMoves[gBattleMons[gBattlerAttacker].moves[moveChecked]].type;
|
||
|
||
if (moveType == TYPE_MYSTERY)
|
||
{
|
||
if (IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
|
||
moveType = TYPE_GHOST;
|
||
else
|
||
moveType = TYPE_NORMAL;
|
||
}
|
||
if (moveType != gBattleMons[gBattlerAttacker].type1
|
||
&& moveType != gBattleMons[gBattlerAttacker].type2
|
||
&& moveType != gBattleMons[gBattlerAttacker].type3)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (moveChecked == validMoves)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
do
|
||
{
|
||
while ((moveChecked = MOD(Random(), MAX_MON_MOVES)) >= validMoves);
|
||
|
||
moveType = gBattleMoves[gBattleMons[gBattlerAttacker].moves[moveChecked]].type;
|
||
|
||
if (moveType == TYPE_MYSTERY)
|
||
{
|
||
if (IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
|
||
moveType = TYPE_GHOST;
|
||
else
|
||
moveType = TYPE_NORMAL;
|
||
}
|
||
}
|
||
while (moveType == gBattleMons[gBattlerAttacker].type1 || moveType == gBattleMons[gBattlerAttacker].type2 || moveType == gBattleMons[gBattlerAttacker].type3);
|
||
|
||
SET_BATTLER_TYPE(gBattlerAttacker, moveType);
|
||
PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void Cmd_givepaydaymoney(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) && gPaydayMoney != 0)
|
||
{
|
||
u32 bonusMoney = gPaydayMoney * gBattleStruct->moneyMultiplier;
|
||
AddMoney(&gSaveBlock1Ptr->money, bonusMoney);
|
||
|
||
PREPARE_HWORD_NUMBER_BUFFER(gBattleTextBuff1, 5, bonusMoney)
|
||
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_PrintPayDayMoneyString;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setlightscreen(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gSideStatuses[GetBattlerSide(gBattlerAttacker)] & SIDE_STATUS_LIGHTSCREEN)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SIDE_STATUS_FAILED;
|
||
}
|
||
else
|
||
{
|
||
gSideStatuses[GetBattlerSide(gBattlerAttacker)] |= SIDE_STATUS_LIGHTSCREEN;
|
||
if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LIGHT_CLAY)
|
||
gSideTimers[GetBattlerSide(gBattlerAttacker)].lightscreenTimer = 8;
|
||
else
|
||
gSideTimers[GetBattlerSide(gBattlerAttacker)].lightscreenTimer = 5;
|
||
gSideTimers[GetBattlerSide(gBattlerAttacker)].lightscreenBattlerId = gBattlerAttacker;
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, gBattlerAttacker) == 2)
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_LIGHTSCREEN_DOUBLE;
|
||
else
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_LIGHTSCREEN_SINGLE;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_tryKO(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
bool32 lands = FALSE;
|
||
u32 holdEffect = GetBattlerHoldEffect(gBattlerTarget, TRUE);
|
||
u16 targetAbility = GetBattlerAbility(gBattlerTarget);
|
||
|
||
// Dynamaxed Pokemon cannot be hit by OHKO moves.
|
||
if (IsDynamaxed(gBattlerTarget))
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_KO_UNAFFECTED;
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
return;
|
||
}
|
||
|
||
gPotentialItemEffectBattler = gBattlerTarget;
|
||
if (holdEffect == HOLD_EFFECT_FOCUS_BAND
|
||
&& (Random() % 100) < GetBattlerHoldEffectParam(gBattlerTarget))
|
||
{
|
||
gSpecialStatuses[gBattlerTarget].focusBanded = TRUE;
|
||
RecordItemEffectBattle(gBattlerTarget, holdEffect);
|
||
}
|
||
else if (holdEffect == HOLD_EFFECT_FOCUS_SASH && BATTLER_MAX_HP(gBattlerTarget))
|
||
{
|
||
gSpecialStatuses[gBattlerTarget].focusSashed = TRUE;
|
||
RecordItemEffectBattle(gBattlerTarget, holdEffect);
|
||
}
|
||
|
||
if (targetAbility == ABILITY_STURDY)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gLastUsedAbility = ABILITY_STURDY;
|
||
gBattlescriptCurrInstr = BattleScript_SturdyPreventsOHKO;
|
||
gBattlerAbility = gBattlerTarget;
|
||
}
|
||
else
|
||
{
|
||
if ((((gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS)
|
||
&& gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker)
|
||
|| GetBattlerAbility(gBattlerAttacker) == ABILITY_NO_GUARD
|
||
|| targetAbility == ABILITY_NO_GUARD)
|
||
&& gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level)
|
||
{
|
||
lands = TRUE;
|
||
}
|
||
else
|
||
{
|
||
u16 odds = gBattleMoves[gCurrentMove].accuracy + (gBattleMons[gBattlerAttacker].level - gBattleMons[gBattlerTarget].level);
|
||
if (B_SHEER_COLD_ACC >= GEN_7 && gCurrentMove == MOVE_SHEER_COLD && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE))
|
||
odds -= 10;
|
||
if (RandomPercentage(RNG_ACCURACY, odds) && gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level)
|
||
lands = TRUE;
|
||
}
|
||
|
||
if (lands)
|
||
{
|
||
if (gProtectStructs[gBattlerTarget].endured)
|
||
{
|
||
gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1;
|
||
gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED;
|
||
}
|
||
else if (gSpecialStatuses[gBattlerTarget].focusBanded || gSpecialStatuses[gBattlerTarget].focusSashed)
|
||
{
|
||
gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1;
|
||
gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON;
|
||
gLastUsedItem = gBattleMons[gBattlerTarget].item;
|
||
}
|
||
else if (B_AFFECTION_MECHANICS == TRUE && gSpecialStatuses[gBattlerTarget].affectionEndured)
|
||
{
|
||
gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1;
|
||
gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED_AFFECTION;
|
||
}
|
||
else
|
||
{
|
||
gBattleMoveDamage = gBattleMons[gBattlerTarget].hp;
|
||
gMoveResultFlags |= MOVE_RESULT_ONE_HIT_KO;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
if (gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level)
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_KO_MISS;
|
||
else
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_KO_UNAFFECTED;
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Super Fang
|
||
static void Cmd_damagetohalftargethp(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gBattleMoveDamage = GetNonDynamaxHP(gBattlerTarget) / 2;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setsandstorm(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_SANDSTORM, FALSE))
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED;
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SANDSTORM;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_weatherdamage(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u32 ability = GetBattlerAbility(gBattlerAttacker);
|
||
|
||
gBattleMoveDamage = 0;
|
||
if (IsBattlerAlive(gBattlerAttacker) && WEATHER_HAS_EFFECT && ability != ABILITY_MAGIC_GUARD)
|
||
{
|
||
if (gBattleWeather & B_WEATHER_SANDSTORM)
|
||
{
|
||
if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ROCK)
|
||
&& !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GROUND)
|
||
&& !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_STEEL)
|
||
&& ability != ABILITY_SAND_VEIL
|
||
&& ability != ABILITY_SAND_FORCE
|
||
&& ability != ABILITY_SAND_RUSH
|
||
&& ability != ABILITY_OVERCOAT
|
||
&& !(gStatuses3[gBattlerAttacker] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER))
|
||
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES)
|
||
{
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 16;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
}
|
||
}
|
||
if (gBattleWeather & B_WEATHER_HAIL)
|
||
{
|
||
if (ability == ABILITY_ICE_BODY
|
||
&& !(gStatuses3[gBattlerAttacker] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER))
|
||
&& !BATTLER_MAX_HP(gBattlerAttacker)
|
||
&& !(gStatuses3[gBattlerAttacker] & STATUS3_HEAL_BLOCK))
|
||
{
|
||
gBattlerAbility = gBattlerAttacker;
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 16;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
gBattleMoveDamage *= -1;
|
||
}
|
||
else if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE)
|
||
&& ability != ABILITY_SNOW_CLOAK
|
||
&& ability != ABILITY_OVERCOAT
|
||
&& ability != ABILITY_ICE_BODY
|
||
&& !(gStatuses3[gBattlerAttacker] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER))
|
||
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES)
|
||
{
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 16;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
}
|
||
}
|
||
if (gBattleWeather & B_WEATHER_SNOW)
|
||
{
|
||
if (ability == ABILITY_ICE_BODY
|
||
&& !(gStatuses3[gBattlerAttacker] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER))
|
||
&& !BATTLER_MAX_HP(gBattlerAttacker)
|
||
&& !(gStatuses3[gBattlerAttacker] & STATUS3_HEAL_BLOCK))
|
||
{
|
||
gBattlerAbility = gBattlerAttacker;
|
||
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 16;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
gBattleMoveDamage *= -1;
|
||
}
|
||
}
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_tryinfatuating(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (GetBattlerAbility(gBattlerTarget) == ABILITY_OBLIVIOUS)
|
||
{
|
||
gBattlescriptCurrInstr = BattleScript_NotAffectedAbilityPopUp;
|
||
gLastUsedAbility = ABILITY_OBLIVIOUS;
|
||
RecordAbilityBattle(gBattlerTarget, ABILITY_OBLIVIOUS);
|
||
}
|
||
else
|
||
{
|
||
if (gBattleMons[gBattlerTarget].status2 & STATUS2_INFATUATION
|
||
|| !AreBattlersOfOppositeGender(gBattlerAttacker, gBattlerTarget))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattleMons[gBattlerTarget].status2 |= STATUS2_INFATUATED_WITH(gBattlerAttacker);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void Cmd_updatestatusicon(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
u32 battler;
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
if (cmd->battler != BS_ATTACKER_WITH_PARTNER)
|
||
{
|
||
battler = GetBattlerForBattleScript(cmd->battler);
|
||
BtlController_EmitStatusIconUpdate(battler, BUFFER_A, gBattleMons[battler].status1, gBattleMons[battler].status2);
|
||
MarkBattlerForControllerExec(battler);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
battler = gBattlerAttacker;
|
||
if (!(gAbsentBattlerFlags & gBitTable[battler]))
|
||
{
|
||
BtlController_EmitStatusIconUpdate(battler, BUFFER_A, gBattleMons[battler].status1, gBattleMons[battler].status2);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||
{
|
||
battler = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker)));
|
||
if (!(gAbsentBattlerFlags & gBitTable[battler]))
|
||
{
|
||
BtlController_EmitStatusIconUpdate(battler, BUFFER_A, gBattleMons[battler].status1, gBattleMons[battler].status2);
|
||
MarkBattlerForControllerExec(battler);
|
||
}
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setmist(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gSideTimers[GetBattlerSide(gBattlerAttacker)].mistTimer)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_FAILED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MIST_FAILED;
|
||
}
|
||
else
|
||
{
|
||
gSideTimers[GetBattlerSide(gBattlerAttacker)].mistTimer = 5;
|
||
gSideTimers[GetBattlerSide(gBattlerAttacker)].mistBattlerId = gBattlerAttacker;
|
||
gSideStatuses[GetBattlerSide(gBattlerAttacker)] |= SIDE_STATUS_MIST;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_MIST;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setfocusenergy(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_FAILED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FOCUS_ENERGY_FAILED;
|
||
}
|
||
else
|
||
{
|
||
gBattleMons[gBattlerAttacker].status2 |= STATUS2_FOCUS_ENERGY;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_GETTING_PUMPED;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_transformdataexecution(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gChosenMove = MOVE_UNAVAILABLE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
if (gBattleMons[gBattlerTarget].status2 & STATUS2_TRANSFORMED
|
||
|| gBattleStruct->illusion[gBattlerTarget].on
|
||
|| gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_FAILED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TRANSFORM_FAILED;
|
||
}
|
||
else
|
||
{
|
||
s32 i;
|
||
u8 *battleMonAttacker, *battleMonTarget;
|
||
u8 timesGotHit;
|
||
|
||
gBattleMons[gBattlerAttacker].status2 |= STATUS2_TRANSFORMED;
|
||
gDisableStructs[gBattlerAttacker].disabledMove = MOVE_NONE;
|
||
gDisableStructs[gBattlerAttacker].disableTimer = 0;
|
||
gDisableStructs[gBattlerAttacker].transformedMonPersonality = gBattleMons[gBattlerTarget].personality;
|
||
gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerTarget].isShiny;
|
||
gDisableStructs[gBattlerAttacker].mimickedMoves = 0;
|
||
gDisableStructs[gBattlerAttacker].usedMoves = 0;
|
||
|
||
timesGotHit = gBattleStruct->timesGotHit[GetBattlerSide(gBattlerTarget)][gBattlerPartyIndexes[gBattlerTarget]];
|
||
gBattleStruct->timesGotHit[GetBattlerSide(gBattlerAttacker)][gBattlerPartyIndexes[gBattlerAttacker]] = timesGotHit;
|
||
|
||
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerTarget].species)
|
||
|
||
battleMonAttacker = (u8 *)(&gBattleMons[gBattlerAttacker]);
|
||
battleMonTarget = (u8 *)(&gBattleMons[gBattlerTarget]);
|
||
|
||
for (i = 0; i < offsetof(struct BattlePokemon, pp); i++)
|
||
battleMonAttacker[i] = battleMonTarget[i];
|
||
|
||
gBattleStruct->overwrittenAbilities[gBattlerAttacker] = GetBattlerAbility(gBattlerTarget);
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].pp < 5)
|
||
gBattleMons[gBattlerAttacker].pp[i] = gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].pp;
|
||
else
|
||
gBattleMons[gBattlerAttacker].pp[i] = 5;
|
||
}
|
||
|
||
// update AI knowledge
|
||
RecordAllMoves(gBattlerAttacker);
|
||
RecordAbilityBattle(gBattlerAttacker, gBattleMons[gBattlerAttacker].ability);
|
||
|
||
BtlController_EmitResetActionMoveSelection(gBattlerAttacker, BUFFER_A, RESET_MOVE_SELECTION);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TRANSFORMED;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setsubstitute(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u32 hp = GetNonDynamaxMaxHP(gBattlerAttacker) / 4;
|
||
if (GetNonDynamaxMaxHP(gBattlerAttacker) / 4 == 0)
|
||
hp = 1;
|
||
|
||
if (gBattleMons[gBattlerAttacker].hp <= hp)
|
||
{
|
||
gBattleMoveDamage = 0;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SUBSTITUTE_FAILED;
|
||
}
|
||
else
|
||
{
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 4; // one bit value will only work for Pokémon which max hp can go to 1020(which is more than possible in games)
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
|
||
gBattleMons[gBattlerAttacker].status2 |= STATUS2_SUBSTITUTE;
|
||
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_WRAPPED;
|
||
gDisableStructs[gBattlerAttacker].substituteHP = gBattleMoveDamage;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_SUBSTITUTE;
|
||
gHitMarker |= HITMARKER_IGNORE_SUBSTITUTE;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_mimicattackcopy(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if ((gBattleMoves[gLastMoves[gBattlerTarget]].mimicBanned)
|
||
|| (gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED)
|
||
|| gLastMoves[gBattlerTarget] == MOVE_NONE
|
||
|| gLastMoves[gBattlerTarget] == MOVE_UNAVAILABLE)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
int i;
|
||
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gBattleMons[gBattlerAttacker].moves[i] == gLastMoves[gBattlerTarget])
|
||
break;
|
||
}
|
||
|
||
if (i == MAX_MON_MOVES)
|
||
{
|
||
gChosenMove = 0xFFFF;
|
||
gBattleMons[gBattlerAttacker].moves[gCurrMovePos] = gLastMoves[gBattlerTarget];
|
||
if (gBattleMoves[gLastMoves[gBattlerTarget]].pp < 5)
|
||
gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = gBattleMoves[gLastMoves[gBattlerTarget]].pp;
|
||
else
|
||
gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = 5;
|
||
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastMoves[gBattlerTarget])
|
||
|
||
gDisableStructs[gBattlerAttacker].mimickedMoves |= gBitTable[gCurrMovePos];
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
}
|
||
|
||
static bool32 InvalidMetronomeMove(u32 move)
|
||
{
|
||
return gBattleMoves[move].effect == EFFECT_PLACEHOLDER
|
||
|| gBattleMoves[move].metronomeBanned;
|
||
}
|
||
|
||
static void Cmd_metronome(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
#if B_METRONOME_MOVES >= GEN_9
|
||
u32 moveCount = MOVES_COUNT_GEN9;
|
||
#elif B_METRONOME_MOVES >= GEN_8
|
||
u32 moveCount = MOVES_COUNT_GEN8;
|
||
#elif B_METRONOME_MOVES >= GEN_7
|
||
u32 moveCount = MOVES_COUNT_GEN7;
|
||
#elif B_METRONOME_MOVES >= GEN_6
|
||
u32 moveCount = MOVES_COUNT_GEN6;
|
||
#elif B_METRONOME_MOVES >= GEN_5
|
||
u32 moveCount = MOVES_COUNT_GEN5;
|
||
#elif B_METRONOME_MOVES >= GEN_4
|
||
u32 moveCount = MOVES_COUNT_GEN4;
|
||
#elif B_METRONOME_MOVES >= GEN_3
|
||
u32 moveCount = MOVES_COUNT_GEN3;
|
||
#elif B_METRONOME_MOVES >= GEN_2
|
||
u32 moveCount = MOVES_COUNT_GEN2;
|
||
#else
|
||
u32 moveCount = MOVES_COUNT_GEN1;
|
||
#endif
|
||
|
||
gCurrentMove = RandomUniformExcept(RNG_METRONOME, 1, moveCount - 1, InvalidMetronomeMove);
|
||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||
SetAtkCancellerForCalledMove();
|
||
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
|
||
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
||
}
|
||
|
||
static void Cmd_dmgtolevel(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gBattleMoveDamage = gBattleMons[gBattlerAttacker].level;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_psywavedamageeffect(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
s32 randDamage = B_PSYWAVE_DMG >= GEN_6 ? (Random() % 101) : ((Random() % 11) * 10);
|
||
gBattleMoveDamage = gBattleMons[gBattlerAttacker].level * (randDamage + 50) / 100;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_counterdamagecalculator(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u8 sideAttacker = GetBattlerSide(gBattlerAttacker);
|
||
u8 sideTarget = GetBattlerSide(gProtectStructs[gBattlerAttacker].physicalBattlerId);
|
||
|
||
if (gProtectStructs[gBattlerAttacker].physicalDmg
|
||
&& sideAttacker != sideTarget
|
||
&& gBattleMons[gProtectStructs[gBattlerAttacker].physicalBattlerId].hp)
|
||
{
|
||
gBattleMoveDamage = gProtectStructs[gBattlerAttacker].physicalDmg * 2;
|
||
|
||
if (IsAffectedByFollowMe(gBattlerAttacker, sideTarget, gCurrentMove))
|
||
gBattlerTarget = gSideTimers[sideTarget].followmeTarget;
|
||
else
|
||
gBattlerTarget = gProtectStructs[gBattlerAttacker].physicalBattlerId;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
// A copy of Cmd with the physical -> special field changes
|
||
static void Cmd_mirrorcoatdamagecalculator(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u8 sideAttacker = GetBattlerSide(gBattlerAttacker);
|
||
u8 sideTarget = GetBattlerSide(gProtectStructs[gBattlerAttacker].specialBattlerId);
|
||
|
||
if (gProtectStructs[gBattlerAttacker].specialDmg
|
||
&& sideAttacker != sideTarget
|
||
&& gBattleMons[gProtectStructs[gBattlerAttacker].specialBattlerId].hp)
|
||
{
|
||
gBattleMoveDamage = gProtectStructs[gBattlerAttacker].specialDmg * 2;
|
||
|
||
if (IsAffectedByFollowMe(gBattlerAttacker, sideTarget, gCurrentMove))
|
||
gBattlerTarget = gSideTimers[sideTarget].followmeTarget;
|
||
else
|
||
gBattlerTarget = gProtectStructs[gBattlerAttacker].specialBattlerId;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_disablelastusedattack(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
s32 i;
|
||
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gBattleMons[gBattlerTarget].moves[i] == gLastMoves[gBattlerTarget])
|
||
break;
|
||
}
|
||
if (gDisableStructs[gBattlerTarget].disabledMove == MOVE_NONE
|
||
&& i != MAX_MON_MOVES && gBattleMons[gBattlerTarget].pp[i] != 0)
|
||
{
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerTarget].moves[i])
|
||
|
||
gDisableStructs[gBattlerTarget].disabledMove = gBattleMons[gBattlerTarget].moves[i];
|
||
if (B_DISABLE_TURNS >= GEN_5)
|
||
gDisableStructs[gBattlerTarget].disableTimer = 4;
|
||
else if (B_DISABLE_TURNS >= GEN_4)
|
||
gDisableStructs[gBattlerTarget].disableTimer = (Random() & 3) + 4; // 4-7 turns
|
||
else
|
||
gDisableStructs[gBattlerTarget].disableTimer = (Random() & 3) + 2; // 2-5 turns
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_trysetencore(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
s32 i;
|
||
|
||
if (IsMaxMove(gLastMoves[gBattlerTarget]) && !IsDynamaxed(gBattlerTarget))
|
||
{
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gBattleMons[gBattlerTarget].moves[i] == gBattleStruct->dynamax.baseMove[gBattlerTarget])
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gBattleMons[gBattlerTarget].moves[i] == gLastMoves[gBattlerTarget])
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ((gBattleMoves[gLastMoves[gBattlerTarget]].encoreBanned)
|
||
|| gLastMoves[gBattlerTarget] == MOVE_NONE
|
||
|| gLastMoves[gBattlerTarget] == MOVE_UNAVAILABLE)
|
||
{
|
||
i = MAX_MON_MOVES;
|
||
}
|
||
|
||
if (gDisableStructs[gBattlerTarget].encoredMove == MOVE_NONE
|
||
&& i != MAX_MON_MOVES && gBattleMons[gBattlerTarget].pp[i] != 0)
|
||
{
|
||
gDisableStructs[gBattlerTarget].encoredMove = gBattleMons[gBattlerTarget].moves[i];
|
||
gDisableStructs[gBattlerTarget].encoredMovePos = i;
|
||
gDisableStructs[gBattlerTarget].encoreTimer = 3;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_painsplitdmgcalc(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (!(DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)))
|
||
{
|
||
s32 hpDiff = (gBattleMons[gBattlerAttacker].hp + GetNonDynamaxHP(gBattlerTarget)) / 2;
|
||
s32 painSplitHp = gBattleMoveDamage = GetNonDynamaxHP(gBattlerTarget) - hpDiff;
|
||
u8 *storeLoc = (void *)(&gBattleScripting.painSplitHp);
|
||
|
||
storeLoc[0] = (painSplitHp);
|
||
storeLoc[1] = (painSplitHp & 0x0000FF00) >> 8;
|
||
storeLoc[2] = (painSplitHp & 0x00FF0000) >> 16;
|
||
storeLoc[3] = (painSplitHp & 0xFF000000) >> 24;
|
||
|
||
gBattleMoveDamage = gBattleMons[gBattlerAttacker].hp - hpDiff;
|
||
gSpecialStatuses[gBattlerTarget].shellBellDmg = IGNORE_SHELL_BELL;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
// Conversion 2
|
||
static void Cmd_settypetorandomresistance(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gLastLandedMoves[gBattlerAttacker] == MOVE_NONE
|
||
|| gLastLandedMoves[gBattlerAttacker] == MOVE_UNAVAILABLE)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else if (gBattleMoves[gLastLandedMoves[gBattlerAttacker]].twoTurnMove
|
||
&& gBattleMons[gLastHitBy[gBattlerAttacker]].status2 & STATUS2_MULTIPLETURNS)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
u32 i, resistTypes = 0;
|
||
u32 hitByType = gLastHitByType[gBattlerAttacker];
|
||
|
||
for (i = 0; i < NUMBER_OF_MON_TYPES; i++) // Find all types that resist.
|
||
{
|
||
switch (GetTypeModifier(hitByType, i))
|
||
{
|
||
case UQ_4_12(0):
|
||
case UQ_4_12(0.5):
|
||
resistTypes |= gBitTable[i];
|
||
break;
|
||
}
|
||
}
|
||
|
||
while (resistTypes != 0)
|
||
{
|
||
i = Random() % NUMBER_OF_MON_TYPES;
|
||
if (resistTypes & gBitTable[i])
|
||
{
|
||
if (IS_BATTLER_OF_TYPE(gBattlerAttacker, i))
|
||
{
|
||
resistTypes &= ~(gBitTable[i]); // Type resists, but the user is already of this type.
|
||
}
|
||
else
|
||
{
|
||
SET_BATTLER_TYPE(gBattlerAttacker, i);
|
||
PREPARE_TYPE_BUFFER(gBattleTextBuff1, i);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setalwayshitflag(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gStatuses3[gBattlerTarget] &= ~STATUS3_ALWAYS_HITS;
|
||
gStatuses3[gBattlerTarget] |= STATUS3_ALWAYS_HITS_TURN(2);
|
||
gDisableStructs[gBattlerTarget].battlerWithSureHit = gBattlerAttacker;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
// Sketch
|
||
static void Cmd_copymovepermanently(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
gChosenMove = MOVE_UNAVAILABLE;
|
||
|
||
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED)
|
||
&& gLastPrintedMoves[gBattlerTarget] != MOVE_UNAVAILABLE
|
||
&& !gBattleMoves[gLastPrintedMoves[gBattlerTarget]].sketchBanned)
|
||
{
|
||
s32 i;
|
||
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gBattleMons[gBattlerAttacker].moves[i] == MOVE_SKETCH)
|
||
continue;
|
||
if (gBattleMons[gBattlerAttacker].moves[i] == gLastPrintedMoves[gBattlerTarget])
|
||
break;
|
||
}
|
||
|
||
if (i != MAX_MON_MOVES)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else // sketch worked
|
||
{
|
||
struct MovePpInfo movePpData;
|
||
|
||
gBattleMons[gBattlerAttacker].moves[gCurrMovePos] = gLastPrintedMoves[gBattlerTarget];
|
||
gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = gBattleMoves[gLastPrintedMoves[gBattlerTarget]].pp;
|
||
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
movePpData.moves[i] = gBattleMons[gBattlerAttacker].moves[i];
|
||
movePpData.pp[i] = gBattleMons[gBattlerAttacker].pp[i];
|
||
}
|
||
movePpData.ppBonuses = gBattleMons[gBattlerAttacker].ppBonuses;
|
||
|
||
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_MOVES_PP_BATTLE, 0, sizeof(movePpData), &movePpData);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastPrintedMoves[gBattlerTarget])
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_trychoosesleeptalkmove(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u32 i, unusableMovesBits = 0, movePosition;
|
||
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].sleepTalkBanned
|
||
|| gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].twoTurnMove)
|
||
{
|
||
unusableMovesBits |= gBitTable[i];
|
||
}
|
||
}
|
||
|
||
unusableMovesBits = CheckMoveLimitations(gBattlerAttacker, unusableMovesBits, ~MOVE_LIMITATION_PP);
|
||
if (unusableMovesBits == ALL_MOVES_MASK) // all 4 moves cannot be chosen
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else // at least one move can be chosen
|
||
{
|
||
// Set Sleep Talk as used move, so it works with Last Resort.
|
||
gDisableStructs[gBattlerAttacker].usedMoves |= gBitTable[gCurrMovePos];
|
||
do
|
||
{
|
||
movePosition = MOD(Random(), MAX_MON_MOVES);
|
||
} while ((gBitTable[movePosition] & unusableMovesBits));
|
||
|
||
gCalledMove = gBattleMons[gBattlerAttacker].moves[movePosition];
|
||
gCurrMovePos = movePosition;
|
||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setdestinybond(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gBattleMons[gBattlerAttacker].status2 |= STATUS2_DESTINY_BOND;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void TrySetDestinyBondToHappen(void)
|
||
{
|
||
u8 sideAttacker = GetBattlerSide(gBattlerAttacker);
|
||
u8 sideTarget = GetBattlerSide(gBattlerTarget);
|
||
if (gBattleMons[gBattlerTarget].status2 & STATUS2_DESTINY_BOND
|
||
&& sideAttacker != sideTarget
|
||
&& !(gHitMarker & HITMARKER_GRUDGE))
|
||
{
|
||
gHitMarker |= HITMARKER_DESTINYBOND;
|
||
}
|
||
}
|
||
|
||
static void Cmd_trysetdestinybondtohappen(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
TrySetDestinyBondToHappen();
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_settailwind(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u8 side = GetBattlerSide(gBattlerAttacker);
|
||
|
||
if (!(gSideStatuses[side] & SIDE_STATUS_TAILWIND))
|
||
{
|
||
gSideStatuses[side] |= SIDE_STATUS_TAILWIND;
|
||
gSideTimers[side].tailwindBattlerId = gBattlerAttacker;
|
||
gSideTimers[side].tailwindTimer = B_TAILWIND_TURNS >= GEN_5 ? 4 : 3;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_tryspiteppreduce(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gLastMoves[gBattlerTarget] != MOVE_NONE
|
||
&& gLastMoves[gBattlerTarget] != MOVE_UNAVAILABLE)
|
||
{
|
||
s32 i;
|
||
|
||
// Get move slot to reduce PP.
|
||
if (IsMaxMove(gLastMoves[gBattlerTarget]))
|
||
{
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gBattleStruct->dynamax.baseMove[gBattlerTarget] == gBattleMons[gBattlerTarget].moves[i])
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gLastMoves[gBattlerTarget] == gBattleMons[gBattlerTarget].moves[i])
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (i != MAX_MON_MOVES && gBattleMons[gBattlerTarget].pp[i] > (B_CAN_SPITE_FAIL >= GEN_4 ? 0 : 1))
|
||
{
|
||
s32 ppToDeduct = B_PP_REDUCED_BY_SPITE >= GEN_4 ? 4 : (Random() & 3) + 2;
|
||
// G-Max Depletion only deducts 2 PP.
|
||
if (IsMaxMove(gCurrentMove) && gBattleMoves[gCurrentMove].argument == MAX_EFFECT_SPITE)
|
||
ppToDeduct = 2;
|
||
|
||
if (gBattleMons[gBattlerTarget].pp[i] < ppToDeduct)
|
||
ppToDeduct = gBattleMons[gBattlerTarget].pp[i];
|
||
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastMoves[gBattlerTarget])
|
||
|
||
ConvertIntToDecimalStringN(gBattleTextBuff2, ppToDeduct, STR_CONV_MODE_LEFT_ALIGN, 1);
|
||
|
||
PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff2, 1, ppToDeduct)
|
||
|
||
gBattleMons[gBattlerTarget].pp[i] -= ppToDeduct;
|
||
|
||
// if (MOVE_IS_PERMANENT(gBattlerTarget, i)), but backwards
|
||
if (!(gDisableStructs[gBattlerTarget].mimickedMoves & gBitTable[i])
|
||
&& !(gBattleMons[gBattlerTarget].status2 & STATUS2_TRANSFORMED))
|
||
{
|
||
BtlController_EmitSetMonData(gBattlerTarget, BUFFER_A, REQUEST_PPMOVE1_BATTLE + i, 0, sizeof(gBattleMons[gBattlerTarget].pp[i]), &gBattleMons[gBattlerTarget].pp[i]);
|
||
MarkBattlerForControllerExec(gBattlerTarget);
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
// Don't cut off Sky Drop if pp is brought to zero.
|
||
if (gBattleMons[gBattlerTarget].pp[i] == 0 && gBattleStruct->skyDropTargets[gBattlerTarget] == 0xFF)
|
||
CancelMultiTurnMoves(gBattlerTarget);
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_healpartystatus(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u32 zero = 0;
|
||
u32 battler;
|
||
u8 toHeal = 0;
|
||
|
||
if (gCurrentMove == MOVE_HEAL_BELL)
|
||
{
|
||
struct Pokemon *party = GetBattlerParty(gBattlerAttacker);
|
||
s32 i;
|
||
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_BELL;
|
||
|
||
if (GetBattlerAbility(gBattlerAttacker) != ABILITY_SOUNDPROOF)
|
||
{
|
||
gBattleMons[gBattlerAttacker].status1 = 0;
|
||
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE;
|
||
}
|
||
else
|
||
{
|
||
RecordAbilityBattle(gBattlerAttacker, gBattleMons[gBattlerAttacker].ability);
|
||
gBattleCommunication[MULTISTRING_CHOOSER] |= B_MSG_BELL_SOUNDPROOF_ATTACKER;
|
||
}
|
||
|
||
battler = gBattleScripting.battler = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker)));
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||
&& !(gAbsentBattlerFlags & gBitTable[battler]))
|
||
{
|
||
if (GetBattlerAbility(battler) != ABILITY_SOUNDPROOF)
|
||
{
|
||
gBattleMons[battler].status1 = 0;
|
||
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
|
||
}
|
||
else
|
||
{
|
||
RecordAbilityBattle(battler, gBattleMons[battler].ability);
|
||
gBattleCommunication[MULTISTRING_CHOOSER] |= B_MSG_BELL_SOUNDPROOF_PARTNER;
|
||
}
|
||
}
|
||
|
||
// Because the above MULTISTRING_CHOOSER are ORd, if both are set then it will be B_MSG_BELL_BOTH_SOUNDPROOF
|
||
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
u16 species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG);
|
||
u8 abilityNum = GetMonData(&party[i], MON_DATA_ABILITY_NUM);
|
||
|
||
if (species != SPECIES_NONE && species != SPECIES_EGG)
|
||
{
|
||
u16 ability;
|
||
|
||
if (gBattlerPartyIndexes[gBattlerAttacker] == i)
|
||
ability = GetBattlerAbility(gBattlerAttacker);
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||
&& gBattlerPartyIndexes[battler] == i
|
||
&& !(gAbsentBattlerFlags & gBitTable[battler]))
|
||
ability = GetBattlerAbility(battler);
|
||
else
|
||
ability = GetAbilityBySpecies(species, abilityNum);
|
||
|
||
if (ability != ABILITY_SOUNDPROOF)
|
||
toHeal |= (1 << i);
|
||
}
|
||
}
|
||
}
|
||
else // Aromatherapy
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SOOTHING_AROMA;
|
||
toHeal = (1 << PARTY_SIZE) - 1;
|
||
|
||
gBattleMons[gBattlerAttacker].status1 = 0;
|
||
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE;
|
||
|
||
battler = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker)));
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||
&& !(gAbsentBattlerFlags & gBitTable[battler]))
|
||
{
|
||
gBattleMons[battler].status1 = 0;
|
||
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
|
||
}
|
||
|
||
}
|
||
|
||
if (toHeal)
|
||
{
|
||
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_STATUS_BATTLE, toHeal, sizeof(zero), &zero);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_cursetarget(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gBattleMons[gBattlerTarget].status2 & STATUS2_CURSED)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattleMons[gBattlerTarget].status2 |= STATUS2_CURSED;
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 2;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_trysetspikes(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u8 targetSide = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
|
||
|
||
if (gSideTimers[targetSide].spikesAmount == 3)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gSideStatuses[targetSide] |= SIDE_STATUS_SPIKES;
|
||
gSideTimers[targetSide].spikesAmount++;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setforesight(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gBattleMons[gBattlerTarget].status2 |= STATUS2_FORESIGHT;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_trysetperishsong(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
s32 i;
|
||
s32 notAffectedCount = 0;
|
||
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (gStatuses3[i] & STATUS3_PERISH_SONG
|
||
|| GetBattlerAbility(i) == ABILITY_SOUNDPROOF
|
||
|| BlocksPrankster(gCurrentMove, gBattlerAttacker, i, TRUE))
|
||
{
|
||
notAffectedCount++;
|
||
}
|
||
else
|
||
{
|
||
gStatuses3[i] |= STATUS3_PERISH_SONG;
|
||
gDisableStructs[i].perishSongTimer = 3;
|
||
}
|
||
}
|
||
|
||
if (notAffectedCount == gBattlersCount)
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_handlerollout(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
{
|
||
CancelMultiTurnMoves(gBattlerAttacker);
|
||
gBattlescriptCurrInstr = BattleScript_MoveMissedPause;
|
||
}
|
||
else
|
||
{
|
||
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)) // First hit.
|
||
{
|
||
gDisableStructs[gBattlerAttacker].rolloutTimer = 5;
|
||
gDisableStructs[gBattlerAttacker].rolloutTimerStartValue = 5;
|
||
gBattleMons[gBattlerAttacker].status2 |= STATUS2_MULTIPLETURNS;
|
||
gLockedMoves[gBattlerAttacker] = gCurrentMove;
|
||
}
|
||
if (--gDisableStructs[gBattlerAttacker].rolloutTimer == 0) // Last hit.
|
||
{
|
||
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_MULTIPLETURNS;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_jumpifconfusedandstatmaxed(void)
|
||
{
|
||
CMD_ARGS(u8 stat, const u8 *jumpInstr);
|
||
|
||
if (gBattleMons[gBattlerTarget].status2 & STATUS2_CONFUSION
|
||
&& !CompareStat(gBattlerTarget, cmd->stat, MAX_STAT_STAGE, CMP_LESS_THAN))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr; // Fails if we're confused AND stat cannot be raised
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_handlefurycutter(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
|
||
{
|
||
gDisableStructs[gBattlerAttacker].furyCutterCounter = 0;
|
||
gBattlescriptCurrInstr = BattleScript_MoveMissedPause;
|
||
}
|
||
else
|
||
{
|
||
if (gDisableStructs[gBattlerAttacker].furyCutterCounter != 5
|
||
&& gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT) // Don't increment counter on first hit
|
||
gDisableStructs[gBattlerAttacker].furyCutterCounter++;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setembargo(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gStatuses3[gBattlerTarget] & STATUS3_EMBARGO)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gStatuses3[gBattlerTarget] |= STATUS3_EMBARGO;
|
||
gDisableStructs[gBattlerTarget].embargoTimer = 5;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_presentdamagecalculation(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u32 rand = Random() & 0xFF;
|
||
|
||
/* Don't reroll present effect/power for the second hit of Parental Bond.
|
||
* Not sure if this is the correct behaviour, but bulbapedia states
|
||
* that if present heals the foe, it doesn't strike twice, and if it deals
|
||
* damage, the second strike will always deal damage too. This is a simple way
|
||
* to replicate that effect.
|
||
*/
|
||
if (gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_2ND_HIT)
|
||
{
|
||
if (rand < 102)
|
||
{
|
||
gBattleStruct->presentBasePower = 40;
|
||
}
|
||
else if (rand < 178)
|
||
{
|
||
gBattleStruct->presentBasePower = 80;
|
||
}
|
||
else if (rand < 204)
|
||
{
|
||
gBattleStruct->presentBasePower = 120;
|
||
}
|
||
else
|
||
{
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerTarget) / 4;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
gBattleMoveDamage *= -1;
|
||
gBattleStruct->presentBasePower = 0;
|
||
}
|
||
}
|
||
|
||
if (gBattleStruct->presentBasePower)
|
||
{
|
||
gBattlescriptCurrInstr = BattleScript_HitFromCritCalc;
|
||
}
|
||
else if (gBattleMons[gBattlerTarget].maxHP == gBattleMons[gBattlerTarget].hp)
|
||
{
|
||
gBattlescriptCurrInstr = BattleScript_AlreadyAtFullHp;
|
||
}
|
||
else
|
||
{
|
||
gMoveResultFlags &= ~MOVE_RESULT_DOESNT_AFFECT_FOE;
|
||
gBattlescriptCurrInstr = BattleScript_PresentHealTarget;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setsafeguard(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gSideStatuses[GetBattlerSide(gBattlerAttacker)] & SIDE_STATUS_SAFEGUARD)
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SIDE_STATUS_FAILED;
|
||
}
|
||
else
|
||
{
|
||
gSideStatuses[GetBattlerSide(gBattlerAttacker)] |= SIDE_STATUS_SAFEGUARD;
|
||
gSideTimers[GetBattlerSide(gBattlerAttacker)].safeguardTimer = 5;
|
||
gSideTimers[GetBattlerSide(gBattlerAttacker)].safeguardBattlerId = gBattlerAttacker;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_SAFEGUARD;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_magnitudedamagecalculation(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u32 magnitude = Random() % 100;
|
||
|
||
if (magnitude < 5)
|
||
{
|
||
gBattleStruct->magnitudeBasePower = 10;
|
||
magnitude = 4;
|
||
}
|
||
else if (magnitude < 15)
|
||
{
|
||
gBattleStruct->magnitudeBasePower = 30;
|
||
magnitude = 5;
|
||
}
|
||
else if (magnitude < 35)
|
||
{
|
||
gBattleStruct->magnitudeBasePower = 50;
|
||
magnitude = 6;
|
||
}
|
||
else if (magnitude < 65)
|
||
{
|
||
gBattleStruct->magnitudeBasePower = 70;
|
||
magnitude = 7;
|
||
}
|
||
else if (magnitude < 85)
|
||
{
|
||
gBattleStruct->magnitudeBasePower = 90;
|
||
magnitude = 8;
|
||
}
|
||
else if (magnitude < 95)
|
||
{
|
||
gBattleStruct->magnitudeBasePower = 110;
|
||
magnitude = 9;
|
||
}
|
||
else
|
||
{
|
||
gBattleStruct->magnitudeBasePower = 150;
|
||
magnitude = 10;
|
||
}
|
||
|
||
PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 2, magnitude)
|
||
|
||
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
|
||
{
|
||
if (gBattlerTarget == gBattlerAttacker)
|
||
continue;
|
||
if (!(gAbsentBattlerFlags & gBitTable[gBattlerTarget])) // A valid target was found.
|
||
break;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_jumpifnopursuitswitchdmg(void)
|
||
{
|
||
CMD_ARGS(const u8 *jumpInstr);
|
||
|
||
if (gMultiHitCounter == 1)
|
||
{
|
||
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
|
||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||
else
|
||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
|
||
}
|
||
else
|
||
{
|
||
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
|
||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||
else
|
||
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
|
||
}
|
||
|
||
if (gChosenActionByBattler[gBattlerTarget] == B_ACTION_USE_MOVE
|
||
&& gBattlerAttacker == *(gBattleStruct->moveTarget + gBattlerTarget)
|
||
&& !(gBattleMons[gBattlerTarget].status1 & (STATUS1_SLEEP | STATUS1_FREEZE))
|
||
&& gBattleMons[gBattlerAttacker].hp
|
||
&& !gDisableStructs[gBattlerTarget].truantCounter
|
||
&& gBattleMoves[gChosenMoveByBattler[gBattlerTarget]].effect == EFFECT_PURSUIT)
|
||
{
|
||
s32 i;
|
||
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (gBattlerByTurnOrder[i] == gBattlerTarget)
|
||
gActionsByTurnOrder[i] = B_ACTION_TRY_FINISH;
|
||
}
|
||
|
||
gCurrentMove = gChosenMoveByBattler[gBattlerTarget];
|
||
gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerTarget);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
gBattleScripting.animTurn = 1;
|
||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setsunny(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_SUN, FALSE))
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED;
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SUNLIGHT;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
// Belly Drum, Fillet Away
|
||
static void Cmd_halvehp(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u32 halfHp = GetNonDynamaxMaxHP(gBattlerAttacker) / 2;
|
||
|
||
if (!(GetNonDynamaxMaxHP(gBattlerAttacker) / 2))
|
||
halfHp = 1;
|
||
|
||
if (gBattleMons[gBattlerAttacker].hp > halfHp)
|
||
{
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 2;
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
// Psych Up
|
||
static void Cmd_copyfoestats(void)
|
||
{
|
||
CMD_ARGS(const u8 *unused);
|
||
|
||
s32 i;
|
||
|
||
for (i = 0; i < NUM_BATTLE_STATS; i++)
|
||
{
|
||
gBattleMons[gBattlerAttacker].statStages[i] = gBattleMons[gBattlerTarget].statStages[i];
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr; // Has an unused jump ptr(possibly for a failed attempt) parameter.
|
||
}
|
||
|
||
static void Cmd_rapidspinfree(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u8 atkSide = GetBattlerSide(gBattlerAttacker);
|
||
|
||
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_WRAPPED)
|
||
{
|
||
gBattleScripting.battler = gBattlerTarget;
|
||
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_WRAPPED;
|
||
gBattlerTarget = *(gBattleStruct->wrappedBy + gBattlerAttacker);
|
||
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleStruct->wrappedMove[gBattlerAttacker]);
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_WrapFree;
|
||
}
|
||
else if (gStatuses3[gBattlerAttacker] & STATUS3_LEECHSEED)
|
||
{
|
||
gStatuses3[gBattlerAttacker] &= ~STATUS3_LEECHSEED;
|
||
gStatuses3[gBattlerAttacker] &= ~STATUS3_LEECHSEED_BATTLER;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_LeechSeedFree;
|
||
}
|
||
else if (gSideStatuses[atkSide] & SIDE_STATUS_SPIKES)
|
||
{
|
||
gSideStatuses[atkSide] &= ~SIDE_STATUS_SPIKES;
|
||
gSideTimers[atkSide].spikesAmount = 0;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_SpikesFree;
|
||
}
|
||
else if (gSideStatuses[atkSide] & SIDE_STATUS_TOXIC_SPIKES)
|
||
{
|
||
gSideStatuses[atkSide] &= ~SIDE_STATUS_TOXIC_SPIKES;
|
||
gSideTimers[atkSide].toxicSpikesAmount = 0;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_ToxicSpikesFree;
|
||
}
|
||
else if (gSideStatuses[atkSide] & SIDE_STATUS_STICKY_WEB)
|
||
{
|
||
gSideStatuses[atkSide] &= ~SIDE_STATUS_STICKY_WEB;
|
||
gSideTimers[atkSide].stickyWebAmount = 0;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_StickyWebFree;
|
||
}
|
||
else if (gSideStatuses[atkSide] & SIDE_STATUS_STEALTH_ROCK)
|
||
{
|
||
gSideStatuses[atkSide] &= ~SIDE_STATUS_STEALTH_ROCK;
|
||
gSideTimers[atkSide].stealthRockAmount = 0;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_StealthRockFree;
|
||
}
|
||
else if (gSideStatuses[atkSide] & SIDE_STATUS_STEELSURGE)
|
||
{
|
||
gSideStatuses[atkSide] &= ~SIDE_STATUS_STEELSURGE;
|
||
gSideTimers[atkSide].steelsurgeAmount = 0;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_SteelsurgeFree;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setdefensecurlbit(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gBattleMons[gBattlerAttacker].status2 |= STATUS2_DEFENSE_CURL;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_recoverbasedonsunlight(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
gBattlerTarget = gBattlerAttacker;
|
||
if (gBattleMons[gBattlerAttacker].hp != gBattleMons[gBattlerAttacker].maxHP)
|
||
{
|
||
if (gCurrentMove == MOVE_SHORE_UP)
|
||
{
|
||
if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM)
|
||
gBattleMoveDamage = 20 * GetNonDynamaxMaxHP(gBattlerAttacker) / 30;
|
||
else
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 2;
|
||
}
|
||
else
|
||
{
|
||
if (!(gBattleWeather & B_WEATHER_ANY) || !WEATHER_HAS_EFFECT || GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_UTILITY_UMBRELLA)
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 2;
|
||
else if (gBattleWeather & B_WEATHER_SUN)
|
||
gBattleMoveDamage = 20 * GetNonDynamaxMaxHP(gBattlerAttacker) / 30;
|
||
else // not sunny weather
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 4;
|
||
}
|
||
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = 1;
|
||
gBattleMoveDamage *= -1;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setstickyweb(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u8 targetSide = GetBattlerSide(gBattlerTarget);
|
||
|
||
if (gSideStatuses[targetSide] & SIDE_STATUS_STICKY_WEB)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gSideStatuses[targetSide] |= SIDE_STATUS_STICKY_WEB;
|
||
gSideTimers[targetSide].stickyWebBattlerId = gBattlerAttacker; // For Mirror Armor
|
||
gSideTimers[targetSide].stickyWebBattlerSide = GetBattlerSide(gBattlerAttacker); // For Court Change/Defiant - set this to the user's side
|
||
gSideTimers[targetSide].stickyWebAmount = 1;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_selectfirstvalidtarget(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
|
||
{
|
||
if (gBattlerTarget == gBattlerAttacker && !(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove) & MOVE_TARGET_USER))
|
||
continue;
|
||
if (IsBattlerAlive(gBattlerTarget))
|
||
break;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_trysetfutureattack(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gWishFutureKnock.futureSightCounter[gBattlerTarget] != 0)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gSideStatuses[GetBattlerSide(gBattlerTarget)] |= SIDE_STATUS_FUTUREATTACK;
|
||
gWishFutureKnock.futureSightMove[gBattlerTarget] = gCurrentMove;
|
||
gWishFutureKnock.futureSightAttacker[gBattlerTarget] = gBattlerAttacker;
|
||
gWishFutureKnock.futureSightCounter[gBattlerTarget] = 3;
|
||
|
||
if (gCurrentMove == MOVE_DOOM_DESIRE)
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOOM_DESIRE;
|
||
else
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FUTURE_SIGHT;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_trydobeatup(void)
|
||
{
|
||
#if B_BEAT_UP >= GEN_5
|
||
CMD_ARGS();
|
||
|
||
gBattleStruct->beatUpSlot++;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
#else
|
||
CMD_ARGS(const u8 *endInstr, const u8 *failInstr);
|
||
struct Pokemon *party = GetBattlerParty(gBattlerAttacker);
|
||
|
||
if (gBattleMons[gBattlerTarget].hp == 0)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->endInstr;
|
||
}
|
||
else
|
||
{
|
||
u8 beforeLoop = gBattleCommunication[0];
|
||
for (;gBattleCommunication[0] < PARTY_SIZE; gBattleCommunication[0]++)
|
||
{
|
||
if (GetMonData(&party[gBattleCommunication[0]], MON_DATA_HP)
|
||
&& GetMonData(&party[gBattleCommunication[0]], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
|
||
&& GetMonData(&party[gBattleCommunication[0]], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG
|
||
&& !GetMonData(&party[gBattleCommunication[0]], MON_DATA_STATUS))
|
||
break;
|
||
}
|
||
if (gBattleCommunication[0] < PARTY_SIZE)
|
||
{
|
||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattleCommunication[0])
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
gBattleMoveDamage = gSpeciesInfo[GetMonData(&party[gBattleCommunication[0]], MON_DATA_SPECIES)].baseAttack;
|
||
gBattleMoveDamage *= gBattleMoves[gCurrentMove].power;
|
||
gBattleMoveDamage *= (GetMonData(&party[gBattleCommunication[0]], MON_DATA_LEVEL) * 2 / 5 + 2);
|
||
gBattleMoveDamage /= gSpeciesInfo[gBattleMons[gBattlerTarget].species].baseDefense;
|
||
gBattleMoveDamage = (gBattleMoveDamage / 50) + 2;
|
||
if (gProtectStructs[gBattlerAttacker].helpingHand)
|
||
gBattleMoveDamage = gBattleMoveDamage * 15 / 10;
|
||
|
||
gBattleCommunication[0]++;
|
||
}
|
||
else if (beforeLoop != 0)
|
||
gBattlescriptCurrInstr = cmd->endInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
static void Cmd_setsemiinvulnerablebit(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
switch (gCurrentMove)
|
||
{
|
||
case MOVE_FLY:
|
||
case MOVE_BOUNCE:
|
||
case MOVE_SKY_DROP:
|
||
gStatuses3[gBattlerAttacker] |= STATUS3_ON_AIR;
|
||
break;
|
||
case MOVE_DIG:
|
||
gStatuses3[gBattlerAttacker] |= STATUS3_UNDERGROUND;
|
||
break;
|
||
case MOVE_DIVE:
|
||
gStatuses3[gBattlerAttacker] |= STATUS3_UNDERWATER;
|
||
break;
|
||
case MOVE_PHANTOM_FORCE:
|
||
case MOVE_SHADOW_FORCE:
|
||
gStatuses3[gBattlerAttacker] |= STATUS3_PHANTOM_FORCE;
|
||
break;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_clearsemiinvulnerablebit(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gStatuses3[gBattlerAttacker] &= ~STATUS3_SEMI_INVULNERABLE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setminimize(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (gHitMarker & HITMARKER_OBEYS)
|
||
gStatuses3[gBattlerAttacker] |= STATUS3_MINIMIZED;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_sethail(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_HAIL, FALSE))
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED;
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_HAIL;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_trymemento(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (B_MEMENTO_FAIL >= GEN_4
|
||
&& (gBattleCommunication[MISS_TYPE] == B_MSG_PROTECTED
|
||
|| gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE
|
||
|| IsBattlerProtected(gBattlerTarget, gCurrentMove)
|
||
|| DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)))
|
||
{
|
||
// Failed, target was protected.
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else if (B_MEMENTO_FAIL < GEN_4
|
||
&& gBattleMons[gBattlerTarget].statStages[STAT_ATK] == MIN_STAT_STAGE
|
||
&& gBattleMons[gBattlerTarget].statStages[STAT_SPATK] == MIN_STAT_STAGE
|
||
&& gBattleCommunication[MISS_TYPE] != B_MSG_PROTECTED)
|
||
{
|
||
// Failed, unprotected target already has minimum Attack and Special Attack.
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
// Success, drop user's HP bar to 0
|
||
gBattleMoveDamage = gBattleMons[gBattlerAttacker].hp;
|
||
BtlController_EmitHealthBarUpdate(gBattlerAttacker, BUFFER_A, INSTANT_HP_BAR_DROP);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
// Follow Me
|
||
static void Cmd_setforcedtarget(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gSideTimers[GetBattlerSide(gBattlerTarget)].followmeTimer = 1;
|
||
gSideTimers[GetBattlerSide(gBattlerTarget)].followmeTarget = gBattlerTarget;
|
||
gSideTimers[GetBattlerSide(gBattlerTarget)].followmePowder = gBattleMoves[gCurrentMove].powderMove;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_setcharge(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
gStatuses3[battler] |= STATUS3_CHARGED_UP;
|
||
gDisableStructs[battler].chargeTimer = 2;
|
||
gBattlescriptCurrInstr++;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
// Nature Power
|
||
static void Cmd_callterrainattack(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||
gCurrentMove = GetNaturePowerMove();
|
||
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
||
BattleScriptPush(gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
u16 GetNaturePowerMove(void)
|
||
{
|
||
if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)
|
||
return MOVE_MOONBLAST;
|
||
else if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)
|
||
return MOVE_THUNDERBOLT;
|
||
else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)
|
||
return MOVE_ENERGY_BALL;
|
||
else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)
|
||
return MOVE_PSYCHIC;
|
||
else if (sNaturePowerMoves[gBattleTerrain] == MOVE_NONE)
|
||
return MOVE_TRI_ATTACK;
|
||
return sNaturePowerMoves[gBattleTerrain];
|
||
}
|
||
|
||
// Refresh
|
||
static void Cmd_cureifburnedparalysedorpoisoned(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gBattleMons[gBattlerAttacker].status1 & (STATUS1_POISON | STATUS1_BURN | STATUS1_PARALYSIS | STATUS1_TOXIC_POISON | STATUS1_FROSTBITE))
|
||
{
|
||
gBattleMons[gBattlerAttacker].status1 = 0;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerAttacker].status1), &gBattleMons[gBattlerAttacker].status1);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_settorment(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gBattleMons[gBattlerTarget].status2 & STATUS2_TORMENT
|
||
|| IsDynamaxed(gBattlerTarget))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattleMons[gBattlerTarget].status2 |= STATUS2_TORMENT;
|
||
gDisableStructs[gBattlerTarget].tormentTimer = PERMANENT_TORMENT; // permanent
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_jumpifnodamage(void)
|
||
{
|
||
CMD_ARGS(const u8 *jumpInstr);
|
||
|
||
if (gProtectStructs[gBattlerAttacker].physicalDmg || gProtectStructs[gBattlerAttacker].specialDmg)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
}
|
||
|
||
static void Cmd_settaunt(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (B_OBLIVIOUS_TAUNT >= GEN_6 && GetBattlerAbility(gBattlerTarget) == ABILITY_OBLIVIOUS)
|
||
{
|
||
gBattlescriptCurrInstr = BattleScript_NotAffectedAbilityPopUp;
|
||
gLastUsedAbility = ABILITY_OBLIVIOUS;
|
||
RecordAbilityBattle(gBattlerTarget, ABILITY_OBLIVIOUS);
|
||
}
|
||
else if (gDisableStructs[gBattlerTarget].tauntTimer == 0)
|
||
{
|
||
u8 turns;
|
||
if (B_TAUNT_TURNS >= GEN_5)
|
||
{
|
||
turns = 4;
|
||
if (GetBattlerTurnOrderNum(gBattlerTarget) > GetBattlerTurnOrderNum(gBattlerAttacker))
|
||
turns--; // If the target hasn't yet moved this turn, Taunt lasts for only three turns (source: Bulbapedia)
|
||
}
|
||
else if (B_TAUNT_TURNS >= GEN_4)
|
||
{
|
||
turns = (Random() & 2) + 3;
|
||
}
|
||
else
|
||
{
|
||
turns = 2;
|
||
}
|
||
|
||
gDisableStructs[gBattlerTarget].tauntTimer = turns;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_trysethelpinghand(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker)));
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||
&& !(gAbsentBattlerFlags & gBitTable[gBattlerTarget])
|
||
&& !gProtectStructs[gBattlerAttacker].helpingHand
|
||
&& !gProtectStructs[gBattlerTarget].helpingHand)
|
||
{
|
||
gProtectStructs[gBattlerTarget].helpingHand = TRUE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
// Trick
|
||
static void Cmd_tryswapitems(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
// opponent can't swap items with player in regular battles
|
||
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL
|
||
|| (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT
|
||
&& !(gBattleTypeFlags & (BATTLE_TYPE_LINK
|
||
| BATTLE_TYPE_EREADER_TRAINER
|
||
| BATTLE_TYPE_FRONTIER
|
||
| BATTLE_TYPE_SECRET_BASE
|
||
| BATTLE_TYPE_RECORDED_LINK
|
||
| (B_TRAINERS_KNOCK_OFF_ITEMS == TRUE ? BATTLE_TYPE_TRAINER : 0)
|
||
))))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
u8 sideAttacker = GetBattlerSide(gBattlerAttacker);
|
||
u8 sideTarget = GetBattlerSide(gBattlerTarget);
|
||
|
||
// You can't swap items if they were knocked off in regular battles
|
||
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK
|
||
| BATTLE_TYPE_EREADER_TRAINER
|
||
| BATTLE_TYPE_FRONTIER
|
||
| BATTLE_TYPE_SECRET_BASE
|
||
| BATTLE_TYPE_RECORDED_LINK))
|
||
&& (gWishFutureKnock.knockedOffMons[sideAttacker] & gBitTable[gBattlerPartyIndexes[gBattlerAttacker]]
|
||
|| gWishFutureKnock.knockedOffMons[sideTarget] & gBitTable[gBattlerPartyIndexes[gBattlerTarget]]))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
// can't swap if two Pokémon don't have an item
|
||
// or if either of them is an enigma berry or a mail
|
||
else if ((gBattleMons[gBattlerAttacker].item == ITEM_NONE && gBattleMons[gBattlerTarget].item == ITEM_NONE)
|
||
|| !CanBattlerGetOrLoseItem(gBattlerAttacker, gBattleMons[gBattlerAttacker].item)
|
||
|| !CanBattlerGetOrLoseItem(gBattlerAttacker, gBattleMons[gBattlerTarget].item)
|
||
|| !CanBattlerGetOrLoseItem(gBattlerTarget, gBattleMons[gBattlerTarget].item)
|
||
|| !CanBattlerGetOrLoseItem(gBattlerTarget, gBattleMons[gBattlerAttacker].item))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
// check if ability prevents swapping
|
||
else if (GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD)
|
||
{
|
||
gBattlescriptCurrInstr = BattleScript_StickyHoldActivates;
|
||
gLastUsedAbility = gBattleMons[gBattlerTarget].ability;
|
||
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
|
||
}
|
||
// took a while, but all checks passed and items can be safely swapped
|
||
else
|
||
{
|
||
u16 oldItemAtk, *newItemAtk;
|
||
|
||
newItemAtk = &gBattleStruct->changedItems[gBattlerAttacker];
|
||
oldItemAtk = gBattleMons[gBattlerAttacker].item;
|
||
*newItemAtk = gBattleMons[gBattlerTarget].item;
|
||
|
||
gBattleMons[gBattlerAttacker].item = ITEM_NONE;
|
||
gBattleMons[gBattlerTarget].item = oldItemAtk;
|
||
|
||
RecordItemEffectBattle(gBattlerAttacker, 0);
|
||
RecordItemEffectBattle(gBattlerTarget, ItemId_GetHoldEffect(oldItemAtk));
|
||
|
||
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(*newItemAtk), newItemAtk);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
|
||
BtlController_EmitSetMonData(gBattlerTarget, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item);
|
||
MarkBattlerForControllerExec(gBattlerTarget);
|
||
|
||
gBattleStruct->choicedMove[gBattlerTarget] = MOVE_NONE;
|
||
gBattleStruct->choicedMove[gBattlerAttacker] = MOVE_NONE;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
PREPARE_ITEM_BUFFER(gBattleTextBuff1, *newItemAtk)
|
||
PREPARE_ITEM_BUFFER(gBattleTextBuff2, oldItemAtk)
|
||
|
||
if (!(sideAttacker == sideTarget && IsPartnerMonFromSameTrainer(gBattlerAttacker)))
|
||
{
|
||
// if targeting your own side and you aren't in a multi battle, don't save items as stolen
|
||
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
|
||
TrySaveExchangedItem(gBattlerAttacker, oldItemAtk);
|
||
if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER)
|
||
TrySaveExchangedItem(gBattlerTarget, *newItemAtk);
|
||
}
|
||
|
||
if (oldItemAtk != ITEM_NONE && *newItemAtk != ITEM_NONE)
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ITEM_SWAP_BOTH; // attacker's item -> <- target's item
|
||
}
|
||
else if (oldItemAtk == ITEM_NONE && *newItemAtk != ITEM_NONE)
|
||
{
|
||
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_UNBURDEN && gBattleResources->flags->flags[gBattlerAttacker] & RESOURCE_FLAG_UNBURDEN)
|
||
gBattleResources->flags->flags[gBattlerAttacker] &= ~RESOURCE_FLAG_UNBURDEN;
|
||
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ITEM_SWAP_TAKEN; // nothing -> <- target's item
|
||
}
|
||
else
|
||
{
|
||
CheckSetUnburden(gBattlerAttacker);
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ITEM_SWAP_GIVEN; // attacker's item -> <- nothing
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Role Play, Doodle
|
||
static void Cmd_trycopyability(void)
|
||
{
|
||
CMD_ARGS(u8 battler, const u8 *failInstr);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
u16 defAbility = gBattleMons[gBattlerTarget].ability;
|
||
|
||
if (gBattleMons[battler].ability == defAbility
|
||
|| defAbility == ABILITY_NONE
|
||
|| gAbilities[gBattleMons[battler].ability].cantBeSuppressed
|
||
|| gAbilities[gBattleMons[BATTLE_PARTNER(battler)].ability].cantBeSuppressed
|
||
|| gAbilities[defAbility].cantBeCopied)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattleScripting.abilityPopupOverwrite = gBattleMons[battler].ability;
|
||
gBattleMons[battler].ability = gBattleStruct->overwrittenAbilities[battler] = defAbility;
|
||
gLastUsedAbility = defAbility;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_trywish(void)
|
||
{
|
||
CMD_ARGS(u8 turnNumber, const u8 *failInstr);
|
||
|
||
switch (cmd->turnNumber)
|
||
{
|
||
case 0: // use wish
|
||
if (gWishFutureKnock.wishCounter[gBattlerAttacker] == 0)
|
||
{
|
||
gWishFutureKnock.wishCounter[gBattlerAttacker] = 2;
|
||
gWishFutureKnock.wishPartyId[gBattlerAttacker] = gBattlerPartyIndexes[gBattlerAttacker];
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
break;
|
||
case 1: // heal effect
|
||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerTarget, gWishFutureKnock.wishPartyId[gBattlerTarget])
|
||
if (B_WISH_HP_SOURCE >= GEN_5)
|
||
{
|
||
if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER)
|
||
gBattleMoveDamage = max(1, GetMonData(&gPlayerParty[gWishFutureKnock.wishPartyId[gBattlerTarget]], MON_DATA_MAX_HP) / 2);
|
||
else
|
||
gBattleMoveDamage = max(1, GetMonData(&gEnemyParty[gWishFutureKnock.wishPartyId[gBattlerTarget]], MON_DATA_MAX_HP) / 2);
|
||
}
|
||
else
|
||
{
|
||
gBattleMoveDamage = max(1, GetNonDynamaxMaxHP(gBattlerAttacker) / 2);
|
||
}
|
||
|
||
gBattleMoveDamage *= -1;
|
||
if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP)
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void Cmd_settoxicspikes(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u8 targetSide = GetBattlerSide(gBattlerTarget);
|
||
if (gSideTimers[targetSide].toxicSpikesAmount >= 2)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gSideTimers[targetSide].toxicSpikesAmount++;
|
||
gSideStatuses[targetSide] |= SIDE_STATUS_TOXIC_SPIKES;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setgastroacid(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gAbilities[gBattleMons[gBattlerTarget].ability].cantBeSuppressed)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
if (gBattleMons[gBattlerTarget].ability == ABILITY_NEUTRALIZING_GAS)
|
||
gSpecialStatuses[gBattlerTarget].neutralizingGasRemoved = TRUE;
|
||
|
||
gStatuses3[gBattlerTarget] |= STATUS3_GASTRO_ACID;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setyawn(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gStatuses3[gBattlerTarget] & STATUS3_YAWN
|
||
|| gBattleMons[gBattlerTarget].status1 & STATUS1_ANY)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_ELECTRIC_TERRAIN))
|
||
{
|
||
// When Yawn is used while Electric Terrain is set and drowsiness is set from Yawn being used against target in the previous turn:
|
||
// "But it failed" will display first.
|
||
gBattlescriptCurrInstr = BattleScript_ElectricTerrainPrevents;
|
||
}
|
||
else if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_MISTY_TERRAIN))
|
||
{
|
||
// When Yawn is used while Misty Terrain is set and drowsiness is set from Yawn being used against target in the previous turn:
|
||
// "But it failed" will display first.
|
||
gBattlescriptCurrInstr = BattleScript_MistyTerrainPrevents;
|
||
}
|
||
else
|
||
{
|
||
gStatuses3[gBattlerTarget] |= STATUS3_YAWN_TURN(2);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setdamagetohealthdifference(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (GetNonDynamaxHP(gBattlerTarget) <= gBattleMons[gBattlerAttacker].hp)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattleMoveDamage = GetNonDynamaxHP(gBattlerTarget) - gBattleMons[gBattlerAttacker].hp;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void HandleRoomMove(u32 statusFlag, u8 *timer, u8 stringId)
|
||
{
|
||
if (gFieldStatuses & statusFlag)
|
||
{
|
||
gFieldStatuses &= ~statusFlag;
|
||
*timer = 0;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = stringId + 1;
|
||
}
|
||
else
|
||
{
|
||
gFieldStatuses |= statusFlag;
|
||
*timer = 5;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = stringId;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setroom(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
switch (gBattleMoves[gCurrentMove].effect)
|
||
{
|
||
case EFFECT_TRICK_ROOM:
|
||
HandleRoomMove(STATUS_FIELD_TRICK_ROOM, &gFieldTimers.trickRoomTimer, 0);
|
||
break;
|
||
case EFFECT_WONDER_ROOM:
|
||
HandleRoomMove(STATUS_FIELD_WONDER_ROOM, &gFieldTimers.wonderRoomTimer, 2);
|
||
break;
|
||
case EFFECT_MAGIC_ROOM:
|
||
HandleRoomMove(STATUS_FIELD_MAGIC_ROOM, &gFieldTimers.magicRoomTimer, 4);
|
||
break;
|
||
default:
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 6;
|
||
break;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
// Skill Swap
|
||
static void Cmd_tryswapabilities(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gAbilities[gBattleMons[gBattlerAttacker].ability].cantBeSwapped
|
||
|| gAbilities[gBattleMons[gBattlerTarget].ability].cantBeSwapped)
|
||
{
|
||
RecordAbilityBattle(gBattlerTarget, gBattleMons[gBattlerTarget].ability);
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else if (GetBattlerHoldEffect(gBattlerTarget, TRUE) == HOLD_EFFECT_ABILITY_SHIELD)
|
||
{
|
||
RecordItemEffectBattle(gBattlerTarget, HOLD_EFFECT_ABILITY_SHIELD);
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT || IsDynamaxed(gBattlerTarget))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
u16 abilityAtk = gBattleMons[gBattlerAttacker].ability;
|
||
gBattleMons[gBattlerAttacker].ability = gBattleStruct->overwrittenAbilities[gBattlerAttacker] = gBattleMons[gBattlerTarget].ability;
|
||
gBattleMons[gBattlerTarget].ability = gBattleStruct->overwrittenAbilities[gBattlerTarget] = abilityAtk;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
}
|
||
|
||
static void Cmd_tryimprison(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if ((gStatuses3[gBattlerAttacker] & STATUS3_IMPRISONED_OTHERS))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else if (B_IMPRISON >= GEN_5)
|
||
{
|
||
gStatuses3[gBattlerAttacker] |= STATUS3_IMPRISONED_OTHERS;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
u8 battler, sideAttacker;
|
||
|
||
sideAttacker = GetBattlerSide(gBattlerAttacker);
|
||
for (battler = 0; battler < gBattlersCount; battler++)
|
||
{
|
||
if (sideAttacker != GetBattlerSide(battler))
|
||
{
|
||
s32 attackerMoveId;
|
||
for (attackerMoveId = 0; attackerMoveId < MAX_MON_MOVES; attackerMoveId++)
|
||
{
|
||
s32 i;
|
||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||
{
|
||
if (gBattleMons[gBattlerAttacker].moves[attackerMoveId] == gBattleMons[battler].moves[i]
|
||
&& gBattleMons[gBattlerAttacker].moves[attackerMoveId] != MOVE_NONE)
|
||
break;
|
||
}
|
||
if (i != MAX_MON_MOVES)
|
||
break;
|
||
}
|
||
if (attackerMoveId != MAX_MON_MOVES)
|
||
{
|
||
gStatuses3[gBattlerAttacker] |= STATUS3_IMPRISONED_OTHERS;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (battler == gBattlersCount) // In Generation 3 games, Imprison fails if the user doesn't share any moves with any of the foes.
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setstealthrock(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u8 targetSide = GetBattlerSide(gBattlerTarget);
|
||
if (gSideStatuses[targetSide] & SIDE_STATUS_STEALTH_ROCK)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gSideStatuses[targetSide] |= SIDE_STATUS_STEALTH_ROCK;
|
||
gSideTimers[targetSide].stealthRockAmount = 1;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_setuserstatus3(void)
|
||
{
|
||
CMD_ARGS(u32 flags, const u8 *failInstr);
|
||
|
||
u32 flags = cmd->flags;
|
||
|
||
if (gStatuses3[gBattlerAttacker] & flags)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gStatuses3[gBattlerAttacker] |= flags;
|
||
if (flags & STATUS3_MAGNET_RISE)
|
||
gDisableStructs[gBattlerAttacker].magnetRiseTimer = 5;
|
||
if (flags & STATUS3_LASER_FOCUS)
|
||
gDisableStructs[gBattlerAttacker].laserFocusTimer = 2;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_assistattackselect(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
s32 chooseableMovesNo = 0;
|
||
struct Pokemon *party;
|
||
s32 monId, moveId;
|
||
u16 *validMoves = Alloc(sizeof(u16) * PARTY_SIZE * MAX_MON_MOVES);
|
||
|
||
if (validMoves != NULL)
|
||
{
|
||
party = GetBattlerParty(gBattlerAttacker);
|
||
|
||
for (monId = 0; monId < PARTY_SIZE; monId++)
|
||
{
|
||
if (monId == gBattlerPartyIndexes[gBattlerAttacker])
|
||
continue;
|
||
if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE)
|
||
continue;
|
||
if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG)
|
||
continue;
|
||
|
||
for (moveId = 0; moveId < MAX_MON_MOVES; moveId++)
|
||
{
|
||
u16 move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId);
|
||
|
||
if (gBattleMoves[move].assistBanned)
|
||
continue;
|
||
|
||
validMoves[chooseableMovesNo++] = move;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (chooseableMovesNo)
|
||
{
|
||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||
gCalledMove = validMoves[Random() % chooseableMovesNo];
|
||
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
|
||
TRY_FREE_AND_SET_NULL(validMoves);
|
||
}
|
||
|
||
static void Cmd_trysetmagiccoat(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gCurrentTurnActionNumber == gBattlersCount - 1) // moves last turn
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gProtectStructs[gBattlerAttacker].bounceMove = TRUE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
// Snatch
|
||
static void Cmd_trysetsnatch(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gCurrentTurnActionNumber == gBattlersCount - 1) // moves last turn
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gProtectStructs[gBattlerAttacker].stealMove = TRUE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_unused2(void)
|
||
{
|
||
}
|
||
|
||
static void Cmd_switchoutabilities(void)
|
||
{
|
||
CMD_ARGS(u8 battler);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (gBattleMons[battler].ability == ABILITY_NEUTRALIZING_GAS)
|
||
{
|
||
gBattleMons[battler].ability = ABILITY_NONE;
|
||
BattleScriptPush(gBattlescriptCurrInstr);
|
||
gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits;
|
||
}
|
||
else
|
||
{
|
||
switch (GetBattlerAbility(battler))
|
||
{
|
||
case ABILITY_NATURAL_CURE:
|
||
gBattleMons[battler].status1 = 0;
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_STATUS_BATTLE,
|
||
gBitTable[*(gBattleStruct->battlerPartyIndexes + battler)],
|
||
sizeof(gBattleMons[battler].status1),
|
||
&gBattleMons[battler].status1);
|
||
MarkBattlerForControllerExec(battler);
|
||
break;
|
||
case ABILITY_REGENERATOR:
|
||
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 3;
|
||
gBattleMoveDamage += gBattleMons[battler].hp;
|
||
if (gBattleMoveDamage > gBattleMons[battler].maxHP)
|
||
gBattleMoveDamage = gBattleMons[battler].maxHP;
|
||
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_HP_BATTLE,
|
||
gBitTable[*(gBattleStruct->battlerPartyIndexes + battler)],
|
||
sizeof(gBattleMoveDamage),
|
||
&gBattleMoveDamage);
|
||
MarkBattlerForControllerExec(battler);
|
||
break;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_jumpifhasnohp(void)
|
||
{
|
||
CMD_ARGS(u8 battler, const u8 *jumpInstr);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
|
||
if (gBattleMons[battler].hp == 0)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_getsecretpowereffect(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gBattleScripting.moveEffect = GetSecretPowerMoveEffect();
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
u16 GetSecretPowerMoveEffect(void)
|
||
{
|
||
u16 moveEffect;
|
||
u32 fieldTerrain = gFieldStatuses & STATUS_FIELD_TERRAIN_ANY;
|
||
if (fieldTerrain)
|
||
{
|
||
switch (fieldTerrain)
|
||
{
|
||
case STATUS_FIELD_MISTY_TERRAIN:
|
||
moveEffect = MOVE_EFFECT_SP_ATK_MINUS_1;
|
||
break;
|
||
case STATUS_FIELD_GRASSY_TERRAIN:
|
||
moveEffect = MOVE_EFFECT_SLEEP;
|
||
break;
|
||
case STATUS_FIELD_ELECTRIC_TERRAIN:
|
||
moveEffect = MOVE_EFFECT_PARALYSIS;
|
||
break;
|
||
case STATUS_FIELD_PSYCHIC_TERRAIN:
|
||
moveEffect = MOVE_EFFECT_SPD_MINUS_1;
|
||
break;
|
||
default:
|
||
moveEffect = MOVE_EFFECT_PARALYSIS;
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
switch (gBattleTerrain)
|
||
{
|
||
case BATTLE_TERRAIN_GRASS:
|
||
moveEffect = (B_SECRET_POWER_EFFECT >= GEN_4 ? MOVE_EFFECT_SLEEP : MOVE_EFFECT_POISON);
|
||
break;
|
||
case BATTLE_TERRAIN_UNDERWATER:
|
||
moveEffect = (B_SECRET_POWER_EFFECT >= GEN_6 ? MOVE_EFFECT_ATK_MINUS_1 : MOVE_EFFECT_DEF_MINUS_1);
|
||
break;
|
||
case BATTLE_TERRAIN_POND:
|
||
moveEffect = (B_SECRET_POWER_EFFECT >= GEN_4 ? MOVE_EFFECT_ATK_MINUS_1 : MOVE_EFFECT_SPD_MINUS_1);
|
||
break;
|
||
case BATTLE_TERRAIN_MOUNTAIN:
|
||
if (B_SECRET_POWER_EFFECT >= GEN_5)
|
||
moveEffect = MOVE_EFFECT_ACC_MINUS_1;
|
||
else if (B_SECRET_POWER_EFFECT >= GEN_4)
|
||
moveEffect = MOVE_EFFECT_FLINCH;
|
||
else
|
||
moveEffect = MOVE_EFFECT_CONFUSION;
|
||
break;
|
||
case BATTLE_TERRAIN_PUDDLE:
|
||
moveEffect = (B_SECRET_POWER_EFFECT >= GEN_5 ? MOVE_EFFECT_SPD_MINUS_1 : MOVE_EFFECT_ACC_MINUS_1);
|
||
break;
|
||
case BATTLE_TERRAIN_LONG_GRASS:
|
||
moveEffect = MOVE_EFFECT_SLEEP;
|
||
break;
|
||
case BATTLE_TERRAIN_SAND:
|
||
moveEffect = MOVE_EFFECT_ACC_MINUS_1;
|
||
break;
|
||
case BATTLE_TERRAIN_WATER:
|
||
moveEffect = MOVE_EFFECT_ATK_MINUS_1;
|
||
break;
|
||
case BATTLE_TERRAIN_CAVE:
|
||
case BATTLE_TERRAIN_BURIAL_GROUND:
|
||
case BATTLE_TERRAIN_SPACE:
|
||
moveEffect = MOVE_EFFECT_FLINCH;
|
||
break;
|
||
case BATTLE_TERRAIN_SOARING:
|
||
case BATTLE_TERRAIN_SKY_PILLAR:
|
||
case BATTLE_TERRAIN_MARSH:
|
||
case BATTLE_TERRAIN_SWAMP:
|
||
moveEffect = MOVE_EFFECT_SPD_MINUS_1;
|
||
break;
|
||
case BATTLE_TERRAIN_SNOW:
|
||
case BATTLE_TERRAIN_ICE:
|
||
moveEffect = (B_USE_FROSTBITE == TRUE ? MOVE_EFFECT_FROSTBITE : MOVE_EFFECT_FREEZE);
|
||
break;
|
||
case BATTLE_TERRAIN_VOLCANO:
|
||
moveEffect = MOVE_EFFECT_BURN;
|
||
break;
|
||
case BATTLE_TERRAIN_ULTRA_SPACE:
|
||
moveEffect = MOVE_EFFECT_DEF_MINUS_1;
|
||
break;
|
||
default:
|
||
moveEffect = MOVE_EFFECT_PARALYSIS;
|
||
break;
|
||
}
|
||
}
|
||
return moveEffect;
|
||
}
|
||
|
||
static void Cmd_pickup(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u32 i, j;
|
||
u16 species, heldItem, ability;
|
||
u8 lvlDivBy10;
|
||
|
||
if (!InBattlePike()) // No items in Battle Pike.
|
||
{
|
||
bool32 isInPyramid = InBattlePyramid_();
|
||
for (i = 0; i < PARTY_SIZE; i++)
|
||
{
|
||
species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG);
|
||
heldItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM);
|
||
lvlDivBy10 = (GetMonData(&gPlayerParty[i], MON_DATA_LEVEL)-1) / 10; //Moving this here makes it easier to add in abilities like Honey Gather.
|
||
if (lvlDivBy10 > 9)
|
||
lvlDivBy10 = 9;
|
||
|
||
ability = gSpeciesInfo[species].abilities[GetMonData(&gPlayerParty[i], MON_DATA_ABILITY_NUM)];
|
||
|
||
if (ability == ABILITY_PICKUP
|
||
&& species != SPECIES_NONE
|
||
&& species != SPECIES_EGG
|
||
&& heldItem == ITEM_NONE
|
||
&& (Random() % 10) == 0)
|
||
{
|
||
if (isInPyramid)
|
||
{
|
||
heldItem = GetBattlePyramidPickupItemId();
|
||
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem);
|
||
}
|
||
else
|
||
{
|
||
u32 rand = Random() % 100;
|
||
u32 percentTotal = 0;
|
||
|
||
for (j = 0; j < ARRAY_COUNT(sPickupTable); j++)
|
||
{
|
||
percentTotal += sPickupTable[j].percentage[lvlDivBy10];
|
||
if (rand < percentTotal)
|
||
{
|
||
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &sPickupTable[j].itemId);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (ability == ABILITY_HONEY_GATHER
|
||
&& species != 0
|
||
&& species != SPECIES_EGG
|
||
&& heldItem == ITEM_NONE)
|
||
{
|
||
if ((lvlDivBy10 + 1 ) * 5 > Random() % 100)
|
||
{
|
||
heldItem = ITEM_HONEY;
|
||
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem);
|
||
}
|
||
}
|
||
#if P_SHUCKLE_BERRY_JUICE == GEN_2
|
||
else if (species == SPECIES_SHUCKLE
|
||
&& heldItem == ITEM_ORAN_BERRY
|
||
&& (Random() % 16) == 0)
|
||
{
|
||
heldItem = ITEM_BERRY_JUICE;
|
||
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem);
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_unused3(void)
|
||
{
|
||
}
|
||
|
||
static void Cmd_unused4(void)
|
||
{
|
||
}
|
||
|
||
// Water and Mud Sport
|
||
static void Cmd_settypebasedhalvers(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
bool8 worked = FALSE;
|
||
|
||
if (gBattleMoves[gCurrentMove].effect == EFFECT_MUD_SPORT)
|
||
{
|
||
if (B_SPORT_TURNS >= GEN_6)
|
||
{
|
||
if (!(gFieldStatuses & STATUS_FIELD_MUDSPORT))
|
||
{
|
||
gFieldStatuses |= STATUS_FIELD_MUDSPORT;
|
||
gFieldTimers.mudSportTimer = 5;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEAKEN_ELECTRIC;
|
||
worked = TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!(gStatuses4[gBattlerAttacker] & STATUS4_MUD_SPORT))
|
||
{
|
||
gStatuses4[gBattlerAttacker] |= STATUS4_MUD_SPORT;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEAKEN_ELECTRIC;
|
||
worked = TRUE;
|
||
}
|
||
}
|
||
}
|
||
else // Water Sport
|
||
{
|
||
if (B_SPORT_TURNS >= GEN_6)
|
||
{
|
||
if (!(gFieldStatuses & STATUS_FIELD_WATERSPORT))
|
||
{
|
||
gFieldStatuses |= STATUS_FIELD_WATERSPORT;
|
||
gFieldTimers.waterSportTimer = 5;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEAKEN_FIRE;
|
||
worked = TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (!(gStatuses4[gBattlerAttacker] & STATUS4_WATER_SPORT))
|
||
{
|
||
gStatuses4[gBattlerAttacker] |= STATUS4_WATER_SPORT;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEAKEN_FIRE;
|
||
worked = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (worked)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
|
||
bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, u32 move)
|
||
{
|
||
if (!(gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE))
|
||
return FALSE;
|
||
else if (gBattleMoves[move].ignoresSubstitute)
|
||
return FALSE;
|
||
else if (GetBattlerAbility(battlerAtk) == ABILITY_INFILTRATOR)
|
||
return FALSE;
|
||
else
|
||
return TRUE;
|
||
}
|
||
|
||
bool32 DoesDisguiseBlockMove(u32 battlerAtk, u32 battlerDef, u32 move)
|
||
{
|
||
if (gBattleMons[battlerDef].species != SPECIES_MIMIKYU_DISGUISED
|
||
|| gBattleMons[battlerDef].status2 & STATUS2_TRANSFORMED
|
||
|| IS_MOVE_STATUS(move)
|
||
|| gHitMarker & HITMARKER_IGNORE_DISGUISE
|
||
|| GetBattlerAbility(battlerDef) != ABILITY_DISGUISE)
|
||
return FALSE;
|
||
else
|
||
return TRUE;
|
||
}
|
||
|
||
static void Cmd_jumpifsubstituteblocks(void)
|
||
{
|
||
CMD_ARGS(const u8 *jumpInstr);
|
||
|
||
if (DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_tryrecycleitem(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u16 *usedHeldItem;
|
||
|
||
usedHeldItem = &gBattleStruct->usedHeldItems[gBattlerPartyIndexes[gBattlerAttacker]][GetBattlerSide(gBattlerAttacker)];
|
||
if (*usedHeldItem != ITEM_NONE && gBattleMons[gBattlerAttacker].item == ITEM_NONE)
|
||
{
|
||
gLastUsedItem = *usedHeldItem;
|
||
*usedHeldItem = ITEM_NONE;
|
||
gBattleMons[gBattlerAttacker].item = gLastUsedItem;
|
||
|
||
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerAttacker].item), &gBattleMons[gBattlerAttacker].item);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
bool32 CanCamouflage(u8 battler)
|
||
{
|
||
if (IS_BATTLER_OF_TYPE(battler, sTerrainToType[gBattleTerrain]))
|
||
return FALSE;
|
||
return TRUE;
|
||
}
|
||
|
||
static void Cmd_settypetoterrain(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u8 terrainType;
|
||
switch(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)
|
||
{
|
||
case STATUS_FIELD_ELECTRIC_TERRAIN:
|
||
terrainType = TYPE_ELECTRIC;
|
||
break;
|
||
case STATUS_FIELD_GRASSY_TERRAIN:
|
||
terrainType = TYPE_GRASS;
|
||
break;
|
||
case STATUS_FIELD_MISTY_TERRAIN:
|
||
terrainType = TYPE_FAIRY;
|
||
break;
|
||
case STATUS_FIELD_PSYCHIC_TERRAIN:
|
||
terrainType = TYPE_PSYCHIC;
|
||
break;
|
||
default:
|
||
terrainType = sTerrainToType[gBattleTerrain];
|
||
break;
|
||
}
|
||
|
||
if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType))
|
||
{
|
||
SET_BATTLER_TYPE(gBattlerAttacker, terrainType);
|
||
PREPARE_TYPE_BUFFER(gBattleTextBuff1, terrainType);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
// Unused
|
||
static void Cmd_pursuitdoubles(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u32 battler = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerAttacker)));
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||
&& !(gAbsentBattlerFlags & gBitTable[battler])
|
||
&& gChosenActionByBattler[battler] == B_ACTION_USE_MOVE
|
||
&& gBattleMoves[gChosenMoveByBattler[battler]].effect == EFFECT_PURSUIT)
|
||
{
|
||
gActionsByTurnOrder[battler] = B_ACTION_TRY_FINISH;
|
||
gCurrentMove = gChosenMoveByBattler[battler];
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
gBattleScripting.animTurn = 1;
|
||
gBattleScripting.savedBattler = gBattlerAttacker;
|
||
gBattlerAttacker = battler;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_snatchsetbattlers(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gEffectBattler = gBattlerAttacker;
|
||
|
||
if (gBattlerAttacker == gBattlerTarget)
|
||
gBattlerAttacker = gBattlerTarget = gBattleScripting.battler;
|
||
else
|
||
gBattlerTarget = gBattleScripting.battler;
|
||
|
||
gBattleScripting.battler = gEffectBattler;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
// Brick Break
|
||
static void Cmd_removelightscreenreflect(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u8 side;
|
||
bool32 failed;
|
||
|
||
if (B_BRICK_BREAK >= GEN_4)
|
||
side = GetBattlerSide(gBattlerTarget); // From Gen 4 onwards, Brick Break can remove screens on the user's side if used on an ally
|
||
else
|
||
side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE;
|
||
|
||
if (B_BRICK_BREAK >= GEN_5)
|
||
failed = (gMoveResultFlags & MOVE_RESULT_NO_EFFECT);
|
||
else
|
||
failed = FALSE;
|
||
|
||
if (!failed
|
||
&& (gSideTimers[side].reflectTimer
|
||
|| gSideTimers[side].lightscreenTimer
|
||
|| gSideTimers[side].auroraVeilTimer))
|
||
{
|
||
gSideStatuses[side] &= ~SIDE_STATUS_REFLECT;
|
||
gSideStatuses[side] &= ~SIDE_STATUS_LIGHTSCREEN;
|
||
gSideStatuses[side] &= ~SIDE_STATUS_AURORA_VEIL;
|
||
gSideTimers[side].reflectTimer = 0;
|
||
gSideTimers[side].lightscreenTimer = 0;
|
||
gSideTimers[side].auroraVeilTimer = 0;
|
||
gBattleScripting.animTurn = 1;
|
||
gBattleScripting.animTargetsHit = 1;
|
||
}
|
||
else
|
||
{
|
||
gBattleScripting.animTurn = 0;
|
||
gBattleScripting.animTargetsHit = 0;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
u8 GetCatchingBattler(void)
|
||
{
|
||
if (IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)))
|
||
return GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||
else
|
||
return GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||
}
|
||
|
||
static void Cmd_handleballthrow(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u16 ballMultiplier = 100;
|
||
s8 ballAddition = 0;
|
||
|
||
if (gBattleControllerExecFlags)
|
||
return;
|
||
|
||
gBattlerTarget = GetCatchingBattler();
|
||
|
||
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
|
||
{
|
||
BtlController_EmitBallThrowAnim(gBattlerAttacker, BUFFER_A, BALL_TRAINER_BLOCK);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
gBattlescriptCurrInstr = BattleScript_TrainerBallBlock;
|
||
}
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL)
|
||
{
|
||
BtlController_EmitBallThrowAnim(gBattlerAttacker, BUFFER_A, BALL_3_SHAKES_SUCCESS);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
gBattlescriptCurrInstr = BattleScript_WallyBallThrow;
|
||
}
|
||
else
|
||
{
|
||
u32 odds, i;
|
||
u8 catchRate;
|
||
|
||
gLastThrownBall = gLastUsedItem;
|
||
gBallToDisplay = gLastThrownBall;
|
||
if (gBattleTypeFlags & BATTLE_TYPE_SAFARI)
|
||
catchRate = gBattleStruct->safariCatchFactor * 1275 / 100;
|
||
else
|
||
catchRate = gSpeciesInfo[gBattleMons[gBattlerTarget].species].catchRate;
|
||
|
||
if (gSpeciesInfo[gBattleMons[gBattlerTarget].species].isUltraBeast)
|
||
{
|
||
if (gLastUsedItem == ITEM_BEAST_BALL)
|
||
ballMultiplier = 500;
|
||
else
|
||
ballMultiplier = 10;
|
||
}
|
||
else
|
||
{
|
||
switch (gLastUsedItem)
|
||
{
|
||
case ITEM_ULTRA_BALL:
|
||
ballMultiplier = 200;
|
||
break;
|
||
case ITEM_SPORT_BALL:
|
||
if (B_SPORT_BALL_MODIFIER <= GEN_7)
|
||
ballMultiplier = 150;
|
||
case ITEM_GREAT_BALL:
|
||
case ITEM_SAFARI_BALL:
|
||
ballMultiplier = 150;
|
||
break;
|
||
case ITEM_NET_BALL:
|
||
if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_WATER) || IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_BUG))
|
||
ballMultiplier = B_NET_BALL_MODIFIER >= GEN_7 ? 350 : 300;
|
||
break;
|
||
case ITEM_DIVE_BALL:
|
||
if (GetCurrentMapType() == MAP_TYPE_UNDERWATER
|
||
|| (B_DIVE_BALL_MODIFIER >= GEN_4 && (gIsFishingEncounter || gIsSurfingEncounter)))
|
||
ballMultiplier = 350;
|
||
break;
|
||
case ITEM_NEST_BALL:
|
||
if (B_NEST_BALL_MODIFIER >= GEN_6)
|
||
{
|
||
//((41 - Pokémon's level) ÷ 10)× if Pokémon's level is between 1 and 29, 1× otherwise.
|
||
if (gBattleMons[gBattlerTarget].level < 30)
|
||
ballMultiplier = 410 - (gBattleMons[gBattlerTarget].level * 10);
|
||
}
|
||
else if (B_NEST_BALL_MODIFIER >= GEN_5)
|
||
{
|
||
//((41 - Pokémon's level) ÷ 10)×, minimum 1×
|
||
if (gBattleMons[gBattlerTarget].level < 31)
|
||
ballMultiplier = 410 - (gBattleMons[gBattlerTarget].level * 10);
|
||
}
|
||
else if (gBattleMons[gBattlerTarget].level < 40)
|
||
{
|
||
//((40 - Pokémon's level) ÷ 10)×, minimum 1×
|
||
ballMultiplier = 400 - (gBattleMons[gBattlerTarget].level * 10);
|
||
if (ballMultiplier <= 90)
|
||
ballMultiplier = 100;
|
||
}
|
||
break;
|
||
case ITEM_REPEAT_BALL:
|
||
if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), FLAG_GET_CAUGHT))
|
||
ballMultiplier = (B_REPEAT_BALL_MODIFIER >= GEN_7 ? 350 : 300);
|
||
break;
|
||
case ITEM_TIMER_BALL:
|
||
ballMultiplier = 100 + (gBattleResults.battleTurnCounter * (B_TIMER_BALL_MODIFIER >= GEN_5 ? 30 : 10));
|
||
if (ballMultiplier > 400)
|
||
ballMultiplier = 400;
|
||
break;
|
||
case ITEM_DUSK_BALL:
|
||
i = GetTimeOfDay();
|
||
if (i == TIME_EVENING || i == TIME_NIGHT || gMapHeader.cave || gMapHeader.mapType == MAP_TYPE_UNDERGROUND)
|
||
ballMultiplier = (B_DUSK_BALL_MODIFIER >= GEN_7 ? 300 : 350);
|
||
break;
|
||
case ITEM_QUICK_BALL:
|
||
if (gBattleResults.battleTurnCounter == 0)
|
||
ballMultiplier = (B_QUICK_BALL_MODIFIER >= GEN_5 ? 500 : 400);
|
||
break;
|
||
case ITEM_LEVEL_BALL:
|
||
if (gBattleMons[gBattlerAttacker].level >= 4 * gBattleMons[gBattlerTarget].level)
|
||
ballMultiplier = 800;
|
||
else if (gBattleMons[gBattlerAttacker].level > 2 * gBattleMons[gBattlerTarget].level)
|
||
ballMultiplier = 400;
|
||
else if (gBattleMons[gBattlerAttacker].level > gBattleMons[gBattlerTarget].level)
|
||
ballMultiplier = 200;
|
||
break;
|
||
case ITEM_LURE_BALL:
|
||
if (gIsFishingEncounter)
|
||
ballMultiplier = (B_LURE_BALL_MODIFIER >= GEN_7 ? 500 : 300);
|
||
break;
|
||
case ITEM_MOON_BALL:
|
||
{
|
||
const struct Evolution *evolutions = GetSpeciesEvolutions(gBattleMons[gBattlerTarget].species);
|
||
if (evolutions == NULL)
|
||
break;
|
||
for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++)
|
||
{
|
||
if (evolutions[i].method == EVO_ITEM
|
||
&& evolutions[i].param == ITEM_MOON_STONE)
|
||
ballMultiplier = 400;
|
||
}
|
||
}
|
||
break;
|
||
case ITEM_LOVE_BALL:
|
||
if (gBattleMons[gBattlerTarget].species == gBattleMons[gBattlerAttacker].species)
|
||
{
|
||
u8 gender1 = GetMonGender(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]);
|
||
u8 gender2 = GetMonGender(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]]);
|
||
|
||
if (gender1 != gender2 && gender1 != MON_GENDERLESS && gender2 != MON_GENDERLESS)
|
||
ballMultiplier = 800;
|
||
}
|
||
break;
|
||
case ITEM_FAST_BALL:
|
||
if (gSpeciesInfo[gBattleMons[gBattlerTarget].species].baseSpeed >= 100)
|
||
ballMultiplier = 400;
|
||
break;
|
||
case ITEM_HEAVY_BALL:
|
||
i = GetSpeciesWeight(gBattleMons[gBattlerTarget].species);
|
||
if (B_HEAVY_BALL_MODIFIER >= GEN_7)
|
||
{
|
||
if (i < 1000)
|
||
ballAddition = -20;
|
||
else if (i < 2000)
|
||
ballAddition = 0;
|
||
else if (i < 3000)
|
||
ballAddition = 20;
|
||
else
|
||
ballAddition = 30;
|
||
}
|
||
else if (B_HEAVY_BALL_MODIFIER >= GEN_4)
|
||
{
|
||
if (i < 2048)
|
||
ballAddition = -20;
|
||
else if (i < 3072)
|
||
ballAddition = 20;
|
||
else if (i < 4096)
|
||
ballAddition = 30;
|
||
else
|
||
ballAddition = 40;
|
||
}
|
||
else
|
||
{
|
||
if (i < 1024)
|
||
ballAddition = -20;
|
||
else if (i < 2048)
|
||
ballAddition = 0;
|
||
else if (i < 3072)
|
||
ballAddition = 20;
|
||
else if (i < 4096)
|
||
ballAddition = 30;
|
||
else
|
||
ballAddition = 40;
|
||
}
|
||
break;
|
||
case ITEM_DREAM_BALL:
|
||
if (B_DREAM_BALL_MODIFIER >= GEN_8 && (gBattleMons[gBattlerTarget].status1 & STATUS1_SLEEP || GetBattlerAbility(gBattlerTarget) == ABILITY_COMATOSE))
|
||
ballMultiplier = 400;
|
||
break;
|
||
case ITEM_BEAST_BALL:
|
||
ballMultiplier = 10;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// catchRate is unsigned, which means that it may potentially overflow if sum is applied directly.
|
||
if (catchRate < 21 && ballAddition == -20)
|
||
catchRate = 1;
|
||
else
|
||
catchRate = catchRate + ballAddition;
|
||
|
||
odds = (catchRate * ballMultiplier / 100)
|
||
* (gBattleMons[gBattlerTarget].maxHP * 3 - gBattleMons[gBattlerTarget].hp * 2)
|
||
/ (3 * gBattleMons[gBattlerTarget].maxHP);
|
||
|
||
if (gBattleMons[gBattlerTarget].status1 & (STATUS1_SLEEP | STATUS1_FREEZE))
|
||
odds *= 2;
|
||
if (gBattleMons[gBattlerTarget].status1 & (STATUS1_POISON | STATUS1_BURN | STATUS1_PARALYSIS | STATUS1_TOXIC_POISON | STATUS1_FROSTBITE))
|
||
odds = (odds * 15) / 10;
|
||
|
||
if (gBattleResults.catchAttempts[gLastUsedItem - FIRST_BALL] < 255)
|
||
gBattleResults.catchAttempts[gLastUsedItem - FIRST_BALL]++;
|
||
|
||
if (odds >= 255) // mon caught
|
||
{
|
||
BtlController_EmitBallThrowAnim(gBattlerAttacker, BUFFER_A, BALL_3_SHAKES_SUCCESS);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
TryBattleFormChange(gBattlerTarget, FORM_CHANGE_END_BATTLE);
|
||
gBattlescriptCurrInstr = BattleScript_SuccessBallThrow;
|
||
SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem);
|
||
|
||
if (CalculatePlayerPartyCount() == PARTY_SIZE)
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
|
||
else
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
|
||
|
||
if (gLastUsedItem == ITEM_HEAL_BALL)
|
||
{
|
||
MonRestorePP(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]);
|
||
HealStatusConditions(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], gBattlerPartyIndexes[gBattlerTarget], STATUS1_ANY, gBattlerTarget);
|
||
gBattleMons[gBattlerTarget].hp = gBattleMons[gBattlerTarget].maxHP;
|
||
SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_HP, &gBattleMons[gBattlerTarget].hp);
|
||
}
|
||
}
|
||
else // mon may be caught, calculate shakes
|
||
{
|
||
u8 shakes;
|
||
u8 maxShakes;
|
||
|
||
gBattleSpritesDataPtr->animationData->isCriticalCapture = FALSE;
|
||
gBattleSpritesDataPtr->animationData->criticalCaptureSuccess = FALSE;
|
||
|
||
if (CriticalCapture(odds))
|
||
{
|
||
maxShakes = BALL_1_SHAKE; // critical capture doesn't guarantee capture
|
||
gBattleSpritesDataPtr->animationData->isCriticalCapture = TRUE;
|
||
}
|
||
else
|
||
{
|
||
maxShakes = BALL_3_SHAKES_SUCCESS;
|
||
}
|
||
|
||
if (gLastUsedItem == ITEM_MASTER_BALL)
|
||
{
|
||
shakes = maxShakes;
|
||
}
|
||
else
|
||
{
|
||
if (P_CATCH_CURVE >= GEN_6)
|
||
{
|
||
odds = (255 * 255 * 255) / (odds * odds * odds);
|
||
odds = 65536 / Sqrt(Sqrt(Sqrt(Sqrt(odds))));
|
||
}
|
||
else
|
||
{
|
||
odds = Sqrt(Sqrt(16711680 / odds));
|
||
odds = 1048560 / odds;
|
||
}
|
||
for (shakes = 0; shakes < maxShakes && Random() < odds; shakes++);
|
||
}
|
||
|
||
BtlController_EmitBallThrowAnim(gBattlerAttacker, BUFFER_A, shakes);
|
||
MarkBattlerForControllerExec(gBattlerAttacker);
|
||
|
||
if (shakes == maxShakes) // mon caught, copy of the code above
|
||
{
|
||
if (IsCriticalCapture())
|
||
gBattleSpritesDataPtr->animationData->criticalCaptureSuccess = TRUE;
|
||
|
||
TryBattleFormChange(gBattlerTarget, FORM_CHANGE_END_BATTLE);
|
||
gBattlescriptCurrInstr = BattleScript_SuccessBallThrow;
|
||
SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem);
|
||
|
||
if (CalculatePlayerPartyCount() == PARTY_SIZE)
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
|
||
else
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
|
||
|
||
if (gLastUsedItem == ITEM_HEAL_BALL)
|
||
{
|
||
MonRestorePP(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]);
|
||
HealStatusConditions(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], gBattlerPartyIndexes[gBattlerTarget], STATUS1_ANY, gBattlerTarget);
|
||
gBattleMons[gBattlerTarget].hp = gBattleMons[gBattlerTarget].maxHP;
|
||
SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_HP, &gBattleMons[gBattlerTarget].hp);
|
||
}
|
||
}
|
||
else // not caught
|
||
{
|
||
if (!gHasFetchedBall)
|
||
gLastUsedBall = gLastUsedItem;
|
||
|
||
if (IsCriticalCapture())
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = BALL_3_SHAKES_FAIL;
|
||
else
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = shakes;
|
||
|
||
gBattlescriptCurrInstr = BattleScript_ShakeBallThrow;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void Cmd_givecaughtmon(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
if (GiveMonToPlayer(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]]) != MON_GIVEN_TO_PARTY)
|
||
{
|
||
if (!ShouldShowBoxWasFullMessage())
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SENT_SOMEONES_PC;
|
||
StringCopy(gStringVar1, GetBoxNamePtr(VarGet(VAR_PC_BOX_TO_SEND_MON)));
|
||
GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]], MON_DATA_NICKNAME, gStringVar2);
|
||
}
|
||
else
|
||
{
|
||
StringCopy(gStringVar1, GetBoxNamePtr(VarGet(VAR_PC_BOX_TO_SEND_MON))); // box the mon was sent to
|
||
GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]], MON_DATA_NICKNAME, gStringVar2);
|
||
StringCopy(gStringVar3, GetBoxNamePtr(GetPCBoxToSendMon())); //box the mon was going to be sent to
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SOMEONES_BOX_FULL;
|
||
}
|
||
|
||
// Change to B_MSG_SENT_LANETTES_PC or B_MSG_LANETTES_BOX_FULL
|
||
if (FlagGet(FLAG_SYS_PC_LANETTE))
|
||
gBattleCommunication[MULTISTRING_CHOOSER]++;
|
||
}
|
||
|
||
gBattleResults.caughtMonSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]], MON_DATA_SPECIES, NULL);
|
||
GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]], MON_DATA_NICKNAME, gBattleResults.caughtMonNick);
|
||
gBattleResults.caughtMonBall = GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]], MON_DATA_POKEBALL, NULL);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_trysetcaughtmondexflags(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
u32 species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]], MON_DATA_SPECIES, NULL);
|
||
u32 personality = GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]], MON_DATA_PERSONALITY, NULL);
|
||
|
||
if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
HandleSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_SET_CAUGHT, personality);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_displaydexinfo(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
u16 species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]], MON_DATA_SPECIES, NULL);
|
||
|
||
switch (gBattleCommunication[0])
|
||
{
|
||
case 0:
|
||
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
|
||
gBattleCommunication[0]++;
|
||
break;
|
||
case 1:
|
||
if (!gPaletteFade.active)
|
||
{
|
||
struct Pokemon *mon = &gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]];
|
||
FreeAllWindowBuffers();
|
||
gBattleCommunication[TASK_ID] = DisplayCaughtMonDexPage(species,
|
||
GetMonData(mon, MON_DATA_IS_SHINY),
|
||
GetMonData(mon, MON_DATA_PERSONALITY));
|
||
gBattleCommunication[0]++;
|
||
}
|
||
break;
|
||
case 2:
|
||
if (!gPaletteFade.active
|
||
&& gMain.callback2 == BattleMainCB2
|
||
&& !gTasks[gBattleCommunication[TASK_ID]].isActive)
|
||
{
|
||
SetVBlankCallback(VBlankCB_Battle);
|
||
gBattleCommunication[0]++;
|
||
}
|
||
break;
|
||
case 3:
|
||
InitBattleBgsVideo();
|
||
LoadBattleTextboxAndBackground();
|
||
gBattle_BG3_X = 256;
|
||
gBattleCommunication[0]++;
|
||
break;
|
||
case 4:
|
||
if (!IsDma3ManagerBusyWithBgCopy())
|
||
{
|
||
BeginNormalPaletteFade(PALETTES_BG, 0, 16, 0, RGB_BLACK);
|
||
ShowBg(0);
|
||
ShowBg(3);
|
||
gBattleCommunication[0]++;
|
||
}
|
||
break;
|
||
case 5:
|
||
if (!gPaletteFade.active)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
break;
|
||
}
|
||
}
|
||
|
||
void HandleBattleWindow(u8 xStart, u8 yStart, u8 xEnd, u8 yEnd, u8 flags)
|
||
{
|
||
s32 destY, destX, bgId;
|
||
u16 var = 0;
|
||
|
||
for (destY = yStart; destY <= yEnd; destY++)
|
||
{
|
||
for (destX = xStart; destX <= xEnd; destX++)
|
||
{
|
||
if (destY == yStart)
|
||
{
|
||
if (destX == xStart)
|
||
var = 0x1022;
|
||
else if (destX == xEnd)
|
||
var = 0x1024;
|
||
else
|
||
var = 0x1023;
|
||
}
|
||
else if (destY == yEnd)
|
||
{
|
||
if (destX == xStart)
|
||
var = 0x1028;
|
||
else if (destX == xEnd)
|
||
var = 0x102A;
|
||
else
|
||
var = 0x1029;
|
||
}
|
||
else
|
||
{
|
||
if (destX == xStart)
|
||
var = 0x1025;
|
||
else if (destX == xEnd)
|
||
var = 0x1027;
|
||
else
|
||
var = 0x1026;
|
||
}
|
||
|
||
if (flags & WINDOW_CLEAR)
|
||
var = 0;
|
||
|
||
bgId = (flags & WINDOW_BG1) ? 1 : 0;
|
||
CopyToBgTilemapBufferRect_ChangePalette(bgId, &var, destX, destY, 1, 1, 0x11);
|
||
}
|
||
}
|
||
}
|
||
|
||
void BattleCreateYesNoCursorAt(u8 cursorPosition)
|
||
{
|
||
u16 src[2];
|
||
src[0] = 1;
|
||
src[1] = 2;
|
||
|
||
CopyToBgTilemapBufferRect_ChangePalette(0, src, 0x19, 9 + (2 * cursorPosition), 1, 2, 0x11);
|
||
CopyBgTilemapBufferToVram(0);
|
||
}
|
||
|
||
void BattleDestroyYesNoCursorAt(u8 cursorPosition)
|
||
{
|
||
u16 src[2];
|
||
src[0] = 0x1016;
|
||
src[1] = 0x1016;
|
||
|
||
CopyToBgTilemapBufferRect_ChangePalette(0, src, 0x19, 9 + (2 * cursorPosition), 1, 2, 0x11);
|
||
CopyBgTilemapBufferToVram(0);
|
||
}
|
||
|
||
static void Cmd_trygivecaughtmonnick(void)
|
||
{
|
||
CMD_ARGS(const u8 *successInstr);
|
||
|
||
switch (gBattleCommunication[MULTIUSE_STATE])
|
||
{
|
||
case 0:
|
||
HandleBattleWindow(YESNOBOX_X_Y, 0);
|
||
BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO);
|
||
gBattleCommunication[MULTIUSE_STATE]++;
|
||
gBattleCommunication[CURSOR_POSITION] = 0;
|
||
BattleCreateYesNoCursorAt(0);
|
||
break;
|
||
case 1:
|
||
if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0)
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||
gBattleCommunication[CURSOR_POSITION] = 0;
|
||
BattleCreateYesNoCursorAt(0);
|
||
}
|
||
if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0)
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
|
||
gBattleCommunication[CURSOR_POSITION] = 1;
|
||
BattleCreateYesNoCursorAt(1);
|
||
}
|
||
if (JOY_NEW(A_BUTTON))
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
if (gBattleCommunication[CURSOR_POSITION] == 0)
|
||
{
|
||
gBattleCommunication[MULTIUSE_STATE]++;
|
||
BeginFastPaletteFade(3);
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MULTIUSE_STATE] = 4;
|
||
}
|
||
}
|
||
else if (JOY_NEW(B_BUTTON))
|
||
{
|
||
PlaySE(SE_SELECT);
|
||
gBattleCommunication[MULTIUSE_STATE] = 4;
|
||
}
|
||
break;
|
||
case 2:
|
||
if (!gPaletteFade.active)
|
||
{
|
||
GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_NICKNAME, gBattleStruct->caughtMonNick);
|
||
FreeAllWindowBuffers();
|
||
|
||
DoNamingScreen(NAMING_SCREEN_CAUGHT_MON, gBattleStruct->caughtMonNick,
|
||
GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_SPECIES),
|
||
GetMonGender(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]),
|
||
GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_PERSONALITY, NULL),
|
||
BattleMainCB2);
|
||
|
||
gBattleCommunication[MULTIUSE_STATE]++;
|
||
}
|
||
break;
|
||
case 3:
|
||
if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active)
|
||
{
|
||
SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_NICKNAME, gBattleStruct->caughtMonNick);
|
||
gBattlescriptCurrInstr = cmd->successInstr;
|
||
}
|
||
break;
|
||
case 4:
|
||
if (CalculatePlayerPartyCount() == PARTY_SIZE)
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->successInstr;
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void Cmd_subattackerhpbydmg(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gBattleMons[gBattlerAttacker].hp -= gBattleMoveDamage;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_removeattackerstatus1(void)
|
||
{
|
||
CMD_ARGS();
|
||
|
||
gBattleMons[gBattlerAttacker].status1 = 0;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
// CMD_ARGS is not needed for these functions as they end the script execution.
|
||
static void Cmd_finishaction(void)
|
||
{
|
||
gCurrentActionFuncId = B_ACTION_FINISHED;
|
||
}
|
||
|
||
static void Cmd_finishturn(void)
|
||
{
|
||
gCurrentActionFuncId = B_ACTION_FINISHED;
|
||
gCurrentTurnActionNumber = gBattlersCount;
|
||
}
|
||
|
||
static void Cmd_trainerslideout(void)
|
||
{
|
||
CMD_ARGS(u8 position);
|
||
|
||
u32 battler = GetBattlerAtPosition(cmd->position);
|
||
BtlController_EmitTrainerSlideBack(battler, BUFFER_A);
|
||
MarkBattlerForControllerExec(battler);
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static const u16 sTelekinesisBanList[] =
|
||
{
|
||
SPECIES_DIGLETT,
|
||
SPECIES_DUGTRIO,
|
||
SPECIES_DIGLETT_ALOLAN,
|
||
SPECIES_DUGTRIO_ALOLAN,
|
||
SPECIES_SANDYGAST,
|
||
SPECIES_PALOSSAND,
|
||
SPECIES_GENGAR_MEGA,
|
||
};
|
||
|
||
bool32 IsTelekinesisBannedSpecies(u16 species)
|
||
{
|
||
u32 i;
|
||
|
||
for (i = 0; i < ARRAY_COUNT(sTelekinesisBanList); i++)
|
||
{
|
||
if (species == sTelekinesisBanList[i])
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
static void Cmd_settelekinesis(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gStatuses3[gBattlerTarget] & (STATUS3_TELEKINESIS | STATUS3_ROOTED | STATUS3_SMACKED_DOWN)
|
||
|| gFieldStatuses & STATUS_FIELD_GRAVITY
|
||
|| IsTelekinesisBannedSpecies(gBattleMons[gBattlerTarget].species))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gStatuses3[gBattlerTarget] |= STATUS3_TELEKINESIS;
|
||
gDisableStructs[gBattlerTarget].telekinesisTimer = 3;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_swapstatstages(void)
|
||
{
|
||
CMD_ARGS(u8 stat);
|
||
|
||
u8 stat = cmd->stat;
|
||
s8 atkStatStage = gBattleMons[gBattlerAttacker].statStages[stat];
|
||
s8 defStatStage = gBattleMons[gBattlerTarget].statStages[stat];
|
||
|
||
gBattleMons[gBattlerAttacker].statStages[stat] = defStatStage;
|
||
gBattleMons[gBattlerTarget].statStages[stat] = atkStatStage;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_averagestats(void)
|
||
{
|
||
CMD_ARGS(u8 stat);
|
||
|
||
u8 stat = cmd->stat;
|
||
u16 atkStat = *(u16 *)((&gBattleMons[gBattlerAttacker].attack) + (stat - 1));
|
||
u16 defStat = *(u16 *)((&gBattleMons[gBattlerTarget].attack) + (stat - 1));
|
||
u16 average = (atkStat + defStat) / 2;
|
||
|
||
*(u16 *)((&gBattleMons[gBattlerAttacker].attack) + (stat - 1)) = average;
|
||
*(u16 *)((&gBattleMons[gBattlerTarget].attack) + (stat - 1)) = average;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_jumpifoppositegenders(void)
|
||
{
|
||
CMD_ARGS(const u8 *jumpInstr);
|
||
|
||
if (AreBattlersOfOppositeGender(gBattlerAttacker, gBattlerTarget))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
static void Cmd_unused(void)
|
||
{
|
||
}
|
||
|
||
static void Cmd_tryworryseed(void)
|
||
{
|
||
CMD_ARGS(const u8 *failInstr);
|
||
|
||
if (gAbilities[gBattleMons[gBattlerTarget].ability].cantBeOverwritten
|
||
|| gBattleMons[gBattlerTarget].ability == ABILITY_INSOMNIA)
|
||
{
|
||
RecordAbilityBattle(gBattlerTarget, gBattleMons[gBattlerTarget].ability);
|
||
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else if (GetBattlerHoldEffect(gBattlerTarget, TRUE) == HOLD_EFFECT_ABILITY_SHIELD)
|
||
{
|
||
RecordItemEffectBattle(gBattlerTarget, HOLD_EFFECT_ABILITY_SHIELD);
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattleMons[gBattlerTarget].ability = gBattleStruct->overwrittenAbilities[gBattlerTarget] = ABILITY_INSOMNIA;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static void Cmd_callnative(void)
|
||
{
|
||
CMD_ARGS(void (*func)(void));
|
||
void (*func)(void) = cmd->func;
|
||
func();
|
||
}
|
||
|
||
// Callnative Funcs
|
||
void BS_CalcMetalBurstDmg(void)
|
||
{
|
||
NATIVE_ARGS(const u8 *failInstr);
|
||
|
||
u8 sideAttacker = GetBattlerSide(gBattlerAttacker);
|
||
u8 sideTarget = 0;
|
||
|
||
if (gProtectStructs[gBattlerAttacker].physicalDmg
|
||
&& sideAttacker != (sideTarget = GetBattlerSide(gProtectStructs[gBattlerAttacker].physicalBattlerId))
|
||
&& gBattleMons[gProtectStructs[gBattlerAttacker].physicalBattlerId].hp)
|
||
{
|
||
gBattleMoveDamage = gProtectStructs[gBattlerAttacker].physicalDmg * 150 / 100;
|
||
|
||
if (IsAffectedByFollowMe(gBattlerAttacker, sideTarget, gCurrentMove))
|
||
gBattlerTarget = gSideTimers[sideTarget].followmeTarget;
|
||
else
|
||
gBattlerTarget = gProtectStructs[gBattlerAttacker].physicalBattlerId;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else if (gProtectStructs[gBattlerAttacker].specialDmg
|
||
&& sideAttacker != (sideTarget = GetBattlerSide(gProtectStructs[gBattlerAttacker].specialBattlerId))
|
||
&& gBattleMons[gProtectStructs[gBattlerAttacker].specialBattlerId].hp)
|
||
{
|
||
gBattleMoveDamage = gProtectStructs[gBattlerAttacker].specialDmg * 150 / 100;
|
||
|
||
if (IsAffectedByFollowMe(gBattlerAttacker, sideTarget, gCurrentMove))
|
||
gBattlerTarget = gSideTimers[sideTarget].followmeTarget;
|
||
else
|
||
gBattlerTarget = gProtectStructs[gBattlerAttacker].specialBattlerId;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
void BS_JumpIfCantFling(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (!CanFling(battler))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_JumpIfMoreThanHalfHP(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (gBattleMons[battler].hp > (gBattleMons[battler].maxHP + 1) / 2)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_JumpIfHoldEffect(void)
|
||
{
|
||
u8 battler = gBattlescriptCurrInstr[5];
|
||
u16 holdEffect = T1_READ_16(gBattlescriptCurrInstr + 6);
|
||
|
||
if (GetBattlerHoldEffect(battler, TRUE) == holdEffect)
|
||
{
|
||
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 8);
|
||
}
|
||
else
|
||
{
|
||
RecordItemEffectBattle(battler, holdEffect);
|
||
gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM
|
||
gBattlescriptCurrInstr += 12;
|
||
}
|
||
}
|
||
|
||
void BS_DoStockpileStatChangesWearOff(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler, const u8 *statChangeInstr);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (gDisableStructs[battler].stockpileDef != 0)
|
||
{
|
||
SET_STATCHANGER(STAT_DEF, abs(gDisableStructs[battler].stockpileDef), TRUE);
|
||
gDisableStructs[battler].stockpileDef = 0;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = cmd->statChangeInstr;
|
||
}
|
||
else if (gDisableStructs[battler].stockpileSpDef)
|
||
{
|
||
SET_STATCHANGER(STAT_SPDEF, abs(gDisableStructs[battler].stockpileSpDef), TRUE);
|
||
gDisableStructs[battler].stockpileSpDef = 0;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = cmd->statChangeInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
static bool32 CriticalCapture(u32 odds)
|
||
{
|
||
u32 numCaught;
|
||
|
||
if (B_CRITICAL_CAPTURE == FALSE)
|
||
return FALSE;
|
||
|
||
numCaught = GetNationalPokedexCount(FLAG_GET_CAUGHT);
|
||
|
||
if (numCaught <= (NATIONAL_DEX_COUNT * 30) / 650)
|
||
odds = 0;
|
||
else if (numCaught <= (NATIONAL_DEX_COUNT * 150) / 650)
|
||
odds /= 2;
|
||
else if (numCaught <= (NATIONAL_DEX_COUNT * 300) / 650)
|
||
; // odds = (odds * 100) / 100;
|
||
else if (numCaught <= (NATIONAL_DEX_COUNT * 450) / 650)
|
||
odds = (odds * 150) / 100;
|
||
else if (numCaught <= (NATIONAL_DEX_COUNT * 600) / 650)
|
||
odds *= 2;
|
||
else
|
||
odds = (odds * 250) / 100;
|
||
|
||
if (CheckBagHasItem(ITEM_CATCHING_CHARM, 1))
|
||
odds = (odds * (100 + B_CATCHING_CHARM_BOOST)) / 100;
|
||
|
||
odds /= 6;
|
||
if ((Random() % 255) < odds)
|
||
return TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
bool32 IsMoveAffectedByParentalBond(u32 move, u32 battler)
|
||
{
|
||
if (move != MOVE_NONE && move != MOVE_UNAVAILABLE && move != MOVE_STRUGGLE
|
||
&& !gBattleMoves[move].parentalBondBanned
|
||
&& gBattleMoves[move].category != BATTLE_CATEGORY_STATUS
|
||
&& gBattleMoves[move].strikeCount < 2)
|
||
{
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||
{
|
||
switch (GetBattlerMoveTargetType(battler, move))
|
||
{
|
||
// Both foes are alive, spread move strikes once
|
||
case MOVE_TARGET_BOTH:
|
||
if (CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, gBattlerTarget) >= 2)
|
||
return FALSE;
|
||
break;
|
||
// Either both foes or one foe and its ally are alive; spread move strikes once
|
||
case MOVE_TARGET_FOES_AND_ALLY:
|
||
if (CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER, gBattlerAttacker) >= 2)
|
||
return FALSE;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
static bool8 IsFinalStrikeEffect(u16 move)
|
||
{
|
||
u32 i;
|
||
u16 moveEffect = gBattleMoves[move].effect;
|
||
|
||
for (i = 0; i < ARRAY_COUNT(sFinalStrikeOnlyEffects); i++)
|
||
{
|
||
if (moveEffect == sFinalStrikeOnlyEffects[i])
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
static bool8 CanAbilityPreventStatLoss(u16 abilityDef, bool8 byIntimidate)
|
||
{
|
||
switch (abilityDef)
|
||
{
|
||
case ABILITY_CLEAR_BODY:
|
||
case ABILITY_FULL_METAL_BODY:
|
||
case ABILITY_WHITE_SMOKE:
|
||
return TRUE;
|
||
case ABILITY_INNER_FOCUS:
|
||
case ABILITY_SCRAPPY:
|
||
case ABILITY_OWN_TEMPO:
|
||
case ABILITY_OBLIVIOUS:
|
||
if (byIntimidate && (B_UPDATED_INTIMIDATE >= GEN_8))
|
||
return TRUE;
|
||
break;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
void BS_CheckParentalBondCounter(void)
|
||
{
|
||
NATIVE_ARGS(u8 counter, const u8 *jumpInstr);
|
||
// Some effects should only happen on the first or second strike of Parental Bond,
|
||
// so a way to check this in battle scripts is useful
|
||
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == cmd->counter && gBattleMons[gBattlerTarget].hp != 0)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_JumpIfCantLoseItem(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
u16 item = gBattleMons[battler].item;
|
||
|
||
if (item == ITEM_NONE || !CanBattlerGetOrLoseItem(battler, item))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_GetBattlerSide(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler);
|
||
gBattleCommunication[0] = GetBattlerSide(GetBattlerForBattleScript(cmd->battler));
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_TrySymbiosis(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
//called by Bestow, Fling, and Bug Bite, which don't work with Cmd_removeitem.
|
||
u32 partner = BATTLE_PARTNER(gBattlerAttacker);
|
||
if (SYMBIOSIS_CHECK(gBattlerAttacker, partner))
|
||
{
|
||
BestowItem(partner, gBattlerAttacker);
|
||
gLastUsedAbility = gBattleMons[partner].ability;
|
||
gBattleScripting.battler = gBattlerAbility = partner;
|
||
BattleScriptPushCursor();
|
||
gBattlescriptCurrInstr = BattleScript_SymbiosisActivates;
|
||
return;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_SetZEffect(void)
|
||
{
|
||
SetZEffect(); // Handles battle script jumping internally
|
||
}
|
||
|
||
static void TryUpdateRoundTurnOrder(void)
|
||
{
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||
{
|
||
u32 i;
|
||
u32 j = 0;
|
||
u32 k = 0;
|
||
u32 currRounder = 0;
|
||
u8 roundUsers[3] = {0xFF, 0xFF, 0xFF};
|
||
u8 nonRoundUsers[3] = {0xFF, 0xFF, 0xFF};
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (gBattlerByTurnOrder[i] == gBattlerAttacker)
|
||
{
|
||
currRounder = i + 1; // Current battler going after attacker
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Get battlers after us using round
|
||
for (i = currRounder; i < gBattlersCount; i++)
|
||
{
|
||
if (gChosenMoveByBattler[gBattlerByTurnOrder[i]] == MOVE_ROUND)
|
||
roundUsers[j++] = gBattlerByTurnOrder[i];
|
||
else
|
||
nonRoundUsers[k++] = gBattlerByTurnOrder[i];
|
||
}
|
||
|
||
// update turn order for round users
|
||
for (i = 0; roundUsers[i] != 0xFF && i < 3; i++)
|
||
{
|
||
gBattlerByTurnOrder[currRounder] = roundUsers[i];
|
||
gActionsByTurnOrder[currRounder] = gActionsByTurnOrder[roundUsers[i]];
|
||
gProtectStructs[roundUsers[i]].quash = TRUE; // Make it so their turn order can't be changed again
|
||
currRounder++;
|
||
}
|
||
|
||
// Update turn order for non-round users
|
||
for (i = 0; nonRoundUsers[i] != 0xFF && i < 3; i++)
|
||
{
|
||
gBattlerByTurnOrder[currRounder] = nonRoundUsers[i];
|
||
gActionsByTurnOrder[currRounder] = gActionsByTurnOrder[nonRoundUsers[i]];
|
||
currRounder++;
|
||
}
|
||
}
|
||
}
|
||
|
||
u8 GetFirstFaintedPartyIndex(u8 battler)
|
||
{
|
||
u32 i;
|
||
u32 start = 0;
|
||
u32 end = PARTY_SIZE;
|
||
struct Pokemon *party = GetBattlerParty(battler);
|
||
|
||
// Check whether partner is separate trainer.
|
||
if ((GetBattlerSide(battler) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
|
||
|| (GetBattlerSide(battler) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))
|
||
{
|
||
if (GetBattlerPosition(battler) == B_POSITION_OPPONENT_LEFT
|
||
|| GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT)
|
||
{
|
||
end = PARTY_SIZE / 2;
|
||
}
|
||
else
|
||
{
|
||
start = PARTY_SIZE / 2;
|
||
}
|
||
}
|
||
|
||
// Loop through to find fainted battler.
|
||
for (i = start; i < end; ++i)
|
||
{
|
||
u32 species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG);
|
||
if (species != SPECIES_NONE
|
||
&& species != SPECIES_EGG
|
||
&& GetMonData(&party[i], MON_DATA_HP) == 0)
|
||
{
|
||
return i;
|
||
}
|
||
}
|
||
|
||
// Returns PARTY_SIZE if none found.
|
||
return PARTY_SIZE;
|
||
}
|
||
|
||
void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 faintedBattler)
|
||
{
|
||
u32 holdEffect = GetMonHoldEffect(&gPlayerParty[expGetterMonId]);
|
||
|
||
if (IsTradedMon(&gPlayerParty[expGetterMonId]))
|
||
*expAmount = (*expAmount * 150) / 100;
|
||
if (holdEffect == HOLD_EFFECT_LUCKY_EGG)
|
||
*expAmount = (*expAmount * 150) / 100;
|
||
if (B_UNEVOLVED_EXP_MULTIPLIER >= GEN_6 && IsMonPastEvolutionLevel(&gPlayerParty[expGetterMonId]))
|
||
*expAmount = (*expAmount * 4915) / 4096;
|
||
if (B_AFFECTION_MECHANICS == TRUE && GetBattlerAffectionHearts(expGetterMonId) >= AFFECTION_FOUR_HEARTS)
|
||
*expAmount = (*expAmount * 4915) / 4096;
|
||
if (CheckBagHasItem(ITEM_EXP_CHARM, 1)) //is also for other exp boosting Powers if/when implemented
|
||
*expAmount = (*expAmount * 150) / 100;
|
||
|
||
if (B_SCALED_EXP >= GEN_5 && B_SCALED_EXP != GEN_6)
|
||
{
|
||
// Note: There is an edge case where if a pokemon receives a large amount of exp, it wouldn't be properly calculated
|
||
// because of multiplying by scaling factor(the value would simply be larger than an u32 can hold). Hence u64 is needed.
|
||
u64 value = *expAmount;
|
||
u8 faintedLevel = gBattleMons[faintedBattler].level;
|
||
u8 expGetterLevel = GetMonData(&gPlayerParty[expGetterMonId], MON_DATA_LEVEL);
|
||
|
||
value *= sExperienceScalingFactors[(faintedLevel * 2) + 10];
|
||
value /= sExperienceScalingFactors[faintedLevel + expGetterLevel + 10];
|
||
*expAmount = value + 1;
|
||
}
|
||
}
|
||
|
||
void BS_ItemRestoreHP(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
u16 healAmount;
|
||
u32 battler = MAX_BATTLERS_COUNT;
|
||
u32 healParam = GetItemEffect(gLastUsedItem)[6];
|
||
u32 side = GetBattlerSide(gBattlerAttacker);
|
||
struct Pokemon *party = GetSideParty(side);
|
||
u16 hp = GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_HP);
|
||
u16 maxHP = GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_MAX_HP);
|
||
gBattleCommunication[MULTIUSE_STATE] = 0;
|
||
|
||
// Track the number of Revives used in a battle.
|
||
if (hp == 0 && side == B_SIDE_PLAYER && gBattleResults.numRevivesUsed < 255)
|
||
gBattleResults.numRevivesUsed++;
|
||
|
||
// Check if the recipient is an active battler.
|
||
if (gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[gBattlerAttacker])
|
||
battler = gBattlerAttacker;
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||
&& gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)])
|
||
battler = BATTLE_PARTNER(gBattlerAttacker);
|
||
|
||
// Get amount to heal.
|
||
switch (healParam)
|
||
{
|
||
case ITEM6_HEAL_HP_FULL:
|
||
healAmount = maxHP;
|
||
break;
|
||
case ITEM6_HEAL_HP_HALF:
|
||
healAmount = maxHP / 2;
|
||
break;
|
||
case ITEM6_HEAL_HP_QUARTER:
|
||
healAmount = maxHP / 4;
|
||
break;
|
||
default:
|
||
healAmount = healParam;
|
||
break;
|
||
}
|
||
if (hp + healAmount > maxHP)
|
||
healAmount = maxHP - hp;
|
||
|
||
// Heal is applied as move damage if battler is active.
|
||
if (battler != MAX_BATTLERS_COUNT && hp != 0)
|
||
{
|
||
gBattleMoveDamage = -healAmount;
|
||
}
|
||
else
|
||
{
|
||
hp += healAmount;
|
||
SetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_HP, &hp);
|
||
|
||
// Revived battlers on the field need to be brought back.
|
||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && battler != MAX_BATTLERS_COUNT)
|
||
{
|
||
gAbsentBattlerFlags &= ~gBitTable[battler];
|
||
gBattleScripting.battler = battler;
|
||
gBattleCommunication[MULTIUSE_STATE] = TRUE;
|
||
}
|
||
}
|
||
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_SPECIES));
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_ItemCureStatus(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
struct Pokemon *party = GetBattlerParty(gBattlerAttacker);
|
||
|
||
// Heal Status1 conditions.
|
||
HealStatusConditions(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], gBattleStruct->itemPartyIndex[gBattlerAttacker], GetItemStatus1Mask(gLastUsedItem), gBattlerAttacker);
|
||
|
||
// Heal Status2 conditions if battler is active.
|
||
if (gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[gBattlerAttacker])
|
||
{
|
||
gBattleMons[gBattlerAttacker].status2 &= ~GetItemStatus2Mask(gLastUsedItem);
|
||
}
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||
&& gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)])
|
||
{
|
||
gBattleMons[gBattlerAttacker].status2 &= ~GetItemStatus2Mask(gLastUsedItem);
|
||
gBattlerTarget = BATTLE_PARTNER(gBattlerAttacker);
|
||
}
|
||
|
||
if (GetItemStatus1Mask(gLastUsedItem) & STATUS1_SLEEP)
|
||
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE;
|
||
if (GetItemStatus2Mask(gLastUsedItem) & STATUS2_CONFUSION)
|
||
gStatuses4[gBattlerAttacker] &= ~STATUS4_INFINITE_CONFUSION;
|
||
|
||
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_SPECIES));
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_ItemIncreaseStat(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
u16 statId = GetItemEffect(gLastUsedItem)[1];
|
||
u16 stages = ItemId_GetHoldEffectParam(gLastUsedItem);
|
||
SET_STATCHANGER(statId, stages, FALSE);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_ItemRestorePP(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
const u8 *effect = GetItemEffect(gLastUsedItem);
|
||
u32 i, pp, maxPP, moveId, loopEnd;
|
||
u32 battler = MAX_BATTLERS_COUNT;
|
||
struct Pokemon *mon = (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) ? &gPlayerParty[gBattleStruct->itemPartyIndex[gBattlerAttacker]] : &gEnemyParty[gBattleStruct->itemPartyIndex[gBattlerAttacker]];
|
||
|
||
// Check whether to apply to all moves.
|
||
if (effect[4] & ITEM4_HEAL_PP_ONE)
|
||
{
|
||
i = gBattleStruct->itemMoveIndex[gBattlerAttacker];
|
||
loopEnd = i + 1;
|
||
}
|
||
else
|
||
{
|
||
i = 0;
|
||
loopEnd = MAX_MON_MOVES;
|
||
}
|
||
|
||
// Check if the recipient is an active battler.
|
||
if (gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[gBattlerAttacker])
|
||
battler = gBattlerAttacker;
|
||
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||
&& gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)])
|
||
battler = BATTLE_PARTNER(gBattlerAttacker);
|
||
|
||
// Heal PP!
|
||
for (; i < loopEnd; i++)
|
||
{
|
||
pp = GetMonData(mon, MON_DATA_PP1 + i, NULL);
|
||
moveId = GetMonData(mon, MON_DATA_MOVE1 + i, NULL);
|
||
maxPP = CalculatePPWithBonus(moveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), i);
|
||
if (pp != maxPP)
|
||
{
|
||
pp += effect[6];
|
||
if (pp > maxPP)
|
||
pp = maxPP;
|
||
SetMonData(mon, MON_DATA_PP1 + i, &pp);
|
||
|
||
// Update battler PP if needed.
|
||
if (battler != MAX_BATTLERS_COUNT
|
||
&& gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[battler]
|
||
&& MOVE_IS_PERMANENT(battler, i))
|
||
{
|
||
gBattleMons[battler].pp[i] = pp;
|
||
}
|
||
}
|
||
}
|
||
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(mon, MON_DATA_SPECIES));
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_TryRevertWeatherForm(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
if (TryBattleFormChange(gBattlerTarget, FORM_CHANGE_BATTLE_WEATHER))
|
||
{
|
||
gBattleScripting.battler = gBattlerTarget;
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_TargetFormChangeWithStringNoPopup;
|
||
return;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_SetSnow(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
|
||
if (!TryChangeBattleWeather(gBattlerAttacker, ENUM_WEATHER_SNOW, FALSE))
|
||
{
|
||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED;
|
||
|
||
}
|
||
else
|
||
{
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SNOW;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_HandleMegaEvolution(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler, u8 caseId);
|
||
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
HandleScriptMegaPrimalBurst(cmd->caseId, battler, HANDLE_TYPE_MEGA_EVOLUTION);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_HandlePrimalReversion(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler, u8 caseId);
|
||
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
HandleScriptMegaPrimalBurst(cmd->caseId, battler, HANDLE_TYPE_PRIMAL_REVERSION);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_HandleUltraBurst(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler, u8 caseId);
|
||
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
HandleScriptMegaPrimalBurst(cmd->caseId, battler, HANDLE_TYPE_ULTRA_BURST);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_JumpIfShellTrap(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
|
||
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (gProtectStructs[battler].shellTrap)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_JumpIfEmergencyExited(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
if (gSpecialStatuses[battler].emergencyExited)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_JumpIfRod(void)
|
||
{
|
||
NATIVE_ARGS(const u8 *jumpInstr);
|
||
if (IsElectricAbilityAffected(ABILITY_LIGHTNING_ROD))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_JumpIfAbsorb(void)
|
||
{
|
||
NATIVE_ARGS(const u8 *jumpInstr);
|
||
if (IsElectricAbilityAffected(ABILITY_VOLT_ABSORB))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_JumpIfMotor(void)
|
||
{
|
||
NATIVE_ARGS(const u8 *jumpInstr);
|
||
if (IsElectricAbilityAffected(ABILITY_MOTOR_DRIVE))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_ApplySaltCure(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler);
|
||
|
||
u8 battler = GetBattlerForBattleScript(cmd->battler);
|
||
gStatuses4[battler] |= STATUS4_SALT_CURE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_JumpIfArgument(void)
|
||
{
|
||
NATIVE_ARGS(u8 argument, const u8 *jumpInstr);
|
||
|
||
if (gBattleMoves[gCurrentMove].argument == cmd->argument)
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_SetRemoveTerrain(void)
|
||
{
|
||
NATIVE_ARGS(const u8 *jumpInstr);
|
||
u32 statusFlag = 0;
|
||
|
||
switch (gBattleMoves[gCurrentMove].effect)
|
||
{
|
||
case EFFECT_MISTY_TERRAIN:
|
||
statusFlag = STATUS_FIELD_MISTY_TERRAIN;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY;
|
||
break;
|
||
case EFFECT_GRASSY_TERRAIN:
|
||
statusFlag = STATUS_FIELD_GRASSY_TERRAIN;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_GRASSY;
|
||
break;
|
||
case EFFECT_ELECTRIC_TERRAIN:
|
||
statusFlag = STATUS_FIELD_ELECTRIC_TERRAIN;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC;
|
||
break;
|
||
case EFFECT_PSYCHIC_TERRAIN:
|
||
statusFlag = STATUS_FIELD_PSYCHIC_TERRAIN;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC;
|
||
break;
|
||
case EFFECT_HIT_SET_REMOVE_TERRAIN:
|
||
switch (gBattleMoves[gCurrentMove].argument)
|
||
{
|
||
case ARG_SET_PSYCHIC_TERRAIN: // Genesis Supernova
|
||
statusFlag = STATUS_FIELD_PSYCHIC_TERRAIN;
|
||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC;
|
||
break;
|
||
case ARG_TRY_REMOVE_TERRAIN_HIT: // Splintered Stormshards
|
||
case ARG_TRY_REMOVE_TERRAIN_FAIL: // Steel Roller
|
||
if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY))
|
||
{
|
||
// No terrain to remove, jump to battle script pointer.
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
}
|
||
else
|
||
{
|
||
// Remove all terrains.
|
||
RemoveAllTerrains();
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
return;
|
||
default:
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
|
||
if (gFieldStatuses & statusFlag || statusFlag == 0)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
}
|
||
else
|
||
{
|
||
u16 atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
|
||
|
||
gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY;
|
||
gFieldStatuses |= statusFlag;
|
||
gFieldTimers.terrainTimer = (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
void BS_JumpIfTerrainAffected(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler, u32 flags, const u8 *jumpInstr);
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
|
||
if (IsBattlerTerrainAffected(battler, cmd->flags))
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_TryReflectType(void)
|
||
{
|
||
NATIVE_ARGS(const u8 *failInstr);
|
||
u16 targetBaseSpecies = GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species);
|
||
u8 targetType1 = GetBattlerType(gBattlerTarget, 0);
|
||
u8 targetType2 = GetBattlerType(gBattlerTarget, 1);
|
||
u8 targetType3 = GetBattlerType(gBattlerTarget, 2);
|
||
|
||
if (targetBaseSpecies == SPECIES_ARCEUS || targetBaseSpecies == SPECIES_SILVALLY)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else if (IS_BATTLER_TYPELESS(gBattlerTarget))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else if (targetType1 == TYPE_MYSTERY && targetType2 == TYPE_MYSTERY && targetType3 != TYPE_MYSTERY)
|
||
{
|
||
gBattleMons[gBattlerAttacker].type1 = TYPE_NORMAL;
|
||
gBattleMons[gBattlerAttacker].type2 = TYPE_NORMAL;
|
||
gBattleMons[gBattlerAttacker].type3 = targetType3;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else if (targetType1 == TYPE_MYSTERY && targetType2 != TYPE_MYSTERY)
|
||
{
|
||
gBattleMons[gBattlerAttacker].type1 = targetType2;
|
||
gBattleMons[gBattlerAttacker].type2 = targetType2;
|
||
gBattleMons[gBattlerAttacker].type3 = targetType3;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else if (targetType1 != TYPE_MYSTERY && targetType2 == TYPE_MYSTERY)
|
||
{
|
||
gBattleMons[gBattlerAttacker].type1 = targetType1;
|
||
gBattleMons[gBattlerAttacker].type2 = targetType1;
|
||
gBattleMons[gBattlerAttacker].type3 = targetType3;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattleMons[gBattlerAttacker].type1 = targetType1;
|
||
gBattleMons[gBattlerAttacker].type2 = targetType2;
|
||
gBattleMons[gBattlerAttacker].type3 = targetType3;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
void BS_TrySetOctolock(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler, const u8 *failInstr);
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
|
||
if (gDisableStructs[battler].octolock)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
gDisableStructs[battler].octolock = TRUE;
|
||
gBattleMons[battler].status2 |= STATUS2_ESCAPE_PREVENTION;
|
||
gDisableStructs[battler].battlerPreventingEscape = gBattlerAttacker;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
void BS_SetGlaiveRush(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
|
||
gStatuses4[gBattlerAttacker] |= STATUS4_GLAIVE_RUSH;
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_TryRelicSong(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
|
||
if (GetBattlerAbility(gBattlerAttacker) != ABILITY_SHEER_FORCE && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED))
|
||
{
|
||
if (gBattleMons[gBattlerAttacker].species == SPECIES_MELOETTA_ARIA)
|
||
gBattleMons[gBattlerAttacker].species = SPECIES_MELOETTA_PIROUETTE;
|
||
else if (gBattleMons[gBattlerAttacker].species == SPECIES_MELOETTA_PIROUETTE)
|
||
gBattleMons[gBattlerAttacker].species = SPECIES_MELOETTA_ARIA;
|
||
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_AttackerFormChangeMoveEffect;
|
||
}
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_SetPledge(void)
|
||
{
|
||
NATIVE_ARGS(const u8 *jumpInstr);
|
||
|
||
u32 partner = BATTLE_PARTNER(gBattlerAttacker);
|
||
u32 partnerMove = gBattleMons[partner].moves[gBattleStruct->chosenMovePositions[partner]];
|
||
u32 i = 0;
|
||
u32 k = 0;
|
||
|
||
if (gBattleStruct->pledgeMove)
|
||
{
|
||
PrepareStringBattle(STRINGID_USEDMOVE, gBattlerAttacker);
|
||
gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED;
|
||
|
||
if ((gCurrentMove == MOVE_GRASS_PLEDGE && partnerMove == MOVE_WATER_PLEDGE)
|
||
|| (gCurrentMove == MOVE_WATER_PLEDGE && partnerMove == MOVE_GRASS_PLEDGE))
|
||
{
|
||
gCurrentMove = MOVE_GRASS_PLEDGE;
|
||
gBattlescriptCurrInstr = BattleScript_EffectCombinedPledge_Grass;
|
||
}
|
||
else if ((gCurrentMove == MOVE_FIRE_PLEDGE && partnerMove == MOVE_GRASS_PLEDGE)
|
||
|| (gCurrentMove == MOVE_GRASS_PLEDGE && partnerMove == MOVE_FIRE_PLEDGE))
|
||
{
|
||
gCurrentMove = MOVE_FIRE_PLEDGE;
|
||
gBattlescriptCurrInstr = BattleScript_EffectCombinedPledge_Fire;
|
||
}
|
||
else if ((gCurrentMove == MOVE_WATER_PLEDGE && partnerMove == MOVE_FIRE_PLEDGE)
|
||
|| (gCurrentMove == MOVE_FIRE_PLEDGE && partnerMove == MOVE_WATER_PLEDGE))
|
||
{
|
||
gCurrentMove = MOVE_WATER_PLEDGE;
|
||
gBattlescriptCurrInstr = BattleScript_EffectCombinedPledge_Water;
|
||
}
|
||
|
||
gBattleCommunication[MSG_DISPLAY] = 0;
|
||
}
|
||
else if ((gChosenActionByBattler[partner] == B_ACTION_USE_MOVE)
|
||
&& gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||
&& IsBattlerAlive(partner)
|
||
&& gCurrentMove != partnerMove
|
||
&& gBattleMoves[partnerMove].effect == EFFECT_PLEDGE)
|
||
{
|
||
u32 currPledgeUser = 0;
|
||
u32 newTurnOrder[] = {0xFF, 0xFF};
|
||
|
||
for (i = 0; i < gBattlersCount; i++)
|
||
{
|
||
if (gBattlerByTurnOrder[i] == gBattlerAttacker)
|
||
{
|
||
currPledgeUser = i + 1; // Current battler going after attacker
|
||
break;
|
||
}
|
||
}
|
||
for (i = currPledgeUser; i < gBattlersCount; i++)
|
||
{
|
||
if (gBattlerByTurnOrder[i] != partner)
|
||
{
|
||
newTurnOrder[k] = gBattlerByTurnOrder[i];
|
||
k++;
|
||
}
|
||
}
|
||
|
||
gBattlerByTurnOrder[currPledgeUser] = partner;
|
||
currPledgeUser++;
|
||
|
||
for (i = 0; newTurnOrder[i] != 0xFF && i < 2; i++)
|
||
{
|
||
gBattlerByTurnOrder[currPledgeUser] = newTurnOrder[i];
|
||
currPledgeUser++;
|
||
}
|
||
|
||
gBattleStruct->pledgeMove = TRUE;
|
||
gBattleScripting.battler = partner;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||
|
||
}
|
||
}
|
||
|
||
void BS_SetPledgeStatus(void)
|
||
{
|
||
NATIVE_ARGS(u8 battler, u32 sideStatus);
|
||
|
||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||
u32 side = GetBattlerSide(battler);
|
||
|
||
gBattleStruct->pledgeMove = FALSE;
|
||
if (!(gSideStatuses[side] & cmd->sideStatus))
|
||
{
|
||
gSideStatuses[side] |= cmd->sideStatus;
|
||
|
||
switch (cmd->sideStatus)
|
||
{
|
||
case SIDE_STATUS_RAINBOW:
|
||
gSideTimers[side].rainbowTimer = 4;
|
||
break;
|
||
case SIDE_STATUS_SEA_OF_FIRE:
|
||
gSideTimers[side].seaOfFireTimer = 4;
|
||
break;
|
||
case SIDE_STATUS_SWAMP:
|
||
gSideTimers[side].swampTimer = 4;
|
||
}
|
||
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
gBattlescriptCurrInstr = BattleScript_MoveEnd;
|
||
}
|
||
|
||
void BS_TryTrainerSlideZMoveMsg(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
s32 shouldSlide;
|
||
|
||
if ((shouldSlide = ShouldDoTrainerSlide(gBattlerAttacker, TRAINER_SLIDE_Z_MOVE)))
|
||
{
|
||
gBattleScripting.battler = gBattlerAttacker;
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = (shouldSlide == 1 ? BattleScript_TrainerASlideMsgRet : BattleScript_TrainerBSlideMsgRet);
|
||
}
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_TryTrainerSlideMegaEvolutionMsg(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
s32 shouldSlide;
|
||
|
||
if ((shouldSlide = ShouldDoTrainerSlide(gBattlerAttacker, TRAINER_SLIDE_MEGA_EVOLUTION)))
|
||
{
|
||
gBattleScripting.battler = gBattlerAttacker;
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = (shouldSlide == 1 ? BattleScript_TrainerASlideMsgRet : BattleScript_TrainerBSlideMsgRet);
|
||
}
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_TryTrainerSlideDynamaxMsg(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
s32 shouldSlide;
|
||
|
||
if ((shouldSlide = ShouldDoTrainerSlide(gBattlerAttacker, TRAINER_SLIDE_DYNAMAX)))
|
||
{
|
||
gBattleScripting.battler = gBattlerAttacker;
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = (shouldSlide == 1 ? BattleScript_TrainerASlideMsgRet : BattleScript_TrainerBSlideMsgRet);
|
||
}
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_TryHealPulse(void)
|
||
{
|
||
NATIVE_ARGS(const u8 *failInstr);
|
||
|
||
if (BATTLER_MAX_HP(gBattlerTarget))
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_MEGA_LAUNCHER && gBattleMoves[gCurrentMove].pulseMove)
|
||
gBattleMoveDamage = -(GetNonDynamaxMaxHP(gBattlerTarget) * 75 / 100);
|
||
else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && gBattleMoves[gCurrentMove].argument == MOVE_EFFECT_FLORAL_HEALING)
|
||
gBattleMoveDamage = -(GetNonDynamaxMaxHP(gBattlerTarget) * 2 / 3);
|
||
else
|
||
gBattleMoveDamage = -(GetNonDynamaxMaxHP(gBattlerTarget) / 2);
|
||
|
||
if (gBattleMoveDamage == 0)
|
||
gBattleMoveDamage = -1;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
void BS_TryCopycat(void)
|
||
{
|
||
NATIVE_ARGS(const u8 *failInstr);
|
||
|
||
if (gLastUsedMove == MOVE_NONE || gLastUsedMove == MOVE_UNAVAILABLE || gBattleMoves[gLastUsedMove].copycatBanned)
|
||
{
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
else
|
||
{
|
||
if (IsMaxMove(gLastUsedMove))
|
||
gCalledMove = gBattleStruct->dynamax.lastUsedBaseMove;
|
||
else
|
||
gCalledMove = gLastUsedMove;
|
||
|
||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
}
|
||
|
||
void BS_TryDefog(void)
|
||
{
|
||
NATIVE_ARGS(u8 clear, const u8 *failInstr);
|
||
|
||
if (cmd->clear)
|
||
{
|
||
if (TryDefogClear(gEffectBattler, TRUE))
|
||
return;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
else
|
||
{
|
||
if (TryDefogClear(gBattlerAttacker, FALSE))
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
else
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
}
|
||
}
|
||
|
||
void BS_TryTriggerStatusForm(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
if (TryBattleFormChange(gBattlerTarget, FORM_CHANGE_STATUS))
|
||
{
|
||
gBattleScripting.battler = gBattlerTarget;
|
||
BattleScriptPush(cmd->nextInstr);
|
||
gBattlescriptCurrInstr = BattleScript_TargetFormChangeWithStringNoPopup;
|
||
return;
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_AllySwitchSwapBattler(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
|
||
gBattleScripting.battler = gBattlerAttacker;
|
||
gBattlerAttacker ^= BIT_FLANK;
|
||
gProtectStructs[gBattlerAttacker].usedAllySwitch = TRUE;
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_AllySwitchFailChance(void)
|
||
{
|
||
NATIVE_ARGS(const u8 *failInstr);
|
||
|
||
if (B_ALLY_SWITCH_FAIL_CHANCE >= GEN_9)
|
||
{
|
||
TryResetProtectUseCounter(gBattlerAttacker);
|
||
if (sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] < Random())
|
||
{
|
||
gDisableStructs[gBattlerAttacker].protectUses = 0;
|
||
gBattlescriptCurrInstr = cmd->failInstr;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
gDisableStructs[gBattlerAttacker].protectUses++;
|
||
}
|
||
}
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|
||
|
||
void BS_SetPhotonGeyserCategory(void)
|
||
{
|
||
NATIVE_ARGS();
|
||
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == BATTLE_CATEGORY_PHYSICAL);
|
||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||
}
|