From c2f81cd5f52a7a0da113b33d4b5e4029b04c1d01 Mon Sep 17 00:00:00 2001 From: W1serV1ser <1prefertheaircauseimanairnomad@gmail.com> Date: Sat, 20 Nov 2021 11:44:48 -0800 Subject: [PATCH] Added Sky Drop --- asm/macros/battle_script.inc | 22 +++ data/battle_scripts_1.s | 65 ++++++++- include/battle.h | 1 + include/constants/battle.h | 5 +- include/constants/battle_move_effects.h | 3 +- include/constants/battle_script_commands.h | 29 ++-- include/constants/battle_string_ids.h | 39 +++--- src/battle_ai_switch_items.c | 4 +- src/battle_main.c | 66 ++++++++- src/battle_message.c | 5 + src/battle_script_commands.c | 154 +++++++++++++++++++-- src/battle_util.c | 140 ++++++++++++++++++- src/data/battle_moves.h | 2 +- 13 files changed, 484 insertions(+), 51 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 6ba63f916..4ef2168fb 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -2055,3 +2055,25 @@ 1: .endm + @ Will jump to script pointer if the target weighs less than 200 kg, or 441 lbs. + .macro jumpifunder200 battler:req, ptr:req + various \battler, VARIOUS_JUMP_IF_UNDER_200 + .4byte \ptr + .endm + + @ Sets the sky drop status and does all other necessary operations + .macro setskydrop + various 0, VARIOUS_SET_SKY_DROP + .endm + + @ Clears the sky drop status and does all other necessary operations. + @ If the target fainted in before this script is called, it goes to the given script. + .macro clearskydrop ptr:req + various 0, VARIOUS_CLEAR_SKY_DROP + .4byte \ptr + .endm + + @ Accounts for if the target of Sky Drop was in confuse_lock when the attacker falls asleep due to Yawn. + .macro skydropyawn + various 0, VARIOUS_SKY_DROP_YAWN + .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index e9d019e43..39f184ed8 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -374,7 +374,7 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectJungleHealing @ EFFECT_JUNGLE_HEALING .4byte BattleScript_EffectCoaching @ EFFECT_COACHING .4byte BattleScript_EffectHit @ EFFECT_LASH_OUT - .4byte BattleScript_EffectHit @ EFFECT_GRASSY_GLIDE + .4byte BattleScript_EffectHit @ EFFECT_GRASSY_GLIDE .4byte BattleScript_EffectRemoveTerrain @ EFFECT_REMOVE_TERRAIN .4byte BattleScript_EffectHit @ EFFECT_DYNAMAX_DOUBLE_DMG .4byte BattleScript_EffectDecorate @ EFFECT_DECORATE @@ -403,6 +403,65 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectOctolock @ EFFECT_OCTOLOCK .4byte BattleScript_EffectClangorousSoul @ EFFECT_CLANGOROUS_SOUL .4byte BattleScript_EffectHit @ EFFECT_BOLT_BEAK + .4byte BattleScript_EffectSkyDrop @ EFFECT_SKY_DROP + +BattleScript_EffectSkyDrop: + jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_SkyDropTurn2 + attackcanceler + ppreduce + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + attackstring + jumpifsubstituteblocks BattleScript_ButItFailed + jumpiftargetally BattleScript_ButItFailed + jumpifunder200 BS_TARGET, BattleScript_SkyDropWork + pause B_WAIT_TIME_SHORT + printstring STRINGID_TARGETTOOHEAVY + waitmessage B_WAIT_TIME_LONG + goto BattleScript_MoveEnd + +BattleScript_SkyDropWork: + setskydrop + setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SKY_DROP + setsemiinvulnerablebit + call BattleScriptFirstChargingTurnAfterAttackString + goto BattleScript_MoveEnd +BattleScript_SkyDropTurn2: + attackcanceler + setmoveeffect MOVE_EFFECT_CHARGING + setbyte sB_ANIM_TURN, 0x1 + clearstatusfromeffect BS_ATTACKER + orword gHitMarker, HITMARKER_NO_PPDEDUCT + argumenttomoveeffect + clearsemiinvulnerablebit + attackstring + clearskydrop BattleScript_SkyDropChangedTarget + jumpiftype BS_TARGET, TYPE_FLYING, BattleScript_SkyDropFlyingType + goto BattleScript_HitFromCritCalc +BattleScript_SkyDropFlyingType: + makevisible BS_TARGET + printstring STRINGID_ITDOESNTAFFECT + waitmessage B_WAIT_TIME_LONG + makevisible BS_ATTACKER + jumpifstatus2 BS_TARGET, STATUS2_CONFUSION, BattleScript_SkyDropFlyingAlreadyConfused + jumpifstatus2 BS_TARGET, STATUS2_LOCK_CONFUSE, BattleScript_SkyDropFlyingConfuseLock + goto BattleScript_MoveEnd +BattleScript_SkyDropChangedTarget: + pause B_WAIT_TIME_SHORT + orhalfword gMoveResultFlags, MOVE_RESULT_FAILED + resultmessage + waitmessage B_WAIT_TIME_LONG + makevisible BS_ATTACKER + goto BattleScript_MoveEnd + +BattleScript_SkyDropFlyingConfuseLock: + setmoveeffect MOVE_EFFECT_CONFUSION + seteffectprimary +BattleScript_SkyDropFlyingAlreadyConfused: + setmoveeffect MOVE_EFFECT_THRASH + clearstatusfromeffect BS_TARGET + jumpifstatus2 BS_TARGET, STATUS2_CONFUSION, BattleScript_MoveEnd + setbyte BS_ATTACKER, BS_TARGET + goto BattleScript_ThrashConfuses BattleScript_EffectShellSideArm: shellsidearmcheck @@ -3400,6 +3459,7 @@ BattleScriptFirstChargingTurn:: printstring STRINGID_EMPTYSTRING3 ppreduce attackstring +BattleScriptFirstChargingTurnAfterAttackString: pause B_WAIT_TIME_LONG copybyte cMULTISTRING_CHOOSER, sTWOTURN_STRINGID printfromtable gFirstTurnOfTwoStringIds @@ -7511,7 +7571,10 @@ BattleScript_YawnMakesAsleep:: waitmessage B_WAIT_TIME_LONG updatestatusicon BS_EFFECT_BATTLER waitstate + jumpifstatus3 BS_EFFECT_BATTLER, STATUS3_SKY_DROPPED, BattleScript_YawnEnd makevisible BS_EFFECT_BATTLER + skydropyawn +BattleScript_YawnEnd: end2 BattleScript_EmbargoEndTurn:: diff --git a/include/battle.h b/include/battle.h index b587e482d..4cb7e7e02 100644 --- a/include/battle.h +++ b/include/battle.h @@ -616,6 +616,7 @@ struct BattleStruct u8 blunderPolicy:1; // should blunder policy activate u8 ballSpriteIds[2]; // item gfx, window gfx u8 stickyWebUser; + u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle. }; #define GET_MOVE_TYPE(move, typeArg) \ diff --git a/include/constants/battle.h b/include/constants/battle.h index 28934abc2..86e5d14c9 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -170,6 +170,7 @@ #define STATUS3_AQUA_RING (1 << 28) #define STATUS3_LASER_FOCUS (1 << 29) #define STATUS3_POWER_TRICK (1 << 30) +#define STATUS3_SKY_DROPPED (1 << 31) // Target of Sky Drop #define STATUS3_SEMI_INVULNERABLE (STATUS3_UNDERGROUND | STATUS3_ON_AIR | STATUS3_UNDERWATER | STATUS3_PHANTOM_FORCE) #define STATUS4_ELECTRIFIED (1 << 0) @@ -359,7 +360,9 @@ #define MOVE_EFFECT_RECOIL_HP_25 0x46 #define MOVE_EFFECT_RELIC_SONG 0x47 #define MOVE_EFFECT_TRAP_BOTH 0x48 -#define NUM_MOVE_EFFECTS 0x49 +#define MOVE_EFFECT_SKY_DROP 0x49 + +#define NUM_MOVE_EFFECTS 0x50 #define MOVE_EFFECT_AFFECTS_USER 0x4000 #define MOVE_EFFECT_CERTAIN 0x8000 diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 3f24caf5e..82fc381a6 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -386,7 +386,8 @@ #define EFFECT_OCTOLOCK 380 #define EFFECT_CLANGOROUS_SOUL 381 #define EFFECT_BOLT_BEAK 382 +#define EFFECT_SKY_DROP 383 -#define NUM_BATTLE_MOVE_EFFECTS 383 +#define NUM_BATTLE_MOVE_EFFECTS 384 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index bd16f741b..c191bc5e5 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -208,6 +208,10 @@ #define VARIOUS_SET_OCTOLOCK 135 #define VARIOUS_CUT_1_3_HP_RAISE_STATS 136 #define VARIOUS_TRY_END_NEUTRALIZING_GAS 137 +#define VARIOUS_JUMP_IF_UNDER_200 138 +#define VARIOUS_SET_SKY_DROP 139 +#define VARIOUS_CLEAR_SKY_DROP 140 +#define VARIOUS_SKY_DROP_YAWN 141 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 @@ -265,18 +269,19 @@ #define MOVEEND_ITEM_EFFECTS_ALL 15 #define MOVEEND_KINGSROCK 16 // These item effects will occur each strike of a multi-hit move #define MOVEEND_SUBSTITUTE 17 -#define MOVEEND_UPDATE_LAST_MOVES 18 -#define MOVEEND_MIRROR_MOVE 19 -#define MOVEEND_NEXT_TARGET 20 // Everything up until here is handled for each strike of a multi-hit move -#define MOVEEND_EJECT_BUTTON 21 -#define MOVEEND_RED_CARD 22 -#define MOVEEND_EJECT_PACK 23 -#define MOVEEND_LIFEORB_SHELLBELL 24 // Includes shell bell, throat spray, etc -#define MOVEEND_PICKPOCKET 25 -#define MOVEEND_DANCER 26 -#define MOVEEND_EMERGENCY_EXIT 27 -#define MOVEEND_CLEAR_BITS 28 -#define MOVEEND_COUNT 29 +#define MOVEEND_SKY_DROP_CONFUSE 18 +#define MOVEEND_UPDATE_LAST_MOVES 19 +#define MOVEEND_MIRROR_MOVE 20 +#define MOVEEND_NEXT_TARGET 21 // Everything up until here is handled for each strike of a multi-hit move +#define MOVEEND_EJECT_BUTTON 22 +#define MOVEEND_RED_CARD 23 +#define MOVEEND_EJECT_PACK 24 +#define MOVEEND_LIFEORB_SHELLBELL 25 // Includes shell bell, throat spray, etc +#define MOVEEND_PICKPOCKET 26 +#define MOVEEND_DANCER 27 +#define MOVEEND_EMERGENCY_EXIT 28 +#define MOVEEND_CLEAR_BITS 29 +#define MOVEEND_COUNT 30 // switch cases #define B_SWITCH_NORMAL 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 7846fa521..83e931c2c 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -592,25 +592,27 @@ #define STRINGID_STRONGWINDSDISSIPATED 588 #define STRINGID_MYSTERIOUSAIRCURRENTBLOWSON 589 #define STRINGID_ATTACKWEAKENEDBSTRONGWINDS 590 -#define STRINGID_STUFFCHEEKSCANTSELECT 592 -#define STRINGID_PKMNREVERTEDTOPRIMAL 593 -#define STRINGID_BUTPOKEMONCANTUSETHEMOVE 594 -#define STRINGID_BUTHOOPACANTUSEIT 595 -#define STRINGID_BROKETHROUGHPROTECTION 596 -#define STRINGID_ABILITYALLOWSONLYMOVE 597 -#define STRINGID_SWAPPEDABILITIES 598 -#define STRINGID_PASTELVEILPROTECTED 599 -#define STRINGID_PASTELVEILENTERS 600 -#define STRINGID_BATTLERTYPECHANGEDTO 601 -#define STRINGID_BOTHCANNOLONGERESCAPE 602 -#define STRINGID_CANTESCAPEDUETOUSEDMOVE 603 -#define STRINGID_PKMNBECAMEWEAKERTOFIRE 604 -#define STRINGID_ABOUTTOUSEPOLTERGEIST 605 -#define STRINGID_CANTESCAPEBECAUSEOFCURRENTMOVE 606 -#define STRINGID_NEUTRALIZINGGASENTERS 607 -#define STRINGID_NEUTRALIZINGGASOVER 608 +#define STRINGID_STUFFCHEEKSCANTSELECT 591 +#define STRINGID_PKMNREVERTEDTOPRIMAL 592 +#define STRINGID_BUTPOKEMONCANTUSETHEMOVE 593 +#define STRINGID_BUTHOOPACANTUSEIT 594 +#define STRINGID_BROKETHROUGHPROTECTION 595 +#define STRINGID_ABILITYALLOWSONLYMOVE 596 +#define STRINGID_SWAPPEDABILITIES 597 +#define STRINGID_PASTELVEILPROTECTED 598 +#define STRINGID_PASTELVEILENTERS 599 +#define STRINGID_BATTLERTYPECHANGEDTO 600 +#define STRINGID_BOTHCANNOLONGERESCAPE 601 +#define STRINGID_CANTESCAPEDUETOUSEDMOVE 602 +#define STRINGID_PKMNBECAMEWEAKERTOFIRE 603 +#define STRINGID_ABOUTTOUSEPOLTERGEIST 604 +#define STRINGID_CANTESCAPEBECAUSEOFCURRENTMOVE 605 +#define STRINGID_NEUTRALIZINGGASENTERS 606 +#define STRINGID_NEUTRALIZINGGASOVER 607 +#define STRINGID_TARGETTOOHEAVY 608 +#define STRINGID_PKMNTOOKTARGETHIGH 609 -#define BATTLESTRINGS_COUNT 609 +#define BATTLESTRINGS_COUNT 610 // The below IDs are all indexes into battle message tables, // used to determine which of a set of messages to print. @@ -661,6 +663,7 @@ #define B_MSG_TURN1_PHANTOM_FORCE 8 #define B_MSG_TURN1_GEOMANCY 9 #define B_MSG_TURN1_FREEZE_SHOCK 10 +#define B_MSG_TURN1_SKY_DROP 11 // gMoveWeatherChangeStringIds #define B_MSG_STARTED_RAIN 0 diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 400876cc9..f5d890140 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -793,7 +793,9 @@ static bool8 ShouldUseItem(void) u8 validMons = 0; bool8 shouldUse = FALSE; - if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_RIGHT) + // If teaming up with player and Pokemon is on the right, or Pokemon is currently held by Sky Drop + if ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_RIGHT) + || gStatuses3[gActiveBattler] & STATUS3_SKY_DROPPED) return FALSE; if (gStatuses3[gActiveBattler] & STATUS3_EMBARGO) diff --git a/src/battle_main.c b/src/battle_main.c index 5277ade1e..c22a3f613 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -2945,6 +2945,12 @@ static void BattleStartClearSetData(void) } gSwapDamageCategory = FALSE; // Photon Geyser, Shell Side Arm, Light That Burns the Sky + + // Clear skyDropTargets data + for (i = 0; i < 4; i++) + { + gBattleStruct->skyDropTargets[i] = 0xFF; + } } void SwitchInClearSetData(void) @@ -3081,6 +3087,56 @@ void FaintClearSetData(void) gBattleMons[i].status2 &= ~(STATUS2_INFATUATED_WITH(gActiveBattler)); if ((gBattleMons[i].status2 & STATUS2_WRAPPED) && *(gBattleStruct->wrappedBy + i) == gActiveBattler) gBattleMons[i].status2 &= ~(STATUS2_WRAPPED); + + // If the fainted mon was holding another Pokemon in Sky Drop, release the mon and clear Sky Drop data + if (gActiveBattler == gBattleStruct->skyDropTargets[0] && i == gBattleStruct->skyDropTargets[1]) + { + gBattleStruct->skyDropTargets[0] = 0xFF; + gBattleStruct->skyDropTargets[1] = 0xFF; + + gStatuses3[i] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR); + gSprites[gBattlerSpriteIds[i]].invisible = FALSE; + + // If the target was sky dropped in the middle of using Outrage/Petal Dance/Thrash, + // confuse them upon release and print "confused via fatigue" message and animation. + if (gBattleMons[i].status2 & STATUS2_LOCK_CONFUSE) + { + gBattleMons[i].status2 &= ~(STATUS2_LOCK_CONFUSE); + + // If the released mon can be confused, do so. + // Don't use CanBeConfused here, since it can cause issues in edge cases. + if (!(GetBattlerAbility(i) == ABILITY_OWN_TEMPO + || gBattleMons[i].status2 & STATUS2_CONFUSION + || IsBattlerTerrainAffected(i, STATUS_FIELD_MISTY_TERRAIN))) + { + gBattleMons[i].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); + gBattlerAttacker = i; + gBattlescriptCurrInstr = BattleScript_ThrashConfuses - 2; + } + } + } + else if (gActiveBattler == gBattleStruct->skyDropTargets[2] && i == gBattleStruct->skyDropTargets[3]) + { + gBattleStruct->skyDropTargets[2] = 0xFF; + gBattleStruct->skyDropTargets[3] = 0xFF; + + gStatuses3[i] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR); + gSprites[gBattlerSpriteIds[i]].invisible = FALSE; + + if (gBattleMons[i].status2 & STATUS2_LOCK_CONFUSE) + { + gBattleMons[i].status2 &= ~(STATUS2_LOCK_CONFUSE); + gEffectBattler = i; + if (!(GetBattlerAbility(i) == ABILITY_OWN_TEMPO + || gBattleMons[i].status2 & STATUS2_CONFUSION + || IsBattlerTerrainAffected(i, STATUS_FIELD_MISTY_TERRAIN))) + { + gBattleMons[i].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); + gBattlerAttacker = i; + gBattlescriptCurrInstr = BattleScript_ThrashConfuses - 2; + } + } + } } gActionSelectionCursor[gActiveBattler] = 0; @@ -3160,6 +3216,12 @@ void FaintClearSetData(void) UndoFormChange(gBattlerPartyIndexes[gActiveBattler], GET_BATTLER_SIDE(gActiveBattler), FALSE); if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) UndoMegaEvolution(gBattlerPartyIndexes[gActiveBattler]); + + // If the fainted Pokemon was being held by Sky Drop, clear their Sky Drop data. + if (gActiveBattler == gBattleStruct->skyDropTargets[1]) + gBattleStruct->skyDropTargets[1] = 0xFF; + else if (gActiveBattler == gBattleStruct->skyDropTargets[3]) + gBattleStruct->skyDropTargets[3] = 0xFF; } static void DoBattleIntro(void) @@ -3897,10 +3959,12 @@ static void HandleTurnActionSelectionState(void) } break; case B_ACTION_USE_ITEM: - if (gBattleTypeFlags & (BATTLE_TYPE_LINK + if ((gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_FRONTIER_NO_PYRAMID | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_RECORDED_LINK)) + // Or if currently held by Sky Drop + || gStatuses3[gActiveBattler] & STATUS3_SKY_DROPPED) { RecordedBattle_ClearBattlerAction(gActiveBattler, 1); gSelectionBattleScripts[gActiveBattler] = BattleScript_ActionSelectionItemsCantBeUsed; diff --git a/src/battle_message.c b/src/battle_message.c index d64d02f90..9174d7c3b 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -431,6 +431,8 @@ static const u8 sText_ExclamationMark4[] = _("!"); static const u8 sText_ExclamationMark5[] = _("!"); static const u8 sText_Accuracy[] = _("accuracy"); static const u8 sText_Evasiveness[] = _("evasiveness"); +static const u8 sText_PkmnTookTargetHigh[] = _("{B_ATK_NAME_WITH_PREFIX} took {B_DEF_NAME_WITH_PREFIX}\ninto the air!"); +static const u8 sText_TargetTooHeavy[] = _("But the target\nwas too heavy!"); const u8 * const gStatNamesTable[NUM_BATTLE_STATS] = { @@ -1334,6 +1336,8 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_PKMNBECAMEWEAKERTOFIRE - 12] = sText_PkmnBecameWeakerToFire, [STRINGID_ABOUTTOUSEPOLTERGEIST - 12] = sText_PkmnAboutToBeAttackedByItsItem, [STRINGID_CANTESCAPEBECAUSEOFCURRENTMOVE - 12] = sText_CantEscapeBecauseOfCurrentMove, + [STRINGID_PKMNTOOKTARGETHIGH - 12] = sText_PkmnTookTargetHigh, + [STRINGID_TARGETTOOHEAVY - 12] = sText_TargetTooHeavy, }; const u16 gMentalHerbCureStringIds[] = @@ -1548,6 +1552,7 @@ const u16 gFirstTurnOfTwoStringIds[] = [B_MSG_TURN1_PHANTOM_FORCE] = STRINGID_VANISHEDINSTANTLY, [B_MSG_TURN1_GEOMANCY] = STRINGID_PKNMABSORBINGPOWER, [B_MSG_TURN1_FREEZE_SHOCK] = STRINGID_CLOAKEDINAFREEZINGLIGHT, + [B_MSG_TURN1_SKY_DROP] = STRINGID_PKMNTOOKTARGETHIGH, }; // Index copied from move's index in sTrappingMoves diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 7923a6739..1b08f86a8 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1089,7 +1089,7 @@ static const u16 sMoveEffectsForbiddenToInstruct[] = EFFECT_SEMI_INVULNERABLE, //EFFECT_SHELL_TRAP, EFFECT_SKETCH, - //EFFECT_SKY_DROP, + EFFECT_SKY_DROP, EFFECT_SKULL_BASH, EFFECT_SLEEP_TALK, EFFECT_SOLARBEAM, @@ -1609,13 +1609,15 @@ static bool32 AccuracyCalcHelper(u16 move) JumpIfMoveFailed(7, move); return TRUE; } - else if (GetBattlerAbility(gBattlerAttacker) == ABILITY_NO_GUARD) + // If the attacker has the ability No Guard and they aren't targeting a Pokemon involved in a Sky Drop with the move Sky Drop, move hits. + else if (GetBattlerAbility(gBattlerAttacker) == ABILITY_NO_GUARD && (move != MOVE_SKY_DROP || (gBattlerTarget != gBattleStruct->skyDropTargets[0] && gBattlerTarget != gBattleStruct->skyDropTargets[1] && gBattlerTarget != gBattleStruct->skyDropTargets[2] && gBattlerTarget != gBattleStruct->skyDropTargets[3]))) { if (!JumpIfMoveFailed(7, move)) RecordAbilityBattle(gBattlerAttacker, ABILITY_NO_GUARD); return TRUE; } - else if (GetBattlerAbility(gBattlerTarget) == ABILITY_NO_GUARD) + // If the target has the ability No Guard and they aren't involved in a Sky Drop or the current move isn't Sky Drop, move hits. + else if (GetBattlerAbility(gBattlerTarget) == ABILITY_NO_GUARD && (move != MOVE_SKY_DROP || (gBattlerTarget != gBattleStruct->skyDropTargets[0] && gBattlerTarget != gBattleStruct->skyDropTargets[1] && gBattlerTarget != gBattleStruct->skyDropTargets[2] && gBattlerTarget != gBattleStruct->skyDropTargets[3]))) { if (!JumpIfMoveFailed(7, move)) RecordAbilityBattle(gBattlerTarget, ABILITY_NO_GUARD); @@ -1623,8 +1625,7 @@ static bool32 AccuracyCalcHelper(u16 move) } if ((gStatuses3[gBattlerTarget] & STATUS3_PHANTOM_FORCE) - || (!(gBattleMoves[move].flags & FLAG_DMG_IN_AIR) && gStatuses3[gBattlerTarget] & STATUS3_ON_AIR) - || (!(gBattleMoves[move].flags & FLAG_DMG_2X_IN_AIR) && gStatuses3[gBattlerTarget] & STATUS3_ON_AIR) + || (!(gBattleMoves[move].flags & FLAG_DMG_IN_AIR || gBattleMoves[move].flags & FLAG_DMG_2X_IN_AIR) && gStatuses3[gBattlerTarget] & STATUS3_ON_AIR) || (!(gBattleMoves[move].flags & FLAG_DMG_UNDERGROUND) && gStatuses3[gBattlerTarget] & STATUS3_UNDERGROUND) || (!(gBattleMoves[move].flags & FLAG_DMG_UNDERWATER) && gStatuses3[gBattlerTarget] & STATUS3_UNDERWATER)) { @@ -2929,9 +2930,20 @@ void SetMoveEffect(bool32 primary, u32 certain) else { gBattleMons[gEffectBattler].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); // 2-5 turns - - BattleScriptPush(gBattlescriptCurrInstr + 1); - gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleScripting.moveEffect]; + + // If the confusion is activating due to being released from Sky Drop, go to "confused due to fatigue" script. + // Otherwise, do normal confusion script. + if(gCurrentMove == MOVE_SKY_DROP) + { + gBattleMons[gEffectBattler].status2 &= ~(STATUS2_LOCK_CONFUSE); + gBattlerAttacker = gEffectBattler; + gBattlescriptCurrInstr = BattleScript_ThrashConfuses; + } + else + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleScripting.moveEffect]; + } } break; case MOVE_EFFECT_FLINCH: @@ -5141,6 +5153,28 @@ static void Cmd_moveend(void) } gBattleScripting.moveendState++; break; + case MOVEEND_SKY_DROP_CONFUSE: // If a Pokemon was released from Sky Drop and was in LOCK_CONFUSE, go to "confused due to fatigue scripts and clear Sky Drop data. + for (i = 0; i < gBattlersCount; i++) + { + if (gBattleStruct->skyDropTargets[0] == 0xFE - i) + { + gBattlerAttacker = gBattleStruct->skyDropTargets[1]; + gBattlescriptCurrInstr = BattleScript_ThrashConfuses; + gBattleStruct->skyDropTargets[0] = 0xFF; + gBattleStruct->skyDropTargets[1] = 0xFF; + return; + } + else if (gBattleStruct->skyDropTargets[2] == 0xFE - i) + { + gBattlerAttacker = gBattleStruct->skyDropTargets[3]; + gBattlescriptCurrInstr = BattleScript_ThrashConfuses; + gBattleStruct->skyDropTargets[2] = 0xFF; + gBattleStruct->skyDropTargets[3] = 0xFF; + return; + } + } + gBattleScripting.moveendState++; + break; case MOVEEND_UPDATE_LAST_MOVES: if (gMoveResultFlags & (MOVE_RESULT_FAILED | MOVE_RESULT_DOESNT_AFFECT_FOE)) gBattleStruct->lastMoveFailed |= gBitTable[gBattlerAttacker]; @@ -7680,7 +7714,8 @@ static void Cmd_various(void) } return; case VARIOUS_GRAVITY_ON_AIRBORNE_MONS: - if (gStatuses3[gActiveBattler] & STATUS3_ON_AIR) + // Cancel all multiturn moves of IN_AIR Pokemon except those being targeted by Sky Drop. + if (gStatuses3[gActiveBattler] & STATUS3_ON_AIR && !(gActiveBattler == gBattleStruct->skyDropTargets[1] || gActiveBattler == gBattleStruct->skyDropTargets[3])) CancelMultiTurnMoves(gActiveBattler); gStatuses3[gActiveBattler] &= ~(STATUS3_MAGNET_RISE | STATUS3_TELEKINESIS | STATUS3_ON_AIR); @@ -8892,7 +8927,7 @@ static void Cmd_various(void) MarkBattlerForControllerExec(gActiveBattler); } - if (gBattleMons[gActiveBattler].pp[i] == 0) + if (gBattleMons[gActiveBattler].pp[i] == 0 && !(gActiveBattler == gBattleStruct->skyDropTargets[0] || gActiveBattler == gBattleStruct->skyDropTargets[2])) CancelMultiTurnMoves(gActiveBattler); gBattlescriptCurrInstr += 7; // continue @@ -8959,6 +8994,101 @@ static void Cmd_various(void) gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY; // remove the terrain TryToRevertMimicry(); // restore the types of Pokémon with Mimicry break; + case VARIOUS_JUMP_IF_UNDER_200: + // If the Pokemon is less than 200 kg, or weighing less than 441 lbs, then Sky Drop will work. Otherwise, it will fail. + if (GetPokedexHeightWeight(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), 1) < 441) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); + else + gBattlescriptCurrInstr += 7; + return; + case VARIOUS_SET_SKY_DROP: + gStatuses3[gBattlerTarget] |= (STATUS3_SKY_DROPPED | STATUS3_ON_AIR); + /* skyDropTargets holds the information of who is the attacker and the target of Sky Drop. + This is needed in the case that multiple Pokemon use Sky Drop in the same turn or if + the target of a Sky Drop faints while in the air.*/ + if (gBattleStruct->skyDropTargets[0] == 0xFF) + { + gBattleStruct->skyDropTargets[0] = gBattlerAttacker; + gBattleStruct->skyDropTargets[1] = gBattlerTarget; + } + else + { + gBattleStruct->skyDropTargets[2] = gBattlerAttacker; + gBattleStruct->skyDropTargets[3] = gBattlerTarget; + } + + // End any multiturn effects caused by the target + gBattleMons[gBattlerTarget].status2 &= ~(STATUS2_MULTIPLETURNS); + gBattleMons[gBattlerTarget].status2 &= ~(STATUS2_UPROAR); + gBattleMons[gBattlerTarget].status2 &= ~(STATUS2_BIDE); + gDisableStructs[gBattlerTarget].rolloutTimer = 0; + gDisableStructs[gBattlerTarget].furyCutterCounter = 0; + break; + case VARIOUS_CLEAR_SKY_DROP: + if (gBattleStruct->skyDropTargets[0] == gBattlerAttacker) + { + // Check to see if the initial target of this Sky Drop fainted before the 2nd turn of Sky Drop. + // If so, make the move fail. If not, clear all of the statuses and continue the move. + if (gBattleStruct->skyDropTargets[1] == 0xFF) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); + else + { + gStatuses3[gBattlerTarget] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR); + gBattlescriptCurrInstr += 7; + } + + // Clear skyDropTargets data + gBattleStruct->skyDropTargets[0] = 0xFF; + gBattleStruct->skyDropTargets[1] = 0xFF; + } + else + { + if (gBattleStruct->skyDropTargets[3] == 0xFF) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); + else + { + gStatuses3[gBattlerTarget] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR); + gBattlescriptCurrInstr += 7; + } + + gBattleStruct->skyDropTargets[2] = 0xFF; + gBattleStruct->skyDropTargets[3] = 0xFF; + } + + // Confuse target if they were in the middle of Petal Dance/Outrage/Thrash when targeted. + if (gBattleMons[gBattlerTarget].status2 & STATUS2_LOCK_CONFUSE) + gBattleScripting.moveEffect = (MOVE_EFFECT_CONFUSION | MOVE_EFFECT_CERTAIN); + return; + case VARIOUS_SKY_DROP_YAWN: // If the mon that's sleeping due to Yawn was holding a Pokemon in Sky Drop, release the target and clear Sky Drop data. + if (gEffectBattler == gBattleStruct->skyDropTargets[0]) + { + gEffectBattler = gBattleStruct->skyDropTargets[1]; + gBattleStruct->skyDropTargets[0] = 0xFF; + gBattleStruct->skyDropTargets[1] = 0xFF; + gBattleMons[gEffectBattler].status2 &= ~(STATUS2_LOCK_CONFUSE); + if (CanBeConfused(gEffectBattler)) + { + gBattlerAttacker = gEffectBattler; + gBattleMons[gBattlerTarget].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); + gBattlescriptCurrInstr = BattleScript_ThrashConfuses; + return; + } + } + else if (gEffectBattler == gBattleStruct->skyDropTargets[2]) + { + gEffectBattler = gBattleStruct->skyDropTargets[3]; + gBattleStruct->skyDropTargets[2] = 0xFF; + gBattleStruct->skyDropTargets[3] = 0xFF; + gBattleMons[gEffectBattler].status2 &= ~(STATUS2_LOCK_CONFUSE); + if (CanBeConfused(gEffectBattler)) + { + gBattlerAttacker = gEffectBattler; + gBattleMons[gBattlerTarget].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); + gBattlescriptCurrInstr = BattleScript_ThrashConfuses; + return; + } + } + break; case VARIOUS_JUMP_IF_PRANKSTER_BLOCKED: if (BlocksPrankster(gCurrentMove, gBattlerAttacker, gActiveBattler, TRUE)) gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); @@ -11351,7 +11481,8 @@ static void Cmd_tryspiteppreduce(void) gBattlescriptCurrInstr += 5; - if (gBattleMons[gBattlerTarget].pp[i] == 0) + // Don't cut off Sky Drop if pp is brought to zero. + if (gBattleMons[gBattlerTarget].pp[i] == 0 && !(gBattlerTarget == gBattleStruct->skyDropTargets[0] || gBattlerTarget == gBattleStruct->skyDropTargets[2])) CancelMultiTurnMoves(gBattlerTarget); } else @@ -11997,6 +12128,7 @@ static void Cmd_setsemiinvulnerablebit(void) { case MOVE_FLY: case MOVE_BOUNCE: + case MOVE_SKY_DROP: gStatuses3[gBattlerAttacker] |= STATUS3_ON_AIR; break; case MOVE_DIG: diff --git a/src/battle_util.c b/src/battle_util.c index cc619decf..7cd7254d7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -224,6 +224,7 @@ bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move) if (gSideTimers[defSide].followmeTimer == 0 || gBattleMons[gSideTimers[defSide].followmeTarget].hp == 0 || gBattleMoves[move].effect == EFFECT_SNIPE_SHOT + || gBattleMoves[move].effect == EFFECT_SKY_DROP || ability == ABILITY_PROPELLER_TAIL || ability == ABILITY_STALWART) return FALSE; @@ -1420,12 +1421,123 @@ void MarkBattlerReceivedLinkData(u8 battlerId) void CancelMultiTurnMoves(u8 battler) { + u8 i; 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); + // Don't clear battler's semi-invulnerable bits if they are held by Sky Drop. + if (gBattleStruct->skyDropTargets[1] != battler && gBattleStruct->skyDropTargets[3] != battler) + gStatuses3[battler] &= ~(STATUS3_SEMI_INVULNERABLE); + + // Check to see if this Pokemon was in the middle of using Sky Drop. If so, release the target. + if (gBattleStruct->skyDropTargets[0] == battler) + { + // Sets skyDropTargets[1] to be the battler id for the target + for(i = 0; i < MAX_BATTLERS_COUNT; i++) + { + if(gBattleStruct->skyDropTargets[1] == i) + { + // Clears sky dropped and on_air statuses + gStatuses3[i] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR); + + // Makes both attacker and target's sprites visible + gSprites[gBattlerSpriteIds[battler]].invisible = FALSE; + gSprites[gBattlerSpriteIds[i]].invisible = FALSE; + + // If target was sky dropped in the middle of Outrage/Thrash/Petal Dance, + // confuse them upon release and display "confused by fatigue" message & animation. + // Don't do this if this CancelMultiTurnMoves is caused by falling asleep via Yawn. + if (gBattleMons[i].status2 & STATUS2_LOCK_CONFUSE && gBattleStruct->turnEffectsTracker != 24) + { + gBattleMons[i].status2 &= ~(STATUS2_LOCK_CONFUSE); + + // If the target can be confused, confuse them. + // Don't use CanBeConfused, can cause issues in edge cases. + if (!(GetBattlerAbility(i) == ABILITY_OWN_TEMPO + || gBattleMons[i].status2 & STATUS2_CONFUSION + || IsBattlerTerrainAffected(i, STATUS_FIELD_MISTY_TERRAIN))) + { + gBattleMons[i].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); + + // If this CancelMultiTurnMoves is occuring due to attackcanceller or VARIOUS_GRAVITY_ON_AIRBORNE_MONS + if (gBattlescriptCurrInstr[0] == 0x0 || (gBattlescriptCurrInstr[0] == 0x76 && gBattlescriptCurrInstr[2] == 76)) + gBattleStruct->skyDropTargets[0] = 0xFE - battler; + + // If this CancelMultiTurnMoves is occuring due to cancelmultiturnmoves script + else if (gBattlescriptCurrInstr[0] == 0x76 && gBattlescriptCurrInstr[2] == 0) + { + gBattlerAttacker = i; + gBattlescriptCurrInstr = BattleScript_ThrashConfuses - 3; + } + + // If this CancelMultiTurnMoves is occuring due to receiving Sleep/Freeze status + else if (gBattleScripting.moveEffect <= PRIMARY_STATUS_MOVE_EFFECT) + { + gBattlerAttacker = i; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_ThrashConfuses - 1; + } + } + + } + break; + } + } + + // Clear skyDropTargets data, unless this CancelMultiTurnMoves is caused by Yawn, attackcanceler, or VARIOUS_GRAVITY_ON_AIRBORNE_MONS + if (!(gBattleMons[gBattleStruct->skyDropTargets[1]].status2 & STATUS2_LOCK_CONFUSE) && gBattleStruct->skyDropTargets[0] < 4) + { + gBattleStruct->skyDropTargets[0] = 0xFF; + gBattleStruct->skyDropTargets[1] = 0xFF; + } + } + else if (gBattleStruct->skyDropTargets[2] == battler) + { + for(i = 0; i < MAX_BATTLERS_COUNT; i++) + { + if(gBattleStruct->skyDropTargets[3] == i) + { + gStatuses3[i] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR); + + gSprites[gBattlerSpriteIds[battler]].invisible = FALSE; + gSprites[gBattlerSpriteIds[i]].invisible = FALSE; + + if (gBattleMons[i].status2 & STATUS2_LOCK_CONFUSE && gBattleStruct->turnEffectsTracker != 24) + { + gBattleMons[i].status2 &= ~(STATUS2_LOCK_CONFUSE); + if (!(GetBattlerAbility(i) == ABILITY_OWN_TEMPO + || gBattleMons[i].status2 & STATUS2_CONFUSION + || IsBattlerTerrainAffected(i, STATUS_FIELD_MISTY_TERRAIN))) + { + gBattleMons[i].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); + + if (gBattlescriptCurrInstr[0] == 0x0 || (gBattlescriptCurrInstr[0] == 0x76 && gBattlescriptCurrInstr[2] == 76)) + gBattleStruct->skyDropTargets[2] = 0xFE - battler; + else if (gBattlescriptCurrInstr[0] == 0x76 && gBattlescriptCurrInstr[2] == 0) + { + gBattlerAttacker = i; + gBattlescriptCurrInstr = BattleScript_ThrashConfuses - 3; + } + else if (gBattleScripting.moveEffect <= PRIMARY_STATUS_MOVE_EFFECT) + { + gBattlerAttacker = i; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_ThrashConfuses - 1; + } + } + } + break; + } + } + + if (!(gBattleMons[gBattleStruct->skyDropTargets[3]].status2 & STATUS2_LOCK_CONFUSE) && gBattleStruct->skyDropTargets[2] < 4) + { + gBattleStruct->skyDropTargets[2] = 0xFF; + gBattleStruct->skyDropTargets[3] = 0xFF; + } + } gDisableStructs[battler].rolloutTimer = 0; gDisableStructs[battler].furyCutterCounter = 0; @@ -2771,7 +2883,8 @@ u8 DoBattlerEndTurnEffects(void) gBattleStruct->turnEffectsTracker++; break; case ENDTURN_THRASH: // thrash - if (gBattleMons[gActiveBattler].status2 & STATUS2_LOCK_CONFUSE) + // Don't decrement STATUS2_LOCK_CONFUSE if the target is held by Sky Drop + if (gBattleMons[gActiveBattler].status2 & STATUS2_LOCK_CONFUSE && !(gStatuses3[gActiveBattler] & STATUS3_SKY_DROPPED)) { gBattleMons[gActiveBattler].status2 -= STATUS2_LOCK_CONFUSE_TURN(1); if (WasUnableToUseMove(gActiveBattler)) @@ -3211,6 +3324,7 @@ void TryClearRageAndFuryCutter(void) enum { CANCELLER_FLAGS, + CANCELLER_SKY_DROP, CANCELLER_ASLEEP, CANCELLER_FROZEN, CANCELLER_TRUANT, @@ -3247,6 +3361,16 @@ u8 AtkCanceller_UnableToUseMove(void) gStatuses3[gBattlerAttacker] &= ~(STATUS3_GRUDGE); gBattleStruct->atkCancellerTracker++; break; + case CANCELLER_SKY_DROP: + // If Pokemon is being held in Sky Drop + if (gStatuses3[gBattlerAttacker] & STATUS3_SKY_DROPPED) + { + gBattlescriptCurrInstr = BattleScript_MoveEnd; + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + effect = 1; + } + gBattleStruct->atkCancellerTracker++; + break; case CANCELLER_ASLEEP: // check being asleep if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) { @@ -3438,7 +3562,7 @@ u8 AtkCanceller_UnableToUseMove(void) if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && (Random() % 4) == 0) { gProtectStructs[gBattlerAttacker].prlzImmobility = TRUE; - // This is removed in Emerald for some reason + // This is removed in Emerald because it is called in BattleScript_MoveUsedIsParalyzed. //CancelMultiTurnMoves(gBattlerAttacker); gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed; gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; @@ -4886,7 +5010,9 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) && (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) && !(gBattleTypeFlags & BATTLE_TYPE_ARENA) - && CountUsablePartyMons(battler) > 0) + && CountUsablePartyMons(battler) > 0 + // Not currently held by Sky Drop + && !(gStatuses3[battler] & STATUS3_SKY_DROPPED)) { gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_EMERGENCY_EXIT; effect++; @@ -5748,6 +5874,8 @@ bool32 CanBattlerEscape(u32 battlerId) // no ability check return FALSE; else if (gFieldStatuses & STATUS_FIELD_FAIRY_LOCK) return FALSE; + else if (gStatuses3[battlerId] & STATUS3_SKY_DROPPED) + return FALSE; else return TRUE; } @@ -9128,6 +9256,10 @@ bool32 CanMegaEvolve(u8 battlerId) && (mega->alreadyEvolved[partnerPosition] || (mega->toEvolve & gBitTable[BATTLE_PARTNER(battlerId)]))) return FALSE; } + + // Check if mon is currently held by Sky Drop + if (gStatuses3[battlerId] & STATUS3_SKY_DROPPED) + return FALSE; // Gets mon data. if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT) diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index a49cf5fd8..adcc40b82 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -8009,7 +8009,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_SKY_DROP] = { - .effect = EFFECT_PLACEHOLDER, // Needs a custom move effect + .effect = EFFECT_SKY_DROP, .power = 60, .type = TYPE_FLYING, .accuracy = 100,