diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 386d636e77..10c81993e8 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6568,6 +6568,24 @@ static void Cmd_moveend(void) effect = TRUE; gBattleScripting.moveendState++; break; + case MOVEEND_SYMBIOSIS: + for (i = 0; i < gBattlersCount; i++) + { + if ((gSpecialStatuses[i].berryReduced + || (B_SYMBIOSIS_GEMS >= GEN_7 && gSpecialStatuses[i].gemBoost)) + && TryTriggerSymbiosis(i, BATTLE_PARTNER(i))) + { + BestowItem(BATTLE_PARTNER(i), i); + gLastUsedAbility = gBattleMons[BATTLE_PARTNER(i)].ability; + gBattleScripting.battler = gBattlerAbility = BATTLE_PARTNER(i); + gBattlerAttacker = i; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_SymbiosisActivates; + effect = TRUE; + } + } + gBattleScripting.moveendState++; + break; case MOVEEND_FIRST_MOVE_BLOCK: if ((gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_1ST_HIT && IsBattlerAlive(gBattlerTarget)) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT @@ -6640,12 +6658,6 @@ static void Cmd_moveend(void) else gBattleScripting.moveendState++; break; - case MOVEEND_KINGSROCK: // King's rock - // These effects will occur at each hit in a multi-strike move - if (ItemBattleEffects(ITEMEFFECT_KINGSROCK, 0, FALSE)) - effect = TRUE; - gBattleScripting.moveendState++; - break; case MOVEEND_ATTACKER_INVISIBLE: // make attacker sprite invisible if (gStatuses3[gBattlerAttacker] & (STATUS3_SEMI_INVULNERABLE) && gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION)) @@ -6683,6 +6695,12 @@ static void Cmd_moveend(void) } gBattleScripting.moveendState++; break; + case MOVEEND_KINGSROCK: // King's rock + // These effects will occur at each hit in a multi-strike move + if (ItemBattleEffects(ITEMEFFECT_KINGSROCK, 0, FALSE)) + effect = TRUE; + gBattleScripting.moveendState++; + break; case MOVEEND_SUBSTITUTE: for (i = 0; i < gBattlersCount; i++) { @@ -6819,6 +6837,37 @@ static void Cmd_moveend(void) } gBattleScripting.moveendState++; break; + case MOVEEND_DEFROST: + if (gBattleMons[gBattlerTarget].status1 & STATUS1_FREEZE + && IsBattlerTurnDamaged(gBattlerTarget) + && IsBattlerAlive(gBattlerTarget) + && gBattlerAttacker != gBattlerTarget + && (moveType == TYPE_FIRE || CanBurnHitThaw(gCurrentMove)) + && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) + { + gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FREEZE; + BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1); + MarkBattlerForControllerExec(gBattlerTarget); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_DefrostedViaFireMove; + effect = TRUE; + } + if (gBattleMons[gBattlerTarget].status1 & STATUS1_FROSTBITE + && IsBattlerTurnDamaged(gBattlerTarget) + && IsBattlerAlive(gBattlerTarget) + && gBattlerAttacker != gBattlerTarget + && MoveThawsUser(originallyUsedMove) + && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) + { + gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FROSTBITE; + BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1); + MarkBattlerForControllerExec(gBattlerTarget); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_FrostbiteHealedViaFireMove; + effect = TRUE; + } + gBattleScripting.moveendState++; + break; case MOVEEND_NEXT_TARGET: // For moves hitting two opposing Pokemon. { u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); @@ -6942,37 +6991,6 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; } - case MOVEEND_DEFROST: - if (gBattleMons[gBattlerTarget].status1 & STATUS1_FREEZE - && IsBattlerTurnDamaged(gBattlerTarget) - && IsBattlerAlive(gBattlerTarget) - && gBattlerAttacker != gBattlerTarget - && (moveType == TYPE_FIRE || CanBurnHitThaw(gCurrentMove)) - && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) - { - gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FREEZE; - BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1); - MarkBattlerForControllerExec(gBattlerTarget); - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_DefrostedViaFireMove; - effect = TRUE; - } - if (gBattleMons[gBattlerTarget].status1 & STATUS1_FROSTBITE - && IsBattlerTurnDamaged(gBattlerTarget) - && IsBattlerAlive(gBattlerTarget) - && gBattlerAttacker != gBattlerTarget - && MoveThawsUser(originallyUsedMove) - && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) - { - gBattleMons[gBattlerTarget].status1 &= ~STATUS1_FROSTBITE; - BtlController_EmitSetMonData(gBattlerTarget, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerTarget].status1), &gBattleMons[gBattlerTarget].status1); - MarkBattlerForControllerExec(gBattlerTarget); - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_FrostbiteHealedViaFireMove; - effect = TRUE; - } - gBattleScripting.moveendState++; - break; case MOVEEND_SECOND_MOVE_BLOCK: if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) { @@ -7083,6 +7101,57 @@ static void Cmd_moveend(void) else gBattleScripting.moveendState++; break; + case MOVEEND_RED_CARD: + { + u32 redCardBattlers = 0, i; + for (i = 0; i < gBattlersCount; i++) + { + if (i == gBattlerAttacker) + continue; + if (GetBattlerHoldEffect(i, TRUE) == HOLD_EFFECT_RED_CARD) + redCardBattlers |= (1u << i); + } + if (redCardBattlers && IsBattlerAlive(gBattlerAttacker)) + { + // Since we check if battler was damaged, we don't need to check move result. + // In fact, doing so actually prevents multi-target moves from activating red card properly + u8 battlers[4] = {0, 1, 2, 3}; + SortBattlersBySpeed(battlers, FALSE); + for (i = 0; i < gBattlersCount; i++) + { + u32 battler = battlers[i]; + // Search for fastest hit pokemon with a red card + // Attacker is the one to be switched out, battler is one with red card + if (redCardBattlers & (1u << battler) + && IsBattlerAlive(battler) + && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) + && IsBattlerTurnDamaged(battler) + && CanBattlerSwitch(gBattlerAttacker) + && !(moveEffect == EFFECT_HIT_SWITCH_TARGET && CanBattlerSwitch(battler))) + { + effect = TRUE; + gLastUsedItem = gBattleMons[battler].item; + SaveBattlerTarget(battler); // save battler with red card + SaveBattlerAttacker(gBattlerAttacker); + gBattleScripting.battler = battler; + gEffectBattler = gBattlerAttacker; + BattleScriptPushCursor(); + if (gBattleStruct->commanderActive[gBattlerAttacker] != SPECIES_NONE + || GetBattlerAbility(gBattlerAttacker) == ABILITY_GUARD_DOG + || GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) + gBattlescriptCurrInstr = BattleScript_RedCardActivationNoSwitch; + else + gBattlescriptCurrInstr = BattleScript_RedCardActivates; + break; // Only fastest red card activates + } + } + } + } + if (effect) + gBattleScripting.moveendState = MOVEEND_OPPORTUNIST; + else + gBattleScripting.moveendState++; + break; case MOVEEND_EJECT_BUTTON: { // Because sorting the battlers by speed takes lots of cycles, it's better to just check if any of the battlers has the Eject items. @@ -7130,6 +7199,61 @@ static void Cmd_moveend(void) else gBattleScripting.moveendState++; break; + case MOVEEND_LIFEORB_SHELLBELL: + if (ItemBattleEffects(ITEMEFFECT_LIFEORB_SHELLBELL, 0, FALSE)) + effect = TRUE; + gBattleScripting.moveendState++; + break; + case MOVEEND_EMERGENCY_EXIT: // Special case, because moves hitting multiple opponents stop after switching out + { + // Because sorting the battlers by speed takes lots of cycles, + // we check if EE can be activated and cound how many. + u32 numEmergencyExitBattlers = 0; + u32 emergencyExitBattlers = 0; + + for (i = 0; i < gBattlersCount; i++) + { + if (EmergencyExitCanBeTriggered(i)) + { + emergencyExitBattlers |= 1u << i; + numEmergencyExitBattlers++; + } + } + + if (numEmergencyExitBattlers == 0) + { + gBattleScripting.moveendState++; + break; + } + + u8 battlers[4] = {0, 1, 2, 3}; + if (numEmergencyExitBattlers > 1) + SortBattlersBySpeed(battlers, FALSE); + + for (i = 0; i < gBattlersCount; i++) + { + u32 battler = battlers[i]; + + if (!(emergencyExitBattlers & 1u << battler)) + continue; + + effect = TRUE; + gBattleScripting.battler = battler; + BattleScriptPushCursor(); + + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || IsOnPlayerSide(battler)) + gBattlescriptCurrInstr = BattleScript_EmergencyExit; + else + gBattlescriptCurrInstr = BattleScript_EmergencyExitWild; + + break; // Only the fastest Emergency Exit / Wimp Out activates + } + } + if (effect) + gBattleScripting.moveendState = MOVEEND_OPPORTUNIST; + else + gBattleScripting.moveendState++; + break; case MOVEEND_EJECT_PACK: { // Because sorting the battlers by speed takes lots of cycles, it's better to just check if any of the battlers has the Eject items. @@ -7184,80 +7308,25 @@ static void Cmd_moveend(void) else gBattleScripting.moveendState++; break; - case MOVEEND_WHITE_HERB: - for (i = 0; i < gBattlersCount; i++) + case MOVEEND_HIT_ESCAPE: + if (moveEffect == EFFECT_HIT_ESCAPE + && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) + && IsBattlerTurnDamaged(gBattlerTarget) + && IsBattlerAlive(gBattlerAttacker) + && !NoAliveMonsForBattlerSide(gBattlerTarget)) { - if (!IsBattlerAlive(i)) - continue; - - if (GetBattlerHoldEffect(i, TRUE) == HOLD_EFFECT_WHITE_HERB - && RestoreWhiteHerbStats(i)) - { - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_WhiteHerbRet; - effect = TRUE; - break; - } + effect = TRUE; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_EffectHitEscape; } - if (!effect) - gBattleScripting.moveendState++; + gBattleScripting.moveendState++; break; - case MOVEEND_RED_CARD: - { - u32 redCardBattlers = 0, i; - for (i = 0; i < gBattlersCount; i++) - { - if (i == gBattlerAttacker) - continue; - if (GetBattlerHoldEffect(i, TRUE) == HOLD_EFFECT_RED_CARD) - redCardBattlers |= (1u << i); - } - if (redCardBattlers && IsBattlerAlive(gBattlerAttacker)) - { - // Since we check if battler was damaged, we don't need to check move result. - // In fact, doing so actually prevents multi-target moves from activating red card properly - u8 battlers[4] = {0, 1, 2, 3}; - SortBattlersBySpeed(battlers, FALSE); - for (i = 0; i < gBattlersCount; i++) - { - u32 battler = battlers[i]; - // Search for fastest hit pokemon with a red card - // Attacker is the one to be switched out, battler is one with red card - if (redCardBattlers & (1u << battler) - && IsBattlerAlive(battler) - && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) - && IsBattlerTurnDamaged(battler) - && CanBattlerSwitch(gBattlerAttacker) - && !(moveEffect == EFFECT_HIT_SWITCH_TARGET && CanBattlerSwitch(battler))) - { - effect = TRUE; - gLastUsedItem = gBattleMons[battler].item; - SaveBattlerTarget(battler); // save battler with red card - SaveBattlerAttacker(gBattlerAttacker); - gBattleScripting.battler = battler; - gEffectBattler = gBattlerAttacker; - BattleScriptPushCursor(); - if (gBattleStruct->commanderActive[gBattlerAttacker] != SPECIES_NONE - || GetBattlerAbility(gBattlerAttacker) == ABILITY_GUARD_DOG - || GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) - gBattlescriptCurrInstr = BattleScript_RedCardActivationNoSwitch; - else - gBattlescriptCurrInstr = BattleScript_RedCardActivates; - break; // Only fastest red card activates - } - } - } - } - if (effect) - gBattleScripting.moveendState = MOVEEND_OPPORTUNIST; + case MOVEEND_OPPORTUNIST: + if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0)) + effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers else gBattleScripting.moveendState++; break; - case MOVEEND_LIFEORB_SHELLBELL: - if (ItemBattleEffects(ITEMEFFECT_LIFEORB_SHELLBELL, 0, FALSE)) - effect = TRUE; - gBattleScripting.moveendState++; - break; case MOVEEND_PICKPOCKET: if (IsBattlerAlive(gBattlerAttacker) && gBattleMons[gBattlerAttacker].item != ITEM_NONE // Attacker must be holding an item @@ -7294,75 +7363,6 @@ static void Cmd_moveend(void) } gBattleScripting.moveendState++; break; - case MOVEEND_EMERGENCY_EXIT: // Special case, because moves hitting multiple opponents stop after switching out - { - // Because sorting the battlers by speed takes lots of cycles, - // we check if EE can be activated and cound how many. - u32 numEmergencyExitBattlers = 0; - u32 emergencyExitBattlers = 0; - - for (i = 0; i < gBattlersCount; i++) - { - if (EmergencyExitCanBeTriggered(i)) - { - emergencyExitBattlers |= 1u << i; - numEmergencyExitBattlers++; - } - } - - if (numEmergencyExitBattlers == 0) - { - gBattleScripting.moveendState++; - break; - } - - u8 battlers[4] = {0, 1, 2, 3}; - if (numEmergencyExitBattlers > 1) - SortBattlersBySpeed(battlers, FALSE); - - for (i = 0; i < gBattlersCount; i++) - { - u32 battler = battlers[i]; - - if (!(emergencyExitBattlers & 1u << battler)) - continue; - - effect = TRUE; - gBattleScripting.battler = battler; - BattleScriptPushCursor(); - - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || IsOnPlayerSide(battler)) - gBattlescriptCurrInstr = BattleScript_EmergencyExit; - else - gBattlescriptCurrInstr = BattleScript_EmergencyExitWild; - - break; // Only the fastest Emergency Exit / Wimp Out activates - } - } - if (effect) - gBattleScripting.moveendState = MOVEEND_OPPORTUNIST; - else - gBattleScripting.moveendState++; - break; - case MOVEEND_HIT_ESCAPE: - if (moveEffect == EFFECT_HIT_ESCAPE - && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) - && IsBattlerTurnDamaged(gBattlerTarget) - && IsBattlerAlive(gBattlerAttacker) - && !NoAliveMonsForBattlerSide(gBattlerTarget)) - { - effect = TRUE; - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_EffectHitEscape; - } - gBattleScripting.moveendState++; - break; - case MOVEEND_OPPORTUNIST: - if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0)) - effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers - else - gBattleScripting.moveendState++; - break; case MOVEEND_REMOVE_TERRAIN: if (GetMoveEffect(gChosenMove) == EFFECT_STEEL_ROLLER // Steel Roller has to check the chosen move, Otherwise it would fail in certain cases && IsBattlerTurnDamaged(gBattlerTarget)) @@ -7381,20 +7381,31 @@ static void Cmd_moveend(void) } gBattleScripting.moveendState++; break; - case MOVEEND_SYMBIOSIS: + case MOVEEND_WHITE_HERB: for (i = 0; i < gBattlersCount; i++) { - if ((gSpecialStatuses[i].berryReduced - || (B_SYMBIOSIS_GEMS >= GEN_7 && gSpecialStatuses[i].gemBoost)) - && TryTriggerSymbiosis(i, BATTLE_PARTNER(i))) + if (!IsBattlerAlive(i)) + continue; + + if (GetBattlerHoldEffect(i, TRUE) == HOLD_EFFECT_WHITE_HERB + && RestoreWhiteHerbStats(i)) { - BestowItem(BATTLE_PARTNER(i), i); - gLastUsedAbility = gBattleMons[BATTLE_PARTNER(i)].ability; - gBattleScripting.battler = gBattlerAbility = BATTLE_PARTNER(i); - gBattlerAttacker = i; BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_SymbiosisActivates; + gBattlescriptCurrInstr = BattleScript_WhiteHerbRet; effect = TRUE; + break; + } + } + if (!effect) + gBattleScripting.moveendState++; + break; + case MOVEEND_CHANGED_ITEMS: + for (i = 0; i < gBattlersCount; i++) + { + if (gBattleStruct->changedItems[i] != ITEM_NONE) + { + gBattleMons[i].item = gBattleStruct->changedItems[i]; + gBattleStruct->changedItems[i] = ITEM_NONE; } } gBattleScripting.moveendState++; @@ -7408,17 +7419,6 @@ static void Cmd_moveend(void) gBattleStruct->sameMoveTurns[gBattlerAttacker]++; gBattleScripting.moveendState++; break; - case MOVEEND_CHANGED_ITEMS: - for (i = 0; i < gBattlersCount; i++) - { - if (gBattleStruct->changedItems[i] != ITEM_NONE) - { - gBattleMons[i].item = gBattleStruct->changedItems[i]; - gBattleStruct->changedItems[i] = ITEM_NONE; - } - } - gBattleScripting.moveendState++; - break; case MOVEEND_CLEAR_BITS: // Clear/Set bits for things like using a move for all targets and all hits. if (gSpecialStatuses[gBattlerAttacker].instructedChosenTarget) gBattleStruct->moveTarget[gBattlerAttacker] = gSpecialStatuses[gBattlerAttacker].instructedChosenTarget & 0x3;