diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 0aeb7309c..b43433205 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1968,6 +1968,14 @@ various \battler, VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM .endm + .macro setbeakblast battler:req + various \battler, VARIOUS_SET_BEAK_BLAST + .endm + + .macro swapsidestatuses + various BS_ATTACKER, VARIOUS_SWAP_SIDE_STATUSES + .endm + @ helpful macros .macro setstatchanger stat:req, stages:req, down:req setbyte sSTATCHANGER, \stat | \stages << 3 | \down << 7 diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 157e638c9..d76aff05a 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -829,6 +829,8 @@ gBattleAnims_General:: .4byte General_StrongWinds @ B_ANIM_STRONG_WINDS .4byte General_PrimalReversion @ B_ANIM_PRIMAL_REVERSION .4byte General_AquaRingHeal @ B_ANIM_AQUA_RING_HEAL + .4byte General_BeakBlastSetUp @ B_ANIM_BEAK_BLAST_SETUP + .4byte General_ShellTrapSetUp @ B_ANIM_SHELL_TRAP_SETUP .align 2 gBattleAnims_Special:: @@ -11735,7 +11737,7 @@ Move_INSTRUCT:: blendoff end -Move_BEAK_BLAST:: +General_BeakBlastSetUp: loadspritegfx ANIM_TAG_SMALL_EMBER @Fire playsewithpan SE_M_DRAGON_RAGE, SOUND_PAN_ATTACKER delay 0x3 @@ -11743,7 +11745,7 @@ Move_BEAK_BLAST:: launchtemplate gFireSpiralOutwardSpriteTemplate 0x3 0x4 0x0 0x0 0x38 0x0 waitforvisualfinish end -BeakBlastUnleash: +Move_BEAK_BLAST:: loadspritegfx ANIM_TAG_IMPACT launchtask AnimTask_BlendBattleAnimPal 0xA 0x5 ANIM_PAL_ATK 0x2 0x0 0x9 0x1F waitforvisualfinish @@ -11892,8 +11894,7 @@ Move_AURORA_VEIL:: blendoff end -Move_SHELL_TRAP:: -ShellTrapChargeUp: +General_ShellTrapSetUp: loadspritegfx ANIM_TAG_SMALL_EMBER loadspritegfx ANIM_TAG_IMPACT monbg ANIM_TARGET @@ -11910,6 +11911,7 @@ ShellTrapChargeUp: clearmonbg ANIM_TARGET blendoff end +Move_SHELL_TRAP:: ShellTrapUnleash: loadspritegfx ANIM_TAG_IMPACT @pound loadspritegfx ANIM_TAG_SMALL_RED_EYE @red @@ -13605,8 +13607,8 @@ Move_DRUM_BEATING:: blendoff end -Move_SNAP_TRAP:: - end @ to do: +Move_SNAP_TRAP:: @ placeholder + goto Move_BITE Move_PYRO_BALL:: loadspritegfx ANIM_TAG_FLAT_ROCK @@ -24214,6 +24216,7 @@ General_TurnTrap: jumpargeq 0, TRAP_ANIM_SAND_TOMB, Status_SandTomb jumpargeq 0, TRAP_ANIM_MAGMA_STORM, Status_MagmaStorm jumpargeq 0, TRAP_ANIM_INFESTATION, Status_Infestation + jumpargeq 0, TRAP_ANIM_SNAP_TRAP, Status_Snap_Trap goto Status_BindWrap Status_BindWrap: loadspritegfx ANIM_TAG_TENDRILS @@ -24300,6 +24303,9 @@ Status_Clamp: waitforvisualfinish end +Status_Snap_Trap: @ placeholder + goto Move_BITE + Status_SandTomb: loadspritegfx ANIM_TAG_MUD_SAND createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 0, 4, 2, 0, 7, RGB(19, 17, 0) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 119635568..6574e26ab 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -404,6 +404,119 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectClangorousSoul @ EFFECT_CLANGOROUS_SOUL .4byte BattleScript_EffectHit @ EFFECT_BOLT_BEAK .4byte BattleScript_EffectSkyDrop @ EFFECT_SKY_DROP + .4byte BattleScript_EffectHit @ EFFECT_EXPANDING_FORCE + .4byte BattleScript_EffectScaleShot @ EFFECT_SCALE_SHOT + .4byte BattleScript_EffectMeteorBeam @ EFFECT_METEOR_BEAM + .4byte BattleScript_EffectHit @ EFFECT_RISING_VOLTAGE + .4byte BattleScript_EffectHit @ EFFECT_BEAK_BLAST + .4byte BattleScript_EffectCourtChange @ EFFECT_COURT_CHANGE + +BattleScript_EffectCourtChange:: + attackcanceler + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + attackstring + ppreduce + swapsidestatuses + attackanimation + waitanimation + printstring STRINGID_COURTCHANGE + waitmessage 0x40 + goto BattleScript_MoveEnd + +BattleScript_BeakBlastSetUp:: + setbeakblast BS_ATTACKER + printstring STRINGID_EMPTYSTRING3 + waitmessage 0x1 + playanimation BS_ATTACKER, B_ANIM_BEAK_BLAST_SETUP, NULL + printstring STRINGID_HEATUPBEAK + waitmessage 0x40 + end2 + +BattleScript_BeakBlastBurn:: + setbyte cMULTISTRING_CHOOSER, 0 + copybyte gEffectBattler, gBattlerAttacker + call BattleScript_MoveEffectBurn + return + +BattleScript_EffectMeteorBeam:: + @ DecideTurn + jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn + jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn + setbyte sTWOTURN_STRINGID, B_MSG_TURN1_METEOR_BEAM + call BattleScript_FirstChargingTurnMeteorBeam + jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd + call BattleScript_PowerHerbActivation + goto BattleScript_TwoTurnMovesSecondTurn + +BattleScript_FirstChargingTurnMeteorBeam:: + attackcanceler + printstring STRINGID_EMPTYSTRING3 + ppreduce + attackanimation + waitanimation + orword gHitMarker, HITMARKER_CHARGING + setmoveeffect MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER + seteffectprimary + copybyte cMULTISTRING_CHOOSER, sTWOTURN_STRINGID + printfromtable gFirstTurnOfTwoStringIds + waitmessage 0x40 + setmoveeffect MOVE_EFFECT_SP_ATK_PLUS_1 | MOVE_EFFECT_AFFECTS_USER + seteffectsecondary + return + +BattleScript_EffectScaleShot:: + attackcanceler + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + attackstring + ppreduce + setmultihitcounter 0x0 + initmultihitstring + sethword sMULTIHIT_EFFECT, 0x0 +BattleScript_ScaleShotLoop:: + jumpifhasnohp BS_ATTACKER, BattleScript_ScaleShotEnd + jumpifhasnohp BS_TARGET, BattleScript_ScaleShotPrintStrings + jumpifhalfword CMP_EQUAL, gChosenMove, MOVE_SLEEP_TALK, BattleScript_ScaleShotDoMultiHit + jumpifstatus BS_ATTACKER, STATUS1_SLEEP, BattleScript_ScaleShotPrintStrings +BattleScript_ScaleShotDoMultiHit:: + movevaluescleanup + copyhword sMOVE_EFFECT, sMULTIHIT_EFFECT + critcalc + damagecalc + jumpifmovehadnoeffect BattleScript_ScaleShotMultiHitNoMoreHits + adjustdamage + attackanimation + waitanimation + effectivenesssound + hitanimation BS_TARGET + waitstate + healthbarupdate BS_TARGET + datahpupdate BS_TARGET + critmessage + waitmessage 0x40 + multihitresultmessage + printstring STRINGID_EMPTYSTRING3 + waitmessage 0x1 + addbyte sMULTIHIT_STRING + 4, 0x1 + moveendto MOVEEND_NEXT_TARGET + jumpifbyte CMP_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_FOE_ENDURED, BattleScript_ScaleShotPrintStrings + decrementmultihit BattleScript_ScaleShotLoop + goto BattleScript_ScaleShotPrintStrings +BattleScript_ScaleShotMultiHitNoMoreHits:: + pause 0x20 +BattleScript_ScaleShotPrintStrings:: + resultmessage + waitmessage 0x40 + jumpifmovehadnoeffect BattleScript_ScaleShotEnd + copyarray gBattleTextBuff1, sMULTIHIT_STRING, 0x6 + printstring STRINGID_HITXTIMES + waitmessage 0x40 +BattleScript_ScaleShotEnd:: + setmoveeffect MOVE_EFFECT_SCALE_SHOT | MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN + seteffectwithchance + tryfaintmon BS_TARGET + moveendcase MOVEEND_SYNCHRONIZE_TARGET + moveendfrom MOVEEND_STATUS_IMMUNITY_ABILITIES + end BattleScript_EffectSkyDrop: jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_SkyDropTurn2 @@ -6760,6 +6873,7 @@ BattleScript_ToxicSpikesPoisoned:: BattleScript_StickyWebOnSwitchIn:: savetarget copybyte gBattlerTarget, sBATTLER + setbyte sSTICKY_WEB_STAT_DROP, 1 printstring STRINGID_STICKYWEBSWITCHIN waitmessage B_WAIT_TIME_LONG jumpifability BS_TARGET, ABILITY_MIRROR_ARMOR, BattleScript_MirrorArmorReflectStickyWeb @@ -7168,6 +7282,26 @@ BattleScript_DefSpDefDownTrySpDef:: BattleScript_DefSpDefDownRet:: return +BattleScript_DefDownSpeedUp:: + jumpifstat BS_ATTACKER, CMP_GREATER_THAN, STAT_DEF, MIN_STAT_STAGE, BattleScript_DefDownSpeedUpTryDef + jumpifstat BS_ATTACKER, CMP_EQUAL, STAT_SPEED, MAX_STAT_STAGE, BattleScript_DefDownSpeedUpRet +BattleScript_DefDownSpeedUpTryDef:: + playstatchangeanimation BS_ATTACKER, BIT_DEF, STAT_CHANGE_NEGATIVE | STAT_CHANGE_CANT_PREVENT + setstatchanger STAT_DEF, 1, TRUE + statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_BUFF_ALLOW_PTR | MOVE_EFFECT_CERTAIN, BattleScript_DefDownSpeedUpTrySpeed + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_DefDownSpeedUpTrySpeed + printfromtable gStatDownStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_DefDownSpeedUpTrySpeed: + playstatchangeanimation BS_ATTACKER, BIT_SPEED, 0 + setstatchanger STAT_SPEED, 1, FALSE + statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_BUFF_ALLOW_PTR | MOVE_EFFECT_CERTAIN, BattleScript_DefDownSpeedUpRet + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_DefDownSpeedUpRet + printfromtable gStatUpStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_DefDownSpeedUpRet:: + return + BattleScript_KnockedOff:: playanimation BS_TARGET, B_ANIM_ITEM_KNOCKOFF printstring STRINGID_PKMNKNOCKEDOFF diff --git a/include/battle.h b/include/battle.h index 47824792e..4068454bf 100644 --- a/include/battle.h +++ b/include/battle.h @@ -141,10 +141,12 @@ struct ProtectStruct u32 usedMicleBerry:1; u32 usedCustapBerry:1; // also quick claw u32 touchedProtectLike:1; - u32 disableEjectPack:1; - u32 statFell:1; - u32 pranksterElevated:1; - u32 quickDraw:1; + // End of 32-bit bitfield + u16 disableEjectPack:1; + u16 statFell:1; + u16 pranksterElevated:1; + u16 quickDraw:1; + u16 beakBlastCharge:1; u32 physicalDmg; u32 specialDmg; u8 physicalBattlerId; @@ -194,19 +196,21 @@ struct SideTimer u8 mistBattlerId; u8 safeguardTimer; u8 safeguardBattlerId; - u8 followmeTimer; - u8 followmeTarget:3; - u8 followmePowder:1; // Rage powder, does not affect grass type pokemon. u8 spikesAmount; u8 toxicSpikesAmount; u8 stealthRockAmount; u8 stickyWebAmount; + u8 stickyWebBattlerSide; // Used for Court Change u8 auroraVeilTimer; u8 auroraVeilBattlerId; u8 tailwindTimer; u8 tailwindBattlerId; u8 luckyChantTimer; u8 luckyChantBattlerId; + // Timers below this point are not swapped by Court Change + u8 followmeTimer; + u8 followmeTarget:3; + u8 followmePowder:1; // Rage powder, does not affect grass type pokemon. u8 retaliateTimer; }; @@ -699,6 +703,7 @@ struct BattleScripting u16 abilityPopupOverwrite; u8 switchCase; // Special switching conditions, eg. red card u8 overrideBerryRequirements; + u8 stickyWebStatDrop; // To prevent Defiant activating on a Court Change'd Sticky Web }; struct BattleSpriteInfo diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 5ccf68c58..0b2afebae 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -60,6 +60,7 @@ bool32 IsAbilityOfRating(u16 ability, s8 rating); s8 GetAbilityRating(u16 ability); bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability); bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u16 move); +u32 AI_GetBattlerMoveTargetType(u8 battlerId, u16 move); // stat stage checks bool32 AnyStatIsRaised(u8 battlerId); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 6f8b0d424..740d27148 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -416,5 +416,8 @@ extern const u8 BattleScript_BothCanNoLongerEscape[]; extern const u8 BattleScript_OctolockEndTurn[]; extern const u8 BattleScript_NeutralizingGasExits[]; extern const u8 BattleScript_MagicianActivates[]; +extern const u8 BattleScript_BeakBlastSetUp[]; +extern const u8 BattleScript_BeakBlastBurn[]; +extern const u8 BattleScript_DefDownSpeedUp[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/battle_util.h b/include/battle_util.h index 1c5216c73..d2d7ac29f 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -170,7 +170,7 @@ bool32 IsBattlerWeatherAffected(u8 battlerId, u32 weatherFlags); void TryToApplyMimicry(u8 battlerId, bool8 various); void TryToRevertMimicry(void); void RestoreBattlerOriginalTypes(u8 battlerId); - +u32 GetBattlerMoveTargetType(u8 battlerId, u16 move); // Ability checks bool32 IsRolePlayBannedAbilityAtk(u16 ability); bool32 IsRolePlayBannedAbility(u16 ability); diff --git a/include/constants/battle.h b/include/constants/battle.h index 3eaa79a40..e5a13205b 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -363,8 +363,9 @@ #define MOVE_EFFECT_RELIC_SONG 0x47 #define MOVE_EFFECT_TRAP_BOTH 0x48 #define MOVE_EFFECT_SKY_DROP 0x49 +#define MOVE_EFFECT_SCALE_SHOT 0x4A -#define NUM_MOVE_EFFECTS 0x50 +#define NUM_MOVE_EFFECTS 0x4B #define MOVE_EFFECT_AFFECTS_USER 0x4000 #define MOVE_EFFECT_CERTAIN 0x8000 diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index d9b9ee5a2..bddb727fd 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -532,6 +532,8 @@ #define B_ANIM_STRONG_WINDS 30 #define B_ANIM_PRIMAL_REVERSION 31 #define B_ANIM_AQUA_RING_HEAL 32 +#define B_ANIM_BEAK_BLAST_SETUP 33 +#define B_ANIM_SHELL_TRAP_SETUP 34 // special animations table (gBattleAnims_Special) #define B_ANIM_LVL_UP 0 @@ -570,6 +572,7 @@ #define TRAP_ANIM_SAND_TOMB 4 #define TRAP_ANIM_MAGMA_STORM 5 #define TRAP_ANIM_INFESTATION 6 +#define TRAP_ANIM_SNAP_TRAP 7 // Weather defines for battle animation scripts. #define ANIM_WEATHER_NONE 0 diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 4131dc4da..3cc3d72b3 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -387,7 +387,13 @@ #define EFFECT_CLANGOROUS_SOUL 381 #define EFFECT_BOLT_BEAK 382 #define EFFECT_SKY_DROP 383 +#define EFFECT_EXPANDING_FORCE 384 +#define EFFECT_SCALE_SHOT 385 +#define EFFECT_METEOR_BEAM 386 +#define EFFECT_RISING_VOLTAGE 387 +#define EFFECT_BEAK_BLAST 388 +#define EFFECT_COURT_CHANGE 389 -#define NUM_BATTLE_MOVE_EFFECTS 384 +#define NUM_BATTLE_MOVE_EFFECTS 390 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 594de9ef7..da44714e2 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -39,6 +39,7 @@ #define sABILITY_OVERWRITE (gBattleScripting + 0x34) // abilityPopupOverwrite #define sSWITCH_CASE (gBattleScripting + 0x36) // switchCase #define sBERRY_OVERRIDE (gBattleScripting + 0x37) // overrideBerryRequirements +#define sSTICKY_WEB_STAT_DROP (gBattleScripting + 0x38) // stickyWebStatDrop // Array entries for battle communication #define MULTIUSE_STATE 0 @@ -238,6 +239,8 @@ #define VARIOUS_SAVE_BATTLER_ITEM 148 #define VARIOUS_RESTORE_BATTLER_ITEM 149 #define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 150 +#define VARIOUS_SET_BEAK_BLAST 151 +#define VARIOUS_SWAP_SIDE_STATUSES 152 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 6d157a350..4eff91c85 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -609,8 +609,12 @@ #define STRINGID_NEUTRALIZINGGASOVER 607 #define STRINGID_TARGETTOOHEAVY 608 #define STRINGID_PKMNTOOKTARGETHIGH 609 +#define STRINGID_PKMNINSNAPTRAP 610 +#define STRINGID_METEORBEAMCHARGING 611 +#define STRINGID_HEATUPBEAK 612 +#define STRINGID_COURTCHANGE 613 -#define BATTLESTRINGS_COUNT 610 +#define BATTLESTRINGS_COUNT 614 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, @@ -667,6 +671,7 @@ #define B_MSG_TURN1_GEOMANCY 9 #define B_MSG_TURN1_FREEZE_SHOCK 10 #define B_MSG_TURN1_SKY_DROP 11 +#define B_MSG_TURN1_METEOR_BEAM 12 // gMoveWeatherChangeStringIds #define B_MSG_STARTED_RAIN 0 diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index f28cb4fe8..192b75d74 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -550,7 +550,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) u8 atkPriority = GetMovePriority(battlerAtk, move); u16 moveEffect = gBattleMoves[move].effect; s32 moveType; - u16 moveTarget = gBattleMoves[move].target; + u16 moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move); u16 accuracy = AI_GetMoveAccuracy(battlerAtk, battlerDef, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect, move); u8 effectiveness = AI_GetMoveEffectiveness(move, battlerAtk, battlerDef); bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk); @@ -566,7 +566,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) GET_MOVE_TYPE(move, moveType); // check non-user target - if (!(gBattleMoves[move].target & MOVE_TARGET_USER)) + if (!(moveTarget & MOVE_TARGET_USER)) { // handle negative checks on non-user target // check powder moves @@ -2371,7 +2371,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) } else { - if (gBattleMoves[instructedMove].target & (MOVE_TARGET_SELECTED + if (AI_GetBattlerMoveTargetType(battlerDef, instructedMove) & (MOVE_TARGET_SELECTED | MOVE_TARGET_DEPENDS | MOVE_TARGET_RANDOM | MOVE_TARGET_BOTH @@ -2568,7 +2568,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) // move data u8 moveType = gBattleMoves[move].type; u16 effect = gBattleMoves[move].effect; - u16 target = gBattleMoves[move].target; + u16 moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move); // ally data u8 battlerAtkPartner = AI_DATA->battlerAtkPartner; u16 atkPartnerAbility = AI_DATA->atkPartnerAbility; @@ -2607,7 +2607,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (IsAttackBoostMoveEffect(effect)) score -= 3; // encourage moves hitting multiple opponents - if (!IS_MOVE_STATUS(move) && (gBattleMoves[move].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) + if (!IS_MOVE_STATUS(move) && (moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) score += 3; } } @@ -2675,7 +2675,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (GetMoveDamageResult(move) == MOVE_POWER_OTHER) { // partner ability checks - if (!partnerProtecting && gBattleMoves[move].target != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(AI_DATA->atkAbility, move)) + if (!partnerProtecting && moveTarget != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(AI_DATA->atkAbility, move)) { switch (atkPartnerAbility) { @@ -2875,7 +2875,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) if (instructedMove != MOVE_NONE && !IS_MOVE_STATUS(instructedMove) - && gBattleMoves[instructedMove].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)) // Use instruct on multi-target moves + && (AI_GetBattlerMoveTargetType(battlerAtkPartner, instructedMove) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) // Use instruct on multi-target moves { RETURN_SCORE_PLUS(1); } @@ -3698,24 +3698,24 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score); break; case MOVE_WIDE_GUARD: - if (predictedMove != MOVE_NONE && gBattleMoves[predictedMove].target & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH)) + if (predictedMove != MOVE_NONE && AI_GetBattlerMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH)) { ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score); } - else if (isDoubleBattle && gBattleMoves[AI_DATA->partnerMove].target & MOVE_TARGET_FOES_AND_ALLY) + else if (isDoubleBattle && AI_GetBattlerMoveTargetType(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove) & MOVE_TARGET_FOES_AND_ALLY) { if (AI_DATA->atkAbility != ABILITY_TELEPATHY) ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score); } break; case MOVE_CRAFTY_SHIELD: - if (predictedMove != MOVE_NONE && IS_MOVE_STATUS(predictedMove) && !(gBattleMoves[predictedMove].target & MOVE_TARGET_USER)) + if (predictedMove != MOVE_NONE && IS_MOVE_STATUS(predictedMove) && !(AI_GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER)) ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score); break; case MOVE_MAT_BLOCK: if (gDisableStructs[battlerAtk].isFirstTurn && predictedMove != MOVE_NONE - && !IS_MOVE_STATUS(predictedMove) && !(gBattleMoves[predictedMove].target & MOVE_TARGET_USER)) + && !IS_MOVE_STATUS(predictedMove) && !(AI_GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER)) ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score); break; case MOVE_KINGS_SHIELD: @@ -4160,7 +4160,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) score += 10; break; case EFFECT_MAGIC_COAT: - if (IS_MOVE_STATUS(predictedMove) && gBattleMoves[predictedMove].target & (MOVE_TARGET_SELECTED | MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_BOTH)) + if (IS_MOVE_STATUS(predictedMove) && AI_GetBattlerMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_SELECTED | MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_BOTH)) score += 3; break; case EFFECT_RECYCLE: diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index fe37f31ec..de4271bba 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1251,6 +1251,16 @@ bool32 AI_WeatherHasEffect(void) return TRUE; } +u32 AI_GetBattlerMoveTargetType(u8 battlerId, u16 move) +{ + u32 target; + + if (gBattleMoves[move].effect == EFFECT_EXPANDING_FORCE && AI_IsTerrainAffected(battlerId, STATUS_FIELD_PSYCHIC_TERRAIN)) + return MOVE_TARGET_BOTH; + else + return gBattleMoves[move].target; +} + bool32 IsAromaVeilProtectedMove(u16 move) { u32 i; @@ -1919,9 +1929,8 @@ bool32 HasMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef, u8 accCheck, bool32 if (ignoreStatus && IS_MOVE_STATUS(moves[i])) continue; else if ((!IS_MOVE_STATUS(moves[i]) && gBattleMoves[moves[i]].accuracy == 0) - || gBattleMoves[moves[i]].target & (MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD)) + || AI_GetBattlerMoveTargetType(battlerAtk, moves[i]) & (MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD)) continue; - if (AI_GetMoveAccuracy(battlerAtk, battlerDef, atkAbility, defAbility, atkHoldEffect, defHoldEffect, moves[i]) <= accCheck) return TRUE; } diff --git a/src/battle_anim.c b/src/battle_anim.c index b6efd2b04..fe42b81cb 100644 --- a/src/battle_anim.c +++ b/src/battle_anim.c @@ -204,7 +204,7 @@ void DoMoveAnim(u16 move) gBattleAnimAttacker = gBattlerAttacker; gBattleAnimTarget = gBattlerTarget; // Make sure the anim target of moves hitting everyone is at the opposite side. - if (gBattleMoves[move].target & MOVE_TARGET_FOES_AND_ALLY && IsDoubleBattle()) + if (GetBattlerMoveTargetType(gBattlerAttacker, move) & MOVE_TARGET_FOES_AND_ALLY && IsDoubleBattle()) { while (GET_BATTLER_SIDE(gBattleAnimAttacker) == GET_BATTLER_SIDE(gBattleAnimTarget)) { diff --git a/src/battle_anim_throw.c b/src/battle_anim_throw.c index 0db7df10c..592a4104f 100755 --- a/src/battle_anim_throw.c +++ b/src/battle_anim_throw.c @@ -2500,6 +2500,8 @@ void AnimTask_GetTrappedMoveAnimId(u8 taskId) gBattleAnimArgs[0] = TRAP_ANIM_MAGMA_STORM; else if (gBattleSpritesDataPtr->animationData->animArg == MOVE_INFESTATION) gBattleAnimArgs[0] = TRAP_ANIM_INFESTATION; + else if (gBattleSpritesDataPtr->animationData->animArg == MOVE_SNAP_TRAP) + gBattleAnimArgs[0] = TRAP_ANIM_SNAP_TRAP; else gBattleAnimArgs[0] = TRAP_ANIM_BIND; diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 7078b4697..69e0de5af 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -1584,9 +1584,9 @@ static void OpponentHandleChooseMove(void) BtlController_EmitTwoReturnValues(BUFFER_B, 15, gBattlerTarget); break; default: - if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) + if (GetBattlerMoveTargetType(gActiveBattler, moveInfo->moves[chosenMoveId]) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) gBattlerTarget = gActiveBattler; - if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH) + if (GetBattlerMoveTargetType(gActiveBattler, moveInfo->moves[chosenMoveId]) & MOVE_TARGET_BOTH) { gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) @@ -1611,7 +1611,7 @@ static void OpponentHandleChooseMove(void) move = moveInfo->moves[chosenMoveId]; } while (move == MOVE_NONE); - if (gBattleMoves[move].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) + if (GetBattlerMoveTargetType(gActiveBattler, move) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) BtlController_EmitTwoReturnValues(BUFFER_B, 10, (chosenMoveId) | (gActiveBattler << 8)); else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index b5a032813..bd6b3a023 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -357,6 +357,7 @@ static void HandleInputChooseTarget(void) s32 i; static const u8 identities[MAX_BATTLERS_COUNT] = {B_POSITION_PLAYER_LEFT, B_POSITION_PLAYER_RIGHT, B_POSITION_OPPONENT_RIGHT, B_POSITION_OPPONENT_LEFT}; u16 move = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MOVE1 + gMoveSelectionCursor[gActiveBattler]); + u16 moveTarget = GetBattlerMoveTargetType(gActiveBattler, move); DoBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX, 15, 1); for (i = 0; i < gBattlersCount; i++) @@ -397,7 +398,7 @@ static void HandleInputChooseTarget(void) PlaySE(SE_SELECT); gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCb_HideAsMoveTarget; - if (gBattleMoves[move].target == (MOVE_TARGET_USER | MOVE_TARGET_ALLY)) + if (moveTarget == (MOVE_TARGET_USER | MOVE_TARGET_ALLY)) { gMultiUsePlayerCursor ^= BIT_FLANK; } @@ -426,7 +427,7 @@ static void HandleInputChooseTarget(void) case B_POSITION_PLAYER_RIGHT: if (gActiveBattler != gMultiUsePlayerCursor) i++; - else if (gBattleMoves[move].target & MOVE_TARGET_USER_OR_SELECTED) + else if (moveTarget & MOVE_TARGET_USER_OR_SELECTED) i++; break; case B_POSITION_OPPONENT_LEFT: @@ -446,7 +447,7 @@ static void HandleInputChooseTarget(void) PlaySE(SE_SELECT); gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCb_HideAsMoveTarget; - if (gBattleMoves[move].target == (MOVE_TARGET_USER | MOVE_TARGET_ALLY)) + if (moveTarget == (MOVE_TARGET_USER | MOVE_TARGET_ALLY)) { gMultiUsePlayerCursor ^= BIT_FLANK; } @@ -475,7 +476,7 @@ static void HandleInputChooseTarget(void) case B_POSITION_PLAYER_RIGHT: if (gActiveBattler != gMultiUsePlayerCursor) i++; - else if (gBattleMoves[move].target & MOVE_TARGET_USER_OR_SELECTED) + else if (moveTarget & MOVE_TARGET_USER_OR_SELECTED) i++; break; case B_POSITION_OPPONENT_LEFT: @@ -608,7 +609,7 @@ static void HandleInputChooseMove(void) } else { - moveTarget = gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].target; + moveTarget = GetBattlerMoveTargetType(gActiveBattler, moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]); } if (moveTarget & MOVE_TARGET_USER) diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 18c58181a..9dbfa7771 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -34,7 +34,7 @@ extern const struct CompressedSpriteSheet gSpriteSheet_EnemyShadow; extern const struct SpriteTemplate gSpriteTemplate_EnemyShadow; // this file's functions -static u8 GetBattlePalaceMoveGroup(u16 move); +static u8 GetBattlePalaceMoveGroup(u8 battlerId, u16 move); static u16 GetBattlePalaceTarget(void); static void SpriteCB_TrainerSlideVertical(struct Sprite *sprite); static bool8 ShouldAnimBeDoneRegardlessOfSubstitute(u8 animId); @@ -151,7 +151,7 @@ u16 ChooseMoveAndTargetInBattlePalace(void) { if (moveInfo->moves[i] == MOVE_NONE) break; - if (selectedGroup == GetBattlePalaceMoveGroup(moveInfo->moves[i]) && moveInfo->currentPp[i] != 0) + if (selectedGroup == GetBattlePalaceMoveGroup(gActiveBattler, moveInfo->moves[i]) && moveInfo->currentPp[i] != 0) selectedMoves |= gBitTable[i]; } @@ -177,11 +177,11 @@ u16 ChooseMoveAndTargetInBattlePalace(void) { // validMoveFlags is used here as a bitfield for which moves can be used for each move group type // first 4 bits are for attack (1 for each move), then 4 bits for defense, and 4 for support - if (GetBattlePalaceMoveGroup(moveInfo->moves[i]) == PALACE_MOVE_GROUP_ATTACK && !(gBitTable[i] & unusableMovesBits)) + if (GetBattlePalaceMoveGroup(gActiveBattler, moveInfo->moves[i]) == PALACE_MOVE_GROUP_ATTACK && !(gBitTable[i] & unusableMovesBits)) validMoveFlags += (1 << 0); - if (GetBattlePalaceMoveGroup(moveInfo->moves[i]) == PALACE_MOVE_GROUP_DEFENSE && !(gBitTable[i] & unusableMovesBits)) + if (GetBattlePalaceMoveGroup(gActiveBattler, moveInfo->moves[i]) == PALACE_MOVE_GROUP_DEFENSE && !(gBitTable[i] & unusableMovesBits)) validMoveFlags += (1 << 4); - if (GetBattlePalaceMoveGroup(moveInfo->moves[i]) == PALACE_MOVE_GROUP_SUPPORT && !(gBitTable[i] & unusableMovesBits)) + if (GetBattlePalaceMoveGroup(gActiveBattler, moveInfo->moves[i]) == PALACE_MOVE_GROUP_SUPPORT && !(gBitTable[i] & unusableMovesBits)) validMoveFlags += (1 << 8); } @@ -218,7 +218,7 @@ u16 ChooseMoveAndTargetInBattlePalace(void) do { i = Random() % MAX_MON_MOVES; - if (!(gBitTable[i] & unusableMovesBits) && validMoveGroup == GetBattlePalaceMoveGroup(moveInfo->moves[i])) + if (!(gBitTable[i] & unusableMovesBits) && validMoveGroup == GetBattlePalaceMoveGroup(gActiveBattler, moveInfo->moves[i])) chosenMoveId = i; } while (chosenMoveId == -1); } @@ -247,7 +247,7 @@ u16 ChooseMoveAndTargetInBattlePalace(void) } else { - moveTarget = gBattleMoves[moveInfo->moves[chosenMoveId]].target; + moveTarget = GetBattlerMoveTargetType(gActiveBattler, moveInfo->moves[chosenMoveId]); } if (moveTarget & MOVE_TARGET_USER) @@ -269,9 +269,9 @@ u16 ChooseMoveAndTargetInBattlePalace(void) #undef numValidMoveGroups #undef validMoveGroup -static u8 GetBattlePalaceMoveGroup(u16 move) +static u8 GetBattlePalaceMoveGroup(u8 battlerId, u16 move) { - switch (gBattleMoves[move].target) + switch (GetBattlerMoveTargetType(battlerId, move)) { case MOVE_TARGET_SELECTED: case MOVE_TARGET_USER_OR_SELECTED: diff --git a/src/battle_main.c b/src/battle_main.c index d92aa150a..479cafae6 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4820,13 +4820,19 @@ static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void) { gActiveBattler = gBattlerAttacker = gBattleStruct->focusPunchBattlerId; gBattleStruct->focusPunchBattlerId++; - if (gChosenMoveByBattler[gActiveBattler] == MOVE_FOCUS_PUNCH - && !(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP) + if (!(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP) && !(gDisableStructs[gBattlerAttacker].truantCounter) && !(gProtectStructs[gActiveBattler].noValidMoves)) { - BattleScriptExecute(BattleScript_FocusPunchSetUp); - return; + switch(gChosenMoveByBattler[gActiveBattler]) + { + case MOVE_FOCUS_PUNCH: + BattleScriptExecute(BattleScript_FocusPunchSetUp); + return; + case MOVE_BEAK_BLAST: + BattleScriptExecute(BattleScript_BeakBlastSetUp); + return; + } } } } diff --git a/src/battle_message.c b/src/battle_message.c index ee0cf63e4..3e2d9abd9 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -144,6 +144,7 @@ static const u8 sText_PkmnDugHole[] = _("{B_ATK_NAME_WITH_PREFIX} dug a hole!"); static const u8 sText_PkmnHidUnderwater[] = _("{B_ATK_NAME_WITH_PREFIX} hid\nunderwater!"); static const u8 sText_PkmnSprangUp[] = _("{B_ATK_NAME_WITH_PREFIX} sprang up!"); static const u8 sText_PkmnSqueezedByBind[] = _("{B_DEF_NAME_WITH_PREFIX} was squeezed by\n{B_ATK_NAME_WITH_PREFIX}'s BIND!"); +static const u8 sText_PkmnInSnapTrap[] = _("{B_DEF_NAME_WITH_PREFIX} got trapped\nby a snap trap!"); static const u8 sText_PkmnTrappedInVortex[] = _("{B_DEF_NAME_WITH_PREFIX} was trapped\nin the vortex!"); static const u8 sText_PkmnTrappedBySandTomb[] = _("{B_DEF_NAME_WITH_PREFIX} was trapped\nby SAND TOMB!"); static const u8 sText_PkmnWrappedBy[] = _("{B_DEF_NAME_WITH_PREFIX} was WRAPPED by\n{B_ATK_NAME_WITH_PREFIX}!"); @@ -734,9 +735,16 @@ static const u8 sText_NeutralizingGasEnters[] = _("Neutralizing Gas filled the a static const u8 sText_NeutralizingGasOver[] = _("The effects of Neutralizing\nGas wore off!"); 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!"); +static const u8 sText_MeteorBeamCharging[] = _("{B_ATK_NAME_WITH_PREFIX} is overflowing\nwith space energy!"); +static const u8 sText_HeatingUpBeak[] = _("{B_ATK_NAME_WITH_PREFIX} started\nheating up its beak!"); +static const u8 sText_CourtChange[] = _("{B_ATK_NAME_WITH_PREFIX} swapped the battle\neffects affecting each side!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_COURTCHANGE - BATTLESTRINGS_TABLE_START] = sText_CourtChange, + [STRINGID_HEATUPBEAK - BATTLESTRINGS_TABLE_START] = sText_HeatingUpBeak, + [STRINGID_METEORBEAMCHARGING - BATTLESTRINGS_TABLE_START] = sText_MeteorBeamCharging, + [STRINGID_PKMNINSNAPTRAP - BATTLESTRINGS_TABLE_START] = sText_PkmnInSnapTrap, [STRINGID_NEUTRALIZINGGASOVER - BATTLESTRINGS_TABLE_START] = sText_NeutralizingGasOver, [STRINGID_NEUTRALIZINGGASENTERS - BATTLESTRINGS_TABLE_START] = sText_NeutralizingGasEnters, [STRINGID_BATTLERTYPECHANGEDTO - BATTLESTRINGS_TABLE_START] = sText_BattlerTypeChangedTo, @@ -1550,6 +1558,7 @@ const u16 gFirstTurnOfTwoStringIds[] = [B_MSG_TURN1_GEOMANCY] = STRINGID_PKNMABSORBINGPOWER, [B_MSG_TURN1_FREEZE_SHOCK] = STRINGID_CLOAKEDINAFREEZINGLIGHT, [B_MSG_TURN1_SKY_DROP] = STRINGID_PKMNTOOKTARGETHIGH, + [B_MSG_TURN1_METEOR_BEAM] = STRINGID_METEORBEAMCHARGING, }; // Index copied from move's index in sTrappingMoves @@ -1563,6 +1572,7 @@ const u16 gWrappedStringIds[] = STRINGID_PKMNTRAPPEDBYSANDTOMB, // MOVE_SAND_TOMB STRINGID_TRAPPEDBYSWIRLINGMAGMA, // MOVE_MAGMA_STORM STRINGID_INFESTATION, // MOVE_INFESTATION + STRINGID_PKMNINSNAPTRAP, // MOVE_SNAPTRAP }; const u16 gMistUsedStringIds[] = diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 5de210a51..d6edb0ab2 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -281,7 +281,7 @@ static const s32 sExperienceScalingFactors[] = static const u16 sTrappingMoves[] = { - MOVE_BIND, MOVE_WRAP, MOVE_FIRE_SPIN, MOVE_CLAMP, MOVE_WHIRLPOOL, MOVE_SAND_TOMB, MOVE_MAGMA_STORM, MOVE_INFESTATION, 0xFFFF + MOVE_BIND, MOVE_WRAP, MOVE_FIRE_SPIN, MOVE_CLAMP, MOVE_WHIRLPOOL, MOVE_SAND_TOMB, MOVE_MAGMA_STORM, MOVE_INFESTATION, MOVE_SNAP_TRAP, 0xFFFF }; #define STAT_CHANGE_WORKED 0 @@ -1296,12 +1296,12 @@ static const u8 sBattlePalaceNatureToFlavorTextId[NUM_NATURES] = [NATURE_QUIRKY] = B_MSG_EAGER_FOR_MORE, }; -static bool32 NoTargetPresent(u32 move) +static bool32 NoTargetPresent(u8 battlerId, u32 move) { if (!IsBattlerAlive(gBattlerTarget)) gBattlerTarget = GetMoveTarget(move, NO_TARGET_OVERRIDE); - switch (gBattleMoves[move].target) + switch (GetBattlerMoveTargetType(battlerId, move)) { case MOVE_TARGET_SELECTED: case MOVE_TARGET_DEPENDS: @@ -1446,7 +1446,7 @@ static void Cmd_attackcanceler(void) } gHitMarker |= HITMARKER_OBEYS; - if (NoTargetPresent(gCurrentMove) && (!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))) + if (NoTargetPresent(gBattlerAttacker, gCurrentMove) && (!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))) { gBattlescriptCurrInstr = BattleScript_ButItFailedAtkStringPpReduce; if (!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)) @@ -1530,6 +1530,11 @@ static void Cmd_attackcanceler(void) gBattleCommunication[MISS_TYPE] = B_MSG_PROTECTED; gBattlescriptCurrInstr++; } + else if (gProtectStructs[gBattlerTarget].beakBlastCharge && IsMoveMakingContact(gCurrentMove, gBattlerAttacker)) + { + gProtectStructs[gBattlerAttacker].touchedProtectLike = TRUE; + gBattlescriptCurrInstr++; + } else { gBattlescriptCurrInstr++; @@ -1724,6 +1729,7 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move) static void Cmd_accuracycheck(void) { u16 type, move = T2_READ_16(gBattlescriptCurrInstr + 5); + u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move); if (move == ACC_CURR_MOVE) move = gCurrentMove; @@ -1753,7 +1759,7 @@ static void Cmd_accuracycheck(void) gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && - (gBattleMoves[move].target == MOVE_TARGET_BOTH || gBattleMoves[move].target == MOVE_TARGET_FOES_AND_ALLY)) + (moveTarget == MOVE_TARGET_BOTH || moveTarget == MOVE_TARGET_FOES_AND_ALLY)) gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_ATK; else gBattleCommunication[MISS_TYPE] = B_MSG_MISSED; @@ -1787,7 +1793,7 @@ static void Cmd_ppreduce(void) if (!gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure) { - switch (gBattleMoves[gCurrentMove].target) + switch (GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove)) { case MOVE_TARGET_FOES_AND_ALLY: for (i = 0; i < gBattlersCount; i++) @@ -2087,6 +2093,8 @@ static void Cmd_multihitresultmessage(void) static void Cmd_attackanimation(void) { + u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); + if (gBattleControllerExecFlags) return; @@ -2103,9 +2111,9 @@ static void Cmd_attackanimation(void) } else { - if ((gBattleMoves[gCurrentMove].target & MOVE_TARGET_BOTH - || gBattleMoves[gCurrentMove].target & MOVE_TARGET_FOES_AND_ALLY - || gBattleMoves[gCurrentMove].target & MOVE_TARGET_DEPENDS) + if ((moveTarget & MOVE_TARGET_BOTH + || moveTarget & MOVE_TARGET_FOES_AND_ALLY + || moveTarget & MOVE_TARGET_DEPENDS) && gBattleScripting.animTargetsHit) { gBattlescriptCurrInstr++; @@ -3159,7 +3167,7 @@ void SetMoveEffect(bool32 primary, u32 certain) | BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_SECRET_BASE)) - && (gWishFutureKnock.knockedOffMons[side] & gBitTable[gBattlerPartyIndexes[gBattlerAttacker]])) + && (gWishFutureKnock.knockedOffMons[side] & gBitTable[gBattlerPartyIndexes[gBattlerAttacker]])) { gBattlescriptCurrInstr++; } @@ -3495,6 +3503,13 @@ void SetMoveEffect(bool32 primary, u32 certain) gBattleMons[gBattlerTarget].status2 |= STATUS2_ESCAPE_PREVENTION; gBattleMons[gBattlerAttacker].status2 |= STATUS2_ESCAPE_PREVENTION; break; + case MOVE_EFFECT_SCALE_SHOT: + if (!NoAliveMonsForEitherParty()) + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_DefDownSpeedUp; + } + break; } } } @@ -5014,7 +5029,7 @@ static void Cmd_moveend(void) } else if (gProtectStructs[gBattlerTarget].obstructed && gCurrentMove != MOVE_SUCKER_PUNCH) { - gProtectStructs[gBattlerAttacker].touchedProtectLike = 0; + gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; i = gBattlerAttacker; gBattlerAttacker = gBattlerTarget; gBattlerTarget = i; // gBattlerTarget and gBattlerAttacker are swapped in order to activate Defiant, if applicable @@ -5023,6 +5038,17 @@ static void Cmd_moveend(void) gBattlescriptCurrInstr = BattleScript_KingsShieldEffect; effect = 1; } + // Not strictly a protect effect, but works the same way + else if (gProtectStructs[gBattlerTarget].beakBlastCharge + && CanBeBurned(gBattlerAttacker) + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; + gBattleMons[gBattlerAttacker].status1 = STATUS1_BURN; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BeakBlastBurn; + effect = 1; + } } gBattleScripting.moveendState++; break; @@ -5318,6 +5344,8 @@ static void Cmd_moveend(void) gBattleScripting.moveendState++; break; case MOVEEND_NEXT_TARGET: // For moves hitting two opposing Pokemon. + { + u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); // Set a flag if move hits either target (for throat spray that can't check damage) if (!(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) @@ -5326,12 +5354,13 @@ static void Cmd_moveend(void) if (!(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) && gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !gProtectStructs[gBattlerAttacker].chargingTurn - && (gBattleMoves[gCurrentMove].target == MOVE_TARGET_BOTH || gBattleMoves[gCurrentMove].target == MOVE_TARGET_FOES_AND_ALLY) + && (moveTarget == MOVE_TARGET_BOTH + || moveTarget == MOVE_TARGET_FOES_AND_ALLY) && !(gHitMarker & HITMARKER_NO_ATTACKSTRING)) { u8 battlerId; - if (gBattleMoves[gCurrentMove].target == MOVE_TARGET_FOES_AND_ALLY) + if (moveTarget == MOVE_TARGET_FOES_AND_ALLY) { gHitMarker |= HITMARKER_NO_PPDEDUCT; for (battlerId = gBattlerTarget + 1; battlerId < gBattlersCount; battlerId++) @@ -5367,6 +5396,7 @@ static void Cmd_moveend(void) RecordLastUsedMoveBy(gBattlerAttacker, gCurrentMove); gBattleScripting.moveendState++; break; + } case MOVEEND_EJECT_BUTTON: if (gCurrentMove != MOVE_DRAGON_TAIL && gCurrentMove != MOVE_CIRCLE_THROW @@ -7621,6 +7651,66 @@ static bool32 IsRototillerAffected(u32 battlerId) return TRUE; } +#define COURTCHANGE_SWAP(status, structField, temp) \ +{ \ + temp = gSideStatuses[B_SIDE_PLAYER]; \ + if (gSideStatuses[B_SIDE_OPPONENT] & status) \ + gSideStatuses[B_SIDE_PLAYER] |= status; \ + else \ + gSideStatuses[B_SIDE_PLAYER] &= ~(status); \ + if (temp & status) \ + gSideStatuses[B_SIDE_OPPONENT] |= status; \ + else \ + gSideStatuses[B_SIDE_OPPONENT] &= ~(status); \ + SWAP(sideTimerPlayer->structField, sideTimerOpp->structField, temp);\ +} \ + +#define UPDATE_COURTCHANGED_BATTLER(structField)\ +{ \ + sideTimerPlayer->structField ^= BIT_SIDE; \ + sideTimerOpp->structField ^= BIT_SIDE; \ +} \ + +static bool32 CourtChangeSwapSideStatuses(void) +{ + struct SideTimer *sideTimerPlayer = &gSideTimers[B_SIDE_PLAYER]; + struct SideTimer *sideTimerOpp = &gSideTimers[B_SIDE_OPPONENT]; + u32 temp; + + // TODO: add Pledge-related effects + // TODO: add Gigantamax-related effects + + // Swap timers and statuses + COURTCHANGE_SWAP(SIDE_STATUS_REFLECT, reflectTimer, temp) + COURTCHANGE_SWAP(SIDE_STATUS_LIGHTSCREEN, lightscreenTimer, temp) + COURTCHANGE_SWAP(SIDE_STATUS_MIST, mistTimer, temp); + COURTCHANGE_SWAP(SIDE_STATUS_SAFEGUARD, safeguardTimer, temp); + COURTCHANGE_SWAP(SIDE_STATUS_AURORA_VEIL, auroraVeilTimer, temp); + COURTCHANGE_SWAP(SIDE_STATUS_TAILWIND, tailwindTimer, temp); + // Lucky Chant doesn't exist in gen 8, but seems like it should be affected by Court Change + COURTCHANGE_SWAP(SIDE_STATUS_LUCKY_CHANT, luckyChantTimer, temp); + COURTCHANGE_SWAP(SIDE_STATUS_SPIKES, spikesAmount, temp); + COURTCHANGE_SWAP(SIDE_STATUS_STEALTH_ROCK, stealthRockAmount, temp); + COURTCHANGE_SWAP(SIDE_STATUS_TOXIC_SPIKES, toxicSpikesAmount, temp); + COURTCHANGE_SWAP(SIDE_STATUS_STICKY_WEB, stickyWebAmount, temp); + + // Change battler IDs of swapped effects. Needed for the correct string when they expire + // E.g. "Foe's Reflect wore off!" + UPDATE_COURTCHANGED_BATTLER(reflectBattlerId); + UPDATE_COURTCHANGED_BATTLER(lightscreenBattlerId); + UPDATE_COURTCHANGED_BATTLER(mistBattlerId); + UPDATE_COURTCHANGED_BATTLER(safeguardBattlerId); + UPDATE_COURTCHANGED_BATTLER(auroraVeilBattlerId); + UPDATE_COURTCHANGED_BATTLER(tailwindBattlerId); + UPDATE_COURTCHANGED_BATTLER(luckyChantBattlerId); + + // For Mirror Armor only + gBattleStruct->stickyWebUser = gBattlerAttacker; + + // Track which side originally set the Sticky Web + SWAP(sideTimerPlayer->stickyWebBattlerSide, sideTimerOpp->stickyWebBattlerSide, temp); +} + static void Cmd_various(void) { struct Pokemon *mon; @@ -8322,6 +8412,7 @@ static void Cmd_various(void) case MOVE_MIRROR_COAT: case MOVE_METAL_BURST: case MOVE_ME_FIRST: + case MOVE_BEAK_BLAST: gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); break; default: @@ -9474,6 +9565,12 @@ static void Cmd_various(void) case VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM: gBattleMons[gActiveBattler].item = gLastUsedItem; break; + case VARIOUS_SET_BEAK_BLAST: + gProtectStructs[gBattlerAttacker].beakBlastCharge = TRUE; + break; + case VARIOUS_SWAP_SIDE_STATUSES: + CourtChangeSwapSideStatuses(); + break; } // End of switch (gBattlescriptCurrInstr[2]) gBattlescriptCurrInstr += 3; @@ -9618,7 +9715,7 @@ static void Cmd_jumpifnexttargetvalid(void) for (gBattlerTarget++; gBattlerTarget < gBattlersCount; gBattlerTarget++) { - if (gBattlerTarget == gBattlerAttacker && !(gBattleMoves[gCurrentMove].target & MOVE_TARGET_USER)) + if (gBattlerTarget == gBattlerAttacker && !(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove) & MOVE_TARGET_USER)) continue; if (IsBattlerAlive(gBattlerTarget)) break; @@ -11429,7 +11526,8 @@ static bool8 IsTwoTurnsMove(u16 move) || gBattleMoves[move].effect == EFFECT_TWO_TURNS_ATTACK || gBattleMoves[move].effect == EFFECT_SOLAR_BEAM || gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE - || gBattleMoves[move].effect == EFFECT_BIDE) + || gBattleMoves[move].effect == EFFECT_BIDE + || gBattleMoves[move].effect == EFFECT_METEOR_BEAM) return TRUE; else return FALSE; @@ -12123,6 +12221,7 @@ static void Cmd_recoverbasedonsunlight(void) static void Cmd_setstickyweb(void) { u8 targetSide = GetBattlerSide(gBattlerTarget); + if (gSideStatuses[targetSide] & SIDE_STATUS_STICKY_WEB) { gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); @@ -12130,6 +12229,7 @@ static void Cmd_setstickyweb(void) else { gSideStatuses[targetSide] |= SIDE_STATUS_STICKY_WEB; + gSideTimers[targetSide].stickyWebBattlerSide = GetBattlerSide(gBattlerAttacker); // For Court Change/Defiant - set this to the user's side gSideTimers[targetSide].stickyWebAmount = 1; gBattleStruct->stickyWebUser = gBattlerAttacker; // For Mirror Armor gBattlescriptCurrInstr += 5; @@ -12140,7 +12240,7 @@ static void Cmd_selectfirstvalidtarget(void) { for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++) { - if (gBattlerTarget == gBattlerAttacker && !(gBattleMoves[gCurrentMove].target & MOVE_TARGET_USER)) + if (gBattlerTarget == gBattlerAttacker && !(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove) & MOVE_TARGET_USER)) continue; if (IsBattlerAlive(gBattlerTarget)) break; @@ -12441,7 +12541,7 @@ static void Cmd_tryswapitems(void) // trick u8 sideAttacker = GetBattlerSide(gBattlerAttacker); u8 sideTarget = GetBattlerSide(gBattlerTarget); - // you can't swap items if they were knocked off in regular battles + // You can't swap items if they were knocked off in regular battles if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_FRONTIER diff --git a/src/battle_util.c b/src/battle_util.c index 3b2d9e2ca..c0a7f2150 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -238,6 +238,7 @@ bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move) void HandleAction_UseMove(void) { u32 i, side, moveType, var = 4; + u16 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; if (gBattleStruct->absentBattlerFlags & gBitTable[gBattlerAttacker] || !IsBattlerAlive(gBattlerAttacker)) @@ -310,14 +311,14 @@ void HandleAction_UseMove(void) // choose target side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove) - && gBattleMoves[gCurrentMove].target == MOVE_TARGET_SELECTED + && moveTarget == MOVE_TARGET_SELECTED && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gSideTimers[side].followmeTarget)) { gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = gSideTimers[side].followmeTarget; // follow me moxie fix } else if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && gSideTimers[side].followmeTimer == 0 - && (gBattleMoves[gCurrentMove].power != 0 || gBattleMoves[gCurrentMove].target != MOVE_TARGET_USER) + && (gBattleMoves[gCurrentMove].power != 0 || moveTarget != MOVE_TARGET_USER) && ((GetBattlerAbility(*(gBattleStruct->moveTarget + gBattlerAttacker)) != ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC) || (GetBattlerAbility(*(gBattleStruct->moveTarget + gBattlerAttacker)) != ABILITY_STORM_DRAIN && moveType == TYPE_WATER))) { @@ -338,7 +339,7 @@ void HandleAction_UseMove(void) } if (var == 4) { - if (gBattleMoves[gChosenMove].target & MOVE_TARGET_RANDOM) + if (moveTarget & MOVE_TARGET_RANDOM) { if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) { @@ -355,7 +356,7 @@ void HandleAction_UseMove(void) gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); } } - else if (gBattleMoves[gChosenMove].target & MOVE_TARGET_FOES_AND_ALLY) + else if (moveTarget & MOVE_TARGET_FOES_AND_ALLY) { for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++) { @@ -399,7 +400,7 @@ void HandleAction_UseMove(void) } } else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE - && gBattleMoves[gChosenMove].target & MOVE_TARGET_RANDOM) + && moveTarget & MOVE_TARGET_RANDOM) { if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) { @@ -422,7 +423,7 @@ void HandleAction_UseMove(void) gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerTarget) ^ BIT_FLANK); } } - else if (gBattleMoves[gChosenMove].target == MOVE_TARGET_ALLY) + else if (moveTarget == MOVE_TARGET_ALLY) { if (IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))) gBattlerTarget = BATTLE_PARTNER(gBattlerAttacker); @@ -430,7 +431,7 @@ void HandleAction_UseMove(void) gBattlerTarget = gBattlerAttacker; } else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE - && gBattleMoves[gChosenMove].target == MOVE_TARGET_FOES_AND_ALLY) + && moveTarget == MOVE_TARGET_FOES_AND_ALLY) { for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++) { @@ -1547,6 +1548,7 @@ bool8 WasUnableToUseMove(u8 battler) void PrepareStringBattle(u16 stringId, u8 battler) { + u32 targetSide = GetBattlerSide(gBattlerTarget); u16 battlerAbility = GetBattlerAbility(battler); u16 targetAbility = GetBattlerAbility(gBattlerTarget); // Support for Contrary ability. @@ -1567,8 +1569,10 @@ void PrepareStringBattle(u16 stringId, u8 battler) && ((targetAbility == ABILITY_DEFIANT && CompareStat(gBattlerTarget, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)) || (targetAbility == ABILITY_COMPETITIVE && CompareStat(gBattlerTarget, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN))) && gSpecialStatuses[gBattlerTarget].changedStatsBattlerId != BATTLE_PARTNER(gBattlerTarget) - && gSpecialStatuses[gBattlerTarget].changedStatsBattlerId != gBattlerTarget) + && ((gSpecialStatuses[gBattlerTarget].changedStatsBattlerId != gBattlerTarget) || gBattleScripting.stickyWebStatDrop == 1) + && !(gBattleScripting.stickyWebStatDrop == 1 && gSideTimers[targetSide].stickyWebBattlerSide == targetSide)) // Sticky Web must have been set by the foe { + gBattleScripting.stickyWebStatDrop = 0; gBattlerAbility = gBattlerTarget; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_DefiantActivates; @@ -4808,7 +4812,10 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move } break; case ABILITYEFFECT_MOVES_BLOCK: // 2 - if ((gLastUsedAbility == ABILITY_SOUNDPROOF && gBattleMoves[move].flags & FLAG_SOUND && !(gBattleMoves[move].target & MOVE_TARGET_USER)) + { + u16 moveTarget = GetBattlerMoveTargetType(battler, move); + + if ((gLastUsedAbility == ABILITY_SOUNDPROOF && gBattleMoves[move].flags & FLAG_SOUND && !(moveTarget & MOVE_TARGET_USER)) || (gLastUsedAbility == ABILITY_BULLETPROOF && gBattleMoves[move].flags & FLAG_BALLISTIC)) { if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS) @@ -4831,14 +4838,14 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move else if (BlocksPrankster(move, gBattlerAttacker, gBattlerTarget, TRUE) && !(IS_MOVE_STATUS(move) && GetBattlerAbility(gBattlerTarget) == ABILITY_MAGIC_BOUNCE)) { - if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE) || !(gBattleMoves[move].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) + if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE) || !(moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) CancelMultiTurnMoves(gBattlerAttacker); // Don't cancel moves that can hit two targets bc one target might not be protected gBattleScripting.battler = gBattlerAbility = gBattlerTarget; gBattlescriptCurrInstr = BattleScript_DarkTypePreventsPrankster; effect = 1; } - break; + } case ABILITYEFFECT_ABSORBING: // 3 if (move != MOVE_NONE) { @@ -7424,7 +7431,7 @@ u32 GetMoveTarget(u16 move, u8 setTarget) if (setTarget != NO_TARGET_OVERRIDE) moveTarget = setTarget - 1; else - moveTarget = gBattleMoves[move].target; + moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move); // Special cases if (move == MOVE_CURSE && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) @@ -7697,7 +7704,7 @@ bool32 IsBattlerProtected(u8 battlerId, u16 move) else if (gProtectStructs[battlerId].protected) return TRUE; else if (gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_WIDE_GUARD - && gBattleMoves[move].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)) + && GetBattlerMoveTargetType(gBattlerAttacker, move) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)) return TRUE; else if (gProtectStructs[battlerId].banefulBunkered) return TRUE; @@ -7823,7 +7830,7 @@ u32 CountBattlerStatIncreases(u8 battlerId, bool32 countEvasionAcc) u32 GetMoveTargetCount(u16 move, u8 battlerAtk, u8 battlerDef) { - switch (gBattleMoves[move].target) + switch (GetBattlerMoveTargetType(gBattlerAttacker, move)) { case MOVE_TARGET_BOTH: return IsBattlerAlive(battlerDef) @@ -8162,9 +8169,17 @@ static u16 CalcMoveBasePower(u16 move, u8 battlerAtk, u8 battlerDef) && IsBattlerGrounded(gBattlerAttacker)) basePower *= 2; break; + case EFFECT_EXPANDING_FORCE: + if (IsBattlerTerrainAffected(gBattlerAttacker, STATUS_FIELD_PSYCHIC_TERRAIN)) + MulModifier(&basePower, UQ_4_12(1.5)); + break; + case EFFECT_RISING_VOLTAGE: + if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_ELECTRIC_TERRAIN)) + basePower *= 2; + break; } - // move-specific base power changes + // Move-specific base power changes switch (move) { case MOVE_WATER_SHURIKEN: @@ -10004,7 +10019,7 @@ bool32 BlocksPrankster(u16 move, u8 battlerPrankster, u8 battlerDef, bool32 chec return FALSE; if (GetBattlerSide(battlerPrankster) == GetBattlerSide(battlerDef)) return FALSE; - if (checkTarget && (gBattleMoves[move].target & (MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_DEPENDS))) + if (checkTarget && (GetBattlerMoveTargetType(battlerPrankster, move) & (MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_DEPENDS))) return FALSE; if (!IS_BATTLER_OF_TYPE(battlerDef, TYPE_DARK)) return FALSE; @@ -10036,3 +10051,16 @@ bool32 IsBattlerWeatherAffected(u8 battlerId, u32 weatherFlags) } return FALSE; } + +// Gets move target before redirection effects etc. are applied +// Possible return values are defined in battle.h following MOVE_TARGET_SELECTED +u32 GetBattlerMoveTargetType(u8 battlerId, u16 move) +{ + u32 target; + + if (gBattleMoves[move].effect == EFFECT_EXPANDING_FORCE + && IsBattlerTerrainAffected(battlerId, STATUS_FIELD_PSYCHIC_TERRAIN)) + return MOVE_TARGET_BOTH; + else + return gBattleMoves[move].target; +} diff --git a/src/contest.c b/src/contest.c index ced9f90cd..4069d8d22 100644 --- a/src/contest.c +++ b/src/contest.c @@ -5388,7 +5388,7 @@ static void SetBattleTargetSpritePosition(void) static void SetMoveTargetPosition(u16 move) { - switch (gBattleMoves[move].target) + switch (GetBattlerMoveTargetType(gBattlerAttacker, move)) { case MOVE_TARGET_USER_OR_SELECTED: case MOVE_TARGET_USER: diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 890c8bafb..1f90abf03 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -10142,7 +10142,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_BEAK_BLAST] = { - .effect = EFFECT_PLACEHOLDER, + .effect = EFFECT_BEAK_BLAST, .power = 100, .type = TYPE_FLYING, .accuracy = 100, @@ -10212,13 +10212,13 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_SHELL_TRAP] = { - .effect = EFFECT_PLACEHOLDER, + .effect = EFFECT_PLACEHOLDER, // EFFECT_SHELL_TRAP, .power = 150, .type = TYPE_FIRE, .accuracy = 100, .pp = 5, .secondaryEffectChance = 0, - .target = MOVE_TARGET_SELECTED, + .target = MOVE_TARGET_BOTH, .priority = -3, .flags = FLAG_PROTECT_AFFECTED | FLAG_KINGS_ROCK_AFFECTED, .split = SPLIT_SPECIAL, @@ -10913,7 +10913,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_COURT_CHANGE] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_COURT_CHANGE, .power = 0, .type = TYPE_NORMAL, .accuracy = 100, @@ -10983,7 +10983,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_SNAP_TRAP] = { - .effect = EFFECT_TRAP, //TODO: add case/effect + .effect = EFFECT_TRAP, .power = 35, .type = TYPE_GRASS, .accuracy = 100, @@ -11235,7 +11235,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_EXPANDING_FORCE] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_EXPANDING_FORCE, .power = 80, .type = TYPE_PSYCHIC, .accuracy = 100, @@ -11263,12 +11263,12 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_SCALE_SHOT] = { - .effect = EFFECT_PLACEHOLDER, //TODO (EFFECT_MULTI_HIT + ABILITY_WEAK_ARMOR, + .effect = EFFECT_SCALE_SHOT, .power = 25, .type = TYPE_DRAGON, .accuracy = 90, .pp = 20, - .secondaryEffectChance = 0, + .secondaryEffectChance = 100, .target = MOVE_TARGET_SELECTED, .priority = 0, .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED, @@ -11277,7 +11277,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_METEOR_BEAM] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_METEOR_BEAM, .power = 120, .type = TYPE_ROCK, .accuracy = 90, @@ -11333,7 +11333,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_RISING_VOLTAGE] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_RISING_VOLTAGE, .power = 70, .type = TYPE_ELECTRIC, .accuracy = 100, @@ -11417,7 +11417,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT] = [MOVE_CORROSIVE_GAS] = { - .effect = EFFECT_PLACEHOLDER, //TODO + .effect = EFFECT_PLACEHOLDER, // EFFECT_CORROSIVE_GAS, TODO .power = 0, .type = TYPE_POISON, .accuracy = 100,