#include "global.h" #include "battle.h" #include "battle_anim.h" #include "battle_arena.h" #include "battle_pyramid.h" #include "battle_util.h" #include "battle_controllers.h" #include "battle_interface.h" #include "battle_setup.h" #include "battle_z_move.h" #include "party_menu.h" #include "pokemon.h" #include "international_string_util.h" #include "item.h" #include "util.h" #include "battle_scripts.h" #include "random.h" #include "text.h" #include "safari_zone.h" #include "sound.h" #include "sprite.h" #include "string_util.h" #include "task.h" #include "test_runner.h" #include "trig.h" #include "window.h" #include "battle_message.h" #include "battle_ai_main.h" #include "battle_ai_util.h" #include "event_data.h" #include "link.h" #include "malloc.h" #include "berry.h" #include "pokedex.h" #include "mail.h" #include "field_weather.h" #include "constants/abilities.h" #include "constants/battle_anim.h" #include "constants/battle_move_effects.h" #include "constants/battle_script_commands.h" #include "constants/battle_string_ids.h" #include "constants/hold_effects.h" #include "constants/items.h" #include "constants/moves.h" #include "constants/songs.h" #include "constants/species.h" #include "constants/trainers.h" #include "constants/weather.h" #include "constants/pokemon.h" extern struct Evolution gEvolutionTable[][EVOS_PER_MON]; /* NOTE: The data and functions in this file up until (but not including) sSoundMovesTable are actually part of battle_main.c. They needed to be moved to this file in order to match the ROM; this is also why sSoundMovesTable's declaration is in the middle of functions instead of at the top of the file with the other declarations. */ static bool32 TryRemoveScreens(u8 battler); static bool32 IsUnnerveAbilityOnOpposingSide(u8 battlerId); static u8 GetFlingPowerFromItemId(u16 itemId); static void SetRandomMultiHitCounter(); static u32 GetBattlerItemHoldEffectParam(u8 battlerId, u16 item); static u16 GetInverseTypeMultiplier(u16 multiplier); static u16 GetSupremeOverlordModifier(u8 battlerId); extern const u8 *const gBattleScriptsForMoveEffects[]; extern const u8 *const gBattlescriptsForRunningByItem[]; extern const u8 *const gBattlescriptsForUsingItem[]; extern const u8 *const gBattlescriptsForSafariActions[]; static const u8 sPkblToEscapeFactor[][3] = { { [B_MSG_MON_CURIOUS] = 0, [B_MSG_MON_ENTHRALLED] = 0, [B_MSG_MON_IGNORED] = 0 },{ [B_MSG_MON_CURIOUS] = 3, [B_MSG_MON_ENTHRALLED] = 5, [B_MSG_MON_IGNORED] = 0 },{ [B_MSG_MON_CURIOUS] = 2, [B_MSG_MON_ENTHRALLED] = 3, [B_MSG_MON_IGNORED] = 0 },{ [B_MSG_MON_CURIOUS] = 1, [B_MSG_MON_ENTHRALLED] = 2, [B_MSG_MON_IGNORED] = 0 },{ [B_MSG_MON_CURIOUS] = 1, [B_MSG_MON_ENTHRALLED] = 1, [B_MSG_MON_IGNORED] = 0 } }; static const u8 sGoNearCounterToCatchFactor[] = {4, 3, 2, 1}; static const u8 sGoNearCounterToEscapeFactor[] = {4, 4, 4, 4}; static const u16 sSkillSwapBannedAbilities[] = { ABILITY_WONDER_GUARD, ABILITY_MULTITYPE, ABILITY_ILLUSION, ABILITY_STANCE_CHANGE, ABILITY_SCHOOLING, ABILITY_COMATOSE, ABILITY_SHIELDS_DOWN, ABILITY_DISGUISE, ABILITY_RKS_SYSTEM, ABILITY_BATTLE_BOND, ABILITY_POWER_CONSTRUCT, ABILITY_NEUTRALIZING_GAS, ABILITY_ICE_FACE, ABILITY_HUNGER_SWITCH, ABILITY_GULP_MISSILE, }; static const u16 sRolePlayBannedAbilities[] = { ABILITY_TRACE, ABILITY_WONDER_GUARD, ABILITY_FORECAST, ABILITY_FLOWER_GIFT, ABILITY_MULTITYPE, ABILITY_ILLUSION, ABILITY_ZEN_MODE, ABILITY_IMPOSTER, ABILITY_STANCE_CHANGE, ABILITY_POWER_OF_ALCHEMY, ABILITY_RECEIVER, ABILITY_SCHOOLING, ABILITY_COMATOSE, ABILITY_SHIELDS_DOWN, ABILITY_DISGUISE, ABILITY_RKS_SYSTEM, ABILITY_BATTLE_BOND, ABILITY_POWER_CONSTRUCT, ABILITY_ICE_FACE, ABILITY_HUNGER_SWITCH, ABILITY_GULP_MISSILE, }; static const u16 sRolePlayBannedAttackerAbilities[] = { ABILITY_MULTITYPE, ABILITY_ZEN_MODE, ABILITY_STANCE_CHANGE, ABILITY_SCHOOLING, ABILITY_COMATOSE, ABILITY_SHIELDS_DOWN, ABILITY_DISGUISE, ABILITY_RKS_SYSTEM, ABILITY_BATTLE_BOND, ABILITY_POWER_CONSTRUCT, ABILITY_ICE_FACE, ABILITY_GULP_MISSILE, }; static const u16 sWorrySeedBannedAbilities[] = { ABILITY_MULTITYPE, ABILITY_STANCE_CHANGE, ABILITY_SCHOOLING, ABILITY_COMATOSE, ABILITY_SHIELDS_DOWN, ABILITY_DISGUISE, ABILITY_RKS_SYSTEM, ABILITY_BATTLE_BOND, ABILITY_POWER_CONSTRUCT, ABILITY_TRUANT, ABILITY_ICE_FACE, ABILITY_GULP_MISSILE, }; static const u16 sGastroAcidBannedAbilities[] = { ABILITY_AS_ONE_ICE_RIDER, ABILITY_AS_ONE_SHADOW_RIDER, ABILITY_BATTLE_BOND, ABILITY_COMATOSE, ABILITY_DISGUISE, ABILITY_GULP_MISSILE, ABILITY_ICE_FACE, ABILITY_MULTITYPE, ABILITY_POWER_CONSTRUCT, ABILITY_RKS_SYSTEM, ABILITY_SCHOOLING, ABILITY_SHIELDS_DOWN, ABILITY_STANCE_CHANGE, ABILITY_ZEN_MODE, }; static const u16 sEntrainmentBannedAttackerAbilities[] = { ABILITY_TRACE, ABILITY_FORECAST, ABILITY_FLOWER_GIFT, ABILITY_ZEN_MODE, ABILITY_ILLUSION, ABILITY_IMPOSTER, ABILITY_POWER_OF_ALCHEMY, ABILITY_RECEIVER, ABILITY_DISGUISE, ABILITY_POWER_CONSTRUCT, ABILITY_NEUTRALIZING_GAS, ABILITY_ICE_FACE, ABILITY_HUNGER_SWITCH, ABILITY_GULP_MISSILE, }; static const u16 sEntrainmentTargetSimpleBeamBannedAbilities[] = { ABILITY_TRUANT, ABILITY_MULTITYPE, ABILITY_STANCE_CHANGE, ABILITY_SCHOOLING, ABILITY_COMATOSE, ABILITY_SHIELDS_DOWN, ABILITY_DISGUISE, ABILITY_RKS_SYSTEM, ABILITY_BATTLE_BOND, ABILITY_ICE_FACE, ABILITY_GULP_MISSILE, }; static u8 CalcBeatUpPower(void) { struct Pokemon *party; u8 basePower; u16 species; if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; // Party slot is incremented by the battle script for Beat Up after this damage calculation species = GetMonData(&party[gBattleStruct->beatUpSlot], MON_DATA_SPECIES); basePower = (gSpeciesInfo[species].baseAttack / 10) + 5; return basePower; } bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move) { u32 ability = GetBattlerAbility(battlerAtk); if (gSideTimers[defSide].followmeTimer == 0 || gBattleMons[gSideTimers[defSide].followmeTarget].hp == 0 || gBattleMoves[move].effect == EFFECT_SNIPE_SHOT || gBattleMoves[move].effect == EFFECT_SKY_DROP || ability == ABILITY_PROPELLER_TAIL || ability == ABILITY_STALWART) return FALSE; if (gSideTimers[defSide].followmePowder && !IsAffectedByPowder(battlerAtk, ability, GetBattlerHoldEffect(battlerAtk, TRUE))) return FALSE; return TRUE; } // Functions void HandleAction_UseMove(void) { u32 i, side, moveType, var = 4; u16 moveTarget; gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; if (gBattleStruct->absentBattlerFlags & gBitTable[gBattlerAttacker] || !IsBattlerAlive(gBattlerAttacker)) { gCurrentActionFuncId = B_ACTION_FINISHED; return; } gIsCriticalHit = FALSE; gBattleStruct->atkCancellerTracker = 0; gMoveResultFlags = 0; gMultiHitCounter = 0; gBattleScripting.savedDmg = 0; gBattleCommunication[MISS_TYPE] = 0; gBattleScripting.savedMoveEffect = 0; gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerAttacker); // choose move if (gProtectStructs[gBattlerAttacker].noValidMoves) { gProtectStructs[gBattlerAttacker].noValidMoves = FALSE; gCurrentMove = gChosenMove = MOVE_STRUGGLE; gHitMarker |= HITMARKER_NO_PPDEDUCT; *(gBattleStruct->moveTarget + gBattlerAttacker) = GetMoveTarget(MOVE_STRUGGLE, NO_TARGET_OVERRIDE); } else if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS || gBattleMons[gBattlerAttacker].status2 & STATUS2_RECHARGE) { gCurrentMove = gChosenMove = gLockedMoves[gBattlerAttacker]; } // encore forces you to use the same move else if (!gBattleStruct->zmove.active && gDisableStructs[gBattlerAttacker].encoredMove != MOVE_NONE && gDisableStructs[gBattlerAttacker].encoredMove == gBattleMons[gBattlerAttacker].moves[gDisableStructs[gBattlerAttacker].encoredMovePos]) { gCurrentMove = gChosenMove = gDisableStructs[gBattlerAttacker].encoredMove; gCurrMovePos = gChosenMovePos = gDisableStructs[gBattlerAttacker].encoredMovePos; *(gBattleStruct->moveTarget + gBattlerAttacker) = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE); } // check if the encored move wasn't overwritten else if (!gBattleStruct->zmove.active && gDisableStructs[gBattlerAttacker].encoredMove != MOVE_NONE && gDisableStructs[gBattlerAttacker].encoredMove != gBattleMons[gBattlerAttacker].moves[gDisableStructs[gBattlerAttacker].encoredMovePos]) { gCurrMovePos = gChosenMovePos = gDisableStructs[gBattlerAttacker].encoredMovePos; gCurrentMove = gChosenMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos]; gDisableStructs[gBattlerAttacker].encoredMove = MOVE_NONE; gDisableStructs[gBattlerAttacker].encoredMovePos = 0; gDisableStructs[gBattlerAttacker].encoreTimer = 0; *(gBattleStruct->moveTarget + gBattlerAttacker) = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE); } else if (gBattleMons[gBattlerAttacker].moves[gCurrMovePos] != gChosenMoveByBattler[gBattlerAttacker]) { gCurrentMove = gChosenMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos]; *(gBattleStruct->moveTarget + gBattlerAttacker) = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE); } else { gCurrentMove = gChosenMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos]; } // check z move used if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] != MOVE_NONE && !IS_MOVE_STATUS(gCurrentMove)) { gCurrentMove = gBattleStruct->zmove.toBeUsed[gBattlerAttacker]; } moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); if (gBattleMons[gBattlerAttacker].hp != 0) { if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) gBattleResults.lastUsedMovePlayer = gCurrentMove; else gBattleResults.lastUsedMoveOpponent = gCurrentMove; } // Set dynamic move type. SetTypeBeforeUsingMove(gChosenMove, gBattlerAttacker); GET_MOVE_TYPE(gChosenMove, moveType); // choose target side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker)); if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove) && moveTarget == MOVE_TARGET_SELECTED && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gSideTimers[side].followmeTarget)) { gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = gSideTimers[side].followmeTarget; // follow me moxie fix } else if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && gSideTimers[side].followmeTimer == 0 && (gBattleMoves[gCurrentMove].power != 0 || (moveTarget != MOVE_TARGET_USER && moveTarget != MOVE_TARGET_ALL_BATTLERS)) && ((GetBattlerAbility(*(gBattleStruct->moveTarget + gBattlerAttacker)) != ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC) || (GetBattlerAbility(*(gBattleStruct->moveTarget + gBattlerAttacker)) != ABILITY_STORM_DRAIN && moveType == TYPE_WATER))) { side = GetBattlerSide(gBattlerAttacker); for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++) { if (side != GetBattlerSide(gActiveBattler) && *(gBattleStruct->moveTarget + gBattlerAttacker) != gActiveBattler && ((GetBattlerAbility(gActiveBattler) == ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC) || (GetBattlerAbility(gActiveBattler) == ABILITY_STORM_DRAIN && moveType == TYPE_WATER)) && GetBattlerTurnOrderNum(gActiveBattler) < var && gBattleMoves[gCurrentMove].effect != EFFECT_SNIPE_SHOT && (GetBattlerAbility(gBattlerAttacker) != ABILITY_PROPELLER_TAIL || GetBattlerAbility(gBattlerAttacker) != ABILITY_STALWART)) { var = GetBattlerTurnOrderNum(gActiveBattler); } } if (var == 4) { if (moveTarget & MOVE_TARGET_RANDOM) { if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) { if (Random() & 1) gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); else gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); } else { if (Random() & 1) gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); else gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); } } else if (moveTarget & MOVE_TARGET_FOES_AND_ALLY) { for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++) { if (gBattlerTarget == gBattlerAttacker) continue; if (IsBattlerAlive(gBattlerTarget)) break; } } else { gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker); } if (!IsBattlerAlive(gBattlerTarget)) { if (GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)) { gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget))); } else { gBattlerTarget = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(gBattlerAttacker))); if (!IsBattlerAlive(gBattlerTarget)) gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget))); } } } else { u16 battlerAbility; gActiveBattler = gBattlerByTurnOrder[var]; battlerAbility = GetBattlerAbility(gActiveBattler); RecordAbilityBattle(gActiveBattler, gBattleMons[gActiveBattler].ability); if (battlerAbility == ABILITY_LIGHTNING_ROD && gCurrentMove != MOVE_TEATIME) gSpecialStatuses[gActiveBattler].lightningRodRedirected = TRUE; else if (battlerAbility == ABILITY_STORM_DRAIN) gSpecialStatuses[gActiveBattler].stormDrainRedirected = TRUE; gBattlerTarget = gActiveBattler; } } else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && moveTarget & MOVE_TARGET_RANDOM) { if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) { if (Random() & 1) gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); else gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); } else { if (Random() & 1) gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); else gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); } if (gAbsentBattlerFlags & gBitTable[gBattlerTarget] && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)) { gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget))); } } else if (moveTarget == MOVE_TARGET_ALLY) { if (IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))) gBattlerTarget = BATTLE_PARTNER(gBattlerAttacker); else gBattlerTarget = gBattlerAttacker; } else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && moveTarget == MOVE_TARGET_FOES_AND_ALLY) { for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++) { if (gBattlerTarget == gBattlerAttacker) continue; if (IsBattlerAlive(gBattlerTarget)) break; } } else { gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker); if (!IsBattlerAlive(gBattlerTarget)) { if (GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)) { gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget))); } else { gBattlerTarget = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(gBattlerAttacker))); if (!IsBattlerAlive(gBattlerTarget)) gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget))); } } } if (gBattleTypeFlags & BATTLE_TYPE_PALACE && gProtectStructs[gBattlerAttacker].palaceUnableToUseMove) { // Battle Palace, select battle script for failure to use move if (gBattleMons[gBattlerAttacker].hp == 0) { gCurrentActionFuncId = B_ACTION_FINISHED; return; } else if (gPalaceSelectionBattleScripts[gBattlerAttacker] != NULL) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_INCAPABLE_OF_POWER; gBattlescriptCurrInstr = gPalaceSelectionBattleScripts[gBattlerAttacker]; gPalaceSelectionBattleScripts[gBattlerAttacker] = NULL; } else { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_INCAPABLE_OF_POWER; gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; } } else { gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]; } if (gBattleTypeFlags & BATTLE_TYPE_ARENA) BattleArena_AddMindPoints(gBattlerAttacker); for (i = 0; i < MAX_BATTLERS_COUNT; i++) { gBattleStruct->hpBefore[i] = gBattleMons[i].hp; gSpecialStatuses[i].emergencyExited = FALSE; } gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } void HandleAction_Switch(void) { gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gActionSelectionCursor[gBattlerAttacker] = 0; gMoveSelectionCursor[gBattlerAttacker] = 0; PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, gBattlerAttacker, *(gBattleStruct->battlerPartyIndexes + gBattlerAttacker)) gBattleScripting.battler = gBattlerAttacker; gBattlescriptCurrInstr = BattleScript_ActionSwitch; gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; if (gBattleResults.playerSwitchesCounter < 255) gBattleResults.playerSwitchesCounter++; UndoFormChange(gBattlerPartyIndexes[gBattlerAttacker], GetBattlerSide(gBattlerAttacker), TRUE); } void HandleAction_UseItem(void) { gActiveBattler = gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; ClearFuryCutterDestinyBondGrudge(gBattlerAttacker); gLastUsedItem = gBattleResources->bufferB[gBattlerAttacker][1] | (gBattleResources->bufferB[gBattlerAttacker][2] << 8); gBattlescriptCurrInstr = gBattlescriptsForUsingItem[ItemId_GetBattleUsage(gLastUsedItem) - 1]; gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } bool8 TryRunFromBattle(u8 battler) { bool8 effect = FALSE; u8 holdEffect; u8 pyramidMultiplier; u8 speedVar; if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY_E_READER) holdEffect = gEnigmaBerries[battler].holdEffect; else holdEffect = ItemId_GetHoldEffect(gBattleMons[battler].item); gPotentialItemEffectBattler = battler; if (holdEffect == HOLD_EFFECT_CAN_ALWAYS_RUN) { gLastUsedItem = gBattleMons[battler].item; gProtectStructs[battler].fleeType = FLEE_ITEM; effect++; } #if B_GHOSTS_ESCAPE >= GEN_6 else if (IS_BATTLER_OF_TYPE(battler, TYPE_GHOST)) { effect++; } #endif else if (GetBattlerAbility(battler) == ABILITY_RUN_AWAY) { if (InBattlePyramid()) { gBattleStruct->runTries++; pyramidMultiplier = GetPyramidRunMultiplier(); speedVar = (gBattleMons[battler].speed * pyramidMultiplier) / (gBattleMons[BATTLE_OPPOSITE(battler)].speed) + (gBattleStruct->runTries * 30); if (speedVar > (Random() & 0xFF)) { gLastUsedAbility = ABILITY_RUN_AWAY; gProtectStructs[battler].fleeType = FLEE_ABILITY; effect++; } } else { gLastUsedAbility = ABILITY_RUN_AWAY; gProtectStructs[battler].fleeType = FLEE_ABILITY; effect++; } } else if (gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_TRAINER_HILL) && gBattleTypeFlags & BATTLE_TYPE_TRAINER) { effect++; } else { u8 runningFromBattler = BATTLE_OPPOSITE(battler); if (!IsBattlerAlive(runningFromBattler)) runningFromBattler |= BIT_FLANK; if (InBattlePyramid()) { pyramidMultiplier = GetPyramidRunMultiplier(); speedVar = (gBattleMons[battler].speed * pyramidMultiplier) / (gBattleMons[runningFromBattler].speed) + (gBattleStruct->runTries * 30); if (speedVar > (Random() & 0xFF)) effect++; } else if (gBattleMons[battler].speed < gBattleMons[runningFromBattler].speed) { speedVar = (gBattleMons[battler].speed * 128) / (gBattleMons[runningFromBattler].speed) + (gBattleStruct->runTries * 30); if (speedVar > (Random() & 0xFF)) effect++; } else // same speed or faster { effect++; } gBattleStruct->runTries++; } if (effect != 0) { gCurrentTurnActionNumber = gBattlersCount; gBattleOutcome = B_OUTCOME_RAN; } return effect; } void HandleAction_Run(void) { gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) { gCurrentTurnActionNumber = gBattlersCount; for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++) { if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) { if (gChosenActionByBattler[gActiveBattler] == B_ACTION_RUN) gBattleOutcome |= B_OUTCOME_LOST; } else { if (gChosenActionByBattler[gActiveBattler] == B_ACTION_RUN) gBattleOutcome |= B_OUTCOME_WON; } } gBattleOutcome |= B_OUTCOME_LINK_BATTLE_RAN; gSaveBlock2Ptr->frontier.disableRecordBattle = TRUE; } else { if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) { if (!TryRunFromBattle(gBattlerAttacker)) // failed to run away { ClearFuryCutterDestinyBondGrudge(gBattlerAttacker); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CANT_ESCAPE_2; gBattlescriptCurrInstr = BattleScript_PrintFailedToRunString; gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } } else { if (!CanBattlerEscape(gBattlerAttacker)) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ATTACKER_CANT_ESCAPE; gBattlescriptCurrInstr = BattleScript_PrintFailedToRunString; gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } else { gCurrentTurnActionNumber = gBattlersCount; gBattleOutcome = B_OUTCOME_MON_FLED; } } } } void HandleAction_WatchesCarefully(void) { gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gBattlescriptCurrInstr = gBattlescriptsForSafariActions[0]; gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } void HandleAction_SafariZoneBallThrow(void) { gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gNumSafariBalls--; gLastUsedItem = ITEM_SAFARI_BALL; gBattlescriptCurrInstr = BattleScript_SafariBallThrow; gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } void HandleAction_ThrowBall(void) { gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gLastUsedItem = gLastThrownBall; RemoveBagItem(gLastUsedItem, 1); gBattlescriptCurrInstr = BattleScript_BallThrow; gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } void HandleAction_ThrowPokeblock(void) { gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gBattleCommunication[MULTISTRING_CHOOSER] = gBattleResources->bufferB[gBattlerAttacker][1] - 1; gLastUsedItem = gBattleResources->bufferB[gBattlerAttacker][2]; if (gBattleResults.pokeblockThrows < 255) gBattleResults.pokeblockThrows++; if (gBattleStruct->safariPkblThrowCounter < 3) gBattleStruct->safariPkblThrowCounter++; if (gBattleStruct->safariEscapeFactor > 1) { // BUG: safariEscapeFactor can become 0 below. This causes the pokeblock throw glitch. #ifdef BUGFIX if (gBattleStruct->safariEscapeFactor <= sPkblToEscapeFactor[gBattleStruct->safariPkblThrowCounter][gBattleCommunication[MULTISTRING_CHOOSER]]) #else if (gBattleStruct->safariEscapeFactor < sPkblToEscapeFactor[gBattleStruct->safariPkblThrowCounter][gBattleCommunication[MULTISTRING_CHOOSER]]) #endif gBattleStruct->safariEscapeFactor = 1; else gBattleStruct->safariEscapeFactor -= sPkblToEscapeFactor[gBattleStruct->safariPkblThrowCounter][gBattleCommunication[MULTISTRING_CHOOSER]]; } gBattlescriptCurrInstr = gBattlescriptsForSafariActions[2]; gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } void HandleAction_GoNear(void) { gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gBattleStruct->safariCatchFactor += sGoNearCounterToCatchFactor[gBattleStruct->safariGoNearCounter]; if (gBattleStruct->safariCatchFactor > 20) gBattleStruct->safariCatchFactor = 20; gBattleStruct->safariEscapeFactor += sGoNearCounterToEscapeFactor[gBattleStruct->safariGoNearCounter]; if (gBattleStruct->safariEscapeFactor > 20) gBattleStruct->safariEscapeFactor = 20; if (gBattleStruct->safariGoNearCounter < 3) { gBattleStruct->safariGoNearCounter++; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CREPT_CLOSER; } else { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CANT_GET_CLOSER; } gBattlescriptCurrInstr = gBattlescriptsForSafariActions[1]; gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } void HandleAction_SafariZoneRun(void) { gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; PlaySE(SE_FLEE); gCurrentTurnActionNumber = gBattlersCount; gBattleOutcome = B_OUTCOME_RAN; } void HandleAction_WallyBallThrow(void) { gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattlerPartyIndexes[gBattlerAttacker]) gBattlescriptCurrInstr = gBattlescriptsForSafariActions[3]; gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; gActionsByTurnOrder[1] = B_ACTION_FINISHED; } void HandleAction_TryFinish(void) { if (!HandleFaintedMonActions()) { gBattleStruct->faintedActionsState = 0; gCurrentActionFuncId = B_ACTION_FINISHED; } } void HandleAction_NothingIsFainted(void) { gCurrentTurnActionNumber++; gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber]; gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED | HITMARKER_NO_PPDEDUCT | HITMARKER_IGNORE_SAFEGUARD | HITMARKER_PASSIVE_DAMAGE | HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT | HITMARKER_CHARGING | HITMARKER_NEVER_SET); } void HandleAction_ActionFinished(void) { #if B_RECALC_TURN_AFTER_ACTIONS >= GEN_8 u32 i, j; bool32 afterYouActive = gSpecialStatuses[gBattlerByTurnOrder[gCurrentTurnActionNumber + 1]].afterYou; #endif *(gBattleStruct->monToSwitchIntoId + gBattlerByTurnOrder[gCurrentTurnActionNumber]) = gSelectedMonPartyId = PARTY_SIZE; gCurrentTurnActionNumber++; gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber]; SpecialStatusesClear(); gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED | HITMARKER_NO_PPDEDUCT | HITMARKER_IGNORE_SAFEGUARD | HITMARKER_PASSIVE_DAMAGE | HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT | HITMARKER_CHARGING | HITMARKER_NEVER_SET | HITMARKER_IGNORE_DISGUISE); gCurrentMove = 0; gBattleMoveDamage = 0; gMoveResultFlags = 0; gBattleScripting.animTurn = 0; gBattleScripting.animTargetsHit = 0; gLastLandedMoves[gBattlerAttacker] = 0; gLastHitByType[gBattlerAttacker] = 0; gBattleStruct->dynamicMoveType = 0; gBattleScripting.moveendState = 0; gBattleScripting.moveendState = 0; gBattleCommunication[3] = 0; gBattleCommunication[4] = 0; gBattleScripting.multihitMoveEffect = 0; gBattleResources->battleScriptsStack->size = 0; #if B_RECALC_TURN_AFTER_ACTIONS >= GEN_8 if (!afterYouActive) { // i starts at `gCurrentTurnActionNumber` because we don't want to recalculate turn order for mon that have already // taken action. It's been previously increased, which we want in order to not recalculate the turn of the mon that just finished its action for (i = gCurrentTurnActionNumber; i < gBattlersCount - 1; i++) { for (j = i + 1; j < gBattlersCount; j++) { u8 battler1 = gBattlerByTurnOrder[i]; u8 battler2 = gBattlerByTurnOrder[j]; if (gProtectStructs[battler1].quash || gProtectStructs[battler2].quash || gProtectStructs[battler1].shellTrap || gProtectStructs[battler2].shellTrap) continue; // We recalculate order only for action of the same priority. If any action other than switch/move has been taken, they should // have been executed before. The only recalculation needed is for moves/switch. Mega evolution is handled in src/battle_main.c/TryChangeOrder if((gActionsByTurnOrder[i] == B_ACTION_USE_MOVE && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE)) { if (GetWhoStrikesFirst(battler1, battler2, FALSE)) SwapTurnOrder(i, j); } else if ((gActionsByTurnOrder[i] == B_ACTION_SWITCH && gActionsByTurnOrder[j] == B_ACTION_SWITCH)) { if (GetWhoStrikesFirst(battler1, battler2, TRUE)) // If the actions chosen are switching, we recalc order but ignoring the moves SwapTurnOrder(i, j); } } } } #endif } static const u8 sMovesNotAffectedByStench[] = { [MOVE_AIR_SLASH] = 1, [MOVE_ASTONISH] = 1, [MOVE_BITE] = 1, [MOVE_BONE_CLUB] = 1, [MOVE_DARK_PULSE] = 1, [MOVE_DOUBLE_IRON_BASH] = 1, [MOVE_DRAGON_RUSH] = 1, [MOVE_EXTRASENSORY] = 1, [MOVE_FAKE_OUT] = 1, [MOVE_FIERY_WRATH] = 1, [MOVE_FIRE_FANG] = 1, [MOVE_FLING] = 1, [MOVE_FLOATY_FALL] = 1, [MOVE_HEADBUTT] = 1, [MOVE_HEART_STAMP] = 1, [MOVE_HYPER_FANG] = 1, [MOVE_ICE_FANG] = 1, [MOVE_ICICLE_CRASH] = 1, [MOVE_IRON_HEAD] = 1, [MOVE_NEEDLE_ARM] = 1, [MOVE_NONE] = 1, [MOVE_ROCK_SLIDE] = 1, [MOVE_ROLLING_KICK] = 1, [MOVE_SECRET_POWER] = 1, [MOVE_SKY_ATTACK] = 1, [MOVE_SNORE] = 1, [MOVE_STEAMROLLER] = 1, [MOVE_STOMP] = 1, [MOVE_THUNDER_FANG] = 1, [MOVE_TWISTER] = 1, [MOVE_WATERFALL] = 1, [MOVE_ZEN_HEADBUTT] = 1, [MOVE_ZING_ZAP] = 1, }; 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, [ABILITY_MIRROR_ARMOR] = 1, [ABILITY_PUNK_ROCK] = 1, [ABILITY_ICE_SCALES] = 1, [ABILITY_ICE_FACE] = 1, [ABILITY_PASTEL_VEIL] = 1, [ABILITY_ARMOR_TAIL] = 1, [ABILITY_EARTH_EATER] = 1, [ABILITY_GOOD_AS_GOLD] = 1, [ABILITY_PURIFYING_SALT] = 1, [ABILITY_WELL_BAKED_BODY] = 1, }; static const u8 sAbilitiesNotTraced[ABILITIES_COUNT] = { [ABILITY_AS_ONE_ICE_RIDER] = 1, [ABILITY_AS_ONE_SHADOW_RIDER] = 1, [ABILITY_BATTLE_BOND] = 1, [ABILITY_COMATOSE] = 1, [ABILITY_DISGUISE] = 1, [ABILITY_FLOWER_GIFT] = 1, [ABILITY_FORECAST] = 1, [ABILITY_GULP_MISSILE] = 1, [ABILITY_HUNGER_SWITCH] = 1, [ABILITY_ICE_FACE] = 1, [ABILITY_ILLUSION] = 1, [ABILITY_IMPOSTER] = 1, [ABILITY_MULTITYPE] = 1, [ABILITY_NEUTRALIZING_GAS] = 1, [ABILITY_NONE] = 1, [ABILITY_POWER_CONSTRUCT] = 1, [ABILITY_POWER_OF_ALCHEMY] = 1, [ABILITY_RECEIVER] = 1, [ABILITY_RKS_SYSTEM] = 1, [ABILITY_SCHOOLING] = 1, [ABILITY_SHIELDS_DOWN] = 1, [ABILITY_STANCE_CHANGE] = 1, [ABILITY_TRACE] = 1, [ABILITY_ZEN_MODE] = 1, }; 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 #if B_STEEL_RESISTANCES >= GEN_6 {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 #else {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(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 #endif {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 #if B_STEEL_RESISTANCES >= GEN_6 {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 #else {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(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(0.5), X(0.5)}, // dark #endif {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_ATTACKER_PARTNER: ret = BATTLE_PARTNER(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 BS_FAINTED_LINK_MULTIPLE_1: ret = gBattlerFainted; break; case BS_ATTACKER_WITH_PARTNER: case BS_FAINTED_LINK_MULTIPLE_2: case BS_ATTACKER_SIDE: case BS_TARGET_SIDE: 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; case BS_ABILITY_BATTLER: ret = gBattlerAbility; break; } return ret; } void PressurePPLose(u8 target, u8 attacker, u16 move) { int moveIndex; if (GetBattlerAbility(target) != ABILITY_PRESSURE) return; for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++) { if (gBattleMons[attacker].moves[moveIndex] == move) break; } if (moveIndex == MAX_MON_MOVES) return; if (gBattleMons[attacker].pp[moveIndex] != 0) gBattleMons[attacker].pp[moveIndex]--; if (MOVE_IS_PERMANENT(attacker, moveIndex)) { gActiveBattler = attacker; BtlController_EmitSetMonData(BUFFER_A, REQUEST_PPMOVE1_BATTLE + moveIndex, 0, 1, &gBattleMons[gActiveBattler].pp[moveIndex]); MarkBattlerForControllerExec(gActiveBattler); } } void PressurePPLoseOnUsingImprison(u8 attacker) { int i, j; int imprisonPos = MAX_MON_MOVES; u8 atkSide = GetBattlerSide(attacker); for (i = 0; i < gBattlersCount; i++) { if (atkSide != GetBattlerSide(i) && GetBattlerAbility(i) == ABILITY_PRESSURE) { for (j = 0; j < MAX_MON_MOVES; j++) { if (gBattleMons[attacker].moves[j] == MOVE_IMPRISON) break; } if (j != MAX_MON_MOVES) { imprisonPos = j; if (gBattleMons[attacker].pp[j] != 0) gBattleMons[attacker].pp[j]--; } } } if (imprisonPos != MAX_MON_MOVES && MOVE_IS_PERMANENT(attacker, imprisonPos)) { gActiveBattler = attacker; BtlController_EmitSetMonData(BUFFER_A, REQUEST_PPMOVE1_BATTLE + imprisonPos, 0, 1, &gBattleMons[gActiveBattler].pp[imprisonPos]); MarkBattlerForControllerExec(gActiveBattler); } } void PressurePPLoseOnUsingPerishSong(u8 attacker) { int i, j; int perishSongPos = MAX_MON_MOVES; for (i = 0; i < gBattlersCount; i++) { if (GetBattlerAbility(i) == ABILITY_PRESSURE && i != attacker) { for (j = 0; j < MAX_MON_MOVES; j++) { if (gBattleMons[attacker].moves[j] == MOVE_PERISH_SONG) break; } if (j != MAX_MON_MOVES) { perishSongPos = j; if (gBattleMons[attacker].pp[j] != 0) gBattleMons[attacker].pp[j]--; } } } if (perishSongPos != MAX_MON_MOVES && MOVE_IS_PERMANENT(attacker, perishSongPos)) { gActiveBattler = attacker; BtlController_EmitSetMonData(BUFFER_A, REQUEST_PPMOVE1_BATTLE + perishSongPos, 0, 1, &gBattleMons[gActiveBattler].pp[perishSongPos]); MarkBattlerForControllerExec(gActiveBattler); } } // Unused static void MarkAllBattlersForControllerExec(void) { int i; if (gBattleTypeFlags & BATTLE_TYPE_LINK) { for (i = 0; i < gBattlersCount; i++) gBattleControllerExecFlags |= gBitTable[i] << (32 - MAX_BATTLERS_COUNT); } else { for (i = 0; i < gBattlersCount; i++) gBattleControllerExecFlags |= gBitTable[i]; } } bool32 IsBattlerMarkedForControllerExec(u8 battlerId) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) return (gBattleControllerExecFlags & (gBitTable[battlerId] << 0x1C)) != 0; else return (gBattleControllerExecFlags & (gBitTable[battlerId])) != 0; } void MarkBattlerForControllerExec(u8 battlerId) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) gBattleControllerExecFlags |= gBitTable[battlerId] << (32 - MAX_BATTLERS_COUNT); else gBattleControllerExecFlags |= gBitTable[battlerId]; } void MarkBattlerReceivedLinkData(u8 battlerId) { s32 i; for (i = 0; i < GetLinkPlayerCount(); i++) gBattleControllerExecFlags |= gBitTable[battlerId] << (i << 2); gBattleControllerExecFlags &= ~((1 << 28) << battlerId); } void CancelMultiTurnMoves(u8 battler) { u8 i; gBattleMons[battler].status2 &= ~(STATUS2_MULTIPLETURNS); gBattleMons[battler].status2 &= ~(STATUS2_LOCK_CONFUSE); gBattleMons[battler].status2 &= ~(STATUS2_UPROAR); gBattleMons[battler].status2 &= ~(STATUS2_BIDE); // Clear battler's semi-invulnerable bits if they are not held by Sky Drop. if (!(gStatuses3[battler] & STATUS3_SKY_DROPPED)) gStatuses3[battler] &= ~(STATUS3_SEMI_INVULNERABLE); // Check to see if this Pokemon was in the middle of using Sky Drop. If so, release the target. if (gBattleStruct->skyDropTargets[battler] != 0xFF && !(gStatuses3[battler] & STATUS3_SKY_DROPPED)) { // Get the target's battler id u8 otherSkyDropper = gBattleStruct->skyDropTargets[battler]; // Clears sky_dropped and on_air statuses gStatuses3[otherSkyDropper] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR); // Makes both attacker and target's sprites visible gSprites[gBattlerSpriteIds[battler]].invisible = FALSE; gSprites[gBattlerSpriteIds[otherSkyDropper]].invisible = FALSE; // If target was sky dropped in the middle of Outrage/Thrash/Petal Dance, // confuse them upon release and display "confused by fatigue" message & animation. // Don't do this if this CancelMultiTurnMoves is caused by falling asleep via Yawn. if (gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE && gBattleStruct->turnEffectsTracker != 24) { gBattleMons[otherSkyDropper].status2 &= ~(STATUS2_LOCK_CONFUSE); // If the target can be confused, confuse them. // Don't use CanBeConfused, can cause issues in edge cases. if (!(GetBattlerAbility(otherSkyDropper) == ABILITY_OWN_TEMPO || gBattleMons[otherSkyDropper].status2 & STATUS2_CONFUSION || IsBattlerTerrainAffected(otherSkyDropper, STATUS_FIELD_MISTY_TERRAIN))) { // Set confused status gBattleMons[otherSkyDropper].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); // If this CancelMultiTurnMoves is occuring due to attackcanceller if (gBattlescriptCurrInstr[0] == 0x0) { gBattleStruct->skyDropTargets[battler] = 0xFE; } // If this CancelMultiTurnMoves is occuring due to VARIOUS_GRAVITY_ON_AIRBORNE_MONS // Reapplying STATUS3_SKY_DROPPED allows for avoiding unecessary messages when Gravity is applied to the target. else if (gBattlescriptCurrInstr[0] == 0x76 && gBattlescriptCurrInstr[2] == 76) { gBattleStruct->skyDropTargets[battler] = 0xFE; gStatuses3[otherSkyDropper] |= STATUS3_SKY_DROPPED; } // If this CancelMultiTurnMoves is occuring due to cancelmultiturnmoves script else if (gBattlescriptCurrInstr[0] == 0x76 && gBattlescriptCurrInstr[2] == 0) { gBattlerAttacker = otherSkyDropper; gBattlescriptCurrInstr = BattleScript_ThrashConfuses - 3; } // If this CancelMultiTurnMoves is occuring due to receiving Sleep/Freeze status else if (gBattleScripting.moveEffect <= PRIMARY_STATUS_MOVE_EFFECT) { gBattlerAttacker = otherSkyDropper; BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_ThrashConfuses - 1; } } } // Clear skyDropTargets data, unless this CancelMultiTurnMoves is caused by Yawn, attackcanceler, or VARIOUS_GRAVITY_ON_AIRBORNE_MONS if (!(gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE) && gBattleStruct->skyDropTargets[battler] < 4) { gBattleStruct->skyDropTargets[battler] = 0xFF; gBattleStruct->skyDropTargets[otherSkyDropper] = 0xFF; } } gDisableStructs[battler].rolloutTimer = 0; gDisableStructs[battler].furyCutterCounter = 0; } bool8 WasUnableToUseMove(u8 battler) { if (gProtectStructs[battler].prlzImmobility || gProtectStructs[battler].usedImprisonedMove || gProtectStructs[battler].loveImmobility || gProtectStructs[battler].usedDisabledMove || gProtectStructs[battler].usedTauntedMove || gProtectStructs[battler].usedGravityPreventedMove || gProtectStructs[battler].usedHealBlockedMove || gProtectStructs[battler].flag2Unknown || gProtectStructs[battler].flinchImmobility || gProtectStructs[battler].confusionSelfDmg || gProtectStructs[battler].powderSelfDmg || gProtectStructs[battler].usedThroatChopPreventedMove) return TRUE; else return FALSE; } void PrepareStringBattle(u16 stringId, u8 battler) { u32 targetSide = GetBattlerSide(gBattlerTarget); u16 battlerAbility = GetBattlerAbility(battler); u16 targetAbility = GetBattlerAbility(gBattlerTarget); // Support for Contrary ability. // If a move attempted to raise stat - print "won't increase". // If a move attempted to lower stat - print "won't decrease". if (stringId == STRINGID_STATSWONTDECREASE && !(gBattleScripting.statChanger & STAT_BUFF_NEGATIVE)) stringId = STRINGID_STATSWONTINCREASE; else if (stringId == STRINGID_STATSWONTINCREASE && gBattleScripting.statChanger & STAT_BUFF_NEGATIVE) stringId = STRINGID_STATSWONTDECREASE; else if (stringId == STRINGID_STATSWONTDECREASE2 && battlerAbility == ABILITY_CONTRARY) stringId = STRINGID_STATSWONTINCREASE2; else if (stringId == STRINGID_STATSWONTINCREASE2 && battlerAbility == ABILITY_CONTRARY) stringId = STRINGID_STATSWONTDECREASE2; // Check Defiant and Competitive stat raise whenever a stat is lowered. else if ((stringId == STRINGID_DEFENDERSSTATFELL || stringId == STRINGID_PKMNCUTSATTACKWITH) && ((targetAbility == ABILITY_DEFIANT && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) || (targetAbility == ABILITY_COMPETITIVE && CompareStat(gBattlerTarget, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))) && gSpecialStatuses[gBattlerTarget].changedStatsBattlerId != BATTLE_PARTNER(gBattlerTarget) && ((gSpecialStatuses[gBattlerTarget].changedStatsBattlerId != gBattlerTarget) || gBattleScripting.stickyWebStatDrop == 1) && !(gBattleScripting.stickyWebStatDrop == 1 && gSideTimers[targetSide].stickyWebBattlerSide == targetSide)) // Sticky Web must have been set by the foe { gBattleScripting.stickyWebStatDrop = 0; gBattlerAbility = gBattlerTarget; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityRaisesDefenderStat; if (targetAbility == ABILITY_DEFIANT) SET_STATCHANGER(STAT_ATK, 2, FALSE); else SET_STATCHANGER(STAT_SPATK, 2, FALSE); } #if B_UPDATED_INTIMIDATE >= GEN_8 else if (stringId == STRINGID_PKMNCUTSATTACKWITH && targetAbility == ABILITY_RATTLED && CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) { gBattlerAbility = gBattlerTarget; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityRaisesDefenderStat; SET_STATCHANGER(STAT_SPEED, 1, FALSE); } #endif gActiveBattler = battler; BtlController_EmitPrintString(BUFFER_A, 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 OpponentSwitchInResetSentPokesToOpponentValue(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 UpdateSentPokesToOpponentValue(u8 battler) { if (GetBattlerSide(battler) == B_SIDE_OPPONENT) { OpponentSwitchInResetSentPokesToOpponentValue(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]; } static bool32 IsGravityPreventingMove(u32 move) { if (!(gFieldStatuses & STATUS_FIELD_GRAVITY)) return FALSE; switch (move) { case MOVE_BOUNCE: case MOVE_FLY: case MOVE_FLYING_PRESS: case MOVE_HIGH_JUMP_KICK: case MOVE_JUMP_KICK: case MOVE_MAGNET_RISE: case MOVE_SKY_DROP: case MOVE_SPLASH: case MOVE_TELEKINESIS: case MOVE_FLOATY_FALL: return TRUE; default: return FALSE; } } bool32 IsHealBlockPreventingMove(u32 battler, u32 move) { if (!(gStatuses3[battler] & STATUS3_HEAL_BLOCK)) return FALSE; switch (gBattleMoves[move].effect) { #if B_HEAL_BLOCKING >= GEN_6 case EFFECT_ABSORB: case EFFECT_STRENGTH_SAP: case EFFECT_DREAM_EATER: #endif case EFFECT_MORNING_SUN: case EFFECT_SYNTHESIS: case EFFECT_MOONLIGHT: case EFFECT_RESTORE_HP: case EFFECT_REST: case EFFECT_ROOST: case EFFECT_HEALING_WISH: case EFFECT_WISH: case EFFECT_HEAL_PULSE: case EFFECT_JUNGLE_HEALING: return TRUE; default: return FALSE; } } static bool32 IsBelchPreventingMove(u32 battler, u32 move) { if (gBattleMoves[move].effect != EFFECT_BELCH) return FALSE; return !(gBattleStruct->ateBerry[battler & BIT_SIDE] & gBitTable[gBattlerPartyIndexes[battler]]); } u8 TrySetCantSelectMoveBattleScript(void) { u32 limitations = 0; u8 moveId = gBattleResources->bufferB[gActiveBattler][2] & ~RET_MEGA_EVOLUTION; u32 move = gBattleMons[gActiveBattler].moves[moveId]; u32 holdEffect = GetBattlerHoldEffect(gActiveBattler, TRUE); u16 *choicedMove = &gBattleStruct->choicedMove[gActiveBattler]; if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[gActiveBattler].disabledMove == move && move != MOVE_NONE) { gBattleScripting.battler = gActiveBattler; gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingDisabledMoveInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingDisabledMove; limitations++; } } if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && move == gLastMoves[gActiveBattler] && move != MOVE_STRUGGLE && (gBattleMons[gActiveBattler].status2 & STATUS2_TORMENT)) { CancelMultiTurnMoves(gActiveBattler); if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingTormentedMoveInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingTormentedMove; limitations++; } } if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[gActiveBattler].tauntTimer != 0 && IS_MOVE_STATUS(move)) { gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveTauntInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveTaunt; limitations++; } } if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[gActiveBattler].throatChopTimer != 0 && gBattleMoves[move].flags & FLAG_SOUND) { gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveThroatChopInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveThroatChop; limitations++; } } if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && GetImprisonedMovesCount(gActiveBattler, move)) { gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingImprisonedMoveInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingImprisonedMove; limitations++; } } if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && IsGravityPreventingMove(move)) { gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveGravityInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveGravity; limitations++; } } if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && IsHealBlockPreventingMove(gActiveBattler, move)) { gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveHealBlockInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveHealBlock; limitations++; } } if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && IsBelchPreventingMove(gActiveBattler, move)) { gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedBelchInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedBelch; limitations++; } } if (move == MOVE_STUFF_CHEEKS && ItemId_GetPocket(gBattleMons[gActiveBattler].item) != POCKET_BERRIES) { gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedStuffCheeksInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedStuffCheeks; limitations++; } } gPotentialItemEffectBattler = gActiveBattler; if (HOLD_EFFECT_CHOICE(holdEffect) && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != move) { gCurrentMove = *choicedMove; gLastUsedItem = gBattleMons[gActiveBattler].item; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveChoiceItemInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveChoiceItem; limitations++; } } else if (holdEffect == HOLD_EFFECT_ASSAULT_VEST && IS_MOVE_STATUS(move) && move != MOVE_ME_FIRST) { gCurrentMove = move; gLastUsedItem = gBattleMons[gActiveBattler].item; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveAssaultVestInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveAssaultVest; limitations++; } } if ((GetBattlerAbility(gActiveBattler) == ABILITY_GORILLA_TACTICS) && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != move) { gCurrentMove = *choicedMove; gLastUsedItem = gBattleMons[gActiveBattler].item; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveGorillaTacticsInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveGorillaTactics; limitations++; } } if (gBattleMons[gActiveBattler].pp[moveId] == 0) { if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingMoveWithNoPP; limitations++; } } if (gBattleMoves[move].effect == EFFECT_PLACEHOLDER) { if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedPlaceholderInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedPlaceholder; limitations++; } } return limitations; } u8 CheckMoveLimitations(u8 battlerId, u8 unusableMoves, u16 check) { u8 holdEffect = GetBattlerHoldEffect(battlerId, TRUE); u16 *choicedMove = &gBattleStruct->choicedMove[battlerId]; s32 i; gPotentialItemEffectBattler = battlerId; for (i = 0; i < MAX_MON_MOVES; i++) { // No move if (check & MOVE_LIMITATION_ZEROMOVE && gBattleMons[battlerId].moves[i] == MOVE_NONE) unusableMoves |= gBitTable[i]; // No PP else if (check & MOVE_LIMITATION_PP && gBattleMons[battlerId].pp[i] == 0) unusableMoves |= gBitTable[i]; // Placeholder else if (check & MOVE_LIMITATION_PLACEHOLDER && gBattleMoves[gBattleMons[battlerId].moves[i]].effect == EFFECT_PLACEHOLDER) unusableMoves |= gBitTable[i]; // Disable else if (check & MOVE_LIMITATION_DISABLED && gBattleMons[battlerId].moves[i] == gDisableStructs[battlerId].disabledMove) unusableMoves |= gBitTable[i]; // Torment else if (check & MOVE_LIMITATION_TORMENTED && gBattleMons[battlerId].moves[i] == gLastMoves[battlerId] && gBattleMons[battlerId].status2 & STATUS2_TORMENT) unusableMoves |= gBitTable[i]; // Taunt else if (check & MOVE_LIMITATION_TAUNT && gDisableStructs[battlerId].tauntTimer && IS_MOVE_STATUS(gBattleMons[battlerId].moves[i])) unusableMoves |= gBitTable[i]; // Imprison else if (check & MOVE_LIMITATION_IMPRISON && GetImprisonedMovesCount(battlerId, gBattleMons[battlerId].moves[i])) unusableMoves |= gBitTable[i]; // Encore else if (check & MOVE_LIMITATION_ENCORE && gDisableStructs[battlerId].encoreTimer && gDisableStructs[battlerId].encoredMove != gBattleMons[battlerId].moves[i]) unusableMoves |= gBitTable[i]; // Choice Items else if (check & MOVE_LIMITATION_CHOICE_ITEM && HOLD_EFFECT_CHOICE(holdEffect) && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != gBattleMons[battlerId].moves[i]) unusableMoves |= gBitTable[i]; // Assault Vest else if (check & MOVE_LIMITATION_ASSAULT_VEST && holdEffect == HOLD_EFFECT_ASSAULT_VEST && IS_MOVE_STATUS(gBattleMons[battlerId].moves[i]) && gBattleMons[battlerId].moves[i] != MOVE_ME_FIRST) unusableMoves |= gBitTable[i]; // Gravity else if (check & MOVE_LIMITATION_GRAVITY && IsGravityPreventingMove(gBattleMons[battlerId].moves[i])) unusableMoves |= gBitTable[i]; // Heal Block else if (check & MOVE_LIMITATION_HEAL_BLOCK && IsHealBlockPreventingMove(battlerId, gBattleMons[battlerId].moves[i])) unusableMoves |= gBitTable[i]; // Belch else if (check & MOVE_LIMITATION_BELCH && IsBelchPreventingMove(battlerId, gBattleMons[battlerId].moves[i])) unusableMoves |= gBitTable[i]; // Throat Chop else if (check & MOVE_LIMITATION_THROAT_CHOP && gDisableStructs[battlerId].throatChopTimer && gBattleMoves[gBattleMons[battlerId].moves[i]].flags & FLAG_SOUND) unusableMoves |= gBitTable[i]; // Stuff Cheeks else if (check & MOVE_LIMITATION_STUFF_CHEEKS && gBattleMons[battlerId].moves[i] == MOVE_STUFF_CHEEKS && ItemId_GetPocket(gBattleMons[gActiveBattler].item) != POCKET_BERRIES) unusableMoves |= gBitTable[i]; // Gorilla Tactics else if (check & MOVE_LIMITATION_CHOICE_ITEM && GetBattlerAbility(battlerId) == ABILITY_GORILLA_TACTICS && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != gBattleMons[battlerId].moves[i]) unusableMoves |= gBitTable[i]; } return unusableMoves; } #define ALL_MOVES_MASK ((1 << MAX_MON_MOVES) - 1) bool8 AreAllMovesUnusable(void) { u8 unusable = CheckMoveLimitations(gActiveBattler, 0, MOVE_LIMITATIONS_ALL); if (unusable == ALL_MOVES_MASK) // All moves are unusable. { gProtectStructs[gActiveBattler].noValidMoves = TRUE; gSelectionBattleScripts[gActiveBattler] = BattleScript_NoMovesLeft; } else { gProtectStructs[gActiveBattler].noValidMoves = FALSE; } return (unusable == ALL_MOVES_MASK); } #undef ALL_MOVES_MASK u8 GetImprisonedMovesCount(u8 battlerId, u16 move) { s32 i; u8 imprisonedMoves = 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 < MAX_MON_MOVES; j++) { if (move == gBattleMons[i].moves[j]) break; } if (j < MAX_MON_MOVES) imprisonedMoves++; } } return imprisonedMoves; } u32 GetBattlerFriendshipScore(u8 battlerId) { u8 side = GetBattlerSide(battlerId); struct Pokemon *party = (side == B_SIDE_PLAYER) ? gPlayerParty : gEnemyParty; u16 species = GetMonData(&party[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES); if (side != B_SIDE_PLAYER) return FRIENDSHIP_NONE; else if (gSpeciesInfo[species].flags & SPECIES_FLAG_MEGA_EVOLUTION || (gBattleTypeFlags & (BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_SECRET_BASE))) return FRIENDSHIP_NONE; return GetMonFriendshipScore(&party[gBattlerPartyIndexes[battlerId]]); } static void TryToRevertMimicry(void) { u32 i; for (i = 0; i < gBattlersCount; i++) { if (GetBattlerAbility(i) == ABILITY_MIMICRY) RESTORE_BATTLER_TYPE(i); } } 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_ION_DELUGE, ENDTURN_FAIRY_LOCK, ENDTURN_RETALIATE, ENDTURN_WEATHER_FORM, ENDTURN_STATUS_HEAL, ENDTURN_FIELD_COUNT, }; static bool32 EndTurnTerrain(u32 terrainFlag, u32 stringTableId) { if (gFieldStatuses & terrainFlag) { if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_PERMANENT) && --gFieldTimers.terrainTimer == 0) { gFieldStatuses &= ~terrainFlag; TryToRevertMimicry(); if (!(terrainFlag & STATUS_FIELD_GRASSY_TERRAIN)) { gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId; BattleScriptExecute(BattleScript_TerrainEnds); } } if (terrainFlag & STATUS_FIELD_GRASSY_TERRAIN) BattleScriptExecute(BattleScript_GrassyTerrainHeals); return TRUE; } return FALSE; } 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 (!gProtectStructs[i].quash && !gProtectStructs[j].quash && GetWhoStrikesFirst(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE)) SwapTurnOrder(i, j); } } gBattleStruct->turnCountersTracker++; gBattleStruct->turnSideTracker = 0; // fall through case ENDTURN_REFLECT: while (gBattleStruct->turnSideTracker < 2) { side = gBattleStruct->turnSideTracker; 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->turnSideTracker++; if (effect != 0) break; } if (effect == 0) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnSideTracker = 0; } break; case ENDTURN_LIGHT_SCREEN: while (gBattleStruct->turnSideTracker < 2) { side = gBattleStruct->turnSideTracker; 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->turnSideTracker++; if (effect != 0) break; } if (effect == 0) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnSideTracker = 0; } break; case ENDTURN_AURORA_VEIL: while (gBattleStruct->turnSideTracker < 2) { side = gBattleStruct->turnSideTracker; 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_SideStatusWoreOff); gBattleCommunication[MULTISTRING_CHOOSER] = side; PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_AURORA_VEIL); effect++; } } gBattleStruct->turnSideTracker++; if (effect != 0) break; } if (!effect) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnSideTracker = 0; } break; case ENDTURN_MIST: while (gBattleStruct->turnSideTracker < 2) { side = gBattleStruct->turnSideTracker; 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->turnSideTracker++; if (effect != 0) break; } if (effect == 0) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnSideTracker = 0; } break; case ENDTURN_SAFEGUARD: while (gBattleStruct->turnSideTracker < 2) { side = gBattleStruct->turnSideTracker; 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->turnSideTracker++; if (effect != 0) break; } if (effect == 0) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnSideTracker = 0; } break; case ENDTURN_LUCKY_CHANT: while (gBattleStruct->turnSideTracker < 2) { side = gBattleStruct->turnSideTracker; 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->turnSideTracker++; if (effect != 0) break; } if (!effect) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnSideTracker = 0; } break; case ENDTURN_TAILWIND: while (gBattleStruct->turnSideTracker < 2) { side = gBattleStruct->turnSideTracker; 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->turnSideTracker++; if (effect != 0) break; } if (!effect) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnSideTracker = 0; } break; case ENDTURN_WISH: while (gBattleStruct->turnSideTracker < gBattlersCount) { gActiveBattler = gBattlerByTurnOrder[gBattleStruct->turnSideTracker]; if (gWishFutureKnock.wishCounter[gActiveBattler] != 0 && --gWishFutureKnock.wishCounter[gActiveBattler] == 0 && gBattleMons[gActiveBattler].hp != 0) { gBattlerTarget = gActiveBattler; BattleScriptExecute(BattleScript_WishComesTrue); effect++; } gBattleStruct->turnSideTracker++; if (effect != 0) break; } if (effect == 0) { gBattleStruct->turnCountersTracker++; gBattleStruct->turnSideTracker = 0; } break; case ENDTURN_RAIN: if (gBattleWeather & B_WEATHER_RAIN) { if (!(gBattleWeather & B_WEATHER_RAIN_PERMANENT) && !(gBattleWeather & B_WEATHER_RAIN_PRIMAL)) { if (--gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~B_WEATHER_RAIN_TEMPORARY; gBattleWeather &= ~B_WEATHER_RAIN_DOWNPOUR; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_RAIN_STOPPED; } else if (gBattleWeather & B_WEATHER_RAIN_DOWNPOUR) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOWNPOUR_CONTINUES; else gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_RAIN_CONTINUES; } else if (gBattleWeather & B_WEATHER_RAIN_DOWNPOUR) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOWNPOUR_CONTINUES; } else { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_RAIN_CONTINUES; } BattleScriptExecute(BattleScript_RainContinuesOrEnds); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_SANDSTORM: if (gBattleWeather & B_WEATHER_SANDSTORM) { if (!(gBattleWeather & B_WEATHER_SANDSTORM_PERMANENT) && --gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~B_WEATHER_SANDSTORM_TEMPORARY; gBattlescriptCurrInstr = BattleScript_SandStormHailEnds; } else { gBattlescriptCurrInstr = BattleScript_DamagingWeatherContinues; } gBattleScripting.animArg1 = B_ANIM_SANDSTORM_CONTINUES; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SANDSTORM; BattleScriptExecute(gBattlescriptCurrInstr); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_SUN: if (gBattleWeather & B_WEATHER_SUN) { if (!(gBattleWeather & B_WEATHER_SUN_PERMANENT) && !(gBattleWeather & B_WEATHER_SUN_PRIMAL) && --gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~B_WEATHER_SUN_TEMPORARY; gBattlescriptCurrInstr = BattleScript_SunlightFaded; } else { gBattlescriptCurrInstr = BattleScript_SunlightContinues; } BattleScriptExecute(gBattlescriptCurrInstr); effect++; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_HAIL: if (gBattleWeather & B_WEATHER_HAIL) { if (!(gBattleWeather & B_WEATHER_HAIL_PERMANENT) && --gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~B_WEATHER_HAIL_TEMPORARY; gBattlescriptCurrInstr = BattleScript_SandStormHailEnds; } else { gBattlescriptCurrInstr = BattleScript_DamagingWeatherContinues; } gBattleScripting.animArg1 = B_ANIM_HAIL_CONTINUES; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_HAIL; 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: effect = EndTurnTerrain(STATUS_FIELD_ELECTRIC_TERRAIN, B_MSG_TERRAINENDS_ELECTRIC); gBattleStruct->turnCountersTracker++; break; case ENDTURN_MISTY_TERRAIN: effect = EndTurnTerrain(STATUS_FIELD_MISTY_TERRAIN, B_MSG_TERRAINENDS_MISTY); gBattleStruct->turnCountersTracker++; break; case ENDTURN_GRASSY_TERRAIN: effect = EndTurnTerrain(STATUS_FIELD_GRASSY_TERRAIN, B_MSG_TERRAINENDS_GRASS); gBattleStruct->turnCountersTracker++; break; case ENDTURN_PSYCHIC_TERRAIN: effect = EndTurnTerrain(STATUS_FIELD_PSYCHIC_TERRAIN, B_MSG_TERRAINENDS_PSYCHIC); gBattleStruct->turnCountersTracker++; break; case ENDTURN_WATER_SPORT: #if B_SPORT_TURNS >= GEN_6 if (gFieldStatuses & STATUS_FIELD_WATERSPORT && --gFieldTimers.waterSportTimer == 0) { gFieldStatuses &= ~STATUS_FIELD_WATERSPORT; BattleScriptExecute(BattleScript_WaterSportEnds); effect++; } #endif gBattleStruct->turnCountersTracker++; break; case ENDTURN_MUD_SPORT: #if B_SPORT_TURNS >= GEN_6 if (gFieldStatuses & STATUS_FIELD_MUDSPORT && --gFieldTimers.mudSportTimer == 0) { gFieldStatuses &= ~STATUS_FIELD_MUDSPORT; BattleScriptExecute(BattleScript_MudSportEnds); effect++; } #endif 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_ION_DELUGE: gFieldStatuses &= ~STATUS_FIELD_ION_DELUGE; gBattleStruct->turnCountersTracker++; break; case ENDTURN_FAIRY_LOCK: if (gFieldStatuses & STATUS_FIELD_FAIRY_LOCK && --gFieldTimers.fairyLockTimer == 0) { gFieldStatuses &= ~STATUS_FIELD_FAIRY_LOCK; } gBattleStruct->turnCountersTracker++; break; case ENDTURN_RETALIATE: if (gSideTimers[B_SIDE_PLAYER].retaliateTimer > 0) gSideTimers[B_SIDE_PLAYER].retaliateTimer--; if (gSideTimers[B_SIDE_OPPONENT].retaliateTimer > 0) gSideTimers[B_SIDE_OPPONENT].retaliateTimer--; gBattleStruct->turnCountersTracker++; break; case ENDTURN_WEATHER_FORM: AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, 0, 0, 0, 0); gBattleStruct->turnCountersTracker++; break; case ENDTURN_STATUS_HEAL: for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) { #if B_AFFECTION_MECHANICS == TRUE if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER && GetBattlerFriendshipScore(gBattlerAttacker) >= FRIENDSHIP_150_TO_199 && (Random() % 100 < 20)) { gBattleCommunication[MULTISTRING_CHOOSER] = 1; BattleScriptExecute(BattleScript_AffectionBasedStatusHeal); break; } #endif } 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_FROSTBITE, ENDTURN_NIGHTMARES, ENDTURN_CURSE, ENDTURN_WRAP, ENDTURN_OCTOLOCK, ENDTURN_UPROAR, ENDTURN_THRASH, ENDTURN_FLINCH, ENDTURN_DISABLE, ENDTURN_ENCORE, ENDTURN_MAGNET_RISE, ENDTURN_TELEKINESIS, ENDTURN_HEALBLOCK, ENDTURN_EMBARGO, ENDTURN_LOCK_ON, ENDTURN_CHARGE, ENDTURN_LASER_FOCUS, ENDTURN_TAUNT, ENDTURN_YAWN, ENDTURN_ITEMS2, ENDTURN_ORBS, ENDTURN_ROOST, ENDTURN_ELECTRIFY, ENDTURN_POWDER, ENDTURN_THROAT_CHOP, ENDTURN_SLOW_START, ENDTURN_PLASMA_FISTS, ENDTURN_CUD_CHEW, ENDTURN_BATTLER_COUNT }; // Ingrain, Leech Seed, Strength Sap and Aqua Ring s32 GetDrainedBigRootHp(u32 battler, s32 hp) { if (GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_BIG_ROOT) hp = (hp * 1300) / 1000; if (hp == 0) hp = 1; return hp * -1; } #define MAGIC_GUARD_CHECK \ if (ability == ABILITY_MAGIC_GUARD) \ {\ RecordAbilityBattle(gActiveBattler, ability);\ gBattleStruct->turnEffectsTracker++;\ break;\ } u8 DoBattlerEndTurnEffects(void) { u32 ability, i, effect = 0; gHitMarker |= (HITMARKER_GRUDGE | HITMARKER_SKIP_DMG_TRACK); while (gBattleStruct->turnEffectsBattlerId < gBattlersCount && gBattleStruct->turnEffectsTracker <= ENDTURN_BATTLER_COUNT) { gActiveBattler = gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->turnEffectsBattlerId]; if (gAbsentBattlerFlags & gBitTable[gActiveBattler]) { gBattleStruct->turnEffectsBattlerId++; continue; } 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 = GetDrainedBigRootHp(gActiveBattler, gBattleMons[gActiveBattler].maxHP / 16); 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 = GetDrainedBigRootHp(gActiveBattler, gBattleMons[gActiveBattler].maxHP / 16); 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(ITEMEFFECT_NORMAL, gActiveBattler, FALSE)) effect++; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_ITEMS2: // item effects again if (ItemBattleEffects(ITEMEFFECT_NORMAL, gActiveBattler, TRUE)) effect++; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_ORBS: if (IsBattlerAlive(gActiveBattler) && ItemBattleEffects(ITEMEFFECT_ORBS, gActiveBattler, FALSE)) 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) { MAGIC_GUARD_CHECK; 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) { MAGIC_GUARD_CHECK; if (ability == ABILITY_POISON_HEAL) { if (!BATTLER_MAX_HP(gActiveBattler) && !(gStatuses3[gActiveBattler] & STATUS3_HEAL_BLOCK)) { 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) { MAGIC_GUARD_CHECK; if (ability == ABILITY_POISON_HEAL) { if (!BATTLER_MAX_HP(gActiveBattler) && !(gStatuses3[gActiveBattler] & STATUS3_HEAL_BLOCK)) { 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_TURN(15)) // not 16 turns gBattleMons[gActiveBattler].status1 += STATUS1_TOXIC_TURN(1); 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) { MAGIC_GUARD_CHECK; #if B_BURN_DAMAGE >= GEN_7 gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; #else gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; #endif if (ability == ABILITY_HEATPROOF) { if (gBattleMoveDamage > (gBattleMoveDamage / 2) + 1) // Record ability if the burn takes less damage than it normally would. RecordAbilityBattle(gActiveBattler, ABILITY_HEATPROOF); gBattleMoveDamage /= 2; } if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptExecute(BattleScript_BurnTurnDmg); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_FROSTBITE: // burn if ((gBattleMons[gActiveBattler].status1 & STATUS1_FROSTBITE) && gBattleMons[gActiveBattler].hp != 0) { MAGIC_GUARD_CHECK; #if B_BURN_DAMAGE >= GEN_7 gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; #else gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; #endif if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptExecute(BattleScript_FrostbiteTurnDmg); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_NIGHTMARES: // spooky nightmares if ((gBattleMons[gActiveBattler].status2 & STATUS2_NIGHTMARE) && gBattleMons[gActiveBattler].hp != 0) { MAGIC_GUARD_CHECK; // 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) { MAGIC_GUARD_CHECK; 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) { if (--gDisableStructs[gActiveBattler].wrapTurns != 0) // damaged by wrap { MAGIC_GUARD_CHECK; gBattleScripting.animArg1 = gBattleStruct->wrappedMove[gActiveBattler]; gBattleScripting.animArg2 = gBattleStruct->wrappedMove[gActiveBattler] >> 8; PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleStruct->wrappedMove[gActiveBattler]); gBattlescriptCurrInstr = BattleScript_WrapTurnDmg; if (GetBattlerHoldEffect(gBattleStruct->wrappedBy[gActiveBattler], TRUE) == HOLD_EFFECT_BINDING_BAND) #if B_BINDING_DAMAGE >= GEN_6 gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 6; else gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; #else gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; else gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; #endif if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; } else // broke free { gBattleMons[gActiveBattler].status2 &= ~STATUS2_WRAPPED; PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleStruct->wrappedMove[gActiveBattler]); gBattlescriptCurrInstr = BattleScript_WrapEnds; } BattleScriptExecute(gBattlescriptCurrInstr); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_OCTOLOCK: { u16 battlerAbility = GetBattlerAbility(gActiveBattler); if (gDisableStructs[gActiveBattler].octolock && !(GetBattlerHoldEffect(gActiveBattler, TRUE) == HOLD_EFFECT_CLEAR_AMULET || battlerAbility == ABILITY_CLEAR_BODY || battlerAbility == ABILITY_FULL_METAL_BODY || battlerAbility == ABILITY_WHITE_SMOKE)) { gBattlerTarget = gActiveBattler; BattleScriptExecute(BattleScript_OctolockEndTurn); 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) && GetBattlerAbility(gBattlerAttacker) != ABILITY_SOUNDPROOF) { gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_SLEEP; gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE; gBattleCommunication[MULTISTRING_CHOOSER] = 1; BattleScriptExecute(BattleScript_MonWokeUpInUproar); gActiveBattler = gBattlerAttacker; BtlController_EmitSetMonData(BUFFER_A, 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 -= STATUS2_UPROAR_TURN(1); // uproar timer goes down if (WasUnableToUseMove(gActiveBattler)) { CancelMultiTurnMoves(gActiveBattler); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS; } else if (gBattleMons[gActiveBattler].status2 & STATUS2_UPROAR) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_CONTINUES; gBattleMons[gActiveBattler].status2 |= STATUS2_MULTIPLETURNS; } else { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS; CancelMultiTurnMoves(gActiveBattler); } BattleScriptExecute(BattleScript_PrintUproarOverTurns); effect = 1; } } if (effect != 2) gBattleStruct->turnEffectsTracker++; break; case ENDTURN_THRASH: // thrash // Don't decrement STATUS2_LOCK_CONFUSE if the target is held by Sky Drop if (gBattleMons[gActiveBattler].status2 & STATUS2_LOCK_CONFUSE && !(gStatuses3[gActiveBattler] & STATUS3_SKY_DROPPED)) { gBattleMons[gActiveBattler].status2 -= STATUS2_LOCK_CONFUSE_TURN(1); 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)) { gBattleScripting.moveEffect = MOVE_EFFECT_CONFUSION | MOVE_EFFECT_AFFECTS_USER; SetMoveEffect(TRUE, 0); if (gBattleMons[gActiveBattler].status2 & STATUS2_CONFUSION) BattleScriptExecute(BattleScript_ThrashConfuses); effect++; } } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_FLINCH: // reset flinch gBattleMons[gActiveBattler].status2 &= ~STATUS2_FLINCHED; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_DISABLE: // disable if (gDisableStructs[gActiveBattler].disableTimer != 0) { for (i = 0; i < MAX_MON_MOVES; i++) { if (gDisableStructs[gActiveBattler].disabledMove == gBattleMons[gActiveBattler].moves[i]) break; } if (i == MAX_MON_MOVES) // pokemon does not have the disabled move anymore { gDisableStructs[gActiveBattler].disabledMove = 0; gDisableStructs[gActiveBattler].disableTimer = 0; } else if (--gDisableStructs[gActiveBattler].disableTimer == 0) // disable ends { gDisableStructs[gActiveBattler].disabledMove = 0; BattleScriptExecute(BattleScript_DisabledNoMore); effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_ENCORE: // encore if (gDisableStructs[gActiveBattler].encoreTimer != 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].encoreTimer = 0; } else if (--gDisableStructs[gActiveBattler].encoreTimer == 0 || gBattleMons[gActiveBattler].pp[gDisableStructs[gActiveBattler].encoredMovePos] == 0) { gDisableStructs[gActiveBattler].encoredMove = 0; gDisableStructs[gActiveBattler].encoreTimer = 0; BattleScriptExecute(BattleScript_EncoredNoMore); effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_LOCK_ON: // lock-on decrement if (gStatuses3[gActiveBattler] & STATUS3_ALWAYS_HITS) gStatuses3[gActiveBattler] -= STATUS3_ALWAYS_HITS_TURN(1); gBattleStruct->turnEffectsTracker++; break; case ENDTURN_CHARGE: // charge if (gDisableStructs[gActiveBattler].chargeTimer && --gDisableStructs[gActiveBattler].chargeTimer == 0) gStatuses3[gActiveBattler] &= ~STATUS3_CHARGED_UP; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_TAUNT: // taunt if (gDisableStructs[gActiveBattler].tauntTimer && --gDisableStructs[gActiveBattler].tauntTimer == 0) { BattleScriptExecute(BattleScript_BufferEndTurn); PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_TAUNT); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_YAWN: // yawn if (gStatuses3[gActiveBattler] & STATUS3_YAWN) { u16 battlerAbility = GetBattlerAbility(gActiveBattler); gStatuses3[gActiveBattler] -= STATUS3_YAWN_TURN(1); if (!(gStatuses3[gActiveBattler] & STATUS3_YAWN) && !(gBattleMons[gActiveBattler].status1 & STATUS1_ANY) && battlerAbility != ABILITY_VITAL_SPIRIT && battlerAbility != ABILITY_INSOMNIA && !UproarWakeUpCheck(gActiveBattler) && !IsLeafGuardProtected(gActiveBattler)) { CancelMultiTurnMoves(gActiveBattler); gEffectBattler = gActiveBattler; if (IsBattlerTerrainAffected(gActiveBattler, STATUS_FIELD_ELECTRIC_TERRAIN)) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINPREVENTS_ELECTRIC; BattleScriptExecute(BattleScript_TerrainPreventsEnd2); } else if (IsBattlerTerrainAffected(gActiveBattler, STATUS_FIELD_MISTY_TERRAIN)) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINPREVENTS_MISTY; BattleScriptExecute(BattleScript_TerrainPreventsEnd2); } else { #if B_SLEEP_TURNS >= GEN_5 gBattleMons[gActiveBattler].status1 |= ((Random() % 3) + 2); #else gBattleMons[gActiveBattler].status1 |= ((Random() % 4) + 3); #endif BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); BattleScriptExecute(BattleScript_YawnMakesAsleep); } effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_LASER_FOCUS: if (gStatuses3[gActiveBattler] & STATUS3_LASER_FOCUS) { if (gDisableStructs[gActiveBattler].laserFocusTimer == 0 || --gDisableStructs[gActiveBattler].laserFocusTimer == 0) gStatuses3[gActiveBattler] &= ~STATUS3_LASER_FOCUS; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_EMBARGO: if (gStatuses3[gActiveBattler] & STATUS3_EMBARGO) { if (gDisableStructs[gActiveBattler].embargoTimer == 0 || --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 == 0) { gStatuses3[gActiveBattler] &= ~STATUS3_MAGNET_RISE; BattleScriptExecute(BattleScript_BufferEndTurn); PREPARE_STRING_BUFFER(gBattleTextBuff1, STRINGID_ELECTROMAGNETISM); effect++; } } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_TELEKINESIS: if (gStatuses3[gActiveBattler] & STATUS3_TELEKINESIS) { if (gDisableStructs[gActiveBattler].telekinesisTimer == 0 || --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 == 0) { gStatuses3[gActiveBattler] &= ~STATUS3_HEAL_BLOCK; BattleScriptExecute(BattleScript_BufferEndTurn); PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_HEAL_BLOCK); 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_ELECTRIFY: gStatuses4[gActiveBattler] &= ~STATUS4_ELECTRIFIED; gBattleStruct->turnEffectsTracker++; case ENDTURN_POWDER: gBattleMons[gActiveBattler].status2 &= ~STATUS2_POWDER; gBattleStruct->turnEffectsTracker++; case ENDTURN_THROAT_CHOP: if (gDisableStructs[gActiveBattler].throatChopTimer && --gDisableStructs[gActiveBattler].throatChopTimer == 0) { BattleScriptExecute(BattleScript_ThroatChopEndTurn); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_SLOW_START: if (gDisableStructs[gActiveBattler].slowStartTimer && --gDisableStructs[gActiveBattler].slowStartTimer == 0 && ability == ABILITY_SLOW_START) { BattleScriptExecute(BattleScript_SlowStartEnds); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_PLASMA_FISTS: gStatuses4[gActiveBattler] &= ~STATUS4_PLASMA_FISTS; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_CUD_CHEW: if (GetBattlerAbility(gActiveBattler) == ABILITY_CUD_CHEW && !gDisableStructs[gActiveBattler].cudChew && ItemId_GetPocket(GetUsedHeldItem(gActiveBattler)) == POCKET_BERRIES) gDisableStructs[gActiveBattler].cudChew = TRUE; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_BATTLER_COUNT: // done gBattleStruct->turnEffectsTracker = 0; gBattleStruct->turnEffectsBattlerId++; break; } if (effect != 0) return effect; } gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_SKIP_DMG_TRACK); return 0; } bool8 HandleWishPerishSongOnTurnEnd(void) { gHitMarker |= (HITMARKER_GRUDGE | HITMARKER_SKIP_DMG_TRACK); 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] = B_MSG_FUTURE_SIGHT; else gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOOM_DESIRE; PREPARE_MOVE_BUFFER(gBattleTextBuff1, gWishFutureKnock.futureSightMove[gActiveBattler]); gBattlerTarget = gActiveBattler; gBattlerAttacker = gWishFutureKnock.futureSightAttacker[gActiveBattler]; gSpecialStatuses[gBattlerTarget].dmg = 0xFFFF; gCurrentMove = gWishFutureKnock.futureSightMove[gActiveBattler]; SetTypeBeforeUsingMove(gCurrentMove, gActiveBattler); BattleScriptExecute(BattleScript_MonTookFutureAttack); if (gWishFutureKnock.futureSightCounter[gActiveBattler] == 0 && gWishFutureKnock.futureSightCounter[BATTLE_PARTNER(gActiveBattler)] == 0) { gSideStatuses[GET_BATTLER_SIDE(gBattlerTarget)] &= ~SIDE_STATUS_FUTUREATTACK; } return TRUE; } } gBattleStruct->wishPerishSongState = 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].perishSongTimer); if (gDisableStructs[gActiveBattler].perishSongTimer == 0) { gStatuses3[gActiveBattler] &= ~STATUS3_PERISH_SONG; gBattleMoveDamage = gBattleMons[gActiveBattler].hp; gBattlescriptCurrInstr = BattleScript_PerishSongTakesLife; } else { gDisableStructs[gActiveBattler].perishSongTimer--; 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->arenaTurnCounter == 2 && gBattleMons[0].hp != 0 && gBattleMons[1].hp != 0) { s32 i; for (i = 0; i < 2; i++) CancelMultiTurnMoves(i); gBattlescriptCurrInstr = BattleScript_ArenaDoJudgment; BattleScriptExecute(BattleScript_ArenaDoJudgment); gBattleStruct->wishPerishSongState++; return TRUE; } break; } gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_SKIP_DMG_TRACK); return FALSE; } #define FAINTED_ACTIONS_MAX_CASE 8 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, PARTY_SIZE, PARTY_SIZE)) gAbsentBattlerFlags &= ~(gBitTable[i]); } // fall through case 1: do { gBattlerFainted = gBattlerTarget = gBattleStruct->faintedActionsBattlerId; if (gBattleMons[gBattleStruct->faintedActionsBattlerId].hp == 0 && !(gBattleStruct->givenExpMons & 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: OpponentSwitchInResetSentPokesToOpponentValue(gBattlerFainted); if (++gBattleStruct->faintedActionsBattlerId == gBattlersCount) gBattleStruct->faintedActionsState = 3; else gBattleStruct->faintedActionsState = 1; #if B_FAINT_SWITCH_IN >= GEN_4 // Don't switch mons until all pokemon performed their actions or the battle's over. if (gBattleOutcome == 0 && !NoAliveMonsForEitherParty() && gCurrentTurnActionNumber != gBattlersCount) { gAbsentBattlerFlags |= gBitTable[gBattlerFainted]; return FALSE; } #endif break; case 3: #if B_FAINT_SWITCH_IN >= GEN_4 // Don't switch mons until all pokemon performed their actions or the battle's over. if (gBattleOutcome == 0 && !NoAliveMonsForEitherParty() && gCurrentTurnActionNumber != gBattlersCount) { return FALSE; } #endif 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: // All battlers switch-in abilities happen here to prevent them happening against an empty field. for (i = 0; i < gBattlersCount; i++) { if (gBattleStruct->switchInAbilityPostponed & gBitTable[i]) { if (DoSwitchInAbilitiesItems(i)) return TRUE; gBattleStruct->switchInAbilityPostponed &= ~(gBitTable[i]); } } gBattleStruct->faintedActionsState++; break; case 7: if (ItemBattleEffects(ITEMEFFECT_NORMAL, 0, TRUE)) return TRUE; gBattleStruct->faintedActionsState++; break; case FAINTED_ACTIONS_MAX_CASE: break; } } while (gBattleStruct->faintedActionsState != FAINTED_ACTIONS_MAX_CASE); return FALSE; } void TryClearRageAndFuryCutter(void) { s32 i; for (i = 0; i < gBattlersCount; i++) { if ((gBattleMons[i].status2 & STATUS2_RAGE) && gChosenMoveByBattler[i] != MOVE_RAGE) gBattleMons[i].status2 &= ~STATUS2_RAGE; if (gDisableStructs[i].furyCutterCounter != 0 && gChosenMoveByBattler[i] != MOVE_FURY_CUTTER) gDisableStructs[i].furyCutterCounter = 0; } } u8 AtkCanceller_UnableToUseMove(void) { u8 effect = 0; s32 *bideDmg = &gBattleScripting.bideDmg; do { switch (gBattleStruct->atkCancellerTracker) { case CANCELLER_FLAGS: // flags clear gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_DESTINY_BOND; gStatuses3[gBattlerAttacker] &= ~STATUS3_GRUDGE; gBattleScripting.tripleKickPower = 0; gBattleStruct->atkCancellerTracker++; break; case CANCELLER_SKY_DROP: // If Pokemon is being held in Sky Drop if (gStatuses3[gBattlerAttacker] & STATUS3_SKY_DROPPED) { gBattlescriptCurrInstr = BattleScript_MoveEnd; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_ASLEEP: // 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] = B_MSG_WOKE_UP_UPROAR; gBattlescriptCurrInstr = BattleScript_MoveUsedWokeUp; effect = 2; } else { u8 toSub; if (GetBattlerAbility(gBattlerAttacker) == 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 (gChosenMove != MOVE_SNORE && gChosenMove != MOVE_SLEEP_TALK) { gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 2; } } else { gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE; BattleScriptPushCursor(); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP; gBattlescriptCurrInstr = BattleScript_MoveUsedWokeUp; effect = 2; } } } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_FROZEN: // check being frozen if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE && !(gBattleMoves[gCurrentMove].flags & FLAG_THAW_USER)) { if (!RandomPercentage(RNG_FROZEN, 20)) { gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen; gHitMarker |= HITMARKER_NO_ATTACKSTRING; } else // unfreeze { gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_FREEZE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MoveUsedUnfroze; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED; } effect = 2; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_TRUANT: // truant if (GetBattlerAbility(gBattlerAttacker) == ABILITY_TRUANT && gDisableStructs[gBattlerAttacker].truantCounter) { CancelMultiTurnMoves(gBattlerAttacker); gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LOAFING; gBattlerAbility = gBattlerAttacker; gBattlescriptCurrInstr = BattleScript_TruantLoafingAround; gMoveResultFlags |= MOVE_RESULT_MISSED; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_RECHARGE: // recharge if (gBattleMons[gBattlerAttacker].status2 & STATUS2_RECHARGE) { gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_RECHARGE; gDisableStructs[gBattlerAttacker].rechargeTimer = 0; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_FLINCH: // flinch if (gBattleMons[gBattlerAttacker].status2 & STATUS2_FLINCHED) { gProtectStructs[gBattlerAttacker].flinchImmobility = TRUE; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_DISABLED: // disabled move if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[gBattlerAttacker].disabledMove == gCurrentMove && gDisableStructs[gBattlerAttacker].disabledMove != MOVE_NONE) { gProtectStructs[gBattlerAttacker].usedDisabledMove = TRUE; gBattleScripting.battler = gBattlerAttacker; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_HEAL_BLOCKED: if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gStatuses3[gBattlerAttacker] & STATUS3_HEAL_BLOCK && IsHealBlockPreventingMove(gBattlerAttacker, gCurrentMove)) { gProtectStructs[gBattlerAttacker].usedHealBlockedMove = TRUE; gBattleScripting.battler = gBattlerAttacker; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedHealBlockPrevents; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_GRAVITY: if (gFieldStatuses & STATUS_FIELD_GRAVITY && IsGravityPreventingMove(gCurrentMove)) { gProtectStructs[gBattlerAttacker].usedGravityPreventedMove = TRUE; gBattleScripting.battler = gBattlerAttacker; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedGravityPrevents; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_TAUNTED: // taunt if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[gBattlerAttacker].tauntTimer && IS_MOVE_STATUS(gCurrentMove)) { gProtectStructs[gBattlerAttacker].usedTauntedMove = TRUE; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_IMPRISONED: // imprisoned if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && GetImprisonedMovesCount(gBattlerAttacker, gCurrentMove)) { gProtectStructs[gBattlerAttacker].usedImprisonedMove = TRUE; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_CONFUSED: // confusion if (gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION) { gBattleMons[gBattlerAttacker].status2 -= STATUS2_CONFUSION_TURN(1); if (gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION) { // confusion dmg #if B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 if (RandomWeighted(RNG_CONFUSION, 2, 1)) #else if (RandomWeighted(RNG_CONFUSION, 1, 1)) #endif { gBattleCommunication[MULTISTRING_CHOOSER] = TRUE; gBattlerTarget = gBattlerAttacker; gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE, FALSE, TRUE); gProtectStructs[gBattlerAttacker].confusionSelfDmg = TRUE; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; } else { gBattleCommunication[MULTISTRING_CHOOSER] = FALSE; BattleScriptPushCursor(); } gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfused; } else // snapped out of confusion { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfusedNoMore; } effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_PARALYSED: // paralysis if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && !RandomPercentage(RNG_PARALYSIS, 75)) { gProtectStructs[gBattlerAttacker].prlzImmobility = TRUE; // This is removed in FRLG and Emerald for some reason //CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_IN_LOVE: // infatuation if (gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) { gBattleScripting.battler = CountTrailingZeroBits((gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) >> 0x10); if (!RandomPercentage(RNG_INFATUATION, 50)) { BattleScriptPushCursor(); } else { BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack); gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gProtectStructs[gBattlerAttacker].loveImmobility = TRUE; CancelMultiTurnMoves(gBattlerAttacker); } gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_BIDE: // bide if (gBattleMons[gBattlerAttacker].status2 & STATUS2_BIDE) { gBattleMons[gBattlerAttacker].status2 -= STATUS2_BIDE_TURN(1); if (gBattleMons[gBattlerAttacker].status2 & STATUS2_BIDE) { gBattlescriptCurrInstr = BattleScript_BideStoringEnergy; } else { // This is removed in FRLG and 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, MOVE_TARGET_SELECTED + 1); gBattlescriptCurrInstr = BattleScript_BideAttack; } else { gBattlescriptCurrInstr = BattleScript_BideNoEnergyToAttack; } } effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_THAW: // move thawing if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE) { if (!(gBattleMoves[gCurrentMove].effect == EFFECT_BURN_UP && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_FIRE))) { gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_FREEZE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MoveUsedUnfroze; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED_BY_MOVE; } effect = 2; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_POWDER_MOVE: if ((gBattleMoves[gCurrentMove].flags & FLAG_POWDER) && (gBattlerAttacker != gBattlerTarget)) { #if B_POWDER_GRASS >= GEN_6 if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_GRASS) || GetBattlerAbility(gBattlerTarget) == ABILITY_OVERCOAT) #else if (GetBattlerAbility(gBattlerTarget) == ABILITY_OVERCOAT) #endif { gBattlerAbility = gBattlerTarget; effect = 1; } else if (GetBattlerHoldEffect(gBattlerTarget, TRUE) == HOLD_EFFECT_SAFETY_GOGGLES) { RecordItemEffectBattle(gBattlerTarget, HOLD_EFFECT_SAFETY_GOGGLES); gLastUsedItem = gBattleMons[gBattlerTarget].item; effect = 1; } if (effect != 0) gBattlescriptCurrInstr = BattleScript_PowderMoveNoEffect; } if (gProtectStructs[gBattlerAttacker].usesBouncedMove) // Edge case for bouncing a powder move against a grass type pokemon. gBattleStruct->atkCancellerTracker = CANCELLER_END; else gBattleStruct->atkCancellerTracker++; break; case CANCELLER_POWDER_STATUS: if (gBattleMons[gBattlerAttacker].status2 & STATUS2_POWDER) { u32 moveType; GET_MOVE_TYPE(gCurrentMove, moveType); if (moveType == TYPE_FIRE) { gProtectStructs[gBattlerAttacker].powderSelfDmg = TRUE; gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 4; gBattlescriptCurrInstr = BattleScript_MoveUsedPowder; effect = 1; } } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_THROAT_CHOP: if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE && gDisableStructs[gBattlerAttacker].throatChopTimer && gBattleMoves[gCurrentMove].flags & FLAG_SOUND) { gProtectStructs[gBattlerAttacker].usedThroatChopPreventedMove = TRUE; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsThroatChopPrevented; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_Z_MOVES: if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] != MOVE_NONE) { //attacker has a queued z move gBattleStruct->zmove.active = TRUE; gBattleStruct->zmove.activeSplit = gBattleStruct->zmove.splits[gBattlerAttacker]; RecordItemEffectBattle(gBattlerAttacker, HOLD_EFFECT_Z_CRYSTAL); gBattleStruct->zmove.used[gBattlerAttacker] = TRUE; if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && IsPartnerMonFromSameTrainer(gBattlerAttacker)) gBattleStruct->zmove.used[BATTLE_PARTNER(gBattlerAttacker)] = TRUE; //if 1v1 double, set partner used flag as well gBattleScripting.battler = gBattlerAttacker; if (gBattleStruct->zmove.activeSplit == SPLIT_STATUS) { gBattleStruct->zmove.effect = gBattleMoves[gBattleStruct->zmove.baseMoves[gBattlerAttacker]].zMoveEffect; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ZMoveActivateStatus; } else { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ZMoveActivateDamaging; } effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_MULTIHIT_MOVES: if (gBattleMoves[gCurrentMove].effect == EFFECT_MULTI_HIT) { u16 ability = gBattleMons[gBattlerAttacker].ability; if (ability == ABILITY_SKILL_LINK) { gMultiHitCounter = 5; } else if (ability == ABILITY_BATTLE_BOND && gCurrentMove == MOVE_WATER_SHURIKEN && gBattleMons[gBattlerAttacker].species == SPECIES_GRENINJA_ASH) { gMultiHitCounter = 3; } else { SetRandomMultiHitCounter(); } PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) } else if (gBattleMoves[gCurrentMove].flags & FLAG_TWO_STRIKES) { gMultiHitCounter = 2; PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) if (gCurrentMove == MOVE_DRAGON_DARTS) { // TODO } } else if (gBattleMoves[gCurrentMove].effect == EFFECT_TRIPLE_KICK || gCurrentMove == MOVE_SURGING_STRIKES) { gMultiHitCounter = 3; PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) } #if B_BEAT_UP >= GEN_5 else if (gBattleMoves[gCurrentMove].effect == EFFECT_BEAT_UP) { struct Pokemon* party; int i; if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_HP) && GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE && !GetMonData(&party[i], MON_DATA_IS_EGG) && !GetMonData(&party[i], MON_DATA_STATUS)) gMultiHitCounter++; } gBattleStruct->beatUpSlot = 0; PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) } #endif gBattleStruct->atkCancellerTracker++; break; case CANCELLER_END: break; } } while (gBattleStruct->atkCancellerTracker != CANCELLER_END && gBattleStruct->atkCancellerTracker != CANCELLER_END2 && effect == 0); if (effect == 2) { gActiveBattler = gBattlerAttacker; BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); } return effect; } // After Protean Activation. u8 AtkCanceller_UnableToUseMove2(void) { u8 effect = 0; do { switch (gBattleStruct->atkCancellerTracker) { case CANCELLER_END: gBattleStruct->atkCancellerTracker++; case CANCELLER_PSYCHIC_TERRAIN: if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN && IsBattlerGrounded(gBattlerTarget) && GetChosenMovePriority(gBattlerAttacker) > 0 && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)) { CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedPsychicTerrainPrevents; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_END2: break; } } while (gBattleStruct->atkCancellerTracker != CANCELLER_END2 && effect == 0); return effect; } bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) { u8 playerId, flankId; struct Pokemon *party; s32 i; if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) return FALSE; if (BATTLE_TWO_VS_ONE_OPPONENT && GetBattlerSide(battler) == B_SIDE_OPPONENT) { flankId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); playerId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); party = gEnemyParty; if (partyIdBattlerOn1 == PARTY_SIZE) partyIdBattlerOn1 = gBattlerPartyIndexes[flankId]; if (partyIdBattlerOn2 == PARTY_SIZE) partyIdBattlerOn2 = gBattlerPartyIndexes[playerId]; for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG && i != partyIdBattlerOn1 && i != partyIdBattlerOn2 && i != *(gBattleStruct->monToSwitchIntoId + flankId) && i != playerId[gBattleStruct->monToSwitchIntoId]) break; } return (i == PARTY_SIZE); } else if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; playerId = ((battler & BIT_FLANK) / 2); for (i = playerId * MULTI_PARTY_SIZE; i < playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG) break; } return (i == playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE); } else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { if (gBattleTypeFlags & BATTLE_TYPE_TOWER_LINK_MULTI) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) { party = gPlayerParty; flankId = GetBattlerMultiplayerId(battler); playerId = GetLinkTrainerFlankId(flankId); } else { party = gEnemyParty; if (battler == 1) playerId = 0; else playerId = 1; } } else { flankId = GetBattlerMultiplayerId(battler); if (GetBattlerSide(battler) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; playerId = GetLinkTrainerFlankId(flankId); } for (i = playerId * MULTI_PARTY_SIZE; i < playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG) break; } return (i == playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE); } else if ((gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && GetBattlerSide(battler) == B_SIDE_OPPONENT) { party = gEnemyParty; if (battler == 1) playerId = 0; else playerId = MULTI_PARTY_SIZE; for (i = playerId; i < playerId + MULTI_PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG) break; } return (i == playerId + 3); } else { if (GetBattlerSide(battler) == B_SIDE_OPPONENT) { flankId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); playerId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); party = gEnemyParty; } else { flankId = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); playerId = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); party = gPlayerParty; } if (partyIdBattlerOn1 == PARTY_SIZE) partyIdBattlerOn1 = gBattlerPartyIndexes[flankId]; if (partyIdBattlerOn2 == PARTY_SIZE) partyIdBattlerOn2 = gBattlerPartyIndexes[playerId]; for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG && i != partyIdBattlerOn1 && i != partyIdBattlerOn2 && i != *(gBattleStruct->monToSwitchIntoId + flankId) && i != playerId[gBattleStruct->monToSwitchIntoId]) break; } return (i == PARTY_SIZE); } } u8 TryWeatherFormChange(u8 battler) { u8 ret = 0; bool32 weatherEffect = WEATHER_HAS_EFFECT; u16 holdEffect = GetBattlerHoldEffect(battler, TRUE); switch (gBattleMons[battler].species) { case SPECIES_CASTFORM: /* Placeholder case SPECIES_CASTFORM_RAINY: case SPECIES_CASTFORM_SNOWY: case SPECIES_CASTFORM_SUNNY:*/ #if B_WEATHER_FORMS >= GEN_5 if (gBattleMons[battler].hp == 0) { ret = 0; // No change } else if (GetBattlerAbility(battler) != ABILITY_FORECAST || !weatherEffect) { if (!IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL)) { SET_BATTLER_TYPE(battler, TYPE_NORMAL); ret = CASTFORM_NORMAL + 1; } else { ret = 0; // No change } } #else if (GetBattlerAbility(battler) != ABILITY_FORECAST || gBattleMons[battler].hp == 0) { ret = 0; // No change } else if (!weatherEffect) { if (!IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL)) { SET_BATTLER_TYPE(battler, TYPE_NORMAL); ret = CASTFORM_NORMAL + 1; } else { ret = 0; // No change } } #endif else if (holdEffect == HOLD_EFFECT_UTILITY_UMBRELLA || (!(gBattleWeather & (B_WEATHER_RAIN | B_WEATHER_SUN | B_WEATHER_HAIL)) && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL))) { SET_BATTLER_TYPE(battler, TYPE_NORMAL); ret = CASTFORM_NORMAL + 1; } else if (gBattleWeather & B_WEATHER_SUN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA && !IS_BATTLER_OF_TYPE(battler, TYPE_FIRE)) { SET_BATTLER_TYPE(battler, TYPE_FIRE); ret = CASTFORM_FIRE + 1; } else if (gBattleWeather & B_WEATHER_RAIN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA && !IS_BATTLER_OF_TYPE(battler, TYPE_WATER)) { SET_BATTLER_TYPE(battler, TYPE_WATER); ret = CASTFORM_WATER + 1; } else if (gBattleWeather & B_WEATHER_HAIL && !IS_BATTLER_OF_TYPE(battler, TYPE_ICE)) { SET_BATTLER_TYPE(battler, TYPE_ICE); ret = CASTFORM_ICE + 1; } break; case SPECIES_CHERRIM: // case SPECIES_CHERRIM_SUNSHINE: if (gBattleMons[battler].hp == 0) ret = 0; // No change #if B_WEATHER_FORMS >= GEN_5 if (GetBattlerAbility(battler) != ABILITY_FLOWER_GIFT) if (gBattleMonForms[battler] != 0) ret = CHERRIM_OVERCAST + 1; else ret = 0; // No change #endif else if (gBattleMonForms[battler] == 0 && weatherEffect && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA && gBattleWeather & B_WEATHER_SUN) ret = CHERRIM_SUNSHINE + 1; else if (gBattleMonForms[battler] != 0 && (!weatherEffect || holdEffect == HOLD_EFFECT_UTILITY_UMBRELLA || !(gBattleWeather & B_WEATHER_SUN))) ret = CHERRIM_OVERCAST + 1; break; } return ret; } static const u16 sWeatherFlagsInfo[][3] = { [ENUM_WEATHER_RAIN] = {B_WEATHER_RAIN_TEMPORARY, B_WEATHER_RAIN_PERMANENT, HOLD_EFFECT_DAMP_ROCK}, [ENUM_WEATHER_RAIN_PRIMAL] = {B_WEATHER_RAIN_PRIMAL, B_WEATHER_RAIN_PRIMAL, HOLD_EFFECT_DAMP_ROCK}, [ENUM_WEATHER_SUN] = {B_WEATHER_SUN_TEMPORARY, B_WEATHER_SUN_PERMANENT, HOLD_EFFECT_HEAT_ROCK}, [ENUM_WEATHER_SUN_PRIMAL] = {B_WEATHER_SUN_PRIMAL, B_WEATHER_SUN_PRIMAL, HOLD_EFFECT_HEAT_ROCK}, [ENUM_WEATHER_SANDSTORM] = {B_WEATHER_SANDSTORM_TEMPORARY, B_WEATHER_SANDSTORM_PERMANENT, HOLD_EFFECT_SMOOTH_ROCK}, [ENUM_WEATHER_HAIL] = {B_WEATHER_HAIL_TEMPORARY, B_WEATHER_HAIL_PERMANENT, HOLD_EFFECT_ICY_ROCK}, [ENUM_WEATHER_STRONG_WINDS] = {B_WEATHER_STRONG_WINDS, B_WEATHER_STRONG_WINDS, HOLD_EFFECT_NONE}, }; static void ShouldChangeFormInWeather(u8 battler) { int i; int side = GetBattlerSide(battler); struct Pokemon *party = (side == B_SIDE_PLAYER) ? gPlayerParty : gEnemyParty; for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_SPECIES) == SPECIES_EISCUE_NOICE_FACE) gBattleStruct->allowedToChangeFormInWeather[i][side] = TRUE; else gBattleStruct->allowedToChangeFormInWeather[i][side] = FALSE; } } bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility) { u16 battlerAbility = GetBattlerAbility(battler); if (gBattleWeather & B_WEATHER_PRIMAL_ANY && battlerAbility != ABILITY_DESOLATE_LAND && battlerAbility != ABILITY_PRIMORDIAL_SEA && battlerAbility != ABILITY_DELTA_STREAM) { return FALSE; } #if B_ABILITY_WEATHER <= GEN_5 else if (viaAbility && !(gBattleWeather & sWeatherFlagsInfo[weatherEnumId][1])) { gBattleWeather = (sWeatherFlagsInfo[weatherEnumId][0] | sWeatherFlagsInfo[weatherEnumId][1]); ShouldChangeFormInWeather(battler); return TRUE; } #endif else if (!(gBattleWeather & (sWeatherFlagsInfo[weatherEnumId][0] | sWeatherFlagsInfo[weatherEnumId][1]))) { gBattleWeather = (sWeatherFlagsInfo[weatherEnumId][0]); if (GetBattlerHoldEffect(battler, TRUE) == sWeatherFlagsInfo[weatherEnumId][2]) gWishFutureKnock.weatherDuration = 8; else gWishFutureKnock.weatherDuration = 5; ShouldChangeFormInWeather(battler); return TRUE; } return FALSE; } static bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag, u8 *timer) { if (!(gFieldStatuses & statusFlag)) { gFieldStatuses &= ~(STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_GRASSY_TERRAIN | STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_PSYCHIC_TERRAIN); gFieldStatuses |= statusFlag; if (GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_TERRAIN_EXTENDER) *timer = 8; else *timer = 5; gBattlerAttacker = gBattleScripting.battler = battler; return TRUE; } return FALSE; } static bool32 ShouldChangeFormHpBased(u32 battler) { // Ability, form >, form <=, hp divided static const u16 forms[][4] = { {ABILITY_ZEN_MODE, SPECIES_DARMANITAN, SPECIES_DARMANITAN_ZEN_MODE, 2}, {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR, SPECIES_MINIOR_CORE_RED, 2}, {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_BLUE, SPECIES_MINIOR_CORE_BLUE, 2}, {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_GREEN, SPECIES_MINIOR_CORE_GREEN, 2}, {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_INDIGO, SPECIES_MINIOR_CORE_INDIGO, 2}, {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_ORANGE, SPECIES_MINIOR_CORE_ORANGE, 2}, {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_VIOLET, SPECIES_MINIOR_CORE_VIOLET, 2}, {ABILITY_SHIELDS_DOWN, SPECIES_MINIOR_METEOR_YELLOW, SPECIES_MINIOR_CORE_YELLOW, 2}, {ABILITY_SCHOOLING, SPECIES_WISHIWASHI_SCHOOL, SPECIES_WISHIWASHI, 4}, {ABILITY_GULP_MISSILE, SPECIES_CRAMORANT, SPECIES_CRAMORANT_GORGING, 2}, {ABILITY_GULP_MISSILE, SPECIES_CRAMORANT, SPECIES_CRAMORANT_GULPING, 1}, {ABILITY_ZEN_MODE, SPECIES_DARMANITAN_GALARIAN, SPECIES_DARMANITAN_ZEN_MODE_GALARIAN, 2}, }; u32 i; u16 battlerAbility = GetBattlerAbility(battler); if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED) return FALSE; for (i = 0; i < ARRAY_COUNT(forms); i++) { if (battlerAbility == forms[i][0]) { if (gBattleMons[battler].species == forms[i][2] && gBattleMons[battler].hp > gBattleMons[battler].maxHP / forms[i][3]) { gBattleMons[battler].species = forms[i][1]; return TRUE; } if (gBattleMons[battler].species == forms[i][1] && gBattleMons[battler].hp <= gBattleMons[battler].maxHP / forms[i][3]) { gBattleMons[battler].species = forms[i][2]; return TRUE; } } } return FALSE; } static u8 ForewarnChooseMove(u32 battler) { struct Forewarn { u8 battlerId; u8 power; u16 moveId; }; u32 i, j, bestId, count; struct Forewarn *data = Alloc(sizeof(struct Forewarn) * MAX_BATTLERS_COUNT * MAX_MON_MOVES); // Put all moves for (count = 0, i = 0; i < MAX_BATTLERS_COUNT; i++) { if (IsBattlerAlive(i) && GetBattlerSide(i) != GetBattlerSide(battler)) { for (j = 0; j < MAX_MON_MOVES; j++) { if (gBattleMons[i].moves[j] == MOVE_NONE) continue; data[count].moveId = gBattleMons[i].moves[j]; data[count].battlerId = i; switch (gBattleMoves[data[count].moveId].effect) { case EFFECT_OHKO: data[count].power = 150; break; case EFFECT_COUNTER: case EFFECT_MIRROR_COAT: case EFFECT_METAL_BURST: data[count].power = 120; break; default: if (gBattleMoves[data[count].moveId].power == 1) data[count].power = 80; else data[count].power = gBattleMoves[data[count].moveId].power; break; } count++; } } } for (bestId = 0, i = 1; i < count; i++) { if (data[i].power > data[bestId].power) bestId = i; else if (data[i].power == data[bestId].power && Random() & 1) bestId = i; } gBattlerTarget = data[bestId].battlerId; PREPARE_MOVE_BUFFER(gBattleTextBuff1, data[bestId].moveId) RecordKnownMove(gBattlerTarget, data[bestId].moveId); Free(data); } bool8 ChangeTypeBasedOnTerrain(u8 battlerId) { u8 battlerType; if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) battlerType = TYPE_ELECTRIC; else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) battlerType = TYPE_GRASS; else if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) battlerType = TYPE_FAIRY; else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) battlerType = TYPE_PSYCHIC; else // failsafe return FALSE; SET_BATTLER_TYPE(battlerId, battlerType); PREPARE_TYPE_BUFFER(gBattleTextBuff1, battlerType); return TRUE; } // Supreme Overlord adds a damage boost for each fainted ally. // The first ally adds a x1.2 boost, and subsequent allies add an extra x0.1 boost each. static u16 GetSupremeOverlordModifier(u8 battlerId) { u32 i; u8 side = GetBattlerSide(battlerId); struct Pokemon *party = (side == B_SIDE_PLAYER) ? gPlayerParty : gEnemyParty; u16 modifier = UQ_4_12(1.0); bool8 appliedFirstBoost = FALSE; for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE && !GetMonData(&party[i], MON_DATA_IS_EGG) && GetMonData(&party[i], MON_DATA_HP) == 0) modifier += (!appliedFirstBoost) ? UQ_4_12(0.2) : UQ_4_12(0.1); appliedFirstBoost = TRUE; } return modifier; } u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 moveArg) { u8 effect = 0; u32 speciesAtk, speciesDef; u32 pidAtk, pidDef; u32 moveType, move; u32 i, j; if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) return 0; if (gBattlerAttacker >= gBattlersCount) gBattlerAttacker = battler; speciesAtk = gBattleMons[gBattlerAttacker].species; pidAtk = gBattleMons[gBattlerAttacker].personality; speciesDef = gBattleMons[gBattlerTarget].species; pidDef = gBattleMons[gBattlerTarget].personality; if (special) gLastUsedAbility = special; else gLastUsedAbility = GetBattlerAbility(battler); if (moveArg) move = moveArg; else move = gCurrentMove; GET_MOVE_TYPE(move, moveType); switch (caseID) { case ABILITYEFFECT_ON_SWITCHIN: // 0 gBattleScripting.battler = battler; switch (gLastUsedAbility) { case ABILITYEFFECT_SWITCH_IN_TERRAIN: if (VarGet(VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY) { u16 terrainFlags = VarGet(VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY; // only works for status flag (1 << 15) gFieldStatuses = terrainFlags | STATUS_FIELD_TERRAIN_PERMANENT; // terrain is permanent switch (VarGet(VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY) { case STATUS_FIELD_ELECTRIC_TERRAIN: gBattleCommunication[MULTISTRING_CHOOSER] = 2; break; case STATUS_FIELD_MISTY_TERRAIN: gBattleCommunication[MULTISTRING_CHOOSER] = 0; break; case STATUS_FIELD_GRASSY_TERRAIN: gBattleCommunication[MULTISTRING_CHOOSER] = 1; break; case STATUS_FIELD_PSYCHIC_TERRAIN: gBattleCommunication[MULTISTRING_CHOOSER] = 3; break; } BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); effect++; } #if B_THUNDERSTORM_TERRAIN == TRUE else if (GetCurrentWeather() == WEATHER_RAIN_THUNDERSTORM && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)) { // overworld weather started rain, so just do electric terrain anim gFieldStatuses = (STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); gBattleCommunication[MULTISTRING_CHOOSER] = 2; BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); effect++; } #endif break; case ABILITYEFFECT_SWITCH_IN_WEATHER: if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) { switch (GetCurrentWeather()) { case WEATHER_RAIN: case WEATHER_RAIN_THUNDERSTORM: case WEATHER_DOWNPOUR: if (!(gBattleWeather & B_WEATHER_RAIN)) { gBattleWeather = (B_WEATHER_RAIN_TEMPORARY | B_WEATHER_RAIN_PERMANENT); gBattleScripting.animArg1 = B_ANIM_RAIN_CONTINUES; effect++; } break; case WEATHER_SANDSTORM: if (!(gBattleWeather & B_WEATHER_SANDSTORM)) { gBattleWeather = B_WEATHER_SANDSTORM; gBattleScripting.animArg1 = B_ANIM_SANDSTORM_CONTINUES; effect++; } break; case WEATHER_DROUGHT: if (!(gBattleWeather & B_WEATHER_SUN)) { gBattleWeather = (B_WEATHER_SUN_PERMANENT | B_WEATHER_SUN_TEMPORARY); gBattleScripting.animArg1 = B_ANIM_SUN_CONTINUES; effect++; } break; } } if (effect != 0) { gBattleCommunication[MULTISTRING_CHOOSER] = GetCurrentWeather(); BattleScriptPushCursorAndCallback(BattleScript_OverworldWeatherStarts); } break; case ABILITY_IMPOSTER: if (IsBattlerAlive(BATTLE_OPPOSITE(battler)) && !(gBattleMons[BATTLE_OPPOSITE(battler)].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE)) && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && !(gBattleStruct->illusion[BATTLE_OPPOSITE(battler)].on) && !(gStatuses3[BATTLE_OPPOSITE(battler)] & STATUS3_SEMI_INVULNERABLE)) { gBattlerAttacker = battler; gBattlerTarget = BATTLE_OPPOSITE(battler); BattleScriptPushCursorAndCallback(BattleScript_ImposterActivates); effect++; } break; case ABILITY_MOLD_BREAKER: if (!gSpecialStatuses[battler].switchInAbilityDone) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_MOLDBREAKER; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_TERAVOLT: if (!gSpecialStatuses[battler].switchInAbilityDone) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_TERAVOLT; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_TURBOBLAZE: if (!gSpecialStatuses[battler].switchInAbilityDone) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_TURBOBLAZE; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_SLOW_START: if (!gSpecialStatuses[battler].switchInAbilityDone) { gDisableStructs[battler].slowStartTimer = 5; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_SLOWSTART; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_UNNERVE: if (!gSpecialStatuses[battler].switchInAbilityDone) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_UNNERVE; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_AS_ONE_ICE_RIDER: case ABILITY_AS_ONE_SHADOW_RIDER: if (!gSpecialStatuses[battler].switchInAbilityDone) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_ASONE; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_ActivateAsOne); effect++; } break; case ABILITY_CURIOUS_MEDICINE: if (!gSpecialStatuses[battler].switchInAbilityDone && IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)) && TryResetBattlerStatChanges(BATTLE_PARTNER(battler))) { u32 i; gEffectBattler = BATTLE_PARTNER(battler); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_CURIOUS_MEDICINE; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_PASTEL_VEIL: if (!gSpecialStatuses[battler].switchInAbilityDone) { gBattlerTarget = battler; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_PASTEL_VEIL; BattleScriptPushCursorAndCallback(BattleScript_PastelVeilActivates); effect++; gSpecialStatuses[battler].switchInAbilityDone = TRUE; } break; case ABILITY_ANTICIPATION: if (!gSpecialStatuses[battler].switchInAbilityDone) { u32 side = GetBattlerSide(battler); for (i = 0; i < MAX_BATTLERS_COUNT; i++) { if (IsBattlerAlive(i) && side != GetBattlerSide(i)) { for (j = 0; j < MAX_MON_MOVES; j++) { move = gBattleMons[i].moves[j]; GET_MOVE_TYPE(move, moveType); if (CalcTypeEffectivenessMultiplier(move, moveType, i, battler, FALSE) >= UQ_4_12(2.0)) { effect++; break; } } } } if (effect != 0) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_ANTICIPATION; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); } } break; case ABILITY_FRISK: if (!gSpecialStatuses[battler].switchInAbilityDone) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_FriskActivates); // Try activate effect++; } return effect; // Note: It returns effect as to not record the ability if Frisk does not activate. case ABILITY_FOREWARN: if (!gSpecialStatuses[battler].switchInAbilityDone) { ForewarnChooseMove(battler); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_FOREWARN; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_DOWNLOAD: if (!gSpecialStatuses[battler].switchInAbilityDone) { u32 statId, opposingBattler; u32 opposingDef = 0, opposingSpDef = 0; opposingBattler = BATTLE_OPPOSITE(battler); for (i = 0; i < 2; opposingBattler ^= BIT_FLANK, i++) { 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 (opposingDef < opposingSpDef) statId = STAT_ATK; else statId = STAT_SPATK; gSpecialStatuses[battler].switchInAbilityDone = TRUE; if (CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) { gBattleMons[battler].statStages[statId]++; SET_STATCHANGER(statId, 1, FALSE); gBattlerAttacker = battler; PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); BattleScriptPushCursorAndCallback(BattleScript_AttackerAbilityStatRaiseEnd3); effect++; } } break; case ABILITY_PRESSURE: if (!gSpecialStatuses[battler].switchInAbilityDone) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_PRESSURE; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_DARK_AURA: if (!gSpecialStatuses[battler].switchInAbilityDone) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_DARKAURA; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_FAIRY_AURA: if (!gSpecialStatuses[battler].switchInAbilityDone) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_FAIRYAURA; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_AURA_BREAK: if (!gSpecialStatuses[battler].switchInAbilityDone) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_AURABREAK; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_COMATOSE: if (!gSpecialStatuses[battler].switchInAbilityDone) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_COMATOSE; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_SCREEN_CLEANER: if (!gSpecialStatuses[battler].switchInAbilityDone && TryRemoveScreens(battler)) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_SCREENCLEANER; gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } break; case ABILITY_DRIZZLE: if (TryChangeBattleWeather(battler, ENUM_WEATHER_RAIN, TRUE)) { BattleScriptPushCursorAndCallback(BattleScript_DrizzleActivates); effect++; } else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT && !gSpecialStatuses[battler].switchInAbilityDone) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_BlockedByPrimalWeatherEnd3); effect++; } break; case ABILITY_SAND_STREAM: if (TryChangeBattleWeather(battler, ENUM_WEATHER_SANDSTORM, TRUE)) { BattleScriptPushCursorAndCallback(BattleScript_SandstreamActivates); effect++; } else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT && !gSpecialStatuses[battler].switchInAbilityDone) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_BlockedByPrimalWeatherEnd3); effect++; } break; case ABILITY_DROUGHT: if (TryChangeBattleWeather(battler, ENUM_WEATHER_SUN, TRUE)) { BattleScriptPushCursorAndCallback(BattleScript_DroughtActivates); effect++; } else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT && !gSpecialStatuses[battler].switchInAbilityDone) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_BlockedByPrimalWeatherEnd3); effect++; } break; case ABILITY_SNOW_WARNING: if (TryChangeBattleWeather(battler, ENUM_WEATHER_HAIL, TRUE)) { BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivates); effect++; } else if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT && !gSpecialStatuses[battler].switchInAbilityDone) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_BlockedByPrimalWeatherEnd3); effect++; } break; case ABILITY_ELECTRIC_SURGE: case ABILITY_HADRON_ENGINE: if (TryChangeBattleTerrain(battler, STATUS_FIELD_ELECTRIC_TERRAIN, &gFieldTimers.terrainTimer)) { BattleScriptPushCursorAndCallback(BattleScript_ElectricSurgeActivates); effect++; } break; case ABILITY_GRASSY_SURGE: if (TryChangeBattleTerrain(battler, STATUS_FIELD_GRASSY_TERRAIN, &gFieldTimers.terrainTimer)) { BattleScriptPushCursorAndCallback(BattleScript_GrassySurgeActivates); effect++; } break; case ABILITY_MISTY_SURGE: if (TryChangeBattleTerrain(battler, STATUS_FIELD_MISTY_TERRAIN, &gFieldTimers.terrainTimer)) { BattleScriptPushCursorAndCallback(BattleScript_MistySurgeActivates); effect++; } break; case ABILITY_PSYCHIC_SURGE: if (TryChangeBattleTerrain(battler, STATUS_FIELD_PSYCHIC_TERRAIN, &gFieldTimers.terrainTimer)) { BattleScriptPushCursorAndCallback(BattleScript_PsychicSurgeActivates); effect++; } break; case ABILITY_INTIMIDATE: if (!gSpecialStatuses[battler].switchInAbilityDone) { gBattlerAttacker = battler; gSpecialStatuses[battler].switchInAbilityDone = TRUE; SET_STATCHANGER(STAT_ATK, 1, TRUE); BattleScriptPushCursorAndCallback(BattleScript_IntimidateActivates); effect++; } break; case ABILITY_TRACE: if (!(gSpecialStatuses[battler].traced)) { gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_TRACED; gSpecialStatuses[battler].traced = TRUE; } break; case ABILITY_CLOUD_NINE: case ABILITY_AIR_LOCK: if (!gSpecialStatuses[battler].switchInAbilityDone) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_AnnounceAirLockCloudNine); effect++; } break; case ABILITY_SCHOOLING: if (gBattleMons[battler].level < 20) break; case ABILITY_SHIELDS_DOWN: if (ShouldChangeFormHpBased(battler)) { BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3); effect++; } break; case ABILITY_INTREPID_SWORD: if (!gSpecialStatuses[battler].switchInAbilityDone && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) { gBattlerAttacker = battler; gSpecialStatuses[battler].switchInAbilityDone = TRUE; SET_STATCHANGER(STAT_ATK, 1, FALSE); BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); effect++; } break; case ABILITY_DAUNTLESS_SHIELD: if (!gSpecialStatuses[battler].switchInAbilityDone && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)) { gBattlerAttacker = battler; gSpecialStatuses[battler].switchInAbilityDone = TRUE; SET_STATCHANGER(STAT_DEF, 1, FALSE); BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn); effect++; } break; case ABILITY_DESOLATE_LAND: if (TryChangeBattleWeather(battler, ENUM_WEATHER_SUN_PRIMAL, TRUE)) { BattleScriptPushCursorAndCallback(BattleScript_DesolateLandActivates); effect++; } break; case ABILITY_PRIMORDIAL_SEA: if (TryChangeBattleWeather(battler, ENUM_WEATHER_RAIN_PRIMAL, TRUE)) { BattleScriptPushCursorAndCallback(BattleScript_PrimordialSeaActivates); effect++; } break; case ABILITY_DELTA_STREAM: if (TryChangeBattleWeather(battler, ENUM_WEATHER_STRONG_WINDS, TRUE)) { BattleScriptPushCursorAndCallback(BattleScript_DeltaStreamActivates); effect++; } break; case ABILITY_VESSEL_OF_RUIN: if (!gSpecialStatuses[battler].switchInAbilityDone) { PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPATK); gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_RuinAbilityActivates); effect++; } break; case ABILITY_SWORD_OF_RUIN: if (!gSpecialStatuses[battler].switchInAbilityDone) { PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_DEF); gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_RuinAbilityActivates); effect++; } break; case ABILITY_TABLETS_OF_RUIN: if (!gSpecialStatuses[battler].switchInAbilityDone) { PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK); gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_RuinAbilityActivates); effect++; } break; case ABILITY_BEADS_OF_RUIN: if (!gSpecialStatuses[battler].switchInAbilityDone) { PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPDEF); gSpecialStatuses[battler].switchInAbilityDone = TRUE; BattleScriptPushCursorAndCallback(BattleScript_RuinAbilityActivates); effect++; } break; case ABILITY_ORICHALCUM_PULSE: if (TryChangeBattleWeather(battler, ENUM_WEATHER_SUN, TRUE)) { BattleScriptPushCursorAndCallback(BattleScript_DroughtActivates); effect++; } break; case ABILITY_SUPREME_OVERLORD: if (!gSpecialStatuses[battler].switchInAbilityDone && CountUsablePartyMons(battler) < PARTY_SIZE) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; gBattleStruct->supremeOverlordModifier[battler] = GetSupremeOverlordModifier(battler); BattleScriptPushCursorAndCallback(BattleScript_SupremeOverlordActivates); effect++; } break; case ABILITY_COSTAR: if (!gSpecialStatuses[battler].switchInAbilityDone && IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)) && CountBattlerStatIncreases(BATTLE_PARTNER(battler), FALSE)) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; for (i = 0; i < NUM_BATTLE_STATS; i++) gBattleMons[battler].statStages[i] = gBattleMons[BATTLE_PARTNER(battler)].statStages[i]; gBattlerTarget = BATTLE_PARTNER(battler); BattleScriptPushCursorAndCallback(BattleScript_CostarActivates); effect++; } break; #if B_WEATHER_FORMS < GEN_5 default: if (gBattleMons[battler].species == SPECIES_CHERRIM) goto TRY_WEATHER_FORM; break; #endif } break; case ABILITYEFFECT_ENDTURN: // 1 if (gBattleMons[battler].hp != 0) { gBattlerAttacker = battler; switch (gLastUsedAbility) { case ABILITY_HARVEST: if ((IsBattlerWeatherAffected(battler, B_WEATHER_SUN) || Random() % 2 == 0) && gBattleMons[battler].item == ITEM_NONE && gBattleStruct->changedItems[battler] == ITEM_NONE // Will not inherit an item && ItemId_GetPocket(GetUsedHeldItem(battler)) == POCKET_BERRIES) { gLastUsedItem = GetUsedHeldItem(battler); BattleScriptPushCursorAndCallback(BattleScript_HarvestActivates); effect++; } break; case ABILITY_DRY_SKIN: if (IsBattlerWeatherAffected(battler, B_WEATHER_SUN)) goto SOLAR_POWER_HP_DROP; // Dry Skin works similarly to Rain Dish in Rain case ABILITY_RAIN_DISH: if (IsBattlerWeatherAffected(battler, B_WEATHER_RAIN) && !BATTLER_MAX_HP(battler) && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)) { BattleScriptPushCursorAndCallback(BattleScript_RainDishActivates); gBattleMoveDamage = gBattleMons[battler].maxHP / (gLastUsedAbility == ABILITY_RAIN_DISH ? 16 : 8); if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; effect++; } break; case ABILITY_HYDRATION: if (IsBattlerWeatherAffected(battler, B_WEATHER_RAIN) && gBattleMons[battler].status1 & STATUS1_ANY) { goto ABILITY_HEAL_MON_STATUS; } break; case ABILITY_SHED_SKIN: if ((gBattleMons[battler].status1 & STATUS1_ANY) && (Random() % 3) == 0) { ABILITY_HEAL_MON_STATUS: 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; gBattleScripting.battler = gActiveBattler = battler; BattleScriptPushCursorAndCallback(BattleScript_ShedSkinActivates); BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); MarkBattlerForControllerExec(gActiveBattler); effect++; } break; case ABILITY_SPEED_BOOST: if (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) && gDisableStructs[battler].isFirstTurn != 2) { gBattleMons[battler].statStages[STAT_SPEED]++; gBattleScripting.animArg1 = 14 + STAT_SPEED; gBattleScripting.animArg2 = 0; BattleScriptPushCursorAndCallback(BattleScript_SpeedBoostActivates); gBattleScripting.battler = battler; effect++; } break; case ABILITY_MOODY: if (gDisableStructs[battler].isFirstTurn != 2) { u32 validToRaise = 0, validToLower = 0; #if B_MOODY_ACC_EVASION < GEN_8 u32 statsNum = NUM_BATTLE_STATS; #else u32 statsNum = NUM_STATS; #endif for (i = STAT_ATK; i < statsNum; i++) { if (CompareStat(battler, i, MIN_STAT_STAGE, CMP_GREATER_THAN)) validToLower |= gBitTable[i]; if (CompareStat(battler, i, MAX_STAT_STAGE, CMP_LESS_THAN)) validToRaise |= gBitTable[i]; } if (validToLower != 0 || validToRaise != 0) // Can lower one stat, or can raise one stat { gBattleScripting.statChanger = gBattleScripting.savedStatChanger = 0; // for raising and lowering stat respectively if (validToRaise != 0) // Find stat to raise { do { i = (Random() % statsNum) + STAT_ATK; } while (!(validToRaise & gBitTable[i])); SET_STATCHANGER(i, 2, FALSE); validToLower &= ~(gBitTable[i]); // Can't lower the same stat as raising. } if (validToLower != 0) // Find stat to lower { do { i = (Random() % statsNum) + STAT_ATK; } while (!(validToLower & gBitTable[i])); SET_STATCHANGER2(gBattleScripting.savedStatChanger, i, 1, TRUE); } BattleScriptPushCursorAndCallback(BattleScript_MoodyActivates); effect++; } } break; case ABILITY_TRUANT: gDisableStructs[gBattlerAttacker].truantCounter ^= 1; break; case ABILITY_BAD_DREAMS: BattleScriptPushCursorAndCallback(BattleScript_BadDreamsActivates); effect++; break; SOLAR_POWER_HP_DROP: case ABILITY_SOLAR_POWER: if (IsBattlerWeatherAffected(battler, B_WEATHER_SUN)) { BattleScriptPushCursorAndCallback(BattleScript_SolarPowerActivates); gBattleMoveDamage = gBattleMons[battler].maxHP / 8; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; effect++; } break; case ABILITY_HEALER: gBattleScripting.battler = BATTLE_PARTNER(battler); if (IsBattlerAlive(gBattleScripting.battler) && gBattleMons[gBattleScripting.battler].status1 & STATUS1_ANY && (Random() % 100) < 30) { BattleScriptPushCursorAndCallback(BattleScript_HealerActivates); effect++; } break; case ABILITY_SCHOOLING: if (gBattleMons[battler].level < 20) break; case ABILITY_ZEN_MODE: case ABILITY_SHIELDS_DOWN: if ((effect = ShouldChangeFormHpBased(battler))) BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3); break; case ABILITY_POWER_CONSTRUCT: if ((gBattleMons[battler].species == SPECIES_ZYGARDE || gBattleMons[battler].species == SPECIES_ZYGARDE_10) && gBattleMons[battler].hp <= gBattleMons[battler].maxHP / 2) { gBattleStruct->changedSpecies[gBattlerPartyIndexes[battler]] = gBattleMons[battler].species; gBattleMons[battler].species = SPECIES_ZYGARDE_COMPLETE; BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3); effect++; } break; case ABILITY_BALL_FETCH: if (gBattleMons[battler].item == ITEM_NONE && gBattleResults.catchAttempts[gLastUsedBall - ITEM_ULTRA_BALL] >= 1 && !gHasFetchedBall) { gBattleScripting.battler = battler; BtlController_EmitSetMonData(BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, 2, &gLastUsedBall); MarkBattlerForControllerExec(battler); gHasFetchedBall = TRUE; gLastUsedItem = gLastUsedBall; BattleScriptPushCursorAndCallback(BattleScript_BallFetch); effect++; } break; case ABILITY_HUNGER_SWITCH: if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)) { if (gBattleMons[battler].species == SPECIES_MORPEKO) { gBattleMons[battler].species = SPECIES_MORPEKO_HANGRY; BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3NoPopup); } else if (gBattleMons[battler].species == SPECIES_MORPEKO_HANGRY) { gBattleMons[battler].species = SPECIES_MORPEKO; BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3NoPopup); } effect++; } break; case ABILITY_CUD_CHEW: if (ItemId_GetPocket(GetUsedHeldItem(battler)) == POCKET_BERRIES && gDisableStructs[gActiveBattler].cudChew == TRUE) { gLastUsedItem = gBattleStruct->usedHeldItems[battler][GetBattlerSide(battler)]; gBattleStruct->usedHeldItems[battler][GetBattlerSide(battler)] = ITEM_NONE; BattleScriptPushCursorAndCallback(BattleScript_CudChewActivates); effect++; } break; } } break; case ABILITYEFFECT_MOVES_BLOCK: // 2 { u16 moveTarget = GetBattlerMoveTargetType(battler, move); u16 battlerAbility = GetBattlerAbility(battler); u16 targetAbility = GetBattlerAbility(gBattlerTarget); if ((gLastUsedAbility == ABILITY_SOUNDPROOF && gBattleMoves[move].flags & FLAG_SOUND && !(moveTarget & MOVE_TARGET_USER)) || (gLastUsedAbility == ABILITY_BULLETPROOF && gBattleMoves[move].flags & FLAG_BALLISTIC)) { if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS) gHitMarker |= HITMARKER_NO_PPDEDUCT; gBattlescriptCurrInstr = BattleScript_SoundproofProtected; effect = 1; } else if ((gLastUsedAbility == ABILITY_DAZZLING || gLastUsedAbility == ABILITY_QUEENLY_MAJESTY || gLastUsedAbility == ABILITY_ARMOR_TAIL || IsBattlerAlive(battler ^= BIT_FLANK)) && (battlerAbility == ABILITY_DAZZLING || battlerAbility == ABILITY_QUEENLY_MAJESTY || battlerAbility == ABILITY_ARMOR_TAIL) && GetChosenMovePriority(gBattlerAttacker) > 0 && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(battler)) { if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS) gHitMarker |= HITMARKER_NO_PPDEDUCT; gBattlescriptCurrInstr = BattleScript_DazzlingProtected; effect = 1; } else if (BlocksPrankster(move, gBattlerAttacker, gBattlerTarget, TRUE) && !(IS_MOVE_STATUS(move) && targetAbility == ABILITY_MAGIC_BOUNCE)) { if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE) || !(moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) CancelMultiTurnMoves(gBattlerAttacker); // Don't cancel moves that can hit two targets bc one target might not be protected gBattleScripting.battler = gBattlerAbility = gBattlerTarget; gBattlescriptCurrInstr = BattleScript_DarkTypePreventsPrankster; effect = 1; } else if (GetBattlerAbility(gBattlerTarget) == ABILITY_GOOD_AS_GOLD && IS_MOVE_STATUS(gCurrentMove) && !(moveTarget & MOVE_TARGET_USER) && !(moveTarget & MOVE_TARGET_OPPONENTS_FIELD) && !(moveTarget & MOVE_TARGET_ALL_BATTLERS)) { gBattlescriptCurrInstr = BattleScript_GoodAsGoldActivates; effect = 1; } else if (gLastUsedAbility == ABILITY_ICE_FACE && IS_MOVE_PHYSICAL(move) && gBattleMons[gBattlerTarget].species == SPECIES_EISCUE) { gBattleMons[gBattlerTarget].species = SPECIES_EISCUE_NOICE_FACE; if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS) gHitMarker |= HITMARKER_NO_PPDEDUCT; gBattleScripting.battler = gBattlerTarget; // For STRINGID_PKMNTRANSFORMED gBattlescriptCurrInstr = BattleScript_IceFaceNullsDamage; effect = 1; } break; } case ABILITYEFFECT_ABSORBING: // 3 if (move != MOVE_NONE) { u8 statId; u8 statAmount = 1; 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 (moveType == TYPE_GRASS) effect = 2, statId = STAT_ATK; break; case ABILITY_FLASH_FIRE: if (moveType == TYPE_FIRE #if B_FLASH_FIRE_FROZEN <= GEN_4 && !(gBattleMons[battler].status1 & STATUS1_FREEZE) #endif ) { if (!(gBattleResources->flags->flags[battler] & RESOURCE_FLAG_FLASH_FIRE)) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FLASH_FIRE_BOOST; if (gProtectStructs[gBattlerAttacker].notFirstStrike) gBattlescriptCurrInstr = BattleScript_FlashFireBoost; else gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss; gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_FLASH_FIRE; effect = 3; } else { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FLASH_FIRE_NO_BOOST; if (gProtectStructs[gBattlerAttacker].notFirstStrike) gBattlescriptCurrInstr = BattleScript_FlashFireBoost; else gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss; effect = 3; } } break; case ABILITY_WELL_BAKED_BODY: if (moveType == TYPE_FIRE) effect = 2, statId = STAT_DEF, statAmount = 2; break; case ABILITY_WIND_RIDER: if (gBattleMoves[gCurrentMove].flags & FLAG_WIND_MOVE && !(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove) & MOVE_TARGET_USER)) effect = 2, statId = STAT_ATK; break; case ABILITY_EARTH_EATER: if (moveType == TYPE_GROUND) effect = 1; break; } if (effect == 1) // Drain Hp ability. { #if B_HEAL_BLOCKING >= GEN_5 if (BATTLER_MAX_HP(battler) || gStatuses3[battler] & STATUS3_HEAL_BLOCK) #else if (BATTLER_MAX_HP(battler)) #endif { 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 (!CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) { 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, statAmount, FALSE); #if B_ABSORBING_ABILITY_STRING < GEN_5 gBattleMons[battler].statStages[statId]++; PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); #endif } } } break; case ABILITYEFFECT_MOVE_END: // Think contact abilities. switch (gLastUsedAbility) { case ABILITY_JUSTIFIED: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && moveType == TYPE_DARK && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) { gEffectBattler = battler; SET_STATCHANGER(STAT_ATK, 1, FALSE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TargetAbilityStatRaiseRet; effect++; } break; case ABILITY_RATTLED: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && (moveType == TYPE_DARK || moveType == TYPE_BUG || moveType == TYPE_GHOST) && CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) { gEffectBattler = battler; SET_STATCHANGER(STAT_SPEED, 1, FALSE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TargetAbilityStatRaiseRet; effect++; } break; case ABILITY_WATER_COMPACTION: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && moveType == TYPE_WATER && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)) { gEffectBattler = battler; SET_STATCHANGER(STAT_DEF, 2, FALSE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TargetAbilityStatRaiseRet; effect++; } break; case ABILITY_STAMINA: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)) { gEffectBattler = battler; SET_STATCHANGER(STAT_DEF, 1, FALSE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TargetAbilityStatRaiseRet; effect++; } break; case ABILITY_BERSERK: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) // Had more than half of hp before, now has less && gBattleStruct->hpBefore[battler] >= gBattleMons[battler].maxHP / 2 && gBattleMons[battler].hp < gBattleMons[battler].maxHP / 2 && (gMultiHitCounter == 0 || gMultiHitCounter == 1) && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) && CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) { gEffectBattler = battler; SET_STATCHANGER(STAT_SPATK, 1, FALSE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TargetAbilityStatRaiseRet; effect++; } break; case ABILITY_EMERGENCY_EXIT: case ABILITY_WIMP_OUT: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) // Had more than half of hp before, now has less && gBattleStruct->hpBefore[battler] > gBattleMons[battler].maxHP / 2 && gBattleMons[battler].hp < gBattleMons[battler].maxHP / 2 && (gMultiHitCounter == 0 || gMultiHitCounter == 1) && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) && (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) && !(gBattleTypeFlags & BATTLE_TYPE_ARENA) && CountUsablePartyMons(battler) > 0 // Not currently held by Sky Drop && !(gStatuses3[battler] & STATUS3_SKY_DROPPED)) { gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_EMERGENCY_EXIT; effect++; } break; case ABILITY_WEAK_ARMOR: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && IS_MOVE_PHYSICAL(gCurrentMove) && (CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) // Don't activate if speed cannot be raised || CompareStat(battler, STAT_DEF, MIN_STAT_STAGE, CMP_GREATER_THAN))) // Don't activate if defense cannot be lowered { if (gBattleMoves[gCurrentMove].effect == EFFECT_HIT_ESCAPE && CanBattlerSwitch(gBattlerAttacker)) gProtectStructs[battler].disableEjectPack = TRUE; // Set flag for target BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_WeakArmorActivates; effect++; } break; case ABILITY_CURSED_BODY: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && gDisableStructs[gBattlerAttacker].disabledMove == MOVE_NONE && IsBattlerAlive(gBattlerAttacker) && !IsAbilityOnSide(gBattlerAttacker, ABILITY_AROMA_VEIL) && gBattleMons[gBattlerAttacker].pp[gChosenMovePos] != 0 && (Random() % 3) == 0) { gDisableStructs[gBattlerAttacker].disabledMove = gChosenMove; gDisableStructs[gBattlerAttacker].disableTimer = 4; PREPARE_MOVE_BUFFER(gBattleTextBuff1, gChosenMove); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_CursedBodyActivates; effect++; } break; case ABILITY_LINGERING_AROMA: case ABILITY_MUMMY: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && IsBattlerAlive(gBattlerAttacker) && TARGET_TURN_DAMAGED && IsMoveMakingContact(move, gBattlerAttacker) && gBattleStruct->overwrittenAbilities[gBattlerAttacker] != GetBattlerAbility(gBattlerTarget)) { switch (gBattleMons[gBattlerAttacker].ability) { case ABILITY_MUMMY: case ABILITY_BATTLE_BOND: case ABILITY_COMATOSE: case ABILITY_DISGUISE: case ABILITY_MULTITYPE: case ABILITY_POWER_CONSTRUCT: case ABILITY_RKS_SYSTEM: case ABILITY_SCHOOLING: case ABILITY_SHIELDS_DOWN: case ABILITY_STANCE_CHANGE: break; default: if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_ABILITY_SHIELD) { RecordItemEffectBattle(gBattlerAttacker, HOLD_EFFECT_ABILITY_SHIELD); break; } gLastUsedAbility = gBattleMons[gBattlerAttacker].ability = gBattleStruct->overwrittenAbilities[gBattlerAttacker] = gBattleMons[gBattlerTarget].ability; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MummyActivates; effect++; break; } } break; case ABILITY_WANDERING_SPIRIT: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && IsBattlerAlive(gBattlerAttacker) && TARGET_TURN_DAMAGED && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT)) { switch (gBattleMons[gBattlerAttacker].ability) { case ABILITY_DISGUISE: case ABILITY_FLOWER_GIFT: case ABILITY_GULP_MISSILE: case ABILITY_HUNGER_SWITCH: case ABILITY_ICE_FACE: case ABILITY_ILLUSION: case ABILITY_IMPOSTER: case ABILITY_RECEIVER: case ABILITY_RKS_SYSTEM: case ABILITY_SCHOOLING: case ABILITY_STANCE_CHANGE: case ABILITY_WONDER_GUARD: case ABILITY_ZEN_MODE: break; default: if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_ABILITY_SHIELD) { RecordItemEffectBattle(gBattlerAttacker, HOLD_EFFECT_ABILITY_SHIELD); break; } gLastUsedAbility = gBattleMons[gBattlerAttacker].ability; gBattleMons[gBattlerAttacker].ability = gBattleStruct->overwrittenAbilities[gBattlerAttacker] = gBattleMons[gBattlerTarget].ability; gBattleMons[gBattlerTarget].ability = gBattleStruct->overwrittenAbilities[gBattlerTarget] = gLastUsedAbility; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_WanderingSpiritActivates; effect++; break; } } break; case ABILITY_ANGER_POINT: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gIsCriticalHit && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) { SET_STATCHANGER(STAT_ATK, MAX_STAT_STAGE - gBattleMons[battler].statStages[STAT_ATK], FALSE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TargetsStatWasMaxedOut; 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_GOOEY: case ABILITY_TANGLING_HAIR: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && (CompareStat(gBattlerAttacker, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN) || GetBattlerAbility(gBattlerAttacker) == ABILITY_MIRROR_ARMOR) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && IsMoveMakingContact(move, gBattlerAttacker)) { SET_STATCHANGER(STAT_SPEED, 1, TRUE); gBattleScripting.moveEffect = MOVE_EFFECT_SPD_MINUS_1; PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_GooeyActivates; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITY_ROUGH_SKIN: case ABILITY_IRON_BARBS: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && IsMoveMakingContact(move, gBattlerAttacker)) { #if B_ROUGH_SKIN_DMG >= GEN_4 gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 8; #else gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 16; #endif if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_RoughSkinActivates; effect++; } break; case ABILITY_AFTERMATH: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerTarget].hp == 0 && IsBattlerAlive(gBattlerAttacker) && IsMoveMakingContact(move, gBattlerAttacker)) { u8 battler; if ((battler = IsAbilityOnField(ABILITY_DAMP))) { gBattleScripting.battler = battler - 1; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_DampPreventsAftermath; } else { gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 4; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AftermathDmg; } effect++; } break; case ABILITY_INNARDS_OUT: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerTarget].hp == 0 && IsBattlerAlive(gBattlerAttacker)) { gBattleMoveDamage = gSpecialStatuses[gBattlerTarget].dmg; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AftermathDmg; effect++; } break; case ABILITY_EFFECT_SPORE: if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GRASS) && GetBattlerAbility(gBattlerAttacker) != ABILITY_OVERCOAT && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES) { i = Random() % 3; if (i == 0) goto POISON_POINT; if (i == 1) goto STATIC; // Sleep if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && CanSleep(gBattlerAttacker) && IsMoveMakingContact(move, gBattlerAttacker) && (Random() % 3) == 0) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_SLEEP; PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityStatusEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } } break; POISON_POINT: case ABILITY_POISON_POINT: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && CanBePoisoned(gBattlerTarget, gBattlerAttacker) && IsMoveMakingContact(move, gBattlerAttacker) && RandomWeighted(RNG_POISON_POINT, 2, 1)) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_POISON; PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityStatusEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; STATIC: case ABILITY_STATIC: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && CanBeParalyzed(gBattlerAttacker) && IsMoveMakingContact(move, gBattlerAttacker) && RandomWeighted(RNG_STATIC, 2, 1)) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_PARALYSIS; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityStatusEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITY_FLAME_BODY: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && (IsMoveMakingContact(move, gBattlerAttacker)) && TARGET_TURN_DAMAGED && CanBeBurned(gBattlerAttacker) && RandomWeighted(RNG_FLAME_BODY, 2, 1)) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityStatusEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITY_CUTE_CHARM: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && (IsMoveMakingContact(move, gBattlerAttacker)) && TARGET_TURN_DAMAGED && gBattleMons[gBattlerTarget].hp != 0 && RandomWeighted(RNG_CUTE_CHARM, 2, 1) && GetBattlerAbility(gBattlerAttacker) != ABILITY_OBLIVIOUS && !IsAbilityOnSide(gBattlerAttacker, ABILITY_AROMA_VEIL) && 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; case ABILITY_ILLUSION: if (gBattleStruct->illusion[gBattlerTarget].on && !gBattleStruct->illusion[gBattlerTarget].broken && TARGET_TURN_DAMAGED) { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_IllusionOff; effect++; } break; case ABILITY_COTTON_DOWN: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED) { gEffectBattler = gBattlerTarget; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_CottonDownActivates; effect++; } break; case ABILITY_STEAM_ENGINE: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && CompareStat(battler, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN) && (moveType == TYPE_FIRE || moveType == TYPE_WATER)) { gEffectBattler = battler; SET_STATCHANGER(STAT_SPEED, 6, FALSE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TargetAbilityStatRaiseRet; effect++; } break; case ABILITY_SAND_SPIT: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && !(gBattleWeather & B_WEATHER_SANDSTORM && WEATHER_HAS_EFFECT)) { if (gBattleWeather & B_WEATHER_PRIMAL_ANY && WEATHER_HAS_EFFECT) { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BlockedByPrimalWeatherRet; effect++; } else if (TryChangeBattleWeather(battler, ENUM_WEATHER_SANDSTORM, TRUE)) { gBattleScripting.battler = gActiveBattler = battler; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SandSpitActivates; effect++; } } break; case ABILITY_PERISH_BODY: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) && (IsMoveMakingContact(move, gBattlerAttacker)) && !(gStatuses3[gBattlerAttacker] & STATUS3_PERISH_SONG)) { if (!(gStatuses3[battler] & STATUS3_PERISH_SONG)) { gStatuses3[battler] |= STATUS3_PERISH_SONG; gDisableStructs[battler].perishSongTimer = 3; } gStatuses3[gBattlerAttacker] |= STATUS3_PERISH_SONG; gDisableStructs[gBattlerAttacker].perishSongTimer = 3; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_PerishBodyActivates; effect++; } break; case ABILITY_GULP_MISSILE: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && IsBattlerAlive(battler)) { if (gBattleMons[gBattlerTarget].species == SPECIES_CRAMORANT_GORGING) { gBattleStruct->changedSpecies[gBattlerPartyIndexes[gBattlerTarget]] = gBattleMons[gBattlerTarget].species; gBattleMons[gBattlerTarget].species = SPECIES_CRAMORANT; if (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) { gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 4; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; } BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_GulpMissileGorging; effect++; } else if (gBattleMons[gBattlerTarget].species == SPECIES_CRAMORANT_GULPING) { gBattleStruct->changedSpecies[gBattlerPartyIndexes[gBattlerTarget]] = gBattleMons[gBattlerTarget].species; gBattleMons[gBattlerTarget].species = SPECIES_CRAMORANT; if (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) { gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 4; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; } BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_GulpMissileGulping; effect++; } } break; case ABILITY_SEED_SOWER: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && IsBattlerAlive(gBattlerTarget) && TryChangeBattleTerrain(gBattlerTarget, STATUS_FIELD_GRASSY_TERRAIN, &gFieldTimers.terrainTimer)) { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SeedSowerActivates; effect++; } break; case ABILITY_THERMAL_EXCHANGE: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(gBattlerTarget) && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN) && moveType == TYPE_FIRE) { gEffectBattler = gBattlerTarget; SET_STATCHANGER(STAT_ATK, 1, FALSE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TargetAbilityStatRaiseRet; effect++; } break; case ABILITY_ANGER_SHELL: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && (gBattleMons[gBattlerTarget].hp <= gBattleMons[gBattlerTarget].maxHP / 2) && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove))) { gBattlerAttacker = gBattlerTarget; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AngerShellActivates; effect++; } break; case ABILITY_WIND_POWER: if (!(gBattleMoves[gCurrentMove].flags & FLAG_WIND_MOVE)) break; // fall through case ABILITY_ELECTROMORPHOSIS: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && IsBattlerAlive(gBattlerTarget)) { gBattlerAttacker = gBattlerTarget; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_WindPowerActivates; effect++; } break; case ABILITY_TOXIC_DEBRIS: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && IS_MOVE_PHYSICAL(gCurrentMove) && TARGET_TURN_DAMAGED && !(gSideStatuses[GetBattlerSide(gBattlerAttacker)] & SIDE_STATUS_TOXIC_SPIKES) && IsBattlerAlive(gBattlerTarget)) { gBattlerTarget = gBattlerAttacker; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ToxicDebrisActivates; effect++; } break; } break; case ABILITYEFFECT_MOVE_END_ATTACKER: // Same as above, but for attacker switch (gLastUsedAbility) { case ABILITY_POISON_TOUCH: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerTarget].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && CanBePoisoned(gBattlerAttacker, gBattlerTarget) && IsMoveMakingContact(move, gBattlerAttacker) && TARGET_TURN_DAMAGED // Need to actually hit the target && (Random() % 3) == 0) { gBattleScripting.moveEffect = MOVE_EFFECT_POISON; PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityStatusEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITY_STENCH: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerTarget].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && RandomWeighted(RNG_STENCH, 9, 1) && !IS_MOVE_STATUS(move) && !sMovesNotAffectedByStench[gCurrentMove]) { gBattleScripting.moveEffect = MOVE_EFFECT_FLINCH; BattleScriptPushCursor(); SetMoveEffect(FALSE, 0); BattleScriptPop(); effect++; } break; case ABILITY_GULP_MISSILE: if (((gCurrentMove == MOVE_SURF && TARGET_TURN_DAMAGED) || gStatuses3[gBattlerAttacker] & STATUS3_UNDERWATER) && (effect = ShouldChangeFormHpBased(gBattlerAttacker))) { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AttackerFormChange; effect++; } break; } break; case ABILITYEFFECT_MOVE_END_OTHER: // Abilities that activate on *another* battler's moveend: Dancer, Soul-Heart, Receiver, Symbiosis switch (GetBattlerAbility(battler)) { case ABILITY_DANCER: if (IsBattlerAlive(battler) && (gBattleMoves[gCurrentMove].flags & FLAG_DANCE) && !gSpecialStatuses[battler].dancerUsedMove && gBattlerAttacker != battler) { // Set bit and save Dancer mon's original target gSpecialStatuses[battler].dancerUsedMove = TRUE; gSpecialStatuses[battler].dancerOriginalTarget = *(gBattleStruct->moveTarget + battler) | 0x4; gBattleStruct->atkCancellerTracker = 0; gBattlerAttacker = gBattlerAbility = battler; gCalledMove = gCurrentMove; // Set the target to the original target of the mon that first used a Dance move gBattlerTarget = gBattleScripting.savedBattler & 0x3; // Make sure that the target isn't an ally - if it is, target the original user if (GetBattlerSide(gBattlerTarget) == GetBattlerSide(gBattlerAttacker)) gBattlerTarget = (gBattleScripting.savedBattler & 0xF0) >> 4; gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED; BattleScriptExecute(BattleScript_DancerActivates); effect++; } break; } break; case ABILITYEFFECT_IMMUNITY: // 5 for (battler = 0; battler < gBattlersCount; battler++) { switch (GetBattlerAbility(battler)) { case ABILITY_IMMUNITY: case ABILITY_PASTEL_VEIL: 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: case ABILITY_WATER_BUBBLE: 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) effect = 3; else if (gDisableStructs[battler].tauntTimer != 0) effect = 4; break; } if (effect != 0) { switch (effect) { case 1: // status cleared gBattleMons[battler].status1 = 0; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityCuredStatus; break; case 2: // get rid of confusion gBattleMons[battler].status2 &= ~STATUS2_CONFUSION; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityCuredStatus; break; case 3: // get rid of infatuation gBattleMons[battler].status2 &= ~STATUS2_INFATUATION; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BattlerGotOverItsInfatuation; break; case 4: // get rid of taunt gDisableStructs[battler].tauntTimer = 0; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BattlerShookOffTaunt; break; } gBattleScripting.battler = gActiveBattler = gBattlerAbility = battler; BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); return effect; } } break; case ABILITYEFFECT_SYNCHRONIZE: if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT)) { gHitMarker &= ~HITMARKER_SYNCHRONISE_EFFECT; if (!(gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY)) { gBattleStruct->synchronizeMoveEffect &= ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); #if B_SYNCHRONIZE_TOXIC < GEN_5 if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC) gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON; #endif gBattleScripting.moveEffect = gBattleStruct->synchronizeMoveEffect + MOVE_EFFECT_AFFECTS_USER; gBattleScripting.battler = gBattlerAbility = gBattlerTarget; PREPARE_ABILITY_BUFFER(gBattleTextBuff1, ABILITY_SYNCHRONIZE); 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; if (!(gBattleMons[gBattlerTarget].status1 & STATUS1_ANY)) { gBattleStruct->synchronizeMoveEffect &= ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC) gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON; gBattleScripting.moveEffect = gBattleStruct->synchronizeMoveEffect; gBattleScripting.battler = gBattlerAbility = gBattlerAttacker; PREPARE_ABILITY_BUFFER(gBattleTextBuff1, ABILITY_SYNCHRONIZE); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SynchronizeActivates; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } } break; case ABILITYEFFECT_TRACE1: case ABILITYEFFECT_TRACE2: for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ABILITY_TRACE && (gBattleResources->flags->flags[i] & RESOURCE_FLAG_TRACED)) { u8 side = (BATTLE_OPPOSITE(GetBattlerPosition(i))) & BIT_SIDE; // side of the opposing pokemon u8 target1 = GetBattlerAtPosition(side); u8 target2 = GetBattlerAtPosition(side + BIT_FLANK); if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { if (!sAbilitiesNotTraced[gBattleMons[target1].ability] && gBattleMons[target1].hp != 0 && !sAbilitiesNotTraced[gBattleMons[target2].ability] && gBattleMons[target2].hp != 0) gActiveBattler = GetBattlerAtPosition(((Random() & 1) * 2) | side), effect++; else if (!sAbilitiesNotTraced[gBattleMons[target1].ability] && gBattleMons[target1].hp != 0) gActiveBattler = target1, effect++; else if (!sAbilitiesNotTraced[gBattleMons[target2].ability] && gBattleMons[target2].hp != 0) gActiveBattler = target2, effect++; } else { if (!sAbilitiesNotTraced[gBattleMons[target1].ability] && gBattleMons[target1].hp != 0) gActiveBattler = target1, effect++; } if (effect != 0) { if (caseID == ABILITYEFFECT_TRACE1) { BattleScriptPushCursorAndCallback(BattleScript_TraceActivatesEnd3); } else { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TraceActivates; } gBattleResources->flags->flags[i] &= ~RESOURCE_FLAG_TRACED; gBattleStruct->tracedAbility[i] = gLastUsedAbility = gBattleMons[gActiveBattler].ability; RecordAbilityBattle(gActiveBattler, gLastUsedAbility); // Record the opposing battler has this ability battler = gBattlerAbility = gBattleScripting.battler = i; PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gActiveBattler, gBattlerPartyIndexes[gActiveBattler]) PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility) break; } } } break; case ABILITYEFFECT_NEUTRALIZINGGAS: // Prints message only. separate from ABILITYEFFECT_ON_SWITCHIN bc activates before entry hazards for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ABILITY_NEUTRALIZING_GAS && !(gBattleResources->flags->flags[i] & RESOURCE_FLAG_NEUTRALIZING_GAS)) { gBattleResources->flags->flags[i] |= RESOURCE_FLAG_NEUTRALIZING_GAS; gBattlerAbility = i; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_NEUTRALIZING_GAS; BattleScriptPushCursorAndCallback(BattleScript_SwitchInAbilityMsg); effect++; } if (effect != 0) break; } break; case ABILITYEFFECT_FIELD_SPORT: switch (gLastUsedAbility) { case ABILITYEFFECT_MUD_SPORT: for (i = 0; i < gBattlersCount; i++) { if (gStatuses4[i] & STATUS4_MUD_SPORT) effect = i + 1; } break; case ABILITYEFFECT_WATER_SPORT: for (i = 0; i < gBattlersCount; i++) { if (gStatuses4[i] & STATUS4_WATER_SPORT) effect = i + 1; } break; default: for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ability) { gLastUsedAbility = ability; effect = i + 1; } } break; } break; case ABILITYEFFECT_ON_WEATHER: // For ability effects that activate when the battle weather changes. battler = gBattlerAbility = gBattleScripting.battler; gLastUsedAbility = GetBattlerAbility(battler); switch (gLastUsedAbility) { case ABILITY_FORECAST: #if B_WEATHER_FORMS >= GEN_5 case ABILITY_FLOWER_GIFT: #else TRY_WEATHER_FORM: #endif effect = TryWeatherFormChange(battler); if (effect != 0) { BattleScriptPushCursorAndCallback(BattleScript_WeatherFormChange); *(&gBattleStruct->formToChangeInto) = effect - 1; } break; case ABILITY_ICE_FACE: if (IsBattlerWeatherAffected(battler, B_WEATHER_HAIL) && gBattleMons[battler].species == SPECIES_EISCUE_NOICE_FACE && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && gBattleStruct->allowedToChangeFormInWeather[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)]) { gBattleStruct->allowedToChangeFormInWeather[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)] = FALSE; gBattleMons[battler].species = SPECIES_EISCUE; BattleScriptPushCursorAndCallback(BattleScript_BattlerFormChangeWithStringEnd3); effect++; } break; case ABILITY_PROTOSYNTHESIS: if (!gSpecialStatuses[battler].weatherAbilityDone && IsBattlerWeatherAffected(battler, B_WEATHER_SUN)) { gSpecialStatuses[battler].weatherAbilityDone = TRUE; PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler)); BattleScriptPushCursorAndCallback(BattleScript_ProtosynthesisActivates); effect++; } break; } break; case ABILITYEFFECT_ON_TERRAIN: // For ability effects that activate when the field terrain changes. battler = gBattlerAbility = gBattleScripting.battler; gLastUsedAbility = GetBattlerAbility(battler); switch (gLastUsedAbility) { case ABILITY_MIMICRY: if (!gSpecialStatuses[battler].terrainAbilityDone && ChangeTypeBasedOnTerrain(battler)) { gSpecialStatuses[battler].terrainAbilityDone = TRUE; ChangeTypeBasedOnTerrain(battler); BattleScriptPushCursorAndCallback(BattleScript_MimicryActivates_End3); effect++; } break; case ABILITY_QUARK_DRIVE: if (!gSpecialStatuses[battler].terrainAbilityDone && IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN)) { gSpecialStatuses[battler].terrainAbilityDone = TRUE; PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler)); BattleScriptPushCursorAndCallback(BattleScript_QuarkDriveActivates); effect++; } break; } break; } if (effect && gLastUsedAbility != 0xFF) RecordAbilityBattle(battler, gLastUsedAbility); if (effect && caseID <= ABILITYEFFECT_MOVE_END) gBattlerAbility = battler; return effect; } bool32 IsNeutralizingGasBannedAbility(u32 ability) { switch (ability) { case ABILITY_MULTITYPE: case ABILITY_ZEN_MODE: case ABILITY_STANCE_CHANGE: case ABILITY_POWER_CONSTRUCT: case ABILITY_SCHOOLING: case ABILITY_RKS_SYSTEM: case ABILITY_SHIELDS_DOWN: case ABILITY_COMATOSE: case ABILITY_DISGUISE: case ABILITY_GULP_MISSILE: case ABILITY_ICE_FACE: case ABILITY_AS_ONE_ICE_RIDER: case ABILITY_AS_ONE_SHADOW_RIDER: return TRUE; default: return FALSE; } } bool32 IsNeutralizingGasOnField(void) { u32 i; for (i = 0; i < gBattlersCount; i++) { if (IsBattlerAlive(i) && gBattleMons[i].ability == ABILITY_NEUTRALIZING_GAS && !(gStatuses3[i] & STATUS3_GASTRO_ACID)) return TRUE; } return FALSE; } bool32 IsMyceliumMightOnField(void) { u32 i; for (i = 0; i < gBattlersCount; i++) { if (IsBattlerAlive(i) && gBattleMons[i].ability == ABILITY_MYCELIUM_MIGHT && IS_MOVE_STATUS(gCurrentMove)) return TRUE; } return FALSE; } u32 GetBattlerAbility(u8 battlerId) { if (gStatuses3[battlerId] & STATUS3_GASTRO_ACID) return ABILITY_NONE; if (IsNeutralizingGasOnField() && !IsNeutralizingGasBannedAbility(gBattleMons[battlerId].ability)) return ABILITY_NONE; if (IsMyceliumMightOnField()) return ABILITY_NONE; if ((((gBattleMons[gBattlerAttacker].ability == ABILITY_MOLD_BREAKER || gBattleMons[gBattlerAttacker].ability == ABILITY_TERAVOLT || gBattleMons[gBattlerAttacker].ability == ABILITY_TURBOBLAZE) && !(gStatuses3[gBattlerAttacker] & STATUS3_GASTRO_ACID)) || gBattleMoves[gCurrentMove].flags & FLAG_TARGET_ABILITY_IGNORED) && sAbilitiesAffectedByMoldBreaker[gBattleMons[battlerId].ability] && gBattlerByTurnOrder[gCurrentTurnActionNumber] == gBattlerAttacker && gActionsByTurnOrder[gBattlerByTurnOrder[gBattlerAttacker]] == B_ACTION_USE_MOVE && gCurrentTurnActionNumber < gBattlersCount) return ABILITY_NONE; return gBattleMons[battlerId].ability; } u32 IsAbilityOnSide(u32 battlerId, u32 ability) { if (IsBattlerAlive(battlerId) && GetBattlerAbility(battlerId) == ability) return battlerId + 1; else if (IsBattlerAlive(BATTLE_PARTNER(battlerId)) && GetBattlerAbility(BATTLE_PARTNER(battlerId)) == ability) return BATTLE_PARTNER(battlerId) + 1; else return 0; } u32 IsAbilityOnOpposingSide(u32 battlerId, u32 ability) { return IsAbilityOnSide(BATTLE_OPPOSITE(battlerId), ability); } u32 IsAbilityOnField(u32 ability) { u32 i; for (i = 0; i < gBattlersCount; i++) { if (IsBattlerAlive(i) && GetBattlerAbility(i) == ability) return i + 1; } return 0; } u32 IsAbilityOnFieldExcept(u32 battlerId, u32 ability) { u32 i; for (i = 0; i < gBattlersCount; i++) { if (i != battlerId && IsBattlerAlive(i) && GetBattlerAbility(i) == ability) return i + 1; } return 0; } u32 IsAbilityPreventingEscape(u32 battlerId) { u32 id; #if B_GHOSTS_ESCAPE >= GEN_6 if (IS_BATTLER_OF_TYPE(battlerId, TYPE_GHOST)) return 0; #endif #if B_SHADOW_TAG_ESCAPE >= GEN_4 if ((id = IsAbilityOnOpposingSide(battlerId, ABILITY_SHADOW_TAG)) && GetBattlerAbility(battlerId) != ABILITY_SHADOW_TAG) #else if ((id = IsAbilityOnOpposingSide(battlerId, ABILITY_SHADOW_TAG))) #endif return id; if ((id = IsAbilityOnOpposingSide(battlerId, ABILITY_ARENA_TRAP)) && IsBattlerGrounded(battlerId)) return id; if ((id = IsAbilityOnOpposingSide(battlerId, ABILITY_MAGNET_PULL)) && IS_BATTLER_OF_TYPE(battlerId, TYPE_STEEL)) return id; return 0; } bool32 CanBattlerEscape(u32 battlerId) // no ability check { if (GetBattlerHoldEffect(battlerId, TRUE) == HOLD_EFFECT_SHED_SHELL) return TRUE; #if B_GHOSTS_ESCAPE >= GEN_6 else if (IS_BATTLER_OF_TYPE(battlerId, TYPE_GHOST)) return TRUE; #endif else if (gBattleMons[battlerId].status2 & (STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED)) return FALSE; else if (gStatuses3[battlerId] & STATUS3_ROOTED) return FALSE; else if (gFieldStatuses & STATUS_FIELD_FAIRY_LOCK) return FALSE; else if (gStatuses3[battlerId] & STATUS3_SKY_DROPPED) return FALSE; else return TRUE; } 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, ITEM_STATUS_CHANGE, ITEM_EFFECT_OTHER, ITEM_PP_CHANGE, ITEM_HP_CHANGE, ITEM_STATS_CHANGE, }; bool32 IsBattlerTerrainAffected(u8 battlerId, u32 terrainFlag) { if (!(gFieldStatuses & terrainFlag)) return FALSE; else if (gStatuses3[battlerId] & STATUS3_SEMI_INVULNERABLE) return FALSE; return IsBattlerGrounded(battlerId); } bool32 CanSleep(u8 battlerId) { u16 ability = GetBattlerAbility(battlerId); if (ability == ABILITY_INSOMNIA || ability == ABILITY_VITAL_SPIRIT || ability == ABILITY_COMATOSE || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_SAFEGUARD || gBattleMons[battlerId].status1 & STATUS1_ANY || IsAbilityOnSide(battlerId, ABILITY_SWEET_VEIL) || IsAbilityStatusProtected(battlerId) || IsBattlerTerrainAffected(battlerId, STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } bool32 CanBePoisoned(u8 battlerAttacker, u8 battlerTarget) { u16 ability = GetBattlerAbility(battlerTarget); if (!(CanPoisonType(battlerAttacker, battlerTarget)) || gSideStatuses[GetBattlerSide(battlerTarget)] & SIDE_STATUS_SAFEGUARD || gBattleMons[battlerTarget].status1 & STATUS1_ANY || ability == ABILITY_IMMUNITY || ability == ABILITY_COMATOSE || IsAbilityOnSide(battlerTarget, ABILITY_PASTEL_VEIL) || gBattleMons[battlerTarget].status1 & STATUS1_ANY || IsAbilityStatusProtected(battlerTarget) || IsBattlerTerrainAffected(battlerTarget, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } bool32 CanBeBurned(u8 battlerId) { u16 ability = GetBattlerAbility(battlerId); if (IS_BATTLER_OF_TYPE(battlerId, TYPE_FIRE) || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_SAFEGUARD || gBattleMons[battlerId].status1 & STATUS1_ANY || ability == ABILITY_WATER_VEIL || ability == ABILITY_WATER_BUBBLE || ability == ABILITY_COMATOSE || ability == ABILITY_THERMAL_EXCHANGE || IsAbilityStatusProtected(battlerId) || IsBattlerTerrainAffected(battlerId, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } bool32 CanBeParalyzed(u8 battlerId) { u16 ability = GetBattlerAbility(battlerId); if ( #if B_PARALYZE_ELECTRIC >= GEN_6 IS_BATTLER_OF_TYPE(battlerId, TYPE_ELECTRIC) || #endif gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_SAFEGUARD || ability == ABILITY_LIMBER || ability == ABILITY_COMATOSE || gBattleMons[battlerId].status1 & STATUS1_ANY || IsAbilityStatusProtected(battlerId) || IsBattlerTerrainAffected(battlerId, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } bool32 CanBeFrozen(u8 battlerId) { u16 ability = GetBattlerAbility(battlerId); if (IS_BATTLER_OF_TYPE(battlerId, TYPE_ICE) || IsBattlerWeatherAffected(battlerId, B_WEATHER_SUN) || gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_SAFEGUARD || ability == ABILITY_MAGMA_ARMOR || ability == ABILITY_COMATOSE || gBattleMons[battlerId].status1 & STATUS1_ANY || IsAbilityStatusProtected(battlerId) || IsBattlerTerrainAffected(battlerId, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } bool32 CanBeConfused(u8 battlerId) { if (GetBattlerAbility(battlerId) == ABILITY_OWN_TEMPO || gBattleMons[battlerId].status2 & STATUS2_CONFUSION || IsBattlerTerrainAffected(battlerId, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } // second argument is 1/X of current hp compared to max hp bool32 HasEnoughHpToEatBerry(u32 battlerId, u32 hpFraction, u32 itemId) { bool32 isBerry = (ItemId_GetPocket(itemId) == POCKET_BERRIES); if (gBattleMons[battlerId].hp == 0) return FALSE; if (gBattleScripting.overrideBerryRequirements) return TRUE; // Unnerve prevents consumption of opponents' berries. if (isBerry && IsUnnerveAbilityOnOpposingSide(battlerId)) return FALSE; if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / hpFraction) return TRUE; if (hpFraction <= 4 && GetBattlerAbility(battlerId) == ABILITY_GLUTTONY && isBerry && gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2) { RecordAbilityBattle(battlerId, ABILITY_GLUTTONY); return TRUE; } return FALSE; } #if B_CONFUSE_BERRIES_HEAL >= GEN_7 #define CONFUSE_BERRY_HP_FRACTION 4 #else #define CONFUSE_BERRY_HP_FRACTION 2 #endif static u8 HealConfuseBerry(u32 battlerId, u32 itemId, u8 flavorId, bool32 end2) { if (HasEnoughHpToEatBerry(battlerId, CONFUSE_BERRY_HP_FRACTION, itemId) #if B_HEAL_BLOCKING >= GEN_5 && !(gStatuses3[battlerId] & STATUS3_HEAL_BLOCK) #endif ) { PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, flavorId); gBattleMoveDamage = gBattleMons[battlerId].maxHP / GetBattlerItemHoldEffectParam(battlerId, itemId); if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; if (GetBattlerAbility(battlerId) == ABILITY_RIPEN) { gBattleMoveDamage *= 2; gBattlerAbility = battlerId; } gBattleScripting.battler = battlerId; if (end2) { if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, flavorId) < 0) BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); else BattleScriptExecute(BattleScript_ItemHealHP_RemoveItemEnd2); } else { BattleScriptPushCursor(); if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, flavorId) < 0) gBattlescriptCurrInstr = BattleScript_BerryConfuseHealRet; else gBattlescriptCurrInstr = BattleScript_ItemHealHP_RemoveItemRet; } return ITEM_HP_CHANGE; } return 0; } #undef CONFUSE_BERRY_HP_FRACTION static u8 StatRaiseBerry(u32 battlerId, u32 itemId, u32 statId, bool32 end2) { if (CompareStat(battlerId, statId, MAX_STAT_STAGE, CMP_LESS_THAN) && HasEnoughHpToEatBerry(battlerId, GetBattlerItemHoldEffectParam(battlerId, itemId), itemId)) { BufferStatChange(battlerId, statId, STRINGID_STATROSE); gEffectBattler = battlerId; if (GetBattlerAbility(battlerId) == ABILITY_RIPEN) SET_STATCHANGER(statId, 2, FALSE); else SET_STATCHANGER(statId, 1, FALSE); gBattleScripting.animArg1 = 14 + statId; gBattleScripting.animArg2 = 0; if (end2) { BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); } else { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryStatRaiseRet; } return ITEM_STATS_CHANGE; } return 0; } static u8 RandomStatRaiseBerry(u32 battlerId, u32 itemId, bool32 end2) { s32 i; u16 stringId; for (i = 0; i < NUM_STATS - 1; i++) { if (CompareStat(battlerId, STAT_ATK + i, MAX_STAT_STAGE, CMP_LESS_THAN)) break; } if (i != NUM_STATS - 1 && HasEnoughHpToEatBerry(battlerId, GetBattlerItemHoldEffectParam(battlerId, itemId), itemId)) { u16 battlerAbility = GetBattlerAbility(battlerId); do { i = Random() % (NUM_STATS - 1); } while (!CompareStat(battlerId, STAT_ATK + i, MAX_STAT_STAGE, CMP_LESS_THAN)); PREPARE_STAT_BUFFER(gBattleTextBuff1, i + 1); stringId = (battlerAbility == ABILITY_CONTRARY) ? STRINGID_STATFELL : STRINGID_STATROSE; 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; gBattleTextBuff2[6] = stringId >> 8; gBattleTextBuff2[7] = EOS; gEffectBattler = battlerId; if (battlerAbility == ABILITY_RIPEN) SET_STATCHANGER(i + 1, 4, FALSE); else SET_STATCHANGER(i + 1, 2, FALSE); gBattleScripting.animArg1 = 0x21 + i + 6; gBattleScripting.animArg2 = 0; if (end2) { BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); } else { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryStatRaiseRet; } return ITEM_STATS_CHANGE; } return 0; } static u8 TrySetMicleBerry(u32 battlerId, u32 itemId, bool32 end2) { if (HasEnoughHpToEatBerry(battlerId, 4, itemId)) { gProtectStructs[battlerId].usedMicleBerry = TRUE; // battler's next attack has increased accuracy if (end2) { BattleScriptExecute(BattleScript_MicleBerryActivateEnd2); } else { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MicleBerryActivateRet; } return ITEM_EFFECT_OTHER; } return 0; } static u8 DamagedStatBoostBerryEffect(u8 battlerId, u8 statId, u8 split) { if (IsBattlerAlive(battlerId) && TARGET_TURN_DAMAGED && CompareStat(battlerId, statId, MAX_STAT_STAGE, CMP_LESS_THAN) && !DoesSubstituteBlockMove(gBattlerAttacker, battlerId, gCurrentMove) && GetBattleMoveSplit(gCurrentMove) == split) { BufferStatChange(battlerId, statId, STRINGID_STATROSE); gEffectBattler = battlerId; if (GetBattlerAbility(battlerId) == ABILITY_RIPEN) SET_STATCHANGER(statId, 2, FALSE); else SET_STATCHANGER(statId, 1, FALSE); gBattleScripting.animArg1 = 14 + statId; gBattleScripting.animArg2 = 0; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryStatRaiseRet; return ITEM_STATS_CHANGE; } return 0; } u8 TryHandleSeed(u8 battler, u32 terrainFlag, u8 statId, u16 itemId, bool32 execute) { if (gFieldStatuses & terrainFlag && CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN)) { BufferStatChange(battler, statId, STRINGID_STATROSE); gLastUsedItem = itemId; // For surge abilities gEffectBattler = gBattleScripting.battler = battler; SET_STATCHANGER(statId, 1, FALSE); gBattleScripting.animArg1 = 14 + statId; gBattleScripting.animArg2 = 0; if (execute) { BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); } else { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryStatRaiseRet; } return ITEM_STATS_CHANGE; } return 0; } static u8 ItemHealHp(u32 battlerId, u32 itemId, bool32 end2, bool32 percentHeal) { if (!(gBattleScripting.overrideBerryRequirements && gBattleMons[battlerId].hp == gBattleMons[battlerId].maxHP) #if B_HEAL_BLOCKING >= GEN_5 && !(gStatuses3[battlerId] & STATUS3_HEAL_BLOCK) #endif && HasEnoughHpToEatBerry(battlerId, 2, itemId)) { if (percentHeal) gBattleMoveDamage = (gBattleMons[battlerId].maxHP * GetBattlerItemHoldEffectParam(battlerId, itemId) / 100) * -1; else gBattleMoveDamage = GetBattlerItemHoldEffectParam(battlerId, itemId) * -1; // check ripen if (ItemId_GetPocket(itemId) == POCKET_BERRIES && GetBattlerAbility(battlerId) == ABILITY_RIPEN) gBattleMoveDamage *= 2; gBattlerAbility = battlerId; // in SWSH, berry juice shows ability pop up but has no effect. This is mimicked here if (end2) { BattleScriptExecute(BattleScript_ItemHealHP_RemoveItemEnd2); } else { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ItemHealHP_RemoveItemRet; } return ITEM_HP_CHANGE; } return 0; } static bool32 UnnerveOn(u32 battlerId, u32 itemId) { if (ItemId_GetPocket(itemId) == POCKET_BERRIES && IsUnnerveAbilityOnOpposingSide(battlerId)) return TRUE; return FALSE; } static bool32 GetMentalHerbEffect(u8 battlerId) { bool32 ret = FALSE; // Check infatuation if (gBattleMons[battlerId].status2 & STATUS2_INFATUATION) { gBattleMons[battlerId].status2 &= ~STATUS2_INFATUATION; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_INFATUATION; // STRINGID_TARGETGOTOVERINFATUATION StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); ret = TRUE; } #if B_MENTAL_HERB >= GEN_5 // Check taunt if (gDisableStructs[battlerId].tauntTimer != 0) { gDisableStructs[battlerId].tauntTimer = 0; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_TAUNT; PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_TAUNT); ret = TRUE; } // Check encore if (gDisableStructs[battlerId].encoreTimer != 0) { gDisableStructs[battlerId].encoredMove = 0; gDisableStructs[battlerId].encoreTimer = 0; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_ENCORE; // STRINGID_PKMNENCOREENDED ret = TRUE; } // Check torment if (gBattleMons[battlerId].status2 & STATUS2_TORMENT) { gBattleMons[battlerId].status2 &= ~STATUS2_TORMENT; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_TORMENT; ret = TRUE; } // Check heal block if (gStatuses3[battlerId] & STATUS3_HEAL_BLOCK) { gStatuses3[battlerId] &= ~STATUS3_HEAL_BLOCK; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_HEALBLOCK; ret = TRUE; } // Check disable if (gDisableStructs[battlerId].disableTimer != 0) { gDisableStructs[battlerId].disableTimer = 0; gDisableStructs[battlerId].disabledMove = 0; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_MENTALHERBCURE_DISABLE; ret = TRUE; } #endif return ret; } static u8 ItemEffectMoveEnd(u32 battlerId, u16 holdEffect) { u8 effect = 0; u32 i; switch (holdEffect) { #if B_HP_BERRIES >= GEN_4 case HOLD_EFFECT_MICLE_BERRY: effect = TrySetMicleBerry(battlerId, gLastUsedItem, FALSE); break; case HOLD_EFFECT_RESTORE_HP: effect = ItemHealHp(battlerId, gLastUsedItem, FALSE, FALSE); break; #endif #if B_BERRIES_INSTANT >= GEN_4 case HOLD_EFFECT_RESTORE_PCT_HP: effect = ItemHealHp(battlerId, gLastUsedItem, FALSE, TRUE); break; case HOLD_EFFECT_CONFUSE_SPICY: effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_SPICY, FALSE); break; case HOLD_EFFECT_CONFUSE_DRY: effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_DRY, FALSE); break; case HOLD_EFFECT_CONFUSE_SWEET: effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_SWEET, FALSE); break; case HOLD_EFFECT_CONFUSE_BITTER: effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_BITTER, FALSE); break; case HOLD_EFFECT_CONFUSE_SOUR: effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_SOUR, FALSE); break; case HOLD_EFFECT_ATTACK_UP: effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_ATK, FALSE); break; case HOLD_EFFECT_DEFENSE_UP: effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_DEF, FALSE); break; case HOLD_EFFECT_SPEED_UP: effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_SPEED, FALSE); break; case HOLD_EFFECT_SP_ATTACK_UP: effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_SPATK, FALSE); break; case HOLD_EFFECT_SP_DEFENSE_UP: effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_SPDEF, FALSE); break; case HOLD_EFFECT_RANDOM_STAT_UP: effect = RandomStatRaiseBerry(battlerId, gLastUsedItem, FALSE); break; #endif case HOLD_EFFECT_CURE_PAR: if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS && !UnnerveOn(battlerId, gLastUsedItem)) { 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 && !UnnerveOn(battlerId, gLastUsedItem)) { 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 && !UnnerveOn(battlerId, gLastUsedItem)) { 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 && !UnnerveOn(battlerId, gLastUsedItem)) { 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 && !UnnerveOn(battlerId, gLastUsedItem)) { 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 && !UnnerveOn(battlerId, gLastUsedItem)) { gBattleMons[battlerId].status2 &= ~STATUS2_CONFUSION; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCureConfusionRet; effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_MENTAL_HERB: if (GetMentalHerbEffect(battlerId)) { gBattleScripting.savedBattler = gBattlerAttacker; gBattlerAttacker = battlerId; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MentalHerbCureRet; effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_CURE_STATUS: if ((gBattleMons[battlerId].status1 & STATUS1_ANY || gBattleMons[battlerId].status2 & STATUS2_CONFUSION) && !UnnerveOn(battlerId, gLastUsedItem)) { 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] = B_MSG_CURED_PROBLEM; gBattlescriptCurrInstr = BattleScript_BerryCureChosenStatusRet; effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_RESTORE_STATS: for (i = 0; i < NUM_BATTLE_STATS; i++) { if (gBattleMons[battlerId].statStages[i] < DEFAULT_STAT_STAGE) { gBattleMons[battlerId].statStages[i] = DEFAULT_STAT_STAGE; effect = ITEM_STATS_CHANGE; } } if (effect != 0) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_WhiteHerbRet; return effect; } break; case HOLD_EFFECT_CRITICAL_UP: // lansat berry if (B_BERRIES_INSTANT >= GEN_4 && !(gBattleMons[battlerId].status2 & STATUS2_FOCUS_ENERGY) && HasEnoughHpToEatBerry(battlerId, GetBattlerItemHoldEffectParam(battlerId, gLastUsedItem), gLastUsedItem)) { gBattleMons[battlerId].status2 |= STATUS2_FOCUS_ENERGY; gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryFocusEnergyRet; effect = ITEM_EFFECT_OTHER; } break; } return effect; } u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) { int i = 0, moveType; u8 effect = ITEM_NO_EFFECT; u8 changedPP = 0; u8 battlerHoldEffect, atkHoldEffect; u8 atkHoldEffectParam; u16 atkItem; if (caseID != ITEMEFFECT_USE_LAST_ITEM) { gLastUsedItem = gBattleMons[battlerId].item; battlerHoldEffect = GetBattlerHoldEffect(battlerId, TRUE); } atkItem = gBattleMons[gBattlerAttacker].item; atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE); atkHoldEffectParam = GetBattlerHoldEffectParam(gBattlerAttacker); switch (caseID) { case ITEMEFFECT_ON_SWITCH_IN: if (!gSpecialStatuses[battlerId].switchInItemDone) { switch (battlerHoldEffect) { case HOLD_EFFECT_DOUBLE_PRIZE: if (GetBattlerSide(battlerId) == B_SIDE_PLAYER && !gBattleStruct->moneyMultiplierItem) { gBattleStruct->moneyMultiplier *= 2; gBattleStruct->moneyMultiplierItem = 1; } break; case HOLD_EFFECT_RESTORE_STATS: for (i = 0; i < NUM_BATTLE_STATS; i++) { if (gBattleMons[battlerId].statStages[i] < DEFAULT_STAT_STAGE) { gBattleMons[battlerId].statStages[i] = DEFAULT_STAT_STAGE; effect = ITEM_STATS_CHANGE; } } if (effect != 0) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; gActiveBattler = gBattlerAttacker = battlerId; BattleScriptExecute(BattleScript_WhiteHerbEnd2); } break; #if B_BERRIES_INSTANT >= GEN_4 case HOLD_EFFECT_CONFUSE_SPICY: effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_SPICY, TRUE); break; case HOLD_EFFECT_CONFUSE_DRY: effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_DRY, TRUE); break; case HOLD_EFFECT_CONFUSE_SWEET: effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_SWEET, TRUE); break; case HOLD_EFFECT_CONFUSE_BITTER: effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_BITTER, TRUE); break; case HOLD_EFFECT_CONFUSE_SOUR: effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_SOUR, TRUE); break; case HOLD_EFFECT_ATTACK_UP: effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_ATK, TRUE); break; case HOLD_EFFECT_DEFENSE_UP: effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_DEF, TRUE); break; case HOLD_EFFECT_SPEED_UP: effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_SPEED, TRUE); break; case HOLD_EFFECT_SP_ATTACK_UP: effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_SPATK, TRUE); break; case HOLD_EFFECT_SP_DEFENSE_UP: effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_SPDEF, TRUE); break; case HOLD_EFFECT_CRITICAL_UP: if (!(gBattleMons[battlerId].status2 & STATUS2_FOCUS_ENERGY) && HasEnoughHpToEatBerry(battlerId, GetBattlerItemHoldEffectParam(battlerId, gLastUsedItem), gLastUsedItem)) { gBattleMons[battlerId].status2 |= STATUS2_FOCUS_ENERGY; gBattleScripting.battler = battlerId; BattleScriptExecute(BattleScript_BerryFocusEnergyEnd2); effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_RANDOM_STAT_UP: effect = RandomStatRaiseBerry(battlerId, gLastUsedItem, TRUE); break; case HOLD_EFFECT_CURE_PAR: if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS && !UnnerveOn(battlerId, gLastUsedItem)) { 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 && !UnnerveOn(battlerId, gLastUsedItem)) { 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 && !UnnerveOn(battlerId, gLastUsedItem)) { gBattleMons[battlerId].status1 &= ~STATUS1_BURN; BattleScriptExecute(BattleScript_BerryCureBrnEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_FRZ: if (gBattleMons[battlerId].status1 & STATUS1_FREEZE && !UnnerveOn(battlerId, gLastUsedItem)) { gBattleMons[battlerId].status1 &= ~STATUS1_FREEZE; BattleScriptExecute(BattleScript_BerryCureFrzEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_SLP: if (gBattleMons[battlerId].status1 & STATUS1_SLEEP && !UnnerveOn(battlerId, gLastUsedItem)) { gBattleMons[battlerId].status1 &= ~STATUS1_SLEEP; gBattleMons[battlerId].status2 &= ~STATUS2_NIGHTMARE; BattleScriptExecute(BattleScript_BerryCureSlpEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_STATUS: if ((gBattleMons[battlerId].status1 & STATUS1_ANY || gBattleMons[battlerId].status2 & STATUS2_CONFUSION) && !UnnerveOn(battlerId, gLastUsedItem)) { 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] = B_MSG_CURED_PROBLEM; else gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_NORMALIZED_STATUS; gBattleMons[battlerId].status1 = 0; gBattleMons[battlerId].status2 &= ~STATUS2_CONFUSION; BattleScriptExecute(BattleScript_BerryCureChosenStatusEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_RESTORE_HP: effect = ItemHealHp(battlerId, gLastUsedItem, TRUE, FALSE); break; case HOLD_EFFECT_RESTORE_PCT_HP: effect = ItemHealHp(battlerId, gLastUsedItem, TRUE, TRUE); break; #endif case HOLD_EFFECT_AIR_BALLOON: effect = ITEM_EFFECT_OTHER; gBattleScripting.battler = battlerId; BattleScriptPushCursorAndCallback(BattleScript_AirBaloonMsgIn); RecordItemEffectBattle(battlerId, HOLD_EFFECT_AIR_BALLOON); break; case HOLD_EFFECT_ROOM_SERVICE: if (TryRoomService(battlerId)) { BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); effect = ITEM_STATS_CHANGE; } break; case HOLD_EFFECT_SEEDS: switch (GetBattlerHoldEffectParam(battlerId)) { case HOLD_EFFECT_PARAM_ELECTRIC_TERRAIN: effect = TryHandleSeed(battlerId, STATUS_FIELD_ELECTRIC_TERRAIN, STAT_DEF, gLastUsedItem, TRUE); break; case HOLD_EFFECT_PARAM_GRASSY_TERRAIN: effect = TryHandleSeed(battlerId, STATUS_FIELD_GRASSY_TERRAIN, STAT_DEF, gLastUsedItem, TRUE); break; case HOLD_EFFECT_PARAM_MISTY_TERRAIN: effect = TryHandleSeed(battlerId, STATUS_FIELD_MISTY_TERRAIN, STAT_SPDEF, gLastUsedItem, TRUE); break; case HOLD_EFFECT_PARAM_PSYCHIC_TERRAIN: effect = TryHandleSeed(battlerId, STATUS_FIELD_PSYCHIC_TERRAIN, STAT_SPDEF, gLastUsedItem, TRUE); break; } break; case HOLD_EFFECT_EJECT_PACK: if (gProtectStructs[battlerId].statFell && gProtectStructs[battlerId].disableEjectPack == 0 && !(gCurrentMove == MOVE_PARTING_SHOT && CanBattlerSwitch(gBattlerAttacker))) // Does not activate if attacker used Parting Shot and can switch out { gProtectStructs[battlerId].statFell = FALSE; gActiveBattler = gBattleScripting.battler = battlerId; effect = ITEM_STATS_CHANGE; if (moveTurn) { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_EjectPackActivate_Ret; } else { BattleScriptExecute(BattleScript_EjectPackActivate_End2); } } break; } if (effect != 0) { gSpecialStatuses[battlerId].switchInItemDone = TRUE; gActiveBattler = gBattlerAttacker = gPotentialItemEffectBattler = gBattleScripting.battler = battlerId; switch (effect) { case ITEM_STATUS_CHANGE: BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battlerId].status1); MarkBattlerForControllerExec(gActiveBattler); break; case ITEM_PP_CHANGE: if (MOVE_IS_PERMANENT(battlerId, i)) gBattleMons[battlerId].pp[i] = changedPP; break; } } } break; case ITEMEFFECT_NORMAL: if (gBattleMons[battlerId].hp) { switch (battlerHoldEffect) { case HOLD_EFFECT_RESTORE_HP: if (!moveTurn) effect = ItemHealHp(battlerId, gLastUsedItem, TRUE, FALSE); break; case HOLD_EFFECT_RESTORE_PCT_HP: if (!moveTurn) effect = ItemHealHp(battlerId, gLastUsedItem, TRUE, TRUE); break; case HOLD_EFFECT_RESTORE_PP: if (!moveTurn) { struct Pokemon *mon; u8 ppBonuses; u16 move; mon = GetBattlerPartyData(battlerId); for (i = 0; i < MAX_MON_MOVES; 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 != MAX_MON_MOVES) { u8 maxPP = CalculatePPWithBonus(move, ppBonuses, i); u8 ppRestored = GetBattlerHoldEffectParam(battlerId); if (GetBattlerAbility(battlerId) == ABILITY_RIPEN) { ppRestored *= 2; gBattlerAbility = battlerId; } if (changedPP + ppRestored > maxPP) changedPP = maxPP; else changedPP = changedPP + ppRestored; PREPARE_MOVE_BUFFER(gBattleTextBuff1, move); BattleScriptExecute(BattleScript_BerryPPHealEnd2); BtlController_EmitSetMonData(BUFFER_A, i + REQUEST_PPMOVE1_BATTLE, 0, 1, &changedPP); MarkBattlerForControllerExec(gActiveBattler); effect = ITEM_PP_CHANGE; } } break; case HOLD_EFFECT_RESTORE_STATS: for (i = 0; i < NUM_BATTLE_STATS; i++) { if (gBattleMons[battlerId].statStages[i] < DEFAULT_STAT_STAGE) { gBattleMons[battlerId].statStages[i] = DEFAULT_STAT_STAGE; effect = ITEM_STATS_CHANGE; } } if (effect != 0) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; gActiveBattler = gBattlerAttacker = battlerId; BattleScriptExecute(BattleScript_WhiteHerbEnd2); } break; case HOLD_EFFECT_BLACK_SLUDGE: if (IS_BATTLER_OF_TYPE(battlerId, TYPE_POISON)) { goto LEFTOVERS; } else if (GetBattlerAbility(battlerId) != ABILITY_MAGIC_GUARD && !moveTurn) { gBattleMoveDamage = gBattleMons[battlerId].maxHP / 8; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptExecute(BattleScript_ItemHurtEnd2); effect = ITEM_HP_CHANGE; RecordItemEffectBattle(battlerId, battlerHoldEffect); PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); } break; case HOLD_EFFECT_LEFTOVERS: LEFTOVERS: #if B_HEAL_BLOCKING >= GEN_5 if (gBattleMons[battlerId].hp < gBattleMons[battlerId].maxHP && !moveTurn && !(gStatuses3[battlerId] & STATUS3_HEAL_BLOCK)) #else if (gBattleMons[battlerId].hp < gBattleMons[battlerId].maxHP && !moveTurn) #endif { gBattleMoveDamage = gBattleMons[battlerId].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; BattleScriptExecute(BattleScript_ItemHealHP_End2); effect = ITEM_HP_CHANGE; RecordItemEffectBattle(battlerId, battlerHoldEffect); } break; case HOLD_EFFECT_CONFUSE_SPICY: if (!moveTurn) effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_SPICY, TRUE); break; case HOLD_EFFECT_CONFUSE_DRY: if (!moveTurn) effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_DRY, TRUE); break; case HOLD_EFFECT_CONFUSE_SWEET: if (!moveTurn) effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_SWEET, TRUE); break; case HOLD_EFFECT_CONFUSE_BITTER: if (!moveTurn) effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_BITTER, TRUE); break; case HOLD_EFFECT_CONFUSE_SOUR: if (!moveTurn) effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_SOUR, TRUE); break; case HOLD_EFFECT_ATTACK_UP: if (!moveTurn) effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_ATK, TRUE); break; case HOLD_EFFECT_DEFENSE_UP: if (!moveTurn) effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_DEF, TRUE); break; case HOLD_EFFECT_SPEED_UP: if (!moveTurn) effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_SPEED, TRUE); break; case HOLD_EFFECT_SP_ATTACK_UP: if (!moveTurn) effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_SPATK, TRUE); break; case HOLD_EFFECT_SP_DEFENSE_UP: if (!moveTurn) effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_SPDEF, TRUE); break; case HOLD_EFFECT_CRITICAL_UP: if (!moveTurn && !(gBattleMons[battlerId].status2 & STATUS2_FOCUS_ENERGY) && HasEnoughHpToEatBerry(battlerId, GetBattlerItemHoldEffectParam(battlerId, gLastUsedItem), gLastUsedItem)) { gBattleMons[battlerId].status2 |= STATUS2_FOCUS_ENERGY; gBattleScripting.battler = battlerId; BattleScriptExecute(BattleScript_BerryFocusEnergyEnd2); effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_RANDOM_STAT_UP: if (!moveTurn) effect = RandomStatRaiseBerry(battlerId, gLastUsedItem, TRUE); break; case HOLD_EFFECT_CURE_PAR: if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS && !UnnerveOn(battlerId, gLastUsedItem)) { 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 && !UnnerveOn(battlerId, gLastUsedItem)) { 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 && !UnnerveOn(battlerId, gLastUsedItem)) { gBattleMons[battlerId].status1 &= ~STATUS1_BURN; BattleScriptExecute(BattleScript_BerryCureBrnEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_FRZ: if (gBattleMons[battlerId].status1 & STATUS1_FREEZE && !UnnerveOn(battlerId, gLastUsedItem)) { gBattleMons[battlerId].status1 &= ~STATUS1_FREEZE; BattleScriptExecute(BattleScript_BerryCureFrzEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_SLP: if (gBattleMons[battlerId].status1 & STATUS1_SLEEP && !UnnerveOn(battlerId, gLastUsedItem)) { 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 && !UnnerveOn(battlerId, gLastUsedItem)) { 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) && !UnnerveOn(battlerId, gLastUsedItem)) { 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] = B_MSG_CURED_PROBLEM; else gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_NORMALIZED_STATUS; gBattleMons[battlerId].status1 = 0; gBattleMons[battlerId].status2 &= ~STATUS2_CONFUSION; BattleScriptExecute(BattleScript_BerryCureChosenStatusEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_MENTAL_HERB: if (GetMentalHerbEffect(battlerId)) { gBattleScripting.savedBattler = gBattlerAttacker; gBattlerAttacker = battlerId; BattleScriptExecute(BattleScript_MentalHerbCureEnd2); effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_MICLE_BERRY: if (!moveTurn) effect = TrySetMicleBerry(battlerId, gLastUsedItem, TRUE); break; } if (effect != 0) { gActiveBattler = gBattlerAttacker = gPotentialItemEffectBattler = gBattleScripting.battler = battlerId; switch (effect) { case ITEM_STATUS_CHANGE: BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battlerId].status1); MarkBattlerForControllerExec(gActiveBattler); break; case ITEM_PP_CHANGE: if (MOVE_IS_PERMANENT(battlerId, i)) gBattleMons[battlerId].pp[i] = changedPP; break; } } } break; case ITEMEFFECT_USE_LAST_ITEM: effect = ItemEffectMoveEnd(battlerId, ItemId_GetHoldEffect(gLastUsedItem)); gBattleScripting.overrideBerryRequirements = 2; // to exit VARIOUS_CONSUME_BERRIES if (effect) { gActiveBattler = gPotentialItemEffectBattler = gBattleScripting.battler = battlerId; if (effect == ITEM_STATUS_CHANGE) { BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); } break; } break; case ITEMEFFECT_MOVE_END: for (battlerId = 0; battlerId < gBattlersCount; battlerId++) { gLastUsedItem = gBattleMons[battlerId].item; effect = ItemEffectMoveEnd(battlerId, GetBattlerHoldEffect(battlerId, TRUE)); if (effect) { gActiveBattler = gPotentialItemEffectBattler = gBattleScripting.battler = battlerId; if (effect == ITEM_STATUS_CHANGE) { BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); } break; } } break; case ITEMEFFECT_KINGSROCK: // Occur on each hit of a multi-strike move switch (atkHoldEffect) { case HOLD_EFFECT_FLINCH: #if B_SERENE_GRACE_BOOST >= GEN_5 if (GetBattlerAbility(gBattlerAttacker) == ABILITY_SERENE_GRACE) atkHoldEffectParam *= 2; #endif if (gBattleMoveDamage != 0 // Need to have done damage && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && RandomPercentage(RNG_HOLD_EFFECT_FLINCH, atkHoldEffectParam) && gBattleMoves[gCurrentMove].flags & FLAG_KINGS_ROCK_AFFECTED && gBattleMons[gBattlerTarget].hp) { gBattleScripting.moveEffect = MOVE_EFFECT_FLINCH; BattleScriptPushCursor(); SetMoveEffect(FALSE, 0); BattleScriptPop(); } break; case HOLD_EFFECT_BLUNDER_POLICY: if (gBattleStruct->blunderPolicy && gBattleMons[gBattlerAttacker].hp != 0 && CompareStat(gBattlerAttacker, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN)) { gBattleStruct->blunderPolicy = FALSE; gLastUsedItem = atkItem; gBattleScripting.statChanger = SET_STATCHANGER(STAT_SPEED, 2, FALSE); effect = ITEM_STATS_CHANGE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AttackerItemStatRaise; } break; } break; case ITEMEFFECT_LIFEORB_SHELLBELL: // Occur after the final hit of a multi-strike move switch (atkHoldEffect) { case HOLD_EFFECT_SHELL_BELL: if (gSpecialStatuses[gBattlerAttacker].damagedMons // Need to have done damage && gBattlerAttacker != gBattlerTarget && gBattleMons[gBattlerAttacker].hp != gBattleMons[gBattlerAttacker].maxHP #if B_HEAL_BLOCKING >= GEN_5 && gBattleMons[gBattlerAttacker].hp != 0 && !(gStatuses3[battlerId] & STATUS3_HEAL_BLOCK)) #else && gBattleMons[gBattlerAttacker].hp != 0) #endif { 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 = ITEM_HP_CHANGE; } break; case HOLD_EFFECT_LIFE_ORB: if (IsBattlerAlive(gBattlerAttacker) && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD && gSpecialStatuses[gBattlerAttacker].damagedMons) { gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 10; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; effect = ITEM_HP_CHANGE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ItemHurtRet; gLastUsedItem = gBattleMons[gBattlerAttacker].item; } break; case HOLD_EFFECT_THROAT_SPRAY: // Does NOT need to be a damaging move if (gProtectStructs[gBattlerAttacker].targetAffected && gBattleMons[gBattlerAttacker].hp != 0 && gBattleMoves[gCurrentMove].flags & FLAG_SOUND && CompareStat(gBattlerAttacker, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN) && !NoAliveMonsForEitherParty()) // Don't activate if battle will end { gLastUsedItem = atkItem; gBattleScripting.battler = gBattlerAttacker; gBattleScripting.statChanger = SET_STATCHANGER(STAT_SPATK, 1, FALSE); effect = ITEM_STATS_CHANGE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AttackerItemStatRaise; } break; } break; case ITEMEFFECT_TARGET: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { GET_MOVE_TYPE(gCurrentMove, moveType); switch (battlerHoldEffect) { case HOLD_EFFECT_AIR_BALLOON: if (TARGET_TURN_DAMAGED) { effect = ITEM_EFFECT_OTHER; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AirBaloonMsgPop; } break; case HOLD_EFFECT_ROCKY_HELMET: if (TARGET_TURN_DAMAGED && IsMoveMakingContact(gCurrentMove, gBattlerAttacker) && IsBattlerAlive(gBattlerAttacker) && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) { gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 6; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; effect = ITEM_HP_CHANGE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_RockyHelmetActivates; PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); RecordItemEffectBattle(battlerId, HOLD_EFFECT_ROCKY_HELMET); } break; case HOLD_EFFECT_WEAKNESS_POLICY: if (IsBattlerAlive(battlerId) && TARGET_TURN_DAMAGED && gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) { effect = ITEM_STATS_CHANGE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_WeaknessPolicy; } break; case HOLD_EFFECT_SNOWBALL: if (IsBattlerAlive(battlerId) && TARGET_TURN_DAMAGED && moveType == TYPE_ICE) { effect = ITEM_STATS_CHANGE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TargetItemStatRaise; gBattleScripting.statChanger = SET_STATCHANGER(STAT_ATK, 1, FALSE); } break; case HOLD_EFFECT_LUMINOUS_MOSS: if (IsBattlerAlive(battlerId) && TARGET_TURN_DAMAGED && moveType == TYPE_WATER) { effect = ITEM_STATS_CHANGE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TargetItemStatRaise; gBattleScripting.statChanger = SET_STATCHANGER(STAT_SPDEF, 1, FALSE); } break; case HOLD_EFFECT_CELL_BATTERY: if (IsBattlerAlive(battlerId) && TARGET_TURN_DAMAGED && moveType == TYPE_ELECTRIC) { effect = ITEM_STATS_CHANGE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TargetItemStatRaise; gBattleScripting.statChanger = SET_STATCHANGER(STAT_ATK, 1, FALSE); } break; case HOLD_EFFECT_ABSORB_BULB: if (IsBattlerAlive(battlerId) && TARGET_TURN_DAMAGED && moveType == TYPE_WATER) { effect = ITEM_STATS_CHANGE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_TargetItemStatRaise; gBattleScripting.statChanger = SET_STATCHANGER(STAT_SPATK, 1, FALSE); } break; case HOLD_EFFECT_JABOCA_BERRY: // consume and damage attacker if used physical move if (IsBattlerAlive(battlerId) && TARGET_TURN_DAMAGED && !DoesSubstituteBlockMove(gBattlerAttacker, battlerId, gCurrentMove) && IS_MOVE_PHYSICAL(gCurrentMove) && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) { gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 8; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; if (GetBattlerAbility(battlerId) == ABILITY_RIPEN) gBattleMoveDamage *= 2; effect = ITEM_HP_CHANGE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_JabocaRowapBerryActivates; PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); RecordItemEffectBattle(battlerId, HOLD_EFFECT_ROCKY_HELMET); } break; case HOLD_EFFECT_ROWAP_BERRY: // consume and damage attacker if used special move if (IsBattlerAlive(battlerId) && TARGET_TURN_DAMAGED && !DoesSubstituteBlockMove(gBattlerAttacker, battlerId, gCurrentMove) && IS_MOVE_SPECIAL(gCurrentMove) && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD) { gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 8; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; if (GetBattlerAbility(battlerId) == ABILITY_RIPEN) gBattleMoveDamage *= 2; effect = ITEM_HP_CHANGE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_JabocaRowapBerryActivates; PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); RecordItemEffectBattle(battlerId, HOLD_EFFECT_ROCKY_HELMET); } break; case HOLD_EFFECT_KEE_BERRY: // consume and boost defense if used physical move effect = DamagedStatBoostBerryEffect(battlerId, STAT_DEF, SPLIT_PHYSICAL); break; case HOLD_EFFECT_MARANGA_BERRY: // consume and boost sp. defense if used special move effect = DamagedStatBoostBerryEffect(battlerId, STAT_SPDEF, SPLIT_SPECIAL); break; case HOLD_EFFECT_STICKY_BARB: if (TARGET_TURN_DAMAGED && (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) && IsMoveMakingContact(gCurrentMove, gBattlerAttacker) && !DoesSubstituteBlockMove(gBattlerAttacker, battlerId, gCurrentMove) && IsBattlerAlive(gBattlerAttacker) && CanStealItem(gBattlerAttacker, gBattlerTarget, gBattleMons[gBattlerTarget].item) && gBattleMons[gBattlerAttacker].item == ITEM_NONE) { // No sticky hold checks. gEffectBattler = battlerId; // gEffectBattler = target StealTargetItem(gBattlerAttacker, gBattlerTarget); // Attacker takes target's barb BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_StickyBarbTransfer; effect = ITEM_EFFECT_OTHER; } break; } } break; case ITEMEFFECT_ORBS: { u16 battlerAbility = GetBattlerAbility(battlerId); switch (battlerHoldEffect) { case HOLD_EFFECT_TOXIC_ORB: if (CanBePoisoned(battlerId, battlerId)) { effect = ITEM_STATUS_CHANGE; gBattleMons[battlerId].status1 = STATUS1_TOXIC_POISON; BattleScriptExecute(BattleScript_ToxicOrb); RecordItemEffectBattle(battlerId, battlerHoldEffect); } break; case HOLD_EFFECT_FLAME_ORB: if (CanBeBurned(battlerId)) { effect = ITEM_STATUS_CHANGE; gBattleMons[battlerId].status1 = STATUS1_BURN; BattleScriptExecute(BattleScript_FlameOrb); RecordItemEffectBattle(battlerId, battlerHoldEffect); } break; case HOLD_EFFECT_STICKY_BARB: // Not an orb per se, but similar effect, and needs to NOT activate with pickpocket if (battlerAbility != ABILITY_MAGIC_GUARD) { gBattleMoveDamage = gBattleMons[battlerId].maxHP / 8; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptExecute(BattleScript_ItemHurtEnd2); effect = ITEM_HP_CHANGE; RecordItemEffectBattle(battlerId, battlerHoldEffect); PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); } break; } if (effect == ITEM_STATUS_CHANGE) { gActiveBattler = battlerId; BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battlerId].status1); MarkBattlerForControllerExec(gActiveBattler); } } break; } // Berry was successfully used on a Pokemon. if (effect && (gLastUsedItem >= FIRST_BERRY_INDEX && gLastUsedItem <= LAST_BERRY_INDEX)) gBattleStruct->ateBerry[battlerId & BIT_SIDE] |= gBitTable[gBattlerPartyIndexes[battlerId]]; 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](); } u32 SetRandomTarget(u32 battlerId) { u32 target; static const u8 targets[2][2] = { [B_SIDE_PLAYER] = {B_POSITION_OPPONENT_LEFT, B_POSITION_OPPONENT_RIGHT}, [B_SIDE_OPPONENT] = {B_POSITION_PLAYER_LEFT, B_POSITION_PLAYER_RIGHT}, }; if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { target = GetBattlerAtPosition(targets[GetBattlerSide(battlerId)][Random() % 2]); if (!IsBattlerAlive(target)) target ^= BIT_FLANK; } else { target = GetBattlerAtPosition(targets[GetBattlerSide(battlerId)][0]); } return target; } u32 GetMoveTarget(u16 move, u8 setTarget) { u8 targetBattler = 0; u32 i, moveTarget, side; if (setTarget != NO_TARGET_OVERRIDE) moveTarget = setTarget - 1; else moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move); // Special cases if (move == MOVE_CURSE && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) moveTarget = MOVE_TARGET_USER; switch (moveTarget) { case MOVE_TARGET_SELECTED: side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker)); if (IsAffectedByFollowMe(gBattlerAttacker, side, move)) { targetBattler = gSideTimers[side].followmeTarget; } else { targetBattler = SetRandomTarget(gBattlerAttacker); if (gBattleMoves[move].type == TYPE_ELECTRIC && IsAbilityOnOpposingSide(gBattlerAttacker, ABILITY_LIGHTNING_ROD) && GetBattlerAbility(targetBattler) != ABILITY_LIGHTNING_ROD) { targetBattler ^= BIT_FLANK; RecordAbilityBattle(targetBattler, gBattleMons[targetBattler].ability); gSpecialStatuses[targetBattler].lightningRodRedirected = TRUE; } else if (gBattleMoves[move].type == TYPE_WATER && IsAbilityOnOpposingSide(gBattlerAttacker, ABILITY_STORM_DRAIN) && GetBattlerAbility(targetBattler) != ABILITY_STORM_DRAIN) { targetBattler ^= BIT_FLANK; RecordAbilityBattle(targetBattler, gBattleMons[targetBattler].ability); gSpecialStatuses[targetBattler].stormDrainRedirected = TRUE; } } break; case MOVE_TARGET_DEPENDS: case MOVE_TARGET_BOTH: case MOVE_TARGET_FOES_AND_ALLY: case MOVE_TARGET_OPPONENTS_FIELD: targetBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GET_BATTLER_SIDE(gBattlerAttacker))); if (!IsBattlerAlive(targetBattler)) targetBattler ^= BIT_FLANK; break; case MOVE_TARGET_RANDOM: side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker)); if (IsAffectedByFollowMe(gBattlerAttacker, side, move)) targetBattler = gSideTimers[side].followmeTarget; else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && moveTarget & MOVE_TARGET_RANDOM) targetBattler = SetRandomTarget(gBattlerAttacker); else targetBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GET_BATTLER_SIDE(gBattlerAttacker))); break; case MOVE_TARGET_USER_OR_SELECTED: case MOVE_TARGET_USER: default: targetBattler = gBattlerAttacker; break; case MOVE_TARGET_ALLY: if (IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))) targetBattler = BATTLE_PARTNER(gBattlerAttacker); else targetBattler = gBattlerAttacker; break; } *(gBattleStruct->moveTarget + gBattlerAttacker) = targetBattler; return targetBattler; } static bool32 IsBattlerModernFatefulEncounter(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_MODERN_FATEFUL_ENCOUNTER, NULL); } u8 IsMonDisobedient(void) { s32 rnd; s32 calc; u8 obedienceLevel = 0; u8 levelReferenced; if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) return 0; if (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT) return 0; if (IsBattlerModernFatefulEncounter(gBattlerAttacker)) // only false if illegal 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 B_OBEDIENCE_MECHANICS < GEN_8 if (!IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName)) return 0; #endif 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 B_OBEDIENCE_MECHANICS >= GEN_8 if (!IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName)) levelReferenced = gBattleMons[gBattlerAttacker].metLevel; else #else if (gBattleMons[gBattlerAttacker].level <= obedienceLevel) #endif levelReferenced = gBattleMons[gBattlerAttacker].level; if (levelReferenced <= obedienceLevel) return 0; rnd = (Random() & 255); calc = (levelReferenced + 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_IgnoresWhileAsleep; return 1; } rnd = (Random() & 255); calc = (levelReferenced + obedienceLevel) * rnd >> 8; if (calc < obedienceLevel) { calc = CheckMoveLimitations(gBattlerAttacker, gBitTable[gCurrMovePos], MOVE_LIMITATIONS_ALL); if (calc == 0xF) // all moves cannot be used { // Randomly select, then print a disobedient string // B_MSG_LOAFING, B_MSG_WONT_OBEY, B_MSG_TURNED_AWAY, or B_MSG_PRETEND_NOT_NOTICE gBattleCommunication[MULTISTRING_CHOOSER] = Random() & (NUM_LOAF_STRINGS - 1); gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; return 1; } else // use a random move { do { gCurrMovePos = gChosenMovePos = Random() & (MAX_MON_MOVES - 1); } while (gBitTable[gCurrMovePos] & calc); gCalledMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos]; gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove; gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); gHitMarker |= HITMARKER_DISOBEDIENT_MOVE; return 2; } } else { obedienceLevel = levelReferenced - obedienceLevel; calc = (Random() & 255); if (calc < obedienceLevel && CanSleep(gBattlerAttacker)) { // 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, TRUE); gBattlerTarget = gBattlerAttacker; gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; return 2; } else { // Randomly select, then print a disobedient string // B_MSG_LOAFING, B_MSG_WONT_OBEY, B_MSG_TURNED_AWAY, or B_MSG_PRETEND_NOT_NOTICE gBattleCommunication[MULTISTRING_CHOOSER] = Random() & (NUM_LOAF_STRINGS - 1); gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; return 1; } } } 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 (GetBattlerAbility(battlerId) == ABILITY_KLUTZ) return HOLD_EFFECT_NONE; } gPotentialItemEffectBattler = battlerId; #if DEBUG_BATTLE_MENU == TRUE if (gBattleStruct->debugHoldEffects[battlerId] != 0 && gBattleMons[battlerId].item) return gBattleStruct->debugHoldEffects[battlerId]; else #endif if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY_E_READER) return gEnigmaBerries[battlerId].holdEffect; else return ItemId_GetHoldEffect(gBattleMons[battlerId].item); } static u32 GetBattlerItemHoldEffectParam(u8 battlerId, u16 item) { if (item == ITEM_ENIGMA_BERRY_E_READER) return gEnigmaBerries[battlerId].holdEffectParam; else return ItemId_GetHoldEffectParam(item); } u32 GetBattlerHoldEffectParam(u8 battlerId) { if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY_E_READER) return gEnigmaBerries[battlerId].holdEffectParam; else return ItemId_GetHoldEffectParam(gBattleMons[battlerId].item); } bool32 IsMoveMakingContact(u16 move, u8 battlerAtk) { u16 atkHoldEffect = GetBattlerHoldEffect(battlerAtk, TRUE); if (!(gBattleMoves[move].flags & FLAG_MAKES_CONTACT)) { if (gBattleMoves[move].effect == EFFECT_SHELL_SIDE_ARM && gBattleStruct->swapDamageCategory) return TRUE; else return FALSE; } else if ((atkHoldEffect == HOLD_EFFECT_PUNCHING_GLOVE && (gBattleMoves[move].flags & FLAG_IRON_FIST_BOOST)) || atkHoldEffect == HOLD_EFFECT_PROTECTIVE_PADS || GetBattlerAbility(battlerAtk) == ABILITY_LONG_REACH) { return FALSE; } else { return TRUE; } } bool32 IsBattlerProtected(u8 battlerId, u16 move) { // Decorate bypasses protect and detect, but not crafty shield if (move == MOVE_DECORATE) { if (gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_CRAFTY_SHIELD) return TRUE; else if (gProtectStructs[battlerId].protected) return FALSE; } if (move == MOVE_TEATIME) { return FALSE; } // Z-Moves and Max Moves bypass protection if (gBattleStruct->zmove.active) { return FALSE; } // Protective Pads doesn't stop Unseen Fist from bypassing Protect effects, so IsMoveMakingContact() isn't used here. // This means extra logic is needed to handle Shell Side Arm. if (GetBattlerAbility(gBattlerAttacker) == ABILITY_UNSEEN_FIST && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT || (gBattleMoves[move].effect == EFFECT_SHELL_SIDE_ARM && gBattleStruct->swapDamageCategory))) return FALSE; else if (!(gBattleMoves[move].flags & FLAG_PROTECT_AFFECTED)) return FALSE; else if (gBattleMoves[move].effect == MOVE_EFFECT_FEINT) return FALSE; else if (gProtectStructs[battlerId].protected) return TRUE; else if (gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_WIDE_GUARD && GetBattlerMoveTargetType(gBattlerAttacker, move) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)) return TRUE; else if (gProtectStructs[battlerId].banefulBunkered) return TRUE; else if ((gProtectStructs[battlerId].obstructed || gProtectStructs[battlerId].silkTrapped) && !IS_MOVE_STATUS(move)) return TRUE; else if (gProtectStructs[battlerId].spikyShielded) return TRUE; else if (gProtectStructs[battlerId].kingsShielded && gBattleMoves[move].power != 0) return TRUE; else if (gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_QUICK_GUARD && GetChosenMovePriority(gBattlerAttacker) > 0) return TRUE; else if (gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_CRAFTY_SHIELD && IS_MOVE_STATUS(move)) return TRUE; else if (gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_MAT_BLOCK && !IS_MOVE_STATUS(move)) return TRUE; else return FALSE; } // Only called directly when calculating damage type effectiveness static bool32 IsBattlerGrounded2(u8 battlerId, bool32 considerInverse) { if (GetBattlerHoldEffect(battlerId, TRUE) == HOLD_EFFECT_IRON_BALL) return TRUE; if (gFieldStatuses & STATUS_FIELD_GRAVITY) return TRUE; #if B_ROOTED_GROUNDING >= GEN_4 if (gStatuses3[battlerId] & STATUS3_ROOTED) return TRUE; #endif if (gStatuses3[battlerId] & STATUS3_SMACKED_DOWN) return TRUE; if (gStatuses3[battlerId] & STATUS3_TELEKINESIS) return FALSE; if (gStatuses3[battlerId] & STATUS3_MAGNET_RISE) return FALSE; if (GetBattlerHoldEffect(battlerId, TRUE) == HOLD_EFFECT_AIR_BALLOON) return FALSE; if (GetBattlerAbility(battlerId) == ABILITY_LEVITATE) return FALSE; if (IS_BATTLER_OF_TYPE(battlerId, TYPE_FLYING) && (!considerInverse || !FlagGet(B_FLAG_INVERSE_BATTLE))) return FALSE; return TRUE; } bool32 IsBattlerGrounded(u8 battlerId) { IsBattlerGrounded2(battlerId, FALSE); } 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 < MAX_MON_MOVES; i++) { if (battleMon->moves[i] == move) break; } return i; } u32 GetBattlerWeight(u8 battlerId) { u32 i; 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; for (i = 0; i < gDisableStructs[battlerId].autotomizeCount; i++) { if (weight > 1000) { weight -= 1000; } else if (weight <= 1000) { weight = 1; break; } } if (weight == 0) weight = 1; return weight; } u32 CountBattlerStatIncreases(u8 battlerId, bool32 countEvasionAcc) { u32 i; u32 count = 0; for (i = 0; i < NUM_BATTLE_STATS; i++) { if ((i == STAT_ACC || i == STAT_EVASION) && !countEvasionAcc) continue; if (gBattleMons[battlerId].statStages[i] > DEFAULT_STAT_STAGE) // Stat is increased. count += gBattleMons[battlerId].statStages[i] - DEFAULT_STAT_STAGE; } return count; } u32 GetMoveTargetCount(u16 move, u8 battlerAtk, u8 battlerDef) { switch (GetBattlerMoveTargetType(gBattlerAttacker, move)) { 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 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 u8 sSpeedDiffPowerTable[] = {40, 60, 80, 120, 150}; static const u8 sHeatCrashPowerTable[] = {40, 40, 60, 80, 100, 120}; static const u8 sTrumpCardPowerTable[] = {200, 80, 60, 50, 40}; const struct TypePower gNaturalGiftTable[] = { [ITEM_TO_BERRY(ITEM_CHERI_BERRY)] = {TYPE_FIRE, 80}, [ITEM_TO_BERRY(ITEM_CHESTO_BERRY)] = {TYPE_WATER, 80}, [ITEM_TO_BERRY(ITEM_PECHA_BERRY)] = {TYPE_ELECTRIC, 80}, [ITEM_TO_BERRY(ITEM_RAWST_BERRY)] = {TYPE_GRASS, 80}, [ITEM_TO_BERRY(ITEM_ASPEAR_BERRY)] = {TYPE_ICE, 80}, [ITEM_TO_BERRY(ITEM_LEPPA_BERRY)] = {TYPE_FIGHTING, 80}, [ITEM_TO_BERRY(ITEM_ORAN_BERRY)] = {TYPE_POISON, 80}, [ITEM_TO_BERRY(ITEM_PERSIM_BERRY)] = {TYPE_GROUND, 80}, [ITEM_TO_BERRY(ITEM_LUM_BERRY)] = {TYPE_FLYING, 80}, [ITEM_TO_BERRY(ITEM_SITRUS_BERRY)] = {TYPE_PSYCHIC, 80}, [ITEM_TO_BERRY(ITEM_FIGY_BERRY)] = {TYPE_BUG, 80}, [ITEM_TO_BERRY(ITEM_WIKI_BERRY)] = {TYPE_ROCK, 80}, [ITEM_TO_BERRY(ITEM_MAGO_BERRY)] = {TYPE_GHOST, 80}, [ITEM_TO_BERRY(ITEM_AGUAV_BERRY)] = {TYPE_DRAGON, 80}, [ITEM_TO_BERRY(ITEM_IAPAPA_BERRY)] = {TYPE_DARK, 80}, [ITEM_TO_BERRY(ITEM_RAZZ_BERRY)] = {TYPE_STEEL, 80}, [ITEM_TO_BERRY(ITEM_OCCA_BERRY)] = {TYPE_FIRE, 80}, [ITEM_TO_BERRY(ITEM_PASSHO_BERRY)] = {TYPE_WATER, 80}, [ITEM_TO_BERRY(ITEM_WACAN_BERRY)] = {TYPE_ELECTRIC, 80}, [ITEM_TO_BERRY(ITEM_RINDO_BERRY)] = {TYPE_GRASS, 80}, [ITEM_TO_BERRY(ITEM_YACHE_BERRY)] = {TYPE_ICE, 80}, [ITEM_TO_BERRY(ITEM_CHOPLE_BERRY)] = {TYPE_FIGHTING, 80}, [ITEM_TO_BERRY(ITEM_KEBIA_BERRY)] = {TYPE_POISON, 80}, [ITEM_TO_BERRY(ITEM_SHUCA_BERRY)] = {TYPE_GROUND, 80}, [ITEM_TO_BERRY(ITEM_COBA_BERRY)] = {TYPE_FLYING, 80}, [ITEM_TO_BERRY(ITEM_PAYAPA_BERRY)] = {TYPE_PSYCHIC, 80}, [ITEM_TO_BERRY(ITEM_TANGA_BERRY)] = {TYPE_BUG, 80}, [ITEM_TO_BERRY(ITEM_CHARTI_BERRY)] = {TYPE_ROCK, 80}, [ITEM_TO_BERRY(ITEM_KASIB_BERRY)] = {TYPE_GHOST, 80}, [ITEM_TO_BERRY(ITEM_HABAN_BERRY)] = {TYPE_DRAGON, 80}, [ITEM_TO_BERRY(ITEM_COLBUR_BERRY)] = {TYPE_DARK, 80}, [ITEM_TO_BERRY(ITEM_BABIRI_BERRY)] = {TYPE_STEEL, 80}, [ITEM_TO_BERRY(ITEM_CHILAN_BERRY)] = {TYPE_NORMAL, 80}, [ITEM_TO_BERRY(ITEM_ROSELI_BERRY)] = {TYPE_FAIRY, 80}, [ITEM_TO_BERRY(ITEM_BLUK_BERRY)] = {TYPE_FIRE, 90}, [ITEM_TO_BERRY(ITEM_NANAB_BERRY)] = {TYPE_WATER, 90}, [ITEM_TO_BERRY(ITEM_WEPEAR_BERRY)] = {TYPE_ELECTRIC, 90}, [ITEM_TO_BERRY(ITEM_PINAP_BERRY)] = {TYPE_GRASS, 90}, [ITEM_TO_BERRY(ITEM_POMEG_BERRY)] = {TYPE_ICE, 90}, [ITEM_TO_BERRY(ITEM_KELPSY_BERRY)] = {TYPE_FIGHTING, 90}, [ITEM_TO_BERRY(ITEM_QUALOT_BERRY)] = {TYPE_POISON, 90}, [ITEM_TO_BERRY(ITEM_HONDEW_BERRY)] = {TYPE_GROUND, 90}, [ITEM_TO_BERRY(ITEM_GREPA_BERRY)] = {TYPE_FLYING, 90}, [ITEM_TO_BERRY(ITEM_TAMATO_BERRY)] = {TYPE_PSYCHIC, 90}, [ITEM_TO_BERRY(ITEM_CORNN_BERRY)] = {TYPE_BUG, 90}, [ITEM_TO_BERRY(ITEM_MAGOST_BERRY)] = {TYPE_ROCK, 90}, [ITEM_TO_BERRY(ITEM_RABUTA_BERRY)] = {TYPE_GHOST, 90}, [ITEM_TO_BERRY(ITEM_NOMEL_BERRY)] = {TYPE_DRAGON, 90}, [ITEM_TO_BERRY(ITEM_SPELON_BERRY)] = {TYPE_DARK, 90}, [ITEM_TO_BERRY(ITEM_PAMTRE_BERRY)] = {TYPE_STEEL, 90}, [ITEM_TO_BERRY(ITEM_WATMEL_BERRY)] = {TYPE_FIRE, 100}, [ITEM_TO_BERRY(ITEM_DURIN_BERRY)] = {TYPE_WATER, 100}, [ITEM_TO_BERRY(ITEM_BELUE_BERRY)] = {TYPE_ELECTRIC, 100}, [ITEM_TO_BERRY(ITEM_LIECHI_BERRY)] = {TYPE_GRASS, 100}, [ITEM_TO_BERRY(ITEM_GANLON_BERRY)] = {TYPE_ICE, 100}, [ITEM_TO_BERRY(ITEM_SALAC_BERRY)] = {TYPE_FIGHTING, 100}, [ITEM_TO_BERRY(ITEM_PETAYA_BERRY)] = {TYPE_POISON, 100}, [ITEM_TO_BERRY(ITEM_APICOT_BERRY)] = {TYPE_GROUND, 100}, [ITEM_TO_BERRY(ITEM_LANSAT_BERRY)] = {TYPE_FLYING, 100}, [ITEM_TO_BERRY(ITEM_STARF_BERRY)] = {TYPE_PSYCHIC, 100}, [ITEM_TO_BERRY(ITEM_ENIGMA_BERRY)] = {TYPE_BUG, 100}, [ITEM_TO_BERRY(ITEM_MICLE_BERRY)] = {TYPE_ROCK, 100}, [ITEM_TO_BERRY(ITEM_CUSTAP_BERRY)] = {TYPE_GHOST, 100}, [ITEM_TO_BERRY(ITEM_JABOCA_BERRY)] = {TYPE_DRAGON, 100}, [ITEM_TO_BERRY(ITEM_ROWAP_BERRY)] = {TYPE_DARK, 100}, [ITEM_TO_BERRY(ITEM_KEE_BERRY)] = {TYPE_FAIRY, 100}, [ITEM_TO_BERRY(ITEM_MARANGA_BERRY)] = {TYPE_DARK, 100}, }; static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) { u32 i; u16 basePower = gBattleMoves[move].power; u32 weight, hpFraction, speed; if (gBattleStruct->zmove.active) return GetZMovePower(gBattleStruct->zmove.baseMoves[battlerAtk]); switch (gBattleMoves[move].effect) { case EFFECT_PLEDGE: // todo break; case EFFECT_FLING: basePower = GetFlingPowerFromItemId(gBattleMons[battlerAtk].item); 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 * (MAX_FRIENDSHIP - 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].rolloutTimer); 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 *= (4 - gMultiHitCounter); 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 (gBattleWeather & B_WEATHER_ANY && WEATHER_HAS_EFFECT) basePower *= 2; break; case EFFECT_PURSUIT: if (gActionsByTurnOrder[GetBattlerTurnOrderNum(battlerDef)] == B_ACTION_SWITCH) basePower *= 2; break; case EFFECT_NATURAL_GIFT: basePower = gNaturalGiftTable[ITEM_TO_BERRY(gBattleMons[battlerAtk].item)].power; break; case EFFECT_WAKE_UP_SLAP: if (gBattleMons[battlerDef].status1 & STATUS1_SLEEP || GetBattlerAbility(battlerDef) == ABILITY_COMATOSE) 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 || GetBattlerAbility(battlerDef) == ABILITY_COMATOSE) basePower *= 2; break; case EFFECT_ASSURANCE: if (gProtectStructs[battlerDef].physicalDmg != 0 || gProtectStructs[battlerDef].specialDmg != 0 || gProtectStructs[battlerDef].confusionSelfDmg) basePower *= 2; break; case EFFECT_TRUMP_CARD: i = GetBattleMonMoveSlot(&gBattleMons[battlerAtk], move); if (i != 4) { if (gBattleMons[battlerAtk].pp[i] >= ARRAY_COUNT(sTrumpCardPowerTable)) basePower = sTrumpCardPowerTable[ARRAY_COUNT(sTrumpCardPowerTable) - 1]; else basePower = sTrumpCardPowerTable[gBattleMons[battlerAtk].pp[i]]; } break; case EFFECT_ACROBATICS: if (gBattleMons[battlerAtk].item == ITEM_NONE // Edge case, because removal of items happens after damage calculation. || (gSpecialStatuses[battlerAtk].gemBoost && GetBattlerHoldEffect(battlerAtk, FALSE) == HOLD_EFFECT_GEMS)) 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 >= ARRAY_COUNT(sHeatCrashPowerTable)) basePower = sHeatCrashPowerTable[ARRAY_COUNT(sHeatCrashPowerTable) - 1]; else basePower = sHeatCrashPowerTable[weight]; break; case EFFECT_PUNISHMENT: basePower = 60 + (CountBattlerStatIncreases(battlerDef, FALSE) * 20); if (basePower > 200) basePower = 200; break; case EFFECT_STORED_POWER: basePower += (CountBattlerStatIncreases(battlerAtk, TRUE) * 20); break; case EFFECT_ELECTRO_BALL: speed = GetBattlerTotalSpeedStat(battlerAtk) / GetBattlerTotalSpeedStat(battlerDef); if (speed >= ARRAY_COUNT(sSpeedDiffPowerTable)) speed = ARRAY_COUNT(sSpeedDiffPowerTable) - 1; basePower = sSpeedDiffPowerTable[speed]; break; case EFFECT_GYRO_BALL: basePower = ((25 * GetBattlerTotalSpeedStat(battlerDef)) / GetBattlerTotalSpeedStat(battlerAtk)) + 1; if (basePower > 150) basePower = 150; break; case EFFECT_ECHOED_VOICE: // gBattleStruct->sameMoveTurns incremented in ppreduce if (gBattleStruct->sameMoveTurns[battlerAtk] != 0) { basePower += (basePower * gBattleStruct->sameMoveTurns[battlerAtk]); if (basePower > 200) basePower = 200; } break; case EFFECT_PAYBACK: if (GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef) #if B_PAYBACK_SWITCH_BOOST >= GEN_5 && (gDisableStructs[battlerDef].isFirstTurn != 2) #endif ) basePower *= 2; break; case EFFECT_BOLT_BEAK: if (GetBattlerTurnOrderNum(battlerAtk) < GetBattlerTurnOrderNum(battlerDef) || gDisableStructs[battlerDef].isFirstTurn == 2) basePower *= 2; break; case EFFECT_ROUND: for (i = 0; i < gBattlersCount; i++) { if (i != battlerAtk && IsBattlerAlive(i) && gLastMoves[i] == MOVE_ROUND) { basePower *= 2; break; } } break; case EFFECT_FUSION_COMBO: if (gBattleMoves[gLastUsedMove].effect == EFFECT_FUSION_COMBO && move != gLastUsedMove) basePower *= 2; break; case EFFECT_LASH_OUT: if (gProtectStructs[battlerAtk].statFell) basePower *= 2; break; case EFFECT_EXPLOSION: if (move == MOVE_MISTY_EXPLOSION && gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN && IsBattlerGrounded(battlerAtk)) MulModifier(&basePower, UQ_4_12(1.5)); break; case EFFECT_DYNAMAX_DOUBLE_DMG: #ifdef B_DYNAMAX if (IsDynamaxed(battlerDef)) basePower *= 2; #endif break; case EFFECT_HIDDEN_POWER: { #if B_HIDDEN_POWER_DMG < GEN_6 u8 powerBits; powerBits = ((gBattleMons[battlerAtk].hpIV & 2) >> 1) | ((gBattleMons[battlerAtk].attackIV & 2) << 0) | ((gBattleMons[battlerAtk].defenseIV & 2) << 1) | ((gBattleMons[battlerAtk].speedIV & 2) << 2) | ((gBattleMons[battlerAtk].spAttackIV & 2) << 3) | ((gBattleMons[battlerAtk].spDefenseIV & 2) << 4); basePower = (40 * powerBits) / 63 + 30; #endif break; } case EFFECT_GRAV_APPLE: if (gFieldStatuses & STATUS_FIELD_GRAVITY) MulModifier(&basePower, UQ_4_12(1.5)); break; case EFFECT_TERRAIN_PULSE: if ((gFieldStatuses & STATUS_FIELD_TERRAIN_ANY) && IsBattlerGrounded(battlerAtk)) basePower *= 2; break; case EFFECT_EXPANDING_FORCE: if (IsBattlerTerrainAffected(battlerAtk, STATUS_FIELD_PSYCHIC_TERRAIN)) MulModifier(&basePower, UQ_4_12(1.5)); break; case EFFECT_RISING_VOLTAGE: if (IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_ELECTRIC_TERRAIN)) basePower *= 2; break; case EFFECT_BEAT_UP: #if B_BEAT_UP >= GEN_5 basePower = CalcBeatUpPower(); #endif break; case EFFECT_PSYBLADE: if (IsBattlerTerrainAffected(battlerAtk, STATUS_FIELD_ELECTRIC_TERRAIN)) MulModifier(&basePower, UQ_4_12(1.5)); break; } // Move-specific base power changes switch (move) { case MOVE_WATER_SHURIKEN: if (gBattleMons[battlerAtk].species == SPECIES_GRENINJA_ASH) basePower = 20; break; } if (basePower == 0) basePower = 1; return basePower; } static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, bool32 updateFlags) { u32 i; u32 holdEffectAtk, holdEffectParamAtk; u16 basePower = CalcMoveBasePower(move, battlerAtk, battlerDef); u16 holdEffectModifier; u16 modifier = UQ_4_12(1.0); u32 atkSide = GET_BATTLER_SIDE(battlerAtk); u16 atkAbility = GetBattlerAbility(battlerAtk); u16 defAbility = GetBattlerAbility(battlerDef); // attacker's abilities switch (atkAbility) { 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) && gBattleWeather & B_WEATHER_SANDSTORM && WEATHER_HAS_EFFECT) 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 (IsMoveMakingContact(move, battlerAtk)) 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; case ABILITY_PIXILATE: if (moveType == TYPE_FAIRY && gBattleStruct->ateBoost[battlerAtk]) MulModifier(&modifier, UQ_4_12(1.2)); break; case ABILITY_GALVANIZE: if (moveType == TYPE_ELECTRIC && gBattleStruct->ateBoost[battlerAtk]) MulModifier(&modifier, UQ_4_12(1.2)); break; case ABILITY_REFRIGERATE: if (moveType == TYPE_ICE && gBattleStruct->ateBoost[battlerAtk]) MulModifier(&modifier, UQ_4_12(1.2)); break; case ABILITY_AERILATE: if (moveType == TYPE_FLYING && gBattleStruct->ateBoost[battlerAtk]) MulModifier(&modifier, UQ_4_12(1.2)); break; case ABILITY_NORMALIZE: if (moveType == TYPE_NORMAL && gBattleStruct->ateBoost[battlerAtk]) MulModifier(&modifier, UQ_4_12(1.2)); break; case ABILITY_PUNK_ROCK: if (gBattleMoves[move].flags & FLAG_SOUND) MulModifier(&modifier, UQ_4_12(1.3)); break; case ABILITY_STEELY_SPIRIT: if (moveType == TYPE_STEEL) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_TRANSISTOR: if (moveType == TYPE_ELECTRIC) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_DRAGONS_MAW: if (moveType == TYPE_DRAGON) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_GORILLA_TACTICS: if (IS_MOVE_PHYSICAL(move)) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_ROCKY_PAYLOAD: if (moveType == TYPE_ROCK) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_PROTOSYNTHESIS: { u8 atkHighestStat = GetHighestStatId(battlerAtk); if (gBattleWeather & B_WEATHER_SUN && WEATHER_HAS_EFFECT && (atkHighestStat == STAT_ATK || atkHighestStat == STAT_SPATK)) MulModifier(&modifier, UQ_4_12(1.3)); } break; case ABILITY_QUARK_DRIVE: { u8 atkHighestStat = GetHighestStatId(battlerAtk); if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && (atkHighestStat == STAT_ATK || atkHighestStat == STAT_SPATK)) MulModifier(&modifier, UQ_4_12(1.3)); } break; case ABILITY_ORICHALCUM_PULSE: if (gBattleWeather & B_WEATHER_SUN && WEATHER_HAS_EFFECT) MulModifier(&modifier, UQ_4_12(1.3)); break; case ABILITY_HADRON_ENGINE: if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) MulModifier(&modifier, UQ_4_12(1.3)); break; case ABILITY_SHARPNESS: if (gBattleMoves[move].flags & FLAG_SLICING_MOVE) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_SUPREME_OVERLORD: MulModifier(&modifier, gBattleStruct->supremeOverlordModifier[battlerAtk]); break; } // field abilities if ((IsAbilityOnField(ABILITY_DARK_AURA) && moveType == TYPE_DARK) || (IsAbilityOnField(ABILITY_FAIRY_AURA) && moveType == TYPE_FAIRY)) { if (IsAbilityOnField(ABILITY_AURA_BREAK)) MulModifier(&modifier, UQ_4_12(0.75)); else MulModifier(&modifier, UQ_4_12(1.33)); } if (IsAbilityOnField(ABILITY_VESSEL_OF_RUIN) && atkAbility != ABILITY_VESSEL_OF_RUIN && IS_MOVE_SPECIAL(gCurrentMove)) MulModifier(&modifier, UQ_4_12(0.25)); if (IsAbilityOnField(ABILITY_SWORD_OF_RUIN) && defAbility != ABILITY_SWORD_OF_RUIN && IS_MOVE_PHYSICAL(gCurrentMove)) MulModifier(&modifier, UQ_4_12(0.25)); if (IsAbilityOnField(ABILITY_TABLETS_OF_RUIN) && atkAbility != ABILITY_TABLETS_OF_RUIN && IS_MOVE_PHYSICAL(gCurrentMove)) MulModifier(&modifier, UQ_4_12(0.25)); if (IsAbilityOnField(ABILITY_BEADS_OF_RUIN) && defAbility != ABILITY_BEADS_OF_RUIN && IS_MOVE_SPECIAL(gCurrentMove)) MulModifier(&modifier, UQ_4_12(0.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; case ABILITY_POWER_SPOT: MulModifier(&modifier, UQ_4_12(1.3)); break; case ABILITY_STEELY_SPIRIT: if (moveType == TYPE_STEEL) MulModifier(&modifier, UQ_4_12(1.5)); break; } } // target's abilities switch (defAbility) { case ABILITY_HEATPROOF: case ABILITY_WATER_BUBBLE: if (moveType == TYPE_FIRE) { MulModifier(&modifier, UQ_4_12(0.5)); if (updateFlags) RecordAbilityBattle(battlerDef, defAbility); } 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 (updateFlags) RecordAbilityBattle(battlerDef, defAbility); } if (moveType == TYPE_FIRE) MulModifier(&modifier, UQ_4_12(2.0)); break; case ABILITY_PROTOSYNTHESIS: { u8 defHighestStat = GetHighestStatId(battlerDef); if (gBattleWeather & B_WEATHER_SUN && WEATHER_HAS_EFFECT && (defHighestStat == STAT_DEF || defHighestStat == STAT_SPDEF)) MulModifier(&modifier, UQ_4_12(0.7)); } break; case ABILITY_QUARK_DRIVE: { u8 defHighestStat = GetHighestStatId(battlerDef); if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && (defHighestStat == STAT_DEF || defHighestStat == STAT_SPDEF)) MulModifier(&modifier, UQ_4_12(0.7)); } 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 (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_PALKIA && (moveType == TYPE_WATER || moveType == TYPE_DRAGON)) MulModifier(&modifier, holdEffectModifier); break; case HOLD_EFFECT_ADAMANT_ORB: if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_DIALGA && (moveType == TYPE_STEEL || moveType == TYPE_DRAGON)) MulModifier(&modifier, holdEffectModifier); break; case HOLD_EFFECT_GRISEOUS_ORB: if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_GIRATINA && (moveType == TYPE_GHOST || moveType == TYPE_DRAGON)) MulModifier(&modifier, holdEffectModifier); break; case HOLD_EFFECT_SOUL_DEW: #if B_SOUL_DEW_BOOST >= GEN_7 if ((gBattleMons[battlerAtk].species == SPECIES_LATIAS || gBattleMons[battlerAtk].species == SPECIES_LATIOS) && (moveType == TYPE_PSYCHIC || moveType == TYPE_DRAGON)) #else if ((gBattleMons[battlerAtk].species == SPECIES_LATIAS || gBattleMons[battlerAtk].species == SPECIES_LATIOS) && !(gBattleTypeFlags & BATTLE_TYPE_FRONTIER) && IS_MOVE_SPECIAL(move)) #endif MulModifier(&modifier, holdEffectModifier); break; case HOLD_EFFECT_GEMS: if (gSpecialStatuses[battlerAtk].gemBoost && gBattleMons[battlerAtk].item) MulModifier(&modifier, UQ_4_12(1.0) + sPercentToModifier[gSpecialStatuses[battlerAtk].gemParam]); 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; case HOLD_EFFECT_PLATE: if (moveType == ItemId_GetSecondaryId(gBattleMons[battlerAtk].item)) MulModifier(&modifier, holdEffectModifier); break; case HOLD_EFFECT_PUNCHING_GLOVE: if (gBattleMoves[move].flags & FLAG_IRON_FIST_BOOST) MulModifier(&modifier, UQ_4_12(1.1)); 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[battlerDef].hp <= (gBattleMons[battlerDef].maxHP / 2)) MulModifier(&modifier, UQ_4_12(2.0)); break; case EFFECT_BARB_BARRAGE: case EFFECT_VENOSHOCK: if (gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY) MulModifier(&modifier, UQ_4_12(2.0)); break; case EFFECT_RETALIATE: if (gSideTimers[atkSide].retaliateTimer == 1) MulModifier(&modifier, UQ_4_12(2.0)); break; case EFFECT_SOLAR_BEAM: if (IsBattlerWeatherAffected(battlerAtk, (B_WEATHER_HAIL | B_WEATHER_SANDSTORM | B_WEATHER_RAIN))) MulModifier(&modifier, UQ_4_12(0.5)); break; case EFFECT_STOMPING_TANTRUM: if (gBattleStruct->lastMoveFailed & gBitTable[battlerAtk]) MulModifier(&modifier, UQ_4_12(2.0)); break; case EFFECT_BULLDOZE: case EFFECT_MAGNITUDE: case EFFECT_EARTHQUAKE: if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && !(gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE)) MulModifier(&modifier, UQ_4_12(0.5)); break; case EFFECT_KNOCK_OFF: #if B_KNOCK_OFF_DMG >= GEN_6 if (gBattleMons[battlerDef].item != ITEM_NONE && CanBattlerGetOrLoseItem(battlerDef, gBattleMons[battlerDef].item)) MulModifier(&modifier, UQ_4_12(1.5)); #endif break; } #if B_TERRAIN_TYPE_BOOST >= GEN_8 #define TERRAIN_TYPE_BOOST UQ_4_12(1.3) #else #define TERRAIN_TYPE_BOOST UQ_4_12(1.5) #endif // various effects 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) && !(gStatuses3[battlerAtk] & STATUS3_SEMI_INVULNERABLE)) MulModifier(&modifier, TERRAIN_TYPE_BOOST); if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN && moveType == TYPE_DRAGON && IsBattlerGrounded(battlerDef) && !(gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE)) MulModifier(&modifier, UQ_4_12(0.5)); if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && moveType == TYPE_ELECTRIC && IsBattlerGrounded(battlerAtk) && !(gStatuses3[battlerAtk] & STATUS3_SEMI_INVULNERABLE)) MulModifier(&modifier, TERRAIN_TYPE_BOOST); if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN && moveType == TYPE_PSYCHIC && IsBattlerGrounded(battlerAtk) && !(gStatuses3[battlerAtk] & STATUS3_SEMI_INVULNERABLE)) MulModifier(&modifier, TERRAIN_TYPE_BOOST); #if B_SPORT_TURNS >= GEN_6 if ((moveType == TYPE_ELECTRIC && gFieldStatuses & STATUS_FIELD_MUDSPORT) || (moveType == TYPE_FIRE && gFieldStatuses & STATUS_FIELD_WATERSPORT)) #else if ((moveType == TYPE_ELECTRIC && AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, 0, ABILITYEFFECT_MUD_SPORT, 0)) || (moveType == TYPE_FIRE && AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, 0, ABILITYEFFECT_WATER_SPORT, 0))) #endif #if B_SPORT_DMG_REDUCTION >= GEN_5 MulModifier(&modifier, UQ_4_12(0.23)); #else MulModifier(&modifier, UQ_4_12(0.5)); #endif return ApplyModifier(modifier, basePower); } #undef TERRAIN_TYPE_BOOST static u32 CalcAttackStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, bool32 isCrit, bool32 updateFlags) { u8 atkStage; u32 atkStat; u16 modifier; u16 atkBaseSpeciesId; atkBaseSpeciesId = GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species); if (gBattleMoves[move].effect == EFFECT_FOUL_PLAY) { if (IS_MOVE_PHYSICAL(move)) { atkStat = gBattleMons[battlerDef].attack; atkStage = gBattleMons[battlerDef].statStages[STAT_ATK]; } else { atkStat = gBattleMons[battlerDef].spAttack; atkStage = gBattleMons[battlerDef].statStages[STAT_SPATK]; } } else if (gBattleMoves[move].effect == EFFECT_BODY_PRESS) { atkStat = gBattleMons[battlerAtk].defense; atkStage = gBattleMons[battlerAtk].statStages[STAT_DEF]; } else { if (IS_MOVE_PHYSICAL(move)) { atkStat = gBattleMons[battlerAtk].attack; atkStage = gBattleMons[battlerAtk].statStages[STAT_ATK]; } else { atkStat = gBattleMons[battlerAtk].spAttack; atkStage = gBattleMons[battlerAtk].statStages[STAT_SPATK]; } } // critical hits ignore attack stat's stage drops if (isCrit && atkStage < DEFAULT_STAT_STAGE) atkStage = DEFAULT_STAT_STAGE; // pokemon with unaware ignore attack stat changes while taking damage if (GetBattlerAbility(battlerDef) == ABILITY_UNAWARE) atkStage = DEFAULT_STAT_STAGE; 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) && IsBattlerWeatherAffected(battlerAtk, B_WEATHER_SUN)) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_DEFEATIST: if (gBattleMons[battlerAtk].hp <= (gBattleMons[battlerAtk].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; #if B_PLUS_MINUS_INTERACTION >= GEN_5 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; #else case ABILITY_PLUS: if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_MINUS) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_MINUS: if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_PLUS) MulModifier(&modifier, UQ_4_12(1.5)); break; #endif case ABILITY_FLOWER_GIFT: if (gBattleMons[battlerAtk].species == SPECIES_CHERRIM && IsBattlerWeatherAffected(battlerAtk, B_WEATHER_SUN) && IS_MOVE_PHYSICAL(move)) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_HUSTLE: if (IS_MOVE_PHYSICAL(move)) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_STAKEOUT: if (gDisableStructs[battlerDef].isFirstTurn == 2) // just switched in MulModifier(&modifier, UQ_4_12(2.0)); break; case ABILITY_GUTS: if (gBattleMons[battlerAtk].status1 & STATUS1_ANY && 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)); if (updateFlags) RecordAbilityBattle(battlerDef, ABILITY_THICK_FAT); } break; case ABILITY_ICE_SCALES: if (IS_MOVE_SPECIAL(move)) 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 && IsBattlerWeatherAffected(BATTLE_PARTNER(battlerAtk), B_WEATHER_SUN) && IS_MOVE_PHYSICAL(move)) MulModifier(&modifier, UQ_4_12(1.5)); break; } } // attacker's hold effect switch (GetBattlerHoldEffect(battlerAtk, TRUE)) { case HOLD_EFFECT_THICK_CLUB: if ((atkBaseSpeciesId == SPECIES_CUBONE || atkBaseSpeciesId == 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 (atkBaseSpeciesId == 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; } // The offensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the 1st badge and 7th badges. // Having the 1st badge boosts physical attack while having the 7th badge boosts special attack. if (ShouldGetStatBadgeBoost(FLAG_BADGE01_GET, battlerAtk) && IS_MOVE_PHYSICAL(move)) MulModifier(&modifier, UQ_4_12(1.1)); if (ShouldGetStatBadgeBoost(FLAG_BADGE07_GET, battlerAtk) && IS_MOVE_SPECIAL(move)) MulModifier(&modifier, UQ_4_12(1.1)); return ApplyModifier(modifier, atkStat); } static bool32 CanEvolve(u32 species) { u32 i; for (i = 0; i < EVOS_PER_MON; i++) { if (gEvolutionTable[species][i].method && gEvolutionTable[species][i].method != EVO_MEGA_EVOLUTION && gEvolutionTable[species][i].method != EVO_MOVE_MEGA_EVOLUTION && gEvolutionTable[species][i].method != EVO_PRIMAL_REVERSION) return TRUE; } return FALSE; } static u32 CalcDefenseStat(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, bool32 isCrit, bool32 updateFlags) { 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; } #if B_EXPLOSION_DEFENSE <= GEN_4 // Self-destruct / Explosion cut defense in half if (gBattleMoves[gCurrentMove].effect == EFFECT_EXPLOSION) defStat /= 2; #endif // critical hits ignore positive stat changes if (isCrit && defStage > DEFAULT_STAT_STAGE) defStage = DEFAULT_STAT_STAGE; // pokemon with unaware ignore defense stat changes while dealing damage if (GetBattlerAbility(battlerAtk) == ABILITY_UNAWARE) defStage = DEFAULT_STAT_STAGE; // certain moves also ignore stat changes if (gBattleMoves[move].flags & FLAG_STAT_STAGES_IGNORED) defStage = DEFAULT_STAT_STAGE; 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)); if (updateFlags) RecordAbilityBattle(battlerDef, ABILITY_MARVEL_SCALE); } break; case ABILITY_FUR_COAT: if (usesDefStat) { MulModifier(&modifier, UQ_4_12(2.0)); if (updateFlags) RecordAbilityBattle(battlerDef, ABILITY_FUR_COAT); } break; case ABILITY_GRASS_PELT: if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && usesDefStat) { MulModifier(&modifier, UQ_4_12(1.5)); if (updateFlags) RecordAbilityBattle(battlerDef, ABILITY_GRASS_PELT); } break; case ABILITY_FLOWER_GIFT: if (gBattleMons[battlerDef].species == SPECIES_CHERRIM && IsBattlerWeatherAffected(battlerDef, B_WEATHER_SUN) && !usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); break; case ABILITY_PUNK_ROCK: if (gBattleMoves[move].flags & FLAG_SOUND) MulModifier(&modifier, UQ_4_12(2.0)); break; case ABILITY_PURIFYING_SALT: if (gBattleMoves[move].type == TYPE_GHOST) MulModifier(&modifier, UQ_4_12(2.0)); 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 && IsBattlerWeatherAffected(BATTLE_PARTNER(battlerDef), B_WEATHER_SUN) && !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: if (CanEvolve(gBattleMons[battlerDef].species)) MulModifier(&modifier, UQ_4_12(1.5)); break; case HOLD_EFFECT_ASSAULT_VEST: if (!usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); break; #if B_SOUL_DEW_BOOST <= GEN_6 case HOLD_EFFECT_SOUL_DEW: if ((gBattleMons[battlerDef].species == SPECIES_LATIAS || gBattleMons[battlerDef].species == SPECIES_LATIOS) && !(gBattleTypeFlags & BATTLE_TYPE_FRONTIER) && !usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); break; #endif } // sandstorm sp.def boost for rock types if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ROCK) && gBattleWeather & B_WEATHER_SANDSTORM && WEATHER_HAS_EFFECT && !usesDefStat) MulModifier(&modifier, UQ_4_12(1.5)); // The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the 5th badge and 7th badges. // Having the 5th badge boosts physical defense while having the 7th badge boosts special defense. if (ShouldGetStatBadgeBoost(FLAG_BADGE05_GET, battlerDef) && IS_MOVE_PHYSICAL(move)) MulModifier(&modifier, UQ_4_12(1.1)); if (ShouldGetStatBadgeBoost(FLAG_BADGE07_GET, battlerDef) && IS_MOVE_SPECIAL(move)) MulModifier(&modifier, UQ_4_12(1.1)); return ApplyModifier(modifier, defStat); } static u32 CalcFinalDmg(u32 dmg, u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, u16 typeEffectivenessModifier, bool32 isCrit, bool32 updateFlags) { u32 percentBoost; u32 abilityAtk = GetBattlerAbility(battlerAtk); u32 abilityDef = GetBattlerAbility(battlerDef); u32 defSide = GET_BATTLER_SIDE(battlerDef); u16 finalModifier = UQ_4_12(1.0); u16 itemDef = gBattleMons[battlerDef].item; // check multiple targets in double battle if (GetMoveTargetCount(move, battlerAtk, battlerDef) >= 2) #if B_MULTIPLE_TARGETS_DMG >= GEN_4 MulModifier(&finalModifier, UQ_4_12(0.75)); #else MulModifier(&finalModifier, UQ_4_12(0.5)); #endif // take type effectiveness MulModifier(&finalModifier, typeEffectivenessModifier); // check crit if (isCrit) #if B_CRIT_MULTIPLIER >= GEN_6 dmg = ApplyModifier(UQ_4_12(1.5), dmg); #else dmg = ApplyModifier(UQ_4_12(2.0), dmg); #endif // check burn if (gBattleMons[battlerAtk].status1 & STATUS1_BURN && IS_MOVE_PHYSICAL(move) #if B_BURN_FACADE_DMG >= GEN_6 && gBattleMoves[move].effect != EFFECT_FACADE #endif && abilityAtk != ABILITY_GUTS) dmg = ApplyModifier(UQ_4_12(0.5), dmg); // check frostbite if (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE && !IS_MOVE_PHYSICAL(move) #if B_BURN_FACADE_DMG >= GEN_6 && gBattleMoves[move].effect != EFFECT_FACADE #endif && abilityAtk != ABILITY_GUTS) dmg = ApplyModifier(UQ_4_12(0.5), dmg); // check sunny/rain weather if (IsBattlerWeatherAffected(battlerAtk, B_WEATHER_RAIN)) { 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 (IsBattlerWeatherAffected(battlerAtk, B_WEATHER_SUN)) { if (moveType == TYPE_FIRE || gBattleMoves[move].effect == EFFECT_HYDRO_STEAM) 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 && move != MOVE_NONE) { 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)) && abilityAtk != ABILITY_INFILTRATOR && !(isCrit) && !gProtectStructs[battlerAtk].confusionSelfDmg) { if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) MulModifier(&finalModifier, UQ_4_12(0.66)); else MulModifier(&finalModifier, UQ_4_12(0.5)); } // Parental Bond Second Strike if (gSpecialStatuses[battlerAtk].parentalBondState == PARENTAL_BOND_2ND_HIT) { if (B_PARENTAL_BOND_DMG < GEN_7) MulModifier(&finalModifier, UQ_4_12(0.5)); else MulModifier(&finalModifier, UQ_4_12(0.25)); } // Z-Moves and Max Moves bypass Protect and do 25% of their original damage if (gBattleStruct->zmove.active && IS_BATTLER_PROTECTED(battlerDef)) { MulModifier(&finalModifier, UQ_4_12(0.25)); } // 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; case ABILITY_NEUROFORCE: if (typeEffectivenessModifier >= UQ_4_12(2.0)) MulModifier(&finalModifier, UQ_4_12(1.25)); break; } // target's abilities switch (abilityDef) { case ABILITY_MULTISCALE: case ABILITY_SHADOW_SHIELD: 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: percentBoost = min((gBattleStruct->sameMoveTurns[battlerAtk] * GetBattlerHoldEffectParam(battlerAtk)), 100); MulModifier(&finalModifier, UQ_4_12(1.0) + sPercentToModifier[percentBoost]); 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 case HOLD_EFFECT_RESIST_BERRY: if (moveType == GetBattlerHoldEffectParam(battlerDef) && (moveType == TYPE_NORMAL || typeEffectivenessModifier >= UQ_4_12(2.0)) && !UnnerveOn(battlerDef, itemDef)) { if (abilityDef == ABILITY_RIPEN) MulModifier(&finalModifier, UQ_4_12(0.25)); else MulModifier(&finalModifier, UQ_4_12(0.5)); if (updateFlags) gSpecialStatuses[battlerDef].berryReduced = TRUE; } break; } 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)); if (gBattleMoves[move].flags & FLAG_DMG_2X_IN_AIR && gStatuses3[battlerDef] & STATUS3_ON_AIR) MulModifier(&finalModifier, UQ_4_12(2.0)); dmg = ApplyModifier(finalModifier, dmg); if (dmg == 0) dmg = 1; return dmg; } static s32 DoMoveDamageCalc(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags, u16 typeEffectivenessModifier) { s32 dmg; // 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, updateFlags); // long dmg basic formula dmg = ((gBattleMons[battlerAtk].level * 2) / 5) + 2; dmg *= gBattleMovePower; dmg *= CalcAttackStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags); dmg /= CalcDefenseStat(move, battlerAtk, battlerDef, moveType, isCrit, updateFlags); dmg = (dmg / 50) + 2; // Calculate final modifiers. dmg = CalcFinalDmg(dmg, move, battlerAtk, battlerDef, moveType, typeEffectivenessModifier, isCrit, updateFlags); // Add a random factor. if (randomFactor) { dmg *= 100 - RandomUniform(RNG_DAMAGE_MODIFIER, 0, 15); dmg /= 100; } if (dmg == 0) dmg = 1; return dmg; } s32 CalculateMoveDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, s32 fixedBasePower, bool32 isCrit, bool32 randomFactor, bool32 updateFlags) { return DoMoveDamageCalc(move, battlerAtk, battlerDef, moveType, fixedBasePower, isCrit, randomFactor, updateFlags, CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, updateFlags)); } // for AI - get move damage and effectiveness with one function call s32 CalculateMoveDamageAndEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType, u16 *typeEffectivenessModifier) { *typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, FALSE); return DoMoveDamageCalc(move, battlerAtk, battlerDef, moveType, 0, FALSE, FALSE, FALSE, *typeEffectivenessModifier); } static void MulByTypeEffectiveness(u16 *modifier, u16 move, u8 moveType, u8 battlerDef, u8 defType, u8 battlerAtk, bool32 recordAbilities) { u16 mod = GetTypeModifier(moveType, defType); if (mod == UQ_4_12(0.0) && GetBattlerHoldEffect(battlerDef, TRUE) == HOLD_EFFECT_RING_TARGET) { mod = UQ_4_12(1.0); if (recordAbilities) RecordItemEffectBattle(battlerDef, HOLD_EFFECT_RING_TARGET); } else if ((moveType == TYPE_FIGHTING || moveType == TYPE_NORMAL) && defType == TYPE_GHOST && gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT && mod == UQ_4_12(0.0)) { mod = UQ_4_12(1.0); } else if ((moveType == TYPE_FIGHTING || moveType == TYPE_NORMAL) && defType == TYPE_GHOST && GetBattlerAbility(battlerAtk) == ABILITY_SCRAPPY && mod == UQ_4_12(0.0)) { mod = UQ_4_12(1.0); if (recordAbilities) RecordAbilityBattle(battlerAtk, ABILITY_SCRAPPY); } if (moveType == TYPE_PSYCHIC && defType == TYPE_DARK && gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED && mod == UQ_4_12(0.0)) mod = UQ_4_12(1.0); if (gBattleMoves[move].effect == EFFECT_FREEZE_DRY && defType == TYPE_WATER) mod = UQ_4_12(2.0); if (moveType == TYPE_GROUND && defType == TYPE_FLYING && IsBattlerGrounded(battlerDef) && mod == UQ_4_12(0.0)) mod = UQ_4_12(1.0); if (moveType == TYPE_FIRE && gDisableStructs[battlerDef].tarShot) mod = UQ_4_12(2.0); // B_WEATHER_STRONG_WINDS weakens Super Effective moves against Flying-type Pokémon if (gBattleWeather & B_WEATHER_STRONG_WINDS && WEATHER_HAS_EFFECT) { if (defType == TYPE_FLYING && mod >= UQ_4_12(2.0)) mod = UQ_4_12(1.0); } MulModifier(modifier, mod); } static void TryNoticeIllusionInTypeEffectiveness(u32 move, u32 moveType, u32 battlerAtk, u32 battlerDef, u16 resultingModifier, u32 illusionSpecies) { // Check if the type effectiveness would've been different if the pokemon really had the types as the disguise. u16 presumedModifier = UQ_4_12(1.0); MulByTypeEffectiveness(&presumedModifier, move, moveType, battlerDef, gSpeciesInfo[illusionSpecies].types[0], battlerAtk, FALSE); if (gSpeciesInfo[illusionSpecies].types[1] != gSpeciesInfo[illusionSpecies].types[0]) MulByTypeEffectiveness(&presumedModifier, move, moveType, battlerDef, gSpeciesInfo[illusionSpecies].types[1], battlerAtk, FALSE); if (presumedModifier != resultingModifier) RecordAbilityBattle(battlerDef, ABILITY_ILLUSION); } 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); } } static u16 CalcTypeEffectivenessMultiplierInternal(u16 move, u8 moveType, u8 battlerAtk, u8 battlerDef, bool32 recordAbilities, u16 modifier) { u32 illusionSpecies; u16 defAbility = GetBattlerAbility(battlerDef); MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type1, battlerAtk, recordAbilities); if (gBattleMons[battlerDef].type2 != gBattleMons[battlerDef].type1) MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type2, battlerAtk, recordAbilities); if (gBattleMons[battlerDef].type3 != TYPE_MYSTERY && gBattleMons[battlerDef].type3 != gBattleMons[battlerDef].type2 && gBattleMons[battlerDef].type3 != gBattleMons[battlerDef].type1) MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, gBattleMons[battlerDef].type3, battlerAtk, recordAbilities); if (recordAbilities && (illusionSpecies = GetIllusionMonSpecies(battlerDef))) TryNoticeIllusionInTypeEffectiveness(move, moveType, battlerAtk, battlerDef, modifier, illusionSpecies); if (gBattleMoves[move].split == SPLIT_STATUS && move != MOVE_THUNDER_WAVE) { modifier = UQ_4_12(1.0); #if B_GLARE_GHOST <= GEN_3 if (move == MOVE_GLARE && IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST)) { modifier = UQ_4_12(0.0); } #endif } else if (moveType == TYPE_GROUND && !IsBattlerGrounded2(battlerDef, TRUE) && !(gBattleMoves[move].flags & FLAG_DMG_UNGROUNDED_IGNORE_TYPE_IF_FLYING)) { modifier = UQ_4_12(0.0); if (recordAbilities && defAbility == ABILITY_LEVITATE) { gLastUsedAbility = ABILITY_LEVITATE; gMoveResultFlags |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE); gLastLandedMoves[battlerDef] = 0; gBattleCommunication[MISS_TYPE] = B_MSG_GROUND_MISS; RecordAbilityBattle(battlerDef, ABILITY_LEVITATE); } } #if B_SHEER_COLD_IMMUNITY >= GEN_7 else if (move == MOVE_SHEER_COLD && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE)) { modifier = UQ_4_12(0.0); } #endif // Thousand Arrows ignores type modifiers for flying mons if (!IsBattlerGrounded(battlerDef) && (gBattleMoves[move].flags & FLAG_DMG_UNGROUNDED_IGNORE_TYPE_IF_FLYING) && (gBattleMons[battlerDef].type1 == TYPE_FLYING || gBattleMons[battlerDef].type2 == TYPE_FLYING || gBattleMons[battlerDef].type3 == TYPE_FLYING)) { modifier = UQ_4_12(1.0); } if (((defAbility == ABILITY_WONDER_GUARD && modifier <= UQ_4_12(1.0)) || (defAbility == ABILITY_TELEPATHY && battlerDef == BATTLE_PARTNER(battlerAtk))) && gBattleMoves[move].power) { modifier = UQ_4_12(0.0); if (recordAbilities) { gLastUsedAbility = gBattleMons[battlerDef].ability; gMoveResultFlags |= MOVE_RESULT_MISSED; gLastLandedMoves[battlerDef] = 0; gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_DMG; RecordAbilityBattle(battlerDef, gBattleMons[battlerDef].ability); } } return modifier; } 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) { modifier = CalcTypeEffectivenessMultiplierInternal(move, moveType, battlerAtk, battlerDef, recordAbilities, modifier); if (gBattleMoves[move].effect == EFFECT_TWO_TYPED_MOVE) modifier = CalcTypeEffectivenessMultiplierInternal(move, gBattleMoves[move].argument, battlerAtk, battlerDef, recordAbilities, modifier); } if (recordAbilities) UpdateMoveResultFlags(modifier); return modifier; } u16 CalcPartyMonTypeEffectivenessMultiplier(u16 move, u16 speciesDef, u16 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, gSpeciesInfo[speciesDef].types[0], 0, FALSE); if (gSpeciesInfo[speciesDef].types[1] != gSpeciesInfo[speciesDef].types[0]) MulByTypeEffectiveness(&modifier, move, moveType, 0, gSpeciesInfo[speciesDef].types[1], 0, FALSE); 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; } static u16 GetInverseTypeMultiplier(u16 multiplier) { switch (multiplier) { case UQ_4_12(0.0): case UQ_4_12(0.5): return UQ_4_12(2.0); case UQ_4_12(2.0): return UQ_4_12(0.5); case UQ_4_12(1.0): default: return UQ_4_12(1.0); } } u16 GetTypeModifier(u8 atkType, u8 defType) { #if B_FLAG_INVERSE_BATTLE != 0 if (FlagGet(B_FLAG_INVERSE_BATTLE)) return GetInverseTypeMultiplier(sTypeEffectivenessTable[atkType][defType]); #endif return sTypeEffectivenessTable[atkType][defType]; } s32 GetStealthHazardDamageByTypesAndHP(u8 hazardType, u8 type1, u8 type2, u32 maxHp) { s32 dmg = 0; u16 modifier = UQ_4_12(1.0); MulModifier(&modifier, GetTypeModifier(hazardType, type1)); if (type2 != type1) MulModifier(&modifier, GetTypeModifier(hazardType, type2)); switch (modifier) { case UQ_4_12(0.0): dmg = 0; break; case UQ_4_12(0.25): dmg = maxHp / 32; if (dmg == 0) dmg = 1; break; case UQ_4_12(0.5): dmg = maxHp / 16; if (dmg == 0) dmg = 1; break; case UQ_4_12(1.0): dmg = maxHp / 8; if (dmg == 0) dmg = 1; break; case UQ_4_12(2.0): dmg = maxHp / 4; if (dmg == 0) dmg = 1; break; case UQ_4_12(4.0): dmg = maxHp / 2; if (dmg == 0) dmg = 1; break; } return dmg; } s32 GetStealthHazardDamage(u8 hazardType, u8 battlerId) { u8 type1 = gBattleMons[battlerId].type1; u8 type2 = gBattleMons[battlerId].type2; u32 maxHp = gBattleMons[battlerId].maxHP; return GetStealthHazardDamageByTypesAndHP(hazardType, type1, type2, maxHp); } bool32 IsPartnerMonFromSameTrainer(u8 battlerId) { if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) return FALSE; else if (GetBattlerSide(battlerId) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) return FALSE; else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) return FALSE; else return TRUE; } u16 GetMegaEvolutionSpecies(u16 preEvoSpecies, u16 heldItemId) { u32 i; for (i = 0; i < EVOS_PER_MON; i++) { if (gEvolutionTable[preEvoSpecies][i].method == EVO_MEGA_EVOLUTION && gEvolutionTable[preEvoSpecies][i].param == heldItemId) return gEvolutionTable[preEvoSpecies][i].targetSpecies; } return SPECIES_NONE; } u16 GetPrimalReversionSpecies(u16 preEvoSpecies, u16 heldItemId) { u32 i; for (i = 0; i < EVOS_PER_MON; i++) { if (gEvolutionTable[preEvoSpecies][i].method == EVO_PRIMAL_REVERSION && gEvolutionTable[preEvoSpecies][i].param == heldItemId) return gEvolutionTable[preEvoSpecies][i].targetSpecies; } return SPECIES_NONE; } u16 GetWishMegaEvolutionSpecies(u16 preEvoSpecies, u16 moveId1, u16 moveId2, u16 moveId3, u16 moveId4) { u32 i, par; for (i = 0; i < EVOS_PER_MON; i++) { if (gEvolutionTable[preEvoSpecies][i].method == EVO_MOVE_MEGA_EVOLUTION) { par = gEvolutionTable[preEvoSpecies][i].param; if (par == moveId1 || par == moveId2 || par == moveId3 || par == moveId4) return gEvolutionTable[preEvoSpecies][i].targetSpecies; } } return SPECIES_NONE; } bool32 CanMegaEvolve(u8 battlerId) { u32 itemId, holdEffect, species; struct Pokemon *mon; u8 battlerPosition = GetBattlerPosition(battlerId); u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battlerId)); struct MegaEvolutionData *mega = &(((struct ChooseMoveStruct *)(&gBattleResources->bufferA[gActiveBattler][4]))->mega); // Check if Player has a Mega Ring if ((GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battlerId) == B_POSITION_PLAYER_RIGHT)) && !CheckBagHasItem(ITEM_MEGA_RING, 1)) return FALSE; // Check if trainer already mega evolved a pokemon. if (mega->alreadyEvolved[battlerPosition]) return FALSE; // Cannot use z move and mega evolve on same turn if (gBattleStruct->zmove.toBeUsed[battlerId]) return FALSE; if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsPartnerMonFromSameTrainer(battlerId) && (mega->alreadyEvolved[partnerPosition] || (mega->toEvolve & gBitTable[BATTLE_PARTNER(battlerId)]))) return FALSE; // Check if mon is currently held by Sky Drop if (gStatuses3[battlerId] & STATUS3_SKY_DROPPED) return FALSE; // Gets mon data. if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT) mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]]; else mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]]; species = GetMonData(mon, MON_DATA_SPECIES); itemId = GetMonData(mon, MON_DATA_HELD_ITEM); // Check if there is an entry in the evolution table for regular Mega Evolution. if (GetMegaEvolutionSpecies(species, itemId) != SPECIES_NONE) { #if DEBUG_BATTLE_MENU == TRUE if (gBattleStruct->debugHoldEffects[battlerId]) holdEffect = gBattleStruct->debugHoldEffects[battlerId]; else #endif if (itemId == ITEM_ENIGMA_BERRY_E_READER) holdEffect = gEnigmaBerries[battlerId].holdEffect; else holdEffect = ItemId_GetHoldEffect(itemId); // Can Mega Evolve via Mega Stone. if (holdEffect == HOLD_EFFECT_MEGA_STONE) return TRUE; } // Check if there is an entry in the evolution table for Wish Mega Evolution. if (GetWishMegaEvolutionSpecies(species, GetMonData(mon, MON_DATA_MOVE1), GetMonData(mon, MON_DATA_MOVE2), GetMonData(mon, MON_DATA_MOVE3), GetMonData(mon, MON_DATA_MOVE4))) return TRUE; // No checks passed, the mon CAN'T mega evolve. return FALSE; } void UndoMegaEvolution(u32 monId) { u16 baseSpecies = GET_BASE_SPECIES_ID(GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES)); if (gBattleStruct->mega.evolvedPartyIds[B_SIDE_PLAYER] & gBitTable[monId]) { gBattleStruct->mega.evolvedPartyIds[B_SIDE_PLAYER] &= ~(gBitTable[monId]); SetMonData(&gPlayerParty[monId], MON_DATA_SPECIES, &baseSpecies); CalculateMonStats(&gPlayerParty[monId]); } else if (gBattleStruct->mega.primalRevertedPartyIds[B_SIDE_PLAYER] & gBitTable[monId]) { gBattleStruct->mega.primalRevertedPartyIds[B_SIDE_PLAYER] &= ~(gBitTable[monId]); SetMonData(&gPlayerParty[monId], MON_DATA_SPECIES, &baseSpecies); CalculateMonStats(&gPlayerParty[monId]); } // While not exactly a mega evolution, Zygarde follows the same rules. else if (GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES, NULL) == SPECIES_ZYGARDE_COMPLETE) { SetMonData(&gPlayerParty[monId], MON_DATA_SPECIES, &gBattleStruct->changedSpecies[monId]); gBattleStruct->changedSpecies[monId] = 0; CalculateMonStats(&gPlayerParty[monId]); } } void UndoFormChange(u32 monId, u32 side, bool32 isSwitchingOut) { u32 i, currSpecies, targetSpecies; struct Pokemon *party = (side == B_SIDE_PLAYER) ? gPlayerParty : gEnemyParty; static const u16 species[][3] = { // Changed Form ID Default Form ID Should change on switch {SPECIES_EISCUE_NOICE_FACE, SPECIES_EISCUE, TRUE}, {SPECIES_MIMIKYU_BUSTED, SPECIES_MIMIKYU, FALSE}, {SPECIES_GRENINJA_ASH, SPECIES_GRENINJA_BATTLE_BOND, FALSE}, {SPECIES_MELOETTA_PIROUETTE, SPECIES_MELOETTA, FALSE}, {SPECIES_AEGISLASH_BLADE, SPECIES_AEGISLASH, TRUE}, {SPECIES_DARMANITAN_ZEN_MODE, SPECIES_DARMANITAN, TRUE}, {SPECIES_MINIOR, SPECIES_MINIOR_CORE_RED, TRUE}, {SPECIES_MINIOR_METEOR_BLUE, SPECIES_MINIOR_CORE_BLUE, TRUE}, {SPECIES_MINIOR_METEOR_GREEN, SPECIES_MINIOR_CORE_GREEN, TRUE}, {SPECIES_MINIOR_METEOR_INDIGO, SPECIES_MINIOR_CORE_INDIGO, TRUE}, {SPECIES_MINIOR_METEOR_ORANGE, SPECIES_MINIOR_CORE_ORANGE, TRUE}, {SPECIES_MINIOR_METEOR_VIOLET, SPECIES_MINIOR_CORE_VIOLET, TRUE}, {SPECIES_MINIOR_METEOR_YELLOW, SPECIES_MINIOR_CORE_YELLOW, TRUE}, {SPECIES_WISHIWASHI_SCHOOL, SPECIES_WISHIWASHI, TRUE}, {SPECIES_CRAMORANT_GORGING, SPECIES_CRAMORANT, TRUE}, {SPECIES_CRAMORANT_GULPING, SPECIES_CRAMORANT, TRUE}, {SPECIES_MORPEKO_HANGRY, SPECIES_MORPEKO, TRUE}, {SPECIES_DARMANITAN_ZEN_MODE_GALARIAN, SPECIES_DARMANITAN_GALARIAN, TRUE}, }; currSpecies = GetMonData(&party[monId], MON_DATA_SPECIES, NULL); for (i = 0; i < ARRAY_COUNT(species); i++) { if (currSpecies == species[i][0] && (!isSwitchingOut || species[i][2] == TRUE)) { SetMonData(&party[monId], MON_DATA_SPECIES, &species[i][1]); CalculateMonStats(&party[monId]); break; } } if (!isSwitchingOut) { targetSpecies = GetFormChangeTargetSpecies(&party[monId], FORM_BATTLE_END, 0); if (targetSpecies != SPECIES_NONE) { SetMonData(&party[monId], MON_DATA_SPECIES, &targetSpecies); CalculateMonStats(&party[monId]); TryToSetBattleFormChangeMoves(&party[monId]); } } } bool32 DoBattlersShareType(u32 battler1, u32 battler2) { s32 i; u8 types1[3] = {gBattleMons[battler1].type1, gBattleMons[battler1].type2, gBattleMons[battler1].type3}; u8 types2[3] = {gBattleMons[battler2].type1, gBattleMons[battler2].type2, gBattleMons[battler2].type3}; if (types1[2] == TYPE_MYSTERY) types1[2] = types1[0]; if (types2[2] == TYPE_MYSTERY) types2[2] = types2[0]; for (i = 0; i < 3; i++) { if (types1[i] == types2[0] || types1[i] == types2[1] || types1[i] == types2[2]) return TRUE; } return FALSE; } bool32 CanBattlerGetOrLoseItem(u8 battlerId, u16 itemId) { u16 species = gBattleMons[battlerId].species; u16 holdEffect = ItemId_GetHoldEffect(itemId); // Mail can be stolen now if (itemId == ITEM_ENIGMA_BERRY_E_READER) return FALSE; // Primal Reversion inducing items cannot be lost if pokemon's base species can undergo primal reversion with it. else if (holdEffect == HOLD_EFFECT_PRIMAL_ORB && (GetPrimalReversionSpecies(GET_BASE_SPECIES_ID(species), itemId) != SPECIES_NONE)) return FALSE; // Mega stone cannot be lost if pokemon's base species can mega evolve with it. else if (holdEffect == HOLD_EFFECT_MEGA_STONE && (GetMegaEvolutionSpecies(GET_BASE_SPECIES_ID(species), itemId) != SPECIES_NONE)) return FALSE; else if (GET_BASE_SPECIES_ID(species) == SPECIES_GIRATINA && itemId == ITEM_GRISEOUS_ORB) return FALSE; else if (GET_BASE_SPECIES_ID(species) == SPECIES_GENESECT && holdEffect == HOLD_EFFECT_DRIVE) return FALSE; else if (GET_BASE_SPECIES_ID(species) == SPECIES_SILVALLY && holdEffect == HOLD_EFFECT_MEMORY) return FALSE; else if (GET_BASE_SPECIES_ID(species) == SPECIES_ARCEUS && holdEffect == HOLD_EFFECT_PLATE) return FALSE; #ifdef HOLD_EFFECT_Z_CRYSTAL else if (holdEffect == HOLD_EFFECT_Z_CRYSTAL) return FALSE; #endif else return TRUE; } struct Pokemon *GetIllusionMonPtr(u32 battlerId) { if (gBattleStruct->illusion[battlerId].broken) return NULL; if (!gBattleStruct->illusion[battlerId].set) { if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) SetIllusionMon(&gPlayerParty[gBattlerPartyIndexes[battlerId]], battlerId); else SetIllusionMon(&gEnemyParty[gBattlerPartyIndexes[battlerId]], battlerId); } if (!gBattleStruct->illusion[battlerId].on) return NULL; return gBattleStruct->illusion[battlerId].mon; } void ClearIllusionMon(u32 battlerId) { memset(&gBattleStruct->illusion[battlerId], 0, sizeof(gBattleStruct->illusion[battlerId])); } u32 GetIllusionMonSpecies(u32 battlerId) { struct Pokemon *illusionMon = GetIllusionMonPtr(battlerId); if (illusionMon != NULL) return GetMonData(illusionMon, MON_DATA_SPECIES); return SPECIES_NONE; } bool32 SetIllusionMon(struct Pokemon *mon, u32 battlerId) { struct Pokemon *party, *partnerMon; s32 i, id; gBattleStruct->illusion[battlerId].set = 1; if (GetMonAbility(mon) != ABILITY_ILLUSION) return FALSE; if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; if (IsBattlerAlive(BATTLE_PARTNER(battlerId))) partnerMon = &party[gBattlerPartyIndexes[BATTLE_PARTNER(battlerId)]]; else partnerMon = mon; // Find last alive non-egg pokemon. for (i = PARTY_SIZE - 1; i >= 0; i--) { id = i; if (GetMonData(&party[id], MON_DATA_SANITY_HAS_SPECIES) && GetMonData(&party[id], MON_DATA_HP) && !GetMonData(&party[id], MON_DATA_IS_EGG) && &party[id] != mon && &party[id] != partnerMon) { gBattleStruct->illusion[battlerId].on = 1; gBattleStruct->illusion[battlerId].broken = 0; gBattleStruct->illusion[battlerId].partyId = id; gBattleStruct->illusion[battlerId].mon = &party[id]; return TRUE; } } return FALSE; } bool8 ShouldGetStatBadgeBoost(u16 badgeFlag, u8 battlerId) { #if B_BADGE_BOOST == GEN_3 if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FRONTIER)) return FALSE; else if (GetBattlerSide(battlerId) != B_SIDE_PLAYER) return FALSE; else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && gTrainerBattleOpponent_A == TRAINER_SECRET_BASE) return FALSE; else if (FlagGet(badgeFlag)) return TRUE; #endif return FALSE; } u8 GetBattleMoveSplit(u32 moveId) { if (gBattleStruct != NULL && gBattleStruct->zmove.active && !IS_MOVE_STATUS(moveId)) return gBattleStruct->zmove.activeSplit; if (gBattleStruct != NULL && gBattleStruct->swapDamageCategory) // Photon Geyser, Shell Side Arm, Light That Burns the Sky return SPLIT_PHYSICAL; #if B_PHYSICAL_SPECIAL_SPLIT >= GEN_4 return gBattleMoves[moveId].split; #else if (IS_MOVE_STATUS(moveId)) return SPLIT_STATUS; else if (gBattleMoves[moveId].type < TYPE_MYSTERY) return SPLIT_PHYSICAL; else return SPLIT_SPECIAL; #endif } static bool32 TryRemoveScreens(u8 battler) { bool32 removed = FALSE; u8 battlerSide = GetBattlerSide(battler); u8 enemySide = GetBattlerSide(BATTLE_OPPOSITE(battler)); // try to remove from battler's side if (gSideStatuses[battlerSide] & (SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL)) { gSideStatuses[battlerSide] &= ~(SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL); gSideTimers[battlerSide].reflectTimer = 0; gSideTimers[battlerSide].lightscreenTimer = 0; gSideTimers[battlerSide].auroraVeilTimer = 0; removed = TRUE; } // try to remove from battler opponent's side if (gSideStatuses[enemySide] & (SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL)) { gSideStatuses[enemySide] &= ~(SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL); gSideTimers[enemySide].reflectTimer = 0; gSideTimers[enemySide].lightscreenTimer = 0; gSideTimers[enemySide].auroraVeilTimer = 0; removed = TRUE; } return removed; } static bool32 IsUnnerveAbilityOnOpposingSide(u8 battlerId) { if (IsAbilityOnOpposingSide(battlerId, ABILITY_UNNERVE) || IsAbilityOnOpposingSide(battlerId, ABILITY_AS_ONE_ICE_RIDER) || IsAbilityOnOpposingSide(battlerId, ABILITY_AS_ONE_SHADOW_RIDER)) return TRUE; return FALSE; } // Photon geyser & light that burns the sky u8 GetSplitBasedOnStats(u8 battlerId) { u32 attack = gBattleMons[battlerId].attack; u32 spAttack = gBattleMons[battlerId].spAttack; attack = attack * gStatStageRatios[gBattleMons[battlerId].statStages[STAT_ATK]][0]; attack = attack / gStatStageRatios[gBattleMons[battlerId].statStages[STAT_ATK]][1]; spAttack = spAttack * gStatStageRatios[gBattleMons[battlerId].statStages[STAT_SPATK]][0]; spAttack = spAttack / gStatStageRatios[gBattleMons[battlerId].statStages[STAT_SPATK]][1]; if (spAttack >= attack) return SPLIT_SPECIAL; else return SPLIT_PHYSICAL; } bool32 TestMoveFlags(u16 move, u32 flag) { if (gBattleMoves[move].flags & flag) return TRUE; return FALSE; } struct Pokemon *GetBattlerPartyData(u8 battlerId) { struct Pokemon *mon; if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]]; else mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]]; return mon; } static u8 GetFlingPowerFromItemId(u16 itemId) { if (itemId >= ITEM_TM01 && itemId <= ITEM_HM08) { u8 power = gBattleMoves[ItemIdToBattleMoveId(itemId)].power; if (power > 1) return power; return 10; // Status moves and moves with variable power always return 10 power. } else return ItemId_GetFlingPower(itemId); } // Make sure the input bank is any bank on the specific mon's side bool32 CanFling(u8 battlerId) { u16 item = gBattleMons[battlerId].item; u16 itemEffect = ItemId_GetHoldEffect(item); if (item == ITEM_NONE #if B_KLUTZ_FLING_INTERACTION >= GEN_5 || GetBattlerAbility(battlerId) == ABILITY_KLUTZ #endif || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM || gDisableStructs[battlerId].embargoTimer != 0 || GetFlingPowerFromItemId(item) == 0 || !CanBattlerGetOrLoseItem(battlerId, item)) return FALSE; return TRUE; } // Ability checks bool32 IsRolePlayBannedAbilityAtk(u16 ability) { u32 i; for (i = 0; i < ARRAY_COUNT(sRolePlayBannedAttackerAbilities); i++) { if (ability == sRolePlayBannedAttackerAbilities[i]) return TRUE; } return FALSE; } bool32 IsRolePlayBannedAbility(u16 ability) { u32 i; for (i = 0; i < ARRAY_COUNT(sRolePlayBannedAbilities); i++) { if (ability == sRolePlayBannedAbilities[i]) return TRUE; } return FALSE; } bool32 IsSkillSwapBannedAbility(u16 ability) { u32 i; for (i = 0; i < ARRAY_COUNT(sSkillSwapBannedAbilities); i++) { if (ability == sSkillSwapBannedAbilities[i]) return TRUE; } return FALSE; } bool32 IsWorrySeedBannedAbility(u16 ability) { u32 i; for (i = 0; i < ARRAY_COUNT(sWorrySeedBannedAbilities); i++) { if (ability == sWorrySeedBannedAbilities[i]) return TRUE; } return FALSE; } bool32 IsGastroAcidBannedAbility(u16 ability) { u32 i; for (i = 0; i < ARRAY_COUNT(sGastroAcidBannedAbilities); i++) { if (ability == sGastroAcidBannedAbilities[i]) return TRUE; } return FALSE; } bool32 IsEntrainmentBannedAbilityAttacker(u16 ability) { u32 i; for (i = 0; i < ARRAY_COUNT(sEntrainmentBannedAttackerAbilities); i++) { if (ability == sEntrainmentBannedAttackerAbilities[i]) return TRUE; } return FALSE; } bool32 IsEntrainmentTargetOrSimpleBeamBannedAbility(u16 ability) { u32 i; for (i = 0; i < ARRAY_COUNT(sEntrainmentTargetSimpleBeamBannedAbilities); i++) { if (ability == sEntrainmentTargetSimpleBeamBannedAbilities[i]) return TRUE; } return FALSE; } // Sort an array of battlers by speed // Useful for effects like pickpocket, eject button, red card, dancer void SortBattlersBySpeed(u8 *battlers, bool8 slowToFast) { int i, j, currSpeed, currBattler; u16 speeds[4] = {0}; for (i = 0; i < gBattlersCount; i++) speeds[i] = GetBattlerTotalSpeedStat(battlers[i]); for (i = 1; i < gBattlersCount; i++) { currBattler = battlers[i]; currSpeed = speeds[i]; j = i - 1; if (slowToFast) { while (j >= 0 && speeds[j] > currSpeed) { battlers[j + 1] = battlers[j]; speeds[j + 1] = speeds[j]; j = j - 1; } } else { while (j >= 0 && speeds[j] < currSpeed) { battlers[j + 1] = battlers[j]; speeds[j + 1] = speeds[j]; j = j - 1; } } battlers[j + 1] = currBattler; speeds[j + 1] = currSpeed; } } void TryRestoreStolenItems(void) { u32 i; u16 stolenItem = ITEM_NONE; for (i = 0; i < PARTY_SIZE; i++) { if (gBattleStruct->itemStolen[i].stolen) { stolenItem = gBattleStruct->itemStolen[i].originalItem; if (stolenItem != ITEM_NONE && ItemId_GetPocket(stolenItem) != POCKET_BERRIES) SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &stolenItem); // Restore stolen non-berry items } } } bool32 CanStealItem(u8 battlerStealing, u8 battlerItem, u16 item) { u8 stealerSide = GetBattlerSide(battlerStealing); if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL) return FALSE; // Check if the battler trying to steal should be able to if (stealerSide == B_SIDE_OPPONENT && !(gBattleTypeFlags & (BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_SECRET_BASE #if B_TRAINERS_KNOCK_OFF_ITEMS == TRUE | BATTLE_TYPE_TRAINER #endif ))) { return FALSE; } else if (!(gBattleTypeFlags & (BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_SECRET_BASE)) && (gWishFutureKnock.knockedOffMons[stealerSide] & gBitTable[gBattlerPartyIndexes[battlerStealing]])) { return FALSE; } if (!CanBattlerGetOrLoseItem(battlerItem, item) // Battler with item cannot have it stolen ||!CanBattlerGetOrLoseItem(battlerStealing, item)) // Stealer cannot take the item return FALSE; return TRUE; } void TrySaveExchangedItem(u8 battlerId, u16 stolenItem) { // Because BtlController_EmitSetMonData does SetMonData, we need to save the stolen item only if it matches the battler's original // So, if the player steals an item during battle and has it stolen from it, it will not end the battle with it (naturally) #if B_TRAINERS_KNOCK_OFF_ITEMS == TRUE // If regular trainer battle and mon's original item matches what is being stolen, save it to be restored at end of battle if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & BATTLE_TYPE_FRONTIER) && GetBattlerSide(battlerId) == B_SIDE_PLAYER && stolenItem == gBattleStruct->itemStolen[gBattlerPartyIndexes[battlerId]].originalItem) gBattleStruct->itemStolen[gBattlerPartyIndexes[battlerId]].stolen = TRUE; #endif } bool32 IsBattlerAffectedByHazards(u8 battlerId, bool32 toxicSpikes) { bool32 ret = TRUE; u32 holdEffect = GetBattlerHoldEffect(gActiveBattler, TRUE); if (toxicSpikes && holdEffect == HOLD_EFFECT_HEAVY_DUTY_BOOTS && !IS_BATTLER_OF_TYPE(battlerId, TYPE_POISON)) { ret = FALSE; RecordItemEffectBattle(battlerId, holdEffect); } else if (holdEffect == HOLD_EFFECT_HEAVY_DUTY_BOOTS) { ret = FALSE; RecordItemEffectBattle(battlerId, holdEffect); } return ret; } bool32 TestSheerForceFlag(u8 battler, u16 move) { if (GetBattlerAbility(battler) == ABILITY_SHEER_FORCE && gBattleMoves[move].flags & FLAG_SHEER_FORCE_BOOST) return TRUE; else return FALSE; } // This function is the body of "jumpifstat", but can be used dynamically in a function bool32 CompareStat(u8 battlerId, u8 statId, u8 cmpTo, u8 cmpKind) { bool8 ret = FALSE; u8 statValue = gBattleMons[battlerId].statStages[statId]; // Because this command is used as a way of checking if a stat can be lowered/raised, // we need to do some modification at run-time. if (GetBattlerAbility(battlerId) == ABILITY_CONTRARY) { if (cmpKind == CMP_GREATER_THAN) cmpKind = CMP_LESS_THAN; else if (cmpKind == CMP_LESS_THAN) cmpKind = CMP_GREATER_THAN; if (cmpTo == MIN_STAT_STAGE) cmpTo = MAX_STAT_STAGE; else if (cmpTo == MAX_STAT_STAGE) cmpTo = MIN_STAT_STAGE; } switch (cmpKind) { case CMP_EQUAL: if (statValue == cmpTo) ret = TRUE; break; case CMP_NOT_EQUAL: if (statValue != cmpTo) ret = TRUE; break; case CMP_GREATER_THAN: if (statValue > cmpTo) ret = TRUE; break; case CMP_LESS_THAN: if (statValue < cmpTo) ret = TRUE; break; case CMP_COMMON_BITS: if (statValue & cmpTo) ret = TRUE; break; case CMP_NO_COMMON_BITS: if (!(statValue & cmpTo)) ret = TRUE; break; } return ret; } void BufferStatChange(u8 battlerId, u8 statId, u8 stringId) { bool8 hasContrary = (GetBattlerAbility(battlerId) == ABILITY_CONTRARY); PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); if (stringId == STRINGID_STATFELL) { if (hasContrary) PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATROSE) else PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATFELL) } else if (stringId == STRINGID_STATROSE) { if (hasContrary) PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATFELL) else PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATROSE) } else { PREPARE_STRING_BUFFER(gBattleTextBuff2, stringId) } } bool32 TryRoomService(u8 battlerId) { if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && CompareStat(battlerId, STAT_SPEED, MIN_STAT_STAGE, CMP_GREATER_THAN)) { BufferStatChange(battlerId, STAT_SPEED, STRINGID_STATFELL); gEffectBattler = gBattleScripting.battler = battlerId; SET_STATCHANGER(STAT_SPEED, 1, TRUE); gBattleScripting.animArg1 = 14 + STAT_SPEED; gBattleScripting.animArg2 = 0; gLastUsedItem = gBattleMons[battlerId].item; return TRUE; } else { return FALSE; } } void DoBurmyFormChange(u32 monId) { u16 newSpecies, currSpecies; struct Pokemon *party = gPlayerParty; currSpecies = GetMonData(&party[monId], MON_DATA_SPECIES, NULL); if ((GET_BASE_SPECIES_ID(currSpecies) == SPECIES_BURMY) && (gBattleStruct->appearedInBattle & gBitTable[monId]) // Burmy appeared in battle && GetMonData(&party[monId], MON_DATA_HP, NULL) != 0) // Burmy isn't fainted { switch (gBattleTerrain) { case BATTLE_TERRAIN_GRASS: case BATTLE_TERRAIN_LONG_GRASS: case BATTLE_TERRAIN_POND: case BATTLE_TERRAIN_MOUNTAIN: case BATTLE_TERRAIN_PLAIN: newSpecies = SPECIES_BURMY; break; case BATTLE_TERRAIN_CAVE: case BATTLE_TERRAIN_SAND: newSpecies = SPECIES_BURMY_SANDY_CLOAK; break; case BATTLE_TERRAIN_BUILDING: newSpecies = SPECIES_BURMY_TRASH_CLOAK; break; default: // Don't change form if last battle was water-related newSpecies = SPECIES_NONE; break; } if (newSpecies != SPECIES_NONE) { SetMonData(&party[monId], MON_DATA_SPECIES, &newSpecies); CalculateMonStats(&party[monId]); } } } bool32 BlocksPrankster(u16 move, u8 battlerPrankster, u8 battlerDef, bool32 checkTarget) { #if B_PRANKSTER_DARK_TYPES >= GEN_7 if (!gProtectStructs[battlerPrankster].pranksterElevated) return FALSE; if (GetBattlerSide(battlerPrankster) == GetBattlerSide(battlerDef)) return FALSE; if (checkTarget && (GetBattlerMoveTargetType(battlerPrankster, move) & (MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_DEPENDS))) return FALSE; if (!IS_BATTLER_OF_TYPE(battlerDef, TYPE_DARK)) return FALSE; if (gStatuses3[battlerDef] & STATUS3_SEMI_INVULNERABLE) return FALSE; return TRUE; #endif return FALSE; } u16 GetUsedHeldItem(u8 battler) { return gBattleStruct->usedHeldItems[gBattlerPartyIndexes[battler]][GetBattlerSide(battler)]; } bool32 IsBattlerWeatherAffected(u8 battlerId, u32 weatherFlags) { if (gBattleWeather & weatherFlags && WEATHER_HAS_EFFECT) { // given weather is active -> check if its sun, rain against utility umbrella ( since only 1 weather can be active at once) if (gBattleWeather & (B_WEATHER_SUN | B_WEATHER_RAIN) && GetBattlerHoldEffect(battlerId, TRUE) == HOLD_EFFECT_UTILITY_UMBRELLA) return FALSE; // utility umbrella blocks sun, rain effects return TRUE; } return FALSE; } // Gets move target before redirection effects etc. are applied // Possible return values are defined in battle.h following MOVE_TARGET_SELECTED u32 GetBattlerMoveTargetType(u8 battlerId, u16 move) { u32 target; if (gBattleMoves[move].effect == EFFECT_EXPANDING_FORCE && IsBattlerTerrainAffected(battlerId, STATUS_FIELD_PSYCHIC_TERRAIN)) return MOVE_TARGET_BOTH; else return gBattleMoves[move].target; } bool32 CanTargetBattler(u8 battlerAtk, u8 battlerDef, u16 move) { if (gBattleMoves[move].effect == EFFECT_HIT_ENEMY_HEAL_ALLY && GetBattlerSide(battlerAtk) == GetBattlerSide(battlerDef) && gStatuses3[battlerAtk] & STATUS3_HEAL_BLOCK) return FALSE; // Pokémon affected by Heal Block cannot target allies with Pollen Puff return TRUE; } static void SetRandomMultiHitCounter() { #if (B_MULTI_HIT_CHANCE >= GEN_5) // Based on Gen 5's odds // 35% for 2 hits // 35% for 3 hits // 15% for 4 hits // 15% for 5 hits gMultiHitCounter = Random() % 100; if (gMultiHitCounter < 35) gMultiHitCounter = 2; else if (gMultiHitCounter < 35 + 35) gMultiHitCounter = 3; else if (gMultiHitCounter < 35 + 35 + 15) gMultiHitCounter = 4; else gMultiHitCounter = 5; #else // 2 and 3 hits: 37.5% // 4 and 5 hits: 12.5% gMultiHitCounter = Random() % 4; if (gMultiHitCounter > 1) gMultiHitCounter = (Random() % 4) + 2; else gMultiHitCounter += 2; #endif if (gMultiHitCounter < 4 && GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LOADED_DICE) { // If roll 4 or 5 Loaded Dice doesn't do anything. Otherwise it rolls the number of hits as 5 minus a random integer from 0 to 1 inclusive. gMultiHitCounter = 5 - (Random() & 1); } }