#include "global.h" #include "battle.h" #include "battle_message.h" #include "battle_anim.h" #include "battle_ai_script_commands.h" #include "battle_scripts.h" #include "item.h" #include "util.h" #include "pokemon.h" #include "random.h" #include "battle_controllers.h" #include "battle_interface.h" #include "text.h" #include "sound.h" #include "pokedex.h" #include "recorded_battle.h" #include "window.h" #include "reshow_battle_screen.h" #include "main.h" #include "palette.h" #include "money.h" #include "bg.h" #include "string_util.h" #include "pokemon_icon.h" #include "m4a.h" #include "mail.h" #include "event_data.h" #include "pokemon_storage_system.h" #include "task.h" #include "naming_screen.h" #include "battle_setup.h" #include "overworld.h" #include "party_menu.h" #include "battle_arena.h" #include "battle_pike.h" #include "battle_pyramid.h" #include "field_specials.h" #include "pokemon_summary_screen.h" #include "pokenav.h" #include "menu_specialized.h" #include "data.h" #include "constants/abilities.h" #include "constants/battle_anim.h" #include "constants/battle_move_effects.h" #include "constants/battle_string_ids.h" #include "constants/hold_effects.h" #include "constants/items.h" #include "constants/map_types.h" #include "constants/moves.h" #include "constants/party_menu.h" #include "constants/rgb.h" #include "constants/songs.h" #include "constants/trainers.h" extern const u8* const gBattleScriptsForMoveEffects[]; #define DEFENDER_IS_PROTECTED ((gProtectStructs[gBattlerTarget].protected) && (gBattleMoves[gCurrentMove].flags & FLAG_PROTECT_AFFECTED)) #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, u8 statId, u8, const u8* BS_ptr); static bool32 IsMonGettingExpSentOut(void); static void InitLevelUpBanner(void); static bool8 SlideInLevelUpBanner(void); static bool8 SlideOutLevelUpBanner(void); static void DrawLevelUpWindow1(void); static void DrawLevelUpWindow2(void); static void PutMonIconOnLvlUpBanner(void); static void DrawLevelUpBannerText(void); static void SpriteCB_MonIconOnLvlUpBanner(struct Sprite* sprite); static 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_adjustnormaldamage(void); static void Cmd_adjustnormaldamage2(void); static void Cmd_attackanimation(void); static void Cmd_waitanimation(void); static void Cmd_healthbarupdate(void); static void Cmd_datahpupdate(void); static void Cmd_critmessage(void); static void Cmd_effectivenesssound(void); static void Cmd_resultmessage(void); static void Cmd_printstring(void); static void Cmd_printselectionstring(void); static void Cmd_waitmessage(void); static void Cmd_printfromtable(void); static void Cmd_printselectionstringfromtable(void); static void Cmd_seteffectwithchance(void); static void Cmd_seteffectprimary(void); static void Cmd_seteffectsecondary(void); static void Cmd_clearstatusfromeffect(void); static void Cmd_tryfaintmon(void); static void Cmd_dofaintanimation(void); static void Cmd_cleareffectsonfaint(void); static void Cmd_jumpifstatus(void); static void Cmd_jumpifstatus2(void); static void Cmd_jumpifability(void); static void Cmd_jumpifsideaffecting(void); static void Cmd_jumpifstat(void); static void Cmd_jumpifstatus3condition(void); static void Cmd_jumpiftype(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_jumpifaffectedbyprotect(void); static void Cmd_call(void); static void Cmd_jumpiftype2(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_typecalc2(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_adjustsetdamage(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_recordlastability(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_faintifabilitynotdamp(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_jumpifattackandspecialattackcannotfall(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_trysetroots(void); static void Cmd_doubledamagedealtifdamaged(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_trysetgrudge(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_setweatherballtype(void); static void Cmd_tryrecycleitem(void); static void Cmd_settypetoterrain(void); static void Cmd_pursuitdoubles(void); static void Cmd_snatchsetbattlers(void); static void Cmd_removelightscreenreflect(void); static void Cmd_handleballthrow(void); static void Cmd_givecaughtmon(void); static void Cmd_trysetcaughtmondexflags(void); static void Cmd_displaydexinfo(void); static void Cmd_trygivecaughtmonnick(void); static void Cmd_subattackerhpbydmg(void); static void Cmd_removeattackerstatus1(void); static void Cmd_finishaction(void); static void Cmd_finishturn(void); static void Cmd_trainerslideout(void); void (* const gBattleScriptingCommandsTable[])(void) = { Cmd_attackcanceler, //0x0 Cmd_accuracycheck, //0x1 Cmd_attackstring, //0x2 Cmd_ppreduce, //0x3 Cmd_critcalc, //0x4 Cmd_damagecalc, //0x5 Cmd_typecalc, //0x6 Cmd_adjustnormaldamage, //0x7 Cmd_adjustnormaldamage2, //0x8 Cmd_attackanimation, //0x9 Cmd_waitanimation, //0xA Cmd_healthbarupdate, //0xB Cmd_datahpupdate, //0xC Cmd_critmessage, //0xD Cmd_effectivenesssound, //0xE Cmd_resultmessage, //0xF Cmd_printstring, //0x10 Cmd_printselectionstring, //0x11 Cmd_waitmessage, //0x12 Cmd_printfromtable, //0x13 Cmd_printselectionstringfromtable, //0x14 Cmd_seteffectwithchance, //0x15 Cmd_seteffectprimary, //0x16 Cmd_seteffectsecondary, //0x17 Cmd_clearstatusfromeffect, //0x18 Cmd_tryfaintmon, //0x19 Cmd_dofaintanimation, //0x1A Cmd_cleareffectsonfaint, //0x1B Cmd_jumpifstatus, //0x1C Cmd_jumpifstatus2, //0x1D Cmd_jumpifability, //0x1E Cmd_jumpifsideaffecting, //0x1F Cmd_jumpifstat, //0x20 Cmd_jumpifstatus3condition, //0x21 Cmd_jumpiftype, //0x22 Cmd_getexp, //0x23 Cmd_checkteamslost, //0x24 Cmd_movevaluescleanup, //0x25 Cmd_setmultihit, //0x26 Cmd_decrementmultihit, //0x27 Cmd_goto, //0x28 Cmd_jumpifbyte, //0x29 Cmd_jumpifhalfword, //0x2A Cmd_jumpifword, //0x2B Cmd_jumpifarrayequal, //0x2C Cmd_jumpifarraynotequal, //0x2D Cmd_setbyte, //0x2E Cmd_addbyte, //0x2F Cmd_subbyte, //0x30 Cmd_copyarray, //0x31 Cmd_copyarraywithindex, //0x32 Cmd_orbyte, //0x33 Cmd_orhalfword, //0x34 Cmd_orword, //0x35 Cmd_bicbyte, //0x36 Cmd_bichalfword, //0x37 Cmd_bicword, //0x38 Cmd_pause, //0x39 Cmd_waitstate, //0x3A Cmd_healthbar_update, //0x3B Cmd_return, //0x3C Cmd_end, //0x3D Cmd_end2, //0x3E Cmd_end3, //0x3F Cmd_jumpifaffectedbyprotect, //0x40 Cmd_call, //0x41 Cmd_jumpiftype2, //0x42 Cmd_jumpifabilitypresent, //0x43 Cmd_endselectionscript, //0x44 Cmd_playanimation, //0x45 Cmd_playanimation_var, //0x46 Cmd_setgraphicalstatchangevalues, //0x47 Cmd_playstatchangeanimation, //0x48 Cmd_moveend, //0x49 Cmd_typecalc2, //0x4A Cmd_returnatktoball, //0x4B Cmd_getswitchedmondata, //0x4C Cmd_switchindataupdate, //0x4D Cmd_switchinanim, //0x4E Cmd_jumpifcantswitch, //0x4F Cmd_openpartyscreen, //0x50 Cmd_switchhandleorder, //0x51 Cmd_switchineffects, //0x52 Cmd_trainerslidein, //0x53 Cmd_playse, //0x54 Cmd_fanfare, //0x55 Cmd_playfaintcry, //0x56 Cmd_endlinkbattle, //0x57 Cmd_returntoball, //0x58 Cmd_handlelearnnewmove, //0x59 Cmd_yesnoboxlearnmove, //0x5A Cmd_yesnoboxstoplearningmove, //0x5B Cmd_hitanimation, //0x5C Cmd_getmoneyreward, //0x5D Cmd_updatebattlermoves, //0x5E Cmd_swapattackerwithtarget, //0x5F Cmd_incrementgamestat, //0x60 Cmd_drawpartystatussummary, //0x61 Cmd_hidepartystatussummary, //0x62 Cmd_jumptocalledmove, //0x63 Cmd_statusanimation, //0x64 Cmd_status2animation, //0x65 Cmd_chosenstatusanimation, //0x66 Cmd_yesnobox, //0x67 Cmd_cancelallactions, //0x68 Cmd_adjustsetdamage, //0x69 Cmd_removeitem, //0x6A Cmd_atknameinbuff1, //0x6B Cmd_drawlvlupbox, //0x6C Cmd_resetsentmonsvalue, //0x6D Cmd_setatktoplayer0, //0x6E Cmd_makevisible, //0x6F Cmd_recordlastability, //0x70 Cmd_buffermovetolearn, //0x71 Cmd_jumpifplayerran, //0x72 Cmd_hpthresholds, //0x73 Cmd_hpthresholds2, //0x74 Cmd_useitemonopponent, //0x75 Cmd_various, //0x76 Cmd_setprotectlike, //0x77 Cmd_faintifabilitynotdamp, //0x78 Cmd_setatkhptozero, //0x79 Cmd_jumpifnexttargetvalid, //0x7A Cmd_tryhealhalfhealth, //0x7B Cmd_trymirrormove, //0x7C Cmd_setrain, //0x7D Cmd_setreflect, //0x7E Cmd_setseeded, //0x7F Cmd_manipulatedamage, //0x80 Cmd_trysetrest, //0x81 Cmd_jumpifnotfirstturn, //0x82 Cmd_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_jumpifattackandspecialattackcannotfall, //0xC9 Cmd_setforcedtarget, //0xCA Cmd_setcharge, //0xCB Cmd_callterrainattack, //0xCC Cmd_cureifburnedparalysedorpoisoned, //0xCD Cmd_settorment, //0xCE Cmd_jumpifnodamage, //0xCF Cmd_settaunt, //0xD0 Cmd_trysethelpinghand, //0xD1 Cmd_tryswapitems, //0xD2 Cmd_trycopyability, //0xD3 Cmd_trywish, //0xD4 Cmd_trysetroots, //0xD5 Cmd_doubledamagedealtifdamaged, //0xD6 Cmd_setyawn, //0xD7 Cmd_setdamagetohealthdifference, //0xD8 Cmd_scaledamagebyhealthratio, //0xD9 Cmd_tryswapabilities, //0xDA Cmd_tryimprison, //0xDB Cmd_trysetgrudge, //0xDC 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_setweatherballtype, //0xE9 Cmd_tryrecycleitem, //0xEA Cmd_settypetoterrain, //0xEB Cmd_pursuitdoubles, //0xEC Cmd_snatchsetbattlers, //0xED Cmd_removelightscreenreflect, //0xEE Cmd_handleballthrow, //0xEF Cmd_givecaughtmon, //0xF0 Cmd_trysetcaughtmondexflags, //0xF1 Cmd_displaydexinfo, //0xF2 Cmd_trygivecaughtmonnick, //0xF3 Cmd_subattackerhpbydmg, //0xF4 Cmd_removeattackerstatus1, //0xF5 Cmd_finishaction, //0xF6 Cmd_finishturn, //0xF7 Cmd_trainerslideout //0xF8 }; struct StatFractions { u8 dividend; u8 divisor; }; static const struct StatFractions sAccuracyStageRatios[] = { { 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 }; // The chance is 1/N for each stage. static const u16 sCriticalHitChance[] = {16, 8, 4, 3, 2}; 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"); // unused static const u8 sRubyLevelUpStatBoxStats[] = { MON_DATA_MAX_HP, MON_DATA_SPATK, MON_DATA_ATK, MON_DATA_SPDEF, MON_DATA_DEF, MON_DATA_SPEED }; static const struct OamData sOamData_MonIconOnLvlUpBanner = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(32x32), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(32x32), .tileNum = 0, .priority = 0, .paletteNum = 0, .affineParam = 0, }; static const struct SpriteTemplate sSpriteTemplate_MonIconOnLvlUpBanner = { .tileTag = TAG_LVLUP_BANNER_MON_ICON, .paletteTag = TAG_LVLUP_BANNER_MON_ICON, .oam = &sOamData_MonIconOnLvlUpBanner, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_MonIconOnLvlUpBanner }; static const u16 sProtectSuccessRates[] = {USHRT_MAX, USHRT_MAX / 2, USHRT_MAX / 4, USHRT_MAX / 8}; #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 sNaturePowerMoves[] = { [BATTLE_TERRAIN_GRASS] = MOVE_STUN_SPORE, [BATTLE_TERRAIN_LONG_GRASS] = MOVE_RAZOR_LEAF, [BATTLE_TERRAIN_SAND] = MOVE_EARTHQUAKE, [BATTLE_TERRAIN_UNDERWATER] = MOVE_HYDRO_PUMP, [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 }; // format: min. weight (hectograms), base power static const u16 sWeightToDamageTable[] = { 100, 20, 250, 40, 500, 60, 1000, 80, 2000, 100, 0xFFFF, 0xFFFF }; static const u16 sPickupItems[] = { ITEM_POTION, ITEM_ANTIDOTE, ITEM_SUPER_POTION, ITEM_GREAT_BALL, ITEM_REPEL, ITEM_ESCAPE_ROPE, ITEM_X_ATTACK, ITEM_FULL_HEAL, ITEM_ULTRA_BALL, ITEM_HYPER_POTION, ITEM_RARE_CANDY, ITEM_PROTEIN, ITEM_REVIVE, ITEM_HP_UP, ITEM_FULL_RESTORE, ITEM_MAX_REVIVE, ITEM_PP_UP, ITEM_MAX_ELIXIR, }; static const u16 sRarePickupItems[] = { ITEM_HYPER_POTION, ITEM_NUGGET, ITEM_KINGS_ROCK, ITEM_FULL_RESTORE, ITEM_ETHER, ITEM_WHITE_HERB, ITEM_TM44_REST, ITEM_ELIXIR, ITEM_TM01_FOCUS_PUNCH, ITEM_LEFTOVERS, ITEM_TM26_EARTHQUAKE, }; static const u8 sPickupProbabilities[] = { 30, 40, 50, 60, 70, 80, 90, 94, 98 }; 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 }; // In Battle Palace, moves are chosen based on the pokemons nature rather than by the player // Moves are grouped into "Attack", "Defense", or "Support" (see PALACE_MOVE_GROUP_*) // Each nature has a certain percent chance of selecting a move from a particular group // and a separate percent chance for each group when below 50% HP // The table below doesn't list percentages for Support because you can subtract the other two // Support percentages are listed in comments off to the side instead #define PALACE_STYLE(atk, def, atkLow, defLow) {atk, atk + def, atkLow, atkLow + defLow} const ALIGNED(4) u8 gBattlePalaceNatureToMoveGroupLikelihood[NUM_NATURES][4] = { [NATURE_HARDY] = PALACE_STYLE(61, 7, 61, 7), // 32% support >= 50% HP, 32% support < 50% HP [NATURE_LONELY] = PALACE_STYLE(20, 25, 84, 8), // 55%, 8% [NATURE_BRAVE] = PALACE_STYLE(70, 15, 32, 60), // 15%, 8% [NATURE_ADAMANT] = PALACE_STYLE(38, 31, 70, 15), // 31%, 15% [NATURE_NAUGHTY] = PALACE_STYLE(20, 70, 70, 22), // 10%, 8% [NATURE_BOLD] = PALACE_STYLE(30, 20, 32, 58), // 50%, 10% [NATURE_DOCILE] = PALACE_STYLE(56, 22, 56, 22), // 22%, 22% [NATURE_RELAXED] = PALACE_STYLE(25, 15, 75, 15), // 60%, 10% [NATURE_IMPISH] = PALACE_STYLE(69, 6, 28, 55), // 25%, 17% [NATURE_LAX] = PALACE_STYLE(35, 10, 29, 6), // 55%, 65% [NATURE_TIMID] = PALACE_STYLE(62, 10, 30, 20), // 28%, 50% [NATURE_HASTY] = PALACE_STYLE(58, 37, 88, 6), // 5%, 6% [NATURE_SERIOUS] = PALACE_STYLE(34, 11, 29, 11), // 55%, 60% [NATURE_JOLLY] = PALACE_STYLE(35, 5, 35, 60), // 60%, 5% [NATURE_NAIVE] = PALACE_STYLE(56, 22, 56, 22), // 22%, 22% [NATURE_MODEST] = PALACE_STYLE(35, 45, 34, 60), // 20%, 6% [NATURE_MILD] = PALACE_STYLE(44, 50, 34, 6), // 6%, 60% [NATURE_QUIET] = PALACE_STYLE(56, 22, 56, 22), // 22%, 22% [NATURE_BASHFUL] = PALACE_STYLE(30, 58, 30, 58), // 12%, 12% [NATURE_RASH] = PALACE_STYLE(30, 13, 27, 6), // 57%, 67% [NATURE_CALM] = PALACE_STYLE(40, 50, 25, 62), // 10%, 13% [NATURE_GENTLE] = PALACE_STYLE(18, 70, 90, 5), // 12%, 5% [NATURE_SASSY] = PALACE_STYLE(88, 6, 22, 20), // 6%, 58% [NATURE_CAREFUL] = PALACE_STYLE(42, 50, 42, 5), // 8%, 53% [NATURE_QUIRKY] = PALACE_STYLE(56, 22, 56, 22) // 22%, 22% }; static const u8 sBattlePalaceNatureToFlavorTextId[NUM_NATURES] = { [NATURE_HARDY] = B_MSG_EAGER_FOR_MORE, [NATURE_LONELY] = B_MSG_GLINT_IN_EYE, [NATURE_BRAVE] = B_MSG_GETTING_IN_POS, [NATURE_ADAMANT] = B_MSG_GLINT_IN_EYE, [NATURE_NAUGHTY] = B_MSG_GLINT_IN_EYE, [NATURE_BOLD] = B_MSG_GETTING_IN_POS, [NATURE_DOCILE] = B_MSG_EAGER_FOR_MORE, [NATURE_RELAXED] = B_MSG_GLINT_IN_EYE, [NATURE_IMPISH] = B_MSG_GETTING_IN_POS, [NATURE_LAX] = B_MSG_GROWL_DEEPLY, [NATURE_TIMID] = B_MSG_GROWL_DEEPLY, [NATURE_HASTY] = B_MSG_GLINT_IN_EYE, [NATURE_SERIOUS] = B_MSG_EAGER_FOR_MORE, [NATURE_JOLLY] = B_MSG_GETTING_IN_POS, [NATURE_NAIVE] = B_MSG_EAGER_FOR_MORE, [NATURE_MODEST] = B_MSG_GETTING_IN_POS, [NATURE_MILD] = B_MSG_GROWL_DEEPLY, [NATURE_QUIET] = B_MSG_EAGER_FOR_MORE, [NATURE_BASHFUL] = B_MSG_EAGER_FOR_MORE, [NATURE_RASH] = B_MSG_GROWL_DEEPLY, [NATURE_CALM] = B_MSG_GETTING_IN_POS, [NATURE_GENTLE] = B_MSG_GLINT_IN_EYE, [NATURE_SASSY] = B_MSG_GROWL_DEEPLY, [NATURE_CAREFUL] = B_MSG_GROWL_DEEPLY, [NATURE_QUIRKY] = B_MSG_EAGER_FOR_MORE, }; static void Cmd_attackcanceler(void) { s32 i; 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 (AtkCanceller_UnableToUseMove()) 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)) && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)) { gBattlescriptCurrInstr = BattleScript_NoPPForMove; gMoveResultFlags |= MOVE_RESULT_MISSED; return; } gHitMarker &= ~HITMARKER_ALLOW_NO_PP; if (!(gHitMarker & HITMARKER_OBEYS) && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)) { i = IsMonDisobedient(); // why use the 'i' variable...? switch (i) { case 0: break; case 2: gHitMarker |= HITMARKER_OBEYS; return; default: gMoveResultFlags |= MOVE_RESULT_MISSED; return; } } gHitMarker |= HITMARKER_OBEYS; if (gProtectStructs[gBattlerTarget].bounceMove && gBattleMoves[gCurrentMove].flags & FLAG_MAGIC_COAT_AFFECTED) { PressurePPLose(gBattlerAttacker, gBattlerTarget, MOVE_MAGIC_COAT); gProtectStructs[gBattlerTarget].bounceMove = 0; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MagicCoatBounce; return; } for (i = 0; i < gBattlersCount; i++) { if ((gProtectStructs[gBattlerByTurnOrder[i]].stealMove) && gBattleMoves[gCurrentMove].flags & FLAG_SNATCH_AFFECTED) { PressurePPLose(gBattlerAttacker, gBattlerByTurnOrder[i], MOVE_SNATCH); gProtectStructs[gBattlerByTurnOrder[i]].stealMove = 0; gBattleScripting.battler = gBattlerByTurnOrder[i]; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SnatchedMove; return; } } if (gSpecialStatuses[gBattlerTarget].lightningRodRedirected) { gSpecialStatuses[gBattlerTarget].lightningRodRedirected = 0; gLastUsedAbility = ABILITY_LIGHTNING_ROD; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TookAttack; RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); } else if (DEFENDER_IS_PROTECTED && (gCurrentMove != MOVE_CURSE || IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) && ((!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))) { CancelMultiTurnMoves(gBattlerAttacker); gMoveResultFlags |= MOVE_RESULT_MISSED; gLastLandedMoves[gBattlerTarget] = 0; gLastHitByType[gBattlerTarget] = 0; gBattleCommunication[MISS_TYPE] = B_MSG_PROTECTED; gBattlescriptCurrInstr++; } else { gBattlescriptCurrInstr++; } } static void JumpIfMoveFailed(u8 adder, u16 move) { const u8 *BS_ptr = gBattlescriptCurrInstr + adder; if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) { gLastLandedMoves[gBattlerTarget] = 0; gLastHitByType[gBattlerTarget] = 0; BS_ptr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } else { TrySetDestinyBondToHappen(); if (AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, move)) return; } gBattlescriptCurrInstr = BS_ptr; } static void Cmd_jumpifaffectedbyprotect(void) { if (DEFENDER_IS_PROTECTED) { gMoveResultFlags |= MOVE_RESULT_MISSED; JumpIfMoveFailed(5, 0); gBattleCommunication[MISS_TYPE] = B_MSG_PROTECTED; } else { gBattlescriptCurrInstr += 5; } } bool8 JumpIfMoveAffectedByProtect(u16 move) { bool8 affected = FALSE; if (DEFENDER_IS_PROTECTED) { gMoveResultFlags |= MOVE_RESULT_MISSED; JumpIfMoveFailed(7, move); gBattleCommunication[MISS_TYPE] = B_MSG_PROTECTED; affected = TRUE; } return affected; } static bool8 AccuracyCalcHelper(u16 move) { if (gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker) { JumpIfMoveFailed(7, move); return TRUE; } if (!(gHitMarker & HITMARKER_IGNORE_ON_AIR) && gStatuses3[gBattlerTarget] & STATUS3_ON_AIR) { gMoveResultFlags |= MOVE_RESULT_MISSED; JumpIfMoveFailed(7, move); return TRUE; } gHitMarker &= ~HITMARKER_IGNORE_ON_AIR; if (!(gHitMarker & HITMARKER_IGNORE_UNDERGROUND) && gStatuses3[gBattlerTarget] & STATUS3_UNDERGROUND) { gMoveResultFlags |= MOVE_RESULT_MISSED; JumpIfMoveFailed(7, move); return TRUE; } gHitMarker &= ~HITMARKER_IGNORE_UNDERGROUND; if (!(gHitMarker & HITMARKER_IGNORE_UNDERWATER) && gStatuses3[gBattlerTarget] & STATUS3_UNDERWATER) { gMoveResultFlags |= MOVE_RESULT_MISSED; JumpIfMoveFailed(7, move); return TRUE; } gHitMarker &= ~HITMARKER_IGNORE_UNDERWATER; if ((WEATHER_HAS_EFFECT && (gBattleWeather & B_WEATHER_RAIN) && gBattleMoves[move].effect == EFFECT_THUNDER) || (gBattleMoves[move].effect == EFFECT_ALWAYS_HIT || gBattleMoves[move].effect == EFFECT_VITAL_THROW)) { JumpIfMoveFailed(7, move); return TRUE; } return FALSE; } static void Cmd_accuracycheck(void) { u16 move = T2_READ_16(gBattlescriptCurrInstr + 5); if (move == NO_ACC_CALC || move == NO_ACC_CALC_CHECK_LOCK_ON) { if (gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && move == NO_ACC_CALC_CHECK_LOCK_ON && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker) gBattlescriptCurrInstr += 7; else if (gStatuses3[gBattlerTarget] & (STATUS3_ON_AIR | STATUS3_UNDERGROUND | STATUS3_UNDERWATER)) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); else if (!JumpIfMoveAffectedByProtect(0)) gBattlescriptCurrInstr += 7; } else { u8 type, moveAcc, holdEffect, param; s8 buff; u16 calc; if (move == ACC_CURR_MOVE) move = gCurrentMove; GET_MOVE_TYPE(move, type); if (JumpIfMoveAffectedByProtect(move)) return; if (AccuracyCalcHelper(move)) return; if (gBattleMons[gBattlerTarget].status2 & STATUS2_FORESIGHT) { u8 acc = gBattleMons[gBattlerAttacker].statStages[STAT_ACC]; buff = acc; } else { u8 acc = gBattleMons[gBattlerAttacker].statStages[STAT_ACC]; buff = acc + DEFAULT_STAT_STAGE - gBattleMons[gBattlerTarget].statStages[STAT_EVASION]; } if (buff < MIN_STAT_STAGE) buff = MIN_STAT_STAGE; if (buff > MAX_STAT_STAGE) buff = MAX_STAT_STAGE; moveAcc = gBattleMoves[move].accuracy; // check Thunder on sunny weather if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SUN && gBattleMoves[move].effect == EFFECT_THUNDER) moveAcc = 50; calc = sAccuracyStageRatios[buff].dividend * moveAcc; calc /= sAccuracyStageRatios[buff].divisor; if (gBattleMons[gBattlerAttacker].ability == ABILITY_COMPOUND_EYES) calc = (calc * 130) / 100; // 1.3 compound eyes boost if (WEATHER_HAS_EFFECT && gBattleMons[gBattlerTarget].ability == ABILITY_SAND_VEIL && gBattleWeather & B_WEATHER_SANDSTORM) calc = (calc * 80) / 100; // 1.2 sand veil loss if (gBattleMons[gBattlerAttacker].ability == ABILITY_HUSTLE && IS_TYPE_PHYSICAL(type)) calc = (calc * 80) / 100; // 1.2 hustle loss 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_EVASION_UP) calc = (calc * (100 - param)) / 100; // final calculation if ((Random() % 100 + 1) > calc) { gMoveResultFlags |= MOVE_RESULT_MISSED; if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && (gBattleMoves[move].target == MOVE_TARGET_BOTH || gBattleMoves[move].target == MOVE_TARGET_FOES_AND_ALLY)) gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_ATK; else gBattleCommunication[MISS_TYPE] = B_MSG_MISSED; CheckWonderGuardAndLevitate(); } JumpIfMoveFailed(7, move); } } static void Cmd_attackstring(void) { if (gBattleControllerExecFlags) return; if (!(gHitMarker & (HITMARKER_NO_ATTACKSTRING | HITMARKER_ATTACKSTRING_PRINTED))) { PrepareStringBattle(STRINGID_USEDMOVE, gBattlerAttacker); gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED; } gBattlescriptCurrInstr++; gBattleCommunication[MSG_DISPLAY] = 0; } static void Cmd_ppreduce(void) { s32 ppToDeduct = 1; if (gBattleControllerExecFlags) return; if (!gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure) { switch (gBattleMoves[gCurrentMove].target) { case MOVE_TARGET_FOES_AND_ALLY: ppToDeduct += AbilityBattleEffects(ABILITYEFFECT_COUNT_ON_FIELD, gBattlerAttacker, ABILITY_PRESSURE, 0, 0); break; case MOVE_TARGET_BOTH: case MOVE_TARGET_OPPONENTS_FIELD: ppToDeduct += AbilityBattleEffects(ABILITYEFFECT_COUNT_OTHER_SIDE, gBattlerAttacker, ABILITY_PRESSURE, 0, 0); break; default: if (gBattlerAttacker != gBattlerTarget && gBattleMons[gBattlerTarget].ability == ABILITY_PRESSURE) ppToDeduct++; break; } } if (!(gHitMarker & (HITMARKER_NO_PPDEDUCT | HITMARKER_NO_ATTACKSTRING)) && gBattleMons[gBattlerAttacker].pp[gCurrMovePos]) { gProtectStructs[gBattlerAttacker].notFirstStrike = 1; if (gBattleMons[gBattlerAttacker].pp[gCurrMovePos] > ppToDeduct) gBattleMons[gBattlerAttacker].pp[gCurrMovePos] -= ppToDeduct; else gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = 0; if (MOVE_IS_PERMANENT(gBattlerAttacker, gCurrMovePos)) { gActiveBattler = gBattlerAttacker; 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++; } static void Cmd_critcalc(void) { u8 holdEffect; u16 item, critChance; item = gBattleMons[gBattlerAttacker].item; if (item == ITEM_ENIGMA_BERRY) holdEffect = gEnigmaBerries[gBattlerAttacker].holdEffect; else holdEffect = ItemId_GetHoldEffect(item); gPotentialItemEffectBattler = gBattlerAttacker; critChance = 2 * ((gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY) != 0) + (gBattleMoves[gCurrentMove].effect == EFFECT_HIGH_CRITICAL) + (gBattleMoves[gCurrentMove].effect == EFFECT_SKY_ATTACK) + (gBattleMoves[gCurrentMove].effect == EFFECT_BLAZE_KICK) + (gBattleMoves[gCurrentMove].effect == EFFECT_POISON_TAIL) + (holdEffect == HOLD_EFFECT_SCOPE_LENS) + 2 * (holdEffect == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[gBattlerAttacker].species == SPECIES_CHANSEY) + 2 * (holdEffect == HOLD_EFFECT_STICK && gBattleMons[gBattlerAttacker].species == SPECIES_FARFETCHD); if (critChance >= ARRAY_COUNT(sCriticalHitChance)) critChance = ARRAY_COUNT(sCriticalHitChance) - 1; if ((gBattleMons[gBattlerTarget].ability != ABILITY_BATTLE_ARMOR && gBattleMons[gBattlerTarget].ability != ABILITY_SHELL_ARMOR) && !(gStatuses3[gBattlerAttacker] & STATUS3_CANT_SCORE_A_CRIT) && !(gBattleTypeFlags & (BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_FIRST_BATTLE)) && !(Random() % sCriticalHitChance[critChance])) gCritMultiplier = 2; else gCritMultiplier = 1; gBattlescriptCurrInstr++; } static void Cmd_damagecalc(void) { u16 sideStatus = gSideStatuses[GET_BATTLER_SIDE(gBattlerTarget)]; gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[gBattlerAttacker], &gBattleMons[gBattlerTarget], gCurrentMove, sideStatus, gDynamicBasePower, gBattleStruct->dynamicMoveType, gBattlerAttacker, gBattlerTarget); gBattleMoveDamage = gBattleMoveDamage * gCritMultiplier * gBattleScripting.dmgMultiplier; if (gStatuses3[gBattlerAttacker] & STATUS3_CHARGED_UP && gBattleMoves[gCurrentMove].type == TYPE_ELECTRIC) gBattleMoveDamage *= 2; if (gProtectStructs[gBattlerAttacker].helpingHand) gBattleMoveDamage = gBattleMoveDamage * 15 / 10; gBattlescriptCurrInstr++; } void AI_CalcDmg(u8 attacker, u8 defender) { u16 sideStatus = gSideStatuses[GET_BATTLER_SIDE(defender)]; gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[attacker], &gBattleMons[defender], gCurrentMove, sideStatus, gDynamicBasePower, gBattleStruct->dynamicMoveType, attacker, defender); gDynamicBasePower = 0; gBattleMoveDamage = gBattleMoveDamage * gCritMultiplier * gBattleScripting.dmgMultiplier; if (gStatuses3[attacker] & STATUS3_CHARGED_UP && gBattleMoves[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 (gBattleMoves[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 (gBattleMoves[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) { s32 i = 0; u8 moveType; if (gCurrentMove == MOVE_STRUGGLE) { gBattlescriptCurrInstr++; return; } GET_MOVE_TYPE(gCurrentMove, moveType); // check stab if (IS_BATTLER_OF_TYPE(gBattlerAttacker, moveType)) { gBattleMoveDamage = gBattleMoveDamage * 15; gBattleMoveDamage = gBattleMoveDamage / 10; } if (gBattleMons[gBattlerTarget].ability == ABILITY_LEVITATE && moveType == TYPE_GROUND) { gLastUsedAbility = gBattleMons[gBattlerTarget].ability; gMoveResultFlags |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE); gLastLandedMoves[gBattlerTarget] = 0; gLastHitByType[gBattlerTarget] = 0; gBattleCommunication[MISS_TYPE] = B_MSG_GROUND_MISS; RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); } else { 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; } else if (TYPE_EFFECT_ATK_TYPE(i) == moveType) { // check type1 if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type1) ModulateDmgByType(TYPE_EFFECT_MULTIPLIER(i)); // check type2 if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2 && gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2) ModulateDmgByType(TYPE_EFFECT_MULTIPLIER(i)); } i += 3; } } if (gBattleMons[gBattlerTarget].ability == ABILITY_WONDER_GUARD && AttacksThisTurn(gBattlerAttacker, gCurrentMove) == 2 && (!(gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) || ((gMoveResultFlags & (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)) == (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE))) && gBattleMoves[gCurrentMove].power) { gLastUsedAbility = ABILITY_WONDER_GUARD; gMoveResultFlags |= MOVE_RESULT_MISSED; gLastLandedMoves[gBattlerTarget] = 0; gLastHitByType[gBattlerTarget] = 0; gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_DMG; RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); } if (gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE) gProtectStructs[gBattlerAttacker].targetNotAffected = 1; gBattlescriptCurrInstr++; } static void CheckWonderGuardAndLevitate(void) { u8 flags = 0; s32 i = 0; u8 moveType; if (gCurrentMove == MOVE_STRUGGLE || !gBattleMoves[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)) && gBattleMoves[gCurrentMove].power) { gLastUsedAbility = ABILITY_WONDER_GUARD; gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_DMG; RecordAbilityBattle(gBattlerTarget, ABILITY_WONDER_GUARD); } } } static void ModulateDmgByType2(u8 multiplier, u16 move, u8* flags) // same as ModulateDmgByType except different arguments { 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 (gBattleMoves[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 (gBattleMoves[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 = gBattleMoves[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))) && gBattleMoves[move].power) { flags |= MOVE_RESULT_MISSED; } return flags; } u8 AI_TypeCalc(u16 move, u16 targetSpecies, u8 targetAbility) { s32 i = 0; u8 flags = 0; u8 type1 = gBaseStats[targetSpecies].type1, type2 = gBaseStats[targetSpecies].type2; u8 moveType; if (move == MOVE_STRUGGLE) return 0; moveType = gBattleMoves[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))) && gBattleMoves[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_adjustnormaldamage(void) { u8 holdEffect, param; ApplyRandomDmgMultiplier(); 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, holdEffect); gSpecialStatuses[gBattlerTarget].focusBanded = 1; } if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE) && (gBattleMoves[gCurrentMove].effect == EFFECT_FALSE_SWIPE || gProtectStructs[gBattlerTarget].endured || gSpecialStatuses[gBattlerTarget].focusBanded) && gBattleMons[gBattlerTarget].hp <= gBattleMoveDamage) { gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1; if (gProtectStructs[gBattlerTarget].endured) { gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED; } else if (gSpecialStatuses[gBattlerTarget].focusBanded) { gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON; gLastUsedItem = gBattleMons[gBattlerTarget].item; } } gBattlescriptCurrInstr++; } static void Cmd_adjustnormaldamage2(void) // The same as adjustnormaldamage except it doesn't check for false swipe move effect. { u8 holdEffect, param; ApplyRandomDmgMultiplier(); 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, holdEffect); gSpecialStatuses[gBattlerTarget].focusBanded = 1; } if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE) && (gProtectStructs[gBattlerTarget].endured || gSpecialStatuses[gBattlerTarget].focusBanded) && gBattleMons[gBattlerTarget].hp <= gBattleMoveDamage) { gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1; if (gProtectStructs[gBattlerTarget].endured) { gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED; } else if (gSpecialStatuses[gBattlerTarget].focusBanded) { gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON; gLastUsedItem = gBattleMons[gBattlerTarget].item; } } gBattlescriptCurrInstr++; } static void Cmd_attackanimation(void) { if (gBattleControllerExecFlags) return; if ((gHitMarker & HITMARKER_NO_ANIMATIONS) && (gCurrentMove != MOVE_TRANSFORM && gCurrentMove != MOVE_SUBSTITUTE)) { BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_Pausex20; gBattleScripting.animTurn++; gBattleScripting.animTargetsHit++; } else { if ((gBattleMoves[gCurrentMove].target & MOVE_TARGET_BOTH || gBattleMoves[gCurrentMove].target & MOVE_TARGET_FOES_AND_ALLY || gBattleMoves[gCurrentMove].target & MOVE_TARGET_DEPENDS) && gBattleScripting.animTargetsHit) { gBattlescriptCurrInstr++; return; } if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { u8 multihit; gActiveBattler = gBattlerAttacker; if (gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE) multihit = gMultiHitCounter; else if (gMultiHitCounter != 0 && gMultiHitCounter != 1) { if (gBattleMons[gBattlerTarget].hp <= gBattleMoveDamage) multihit = 1; else multihit = gMultiHitCounter; } else multihit = gMultiHitCounter; BtlController_EmitMoveAnimation(BUFFER_A, gCurrentMove, gBattleScripting.animTurn, gBattleMovePower, gBattleMoveDamage, gBattleMons[gBattlerAttacker].friendship, &gDisableStructs[gBattlerAttacker], multihit); gBattleScripting.animTurn += 1; gBattleScripting.animTargetsHit += 1; MarkBattlerForControllerExec(gBattlerAttacker); gBattlescriptCurrInstr++; } else { BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_Pausex20; } } } static void Cmd_waitanimation(void) { if (gBattleControllerExecFlags == 0) gBattlescriptCurrInstr++; } static void Cmd_healthbarupdate(void) { if (gBattleControllerExecFlags) return; if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); if (gBattleMons[gActiveBattler].status2 & STATUS2_SUBSTITUTE && gDisableStructs[gActiveBattler].substituteHP && !(gHitMarker & HITMARKER_IGNORE_SUBSTITUTE)) { PrepareStringBattle(STRINGID_SUBSTITUTEDAMAGED, gActiveBattler); } else { s16 healthValue; s32 currDmg = gBattleMoveDamage; s32 maxPossibleDmgValue = 10000; // not present in R/S, ensures that huge damage values don't change sign if (currDmg <= maxPossibleDmgValue) healthValue = currDmg; else healthValue = maxPossibleDmgValue; BtlController_EmitHealthBarUpdate(BUFFER_A, healthValue); MarkBattlerForControllerExec(gActiveBattler); if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER && gBattleMoveDamage > 0) gBattleResults.playerMonWasDamaged = TRUE; } } gBattlescriptCurrInstr += 2; } static void Cmd_datahpupdate(void) { u32 moveType; if (gBattleControllerExecFlags) return; if (gBattleStruct->dynamicMoveType == 0) moveType = gBattleMoves[gCurrentMove].type; else if (!(gBattleStruct->dynamicMoveType & F_DYNAMIC_TYPE_1)) moveType = gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK; else moveType = gBattleMoves[gCurrentMove].type; if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); if (gBattleMons[gActiveBattler].status2 & STATUS2_SUBSTITUTE && gDisableStructs[gActiveBattler].substituteHP && !(gHitMarker & HITMARKER_IGNORE_SUBSTITUTE)) { if (gDisableStructs[gActiveBattler].substituteHP >= gBattleMoveDamage) { if (gSpecialStatuses[gActiveBattler].dmg == 0) gSpecialStatuses[gActiveBattler].dmg = gBattleMoveDamage; gDisableStructs[gActiveBattler].substituteHP -= gBattleMoveDamage; gHpDealt = gBattleMoveDamage; } else { if (gSpecialStatuses[gActiveBattler].dmg == 0) gSpecialStatuses[gActiveBattler].dmg = gDisableStructs[gActiveBattler].substituteHP; gHpDealt = gDisableStructs[gActiveBattler].substituteHP; gDisableStructs[gActiveBattler].substituteHP = 0; } // check substitute fading if (gDisableStructs[gActiveBattler].substituteHP == 0) { gBattlescriptCurrInstr += 2; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SubstituteFade; return; } } else { gHitMarker &= ~HITMARKER_IGNORE_SUBSTITUTE; if (gBattleMoveDamage < 0) // hp goes up { gBattleMons[gActiveBattler].hp -= gBattleMoveDamage; if (gBattleMons[gActiveBattler].hp > gBattleMons[gActiveBattler].maxHP) gBattleMons[gActiveBattler].hp = gBattleMons[gActiveBattler].maxHP; } else // hp goes down { if (gHitMarker & HITMARKER_SKIP_DMG_TRACK) { gHitMarker &= ~HITMARKER_SKIP_DMG_TRACK; } else { gTakenDmg[gActiveBattler] += gBattleMoveDamage; if (gBattlescriptCurrInstr[1] == BS_TARGET) gTakenDmgByBattler[gActiveBattler] = gBattlerAttacker; else gTakenDmgByBattler[gActiveBattler] = gBattlerTarget; } if (gBattleMons[gActiveBattler].hp > gBattleMoveDamage) { gBattleMons[gActiveBattler].hp -= gBattleMoveDamage; gHpDealt = gBattleMoveDamage; } else { gHpDealt = gBattleMons[gActiveBattler].hp; gBattleMons[gActiveBattler].hp = 0; } if (!gSpecialStatuses[gActiveBattler].dmg && !(gHitMarker & HITMARKER_PASSIVE_DAMAGE)) gSpecialStatuses[gActiveBattler].dmg = gHpDealt; if (IS_TYPE_PHYSICAL(moveType) && !(gHitMarker & HITMARKER_PASSIVE_DAMAGE) && gCurrentMove != MOVE_PAIN_SPLIT) { gProtectStructs[gActiveBattler].physicalDmg = gHpDealt; gSpecialStatuses[gActiveBattler].physicalDmg = gHpDealt; if (gBattlescriptCurrInstr[1] == BS_TARGET) { gProtectStructs[gActiveBattler].physicalBattlerId = gBattlerAttacker; gSpecialStatuses[gActiveBattler].physicalBattlerId = gBattlerAttacker; } else { gProtectStructs[gActiveBattler].physicalBattlerId = gBattlerTarget; gSpecialStatuses[gActiveBattler].physicalBattlerId = gBattlerTarget; } } else if (!IS_TYPE_PHYSICAL(moveType) && !(gHitMarker & HITMARKER_PASSIVE_DAMAGE)) { gProtectStructs[gActiveBattler].specialDmg = gHpDealt; gSpecialStatuses[gActiveBattler].specialDmg = gHpDealt; if (gBattlescriptCurrInstr[1] == BS_TARGET) { gProtectStructs[gActiveBattler].specialBattlerId = gBattlerAttacker; gSpecialStatuses[gActiveBattler].specialBattlerId = gBattlerAttacker; } else { gProtectStructs[gActiveBattler].specialBattlerId = gBattlerTarget; gSpecialStatuses[gActiveBattler].specialBattlerId = gBattlerTarget; } } } gHitMarker &= ~HITMARKER_PASSIVE_DAMAGE; BtlController_EmitSetMonData(BUFFER_A, REQUEST_HP_BATTLE, 0, sizeof(gBattleMons[gActiveBattler].hp), &gBattleMons[gActiveBattler].hp); MarkBattlerForControllerExec(gActiveBattler); } } else { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); if (gSpecialStatuses[gActiveBattler].dmg == 0) gSpecialStatuses[gActiveBattler].dmg = 0xFFFF; } gBattlescriptCurrInstr += 2; } static void Cmd_critmessage(void) { if (gBattleControllerExecFlags == 0) { if (gCritMultiplier == 2 && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { PrepareStringBattle(STRINGID_CRITICALHIT, gBattlerAttacker); gBattleCommunication[MSG_DISPLAY] = 1; } gBattlescriptCurrInstr++; } } static void Cmd_effectivenesssound(void) { if (gBattleControllerExecFlags) return; gActiveBattler = gBattlerTarget; if (!(gMoveResultFlags & MOVE_RESULT_MISSED)) { switch (gMoveResultFlags & (u8)(~MOVE_RESULT_MISSED)) { case MOVE_RESULT_SUPER_EFFECTIVE: BtlController_EmitPlaySE(BUFFER_A, SE_SUPER_EFFECTIVE); MarkBattlerForControllerExec(gActiveBattler); break; case MOVE_RESULT_NOT_VERY_EFFECTIVE: BtlController_EmitPlaySE(BUFFER_A, SE_NOT_EFFECTIVE); MarkBattlerForControllerExec(gActiveBattler); 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: default: if (gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) { BtlController_EmitPlaySE(BUFFER_A, SE_SUPER_EFFECTIVE); MarkBattlerForControllerExec(gActiveBattler); } else if (gMoveResultFlags & MOVE_RESULT_NOT_VERY_EFFECTIVE) { BtlController_EmitPlaySE(BUFFER_A, SE_NOT_EFFECTIVE); MarkBattlerForControllerExec(gActiveBattler); } else if (!(gMoveResultFlags & (MOVE_RESULT_DOESNT_AFFECT_FOE | MOVE_RESULT_FAILED))) { BtlController_EmitPlaySE(BUFFER_A, SE_EFFECTIVE); MarkBattlerForControllerExec(gActiveBattler); } break; } } gBattlescriptCurrInstr++; } static void Cmd_resultmessage(void) { u32 stringId = 0; if (gBattleControllerExecFlags) return; if (gMoveResultFlags & MOVE_RESULT_MISSED && (!(gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE) || gBattleCommunication[MISS_TYPE] > B_MSG_AVOIDED_ATK)) { stringId = gMissStringIds[gBattleCommunication[MISS_TYPE]]; gBattleCommunication[MSG_DISPLAY] = 1; } else { gBattleCommunication[MSG_DISPLAY] = 1; switch (gMoveResultFlags & (u8)(~MOVE_RESULT_MISSED)) { case MOVE_RESULT_SUPER_EFFECTIVE: stringId = STRINGID_SUPEREFFECTIVE; break; case MOVE_RESULT_NOT_VERY_EFFECTIVE: 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_FocusBandActivates; 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_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_FocusBandActivates; return; } else if (gMoveResultFlags & MOVE_RESULT_FAILED) { stringId = STRINGID_BUTITFAILED; } else { gBattleCommunication[MSG_DISPLAY] = 0; } } } if (stringId) PrepareStringBattle(stringId, gBattlerAttacker); gBattlescriptCurrInstr++; } static void Cmd_printstring(void) { if (gBattleControllerExecFlags == 0) { u16 var = T2_READ_16(gBattlescriptCurrInstr + 1); PrepareStringBattle(var, gBattlerAttacker); gBattlescriptCurrInstr += 3; gBattleCommunication[MSG_DISPLAY] = 1; } } static void Cmd_printselectionstring(void) { gActiveBattler = gBattlerAttacker; BtlController_EmitPrintSelectionString(BUFFER_A, T2_READ_16(gBattlescriptCurrInstr + 1)); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 3; gBattleCommunication[MSG_DISPLAY] = 1; } static void Cmd_waitmessage(void) { if (gBattleControllerExecFlags == 0) { if (!gBattleCommunication[MSG_DISPLAY]) { gBattlescriptCurrInstr += 3; } else { u16 toWait = T2_READ_16(gBattlescriptCurrInstr + 1); if (++gPauseCounterBattle >= toWait) { gPauseCounterBattle = 0; gBattlescriptCurrInstr += 3; gBattleCommunication[MSG_DISPLAY] = 0; } } } } static void Cmd_printfromtable(void) { if (gBattleControllerExecFlags == 0) { const u16 *ptr = (const u16*) T1_READ_PTR(gBattlescriptCurrInstr + 1); ptr += gBattleCommunication[MULTISTRING_CHOOSER]; PrepareStringBattle(*ptr, gBattlerAttacker); gBattlescriptCurrInstr += 5; gBattleCommunication[MSG_DISPLAY] = 1; } } static void Cmd_printselectionstringfromtable(void) { if (gBattleControllerExecFlags == 0) { const u16 *ptr = (const u16*) T1_READ_PTR(gBattlescriptCurrInstr + 1); ptr += gBattleCommunication[MULTISTRING_CHOOSER]; gActiveBattler = gBattlerAttacker; BtlController_EmitPrintSelectionString(BUFFER_A, *ptr); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 5; gBattleCommunication[MSG_DISPLAY] = 1; } } u8 GetBattlerTurnOrderNum(u8 battlerId) { s32 i; for (i = 0; i < gBattlersCount; i++) { if (gBattlerByTurnOrder[i] == battlerId) break; } return i; } #define INCREMENT_RESET_RETURN \ { \ gBattlescriptCurrInstr++; \ gBattleCommunication[MOVE_EFFECT_BYTE] = 0; \ return; \ } #define RESET_RETURN \ { \ gBattleCommunication[MOVE_EFFECT_BYTE] = 0; \ return; \ } void SetMoveEffect(bool8 primary, u8 certain) { bool32 statusChanged = FALSE; u8 affectsUser = 0; // 0x40 otherwise bool32 noSunCanFreeze = TRUE; if (gBattleCommunication[MOVE_EFFECT_BYTE] & MOVE_EFFECT_AFFECTS_USER) { gEffectBattler = gBattlerAttacker; // battlerId that effects get applied on gBattleCommunication[MOVE_EFFECT_BYTE] &= ~MOVE_EFFECT_AFFECTS_USER; affectsUser = MOVE_EFFECT_AFFECTS_USER; gBattleScripting.battler = gBattlerTarget; // theoretically the attacker } else { gEffectBattler = gBattlerTarget; gBattleScripting.battler = gBattlerAttacker; } if (gBattleMons[gEffectBattler].ability == ABILITY_SHIELD_DUST && !(gHitMarker & HITMARKER_IGNORE_SAFEGUARD) && !primary && gBattleCommunication[MOVE_EFFECT_BYTE] <= 9) INCREMENT_RESET_RETURN if (gSideStatuses[GET_BATTLER_SIDE(gEffectBattler)] & SIDE_STATUS_SAFEGUARD && !(gHitMarker & HITMARKER_IGNORE_SAFEGUARD) && !primary && gBattleCommunication[MOVE_EFFECT_BYTE] <= 7) INCREMENT_RESET_RETURN if (gBattleMons[gEffectBattler].hp == 0 && gBattleCommunication[MOVE_EFFECT_BYTE] != MOVE_EFFECT_PAYDAY && gBattleCommunication[MOVE_EFFECT_BYTE] != MOVE_EFFECT_STEAL_ITEM) INCREMENT_RESET_RETURN if (gBattleMons[gEffectBattler].status2 & STATUS2_SUBSTITUTE && affectsUser != MOVE_EFFECT_AFFECTS_USER) INCREMENT_RESET_RETURN if (gBattleCommunication[MOVE_EFFECT_BYTE] <= PRIMARY_STATUS_MOVE_EFFECT) { switch (sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]]) { case STATUS1_SLEEP: // check active uproar if (gBattleMons[gEffectBattler].ability != ABILITY_SOUNDPROOF) { for (gActiveBattler = 0; gActiveBattler < gBattlersCount && !(gBattleMons[gActiveBattler].status2 & STATUS2_UPROAR); gActiveBattler++) {} } else gActiveBattler = gBattlersCount; if (gBattleMons[gEffectBattler].status1) break; if (gActiveBattler != gBattlersCount) break; if (gBattleMons[gEffectBattler].ability == ABILITY_VITAL_SPIRIT) break; if (gBattleMons[gEffectBattler].ability == ABILITY_INSOMNIA) break; CancelMultiTurnMoves(gEffectBattler); statusChanged = TRUE; break; case STATUS1_POISON: if (gBattleMons[gEffectBattler].ability == ABILITY_IMMUNITY && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) { gLastUsedAbility = ABILITY_IMMUNITY; RecordAbilityBattle(gEffectBattler, ABILITY_IMMUNITY); BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_PSNPrevention; if (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_ABILITY_STATUS; gHitMarker &= ~HITMARKER_IGNORE_SAFEGUARD; } else { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_STATUS; } RESET_RETURN } if ((IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_POISON) || IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_STEEL)) && (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) { BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_PSNPrevention; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT; RESET_RETURN } if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_POISON)) break; if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_STEEL)) break; if (gBattleMons[gEffectBattler].status1) break; if (gBattleMons[gEffectBattler].ability == ABILITY_IMMUNITY) break; statusChanged = TRUE; break; case STATUS1_BURN: if (gBattleMons[gEffectBattler].ability == ABILITY_WATER_VEIL && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) { gLastUsedAbility = ABILITY_WATER_VEIL; RecordAbilityBattle(gEffectBattler, ABILITY_WATER_VEIL); BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_BRNPrevention; if (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_ABILITY_STATUS; gHitMarker &= ~HITMARKER_IGNORE_SAFEGUARD; } else { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_STATUS; } RESET_RETURN } if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_FIRE) && (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) { BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_BRNPrevention; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT; RESET_RETURN } if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_FIRE)) break; if (gBattleMons[gEffectBattler].ability == ABILITY_WATER_VEIL) break; if (gBattleMons[gEffectBattler].status1) break; statusChanged = TRUE; break; case STATUS1_FREEZE: if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SUN) noSunCanFreeze = FALSE; if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_ICE)) break; if (gBattleMons[gEffectBattler].status1) break; if (noSunCanFreeze == 0) break; if (gBattleMons[gEffectBattler].ability == ABILITY_MAGMA_ARMOR) break; CancelMultiTurnMoves(gEffectBattler); statusChanged = TRUE; break; case STATUS1_PARALYSIS: if (gBattleMons[gEffectBattler].ability == ABILITY_LIMBER) { if (primary == TRUE || certain == MOVE_EFFECT_CERTAIN) { gLastUsedAbility = ABILITY_LIMBER; RecordAbilityBattle(gEffectBattler, ABILITY_LIMBER); BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_PRLZPrevention; if (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_ABILITY_STATUS; gHitMarker &= ~HITMARKER_IGNORE_SAFEGUARD; } else { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_STATUS; } RESET_RETURN } else break; } if (gBattleMons[gEffectBattler].status1) break; statusChanged = TRUE; break; case STATUS1_TOXIC_POISON: if (gBattleMons[gEffectBattler].ability == ABILITY_IMMUNITY && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) { gLastUsedAbility = ABILITY_IMMUNITY; RecordAbilityBattle(gEffectBattler, ABILITY_IMMUNITY); BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_PSNPrevention; if (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_ABILITY_STATUS; gHitMarker &= ~HITMARKER_IGNORE_SAFEGUARD; } else { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ABILITY_PREVENTS_MOVE_STATUS; } RESET_RETURN } if ((IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_POISON) || IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_STEEL)) && (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) { BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_PSNPrevention; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT; RESET_RETURN } if (gBattleMons[gEffectBattler].status1) break; if (!IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_POISON) && !IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_STEEL)) { if (gBattleMons[gEffectBattler].ability == ABILITY_IMMUNITY) break; // 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; } if (statusChanged == TRUE) { BattleScriptPush(gBattlescriptCurrInstr + 1); if (sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]] == STATUS1_SLEEP) gBattleMons[gEffectBattler].status1 |= STATUS1_SLEEP_TURN((Random() & 3) + 2); // 2-5 turns else gBattleMons[gEffectBattler].status1 |= sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]]; gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; gActiveBattler = gEffectBattler; BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gEffectBattler].status1), &gBattleMons[gEffectBattler].status1); MarkBattlerForControllerExec(gActiveBattler); if (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUSED_BY_ABILITY; gHitMarker &= ~HITMARKER_IGNORE_SAFEGUARD; } else { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUSED; } // for synchronize if (gBattleCommunication[MOVE_EFFECT_BYTE] == MOVE_EFFECT_POISON || gBattleCommunication[MOVE_EFFECT_BYTE] == MOVE_EFFECT_TOXIC || gBattleCommunication[MOVE_EFFECT_BYTE] == MOVE_EFFECT_PARALYSIS || gBattleCommunication[MOVE_EFFECT_BYTE] == MOVE_EFFECT_BURN) { u8* synchronizeEffect = &gBattleStruct->synchronizeMoveEffect; *synchronizeEffect = gBattleCommunication[MOVE_EFFECT_BYTE]; gHitMarker |= HITMARKER_SYNCHRONISE_EFFECT; } return; } else if (statusChanged == FALSE) { gBattleCommunication[MOVE_EFFECT_BYTE] = 0; gBattlescriptCurrInstr++; return; } return; } else { if (gBattleMons[gEffectBattler].status2 & sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]]) { gBattlescriptCurrInstr++; } else { u8 side; switch (gBattleCommunication[MOVE_EFFECT_BYTE]) { case MOVE_EFFECT_CONFUSION: if (gBattleMons[gEffectBattler].ability == ABILITY_OWN_TEMPO || gBattleMons[gEffectBattler].status2 & STATUS2_CONFUSION) { gBattlescriptCurrInstr++; } else { gBattleMons[gEffectBattler].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); // 2-5 turns BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; } break; case MOVE_EFFECT_FLINCH: if (gBattleMons[gEffectBattler].ability == ABILITY_INNER_FOCUS) { if (primary == TRUE || certain == MOVE_EFFECT_CERTAIN) { gLastUsedAbility = ABILITY_INNER_FOCUS; RecordAbilityBattle(gEffectBattler, ABILITY_INNER_FOCUS); gBattlescriptCurrInstr = BattleScript_FlinchPrevention; } else { gBattlescriptCurrInstr++; } } else { if (GetBattlerTurnOrderNum(gEffectBattler) > gCurrentTurnActionNumber) gBattleMons[gEffectBattler].status2 |= sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]]; 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((Random() & 3) + 2); // 2-5 turns BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; } else { gBattlescriptCurrInstr++; } break; case MOVE_EFFECT_PAYDAY: if (GET_BATTLER_SIDE(gBattlerAttacker) == B_SIDE_PLAYER) { u16 PayDay = gPaydayMoney; gPaydayMoney += (gBattleMons[gBattlerAttacker].level * 5); if (PayDay > gPaydayMoney) gPaydayMoney = 0xFFFF; } BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; break; case MOVE_EFFECT_TRI_ATTACK: if (gBattleMons[gEffectBattler].status1) { gBattlescriptCurrInstr++; } else { gBattleCommunication[MOVE_EFFECT_BYTE] = Random() % 3 + 3; SetMoveEffect(FALSE, 0); } break; case MOVE_EFFECT_CHARGING: gBattleMons[gEffectBattler].status2 |= STATUS2_MULTIPLETURNS; gLockedMoves[gEffectBattler] = gCurrentMove; gProtectStructs[gEffectBattler].chargingTurn = 1; gBattlescriptCurrInstr++; break; case MOVE_EFFECT_WRAP: if (gBattleMons[gEffectBattler].status2 & STATUS2_WRAPPED) { gBattlescriptCurrInstr++; } else { gBattleMons[gEffectBattler].status2 |= STATUS2_WRAPPED_TURN((Random() & 3) + 3); // 3-6 turns *(gBattleStruct->wrappedMove + gEffectBattler * 2 + 0) = gCurrentMove; *(gBattleStruct->wrappedMove + gEffectBattler * 2 + 1) = gCurrentMove >> 8; *(gBattleStruct->wrappedBy + gEffectBattler) = gBattlerAttacker; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; for (gBattleCommunication[MULTISTRING_CHOOSER] = 0; ; gBattleCommunication[MULTISTRING_CHOOSER]++) { if (gBattleCommunication[MULTISTRING_CHOOSER] > 4) break; if (gTrappingMoves[gBattleCommunication[MULTISTRING_CHOOSER]] == gCurrentMove) break; } } break; case MOVE_EFFECT_RECOIL_25: // 25% recoil gBattleMoveDamage = (gHpDealt) / 4; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; 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 (ChangeStatBuffs(SET_STAT_BUFF_VALUE(1), gBattleCommunication[MOVE_EFFECT_BYTE] - MOVE_EFFECT_ATK_PLUS_1 + 1, affectsUser, 0)) { gBattlescriptCurrInstr++; } else { gBattleScripting.animArg1 = gBattleCommunication[MOVE_EFFECT_BYTE] & ~(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: if (ChangeStatBuffs(SET_STAT_BUFF_VALUE(1) | STAT_BUFF_NEGATIVE, gBattleCommunication[MOVE_EFFECT_BYTE] - MOVE_EFFECT_ATK_MINUS_1 + 1, affectsUser, 0)) { gBattlescriptCurrInstr++; } else { gBattleScripting.animArg1 = gBattleCommunication[MOVE_EFFECT_BYTE] & ~(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 (ChangeStatBuffs(SET_STAT_BUFF_VALUE(2), gBattleCommunication[MOVE_EFFECT_BYTE] - MOVE_EFFECT_ATK_PLUS_2 + 1, affectsUser, 0)) { gBattlescriptCurrInstr++; } else { gBattleScripting.animArg1 = gBattleCommunication[MOVE_EFFECT_BYTE] & ~(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: if (ChangeStatBuffs(SET_STAT_BUFF_VALUE(2) | STAT_BUFF_NEGATIVE, gBattleCommunication[MOVE_EFFECT_BYTE] - MOVE_EFFECT_ATK_MINUS_2 + 1, affectsUser, 0)) { gBattlescriptCurrInstr++; } else { gBattleScripting.animArg1 = gBattleCommunication[MOVE_EFFECT_BYTE] & ~(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 (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL) { gBattlescriptCurrInstr++; break; } side = GetBattlerSide(gBattlerAttacker); if (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT && !(gBattleTypeFlags & (BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_SECRET_BASE))) { gBattlescriptCurrInstr++; } else if (!(gBattleTypeFlags & (BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_SECRET_BASE)) && (gWishFutureKnock.knockedOffMons[side] & gBitTable[gBattlerPartyIndexes[gBattlerAttacker]])) { gBattlescriptCurrInstr++; } else if (gBattleMons[gBattlerTarget].item && gBattleMons[gBattlerTarget].ability == ABILITY_STICKY_HOLD) { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_NoItemSteal; gLastUsedAbility = gBattleMons[gBattlerTarget].ability; RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); } else if (gBattleMons[gBattlerAttacker].item != 0 || gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY || IS_ITEM_MAIL(gBattleMons[gBattlerTarget].item) || gBattleMons[gBattlerTarget].item == 0) { gBattlescriptCurrInstr++; } else { u16* changedItem = &gBattleStruct->changedItems[gBattlerAttacker]; gLastUsedItem = *changedItem = gBattleMons[gBattlerTarget].item; gBattleMons[gBattlerTarget].item = 0; gActiveBattler = gBattlerAttacker; BtlController_EmitSetMonData(BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gLastUsedItem), &gLastUsedItem); MarkBattlerForControllerExec(gBattlerAttacker); gActiveBattler = gBattlerTarget; BtlController_EmitSetMonData(BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].item), &gBattleMons[gBattlerTarget].item); MarkBattlerForControllerExec(gBattlerTarget); BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_ItemSteal; *(u8*)((u8*)(&gBattleStruct->choicedMove[gBattlerTarget]) + 0) = 0; *(u8*)((u8*)(&gBattleStruct->choicedMove[gBattlerTarget]) + 1) = 0; } } 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: BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_AllStatsUp; break; case MOVE_EFFECT_RAPIDSPIN: BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_RapidSpinAway; break; case MOVE_EFFECT_REMOVE_PARALYSIS: // Smelling salts if (!(gBattleMons[gBattlerTarget].status1 & STATUS1_PARALYSIS)) { gBattlescriptCurrInstr++; } else { gBattleMons[gBattlerTarget].status1 &= ~STATUS1_PARALYSIS; gActiveBattler = gBattlerTarget; BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gActiveBattler].status1), &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_TargetPRLZHeal; } break; case MOVE_EFFECT_ATK_DEF_DOWN: // SuperPower BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_AtkDefDown; break; case MOVE_EFFECT_RECOIL_33: // Double Edge gBattleMoveDamage = gHpDealt / 3; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; break; case MOVE_EFFECT_THRASH: if (gBattleMons[gEffectBattler].status2 & STATUS2_LOCK_CONFUSE) { gBattlescriptCurrInstr++; } else { gBattleMons[gEffectBattler].status2 |= STATUS2_MULTIPLETURNS; gLockedMoves[gEffectBattler] = gCurrentMove; gBattleMons[gEffectBattler].status2 |= STATUS2_LOCK_CONFUSE_TURN((Random() & 1) + 2); // thrash for 2-3 turns } break; case MOVE_EFFECT_KNOCK_OFF: if (gBattleMons[gEffectBattler].ability == ABILITY_STICKY_HOLD) { if (gBattleMons[gEffectBattler].item == 0) { gBattlescriptCurrInstr++; } else { gLastUsedAbility = ABILITY_STICKY_HOLD; gBattlescriptCurrInstr = BattleScript_StickyHoldActivates; RecordAbilityBattle(gEffectBattler, ABILITY_STICKY_HOLD); } break; } if (gBattleMons[gEffectBattler].item) { side = GetBattlerSide(gEffectBattler); gLastUsedItem = gBattleMons[gEffectBattler].item; gBattleMons[gEffectBattler].item = 0; gWishFutureKnock.knockedOffMons[side] |= gBitTable[gBattlerPartyIndexes[gEffectBattler]]; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_KnockedOff; *(u8*)((u8*)(&gBattleStruct->choicedMove[gEffectBattler]) + 0) = 0; *(u8*)((u8*)(&gBattleStruct->choicedMove[gEffectBattler]) + 1) = 0; } else { gBattlescriptCurrInstr++; } break; case MOVE_EFFECT_SP_ATK_TWO_DOWN: // Overheat BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_SAtkDown2; break; } } } gBattleCommunication[MOVE_EFFECT_BYTE] = 0; } static void Cmd_seteffectwithchance(void) { u32 percentChance; if (gBattleMons[gBattlerAttacker].ability == ABILITY_SERENE_GRACE) percentChance = gBattleMoves[gCurrentMove].secondaryEffectChance * 2; else percentChance = gBattleMoves[gCurrentMove].secondaryEffectChance; if (gBattleCommunication[MOVE_EFFECT_BYTE] & MOVE_EFFECT_CERTAIN && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { gBattleCommunication[MOVE_EFFECT_BYTE] &= ~MOVE_EFFECT_CERTAIN; SetMoveEffect(FALSE, MOVE_EFFECT_CERTAIN); } else if (Random() % 100 < percentChance && gBattleCommunication[MOVE_EFFECT_BYTE] && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { if (percentChance >= 100) SetMoveEffect(FALSE, MOVE_EFFECT_CERTAIN); else SetMoveEffect(FALSE, 0); } else { gBattlescriptCurrInstr++; } gBattleCommunication[MOVE_EFFECT_BYTE] = 0; gBattleScripting.multihitMoveEffect = 0; } static void Cmd_seteffectprimary(void) { SetMoveEffect(TRUE, 0); } static void Cmd_seteffectsecondary(void) { SetMoveEffect(FALSE, 0); } static void Cmd_clearstatusfromeffect(void) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); if (gBattleCommunication[MOVE_EFFECT_BYTE] <= PRIMARY_STATUS_MOVE_EFFECT) gBattleMons[gActiveBattler].status1 &= (~sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]]); else gBattleMons[gActiveBattler].status2 &= (~sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]]); gBattleCommunication[MOVE_EFFECT_BYTE] = 0; gBattlescriptCurrInstr += 2; gBattleScripting.multihitMoveEffect = 0; } static void Cmd_tryfaintmon(void) { const u8 *BS_ptr; if (gBattlescriptCurrInstr[2] != 0) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); if (gHitMarker & HITMARKER_FAINTED(gActiveBattler)) { BS_ptr = T1_READ_PTR(gBattlescriptCurrInstr + 3); BattleScriptPop(); gBattlescriptCurrInstr = BS_ptr; gSideStatuses[GetBattlerSide(gActiveBattler)] &= ~SIDE_STATUS_SPIKES_DAMAGED; } else { gBattlescriptCurrInstr += 7; } } else { u8 battlerId; if (gBattlescriptCurrInstr[1] == BS_ATTACKER) { gActiveBattler = gBattlerAttacker; battlerId = gBattlerTarget; BS_ptr = BattleScript_FaintAttacker; } else { gActiveBattler = gBattlerTarget; battlerId = gBattlerAttacker; BS_ptr = BattleScript_FaintTarget; } if (!(gAbsentBattlerFlags & gBitTable[gActiveBattler]) && gBattleMons[gActiveBattler].hp == 0) { gHitMarker |= HITMARKER_FAINTED(gActiveBattler); BattleScriptPush(gBattlescriptCurrInstr + 7); gBattlescriptCurrInstr = BS_ptr; if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) { gHitMarker |= HITMARKER_PLAYER_FAINTED; if (gBattleResults.playerFaintCounter < 255) gBattleResults.playerFaintCounter++; AdjustFriendshipOnBattleFaint(gActiveBattler); } else { if (gBattleResults.opponentFaintCounter < 255) gBattleResults.opponentFaintCounter++; gBattleResults.lastOpponentSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES, NULL); } if ((gHitMarker & HITMARKER_DESTINYBOND) && gBattleMons[gBattlerAttacker].hp != 0) { gHitMarker &= ~HITMARKER_DESTINYBOND; BattleScriptPush(gBattlescriptCurrInstr); gBattleMoveDamage = gBattleMons[battlerId].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[gActiveBattler].pp[moveIndex]), &gBattleMons[gActiveBattler].pp[moveIndex]); MarkBattlerForControllerExec(gActiveBattler); PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].moves[moveIndex]) } } else { gBattlescriptCurrInstr += 7; } } } static void Cmd_dofaintanimation(void) { if (gBattleControllerExecFlags == 0) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); BtlController_EmitFaintAnimation(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 2; } } static void Cmd_cleareffectsonfaint(void) { if (gBattleControllerExecFlags == 0) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); if (!(gBattleTypeFlags & BATTLE_TYPE_ARENA) || gBattleMons[gActiveBattler].hp == 0) { gBattleMons[gActiveBattler].status1 = 0; BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gActiveBattler].status1), &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); } FaintClearSetData(); // Effects like attractions, trapping, etc. gBattlescriptCurrInstr += 2; } } static void Cmd_jumpifstatus(void) { u8 battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); u32 flags = T2_READ_32(gBattlescriptCurrInstr + 2); const u8* jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 6); if (gBattleMons[battlerId].status1 & flags && gBattleMons[battlerId].hp) gBattlescriptCurrInstr = jumpPtr; else gBattlescriptCurrInstr += 10; } static void Cmd_jumpifstatus2(void) { u8 battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); u32 flags = T2_READ_32(gBattlescriptCurrInstr + 2); const u8* jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 6); if (gBattleMons[battlerId].status2 & flags && gBattleMons[battlerId].hp) gBattlescriptCurrInstr = jumpPtr; else gBattlescriptCurrInstr += 10; } static void Cmd_jumpifability(void) { u8 battlerId; u8 ability = gBattlescriptCurrInstr[2]; const u8* jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 3); if (gBattlescriptCurrInstr[1] == BS_ATTACKER_SIDE) { battlerId = AbilityBattleEffects(ABILITYEFFECT_CHECK_BATTLER_SIDE, gBattlerAttacker, ability, 0, 0); if (battlerId) { gLastUsedAbility = ability; gBattlescriptCurrInstr = jumpPtr; RecordAbilityBattle(battlerId - 1, gLastUsedAbility); gBattleScripting.battlerWithAbility = battlerId - 1; } else gBattlescriptCurrInstr += 7; } else if (gBattlescriptCurrInstr[1] == BS_NOT_ATTACKER_SIDE) { battlerId = AbilityBattleEffects(ABILITYEFFECT_CHECK_OTHER_SIDE, gBattlerAttacker, ability, 0, 0); if (battlerId) { gLastUsedAbility = ability; gBattlescriptCurrInstr = jumpPtr; RecordAbilityBattle(battlerId - 1, gLastUsedAbility); gBattleScripting.battlerWithAbility = battlerId - 1; } else gBattlescriptCurrInstr += 7; } else { battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); if (gBattleMons[battlerId].ability == ability) { gLastUsedAbility = ability; gBattlescriptCurrInstr = jumpPtr; RecordAbilityBattle(battlerId, gLastUsedAbility); gBattleScripting.battlerWithAbility = battlerId; } else gBattlescriptCurrInstr += 7; } } static void Cmd_jumpifsideaffecting(void) { u8 side; u16 flags; const u8* jumpPtr; if (gBattlescriptCurrInstr[1] == BS_ATTACKER) side = GET_BATTLER_SIDE(gBattlerAttacker); else side = GET_BATTLER_SIDE(gBattlerTarget); flags = T2_READ_16(gBattlescriptCurrInstr + 2); jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 4); if (gSideStatuses[side] & flags) gBattlescriptCurrInstr = jumpPtr; else gBattlescriptCurrInstr += 8; } static void Cmd_jumpifstat(void) { u8 ret = 0; u8 battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); u8 value = gBattleMons[battlerId].statStages[gBattlescriptCurrInstr[3]]; switch (gBattlescriptCurrInstr[2]) { case CMP_EQUAL: if (value == gBattlescriptCurrInstr[4]) ret++; break; case CMP_NOT_EQUAL: if (value != gBattlescriptCurrInstr[4]) ret++; break; case CMP_GREATER_THAN: if (value > gBattlescriptCurrInstr[4]) ret++; break; case CMP_LESS_THAN: if (value < gBattlescriptCurrInstr[4]) ret++; break; case CMP_COMMON_BITS: if (value & gBattlescriptCurrInstr[4]) ret++; break; case CMP_NO_COMMON_BITS: if (!(value & gBattlescriptCurrInstr[4])) ret++; break; } if (ret) gBattlescriptCurrInstr = T2_READ_PTR(gBattlescriptCurrInstr + 5); else gBattlescriptCurrInstr += 9; } static void Cmd_jumpifstatus3condition(void) { u32 status; const u8 *jumpPtr; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); status = T2_READ_32(gBattlescriptCurrInstr + 2); jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 7); if (gBattlescriptCurrInstr[6]) { if ((gStatuses3[gActiveBattler] & status) != 0) gBattlescriptCurrInstr += 11; else gBattlescriptCurrInstr = jumpPtr; } else { if ((gStatuses3[gActiveBattler] & status) != 0) gBattlescriptCurrInstr = jumpPtr; else gBattlescriptCurrInstr += 11; } } static void Cmd_jumpiftype(void) { u8 battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); u8 type = gBattlescriptCurrInstr[2]; const u8* jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 3); if (IS_BATTLER_OF_TYPE(battlerId, type)) gBattlescriptCurrInstr = jumpPtr; else gBattlescriptCurrInstr += 7; } static void Cmd_getexp(void) { u16 item; s32 i; // also used as stringId u8 holdEffect; s32 sentIn; s32 viaExpShare = 0; u16 *exp = &gBattleStruct->expValue; gBattlerFainted = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); sentIn = gSentPokesToOpponent[(gBattlerFainted & 2) >> 1]; switch (gBattleScripting.getexpState) { case 0: // check if should receive exp at all if (GetBattlerSide(gBattlerFainted) != B_SIDE_OPPONENT || (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_TRAINER_HILL | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_SAFARI | BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_EREADER_TRAINER))) { gBattleScripting.getexpState = 6; // goto last case } else { gBattleScripting.getexpState++; gBattleStruct->givenExpMons |= gBitTable[gBattlerPartyIndexes[gBattlerFainted]]; } break; case 1: // calculate experience points to redistribute { u16 calculatedExp; s32 viaSentIn; for (viaSentIn = 0, i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) == SPECIES_NONE || GetMonData(&gPlayerParty[i], MON_DATA_HP) == 0) continue; if (gBitTable[i] & sentIn) viaSentIn++; item = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); if (item == ITEM_ENIGMA_BERRY) holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect; else holdEffect = ItemId_GetHoldEffect(item); if (holdEffect == HOLD_EFFECT_EXP_SHARE) viaExpShare++; } calculatedExp = gBaseStats[gBattleMons[gBattlerFainted].species].expYield * gBattleMons[gBattlerFainted].level / 7; if (viaExpShare) // at least one mon is getting exp via exp share { *exp = SAFE_DIV(calculatedExp / 2, viaSentIn); if (*exp == 0) *exp = 1; gExpShareExp = calculatedExp / 2 / viaExpShare; if (gExpShareExp == 0) gExpShareExp = 1; } else { *exp = SAFE_DIV(calculatedExp, viaSentIn); if (*exp == 0) *exp = 1; gExpShareExp = 0; } gBattleScripting.getexpState++; gBattleStruct->expGetterMonId = 0; gBattleStruct->sentInPokes = sentIn; } // fall through case 2: // set exp value to the poke in expgetter_id and print message if (gBattleControllerExecFlags == 0) { item = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HELD_ITEM); if (item == ITEM_ENIGMA_BERRY) holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect; else holdEffect = ItemId_GetHoldEffect(item); if (holdEffect != HOLD_EFFECT_EXP_SHARE && !(gBattleStruct->sentInPokes & 1)) { *(&gBattleStruct->sentInPokes) >>= 1; gBattleScripting.getexpState = 5; gBattleMoveDamage = 0; // used for exp } else if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL) == MAX_LEVEL) { *(&gBattleStruct->sentInPokes) >>= 1; gBattleScripting.getexpState = 5; gBattleMoveDamage = 0; // used for exp } else { // music change in wild battle after fainting a poke if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER) && gBattleMons[0].hp && !gBattleStruct->wildVictorySong) { BattleStopLowHpSound(); PlayBGM(MUS_VICTORY_WILD); gBattleStruct->wildVictorySong++; } if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HP)) { if (gBattleStruct->sentInPokes & 1) gBattleMoveDamage = *exp; else gBattleMoveDamage = 0; if (holdEffect == HOLD_EFFECT_EXP_SHARE) gBattleMoveDamage += gExpShareExp; if (holdEffect == HOLD_EFFECT_LUCKY_EGG) gBattleMoveDamage = (gBattleMoveDamage * 150) / 100; if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) gBattleMoveDamage = (gBattleMoveDamage * 150) / 100; if (IsTradedMon(&gPlayerParty[gBattleStruct->expGetterMonId])) { // check if the pokemon doesn't belong to the player if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gBattleStruct->expGetterMonId >= 3) { i = STRINGID_EMPTYSTRING4; } else { gBattleMoveDamage = (gBattleMoveDamage * 150) / 100; i = STRINGID_ABOOSTED; } } else { i = STRINGID_EMPTYSTRING4; } // get exp getter battlerId if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { if (gBattlerPartyIndexes[2] == gBattleStruct->expGetterMonId && !(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, gBattleStruct->expGetterMonId); // buffer 'gained' or 'gained a boosted' PREPARE_STRING_BUFFER(gBattleTextBuff2, i); PREPARE_WORD_NUMBER_BUFFER(gBattleTextBuff3, 5, gBattleMoveDamage); PrepareStringBattle(STRINGID_PKMNGAINEDEXP, gBattleStruct->expGetterBattlerId); MonGainEVs(&gPlayerParty[gBattleStruct->expGetterMonId], gBattleMons[gBattlerFainted].species); } gBattleStruct->sentInPokes >>= 1; gBattleScripting.getexpState++; } } break; case 3: // Set stats and give exp if (gBattleControllerExecFlags == 0) { gBattleBufferB[gBattleStruct->expGetterBattlerId][0] = 0; if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HP) && GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL) != MAX_LEVEL) { gBattleResources->beforeLvlUp->stats[STAT_HP] = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_MAX_HP); gBattleResources->beforeLvlUp->stats[STAT_ATK] = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_ATK); gBattleResources->beforeLvlUp->stats[STAT_DEF] = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_DEF); gBattleResources->beforeLvlUp->stats[STAT_SPEED] = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPEED); gBattleResources->beforeLvlUp->stats[STAT_SPATK] = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPATK); gBattleResources->beforeLvlUp->stats[STAT_SPDEF] = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPDEF); gActiveBattler = gBattleStruct->expGetterBattlerId; BtlController_EmitExpUpdate(BUFFER_A, gBattleStruct->expGetterMonId, gBattleMoveDamage); MarkBattlerForControllerExec(gActiveBattler); } gBattleScripting.getexpState++; } break; case 4: // lvl up if necessary if (gBattleControllerExecFlags == 0) { gActiveBattler = gBattleStruct->expGetterBattlerId; if (gBattleBufferB[gActiveBattler][0] == CONTROLLER_TWORETURNVALUES && gBattleBufferB[gActiveBattler][1] == RET_VALUE_LEVELED_UP) { if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && gBattlerPartyIndexes[gActiveBattler] == gBattleStruct->expGetterMonId) HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gActiveBattler, gBattleStruct->expGetterMonId); PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff2, 3, GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL)); BattleScriptPushCursor(); gLeveledUpInBattle |= gBitTable[gBattleStruct->expGetterMonId]; gBattlescriptCurrInstr = BattleScript_LevelUp; gBattleMoveDamage = (gBattleBufferB[gActiveBattler][2] | (gBattleBufferB[gActiveBattler][3] << 8)); AdjustFriendship(&gPlayerParty[gBattleStruct->expGetterMonId], FRIENDSHIP_EVENT_GROW_LEVEL); // update battle mon structure after level up if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId && gBattleMons[0].hp) { gBattleMons[0].level = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL); gBattleMons[0].hp = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HP); gBattleMons[0].maxHP = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_MAX_HP); gBattleMons[0].attack = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_ATK); gBattleMons[0].defense = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_DEF); // Speed is duplicated, likely due to a copy-paste error. gBattleMons[0].speed = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPEED); gBattleMons[0].speed = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPEED); gBattleMons[0].spAttack = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPATK); gBattleMons[0].spDefense = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPDEF); } if (gBattlerPartyIndexes[2] == gBattleStruct->expGetterMonId && gBattleMons[2].hp && (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) { gBattleMons[2].level = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL); gBattleMons[2].hp = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HP); gBattleMons[2].maxHP = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_MAX_HP); gBattleMons[2].attack = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_ATK); gBattleMons[2].defense = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_DEF); gBattleMons[2].speed = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPEED); // Speed is duplicated again, but Special Defense is missing. #ifdef BUGFIX gBattleMons[2].spDefense = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPDEF); #else gBattleMons[2].speed = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPEED); #endif gBattleMons[2].spAttack = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPATK); } 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 { gBattleStruct->expGetterMonId++; if (gBattleStruct->expGetterMonId < PARTY_SIZE) gBattleScripting.getexpState = 2; // loop again else gBattleScripting.getexpState = 6; // we're done } break; case 6: // increment instruction if (gBattleControllerExecFlags == 0) { // not sure why gf clears the item and ability here gBattleMons[gBattlerFainted].item = 0; gBattleMons[gBattlerFainted].ability = 0; gBattlescriptCurrInstr += 2; } break; } } // For battles that aren't BATTLE_TYPE_LINK or BATTLE_TYPE_RECORDED_LINK, the only thing this // command does is check whether the player has won/lost by totaling each team's HP. It then // sets gBattleOutcome accordingly, if necessary. static void Cmd_checkteamslost(void) { u16 HP_count = 0; s32 i; if (gBattleControllerExecFlags) return; // Get total HP for the player's party to determine if the player has lost if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gPartnerTrainerId == TRAINER_STEVEN_PARTNER) { // In multi battle with Steven, skip his Pokémon for (i = 0; i < MULTI_PARTY_SIZE; i++) { if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) && !GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG)) HP_count += GetMonData(&gPlayerParty[i], MON_DATA_HP); } } else { for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) && !GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG) && (!(gBattleTypeFlags & BATTLE_TYPE_ARENA) || !(gBattleStruct->arenaLostPlayerMons & gBitTable[i]))) { HP_count += GetMonData(&gPlayerParty[i], MON_DATA_HP); } } } if (HP_count == 0) gBattleOutcome |= B_OUTCOME_LOST; HP_count = 0; // Get total HP for the enemy's party to determine if the player has won for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES) && !GetMonData(&gEnemyParty[i], MON_DATA_IS_EGG) && (!(gBattleTypeFlags & BATTLE_TYPE_ARENA) || !(gBattleStruct->arenaLostOpponentMons & gBitTable[i]))) { HP_count += GetMonData(&gEnemyParty[i], MON_DATA_HP); } } if (HP_count == 0) gBattleOutcome |= B_OUTCOME_WON; // For link battles that haven't ended, count number of empty battler spots // In link multi battles, jump to pointer if more than 1 spot empty // In non-multi battles, jump to pointer if 1 spot is missing on both sides if (gBattleOutcome == 0 && (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))) { s32 emptyPlayerSpots = 0; s32 emptyOpponentSpots; for (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 = T2_READ_PTR(gBattlescriptCurrInstr + 1); else gBattlescriptCurrInstr += 5; } else { if (emptyOpponentSpots != 0 && emptyPlayerSpots != 0) gBattlescriptCurrInstr = T2_READ_PTR(gBattlescriptCurrInstr + 1); else gBattlescriptCurrInstr += 5; } } else { gBattlescriptCurrInstr += 5; } } static void MoveValuesCleanUp(void) { gMoveResultFlags = 0; gBattleScripting.dmgMultiplier = 1; gCritMultiplier = 1; gBattleCommunication[MOVE_EFFECT_BYTE] = 0; gBattleCommunication[MISS_TYPE] = 0; gHitMarker &= ~HITMARKER_DESTINYBOND; gHitMarker &= ~HITMARKER_SYNCHRONISE_EFFECT; } static void Cmd_movevaluescleanup(void) { MoveValuesCleanUp(); gBattlescriptCurrInstr += 1; } static void Cmd_setmultihit(void) { gMultiHitCounter = gBattlescriptCurrInstr[1]; gBattlescriptCurrInstr += 2; } static void Cmd_decrementmultihit(void) { if (--gMultiHitCounter == 0) gBattlescriptCurrInstr += 5; else gBattlescriptCurrInstr = T2_READ_PTR(gBattlescriptCurrInstr + 1); } static void Cmd_goto(void) { gBattlescriptCurrInstr = T2_READ_PTR(gBattlescriptCurrInstr + 1); } static void Cmd_jumpifbyte(void) { u8 caseID = gBattlescriptCurrInstr[1]; const u8* memByte = T2_READ_PTR(gBattlescriptCurrInstr + 2); u8 value = gBattlescriptCurrInstr[6]; const u8* jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 7); gBattlescriptCurrInstr += 11; switch (caseID) { case CMP_EQUAL: if (*memByte == value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_NOT_EQUAL: if (*memByte != value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_GREATER_THAN: if (*memByte > value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_LESS_THAN: if (*memByte < value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_COMMON_BITS: if (*memByte & value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_NO_COMMON_BITS: if (!(*memByte & value)) gBattlescriptCurrInstr = jumpPtr; break; } } static void Cmd_jumpifhalfword(void) { u8 caseID = gBattlescriptCurrInstr[1]; const u16* memHword = T2_READ_PTR(gBattlescriptCurrInstr + 2); u16 value = T2_READ_16(gBattlescriptCurrInstr + 6); const u8* jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 8); gBattlescriptCurrInstr += 12; switch (caseID) { case CMP_EQUAL: if (*memHword == value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_NOT_EQUAL: if (*memHword != value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_GREATER_THAN: if (*memHword > value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_LESS_THAN: if (*memHword < value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_COMMON_BITS: if (*memHword & value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_NO_COMMON_BITS: if (!(*memHword & value)) gBattlescriptCurrInstr = jumpPtr; break; } } static void Cmd_jumpifword(void) { u8 caseID = gBattlescriptCurrInstr[1]; const u32* memWord = T2_READ_PTR(gBattlescriptCurrInstr + 2); u32 value = T1_READ_32(gBattlescriptCurrInstr + 6); const u8* jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 10); gBattlescriptCurrInstr += 14; switch (caseID) { case CMP_EQUAL: if (*memWord == value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_NOT_EQUAL: if (*memWord != value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_GREATER_THAN: if (*memWord > value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_LESS_THAN: if (*memWord < value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_COMMON_BITS: if (*memWord & value) gBattlescriptCurrInstr = jumpPtr; break; case CMP_NO_COMMON_BITS: if (!(*memWord & value)) gBattlescriptCurrInstr = jumpPtr; break; } } static void Cmd_jumpifarrayequal(void) { const u8* mem1 = T2_READ_PTR(gBattlescriptCurrInstr + 1); const u8* mem2 = T2_READ_PTR(gBattlescriptCurrInstr + 5); u32 size = gBattlescriptCurrInstr[9]; const u8* jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 10); u8 i; for (i = 0; i < size; i++) { if (*mem1 != *mem2) { gBattlescriptCurrInstr += 14; break; } mem1++, mem2++; } if (i == size) gBattlescriptCurrInstr = jumpPtr; } static void Cmd_jumpifarraynotequal(void) { u8 equalBytes = 0; const u8* mem1 = T2_READ_PTR(gBattlescriptCurrInstr + 1); const u8* mem2 = T2_READ_PTR(gBattlescriptCurrInstr + 5); u32 size = gBattlescriptCurrInstr[9]; const u8* jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 10); u8 i; for (i = 0; i < size; i++) { if (*mem1 == *mem2) equalBytes++; mem1++, mem2++; } if (equalBytes != size) gBattlescriptCurrInstr = jumpPtr; else gBattlescriptCurrInstr += 14; } static void Cmd_setbyte(void) { u8* memByte = T2_READ_PTR(gBattlescriptCurrInstr + 1); *memByte = gBattlescriptCurrInstr[5]; gBattlescriptCurrInstr += 6; } static void Cmd_addbyte(void) { u8* memByte = T2_READ_PTR(gBattlescriptCurrInstr + 1); *memByte += gBattlescriptCurrInstr[5]; gBattlescriptCurrInstr += 6; } static void Cmd_subbyte(void) { u8* memByte = T2_READ_PTR(gBattlescriptCurrInstr + 1); *memByte -= gBattlescriptCurrInstr[5]; gBattlescriptCurrInstr += 6; } static void Cmd_copyarray(void) { u8* dest = T2_READ_PTR(gBattlescriptCurrInstr + 1); const u8* src = T2_READ_PTR(gBattlescriptCurrInstr + 5); s32 size = gBattlescriptCurrInstr[9]; s32 i; for (i = 0; i < size; i++) dest[i] = src[i]; gBattlescriptCurrInstr += 10; } static void Cmd_copyarraywithindex(void) { u8* dest = T2_READ_PTR(gBattlescriptCurrInstr + 1); const u8* src = T2_READ_PTR(gBattlescriptCurrInstr + 5); const u8* index = T2_READ_PTR(gBattlescriptCurrInstr + 9); s32 size = gBattlescriptCurrInstr[13]; s32 i; for (i = 0; i < size; i++) dest[i] = src[i + *index]; gBattlescriptCurrInstr += 14; } static void Cmd_orbyte(void) { u8* memByte = T2_READ_PTR(gBattlescriptCurrInstr + 1); *memByte |= gBattlescriptCurrInstr[5]; gBattlescriptCurrInstr += 6; } static void Cmd_orhalfword(void) { u16* memHword = T2_READ_PTR(gBattlescriptCurrInstr + 1); u16 val = T2_READ_16(gBattlescriptCurrInstr + 5); *memHword |= val; gBattlescriptCurrInstr += 7; } static void Cmd_orword(void) { u32* memWord = T2_READ_PTR(gBattlescriptCurrInstr + 1); u32 val = T2_READ_32(gBattlescriptCurrInstr + 5); *memWord |= val; gBattlescriptCurrInstr += 9; } static void Cmd_bicbyte(void) { u8* memByte = T2_READ_PTR(gBattlescriptCurrInstr + 1); *memByte &= ~(gBattlescriptCurrInstr[5]); gBattlescriptCurrInstr += 6; } static void Cmd_bichalfword(void) { u16* memHword = T2_READ_PTR(gBattlescriptCurrInstr + 1); u16 val = T2_READ_16(gBattlescriptCurrInstr + 5); *memHword &= ~val; gBattlescriptCurrInstr += 7; } static void Cmd_bicword(void) { u32* memWord = T2_READ_PTR(gBattlescriptCurrInstr + 1); u32 val = T2_READ_32(gBattlescriptCurrInstr + 5); *memWord &= ~val; gBattlescriptCurrInstr += 9; } static void Cmd_pause(void) { if (gBattleControllerExecFlags == 0) { u16 value = T2_READ_16(gBattlescriptCurrInstr + 1); if (++gPauseCounterBattle >= value) { gPauseCounterBattle = 0; gBattlescriptCurrInstr += 3; } } } static void Cmd_waitstate(void) { if (gBattleControllerExecFlags == 0) gBattlescriptCurrInstr++; } static void Cmd_healthbar_update(void) { if (gBattlescriptCurrInstr[1] == BS_TARGET) gActiveBattler = gBattlerTarget; else gActiveBattler = gBattlerAttacker; BtlController_EmitHealthBarUpdate(BUFFER_A, gBattleMoveDamage); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 2; } static void Cmd_return(void) { BattleScriptPop(); } static void Cmd_end(void) { if (gBattleTypeFlags & BATTLE_TYPE_ARENA) BattleArena_AddSkillPoints(gBattlerAttacker); gMoveResultFlags = 0; gActiveBattler = 0; gCurrentActionFuncId = B_ACTION_TRY_FINISH; } static void Cmd_end2(void) { gActiveBattler = 0; gCurrentActionFuncId = B_ACTION_TRY_FINISH; } static void Cmd_end3(void) // pops the main function stack { BattleScriptPop(); if (gBattleResources->battleCallbackStack->size != 0) gBattleResources->battleCallbackStack->size--; gBattleMainFunc = gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size]; } static void Cmd_call(void) { BattleScriptPush(gBattlescriptCurrInstr + 5); gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } static void Cmd_jumpiftype2(void) { u8 battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); if (gBattlescriptCurrInstr[2] == gBattleMons[battlerId].type1 || gBattlescriptCurrInstr[2] == gBattleMons[battlerId].type2) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); else gBattlescriptCurrInstr += 7; } static void Cmd_jumpifabilitypresent(void) { if (AbilityBattleEffects(ABILITYEFFECT_CHECK_ON_FIELD, 0, gBattlescriptCurrInstr[1], 0, 0)) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); else gBattlescriptCurrInstr += 6; } static void Cmd_endselectionscript(void) { *(gBattlerAttacker + gBattleStruct->selectionScriptFinished) = TRUE; } static void Cmd_playanimation(void) { const u16* argumentPtr; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); argumentPtr = T2_READ_PTR(gBattlescriptCurrInstr + 3); if (gBattlescriptCurrInstr[2] == B_ANIM_STATS_CHANGE || gBattlescriptCurrInstr[2] == B_ANIM_SNATCH_MOVE || gBattlescriptCurrInstr[2] == B_ANIM_SUBSTITUTE_FADE) { BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], *argumentPtr); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 7; } else if (gHitMarker & HITMARKER_NO_ANIMATIONS) { BattleScriptPush(gBattlescriptCurrInstr + 7); gBattlescriptCurrInstr = BattleScript_Pausex20; } else if (gBattlescriptCurrInstr[2] == B_ANIM_RAIN_CONTINUES || gBattlescriptCurrInstr[2] == B_ANIM_SUN_CONTINUES || gBattlescriptCurrInstr[2] == B_ANIM_SANDSTORM_CONTINUES || gBattlescriptCurrInstr[2] == B_ANIM_HAIL_CONTINUES) { BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], *argumentPtr); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 7; } else if (gStatuses3[gActiveBattler] & STATUS3_SEMI_INVULNERABLE) { gBattlescriptCurrInstr += 7; } else { BtlController_EmitBattleAnimation(BUFFER_A, gBattlescriptCurrInstr[2], *argumentPtr); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 7; } } // Same as playanimation, expect it takes a pointer to some animation id, instead of taking the value directly static void Cmd_playanimation_var(void) { const u16* argumentPtr; const u8* animationIdPtr; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); animationIdPtr = T2_READ_PTR(gBattlescriptCurrInstr + 2); argumentPtr = T2_READ_PTR(gBattlescriptCurrInstr + 6); if (*animationIdPtr == B_ANIM_STATS_CHANGE || *animationIdPtr == B_ANIM_SNATCH_MOVE || *animationIdPtr == B_ANIM_SUBSTITUTE_FADE) { BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, *argumentPtr); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 10; } else if (gHitMarker & HITMARKER_NO_ANIMATIONS) { gBattlescriptCurrInstr += 10; } else if (*animationIdPtr == B_ANIM_RAIN_CONTINUES || *animationIdPtr == B_ANIM_SUN_CONTINUES || *animationIdPtr == B_ANIM_SANDSTORM_CONTINUES || *animationIdPtr == B_ANIM_HAIL_CONTINUES) { BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, *argumentPtr); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 10; } else if (gStatuses3[gActiveBattler] & STATUS3_SEMI_INVULNERABLE) { gBattlescriptCurrInstr += 10; } else { BtlController_EmitBattleAnimation(BUFFER_A, *animationIdPtr, *argumentPtr); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 10; } } static void Cmd_setgraphicalstatchangevalues(void) { u8 value = 0; switch (GET_STAT_BUFF_VALUE2(gBattleScripting.statChanger)) { case SET_STAT_BUFF_VALUE(1): // +1 value = STAT_ANIM_PLUS1; break; case SET_STAT_BUFF_VALUE(2): // +2 value = STAT_ANIM_PLUS2; break; case SET_STAT_BUFF_VALUE(1) | STAT_BUFF_NEGATIVE: // -1 value = STAT_ANIM_MINUS1; break; case SET_STAT_BUFF_VALUE(2) | STAT_BUFF_NEGATIVE: // -2 value = STAT_ANIM_MINUS2; break; } gBattleScripting.animArg1 = GET_STAT_BUFF_ID(gBattleScripting.statChanger) + value - 1; gBattleScripting.animArg2 = 0; gBattlescriptCurrInstr++; } static void Cmd_playstatchangeanimation(void) { u32 currStat = 0; u16 statAnimId = 0; s32 changeableStatsCount = 0; u8 statsToCheck = 0; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); statsToCheck = gBattlescriptCurrInstr[2]; if (gBattlescriptCurrInstr[3] & STAT_CHANGE_NEGATIVE) // goes down { s16 startingStatAnimId; if (gBattlescriptCurrInstr[3] & STAT_CHANGE_BY_TWO) startingStatAnimId = STAT_ANIM_MINUS2 - 1; else startingStatAnimId = STAT_ANIM_MINUS1 - 1; while (statsToCheck != 0) { if (statsToCheck & 1) { if (gBattlescriptCurrInstr[3] & STAT_CHANGE_CANT_PREVENT) { if (gBattleMons[gActiveBattler].statStages[currStat] > MIN_STAT_STAGE) { statAnimId = startingStatAnimId + currStat; changeableStatsCount++; } } else if (!gSideTimers[GET_BATTLER_SIDE(gActiveBattler)].mistTimer && gBattleMons[gActiveBattler].ability != ABILITY_CLEAR_BODY && gBattleMons[gActiveBattler].ability != ABILITY_WHITE_SMOKE && !(gBattleMons[gActiveBattler].ability == ABILITY_KEEN_EYE && currStat == STAT_ACC) && !(gBattleMons[gActiveBattler].ability == ABILITY_HYPER_CUTTER && currStat == STAT_ATK)) { if (gBattleMons[gActiveBattler].statStages[currStat] > MIN_STAT_STAGE) { statAnimId = startingStatAnimId + currStat; changeableStatsCount++; } } } statsToCheck >>= 1, currStat++; } if (changeableStatsCount > 1) // more than one stat, so the color is gray { if (gBattlescriptCurrInstr[3] & STAT_CHANGE_BY_TWO) statAnimId = STAT_ANIM_MULTIPLE_MINUS2; else statAnimId = STAT_ANIM_MULTIPLE_MINUS1; } } else // goes up { s16 startingStatAnimId; if (gBattlescriptCurrInstr[3] & STAT_CHANGE_BY_TWO) startingStatAnimId = STAT_ANIM_PLUS2 - 1; else startingStatAnimId = STAT_ANIM_PLUS1 - 1; while (statsToCheck != 0) { if (statsToCheck & 1 && gBattleMons[gActiveBattler].statStages[currStat] < MAX_STAT_STAGE) { statAnimId = startingStatAnimId + currStat; changeableStatsCount++; } statsToCheck >>= 1, currStat++; } if (changeableStatsCount > 1) // more than one stat, so the color is gray { if (gBattlescriptCurrInstr[3] & STAT_CHANGE_BY_TWO) statAnimId = STAT_ANIM_MULTIPLE_PLUS2; else statAnimId = STAT_ANIM_MULTIPLE_PLUS1; } } if (gBattlescriptCurrInstr[3] & STAT_CHANGE_MULTIPLE_STATS && changeableStatsCount < 2) { gBattlescriptCurrInstr += 4; } else if (changeableStatsCount != 0 && !gBattleScripting.statAnimPlayed) { BtlController_EmitBattleAnimation(BUFFER_A, B_ANIM_STATS_CHANGE, statAnimId); MarkBattlerForControllerExec(gActiveBattler); if (gBattlescriptCurrInstr[3] & STAT_CHANGE_MULTIPLE_STATS && changeableStatsCount > 1) gBattleScripting.statAnimPlayed = TRUE; gBattlescriptCurrInstr += 4; } else { gBattlescriptCurrInstr += 4; } } static void Cmd_moveend(void) { s32 i; bool32 effect = FALSE; u8 moveType = 0; u8 holdEffectAtk = 0; u16 *choicedMoveAtk = NULL; u8 endMode, endState; u16 originallyUsedMove; if (gChosenMove == 0xFFFF) originallyUsedMove = 0; else originallyUsedMove = gChosenMove; endMode = gBattlescriptCurrInstr[1]; endState = gBattlescriptCurrInstr[2]; if (gBattleMons[gBattlerAttacker].item == ITEM_ENIGMA_BERRY) holdEffectAtk = gEnigmaBerries[gBattlerAttacker].holdEffect; else holdEffectAtk = ItemId_GetHoldEffect(gBattleMons[gBattlerAttacker].item); choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker]; GET_MOVE_TYPE(gCurrentMove, moveType); do { switch (gBattleScripting.moveendState) { case MOVEEND_RAGE: // rage check if (gBattleMons[gBattlerTarget].status2 & STATUS2_RAGE && gBattleMons[gBattlerTarget].hp != 0 && gBattlerAttacker != gBattlerTarget && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget) && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && gBattleMoves[gCurrentMove].power && gBattleMons[gBattlerTarget].statStages[STAT_ATK] < MAX_STAT_STAGE) { gBattleMons[gBattlerTarget].statStages[STAT_ATK]++; 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 && gSpecialStatuses[gBattlerTarget].specialDmg && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && moveType == TYPE_FIRE) { gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FREEZE; gActiveBattler = gBattlerTarget; BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1); MarkBattlerForControllerExec(gActiveBattler); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_DefrostedViaFireMove; 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_ON_DAMAGE_ABILITIES: // Contact abilities and Color Change if (AbilityBattleEffects(ABILITYEFFECT_ON_DAMAGE, gBattlerTarget, 0, 0, 0)) effect = TRUE; gBattleScripting.moveendState++; break; case MOVEEND_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 && holdEffectAtk == HOLD_EFFECT_CHOICE_BAND && gChosenMove != MOVE_STRUGGLE && (*choicedMoveAtk == 0 || *choicedMoveAtk == 0xFFFF)) { if (gChosenMove == MOVE_BATON_PASS && !(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 = 0; ++gBattleScripting.moveendState; break; case MOVEEND_CHANGED_ITEMS: // changed held items for (i = 0; i < gBattlersCount; i++) { u16* changedItem = &gBattleStruct->changedItems[i]; if (*changedItem != 0) { gBattleMons[i].item = *changedItem; *changedItem = 0; } } gBattleScripting.moveendState++; break; 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_SHELLBELL: // king's rock and shell bell if (ItemBattleEffects(ITEMEFFECT_KINGSROCK_SHELLBELL, 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) { gActiveBattler = gBattlerAttacker; BtlController_EmitSpriteInvisibility(BUFFER_A, TRUE); MarkBattlerForControllerExec(gActiveBattler); 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(gActiveBattler); gStatuses3[gBattlerAttacker] &= ~STATUS3_SEMI_INVULNERABLE; gSpecialStatuses[gBattlerAttacker].restoredBattlerSprite = 1; 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(gActiveBattler); gStatuses3[gBattlerTarget] &= ~STATUS3_SEMI_INVULNERABLE; gBattleScripting.moveendState++; return; } 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_UPDATE_LAST_MOVES: if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET) { gActiveBattler = gBattlerAttacker; gBattlerAttacker = gBattlerTarget; gBattlerTarget = gActiveBattler; gHitMarker &= ~HITMARKER_SWAP_ATTACKER_TARGET; } if (gHitMarker & HITMARKER_ATTACKSTRING_PRINTED) { gLastPrintedMoves[gBattlerAttacker] = gChosenMove; } if (!(gAbsentBattlerFlags & gBitTable[gBattlerAttacker]) && !(gBattleStruct->absentBattlerFlags & gBitTable[gBattlerAttacker]) && gBattleMoves[originallyUsedMove].effect != EFFECT_BATON_PASS) { if (gHitMarker & HITMARKER_OBEYS) { gLastMoves[gBattlerAttacker] = gChosenMove; gLastResultingMoves[gBattlerAttacker] = gCurrentMove; } else { gLastMoves[gBattlerAttacker] = 0xFFFF; gLastResultingMoves[gBattlerAttacker] = 0xFFFF; } if (!(gHitMarker & HITMARKER_FAINTED(gBattlerTarget))) gLastHitBy[gBattlerTarget] = gBattlerAttacker; if (gHitMarker & HITMARKER_OBEYS && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { if (gChosenMove == 0xFFFF) { gLastLandedMoves[gBattlerTarget] = gChosenMove; } else { gLastLandedMoves[gBattlerTarget] = gCurrentMove; GET_MOVE_TYPE(gCurrentMove, gLastHitByType[gBattlerTarget]); } } else { gLastLandedMoves[gBattlerTarget] = 0xFFFF; } } gBattleScripting.moveendState++; break; case MOVEEND_MIRROR_MOVE: // mirror move if (!(gAbsentBattlerFlags & gBitTable[gBattlerAttacker]) && !(gBattleStruct->absentBattlerFlags & gBitTable[gBattlerAttacker]) && gBattleMoves[originallyUsedMove].flags & FLAG_MIRROR_MOVE_AFFECTED && gHitMarker & HITMARKER_OBEYS && gBattlerAttacker != gBattlerTarget && !(gHitMarker & HITMARKER_FAINTED(gBattlerTarget)) && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { u8 target, attacker; *(gBattleStruct->lastTakenMove + gBattlerTarget * 2 + 0) = gChosenMove; *(gBattleStruct->lastTakenMove + gBattlerTarget * 2 + 1) = gChosenMove >> 8; target = gBattlerTarget; attacker = gBattlerAttacker; *(attacker * 2 + target * 8 + (u8*)(gBattleStruct->lastTakenMoveFrom) + 0) = gChosenMove; target = gBattlerTarget; attacker = gBattlerAttacker; *(attacker * 2 + target * 8 + (u8*)(gBattleStruct->lastTakenMoveFrom) + 1) = gChosenMove >> 8; } gBattleScripting.moveendState++; break; case MOVEEND_NEXT_TARGET: // For moves hitting two opposing Pokemon. if (!(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) && gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !gProtectStructs[gBattlerAttacker].chargingTurn && gBattleMoves[gCurrentMove].target == MOVE_TARGET_BOTH && !(gHitMarker & HITMARKER_NO_ATTACKSTRING)) { u8 battlerId = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget))); if (gBattleMons[battlerId].hp != 0) { gBattlerTarget = battlerId; gHitMarker |= HITMARKER_NO_ATTACKSTRING; gBattleScripting.moveendState = 0; MoveValuesCleanUp(); BattleScriptPush(gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]); gBattlescriptCurrInstr = BattleScript_FlushMessageBox; return; } else { gHitMarker |= HITMARKER_NO_ATTACKSTRING; } } 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 += 3; } static void Cmd_typecalc2(void) { u8 flags = 0; s32 i = 0; u8 moveType = gBattleMoves[gCurrentMove].type; if (gBattleMons[gBattlerTarget].ability == ABILITY_LEVITATE && moveType == TYPE_GROUND) { gLastUsedAbility = gBattleMons[gBattlerTarget].ability; gMoveResultFlags |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE); gLastLandedMoves[gBattlerTarget] = 0; gBattleCommunication[MISS_TYPE] = B_MSG_GROUND_MISS; RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); } else { while (TYPE_EFFECT_ATK_TYPE(i) != TYPE_ENDTABLE) { if (TYPE_EFFECT_ATK_TYPE(i) == TYPE_FORESIGHT) { if (gBattleMons[gBattlerTarget].status2 & STATUS2_FORESIGHT) { break; } else { i += 3; continue; } } if (TYPE_EFFECT_ATK_TYPE(i) == moveType) { // check type1 if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type1) { if (TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NO_EFFECT) { gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE; break; } if (TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NOT_EFFECTIVE) { flags |= MOVE_RESULT_NOT_VERY_EFFECTIVE; } if (TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_SUPER_EFFECTIVE) { flags |= MOVE_RESULT_SUPER_EFFECTIVE; } } // check type2 if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2) { if (gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2 && TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NO_EFFECT) { gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE; break; } if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2 && gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2 && TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NOT_EFFECTIVE) { flags |= MOVE_RESULT_NOT_VERY_EFFECTIVE; } if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2 && gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2 && TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_SUPER_EFFECTIVE) { flags |= MOVE_RESULT_SUPER_EFFECTIVE; } } } i += 3; } } if (gBattleMons[gBattlerTarget].ability == ABILITY_WONDER_GUARD && !(flags & MOVE_RESULT_NO_EFFECT) && AttacksThisTurn(gBattlerAttacker, gCurrentMove) == 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))) && gBattleMoves[gCurrentMove].power) { gLastUsedAbility = ABILITY_WONDER_GUARD; gMoveResultFlags |= MOVE_RESULT_MISSED; gLastLandedMoves[gBattlerTarget] = 0; gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_DMG; RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); } if (gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE) gProtectStructs[gBattlerAttacker].targetNotAffected = 1; gBattlescriptCurrInstr++; } static void Cmd_returnatktoball(void) { gActiveBattler = gBattlerAttacker; if (!(gHitMarker & HITMARKER_FAINTED(gActiveBattler))) { BtlController_EmitReturnMonToBall(BUFFER_A, FALSE); MarkBattlerForControllerExec(gActiveBattler); } gBattlescriptCurrInstr++; } static void Cmd_getswitchedmondata(void) { if (gBattleControllerExecFlags) return; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); gBattlerPartyIndexes[gActiveBattler] = *(gBattleStruct->monToSwitchIntoId + gActiveBattler); BtlController_EmitGetMonData(BUFFER_A, REQUEST_ALL_BATTLE, gBitTable[gBattlerPartyIndexes[gActiveBattler]]); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 2; } static void Cmd_switchindataupdate(void) { struct BattlePokemon oldData; s32 i; u8 *monData; if (gBattleControllerExecFlags) return; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); oldData = gBattleMons[gActiveBattler]; monData = (u8*)(&gBattleMons[gActiveBattler]); for (i = 0; i < sizeof(struct BattlePokemon); i++) monData[i] = gBattleBufferB[gActiveBattler][4 + i]; gBattleMons[gActiveBattler].type1 = gBaseStats[gBattleMons[gActiveBattler].species].type1; gBattleMons[gActiveBattler].type2 = gBaseStats[gBattleMons[gActiveBattler].species].type2; gBattleMons[gActiveBattler].ability = GetAbilityBySpecies(gBattleMons[gActiveBattler].species, gBattleMons[gActiveBattler].abilityNum); // check knocked off item i = GetBattlerSide(gActiveBattler); if (gWishFutureKnock.knockedOffMons[i] & gBitTable[gBattlerPartyIndexes[gActiveBattler]]) { gBattleMons[gActiveBattler].item = 0; } if (gBattleMoves[gCurrentMove].effect == EFFECT_BATON_PASS) { for (i = 0; i < NUM_BATTLE_STATS; i++) { gBattleMons[gActiveBattler].statStages[i] = oldData.statStages[i]; } gBattleMons[gActiveBattler].status2 = oldData.status2; } SwitchInClearSetData(); if (gBattleTypeFlags & BATTLE_TYPE_PALACE && gBattleMons[gActiveBattler].maxHP / 2 >= gBattleMons[gActiveBattler].hp && gBattleMons[gActiveBattler].hp != 0 && !(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP)) { gBattleStruct->palaceFlags |= gBitTable[gActiveBattler]; } gBattleScripting.battler = gActiveBattler; PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, gActiveBattler, gBattlerPartyIndexes[gActiveBattler]); gBattlescriptCurrInstr += 2; } static void Cmd_switchinanim(void) { if (gBattleControllerExecFlags) return; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); if (GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_TRAINER_HILL | BATTLE_TYPE_FRONTIER))) HandleSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gActiveBattler].species), FLAG_SET_SEEN, gBattleMons[gActiveBattler].personality); gAbsentBattlerFlags &= ~(gBitTable[gActiveBattler]); BtlController_EmitSwitchInAnim(BUFFER_A, gBattlerPartyIndexes[gActiveBattler], gBattlescriptCurrInstr[2]); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 3; if (gBattleTypeFlags & BATTLE_TYPE_ARENA) BattleArena_InitPoints(); } static void Cmd_jumpifcantswitch(void) { s32 i; s32 lastMonId; struct Pokemon *party; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1] & ~SWITCH_IGNORE_ESCAPE_PREVENTION); if (!(gBattlescriptCurrInstr[1] & SWITCH_IGNORE_ESCAPE_PREVENTION) && ((gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION)) || (gStatuses3[gActiveBattler] & STATUS3_ROOTED))) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); } else if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { if (GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT) party = gEnemyParty; else party = gPlayerParty; lastMonId = 0; if (gActiveBattler & 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[gActiveBattler] != i) break; } if (i == lastMonId + MULTI_PARTY_SIZE) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); else gBattlescriptCurrInstr += 6; } else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { if (gBattleTypeFlags & BATTLE_TYPE_TOWER_LINK_MULTI) { if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) { party = gPlayerParty; lastMonId = 0; if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(gActiveBattler)) == TRUE) lastMonId = MULTI_PARTY_SIZE; } else { party = gEnemyParty; if (gActiveBattler == 1) lastMonId = 0; else lastMonId = MULTI_PARTY_SIZE; } } else { if (GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT) party = gEnemyParty; else party = gPlayerParty; lastMonId = 0; if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(gActiveBattler)) == 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[gActiveBattler] != i) break; } if (i == lastMonId + MULTI_PARTY_SIZE) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); else gBattlescriptCurrInstr += 6; } else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT) { party = gEnemyParty; lastMonId = 0; if (gActiveBattler == 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[gActiveBattler] != i) break; } if (i == lastMonId + (PARTY_SIZE / 2)) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); else gBattlescriptCurrInstr += 6; } else { u8 battlerIn1, battlerIn2; if (GetBattlerSide(gActiveBattler) == 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 { 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; } if (i == PARTY_SIZE) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); else gBattlescriptCurrInstr += 6; } } // 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(u8 slotId) { *(gBattleStruct->battlerPartyIndexes + gActiveBattler) = gBattlerPartyIndexes[gActiveBattler]; *(gBattleStruct->monToSwitchIntoId + gActiveBattler) = PARTY_SIZE; gBattleStruct->field_93 &= ~(gBitTable[gActiveBattler]); BtlController_EmitChoosePokemon(BUFFER_A, PARTY_ACTION_SEND_OUT, slotId, ABILITY_NONE, gBattleStruct->battlerPartyOrders[gActiveBattler]); MarkBattlerForControllerExec(gActiveBattler); } static void Cmd_openpartyscreen(void) { u32 flags; u8 hitmarkerFaintBits; u8 battlerId; const u8 *jumpPtr; battlerId = 0; flags = 0; jumpPtr = T1_READ_PTR(gBattlescriptCurrInstr + 2); if (gBattlescriptCurrInstr[1] == BS_FAINTED_LINK_MULTIPLE_1) { if ((gBattleTypeFlags & BATTLE_TYPE_MULTI) || !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) { for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++) { if (gHitMarker & HITMARKER_FAINTED(gActiveBattler)) { if (HasNoMonsToSwitch(gActiveBattler, PARTY_SIZE, PARTY_SIZE)) { gAbsentBattlerFlags |= gBitTable[gActiveBattler]; gHitMarker &= ~HITMARKER_FAINTED(gActiveBattler); BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); MarkBattlerForControllerExec(gActiveBattler); } else if (!gSpecialStatuses[gActiveBattler].faintedHasReplacement) { ChooseMonToSendOut(PARTY_SIZE); gSpecialStatuses[gActiveBattler].faintedHasReplacement = TRUE; } } else { BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); MarkBattlerForControllerExec(gActiveBattler); } } } else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { bool8 hasReplacement_0, hasReplacement_1, hasReplacement_2, hasReplacement_3; hitmarkerFaintBits = gHitMarker >> 28; if (gBitTable[0] & hitmarkerFaintBits) { gActiveBattler = 0; if (HasNoMonsToSwitch(gActiveBattler, PARTY_SIZE, PARTY_SIZE)) { gAbsentBattlerFlags |= gBitTable[gActiveBattler]; gHitMarker &= ~HITMARKER_FAINTED(gActiveBattler); BtlController_EmitCantSwitch(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); } else if (!gSpecialStatuses[gActiveBattler].faintedHasReplacement) { ChooseMonToSendOut(gBattleStruct->monToSwitchIntoId[2]); gSpecialStatuses[gActiveBattler].faintedHasReplacement = TRUE; } else { BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); MarkBattlerForControllerExec(gActiveBattler); flags |= 1; } } if (gBitTable[2] & hitmarkerFaintBits && !(gBitTable[0] & hitmarkerFaintBits)) { gActiveBattler = 2; if (HasNoMonsToSwitch(gActiveBattler, PARTY_SIZE, PARTY_SIZE)) { gAbsentBattlerFlags |= gBitTable[gActiveBattler]; gHitMarker &= ~HITMARKER_FAINTED(gActiveBattler); BtlController_EmitCantSwitch(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); } else if (!gSpecialStatuses[gActiveBattler].faintedHasReplacement) { ChooseMonToSendOut(gBattleStruct->monToSwitchIntoId[0]); gSpecialStatuses[gActiveBattler].faintedHasReplacement = TRUE; } else if (!(flags & 1)) { BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); MarkBattlerForControllerExec(gActiveBattler); } } if (gBitTable[1] & hitmarkerFaintBits) { gActiveBattler = 1; if (HasNoMonsToSwitch(gActiveBattler, PARTY_SIZE, PARTY_SIZE)) { gAbsentBattlerFlags |= gBitTable[gActiveBattler]; gHitMarker &= ~HITMARKER_FAINTED(gActiveBattler); BtlController_EmitCantSwitch(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); } else if (!gSpecialStatuses[gActiveBattler].faintedHasReplacement) { ChooseMonToSendOut(gBattleStruct->monToSwitchIntoId[3]); gSpecialStatuses[gActiveBattler].faintedHasReplacement = TRUE; } else { BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); MarkBattlerForControllerExec(gActiveBattler); flags |= 2; } } if (gBitTable[3] & hitmarkerFaintBits && !(gBitTable[1] & hitmarkerFaintBits)) { gActiveBattler = 3; if (HasNoMonsToSwitch(gActiveBattler, PARTY_SIZE, PARTY_SIZE)) { gAbsentBattlerFlags |= gBitTable[gActiveBattler]; gHitMarker &= ~HITMARKER_FAINTED(gActiveBattler); BtlController_EmitCantSwitch(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); } else if (!gSpecialStatuses[gActiveBattler].faintedHasReplacement) { ChooseMonToSendOut(gBattleStruct->monToSwitchIntoId[1]); gSpecialStatuses[gActiveBattler].faintedHasReplacement = TRUE; } else if (!(flags & 2)) { BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); MarkBattlerForControllerExec(gActiveBattler); } } hasReplacement_0 = gSpecialStatuses[0].faintedHasReplacement; if (!hasReplacement_0) { hasReplacement_2 = gSpecialStatuses[2].faintedHasReplacement; if (!hasReplacement_2 && hitmarkerFaintBits != 0) { if (gAbsentBattlerFlags & gBitTable[0]) gActiveBattler = 2; else gActiveBattler = 0; BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); MarkBattlerForControllerExec(gActiveBattler); } } hasReplacement_1 = gSpecialStatuses[1].faintedHasReplacement; if (!hasReplacement_1) { hasReplacement_3 = gSpecialStatuses[3].faintedHasReplacement; if (!hasReplacement_3 && hitmarkerFaintBits != 0) { if (gAbsentBattlerFlags & gBitTable[1]) gActiveBattler = 3; else gActiveBattler = 1; BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); MarkBattlerForControllerExec(gActiveBattler); } } } gBattlescriptCurrInstr += 6; } else if (gBattlescriptCurrInstr[1] == 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) { gActiveBattler = 2; if (HasNoMonsToSwitch(gActiveBattler, gBattleBufferB[0][1], PARTY_SIZE)) { gAbsentBattlerFlags |= gBitTable[gActiveBattler]; gHitMarker &= ~HITMARKER_FAINTED(gActiveBattler); BtlController_EmitCantSwitch(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); } else if (!gSpecialStatuses[gActiveBattler].faintedHasReplacement) { ChooseMonToSendOut(gBattleStruct->monToSwitchIntoId[0]); gSpecialStatuses[gActiveBattler].faintedHasReplacement = TRUE; } } if (gBitTable[3] & hitmarkerFaintBits && hitmarkerFaintBits & gBitTable[1]) { gActiveBattler = 3; if (HasNoMonsToSwitch(gActiveBattler, gBattleBufferB[1][1], PARTY_SIZE)) { gAbsentBattlerFlags |= gBitTable[gActiveBattler]; gHitMarker &= ~HITMARKER_FAINTED(gActiveBattler); BtlController_EmitCantSwitch(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); } else if (!gSpecialStatuses[gActiveBattler].faintedHasReplacement) { ChooseMonToSendOut(gBattleStruct->monToSwitchIntoId[1]); gSpecialStatuses[gActiveBattler].faintedHasReplacement = TRUE; } } gBattlescriptCurrInstr += 6; } else { // Not multi or double battle gBattlescriptCurrInstr += 6; } } else { // Multi battle gBattlescriptCurrInstr += 6; } hitmarkerFaintBits = gHitMarker >> 28; gBattlerFainted = 0; while (!(gBitTable[gBattlerFainted] & hitmarkerFaintBits) && gBattlerFainted < gBattlersCount) gBattlerFainted++; if (gBattlerFainted == gBattlersCount) gBattlescriptCurrInstr = jumpPtr; } else { if (gBattlescriptCurrInstr[1] & PARTY_SCREEN_OPTIONAL) hitmarkerFaintBits = PARTY_ACTION_CHOOSE_MON; // Used here as the caseId for the EmitChoose function. else hitmarkerFaintBits = PARTY_ACTION_SEND_OUT; battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1] & ~PARTY_SCREEN_OPTIONAL); if (gSpecialStatuses[battlerId].faintedHasReplacement) { gBattlescriptCurrInstr += 6; } else if (HasNoMonsToSwitch(battlerId, PARTY_SIZE, PARTY_SIZE)) { gActiveBattler = battlerId; gAbsentBattlerFlags |= gBitTable[gActiveBattler]; gHitMarker &= ~HITMARKER_FAINTED(gActiveBattler); gBattlescriptCurrInstr = jumpPtr; } else { gActiveBattler = battlerId; *(gBattleStruct->battlerPartyIndexes + gActiveBattler) = gBattlerPartyIndexes[gActiveBattler]; *(gBattleStruct->monToSwitchIntoId + gActiveBattler) = PARTY_SIZE; gBattleStruct->field_93 &= ~(gBitTable[gActiveBattler]); BtlController_EmitChoosePokemon(BUFFER_A, hitmarkerFaintBits, *(gBattleStruct->monToSwitchIntoId + (gActiveBattler ^ 2)), ABILITY_NONE, gBattleStruct->battlerPartyOrders[gActiveBattler]); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 6; if (GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_LEFT && gBattleResults.playerSwitchesCounter < 255) gBattleResults.playerSwitchesCounter++; if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++) { if (gActiveBattler != battlerId) { BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); MarkBattlerForControllerExec(gActiveBattler); } } } else { gActiveBattler = GetBattlerAtPosition(GetBattlerPosition(battlerId) ^ BIT_SIDE); if (gAbsentBattlerFlags & gBitTable[gActiveBattler]) gActiveBattler ^= BIT_FLANK; BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE); MarkBattlerForControllerExec(gActiveBattler); } } } } static void Cmd_switchhandleorder(void) { s32 i; if (gBattleControllerExecFlags) return; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); switch (gBattlescriptCurrInstr[2]) { 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])) { RecordedBattle_SetBattlerAction(i, gBattleBufferB[i][1]); gBattleStruct->field_93 |= gBitTable[i]; } } } break; case 1: if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI)) SwitchPartyOrder(gActiveBattler); break; case 2: if (!(gBattleStruct->field_93 & gBitTable[gActiveBattler])) { RecordedBattle_SetBattlerAction(gActiveBattler, gBattleBufferB[gActiveBattler][1]); gBattleStruct->field_93 |= gBitTable[gActiveBattler]; } // fall through case 3: gBattleCommunication[0] = gBattleBufferB[gActiveBattler][1]; *(gBattleStruct->monToSwitchIntoId + gActiveBattler) = gBattleBufferB[gActiveBattler][1]; if (gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleTypeFlags & BATTLE_TYPE_MULTI) { *(gActiveBattler * 3 + (u8*)(gBattleStruct->battlerPartyOrders) + 0) &= 0xF; *(gActiveBattler * 3 + (u8*)(gBattleStruct->battlerPartyOrders) + 0) |= (gBattleBufferB[gActiveBattler][2] & 0xF0); *(gActiveBattler * 3 + (u8*)(gBattleStruct->battlerPartyOrders) + 1) = gBattleBufferB[gActiveBattler][3]; *((gActiveBattler ^ BIT_FLANK) * 3 + (u8*)(gBattleStruct->battlerPartyOrders) + 0) &= (0xF0); *((gActiveBattler ^ BIT_FLANK) * 3 + (u8*)(gBattleStruct->battlerPartyOrders) + 0) |= (gBattleBufferB[gActiveBattler][2] & 0xF0) >> 4; *((gActiveBattler ^ BIT_FLANK) * 3 + (u8*)(gBattleStruct->battlerPartyOrders) + 2) = gBattleBufferB[gActiveBattler][3]; } else if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { SwitchPartyOrderInGameMulti(gActiveBattler, *(gBattleStruct->monToSwitchIntoId + gActiveBattler)); } else { SwitchPartyOrder(gActiveBattler); } PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].species) PREPARE_MON_NICK_BUFFER(gBattleTextBuff2, gActiveBattler, gBattleBufferB[gActiveBattler][1]) break; } gBattlescriptCurrInstr += 3; } static void Cmd_switchineffects(void) { s32 i; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); UpdateSentPokesToOpponentValue(gActiveBattler); gHitMarker &= ~HITMARKER_FAINTED(gActiveBattler); gSpecialStatuses[gActiveBattler].faintedHasReplacement = FALSE; if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SPIKES_DAMAGED) && (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SPIKES) && !IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_FLYING) && gBattleMons[gActiveBattler].ability != ABILITY_LEVITATE) { u8 spikesDmg; gSideStatuses[GetBattlerSide(gActiveBattler)] |= SIDE_STATUS_SPIKES_DAMAGED; gBattleMons[gActiveBattler].status2 &= ~STATUS2_DESTINY_BOND; gHitMarker &= ~HITMARKER_DESTINYBOND; spikesDmg = (5 - gSideTimers[GetBattlerSide(gActiveBattler)].spikesAmount) * 2; gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / (spikesDmg); if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleScripting.battler = gActiveBattler; BattleScriptPushCursor(); if (gBattlescriptCurrInstr[1] == BS_TARGET) gBattlescriptCurrInstr = BattleScript_SpikesOnTarget; else if (gBattlescriptCurrInstr[1] == BS_ATTACKER) gBattlescriptCurrInstr = BattleScript_SpikesOnAttacker; else gBattlescriptCurrInstr = BattleScript_SpikesOnFaintedBattler; } else { // There is a hack here to ensure the truant counter will be 0 when the battler's next turn starts. // The truant counter is not updated in the case where a mon switches in after a lost judgement in the battle arena. if (gBattleMons[gActiveBattler].ability == ABILITY_TRUANT && !gDisableStructs[gActiveBattler].truantSwitchInHack) gDisableStructs[gActiveBattler].truantCounter = 1; gDisableStructs[gActiveBattler].truantSwitchInHack = 0; if (!AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gActiveBattler, 0, 0, 0) && !ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, gActiveBattler, FALSE)) { gSideStatuses[GetBattlerSide(gActiveBattler)] &= ~SIDE_STATUS_SPIKES_DAMAGED; for (i = 0; i < gBattlersCount; i++) { if (gBattlerByTurnOrder[i] == gActiveBattler) gActionsByTurnOrder[i] = B_ACTION_CANCEL_PARTNER; } for (i = 0; i < gBattlersCount; i++) { u16* hpOnSwitchout = &gBattleStruct->hpOnSwitchout[GetBattlerSide(i)]; *hpOnSwitchout = gBattleMons[i].hp; } if (gBattlescriptCurrInstr[1] == 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++; } } gBattlescriptCurrInstr += 2; } } } static void Cmd_trainerslidein(void) { gActiveBattler = GetBattlerAtPosition(gBattlescriptCurrInstr[1]); BtlController_EmitTrainerSlide(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 2; } static void Cmd_playse(void) { gActiveBattler = gBattlerAttacker; BtlController_EmitPlaySE(BUFFER_A, T2_READ_16(gBattlescriptCurrInstr + 1)); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 3; } static void Cmd_fanfare(void) { gActiveBattler = gBattlerAttacker; BtlController_EmitPlayFanfareOrBGM(BUFFER_A, T2_READ_16(gBattlescriptCurrInstr + 1), FALSE); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 3; } static void Cmd_playfaintcry(void) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); BtlController_EmitFaintingCry(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 2; } static void Cmd_endlinkbattle(void) { gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); BtlController_EmitEndLinkBattle(BUFFER_A, gBattleOutcome); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 1; } static void Cmd_returntoball(void) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); BtlController_EmitReturnMonToBall(BUFFER_A, TRUE); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 2; } static void Cmd_handlelearnnewmove(void) { const u8 *learnedMovePtr = T1_READ_PTR(gBattlescriptCurrInstr + 1); const u8 *nothingToLearnPtr = T1_READ_PTR(gBattlescriptCurrInstr + 5); u16 learnMove = MonTryLearningNewMove(&gPlayerParty[gBattleStruct->expGetterMonId], gBattlescriptCurrInstr[9]); while (learnMove == MON_ALREADY_KNOWS_MOVE) learnMove = MonTryLearningNewMove(&gPlayerParty[gBattleStruct->expGetterMonId], FALSE); if (learnMove == MOVE_NONE) { gBattlescriptCurrInstr = nothingToLearnPtr; } else if (learnMove == MON_HAS_MAX_MOVES) { gBattlescriptCurrInstr += 10; } else { gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); if (gBattlerPartyIndexes[gActiveBattler] == gBattleStruct->expGetterMonId && !(gBattleMons[gActiveBattler].status2 & STATUS2_TRANSFORMED)) { GiveMoveToBattleMon(&gBattleMons[gActiveBattler], learnMove); } if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); if (gBattlerPartyIndexes[gActiveBattler] == gBattleStruct->expGetterMonId && !(gBattleMons[gActiveBattler].status2 & STATUS2_TRANSFORMED)) { GiveMoveToBattleMon(&gBattleMons[gActiveBattler], learnMove); } } gBattlescriptCurrInstr = learnedMovePtr; } } static void Cmd_yesnoboxlearnmove(void) { gActiveBattler = 0; switch (gBattleScripting.learnMoveState) { case 0: HandleBattleWindow(24, 8, 29, 13, 0); BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO); gBattleScripting.learnMoveState++; gBattleCommunication[CURSOR_POSITION] = 0; BattleCreateYesNoCursorAt(0); break; case 1: if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0) { PlaySE(SE_SELECT); BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]); gBattleCommunication[CURSOR_POSITION] = 0; BattleCreateYesNoCursorAt(0); } if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0) { PlaySE(SE_SELECT); BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]); gBattleCommunication[CURSOR_POSITION] = 1; BattleCreateYesNoCursorAt(1); } if (JOY_NEW(A_BUTTON)) { PlaySE(SE_SELECT); if (gBattleCommunication[1] == 0) { HandleBattleWindow(24, 8, 29, 13, 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 (IsHMMove2(moveId)) { PrepareStringBattle(STRINGID_HMMOVESCANTBEFORGOTTEN, gActiveBattler); gBattleScripting.learnMoveState = 6; } else { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); 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(24, 8, 29, 13, WINDOW_CLEAR); gBattlescriptCurrInstr += 5; break; case 6: if (gBattleControllerExecFlags == 0) { gBattleScripting.learnMoveState = 2; } break; } } static void Cmd_yesnoboxstoplearningmove(void) { switch (gBattleScripting.learnMoveState) { case 0: HandleBattleWindow(24, 8, 29, 13, 0); BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO); gBattleScripting.learnMoveState++; gBattleCommunication[CURSOR_POSITION] = 0; BattleCreateYesNoCursorAt(0); break; case 1: if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0) { PlaySE(SE_SELECT); BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]); gBattleCommunication[CURSOR_POSITION] = 0; BattleCreateYesNoCursorAt(0); } if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0) { PlaySE(SE_SELECT); BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]); gBattleCommunication[CURSOR_POSITION] = 1; BattleCreateYesNoCursorAt(1); } if (JOY_NEW(A_BUTTON)) { PlaySE(SE_SELECT); if (gBattleCommunication[1] != 0) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); else gBattlescriptCurrInstr += 5; HandleBattleWindow(24, 8, 29, 13, WINDOW_CLEAR); } else if (JOY_NEW(B_BUTTON)) { PlaySE(SE_SELECT); gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); HandleBattleWindow(24, 8, 29, 13, WINDOW_CLEAR); } break; } } static void Cmd_hitanimation(void) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) { gBattlescriptCurrInstr += 2; } else if (!(gHitMarker & HITMARKER_IGNORE_SUBSTITUTE) || !(gBattleMons[gActiveBattler].status2 & STATUS2_SUBSTITUTE) || gDisableStructs[gActiveBattler].substituteHP == 0) { BtlController_EmitHitAnimation(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 2; } else { gBattlescriptCurrInstr += 2; } } static u32 GetTrainerMoneyToGive(u16 trainerId) { u32 i = 0; u32 lastMonLevel = 0; u32 moneyReward; if (trainerId == TRAINER_SECRET_BASE) { moneyReward = 20 * gBattleResources->secretBase->party.levels[0] * gBattleStruct->moneyMultiplier; } else { switch (gTrainers[trainerId].partyFlags) { case 0: { const struct TrainerMonNoItemDefaultMoves *party = gTrainers[trainerId].party.NoItemDefaultMoves; lastMonLevel = party[gTrainers[trainerId].partySize - 1].lvl; } break; case F_TRAINER_PARTY_CUSTOM_MOVESET: { const struct TrainerMonNoItemCustomMoves *party = gTrainers[trainerId].party.NoItemCustomMoves; lastMonLevel = party[gTrainers[trainerId].partySize - 1].lvl; } break; case F_TRAINER_PARTY_HELD_ITEM: { const struct TrainerMonItemDefaultMoves *party = gTrainers[trainerId].party.ItemDefaultMoves; lastMonLevel = party[gTrainers[trainerId].partySize - 1].lvl; } break; case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM: { const struct TrainerMonItemCustomMoves *party = gTrainers[trainerId].party.ItemCustomMoves; lastMonLevel = party[gTrainers[trainerId].partySize - 1].lvl; } break; } for (; gTrainerMoneyTable[i].classId != 0xFF; i++) { if (gTrainerMoneyTable[i].classId == gTrainers[trainerId].trainerClass) break; } if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) moneyReward = 4 * lastMonLevel * gBattleStruct->moneyMultiplier * gTrainerMoneyTable[i].value; else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) moneyReward = 4 * lastMonLevel * gBattleStruct->moneyMultiplier * 2 * gTrainerMoneyTable[i].value; else moneyReward = 4 * lastMonLevel * gBattleStruct->moneyMultiplier * gTrainerMoneyTable[i].value; } return moneyReward; } static void Cmd_getmoneyreward(void) { u32 moneyReward = GetTrainerMoneyToGive(gTrainerBattleOpponent_A); if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) moneyReward += GetTrainerMoneyToGive(gTrainerBattleOpponent_B); AddMoney(&gSaveBlock1Ptr->money, moneyReward); PREPARE_WORD_NUMBER_BUFFER(gBattleTextBuff1, 5, moneyReward); gBattlescriptCurrInstr++; } // Command is never used static void Cmd_updatebattlermoves(void) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); switch (gBattleCommunication[0]) { case 0: BtlController_EmitGetMonData(BUFFER_A, REQUEST_ALL_BATTLE, 0); MarkBattlerForControllerExec(gActiveBattler); gBattleCommunication[0]++; break; case 1: if (gBattleControllerExecFlags == 0) { s32 i; struct BattlePokemon *bufferPoke = (struct BattlePokemon*) &gBattleBufferB[gActiveBattler][4]; for (i = 0; i < MAX_MON_MOVES; i++) { gBattleMons[gActiveBattler].moves[i] = bufferPoke->moves[i]; gBattleMons[gActiveBattler].pp[i] = bufferPoke->pp[i]; } gBattlescriptCurrInstr += 2; } break; } } static void Cmd_swapattackerwithtarget(void) { gActiveBattler = gBattlerAttacker; gBattlerAttacker = gBattlerTarget; gBattlerTarget = gActiveBattler; if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET) gHitMarker &= ~HITMARKER_SWAP_ATTACKER_TARGET; else gHitMarker |= HITMARKER_SWAP_ATTACKER_TARGET; gBattlescriptCurrInstr++; } static void Cmd_incrementgamestat(void) { if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) IncrementGameStat(gBattlescriptCurrInstr[1]); gBattlescriptCurrInstr += 2; } static void Cmd_drawpartystatussummary(void) { s32 i; struct Pokemon *party; struct HpAndStatus hpStatuses[PARTY_SIZE]; if (gBattleControllerExecFlags) return; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_NONE || GetMonData(&party[i], MON_DATA_SPECIES2) == 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(gActiveBattler); gBattlescriptCurrInstr += 2; } static void Cmd_hidepartystatussummary(void) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); BtlController_EmitHidePartyStatusSummary(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 2; } static void Cmd_jumptocalledmove(void) { if (gBattlescriptCurrInstr[1]) gCurrentMove = gCalledMove; else gChosenMove = gCurrentMove = gCalledMove; gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]; } static void Cmd_statusanimation(void) { if (gBattleControllerExecFlags == 0) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); if (!(gStatuses3[gActiveBattler] & STATUS3_SEMI_INVULNERABLE) && gDisableStructs[gActiveBattler].substituteHP == 0 && !(gHitMarker & HITMARKER_NO_ANIMATIONS)) { BtlController_EmitStatusAnimation(BUFFER_A, FALSE, gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); } gBattlescriptCurrInstr += 2; } } static void Cmd_status2animation(void) { u32 wantedToAnimate; if (gBattleControllerExecFlags == 0) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); wantedToAnimate = T1_READ_32(gBattlescriptCurrInstr + 2); if (!(gStatuses3[gActiveBattler] & STATUS3_SEMI_INVULNERABLE) && gDisableStructs[gActiveBattler].substituteHP == 0 && !(gHitMarker & HITMARKER_NO_ANIMATIONS)) { BtlController_EmitStatusAnimation(BUFFER_A, TRUE, gBattleMons[gActiveBattler].status2 & wantedToAnimate); MarkBattlerForControllerExec(gActiveBattler); } gBattlescriptCurrInstr += 6; } } static void Cmd_chosenstatusanimation(void) { u32 wantedStatus; if (gBattleControllerExecFlags == 0) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); wantedStatus = T1_READ_32(gBattlescriptCurrInstr + 3); if (!(gStatuses3[gActiveBattler] & STATUS3_SEMI_INVULNERABLE) && gDisableStructs[gActiveBattler].substituteHP == 0 && !(gHitMarker & HITMARKER_NO_ANIMATIONS)) { BtlController_EmitStatusAnimation(BUFFER_A, gBattlescriptCurrInstr[2], wantedStatus); MarkBattlerForControllerExec(gActiveBattler); } gBattlescriptCurrInstr += 7; } } static void Cmd_yesnobox(void) { switch (gBattleCommunication[0]) { case 0: HandleBattleWindow(24, 8, 29, 13, 0); BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO); gBattleCommunication[0]++; gBattleCommunication[CURSOR_POSITION] = 0; BattleCreateYesNoCursorAt(0); break; case 1: if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0) { PlaySE(SE_SELECT); BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]); gBattleCommunication[CURSOR_POSITION] = 0; BattleCreateYesNoCursorAt(0); } if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0) { PlaySE(SE_SELECT); BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]); gBattleCommunication[CURSOR_POSITION] = 1; BattleCreateYesNoCursorAt(1); } if (JOY_NEW(B_BUTTON)) { gBattleCommunication[CURSOR_POSITION] = 1; PlaySE(SE_SELECT); HandleBattleWindow(24, 8, 29, 13, WINDOW_CLEAR); gBattlescriptCurrInstr++; } else if (JOY_NEW(A_BUTTON)) { PlaySE(SE_SELECT); HandleBattleWindow(24, 8, 29, 13, WINDOW_CLEAR); gBattlescriptCurrInstr++; } break; } } static void Cmd_cancelallactions(void) { s32 i; for (i = 0; i < gBattlersCount; i++) gActionsByTurnOrder[i] = B_ACTION_CANCEL_PARTNER; gBattlescriptCurrInstr++; } static void Cmd_adjustsetdamage(void) // The same as adjustnormaldamage, except there's no random damage multiplier. { 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, holdEffect); gSpecialStatuses[gBattlerTarget].focusBanded = 1; } if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE) && (gBattleMoves[gCurrentMove].effect == EFFECT_FALSE_SWIPE || gProtectStructs[gBattlerTarget].endured || gSpecialStatuses[gBattlerTarget].focusBanded) && gBattleMons[gBattlerTarget].hp <= gBattleMoveDamage) { gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1; if (gProtectStructs[gBattlerTarget].endured) { gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED; } else if (gSpecialStatuses[gBattlerTarget].focusBanded) { gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON; gLastUsedItem = gBattleMons[gBattlerTarget].item; } } gBattlescriptCurrInstr++; } static void Cmd_removeitem(void) { u16* usedHeldItem; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); usedHeldItem = &gBattleStruct->usedHeldItems[gActiveBattler]; *usedHeldItem = gBattleMons[gActiveBattler].item; gBattleMons[gActiveBattler].item = 0; BtlController_EmitSetMonData(BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, sizeof(gBattleMons[gActiveBattler].item), &gBattleMons[gActiveBattler].item); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 2; } static void Cmd_atknameinbuff1(void) { PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattlerPartyIndexes[gBattlerAttacker]) gBattlescriptCurrInstr++; } static void Cmd_drawlvlupbox(void) { 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++; } 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, 0x60, 0x20); 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) { u16 monLevel; u8 monGender; struct TextPrinterTemplate printerTemplate; u8 *txtPtr; u32 var; monLevel = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL); monGender = GetMonGender(&gPlayerParty[gBattleStruct->expGetterMonId]); GetMonNickname(&gPlayerParty[gBattleStruct->expGetterMonId], 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; const u16* iconPal; struct SpriteSheet iconSheet; struct SpritePalette iconPalSheet; u16 species = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPECIES); u32 personality = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_PERSONALITY); const u8* iconPtr = GetMonIconPtr(species, personality, 1); iconSheet.data = iconPtr; iconSheet.size = 0x200; iconSheet.tag = TAG_LVLUP_BANNER_MON_ICON; iconPal = GetValidMonIconPalettePtr(species); iconPalSheet.data = iconPal; iconPalSheet.tag = TAG_LVLUP_BANNER_MON_ICON; LoadSpriteSheet(&iconSheet); LoadSpritePalette(&iconPalSheet); spriteId = CreateSprite(&sSpriteTemplate_MonIconOnLvlUpBanner, 256, 10, 0); gSprites[spriteId].sDestroy = FALSE; gSprites[spriteId].sXOffset = gBattle_BG2_X; } static void SpriteCB_MonIconOnLvlUpBanner(struct Sprite* sprite) { sprite->x2 = sprite->sXOffset - gBattle_BG2_X; if (sprite->x2 != 0) { sprite->sDestroy = TRUE; } else if (sprite->sDestroy) { DestroySprite(sprite); FreeSpriteTilesByTag(TAG_LVLUP_BANNER_MON_ICON); FreeSpritePaletteByTag(TAG_LVLUP_BANNER_MON_ICON); } } #undef sDestroy #undef sXOffset static bool32 IsMonGettingExpSentOut(void) { if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId) return TRUE; if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gBattlerPartyIndexes[2] == gBattleStruct->expGetterMonId) return TRUE; return FALSE; } static void Cmd_resetsentmonsvalue(void) { ResetSentPokesToOpponentValue(); gBattlescriptCurrInstr++; } static void Cmd_setatktoplayer0(void) { gBattlerAttacker = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); gBattlescriptCurrInstr++; } static void Cmd_makevisible(void) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); BtlController_EmitSpriteInvisibility(BUFFER_A, FALSE); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 2; } static void Cmd_recordlastability(void) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); RecordAbilityBattle(gActiveBattler, gLastUsedAbility); #ifdef BUGFIX // This command occupies two bytes (one for the command id, and one for the battler id parameter). gBattlescriptCurrInstr += 2; #else gBattlescriptCurrInstr += 1; #endif } void BufferMoveToLearnIntoBattleTextBuff2(void) { PREPARE_MOVE_BUFFER(gBattleTextBuff2, gMoveToLearn); } static void Cmd_buffermovetolearn(void) { BufferMoveToLearnIntoBattleTextBuff2(); gBattlescriptCurrInstr++; } static void Cmd_jumpifplayerran(void) { if (TryRunFromBattle(gBattlerFainted)) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); else gBattlescriptCurrInstr += 5; } static void Cmd_hpthresholds(void) { u8 opposingBank; s32 result; if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); opposingBank = gActiveBattler ^ BIT_SIDE; result = gBattleMons[opposingBank].hp * 100 / gBattleMons[opposingBank].maxHP; if (result == 0) result = 1; if (result > 69 || !gBattleMons[opposingBank].hp) gBattleStruct->hpScale = 0; else if (result > 39) gBattleStruct->hpScale = 1; else if (result > 9) gBattleStruct->hpScale = 2; else gBattleStruct->hpScale = 3; } gBattlescriptCurrInstr += 2; } static void Cmd_hpthresholds2(void) { u8 opposingBank; s32 result; u8 hpSwitchout; if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); opposingBank = gActiveBattler ^ BIT_SIDE; hpSwitchout = *(gBattleStruct->hpOnSwitchout + GetBattlerSide(opposingBank)); result = (hpSwitchout - gBattleMons[opposingBank].hp) * 100 / hpSwitchout; if (gBattleMons[opposingBank].hp >= hpSwitchout) gBattleStruct->hpScale = 0; else if (result <= 29) gBattleStruct->hpScale = 1; else if (result <= 69) gBattleStruct->hpScale = 2; else gBattleStruct->hpScale = 3; } gBattlescriptCurrInstr += 2; } static void Cmd_useitemonopponent(void) { gBattlerInMenuId = gBattlerAttacker; PokemonUseItemEffects(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker]], gLastUsedItem, gBattlerPartyIndexes[gBattlerAttacker], 0, TRUE); gBattlescriptCurrInstr += 1; } static void Cmd_various(void) { u8 side; s32 i; gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); switch (gBattlescriptCurrInstr[2]) { case VARIOUS_CANCEL_MULTI_TURN_MOVES: CancelMultiTurnMoves(gActiveBattler); break; case VARIOUS_SET_MAGIC_COAT_TARGET: gBattlerAttacker = gBattlerTarget; side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; if (gSideTimers[side].followmeTimer != 0 && gBattleMons[gSideTimers[side].followmeTarget].hp != 0) gBattlerTarget = gSideTimers[side].followmeTarget; else gBattlerTarget = gActiveBattler; break; case VARIOUS_IS_RUNNING_IMPOSSIBLE: gBattleCommunication[0] = IsRunningFromBattleImpossible(); break; case VARIOUS_GET_MOVE_TARGET: gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE); break; case VARIOUS_GET_BATTLER_FAINTED: if (gHitMarker & HITMARKER_FAINTED(gActiveBattler)) gBattleCommunication[0] = TRUE; else gBattleCommunication[0] = FALSE; break; case VARIOUS_RESET_INTIMIDATE_TRACE_BITS: gSpecialStatuses[gActiveBattler].intimidatedMon = 0; gSpecialStatuses[gActiveBattler].traced = 0; break; case VARIOUS_UPDATE_CHOICE_MOVE_ON_LVL_UP: if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId || gBattlerPartyIndexes[2] == gBattleStruct->expGetterMonId) { u16 *choicedMove; if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId) gActiveBattler = 0; else gActiveBattler = 2; choicedMove = &gBattleStruct->choicedMove[gActiveBattler]; for (i = 0; i < MAX_MON_MOVES; i++) { if (gBattleMons[gActiveBattler].moves[i] == *choicedMove) break; } if (i == MAX_MON_MOVES) *choicedMove = 0; } break; case VARIOUS_RESET_PLAYER_FAINTED: 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: // Try and print end-of-turn Battle Palace flavor text (e.g. "A glint appears in mon's eyes") gBattleCommunication[0] = FALSE; // whether or not msg should be printed gBattleScripting.battler = gActiveBattler = gBattleCommunication[1]; if (!(gBattleStruct->palaceFlags & gBitTable[gActiveBattler]) && gBattleMons[gActiveBattler].maxHP / 2 >= gBattleMons[gActiveBattler].hp && gBattleMons[gActiveBattler].hp != 0 && !(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP)) { gBattleStruct->palaceFlags |= gBitTable[gActiveBattler]; gBattleCommunication[0] = TRUE; gBattleCommunication[MULTISTRING_CHOOSER] = sBattlePalaceNatureToFlavorTextId[GetNatureFromPersonality(gBattleMons[gActiveBattler].personality)]; } break; case VARIOUS_ARENA_JUDGMENT_WINDOW: i = BattleArena_ShowJudgmentWindow(&gBattleCommunication[0]); if (i == 0) return; gBattleCommunication[1] = i; break; case VARIOUS_ARENA_OPPONENT_MON_LOST: gBattleMons[1].hp = 0; gHitMarker |= HITMARKER_FAINTED(1); gBattleStruct->arenaLostOpponentMons |= gBitTable[gBattlerPartyIndexes[1]]; gDisableStructs[1].truantSwitchInHack = 1; break; case VARIOUS_ARENA_PLAYER_MON_LOST: gBattleMons[0].hp = 0; gHitMarker |= HITMARKER_FAINTED(0); gHitMarker |= HITMARKER_PLAYER_FAINTED; gBattleStruct->arenaLostPlayerMons |= gBitTable[gBattlerPartyIndexes[0]]; gDisableStructs[0].truantSwitchInHack = 1; break; case VARIOUS_ARENA_BOTH_MONS_LOST: gBattleMons[0].hp = 0; gBattleMons[1].hp = 0; gHitMarker |= HITMARKER_FAINTED(0); gHitMarker |= HITMARKER_FAINTED(1); gHitMarker |= HITMARKER_PLAYER_FAINTED; gBattleStruct->arenaLostPlayerMons |= gBitTable[gBattlerPartyIndexes[0]]; gBattleStruct->arenaLostOpponentMons |= gBitTable[gBattlerPartyIndexes[1]]; gDisableStructs[0].truantSwitchInHack = 1; gDisableStructs[1].truantSwitchInHack = 1; break; case VARIOUS_EMIT_YESNOBOX: BtlController_EmitYesNoBox(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); break; case VARIOUS_DRAW_ARENA_REF_TEXT_BOX: DrawArenaRefereeTextBox(); break; case VARIOUS_ERASE_ARENA_REF_TEXT_BOX: EraseArenaRefereeTextBox(); break; case VARIOUS_ARENA_JUDGMENT_STRING: BattleStringExpandPlaceholdersToDisplayedString(gRefereeStringsTable[gBattlescriptCurrInstr[1]]); BattlePutTextOnWindow(gDisplayedStringBattle, ARENA_WIN_JUDGEMENT_TEXT); break; case VARIOUS_ARENA_WAIT_STRING: if (IsTextPrinterActive(ARENA_WIN_JUDGEMENT_TEXT)) return; break; case VARIOUS_WAIT_CRY: if (!IsCryFinished()) return; break; case VARIOUS_RETURN_OPPONENT_MON1: gActiveBattler = 1; if (gBattleMons[gActiveBattler].hp != 0) { BtlController_EmitReturnMonToBall(BUFFER_A, FALSE); MarkBattlerForControllerExec(gActiveBattler); } break; case VARIOUS_RETURN_OPPONENT_MON2: if (gBattlersCount > 3) { gActiveBattler = 3; if (gBattleMons[gActiveBattler].hp != 0) { BtlController_EmitReturnMonToBall(BUFFER_A, FALSE); MarkBattlerForControllerExec(gActiveBattler); } } break; case VARIOUS_VOLUME_DOWN: m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x55); break; case VARIOUS_VOLUME_UP: m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100); break; case VARIOUS_SET_ALREADY_STATUS_MOVE_ATTEMPT: gBattleStruct->alreadyStatusedMoveAttempt |= gBitTable[gActiveBattler]; break; case VARIOUS_PALACE_TRY_ESCAPE_STATUS: if (BattlePalace_TryEscapeStatus(gActiveBattler)) return; break; case VARIOUS_SET_TELEPORT_OUTCOME: if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) gBattleOutcome = B_OUTCOME_PLAYER_TELEPORTED; else gBattleOutcome = B_OUTCOME_MON_TELEPORTED; break; case VARIOUS_PLAY_TRAINER_DEFEATED_MUSIC: BtlController_EmitPlayFanfareOrBGM(BUFFER_A, MUS_VICTORY_TRAINER, TRUE); MarkBattlerForControllerExec(gActiveBattler); break; } gBattlescriptCurrInstr += 3; } static void Cmd_setprotectlike(void) // protect and endure { bool8 notLastTurn = TRUE; u16 lastMove = gLastResultingMoves[gBattlerAttacker]; if (lastMove != MOVE_PROTECT && lastMove != MOVE_DETECT && lastMove != MOVE_ENDURE) gDisableStructs[gBattlerAttacker].protectUses = 0; if (gCurrentTurnActionNumber == (gBattlersCount - 1)) notLastTurn = FALSE; if (sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= Random() && notLastTurn) { if (gBattleMoves[gCurrentMove].effect == EFFECT_PROTECT) { gProtectStructs[gBattlerAttacker].protected = 1; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECTED_ITSELF; } if (gBattleMoves[gCurrentMove].effect == EFFECT_ENDURE) { gProtectStructs[gBattlerAttacker].endured = 1; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_BRACED_ITSELF; } gDisableStructs[gBattlerAttacker].protectUses++; } else { gDisableStructs[gBattlerAttacker].protectUses = 0; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PROTECT_FAILED; gMoveResultFlags |= MOVE_RESULT_MISSED; } gBattlescriptCurrInstr++; } static void Cmd_faintifabilitynotdamp(void) { if (gBattleControllerExecFlags) return; for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++) { if (gBattleMons[gBattlerTarget].ability == ABILITY_DAMP) break; } if (gBattlerTarget == gBattlersCount) { gActiveBattler = gBattlerAttacker; gBattleMoveDamage = gBattleMons[gActiveBattler].hp; BtlController_EmitHealthBarUpdate(BUFFER_A, INSTANT_HP_BAR_DROP); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr++; for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++) { if (gBattlerTarget == gBattlerAttacker) continue; if (!(gAbsentBattlerFlags & gBitTable[gBattlerTarget])) break; } } else { gLastUsedAbility = ABILITY_DAMP; RecordAbilityBattle(gBattlerTarget, gBattleMons[gBattlerTarget].ability); gBattlescriptCurrInstr = BattleScript_DampStopsExplosion; } } static void Cmd_setatkhptozero(void) { if (gBattleControllerExecFlags) return; gActiveBattler = gBattlerAttacker; gBattleMons[gActiveBattler].hp = 0; BtlController_EmitSetMonData(BUFFER_A, REQUEST_HP_BATTLE, 0, sizeof(gBattleMons[gActiveBattler].hp), &gBattleMons[gActiveBattler].hp); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr++; } static void Cmd_jumpifnexttargetvalid(void) { const u8 *jumpPtr = T1_READ_PTR(gBattlescriptCurrInstr + 1); if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { for (gBattlerTarget++; ; gBattlerTarget++) { if (gBattlerTarget == gBattlerAttacker) continue; if (!(gAbsentBattlerFlags & gBitTable[gBattlerTarget])) break; } if (gBattlerTarget >= gBattlersCount) gBattlescriptCurrInstr += 5; else gBattlescriptCurrInstr = jumpPtr; } else { gBattlescriptCurrInstr += 5; } } static void Cmd_tryhealhalfhealth(void) { const u8* failPtr = T1_READ_PTR(gBattlescriptCurrInstr + 1); if (gBattlescriptCurrInstr[5] == BS_ATTACKER) gBattlerTarget = gBattlerAttacker; gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 2; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP) gBattlescriptCurrInstr = failPtr; else gBattlescriptCurrInstr += 6; } static void Cmd_trymirrormove(void) { s32 validMovesCount; s32 i; u16 move; u16 movesArray[4]; for (i = 0; i < 3; i++) movesArray[i] = 0; for (validMovesCount = 0, i = 0; i < gBattlersCount; i++) { if (i != gBattlerAttacker) { move = *(i * 2 + gBattlerAttacker * 8 + (u8*)(gBattleStruct->lastTakenMoveFrom) + 0) | (*(i * 2 + gBattlerAttacker * 8 + (u8*)(gBattleStruct->lastTakenMoveFrom) + 1) << 8); if (move != 0 && move != 0xFFFF) { movesArray[validMovesCount] = move; validMovesCount++; } } } move = *(gBattleStruct->lastTakenMove + gBattlerAttacker * 2 + 0) | (*(gBattleStruct->lastTakenMove + gBattlerAttacker * 2 + 1) << 8); if (move != 0 && move != 0xFFFF) { gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; gCurrentMove = move; gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE); gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]; } else if (validMovesCount) { gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; i = Random() % validMovesCount; gCurrentMove = movesArray[i]; gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE); gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]; } else { gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = 1; gBattlescriptCurrInstr++; } } static void Cmd_setrain(void) { if (gBattleWeather & B_WEATHER_RAIN) { gMoveResultFlags |= MOVE_RESULT_MISSED; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_FAILED; } else { gBattleWeather = B_WEATHER_RAIN_TEMPORARY; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STARTED_RAIN; gWishFutureKnock.weatherDuration = 5; } gBattlescriptCurrInstr++; } static void Cmd_setreflect(void) { if (gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] & SIDE_STATUS_REFLECT) { gMoveResultFlags |= MOVE_RESULT_MISSED; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SIDE_STATUS_FAILED; } else { gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] |= SIDE_STATUS_REFLECT; gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].reflectTimer = 5; gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].reflectBattlerId = gBattlerAttacker; if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && CountAliveMonsInBattle(BATTLE_ALIVE_ATK_SIDE) == 2) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_REFLECT_DOUBLE; else gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_REFLECT_SINGLE; } gBattlescriptCurrInstr++; } static void Cmd_setseeded(void) { 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++; } static void Cmd_manipulatedamage(void) { switch (gBattlescriptCurrInstr[1]) { case DMG_CHANGE_SIGN: gBattleMoveDamage *= -1; break; case DMG_RECOIL_FROM_MISS: gBattleMoveDamage /= 2; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; if ((gBattleMons[gBattlerTarget].maxHP / 2) < gBattleMoveDamage) gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 2; break; case DMG_DOUBLED: gBattleMoveDamage *= 2; break; } gBattlescriptCurrInstr += 2; } static void Cmd_trysetrest(void) { const u8 *failJump = T1_READ_PTR(gBattlescriptCurrInstr + 1); gActiveBattler = gBattlerTarget = gBattlerAttacker; gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP * (-1); if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP) { gBattlescriptCurrInstr = failJump; } 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[gActiveBattler].status1), &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 5; } } static void Cmd_jumpifnotfirstturn(void) { const u8* failJump = T1_READ_PTR(gBattlescriptCurrInstr + 1); if (gDisableStructs[gBattlerAttacker].isFirstTurn) gBattlescriptCurrInstr += 5; else gBattlescriptCurrInstr = failJump; } 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 = CalculateBaseDamage(&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++; } #define STAT_BUFF_WORKED 0 #define STAT_BUFF_DIDNT_WORK 1 static u8 ChangeStatBuffs(s8 statValue, u8 statId, u8 flags, const u8 *BS_ptr) { bool8 certain = FALSE; bool8 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_BUFF_NOT_PROTECT_AFFECTED) notProtectAffected++; flags &= ~STAT_BUFF_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_BUFF_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_BUFF_DIDNT_WORK; } else if (gCurrentMove != MOVE_CURSE && notProtectAffected != TRUE && JumpIfMoveAffectedByProtect(0)) { gBattlescriptCurrInstr = BattleScript_ButItFailed; return STAT_BUFF_DIDNT_WORK; } else if ((gBattleMons[gActiveBattler].ability == ABILITY_CLEAR_BODY || gBattleMons[gActiveBattler].ability == ABILITY_WHITE_SMOKE) && !certain && gCurrentMove != MOVE_CURSE) { if (flags == STAT_BUFF_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_BUFF_DIDNT_WORK; } else if (gBattleMons[gActiveBattler].ability == ABILITY_KEEN_EYE && !certain && statId == STAT_ACC) { if (flags == STAT_BUFF_ALLOW_PTR) { BattleScriptPush(BS_ptr); gBattleScripting.battler = gActiveBattler; gBattlescriptCurrInstr = BattleScript_AbilityNoSpecificStatLoss; gLastUsedAbility = gBattleMons[gActiveBattler].ability; RecordAbilityBattle(gActiveBattler, gLastUsedAbility); } return STAT_BUFF_DIDNT_WORK; } else if (gBattleMons[gActiveBattler].ability == ABILITY_HYPER_CUTTER && !certain && statId == STAT_ATK) { if (flags == STAT_BUFF_ALLOW_PTR) { BattleScriptPush(BS_ptr); gBattleScripting.battler = gActiveBattler; gBattlescriptCurrInstr = BattleScript_AbilityNoSpecificStatLoss; gLastUsedAbility = gBattleMons[gActiveBattler].ability; RecordAbilityBattle(gActiveBattler, gLastUsedAbility); } return STAT_BUFF_DIDNT_WORK; } else if (gBattleMons[gActiveBattler].ability == ABILITY_SHIELD_DUST && flags == 0) { return STAT_BUFF_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; index++; gBattleTextBuff2[index] = STRINGID_STATFELL; index++; gBattleTextBuff2[index] = STRINGID_STATFELL >> 8; index++; 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; index++; gBattleTextBuff2[index] = STRINGID_STATROSE; index++; gBattleTextBuff2[index] = STRINGID_STATROSE >> 8; index++; 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_BUFF_ALLOW_PTR) gMoveResultFlags |= MOVE_RESULT_MISSED; if (gBattleCommunication[MULTISTRING_CHOOSER] == B_MSG_STAT_WONT_INCREASE && !(flags & STAT_BUFF_ALLOW_PTR)) return STAT_BUFF_DIDNT_WORK; return STAT_BUFF_WORKED; } static void Cmd_statbuffchange(void) { const u8* jumpPtr = T1_READ_PTR(gBattlescriptCurrInstr + 2); if (ChangeStatBuffs(gBattleScripting.statChanger & 0xF0, GET_STAT_BUFF_ID(gBattleScripting.statChanger), gBattlescriptCurrInstr[1], jumpPtr) == STAT_BUFF_WORKED) gBattlescriptCurrInstr += 6; } static void Cmd_normalisebuffs(void) // haze { 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++; } 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)) gBattleCommunication[MOVE_EFFECT_BYTE] = (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; } static void Cmd_forcerandomswitch(void) { s32 i; s32 battler1PartyId = 0; s32 battler2PartyId = 0; s32 firstMonId; s32 lastMonId = 0; // + 1 s32 monsCount; struct Pokemon* party = NULL; s32 validMons = 0; s32 minNeeded; if ((gBattleTypeFlags & BATTLE_TYPE_TRAINER)) { if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; if ((gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER && gBattleTypeFlags & BATTLE_TYPE_LINK) || (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER && gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) || (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)) { if ((gBattlerTarget & BIT_FLANK) != 0) { firstMonId = 3; lastMonId = 6; } else { firstMonId = 0; lastMonId = 3; } monsCount = 3; minNeeded = 1; battler2PartyId = gBattlerPartyIndexes[gBattlerTarget]; battler1PartyId = gBattlerPartyIndexes[gBattlerTarget ^ BIT_FLANK]; } else if ((gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_LINK) || (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK)) { if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(gBattlerTarget)) == 1) { firstMonId = 3; lastMonId = 6; } else { firstMonId = 0; lastMonId = 3; } monsCount = 3; minNeeded = 1; battler2PartyId = gBattlerPartyIndexes[gBattlerTarget]; battler1PartyId = gBattlerPartyIndexes[gBattlerTarget ^ BIT_FLANK]; } else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) { if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER) { firstMonId = 0; lastMonId = 6; monsCount = 6; minNeeded = 2; // since there are two opponents, it has to be a double battle } else { if ((gBattlerTarget & BIT_FLANK) != 0) { firstMonId = 3; lastMonId = 6; } else { firstMonId = 0; lastMonId = 3; } monsCount = 3; minNeeded = 1; } battler2PartyId = gBattlerPartyIndexes[gBattlerTarget]; battler1PartyId = gBattlerPartyIndexes[gBattlerTarget ^ BIT_FLANK]; } else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { firstMonId = 0; lastMonId = 6; monsCount = 6; minNeeded = 2; battler2PartyId = gBattlerPartyIndexes[gBattlerTarget]; battler1PartyId = gBattlerPartyIndexes[gBattlerTarget ^ BIT_FLANK]; } else { firstMonId = 0; lastMonId = 6; monsCount = 6; minNeeded = 1; battler2PartyId = gBattlerPartyIndexes[gBattlerTarget]; // there is only one pokemon out in single battles battler1PartyId = gBattlerPartyIndexes[gBattlerTarget]; } for (i = firstMonId; i < lastMonId; i++) { if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE && !GetMonData(&party[i], MON_DATA_IS_EGG) && GetMonData(&party[i], MON_DATA_HP) != 0) { validMons++; } } if (validMons <= minNeeded) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } else { if (TryDoForceSwitchOut()) { do { do { i = Random() % monsCount; i += firstMonId; } while (i == battler2PartyId || i == battler1PartyId); } while (GetMonData(&party[i], MON_DATA_SPECIES) == SPECIES_NONE || GetMonData(&party[i], MON_DATA_IS_EGG) == TRUE || GetMonData(&party[i], MON_DATA_HP) == 0); //should be one while loop, but that doesn't match. } *(gBattleStruct->monToSwitchIntoId + gBattlerTarget) = i; if (!IsMultiBattle()) SwitchPartyOrder(gBattlerTarget); if ((gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) || (gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleTypeFlags & BATTLE_TYPE_MULTI) || (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) || (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && gBattleTypeFlags & BATTLE_TYPE_MULTI)) { SwitchPartyOrderLinkMulti(gBattlerTarget, i, 0); SwitchPartyOrderLinkMulti(gBattlerTarget ^ BIT_FLANK, i, 1); } if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) SwitchPartyOrderInGameMulti(gBattlerTarget, i); } } else { TryDoForceSwitchOut(); } } static void Cmd_tryconversiontypechange(void) // randomly changes user's type to one of its moves' type { u8 validMoves = 0; u8 moveChecked; u8 moveType; while (validMoves < MAX_MON_MOVES) { if (gBattleMons[gBattlerAttacker].moves[validMoves] == 0) break; validMoves++; } for (moveChecked = 0; moveChecked < validMoves; moveChecked++) { moveType = gBattleMoves[gBattleMons[gBattlerAttacker].moves[moveChecked]].type; if (moveType == TYPE_MYSTERY) { if (IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) moveType = TYPE_GHOST; else moveType = TYPE_NORMAL; } if (moveType != gBattleMons[gBattlerAttacker].type1 && moveType != gBattleMons[gBattlerAttacker].type2) { break; } } if (moveChecked == validMoves) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } else { do { while ((moveChecked = Random() & (MAX_MON_MOVES - 1)) >= validMoves); moveType = gBattleMoves[gBattleMons[gBattlerAttacker].moves[moveChecked]].type; if (moveType == TYPE_MYSTERY) { if (IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) moveType = TYPE_GHOST; else moveType = TYPE_NORMAL; } } while (moveType == gBattleMons[gBattlerAttacker].type1 || moveType == gBattleMons[gBattlerAttacker].type2); SET_BATTLER_TYPE(gBattlerAttacker, moveType); PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType); gBattlescriptCurrInstr += 5; } } static void Cmd_givepaydaymoney(void) { if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) && gPaydayMoney != 0) { u32 bonusMoney = gPaydayMoney * gBattleStruct->moneyMultiplier; AddMoney(&gSaveBlock1Ptr->money, bonusMoney); PREPARE_HWORD_NUMBER_BUFFER(gBattleTextBuff1, 5, bonusMoney) BattleScriptPush(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_ATK_SIDE) == 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 = gBattleMoves[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 = gBattleMoves[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); } } } static void Cmd_damagetohalftargethp(void) // super fang { 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 (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_ATTACKER_WITH_PARTNER) { gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); BtlController_EmitStatusIconUpdate(BUFFER_A, gBattleMons[gActiveBattler].status1, gBattleMons[gActiveBattler].status2); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 2; } else { 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; } } 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 = 0xFFFF; 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 = 0; 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 (gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].pp < 5) gBattleMons[gBattlerAttacker].pp[i] = gBattleMoves[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 = 0xFFFF; if (IsMoveUncopyableByMimic(gLastMoves[gBattlerTarget]) || gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED || gLastMoves[gBattlerTarget] == 0 || gLastMoves[gBattlerTarget] == 0xFFFF) { 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 (gBattleMoves[gLastMoves[gBattlerTarget]].pp < 5) gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = gBattleMoves[gLastMoves[gBattlerTarget]].pp; else gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = 5; PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastMoves[gBattlerTarget]) gDisableStructs[gBattlerAttacker].mimickedMoves |= gBitTable[gCurrMovePos]; gBattlescriptCurrInstr += 5; } else { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } } } static void Cmd_metronome(void) { while (1) { s32 i; gCurrentMove = (Random() & 0x1FF) + 1; if (gCurrentMove >= MOVES_COUNT) continue; for (i = 0; i < MAX_MON_MOVES; i++); // ? i = -1; while (1) { i++; if (sMovesForbiddenToCopy[i] == gCurrentMove) break; if (sMovesForbiddenToCopy[i] == METRONOME_FORBIDDEN_END) break; } if (sMovesForbiddenToCopy[i] == METRONOME_FORBIDDEN_END) { gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]; 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); } } static void Cmd_mirrorcoatdamagecalculator(void) // a copy of Cmd with the physical -> special field changes { 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 == 0 && 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 = 4; } if (gDisableStructs[gBattlerTarget].encoredMove == 0 && i != 4 && 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); } } static void Cmd_settypetorandomresistance(void) // conversion 2 { if (gLastLandedMoves[gBattlerAttacker] == MOVE_NONE || gLastLandedMoves[gBattlerAttacker] == 0xFFFF) { 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++; } static void Cmd_copymovepermanently(void) // sketch { gChosenMove = 0xFFFF; if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED) && gLastPrintedMoves[gBattlerTarget] != MOVE_STRUGGLE && gLastPrintedMoves[gBattlerTarget] != 0 && gLastPrintedMoves[gBattlerTarget] != 0xFFFF && 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] = gBattleMoves[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 (gBattleMoves[move].effect == EFFECT_SKULL_BASH || gBattleMoves[move].effect == EFFECT_RAZOR_WIND || gBattleMoves[move].effect == EFFECT_SKY_ATTACK || gBattleMoves[move].effect == EFFECT_SOLAR_BEAM || gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE || gBattleMoves[move].effect == EFFECT_BIDE) return TRUE; else return FALSE; } static bool8 IsInvalidForSleepTalkOrAssist(u16 move) { if (move == 0 || 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 (gBattleMoves[move].effect == EFFECT_SOLAR_BEAM && (gBattleWeather & B_WEATHER_SUN)) return 2; if (gBattleMoves[move].effect == EFFECT_SKULL_BASH || gBattleMoves[move].effect == EFFECT_RAZOR_WIND || gBattleMoves[move].effect == EFFECT_SKY_ATTACK || gBattleMoves[move].effect == EFFECT_SOLAR_BEAM || gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE || gBattleMoves[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] != 0 && gLastMoves[gBattlerTarget] != 0xFFFF) { 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_SPECIES2); u8 abilityNum = GetMonData(&party[i], MON_DATA_ABILITY_NUM); if (species != SPECIES_NONE && species != SPECIES_EGG) { u8 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 = gBattleMoves[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 = gBattleMoves[gCurrentMove].power; for (i = 1; i < gDisableStructs[gBattlerAttacker].furyCutterCounter; i++) gDynamicBasePower *= 2; gBattlescriptCurrInstr++; } } static void Cmd_friendshiptodamagecalculation(void) { if (gBattleMoves[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++; } static void Cmd_maxattackhalvehp(void) // belly drum { 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); } } static void Cmd_copyfoestats(void) // psych up { 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) { u8 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); u8 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_1 | F_DYNAMIC_TYPE_2; 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 { gSideStatuses[GET_BATTLER_SIDE(gBattlerTarget)] |= SIDE_STATUS_FUTUREATTACK; gWishFutureKnock.futureSightMove[gBattlerTarget] = gCurrentMove; gWishFutureKnock.futureSightAttacker[gBattlerTarget] = gBattlerAttacker; gWishFutureKnock.futureSightCounter[gBattlerTarget] = 3; gWishFutureKnock.futureSightDmg[gBattlerTarget] = CalculateBaseDamage(&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_SPECIES2) && GetMonData(&party[gBattleCommunication[0]], MON_DATA_SPECIES2) != 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 = gBaseStats[GetMonData(&party[gBattleCommunication[0]], MON_DATA_SPECIES)].baseAttack; gBattleMoveDamage *= gBattleMoves[gCurrentMove].power; gBattleMoveDamage *= (GetMonData(&party[gBattleCommunication[0]], MON_DATA_LEVEL) * 2 / 5 + 2); gBattleMoveDamage /= gBaseStats[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_jumpifattackandspecialattackcannotfall(void) // memento { if (gBattleMons[gBattlerTarget].statStages[STAT_ATK] == MIN_STAT_STAGE && gBattleMons[gBattlerTarget].statStages[STAT_SPATK] == MIN_STAT_STAGE && gBattleCommunication[MISS_TYPE] != B_MSG_PROTECTED) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } else { gActiveBattler = gBattlerAttacker; gBattleMoveDamage = gBattleMons[gActiveBattler].hp; BtlController_EmitHealthBarUpdate(BUFFER_A, INSTANT_HP_BAR_DROP); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 5; } } static void Cmd_setforcedtarget(void) // follow me { gSideTimers[GetBattlerSide(gBattlerAttacker)].followmeTimer = 1; gSideTimers[GetBattlerSide(gBattlerAttacker)].followmeTarget = gBattlerAttacker; gBattlescriptCurrInstr++; } static void Cmd_setcharge(void) { gStatuses3[gBattlerAttacker] |= STATUS3_CHARGED_UP; gDisableStructs[gBattlerAttacker].chargeTimer = 2; gDisableStructs[gBattlerAttacker].chargeTimerStartValue = 2; gBattlescriptCurrInstr++; } static void Cmd_callterrainattack(void) // nature power { gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; gCurrentMove = sNaturePowerMoves[gBattleTerrain]; gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE); BattleScriptPush(gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]); gBattlescriptCurrInstr++; } static void Cmd_cureifburnedparalysedorpoisoned(void) // refresh { 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); } } static void Cmd_tryswapitems(void) // trick { // opponent can't swap items with player in regular battles if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL || (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_SECRET_BASE | BATTLE_TYPE_RECORDED_LINK)))) { 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_EREADER_TRAINER | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_SECRET_BASE | BATTLE_TYPE_RECORDED_LINK)) && (gWishFutureKnock.knockedOffMons[sideAttacker] & gBitTable[gBattlerPartyIndexes[gBattlerAttacker]] || gWishFutureKnock.knockedOffMons[sideTarget] & gBitTable[gBattlerPartyIndexes[gBattlerTarget]])) { gBattlescriptCurrInstr = 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 == 0 && gBattleMons[gBattlerTarget].item == 0) || 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 } } } static void Cmd_trycopyability(void) // role play { if (gBattleMons[gBattlerTarget].ability != 0 && 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; } } static void Cmd_trysetroots(void) // ingrain { if (gStatuses3[gBattlerAttacker] & STATUS3_ROOTED) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } else { gStatuses3[gBattlerAttacker] |= STATUS3_ROOTED; gBattlescriptCurrInstr += 5; } } static void Cmd_doubledamagedealtifdamaged(void) { if ((gProtectStructs[gBattlerAttacker].physicalDmg && gProtectStructs[gBattlerAttacker].physicalBattlerId == gBattlerTarget) || (gProtectStructs[gBattlerAttacker].specialDmg && gProtectStructs[gBattlerAttacker].specialBattlerId == gBattlerTarget)) { gBattleScripting.dmgMultiplier = 2; } gBattlescriptCurrInstr++; } 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 = gBattleMoves[gCurrentMove].power; gDynamicBasePower = gBattleMons[gBattlerAttacker].hp * power / gBattleMons[gBattlerAttacker].maxHP; if (gDynamicBasePower == 0) gDynamicBasePower = 1; } gBattlescriptCurrInstr++; } static void Cmd_tryswapabilities(void) // skill swap { if ((gBattleMons[gBattlerAttacker].ability == 0 && gBattleMons[gBattlerTarget].ability == 0) || gBattleMons[gBattlerAttacker].ability == ABILITY_WONDER_GUARD || gBattleMons[gBattlerTarget].ability == ABILITY_WONDER_GUARD || gMoveResultFlags & MOVE_RESULT_NO_EFFECT) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } else { u8 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_trysetgrudge(void) { if (gStatuses3[gBattlerAttacker] & STATUS3_GRUDGE) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } else { gStatuses3[gBattlerAttacker] |= STATUS3_GRUDGE; gBattlescriptCurrInstr += 5; } } 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* movesArray = 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_SPECIES2) == SPECIES_NONE) continue; if (GetMonData(&party[monId], MON_DATA_SPECIES2) == 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; movesArray[chooseableMovesNo] = move; chooseableMovesNo++; } } if (chooseableMovesNo) { gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; gCalledMove = movesArray[((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 = 1; gBattlescriptCurrInstr += 5; } } static void Cmd_trysetsnatch(void) // snatch { 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: gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_POISON; break; case BATTLE_TERRAIN_LONG_GRASS: gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_SLEEP; break; case BATTLE_TERRAIN_SAND: gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_ACC_MINUS_1; break; case BATTLE_TERRAIN_UNDERWATER: gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_DEF_MINUS_1; break; case BATTLE_TERRAIN_WATER: gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_ATK_MINUS_1; break; case BATTLE_TERRAIN_POND: gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_SPD_MINUS_1; break; case BATTLE_TERRAIN_MOUNTAIN: gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_CONFUSION; break; case BATTLE_TERRAIN_CAVE: gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_FLINCH; break; default: gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_PARALYSIS; break; } gBattlescriptCurrInstr++; } static void Cmd_pickup(void) { s32 i; u16 species, heldItem; u8 ability; if (InBattlePike()) { } else if (InBattlePyramid()) { for (i = 0; i < PARTY_SIZE; i++) { species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2); heldItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); if (GetMonData(&gPlayerParty[i], MON_DATA_ABILITY_NUM)) ability = gBaseStats[species].abilities[1]; else ability = gBaseStats[species].abilities[0]; if (ability == ABILITY_PICKUP && species != SPECIES_NONE && species != SPECIES_EGG && heldItem == ITEM_NONE && (Random() % 10) == 0) { heldItem = GetBattlePyramidPickupItemId(); SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &heldItem); } } } else { for (i = 0; i < PARTY_SIZE; i++) { species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2); heldItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); if (GetMonData(&gPlayerParty[i], MON_DATA_ABILITY_NUM)) ability = gBaseStats[species].abilities[1]; else ability = gBaseStats[species].abilities[0]; if (ability == ABILITY_PICKUP && species != SPECIES_NONE && species != SPECIES_EGG && heldItem == ITEM_NONE && (Random() % 10) == 0) { s32 j; s32 rand = Random() % 100; u8 lvlDivBy10 = (GetMonData(&gPlayerParty[i], MON_DATA_LEVEL) - 1) / 10; if (lvlDivBy10 > 9) lvlDivBy10 = 9; for (j = 0; j < (int)ARRAY_COUNT(sPickupProbabilities); j++) { if (sPickupProbabilities[j] > rand) { SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &sPickupItems[lvlDivBy10 + j]); break; } else if (rand == 99 || rand == 98) { SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &sRarePickupItems[lvlDivBy10 + (99 - rand)]); break; } } } } } 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_CASTFORM_CHANGE, 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; } } static void Cmd_settypebasedhalvers(void) // water and mud sport { bool8 worked = FALSE; if (gBattleMoves[gCurrentMove].effect == EFFECT_MUD_SPORT) { if (!(gStatuses3[gBattlerAttacker] & STATUS3_MUDSPORT)) { gStatuses3[gBattlerAttacker] |= STATUS3_MUDSPORT; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEAKEN_ELECTRIC; worked = TRUE; } } else // water sport { if (!(gStatuses3[gBattlerAttacker] & STATUS3_WATERSPORT)) { gStatuses3[gBattlerAttacker] |= STATUS3_WATERSPORT; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEAKEN_FIRE; worked = TRUE; } } if (worked) gBattlescriptCurrInstr += 5; else gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } static void Cmd_setweatherballtype(void) { if (WEATHER_HAS_EFFECT) { if (gBattleWeather & B_WEATHER_ANY) gBattleScripting.dmgMultiplier = 2; if (gBattleWeather & B_WEATHER_RAIN) *(&gBattleStruct->dynamicMoveType) = TYPE_WATER | F_DYNAMIC_TYPE_2; else if (gBattleWeather & B_WEATHER_SANDSTORM) *(&gBattleStruct->dynamicMoveType) = TYPE_ROCK | F_DYNAMIC_TYPE_2; else if (gBattleWeather & B_WEATHER_SUN) *(&gBattleStruct->dynamicMoveType) = TYPE_FIRE | F_DYNAMIC_TYPE_2; else if (gBattleWeather & B_WEATHER_HAIL) *(&gBattleStruct->dynamicMoveType) = TYPE_ICE | F_DYNAMIC_TYPE_2; else *(&gBattleStruct->dynamicMoveType) = TYPE_NORMAL | F_DYNAMIC_TYPE_2; } gBattlescriptCurrInstr++; } static void Cmd_tryrecycleitem(void) { u16 *usedHeldItem; gActiveBattler = gBattlerAttacker; usedHeldItem = &gBattleStruct->usedHeldItems[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; gBattleScripting.pursuitDoublesAttacker = gBattlerAttacker; 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++; } static void Cmd_removelightscreenreflect(void) // brick break { 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++; } static void Cmd_handleballthrow(void) { u8 ballMultiplier = 0; if (gBattleControllerExecFlags) return; gActiveBattler = gBattlerAttacker; gBattlerTarget = gBattlerAttacker ^ BIT_SIDE; if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) { BtlController_EmitBallThrowAnim(BUFFER_A, BALL_TRAINER_BLOCK); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr = BattleScript_TrainerBallBlock; } else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL) { BtlController_EmitBallThrowAnim(BUFFER_A, BALL_3_SHAKES_SUCCESS); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr = BattleScript_WallyBallThrow; } else { u32 odds; u8 catchRate; if (gLastUsedItem == ITEM_SAFARI_BALL) catchRate = gBattleStruct->safariCatchFactor * 1275 / 100; else catchRate = gBaseStats[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_LANETTES_PC or B_MSG_LANETTES_BOX_FULL if (FlagGet(FLAG_SYS_PC_LANETTE)) gBattleCommunication[MULTISTRING_CHOOSER]++; } gBattleResults.caughtMonSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_SPECIES, NULL); GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_NICKNAME, gBattleResults.caughtMonNick); gBattleResults.caughtMonBall = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_POKEBALL, NULL); 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_BLACK); gBattleCommunication[0]++; break; case 1: if (!gPaletteFade.active) { FreeAllWindowBuffers(); gBattleCommunication[TASK_ID] = DisplayCaughtMonDexPage(SpeciesToNationalPokedexNum(species), gBattleMons[gBattlerTarget].otId, gBattleMons[gBattlerTarget].personality); gBattleCommunication[0]++; } break; case 2: if (!gPaletteFade.active && gMain.callback2 == BattleMainCB2 && !gTasks[gBattleCommunication[TASK_ID]].isActive) { SetVBlankCallback(VBlankCB_Battle); gBattleCommunication[0]++; } break; case 3: InitBattleBgsVideo(); LoadBattleTextboxAndBackground(); gBattle_BG3_X = 256; gBattleCommunication[0]++; break; case 4: if (!IsDma3ManagerBusyWithBgCopy()) { BeginNormalPaletteFade(PALETTES_BG, 0, 16, 0, RGB_BLACK); ShowBg(0); ShowBg(3); gBattleCommunication[0]++; } break; case 5: if (!gPaletteFade.active) gBattlescriptCurrInstr++; 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); } } } void BattleCreateYesNoCursorAt(u8 cursorPosition) { u16 src[2]; src[0] = 1; src[1] = 2; CopyToBgTilemapBufferRect_ChangePalette(0, src, 0x19, 9 + (2 * cursorPosition), 1, 2, 0x11); CopyBgTilemapBufferToVram(0); } void BattleDestroyYesNoCursorAt(u8 cursorPosition) { u16 src[2]; src[0] = 0x1016; src[1] = 0x1016; CopyToBgTilemapBufferRect_ChangePalette(0, src, 0x19, 9 + (2 * cursorPosition), 1, 2, 0x11); CopyBgTilemapBufferToVram(0); } static void Cmd_trygivecaughtmonnick(void) { switch (gBattleCommunication[MULTIUSE_STATE]) { case 0: HandleBattleWindow(24, 8, 29, 13, 0); BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO); gBattleCommunication[MULTIUSE_STATE]++; gBattleCommunication[CURSOR_POSITION] = 0; BattleCreateYesNoCursorAt(0); break; case 1: if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0) { PlaySE(SE_SELECT); BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]); gBattleCommunication[CURSOR_POSITION] = 0; BattleCreateYesNoCursorAt(0); } if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0) { PlaySE(SE_SELECT); BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]); gBattleCommunication[CURSOR_POSITION] = 1; BattleCreateYesNoCursorAt(1); } if (JOY_NEW(A_BUTTON)) { PlaySE(SE_SELECT); if (gBattleCommunication[CURSOR_POSITION] == 0) { gBattleCommunication[MULTIUSE_STATE]++; BeginFastPaletteFade(3); } else { gBattleCommunication[MULTIUSE_STATE] = 4; } } else if (JOY_NEW(B_BUTTON)) { PlaySE(SE_SELECT); gBattleCommunication[MULTIUSE_STATE] = 4; } break; case 2: if (!gPaletteFade.active) { GetMonData(&gEnemyParty[gBattlerPartyIndexes[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_trainerslideout(void) { gActiveBattler = GetBattlerAtPosition(gBattlescriptCurrInstr[1]); BtlController_EmitTrainerSlideBack(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); gBattlescriptCurrInstr += 2; }