#include "global.h" #include "battle.h" #include "battle_anim.h" #include "battle_arena.h" #include "battle_pyramid.h" #include "battle_util.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 "trig.h" #include "window.h" #include "battle_message.h" #include "battle_ai_script_commands.h" #include "battle_controllers.h" #include "event_data.h" #include "link.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/weather.h" /* 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. */ extern const u8 *const gBattleScriptsForMoveEffects[]; extern const u8 *const gBattlescriptsForBallThrow[]; 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}; void HandleAction_UseMove(void) { u8 side; u8 var = 4; gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; if (*(&gBattleStruct->absentBattlerFlags) & gBitTable[gBattlerAttacker]) { gCurrentActionFuncId = B_ACTION_FINISHED; return; } gCritMultiplier = 1; gBattleScripting.dmgMultiplier = 1; gBattleStruct->atkCancellerTracker = 0; gMoveResultFlags = 0; gMultiHitCounter = 0; gBattleCommunication[6] = 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 (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 (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]; } if (gBattleMons[gBattlerAttacker].hp != 0) { if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) gBattleResults.lastUsedMovePlayer = gCurrentMove; else gBattleResults.lastUsedMoveOpponent = gCurrentMove; } // choose target side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; if (gSideTimers[side].followmeTimer != 0 && gBattleMoves[gCurrentMove].target == MOVE_TARGET_SELECTED && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gSideTimers[side].followmeTarget) && gBattleMons[gSideTimers[side].followmeTarget].hp != 0) { gBattlerTarget = gSideTimers[side].followmeTarget; } else if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && gSideTimers[side].followmeTimer == 0 && (gBattleMoves[gCurrentMove].power != 0 || gBattleMoves[gCurrentMove].target != MOVE_TARGET_USER) && gBattleMons[*(gBattleStruct->moveTarget + gBattlerAttacker)].ability != ABILITY_LIGHTNING_ROD && gBattleMoves[gCurrentMove].type == TYPE_ELECTRIC) { side = GetBattlerSide(gBattlerAttacker); for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++) { if (side != GetBattlerSide(gActiveBattler) && *(gBattleStruct->moveTarget + gBattlerAttacker) != gActiveBattler && gBattleMons[gActiveBattler].ability == ABILITY_LIGHTNING_ROD && GetBattlerTurnOrderNum(gActiveBattler) < var) { var = GetBattlerTurnOrderNum(gActiveBattler); } } if (var == 4) { if (gBattleMoves[gChosenMove].target & 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 { gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker); } if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) { if (GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)) { gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerTarget) ^ BIT_FLANK); } else { gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_SIDE); if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerTarget) ^ BIT_FLANK); } } } else { gActiveBattler = gBattlerByTurnOrder[var]; RecordAbilityBattle(gActiveBattler, gBattleMons[gActiveBattler].ability); gSpecialStatuses[gActiveBattler].lightningRodRedirected = 1; gBattlerTarget = gActiveBattler; } } else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gBattleMoves[gChosenMove].target & 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(GetBattlerPosition(gBattlerTarget) ^ BIT_FLANK); } } else { gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker); if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) { if (GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)) { gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerTarget) ^ BIT_FLANK); } else { gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_SIDE); if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerTarget) ^ BIT_FLANK); } } } 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); 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++; } void HandleAction_UseItem(void) { gBattlerAttacker = gBattlerTarget = gBattlerByTurnOrder[gCurrentTurnActionNumber]; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; ClearFuryCutterDestinyBondGrudge(gBattlerAttacker); gLastUsedItem = gBattleBufferB[gBattlerAttacker][1] | (gBattleBufferB[gBattlerAttacker][2] << 8); if (gLastUsedItem <= LAST_BALL) // is ball { gBattlescriptCurrInstr = gBattlescriptsForBallThrow[gLastUsedItem]; } else if (gLastUsedItem == ITEM_POKE_DOLL || gLastUsedItem == ITEM_FLUFFY_TAIL) { gBattlescriptCurrInstr = gBattlescriptsForRunningByItem[0]; // BattleScript_RunByUsingItem } else if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) { gBattlescriptCurrInstr = gBattlescriptsForUsingItem[0]; // BattleScript_PlayerUsesItem } else { gBattleScripting.battler = gBattlerAttacker; switch (*(gBattleStruct->AI_itemType + (gBattlerAttacker >> 1))) { case AI_ITEM_FULL_RESTORE: case AI_ITEM_HEAL_HP: break; case AI_ITEM_CURE_CONDITION: gBattleCommunication[MULTISTRING_CHOOSER] = AI_HEAL_CONFUSION; if (*(gBattleStruct->AI_itemFlags + gBattlerAttacker / 2) & (1 << AI_HEAL_CONFUSION)) { if (*(gBattleStruct->AI_itemFlags + gBattlerAttacker / 2) & 0x3E) gBattleCommunication[MULTISTRING_CHOOSER] = AI_HEAL_SLEEP; } else { // Check for other statuses, stopping at first (shouldn't be more than one) while (!(*(gBattleStruct->AI_itemFlags + gBattlerAttacker / 2) & 1)) { *(gBattleStruct->AI_itemFlags + gBattlerAttacker / 2) >>= 1; gBattleCommunication[MULTISTRING_CHOOSER]++; // MULTISTRING_CHOOSER will be either AI_HEAL_PARALYSIS, AI_HEAL_FREEZE, // AI_HEAL_BURN, AI_HEAL_POISON, or AI_HEAL_SLEEP } } break; case AI_ITEM_X_STAT: gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STAT_ROSE_ITEM; if (*(gBattleStruct->AI_itemFlags + (gBattlerAttacker >> 1)) & (1 << AI_DIRE_HIT)) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_USED_DIRE_HIT; } else { PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK) PREPARE_STRING_BUFFER(gBattleTextBuff2, CHAR_X) while (!((*(gBattleStruct->AI_itemFlags + (gBattlerAttacker >> 1))) & 1)) { *(gBattleStruct->AI_itemFlags + gBattlerAttacker / 2) >>= 1; gBattleTextBuff1[2]++; } gBattleScripting.animArg1 = gBattleTextBuff1[2] + 14; gBattleScripting.animArg2 = 0; } break; case AI_ITEM_GUARD_SPEC: // It seems probable that at some point there was a special message for // an AI trainer using Guard Spec in a double battle. // There isn't now however, and the assignment to 2 below goes out of // bounds for gMistUsedStringIds and instead prints "{mon} is getting pumped" // from the next table, gFocusEnergyUsedStringIds. // In any case this isn't an issue in the retail version, as no trainers // are ever given any Guard Spec to use. #ifndef UBFIX if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) gBattleCommunication[MULTISTRING_CHOOSER] = 2; else #endif gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_MIST; break; } gBattlescriptCurrInstr = gBattlescriptsForUsingItem[*(gBattleStruct->AI_itemType + gBattlerAttacker / 2)]; } 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) 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++; } else if (gBattleMons[battler].ability == 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 { if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) { if (InBattlePyramid()) { pyramidMultiplier = GetPyramidRunMultiplier(); speedVar = (gBattleMons[battler].speed * pyramidMultiplier) / (gBattleMons[BATTLE_OPPOSITE(battler)].speed) + (gBattleStruct->runTries * 30); if (speedVar > (Random() & 0xFF)) effect++; } else if (gBattleMons[battler].speed < gBattleMons[BATTLE_OPPOSITE(battler)].speed) { speedVar = (gBattleMons[battler].speed * 128) / (gBattleMons[BATTLE_OPPOSITE(battler)].speed) + (gBattleStruct->runTries * 30); if (speedVar > (Random() & 0xFF)) effect++; } else // same speed or faster { effect++; } } gBattleStruct->runTries++; } if (effect) { 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 (gBattleMons[gBattlerAttacker].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION)) { 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 = gBattlescriptsForBallThrow[ITEM_SAFARI_BALL]; gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT; } void HandleAction_ThrowPokeblock(void) { gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; gBattle_BG0_X = 0; gBattle_BG0_Y = 0; gBattleCommunication[MULTISTRING_CHOOSER] = gBattleBufferB[gBattlerAttacker][1] - 1; gLastUsedItem = gBattleBufferB[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_IGNORE_ON_AIR | HITMARKER_IGNORE_UNDERGROUND | HITMARKER_IGNORE_UNDERWATER | HITMARKER_PASSIVE_DAMAGE | HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT | HITMARKER_CHARGING | HITMARKER_NEVER_SET); } void HandleAction_ActionFinished(void) { *(gBattleStruct->monToSwitchIntoId + gBattlerByTurnOrder[gCurrentTurnActionNumber]) = PARTY_SIZE; gCurrentTurnActionNumber++; gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber]; SpecialStatusesClear(); gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED | HITMARKER_NO_PPDEDUCT | HITMARKER_IGNORE_SAFEGUARD | HITMARKER_IGNORE_ON_AIR | HITMARKER_IGNORE_UNDERGROUND | HITMARKER_IGNORE_UNDERWATER | HITMARKER_PASSIVE_DAMAGE | HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT | HITMARKER_CHARGING | HITMARKER_NEVER_SET); gCurrentMove = 0; gBattleMoveDamage = 0; gMoveResultFlags = 0; gBattleScripting.animTurn = 0; gBattleScripting.animTargetsHit = 0; gLastLandedMoves[gBattlerAttacker] = 0; gLastHitByType[gBattlerAttacker] = 0; gBattleStruct->dynamicMoveType = 0; gDynamicBasePower = 0; gBattleScripting.moveendState = 0; gBattleCommunication[3] = 0; gBattleCommunication[4] = 0; gBattleScripting.multihitMoveEffect = 0; gBattleResources->battleScriptsStack->size = 0; } static const u16 sSoundMovesTable[] = { MOVE_GROWL, MOVE_ROAR, MOVE_SING, MOVE_SUPERSONIC, MOVE_SCREECH, MOVE_SNORE, MOVE_UPROAR, MOVE_METAL_SOUND, MOVE_GRASS_WHISTLE, MOVE_HYPER_VOICE, 0xFFFF }; u8 GetBattlerForBattleScript(u8 caseId) { u8 ret = 0; switch (caseId) { case BS_TARGET: ret = gBattlerTarget; break; case BS_ATTACKER: ret = gBattlerAttacker; break; case BS_EFFECT_BATTLER: ret = gEffectBattler; break; case BS_BATTLER_0: ret = 0; break; case BS_SCRIPTING: ret = gBattleScripting.battler; break; case BS_FAINTED: ret = gBattlerFainted; break; case 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_NOT_ATTACKER_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; } return ret; } void PressurePPLose(u8 target, u8 attacker, u16 move) { int moveIndex; if (gBattleMons[target].ability != 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) && gBattleMons[i].ability == 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 (gBattleMons[i].ability == 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); } } void MarkAllBattlersForControllerExec(void) // unused { 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]; } } 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) { gBattleMons[battler].status2 &= ~STATUS2_MULTIPLETURNS; gBattleMons[battler].status2 &= ~STATUS2_LOCK_CONFUSE; gBattleMons[battler].status2 &= ~STATUS2_UPROAR; gBattleMons[battler].status2 &= ~STATUS2_BIDE; gStatuses3[battler] &= ~STATUS3_SEMI_INVULNERABLE; gDisableStructs[battler].rolloutTimer = 0; gDisableStructs[battler].furyCutterCounter = 0; } bool8 WasUnableToUseMove(u8 battler) { if (gProtectStructs[battler].prlzImmobility || gProtectStructs[battler].targetNotAffected || gProtectStructs[battler].usedImprisonedMove || gProtectStructs[battler].loveImmobility || gProtectStructs[battler].usedDisabledMove || gProtectStructs[battler].usedTauntedMove || gProtectStructs[battler].flag2Unknown || gProtectStructs[battler].flinchImmobility || gProtectStructs[battler].confusionSelfDmg) return TRUE; else return FALSE; } void PrepareStringBattle(u16 stringId, u8 battler) { gActiveBattler = battler; BtlController_EmitPrintString(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]; } u8 TrySetCantSelectMoveBattleScript(void) { u8 limitations = 0; u16 move = gBattleMons[gActiveBattler].moves[gBattleBufferB[gActiveBattler][2]]; u8 holdEffect; u16* choicedMove = &gBattleStruct->choicedMove[gActiveBattler]; if (gDisableStructs[gActiveBattler].disabledMove == move && move != 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 = 1; } } if (move == gLastMoves[gActiveBattler] && move != MOVE_STRUGGLE && (gBattleMons[gActiveBattler].status2 & STATUS2_TORMENT)) { CancelMultiTurnMoves(gActiveBattler); if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingTormentedMoveInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingTormentedMove; limitations++; } } if (gDisableStructs[gActiveBattler].tauntTimer != 0 && gBattleMoves[move].power == 0) { gCurrentMove = move; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gPalaceSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveTauntInPalace; gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveTaunt; limitations++; } } if (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 (gBattleMons[gActiveBattler].item == ITEM_ENIGMA_BERRY) holdEffect = gEnigmaBerries[gActiveBattler].holdEffect; else holdEffect = ItemId_GetHoldEffect(gBattleMons[gActiveBattler].item); gPotentialItemEffectBattler = gActiveBattler; if (holdEffect == HOLD_EFFECT_CHOICE_BAND && *choicedMove != MOVE_NONE && *choicedMove != 0xFFFF && *choicedMove != move) { gCurrentMove = *choicedMove; gLastUsedItem = gBattleMons[gActiveBattler].item; if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveChoiceItem; limitations++; } } if (gBattleMons[gActiveBattler].pp[gBattleBufferB[gActiveBattler][2]] == 0) { if (gBattleTypeFlags & BATTLE_TYPE_PALACE) { gProtectStructs[gActiveBattler].palaceUnableToUseMove = TRUE; } else { gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingMoveWithNoPP; limitations++; } } return limitations; } u8 CheckMoveLimitations(u8 battlerId, u8 unusableMoves, u8 check) { u8 holdEffect; u16 *choicedMove = &gBattleStruct->choicedMove[battlerId]; s32 i; if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY) holdEffect = gEnigmaBerries[battlerId].holdEffect; else holdEffect = ItemId_GetHoldEffect(gBattleMons[battlerId].item); gPotentialItemEffectBattler = battlerId; for (i = 0; i < MAX_MON_MOVES; i++) { // No move if (gBattleMons[battlerId].moves[i] == MOVE_NONE && check & MOVE_LIMITATION_ZEROMOVE) unusableMoves |= gBitTable[i]; // No PP if (gBattleMons[battlerId].pp[i] == 0 && check & MOVE_LIMITATION_PP) unusableMoves |= gBitTable[i]; // Disable if (gBattleMons[battlerId].moves[i] == gDisableStructs[battlerId].disabledMove && check & MOVE_LIMITATION_DISABLED) unusableMoves |= gBitTable[i]; // Torment if (gBattleMons[battlerId].moves[i] == gLastMoves[battlerId] && check & MOVE_LIMITATION_TORMENTED && gBattleMons[battlerId].status2 & STATUS2_TORMENT) unusableMoves |= gBitTable[i]; // Taunt if (gDisableStructs[battlerId].tauntTimer && check & MOVE_LIMITATION_TAUNT && gBattleMoves[gBattleMons[battlerId].moves[i]].power == 0) unusableMoves |= gBitTable[i]; // Imprison if (GetImprisonedMovesCount(battlerId, gBattleMons[battlerId].moves[i]) && check & MOVE_LIMITATION_IMPRISON) unusableMoves |= gBitTable[i]; // Encore if (gDisableStructs[battlerId].encoreTimer && gDisableStructs[battlerId].encoredMove != gBattleMons[battlerId].moves[i]) unusableMoves |= gBitTable[i]; // Choice Band if (holdEffect == HOLD_EFFECT_CHOICE_BAND && *choicedMove != MOVE_NONE && *choicedMove != 0xFFFF && *choicedMove != gBattleMons[battlerId].moves[i]) unusableMoves |= gBitTable[i]; } return unusableMoves; } #define ALL_MOVES_MASK ((1 << MAX_MON_MOVES) - 1) bool8 AreAllMovesUnusable(void) { u8 unusable; 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; } enum { ENDTURN_ORDER, ENDTURN_REFLECT, ENDTURN_LIGHT_SCREEN, ENDTURN_MIST, ENDTURN_SAFEGUARD, ENDTURN_WISH, ENDTURN_RAIN, ENDTURN_SANDSTORM, ENDTURN_SUN, ENDTURN_HAIL, ENDTURN_FIELD_COUNT, }; u8 DoFieldEndTurnEffects(void) { u8 effect = 0; s32 i; for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount && gAbsentBattlerFlags & gBitTable[gBattlerAttacker]; gBattlerAttacker++) { } for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount && gAbsentBattlerFlags & gBitTable[gBattlerTarget]; gBattlerTarget++) { } do { u8 side; switch (gBattleStruct->turnCountersTracker) { case ENDTURN_ORDER: for (i = 0; i < gBattlersCount; i++) { gBattlerByTurnOrder[i] = i; } for (i = 0; i < gBattlersCount - 1; i++) { s32 j; for (j = i + 1; j < gBattlersCount; j++) { if (GetWhoStrikesFirst(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], 0)) SwapTurnOrder(i, j); } } // It's stupid, but won't match without it { u8* var = &gBattleStruct->turnCountersTracker; (*var)++; 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) break; } if (!effect) { 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) 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) break; } if (!effect) { 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) 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) break; } if (!effect) { gBattleStruct->turnCountersTracker++; } break; case ENDTURN_RAIN: if (gBattleWeather & B_WEATHER_RAIN) { if (!(gBattleWeather & B_WEATHER_RAIN_PERMANENT)) { 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) && --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 (--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_FIELD_COUNT: effect++; break; } } while (effect == 0); return (gBattleMainFunc != BattleTurnPassed); } enum { ENDTURN_INGRAIN, ENDTURN_ABILITIES, ENDTURN_ITEMS1, ENDTURN_LEECH_SEED, ENDTURN_POISON, ENDTURN_BAD_POISON, ENDTURN_BURN, ENDTURN_NIGHTMARES, ENDTURN_CURSE, ENDTURN_WRAP, ENDTURN_UPROAR, ENDTURN_THRASH, ENDTURN_DISABLE, ENDTURN_ENCORE, ENDTURN_LOCK_ON, ENDTURN_CHARGE, ENDTURN_TAUNT, ENDTURN_YAWN, ENDTURN_ITEMS2, ENDTURN_BATTLER_COUNT }; u8 DoBattlerEndTurnEffects(void) { u8 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++; } else { switch (gBattleStruct->turnEffectsTracker) { case ENDTURN_INGRAIN: // ingrain if ((gStatuses3[gActiveBattler] & STATUS3_ROOTED) && gBattleMons[gActiveBattler].hp != gBattleMons[gActiveBattler].maxHP && gBattleMons[gActiveBattler].hp != 0) { gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; BattleScriptExecute(BattleScript_IngrainTurnHeal); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_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_LEECH_SEED: // leech seed if ((gStatuses3[gActiveBattler] & STATUS3_LEECHSEED) && gBattleMons[gStatuses3[gActiveBattler] & STATUS3_LEECHSEED_BATTLER].hp != 0 && gBattleMons[gActiveBattler].hp != 0) { gBattlerTarget = gStatuses3[gActiveBattler] & STATUS3_LEECHSEED_BATTLER; // Notice gBattlerTarget is actually the HP receiver. gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleScripting.animArg1 = gBattlerTarget; gBattleScripting.animArg2 = gBattlerAttacker; BattleScriptExecute(BattleScript_LeechSeedTurnDrain); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_POISON: // poison if ((gBattleMons[gActiveBattler].status1 & STATUS1_POISON) && gBattleMons[gActiveBattler].hp != 0) { 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) { 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) { gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptExecute(BattleScript_BurnTurnDmg); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_NIGHTMARES: // spooky nightmares if ((gBattleMons[gActiveBattler].status2 & STATUS2_NIGHTMARE) && gBattleMons[gActiveBattler].hp != 0) { // 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) { gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 4; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptExecute(BattleScript_CurseTurnDmg); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_WRAP: // wrap if ((gBattleMons[gActiveBattler].status2 & STATUS2_WRAPPED) && gBattleMons[gActiveBattler].hp != 0) { gBattleMons[gActiveBattler].status2 -= STATUS2_WRAPPED_TURN(1); if (gBattleMons[gActiveBattler].status2 & STATUS2_WRAPPED) // damaged by wrap { // This is the only way I could get this array access to match. gBattleScripting.animArg1 = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 0); gBattleScripting.animArg2 = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 1); gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN; gBattleTextBuff1[1] = B_BUFF_MOVE; gBattleTextBuff1[2] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 0); gBattleTextBuff1[3] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 1); gBattleTextBuff1[4] = EOS; gBattlescriptCurrInstr = BattleScript_WrapTurnDmg; gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; } else // broke free { gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN; gBattleTextBuff1[1] = B_BUFF_MOVE; gBattleTextBuff1[2] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 0); gBattleTextBuff1[3] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 1); gBattleTextBuff1[4] = EOS; gBattlescriptCurrInstr = BattleScript_WrapEnds; } BattleScriptExecute(gBattlescriptCurrInstr); effect++; } gBattleStruct->turnEffectsTracker++; break; case ENDTURN_UPROAR: // uproar if (gBattleMons[gActiveBattler].status2 & STATUS2_UPROAR) { for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) { if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && gBattleMons[gBattlerAttacker].ability != ABILITY_SOUNDPROOF) { gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_SLEEP; gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE; gBattleCommunication[MULTISTRING_CHOOSER] = 1; BattleScriptExecute(BattleScript_MonWokeUpInUproar); gActiveBattler = gBattlerAttacker; BtlController_EmitSetMonData(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); 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 if (gBattleMons[gActiveBattler].status2 & STATUS2_LOCK_CONFUSE) { 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)) { gBattleCommunication[MOVE_EFFECT_BYTE] = 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_DISABLE: // disable if (gDisableStructs[gActiveBattler].disableTimer != 0) { s32 i; 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--; gBattleStruct->turnEffectsTracker++; break; case ENDTURN_YAWN: // yawn if (gStatuses3[gActiveBattler] & STATUS3_YAWN) { gStatuses3[gActiveBattler] -= STATUS3_YAWN_TURN(1); if (!(gStatuses3[gActiveBattler] & STATUS3_YAWN) && !(gBattleMons[gActiveBattler].status1 & STATUS1_ANY) && gBattleMons[gActiveBattler].ability != ABILITY_VITAL_SPIRIT && gBattleMons[gActiveBattler].ability != ABILITY_INSOMNIA && !UproarWakeUpCheck(gActiveBattler)) { CancelMultiTurnMoves(gActiveBattler); gBattleMons[gActiveBattler].status1 |= STATUS1_SLEEP_TURN((Random() & 3) + 2); // 2-5 turns of sleep BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); gEffectBattler = gActiveBattler; BattleScriptExecute(BattleScript_YawnMakesAsleep); effect++; } } 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]; gBattleMoveDamage = gWishFutureKnock.futureSightDmg[gActiveBattler]; gSpecialStatuses[gBattlerTarget].dmg = 0xFFFF; BattleScriptExecute(BattleScript_MonTookFutureAttack); if (gWishFutureKnock.futureSightCounter[gActiveBattler] == 0 && gWishFutureKnock.futureSightCounter[gActiveBattler ^ BIT_FLANK] == 0) { gSideStatuses[GET_BATTLER_SIDE(gBattlerTarget)] &= ~SIDE_STATUS_FUTUREATTACK; } return TRUE; } } // Why do I have to keep doing this to match? { u8 *state = &gBattleStruct->wishPerishSongState; *state = 1; gBattleStruct->wishPerishSongBattlerId = 0; } // fall through case 1: while (gBattleStruct->wishPerishSongBattlerId < gBattlersCount) { gActiveBattler = gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->wishPerishSongBattlerId]; if (gAbsentBattlerFlags & gBitTable[gActiveBattler]) { gBattleStruct->wishPerishSongBattlerId++; continue; } gBattleStruct->wishPerishSongBattlerId++; if (gStatuses3[gActiveBattler] & STATUS3_PERISH_SONG) { PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[gActiveBattler].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 7 bool8 HandleFaintedMonActions(void) { if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) return FALSE; do { s32 i; switch (gBattleStruct->faintedActionsState) { case 0: gBattleStruct->faintedActionsBattlerId = 0; gBattleStruct->faintedActionsState++; for (i = 0; i < gBattlersCount; i++) { if (gAbsentBattlerFlags & gBitTable[i] && !HasNoMonsToSwitch(i, 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; break; case 3: gBattleStruct->faintedActionsBattlerId = 0; gBattleStruct->faintedActionsState++; // fall through case 4: do { gBattlerFainted = gBattlerTarget = gBattleStruct->faintedActionsBattlerId; if (gBattleMons[gBattleStruct->faintedActionsBattlerId].hp == 0 && !(gAbsentBattlerFlags & gBitTable[gBattleStruct->faintedActionsBattlerId])) { BattleScriptExecute(BattleScript_HandleFaintedMon); gBattleStruct->faintedActionsState = 5; return TRUE; } } while (++gBattleStruct->faintedActionsBattlerId != gBattlersCount); gBattleStruct->faintedActionsState = 6; break; case 5: if (++gBattleStruct->faintedActionsBattlerId == gBattlersCount) gBattleStruct->faintedActionsState = 6; else gBattleStruct->faintedActionsState = 4; break; case 6: if (AbilityBattleEffects(ABILITYEFFECT_INTIMIDATE1, 0, 0, 0, 0) || AbilityBattleEffects(ABILITYEFFECT_TRACE, 0, 0, 0, 0) || ItemBattleEffects(ITEMEFFECT_NORMAL, 0, TRUE) || AbilityBattleEffects(ABILITYEFFECT_FORECAST, 0, 0, 0, 0)) return TRUE; gBattleStruct->faintedActionsState++; break; case FAINTED_ACTIONS_MAX_CASE: break; } } while (gBattleStruct->faintedActionsState != FAINTED_ACTIONS_MAX_CASE); return FALSE; } void TryClearRageStatuses(void) { s32 i; for (i = 0; i < gBattlersCount; i++) { if ((gBattleMons[i].status2 & STATUS2_RAGE) && gChosenMoveByBattler[i] != MOVE_RAGE) gBattleMons[i].status2 &= ~STATUS2_RAGE; } } enum { CANCELLER_FLAGS, CANCELLER_ASLEEP, CANCELLER_FROZEN, CANCELLER_TRUANT, CANCELLER_RECHARGE, CANCELLER_FLINCH, CANCELLER_DISABLED, CANCELLER_TAUNTED, CANCELLER_IMPRISONED, CANCELLER_CONFUSED, CANCELLER_PARALYSED, CANCELLER_IN_LOVE, CANCELLER_BIDE, CANCELLER_THAW, CANCELLER_END, }; 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; 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 (gBattleMons[gBattlerAttacker].ability == ABILITY_EARLY_BIRD) toSub = 2; else toSub = 1; if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) < toSub) gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_SLEEP; else gBattleMons[gBattlerAttacker].status1 -= toSub; if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) { if (gCurrentMove != MOVE_SNORE && gCurrentMove != MOVE_SLEEP_TALK) { gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 2; } } else { gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE; BattleScriptPushCursor(); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WOKE_UP; gBattlescriptCurrInstr = BattleScript_MoveUsedWokeUp; effect = 2; } } } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_FROZEN: // check being frozen if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE) { if (Random() % 5) { if (gBattleMoves[gCurrentMove].effect != EFFECT_THAW_HIT) // unfreezing via a move effect happens in case 13 { gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen; gHitMarker |= HITMARKER_NO_ATTACKSTRING; } else { gBattleStruct->atkCancellerTracker++; break; } } else // unfreeze { gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_FREEZE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MoveUsedUnfroze; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED; } effect = 2; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_TRUANT: // truant if (gBattleMons[gBattlerAttacker].ability == ABILITY_TRUANT && gDisableStructs[gBattlerAttacker].truantCounter) { CancelMultiTurnMoves(gBattlerAttacker); gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LOAFING; gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; 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) { gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_FLINCHED; gProtectStructs[gBattlerAttacker].flinchImmobility = 1; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_DISABLED: // disabled move if (gDisableStructs[gBattlerAttacker].disabledMove == gCurrentMove && gDisableStructs[gBattlerAttacker].disabledMove != 0) { gProtectStructs[gBattlerAttacker].usedDisabledMove = 1; gBattleScripting.battler = gBattlerAttacker; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_TAUNTED: // taunt if (gDisableStructs[gBattlerAttacker].tauntTimer && gBattleMoves[gCurrentMove].power == 0) { gProtectStructs[gBattlerAttacker].usedTauntedMove = 1; CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_IMPRISONED: // imprisoned if (GetImprisonedMovesCount(gBattlerAttacker, gCurrentMove)) { gProtectStructs[gBattlerAttacker].usedImprisonedMove = 1; 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) { if (Random() & 1) { // The MULTISTRING_CHOOSER is used here as a bool to signal // to BattleScript_MoveUsedIsConfused whether or not damage was taken gBattleCommunication[MULTISTRING_CHOOSER] = FALSE; BattleScriptPushCursor(); } else // confusion dmg { gBattleCommunication[MULTISTRING_CHOOSER] = TRUE; gBattlerTarget = gBattlerAttacker; gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[gBattlerAttacker], &gBattleMons[gBattlerAttacker], MOVE_POUND, 0, 40, 0, gBattlerAttacker, gBattlerAttacker); gProtectStructs[gBattlerAttacker].confusionSelfDmg = 1; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; } gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfused; } else // snapped out of confusion { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfusedNoMore; } effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_PARALYSED: // paralysis if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && (Random() % 4) == 0) { gProtectStructs[gBattlerAttacker].prlzImmobility = 1; // This is removed in Emerald for some reason //CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; effect = 1; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_IN_LOVE: // infatuation if (gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) { gBattleScripting.battler = CountTrailingZeroBits((gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) >> 0x10); if (Random() & 1) { BattleScriptPushCursor(); } else { BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack); gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; gProtectStructs[gBattlerAttacker].loveImmobility = 1; 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 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_THAW_HIT) { gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_FREEZE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_MoveUsedUnfroze; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED_BY_MOVE; } effect = 2; } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_END: break; } } while (gBattleStruct->atkCancellerTracker != CANCELLER_END && effect == 0); if (effect == 2) { gActiveBattler = gBattlerAttacker; BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); } return effect; } bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) { struct Pokemon *party; u8 id1, id2; s32 i; if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) return FALSE; if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; id1 = ((battler & BIT_FLANK) / 2); for (i = id1 * MULTI_PARTY_SIZE; i < id1 * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG) break; } return (i == id1 * 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; id2 = GetBattlerMultiplayerId(battler); id1 = GetLinkTrainerFlankId(id2); } else { party = gEnemyParty; if (battler == 1) id1 = 0; else id1 = 1; } } else { id2 = GetBattlerMultiplayerId(battler); if (GetBattlerSide(battler) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; id1 = GetLinkTrainerFlankId(id2); } for (i = id1 * MULTI_PARTY_SIZE; i < id1 * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG) break; } return (i == id1 * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE); } else if ((gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && GetBattlerSide(battler) == B_SIDE_OPPONENT) { party = gEnemyParty; if (battler == 1) id1 = 0; else id1 = MULTI_PARTY_SIZE; for (i = id1; i < id1 + MULTI_PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG) break; } return (i == id1 + 3); } else { if (GetBattlerSide(battler) == B_SIDE_OPPONENT) { id2 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); id1 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); party = gEnemyParty; } else { id2 = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); id1 = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); party = gPlayerParty; } if (partyIdBattlerOn1 == PARTY_SIZE) partyIdBattlerOn1 = gBattlerPartyIndexes[id2]; if (partyIdBattlerOn2 == PARTY_SIZE) partyIdBattlerOn2 = gBattlerPartyIndexes[id1]; for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&party[i], MON_DATA_HP) != 0 && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG && i != partyIdBattlerOn1 && i != partyIdBattlerOn2 && i != *(gBattleStruct->monToSwitchIntoId + id2) && i != id1[gBattleStruct->monToSwitchIntoId]) break; } return (i == PARTY_SIZE); } } u8 CastformDataTypeChange(u8 battler) { u8 formChange = 0; if (gBattleMons[battler].species != SPECIES_CASTFORM || gBattleMons[battler].ability != ABILITY_FORECAST || gBattleMons[battler].hp == 0) return 0; // No change if (!WEATHER_HAS_EFFECT && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL)) { SET_BATTLER_TYPE(battler, TYPE_NORMAL); return CASTFORM_NORMAL + 1; } if (!WEATHER_HAS_EFFECT) return 0; // No change if (!(gBattleWeather & (B_WEATHER_RAIN | B_WEATHER_SUN | B_WEATHER_HAIL)) && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL)) { SET_BATTLER_TYPE(battler, TYPE_NORMAL); formChange = CASTFORM_NORMAL + 1; } if (gBattleWeather & B_WEATHER_SUN && !IS_BATTLER_OF_TYPE(battler, TYPE_FIRE)) { SET_BATTLER_TYPE(battler, TYPE_FIRE); formChange = CASTFORM_FIRE + 1; } if (gBattleWeather & B_WEATHER_RAIN && !IS_BATTLER_OF_TYPE(battler, TYPE_WATER)) { SET_BATTLER_TYPE(battler, TYPE_WATER); formChange = CASTFORM_WATER + 1; } if (gBattleWeather & B_WEATHER_HAIL && !IS_BATTLER_OF_TYPE(battler, TYPE_ICE)) { SET_BATTLER_TYPE(battler, TYPE_ICE); formChange = CASTFORM_ICE + 1; } return formChange; } u8 AbilityBattleEffects(u8 caseID, u8 battler, u8 ability, u8 special, u16 moveArg) { u8 effect = 0; struct Pokemon *pokeAtk; struct Pokemon *pokeDef; u16 speciesAtk; u16 speciesDef; u32 pidAtk; u32 pidDef; if (gBattlerAttacker >= gBattlersCount) gBattlerAttacker = battler; if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) pokeAtk = &gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]]; else pokeAtk = &gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker]]; if (gBattlerTarget >= gBattlersCount) gBattlerTarget = battler; if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER) pokeDef = &gPlayerParty[gBattlerPartyIndexes[gBattlerTarget]]; else pokeDef = &gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]; speciesAtk = GetMonData(pokeAtk, MON_DATA_SPECIES); pidAtk = GetMonData(pokeAtk, MON_DATA_PERSONALITY); speciesDef = GetMonData(pokeDef, MON_DATA_SPECIES); pidDef = GetMonData(pokeDef, MON_DATA_PERSONALITY); if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI)) // Why isn't that check done at the beginning? { u8 moveType; s32 i; u16 move; u8 side; u8 target1; if (special) gLastUsedAbility = special; else gLastUsedAbility = gBattleMons[battler].ability; if (moveArg) move = moveArg; else move = gCurrentMove; GET_MOVE_TYPE(move, moveType); switch (caseID) { case ABILITYEFFECT_ON_SWITCHIN: // 0 if (gBattlerAttacker >= gBattlersCount) gBattlerAttacker = battler; switch (gLastUsedAbility) { 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; gBattleScripting.battler = battler; effect++; } break; case WEATHER_SANDSTORM: if (!(gBattleWeather & B_WEATHER_SANDSTORM)) { gBattleWeather = B_WEATHER_SANDSTORM; gBattleScripting.animArg1 = B_ANIM_SANDSTORM_CONTINUES; gBattleScripting.battler = battler; effect++; } break; case WEATHER_DROUGHT: if (!(gBattleWeather & B_WEATHER_SUN)) { gBattleWeather = B_WEATHER_SUN; gBattleScripting.animArg1 = B_ANIM_SUN_CONTINUES; gBattleScripting.battler = battler; effect++; } break; } } if (effect) { gBattleCommunication[MULTISTRING_CHOOSER] = GetCurrentWeather(); BattleScriptPushCursorAndCallback(BattleScript_OverworldWeatherStarts); } break; case ABILITY_DRIZZLE: if (!(gBattleWeather & B_WEATHER_RAIN_PERMANENT)) { gBattleWeather = (B_WEATHER_RAIN_PERMANENT | B_WEATHER_RAIN_TEMPORARY); BattleScriptPushCursorAndCallback(BattleScript_DrizzleActivates); gBattleScripting.battler = battler; effect++; } break; case ABILITY_SAND_STREAM: if (!(gBattleWeather & B_WEATHER_SANDSTORM_PERMANENT)) { gBattleWeather = B_WEATHER_SANDSTORM; BattleScriptPushCursorAndCallback(BattleScript_SandstreamActivates); gBattleScripting.battler = battler; effect++; } break; case ABILITY_DROUGHT: if (!(gBattleWeather & B_WEATHER_SUN_PERMANENT)) { gBattleWeather = B_WEATHER_SUN; BattleScriptPushCursorAndCallback(BattleScript_DroughtActivates); gBattleScripting.battler = battler; effect++; } break; case ABILITY_INTIMIDATE: if (!(gSpecialStatuses[battler].intimidatedMon)) { gStatuses3[battler] |= STATUS3_INTIMIDATE_POKES; gSpecialStatuses[battler].intimidatedMon = 1; } break; case ABILITY_FORECAST: effect = CastformDataTypeChange(battler); if (effect) { BattleScriptPushCursorAndCallback(BattleScript_CastformChange); gBattleScripting.battler = battler; *(&gBattleStruct->formToChangeInto) = effect - 1; } break; case ABILITY_TRACE: if (!(gSpecialStatuses[battler].traced)) { gStatuses3[battler] |= STATUS3_TRACE; gSpecialStatuses[battler].traced = 1; } break; case ABILITY_CLOUD_NINE: case ABILITY_AIR_LOCK: { // that's a weird choice for a variable, why not use i or battler? for (target1 = 0; target1 < gBattlersCount; target1++) { effect = CastformDataTypeChange(target1); if (effect) { BattleScriptPushCursorAndCallback(BattleScript_CastformChange); gBattleScripting.battler = target1; *(&gBattleStruct->formToChangeInto) = effect - 1; break; } } } break; } break; case ABILITYEFFECT_ENDTURN: // 1 if (gBattleMons[battler].hp != 0) { gBattlerAttacker = battler; switch (gLastUsedAbility) { case ABILITY_RAIN_DISH: if (WEATHER_HAS_EFFECT && (gBattleWeather & B_WEATHER_RAIN) && gBattleMons[battler].maxHP > gBattleMons[battler].hp) { gLastUsedAbility = ABILITY_RAIN_DISH; // why BattleScriptPushCursorAndCallback(BattleScript_RainDishActivates); gBattleMoveDamage = gBattleMons[battler].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; effect++; } break; case ABILITY_SHED_SKIN: if ((gBattleMons[battler].status1 & STATUS1_ANY) && (Random() % 3) == 0) { if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON)) StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); if (gBattleMons[battler].status1 & STATUS1_SLEEP) StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); if (gBattleMons[battler].status1 & STATUS1_PARALYSIS) StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); if (gBattleMons[battler].status1 & STATUS1_BURN) StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); if (gBattleMons[battler].status1 & STATUS1_FREEZE) StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); gBattleMons[battler].status1 = 0; gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE; // fix nightmare glitch gBattleScripting.battler = gActiveBattler = battler; BattleScriptPushCursorAndCallback(BattleScript_ShedSkinActivates); BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); MarkBattlerForControllerExec(gActiveBattler); effect++; } break; case ABILITY_SPEED_BOOST: if (gBattleMons[battler].statStages[STAT_SPEED] < MAX_STAT_STAGE && 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_TRUANT: gDisableStructs[gBattlerAttacker].truantCounter ^= 1; break; } } break; case ABILITYEFFECT_MOVES_BLOCK: // 2 if (gLastUsedAbility == ABILITY_SOUNDPROOF) { for (i = 0; sSoundMovesTable[i] != 0xFFFF; i++) { if (sSoundMovesTable[i] == move) break; } if (sSoundMovesTable[i] != 0xFFFF) { if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS) gHitMarker |= HITMARKER_NO_PPDEDUCT; gBattlescriptCurrInstr = BattleScript_SoundproofProtected; effect = 1; } } break; case ABILITYEFFECT_ABSORBING: // 3 if (move) { switch (gLastUsedAbility) { case ABILITY_VOLT_ABSORB: if (moveType == TYPE_ELECTRIC && gBattleMoves[move].power != 0) { if (gProtectStructs[gBattlerAttacker].notFirstStrike) gBattlescriptCurrInstr = BattleScript_MoveHPDrain; else gBattlescriptCurrInstr = BattleScript_MoveHPDrain_PPLoss; effect = 1; } break; case ABILITY_WATER_ABSORB: if (moveType == TYPE_WATER && gBattleMoves[move].power != 0) { if (gProtectStructs[gBattlerAttacker].notFirstStrike) gBattlescriptCurrInstr = BattleScript_MoveHPDrain; else gBattlescriptCurrInstr = BattleScript_MoveHPDrain_PPLoss; effect = 1; } break; case ABILITY_FLASH_FIRE: if (moveType == TYPE_FIRE && !(gBattleMons[battler].status1 & STATUS1_FREEZE)) { if (!(gBattleResources->flags->flags[battler] & RESOURCE_FLAG_FLASH_FIRE)) { gBattleCommunication[MULTISTRING_CHOOSER] = 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 = 2; } else { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FLASH_FIRE_NO_BOOST; if (gProtectStructs[gBattlerAttacker].notFirstStrike) gBattlescriptCurrInstr = BattleScript_FlashFireBoost; else gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss; effect = 2; } } break; } if (effect == 1) { if (gBattleMons[battler].maxHP == gBattleMons[battler].hp) { if ((gProtectStructs[gBattlerAttacker].notFirstStrike)) gBattlescriptCurrInstr = BattleScript_MonMadeMoveUseless; else gBattlescriptCurrInstr = BattleScript_MonMadeMoveUseless_PPLoss; } else { gBattleMoveDamage = gBattleMons[battler].maxHP / 4; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; gBattleMoveDamage *= -1; } } } break; case ABILITYEFFECT_ON_DAMAGE: // Contact abilities and Color Change switch (gLastUsedAbility) { case ABILITY_COLOR_CHANGE: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && move != MOVE_STRUGGLE && gBattleMoves[move].power != 0 && TARGET_TURN_DAMAGED && !IS_BATTLER_OF_TYPE(battler, moveType) && gBattleMons[battler].hp != 0) { SET_BATTLER_TYPE(battler, moveType); PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ColorChangeActivates; effect++; } break; case ABILITY_ROUGH_SKIN: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT)) { gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_RoughSkinActivates; effect++; } break; case ABILITY_EFFECT_SPORE: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) && (Random() % 10) == 0) { do { gBattleCommunication[MOVE_EFFECT_BYTE] = Random() & 3; } while (gBattleCommunication[MOVE_EFFECT_BYTE] == 0); if (gBattleCommunication[MOVE_EFFECT_BYTE] == MOVE_EFFECT_BURN) gBattleCommunication[MOVE_EFFECT_BYTE] += 2; // 5 MOVE_EFFECT_PARALYSIS gBattleCommunication[MOVE_EFFECT_BYTE] += MOVE_EFFECT_AFFECTS_USER; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITY_POISON_POINT: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) && (Random() % 3) == 0) { gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_POISON; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITY_STATIC: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) && (Random() % 3) == 0) { gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_PARALYSIS; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITY_FLAME_BODY: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) && TARGET_TURN_DAMAGED && (Random() % 3) == 0) { gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITY_CUTE_CHARM: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleMons[gBattlerAttacker].hp != 0 && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && (gBattleMoves[move].flags & FLAG_MAKES_CONTACT) && TARGET_TURN_DAMAGED && gBattleMons[gBattlerTarget].hp != 0 && (Random() % 3) == 0 && gBattleMons[gBattlerAttacker].ability != ABILITY_OBLIVIOUS && GetGenderFromSpeciesAndPersonality(speciesAtk, pidAtk) != GetGenderFromSpeciesAndPersonality(speciesDef, pidDef) && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) && GetGenderFromSpeciesAndPersonality(speciesAtk, pidAtk) != MON_GENDERLESS && GetGenderFromSpeciesAndPersonality(speciesDef, pidDef) != MON_GENDERLESS) { gBattleMons[gBattlerAttacker].status2 |= STATUS2_INFATUATED_WITH(gBattlerTarget); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_CuteCharmActivates; effect++; } break; } break; case ABILITYEFFECT_IMMUNITY: // 5 for (battler = 0; battler < gBattlersCount; battler++) { switch (gBattleMons[battler].ability) { case ABILITY_IMMUNITY: if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON | STATUS1_TOXIC_COUNTER)) { StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); effect = 1; } break; case ABILITY_OWN_TEMPO: if (gBattleMons[battler].status2 & STATUS2_CONFUSION) { StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); effect = 2; } break; case ABILITY_LIMBER: if (gBattleMons[battler].status1 & STATUS1_PARALYSIS) { StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); effect = 1; } break; case ABILITY_INSOMNIA: case ABILITY_VITAL_SPIRIT: if (gBattleMons[battler].status1 & STATUS1_SLEEP) { gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE; StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); effect = 1; } break; case ABILITY_WATER_VEIL: if (gBattleMons[battler].status1 & STATUS1_BURN) { StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); effect = 1; } break; case ABILITY_MAGMA_ARMOR: if (gBattleMons[battler].status1 & STATUS1_FREEZE) { StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); effect = 1; } break; case ABILITY_OBLIVIOUS: if (gBattleMons[battler].status2 & STATUS2_INFATUATION) { StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); effect = 3; } break; } if (effect) { switch (effect) { case 1: // status cleared gBattleMons[battler].status1 = 0; break; case 2: // get rid of confusion gBattleMons[battler].status2 &= ~STATUS2_CONFUSION; break; case 3: // get rid of infatuation gBattleMons[battler].status2 &= ~STATUS2_INFATUATION; break; } BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_AbilityCuredStatus; gBattleScripting.battler = battler; gActiveBattler = battler; BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); return effect; } } break; case ABILITYEFFECT_FORECAST: // 6 for (battler = 0; battler < gBattlersCount; battler++) { if (gBattleMons[battler].ability == ABILITY_FORECAST) { effect = CastformDataTypeChange(battler); if (effect) { BattleScriptPushCursorAndCallback(BattleScript_CastformChange); gBattleScripting.battler = battler; *(&gBattleStruct->formToChangeInto) = effect - 1; return effect; } } } break; case ABILITYEFFECT_SYNCHRONIZE: // 7 if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT)) { gHitMarker &= ~HITMARKER_SYNCHRONISE_EFFECT; gBattleStruct->synchronizeMoveEffect &= ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC) gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON; gBattleCommunication[MOVE_EFFECT_BYTE] = gBattleStruct->synchronizeMoveEffect + MOVE_EFFECT_AFFECTS_USER; gBattleScripting.battler = gBattlerTarget; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SynchronizeActivates; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITYEFFECT_ATK_SYNCHRONIZE: // 8 if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT)) { gHitMarker &= ~HITMARKER_SYNCHRONISE_EFFECT; gBattleStruct->synchronizeMoveEffect &= ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC) gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON; gBattleCommunication[MOVE_EFFECT_BYTE] = gBattleStruct->synchronizeMoveEffect; gBattleScripting.battler = gBattlerAttacker; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_SynchronizeActivates; gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; effect++; } break; case ABILITYEFFECT_INTIMIDATE1: // 9 for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ABILITY_INTIMIDATE && gStatuses3[i] & STATUS3_INTIMIDATE_POKES) { gLastUsedAbility = ABILITY_INTIMIDATE; gStatuses3[i] &= ~STATUS3_INTIMIDATE_POKES; BattleScriptPushCursorAndCallback(BattleScript_IntimidateActivatesEnd3); gBattleStruct->intimidateBattler = i; effect++; break; } } break; case ABILITYEFFECT_TRACE: // 11 for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ABILITY_TRACE && (gStatuses3[i] & STATUS3_TRACE)) { u8 target2; side = (GetBattlerPosition(i) ^ BIT_SIDE) & BIT_SIDE; // side of the opposing pokemon target1 = GetBattlerAtPosition(side); target2 = GetBattlerAtPosition(side + BIT_FLANK); if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { if (gBattleMons[target1].ability != 0 && gBattleMons[target1].hp != 0 && gBattleMons[target2].ability != 0 && gBattleMons[target2].hp != 0) { gActiveBattler = GetBattlerAtPosition(((Random() & 1) * 2) | side); gBattleMons[i].ability = gBattleMons[gActiveBattler].ability; gLastUsedAbility = gBattleMons[gActiveBattler].ability; effect++; } else if (gBattleMons[target1].ability != 0 && gBattleMons[target1].hp != 0) { gActiveBattler = target1; gBattleMons[i].ability = gBattleMons[gActiveBattler].ability; gLastUsedAbility = gBattleMons[gActiveBattler].ability; effect++; } else if (gBattleMons[target2].ability != 0 && gBattleMons[target2].hp != 0) { gActiveBattler = target2; gBattleMons[i].ability = gBattleMons[gActiveBattler].ability; gLastUsedAbility = gBattleMons[gActiveBattler].ability; effect++; } } else { gActiveBattler = target1; if (gBattleMons[target1].ability && gBattleMons[target1].hp) { gBattleMons[i].ability = gBattleMons[target1].ability; gLastUsedAbility = gBattleMons[target1].ability; effect++; } } if (effect) { BattleScriptPushCursorAndCallback(BattleScript_TraceActivates); gStatuses3[i] &= ~STATUS3_TRACE; gBattleScripting.battler = i; PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gActiveBattler, gBattlerPartyIndexes[gActiveBattler]) PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility) break; } } } break; case ABILITYEFFECT_INTIMIDATE2: // 10 for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ABILITY_INTIMIDATE && (gStatuses3[i] & STATUS3_INTIMIDATE_POKES)) { gLastUsedAbility = ABILITY_INTIMIDATE; gStatuses3[i] &= ~STATUS3_INTIMIDATE_POKES; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_IntimidateActivates; gBattleStruct->intimidateBattler = i; effect++; break; } } break; case ABILITYEFFECT_CHECK_OTHER_SIDE: // 12 side = GetBattlerSide(battler); for (i = 0; i < gBattlersCount; i++) { if (GetBattlerSide(i) != side && gBattleMons[i].ability == ability) { gLastUsedAbility = ability; effect = i + 1; } } break; case ABILITYEFFECT_CHECK_BATTLER_SIDE: // 13 side = GetBattlerSide(battler); for (i = 0; i < gBattlersCount; i++) { if (GetBattlerSide(i) == side && gBattleMons[i].ability == ability) { gLastUsedAbility = ability; effect = i + 1; } } break; case ABILITYEFFECT_FIELD_SPORT: // 14 switch (gLastUsedAbility) { case ABILITYEFFECT_MUD_SPORT: for (i = 0; i < gBattlersCount; i++) { if (gStatuses3[i] & STATUS3_MUDSPORT) effect = i + 1; } break; case ABILITYEFFECT_WATER_SPORT: for (i = 0; i < gBattlersCount; i++) { if (gStatuses3[i] & STATUS3_WATERSPORT) 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_CHECK_ON_FIELD: // 19 for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ability && gBattleMons[i].hp != 0) { gLastUsedAbility = ability; effect = i + 1; } } break; case ABILITYEFFECT_CHECK_FIELD_EXCEPT_BATTLER: // 15 for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ability && i != battler) { gLastUsedAbility = ability; effect = i + 1; } } break; case ABILITYEFFECT_COUNT_OTHER_SIDE: // 16 side = GetBattlerSide(battler); for (i = 0; i < gBattlersCount; i++) { if (GetBattlerSide(i) != side && gBattleMons[i].ability == ability) { gLastUsedAbility = ability; effect++; } } break; case ABILITYEFFECT_COUNT_BATTLER_SIDE: // 17 side = GetBattlerSide(battler); for (i = 0; i < gBattlersCount; i++) { if (GetBattlerSide(i) == side && gBattleMons[i].ability == ability) { gLastUsedAbility = ability; effect++; } } break; case ABILITYEFFECT_COUNT_ON_FIELD: // 18 for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].ability == ability && i != battler) { gLastUsedAbility = ability; effect++; } } break; } if (effect && caseID < ABILITYEFFECT_CHECK_OTHER_SIDE && gLastUsedAbility != 0xFF) RecordAbilityBattle(battler, gLastUsedAbility); } return effect; } void BattleScriptExecute(const u8 *BS_ptr) { gBattlescriptCurrInstr = BS_ptr; gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size++] = gBattleMainFunc; gBattleMainFunc = RunBattleScriptCommands_PopCallbacksStack; gCurrentActionFuncId = 0; } void BattleScriptPushCursorAndCallback(const u8 *BS_ptr) { BattleScriptPushCursor(); gBattlescriptCurrInstr = BS_ptr; gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size++] = gBattleMainFunc; gBattleMainFunc = RunBattleScriptCommands; } enum { ITEM_NO_EFFECT, ITEM_STATUS_CHANGE, ITEM_EFFECT_OTHER, ITEM_PP_CHANGE, ITEM_HP_CHANGE, ITEM_STATS_CHANGE, }; #define TRY_EAT_CONFUSE_BERRY(flavor) \ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) \ { \ PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, flavor); \ gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam; \ if (gBattleMoveDamage == 0) \ gBattleMoveDamage = 1; \ if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) \ gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; \ gBattleMoveDamage *= -1; \ if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, flavor) < 0) \ BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); \ else \ BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); \ effect = ITEM_HP_CHANGE; \ } #define TRY_EAT_STAT_UP_BERRY(stat) \ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam \ && !moveTurn && gBattleMons[battlerId].statStages[stat] < MAX_STAT_STAGE) \ { \ PREPARE_STAT_BUFFER(gBattleTextBuff1, stat); \ gEffectBattler = battlerId; \ SET_STATCHANGER(stat, 1, FALSE); \ gBattleScripting.animArg1 = 14 + (stat); \ gBattleScripting.animArg2 = 0; \ BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); \ effect = ITEM_STATS_CHANGE; \ } u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) { int i = 0; u8 effect = ITEM_NO_EFFECT; u8 changedPP = 0; u8 battlerHoldEffect, atkHoldEffect, defHoldEffect; u8 battlerHoldEffectParam, atkHoldEffectParam, defHoldEffectParam; u16 atkItem, defItem; gLastUsedItem = gBattleMons[battlerId].item; if (gLastUsedItem == ITEM_ENIGMA_BERRY) { battlerHoldEffect = gEnigmaBerries[battlerId].holdEffect; battlerHoldEffectParam = gEnigmaBerries[battlerId].holdEffectParam; } else { battlerHoldEffect = ItemId_GetHoldEffect(gLastUsedItem); battlerHoldEffectParam = ItemId_GetHoldEffectParam(gLastUsedItem); } atkItem = gBattleMons[gBattlerAttacker].item; if (atkItem == ITEM_ENIGMA_BERRY) { atkHoldEffect = gEnigmaBerries[gBattlerAttacker].holdEffect; atkHoldEffectParam = gEnigmaBerries[gBattlerAttacker].holdEffectParam; } else { atkHoldEffect = ItemId_GetHoldEffect(atkItem); atkHoldEffectParam = ItemId_GetHoldEffectParam(atkItem); } // def variables are unused defItem = gBattleMons[gBattlerTarget].item; if (defItem == ITEM_ENIGMA_BERRY) { defHoldEffect = gEnigmaBerries[gBattlerTarget].holdEffect; defHoldEffectParam = gEnigmaBerries[gBattlerTarget].holdEffectParam; } else { defHoldEffect = ItemId_GetHoldEffect(defItem); defHoldEffectParam = ItemId_GetHoldEffectParam(defItem); } switch (caseID) { case ITEMEFFECT_ON_SWITCH_IN: switch (battlerHoldEffect) { case HOLD_EFFECT_DOUBLE_PRIZE: if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) gBattleStruct->moneyMultiplier = 2; break; case HOLD_EFFECT_RESTORE_STATS: for (i = 0; i < 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) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; gActiveBattler = gBattlerAttacker = battlerId; BattleScriptExecute(BattleScript_WhiteHerbEnd2); } break; } break; case ITEMEFFECT_NORMAL: if (gBattleMons[battlerId].hp) { switch (battlerHoldEffect) { case HOLD_EFFECT_RESTORE_HP: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) { gBattleMoveDamage = battlerHoldEffectParam; if (gBattleMons[battlerId].hp + battlerHoldEffectParam > gBattleMons[battlerId].maxHP) gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; gBattleMoveDamage *= -1; BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); effect = ITEM_HP_CHANGE; } break; case HOLD_EFFECT_RESTORE_PP: if (!moveTurn) { struct Pokemon *mon; u8 ppBonuses; u16 move; if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]]; else mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]]; for (i = 0; i < 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); if (changedPP + battlerHoldEffectParam > maxPP) changedPP = maxPP; else changedPP = changedPP + battlerHoldEffectParam; 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) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; gActiveBattler = gBattlerAttacker = battlerId; BattleScriptExecute(BattleScript_WhiteHerbEnd2); } break; case HOLD_EFFECT_LEFTOVERS: if (gBattleMons[battlerId].hp < gBattleMons[battlerId].maxHP && !moveTurn) { gBattleMoveDamage = gBattleMons[battlerId].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; gBattleMoveDamage *= -1; BattleScriptExecute(BattleScript_ItemHealHP_End2); effect = ITEM_HP_CHANGE; RecordItemEffectBattle(battlerId, battlerHoldEffect); } break; case HOLD_EFFECT_CONFUSE_SPICY: TRY_EAT_CONFUSE_BERRY(FLAVOR_SPICY); break; case HOLD_EFFECT_CONFUSE_DRY: TRY_EAT_CONFUSE_BERRY(FLAVOR_DRY); break; case HOLD_EFFECT_CONFUSE_SWEET: TRY_EAT_CONFUSE_BERRY(FLAVOR_SWEET); break; case HOLD_EFFECT_CONFUSE_BITTER: TRY_EAT_CONFUSE_BERRY(FLAVOR_BITTER); break; case HOLD_EFFECT_CONFUSE_SOUR: TRY_EAT_CONFUSE_BERRY(FLAVOR_SOUR); break; case HOLD_EFFECT_ATTACK_UP: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_ATK] < MAX_STAT_STAGE) { PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK); PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATROSE); // Only the Attack stat-up berry has this gEffectBattler = battlerId; SET_STATCHANGER(STAT_ATK, 1, FALSE); gBattleScripting.animArg1 = 14 + STAT_ATK; gBattleScripting.animArg2 = 0; BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); effect = ITEM_STATS_CHANGE; } break; case HOLD_EFFECT_DEFENSE_UP: TRY_EAT_STAT_UP_BERRY(STAT_DEF); break; case HOLD_EFFECT_SPEED_UP: TRY_EAT_STAT_UP_BERRY(STAT_SPEED); break; case HOLD_EFFECT_SP_ATTACK_UP: TRY_EAT_STAT_UP_BERRY(STAT_SPATK); break; case HOLD_EFFECT_SP_DEFENSE_UP: TRY_EAT_STAT_UP_BERRY(STAT_SPDEF); break; case HOLD_EFFECT_CRITICAL_UP: if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && !(gBattleMons[battlerId].status2 & STATUS2_FOCUS_ENERGY)) { gBattleMons[battlerId].status2 |= STATUS2_FOCUS_ENERGY; BattleScriptExecute(BattleScript_BerryFocusEnergyEnd2); effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_RANDOM_STAT_UP: if (!moveTurn && gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam) { for (i = 0; i < NUM_STATS - 1; i++) { if (gBattleMons[battlerId].statStages[STAT_ATK + i] < MAX_STAT_STAGE) break; } if (i != NUM_STATS - 1) { do { i = Random() % (NUM_STATS - 1); } while (gBattleMons[battlerId].statStages[STAT_ATK + i] == MAX_STAT_STAGE); PREPARE_STAT_BUFFER(gBattleTextBuff1, i + 1); gBattleTextBuff2[0] = B_BUFF_PLACEHOLDER_BEGIN; gBattleTextBuff2[1] = B_BUFF_STRING; gBattleTextBuff2[2] = STRINGID_STATSHARPLY; gBattleTextBuff2[3] = STRINGID_STATSHARPLY >> 8; gBattleTextBuff2[4] = B_BUFF_STRING; gBattleTextBuff2[5] = STRINGID_STATROSE; gBattleTextBuff2[6] = STRINGID_STATROSE >> 8; gBattleTextBuff2[7] = EOS; gEffectBattler = battlerId; SET_STATCHANGER(i + 1, 2, FALSE); gBattleScripting.animArg1 = 0x21 + i + 6; gBattleScripting.animArg2 = 0; BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); effect = ITEM_STATS_CHANGE; } } break; case HOLD_EFFECT_CURE_PAR: if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS) { gBattleMons[battlerId].status1 &= ~STATUS1_PARALYSIS; BattleScriptExecute(BattleScript_BerryCurePrlzEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_PSN: if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY) { gBattleMons[battlerId].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER); BattleScriptExecute(BattleScript_BerryCurePsnEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_BRN: if (gBattleMons[battlerId].status1 & STATUS1_BURN) { gBattleMons[battlerId].status1 &= ~STATUS1_BURN; BattleScriptExecute(BattleScript_BerryCureBrnEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_FRZ: if (gBattleMons[battlerId].status1 & STATUS1_FREEZE) { gBattleMons[battlerId].status1 &= ~STATUS1_FREEZE; BattleScriptExecute(BattleScript_BerryCureFrzEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_SLP: if (gBattleMons[battlerId].status1 & STATUS1_SLEEP) { gBattleMons[battlerId].status1 &= ~STATUS1_SLEEP; gBattleMons[battlerId].status2 &= ~STATUS2_NIGHTMARE; BattleScriptExecute(BattleScript_BerryCureSlpEnd2); effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_CONFUSION: if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION) { gBattleMons[battlerId].status2 &= ~STATUS2_CONFUSION; BattleScriptExecute(BattleScript_BerryCureConfusionEnd2); effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_CURE_STATUS: if (gBattleMons[battlerId].status1 & STATUS1_ANY || gBattleMons[battlerId].status2 & STATUS2_CONFUSION) { i = 0; if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY) { StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); i++; } if (gBattleMons[battlerId].status1 & STATUS1_SLEEP) { gBattleMons[battlerId].status2 &= ~STATUS2_NIGHTMARE; StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); i++; } if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS) { StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); i++; } if (gBattleMons[battlerId].status1 & STATUS1_BURN) { StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); i++; } if (gBattleMons[battlerId].status1 & STATUS1_FREEZE) { StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); i++; } if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION) { StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); i++; } if (i <= 1) gBattleCommunication[MULTISTRING_CHOOSER] = 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_CURE_ATTRACT: if (gBattleMons[battlerId].status2 & STATUS2_INFATUATION) { gBattleMons[battlerId].status2 &= ~STATUS2_INFATUATION; StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); BattleScriptExecute(BattleScript_BerryCureChosenStatusEnd2); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_PROBLEM; effect = ITEM_EFFECT_OTHER; } break; } if (effect) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; gActiveBattler = gBattlerAttacker = 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_DUMMY: break; case ITEMEFFECT_MOVE_END: for (battlerId = 0; battlerId < gBattlersCount; battlerId++) { gLastUsedItem = gBattleMons[battlerId].item; if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY) { battlerHoldEffect = gEnigmaBerries[battlerId].holdEffect; battlerHoldEffectParam = gEnigmaBerries[battlerId].holdEffectParam; } else { battlerHoldEffect = ItemId_GetHoldEffect(gLastUsedItem); battlerHoldEffectParam = ItemId_GetHoldEffectParam(gLastUsedItem); } switch (battlerHoldEffect) { case HOLD_EFFECT_CURE_PAR: if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS) { gBattleMons[battlerId].status1 &= ~STATUS1_PARALYSIS; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCureParRet; effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_PSN: if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY) { gBattleMons[battlerId].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCurePsnRet; effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_BRN: if (gBattleMons[battlerId].status1 & STATUS1_BURN) { gBattleMons[battlerId].status1 &= ~STATUS1_BURN; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCureBrnRet; effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_FRZ: if (gBattleMons[battlerId].status1 & STATUS1_FREEZE) { gBattleMons[battlerId].status1 &= ~STATUS1_FREEZE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCureFrzRet; effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_SLP: if (gBattleMons[battlerId].status1 & STATUS1_SLEEP) { gBattleMons[battlerId].status1 &= ~STATUS1_SLEEP; gBattleMons[battlerId].status2 &= ~STATUS2_NIGHTMARE; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCureSlpRet; effect = ITEM_STATUS_CHANGE; } break; case HOLD_EFFECT_CURE_CONFUSION: if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION) { gBattleMons[battlerId].status2 &= ~STATUS2_CONFUSION; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BerryCureConfusionRet; effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_CURE_ATTRACT: if (gBattleMons[battlerId].status2 & STATUS2_INFATUATION) { gBattleMons[battlerId].status2 &= ~STATUS2_INFATUATION; StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); BattleScriptPushCursor(); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CURED_PROBLEM; gBattlescriptCurrInstr = BattleScript_BerryCureChosenStatusRet; effect = ITEM_EFFECT_OTHER; } break; case HOLD_EFFECT_CURE_STATUS: if (gBattleMons[battlerId].status1 & STATUS1_ANY || gBattleMons[battlerId].status2 & STATUS2_CONFUSION) { if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY) StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); if (gBattleMons[battlerId].status1 & STATUS1_SLEEP) { gBattleMons[battlerId].status2 &= ~STATUS2_NIGHTMARE; StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); } if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS) StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); if (gBattleMons[battlerId].status1 & STATUS1_BURN) StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); if (gBattleMons[battlerId].status1 & STATUS1_FREEZE) StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION) StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); gBattleMons[battlerId].status1 = 0; gBattleMons[battlerId].status2 &= ~STATUS2_CONFUSION; BattleScriptPushCursor(); gBattleCommunication[MULTISTRING_CHOOSER] = 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) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_WhiteHerbRet; return effect; } break; } if (effect) { gBattleScripting.battler = battlerId; gPotentialItemEffectBattler = battlerId; gActiveBattler = battlerId; BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); break; } } break; case ITEMEFFECT_KINGSROCK_SHELLBELL: if (gBattleMoveDamage) { switch (atkHoldEffect) { case HOLD_EFFECT_FLINCH: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && (Random() % 100) < atkHoldEffectParam && gBattleMoves[gCurrentMove].flags & FLAG_KINGS_ROCK_AFFECTED && gBattleMons[gBattlerTarget].hp) { gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_FLINCH; BattleScriptPushCursor(); SetMoveEffect(FALSE, 0); BattleScriptPop(); } break; case HOLD_EFFECT_SHELL_BELL: if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gSpecialStatuses[gBattlerTarget].dmg != 0 && gSpecialStatuses[gBattlerTarget].dmg != 0xFFFF && gBattlerAttacker != gBattlerTarget && gBattleMons[gBattlerAttacker].hp != gBattleMons[gBattlerAttacker].maxHP && gBattleMons[gBattlerAttacker].hp != 0) { gLastUsedItem = atkItem; gPotentialItemEffectBattler = gBattlerAttacker; gBattleScripting.battler = gBattlerAttacker; gBattleMoveDamage = (gSpecialStatuses[gBattlerTarget].dmg / atkHoldEffectParam) * -1; if (gBattleMoveDamage == 0) gBattleMoveDamage = -1; gSpecialStatuses[gBattlerTarget].dmg = 0; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ItemHealHP_Ret; effect++; } break; } } break; } return effect; } void ClearFuryCutterDestinyBondGrudge(u8 battlerId) { gDisableStructs[battlerId].furyCutterCounter = 0; gBattleMons[battlerId].status2 &= ~STATUS2_DESTINY_BOND; gStatuses3[battlerId] &= ~STATUS3_GRUDGE; } void HandleAction_RunBattleScript(void) // identical to RunBattleScriptCommands { if (gBattleControllerExecFlags == 0) gBattleScriptingCommandsTable[*gBattlescriptCurrInstr](); } u8 GetMoveTarget(u16 move, u8 setTarget) { u8 targetBattler = 0; u8 moveTarget; u8 side; if (setTarget != NO_TARGET_OVERRIDE) moveTarget = setTarget - 1; else moveTarget = gBattleMoves[move].target; switch (moveTarget) { case MOVE_TARGET_SELECTED: side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; if (gSideTimers[side].followmeTimer && gBattleMons[gSideTimers[side].followmeTarget].hp) targetBattler = gSideTimers[side].followmeTarget; else { side = GetBattlerSide(gBattlerAttacker); do { targetBattler = Random() % gBattlersCount; } while (targetBattler == gBattlerAttacker || side == GetBattlerSide(targetBattler) || gAbsentBattlerFlags & gBitTable[targetBattler]); if (gBattleMoves[move].type == TYPE_ELECTRIC && AbilityBattleEffects(ABILITYEFFECT_COUNT_OTHER_SIDE, gBattlerAttacker, ABILITY_LIGHTNING_ROD, 0, 0) && gBattleMons[targetBattler].ability != ABILITY_LIGHTNING_ROD) { targetBattler ^= BIT_FLANK; RecordAbilityBattle(targetBattler, gBattleMons[targetBattler].ability); gSpecialStatuses[targetBattler].lightningRodRedirected = 1; } } break; case MOVE_TARGET_DEPENDS: case MOVE_TARGET_BOTH: case MOVE_TARGET_FOES_AND_ALLY: case MOVE_TARGET_OPPONENTS_FIELD: targetBattler = GetBattlerAtPosition((GetBattlerPosition(gBattlerAttacker) & BIT_SIDE) ^ BIT_SIDE); if (gAbsentBattlerFlags & gBitTable[targetBattler]) targetBattler ^= BIT_FLANK; break; case MOVE_TARGET_RANDOM: side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; if (gSideTimers[side].followmeTimer && gBattleMons[gSideTimers[side].followmeTarget].hp) targetBattler = gSideTimers[side].followmeTarget; else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && moveTarget & MOVE_TARGET_RANDOM) { if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) { if (Random() & 1) targetBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); else targetBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); } else { if (Random() & 1) targetBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); else targetBattler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); } if (gAbsentBattlerFlags & gBitTable[targetBattler]) targetBattler ^= BIT_FLANK; } else targetBattler = GetBattlerAtPosition((GetBattlerPosition(gBattlerAttacker) & BIT_SIDE) ^ BIT_SIDE); break; case MOVE_TARGET_USER_OR_SELECTED: case MOVE_TARGET_USER: targetBattler = gBattlerAttacker; break; } *(gBattleStruct->moveTarget + gBattlerAttacker) = targetBattler; return targetBattler; } static bool32 IsMonEventLegal(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_EVENT_LEGAL, NULL); } u8 IsMonDisobedient(void) { s32 rnd; s32 calc; u8 obedienceLevel = 0; if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) return 0; if (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT) return 0; if (IsMonEventLegal(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 (!IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName)) return 0; if (FlagGet(FLAG_BADGE08_GET)) return 0; obedienceLevel = 10; if (FlagGet(FLAG_BADGE02_GET)) obedienceLevel = 30; if (FlagGet(FLAG_BADGE04_GET)) obedienceLevel = 50; if (FlagGet(FLAG_BADGE06_GET)) obedienceLevel = 70; } if (gBattleMons[gBattlerAttacker].level <= obedienceLevel) return 0; rnd = (Random() & 255); calc = (gBattleMons[gBattlerAttacker].level + obedienceLevel) * rnd >> 8; if (calc < obedienceLevel) return 0; // is not obedient if (gCurrentMove == MOVE_RAGE) gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_RAGE; if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP && (gCurrentMove == MOVE_SNORE || gCurrentMove == MOVE_SLEEP_TALK)) { gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep; return 1; } rnd = (Random() & 255); calc = (gBattleMons[gBattlerAttacker].level + 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 = gBattleMons[gBattlerAttacker].level - obedienceLevel; calc = (Random() & 255); if (calc < obedienceLevel && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY) && gBattleMons[gBattlerAttacker].ability != ABILITY_VITAL_SPIRIT && gBattleMons[gBattlerAttacker].ability != ABILITY_INSOMNIA) { // try putting asleep int i; for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].status2 & STATUS2_UPROAR) break; } if (i == gBattlersCount) { gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep; return 1; } } calc -= obedienceLevel; if (calc < obedienceLevel) { gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[gBattlerAttacker], &gBattleMons[gBattlerAttacker], MOVE_POUND, 0, 40, 0, gBattlerAttacker, gBattlerAttacker); 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; } } }