#include "global.h" #include "battle.h" #include "battle_interface.h" #include "constants/battle_script_commands.h" #include "constants/abilities.h" #include "constants/moves.h" #include "constants/hold_effects.h" #include "constants/battle_anim.h" #include "pokemon.h" #include "constants/species.h" #include "item.h" #include "constants/items.h" #include "util.h" #include "constants/battle_move_effects.h" #include "battle_scripts.h" #include "random.h" #include "text.h" #include "string_util.h" #include "battle_message.h" #include "constants/battle_string_ids.h" #include "battle_ai_script_commands.h" #include "battle_controllers.h" #include "event_data.h" #include "link.h" #include "berry.h" #include "pokedex.h" extern u8 weather_get_current(void); // rom const data static const u16 sSoundMovesTable[] = { MOVE_GROWL, MOVE_ROAR, MOVE_SING, MOVE_SUPERSONIC, MOVE_SCREECH, MOVE_SNORE, MOVE_UPROAR, MOVE_METAL_SOUND, MOVE_GRASS_WHISTLE, MOVE_HYPER_VOICE, 0xFFFF }; static const u8 sAbilitiesAffectedByMoldBreaker[] = { [ABILITY_BATTLE_ARMOR] = 1, [ABILITY_CLEAR_BODY] = 1, [ABILITY_DAMP] = 1, [ABILITY_DRY_SKIN] = 1, [ABILITY_FILTER] = 1, [ABILITY_FLASH_FIRE] = 1, [ABILITY_FLOWER_GIFT] = 1, [ABILITY_HEATPROOF] = 1, [ABILITY_HYPER_CUTTER] = 1, [ABILITY_IMMUNITY] = 1, [ABILITY_INNER_FOCUS] = 1, [ABILITY_INSOMNIA] = 1, [ABILITY_KEEN_EYE] = 1, [ABILITY_LEAF_GUARD] = 1, [ABILITY_LEVITATE] = 1, [ABILITY_LIGHTNING_ROD] = 1, [ABILITY_LIMBER] = 1, [ABILITY_MAGMA_ARMOR] = 1, [ABILITY_MARVEL_SCALE] = 1, [ABILITY_MOTOR_DRIVE] = 1, [ABILITY_OBLIVIOUS] = 1, [ABILITY_OWN_TEMPO] = 1, [ABILITY_SAND_VEIL] = 1, [ABILITY_SHELL_ARMOR] = 1, [ABILITY_SHIELD_DUST] = 1, [ABILITY_SIMPLE] = 1, [ABILITY_SNOW_CLOAK] = 1, [ABILITY_SOLID_ROCK] = 1, [ABILITY_SOUNDPROOF] = 1, [ABILITY_STICKY_HOLD] = 1, [ABILITY_STORM_DRAIN] = 1, [ABILITY_STURDY] = 1, [ABILITY_SUCTION_CUPS] = 1, [ABILITY_TANGLED_FEET] = 1, [ABILITY_THICK_FAT] = 1, [ABILITY_UNAWARE] = 1, [ABILITY_VITAL_SPIRIT] = 1, [ABILITY_VOLT_ABSORB] = 1, [ABILITY_WATER_ABSORB] = 1, [ABILITY_WATER_VEIL] = 1, [ABILITY_WHITE_SMOKE] = 1, [ABILITY_WONDER_GUARD] = 1, [ABILITY_BIG_PECKS] = 1, [ABILITY_CONTRARY] = 1, [ABILITY_FRIEND_GUARD] = 1, [ABILITY_HEAVY_METAL] = 1, [ABILITY_LIGHT_METAL] = 1, [ABILITY_MAGIC_BOUNCE] = 1, [ABILITY_MULTISCALE] = 1, [ABILITY_SAP_SIPPER] = 1, [ABILITY_TELEPATHY] = 1, [ABILITY_WONDER_SKIN] = 1, [ABILITY_AROMA_VEIL] = 1, [ABILITY_BULLETPROOF] = 1, [ABILITY_FLOWER_VEIL] = 1, [ABILITY_FUR_COAT] = 1, [ABILITY_OVERCOAT] = 1, [ABILITY_SWEET_VEIL] = 1, [ABILITY_DAZZLING] = 1, [ABILITY_DISGUISE] = 1, [ABILITY_FLUFFY] = 1, [ABILITY_QUEENLY_MAJESTY] = 1, [ABILITY_WATER_BUBBLE] = 1, }; static const u8 sFlailHpScaleToPowerTable[] = { 1, 200, 4, 150, 9, 100, 16, 80, 32, 40, 48, 20 }; // 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 sSpeedDiffToDmgTable[] = { 40, 60, 80, 120, 150 }; static const u8 sHoldEffectToType[][2] = { {HOLD_EFFECT_BUG_POWER, TYPE_BUG}, {HOLD_EFFECT_STEEL_POWER, TYPE_STEEL}, {HOLD_EFFECT_GROUND_POWER, TYPE_GROUND}, {HOLD_EFFECT_ROCK_POWER, TYPE_ROCK}, {HOLD_EFFECT_GRASS_POWER, TYPE_GRASS}, {HOLD_EFFECT_DARK_POWER, TYPE_DARK}, {HOLD_EFFECT_FIGHTING_POWER, TYPE_FIGHTING}, {HOLD_EFFECT_ELECTRIC_POWER, TYPE_ELECTRIC}, {HOLD_EFFECT_WATER_POWER, TYPE_WATER}, {HOLD_EFFECT_FLYING_POWER, TYPE_FLYING}, {HOLD_EFFECT_POISON_POWER, TYPE_POISON}, {HOLD_EFFECT_ICE_POWER, TYPE_ICE}, {HOLD_EFFECT_GHOST_POWER, TYPE_GHOST}, {HOLD_EFFECT_PSYCHIC_POWER, TYPE_PSYCHIC}, {HOLD_EFFECT_FIRE_POWER, TYPE_FIRE}, {HOLD_EFFECT_DRAGON_POWER, TYPE_DRAGON}, {HOLD_EFFECT_NORMAL_POWER, TYPE_NORMAL}, {HOLD_EFFECT_FAIRY_POWER, TYPE_FAIRY}, }; // percent in UQ_4_12 format static const u16 sPercentToModifier[] = { UQ_4_12(0.00), // 0 UQ_4_12(0.01), // 1 UQ_4_12(0.02), // 2 UQ_4_12(0.03), // 3 UQ_4_12(0.04), // 4 UQ_4_12(0.05), // 5 UQ_4_12(0.06), // 6 UQ_4_12(0.07), // 7 UQ_4_12(0.08), // 8 UQ_4_12(0.09), // 9 UQ_4_12(0.10), // 10 UQ_4_12(0.11), // 11 UQ_4_12(0.12), // 12 UQ_4_12(0.13), // 13 UQ_4_12(0.14), // 14 UQ_4_12(0.15), // 15 UQ_4_12(0.16), // 16 UQ_4_12(0.17), // 17 UQ_4_12(0.18), // 18 UQ_4_12(0.19), // 19 UQ_4_12(0.20), // 20 UQ_4_12(0.21), // 21 UQ_4_12(0.22), // 22 UQ_4_12(0.23), // 23 UQ_4_12(0.24), // 24 UQ_4_12(0.25), // 25 UQ_4_12(0.26), // 26 UQ_4_12(0.27), // 27 UQ_4_12(0.28), // 28 UQ_4_12(0.29), // 29 UQ_4_12(0.30), // 30 UQ_4_12(0.31), // 31 UQ_4_12(0.32), // 32 UQ_4_12(0.33), // 33 UQ_4_12(0.34), // 34 UQ_4_12(0.35), // 35 UQ_4_12(0.36), // 36 UQ_4_12(0.37), // 37 UQ_4_12(0.38), // 38 UQ_4_12(0.39), // 39 UQ_4_12(0.40), // 40 UQ_4_12(0.41), // 41 UQ_4_12(0.42), // 42 UQ_4_12(0.43), // 43 UQ_4_12(0.44), // 44 UQ_4_12(0.45), // 45 UQ_4_12(0.46), // 46 UQ_4_12(0.47), // 47 UQ_4_12(0.48), // 48 UQ_4_12(0.49), // 49 UQ_4_12(0.50), // 50 UQ_4_12(0.51), // 51 UQ_4_12(0.52), // 52 UQ_4_12(0.53), // 53 UQ_4_12(0.54), // 54 UQ_4_12(0.55), // 55 UQ_4_12(0.56), // 56 UQ_4_12(0.57), // 57 UQ_4_12(0.58), // 58 UQ_4_12(0.59), // 59 UQ_4_12(0.60), // 60 UQ_4_12(0.61), // 61 UQ_4_12(0.62), // 62 UQ_4_12(0.63), // 63 UQ_4_12(0.64), // 64 UQ_4_12(0.65), // 65 UQ_4_12(0.66), // 66 UQ_4_12(0.67), // 67 UQ_4_12(0.68), // 68 UQ_4_12(0.69), // 69 UQ_4_12(0.70), // 70 UQ_4_12(0.71), // 71 UQ_4_12(0.72), // 72 UQ_4_12(0.73), // 73 UQ_4_12(0.74), // 74 UQ_4_12(0.75), // 75 UQ_4_12(0.76), // 76 UQ_4_12(0.77), // 77 UQ_4_12(0.78), // 78 UQ_4_12(0.79), // 79 UQ_4_12(0.80), // 80 UQ_4_12(0.81), // 81 UQ_4_12(0.82), // 82 UQ_4_12(0.83), // 83 UQ_4_12(0.84), // 84 UQ_4_12(0.85), // 85 UQ_4_12(0.86), // 86 UQ_4_12(0.87), // 87 UQ_4_12(0.88), // 88 UQ_4_12(0.89), // 89 UQ_4_12(0.90), // 90 UQ_4_12(0.91), // 91 UQ_4_12(0.92), // 92 UQ_4_12(0.93), // 93 UQ_4_12(0.94), // 94 UQ_4_12(0.95), // 95 UQ_4_12(0.96), // 96 UQ_4_12(0.97), // 97 UQ_4_12(0.98), // 98 UQ_4_12(0.99), // 99 UQ_4_12(1.00), // 100 }; #define X UQ_4_12 static const u16 sTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES] = { // normal fight flying poison ground rock bug ghost steel mystery fire water grass electric psychic ice dragon dark fairy {X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(0.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0)}, // normal {X(2.0), X(1.0), X(0.5), X(0.5), X(1.0), X(2.0), X(0.5), X(0.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(2.0), X(1.0), X(2.0), X(0.5)}, // fight {X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(0.5), X(2.0), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(2.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0)}, // flying {X(1.0), X(1.0), X(1.0), X(0.5), X(0.5), X(0.5), X(1.0), X(0.5), X(0.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0)}, // poison {X(1.0), X(1.0), X(0.0), X(2.0), X(1.0), X(2.0), X(0.5), X(1.0), X(2.0), X(1.0), X(2.0), X(1.0), X(0.5), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0)}, // ground {X(1.0), X(0.5), X(2.0), X(1.0), X(0.5), X(1.0), X(2.0), X(1.0), X(0.5), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0)}, // rock {X(1.0), X(0.5), X(0.5), X(0.5), X(1.0), X(1.0), X(1.0), X(0.5), X(0.5), X(1.0), X(0.5), X(1.0), X(2.0), X(1.0), X(2.0), X(1.0), X(1.0), X(2.0), X(0.5)}, // bug {X(0.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(0.5), X(1.0)}, // ghost {X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(0.5), X(1.0), X(0.5), X(0.5), X(1.0), X(0.5), X(1.0), X(2.0), X(1.0), X(1.0), X(2.0)}, // steel {X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0)}, // mystery {X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(2.0), X(1.0), X(2.0), X(1.0), X(0.5), X(0.5), X(2.0), X(1.0), X(1.0), X(2.0), X(0.5), X(1.0), X(1.0)}, // fire {X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(0.5), X(0.5), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0)}, // water {X(1.0), X(1.0), X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), X(1.0), X(0.5), X(1.0), X(0.5), X(2.0), X(0.5), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0)}, // grass {X(1.0), X(1.0), X(2.0), X(1.0), X(0.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(0.5), X(0.5), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0)}, // electric {X(1.0), X(2.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0), X(0.0), X(1.0)}, // psychic {X(1.0), X(1.0), X(2.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(0.5), X(0.5), X(2.0), X(1.0), X(1.0), X(0.5), X(2.0), X(1.0), X(1.0)}, // ice {X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(0.0)}, // dragon {X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(1.0), X(1.0), X(0.5), X(0.5)}, // dark {X(1.0), X(2.0), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(0.5), X(1.0), X(0.5), X(1.0), X(1.0), X(1.0), X(1.0), X(1.0), X(2.0), X(2.0), X(1.0)}, // fairy }; #undef X // code u8 GetBattlerForBattleScript(u8 caseId) { u8 ret = 0; switch (caseId) { case BS_TARGET: ret = gBattlerTarget; break; case BS_ATTACKER: ret = gBattlerAttacker; break; case BS_EFFECT_BATTLER: ret = gEffectBattler; break; case BS_BATTLER_0: ret = 0; break; case BS_SCRIPTING: ret = gBattleScripting.battler; break; case BS_FAINTED: ret = gBattlerFainted; break; case 5: ret = gBattlerFainted; break; case 4: case 6: case 8: case 9: case BS_PLAYER1: ret = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); break; case BS_OPPONENT1: ret = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); break; case BS_PLAYER2: ret = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); break; case BS_OPPONENT2: ret = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); break; } return ret; } void PressurePPLose(u8 defender, u8 attacker, u16 move) { s32 i; if (gBattleMons[defender].ability != ABILITY_PRESSURE) return; for (i = 0; i < 4; i++) { if (gBattleMons[attacker].moves[i] == move) break; } if (i == 4) // mons don't share any moves return; if (gBattleMons[attacker].pp[i] != 0) gBattleMons[attacker].pp[i]--; if (!(gBattleMons[attacker].status2 & STATUS2_TRANSFORMED) && !(gDisableStructs[attacker].unk18_b & gBitTable[i])) { gActiveBattler = attacker; BtlController_EmitSetMonData(0, REQUEST_PPMOVE1_BATTLE + i, 0, 1, &gBattleMons[gActiveBattler].pp[i]); MarkBattlerForControllerExec(gActiveBattler); } } void PressurePPLoseOnUsingImprision(u8 attacker) { s32 i, j; s32 imprisionPos = 4; u8 atkSide = GetBattlerSide(attacker); for (i = 0; i < gBattlersCount; i++) { if (atkSide != GetBattlerSide(i) && gBattleMons[i].ability == ABILITY_PRESSURE) { for (j = 0; j < 4; j++) { if (gBattleMons[attacker].moves[j] == MOVE_IMPRISON) break; } if (j != 4) { imprisionPos = j; if (gBattleMons[attacker].pp[j] != 0) gBattleMons[attacker].pp[j]--; } } } if (imprisionPos != 4 && !(gBattleMons[attacker].status2 & STATUS2_TRANSFORMED) && !(gDisableStructs[attacker].unk18_b & gBitTable[imprisionPos])) { gActiveBattler = attacker; BtlController_EmitSetMonData(0, REQUEST_PPMOVE1_BATTLE + imprisionPos, 0, 1, &gBattleMons[gActiveBattler].pp[imprisionPos]); MarkBattlerForControllerExec(gActiveBattler); } } void PressurePPLoseOnUsingPerishSong(u8 attacker) { s32 i, j; s32 perishSongPos = 4; for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ABILITY_PRESSURE && i != attacker) { for (j = 0; j < 4; j++) { if (gBattleMons[attacker].moves[j] == MOVE_PERISH_SONG) break; } if (j != 4) { perishSongPos = j; if (gBattleMons[attacker].pp[j] != 0) gBattleMons[attacker].pp[j]--; } } } if (perishSongPos != 4 && !(gBattleMons[attacker].status2 & STATUS2_TRANSFORMED) && !(gDisableStructs[attacker].unk18_b & gBitTable[perishSongPos])) { gActiveBattler = attacker; BtlController_EmitSetMonData(0, REQUEST_PPMOVE1_BATTLE + perishSongPos, 0, 1, &gBattleMons[gActiveBattler].pp[perishSongPos]); MarkBattlerForControllerExec(gActiveBattler); } } void MarkAllBattlersForControllerExec(void) // unused { s32 i; if (gBattleTypeFlags & BATTLE_TYPE_LINK) { for (i = 0; i < gBattlersCount; i++) gBattleControllerExecFlags |= gBitTable[i] << 0x1C; } else { for (i = 0; i < gBattlersCount; i++) gBattleControllerExecFlags |= gBitTable[i]; } } void MarkBattlerForControllerExec(u8 battlerId) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) gBattleControllerExecFlags |= gBitTable[battlerId] << 0x1C; else gBattleControllerExecFlags |= gBitTable[battlerId]; } void sub_803F850(u8 arg0) { s32 i; for (i = 0; i < GetLinkPlayerCount(); i++) gBattleControllerExecFlags |= gBitTable[arg0] << (i << 2); gBattleControllerExecFlags &= ~(0x10000000 << arg0); } void CancelMultiTurnMoves(u8 battler) { gBattleMons[battler].status2 &= ~(STATUS2_MULTIPLETURNS); gBattleMons[battler].status2 &= ~(STATUS2_LOCK_CONFUSE); gBattleMons[battler].status2 &= ~(STATUS2_UPROAR); gBattleMons[battler].status2 &= ~(STATUS2_BIDE); gStatuses3[battler] &= ~(STATUS3_SEMI_INVULNERABLE); gDisableStructs[battler].rolloutCounter1 = 0; gDisableStructs[battler].furyCutterCounter = 0; } bool8 WasUnableToUseMove(u8 battler) { if (gProtectStructs[battler].prlzImmobility || gProtectStructs[battler].targetNotAffected || gProtectStructs[battler].usedImprisionedMove || gProtectStructs[battler].loveImmobility || gProtectStructs[battler].usedDisabledMove || gProtectStructs[battler].usedTauntedMove || gProtectStructs[battler].flag2Unknown || gProtectStructs[battler].flinchImmobility || gProtectStructs[battler].confusionSelfDmg) return TRUE; else return FALSE; } void PrepareStringBattle(u16 stringId, u8 battler) { gActiveBattler = battler; BtlController_EmitPrintString(0, stringId); MarkBattlerForControllerExec(gActiveBattler); } void ResetSentPokesToOpponentValue(void) { s32 i; u32 bits = 0; gSentPokesToOpponent[0] = 0; gSentPokesToOpponent[1] = 0; for (i = 0; i < gBattlersCount; i += 2) bits |= gBitTable[gBattlerPartyIndexes[i]]; for (i = 1; i < gBattlersCount; i += 2) gSentPokesToOpponent[(i & BIT_FLANK) >> 1] = bits; } void sub_803F9EC(u8 battler) { s32 i = 0; u32 bits = 0; if (GetBattlerSide(battler) == B_SIDE_OPPONENT) { u8 flank = ((battler & BIT_FLANK) >> 1); gSentPokesToOpponent[flank] = 0; for (i = 0; i < gBattlersCount; i += 2) { if (!(gAbsentBattlerFlags & gBitTable[i])) bits |= gBitTable[gBattlerPartyIndexes[i]]; } gSentPokesToOpponent[flank] = bits; } } void sub_803FA70(u8 battler) { if (GetBattlerSide(battler) == B_SIDE_OPPONENT) { sub_803F9EC(battler); } else { s32 i; for (i = 1; i < gBattlersCount; i++) gSentPokesToOpponent[(i & BIT_FLANK) >> 1] |= gBitTable[gBattlerPartyIndexes[battler]]; } } void BattleScriptPush(const u8 *bsPtr) { gBattleResources->battleScriptsStack->ptr[gBattleResources->battleScriptsStack->size++] = bsPtr; } void BattleScriptPushCursor(void) { gBattleResources->battleScriptsStack->ptr[gBattleResources->battleScriptsStack->size++] = gBattlescriptCurrInstr; } void BattleScriptPop(void) { gBattlescriptCurrInstr = gBattleResources->battleScriptsStack->ptr[--gBattleResources->battleScriptsStack->size]; } u8 TrySetCantSelectMoveBattleScript(void) { u8 limitations = 0; u16 move = gBattleMons[gActiveBattler].moves[gBattleBufferB[gActiveBattler][2]]; u8 holdEffect; u16* choicedMove = &gBattleStruct->choicedMove[gActiveBattler]; if (gDisableStructs[gActiveBattler].disabledMove == move && move != 0) { gBattleScripting.battler = gActiveBattler; gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingDisabledMoveInPalace; gProtectStructs[gActiveBattler].flag_x10 = 1; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingDisabledMove; limitations = 1; } } if (move == gLastMoves[gActiveBattler] && move != MOVE_STRUGGLE && (gBattleMons[gActiveBattler].status2 & STATUS2_TORMENT)) { CancelMultiTurnMoves(gActiveBattler); if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingTormentedMoveInPalace; gProtectStructs[gActiveBattler].flag_x10 = 1; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingTormentedMove; limitations++; } } if (gDisableStructs[gActiveBattler].tauntTimer1 != 0 && gBattleMoves[move].power == 0) { gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveTauntInPalace; gProtectStructs[gActiveBattler].flag_x10 = 1; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveTaunt; limitations++; } } if (GetImprisonedMovesCount(gActiveBattler, move)) { gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingImprisionedMoveInPalace; gProtectStructs[gActiveBattler].flag_x10 = 1; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingImprisionedMove; limitations++; } } if (gBattleMons[gActiveBattler].item == ITEM_ENIGMA_BERRY) holdEffect = gEnigmaBerries[gActiveBattler].holdEffect; else holdEffect = ItemId_GetHoldEffect(gBattleMons[gActiveBattler].item); gPotentialItemEffectBattler = gActiveBattler; if (holdEffect == HOLD_EFFECT_CHOICE_BAND && *choicedMove != 0 && *choicedMove != 0xFFFF && *choicedMove != move) { gCurrentMove = *choicedMove; gLastUsedItem = gBattleMons[gActiveBattler].item; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gProtectStructs[gActiveBattler].flag_x10 = 1; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveChoiceItem; limitations++; } } if (gBattleMons[gActiveBattler].pp[gBattleBufferB[gActiveBattler][2]] == 0) { if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gProtectStructs[gActiveBattler].flag_x10 = 1; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingMoveWithNoPP; limitations++; } } return limitations; } u8 CheckMoveLimitations(u8 battlerId, u8 unusableMoves, u8 check) { u8 holdEffect; u16 *choicedMove = &gBattleStruct->choicedMove[battlerId]; s32 i; if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY) holdEffect = gEnigmaBerries[battlerId].holdEffect; else holdEffect = ItemId_GetHoldEffect(gBattleMons[battlerId].item); gPotentialItemEffectBattler = battlerId; for (i = 0; i < MAX_BATTLERS_COUNT; i++) { if (gBattleMons[battlerId].moves[i] == 0 && check & MOVE_LIMITATION_ZEROMOVE) unusableMoves |= gBitTable[i]; if (gBattleMons[battlerId].pp[i] == 0 && check & MOVE_LIMITATION_PP) unusableMoves |= gBitTable[i]; if (gBattleMons[battlerId].moves[i] == gDisableStructs[battlerId].disabledMove && check & MOVE_LIMITATION_DISABLED) unusableMoves |= gBitTable[i]; if (gBattleMons[battlerId].moves[i] == gLastMoves[battlerId] && check & MOVE_LIMITATION_TORMENTED && gBattleMons[battlerId].status2 & STATUS2_TORMENT) unusableMoves |= gBitTable[i]; if (gDisableStructs[battlerId].tauntTimer1 && check & MOVE_LIMITATION_TAUNT && gBattleMoves[gBattleMons[battlerId].moves[i]].power == 0) unusableMoves |= gBitTable[i]; if (GetImprisonedMovesCount(battlerId, gBattleMons[battlerId].moves[i]) && check & MOVE_LIMITATION_IMPRISION) unusableMoves |= gBitTable[i]; if (gDisableStructs[battlerId].encoreTimer1 && gDisableStructs[battlerId].encoredMove != gBattleMons[battlerId].moves[i]) unusableMoves |= gBitTable[i]; if (holdEffect == HOLD_EFFECT_CHOICE_BAND && *choicedMove != 0 && *choicedMove != 0xFFFF && *choicedMove != gBattleMons[battlerId].moves[i]) unusableMoves |= gBitTable[i]; } return unusableMoves; } bool8 AreAllMovesUnusable(void) { u8 unusable; unusable = CheckMoveLimitations(gActiveBattler, 0, 0xFF); if (unusable == 0xF) // All moves are unusable. { gProtectStructs[gActiveBattler].noValidMoves = 1; gSelectionBattleScripts[gActiveBattler] = BattleScript_NoMovesLeft; } else { gProtectStructs[gActiveBattler].noValidMoves = 0; } return (unusable == 0xF); } u8 GetImprisonedMovesCount(u8 battlerId, u16 move) { s32 i; u8 imprisionedMoves = 0; u8 battlerSide = GetBattlerSide(battlerId); for (i = 0; i < gBattlersCount; i++) { if (battlerSide != GetBattlerSide(i) && gStatuses3[i] & STATUS3_IMPRISONED_OTHERS) { s32 j; for (j = 0; j < 4; j++) { if (move == gBattleMons[i].moves[j]) break; } if (j < 4) imprisionedMoves++; } } return imprisionedMoves; } enum { ENDTURN_ORDER, ENDTURN_REFLECT, ENDTURN_LIGHT_SCREEN, ENDTURN_AURORA_VEIL, ENDTURN_MIST, ENDTURN_LUCKY_CHANT, ENDTURN_SAFEGUARD, ENDTURN_TAILWIND, ENDTURN_WISH, ENDTURN_RAIN, ENDTURN_SANDSTORM, ENDTURN_SUN, ENDTURN_HAIL, ENDTURN_GRAVITY, ENDTURN_WATER_SPORT, ENDTURN_MUD_SPORT, ENDTURN_TRICK_ROOM, ENDTURN_WONDER_ROOM, ENDTURN_MAGIC_ROOM, ENDTURN_ELECTRIC_TERRAIN, ENDTURN_MISTY_TERRAIN, ENDTURN_GRASSY_TERRAIN, ENDTURN_PSYCHIC_TERRAIN, ENDTURN_FIELD_COUNT, }; u8 DoFieldEndTurnEffects(void) { u8 effect = 0; for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount && gAbsentBattlerFlags & gBitTable[gBattlerAttacker]; gBattlerAttacker++) { } for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount && gAbsentBattlerFlags & gBitTable[gBattlerTarget]; gBattlerTarget++) { } do { s32 i; u8 side; switch (gBattleStruct->turnCountersTracker) { case ENDTURN_ORDER: for (i = 0; i < gBattlersCount; i++) { gBattlerByTurnOrder[i] = i; } for (i = 0; i < gBattlersCount - 1; i++) { s32 j; for (j = i + 1; j < gBattlersCount; j++) { if (GetWhoStrikesFirst(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], 0)) SwapTurnOrder(i, j); } } gBattleStruct->turnCountersTracker++; gBattleStruct->turnEffectsSide = 0; // fall through case ENDTURN_REFLECT: while (gBattleStruct->turnEffectsSide < 2) { side = gBattleStruct->turnEffectsSide; gActiveBattler = gBattlerAttacker = gSideTimers[side].reflectBattlerId; if (gSideStatuses[side] & SIDE_STATUS_REFLECT) { if (--gSideTimers[side].reflectTimer == 0) { gSideStatuses[side] &= ~SIDE_STATUS_REFLECT; BattleScriptExecute(BattleScript_SideStatusWoreOff); PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_REFLECT); effect++; } } gBattleStruct->turnEffectsSide++; if (effect) break; } if (!effect) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnEffectsSide = 0; } break; case ENDTURN_LIGHT_SCREEN: while (gBattleStruct->turnEffectsSide < 2) { side = gBattleStruct->turnEffectsSide; gActiveBattler = gBattlerAttacker = gSideTimers[side].lightscreenBattlerId; if (gSideStatuses[side] & SIDE_STATUS_LIGHTSCREEN) { if (--gSideTimers[side].lightscreenTimer == 0) { gSideStatuses[side] &= ~SIDE_STATUS_LIGHTSCREEN; BattleScriptExecute(BattleScript_SideStatusWoreOff); gBattleCommunication[MULTISTRING_CHOOSER] = side; PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_LIGHT_SCREEN); effect++; } } gBattleStruct->turnEffectsSide++; if (effect) break; } if (!effect) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnEffectsSide = 0; } break; case ENDTURN_AURORA_VEIL: while (gBattleStruct->turnEffectsSide < 2) { side = gBattleStruct->turnEffectsSide; gActiveBattler = gBattlerAttacker = gSideTimers[side].auroraVeilBattlerId; if (gSideStatuses[side] & SIDE_STATUS_AURORA_VEIL) { if (--gSideTimers[side].auroraVeilTimer == 0) { gSideStatuses[side] &= ~SIDE_STATUS_AURORA_VEIL; BattleScriptExecute(BattleScript_AuroraVeilEnds); effect++; } } gBattleStruct->turnEffectsSide++; if (effect) break; } if (!effect) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnEffectsSide = 0; } break; case ENDTURN_MIST: while (gBattleStruct->turnEffectsSide < 2) { side = gBattleStruct->turnEffectsSide; gActiveBattler = gBattlerAttacker = gSideTimers[side].mistBattlerId; if (gSideTimers[side].mistTimer != 0 && --gSideTimers[side].mistTimer == 0) { gSideStatuses[side] &= ~SIDE_STATUS_MIST; BattleScriptExecute(BattleScript_SideStatusWoreOff); gBattleCommunication[MULTISTRING_CHOOSER] = side; PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_MIST); effect++; } gBattleStruct->turnEffectsSide++; if (effect) break; } if (!effect) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnEffectsSide = 0; } break; case ENDTURN_SAFEGUARD: while (gBattleStruct->turnEffectsSide < 2) { side = gBattleStruct->turnEffectsSide; gActiveBattler = gBattlerAttacker = gSideTimers[side].safeguardBattlerId; if (gSideStatuses[side] & SIDE_STATUS_SAFEGUARD) { if (--gSideTimers[side].safeguardTimer == 0) { gSideStatuses[side] &= ~SIDE_STATUS_SAFEGUARD; BattleScriptExecute(BattleScript_SafeguardEnds); effect++; } } gBattleStruct->turnEffectsSide++; if (effect) break; } if (!effect) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnEffectsSide = 0; } break; case ENDTURN_LUCKY_CHANT: while (gBattleStruct->turnEffectsSide < 2) { side = gBattleStruct->turnEffectsSide; gActiveBattler = gBattlerAttacker = gSideTimers[side].luckyChantBattlerId; if (gSideStatuses[side] & SIDE_STATUS_LUCKY_CHANT) { if (--gSideTimers[side].luckyChantTimer == 0) { gSideStatuses[side] &= ~SIDE_STATUS_LUCKY_CHANT; BattleScriptExecute(BattleScript_LuckyChantEnds); effect++; } } gBattleStruct->turnEffectsSide++; if (effect) break; } if (!effect) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnEffectsSide = 0; } break; case ENDTURN_TAILWIND: while (gBattleStruct->turnEffectsSide < 2) { side = gBattleStruct->turnEffectsSide; gActiveBattler = gBattlerAttacker = gSideTimers[side].tailwindBattlerId; if (gSideStatuses[side] & SIDE_STATUS_TAILWIND) { if (--gSideTimers[side].tailwindTimer == 0) { gSideStatuses[side] &= ~SIDE_STATUS_TAILWIND; BattleScriptExecute(BattleScript_TailwindEnds); effect++; } } gBattleStruct->turnEffectsSide++; if (effect) break; } if (!effect) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnEffectsSide = 0; } break; case ENDTURN_WISH: while (gBattleStruct->turnEffectsSide < gBattlersCount) { gActiveBattler = gBattlerByTurnOrder[gBattleStruct->turnEffectsSide]; if (gWishFutureKnock.wishCounter[gActiveBattler] != 0 && --gWishFutureKnock.wishCounter[gActiveBattler] == 0 && gBattleMons[gActiveBattler].hp != 0) { gBattlerTarget = gActiveBattler; BattleScriptExecute(BattleScript_WishComesTrue); effect++; } gBattleStruct->turnEffectsSide++; if (effect) break; } if (!effect) { gBattleStruct->turnCountersTracker++; } break; case ENDTURN_RAIN: if (gBattleWeather & WEATHER_RAIN_ANY) { if (!(gBattleWeather & WEATHER_RAIN_PERMANENT)) { if (--gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~WEATHER_RAIN_TEMPORARY; gBattleWeather &= ~WEATHER_RAIN_DOWNPOUR; gBattleCommunication[MULTISTRING_CHOOSER] = 2; } else if (gBattleWeather & WEATHER_RAIN_DOWNPOUR) gBattleCommunication[MULTISTRING_CHOOSER] = 1; else gBattleCommunication[MULTISTRING_CHOOSER] = 0; } else if (gBattleWeather & WEATHER_RAIN_DOWNPOUR) { gBattleCommunication[MULTISTRING_CHOOSER] = 1; } else { gBattleCommunication[MULTISTRING_CHOOSER] = 0; } BattleScriptExecute(BattleScript_RainContinuesOrEnds); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_SANDSTORM: if (gBattleWeather & WEATHER_SANDSTORM_ANY) { if (!(gBattleWeather & WEATHER_SANDSTORM_PERMANENT) && --gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~WEATHER_SANDSTORM_TEMPORARY; gBattlescriptCurrInstr = BattleScript_SandStormHailEnds; } else { gBattlescriptCurrInstr = BattleScript_DamagingWeatherContinues; } gBattleScripting.animArg1 = B_ANIM_SANDSTORM_CONTINUES; gBattleCommunication[MULTISTRING_CHOOSER] = 0; BattleScriptExecute(gBattlescriptCurrInstr); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_SUN: if (gBattleWeather & WEATHER_SUN_ANY) { if (!(gBattleWeather & WEATHER_SUN_PERMANENT) && --gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~WEATHER_SUN_TEMPORARY; gBattlescriptCurrInstr = BattleScript_SunlightFaded; } else { gBattlescriptCurrInstr = BattleScript_SunlightContinues; } BattleScriptExecute(gBattlescriptCurrInstr); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_HAIL: if (gBattleWeather & WEATHER_HAIL_ANY) { if (--gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~WEATHER_HAIL; gBattlescriptCurrInstr = BattleScript_SandStormHailEnds; } else { gBattlescriptCurrInstr = BattleScript_DamagingWeatherContinues; } gBattleScripting.animArg1 = B_ANIM_HAIL_CONTINUES; gBattleCommunication[MULTISTRING_CHOOSER] = 1; BattleScriptExecute(gBattlescriptCurrInstr); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_TRICK_ROOM: if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && --gFieldTimers.trickRoomTimer == 0) { gFieldStatuses &= ~(STATUS_FIELD_TRICK_ROOM); BattleScriptExecute(BattleScript_TrickRoomEnds); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_WONDER_ROOM: if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM && --gFieldTimers.wonderRoomTimer == 0) { gFieldStatuses &= ~(STATUS_FIELD_WONDER_ROOM); BattleScriptExecute(BattleScript_WonderRoomEnds); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_MAGIC_ROOM: if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM && --gFieldTimers.magicRoomTimer == 0) { gFieldStatuses &= ~(STATUS_FIELD_MAGIC_ROOM); BattleScriptExecute(BattleScript_MagicRoomEnds); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_ELECTRIC_TERRAIN: if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && --gFieldTimers.electricTerrainTimer == 0) { gFieldStatuses &= ~(STATUS_FIELD_ELECTRIC_TERRAIN); BattleScriptExecute(BattleScript_ElectricTerrainEnds); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_MISTY_TERRAIN: if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN && --gFieldTimers.mistyTerrainTimer == 0) { gFieldStatuses &= ~(STATUS_FIELD_MISTY_TERRAIN); BattleScriptExecute(BattleScript_MistyTerrainEnds); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_GRASSY_TERRAIN: if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && --gFieldTimers.grassyTerrainTimer == 0) { gFieldStatuses &= ~(STATUS_FIELD_GRASSY_TERRAIN); BattleScriptExecute(BattleScript_GrassyTerrainEnds); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_PSYCHIC_TERRAIN: if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN && --gFieldTimers.psychicTerrainTimer == 0) { gFieldStatuses &= ~(STATUS_FIELD_PSYCHIC_TERRAIN); BattleScriptExecute(BattleScript_PsychicTerrainEnds); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_WATER_SPORT: if (gFieldStatuses & STATUS_FIELD_WATERSPORT && --gFieldTimers.waterSportTimer == 0) { gFieldStatuses &= ~(STATUS_FIELD_WATERSPORT); BattleScriptExecute(BattleScript_WaterSportEnds); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_MUD_SPORT: if (gFieldStatuses & STATUS_FIELD_MUDSPORT && --gFieldTimers.mudSportTimer == 0) { gFieldStatuses &= ~(STATUS_FIELD_MUDSPORT); BattleScriptExecute(BattleScript_MudSportEnds); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_GRAVITY: if (gFieldStatuses & STATUS_FIELD_GRAVITY && --gFieldTimers.gravityTimer == 0) { gFieldStatuses &= ~(STATUS_FIELD_GRAVITY); BattleScriptExecute(BattleScript_GravityEnds); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_FIELD_COUNT: effect++; break; } } while (effect == 0); return (gBattleMainFunc != BattleTurnPassed); } enum { ENDTURN_INGRAIN, ENDTURN_AQUA_RING, ENDTURN_ABILITIES, ENDTURN_ITEMS1, ENDTURN_LEECH_SEED, ENDTURN_POISON, ENDTURN_BAD_POISON, ENDTURN_BURN, ENDTURN_NIGHTMARES, ENDTURN_CURSE, ENDTURN_WRAP, ENDTURN_UPROAR, ENDTURN_THRASH, ENDTURN_DISABLE, ENDTURN_ENCORE, ENDTURN_MAGNET_RISE, ENDTURN_TELEKINESIS, ENDTURN_HEALBLOCK, ENDTURN_EMBARGO, ENDTURN_LOCK_ON, ENDTURN_CHARGE, ENDTURN_TAUNT, ENDTURN_YAWN, ENDTURN_ITEMS2, ENDTURN_ROOST, ENDTURN_BATTLER_COUNT }; u8 DoBattlerEndTurnEffects(void) { u8 effect = FALSE; gHitMarker |= (HITMARKER_GRUDGE | HITMARKER_x20); while (gBattleStruct->turnEffectsBattlerId < gBattlersCount && gBattleStruct->turnEffectsTracker <= ENDTURN_BATTLER_COUNT) { gActiveBattler = gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->turnEffectsBattlerId]; if (gAbsentBattlerFlags & gBitTable[gActiveBattler]) { gBattleStruct->turnEffectsBattlerId++; } else { u8 ability = GetBattlerAbility(gActiveBattler); switch (gBattleStruct->turnEffectsTracker) { case ENDTURN_INGRAIN: // ingrain if ((gStatuses3[gActiveBattler] & STATUS3_ROOTED) && !BATTLER_MAX_HP(gActiveBattler) && !(gStatuses3[gActiveBattler] & STATUS3_HEAL_BLOCK) && gBattleMons[gActiveBattler].hp != 0) { gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; BattleScriptExecute(BattleScript_IngrainTurnHeal); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_AQUA_RING: // aqua ring if ((gStatuses3[gActiveBattler] & STATUS3_AQUA_RING) && !BATTLER_MAX_HP(gActiveBattler) && !(gStatuses3[gActiveBattler] & STATUS3_HEAL_BLOCK) && gBattleMons[gActiveBattler].hp != 0) { gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; BattleScriptExecute(BattleScript_AquaRingHeal); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_ABILITIES: // end turn abilities if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, gActiveBattler, 0, 0, 0)) effect++; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_ITEMS1: // item effects if (ItemBattleEffects(1, gActiveBattler, 0)) effect++; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_ITEMS2: // item effects again if (ItemBattleEffects(1, gActiveBattler, 1)) effect++; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_LEECH_SEED: // leech seed if ((gStatuses3[gActiveBattler] & STATUS3_LEECHSEED) && gBattleMons[gStatuses3[gActiveBattler] & STATUS3_LEECHSEED_BATTLER].hp != 0 && gBattleMons[gActiveBattler].hp != 0) { gBattlerTarget = gStatuses3[gActiveBattler] & STATUS3_LEECHSEED_BATTLER; // Notice gBattlerTarget is actually the HP receiver. gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleScripting.animArg1 = gBattlerTarget; gBattleScripting.animArg2 = gBattlerAttacker; BattleScriptExecute(BattleScript_LeechSeedTurnDrain); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_POISON: // poison if ((gBattleMons[gActiveBattler].status1 & STATUS1_POISON) && gBattleMons[gActiveBattler].hp != 0 && ability != ABILITY_MAGIC_GUARD) { if (ability == ABILITY_POISON_HEAL) { if (!BATTLER_MAX_HP(gActiveBattler)) { gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; BattleScriptExecute(BattleScript_PoisonHealActivates); effect++; } } else { gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptExecute(BattleScript_PoisonTurnDmg); effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_BAD_POISON: // toxic poison if ((gBattleMons[gActiveBattler].status1 & STATUS1_TOXIC_POISON) && gBattleMons[gActiveBattler].hp != 0 && ability != ABILITY_MAGIC_GUARD) { if (ability == ABILITY_POISON_HEAL) { if (!BATTLER_MAX_HP(gActiveBattler)) { gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; BattleScriptExecute(BattleScript_PoisonHealActivates); effect++; } } else { gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; if ((gBattleMons[gActiveBattler].status1 & STATUS1_TOXIC_COUNTER) != STATUS1_TOXIC_COUNTER) // not 16 turns gBattleMons[gActiveBattler].status1 += 0x100; gBattleMoveDamage *= (gBattleMons[gActiveBattler].status1 & STATUS1_TOXIC_COUNTER) >> 8; BattleScriptExecute(BattleScript_PoisonTurnDmg); effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_BURN: // burn if ((gBattleMons[gActiveBattler].status1 & STATUS1_BURN) && gBattleMons[gActiveBattler].hp != 0 && ability != ABILITY_MAGIC_GUARD) { gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptExecute(BattleScript_BurnTurnDmg); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_NIGHTMARES: // spooky nightmares if ((gBattleMons[gActiveBattler].status2 & STATUS2_NIGHTMARE) && gBattleMons[gActiveBattler].hp != 0 && ability != ABILITY_MAGIC_GUARD) { // R/S does not perform this sleep check, which causes the nightmare effect to // persist even after the affected Pokemon has been awakened by Shed Skin. if (gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP) { gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 4; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptExecute(BattleScript_NightmareTurnDmg); effect++; } else { gBattleMons[gActiveBattler].status2 &= ~STATUS2_NIGHTMARE; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_CURSE: // curse if ((gBattleMons[gActiveBattler].status2 & STATUS2_CURSED) && gBattleMons[gActiveBattler].hp != 0 && ability != ABILITY_MAGIC_GUARD) { gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 4; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptExecute(BattleScript_CurseTurnDmg); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_WRAP: // wrap if ((gBattleMons[gActiveBattler].status2 & STATUS2_WRAPPED) && gBattleMons[gActiveBattler].hp != 0) { gBattleMons[gActiveBattler].status2 -= 0x2000; if (gBattleMons[gActiveBattler].status2 & STATUS2_WRAPPED) // damaged by wrap { // This is the only way I could get this array access to match. gBattleScripting.animArg1 = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 0); gBattleScripting.animArg2 = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 1); gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN; gBattleTextBuff1[1] = B_BUFF_MOVE; gBattleTextBuff1[2] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 0); gBattleTextBuff1[3] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 1); gBattleTextBuff1[4] = EOS; gBattlescriptCurrInstr = BattleScript_WrapTurnDmg; gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; } else // broke free { gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN; gBattleTextBuff1[1] = B_BUFF_MOVE; gBattleTextBuff1[2] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 0); gBattleTextBuff1[3] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 1); gBattleTextBuff1[4] = EOS; gBattlescriptCurrInstr = BattleScript_WrapEnds; } BattleScriptExecute(gBattlescriptCurrInstr); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_UPROAR: // uproar if (gBattleMons[gActiveBattler].status2 & STATUS2_UPROAR) { for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) { if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && gBattleMons[gBattlerAttacker].ability != ABILITY_SOUNDPROOF) { gBattleMons[gBattlerAttacker].status1 &= ~(STATUS1_SLEEP); gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_NIGHTMARE); gBattleCommunication[MULTISTRING_CHOOSER] = 1; BattleScriptExecute(BattleScript_MonWokeUpInUproar); gActiveBattler = gBattlerAttacker; BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); break; } } if (gBattlerAttacker != gBattlersCount) { effect = 2; // a pokemon was awaken break; } else { gBattlerAttacker = gActiveBattler; gBattleMons[gActiveBattler].status2 -= 0x10; // uproar timer goes down if (WasUnableToUseMove(gActiveBattler)) { CancelMultiTurnMoves(gActiveBattler); gBattleCommunication[MULTISTRING_CHOOSER] = 1; } else if (gBattleMons[gActiveBattler].status2 & STATUS2_UPROAR) { gBattleCommunication[MULTISTRING_CHOOSER] = 0; gBattleMons[gActiveBattler].status2 |= STATUS2_MULTIPLETURNS; } else { gBattleCommunication[MULTISTRING_CHOOSER] = 1; CancelMultiTurnMoves(gActiveBattler); } BattleScriptExecute(BattleScript_PrintUproarOverTurns); effect = 1; } } if (effect != 2) gBattleStruct->turnEffectsTracker++; break; case ENDTURN_THRASH: // thrash if (gBattleMons[gActiveBattler].status2 & STATUS2_LOCK_CONFUSE) { gBattleMons[gActiveBattler].status2 -= 0x400; if (WasUnableToUseMove(gActiveBattler)) CancelMultiTurnMoves(gActiveBattler); else if (!(gBattleMons[gActiveBattler].status2 & STATUS2_LOCK_CONFUSE) && (gBattleMons[gActiveBattler].status2 & STATUS2_MULTIPLETURNS)) { gBattleMons[gActiveBattler].status2 &= ~(STATUS2_MULTIPLETURNS); if (!(gBattleMons[gActiveBattler].status2 & STATUS2_CONFUSION)) { gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_CONFUSION | MOVE_EFFECT_AFFECTS_USER; SetMoveEffect(1, 0); if (gBattleMons[gActiveBattler].status2 & STATUS2_CONFUSION) BattleScriptExecute(BattleScript_ThrashConfuses); effect++; } } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_DISABLE: // disable if (gDisableStructs[gActiveBattler].disableTimer1 != 0) { s32 i; for (i = 0; i < 4; i++) { if (gDisableStructs[gActiveBattler].disabledMove == gBattleMons[gActiveBattler].moves[i]) break; } if (i == 4) // pokemon does not have the disabled move anymore { gDisableStructs[gActiveBattler].disabledMove = 0; gDisableStructs[gActiveBattler].disableTimer1 = 0; } else if (--gDisableStructs[gActiveBattler].disableTimer1 == 0) // disable ends { gDisableStructs[gActiveBattler].disabledMove = 0; BattleScriptExecute(BattleScript_DisabledNoMore); effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_ENCORE: // encore if (gDisableStructs[gActiveBattler].encoreTimer1 != 0) { if (gBattleMons[gActiveBattler].moves[gDisableStructs[gActiveBattler].encoredMovePos] != gDisableStructs[gActiveBattler].encoredMove) // pokemon does not have the encored move anymore { gDisableStructs[gActiveBattler].encoredMove = 0; gDisableStructs[gActiveBattler].encoreTimer1 = 0; } else if (--gDisableStructs[gActiveBattler].encoreTimer1 == 0 || gBattleMons[gActiveBattler].pp[gDisableStructs[gActiveBattler].encoredMovePos] == 0) { gDisableStructs[gActiveBattler].encoredMove = 0; gDisableStructs[gActiveBattler].encoreTimer1 = 0; BattleScriptExecute(BattleScript_EncoredNoMore); effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_LOCK_ON: // lock-on decrement if (gStatuses3[gActiveBattler] & STATUS3_ALWAYS_HITS) gStatuses3[gActiveBattler] -= 0x8; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_CHARGE: // charge if (gDisableStructs[gActiveBattler].chargeTimer1 && --gDisableStructs[gActiveBattler].chargeTimer1 == 0) gStatuses3[gActiveBattler] &= ~STATUS3_CHARGED_UP; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_TAUNT: // taunt if (gDisableStructs[gActiveBattler].tauntTimer1) gDisableStructs[gActiveBattler].tauntTimer1--; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_YAWN: // yawn if (gStatuses3[gActiveBattler] & STATUS3_YAWN) { gStatuses3[gActiveBattler] -= 0x800; if (!(gStatuses3[gActiveBattler] & STATUS3_YAWN) && !(gBattleMons[gActiveBattler].status1 & STATUS1_ANY) && gBattleMons[gActiveBattler].ability != ABILITY_VITAL_SPIRIT && gBattleMons[gActiveBattler].ability != ABILITY_INSOMNIA && !UproarWakeUpCheck(gActiveBattler)) { CancelMultiTurnMoves(gActiveBattler); gBattleMons[gActiveBattler].status1 |= (Random() & 3) + 2; BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); gEffectBattler = gActiveBattler; BattleScriptExecute(BattleScript_YawnMakesAsleep); effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_EMBARGO: if (gStatuses3[gActiveBattler] & STATUS3_EMBARGO) { if (gDisableStructs[gActiveBattler].embargoTimer != 0) gDisableStructs[gActiveBattler].embargoTimer--; if (gDisableStructs[gActiveBattler].embargoTimer == 0) { gStatuses3[gActiveBattler] &= ~(STATUS3_EMBARGO); BattleScriptExecute(BattleScript_EmbargoEndTurn); effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_MAGNET_RISE: if (gStatuses3[gActiveBattler] & STATUS3_MAGNET_RISE) { if (gDisableStructs[gActiveBattler].magnetRiseTimer != 0) gDisableStructs[gActiveBattler].magnetRiseTimer--; if (gDisableStructs[gActiveBattler].magnetRiseTimer == 0) { gStatuses3[gActiveBattler] &= ~(STATUS3_MAGNET_RISE); BattleScriptExecute(BattleScript_MagnetRiseEndTurn); effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_TELEKINESIS: if (gStatuses3[gActiveBattler] & STATUS3_TELEKINESIS) { if (gDisableStructs[gActiveBattler].telekinesisTimer != 0) gDisableStructs[gActiveBattler].telekinesisTimer--; if (gDisableStructs[gActiveBattler].telekinesisTimer == 0) { gStatuses3[gActiveBattler] &= ~(STATUS3_TELEKINESIS); BattleScriptExecute(BattleScript_TelekinesisEndTurn); effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_HEALBLOCK: if (gStatuses3[gActiveBattler] & STATUS3_HEAL_BLOCK) { if (gDisableStructs[gActiveBattler].healBlockTimer != 0) gDisableStructs[gActiveBattler].healBlockTimer--; if (gDisableStructs[gActiveBattler].healBlockTimer == 0) { gStatuses3[gActiveBattler] &= ~(STATUS3_HEAL_BLOCK); BattleScriptExecute(BattleScript_HealBlockEndTurn); effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_ROOST: // Return flying type. if (gBattleResources->flags->flags[gActiveBattler] & RESOURCE_FLAG_ROOST) { gBattleResources->flags->flags[gActiveBattler] &= ~(RESOURCE_FLAG_ROOST); gBattleMons[gActiveBattler].type1 = gBattleStruct->roostTypes[gActiveBattler][0]; gBattleMons[gActiveBattler].type2 = gBattleStruct->roostTypes[gActiveBattler][1]; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_BATTLER_COUNT: // done gBattleStruct->turnEffectsTracker = 0; gBattleStruct->turnEffectsBattlerId++; break; } if (effect != 0) return effect; } } gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_x20); return 0; } bool8 HandleWishPerishSongOnTurnEnd(void) { gHitMarker |= (HITMARKER_GRUDGE | HITMARKER_x20); switch (gBattleStruct->wishPerishSongState) { case 0: while (gBattleStruct->wishPerishSongBattlerId < gBattlersCount) { gActiveBattler = gBattleStruct->wishPerishSongBattlerId; if (gAbsentBattlerFlags & gBitTable[gActiveBattler]) { gBattleStruct->wishPerishSongBattlerId++; continue; } gBattleStruct->wishPerishSongBattlerId++; if (gWishFutureKnock.futureSightCounter[gActiveBattler] != 0 && --gWishFutureKnock.futureSightCounter[gActiveBattler] == 0 && gBattleMons[gActiveBattler].hp != 0) { if (gWishFutureKnock.futureSightMove[gActiveBattler] == MOVE_FUTURE_SIGHT) gBattleCommunication[MULTISTRING_CHOOSER] = 0; else gBattleCommunication[MULTISTRING_CHOOSER] = 1; PREPARE_MOVE_BUFFER(gBattleTextBuff1, gWishFutureKnock.futureSightMove[gActiveBattler]); gBattlerTarget = gActiveBattler; gBattlerAttacker = gWishFutureKnock.futureSightAttacker[gActiveBattler]; gBattleMoveDamage = gWishFutureKnock.futureSightDmg[gActiveBattler]; gSpecialStatuses[gBattlerTarget].dmg = 0xFFFF; BattleScriptExecute(BattleScript_MonTookFutureAttack); if (gWishFutureKnock.futureSightCounter[gActiveBattler] == 0 && gWishFutureKnock.futureSightCounter[gActiveBattler ^ BIT_FLANK] == 0) { gSideStatuses[GET_BATTLER_SIDE(gBattlerTarget)] &= ~(SIDE_STATUS_FUTUREATTACK); } return TRUE; } } // Why do I have to keep doing this to match? { u8 *state = &gBattleStruct->wishPerishSongState; *state = 1; gBattleStruct->wishPerishSongBattlerId = 0; } // fall through case 1: while (gBattleStruct->wishPerishSongBattlerId < gBattlersCount) { gActiveBattler = gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->wishPerishSongBattlerId]; if (gAbsentBattlerFlags & gBitTable[gActiveBattler]) { gBattleStruct->wishPerishSongBattlerId++; continue; } gBattleStruct->wishPerishSongBattlerId++; if (gStatuses3[gActiveBattler] & STATUS3_PERISH_SONG) { PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[gActiveBattler].perishSongTimer1); if (gDisableStructs[gActiveBattler].perishSongTimer1 == 0) { gStatuses3[gActiveBattler] &= ~STATUS3_PERISH_SONG; gBattleMoveDamage = gBattleMons[gActiveBattler].hp; gBattlescriptCurrInstr = BattleScript_PerishSongTakesLife; } else { gDisableStructs[gActiveBattler].perishSongTimer1--; gBattlescriptCurrInstr = BattleScript_PerishSongCountGoesDown; } BattleScriptExecute(gBattlescriptCurrInstr); return TRUE; } } // Hm... { u8 *state = &gBattleStruct->wishPerishSongState; *state = 2; gBattleStruct->wishPerishSongBattlerId = 0; } // fall through case 2: if ((gBattleTypeFlags & BATTLE_TYPE_ARENA) && gBattleStruct->field_DA == 2 && gBattleMons[0].hp != 0 && gBattleMons[1].hp != 0) { s32 i; for (i = 0; i < 2; i++) CancelMultiTurnMoves(i); gBattlescriptCurrInstr = BattleScript_82DB8F3; BattleScriptExecute(BattleScript_82DB8F3); gBattleStruct->wishPerishSongState++; return TRUE; } break; } gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_x20); return FALSE; } #define FAINTED_ACTIONS_MAX_CASE 7 bool8 HandleFaintedMonActions(void) { if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) return FALSE; do { s32 i; switch (gBattleStruct->faintedActionsState) { case 0: gBattleStruct->faintedActionsBattlerId = 0; gBattleStruct->faintedActionsState++; for (i = 0; i < gBattlersCount; i++) { if (gAbsentBattlerFlags & gBitTable[i] && !HasNoMonsToSwitch(i, 6, 6)) gAbsentBattlerFlags &= ~(gBitTable[i]); } // fall through case 1: do { gBattlerFainted = gBattlerTarget = gBattleStruct->faintedActionsBattlerId; if (gBattleMons[gBattleStruct->faintedActionsBattlerId].hp == 0 && !(gBattleStruct->field_DF & gBitTable[gBattlerPartyIndexes[gBattleStruct->faintedActionsBattlerId]]) && !(gAbsentBattlerFlags & gBitTable[gBattleStruct->faintedActionsBattlerId])) { BattleScriptExecute(BattleScript_GiveExp); gBattleStruct->faintedActionsState = 2; return TRUE; } } while (++gBattleStruct->faintedActionsBattlerId != gBattlersCount); gBattleStruct->faintedActionsState = 3; break; case 2: sub_803F9EC(gBattlerFainted); if (++gBattleStruct->faintedActionsBattlerId == gBattlersCount) gBattleStruct->faintedActionsState = 3; else gBattleStruct->faintedActionsState = 1; break; case 3: gBattleStruct->faintedActionsBattlerId = 0; gBattleStruct->faintedActionsState++; // fall through case 4: do { gBattlerFainted = gBattlerTarget = gBattleStruct->faintedActionsBattlerId; if (gBattleMons[gBattleStruct->faintedActionsBattlerId].hp == 0 && !(gAbsentBattlerFlags & gBitTable[gBattleStruct->faintedActionsBattlerId])) { BattleScriptExecute(BattleScript_HandleFaintedMon); gBattleStruct->faintedActionsState = 5; return TRUE; } } while (++gBattleStruct->faintedActionsBattlerId != gBattlersCount); gBattleStruct->faintedActionsState = 6; break; case 5: if (++gBattleStruct->faintedActionsBattlerId == gBattlersCount) gBattleStruct->faintedActionsState = 6; else gBattleStruct->faintedActionsState = 4; break; case 6: if (AbilityBattleEffects(ABILITYEFFECT_INTIMIDATE1, 0, 0, 0, 0) || AbilityBattleEffects(ABILITYEFFECT_TRACE, 0, 0, 0, 0) || ItemBattleEffects(1, 0, 1) || AbilityBattleEffects(ABILITYEFFECT_FORECAST, 0, 0, 0, 0)) return TRUE; gBattleStruct->faintedActionsState++; break; case FAINTED_ACTIONS_MAX_CASE: break; } } while (gBattleStruct->faintedActionsState != FAINTED_ACTIONS_MAX_CASE); return FALSE; } void TryClearRageStatuses(void) { s32 i; for (i = 0; i < gBattlersCount; i++) { if ((gBattleMons[i].status2 & STATUS2_RAGE) && gChosenMoveByBattler[i] != MOVE_RAGE) gBattleMons[i].status2 &= ~(STATUS2_RAGE); } } #define ATKCANCELLER_MAX_CASE 14 u8 AtkCanceller_UnableToUseMove(void) { u8 effect = 0; s32 *bideDmg = &gBattleScripting.bideDmg; do { switch (gBattleStruct->atkCancellerTracker) { case 0: // flags clear gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_DESTINY_BOND); gStatuses3[gBattlerAttacker] &= ~(STATUS3_GRUDGE); gBattleStruct->atkCancellerTracker++; break; case 1: // check being asleep if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) { if (UproarWakeUpCheck(gBattlerAttacker)) { gBattleMons[gBattlerAttacker].status1 &= ~(STATUS1_SLEEP); gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_NIGHTMARE); BattleScriptPushCursor(); gBattleCommunication[MULTISTRING_CHOOSER] = 1; gBattlescriptCurrInstr = BattleScript_MoveUsedWokeUp; effect = 2; } else { u8 toSub; if (gBattleMons[gBattlerAttacker].ability == ABILITY_EARLY_BIRD) toSub = 2; else toSub = 1; if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) < toSub) gBattleMons[gBattlerAttacker].status1 &= ~(STATUS1_SLEEP); else gBattleMons[gBattlerAttacker].status1 -= toSub; if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) { if (gCurrentMove != MOVE_SNORE && gCurrentMove != MOVE_SLEEP_TALK) { gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 2; } } else { gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_NIGHTMARE); BattleScriptPushCursor(); gBattleCommunication[MULTISTRING_CHOOSER] = 0; gBattlescriptCurrInstr = BattleScript_MoveUsedWokeUp; effect = 2; } } } gBattleStruct->atkCancellerTracker++; break; case 2: // check being frozen if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE) { if (Random() % 5) { if (gBattleMoves[gCurrentMove].effect != EFFECT_THAW_HIT) // unfreezing via a move effect happens in case 13 { gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen; gHitMarker |= HITMARKER_NO_ATTACKSTRING; } else { gBattleStruct->atkCancellerTracker++; break; } } else // unfreeze { gBattleMons[gBattlerAttacker].status1 &= ~(STATUS1_FREEZE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MoveUsedUnfroze; gBattleCommunication[MULTISTRING_CHOOSER] = 0; } effect = 2; } gBattleStruct->atkCancellerTracker++; break; case 3: // truant if (gBattleMons[gBattlerAttacker].ability == ABILITY_TRUANT && gDisableStructs[gBattlerAttacker].truantCounter) { CancelMultiTurnMoves(gBattlerAttacker); gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gBattleCommunication[MULTISTRING_CHOOSER] = 0; gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; gMoveResultFlags |= MOVE_RESULT_MISSED; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case 4: // recharge if (gBattleMons[gBattlerAttacker].status2 & STATUS2_RECHARGE) { gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_RECHARGE); gDisableStructs[gBattlerAttacker].rechargeCounter = 0; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case 5: // flinch if (gBattleMons[gBattlerAttacker].status2 & STATUS2_FLINCHED) { gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_FLINCHED); gProtectStructs[gBattlerAttacker].flinchImmobility = 1; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case 6: // disabled move if (gDisableStructs[gBattlerAttacker].disabledMove == gCurrentMove && gDisableStructs[gBattlerAttacker].disabledMove != 0) { gProtectStructs[gBattlerAttacker].usedDisabledMove = 1; gBattleScripting.battler = gBattlerAttacker; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case 7: // taunt if (gDisableStructs[gBattlerAttacker].tauntTimer1 && gBattleMoves[gCurrentMove].power == 0) { gProtectStructs[gBattlerAttacker].usedTauntedMove = 1; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case 8: // imprisoned if (GetImprisonedMovesCount(gBattlerAttacker, gCurrentMove)) { gProtectStructs[gBattlerAttacker].usedImprisionedMove = 1; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case 9: // confusion if (gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION) { gBattleMons[gBattlerAttacker].status2--; if (gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION) { if (Random() & 1) { gBattleCommunication[MULTISTRING_CHOOSER] = 0; BattleScriptPushCursor(); } else // confusion dmg { gBattleCommunication[MULTISTRING_CHOOSER] = 1; gBattlerTarget = gBattlerAttacker; gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE, FALSE); gProtectStructs[gBattlerAttacker].confusionSelfDmg = 1; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; } gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfused; } else // snapped out of confusion { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfusedNoMore; } effect = 1; } gBattleStruct->atkCancellerTracker++; break; case 10: // paralysis if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && (Random() % 4) == 0) { gProtectStructs[gBattlerAttacker].prlzImmobility = 1; // This is removed in Emerald for some reason //CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case 11: // infatuation if (gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) { gBattleScripting.battler = CountTrailingZeroBits((gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) >> 0x10); if (Random() & 1) { BattleScriptPushCursor(); } else { BattleScriptPush(BattleScript_MoveUsedIsParalyzedCantAttack); gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gProtectStructs[gBattlerAttacker].loveImmobility = 1; CancelMultiTurnMoves(gBattlerAttacker); } gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case 12: // bide if (gBattleMons[gBattlerAttacker].status2 & STATUS2_BIDE) { gBattleMons[gBattlerAttacker].status2 -= 0x100; if (gBattleMons[gBattlerAttacker].status2 & STATUS2_BIDE) { gBattlescriptCurrInstr = BattleScript_BideStoringEnergy; } else { // This is removed in Emerald for some reason //gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_MULTIPLETURNS); if (gTakenDmg[gBattlerAttacker]) { gCurrentMove = MOVE_BIDE; *bideDmg = gTakenDmg[gBattlerAttacker] * 2; gBattlerTarget = gTakenDmgByBattler[gBattlerAttacker]; if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) gBattlerTarget = GetMoveTarget(MOVE_BIDE, 1); gBattlescriptCurrInstr = BattleScript_BideAttack; } else { gBattlescriptCurrInstr = BattleScript_BideNoEnergyToAttack; } } effect = 1; } gBattleStruct->atkCancellerTracker++; break; case 13: // move thawing if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE) { if (gBattleMoves[gCurrentMove].effect == EFFECT_THAW_HIT) { gBattleMons[gBattlerAttacker].status1 &= ~(STATUS1_FREEZE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MoveUsedUnfroze; gBattleCommunication[MULTISTRING_CHOOSER] = 1; } effect = 2; } gBattleStruct->atkCancellerTracker++; break; case ATKCANCELLER_MAX_CASE: break; } } while (gBattleStruct->atkCancellerTracker != ATKCANCELLER_MAX_CASE && effect == 0); if (effect == 2) { gActiveBattler = gBattlerAttacker; BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); } return effect; } bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) { struct Pokemon *party; u8 id1, id2; s32 i; if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) return FALSE; if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; id1 = ((battler & BIT_FLANK) / 2); for (i = id1 * 3; i < id1 * 3 + 3; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG) break; } return (i == id1 * 3 + 3); } else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { if (gBattleTypeFlags & BATTLE_TYPE_x800000) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) { party = gPlayerParty; id2 = GetBattlerMultiplayerId(battler); id1 = GetLinkTrainerFlankId(id2); } else { // FIXME: Compiler insists on moving r4 into r1 before doing the eor. #ifndef NONMATCHING register u32 var asm("r1"); #else u32 var; #endif // NONMATCHING party = gEnemyParty; var = battler ^ BIT_SIDE; if (var == 0) id1 = 0; else id1 = 1; } } else { id2 = GetBattlerMultiplayerId(battler); if (GetBattlerSide(battler) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; id1 = GetLinkTrainerFlankId(id2); } for (i = id1 * 3; i < id1 * 3 + 3; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG) break; } return (i == id1 * 3 + 3); } else if ((gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && GetBattlerSide(battler) == B_SIDE_OPPONENT) { party = gEnemyParty; if (battler == 1) id1 = 0; else id1 = 3; for (i = id1; i < id1 + 3; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG) break; } return (i == id1 + 3); } else { if (GetBattlerSide(battler) == B_SIDE_OPPONENT) { id2 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); id1 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); party = gEnemyParty; } else { id2 = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); id1 = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); party = gPlayerParty; } if (partyIdBattlerOn1 == PARTY_SIZE) partyIdBattlerOn1 = gBattlerPartyIndexes[id2]; if (partyIdBattlerOn2 == PARTY_SIZE) partyIdBattlerOn2 = gBattlerPartyIndexes[id1]; for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG && i != partyIdBattlerOn1 && i != partyIdBattlerOn2 && i != *(gBattleStruct->monToSwitchIntoId + id2) && i != id1[gBattleStruct->monToSwitchIntoId]) break; } return (i == PARTY_SIZE); } } enum { CASTFORM_NO_CHANGE, //0 CASTFORM_TO_NORMAL, //1 CASTFORM_TO_FIRE, //2 CASTFORM_TO_WATER, //3 CASTFORM_TO_ICE, //4 }; u8 CastformDataTypeChange(u8 battler) { u8 formChange = 0; if (gBattleMons[battler].species != SPECIES_CASTFORM || gBattleMons[battler].ability != ABILITY_FORECAST || gBattleMons[battler].hp == 0) return CASTFORM_NO_CHANGE; if (!WEATHER_HAS_EFFECT && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL)) { SET_BATTLER_TYPE(battler, TYPE_NORMAL); return CASTFORM_TO_NORMAL; } if (!WEATHER_HAS_EFFECT) return CASTFORM_NO_CHANGE; if (!(gBattleWeather & (WEATHER_RAIN_ANY | WEATHER_SUN_ANY | WEATHER_HAIL_ANY)) && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL)) { SET_BATTLER_TYPE(battler, TYPE_NORMAL); formChange = CASTFORM_TO_NORMAL; } if (gBattleWeather & WEATHER_SUN_ANY && !IS_BATTLER_OF_TYPE(battler, TYPE_FIRE)) { SET_BATTLER_TYPE(battler, TYPE_FIRE); formChange = CASTFORM_TO_FIRE; } if (gBattleWeather & WEATHER_RAIN_ANY && !IS_BATTLER_OF_TYPE(battler, TYPE_WATER)) { SET_BATTLER_TYPE(battler, TYPE_WATER); formChange = CASTFORM_TO_WATER; } if (gBattleWeather & WEATHER_HAIL_ANY && !IS_BATTLER_OF_TYPE(battler, TYPE_ICE)) { SET_BATTLER_TYPE(battler, TYPE_ICE); formChange = CASTFORM_TO_ICE; } return formChange; } // The largest function in the game, but even it could not save itself from decompiling. u8 AbilityBattleEffects(u8 caseID, u8 battler, u8 ability, u8 special, u16 moveArg) { u8 effect = 0; u32 speciesAtk, speciesDef; u32 pidAtk, pidDef; u32 moveType; u32 i; u32 move; u8 side; u8 target1; if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) return; if (gBattlerAttacker >= gBattlersCount) gBattlerAttacker = battler; speciesAtk = gBattleMons[gBattlerAttacker].species; pidAtk = gBattleMons[gBattlerAttacker].personality; speciesDef = gBattleMons[gBattlerTarget].species; pidDef = gBattleMons[gBattlerTarget].personality; gLastUsedAbility = GetBattlerAbility(battler); if (moveArg) move = moveArg; else move = gCurrentMove; GET_MOVE_TYPE(move, moveType); switch (caseID) { case ABILITYEFFECT_ON_SWITCHIN: // 0 switch (gLastUsedAbility) { case ABILITYEFFECT_SWITCH_IN_WEATHER: if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) { switch (weather_get_current()) { case 3: case 5: case 13: if (!(gBattleWeather & WEATHER_RAIN_ANY)) { gBattleWeather = (WEATHER_RAIN_TEMPORARY | WEATHER_RAIN_PERMANENT); gBattleScripting.animArg1 = B_ANIM_RAIN_CONTINUES; gBattleScripting.battler = battler; effect++; } break; case 8: if (!(gBattleWeather & WEATHER_SANDSTORM_ANY)) { gBattleWeather = (WEATHER_SANDSTORM_PERMANENT | WEATHER_SANDSTORM_TEMPORARY); gBattleScripting.animArg1 = B_ANIM_SANDSTORM_CONTINUES; gBattleScripting.battler = battler; effect++; } break; case 12: if (!(gBattleWeather & WEATHER_SUN_ANY)) { gBattleWeather = (WEATHER_SUN_PERMANENT | WEATHER_SUN_TEMPORARY); gBattleScripting.animArg1 = B_ANIM_SUN_CONTINUES; gBattleScripting.battler = battler; effect++; } break; } } if (effect) { gBattleCommunication[MULTISTRING_CHOOSER] = weather_get_current(); BattleScriptPushCursorAndCallback(BattleScript_OverworldWeatherStarts); } break; case ABILITY_DOWNLOAD: { u8 statId; u32 opposingBattler = BATTLE_OPPOSITE(battler); u32 opposingDef = gBattleMons[opposingBattler].defense * gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_DEF]][0] / gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_DEF]][1]; u32 opposingSpDef = gBattleMons[opposingBattler].spDefense * gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_SPDEF]][0] / gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_SPDEF]][1]; opposingBattler = BATTLE_PARTNER(opposingBattler); if (IsBattlerAlive(opposingBattler)) { opposingDef += gBattleMons[opposingBattler].defense * gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_DEF]][0] / gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_DEF]][1]; opposingSpDef += gBattleMons[opposingBattler].spDefense * gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_SPDEF]][0] / gStatStageRatios[gBattleMons[opposingBattler].statStages[STAT_SPDEF]][1]; } if (opposingSpDef > opposingDef) statId = STAT_SPATK; else statId = STAT_ATK; if (gBattleMons[battler].statStages[statId] != 0xC) { gBattleMons[battler].statStages[statId]++; SET_STATCHANGER(statId, 1, FALSE); PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); BattleScriptPushCursorAndCallback(BattleScript_AttackerAbilityStatRaiseEnd3); effect++; } } break; case ABILITY_DRIZZLE: if (!(gBattleWeather & WEATHER_RAIN_PERMANENT)) { gBattleWeather = (WEATHER_RAIN_PERMANENT | WEATHER_RAIN_TEMPORARY); BattleScriptPushCursorAndCallback(BattleScript_DrizzleActivates); gBattleScripting.battler = battler; effect++; } break; case ABILITY_SAND_STREAM: if (!(gBattleWeather & WEATHER_SANDSTORM_PERMANENT)) { gBattleWeather = (WEATHER_SANDSTORM_PERMANENT | WEATHER_SANDSTORM_TEMPORARY); BattleScriptPushCursorAndCallback(BattleScript_SandstreamActivates); gBattleScripting.battler = battler; effect++; } break; case ABILITY_DROUGHT: if (!(gBattleWeather & WEATHER_SUN_PERMANENT)) { gBattleWeather = (WEATHER_SUN_PERMANENT | WEATHER_SUN_TEMPORARY); BattleScriptPushCursorAndCallback(BattleScript_DroughtActivates); gBattleScripting.battler = battler; effect++; } break; case ABILITY_INTIMIDATE: if (!(gSpecialStatuses[battler].intimidatedMon)) { gStatuses3[battler] |= STATUS3_INTIMIDATE_POKES; gSpecialStatuses[battler].intimidatedMon = 1; } break; case ABILITY_FORECAST: effect = CastformDataTypeChange(battler); if (effect != 0) { BattleScriptPushCursorAndCallback(BattleScript_CastformChange); gBattleScripting.battler = battler; *(&gBattleStruct->formToChangeInto) = effect - 1; } break; case ABILITY_TRACE: if (!(gSpecialStatuses[battler].traced)) { gStatuses3[battler] |= STATUS3_TRACE; gSpecialStatuses[battler].traced = 1; } break; case ABILITY_CLOUD_NINE: case ABILITY_AIR_LOCK: { // that's a weird choice for a variable, why not use i or battler? for (target1 = 0; target1 < gBattlersCount; target1++) { effect = CastformDataTypeChange(target1); if (effect != 0) { BattleScriptPushCursorAndCallback(BattleScript_CastformChange); gBattleScripting.battler = target1; *(&gBattleStruct->formToChangeInto) = effect - 1; break; } } } break; } break; case ABILITYEFFECT_ENDTURN: // 1 if (gBattleMons[battler].hp != 0) { gBattlerAttacker = battler; switch (gLastUsedAbility) { case ABILITY_RAIN_DISH: if (WEATHER_HAS_EFFECT && (gBattleWeather & WEATHER_RAIN_ANY) && gBattleMons[battler].maxHP > gBattleMons[battler].hp) { gLastUsedAbility = ABILITY_RAIN_DISH; // why BattleScriptPushCursorAndCallback(BattleScript_RainDishActivates); gBattleMoveDamage = gBattleMons[battler].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; effect++; } break; case ABILITY_SHED_SKIN: if ((gBattleMons[battler].status1 & STATUS1_ANY) && (Random() % 3) == 0) { if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON)) StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); if (gBattleMons[battler].status1 & STATUS1_SLEEP) StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); if (gBattleMons[battler].status1 & STATUS1_PARALYSIS) StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); if (gBattleMons[battler].status1 & STATUS1_BURN) StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); if (gBattleMons[battler].status1 & STATUS1_FREEZE) StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); gBattleMons[battler].status1 = 0; gBattleMons[battler].status2 &= ~(STATUS2_NIGHTMARE); // fix nightmare glitch gBattleScripting.battler = gActiveBattler = battler; BattleScriptPushCursorAndCallback(BattleScript_ShedSkinActivates); BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); MarkBattlerForControllerExec(gActiveBattler); effect++; } break; case ABILITY_SPEED_BOOST: if (gBattleMons[battler].statStages[STAT_SPEED] < 0xC && gDisableStructs[battler].isFirstTurn != 2) { gBattleMons[battler].statStages[STAT_SPEED]++; gBattleScripting.animArg1 = 0x11; gBattleScripting.animArg2 = 0; BattleScriptPushCursorAndCallback(BattleScript_SpeedBoostActivates); gBattleScripting.battler = battler; effect++; } break; case ABILITY_TRUANT: gDisableStructs[gBattlerAttacker].truantCounter ^= 1; break; } } break; case ABILITYEFFECT_MOVES_BLOCK: // 2 if (gLastUsedAbility == ABILITY_SOUNDPROOF) { for (i = 0; sSoundMovesTable[i] != 0xFFFF; i++) { if (sSoundMovesTable[i] == move) break; } if (sSoundMovesTable[i] != 0xFFFF) { if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS) gHitMarker |= HITMARKER_NO_PPDEDUCT; gBattlescriptCurrInstr = BattleScript_SoundproofProtected; effect = 1; } } break; case ABILITYEFFECT_ABSORBING: // 3 if (move) { u8 statId; switch (gLastUsedAbility) { case ABILITY_VOLT_ABSORB: if (moveType == TYPE_ELECTRIC) effect = 1; break; case ABILITY_WATER_ABSORB: case ABILITY_DRY_SKIN: if (moveType == TYPE_WATER) effect = 1; break; case ABILITY_MOTOR_DRIVE: if (moveType == TYPE_ELECTRIC) effect = 2, statId = STAT_SPEED; break; case ABILITY_LIGHTNING_ROD: if (moveType == TYPE_ELECTRIC) effect = 2, statId = STAT_SPATK; break; case ABILITY_STORM_DRAIN: if (moveType == TYPE_WATER) effect = 2, statId = STAT_SPATK; break; case ABILITY_SAP_SIPPER: if (moveArg == TYPE_GRASS) effect = 2, statId = STAT_ATK; break; case ABILITY_FLASH_FIRE: if (moveType == TYPE_FIRE && !(gBattleMons[battler].status1 & STATUS1_FREEZE)) { if (!(gBattleResources->flags->flags[battler] & RESOURCE_FLAG_FLASH_FIRE)) { gBattleCommunication[MULTISTRING_CHOOSER] = 0; if (gProtectStructs[gBattlerAttacker].notFirstStrike) gBattlescriptCurrInstr = BattleScript_FlashFireBoost; else gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss; gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_FLASH_FIRE; effect = 2; } else { gBattleCommunication[MULTISTRING_CHOOSER] = 1; if (gProtectStructs[gBattlerAttacker].notFirstStrike) gBattlescriptCurrInstr = BattleScript_FlashFireBoost; else gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss; effect = 2; } } break; } if (effect == 1) // Drain Hp ability. { if (gBattleMons[battler].maxHP == gBattleMons[battler].hp) { if ((gProtectStructs[gBattlerAttacker].notFirstStrike)) gBattlescriptCurrInstr = BattleScript_MonMadeMoveUseless; else gBattlescriptCurrInstr = BattleScript_MonMadeMoveUseless_PPLoss; } else { if (gProtectStructs[gBattlerAttacker].notFirstStrike) gBattlescriptCurrInstr = BattleScript_MoveHPDrain; else gBattlescriptCurrInstr = BattleScript_MoveHPDrain_PPLoss; gBattleMoveDamage = gBattleMons[battler].maxHP / 4; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; } } else if (effect == 2) // Boost Stat ability; { if (gBattleMons[battler].statStages[statId] == 0xC) { if ((gProtectStructs[gBattlerAttacker].notFirstStrike)) gBattlescriptCurrInstr = BattleScript_MonMadeMoveUseless; else gBattlescriptCurrInstr = BattleScript_MonMadeMoveUseless_PPLoss; } else { if (gProtectStructs[gBattlerAttacker].notFirstStrike) gBattlescriptCurrInstr = BattleScript_MoveStatDrain; else gBattlescriptCurrInstr = BattleScript_MoveStatDrain_PPLoss; SET_STATCHANGER(statId, 1, FALSE); gBattleMons[battler].statStages[statId]++; PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); } } } break; case ABILITYEFFECT_CONTACT: // 4 switch (gLastUsedAbility) { case ABILITY_ANGER_POINT: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gIsCriticalHit && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && gBattleMons[battler].statStages[STAT_ATK] != 0xC) { gBattleMons[battler].statStages[STAT_ATK] = 0xC; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AngryPointActivates; effect++; } break; case ABILITY_COLOR_CHANGE: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && move != MOVE_STRUGGLE && gBattleMoves[move].power != 0 && TARGET_TURN_DAMAGED && !IS_BATTLER_OF_TYPE(battler, moveType) && gBattleMons[battler].hp != 0) { SET_BATTLER_TYPE(battler, moveType); PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ColorChangeActivates; effect++; } break; case ABILITY_ROUGH_SKIN: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT)) { gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_RoughSkinActivates; effect++; } break; case ABILITY_EFFECT_SPORE: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) && (Random() % 10) == 0) { do { gBattleCommunication[MOVE_EFFECT_BYTE] = Random() & 3; } while (gBattleCommunication[MOVE_EFFECT_BYTE] == 0); if (gBattleCommunication[MOVE_EFFECT_BYTE] == MOVE_EFFECT_BURN) gBattleCommunication[MOVE_EFFECT_BYTE] += 2; // 5 MOVE_EFFECT_PARALYSIS gBattleCommunication[MOVE_EFFECT_BYTE] += MOVE_EFFECT_AFFECTS_USER; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITY_POISON_POINT: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) && (Random() % 3) == 0) { gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_POISON; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITY_STATIC: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) && (Random() % 3) == 0) { gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_PARALYSIS; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITY_FLAME_BODY: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) && TARGET_TURN_DAMAGED && (Random() % 3) == 0) { gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITY_CUTE_CHARM: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) && TARGET_TURN_DAMAGED && gBattleMons[gBattlerTarget].hp != 0 && (Random() % 3) == 0 && gBattleMons[gBattlerAttacker].ability != ABILITY_OBLIVIOUS && GetGenderFromSpeciesAndPersonality(speciesAtk, pidAtk) != GetGenderFromSpeciesAndPersonality(speciesDef, pidDef) && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) && GetGenderFromSpeciesAndPersonality(speciesAtk, pidAtk) != MON_GENDERLESS && GetGenderFromSpeciesAndPersonality(speciesDef, pidDef) != MON_GENDERLESS) { gBattleMons[gBattlerAttacker].status2 |= STATUS2_INFATUATED_WITH(gBattlerTarget); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_CuteCharmActivates; effect++; } break; } break; case ABILITYEFFECT_IMMUNITY: // 5 for (battler = 0; battler < gBattlersCount; battler++) { switch (gBattleMons[battler].ability) { case ABILITY_IMMUNITY: if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON | STATUS1_TOXIC_COUNTER)) { StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); effect = 1; } break; case ABILITY_OWN_TEMPO: if (gBattleMons[battler].status2 & STATUS2_CONFUSION) { StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); effect = 2; } break; case ABILITY_LIMBER: if (gBattleMons[battler].status1 & STATUS1_PARALYSIS) { StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); effect = 1; } break; case ABILITY_INSOMNIA: case ABILITY_VITAL_SPIRIT: if (gBattleMons[battler].status1 & STATUS1_SLEEP) { gBattleMons[battler].status2 &= ~(STATUS2_NIGHTMARE); StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); effect = 1; } break; case ABILITY_WATER_VEIL: if (gBattleMons[battler].status1 & STATUS1_BURN) { StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); effect = 1; } break; case ABILITY_MAGMA_ARMOR: if (gBattleMons[battler].status1 & STATUS1_FREEZE) { StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); effect = 1; } break; case ABILITY_OBLIVIOUS: if (gBattleMons[battler].status2 & STATUS2_INFATUATION) { StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); effect = 3; } break; } if (effect) { switch (effect) { case 1: // status cleared gBattleMons[battler].status1 = 0; break; case 2: // get rid of confusion gBattleMons[battler].status2 &= ~(STATUS2_CONFUSION); break; case 3: // get rid of infatuation gBattleMons[battler].status2 &= ~(STATUS2_INFATUATION); break; } BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityCuredStatus; gBattleScripting.battler = battler; gActiveBattler = battler; BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); return effect; } } break; case ABILITYEFFECT_FORECAST: // 6 for (battler = 0; battler < gBattlersCount; battler++) { if (gBattleMons[battler].ability == ABILITY_FORECAST) { effect = CastformDataTypeChange(battler); if (effect) { BattleScriptPushCursorAndCallback(BattleScript_CastformChange); gBattleScripting.battler = battler; *(&gBattleStruct->formToChangeInto) = effect - 1; return effect; } } } break; case ABILITYEFFECT_SYNCHRONIZE: // 7 if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT)) { gHitMarker &= ~(HITMARKER_SYNCHRONISE_EFFECT); gBattleStruct->synchronizeMoveEffect &= ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC) gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON; gBattleCommunication[MOVE_EFFECT_BYTE] = gBattleStruct->synchronizeMoveEffect + MOVE_EFFECT_AFFECTS_USER; gBattleScripting.battler = gBattlerTarget; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SynchronizeActivates; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITYEFFECT_ATK_SYNCHRONIZE: // 8 if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT)) { gHitMarker &= ~(HITMARKER_SYNCHRONISE_EFFECT); gBattleStruct->synchronizeMoveEffect &= ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC) gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON; gBattleCommunication[MOVE_EFFECT_BYTE] = gBattleStruct->synchronizeMoveEffect; gBattleScripting.battler = gBattlerAttacker; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SynchronizeActivates; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITYEFFECT_INTIMIDATE1: // 9 for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ABILITY_INTIMIDATE && gStatuses3[i] & STATUS3_INTIMIDATE_POKES) { gLastUsedAbility = ABILITY_INTIMIDATE; gStatuses3[i] &= ~(STATUS3_INTIMIDATE_POKES); BattleScriptPushCursorAndCallback(BattleScript_82DB4B8); gBattleStruct->intimidateBattler = i; effect++; break; } } break; case ABILITYEFFECT_TRACE: // 11 for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ABILITY_TRACE && (gStatuses3[i] & STATUS3_TRACE)) { u8 target2; side = (GetBattlerPosition(i) ^ BIT_SIDE) & BIT_SIDE; // side of the opposing pokemon target1 = GetBattlerAtPosition(side); target2 = GetBattlerAtPosition(side + BIT_FLANK); if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { if (gBattleMons[target1].ability != 0 && gBattleMons[target1].hp != 0 && gBattleMons[target2].ability != 0 && gBattleMons[target2].hp != 0) { gActiveBattler = GetBattlerAtPosition(((Random() & 1) * 2) | side); gBattleMons[i].ability = gBattleMons[gActiveBattler].ability; gLastUsedAbility = gBattleMons[gActiveBattler].ability; effect++; } else if (gBattleMons[target1].ability != 0 && gBattleMons[target1].hp != 0) { gActiveBattler = target1; gBattleMons[i].ability = gBattleMons[gActiveBattler].ability; gLastUsedAbility = gBattleMons[gActiveBattler].ability; effect++; } else if (gBattleMons[target2].ability != 0 && gBattleMons[target2].hp != 0) { gActiveBattler = target2; gBattleMons[i].ability = gBattleMons[gActiveBattler].ability; gLastUsedAbility = gBattleMons[gActiveBattler].ability; effect++; } } else { gActiveBattler = target1; if (gBattleMons[target1].ability && gBattleMons[target1].hp) { gBattleMons[i].ability = gBattleMons[target1].ability; gLastUsedAbility = gBattleMons[target1].ability; effect++; } } if (effect) { BattleScriptPushCursorAndCallback(BattleScript_TraceActivates); gStatuses3[i] &= ~(STATUS3_TRACE); gBattleScripting.battler = i; PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gActiveBattler, gBattlerPartyIndexes[gActiveBattler]) PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility) break; } } } break; case ABILITYEFFECT_INTIMIDATE2: // 10 for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ABILITY_INTIMIDATE && (gStatuses3[i] & STATUS3_INTIMIDATE_POKES)) { gLastUsedAbility = ABILITY_INTIMIDATE; gStatuses3[i] &= ~(STATUS3_INTIMIDATE_POKES); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_82DB4C1; gBattleStruct->intimidateBattler = i; effect++; break; } } break; case ABILITYEFFECT_CHECK_OTHER_SIDE: // 12 side = GetBattlerSide(battler); for (i = 0; i < gBattlersCount; i++) { if (GetBattlerSide(i) != side && gBattleMons[i].ability == ability) { gLastUsedAbility = ability; effect = i + 1; } } break; case ABILITYEFFECT_CHECK_BATTLER_SIDE: // 13 side = GetBattlerSide(battler); for (i = 0; i < gBattlersCount; i++) { if (GetBattlerSide(i) == side && gBattleMons[i].ability == ability) { gLastUsedAbility = ability; effect = i + 1; } } break; case ABILITYEFFECT_CHECK_ON_FIELD: // 19 for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ability && gBattleMons[i].hp != 0) { gLastUsedAbility = ability; effect = i + 1; } } break; case ABILITYEFFECT_CHECK_FIELD_EXCEPT_BATTLER: // 15 for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ability && i != battler) { gLastUsedAbility = ability; effect = i + 1; } } break; case ABILITYEFFECT_COUNT_OTHER_SIDE: // 16 side = GetBattlerSide(battler); for (i = 0; i < gBattlersCount; i++) { if (GetBattlerSide(i) != side && gBattleMons[i].ability == ability) { gLastUsedAbility = ability; effect++; } } break; case ABILITYEFFECT_COUNT_BATTLER_SIDE: // 17 side = GetBattlerSide(battler); for (i = 0; i < gBattlersCount; i++) { if (GetBattlerSide(i) == side && gBattleMons[i].ability == ability) { gLastUsedAbility = ability; effect++; } } break; case ABILITYEFFECT_COUNT_ON_FIELD: // 18 for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ability && i != battler) { gLastUsedAbility = ability; effect++; } } break; } if (effect && caseID < ABILITYEFFECT_CHECK_OTHER_SIDE && gLastUsedAbility != 0xFF) RecordAbilityBattle(battler, gLastUsedAbility); return effect; } void BattleScriptExecute(const u8 *BS_ptr) { gBattlescriptCurrInstr = BS_ptr; gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size++] = gBattleMainFunc; gBattleMainFunc = RunBattleScriptCommands_PopCallbacksStack; gCurrentActionFuncId = 0; } void BattleScriptPushCursorAndCallback(const u8 *BS_ptr) { BattleScriptPushCursor(); gBattlescriptCurrInstr = BS_ptr; gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size++] = gBattleMainFunc; gBattleMainFunc = RunBattleScriptCommands; } enum { ITEM_NO_EFFECT, // 0 ITEM_STATUS_CHANGE, // 1 ITEM_EFFECT_OTHER, // 2 ITEM_PP_CHANGE, // 3 ITEM_HP_CHANGE, // 4 ITEM_STATS_CHANGE, // 5 }; u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) { int i = 0; u8 effect = ITEM_NO_EFFECT; u8 changedPP = 0; u8 battlerHoldEffect, atkHoldEffect, defHoldEffect; u8 battlerHoldEffectParam, atkHoldEffectParam, defHoldEffectParam; u16 atkItem, defItem; gLastUsedItem = gBattleMons[battlerId].item; if (gLastUsedItem == ITEM_ENIGMA_BERRY) { battlerHoldEffect = gEnigmaBerries[battlerId].holdEffect; battlerHoldEffectParam = gEnigmaBerries[battlerId].holdEffectParam; } else { battlerHoldEffect = ItemId_GetHoldEffect(gLastUsedItem); battlerHoldEffectParam = ItemId_GetHoldEffectParam(gLastUsedItem); } atkItem = gBattleMons[gBattlerAttacker].item; if (atkItem == ITEM_ENIGMA_BERRY) { atkHoldEffect = gEnigmaBerries[gBattlerAttacker].holdEffect; atkHoldEffectParam = gEnigmaBerries[gBattlerAttacker].holdEffectParam; } else { atkHoldEffect = ItemId_GetHoldEffect(atkItem); atkHoldEffectParam = ItemId_GetHoldEffectParam(atkItem); } // def variables are unused defItem = gBattleMons[gBattlerTarget].item; if (defItem == ITEM_ENIGMA_BERRY) { defHoldEffect = gEnigmaBerries[gBattlerTarget].holdEffect; defHoldEffectParam = gEnigmaBerries[gBattlerTarget].holdEffectParam; } else { defHoldEffect = ItemId_GetHoldEffect(defItem); defHoldEffectParam = ItemId_GetHoldEffectParam(defItem); } switch (caseID) { case ITEMEFFECT_ON_SWITCH_IN: switch (battlerHoldEffect) { case HOLD_EFFECT_DOUBLE_PRIZE: if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) gBattleStruct->moneyMultiplier = 2; break; case HOLD_EFFECT_RESTORE_STATS: for (i = 0; i < BATTLE_STATS_NO; i++) { if (gBattleMons[battlerId].statStages[i] < 6) { gBattleMons[battlerId].statStages[i] = 6; effect = ITEM_STATS_CHANGE; } } if (effect) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; gActiveBattler = gBattlerAttacker = battlerId; BattleScriptExecute(BattleScript_WhiteHerbEnd2); } break; } break; case 1: if (gBattleMons[battlerId].hp) { switch (battlerHoldEffect) { case HOLD_EFFECT_RESTORE_HP: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) { gBattleMoveDamage = battlerHoldEffectParam; if (gBattleMons[battlerId].hp + battlerHoldEffectParam > gBattleMons[battlerId].maxHP) gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; gBattleMoveDamage *= -1; BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); effect = 4; } break; case HOLD_EFFECT_RESTORE_PP: if (!moveTurn) { struct Pokemon *mon; u8 ppBonuses; u16 move; if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]]; else mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]]; for (i = 0; i < 4; i++) { move = GetMonData(mon, MON_DATA_MOVE1 + i); changedPP = GetMonData(mon, MON_DATA_PP1 + i); ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES); if (move && changedPP == 0) break; } if (i != 4) { u8 maxPP = CalculatePPWithBonus(move, ppBonuses, i); if (changedPP + battlerHoldEffectParam > maxPP) changedPP = maxPP; else changedPP = changedPP + battlerHoldEffectParam; PREPARE_MOVE_BUFFER(gBattleTextBuff1, move); BattleScriptExecute(BattleScript_BerryPPHealEnd2); BtlController_EmitSetMonData(0, i + REQUEST_PPMOVE1_BATTLE, 0, 1, &changedPP); MarkBattlerForControllerExec(gActiveBattler); effect = ITEM_PP_CHANGE; } } break; case HOLD_EFFECT_RESTORE_STATS: for (i = 0; i < BATTLE_STATS_NO; i++) { if (gBattleMons[battlerId].statStages[i] < 6) { gBattleMons[battlerId].statStages[i] = 6; effect = ITEM_STATS_CHANGE; } } if (effect) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; gActiveBattler = gBattlerAttacker = battlerId; BattleScriptExecute(BattleScript_WhiteHerbEnd2); } break; case HOLD_EFFECT_LEFTOVERS: if (gBattleMons[battlerId].hp < gBattleMons[battlerId].maxHP && !moveTurn) { gBattleMoveDamage = gBattleMons[battlerId].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; gBattleMoveDamage *= -1; BattleScriptExecute(BattleScript_ItemHealHP_End2); effect = ITEM_HP_CHANGE; RecordItemEffectBattle(battlerId, battlerHoldEffect); } break; // nice copy/paste there gamefreak, making a function for confuse berries was too much eh? case HOLD_EFFECT_CONFUSE_SPICY: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) { PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_SPICY); gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; gBattleMoveDamage *= -1; if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_SPICY) < 0) BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); else BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); effect = ITEM_HP_CHANGE; } break; case HOLD_EFFECT_CONFUSE_DRY: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) { PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_DRY); gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; gBattleMoveDamage *= -1; if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_DRY) < 0) BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); else BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); effect = ITEM_HP_CHANGE; } break; case HOLD_EFFECT_CONFUSE_SWEET: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) { PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_SWEET); gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; gBattleMoveDamage *= -1; if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_SWEET) < 0) BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); else BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); effect = ITEM_HP_CHANGE; } break; case HOLD_EFFECT_CONFUSE_BITTER: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) { PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_BITTER); gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; gBattleMoveDamage *= -1; if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_BITTER) < 0) BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); else BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); effect = ITEM_HP_CHANGE; } break; case HOLD_EFFECT_CONFUSE_SOUR: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) { PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_SOUR); gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; gBattleMoveDamage *= -1; if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_SOUR) < 0) BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); else BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); effect = ITEM_HP_CHANGE; } break; // copy/paste again, smh case HOLD_EFFECT_ATTACK_UP: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_ATK] < 0xC) { PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK); PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATROSE); gEffectBattler = battlerId; SET_STATCHANGER(STAT_ATK, 1, FALSE); gBattleScripting.animArg1 = 0xE + STAT_ATK; gBattleScripting.animArg2 = 0; BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); effect = ITEM_STATS_CHANGE; } break; case HOLD_EFFECT_DEFENSE_UP: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_DEF] < 0xC) { PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_DEF); gEffectBattler = battlerId; SET_STATCHANGER(STAT_DEF, 1, FALSE); gBattleScripting.animArg1 = 0xE + STAT_DEF; gBattleScripting.animArg2 = 0; BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); effect = ITEM_STATS_CHANGE; } break; case HOLD_EFFECT_SPEED_UP: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_SPEED] < 0xC) { PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPEED); gEffectBattler = battlerId; SET_STATCHANGER(STAT_SPEED, 1, FALSE); gBattleScripting.animArg1 = 0xE + STAT_SPEED; gBattleScripting.animArg2 = 0; BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); effect = ITEM_STATS_CHANGE; } break; case HOLD_EFFECT_SP_ATTACK_UP: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_SPATK] < 0xC) { PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPATK); gEffectBattler = battlerId; SET_STATCHANGER(STAT_SPATK, 1, FALSE); gBattleScripting.animArg1 = 0xE + STAT_SPATK; gBattleScripting.animArg2 = 0; BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); effect = ITEM_STATS_CHANGE; } break; case HOLD_EFFECT_SP_DEFENSE_UP: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_SPDEF] < 0xC) { PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPDEF); gEffectBattler = battlerId; SET_STATCHANGER(STAT_SPDEF, 1, FALSE); gBattleScripting.animArg1 = 0xE + STAT_SPDEF; gBattleScripting.animArg2 = 0; BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); effect = ITEM_STATS_CHANGE; } break; case HOLD_EFFECT_CRITICAL_UP: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && !(gBattleMons[battlerId].status2 & STATUS2_FOCUS_ENERGY)) { gBattleMons[battlerId].status2 |= STATUS2_FOCUS_ENERGY; BattleScriptExecute(BattleScript_BerryFocusEnergyEnd2); effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_RANDOM_STAT_UP: if (!moveTurn && gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam) { for (i = 0; i < 5; i++) { if (gBattleMons[battlerId].statStages[STAT_ATK + i] < 0xC) break; } if (i != 5) { do { i = Random() % 5; } while (gBattleMons[battlerId].statStages[STAT_ATK + i] == 0xC); PREPARE_STAT_BUFFER(gBattleTextBuff1, i + 1); gBattleTextBuff2[0] = B_BUFF_PLACEHOLDER_BEGIN; gBattleTextBuff2[1] = B_BUFF_STRING; gBattleTextBuff2[2] = STRINGID_STATSHARPLY; gBattleTextBuff2[3] = STRINGID_STATSHARPLY >> 8; gBattleTextBuff2[4] = B_BUFF_STRING; gBattleTextBuff2[5] = STRINGID_STATROSE; gBattleTextBuff2[6] = STRINGID_STATROSE >> 8; gBattleTextBuff2[7] = EOS; gEffectBattler = battlerId; SET_STATCHANGER(i + 1, 2, FALSE); gBattleScripting.animArg1 = 0x21 + i + 6; gBattleScripting.animArg2 = 0; BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); effect = ITEM_STATS_CHANGE; } } break; case HOLD_EFFECT_CURE_PAR: if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS) { gBattleMons[battlerId].status1 &= ~(STATUS1_PARALYSIS); BattleScriptExecute(BattleScript_BerryCurePrlzEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_PSN: if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY) { gBattleMons[battlerId].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER); BattleScriptExecute(BattleScript_BerryCurePsnEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_BRN: if (gBattleMons[battlerId].status1 & STATUS1_BURN) { gBattleMons[battlerId].status1 &= ~(STATUS1_BURN); BattleScriptExecute(BattleScript_BerryCureBrnEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_FRZ: if (gBattleMons[battlerId].status1 & STATUS1_FREEZE) { gBattleMons[battlerId].status1 &= ~(STATUS1_FREEZE); BattleScriptExecute(BattleScript_BerryCureFrzEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_SLP: if (gBattleMons[battlerId].status1 & STATUS1_SLEEP) { gBattleMons[battlerId].status1 &= ~(STATUS1_SLEEP); gBattleMons[battlerId].status2 &= ~(STATUS2_NIGHTMARE); BattleScriptExecute(BattleScript_BerryCureSlpEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_CONFUSION: if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION) { gBattleMons[battlerId].status2 &= ~(STATUS2_CONFUSION); BattleScriptExecute(BattleScript_BerryCureConfusionEnd2); effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_CURE_STATUS: if (gBattleMons[battlerId].status1 & STATUS1_ANY || gBattleMons[battlerId].status2 & STATUS2_CONFUSION) { i = 0; if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY) { StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); i++; } if (gBattleMons[battlerId].status1 & STATUS1_SLEEP) { gBattleMons[battlerId].status2 &= ~(STATUS2_NIGHTMARE); StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); i++; } if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS) { StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); i++; } if (gBattleMons[battlerId].status1 & STATUS1_BURN) { StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); i++; } if (gBattleMons[battlerId].status1 & STATUS1_FREEZE) { StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); i++; } if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION) { StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); i++; } if (!(i > 1)) gBattleCommunication[MULTISTRING_CHOOSER] = 0; else gBattleCommunication[MULTISTRING_CHOOSER] = 1; gBattleMons[battlerId].status1 = 0; gBattleMons[battlerId].status2 &= ~(STATUS2_CONFUSION); BattleScriptExecute(BattleScript_BerryCureChosenStatusEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_ATTRACT: if (gBattleMons[battlerId].status2 & STATUS2_INFATUATION) { gBattleMons[battlerId].status2 &= ~(STATUS2_INFATUATION); StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); BattleScriptExecute(BattleScript_BerryCureChosenStatusEnd2); gBattleCommunication[MULTISTRING_CHOOSER] = 0; effect = ITEM_EFFECT_OTHER; } break; } if (effect) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; gActiveBattler = gBattlerAttacker = battlerId; switch (effect) { case ITEM_STATUS_CHANGE: BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battlerId].status1); MarkBattlerForControllerExec(gActiveBattler); break; case ITEM_PP_CHANGE: if (!(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED) && !(gDisableStructs[battlerId].unk18_b & gBitTable[i])) gBattleMons[battlerId].pp[i] = changedPP; break; } } } break; case 2: break; case 3: for (battlerId = 0; battlerId < gBattlersCount; battlerId++) { gLastUsedItem = gBattleMons[battlerId].item; if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY) { battlerHoldEffect = gEnigmaBerries[battlerId].holdEffect; battlerHoldEffectParam = gEnigmaBerries[battlerId].holdEffectParam; } else { battlerHoldEffect = ItemId_GetHoldEffect(gLastUsedItem); battlerHoldEffectParam = ItemId_GetHoldEffectParam(gLastUsedItem); } switch (battlerHoldEffect) { case HOLD_EFFECT_CURE_PAR: if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS) { gBattleMons[battlerId].status1 &= ~(STATUS1_PARALYSIS); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCureParRet; effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_PSN: if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY) { gBattleMons[battlerId].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCurePsnRet; effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_BRN: if (gBattleMons[battlerId].status1 & STATUS1_BURN) { gBattleMons[battlerId].status1 &= ~(STATUS1_BURN); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCureBrnRet; effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_FRZ: if (gBattleMons[battlerId].status1 & STATUS1_FREEZE) { gBattleMons[battlerId].status1 &= ~(STATUS1_FREEZE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCureFrzRet; effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_SLP: if (gBattleMons[battlerId].status1 & STATUS1_SLEEP) { gBattleMons[battlerId].status1 &= ~(STATUS1_SLEEP); gBattleMons[battlerId].status2 &= ~(STATUS2_NIGHTMARE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCureSlpRet; effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_CONFUSION: if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION) { gBattleMons[battlerId].status2 &= ~(STATUS2_CONFUSION); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCureConfusionRet; effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_CURE_ATTRACT: if (gBattleMons[battlerId].status2 & STATUS2_INFATUATION) { gBattleMons[battlerId].status2 &= ~(STATUS2_INFATUATION); StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); BattleScriptPushCursor(); gBattleCommunication[MULTISTRING_CHOOSER] = 0; gBattlescriptCurrInstr = BattleScript_BerryCureChosenStatusRet; effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_CURE_STATUS: if (gBattleMons[battlerId].status1 & STATUS1_ANY || gBattleMons[battlerId].status2 & STATUS2_CONFUSION) { if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY) { StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); } if (gBattleMons[battlerId].status1 & STATUS1_SLEEP) { gBattleMons[battlerId].status2 &= ~(STATUS2_NIGHTMARE); StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); } if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS) { StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); } if (gBattleMons[battlerId].status1 & STATUS1_BURN) { StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); } if (gBattleMons[battlerId].status1 & STATUS1_FREEZE) { StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); } if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION) { StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); } gBattleMons[battlerId].status1 = 0; gBattleMons[battlerId].status2 &= ~(STATUS2_CONFUSION); BattleScriptPushCursor(); gBattleCommunication[MULTISTRING_CHOOSER] = 0; gBattlescriptCurrInstr = BattleScript_BerryCureChosenStatusRet; effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_RESTORE_STATS: for (i = 0; i < BATTLE_STATS_NO; i++) { if (gBattleMons[battlerId].statStages[i] < 6) { gBattleMons[battlerId].statStages[i] = 6; effect = ITEM_STATS_CHANGE; } } if (effect) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_WhiteHerbRet; return effect; // unnecessary return } break; } if (effect) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; gActiveBattler = battlerId; BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); break; } } break; case 4: if (gBattleMoveDamage) { switch (atkHoldEffect) { case HOLD_EFFECT_FLINCH: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && (Random() % 100) < atkHoldEffectParam && gBattleMoves[gCurrentMove].flags & FLAG_KINGSROCK_AFFECTED && gBattleMons[gBattlerTarget].hp) { gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_FLINCH; BattleScriptPushCursor(); SetMoveEffect(0, 0); BattleScriptPop(); } break; case HOLD_EFFECT_SHELL_BELL: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gSpecialStatuses[gBattlerTarget].dmg != 0 && gSpecialStatuses[gBattlerTarget].dmg != 0xFFFF && gBattlerAttacker != gBattlerTarget && gBattleMons[gBattlerAttacker].hp != gBattleMons[gBattlerAttacker].maxHP && gBattleMons[gBattlerAttacker].hp != 0) { gLastUsedItem = atkItem; gPotentialItemEffectBattler = gBattlerAttacker; gBattleScripting.battler = gBattlerAttacker; gBattleMoveDamage = (gSpecialStatuses[gBattlerTarget].dmg / atkHoldEffectParam) * -1; if (gBattleMoveDamage == 0) gBattleMoveDamage = -1; gSpecialStatuses[gBattlerTarget].dmg = 0; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ItemHealHP_Ret; effect++; } break; } } break; } return effect; } void ClearFuryCutterDestinyBondGrudge(u8 battlerId) { gDisableStructs[battlerId].furyCutterCounter = 0; gBattleMons[battlerId].status2 &= ~(STATUS2_DESTINY_BOND); gStatuses3[battlerId] &= ~(STATUS3_GRUDGE); } void HandleAction_RunBattleScript(void) // identical to RunBattleScriptCommands { if (gBattleControllerExecFlags == 0) gBattleScriptingCommandsTable[*gBattlescriptCurrInstr](); } u8 GetMoveTarget(u16 move, u8 setTarget) { u8 targetBattler = 0; u8 moveTarget; u8 side; if (setTarget) moveTarget = setTarget - 1; else moveTarget = gBattleMoves[move].target; switch (moveTarget) { case MOVE_TARGET_SELECTED: side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; if (gSideTimers[side].followmeTimer && gBattleMons[gSideTimers[side].followmeTarget].hp) targetBattler = gSideTimers[side].followmeTarget; else { side = GetBattlerSide(gBattlerAttacker); do { targetBattler = Random() % gBattlersCount; } while (targetBattler == gBattlerAttacker || side == GetBattlerSide(targetBattler) || gAbsentBattlerFlags & gBitTable[targetBattler]); if (gBattleMoves[move].type == TYPE_ELECTRIC && AbilityBattleEffects(ABILITYEFFECT_COUNT_OTHER_SIDE, gBattlerAttacker, ABILITY_LIGHTNING_ROD, 0, 0) && gBattleMons[targetBattler].ability != ABILITY_LIGHTNING_ROD) { targetBattler ^= BIT_FLANK; RecordAbilityBattle(targetBattler, gBattleMons[targetBattler].ability); gSpecialStatuses[targetBattler].lightningRodRedirected = 1; } } break; case MOVE_TARGET_DEPENDS: case MOVE_TARGET_BOTH: case MOVE_TARGET_FOES_AND_ALLY: case MOVE_TARGET_OPPONENTS_FIELD: targetBattler = GetBattlerAtPosition((GetBattlerPosition(gBattlerAttacker) & BIT_SIDE) ^ BIT_SIDE); if (gAbsentBattlerFlags & gBitTable[targetBattler]) targetBattler ^= BIT_FLANK; break; case MOVE_TARGET_RANDOM: side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; if (gSideTimers[side].followmeTimer && gBattleMons[gSideTimers[side].followmeTarget].hp) targetBattler = gSideTimers[side].followmeTarget; else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && moveTarget & MOVE_TARGET_RANDOM) { if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) { if (Random() & 1) targetBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); else targetBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); } else { if (Random() & 1) targetBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); else targetBattler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); } if (gAbsentBattlerFlags & gBitTable[targetBattler]) targetBattler ^= BIT_FLANK; } else targetBattler = GetBattlerAtPosition((GetBattlerPosition(gBattlerAttacker) & BIT_SIDE) ^ BIT_SIDE); break; case MOVE_TARGET_USER_OR_SELECTED: case MOVE_TARGET_USER: targetBattler = gBattlerAttacker; break; } *(gBattleStruct->moveTarget + gBattlerAttacker) = targetBattler; return targetBattler; } static bool32 HasObedientBitSet(u8 battlerId) { if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT) return TRUE; if (GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES, NULL) != SPECIES_DEOXYS && GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES, NULL) != SPECIES_MEW) return TRUE; return GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_OBEDIENCE, NULL); } u8 IsMonDisobedient(void) { s32 rnd; s32 calc; u8 obedienceLevel = 0; if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000)) return 0; if (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT) return 0; if (HasObedientBitSet(gBattlerAttacker)) // only if species is Mew or Deoxys { if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gBattlerAttacker) == 2) return 0; if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) return 0; if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) return 0; if (!IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName)) return 0; if (FlagGet(FLAG_BADGE08_GET)) return 0; obedienceLevel = 10; if (FlagGet(FLAG_BADGE02_GET)) obedienceLevel = 30; if (FlagGet(FLAG_BADGE04_GET)) obedienceLevel = 50; if (FlagGet(FLAG_BADGE06_GET)) obedienceLevel = 70; } if (gBattleMons[gBattlerAttacker].level <= obedienceLevel) return 0; rnd = (Random() & 255); calc = (gBattleMons[gBattlerAttacker].level + obedienceLevel) * rnd >> 8; if (calc < obedienceLevel) return 0; // is not obedient if (gCurrentMove == MOVE_RAGE) gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_RAGE); if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP && (gCurrentMove == MOVE_SNORE || gCurrentMove == MOVE_SLEEP_TALK)) { gBattlescriptCurrInstr = BattleScript_82DB695; return 1; } rnd = (Random() & 255); calc = (gBattleMons[gBattlerAttacker].level + obedienceLevel) * rnd >> 8; if (calc < obedienceLevel) { calc = CheckMoveLimitations(gBattlerAttacker, gBitTable[gCurrMovePos], 0xFF); if (calc == 0xF) // all moves cannot be used { gBattleCommunication[MULTISTRING_CHOOSER] = Random() & 3; gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; return 1; } else // use a random move { do { gCurrMovePos = gChosenMovePos = Random() & 3; } while (gBitTable[gCurrMovePos] & calc); gRandomMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos]; gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove; gBattlerTarget = GetMoveTarget(gRandomMove, 0); gHitMarker |= HITMARKER_x200000; return 2; } } else { obedienceLevel = gBattleMons[gBattlerAttacker].level - obedienceLevel; calc = (Random() & 255); if (calc < obedienceLevel && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY) && gBattleMons[gBattlerAttacker].ability != ABILITY_VITAL_SPIRIT && gBattleMons[gBattlerAttacker].ability != ABILITY_INSOMNIA) { // try putting asleep int i; for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].status2 & STATUS2_UPROAR) break; } if (i == gBattlersCount) { gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep; return 1; } } calc -= obedienceLevel; if (calc < obedienceLevel) { gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE, FALSE); gBattlerTarget = gBattlerAttacker; gBattlescriptCurrInstr = BattleScript_82DB6F0; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; return 2; } else { gBattleCommunication[MULTISTRING_CHOOSER] = Random() & 3; gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; return 1; } } } u32 GetBattlerAbility(u8 battlerId) { if (gStatuses3[battlerId] & STATUS3_GASTRO_ACID) return ABILITY_NONE; else if ((gBattleMons[gBattlerAttacker].ability == ABILITY_MOLD_BREAKER || gBattleMons[gBattlerAttacker].ability == ABILITY_TERAVOLT || gBattleMons[gBattlerAttacker].ability == ABILITY_TURBOBLAZE) && sAbilitiesAffectedByMoldBreaker[gBattleMons[battlerId].ability] && gBattlerByTurnOrder[gCurrentTurnActionNumber] == gBattlerAttacker && gActionsByTurnOrder[gBattlerByTurnOrder[gBattlerAttacker]] == B_ACTION_USE_MOVE && gCurrentTurnActionNumber < gBattlersCount && !(gStatuses3[gBattlerAttacker] & STATUS3_GASTRO_ACID)) return ABILITY_NONE; else return gBattleMons[battlerId].ability; } u32 GetBattlerHoldEffect(u8 battlerId, bool32 checkNegating) { if (checkNegating) { if (gStatuses3[battlerId] & STATUS3_EMBARGO) return HOLD_EFFECT_NONE; if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM) return HOLD_EFFECT_NONE; if (gBattleMons[battlerId].ability == ABILITY_KLUTZ && !(gStatuses3[battlerId] & STATUS3_GASTRO_ACID)) return HOLD_EFFECT_NONE; } gPotentialItemEffectBattler = battlerId; if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY) return gEnigmaBerries[battlerId].holdEffect; else return ItemId_GetHoldEffect(gBattleMons[battlerId].item); } u32 GetBattlerHoldEffectParam(u8 battlerId) { if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY) return gEnigmaBerries[battlerId].holdEffectParam; else return ItemId_GetHoldEffectParam(gBattleMons[battlerId].item); } bool32 IsMoveMakingContact(u16 move, u8 battlerAtk) { if (!(gBattleMoves[move].flags & FLAG_MAKES_CONTACT)) return FALSE; else if (GetBattlerAbility(battlerAtk) == ABILITY_LONG_REACH) return FALSE; else if (GetBattlerHoldEffect(battlerAtk, TRUE) == HOLD_EFFECT_PROTECTIVE_PADS) return FALSE; else return TRUE; } bool32 IsBattlerGrounded(u8 battlerId) { if (GetBattlerHoldEffect(battlerId, TRUE) == HOLD_EFFECT_IRON_BALL) return TRUE; else if (gFieldStatuses & STATUS_FIELD_GRAVITY) return TRUE; else if (gStatuses3[battlerId] & STATUS3_ROOTED) return TRUE; else if (gStatuses3[battlerId] & STATUS3_SMACKED_DOWN) return TRUE; else if (gStatuses3[battlerId] & STATUS3_TELEKINESIS) return FALSE; else if (gStatuses3[battlerId] & STATUS3_MAGNET_RISE) return FALSE; else if (GetBattlerAbility(battlerId) == ABILITY_LEVITATE) return FALSE; else if (IS_BATTLER_OF_TYPE(battlerId, TYPE_FLYING)) return FALSE; else return TRUE; } bool32 IsBattlerAlive(u8 battlerId) { if (gBattleMons[battlerId].hp == 0) return FALSE; else if (battlerId >= gBattlersCount) return FALSE; else if (gAbsentBattlerFlags & gBitTable[battlerId]) return FALSE; else return TRUE; } u8 GetBattleMonMoveSlot(struct BattlePokemon *battleMon, u16 move) { u8 i; for (i = 0; i < 4; i++) { if (battleMon->moves[i] == move) break; } return i; } u32 GetBattlerWeight(u8 battlerId) { u32 weight = GetPokedexHeightWeight(SpeciesToNationalPokedexNum(gBattleMons[battlerId].species), 1); u32 ability = GetBattlerAbility(battlerId); u32 holdEffect = GetBattlerHoldEffect(battlerId, TRUE); if (ability == ABILITY_HEAVY_METAL) weight *= 2; else if (ability == ABILITY_LIGHT_METAL) weight /= 2; if (holdEffect == HOLD_EFFECT_FLOAT_STONE) weight /= 2; if (gDisableStructs[battlerId].autonomizeCount) weight -= 1000 * gDisableStructs[battlerId].autonomizeCount; if (weight == 0) weight = 1; return weight; } u32 CountBattlerStatIncreases(u8 battlerId, bool32 countEvasionAcc) { u32 i; u32 count = 0; for (i = 0; i < BATTLE_STATS_NO; i++) { if ((i == STAT_ACC || i == STAT_EVASION) && !countEvasionAcc) continue; if (gBattleMons[battlerId].statStages[i] > 6) // Stat is increased. count += gBattleMons[battlerId].statStages[i] - 6; } return count; } u32 GetMoveTargetCount(u16 move, u8 battlerAtk, u8 battlerDef) { switch (gBattleMoves[move].target) { case MOVE_TARGET_BOTH: return IsBattlerAlive(battlerDef) + IsBattlerAlive(BATTLE_PARTNER(battlerDef)); case MOVE_TARGET_FOES_AND_ALLY: return IsBattlerAlive(battlerDef) + IsBattlerAlive(BATTLE_PARTNER(battlerDef)) + IsBattlerAlive(BATTLE_PARTNER(battlerAtk)); case MOVE_TARGET_OPPONENTS_FIELD: return 1; case MOVE_TARGET_DEPENDS: case MOVE_TARGET_SELECTED: case MOVE_TARGET_RANDOM: case MOVE_TARGET_USER_OR_SELECTED: return IsBattlerAlive(battlerDef); case MOVE_TARGET_USER: return IsBattlerAlive(battlerAtk); default: return 0; } } static void MulModifier(u16 *modifier, u16 val) { *modifier = UQ_4_12_TO_INT((*modifier * val) + UQ_4_12_ROUND); } static u32 ApplyModifier(u16 modifier, u32 val) { return UQ_4_12_TO_INT((modifier * val) + UQ_4_12_ROUND); } static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) { u32 i; u16 basePower = gBattleMoves[move].power; u32 weight, hpFraction, speed; switch (gBattleMoves[move].effect) { case EFFECT_PLEDGE: // todo break; case EFFECT_FLING: // todo break; case EFFECT_ERUPTION: basePower = gBattleMons[battlerAtk].hp * basePower / gBattleMons[battlerAtk].maxHP; break; case EFFECT_FLAIL: hpFraction = GetScaledHPFraction(gBattleMons[battlerAtk].hp, gBattleMons[battlerAtk].maxHP, 48); for (i = 0; i < sizeof(sFlailHpScaleToPowerTable); i += 2) { if (hpFraction <= sFlailHpScaleToPowerTable[i]) break; } basePower = sFlailHpScaleToPowerTable[i + 1]; break; case EFFECT_RETURN: basePower = 10 * (gBattleMons[battlerAtk].friendship) / 25; break; case EFFECT_FRUSTRATION: basePower = 10 * (255 - gBattleMons[battlerAtk].friendship) / 25; break; case EFFECT_FURY_CUTTER: for (i = 1; i < gDisableStructs[battlerAtk].furyCutterCounter; i++) basePower *= 2; break; case EFFECT_ROLLOUT: for (i = 1; i < (5 - gDisableStructs[battlerAtk].rolloutCounter1); i++) basePower *= 2; if (gBattleMons[battlerAtk].status2 & STATUS2_DEFENSE_CURL) basePower *= 2; break; case EFFECT_MAGNITUDE: basePower = gBattleStruct->magnitudeBasePower; break; case EFFECT_PRESENT: basePower = gBattleStruct->presentBasePower; break; case EFFECT_TRIPLE_KICK: basePower += gBattleScripting.tripleKickPower; break; case EFFECT_SPIT_UP: basePower = 100 * gDisableStructs[battlerAtk].stockpileCounter; break; case EFFECT_REVENGE: if ((gProtectStructs[battlerAtk].physicalDmg && gProtectStructs[battlerAtk].physicalBattlerId == battlerDef) || (gProtectStructs[battlerAtk].specialDmg && gProtectStructs[battlerAtk].specialBattlerId == battlerDef)) basePower *= 2; break; case EFFECT_WEATHER_BALL: if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_ANY) basePower *= 2; break; case EFFECT_PURSUIT: if (gCurrentActionFuncId == B_ACTION_SWITCH) basePower *= 2; break; case EFFECT_NATURAL_GIFT: // todo break; case EFFECT_WAKE_UP_SLAP: if (gBattleMons[battlerDef].status1 & STATUS1_SLEEP) basePower *= 2; break; case EFFECT_SMELLINGSALT: if (gBattleMons[battlerDef].status1 & STATUS1_PARALYSIS) basePower *= 2; break; case EFFECT_WRING_OUT: basePower = 120 * gBattleMons[battlerDef].hp / gBattleMons[battlerDef].maxHP; break; case EFFECT_HEX: if (gBattleMons[battlerDef].status1 & STATUS1_ANY) basePower *= 2; break; case EFFECT_ASSURANCE: if (gSpecialStatuses[battlerDef].physicalDmg != 0 || gSpecialStatuses[battlerDef].specialDmg != 0) basePower *= 2; break; case EFFECT_TRUMP_CARD: i = GetBattleMonMoveSlot(&gBattleMons[battlerAtk], move); if (i != 4) { switch (gBattleMons[battlerAtk].pp[i]) { case 0: basePower = 200; break; case 1: basePower = 80; break; case 2: basePower = 60; break; case 3: basePower = 50; break; default: basePower = 40; break; } } break; case EFFECT_ACROBATICS: if (gBattleMons[battlerAtk].item == ITEM_NONE) basePower *= 2; break; case EFFECT_LOW_KICK: weight = GetBattlerWeight(battlerDef); for (i = 0; sWeightToDamageTable[i] != 0xFFFF; i += 2) { if (sWeightToDamageTable[i] > weight) break; } if (sWeightToDamageTable[i] != 0xFFFF) basePower = sWeightToDamageTable[i + 1]; else basePower = 120; break; case EFFECT_HEAT_CRASH: weight = GetBattlerWeight(battlerAtk) / GetBattlerWeight(battlerDef); if (weight >= 5) basePower = 120; else if (weight == 4) basePower = 100; else if (weight == 3) basePower = 80; else if (weight == 2) basePower = 60; else basePower = 40; break; case EFFECT_PUNISHMENT: basePower = 60 + (CountBattlerStatIncreases(battlerAtk, FALSE) * 20); if (basePower > 200) basePower = 200; break; case EFFECT_STORED_POWER: basePower = 60 + (CountBattlerStatIncreases(battlerAtk, TRUE) * 20); break; case EFFECT_ELECTRO_BALL: speed = GetBattlerTotalSpeedStat(battlerAtk) / GetBattlerTotalSpeedStat(battlerDef); if (speed >= ARRAY_COUNT(sSpeedDiffToDmgTable)) speed = ARRAY_COUNT(sSpeedDiffToDmgTable) - 1; basePower = sSpeedDiffToDmgTable[speed]; break; case EFFECT_GYRO_BALL: basePower = ((25 * GetBattlerTotalSpeedStat(battlerDef)) / GetBattlerTotalSpeedStat(battlerAtk)) + 1; if (basePower > 150) basePower = 150; break; case EFFECT_ECHOED_VOICE: if (gFieldTimers.echoVoiceCounter != 0) { if (gFieldTimers.echoVoiceCounter >= 5) basePower *= 5; else basePower *= gFieldTimers.echoVoiceCounter; } break; case EFFECT_PAYBACK: if (GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef)) basePower *= 2; break; case EFFECT_GUST: case EFFECT_TWISTER: if (gStatuses3[battlerDef] & STATUS3_ON_AIR) basePower *= 2; break; case EFFECT_ROUND: if (gChosenMoveByBattler[BATTLE_PARTNER(battlerAtk)] == MOVE_ROUND && !(gAbsentBattlerFlags & gBitTable[BATTLE_PARTNER(battlerAtk)])) basePower *= 2; break; } if (basePower == 0) basePower = 1; return basePower; } static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType) { u32 i; u32 holdEffectAtk, holdEffectParamAtk; u16 basePower = CalcMoveBasePower(move, battlerAtk, battlerDef); u16 holdEffectModifier; u16 modifier = UQ_4_12(1.0); // attacker's abilities switch (GetBattlerAbility(battlerAtk)) { case ABILITY_TECHNICIAN: if (basePower <= 60) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_FLARE_BOOST: if (gBattleMons[battlerAtk].status1 & STATUS1_BURN && IS_MOVE_SPECIAL(move)) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_TOXIC_BOOST: if (gBattleMons[battlerAtk].status1 & STATUS1_PSN_ANY && IS_MOVE_PHYSICAL(move)) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_RECKLESS: if (gBattleMoves[move].flags & FLAG_RECKLESS_BOOST) MulModifier(&modifier, UQ_4_12(1.2)); break; case ABILITY_IRON_FIST: if (gBattleMoves[move].flags & FLAG_IRON_FIST_BOOST) MulModifier(&modifier, UQ_4_12(1.2)); break; case ABILITY_SHEER_FORCE: if (gBattleMoves[move].flags & FLAG_SHEER_FORCE_BOOST) MulModifier(&modifier, UQ_4_12(1.3)); break; case ABILITY_SAND_FORCE: if (moveType == TYPE_STEEL || moveType == TYPE_ROCK || moveType == TYPE_GROUND) MulModifier(&modifier, UQ_4_12(1.3)); break; case ABILITY_RIVALRY: if (GetGenderFromSpeciesAndPersonality(gBattleMons[battlerAtk].species, gBattleMons[battlerAtk].personality) != MON_GENDERLESS && GetGenderFromSpeciesAndPersonality(gBattleMons[battlerDef].species, gBattleMons[battlerDef].personality) != MON_GENDERLESS) { if (GetGenderFromSpeciesAndPersonality(gBattleMons[battlerAtk].species, gBattleMons[battlerAtk].personality) == GetGenderFromSpeciesAndPersonality(gBattleMons[battlerDef].species, gBattleMons[battlerDef].personality)) MulModifier(&modifier, UQ_4_12(1.25)); else MulModifier(&modifier, UQ_4_12(0.75)); } break; case ABILITY_ANALYTIC: if (GetBattlerTurnOrderNum(battlerAtk) == gBattlersCount - 1 && move != MOVE_FUTURE_SIGHT && move != MOVE_DOOM_DESIRE) MulModifier(&modifier, UQ_4_12(1.3)); break; case ABILITY_TOUGH_CLAWS: if (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) MulModifier(&modifier, UQ_4_12(1.3)); break; case ABILITY_STRONG_JAW: if (gBattleMoves[move].flags & FLAG_STRONG_JAW_BOOST) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_MEGA_LAUNCHER: if (gBattleMoves[move].flags & FLAG_MEGA_LAUNCHER_BOOST) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_WATER_BUBBLE: if (moveType == TYPE_WATER) MulModifier(&modifier, UQ_4_12(2.0)); break; case ABILITY_STEELWORKER: if (moveType == TYPE_STEEL) MulModifier(&modifier, UQ_4_12(1.5)); break; } // field abilities if ((ABILITY_ON_FIELD(ABILITY_DARK_AURA) && moveType == TYPE_DARK) || (ABILITY_ON_FIELD(ABILITY_FAIRY_AURA) && moveType == TYPE_FAIRY)) { if (ABILITY_ON_FIELD(ABILITY_AURA_BREAK)) MulModifier(&modifier, UQ_4_12(0.75)); else MulModifier(&modifier, UQ_4_12(1.25)); } // attacker partner's abilities if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk))) { switch (GetBattlerAbility(BATTLE_PARTNER(battlerAtk))) { case ABILITY_BATTERY: if (IS_MOVE_SPECIAL(move)) MulModifier(&modifier, UQ_4_12(1.3)); break; } } // target's abilities switch (GetBattlerAbility(battlerDef)) { case ABILITY_HEATPROOF: case ABILITY_WATER_BUBBLE: if (moveType == TYPE_FIRE) MulModifier(&modifier, UQ_4_12(0.5)); break; case ABILITY_DRY_SKIN: if (moveType == TYPE_FIRE) MulModifier(&modifier, UQ_4_12(1.25)); break; case ABILITY_FLUFFY: if (IsMoveMakingContact(move, battlerAtk)) MulModifier(&modifier, UQ_4_12(0.5)); if (moveType == TYPE_FIRE) MulModifier(&modifier, UQ_4_12(2.0)); break; } holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); holdEffectParamAtk = GetBattlerHoldEffectParam(battlerAtk); if (holdEffectParamAtk > 100) holdEffectParamAtk = 100; holdEffectModifier = UQ_4_12(1.0) + sPercentToModifier[holdEffectParamAtk]; // attacker's hold effect switch (holdEffectAtk) { case HOLD_EFFECT_MUSCLE_BAND: if (IS_MOVE_PHYSICAL(move)) MulModifier(&modifier, holdEffectModifier); break; case HOLD_EFFECT_WISE_GLASSES: if (IS_MOVE_SPECIAL(move)) MulModifier(&modifier, holdEffectModifier); break; case HOLD_EFFECT_LUSTROUS_ORB: if (gBattleMons[battlerAtk].species == SPECIES_PALKIA && (moveType == TYPE_WATER || moveType == TYPE_DRAGON)) MulModifier(&modifier, holdEffectModifier); break; case HOLD_EFFECT_ADAMANT_ORB: if (gBattleMons[battlerAtk].species == SPECIES_DIALGA && (moveType == TYPE_STEEL || moveType == TYPE_DRAGON)) MulModifier(&modifier, holdEffectModifier); break; case HOLD_EFFECT_GRISEOUS_ORB: if (gBattleMons[battlerAtk].species == SPECIES_GIRATINA && (moveType == TYPE_GHOST || moveType == TYPE_DRAGON)) MulModifier(&modifier, holdEffectModifier); break; case HOLD_EFFECT_SOUL_DEW: if ((gBattleMons[battlerAtk].species == SPECIES_LATIAS || gBattleMons[battlerAtk].species == SPECIES_LATIOS) && !(gBattleTypeFlags & BATTLE_TYPE_FRONTIER)) MulModifier(&modifier, holdEffectModifier); break; case HOLD_EFFECT_BUG_POWER: case HOLD_EFFECT_STEEL_POWER: case HOLD_EFFECT_GROUND_POWER: case HOLD_EFFECT_ROCK_POWER: case HOLD_EFFECT_GRASS_POWER: case HOLD_EFFECT_DARK_POWER: case HOLD_EFFECT_FIGHTING_POWER: case HOLD_EFFECT_ELECTRIC_POWER: case HOLD_EFFECT_WATER_POWER: case HOLD_EFFECT_FLYING_POWER: case HOLD_EFFECT_POISON_POWER: case HOLD_EFFECT_ICE_POWER: case HOLD_EFFECT_GHOST_POWER: case HOLD_EFFECT_PSYCHIC_POWER: case HOLD_EFFECT_FIRE_POWER: case HOLD_EFFECT_DRAGON_POWER: case HOLD_EFFECT_NORMAL_POWER: case HOLD_EFFECT_FAIRY_POWER: for (i = 0; i < ARRAY_COUNT(sHoldEffectToType); i++) { if (holdEffectAtk == sHoldEffectToType[i][0]) { if (moveType == sHoldEffectToType[i][1]) MulModifier(&modifier, holdEffectModifier); break; } } break; } // move effect switch (gBattleMoves[move].effect) { case EFFECT_FACADE: if (gBattleMons[battlerAtk].status1 & (STATUS1_BURN | STATUS1_PSN_ANY | STATUS1_PARALYSIS)) MulModifier(&modifier, UQ_4_12(2.0)); break; case EFFECT_BRINE: if (gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 2)) MulModifier(&modifier, UQ_4_12(2.0)); break; case EFFECT_VENOSHOCK: if (gBattleMons[battlerAtk].status1 & STATUS1_PSN_ANY) MulModifier(&modifier, UQ_4_12(2.0)); break; case EFFECT_RETALITATE: // todo break; case EFFECT_SOLARBEAM: if (WEATHER_HAS_EFFECT && gBattleWeather & (WEATHER_HAIL_ANY | WEATHER_SANDSTORM_ANY | WEATHER_RAIN_ANY)) MulModifier(&modifier, UQ_4_12(0.5)); break; case EFFECT_BULLDOZE: case EFFECT_MAGNITUDE: case EFFECT_EARTHQUAKE: if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battlerDef)) MulModifier(&modifier, UQ_4_12(0.5)); break; case EFFECT_KNOCK_OFF: if (gBattleMons[battlerDef].item != ITEM_NONE && GetBattlerAbility(battlerDef) != ABILITY_STICKY_HOLD) MulModifier(&modifier, UQ_4_12(1.5)); break; } // various effecs if (gProtectStructs[battlerAtk].helpingHand) MulModifier(&modifier, UQ_4_12(1.5)); if (gStatuses3[battlerAtk] & STATUS3_CHARGED_UP && moveType == TYPE_ELECTRIC) MulModifier(&modifier, UQ_4_12(2.0)); if (gStatuses3[battlerAtk] & STATUS3_ME_FIRST) MulModifier(&modifier, UQ_4_12(1.5)); if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && moveType == TYPE_GRASS && IsBattlerGrounded(battlerAtk)) MulModifier(&modifier, UQ_4_12(1.5)); if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN && moveType == TYPE_DRAGON && IsBattlerGrounded(battlerDef)) MulModifier(&modifier, UQ_4_12(0.5)); if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && moveType == TYPE_ELECTRIC && IsBattlerGrounded(battlerAtk)) MulModifier(&modifier, UQ_4_12(1.5)); if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN && moveType == TYPE_PSYCHIC && IsBattlerGrounded(battlerAtk)) MulModifier(&modifier, UQ_4_12(1.5)); return ApplyModifier(modifier, basePower); } static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, bool32 isCrit) { u8 atkStage; u32 atkStat; u16 modifier; if (gBattleMoves[move].effect == EFFECT_FOUL_PLAY) { if (IS_MOVE_PHYSICAL(move)) atkStat = gBattleMons[battlerDef].attack; else atkStat = gBattleMons[battlerDef].spAttack; atkStage = gBattleMons[battlerDef].statStages[STAT_ATK]; } else { if (IS_MOVE_PHYSICAL(move)) atkStat = gBattleMons[battlerAtk].attack; else atkStat = gBattleMons[battlerAtk].spAttack; atkStage = gBattleMons[battlerAtk].statStages[STAT_ATK]; } // critical hits ignore attack stat's stage drops if (isCrit && atkStage < 6) atkStage = 6; // pokemon with unaware ignore attack stat changes while taking damage if (GetBattlerAbility(battlerDef) == ABILITY_UNAWARE) atkStage = 6; atkStat *= gStatStageRatios[atkStage][0]; atkStat /= gStatStageRatios[atkStage][1]; // apply attack stat modifiers modifier = UQ_4_12(1.0); // attacker's abilities switch (GetBattlerAbility(battlerAtk)) { case ABILITY_HUGE_POWER: case ABILITY_PURE_POWER: if (IS_MOVE_PHYSICAL(move)) MulModifier(&modifier, UQ_4_12(2.0)); break; case ABILITY_SLOW_START: if (gDisableStructs[battlerAtk].slowStartTimer != 0) MulModifier(&modifier, UQ_4_12(0.5)); break; case ABILITY_SOLAR_POWER: if (IS_MOVE_SPECIAL(move) && WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_DEFEATIST: if (gBattleMons[battlerAtk].hp <= (gBattleMons[battlerDef].maxHP / 2)) MulModifier(&modifier, UQ_4_12(0.5)); break; case ABILITY_FLASH_FIRE: if (moveType == TYPE_FIRE && gBattleResources->flags->flags[battlerAtk] & RESOURCE_FLAG_FLASH_FIRE) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_SWARM: if (moveType == TYPE_BUG && gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 3)) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_TORRENT: if (moveType == TYPE_WATER && gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 3)) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_BLAZE: if (moveType == TYPE_FIRE && gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 3)) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_OVERGROW: if (moveType == TYPE_GRASS && gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].maxHP / 3)) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_PLUS: case ABILITY_MINUS: if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk))) { u32 partnerAbility = GetBattlerAbility(BATTLE_PARTNER(battlerAtk)); if (partnerAbility == ABILITY_PLUS || partnerAbility == ABILITY_MINUS) MulModifier(&modifier, UQ_4_12(1.5)); } break; case ABILITY_FLOWER_GIFT: if (gBattleMons[battlerAtk].species == SPECIES_CHERRIM && WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_HUSTLE: if (IS_MOVE_PHYSICAL(move)) MulModifier(&modifier, UQ_4_12(1.5)); break; } // target's abilities switch (GetBattlerAbility(battlerDef)) { case ABILITY_THICK_FAT: if (moveType == TYPE_FIRE || moveType == TYPE_ICE) MulModifier(&modifier, UQ_4_12(0.5)); break; } // ally's abilities if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk))) { switch (GetBattlerAbility(BATTLE_PARTNER(battlerAtk))) { case ABILITY_FLOWER_GIFT: if (gBattleMons[BATTLE_PARTNER(battlerAtk)].species == SPECIES_CHERRIM) MulModifier(&modifier, UQ_4_12(1.5)); break; } } // attacker's hold effect switch (GetBattlerHoldEffect(battlerAtk, TRUE)) { case HOLD_EFFECT_THICK_CLUB: if ((gBattleMons[battlerAtk].species == SPECIES_CUBONE || gBattleMons[battlerAtk].species == SPECIES_MAROWAK) && IS_MOVE_PHYSICAL(move)) MulModifier(&modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_DEEP_SEA_TOOTH: if (gBattleMons[battlerAtk].species == SPECIES_CLAMPERL && IS_MOVE_SPECIAL(move)) MulModifier(&modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_LIGHT_BALL: if (gBattleMons[battlerAtk].species == SPECIES_PIKACHU) MulModifier(&modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_CHOICE_BAND: if (IS_MOVE_PHYSICAL(move)) MulModifier(&modifier, UQ_4_12(1.5)); break; case HOLD_EFFECT_CHOICE_SPECS: if (IS_MOVE_SPECIAL(move)) MulModifier(&modifier, UQ_4_12(1.5)); break; } return ApplyModifier(modifier, atkStat); } static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, bool32 isCrit) { bool32 usesDefStat; u8 defStage; u32 defStat, def, spDef; u16 modifier; if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM) // the defense stats are swapped { def = gBattleMons[battlerDef].spDefense; spDef = gBattleMons[battlerDef].defense; } else { def = gBattleMons[battlerDef].defense; spDef = gBattleMons[battlerDef].spDefense; } if (gBattleMoves[move].effect == EFFECT_PSYSHOCK || IS_MOVE_PHYSICAL(move)) // uses defense stat instead of sp.def { defStat = def; defStage = gBattleMons[battlerDef].statStages[STAT_DEF]; usesDefStat = TRUE; } else // is special { defStat = spDef; defStage = gBattleMons[battlerDef].statStages[STAT_SPDEF]; usesDefStat = FALSE; } // critical hits ignore positive stat changes if (isCrit && defStage > 6) defStage = 6; // pokemon with unaware ignore defense stat changes while dealing damage if (GetBattlerAbility(battlerAtk) == ABILITY_UNAWARE) defStage = 6; // certain moves also ignore stat changes if (gBattleMoves[move].flags & FLAG_STAT_STAGES_IGNORED) defStage = 6; defStat *= gStatStageRatios[defStage][0]; defStat /= gStatStageRatios[defStage][1]; // apply defense stat modifiers modifier = UQ_4_12(1.0); // target's abilities switch (GetBattlerAbility(battlerDef)) { case ABILITY_MARVEL_SCALE: if (gBattleMons[battlerDef].status1 & STATUS1_ANY && usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_FUR_COAT: if (usesDefStat) MulModifier(&modifier, UQ_4_12(2.0)); break; case ABILITY_GRASS_PELT: if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_FLOWER_GIFT: if (gBattleMons[battlerDef].species == SPECIES_CHERRIM && WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY && !usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); break; } // ally's abilities if (IsBattlerAlive(BATTLE_PARTNER(battlerDef))) { switch (GetBattlerAbility(BATTLE_PARTNER(battlerDef))) { case ABILITY_FLOWER_GIFT: if (gBattleMons[BATTLE_PARTNER(battlerDef)].species == SPECIES_CHERRIM && !usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); break; } } // target's hold effects switch (GetBattlerHoldEffect(battlerDef, TRUE)) { case HOLD_EFFECT_DEEP_SEA_SCALE: if (gBattleMons[battlerDef].species == SPECIES_CLAMPERL && !usesDefStat) MulModifier(&modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_METAL_POWDER: if (gBattleMons[battlerDef].species == SPECIES_DITTO && usesDefStat && !(gBattleMons[battlerDef].status2 & STATUS2_TRANSFORMED)) MulModifier(&modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_EVIOLITE: // todo break; case HOLD_EFFECT_ASSAULT_VEST: if (!usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); break; } return ApplyModifier(modifier, defStat); } static u32 CalcFinalDmg(u32 dmg, u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, u16 typeEffectivenessModifier, bool32 isCrit) { u32 abilityAtk = GetBattlerAbility(battlerAtk); u32 abilityDef = GetBattlerAbility(battlerDef); u32 defSide = GET_BATTLER_SIDE(battlerDef); u16 finalModifier = UQ_4_12(1.0); // check multiple targets in double battle if (GetMoveTargetCount(move, battlerAtk, battlerDef) >= 2) MulModifier(&finalModifier, UQ_4_12(0.75)); // take type effectiveness MulModifier(&finalModifier, typeEffectivenessModifier); // check crit if (isCrit) dmg = ApplyModifier(UQ_4_12(1.5), dmg); // check burn if (gBattleMons[battlerAtk].status1 & STATUS1_BURN && gBattleMoves[move].effect != EFFECT_FACADE && abilityAtk != ABILITY_GUTS) dmg = ApplyModifier(UQ_4_12(0.5), dmg); // check sunny/rain weather if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_RAIN_ANY) { if (moveType == TYPE_FIRE) dmg = ApplyModifier(UQ_4_12(0.5), dmg); else if (moveType == TYPE_WATER) dmg = ApplyModifier(UQ_4_12(1.5), dmg); } else if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY) { if (moveType == TYPE_FIRE) dmg = ApplyModifier(UQ_4_12(1.5), dmg); else if (moveType == TYPE_WATER) dmg = ApplyModifier(UQ_4_12(0.5), dmg); } // check stab if (IS_BATTLER_OF_TYPE(battlerAtk, moveType) && move != MOVE_STRUGGLE) { if (abilityAtk == ABILITY_ADAPTABILITY) MulModifier(&finalModifier, UQ_4_12(2.0)); else MulModifier(&finalModifier, UQ_4_12(1.5)); } // reflect, light screen, aurora veil if ((gSideStatuses[defSide] & SIDE_STATUS_REFLECT && IS_MOVE_PHYSICAL(move)) || (gSideStatuses[defSide] & SIDE_STATUS_LIGHTSCREEN && IS_MOVE_SPECIAL(move)) || (gSideStatuses[defSide] & SIDE_STATUS_AURORA_VEIL)) { if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) MulModifier(&finalModifier, UQ_4_12(0.66)); else MulModifier(&finalModifier, UQ_4_12(0.5)); } // attacker's abilities switch (abilityAtk) { case ABILITY_TINTED_LENS: if (typeEffectivenessModifier <= UQ_4_12(0.5)) MulModifier(&finalModifier, UQ_4_12(2.0)); break; case ABILITY_SNIPER: if (isCrit) MulModifier(&finalModifier, UQ_4_12(1.5)); break; } // target's abilities switch (abilityDef) { case ABILITY_MULTISCALE: if (BATTLER_MAX_HP(battlerDef)) MulModifier(&finalModifier, UQ_4_12(0.5)); break; case ABILITY_FILTER: case ABILITY_SOLID_ROCK: case ABILITY_PRISM_ARMOR: if (typeEffectivenessModifier >= UQ_4_12(2.0)) MulModifier(&finalModifier, UQ_4_12(0.75)); break; } // target's ally's abilities if (IsBattlerAlive(BATTLE_PARTNER(battlerDef))) { switch (GetBattlerAbility(BATTLE_PARTNER(battlerDef))) { case ABILITY_FRIEND_GUARD: MulModifier(&finalModifier, UQ_4_12(0.75)); break; } } // attacker's hold effect switch (GetBattlerHoldEffect(battlerAtk, TRUE)) { case HOLD_EFFECT_METRONOME: // todo break; case HOLD_EFFECT_EXPERT_BELT: if (typeEffectivenessModifier >= UQ_4_12(2.0)) MulModifier(&finalModifier, UQ_4_12(1.2)); break; case HOLD_EFFECT_LIFE_ORB: MulModifier(&finalModifier, UQ_4_12(1.3)); break; } // target's hold effect switch (GetBattlerHoldEffect(battlerDef, TRUE)) { // berries reducing dmg } if (gBattleMoves[move].flags & FLAG_DMG_MINIMIZE && gStatuses3[battlerDef] & STATUS3_MINIMIZED) MulModifier(&finalModifier, UQ_4_12(2.0)); if (gBattleMoves[move].flags & FLAG_DMG_UNDERGROUND && gStatuses3[battlerDef] & STATUS3_UNDERGROUND) MulModifier(&finalModifier, UQ_4_12(2.0)); if (gBattleMoves[move].flags & FLAG_DMG_UNDERWATER && gStatuses3[battlerDef] & STATUS3_UNDERWATER) MulModifier(&finalModifier, UQ_4_12(2.0)); dmg = ApplyModifier(finalModifier, dmg); if (dmg == 0) dmg = 1; return dmg; } s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor) { s32 dmg; u16 finalModifier, typeEffectivenessModifier; typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, randomFactor); // Don't calculate damage if the move has no effect on target. if (typeEffectivenessModifier == UQ_4_12(0)) return 0; if (fixedBasePower) gBattleMovePower = fixedBasePower; else gBattleMovePower = CalcMoveBasePowerAfterModifiers(move, battlerAtk, battlerDef, moveType); // long dmg basic formula dmg = ((gBattleMons[battlerAtk].level * 2) / 5) + 2; dmg *= gBattleMovePower; dmg *= CalcAttackStat(move, battlerAtk, battlerDef, moveType, isCrit); dmg /= CalcDefenseStat(move, battlerAtk, battlerDef, moveType, isCrit); dmg = (dmg / 50) + 2; // Calculate final modifiers. dmg = CalcFinalDmg(dmg, move, battlerAtk, battlerDef, moveType, typeEffectivenessModifier, isCrit); // Add a random factor. if (randomFactor) { dmg *= 100 - (Random() % 16); dmg /= 100; } if (dmg == 0) dmg = 1; return dmg; } static inline void MulByTypeEffectiveness(u16 *modifier, u16 move, u8 moveType, u8 battlerDef, u8 defType) { u16 mod = sTypeEffectivenessTable[moveType][defType]; if ((moveType == TYPE_FIGHTING || moveType == TYPE_NORMAL) && defType == TYPE_GHOST && gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT) mod = UQ_4_12(1.0); if (moveType == TYPE_PSYCHIC && defType == TYPE_DARK && gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED) mod = UQ_4_12(1.0); if (move == MOVE_FREEZE_DRY && defType == TYPE_WATER) mod = UQ_4_12(2.0); MulModifier(modifier, mod); } static void UpdateMoveResultFlags(u16 modifier) { if (modifier == UQ_4_12(0.0)) { gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE; gMoveResultFlags &= ~(MOVE_RESULT_NOT_VERY_EFFECTIVE | MOVE_RESULT_SUPER_EFFECTIVE); } else if (modifier == UQ_4_12(1.0)) { gMoveResultFlags &= ~(MOVE_RESULT_NOT_VERY_EFFECTIVE | MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_DOESNT_AFFECT_FOE); } else if (modifier > UQ_4_12(1.0)) { gMoveResultFlags |= MOVE_RESULT_SUPER_EFFECTIVE; gMoveResultFlags &= ~(MOVE_RESULT_NOT_VERY_EFFECTIVE | MOVE_RESULT_DOESNT_AFFECT_FOE); } else //if (modifier < UQ_4_12(1.0)) { gMoveResultFlags |= MOVE_RESULT_NOT_VERY_EFFECTIVE; gMoveResultFlags &= ~(MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_DOESNT_AFFECT_FOE); } } u16 CalcTypeEffectivenessMultiplier(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities) { u16 modifier = UQ_4_12(1.0); if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY) { MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type1); if (gBattleMons[battlerDef].type2 != gBattleMons[battlerDef].type1) MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type2); if (moveType == TYPE_GROUND && !IsBattlerGrounded(battlerDef)) { modifier = UQ_4_12(0.0); if (recordAbilities && GetBattlerAbility(battlerDef) == ABILITY_LEVITATE) { gLastUsedAbility = ABILITY_LEVITATE; gMoveResultFlags |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE); gLastLandedMoves[battlerDef] = 0; gBattleCommunication[6] = moveType; RecordAbilityBattle(battlerDef, ABILITY_LEVITATE); } } if (GetBattlerAbility(battlerDef) == ABILITY_WONDER_GUARD && modifier <= UQ_4_12(1.0) && gBattleMoves[move].power) { modifier = UQ_4_12(0.0); if (recordAbilities) { gLastUsedAbility = ABILITY_WONDER_GUARD; gMoveResultFlags |= MOVE_RESULT_MISSED; gLastLandedMoves[battlerDef] = 0; gBattleCommunication[6] = 3; RecordAbilityBattle(battlerDef, ABILITY_WONDER_GUARD); } } } UpdateMoveResultFlags(modifier); return modifier; } u16 CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u8 abilityDef) { u16 modifier = UQ_4_12(1.0); u8 moveType = gBattleMoves[move].type; if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY) { MulByTypeEffectiveness(&modifier, move, moveType, 0, gBaseStats[speciesDef].type1); if (gBaseStats[speciesDef].type2 != gBaseStats[speciesDef].type1) MulByTypeEffectiveness(&modifier, move, moveType, 0, gBaseStats[speciesDef].type2); if (moveType == TYPE_GROUND && abilityDef == ABILITY_LEVITATE && !(gFieldStatuses & STATUS_FIELD_GRAVITY)) modifier = UQ_4_12(0.0); if (abilityDef == ABILITY_WONDER_GUARD && modifier <= UQ_4_12(1.0) && gBattleMoves[move].power) modifier = UQ_4_12(0.0); } UpdateMoveResultFlags(modifier); return modifier; } u16 GetTypeModifier(u8 atkType, u8 defType) { return sTypeEffectivenessTable[atkType][defType]; }