pokeemerald/src/battle_4.c
2017-09-20 00:28:00 +02:00

1661 lines
51 KiB
C

#include "global.h"
#include "battle.h"
#include "battle_move_effects.h"
#include "battle_message.h"
#include "battle_ai.h"
#include "moves.h"
#include "abilities.h"
#include "item.h"
#include "items.h"
#include "hold_effects.h"
#include "util.h"
#include "pokemon.h"
#include "calculate_base_damage.h"
#include "rng.h"
#include "battle_controllers.h"
#include "species.h"
// variables
extern u8 gCritMultiplier;
extern s32 gBattleMoveDamage;
extern u32 gStatuses3[BATTLE_BANKS_COUNT];
extern u32 gBattleTypeFlags;
extern struct BattleEnigmaBerry gEnigmaBerries[BATTLE_BANKS_COUNT];
extern struct BattlePokemon gBattleMons[BATTLE_BANKS_COUNT];
extern u8 gActiveBank;
extern u32 gBattleExecBuffer;
extern u8 gNoOfAllBanks;
extern u16 gBattlePartyID[BATTLE_BANKS_COUNT];
extern u8 gTurnOrder[BATTLE_BANKS_COUNT];
extern u8 gUnknown_02024A76[BATTLE_BANKS_COUNT];
extern u16 gCurrentMove;
extern u8 gLastUsedAbility;
extern u16 gBattleWeather;
extern u8 gStringBank;
extern u8 gEffectBank;
extern u8 gAbsentBankFlags;
extern u8 gMultiHitCounter;
extern u16 gChosenMovesByBanks[BATTLE_BANKS_COUNT];
extern u16 gSideAffecting[2];
extern u16 gPauseCounterBattle;
extern u16 gPaydayMoney;
extern u16 gRandomTurnNumber;
extern u8 gBattleOutcome;
extern u8 gBattleTerrain;
extern u16 gTrainerBattleOpponent;
extern u8 gBankAttacker;
extern u8 gBankTarget;
extern const u8* gBattlescriptCurrInstr;
extern u8 gCurrMovePos;
extern u8 gFightStateTracker;
extern u32 gHitMarker;
extern u8 gBattleMoveFlags;
extern u8 gBattleCommunication[];
extern u16 gUnknown_02024250[4];
extern u16 gUnknown_02024258[4];
extern u8 gStringBank;
extern u16 gDynamicBasePower;
extern u16 gLastUsedItem;
extern u16 gBattleMovePower;
extern s32 gHP_dealt;
extern s32 gTakenDmg[BATTLE_BANKS_COUNT];
extern u8 gTakenDmgBanks[BATTLE_BANKS_COUNT];
extern u8 gSentPokesToOpponent[2];
extern u8 gBank1;
extern u16 gExpShareExp;
extern u8 gLeveledUpInBattle;
extern void (*gBattleMainFunc)(void);
extern u8 gPlayerPartyCount;
extern u16 gMoveToLearn;
extern u16 gRandomMove;
extern u8 gBankInMenu;
extern u8 gActionForBanks[4];
extern u8 gCurrentMoveTurn;
extern u8 gBattleBufferB[4][0x200];
extern const struct BattleMove gBattleMoves[];
extern const u16 gMissStrings[];
extern const u8 gTrainerMoney[];
extern const u8 gTypeEffectiveness[];
extern const struct BaseStats gBaseStats[];
// functions
// BattleScripts
extern const u8 BattleScript_MoveEnd[];
extern const u8 BattleScript_NoPPForMove[];
extern const u8 BattleScript_MagicCoatBounce[];
extern const u8 BattleScript_TookAttack[];
extern const u8 BattleScript_SnatchedMove[];
extern const u8 BattleScript_Pausex20[];
extern const u8 BattleScript_SubstituteFade[];
extern const u8 BattleScript_HangedOnMsg[];
extern const u8 BattleScript_OneHitKOMsg[];
extern const u8 BattleScript_EnduredMsg[];
extern const u8 BattleScript_PSNPrevention[];
extern const u8 BattleScript_BRNPrevention[];
extern const u8 BattleScript_PRLZPrevention[];
extern const u8 BattleScript_FlinchPrevention[];
extern const u8 BattleScript_StatUp[];
extern const u8 BattleScript_StatDown[];
extern const u8 BattleScript_NoItemSteal[];
extern const u8 BattleScript_ItemSteal[];
extern const u8 BattleScript_RapidSpinAway[];
extern const u8 BattleScript_TargetPRLZHeal[];
extern const u8 BattleScript_KnockedOff[];
extern const u8 BattleScript_LevelUp[];
extern const u8 BattleScript_WrapFree[];
extern const u8 BattleScript_LeechSeedFree[];
extern const u8 BattleScript_SpikesFree[];
extern const u8 BattleScript_ButItFailed[];
extern const u8 BattleScript_ObliviousPreventsAttraction[];
extern const u8 BattleScript_MistProtected[];
extern const u8 BattleScript_AbilityNoStatLoss[];
extern const u8 BattleScript_AbilityNoSpecificStatLoss[];
extern const u8 BattleScript_TrainerBallBlock[];
extern const u8 BattleScript_WallyBallThrow[];
extern const u8 BattleScript_SuccessBallThrow[];
extern const u8 BattleScript_ShakeBallThrow[];
// read via orr
#define BSScriptRead32(ptr) ((ptr)[0] | (ptr)[1] << 8 | (ptr)[2] << 16 | (ptr)[3] << 24)
#define BSScriptRead8(ptr) (((u8)((ptr)[0])))
#define BSScriptReadPtr(ptr) ((void *)BSScriptRead32(ptr))
// read via add
#define BS2ScriptRead32(ptr) ((ptr)[0] + ((ptr)[1] << 8) + ((ptr)[2] << 16) + ((ptr)[3] << 24))
#define BS2ScriptRead16(ptr) ((ptr)[0] + ((ptr)[1] << 8))
#define BS2ScriptReadPtr(ptr) ((void *)BS2ScriptRead32(ptr))
#define TARGET_PROTECT_AFFECTED ((gProtectStructs[gBankTarget].protected && gBattleMoves[gCurrentMove].flags & FLAG_PROTECT_AFFECTED))
#define TARGET_TURN_DAMAGED (((gSpecialStatuses[gBankTarget].moveturnLostHP_physical || gSpecialStatuses[gBankTarget].moveturnLostHP_physical.moveturnLostHP_special)))
// this file's functions
bool8 IsTwoTurnsMove(u16 move);
void DestinyBondFlagUpdate(void);
u8 AttacksThisTurn(u8 bank, u16 move); // Note: returns 1 if it's a charging turn, otherwise 2.
static void CheckWonderGuardAndLevitate(void);
void atk00_attackcanceler(void);
void atk01_accuracycheck(void);
void atk02_attackstring(void);
void atk03_ppreduce(void);
void atk04_critcalc(void);
void atk05_damagecalc1(void);
void atk06_typecalc(void);
void atk07_dmg_adjustment(void);
void atk08_dmg_adjustment2(void);
void atk09_attackanimation(void);
void atk0A_waitanimation(void);
void atk0B_healthbarupdate(void);
void atk0C_datahpupdate(void);
void atk0D_critmessage(void);
void atk0E_effectiveness_sound(void);
void atk0F_resultmessage(void);
void atk10_printstring(void);
void atk11_printstring_playeronly(void);
void atk12_waitmessage(void);
void atk13_printfromtable(void);
void atk14_printfromtable_playeronly(void);
void atk15_seteffectwithchancetarget(void);
void atk16_seteffectprimary(void);
void atk17_seteffectsecondary(void);
void atk18_status_effect_clear(void);
void atk19_faint_pokemon(void);
void atk1A_faint_animation(void);
void atk1B_faint_effects_clear(void);
void atk1C_jumpifstatus(void);
void atk1D_jumpifstatus2(void);
void atk1E_jumpifability(void);
void atk1F_jumpifsideaffecting(void);
void atk20_jumpifstat(void);
void atk21_jumpifstatus3(void);
void atk22_jumpiftype(void);
void atk23_getexp(void);
void atk24(void);
void atk25_move_values_cleanup(void);
void atk26_set_multihit(void);
void atk27_decrement_multihit(void);
void atk28_goto(void);
void atk29_jumpifbyte(void);
void atk2A_jumpifhalfword(void);
void atk2B_jumpifword(void);
void atk2C_jumpifarrayequal(void);
void atk2D_jumpifarraynotequal(void);
void atk2E_setbyte(void);
void atk2F_addbyte(void);
void atk30_subbyte(void);
void atk31_copyarray(void);
void atk32_copyarray_withindex(void);
void atk33_orbyte(void);
void atk34_orhalfword(void);
void atk35_orword(void);
void atk36_bicbyte(void);
void atk37_bichalfword(void);
void atk38_bicword(void);
void atk39_pause(void);
void atk3A_waitstate(void);
void atk3B_healthbar_update(void);
void atk3C_return(void);
void atk3D_end(void);
void atk3E_end2(void);
void atk3F_end3(void);
void atk40_jump_if_move_affected_by_protect(void);
void atk41_call(void);
void atk42_jumpiftype2(void);
void atk43_jumpifabilitypresent(void);
void atk44(void);
void atk45_playanimation(void);
void atk46_playanimation2(void);
void atk47_setgraphicalstatchangevalues(void);
void atk48_playstatchangeanimation(void);
void atk49_moveendturn(void);
void atk4A_typecalc2(void);
void atk4B_return_atk_to_ball(void);
void atk4C_copy_poke_data(void);
void atk4D_switch_data_update(void);
void atk4E_switchin_anim(void);
void atk4F_jump_if_cannot_switch(void);
void atk50_openpartyscreen(void);
void atk51_switch_handle_order(void);
void atk52_switch_in_effects(void);
void atk53_trainer_slide(void);
void atk54_effectiveness_sound(void);
void atk55_play_sound(void);
void atk56_fainting_cry(void);
void atk57(void);
void atk58_return_to_ball(void);
void atk59_learnmove_inbattle(void);
void atk5A(void);
void atk5B_80256E0(void);
void atk5C_hitanimation(void);
void atk5D_getmoneyreward(void);
void atk5E_8025A70(void);
void atk5F_8025B24(void);
void atk60_increment_gamestat(void);
void atk61_8025BA4(void);
void atk62_08025C6C(void);
void atk63_jumptorandomattack(void);
void atk64_statusanimation(void);
void atk65_status2animation(void);
void atk66_chosenstatusanimation(void);
void atk67_8025ECC(void);
void atk68_80246A0(void);
void atk69_dmg_adjustment2(void);
void atk6A_removeitem(void);
void atk6B_atknameinbuff1(void);
void atk6C_lvlbox_display(void);
void atk6D_set_sentpokes_values(void);
void atk6E_set_atk_to_player0(void);
void atk6F_set_visible(void);
void atk70_record_ability(void);
void atk71_buffer_move_to_learn(void);
void atk72_jump_if_can_run_frombattle(void);
void atk73_hp_thresholds(void);
void atk74_hp_thresholds2(void);
void atk75_8026A58(void);
void atk76_various(void);
void atk77_setprotect(void);
void atk78_faintifabilitynotdamp(void);
void atk79_setatkhptozero(void);
void atk7A_jumpwhiletargetvalid(void);
void atk7B_healhalfHP_if_possible(void);
void atk7C_8025508(void);
void atk7D_set_rain(void);
void atk7E_setreflect(void);
void atk7F_setseeded(void);
void atk80_manipulatedamage(void);
void atk81_setrest(void);
void atk82_jumpifnotfirstturn(void);
void atk83_nop(void);
void atk84_jump_if_cant_sleep(void);
void atk85_stockpile(void);
void atk86_stockpiletobasedamage(void);
void atk87_stockpiletohpheal(void);
void atk88_negativedamage(void);
void atk89_statbuffchange(void);
void atk8A_normalisebuffs(void);
void atk8B_setbide(void);
void atk8C_confuseifrepeatingattackends(void);
void atk8D_setmultihit_counter(void);
void atk8E_prepare_multihit(void);
void atk8F_forcerandomswitch(void);
void atk90_conversion_type_change(void);
void atk91_givepaydaymoney(void);
void atk92_setlightscreen(void);
void atk93_ko_move(void);
void atk94_gethalfcurrentenemyhp(void);
void atk95_setsandstorm(void);
void atk96_weatherdamage(void);
void atk97_try_infatuation(void);
void atk98_status_icon_update(void);
void atk99_setmist(void);
void atk9A_set_focusenergy(void);
void atk9B_transformdataexecution(void);
void atk9C_set_substitute(void);
void atk9D_copyattack(void);
void atk9E_metronome(void);
void atk9F_dmgtolevel(void);
void atkA0_psywavedamageeffect(void);
void atkA1_counterdamagecalculator(void);
void atkA2_mirrorcoatdamagecalculator(void);
void atkA3_disablelastusedattack(void);
void atkA4_setencore(void);
void atkA5_painsplitdmgcalc(void);
void atkA6_settypetorandomresistance(void);
void atkA7_setalwayshitflag(void);
void atkA8_copymovepermanently(void);
void atkA9_sleeptalk_choose_move(void);
void atkAA_set_destinybond(void);
void atkAB_DestinyBondFlagUpdate(void);
void atkAC_remaininghptopower(void);
void atkAD_spite_ppreduce(void);
void atkAE_heal_party_status(void);
void atkAF_cursetarget(void);
void atkB0_set_spikes(void);
void atkB1_set_foresight(void);
void atkB2_setperishsong(void);
void atkB3_rolloutdamagecalculation(void);
void atkB4_jumpifconfusedandstatmaxed(void);
void atkB5_furycuttercalc(void);
void atkB6_happinesstodamagecalculation(void);
void atkB7_presentdamagecalculation(void);
void atkB8_set_safeguard(void);
void atkB9_magnitudedamagecalculation(void);
void atkBA_jumpifnopursuitswitchdmg(void);
void atkBB_setsunny(void);
void atkBC_maxattackhalvehp(void);
void atkBD_copyfoestats(void);
void atkBE_breakfree(void);
void atkBF_set_defense_curl(void);
void atkC0_recoverbasedonsunlight(void);
void atkC1_hidden_power(void);
void atkC2_selectnexttarget(void);
void atkC3_setfutureattack(void);
void atkC4_beat_up(void);
void atkC5_hidepreattack(void);
void atkC6_unhidepostattack(void);
void atkC7_setminimize(void);
void atkC8_sethail(void);
void atkC9_jumpifattackandspecialattackcannotfall(void);
void atkCA_setforcedtarget(void);
void atkCB_setcharge(void);
void atkCC_callterrainattack(void);
void atkCD_cureifburnedparalysedorpoisoned(void);
void atkCE_settorment(void);
void atkCF_jumpifnodamage(void);
void atkD0_settaunt(void);
void atkD1_set_helpinghand(void);
void atkD2_swap_items(void);
void atkD3_copy_ability(void);
void atkD4_wish_effect(void);
void atkD5_setroots(void);
void atkD6_doubledamagedealtifdamaged(void);
void atkD7_setyawn(void);
void atkD8_setdamagetohealthdifference(void);
void atkD9_scaledamagebyhealthratio(void);
void atkDA_abilityswap(void);
void atkDB_imprisoneffect(void);
void atkDC_setgrudge(void);
void atkDD_weightdamagecalculation(void);
void atkDE_asistattackselect(void);
void atkDF_setmagiccoat(void);
void atkE0_setstealstatchange(void);
void atkE1_intimidate_string_loader(void);
void atkE2_switchout_abilities(void);
void atkE3_jumpiffainted(void);
void atkE4_getsecretpowereffect(void);
void atkE5_pickup(void);
void atkE6_castform_change_animation(void);
void atkE7_castform_data_change(void);
void atkE8_settypebasedhalvers(void);
void atkE9_setweatherballtype(void);
void atkEA_recycleitem(void);
void atkEB_settypetoterrain(void);
void atkEC_pursuit_sth(void);
void atkED_802B4B4(void);
void atkEE_removelightscreenreflect(void);
void atkEF_pokeball_catch_calculation(void);
void atkF0_copy_caught_poke(void);
void atkF1_setpoke_as_caught(void);
void atkF2_display_dex_info(void);
void atkF3_nickname_caught_poke(void);
void atkF4_802BEF0(void);
void atkF5_removeattackerstatus1(void);
void atkF6_802BF48(void);
void atkF7_802BF54(void);
void sub_8056EF8(void);
void (* const gBattleScriptingCommandsTable[])(void) =
{
atk00_attackcanceler,
atk01_accuracycheck,
atk02_attackstring,
atk03_ppreduce,
atk04_critcalc,
atk05_damagecalc1,
atk06_typecalc,
atk07_dmg_adjustment,
atk08_dmg_adjustment2,
atk09_attackanimation,
atk0A_waitanimation,
atk0B_healthbarupdate,
atk0C_datahpupdate,
atk0D_critmessage,
atk0E_effectiveness_sound,
atk0F_resultmessage,
atk10_printstring,
atk11_printstring_playeronly,
atk12_waitmessage,
atk13_printfromtable,
atk14_printfromtable_playeronly,
atk15_seteffectwithchancetarget,
atk16_seteffectprimary,
atk17_seteffectsecondary,
atk18_status_effect_clear,
atk19_faint_pokemon,
atk1A_faint_animation,
atk1B_faint_effects_clear,
atk1C_jumpifstatus,
atk1D_jumpifstatus2,
atk1E_jumpifability,
atk1F_jumpifsideaffecting,
atk20_jumpifstat,
atk21_jumpifstatus3,
atk22_jumpiftype,
atk23_getexp,
atk24,
atk25_move_values_cleanup,
atk26_set_multihit,
atk27_decrement_multihit,
atk28_goto,
atk29_jumpifbyte,
atk2A_jumpifhalfword,
atk2B_jumpifword,
atk2C_jumpifarrayequal,
atk2D_jumpifarraynotequal,
atk2E_setbyte,
atk2F_addbyte,
atk30_subbyte,
atk31_copyarray,
atk32_copyarray_withindex,
atk33_orbyte,
atk34_orhalfword,
atk35_orword,
atk36_bicbyte,
atk37_bichalfword,
atk38_bicword,
atk39_pause,
atk3A_waitstate,
atk3B_healthbar_update,
atk3C_return,
atk3D_end,
atk3E_end2,
atk3F_end3,
atk40_jump_if_move_affected_by_protect,
atk41_call,
atk42_jumpiftype2,
atk43_jumpifabilitypresent,
atk44,
atk45_playanimation,
atk46_playanimation2,
atk47_setgraphicalstatchangevalues,
atk48_playstatchangeanimation,
atk49_moveendturn,
atk4A_typecalc2,
atk4B_return_atk_to_ball,
atk4C_copy_poke_data,
atk4D_switch_data_update,
atk4E_switchin_anim,
atk4F_jump_if_cannot_switch,
atk50_openpartyscreen,
atk51_switch_handle_order,
atk52_switch_in_effects,
atk53_trainer_slide,
atk54_effectiveness_sound,
atk55_play_sound,
atk56_fainting_cry,
atk57,
atk58_return_to_ball,
atk59_learnmove_inbattle,
atk5A,
atk5B_80256E0,
atk5C_hitanimation,
atk5D_getmoneyreward,
atk5E_8025A70,
atk5F_8025B24,
atk60_increment_gamestat,
atk61_8025BA4,
atk62_08025C6C,
atk63_jumptorandomattack,
atk64_statusanimation,
atk65_status2animation,
atk66_chosenstatusanimation,
atk67_8025ECC,
atk68_80246A0,
atk69_dmg_adjustment2,
atk6A_removeitem,
atk6B_atknameinbuff1,
atk6C_lvlbox_display,
atk6D_set_sentpokes_values,
atk6E_set_atk_to_player0,
atk6F_set_visible,
atk70_record_ability,
atk71_buffer_move_to_learn,
atk72_jump_if_can_run_frombattle,
atk73_hp_thresholds,
atk74_hp_thresholds2,
atk75_8026A58,
atk76_various,
atk77_setprotect,
atk78_faintifabilitynotdamp,
atk79_setatkhptozero,
atk7A_jumpwhiletargetvalid,
atk7B_healhalfHP_if_possible,
atk7C_8025508,
atk7D_set_rain,
atk7E_setreflect,
atk7F_setseeded,
atk80_manipulatedamage,
atk81_setrest,
atk82_jumpifnotfirstturn,
atk83_nop,
atk84_jump_if_cant_sleep,
atk85_stockpile,
atk86_stockpiletobasedamage,
atk87_stockpiletohpheal,
atk88_negativedamage,
atk89_statbuffchange,
atk8A_normalisebuffs,
atk8B_setbide,
atk8C_confuseifrepeatingattackends,
atk8D_setmultihit_counter,
atk8E_prepare_multihit,
atk8F_forcerandomswitch,
atk90_conversion_type_change,
atk91_givepaydaymoney,
atk92_setlightscreen,
atk93_ko_move,
atk94_gethalfcurrentenemyhp,
atk95_setsandstorm,
atk96_weatherdamage,
atk97_try_infatuation,
atk98_status_icon_update,
atk99_setmist,
atk9A_set_focusenergy,
atk9B_transformdataexecution,
atk9C_set_substitute,
atk9D_copyattack,
atk9E_metronome,
atk9F_dmgtolevel,
atkA0_psywavedamageeffect,
atkA1_counterdamagecalculator,
atkA2_mirrorcoatdamagecalculator,
atkA3_disablelastusedattack,
atkA4_setencore,
atkA5_painsplitdmgcalc,
atkA6_settypetorandomresistance,
atkA7_setalwayshitflag,
atkA8_copymovepermanently,
atkA9_sleeptalk_choose_move,
atkAA_set_destinybond,
atkAB_DestinyBondFlagUpdate,
atkAC_remaininghptopower,
atkAD_spite_ppreduce,
atkAE_heal_party_status,
atkAF_cursetarget,
atkB0_set_spikes,
atkB1_set_foresight,
atkB2_setperishsong,
atkB3_rolloutdamagecalculation,
atkB4_jumpifconfusedandstatmaxed,
atkB5_furycuttercalc,
atkB6_happinesstodamagecalculation,
atkB7_presentdamagecalculation,
atkB8_set_safeguard,
atkB9_magnitudedamagecalculation,
atkBA_jumpifnopursuitswitchdmg,
atkBB_setsunny,
atkBC_maxattackhalvehp,
atkBD_copyfoestats,
atkBE_breakfree,
atkBF_set_defense_curl,
atkC0_recoverbasedonsunlight,
atkC1_hidden_power,
atkC2_selectnexttarget,
atkC3_setfutureattack,
atkC4_beat_up,
atkC5_hidepreattack,
atkC6_unhidepostattack,
atkC7_setminimize,
atkC8_sethail,
atkC9_jumpifattackandspecialattackcannotfall,
atkCA_setforcedtarget,
atkCB_setcharge,
atkCC_callterrainattack,
atkCD_cureifburnedparalysedorpoisoned,
atkCE_settorment,
atkCF_jumpifnodamage,
atkD0_settaunt,
atkD1_set_helpinghand,
atkD2_swap_items,
atkD3_copy_ability,
atkD4_wish_effect,
atkD5_setroots,
atkD6_doubledamagedealtifdamaged,
atkD7_setyawn,
atkD8_setdamagetohealthdifference,
atkD9_scaledamagebyhealthratio,
atkDA_abilityswap,
atkDB_imprisoneffect,
atkDC_setgrudge,
atkDD_weightdamagecalculation,
atkDE_asistattackselect,
atkDF_setmagiccoat,
atkE0_setstealstatchange,
atkE1_intimidate_string_loader,
atkE2_switchout_abilities,
atkE3_jumpiffainted,
atkE4_getsecretpowereffect,
atkE5_pickup,
atkE6_castform_change_animation,
atkE7_castform_data_change,
atkE8_settypebasedhalvers,
atkE9_setweatherballtype,
atkEA_recycleitem,
atkEB_settypetoterrain,
atkEC_pursuit_sth,
atkED_802B4B4,
atkEE_removelightscreenreflect,
atkEF_pokeball_catch_calculation,
atkF0_copy_caught_poke,
atkF1_setpoke_as_caught,
atkF2_display_dex_info,
atkF3_nickname_caught_poke,
atkF4_802BEF0,
atkF5_removeattackerstatus1,
atkF6_802BF48,
atkF7_802BF54,
sub_8056EF8
};
struct StatFractions
{
u8 dividend;
u8 divisor;
};
const struct StatFractions gAccuracyStageRatios[] =
{
{ 33, 100}, // -6
{ 36, 100}, // -5
{ 43, 100}, // -4
{ 50, 100}, // -3
{ 60, 100}, // -2
{ 75, 100}, // -1
{ 1, 1}, // 0
{133, 100}, // +1
{166, 100}, // +2
{ 2, 1}, // +3
{233, 100}, // +4
{133, 50}, // +5
{ 3, 1}, // +6
};
// The chance is 1/N for each stage.
const u16 gCriticalHitChance[] = {16, 8, 4, 3, 2};
const u32 gStatusFlagsForMoveEffects[] =
{
0x00000000,
0x00000007,
0x00000008,
0x00000010,
0x00000020,
0x00000040,
0x00000080,
0x00000007,
0x00000008,
0x00000000,
0x00000070,
0x00000000,
0x00001000,
0x0000E000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00400000,
0x00000000,
0x00000000,
0x04000000,
0x08000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000C00,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000
};
void atk00_attackcanceler(void)
{
s32 i;
if (gBattleOutcome)
{
gFightStateTracker = 0xC;
return;
}
if (gBattleMons[gBankAttacker].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, gBankTarget, 0, 0, 0))
return;
if (!gBattleMons[gBankAttacker].pp[gCurrMovePos] && gCurrentMove != MOVE_STRUGGLE && !(gHitMarker & 0x800200)
&& !(gBattleMons[gBankAttacker].status2 & STATUS2_MULTIPLETURNS))
{
gBattlescriptCurrInstr = BattleScript_NoPPForMove;
gBattleMoveFlags |= MOVESTATUS_MISSED;
return;
}
gHitMarker &= ~(HITMARKER_x800000);
if (!(gHitMarker & HITMARKER_OBEYS) && !(gBattleMons[gBankAttacker].status2 & STATUS2_MULTIPLETURNS))
{
i = IsPokeDisobedient(); // why use the 'i' variable...?
switch (i)
{
case 0:
break;
case 2:
gHitMarker |= HITMARKER_OBEYS;
return;
default:
gBattleMoveFlags |= MOVESTATUS_MISSED;
return;
}
}
gHitMarker |= HITMARKER_OBEYS;
if (gProtectStructs[gBankTarget].bounceMove && gBattleMoves[gCurrentMove].flags & FLAG_MAGICCOAT_AFFECTED)
{
PressurePPLose(gBankAttacker, gBankTarget, MOVE_MAGIC_COAT);
gProtectStructs[gBankTarget].bounceMove = 0;
b_movescr_stack_push_cursor();
gBattlescriptCurrInstr = BattleScript_MagicCoatBounce;
return;
}
for (i = 0; i < gNoOfAllBanks; i++)
{
if ((gProtectStructs[gTurnOrder[i]].stealMove) && gBattleMoves[gCurrentMove].flags & FLAG_SNATCH_AFFECTED)
{
PressurePPLose(gBankAttacker, gTurnOrder[i], MOVE_SNATCH);
gProtectStructs[gTurnOrder[i]].stealMove = 0;
gBattleScripting.bank = gTurnOrder[i];
b_movescr_stack_push_cursor();
gBattlescriptCurrInstr = BattleScript_SnatchedMove;
return;
}
}
if (gSpecialStatuses[gBankTarget].lightningRodRedirected)
{
gSpecialStatuses[gBankTarget].lightningRodRedirected = 0;
gLastUsedAbility = ABILITY_LIGHTNING_ROD;
b_movescr_stack_push_cursor();
gBattlescriptCurrInstr = BattleScript_TookAttack;
RecordAbilityBattle(gBankTarget, gLastUsedAbility);
}
else if (TARGET_PROTECT_AFFECTED
&& (gCurrentMove != MOVE_CURSE || (gBattleMons[gBankAttacker].type1 == TYPE_GHOST || gBattleMons[gBankAttacker].type2 == TYPE_GHOST))
&& ((!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBankAttacker].status2 & STATUS2_MULTIPLETURNS))))
{
CancelMultiTurnMoves(gBankAttacker);
gBattleMoveFlags |= MOVESTATUS_MISSED;
gUnknown_02024250[gBankTarget] = 0;
gUnknown_02024258[gBankTarget] = 0;
gBattleCommunication[6] = 1;
gBattlescriptCurrInstr++;
}
else
{
gBattlescriptCurrInstr++;
}
}
void JumpIfMoveFailed(u8 adder, u16 move)
{
const void* BS_ptr = gBattlescriptCurrInstr + adder;
if (gBattleMoveFlags & MOVESTATUS_NOEFFECT)
{
gUnknown_02024250[gBankTarget] = 0;
gUnknown_02024258[gBankTarget] = 0;
BS_ptr = BSScriptReadPtr(gBattlescriptCurrInstr + 1);
}
else
{
DestinyBondFlagUpdate();
if (AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBankTarget, 0, 0, move))
return;
}
gBattlescriptCurrInstr = BS_ptr;
}
void atk40_jump_if_move_affected_by_protect(void)
{
if (TARGET_PROTECT_AFFECTED)
{
gBattleMoveFlags |= MOVESTATUS_MISSED;
JumpIfMoveFailed(5, 0);
gBattleCommunication[6] = 1;
}
else
{
gBattlescriptCurrInstr += 5;
}
}
bool8 JumpIfMoveAffectedByProtect(u16 move)
{
bool8 affected = FALSE;
if (TARGET_PROTECT_AFFECTED)
{
gBattleMoveFlags |= MOVESTATUS_MISSED;
JumpIfMoveFailed(7, move);
gBattleCommunication[6] = 1;
affected = TRUE;
}
return affected;
}
bool8 AccuracyCalcHelper(u16 move)
{
if (gStatuses3[gBankTarget] & STATUS3_ALWAYS_HITS && gDisableStructs[gBankTarget].bankWithSureHit == gBankAttacker)
{
JumpIfMoveFailed(7, move);
return TRUE;
}
if (!(gHitMarker & HITMARKER_IGNORE_ON_AIR) && gStatuses3[gBankTarget] & STATUS3_ON_AIR)
{
gBattleMoveFlags |= MOVESTATUS_MISSED;
JumpIfMoveFailed(7, move);
return TRUE;
}
gHitMarker &= ~HITMARKER_IGNORE_ON_AIR;
if (!(gHitMarker & HITMARKER_IGNORE_UNDERGROUND) && gStatuses3[gBankTarget] & STATUS3_UNDERGROUND)
{
gBattleMoveFlags |= MOVESTATUS_MISSED;
JumpIfMoveFailed(7, move);
return TRUE;
}
gHitMarker &= ~HITMARKER_IGNORE_UNDERGROUND;
if (!(gHitMarker & HITMARKER_IGNORE_UNDERWATER) && gStatuses3[gBankTarget] & STATUS3_UNDERWATER)
{
gBattleMoveFlags |= MOVESTATUS_MISSED;
JumpIfMoveFailed(7, move);
return TRUE;
}
gHitMarker &= ~HITMARKER_IGNORE_UNDERWATER;
if ((WEATHER_HAS_EFFECT && (gBattleWeather & WEATHER_RAIN_ANY) && gBattleMoves[move].effect == EFFECT_THUNDER)
|| (gBattleMoves[move].effect == EFFECT_ALWAYS_HIT || gBattleMoves[move].effect == EFFECT_VITAL_THROW))
{
JumpIfMoveFailed(7, move);
return TRUE;
}
return FALSE;
}
void atk01_accuracycheck(void)
{
u16 move = BS2ScriptRead16(gBattlescriptCurrInstr + 5);
if (move == 0xFFFE || move == 0xFFFF)
{
if (gStatuses3[gBankTarget] & STATUS3_ALWAYS_HITS && move == 0xFFFF && gDisableStructs[gBankTarget].bankWithSureHit == gBankAttacker)
gBattlescriptCurrInstr += 7;
else if (gStatuses3[gBankTarget] & (STATUS3_ON_AIR | STATUS3_UNDERGROUND | STATUS3_UNDERWATER))
gBattlescriptCurrInstr = BSScriptReadPtr(gBattlescriptCurrInstr + 1);
else if (!JumpIfMoveAffectedByProtect(0))
gBattlescriptCurrInstr += 7;
}
else
{
u8 type, moveAcc, holdEffect, quality;
s8 buff;
u16 calc;
if (move == 0)
move = gCurrentMove;
GET_MOVE_TYPE(move, type);
if (JumpIfMoveAffectedByProtect(move))
return;
if (AccuracyCalcHelper(move))
return;
if (gBattleMons[gBankTarget].status2 & STATUS2_FORESIGHT)
{
u8 acc = gBattleMons[gBankAttacker].statStages[STAT_STAGE_ACC];
buff = acc;
}
else
{
u8 acc = gBattleMons[gBankAttacker].statStages[STAT_STAGE_ACC];
buff = acc + 6 - gBattleMons[gBankTarget].statStages[STAT_STAGE_EVASION];
}
if (buff < 0)
buff = 0;
if (buff > 0xC)
buff = 0xC;
moveAcc = gBattleMoves[move].accuracy;
// check Thunder on sunny weather
if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY && gBattleMoves[move].effect == EFFECT_THUNDER)
moveAcc = 50;
calc = gAccuracyStageRatios[buff].dividend * moveAcc;
calc /= gAccuracyStageRatios[buff].divisor;
if (gBattleMons[gBankAttacker].ability == ABILITY_COMPOUND_EYES)
calc = (calc * 130) / 100; // 1.3 compound eyes boost
if (WEATHER_HAS_EFFECT && gBattleMons[gBankTarget].ability == ABILITY_SAND_VEIL && gBattleWeather & WEATHER_SANDSTORM_ANY)
calc = (calc * 80) / 100; // 1.2 sand veil loss
if (gBattleMons[gBankAttacker].ability == ABILITY_HUSTLE && type < 9)
calc = (calc * 80) / 100; // 1.2 hustle loss
if (gBattleMons[gBankTarget].item == ITEM_ENIGMA_BERRY)
{
holdEffect = gEnigmaBerries[gBankTarget].holdEffect;
quality = gEnigmaBerries[gBankTarget].holdEffectParam;
}
else
{
holdEffect = ItemId_GetHoldEffect(gBattleMons[gBankTarget].item);
quality = ItemId_GetHoldEffectParam(gBattleMons[gBankTarget].item);
}
gStringBank = gBankTarget;
if (holdEffect == HOLD_EFFECT_EVASION_UP)
calc = (calc * (100 - quality)) / 100;
// final calculation
if ((Random() % 100 + 1) > calc)
{
gBattleMoveFlags |= MOVESTATUS_MISSED;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE &&
(gBattleMoves[move].target == MOVE_TARGET_BOTH || gBattleMoves[move].target == MOVE_TARGET_FOES_AND_ALLY))
gBattleCommunication[6] = 2;
else
gBattleCommunication[6] = 0;
CheckWonderGuardAndLevitate();
}
JumpIfMoveFailed(7, move);
}
}
void atk02_attackstring(void)
{
if (gBattleExecBuffer)
return;
if (!(gHitMarker & (HITMARKER_NO_ATTACKSTRING | HITMARKER_ATTACKSTRING_PRINTED)))
{
PrepareStringBattle(4, gBankAttacker);
gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED;
}
gBattlescriptCurrInstr++;
gBattleCommunication[MSG_DISPLAY] = 0;
}
void atk03_ppreduce(void)
{
s32 to_deduct = 1;
if (gBattleExecBuffer)
return;
if (!gSpecialStatuses[gBankAttacker].flag20)
{
switch (gBattleMoves[gCurrentMove].target)
{
case MOVE_TARGET_FOES_AND_ALLY:
to_deduct += AbilityBattleEffects(ABILITYEFFECT_COUNT_ON_FIELD, gBankAttacker, ABILITY_PRESSURE, 0, 0);
break;
case MOVE_TARGET_BOTH:
case MOVE_TARGET_OPPONENTS_FIELD:
to_deduct += AbilityBattleEffects(ABILITYEFFECT_COUNT_OTHER_SIZE, gBankAttacker, ABILITY_PRESSURE, 0, 0);
break;
default:
if (gBankAttacker != gBankTarget && gBattleMons[gBankTarget].ability == ABILITY_PRESSURE)
to_deduct++;
break;
}
}
if (!(gHitMarker & (HITMARKER_NO_PPDEDUCT | HITMARKER_NO_ATTACKSTRING)) && gBattleMons[gBankAttacker].pp[gCurrMovePos])
{
gProtectStructs[gBankAttacker].notFirstStrike = 1;
if (gBattleMons[gBankAttacker].pp[gCurrMovePos] > to_deduct)
gBattleMons[gBankAttacker].pp[gCurrMovePos] -= to_deduct;
else
gBattleMons[gBankAttacker].pp[gCurrMovePos] = 0;
if (!(gBattleMons[gBankAttacker].status2 & STATUS2_TRANSFORMED)
&& !((gDisableStructs[gBankAttacker].unk18_b) & gBitTable[gCurrMovePos]))
{
gActiveBank = gBankAttacker;
EmitSetAttributes(0, REQUEST_PPMOVE1_BATTLE + gCurrMovePos, 0, 1, &gBattleMons[gBankAttacker].pp[gCurrMovePos]);
MarkBufferBankForExecution(gBankAttacker);
}
}
gHitMarker &= ~(HITMARKER_NO_PPDEDUCT);
gBattlescriptCurrInstr++;
}
void atk04_critcalc(void)
{
u8 holdEffect;
u16 item, critChance;
item = gBattleMons[gBankAttacker].item;
if (item == ITEM_ENIGMA_BERRY)
holdEffect = gEnigmaBerries[gBankAttacker].holdEffect;
else
holdEffect = ItemId_GetHoldEffect(item);
gStringBank = gBankAttacker;
critChance = 2 * ((gBattleMons[gBankAttacker].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[gBankAttacker].species == SPECIES_CHANSEY)
+ 2 * (holdEffect == HOLD_EFFECT_STICK && gBattleMons[gBankAttacker].species == SPECIES_FARFETCHD);
if (critChance > 4)
critChance = 4;
if ((gBattleMons[gBankTarget].ability != ABILITY_BATTLE_ARMOR && gBattleMons[gBankTarget].ability != ABILITY_SHELL_ARMOR)
&& !(gStatuses3[gBankAttacker] & STATUS3_CANT_SCORE_A_CRIT)
&& !(gBattleTypeFlags & (BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_FIRST_BATTLE))
&& !(Random() % gCriticalHitChance[critChance]))
gCritMultiplier = 2;
else
gCritMultiplier = 1;
gBattlescriptCurrInstr++;
}
void atk05_damagecalc1(void)
{
u16 sideStatus = gSideAffecting[GET_BANK_SIDE(gBankTarget)];
gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[gBankAttacker], &gBattleMons[gBankTarget], gCurrentMove,
sideStatus, gDynamicBasePower,
gBattleStruct->dynamicMoveType, gBankAttacker, gBankTarget);
gBattleMoveDamage = gBattleMoveDamage * gCritMultiplier * gBattleScripting.dmgMultiplier;
if (gStatuses3[gBankAttacker] & STATUS3_CHARGED_UP && gBattleMoves[gCurrentMove].type == TYPE_ELECTRIC)
gBattleMoveDamage *= 2;
if (gProtectStructs[gBankAttacker].helpingHand)
gBattleMoveDamage = gBattleMoveDamage * 15 / 10;
gBattlescriptCurrInstr++;
}
void AI_CalcDmg(u8 bankAtk, u8 bankDef)
{
u16 sideStatus = gSideAffecting[GET_BANK_SIDE(bankDef)];
gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[bankAtk], &gBattleMons[bankDef], gCurrentMove,
sideStatus, gDynamicBasePower,
gBattleStruct->dynamicMoveType, bankAtk, bankDef);
gDynamicBasePower = 0;
gBattleMoveDamage = gBattleMoveDamage * gCritMultiplier * gBattleScripting.dmgMultiplier;
if (gStatuses3[bankAtk] & STATUS3_CHARGED_UP && gBattleMoves[gCurrentMove].type == TYPE_ELECTRIC)
gBattleMoveDamage *= 2;
if (gProtectStructs[bankAtk].helpingHand)
gBattleMoveDamage = gBattleMoveDamage * 15 / 10;
}
void ModulateDmgByType(u8 multiplier)
{
gBattleMoveDamage = gBattleMoveDamage * multiplier / 10;
if (gBattleMoveDamage == 0 && multiplier != 0)
gBattleMoveDamage = 1;
switch (multiplier)
{
case TYPE_MUL_NO_EFFECT:
gBattleMoveFlags |= MOVESTATUS_NOTAFFECTED;
gBattleMoveFlags &= ~MOVESTATUS_NOTVERYEFFECTIVE;
gBattleMoveFlags &= ~MOVESTATUS_SUPEREFFECTIVE;
break;
case TYPE_MUL_NOT_EFFECTIVE:
if (gBattleMoves[gCurrentMove].power && !(gBattleMoveFlags & MOVESTATUS_NOEFFECT))
{
if (gBattleMoveFlags & MOVESTATUS_SUPEREFFECTIVE)
gBattleMoveFlags &= ~MOVESTATUS_SUPEREFFECTIVE;
else
gBattleMoveFlags |= MOVESTATUS_NOTVERYEFFECTIVE;
}
break;
case TYPE_MUL_SUPER_EFFECTIVE:
if (gBattleMoves[gCurrentMove].power && !(gBattleMoveFlags & MOVESTATUS_NOEFFECT))
{
if (gBattleMoveFlags & MOVESTATUS_NOTVERYEFFECTIVE)
gBattleMoveFlags &= ~MOVESTATUS_NOTVERYEFFECTIVE;
else
gBattleMoveFlags |= MOVESTATUS_SUPEREFFECTIVE;
}
break;
}
}
#define TYPE_FORESIGHT 0xFE
#define TYPE_ENDTABLE 0xFF
void atk06_typecalc(void)
{
s32 i = 0;
u8 moveType;
if (gCurrentMove == MOVE_STRUGGLE)
{
gBattlescriptCurrInstr++;
return;
}
GET_MOVE_TYPE(gCurrentMove, moveType);
// check stab
if (gBattleMons[gBankAttacker].type1 == moveType || gBattleMons[gBankAttacker].type2 == moveType)
{
gBattleMoveDamage = gBattleMoveDamage * 15;
gBattleMoveDamage = gBattleMoveDamage / 10;
}
if (gBattleMons[gBankTarget].ability == ABILITY_LEVITATE && moveType == TYPE_GROUND)
{
gLastUsedAbility = gBattleMons[gBankTarget].ability;
gBattleMoveFlags |= (MOVESTATUS_MISSED | MOVESTATUS_NOTAFFECTED);
gUnknown_02024250[gBankTarget] = 0;
gUnknown_02024258[gBankTarget] = 0;
gBattleCommunication[6] = moveType;
RecordAbilityBattle(gBankTarget, gLastUsedAbility);
}
else
{
while (gTypeEffectiveness[i] != TYPE_ENDTABLE)
{
if (gTypeEffectiveness[i] == TYPE_FORESIGHT)
{
if (gBattleMons[gBankTarget].status2 & STATUS2_FORESIGHT)
break;
i += 3;
continue;
}
else if (gTypeEffectiveness[i] == moveType)
{
// check type1
if (gTypeEffectiveness[i + 1] == gBattleMons[gBankTarget].type1)
ModulateDmgByType(gTypeEffectiveness[i + 2]);
// check type2
if (gTypeEffectiveness[i + 1] == gBattleMons[gBankTarget].type2 &&
gBattleMons[gBankTarget].type1 != gBattleMons[gBankTarget].type2)
ModulateDmgByType(gTypeEffectiveness[i + 2]);
}
i += 3;
}
}
if (gBattleMons[gBankTarget].ability == ABILITY_WONDER_GUARD && AttacksThisTurn(gBankAttacker, gCurrentMove) == 2
&& (!(gBattleMoveFlags & MOVESTATUS_SUPEREFFECTIVE) || ((gBattleMoveFlags & (MOVESTATUS_SUPEREFFECTIVE | MOVESTATUS_NOTVERYEFFECTIVE)) == (MOVESTATUS_SUPEREFFECTIVE | MOVESTATUS_NOTVERYEFFECTIVE)))
&& gBattleMoves[gCurrentMove].power)
{
gLastUsedAbility = ABILITY_WONDER_GUARD;
gBattleMoveFlags |= MOVESTATUS_MISSED;
gUnknown_02024250[gBankTarget] = 0;
gUnknown_02024258[gBankTarget] = 0;
gBattleCommunication[6] = 3;
RecordAbilityBattle(gBankTarget, gLastUsedAbility);
}
if (gBattleMoveFlags & MOVESTATUS_NOTAFFECTED)
gProtectStructs[gBankAttacker].notEffective = 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[gBankTarget].ability == ABILITY_LEVITATE && moveType == TYPE_GROUND)
{
gLastUsedAbility = ABILITY_LEVITATE;
gBattleCommunication[6] = moveType;
RecordAbilityBattle(gBankTarget, ABILITY_LEVITATE);
return;
}
while (gTypeEffectiveness[i] != TYPE_ENDTABLE)
{
if (gTypeEffectiveness[i] == TYPE_FORESIGHT)
{
if (gBattleMons[gBankTarget].status2 & STATUS2_FORESIGHT)
break;
i += 3;
continue;
}
if (gTypeEffectiveness[i] == moveType)
{
// check no effect
if (gTypeEffectiveness[i + 1] == gBattleMons[gBankTarget].type1 && gTypeEffectiveness[i + 2] == 0)
{
gBattleMoveFlags |= MOVESTATUS_NOTAFFECTED;
gProtectStructs[gBankAttacker].notEffective = 1;
}
if (gTypeEffectiveness[i + 1] == gBattleMons[gBankTarget].type2 &&
gBattleMons[gBankTarget].type1 != gBattleMons[gBankTarget].type2 &&
gTypeEffectiveness[i + 2] == TYPE_MUL_NO_EFFECT)
{
gBattleMoveFlags |= MOVESTATUS_NOTAFFECTED;
gProtectStructs[gBankAttacker].notEffective = 1;
}
// check super effective
if (gTypeEffectiveness[i + 1] == gBattleMons[gBankTarget].type1 && gTypeEffectiveness[i + 2] == 20)
flags |= 1;
if (gTypeEffectiveness[i + 1] == gBattleMons[gBankTarget].type2
&& gBattleMons[gBankTarget].type1 != gBattleMons[gBankTarget].type2
&& gTypeEffectiveness[i + 2] == TYPE_MUL_SUPER_EFFECTIVE)
flags |= 1;
// check not very effective
if (gTypeEffectiveness[i + 1] == gBattleMons[gBankTarget].type1 && gTypeEffectiveness[i + 2] == 5)
flags |= 2;
if (gTypeEffectiveness[i + 1] == gBattleMons[gBankTarget].type2
&& gBattleMons[gBankTarget].type1 != gBattleMons[gBankTarget].type2
&& gTypeEffectiveness[i + 2] == TYPE_MUL_NOT_EFFECTIVE)
flags |= 2;
}
i += 3;
}
if (gBattleMons[gBankTarget].ability == ABILITY_WONDER_GUARD && AttacksThisTurn(gBankAttacker, gCurrentMove) == 2)
{
if (((flags & 2) || !(flags & 1)) && gBattleMoves[gCurrentMove].power)
{
gLastUsedAbility = ABILITY_WONDER_GUARD;
gBattleCommunication[6] = 3;
RecordAbilityBattle(gBankTarget, ABILITY_WONDER_GUARD);
}
}
}
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 |= MOVESTATUS_NOTAFFECTED;
*flags &= ~MOVESTATUS_NOTVERYEFFECTIVE;
*flags &= ~MOVESTATUS_SUPEREFFECTIVE;
break;
case TYPE_MUL_NOT_EFFECTIVE:
if (gBattleMoves[move].power && !(*flags & MOVESTATUS_NOEFFECT))
{
if (*flags & MOVESTATUS_SUPEREFFECTIVE)
*flags &= ~MOVESTATUS_SUPEREFFECTIVE;
else
*flags |= MOVESTATUS_NOTVERYEFFECTIVE;
}
break;
case TYPE_MUL_SUPER_EFFECTIVE:
if (gBattleMoves[move].power && !(*flags & MOVESTATUS_NOEFFECT))
{
if (*flags & MOVESTATUS_NOTVERYEFFECTIVE)
*flags &= ~MOVESTATUS_NOTVERYEFFECTIVE;
else
*flags |= MOVESTATUS_SUPEREFFECTIVE;
}
break;
}
}
u8 TypeCalc(u16 move, u8 bankAtk, u8 bankDef)
{
s32 i = 0;
u8 flags = 0;
u8 moveType;
if (move == MOVE_STRUGGLE)
return 0;
moveType = gBattleMoves[move].type;
// check stab
if (gBattleMons[bankAtk].type1 == moveType || gBattleMons[bankAtk].type2 == moveType)
{
gBattleMoveDamage = gBattleMoveDamage * 15;
gBattleMoveDamage = gBattleMoveDamage / 10;
}
if (gBattleMons[bankDef].ability == ABILITY_LEVITATE && moveType == TYPE_GROUND)
{
flags |= (MOVESTATUS_MISSED | MOVESTATUS_NOTAFFECTED);
}
else
{
while (gTypeEffectiveness[i]!= TYPE_ENDTABLE)
{
if (gTypeEffectiveness[i] == TYPE_FORESIGHT)
{
if (gBattleMons[bankDef].status2 & STATUS2_FORESIGHT)
break;
i += 3;
continue;
}
else if (gTypeEffectiveness[i] == moveType)
{
// check type1
if (gTypeEffectiveness[i + 1] == gBattleMons[bankDef].type1)
ModulateDmgByType2(gTypeEffectiveness[i + 2], move, &flags);
// check type2
if (gTypeEffectiveness[i + 1] == gBattleMons[bankDef].type2 &&
gBattleMons[bankDef].type1 != gBattleMons[bankDef].type2)
ModulateDmgByType2(gTypeEffectiveness[i + 2], move, &flags);
}
i += 3;
}
}
if (gBattleMons[bankDef].ability == ABILITY_WONDER_GUARD && !(flags & MOVESTATUS_MISSED)
&& AttacksThisTurn(bankAtk, move) == 2
&& (!(flags & MOVESTATUS_SUPEREFFECTIVE) || ((flags & (MOVESTATUS_SUPEREFFECTIVE | MOVESTATUS_NOTVERYEFFECTIVE)) == (MOVESTATUS_SUPEREFFECTIVE | MOVESTATUS_NOTVERYEFFECTIVE)))
&& gBattleMoves[move].power)
{
flags |= MOVESTATUS_MISSED;
}
return flags;
}
u8 AI_TypeCalc(u16 move, u16 species, u8 ability)
{
s32 i = 0;
u8 flags = 0;
u8 type1 = gBaseStats[species].type1, type2 = gBaseStats[species].type2;
u8 moveType;
if (move == MOVE_STRUGGLE)
return 0;
moveType = gBattleMoves[move].type;
if (ability == ABILITY_LEVITATE && moveType == TYPE_GROUND)
{
flags = MOVESTATUS_MISSED | MOVESTATUS_NOTAFFECTED;
}
else
{
while (gTypeEffectiveness[i] != TYPE_ENDTABLE)
{
if (gTypeEffectiveness[i] == TYPE_FORESIGHT)
{
i += 3;
continue;
}
if (gTypeEffectiveness[i] == moveType)
{
// check type1
if (gTypeEffectiveness[i + 1] == type1)
ModulateDmgByType2(gTypeEffectiveness[i + 2], move, &flags);
// check type2
if (gTypeEffectiveness[i + 1] == type2 && type1 != type2)
ModulateDmgByType2(gTypeEffectiveness[i + 2], move, &flags);
}
i += 3;
}
}
if (ability == ABILITY_WONDER_GUARD
&& (!(flags & MOVESTATUS_SUPEREFFECTIVE) || ((flags & (MOVESTATUS_SUPEREFFECTIVE | MOVESTATUS_NOTVERYEFFECTIVE)) == (MOVESTATUS_SUPEREFFECTIVE | MOVESTATUS_NOTVERYEFFECTIVE)))
&& gBattleMoves[move].power)
flags |= MOVESTATUS_NOTAFFECTED;
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;
}
}
void Unused_ApplyRandomDmgMultiplier(void)
{
ApplyRandomDmgMultiplier();
}
void atk07_dmg_adjustment(void)
{
u8 holdEffect, quality;
ApplyRandomDmgMultiplier();
if (gBattleMons[gBankTarget].item == ITEM_ENIGMA_BERRY)
{
holdEffect = gEnigmaBerries[gBankTarget].holdEffect, quality = gEnigmaBerries[gBankTarget].holdEffectParam;
}
else
{
holdEffect = ItemId_GetHoldEffect(gBattleMons[gBankTarget].item);
quality = ItemId_GetHoldEffectParam(gBattleMons[gBankTarget].item);
}
gStringBank = gBankTarget;
if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < quality)
{
RecordItemEffectBattle(gBankTarget, holdEffect);
gSpecialStatuses[gBankTarget].focusBanded = 1;
}
if (gBattleMons[gBankTarget].status2 & STATUS2_SUBSTITUTE)
goto END;
if (gBattleMoves[gCurrentMove].effect != EFFECT_FALSE_SWIPE && !gProtectStructs[gBankTarget].endured
&& !gSpecialStatuses[gBankTarget].focusBanded)
goto END;
if (gBattleMons[gBankTarget].hp > gBattleMoveDamage)
goto END;
gBattleMoveDamage = gBattleMons[gBankTarget].hp - 1;
if (gProtectStructs[gBankTarget].endured)
{
gBattleMoveFlags |= MOVESTATUS_ENDURED;
}
else if (gSpecialStatuses[gBankTarget].focusBanded)
{
gBattleMoveFlags |= MOVESTATUS_HUNGON;
gLastUsedItem = gBattleMons[gBankTarget].item;
}
END:
gBattlescriptCurrInstr++;
}
void atk08_dmg_adjustment2(void) // The same as 0x7 except it doesn't check for false swipe move effect.
{
u8 holdEffect, quality;
ApplyRandomDmgMultiplier();
if (gBattleMons[gBankTarget].item == ITEM_ENIGMA_BERRY)
{
holdEffect = gEnigmaBerries[gBankTarget].holdEffect, quality = gEnigmaBerries[gBankTarget].holdEffectParam;
}
else
{
holdEffect = ItemId_GetHoldEffect(gBattleMons[gBankTarget].item);
quality = ItemId_GetHoldEffectParam(gBattleMons[gBankTarget].item);
}
gStringBank = gBankTarget;
if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < quality)
{
RecordItemEffectBattle(gBankTarget, holdEffect);
gSpecialStatuses[gBankTarget].focusBanded = 1;
}
if (gBattleMons[gBankTarget].status2 & STATUS2_SUBSTITUTE)
goto END;
if (!gProtectStructs[gBankTarget].endured && !gSpecialStatuses[gBankTarget].focusBanded)
goto END;
if (gBattleMons[gBankTarget].hp > gBattleMoveDamage)
goto END;
gBattleMoveDamage = gBattleMons[gBankTarget].hp - 1;
if (gProtectStructs[gBankTarget].endured)
{
gBattleMoveFlags |= MOVESTATUS_ENDURED;
}
else if (gSpecialStatuses[gBankTarget].focusBanded)
{
gBattleMoveFlags |= MOVESTATUS_HUNGON;
gLastUsedItem = gBattleMons[gBankTarget].item;
}
END:
gBattlescriptCurrInstr++;
}
void atk09_attackanimation(void)
{
if (gBattleExecBuffer)
return;
if ((gHitMarker & HITMARKER_NO_ANIMATIONS) && (gCurrentMove != MOVE_TRANSFORM && gCurrentMove != MOVE_SUBSTITUTE))
{
b_movescr_stack_push(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 (!(gBattleMoveFlags & MOVESTATUS_NOEFFECT))
{
u8 multihit;
gActiveBank = gBankAttacker;
if (gBattleMons[gBankTarget].status2 & STATUS2_SUBSTITUTE)
multihit = gMultiHitCounter;
else if (gMultiHitCounter != 0 && gMultiHitCounter != 1)
{
if (gBattleMons[gBankTarget].hp <= gBattleMoveDamage)
multihit = 1;
else
multihit = gMultiHitCounter;
}
else
multihit = gMultiHitCounter;
EmitMoveAnimation(0, gCurrentMove, gBattleScripting.animTurn, gBattleMovePower, gBattleMoveDamage, gBattleMons[gBankAttacker].friendship, &gDisableStructs[gBankAttacker], multihit);
gBattleScripting.animTurn += 1;
gBattleScripting.animTargetsHit += 1;
MarkBufferBankForExecution(gBankAttacker);
gBattlescriptCurrInstr++;
}
else
{
b_movescr_stack_push(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_Pausex20;
}
}
}
void atk0A_waitanimation(void)
{
if (gBattleExecBuffer == 0)
gBattlescriptCurrInstr++;
}
void atk0B_healthbarupdate(void)
{
register s16 healthValue asm("r1");
if (gBattleExecBuffer)
return;
if (gBattleMoveFlags & MOVESTATUS_NOEFFECT)
goto END;
gActiveBank = GetBattleBank(BSScriptRead8(gBattlescriptCurrInstr + 1));
if (gBattleMons[gActiveBank].status2 & STATUS2_SUBSTITUTE && gDisableStructs[gActiveBank].substituteHP && !(gHitMarker & HITMARKER_IGNORE_SUBSTITUTE))
{
PrepareStringBattle(0x80, gActiveBank);
goto END;
}
healthValue = 10000;
if (healthValue <= gBattleMoveDamage)
healthValue = gBattleMoveDamage;
EmitHealthBarUpdate(0, healthValue);
MarkBufferBankForExecution(gActiveBank);
if (GetBankSide(gActiveBank) == SIDE_PLAYER && gBattleMoveDamage > 0)
gBattleResults.unk5_0 = 1;
END:
gBattlescriptCurrInstr += 2;
}