diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 12439efc4a..9d61889b40 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1670,6 +1670,10 @@ callnative BS_DamageToQuarterTargetHP .endm + .macro ficklebeamdamagecalculation + callnative BS_FickleBeamDamageCalculation + .endm + @ various command changed to more readable macros .macro cancelmultiturnmoves battler:req various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index f842b8c113..2f1a3cb73c 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -16219,6 +16219,12 @@ Move_RAGING_BULL:: loadspritegfx ANIM_TAG_IMPACT loadspritegfx ANIM_TAG_ANGER loadspritegfx ANIM_TAG_BREATH + loadspritegfx ANIM_TAG_BLUE_LIGHT_WALL + loadspritegfx ANIM_TAG_TORN_METAL + choosetwoturnanim RagingBullNormal, RagingBullShatteredWall +RagingBullNormal: + monbg ANIM_TARGET + setalpha 12, 8 createsprite gBreathPuffSpriteTemplate, ANIM_ATTACKER, 2 loopsewithpan SE_M_SWAGGER, SOUND_PAN_ATTACKER, 4, 2 createsprite gAngerMarkSpriteTemplate, ANIM_ATTACKER, 2, 0, -20, -28 @@ -16250,6 +16256,49 @@ Move_RAGING_BULL:: waitforvisualfinish restorebg waitbgfadein + clearmonbg ANIM_TARGET + end +RagingBullShatteredWall: + monbg ANIM_TARGET + setalpha 12, 8 + createsprite gBreathPuffSpriteTemplate, ANIM_ATTACKER, 2 + loopsewithpan SE_M_SWAGGER, SOUND_PAN_ATTACKER, 4, 2 + createsprite gAngerMarkSpriteTemplate, ANIM_ATTACKER, 2, 0, -20, -28 + delay 20 + createsprite gAngerMarkSpriteTemplate, ANIM_ATTACKER, 2, 0, 20, -28 + waitforvisualfinish + createvisualtask AnimTask_TranslateMonEllipticalRespectSide, 2, ANIM_ATTACKER, 18, 6, 2, 4 + waitforvisualfinish + playsewithpan SE_M_SWIFT, SOUND_PAN_ATTACKER + call SetImpactBackground + createsprite gSlideMonToOffsetSpriteTemplate, ANIM_ATTACKER, 2, 0, 20, 0, 0, 4 + delay 3 + waitforvisualfinish + createsprite gBrickBreakWallSpriteTemplate, ANIM_ATTACKER, 3, ANIM_TARGET, 0, 0, 90, 10 + playsewithpan SE_M_MEGA_KICK2, SOUND_PAN_TARGET + createsprite gBasicHitSplatSpriteTemplate, ANIM_TARGET, 4, -10, 0, ANIM_TARGET, 0 + createsprite gSlideMonToOffsetSpriteTemplate, ANIM_ATTACKER, 2, 1, -32, 0, 0, 3 + delay 20 + createvisualtask AnimTask_RotateMonSpriteToSide, 2, 8, -256, ANIM_ATTACKER, 0 + createvisualtask AnimTask_RotateMonSpriteToSide, 2, 8, -256, ANIM_TARGET, 0 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_ATTACKER, 4, 0, 12, 1 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + delay 20 + createvisualtask AnimTask_RotateMonSpriteToSide, 2, 8, -256, ANIM_ATTACKER, 1 + createvisualtask AnimTask_RotateMonSpriteToSide, 2, 8, -256, ANIM_TARGET, 1 + waitforvisualfinish + createsprite gBrickBreakWallShardSpriteTemplate, ANIM_ATTACKER, 2, ANIM_TARGET, 0, -8, -12 + createsprite gBrickBreakWallShardSpriteTemplate, ANIM_ATTACKER, 2, ANIM_TARGET, 1, 8, -12 + createsprite gBrickBreakWallShardSpriteTemplate, ANIM_ATTACKER, 2, ANIM_TARGET, 2, -8, 12 + createsprite gBrickBreakWallShardSpriteTemplate, ANIM_ATTACKER, 2, ANIM_TARGET, 3, 8, 12 + playsewithpan SE_M_BRICK_BREAK, SOUND_PAN_TARGET + createsprite gSlideMonToOriginalPosSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, 5 + delay 3 + createsprite gSlideMonToOriginalPosSpriteTemplate, ANIM_ATTACKER, 2, 1, 0, 7 + waitforvisualfinish + restorebg + waitbgfadein + clearmonbg ANIM_TARGET end @ Credits to Z-nogyroP. Simple anim that combines Force Palm + Fake Out @@ -17013,44 +17062,597 @@ Move_AXE_KICK:: createsprite gSlideMonToOriginalPosSpriteTemplate, ANIM_ATTACKER, 2, 0, 1, 4 end +Move_SPIN_OUT:: + loadspritegfx ANIM_TAG_IMPACT + loadspritegfx ANIM_TAG_RAPID_SPIN + loopsewithpan SE_M_HARDEN, SOUND_PAN_ATTACKER, 28, 2 + createvisualtask AnimTask_MetallicShine, 5, 0, 0, RGB_BLACK + waitforvisualfinish + call SetHighSpeedBg + loopsewithpan SE_M_RAZOR_WIND2, SOUND_PAN_ATTACKER, 8, 3 + waitforvisualfinish + monbg ANIM_ATTACKER + createsprite gRapidSpinSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, 32, -32, 40, -2 + createvisualtask AnimTask_RapinSpinMonElevation, 2, 0, 2, 0 + loopsewithpan SE_M_RAZOR_WIND2, SOUND_PAN_ATTACKER, 8, 4 + waitforvisualfinish + createsprite gBasicHitSplatSpriteTemplate, ANIM_TARGET, 2, 0, 0, ANIM_TARGET, 2 + createvisualtask AnimTask_ShakeTargetBasedOnMovePowerOrDmg, 2, FALSE, 1, 10, 1, 0 + playsewithpan SE_M_DOUBLE_SLAP, SOUND_PAN_TARGET + waitforvisualfinish + delay 8 + createvisualtask AnimTask_RapinSpinMonElevation, 2, 0, 2, 1 + loopsewithpan SE_M_RAZOR_WIND2, SOUND_PAN_ATTACKER, 8, 4 + waitforvisualfinish + call UnsetHighSpeedBg + clearmonbg ANIM_ATTACKER + end + +Move_MORTAL_SPIN:: + loadspritegfx ANIM_TAG_IMPACT + loadspritegfx ANIM_TAG_RAPID_SPIN + loadspritegfx ANIM_TAG_POISON_BUBBLE + createvisualtask AnimTask_BlendParticle, 5, ANIM_TAG_RAPID_SPIN, 0, 6, 6, RGB(15, 0, 15) + monbg ANIM_ATTACKER + createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_ATTACKER, 1, 0, 13, RGB(10, 2, 19) + delay 16 + createsprite gRapidSpinSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, 32, -32, 40, -2 + createvisualtask AnimTask_RapinSpinMonElevation, 2, 0, 2, 0 + loopsewithpan SE_M_RAZOR_WIND2, SOUND_PAN_ATTACKER, 8, 4 + waitforvisualfinish + createsprite gBasicHitSplatSpriteTemplate, ANIM_TARGET, 2, 0, 0, ANIM_TARGET, 2 + createvisualtask AnimTask_ShakeTargetBasedOnMovePowerOrDmg, 2, FALSE, 1, 10, 1, 0 + createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_ATTACKER, 1, 13, 0, RGB(10, 2, 19) + call PoisonBubblesEffect + waitforvisualfinish + delay 8 + createvisualtask AnimTask_RapinSpinMonElevation, 2, 0, 2, 1 + loopsewithpan SE_M_RAZOR_WIND2, SOUND_PAN_ATTACKER, 8, 4 + waitforvisualfinish + clearmonbg ANIM_ATTACKER + blendoff + end + +Move_FILLET_AWAY:: + loadspritegfx ANIM_TAG_CUT + createsprite gCuttingSliceSpriteTemplate, ANIM_ATTACKER, 2, 40, -32, 0 + playsewithpan SE_M_CUT, SOUND_PAN_ATTACKER + delay 5 + createsprite gCuttingSliceSpriteTemplate, ANIM_ATTACKER, 2, 20, -27, 0 + playsewithpan SE_M_CUT, SOUND_PAN_ATTACKER + delay 5 + createsprite gCuttingSliceSpriteTemplate, ANIM_ATTACKER, 2, 60, -37, 0 + playsewithpan SE_M_CUT, SOUND_PAN_ATTACKER + waitforvisualfinish + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_ATTACKER, 2, 0, 9, 0x7FFF + waitforvisualfinish + createvisualtask AnimTask_ShakeMon, 2, ANIM_ATTACKER, 0, 2, 4, 4 + waitforvisualfinish + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_ATTACKER, 2, 9, 0, 0x7FFF + waitforvisualfinish + blendoff + end + +Move_FLOWER_TRICK:: + loadspritegfx ANIM_TAG_SPOTLIGHT + loadspritegfx ANIM_TAG_LEAF @leaves + loadspritegfx ANIM_TAG_FLOWER @flowers + loadspritegfx ANIM_TAG_IMPACT + createvisualtask AnimTask_CreateSpotlight, 2 + createvisualtask AnimTask_HardwarePaletteFade, 2, (BLDCNT_TGT1_BG3 | BLDCNT_TGT1_OBJ | BLDCNT_TGT1_BD | BLDCNT_EFFECT_DARKEN), 3, 0, 10, FALSE + waitforvisualfinish + createsprite gSpotlightSpriteTemplate, ANIM_TARGET, 2, 0, -8 + delay 16 + createvisualtask SoundTask_PlaySE2WithPanning, 5, SE_M_ENCORE2, SOUND_PAN_TARGET + createvisualtask AnimTask_SwayMon, 5, 1, 8, 1536, 5, ANIM_TARGET + waitforvisualfinish + playsewithpan SE_M_DOUBLE_SLAP, SOUND_PAN_TARGET + createsprite gTropKickLeavesTemplate, ANIM_TARGET, 1, 0, 10, 192, 176, 40 + createsprite gTropKickLeavesTemplate, ANIM_TARGET, 1, 0, 10, -192, 240, 40 + createsprite gTropKickFlowerTemplate, ANIM_TARGET, 1, 0, 10, 192, -160, 40 + createsprite gTropKickFlowerTemplate, ANIM_TARGET, 1, 0, 10, -192, -112, 40 + createsprite gTropKickFlowerTemplate, ANIM_TARGET, 1, 0, 10, 160, 48, 40 + createsprite gTropKickLeavesTemplate, ANIM_TARGET, 1, 0, 10, -224, -32, 40 + createsprite gTropKickLeavesTemplate, ANIM_TARGET, 1, 0, 10, 112, -128, 40 + createsprite gBasicHitSplatSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, ANIM_TARGET, 2 + waitforvisualfinish + createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 3, 0, 6, 1 + waitforvisualfinish + createvisualtask AnimTask_HardwarePaletteFade, 2, (BLDCNT_TGT1_BG3 | BLDCNT_TGT1_OBJ | BLDCNT_TGT1_BD | BLDCNT_EFFECT_DARKEN), 3, 10, 0, TRUE + createvisualtask AnimTask_RemoveSpotlight, 2 + end + +Move_MAKE_IT_RAIN:: + loadspritegfx ANIM_TAG_COIN + loadspritegfx ANIM_TAG_IMPACT + monbg ANIM_ATTACKER + setalpha 12, 8 + playsewithpan SE_M_RAZOR_WIND2, SOUND_PAN_ATTACKER + createsprite gCoinThrowSpriteTemplate, ANIM_ATTACKER, 2, 20, 0, 0, 0, 1152 + waitforvisualfinish + playsewithpan SE_M_PAY_DAY, SOUND_PAN_TARGET + createsprite gBasicHitSplatSpriteTemplate, ANIM_ATTACKER, 1, 0, 0, ANIM_TARGET, 2 + createsprite gFallingCoinSpriteTemplate, ANIM_ATTACKER, 2 + createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 1, 0, 6, 1 + waitforvisualfinish + delay 20 + createvisualtask AnimTask_IsTargetPlayerSide, 2 + jumpretfalse MakingItRainOnOpponent + jumprettrue MakingItRainOnPlayer +MakingItRainContinue: + waitbgfadeout + createsprite gShakeMonOrTerrainSpriteTemplate, ANIM_ATTACKER, 2, 7, 1, 11, 1 + loopsewithpan SE_M_PAY_DAY, SOUND_PAN_TARGET, 8, 15 + createsprite gMakingItRainTemplate, ANIM_TARGET, 2, -5, 0, -5, 1 + delay 2 + createsprite gMakingItRainTemplate, ANIM_TARGET, 2, 5, 0, 6, 1 + delay 2 + createsprite gMakingItRainTemplate, ANIM_TARGET, 2, 19, 0, 10, 1 + delay 2 + createsprite gMakingItRainTemplate, ANIM_TARGET, 2, -23, 0, -10, 1 + createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 0, 5, 50, 1 + createvisualtask AnimTask_ShakeMon, 2, ANIM_DEF_PARTNER, 0, 5, 50, 1 + call MakingItRain + call MakingItRain + call MakingItRain + restorebg + waitbgfadein + blendoff + waitforvisualfinish + clearmonbg ANIM_ATTACKER + end +MakingItRainOnOpponent: + fadetobg BG_IMPACT_OPPONENT + goto MakingItRainContinue +MakingItRainOnPlayer: + fadetobg BG_IMPACT_PLAYER + goto MakingItRainContinue +MakingItRain: + createsprite gMakingItRainTemplate, ANIM_TARGET, 2, -20, 0, -10, 1 + delay 2 + createsprite gMakingItRainTemplate, ANIM_TARGET, 2, 28, 0, 10, 1 + delay 2 + createsprite gMakingItRainTemplate, ANIM_TARGET, 2, -10, 0, -5, 1 + delay 2 + createsprite gMakingItRainTemplate, ANIM_TARGET, 2, 10, 0, 6, 1 + delay 2 + createsprite gMakingItRainTemplate, ANIM_TARGET, 2, 24, 0, 10, 1 + delay 2 + createsprite gMakingItRainTemplate, ANIM_TARGET, 2, -32, 0, -10, 1 + delay 2 + createsprite gMakingItRainTemplate, ANIM_TARGET, 2, -20, 0, -10, 1 + delay 2 + createsprite gMakingItRainTemplate, ANIM_TARGET, 2, 30, 0, 10, 1 + delay 2 + return + +Move_SHED_TAIL:: + loopsewithpan SE_M_TAIL_WHIP, SOUND_PAN_ATTACKER, 24, 3 + createvisualtask AnimTask_TranslateMonEllipticalRespectSide, 2, ANIM_ATTACKER, 12, 4, 2, 3 + waitforvisualfinish + playsewithpan SE_M_ATTRACT, SOUND_PAN_ATTACKER + createvisualtask AnimTask_MonToSubstitute, 2 + end + +Move_HYPER_DRILL:: + loadspritegfx ANIM_TAG_IMPACT + loadspritegfx ANIM_TAG_HORN_HIT + fadetobgfromset BG_GUILLOTINE_OPPONENT, BG_GUILLOTINE_PLAYER, BG_GUILLOTINE_CONTESTS + waitbgfadein + createsprite gBowMonSpriteTemplate, ANIM_ATTACKER, 2, 0 + playsewithpan SE_M_HEADBUTT, SOUND_PAN_ATTACKER + waitforvisualfinish + delay 2 + createsprite gBowMonSpriteTemplate, ANIM_ATTACKER, 2, 1 + createsprite gHornHitSpriteTemplate, ANIM_TARGET, 4, 0, 0, 12 + waitforvisualfinish + playse 20 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_ATTACKER, 2, 0, 40, 1 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 10, 0, 40, 1 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, 0, 0, 1, 3 + playsewithpan SE_M_HORN_ATTACK, SOUND_PAN_TARGET + delay 4 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, 0, 2, 1, 3 + playsewithpan SE_M_HORN_ATTACK, SOUND_PAN_TARGET + delay 4 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, -4, 3, 1, 3 + playsewithpan SE_M_HORN_ATTACK, SOUND_PAN_TARGET + delay 4 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, -8, -5, 1, 3 + playsewithpan SE_M_HORN_ATTACK, SOUND_PAN_TARGET + delay 4 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, 4, -12, 1, 3 + playsewithpan SE_M_HORN_ATTACK, SOUND_PAN_TARGET + delay 4 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, 16, 0, 1, 3 + playsewithpan SE_M_HORN_ATTACK, SOUND_PAN_TARGET + delay 4 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, 5, 18, 1, 3 + playsewithpan SE_M_HORN_ATTACK, SOUND_PAN_TARGET + delay 4 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, -17, 12, 1, 2 + playsewithpan SE_M_HORN_ATTACK, SOUND_PAN_TARGET + delay 4 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, -21, -15, 1, 2 + playsewithpan SE_M_HORN_ATTACK, SOUND_PAN_TARGET + delay 4 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, 8, -27, 1, 2 + playsewithpan SE_M_HORN_ATTACK, SOUND_PAN_TARGET + delay 4 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, 32, 0, 1, 2 + playsewithpan SE_M_HORN_ATTACK, SOUND_PAN_TARGET + delay 4 + createsprite gBowMonSpriteTemplate, ANIM_ATTACKER, 2, 2 + createsprite gComplexPaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_BG | F_PAL_BATTLERS, 3, 1, RGB_BLACK, 8, RGB_BLACK, 0 + playsewithpan SE_M_RAZOR_WIND, SOUND_PAN_TARGET + waitforvisualfinish + createsprite gComplexPaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_BG | F_PAL_BATTLERS, 3, 1, RGB_BLACK, 8, RGB_BLACK, 0 + restorebg + waitbgfadein + blendoff + end + +Move_TWIN_BEAM:: + loadspritegfx ANIM_TAG_BLUE_RING + loadspritegfx ANIM_TAG_GOLD_RING + playsewithpan SE_M_PSYBEAM, SOUND_PAN_ATTACKER + call SetPsychicBackground + createvisualtask AnimTask_StartSinAnimTimer, 5, 100 + createsoundtask SoundTask_LoopSEAdjustPanning, SE_M_TELEPORT, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, 2, 9, 0, 10 + call PsywaveRings + call PsybeamRings + call PsywaveRings + call PsybeamRings + createvisualtask AnimTask_BlendColorCycle, 2, F_PAL_TARGET, 1, 4, 0, 12, RGB(31, 18, 31) + call PsywaveRings + call PsybeamRings + call PsywaveRings + call PsybeamRings + call PsywaveRings + call PsybeamRings + call PsywaveRings + call PsybeamRings + waitforvisualfinish + delay 1 + call UnsetPsychicBg + end + +Move_COMEUPPANCE:: + loadspritegfx ANIM_TAG_IMPACT + monbg ANIM_TARGET + fadetobg BG_DARK + waitbgfadein + delay 0 + playsewithpan SE_M_DRAGON_RAGE, SOUND_PAN_ATTACKER + createvisualtask AnimTask_ShakeMon2, 5, ANIM_ATTACKER, 1, 0, 15, 1 + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_ATTACKER, 3, 0, 9, RGB_RED + waitforvisualfinish + delay 10 + createvisualtask AnimTask_SwayMon, 5, 0, 16, 6144, 8, ANIM_ATTACKER + delay 5 + setalpha 12, 8 + createvisualtask AnimTask_ShakeMon2, 5, ANIM_TARGET, 4, 0, 30, 1 + createsprite gBasicHitSplatSpriteTemplate, ANIM_ATTACKER, 3, 0, 0, 1, 0 + playsewithpan SE_M_COMET_PUNCH, +63 + delay 5 + createsprite gBasicHitSplatSpriteTemplate, ANIM_ATTACKER, 3, 24, 8, 1, 0 + playsewithpan SE_M_COMET_PUNCH, +63 + delay 5 + createsprite gBasicHitSplatSpriteTemplate, ANIM_ATTACKER, 3, -24, -16, 1, 0 + playsewithpan SE_M_COMET_PUNCH, +63 + delay 5 + createsprite gBasicHitSplatSpriteTemplate, ANIM_ATTACKER, 3, 8, 4, 1, 0 + playsewithpan SE_M_COMET_PUNCH, +63 + delay 5 + createsprite gBasicHitSplatSpriteTemplate, ANIM_ATTACKER, 3, -16, 19, 1, 0 + playsewithpan SE_M_COMET_PUNCH, +63 + delay 5 + createsprite gBasicHitSplatSpriteTemplate, ANIM_ATTACKER, 3, 18, -18, 1, 0 + playsewithpan SE_M_COMET_PUNCH, +63 + waitforvisualfinish + clearmonbg ANIM_TARGET + blendoff + restorebg + waitbgfadein + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_ATTACKER, 3, 9, 0, RGB_RED + waitforvisualfinish + clearmonbg ANIM_DEF_PARTNER + end + +Move_BLOOD_MOON:: + loadspritegfx ANIM_TAG_BLOOD_MOON + loadspritegfx ANIM_TAG_BEAM + loadspritegfx ANIM_TAG_RED_EXPLOSION + createvisualtask AnimTask_BlendBattleAnimPal, 5, F_PAL_BG, 0, 16, 16, RGB_BLACK + createsprite gVerticalDipSpriteTemplate, ANIM_ATTACKER, 2, 8, 1, ANIM_ATTACKER + delay 8 + playsewithpan SE_M_BARRIER, SOUND_PAN_ATTACKER + createsprite gMoonUpSpriteTemplate, ANIM_ATTACKER, 2 + waitforvisualfinish + delay 15 + playsewithpan SE_M_DETECT, 0 + waitforvisualfinish + createvisualtask AnimTask_IsTargetPlayerSide, 2 + jumpargeq 7 ANIM_TARGET BloodMoonOnPlayer +BloodMoonOnOpponent: + createsoundtask SoundTask_LoopSEAdjustPanning, SE_M_HYPER_BEAM2, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, 1, 10, 0, 5 + call BloodMoonOnslaughtOpponent + call BloodMoonOnslaughtOpponent + call BloodMoonOnslaughtOpponent + call BloodMoonOnslaughtOpponent + call BloodMoonOnslaughtOpponent + createsprite gBloodMoonOnslaughtSpriteTemplate, ANIM_TARGET, 2, 140, -16, 165, 40, 4 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + createsprite gRedExplosionSpriteTemplate, ANIM_TARGET, 4, 6, 5, 1, 0 + delay 0 + waitforvisualfinish + goto BloodMoonFinish +BloodMoonOnPlayer: + createsoundtask SoundTask_LoopSEAdjustPanning, SE_M_HYPER_BEAM2, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, 1, 10, 0, 5 + call BloodMoonOnslaughtPlayer + call BloodMoonOnslaughtPlayer + call BloodMoonOnslaughtPlayer + call BloodMoonOnslaughtPlayer + call BloodMoonOnslaughtPlayer + createsprite gBloodMoonOnslaughtSpriteTemplate, ANIM_TARGET, 2, 40, -16, 75, 80, 4 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + delay 0 + waitforvisualfinish +BloodMoonFinish: + createvisualtask AnimTask_BlendBattleAnimPal, 5, F_PAL_BG, 0, 0, 0, RGB_BLACK + waitforvisualfinish + clearmonbg ANIM_ATTACKER + end +BloodMoonOnslaughtOpponent: + createsprite gBloodMoonOnslaughtSpriteTemplate, ANIM_TARGET, 2, 140, -16, 165, 40, 4 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + createsprite gRedExplosionSpriteTemplate, ANIM_TARGET, 4, 6, 5, 1, 0 + delay 0 + createsprite gBloodMoonOnslaughtSpriteTemplate, ANIM_TARGET, 2, 140, -16, 165, 40, 4 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + createsprite gRedExplosionSpriteTemplate, ANIM_TARGET, 4, -16, -15, 1, 0 + delay 0 + createsprite gBloodMoonOnslaughtSpriteTemplate, ANIM_TARGET, 2, 140, -16, 165, 40, 4 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + createsprite gRedExplosionSpriteTemplate, ANIM_TARGET, 4, 16, -5, 1, 0 + delay 0 + createsprite gBloodMoonOnslaughtSpriteTemplate, ANIM_TARGET, 2, 140, -16, 165, 40, 4 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + createsprite gRedExplosionSpriteTemplate, ANIM_TARGET, 4, -12, 18, 1, 0 + delay 0 + createsprite gBloodMoonOnslaughtSpriteTemplate, ANIM_TARGET, 2, 140, -16, 165, 40, 4 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + createsprite gRedExplosionSpriteTemplate, ANIM_TARGET, 4, 0, 5, 1, 0 + delay 0 + return +BloodMoonOnslaughtPlayer: + createsprite gBloodMoonOnslaughtSpriteTemplate, ANIM_TARGET, 2, 40, -16, 75, 80, 4 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + createsprite gRedExplosionSpriteTemplate, ANIM_TARGET, 4, 6, 5, 1, 0 + delay 0 + createsprite gBloodMoonOnslaughtSpriteTemplate, ANIM_TARGET, 2, 40, -16, 75, 80, 4 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + createsprite gRedExplosionSpriteTemplate, ANIM_TARGET, 4, -16, -15, 1, 0 + delay 0 + createsprite gBloodMoonOnslaughtSpriteTemplate, ANIM_TARGET, 2, 40, -16, 75, 80, 4 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + createsprite gRedExplosionSpriteTemplate, ANIM_TARGET, 4, 16, -5, 1, 0 + delay 0 + createsprite gBloodMoonOnslaughtSpriteTemplate, ANIM_TARGET, 2, 40, -16, 75, 80, 4 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + createsprite gRedExplosionSpriteTemplate, ANIM_TARGET, 4, -12, 18, 1, 0 + delay 0 + createsprite gBloodMoonOnslaughtSpriteTemplate, ANIM_TARGET, 2, 40, -16, 75, 80, 4 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 4, 0, 12, 1 + createsprite gRedExplosionSpriteTemplate, ANIM_TARGET, 4, 0, 5, 1, 0 + delay 0 + return + +Move_FICKLE_BEAM:: + createvisualtask AnimTask_IsPowerOver99, 2 + waitforvisualfinish + jumpreteq FALSE, FickleBeamRegular + jumpreteq TRUE, FickleBeamIntense +FickleBeamRegular: + loadspritegfx ANIM_TAG_GOLD_RING + playsewithpan SE_M_PSYBEAM, SOUND_PAN_ATTACKER + createsoundtask SoundTask_LoopSEAdjustPanning, SE_M_PSYBEAM2, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, 3, 4, 0, 15 + call PsybeamRings + call PsybeamRings + createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 0, 4, 25, 1 + createvisualtask AnimTask_BlendColorCycle, 2, F_PAL_TARGET, 2, 2, 0, 12, RGB(15, 8, 30) + call PsybeamRings + call PsybeamRings + call PsybeamRings + call PsybeamRings + call PsybeamRings + call PsybeamRings + call PsybeamRings + call PsybeamRings + call PsybeamRings + waitforvisualfinish + end +FickleBeamIntense: + loadspritegfx ANIM_TAG_ORBS + fadetobgfromset BG_SPACIAL_REND_ON_OPPONENT BG_SPACIAL_REND_ON_PLAYER BG_SPACIAL_REND_ON_OPPONENT + waitbgfadein + delay 10 + playsewithpan SE_M_HYPER_BEAM, SOUND_PAN_ATTACKER + createvisualtask AnimTask_ShakeMon2, 2, ANIM_ATTACKER, 1, 0, 4, 1 + waitforvisualfinish + delay 30 + createsoundtask SoundTask_LoopSEAdjustPanning, SE_M_HYPER_BEAM2, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, 1, 15, 0, 5 + createvisualtask AnimTask_ShakeMon, 2, ANIM_ATTACKER, 0, 4, 50, 1 + createvisualtask AnimTask_FlashAnimTagWithColor, 2, ANIM_TAG_ORBS, 1, 12, RGB_RED, 16, 0, 0 + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 4, 0, 50, 1 + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + call HyperBeamOrbs + waitforvisualfinish + restorebg + waitbgfadein + end + +Move_THUNDERCLAP:: + loadspritegfx ANIM_TAG_LIGHTNING + monbg ANIM_ATK_PARTNER + setalpha 12, 8 + createvisualtask AnimTask_TranslateMonEllipticalRespectSide, 2, ANIM_ATTACKER, 24, 6, 1, 5 + createvisualtask AnimTask_TraceMonBlended, 2, 0, 4, 7, 3 + playsewithpan SE_M_JUMP_KICK, SOUND_PAN_ATTACKER + delay 4 + playsewithpan SE_M_THUNDER_WAVE, SOUND_PAN_TARGET + delay 1 + createsprite gLightningSpriteTemplate, ANIM_TARGET, 2, 0, -32 + playsewithpan SE_M_TRI_ATTACK2, SOUND_PAN_TARGET + delay 1 + createsprite gLightningSpriteTemplate, ANIM_TARGET, 2, 0, -16 + delay 1 + createsprite gLightningSpriteTemplate, ANIM_TARGET, 2, 0, 0 + delay 1 + createsprite gLightningSpriteTemplate, ANIM_TARGET, 2, 0, 16 + delay 10 + createvisualtask AnimTask_ShakeTargetInPattern, 2, 30, 3, TRUE, 0 + delay 2 + createvisualtask AnimTask_BlendBattleAnimPal, 5, F_PAL_BG, 3, 16, 0, RGB_WHITE + createvisualtask AnimTask_BlendBattleAnimPal, 5, F_PAL_TARGET, 0, 16, 16, RGB_BLACK + delay 4 + createvisualtask AnimTask_BlendBattleAnimPal, 5, F_PAL_TARGET, 0, 0, 0, RGB_BLACK + waitforvisualfinish + clearmonbg ANIM_ATK_PARTNER + blendoff + waitforvisualfinish + end + +Move_HARD_PRESS:: + loadspritegfx ANIM_TAG_EXPLOSION + loadspritegfx ANIM_TAG_ACUPRESSURE + loadspritegfx ANIM_TAG_PURPLE_HAND_OUTLINE + setalpha 15, 0 + call SetSteelBeamBackground + createvisualtask AnimTask_CompressTargetHorizontallyFast, 2 + createsprite gCrushGripExplosionTemplate, ANIM_TARGET, 0, 0, 0, 1, 1 + delay 3 + playsewithpan SE_M_EXPLOSION, SOUND_PAN_ATTACKER + createsprite gCrushGripExplosionTemplate, ANIM_TARGET, 0, 24, -24, 1, 1 + delay 3 + playsewithpan SE_M_EXPLOSION, SOUND_PAN_ATTACKER + createsprite gCrushGripExplosionTemplate, ANIM_TARGET, 0, -16, 16, 1, 1 + delay 3 + playsewithpan SE_M_EXPLOSION, SOUND_PAN_ATTACKER + createsprite gCrushGripExplosionTemplate, ANIM_TARGET, 0, -24, -12, 1, 1 + delay 3 + playsewithpan SE_M_EXPLOSION, SOUND_PAN_ATTACKER + createsprite gCrushGripExplosionTemplate, ANIM_TARGET, 0, 16, 16, 1, 1 + waitforvisualfinish + blendoff + call UnsetHighSpeedBg + end + +Move_DRAGON_CHEER:: + loadspritegfx ANIM_TAG_NOISE_LINE + loadspritegfx ANIM_TAG_CONFETTI + loadspritegfx ANIM_TAG_PINK_CLOUD + createvisualtask AnimTask_DeepInhale, 2, ANIM_ATTACKER + delay 12 + call RoarEffect + createvisualtask SoundTask_PlayCryHighPitch, 2, ANIM_ATTACKER, 3 + waitforvisualfinish + monbg ANIM_ATTACKER + playsewithpan SE_BALL_OPEN, SOUND_PAN_TARGET + createsprite gSmokeBallEscapeCloudSpriteTemplate, ANIM_ATTACKER, 122, 3, -14, 18, 24 + createsprite gSmokeBallEscapeCloudSpriteTemplate, ANIM_ATTACKER, 121, 3, 14, 6, 24 + createsprite gSmokeBallEscapeCloudSpriteTemplate, ANIM_ATTACKER, 120, 3, -12, 12, 24 + createsprite gSmokeBallEscapeCloudSpriteTemplate, ANIM_ATTACKER, 119, 3, 14, 18, 24 + createsprite gSmokeBallEscapeCloudSpriteTemplate, ANIM_ATTACKER, 118, 3, 0, 0, 24 + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + call CreateFlatterConfetti + waitforvisualfinish + clearmonbg ANIM_ATTACKER + delay 10 + end + +Move_MALIGNANT_CHAIN:: + loadspritegfx ANIM_TAG_PURPLE_CHAIN + loadspritegfx ANIM_TAG_POISON_BUBBLE + loopsewithpan SE_M_SCRATCH, SOUND_PAN_TARGET, 6, 2 + createsprite gChainBindingSpriteTemplate, ANIM_TARGET, 4, 0, 16, 0, 1 + delay 7 + createsprite gChainBindingSpriteTemplate, ANIM_TARGET, 2, 0, 8, 1, 1 + delay 3 + createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 2, 0, 8, 1 + delay 20 + setarg 7, -1 + playsewithpan SE_M_BIND, SOUND_PAN_TARGET + waitforvisualfinish + call PoisonBubblesEffect + waitforvisualfinish + end + Move_TERA_BLAST:: Move_ORDER_UP:: -Move_SPIN_OUT:: Move_POPULATION_BOMB:: Move_GLAIVE_RUSH:: Move_REVIVAL_BLESSING:: Move_SALT_CURE:: Move_TRIPLE_DIVE:: -Move_MORTAL_SPIN:: Move_DOODLE:: -Move_FILLET_AWAY:: -Move_FLOWER_TRICK:: -Move_MAKE_IT_RAIN:: Move_RUINATION:: Move_COLLISION_COURSE:: Move_ELECTRO_DRIFT:: -Move_SHED_TAIL:: -Move_HYPER_DRILL:: -Move_TWIN_BEAM:: Move_ARMOR_CANNON:: -Move_COMEUPPANCE:: Move_BLAZING_TORQUE:: Move_WICKED_TORQUE:: Move_NOXIOUS_TORQUE:: Move_COMBAT_TORQUE:: Move_MAGICAL_TORQUE:: Move_PSYBLADE:: -Move_BLOOD_MOON:: Move_MATCHA_GOTCHA:: Move_TERA_STARSTORM:: -Move_FICKLE_BEAM:: -Move_THUNDERCLAP:: Move_MIGHTY_CLEAVE:: Move_TACHYON_CUTTER:: -Move_HARD_PRESS:: -Move_DRAGON_CHEER:: Move_SUPERCELL_SLAM:: -Move_MALIGNANT_CHAIN:: end @to do @@@@@@@@@@@@@@@@@@@@@@@ GEN 1-3 @@@@@@@@@@@@@@@@@@@@@@@ diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index beb3bd5423..c47127c651 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -30,6 +30,19 @@ BattleScript_DamageToQuarterTargetHP:: damagetoquartertargethp goto BattleScript_HitFromAtkAnimation +BattleScript_EffectFickleBeam:: + attackcanceler + attackstring + ppreduce + accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE + ficklebeamdamagecalculation + goto BattleScript_HitFromCritCalc +BattleScript_FickleBeamDoubled:: + pause B_WAIT_TIME_SHORTEST + printstring STRINGID_FICKLEBEAMDOUBLED + waitmessage B_WAIT_TIME_LONG + goto BattleScript_HitFromCritCalc + BattleScript_Terastallization:: @ TODO: no string prints in S/V, but right now this helps with clarity printstring STRINGID_PKMNSTORINGENERGY @@ -865,6 +878,7 @@ BattleScript_OctlockTurnDmgEnd: BattleScript_EffectPoltergeist:: attackcanceler + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE attackstring ppreduce checkpoltergeist BS_TARGET, BattleScript_ButItFailed diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index fce328d858..0e065d018e 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -3,6 +3,8 @@ - [README](./README.md) - [Installation](./INSTALL.md) - [Setting up WSL1 (Legacy Portion)](./legacy_WSL1_INSTALL.md) +- [Run documentation site locally](local_mdbook/index.md) + - [Ubuntu WSL1/WSL2](local_mdbook/ubuntu_WSL.md) - [AI Flags](./ai_flags.md) - [Tutorials]() - [How to add new AI Flags](./ai_logic.md) diff --git a/docs/local_mdbook/index.md b/docs/local_mdbook/index.md new file mode 100644 index 0000000000..ef362dc45f --- /dev/null +++ b/docs/local_mdbook/index.md @@ -0,0 +1,2 @@ +## Running documentation website locally +- [Ubuntu WSL1/WSL2](/docs/local_mdbook/ubuntu_WSL.md) diff --git a/docs/local_mdbook/ubuntu_WSL.md b/docs/local_mdbook/ubuntu_WSL.md new file mode 100644 index 0000000000..88ee2468e6 --- /dev/null +++ b/docs/local_mdbook/ubuntu_WSL.md @@ -0,0 +1,51 @@ +Note: For further information beyond this very basic guide, please visit mdBook's [official documentation](https://rust-lang.github.io/mdBook/). + +## Running documentation website locally (Ubuntu WSL1/WSL2) +### Previous Requirements: +- Option 1: Install via Rust toolchain + - Install Rust toolchain if you don't have it via the `sudo apt install cargo` command. + - Install mdBook via the `cargo install mdbook` command. Once finished, this message will pop up, with {USER} being your Ubuntu + ``` + warning: be sure to add `/home/{USER}/.cargo/bin` to your PATH to be able to run the installed binaries + ``` + - Add `/home/{USER}/.cargo/bin` to your PATH (with {USER} being the Ubuntu username.) + - Run command `nano ~/.profile` to edit the file. + - Add the following lines, ***replacing {USER} with your Linux username.*** + ```diff + # set PATH so it includes user's private bin if it exists + if [ -d "$HOME/bin" ] ; then + PATH="$HOME/bin:$PATH" + fi + + # set PATH so it includes user's private bin if it exists + if [ -d "$HOME/.local/bin" ] ; then + PATH="$HOME/.local/bin:$PATH" + fi + + +# set PATH so it includes cargo bin if it exists + +if [ -d "/home/{USER}/.cargo/bin" ] ; then + + PATH="/home/{USER}/.cargo/bin:$PATH" + +fi + ``` + - Run the `source ~/.profile` command to refresh the path in the current session. +- Option 2: Install downloaded binaries directly + - TODO: Add documentation of this process. + +### Running the website +- Navigate to the `docs` folder on the repository. +- Run `mdbook serve`. Once started, you may now open the website on your browser by going to `http://127.0.0.1:3000`. +- Every change done to the `docs` folder will be reflected with an automatic refresh. +- To stop the server and go back to the terminal, press `Ctrl + C`. + +### Modifying the website +- The navigation menu on the left is handled by `docs/SUMMARY.md`. Every file added needs to be added somewhere here in order to become visible, otherwise you'll get a 404 error. +- Any Markdown files (.md extension) added to the `docs/` directory will automatically be read by mdBook. +- To add Markdown files that are not in the `docs/` directory, you may create an empty `.md` file and add the following ***without the "----"***: + ```md + {{ ----#include ../INSTALL.md}}` + ``` + This will include the `INSTALL.md` Markdown file from the root directory. + +Once you're set up, you can now check your changes before pushing them to your repo! :D + +We hope that this will make it easier for users to contribute to the documentation :) diff --git a/graphics/battle_anims/sprites/beam.png b/graphics/battle_anims/sprites/beam.png new file mode 100644 index 0000000000..cce0a23a4c Binary files /dev/null and b/graphics/battle_anims/sprites/beam.png differ diff --git a/graphics/battle_anims/sprites/blood_moon.png b/graphics/battle_anims/sprites/blood_moon.png new file mode 100644 index 0000000000..014c7bd445 Binary files /dev/null and b/graphics/battle_anims/sprites/blood_moon.png differ diff --git a/graphics/battle_anims/sprites/purple_chain.png b/graphics/battle_anims/sprites/purple_chain.png new file mode 100644 index 0000000000..8c46b34aeb Binary files /dev/null and b/graphics/battle_anims/sprites/purple_chain.png differ diff --git a/graphics/battle_anims/sprites/red_explosion.png b/graphics/battle_anims/sprites/red_explosion.png new file mode 100644 index 0000000000..2c974db5c9 Binary files /dev/null and b/graphics/battle_anims/sprites/red_explosion.png differ diff --git a/include/battle.h b/include/battle.h index dd2ca35f61..28a6dd0f36 100644 --- a/include/battle.h +++ b/include/battle.h @@ -800,6 +800,7 @@ struct BattleStruct u8 distortedTypeMatchups; u8 categoryOverride; // for Z-Moves and Max Moves u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side + u8 fickleBeamBoosted:1; }; // The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider, diff --git a/include/battle_anim.h b/include/battle_anim.h index b3460fd6bc..351dbbd454 100644 --- a/include/battle_anim.h +++ b/include/battle_anim.h @@ -252,6 +252,7 @@ void AnimParticleBurst(struct Sprite *); void AnimPowerAbsorptionOrb(struct Sprite *sprite); void AnimNeedleArmSpike(struct Sprite *); void AnimTask_CompressTargetHorizontally(u8 taskId); +void AnimTask_CompressTargetHorizontallyFast(u8 taskId); void AnimSporeParticle(struct Sprite *sprite); void AnimAbsorptionOrb(struct Sprite *sprite); void AnimPetalDanceSmallFlower(struct Sprite *sprite); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 0c9f72c276..6bdb88b28f 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -843,5 +843,7 @@ extern const u8 BattleScript_EffectUpperHand[]; extern const u8 BattleScript_EffectTidyUp[]; extern const u8 BattleScript_EffectSpicyExtract[]; extern const u8 BattleScript_DamageToQuarterTargetHP[]; +extern const u8 BattleScript_EffectFickleBeam[]; +extern const u8 BattleScript_FickleBeamDoubled[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/config/battle.h b/include/config/battle.h index 4bf84a1716..ebe979824c 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -251,22 +251,22 @@ #define B_TRY_CATCH_TRAINER_BALL GEN_LATEST // In Gen4+, trying to catch a Trainer's Pokémon does not consume the Poké Ball. // Animation Settings -#define B_NEW_SWORD_PARTICLE TRUE // If set to TRUE, it updates Swords Dance's particle. -#define B_NEW_LEECH_SEED_PARTICLE TRUE // If set to TRUE, it updates Leech Seed's animation particle. -#define B_NEW_HORN_ATTACK_PARTICLE TRUE // If set to TRUE, it updates Horn Attack's horn particle. -#define B_NEW_ROCKS_PARTICLE TRUE // If set to TRUE, it updates rock particles. -#define B_NEW_LEAF_PARTICLE TRUE // If set to TRUE, it updates leaf particle. -#define B_NEW_EMBER_PARTICLES TRUE // If set to TRUE, it updates Ember's fire particle. -#define B_NEW_MEAN_LOOK_PARTICLE TRUE // If set to TRUE, it updates Mean Look's eye particle. -#define B_NEW_TEETH_PARTICLE TRUE // If set to TRUE, it updates Bite/Crunch teeth particle. -#define B_NEW_HANDS_FEET_PARTICLE TRUE // If set to TRUE, it updates chop/kick/punch particles. -#define B_NEW_SPIKES_PARTICLE TRUE // If set to TRUE, it updates Spikes particle. -#define B_NEW_FLY_BUBBLE_PARTICLE TRUE // If set to TRUE, it updates Fly's 'bubble' particle. -#define B_NEW_CURSE_NAIL_PARTICLE TRUE // If set to TRUE, it updates Curse's nail. -#define B_NEW_BATON_PASS_BALL_PARTICLE TRUE // If set to TRUE, it updates Baton Pass' Poké Ball sprite. -#define B_NEW_MORNING_SUN_STAR_PARTICLE TRUE // If set to TRUE, it updates Morning Sun's star particles. -#define B_NEW_IMPACT_PALETTE TRUE // If set to TRUE, it updates the basic 'hit' palette. -#define B_NEW_SURF_PARTICLE_PALETTE TRUE // If set to TRUE, it updates Surf's wave palette. +#define B_NEW_SWORD_PARTICLE FALSE // If set to TRUE, it updates Swords Dance's particle. +#define B_NEW_LEECH_SEED_PARTICLE FALSE // If set to TRUE, it updates Leech Seed's animation particle. +#define B_NEW_HORN_ATTACK_PARTICLE FALSE // If set to TRUE, it updates Horn Attack's horn particle. +#define B_NEW_ROCKS_PARTICLE FALSE // If set to TRUE, it updates rock particles. +#define B_NEW_LEAF_PARTICLE FALSE // If set to TRUE, it updates leaf particle. +#define B_NEW_EMBER_PARTICLES FALSE // If set to TRUE, it updates Ember's fire particle. +#define B_NEW_MEAN_LOOK_PARTICLE FALSE // If set to TRUE, it updates Mean Look's eye particle. +#define B_NEW_TEETH_PARTICLE FALSE // If set to TRUE, it updates Bite/Crunch teeth particle. +#define B_NEW_HANDS_FEET_PARTICLE FALSE // If set to TRUE, it updates chop/kick/punch particles. +#define B_NEW_SPIKES_PARTICLE FALSE // If set to TRUE, it updates Spikes particle. +#define B_NEW_FLY_BUBBLE_PARTICLE FALSE // If set to TRUE, it updates Fly's 'bubble' particle. +#define B_NEW_CURSE_NAIL_PARTICLE FALSE // If set to TRUE, it updates Curse's nail. +#define B_NEW_BATON_PASS_BALL_PARTICLE FALSE // If set to TRUE, it updates Baton Pass' Poké Ball sprite. +#define B_NEW_MORNING_SUN_STAR_PARTICLE FALSE // If set to TRUE, it updates Morning Sun's star particles. +#define B_NEW_IMPACT_PALETTE FALSE // If set to TRUE, it updates the basic 'hit' palette. +#define B_NEW_SURF_PARTICLE_PALETTE FALSE // If set to TRUE, it updates Surf's wave palette. // Poké Ball animation and sounds #define B_ENEMY_THROW_BALLS GEN_LATEST // In GEN_6+, enemy Trainers throw Poké Balls into battle instead of them just appearing on the ground and opening. diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index 389cd7e127..54573a4868 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -412,6 +412,10 @@ #define ANIM_TAG_TERA_CRYSTAL (ANIM_SPRITES_START + 398) #define ANIM_TAG_TERA_SHATTER (ANIM_SPRITES_START + 399) #define ANIM_TAG_DREEPY_SHINY (ANIM_SPRITES_START + 400) +#define ANIM_TAG_BLOOD_MOON (ANIM_SPRITES_START + 401) +#define ANIM_TAG_BEAM (ANIM_SPRITES_START + 402) +#define ANIM_TAG_RED_EXPLOSION (ANIM_SPRITES_START + 403) +#define ANIM_TAG_PURPLE_CHAIN (ANIM_SPRITES_START + 404) // battlers #define ANIM_ATTACKER 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 1e6e2d86d7..9529e026c1 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -712,8 +712,9 @@ #define STRINGID_FOGISDEEP 710 #define STRINGID_FOGLIFTED 711 #define STRINGID_PKMNMADESHELLGLEAM 712 +#define STRINGID_FICKLEBEAMDOUBLED 713 -#define BATTLESTRINGS_COUNT 713 +#define BATTLESTRINGS_COUNT 714 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index c606eef8d3..29249712b6 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -130,7 +130,7 @@ struct MapEvents struct MapConnection { u8 direction; - u32 offset; + s32 offset; u8 mapGroup; u8 mapNum; }; diff --git a/include/graphics.h b/include/graphics.h index 9e6f53b3e4..1ec04f0056 100644 --- a/include/graphics.h +++ b/include/graphics.h @@ -2724,6 +2724,8 @@ extern const u32 gBattleAnimSpritePal_AuraSphere[]; extern const u32 gBattleAnimSpritePal_AvalancheRocks[]; extern const u32 gBattleAnimSpriteGfx_NewPokeball[]; extern const u32 gBattleAnimSpritePal_NewPokeball[]; +extern const u32 gBattleAnimSpriteGfx_Beam[]; +extern const u32 gBattleAnimSpritePal_Beam[]; extern const u32 gBattleAnimSpriteGfx_BerryEaten[]; extern const u32 gBattleAnimSpritePal_BerryEaten[]; extern const u32 gBattleAnimSpriteGfx_BerryNormal[]; @@ -2732,6 +2734,8 @@ extern const u32 gBattleAnimSpriteGfx_BigRock[]; extern const u32 gBattleAnimSpritePal_BigRock[]; extern const u32 gBattleAnimSpriteGfx_BlacephalonHead[]; extern const u32 gBattleAnimSpritePal_BlacephalonHead[]; +extern const u32 gBattleAnimSpriteGfx_BloodMoon[]; +extern const u32 gBattleAnimSpritePal_BloodMoon[]; extern const u32 gBattleAnimSpritePal_BlueFlare[]; extern const u32 gBattleAnimSpriteGfx_Branch[]; extern const u32 gBattleAnimSpritePal_Branch[]; @@ -2825,12 +2829,16 @@ extern const u32 gBattleAnimSpriteGfx_PoisonColumn[]; extern const u32 gBattleAnimSpritePal_PoisonColumn[]; extern const u32 gBattleAnimSpriteGfx_PowerTrick[]; extern const u32 gBattleAnimSpritePal_PowerTrick[]; +extern const u32 gBattleAnimSpriteGfx_PurpleChain[]; +extern const u32 gBattleAnimSpritePal_PurpleChain[]; extern const u32 gBattleAnimSpriteGfx_PurpleDrake[]; extern const u32 gBattleAnimSpritePal_PurpleDrake[]; extern const u32 gBattleAnimSpriteGfx_QuickGuard[]; extern const u32 gBattleAnimSpritePal_QuickGuard[]; extern const u32 gBattleAnimSpriteGfx_RazorShell[]; extern const u32 gBattleAnimSpritePal_RazorShell[]; +extern const u32 gBattleAnimSpriteGfx_RedExplosion[]; +extern const u32 gBattleAnimSpritePal_RedExplosion[]; extern const u32 gBattleAnimSpriteGfx_RocksSmall[]; extern const u32 gBattleAnimSpriteGfx_NewRocks[]; extern const u32 gBattleAnimSpritePal_NewRocks[]; diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c index a852033133..e333d2db40 100644 --- a/src/battle_anim_effects_1.c +++ b/src/battle_anim_effects_1.c @@ -3101,6 +3101,17 @@ const struct SpriteTemplate gPsyshockSmokeSpriteTemplate = .callback = AnimSpriteOnMonPos, }; +const struct SpriteTemplate gChainBindingSpriteTemplate = +{ + .tileTag = ANIM_TAG_PURPLE_CHAIN, + .paletteTag = ANIM_TAG_PURPLE_CHAIN, + .oam = &gOamData_AffineNormal_ObjNormal_64x32, + .anims = sAnims_ConstrictBinding, + .images = NULL, + .affineAnims = sAffineAnims_ConstrictBinding, + .callback = AnimConstrictBinding, +}; + // functions static void AnimGrassKnot(struct Sprite *sprite) { @@ -7229,6 +7240,14 @@ static const union AffineAnimCmd sCompressTargetHorizontallyAffineAnimCmds[] = AFFINEANIMCMD_END, }; +static const union AffineAnimCmd sCompressTargetHorizontallyAffineAnimCmdsFast[] = +{ + AFFINEANIMCMD_FRAME(32, 0, 0, 16), //Compress + AFFINEANIMCMD_FRAME(0, 0, 0, 32), + AFFINEANIMCMD_FRAME(-32, 0, 0, 16), + AFFINEANIMCMD_END, +}; + static void AnimTask_CompressTargetStep(u8 taskId) { struct Task* task = &gTasks[taskId]; @@ -7245,6 +7264,14 @@ void AnimTask_CompressTargetHorizontally(u8 taskId) task->func = AnimTask_CompressTargetStep; } +void AnimTask_CompressTargetHorizontallyFast(u8 taskId) +{ + struct Task* task = &gTasks[taskId]; + u8 spriteId = GetAnimBattlerSpriteId(ANIM_TARGET); + PrepareAffineAnimInTaskData(task, spriteId, sCompressTargetHorizontallyAffineAnimCmdsFast); + task->func = AnimTask_CompressTargetStep; +} + void AnimTask_CreateSmallSteelBeamOrbs(u8 taskId) { if (--gTasks[taskId].data[0] == -1) diff --git a/src/battle_anim_new.c b/src/battle_anim_new.c index e03ad2ace6..a704a73f40 100644 --- a/src/battle_anim_new.c +++ b/src/battle_anim_new.c @@ -99,6 +99,7 @@ static void SpriteCB_GlacialLance_Step1(struct Sprite* sprite); static void SpriteCB_GlacialLance_Step2(struct Sprite* sprite); static void SpriteCB_GlacialLance(struct Sprite* sprite); static void SpriteCB_TripleArrowKick(struct Sprite* sprite); +static void AnimMakingItRain(struct Sprite *sprite); // const data // general @@ -7234,6 +7235,51 @@ const struct SpriteTemplate gBitterBladeImpactTemplate = .callback = AnimClawSlash }; +// Make It Rain +const struct SpriteTemplate gMakingItRainTemplate = +{ + .tileTag = ANIM_TAG_COIN, + .paletteTag = ANIM_TAG_COIN, + .oam = &gOamData_AffineNormal_ObjNormal_16x16, + .anims = gCoinAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimMakingItRain, +}; + +const struct SpriteTemplate gRedExplosionSpriteTemplate = +{ + .tileTag = ANIM_TAG_RED_EXPLOSION, + .paletteTag = ANIM_TAG_RED_EXPLOSION, + .oam = &gOamData_AffineOff_ObjNormal_32x32, + .anims = gExplosionAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimSpriteOnMonPos, +}; + +const struct SpriteTemplate gBloodMoonOnslaughtSpriteTemplate = +{ + .tileTag = ANIM_TAG_BEAM, + .paletteTag = ANIM_TAG_BEAM, + .oam = &gOamData_AffineNormal_ObjBlend_64x64, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = sArrowRaidOnslaughtAffineAnimTable, + .callback = AnimAssistPawprint +}; + +const struct SpriteTemplate gMoonUpSpriteTemplate = +{ + .tileTag = ANIM_TAG_BLOOD_MOON, + .paletteTag = ANIM_TAG_BLOOD_MOON, + .oam = &gOamData_AffineOff_ObjNormal_64x64, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimWeatherBallUp, +}; + // functions //general void AnimTask_IsTargetPartner(u8 taskId) @@ -9219,3 +9265,23 @@ void AnimTask_StickySyrup(u8 taskId) gBattleAnimArgs[0] = gAnimDisableStructPtr->syrupBombIsShiny; DestroyAnimVisualTask(taskId); } + +static void AnimMakingItRain(struct Sprite *sprite) +{ + if (gBattleAnimArgs[3] != 0) + SetAverageBattlerPositions(gBattleAnimTarget, FALSE, &sprite->x, &sprite->y); //coin shower on target + + sprite->x += gBattleAnimArgs[0]; + sprite->y += 14; + StartSpriteAnim(sprite, gBattleAnimArgs[1]); + AnimateSprite(sprite); + sprite->data[0] = 0; + sprite->data[1] = 0; + sprite->data[2] = 4; + sprite->data[3] = 16; + sprite->data[4] = -70; + sprite->data[5] = gBattleAnimArgs[2]; + StoreSpriteCallbackInData6(sprite, AnimFallingRock_Step); + sprite->callback = TranslateSpriteInEllipse; + sprite->callback(sprite); +} diff --git a/src/battle_main.c b/src/battle_main.c index f897b4f287..b46feaf0f9 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4717,7 +4717,6 @@ void SwapTurnOrder(u8 id1, u8 id2) u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect) { u32 speed = gBattleMons[battler].speed; - u32 highestStat = GetHighestStatId(battler); // weather abilities if (WEATHER_HAS_EFFECT) @@ -4739,10 +4738,10 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect) speed *= 2; else if (ability == ABILITY_SLOW_START && gDisableStructs[battler].slowStartTimer != 0) speed /= 2; - else if (ability == ABILITY_PROTOSYNTHESIS && gBattleWeather & B_WEATHER_SUN && highestStat == STAT_SPEED) - speed = (speed * 150) / 100; - else if (ability == ABILITY_QUARK_DRIVE && gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && highestStat == STAT_SPEED) - speed = (speed * 150) / 100; + else if (ability == ABILITY_PROTOSYNTHESIS && (gBattleWeather & B_WEATHER_SUN || gBattleStruct->boosterEnergyActivates & gBitTable[battler])) + speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed; + else if (ability == ABILITY_QUARK_DRIVE && (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gBattleStruct->boosterEnergyActivates & gBitTable[battler])) + speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed; // stat stages speed *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][0]; diff --git a/src/battle_message.c b/src/battle_message.c index b1c536b865..69be69a14f 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -849,9 +849,11 @@ static const u8 sText_ShedItsTail[] = _("{B_ATK_NAME_WITH_PREFIX} shed its tail\ static const u8 sText_PkmnTerastallizedInto[] = _("{B_ATK_NAME_WITH_PREFIX} terastallized\ninto the {B_BUFF1} type!"); static const u8 sText_SupersweetAromaWafts[] = _("A supersweet aroma is wafting from\nthe syrup covering {B_ATK_NAME_WITH_PREFIX}!"); static const u8 sText_TidyingUpComplete[] = _("Tidying up complete!"); +static const u8 sText_FickleBeamDoubled[] = _("{B_ATK_NAME_WITH_PREFIX} is going all\nout for this attack!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_FICKLEBEAMDOUBLED - BATTLESTRINGS_TABLE_START] = sText_FickleBeamDoubled, [STRINGID_PKMNTERASTALLIZEDINTO - BATTLESTRINGS_TABLE_START] = sText_PkmnTerastallizedInto, [STRINGID_TIDYINGUPCOMPLETE - BATTLESTRINGS_TABLE_START] = sText_TidyingUpComplete, [STRINGID_SUPERSWEETAROMAWAFTS - BATTLESTRINGS_TABLE_START] = sText_SupersweetAromaWafts, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index befc8f95ca..44ad1868a8 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6391,6 +6391,7 @@ static void Cmd_moveend(void) gBattleStruct->enduredDamage = 0; gBattleStruct->additionalEffectsCounter = 0; gBattleStruct->poisonPuppeteerConfusion = FALSE; + gBattleStruct->fickleBeamBoosted = FALSE; if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE) SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE); gBattleStruct->distortedTypeMatchups = 0; @@ -17123,3 +17124,19 @@ void BS_DamageToQuarterTargetHP(void) gBattlescriptCurrInstr = cmd->nextInstr; } + +void BS_FickleBeamDamageCalculation(void) +{ + NATIVE_ARGS(); + gBattleStruct->fickleBeamBoosted = FALSE; + + if (RandomPercentage(RNG_FICKLE_BEAM, 30)) + { + gBattleStruct->fickleBeamBoosted = TRUE; + gBattlescriptCurrInstr = BattleScript_FickleBeamDoubled; + } + else + { + gBattlescriptCurrInstr = cmd->nextInstr; + } +} diff --git a/src/battle_util.c b/src/battle_util.c index 7bcdf1ee08..b16a33764b 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4095,7 +4095,7 @@ static void ChooseStatBoostAnimation(u32 battler) if (gBattleScripting.animArg1 != 0) // Already set in a different stat so now boosting multiple stats gBattleScripting.animArg1 = (statBuffMoreThan1 ? STAT_ANIM_MULTIPLE_PLUS2 : STAT_ANIM_MULTIPLE_PLUS1); else - gBattleScripting.animArg1 = GET_STAT_BUFF_ID((statsOrder[stat] + 1)) + (!statBuffMoreThan1 ? STAT_ANIM_PLUS1 : STAT_ANIM_PLUS2); + gBattleScripting.animArg1 = GET_STAT_BUFF_ID((statsOrder[stat] + 1)) + (statBuffMoreThan1 ? STAT_ANIM_PLUS2 : STAT_ANIM_PLUS1); } } #undef ANIM_STAT_HP @@ -9071,7 +9071,7 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3 basePower = (basePower > 350) ? 350 : basePower; break; case EFFECT_FICKLE_BEAM: - if (RandomPercentage(RNG_FICKLE_BEAM, 30)) + if (gBattleStruct->fickleBeamBoosted) basePower *= 2; break; case EFFECT_TERA_BLAST: diff --git a/src/data/battle_anim.h b/src/data/battle_anim.h index ec98ecd854..72a3c7121b 100644 --- a/src/data/battle_anim.h +++ b/src/data/battle_anim.h @@ -1461,6 +1461,10 @@ const struct CompressedSpriteSheet gBattleAnimPicTable[] = {gBattleAnimSpriteGfx_TeraCrystal, 0x800, ANIM_TAG_TERA_CRYSTAL}, {gBattleAnimSpriteGfx_TeraShatter, 0x0180, ANIM_TAG_TERA_SHATTER}, {gBattleAnimSpriteGfx_DreepyMissile, 0x200, ANIM_TAG_DREEPY_SHINY}, + {gBattleAnimSpriteGfx_BloodMoon, 0x0800, ANIM_TAG_BLOOD_MOON}, + {gBattleAnimSpriteGfx_RedExplosion, 0x0800, ANIM_TAG_RED_EXPLOSION}, + {gBattleAnimSpriteGfx_Beam, 0x0800, ANIM_TAG_BEAM}, + {gBattleAnimSpriteGfx_PurpleChain, 0x1000, ANIM_TAG_PURPLE_CHAIN}, }; const struct CompressedSpritePalette gBattleAnimPaletteTable[] = @@ -1923,6 +1927,10 @@ const struct CompressedSpritePalette gBattleAnimPaletteTable[] = {gBattleAnimSpritePal_TeraCrystal, ANIM_TAG_TERA_CRYSTAL}, {gBattleAnimSpritePal_TeraShatter, ANIM_TAG_TERA_SHATTER}, {gBattleAnimSpritePal_DreepyMissileShiny, ANIM_TAG_DREEPY_SHINY}, + {gBattleAnimSpritePal_BloodMoon, ANIM_TAG_BLOOD_MOON}, + {gBattleAnimSpritePal_RedExplosion, ANIM_TAG_RED_EXPLOSION}, + {gBattleAnimSpritePal_Beam, ANIM_TAG_BEAM}, + {gBattleAnimSpritePal_PurpleChain, ANIM_TAG_PURPLE_CHAIN}, }; const struct BattleAnimBackground gBattleAnimBackgroundTable[] = diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index f970f0e9f7..ead7cee6f1 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -2169,7 +2169,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_FICKLE_BEAM] = { - .battleScript = BattleScript_EffectHit, + .battleScript = BattleScript_EffectFickleBeam, .battleTvScore = 0, // TODO: Assign points }, diff --git a/src/fieldmap.c b/src/fieldmap.c index 30daae49ca..7c40c755c4 100644 --- a/src/fieldmap.c +++ b/src/fieldmap.c @@ -141,7 +141,7 @@ static void InitBackupMapLayoutConnections(struct MapHeader *mapHeader) for (i = 0; i < count; i++, connection++) { struct MapHeader const *cMap = GetMapHeaderFromConnection(connection); - u32 offset = connection->offset; + s32 offset = connection->offset; switch (connection->direction) { case CONNECTION_SOUTH: diff --git a/src/graphics.c b/src/graphics.c index 9fb0771575..8df7055bd8 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -81,6 +81,9 @@ const u32 gBattleAnimSpritePal_AvalancheRocks[] = INCBIN_U32("graphics/battle_an const u32 gBattleAnimSpriteGfx_NewPokeball[] = INCBIN_U32("graphics/battle_anims/sprites/baton_pass_ball.4bpp.lz"); const u32 gBattleAnimSpritePal_NewPokeball[] = INCBIN_U32("graphics/battle_anims/sprites/baton_pass_ball.gbapal.lz"); +const u32 gBattleAnimSpriteGfx_Beam[] = INCBIN_U32("graphics/battle_anims/sprites/beam.4bpp.lz"); +const u32 gBattleAnimSpritePal_Beam[] = INCBIN_U32("graphics/battle_anims/sprites/beam.gbapal.lz"); + const u32 gBattleAnimSpriteGfx_BerryEaten[] = INCBIN_U32("graphics/battle_anims/sprites/berry_eaten.4bpp.lz"); const u32 gBattleAnimSpritePal_BerryEaten[] = INCBIN_U32("graphics/battle_anims/sprites/berry_eaten.gbapal.lz"); @@ -93,6 +96,9 @@ const u32 gBattleAnimSpritePal_BigRock[] = INCBIN_U32("graphics/battle_anims/spr const u32 gBattleAnimSpriteGfx_BlacephalonHead[] = INCBIN_U32("graphics/battle_anims/sprites/blacephalon_head.4bpp.lz"); const u32 gBattleAnimSpritePal_BlacephalonHead[] = INCBIN_U32("graphics/battle_anims/sprites/blacephalon_head.gbapal.lz"); +const u32 gBattleAnimSpriteGfx_BloodMoon[] = INCBIN_U32("graphics/battle_anims/sprites/blood_moon.4bpp.lz"); +const u32 gBattleAnimSpritePal_BloodMoon[] = INCBIN_U32("graphics/battle_anims/sprites/blood_moon.gbapal.lz"); + const u32 gBattleAnimSpritePal_BlueFlare[] = INCBIN_U32("graphics/battle_anims/sprites/blue_flare.gbapal.lz"); const u32 gBattleAnimSpriteGfx_Branch[] = INCBIN_U32("graphics/battle_anims/sprites/branch.4bpp.lz"); @@ -233,12 +239,18 @@ const u32 gBattleAnimSpritePal_PoisonColumn[] = INCBIN_U32("graphics/battle_anim const u32 gBattleAnimSpriteGfx_PowerTrick[] = INCBIN_U32("graphics/battle_anims/sprites/power_trick.4bpp.lz"); const u32 gBattleAnimSpritePal_PowerTrick[] = INCBIN_U32("graphics/battle_anims/sprites/power_trick.gbapal.lz"); +const u32 gBattleAnimSpriteGfx_PurpleChain[] = INCBIN_U32("graphics/battle_anims/sprites/purple_chain.4bpp.lz"); +const u32 gBattleAnimSpritePal_PurpleChain[] = INCBIN_U32("graphics/battle_anims/sprites/purple_chain.gbapal.lz"); + const u32 gBattleAnimSpriteGfx_PurpleDrake[] = INCBIN_U32("graphics/battle_anims/sprites/purple_drake.4bpp.lz"); const u32 gBattleAnimSpritePal_PurpleDrake[] = INCBIN_U32("graphics/battle_anims/sprites/purple_drake.gbapal.lz"); const u32 gBattleAnimSpriteGfx_RazorShell[] = INCBIN_U32("graphics/battle_anims/sprites/razor_shell.4bpp.lz"); const u32 gBattleAnimSpritePal_RazorShell[] = INCBIN_U32("graphics/battle_anims/sprites/razor_shell.gbapal.lz"); +const u32 gBattleAnimSpriteGfx_RedExplosion[] = INCBIN_U32("graphics/battle_anims/sprites/red_explosion.4bpp.lz"); +const u32 gBattleAnimSpritePal_RedExplosion[] = INCBIN_U32("graphics/battle_anims/sprites/red_explosion.gbapal.lz"); + const u32 gBattleAnimSpriteGfx_RocksSmall[] = INCBIN_U32("graphics/battle_anims/sprites/rock_small.4bpp.lz"); const u32 gBattleAnimSpriteGfx_NewRocks[] = INCBIN_U32("graphics/battle_anims/sprites/rocks_new.4bpp.lz"); diff --git a/src/item_use.c b/src/item_use.c index 502612a184..fd74b7082b 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -430,46 +430,43 @@ static bool8 IsHiddenItemPresentAtCoords(const struct MapEvents *events, s16 x, static bool8 IsHiddenItemPresentInConnection(const struct MapConnection *connection, int x, int y) { + s16 connectionX, connectionY; + struct MapHeader const *const connectionHeader = GetMapHeaderFromConnection(connection); - u16 localX, localY; - u32 localOffset; - s32 localLength; - - struct MapHeader const *const mapHeader = GetMapHeaderFromConnection(connection); - +// To convert our x/y into coordinates that are relative to the connected map, we must: +// - Subtract the virtual offset used for the border buffer (MAP_OFFSET). +// - Subtract the horizontal offset between North/South connections, or the vertical offset for East/West +// - Account for map size. (0,0) is in the NW corner of our map, so when looking North/West we have to add the height/width of the connected map, +// and when looking South/East we have to subtract the height/width of our current map. +#define localX (x - MAP_OFFSET) +#define localY (y - MAP_OFFSET) switch (connection->direction) { - // same weird temp variable behavior seen in IsHiddenItemPresentAtCoords case CONNECTION_NORTH: - localOffset = connection->offset + MAP_OFFSET; - localX = x - localOffset; - localLength = mapHeader->mapLayout->height - MAP_OFFSET; - localY = localLength + y; // additions are reversed for some reason + connectionX = localX - connection->offset; + connectionY = connectionHeader->mapLayout->height + localY; break; case CONNECTION_SOUTH: - localOffset = connection->offset + MAP_OFFSET; - localX = x - localOffset; - localLength = gMapHeader.mapLayout->height + MAP_OFFSET; - localY = y - localLength; + connectionX = localX - connection->offset; + connectionY = localY - gMapHeader.mapLayout->height; break; case CONNECTION_WEST: - localLength = mapHeader->mapLayout->width - MAP_OFFSET; - localX = localLength + x; // additions are reversed for some reason - localOffset = connection->offset + MAP_OFFSET; - localY = y - localOffset; + connectionX = connectionHeader->mapLayout->width + localX; + connectionY = localY - connection->offset; break; case CONNECTION_EAST: - localLength = gMapHeader.mapLayout->width + MAP_OFFSET; - localX = x - localLength; - localOffset = connection->offset + MAP_OFFSET; - localY = y - localOffset; + connectionX = localX - gMapHeader.mapLayout->width; + connectionY = localY - connection->offset; break; default: return FALSE; } - return IsHiddenItemPresentAtCoords(mapHeader->events, localX, localY); + return IsHiddenItemPresentAtCoords(connectionHeader->events, connectionX, connectionY); } +#undef localX +#undef localY + static void CheckForHiddenItemsInMapConnection(u8 taskId) { s16 playerX, playerY; diff --git a/test/battle/ability/adaptability.c b/test/battle/ability/adaptability.c new file mode 100644 index 0000000000..fecdc6b7e2 --- /dev/null +++ b/test/battle/ability/adaptability.c @@ -0,0 +1,46 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Adaptability increases same-type attack bonus from x1.5 to x2"); + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type with Adaptability gives 2.0x STAB", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = GIMMICK_NONE; } + PARAMETRIZE { tera = GIMMICK_TERA; } + GIVEN { + PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_HEADBUTT, gimmick: tera); } + } SCENE { + MESSAGE("Crawdaunt used Headbutt!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from no STAB to 2.0x STAB is a 2.0x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type with Adaptability gives 2.25x STAB", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = GIMMICK_NONE; } + PARAMETRIZE { tera = GIMMICK_TERA; } + GIVEN { + PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_WATER); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_WATER_PULSE, gimmick: tera); } + } SCENE { + MESSAGE("Crawdaunt used Water Pulse!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from 2x STAB to 2.25x STAB is a 1.125x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.125), results[1].damage); + } +} + +TO_DO_BATTLE_TEST("Adaptability does not affect Stellar-type moves"); diff --git a/test/battle/ability/aerilate.c b/test/battle/ability/aerilate.c new file mode 100644 index 0000000000..8a5f889a66 --- /dev/null +++ b/test/battle/ability/aerilate.c @@ -0,0 +1,33 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); +} + +SINGLE_BATTLE_TEST("Aerilate turns a Normal-type move into Flying-type move") +{ + GIVEN { + PLAYER(SPECIES_MEGANIUM); + OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); } + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE, gimmick: GIMMICK_MEGA); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + MESSAGE("It's super effective!"); + } +} + +TO_DO_BATTLE_TEST("Aerilate can not turn certain moves into Flying type moves"); +TO_DO_BATTLE_TEST("Aerilate boosts power of affected moves by 20% (Gen7+)"); +TO_DO_BATTLE_TEST("Aerilate boosts power of affected moves by 30% (Gen6)"); + +// Gen 6-7 +TO_DO_BATTLE_TEST("Aerilate overrides Electrify (Gen6-7)"); +TO_DO_BATTLE_TEST("Aerilate overrides Ion Deluge (Gen6-7)"); +// Gen 8+ +//TO_DO_BATTLE_TEST("Aerilate doesn't override Electrify (Gen8+)"); // No mon with Aerilate exists in Gen8+, but probably behaves similar to Pixilate, which does. +//TO_DO_BATTLE_TEST("Aerilate doesn't override Ion Deluge (Gen8+)"); // Ion Deluge doesn't exist in Gen 8+, but we probably could assume it behaves similar to under Electrify. TODO: Test by hacking SV. diff --git a/test/battle/ability/aftermath.c b/test/battle/ability/aftermath.c new file mode 100644 index 0000000000..65c0be7dfd --- /dev/null +++ b/test/battle/ability/aftermath.c @@ -0,0 +1,4 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Aftermath damages the attacker by 1/4th of its max HP if they faint the target has this ability by a contact move"); diff --git a/test/battle/ability/air_lock.c b/test/battle/ability/air_lock.c new file mode 100644 index 0000000000..0201e86a20 --- /dev/null +++ b/test/battle/ability/air_lock.c @@ -0,0 +1,4 @@ +#include "global.h" +#include "test/battle.h" + +// Tests for Air Lock are handled in test/battle/ability/cloud_nine.c diff --git a/test/battle/ability/analytic.c b/test/battle/ability/analytic.c new file mode 100644 index 0000000000..44c42ae2d5 --- /dev/null +++ b/test/battle/ability/analytic.c @@ -0,0 +1,11 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Analytic increases the power of moves by 30% if it's the last one that uses its move"); +TO_DO_BATTLE_TEST("Analytic takes into account modifications to speeed an priority (Gen 5-8)"); //Eg. Paralysis, Power Weight, Stall +TO_DO_BATTLE_TEST("Analytic does not take into account modifications to speeed an priority (Gen 8)"); //Eg. Paralysis, Power Weight, Stall +TO_DO_BATTLE_TEST("Analytic takes into account the turn order of what fainted Pokémon would've moved"); + +// Triple Battles needed to test +//TO_DO_BATTLE_TEST("If the Pokémon with Analytic is targeting a Pokémon in a flank position that chooses to switch with its ally in the middle, its move's power will always be normal when it attacks the Pokémon that is shifted into the flank position"); +//TO_DO_BATTLE_TEST("If the Pokémon with Analytic targets a Pokémon in the middle whose ally on a flank chooses to shift into the middle position, its move's power still depends on whether the Pokémon that was in the middle (and is now on a flank) has acted when the Pokémon with Analytic uses its move"); diff --git a/test/battle/ability/anger_point.c b/test/battle/ability/anger_point.c index 0b13b9df4b..7cb283426a 100644 --- a/test/battle/ability/anger_point.c +++ b/test/battle/ability/anger_point.c @@ -47,7 +47,9 @@ SINGLE_BATTLE_TEST("Anger Point does not trigger when already at maximum Attack } } -SINGLE_BATTLE_TEST("Anger Point does not trigger when a substitute takes the hit") +TO_DO_BATTLE_TEST("Anger Point triggers when a substitute takes the hit (Gen4)"); + +SINGLE_BATTLE_TEST("Anger Point does not trigger when a substitute takes the hit (Gen5+)") { ASSUME(gMovesInfo[MOVE_FROST_BREATH].alwaysCriticalHit); ASSUME(gMovesInfo[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE); diff --git a/test/battle/ability/anger_shell.c b/test/battle/ability/anger_shell.c index f0d11d7576..c5b490216b 100644 --- a/test/battle/ability/anger_shell.c +++ b/test/battle/ability/anger_shell.c @@ -1,7 +1,7 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Anger Shell activates only if the target had more than 50% of its hp") +SINGLE_BATTLE_TEST("Anger Shell activates only if the target had more than 50% of its HP") { bool32 activates = FALSE; u16 maxHp = 500, hp = 0; diff --git a/test/battle/ability/anticipation.c b/test/battle/ability/anticipation.c new file mode 100644 index 0000000000..a15ff153d9 --- /dev/null +++ b/test/battle/ability/anticipation.c @@ -0,0 +1,24 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Anticipation causes notifies if an opponent has a super-effective move"); +TO_DO_BATTLE_TEST("Anticipation causes notifies if an opponent has a One-hit KO move"); +TO_DO_BATTLE_TEST("Anticipation causes notifies if an opponent has a Self-Destruct or Explosion (Gen4)"); +TO_DO_BATTLE_TEST("Anticipation treats Self-Destruct and Explosion like all other Normal types (Gen5+)"); + +TO_DO_BATTLE_TEST("Anticipation considers Scrappy and Normalize into their effectiveness (Gen4)"); +TO_DO_BATTLE_TEST("Anticipation doesn't consider Scrappy and Normalize into their effectiveness (Gen5+)"); +TO_DO_BATTLE_TEST("Anticipation considers Gravity into their effectiveness (Gen4)"); +TO_DO_BATTLE_TEST("Anticipation doesn't consider Gravity into their effectiveness (Gen5+)"); +TO_DO_BATTLE_TEST("Anticipation doesn't trigger from Counter, Metal Burst or Mirror Coat (Gen4)"); +TO_DO_BATTLE_TEST("Anticipation counts Counter, Metal Burst or Mirror Coat as attacking moves of their types (Gen5+)"); +TO_DO_BATTLE_TEST("Anticipation considers Synchronoise as an ordinary Psychic-type move"); +TO_DO_BATTLE_TEST("Anticipation considers Freeze-Dry as an ordinary Ice-type move"); +TO_DO_BATTLE_TEST("Anticipation considers Flying Press as an ordinary Fighting-type move"); +TO_DO_BATTLE_TEST("Anticipation considers Aura Wheel as an ordinary Electric-type move"); +TO_DO_BATTLE_TEST("Anticipation considers Inverse Battle types"); //Check with Normal-type moves +TO_DO_BATTLE_TEST("Anticipation treats dynamic move types as their base type (Normal)"); // Judgment, Weather Ball, Natural Gift, Techno Blast, Revelation Dance, Multi Attack +TO_DO_BATTLE_TEST("Anticipation treats Hidden Power as Normal Type (Gen4-5)"); +TO_DO_BATTLE_TEST("Anticipation treats Hidden Power as its dynamic type (Gen6+)"); +TO_DO_BATTLE_TEST("Anticipation does not consider Strong Winds on type matchups"); +TO_DO_BATTLE_TEST("Anticipation does not consider ate-abilities"); diff --git a/test/battle/ability/arena_trap.c b/test/battle/ability/arena_trap.c new file mode 100644 index 0000000000..ef0d5b7d8c --- /dev/null +++ b/test/battle/ability/arena_trap.c @@ -0,0 +1,11 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Arena Trap prevents grounded adjacent opponents from switching out"); +TO_DO_BATTLE_TEST("Arena Trap doesn't prevent switch outs if the Pokémon is switched in the same turn the opponent decided to switch out"); +TO_DO_BATTLE_TEST("Arena Trap doesn't prevent switch outs via moves that switch out"); // Baton Pass, U-Turn, Volt Switch, Flip Turn, Parting Shot +TO_DO_BATTLE_TEST("Arena Trap doesn't prevent switch outs via Shed Shell, but not via Teleport"); +TO_DO_BATTLE_TEST("Arena Trap doesn't prevent switch outs via Run Away"); +TO_DO_BATTLE_TEST("Arena Trap doesn't prevent switch outs via Smoke Ball"); +TO_DO_BATTLE_TEST("Arena Trap prevents switch outs from Ghost-type Pokémon (Gen3-5)"); +TO_DO_BATTLE_TEST("Arena Trap doesn't prevent switch outs from Ghost-type Pokémon (Gen6+)"); diff --git a/test/battle/ability/aroma_veil.c b/test/battle/ability/aroma_veil.c new file mode 100644 index 0000000000..dc53fa1492 --- /dev/null +++ b/test/battle/ability/aroma_veil.c @@ -0,0 +1,15 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Taunt"); +TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Torment"); +TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Encore"); +TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Disable"); +TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Cursed Body"); +TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Heal Block"); +TO_DO_BATTLE_TEST("Aroma Veil protects the Pokémon's side from Infatuation"); +TO_DO_BATTLE_TEST("Aroma Veil does not protect the Pokémon's side from Imprison"); + +// Marked in Bulbapedia as need of research +//TO_DO_BATTLE_TEST("Aroma Veil prevents G-Max Meltdown's effect"); +//TO_DO_BATTLE_TEST("Aroma Veil prevents Psychic Noise's effect"); diff --git a/test/battle/ability/as_one.c b/test/battle/ability/as_one.c new file mode 100644 index 0000000000..0d5367bf37 --- /dev/null +++ b/test/battle/ability/as_one.c @@ -0,0 +1,7 @@ +#include "global.h" +#include "test/battle.h" + +// Tests for the individual ability effects are handled in the following times: +// - Unnerve: test/battle/ability/unnerve.c +// - Chilling Neigh: test/battle/ability/chilling_neigh.c +// - Grim Neigh: test/battle/ability/grim_neigh.c diff --git a/test/battle/ability/aura_break.c b/test/battle/ability/aura_break.c new file mode 100644 index 0000000000..93b21421e4 --- /dev/null +++ b/test/battle/ability/aura_break.c @@ -0,0 +1,6 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Aura Break inverts Fairy Aura's effect"); +TO_DO_BATTLE_TEST("Aura Break inverts Dark Aura's effect"); +TO_DO_BATTLE_TEST("Aura Break ignores Mold Breaker abilities"); diff --git a/test/battle/ability/chilling_neigh.c b/test/battle/ability/chilling_neigh.c new file mode 100644 index 0000000000..03be77000b --- /dev/null +++ b/test/battle/ability/chilling_neigh.c @@ -0,0 +1,4 @@ +#include "global.h" +#include "test/battle.h" + +// Tests for Chilling Neigh are handled in test/battle/ability/moxie.c diff --git a/test/battle/ability/chlorophyll.c b/test/battle/ability/chlorophyll.c new file mode 100644 index 0000000000..493a197638 --- /dev/null +++ b/test/battle/ability/chlorophyll.c @@ -0,0 +1,6 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Chlorophyll doubles speed if it's sunny"); +TO_DO_BATTLE_TEST("Chlorophyll doesn't double speed if Cloud Nine/Air Lock is on the field"); +TO_DO_BATTLE_TEST("Chlorophyll doesn't double speed if they have an Utility Umbrella"); diff --git a/test/battle/ability/cloud_nine.c b/test/battle/ability/cloud_nine.c index dfe5611fc4..887769a5ec 100644 --- a/test/battle/ability/cloud_nine.c +++ b/test/battle/ability/cloud_nine.c @@ -1,16 +1,41 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Cloud Nine prevents weather effects") +SINGLE_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Sandstorm") { + u32 species = 0, ability = 0; + PARAMETRIZE { species = SPECIES_PSYDUCK; ability = ABILITY_CLOUD_NINE; } + PARAMETRIZE { species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; } GIVEN { ASSUME(gMovesInfo[MOVE_SANDSTORM].effect == EFFECT_SANDSTORM); - PLAYER(SPECIES_PSYDUCK) { Ability(ABILITY_CLOUD_NINE); } + PLAYER(species) { Ability(ability); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, MOVE_SANDSTORM); } TURN {} } SCENE { - NONE_OF { HP_BAR(player); } + ABILITY_POPUP(player, ability); + MESSAGE("The effects of weather disappeared."); + MESSAGE("Foe Wobbuffet used Sandstorm!"); + MESSAGE("The sandstorm rages."); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SANDSTORM_CONTINUES); + NONE_OF { + HP_BAR(player); + HP_BAR(opponent); + MESSAGE("Foe Wobbuffet is buffeted by the sandstorm!"); + } + MESSAGE("The sandstorm rages."); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SANDSTORM_CONTINUES); } } + +TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Sun"); +TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Rain"); +TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Hail"); +TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Snow"); +TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Fog"); +TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Primal Sun"); +TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Primal Rain"); +TO_DO_BATTLE_TEST("Cloud Nine/Air Lock prevent basic weather effects, but without them disappearing - Strong Winds"); + +// Moves and abilities that are affected by weather should have new tests that check for Clould Nine/Air Lock, like Mold-Breaker Abilities diff --git a/test/battle/ability/damp.c b/test/battle/ability/damp.c index 9a3b795f2a..b567293aa0 100644 --- a/test/battle/ability/damp.c +++ b/test/battle/ability/damp.c @@ -57,7 +57,7 @@ SINGLE_BATTLE_TEST("Damp prevents explosion-like moves from self") } } -SINGLE_BATTLE_TEST("Damp prevents damage from aftermath") +SINGLE_BATTLE_TEST("Damp prevents damage from Aftermath") { GIVEN { ASSUME(gMovesInfo[MOVE_TACKLE].makesContact); diff --git a/test/battle/ability/drought.c b/test/battle/ability/drought.c new file mode 100644 index 0000000000..bbe4dbe853 --- /dev/null +++ b/test/battle/ability/drought.c @@ -0,0 +1,5 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Drought sets up sun for 5 turns (Gen6+)"); +TO_DO_BATTLE_TEST("Drought sets up permanent sun (Gen3-5)"); diff --git a/test/battle/ability/dry_skin.c b/test/battle/ability/dry_skin.c index 368e4b8047..5709a58a94 100644 --- a/test/battle/ability/dry_skin.c +++ b/test/battle/ability/dry_skin.c @@ -15,6 +15,8 @@ SINGLE_BATTLE_TEST("Dry Skin causes 1/8th Max HP damage in Sun") } } +TO_DO_BATTLE_TEST("Dry Skin doesn't get damaged in Sun if Cloud Nine/Air Lock is on the field"); + SINGLE_BATTLE_TEST("Dry Skin heals 1/8th Max HP in Rain") { GIVEN { @@ -29,6 +31,8 @@ SINGLE_BATTLE_TEST("Dry Skin heals 1/8th Max HP in Rain") } } +TO_DO_BATTLE_TEST("Dry Skin doesn't heal in Rain if Cloud Nine/Air Lock is on the field"); + SINGLE_BATTLE_TEST("Dry Skin increases damage taken from Fire-type moves by 25%", s16 damage) { u32 ability; diff --git a/test/battle/ability/flower_gift.c b/test/battle/ability/flower_gift.c index 69a76b8e81..68712641e0 100644 --- a/test/battle/ability/flower_gift.c +++ b/test/battle/ability/flower_gift.c @@ -17,6 +17,8 @@ SINGLE_BATTLE_TEST("Flower Gift transforms Cherrim in harsh sunlight") } } +TO_DO_BATTLE_TEST("Flower Gift doesn't transform Cherrim if Cloud Nine/Air Lock is on the field"); + SINGLE_BATTLE_TEST("Flower Gift transforms Cherrim back to normal when weather changes") { GIVEN { @@ -61,6 +63,8 @@ SINGLE_BATTLE_TEST("Flower Gift transforms Cherrim back to normal when its abili } } +TO_DO_BATTLE_TEST("Forecast transforms Castform back to normal under Cloud Nine/Air Lock"); + DOUBLE_BATTLE_TEST("Flower Gift increases the attack of Cherrim and its allies by 1.5x", s16 damageL, s16 damageR) { bool32 sunny; diff --git a/test/battle/ability/forecast.c b/test/battle/ability/forecast.c index 427a8e6807..296c123dbd 100644 --- a/test/battle/ability/forecast.c +++ b/test/battle/ability/forecast.c @@ -264,12 +264,15 @@ SINGLE_BATTLE_TEST("Forecast transforms Castform back to normal when Sandstorm i } } -SINGLE_BATTLE_TEST("Forecast transforms Castform back to normal under Air Lock") +SINGLE_BATTLE_TEST("Forecast transforms Castform back to normal under Cloud Nine/Air Lock") { + u32 species = 0, ability = 0; + PARAMETRIZE { species = SPECIES_PSYDUCK; ability = ABILITY_CLOUD_NINE; } + PARAMETRIZE { species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; } GIVEN { PLAYER(SPECIES_CASTFORM_NORMAL) { Ability(ABILITY_FORECAST); } OPPONENT(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_RAYQUAZA); + OPPONENT(species) { Ability(ability); } } WHEN { TURN { MOVE(player, MOVE_RAIN_DANCE); } TURN { SWITCH(opponent, 1); } @@ -279,7 +282,7 @@ SINGLE_BATTLE_TEST("Forecast transforms Castform back to normal under Air Lock") ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); MESSAGE("Castform transformed!"); // back to normal - ABILITY_POPUP(opponent, ABILITY_AIR_LOCK); + ABILITY_POPUP(opponent, ability); ABILITY_POPUP(player, ABILITY_FORECAST); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); MESSAGE("Castform transformed!"); diff --git a/test/battle/ability/ate_abilities.c b/test/battle/ability/galvanize.c similarity index 50% rename from test/battle/ability/ate_abilities.c rename to test/battle/ability/galvanize.c index 203452cc01..55dfd7287a 100644 --- a/test/battle/ability/ate_abilities.c +++ b/test/battle/ability/galvanize.c @@ -7,6 +7,19 @@ ASSUMPTIONS ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); } +SINGLE_BATTLE_TEST("Galvanize turns a normal type move into Electric") +{ + GIVEN { + PLAYER(SPECIES_KRABBY); + OPPONENT(SPECIES_GEODUDE_ALOLAN) { Ability(ABILITY_GALVANIZE); } + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + MESSAGE("It's super effective!"); + } +} + SINGLE_BATTLE_TEST("Galvanize can not turn certain moves into Electric type moves") { u32 move; @@ -29,56 +42,7 @@ SINGLE_BATTLE_TEST("Galvanize can not turn certain moves into Electric type move } } -SINGLE_BATTLE_TEST("Galvanize turns a normal type move into Electric") -{ - GIVEN { - PLAYER(SPECIES_KRABBY); - OPPONENT(SPECIES_GEODUDE_ALOLAN) { Ability(ABILITY_GALVANIZE); } - } WHEN { - TURN { MOVE(opponent, MOVE_TACKLE); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); - MESSAGE("It's super effective!"); - } -} - -SINGLE_BATTLE_TEST("Pixilate turns a normal type move into Fairy") -{ - GIVEN { - PLAYER(SPECIES_DRAGONITE); - OPPONENT(SPECIES_ALTARIA) { Item(ITEM_ALTARIANITE); } - } WHEN { - TURN { MOVE(opponent, MOVE_TACKLE, gimmick: GIMMICK_MEGA); } - } SCENE { - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); - MESSAGE("It's super effective!"); - } -} - -SINGLE_BATTLE_TEST("Refrigerate turns a normal type move into Ice") -{ - GIVEN { - PLAYER(SPECIES_MEGANIUM); - OPPONENT(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); } - } WHEN { - TURN { MOVE(opponent, MOVE_TACKLE); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); - MESSAGE("It's super effective!"); - } -} - -SINGLE_BATTLE_TEST("Aerilate turns a normal type move into Flying") -{ - GIVEN { - PLAYER(SPECIES_MEGANIUM); - OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); } - } WHEN { - TURN { MOVE(opponent, MOVE_TACKLE, gimmick: GIMMICK_MEGA); } - } SCENE { - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); - MESSAGE("It's super effective!"); - } -} +TO_DO_BATTLE_TEST("Galvanize boosts power of affected moves by 20% (Gen7+)"); +TO_DO_BATTLE_TEST("Galvanize boosts power of affected moves by 30% (Gen6)"); +TO_DO_BATTLE_TEST("(DYNAMAX) Galvanize turns Max Strike into Max Lightning when not used by Gigantamax Pikachu/Toxtricity"); +//TO_DO_BATTLE_TEST("(DYNAMAX) Galvanize doesn't turn Max Strike into Max Lightning when used by Gigantamax Pikachu/Toxtricity, instead becoming G-Max Volt Crash/Stun Shock"); // Marked in Bulbapedia as "needs research", so this assumes that it behaves like Pixilate. diff --git a/test/battle/ability/grim_neigh.c b/test/battle/ability/grim_neigh.c new file mode 100644 index 0000000000..b4d82487d8 --- /dev/null +++ b/test/battle/ability/grim_neigh.c @@ -0,0 +1,109 @@ +#include "global.h" +#include "test/battle.h" + +DOUBLE_BATTLE_TEST("Grim Neigh raises Sp. Attack by one stage after directly causing a Pokemon to faint") +{ + u32 species = 0, ability = 0, abilityPopUp = 0; + PARAMETRIZE { species = SPECIES_SPECTRIER; ability = ABILITY_GRIM_NEIGH; abilityPopUp = ABILITY_GRIM_NEIGH; } + PARAMETRIZE { species = SPECIES_CALYREX_SHADOW_RIDER; ability = ABILITY_AS_ONE_SHADOW_RIDER; abilityPopUp = ABILITY_GRIM_NEIGH; } + GIVEN { + ASSUME(gMovesInfo[MOVE_DISCHARGE].target == MOVE_TARGET_FOES_AND_ALLY); + PLAYER(species) { Ability(ability); } + PLAYER(SPECIES_SNORUNT) { HP(1); } + OPPONENT(SPECIES_GLALIE) { HP(1); } + OPPONENT(SPECIES_ABRA) { HP(1); } + OPPONENT(SPECIES_ABRA); + } WHEN { + TURN { MOVE(playerLeft, MOVE_DISCHARGE); SEND_OUT(opponentLeft, 2); } + } SCENE { + int i; + + ANIMATION(ANIM_TYPE_MOVE, MOVE_DISCHARGE, playerLeft); + for (i = 0; i < 3; i++) { + ONE_OF { + MESSAGE("Snorunt fainted!"); + MESSAGE("Foe Glalie fainted!"); + MESSAGE("Foe Abra fainted!"); + } + ABILITY_POPUP(playerLeft, abilityPopUp); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + if (species == SPECIES_SPECTRIER) + MESSAGE("Spectrier's Grim Neigh raised its Sp. Atk!"); + else + MESSAGE("Calyrex's Grim Neigh raised its Sp. Atk!"); + } + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 3); + } +} + +DOUBLE_BATTLE_TEST("Grim Neigh does not trigger if Pokemon faint to indirect damage or damage from other Pokemon") +{ + u32 species = 0, ability = 0, abilityPopUp = 0; + PARAMETRIZE { species = SPECIES_SPECTRIER; ability = ABILITY_GRIM_NEIGH; abilityPopUp = ABILITY_GRIM_NEIGH; } + PARAMETRIZE { species = SPECIES_CALYREX_SHADOW_RIDER; ability = ABILITY_AS_ONE_SHADOW_RIDER; abilityPopUp = ABILITY_GRIM_NEIGH; } + GIVEN { + PLAYER(species) { Ability(ability); } + PLAYER(SPECIES_SNORUNT) { HP(1); Status1(STATUS1_POISON); } + OPPONENT(SPECIES_GLALIE) { HP(1); Status1(STATUS1_BURN); } + OPPONENT(SPECIES_ABRA) { HP(1); } + OPPONENT(SPECIES_ABRA); + } WHEN { + TURN { MOVE(playerRight, MOVE_QUICK_ATTACK, target: opponentRight); SEND_OUT(opponentLeft, 2); } + } SCENE { + int i; + + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, playerRight); + for (i = 0; i < 3; i++) { + ONE_OF { + MESSAGE("Snorunt fainted!"); + MESSAGE("Foe Glalie fainted!"); + MESSAGE("Foe Abra fainted!"); + } + NONE_OF { + ABILITY_POPUP(playerLeft, abilityPopUp); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Salamence's Moxie raised its Sp. Atk!"); + MESSAGE("Spectrier's Grim Neigh raised its Sp. Atk!"); + MESSAGE("Calyrex's Grim Neigh raised its Sp. Atk!"); + } + } + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + } +} + +DOUBLE_BATTLE_TEST("Grim Neigh does not increase damage done by the same move that causes another Pokemon to faint") +{ + s16 damage[2]; + u32 species = 0, ability = 0, abilityPopUp = 0; + PARAMETRIZE { species = SPECIES_SPECTRIER; ability = ABILITY_GRIM_NEIGH; abilityPopUp = ABILITY_GRIM_NEIGH; } + PARAMETRIZE { species = SPECIES_CALYREX_SHADOW_RIDER; ability = ABILITY_AS_ONE_SHADOW_RIDER; abilityPopUp = ABILITY_GRIM_NEIGH; } + + KNOWN_FAILING; // Requires simultaneous damage implementation + GIVEN { + ASSUME(gMovesInfo[MOVE_DISCHARGE].target == MOVE_TARGET_FOES_AND_ALLY); + PLAYER(species) { Ability(ability); } + PLAYER(SPECIES_ABRA) { HP(1); } + OPPONENT(SPECIES_GLALIE); + OPPONENT(SPECIES_GLALIE); + OPPONENT(SPECIES_ABRA); + } WHEN { + TURN { MOVE(playerLeft, MOVE_DISCHARGE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DISCHARGE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &damage[0]); + HP_BAR(playerRight); + MESSAGE("Abra fainted!"); + ABILITY_POPUP(playerLeft, abilityPopUp); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + if (species == SPECIES_SPECTRIER) + MESSAGE("Spectrier's Grim Neigh raised its Sp. Atk!"); + else + MESSAGE("Calyrex's Grim Neigh raised its Sp. Atk!"); + HP_BAR(opponentRight, captureDamage: &damage[1]); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(damage[0], damage[1]); + } +} diff --git a/test/battle/ability/harvest.c b/test/battle/ability/harvest.c index 9fb969b036..b4783c4542 100644 --- a/test/battle/ability/harvest.c +++ b/test/battle/ability/harvest.c @@ -3,6 +3,7 @@ TO_DO_BATTLE_TEST("Harvest has a 50% chance to restore a Berry at the end of the turn"); TO_DO_BATTLE_TEST("Harvest always restores a Berry in Sunlight"); +TO_DO_BATTLE_TEST("Harvest doesn't always restore a Berry if Cloud Nine/Air Lock is on the field"); TO_DO_BATTLE_TEST("Harvest restores a Berry even after being switched out and back in"); TO_DO_BATTLE_TEST("Harvest restores a Berry consumed by Fling"); TO_DO_BATTLE_TEST("Harvest restores a Berry consumed by Natural Gift"); diff --git a/test/battle/ability/hydration.c b/test/battle/ability/hydration.c index 2dca490c02..7d16ad04ae 100644 --- a/test/battle/ability/hydration.c +++ b/test/battle/ability/hydration.c @@ -14,3 +14,5 @@ SINGLE_BATTLE_TEST("Hydration cures non-volatile Status conditions if it is rain STATUS_ICON(player, none: TRUE); } } + +TO_DO_BATTLE_TEST("Hydration doesn't cure status conditions if Cloud Nine/Air Lock is on the field"); diff --git a/test/battle/ability/ice_body.c b/test/battle/ability/ice_body.c index 1aa7c80a4d..304059a00e 100644 --- a/test/battle/ability/ice_body.c +++ b/test/battle/ability/ice_body.c @@ -26,3 +26,5 @@ SINGLE_BATTLE_TEST("Ice Body recovers 1/16th of Max HP in hail.") MESSAGE("Glalie's Ice Body healed it a little bit!"); } } + +TO_DO_BATTLE_TEST("Sand Rush doesn't recover HP if Cloud Nine/Air Lock is on the field"); diff --git a/test/battle/ability/ice_face.c b/test/battle/ability/ice_face.c new file mode 100644 index 0000000000..53917b5623 --- /dev/null +++ b/test/battle/ability/ice_face.c @@ -0,0 +1,9 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Ice Face blocks physical moves, changing Eiscue into its Noice Face form"); // Include Special move in test +TO_DO_BATTLE_TEST("Ice Face is restored if hail or snow begins while Noice Face Eiscue is out"); +TO_DO_BATTLE_TEST("Ice Face is restored if Noice Face Eiscue is sent in while hail or snow is active"); +TO_DO_BATTLE_TEST("Ice Face is not restored if Eiscue changes into Noice Face form while there's already hail"); +TO_DO_BATTLE_TEST("Ice Face form change persists after switching out"); +TO_DO_BATTLE_TEST("Ice Face doesn't transform Eiscue if Cloud Nine/Air Lock is on the field"); diff --git a/test/battle/ability/leaf_guard.c b/test/battle/ability/leaf_guard.c index 6c559ea48a..2c55236def 100644 --- a/test/battle/ability/leaf_guard.c +++ b/test/battle/ability/leaf_guard.c @@ -27,6 +27,8 @@ SINGLE_BATTLE_TEST("Leaf Guard prevents non-volatile status conditions in sun") } } +TO_DO_BATTLE_TEST("Leaf Guard doesn't prevent non-volatile status conditions if Cloud Nine/Air Lock is on the field"); + SINGLE_BATTLE_TEST("Leaf Guard prevents status conditions from Flame Orb and Toxic Orb") { u32 item; @@ -49,6 +51,8 @@ SINGLE_BATTLE_TEST("Leaf Guard prevents status conditions from Flame Orb and Tox } } +TO_DO_BATTLE_TEST("Leaf Guard doesn't prevent status conditions from Flame Orb and Toxic Orb if Cloud Nine/Air Lock is on the field"); + SINGLE_BATTLE_TEST("Leaf Guard prevents Rest during sun") { GIVEN { @@ -66,3 +70,5 @@ SINGLE_BATTLE_TEST("Leaf Guard prevents Rest during sun") } } } + +TO_DO_BATTLE_TEST("Leaf Guard doesn't prevent Rest if Cloud Nine/Air Lock is on the field"); diff --git a/test/battle/ability/moxie.c b/test/battle/ability/moxie.c index b60a11507a..79b1fc989f 100644 --- a/test/battle/ability/moxie.c +++ b/test/battle/ability/moxie.c @@ -1,12 +1,15 @@ #include "global.h" #include "test/battle.h" -DOUBLE_BATTLE_TEST("Moxie raises Attack by one stage after directly causing a Pokemon to faint") +DOUBLE_BATTLE_TEST("Moxie/Chilling Neigh raises Attack by one stage after directly causing a Pokemon to faint") { - ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY); - + u32 species = 0, ability = 0, abilityPopUp = 0; + PARAMETRIZE { species = SPECIES_SALAMENCE; ability = ABILITY_MOXIE; abilityPopUp = ABILITY_MOXIE; } + PARAMETRIZE { species = SPECIES_GLASTRIER; ability = ABILITY_CHILLING_NEIGH; abilityPopUp = ABILITY_CHILLING_NEIGH; } + PARAMETRIZE { species = SPECIES_CALYREX_ICE_RIDER; ability = ABILITY_AS_ONE_ICE_RIDER; abilityPopUp = ABILITY_CHILLING_NEIGH; } GIVEN { - PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); } + ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY); + PLAYER(species) { Ability(ability); } PLAYER(SPECIES_SNORUNT) { HP(1); } OPPONENT(SPECIES_GLALIE) { HP(1); } OPPONENT(SPECIES_ABRA) { HP(1); } @@ -23,19 +26,28 @@ DOUBLE_BATTLE_TEST("Moxie raises Attack by one stage after directly causing a Po MESSAGE("Foe Glalie fainted!"); MESSAGE("Foe Abra fainted!"); } - ABILITY_POPUP(playerLeft, ABILITY_MOXIE); + ABILITY_POPUP(playerLeft, abilityPopUp); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); - MESSAGE("Salamence's Moxie raised its Attack!"); + if (species == SPECIES_SALAMENCE) + MESSAGE("Salamence's Moxie raised its Attack!"); + else if (species == SPECIES_GLASTRIER) + MESSAGE("Glastrier's Chilling Neigh raised its Attack!"); + else + MESSAGE("Calyrex's Chilling Neigh raised its Attack!"); } } THEN { EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 3); } } -DOUBLE_BATTLE_TEST("Moxie does not trigger if Pokemon faint to indirect damage or damage from other Pokemon") +DOUBLE_BATTLE_TEST("Moxie/Chilling Neigh does not trigger if Pokemon faint to indirect damage or damage from other Pokemon") { + u32 species = 0, ability = 0, abilityPopUp = 0; + PARAMETRIZE { species = SPECIES_SALAMENCE; ability = ABILITY_MOXIE; abilityPopUp = ABILITY_MOXIE; } + PARAMETRIZE { species = SPECIES_GLASTRIER; ability = ABILITY_CHILLING_NEIGH; abilityPopUp = ABILITY_CHILLING_NEIGH; } + PARAMETRIZE { species = SPECIES_CALYREX_ICE_RIDER; ability = ABILITY_AS_ONE_ICE_RIDER; abilityPopUp = ABILITY_CHILLING_NEIGH; } GIVEN { - PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); } + PLAYER(species) { Ability(ability); } PLAYER(SPECIES_SNORUNT) { HP(1); Status1(STATUS1_POISON); } OPPONENT(SPECIES_GLALIE) { HP(1); Status1(STATUS1_BURN); } OPPONENT(SPECIES_ABRA) { HP(1); } @@ -53,9 +65,11 @@ DOUBLE_BATTLE_TEST("Moxie does not trigger if Pokemon faint to indirect damage o MESSAGE("Foe Abra fainted!"); } NONE_OF { - ABILITY_POPUP(playerLeft, ABILITY_MOXIE); + ABILITY_POPUP(playerLeft, abilityPopUp); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); MESSAGE("Salamence's Moxie raised its Attack!"); + MESSAGE("Glastrier's Chilling Neigh raised its Attack!"); + MESSAGE("Calyrex's Chilling Neigh raised its Attack!"); } } } THEN { @@ -63,12 +77,15 @@ DOUBLE_BATTLE_TEST("Moxie does not trigger if Pokemon faint to indirect damage o } } -SINGLE_BATTLE_TEST("Moxie does not trigger when already at maximum Attack stage") +SINGLE_BATTLE_TEST("Moxie/Chilling Neigh does not trigger when already at maximum Attack stage") { - ASSUME(gMovesInfo[MOVE_BELLY_DRUM].effect == EFFECT_BELLY_DRUM); - + u32 species = 0, ability = 0, abilityPopUp = 0; + PARAMETRIZE { species = SPECIES_SALAMENCE; ability = ABILITY_MOXIE; abilityPopUp = ABILITY_MOXIE; } + PARAMETRIZE { species = SPECIES_GLASTRIER; ability = ABILITY_CHILLING_NEIGH; abilityPopUp = ABILITY_CHILLING_NEIGH; } + PARAMETRIZE { species = SPECIES_CALYREX_ICE_RIDER; ability = ABILITY_AS_ONE_ICE_RIDER; abilityPopUp = ABILITY_CHILLING_NEIGH; } GIVEN { - PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); } + ASSUME(gMovesInfo[MOVE_BELLY_DRUM].effect == EFFECT_BELLY_DRUM); + PLAYER(species) { Ability(ability); } OPPONENT(SPECIES_SNORUNT) { HP(1); } OPPONENT(SPECIES_SNORUNT); } WHEN { @@ -77,28 +94,38 @@ SINGLE_BATTLE_TEST("Moxie does not trigger when already at maximum Attack stage" } SCENE { ANIMATION(ANIM_TYPE_MOVE, MOVE_BELLY_DRUM, player); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Salamence cut its own HP and maximized ATTACK!"); + if (species == SPECIES_SALAMENCE) + MESSAGE("Salamence cut its own HP and maximized ATTACK!"); + else if (species == SPECIES_GLASTRIER) + MESSAGE("Glastrier cut its own HP and maximized ATTACK!"); + else + MESSAGE("Calyrex cut its own HP and maximized ATTACK!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player); MESSAGE("Foe Snorunt fainted!"); - NONE_OF { - ABILITY_POPUP(player, ABILITY_MOXIE); + NONE_OF { + ABILITY_POPUP(player, abilityPopUp); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); MESSAGE("Salamence's Moxie raised its Attack!"); + MESSAGE("Glastrier's Chilling Neigh raised its Attack!"); + MESSAGE("Calyrex's Chilling Neigh raised its Attack!"); } } THEN { EXPECT_EQ(player->statStages[STAT_ATK], MAX_STAT_STAGE); } } -DOUBLE_BATTLE_TEST("Moxie does not increase damage done by the same move that causes another Pokemon to faint") +DOUBLE_BATTLE_TEST("Moxie/Chilling Neigh does not increase damage done by the same move that causes another Pokemon to faint") { s16 damage[2]; - - ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY); + u32 species = 0, ability = 0, abilityPopUp = 0; + PARAMETRIZE { species = SPECIES_SALAMENCE; ability = ABILITY_MOXIE; abilityPopUp = ABILITY_MOXIE; } + PARAMETRIZE { species = SPECIES_GLASTRIER; ability = ABILITY_CHILLING_NEIGH; abilityPopUp = ABILITY_CHILLING_NEIGH; } + PARAMETRIZE { species = SPECIES_CALYREX_ICE_RIDER; ability = ABILITY_AS_ONE_ICE_RIDER; abilityPopUp = ABILITY_CHILLING_NEIGH; } KNOWN_FAILING; // Requires simultaneous damage implementation GIVEN { - PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); } + ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY); + PLAYER(species) { Ability(ability); } PLAYER(SPECIES_ABRA) { HP(1); } OPPONENT(SPECIES_GLALIE); OPPONENT(SPECIES_GLALIE); @@ -110,9 +137,14 @@ DOUBLE_BATTLE_TEST("Moxie does not increase damage done by the same move that ca HP_BAR(opponentLeft, captureDamage: &damage[0]); HP_BAR(playerRight); MESSAGE("Abra fainted!"); - ABILITY_POPUP(playerLeft, ABILITY_MOXIE); + ABILITY_POPUP(playerLeft, abilityPopUp); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); - MESSAGE("Salamence's Moxie raised its Attack!"); + if (species == SPECIES_SALAMENCE) + MESSAGE("Salamence's Moxie raised its Attack!"); + else if (species == SPECIES_GLASTRIER) + MESSAGE("Glastrier's Chilling Neigh raised its Attack!"); + else + MESSAGE("Calyrex's Chilling Neigh raised its Attack!"); HP_BAR(opponentRight, captureDamage: &damage[1]); } THEN { EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); diff --git a/test/battle/ability/normalize.c b/test/battle/ability/normalize.c new file mode 100644 index 0000000000..d3e8df6bee --- /dev/null +++ b/test/battle/ability/normalize.c @@ -0,0 +1,16 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Normalize tuns a move into a Normal-type move"); +TO_DO_BATTLE_TEST("Normalize boosts power of both affected and originally Normal-type moves by 20% (Gen7+)"); +TO_DO_BATTLE_TEST("Normalize affects status moves"); // Eg. Thunder Wave can affect Ground types +TO_DO_BATTLE_TEST("Normalize makes Flying Press do Normal/Flying damage"); +TO_DO_BATTLE_TEST("Normalize still makes Freeze-Dry do super effective damage to Water-type Pokémon"); +TO_DO_BATTLE_TEST("Normalize-affected moves become Electric-type under Electrify's effect"); +TO_DO_BATTLE_TEST("Normalize-affected moves become Electric-type under Ion Deluge's effect"); +TO_DO_BATTLE_TEST("Normalize doesn't affect Hidden Power's type"); +TO_DO_BATTLE_TEST("Normalize doesn't affect Weather Ball's type"); +TO_DO_BATTLE_TEST("Normalize doesn't affect Natural Gift's type"); +TO_DO_BATTLE_TEST("Normalize doesn't affect Judgment/Techno Blast/Multi-Attack's type"); +TO_DO_BATTLE_TEST("Normalize doesn't affect Terrain Pulse's type"); +TO_DO_BATTLE_TEST("Normalize doesn't affect damaging Z-Move types"); diff --git a/test/battle/ability/orichalcum_pulse.c b/test/battle/ability/orichalcum_pulse.c new file mode 100644 index 0000000000..295e0db23c --- /dev/null +++ b/test/battle/ability/orichalcum_pulse.c @@ -0,0 +1,5 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Orichalcum Pulse sets up sun for 5 turns"); +TO_DO_BATTLE_TEST("Orichalcum Pulse boosts the Pokémon's Attack by 33% in sun, even if it's holding an Utility Umbrella"); diff --git a/test/battle/ability/pixilate.c b/test/battle/ability/pixilate.c new file mode 100644 index 0000000000..97c9c37a0c --- /dev/null +++ b/test/battle/ability/pixilate.c @@ -0,0 +1,35 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); +} + +SINGLE_BATTLE_TEST("Pixilate turns a Normal-type move into a Fairy-type move") +{ + GIVEN { + PLAYER(SPECIES_DRAGONITE); + OPPONENT(SPECIES_ALTARIA) { Item(ITEM_ALTARIANITE); } + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE, gimmick: GIMMICK_MEGA); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + MESSAGE("It's super effective!"); + } +} + +TO_DO_BATTLE_TEST("Pixilate can not turn certain moves into Fairy type moves"); +TO_DO_BATTLE_TEST("Pixilate boosts power of affected moves by 20% (Gen7+)"); +TO_DO_BATTLE_TEST("Pixilate boosts power of affected moves by 30% (Gen6)"); +TO_DO_BATTLE_TEST("(DYNAMAX) Pixilate turns Max Strike into Max Starfall when not used by Gigantamax Alcremie"); +TO_DO_BATTLE_TEST("(DYNAMAX) Pixilate doesn't turn Max Strike into Max Starfall when used by Gigantamax Alcremie, instead becoming G-Max Finale"); + +// Gen 6-7 +TO_DO_BATTLE_TEST("Pixilate overrides Electrify (Gen6-7)"); +TO_DO_BATTLE_TEST("Pixilate overrides Ion Deluge (Gen6-7)"); +// Gen 8+ +TO_DO_BATTLE_TEST("Pixilate doesn't override Electrify (Gen8+)"); +//TO_DO_BATTLE_TEST("Pixilate doesn't override Ion Deluge (Gen8+)"); // Ion Deluge doesn't exist in Gen 8+, but we probably could assume it behaves similar to under Electrify. TODO: Test by hacking SV. diff --git a/test/battle/ability/protosynthesis.c b/test/battle/ability/protosynthesis.c index 3cb35164ec..58f10b366f 100644 --- a/test/battle/ability/protosynthesis.c +++ b/test/battle/ability/protosynthesis.c @@ -99,3 +99,7 @@ SINGLE_BATTLE_TEST("Protosynthesis activates on switch-in") MESSAGE("Roaring Moon's Attack was heightened!"); } } + +TO_DO_BATTLE_TEST("Protosynthesis activates in sun before Booster Energy"); +TO_DO_BATTLE_TEST("Protosynthesis activates even if the Pokémon is holding an Utility Umbrella"); +TO_DO_BATTLE_TEST("Protosynthesis doesn't activate if Cloud Nine/Air Lock is on the field"); diff --git a/test/battle/ability/rain_dish.c b/test/battle/ability/rain_dish.c index dd647f0eb9..ed71a67f92 100644 --- a/test/battle/ability/rain_dish.c +++ b/test/battle/ability/rain_dish.c @@ -14,3 +14,5 @@ SINGLE_BATTLE_TEST("Rain Dish recovers 1/16th of Max HP in Rain") HP_BAR(player, damage: -(100 / 16)); } } + +TO_DO_BATTLE_TEST("Rain Dish doesn't recover HP if Cloud Nine/Air Lock is on the field"); diff --git a/test/battle/ability/refrigerate.c b/test/battle/ability/refrigerate.c new file mode 100644 index 0000000000..dbbaa30eb8 --- /dev/null +++ b/test/battle/ability/refrigerate.c @@ -0,0 +1,34 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); +} + +SINGLE_BATTLE_TEST("Refrigerate turns a Normal-type move into a Ice-type move") +{ + GIVEN { + PLAYER(SPECIES_MEGANIUM); + OPPONENT(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); } + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + MESSAGE("It's super effective!"); + } +} + +TO_DO_BATTLE_TEST("Refrigerate can not turn certain moves into Ice type moves"); +TO_DO_BATTLE_TEST("Refrigerate boosts power of affected moves by 20% (Gen7+)"); +TO_DO_BATTLE_TEST("Refrigerate boosts power of affected moves by 30% (Gen6)"); +TO_DO_BATTLE_TEST("(DYNAMAX) Refrigerate turns Max Strike into Max Hailstorm when not used by Gigantamax Lapras"); +//TO_DO_BATTLE_TEST("(DYNAMAX) Refrigerate doesn't turn Max Strike into Max Hailstorm when used by Gigantamax Lapras, instead becoming G-Max Resonance"); // Marked in Bulbapedia as "needs research", so this assumes that it behaves like Pixilate. + +// Gen 6-7 +TO_DO_BATTLE_TEST("Refrigerate overrides Electrify (Gen6-7)"); +TO_DO_BATTLE_TEST("Refrigerate overrides Ion Deluge (Gen6-7)"); +// Gen 8+ +//TO_DO_BATTLE_TEST("Refrigerate doesn't override Electrify (Gen8+)"); // Bulbapedia doesn't list this effect, so it assumes it behaves like Pixilate. +//TO_DO_BATTLE_TEST("Refrigerate doesn't override Ion Deluge (Gen8+)"); // Ion Deluge doesn't exist in Gen 8+, but we probably could assume it behaves similar to under Electrify. TODO: Test by hacking SV. diff --git a/test/battle/ability/sand_force.c b/test/battle/ability/sand_force.c new file mode 100644 index 0000000000..e17722a885 --- /dev/null +++ b/test/battle/ability/sand_force.c @@ -0,0 +1,6 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Sand Force prevents damage from sandstorm"); +TO_DO_BATTLE_TEST("Sand Force increases the power of Rock-, Ground- and Steel-type moves by 30% in sandstorm"); +TO_DO_BATTLE_TEST("Sand Force increases move power if Cloud Nine/Air Lock is on the field"); diff --git a/test/battle/ability/sand_rush.c b/test/battle/ability/sand_rush.c new file mode 100644 index 0000000000..fa7695c129 --- /dev/null +++ b/test/battle/ability/sand_rush.c @@ -0,0 +1,6 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Sand Rush prevents damage from sandstorm"); +TO_DO_BATTLE_TEST("Sand Rush doubles speed from sandstorm"); +TO_DO_BATTLE_TEST("Sand Rush doesn't double speed if Cloud Nine/Air Lock is on the field"); diff --git a/test/battle/ability/sand_veil.c b/test/battle/ability/sand_veil.c index 9e4a634de2..f42c267273 100644 --- a/test/battle/ability/sand_veil.c +++ b/test/battle/ability/sand_veil.c @@ -28,3 +28,6 @@ SINGLE_BATTLE_TEST("Sand Veil increases evasion during sandstorm") HP_BAR(player); } } + +TO_DO_BATTLE_TEST("Sand Veil doesn't prevent Sandstorm damage if Cloud Nine/Air Lock is on the field"); +TO_DO_BATTLE_TEST("Sand Veil doesn't increase evasion if Cloud Nine/Air Lock is on the field"); diff --git a/test/battle/ability/slush_rush.c b/test/battle/ability/slush_rush.c new file mode 100644 index 0000000000..8ae62454bb --- /dev/null +++ b/test/battle/ability/slush_rush.c @@ -0,0 +1,6 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Slush Rush doubles speed from hail"); +TO_DO_BATTLE_TEST("Slush Rush doubles speed from snow"); +TO_DO_BATTLE_TEST("Slush Rush doesn't double speed if Cloud Nine/Air Lock is on the field"); diff --git a/test/battle/ability/snow_cloak.c b/test/battle/ability/snow_cloak.c index f1bfbcb9b0..a4d1acadb7 100644 --- a/test/battle/ability/snow_cloak.c +++ b/test/battle/ability/snow_cloak.c @@ -27,3 +27,6 @@ SINGLE_BATTLE_TEST("Snow Cloak increases evasion during hail") HP_BAR(player); } } + +TO_DO_BATTLE_TEST("Snow Cloak doesn't prevent hail damage if Cloud Nine/Air Lock is on the field"); +TO_DO_BATTLE_TEST("Snow Cloak doesn't increase evasion if Cloud Nine/Air Lock is on the field"); diff --git a/test/battle/ability/solar_power.c b/test/battle/ability/solar_power.c new file mode 100644 index 0000000000..f14ea11ee1 --- /dev/null +++ b/test/battle/ability/solar_power.c @@ -0,0 +1,7 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Solar Power increases a Sp. Attack by x1.5 in Sun"); +TO_DO_BATTLE_TEST("Solar Power doesn't increases a Sp. Attack if Cloud Nine/Air Lock is on the field"); +TO_DO_BATTLE_TEST("Solar Power causes the Pokémon to lose 1/8 max HP in Sun"); +TO_DO_BATTLE_TEST("Solar Power doesn't cause the Pokémon to lose 1/8 max HP if Cloud Nine/Air Lock is on the field"); diff --git a/test/battle/ability/swift_swim.c b/test/battle/ability/swift_swim.c new file mode 100644 index 0000000000..4282ac2741 --- /dev/null +++ b/test/battle/ability/swift_swim.c @@ -0,0 +1,6 @@ +#include "global.h" +#include "test/battle.h" + +TO_DO_BATTLE_TEST("Swift Swim doubles speed if it's raining"); +TO_DO_BATTLE_TEST("Swift Swim doesn't double speed if Cloud Nine/Air Lock is on the field"); +TO_DO_BATTLE_TEST("Swift Swim doesn't double speed if they have an Utility Umbrella"); diff --git a/test/battle/ability/unnerve.c b/test/battle/ability/unnerve.c new file mode 100644 index 0000000000..9ad4ee7e5f --- /dev/null +++ b/test/battle/ability/unnerve.c @@ -0,0 +1,6 @@ +#include "global.h" +#include "test/battle.h" + +// Remember to add a PARAMETRIZE for As One in the following tests: +TO_DO_BATTLE_TEST("Unnerve prevents opposing Pokémon from eating their own berries"); +TO_DO_BATTLE_TEST("Unnerve doesn't prevent opposing Pokémon from using Natural Gift"); diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index f14c398b64..7a70ba0de0 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -86,46 +86,6 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type gives that type 2x } } -SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type with Adaptability gives 2.0x STAB", s16 damage) -{ - bool32 tera; - PARAMETRIZE { tera = GIMMICK_NONE; } - PARAMETRIZE { tera = GIMMICK_TERA; } - GIVEN { - PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_NORMAL); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_HEADBUTT, gimmick: tera); } - } SCENE { - MESSAGE("Crawdaunt used Headbutt!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player); - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - // The jump from no STAB to 2.0x STAB is a 2.0x boost. - EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type with Adaptability gives 2.25x STAB", s16 damage) -{ - bool32 tera; - PARAMETRIZE { tera = GIMMICK_NONE; } - PARAMETRIZE { tera = GIMMICK_TERA; } - GIVEN { - PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_WATER); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_WATER_PULSE, gimmick: tera); } - } SCENE { - MESSAGE("Crawdaunt used Water Pulse!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player); - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - // The jump from 2x STAB to 2.25x STAB is a 1.125x boost. - EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.125), results[1].damage); - } -} - SINGLE_BATTLE_TEST("(TERA) Terastallizing boosts moves of the same type to 60 BP", s16 damage) { bool32 tera; diff --git a/test/battle/ability/booster_energy.c b/test/battle/hold_effect/booster_energy.c similarity index 100% rename from test/battle/ability/booster_energy.c rename to test/battle/hold_effect/booster_energy.c diff --git a/test/battle/hold_effect/utility_umbrella.c b/test/battle/hold_effect/utility_umbrella.c index e02a4fdef3..f04a773789 100644 --- a/test/battle/hold_effect/utility_umbrella.c +++ b/test/battle/hold_effect/utility_umbrella.c @@ -52,3 +52,5 @@ SINGLE_BATTLE_TEST("Utility Umbrella blocks Rain damage modifiers", s16 damage) EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.5), results[3].damage); } } + +// Moves and abilities affected by Utility Umbrella have their tests in the respective files diff --git a/test/battle/move.c b/test/battle/move.c index 228a09a7c6..081a91712d 100644 --- a/test/battle/move.c +++ b/test/battle/move.c @@ -80,57 +80,57 @@ SINGLE_BATTLE_TEST("Turn order is determined randomly if priority and Speed tie } } -DOUBLE_BATTLE_TEST("Turn order is determined randomly if priority and Speed tie [doubles]") +DOUBLE_BATTLE_TEST("Turn order is determined randomly if priority and Speed tie [doubles]", u32 permutations) { - struct BattlePokemon *order[4] = { NULL, NULL, NULL, NULL }; - u32 a, b, c, d; - - // TODO: Test all of these in a single PASSES_RANDOMLY pass rather - // than 24 PARAMETRIZEd passes. - PARAMETRIZE { a = 0; b = 1; c = 2; d = 3; } - PARAMETRIZE { a = 0; b = 1; c = 3; d = 2; } - PARAMETRIZE { a = 0; b = 2; c = 1; d = 3; } - PARAMETRIZE { a = 0; b = 2; c = 3; d = 1; } - PARAMETRIZE { a = 0; b = 3; c = 1; d = 2; } - PARAMETRIZE { a = 0; b = 3; c = 2; d = 1; } - PARAMETRIZE { a = 1; b = 0; c = 2; d = 3; } - PARAMETRIZE { a = 1; b = 0; c = 3; d = 2; } - PARAMETRIZE { a = 1; b = 2; c = 0; d = 3; } - PARAMETRIZE { a = 1; b = 2; c = 3; d = 0; } - PARAMETRIZE { a = 1; b = 3; c = 0; d = 2; } - PARAMETRIZE { a = 1; b = 3; c = 2; d = 0; } - PARAMETRIZE { a = 2; b = 0; c = 1; d = 3; } - PARAMETRIZE { a = 2; b = 0; c = 3; d = 1; } - PARAMETRIZE { a = 2; b = 1; c = 0; d = 3; } - PARAMETRIZE { a = 2; b = 1; c = 3; d = 0; } - PARAMETRIZE { a = 2; b = 3; c = 0; d = 1; } - PARAMETRIZE { a = 2; b = 3; c = 1; d = 0; } - PARAMETRIZE { a = 3; b = 0; c = 1; d = 2; } - PARAMETRIZE { a = 3; b = 0; c = 2; d = 1; } - PARAMETRIZE { a = 3; b = 1; c = 0; d = 2; } - PARAMETRIZE { a = 3; b = 1; c = 2; d = 0; } - PARAMETRIZE { a = 3; b = 2; c = 0; d = 1; } - PARAMETRIZE { a = 3; b = 2; c = 1; d = 0; } - - order[a] = playerLeft; - order[b] = playerRight; - order[c] = opponentLeft; - order[d] = opponentRight; - - PASSES_RANDOMLY(1, 24, RNG_SPEED_TIE); + PARAMETRIZE {} // Hack to make permutations legal. + PASSES_RANDOMLY(24, 24, RNG_SPEED_TIE); + ASSUME(gMovesInfo[MOVE_ENDEAVOR].effect == EFFECT_ENDEAVOR); + ASSUME(gMovesInfo[MOVE_LIFE_DEW].effect == EFFECT_JUNGLE_HEALING); + ASSUME(gMovesInfo[MOVE_CRUSH_GRIP].effect == EFFECT_VARY_POWER_BASED_ON_HP); + ASSUME(gMovesInfo[MOVE_SUPER_FANG].effect == EFFECT_SUPER_FANG); GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Speed(1); } + PLAYER(SPECIES_WOBBUFFET) { MaxHP(480); HP(360); Defense(100); Speed(1); } PLAYER(SPECIES_WYNAUT) { Speed(1); } - OPPONENT(SPECIES_WOBBUFFET) { Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Attack(100); Speed(1); } OPPONENT(SPECIES_WYNAUT) { Speed(1); } } WHEN { - TURN { MOVE(playerLeft, MOVE_SPLASH); MOVE(playerRight, MOVE_SPLASH); MOVE(opponentLeft, MOVE_SPLASH); MOVE(opponentRight, MOVE_SPLASH); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, order[0]); - ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, order[1]); - ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, order[2]); - ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, order[3]); + TURN { MOVE(playerLeft, MOVE_ENDEAVOR, target: opponentLeft); MOVE(playerRight, MOVE_LIFE_DEW); MOVE(opponentLeft, MOVE_CRUSH_GRIP, target: playerLeft, WITH_RNG(RNG_DAMAGE_MODIFIER, 0)); MOVE(opponentRight, MOVE_SUPER_FANG, target: playerLeft); } + } THEN { + // This tests for unique combinatins of HP values depending on which order the moves are executed in + // The unique outcomes arise from the specific attacks and HP, Def, and Atk values chosen + // The switch is then set up in such a way that the only way for this test to pass exactly one is for each HP combination to occur exactly once +#define HP_PAIR(a, b) ((a) * 1000 + (b)) + switch (HP_PAIR(playerLeft->hp, opponentLeft->hp)) + { + case HP_PAIR(188, 360): results[i].permutations += 1 << 0; break; + case HP_PAIR(189, 360): results[i].permutations += 1 << 1; break; + case HP_PAIR(261, 360): results[i].permutations += 1 << 2; break; + case HP_PAIR(235, 360): results[i].permutations += 1 << 3; break; + case HP_PAIR(262, 360): results[i].permutations += 1 << 4; break; + case HP_PAIR(202, 360): results[i].permutations += 1 << 5; break; + case HP_PAIR(189, 378): results[i].permutations += 1 << 6; break; + case HP_PAIR(189, 189): results[i].permutations += 1 << 7; break; + case HP_PAIR(189, 480): results[i].permutations += 1 << 8; break; + case HP_PAIR(188, 480): results[i].permutations += 1 << 9; break; + case HP_PAIR(188, 240): results[i].permutations += 1 << 10; break; + case HP_PAIR(188, 188): results[i].permutations += 1 << 11; break; + case HP_PAIR(262, 262): results[i].permutations += 1 << 12; break; + case HP_PAIR(262, 142): results[i].permutations += 1 << 13; break; + case HP_PAIR(202, 403): results[i].permutations += 1 << 14; break; + case HP_PAIR(202, 202): results[i].permutations += 1 << 15; break; + case HP_PAIR(262, 283): results[i].permutations += 1 << 16; break; + case HP_PAIR(202, 283): results[i].permutations += 1 << 17; break; + case HP_PAIR(235, 180): results[i].permutations += 1 << 18; break; + case HP_PAIR(261, 180): results[i].permutations += 1 << 19; break; + case HP_PAIR(235, 235): results[i].permutations += 1 << 20; break; + case HP_PAIR(235, 300): results[i].permutations += 1 << 21; break; + case HP_PAIR(261, 141): results[i].permutations += 1 << 22; break; + case HP_PAIR(261, 261): results[i].permutations += 1 << 23; break; + } +#undef HP_PAIR + } FINALLY { + EXPECT_EQ(results[i].permutations, (1 << 24) - 1); } } diff --git a/test/battle/move_effect/weather_ball.c b/test/battle/move_effect/weather_ball.c index 1656b60d87..432e5f79f7 100644 --- a/test/battle/move_effect/weather_ball.c +++ b/test/battle/move_effect/weather_ball.c @@ -78,3 +78,5 @@ SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to an Ice-type move EXPECT_MUL_EQ(results[0].damage, Q_4_12(4.0), results[1].damage); // double base power + type effectiveness. } } + +TO_DO_BATTLE_TEST("Weather Ball doesn't double its power or change type if Cloud Nine/Air Lock is on the field"); diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 11432bd12b..92789710f7 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1327,7 +1327,7 @@ void TestRunner_Battle_AfterLastTurn(void) } STATE->runThen = TRUE; - STATE->runFinally = STATE->runParameter + 1 == STATE->parameters; + STATE->runFinally = STATE->runParameter + 1 == STATE->parameters && STATE->runTrial + 1 >= STATE->trials; InvokeTestFunction(test); STATE->runThen = FALSE; STATE->runFinally = FALSE; @@ -1369,6 +1369,7 @@ static inline rng_value_t MakeRngValue(const u16 seed) return ISO_RANDOMIZE1(seed); #endif } + static void CB2_BattleTest_NextTrial(void) { ClearFlagAfterTest(); @@ -1452,7 +1453,7 @@ void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext ctx { const struct BattleTest *test = GetBattleTest(); INVALID_IF(STATE->trials != 0, "PASSES_RANDOMLY can only be used once per test"); - INVALID_IF(test->resultsSize > 0, "PASSES_RANDOMLY is incompatible with results"); + INVALID_IF(test->resultsSize > 0 && STATE->parametersCount > 1, "PASSES_RANDOMLY is incompatible with results"); INVALID_IF(passes > trials, "%d passes specified, but only %d trials", passes, trials); STATE->rngTag = ctx.tag; STATE->rngTrialOffset = 0;