diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 51c0a78e51..8c5bcb7ed8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,9 @@ jobs: repository: pret/agbcc - name: Install binutils - run: sudo apt install -y build-essential libpng-dev libelf-dev + run: | + sudo apt update + sudo apt install -y build-essential libpng-dev libelf-dev # build-essential, git, and libpng-dev are already installed # gcc-arm-none-eabi is only needed for the modern build # as an alternative to dkP diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 4943e887f2..8b64d3e0e1 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -4061,58 +4061,57 @@ Move_BUG_BITE: end Move_CHARGE_BEAM: - loadspritegfx ANIM_TAG_BLACK_BALL_2 + loadspritegfx ANIM_TAG_ELECTRIC_ORBS + loadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT + loadspritegfx ANIM_TAG_ELECTRICITY loadspritegfx ANIM_TAG_SPARK_2 - delay 0 - createvisualtask AnimTask_BlendColorCycle, 2, (F_PAL_BG | F_PAL_ATTACKER), -31, 1, 5, 5, RGB(31, 31, 22) - playsewithpan SE_M_THUNDERBOLT2, SOUND_PAN_ATTACKER - createsprite gSparkElectricitySpriteTemplate, ANIM_ATTACKER, 0, 32, 24, 190, 12, 0, 1, 0 - delay 0 - createsprite gSparkElectricitySpriteTemplate, ANIM_ATTACKER, 0, 80, 24, 22, 12, 0, 1, 0 - createsprite gSparkElectricitySpriteTemplate, ANIM_ATTACKER, 0, 156, 24, 121, 13, 0, 1, 1 - delay 0 - createvisualtask AnimTask_BlendColorCycle, 2, (F_PAL_BG | F_PAL_ATTACKER), -31, 1, 0, 0, RGB(31, 31, 22) - delay 10 - createvisualtask AnimTask_BlendColorCycle, 2, (F_PAL_BG | F_PAL_ATTACKER), -31, 1, 5, 5, RGB(31, 31, 22) - playsewithpan SE_M_THUNDERBOLT2, SOUND_PAN_ATTACKER - createsprite gSparkElectricitySpriteTemplate, ANIM_ATTACKER, 0, 100, 24, 60, 10, 0, 1, 0 - createsprite gSparkElectricitySpriteTemplate, ANIM_ATTACKER, 0, 170, 24, 42, 11, 0, 1, 1 - delay 0 - createsprite gSparkElectricitySpriteTemplate, ANIM_ATTACKER, 0, 238, 24, 165, 10, 0, 1, 1 - delay 0 - createvisualtask AnimTask_BlendColorCycle, 2, (F_PAL_BG | F_PAL_ATTACKER), -31, 1, 0, 0, RGB(31, 31, 22) + setalpha 12, 8 + createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_BG, 2, 0, 4, RGB_BLACK + waitforvisualfinish + createvisualtask AnimTask_ElectricChargingParticles, 2, ANIM_ATTACKER, 20, 0, 2 + playsewithpan SE_M_CHARGE, SOUND_PAN_ATTACKER + delay 12 + createsprite gGrowingShockWaveOrbSpriteTemplate, ANIM_ATTACKER, 2 + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_ATTACKER, 2, 0, 11, RGB(31, 31, 22) + delay 50 + createsoundtask SoundTask_LoopSEAdjustPanning, SE_M_THUNDERBOLT2, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, 1, 16, 0, 5 + createvisualtask AnimTask_ShakeMon, 2, ANIM_ATTACKER, 0, 4, 50, 1 + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 4, 0, 50, 1 + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_ATTACKER, 2, 11, 0, RGB(31, 31, 22) + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_TARGET, 2, 0, 11, RGB(31, 31, 22) + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam delay 20 - createvisualtask AnimTask_BlendColorCycle, 2, (F_PAL_BG | F_PAL_ATTACKER), -31, 1, 7, 7, RGB(31, 31, 22) - playsewithpan SE_M_THUNDERBOLT2, SOUND_PAN_ATTACKER - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 32, 12, 0, 20, 0, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 32, 12, 64, 20, 1, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 32, 12, 128, 20, 0, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 32, 12, 192, 20, 2, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 16, 12, 32, 20, 0, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 16, 12, 96, 20, 1, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 16, 12, 160, 20, 0, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 16, 12, 224, 20, 2, 0 - delay 4 - waitforvisualfinish - createvisualtask AnimTask_BlendColorCycle, 2, (F_PAL_BG | F_PAL_ATTACKER), -31, 1, 0, 0, RGB(31, 31, 22) - playsewithpan SE_M_THUNDER_WAVE, SOUND_PAN_ATTACKER - createsprite gZapCannonBallSpriteTemplate, ANIM_TARGET, 3, 10, 0, 0, 0, 30, 0 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 16, 30, 0, 40, 0 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 16, 30, 64, 40, 1 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 16, 30, 128, 40, 0 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 16, 30, 192, 40, 2 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 8, 30, 32, 40, 0 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 8, 30, 96, 40, 1 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 8, 30, 160, 40, 0 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 8, 30, 224, 40, 2 - waitforvisualfinish - createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 4, 0, 5, 1 - delay 15 - waitplaysewithpan SE_M_THUNDERBOLT2, SOUND_PAN_TARGET, 19 - call ElectricityEffect + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_TARGET, 2, 11, 0, RGB(31, 31, 22) waitforvisualfinish + createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_BG, 4, 4, 0, RGB_BLACK + blendoff end +SparkBeam: + createsprite gSparkBeamSpriteTemplate, ANIM_ATTACKER, 3, 0, 0, 0, 0 + delay 1 + createsprite gSparkBeamSpriteTemplate, ANIM_ATTACKER, 3, 0, 0, 0, 0 + delay 1 + return + Move_WOOD_HAMMER: loadspritegfx ANIM_TAG_WOOD_HAMMER loadspritegfx ANIM_TAG_WOOD_HAMMER_HAMMER @@ -4297,22 +4296,22 @@ Move_HEAL_ORDER: Move_HEAD_SMASH: loadspritegfx ANIM_TAG_IMPACT - call SetImpactBackground - createsprite gBowMonSpriteTemplate, ANIM_ATTACKER, 2, 0 + loadspritegfx ANIM_TAG_ROCKS + createvisualtask AnimTask_SkullBashPosition, 2, 0 playsewithpan SE_M_TAKE_DOWN, SOUND_PAN_ATTACKER waitforvisualfinish - delay 2 - createsprite gBowMonSpriteTemplate, ANIM_ATTACKER, 2, 1 + playse SE_BANG + call SetImpactBackground + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_ATTACKER, 2, 0, 40, 1 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 10, 0, 40, 1 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 4, 0, 0, ANIM_TARGET, 0 + playsewithpan SE_M_ROCK_THROW, SOUND_PAN_TARGET + createsprite gRockScatterSpriteTemplate, ANIM_TARGET, 2, -12, 32, 3, 4 + createsprite gRockScatterSpriteTemplate, ANIM_TARGET, 2, 8, 31, 2, 2 + createsprite gRockScatterSpriteTemplate, ANIM_TARGET, 2, -4, 28, 2, 3 + createsprite gRockScatterSpriteTemplate, ANIM_TARGET, 2, 12, 30, 4, 3 waitforvisualfinish - createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_ATTACKER, 2, 0, 4, 1 - createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 5, 0, 6, 1 - createsprite gBowMonSpriteTemplate, ANIM_ATTACKER, 2, 2 - createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, 0, 0, 1, 1 - loopsewithpan SE_M_MEGA_KICK2, SOUND_PAN_TARGET, 8, 3 - waitforvisualfinish - clearmonbg ANIM_TARGET - blendoff - delay 2 + createvisualtask AnimTask_SkullBashPosition, 2, 1 restorebg waitbgfadein end @@ -4861,19 +4860,82 @@ Move_WONDER_ROOM:: end Move_PSYSHOCK: + loadspritegfx ANIM_TAG_RED_ORB_2 + loadspritegfx ANIM_TAG_POISON_JAB + loadspritegfx ANIM_TAG_GRAY_SMOKE + loadspritegfx ANIM_TAG_WISP_FIRE monbg ANIM_TARGET setalpha 8, 8 + playsewithpan SE_M_SUPERSONIC, SOUND_PAN_ATTACKER createvisualtask AnimTask_ShakeMon2, 2, ANIM_ATTACKER, 1, 0, 10, 1 - createvisualtask AnimTask_BlendColorCycle, 2, F_PAL_ATTACKER, 0, 2, 0, 8, RGB(31, 23, 0) + createvisualtask AnimTask_BlendColorCycle, 2, F_PAL_ATTACKER, 0, 2, 0, 8, RGB_WHITE waitforvisualfinish - loopsewithpan SE_M_SUPERSONIC, SOUND_PAN_TARGET, 10, 3 - createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 5, 0, 15, 1 - createvisualtask AnimTask_ScaleMonAndRestore, 5, -6, -6, 15, ANIM_TARGET, 1 + delay 10 + call PsyshockConverge waitforvisualfinish clearmonbg ANIM_TARGET blendoff end +PsyshockConverge: + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 40, 40, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, -40, -40, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 0, 40, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 0, -40, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 40, -20, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 40, 20, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, -40, -20, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, -40, 20, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, -20, 30, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 20, -30, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, -20, -30, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 20, 30, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, -40, 0, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 40, 0, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 6 + createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 5, 0, 15, 1 + createvisualtask AnimTask_BlendMonInAndOut, 5, ANIM_TARGET, RGB_WHITE, 12, 0, 1 + createsprite gPsyshockSmokeSpriteTemplate, ANIM_TARGET, 2, 8, 8, 1, 0 + playsewithpan SE_M_SWAGGER, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockSmokeSpriteTemplate, ANIM_TARGET, 2, -8, -8, 1, 0 + playsewithpan SE_M_SWAGGER, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockSmokeSpriteTemplate, ANIM_TARGET, 2, 8, -8, 1, 0 + playsewithpan SE_M_SWAGGER, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockSmokeSpriteTemplate, ANIM_TARGET, 2, -8, 8, 1, 0 + playsewithpan SE_M_SWAGGER, SOUND_PAN_TARGET + delay 2 + return + Move_VENOSHOCK: loadspritegfx ANIM_TAG_POISON_BUBBLE loadspritegfx ANIM_TAG_TOXIC_BUBBLE @@ -5802,23 +5864,79 @@ Move_QUASH: Move_ACROBATICS: loadspritegfx ANIM_TAG_ROUND_SHADOW + loadspritegfx ANIM_TAG_WHITE_STREAK loadspritegfx ANIM_TAG_IMPACT monbg ANIM_TARGET setalpha 12, 8 playsewithpan SE_M_FLY, SOUND_PAN_ATTACKER createsprite gFlyBallUpSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, 13, 336 waitforvisualfinish - playsewithpan SE_M_SWAGGER, SOUND_PAN_TARGET - createsprite gBounceBallLandSpriteTemplate, ANIM_TARGET, 3 - delay 7 - playsewithpan SE_M_MEGA_KICK2, SOUND_PAN_TARGET - createsprite gBasicHitSplatSpriteTemplate, ANIM_TARGET, 2, 0, 0, 1, 0 - createvisualtask AnimTask_ShakeMon, 5, ANIM_TARGET, 0, 5, 11, 1 + call SetSkyBg + call AcrobaticsSlashes waitforvisualfinish clearmonbg ANIM_TARGET - blendoff + visible ANIM_ATTACKER + call UnsetSkyBg end +AcrobaticsSlashes: + createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 4, 0, 40, 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -10, 3 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 24, -19 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -28, -15 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -6, -30 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -20, 6 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 28, 2 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -14, -25 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 9, -2 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -1, 0 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 21, 4 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 28, 20 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -7, 24 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -11, 1 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 12, -18 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -21, -14 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -29, 7 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 15, 28 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -21, -16 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + return + Move_REFLECT_TYPE: loadspritegfx ANIM_TAG_GUARD_RING @ring around user loadspritegfx ANIM_TAG_ICE_CHUNK @blue green color @@ -8313,17 +8431,99 @@ Move_FELL_STINGER: end Move_PHANTOM_FORCE: - choosetwoturnanim PhantomForceSetUp, PhantomForceUnleash -PhantomForceEnd: + loadspritegfx ANIM_TAG_ROUND_SHADOW + loadspritegfx ANIM_TAG_IMPACT + choosetwoturnanim PhantomForcePrep PhantomForceAttack +PhantomForceWaitEnd: + waitforvisualfinish + restorebg + waitbgfadein end -PhantomForceSetUp: +PhantomForcePrep: + monbg ANIM_ATTACKER + fadetobg BG_GHOST + waitbgfadein + delay 0 + playsewithpan SE_M_FAINT_ATTACK, SOUND_PAN_ATTACKER + createvisualtask AnimTask_TranslateMonEllipticalRespectSide, 2, ANIM_ATTACKER, 18, 6, 1, 3 + createvisualtask AnimTask_AttackerFadeToInvisible, 2, 1 + waitforvisualfinish + clearmonbg ANIM_ATTACKER invisible ANIM_ATTACKER - goto PhantomForceEnd -PhantomForceUnleash: - visible ANIM_ATTACKER - goto PhantomForceEnd + delay 1 + goto PhantomForceWaitEnd +PhantomForceAttack: + loadspritegfx ANIM_TAG_PURPLE_FLAME + loadspritegfx ANIM_TAG_WHITE_SHADOW @Destiny Bond + monbg ANIM_ATTACKER + splitbgprio ANIM_ATTACKER + fadetobg BG_GHOST + waitbgfadein + delay 1 + createvisualtask AnimTask_BlendParticle, 5, ANIM_TAG_IMPACT, 0, 12, 12, RGB(0, 0, 23) + setalpha 12, 8 + waitforvisualfinish + delay 10 + playsewithpan SE_M_PSYBEAM, SOUND_PAN_ATTACKER + createvisualtask AnimTask_PurpleFlamesOnTarget, 0x3 + createvisualtask AnimTask_DestinyBondWhiteShadow, 0x5, 0x0, 0x30 + delay 30 + createvisualtask AnimTask_BlendParticle, 5, ANIM_TAG_IMPACT, 0, 12, 12, RGB(0, 0, 23) + waitforvisualfinish + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_TARGET, 2 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_TARGET, 2 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_TARGET, 2 + createvisualtask SoundTask_PlaySE1WithPanning, 5, 215, SOUND_PAN_TARGET + delay 3 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_TARGET, 2 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_DEF_PARTNER, 2 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_ATK_PARTNER, 2 + createvisualtask SoundTask_PlaySE1WithPanning, 5, 215, SOUND_PAN_TARGET + delay 3 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_TARGET, 2 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_DEF_PARTNER, 2 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_ATK_PARTNER, 2 + createvisualtask SoundTask_PlaySE1WithPanning, 5, 215, SOUND_PAN_TARGET + delay 3 + createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 2, 0, 12, 1 + createvisualtask AnimTask_BlendColorCycle, 2, F_PAL_TARGET, 0, 2, 0, 13, RGB_PURPLE + waitforvisualfinish + delay 1 + playsewithpan SOUND_PAN_ATTACKER, 192 + createvisualtask AnimTask_NightShadeClone, 5, 10 + waitforvisualfinish + clearmonbg ANIM_ATTACKER + delay 1 + goto PhantomForceWaitEnd +PhantomForceBg: + fadetobg BG_DARK + waitbgfadeout + createvisualtask AnimTask_FadeScreenToWhite, 5 + waitbgfadein + return Move_TRICK_OR_TREAT: + loadspritegfx ANIM_TAG_EYE_SPARKLE + loadspritegfx ANIM_TAG_GHOSTLY_SPIRIT + fadetobg BG_NIGHTMARE + waitbgfadein + delay 10 + playsewithpan SE_M_PSYBEAM, SOUND_PAN_ATTACKER + createvisualtask AnimTask_ShakeMon2, 2, ANIM_ATTACKER, 1, 0, 10, 1 + createvisualtask AnimTask_BlendColorCycle, 2, F_PAL_ATTACKER, 0, 2, 0, 8, RGB(10, 2, 19) + waitforvisualfinish + playsewithpan SE_M_LEER, SOUND_PAN_ATTACKER + createvisualtask AnimTask_ScaryFace, 5 + delay 13 + waitforvisualfinish + createvisualtask AnimTask_BlendColorCycle, 2, F_PAL_TARGET, 2, 2, 0, 12, RGB(10, 2, 19) @;Deep purple + playsewithpan SE_M_NIGHTMARE, SOUND_PAN_TARGET + createsprite gCurseGhostSpriteTemplate, ANIM_TARGET, 2 + createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 2, 0, 14, 1 + waitforvisualfinish + clearmonbg ANIM_TARGET + restorebg + waitbgfadein end Move_NOBLE_ROAR: diff --git a/graphics/items/icon_palettes/fairy_feather.pal b/graphics/items/icon_palettes/fairy_feather.pal new file mode 100644 index 0000000000..a20a702b74 --- /dev/null +++ b/graphics/items/icon_palettes/fairy_feather.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +180 180 180 +11 15 16 +33 19 27 +59 38 38 +102 90 92 +200 124 124 +182 116 141 +255 124 189 +227 140 140 +253 155 155 +222 173 189 +255 175 175 +195 191 192 +252 161 206 +225 221 223 +245 245 245 diff --git a/graphics/items/icons/fairy_feather.png b/graphics/items/icons/fairy_feather.png new file mode 100644 index 0000000000..26446886a8 Binary files /dev/null and b/graphics/items/icons/fairy_feather.png differ diff --git a/graphics/pokemon/pikachu/iconf.png b/graphics/pokemon/pikachu/iconf.png new file mode 100644 index 0000000000..22eeed0a46 Binary files /dev/null and b/graphics/pokemon/pikachu/iconf.png differ diff --git a/graphics/pokemon/wobbuffet/iconf.png b/graphics/pokemon/wobbuffet/iconf.png new file mode 100644 index 0000000000..1f7f86991b Binary files /dev/null and b/graphics/pokemon/wobbuffet/iconf.png differ diff --git a/include/battle.h b/include/battle.h index 7fa841e835..04db26bf4a 100644 --- a/include/battle.h +++ b/include/battle.h @@ -672,7 +672,7 @@ struct BattleStruct u8 lastMoveFailed; // as bits for each battler, for the sake of Stomping Tantrum u8 lastMoveTarget[MAX_BATTLERS_COUNT]; // The last target on which each mon used a move, for the sake of Instruct u16 tracedAbility[MAX_BATTLERS_COUNT]; - u16 hpBefore[MAX_BATTLERS_COUNT]; // Hp of battlers before using a move. For Berserk + u16 hpBefore[MAX_BATTLERS_COUNT]; // Hp of battlers before using a move. For Berserk and Anger Shell. bool8 spriteIgnore0Hp; struct Illusion illusion[MAX_BATTLERS_COUNT]; s32 aiFinalScore[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // AI, target, moves to make debugging easier diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 3d37a185c7..2e0629b0cd 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -91,7 +91,6 @@ bool32 AI_IsDamagedByRecoil(u32 battler); u32 GetNoOfHitsToKO(u32 dmg, s32 hp); u32 GetNoOfHitsToKOBattlerDmg(u32 dmg, u32 battlerDef); u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex); -bool32 IsInIgnoredPowerfulMoveEffects(u32 effect); void SetMovesDamageResults(u32 battlerAtk, u16 *moves); u32 GetMoveDamageResult(u32 battlerAtk, u32 battlerDef, u32 moveIndex); u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef); @@ -113,6 +112,7 @@ bool32 IsMoveRedirectionPrevented(u32 move, u32 atkAbility); bool32 IsMoveEncouragedToHit(u32 battlerAtk, u32 battlerDef, u32 move); bool32 IsHazardMoveEffect(u32 moveEffect); bool32 IsEncoreEncouragedEffect(u32 moveEffect); +bool32 IsChargingMove(u32 battlerAtk, u32 effect); void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove, s32 *score); bool32 ShouldSetSandstorm(u32 battler, u32 ability, u32 holdEffect); bool32 ShouldSetHail(u32 battler, u32 ability, u32 holdEffect); diff --git a/include/config/pokemon.h b/include/config/pokemon.h index 1859195c30..87fb8cc24e 100644 --- a/include/config/pokemon.h +++ b/include/config/pokemon.h @@ -25,10 +25,10 @@ // Species-specific settings #define P_SHEDINJA_BALL GEN_LATEST // Since Gen 4, Shedinja requires a Poké Ball for its evolution. In Gen 3, Shedinja inherits Nincada's Ball. #define P_KADABRA_EVERSTONE GEN_LATEST // Since Gen 4, Kadabra can evolve even when holding an Everstone. -#define P_HIPPO_GENDER_DIFF_ICONS TRUE // If TRUE, will give Hippopotas and Hippowdon custom icons for their female forms. #define P_SHUCKLE_BERRY_JUICE TRUE // In Gen 2, Shuckle had a 1/16 chance of converting Berry that it's holding into Berry Juice. Setting this to TRUE will allow to do this with an Oran Berry, which is the spiritual succesor of the Berry item. // Other settings +#define P_CUSTOM_GENDER_DIFF_ICONS TRUE // If TRUE, will give more Pokémon custom icons for their female forms, i.e. Hippopotas and Hippowdon #define P_LEGENDARY_PERFECT_IVS GEN_LATEST // Since Gen 6, Legendaries, Mythicals and Ultra Beasts found in the wild or given through gifts have at least 3 perfect IVs. #define P_EV_CAP GEN_LATEST // Since Gen 6, the max EVs per stat is 252 instead of 255. diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 087af0e301..5fea56b189 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -301,16 +301,16 @@ #define MOVEEND_ATTACKER_VISIBLE 10 #define MOVEEND_TARGET_VISIBLE 11 #define MOVEEND_ITEM_EFFECTS_TARGET 12 -#define MOVEEND_ITEM_EFFECTS_ALL 13 -#define MOVEEND_KINGSROCK 14 // These item effects will occur each strike of a multi-hit move -#define MOVEEND_SUBSTITUTE 15 -#define MOVEEND_SKY_DROP_CONFUSE 16 -#define MOVEEND_UPDATE_LAST_MOVES 17 -#define MOVEEND_MIRROR_MOVE 18 -#define MOVEEND_NEXT_TARGET 19 // Everything up until here is handled for each strike of a multi-hit move -#define MOVEEND_MULTIHIT_MOVE 20 -#define MOVEEND_DEFROST 21 -#define MOVEEND_MOVE_EFFECTS2 22 +#define MOVEEND_MOVE_EFFECTS2 13 +#define MOVEEND_ITEM_EFFECTS_ALL 14 +#define MOVEEND_KINGSROCK 15 // These item effects will occur each strike of a multi-hit move +#define MOVEEND_SUBSTITUTE 16 +#define MOVEEND_SKY_DROP_CONFUSE 17 +#define MOVEEND_UPDATE_LAST_MOVES 18 +#define MOVEEND_MIRROR_MOVE 19 +#define MOVEEND_NEXT_TARGET 20 // Everything up until here is handled for each strike of a multi-hit move +#define MOVEEND_MULTIHIT_MOVE 21 +#define MOVEEND_DEFROST 22 #define MOVEEND_RECOIL 23 #define MOVEEND_MAGICIAN 24 // Occurs after final multi-hit strike, and after other items/abilities would activate #define MOVEEND_EJECT_BUTTON 25 diff --git a/include/graphics.h b/include/graphics.h index 9a066bfbe2..46de942326 100644 --- a/include/graphics.h +++ b/include/graphics.h @@ -5227,6 +5227,9 @@ extern const u8 gMonIcon_Fearow[]; extern const u8 gMonIcon_Ekans[]; extern const u8 gMonIcon_Arbok[]; extern const u8 gMonIcon_Pikachu[]; +#if P_CUSTOM_GENDER_DIFF_ICONS == TRUE +extern const u8 gMonIcon_PikachuF[]; +#endif extern const u8 gMonIcon_Raichu[]; extern const u8 gMonIcon_Sandshrew[]; extern const u8 gMonIcon_Sandslash[]; @@ -5404,6 +5407,9 @@ extern const u8 gMonIcon_Slowking[]; extern const u8 gMonIcon_Misdreavus[]; extern const u8 gMonIcon_Unown[]; extern const u8 gMonIcon_Wobbuffet[]; +#if P_CUSTOM_GENDER_DIFF_ICONS == TRUE +extern const u8 gMonIcon_WobbuffetF[]; +#endif extern const u8 gMonIcon_Girafarig[]; extern const u8 gMonIcon_Pineco[]; extern const u8 gMonIcon_Forretress[]; @@ -5653,7 +5659,7 @@ extern const u8 gMonIcon_Riolu[]; extern const u8 gMonIcon_Lucario[]; extern const u8 gMonIcon_Hippopotas[]; extern const u8 gMonIcon_Hippowdon[]; -#if P_HIPPO_GENDER_DIFF_ICONS == TRUE +#if P_CUSTOM_GENDER_DIFF_ICONS == TRUE extern const u8 gMonIcon_HippopotasF[]; extern const u8 gMonIcon_HippowdonF[]; #endif @@ -10058,6 +10064,9 @@ extern const u32 gItemIconPalette_LustrousGlobe[]; extern const u32 gItemIcon_BerserkGene[]; extern const u32 gItemIconPalette_BerserkGene[]; +extern const u32 gItemIcon_FairyFeather[]; +extern const u32 gItemIconPalette_FairyFeather[]; + extern const u32 gItemIcon_ReturnToFieldArrow[]; extern const u32 gItemIconPalette_ReturnToFieldArrow[]; diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 8339195ea7..33282b955c 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -760,6 +760,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) RETURN_SCORE_MINUS(20); // if target off screen and we go first, don't use move + if (IsChargingMove(battlerAtk, moveEffect) && CanTargetFaintAi(battlerDef, battlerAtk)) + RETURN_SCORE_MINUS(10); + // check if negates type switch (effectiveness) { @@ -1356,22 +1359,13 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) } break; //case EFFECT_BIDE: - //case EFFECT_SUPER_FANG: //case EFFECT_RECHARGE: - case EFFECT_LEVEL_DAMAGE: - case EFFECT_PSYWAVE: //case EFFECT_COUNTER: - //case EFFECT_FLAIL: - case EFFECT_RETURN: case EFFECT_PRESENT: - case EFFECT_FRUSTRATION: case EFFECT_SONICBOOM: //case EFFECT_MIRROR_COAT: - case EFFECT_SKULL_BASH: case EFFECT_FOCUS_PUNCH: - case EFFECT_SUPERPOWER: //case EFFECT_ENDEAVOR: - case EFFECT_LOW_KICK: // AI_CBM_HighRiskForDamage if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2) ADJUST_SCORE(-10); @@ -1906,17 +1900,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) || (gBattleMons[battlerDef].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE))) //Leave out Illusion b/c AI is supposed to be fooled ADJUST_SCORE(-10); break; - case EFFECT_TWO_TURNS_ATTACK: - if (aiData->holdEffects[battlerAtk] != HOLD_EFFECT_POWER_HERB && CanTargetFaintAi(battlerDef, battlerAtk)) - ADJUST_SCORE(-6); - break; - case EFFECT_RECHARGE: - if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2) - ADJUST_SCORE(-10); - else if (aiData->abilities[battlerAtk] != ABILITY_TRUANT - && !CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0)) - ADJUST_SCORE(-2); - break; case EFFECT_SPITE: case EFFECT_MIMIC: if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker should go first @@ -2109,13 +2092,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_SPECTRAL_THIEF: break; - case EFFECT_SOLAR_BEAM: - if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB - || ((AI_GetWeather(aiData) & B_WEATHER_SUN) && aiData->holdEffects[battlerAtk] != HOLD_EFFECT_UTILITY_UMBRELLA)) - break; - if (CanTargetFaintAi(battlerDef, battlerAtk)) //Attacker can be knocked out - ADJUST_SCORE(-4); - break; case EFFECT_SEMI_INVULNERABLE: if (predictedMove != MOVE_NONE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER @@ -2674,6 +2650,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) && !BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) ADJUST_SCORE(-10); break; + case EFFECT_LOW_KICK: + if (IsDynamaxed(battlerDef)) + ADJUST_SCORE(-10); + break; case EFFECT_PLACEHOLDER: return 0; // cannot even select } // move effect checks @@ -3151,7 +3131,7 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) s32 score = 0; s32 leastHits = 1000; u16 *moves = GetMovesArray(battlerAtk); - bool8 isPowerfulIgnoredEffect[MAX_MON_MOVES]; + bool8 isChargingMoveEffect[MAX_MON_MOVES]; for (i = 0; i < MAX_MON_MOVES; i++) { @@ -3163,13 +3143,13 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) leastHits = noOfHits[i]; } viableMoveScores[i] = AI_SCORE_DEFAULT; - isPowerfulIgnoredEffect[i] = IsInIgnoredPowerfulMoveEffects(gBattleMoves[moves[i]].effect); + isChargingMoveEffect[i] = IsChargingMove(battlerAtk, gBattleMoves[moves[i]].effect); } else { noOfHits[i] = -1; viableMoveScores[i] = 0; - isPowerfulIgnoredEffect[i] = FALSE; + isChargingMoveEffect[i] = FALSE; } /* MgbaPrintf_("%S: required hits: %d Dmg: %d", gMoveNames[moves[i]], noOfHits[i], AI_DATA->simulatedDmg[battlerAtk][battlerDef][i]); @@ -3178,7 +3158,7 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) // Priority list: // 1. Less no of hits to ko - // 2. Not in the powerful but ignored move effects table + // 2. Not charging // 3. More accuracy // 4. Better effect @@ -3193,9 +3173,9 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) { multipleBestMoves = TRUE; // We need to make sure it's the current move which is objectively better. - if (isPowerfulIgnoredEffect[i] && !isPowerfulIgnoredEffect[currId]) + if (isChargingMoveEffect[i] && !isChargingMoveEffect[currId]) viableMoveScores[i] -= 3; - else if (!isPowerfulIgnoredEffect[i] && isPowerfulIgnoredEffect[currId]) + else if (!isChargingMoveEffect[i] && isChargingMoveEffect[currId]) viableMoveScores[currId] -= 3; switch (CompareMoveAccuracies(battlerAtk, battlerDef, currId, i)) @@ -4847,15 +4827,6 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score IncreasePoisonScore(battlerAtk, battlerDef, move, &score); IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPEED, &score); break; - case EFFECT_SOLAR_BEAM: - if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, movesetIndex) >= 2 - && HasMoveEffect(battlerAtk, EFFECT_SUNNY_DAY) && (AI_GetWeather(aiData) & B_WEATHER_SUN)) // Use Sunny Day to boost damage. - ADJUST_SCORE(-3); - case EFFECT_TWO_TURNS_ATTACK: - case EFFECT_SKULL_BASH: - if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB) - ADJUST_SCORE(2); - break; case EFFECT_COUNTER: if (!IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]) && predictedMove != MOVE_NONE) { @@ -5246,9 +5217,6 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_BELLY_DRUM: case EFFECT_PSYCH_UP: case EFFECT_MIRROR_COAT: - case EFFECT_SOLAR_BEAM: - case EFFECT_TWO_TURNS_ATTACK: - case EFFECT_ERUPTION: case EFFECT_TICKLE: case EFFECT_SUNNY_DAY: case EFFECT_SANDSTORM: diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index dd06a69eaf..68450e4e59 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -363,25 +363,6 @@ static const u16 sEncouragedEncoreEffects[] = EFFECT_CAMOUFLAGE, }; -// For the purposes of determining the most powerful move in a moveset, these -// moves are treated the same as having a power of 0 or 1 -#define IGNORED_MOVES_END 0xFFFF -static const u16 sIgnoredPowerfulMoveEffects[] = -{ - EFFECT_EXPLOSION, - EFFECT_DREAM_EATER, - EFFECT_RECHARGE, - EFFECT_SKULL_BASH, - EFFECT_SOLAR_BEAM, - EFFECT_FOCUS_PUNCH, - EFFECT_SUPERPOWER, - EFFECT_ERUPTION, - EFFECT_OVERHEAT, - EFFECT_MIND_BLOWN, - EFFECT_MAKE_IT_RAIN, - IGNORED_MOVES_END -}; - // Functions u32 GetAIChosenMove(u32 battlerId) { @@ -980,6 +961,11 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s switch (gBattleMoves[move].effect) { case EFFECT_RECHARGE: + case EFFECT_SUPERPOWER: + case EFFECT_OVERHEAT: + case EFFECT_MAKE_IT_RAIN: + case EFFECT_MIND_BLOWN: + case EFFECT_STEEL_BEAM: return TRUE; case EFFECT_RECOIL_25: case EFFECT_RECOIL_IF_MISS: @@ -1054,22 +1040,6 @@ u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex) return GetNoOfHitsToKOBattlerDmg(AI_DATA->simulatedDmg[battlerAtk][battlerDef][moveIndex], battlerDef); } -bool32 IsInIgnoredPowerfulMoveEffects(u32 effect) -{ - u32 i; - for (i = 0; sIgnoredPowerfulMoveEffects[i] != IGNORED_MOVES_END; i++) - { - if (effect == sIgnoredPowerfulMoveEffects[i]) - { - // Don't ingore Solar Beam if doesn't have a charging turn. - if (effect == EFFECT_SOLAR_BEAM && (AI_GetWeather(AI_DATA) & B_WEATHER_SUN)) - break; - return TRUE; - } - } - return FALSE; -} - void SetMovesDamageResults(u32 battlerAtk, u16 *moves) { s32 i, j, battlerDef, bestId, currId, hp, result; @@ -1079,7 +1049,7 @@ void SetMovesDamageResults(u32 battlerAtk, u16 *moves) for (i = 0; i < MAX_MON_MOVES; i++) { u32 move = moves[i]; - if (move == MOVE_NONE || move == MOVE_UNAVAILABLE || gBattleMoves[move].power == 0 || IsInIgnoredPowerfulMoveEffects(gBattleMoves[move].effect)) + if (move == MOVE_NONE || move == MOVE_UNAVAILABLE || gBattleMoves[move].power == 0) isNotConsidered[i] = TRUE; else isNotConsidered[i] = FALSE; @@ -1094,11 +1064,10 @@ void SetMovesDamageResults(u32 battlerAtk, u16 *moves) if (isNotConsidered[i]) { - result = MOVE_POWER_OTHER; // Move has a power of 0/1, or is in the group sIgnoredPowerfulMoveEffects + result = MOVE_POWER_OTHER; // Move has a power of 0/1 } else { - // Considered move has power and is not in sIgnoredPowerfulMoveEffects // Check all other moves and calculate their power for (j = 0; j < MAX_MON_MOVES; j++) { @@ -2394,6 +2363,24 @@ bool32 IsEncoreEncouragedEffect(u32 moveEffect) return FALSE; } +bool32 IsChargingMove(u32 battlerAtk, u32 effect) +{ + switch (effect) + { + case EFFECT_SOLAR_BEAM: + if (AI_GetWeather(AI_DATA) & B_WEATHER_SUN) + return FALSE; + case EFFECT_SKULL_BASH: + case EFFECT_METEOR_BEAM: + case EFFECT_TWO_TURNS_ATTACK: + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB) + return FALSE; + return TRUE; + default: + return FALSE; + } +} + static u32 GetLeechSeedDamage(u32 battlerId) { u32 damage = 0; diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c index d8addec01c..b79c94a6a3 100644 --- a/src/battle_anim_effects_1.c +++ b/src/battle_anim_effects_1.c @@ -158,6 +158,7 @@ static void AnimRockPolishSparkle(struct Sprite *); static void AnimPoisonJabProjectile(struct Sprite *); static void AnimNightSlash(struct Sprite *); static void AnimPluck(struct Sprite *); +static void AnimAcrobaticsSlashes(struct Sprite *); const union AnimCmd gPowderParticlesAnimCmds[] = { @@ -3006,6 +3007,61 @@ const struct SpriteTemplate gSeedFlareGreenCirclesTemplate = .callback = AnimPowerAbsorptionOrb }; +const struct SpriteTemplate gSteelBeamBigOrbSpriteTemplate = +{ + .tileTag = ANIM_TAG_STEEL_BEAM, + .paletteTag = ANIM_TAG_STEEL_BEAM, + .oam = &gOamData_AffineOff_ObjNormal_8x8, + .anims = gSolarBeamBigOrbAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimSolarBeamBigOrb, +}; + +const struct SpriteTemplate gSteelBeamSmallOrbSpriteTemplate = +{ + .tileTag = ANIM_TAG_STEEL_BEAM, + .paletteTag = ANIM_TAG_STEEL_BEAM, + .oam = &gOamData_AffineOff_ObjNormal_8x8, + .anims = gSolarBeamSmallOrbAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimSolarBeamSmallOrb, +}; + +const struct SpriteTemplate gAcrobaticsSlashesSpriteTemplate = +{ + .tileTag = ANIM_TAG_WHITE_STREAK, + .paletteTag = ANIM_TAG_WHITE_STREAK, + .oam = &gOamData_AffineDouble_ObjBlend_32x8, + .anims = gRockPolishStreak_AnimCmds, + .images = NULL, + .affineAnims = gRockPolishStreak_AffineAnimCmds, + .callback = AnimAcrobaticsSlashes, +}; + +const struct SpriteTemplate gPsyshockOrbSpriteTemplate = +{ + .tileTag = ANIM_TAG_RED_ORB_2, + .paletteTag = ANIM_TAG_POISON_JAB, + .oam = &gOamData_AffineOff_ObjNormal_8x8, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimPoisonJabProjectile, +}; + +const struct SpriteTemplate gPsyshockSmokeSpriteTemplate = +{ + .tileTag = ANIM_TAG_GRAY_SMOKE, + .paletteTag = ANIM_TAG_WISP_FIRE, + .oam = &gOamData_AffineOff_ObjNormal_32x32, + .anims = gOctazookaAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimSpriteOnMonPos, +}; + // functions static void AnimGrassKnot(struct Sprite *sprite) { @@ -6971,28 +7027,6 @@ void AnimTask_CompressTargetHorizontally(u8 taskId) task->func = AnimTask_CompressTargetStep; } -const struct SpriteTemplate gSteelBeamBigOrbSpriteTemplate = -{ - .tileTag = ANIM_TAG_STEEL_BEAM, - .paletteTag = ANIM_TAG_STEEL_BEAM, - .oam = &gOamData_AffineOff_ObjNormal_8x8, - .anims = gSolarBeamBigOrbAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = AnimSolarBeamBigOrb, -}; - -const struct SpriteTemplate gSteelBeamSmallOrbSpriteTemplate = -{ - .tileTag = ANIM_TAG_STEEL_BEAM, - .paletteTag = ANIM_TAG_STEEL_BEAM, - .oam = &gOamData_AffineOff_ObjNormal_8x8, - .anims = gSolarBeamSmallOrbAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = AnimSolarBeamSmallOrb, -}; - void AnimTask_CreateSmallSteelBeamOrbs(u8 taskId) { if (--gTasks[taskId].data[0] == -1) @@ -7009,3 +7043,12 @@ void AnimTask_CreateSmallSteelBeamOrbs(u8 taskId) if (gTasks[taskId].data[1] == 15) DestroyAnimVisualTask(taskId); } + +static void AnimAcrobaticsSlashes(struct Sprite *sprite) +{ + int affineAnimNum = Random2() % ARRAY_COUNT(gRockPolishStreak_AffineAnimCmds); + InitSpritePosToAnimTarget(sprite, TRUE); + StartSpriteAffineAnim(sprite, affineAnimNum); + StoreSpriteCallbackInData6(sprite, DestroySpriteAndMatrix); + sprite->callback = RunStoredCallbackWhenAnimEnds; +} diff --git a/src/battle_anim_water.c b/src/battle_anim_water.c index e4044efe65..159b27dec9 100644 --- a/src/battle_anim_water.c +++ b/src/battle_anim_water.c @@ -584,6 +584,28 @@ const struct SpriteTemplate gAquaTailHitSpriteTemplate = .callback = AnimAquaTail, }; +static const union AnimCmd sAnimCmdAnimatedSpark2[] = { + ANIMCMD_FRAME((8 * 8) / (16 * 16) * 0, 8), + ANIMCMD_FRAME((8 * 8) / (16 * 16) * 1, 8), + ANIMCMD_FRAME((8 * 8) / (16 * 16) * 2, 8), + ANIMCMD_JUMP(0) +}; + +static const union AnimCmd *const sAnimCmdTable_AnimatedSpark2[] = { + sAnimCmdAnimatedSpark2, +}; + +const struct SpriteTemplate gSparkBeamSpriteTemplate = +{ + .tileTag = ANIM_TAG_SPARK_2, + .paletteTag = ANIM_TAG_SPARK_2, + .oam = &gOamData_AffineOff_ObjNormal_16x16, + .anims = sAnimCmdTable_AnimatedSpark2, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimToTargetInSinWave, +}; + static void AnimAquaTail(struct Sprite *sprite) { StartSpriteAffineAnim(sprite, gBattleAnimArgs[3]); diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index e1041582e8..48ab691dfa 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -233,7 +233,7 @@ static u16 GetNextBall(u16 ballId) { if (ballId == gBagPockets[BALLS_POCKET].itemSlots[i].itemId) { - ballNext = gBagPockets[BALLS_POCKET].itemSlots[i].itemId; + ballNext = gBagPockets[BALLS_POCKET].itemSlots[i+1].itemId; break; } } diff --git a/src/battle_interface.c b/src/battle_interface.c index ac33177a77..d3751f702f 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -1057,6 +1057,7 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl) objVram = ConvertIntToDecimalStringN(text + 2, lvl, STR_CONV_MODE_LEFT_ALIGN, 3); xPos = 5 * (3 - (objVram - (text + 2))); + MegaIndicator_SetVisibilities(healthboxSpriteId, TRUE); } windowTileData = AddTextPrinterAndCreateWindowOnHealthbox(text, xPos, 3, 2, &windowId); diff --git a/src/battle_util.c b/src/battle_util.c index 91e6e69b57..abd5f6ac20 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3760,6 +3760,8 @@ u8 AtkCanceller_UnableToUseMove2(void) if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN && IsBattlerGrounded(gBattlerTarget) && GetChosenMovePriority(gBattlerAttacker) > 0 + && gBattleMoves[gCurrentMove].target != MOVE_TARGET_ALL_BATTLERS + && gBattleMoves[gCurrentMove].target != MOVE_TARGET_OPPONENTS_FIELD && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)) { CancelMultiTurnMoves(gBattlerAttacker); @@ -4095,6 +4097,13 @@ static uq4_12_t GetSupremeOverlordModifier(u32 battler) return modifier; } +static bool32 HadMoreThanHalfHpNowHasLess(u32 battler) +{ + // Had more than half of hp before, now has less + return (gBattleStruct->hpBefore[battler] >= gBattleMons[battler].maxHP / 2 + && gBattleMons[battler].hp < gBattleMons[battler].maxHP / 2); +} + u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg) { u32 effect = 0; @@ -5043,6 +5052,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 PREPARE_STAT_BUFFER(gBattleTextBuff1, statId); } } + + if (effect) + gMultiHitCounter = 0; // Prevent multi-hit moves from hitting more than once after move has been absorbed. } break; case ABILITYEFFECT_MOVE_END: // Think contact abilities. @@ -5107,9 +5119,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) - // Had more than half of hp before, now has less - && gBattleStruct->hpBefore[battler] >= gBattleMons[battler].maxHP / 2 - && gBattleMons[battler].hp < gBattleMons[battler].maxHP / 2 + && HadMoreThanHalfHpNowHasLess(battler) && (gMultiHitCounter == 0 || gMultiHitCounter == 1) && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) && CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -5586,8 +5596,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED + && (gMultiHitCounter == 0 || gMultiHitCounter == 1) // Activates after all hits from a multi-hit move. && IsBattlerAlive(gBattlerTarget) - && (gBattleMons[gBattlerTarget].hp <= gBattleMons[gBattlerTarget].maxHP / 2) + && HadMoreThanHalfHpNowHasLess(gBattlerTarget) && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove))) { gBattlerAttacker = gBattlerTarget; diff --git a/src/data/graphics/items.h b/src/data/graphics/items.h index 60cd7d5187..2ef587d994 100644 --- a/src/data/graphics/items.h +++ b/src/data/graphics/items.h @@ -2023,3 +2023,6 @@ const u32 gItemIconPalette_LustrousGlobe[] = INCBIN_U32("graphics/items/icon_pal const u32 gItemIcon_BerserkGene[] = INCBIN_U32("graphics/items/icons/berserk_gene.4bpp.lz"); const u32 gItemIconPalette_BerserkGene[] = INCBIN_U32("graphics/items/icon_palettes/berserk_gene.gbapal.lz"); + +const u32 gItemIcon_FairyFeather[] = INCBIN_U32("graphics/items/icons/fairy_feather.4bpp.lz"); +const u32 gItemIconPalette_FairyFeather[] = INCBIN_U32("graphics/items/icon_palettes/fairy_feather.gbapal.lz"); diff --git a/src/data/graphics/pokemon.h b/src/data/graphics/pokemon.h index 7134fe20b4..176ccce380 100644 --- a/src/data/graphics/pokemon.h +++ b/src/data/graphics/pokemon.h @@ -5199,6 +5199,9 @@ const u8 gMonIcon_Fearow[] = INCBIN_U8("graphics/pokemon/fearow/icon.4bpp"); const u8 gMonIcon_Ekans[] = INCBIN_U8("graphics/pokemon/ekans/icon.4bpp"); const u8 gMonIcon_Arbok[] = INCBIN_U8("graphics/pokemon/arbok/icon.4bpp"); const u8 gMonIcon_Pikachu[] = INCBIN_U8("graphics/pokemon/pikachu/icon.4bpp"); +#if P_CUSTOM_GENDER_DIFF_ICONS == TRUE +const u8 gMonIcon_PikachuF[] = INCBIN_U8("graphics/pokemon/pikachu/iconf.4bpp"); +#endif const u8 gMonIcon_Raichu[] = INCBIN_U8("graphics/pokemon/raichu/icon.4bpp"); const u8 gMonIcon_Sandshrew[] = INCBIN_U8("graphics/pokemon/sandshrew/icon.4bpp"); const u8 gMonIcon_Sandslash[] = INCBIN_U8("graphics/pokemon/sandslash/icon.4bpp"); @@ -5376,6 +5379,9 @@ const u8 gMonIcon_Slowking[] = INCBIN_U8("graphics/pokemon/slowking/icon.4bpp"); const u8 gMonIcon_Misdreavus[] = INCBIN_U8("graphics/pokemon/misdreavus/icon.4bpp"); const u8 gMonIcon_Unown[] = INCBIN_U8("graphics/pokemon/unown/icon.4bpp"); const u8 gMonIcon_Wobbuffet[] = INCBIN_U8("graphics/pokemon/wobbuffet/icon.4bpp"); +#if P_CUSTOM_GENDER_DIFF_ICONS == TRUE +const u8 gMonIcon_WobbuffetF[] = INCBIN_U8("graphics/pokemon/wobbuffet/iconf.4bpp"); +#endif const u8 gMonIcon_Girafarig[] = INCBIN_U8("graphics/pokemon/girafarig/icon.4bpp"); const u8 gMonIcon_Pineco[] = INCBIN_U8("graphics/pokemon/pineco/icon.4bpp"); const u8 gMonIcon_Forretress[] = INCBIN_U8("graphics/pokemon/forretress/icon.4bpp"); @@ -5625,7 +5631,7 @@ const u8 gMonIcon_Riolu[] = INCBIN_U8("graphics/pokemon/riolu/icon.4bpp"); const u8 gMonIcon_Lucario[] = INCBIN_U8("graphics/pokemon/lucario/icon.4bpp"); const u8 gMonIcon_Hippopotas[] = INCBIN_U8("graphics/pokemon/hippopotas/icon.4bpp"); const u8 gMonIcon_Hippowdon[] = INCBIN_U8("graphics/pokemon/hippowdon/icon.4bpp"); -#if P_HIPPO_GENDER_DIFF_ICONS == TRUE +#if P_CUSTOM_GENDER_DIFF_ICONS == TRUE const u8 gMonIcon_HippopotasF[] = INCBIN_U8("graphics/pokemon/hippopotas/iconf.4bpp"); const u8 gMonIcon_HippowdonF[] = INCBIN_U8("graphics/pokemon/hippowdon/iconf.4bpp"); #endif diff --git a/src/data/item_icon_table.h b/src/data/item_icon_table.h index 30ae4ba19f..de6e92371b 100644 --- a/src/data/item_icon_table.h +++ b/src/data/item_icon_table.h @@ -844,7 +844,7 @@ const u32 *const gItemIconTable[ITEMS_COUNT + 1][2] = [ITEM_LINKING_CORD] = {gItemIcon_LinkingCord, gItemIconPalette_LinkingCord}, [ITEM_PEAT_BLOCK] = {gItemIcon_PeatBlock, gItemIconPalette_PeatBlock}, [ITEM_BERSERK_GENE] = {gItemIcon_BerserkGene, gItemIconPalette_BerserkGene}, - [ITEM_FAIRY_FEATHER] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FairyFeather, gItemIconPalette_FairyFeather}, + [ITEM_FAIRY_FEATHER] = {gItemIcon_FairyFeather, gItemIconPalette_FairyFeather}, [ITEM_SYRUPY_APPLE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_SyrupyApple, gItemIconPalette_SyrupyApple}, [ITEM_UNREMARKABLE_TEACUP] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_UnremarkableTeacup, gItemIconPalette_UnremarkableTeacup}, [ITEM_MASTERPIECE_TEACUP] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_MasterpieceTeacup, gItemIconPalette_MasterpieceTeacup}, diff --git a/src/pokemon_icon.c b/src/pokemon_icon.c index af10fb542b..8c6065aa40 100644 --- a/src/pokemon_icon.c +++ b/src/pokemon_icon.c @@ -1528,7 +1528,11 @@ const u8 *const gMonIconTable[NUM_SPECIES + 1] = // Female icon palette indexes still need to be defined in gMonIconPaletteIndicesFemale, even if they are the same as males. const u8 *const gMonIconTableFemale[NUM_SPECIES + 1] = { -#if P_GEN_4_POKEMON == TRUE && P_HIPPO_GENDER_DIFF_ICONS == TRUE +#if P_CUSTOM_GENDER_DIFF_ICONS == TRUE + [SPECIES_PIKACHU] = gMonIcon_PikachuF, + [SPECIES_WOBBUFFET] = gMonIcon_WobbuffetF, +#endif +#if P_GEN_4_POKEMON == TRUE && P_CUSTOM_GENDER_DIFF_ICONS == TRUE [SPECIES_HIPPOPOTAS] = gMonIcon_HippopotasF, [SPECIES_HIPPOWDON] = gMonIcon_HippowdonF, #endif @@ -2984,6 +2988,8 @@ const u8 gMonIconPaletteIndices[] = const u8 gMonIconPaletteIndicesFemale[] = { + [SPECIES_PIKACHU] = 2, + [SPECIES_WOBBUFFET] = 0, #if P_GEN_4_POKEMON == TRUE [SPECIES_HIPPOPOTAS] = 1, [SPECIES_HIPPOWDON] = 1, diff --git a/test/battle/ability/anger_shell.c b/test/battle/ability/anger_shell.c new file mode 100644 index 0000000000..6b916e088f --- /dev/null +++ b/test/battle/ability/anger_shell.c @@ -0,0 +1,95 @@ +#include "global.h" +#include "test/battle.h" + +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; + + PARAMETRIZE { hp = 249; activates = FALSE; } + PARAMETRIZE { hp = 100; activates = FALSE; } + PARAMETRIZE { hp = 50; activates = FALSE; } + PARAMETRIZE { hp = 251; activates = TRUE; } + PARAMETRIZE { hp = 255; activates = TRUE; } + + GIVEN { + ASSUME(gBattleMoves[MOVE_TACKLE].power != 0); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(hp); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + if (activates) { + ABILITY_POPUP(player, ABILITY_ANGER_SHELL); + } else { + NOT ABILITY_POPUP(player, ABILITY_ANGER_SHELL); + } + } THEN { + if (activates) { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + } + } +} + +SINGLE_BATTLE_TEST("Anger Shell lowers Def/Sp.Def by 1 and raises Atk/Sp.Atk/Spd by 1") +{ + u16 maxHp = 500; + GIVEN { + ASSUME(gBattleMoves[MOVE_TACKLE].power != 0); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + ABILITY_POPUP(player, ABILITY_ANGER_SHELL); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Defense fell!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Sp. Def fell!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Attack rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Sp. Atk rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Speed rose!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("Anger Shell activates after all hits from a multi-hit move") +{ + u32 j; + u16 maxHp = 500; + GIVEN { + ASSUME(gBattleMoves[MOVE_DOUBLE_SLAP].effect == EFFECT_MULTI_HIT); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } + OPPONENT(SPECIES_SHELLDER) { Ability(ABILITY_SKILL_LINK); } // Always hits 5 times. + } WHEN { + TURN { MOVE(opponent, MOVE_DOUBLE_SLAP); } + } SCENE { + for (j = 0; j < 4; j++) + { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent); + NOT ABILITY_POPUP(player, ABILITY_ANGER_SHELL); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent); + ABILITY_POPUP(player, ABILITY_ANGER_SHELL); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + } +} diff --git a/test/battle/ability/defiant.c b/test/battle/ability/defiant.c index 4b9cfa42b5..e8443ecc5f 100644 --- a/test/battle/ability/defiant.c +++ b/test/battle/ability/defiant.c @@ -115,3 +115,26 @@ DOUBLE_BATTLE_TEST("Defiant sharply raises opponent's Attack after Intimidate") EXPECT_EQ(opponentRight->statStages[STAT_ATK], (abilityRight == ABILITY_DEFIANT) ? DEFAULT_STAT_STAGE + 2 : DEFAULT_STAT_STAGE - 2); } } + +SINGLE_BATTLE_TEST("Defiant activates after Sticky Web lowers Speed") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_MANKEY) { Ability(ABILITY_DEFIANT); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_STICKY_WEB); } + TURN { SWITCH(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponent); + // Switch-in - Sticky Web activates + MESSAGE("Go! Mankey!"); + MESSAGE("Mankey was caught in a Sticky Web!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Mankey's Speed fell!"); + // Defiant activates + ABILITY_POPUP(player, ABILITY_DEFIANT); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Mankey's Attack sharply rose!"); + } +} diff --git a/test/battle/ability/sap_sipper.c b/test/battle/ability/sap_sipper.c index 918e553a3a..903427fcc5 100644 --- a/test/battle/ability/sap_sipper.c +++ b/test/battle/ability/sap_sipper.c @@ -57,3 +57,24 @@ SINGLE_BATTLE_TEST("Sap Sipper does not increase Attack if already maxed") } } } + +SINGLE_BATTLE_TEST("Sap Sipper blocks multi-hit grass type moves") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_BULLET_SEED].effect == EFFECT_MULTI_HIT); + PLAYER(SPECIES_MARILL) { Ability(ABILITY_SAP_SIPPER); } + OPPONENT(SPECIES_SHELLDER) { Ability(ABILITY_SKILL_LINK); } + } WHEN { + TURN { MOVE(opponent, MOVE_BULLET_SEED); } + } SCENE { + MESSAGE("Foe Shellder used Bullet Seed!"); + ABILITY_POPUP(player, ABILITY_SAP_SIPPER); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Marill's Attack rose!"); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, opponent); + HP_BAR(player); + MESSAGE("Hit 5 time(s)!"); + } + } +} diff --git a/test/battle/ai.c b/test/battle/ai.c index 6f863e3f31..91a9bc663a 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -260,8 +260,8 @@ AI_SINGLE_BATTLE_TEST("AI can choose a status move that boosts the attack by two AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking into account accuracy and move effect") { u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE; - u16 expectedMove, abilityAtk = ABILITY_NONE; - u16 expectedMove2 = MOVE_NONE; + u16 expectedMove, expectedMove2 = MOVE_NONE; + u16 abilityAtk = ABILITY_NONE, holdItemAtk = ITEM_NONE; // Psychic is not very effective, but always hits. Solarbeam requires a charging turn, Double Edge has recoil and Focus Blast can miss; PARAMETRIZE { abilityAtk = ABILITY_STURDY; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; expectedMove = MOVE_PSYCHIC; } @@ -272,12 +272,24 @@ AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking // This time it's Solarbeam + Psychic, because the weather is sunny. PARAMETRIZE { abilityAtk = ABILITY_DROUGHT; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SOLAR_BEAM; } + // Psychic and Solar Beam are chosen because user is holding Power Herb + PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; + expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SOLAR_BEAM; } + // Psychic and Skull Bash are chosen because user is holding Power Herb + PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; + expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SKULL_BASH; } + // Skull Bash is chosen because it's the most accurate and is holding Power Herb + PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_SLAM; move4 = MOVE_DOUBLE_EDGE; + expectedMove = MOVE_SKULL_BASH; } + // Crabhammer is chosen even if Skull Bash is more accurate, the user has no Power Herb + PARAMETRIZE { abilityAtk = ABILITY_STURDY; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_SLAM; move4 = MOVE_CRABHAMMER; + expectedMove = MOVE_CRABHAMMER; } GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_WOBBUFFET) { HP(5); } PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_GEODUDE) { Moves(move1, move2, move3, move4); Ability(abilityAtk); } + OPPONENT(SPECIES_GEODUDE) { Moves(move1, move2, move3, move4); Ability(abilityAtk); Item(holdItemAtk); } } WHEN { TURN { if (expectedMove2 == MOVE_NONE) { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); } else {EXPECT_MOVES(opponent, expectedMove, expectedMove2); SCORE_EQ(opponent, expectedMove, expectedMove2); SEND_OUT(player, 1);} @@ -288,6 +300,36 @@ AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking } } +AI_SINGLE_BATTLE_TEST("AI won't use Solar Beam if there is no Sun up or the user is not holding Power Herb") +{ + u16 abilityAtk = ABILITY_NONE; + u16 holdItemAtk = ITEM_NONE; + + PARAMETRIZE { abilityAtk = ABILITY_DROUGHT; } + PARAMETRIZE { holdItemAtk = ITEM_POWER_HERB; } + PARAMETRIZE { } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { HP(211); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_TYPHLOSION) { Moves(MOVE_SOLAR_BEAM, MOVE_GRASS_PLEDGE); Ability(abilityAtk); Item(holdItemAtk); } + } WHEN { + if (abilityAtk == ABILITY_DROUGHT) { + TURN { EXPECT_MOVES(opponent, MOVE_SOLAR_BEAM, MOVE_GRASS_PLEDGE); } + TURN { EXPECT_MOVES(opponent, MOVE_SOLAR_BEAM, MOVE_GRASS_PLEDGE); SEND_OUT(player, 1); } + } else if (holdItemAtk == ITEM_POWER_HERB) { + TURN { EXPECT_MOVES(opponent, MOVE_SOLAR_BEAM, MOVE_GRASS_PLEDGE); MOVE(player, MOVE_KNOCK_OFF); } + TURN { EXPECT_MOVE(opponent, MOVE_GRASS_PLEDGE); SEND_OUT(player, 1); } + } else { + TURN { EXPECT_MOVE(opponent, MOVE_GRASS_PLEDGE); } + TURN { EXPECT_MOVE(opponent, MOVE_GRASS_PLEDGE); SEND_OUT(player, 1); } + } + } SCENE { + MESSAGE("Wobbuffet fainted!"); + } +} + AI_SINGLE_BATTLE_TEST("AI won't use ground type attacks against flying type Pokemon unless Gravity is in effect") { GIVEN { diff --git a/test/battle/move_effect/knock_off.c b/test/battle/move_effect/knock_off.c new file mode 100644 index 0000000000..3022a19633 --- /dev/null +++ b/test/battle/move_effect/knock_off.c @@ -0,0 +1,53 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_KNOCK_OFF].effect == EFFECT_KNOCK_OFF); +} + +SINGLE_BATTLE_TEST("Knock Off knocks a healing berry before it has the chance to activate") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); MaxHP(500); HP(255); } + } WHEN { + TURN { MOVE(player, MOVE_KNOCK_OFF); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + MESSAGE("Foe Wobbuffet's Sitrus Berry restored health!"); + } + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF); + MESSAGE("Wobbuffet knocked off Foe Wobbuffet's Sitrus Berry!"); + } +} + +SINGLE_BATTLE_TEST("Knock Off activates after Rocky Helmet and Weakness Policy") +{ + u16 item = 0; + + PARAMETRIZE { item = ITEM_WEAKNESS_POLICY; } + PARAMETRIZE { item = ITEM_ROCKY_HELMET; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_KNOCK_OFF); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + if (item == ITEM_WEAKNESS_POLICY) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE); + MESSAGE("Using WeaknssPolicy, the Attack of Foe Wobbuffet sharply rose!"); + MESSAGE("Using WeaknssPolicy, the Sp. Atk of Foe Wobbuffet sharply rose!"); + } else if (item == ITEM_ROCKY_HELMET) { + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by Foe Wobbuffet's Rocky Helmet!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF); + MESSAGE("Wobbuffet knocked off Foe Wobbuffet's Rocky Helmet!"); + } + } +} diff --git a/test/battle/terrain/psychic.c b/test/battle/terrain/psychic.c index c810af3401..45c2886e32 100644 --- a/test/battle/terrain/psychic.c +++ b/test/battle/terrain/psychic.c @@ -78,7 +78,6 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all battlers") { - KNOWN_FAILING; GIVEN { PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); } OPPONENT(SPECIES_WOBBUFFET); @@ -93,7 +92,6 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all opponents") { - KNOWN_FAILING; GIVEN { PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); } OPPONENT(SPECIES_WOBBUFFET); @@ -124,7 +122,6 @@ DOUBLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority field moves") { - KNOWN_FAILING; GIVEN { PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); } OPPONENT(SPECIES_WOBBUFFET);