diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 80e498d2b061..b3a5c777163f 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -72,6 +72,10 @@ .2byte \id .endm + .macro printsavedstring + printstring 0 + .endm + .macro printselectionstring id:req .byte 0x11 .2byte \id @@ -776,8 +780,21 @@ .byte 0x8b .endm - .macro unused0x8C + .macro twoturnmoveschargestringandanimation .byte 0x8c + .4byte 1f @animation then attack string + @default - attack string then animation + printsavedstring + waitmessage B_WAIT_TIME_LONG + attackanimation + waitanimation + goto 2f + 1: + attackanimation + waitanimation + printsavedstring + waitmessage B_WAIT_TIME_LONG + 2: .endm .macro setmultihitcounter value:req @@ -1035,12 +1052,20 @@ .4byte \failInstr .endm - .macro setsemiinvulnerablebit + .macro setsemiinvulnerablebit clear=FALSE .byte 0xc5 + .byte \clear .endm .macro clearsemiinvulnerablebit + setsemiinvulnerablebit TRUE + .endm + + .macro jumpifweathercheckchargeeffects battler:req, checkChargeTurnEffects:req, jumpInstr:req .byte 0xc6 + .byte \battler + .byte \checkChargeTurnEffects + .4byte \jumpInstr .endm .macro setminimize @@ -1337,13 +1362,6 @@ .4byte \jumpInstr .endm - .macro jumpifholdeffect battler:req, holdEffect:req, jumpInstr:req - callnative BS_JumpIfHoldEffect - .byte \battler - .2byte \holdEffect - .4byte \jumpInstr - .endm - .macro dostockpilestatchangeswearoff, battler:req, statChangeInstr:req callnative BS_DoStockpileStatChangesWearOff .byte \battler @@ -2005,10 +2023,15 @@ .4byte \jumpInstr .endm - .macro jumpifnoholdeffect battler:req, holdEffect:req, jumpInstr:req - various \battler, VARIOUS_JUMP_IF_NO_HOLD_EFFECT + .macro jumpifholdeffect battler:req, holdEffect:req, jumpInstr:req, equal=TRUE + various \battler, VARIOUS_JUMP_IF_HOLD_EFFECT .byte \holdEffect .4byte \jumpInstr + .byte \equal + .endm + + .macro jumpifnoholdeffect battler:req, holdEffect:req, jumpInstr:req + jumpifholdeffect \battler, \holdEffect, \jumpInstr, FALSE .endm .macro infatuatewithbattler battler:req, infatuateWith:req diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index c814b85510b4..6798c0e8e81f 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -585,46 +585,9 @@ BattleScript_BeakBlastBurn:: call BattleScript_MoveEffectBurn return -BattleScript_EffectMeteorBeam:: - @ DecideTurn - jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn - jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn - jumpifmove MOVE_METEOR_BEAM, BattleScript_SetStringMeteorBeam - jumpifmove MOVE_ELECTRO_SHOT, BattleScript_SetStringElectroShock -BattleScript_TryCharging: - call BattleScript_FirstChargingTurnMeteorBeam - jumpifmove MOVE_METEOR_BEAM, BattleScript_TryMeteorBeam - jumpifweatheraffected BS_ATTACKER, B_WEATHER_RAIN, BattleScript_TwoTurnMovesSecondTurn @ Check for move Electro Shot -BattleScript_TryMeteorBeam: - jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd - call BattleScript_PowerHerbActivation - goto BattleScript_TwoTurnMovesSecondTurn - -BattleScript_SetStringMeteorBeam: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_METEOR_BEAM - goto BattleScript_TryCharging - -BattleScript_SetStringElectroShock: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_ELECTRO_SHOCK - goto BattleScript_TryCharging - -BattleScript_FirstChargingTurnMeteorBeam:: - attackcanceler - flushtextbox - ppreduce - attackanimation - waitanimation - orword gHitMarker, HITMARKER_CHARGING - seteffectprimary MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER - copybyte cMULTISTRING_CHOOSER, sTWOTURN_STRINGID - printfromtable gFirstTurnOfTwoStringIds - waitmessage B_WAIT_TIME_LONG - setadditionaleffects @ only onChargeTurnOnly effects will work here - return - BattleScript_EffectSkyDrop:: - jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_SkyDropTurn2 attackcanceler + jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_SkyDropTurn2 ppreduce accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE attackstring @@ -638,16 +601,10 @@ BattleScript_EffectSkyDrop:: BattleScript_SkyDropWork: setskydrop - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SKY_DROP - setsemiinvulnerablebit - call BattleScriptFirstChargingTurnAfterAttackString + call BattleScript_FirstChargingTurnAfterAttackString goto BattleScript_MoveEnd BattleScript_SkyDropTurn2: - attackcanceler - setbyte sB_ANIM_TURN, 0x1 - clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING - orword gHitMarker, HITMARKER_NO_PPDEDUCT - clearsemiinvulnerablebit + call BattleScript_TwoTurnMovesSecondTurnRet attackstring clearskydrop BattleScript_SkyDropChangedTarget jumpiftype BS_TARGET, TYPE_FLYING, BattleScript_SkyDropFlyingType @@ -3485,29 +3442,6 @@ BattleScript_KOFail:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_TwoTurnMovesSecondTurn:: - attackcanceler - setbyte sB_ANIM_TURN, 1 - clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING - orword gHitMarker, HITMARKER_NO_PPDEDUCT - goto BattleScript_HitFromAccCheck - -BattleScriptFirstChargingTurn:: - attackcanceler - flushtextbox - ppreduce - attackstring -BattleScriptFirstChargingTurnAfterAttackString: - pause B_WAIT_TIME_LONG - copybyte cMULTISTRING_CHOOSER, sTWOTURN_STRINGID - printfromtable gFirstTurnOfTwoStringIds - waitmessage B_WAIT_TIME_LONG - attackanimation - waitanimation - orword gHitMarker, HITMARKER_CHARGING - seteffectprimary MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER - return - BattleScript_EffectSuperFang:: attackcanceler accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE @@ -3784,34 +3718,16 @@ BattleScript_PowerHerbActivation: BattleScript_EffectTwoTurnsAttack:: jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn - jumpifmove MOVE_SKY_ATTACK, BattleScript_EffectTwoTurnsAttackSkyAttack - jumpifmove MOVE_RAZOR_WIND, BattleScript_EffectTwoTurnsAttackRazorWind - jumpifmove MOVE_ICE_BURN, BattleScript_EffectTwoTurnsAttackIceBurn - jumpifmove MOVE_FREEZE_SHOCK, BattleScript_EffectTwoTurnsAttackFreezeShock - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_RAZOR_WIND -BattleScript_EffectTwoTurnsAttackContinue: - call BattleScriptFirstChargingTurn - jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd - call BattleScript_PowerHerbActivation - goto BattleScript_TwoTurnMovesSecondTurn -BattleScript_EffectTwoTurnsAttackSkyAttack: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SKY_ATTACK - goto BattleScript_EffectTwoTurnsAttackContinue -BattleScript_EffectTwoTurnsAttackRazorWind: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_RAZOR_WIND - goto BattleScript_EffectTwoTurnsAttackContinue -BattleScript_EffectTwoTurnsAttackIceBurn: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_RAZOR_WIND - goto BattleScript_EffectTwoTurnsAttackContinue -BattleScript_EffectTwoTurnsAttackFreezeShock: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_FREEZE_SHOCK - goto BattleScript_EffectTwoTurnsAttackContinue + jumpifweathercheckchargeeffects BS_ATTACKER, TRUE, BattleScript_EffectHit + call BattleScript_FirstChargingTurn + jumpifweathercheckchargeeffects BS_ATTACKER, FALSE, BattleScript_TwoTurnMovesSecondTurn + jumpifholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_TwoTurnMovesSecondPowerHerbActivates + goto BattleScript_MoveEnd BattleScript_EffectGeomancy:: jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_GeomancySecondTurn jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_GeomancySecondTurn - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_GEOMANCY - call BattleScriptFirstChargingTurn + call BattleScript_FirstChargingTurn jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd call BattleScript_PowerHerbActivation BattleScript_GeomancySecondTurn: @@ -3848,6 +3764,43 @@ BattleScript_GeomancyTrySpeed:: BattleScript_GeomancyEnd:: goto BattleScript_MoveEnd +BattleScript_FirstChargingTurn:: + attackcanceler +.if B_UPDATED_MOVE_DATA >= GEN_5 @ before Gen 5, charge moves did not print an attack string on the charge turn + flushtextbox + attackstring + waitmessage B_WAIT_TIME_LONG +.endif + ppreduce +BattleScript_FirstChargingTurnAfterAttackString: + setsemiinvulnerablebit @ only for moves with EFFECT_SEMI_INVULNERABLE/EFFECT_SKY_DROP + orword gHitMarker, HITMARKER_CHARGING + seteffectprimary MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER + twoturnmoveschargestringandanimation + setadditionaleffects @ only onChargeTurnOnly effects will work here + return + +BattleScript_TwoTurnMovesSecondPowerHerbActivates: + call BattleScript_PowerHerbActivation + call BattleScript_TwoTurnMovesSecondTurnRet + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE +.if B_UPDATED_MOVE_DATA < GEN_5 @ before Gen 5, charge moves did not print an attack string on the charge turn + attackstring +.endif + goto BattleScript_HitFromCritCalc + +BattleScript_TwoTurnMovesSecondTurn:: + attackcanceler + call BattleScript_TwoTurnMovesSecondTurnRet + orword gHitMarker, HITMARKER_NO_PPDEDUCT + goto BattleScript_HitFromAccCheck + +BattleScript_TwoTurnMovesSecondTurnRet: + setbyte sB_ANIM_TURN, 1 + clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING + clearsemiinvulnerablebit @ only for moves with EFFECT_SEMI_INVULNERABLE/EFFECT_SKY_DROP + return + BattleScript_EffectSubstitute:: attackcanceler ppreduce @@ -4178,17 +4131,6 @@ BattleScript_PartyHealEnd:: waitstate goto BattleScript_MoveEnd -BattleScript_EffectTripleKick:: - attackcanceler - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE - jumpifmove MOVE_TRIPLE_AXEL BS_TripleAxel - addbyte sTRIPLE_KICK_POWER, 10 @ triple kick gets +10 power - goto BattleScript_HitFromAtkString - -BS_TripleAxel: - addbyte sTRIPLE_KICK_POWER, 20 @ triple axel gets +20 power - goto BattleScript_HitFromAtkString - BattleScript_EffectMeanLook:: attackcanceler attackstring @@ -4654,23 +4596,6 @@ BattleScript_EffectMirrorCoat:: adjustdamage goto BattleScript_HitFromAtkAnimation -BattleScript_EffectSkullBash:: - jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn - jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SKULL_BASH - call BattleScriptFirstChargingTurn - setstatchanger STAT_DEF, 1, FALSE - statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_SkullBashEnd - jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_SkullBashEnd - setgraphicalstatchangevalues - playanimation BS_ATTACKER, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 - printfromtable gStatUpStringIds - waitmessage B_WAIT_TIME_LONG -BattleScript_SkullBashEnd:: - jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd - call BattleScript_PowerHerbActivation - goto BattleScript_TwoTurnMovesSecondTurn - BattleScript_EffectFutureSight:: attackcanceler attackstring @@ -4682,25 +4607,6 @@ BattleScript_EffectFutureSight:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_EffectGust:: - goto BattleScript_EffectHit - -BattleScript_EffectSolarBeam:: - jumpifweatheraffected BS_ATTACKER, B_WEATHER_SUN, BattleScript_SolarBeamOnFirstTurn -BattleScript_SolarBeamDecideTurn:: - jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn - jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SOLAR_BEAM - call BattleScriptFirstChargingTurn - jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd - call BattleScript_PowerHerbActivation - goto BattleScript_TwoTurnMovesSecondTurn -BattleScript_SolarBeamOnFirstTurn:: - orword gHitMarker, HITMARKER_CHARGING - seteffectprimary MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER - ppreduce - goto BattleScript_TwoTurnMovesSecondTurn - BattleScript_EffectTeleport:: .if B_TELEPORT_BEHAVIOR >= GEN_7 jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_EffectBatonPass @@ -4765,46 +4671,6 @@ BattleScript_BeatUpEnd:: end .endif -BattleScript_EffectSemiInvulnerable:: - jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_SecondTurnSemiInvulnerable - jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_SecondTurnSemiInvulnerable - jumpifmove MOVE_FLY, BattleScript_FirstTurnFly - jumpifmove MOVE_DIVE, BattleScript_FirstTurnDive - jumpifmove MOVE_BOUNCE, BattleScript_FirstTurnBounce - jumpifmove MOVE_PHANTOM_FORCE, BattleScript_FirstTurnPhantomForce - jumpifmove MOVE_SHADOW_FORCE, BattleScript_FirstTurnPhantomForce - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_DIG - goto BattleScript_FirstTurnSemiInvulnerable -BattleScript_FirstTurnBounce:: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_BOUNCE - goto BattleScript_FirstTurnSemiInvulnerable -BattleScript_FirstTurnDive:: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_DIVE - goto BattleScript_FirstTurnSemiInvulnerable -BattleScript_FirstTurnPhantomForce: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_PHANTOM_FORCE - goto BattleScript_FirstTurnSemiInvulnerable -BattleScript_FirstTurnFly:: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_FLY -BattleScript_FirstTurnSemiInvulnerable:: - call BattleScriptFirstChargingTurn - setsemiinvulnerablebit - jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd - call BattleScript_PowerHerbActivation -BattleScript_SecondTurnSemiInvulnerable:: - attackcanceler - setbyte sB_ANIM_TURN, 1 - clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING - orword gHitMarker, HITMARKER_NO_PPDEDUCT -BattleScript_SemiInvulnerableTryHit:: - accuracycheck BattleScript_SemiInvulnerableMiss, ACC_CURR_MOVE - clearsemiinvulnerablebit - goto BattleScript_HitFromAtkString - -BattleScript_SemiInvulnerableMiss:: - clearsemiinvulnerablebit - goto BattleScript_PrintMoveMissed - BattleScript_EffectDefenseCurl:: attackcanceler attackstring @@ -5544,9 +5410,6 @@ BattleScript_CosmicPowerTrySpDef:: BattleScript_CosmicPowerEnd:: goto BattleScript_MoveEnd -BattleScript_EffectSkyUppercut:: - goto BattleScript_EffectHit - BattleScript_EffectBulkUp:: attackcanceler attackstring diff --git a/include/battle.h b/include/battle.h index 5f18b5dcf21f..1fd7c5db00ad 100644 --- a/include/battle.h +++ b/include/battle.h @@ -54,7 +54,8 @@ struct __attribute__((packed, aligned(2))) BattleMoveEffect const u8 *battleScript; u16 battleTvScore:3; u16 encourageEncore:1; - u16 flags:12; // coming soon... + u16 semiInvulnerableEffect:1; + u16 flags:11; // coming soon... }; #define GET_MOVE_BATTLESCRIPT(move) gBattleMoveEffects[gMovesInfo[move].effect].battleScript @@ -846,10 +847,10 @@ struct BattleScripting s32 bideDmg; u8 multihitString[6]; bool8 expOnCatch; - u8 twoTurnsMoveStringId; + u8 unused; u8 animArg1; u8 animArg2; - u16 tripleKickPower; + u16 savedStringId; u8 moveendState; u8 savedStatChanger; // For further use, if attempting to change stat two times(ex. Moody) u8 shiftSwitched; // When the game tells you the next enemy's pokemon and you switch. Option for noobs but oh well. diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 8cfa5b2cb643..cfdb791e0ece 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -108,7 +108,7 @@ bool32 IsStatLoweringMoveEffect(u32 moveEffect); bool32 IsMoveRedirectionPrevented(u32 move, u32 atkAbility); bool32 IsMoveEncouragedToHit(u32 battlerAtk, u32 battlerDef, u32 move); bool32 IsHazardMoveEffect(u32 moveEffect); -bool32 IsChargingMove(u32 battlerAtk, u32 effect); +bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, u32 move); void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove, s32 *score); bool32 ShouldSetSandstorm(u32 battler, u32 ability, u32 holdEffect); bool32 ShouldSetHail(u32 battler, u32 ability, u32 holdEffect); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 44db02987f1f..3ff275d3bf89 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -611,7 +611,6 @@ extern const u8 BattleScript_EffectSleepTalk[]; extern const u8 BattleScript_EffectDestinyBond[]; extern const u8 BattleScript_EffectSpite[]; extern const u8 BattleScript_EffectHealBell[]; -extern const u8 BattleScript_EffectTripleKick[]; extern const u8 BattleScript_EffectMeanLook[]; extern const u8 BattleScript_EffectNightmare[]; extern const u8 BattleScript_EffectMinimize[]; diff --git a/include/battle_util.h b/include/battle_util.h index e9b0c6f472fc..02eec4870f1c 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -225,6 +225,7 @@ bool32 MoveHasMoveEffect(u32 move, u32 moveEffect); bool32 MoveHasMoveEffectWithChance(u32 move, u32 moveEffect, u32 chance); bool32 MoveHasMoveEffectSelf(u32 move, u32 moveEffect); bool32 MoveHasMoveEffectSelfArg(u32 move, u32 moveEffect, u32 argument); +bool32 MoveHasChargeTurnMoveEffect(u32 move); bool32 CanSleep(u32 battler); bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget); diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 004d6757019c..7390f3e05bfa 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -117,10 +117,8 @@ enum { EFFECT_BELLY_DRUM, EFFECT_PSYCH_UP, EFFECT_MIRROR_COAT, - EFFECT_SKULL_BASH, EFFECT_EARTHQUAKE, EFFECT_FUTURE_SIGHT, - EFFECT_GUST, EFFECT_SOLAR_BEAM, EFFECT_THUNDER, EFFECT_TELEPORT, @@ -172,7 +170,6 @@ enum { EFFECT_WEATHER_BALL, EFFECT_TICKLE, EFFECT_COSMIC_POWER, - EFFECT_SKY_UPPERCUT, EFFECT_BULK_UP, EFFECT_WATER_SPORT, EFFECT_CALM_MIND, @@ -318,7 +315,6 @@ enum { EFFECT_BOLT_BEAK, EFFECT_SKY_DROP, EFFECT_EXPANDING_FORCE, - EFFECT_METEOR_BEAM, EFFECT_RISING_VOLTAGE, EFFECT_BEAK_BLAST, EFFECT_COURT_CHANGE, diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 4e653cf9e663..6d635fe84884 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -6,10 +6,10 @@ #define sBIDE_DMG (gBattleScripting + 0x04) // bideDmg #define sMULTIHIT_STRING (gBattleScripting + 0x08) // multihitString #define sEXP_CATCH (gBattleScripting + 0x0E) // expOnCatch -#define sTWOTURN_STRINGID (gBattleScripting + 0x0F) // twoTurnsMoveStringId +#define sUNUSED (gBattleScripting + 0x0F) // unused #define sB_ANIM_ARG1 (gBattleScripting + 0x10) // animArg1 #define sB_ANIM_ARG2 (gBattleScripting + 0x11) // animArg2 -#define sTRIPLE_KICK_POWER (gBattleScripting + 0x12) // tripleKickPower +#define sSAVED_STRINID (gBattleScripting + 0x12) // savedStringId #define sMOVEEND_STATE (gBattleScripting + 0x14) // moveendState #define sSAVED_STAT_CHANGER (gBattleScripting + 0x15) // savedStatChanger #define sSHIFT_SWITCHED (gBattleScripting + 0x16) // shiftSwitched @@ -181,7 +181,7 @@ #define VARIOUS_TRY_FAIRY_LOCK 89 #define VARIOUS_JUMP_IF_NO_ALLY 90 #define VARIOUS_POISON_TYPE_IMMUNITY 91 -#define VARIOUS_JUMP_IF_NO_HOLD_EFFECT 92 +#define VARIOUS_JUMP_IF_HOLD_EFFECT 92 #define VARIOUS_INFATUATE_WITH_BATTLER 93 #define VARIOUS_SET_LAST_USED_ITEM 94 #define VARIOUS_PARALYZE_TYPE_IMMUNITY 95 @@ -220,25 +220,24 @@ #define VARIOUS_SET_SKY_DROP 128 #define VARIOUS_CLEAR_SKY_DROP 129 #define VARIOUS_SKY_DROP_YAWN 130 -#define VARIOUS_JUMP_IF_HOLD_EFFECT 131 -#define VARIOUS_CURE_CERTAIN_STATUSES 132 -#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 133 -#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 134 -#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 135 -#define VARIOUS_SAVE_BATTLER_ITEM 136 -#define VARIOUS_RESTORE_BATTLER_ITEM 137 -#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 138 -#define VARIOUS_SET_BEAK_BLAST 139 -#define VARIOUS_SWAP_SIDE_STATUSES 140 -#define VARIOUS_SWAP_STATS 141 -#define VARIOUS_TEATIME_INVUL 142 -#define VARIOUS_TEATIME_TARGETS 143 -#define VARIOUS_TRY_WIND_RIDER_POWER 144 -#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 145 -#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 146 -#define VARIOUS_STORE_HEALING_WISH 147 -#define VARIOUS_HIT_SWITCH_TARGET_FAILED 148 -#define VARIOUS_TRY_REVIVAL_BLESSING 149 +#define VARIOUS_CURE_CERTAIN_STATUSES 131 +#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 132 +#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 133 +#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 134 +#define VARIOUS_SAVE_BATTLER_ITEM 135 +#define VARIOUS_RESTORE_BATTLER_ITEM 136 +#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 137 +#define VARIOUS_SET_BEAK_BLAST 138 +#define VARIOUS_SWAP_SIDE_STATUSES 139 +#define VARIOUS_SWAP_STATS 140 +#define VARIOUS_TEATIME_INVUL 141 +#define VARIOUS_TEATIME_TARGETS 142 +#define VARIOUS_TRY_WIND_RIDER_POWER 143 +#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 144 +#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 145 +#define VARIOUS_STORE_HEALING_WISH 146 +#define VARIOUS_HIT_SWITCH_TARGET_FAILED 147 +#define VARIOUS_TRY_REVIVAL_BLESSING 148 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index dc971b7f21db..852e9a26c6b1 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -697,13 +697,14 @@ #define STRINGID_THESWAMPDISAPPEARED 695 #define STRINGID_PKMNTELLCHILLINGRECEPTIONJOKE 696 #define STRINGID_HOSPITALITYRESTORATION 697 -#define STRINGID_ELECTROSHOCKCHARGING 698 +#define STRINGID_ELECTROSHOTCHARGING 698 #define STRINGID_ITEMWASUSEDUP 699 #define STRINGID_ATTACKERLOSTITSTYPE 700 #define STRINGID_SHEDITSTAIL 701 -#define STRINGID_SUPERSWEETAROMAWAFTS 702 +#define STRINGID_CLOAKEDINAHARSHLIGHT 702 +#define STRINGID_SUPERSWEETAROMAWAFTS 703 -#define BATTLESTRINGS_COUNT 703 +#define BATTLESTRINGS_COUNT 704 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, @@ -747,22 +748,6 @@ #define B_MSG_LEECH_SEED_DRAIN 3 #define B_MSG_LEECH_SEED_OOZE 4 -// gFirstTurnOfTwoStringIds -#define B_MSG_TURN1_RAZOR_WIND 0 -#define B_MSG_TURN1_SOLAR_BEAM 1 -#define B_MSG_TURN1_SKULL_BASH 2 -#define B_MSG_TURN1_SKY_ATTACK 3 -#define B_MSG_TURN1_FLY 4 -#define B_MSG_TURN1_DIG 5 -#define B_MSG_TURN1_DIVE 6 -#define B_MSG_TURN1_BOUNCE 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 -#define B_MSG_TURN1_METEOR_BEAM 12 -#define B_MSG_TURN1_ELECTRO_SHOCK 13 - // gMoveWeatherChangeStringIds #define B_MSG_STARTED_RAIN 0 #define B_MSG_STARTED_DOWNPOUR 1 diff --git a/include/global.h b/include/global.h index 49095a5cd91b..17790a3cc1cc 100644 --- a/include/global.h +++ b/include/global.h @@ -83,6 +83,62 @@ // Extracts the lower 16 bits of a 32-bit number #define LOHALF(n) ((n) & 0xFFFF) +/* (Credit to MGriffin) A rather monstrous way of finding the set bit in a word. +Invalid input causes a compiler error. Sample: https://cexplore.karathan.at/z/x1hm7B */ +#define BIT_INDEX(n) \ + (n) == (1 << 0) ? 0 : \ + (n) == (1 << 1) ? 1 : \ + (n) == (1 << 2) ? 2 : \ + (n) == (1 << 3) ? 3 : \ + (n) == (1 << 4) ? 4 : \ + (n) == (1 << 5) ? 5 : \ + (n) == (1 << 6) ? 6 : \ + (n) == (1 << 7) ? 7 : \ + (n) == (1 << 8) ? 8 : \ + (n) == (1 << 9) ? 9 : \ + (n) == (1 << 10) ? 10 : \ + (n) == (1 << 11) ? 11 : \ + (n) == (1 << 12) ? 12 : \ + (n) == (1 << 13) ? 13 : \ + (n) == (1 << 14) ? 14 : \ + (n) == (1 << 15) ? 15 : \ + (n) == (1 << 16) ? 16 : \ + (n) == (1 << 17) ? 17 : \ + (n) == (1 << 18) ? 18 : \ + (n) == (1 << 19) ? 19 : \ + (n) == (1 << 20) ? 20 : \ + (n) == (1 << 21) ? 21 : \ + (n) == (1 << 22) ? 22 : \ + (n) == (1 << 23) ? 23 : \ + (n) == (1 << 24) ? 24 : \ + (n) == (1 << 25) ? 25 : \ + (n) == (1 << 26) ? 26 : \ + (n) == (1 << 27) ? 27 : \ + (n) == (1 << 28) ? 28 : \ + (n) == (1 << 29) ? 29 : \ + (n) == (1 << 30) ? 30 : \ + (n) == (1 << 31) ? 31 : \ + *(u32 *)NULL + +#define COMPRESS_BITS_0 0, 1 +#define COMPRESS_BITS_1 1, 1 +#define COMPRESS_BITS_2 2, 1 +#define COMPRESS_BITS_3 3, 1 +#define COMPRESS_BITS_4 4, 1 +#define COMPRESS_BITS_5 5, 1 +#define COMPRESS_BITS_6 6, 1 +#define COMPRESS_BITS_7 7, 1 + +/* Will try and compress a set bit (or up to three sequential bits) into a single byte +Input must be of the form (upper << lower) where upper can be up to 3, lower up to 31 */ +#define COMPRESS_BITS(_val) COMPRESS_BITS_STEP_2 _val +#define COMPRESS_BITS_STEP_2(_unpacked) COMPRESS_BITS_STEP_3(COMPRESS_BITS_## _unpacked) +#define COMPRESS_BITS_STEP_3(...) COMPRESS_BITS_STEP_4(__VA_ARGS__) +#define COMPRESS_BITS_STEP_4(upper, lower) (((upper % 8) << 5) + (BIT_INDEX(lower))) + +/* Will read a compressed bit stored by COMPRESS_BIT into a single byte */ +#define UNCOMPRESS_BITS(compressed) ((compressed >> 5) << (compressed & 0x1F)) + // There are many quirks in the source code which have overarching behavioral differences from // a number of other files. For example, diploma.c seems to declare rodata before each use while // other files declare out of order and must be at the beginning. There are also a number of diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 4945a7d87317..d3ef2dca0e89 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -813,7 +813,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) RETURN_SCORE_MINUS(20); // if target off screen and we go first, don't use move - if (IsChargingMove(battlerAtk, moveEffect) && CanTargetFaintAi(battlerDef, battlerAtk)) + if (IsTwoTurnNotSemiInvulnerableMove(battlerAtk, move) && CanTargetFaintAi(battlerDef, battlerAtk)) RETURN_SCORE_MINUS(10); // check if negates type @@ -3133,7 +3133,7 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) s32 score = 0; s32 leastHits = 1000; u16 *moves = GetMovesArray(battlerAtk); - bool8 isChargingMoveEffect[MAX_MON_MOVES]; + bool8 isTwoTurnNotSemiInvulnerableMove[MAX_MON_MOVES]; for (i = 0; i < MAX_MON_MOVES; i++) { @@ -3145,13 +3145,13 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) leastHits = noOfHits[i]; } viableMoveScores[i] = AI_SCORE_DEFAULT; - isChargingMoveEffect[i] = IsChargingMove(battlerAtk, gMovesInfo[moves[i]].effect); + isTwoTurnNotSemiInvulnerableMove[i] = IsTwoTurnNotSemiInvulnerableMove(battlerAtk, moves[i]); } else { noOfHits[i] = -1; viableMoveScores[i] = 0; - isChargingMoveEffect[i] = FALSE; + isTwoTurnNotSemiInvulnerableMove[i] = FALSE; } /* MgbaPrintf_("%S: required hits: %d Dmg: %d", gMoveNames[moves[i]], noOfHits[i], AI_DATA->simulatedDmg[battlerAtk][battlerDef][i]); @@ -3175,9 +3175,9 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) { multipleBestMoves = TRUE; // We need to make sure it's the current move which is objectively better. - if (isChargingMoveEffect[i] && !isChargingMoveEffect[currId]) + if (isTwoTurnNotSemiInvulnerableMove[i] && !isTwoTurnNotSemiInvulnerableMove[currId]) viableMoveScores[i] -= 3; - else if (!isChargingMoveEffect[i] && isChargingMoveEffect[currId]) + else if (!isTwoTurnNotSemiInvulnerableMove[i] && isTwoTurnNotSemiInvulnerableMove[currId]) viableMoveScores[currId] -= 3; switch (CompareMoveAccuracies(battlerAtk, battlerDef, currId, i)) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 4d3e69485d2a..fae5f684d77b 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2016,17 +2016,14 @@ bool32 HasSnatchAffectedMove(u32 battler) CHECK_MOVE_FLAG(snatchAffected); } -bool32 IsChargingMove(u32 battlerAtk, u32 effect) +bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, u32 move) { - switch (effect) + switch (gMovesInfo[move].effect) { case EFFECT_SOLAR_BEAM: - if (AI_GetWeather(AI_DATA) & B_WEATHER_SUN) - return FALSE; - case EFFECT_SKULL_BASH: - case EFFECT_METEOR_BEAM: case EFFECT_TWO_TURNS_ATTACK: - return !(AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB); + return !(AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB + || (AI_GetWeather(AI_DATA) & gMovesInfo[move].argument)); default: return FALSE; } diff --git a/src/battle_message.c b/src/battle_message.c index 258b103af7ab..97ea9794a56f 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -151,6 +151,7 @@ static const u8 sText_PkmnWhippedWhirlwind[] = _("{B_ATK_NAME_WITH_PREFIX} whipp static const u8 sText_PkmnTookSunlight[] = _("{B_ATK_NAME_WITH_PREFIX} took\nin sunlight!"); static const u8 sText_PkmnLoweredHead[] = _("{B_ATK_NAME_WITH_PREFIX} lowered\nits head!"); static const u8 sText_PkmnIsGlowing[] = _("{B_ATK_NAME_WITH_PREFIX} is glowing!"); +static const u8 sText_PkmnIsCloakedInAHarshLight[] = _("{B_ATK_NAME_WITH_PREFIX} became\ncloaked in a harsh light!"); static const u8 sText_PkmnFlewHigh[] = _("{B_ATK_NAME_WITH_PREFIX} flew\nup high!"); 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!"); @@ -834,7 +835,7 @@ static const u8 sText_TheSeaOfFireDisappeared[] = _("The sea of fire around {B_A static const u8 sText_SwampEnvelopedSide[] = _("A swamp enveloped\n{B_DEF_TEAM2} team!"); static const u8 sText_TheSwampDisappeared[] = _("The swamp around {B_ATK_TEAM2}\nteam disappeared!"); static const u8 sText_HospitalityRestoration[] = _("The {B_ATK_PARTNER_NAME} drank down all\nthe matcha that Sinistcha made!"); -static const u8 sText_ElectroShockCharging[] = _("{B_ATK_NAME_WITH_PREFIX} absorbed\nelectricity!"); +static const u8 sText_ElectroShotCharging[] = _("{B_ATK_NAME_WITH_PREFIX} absorbed\nelectricity!"); static const u8 sText_ItemWasUsedUp[] = _("The {B_LAST_ITEM}\nwas used up..."); static const u8 sText_AttackerLostItsType[] = _("{B_ATK_NAME_WITH_PREFIX} lost\nits {B_BUFF1} type!"); static const u8 sText_ShedItsTail[] = _("{B_ATK_NAME_WITH_PREFIX} shed its tail\nto create a decoy!"); @@ -844,7 +845,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { [STRINGID_SUPERSWEETAROMAWAFTS - BATTLESTRINGS_TABLE_START] = sText_SupersweetAromaWafts, [STRINGID_SHEDITSTAIL - BATTLESTRINGS_TABLE_START] = sText_ShedItsTail, - [STRINGID_ELECTROSHOCKCHARGING - BATTLESTRINGS_TABLE_START] = sText_ElectroShockCharging, + [STRINGID_ELECTROSHOTCHARGING - BATTLESTRINGS_TABLE_START] = sText_ElectroShotCharging, [STRINGID_HOSPITALITYRESTORATION - BATTLESTRINGS_TABLE_START] = sText_HospitalityRestoration, [STRINGID_THESWAMPDISAPPEARED - BATTLESTRINGS_TABLE_START] = sText_TheSwampDisappeared, [STRINGID_SWAMPENVELOPEDSIDE - BATTLESTRINGS_TABLE_START] = sText_SwampEnvelopedSide, @@ -1533,6 +1534,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP - BATTLESTRINGS_TABLE_START] = sText_TargetCoveredInStickyCandySyrup, [STRINGID_ITEMWASUSEDUP - BATTLESTRINGS_TABLE_START] = sText_ItemWasUsedUp, [STRINGID_ATTACKERLOSTITSTYPE - BATTLESTRINGS_TABLE_START] = sText_AttackerLostItsType, + [STRINGID_CLOAKEDINAHARSHLIGHT - BATTLESTRINGS_TABLE_START] = sText_PkmnIsCloakedInAHarshLight, }; const u16 gTrainerUsedItemStringIds[] = @@ -1758,25 +1760,6 @@ const u16 gStatDownStringIds[] = [B_MSG_STAT_FELL_EMPTY] = STRINGID_EMPTYSTRING3, }; -// Index read from sTWOTURN_STRINGID -const u16 gFirstTurnOfTwoStringIds[] = -{ - [B_MSG_TURN1_RAZOR_WIND] = STRINGID_PKMNWHIPPEDWHIRLWIND, - [B_MSG_TURN1_SOLAR_BEAM] = STRINGID_PKMNTOOKSUNLIGHT, - [B_MSG_TURN1_SKULL_BASH] = STRINGID_PKMNLOWEREDHEAD, - [B_MSG_TURN1_SKY_ATTACK] = STRINGID_PKMNISGLOWING, - [B_MSG_TURN1_FLY] = STRINGID_PKMNFLEWHIGH, - [B_MSG_TURN1_DIG] = STRINGID_PKMNDUGHOLE, - [B_MSG_TURN1_DIVE] = STRINGID_PKMNHIDUNDERWATER, - [B_MSG_TURN1_BOUNCE] = STRINGID_PKMNSPRANGUP, - [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, - [B_MSG_TURN1_METEOR_BEAM] = STRINGID_METEORBEAMCHARGING, - [B_MSG_TURN1_ELECTRO_SHOCK] = STRINGID_ELECTROSHOCKCHARGING, -}; - // Index copied from move's index in sTrappingMoves const u16 gWrappedStringIds[NUM_TRAPPING_MOVES] = { diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 5ea9d6563ac8..47246d935600 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -498,7 +498,7 @@ static void Cmd_setdrainedhp(void); static void Cmd_statbuffchange(void); static void Cmd_normalisebuffs(void); static void Cmd_setbide(void); -static void Cmd_unused0x8C(void); +static void Cmd_twoturnmoveschargestringandanimation(void); static void Cmd_setmultihitcounter(void); static void Cmd_initmultihitstring(void); static void Cmd_forcerandomswitch(void); @@ -556,7 +556,7 @@ static void Cmd_selectfirstvalidtarget(void); static void Cmd_trysetfutureattack(void); static void Cmd_trydobeatup(void); static void Cmd_setsemiinvulnerablebit(void); -static void Cmd_clearsemiinvulnerablebit(void); +static void Cmd_jumpifweathercheckchargeeffects(void); static void Cmd_setminimize(void); static void Cmd_sethail(void); static void Cmd_trymemento(void); @@ -757,7 +757,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_statbuffchange, //0x89 Cmd_normalisebuffs, //0x8A Cmd_setbide, //0x8B - Cmd_unused0x8C, //0x8C + Cmd_twoturnmoveschargestringandanimation, //0x8C Cmd_setmultihitcounter, //0x8D Cmd_initmultihitstring, //0x8E Cmd_forcerandomswitch, //0x8F @@ -815,7 +815,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_trysetfutureattack, //0xC3 Cmd_trydobeatup, //0xC4 Cmd_setsemiinvulnerablebit, //0xC5 - Cmd_clearsemiinvulnerablebit, //0xC6 + Cmd_jumpifweathercheckchargeeffects, //0xC6 Cmd_setminimize, //0xC7 Cmd_sethail, //0xC8 Cmd_trymemento, //0xC9 @@ -845,7 +845,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_unused2, //0xE1 Cmd_switchoutabilities, //0xE2 Cmd_jumpifhasnohp, //0xE3 - Cmd_jumpifnotcurrentmoveargtype, //0xE4 + Cmd_jumpifnotcurrentmoveargtype, //0xE4 Cmd_pickup, //0xE5 Cmd_unused3, //0xE6 Cmd_unused4, //0xE7 @@ -2622,7 +2622,7 @@ static void Cmd_printstring(void) if (gBattleControllerExecFlags == 0) { - u16 id = cmd->id; + u16 id = (cmd->id == 0 ? gBattleScripting.savedStringId : cmd->id); gBattlescriptCurrInstr = cmd->nextInstr; PrepareStringBattle(id, gBattlerAttacker); @@ -8694,16 +8694,19 @@ static void Cmd_various(void) } return; } - case VARIOUS_JUMP_IF_NO_HOLD_EFFECT: + case VARIOUS_JUMP_IF_HOLD_EFFECT: { - VARIOUS_ARGS(u8 holdEffect, const u8 *jumpInstr); - if (GetBattlerHoldEffect(battler, TRUE) != cmd->holdEffect) + VARIOUS_ARGS(u8 holdEffect, const u8 *jumpInstr, u8 equal); + if ((GetBattlerHoldEffect(battler, TRUE) == cmd->holdEffect) == cmd->equal) { + if (cmd->equal) + gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM gBattlescriptCurrInstr = cmd->jumpInstr; } else { - gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM + if (!cmd->equal) + gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM gBattlescriptCurrInstr = cmd->nextInstr; } return; @@ -11690,8 +11693,15 @@ static void Cmd_setbide(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused0x8C(void) +static void Cmd_twoturnmoveschargestringandanimation(void) { + CMD_ARGS(const u8 *animationThenStringPtr); + + gBattleScripting.savedStringId = LOHALF(gMovesInfo[gCurrentMove].argument); + if (B_UPDATED_MOVE_DATA < GEN_5 || MoveHasChargeTurnMoveEffect(gCurrentMove)) + gBattlescriptCurrInstr = cmd->animationThenStringPtr; + else + gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_setmultihitcounter(void) @@ -13646,36 +13656,36 @@ static void Cmd_trydobeatup(void) static void Cmd_setsemiinvulnerablebit(void) { - CMD_ARGS(); + CMD_ARGS(bool8 clear); - switch (gCurrentMove) + if (gBattleMoveEffects[gMovesInfo[gCurrentMove].effect].semiInvulnerableEffect == TRUE) { - case MOVE_FLY: - case MOVE_BOUNCE: - case MOVE_SKY_DROP: - gStatuses3[gBattlerAttacker] |= STATUS3_ON_AIR; - break; - case MOVE_DIG: - gStatuses3[gBattlerAttacker] |= STATUS3_UNDERGROUND; - break; - case MOVE_DIVE: - gStatuses3[gBattlerAttacker] |= STATUS3_UNDERWATER; - break; - case MOVE_PHANTOM_FORCE: - case MOVE_SHADOW_FORCE: - gStatuses3[gBattlerAttacker] |= STATUS3_PHANTOM_FORCE; - break; + u32 semiInvulnerableEffect = UNCOMPRESS_BITS(HIHALF(gMovesInfo[gCurrentMove].argument)); + if (cmd->clear) + gStatuses3[gBattlerAttacker] &= ~semiInvulnerableEffect; + else + gStatuses3[gBattlerAttacker] |= semiInvulnerableEffect; } gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_clearsemiinvulnerablebit(void) +static void Cmd_jumpifweathercheckchargeeffects(void) { - CMD_ARGS(); + CMD_ARGS(u8 battler, bool8 checkChargeTurnEffects, const u8 *jumpInstr); - gStatuses3[gBattlerAttacker] &= ~STATUS3_SEMI_INVULNERABLE; - gBattlescriptCurrInstr = cmd->nextInstr; + /* If this is NOT semi-invulnerable move and we don't have charge turn effects + yet to fire, we can fire the move right away so long as the weather matches + the argument and the battler is affected by it (not blocked by Cloud Nine etc) */ + if (gBattleMoveEffects[gMovesInfo[gCurrentMove].effect].semiInvulnerableEffect == FALSE + && !(cmd->checkChargeTurnEffects && MoveHasChargeTurnMoveEffect(gCurrentMove)) + && IsBattlerWeatherAffected(cmd->battler, HIHALF(gMovesInfo[gCurrentMove].argument))) + { + gBattleScripting.animTurn = 1; + gBattlescriptCurrInstr = cmd->jumpInstr; + } + else + gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_setminimize(void) @@ -15552,23 +15562,6 @@ void BS_JumpIfMoreThanHalfHP(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_JumpIfHoldEffect(void) -{ - u8 battler = gBattlescriptCurrInstr[5]; - u16 holdEffect = T1_READ_16(gBattlescriptCurrInstr + 6); - - if (GetBattlerHoldEffect(battler, TRUE) == holdEffect) - { - gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 8); - } - else - { - RecordItemEffectBattle(battler, holdEffect); - gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM - gBattlescriptCurrInstr += 12; - } -} - void BS_DoStockpileStatChangesWearOff(void) { NATIVE_ARGS(u8 battler, const u8 *statChangeInstr); diff --git a/src/battle_util.c b/src/battle_util.c index 735b286e24ae..f3321b71f867 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3192,7 +3192,6 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType) gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_DESTINY_BOND; gStatuses3[gBattlerAttacker] &= ~STATUS3_GRUDGE; gStatuses4[gBattlerAttacker] &= ~ STATUS4_GLAIVE_RUSH; - gBattleScripting.tripleKickPower = 0; gBattleStruct->atkCancellerTracker++; break; case CANCELLER_SKY_DROP: @@ -11050,6 +11049,17 @@ bool32 MoveHasMoveEffectSelfArg(u32 move, u32 moveEffect, u32 argument) return (gMovesInfo[move].argument == argument) && MoveHasMoveEffectSelf(move, moveEffect); } +bool32 MoveHasChargeTurnMoveEffect(u32 move) +{ + u8 i = 0; + for (i = 0; i < gMovesInfo[move].numAdditionalEffects; i++) + { + if (gMovesInfo[move].additionalEffects[i].onChargeTurnOnly) + return TRUE; + } + return FALSE; +} + bool8 CanMonParticipateInSkyBattle(struct Pokemon *mon) { u16 species = GetMonData(mon, MON_DATA_SPECIES); diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 09234f6fdf74..5a11be96cc20 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -383,7 +383,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_TWO_TURNS_ATTACK] = { .battleScript = BattleScript_EffectTwoTurnsAttack, - .battleTvScore = 0, // TODO: Assign points + .battleTvScore = 3, }, [EFFECT_SUBSTITUTE] = @@ -527,7 +527,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_TRIPLE_KICK] = { - .battleScript = BattleScript_EffectTripleKick, + .battleScript = BattleScript_EffectHit, .battleTvScore = 1, }, @@ -748,12 +748,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .encourageEncore = TRUE, }, - [EFFECT_SKULL_BASH] = - { - .battleScript = BattleScript_EffectSkullBash, - .battleTvScore = 3, - }, - [EFFECT_EARTHQUAKE] = { .battleScript = BattleScript_EffectHit, @@ -767,15 +761,9 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .encourageEncore = TRUE, }, - [EFFECT_GUST] = - { - .battleScript = BattleScript_EffectGust, - .battleTvScore = 1, - }, - [EFFECT_SOLAR_BEAM] = { - .battleScript = BattleScript_EffectSolarBeam, + .battleScript = BattleScript_EffectTwoTurnsAttack, .battleTvScore = 1, }, @@ -799,8 +787,9 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_SEMI_INVULNERABLE] = { - .battleScript = BattleScript_EffectSemiInvulnerable, + .battleScript = BattleScript_EffectTwoTurnsAttack, .battleTvScore = 3, + .semiInvulnerableEffect = TRUE, }, [EFFECT_DEFENSE_CURL] = @@ -1101,12 +1090,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .encourageEncore = TRUE, }, - [EFFECT_SKY_UPPERCUT] = - { - .battleScript = BattleScript_EffectSkyUppercut, - .battleTvScore = 1, - }, - [EFFECT_BULK_UP] = { .battleScript = BattleScript_EffectBulkUp, @@ -2016,6 +1999,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = { .battleScript = BattleScript_EffectSkyDrop, .battleTvScore = 0, // TODO: Assign points + .semiInvulnerableEffect = TRUE, }, [EFFECT_EXPANDING_FORCE] = @@ -2024,12 +2008,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleTvScore = 0, // TODO: Assign points }, - [EFFECT_METEOR_BEAM] = - { - .battleScript = BattleScript_EffectMeteorBeam, - .battleTvScore = 0, // TODO: Assign points - }, - [EFFECT_RISING_VOLTAGE] = { .battleScript = BattleScript_EffectHit, diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 10cda85a43c0..f0abe005156f 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -2,6 +2,7 @@ #include "constants/battle.h" #include "constants/battle_move_effects.h" #include "constants/battle_script_commands.h" +#include "constants/battle_string_ids.h" #include "constants/battle_z_move_effects.h" #include "constants/hold_effects.h" #include "constants/moves.h" @@ -21,6 +22,11 @@ #define BINDING_TURNS "2 to 5" #endif +/* First arg is the charge turn string id, second arg depends on effect +EFFECT_SEMI_INVULNERABLE/EFFECT_SKY_DROP: semi-invulnerable STATUS3 to apply to battler +EFFECT_TWO_TURNS_ATTACK/EFFECT_SOLAR_BEAM: weather in which to skip charge turn */ +#define TWO_TURN_ARG(stringid, ...) (stringid) __VA_OPT__(| ((__VA_ARGS__) << 16)) + // Shared Move Description entries const u8 gNotDoneYetDescription[] = _( @@ -427,6 +433,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .windMove = B_EXTRAPOLATED_MOVE_FLAGS, + .argument = TWO_TURN_ARG(STRINGID_PKMNWHIPPEDWHIRLWIND), .contestEffect = CONTEST_EFFECT_AFFECTED_BY_PREV_APPEAL, .contestCategory = CONTEST_CATEGORY_COOL, .contestComboStarterId = 0, @@ -484,7 +491,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Strikes the foe with a gust\n" "of wind whipped up by wings."), - .effect = EFFECT_GUST, + .effect = EFFECT_HIT, .power = 40, .type = TYPE_FLYING, .accuracy = 100, @@ -569,6 +576,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .assistBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNFLEWHIGH, COMPRESS_BITS(STATUS3_ON_AIR)), .contestEffect = CONTEST_EFFECT_AVOID_STARTLE, .contestCategory = CONTEST_CATEGORY_SMART, .contestComboStarterId = 0, @@ -1932,6 +1940,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .twoTurnMove = TRUE, .sleepTalkBanned = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNTOOKSUNLIGHT, B_WEATHER_SUN), .contestEffect = CONTEST_EFFECT_HIGHLY_APPEALING, .contestCategory = CONTEST_CATEGORY_COOL, .contestComboStarterId = 0, @@ -2291,6 +2300,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .instructBanned = TRUE, .assistBanned = TRUE, .skyBattleBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNDUGHOLE, COMPRESS_BITS(STATUS3_UNDERGROUND)), .contestEffect = CONTEST_EFFECT_AVOID_STARTLE, .contestCategory = CONTEST_CATEGORY_SMART, .contestComboStarterId = 0, @@ -3221,7 +3231,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Tucks in the head, then\n" "attacks on the next turn."), - .effect = EFFECT_SKULL_BASH, + .effect = EFFECT_TWO_TURNS_ATTACK, .power = B_UPDATED_MOVE_DATA >= GEN_6 ? 130 : 100, .type = TYPE_NORMAL, .accuracy = 100, @@ -3233,6 +3243,12 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .makesContact = TRUE, .sleepTalkBanned = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNLOWEREDHEAD), + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_DEF_PLUS_1, + .self = TRUE, + .onChargeTurnOnly = TRUE, + }), .contestEffect = CONTEST_EFFECT_BADLY_STARTLE_FRONT_MON, .contestCategory = CONTEST_CATEGORY_TOUGH, .contestComboStarterId = 0, @@ -3541,11 +3557,12 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, - .criticalHitStage = B_UPDATED_MOVE_DATA >= GEN_3 ? 1 : 0, + .criticalHitStage = B_UPDATED_MOVE_DATA >= GEN_3, .twoTurnMove = TRUE, .sheerForceBoost = TRUE, .sleepTalkBanned = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(B_UPDATED_MOVE_DATA >= GEN_4 ? STRINGID_CLOAKEDINAHARSHLIGHT : STRINGID_PKMNISGLOWING), #if B_UPDATED_MOVE_DATA >= GEN_3 .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_FLINCH, @@ -7140,6 +7157,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .instructBanned = TRUE, .assistBanned = TRUE, .skyBattleBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNHIDUNDERWATER, COMPRESS_BITS(STATUS3_UNDERWATER)), .contestEffect = CONTEST_EFFECT_AVOID_STARTLE_ONCE, .contestCategory = CONTEST_CATEGORY_BEAUTY, .contestComboStarterId = COMBO_STARTER_DIVE, @@ -8000,7 +8018,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "An uppercut thrown as if\n" "leaping into the sky."), - .effect = EFFECT_SKY_UPPERCUT, + .effect = EFFECT_HIT, .power = 85, .type = TYPE_FIGHTING, .accuracy = 90, @@ -8312,6 +8330,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .assistBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNSPRANGUP, COMPRESS_BITS(STATUS3_ON_AIR)), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_PARALYSIS, .chance = 30, @@ -11319,6 +11338,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .assistBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_VANISHEDINSTANTLY, COMPRESS_BITS(STATUS3_PHANTOM_FORCE)), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_FEINT, }), @@ -12252,6 +12272,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .assistBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNTOOKTARGETHIGH, COMPRESS_BITS(STATUS3_ON_AIR)), .contestEffect = CONTEST_EFFECT_AVOID_STARTLE, .contestCategory = CONTEST_CATEGORY_SMART, .contestComboStarterId = 0, @@ -13318,6 +13339,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .metronomeBanned = TRUE, .sleepTalkBanned = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_CLOAKEDINAFREEZINGLIGHT), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_PARALYSIS, .chance = 30, @@ -13347,6 +13369,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .metronomeBanned = TRUE, .sleepTalkBanned = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_CLOAKEDINAFREEZINGLIGHT), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_BURN, .chance = 30, @@ -13648,6 +13671,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .assistBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_VANISHEDINSTANTLY, COMPRESS_BITS(STATUS3_PHANTOM_FORCE)), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_FEINT, }), @@ -14475,6 +14499,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .skyBattleBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKNMABSORBINGPOWER), .contestEffect = CONTEST_EFFECT_IMPROVE_CONDITION_PREVENT_NERVOUSNESS, .contestCategory = CONTEST_CATEGORY_CUTE, .contestComboStarterId = 0, @@ -15218,6 +15243,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .slicingMove = TRUE, .sleepTalkBanned = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNTOOKSUNLIGHT, B_WEATHER_SUN), .contestEffect = CONTEST_EFFECT_HIGHLY_APPEALING, .contestCategory = CONTEST_CATEGORY_TOUGH, .contestComboStarterId = 0, @@ -17458,7 +17484,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "A 2-turn move that raises\n" "Sp. Attack before attacking."), - .effect = EFFECT_METEOR_BEAM, + .effect = EFFECT_TWO_TURNS_ATTACK, .power = 120, .type = TYPE_ROCK, .accuracy = 90, @@ -17468,6 +17494,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .category = DAMAGE_CATEGORY_SPECIAL, .twoTurnMove = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_METEORBEAMCHARGING), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_SP_ATK_PLUS_1, .self = TRUE, @@ -19714,7 +19741,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Absorbs electricity in one turn,\n" "then attacks next turn."), - .effect = EFFECT_METEOR_BEAM, + .effect = EFFECT_TWO_TURNS_ATTACK, .power = 130, .type = TYPE_ELECTRIC, .accuracy = 100, @@ -19723,6 +19750,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, .sheerForceBoost = TRUE, + .argument = TWO_TURN_ARG(STRINGID_ELECTROSHOTCHARGING, B_WEATHER_RAIN), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_SP_ATK_PLUS_1, .self = TRUE, diff --git a/test/battle/ability/sheer_force.c b/test/battle/ability/sheer_force.c index be115ee918ba..e50ead4bdb18 100644 --- a/test/battle/ability/sheer_force.c +++ b/test/battle/ability/sheer_force.c @@ -28,8 +28,7 @@ SINGLE_BATTLE_TEST("Sheer Force boosts power, but removes secondary effects of m TURN { MOVE(opponent, MOVE_QUICK_ATTACK); MOVE(player, move); } else TURN { MOVE(player, move); } - if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK || gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE - || gMovesInfo[move].effect == EFFECT_METEOR_BEAM) { + if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK || gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE) { TURN { SKIP_TURN(player); } TURN { ; } } diff --git a/test/battle/ai.c b/test/battle/ai.c index 7d7da1094783..7b9d07429e19 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -292,16 +292,39 @@ AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking // Psychic and Solar Beam are chosen because user is holding Power Herb PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SOLAR_BEAM; } - // Psychic and Skull Bash are chosen because user is holding Power Herb - PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; - expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SKULL_BASH; } // Skull Bash is chosen because it's the most accurate and is holding Power Herb PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_SLAM; move4 = MOVE_DOUBLE_EDGE; expectedMove = MOVE_SKULL_BASH; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { HP(5); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GEODUDE) { Moves(move1, move2, move3, move4); Ability(abilityAtk); Item(holdItemAtk); } + } WHEN { + TURN { if (expectedMove2 == MOVE_NONE) { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); } + else {EXPECT_MOVES(opponent, expectedMove, expectedMove2); SCORE_EQ(opponent, expectedMove, expectedMove2); SEND_OUT(player, 1);} + } + } + SCENE { + MESSAGE("Wobbuffet fainted!"); + } +} + +AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking into account accuracy and move effect failing") +{ + u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE; + u16 expectedMove, expectedMove2 = MOVE_NONE; + u16 abilityAtk = ABILITY_NONE, holdItemAtk = ITEM_NONE; + + // Fiery Dance and Skull Bash are chosen because user is holding Power Herb + PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_FIERY_DANCE; move4 = MOVE_DOUBLE_EDGE; + expectedMove = MOVE_FIERY_DANCE; expectedMove2 = MOVE_SKULL_BASH; } // Crabhammer is chosen even if Skull Bash is more accurate, the user has no Power Herb PARAMETRIZE { abilityAtk = ABILITY_STURDY; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_SLAM; move4 = MOVE_CRABHAMMER; expectedMove = MOVE_CRABHAMMER; } + KNOWN_FAILING; GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_WOBBUFFET) { HP(5); } diff --git a/test/battle/move_effect/meteor_beam.c b/test/battle/move_effect/meteor_beam.c deleted file mode 100644 index d9ae6fb99efb..000000000000 --- a/test/battle/move_effect/meteor_beam.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -ASSUMPTIONS -{ - ASSUME(gMovesInfo[MOVE_ELECTRO_SHOT].effect == EFFECT_METEOR_BEAM); -} - -SINGLE_BATTLE_TEST("Electro Shot needs a charging Turn") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_ELECTRO_SHOT); } - TURN { SKIP_TURN(player); } - } SCENE { - // Charging turn - ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player); - MESSAGE("Wobbuffet absorbed electricity!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Sp. Atk rose!"); - // Attack turn - MESSAGE("Wobbuffet used Electro Shot!"); - } -} - -SINGLE_BATTLE_TEST("Electro Shot doesn't need to charge when it's raining") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, MOVE_RAIN_DANCE); MOVE(player, MOVE_ELECTRO_SHOT); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, opponent); - ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player); - MESSAGE("Wobbuffet absorbed electricity!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Sp. Atk rose!"); - MESSAGE("Wobbuffet used Electro Shot!"); - } -} - -SINGLE_BATTLE_TEST("Electro Shot doesn't need to charge with Power Herb") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_ELECTRO_SHOT); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player); - MESSAGE("Wobbuffet absorbed electricity!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Sp. Atk rose!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); - MESSAGE("Wobbuffet became fully charged due to its Power Herb!"); - MESSAGE("Wobbuffet used Electro Shot!"); - } -} diff --git a/test/battle/move_effect/semi_invulnerable_moves.c b/test/battle/move_effect/semi_invulnerable_moves.c new file mode 100644 index 000000000000..97760225d132 --- /dev/null +++ b/test/battle/move_effect/semi_invulnerable_moves.c @@ -0,0 +1,250 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_FLY].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_FLY].argument)) == STATUS3_ON_AIR); + ASSUME(gMovesInfo[MOVE_DIG].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_DIG].argument)) == STATUS3_UNDERGROUND); + ASSUME(gMovesInfo[MOVE_BOUNCE].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_BOUNCE].argument)) == STATUS3_ON_AIR); + ASSUME(gMovesInfo[MOVE_DIVE].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_DIVE].argument)) == STATUS3_UNDERWATER); + ASSUME(gMovesInfo[MOVE_PHANTOM_FORCE].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_PHANTOM_FORCE].argument)) == STATUS3_PHANTOM_FORCE); + ASSUME(gMovesInfo[MOVE_SHADOW_FORCE].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_SHADOW_FORCE].argument)) == STATUS3_PHANTOM_FORCE); +} + +SINGLE_BATTLE_TEST("Semi-invulnerable moves make the user semi-invulnerable turn 1, then strike turn 2") +{ + u16 move; + + PARAMETRIZE { move = MOVE_FLY; } + PARAMETRIZE { move = MOVE_DIG; } + PARAMETRIZE { move = MOVE_BOUNCE; } + PARAMETRIZE { move = MOVE_DIVE; } + PARAMETRIZE { move = MOVE_PHANTOM_FORCE; } + PARAMETRIZE { move = MOVE_SHADOW_FORCE; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); MOVE(opponent, MOVE_AERIAL_ACE); } + TURN { SKIP_TURN(player); } + } SCENE { + // Charging turn + if (B_UPDATED_MOVE_DATA >= GEN_5) + { + switch (move) + { + case MOVE_FLY: + NOT MESSAGE("Wobbuffet flew up high!"); + MESSAGE("Wobbuffet used Fly!"); + break; + case MOVE_DIG: + NOT MESSAGE("Wobbuffet dug a hole!"); + MESSAGE("Wobbuffet used Dig!"); + break; + case MOVE_BOUNCE: + NOT MESSAGE("Wobbuffet sprang up!"); + MESSAGE("Wobbuffet used Bounce!"); + break; + case MOVE_DIVE: + NOT MESSAGE("Wobbuffet hid underwater!"); + MESSAGE("Wobbuffet used Dive!"); + break; + case MOVE_PHANTOM_FORCE: + NOT MESSAGE("Wobbuffet vanished instantly!"); + MESSAGE("Wobbuffet used PhantomForce!"); + break; + case MOVE_SHADOW_FORCE: + NOT MESSAGE("Wobbuffet vanished instantly!"); + MESSAGE("Wobbuffet used Shadow Force!"); + break; + } + } else { + ANIMATION(ANIM_TYPE_MOVE, move, player); + } + if (B_UPDATED_MOVE_DATA < GEN_5) + { + switch (move) + { + case MOVE_FLY: + MESSAGE("Wobbuffet flew up high!"); + break; + case MOVE_DIG: + MESSAGE("Wobbuffet dug a hole!"); + break; + case MOVE_BOUNCE: + MESSAGE("Wobbuffet sprang up!"); + break; + case MOVE_DIVE: + MESSAGE("Wobbuffet hid underwater!"); + break; + case MOVE_PHANTOM_FORCE: + case MOVE_SHADOW_FORCE: + MESSAGE("Wobbuffet vanished instantly!"); + break; + } + } + else + ANIMATION(ANIM_TYPE_MOVE, move, player); + + // Aerial Ace cannot miss unless the target is semi-invulnerable + MESSAGE("Foe Wobbuffet used Aerial Ace!"); + MESSAGE("Foe Wobbuffet's attack missed!"); + // Attack turn + switch (move) + { + case MOVE_FLY: + MESSAGE("Wobbuffet used Fly!"); + break; + case MOVE_DIG: + MESSAGE("Wobbuffet used Dig!"); + break; + case MOVE_BOUNCE: + MESSAGE("Wobbuffet used Bounce!"); + break; + case MOVE_DIVE: + MESSAGE("Wobbuffet used Dive!"); + break; + case MOVE_PHANTOM_FORCE: + MESSAGE("Wobbuffet used PhantomForce!"); + break; + case MOVE_SHADOW_FORCE: + MESSAGE("Wobbuffet used Shadow Force!"); + break; + } + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Semi-invulnerable moves don't need to charge with Power Herb") +{ + u16 move; + + PARAMETRIZE { move = MOVE_FLY; } + PARAMETRIZE { move = MOVE_DIG; } + PARAMETRIZE { move = MOVE_BOUNCE; } + PARAMETRIZE { move = MOVE_DIVE; } + PARAMETRIZE { move = MOVE_PHANTOM_FORCE; } + PARAMETRIZE { move = MOVE_SHADOW_FORCE; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + // Charging turn + if (B_UPDATED_MOVE_DATA >= GEN_5) + { + switch (move) + { + case MOVE_FLY: + NOT MESSAGE("Wobbuffet flew up high!"); + MESSAGE("Wobbuffet used Fly!"); + break; + case MOVE_DIG: + NOT MESSAGE("Wobbuffet dug a hole!"); + MESSAGE("Wobbuffet used Dig!"); + break; + case MOVE_BOUNCE: + NOT MESSAGE("Wobbuffet sprang up!"); + MESSAGE("Wobbuffet used Bounce!"); + break; + case MOVE_DIVE: + NOT MESSAGE("Wobbuffet hid underwater!"); + MESSAGE("Wobbuffet used Dive!"); + break; + case MOVE_PHANTOM_FORCE: + NOT MESSAGE("Wobbuffet vanished instantly!"); + MESSAGE("Wobbuffet used PhantomForce!"); + break; + case MOVE_SHADOW_FORCE: + NOT MESSAGE("Wobbuffet vanished instantly!"); + MESSAGE("Wobbuffet used Shadow Force!"); + break; + } + } else { + ANIMATION(ANIM_TYPE_MOVE, move, player); + } + if (B_UPDATED_MOVE_DATA < GEN_5) + { + switch (move) + { + case MOVE_FLY: + MESSAGE("Wobbuffet flew up high!"); + break; + case MOVE_DIG: + MESSAGE("Wobbuffet dug a hole!"); + break; + case MOVE_BOUNCE: + MESSAGE("Wobbuffet sprang up!"); + break; + case MOVE_DIVE: + MESSAGE("Wobbuffet hid underwater!"); + break; + case MOVE_PHANTOM_FORCE: + case MOVE_SHADOW_FORCE: + MESSAGE("Wobbuffet vanished instantly!"); + break; + } + } + else + ANIMATION(ANIM_TYPE_MOVE, move, player); + MESSAGE("Wobbuffet became fully charged due to its Power Herb!"); + if (B_UPDATED_MOVE_DATA < GEN_5) + { + switch (move) + { + case MOVE_FLY: + MESSAGE("Wobbuffet used Fly!"); + break; + case MOVE_DIG: + MESSAGE("Wobbuffet used Dig!"); + break; + case MOVE_BOUNCE: + MESSAGE("Wobbuffet used Bounce!"); + break; + case MOVE_DIVE: + MESSAGE("Wobbuffet used Dive!"); + break; + case MOVE_PHANTOM_FORCE: + MESSAGE("Wobbuffet used PhantomForce!"); + break; + case MOVE_SHADOW_FORCE: + MESSAGE("Wobbuffet used Shadow Force!"); + break; + } + } + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + } +} + +// No way to apply this test with Shadow Force/Phantom Force +SINGLE_BATTLE_TEST("Semi-invulnerable moves apply a status that won't block certain moves") +{ + u16 move, opMove; + + PARAMETRIZE { move = MOVE_FLY; opMove = MOVE_SKY_UPPERCUT; } + PARAMETRIZE { move = MOVE_DIG; opMove = MOVE_EARTHQUAKE; } + PARAMETRIZE { move = MOVE_BOUNCE; opMove = MOVE_THUNDER; } + PARAMETRIZE { move = MOVE_DIVE; opMove = MOVE_SURF; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); MOVE(opponent, opMove); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + ANIMATION(ANIM_TYPE_MOVE, opMove, opponent); + HP_BAR(player); + } +} diff --git a/test/battle/move_effect/solar_beam.c b/test/battle/move_effect/solar_beam.c deleted file mode 100644 index 11edb61ba08f..000000000000 --- a/test/battle/move_effect/solar_beam.c +++ /dev/null @@ -1,167 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -ASSUMPTIONS -{ - ASSUME(gMovesInfo[MOVE_SOLAR_BEAM].effect == EFFECT_SOLAR_BEAM); - ASSUME(gMovesInfo[MOVE_SOLAR_BLADE].effect == EFFECT_SOLAR_BEAM); -} - -SINGLE_BATTLE_TEST("Solar Beam and Solar Blade can be used instantly in Sunlight") -{ - u32 move; - PARAMETRIZE { move = MOVE_SOLAR_BEAM; } - PARAMETRIZE { move = MOVE_SOLAR_BLADE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, MOVE_SUNNY_DAY); MOVE(player, move); } - } SCENE { - NOT MESSAGE("Wobbuffet took in sunlight!"); - } -} - -SINGLE_BATTLE_TEST("Solar Beam's power is halved in Rain", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_RAIN_DANCE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Blade's power is halved in Rain", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_RAIN_DANCE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WYNAUT); - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Beam's power is halved in a Sandstorm", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SANDSTORM; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Blade's power is halved in a Sandstorm", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SANDSTORM; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Beam's power is halved in Hail", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_HAIL; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Blade's power is halved in Hail", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_HAIL; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Beam's power is halved in Snow", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SNOWSCAPE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Blade's power is halved in Snow", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SNOWSCAPE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WYNAUT); - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} diff --git a/test/battle/move_effect/two_turn_moves.c b/test/battle/move_effect/two_turn_moves.c new file mode 100644 index 000000000000..301165675683 --- /dev/null +++ b/test/battle/move_effect/two_turn_moves.c @@ -0,0 +1,449 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_RAZOR_WIND].effect == EFFECT_TWO_TURNS_ATTACK); + ASSUME(gMovesInfo[MOVE_SKULL_BASH].effect == EFFECT_TWO_TURNS_ATTACK); + ASSUME(MoveHasMoveEffectSelf(MOVE_SKULL_BASH, MOVE_EFFECT_DEF_PLUS_1) == TRUE); + ASSUME(gMovesInfo[MOVE_SKY_ATTACK].effect == EFFECT_TWO_TURNS_ATTACK); + + // Solar Beam - check for sun + ASSUME(gMovesInfo[MOVE_SOLAR_BEAM].effect == EFFECT_SOLAR_BEAM); + ASSUME(HIHALF(gMovesInfo[MOVE_SOLAR_BLADE].argument) == B_WEATHER_SUN); + ASSUME(gMovesInfo[MOVE_SOLAR_BLADE].effect == EFFECT_SOLAR_BEAM); + ASSUME(HIHALF(gMovesInfo[MOVE_SOLAR_BLADE].argument) == B_WEATHER_SUN); + + // Electro shot - check for rain + ASSUME(HIHALF(gMovesInfo[MOVE_ELECTRO_SHOT].argument) == B_WEATHER_RAIN); + ASSUME(gMovesInfo[MOVE_ELECTRO_SHOT].effect == EFFECT_TWO_TURNS_ATTACK); + ASSUME(MoveHasMoveEffectSelf(MOVE_ELECTRO_SHOT, MOVE_EFFECT_SP_ATK_PLUS_1) == TRUE); +} + +SINGLE_BATTLE_TEST("Razor Wind needs a charging turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_RAZOR_WIND); } + TURN { SKIP_TURN(player); } + } SCENE { + // Charging turn + if (B_UPDATED_MOVE_DATA >= GEN_5) { + NOT MESSAGE("Wobbuffet whipped up a whirlwind!"); + MESSAGE("Wobbuffet used Razor Wind!"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player); + } + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet whipped up a whirlwind!"); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player); + // Attack turn + MESSAGE("Wobbuffet used Razor Wind!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Razor Wind doesn't need to charge with Power Herb") +{ + KNOWN_FAILING; + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_RAZOR_WIND); } + } SCENE { + if (B_UPDATED_MOVE_DATA >= GEN_5) { + NOT MESSAGE("Wobbuffet whipped up a whirlwind!"); + MESSAGE("Wobbuffet used Razor Wind!"); + } else + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player); + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet whipped up a whirlwind!"); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet became fully charged due to its Power Herb!"); + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet used Razor Wind!"); + // For some reason, this breaks with and only with Razor Wind... + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Skull Bash needs a charging turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SKULL_BASH); } + TURN { SKIP_TURN(player); } + } SCENE { + // Charging turn + if (B_UPDATED_MOVE_DATA >= GEN_5) { + NOT MESSAGE("Wobbuffet lowered its head!"); + MESSAGE("Wobbuffet used Skull Bash!"); + } else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player); + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet lowered its head!"); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Defense rose!"); + // Attack turn + MESSAGE("Wobbuffet used Skull Bash!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Skull Bash doesn't need to charge with Power Herb") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SKULL_BASH); } + } SCENE { + if (B_UPDATED_MOVE_DATA >= GEN_5) { + NOT MESSAGE("Wobbuffet lowered its head!"); + MESSAGE("Wobbuffet used Skull Bash!"); + } else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player); + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet lowered its head!"); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Defense rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet became fully charged due to its Power Herb!"); + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet used Skull Bash!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Sky Attack needs a charging turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SKY_ATTACK); } + TURN { SKIP_TURN(player); } + } SCENE { + // Charging turn + if (B_UPDATED_MOVE_DATA >= GEN_5) { + NONE_OF { + MESSAGE("Wobbuffet became cloaked in a harsh light!"); + MESSAGE("Wobbuffet is glowing!"); + } + MESSAGE("Wobbuffet used Sky Attack!"); + } else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player); + if (B_UPDATED_MOVE_DATA < GEN_4) + MESSAGE("Wobbuffet is glowing!"); + else if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet became cloaked in a harsh light!"); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player); + // Attack turn + MESSAGE("Wobbuffet used Sky Attack!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Sky Attack doesn't need to charge with Power Herb") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SKY_ATTACK); } + } SCENE { + if (B_UPDATED_MOVE_DATA >= GEN_5) { + NONE_OF { + MESSAGE("Wobbuffet became cloaked in a harsh light!"); + MESSAGE("Wobbuffet is glowing!"); + } + MESSAGE("Wobbuffet used Sky Attack!"); + } else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player); + if (B_UPDATED_MOVE_DATA < GEN_4) + MESSAGE("Wobbuffet is glowing!"); + else if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet became cloaked in a harsh light!"); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet became fully charged due to its Power Herb!"); + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet used Sky Attack!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Solar Beam and Solar Blade can be used instantly in Sunlight") +{ + u32 move1, move2; + PARAMETRIZE { move1 = MOVE_SPLASH; move2 = MOVE_SOLAR_BEAM; } + PARAMETRIZE { move1 = MOVE_SUNNY_DAY; move2 = MOVE_SOLAR_BEAM; } + PARAMETRIZE { move1 = MOVE_SPLASH; move2 = MOVE_SOLAR_BLADE; } + PARAMETRIZE { move1 = MOVE_SUNNY_DAY; move2 = MOVE_SOLAR_BLADE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move1); MOVE(player, move2); } + TURN { SKIP_TURN(player); } + } SCENE { + if (move1 == MOVE_SUNNY_DAY) { + NOT MESSAGE("Wobbuffet took in sunlight!"); + } else { + if (move2 == MOVE_SOLAR_BEAM) { + if (B_UPDATED_MOVE_DATA >= GEN_5) + { + MESSAGE("Wobbuffet used Solar Beam!"); + MESSAGE("Wobbuffet took in sunlight!"); + ANIMATION(ANIM_TYPE_MOVE, move2, player); + } else { + NOT MESSAGE("Wobbuffet used Solar Beam!"); + ANIMATION(ANIM_TYPE_MOVE, move2, player); + MESSAGE("Wobbuffet took in sunlight!"); + } + MESSAGE("Wobbuffet used Solar Beam!"); + } else { + if (B_UPDATED_MOVE_DATA >= GEN_5) { + MESSAGE("Wobbuffet used Solar Blade!"); + MESSAGE("Wobbuffet took in sunlight!"); + ANIMATION(ANIM_TYPE_MOVE, move2, player); + } else { + NOT MESSAGE("Wobbuffet used Solar Blade!"); + ANIMATION(ANIM_TYPE_MOVE, move2, player); + MESSAGE("Wobbuffet took in sunlight!"); + } + MESSAGE("Wobbuffet used Solar Blade!"); + } + ANIMATION(ANIM_TYPE_MOVE, move2, player); + HP_BAR(opponent); + } + } +} + +SINGLE_BATTLE_TEST("Solar Beam's power is halved in Rain", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_RAIN_DANCE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Blade's power is halved in Rain", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_RAIN_DANCE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Beam's power is halved in a Sandstorm", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SANDSTORM; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Blade's power is halved in a Sandstorm", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SANDSTORM; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Beam's power is halved in Hail", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_HAIL; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Blade's power is halved in Hail", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_HAIL; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Beam's power is halved in Snow", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Blade's power is halved in Snow", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Electro Shot needs a charging Turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ELECTRO_SHOT); } + TURN { SKIP_TURN(player); } + } SCENE { + // Charging turn + MESSAGE("Wobbuffet used Electro Shot!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player); + MESSAGE("Wobbuffet absorbed electricity!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Sp. Atk rose!"); + // Attack turn + MESSAGE("Wobbuffet used Electro Shot!"); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Electro Shot doesn't need to charge when it's raining") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_RAIN_DANCE); MOVE(player, MOVE_ELECTRO_SHOT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, opponent); + MESSAGE("Wobbuffet used Electro Shot!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player); + MESSAGE("Wobbuffet absorbed electricity!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Sp. Atk rose!"); + NONE_OF { + MESSAGE("Wobbuffet used Electro Shot!"); + } + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Electro Shot doesn't need to charge with Power Herb") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ELECTRO_SHOT); } + } SCENE { + MESSAGE("Wobbuffet used Electro Shot!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player); + MESSAGE("Wobbuffet absorbed electricity!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Sp. Atk rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet became fully charged due to its Power Herb!"); + NONE_OF { + MESSAGE("Wobbuffet used Electro Shot!"); + } + HP_BAR(opponent); + } +}