diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 65bac00d73..9ebf32a927 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -863,6 +863,7 @@ Move_ROOST: Move_GRAVITY: fadetobg BG_COSMIC waitbgfadein + createvisualtask AnimTask_SetAnimTargetToAttackerOpposite, 1 playsewithpan SE_M_TAKE_DOWN, SOUND_PAN_ATTACKER createvisualtask AnimTask_ShakeAndSinkMon, 5, ANIM_ATTACKER, 2, 0, 96, 30 createvisualtask AnimTask_ShakeAndSinkMon, 5, ANIM_DEF_PARTNER, 2, 0, 96, 30 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index e3b34fdda9..007d3649c4 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6258,6 +6258,8 @@ BattleScript_CottonDownActivates:: savetarget setbyte gBattlerTarget, 0 BattleScript_CottonDownLoop: + getbattlerfainted BS_TARGET + jumpifbyte CMP_EQUAL, gBattleCommunication, TRUE, BattleScript_CottonDownLoopIncrement setstatchanger STAT_SPEED, 1, TRUE jumpifbyteequal gBattlerTarget, gEffectBattler, BattleScript_CottonDownLoopIncrement statbuffchange STAT_BUFF_NOT_PROTECT_AFFECTED, BattleScript_CottonDownTargetSpeedCantGoLower diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index db33a7ff11..d4df682c63 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -36,6 +36,7 @@ u32 AI_GetMoveAccuracy(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbil bool32 DoesBattlerIgnoreAbilityChecks(u16 atkAbility, u16 move); bool32 AI_WeatherHasEffect(void); bool32 CanAttackerFaintTarget(u8 battlerAtk, u8 battlerDef, u8 index, u8 numHits); +bool32 AI_IsTerrainAffected(u8 battlerId, u32 flags); bool32 AI_IsBattlerGrounded(u8 battlerId); bool32 HasDamagingMove(u8 battlerId); bool32 HasDamagingMoveOfType(u8 battlerId, u8 type); diff --git a/include/battle_util.h b/include/battle_util.h index 717017c06c..9aa61221f4 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -124,7 +124,7 @@ u16 GetMegaEvolutionSpecies(u16 preEvoSpecies, u16 heldItemId); u16 GetWishMegaEvolutionSpecies(u16 preEvoSpecies, u16 moveId1, u16 moveId2, u16 moveId3, u16 moveId4); bool32 CanMegaEvolve(u8 battlerId); void UndoMegaEvolution(u32 monId); -void UndoFormChange(u32 monId, u32 side); +void UndoFormChange(u32 monId, u32 side, bool32 isSwitchingOut); bool32 DoBattlersShareType(u32 battler1, u32 battler2); bool32 CanBattlerGetOrLoseItem(u8 battlerId, u16 itemId); struct Pokemon *GetIllusionMonPtr(u32 battlerId); diff --git a/include/constants/battle_config.h b/include/constants/battle_config.h index 0f878d3af6..fd478ae6be 100644 --- a/include/constants/battle_config.h +++ b/include/constants/battle_config.h @@ -1,6 +1,8 @@ #ifndef GUARD_CONSTANTS_BATTLE_CONFIG_H #define GUARD_CONSTANTS_BATTLE_CONFIG_H +#include "constants/expansion_branches.h" + // Species with peculiar battle effects. #ifndef POKEMON_EXPANSION #define SPECIES_DIALGA 0 diff --git a/include/constants/expansion_branches.h b/include/constants/expansion_branches.h new file mode 100644 index 0000000000..f88066e684 --- /dev/null +++ b/include/constants/expansion_branches.h @@ -0,0 +1,10 @@ +#ifndef GUARD_CONSTANTS_EXPANSION_BRANCHES_H +#define GUARD_CONSTANTS_EXPANSION_BRANCHES_H + +// Branch defines: Used by other branches to detect each other. +// Each define must be here for each of RHH's branch you have pulled. +// e.g. If you have both the battle_engine and pokemon_expansion branch, +// then both BATTLE_ENGINE and POKEMON_EXPANSION must be defined here. +#define BATTLE_ENGINE + +#endif diff --git a/include/global.h b/include/global.h index a5d720eda5..a0aa45d3d0 100644 --- a/include/global.h +++ b/include/global.h @@ -10,6 +10,7 @@ #include "constants/vars.h" #include "constants/species.h" #include "constants/berry.h" +#include "constants/expansion_branches.h" // Prevent cross-jump optimization. #define BLOCK_CROSS_JUMP asm(""); @@ -127,12 +128,6 @@ f; \ }) -// Branch defines: Used by other branches to detect each other. -// Each define must be here for each of RHH's branch you have pulled. -// e.g. If you have both the battle_engine and pokemon_expansion branch, -// then both BATTLE_ENGINE and POKEMON_EXPANSION must be defined here. -#define BATTLE_ENGINE - #define ROUND_BITS_TO_BYTES(numBits)(((numBits) / 8) + (((numBits) % 8) ? 1 : 0)) #define DEX_FLAGS_NO (ROUND_BITS_TO_BYTES(NUM_SPECIES)) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 5dc460f6a5..81d97fcab6 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -721,22 +721,21 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) #endif // terrain & effect checks - if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) + if (AI_IsTerrainAffected(battlerDef, STATUS_FIELD_ELECTRIC_TERRAIN)) { if (moveEffect == EFFECT_SLEEP || moveEffect == EFFECT_YAWN) RETURN_SCORE_MINUS(20); } - if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + if (AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) { if (IsNonVolatileStatusMoveEffect(moveEffect) || IsConfusionMoveEffect(moveEffect)) RETURN_SCORE_MINUS(20); } - if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) + if (AI_IsTerrainAffected(battlerAtk, STATUS_FIELD_PSYCHIC_TERRAIN) && atkPriority > 0) { - if (atkPriority > 0) - RETURN_SCORE_MINUS(20); + RETURN_SCORE_MINUS(20); } } // end check MOVE_TARGET_USER @@ -1641,11 +1640,11 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) case EFFECT_TEETER_DANCE: if (((gBattleMons[battlerDef].status2 & STATUS2_CONFUSION) || (!DoesBattlerIgnoreAbilityChecks(AI_DATA->atkAbility, move) && AI_DATA->defAbility == ABILITY_OWN_TEMPO) - || (IsBattlerGrounded(battlerDef) && (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + || (IsBattlerGrounded(battlerDef) && AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) || (DoesSubstituteBlockMove(battlerAtk, battlerDef, move))) && ((gBattleMons[AI_DATA->battlerDefPartner].status2 & STATUS2_CONFUSION) || (!DoesBattlerIgnoreAbilityChecks(AI_DATA->atkAbility, move) && AI_DATA->defPartnerAbility == ABILITY_OWN_TEMPO) - || (IsBattlerGrounded(AI_DATA->battlerDefPartner) && (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + || (IsBattlerGrounded(AI_DATA->battlerDefPartner) && AI_IsTerrainAffected(AI_DATA->battlerDefPartner, STATUS_FIELD_MISTY_TERRAIN)) || (DoesSubstituteBlockMove(battlerAtk, AI_DATA->battlerDefPartner, move)))) { score -= 10; @@ -3836,7 +3835,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score++; break; case EFFECT_SAFEGUARD: - if (!(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) || !IsBattlerGrounded(battlerAtk)) + if (!AI_IsTerrainAffected(battlerAtk, STATUS_FIELD_MISTY_TERRAIN) || !IsBattlerGrounded(battlerAtk)) score++; //if (CountUsablePartyMons(battlerDef) != 0) //score += 8; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 5695677bba..600921d97c 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1110,6 +1110,15 @@ u16 AI_GetHoldEffect(u32 battlerId) return holdEffect; } +bool32 AI_IsTerrainAffected(u8 battlerId, u32 flags) +{ + if (gStatuses3[battlerId] & STATUS3_SEMI_INVULNERABLE) + return FALSE; + else if (!(gFieldStatuses & flags)) + return FALSE; + return AI_IsBattlerGrounded(battlerId); +} + // different from IsBattlerGrounded in that we don't always know battler's hold effect or ability bool32 AI_IsBattlerGrounded(u8 battlerId) { diff --git a/src/battle_anim_mon_movement.c b/src/battle_anim_mon_movement.c index 9b08352244..75b77494e3 100644 --- a/src/battle_anim_mon_movement.c +++ b/src/battle_anim_mon_movement.c @@ -321,6 +321,12 @@ static void AnimTask_ShakeMonInPlace_Step(u8 taskId) void AnimTask_ShakeAndSinkMon(u8 taskId) { u8 spriteId = GetAnimBattlerSpriteId(gBattleAnimArgs[0]); + + if (spriteId == SPRITE_NONE) + { + DestroyAnimVisualTask(taskId); + return; + } gSprites[spriteId].pos2.x = gBattleAnimArgs[1]; gTasks[taskId].data[0] = spriteId; gTasks[taskId].data[1] = gBattleAnimArgs[1]; diff --git a/src/battle_anim_utility_funcs.c b/src/battle_anim_utility_funcs.c index 4d18d04fa2..0f2c8bda80 100644 --- a/src/battle_anim_utility_funcs.c +++ b/src/battle_anim_utility_funcs.c @@ -1084,3 +1084,9 @@ void AnimTask_SetInvisible(u8 taskId) gSprites[spriteId].invisible = gBattleSpritesDataPtr->battlerData[battlerId].invisible = gBattleAnimArgs[1]; DestroyAnimVisualTask(taskId); } + +void AnimTask_SetAnimTargetToAttackerOpposite(u8 taskId) +{ + gBattleAnimTarget = BATTLE_OPPOSITE(gBattleAnimAttacker); + DestroyAnimVisualTask(taskId); +} diff --git a/src/battle_main.c b/src/battle_main.c index 6befbfbf5a..c728ae266a 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3123,7 +3123,7 @@ void FaintClearSetData(void) ClearBattlerMoveHistory(gActiveBattler); ClearBattlerAbilityHistory(gActiveBattler); ClearBattlerItemEffectHistory(gActiveBattler); - UndoFormChange(gBattlerPartyIndexes[gActiveBattler], GET_BATTLER_SIDE(gActiveBattler)); + UndoFormChange(gBattlerPartyIndexes[gActiveBattler], GET_BATTLER_SIDE(gActiveBattler), FALSE); if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) UndoMegaEvolution(gBattlerPartyIndexes[gActiveBattler]); } @@ -4888,7 +4888,7 @@ static void HandleEndTurn_FinishBattle(void) for (i = 0; i < PARTY_SIZE; i++) { UndoMegaEvolution(i); - UndoFormChange(i, B_SIDE_PLAYER); + UndoFormChange(i, B_SIDE_PLAYER, FALSE); } gBattleMainFunc = FreeResetData_ReturnToOvOrDoEvolutions; gCB2_AfterEvolution = BattleMainCB2; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index f5f093cc5c..cb2f791919 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -12098,6 +12098,7 @@ static void Cmd_handleballthrow(void) { BtlController_EmitBallThrowAnim(0, BALL_3_SHAKES_SUCCESS); MarkBattlerForControllerExec(gActiveBattler); + UndoFormChange(gBattlerPartyIndexes[gBattlerTarget], GET_BATTLER_SIDE(gBattlerTarget)); gBattlescriptCurrInstr = BattleScript_SuccessBallThrow; SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem); @@ -12142,6 +12143,7 @@ static void Cmd_handleballthrow(void) if (IsCriticalCapture()) gBattleSpritesDataPtr->animationData->criticalCaptureSuccess = 1; + UndoFormChange(gBattlerPartyIndexes[gBattlerTarget], GET_BATTLER_SIDE(gBattlerTarget)); gBattlescriptCurrInstr = BattleScript_SuccessBallThrow; SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem); if (CalculatePlayerPartyCount() == PARTY_SIZE) diff --git a/src/battle_util.c b/src/battle_util.c index 7a86b659f8..4ed7572c4a 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -492,7 +492,7 @@ void HandleAction_Switch(void) if (gBattleResults.playerSwitchesCounter < 255) gBattleResults.playerSwitchesCounter++; - UndoFormChange(gBattlerPartyIndexes[gBattlerAttacker], GetBattlerSide(gBattlerAttacker)); + UndoFormChange(gBattlerPartyIndexes[gBattlerAttacker], GetBattlerSide(gBattlerAttacker), TRUE); } void HandleAction_UseItem(void) @@ -8333,14 +8333,14 @@ void UndoMegaEvolution(u32 monId) } } -void UndoFormChange(u32 monId, u32 side) +void UndoFormChange(u32 monId, u32 side, bool32 isSwitchingOut) { u32 i, currSpecies; struct Pokemon *party = (side == B_SIDE_PLAYER) ? gPlayerParty : gEnemyParty; static const u16 species[][2] = // changed form id, default form id { - {SPECIES_AEGISLASH_BLADE, SPECIES_AEGISLASH}, {SPECIES_MIMIKYU_BUSTED, SPECIES_MIMIKYU}, + {SPECIES_AEGISLASH_BLADE, SPECIES_AEGISLASH}, {SPECIES_DARMANITAN_ZEN_MODE, SPECIES_DARMANITAN}, {SPECIES_MINIOR, SPECIES_MINIOR_CORE_RED}, {SPECIES_MINIOR_METEOR_BLUE, SPECIES_MINIOR_CORE_BLUE}, @@ -8352,8 +8352,13 @@ void UndoFormChange(u32 monId, u32 side) {SPECIES_WISHIWASHI_SCHOOL, SPECIES_WISHIWASHI}, }; + if (isSwitchingOut) // Don't revert Mimikyu Busted when switching out + i = 1; + else + i = 0; + currSpecies = GetMonData(&party[monId], MON_DATA_SPECIES, NULL); - for (i = 0; i < ARRAY_COUNT(species); i++) + for (; i < ARRAY_COUNT(species); i++) { if (currSpecies == species[i][0]) {