pokeemmo/src/battle_end_turn.c
2025-03-08 21:24:22 +01:00

1590 lines
52 KiB
C

#include "global.h"
#include "battle.h"
#include "battle_util.h"
#include "battle_controllers.h"
#include "battle_ai_util.h"
#include "battle_gimmick.h"
#include "battle_scripts.h"
#include "constants/battle.h"
#include "constants/battle_string_ids.h"
#include "constants/hold_effects.h"
#include "constants/abilities.h"
#include "constants/items.h"
#include "constants/moves.h"
enum EndTurnResolutionOrder
{
ENDTURN_ORDER,
ENDTURN_VARIOUS,
ENDTURN_WEATHER,
ENDTURN_WEATHER_DAMAGE,
ENDTURN_EMERGENCY_EXIT_1,
ENDTURN_AFFECTION,
ENDTURN_FUTURE_SIGHT,
ENDTURN_WISH,
ENDTURN_FIRST_EVENT_BLOCK,
ENDTURN_EMERGENCY_EXIT_2,
ENDTURN_AQUA_RING,
ENDTURN_INGRAIN,
ENDTURN_LEECH_SEED,
ENDTURN_POISON,
ENDTURN_BURN,
ENDTURN_FROSTBITE,
ENDTURN_NIGHTMARE,
ENDTURN_CURSE,
ENDTURN_WRAP,
ENDTURN_SALT_CURE,
ENDTURN_OCTOLOCK,
ENDTURN_SYRUP_BOMB,
ENDTURN_TAUNT,
ENDTURN_TORMENT,
ENDTURN_ENCORE,
ENDTURN_DISABLE,
ENDTURN_MAGNET_RISE,
ENDTURN_TELEKINESIS,
ENDTURN_HEAL_BLOCK,
ENDTURN_EMBARGO,
ENDTURN_YAWN,
ENDTURN_PERISH_SONG,
ENDTURN_ROOST,
ENDTURN_EMERGENCY_EXIT_3,
ENDTURN_SECOND_EVENT_BLOCK,
ENDTURN_TRICK_ROOM,
ENDTURN_GRAVITY,
ENDTURN_WATER_SPORT,
ENDTURN_MUD_SPORT,
ENDTURN_WONDER_ROOM,
ENDTURN_MAGIC_ROOM,
ENDTURN_TERRAIN,
ENDTURN_THIRD_EVENT_BLOCK,
ENDTURN_EMERGENCY_EXIT_4,
ENDTURN_ABILITIES,
ENDTURN_FOURTH_EVENT_BLOCK,
ENDTURN_DYNAMAX,
ENDTURN_COUNT,
};
enum FirstEventBlock
{
FIRST_EVENT_BLOCK_GMAX_MOVE_RESIDUAL,
FIRST_EVENT_BLOCK_SEA_OF_FIRE_DAMAGE,
FIRST_EVENT_BLOCK_THRASH,
FIRST_EVENT_BLOCK_GRASSY_TERRAIN_HEAL,
FIRST_EVENT_BLOCK_ABILITIES,
FIRST_EVENT_BLOCK_HEAL_ITEMS,
};
enum SecondEventBlock
{
SECOND_EVENT_BLOCK_REFLECT,
SECOND_EVENT_BLOCK_LIGHT_SCREEN,
SECOND_EVENT_BLOCK_SAFEGUARD,
SECOND_EVENT_BLOCK_MIST,
SECOND_EVENT_BLOCK_TAILWIND,
SECOND_EVENT_BLOCK_LUCKY_CHANT,
SECOND_EVENT_BLOCK_RAINBOW,
SECOND_EVENT_BLOCK_SEA_OF_FIRE,
SECOND_EVENT_BLOCK_SWAMP,
SECOND_EVENT_BLOCK_AURORA_VEIL,
};
enum ThirdEventBlock
{
THIRD_EVENT_BLOCK_UPROAR,
THIRD_EVENT_BLOCK_ABILITIES,
THIRD_EVENT_BLOCK_ITEMS,
};
enum FourthEventBlock
{
FOURTH_EVENT_BLOCK_HUNGER_SWITCH,
FOURTH_EVENT_BLOCK_EJECT_PACK,
};
static inline bool32 IsBattlerProtectedByMagicGuard(u32 battler, u32 ability)
{
if (ability != ABILITY_MAGIC_GUARD)
return FALSE;
RecordAbilityBattle(battler, ability);
return TRUE;
}
static bool32 HandleEndTurnOrder(u32 battler)
{
bool32 effect = FALSE;
gBattleTurnCounter++;
gBattleStruct->endTurnEventsCounter++;
u32 i, j;
for (i = 0; i < gBattlersCount; i++)
{
gBattlerByTurnOrder[i] = i;
}
for (i = 0; i < gBattlersCount - 1; i++)
{
for (j = i + 1; j < gBattlersCount; j++)
{
if (GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE) == -1)
SwapTurnOrder(i, j);
}
}
return effect;
}
static bool32 HandleEndTurnVarious(u32 battler)
{
u32 i;
bool32 effect = FALSE;
gBattleStruct->endTurnEventsCounter++;
if (gFieldStatuses & STATUS_FIELD_FAIRY_LOCK && gFieldTimers.fairyLockTimer == gBattleTurnCounter)
gFieldStatuses &= ~STATUS_FIELD_FAIRY_LOCK;
for (i = 0; i < NUM_BATTLE_SIDES; i++)
{
if (gSideStatuses[i] & SIDE_STATUS_DAMAGE_NON_TYPES && gSideTimers[i].damageNonTypesTimer == gBattleTurnCounter)
gSideStatuses[i] &= ~SIDE_STATUS_DAMAGE_NON_TYPES;
}
for (i = 0; i < gBattlersCount; i++)
{
if (gStatuses3[i] & STATUS3_ALWAYS_HITS)
gStatuses3[i] -= STATUS3_ALWAYS_HITS_TURN(1);
if (gDisableStructs[i].chargeTimer && --gDisableStructs[i].chargeTimer == 0)
gStatuses3[i] &= ~STATUS3_CHARGED_UP;
if (gStatuses3[i] & STATUS3_LASER_FOCUS && gDisableStructs[i].laserFocusTimer == gBattleTurnCounter)
gStatuses3[i] &= ~STATUS3_LASER_FOCUS;
gBattleStruct->hpBefore[i] = gBattleMons[i].hp;
}
return effect;
}
static bool32 HandleEndTurnWeather(u32 battler)
{
gBattleStruct->endTurnEventsCounter++;
return EndOrContinueWeather();
}
static bool32 HandleEndTurnWeatherDamage(u32 battler)
{
bool32 effect = FALSE;
u32 ability = GetBattlerAbility(battler);
u32 currBattleWeather = GetCurrentBattleWeather();
if (currBattleWeather == 0xFF)
{
// If there is no weather on the field, no need to check other battlers so go to next state
gBattleStruct->turnEffectsBattlerId = 0;
gBattleStruct->endTurnEventsCounter++;
return effect;
}
gBattleStruct->turnEffectsBattlerId++;
if (!IsBattlerAlive(battler) || !HasWeatherEffect())
return effect;
switch (currBattleWeather)
{
case BATTLE_WEATHER_FOG:
case BATTLE_WEATHER_STRONG_WINDS:
break;
case BATTLE_WEATHER_RAIN:
case BATTLE_WEATHER_RAIN_PRIMAL:
case BATTLE_WEATHER_RAIN_DOWNPOUR:
if (ability == ABILITY_DRY_SKIN || ability == ABILITY_RAIN_DISH)
{
if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE))
effect = TRUE;
}
break;
case BATTLE_WEATHER_SUN:
case BATTLE_WEATHER_SUN_PRIMAL:
if (ability == ABILITY_DRY_SKIN || ability == ABILITY_SOLAR_POWER)
{
if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE))
effect = TRUE;
}
break;
case BATTLE_WEATHER_SANDSTORM:
if (ability != ABILITY_SAND_VEIL
&& ability != ABILITY_SAND_FORCE
&& ability != ABILITY_SAND_RUSH
&& ability != ABILITY_OVERCOAT
&& !IS_BATTLER_ANY_TYPE(gBattlerAttacker, TYPE_ROCK, TYPE_GROUND, TYPE_STEEL)
&& !(gStatuses3[gBattlerAttacker] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER))
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES
&& !IsBattlerProtectedByMagicGuard(battler, ability))
{
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SANDSTORM;
BattleScriptExecute(BattleScript_DamagingWeather);
effect = TRUE;
}
break;
case BATTLE_WEATHER_HAIL:
case BATTLE_WEATHER_SNOW:
if (ability == ABILITY_ICE_BODY)
{
if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE))
effect = TRUE;
}
else if (currBattleWeather == BATTLE_WEATHER_HAIL)
{
if (ability != ABILITY_SNOW_CLOAK
&& ability != ABILITY_OVERCOAT
&& !IS_BATTLER_OF_TYPE(battler, TYPE_ICE)
&& !(gStatuses3[battler] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER))
&& GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES
&& !IsBattlerProtectedByMagicGuard(battler, ability))
{
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_HAIL;
BattleScriptExecute(BattleScript_DamagingWeather);
effect = TRUE;
}
}
break;
}
return effect;
}
static bool32 HandleEndTurnEmergencyExit(u32 battler)
{
bool32 effect = FALSE;
u32 ability = GetBattlerAbility(battler);
gBattleStruct->turnEffectsBattlerId++;
if (ability == ABILITY_EMERGENCY_EXIT || ability == ABILITY_WIMP_OUT)
{
u32 cutoff = gBattleMons[battler].maxHP / 2;
bool32 HadMoreThanHalfHpNowDoesnt = gBattleStruct->hpBefore[battler] > cutoff && gBattleMons[battler].hp <= cutoff;
if (HadMoreThanHalfHpNowDoesnt
&& IsBattlerAlive(battler)
&& (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER))
&& !(gBattleTypeFlags & BATTLE_TYPE_ARENA)
&& CountUsablePartyMons(battler) > 0
&& !(gStatuses3[battler] & STATUS3_SKY_DROPPED)) // Not currently held by Sky Drop
{
gBattlerAbility = battler;
gLastUsedAbility = ability;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || GetBattlerSide(battler) == B_SIDE_PLAYER)
BattleScriptExecute(BattleScript_EmergencyExitEnd2);
else
BattleScriptExecute(BattleScript_EmergencyExitWildEnd2);
effect = TRUE;
}
}
return effect;
}
static bool32 HandleEndTurnAffection(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (!B_AFFECTION_MECHANICS
|| !IsBattlerAlive(battler)
|| GetBattlerSide(battler) != B_SIDE_PLAYER)
return effect;
if (GetBattlerAffectionHearts(gBattlerAttacker) >= AFFECTION_FOUR_HEARTS && (Random() % 100 < 20))
{
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
BattleScriptExecute(BattleScript_AffectionBasedStatusHeal);
effect = TRUE;
}
return effect;
}
// Note: Technically Future Sight, Doom Desire and Wish need a queue but
// I think we should accept this slight inconsistency so custom moves don't have to touch this code
static bool32 HandleEndTurnFutureSight(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gWishFutureKnock.futureSightCounter[battler] == gBattleTurnCounter)
{
struct Pokemon *party;
if (gWishFutureKnock.futureSightCounter[battler] == gBattleTurnCounter
&& gWishFutureKnock.futureSightCounter[BATTLE_PARTNER(battler)] <= gBattleTurnCounter)
{
gSideStatuses[GetBattlerSide(battler)] &= ~SIDE_STATUS_FUTUREATTACK;
}
if (!IsBattlerAlive(battler))
return effect;
if (gWishFutureKnock.futureSightMove[battler] == MOVE_FUTURE_SIGHT)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FUTURE_SIGHT;
else
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOOM_DESIRE;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gWishFutureKnock.futureSightMove[battler]);
gBattlerTarget = battler;
gBattlerAttacker = gWishFutureKnock.futureSightBattlerIndex[battler];
gCurrentMove = gWishFutureKnock.futureSightMove[battler];
party = GetSideParty(GetBattlerSide(gBattlerAttacker));
if (&party[gWishFutureKnock.futureSightPartyIndex[gBattlerTarget]] == &party[gBattlerPartyIndexes[gBattlerAttacker]])
SetTypeBeforeUsingMove(gCurrentMove, gBattlerAttacker);
BattleScriptExecute(BattleScript_MonTookFutureAttack);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnWish(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gWishFutureKnock.wishCounter[battler] == gBattleTurnCounter && IsBattlerAlive(battler))
{
gBattlerTarget = battler;
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, battler, gWishFutureKnock.wishPartyId[battler])
if (B_WISH_HP_SOURCE >= GEN_5)
{
if (GetBattlerSide(battler) == B_SIDE_PLAYER)
gBattleStruct->moveDamage[battler] = max(1, GetMonData(&gPlayerParty[gWishFutureKnock.wishPartyId[battler]], MON_DATA_MAX_HP) / 2);
else
gBattleStruct->moveDamage[battler] = max(1, GetMonData(&gEnemyParty[gWishFutureKnock.wishPartyId[battler]], MON_DATA_MAX_HP) / 2);
}
else
{
gBattleStruct->moveDamage[battler] = max(1, GetNonDynamaxMaxHP(battler) / 2);
}
gBattleStruct->moveDamage[battler] *= -1;
if (gBattleMons[battler].hp == gBattleMons[battler].maxHP)
BattleScriptExecute(BattleScript_WishButFullHp);
else
BattleScriptExecute(BattleScript_WishComesTrue);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnFirstEventBlock(u32 battler)
{
bool32 effect = FALSE;
u32 side;
switch (gBattleStruct->eventBlockCounter)
{
case FIRST_EVENT_BLOCK_GMAX_MOVE_RESIDUAL: // TODO: Has to be split into 3 statuses and needs a queue
side = GetBattlerSide(battler);
if (gSideStatuses[side] & SIDE_STATUS_DAMAGE_NON_TYPES)
{
if (IsBattlerAlive(battler)
&& !IS_BATTLER_OF_TYPE(battler, gSideTimers[side].damageNonTypesType)
&& !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler)))
{
gBattlerAttacker = battler;
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 6;
ChooseDamageNonTypesString(gSideTimers[side].damageNonTypesType);
BattleScriptExecute(BattleScript_DamageNonTypesContinues);
effect = TRUE;
}
}
gBattleStruct->eventBlockCounter++;
break;
case FIRST_EVENT_BLOCK_SEA_OF_FIRE_DAMAGE:
if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SEA_OF_FIRE && IsBattlerAlive(battler))
{
gBattlerAttacker = battler;
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8;
BtlController_EmitStatusAnimation(battler, BUFFER_A, FALSE, STATUS1_BURN);
MarkBattlerForControllerExec(battler);
BattleScriptExecute(BattleScript_HurtByTheSeaOfFire);
effect = TRUE;
}
gBattleStruct->eventBlockCounter++;
break;
case FIRST_EVENT_BLOCK_THRASH: // TODO: Move to moveend / not sure. Might be best to keep here
if (gBattleMons[battler].status2 & STATUS2_LOCK_CONFUSE && !(gStatuses3[battler] & STATUS3_SKY_DROPPED))
{
gBattleMons[battler].status2 -= STATUS2_LOCK_CONFUSE_TURN(1);
if (WasUnableToUseMove(battler))
{
CancelMultiTurnMoves(battler);
}
else if (!(gBattleMons[battler].status2 & STATUS2_LOCK_CONFUSE) && (gBattleMons[battler].status2 & STATUS2_MULTIPLETURNS))
{
gBattleMons[battler].status2 &= ~STATUS2_MULTIPLETURNS;
if (!(gBattleMons[battler].status2 & STATUS2_CONFUSION))
{
gBattleScripting.moveEffect = MOVE_EFFECT_CONFUSION | MOVE_EFFECT_AFFECTS_USER;
SetMoveEffect(TRUE, FALSE);
if (gBattleMons[battler].status2 & STATUS2_CONFUSION)
BattleScriptExecute(BattleScript_ThrashConfuses);
effect = TRUE;
}
}
}
gBattleStruct->eventBlockCounter++;
break;
case FIRST_EVENT_BLOCK_GRASSY_TERRAIN_HEAL:
if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerAlive(battler) && !IsBattlerAtMaxHp(battler))
{
gBattlerAttacker = battler;
gBattleStruct->moveDamage[battler] = -(GetNonDynamaxMaxHP(battler) / 16);
BattleScriptExecute(BattleScript_GrassyTerrainHeals);
effect = TRUE;
}
gBattleStruct->eventBlockCounter++;
break;
case FIRST_EVENT_BLOCK_ABILITIES:
{
u32 ability = GetBattlerAbility(battler);
switch (ability)
{
case ABILITY_HEALER:
case ABILITY_HYDRATION:
case ABILITY_SHED_SKIN:
if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE))
effect = TRUE;
break;
}
gBattleStruct->eventBlockCounter++;
break;
}
case FIRST_EVENT_BLOCK_HEAL_ITEMS:
{
u32 holdEffect = GetBattlerHoldEffect(battler, TRUE);
switch (holdEffect)
{
case HOLD_EFFECT_LEFTOVERS:
case HOLD_EFFECT_BLACK_SLUDGE:
if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler, FALSE))
effect = TRUE;
break;
}
gBattleStruct->eventBlockCounter = 0;
gBattleStruct->turnEffectsBattlerId++;
break;
}
}
return effect;
}
static bool32 HandleEndTurnAquaRing(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gStatuses3[battler] & STATUS3_AQUA_RING
&& !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)
&& !IsBattlerAtMaxHp(battler)
&& IsBattlerAlive(battler))
{
gBattleStruct->moveDamage[battler] = GetDrainedBigRootHp(battler, GetNonDynamaxMaxHP(battler) / 16);
BattleScriptExecute(BattleScript_AquaRingHeal);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnIngrain(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gStatuses3[battler] & STATUS3_ROOTED
&& !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)
&& !IsBattlerAtMaxHp(battler)
&& IsBattlerAlive(battler))
{
gBattleStruct->moveDamage[battler] = GetDrainedBigRootHp(battler, GetNonDynamaxMaxHP(battler) / 16);
BattleScriptExecute(BattleScript_IngrainTurnHeal);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnLeechSeed(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gStatuses3[battler] & STATUS3_LEECHSEED
&& IsBattlerAlive(gStatuses3[battler] & STATUS3_LEECHSEED_BATTLER)
&& IsBattlerAlive(battler)
&& !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler)))
{
gBattlerTarget = gStatuses3[battler] & STATUS3_LEECHSEED_BATTLER; // Notice gBattlerTarget is actually the HP receiver.
gBattleScripting.animArg1 = gBattlerTarget;
gBattleScripting.animArg2 = gBattlerAttacker;
gBattleStruct->moveDamage[gBattlerAttacker] = max(1, GetNonDynamaxMaxHP(battler) / 8);
gBattleStruct->moveDamage[gBattlerTarget] = GetDrainedBigRootHp(gBattlerAttacker, gBattleStruct->moveDamage[gBattlerAttacker]);
gHitMarker |= HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE;
if (GetBattlerAbility(battler) == ABILITY_LIQUID_OOZE)
{
gBattleStruct->moveDamage[gBattlerTarget] = gBattleStruct->moveDamage[gBattlerTarget] * -1;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LEECH_SEED_OOZE;
BattleScriptExecute(BattleScript_LeechSeedTurnDrainLiquidOoze);
}
else if (gStatuses3[gBattlerTarget] & STATUS3_HEAL_BLOCK)
{
BattleScriptExecute(BattleScript_LeechSeedTurnDrainHealBlock);
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LEECH_SEED_DRAIN;
BattleScriptExecute(BattleScript_LeechSeedTurnDrainRecovery);
}
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnPoison(u32 battler)
{
bool32 effect = FALSE;
u32 ability = GetBattlerAbility(battler);
gBattleStruct->turnEffectsBattlerId++;
if ((gBattleMons[battler].status1 & STATUS1_POISON || gBattleMons[battler].status1 & STATUS1_TOXIC_POISON)
&& IsBattlerAlive(battler)
&& !IsBattlerProtectedByMagicGuard(battler, ability))
{
if (ability == ABILITY_POISON_HEAL)
{
if (!IsBattlerAtMaxHp(battler) && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK))
{
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
gBattleStruct->moveDamage[battler] *= -1;
BattleScriptExecute(BattleScript_PoisonHealActivates);
effect = TRUE;
}
}
else if (gBattleMons[battler].status1 & STATUS1_TOXIC_POISON)
{
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 16;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
if ((gBattleMons[battler].status1 & STATUS1_TOXIC_COUNTER) != STATUS1_TOXIC_TURN(15)) // not 16 turns
gBattleMons[battler].status1 += STATUS1_TOXIC_TURN(1);
gBattleStruct->moveDamage[battler] *= (gBattleMons[battler].status1 & STATUS1_TOXIC_COUNTER) >> 8;
BattleScriptExecute(BattleScript_PoisonTurnDmg);
effect = TRUE;
}
else
{
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 8;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
BattleScriptExecute(BattleScript_PoisonTurnDmg);
effect = TRUE;
}
}
return effect;
}
static bool32 HandleEndTurnBurn(u32 battler)
{
bool32 effect = FALSE;
u32 ability = GetBattlerAbility(battler);
gBattleStruct->turnEffectsBattlerId++;
if (gBattleMons[battler].status1 & STATUS1_BURN
&& IsBattlerAlive(battler)
&& !IsBattlerProtectedByMagicGuard(battler, ability))
{
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / (B_BURN_DAMAGE >= GEN_7 ? 16 : 8);
if (ability == ABILITY_HEATPROOF)
{
if (gBattleStruct->moveDamage[battler] > (gBattleStruct->moveDamage[battler] / 2) + 1) // Record ability if the burn takes less damage than it normally would.
RecordAbilityBattle(battler, ABILITY_HEATPROOF);
gBattleStruct->moveDamage[battler] /= 2;
}
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
BattleScriptExecute(BattleScript_BurnTurnDmg);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnFrostbite(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gBattleMons[battler].status1 & STATUS1_FROSTBITE
&& IsBattlerAlive(battler)
&& !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler)))
{
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / (B_BURN_DAMAGE >= GEN_7 ? 16 : 8);
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
BattleScriptExecute(BattleScript_FrostbiteTurnDmg);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnNightmare(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gBattleMons[battler].status2 & STATUS2_NIGHTMARE
&& IsBattlerAlive(battler)
&& !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler)))
{
if (gBattleMons[battler].status1 & STATUS1_SLEEP)
{
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 4;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
BattleScriptExecute(BattleScript_NightmareTurnDmg);
effect = TRUE;
}
else
{
gBattleMons[battler].status2 &= ~STATUS2_NIGHTMARE;
}
}
return effect;
}
static bool32 HandleEndTurnCurse(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gBattleMons[battler].status2 & STATUS2_CURSED
&& IsBattlerAlive(battler)
&& !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler)))
{
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 4;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
BattleScriptExecute(BattleScript_CurseTurnDmg);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnWrap(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gBattleMons[battler].status2 & STATUS2_WRAPPED && IsBattlerAlive(battler))
{
if (--gDisableStructs[battler].wrapTurns != 0 && !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler)))
{
gBattleScripting.animArg1 = gBattleStruct->wrappedMove[battler];
gBattleScripting.animArg2 = gBattleStruct->wrappedMove[battler] >> 8;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleStruct->wrappedMove[battler]);
BattleScriptExecute(BattleScript_WrapTurnDmg);
if (GetBattlerHoldEffect(gBattleStruct->wrappedBy[battler], TRUE) == HOLD_EFFECT_BINDING_BAND)
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / (B_BINDING_DAMAGE >= GEN_6 ? 6 : 8);
else
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / (B_BINDING_DAMAGE >= GEN_6 ? 8 : 16);
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
}
else // broke free
{
gBattleMons[battler].status2 &= ~STATUS2_WRAPPED;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleStruct->wrappedMove[battler]);
BattleScriptExecute(BattleScript_WrapEnds);
}
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnSaltCure(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gStatuses4[battler] & STATUS4_SALT_CURE
&& IsBattlerAlive(battler)
&& !IsBattlerProtectedByMagicGuard(battler, GetBattlerAbility(battler)))
{
if (IS_BATTLER_ANY_TYPE(battler, TYPE_STEEL, TYPE_WATER))
gBattleStruct->moveDamage[battler] = gBattleMons[battler].maxHP / 4;
else
gBattleStruct->moveDamage[battler] = gBattleMons[battler].maxHP / 8;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SALT_CURE);
BattleScriptExecute(BattleScript_SaltCureExtraDamage);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnOctolock(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gDisableStructs[battler].octolock)
{
gBattlerTarget = battler;
gBattlerAttacker = gDisableStructs[battler].battlerPreventingEscape;
BattleScriptExecute(BattleScript_OctolockEndTurn);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnSyrupBomb(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if ((gStatuses4[battler] & STATUS4_SYRUP_BOMB) && (IsBattlerAlive(battler)))
{
if (gDisableStructs[battler].syrupBombTimer > 0 && --gDisableStructs[battler].syrupBombTimer == 0)
gStatuses4[battler] &= ~STATUS4_SYRUP_BOMB;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SYRUP_BOMB);
gBattlescriptCurrInstr = BattleScript_SyrupBombEndTurn;
BattleScriptExecute(gBattlescriptCurrInstr);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnTaunt(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gDisableStructs[battler].tauntTimer && --gDisableStructs[battler].tauntTimer == 0)
{
BattleScriptExecute(BattleScript_BufferEndTurn);
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_TAUNT);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnTorment(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gDisableStructs[battler].tormentTimer > 0 && --gDisableStructs[battler].tormentTimer == 0)
{
gBattleMons[battler].status2 &= ~STATUS2_TORMENT;
BattleScriptExecute(BattleScript_TormentEnds);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnEncore(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gDisableStructs[battler].encoreTimer != 0)
{
if (gBattleMons[battler].moves[gDisableStructs[battler].encoredMovePos] != gDisableStructs[battler].encoredMove) // Pokémon does not have the encored move anymore
{
gDisableStructs[battler].encoredMove = 0;
gDisableStructs[battler].encoreTimer = 0;
}
else if (--gDisableStructs[battler].encoreTimer == 0
|| gBattleMons[battler].pp[gDisableStructs[battler].encoredMovePos] == 0)
{
gDisableStructs[battler].encoredMove = 0;
gDisableStructs[battler].encoreTimer = 0;
BattleScriptExecute(BattleScript_EncoredNoMore);
effect = TRUE;
}
}
return effect;
}
static bool32 HandleEndTurnDisable(u32 battler)
{
bool32 effect = FALSE;
u32 moveIndex = 0;
gBattleStruct->turnEffectsBattlerId++;
if (gDisableStructs[battler].disableTimer != 0)
{
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (gDisableStructs[battler].disabledMove == gBattleMons[battler].moves[moveIndex])
break;
}
if (moveIndex == MAX_MON_MOVES) // Pokémon does not have the disabled move anymore
{
gDisableStructs[battler].disabledMove = 0;
gDisableStructs[battler].disableTimer = 0;
}
else if (--gDisableStructs[battler].disableTimer == 0) // disable ends
{
gDisableStructs[battler].disabledMove = 0;
BattleScriptExecute(BattleScript_DisabledNoMore);
effect = TRUE;
}
}
return effect;
}
static bool32 HandleEndTurnMagnetRise(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gStatuses3[battler] & STATUS3_MAGNET_RISE && gDisableStructs[battler].magnetRiseTimer == gBattleTurnCounter)
{
gStatuses3[battler] &= ~STATUS3_MAGNET_RISE;
BattleScriptExecute(BattleScript_BufferEndTurn);
PREPARE_STRING_BUFFER(gBattleTextBuff1, STRINGID_ELECTROMAGNETISM);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnTelekinesis(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gStatuses3[battler] & STATUS3_TELEKINESIS && gDisableStructs[battler].telekinesisTimer == gBattleTurnCounter)
{
gStatuses3[battler] &= ~STATUS3_TELEKINESIS;
BattleScriptExecute(BattleScript_TelekinesisEndTurn);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnHealBlock(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gStatuses3[battler] & STATUS3_HEAL_BLOCK && gDisableStructs[battler].healBlockTimer == gBattleTurnCounter)
{
gStatuses3[battler] &= ~STATUS3_HEAL_BLOCK;
BattleScriptExecute(BattleScript_BufferEndTurn);
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_HEAL_BLOCK);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnEmbargo(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gStatuses3[battler] & STATUS3_EMBARGO && gDisableStructs[battler].embargoTimer == gBattleTurnCounter)
{
gStatuses3[battler] &= ~STATUS3_EMBARGO;
BattleScriptExecute(BattleScript_EmbargoEndTurn);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnYawn(u32 battler)
{
bool32 effect = FALSE;
u32 ability = GetBattlerAbility(battler);
gBattleStruct->turnEffectsBattlerId++;
if (gStatuses3[battler] & STATUS3_YAWN)
{
gStatuses3[battler] -= STATUS3_YAWN_TURN(1);
if (!(gStatuses3[battler] & STATUS3_YAWN) && !(gBattleMons[battler].status1 & STATUS1_ANY)
&& ability != ABILITY_VITAL_SPIRIT
&& ability != ABILITY_INSOMNIA
&& !UproarWakeUpCheck(battler)
&& !IsLeafGuardProtected(battler, ability))
{
CancelMultiTurnMoves(battler);
gEffectBattler = gBattlerTarget = battler;
if (IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINPREVENTS_ELECTRIC;
BattleScriptExecute(BattleScript_TerrainPreventsEnd2);
}
else if (IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAINPREVENTS_MISTY;
BattleScriptExecute(BattleScript_TerrainPreventsEnd2);
}
else if (IsSleepClauseActiveForSide(GetBattlerSide(battler)))
{
BattleScriptExecute(BattleScript_SleepClausePreventsEnd);
}
else
{
if (B_SLEEP_TURNS >= GEN_5)
gBattleMons[battler].status1 |= ((Random() % 3) + 2);
else
gBattleMons[battler].status1 |= ((Random() % 4) + 3);
TryActivateSleepClause(battler, gBattlerPartyIndexes[battler]);
BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1);
MarkBattlerForControllerExec(battler);
BattleScriptExecute(BattleScript_YawnMakesAsleep);
}
effect = TRUE;
}
}
return effect;
}
static bool32 HandleEndTurnPerishSong(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (IsBattlerAlive(battler) && gStatuses3[battler] & STATUS3_PERISH_SONG)
{
PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[battler].perishSongTimer);
if (gDisableStructs[battler].perishSongTimer == 0)
{
gStatuses3[battler] &= ~STATUS3_PERISH_SONG;
gBattleStruct->moveDamage[battler] = gBattleMons[battler].hp;
BattleScriptExecute(BattleScript_PerishSongTakesLife);
}
else
{
gDisableStructs[battler].perishSongTimer--;
BattleScriptExecute(BattleScript_PerishSongCountGoesDown);
}
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnRoost(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (gDisableStructs[battler].roostActive)
gDisableStructs[battler].roostActive = FALSE;
return effect;
}
static bool32 HandleEndTurnSecondEventBlock(u32 battler)
{
bool32 effect = FALSE;
u32 side = gBattleStruct->turnSideTracker;
switch (gBattleStruct->eventBlockCounter)
{
case SECOND_EVENT_BLOCK_REFLECT:
if (gSideStatuses[side] & SIDE_STATUS_REFLECT && gSideTimers[side].reflectTimer == gBattleTurnCounter)
{
gBattlerAttacker = gSideTimers[side].reflectBattlerId;
gSideStatuses[side] &= ~SIDE_STATUS_REFLECT;
BattleScriptExecute(BattleScript_SideStatusWoreOff);
gBattleCommunication[MULTISTRING_CHOOSER] = side;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_REFLECT);
effect = TRUE;
}
gBattleStruct->eventBlockCounter++;
break;
case SECOND_EVENT_BLOCK_LIGHT_SCREEN:
if (gSideStatuses[side] & SIDE_STATUS_LIGHTSCREEN && gSideTimers[side].lightscreenTimer == gBattleTurnCounter)
{
gBattlerAttacker = gSideTimers[side].lightscreenBattlerId;
gSideStatuses[side] &= ~SIDE_STATUS_LIGHTSCREEN;
BattleScriptExecute(BattleScript_SideStatusWoreOff);
gBattleCommunication[MULTISTRING_CHOOSER] = side;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_LIGHT_SCREEN);
effect = TRUE;
}
gBattleStruct->eventBlockCounter++;
break;
case SECOND_EVENT_BLOCK_SAFEGUARD:
if (gSideStatuses[side] & SIDE_STATUS_SAFEGUARD && gSideTimers[side].safeguardTimer == gBattleTurnCounter)
{
gBattlerAttacker = gSideTimers[side].safeguardBattlerId;
gSideStatuses[side] &= ~SIDE_STATUS_SAFEGUARD;
BattleScriptExecute(BattleScript_SafeguardEnds);
effect = TRUE;
}
gBattleStruct->eventBlockCounter++;
break;
case SECOND_EVENT_BLOCK_MIST:
if (gSideTimers[side].mistTimer != 0 && gSideTimers[side].mistTimer == gBattleTurnCounter)
{
gBattlerAttacker = gSideTimers[side].mistBattlerId;
gSideStatuses[side] &= ~SIDE_STATUS_MIST;
BattleScriptExecute(BattleScript_SideStatusWoreOff);
gBattleCommunication[MULTISTRING_CHOOSER] = side;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_MIST);
effect = TRUE;
}
gBattleStruct->eventBlockCounter++;
break;
case SECOND_EVENT_BLOCK_TAILWIND:
if (gSideStatuses[side] & SIDE_STATUS_TAILWIND && gSideTimers[side].tailwindTimer == gBattleTurnCounter)
{
gBattlerAttacker = gSideTimers[side].tailwindBattlerId;
gSideStatuses[side] &= ~SIDE_STATUS_TAILWIND;
BattleScriptExecute(BattleScript_TailwindEnds);
effect = TRUE;
}
gBattleStruct->eventBlockCounter++;
break;
case SECOND_EVENT_BLOCK_LUCKY_CHANT:
if (gSideStatuses[side] & SIDE_STATUS_LUCKY_CHANT && gSideTimers[side].luckyChantTimer == gBattleTurnCounter)
{
gBattlerAttacker = gSideTimers[side].luckyChantBattlerId;
gSideStatuses[side] &= ~SIDE_STATUS_LUCKY_CHANT;
BattleScriptExecute(BattleScript_LuckyChantEnds);
effect = TRUE;
}
gBattleStruct->eventBlockCounter++;
break;
case SECOND_EVENT_BLOCK_RAINBOW:
if (gSideStatuses[side] & SIDE_STATUS_RAINBOW)
{
for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++)
{
if (GetBattlerSide(gBattlerAttacker) == side)
break;
}
if (gSideTimers[side].rainbowTimer == gBattleTurnCounter)
{
gSideStatuses[side] &= ~SIDE_STATUS_RAINBOW;
BattleScriptExecute(BattleScript_TheRainbowDisappeared);
effect = TRUE;
}
}
gBattleStruct->eventBlockCounter++;
break;
case SECOND_EVENT_BLOCK_SEA_OF_FIRE:
if (gSideStatuses[side] & SIDE_STATUS_SEA_OF_FIRE)
{
for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++)
{
if (GetBattlerSide(gBattlerAttacker) == side)
break;
}
if (gSideTimers[side].seaOfFireTimer == gBattleTurnCounter)
{
gSideStatuses[side] &= ~SIDE_STATUS_SEA_OF_FIRE;
BattleScriptExecute(BattleScript_TheSeaOfFireDisappeared);
effect = TRUE;
}
}
gBattleStruct->eventBlockCounter++;
break;
case SECOND_EVENT_BLOCK_SWAMP:
if (gSideStatuses[side] & SIDE_STATUS_SWAMP)
{
for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++)
{
if (GetBattlerSide(gBattlerAttacker) == side)
break;
}
if (gSideTimers[side].swampTimer == gBattleTurnCounter)
{
gSideStatuses[side] &= ~SIDE_STATUS_SWAMP;
BattleScriptExecute(BattleScript_TheSwampDisappeared);
effect = TRUE;
}
}
gBattleStruct->eventBlockCounter++;
break;
case SECOND_EVENT_BLOCK_AURORA_VEIL:
if (gSideStatuses[side] & SIDE_STATUS_AURORA_VEIL && gSideTimers[side].auroraVeilTimer == gBattleTurnCounter)
{
gBattlerAttacker = gSideTimers[side].auroraVeilBattlerId;
gSideStatuses[side] &= ~SIDE_STATUS_AURORA_VEIL;
BattleScriptExecute(BattleScript_SideStatusWoreOff);
gBattleCommunication[MULTISTRING_CHOOSER] = side;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_AURORA_VEIL);
effect = TRUE;
}
gBattleStruct->turnSideTracker++;
gBattleStruct->eventBlockCounter = 0;
break;
}
return effect;
}
static bool32 HandleEndTurnTrickRoom(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->endTurnEventsCounter++;
if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer == gBattleTurnCounter)
{
gFieldStatuses &= ~STATUS_FIELD_TRICK_ROOM;
BattleScriptExecute(BattleScript_TrickRoomEnds);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnGravity(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->endTurnEventsCounter++;
if (gFieldStatuses & STATUS_FIELD_GRAVITY && gFieldTimers.gravityTimer == gBattleTurnCounter)
{
gFieldStatuses &= ~STATUS_FIELD_GRAVITY;
BattleScriptExecute(BattleScript_GravityEnds);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnWaterSport(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->endTurnEventsCounter++;
if (gFieldStatuses & STATUS_FIELD_WATERSPORT && gFieldTimers.waterSportTimer == gBattleTurnCounter)
{
gFieldStatuses &= ~STATUS_FIELD_WATERSPORT;
BattleScriptExecute(BattleScript_WaterSportEnds);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnMudSport(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->endTurnEventsCounter++;
if (gFieldStatuses & STATUS_FIELD_MUDSPORT && gFieldTimers.mudSportTimer == gBattleTurnCounter)
{
gFieldStatuses &= ~STATUS_FIELD_MUDSPORT;
BattleScriptExecute(BattleScript_MudSportEnds);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnWonderRoom(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->endTurnEventsCounter++;
if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM && gFieldTimers.wonderRoomTimer == gBattleTurnCounter)
{
gFieldStatuses &= ~STATUS_FIELD_WONDER_ROOM;
BattleScriptExecute(BattleScript_WonderRoomEnds);
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnMagicRoom(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->endTurnEventsCounter++;
if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM && gFieldTimers.magicRoomTimer == gBattleTurnCounter)
{
gFieldStatuses &= ~STATUS_FIELD_MAGIC_ROOM;
BattleScriptExecute(BattleScript_MagicRoomEnds);
effect = TRUE;
}
return effect;
}
static bool32 EndTurnTerrain(u32 terrainFlag, u32 stringTableId)
{
if (gFieldStatuses & terrainFlag && gFieldTimers.terrainTimer == gBattleTurnCounter)
{
gFieldStatuses &= ~terrainFlag;
TryToRevertMimicryAndFlags();
gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId;
BattleScriptExecute(BattleScript_TerrainEnds);
return TRUE;
}
return FALSE;
}
static bool32 HandleEndTurnTerrain(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->endTurnEventsCounter++;
if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)
effect = EndTurnTerrain(STATUS_FIELD_ELECTRIC_TERRAIN, B_MSG_TERRAIN_END_ELECTRIC);
else if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)
effect = EndTurnTerrain(STATUS_FIELD_MISTY_TERRAIN, B_MSG_TERRAIN_END_MISTY);
else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)
effect = EndTurnTerrain(STATUS_FIELD_GRASSY_TERRAIN, B_MSG_TERRAIN_END_GRASSY);
else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)
effect = EndTurnTerrain(STATUS_FIELD_PSYCHIC_TERRAIN, B_MSG_TERRAIN_END_PSYCHIC);
return effect;
}
static bool32 HandleEndTurnThirdEventBlock(u32 battler)
{
bool32 effect = FALSE;
if (!IsBattlerAlive(battler))
{
gBattleStruct->turnEffectsBattlerId++;
return effect;
}
switch (gBattleStruct->eventBlockCounter)
{
case THIRD_EVENT_BLOCK_UPROAR:
if (gBattleMons[battler].status2 & STATUS2_UPROAR)
{
for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++)
{
if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP)
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_SOUNDPROOF)
{
gBattleMons[gBattlerAttacker].status1 &= ~STATUS1_SLEEP;
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_NIGHTMARE;
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
BattleScriptExecute(BattleScript_MonWokeUpInUproar);
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gBattlerAttacker].status1);
MarkBattlerForControllerExec(gBattlerAttacker);
break;
}
}
if (gBattlerAttacker != gBattlersCount)
{
break;
}
else
{
gBattlerAttacker = battler;
gBattleMons[battler].status2 -= STATUS2_UPROAR_TURN(1); // uproar timer goes down
if (WasUnableToUseMove(battler))
{
CancelMultiTurnMoves(battler);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS;
}
else if (gBattleMons[battler].status2 & STATUS2_UPROAR)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_CONTINUES;
gBattleMons[battler].status2 |= STATUS2_MULTIPLETURNS;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_UPROAR_ENDS;
CancelMultiTurnMoves(battler);
}
BattleScriptExecute(BattleScript_PrintUproarOverTurns);
effect = TRUE;
}
}
gBattleStruct->eventBlockCounter++;
break;
case THIRD_EVENT_BLOCK_ABILITIES:
{
u32 ability = GetBattlerAbility(battler);
switch (ability)
{
case ABILITY_TRUANT: // Not fully accurate but it has to be handled somehow. TODO: Find a better way.
case ABILITY_CUD_CHEW:
case ABILITY_SLOW_START:
case ABILITY_BAD_DREAMS:
case ABILITY_BALL_FETCH:
case ABILITY_HARVEST:
case ABILITY_MOODY:
case ABILITY_PICKUP:
case ABILITY_SPEED_BOOST:
if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE))
effect = TRUE;
break;
}
gBattleStruct->eventBlockCounter++;
break;
}
case THIRD_EVENT_BLOCK_ITEMS:
{
u32 holdEffect = GetBattlerHoldEffect(battler, TRUE);
switch (holdEffect)
{
case HOLD_EFFECT_FLAME_ORB:
case HOLD_EFFECT_STICKY_BARB:
case HOLD_EFFECT_TOXIC_ORB:
if (ItemBattleEffects(ITEMEFFECT_ORBS, battler, FALSE))
effect = TRUE;
break;
case HOLD_EFFECT_RESTORE_STATS:
if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler, FALSE))
effect = TRUE;
break;
}
gBattleStruct->eventBlockCounter = 0;
gBattleStruct->turnEffectsBattlerId++;
break;
}
}
return effect;
}
static bool32 HandleEndTurnAbilities(u32 battler)
{
bool32 effect = FALSE;
u32 ability = GetBattlerAbility(battler);
gBattleStruct->turnEffectsBattlerId++;
switch (ability)
{
case ABILITY_POWER_CONSTRUCT:
case ABILITY_SCHOOLING:
case ABILITY_SHIELDS_DOWN:
case ABILITY_ZEN_MODE:
if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE))
effect = TRUE;
}
return effect;
}
static bool32 HandleEndTurnFourthEventBlock(u32 battler)
{
bool32 effect = FALSE;
switch (gBattleStruct->eventBlockCounter)
{
case FOURTH_EVENT_BLOCK_HUNGER_SWITCH:
{
u32 ability = GetBattlerAbility(battler);
if (ability == ABILITY_HUNGER_SWITCH)
{
if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, battler, ability, 0, MOVE_NONE))
effect = TRUE;
}
gBattleStruct->eventBlockCounter++;
break;
}
case FOURTH_EVENT_BLOCK_EJECT_PACK:
{
u32 holdEffect = GetBattlerHoldEffect(battler, TRUE);
if (holdEffect == HOLD_EFFECT_EJECT_PACK)
{
if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler, FALSE))
effect = TRUE;
}
gBattleStruct->eventBlockCounter = 0;
gBattleStruct->turnEffectsBattlerId++;
break;
}
}
return effect;
}
static bool32 HandleEndTurnDynamax(u32 battler)
{
bool32 effect = FALSE;
gBattleStruct->turnEffectsBattlerId++;
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && gBattleStruct->dynamax.dynamaxTurns[battler] == gBattleTurnCounter)
{
UndoDynamax(battler);
BattleScriptExecute(BattleScript_DynamaxEnds);
effect = TRUE;
}
return effect;
}
static bool32 (*const sEndTurnEffectHandlers[])(u32 battler) =
{
[ENDTURN_ORDER] = HandleEndTurnOrder,
[ENDTURN_VARIOUS] = HandleEndTurnVarious,
[ENDTURN_WEATHER] = HandleEndTurnWeather,
[ENDTURN_WEATHER_DAMAGE] = HandleEndTurnWeatherDamage,
[ENDTURN_EMERGENCY_EXIT_1] = HandleEndTurnEmergencyExit,
[ENDTURN_AFFECTION] = HandleEndTurnAffection,
[ENDTURN_FUTURE_SIGHT] = HandleEndTurnFutureSight,
[ENDTURN_WISH] = HandleEndTurnWish,
[ENDTURN_FIRST_EVENT_BLOCK] = HandleEndTurnFirstEventBlock,
[ENDTURN_EMERGENCY_EXIT_2] = HandleEndTurnEmergencyExit,
[ENDTURN_AQUA_RING] = HandleEndTurnAquaRing,
[ENDTURN_INGRAIN] = HandleEndTurnIngrain,
[ENDTURN_LEECH_SEED] = HandleEndTurnLeechSeed,
[ENDTURN_POISON] = HandleEndTurnPoison,
[ENDTURN_BURN] = HandleEndTurnBurn,
[ENDTURN_FROSTBITE] = HandleEndTurnFrostbite,
[ENDTURN_NIGHTMARE] = HandleEndTurnNightmare,
[ENDTURN_CURSE] = HandleEndTurnCurse,
[ENDTURN_WRAP] = HandleEndTurnWrap,
[ENDTURN_SALT_CURE] = HandleEndTurnSaltCure,
[ENDTURN_OCTOLOCK] = HandleEndTurnOctolock,
[ENDTURN_SYRUP_BOMB] = HandleEndTurnSyrupBomb,
[ENDTURN_TAUNT] = HandleEndTurnTaunt,
[ENDTURN_TORMENT] = HandleEndTurnTorment,
[ENDTURN_ENCORE] = HandleEndTurnEncore,
[ENDTURN_DISABLE] = HandleEndTurnDisable,
[ENDTURN_MAGNET_RISE] = HandleEndTurnMagnetRise,
[ENDTURN_TELEKINESIS] = HandleEndTurnTelekinesis,
[ENDTURN_HEAL_BLOCK] = HandleEndTurnHealBlock,
[ENDTURN_EMBARGO] = HandleEndTurnEmbargo,
[ENDTURN_YAWN] = HandleEndTurnYawn,
[ENDTURN_PERISH_SONG] = HandleEndTurnPerishSong,
[ENDTURN_ROOST] = HandleEndTurnRoost,
[ENDTURN_EMERGENCY_EXIT_3] = HandleEndTurnEmergencyExit,
[ENDTURN_SECOND_EVENT_BLOCK] = HandleEndTurnSecondEventBlock,
[ENDTURN_TRICK_ROOM] = HandleEndTurnTrickRoom,
[ENDTURN_GRAVITY] = HandleEndTurnGravity,
[ENDTURN_WATER_SPORT] = HandleEndTurnWaterSport,
[ENDTURN_MUD_SPORT] = HandleEndTurnMudSport,
[ENDTURN_WONDER_ROOM] = HandleEndTurnWonderRoom,
[ENDTURN_MAGIC_ROOM] = HandleEndTurnMagicRoom,
[ENDTURN_TERRAIN] = HandleEndTurnTerrain,
[ENDTURN_THIRD_EVENT_BLOCK] = HandleEndTurnThirdEventBlock,
[ENDTURN_EMERGENCY_EXIT_4] = HandleEndTurnEmergencyExit,
[ENDTURN_ABILITIES] = HandleEndTurnAbilities,
[ENDTURN_FOURTH_EVENT_BLOCK] = HandleEndTurnFourthEventBlock,
[ENDTURN_DYNAMAX] = HandleEndTurnDynamax,
};
u32 DoEndTurnEffects(void)
{
u32 battler = MAX_BATTLERS_COUNT;
gHitMarker |= (HITMARKER_GRUDGE | HITMARKER_IGNORE_BIDE);
for (;;)
{
// If either turnEffectsBattlerId or turnSideTracker are at max count, reest values and go to the next state
if (gBattleStruct->turnEffectsBattlerId == gBattlersCount || gBattleStruct->turnSideTracker == NUM_BATTLE_SIDES)
{
gBattleStruct->turnEffectsBattlerId = 0;
gBattleStruct->turnSideTracker = 0;
gBattleStruct->eventBlockCounter = 0;
gBattleStruct->endTurnEventsCounter++;
}
// Jump out if possible after endTurnEventsCounter was increased in the above code block
if (gBattleStruct->endTurnEventsCounter == ENDTURN_COUNT)
{
gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_IGNORE_BIDE);
return FALSE;
}
battler = gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->turnEffectsBattlerId];
if (sEndTurnEffectHandlers[gBattleStruct->endTurnEventsCounter](battler))
return TRUE;
}
}