pokefirered/src/battle_script_commands.c
2024-05-02 00:51:52 +02:00

15458 lines
561 KiB
C

#include "global.h"
#include "gflib.h"
#include "item.h"
#include "util.h"
#include "random.h"
#include "pokedex.h"
#include "money.h"
#include "pokemon_icon.h"
#include "m4a.h"
#include "mail.h"
#include "event_data.h"
#include "strings.h"
#include "pokemon_special_anim.h"
#include "pokemon_storage_system.h"
#include "pokemon_summary_screen.h"
#include "task.h"
#include "naming_screen.h"
#include "overworld.h"
#include "party_menu.h"
#include "trainer_pokemon_sprites.h"
#include "field_specials.h"
#include "battle.h"
#include "battle_message.h"
#include "battle_anim.h"
#include "battle_ai_script_commands.h"
#include "battle_scripts.h"
#include "reshow_battle_screen.h"
#include "battle_controllers.h"
#include "battle_interface.h"
#include "constants/battle_anim.h"
#include "constants/battle_move_effects.h"
#include "constants/battle_script_commands.h"
#include "constants/items.h"
#include "constants/item_effects.h"
#include "constants/hold_effects.h"
#include "constants/songs.h"
#include "constants/moves.h"
#include "constants/abilities.h"
#include "constants/pokemon.h"
#include "constants/maps.h"
#include "data/battle_move_effects.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;
// 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,
MOVE_THUNDER_CAGE
};
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 bool8 IsTwoTurnsMove(u16 move);
static void TrySetDestinyBondToHappen(void);
static u8 AttacksThisTurn(u8 battlerId, u16 move); // Note: returns 1 if it's a charging turn, otherwise 2.
static void CheckWonderGuardAndLevitate(void);
static u8 ChangeStatBuffs(s8 statValue, u32 statId, u32, const u8 *BS_ptr);
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 u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent);
static bool8 IsFinalStrikeEffect(u16 move);
static void TryUpdateRoundTurnOrder(void);
static void BestowItem(u32 battlerAtk, u32 battlerDef);
static void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 faintedBattler);
static bool8 CanBurnHitThaw(u16 move);
static bool32 ChangeOrderTargetAfterAttacker(void);
static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount);
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_setadditionaleffects(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_nop(void);
static void Cmd_jumpifcantmakeasleep(void);
static void Cmd_stockpile(void);
static void Cmd_stockpiletobasedamage(void);
static void Cmd_stockpiletohpheal(void);
static void Cmd_negativedamage(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_remaininghptopower(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_rolloutdamagecalculation(void);
static void Cmd_jumpifconfusedandstatmaxed(void);
static void Cmd_furycuttercalc(void);
static void Cmd_friendshiptodamagecalculation(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_maxattackhalvehp(void);
static void Cmd_copyfoestats(void);
static void Cmd_rapidspinfree(void);
static void Cmd_setdefensecurlbit(void);
static void Cmd_recoverbasedonsunlight(void);
static void Cmd_hiddenpowercalc(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_scaledamagebyhealthratio(void);
static void Cmd_tryswapabilities(void);
static void Cmd_tryimprison(void);
static void Cmd_setstealthrock(void);
static void Cmd_weightdamagecalculation(void);
static void Cmd_assistattackselect(void);
static void Cmd_trysetmagiccoat(void);
static void Cmd_trysetsnatch(void);
static void Cmd_trygetintimidatetarget(void);
static void Cmd_switchoutabilities(void);
static void Cmd_jumpifhasnohp(void);
static void Cmd_getsecretpowereffect(void);
static void Cmd_pickup(void);
static void Cmd_docastformchangeanimation(void);
static void Cmd_trycastformdatachange(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_callnative(void);
void (* const gBattleScriptingCommandsTable[])(void) =
{
Cmd_attackcanceler, //0x0 // done
Cmd_accuracycheck, //0x1 // done
Cmd_attackstring, //0x2 // done
Cmd_ppreduce, //0x3 // done
Cmd_critcalc, //0x4 // done
Cmd_damagecalc, //0x5 // done
Cmd_typecalc, //0x6 // done
Cmd_adjustdamage, //0x7 // done
Cmd_multihitresultmessage, //0x8 // done
Cmd_attackanimation, //0x9 // done
Cmd_waitanimation, //0xA // done
Cmd_healthbarupdate, //0xB // done
Cmd_datahpupdate, //0xC // done
Cmd_critmessage, //0xD // done
Cmd_effectivenesssound, //0xE // done
Cmd_resultmessage, //0xF // done
Cmd_printstring, //0x10 // done
Cmd_printselectionstring, //0x11 // done
Cmd_waitmessage, //0x12 // done
Cmd_printfromtable, //0x13 // done
Cmd_printselectionstringfromtable, //0x14 // done
Cmd_setadditionaleffects, //0x15 // done
Cmd_seteffectprimary, //0x16 // done
Cmd_seteffectsecondary, //0x17 // done
Cmd_clearstatusfromeffect, //0x18 // done
Cmd_tryfaintmon, //0x19 // done
Cmd_dofaintanimation, //0x1A // done
Cmd_cleareffectsonfaint, //0x1B // done
Cmd_jumpifstatus, //0x1C // done
Cmd_jumpifstatus2, //0x1D // done
Cmd_jumpifability, //0x1E // done
Cmd_jumpifsideaffecting, //0x1F // done
Cmd_jumpifstat, //0x20 // done
Cmd_jumpifstatus3condition, //0x21 // done
Cmd_jumpbasedontype, //0x22 // done
Cmd_getexp, //0x23 // done
Cmd_checkteamslost, //0x24 // done
Cmd_movevaluescleanup, //0x25 // done
Cmd_setmultihit, //0x26 // done
Cmd_decrementmultihit, //0x27 // done
Cmd_goto, //0x28 // done
Cmd_jumpifbyte, //0x29 // done
Cmd_jumpifhalfword, //0x2A // done
Cmd_jumpifword, //0x2B // done
Cmd_jumpifarrayequal, //0x2C // done
Cmd_jumpifarraynotequal, //0x2D // done
Cmd_setbyte, //0x2E // done
Cmd_addbyte, //0x2F // done
Cmd_subbyte, //0x30 // done
Cmd_copyarray, //0x31 // done
Cmd_copyarraywithindex, //0x32 // done
Cmd_orbyte, //0x33 // done
Cmd_orhalfword, //0x34 // done
Cmd_orword, //0x35 // done
Cmd_bicbyte, //0x36 // done
Cmd_bichalfword, //0x37 // done
Cmd_bicword, //0x38 // done
Cmd_pause, //0x39 // done
Cmd_waitstate, //0x3A // done
Cmd_healthbar_update, //0x3B // done
Cmd_return, //0x3C // done
Cmd_end, //0x3D // done
Cmd_end2, //0x3E // done
Cmd_end3, //0x3F // done
Cmd_unused5, //0x40 // done
Cmd_call, //0x41 // done
Cmd_setroost, //0x42 // done
Cmd_jumpifabilitypresent, //0x43 // done
Cmd_endselectionscript, //0x44 // done
Cmd_playanimation, //0x45 // done
Cmd_playanimation_var, //0x46 // done
Cmd_setgraphicalstatchangevalues, //0x47 // done
Cmd_playstatchangeanimation, //0x48 // done
Cmd_moveend, //0x49 // done
Cmd_sethealblock, //0x4A // done
Cmd_returnatktoball, //0x4B // done
Cmd_getswitchedmondata, //0x4C // done
Cmd_switchindataupdate, //0x4D // done
Cmd_switchinanim, //0x4E // done
Cmd_jumpifcantswitch, //0x4F // done
Cmd_openpartyscreen, //0x50 // done
Cmd_switchhandleorder, //0x51 // done
Cmd_switchineffects, //0x52 // done
Cmd_trainerslidein, //0x53 // done
Cmd_playse, //0x54 // done
Cmd_fanfare, //0x55 // done
Cmd_playfaintcry, //0x56 // done
Cmd_endlinkbattle, //0x57 // done
Cmd_returntoball, //0x58 // done
Cmd_handlelearnnewmove, //0x59 // done
Cmd_yesnoboxlearnmove, //0x5A // done
Cmd_yesnoboxstoplearningmove, //0x5B // done
Cmd_hitanimation, //0x5C // done
Cmd_getmoneyreward, //0x5D // done
Cmd_updatebattlermoves, //0x5E // done
Cmd_swapattackerwithtarget, //0x5F // done
Cmd_incrementgamestat, //0x60 // done
Cmd_drawpartystatussummary, //0x61 // done
Cmd_hidepartystatussummary, //0x62 // done
Cmd_jumptocalledmove, //0x63 // done
Cmd_statusanimation, //0x64 // done
Cmd_status2animation, //0x65 // done
Cmd_chosenstatusanimation, //0x66 // done
Cmd_yesnobox, //0x67 // done
Cmd_cancelallactions, //0x68 // done
Cmd_setgravity, //0x69 // done
Cmd_removeitem, //0x6A // done
Cmd_atknameinbuff1, //0x6B // done
Cmd_drawlvlupbox, //0x6C // done
Cmd_resetsentmonsvalue, //0x6D // done
Cmd_setatktoplayer0, //0x6E // done
Cmd_makevisible, //0x6F // done
Cmd_recordability, //0x70 // done
Cmd_buffermovetolearn, //0x71 // done
Cmd_jumpifplayerran, //0x72 // done
Cmd_hpthresholds, //0x73 // done
Cmd_hpthresholds2, //0x74 // done
Cmd_useitemonopponent, //0x75 // done
Cmd_various, //0x76 // done
Cmd_setprotectlike, //0x77 // done
Cmd_tryexplosion, //0x78 // done
Cmd_setatkhptozero, //0x79 // done
Cmd_jumpifnexttargetvalid, //0x7A // done
Cmd_tryhealhalfhealth, //0x7B // done
Cmd_trymirrormove, //0x7C // done
Cmd_setrain, //0x7D // done
Cmd_setreflect, //0x7E // done
Cmd_setseeded, //0x7F // done
Cmd_manipulatedamage, //0x80 // done
Cmd_trysetrest, //0x81 // done
Cmd_jumpifnotfirstturn, //0x82 // done
Cmd_nop, //0x83
Cmd_jumpifcantmakeasleep, //0x84
Cmd_stockpile, //0x85
Cmd_stockpiletobasedamage, //0x86
Cmd_stockpiletohpheal, //0x87
Cmd_negativedamage, //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_remaininghptopower, //0xAC
Cmd_tryspiteppreduce, //0xAD
Cmd_healpartystatus, //0xAE
Cmd_cursetarget, //0xAF
Cmd_trysetspikes, //0xB0
Cmd_setforesight, //0xB1
Cmd_trysetperishsong, //0xB2
Cmd_rolloutdamagecalculation, //0xB3
Cmd_jumpifconfusedandstatmaxed, //0xB4
Cmd_furycuttercalc, //0xB5
Cmd_friendshiptodamagecalculation, //0xB6
Cmd_presentdamagecalculation, //0xB7
Cmd_setsafeguard, //0xB8
Cmd_magnitudedamagecalculation, //0xB9
Cmd_jumpifnopursuitswitchdmg, //0xBA
Cmd_setsunny, //0xBB
Cmd_maxattackhalvehp, //0xBC
Cmd_copyfoestats, //0xBD
Cmd_rapidspinfree, //0xBE
Cmd_setdefensecurlbit, //0xBF
Cmd_recoverbasedonsunlight, //0xC0
Cmd_hiddenpowercalc, //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 // done
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 // done
Cmd_setgastroacid, //0xD6 // done
Cmd_setyawn, //0xD7
Cmd_setdamagetohealthdifference, //0xD8
Cmd_scaledamagebyhealthratio, //0xD9
Cmd_tryswapabilities, //0xDA
Cmd_tryimprison, //0xDB
Cmd_setstealthrock, //0xDC // done
Cmd_weightdamagecalculation, //0xDD
Cmd_assistattackselect, //0xDE
Cmd_trysetmagiccoat, //0xDF
Cmd_trysetsnatch, //0xE0
Cmd_trygetintimidatetarget, //0xE1
Cmd_switchoutabilities, //0xE2
Cmd_jumpifhasnohp, //0xE3
Cmd_getsecretpowereffect, //0xE4
Cmd_pickup, //0xE5
Cmd_docastformchangeanimation, //0xE6
Cmd_trycastformdatachange, //0xE7
Cmd_settypebasedhalvers, //0xE8
Cmd_jumpifsubstituteblocks, //0xE9 // done
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
NULL, //0xF8
NULL, //0xF9
NULL, //0xFA
NULL, //0xFB
NULL, //0xFC
NULL, //0xFD
NULL, //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_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[] =
{
[0] = BattleScript_MoveEffectSleep,
[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_FLINCH] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_TRI_ATTACK] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_UPROAR] = BattleScript_MoveEffectUproar,
[MOVE_EFFECT_PAYDAY] = BattleScript_MoveEffectPayDay,
[MOVE_EFFECT_CHARGING] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_WRAP] = BattleScript_MoveEffectWrap,
[MOVE_EFFECT_RECOIL_25] = BattleScript_MoveEffectRecoil,
[MOVE_EFFECT_ATK_PLUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_DEF_PLUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_SPD_PLUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_SP_ATK_PLUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_SP_DEF_PLUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_ACC_PLUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_EVS_PLUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_ATK_MINUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_DEF_MINUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_SPD_MINUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_SP_ATK_MINUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_SP_DEF_MINUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_ACC_MINUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_EVS_MINUS_1] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_RECHARGE] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_RAGE] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_STEAL_ITEM] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_PREVENT_ESCAPE] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_NIGHTMARE] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_ALL_STATS_UP] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_RAPIDSPIN] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_REMOVE_PARALYSIS] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_ATK_DEF_DOWN] = BattleScript_MoveEffectSleep,
[MOVE_EFFECT_RECOIL_33] = BattleScript_MoveEffectRecoil,
};
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 = 0,
.mosaic = FALSE,
.bpp = 0,
.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};
#define MIMIC_FORBIDDEN_END 0xFFFE
#define METRONOME_FORBIDDEN_END 0xFFFF
#define ASSIST_FORBIDDEN_END 0xFFFF
static const u16 sMovesForbiddenToCopy[] =
{
MOVE_METRONOME,
MOVE_STRUGGLE,
MOVE_SKETCH,
MOVE_MIMIC,
MIMIC_FORBIDDEN_END,
MOVE_COUNTER,
MOVE_MIRROR_COAT,
MOVE_PROTECT,
MOVE_DETECT,
MOVE_ENDURE,
MOVE_DESTINY_BOND,
MOVE_SLEEP_TALK,
MOVE_THIEF,
MOVE_FOLLOW_ME,
MOVE_SNATCH,
MOVE_HELPING_HAND,
MOVE_COVET,
MOVE_TRICK,
MOVE_FOCUS_PUNCH,
METRONOME_FORBIDDEN_END
};
static const u8 sFlailHpScaleToPowerTable[] =
{
1, 200,
4, 150,
9, 100,
16, 80,
32, 40,
48, 20
};
static const u16 sFinalStrikeOnlyEffects[] =
{
MOVE_EFFECT_BUG_BITE,
MOVE_EFFECT_STEAL_ITEM,
MOVE_EFFECT_REMOVE_ARG_TYPE,
MOVE_EFFECT_SMACK_DOWN,
MOVE_EFFECT_REMOVE_STATUS,
MOVE_EFFECT_RECOIL_HP_25,
MOVE_EFFECT_PREVENT_ESCAPE,
MOVE_EFFECT_WRAP,
};
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,
};
// format: min. weight (hectograms), base power
static const u16 sWeightToDamageTable[] =
{
100, 20,
250, 40,
500, 60,
1000, 80,
2000, 100,
0xFFFF, 0xFFFF
};
struct PickupItem
{
u16 itemId;
u8 chance;
};
static const struct PickupItem sPickupItems[] =
{
{ ITEM_ORAN_BERRY, 15 },
{ ITEM_CHERI_BERRY, 25 },
{ ITEM_CHESTO_BERRY, 35 },
{ ITEM_PECHA_BERRY, 45 },
{ ITEM_RAWST_BERRY, 55 },
{ ITEM_ASPEAR_BERRY, 65 },
{ ITEM_PERSIM_BERRY, 75 },
{ ITEM_TM10, 80 },
{ ITEM_PP_UP, 85 },
{ ITEM_RARE_CANDY, 90 },
{ ITEM_NUGGET, 95 },
{ ITEM_SPELON_BERRY, 96 },
{ ITEM_PAMTRE_BERRY, 97 },
{ ITEM_WATMEL_BERRY, 98 },
{ ITEM_DURIN_BERRY, 99 },
{ ITEM_BELUE_BERRY, 1 },
};
static const u8 sTerrainToType[] =
{
[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_MOUNTAIN] = TYPE_ROCK,
[BATTLE_TERRAIN_CAVE] = TYPE_ROCK,
[BATTLE_TERRAIN_BUILDING] = TYPE_NORMAL,
[BATTLE_TERRAIN_PLAIN] = TYPE_NORMAL,
};
// - ITEM_ULTRA_BALL skips Master Ball and ITEM_NONE
static const u8 sBallCatchBonuses[] =
{
[ITEM_ULTRA_BALL - ITEM_ULTRA_BALL] = 20,
[ITEM_GREAT_BALL - ITEM_ULTRA_BALL] = 15,
[ITEM_POKE_BALL - ITEM_ULTRA_BALL] = 10,
[ITEM_SAFARI_BALL - ITEM_ULTRA_BALL] = 15
};
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;
}
// TODO: Convert this to a proper FORM_CHANGE type.
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;
return FALSE;
}
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;
}
static bool32 IsMoveNotAllowedInSkyBattles(u32 move)
{
return ((gBattleStruct->isSkyBattle) && (gMovesInfo[gCurrentMove].skyBattleBanned));
}
static void Cmd_attackcanceler(void)
{
CMD_ARGS();
s32 i, moveType;
u16 attackerAbility = GetBattlerAbility(gBattlerAttacker);
GET_MOVE_TYPE(gCurrentMove, moveType);
// TODO: dynamax
// 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 && gMovesInfo[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*/) // TODO: Z-Moves
{
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)
&& (!gBattleMoveEffects[gMovesInfo[gCurrentMove].effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))
|| (IsMoveNotAllowedInSkyBattles(gCurrentMove)))
{
if (gMovesInfo[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 (!gBattleMoveEffects[gMovesInfo[gCurrentMove].effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
CancelMultiTurnMoves(gBattlerAttacker);
return;
}
if (gProtectStructs[gBattlerTarget].bounceMove
&& gMovesInfo[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
&& gMovesInfo[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)
// TODO: Z-Moves and Dynamax
// 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) && gMovesInfo[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))
&& (!gBattleMoveEffects[gMovesInfo[gCurrentMove].effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
&& gMovesInfo[gCurrentMove].effect != EFFECT_SUCKER_PUNCH
&& gMovesInfo[gCurrentMove].effect != EFFECT_UPPER_HAND)
{
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;
}
// used to be Cmd_jumpifaffectedbyprotect
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 && gMovesInfo[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(gBattlerAttacker) == 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)
&& gMovesInfo[move].effect != EFFECT_OHKO)
{
JumpIfMoveFailed(7, move);
return TRUE;
}
// TODO: Z-Moves
// 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) && !(gMovesInfo[move].damagesAirborne || gMovesInfo[move].damagesAirborneDoubleDamage))
|| ((gStatuses3[gBattlerTarget] & STATUS3_UNDERGROUND) && !gMovesInfo[move].damagesUnderground)
|| ((gStatuses3[gBattlerTarget] & STATUS3_UNDERWATER) && !gMovesInfo[move].damagesUnderwater))
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
JumpIfMoveFailed(7, move);
return TRUE;
}
if (WEATHER_HAS_EFFECT)
{
if ((gMovesInfo[move].effect == EFFECT_THUNDER || gMovesInfo[move].effect == EFFECT_RAIN_ALWAYS_HIT)
&& IsBattlerWeatherAffected(gBattlerTarget, B_WEATHER_RAIN))
{
// thunder/hurricane/genie moves ignore acc checks in rain unless target is holding utility umbrella
JumpIfMoveFailed(7, move);
return TRUE;
}
else if ((gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)) && gMovesInfo[move].effect == EFFECT_BLIZZARD)
{
// Blizzard ignores acc checks in Hail in Gen4+
JumpIfMoveFailed(7, move);
return TRUE;
}
}
if (B_MINIMIZE_DMG_ACC >= GEN_6
&& (gStatuses3[gBattlerTarget] & STATUS3_MINIMIZED)
&& gMovesInfo[move].minimizeDoubleDamage)
{
JumpIfMoveFailed(7, move);
return TRUE;
}
if (gMovesInfo[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 (gMovesInfo[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 = gMovesInfo[move].accuracy;
// Check Thunder and Hurricane on sunny weather.
if (IsBattlerWeatherAffected(battlerDef, B_WEATHER_SUN) && gMovesInfo[move].effect == EFFECT_THUNDER)
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);
// pokefirered specific
if ((gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE
&& !BtlCtrl_OakOldMan_TestState2Flag(1)
&& gMovesInfo[move].power != 0
&& GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
|| (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE
&& !BtlCtrl_OakOldMan_TestState2Flag(2)
&& gMovesInfo[move].power == 0
&& GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
|| (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE))
{
JumpIfMoveFailed(7, move);
return;
}
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
|| !(gMovesInfo[move].effect == EFFECT_TRIPLE_KICK || gMovesInfo[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 (gMovesInfo[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 (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
if (moveTarget == MOVE_TARGET_BOTH
|| moveTarget == MOVE_TARGET_FOES_AND_ALLY
|| moveTarget == MOVE_TARGET_ALL_BATTLERS
|| gMovesInfo[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(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
|| abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR)
{
critChance = -1;
}
else if (gStatuses3[battlerAtk] & STATUS3_LASER_FOCUS
|| gMovesInfo[gCurrentMove].alwaysCriticalHit
|| (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY))
{
critChance = -2;
}
else
{
critChance = 2 * ((gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY) != 0)
+ 1 * ((gBattleMons[battlerAtk].status2 & STATUS2_DRAGON_CHEER) != 0)
+ gMovesInfo[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
static void Cmd_critcalc(void)
{
CMD_ARGS();
u16 partySlot;
s32 critChance = CalcCritChanceStage(gBattlerAttacker, gBattlerTarget, gCurrentMove, TRUE);
gPotentialItemEffectBattler = gBattlerAttacker;
if ((gBattleTypeFlags & (BATTLE_TYPE_OLD_MAN_TUTORIAL | BATTLE_TYPE_POKEDUDE))
|| ((gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) && !BtlCtrl_OakOldMan_TestState2Flag(1)))
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;
}
void AI_CalcDmg(u8 attacker, u8 defender)
{
u32 sideStatus = gSideStatuses[GET_BATTLER_SIDE(defender)];
gBattleMoveDamage = CalculateBaseDamageOld(&gBattleMons[attacker], &gBattleMons[defender], gCurrentMove,
sideStatus, gDynamicBasePower,
gBattleStruct->dynamicMoveType, attacker, defender);
gDynamicBasePower = 0;
gBattleMoveDamage = gBattleMoveDamage * gCritMultiplier * gBattleScripting.dmgMultiplier;
if (gStatuses3[attacker] & STATUS3_CHARGED_UP && gMovesInfo[gCurrentMove].type == TYPE_ELECTRIC)
gBattleMoveDamage *= 2;
if (gProtectStructs[attacker].helpingHand)
gBattleMoveDamage = gBattleMoveDamage * 15 / 10;
}
static void ModulateDmgByType(u8 multiplier)
{
gBattleMoveDamage = gBattleMoveDamage * multiplier / 10;
if (gBattleMoveDamage == 0 && multiplier != 0)
gBattleMoveDamage = 1;
switch (multiplier)
{
case TYPE_MUL_NO_EFFECT:
gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE;
gMoveResultFlags &= ~MOVE_RESULT_NOT_VERY_EFFECTIVE;
gMoveResultFlags &= ~MOVE_RESULT_SUPER_EFFECTIVE;
break;
case TYPE_MUL_NOT_EFFECTIVE:
if (gMovesInfo[gCurrentMove].power && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
{
if (gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE)
gMoveResultFlags &= ~MOVE_RESULT_SUPER_EFFECTIVE;
else
gMoveResultFlags |= MOVE_RESULT_NOT_VERY_EFFECTIVE;
}
break;
case TYPE_MUL_SUPER_EFFECTIVE:
if (gMovesInfo[gCurrentMove].power && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
{
if (gMoveResultFlags & MOVE_RESULT_NOT_VERY_EFFECTIVE)
gMoveResultFlags &= ~MOVE_RESULT_NOT_VERY_EFFECTIVE;
else
gMoveResultFlags |= MOVE_RESULT_SUPER_EFFECTIVE;
}
break;
}
}
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 CheckWonderGuardAndLevitate(void)
{
u8 flags = 0;
s32 i = 0;
u8 moveType;
if (gCurrentMove == MOVE_STRUGGLE || !gMovesInfo[gCurrentMove].power)
return;
GET_MOVE_TYPE(gCurrentMove, moveType);
if (gBattleMons[gBattlerTarget].ability == ABILITY_LEVITATE && moveType == TYPE_GROUND)
{
gLastUsedAbility = ABILITY_LEVITATE;
gBattleCommunication[MISS_TYPE] = B_MSG_GROUND_MISS;
RecordAbilityBattle(gBattlerTarget, ABILITY_LEVITATE);
return;
}
while (TYPE_EFFECT_ATK_TYPE(i) != TYPE_ENDTABLE)
{
if (TYPE_EFFECT_ATK_TYPE(i) == TYPE_FORESIGHT)
{
if (gBattleMons[gBattlerTarget].status2 & STATUS2_FORESIGHT)
break;
i += 3;
continue;
}
if (TYPE_EFFECT_ATK_TYPE(i) == moveType)
{
// check no effect
if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type1
&& TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NO_EFFECT)
{
gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE;
gProtectStructs[gBattlerAttacker].targetNotAffected = 1;
}
if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2 &&
gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2 &&
TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NO_EFFECT)
{
gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE;
gProtectStructs[gBattlerAttacker].targetNotAffected = 1;
}
// check super effective
if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type1 && TYPE_EFFECT_MULTIPLIER(i) == 20)
flags |= 1;
if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2
&& gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2
&& TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_SUPER_EFFECTIVE)
flags |= 1;
// check not very effective
if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type1 && TYPE_EFFECT_MULTIPLIER(i) == 5)
flags |= 2;
if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2
&& gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2
&& TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NOT_EFFECTIVE)
flags |= 2;
}
i += 3;
}
if (gBattleMons[gBattlerTarget].ability == ABILITY_WONDER_GUARD && AttacksThisTurn(gBattlerAttacker, gCurrentMove) == 2)
{
if (((flags & 2) || !(flags & 1)) && gMovesInfo[gCurrentMove].power)
{
gLastUsedAbility = ABILITY_WONDER_GUARD;
gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_DMG;
RecordAbilityBattle(gBattlerTarget, ABILITY_WONDER_GUARD);
}
}
}
// Same as ModulateDmgByType except different arguments
static void ModulateDmgByType2(u8 multiplier, u16 move, u8 *flags)
{
gBattleMoveDamage = gBattleMoveDamage * multiplier / 10;
if (gBattleMoveDamage == 0 && multiplier != 0)
gBattleMoveDamage = 1;
switch (multiplier)
{
case TYPE_MUL_NO_EFFECT:
*flags |= MOVE_RESULT_DOESNT_AFFECT_FOE;
*flags &= ~MOVE_RESULT_NOT_VERY_EFFECTIVE;
*flags &= ~MOVE_RESULT_SUPER_EFFECTIVE;
break;
case TYPE_MUL_NOT_EFFECTIVE:
if (gMovesInfo[move].power && !(*flags & MOVE_RESULT_NO_EFFECT))
{
if (*flags & MOVE_RESULT_SUPER_EFFECTIVE)
*flags &= ~MOVE_RESULT_SUPER_EFFECTIVE;
else
*flags |= MOVE_RESULT_NOT_VERY_EFFECTIVE;
}
break;
case TYPE_MUL_SUPER_EFFECTIVE:
if (gMovesInfo[move].power && !(*flags & MOVE_RESULT_NO_EFFECT))
{
if (*flags & MOVE_RESULT_NOT_VERY_EFFECTIVE)
*flags &= ~MOVE_RESULT_NOT_VERY_EFFECTIVE;
else
*flags |= MOVE_RESULT_SUPER_EFFECTIVE;
}
break;
}
}
u8 TypeCalc(u16 move, u8 attacker, u8 defender)
{
s32 i = 0;
u8 flags = 0;
u8 moveType;
if (move == MOVE_STRUGGLE)
return 0;
moveType = gMovesInfo[move].type;
// check stab
if (IS_BATTLER_OF_TYPE(attacker, moveType))
{
gBattleMoveDamage = gBattleMoveDamage * 15;
gBattleMoveDamage = gBattleMoveDamage / 10;
}
if (gBattleMons[defender].ability == ABILITY_LEVITATE && moveType == TYPE_GROUND)
{
flags |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE);
}
else
{
while (TYPE_EFFECT_ATK_TYPE(i) != TYPE_ENDTABLE)
{
if (TYPE_EFFECT_ATK_TYPE(i) == TYPE_FORESIGHT)
{
if (gBattleMons[defender].status2 & STATUS2_FORESIGHT)
break;
i += 3;
continue;
}
else if (TYPE_EFFECT_ATK_TYPE(i) == moveType)
{
// check type1
if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[defender].type1)
ModulateDmgByType2(TYPE_EFFECT_MULTIPLIER(i), move, &flags);
// check type2
if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[defender].type2 &&
gBattleMons[defender].type1 != gBattleMons[defender].type2)
ModulateDmgByType2(TYPE_EFFECT_MULTIPLIER(i), move, &flags);
}
i += 3;
}
}
if (gBattleMons[defender].ability == ABILITY_WONDER_GUARD && !(flags & MOVE_RESULT_MISSED)
&& AttacksThisTurn(attacker, move) == 2
&& (!(flags & MOVE_RESULT_SUPER_EFFECTIVE) || ((flags & (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)) == (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)))
&& gMovesInfo[move].power)
{
flags |= MOVE_RESULT_MISSED;
}
return flags;
}
u8 AI_TypeCalc(u16 move, u16 targetSpecies, u16 targetAbility)
{
s32 i = 0;
u8 flags = 0;
u8 type1 = gSpeciesInfo[targetSpecies].types[0], type2 = gSpeciesInfo[targetSpecies].types[1];
u8 moveType;
if (move == MOVE_STRUGGLE)
return 0;
moveType = gMovesInfo[move].type;
if (targetAbility == ABILITY_LEVITATE && moveType == TYPE_GROUND)
{
flags = MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE;
}
else
{
while (TYPE_EFFECT_ATK_TYPE(i) != TYPE_ENDTABLE)
{
if (TYPE_EFFECT_ATK_TYPE(i) == TYPE_FORESIGHT)
{
i += 3;
continue;
}
if (TYPE_EFFECT_ATK_TYPE(i) == moveType)
{
// check type1
if (TYPE_EFFECT_DEF_TYPE(i) == type1)
ModulateDmgByType2(TYPE_EFFECT_MULTIPLIER(i), move, &flags);
// check type2
if (TYPE_EFFECT_DEF_TYPE(i) == type2 && type1 != type2)
ModulateDmgByType2(TYPE_EFFECT_MULTIPLIER(i), move, &flags);
}
i += 3;
}
}
if (targetAbility == ABILITY_WONDER_GUARD
&& (!(flags & MOVE_RESULT_SUPER_EFFECTIVE) || ((flags & (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)) == (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)))
&& gMovesInfo[move].power)
flags |= MOVE_RESULT_DOESNT_AFFECT_FOE;
return flags;
}
// Multiplies the damage by a random factor between 85% to 100% inclusive
static inline void ApplyRandomDmgMultiplier(void)
{
u16 rand = Random();
u16 randPercent = 100 - (rand % 16);
if (gBattleMoveDamage != 0)
{
gBattleMoveDamage *= randPercent;
gBattleMoveDamage /= 100;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
}
}
static void Unused_ApplyRandomDmgMultiplier(void)
{
ApplyRandomDmgMultiplier();
}
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(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 (gMovesInfo[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
&& gMovesInfo[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;
gActiveBattler = gBattlerAttacker;
BtlController_EmitMoveAnimation(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);
gActiveBattler = battler;
if (DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) && gDisableStructs[battler].substituteHP && !(gHitMarker & HITMARKER_IGNORE_SUBSTITUTE))
{
PrepareStringBattle(STRINGID_SUBSTITUTEDAMAGED, battler);
}
else if (!DoesDisguiseBlockMove(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(BUFFER_A, healthValue);
MarkBattlerForControllerExec(battler);
if (GetBattlerSide(battler) == B_SIDE_PLAYER && gBattleMoveDamage > 0)
gBattleResults.playerMonWasDamaged = TRUE;
}
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
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);
gActiveBattler = 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(battler, gCurrentMove))
{
// TODO: Convert this to a proper FORM_CHANGE type.
u32 side = GetBattlerSide(battler);
gBattleScripting.battler = battler;
if (gBattleStruct->changedSpecies[side][gBattlerPartyIndexes[battler]] == SPECIES_NONE)
gBattleStruct->changedSpecies[side][gBattlerPartyIndexes[battler]] = gBattleMons[battler].species;
// TODO: Totems
// if (gBattleMons[battler].species == SPECIES_MIMIKYU_TOTEM_DISGUISED)
// gBattleMons[battler].species = SPECIES_MIMIKYU_TOTEM_BUSTED;
// else
// gBattleMons[battler].species = SPECIES_MIMIKYU_BUSTED;
gBattleMons[battler].species = SPECIES_MIMIKYU_BUSTED;
if (B_DISGUISE_HP_LOSS >= GEN_8)
{
// TODO: Dynamax
// gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 8;
gBattleMoveDamage = gBattleMons[battler].maxHP / 8;
}
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) && gMovesInfo[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) && gMovesInfo[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(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;
gActiveBattler = gBattlerTarget;
if (!(gMoveResultFlags & MOVE_RESULT_MISSED))
{
switch (gMoveResultFlags & ~MOVE_RESULT_MISSED)
{
case MOVE_RESULT_SUPER_EFFECTIVE:
BtlController_EmitPlaySE(BUFFER_A, SE_SUPER_EFFECTIVE);
MarkBattlerForControllerExec(gBattlerTarget);
break;
case MOVE_RESULT_NOT_VERY_EFFECTIVE:
BtlController_EmitPlaySE(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(BUFFER_A, SE_SUPER_EFFECTIVE);
MarkBattlerForControllerExec(gBattlerTarget);
}
else if (gMoveResultFlags & MOVE_RESULT_NOT_VERY_EFFECTIVE)
{
BtlController_EmitPlaySE(BUFFER_A, SE_NOT_EFFECTIVE);
MarkBattlerForControllerExec(gBattlerTarget);
}
else if (!(gMoveResultFlags & (MOVE_RESULT_DOESNT_AFFECT_FOE | MOVE_RESULT_FAILED)))
{
BtlController_EmitPlaySE(BUFFER_A, SE_EFFECTIVE);
MarkBattlerForControllerExec(gBattlerTarget);
}
break;
}
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_resultmessage(void)
{
CMD_ARGS();
u32 stringId = 0;
if (gBattleControllerExecFlags)
return;
// TODO: Convert this to a proper FORM_CHANGE type.
// 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))
{
// TODO: Ability Popup
// 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);
gActiveBattler = gBattlerAttacker;
BtlController_EmitPrintSelectionString(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];
gActiveBattler = gBattlerAttacker;
BtlController_EmitPrintSelectionString(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;
gActiveBattler = battlerStealer;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gLastUsedItem), &gLastUsedItem); // set attacker item
MarkBattlerForControllerExec(battlerStealer);
gActiveBattler = battlerItem;
BtlController_EmitSetMonData(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, bool32 certain)
{
s32 i, affectsUser = 0;
bool32 statusChanged = FALSE;
bool32 mirrorArmorReflected = (GetBattlerAbility(gBattlerTarget) == ABILITY_MIRROR_ARMOR);
u32 flags = 0;
u16 battlerAbility;
bool8 activateAfterFaint = FALSE;
// NULL move effect
if (gBattleScripting.moveEffect == 0)
return;
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_1ST_HIT
&& gBattleMons[gBattlerTarget].hp != 0
&& IsFinalStrikeEffect(gBattleScripting.moveEffect))
{
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 (!primary && affectsUser != MOVE_EFFECT_AFFECTS_USER
&& !(gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
&& (battlerAbility == ABILITY_SHIELD_DUST || GetBattlerHoldEffect(gEffectBattler, TRUE) == HOLD_EFFECT_COVERT_CLOAK))
{
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 (!(gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
&& TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)
&& !primary
&& 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 || B_UPROAR_IGNORE_SOUNDPROOF >= GEN_5)
{
for (i = 0; i < gBattlersCount && !(gBattleMons[i].status2 & STATUS2_UPROAR); i++)
;
}
else
{
i = gBattlersCount;
gActiveBattler = 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 == TRUE))
{
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 == TRUE))
{
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 ((battlerAbility == ABILITY_WATER_VEIL || battlerAbility == ABILITY_WATER_BUBBLE)
&& (primary == TRUE || certain == TRUE))
{
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 == TRUE))
{
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 == FALSE && 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 == FALSE && 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 == TRUE)
{
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 == FALSE && IS_BATTLER_OF_TYPE(gEffectBattler, moveType))
break;
}
if (!CanParalyzeType(gBattleScripting.battler, gEffectBattler)
&& (gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT)
&& (primary == TRUE || certain == TRUE))
{
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 == TRUE))
{
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 == TRUE))
{
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 == FALSE && 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];
gActiveBattler = gEffectBattler;
BtlController_EmitSetMonData(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 (!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)
{
// Inner Focus ALWAYS prevents flinching but only activates
// on a move that's supposed to flinch, like Fake Out
if (primary == TRUE || certain == TRUE)
{
gLastUsedAbility = ABILITY_INNER_FOCUS;
gBattlerAbility = gEffectBattler;
RecordAbilityBattle(gEffectBattler, ABILITY_INNER_FOCUS);
gBattlescriptCurrInstr = BattleScript_FlinchPrevention;
}
else
{
gBattlescriptCurrInstr++;
}
}
else if (GetBattlerTurnOrderNum(gEffectBattler) > gCurrentTurnActionNumber
/* && !IsDynamaxed(gEffectBattler) */) // TODO: Dynamax
{
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;
u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
gPaydayMoney += (gBattleMons[gBattlerAttacker].level * 5);
if (payday > gPaydayMoney)
gPaydayMoney = 0xFFFF;
// For a move that hits multiple targets (i.e. Make it Rain)
// we only want to print the message on the final hit
if (!((moveTarget == MOVE_TARGET_BOTH || moveTarget == MOVE_TARGET_FOES_AND_ALLY)
&& GetNextTarget(moveTarget, TRUE) != MAX_BATTLERS_COUNT))
{
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_MoveEffectPayDay;
}
else
gBattlescriptCurrInstr++;
}
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,
MOVE_EFFECT_FREEZE_OR_FROSTBITE,
MOVE_EFFECT_PARALYSIS
};
gBattleScripting.moveEffect = RandomElement(RNG_TRI_ATTACK, sTriAttackEffects);
SetMoveEffect(primary, certain);
}
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)
flags |= (STAT_CHANGE_ALLOW_PTR * !affectsUser);
else
flags |= STAT_CHANGE_UPDATE_MOVE_EFFECT;
if (ChangeStatBuffs(SET_STAT_BUFF_VALUE(1) | STAT_BUFF_NEGATIVE,
gBattleScripting.moveEffect - MOVE_EFFECT_ATK_MINUS_1 + 1,
flags, 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_BATTLE_TOWER
| BATTLE_TYPE_LINK))
&& gTrainerBattleOpponent_A != TRAINER_SECRET_BASE)
{
gBattlescriptCurrInstr++;
}
else if (!(gBattleTypeFlags &
(BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_BATTLE_TOWER
| BATTLE_TYPE_LINK))
&& gTrainerBattleOpponent_A != TRAINER_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_RAPID_SPIN:
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:
// Petal Dance doesn't lock mons that copy the move with Dancer
if (gSpecialStatuses[gEffectBattler].dancerUsedMove)
{
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);
gActiveBattler = gEffectBattler;
BtlController_EmitSetMonData(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_BERRY_POUCH
&& battlerAbility != ABILITY_STICKY_HOLD)
{
// target loses their berry
gLastUsedItem = gBattleMons[gEffectBattler].item;
gBattleMons[gEffectBattler].item = 0;
CheckSetUnburden(gEffectBattler);
gActiveBattler = gEffectBattler;
BtlController_EmitSetMonData(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_REMOVE_ARG_TYPE:
// This seems unnecessary but is done to make it work properly with Parental Bond
BattleScriptPush(gBattlescriptCurrInstr + 1);
switch (gMovesInfo[gCurrentMove].argument)
{
case TYPE_FIRE: // Burn Up
gBattlescriptCurrInstr = BattleScript_RemoveFireType;
break;
case TYPE_ELECTRIC: // Double Shot
gBattlescriptCurrInstr = BattleScript_RemoveElectricType;
break;
default:
gBattlescriptCurrInstr = BattleScript_RemoveGenericType;
break;
}
RemoveBattlerType(gEffectBattler, gMovesInfo[gCurrentMove].argument);
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(primary, certain);
}
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_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]]);
gBattleStruct->stickySyrupdBy[gEffectBattler] = gBattlerAttacker;
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_SyrupBombActivates;
}
break;
case MOVE_EFFECT_SECRET_POWER:
if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)
{
switch (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)
{
case STATUS_FIELD_MISTY_TERRAIN:
gBattleScripting.moveEffect = MOVE_EFFECT_SP_ATK_MINUS_1;
break;
case STATUS_FIELD_GRASSY_TERRAIN:
gBattleScripting.moveEffect = MOVE_EFFECT_SLEEP;
break;
case STATUS_FIELD_ELECTRIC_TERRAIN:
gBattleScripting.moveEffect = MOVE_EFFECT_PARALYSIS;
break;
case STATUS_FIELD_PSYCHIC_TERRAIN:
gBattleScripting.moveEffect = MOVE_EFFECT_SPD_MINUS_1;
break;
default:
gBattleScripting.moveEffect = MOVE_EFFECT_PARALYSIS;
break;
}
}
else
{
switch (gBattleTerrain)
{
case BATTLE_TERRAIN_GRASS:
gBattleScripting.moveEffect = (B_SECRET_POWER_EFFECT >= GEN_4 ? MOVE_EFFECT_SLEEP : MOVE_EFFECT_POISON);
break;
case BATTLE_TERRAIN_UNDERWATER:
gBattleScripting.moveEffect = (B_SECRET_POWER_EFFECT >= GEN_6 ? MOVE_EFFECT_ATK_MINUS_1 : MOVE_EFFECT_DEF_MINUS_1);
break;
case BATTLE_TERRAIN_POND:
gBattleScripting.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)
gBattleScripting.moveEffect = MOVE_EFFECT_ACC_MINUS_1;
else if (B_SECRET_POWER_EFFECT >= GEN_4)
gBattleScripting.moveEffect = MOVE_EFFECT_FLINCH;
else
gBattleScripting.moveEffect = MOVE_EFFECT_CONFUSION;
break;
case BATTLE_TERRAIN_PUDDLE:
gBattleScripting.moveEffect = (B_SECRET_POWER_EFFECT >= GEN_5 ? MOVE_EFFECT_SPD_MINUS_1 : MOVE_EFFECT_ACC_MINUS_1);
break;
case BATTLE_TERRAIN_LONG_GRASS:
gBattleScripting.moveEffect = MOVE_EFFECT_SLEEP;
break;
case BATTLE_TERRAIN_SAND:
gBattleScripting.moveEffect = MOVE_EFFECT_ACC_MINUS_1;
break;
case BATTLE_TERRAIN_WATER:
gBattleScripting.moveEffect = MOVE_EFFECT_ATK_MINUS_1;
break;
case BATTLE_TERRAIN_CAVE:
case BATTLE_TERRAIN_BURIAL_GROUND:
case BATTLE_TERRAIN_SPACE:
gBattleScripting.moveEffect = MOVE_EFFECT_FLINCH;
break;
case BATTLE_TERRAIN_SOARING:
case BATTLE_TERRAIN_SKY_PILLAR:
case BATTLE_TERRAIN_MARSH:
case BATTLE_TERRAIN_SWAMP:
gBattleScripting.moveEffect = MOVE_EFFECT_SPD_MINUS_1;
break;
case BATTLE_TERRAIN_SNOW:
case BATTLE_TERRAIN_ICE:
gBattleScripting.moveEffect = MOVE_EFFECT_FREEZE_OR_FROSTBITE;
break;
case BATTLE_TERRAIN_VOLCANO:
gBattleScripting.moveEffect = MOVE_EFFECT_BURN;
break;
case BATTLE_TERRAIN_ULTRA_SPACE:
gBattleScripting.moveEffect = MOVE_EFFECT_DEF_MINUS_1;
break;
default:
gBattleScripting.moveEffect = MOVE_EFFECT_PARALYSIS;
break;
}
}
SetMoveEffect(primary, certain);
break;
case MOVE_EFFECT_PSYCHIC_NOISE:
battlerAbility = IsAbilityOnSide(gEffectBattler, ABILITY_AROMA_VEIL);
if (battlerAbility)
{
gBattlerAbility = battlerAbility - 1;
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_AromaVeilProtectsRet;
}
else if (!(gStatuses3[gEffectBattler] & STATUS3_HEAL_BLOCK))
{
gStatuses3[gEffectBattler] |= STATUS3_HEAL_BLOCK;
gDisableStructs[gEffectBattler].healBlockTimer = 2;
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_EffectPsychicNoise;
}
break;
}
}
}
gBattleScripting.moveEffect = 0;
}
static bool32 CanApplyAdditionalEffect(const struct AdditionalEffect *additionalEffect)
{
// Self-targeting move effects only apply after the last mon has been hit
u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
if (additionalEffect->self
&& (moveTarget == MOVE_TARGET_BOTH || moveTarget == MOVE_TARGET_FOES_AND_ALLY)
&& GetNextTarget(moveTarget, TRUE) != MAX_BATTLERS_COUNT)
return FALSE;
// Certain move effects only apply if the target raised stats this turn (e.g. Burning Jealousy)
if (additionalEffect->onlyIfTargetRaisedStats && !gProtectStructs[gBattlerTarget].statRaised)
return FALSE;
// Certain additional effects only apply on a two-turn move's charge turn
if (additionalEffect->onChargeTurnOnly != gProtectStructs[gBattlerAttacker].chargingTurn)
return FALSE;
return TRUE;
}
static void Cmd_setadditionaleffects(void)
{
CMD_ARGS();
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
{
if (gMovesInfo[gCurrentMove].numAdditionalEffects > gBattleStruct->additionalEffectsCounter)
{
u32 percentChance;
const struct AdditionalEffect *additionalEffect = &gMovesInfo[gCurrentMove].additionalEffects[gBattleStruct->additionalEffectsCounter];
const u8 *currentPtr = gBattlescriptCurrInstr;
// Various checks for if this move effect can be applied this turn
if (CanApplyAdditionalEffect(additionalEffect))
{
percentChance = CalcSecondaryEffectChance(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), additionalEffect);
// Activate effect if it's primary (chance == 0) or if RNGesus says so
if ((percentChance == 0) || RandomPercentage(RNG_SECONDARY_EFFECT + gBattleStruct->additionalEffectsCounter, percentChance))
{
gBattleScripting.moveEffect = additionalEffect->moveEffect | (MOVE_EFFECT_AFFECTS_USER * (additionalEffect->self));
SetMoveEffect(
percentChance == 0, // a primary effect
percentChance >= 100 // certain to happen
);
}
}
// Move script along if we haven't jumped elsewhere
if (gBattlescriptCurrInstr == currentPtr)
gBattlescriptCurrInstr = cmd->nextInstr;
// Call setadditionaleffects again in the case of a move with multiple effects
gBattleStruct->additionalEffectsCounter++;
if (gMovesInfo[gCurrentMove].numAdditionalEffects > gBattleStruct->additionalEffectsCounter)
gBattleScripting.moveEffect = MOVE_EFFECT_CONTINUE;
else
gBattleScripting.moveEffect = gBattleStruct->additionalEffectsCounter = 0;
}
else
{
gBattleScripting.moveEffect = 0;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
else
{
gBattleScripting.moveEffect = 0;
gBattlescriptCurrInstr = cmd->nextInstr;
}
gBattleScripting.multihitMoveEffect = 0;
}
static void Cmd_seteffectprimary(void)
{
CMD_ARGS();
SetMoveEffect(TRUE, FALSE);
}
static void Cmd_seteffectsecondary(void)
{
CMD_ARGS();
SetMoveEffect(FALSE, FALSE);
}
static void Cmd_clearstatusfromeffect(void)
{
CMD_ARGS(u8 battler);
u32 battler = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
if (gBattleScripting.moveEffect <= PRIMARY_STATUS_MOVE_EFFECT)
gBattleMons[battler].status1 &= (~sStatusFlagsForMoveEffects[gBattleScripting.moveEffect]);
else
{
gBattleMons[battler].status2 &= (~sStatusFlagsForMoveEffects[gBattleScripting.moveEffect]);
if (gBattleScripting.moveEffect == MOVE_EFFECT_CHARGING)
gProtectStructs[battler].chargingTurn = FALSE;
}
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 = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
if (cmd->isSpikes != FALSE)
{
if (gHitMarker & HITMARKER_FAINTED(battler))
{
BattleScriptPop();
gBattlescriptCurrInstr = cmd->instr;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
else
{
if (cmd->battler == BS_ATTACKER)
{
gActiveBattler = gBattlerAttacker;
destinyBondBattler = gBattlerTarget;
faintScript = BattleScript_FaintAttacker;
}
else
{
gActiveBattler = gBattlerTarget;
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) */) // TODO: Dynamax
{
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;
gActiveBattler = gBattlerAttacker;
BtlController_EmitSetMonData(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 = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
BtlController_EmitFaintAnimation(BUFFER_A);
MarkBattlerForControllerExec(battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
static void Cmd_cleareffectsonfaint(void)
{
CMD_ARGS(u8 battler);
if (gBattleControllerExecFlags == 0)
{
u32 battler = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
const u8 *clearDataResult = NULL;
gBattleMons[battler].status1 = 0;
BtlController_EmitSetMonData(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 (gBattleTypeFlags &
( BATTLE_TYPE_LINK
| BATTLE_TYPE_TRAINER_TOWER
| BATTLE_TYPE_BATTLE_TOWER
| BATTLE_TYPE_SAFARI
| 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)
#if FREE_ENIGMA_BERRY == FALSE
holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
#else
holdEffect = 0;
#endif //FREE_ENIGMA_BERRY
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; // GetSoftLevelCapExpValue(gPlayerParty[*expMonId].level, gBattleStruct->expValue); TODO: Level caps?
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->expValue; // GetSoftLevelCapExpValue(gPlayerParty[*expMonId].level, gBattleStruct->expShareExpValue); TODO: Level caps?
}
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; TODO: bufferB as part of battle resource
gBattleBufferB[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);
gActiveBattler = gBattleStruct->expGetterBattlerId;
BtlController_EmitExpUpdate(BUFFER_A, *expMonId, gBattleMoveDamage);
MarkBattlerForControllerExec(gBattleStruct->expGetterBattlerId);
}
gBattleScripting.getexpState++;
}
break;
case 4: // lvl up if necessary
if (gBattleControllerExecFlags == 0)
{
u32 expBattler = gBattleStruct->expGetterBattlerId;
if (gBattleBufferB[expBattler][0] == CONTROLLER_TWORETURNVALUES && gBattleBufferB[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(&gBattleBufferB[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;
}
}
bool32 NoAliveMonsForPlayer(void)
{
u32 i;
u32 maxI = PARTY_SIZE;
u32 HP_count = 0;
if (B_MULTI_BATTLE_WHITEOUT < GEN_4 && gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER))
maxI = MULTI_PARTY_SIZE;
// Get total HP for the player's party to determine if the player has lost
for (i = 0; i < maxI; i++)
{
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) && !GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG))
{
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))
{
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, 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 (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)))
{
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;
gActiveBattler = battler;
BtlController_EmitHealthBarUpdate(BUFFER_A, gBattleMoveDamage);
MarkBattlerForControllerExec(battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_return(void)
{
BattleScriptPop();
}
static void Cmd_end(void)
{
CMD_ARGS();
gMoveResultFlags = 0;
gActiveBattler = 0;
gCurrentActionFuncId = B_ACTION_TRY_FINISH;
}
static void Cmd_end2(void)
{
CMD_ARGS();
gActiveBattler = 0;
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;
}
gActiveBattler = battler;
// TODO: Animation
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(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 */) // TODO: Animation
{
BtlController_EmitBattleAnimation(BUFFER_A, animId, &gDisableStructs[battler], *argPtr);
MarkBattlerForControllerExec(battler);
gBattlescriptCurrInstr = nextInstr;
}
else if (gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)
{
gBattlescriptCurrInstr = nextInstr;
}
else
{
BtlController_EmitBattleAnimation(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 = gActiveBattler = 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(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, bool32 excludeCurrent)
{
u32 i;
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (i != gBattlerAttacker
&& !(excludeCurrent && i == gBattlerTarget)
&& 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;
// TODO: Dynamax
// gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 8;
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 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 && gMovesInfo[gCurrentMove].effect != EFFECT_SUCKER_PUNCH && gMovesInfo[gCurrentMove].effect != EFFECT_UPPER_HAND)
{
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;
gActiveBattler = gBattlerAttacker;
BtlController_EmitSetMonData(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
&& gMovesInfo[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 || CanBurnHitThaw(gCurrentMove))
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
{
gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FREEZE;
gActiveBattler = gBattlerTarget;
BtlController_EmitSetMonData(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
&& gMovesInfo[originallyUsedMove].thawsUser
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
{
gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FROSTBITE;
gActiveBattler = gBattlerTarget;
BtlController_EmitSetMonData(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
{
if (gMovesInfo[gCurrentMove].recoil > 0)
{
gBattleMoveDamage = max(1, gBattleScripting.savedDmg * max(1, gMovesInfo[gCurrentMove].recoil) / 100);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil;
effect = TRUE;
}
}
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 ((gMovesInfo[gChosenMove].effect == EFFECT_BATON_PASS
|| gMovesInfo[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 & gMovesInfo[gCurrentMove].argument) && IsBattlerAlive(gBattlerTarget))
{
gBattleMons[gBattlerTarget].status1 &= ~(gMovesInfo[gCurrentMove].argument);
gActiveBattler = gBattlerTarget;
BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gBattlerTarget].status1);
MarkBattlerForControllerExec(gBattlerTarget);
effect = TRUE;
BattleScriptPush(gBattlescriptCurrInstr);
switch (gMovesInfo[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))
{
gActiveBattler = gBattlerAttacker;
BtlController_EmitSpriteInvisibility(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))
{
gActiveBattler = gBattlerAttacker;
BtlController_EmitSpriteInvisibility(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))
{
gActiveBattler = gBattlerTarget;
BtlController_EmitSpriteInvisibility(BUFFER_A, FALSE);
MarkBattlerForControllerExec(gBattlerTarget);
gStatuses3[gBattlerTarget] &= ~STATUS3_SEMI_INVULNERABLE;
gBattleScripting.moveendState++;
return;
}
gBattleScripting.moveendState++;
break;
case MOVEEND_NUM_HITS:
if (gBattlerAttacker != gBattlerTarget
&& gMovesInfo[gCurrentMove].category != DAMAGE_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 (gMovesInfo[gChosenMoveByBattler[gBattlerTarget]].effect == EFFECT_SHELL_TRAP
&& gBattlerTarget != gBattlerAttacker
&& GetBattlerSide(gBattlerTarget) != GetBattlerSide(gBattlerAttacker)
&& gProtectStructs[gBattlerTarget].physicalDmg
&& gProtectStructs[gBattlerTarget].physicalBattlerId == gBattlerAttacker
&& !TestIfSheerForceAffected(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;
// TODO: Dynamax
// if (IsMaxMove(gCurrentMove))
// gBattleStruct->dynamax.lastUsedBaseMove = gBattleStruct->dynamax.baseMove[gBattlerAttacker];
}
}
if (!(gAbsentBattlerFlags & gBitTable[gBattlerAttacker])
&& !(gBattleStruct->absentBattlerFlags & gBitTable[gBattlerAttacker])
&& gMovesInfo[originallyUsedMove].effect != EFFECT_BATON_PASS
&& gMovesInfo[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])
&& !gMovesInfo[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_NEXT_TARGET: // For moves hitting two opposing Pokemon.
{
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, FALSE);
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(GET_MOVE_BATTLESCRIPT(gCurrentMove));
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, FALSE);
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(GET_MOVE_BATTLESCRIPT(gCurrentMove));
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
&& !(gMovesInfo[gCurrentMove].effect == EFFECT_PRESENT && gBattleStruct->presentBasePower == 0)) // Silly edge case
{
gBattleScripting.multihitString[4]++;
if (--gMultiHitCounter == 0)
{
if (gMovesInfo[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(GET_MOVE_BATTLESCRIPT(gCurrentMove));
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;
}
// The order of abilities/items activating after moves hitting multiple targets is
// 1. Magician
// 2. The fastest mon gets switched out using Eject Button / Eject Pack
// 3. White Herb activates
// 4. Red Card activates
// 5. Life Orb / Shell Bell
// 6. Pickpocket
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].preventLifeOrbDamage = TRUE;
effect = TRUE;
}
gBattleScripting.moveendState++;
break;
case MOVEEND_EJECT_ITEMS:
{
// Because sorting the battlers by speed takes lots of cycles, it's better to just check if any of the battlers has the Eject items.
u32 ejectPackBattlers = 0, ejectButtonBattlers = 0, i;
for (i = 0; i < gBattlersCount; i++)
{
u32 holdEffect;
if (i == gBattlerAttacker)
continue;
holdEffect = GetBattlerHoldEffect(i, TRUE);
if (holdEffect == HOLD_EFFECT_EJECT_BUTTON)
ejectButtonBattlers |= gBitTable[i];
else if (holdEffect == HOLD_EFFECT_EJECT_PACK)
ejectPackBattlers |= gBitTable[i];
}
if (ejectButtonBattlers || ejectPackBattlers)
{
u8 battlers[4] = {0, 1, 2, 3};
SortBattlersBySpeed(battlers, FALSE);
for (i = 0; i < gBattlersCount; i++)
{
u32 battler = battlers[i];
if (ejectButtonBattlers & gBitTable[battler])
{
if (TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) // Apparently Sheer Force blocks Eject Button, but not Eject Pack
continue;
// 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
if (!BATTLER_TURN_DAMAGED(battler))
continue;
}
else if (ejectPackBattlers & gBitTable[battler])
{
if (!gProtectStructs[battler].statFell || gProtectStructs[battler].disableEjectPack)
continue;
}
else
{
continue;
}
if (IsBattlerAlive(battler)
&& CountUsablePartyMons(battler) > 0 // Has mon to switch into
// Does not activate if attacker used Parting Shot and can switch out
&& !(gMovesInfo[gCurrentMove].effect == EFFECT_HIT_SWITCH_TARGET && CanBattlerSwitch(gBattlerAttacker))
)
{
gBattleScripting.battler = battler;
gLastUsedItem = gBattleMons[battler].item;
if (gMovesInfo[gCurrentMove].effect == EFFECT_HIT_ESCAPE)
gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection
effect = TRUE;
BattleScriptPushCursor();
if (ejectButtonBattlers & gBitTable[battler])
{
gBattlescriptCurrInstr = BattleScript_EjectButtonActivates;
}
else // Eject Pack
{
gBattlescriptCurrInstr = BattleScript_EjectPackActivates;
// Are these 2 lines below needed?
gProtectStructs[battler].statFell = FALSE;
gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE;
}
break; // Only the fastest Eject item activates
}
}
}
}
gBattleScripting.moveendState++;
break;
case MOVEEND_WHITE_HERB:
for (i = 0; i < gBattlersCount; i++)
{
if (IsBattlerAlive(i)
&& ItemBattleEffects(ITEMEFFECT_STATS_CHANGED, i, FALSE))
{
effect = TRUE;
break;
}
}
if (!effect)
gBattleScripting.moveendState++;
break;
case MOVEEND_RED_CARD:
{
u32 redCardBattlers = 0, i;
for (i = 0; i < gBattlersCount; i++)
{
if (i == gBattlerAttacker)
continue;
if (GetBattlerHoldEffect(i, TRUE) == HOLD_EFFECT_RED_CARD)
redCardBattlers |= gBitTable[i];
}
if (redCardBattlers
&& (gMovesInfo[gCurrentMove].effect != EFFECT_HIT_SWITCH_TARGET || gBattleStruct->hitSwitchTargetFailed)
&& IsBattlerAlive(gBattlerAttacker)
&& !TestIfSheerForceAffected(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++)
{
u32 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 (redCardBattlers & gBitTable[battler]
&& IsBattlerAlive(battler)
&& !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove)
&& BATTLER_TURN_DAMAGED(battler)
&& CanBattlerSwitch(gBattlerAttacker))
{
gLastUsedItem = gBattleMons[battler].item;
gBattleStruct->savedBattlerTarget = gBattleScripting.battler = battler; // Battler with red card
gEffectBattler = gBattlerAttacker;
if (gMovesInfo[gCurrentMove].effect == EFFECT_HIT_ESCAPE)
gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_RedCardActivates;
gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE;
effect = TRUE;
break; // Only fastest red card 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
&& !(TestIfSheerForceAffected(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 (gMovesInfo[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_SET_EVOLUTION_TRACKER:
// If the Pokémon needs to keep track of move usage for its evolutions, do it
if (originallyUsedMove != MOVE_NONE)
TryUpdateEvolutionTracker(EVO_LEVEL_MOVE_TWENTY_TIMES, 1);
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
&& MoveHasAdditionalEffectSelf(gCurrentMove, MOVE_EFFECT_THRASH) // 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].preventLifeOrbDamage = 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;
gBattleStruct->additionalEffectsCounter = 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();
gActiveBattler = gBattlerAttacker;
if (!(gHitMarker & HITMARKER_FAINTED(gBattlerAttacker)))
{
BtlController_EmitReturnMonToBall(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;
gActiveBattler = battler;
gBattlerPartyIndexes[battler] = gBattleStruct->monToSwitchIntoId[battler];
BtlController_EmitGetMonData(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 = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
oldData = gBattleMons[battler];
monData = (u8 *)(&gBattleMons[battler]);
for (i = 0; i < sizeof(struct BattlePokemon); i++)
monData[i] = gBattleBufferB[battler][4 + i];
// Edge case: the sent out pokemon has 0 HP. This should never happen.
if (gBattleMons[battler].hp == 0)
{
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(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 (gMovesInfo[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);
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 = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
if (GetBattlerSide(battler) == B_SIDE_OPPONENT
&& !(gBattleTypeFlags & (BATTLE_TYPE_LINK
| BATTLE_TYPE_LEGENDARY
| BATTLE_TYPE_OLD_MAN_TUTORIAL
| BATTLE_TYPE_POKEDUDE
| BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_GHOST)))
HandleSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[battler].species), FLAG_SET_SEEN, gBattleMons[battler].personality);
gAbsentBattlerFlags &= ~(gBitTable[battler]);
BtlController_EmitSwitchInAnim(BUFFER_A, gBattlerPartyIndexes[battler], cmd->dontClearSubstitute);
MarkBattlerForControllerExec(battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
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 = gActiveBattler = 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]);
gActiveBattler = battler;
BtlController_EmitChoosePokemon(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++)
{
gActiveBattler = battler;
if (gHitMarker & HITMARKER_FAINTED(battler))
{
if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE))
{
gAbsentBattlerFlags |= gBitTable[battler];
gHitMarker &= ~HITMARKER_FAINTED(battler);
BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY);
MarkBattlerForControllerExec(battler);
}
else if (!gSpecialStatuses[battler].faintedHasReplacement)
{
ChooseMonToSendOut(battler, PARTY_SIZE);
gSpecialStatuses[battler].faintedHasReplacement = TRUE;
}
}
else
{
BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY);
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 = gActiveBattler = 0;
if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE))
{
gAbsentBattlerFlags |= gBitTable[battler];
gHitMarker &= ~HITMARKER_FAINTED(battler);
BtlController_EmitCantSwitch(BUFFER_A);
MarkBattlerForControllerExec(battler);
}
else if (!gSpecialStatuses[battler].faintedHasReplacement)
{
ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[2]);
gSpecialStatuses[battler].faintedHasReplacement = TRUE;
}
else
{
BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY);
MarkBattlerForControllerExec(battler);
flags |= 1;
}
}
if (gBitTable[2] & hitmarkerFaintBits && !(gBitTable[0] & hitmarkerFaintBits))
{
battler = gActiveBattler = 2;
if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE))
{
gAbsentBattlerFlags |= gBitTable[battler];
gHitMarker &= ~HITMARKER_FAINTED(battler);
BtlController_EmitCantSwitch(BUFFER_A);
MarkBattlerForControllerExec(battler);
}
else if (!gSpecialStatuses[battler].faintedHasReplacement)
{
ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[0]);
gSpecialStatuses[battler].faintedHasReplacement = TRUE;
}
else if (!(flags & 1))
{
BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY);
MarkBattlerForControllerExec(battler);
}
}
if (gBitTable[1] & hitmarkerFaintBits)
{
battler = gActiveBattler = 1;
if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE))
{
gAbsentBattlerFlags |= gBitTable[battler];
gHitMarker &= ~HITMARKER_FAINTED(battler);
BtlController_EmitCantSwitch(BUFFER_A);
MarkBattlerForControllerExec(battler);
}
else if (!gSpecialStatuses[battler].faintedHasReplacement)
{
ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[3]);
gSpecialStatuses[battler].faintedHasReplacement = TRUE;
}
else
{
BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY);
MarkBattlerForControllerExec(battler);
flags |= 2;
}
}
if (gBitTable[3] & hitmarkerFaintBits && !(gBitTable[1] & hitmarkerFaintBits))
{
battler = gActiveBattler = 3;
if (HasNoMonsToSwitch(battler, PARTY_SIZE, PARTY_SIZE))
{
gAbsentBattlerFlags |= gBitTable[battler];
gHitMarker &= ~HITMARKER_FAINTED(battler);
BtlController_EmitCantSwitch(BUFFER_A);
MarkBattlerForControllerExec(battler);
}
else if (!gSpecialStatuses[battler].faintedHasReplacement)
{
ChooseMonToSendOut(battler, gBattleStruct->monToSwitchIntoId[1]);
gSpecialStatuses[battler].faintedHasReplacement = TRUE;
}
else if (!(flags & 2))
{
BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY);
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 = gActiveBattler = 2;
else
battler = gActiveBattler = 0;
BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY);
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 = gActiveBattler = 3;
else
battler = gActiveBattler = 1;
BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY);
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 = gActiveBattler = 2;
if (HasNoMonsToSwitch(battler, gBattleBufferB[0][1], PARTY_SIZE))
{
gAbsentBattlerFlags |= gBitTable[battler];
gHitMarker &= ~HITMARKER_FAINTED(battler);
BtlController_EmitCantSwitch(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 = gActiveBattler = 3;
if (HasNoMonsToSwitch(battler, gBattleBufferB[1][1], PARTY_SIZE))
{
gAbsentBattlerFlags |= gBitTable[battler];
gHitMarker &= ~HITMARKER_FAINTED(battler);
BtlController_EmitCantSwitch(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))
{
gActiveBattler = battler;
gAbsentBattlerFlags |= gBitTable[battler];
gHitMarker &= ~HITMARKER_FAINTED(battler);
gBattlescriptCurrInstr = failInstr;
}
else
{
gActiveBattler = battler;
*(gBattleStruct->battlerPartyIndexes + battler) = gBattlerPartyIndexes[battler];
*(gBattleStruct->monToSwitchIntoId + battler) = PARTY_SIZE;
gBattleStruct->field_93 &= ~(gBitTable[battler]);
BtlController_EmitChoosePokemon(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++)
{
gActiveBattler = i;
if (i != battler)
{
BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY);
MarkBattlerForControllerExec(i);
}
}
}
else
{
u32 battlerOpposite = gActiveBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(battler)));
if (gAbsentBattlerFlags & gBitTable[battlerOpposite])
battlerOpposite = gActiveBattler ^= BIT_FLANK;
BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY);
MarkBattlerForControllerExec(battlerOpposite);
}
}
}
}
static void Cmd_switchhandleorder(void)
{
CMD_ARGS(u8 battler, u8 state);
u32 battler, i;
if (gBattleControllerExecFlags)
return;
battler = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
switch (cmd->state)
{
case 0:
for (i = 0; i < gBattlersCount; i++)
{
if (gBattleBufferB[i][0] == CONTROLLER_CHOSENMONRETURNVALUE)
{
*(gBattleStruct->monToSwitchIntoId + i) = gBattleBufferB[i][1];
if (!(gBattleStruct->field_93 & gBitTable[i]))
{
gBattleStruct->field_93 |= gBitTable[i];
}
}
}
break;
case 1:
if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI))
SwitchPartyOrder(battler);
break;
case 2:
if (!(gBattleStruct->field_93 & gBitTable[battler]))
{
gBattleStruct->field_93 |= gBitTable[battler];
}
// fall through
case 3:
gBattleCommunication[0] = gBattleBufferB[battler][1];
*(gBattleStruct->monToSwitchIntoId + battler) = gBattleBufferB[battler][1];
if (gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleTypeFlags & BATTLE_TYPE_MULTI)
{
*(battler * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) &= 0xF;
*(battler * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) |= (gBattleBufferB[battler][2] & 0xF0);
*(battler * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 1) = gBattleBufferB[battler][3];
*((BATTLE_PARTNER(battler)) * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) &= (0xF0);
*((BATTLE_PARTNER(battler)) * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) |= (gBattleBufferB[battler][2] & 0xF0) >> 4;
*((BATTLE_PARTNER(battler)) * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 2) = gBattleBufferB[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, gBattleBufferB[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 DoSwitchInAbilities(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))
|| AbilityBattleEffects(ABILITYEFFECT_TRACE2, 0, 0, 0, 0));
}
static void Cmd_switchineffects(void)
{
CMD_ARGS(u8 battler);
s32 i;
u32 battler = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
UpdateSentPokesToOpponentValue(battler); // TODO: compare with pokeemerald
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 = gBattleMons[battler].maxHP / (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(gMovesInfo[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;
gActiveBattler = battler;
BtlController_EmitSetMonData(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(gMovesInfo[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
{
u32 battlerAbility = GetBattlerAbility(battler);
// 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 (battlerAbility == 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 ((battlerAbility == ABILITY_INTIMIDATE || battlerAbility == ABILITY_SUPERSWEET_SYRUP || battlerAbility == ABILITY_DOWNLOAD)
&& !IsBattlerAlive(BATTLE_OPPOSITE(battler))
&& !IsBattlerAlive(BATTLE_PARTNER(BATTLE_OPPOSITE(battler))))
{
if (ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battler, FALSE))
return;
}
else
{
if (DoSwitchInAbilities(battler) || ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battler, FALSE))
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 = gActiveBattler = GetBattlerAtPosition(cmd->battler);
BtlController_EmitTrainerSlide(BUFFER_A);
MarkBattlerForControllerExec(battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_playse(void)
{
CMD_ARGS(u16 song);
gActiveBattler = gBattlerAttacker;
BtlController_EmitPlaySE(BUFFER_A, cmd->song);
MarkBattlerForControllerExec(gBattlerAttacker);
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_fanfare(void)
{
CMD_ARGS(u16 song);
gActiveBattler = gBattlerAttacker;
BtlController_EmitPlayFanfareOrBGM(BUFFER_A, cmd->song, FALSE);
MarkBattlerForControllerExec(gBattlerAttacker);
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_playfaintcry(void)
{
CMD_ARGS(u8 battler);
u32 battler = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
BtlController_EmitFaintingCry(BUFFER_A);
MarkBattlerForControllerExec(battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_endlinkbattle(void)
{
CMD_ARGS();
u32 battler = gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
BtlController_EmitEndLinkBattle(BUFFER_A, gBattleOutcome);
MarkBattlerForControllerExec(battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_returntoball(void)
{
CMD_ARGS(u8 battler, bool8 changingForm);
u32 battler = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
BtlController_EmitReturnMonToBall(BUFFER_A, TRUE);
MarkBattlerForControllerExec(battler);
// Don't always execute a form change here otherwise we can stomp gigantamax
if(!cmd->changingForm)
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)
{
gBattlescriptCurrInstr = cmd->nothingToLearnPtr;
}
else if (learnMove == MON_HAS_MAX_MOVES)
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
u32 battler = gActiveBattler = 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);
gActiveBattler = 0;
switch (gBattleScripting.learnMoveState)
{
case 0:
HandleBattleWindow(YESNOBOX_X_Y, 0);
BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO);
gBattleScripting.learnMoveState++;
gBattleCommunication[CURSOR_POSITION] = 0;
BattleCreateYesNoCursorAt();
break;
case 1:
if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0)
{
PlaySE(SE_SELECT);
BattleDestroyYesNoCursorAt();
gBattleCommunication[CURSOR_POSITION] = 0;
BattleCreateYesNoCursorAt();
}
if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0)
{
PlaySE(SE_SELECT);
BattleDestroyYesNoCursorAt();
gBattleCommunication[CURSOR_POSITION] = 1;
BattleCreateYesNoCursorAt();
}
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();
break;
case 1:
if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0)
{
PlaySE(SE_SELECT);
BattleDestroyYesNoCursorAt();
gBattleCommunication[CURSOR_POSITION] = 0;
BattleCreateYesNoCursorAt();
}
if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0)
{
PlaySE(SE_SELECT);
BattleDestroyYesNoCursorAt();
gBattleCommunication[CURSOR_POSITION] = 1;
BattleCreateYesNoCursorAt();
}
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 = gActiveBattler = 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(BUFFER_A);
MarkBattlerForControllerExec(battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
static u32 GetTrainerMoneyToGive(u16 trainerId)
{
u32 lastMonLevel = 0;
u32 moneyReward;
u8 trainerMoney = 0;
if (trainerId == TRAINER_SECRET_BASE)
{
moneyReward = 20 * gBattleResources->secretBase->party.levels[0] * gBattleStruct->moneyMultiplier;
}
else
{
// TODO: Update Trainer struct
// const struct TrainerMon *party = GetTrainerPartyFromId(trainerId);
// if (party == NULL)
// return 20;
// lastMonLevel = party[GetTrainerPartySizeFromId(trainerId) - 1].lvl;
// trainerMoney = gTrainerClasses[GetTrainerClassFromId(trainerId)].money;
lastMonLevel = 10;
trainerMoney = 100;
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 = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
switch (gBattleCommunication[0])
{
case 0:
BtlController_EmitGetMonData(BUFFER_A, REQUEST_ALL_BATTLE, 0);
MarkBattlerForControllerExec(battler);
gBattleCommunication[0]++;
break;
case 1:
if (gBattleControllerExecFlags == 0)
{
s32 i;
struct BattlePokemon *bufferPoke = (struct BattlePokemon *) &gBattleBufferB[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);
SWAP(gBattlerAttacker, gBattlerTarget, gActiveBattler);
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); // TODO: compare IncrementGameStat to pokeemerald
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 = gActiveBattler = 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(BUFFER_A, hpStatuses, 1);
MarkBattlerForControllerExec(battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_hidepartystatussummary(void)
{
CMD_ARGS(u8 battler);
u32 battler = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
BtlController_EmitHidePartyStatusSummary(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 = GET_MOVE_BATTLESCRIPT(gCurrentMove);
}
static void Cmd_statusanimation(void)
{
CMD_ARGS(u8 battler);
if (gBattleControllerExecFlags == 0)
{
u32 battler = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
if (!(gStatuses3[battler] & STATUS3_SEMI_INVULNERABLE)
&& gDisableStructs[battler].substituteHP == 0
&& !(gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION)))
{
BtlController_EmitStatusAnimation(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 = gActiveBattler = 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(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 = gActiveBattler = 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(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();
break;
case 1:
if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0)
{
PlaySE(SE_SELECT);
BattleDestroyYesNoCursorAt();
gBattleCommunication[CURSOR_POSITION] = 0;
BattleCreateYesNoCursorAt();
}
if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0)
{
PlaySE(SE_SELECT);
BattleDestroyYesNoCursorAt();
gBattleCommunication[CURSOR_POSITION] = 1;
BattleCreateYesNoCursorAt();
}
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;
}
// The same as adjustdamage, except there's no random damage multiplier.
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_BERRY_POUCH
&& GetBattlerAbility(battler) == ABILITY_CHEEK_POUCH
&& !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)
&& gBattleStruct->ateBerry[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]]
&& !BATTLER_MAX_HP(battler))
{
// TODO: Dynamax
// gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 3;
gBattleMoveDamage = gBattleMons[battler].maxHP / 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;
gActiveBattler = battlerAtk;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[battlerAtk].item), &gBattleMons[battlerAtk].item);
MarkBattlerForControllerExec(battlerAtk);
CheckSetUnburden(battlerAtk);
gBattleMons[battlerDef].item = gLastUsedItem;
gActiveBattler = battlerDef;
BtlController_EmitSetMonData(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 = gActiveBattler = 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
&& gMovesInfo[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(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)
{
// 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)
{
// 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
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 = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
BtlController_EmitSpriteInvisibility(BUFFER_A, FALSE);
MarkBattlerForControllerExec(battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_recordability(void)
{
CMD_ARGS(u8 battler);
u8 battler = gActiveBattler = 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 = gActiveBattler = 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 = gActiveBattler = 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)
&& gMovesInfo[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
|| (!IS_BATTLER_OF_TYPE(battlerTarget, TYPE_STEEL) && !IS_BATTLER_OF_TYPE(battlerTarget, TYPE_POISON));
}
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;
}
static bool32 TryTidyUpClear(u32 battlerAtk, bool32 clear)
{
s32 i;
u8 saveBattler = gBattlerAttacker;
for (i = 0; i < NUM_BATTLE_SIDES; i++)
{
struct SideTimer *sideTimer = &gSideTimers[i];
u32 *sideStatuses = &gSideStatuses[i];
gBattlerAttacker = i; // For correct battle string. Ally's / Foe's
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);
}
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (gBattleMons[i].status2 & STATUS2_SUBSTITUTE)
{
if (clear)
{
gBattlerTarget = i;
gDisableStructs[i].substituteHP = 0;
gBattleMons[i].status2 &= ~STATUS2_SUBSTITUTE;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_SubstituteFade;
}
gBattlerAttacker = saveBattler;
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 = gMovesInfo[gCurrentMove].type;
else if (!(gBattleStruct->dynamicMoveType & F_DYNAMIC_TYPE_IGNORE_PHYSICALITY))
moveType = gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK;
else
moveType = gMovesInfo[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_BERRY_POUCH)
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);
gActiveBattler = battler;
BtlController_EmitSetMonData(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 = gActiveBattler = 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_HOLD_EFFECT:
{
VARIOUS_ARGS(u8 holdEffect, const u8 *jumpInstr, u8 equal);
if ((GetBattlerHoldEffect(battler, TRUE) == cmd->holdEffect) == cmd->equal)
{
if (cmd->equal)
gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM
gBattlescriptCurrInstr = cmd->jumpInstr;
}
else
{
if (!cmd->equal)
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_BERRY_POUCH)
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
{
// TODO: Dynamax
// gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 16;
gBattleMoveDamage = gBattleMons[battler].maxHP / 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: // unused, from pokeemerald
case VARIOUS_ARENA_JUDGMENT_WINDOW: // unused, from pokeemerald
case VARIOUS_ARENA_OPPONENT_MON_LOST: // unused, from pokeemerald
case VARIOUS_ARENA_PLAYER_MON_LOST: // unused, from pokeemerald
case VARIOUS_ARENA_BOTH_MONS_LOST: // unused, from pokeemerald
case VARIOUS_EMIT_YESNOBOX: // unused, from pokeemerald
case VARIOUS_DRAW_ARENA_REF_TEXT_BOX: // unused, from pokeemerald
case VARIOUS_ERASE_ARENA_REF_TEXT_BOX: // unused, from pokeemerald
case VARIOUS_ARENA_JUDGMENT_STRING: // unused, from pokeemerald
case VARIOUS_ARENA_WAIT_STRING: // unused, from pokeemerald
{
VARIOUS_ARGS();
break;
}
case VARIOUS_WAIT_CRY:
{
VARIOUS_ARGS();
if (!IsCryFinished())
return;
break;
}
case VARIOUS_RETURN_OPPONENT_MON1:
{
VARIOUS_ARGS();
battler = gActiveBattler = 1;
if (gBattleMons[battler].hp != 0)
{
BtlController_EmitReturnMonToBall(BUFFER_A, FALSE);
MarkBattlerForControllerExec(battler);
}
break;
}
case VARIOUS_RETURN_OPPONENT_MON2:
{
VARIOUS_ARGS();
if (gBattlersCount > 3)
{
battler = gActiveBattler = 3;
if (gBattleMons[battler].hp != 0)
{
BtlController_EmitReturnMonToBall(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: // unused from pokeemerald
{
VARIOUS_ARGS();
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(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(BUFFER_A, INSTANT_HP_BAR_DROP);
MarkBattlerForControllerExec(battler);
break;
}
case VARIOUS_CLEAR_STATUS:
{
VARIOUS_ARGS();
gBattleMons[battler].status1 = 0;
BtlController_EmitSetMonData(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(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
&& !gAbilitiesInfo[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 (gMovesInfo[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(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 (gAbilitiesInfo[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 (gAbilitiesInfo[gBattleMons[gBattlerAttacker].ability].cantBeCopied
|| gAbilitiesInfo[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) */) // TODO: Dynamax
{
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_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) || gMovesInfo[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) == gMovesInfo[gCurrentMove].type
&& GetBattlerType(gBattlerTarget, 1) == gMovesInfo[gCurrentMove].type)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
SET_BATTLER_TYPE(gBattlerTarget, gMovesInfo[gCurrentMove].type);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, gMovesInfo[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(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_SET_ARG_TO_BATTLE_DAMAGE:
{
VARIOUS_ARGS();
gBattleMoveDamage = gMovesInfo[gCurrentMove].argument;
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 || MoveHasAdditionalEffectSelf(move, MOVE_EFFECT_RECHARGE)
|| gMovesInfo[move].instructBanned
|| gBattleMoveEffects[gMovesInfo[move].effect].twoTurnEffect
/* || IsDynamaxed(gBattlerTarget) */) // TODO: Dynamax
{
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();
// TODO: Ability popup
// CreateAbilityPopUp(battler, gBattleMons[battler].ability, (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) != 0);
break;
}
case VARIOUS_UPDATE_ABILITY_POPUP:
{
VARIOUS_ARGS();
// TODO: Ability popup
// 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);
RemoveBattlerType(battler, cmd->type);
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 = gActiveBattler = gBattlerTarget;
BtlController_EmitSetMonData(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(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_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: // unused for now
{
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(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: // unused for now
{
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: // unused for now
{
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, gMovesInfo[gCurrentMove].argument))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
gBattleMons[battler].type3 = gMovesInfo[gCurrentMove].argument;
PREPARE_TYPE_BUFFER(gBattleTextBuff1, gMovesInfo[gCurrentMove].argument);
gBattlescriptCurrInstr = cmd->nextInstr;
}
return;
}
case VARIOUS_DESTROY_ABILITY_POPUP:
{
VARIOUS_ARGS();
// TODO: Ability popup
// 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(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(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);
// TODO: Dynamax
// gBattleMoveDamage = GetNonDynamaxMaxHP(battler) / 4;
gBattleMoveDamage = gBattleMons[battler].maxHP / 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;
}
// TODO: Convert this to a proper FORM_CHANGE type.
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;
gBattleStruct->ateBerry[battler & BIT_SIDE] |= gBitTable[gBattlerPartyIndexes[battler]];
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) * gMovesInfo[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) * gMovesInfo[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;
// TODO: Dynamax
// u16 hpFraction = max(1, GetNonDynamaxMaxHP(gBattlerAttacker) / 3);
u16 hpFraction = max(1, gBattleMons[gBattlerAttacker].maxHP / 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_BERRY_POUCH)
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_BERRY_POUCH && !(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.
gActiveBattler = gBattlerAttacker;
if (gSelectedMonPartyId != PARTY_SIZE)
{
struct Pokemon *party = GetSideParty(side);
u16 hp = GetMonData(&party[gSelectedMonPartyId], MON_DATA_MAX_HP) / 2;
BtlController_EmitSetMonData(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(BUFFER_A, PARTY_ACTION_CHOOSE_FAINTED_MON, PARTY_SIZE, ABILITY_NONE, gBattleStruct->battlerPartyOrders[gBattlerAttacker]);
MarkBattlerForControllerExec(gBattlerAttacker);
}
return;
}
case VARIOUS_GET_BATTLERS_FOR_RECALL:
i = 0; // redundant
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
gActiveBattler = 1;
for (i = 0; gActiveBattler < MAX_BATTLERS_COUNT; gActiveBattler += 2)
{
if (gActiveBattler < gBattlersCount && gBattleMons[gActiveBattler].hp != 0)
gBattleCommunication[MULTISTRING_CHOOSER] |= gBitTable[i];
i++;
}
break;
case VARIOUS_CHECK_POKEFLUTE:
{
u32 status, monToCheck = 0;
u16 species = SPECIES_NONE;
u8 abilityNum;
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
for (i = 0; i < gBattlersCount; i++)
{
if (gBattleMons[i].ability != ABILITY_SOUNDPROOF)
{
gBattleMons[i].status1 &= ~STATUS1_SLEEP;
gBattleMons[i].status2 &= ~STATUS2_NIGHTMARE;
}
}
for (i = 0; i < PARTY_SIZE; i++)
{
species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG);
abilityNum = GetMonData(&gPlayerParty[i], MON_DATA_ABILITY_NUM);
status = GetMonData(&gPlayerParty[i], MON_DATA_STATUS);
if (species != SPECIES_NONE
&& species != SPECIES_EGG
&& status & AILMENT_FNT
&& GetAbilityBySpecies(species, abilityNum) != ABILITY_SOUNDPROOF)
monToCheck |= (1 << i);
}
if (monToCheck)
{
gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
status = 0;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, monToCheck, 4, &status);
MarkBattlerForControllerExec(gActiveBattler);
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
}
monToCheck = 0;
for (i = 0; i < PARTY_SIZE; i++)
{
species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES_OR_EGG);
abilityNum = GetMonData(&gEnemyParty[i], MON_DATA_ABILITY_NUM);
status = GetMonData(&gEnemyParty[i], MON_DATA_STATUS);
if (species != SPECIES_NONE
&& species != SPECIES_EGG
&& status & AILMENT_FNT
&& GetAbilityBySpecies(species, abilityNum) != ABILITY_SOUNDPROOF)
monToCheck |= (1 << i);
}
if (monToCheck)
{
gActiveBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
status = 0;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, monToCheck, 4, &status);
MarkBattlerForControllerExec(gActiveBattler);
gBattleCommunication[5] = 1;
}
break;
}
case VARIOUS_WAIT_FANFARE:
if (!IsFanfareTaskInactive())
return;
break;
} // End of switch (cmd->id)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void TryResetProtectUseCounter(u32 battler)
{
u32 lastMove = gLastResultingMoves[battler];
if (lastMove == MOVE_UNAVAILABLE
|| (!gBattleMoveEffects[gMovesInfo[lastMove].effect].usesProtectCounter
&& (B_ALLY_SWITCH_FAIL_CHANCE >= GEN_9 && gMovesInfo[lastMove].effect != EFFECT_ALLY_SWITCH)))
gDisableStructs[battler].protectUses = 0;
}
// Protect and Endure
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 (!gMovesInfo[gCurrentMove].argument) // Protects one mon only.
{
if (gMovesInfo[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;
}
gActiveBattler = gBattlerAttacker;
gBattleMoveDamage = gBattleMons[gBattlerAttacker].hp;
BtlController_EmitHealthBarUpdate(BUFFER_A, INSTANT_HP_BAR_DROP);
MarkBattlerForControllerExec(gBattlerAttacker);
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_setatkhptozero(void)
{
CMD_ARGS();
if (gBattleControllerExecFlags)
return;
gActiveBattler = gBattlerAttacker;
gBattleMons[gBattlerAttacker].hp = 0;
BtlController_EmitSetMonData(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;
// TODO: Dynamax
// gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 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;
// Edge case, we used Z Mirror Move, got the stat boost and now need to use the Z-move
if (FALSE /* gBattleStruct->zmove.toBeUsed[gBattlerAttacker] && !IS_MOVE_STATUS(move) */) // TODO: Z-Moves
{
// TODO: Z-Moves
// gCurrentMove = gBattleStruct->zmove.chosenZMove = GetTypeBasedZMove(move, gBattlerAttacker);
// QueueZMove(gBattlerAttacker, move);
}
else
{
gCurrentMove = move;
}
SetAtkCancellerForCalledMove();
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
gBattlescriptCurrInstr = GET_MOVE_BATTLESCRIPT(gCurrentMove);
}
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)
{
// TODO: Dynamax
// gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 2;
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 2;
}
else if (B_RECOIL_IF_MISS_DMG == GEN_4)
{
if ((gBattleMons[gBattlerTarget].maxHP / 2) < gBattleMoveDamage)
{
// TODO: Dynamax
// gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 2;
}
}
else
{
gBattleMoveDamage /= 2;
}
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
break;
case DMG_DOUBLED:
gBattleMoveDamage *= 2;
break;
case DMG_1_8_TARGET_HP:
// TODO: Dynamax
// gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerTarget) / 8;
gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 8;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
break;
case DMG_FULL_ATTACKER_HP:
// TODO: Dynamax
// gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker);
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP;
break;
case DMG_CURR_ATTACKER_HP:
// TODO: Dynamax
// gBattleMoveDamage = GetNonDynamaxHP(gBattlerAttacker);
gBattleMoveDamage = gBattleMons[gBattlerAttacker].hp;
break;
case DMG_BIG_ROOT:
gBattleMoveDamage = GetDrainedBigRootHp(gBattlerAttacker, gBattleMoveDamage);
break;
case DMG_1_2_ATTACKER_HP:
// TODO: Dynamax
// gBattleMoveDamage = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP
gBattleMoveDamage = (gBattleMons[gBattlerAttacker].maxHP + 1) / 2; // Half of Max HP Rounded UP
break;
case DMG_RECOIL_FROM_IMMUNE:
// TODO: Dynamax
// gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerTarget) / 2;
gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 2;
break;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_trysetrest(void)
{
CMD_ARGS(const u8 *failInstr);
const u8 *failInstr = cmd->failInstr;
gActiveBattler = 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(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_nop(void)
{
gBattlescriptCurrInstr++;
}
bool8 UproarWakeUpCheck(u8 battlerId)
{
s32 i;
for (i = 0; i < gBattlersCount; i++)
{
if (!(gBattleMons[i].status2 & STATUS2_UPROAR) || gBattleMons[battlerId].ability == 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_jumpifcantmakeasleep(void)
{
const u8 *jumpPtr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
if (UproarWakeUpCheck(gBattlerTarget))
{
gBattlescriptCurrInstr = jumpPtr;
}
else if (gBattleMons[gBattlerTarget].ability == ABILITY_INSOMNIA
|| gBattleMons[gBattlerTarget].ability == ABILITY_VITAL_SPIRIT)
{
gLastUsedAbility = gBattleMons[gBattlerTarget].ability;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STAYED_AWAKE_USING;
gBattlescriptCurrInstr = jumpPtr;
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
}
else
{
gBattlescriptCurrInstr += 5;
}
}
static void Cmd_stockpile(void)
{
if (gDisableStructs[gBattlerAttacker].stockpileCounter == 3)
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CANT_STOCKPILE;
}
else
{
gDisableStructs[gBattlerAttacker].stockpileCounter++;
PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[gBattlerAttacker].stockpileCounter)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STOCKPILED;
}
gBattlescriptCurrInstr++;
}
static void Cmd_stockpiletobasedamage(void)
{
const u8 *jumpPtr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0)
{
gBattlescriptCurrInstr = jumpPtr;
}
else
{
if (gBattleCommunication[MISS_TYPE] != B_MSG_PROTECTED)
{
gBattleMoveDamage = CalculateBaseDamageOld(&gBattleMons[gBattlerAttacker], &gBattleMons[gBattlerTarget], gCurrentMove,
gSideStatuses[GET_BATTLER_SIDE(gBattlerTarget)], 0,
0, gBattlerAttacker, gBattlerTarget)
* gDisableStructs[gBattlerAttacker].stockpileCounter;
gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter;
if (gProtectStructs[gBattlerAttacker].helpingHand)
gBattleMoveDamage = gBattleMoveDamage * 15 / 10;
}
gDisableStructs[gBattlerAttacker].stockpileCounter = 0;
gBattlescriptCurrInstr += 5;
}
}
static void Cmd_stockpiletohpheal(void)
{
const u8 *jumpPtr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0)
{
gBattlescriptCurrInstr = jumpPtr;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWALLOW_FAILED;
}
else if (gBattleMons[gBattlerAttacker].maxHP == gBattleMons[gBattlerAttacker].hp)
{
gDisableStructs[gBattlerAttacker].stockpileCounter = 0;
gBattlescriptCurrInstr = jumpPtr;
gBattlerTarget = gBattlerAttacker;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWALLOW_FULL_HP;
}
else
{
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / (1 << (3 - gDisableStructs[gBattlerAttacker].stockpileCounter));
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattleMoveDamage *= -1;
gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter;
gDisableStructs[gBattlerAttacker].stockpileCounter = 0;
gBattlescriptCurrInstr += 5;
gBattlerTarget = gBattlerAttacker;
}
}
static void Cmd_negativedamage(void)
{
gBattleMoveDamage = -(gHpDealt / 2);
if (gBattleMoveDamage == 0)
gBattleMoveDamage = -1;
gBattlescriptCurrInstr++;
}
static u8 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr)
{
bool32 certain = FALSE;
bool32 notProtectAffected = FALSE;
u32 index;
if (flags & MOVE_EFFECT_AFFECTS_USER)
gActiveBattler = gBattlerAttacker;
else
gActiveBattler = gBattlerTarget;
flags &= ~MOVE_EFFECT_AFFECTS_USER;
if (flags & MOVE_EFFECT_CERTAIN)
certain++;
flags &= ~MOVE_EFFECT_CERTAIN;
if (flags & STAT_CHANGE_NOT_PROTECT_AFFECTED)
notProtectAffected++;
flags &= ~STAT_CHANGE_NOT_PROTECT_AFFECTED;
PREPARE_STAT_BUFFER(gBattleTextBuff1, statId)
if (statValue <= -1) // Stat decrease.
{
if (gSideTimers[GET_BATTLER_SIDE(gActiveBattler)].mistTimer
&& !certain && gCurrentMove != MOVE_CURSE)
{
if (flags == STAT_CHANGE_ALLOW_PTR)
{
if (gSpecialStatuses[gActiveBattler].statLowered)
{
gBattlescriptCurrInstr = BS_ptr;
}
else
{
BattleScriptPush(BS_ptr);
gBattleScripting.battler = gActiveBattler;
gBattlescriptCurrInstr = BattleScript_MistProtected;
gSpecialStatuses[gActiveBattler].statLowered = 1;
}
}
return STAT_CHANGE_DIDNT_WORK;
}
else if (gCurrentMove != MOVE_CURSE
&& notProtectAffected != TRUE && JumpIfMoveAffectedByProtect(0))
{
gBattlescriptCurrInstr = BattleScript_ButItFailed;
return STAT_CHANGE_DIDNT_WORK;
}
else if ((gBattleMons[gActiveBattler].ability == ABILITY_CLEAR_BODY
|| gBattleMons[gActiveBattler].ability == ABILITY_WHITE_SMOKE)
&& !certain && gCurrentMove != MOVE_CURSE)
{
if (flags == STAT_CHANGE_ALLOW_PTR)
{
if (gSpecialStatuses[gActiveBattler].statLowered)
{
gBattlescriptCurrInstr = BS_ptr;
}
else
{
BattleScriptPush(BS_ptr);
gBattleScripting.battler = gActiveBattler;
gBattlescriptCurrInstr = BattleScript_AbilityNoStatLoss;
gLastUsedAbility = gBattleMons[gActiveBattler].ability;
RecordAbilityBattle(gActiveBattler, gLastUsedAbility);
gSpecialStatuses[gActiveBattler].statLowered = 1;
}
}
return STAT_CHANGE_DIDNT_WORK;
}
else if (gBattleMons[gActiveBattler].ability == ABILITY_KEEN_EYE
&& !certain && statId == STAT_ACC)
{
if (flags == STAT_CHANGE_ALLOW_PTR)
{
BattleScriptPush(BS_ptr);
gBattleScripting.battler = gActiveBattler;
gBattlescriptCurrInstr = BattleScript_AbilityNoSpecificStatLoss;
gLastUsedAbility = gBattleMons[gActiveBattler].ability;
RecordAbilityBattle(gActiveBattler, gLastUsedAbility);
}
return STAT_CHANGE_DIDNT_WORK;
}
else if (gBattleMons[gActiveBattler].ability == ABILITY_HYPER_CUTTER
&& !certain && statId == STAT_ATK)
{
if (flags == STAT_CHANGE_ALLOW_PTR)
{
BattleScriptPush(BS_ptr);
gBattleScripting.battler = gActiveBattler;
gBattlescriptCurrInstr = BattleScript_AbilityNoSpecificStatLoss;
gLastUsedAbility = gBattleMons[gActiveBattler].ability;
RecordAbilityBattle(gActiveBattler, gLastUsedAbility);
}
return STAT_CHANGE_DIDNT_WORK;
}
else if (gBattleMons[gActiveBattler].ability == ABILITY_SHIELD_DUST && flags == 0)
{
return STAT_CHANGE_DIDNT_WORK;
}
else // try to decrease
{
statValue = -GET_STAT_BUFF_VALUE(statValue);
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;
}
gBattleTextBuff2[index++] = B_BUFF_STRING;
gBattleTextBuff2[index++] = STRINGID_STATFELL;
gBattleTextBuff2[index++] = STRINGID_STATFELL >> 8;
gBattleTextBuff2[index] = B_BUFF_EOS;
if (gBattleMons[gActiveBattler].statStages[statId] == MIN_STAT_STAGE)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STAT_WONT_DECREASE;
else
gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler); // B_MSG_ATTACKER_STAT_FELL or B_MSG_DEFENDER_STAT_FELL
}
}
else // stat increase
{
statValue = GET_STAT_BUFF_VALUE(statValue);
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;
}
gBattleTextBuff2[index++] = B_BUFF_STRING;
gBattleTextBuff2[index++] = STRINGID_STATROSE;
gBattleTextBuff2[index++] = STRINGID_STATROSE >> 8;
gBattleTextBuff2[index] = B_BUFF_EOS;
if (gBattleMons[gActiveBattler].statStages[statId] == MAX_STAT_STAGE)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STAT_WONT_INCREASE;
else
gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler); // B_MSG_ATTACKER_STAT_ROSE or B_MSG_DEFENDER_STAT_ROSE
}
gBattleMons[gActiveBattler].statStages[statId] += statValue;
if (gBattleMons[gActiveBattler].statStages[statId] < MIN_STAT_STAGE)
gBattleMons[gActiveBattler].statStages[statId] = MIN_STAT_STAGE;
if (gBattleMons[gActiveBattler].statStages[statId] > MAX_STAT_STAGE)
gBattleMons[gActiveBattler].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(gBattleScripting.statChanger & 0xF0, GET_STAT_BUFF_ID(gBattleScripting.statChanger), flags, failInstr) == STAT_CHANGE_WORKED)
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattlescriptCurrInstr = ptrBefore;
}
}
// Haze
static void Cmd_normalisebuffs(void)
{
s32 i, j;
for (i = 0; i < gBattlersCount; i++)
{
for (j = 0; j < NUM_BATTLE_STATS; j++)
gBattleMons[i].statStages[j] = DEFAULT_STAT_STAGE;
}
gBattlescriptCurrInstr++;
}
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;
}
static void Cmd_setbide(void)
{
gBattleMons[gBattlerAttacker].status2 |= STATUS2_MULTIPLETURNS;
gLockedMoves[gBattlerAttacker] = gCurrentMove;
gTakenDmg[gBattlerAttacker] = 0;
gBattleMons[gBattlerAttacker].status2 |= STATUS2_BIDE_TURN(2);
gBattlescriptCurrInstr++;
}
static void Cmd_confuseifrepeatingattackends(void)
{
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_LOCK_CONFUSE))
{
gBattleScripting.moveEffect = (MOVE_EFFECT_THRASH | MOVE_EFFECT_AFFECTS_USER);
}
gBattlescriptCurrInstr++;
}
static void Cmd_setmultihitcounter(void)
{
if (gBattlescriptCurrInstr[1])
{
gMultiHitCounter = gBattlescriptCurrInstr[1];
}
else
{
gMultiHitCounter = Random() & 3;
if (gMultiHitCounter > 1)
gMultiHitCounter = (Random() & 3) + 2;
else
gMultiHitCounter += 2;
}
gBattlescriptCurrInstr += 2;
}
static void Cmd_initmultihitstring(void)
{
PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0)
gBattlescriptCurrInstr++;
}
static bool8 TryDoForceSwitchOut(void)
{
if (gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level)
{
*(gBattleStruct->battlerPartyIndexes + gBattlerTarget) = gBattlerPartyIndexes[gBattlerTarget];
}
else
{
u16 random = Random() & 0xFF;
if ((u32)((random * (gBattleMons[gBattlerAttacker].level + gBattleMons[gBattlerTarget].level) >> 8) + 1) <= (gBattleMons[gBattlerTarget].level / 4))
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
return FALSE;
}
*(gBattleStruct->battlerPartyIndexes + gBattlerTarget) = gBattlerPartyIndexes[gBattlerTarget];
}
gBattlescriptCurrInstr = BattleScript_SuccessForceOut;
return TRUE;
}
#define MON_CAN_BATTLE(mon) (((GetMonData(mon, MON_DATA_SPECIES) && GetMonData(mon, MON_DATA_IS_EGG) != TRUE && GetMonData(mon, MON_DATA_HP))))
static void Cmd_forcerandomswitch(void)
{
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
{
u8 i;
struct Pokemon *party;
u8 valid;
u8 val;
if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER)
party = gPlayerParty;
else
party = gEnemyParty;
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
{
valid = 0;
val = 0;
if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(gBattlerTarget)) == 1)
val = PARTY_SIZE / 2;
for (i = val; i < val + (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)
++valid;
}
}
else
{
valid = 0;
for (i = 0; i < 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)
++valid;
}
}
// Fails if there's only 1 mon left in single battle or there's less than 3 left in non-multi double battle.
if ((valid < 2 && (gBattleTypeFlags & (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI)) != BATTLE_TYPE_DOUBLE)
|| (valid < 3 && (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)))
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else if (TryDoForceSwitchOut())
{
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
{
do
{
val = Random() % (PARTY_SIZE / 2);
if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(gBattlerTarget)) == 1)
i = val + (PARTY_SIZE / 2);
else
i = val;
}
while (i == gBattlerPartyIndexes[gBattlerTarget]
|| i == gBattlerPartyIndexes[gBattlerTarget ^ 2]
|| !MON_CAN_BATTLE(&party[i]));
}
else
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
do
{
i = Random() % PARTY_SIZE;
}
while (i == gBattlerPartyIndexes[gBattlerTarget]
|| i == gBattlerPartyIndexes[gBattlerTarget ^ 2]
|| !MON_CAN_BATTLE(&party[i]));
}
else
{
do
{
i = Random() % PARTY_SIZE;
}
while (i == gBattlerPartyIndexes[gBattlerTarget]
|| !MON_CAN_BATTLE(&party[i]));
}
}
*(gBattleStruct->monToSwitchIntoId + gBattlerTarget) = i;
if (!IsMultiBattle())
SwitchPartyOrder(gBattlerTarget);
SwitchPartyOrderLinkMulti(gBattlerTarget, i, 0);
SwitchPartyOrderLinkMulti(gBattlerTarget ^ BIT_FLANK, i, 1);
}
}
else
{
TryDoForceSwitchOut();
}
}
// Randomly changes user's type to one of its moves' type
static void Cmd_tryconversiontypechange(void)
{
u8 validMoves = 0;
u8 moveChecked;
u8 moveType;
while (validMoves < MAX_MON_MOVES)
{
if (gBattleMons[gBattlerAttacker].moves[validMoves] == MOVE_NONE)
break;
validMoves++;
}
for (moveChecked = 0; moveChecked < validMoves; moveChecked++)
{
moveType = gMovesInfo[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)
{
break;
}
}
if (moveChecked == validMoves)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
do
{
while ((moveChecked = Random() & (MAX_MON_MOVES - 1)) >= validMoves);
moveType = gMovesInfo[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);
SET_BATTLER_TYPE(gBattlerAttacker, moveType);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType);
gBattlescriptCurrInstr += 5;
}
}
static void Cmd_givepaydaymoney(void)
{
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK) && gPaydayMoney != 0)
{
u32 bonusMoney = gPaydayMoney * gBattleStruct->moneyMultiplier;
AddMoney(&gSaveBlock1Ptr->money, bonusMoney);
PREPARE_HWORD_NUMBER_BUFFER(gBattleTextBuff1, 5, bonusMoney)
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_PrintPayDayMoneyString;
}
else
{
gBattlescriptCurrInstr++;
}
}
static void Cmd_setlightscreen(void)
{
if (gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] & SIDE_STATUS_LIGHTSCREEN)
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SIDE_STATUS_FAILED;
}
else
{
gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] |= SIDE_STATUS_LIGHTSCREEN;
gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].lightscreenTimer = 5;
gSideTimers[GET_BATTLER_SIDE(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++;
}
static void Cmd_tryKO(void)
{
u8 holdEffect, param;
if (gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY)
{
holdEffect = gEnigmaBerries[gBattlerTarget].holdEffect;
param = gEnigmaBerries[gBattlerTarget].holdEffectParam;
}
else
{
holdEffect = ItemId_GetHoldEffect(gBattleMons[gBattlerTarget].item);
param = ItemId_GetHoldEffectParam(gBattleMons[gBattlerTarget].item);
}
gPotentialItemEffectBattler = gBattlerTarget;
if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < param)
{
RecordItemEffectBattle(gBattlerTarget, HOLD_EFFECT_FOCUS_BAND);
gSpecialStatuses[gBattlerTarget].focusBanded = 1;
}
if (gBattleMons[gBattlerTarget].ability == ABILITY_STURDY)
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
gLastUsedAbility = ABILITY_STURDY;
gBattlescriptCurrInstr = BattleScript_SturdyPreventsOHKO;
RecordAbilityBattle(gBattlerTarget, ABILITY_STURDY);
}
else
{
u16 chance;
if (!(gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS))
{
chance = gMovesInfo[gCurrentMove].accuracy + (gBattleMons[gBattlerAttacker].level - gBattleMons[gBattlerTarget].level);
if (Random() % 100 + 1 < chance && gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level)
chance = TRUE;
else
chance = FALSE;
}
else if (gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker
&& gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level)
{
chance = TRUE;
}
else
{
chance = gMovesInfo[gCurrentMove].accuracy + (gBattleMons[gBattlerAttacker].level - gBattleMons[gBattlerTarget].level);
if (Random() % 100 + 1 < chance && gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level)
chance = TRUE;
else
chance = FALSE;
}
if (chance)
{
if (gProtectStructs[gBattlerTarget].endured)
{
gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1;
gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED;
}
else if (gSpecialStatuses[gBattlerTarget].focusBanded)
{
gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1;
gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON;
gLastUsedItem = gBattleMons[gBattlerTarget].item;
}
else
{
gBattleMoveDamage = gBattleMons[gBattlerTarget].hp;
gMoveResultFlags |= MOVE_RESULT_ONE_HIT_KO;
}
gBattlescriptCurrInstr += 5;
}
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 = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
}
// Super Fang
static void Cmd_damagetohalftargethp(void)
{
gBattleMoveDamage = gBattleMons[gBattlerTarget].hp / 2;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattlescriptCurrInstr++;
}
static void Cmd_setsandstorm(void)
{
if (gBattleWeather & B_WEATHER_SANDSTORM)
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED;
}
else
{
gBattleWeather = B_WEATHER_SANDSTORM_TEMPORARY;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SANDSTORM;
gWishFutureKnock.weatherDuration = 5;
}
gBattlescriptCurrInstr++;
}
static void Cmd_weatherdamage(void)
{
if (IS_BATTLE_TYPE_GHOST_WITHOUT_SCOPE(gBattleTypeFlags)
&& (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT))
{
gBattleMoveDamage = 0;
gBattlescriptCurrInstr++;
return;
}
if (WEATHER_HAS_EFFECT)
{
if (gBattleWeather & B_WEATHER_SANDSTORM)
{
if (gBattleMons[gBattlerAttacker].type1 != TYPE_ROCK
&& gBattleMons[gBattlerAttacker].type1 != TYPE_STEEL
&& gBattleMons[gBattlerAttacker].type1 != TYPE_GROUND
&& gBattleMons[gBattlerAttacker].type2 != TYPE_ROCK
&& gBattleMons[gBattlerAttacker].type2 != TYPE_STEEL
&& gBattleMons[gBattlerAttacker].type2 != TYPE_GROUND
&& gBattleMons[gBattlerAttacker].ability != ABILITY_SAND_VEIL
&& !(gStatuses3[gBattlerAttacker] & STATUS3_UNDERGROUND)
&& !(gStatuses3[gBattlerAttacker] & STATUS3_UNDERWATER))
{
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 16;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
}
else
{
gBattleMoveDamage = 0;
}
}
if (gBattleWeather & B_WEATHER_HAIL)
{
if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE)
&& !(gStatuses3[gBattlerAttacker] & STATUS3_UNDERGROUND)
&& !(gStatuses3[gBattlerAttacker] & STATUS3_UNDERWATER))
{
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 16;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
}
else
{
gBattleMoveDamage = 0;
}
}
}
else
{
gBattleMoveDamage = 0;
}
if (gAbsentBattlerFlags & gBitTable[gBattlerAttacker])
gBattleMoveDamage = 0;
gBattlescriptCurrInstr++;
}
static void Cmd_tryinfatuating(void)
{
struct Pokemon *monAttacker, *monTarget;
u16 speciesAttacker, speciesTarget;
u32 personalityAttacker, personalityTarget;
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
monAttacker = &gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]];
else
monAttacker = &gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker]];
if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER)
monTarget = &gPlayerParty[gBattlerPartyIndexes[gBattlerTarget]];
else
monTarget = &gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]];
speciesAttacker = GetMonData(monAttacker, MON_DATA_SPECIES);
personalityAttacker = GetMonData(monAttacker, MON_DATA_PERSONALITY);
speciesTarget = GetMonData(monTarget, MON_DATA_SPECIES);
personalityTarget = GetMonData(monTarget, MON_DATA_PERSONALITY);
if (gBattleMons[gBattlerTarget].ability == ABILITY_OBLIVIOUS)
{
gBattlescriptCurrInstr = BattleScript_ObliviousPreventsAttraction;
gLastUsedAbility = ABILITY_OBLIVIOUS;
RecordAbilityBattle(gBattlerTarget, ABILITY_OBLIVIOUS);
}
else
{
if (GetGenderFromSpeciesAndPersonality(speciesAttacker, personalityAttacker) == GetGenderFromSpeciesAndPersonality(speciesTarget, personalityTarget)
|| gBattleMons[gBattlerTarget].status2 & STATUS2_INFATUATION
|| GetGenderFromSpeciesAndPersonality(speciesAttacker, personalityAttacker) == MON_GENDERLESS
|| GetGenderFromSpeciesAndPersonality(speciesTarget, personalityTarget) == MON_GENDERLESS)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_INFATUATED_WITH(gBattlerAttacker);
gBattlescriptCurrInstr += 5;
}
}
}
static void Cmd_updatestatusicon(void)
{
if (gBattleControllerExecFlags)
return;
if (gBattlescriptCurrInstr[1] == BS_PLAYER2)
{
for (gActiveBattler = gBattleControllerExecFlags; gActiveBattler < gBattlersCount; gActiveBattler++)
{
if (!(gAbsentBattlerFlags & gBitTable[gActiveBattler]))
{
BtlController_EmitStatusIconUpdate(BUFFER_A, gBattleMons[gActiveBattler].status1, gBattleMons[gActiveBattler].status2);
MarkBattlerForControllerExec(gActiveBattler);
}
}
gBattlescriptCurrInstr += 2;
}
else if (gBattlescriptCurrInstr[1] == BS_ATTACKER_WITH_PARTNER)
{
gActiveBattler = gBattlerAttacker;
if (!(gAbsentBattlerFlags & gBitTable[gActiveBattler]))
{
BtlController_EmitStatusIconUpdate(BUFFER_A, gBattleMons[gActiveBattler].status1, gBattleMons[gActiveBattler].status2);
MarkBattlerForControllerExec(gActiveBattler);
}
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
{
gActiveBattler = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_FLANK);
if (!(gAbsentBattlerFlags & gBitTable[gActiveBattler]))
{
BtlController_EmitStatusIconUpdate(BUFFER_A, gBattleMons[gActiveBattler].status1, gBattleMons[gActiveBattler].status2);
MarkBattlerForControllerExec(gActiveBattler);
}
}
gBattlescriptCurrInstr += 2;
}
else
{
gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]);
BtlController_EmitStatusIconUpdate(BUFFER_A, gBattleMons[gActiveBattler].status1, gBattleMons[gActiveBattler].status2);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr += 2;
}
}
static void Cmd_setmist(void)
{
if (gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].mistTimer)
{
gMoveResultFlags |= MOVE_RESULT_FAILED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MIST_FAILED;
}
else
{
gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].mistTimer = 5;
gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].mistBattlerId = gBattlerAttacker;
gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] |= SIDE_STATUS_MIST;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_MIST;
}
gBattlescriptCurrInstr++;
}
static void Cmd_setfocusenergy(void)
{
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++;
}
static void Cmd_transformdataexecution(void)
{
gChosenMove = MOVE_UNAVAILABLE;
gBattlescriptCurrInstr++;
if (gBattleMons[gBattlerTarget].status2 & STATUS2_TRANSFORMED
|| gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE)
{
gMoveResultFlags |= MOVE_RESULT_FAILED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TRANSFORM_FAILED;
}
else
{
s32 i;
u8 *battleMonAttacker, *battleMonTarget;
gBattleMons[gBattlerAttacker].status2 |= STATUS2_TRANSFORMED;
gDisableStructs[gBattlerAttacker].disabledMove = MOVE_NONE;
gDisableStructs[gBattlerAttacker].disableTimer = 0;
gDisableStructs[gBattlerAttacker].transformedMonPersonality = gBattleMons[gBattlerTarget].personality;
gDisableStructs[gBattlerAttacker].mimickedMoves = 0;
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];
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (gMovesInfo[gBattleMons[gBattlerAttacker].moves[i]].pp < 5)
gBattleMons[gBattlerAttacker].pp[i] = gMovesInfo[gBattleMons[gBattlerAttacker].moves[i]].pp;
else
gBattleMons[gBattlerAttacker].pp[i] = 5;
}
gActiveBattler = gBattlerAttacker;
BtlController_EmitResetActionMoveSelection(BUFFER_A, RESET_MOVE_SELECTION);
MarkBattlerForControllerExec(gActiveBattler);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TRANSFORMED;
}
}
static void Cmd_setsubstitute(void)
{
u32 hp = gBattleMons[gBattlerAttacker].maxHP / 4;
if (gBattleMons[gBattlerAttacker].maxHP / 4 == 0)
hp = 1;
if (gBattleMons[gBattlerAttacker].hp <= hp)
{
gBattleMoveDamage = 0;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SUBSTITUTE_FAILED;
}
else
{
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 4; // one bit value will only work for pokemon 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++;
}
static bool8 IsMoveUncopyableByMimic(u16 move)
{
s32 i;
for (i = 0; sMovesForbiddenToCopy[i] != MIMIC_FORBIDDEN_END
&& sMovesForbiddenToCopy[i] != move; i++);
return (sMovesForbiddenToCopy[i] != MIMIC_FORBIDDEN_END);
}
static void Cmd_mimicattackcopy(void)
{
gChosenMove = MOVE_UNAVAILABLE;
if (IsMoveUncopyableByMimic(gLastMoves[gBattlerTarget])
|| gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED
|| gLastMoves[gBattlerTarget] == MOVE_NONE
|| gLastMoves[gBattlerTarget] == MOVE_UNAVAILABLE)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
int i;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (gBattleMons[gBattlerAttacker].moves[i] == gLastMoves[gBattlerTarget])
break;
}
if (i == MAX_MON_MOVES)
{
gBattleMons[gBattlerAttacker].moves[gCurrMovePos] = gLastMoves[gBattlerTarget];
if (gMovesInfo[gLastMoves[gBattlerTarget]].pp < 5)
gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = gMovesInfo[gLastMoves[gBattlerTarget]].pp;
else
gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = 5;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastMoves[gBattlerTarget])
gDisableStructs[gBattlerAttacker].mimickedMoves |= gBitTable[gCurrMovePos];
gBattlescriptCurrInstr += 5;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
}
static void Cmd_metronome(void)
{
while (TRUE)
{
s32 i;
gCurrentMove = (Random() & 0x1FF) + 1;
if (gCurrentMove >= MOVES_COUNT)
continue;
for (i = 0; i < MAX_MON_MOVES; i++); // ?
i = -1;
while (TRUE)
{
i++;
if (sMovesForbiddenToCopy[i] == gCurrentMove)
break;
if (sMovesForbiddenToCopy[i] == METRONOME_FORBIDDEN_END)
break;
}
if (sMovesForbiddenToCopy[i] == METRONOME_FORBIDDEN_END)
{
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattlescriptCurrInstr = GET_MOVE_BATTLESCRIPT(gCurrentMove);
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
return;
}
}
}
static void Cmd_dmgtolevel(void)
{
gBattleMoveDamage = gBattleMons[gBattlerAttacker].level;
gBattlescriptCurrInstr++;
}
static void Cmd_psywavedamageeffect(void)
{
s32 randDamage;
while ((randDamage = Random() % 16) > 10);
randDamage *= 10;
gBattleMoveDamage = gBattleMons[gBattlerAttacker].level * (randDamage + 50) / 100;
gBattlescriptCurrInstr++;
}
static void Cmd_counterdamagecalculator(void)
{
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 (gSideTimers[sideTarget].followmeTimer && gBattleMons[gSideTimers[sideTarget].followmeTarget].hp)
gBattlerTarget = gSideTimers[sideTarget].followmeTarget;
else
gBattlerTarget = gProtectStructs[gBattlerAttacker].physicalBattlerId;
gBattlescriptCurrInstr += 5;
}
else
{
gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = 1;
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
// A copy of Cmd_counterdamagecalculator with the physical -> special field changes
static void Cmd_mirrorcoatdamagecalculator(void)
{
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 (gSideTimers[sideTarget].followmeTimer && gBattleMons[gSideTimers[sideTarget].followmeTarget].hp)
gBattlerTarget = gSideTimers[sideTarget].followmeTarget;
else
gBattlerTarget = gProtectStructs[gBattlerAttacker].specialBattlerId;
gBattlescriptCurrInstr += 5;
}
else
{
gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = 1;
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_disablelastusedattack(void)
{
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];
gDisableStructs[gBattlerTarget].disableTimer = (Random() & 3) + 2;
// gDisableStructs[gBattlerTarget].disableTimerStartValue = gDisableStructs[gBattlerTarget].disableTimer; // used to save the random amount of turns?
gBattlescriptCurrInstr += 5;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_trysetencore(void)
{
s32 i;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (gBattleMons[gBattlerTarget].moves[i] == gLastMoves[gBattlerTarget])
break;
}
if (gLastMoves[gBattlerTarget] == MOVE_STRUGGLE
|| gLastMoves[gBattlerTarget] == MOVE_ENCORE
|| gLastMoves[gBattlerTarget] == MOVE_MIRROR_MOVE)
{
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 = (Random() & 3) + 3;
// gDisableStructs[gBattlerTarget].encoreTimerStartValue = gDisableStructs[gBattlerTarget].encoreTimer;
gBattlescriptCurrInstr += 5;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_painsplitdmgcalc(void)
{
if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE))
{
s32 hpDiff = (gBattleMons[gBattlerAttacker].hp + gBattleMons[gBattlerTarget].hp) / 2;
s32 painSplitHp = gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 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].dmg = 0xFFFF;
gBattlescriptCurrInstr += 5;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
// Conversion 2
static void Cmd_settypetorandomresistance(void)
{
if (gLastLandedMoves[gBattlerAttacker] == MOVE_NONE
|| gLastLandedMoves[gBattlerAttacker] == MOVE_UNAVAILABLE)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else if (IsTwoTurnsMove(gLastLandedMoves[gBattlerAttacker])
&& gBattleMons[gLastHitBy[gBattlerAttacker]].status2 & STATUS2_MULTIPLETURNS)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
s32 i, j, rands;
for (rands = 0; rands < 1000; rands++)
{
while (((i = Random() % 128) > sizeof(gTypeEffectiveness) / 3));
i *= 3;
if (TYPE_EFFECT_ATK_TYPE(i) == gLastHitByType[gBattlerAttacker]
&& TYPE_EFFECT_MULTIPLIER(i) <= TYPE_MUL_NOT_EFFECTIVE
&& !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_EFFECT_DEF_TYPE(i)))
{
SET_BATTLER_TYPE(gBattlerAttacker, TYPE_EFFECT_DEF_TYPE(i));
PREPARE_TYPE_BUFFER(gBattleTextBuff1, TYPE_EFFECT_DEF_TYPE(i));
gBattlescriptCurrInstr += 5;
return;
}
}
for (j = 0, rands = 0; rands < sizeof(gTypeEffectiveness); j += 3, rands += 3)
{
switch (TYPE_EFFECT_ATK_TYPE(j))
{
case TYPE_ENDTABLE:
case TYPE_FORESIGHT:
break;
default:
if (TYPE_EFFECT_ATK_TYPE(j) == gLastHitByType[gBattlerAttacker]
&& TYPE_EFFECT_MULTIPLIER(j) <= 5
&& !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_EFFECT_DEF_TYPE(i)))
{
SET_BATTLER_TYPE(gBattlerAttacker, TYPE_EFFECT_DEF_TYPE(rands));
PREPARE_TYPE_BUFFER(gBattleTextBuff1, TYPE_EFFECT_DEF_TYPE(rands))
gBattlescriptCurrInstr += 5;
return;
}
break;
}
}
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_setalwayshitflag(void)
{
gStatuses3[gBattlerTarget] &= ~STATUS3_ALWAYS_HITS;
gStatuses3[gBattlerTarget] |= STATUS3_ALWAYS_HITS_TURN(2);
gDisableStructs[gBattlerTarget].battlerWithSureHit = gBattlerAttacker;
gBattlescriptCurrInstr++;
}
// Sketch
static void Cmd_copymovepermanently(void)
{
gChosenMove = MOVE_UNAVAILABLE;
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED)
&& gLastPrintedMoves[gBattlerTarget] != MOVE_STRUGGLE
&& gLastPrintedMoves[gBattlerTarget] != MOVE_NONE
&& gLastPrintedMoves[gBattlerTarget] != MOVE_UNAVAILABLE
&& gLastPrintedMoves[gBattlerTarget] != MOVE_SKETCH)
{
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 = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else // sketch worked
{
struct MovePpInfo movePpData;
gBattleMons[gBattlerAttacker].moves[gCurrMovePos] = gLastPrintedMoves[gBattlerTarget];
gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = gMovesInfo[gLastPrintedMoves[gBattlerTarget]].pp;
gActiveBattler = gBattlerAttacker;
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(BUFFER_A, REQUEST_MOVES_PP_BATTLE, 0, sizeof(movePpData), &movePpData);
MarkBattlerForControllerExec(gActiveBattler);
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastPrintedMoves[gBattlerTarget])
gBattlescriptCurrInstr += 5;
}
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static bool8 IsTwoTurnsMove(u16 move)
{
if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK
|| gMovesInfo[move].effect == EFFECT_SOLAR_BEAM
|| gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE
|| gMovesInfo[move].effect == EFFECT_BIDE)
return TRUE;
else
return FALSE;
}
static bool8 IsInvalidForSleepTalkOrAssist(u16 move)
{
if (move == MOVE_NONE
|| move == MOVE_SLEEP_TALK
|| move == MOVE_ASSIST
|| move == MOVE_MIRROR_MOVE
|| move == MOVE_METRONOME)
return TRUE;
else
return FALSE;
}
static u8 AttacksThisTurn(u8 battlerId, u16 move) // Note: returns 1 if it's a charging turn, otherwise 2
{
// first argument is unused
if (gMovesInfo[move].effect == EFFECT_SOLAR_BEAM
&& (gBattleWeather & B_WEATHER_SUN))
return 2;
if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK
|| gMovesInfo[move].effect == EFFECT_SOLAR_BEAM
|| gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE
|| gMovesInfo[move].effect == EFFECT_BIDE)
{
if ((gHitMarker & HITMARKER_CHARGING))
return 1;
}
return 2;
}
static void Cmd_trychoosesleeptalkmove(void)
{
s32 i;
u8 unusableMovesBits = 0;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (IsInvalidForSleepTalkOrAssist(gBattleMons[gBattlerAttacker].moves[i])
|| gBattleMons[gBattlerAttacker].moves[i] == MOVE_FOCUS_PUNCH
|| gBattleMons[gBattlerAttacker].moves[i] == MOVE_UPROAR
|| IsTwoTurnsMove(gBattleMons[gBattlerAttacker].moves[i]))
{
unusableMovesBits |= gBitTable[i];
}
}
unusableMovesBits = CheckMoveLimitations(gBattlerAttacker, unusableMovesBits, ~MOVE_LIMITATION_PP);
if (unusableMovesBits == (1 << MAX_MON_MOVES) - 1) // all 4 moves cannot be chosen
{
gBattlescriptCurrInstr += 5;
}
else // at least one move can be chosen
{
u32 movePosition;
do
{
movePosition = Random() & (MAX_MON_MOVES - 1);
} while ((gBitTable[movePosition] & unusableMovesBits));
gCalledMove = gBattleMons[gBattlerAttacker].moves[movePosition];
gCurrMovePos = movePosition;
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_setdestinybond(void)
{
gBattleMons[gBattlerAttacker].status2 |= STATUS2_DESTINY_BOND;
gBattlescriptCurrInstr++;
}
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)
{
TrySetDestinyBondToHappen();
gBattlescriptCurrInstr++;
}
static void Cmd_remaininghptopower(void)
{
s32 i;
s32 hpFraction = GetScaledHPFraction(gBattleMons[gBattlerAttacker].hp, gBattleMons[gBattlerAttacker].maxHP, 48);
for (i = 0; i < (s32) sizeof(sFlailHpScaleToPowerTable); i += 2)
{
if (hpFraction <= sFlailHpScaleToPowerTable[i])
break;
}
gDynamicBasePower = sFlailHpScaleToPowerTable[i + 1];
gBattlescriptCurrInstr++;
}
static void Cmd_tryspiteppreduce(void)
{
if (gLastMoves[gBattlerTarget] != MOVE_NONE
&& gLastMoves[gBattlerTarget] != MOVE_UNAVAILABLE)
{
s32 i;
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] > 1)
{
s32 ppToDeduct = (Random() & 3) + 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;
gActiveBattler = gBattlerTarget;
// if (MOVE_IS_PERMANENT(gActiveBattler, i)), but backwards
if (!(gDisableStructs[gActiveBattler].mimickedMoves & gBitTable[i])
&& !(gBattleMons[gActiveBattler].status2 & STATUS2_TRANSFORMED))
{
BtlController_EmitSetMonData(BUFFER_A, REQUEST_PPMOVE1_BATTLE + i, 0, sizeof(gBattleMons[gActiveBattler].pp[i]), &gBattleMons[gActiveBattler].pp[i]);
MarkBattlerForControllerExec(gActiveBattler);
}
gBattlescriptCurrInstr += 5;
if (gBattleMons[gBattlerTarget].pp[i] == 0)
CancelMultiTurnMoves(gBattlerTarget);
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_healpartystatus(void)
{
u32 zero = 0;
u8 toHeal = 0;
if (gCurrentMove == MOVE_HEAL_BELL)
{
struct Pokemon *party;
s32 i;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_BELL;
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
party = gPlayerParty;
else
party = gEnemyParty;
if (gBattleMons[gBattlerAttacker].ability != 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;
}
gActiveBattler = gBattleScripting.battler = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_FLANK);
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& !(gAbsentBattlerFlags & gBitTable[gActiveBattler]))
{
if (gBattleMons[gActiveBattler].ability != ABILITY_SOUNDPROOF)
{
gBattleMons[gActiveBattler].status1 = 0;
gBattleMons[gActiveBattler].status2 &= ~STATUS2_NIGHTMARE;
}
else
{
RecordAbilityBattle(gActiveBattler, gBattleMons[gActiveBattler].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 = gBattleMons[gBattlerAttacker].ability;
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& gBattlerPartyIndexes[gActiveBattler] == i
&& !(gAbsentBattlerFlags & gBitTable[gActiveBattler]))
ability = gBattleMons[gActiveBattler].ability;
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;
gActiveBattler = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_FLANK);
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& !(gAbsentBattlerFlags & gBitTable[gActiveBattler]))
{
gBattleMons[gActiveBattler].status1 = 0;
gBattleMons[gActiveBattler].status2 &= ~STATUS2_NIGHTMARE;
}
}
if (toHeal)
{
gActiveBattler = gBattlerAttacker;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, toHeal, sizeof(zero), &zero);
MarkBattlerForControllerExec(gActiveBattler);
}
gBattlescriptCurrInstr++;
}
static void Cmd_cursetarget(void)
{
if (gBattleMons[gBattlerTarget].status2 & STATUS2_CURSED)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_CURSED;
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 2;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattlescriptCurrInstr += 5;
}
}
static void Cmd_trysetspikes(void)
{
u8 targetSide = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE;
if (gSideTimers[targetSide].spikesAmount == 3)
{
gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = 1;
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
gSideStatuses[targetSide] |= SIDE_STATUS_SPIKES;
gSideTimers[targetSide].spikesAmount++;
gBattlescriptCurrInstr += 5;
}
}
static void Cmd_setforesight(void)
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_FORESIGHT;
gBattlescriptCurrInstr++;
}
static void Cmd_trysetperishsong(void)
{
s32 i;
s32 notAffectedCount = 0;
for (i = 0; i < gBattlersCount; i++)
{
if (gStatuses3[i] & STATUS3_PERISH_SONG
|| gBattleMons[i].ability == ABILITY_SOUNDPROOF)
{
notAffectedCount++;
}
else
{
gStatuses3[i] |= STATUS3_PERISH_SONG;
gDisableStructs[i].perishSongTimer = 3;
// gDisableStructs[i].perishSongTimerStartValue = 3;
}
}
PressurePPLoseOnUsingPerishSong(gBattlerAttacker);
if (notAffectedCount == gBattlersCount)
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
else
gBattlescriptCurrInstr += 5;
}
static void Cmd_rolloutdamagecalculation(void)
{
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
{
CancelMultiTurnMoves(gBattlerAttacker);
gBattlescriptCurrInstr = BattleScript_MoveMissedPause;
}
else
{
s32 i;
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;
}
gDynamicBasePower = gMovesInfo[gCurrentMove].power;
for (i = 1; i < (5 - gDisableStructs[gBattlerAttacker].rolloutTimer); i++)
gDynamicBasePower *= 2;
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_DEFENSE_CURL)
gDynamicBasePower *= 2;
gBattlescriptCurrInstr++;
}
}
static void Cmd_jumpifconfusedandstatmaxed(void)
{
if (gBattleMons[gBattlerTarget].status2 & STATUS2_CONFUSION
&& gBattleMons[gBattlerTarget].statStages[gBattlescriptCurrInstr[1]] == MAX_STAT_STAGE)
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2);
else
gBattlescriptCurrInstr += 6;
}
static void Cmd_furycuttercalc(void)
{
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
{
gDisableStructs[gBattlerAttacker].furyCutterCounter = 0;
gBattlescriptCurrInstr = BattleScript_MoveMissedPause;
}
else
{
s32 i;
if (gDisableStructs[gBattlerAttacker].furyCutterCounter != 5)
gDisableStructs[gBattlerAttacker].furyCutterCounter++;
gDynamicBasePower = gMovesInfo[gCurrentMove].power;
for (i = 1; i < gDisableStructs[gBattlerAttacker].furyCutterCounter; i++)
gDynamicBasePower *= 2;
gBattlescriptCurrInstr++;
}
}
static void Cmd_friendshiptodamagecalculation(void)
{
if (gMovesInfo[gCurrentMove].effect == EFFECT_RETURN)
gDynamicBasePower = 10 * (gBattleMons[gBattlerAttacker].friendship) / 25;
else // EFFECT_FRUSTRATION
gDynamicBasePower = 10 * (255 - gBattleMons[gBattlerAttacker].friendship) / 25;
gBattlescriptCurrInstr++;
}
static void Cmd_presentdamagecalculation(void)
{
s32 rand = Random() & 0xFF;
if (rand < 102)
gDynamicBasePower = 40;
else if (rand < 178)
gDynamicBasePower = 80;
else if (rand < 204)
gDynamicBasePower = 120;
else
{
gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 4;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattleMoveDamage *= -1;
}
if (rand < 204)
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)
{
if (gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] & SIDE_STATUS_SAFEGUARD)
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SIDE_STATUS_FAILED;
}
else
{
gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] |= SIDE_STATUS_SAFEGUARD;
gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].safeguardTimer = 5;
gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].safeguardBattlerId = gBattlerAttacker;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_SAFEGUARD;
}
gBattlescriptCurrInstr++;
}
static void Cmd_magnitudedamagecalculation(void)
{
s32 magnitude = Random() % 100;
if (magnitude < 5)
{
gDynamicBasePower = 10;
magnitude = 4;
}
else if (magnitude < 15)
{
gDynamicBasePower = 30;
magnitude = 5;
}
else if (magnitude < 35)
{
gDynamicBasePower = 50;
magnitude = 6;
}
else if (magnitude < 65)
{
gDynamicBasePower = 70;
magnitude = 7;
}
else if (magnitude < 85)
{
gDynamicBasePower = 90;
magnitude = 8;
}
else if (magnitude < 95)
{
gDynamicBasePower = 110;
magnitude = 9;
}
else
{
gDynamicBasePower = 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++;
}
static void Cmd_jumpifnopursuitswitchdmg(void)
{
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
&& gChosenMoveByBattler[gBattlerTarget] == MOVE_PURSUIT)
{
s32 i;
for (i = 0; i < gBattlersCount; i++)
{
if (gBattlerByTurnOrder[i] == gBattlerTarget)
gActionsByTurnOrder[i] = B_ACTION_TRY_FINISH;
}
gCurrentMove = MOVE_PURSUIT;
gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerTarget);
gBattlescriptCurrInstr += 5;
gBattleScripting.animTurn = 1;
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_setsunny(void)
{
if (gBattleWeather & B_WEATHER_SUN)
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED;
}
else
{
gBattleWeather = B_WEATHER_SUN_TEMPORARY;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_SUNLIGHT;
gWishFutureKnock.weatherDuration = 5;
}
gBattlescriptCurrInstr++;
}
// Belly Drum
static void Cmd_maxattackhalvehp(void)
{
u32 halfHp = gBattleMons[gBattlerAttacker].maxHP / 2;
if (!(gBattleMons[gBattlerAttacker].maxHP / 2))
halfHp = 1;
if (gBattleMons[gBattlerAttacker].statStages[STAT_ATK] < MAX_STAT_STAGE
&& gBattleMons[gBattlerAttacker].hp > halfHp)
{
gBattleMons[gBattlerAttacker].statStages[STAT_ATK] = MAX_STAT_STAGE;
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 2;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattlescriptCurrInstr += 5;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
// Psych Up
static void Cmd_copyfoestats(void)
{
s32 i;
for (i = 0; i < NUM_BATTLE_STATS; i++)
{
gBattleMons[gBattlerAttacker].statStages[i] = gBattleMons[gBattlerTarget].statStages[i];
}
gBattlescriptCurrInstr += 5; // Has an unused jump ptr(possibly for a failed attempt) parameter.
}
static void Cmd_rapidspinfree(void)
{
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_WRAPPED)
{
gBattleScripting.battler = gBattlerTarget;
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_WRAPPED;
gBattlerTarget = *(gBattleStruct->wrappedBy + gBattlerAttacker);
gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN;
gBattleTextBuff1[1] = B_BUFF_MOVE;
gBattleTextBuff1[2] = *(gBattleStruct->wrappedMove + gBattlerAttacker * 2 + 0);
gBattleTextBuff1[3] = *(gBattleStruct->wrappedMove + gBattlerAttacker * 2 + 1);
gBattleTextBuff1[4] = B_BUFF_EOS;
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[GetBattlerSide(gBattlerAttacker)] & SIDE_STATUS_SPIKES)
{
gSideStatuses[GetBattlerSide(gBattlerAttacker)] &= ~SIDE_STATUS_SPIKES;
gSideTimers[GetBattlerSide(gBattlerAttacker)].spikesAmount = 0;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_SpikesFree;
}
else
{
gBattlescriptCurrInstr++;
}
}
static void Cmd_setdefensecurlbit(void)
{
gBattleMons[gBattlerAttacker].status2 |= STATUS2_DEFENSE_CURL;
gBattlescriptCurrInstr++;
}
static void Cmd_recoverbasedonsunlight(void)
{
gBattlerTarget = gBattlerAttacker;
if (gBattleMons[gBattlerAttacker].hp != gBattleMons[gBattlerAttacker].maxHP)
{
if (gBattleWeather == 0 || !WEATHER_HAS_EFFECT)
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 2;
else if (gBattleWeather & B_WEATHER_SUN)
gBattleMoveDamage = 20 * gBattleMons[gBattlerAttacker].maxHP / 30;
else // not sunny weather
gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 4;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattleMoveDamage *= -1;
gBattlescriptCurrInstr += 5;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_hiddenpowercalc(void)
{
s32 powerBits, typeBits;
powerBits = ((gBattleMons[gBattlerAttacker].hpIV & 2) >> 1)
| ((gBattleMons[gBattlerAttacker].attackIV & 2) << 0)
| ((gBattleMons[gBattlerAttacker].defenseIV & 2) << 1)
| ((gBattleMons[gBattlerAttacker].speedIV & 2) << 2)
| ((gBattleMons[gBattlerAttacker].spAttackIV & 2) << 3)
| ((gBattleMons[gBattlerAttacker].spDefenseIV & 2) << 4);
typeBits = ((gBattleMons[gBattlerAttacker].hpIV & 1) << 0)
| ((gBattleMons[gBattlerAttacker].attackIV & 1) << 1)
| ((gBattleMons[gBattlerAttacker].defenseIV & 1) << 2)
| ((gBattleMons[gBattlerAttacker].speedIV & 1) << 3)
| ((gBattleMons[gBattlerAttacker].spAttackIV & 1) << 4)
| ((gBattleMons[gBattlerAttacker].spDefenseIV & 1) << 5);
gDynamicBasePower = (40 * powerBits) / 63 + 30;
// Subtract 3 instead of 1 below because 2 types are excluded (TYPE_NORMAL and TYPE_MYSTERY)
// The final + 1 skips past Normal, and the following conditional skips TYPE_MYSTERY
gBattleStruct->dynamicMoveType = ((NUMBER_OF_MON_TYPES - 3) * typeBits) / 63 + 1;
if (gBattleStruct->dynamicMoveType >= TYPE_MYSTERY)
gBattleStruct->dynamicMoveType++;
gBattleStruct->dynamicMoveType |= F_DYNAMIC_TYPE_IGNORE_PHYSICALITY | F_DYNAMIC_TYPE_SET;
gBattlescriptCurrInstr++;
}
static void Cmd_selectfirstvalidtarget(void)
{
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
{
if (gBattlerTarget == gBattlerAttacker)
continue;
if (!(gAbsentBattlerFlags & gBitTable[gBattlerTarget]))
break;
}
gBattlescriptCurrInstr++;
}
static void Cmd_trysetfutureattack(void)
{
if (gWishFutureKnock.futureSightCounter[gBattlerTarget] != 0)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
gWishFutureKnock.futureSightMove[gBattlerTarget] = gCurrentMove;
gWishFutureKnock.futureSightAttacker[gBattlerTarget] = gBattlerAttacker;
gWishFutureKnock.futureSightCounter[gBattlerTarget] = 3;
gWishFutureKnock.futureSightDmg[gBattlerTarget] = CalculateBaseDamageOld(&gBattleMons[gBattlerAttacker], &gBattleMons[gBattlerTarget], gCurrentMove,
gSideStatuses[GET_BATTLER_SIDE(gBattlerTarget)], 0,
0, gBattlerAttacker, gBattlerTarget);
if (gProtectStructs[gBattlerAttacker].helpingHand)
gWishFutureKnock.futureSightDmg[gBattlerTarget] = gWishFutureKnock.futureSightDmg[gBattlerTarget] * 15 / 10;
if (gCurrentMove == MOVE_DOOM_DESIRE)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOOM_DESIRE;
else
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FUTURE_SIGHT;
gBattlescriptCurrInstr += 5;
}
}
static void Cmd_trydobeatup(void)
{
struct Pokemon *party;
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
party = gPlayerParty;
else
party = gEnemyParty;
if (gBattleMons[gBattlerTarget].hp == 0)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
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)
&& 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 += 9;
gBattleMoveDamage = gSpeciesInfo[GetMonData(&party[gBattleCommunication[0]], MON_DATA_SPECIES)].baseAttack;
gBattleMoveDamage *= gMovesInfo[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 = T1_READ_PTR(gBattlescriptCurrInstr + 1);
else
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 5);
}
}
static void Cmd_setsemiinvulnerablebit(void)
{
switch (gCurrentMove)
{
case MOVE_FLY:
case MOVE_BOUNCE:
gStatuses3[gBattlerAttacker] |= STATUS3_ON_AIR;
break;
case MOVE_DIG:
gStatuses3[gBattlerAttacker] |= STATUS3_UNDERGROUND;
break;
case MOVE_DIVE:
gStatuses3[gBattlerAttacker] |= STATUS3_UNDERWATER;
break;
}
gBattlescriptCurrInstr++;
}
static void Cmd_clearsemiinvulnerablebit(void)
{
switch (gCurrentMove)
{
case MOVE_FLY:
case MOVE_BOUNCE:
gStatuses3[gBattlerAttacker] &= ~STATUS3_ON_AIR;
break;
case MOVE_DIG:
gStatuses3[gBattlerAttacker] &= ~STATUS3_UNDERGROUND;
break;
case MOVE_DIVE:
gStatuses3[gBattlerAttacker] &= ~STATUS3_UNDERWATER;
break;
}
gBattlescriptCurrInstr++;
}
static void Cmd_setminimize(void)
{
if (gHitMarker & HITMARKER_OBEYS)
gStatuses3[gBattlerAttacker] |= STATUS3_MINIMIZED;
gBattlescriptCurrInstr++;
}
static void Cmd_sethail(void)
{
if (gBattleWeather & B_WEATHER_HAIL)
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED;
}
else
{
gBattleWeather = B_WEATHER_HAIL_TEMPORARY;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_HAIL;
gWishFutureKnock.weatherDuration = 5;
}
gBattlescriptCurrInstr++;
}
static void Cmd_trymemento(void)
{
if (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 = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
// Success, drop user's HP bar to 0
gActiveBattler = gBattlerAttacker;
gBattleMoveDamage = gBattleMons[gActiveBattler].hp;
BtlController_EmitHealthBarUpdate(BUFFER_A, INSTANT_HP_BAR_DROP);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr += 5;
}
}
// Follow Me
static void Cmd_setforcedtarget(void)
{
gSideTimers[GetBattlerSide(gBattlerAttacker)].followmeTimer = 1;
gSideTimers[GetBattlerSide(gBattlerAttacker)].followmeTarget = gBattlerAttacker;
gBattlescriptCurrInstr++;
}
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)
{
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gCurrentMove = sNaturePowerMoves[gBattleTerrain];
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
BattleScriptPush(GET_MOVE_BATTLESCRIPT(gCurrentMove));
gBattlescriptCurrInstr++;
}
// Refresh
static void Cmd_cureifburnedparalysedorpoisoned(void)
{
if (gBattleMons[gBattlerAttacker].status1 & (STATUS1_POISON | STATUS1_BURN | STATUS1_PARALYSIS | STATUS1_TOXIC_POISON))
{
gBattleMons[gBattlerAttacker].status1 = 0;
gBattlescriptCurrInstr += 5;
gActiveBattler = gBattlerAttacker;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gActiveBattler].status1), &gBattleMons[gActiveBattler].status1);
MarkBattlerForControllerExec(gActiveBattler);
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_settorment(void)
{
if (gBattleMons[gBattlerTarget].status2 & STATUS2_TORMENT)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
gBattleMons[gBattlerTarget].status2 |= STATUS2_TORMENT;
gBattlescriptCurrInstr += 5;
}
}
static void Cmd_jumpifnodamage(void)
{
if (gProtectStructs[gBattlerAttacker].physicalDmg || gProtectStructs[gBattlerAttacker].specialDmg)
gBattlescriptCurrInstr += 5;
else
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
static void Cmd_settaunt(void)
{
if (gDisableStructs[gBattlerTarget].tauntTimer == 0)
{
gDisableStructs[gBattlerTarget].tauntTimer = 2;
// gDisableStructs[gBattlerTarget].tauntTimer2 = 2;
gBattlescriptCurrInstr += 5;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_trysethelpinghand(void)
{
gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_FLANK);
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& !(gAbsentBattlerFlags & gBitTable[gBattlerTarget])
&& !gProtectStructs[gBattlerAttacker].helpingHand
&& !gProtectStructs[gBattlerTarget].helpingHand)
{
gProtectStructs[gBattlerTarget].helpingHand = 1;
gBattlescriptCurrInstr += 5;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
// Trick
static void Cmd_tryswapitems(void)
{
// opponent can't swap items with player in regular battles
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER
|| (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT
&& !(gBattleTypeFlags & (BATTLE_TYPE_LINK
| BATTLE_TYPE_BATTLE_TOWER
| BATTLE_TYPE_EREADER_TRAINER))
&& gTrainerBattleOpponent_A != TRAINER_SECRET_BASE))
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
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_BATTLE_TOWER
| BATTLE_TYPE_EREADER_TRAINER))
&& gTrainerBattleOpponent_A != TRAINER_SECRET_BASE
&& (gWishFutureKnock.knockedOffMons[sideAttacker] & gBitTable[gBattlerPartyIndexes[gBattlerAttacker]]
|| gWishFutureKnock.knockedOffMons[sideTarget] & gBitTable[gBattlerPartyIndexes[gBattlerTarget]]))
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
// can't swap if two pokemon 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)
|| gBattleMons[gBattlerAttacker].item == ITEM_ENIGMA_BERRY
|| gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY
|| IS_ITEM_MAIL(gBattleMons[gBattlerAttacker].item)
|| IS_ITEM_MAIL(gBattleMons[gBattlerTarget].item))
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
// check if ability prevents swapping
else if (gBattleMons[gBattlerTarget].ability == 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;
gActiveBattler = gBattlerAttacker;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(*newItemAtk), newItemAtk);
MarkBattlerForControllerExec(gBattlerAttacker);
gActiveBattler = gBattlerTarget;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item);
MarkBattlerForControllerExec(gBattlerTarget);
*(u8 *)((u8 *)(&gBattleStruct->choicedMove[gBattlerTarget]) + 0) = 0;
*(u8 *)((u8 *)(&gBattleStruct->choicedMove[gBattlerTarget]) + 1) = 0;
*(u8 *)((u8 *)(&gBattleStruct->choicedMove[gBattlerAttacker]) + 0) = 0;
*(u8 *)((u8 *)(&gBattleStruct->choicedMove[gBattlerAttacker]) + 1) = 0;
gBattlescriptCurrInstr += 5;
PREPARE_ITEM_BUFFER(gBattleTextBuff1, *newItemAtk)
PREPARE_ITEM_BUFFER(gBattleTextBuff2, oldItemAtk)
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)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ITEM_SWAP_TAKEN; // nothing -> <- target's item
else
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ITEM_SWAP_GIVEN; // attacker's item -> <- nothing
}
}
}
// Role Play
static void Cmd_trycopyability(void)
{
if (gBattleMons[gBattlerTarget].ability != ABILITY_NONE
&& gBattleMons[gBattlerTarget].ability != ABILITY_WONDER_GUARD)
{
gBattleMons[gBattlerAttacker].ability = gBattleMons[gBattlerTarget].ability;
gLastUsedAbility = gBattleMons[gBattlerTarget].ability;
gBattlescriptCurrInstr += 5;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_trywish(void)
{
switch (gBattlescriptCurrInstr[1])
{
case 0: // use wish
if (gWishFutureKnock.wishCounter[gBattlerAttacker] == 0)
{
gWishFutureKnock.wishCounter[gBattlerAttacker] = 2;
gWishFutureKnock.wishMonId[gBattlerAttacker] = gBattlerPartyIndexes[gBattlerAttacker];
gBattlescriptCurrInstr += 6;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2);
}
break;
case 1: // heal effect
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerTarget, gWishFutureKnock.wishMonId[gBattlerTarget])
gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 2;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattleMoveDamage *= -1;
if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP)
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2);
else
gBattlescriptCurrInstr += 6;
break;
}
}
// Ingrain
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 (gAbilitiesInfo[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)
{
if (gStatuses3[gBattlerTarget] & STATUS3_YAWN
|| gBattleMons[gBattlerTarget].status1 & STATUS1_ANY)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
gStatuses3[gBattlerTarget] |= STATUS3_YAWN_TURN(2);
gBattlescriptCurrInstr += 5;
}
}
static void Cmd_setdamagetohealthdifference(void)
{
if (gBattleMons[gBattlerTarget].hp <= gBattleMons[gBattlerAttacker].hp)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - gBattleMons[gBattlerAttacker].hp;
gBattlescriptCurrInstr += 5;
}
}
static void Cmd_scaledamagebyhealthratio(void)
{
if (gDynamicBasePower == 0)
{
u8 power = gMovesInfo[gCurrentMove].power;
gDynamicBasePower = gBattleMons[gBattlerAttacker].hp * power / gBattleMons[gBattlerAttacker].maxHP;
if (gDynamicBasePower == 0)
gDynamicBasePower = 1;
}
gBattlescriptCurrInstr++;
}
// Skill Swap
static void Cmd_tryswapabilities(void)
{
if ((gBattleMons[gBattlerAttacker].ability == ABILITY_NONE
&& gBattleMons[gBattlerTarget].ability == ABILITY_NONE)
|| gBattleMons[gBattlerAttacker].ability == ABILITY_WONDER_GUARD
|| gBattleMons[gBattlerTarget].ability == ABILITY_WONDER_GUARD
|| gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
u16 abilityAtk = gBattleMons[gBattlerAttacker].ability;
gBattleMons[gBattlerAttacker].ability = gBattleMons[gBattlerTarget].ability;
gBattleMons[gBattlerTarget].ability = abilityAtk;
gBattlescriptCurrInstr += 5;
}
}
static void Cmd_tryimprison(void)
{
if ((gStatuses3[gBattlerAttacker] & STATUS3_IMPRISONED_OTHERS))
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
u8 battlerId, sideAttacker;
sideAttacker = GetBattlerSide(gBattlerAttacker);
PressurePPLoseOnUsingImprison(gBattlerAttacker);
for (battlerId = 0; battlerId < gBattlersCount; battlerId++)
{
if (sideAttacker != GetBattlerSide(battlerId))
{
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[battlerId].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 += 5;
break;
}
}
}
if (battlerId == gBattlersCount) // In Generation 3 games, Imprison fails if the user doesn't share any moves with any of the foes
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
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_weightdamagecalculation(void)
{
s32 i;
for (i = 0; sWeightToDamageTable[i] != 0xFFFF; i += 2)
{
if (sWeightToDamageTable[i] > GetPokedexHeightWeight(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), 1))
break;
}
if (sWeightToDamageTable[i] != 0xFFFF)
gDynamicBasePower = sWeightToDamageTable[i + 1];
else
gDynamicBasePower = 120;
gBattlescriptCurrInstr++;
}
static void Cmd_assistattackselect(void)
{
s32 chooseableMovesNo = 0;
struct Pokemon* party;
s32 monId, moveId;
u16 *validMoves = gBattleStruct->assistPossibleMoves;
if (GET_BATTLER_SIDE(gBattlerAttacker) != B_SIDE_PLAYER)
party = gEnemyParty;
else
party = gPlayerParty;
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++)
{
s32 i = 0;
u16 move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId);
if (IsInvalidForSleepTalkOrAssist(move))
continue;
for (; sMovesForbiddenToCopy[i] != ASSIST_FORBIDDEN_END && move != sMovesForbiddenToCopy[i]; i++);
if (sMovesForbiddenToCopy[i] != ASSIST_FORBIDDEN_END)
continue;
if (move == MOVE_NONE)
continue;
validMoves[chooseableMovesNo] = move;
chooseableMovesNo++;
}
}
if (chooseableMovesNo)
{
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gCalledMove = validMoves[((Random() & 0xFF) * chooseableMovesNo) >> 8];
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gBattlescriptCurrInstr += 5;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_trysetmagiccoat(void)
{
gBattlerTarget = gBattlerAttacker;
gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = 1;
if (gCurrentTurnActionNumber == gBattlersCount - 1) // moves last turn
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
gProtectStructs[gBattlerAttacker].bounceMove = TRUE;
gBattlescriptCurrInstr += 5;
}
}
// Snatch
static void Cmd_trysetsnatch(void)
{
gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = 1;
if (gCurrentTurnActionNumber == gBattlersCount - 1) // moves last turn
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
gProtectStructs[gBattlerAttacker].stealMove = 1;
gBattlescriptCurrInstr += 5;
}
}
static void Cmd_trygetintimidatetarget(void)
{
u8 side;
gBattleScripting.battler = gBattleStruct->intimidateBattler;
side = GetBattlerSide(gBattleScripting.battler);
PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gBattleMons[gBattleScripting.battler].ability)
for (;gBattlerTarget < gBattlersCount; gBattlerTarget++)
{
if (GetBattlerSide(gBattlerTarget) == side)
continue;
if (!(gAbsentBattlerFlags & gBitTable[gBattlerTarget]))
break;
}
if (gBattlerTarget >= gBattlersCount)
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
else
gBattlescriptCurrInstr += 5;
}
static void Cmd_switchoutabilities(void)
{
gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]);
switch (gBattleMons[gActiveBattler].ability)
{
case ABILITY_NATURAL_CURE:
gBattleMons[gActiveBattler].status1 = 0;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE,
gBitTable[*(gBattleStruct->battlerPartyIndexes + gActiveBattler)],
sizeof(gBattleMons[gActiveBattler].status1),
&gBattleMons[gActiveBattler].status1);
MarkBattlerForControllerExec(gActiveBattler);
break;
}
gBattlescriptCurrInstr += 2;
}
static void Cmd_jumpifhasnohp(void)
{
gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]);
if (gBattleMons[gActiveBattler].hp == 0)
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2);
else
gBattlescriptCurrInstr += 6;
}
static void Cmd_getsecretpowereffect(void)
{
switch (gBattleTerrain)
{
case BATTLE_TERRAIN_GRASS:
gBattleScripting.moveEffect= MOVE_EFFECT_POISON;
break;
case BATTLE_TERRAIN_LONG_GRASS:
gBattleScripting.moveEffect = MOVE_EFFECT_SLEEP;
break;
case BATTLE_TERRAIN_SAND:
gBattleScripting.moveEffect = MOVE_EFFECT_ACC_MINUS_1;
break;
case BATTLE_TERRAIN_UNDERWATER:
gBattleScripting.moveEffect = MOVE_EFFECT_DEF_MINUS_1;
break;
case BATTLE_TERRAIN_WATER:
gBattleScripting.moveEffect = MOVE_EFFECT_ATK_MINUS_1;
break;
case BATTLE_TERRAIN_POND:
gBattleScripting.moveEffect = MOVE_EFFECT_SPD_MINUS_1;
break;
case BATTLE_TERRAIN_MOUNTAIN:
gBattleScripting.moveEffect = MOVE_EFFECT_CONFUSION;
break;
case BATTLE_TERRAIN_CAVE:
gBattleScripting.moveEffect = MOVE_EFFECT_FLINCH;
break;
default:
gBattleScripting.moveEffect = MOVE_EFFECT_PARALYSIS;
break;
}
gBattlescriptCurrInstr++;
}
static void Cmd_pickup(void)
{
s32 i;
u32 j;
u16 species, heldItem;
u32 ability;
for (i = 0; i < PARTY_SIZE; i++)
{
species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG);
heldItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM);
if (GetMonData(&gPlayerParty[i], MON_DATA_ABILITY_NUM) != ABILITY_NONE)
ability = gSpeciesInfo[species].abilities[1];
else
ability = gSpeciesInfo[species].abilities[0];
if (ability == ABILITY_PICKUP && species != SPECIES_NONE && species != SPECIES_EGG && heldItem == ITEM_NONE && !(Random() % 10))
{
s32 random = Random() % 100;
for (j = 0; j < 15; ++j)
if (sPickupItems[j].chance > random)
break;
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &sPickupItems[j]);
}
}
gBattlescriptCurrInstr++;
}
static void Cmd_docastformchangeanimation(void)
{
gActiveBattler = gBattleScripting.battler;
if (gBattleMons[gActiveBattler].status2 & STATUS2_SUBSTITUTE)
*(&gBattleStruct->formToChangeInto) |= CASTFORM_SUBSTITUTE;
BtlController_EmitBattleAnimation(BUFFER_A, B_ANIM_FORM_CHANGE, &gDisableStructs[gActiveBattler], gBattleStruct->formToChangeInto);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr++;
}
static void Cmd_trycastformdatachange(void)
{
u8 form;
gBattlescriptCurrInstr++;
form = CastformDataTypeChange(gBattleScripting.battler);
if (form)
{
BattleScriptPushCursorAndCallback(BattleScript_CastformChange);
*(&gBattleStruct->formToChangeInto) = form - 1;
}
}
// Water and Mud Sport
static void Cmd_settypebasedhalvers(void)
{
bool8 worked = FALSE;
if (gMovesInfo[gCurrentMove].effect == EFFECT_MUD_SPORT)
{
if (!(gStatuses4[gBattlerAttacker] & STATUS4_MUD_SPORT))
{
gStatuses4[gBattlerAttacker] |= STATUS4_MUD_SPORT;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEAKEN_ELECTRIC;
worked = TRUE;
}
}
else // Water Sport
{
if (!(gStatuses4[gBattlerAttacker] & STATUS4_WATER_SPORT))
{
gStatuses4[gBattlerAttacker] |= STATUS4_WATER_SPORT;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEAKEN_FIRE;
worked = TRUE;
}
}
if (worked)
gBattlescriptCurrInstr += 5;
else
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
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)
{
u16 *usedHeldItem;
gActiveBattler = gBattlerAttacker;
usedHeldItem = &gBattleStruct->usedHeldItems[gActiveBattler][GetBattlerSide(gActiveBattler)];
if (*usedHeldItem != ITEM_NONE && gBattleMons[gActiveBattler].item == ITEM_NONE)
{
gLastUsedItem = *usedHeldItem;
*usedHeldItem = ITEM_NONE;
gBattleMons[gActiveBattler].item = gLastUsedItem;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gActiveBattler].item), &gBattleMons[gActiveBattler].item);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr += 5;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_settypetoterrain(void)
{
if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, sTerrainToType[gBattleTerrain]))
{
SET_BATTLER_TYPE(gBattlerAttacker, sTerrainToType[gBattleTerrain]);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, sTerrainToType[gBattleTerrain]);
gBattlescriptCurrInstr += 5;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
// Unused
static void Cmd_pursuitdoubles(void)
{
gActiveBattler = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_FLANK);
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& !(gAbsentBattlerFlags & gBitTable[gActiveBattler])
&& gChosenActionByBattler[gActiveBattler] == B_ACTION_USE_MOVE
&& gChosenMoveByBattler[gActiveBattler] == MOVE_PURSUIT)
{
gActionsByTurnOrder[gActiveBattler] = B_ACTION_TRY_FINISH;
gCurrentMove = MOVE_PURSUIT;
gBattlescriptCurrInstr += 5;
gBattleScripting.animTurn = 1;
gBattlerAttacker = gActiveBattler;
}
else
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
}
static void Cmd_snatchsetbattlers(void)
{
gEffectBattler = gBattlerAttacker;
if (gBattlerAttacker == gBattlerTarget)
gBattlerAttacker = gBattlerTarget = gBattleScripting.battler;
else
gBattlerTarget = gBattleScripting.battler;
gBattleScripting.battler = gEffectBattler;
gBattlescriptCurrInstr++;
}
// Brick Break
static void Cmd_removelightscreenreflect(void)
{
u8 opposingSide = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE;
if (gSideTimers[opposingSide].reflectTimer || gSideTimers[opposingSide].lightscreenTimer)
{
gSideStatuses[opposingSide] &= ~SIDE_STATUS_REFLECT;
gSideStatuses[opposingSide] &= ~SIDE_STATUS_LIGHTSCREEN;
gSideTimers[opposingSide].reflectTimer = 0;
gSideTimers[opposingSide].lightscreenTimer = 0;
gBattleScripting.animTurn = 1;
gBattleScripting.animTargetsHit = 1;
}
else
{
gBattleScripting.animTurn = 0;
gBattleScripting.animTargetsHit = 0;
}
gBattlescriptCurrInstr++;
}
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)
{
u8 ballMultiplier = 0;
if (gBattleControllerExecFlags)
return;
gActiveBattler = gBattlerAttacker;
gBattlerTarget = gBattlerAttacker ^ BIT_SIDE;
if (gBattleTypeFlags & BATTLE_TYPE_GHOST)
{
BtlController_EmitBallThrowAnim(BUFFER_A, BALL_GHOST_DODGE);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr = BattleScript_GhostBallDodge;
}
else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
{
BtlController_EmitBallThrowAnim(BUFFER_A, BALL_TRAINER_BLOCK);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr = BattleScript_TrainerBallBlock;
}
else if (gBattleTypeFlags & (BATTLE_TYPE_POKEDUDE | BATTLE_TYPE_OLD_MAN_TUTORIAL))
{
BtlController_EmitBallThrowAnim(BUFFER_A, BALL_3_SHAKES_SUCCESS);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr = BattleScript_OldMan_Pokedude_CaughtMessage;
}
else
{
u32 odds;
u8 catchRate;
if (gLastUsedItem == ITEM_SAFARI_BALL)
catchRate = gBattleStruct->safariCatchFactor * 1275 / 100;
else
catchRate = gSpeciesInfo[gBattleMons[gBattlerTarget].species].catchRate;
if (gLastUsedItem > ITEM_SAFARI_BALL)
{
switch (gLastUsedItem)
{
case ITEM_NET_BALL:
if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_WATER) || IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_BUG))
ballMultiplier = 30;
else
ballMultiplier = 10;
break;
case ITEM_DIVE_BALL:
if (GetCurrentMapType() == MAP_TYPE_UNDERWATER)
ballMultiplier = 35;
else
ballMultiplier = 10;
break;
case ITEM_NEST_BALL:
if (gBattleMons[gBattlerTarget].level < 40)
{
ballMultiplier = 40 - gBattleMons[gBattlerTarget].level;
if (ballMultiplier <= 9)
ballMultiplier = 10;
}
else
{
ballMultiplier = 10;
}
break;
case ITEM_REPEAT_BALL:
if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), FLAG_GET_CAUGHT))
ballMultiplier = 30;
else
ballMultiplier = 10;
break;
case ITEM_TIMER_BALL:
ballMultiplier = gBattleResults.battleTurnCounter + 10;
if (ballMultiplier > 40)
ballMultiplier = 40;
break;
case ITEM_LUXURY_BALL:
case ITEM_PREMIER_BALL:
ballMultiplier = 10;
break;
}
}
else
ballMultiplier = sBallCatchBonuses[gLastUsedItem - ITEM_ULTRA_BALL];
odds = (catchRate * ballMultiplier / 10)
* (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))
odds = (odds * 15) / 10;
if (gLastUsedItem != ITEM_SAFARI_BALL)
{
if (gLastUsedItem == ITEM_MASTER_BALL)
{
gBattleResults.usedMasterBall = TRUE;
}
else
{
if (gBattleResults.catchAttempts[gLastUsedItem - ITEM_ULTRA_BALL] < 255)
gBattleResults.catchAttempts[gLastUsedItem - ITEM_ULTRA_BALL]++;
}
}
if (odds > 254) // mon caught
{
BtlController_EmitBallThrowAnim(BUFFER_A, BALL_3_SHAKES_SUCCESS);
MarkBattlerForControllerExec(gActiveBattler);
gBattlescriptCurrInstr = BattleScript_SuccessBallThrow;
SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem);
if (CalculatePlayerPartyCount() == PARTY_SIZE)
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
else
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
}
else // mon may be caught, calculate shakes
{
u8 shakes;
odds = Sqrt(Sqrt(16711680 / odds));
odds = 1048560 / odds;
for (shakes = 0; shakes < BALL_3_SHAKES_SUCCESS && Random() < odds; shakes++);
if (gLastUsedItem == ITEM_MASTER_BALL)
shakes = BALL_3_SHAKES_SUCCESS; // why calculate the shakes before that check?
BtlController_EmitBallThrowAnim(BUFFER_A, shakes);
MarkBattlerForControllerExec(gActiveBattler);
if (shakes == BALL_3_SHAKES_SUCCESS) // mon caught, copy of the code above
{
gBattlescriptCurrInstr = BattleScript_SuccessBallThrow;
SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem);
if (CalculatePlayerPartyCount() == PARTY_SIZE)
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
else
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
}
else // not caught
{
gBattleCommunication[MULTISTRING_CHOOSER] = shakes;
gBattlescriptCurrInstr = BattleScript_ShakeBallThrow;
}
}
}
}
static void Cmd_givecaughtmon(void)
{
if (GiveMonToPlayer(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]]) != 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[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_NICKNAME, gStringVar2);
}
else
{
StringCopy(gStringVar1, GetBoxNamePtr(VarGet(VAR_PC_BOX_TO_SEND_MON))); // box the mon was sent to
GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], 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_BILLS_PC or B_MSG_BILLS_BOX_FULL
if (FlagGet(FLAG_SYS_NOT_SOMEONES_PC))
gBattleCommunication[MULTISTRING_CHOOSER]++;
}
gBattleResults.caughtMonSpecies = gBattleMons[gBattlerAttacker ^ BIT_SIDE].species;
GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_NICKNAME, gBattleResults.caughtMonNick);
gBattlescriptCurrInstr++;
}
static void Cmd_trysetcaughtmondexflags(void)
{
u16 species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, NULL);
u32 personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY, NULL);
if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT))
{
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
else
{
HandleSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_SET_CAUGHT, personality);
gBattlescriptCurrInstr += 5;
}
}
static void Cmd_displaydexinfo(void)
{
u16 species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, NULL);
switch (gBattleCommunication[0])
{
case 0:
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_WHITE);
gBattleCommunication[0]++;
break;
case 1:
if (!gPaletteFade.active)
{
FreeAllWindowBuffers();
gBattleCommunication[TASK_ID] = DexScreen_RegisterMonToPokedex(species);
gBattleCommunication[0]++;
}
break;
case 2:
if (!gPaletteFade.active
&& gMain.callback2 == BattleMainCB2
&& !gTasks[gBattleCommunication[TASK_ID]].isActive)
{
CpuFill32(0, (void *)VRAM, VRAM_SIZE);
SetVBlankCallback(VBlankCB_Battle);
gBattleCommunication[0]++;
}
break;
case 3:
InitBattleBgsVideo();
LoadBattleTextboxAndBackground();
gBattle_BG3_X = 256;
gBattleCommunication[0]++;
break;
case 4:
if (!IsDma3ManagerBusyWithBgCopy())
{
CreateMonPicSprite_HandleDeoxys(species,
gBattleMons[B_POSITION_OPPONENT_LEFT].otId,
gBattleMons[B_POSITION_OPPONENT_LEFT].personality,
TRUE,
120,
64,
0,
0xFFFF);
CpuFill32(0, gPlttBufferFaded, BG_PLTT_SIZE);
BeginNormalPaletteFade(0x1FFFF, 0, 16, 0, RGB_BLACK);
ShowBg(0);
ShowBg(3);
gBattleCommunication[0]++;
}
break;
case 5:
if (!gPaletteFade.active)
gBattlescriptCurrInstr++;
break;
}
}
void HandleBattleWindow(u8 xStart, u8 yStart, u8 xEnd, u8 yEnd, u8 flags)
{
s32 destY, destX;
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;
if (flags & WINDOW_BG1)
CopyToBgTilemapBufferRect_ChangePalette(1, &var, destX, destY, 1, 1, 0x11);
else
CopyToBgTilemapBufferRect_ChangePalette(0, &var, destX, destY, 1, 1, 0x11);
}
}
CopyBgTilemapBufferToVram(1);
}
void BattleCreateYesNoCursorAt(void)
{
u16 src[2];
src[0] = 1;
src[1] = 2;
CopyToBgTilemapBufferRect_ChangePalette(0, src, 0x18, 9 + (2 * gBattleCommunication[1]), 1, 2, 0x11);
CopyBgTilemapBufferToVram(0);
}
void BattleDestroyYesNoCursorAt(void)
{
u16 src[2];
src[0] = 32;
src[1] = 32;
CopyToBgTilemapBufferRect_ChangePalette(0, src, 0x18, 9 + (2 * gBattleCommunication[1]), 1, 2, 0x11);
CopyBgTilemapBufferToVram(0);
}
static void Cmd_trygivecaughtmonnick(void)
{
switch (gBattleCommunication[MULTIUSE_STATE])
{
case 0:
HandleBattleWindow(23, 8, 29, 13, 0);
BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO);
gBattleCommunication[MULTIUSE_STATE]++;
gBattleCommunication[CURSOR_POSITION] = 0;
BattleCreateYesNoCursorAt();
break;
case 1:
if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0)
{
PlaySE(SE_SELECT);
BattleDestroyYesNoCursorAt();
gBattleCommunication[CURSOR_POSITION] = 0;
BattleCreateYesNoCursorAt();
}
if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0)
{
PlaySE(SE_SELECT);
BattleDestroyYesNoCursorAt();
gBattleCommunication[CURSOR_POSITION] = 1;
BattleCreateYesNoCursorAt();
}
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[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_NICKNAME, gBattleStruct->caughtMonNick);
FreeAllWindowBuffers();
DoNamingScreen(NAMING_SCREEN_CAUGHT_MON, gBattleStruct->caughtMonNick,
GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_SPECIES),
GetMonGender(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]]),
GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_PERSONALITY, NULL),
BattleMainCB2);
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 3:
if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active)
{
SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_NICKNAME, gBattleStruct->caughtMonNick);
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
}
break;
case 4:
if (CalculatePlayerPartyCount() == PARTY_SIZE)
gBattlescriptCurrInstr += 5;
else
gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1);
break;
}
}
static void Cmd_subattackerhpbydmg(void)
{
gBattleMons[gBattlerAttacker].hp -= gBattleMoveDamage;
gBattlescriptCurrInstr++;
}
static void Cmd_removeattackerstatus1(void)
{
gBattleMons[gBattlerAttacker].status1 = 0;
gBattlescriptCurrInstr++;
}
static void Cmd_finishaction(void)
{
gCurrentActionFuncId = B_ACTION_FINISHED;
}
static void Cmd_finishturn(void)
{
gCurrentActionFuncId = B_ACTION_FINISHED;
gCurrentTurnActionNumber = gBattlersCount;
}
static void Cmd_callnative(void)
{
CMD_ARGS(void (*func)(void));
void (*func)(void) = cmd->func;
func();
}
void BS_GetBattlerSide(void)
{
NATIVE_ARGS(u8 battler);
gBattleCommunication[0] = GetBattlerSide(GetBattlerForBattleScript(cmd->battler));
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_ItemRestoreHP(void)
{
NATIVE_ARGS(const u8 *alreadyMaxHpInstr);
u16 healAmount;
u32 battler = MAX_BATTLERS_COUNT;
u32 healParam = ItemId_GetEffect(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;
if (hp == maxHP)
{
gBattlescriptCurrInstr = cmd->alreadyMaxHpInstr;
}
else
{
// 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;
gBattleScripting.battler = battler;
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_SPECIES));
// Heal is applied as move damage if battler is active.
if (battler != MAX_BATTLERS_COUNT && hp != 0)
{
gBattleMoveDamage = -healAmount;
gBattlescriptCurrInstr = cmd->nextInstr;
}
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];
gBattleCommunication[MULTIUSE_STATE] = TRUE;
}
gBattlescriptCurrInstr = BattleScript_ItemRestoreHP_Party;
}
}
}
void BS_ItemCureStatus(void)
{
NATIVE_ARGS(const u8 *noStatusInstr);
u32 battler = gBattlerAttacker;
u32 side = GetBattlerSide(gBattlerAttacker);
u32 previousStatus2 = 0;
bool32 statusChanged = FALSE;
struct Pokemon *party = GetSideParty(side);
// Heal Status2 conditions if battler is active.
if (gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[gBattlerAttacker])
{
previousStatus2 = gBattleMons[battler].status2;
gBattleMons[gBattlerAttacker].status2 &= ~GetItemStatus2Mask(gLastUsedItem);
}
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)])
{
battler = BATTLE_PARTNER(gBattlerAttacker);
previousStatus2 = gBattleMons[battler].status2;
gBattleMons[battler].status2 &= ~GetItemStatus2Mask(gLastUsedItem);
}
if (previousStatus2 != gBattleMons[battler].status2)
statusChanged = TRUE;
// Heal Status1 conditions.
if (!HealStatusConditions(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], GetItemStatus1Mask(gLastUsedItem), battler))
{
statusChanged = TRUE;
if (GetItemStatus1Mask(gLastUsedItem) & STATUS1_SLEEP)
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
if (GetItemStatus2Mask(gLastUsedItem) & STATUS2_CONFUSION)
gStatuses4[battler] &= ~STATUS4_INFINITE_CONFUSION;
}
if (statusChanged)
{
gBattleScripting.battler = battler;
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_SPECIES));
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattlescriptCurrInstr = cmd->noStatusInstr;
}
}
void BS_ItemIncreaseStat(void)
{
NATIVE_ARGS();
u16 statId = ItemId_GetEffect(gLastUsedItem)[1];
u16 stages = ItemId_GetHoldEffectParam(gLastUsedItem);
SET_STATCHANGER(statId, stages, FALSE);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_ItemRestorePP(void)
{
NATIVE_ARGS();
const u8 *effect = ItemId_GetEffect(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]];
u16 species = GetMonData(mon, MON_DATA_SPECIES);
// // 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;
}
}
}
gBattleScripting.battler = battler;
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, species);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_RunStatChangeItems(void)
{
NATIVE_ARGS(u8 battler);
// Change instruction before calling ItemBattleEffects.
gBattlescriptCurrInstr = cmd->nextInstr;
ItemBattleEffects(ITEMEFFECT_STATS_CHANGED, GetBattlerForBattleScript(cmd->battler), FALSE); // TODO: update
}
bool32 IsMoveAffectedByParentalBond(u32 move, u32 battler)
{
if (move != MOVE_NONE && move != MOVE_UNAVAILABLE && move != MOVE_STRUGGLE
&& !gMovesInfo[move].parentalBondBanned
&& gMovesInfo[move].category != DAMAGE_CATEGORY_STATUS
&& gMovesInfo[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;
}
bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, u32 move)
{
if (!(gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE))
return FALSE;
else if (gMovesInfo[move].ignoresSubstitute)
return FALSE;
else if (GetBattlerAbility(battlerAtk) == ABILITY_INFILTRATOR)
return FALSE;
else
return TRUE;
}
bool32 DoesDisguiseBlockMove(u32 battler, u32 move)
{
// TODO: Totems
if (!(gBattleMons[battler].species == SPECIES_MIMIKYU_DISGUISED /* || gBattleMons[battler].species == SPECIES_MIMIKYU_TOTEM_DISGUISED */)
|| gBattleMons[battler].status2 & STATUS2_TRANSFORMED
|| (!gProtectStructs[battler].confusionSelfDmg && (IS_MOVE_STATUS(move) || gHitMarker & HITMARKER_PASSIVE_DAMAGE))
|| gHitMarker & HITMARKER_IGNORE_DISGUISE
|| GetBattlerAbility(battler) != ABILITY_DISGUISE)
return FALSE;
else
return TRUE;
}
static bool8 IsFinalStrikeEffect(u16 move)
{
u32 i;
u16 moveEffect = gMovesInfo[move].effect;
for (i = 0; i < ARRAY_COUNT(sFinalStrikeOnlyEffects); i++)
{
if (moveEffect == sFinalStrikeOnlyEffects[i])
return TRUE;
}
return FALSE;
}
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_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;
}
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;
}
static 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;
}
}
static bool8 CanBurnHitThaw(u16 move)
{
u8 i;
if (B_BURN_HIT_THAW >= GEN_6)
{
for (i = 0; i < gMovesInfo[move].numAdditionalEffects; i++)
{
if (gMovesInfo[move].additionalEffects[i].moveEffect == MOVE_EFFECT_BURN)
return TRUE;
}
}
return FALSE;
}
static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount)
{
u32 i;
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER
&& !(gBattleTypeFlags & (BATTLE_TYPE_LINK
| BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_BATTLE_TOWER
| BATTLE_TYPE_TRAINER_TOWER)))
{
const struct Evolution *evolutions = GetSpeciesEvolutions(gBattleMons[gBattlerAttacker].species);
if (evolutions == NULL)
return;
for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++)
{
if (SanitizeSpeciesId(evolutions[i].targetSpecies) == SPECIES_NONE)
continue;
if (evolutions[i].method == evolutionMethod)
{
// We only have 9 bits to use
u16 val = min(511, GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER) + upAmount);
// Reset progress if you faint for the recoil method.
if (gBattleMons[gBattlerAttacker].hp == 0 && (evolutionMethod == EVO_LEVEL_RECOIL_DAMAGE_MALE || evolutionMethod == EVO_LEVEL_RECOIL_DAMAGE_FEMALE))
val = 0;
SetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]], MON_DATA_EVOLUTION_TRACKER, &val);
return;
}
}
}
}
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;
}
}
void BS_HandlePrimalReversion(void)
{
NATIVE_ARGS(u8 battler, u8 caseId);
u8 battler = gActiveBattler = GetBattlerForBattleScript(cmd->battler);
HandleScriptMegaPrimalBurst(cmd->caseId, battler, HANDLE_TYPE_PRIMAL_REVERSION);
gBattlescriptCurrInstr = cmd->nextInstr;
}