#include "global.h" #include "item.h" #include "text.h" #include "util.h" #include "link.h" #include "berry.h" #include "random.h" #include "pokemon.h" #include "string_util.h" #include "battle.h" #include "battle_anim.h" #include "battle_scripts.h" #include "battle_message.h" #include "battle_controllers.h" #include "constants/battle.h" #include "constants/moves.h" #include "constants/items.h" #include "constants/species.h" #include "constants/abilities.h" #include "constants/battle_anim.h" #include "constants/hold_effects.h" #include "constants/battle_move_effects.h" #include "constants/battle_script_commands.h" 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) { u32 ret = 0; switch (caseId) { case BS_TARGET: ret = gBattlerTarget; break; case BS_ATTACKER: ret = gBattlerAttacker; break; case BS_EFFECT_BATTLER: ret = gEffectBattler; break; case BS_BATTLER_0: ret = 0; break; case BS_SCRIPTING: ret = gBattleScripting.battler; break; case BS_FAINTED: ret = gBattlerFainted; break; case 5: ret = gBattlerFainted; break; case BS_PLAYER1: ret = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); break; case BS_OPPONENT1: ret = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); break; case 4: case 6: case 8: case 9: break; } return ret; } void PressurePPLose(u8 target, u8 attacker, u16 move) { s32 i; if (gBattleMons[target].ability == ABILITY_PRESSURE) { for (i = 0; i < MAX_MON_MOVES && gBattleMons[attacker].moves[i] != move; ++i); if (i != MAX_MON_MOVES) { if (gBattleMons[attacker].pp[i]) --gBattleMons[attacker].pp[i]; if (!(gBattleMons[attacker].status2 & STATUS2_TRANSFORMED) && !(gDisableStructs[attacker].mimickedMoves & gBitTable[i])) { gActiveBattler = attacker; BtlController_EmitSetMonData(0, REQUEST_PPMOVE1_BATTLE + i, 0, 1, &gBattleMons[gActiveBattler].pp[i]); MarkBattlerForControllerExec(gActiveBattler); } } } } void PressurePPLoseOnUsingImprison(u8 attacker) { s32 i, j; s32 imprisonPos = 4; 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 && gBattleMons[attacker].moves[j] != MOVE_IMPRISON; ++j); if (j != MAX_MON_MOVES) { imprisonPos = j; if (gBattleMons[attacker].pp[j]) --gBattleMons[attacker].pp[j]; } } } if (imprisonPos != 4 && !(gBattleMons[attacker].status2 & STATUS2_TRANSFORMED) && !(gDisableStructs[attacker].mimickedMoves & gBitTable[imprisonPos])) { gActiveBattler = attacker; BtlController_EmitSetMonData(0, REQUEST_PPMOVE1_BATTLE + imprisonPos, 0, 1, &gBattleMons[gActiveBattler].pp[imprisonPos]); MarkBattlerForControllerExec(gActiveBattler); } } void PressurePPLoseOnUsingPerishSong(u8 attacker) { s32 i, j; s32 perishSongPos = 4; for (i = 0; i < gBattlersCount; ++i) { if (gBattleMons[i].ability == ABILITY_PRESSURE && i != attacker) { for (j = 0; j < MAX_MON_MOVES && gBattleMons[attacker].moves[j] != MOVE_PERISH_SONG; ++j); if (j != MAX_MON_MOVES) { perishSongPos = j; if (gBattleMons[attacker].pp[j]) --gBattleMons[attacker].pp[j]; } } } if (perishSongPos != MAX_MON_MOVES && !(gBattleMons[attacker].status2 & STATUS2_TRANSFORMED) && !(gDisableStructs[attacker].mimickedMoves & gBitTable[perishSongPos])) { gActiveBattler = attacker; BtlController_EmitSetMonData(0, REQUEST_PPMOVE1_BATTLE + perishSongPos, 0, 1, &gBattleMons[gActiveBattler].pp[perishSongPos]); MarkBattlerForControllerExec(gActiveBattler); } } void MarkAllBattlersForControllerExec(void) { s32 i; if (gBattleTypeFlags & BATTLE_TYPE_LINK) for (i = 0; i < gBattlersCount; ++i) gBattleControllerExecFlags |= gBitTable[i] << 0x1C; else for (i = 0; i < gBattlersCount; ++i) gBattleControllerExecFlags |= gBitTable[i]; } void MarkBattlerForControllerExec(u8 battlerId) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) gBattleControllerExecFlags |= gBitTable[battlerId] << 0x1C; else gBattleControllerExecFlags |= gBitTable[battlerId]; } void sub_8017298(u8 arg0) { s32 i; for (i = 0; i < GetLinkPlayerCount(); ++i) gBattleControllerExecFlags |= gBitTable[arg0] << (i << 2); gBattleControllerExecFlags &= ~(0x10000000 << arg0); } void CancelMultiTurnMoves(u8 battler) { gBattleMons[battler].status2 &= ~(STATUS2_MULTIPLETURNS); gBattleMons[battler].status2 &= ~(STATUS2_LOCK_CONFUSE); gBattleMons[battler].status2 &= ~(STATUS2_UPROAR); gBattleMons[battler].status2 &= ~(STATUS2_BIDE); gStatuses3[battler] &= ~(STATUS3_SEMI_INVULNERABLE); gDisableStructs[battler].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(0, stringId); MarkBattlerForControllerExec(gActiveBattler); } void ResetSentPokesToOpponentValue(void) { s32 i; u32 bits = 0; gSentPokesToOpponent[0] = 0; gSentPokesToOpponent[1] = 0; for (i = 0; i < gBattlersCount; i += 2) bits |= gBitTable[gBattlerPartyIndexes[i]]; for (i = 1; i < gBattlersCount; i += 2) gSentPokesToOpponent[(i & BIT_FLANK) >> 1] = bits; } void sub_8017434(u8 battler) { s32 i = 0; u32 bits = 0; if (GetBattlerSide(battler) == B_SIDE_OPPONENT) { u8 flank = ((battler & BIT_FLANK) >> 1); gSentPokesToOpponent[flank] = 0; for (i = 0; i < gBattlersCount; i += 2) if (!(gAbsentBattlerFlags & gBitTable[i])) bits |= gBitTable[gBattlerPartyIndexes[i]]; gSentPokesToOpponent[flank] = bits; } } void sub_80174B8(u8 battler) { if (GetBattlerSide(battler) == B_SIDE_OPPONENT) { sub_8017434(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 holdEffect; u8 limitations = 0; u16 move = gBattleMons[gActiveBattler].moves[gBattleBufferB[gActiveBattler][2]]; u16* choicedMove = &gBattleStruct->choicedMove[gActiveBattler]; if (gDisableStructs[gActiveBattler].disabledMove == move && move != MOVE_NONE) { gBattleScripting.battler = gActiveBattler; gCurrentMove = move; gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingDisabledMove; limitations = 1; } if (move == gLastMoves[gActiveBattler] && move != MOVE_STRUGGLE && (gBattleMons[gActiveBattler].status2 & STATUS2_TORMENT)) { CancelMultiTurnMoves(gActiveBattler); gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingTormentedMove; ++limitations; } if (gDisableStructs[gActiveBattler].tauntTimer && !gBattleMoves[move].power) { gCurrentMove = move; gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveTaunt; ++limitations; } if (GetImprisonedMovesCount(gActiveBattler, move)) { gCurrentMove = move; 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 && *choicedMove != 0xFFFF && *choicedMove != move) { gCurrentMove = *choicedMove; gLastUsedItem = gBattleMons[gActiveBattler].item; gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveChoiceItem; ++limitations; } if (!gBattleMons[gActiveBattler].pp[gBattleBufferB[gActiveBattler][2]]) { 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) { if (gBattleMons[battlerId].moves[i] == 0 && check & MOVE_LIMITATION_ZEROMOVE) unusableMoves |= gBitTable[i]; if (gBattleMons[battlerId].pp[i] == 0 && check & MOVE_LIMITATION_PP) unusableMoves |= gBitTable[i]; if (gBattleMons[battlerId].moves[i] == gDisableStructs[battlerId].disabledMove && check & MOVE_LIMITATION_DISABLED) unusableMoves |= gBitTable[i]; if (gBattleMons[battlerId].moves[i] == gLastMoves[battlerId] && check & MOVE_LIMITATION_TORMENTED && gBattleMons[battlerId].status2 & STATUS2_TORMENT) unusableMoves |= gBitTable[i]; if (gDisableStructs[battlerId].tauntTimer && check & MOVE_LIMITATION_TAUNT && gBattleMoves[gBattleMons[battlerId].moves[i]].power == 0) unusableMoves |= gBitTable[i]; if (GetImprisonedMovesCount(battlerId, gBattleMons[battlerId].moves[i]) && check & MOVE_LIMITATION_IMPRISON) unusableMoves |= gBitTable[i]; if (gDisableStructs[battlerId].encoreTimer && gDisableStructs[battlerId].encoredMove != gBattleMons[battlerId].moves[i]) unusableMoves |= gBitTable[i]; if (holdEffect == HOLD_EFFECT_CHOICE_BAND && *choicedMove != 0 && *choicedMove != 0xFFFF && *choicedMove != gBattleMons[battlerId].moves[i]) unusableMoves |= gBitTable[i]; } return unusableMoves; } bool8 AreAllMovesUnusable(void) { u8 unusable = CheckMoveLimitations(gActiveBattler, 0, 0xFF); if (unusable == 0xF) // All moves are unusable. { gProtectStructs[gActiveBattler].noValidMoves = 1; gSelectionBattleScripts[gActiveBattler] = BattleScript_NoMovesLeft; if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) gBattleBufferB[gActiveBattler][3] = GetBattlerAtPosition((BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler))) | (Random() & 2)); else gBattleBufferB[gActiveBattler][3] = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler))); } else { gProtectStructs[gActiveBattler].noValidMoves = 0; } return (unusable == 0xF); } 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 && move != gBattleMons[i].moves[j]; ++j); 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); } { 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 & WEATHER_RAIN_ANY) { if (!(gBattleWeather & WEATHER_RAIN_PERMANENT)) { if (--gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~WEATHER_RAIN_TEMPORARY; gBattleWeather &= ~WEATHER_RAIN_DOWNPOUR; gBattleCommunication[MULTISTRING_CHOOSER] = 2; } else if (gBattleWeather & WEATHER_RAIN_DOWNPOUR) gBattleCommunication[MULTISTRING_CHOOSER] = 1; else gBattleCommunication[MULTISTRING_CHOOSER] = 0; } else if (gBattleWeather & WEATHER_RAIN_DOWNPOUR) { gBattleCommunication[MULTISTRING_CHOOSER] = 1; } else { gBattleCommunication[MULTISTRING_CHOOSER] = 0; } BattleScriptExecute(BattleScript_RainContinuesOrEnds); ++effect; } ++gBattleStruct->turnCountersTracker; break; case ENDTURN_SANDSTORM: if (gBattleWeather & WEATHER_SANDSTORM_ANY) { if (!(gBattleWeather & WEATHER_SANDSTORM_PERMANENT) && --gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~WEATHER_SANDSTORM_TEMPORARY; gBattlescriptCurrInstr = BattleScript_SandStormHailEnds; } else { gBattlescriptCurrInstr = BattleScript_DamagingWeatherContinues; } gBattleScripting.animArg1 = B_ANIM_SANDSTORM_CONTINUES; gBattleCommunication[MULTISTRING_CHOOSER] = 0; BattleScriptExecute(gBattlescriptCurrInstr); ++effect; } ++gBattleStruct->turnCountersTracker; break; case ENDTURN_SUN: if (gBattleWeather & WEATHER_SUN_ANY) { if (!(gBattleWeather & WEATHER_SUN_PERMANENT) && --gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~WEATHER_SUN_TEMPORARY; gBattlescriptCurrInstr = BattleScript_SunlightFaded; } else { gBattlescriptCurrInstr = BattleScript_SunlightContinues; } BattleScriptExecute(gBattlescriptCurrInstr); ++effect; } ++gBattleStruct->turnCountersTracker; break; case ENDTURN_HAIL: if (gBattleWeather & WEATHER_HAIL_ANY) { if (--gWishFutureKnock.weatherDuration == 0) { gBattleWeather &= ~WEATHER_HAIL; gBattlescriptCurrInstr = BattleScript_SandStormHailEnds; } else { gBattlescriptCurrInstr = BattleScript_DamagingWeatherContinues; } gBattleScripting.animArg1 = B_ANIM_HAIL_CONTINUES; gBattleCommunication[MULTISTRING_CHOOSER] = 1; BattleScriptExecute(gBattlescriptCurrInstr); ++effect; } ++gBattleStruct->turnCountersTracker; break; case ENDTURN_FIELD_COUNT: ++effect; break; } } while (!effect); 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_x20); 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(1, gActiveBattler, FALSE)) ++effect; ++gBattleStruct->turnEffectsTracker; break; case ENDTURN_ITEMS2: // item effects again if (ItemBattleEffects(1, 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 & 0xF00) != 0xF00) // not 16 turns gBattleMons[gActiveBattler].status1 += 0x100; gBattleMoveDamage *= (gBattleMons[gActiveBattler].status1 & 0xF00) >> 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 -= 0x2000; if (gBattleMons[gActiveBattler].status2 & STATUS2_WRAPPED) // damaged by wrap { gBattleScripting.animArg1 = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 0); gBattleScripting.animArg2 = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 1); gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN; gBattleTextBuff1[1] = B_BUFF_MOVE; gBattleTextBuff1[2] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 0); gBattleTextBuff1[3] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 1); gBattleTextBuff1[4] = EOS; gBattlescriptCurrInstr = BattleScript_WrapTurnDmg; gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; if (gBattleMoveDamage == 0) gBattleMoveDamage = 1; } else // broke free { gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN; gBattleTextBuff1[1] = B_BUFF_MOVE; gBattleTextBuff1[2] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 0); gBattleTextBuff1[3] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 1); gBattleTextBuff1[4] = EOS; gBattlescriptCurrInstr = BattleScript_WrapEnds; } BattleScriptExecute(gBattlescriptCurrInstr); ++effect; } ++gBattleStruct->turnEffectsTracker; break; case ENDTURN_UPROAR: // uproar if (gBattleMons[gActiveBattler].status2 & STATUS2_UPROAR) { for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; ++gBattlerAttacker) { if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && gBattleMons[gBattlerAttacker].ability != ABILITY_SOUNDPROOF) { gBattleMons[gBattlerAttacker].status1 &= ~(STATUS1_SLEEP); gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_NIGHTMARE); gBattleCommunication[MULTISTRING_CHOOSER] = 1; BattleScriptExecute(BattleScript_MonWokeUpInUproar); gActiveBattler = gBattlerAttacker; BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); break; } } if (gBattlerAttacker != gBattlersCount) { effect = 2; // a pokemon was awaken break; } else { gBattlerAttacker = gActiveBattler; gBattleMons[gActiveBattler].status2 -= 0x10; // uproar timer goes down if (WasUnableToUseMove(gActiveBattler)) { CancelMultiTurnMoves(gActiveBattler); gBattleCommunication[MULTISTRING_CHOOSER] = 1; } else if (gBattleMons[gActiveBattler].status2 & STATUS2_UPROAR) { gBattleCommunication[MULTISTRING_CHOOSER] = 0; gBattleMons[gActiveBattler].status2 |= STATUS2_MULTIPLETURNS; } else { gBattleCommunication[MULTISTRING_CHOOSER] = 1; CancelMultiTurnMoves(gActiveBattler); } BattleScriptExecute(BattleScript_PrintUproarOverTurns); effect = 1; } } if (effect != 2) ++gBattleStruct->turnEffectsTracker; break; case ENDTURN_THRASH: // thrash if (gBattleMons[gActiveBattler].status2 & STATUS2_LOCK_CONFUSE) { gBattleMons[gActiveBattler].status2 -= 0x400; if (WasUnableToUseMove(gActiveBattler)) CancelMultiTurnMoves(gActiveBattler); else if (!(gBattleMons[gActiveBattler].status2 & STATUS2_LOCK_CONFUSE) && (gBattleMons[gActiveBattler].status2 & STATUS2_MULTIPLETURNS)) { gBattleMons[gActiveBattler].status2 &= ~(STATUS2_MULTIPLETURNS); if (!(gBattleMons[gActiveBattler].status2 & STATUS2_CONFUSION)) { gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_CONFUSION | MOVE_EFFECT_AFFECTS_USER; SetMoveEffect(1, 0); if (gBattleMons[gActiveBattler].status2 & STATUS2_CONFUSION) BattleScriptExecute(BattleScript_ThrashConfuses); ++effect; } } } ++gBattleStruct->turnEffectsTracker; break; case ENDTURN_DISABLE: // disable if (gDisableStructs[gActiveBattler].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] -= 0x8; ++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] -= 0x800; if (!(gStatuses3[gActiveBattler] & STATUS3_YAWN) && !(gBattleMons[gActiveBattler].status1 & STATUS1_ANY) && gBattleMons[gActiveBattler].ability != ABILITY_VITAL_SPIRIT && gBattleMons[gActiveBattler].ability != ABILITY_INSOMNIA && !UproarWakeUpCheck(gActiveBattler)) { CancelMultiTurnMoves(gActiveBattler); gBattleMons[gActiveBattler].status1 |= (Random() & 3) + 2; BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); MarkBattlerForControllerExec(gActiveBattler); gEffectBattler = gActiveBattler; BattleScriptExecute(BattleScript_YawnMakesAsleep); ++effect; } } ++gBattleStruct->turnEffectsTracker; break; case ENDTURN_BATTLER_COUNT: // done gBattleStruct->turnEffectsTracker = 0; ++gBattleStruct->turnEffectsBattlerId; break; } if (effect) return effect; } } gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_x20); return 0; } bool8 HandleWishPerishSongOnTurnEnd(void) { gHitMarker |= (HITMARKER_GRUDGE | HITMARKER_x20); switch (gBattleStruct->wishPerishSongState) { case 0: while (gBattleStruct->wishPerishSongBattlerId < gBattlersCount) { gActiveBattler = gBattleStruct->wishPerishSongBattlerId; if (gAbsentBattlerFlags & gBitTable[gActiveBattler]) { ++gBattleStruct->wishPerishSongBattlerId; continue; } ++gBattleStruct->wishPerishSongBattlerId; if (gWishFutureKnock.futureSightCounter[gActiveBattler] != 0 && --gWishFutureKnock.futureSightCounter[gActiveBattler] == 0 && gBattleMons[gActiveBattler].hp != 0) { if (gWishFutureKnock.futureSightMove[gActiveBattler] == MOVE_FUTURE_SIGHT) gBattleCommunication[MULTISTRING_CHOOSER] = 0; else gBattleCommunication[MULTISTRING_CHOOSER] = 1; PREPARE_MOVE_BUFFER(gBattleTextBuff1, gWishFutureKnock.futureSightMove[gActiveBattler]); gBattlerTarget = gActiveBattler; gBattlerAttacker = gWishFutureKnock.futureSightAttacker[gActiveBattler]; gBattleMoveDamage = gWishFutureKnock.futureSightDmg[gActiveBattler]; gSpecialStatuses[gBattlerTarget].dmg = 0xFFFF; BattleScriptExecute(BattleScript_MonTookFutureAttack); return TRUE; } } { 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; } } break; } gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_x20); return FALSE; } #define FAINTED_ACTIONS_MAX_CASE 7 bool8 HandleFaintedMonActions(void) { if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) return FALSE; do { s32 i; switch (gBattleStruct->faintedActionsState) { case 0: gBattleStruct->faintedActionsBattlerId = 0; ++gBattleStruct->faintedActionsState; for (i = 0; i < gBattlersCount; ++i) { if (gAbsentBattlerFlags & gBitTable[i] && !HasNoMonsToSwitch(i, 6, 6)) gAbsentBattlerFlags &= ~(gBitTable[i]); } // fall through case 1: do { gBattlerFainted = gBattlerTarget = gBattleStruct->faintedActionsBattlerId; if (gBattleMons[gBattleStruct->faintedActionsBattlerId].hp == 0 && !(gBattleStruct->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: sub_8017434(gBattlerFainted); if (++gBattleStruct->faintedActionsBattlerId == gBattlersCount) gBattleStruct->faintedActionsState = 3; else gBattleStruct->faintedActionsState = 1; break; case 3: gBattleStruct->faintedActionsBattlerId = 0; ++gBattleStruct->faintedActionsState; // fall through case 4: do { gBattlerFainted = gBattlerTarget = gBattleStruct->faintedActionsBattlerId; if (gBattleMons[gBattleStruct->faintedActionsBattlerId].hp == 0 && !(gAbsentBattlerFlags & gBitTable[gBattleStruct->faintedActionsBattlerId])) { BattleScriptExecute(BattleScript_HandleFaintedMon); gBattleStruct->faintedActionsState = 5; return TRUE; } } while (++gBattleStruct->faintedActionsBattlerId != gBattlersCount); gBattleStruct->faintedActionsState = 6; break; case 5: if (++gBattleStruct->faintedActionsBattlerId == gBattlersCount) gBattleStruct->faintedActionsState = 6; else gBattleStruct->faintedActionsState = 4; break; case 6: if (AbilityBattleEffects(ABILITYEFFECT_INTIMIDATE1, 0, 0, 0, 0) || AbilityBattleEffects(ABILITYEFFECT_TRACE, 0, 0, 0, 0) || ItemBattleEffects(1, 0, 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; }