diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml index 44cc8c2891..6493f4c11b 100644 --- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml +++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml @@ -23,9 +23,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.7.2 (Latest release) + - 1.7.3 (Latest release) - master (default when pulling, unreleased bugfixes) - upcoming (Edge) + - 1.7.2 - 1.7.1 - 1.7.0 - 1.6.2 diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml index 41d5c30eff..e39eac4f56 100644 --- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml +++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml @@ -23,9 +23,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.7.2 (Latest release) + - 1.7.3 (Latest release) - master (default when pulling, unreleased bugfixes) - upcoming (Edge) + - 1.7.2 - 1.7.1 - 1.7.0 - 1.6.2 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8c5bcb7ed8..0322cbe11f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,6 +17,7 @@ jobs: GAME_LANGUAGE: ENGLISH MODERN: 0 COMPARE: 0 + UNUSED_ERROR: 1 steps: - name: Checkout uses: actions/checkout@v2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fba967c8a..d4805a5d40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Pokeemerald-Expansion Changelogs -## [Version 1.7.2](docs/changelogs/1.7.2.md) - Bugfix Release +## Version 1.7.x +### [Version 1.7.3](docs/changelogs/1.7.3.md) - Bugfix Release +### [Version 1.7.2](docs/changelogs/1.7.2.md) - Bugfix Release +### [Version 1.7.1](docs/changelogs/1.7.1.md) - Bugfix Release +### [Version 1.7.0](docs/changelogs/1.7.0.md) - Feature Release -## [Version 1.7.1](docs/changelogs/1.7.1.md) - Bugfix Release - -## [Version 1.7.0](docs/changelogs/1.7.0.md) - Feature Release - -## [Version 1.6.2](docs/changelogs/1.6.2.md) - Bugfix Release +## Version 1.6.x +### [Version 1.6.2](docs/changelogs/1.6.2.md) - Bugfix Release diff --git a/Makefile b/Makefile index d899015225..5632b3570e 100644 --- a/Makefile +++ b/Makefile @@ -36,13 +36,14 @@ else EXE := endif -TITLE := POKEMON EMER -GAME_CODE := BPEE -MAKER_CODE := 01 -REVISION := 0 -MODERN ?= 1 -TEST ?= 0 -ANALYZE ?= 0 +TITLE := POKEMON EMER +GAME_CODE := BPEE +MAKER_CODE := 01 +REVISION := 0 +MODERN ?= 1 +TEST ?= 0 +ANALYZE ?= 0 +UNUSED_ERROR ?= 0 ifeq (agbcc,$(MAKECMDGOALS)) MODERN := 0 @@ -127,6 +128,12 @@ override CFLAGS += -mthumb -mthumb-interwork -O2 -mabi=apcs-gnu -mtune=arm7tdmi ifeq ($(ANALYZE),1) override CFLAGS += -fanalyzer endif +# Only throw an error for unused elements if its RH-Hideout's repo +ifeq ($(UNUSED_ERROR),0) +ifneq ($(GITHUB_REPOSITORY_OWNER),rh-hideout) +override CFLAGS += -Wno-error=unused-variable -Wno-error=unused-const-variable -Wno-error=unused-parameter -Wno-error=unused-function -Wno-error=unused-but-set-parameter -Wno-error=unused-but-set-variable -Wno-error=unused-value -Wno-error=unused-local-typedefs +endif +endif ROM := $(MODERN_ROM_NAME) OBJ_DIR := $(MODERN_OBJ_DIR_NAME) LIBPATH := -L "$(dir $(shell $(PATH_MODERNCC) -mthumb -print-file-name=libgcc.a))" -L "$(dir $(shell $(PATH_MODERNCC) -mthumb -print-file-name=libnosys.a))" -L "$(dir $(shell $(PATH_MODERNCC) -mthumb -print-file-name=libc.a))" diff --git a/README.md b/README.md index 4c7868e0a8..82a9582337 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ pokeemerald-expansion is a decomp hack base project based off pret's [pokeemeral If you use pokeemerald-expansion in your hack, please add RHH (Rom Hacking Hideout) to your credits list. Optionally, you can list the version used, so it can help players know what features to expect. You can phrase it as the following: ``` -Based off RHH's pokeemerald-expansion v1.7.2 https://github.com/rh-hideout/pokeemerald-expansion/ +Based off RHH's pokeemerald-expansion v1.7.3 https://github.com/rh-hideout/pokeemerald-expansion/ ``` ## What features are included? @@ -167,7 +167,7 @@ With this, you'll get the latest version of pokeemerald-expansion, plus a couple ## **How do I update my version of pokeemerald-expansion?** - If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`. -- Once you have your remote set up, run the command `git pull RHH expansion/1.7.2`. +- Once you have your remote set up, run the command `git pull RHH expansion/1.7.3`. ### Please consider crediting the entire [list of contributors](https://github.com/rh-hideout/pokeemerald-expansion/wiki/Credits) in your project, as they have all worked hard to develop this project :) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 80e498d2b0..b3a5c77716 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -72,6 +72,10 @@ .2byte \id .endm + .macro printsavedstring + printstring 0 + .endm + .macro printselectionstring id:req .byte 0x11 .2byte \id @@ -776,8 +780,21 @@ .byte 0x8b .endm - .macro unused0x8C + .macro twoturnmoveschargestringandanimation .byte 0x8c + .4byte 1f @animation then attack string + @default - attack string then animation + printsavedstring + waitmessage B_WAIT_TIME_LONG + attackanimation + waitanimation + goto 2f + 1: + attackanimation + waitanimation + printsavedstring + waitmessage B_WAIT_TIME_LONG + 2: .endm .macro setmultihitcounter value:req @@ -1035,12 +1052,20 @@ .4byte \failInstr .endm - .macro setsemiinvulnerablebit + .macro setsemiinvulnerablebit clear=FALSE .byte 0xc5 + .byte \clear .endm .macro clearsemiinvulnerablebit + setsemiinvulnerablebit TRUE + .endm + + .macro jumpifweathercheckchargeeffects battler:req, checkChargeTurnEffects:req, jumpInstr:req .byte 0xc6 + .byte \battler + .byte \checkChargeTurnEffects + .4byte \jumpInstr .endm .macro setminimize @@ -1337,13 +1362,6 @@ .4byte \jumpInstr .endm - .macro jumpifholdeffect battler:req, holdEffect:req, jumpInstr:req - callnative BS_JumpIfHoldEffect - .byte \battler - .2byte \holdEffect - .4byte \jumpInstr - .endm - .macro dostockpilestatchangeswearoff, battler:req, statChangeInstr:req callnative BS_DoStockpileStatChangesWearOff .byte \battler @@ -2005,10 +2023,15 @@ .4byte \jumpInstr .endm - .macro jumpifnoholdeffect battler:req, holdEffect:req, jumpInstr:req - various \battler, VARIOUS_JUMP_IF_NO_HOLD_EFFECT + .macro jumpifholdeffect battler:req, holdEffect:req, jumpInstr:req, equal=TRUE + various \battler, VARIOUS_JUMP_IF_HOLD_EFFECT .byte \holdEffect .4byte \jumpInstr + .byte \equal + .endm + + .macro jumpifnoholdeffect battler:req, holdEffect:req, jumpInstr:req + jumpifholdeffect \battler, \holdEffect, \jumpInstr, FALSE .endm .macro infatuatewithbattler battler:req, infatuateWith:req diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 6a9fce1b89..b9bd3efaf0 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -9278,13 +9278,12 @@ Boomburst_Doubles: goto Boomburst_Last Move_FAIRY_LOCK:: - loadspritegfx ANIM_TAG_CHAIN_LINK @Chain Colour - loadspritegfx ANIM_TAG_FAIRY_LOCK_CHAINS @AnimTask is missing for Fairy Lock Chain + loadspritegfx ANIM_TAG_FAIRY_LOCK_CHAINS setalpha 8, 8 monbg ANIM_ATK_PARTNER createvisualtask AnimTask_BlendBattleAnimPal, 0xa, F_PAL_BG, 0x1, 0x0, 0x8, 0x6B1F waitforvisualfinish - loopsewithpan SE_M_SCRATCH, SOUND_PAN_TARGET, 0x6, 0x4 + loopsewithpan SE_M_SCRATCH, SOUND_PAN_TARGET, 0x6, 0x9 createvisualtask AnimTask_VoltTackleBolt, 0x5, 0x7 createvisualtask AnimTask_VoltTackleBolt, 0x5, 0x33 createvisualtask AnimTask_VoltTackleBolt, 0x5, 0x2 @@ -17159,7 +17158,7 @@ Move_RAGING_BULL:: restorebg waitbgfadein end - + @ Credits to Z-nogyroP. Simple anim that combines Force Palm + Fake Out Move_UPPER_HAND:: loadspritegfx ANIM_TAG_SHADOW_BALL @@ -17187,12 +17186,53 @@ Move_UPPER_HAND:: blendoff end +Move_JET_PUNCH: + loadspritegfx ANIM_TAG_ICE_CRYSTALS + loadspritegfx ANIM_TAG_HANDS_AND_FEET + loadspritegfx ANIM_TAG_IMPACT + loadspritegfx ANIM_TAG_SPLASH + loadspritegfx ANIM_TAG_WATER_IMPACT + loadspritegfx ANIM_TAG_SMALL_BUBBLES + monbg ANIM_DEF_PARTNER + setalpha 12, 8 + playsewithpan SE_M_DIVE, SOUND_PAN_TARGET + createvisualtask AnimTask_AttackerStretchAndDisappear, 2 + createvisualtask AnimTask_TranslateMonEllipticalRespectSide, 1, ANIM_ATTACKER, 24, 6, 1, 5 + createvisualtask AnimTask_TraceMonBlended, 2, 0, 4, 7, 3 + delay 18 + createvisualtask AnimTask_SetAttackerInvisibleWaitForSignal, 2 + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_TARGET, 2, 0, 9, RGB_BLUE + delay 8 + createvisualtask AnimTask_ExtremeSpeedMonReappear, 2 + createsprite gSmallBubblePairSpriteTemplate, ANIM_TARGET, 2, 0x14, 0xffec, 0x14, ANIM_TARGET + createsprite gSmallBubblePairSpriteTemplate, ANIM_TARGET, 2, 0xa, 0xa, 0x14, ANIM_TARGET + createsprite gFistFootSpriteTemplate, ANIM_TARGET, 3, 0, 0, 8, 1, 0 + playsewithpan SE_M_DIVE, SOUND_PAN_TARGET + createsprite gBasicHitSplatSpriteTemplate, ANIM_TARGET, 2, 0, 0, ANIM_TARGET, 1 + createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 0, 3, 15, 1 + delay 6 + createsprite gSmallBubblePairSpriteTemplate, ANIM_TARGET, 2, 0x14, 0xffec, 0x14, ANIM_TARGET + createsprite gSmallBubblePairSpriteTemplate, ANIM_TARGET, 2, 0xa, 0xa, 0x14, ANIM_TARGET + createsprite gWaterHitSplatSpriteTemplate, ANIM_ATTACKER, 3, 0, 13, ANIM_TARGET, 1 + createsprite gSmallDriftingBubblesSpriteTemplate, ANIM_ATTACKER, 4, 0, 13 + createsprite gSmallDriftingBubblesSpriteTemplate, ANIM_ATTACKER, 4, 0, 13 + delay 2 + createsprite gWaterHitSplatSpriteTemplate, ANIM_ATTACKER, 3, 0, 8, ANIM_TARGET, 1 + createsprite gSmallDriftingBubblesSpriteTemplate, ANIM_ATTACKER, 4, 0, 8 + createsprite gSmallDriftingBubblesSpriteTemplate, ANIM_ATTACKER, 4, 0, 8 + delay 2 + call DiveSetUpWaterDroplets + waitforvisualfinish + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_TARGET, 0, 9, 0, RGB_BLUE + clearmonbg ANIM_DEF_PARTNER + blendoff + end + Move_TERA_BLAST:: Move_AXE_KICK:: Move_LAST_RESPECTS:: Move_LUMINA_CRASH:: Move_ORDER_UP:: -Move_JET_PUNCH:: Move_SPICY_EXTRACT:: Move_SPIN_OUT:: Move_POPULATION_BOMB:: diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 685378df8f..d3ac025056 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -585,46 +585,9 @@ BattleScript_BeakBlastBurn:: call BattleScript_MoveEffectBurn return -BattleScript_EffectMeteorBeam:: - @ DecideTurn - jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn - jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn - jumpifmove MOVE_METEOR_BEAM, BattleScript_SetStringMeteorBeam - jumpifmove MOVE_ELECTRO_SHOT, BattleScript_SetStringElectroShock -BattleScript_TryCharging: - call BattleScript_FirstChargingTurnMeteorBeam - jumpifmove MOVE_METEOR_BEAM, BattleScript_TryMeteorBeam - jumpifweatheraffected BS_ATTACKER, B_WEATHER_RAIN, BattleScript_TwoTurnMovesSecondTurn @ Check for move Electro Shot -BattleScript_TryMeteorBeam: - jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd - call BattleScript_PowerHerbActivation - goto BattleScript_TwoTurnMovesSecondTurn - -BattleScript_SetStringMeteorBeam: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_METEOR_BEAM - goto BattleScript_TryCharging - -BattleScript_SetStringElectroShock: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_ELECTRO_SHOCK - goto BattleScript_TryCharging - -BattleScript_FirstChargingTurnMeteorBeam:: - attackcanceler - flushtextbox - ppreduce - attackanimation - waitanimation - orword gHitMarker, HITMARKER_CHARGING - seteffectprimary MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER - copybyte cMULTISTRING_CHOOSER, sTWOTURN_STRINGID - printfromtable gFirstTurnOfTwoStringIds - waitmessage B_WAIT_TIME_LONG - setadditionaleffects @ only onChargeTurnOnly effects will work here - return - BattleScript_EffectSkyDrop:: - jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_SkyDropTurn2 attackcanceler + jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_SkyDropTurn2 ppreduce accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE attackstring @@ -638,16 +601,10 @@ BattleScript_EffectSkyDrop:: BattleScript_SkyDropWork: setskydrop - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SKY_DROP - setsemiinvulnerablebit - call BattleScriptFirstChargingTurnAfterAttackString + call BattleScript_FirstChargingTurnAfterAttackString goto BattleScript_MoveEnd BattleScript_SkyDropTurn2: - attackcanceler - setbyte sB_ANIM_TURN, 0x1 - clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING - orword gHitMarker, HITMARKER_NO_PPDEDUCT - clearsemiinvulnerablebit + call BattleScript_TwoTurnMovesSecondTurnRet attackstring clearskydrop BattleScript_SkyDropChangedTarget jumpiftype BS_TARGET, TYPE_FLYING, BattleScript_SkyDropFlyingType @@ -3485,29 +3442,6 @@ BattleScript_KOFail:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_TwoTurnMovesSecondTurn:: - attackcanceler - setbyte sB_ANIM_TURN, 1 - clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING - orword gHitMarker, HITMARKER_NO_PPDEDUCT - goto BattleScript_HitFromAccCheck - -BattleScriptFirstChargingTurn:: - attackcanceler - flushtextbox - ppreduce - attackstring -BattleScriptFirstChargingTurnAfterAttackString: - pause B_WAIT_TIME_LONG - copybyte cMULTISTRING_CHOOSER, sTWOTURN_STRINGID - printfromtable gFirstTurnOfTwoStringIds - waitmessage B_WAIT_TIME_LONG - attackanimation - waitanimation - orword gHitMarker, HITMARKER_CHARGING - seteffectprimary MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER - return - BattleScript_EffectSuperFang:: attackcanceler accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE @@ -3579,7 +3513,7 @@ BattleScript_EffectFocusEnergy:: attackcanceler attackstring ppreduce - jumpifstatus2 BS_ATTACKER, STATUS2_FOCUS_ENERGY, BattleScript_ButItFailed + jumpifstatus2 BS_ATTACKER, STATUS2_FOCUS_ENERGY_ANY, BattleScript_ButItFailed setfocusenergy attackanimation waitanimation @@ -3784,34 +3718,16 @@ BattleScript_PowerHerbActivation: BattleScript_EffectTwoTurnsAttack:: jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn - jumpifmove MOVE_SKY_ATTACK, BattleScript_EffectTwoTurnsAttackSkyAttack - jumpifmove MOVE_RAZOR_WIND, BattleScript_EffectTwoTurnsAttackRazorWind - jumpifmove MOVE_ICE_BURN, BattleScript_EffectTwoTurnsAttackIceBurn - jumpifmove MOVE_FREEZE_SHOCK, BattleScript_EffectTwoTurnsAttackFreezeShock - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_RAZOR_WIND -BattleScript_EffectTwoTurnsAttackContinue: - call BattleScriptFirstChargingTurn - jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd - call BattleScript_PowerHerbActivation - goto BattleScript_TwoTurnMovesSecondTurn -BattleScript_EffectTwoTurnsAttackSkyAttack: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SKY_ATTACK - goto BattleScript_EffectTwoTurnsAttackContinue -BattleScript_EffectTwoTurnsAttackRazorWind: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_RAZOR_WIND - goto BattleScript_EffectTwoTurnsAttackContinue -BattleScript_EffectTwoTurnsAttackIceBurn: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_RAZOR_WIND - goto BattleScript_EffectTwoTurnsAttackContinue -BattleScript_EffectTwoTurnsAttackFreezeShock: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_FREEZE_SHOCK - goto BattleScript_EffectTwoTurnsAttackContinue + jumpifweathercheckchargeeffects BS_ATTACKER, TRUE, BattleScript_EffectHit + call BattleScript_FirstChargingTurn + jumpifweathercheckchargeeffects BS_ATTACKER, FALSE, BattleScript_TwoTurnMovesSecondTurn + jumpifholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_TwoTurnMovesSecondPowerHerbActivates + goto BattleScript_MoveEnd BattleScript_EffectGeomancy:: jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_GeomancySecondTurn jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_GeomancySecondTurn - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_GEOMANCY - call BattleScriptFirstChargingTurn + call BattleScript_FirstChargingTurn jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd call BattleScript_PowerHerbActivation BattleScript_GeomancySecondTurn: @@ -3848,6 +3764,43 @@ BattleScript_GeomancyTrySpeed:: BattleScript_GeomancyEnd:: goto BattleScript_MoveEnd +BattleScript_FirstChargingTurn:: + attackcanceler +.if B_UPDATED_MOVE_DATA >= GEN_5 @ before Gen 5, charge moves did not print an attack string on the charge turn + flushtextbox + attackstring + waitmessage B_WAIT_TIME_LONG +.endif + ppreduce +BattleScript_FirstChargingTurnAfterAttackString: + setsemiinvulnerablebit @ only for moves with EFFECT_SEMI_INVULNERABLE/EFFECT_SKY_DROP + orword gHitMarker, HITMARKER_CHARGING + seteffectprimary MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER + twoturnmoveschargestringandanimation + setadditionaleffects @ only onChargeTurnOnly effects will work here + return + +BattleScript_TwoTurnMovesSecondPowerHerbActivates: + call BattleScript_PowerHerbActivation + call BattleScript_TwoTurnMovesSecondTurnRet + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE +.if B_UPDATED_MOVE_DATA < GEN_5 @ before Gen 5, charge moves did not print an attack string on the charge turn + attackstring +.endif + goto BattleScript_HitFromCritCalc + +BattleScript_TwoTurnMovesSecondTurn:: + attackcanceler + call BattleScript_TwoTurnMovesSecondTurnRet + orword gHitMarker, HITMARKER_NO_PPDEDUCT + goto BattleScript_HitFromAccCheck + +BattleScript_TwoTurnMovesSecondTurnRet: + setbyte sB_ANIM_TURN, 1 + clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING + clearsemiinvulnerablebit @ only for moves with EFFECT_SEMI_INVULNERABLE/EFFECT_SKY_DROP + return + BattleScript_EffectSubstitute:: attackcanceler ppreduce @@ -4117,26 +4070,7 @@ BattleScript_EffectDestinyBond:: goto BattleScript_MoveEnd BattleScript_EffectEerieSpell:: - attackcanceler - attackstring - ppreduce - accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE - attackstring - ppreduce - critcalc - damagecalc - adjustdamage - attackanimation - waitanimation - effectivenesssound - hitanimation BS_TARGET - waitstate - healthbarupdate BS_TARGET - datahpupdate BS_TARGET - critmessage - waitmessage B_WAIT_TIME_LONG - resultmessage - waitmessage B_WAIT_TIME_LONG + call BattleScript_EffectHit_Ret tryfaintmon BS_TARGET eeriespellppreduce BattleScript_MoveEnd printstring STRINGID_PKMNREDUCEDPP @@ -4178,17 +4112,6 @@ BattleScript_PartyHealEnd:: waitstate goto BattleScript_MoveEnd -BattleScript_EffectTripleKick:: - attackcanceler - accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE - jumpifmove MOVE_TRIPLE_AXEL BS_TripleAxel - addbyte sTRIPLE_KICK_POWER, 10 @ triple kick gets +10 power - goto BattleScript_HitFromAtkString - -BS_TripleAxel: - addbyte sTRIPLE_KICK_POWER, 20 @ triple axel gets +20 power - goto BattleScript_HitFromAtkString - BattleScript_EffectMeanLook:: attackcanceler attackstring @@ -4654,23 +4577,6 @@ BattleScript_EffectMirrorCoat:: adjustdamage goto BattleScript_HitFromAtkAnimation -BattleScript_EffectSkullBash:: - jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn - jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SKULL_BASH - call BattleScriptFirstChargingTurn - setstatchanger STAT_DEF, 1, FALSE - statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_SkullBashEnd - jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_SkullBashEnd - setgraphicalstatchangevalues - playanimation BS_ATTACKER, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 - printfromtable gStatUpStringIds - waitmessage B_WAIT_TIME_LONG -BattleScript_SkullBashEnd:: - jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd - call BattleScript_PowerHerbActivation - goto BattleScript_TwoTurnMovesSecondTurn - BattleScript_EffectFutureSight:: attackcanceler attackstring @@ -4682,25 +4588,6 @@ BattleScript_EffectFutureSight:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_EffectGust:: - goto BattleScript_EffectHit - -BattleScript_EffectSolarBeam:: - jumpifweatheraffected BS_ATTACKER, B_WEATHER_SUN, BattleScript_SolarBeamOnFirstTurn -BattleScript_SolarBeamDecideTurn:: - jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_TwoTurnMovesSecondTurn - jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_TwoTurnMovesSecondTurn - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_SOLAR_BEAM - call BattleScriptFirstChargingTurn - jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd - call BattleScript_PowerHerbActivation - goto BattleScript_TwoTurnMovesSecondTurn -BattleScript_SolarBeamOnFirstTurn:: - orword gHitMarker, HITMARKER_CHARGING - seteffectprimary MOVE_EFFECT_CHARGING | MOVE_EFFECT_AFFECTS_USER - ppreduce - goto BattleScript_TwoTurnMovesSecondTurn - BattleScript_EffectTeleport:: .if B_TELEPORT_BEHAVIOR >= GEN_7 jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_EffectBatonPass @@ -4765,46 +4652,6 @@ BattleScript_BeatUpEnd:: end .endif -BattleScript_EffectSemiInvulnerable:: - jumpifstatus2 BS_ATTACKER, STATUS2_MULTIPLETURNS, BattleScript_SecondTurnSemiInvulnerable - jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_NO_ATTACKSTRING, BattleScript_SecondTurnSemiInvulnerable - jumpifmove MOVE_FLY, BattleScript_FirstTurnFly - jumpifmove MOVE_DIVE, BattleScript_FirstTurnDive - jumpifmove MOVE_BOUNCE, BattleScript_FirstTurnBounce - jumpifmove MOVE_PHANTOM_FORCE, BattleScript_FirstTurnPhantomForce - jumpifmove MOVE_SHADOW_FORCE, BattleScript_FirstTurnPhantomForce - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_DIG - goto BattleScript_FirstTurnSemiInvulnerable -BattleScript_FirstTurnBounce:: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_BOUNCE - goto BattleScript_FirstTurnSemiInvulnerable -BattleScript_FirstTurnDive:: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_DIVE - goto BattleScript_FirstTurnSemiInvulnerable -BattleScript_FirstTurnPhantomForce: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_PHANTOM_FORCE - goto BattleScript_FirstTurnSemiInvulnerable -BattleScript_FirstTurnFly:: - setbyte sTWOTURN_STRINGID, B_MSG_TURN1_FLY -BattleScript_FirstTurnSemiInvulnerable:: - call BattleScriptFirstChargingTurn - setsemiinvulnerablebit - jumpifnoholdeffect BS_ATTACKER, HOLD_EFFECT_POWER_HERB, BattleScript_MoveEnd - call BattleScript_PowerHerbActivation -BattleScript_SecondTurnSemiInvulnerable:: - attackcanceler - setbyte sB_ANIM_TURN, 1 - clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING - orword gHitMarker, HITMARKER_NO_PPDEDUCT -BattleScript_SemiInvulnerableTryHit:: - accuracycheck BattleScript_SemiInvulnerableMiss, ACC_CURR_MOVE - clearsemiinvulnerablebit - goto BattleScript_HitFromAtkString - -BattleScript_SemiInvulnerableMiss:: - clearsemiinvulnerablebit - goto BattleScript_PrintMoveMissed - BattleScript_EffectDefenseCurl:: attackcanceler attackstring @@ -5544,9 +5391,6 @@ BattleScript_CosmicPowerTrySpDef:: BattleScript_CosmicPowerEnd:: goto BattleScript_MoveEnd -BattleScript_EffectSkyUppercut:: - goto BattleScript_EffectHit - BattleScript_EffectBulkUp:: attackcanceler attackstring @@ -9941,10 +9785,8 @@ BattleScript_RaiseCritAlliesLoop: setstatchanger STAT_ATK, 0, FALSE @ for animation setgraphicalstatchangevalues playanimation BS_TARGET, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 - swapattackerwithtarget printstring STRINGID_PKMNGETTINGPUMPED waitmessage B_WAIT_TIME_LONG - swapattackerwithtarget BattleScript_RaiseCritAlliesIncrement: setbyte sSTAT_ANIM_PLAYED, FALSE jumpifbytenotequal gBattlerTarget, gBattlerAttacker, BattleScript_RaiseCritAlliesEnd diff --git a/data/battle_scripts_2.s b/data/battle_scripts_2.s index 78e077fbd5..d760e00acd 100644 --- a/data/battle_scripts_2.s +++ b/data/battle_scripts_2.s @@ -114,7 +114,7 @@ BattleScript_ItemSetMist:: BattleScript_ItemSetFocusEnergy:: call BattleScript_UseItemMessage - jumpifstatus2 BS_ATTACKER, STATUS2_FOCUS_ENERGY, BattleScript_ButItFailed + jumpifstatus2 BS_ATTACKER, STATUS2_FOCUS_ENERGY_ANY, BattleScript_ButItFailed setfocusenergy playmoveanimation BS_ATTACKER, MOVE_FOCUS_ENERGY waitanimation diff --git a/docs/changelogs/1.7.3.md b/docs/changelogs/1.7.3.md new file mode 100644 index 0000000000..4727e195bc --- /dev/null +++ b/docs/changelogs/1.7.3.md @@ -0,0 +1,77 @@ +# Version 1.7.3 + +```md +## How to update +- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`. +- Once you have your remote set up, run the command `git pull RHH expansion/1.7.3`. +``` + +## 🌋 *IMPORTANT CHANGES* 🌋 +* Unused warnings are no longer treated as errrors by default by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4092 +* [Critical fix] Backported gHeap alignment fix from upstream pret by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4089 + * Fixes issue that causes graphics to bug when leaving Littleroot for Birch's cutscene. + +## 🧬 General 🧬 +* Fixed HGSS Dex's dark mode search palette by @ravepossum in https://github.com/rh-hideout/pokeemerald-expansion/pull/4095 + +## 🐉 Pokémon 🐉 +### Changed +* Condensed Oinkologne teachable learnsets (they previously had different tables despite having the same learnsets) by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4026 +* Removed illegal teachable learnset moves that didn't match Gen 7 or 9 by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4039 and https://github.com/rh-hideout/pokeemerald-expansion/pull/4042 +### Fixed +* Fixed incorrect family toggle preproc blocks by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4024 + * Cosplay and Cap Pikachu animations were still being included even if disabled. + * Fixed compile errors when: + * Feebas' family was disabled but not Castform. + * Chatot was disabled but not Spiritomb. + * Virizion was disabled but not Tornadus or Thundurus. + * Zekrom was disabled but not Landorus. + * Kyurem was disabled but not Keldeo or Meloetta. + * Wishiwashi was disabled but not Rockruff. +* Fixed compile error when disabling Gen 4 cross-evolutions while having Kingdra enabled by @Skyeward and @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4044 and https://github.com/rh-hideout/pokeemerald-expansion/pull/4046 + +## ⚔️ Battle General ⚔️ ## +### Fixed +* Fixed Steven double battle palette error by @johannakullmann in https://github.com/rh-hideout/pokeemerald-expansion/pull/4078 + +## 🤹 Moves 🤹 +### Changed +* Renamed `EFFECT_STEEL_BEAM` to `EFFECT_MAX_HP_50_RECOIL` by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4043 +### Fixed +* Fixed Pursuit's effect not working by @ZnogyroP @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4086 + +## 🎭 Abilities 🎭 +### Fixed +* Fixed Emergency Exit issues: + * Fixed rounding error that caused it to not switch out when odd-numbered HP was off by 1 by @SBird1337 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4040 + * Eg: going from 101 Max HP to 50 HP would've *not* cause it to switch out. + * Fixes Emergency Exit switching out even if the Pokémon was healed above the threshold before it would've triggered by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4041 + * Eg: going from 100 Max HP to 45 HP and eating a Sitrus Berry back to 55 HP would've cause it to switch out. + * Cleanup by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4100 +* Fixed Corrosion only working for status moves by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4037 +* Fixed Magic Guard not preventing Mind Blown recoil damage by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4043 + +## 🧶 Items 🧶 +### Fixed +* Fixed oversight causing chosen fossil to be lost if bag is full by @fakuzatsu in https://github.com/rh-hideout/pokeemerald-expansion/pull/3978 + * Fixed other Key Item script oversights by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4066 +* Fixed Life Orb causing damage on switch-in from Eject Pack and Red Card by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4038 +* Fixed Glimmering Charm sprite by @SonikkuA-DatH and @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4047 + +## 🤖 Battle AI 🤖 +### Fixed +* Fixed AI trying to switch into the same mon twice in the same turn by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4098 + +## 🧪 Test Runner 🧪 +### Added +* Corrosion tests by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4037 +* Pursuit/Tangling Hair interaction by @ZnogyroP @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4086 +### Changed +* Passing `KNOWN_FAILING` tests are now listed separately from `PASSED` tests by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4063 + +## New Contributors +* @Skyeward made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/4044 + +**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.7.2...expansion/1.7.3 + + diff --git a/docs/changelogs/template.md b/docs/changelogs/template.md index 607d69592a..f67520c388 100644 --- a/docs/changelogs/template.md +++ b/docs/changelogs/template.md @@ -3,7 +3,7 @@ ```md ## How to update - If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`. -- Once you have your remote set up, run the command `git pull RHH expansion/1.7.2`. +- Once you have your remote set up, run the command `git pull RHH expansion/1.Y.Z`. ``` ## 🌋 *IMPORTANT CHANGES* 🌋 @@ -88,6 +88,6 @@ ## New Contributors * Tony -**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.7.1...expansion/1.7.2 +**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.Y.Z...expansion/1.Y.Z - + diff --git a/graphics/pokedex/hgss/tileset_menu_list_DECA.png b/graphics/pokedex/hgss/tileset_menu_list_DECA.png index dc84b9ea38..f7f28de039 100644 Binary files a/graphics/pokedex/hgss/tileset_menu_list_DECA.png and b/graphics/pokedex/hgss/tileset_menu_list_DECA.png differ diff --git a/graphics/pokemon/archaludon/back.png b/graphics/pokemon/archaludon/back.png new file mode 100644 index 0000000000..4b2d3971ef Binary files /dev/null and b/graphics/pokemon/archaludon/back.png differ diff --git a/graphics/pokemon/archaludon/front.png b/graphics/pokemon/archaludon/front.png new file mode 100644 index 0000000000..69afa77cd5 Binary files /dev/null and b/graphics/pokemon/archaludon/front.png differ diff --git a/graphics/pokemon/archaludon/normal.pal b/graphics/pokemon/archaludon/normal.pal new file mode 100644 index 0000000000..cda3805601 --- /dev/null +++ b/graphics/pokemon/archaludon/normal.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +154 212 167 +136 73 83 +8 8 8 +189 126 61 +224 90 94 +240 225 123 +3 33 81 +105 99 119 +223 226 244 +252 253 253 +184 181 206 +49 93 142 +141 132 166 +18 56 103 +98 133 172 +132 109 58 diff --git a/graphics/pokemon/archaludon/shiny.pal b/graphics/pokemon/archaludon/shiny.pal new file mode 100644 index 0000000000..79e6173d9a --- /dev/null +++ b/graphics/pokemon/archaludon/shiny.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +154 212 167 +134 152 175 +8 8 8 +174 103 68 +217 226 242 +227 155 106 +17 62 103 +61 65 71 +131 147 160 +172 186 203 +107 116 127 +52 154 201 +83 90 99 +23 98 141 +118 190 214 +241 240 251 diff --git a/graphics/pokemon/gouging_fire/back.png b/graphics/pokemon/gouging_fire/back.png new file mode 100644 index 0000000000..8a0f79ef5c Binary files /dev/null and b/graphics/pokemon/gouging_fire/back.png differ diff --git a/graphics/pokemon/gouging_fire/front.png b/graphics/pokemon/gouging_fire/front.png new file mode 100644 index 0000000000..a399776f4d Binary files /dev/null and b/graphics/pokemon/gouging_fire/front.png differ diff --git a/graphics/pokemon/gouging_fire/normal.pal b/graphics/pokemon/gouging_fire/normal.pal new file mode 100644 index 0000000000..26c06154b0 --- /dev/null +++ b/graphics/pokemon/gouging_fire/normal.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +154 212 167 +76 44 11 +255 222 123 +8 8 8 +184 148 35 +249 199 40 +99 86 99 +210 212 207 +172 158 149 +53 140 58 +78 198 90 +136 17 14 +192 25 22 +151 77 32 +81 36 0 +207 114 56 diff --git a/graphics/pokemon/gouging_fire/shiny.pal b/graphics/pokemon/gouging_fire/shiny.pal new file mode 100644 index 0000000000..115a249070 --- /dev/null +++ b/graphics/pokemon/gouging_fire/shiny.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +154 212 167 +61 17 25 +249 189 116 +8 8 8 +199 100 36 +235 148 37 +99 86 99 +210 212 207 +172 158 149 +39 146 176 +70 220 210 +51 50 50 +93 92 92 +116 61 52 +61 17 25 +152 96 57 diff --git a/graphics/pokemon/hydrapple/back.png b/graphics/pokemon/hydrapple/back.png new file mode 100644 index 0000000000..f84712a508 Binary files /dev/null and b/graphics/pokemon/hydrapple/back.png differ diff --git a/graphics/pokemon/hydrapple/front.png b/graphics/pokemon/hydrapple/front.png new file mode 100644 index 0000000000..eecdea6d78 Binary files /dev/null and b/graphics/pokemon/hydrapple/front.png differ diff --git a/graphics/pokemon/hydrapple/icon.png b/graphics/pokemon/hydrapple/icon.png new file mode 100644 index 0000000000..352513f998 Binary files /dev/null and b/graphics/pokemon/hydrapple/icon.png differ diff --git a/graphics/pokemon/hydrapple/normal.pal b/graphics/pokemon/hydrapple/normal.pal new file mode 100644 index 0000000000..6fec9a7957 --- /dev/null +++ b/graphics/pokemon/hydrapple/normal.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +152 208 160 +88 56 32 +16 16 16 +152 24 16 +96 16 8 +32 72 24 +136 88 56 +216 80 64 +72 128 56 +248 160 160 +192 168 80 +40 96 8 +88 168 24 +160 216 72 +240 200 80 +32 88 8 diff --git a/graphics/pokemon/hydrapple/shiny.pal b/graphics/pokemon/hydrapple/shiny.pal new file mode 100644 index 0000000000..d3d0f68f92 --- /dev/null +++ b/graphics/pokemon/hydrapple/shiny.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +152 208 160 +88 56 32 +16 16 16 +216 136 0 +192 80 0 +32 72 24 +136 88 56 +248 208 16 +72 128 56 +248 232 144 +192 168 80 +40 96 8 +88 168 24 +160 216 72 +240 200 80 +32 88 8 diff --git a/graphics/pokemon/iron_boulder/back.png b/graphics/pokemon/iron_boulder/back.png new file mode 100644 index 0000000000..3c7cf2bf33 Binary files /dev/null and b/graphics/pokemon/iron_boulder/back.png differ diff --git a/graphics/pokemon/iron_boulder/front.png b/graphics/pokemon/iron_boulder/front.png new file mode 100644 index 0000000000..cf306085da Binary files /dev/null and b/graphics/pokemon/iron_boulder/front.png differ diff --git a/graphics/pokemon/iron_boulder/normal.pal b/graphics/pokemon/iron_boulder/normal.pal new file mode 100644 index 0000000000..d6a1404c07 --- /dev/null +++ b/graphics/pokemon/iron_boulder/normal.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +154 212 167 +8 8 8 +35 26 26 +62 53 44 +96 79 70 +113 92 83 +110 110 110 +245 245 245 +210 210 210 +193 99 32 +161 161 161 +254 172 53 +250 226 167 +123 51 30 +186 83 52 +242 112 4 diff --git a/graphics/pokemon/iron_boulder/shiny.pal b/graphics/pokemon/iron_boulder/shiny.pal new file mode 100644 index 0000000000..9ff1b42075 --- /dev/null +++ b/graphics/pokemon/iron_boulder/shiny.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +154 212 167 +8 8 8 +82 81 103 +149 148 170 +203 204 218 +249 248 250 +91 100 116 +228 232 235 +191 197 204 +193 99 32 +148 156 170 +254 172 53 +250 226 167 +97 95 107 +150 148 160 +209 204 217 diff --git a/graphics/pokemon/iron_crown/back.png b/graphics/pokemon/iron_crown/back.png new file mode 100644 index 0000000000..a3a5135503 Binary files /dev/null and b/graphics/pokemon/iron_crown/back.png differ diff --git a/graphics/pokemon/iron_crown/front.png b/graphics/pokemon/iron_crown/front.png new file mode 100644 index 0000000000..af3ab5bc9a Binary files /dev/null and b/graphics/pokemon/iron_crown/front.png differ diff --git a/graphics/pokemon/iron_crown/normal.pal b/graphics/pokemon/iron_crown/normal.pal new file mode 100644 index 0000000000..2df8f4b6a9 --- /dev/null +++ b/graphics/pokemon/iron_crown/normal.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +154 212 167 +0 163 172 +96 240 240 +210 248 248 +8 8 8 +179 146 50 +122 86 30 +130 138 163 +240 194 19 +1 68 78 +19 163 180 +59 202 202 +19 114 130 +60 68 78 +202 202 218 +162 202 218 diff --git a/graphics/pokemon/iron_crown/shiny.pal b/graphics/pokemon/iron_crown/shiny.pal new file mode 100644 index 0000000000..aaf724e6b4 --- /dev/null +++ b/graphics/pokemon/iron_crown/shiny.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +154 212 167 +0 163 172 +96 240 240 +210 248 248 +8 8 8 +171 172 186 +50 60 68 +130 138 163 +234 234 240 +96 96 104 +194 194 202 +248 248 251 +138 146 154 +60 68 78 +202 202 218 +162 202 218 diff --git a/graphics/pokemon/raging_bolt/back.png b/graphics/pokemon/raging_bolt/back.png new file mode 100644 index 0000000000..ae03ffab6b Binary files /dev/null and b/graphics/pokemon/raging_bolt/back.png differ diff --git a/graphics/pokemon/raging_bolt/front.png b/graphics/pokemon/raging_bolt/front.png new file mode 100644 index 0000000000..01e805fe98 Binary files /dev/null and b/graphics/pokemon/raging_bolt/front.png differ diff --git a/graphics/pokemon/raging_bolt/normal.pal b/graphics/pokemon/raging_bolt/normal.pal new file mode 100644 index 0000000000..a2feea268e --- /dev/null +++ b/graphics/pokemon/raging_bolt/normal.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +154 212 167 +80 79 87 +8 8 10 +8 8 8 +86 34 117 +123 98 19 +255 255 255 +114 81 156 +165 128 211 +192 154 43 +247 198 58 +149 149 149 +198 199 199 +197 64 62 +143 213 230 +118 104 104 diff --git a/graphics/pokemon/raging_bolt/shiny.pal b/graphics/pokemon/raging_bolt/shiny.pal new file mode 100644 index 0000000000..bec4088cca --- /dev/null +++ b/graphics/pokemon/raging_bolt/shiny.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +154 212 167 +94 82 82 +8 8 10 +8 8 8 +104 84 22 +140 73 2 +255 255 255 +182 149 49 +231 206 90 +201 118 6 +247 159 53 +175 160 153 +198 199 199 +11 125 232 +206 206 214 +118 104 104 diff --git a/include/battle.h b/include/battle.h index 5f18b5dcf2..1fd7c5db00 100644 --- a/include/battle.h +++ b/include/battle.h @@ -54,7 +54,8 @@ struct __attribute__((packed, aligned(2))) BattleMoveEffect const u8 *battleScript; u16 battleTvScore:3; u16 encourageEncore:1; - u16 flags:12; // coming soon... + u16 semiInvulnerableEffect:1; + u16 flags:11; // coming soon... }; #define GET_MOVE_BATTLESCRIPT(move) gBattleMoveEffects[gMovesInfo[move].effect].battleScript @@ -846,10 +847,10 @@ struct BattleScripting s32 bideDmg; u8 multihitString[6]; bool8 expOnCatch; - u8 twoTurnsMoveStringId; + u8 unused; u8 animArg1; u8 animArg2; - u16 tripleKickPower; + u16 savedStringId; u8 moveendState; u8 savedStatChanger; // For further use, if attempting to change stat two times(ex. Moody) u8 shiftSwitched; // When the game tells you the next enemy's pokemon and you switch. Option for noobs but oh well. diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 8cfa5b2cb6..cfdb791e0e 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -108,7 +108,7 @@ bool32 IsStatLoweringMoveEffect(u32 moveEffect); bool32 IsMoveRedirectionPrevented(u32 move, u32 atkAbility); bool32 IsMoveEncouragedToHit(u32 battlerAtk, u32 battlerDef, u32 move); bool32 IsHazardMoveEffect(u32 moveEffect); -bool32 IsChargingMove(u32 battlerAtk, u32 effect); +bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, u32 move); void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove, s32 *score); bool32 ShouldSetSandstorm(u32 battler, u32 ability, u32 holdEffect); bool32 ShouldSetHail(u32 battler, u32 ability, u32 holdEffect); diff --git a/include/battle_anim.h b/include/battle_anim.h index 75cce62d2c..fafde10c5b 100644 --- a/include/battle_anim.h +++ b/include/battle_anim.h @@ -417,6 +417,7 @@ extern const struct OamData gOamData_AffineDouble_ObjNormal_64x64; extern const struct OamData gOamData_AffineDouble_ObjBlend_64x64; extern const struct OamData gOamData_AffineDouble_ObjBlend_64x32; extern const struct OamData gOamData_AffineDouble_ObjNormal_8x16; +extern const struct OamData gOamData_AffineDouble_ObjNormal_64x32; extern const struct OamData gOamData_AffineOff_ObjBlend_16x16; extern const struct OamData gOamData_AffineDouble_ObjBlend_16x16; extern const struct OamData gOamData_AffineNormal_ObjNormal_8x8; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 44db02987f..3ff275d3bf 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -611,7 +611,6 @@ extern const u8 BattleScript_EffectSleepTalk[]; extern const u8 BattleScript_EffectDestinyBond[]; extern const u8 BattleScript_EffectSpite[]; extern const u8 BattleScript_EffectHealBell[]; -extern const u8 BattleScript_EffectTripleKick[]; extern const u8 BattleScript_EffectMeanLook[]; extern const u8 BattleScript_EffectNightmare[]; extern const u8 BattleScript_EffectMinimize[]; diff --git a/include/battle_util.h b/include/battle_util.h index e9b0c6f472..02eec4870f 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -225,6 +225,7 @@ bool32 MoveHasMoveEffect(u32 move, u32 moveEffect); bool32 MoveHasMoveEffectWithChance(u32 move, u32 moveEffect, u32 chance); bool32 MoveHasMoveEffectSelf(u32 move, u32 moveEffect); bool32 MoveHasMoveEffectSelfArg(u32 move, u32 moveEffect, u32 argument); +bool32 MoveHasChargeTurnMoveEffect(u32 move); bool32 CanSleep(u32 battler); bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget); diff --git a/include/config/battle.h b/include/config/battle.h index 49b0c5a073..163e4c0f08 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -179,15 +179,15 @@ // Var Settings // To use the following features in scripting, replace the 0s with the var ID you're assigning it to. -// Eg: Replace with VAR_UNUSED_0x40F7 so you can use VAR_TERRAIN for that feature. -#define VAR_TERRAIN 0 // If this var has a value, assigning a STATUS_FIELD_xx_TERRAIN to it before battle causes the battle to start with that terrain active +// Eg: Replace with VAR_UNUSED_0x40F7 so you can use B_VAR_TERRAIN for that feature. +#define B_VAR_TERRAIN 0 // If this var has a value, assigning a STATUS_FIELD_xx_TERRAIN to it before battle causes the battle to start with that terrain active. +#define B_VAR_TERRAIN_TIMER 0 // If this var has a value greater or equal than 1 field terrains will last that number of turns, otherwise they will last until they're overwritten. #define B_VAR_WILD_AI_FLAGS 0 // If not 0, you can use this var to add to default wild AI flags. NOT usable with flags above (1 << 15) // Sky Battles -#define B_FLAG_SKY_BATTLE 0 // If this flag has a value, the player will be able to engage in scripted Sky Battles. -#define B_VAR_SKY_BATTLE 0 // If this var has a value, the game will remember the positions of Pokémon used in Sky Battles. - -#define B_SKY_BATTLE_STRICT_ELIGIBILITY FALSE //If TRUE, Sky Battles will use the eligibility from Pokémon XY. If FALSE, all Flying-types or Pokémon with Levitate are allowed. +#define B_FLAG_SKY_BATTLE 0 // If this flag has a value, the player will be able to engage in scripted Sky Battles. +#define B_VAR_SKY_BATTLE 0 // If this var has a value, the game will remember the positions of Pokémon used in Sky Battles. +#define B_SKY_BATTLE_STRICT_ELIGIBILITY FALSE // If TRUE, Sky Battles will use the eligibility from Pokémon XY. If FALSE, all Flying-types or Pokémon with Levitate are allowed. // Flag and Var settings #define B_RESET_FLAGS_VARS_AFTER_WHITEOUT TRUE // If TRUE, Overworld_ResetBattleFlagsAndVars will reset battle-related Flags and Vars when the player whites out. diff --git a/include/constants/battle.h b/include/constants/battle.h index ddaa79cbc9..a353fbb357 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -129,7 +129,7 @@ #define STATUS2_FLINCHED (1 << 3) #define STATUS2_UPROAR (1 << 4 | 1 << 5 | 1 << 6) #define STATUS2_UPROAR_TURN(num) ((num) << 4) -#define STATUS2_UNUSED (1 << 7) +#define STATUS2_TORMENT (1 << 7) #define STATUS2_BIDE (1 << 8 | 1 << 9) #define STATUS2_BIDE_TURN(num) (((num) << 8) & STATUS2_BIDE) #define STATUS2_LOCK_CONFUSE (1 << 10 | 1 << 11) // e.g. Thrash @@ -139,7 +139,7 @@ #define STATUS2_POWDER (1 << 14) #define STATUS2_INFATUATION (1 << 16 | 1 << 17 | 1 << 18 | 1 << 19) // 4 bits, one for every battler #define STATUS2_INFATUATED_WITH(battler) (gBitTable[battler] << 16) -#define STATUS2_FOCUS_ENERGY (1 << 20) +#define STATUS2_DEFENSE_CURL (1 << 20) #define STATUS2_TRANSFORMED (1 << 21) #define STATUS2_RECHARGE (1 << 22) #define STATUS2_RAGE (1 << 23) @@ -149,8 +149,9 @@ #define STATUS2_NIGHTMARE (1 << 27) #define STATUS2_CURSED (1 << 28) #define STATUS2_FORESIGHT (1 << 29) -#define STATUS2_DEFENSE_CURL (1 << 30) -#define STATUS2_TORMENT (1 << 31) +#define STATUS2_DRAGON_CHEER (1 << 30) +#define STATUS2_FOCUS_ENERGY (1 << 31) +#define STATUS2_FOCUS_ENERGY_ANY (STATUS2_DRAGON_CHEER | STATUS2_FOCUS_ENERGY) #define STATUS3_LEECHSEED_BATTLER (1 << 0 | 1 << 1) // The battler to receive HP from Leech Seed #define STATUS3_LEECHSEED (1 << 2) diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index ddd7b37843..b085d305ab 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -414,8 +414,8 @@ // Below are used by AnimTask_ShakeMon2 and AnimTask_SetGrayscaleOrOriginalPal #define ANIM_PLAYER_LEFT (MAX_BATTLERS_COUNT + 0) -#define ANIM_PLAYER_RIGHT (MAX_BATTLERS_COUNT + 1) -#define ANIM_OPPONENT_LEFT (MAX_BATTLERS_COUNT + 2) +#define ANIM_OPPONENT_LEFT (MAX_BATTLERS_COUNT + 1) +#define ANIM_PLAYER_RIGHT (MAX_BATTLERS_COUNT + 2) #define ANIM_OPPONENT_RIGHT (MAX_BATTLERS_COUNT + 3) #define ANIM_ATTACKER_FORCE (MAX_BATTLERS_COUNT + 4) diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 4b03a2eef1..7390f3e05b 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -117,10 +117,8 @@ enum { EFFECT_BELLY_DRUM, EFFECT_PSYCH_UP, EFFECT_MIRROR_COAT, - EFFECT_SKULL_BASH, EFFECT_EARTHQUAKE, EFFECT_FUTURE_SIGHT, - EFFECT_GUST, EFFECT_SOLAR_BEAM, EFFECT_THUNDER, EFFECT_TELEPORT, @@ -172,7 +170,6 @@ enum { EFFECT_WEATHER_BALL, EFFECT_TICKLE, EFFECT_COSMIC_POWER, - EFFECT_SKY_UPPERCUT, EFFECT_BULK_UP, EFFECT_WATER_SPORT, EFFECT_CALM_MIND, @@ -318,7 +315,6 @@ enum { EFFECT_BOLT_BEAK, EFFECT_SKY_DROP, EFFECT_EXPANDING_FORCE, - EFFECT_METEOR_BEAM, EFFECT_RISING_VOLTAGE, EFFECT_BEAK_BLAST, EFFECT_COURT_CHANGE, @@ -352,6 +348,7 @@ enum { EFFECT_RAIN_ALWAYS_HIT, // Unlike EFFECT_THUNDER, it doesn't get its accuracy reduced under sun. EFFECT_SHED_TAIL, EFFECT_UPPER_HAND, + EFFECT_DRAGON_CHEER, NUM_BATTLE_MOVE_EFFECTS, }; diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 4e653cf9e6..6d635fe848 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -6,10 +6,10 @@ #define sBIDE_DMG (gBattleScripting + 0x04) // bideDmg #define sMULTIHIT_STRING (gBattleScripting + 0x08) // multihitString #define sEXP_CATCH (gBattleScripting + 0x0E) // expOnCatch -#define sTWOTURN_STRINGID (gBattleScripting + 0x0F) // twoTurnsMoveStringId +#define sUNUSED (gBattleScripting + 0x0F) // unused #define sB_ANIM_ARG1 (gBattleScripting + 0x10) // animArg1 #define sB_ANIM_ARG2 (gBattleScripting + 0x11) // animArg2 -#define sTRIPLE_KICK_POWER (gBattleScripting + 0x12) // tripleKickPower +#define sSAVED_STRINID (gBattleScripting + 0x12) // savedStringId #define sMOVEEND_STATE (gBattleScripting + 0x14) // moveendState #define sSAVED_STAT_CHANGER (gBattleScripting + 0x15) // savedStatChanger #define sSHIFT_SWITCHED (gBattleScripting + 0x16) // shiftSwitched @@ -181,7 +181,7 @@ #define VARIOUS_TRY_FAIRY_LOCK 89 #define VARIOUS_JUMP_IF_NO_ALLY 90 #define VARIOUS_POISON_TYPE_IMMUNITY 91 -#define VARIOUS_JUMP_IF_NO_HOLD_EFFECT 92 +#define VARIOUS_JUMP_IF_HOLD_EFFECT 92 #define VARIOUS_INFATUATE_WITH_BATTLER 93 #define VARIOUS_SET_LAST_USED_ITEM 94 #define VARIOUS_PARALYZE_TYPE_IMMUNITY 95 @@ -220,25 +220,24 @@ #define VARIOUS_SET_SKY_DROP 128 #define VARIOUS_CLEAR_SKY_DROP 129 #define VARIOUS_SKY_DROP_YAWN 130 -#define VARIOUS_JUMP_IF_HOLD_EFFECT 131 -#define VARIOUS_CURE_CERTAIN_STATUSES 132 -#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 133 -#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 134 -#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 135 -#define VARIOUS_SAVE_BATTLER_ITEM 136 -#define VARIOUS_RESTORE_BATTLER_ITEM 137 -#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 138 -#define VARIOUS_SET_BEAK_BLAST 139 -#define VARIOUS_SWAP_SIDE_STATUSES 140 -#define VARIOUS_SWAP_STATS 141 -#define VARIOUS_TEATIME_INVUL 142 -#define VARIOUS_TEATIME_TARGETS 143 -#define VARIOUS_TRY_WIND_RIDER_POWER 144 -#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 145 -#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 146 -#define VARIOUS_STORE_HEALING_WISH 147 -#define VARIOUS_HIT_SWITCH_TARGET_FAILED 148 -#define VARIOUS_TRY_REVIVAL_BLESSING 149 +#define VARIOUS_CURE_CERTAIN_STATUSES 131 +#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 132 +#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 133 +#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 134 +#define VARIOUS_SAVE_BATTLER_ITEM 135 +#define VARIOUS_RESTORE_BATTLER_ITEM 136 +#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 137 +#define VARIOUS_SET_BEAK_BLAST 138 +#define VARIOUS_SWAP_SIDE_STATUSES 139 +#define VARIOUS_SWAP_STATS 140 +#define VARIOUS_TEATIME_INVUL 141 +#define VARIOUS_TEATIME_TARGETS 142 +#define VARIOUS_TRY_WIND_RIDER_POWER 143 +#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 144 +#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 145 +#define VARIOUS_STORE_HEALING_WISH 146 +#define VARIOUS_HIT_SWITCH_TARGET_FAILED 147 +#define VARIOUS_TRY_REVIVAL_BLESSING 148 // Cmd_manipulatedamage #define DMG_CHANGE_SIGN 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index dc971b7f21..852e9a26c6 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -697,13 +697,14 @@ #define STRINGID_THESWAMPDISAPPEARED 695 #define STRINGID_PKMNTELLCHILLINGRECEPTIONJOKE 696 #define STRINGID_HOSPITALITYRESTORATION 697 -#define STRINGID_ELECTROSHOCKCHARGING 698 +#define STRINGID_ELECTROSHOTCHARGING 698 #define STRINGID_ITEMWASUSEDUP 699 #define STRINGID_ATTACKERLOSTITSTYPE 700 #define STRINGID_SHEDITSTAIL 701 -#define STRINGID_SUPERSWEETAROMAWAFTS 702 +#define STRINGID_CLOAKEDINAHARSHLIGHT 702 +#define STRINGID_SUPERSWEETAROMAWAFTS 703 -#define BATTLESTRINGS_COUNT 703 +#define BATTLESTRINGS_COUNT 704 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, @@ -747,22 +748,6 @@ #define B_MSG_LEECH_SEED_DRAIN 3 #define B_MSG_LEECH_SEED_OOZE 4 -// gFirstTurnOfTwoStringIds -#define B_MSG_TURN1_RAZOR_WIND 0 -#define B_MSG_TURN1_SOLAR_BEAM 1 -#define B_MSG_TURN1_SKULL_BASH 2 -#define B_MSG_TURN1_SKY_ATTACK 3 -#define B_MSG_TURN1_FLY 4 -#define B_MSG_TURN1_DIG 5 -#define B_MSG_TURN1_DIVE 6 -#define B_MSG_TURN1_BOUNCE 7 -#define B_MSG_TURN1_PHANTOM_FORCE 8 -#define B_MSG_TURN1_GEOMANCY 9 -#define B_MSG_TURN1_FREEZE_SHOCK 10 -#define B_MSG_TURN1_SKY_DROP 11 -#define B_MSG_TURN1_METEOR_BEAM 12 -#define B_MSG_TURN1_ELECTRO_SHOCK 13 - // gMoveWeatherChangeStringIds #define B_MSG_STARTED_RAIN 0 #define B_MSG_STARTED_DOWNPOUR 1 diff --git a/include/constants/expansion.h b/include/constants/expansion.h index 84a14db36b..7e6cf52b26 100644 --- a/include/constants/expansion.h +++ b/include/constants/expansion.h @@ -3,7 +3,7 @@ #define EXPANSION_VERSION_MAJOR 1 #define EXPANSION_VERSION_MINOR 7 -#define EXPANSION_VERSION_PATCH 2 +#define EXPANSION_VERSION_PATCH 3 // FALSE if this this version of Expansion is not a tagged commit, i.e. // it contains unreleased changes. diff --git a/include/data.h b/include/data.h index 09a1db7f7e..e80768f68c 100644 --- a/include/data.h +++ b/include/data.h @@ -123,6 +123,7 @@ extern const union AffineAnimCmd *const gAffineAnims_BattleSpriteContest[]; extern const union AnimCmd sAnim_GeneralFrame0[]; extern const union AnimCmd sAnim_GeneralFrame3[]; extern const union AnimCmd *const gAnims_MonPic[]; +extern const union AnimCmd *const sAnims_Trainer[]; extern const struct TrainerSprite gTrainerSprites[]; extern const struct TrainerBacksprite gTrainerBacksprites[]; diff --git a/include/global.h b/include/global.h index e39b880362..75dcf7d245 100644 --- a/include/global.h +++ b/include/global.h @@ -6,6 +6,7 @@ #include "config.h" // we need to define config before gba headers as print stuff needs the functions nulled before defines. #include "gba/gba.h" #include "fpmath.h" +#include "metaprogram.h" #include "constants/global.h" #include "constants/flags.h" #include "constants/vars.h" @@ -66,7 +67,7 @@ // Used in cases where division by 0 can occur in the retail version. // Avoids invalid opcodes on some emulators, and the otherwise UB. #ifdef UBFIX -#define SAFE_DIV(a, b) ((b) ? (a) / (b) : 0) +#define SAFE_DIV(a, b) (((b) != 0) ? (a) / (b) : 0) #else #define SAFE_DIV(a, b) ((a) / (b)) #endif @@ -124,22 +125,6 @@ #define NUM_FLAG_BYTES ROUND_BITS_TO_BYTES(FLAGS_COUNT) #define NUM_TRENDY_SAYING_BYTES ROUND_BITS_TO_BYTES(NUM_TRENDY_SAYINGS) -// Calls m0/m1/.../m8 depending on how many arguments are passed. -#define VARARG_8(m, ...) CAT(m, NARG_8(__VA_ARGS__))(__VA_ARGS__) - -// This returns the number of arguments passed to it (up to 8). -#define NARG_8(...) NARG_8_(_, ##__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0) -#define NARG_8_(_, a, b, c, d, e, f, g, h, N, ...) N - -#define CAT(a, b) CAT_(a, b) -#define CAT_(a, b) a ## b - -#define STR(...) STR_(__VA_ARGS__) -#define STR_(...) #__VA_ARGS__ - -// Converts a string to a compound literal, essentially making it a pointer to const u8 -#define COMPOUND_STRING(str) (const u8[]) _(str) - // This produces an error at compile-time if expr is zero. // It looks like file.c:line: size of array `id' is negative #define STATIC_ASSERT(expr, id) typedef char id[(expr) ? 1 : -1]; diff --git a/include/metaprogram.h b/include/metaprogram.h new file mode 100644 index 0000000000..eee79b73b1 --- /dev/null +++ b/include/metaprogram.h @@ -0,0 +1,134 @@ +/* Macros to aid with metaprogramming. */ +#ifndef METAPROGRAM_H +#define METAPROGRAM_H + +/* Calls m0/m1/.../m8 depending on how many arguments are passed. */ +#define VARARG_8(m, ...) CAT(m, NARG_8(__VA_ARGS__))(__VA_ARGS__) + +/* Returns the number of arguments passed to it (up to 8). */ +#define NARG_8(...) NARG_8_(_, ##__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#define NARG_8_(_, a, b, c, d, e, f, g, h, N, ...) N + +/* Expands 'a' and 'b' and then concatenates them. */ +#define CAT(a, b) CAT_(a, b) +#define CAT_(a, b) a ## b + +/* Expands '__VA_ARGS__' and then stringizes them. */ +#define STR(...) STR_(__VA_ARGS__) +#define STR_(...) #__VA_ARGS__ + +/* Converts a string to a compound literal, essentially making it a pointer to const u8 */ +#define COMPOUND_STRING(str) (const u8[]) _(str) + +/* Expands to the first/second/third/fourth argument. */ +#define FIRST(a, ...) a +#define SECOND(a, ...) __VA_OPT__(FIRST(__VA_ARGS__)) +#define THIRD(a, ...) __VA_OPT__(SECOND(__VA_ARGS__)) +#define FOURTH(a, ...) __VA_OPT__(THIRD(__VA_ARGS__)) + +/* Expands to everything but the first x arguments */ +#define EXCEPT_1(a, ...) __VA_OPT__(__VA_ARGS__) +#define EXCEPT_2(a, ...) __VA_OPT__(EXCEPT_1(__VA_ARGS__)) +#define EXCEPT_3(a, ...) __VA_OPT__(EXCEPT_2(__VA_ARGS__)) +#define EXCEPT_4(a, ...) __VA_OPT__(EXCEPT_3(__VA_ARGS__)) + +/* 'UNPACK (x, y, z)' expands to 'x, y, z'. + * Useful for passing arguments which may contain commas into a macro. */ +#define UNPACK(...) __VA_ARGS__ + +/* Expands to 'macro(...args, ...)'. */ +#define INVOKE_WITH(macro, args, ...) INVOKE_WITH_(macro, UNPACK args __VA_OPT__(, __VA_ARGS__)) +#define INVOKE_WITH_(macro, ...) macro(__VA_ARGS__) + +/* Recursive macros. + * Based on https://www.scs.stanford.edu/~dm/blog/va-opt.html + * + * Macros prefixed with R_ are recursive, to correctly expand them the + * top-level macro which references them should use 'RECURSIVELY' around + * them. 'RECURSIVELY' cannot be nested, hence the top-level macro must + * use it so that a recursive macro is able to reference another + * recursive macro. */ + +#define RECURSIVELY(...) RECURSIVELY_4(RECURSIVELY_4(RECURSIVELY_4(RECURSIVELY_4(__VA_ARGS__)))) +#define RECURSIVELY_4(...) RECURSIVELY_3(RECURSIVELY_3(RECURSIVELY_3(RECURSIVELY_3(__VA_ARGS__)))) +#define RECURSIVELY_3(...) RECURSIVELY_2(RECURSIVELY_2(RECURSIVELY_2(RECURSIVELY_2(__VA_ARGS__)))) +#define RECURSIVELY_2(...) RECURSIVELY_1(RECURSIVELY_1(RECURSIVELY_1(RECURSIVELY_1(__VA_ARGS__)))) +#define RECURSIVELY_1(...) __VA_ARGS__ + +/* Useful for deferring expansion until the second scan. See + * https://www.scs.stanford.edu/~dm/blog/va-opt.html for more info. */ +#define PARENS () + +/* Expands to 'macro(a)' for each 'a' in '...' */ +#define R_FOR_EACH(macro, ...) __VA_OPT__(R_FOR_EACH_(macro, __VA_ARGS__)) +#define R_FOR_EACH_(macro, a, ...) macro(a) __VA_OPT__(R_FOR_EACH_P PARENS (macro, __VA_ARGS__)) +#define R_FOR_EACH_P() R_FOR_EACH_ + +/* Expands to 'macro(...args, a)' for each 'a' in '...'. */ +#define R_FOR_EACH_WITH(macro, args, ...) __VA_OPT__(R_FOR_EACH_WITH_(macro, args, __VA_ARGS__)) +#define R_FOR_EACH_WITH_(macro, args, a, ...) INVOKE_WITH(macro, args, a) __VA_OPT__(R_FOR_EACH_WITH_P PARENS (macro, args, __VA_ARGS__)) +#define R_FOR_EACH_WITH_P() R_FOR_EACH_WITH_ + +/* Picks the xth VA_ARG if it exists, otherwise returns a default value */ +#define DEFAULT(_default, ...) FIRST(__VA_OPT__(__VA_ARGS__, ) _default) +#define DEFAULT_2(_default, ...) DEFAULT(_default __VA_OPT__(, SECOND(__VA_ARGS__))) +#define DEFAULT_3(_default, ...) DEFAULT(_default __VA_OPT__(, THIRD(__VA_ARGS__))) +#define DEFAULT_4(_default, ...) DEFAULT(_default __VA_OPT__(, FOURTH(__VA_ARGS__))) + +/* (Credit to MGriffin) A rather monstrous way of finding the set bit in a word. +Invalid input causes a compiler error. Sample: https://cexplore.karathan.at/z/x1hm7B */ +#define BIT_INDEX(n) \ + (n) == (1 << 0) ? 0 : \ + (n) == (1 << 1) ? 1 : \ + (n) == (1 << 2) ? 2 : \ + (n) == (1 << 3) ? 3 : \ + (n) == (1 << 4) ? 4 : \ + (n) == (1 << 5) ? 5 : \ + (n) == (1 << 6) ? 6 : \ + (n) == (1 << 7) ? 7 : \ + (n) == (1 << 8) ? 8 : \ + (n) == (1 << 9) ? 9 : \ + (n) == (1 << 10) ? 10 : \ + (n) == (1 << 11) ? 11 : \ + (n) == (1 << 12) ? 12 : \ + (n) == (1 << 13) ? 13 : \ + (n) == (1 << 14) ? 14 : \ + (n) == (1 << 15) ? 15 : \ + (n) == (1 << 16) ? 16 : \ + (n) == (1 << 17) ? 17 : \ + (n) == (1 << 18) ? 18 : \ + (n) == (1 << 19) ? 19 : \ + (n) == (1 << 20) ? 20 : \ + (n) == (1 << 21) ? 21 : \ + (n) == (1 << 22) ? 22 : \ + (n) == (1 << 23) ? 23 : \ + (n) == (1 << 24) ? 24 : \ + (n) == (1 << 25) ? 25 : \ + (n) == (1 << 26) ? 26 : \ + (n) == (1 << 27) ? 27 : \ + (n) == (1 << 28) ? 28 : \ + (n) == (1 << 29) ? 29 : \ + (n) == (1 << 30) ? 30 : \ + (n) == (1 << 31) ? 31 : \ + *(u32 *)NULL + +#define COMPRESS_BITS_0 0, 1 +#define COMPRESS_BITS_1 1, 1 +#define COMPRESS_BITS_2 2, 1 +#define COMPRESS_BITS_3 3, 1 +#define COMPRESS_BITS_4 4, 1 +#define COMPRESS_BITS_5 5, 1 +#define COMPRESS_BITS_6 6, 1 +#define COMPRESS_BITS_7 7, 1 + +/* Will try and compress a set bit (or up to three sequential bits) into a single byte +Input must be of the form (upper << lower) where upper can be up to 3, lower up to 31 */ +#define COMPRESS_BITS(_val) COMPRESS_BITS_STEP_2 _val +#define COMPRESS_BITS_STEP_2(_unpacked) COMPRESS_BITS_STEP_3(COMPRESS_BITS_## _unpacked) +#define COMPRESS_BITS_STEP_3(...) COMPRESS_BITS_STEP_4(__VA_ARGS__) +#define COMPRESS_BITS_STEP_4(upper, lower) (((upper % 8) << 5) + (BIT_INDEX(lower))) + +/* Will read a compressed bit stored by COMPRESS_BIT into a single byte */ +#define UNCOMPRESS_BITS(compressed) ((compressed >> 5) << (compressed & 0x1F)) + +#endif diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index a6d320b129..d3ef2dca0e 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -813,7 +813,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) RETURN_SCORE_MINUS(20); // if target off screen and we go first, don't use move - if (IsChargingMove(battlerAtk, moveEffect) && CanTargetFaintAi(battlerDef, battlerAtk)) + if (IsTwoTurnNotSemiInvulnerableMove(battlerAtk, move) && CanTargetFaintAi(battlerDef, battlerAtk)) RETURN_SCORE_MINUS(10); // check if negates type @@ -1477,7 +1477,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_FOCUS_ENERGY: - if (gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY) + if (gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY_ANY) ADJUST_SCORE(-10); break; case EFFECT_CONFUSE: @@ -1762,6 +1762,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_FOLLOW_ME: case EFFECT_HELPING_HAND: + case EFFECT_DRAGON_CHEER: if (!isDoubleBattle || !IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove) @@ -2781,6 +2782,13 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) RETURN_SCORE_PLUS(DECENT_EFFECT); // partner has earthquake or magnitude -> good idea to use magnet rise } break; + case EFFECT_DRAGON_CHEER: + if (gBattleMons[battlerAtkPartner].status2 & STATUS2_FOCUS_ENERGY_ANY || !HasDamagingMove(battlerAtkPartner)) + ADJUST_SCORE(-5); + else if (atkPartnerHoldEffect == HOLD_EFFECT_SCOPE_LENS + || IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_DRAGON) + || gMovesInfo[aiData->partnerMove].criticalHitStage > 0) + ADJUST_SCORE(GOOD_EFFECT); } // our effect relative to partner // consider global move effects @@ -3125,7 +3133,7 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) s32 score = 0; s32 leastHits = 1000; u16 *moves = GetMovesArray(battlerAtk); - bool8 isChargingMoveEffect[MAX_MON_MOVES]; + bool8 isTwoTurnNotSemiInvulnerableMove[MAX_MON_MOVES]; for (i = 0; i < MAX_MON_MOVES; i++) { @@ -3137,13 +3145,13 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) leastHits = noOfHits[i]; } viableMoveScores[i] = AI_SCORE_DEFAULT; - isChargingMoveEffect[i] = IsChargingMove(battlerAtk, gMovesInfo[moves[i]].effect); + isTwoTurnNotSemiInvulnerableMove[i] = IsTwoTurnNotSemiInvulnerableMove(battlerAtk, moves[i]); } else { noOfHits[i] = -1; viableMoveScores[i] = 0; - isChargingMoveEffect[i] = FALSE; + isTwoTurnNotSemiInvulnerableMove[i] = FALSE; } /* MgbaPrintf_("%S: required hits: %d Dmg: %d", gMoveNames[moves[i]], noOfHits[i], AI_DATA->simulatedDmg[battlerAtk][battlerDef][i]); @@ -3167,9 +3175,9 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) { multipleBestMoves = TRUE; // We need to make sure it's the current move which is objectively better. - if (isChargingMoveEffect[i] && !isChargingMoveEffect[currId]) + if (isTwoTurnNotSemiInvulnerableMove[i] && !isTwoTurnNotSemiInvulnerableMove[currId]) viableMoveScores[i] -= 3; - else if (!isChargingMoveEffect[i] && isChargingMoveEffect[currId]) + else if (!isTwoTurnNotSemiInvulnerableMove[i] && isTwoTurnNotSemiInvulnerableMove[currId]) viableMoveScores[currId] -= 3; switch (CompareMoveAccuracies(battlerAtk, battlerDef, currId, i)) diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 657ebb9c73..231ab442a5 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -2088,7 +2088,7 @@ static bool32 ShouldUseItem(u32 battler) break; case EFFECT_ITEM_SET_FOCUS_ENERGY: if (!gDisableStructs[battler].isFirstTurn - || gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY + || gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY_ANY || AI_OpponentCanFaintAiWithMod(battler, 0)) break; shouldUse = TRUE; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 4d3e69485d..fae5f684d7 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2016,17 +2016,14 @@ bool32 HasSnatchAffectedMove(u32 battler) CHECK_MOVE_FLAG(snatchAffected); } -bool32 IsChargingMove(u32 battlerAtk, u32 effect) +bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, u32 move) { - switch (effect) + switch (gMovesInfo[move].effect) { case EFFECT_SOLAR_BEAM: - if (AI_GetWeather(AI_DATA) & B_WEATHER_SUN) - return FALSE; - case EFFECT_SKULL_BASH: - case EFFECT_METEOR_BEAM: case EFFECT_TWO_TURNS_ATTACK: - return !(AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB); + return !(AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB + || (AI_GetWeather(AI_DATA) & gMovesInfo[move].argument)); default: return FALSE; } diff --git a/src/battle_anim.c b/src/battle_anim.c index c9e3789ee5..af1a5a262a 100644 --- a/src/battle_anim.c +++ b/src/battle_anim.c @@ -442,28 +442,44 @@ static void Cmd_unloadspritegfx(void) static u8 GetBattleAnimMoveTargets(u8 battlerArgIndex, u8 *targets) { u8 numTargets = 0; - int idx = 0; - u32 battler = gBattleAnimArgs[battlerArgIndex]; - switch (GetBattlerMoveTargetType(gBattleAnimAttacker, gAnimMoveIndex)) + u32 battlerAnimId = gBattleAnimArgs[battlerArgIndex]; // ANIM_xx input + u32 i; + u32 ignoredTgt = gBattlerAttacker; + u32 target = GetBattlerMoveTargetType(gBattleAnimAttacker, gAnimMoveIndex); + + switch (battlerAnimId) + { + case ANIM_ATTACKER: + case ANIM_ATK_PARTNER: + ignoredTgt = gBattlerTarget; + break; + case ANIM_TARGET: + case ANIM_DEF_PARTNER: + ignoredTgt = gBattlerAttacker; + break; + } + + switch (target) { case MOVE_TARGET_FOES_AND_ALLY: - if (IS_ALIVE_AND_PRESENT(BATTLE_PARTNER(BATTLE_OPPOSITE(battler)))) + if (battlerAnimId == ANIM_ATTACKER) { - targets[idx++] = BATTLE_PARTNER(BATTLE_OPPOSITE(battler)); - numTargets++; + targets[numTargets++] = gBattleAnimAttacker; } - // fallthrough - case MOVE_TARGET_BOTH: - if (IS_ALIVE_AND_PRESENT(battler)) + else { - targets[idx++] = battler; - numTargets++; + for (i = 0; i < gBattlersCount; i++) + { + if (i != gBattleAnimAttacker && IS_ALIVE_AND_PRESENT(i)) + targets[numTargets++] = i + MAX_BATTLERS_COUNT; // anim ids for battler ids + } } - battler = BATTLE_PARTNER(battler); - if (IS_ALIVE_AND_PRESENT(battler)) + break; + case MOVE_TARGET_BOTH: // all opponents + for (i = 0; i < gBattlersCount; i++) { - targets[idx++] = battler; - numTargets++; + if (i != ignoredTgt && !IsAlly(i, ignoredTgt) && IS_ALIVE_AND_PRESENT(i)) + targets[numTargets++] = i + MAX_BATTLERS_COUNT; } break; default: @@ -541,7 +557,7 @@ static void Cmd_createsprite(void) static void CreateSpriteOnTargets(const struct SpriteTemplate *template, u8 argVar, u8 battlerArgIndex, u8 argsCount, bool32 overwriteAnimTgt) { - u32 i; + u32 i, battler; u8 targets[MAX_BATTLERS_COUNT]; int ntargets; s16 subpriority; @@ -560,12 +576,13 @@ static void CreateSpriteOnTargets(const struct SpriteTemplate *template, u8 argV for (i = 0; i < ntargets; i++) { + battler = GetAnimBattlerId(targets[i]); if (overwriteAnimTgt) gBattleAnimArgs[battlerArgIndex] = targets[i]; if (CreateSpriteAndAnimate(template, - GetBattlerSpriteCoord(targets[i], BATTLER_COORD_X_2), - GetBattlerSpriteCoord(targets[i], BATTLER_COORD_Y_PIC_OFFSET), + GetBattlerSpriteCoord(battler, BATTLER_COORD_X_2), + GetBattlerSpriteCoord(battler, BATTLER_COORD_Y_PIC_OFFSET), subpriority) != MAX_SPRITES) // Don't increment the task count if the sprite couldn't be created(i.e. there are too many created sprites atm). { gAnimVisualTaskCount++; @@ -904,14 +921,20 @@ static void Cmd_monbg(void) u8 GetAnimBattlerId(u8 wantedBattler) { - if (wantedBattler == ANIM_ATTACKER) + switch (wantedBattler) + { + case ANIM_ATTACKER: + default: return gBattleAnimAttacker; - else if (wantedBattler == ANIM_TARGET) + case ANIM_TARGET: return gBattleAnimTarget; - else if (wantedBattler == ANIM_ATK_PARTNER) + case ANIM_ATK_PARTNER: return BATTLE_PARTNER(gBattleAnimAttacker); - else + case ANIM_DEF_PARTNER: return BATTLE_PARTNER(gBattleAnimTarget); + case ANIM_PLAYER_LEFT ... ANIM_OPPONENT_RIGHT: + return wantedBattler - MAX_BATTLERS_COUNT; + } } bool8 IsBattlerSpriteVisible(u8 battlerId) diff --git a/src/battle_anim_electric.c b/src/battle_anim_electric.c index 415f112d30..38ca948749 100644 --- a/src/battle_anim_electric.c +++ b/src/battle_anim_electric.c @@ -479,6 +479,17 @@ const struct SpriteTemplate gVoltTackleBoltSpriteTemplate = .callback = AnimVoltTackleBolt, }; +const struct SpriteTemplate gFairyLockChainsSpriteTemplate = +{ + .tileTag = ANIM_TAG_FAIRY_LOCK_CHAINS, + .paletteTag = ANIM_TAG_FAIRY_LOCK_CHAINS, + .oam = &gOamData_AffineOff_ObjNormal_64x32, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimVoltTackleBolt, +}; + const struct SpriteTemplate gGrowingShockWaveOrbSpriteTemplate = { .tileTag = ANIM_TAG_CIRCLE_OF_LIGHT, @@ -1190,11 +1201,20 @@ void AnimTask_VoltTackleBolt(u8 taskId) static bool8 CreateVoltTackleBolt(struct Task *task, u8 taskId) { - u8 spriteId = CreateSprite(&gVoltTackleBoltSpriteTemplate, task->data[3], task->data[5], 35); + u32 spriteId; + bool32 isFairyLock = (gAnimMoveIndex == MOVE_FAIRY_LOCK); + + if (isFairyLock) + spriteId = CreateSprite(&gFairyLockChainsSpriteTemplate, task->data[3], task->data[5] + 10, 35); + else + spriteId = CreateSprite(&gVoltTackleBoltSpriteTemplate, task->data[3], task->data[5], 35); + if (spriteId != MAX_SPRITES) { gSprites[spriteId].data[6] = taskId; gSprites[spriteId].data[7] = 7; + gSprites[spriteId].data[1] = isFairyLock ? 25 : 12; // How long the chains / bolts stay on screen. + gSprites[spriteId].data[2] = isFairyLock; // Whether to destroy the Oam Matrix. task->data[7]++; } @@ -1220,10 +1240,11 @@ static bool8 CreateVoltTackleBolt(struct Task *task, u8 taskId) static void AnimVoltTackleBolt(struct Sprite *sprite) { - if (++sprite->data[0] > 12) + if (++sprite->data[0] > sprite->data[1]) { gTasks[sprite->data[6]].data[sprite->data[7]]--; - FreeOamMatrix(sprite->oam.matrixNum); + if (!sprite->data[2]) + FreeOamMatrix(sprite->oam.matrixNum); DestroySprite(sprite); } } diff --git a/src/battle_anim_ghost.c b/src/battle_anim_ghost.c index 7bd6a0268e..6a12481531 100644 --- a/src/battle_anim_ghost.c +++ b/src/battle_anim_ghost.c @@ -474,8 +474,8 @@ void AnimShadowBall(struct Sprite *sprite) sprite->data[3] = gBattleAnimArgs[2]; sprite->data[4] = sprite->x << 4; sprite->data[5] = sprite->y << 4; - sprite->data[6] = ((oldPosX - sprite->x) << 4) / (gBattleAnimArgs[0] << 1); - sprite->data[7] = ((oldPosY - sprite->y) << 4) / (gBattleAnimArgs[0] << 1); + sprite->data[6] = SAFE_DIV(((oldPosX - sprite->x) << 4), (gBattleAnimArgs[0] << 1)); + sprite->data[7] = SAFE_DIV(((oldPosY - sprite->y) << 4), (gBattleAnimArgs[0] << 1)); sprite->callback = AnimShadowBall_Step; } diff --git a/src/battle_anim_mons.c b/src/battle_anim_mons.c index b01f59a260..542a179d6a 100644 --- a/src/battle_anim_mons.c +++ b/src/battle_anim_mons.c @@ -309,45 +309,51 @@ u8 GetBattlerYCoordWithElevation(u8 battlerId) u8 GetAnimBattlerSpriteId(u8 animBattler) { - u8 *sprites; + u32 partner; - if (animBattler == ANIM_ATTACKER) + switch (animBattler) { + case ANIM_ATTACKER: if (IsBattlerSpritePresent(gBattleAnimAttacker)) { - sprites = gBattlerSpriteIds; - return sprites[gBattleAnimAttacker]; + return gBattlerSpriteIds[gBattleAnimAttacker]; } else { return SPRITE_NONE; } - } - else if (animBattler == ANIM_TARGET) - { + break; + case ANIM_TARGET: if (IsBattlerSpritePresent(gBattleAnimTarget)) { - sprites = gBattlerSpriteIds; - return sprites[gBattleAnimTarget]; + return gBattlerSpriteIds[gBattleAnimTarget]; } else { return SPRITE_NONE; } - } - else if (animBattler == ANIM_ATK_PARTNER) - { + break; + case ANIM_ATK_PARTNER: if (!IsBattlerSpriteVisible(BATTLE_PARTNER(gBattleAnimAttacker))) return SPRITE_NONE; else return gBattlerSpriteIds[BATTLE_PARTNER(gBattleAnimAttacker)]; - } - else - { + break; + case ANIM_DEF_PARTNER: if (IsBattlerSpriteVisible(BATTLE_PARTNER(gBattleAnimTarget))) return gBattlerSpriteIds[BATTLE_PARTNER(gBattleAnimTarget)]; else return SPRITE_NONE; + break; + case ANIM_PLAYER_LEFT ... ANIM_OPPONENT_RIGHT: + partner = animBattler - MAX_BATTLERS_COUNT; + if (IsBattlerSpriteVisible(partner)) + return gBattlerSpriteIds[partner]; + else + return SPRITE_NONE; + break; + default: + return SPRITE_NONE; } } @@ -1025,8 +1031,8 @@ void InitSpriteDataForLinearTranslation(struct Sprite *sprite) { s16 x = (sprite->data[2] - sprite->data[1]) << 8; s16 y = (sprite->data[4] - sprite->data[3]) << 8; - sprite->data[1] = x / sprite->data[0]; - sprite->data[2] = y / sprite->data[0]; + sprite->data[1] = SAFE_DIV(x, sprite->data[0]); + sprite->data[2] = SAFE_DIV(y, sprite->data[0]); sprite->data[4] = 0; sprite->data[3] = 0; } diff --git a/src/battle_controller_link_opponent.c b/src/battle_controller_link_opponent.c index c9197bc5df..0c71d0a1da 100644 --- a/src/battle_controller_link_opponent.c +++ b/src/battle_controller_link_opponent.c @@ -462,9 +462,7 @@ static void LinkOpponentHandleDrawTrainerPic(u32 battler) } } - BtlController_HandleDrawTrainerPic(battler, trainerPicId, TRUE, - xPos, 40 + 4 * (8 - gTrainerSprites[trainerPicId].y_offset), - -1); + BtlController_HandleDrawTrainerPic(battler, trainerPicId, TRUE, xPos, 40, -1); } static void LinkOpponentHandleTrainerSlide(u32 battler) diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 4e272f4d0d..aeb13ec9a5 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -484,9 +484,7 @@ static void OpponentHandleDrawTrainerPic(u32 battler) xPos = 176; } - BtlController_HandleDrawTrainerPic(battler, trainerPicId, TRUE, - xPos, 40 + 4 * (8 - gTrainerSprites[trainerPicId].y_offset), - -1); + BtlController_HandleDrawTrainerPic(battler, trainerPicId, TRUE, xPos, 40, -1); } static void OpponentHandleTrainerSlide(u32 battler) diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index e70784e153..f1bdbdc668 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -1882,7 +1882,7 @@ static void PlayerHandleDrawTrainerPic(u32 battler) if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gPartnerTrainerId < TRAINER_PARTNER(PARTNER_NONE)) { xPos = 90; - yPos = (8 - gTrainerSprites[trainerPicId].y_offset) * 4 + 80; + yPos = 80; } else { diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index 6ede6587b2..b9dbfdd2c6 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -307,13 +307,13 @@ static void PlayerPartnerHandleDrawTrainerPic(u32 battler) { trainerPicId = gTrainers[gPartnerTrainerId].trainerPic; xPos = 60; - yPos = (8 - gTrainerSprites[trainerPicId].y_offset) * 4 + 80; + yPos = 80; } else { trainerPicId = GetFrontierTrainerFrontSpriteId(gPartnerTrainerId); xPos = 32; - yPos = (8 - gTrainerSprites[trainerPicId].y_offset) * 4 + 80; + yPos = 80; } // Use back pic only if the partner Steven or is custom. diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c index 36caa14c14..abadcc231c 100644 --- a/src/battle_controller_recorded_opponent.c +++ b/src/battle_controller_recorded_opponent.c @@ -420,9 +420,7 @@ static void RecordedOpponentHandleDrawTrainerPic(u32 battler) } } - BtlController_HandleDrawTrainerPic(battler, trainerPicId, TRUE, - xPos, 40 + 4 * (8 - gTrainerSprites[trainerPicId].y_offset), - -1); + BtlController_HandleDrawTrainerPic(battler, trainerPicId, TRUE, xPos, 40, -1); } static void RecordedOpponentHandleTrainerSlideBack(u32 battler) diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index b8322b3eaf..53860990c4 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -393,7 +393,7 @@ static void RecordedPlayerHandleDrawTrainerPic(u32 battler) if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { xPos = 90; - yPos = (8 - gTrainerSprites[trainerPicId].y_offset) * 4 + 80; + yPos = 80; } else { diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 27623c97bc..a7d9610dac 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -2542,10 +2542,7 @@ void BtlController_HandleTrainerSlide(u32 battler, u32 trainerPicId) { DecompressTrainerFrontPic(trainerPicId, battler); SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(battler)); - gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, - 176, - (8 - gTrainerSprites[trainerPicId].y_offset) * 4 + 40, - 30); + gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, 176, 40, 30); gSprites[gBattlerSpriteIds[battler]].oam.affineParam = trainerPicId; gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = IndexOfSpritePaletteTag(gTrainerSprites[trainerPicId].palette.tag); gSprites[gBattlerSpriteIds[battler]].x2 = 96; diff --git a/src/battle_debug.c b/src/battle_debug.c index b01ff6bd5e..cdda88e236 100644 --- a/src/battle_debug.c +++ b/src/battle_debug.c @@ -764,6 +764,22 @@ static void PutMovesPointsText(struct BattleDebugMenu *data) Free(text); } +static void CleanUpAiInfoWindow(u8 taskId) +{ + u32 i; + struct BattleDebugMenu *data = GetStructPtr(taskId); + + FreeMonIconPalettes(); + for (i = 0; i < MAX_BATTLERS_COUNT; i++) + { + if (data->spriteIds.aiIconSpriteIds[i] != 0xFF) + FreeAndDestroyMonIconSprite(&gSprites[data->spriteIds.aiIconSpriteIds[i]]); + } + FreeAndDestroyMonPicSprite(data->aiMonSpriteId); + ClearWindowTilemap(data->aiMovesWindowId); + RemoveWindow(data->aiMovesWindowId); +} + static void Task_ShowAiPoints(u8 taskId) { u32 i, count; @@ -784,6 +800,7 @@ static void Task_ShowAiPoints(u8 taskId) if (++data->aiBattlerId >= gBattlersCount) data->aiBattlerId = 0; } + data->battlerId = data->aiBattlerId; LoadMonIconPalettes(); for (count = 0, i = 0; i < MAX_BATTLERS_COUNT; i++) @@ -822,7 +839,27 @@ static void Task_ShowAiPoints(u8 taskId) break; // Input case 2: - if (JOY_NEW(SELECT_BUTTON | B_BUTTON)) + if (JOY_NEW(R_BUTTON) && IsDoubleBattle()) + { + CleanUpAiInfoWindow(taskId); + do { + data->battlerId++; + data->battlerId %= gBattlersCount; + } while (!IsBattlerAlive(data->battlerId)); + data->aiViewState = 0; + } + else if (JOY_NEW(L_BUTTON) && IsDoubleBattle()) + { + CleanUpAiInfoWindow(taskId); + do { + if (data->battlerId == 0) + data->battlerId = gBattlersCount - 1; + else + data->battlerId--; + } while (!IsBattlerAlive(data->battlerId) || !BattlerHasAi(data->battlerId)); + data->aiViewState = 0; + } + else if (JOY_NEW(SELECT_BUTTON | B_BUTTON)) { SwitchToDebugView(taskId); HideBg(1); @@ -1093,19 +1130,7 @@ static void SwitchToDebugViewFromAiParty(u8 taskId) static void SwitchToDebugView(u8 taskId) { - u32 i; - struct BattleDebugMenu *data = GetStructPtr(taskId); - - FreeMonIconPalettes(); - for (i = 0; i < MAX_BATTLERS_COUNT; i++) - { - if (data->spriteIds.aiIconSpriteIds[i] != 0xFF) - FreeAndDestroyMonIconSprite(&gSprites[data->spriteIds.aiIconSpriteIds[i]]); - } - FreeAndDestroyMonPicSprite(data->aiMonSpriteId); - ClearWindowTilemap(data->aiMovesWindowId); - RemoveWindow(data->aiMovesWindowId); - + CleanUpAiInfoWindow(taskId); gTasks[taskId].func = Task_DebugMenuProcessInput; } diff --git a/src/battle_main.c b/src/battle_main.c index 6f42f553f9..b41d6ea27d 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -322,85 +322,83 @@ const u8 gTypeNames[NUMBER_OF_MON_TYPES][TYPE_NAME_LENGTH + 1] = [TYPE_FAIRY] = _("Fairy"), }; -#define DEFAULT_MONEY 5 -#define DEFAULT_BALL ITEM_POKE_BALL - -#define TRAINER_CLASS(trainerClass, trainerName, trainerMoney, trainerBall) \ - [TRAINER_CLASS_##trainerClass] = \ - { \ - .name = _(trainerName), \ - .money = trainerMoney, \ - .ball = trainerBall, \ +// extra args are money and ball +#define TRAINER_CLASS(trainerClass, trainerName, ...) \ + [TRAINER_CLASS_##trainerClass] = \ + { \ + .name = _(trainerName), \ + .money = DEFAULT(5, __VA_ARGS__), \ + .ball = DEFAULT_2(ITEM_POKE_BALL, __VA_ARGS__), \ } const struct TrainerClass gTrainerClasses[TRAINER_CLASS_COUNT] = { - TRAINER_CLASS(PKMN_TRAINER_1, "{PKMN} TRAINER", DEFAULT_MONEY, DEFAULT_BALL), - TRAINER_CLASS(PKMN_TRAINER_2, "{PKMN} TRAINER", DEFAULT_MONEY, DEFAULT_BALL), - TRAINER_CLASS(HIKER, "HIKER", 10, DEFAULT_BALL), - TRAINER_CLASS(TEAM_AQUA, "TEAM AQUA", 5, DEFAULT_BALL), + TRAINER_CLASS(PKMN_TRAINER_1, "{PKMN} TRAINER"), + TRAINER_CLASS(PKMN_TRAINER_2, "{PKMN} TRAINER"), + TRAINER_CLASS(HIKER, "HIKER", 10), + TRAINER_CLASS(TEAM_AQUA, "TEAM AQUA"), TRAINER_CLASS(PKMN_BREEDER, "{PKMN} BREEDER", 10, B_TRAINER_CLASS_POKE_BALLS >= GEN_8 ? ITEM_HEAL_BALL : ITEM_FRIEND_BALL), TRAINER_CLASS(COOLTRAINER, "COOLTRAINER", 12, ITEM_ULTRA_BALL), - TRAINER_CLASS(BIRD_KEEPER, "BIRD KEEPER", 8, DEFAULT_BALL), + TRAINER_CLASS(BIRD_KEEPER, "BIRD KEEPER", 8), TRAINER_CLASS(COLLECTOR, "COLLECTOR", 15, ITEM_PREMIER_BALL), TRAINER_CLASS(SWIMMER_M, "SWIMMER♂", 2, ITEM_DIVE_BALL), - TRAINER_CLASS(TEAM_MAGMA, "TEAM MAGMA", 5, DEFAULT_BALL), - TRAINER_CLASS(EXPERT, "EXPERT", 10, DEFAULT_BALL), - TRAINER_CLASS(AQUA_ADMIN, "AQUA ADMIN", 10, DEFAULT_BALL), + TRAINER_CLASS(TEAM_MAGMA, "TEAM MAGMA"), + TRAINER_CLASS(EXPERT, "EXPERT", 10), + TRAINER_CLASS(AQUA_ADMIN, "AQUA ADMIN", 10), TRAINER_CLASS(BLACK_BELT, "BLACK BELT", 8, ITEM_ULTRA_BALL), TRAINER_CLASS(AQUA_LEADER, "AQUA LEADER", 20, ITEM_MASTER_BALL), - TRAINER_CLASS(HEX_MANIAC, "HEX MANIAC", 6, DEFAULT_BALL), - TRAINER_CLASS(AROMA_LADY, "AROMA LADY", 10, DEFAULT_BALL), - TRAINER_CLASS(RUIN_MANIAC, "RUIN MANIAC", 15, DEFAULT_BALL), - TRAINER_CLASS(INTERVIEWER, "INTERVIEWER", 12, DEFAULT_BALL), - TRAINER_CLASS(TUBER_F, "TUBER", 1, DEFAULT_BALL), - TRAINER_CLASS(TUBER_M, "TUBER", 1, DEFAULT_BALL), - TRAINER_CLASS(LADY, "LADY", 50, DEFAULT_BALL), - TRAINER_CLASS(BEAUTY, "BEAUTY", 20, DEFAULT_BALL), - TRAINER_CLASS(RICH_BOY, "RICH BOY", 50, DEFAULT_BALL), - TRAINER_CLASS(POKEMANIAC, "POKéMANIAC", 15, DEFAULT_BALL), - TRAINER_CLASS(GUITARIST, "GUITARIST", 8, DEFAULT_BALL), - TRAINER_CLASS(KINDLER, "KINDLER", 8, DEFAULT_BALL), - TRAINER_CLASS(CAMPER, "CAMPER", 4, DEFAULT_BALL), - TRAINER_CLASS(PICNICKER, "PICNICKER", 4, DEFAULT_BALL), - TRAINER_CLASS(BUG_MANIAC, "BUG MANIAC", 15, DEFAULT_BALL), - TRAINER_CLASS(PSYCHIC, "PSYCHIC", 6, DEFAULT_BALL), + TRAINER_CLASS(HEX_MANIAC, "HEX MANIAC", 6), + TRAINER_CLASS(AROMA_LADY, "AROMA LADY", 10), + TRAINER_CLASS(RUIN_MANIAC, "RUIN MANIAC", 15), + TRAINER_CLASS(INTERVIEWER, "INTERVIEWER", 12), + TRAINER_CLASS(TUBER_F, "TUBER", 1), + TRAINER_CLASS(TUBER_M, "TUBER", 1), + TRAINER_CLASS(LADY, "LADY", 50), + TRAINER_CLASS(BEAUTY, "BEAUTY", 20), + TRAINER_CLASS(RICH_BOY, "RICH BOY", 50), + TRAINER_CLASS(POKEMANIAC, "POKéMANIAC", 15), + TRAINER_CLASS(GUITARIST, "GUITARIST", 8), + TRAINER_CLASS(KINDLER, "KINDLER", 8), + TRAINER_CLASS(CAMPER, "CAMPER", 4), + TRAINER_CLASS(PICNICKER, "PICNICKER", 4), + TRAINER_CLASS(BUG_MANIAC, "BUG MANIAC", 15), + TRAINER_CLASS(PSYCHIC, "PSYCHIC", 6), TRAINER_CLASS(GENTLEMAN, "GENTLEMAN", 20, ITEM_LUXURY_BALL), TRAINER_CLASS(ELITE_FOUR, "ELITE FOUR", 25, ITEM_ULTRA_BALL), - TRAINER_CLASS(LEADER, "LEADER", 25, DEFAULT_BALL), - TRAINER_CLASS(SCHOOL_KID, "SCHOOL KID", 5, DEFAULT_BALL), - TRAINER_CLASS(SR_AND_JR, "SR. AND JR.", 4, DEFAULT_BALL), - TRAINER_CLASS(WINSTRATE, "WINSTRATE", 10, DEFAULT_BALL), - TRAINER_CLASS(POKEFAN, "POKéFAN", 20, DEFAULT_BALL), - TRAINER_CLASS(YOUNGSTER, "YOUNGSTER", 4, DEFAULT_BALL), - TRAINER_CLASS(CHAMPION, "CHAMPION", 50, DEFAULT_BALL), + TRAINER_CLASS(LEADER, "LEADER", 25), + TRAINER_CLASS(SCHOOL_KID, "SCHOOL KID"), + TRAINER_CLASS(SR_AND_JR, "SR. AND JR.", 4), + TRAINER_CLASS(WINSTRATE, "WINSTRATE", 10), + TRAINER_CLASS(POKEFAN, "POKéFAN", 20), + TRAINER_CLASS(YOUNGSTER, "YOUNGSTER", 4), + TRAINER_CLASS(CHAMPION, "CHAMPION", 50), TRAINER_CLASS(FISHERMAN, "FISHERMAN", 10, B_TRAINER_CLASS_POKE_BALLS >= GEN_8 ? ITEM_DIVE_BALL : ITEM_LURE_BALL), - TRAINER_CLASS(TRIATHLETE, "TRIATHLETE", 10, DEFAULT_BALL), - TRAINER_CLASS(DRAGON_TAMER, "DRAGON TAMER", 12, DEFAULT_BALL), - TRAINER_CLASS(NINJA_BOY, "NINJA BOY", 3, DEFAULT_BALL), - TRAINER_CLASS(BATTLE_GIRL, "BATTLE GIRL", 6, DEFAULT_BALL), - TRAINER_CLASS(PARASOL_LADY, "PARASOL LADY", 10, DEFAULT_BALL), + TRAINER_CLASS(TRIATHLETE, "TRIATHLETE", 10), + TRAINER_CLASS(DRAGON_TAMER, "DRAGON TAMER", 12), + TRAINER_CLASS(NINJA_BOY, "NINJA BOY", 3), + TRAINER_CLASS(BATTLE_GIRL, "BATTLE GIRL", 6), + TRAINER_CLASS(PARASOL_LADY, "PARASOL LADY", 10), TRAINER_CLASS(SWIMMER_F, "SWIMMER♀", 2, ITEM_DIVE_BALL), - TRAINER_CLASS(TWINS, "TWINS", 3, DEFAULT_BALL), - TRAINER_CLASS(SAILOR, "SAILOR", 8, DEFAULT_BALL), - TRAINER_CLASS(COOLTRAINER_2, "COOLTRAINER", DEFAULT_MONEY, ITEM_ULTRA_BALL), - TRAINER_CLASS(MAGMA_ADMIN, "MAGMA ADMIN", 10, DEFAULT_BALL), - TRAINER_CLASS(RIVAL, "{PKMN} TRAINER", 15, DEFAULT_BALL), - TRAINER_CLASS(BUG_CATCHER, "BUG CATCHER", 4, DEFAULT_BALL), - TRAINER_CLASS(PKMN_RANGER, "{PKMN} RANGER", 12, DEFAULT_BALL), + TRAINER_CLASS(TWINS, "TWINS", 3), + TRAINER_CLASS(SAILOR, "SAILOR", 8), + TRAINER_CLASS(COOLTRAINER_2, "COOLTRAINER", 5, ITEM_ULTRA_BALL), + TRAINER_CLASS(MAGMA_ADMIN, "MAGMA ADMIN", 10), + TRAINER_CLASS(RIVAL, "{PKMN} TRAINER", 15), + TRAINER_CLASS(BUG_CATCHER, "BUG CATCHER", 4), + TRAINER_CLASS(PKMN_RANGER, "{PKMN} RANGER", 12), TRAINER_CLASS(MAGMA_LEADER, "MAGMA LEADER", 20, ITEM_MASTER_BALL), - TRAINER_CLASS(LASS, "LASS", 4, DEFAULT_BALL), - TRAINER_CLASS(YOUNG_COUPLE, "YOUNG COUPLE", 8, DEFAULT_BALL), - TRAINER_CLASS(OLD_COUPLE, "OLD COUPLE", 10, DEFAULT_BALL), - TRAINER_CLASS(SIS_AND_BRO, "SIS AND BRO", 3, DEFAULT_BALL), - TRAINER_CLASS(SALON_MAIDEN, "SALON MAIDEN", DEFAULT_MONEY, DEFAULT_BALL), - TRAINER_CLASS(DOME_ACE, "DOME ACE", DEFAULT_MONEY, DEFAULT_BALL), - TRAINER_CLASS(PALACE_MAVEN, "PALACE MAVEN", DEFAULT_MONEY, DEFAULT_BALL), - TRAINER_CLASS(ARENA_TYCOON, "ARENA TYCOON", DEFAULT_MONEY, DEFAULT_BALL), - TRAINER_CLASS(FACTORY_HEAD, "FACTORY HEAD", DEFAULT_MONEY, DEFAULT_BALL), - TRAINER_CLASS(PIKE_QUEEN, "PIKE QUEEN", DEFAULT_MONEY, DEFAULT_BALL), - TRAINER_CLASS(PYRAMID_KING, "PYRAMID KING", DEFAULT_MONEY, DEFAULT_BALL), - TRAINER_CLASS(RS_PROTAG, "{PKMN} TRAINER", DEFAULT_MONEY, DEFAULT_BALL), + TRAINER_CLASS(LASS, "LASS", 4), + TRAINER_CLASS(YOUNG_COUPLE, "YOUNG COUPLE", 8), + TRAINER_CLASS(OLD_COUPLE, "OLD COUPLE", 10), + TRAINER_CLASS(SIS_AND_BRO, "SIS AND BRO", 3), + TRAINER_CLASS(SALON_MAIDEN, "SALON MAIDEN"), + TRAINER_CLASS(DOME_ACE, "DOME ACE"), + TRAINER_CLASS(PALACE_MAVEN, "PALACE MAVEN"), + TRAINER_CLASS(ARENA_TYCOON, "ARENA TYCOON"), + TRAINER_CLASS(FACTORY_HEAD, "FACTORY HEAD"), + TRAINER_CLASS(PIKE_QUEEN, "PIKE QUEEN"), + TRAINER_CLASS(PYRAMID_KING, "PYRAMID KING"), + TRAINER_CLASS(RS_PROTAG, "{PKMN} TRAINER"), }; static void (* const sTurnActionsFuncsTable[])(void) = @@ -3110,7 +3108,7 @@ void SwitchInClearSetData(u32 battler) } if (gMovesInfo[gCurrentMove].effect == EFFECT_BATON_PASS) { - gBattleMons[battler].status2 &= (STATUS2_CONFUSION | STATUS2_FOCUS_ENERGY | STATUS2_SUBSTITUTE | STATUS2_ESCAPE_PREVENTION | STATUS2_CURSED); + gBattleMons[battler].status2 &= (STATUS2_CONFUSION | STATUS2_FOCUS_ENERGY_ANY | STATUS2_SUBSTITUTE | STATUS2_ESCAPE_PREVENTION | STATUS2_CURSED); gStatuses3[battler] &= (STATUS3_LEECHSEED_BATTLER | STATUS3_LEECHSEED | STATUS3_ALWAYS_HITS | STATUS3_PERISH_SONG | STATUS3_ROOTED | STATUS3_GASTRO_ACID | STATUS3_EMBARGO | STATUS3_TELEKINESIS | STATUS3_MAGNET_RISE | STATUS3_HEAL_BLOCK | STATUS3_AQUA_RING | STATUS3_POWER_TRICK); diff --git a/src/battle_message.c b/src/battle_message.c index 28318b3850..97ea9794a5 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -151,6 +151,7 @@ static const u8 sText_PkmnWhippedWhirlwind[] = _("{B_ATK_NAME_WITH_PREFIX} whipp static const u8 sText_PkmnTookSunlight[] = _("{B_ATK_NAME_WITH_PREFIX} took\nin sunlight!"); static const u8 sText_PkmnLoweredHead[] = _("{B_ATK_NAME_WITH_PREFIX} lowered\nits head!"); static const u8 sText_PkmnIsGlowing[] = _("{B_ATK_NAME_WITH_PREFIX} is glowing!"); +static const u8 sText_PkmnIsCloakedInAHarshLight[] = _("{B_ATK_NAME_WITH_PREFIX} became\ncloaked in a harsh light!"); static const u8 sText_PkmnFlewHigh[] = _("{B_ATK_NAME_WITH_PREFIX} flew\nup high!"); static const u8 sText_PkmnDugHole[] = _("{B_ATK_NAME_WITH_PREFIX} dug a hole!"); static const u8 sText_PkmnHidUnderwater[] = _("{B_ATK_NAME_WITH_PREFIX} hid\nunderwater!"); @@ -166,7 +167,7 @@ static const u8 sText_PkmnFreedFrom[] = _("{B_ATK_NAME_WITH_PREFIX} was freed\nf static const u8 sText_PkmnCrashed[] = _("{B_ATK_NAME_WITH_PREFIX} kept going\nand crashed!"); const u8 gText_PkmnShroudedInMist[] = _("{B_ATK_PREFIX2} became\nshrouded in MIST!"); static const u8 sText_PkmnProtectedByMist[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is protected\nby MIST!"); -const u8 gText_PkmnGettingPumped[] = _("{B_ATK_NAME_WITH_PREFIX} is getting\npumped!"); +const u8 gText_PkmnGettingPumped[] = _("{B_DEF_NAME_WITH_PREFIX} is getting\npumped!"); static const u8 sText_PkmnHitWithRecoil[] = _("{B_ATK_NAME_WITH_PREFIX} is hit\nwith recoil!"); static const u8 sText_PkmnProtectedItself2[] = _("{B_ATK_NAME_WITH_PREFIX} protected\nitself!"); static const u8 sText_PkmnBuffetedBySandstorm[] = _("{B_ATK_NAME_WITH_PREFIX} is buffeted\nby the sandstorm!"); @@ -834,7 +835,7 @@ static const u8 sText_TheSeaOfFireDisappeared[] = _("The sea of fire around {B_A static const u8 sText_SwampEnvelopedSide[] = _("A swamp enveloped\n{B_DEF_TEAM2} team!"); static const u8 sText_TheSwampDisappeared[] = _("The swamp around {B_ATK_TEAM2}\nteam disappeared!"); static const u8 sText_HospitalityRestoration[] = _("The {B_ATK_PARTNER_NAME} drank down all\nthe matcha that Sinistcha made!"); -static const u8 sText_ElectroShockCharging[] = _("{B_ATK_NAME_WITH_PREFIX} absorbed\nelectricity!"); +static const u8 sText_ElectroShotCharging[] = _("{B_ATK_NAME_WITH_PREFIX} absorbed\nelectricity!"); static const u8 sText_ItemWasUsedUp[] = _("The {B_LAST_ITEM}\nwas used up..."); static const u8 sText_AttackerLostItsType[] = _("{B_ATK_NAME_WITH_PREFIX} lost\nits {B_BUFF1} type!"); static const u8 sText_ShedItsTail[] = _("{B_ATK_NAME_WITH_PREFIX} shed its tail\nto create a decoy!"); @@ -844,7 +845,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { [STRINGID_SUPERSWEETAROMAWAFTS - BATTLESTRINGS_TABLE_START] = sText_SupersweetAromaWafts, [STRINGID_SHEDITSTAIL - BATTLESTRINGS_TABLE_START] = sText_ShedItsTail, - [STRINGID_ELECTROSHOCKCHARGING - BATTLESTRINGS_TABLE_START] = sText_ElectroShockCharging, + [STRINGID_ELECTROSHOTCHARGING - BATTLESTRINGS_TABLE_START] = sText_ElectroShotCharging, [STRINGID_HOSPITALITYRESTORATION - BATTLESTRINGS_TABLE_START] = sText_HospitalityRestoration, [STRINGID_THESWAMPDISAPPEARED - BATTLESTRINGS_TABLE_START] = sText_TheSwampDisappeared, [STRINGID_SWAMPENVELOPEDSIDE - BATTLESTRINGS_TABLE_START] = sText_SwampEnvelopedSide, @@ -1533,6 +1534,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP - BATTLESTRINGS_TABLE_START] = sText_TargetCoveredInStickyCandySyrup, [STRINGID_ITEMWASUSEDUP - BATTLESTRINGS_TABLE_START] = sText_ItemWasUsedUp, [STRINGID_ATTACKERLOSTITSTYPE - BATTLESTRINGS_TABLE_START] = sText_AttackerLostItsType, + [STRINGID_CLOAKEDINAHARSHLIGHT - BATTLESTRINGS_TABLE_START] = sText_PkmnIsCloakedInAHarshLight, }; const u16 gTrainerUsedItemStringIds[] = @@ -1758,25 +1760,6 @@ const u16 gStatDownStringIds[] = [B_MSG_STAT_FELL_EMPTY] = STRINGID_EMPTYSTRING3, }; -// Index read from sTWOTURN_STRINGID -const u16 gFirstTurnOfTwoStringIds[] = -{ - [B_MSG_TURN1_RAZOR_WIND] = STRINGID_PKMNWHIPPEDWHIRLWIND, - [B_MSG_TURN1_SOLAR_BEAM] = STRINGID_PKMNTOOKSUNLIGHT, - [B_MSG_TURN1_SKULL_BASH] = STRINGID_PKMNLOWEREDHEAD, - [B_MSG_TURN1_SKY_ATTACK] = STRINGID_PKMNISGLOWING, - [B_MSG_TURN1_FLY] = STRINGID_PKMNFLEWHIGH, - [B_MSG_TURN1_DIG] = STRINGID_PKMNDUGHOLE, - [B_MSG_TURN1_DIVE] = STRINGID_PKMNHIDUNDERWATER, - [B_MSG_TURN1_BOUNCE] = STRINGID_PKMNSPRANGUP, - [B_MSG_TURN1_PHANTOM_FORCE] = STRINGID_VANISHEDINSTANTLY, - [B_MSG_TURN1_GEOMANCY] = STRINGID_PKNMABSORBINGPOWER, - [B_MSG_TURN1_FREEZE_SHOCK] = STRINGID_CLOAKEDINAFREEZINGLIGHT, - [B_MSG_TURN1_SKY_DROP] = STRINGID_PKMNTOOKTARGETHIGH, - [B_MSG_TURN1_METEOR_BEAM] = STRINGID_METEORBEAMCHARGING, - [B_MSG_TURN1_ELECTRO_SHOCK] = STRINGID_ELECTROSHOCKCHARGING, -}; - // Index copied from move's index in sTrappingMoves const u16 gWrappedStringIds[NUM_TRAPPING_MOVES] = { diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ce846e0078..97c237fda7 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -498,7 +498,7 @@ static void Cmd_setdrainedhp(void); static void Cmd_statbuffchange(void); static void Cmd_normalisebuffs(void); static void Cmd_setbide(void); -static void Cmd_unused0x8C(void); +static void Cmd_twoturnmoveschargestringandanimation(void); static void Cmd_setmultihitcounter(void); static void Cmd_initmultihitstring(void); static void Cmd_forcerandomswitch(void); @@ -556,7 +556,7 @@ static void Cmd_selectfirstvalidtarget(void); static void Cmd_trysetfutureattack(void); static void Cmd_trydobeatup(void); static void Cmd_setsemiinvulnerablebit(void); -static void Cmd_clearsemiinvulnerablebit(void); +static void Cmd_jumpifweathercheckchargeeffects(void); static void Cmd_setminimize(void); static void Cmd_sethail(void); static void Cmd_trymemento(void); @@ -757,7 +757,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_statbuffchange, //0x89 Cmd_normalisebuffs, //0x8A Cmd_setbide, //0x8B - Cmd_unused0x8C, //0x8C + Cmd_twoturnmoveschargestringandanimation, //0x8C Cmd_setmultihitcounter, //0x8D Cmd_initmultihitstring, //0x8E Cmd_forcerandomswitch, //0x8F @@ -815,7 +815,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_trysetfutureattack, //0xC3 Cmd_trydobeatup, //0xC4 Cmd_setsemiinvulnerablebit, //0xC5 - Cmd_clearsemiinvulnerablebit, //0xC6 + Cmd_jumpifweathercheckchargeeffects, //0xC6 Cmd_setminimize, //0xC7 Cmd_sethail, //0xC8 Cmd_trymemento, //0xC9 @@ -845,7 +845,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_unused2, //0xE1 Cmd_switchoutabilities, //0xE2 Cmd_jumpifhasnohp, //0xE3 - Cmd_jumpifnotcurrentmoveargtype, //0xE4 + Cmd_jumpifnotcurrentmoveargtype, //0xE4 Cmd_pickup, //0xE5 Cmd_unused3, //0xE6 Cmd_unused4, //0xE7 @@ -1906,6 +1906,7 @@ s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 rec else { critChance = 2 * ((gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY) != 0) + + 1 * ((gBattleMons[battlerAtk].status2 & STATUS2_DRAGON_CHEER) != 0) + gMovesInfo[gCurrentMove].criticalHitStage + (holdEffectAtk == HOLD_EFFECT_SCOPE_LENS) + 2 * (holdEffectAtk == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[battlerAtk].species == SPECIES_CHANSEY) @@ -2621,7 +2622,7 @@ static void Cmd_printstring(void) if (gBattleControllerExecFlags == 0) { - u16 id = cmd->id; + u16 id = (cmd->id == 0 ? gBattleScripting.savedStringId : cmd->id); gBattlescriptCurrInstr = cmd->nextInstr; PrepareStringBattle(id, gBattlerAttacker); @@ -2805,11 +2806,9 @@ void SetMoveEffect(bool32 primary, bool32 certain) // Just in case this flag is still set gBattleScripting.moveEffect &= ~MOVE_EFFECT_CERTAIN; - if ((battlerAbility == ABILITY_SHIELD_DUST - || GetBattlerHoldEffect(gEffectBattler, TRUE) == HOLD_EFFECT_COVERT_CLOAK) + if (!primary && affectsUser != MOVE_EFFECT_AFFECTS_USER && !(gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT) - && !primary - && (gBattleScripting.moveEffect <= MOVE_EFFECT_TRI_ATTACK || gBattleScripting.moveEffect >= MOVE_EFFECT_SMACK_DOWN)) // Exclude stat lowering effects + && (battlerAbility == ABILITY_SHIELD_DUST || GetBattlerHoldEffect(gEffectBattler, TRUE) == HOLD_EFFECT_COVERT_CLOAK)) { if (battlerAbility == ABILITY_SHIELD_DUST) RecordAbilityBattle(gEffectBattler, battlerAbility); @@ -8697,16 +8696,19 @@ static void Cmd_various(void) } return; } - case VARIOUS_JUMP_IF_NO_HOLD_EFFECT: + case VARIOUS_JUMP_IF_HOLD_EFFECT: { - VARIOUS_ARGS(u8 holdEffect, const u8 *jumpInstr); - if (GetBattlerHoldEffect(battler, TRUE) != cmd->holdEffect) + VARIOUS_ARGS(u8 holdEffect, const u8 *jumpInstr, u8 equal); + if ((GetBattlerHoldEffect(battler, TRUE) == cmd->holdEffect) == cmd->equal) { + if (cmd->equal) + gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM gBattlescriptCurrInstr = cmd->jumpInstr; } else { - gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM + if (!cmd->equal) + gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM gBattlescriptCurrInstr = cmd->nextInstr; } return; @@ -11693,8 +11695,15 @@ static void Cmd_setbide(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_unused0x8C(void) +static void Cmd_twoturnmoveschargestringandanimation(void) { + CMD_ARGS(const u8 *animationThenStringPtr); + + gBattleScripting.savedStringId = LOHALF(gMovesInfo[gCurrentMove].argument); + if (B_UPDATED_MOVE_DATA < GEN_5 || MoveHasChargeTurnMoveEffect(gCurrentMove)) + gBattlescriptCurrInstr = cmd->animationThenStringPtr; + else + gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_setmultihitcounter(void) @@ -12335,14 +12344,20 @@ static void Cmd_setfocusenergy(void) { CMD_ARGS(); - if (gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY) + if ((gMovesInfo[gCurrentMove].effect == EFFECT_DRAGON_CHEER && (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE) || (gAbsentBattlerFlags & gBitTable[gBattlerTarget]))) + || gBattleMons[gBattlerTarget].status2 & STATUS2_FOCUS_ENERGY_ANY) { gMoveResultFlags |= MOVE_RESULT_FAILED; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FOCUS_ENERGY_FAILED; } + else if (gMovesInfo[gCurrentMove].effect == EFFECT_DRAGON_CHEER && !IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_DRAGON)) + { + gBattleMons[gBattlerTarget].status2 |= STATUS2_DRAGON_CHEER; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_GETTING_PUMPED; + } else { - gBattleMons[gBattlerAttacker].status2 |= STATUS2_FOCUS_ENERGY; + gBattleMons[gBattlerTarget].status2 |= STATUS2_FOCUS_ENERGY; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_GETTING_PUMPED; } gBattlescriptCurrInstr = cmd->nextInstr; @@ -13643,36 +13658,36 @@ static void Cmd_trydobeatup(void) static void Cmd_setsemiinvulnerablebit(void) { - CMD_ARGS(); + CMD_ARGS(bool8 clear); - switch (gCurrentMove) + if (gBattleMoveEffects[gMovesInfo[gCurrentMove].effect].semiInvulnerableEffect == TRUE) { - case MOVE_FLY: - case MOVE_BOUNCE: - case MOVE_SKY_DROP: - gStatuses3[gBattlerAttacker] |= STATUS3_ON_AIR; - break; - case MOVE_DIG: - gStatuses3[gBattlerAttacker] |= STATUS3_UNDERGROUND; - break; - case MOVE_DIVE: - gStatuses3[gBattlerAttacker] |= STATUS3_UNDERWATER; - break; - case MOVE_PHANTOM_FORCE: - case MOVE_SHADOW_FORCE: - gStatuses3[gBattlerAttacker] |= STATUS3_PHANTOM_FORCE; - break; + u32 semiInvulnerableEffect = UNCOMPRESS_BITS(HIHALF(gMovesInfo[gCurrentMove].argument)); + if (cmd->clear) + gStatuses3[gBattlerAttacker] &= ~semiInvulnerableEffect; + else + gStatuses3[gBattlerAttacker] |= semiInvulnerableEffect; } gBattlescriptCurrInstr = cmd->nextInstr; } -static void Cmd_clearsemiinvulnerablebit(void) +static void Cmd_jumpifweathercheckchargeeffects(void) { - CMD_ARGS(); + CMD_ARGS(u8 battler, bool8 checkChargeTurnEffects, const u8 *jumpInstr); - gStatuses3[gBattlerAttacker] &= ~STATUS3_SEMI_INVULNERABLE; - gBattlescriptCurrInstr = cmd->nextInstr; + /* If this is NOT semi-invulnerable move and we don't have charge turn effects + yet to fire, we can fire the move right away so long as the weather matches + the argument and the battler is affected by it (not blocked by Cloud Nine etc) */ + if (gBattleMoveEffects[gMovesInfo[gCurrentMove].effect].semiInvulnerableEffect == FALSE + && !(cmd->checkChargeTurnEffects && MoveHasChargeTurnMoveEffect(gCurrentMove)) + && IsBattlerWeatherAffected(cmd->battler, HIHALF(gMovesInfo[gCurrentMove].argument))) + { + gBattleScripting.animTurn = 1; + gBattlescriptCurrInstr = cmd->jumpInstr; + } + else + gBattlescriptCurrInstr = cmd->nextInstr; } static void Cmd_setminimize(void) @@ -15549,23 +15564,6 @@ void BS_JumpIfMoreThanHalfHP(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_JumpIfHoldEffect(void) -{ - u8 battler = gBattlescriptCurrInstr[5]; - u16 holdEffect = T1_READ_16(gBattlescriptCurrInstr + 6); - - if (GetBattlerHoldEffect(battler, TRUE) == holdEffect) - { - gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 8); - } - else - { - RecordItemEffectBattle(battler, holdEffect); - gLastUsedItem = gBattleMons[battler].item; // For B_LAST_USED_ITEM - gBattlescriptCurrInstr += 12; - } -} - void BS_DoStockpileStatChangesWearOff(void) { NATIVE_ARGS(u8 battler, const u8 *statChangeInstr); @@ -16543,10 +16541,10 @@ void BS_TryUpperHand(void) { NATIVE_ARGS(const u8 *failInstr); - if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget) - || gChosenMoveByBattler[gBattlerTarget] == MOVE_NONE - || IS_MOVE_STATUS(gChosenMoveByBattler[gBattlerTarget]) - || GetChosenMovePriority(gBattlerTarget) < 1 || GetChosenMovePriority(gBattlerTarget) > 3) // Fails if priority is less than 1 or greater than 3, if target already moved, or if using a status + if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget) + || gChosenMoveByBattler[gBattlerTarget] == MOVE_NONE + || IS_MOVE_STATUS(gChosenMoveByBattler[gBattlerTarget]) + || GetChosenMovePriority(gBattlerTarget) < 1 || GetChosenMovePriority(gBattlerTarget) > 3) // Fails if priority is less than 1 or greater than 3, if target already moved, or if using a status gBattlescriptCurrInstr = cmd->failInstr; else gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_util.c b/src/battle_util.c index 4385dc3ecb..fe40102f6f 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3192,7 +3192,6 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType) gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_DESTINY_BOND; gStatuses3[gBattlerAttacker] &= ~STATUS3_GRUDGE; gStatuses4[gBattlerAttacker] &= ~ STATUS4_GLAIVE_RUSH; - gBattleScripting.tripleKickPower = 0; gBattleStruct->atkCancellerTracker++; break; case CANCELLER_SKY_DROP: @@ -3607,6 +3606,10 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType) gBattleStruct->beatUpSlot = 0; PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) } + else + { + gMultiHitCounter = 0; + } gBattleStruct->atkCancellerTracker++; break; case CANCELLER_END: @@ -4016,50 +4019,79 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 switch (caseID) { case ABILITYEFFECT_SWITCH_IN_TERRAIN: - gBattleScripting.battler = battler; - if (VarGet(VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY) { - u16 terrainFlags = VarGet(VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY; // only works for status flag (1 << 15) - gFieldStatuses = terrainFlags | STATUS_FIELD_TERRAIN_PERMANENT; // terrain is permanent - switch (VarGet(VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY) - { - case STATUS_FIELD_ELECTRIC_TERRAIN: - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; - break; - case STATUS_FIELD_MISTY_TERRAIN: - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; - break; - case STATUS_FIELD_GRASSY_TERRAIN: - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_GRASSY; - break; - case STATUS_FIELD_PSYCHIC_TERRAIN: - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC; - break; - } + u8 varTerrainTimer = VarGet(B_VAR_TERRAIN_TIMER); - BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); - effect++; + gBattleScripting.battler = battler; + if (VarGet(B_VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY) + { + u16 terrainFlags = VarGet(B_VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY; // only works for status flag (1 << 15) + + if (varTerrainTimer == 0) + { + gFieldStatuses = terrainFlags | STATUS_FIELD_TERRAIN_PERMANENT; // terrain is permanent + } + else + { + gFieldStatuses |= terrainFlags; + gFieldTimers.terrainTimer = varTerrainTimer; + } + + switch (VarGet(B_VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY) + { + case STATUS_FIELD_ELECTRIC_TERRAIN: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; + break; + case STATUS_FIELD_MISTY_TERRAIN: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; + break; + case STATUS_FIELD_GRASSY_TERRAIN: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_GRASSY; + break; + case STATUS_FIELD_PSYCHIC_TERRAIN: + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC; + break; + } + BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); + effect++; + } + else if (B_THUNDERSTORM_TERRAIN == TRUE + && GetCurrentWeather() == WEATHER_RAIN_THUNDERSTORM + && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)) + { + // overworld weather started rain, so just do electric terrain anim + if (varTerrainTimer == 0) + { + gFieldStatuses = (STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); + } + else + { + gFieldStatuses |= STATUS_FIELD_ELECTRIC_TERRAIN; + gFieldTimers.terrainTimer = varTerrainTimer; + } + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; + BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); + effect++; + } + else if (B_FOG_TERRAIN == TRUE + && (GetCurrentWeather() == WEATHER_FOG_HORIZONTAL || GetCurrentWeather() == WEATHER_FOG_DIAGONAL) + && !(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + { + if (varTerrainTimer == 0) + { + gFieldStatuses = (STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); + } + else + { + gFieldStatuses |= STATUS_FIELD_ELECTRIC_TERRAIN; + gFieldTimers.terrainTimer = varTerrainTimer; + } + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; + BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); + effect++; + } } - else if (B_THUNDERSTORM_TERRAIN == TRUE - && GetCurrentWeather() == WEATHER_RAIN_THUNDERSTORM - && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)) - { - // overworld weather started rain, so just do electric terrain anim - gFieldStatuses = (STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; - BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); - effect++; - } - else if (B_FOG_TERRAIN == TRUE - && (GetCurrentWeather() == WEATHER_FOG_HORIZONTAL || GetCurrentWeather() == WEATHER_FOG_DIAGONAL) - && !(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) - { - gFieldStatuses = (STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; - BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); - effect++; - } - break; + break; case ABILITYEFFECT_SWITCH_IN_WEATHER: gBattleScripting.battler = battler; if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) @@ -6859,7 +6891,7 @@ static u8 ItemEffectMoveEnd(u32 battler, u16 holdEffect) break; case HOLD_EFFECT_CRITICAL_UP: // lansat berry if (B_BERRIES_INSTANT >= GEN_4 - && !(gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY) + && !(gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY_ANY) && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, gLastUsedItem), gLastUsedItem)) { gBattleMons[battler].status2 |= STATUS2_FOCUS_ENERGY; @@ -6984,7 +7016,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) break; case HOLD_EFFECT_CRITICAL_UP: if (B_BERRIES_INSTANT >= GEN_4 - && !(gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY) + && !(gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY_ANY) && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, gLastUsedItem), gLastUsedItem)) { gBattleMons[battler].status2 |= STATUS2_FOCUS_ENERGY; @@ -7295,7 +7327,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPDEF, TRUE); break; case HOLD_EFFECT_CRITICAL_UP: - if (!moveTurn && !(gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY) + if (!moveTurn && !(gBattleMons[battler].status2 & STATUS2_FOCUS_ENERGY_ANY) && HasEnoughHpToEatBerry(battler, GetBattlerItemHoldEffectParam(battler, gLastUsedItem), gLastUsedItem)) { gBattleMons[battler].status2 |= STATUS2_FOCUS_ENERGY; @@ -7994,6 +8026,7 @@ u8 IsMonDisobedient(void) } while (gBitTable[gCurrMovePos] & calc); gCalledMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos]; + SetAtkCancellerForCalledMove(); gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove; gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE); gHitMarker |= HITMARKER_DISOBEDIENT_MOVE; @@ -11021,6 +11054,17 @@ bool32 MoveHasMoveEffectSelfArg(u32 move, u32 moveEffect, u32 argument) return (gMovesInfo[move].argument == argument) && MoveHasMoveEffectSelf(move, moveEffect); } +bool32 MoveHasChargeTurnMoveEffect(u32 move) +{ + u8 i = 0; + for (i = 0; i < gMovesInfo[move].numAdditionalEffects; i++) + { + if (gMovesInfo[move].additionalEffects[i].onChargeTurnOnly) + return TRUE; + } + return FALSE; +} + bool8 CanMonParticipateInSkyBattle(struct Pokemon *mon) { u16 species = GetMonData(mon, MON_DATA_SPECIES); diff --git a/src/battle_z_move.c b/src/battle_z_move.c index 8cb379d892..b2377bffd4 100644 --- a/src/battle_z_move.c +++ b/src/battle_z_move.c @@ -606,7 +606,7 @@ void SetZEffect(void) } break; case Z_EFFECT_BOOST_CRITS: - if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY)) + if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY_ANY)) { gBattleMons[gBattlerAttacker].status2 |= STATUS2_FOCUS_ENERGY; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_Z_BOOST_CRITS; diff --git a/src/data.c b/src/data.c index f559656d35..074b067ac9 100644 --- a/src/data.c +++ b/src/data.c @@ -220,5 +220,9 @@ const union AnimCmd *const gAnims_MonPic[MAX_MON_PIC_FRAMES] = sAnim_MonPic_1, }; +const union AnimCmd *const sAnims_Trainer[] ={ + sAnim_GeneralFrame0, +}; + #include "data/trainer_parties.h" #include "data/trainers.h" diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 97d91b2630..5a11be96cc 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -383,7 +383,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_TWO_TURNS_ATTACK] = { .battleScript = BattleScript_EffectTwoTurnsAttack, - .battleTvScore = 0, // TODO: Assign points + .battleTvScore = 3, }, [EFFECT_SUBSTITUTE] = @@ -527,7 +527,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_TRIPLE_KICK] = { - .battleScript = BattleScript_EffectTripleKick, + .battleScript = BattleScript_EffectHit, .battleTvScore = 1, }, @@ -748,12 +748,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .encourageEncore = TRUE, }, - [EFFECT_SKULL_BASH] = - { - .battleScript = BattleScript_EffectSkullBash, - .battleTvScore = 3, - }, - [EFFECT_EARTHQUAKE] = { .battleScript = BattleScript_EffectHit, @@ -767,15 +761,9 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .encourageEncore = TRUE, }, - [EFFECT_GUST] = - { - .battleScript = BattleScript_EffectGust, - .battleTvScore = 1, - }, - [EFFECT_SOLAR_BEAM] = { - .battleScript = BattleScript_EffectSolarBeam, + .battleScript = BattleScript_EffectTwoTurnsAttack, .battleTvScore = 1, }, @@ -799,8 +787,9 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_SEMI_INVULNERABLE] = { - .battleScript = BattleScript_EffectSemiInvulnerable, + .battleScript = BattleScript_EffectTwoTurnsAttack, .battleTvScore = 3, + .semiInvulnerableEffect = TRUE, }, [EFFECT_DEFENSE_CURL] = @@ -1101,12 +1090,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .encourageEncore = TRUE, }, - [EFFECT_SKY_UPPERCUT] = - { - .battleScript = BattleScript_EffectSkyUppercut, - .battleTvScore = 1, - }, - [EFFECT_BULK_UP] = { .battleScript = BattleScript_EffectBulkUp, @@ -2016,6 +1999,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = { .battleScript = BattleScript_EffectSkyDrop, .battleTvScore = 0, // TODO: Assign points + .semiInvulnerableEffect = TRUE, }, [EFFECT_EXPANDING_FORCE] = @@ -2024,12 +2008,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleTvScore = 0, // TODO: Assign points }, - [EFFECT_METEOR_BEAM] = - { - .battleScript = BattleScript_EffectMeteorBeam, - .battleTvScore = 0, // TODO: Assign points - }, - [EFFECT_RISING_VOLTAGE] = { .battleScript = BattleScript_EffectHit, @@ -2229,4 +2207,11 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleScript = BattleScript_EffectUpperHand, .battleTvScore = 0, // TODO: Assign points }, + + [EFFECT_DRAGON_CHEER] = + { + .battleScript = BattleScript_EffectFocusEnergy, + .battleTvScore = 1, + .encourageEncore = TRUE, + }, }; diff --git a/src/data/graphics/pokemon.h b/src/data/graphics/pokemon.h index de164520d2..efb16af883 100644 --- a/src/data/graphics/pokemon.h +++ b/src/data/graphics/pokemon.h @@ -10994,11 +10994,11 @@ const u8 gMonFootprint_QuestionMark[] = INCBIN_U8("graphics/pokemon/question_mar // const u8 gMonFootprint_Dipplin[] = INCBIN_U8("graphics/pokemon/dipplin/footprint.1bpp"); #endif //P_FOOTPRINTS - // const u32 gMonFrontPic_Hydrapple[] = INCBIN_U32("graphics/pokemon/hydrapple/front.4bpp.lz"); - // const u32 gMonPalette_Hydrapple[] = INCBIN_U32("graphics/pokemon/hydrapple/normal.gbapal.lz"); - // const u32 gMonBackPic_Hydrapple[] = INCBIN_U32("graphics/pokemon/hydrapple/back.4bpp.lz"); - // const u32 gMonShinyPalette_Hydrapple[] = INCBIN_U32("graphics/pokemon/hydrapple/shiny.gbapal.lz"); - // const u8 gMonIcon_Hydrapple[] = INCBIN_U8("graphics/pokemon/hydrapple/icon.4bpp"); + const u32 gMonFrontPic_Hydrapple[] = INCBIN_U32("graphics/pokemon/hydrapple/front.4bpp.lz"); + const u32 gMonPalette_Hydrapple[] = INCBIN_U32("graphics/pokemon/hydrapple/normal.gbapal.lz"); + const u32 gMonBackPic_Hydrapple[] = INCBIN_U32("graphics/pokemon/hydrapple/back.4bpp.lz"); + const u32 gMonShinyPalette_Hydrapple[] = INCBIN_U32("graphics/pokemon/hydrapple/shiny.gbapal.lz"); + const u8 gMonIcon_Hydrapple[] = INCBIN_U8("graphics/pokemon/hydrapple/icon.4bpp"); #if P_FOOTPRINTS // const u8 gMonFootprint_Hydrapple[] = INCBIN_U8("graphics/pokemon/hydrapple/footprint.1bpp"); #endif //P_FOOTPRINTS @@ -11574,10 +11574,10 @@ const u8 gMonFootprint_QuestionMark[] = INCBIN_U8("graphics/pokemon/question_mar #endif //P_GIGANTAMAX_FORMS #if P_GEN_9_CROSS_EVOS - // const u32 gMonFrontPic_Archaludon[] = INCBIN_U32("graphics/pokemon/archaludon/front.4bpp.lz"); - // const u32 gMonPalette_Archaludon[] = INCBIN_U32("graphics/pokemon/archaludon/normal.gbapal.lz"); - // const u32 gMonBackPic_Archaludon[] = INCBIN_U32("graphics/pokemon/archaludon/back.4bpp.lz"); - // const u32 gMonShinyPalette_Archaludon[] = INCBIN_U32("graphics/pokemon/archaludon/shiny.gbapal.lz"); + const u32 gMonFrontPic_Archaludon[] = INCBIN_U32("graphics/pokemon/archaludon/front.4bpp.lz"); + const u32 gMonPalette_Archaludon[] = INCBIN_U32("graphics/pokemon/archaludon/normal.gbapal.lz"); + const u32 gMonBackPic_Archaludon[] = INCBIN_U32("graphics/pokemon/archaludon/back.4bpp.lz"); + const u32 gMonShinyPalette_Archaludon[] = INCBIN_U32("graphics/pokemon/archaludon/shiny.gbapal.lz"); // const u8 gMonIcon_Archaludon[] = INCBIN_U8("graphics/pokemon/archaludon/icon.4bpp"); #if P_FOOTPRINTS // const u8 gMonFootprint_Archaludon[] = INCBIN_U8("graphics/pokemon/archaludon/footprint.1bpp"); @@ -12963,10 +12963,10 @@ const u8 gMonFootprint_QuestionMark[] = INCBIN_U8("graphics/pokemon/question_mar #endif //P_FAMILY_OGERPON #if P_FAMILY_GOUGING_FIRE - // const u32 gMonFrontPic_GougingFire[] = INCBIN_U32("graphics/pokemon/gouging_fire/front.4bpp.lz"); - // const u32 gMonPalette_GougingFire[] = INCBIN_U32("graphics/pokemon/gouging_fire/normal.gbapal.lz"); - // const u32 gMonBackPic_GougingFire[] = INCBIN_U32("graphics/pokemon/gouging_fire/back.4bpp.lz"); - // const u32 gMonShinyPalette_GougingFire[] = INCBIN_U32("graphics/pokemon/gouging_fire/shiny.gbapal.lz"); + const u32 gMonFrontPic_GougingFire[] = INCBIN_U32("graphics/pokemon/gouging_fire/front.4bpp.lz"); + const u32 gMonPalette_GougingFire[] = INCBIN_U32("graphics/pokemon/gouging_fire/normal.gbapal.lz"); + const u32 gMonBackPic_GougingFire[] = INCBIN_U32("graphics/pokemon/gouging_fire/back.4bpp.lz"); + const u32 gMonShinyPalette_GougingFire[] = INCBIN_U32("graphics/pokemon/gouging_fire/shiny.gbapal.lz"); // const u8 gMonIcon_GougingFire[] = INCBIN_U8("graphics/pokemon/gouging_fire/icon.4bpp"); #if P_FOOTPRINTS // const u8 gMonFootprint_GougingFire[] = INCBIN_U8("graphics/pokemon/gouging_fire/footprint.1bpp"); @@ -12974,10 +12974,10 @@ const u8 gMonFootprint_QuestionMark[] = INCBIN_U8("graphics/pokemon/question_mar #endif //P_FAMILY_GOUGING_FIRE #if P_FAMILY_RAGING_BOLT - // const u32 gMonFrontPic_RagingBolt[] = INCBIN_U32("graphics/pokemon/raging_bolt/front.4bpp.lz"); - // const u32 gMonPalette_RagingBolt[] = INCBIN_U32("graphics/pokemon/raging_bolt/normal.gbapal.lz"); - // const u32 gMonBackPic_RagingBolt[] = INCBIN_U32("graphics/pokemon/raging_bolt/back.4bpp.lz"); - // const u32 gMonShinyPalette_RagingBolt[] = INCBIN_U32("graphics/pokemon/raging_bolt/shiny.gbapal.lz"); + const u32 gMonFrontPic_RagingBolt[] = INCBIN_U32("graphics/pokemon/raging_bolt/front.4bpp.lz"); + const u32 gMonPalette_RagingBolt[] = INCBIN_U32("graphics/pokemon/raging_bolt/normal.gbapal.lz"); + const u32 gMonBackPic_RagingBolt[] = INCBIN_U32("graphics/pokemon/raging_bolt/back.4bpp.lz"); + const u32 gMonShinyPalette_RagingBolt[] = INCBIN_U32("graphics/pokemon/raging_bolt/shiny.gbapal.lz"); // const u8 gMonIcon_RagingBolt[] = INCBIN_U8("graphics/pokemon/raging_bolt/icon.4bpp"); #if P_FOOTPRINTS // const u8 gMonFootprint_RagingBolt[] = INCBIN_U8("graphics/pokemon/raging_bolt/footprint.1bpp"); @@ -12985,10 +12985,10 @@ const u8 gMonFootprint_QuestionMark[] = INCBIN_U8("graphics/pokemon/question_mar #endif //P_FAMILY_RAGING_BOLT #if P_FAMILY_IRON_BOULDER - // const u32 gMonFrontPic_IronBoulder[] = INCBIN_U32("graphics/pokemon/iron_boulder/front.4bpp.lz"); - // const u32 gMonPalette_IronBoulder[] = INCBIN_U32("graphics/pokemon/iron_boulder/normal.gbapal.lz"); - // const u32 gMonBackPic_IronBoulder[] = INCBIN_U32("graphics/pokemon/iron_boulder/back.4bpp.lz"); - // const u32 gMonShinyPalette_IronBoulder[] = INCBIN_U32("graphics/pokemon/iron_boulder/shiny.gbapal.lz"); + const u32 gMonFrontPic_IronBoulder[] = INCBIN_U32("graphics/pokemon/iron_boulder/front.4bpp.lz"); + const u32 gMonPalette_IronBoulder[] = INCBIN_U32("graphics/pokemon/iron_boulder/normal.gbapal.lz"); + const u32 gMonBackPic_IronBoulder[] = INCBIN_U32("graphics/pokemon/iron_boulder/back.4bpp.lz"); + const u32 gMonShinyPalette_IronBoulder[] = INCBIN_U32("graphics/pokemon/iron_boulder/shiny.gbapal.lz"); // const u8 gMonIcon_IronBoulder[] = INCBIN_U8("graphics/pokemon/iron_boulder/icon.4bpp"); #if P_FOOTPRINTS // const u8 gMonFootprint_IronBoulder[] = INCBIN_U8("graphics/pokemon/iron_boulder/footprint.1bpp"); @@ -12996,10 +12996,10 @@ const u8 gMonFootprint_QuestionMark[] = INCBIN_U8("graphics/pokemon/question_mar #endif //P_FAMILY_IRON_BOULDER #if P_FAMILY_IRON_CROWN - // const u32 gMonFrontPic_IronCrown[] = INCBIN_U32("graphics/pokemon/iron_crown/front.4bpp.lz"); - // const u32 gMonPalette_IronCrown[] = INCBIN_U32("graphics/pokemon/iron_crown/normal.gbapal.lz"); - // const u32 gMonBackPic_IronCrown[] = INCBIN_U32("graphics/pokemon/iron_crown/back.4bpp.lz"); - // const u32 gMonShinyPalette_IronCrown[] = INCBIN_U32("graphics/pokemon/iron_crown/shiny.gbapal.lz"); + const u32 gMonFrontPic_IronCrown[] = INCBIN_U32("graphics/pokemon/iron_crown/front.4bpp.lz"); + const u32 gMonPalette_IronCrown[] = INCBIN_U32("graphics/pokemon/iron_crown/normal.gbapal.lz"); + const u32 gMonBackPic_IronCrown[] = INCBIN_U32("graphics/pokemon/iron_crown/back.4bpp.lz"); + const u32 gMonShinyPalette_IronCrown[] = INCBIN_U32("graphics/pokemon/iron_crown/shiny.gbapal.lz"); // const u8 gMonIcon_IronCrown[] = INCBIN_U8("graphics/pokemon/iron_crown/icon.4bpp"); #if P_FOOTPRINTS // const u8 gMonFootprint_IronCrown[] = INCBIN_U8("graphics/pokemon/iron_crown/footprint.1bpp"); diff --git a/src/data/graphics/trainers.h b/src/data/graphics/trainers.h index e20d13ec16..309fd0b80c 100644 --- a/src/data/graphics/trainers.h +++ b/src/data/graphics/trainers.h @@ -292,116 +292,115 @@ const u8 gTrainerBackPic_Steven[] = INCBIN_U8("graphics/trainers/back_pics/steve const u32 gTrainerBackPicPalette_Red[] = INCBIN_U32("graphics/trainers/back_pics/red.gbapal.lz"); const u32 gTrainerBackPicPalette_Leaf[] = INCBIN_U32("graphics/trainers/back_pics/leaf.gbapal.lz"); -static const union AnimCmd *const sAnims_Trainer[] ={ - sAnim_GeneralFrame0, -}; - -#define TRAINER_SPRITE(trainerPic, file, x, y, rotation) \ +// The first two parameters invoke a front pic and palette by +// calling a "TRAINER_PIC" constant (e.g. TRAINER_PIC_HIKER), and +// gTrainerFrontPic/gTrainerPalette pointers, (e.g "gTrainerFrontPic_Hiker" and "gTrainerPalette_Hiker"). +// The last three parameters control the X and Y coordinates and rotation of the mugshot on the screen. +// They default to 0, 0, and 0x200 which are default values used by the majority of the game's trainer sprites. +#define TRAINER_SPRITE(trainerPic, file, ...) \ [TRAINER_PIC_##trainerPic] = \ { \ - .y_offset = 8, \ .frontPic = {gTrainerFrontPic_##file, TRAINER_PIC_SIZE, TRAINER_PIC_##trainerPic},\ .palette = {gTrainerPalette_##file, TRAINER_PIC_##trainerPic}, \ - .animation = sAnims_Trainer, \ - .mugshotCoords = {x, y}, \ - .mugshotRotation = rotation, \ + .mugshotCoords = {DEFAULT(0, __VA_ARGS__), DEFAULT_2(0, __VA_ARGS__)}, \ + .mugshotRotation = DEFAULT_3(0x200, __VA_ARGS__), \ } const struct TrainerSprite gTrainerSprites[] = { - TRAINER_SPRITE(HIKER, Hiker, 0, 0, 0x200), - TRAINER_SPRITE(AQUA_GRUNT_M, AquaGruntM, 0, 0, 0x200), - TRAINER_SPRITE(POKEMON_BREEDER_F, PokemonBreederF, 0, 0, 0x200), - TRAINER_SPRITE(COOLTRAINER_M, CoolTrainerM, 0, 0, 0x200), - TRAINER_SPRITE(BIRD_KEEPER, BirdKeeper, 0, 0, 0x200), - TRAINER_SPRITE(COLLECTOR, Collector, 0, 0, 0x200), - TRAINER_SPRITE(AQUA_GRUNT_F, AquaGruntF, 0, 0, 0x200), - TRAINER_SPRITE(SWIMMER_M, SwimmerM, 0, 0, 0x200), - TRAINER_SPRITE(MAGMA_GRUNT_M, MagmaGruntM, 0, 0, 0x200), - TRAINER_SPRITE(EXPERT_M, ExpertM, 0, 0, 0x200), - TRAINER_SPRITE(AQUA_ADMIN_M, AquaAdminM, 0, 0, 0x200), - TRAINER_SPRITE(BLACK_BELT, BlackBelt, 0, 0, 0x200), - TRAINER_SPRITE(AQUA_ADMIN_F, AquaAdminF, 0, 0, 0x200), - TRAINER_SPRITE(AQUA_LEADER_ARCHIE, AquaLeaderArchie, 0, 0, 0x200), - TRAINER_SPRITE(HEX_MANIAC, HexManiac, 0, 0, 0x200), - TRAINER_SPRITE(AROMA_LADY, AromaLady, 0, 0, 0x200), - TRAINER_SPRITE(RUIN_MANIAC, RuinManiac, 0, 0, 0x200), - TRAINER_SPRITE(INTERVIEWER, Interviewer, 0, 0, 0x200), - TRAINER_SPRITE(TUBER_F, TuberF, 0, 0, 0x200), - TRAINER_SPRITE(TUBER_M, TuberM, 0, 0, 0x200), - TRAINER_SPRITE(COOLTRAINER_F, CoolTrainerF, 0, 0, 0x200), - TRAINER_SPRITE(LADY, Lady, 0, 0, 0x200), - TRAINER_SPRITE(BEAUTY, Beauty, 0, 0, 0x200), - TRAINER_SPRITE(RICH_BOY, RichBoy, 0, 0, 0x200), - TRAINER_SPRITE(EXPERT_F, ExpertF, 0, 0, 0x200), - TRAINER_SPRITE(POKEMANIAC, Pokemaniac, 0, 0, 0x200), - TRAINER_SPRITE(MAGMA_GRUNT_F, MagmaGruntF, 0, 0, 0x200), - TRAINER_SPRITE(GUITARIST, Guitarist, 0, 0, 0x200), - TRAINER_SPRITE(KINDLER, Kindler, 0, 0, 0x200), - TRAINER_SPRITE(CAMPER, Camper, 0, 0, 0x200), - TRAINER_SPRITE(PICNICKER, Picnicker, 0, 0, 0x200), - TRAINER_SPRITE(BUG_MANIAC, BugManiac, 0, 0, 0x200), - TRAINER_SPRITE(POKEMON_BREEDER_M, PokemonBreederM, 0, 0, 0x200), - TRAINER_SPRITE(PSYCHIC_M, PsychicM, 0, 0, 0x200), - TRAINER_SPRITE(PSYCHIC_F, PsychicF, 0, 0, 0x200), - TRAINER_SPRITE(GENTLEMAN, Gentleman, 0, 0, 0x200), - TRAINER_SPRITE(ELITE_FOUR_SIDNEY, EliteFourSidney, 0, 0, 0x200), - TRAINER_SPRITE(ELITE_FOUR_PHOEBE, EliteFourPhoebe, 0, 0, 0x200), + TRAINER_SPRITE(HIKER, Hiker), + TRAINER_SPRITE(AQUA_GRUNT_M, AquaGruntM), + TRAINER_SPRITE(POKEMON_BREEDER_F, PokemonBreederF), + TRAINER_SPRITE(COOLTRAINER_M, CoolTrainerM), + TRAINER_SPRITE(BIRD_KEEPER, BirdKeeper), + TRAINER_SPRITE(COLLECTOR, Collector), + TRAINER_SPRITE(AQUA_GRUNT_F, AquaGruntF), + TRAINER_SPRITE(SWIMMER_M, SwimmerM), + TRAINER_SPRITE(MAGMA_GRUNT_M, MagmaGruntM), + TRAINER_SPRITE(EXPERT_M, ExpertM), + TRAINER_SPRITE(AQUA_ADMIN_M, AquaAdminM), + TRAINER_SPRITE(BLACK_BELT, BlackBelt), + TRAINER_SPRITE(AQUA_ADMIN_F, AquaAdminF), + TRAINER_SPRITE(AQUA_LEADER_ARCHIE, AquaLeaderArchie), + TRAINER_SPRITE(HEX_MANIAC, HexManiac), + TRAINER_SPRITE(AROMA_LADY, AromaLady), + TRAINER_SPRITE(RUIN_MANIAC, RuinManiac), + TRAINER_SPRITE(INTERVIEWER, Interviewer), + TRAINER_SPRITE(TUBER_F, TuberF), + TRAINER_SPRITE(TUBER_M, TuberM), + TRAINER_SPRITE(COOLTRAINER_F, CoolTrainerF), + TRAINER_SPRITE(LADY, Lady), + TRAINER_SPRITE(BEAUTY, Beauty), + TRAINER_SPRITE(RICH_BOY, RichBoy), + TRAINER_SPRITE(EXPERT_F, ExpertF), + TRAINER_SPRITE(POKEMANIAC, Pokemaniac), + TRAINER_SPRITE(MAGMA_GRUNT_F, MagmaGruntF), + TRAINER_SPRITE(GUITARIST, Guitarist), + TRAINER_SPRITE(KINDLER, Kindler), + TRAINER_SPRITE(CAMPER, Camper), + TRAINER_SPRITE(PICNICKER, Picnicker), + TRAINER_SPRITE(BUG_MANIAC, BugManiac), + TRAINER_SPRITE(POKEMON_BREEDER_M, PokemonBreederM), + TRAINER_SPRITE(PSYCHIC_M, PsychicM), + TRAINER_SPRITE(PSYCHIC_F, PsychicF), + TRAINER_SPRITE(GENTLEMAN, Gentleman), + TRAINER_SPRITE(ELITE_FOUR_SIDNEY, EliteFourSidney), + TRAINER_SPRITE(ELITE_FOUR_PHOEBE, EliteFourPhoebe), TRAINER_SPRITE(ELITE_FOUR_GLACIA, EliteFourGlacia, -4, 4, 0x1B0), TRAINER_SPRITE(ELITE_FOUR_DRAKE, EliteFourDrake, 0, 5, 0x1A0), - TRAINER_SPRITE(LEADER_ROXANNE, LeaderRoxanne, 0, 0, 0x200), - TRAINER_SPRITE(LEADER_BRAWLY, LeaderBrawly, 0, 0, 0x200), - TRAINER_SPRITE(LEADER_WATTSON, LeaderWattson, 0, 0, 0x200), - TRAINER_SPRITE(LEADER_FLANNERY, LeaderFlannery, 0, 0, 0x200), - TRAINER_SPRITE(LEADER_NORMAN, LeaderNorman, 0, 0, 0x200), - TRAINER_SPRITE(LEADER_WINONA, LeaderWinona, 0, 0, 0x200), - TRAINER_SPRITE(LEADER_TATE_AND_LIZA, LeaderTateAndLiza, 0, 0, 0x200), - TRAINER_SPRITE(LEADER_JUAN, LeaderJuan, 0, 0, 0x200), - TRAINER_SPRITE(SCHOOL_KID_M, SchoolKidM, 0, 0, 0x200), - TRAINER_SPRITE(SCHOOL_KID_F, SchoolKidF, 0, 0, 0x200), - TRAINER_SPRITE(SR_AND_JR, SrAndJr, 0, 0, 0x200), - TRAINER_SPRITE(POKEFAN_M, PokefanM, 0, 0, 0x200), - TRAINER_SPRITE(POKEFAN_F, PokefanF, 0, 0, 0x200), - TRAINER_SPRITE(YOUNGSTER, Youngster, 0, 0, 0x200), + TRAINER_SPRITE(LEADER_ROXANNE, LeaderRoxanne), + TRAINER_SPRITE(LEADER_BRAWLY, LeaderBrawly), + TRAINER_SPRITE(LEADER_WATTSON, LeaderWattson), + TRAINER_SPRITE(LEADER_FLANNERY, LeaderFlannery), + TRAINER_SPRITE(LEADER_NORMAN, LeaderNorman), + TRAINER_SPRITE(LEADER_WINONA, LeaderWinona), + TRAINER_SPRITE(LEADER_TATE_AND_LIZA, LeaderTateAndLiza), + TRAINER_SPRITE(LEADER_JUAN, LeaderJuan), + TRAINER_SPRITE(SCHOOL_KID_M, SchoolKidM), + TRAINER_SPRITE(SCHOOL_KID_F, SchoolKidF), + TRAINER_SPRITE(SR_AND_JR, SrAndJr), + TRAINER_SPRITE(POKEFAN_M, PokefanM), + TRAINER_SPRITE(POKEFAN_F, PokefanF), + TRAINER_SPRITE(YOUNGSTER, Youngster), TRAINER_SPRITE(CHAMPION_WALLACE, ChampionWallace, -8, 7, 0x188), - TRAINER_SPRITE(FISHERMAN, Fisherman, 0, 0, 0x200), - TRAINER_SPRITE(CYCLING_TRIATHLETE_M, CyclingTriathleteM, 0, 0, 0x200), - TRAINER_SPRITE(CYCLING_TRIATHLETE_F, CyclingTriathleteF, 0, 0, 0x200), - TRAINER_SPRITE(RUNNING_TRIATHLETE_M, RunningTriathleteM, 0, 0, 0x200), - TRAINER_SPRITE(RUNNING_TRIATHLETE_F, RunningTriathleteF, 0, 0, 0x200), - TRAINER_SPRITE(SWIMMING_TRIATHLETE_M, SwimmingTriathleteM, 0, 0, 0x200), - TRAINER_SPRITE(SWIMMING_TRIATHLETE_F, SwimmingTriathleteF, 0, 0, 0x200), - TRAINER_SPRITE(DRAGON_TAMER, DragonTamer, 0, 0, 0x200), - TRAINER_SPRITE(NINJA_BOY, NinjaBoy, 0, 0, 0x200), - TRAINER_SPRITE(BATTLE_GIRL, BattleGirl, 0, 0, 0x200), - TRAINER_SPRITE(PARASOL_LADY, ParasolLady, 0, 0, 0x200), - TRAINER_SPRITE(SWIMMER_F, SwimmerF, 0, 0, 0x200), - TRAINER_SPRITE(TWINS, Twins, 0, 0, 0x200), - TRAINER_SPRITE(SAILOR, Sailor, 0, 0, 0x200), - TRAINER_SPRITE(MAGMA_ADMIN, MagmaAdmin, 0, 0, 0x200), - TRAINER_SPRITE(WALLY, Wally, 0, 0, 0x200), - TRAINER_SPRITE(BRENDAN, Brendan, 0, 0, 0x200), - TRAINER_SPRITE(MAY, May, 0, 0, 0x200), - TRAINER_SPRITE(BUG_CATCHER, BugCatcher, 0, 0, 0x200), - TRAINER_SPRITE(POKEMON_RANGER_M, PokemonRangerM, 0, 0, 0x200), - TRAINER_SPRITE(POKEMON_RANGER_F, PokemonRangerF, 0, 0, 0x200), - TRAINER_SPRITE(MAGMA_LEADER_MAXIE, MagmaLeaderMaxie, 0, 0, 0x200), - TRAINER_SPRITE(LASS, Lass, 0, 0, 0x200), - TRAINER_SPRITE(YOUNG_COUPLE, YoungCouple, 0, 0, 0x200), - TRAINER_SPRITE(OLD_COUPLE, OldCouple, 0, 0, 0x200), - TRAINER_SPRITE(SIS_AND_BRO, SisAndBro, 0, 0, 0x200), + TRAINER_SPRITE(FISHERMAN, Fisherman), + TRAINER_SPRITE(CYCLING_TRIATHLETE_M, CyclingTriathleteM), + TRAINER_SPRITE(CYCLING_TRIATHLETE_F, CyclingTriathleteF), + TRAINER_SPRITE(RUNNING_TRIATHLETE_M, RunningTriathleteM), + TRAINER_SPRITE(RUNNING_TRIATHLETE_F, RunningTriathleteF), + TRAINER_SPRITE(SWIMMING_TRIATHLETE_M, SwimmingTriathleteM), + TRAINER_SPRITE(SWIMMING_TRIATHLETE_F, SwimmingTriathleteF), + TRAINER_SPRITE(DRAGON_TAMER, DragonTamer), + TRAINER_SPRITE(NINJA_BOY, NinjaBoy), + TRAINER_SPRITE(BATTLE_GIRL, BattleGirl), + TRAINER_SPRITE(PARASOL_LADY, ParasolLady), + TRAINER_SPRITE(SWIMMER_F, SwimmerF), + TRAINER_SPRITE(TWINS, Twins), + TRAINER_SPRITE(SAILOR, Sailor), + TRAINER_SPRITE(MAGMA_ADMIN, MagmaAdmin), + TRAINER_SPRITE(WALLY, Wally), + TRAINER_SPRITE(BRENDAN, Brendan), + TRAINER_SPRITE(MAY, May), + TRAINER_SPRITE(BUG_CATCHER, BugCatcher), + TRAINER_SPRITE(POKEMON_RANGER_M, PokemonRangerM), + TRAINER_SPRITE(POKEMON_RANGER_F, PokemonRangerF), + TRAINER_SPRITE(MAGMA_LEADER_MAXIE, MagmaLeaderMaxie), + TRAINER_SPRITE(LASS, Lass), + TRAINER_SPRITE(YOUNG_COUPLE, YoungCouple), + TRAINER_SPRITE(OLD_COUPLE, OldCouple), + TRAINER_SPRITE(SIS_AND_BRO, SisAndBro), TRAINER_SPRITE(STEVEN, Steven, 0, 7, 0x188), - TRAINER_SPRITE(SALON_MAIDEN_ANABEL, SalonMaidenAnabel, 0, 0, 0x200), - TRAINER_SPRITE(DOME_ACE_TUCKER, DomeAceTucker, 0, 0, 0x200), - TRAINER_SPRITE(PALACE_MAVEN_SPENSER, PalaceMavenSpenser, 0, 0, 0x200), - TRAINER_SPRITE(ARENA_TYCOON_GRETA, ArenaTycoonGreta, 0, 0, 0x200), - TRAINER_SPRITE(FACTORY_HEAD_NOLAND, FactoryHeadNoland, 0, 0, 0x200), - TRAINER_SPRITE(PIKE_QUEEN_LUCY, PikeQueenLucy, 0, 0, 0x200), - TRAINER_SPRITE(PYRAMID_KING_BRANDON, PyramidKingBrandon, 0, 0, 0x200), - TRAINER_SPRITE(RED, Red, 0, 0, 0x200), - TRAINER_SPRITE(LEAF, Leaf, 0, 0, 0x200), - TRAINER_SPRITE(RS_BRENDAN, RubySapphireBrendan, 0, 0, 0x200), - TRAINER_SPRITE(RS_MAY, RubySapphireMay, 0, 0, 0x200), + TRAINER_SPRITE(SALON_MAIDEN_ANABEL, SalonMaidenAnabel), + TRAINER_SPRITE(DOME_ACE_TUCKER, DomeAceTucker), + TRAINER_SPRITE(PALACE_MAVEN_SPENSER, PalaceMavenSpenser), + TRAINER_SPRITE(ARENA_TYCOON_GRETA, ArenaTycoonGreta), + TRAINER_SPRITE(FACTORY_HEAD_NOLAND, FactoryHeadNoland), + TRAINER_SPRITE(PIKE_QUEEN_LUCY, PikeQueenLucy), + TRAINER_SPRITE(PYRAMID_KING_BRANDON, PyramidKingBrandon), + TRAINER_SPRITE(RED, Red), + TRAINER_SPRITE(LEAF, Leaf), + TRAINER_SPRITE(RS_BRENDAN, RubySapphireBrendan), + TRAINER_SPRITE(RS_MAY, RubySapphireMay), }; static const union AnimCmd sAnimCmd_Hoenn[] = diff --git a/src/data/moves_info.h b/src/data/moves_info.h index e46482e304..5fc35dba83 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -2,6 +2,7 @@ #include "constants/battle.h" #include "constants/battle_move_effects.h" #include "constants/battle_script_commands.h" +#include "constants/battle_string_ids.h" #include "constants/battle_z_move_effects.h" #include "constants/hold_effects.h" #include "constants/moves.h" @@ -9,9 +10,8 @@ // The Gen. 4+ contest data comes from urpg's contest movedex. -#define FIRST(a, ...) a #if B_EXPANDED_MOVE_NAMES == TRUE -#define HANDLE_EXPANDED_MOVE_NAME(_name, ...) COMPOUND_STRING(FIRST(__VA_OPT__(__VA_ARGS__, ) _name)) +#define HANDLE_EXPANDED_MOVE_NAME(_name, ...) COMPOUND_STRING(DEFAULT(_name, __VA_ARGS__)) #else #define HANDLE_EXPANDED_MOVE_NAME(_name, ...) COMPOUND_STRING(_name) #endif @@ -22,6 +22,11 @@ #define BINDING_TURNS "2 to 5" #endif +/* First arg is the charge turn string id, second arg depends on effect +EFFECT_SEMI_INVULNERABLE/EFFECT_SKY_DROP: semi-invulnerable STATUS3 to apply to battler +EFFECT_TWO_TURNS_ATTACK/EFFECT_SOLAR_BEAM: weather in which to skip charge turn */ +#define TWO_TURN_ARG(stringid, ...) (stringid) __VA_OPT__(| ((__VA_ARGS__) << 16)) + // Shared Move Description entries const u8 gNotDoneYetDescription[] = _( @@ -428,6 +433,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .windMove = B_EXTRAPOLATED_MOVE_FLAGS, + .argument = TWO_TURN_ARG(STRINGID_PKMNWHIPPEDWHIRLWIND), .contestEffect = CONTEST_EFFECT_AFFECTED_BY_PREV_APPEAL, .contestCategory = CONTEST_CATEGORY_COOL, .contestComboStarterId = 0, @@ -485,7 +491,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Strikes the foe with a gust\n" "of wind whipped up by wings."), - .effect = EFFECT_GUST, + .effect = EFFECT_HIT, .power = 40, .type = TYPE_FLYING, .accuracy = 100, @@ -570,6 +576,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .assistBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNFLEWHIGH, COMPRESS_BITS(STATUS3_ON_AIR)), .contestEffect = CONTEST_EFFECT_AVOID_STARTLE, .contestCategory = CONTEST_CATEGORY_SMART, .contestComboStarterId = 0, @@ -1933,6 +1940,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .twoTurnMove = TRUE, .sleepTalkBanned = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNTOOKSUNLIGHT, B_WEATHER_SUN), .contestEffect = CONTEST_EFFECT_HIGHLY_APPEALING, .contestCategory = CONTEST_CATEGORY_COOL, .contestComboStarterId = 0, @@ -2292,6 +2300,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .instructBanned = TRUE, .assistBanned = TRUE, .skyBattleBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNDUGHOLE, COMPRESS_BITS(STATUS3_UNDERGROUND)), .contestEffect = CONTEST_EFFECT_AVOID_STARTLE, .contestCategory = CONTEST_CATEGORY_SMART, .contestComboStarterId = 0, @@ -2884,6 +2893,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_ACC_UP_1 }, + .argument = STATUS2_FOCUS_ENERGY, .ignoresProtect = TRUE, .mirrorMoveBanned = TRUE, .snatchAffected = TRUE, @@ -3221,7 +3231,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Tucks in the head, then\n" "attacks on the next turn."), - .effect = EFFECT_SKULL_BASH, + .effect = EFFECT_TWO_TURNS_ATTACK, .power = B_UPDATED_MOVE_DATA >= GEN_6 ? 130 : 100, .type = TYPE_NORMAL, .accuracy = 100, @@ -3233,6 +3243,12 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .makesContact = TRUE, .sleepTalkBanned = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNLOWEREDHEAD), + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_DEF_PLUS_1, + .self = TRUE, + .onChargeTurnOnly = TRUE, + }), .contestEffect = CONTEST_EFFECT_BADLY_STARTLE_FRONT_MON, .contestCategory = CONTEST_CATEGORY_TOUGH, .contestComboStarterId = 0, @@ -3541,11 +3557,12 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, - .criticalHitStage = B_UPDATED_MOVE_DATA >= GEN_3 ? 1 : 0, + .criticalHitStage = B_UPDATED_MOVE_DATA >= GEN_3, .twoTurnMove = TRUE, .sheerForceBoost = TRUE, .sleepTalkBanned = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(B_UPDATED_MOVE_DATA >= GEN_4 ? STRINGID_CLOAKEDINAHARSHLIGHT : STRINGID_PKMNISGLOWING), #if B_UPDATED_MOVE_DATA >= GEN_3 .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_FLINCH, @@ -7140,6 +7157,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .instructBanned = TRUE, .assistBanned = TRUE, .skyBattleBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNHIDUNDERWATER, COMPRESS_BITS(STATUS3_UNDERWATER)), .contestEffect = CONTEST_EFFECT_AVOID_STARTLE_ONCE, .contestCategory = CONTEST_CATEGORY_BEAUTY, .contestComboStarterId = COMBO_STARTER_DIVE, @@ -8000,7 +8018,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "An uppercut thrown as if\n" "leaping into the sky."), - .effect = EFFECT_SKY_UPPERCUT, + .effect = EFFECT_HIT, .power = 85, .type = TYPE_FIGHTING, .accuracy = 90, @@ -8312,6 +8330,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .assistBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNSPRANGUP, COMPRESS_BITS(STATUS3_ON_AIR)), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_PARALYSIS, .chance = 30, @@ -11319,6 +11338,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .assistBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_VANISHEDINSTANTLY, COMPRESS_BITS(STATUS3_PHANTOM_FORCE)), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_FEINT, }), @@ -12252,6 +12272,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .assistBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNTOOKTARGETHIGH, COMPRESS_BITS(STATUS3_ON_AIR)), .contestEffect = CONTEST_EFFECT_AVOID_STARTLE, .contestCategory = CONTEST_CATEGORY_SMART, .contestComboStarterId = 0, @@ -13318,6 +13339,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .metronomeBanned = TRUE, .sleepTalkBanned = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_CLOAKEDINAFREEZINGLIGHT), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_PARALYSIS, .chance = 30, @@ -13347,6 +13369,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .metronomeBanned = TRUE, .sleepTalkBanned = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_CLOAKEDINAFREEZINGLIGHT), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_BURN, .chance = 30, @@ -13648,6 +13671,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .assistBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_VANISHEDINSTANTLY, COMPRESS_BITS(STATUS3_PHANTOM_FORCE)), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_FEINT, }), @@ -14475,6 +14499,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sleepTalkBanned = TRUE, .instructBanned = TRUE, .skyBattleBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKNMABSORBINGPOWER), .contestEffect = CONTEST_EFFECT_IMPROVE_CONDITION_PREVENT_NERVOUSNESS, .contestCategory = CONTEST_CATEGORY_CUTE, .contestComboStarterId = 0, @@ -14944,9 +14969,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .ignoresSubstitute = TRUE, .metronomeBanned = TRUE, .sketchBanned = (B_SKETCH_BANS >= GEN_9), - .additionalEffects = ADDITIONAL_EFFECTS( + .additionalEffects = ADDITIONAL_EFFECTS({ // Feint move effect handled in script as it goes before animation - { .moveEffect = MOVE_EFFECT_DEF_MINUS_1, .self = TRUE, }), @@ -15218,6 +15242,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .slicingMove = TRUE, .sleepTalkBanned = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_PKMNTOOKSUNLIGHT, B_WEATHER_SUN), .contestEffect = CONTEST_EFFECT_HIGHLY_APPEALING, .contestCategory = CONTEST_CATEGORY_TOUGH, .contestComboStarterId = 0, @@ -17458,7 +17483,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "A 2-turn move that raises\n" "Sp. Attack before attacking."), - .effect = EFFECT_METEOR_BEAM, + .effect = EFFECT_TWO_TURNS_ATTACK, .power = 120, .type = TYPE_ROCK, .accuracy = 90, @@ -17468,6 +17493,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .category = DAMAGE_CATEGORY_SPECIAL, .twoTurnMove = TRUE, .instructBanned = TRUE, + .argument = TWO_TURN_ARG(STRINGID_METEORBEAMCHARGING), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_SP_ATK_PLUS_1, .self = TRUE, @@ -19714,7 +19740,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Absorbs electricity in one turn,\n" "then attacks next turn."), - .effect = EFFECT_METEOR_BEAM, + .effect = EFFECT_TWO_TURNS_ATTACK, .power = 130, .type = TYPE_ELECTRIC, .accuracy = 100, @@ -19723,6 +19749,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, .sheerForceBoost = TRUE, + .argument = TWO_TURN_ARG(STRINGID_ELECTROSHOTCHARGING, B_WEATHER_RAIN), .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_SP_ATK_PLUS_1, .self = TRUE, @@ -19859,7 +19886,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Increases allies' critical hit\n" "ratio, especially if Dragons."), - .effect = EFFECT_PLACEHOLDER, //EFFECT_DRAGON_CHEER + .effect = EFFECT_DRAGON_CHEER, .power = 0, .type = TYPE_DRAGON, .accuracy = 0, diff --git a/src/data/pokemon/species_info/gen_8.h b/src/data/pokemon/species_info/gen_8.h index fa0e606b45..3e956b371a 100644 --- a/src/data/pokemon/species_info/gen_8.h +++ b/src/data/pokemon/species_info/gen_8.h @@ -1977,15 +1977,15 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .pokemonOffset = 17, .trainerScale = 256, .trainerOffset = 0, - //FRONT_PIC(Hydrapple, 64, 64), - .frontPicYOffset = 0, + FRONT_PIC(Hydrapple, 64, 64), + .frontPicYOffset = 1, .frontAnimFrames = sAnims_Hydrapple, - //.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, - //BACK_PIC(Hydrapple, 64, 64), + .frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, + BACK_PIC(Hydrapple, 64, 64), .backPicYOffset = 0, - //.backAnimId = BACK_ANIM_NONE, - //PALETTES(Hydrapple), - //ICON(Hydrapple, 0), + .backAnimId = BACK_ANIM_NONE, + PALETTES(Hydrapple), + ICON(Hydrapple, 1), //FOOTPRINT(Hydrapple) LEARNSETS(Hydrapple), }, @@ -4333,14 +4333,14 @@ const struct SpeciesInfo gSpeciesInfoGen8[] = .pokemonOffset = 2, .trainerScale = 286, .trainerOffset = 1, - //FRONT_PIC(Archaludon, 64, 64), + FRONT_PIC(Archaludon, 64, 64), .frontPicYOffset = 0, .frontAnimFrames = sAnims_Archaludon, - //.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, - //BACK_PIC(Archaludon, 64, 64), - .backPicYOffset = 0, - //.backAnimId = BACK_ANIM_NONE, - //PALETTES(Archaludon), + .frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, + BACK_PIC(Archaludon, 64, 64), + .backPicYOffset = 7, + .backAnimId = BACK_ANIM_NONE, + PALETTES(Archaludon), //ICON(Archaludon, 0), //FOOTPRINT(Archaludon) LEARNSETS(Archaludon), diff --git a/src/data/pokemon/species_info/gen_9.h b/src/data/pokemon/species_info/gen_9.h index 6fada1325d..c0feb0b3e9 100644 --- a/src/data/pokemon/species_info/gen_9.h +++ b/src/data/pokemon/species_info/gen_9.h @@ -5468,14 +5468,14 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .pokemonOffset = 0, .trainerScale = 345, .trainerOffset = 7, - //FRONT_PIC(GougingFire, 64, 64), - .frontPicYOffset = 0, + FRONT_PIC(GougingFire, 64, 64), + .frontPicYOffset = 2, .frontAnimFrames = sAnims_GougingFire, - //.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, - //BACK_PIC(GougingFire, 64, 64), - .backPicYOffset = 0, - //.backAnimId = BACK_ANIM_NONE, - //PALETTES(GougingFire), + .frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, + BACK_PIC(GougingFire, 64, 64), + .backPicYOffset = 6, + .backAnimId = BACK_ANIM_NONE, + PALETTES(GougingFire), //ICON(GougingFire, 0), //FOOTPRINT(GougingFire) LEARNSETS(GougingFire), @@ -5518,14 +5518,14 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .pokemonOffset = 0, .trainerScale = 345, .trainerOffset = 7, - //FRONT_PIC(RagingBolt, 64, 64), + FRONT_PIC(RagingBolt, 64, 64), .frontPicYOffset = 0, .frontAnimFrames = sAnims_RagingBolt, - //.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, - //BACK_PIC(RagingBolt, 64, 64), + .frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, + BACK_PIC(RagingBolt, 64, 64), .backPicYOffset = 0, - //.backAnimId = BACK_ANIM_NONE, - //PALETTES(RagingBolt), + .backAnimId = BACK_ANIM_NONE, + PALETTES(RagingBolt), //ICON(RagingBolt, 0), //FOOTPRINT(RagingBolt) LEARNSETS(RagingBolt), @@ -5567,14 +5567,14 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .pokemonOffset = 1, .trainerScale = 336, .trainerOffset = 4, - //FRONT_PIC(IronBoulder, 64, 64), - .frontPicYOffset = 0, + FRONT_PIC(IronBoulder, 64, 64), + .frontPicYOffset = 3, .frontAnimFrames = sAnims_IronBoulder, - //.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, - //BACK_PIC(IronBoulder, 64, 64), - .backPicYOffset = 0, - //.backAnimId = BACK_ANIM_NONE, - //PALETTES(IronBoulder), + .frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, + BACK_PIC(IronBoulder, 64, 64), + .backPicYOffset = 12, + .backAnimId = BACK_ANIM_NONE, + PALETTES(IronBoulder), //ICON(IronBoulder, 0), //FOOTPRINT(IronBoulder) LEARNSETS(IronBoulder), @@ -5617,14 +5617,14 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = .pokemonOffset = 0, .trainerScale = 365, .trainerOffset = 7, - //FRONT_PIC(IronCrown, 64, 64), + FRONT_PIC(IronCrown, 64, 64), .frontPicYOffset = 0, .frontAnimFrames = sAnims_IronCrown, - //.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, - //BACK_PIC(IronCrown, 64, 64), - .backPicYOffset = 0, - //.backAnimId = BACK_ANIM_NONE, - //PALETTES(IronCrown), + .frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, + BACK_PIC(IronCrown, 64, 64), + .backPicYOffset = 2, + .backAnimId = BACK_ANIM_NONE, + PALETTES(IronCrown), //ICON(IronCrown, 0), //FOOTPRINT(IronCrown) LEARNSETS(IronCrown), diff --git a/src/item_use.c b/src/item_use.c index f9badda639..e711f8cd22 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -1192,7 +1192,7 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon) cannotUse = TRUE; break; case EFFECT_ITEM_SET_FOCUS_ENERGY: - if (gBattleMons[gBattlerInMenuId].status2 & STATUS2_FOCUS_ENERGY) + if (gBattleMons[gBattlerInMenuId].status2 & STATUS2_FOCUS_ENERGY_ANY) cannotUse = TRUE; break; case EFFECT_ITEM_SET_MIST: diff --git a/src/overworld.c b/src/overworld.c index 5e11c04d5f..8bad752cc0 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -399,10 +399,14 @@ void Overworld_ResetStateAfterDigEscRope(void) } #if B_RESET_FLAGS_VARS_AFTER_WHITEOUT == TRUE - void Overworld_ResetBattleFlagsAndVars(void) +void Overworld_ResetBattleFlagsAndVars(void) { - #if VAR_TERRAIN != 0 - VarSet(VAR_TERRAIN, 0); + #if B_VAR_TERRAIN != 0 + VarSet(B_VAR_TERRAIN, 0); + #endif + + #if B_VAR_TERRAIN_TIMER != 0 + VarSet(B_VAR_TERRAIN_TIMER, 0); #endif #if B_VAR_WILD_AI_FLAGS != 0 diff --git a/src/pokedex_area_screen.c b/src/pokedex_area_screen.c index d5aa3f96c9..1c155bf5ec 100755 --- a/src/pokedex_area_screen.c +++ b/src/pokedex_area_screen.c @@ -107,9 +107,11 @@ static void CreateAreaUnknownSprites(void); static void Task_HandlePokedexAreaScreenInput(u8); static void ResetPokedexAreaMapBg(void); static void DestroyAreaScreenSprites(void); +static void LoadHGSSScreenSelectBarSubmenu(void); static const u32 sAreaGlow_Pal[] = INCBIN_U32("graphics/pokedex/area_glow.gbapal"); static const u32 sAreaGlow_Gfx[] = INCBIN_U32("graphics/pokedex/area_glow.4bpp.lz"); +static const u32 sPokedexPlusHGSS_ScreenSelectBarSubmenu_Tilemap[] = INCBIN_U32("graphics/pokedex/hgss/SelectBar.bin.lz"); static const u16 sSpeciesHiddenFromAreaScreen[] = { SPECIES_WYNAUT }; @@ -639,6 +641,9 @@ static void Task_ShowPokedexAreaScreen(u8 taskId) BeginNormalPaletteFade(PALETTES_ALL & ~(0x14), 0, 16, 0, RGB_BLACK); break; case 10: + if (POKEDEX_PLUS_HGSS) + LoadHGSSScreenSelectBarSubmenu(); + SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG0 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_BG0 | BLDCNT_TGT2_ALL); StartAreaGlow(); ShowBg(2); @@ -806,3 +811,9 @@ static void CreateAreaUnknownSprites(void) } } } + +static void LoadHGSSScreenSelectBarSubmenu(void) +{ + CopyToBgTilemapBuffer(1, sPokedexPlusHGSS_ScreenSelectBarSubmenu_Tilemap, 0, 0); + CopyBgTilemapBufferToVram(1); +} diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index 9f02c46cf9..0e3817d4ac 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -531,7 +531,6 @@ static void Task_LoadSizeScreen(u8); static void Task_HandleSizeScreenInput(u8); static void Task_SwitchScreensFromSizeScreen(u8); static void LoadScreenSelectBarMain(u16); -static void LoadScreenSelectBarSubmenu(u16); static void HighlightScreenSelectBarItem(u8, u16); static void Task_HandleCaughtMonPageInput(u8); static void Task_ExitCaughtMonPage(u8); @@ -4032,7 +4031,6 @@ static void Task_LoadAreaScreen(u8 taskId) } break; case 1: - LoadScreenSelectBarSubmenu(0xD); LoadPokedexBgPalette(sPokedexView->isSearchResults); SetGpuReg(REG_OFFSET_BG1CNT, BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(13) | BGCNT_16COLOR | BGCNT_TXT256x256); gMain.state++; @@ -4087,12 +4085,6 @@ static void LoadScreenSelectBarMain(u16 unused) CopyBgTilemapBufferToVram(1); } -static void LoadScreenSelectBarSubmenu(u16 unused) -{ - CopyToBgTilemapBuffer(1, sPokedexPlusHGSS_ScreenSelectBarSubmenu_Tilemap, 0, 0); - CopyBgTilemapBufferToVram(1); -} - static void UNUSED HighlightScreenSelectBarItem(u8 selectedScreen, u16 unused) { u8 i; diff --git a/src/pokemon.c b/src/pokemon.c index dd9b4bd74c..820a20413b 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1927,7 +1927,7 @@ void SetMultiuseSpriteTemplateToTrainerBack(u16 trainerPicId, u8 battlerPosition gMultiuseSpriteTemplate = gMonSpritesGfxPtr->templates[battlerPosition]; else gMultiuseSpriteTemplate = gBattlerSpriteTemplates[battlerPosition]; - gMultiuseSpriteTemplate.anims = gTrainerSprites[trainerPicId].animation; + gMultiuseSpriteTemplate.anims = sAnims_Trainer; } } @@ -1939,7 +1939,7 @@ void SetMultiuseSpriteTemplateToTrainerFront(u16 trainerPicId, u8 battlerPositio gMultiuseSpriteTemplate = gBattlerSpriteTemplates[battlerPosition]; gMultiuseSpriteTemplate.paletteTag = trainerPicId; - gMultiuseSpriteTemplate.anims = gTrainerSprites[trainerPicId].animation; + gMultiuseSpriteTemplate.anims = sAnims_Trainer; } static void EncryptBoxMon(struct BoxPokemon *boxMon) @@ -5151,27 +5151,19 @@ u8 CanLearnTeachableMove(u16 species, u16 move) switch (move) { case MOVE_BADDY_BAD: - case MOVE_BLAST_BURN: case MOVE_BOUNCY_BUBBLE: case MOVE_BUZZY_BUZZ: - case MOVE_DRACO_METEOR: case MOVE_DRAGON_ASCENT: - case MOVE_FIRE_PLEDGE: case MOVE_FLOATY_FALL: case MOVE_FREEZY_FROST: - case MOVE_FRENZY_PLANT: case MOVE_GLITZY_GLOW: - case MOVE_GRASS_PLEDGE: - case MOVE_HYDRO_CANNON: case MOVE_RELIC_SONG: case MOVE_SAPPY_SEED: case MOVE_SECRET_SWORD: case MOVE_SIZZLY_SLIDE: case MOVE_SPARKLY_SWIRL: case MOVE_SPLISHY_SPLASH: - case MOVE_STEEL_BEAM: case MOVE_VOLT_TACKLE: - case MOVE_WATER_PLEDGE: case MOVE_ZIPPY_ZAP: return FALSE; default: diff --git a/src/trainer_pokemon_sprites.c b/src/trainer_pokemon_sprites.c index cfa013620a..06b21d05cf 100644 --- a/src/trainer_pokemon_sprites.c +++ b/src/trainer_pokemon_sprites.c @@ -116,7 +116,7 @@ static void AssignSpriteAnimsTable(bool8 isTrainer) if (!isTrainer) sCreatingSpriteTemplate.anims = gAnims_MonPic; else - sCreatingSpriteTemplate.anims = gTrainerSprites[0].animation; + sCreatingSpriteTemplate.anims = sAnims_Trainer; } static u16 CreatePicSprite(u16 species, bool8 isShiny, u32 personality, bool8 isFrontPic, s16 x, s16 y, u8 paletteSlot, u16 paletteTag, bool8 isTrainer) diff --git a/test/battle/ability/sheer_force.c b/test/battle/ability/sheer_force.c index be115ee918..e50ead4bdb 100644 --- a/test/battle/ability/sheer_force.c +++ b/test/battle/ability/sheer_force.c @@ -28,8 +28,7 @@ SINGLE_BATTLE_TEST("Sheer Force boosts power, but removes secondary effects of m TURN { MOVE(opponent, MOVE_QUICK_ATTACK); MOVE(player, move); } else TURN { MOVE(player, move); } - if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK || gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE - || gMovesInfo[move].effect == EFFECT_METEOR_BEAM) { + if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK || gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE) { TURN { SKIP_TURN(player); } TURN { ; } } diff --git a/test/battle/ability/shield_dust.c b/test/battle/ability/shield_dust.c new file mode 100644 index 0000000000..4cd95de619 --- /dev/null +++ b/test/battle/ability/shield_dust.c @@ -0,0 +1,139 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Shield Dust blocks secondary effects") +{ + u16 move; + PARAMETRIZE { move = MOVE_NUZZLE; } + PARAMETRIZE { move = MOVE_INFERNO; } + PARAMETRIZE { move = MOVE_MORTAL_SPIN; } + PARAMETRIZE { move = MOVE_FAKE_OUT; } + PARAMETRIZE { move = MOVE_ROCK_TOMB; } + PARAMETRIZE { move = MOVE_SPIRIT_SHACKLE; } + PARAMETRIZE { move = MOVE_PSYCHIC_NOISE; } + + GIVEN { + ASSUME(MoveHasMoveEffectWithChance(MOVE_NUZZLE, MOVE_EFFECT_PARALYSIS, 100) == TRUE); + ASSUME(MoveHasMoveEffectWithChance(MOVE_INFERNO, MOVE_EFFECT_BURN, 100) == TRUE); + ASSUME(MoveHasMoveEffectWithChance(MOVE_MORTAL_SPIN, MOVE_EFFECT_POISON, 100) == TRUE); + ASSUME(MoveHasMoveEffectWithChance(MOVE_FAKE_OUT, MOVE_EFFECT_FLINCH, 100) == TRUE); + ASSUME(MoveHasMoveEffectWithChance(MOVE_ROCK_TOMB, MOVE_EFFECT_SPD_MINUS_1, 100) == TRUE); + ASSUME(MoveHasMoveEffectWithChance(MOVE_SPIRIT_SHACKLE, MOVE_EFFECT_PREVENT_ESCAPE, 100) == TRUE); + ASSUME(MoveHasMoveEffectWithChance(MOVE_PSYCHIC_NOISE, MOVE_EFFECT_PSYCHIC_NOISE, 100) == TRUE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_VIVILLON) { Ability(ABILITY_SHIELD_DUST); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + NONE_OF { + MESSAGE("Foe Vivillon is paralyzed! It may be unable to move!"); + MESSAGE("Foe Vivillon was burned!"); + MESSAGE("Foe Vivillon was poisoned!"); + MESSAGE("Foe Vivillon flinched!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + MESSAGE("Foe Vivillon was prevented from healing!"); + } + } THEN { // Can't find good way to test trapping + EXPECT(!(opponent->status2 & STATUS2_ESCAPE_PREVENTION)); + } +} + +SINGLE_BATTLE_TEST("Shield Dust does not block primary effects") +{ + u16 move; + PARAMETRIZE { move = MOVE_INFESTATION; } + PARAMETRIZE { move = MOVE_THOUSAND_ARROWS; } + PARAMETRIZE { move = MOVE_JAW_LOCK; } + PARAMETRIZE { move = MOVE_PAY_DAY; } + + GIVEN { + ASSUME(MoveHasMoveEffectWithChance(MOVE_INFESTATION, MOVE_EFFECT_WRAP, 0) == TRUE); + ASSUME(MoveHasMoveEffectWithChance(MOVE_THOUSAND_ARROWS, MOVE_EFFECT_SMACK_DOWN, 0) == TRUE); + ASSUME(MoveHasMoveEffectWithChance(MOVE_JAW_LOCK, MOVE_EFFECT_TRAP_BOTH, 0) == TRUE); + ASSUME(MoveHasMoveEffectWithChance(MOVE_PAY_DAY, MOVE_EFFECT_PAYDAY, 0) == TRUE); + ASSUME(MoveHasMoveEffectWithChance(MOVE_SMACK_DOWN, MOVE_EFFECT_SMACK_DOWN, 0) == TRUE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_VIVILLON) { Ability(ABILITY_SHIELD_DUST); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + switch (move) + { + case MOVE_INFESTATION: + MESSAGE("Foe Vivillon has been afflicted with an infestation by Wobbuffet!"); + break; + case MOVE_THOUSAND_ARROWS: + MESSAGE("Foe Vivillon fell straight down!"); + break; + case MOVE_JAW_LOCK: + MESSAGE("Neither Pokémon can run away!"); + break; + case MOVE_PAY_DAY: + MESSAGE("Coins scattered everywhere!"); + break; + } + } THEN { // Can't find good way to test trapping + if (move == MOVE_JAW_LOCK) { + EXPECT(opponent->status2 & STATUS2_ESCAPE_PREVENTION); + EXPECT(player->status2 & STATUS2_ESCAPE_PREVENTION); + } + } +} + +SINGLE_BATTLE_TEST("Shield Dust does not block self-targeting effects, primary or secondary") +{ + u16 move; + PARAMETRIZE { move = MOVE_POWER_UP_PUNCH; } + PARAMETRIZE { move = MOVE_RAPID_SPIN; } + PARAMETRIZE { move = MOVE_LEAF_STORM; } + PARAMETRIZE { move = MOVE_METEOR_ASSAULT; } + + GIVEN { + ASSUME(MoveHasMoveEffectSelf(MOVE_POWER_UP_PUNCH, MOVE_EFFECT_ATK_PLUS_1) == TRUE); + ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPIDSPIN) == TRUE); + ASSUME(MoveHasMoveEffectSelf(MOVE_LEAF_STORM, MOVE_EFFECT_SP_ATK_TWO_DOWN) == TRUE); + ASSUME(MoveHasMoveEffectSelf(MOVE_METEOR_ASSAULT, MOVE_EFFECT_RECHARGE) == TRUE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_VIVILLON) { Ability(ABILITY_SHIELD_DUST); } + } WHEN { + TURN { MOVE(player, move); } + if (move == MOVE_METEOR_ASSAULT) { + TURN { SKIP_TURN(player); } + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + switch (move) + { + case MOVE_POWER_UP_PUNCH: + case MOVE_RAPID_SPIN: + case MOVE_LEAF_STORM: + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + break; + case MOVE_METEOR_ASSAULT: // second turn + MESSAGE("Wobbuffet must recharge!"); + break; + } + } +} + +DOUBLE_BATTLE_TEST("Shield Dust does not block Sparkling Aria in doubles") +{ + KNOWN_FAILING; + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_VIVILLON) { Ability(ABILITY_SHIELD_DUST); Status1(STATUS1_BURN); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_SPARKLING_ARIA); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPARKLING_ARIA, playerLeft); + MESSAGE("Foe Vivillion's burn was healed."); + STATUS_ICON(opponentLeft, burn: TRUE); + } +} diff --git a/test/battle/ai.c b/test/battle/ai.c index 7d7da10947..7b9d07429e 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -292,16 +292,39 @@ AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking // Psychic and Solar Beam are chosen because user is holding Power Herb PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SOLAR_BEAM; } - // Psychic and Skull Bash are chosen because user is holding Power Herb - PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; - expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SKULL_BASH; } // Skull Bash is chosen because it's the most accurate and is holding Power Herb PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_SLAM; move4 = MOVE_DOUBLE_EDGE; expectedMove = MOVE_SKULL_BASH; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { HP(5); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GEODUDE) { Moves(move1, move2, move3, move4); Ability(abilityAtk); Item(holdItemAtk); } + } WHEN { + TURN { if (expectedMove2 == MOVE_NONE) { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); } + else {EXPECT_MOVES(opponent, expectedMove, expectedMove2); SCORE_EQ(opponent, expectedMove, expectedMove2); SEND_OUT(player, 1);} + } + } + SCENE { + MESSAGE("Wobbuffet fainted!"); + } +} + +AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking into account accuracy and move effect failing") +{ + u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE; + u16 expectedMove, expectedMove2 = MOVE_NONE; + u16 abilityAtk = ABILITY_NONE, holdItemAtk = ITEM_NONE; + + // Fiery Dance and Skull Bash are chosen because user is holding Power Herb + PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_FIERY_DANCE; move4 = MOVE_DOUBLE_EDGE; + expectedMove = MOVE_FIERY_DANCE; expectedMove2 = MOVE_SKULL_BASH; } // Crabhammer is chosen even if Skull Bash is more accurate, the user has no Power Herb PARAMETRIZE { abilityAtk = ABILITY_STURDY; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_SLAM; move4 = MOVE_CRABHAMMER; expectedMove = MOVE_CRABHAMMER; } + KNOWN_FAILING; GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_WOBBUFFET) { HP(5); } diff --git a/test/battle/crit_chance.c b/test/battle/crit_chance.c index 66ba62495f..4964767e8e 100644 --- a/test/battle/crit_chance.c +++ b/test/battle/crit_chance.c @@ -212,7 +212,6 @@ SINGLE_BATTLE_TEST("Signature items Leek and Lucky Punch increase the critical h u32 species; u32 item; - ASSUME(B_CRIT_CHANCE >= GEN_7); PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT); PARAMETRIZE { species = SPECIES_FARFETCHD; item = ITEM_LEEK; } @@ -250,3 +249,93 @@ SINGLE_BATTLE_TEST("Dire Hit increases a battler's critical hit chance by 2 stag MESSAGE("A critical hit!"); } } + +SINGLE_BATTLE_TEST("Focus Energy increases critical hit ratio by two") +{ + PASSES_RANDOMLY(8, 8, RNG_CRITICAL_HIT); + GIVEN { + ASSUME(gMovesInfo[MOVE_SLASH].criticalHitStage == 1); + ASSUME(gMovesInfo[MOVE_FOCUS_ENERGY].effect == EFFECT_FOCUS_ENERGY); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FOCUS_ENERGY); } + TURN { MOVE(player, MOVE_SLASH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_ENERGY, player); + MESSAGE("Wobbuffet is getting pumped!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SLASH, player); + MESSAGE("A critical hit!"); + } +} + +SINGLE_BATTLE_TEST("Dragon Cheer fails in a single battle") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_DRAGON_CHEER].effect == EFFECT_DRAGON_CHEER); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_DRAGON_CHEER); } + } SCENE { + MESSAGE("But it failed!"); + } +} + +DOUBLE_BATTLE_TEST("Dragon Cheer increases critical hit ratio by one on non Dragon types") +{ + PASSES_RANDOMLY(1, 8, RNG_CRITICAL_HIT); + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].criticalHitStage == 0); + ASSUME(gMovesInfo[MOVE_DRAGON_CHEER].effect == EFFECT_DRAGON_CHEER); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_DRAGON_CHEER, target: playerRight); MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_CHEER, playerLeft); + MESSAGE("Wynaut is getting pumped!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerRight); + MESSAGE("A critical hit!"); + } +} + +DOUBLE_BATTLE_TEST("Dragon Cheer increases critical hit ratio by two on Dragon types") +{ + PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT); + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].criticalHitStage == 0); + ASSUME(gMovesInfo[MOVE_DRAGON_CHEER].effect == EFFECT_DRAGON_CHEER); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_DRATINI); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_DRAGON_CHEER, target: playerRight); MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_CHEER, playerLeft); + MESSAGE("Dratini is getting pumped!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerRight); + MESSAGE("A critical hit!"); + } +} + +DOUBLE_BATTLE_TEST("Dragon Cheer fails if critical hit stage was already increased by Focus Energy") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_SLASH].criticalHitStage == 1); + ASSUME(gMovesInfo[MOVE_FOCUS_ENERGY].effect == EFFECT_FOCUS_ENERGY); + ASSUME(gMovesInfo[MOVE_DRAGON_CHEER].effect == EFFECT_DRAGON_CHEER); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_FOCUS_ENERGY); MOVE(playerRight, MOVE_DRAGON_CHEER, target: playerLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_ENERGY, playerLeft); + MESSAGE("But it failed!"); + } +} diff --git a/test/battle/move_effect/meteor_beam.c b/test/battle/move_effect/meteor_beam.c deleted file mode 100644 index d9ae6fb99e..0000000000 --- a/test/battle/move_effect/meteor_beam.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -ASSUMPTIONS -{ - ASSUME(gMovesInfo[MOVE_ELECTRO_SHOT].effect == EFFECT_METEOR_BEAM); -} - -SINGLE_BATTLE_TEST("Electro Shot needs a charging Turn") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_ELECTRO_SHOT); } - TURN { SKIP_TURN(player); } - } SCENE { - // Charging turn - ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player); - MESSAGE("Wobbuffet absorbed electricity!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Sp. Atk rose!"); - // Attack turn - MESSAGE("Wobbuffet used Electro Shot!"); - } -} - -SINGLE_BATTLE_TEST("Electro Shot doesn't need to charge when it's raining") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, MOVE_RAIN_DANCE); MOVE(player, MOVE_ELECTRO_SHOT); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, opponent); - ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player); - MESSAGE("Wobbuffet absorbed electricity!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Sp. Atk rose!"); - MESSAGE("Wobbuffet used Electro Shot!"); - } -} - -SINGLE_BATTLE_TEST("Electro Shot doesn't need to charge with Power Herb") -{ - GIVEN { - PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); } - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_ELECTRO_SHOT); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player); - MESSAGE("Wobbuffet absorbed electricity!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - MESSAGE("Wobbuffet's Sp. Atk rose!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); - MESSAGE("Wobbuffet became fully charged due to its Power Herb!"); - MESSAGE("Wobbuffet used Electro Shot!"); - } -} diff --git a/test/battle/move_effect/semi_invulnerable_moves.c b/test/battle/move_effect/semi_invulnerable_moves.c new file mode 100644 index 0000000000..97760225d1 --- /dev/null +++ b/test/battle/move_effect/semi_invulnerable_moves.c @@ -0,0 +1,250 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_FLY].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_FLY].argument)) == STATUS3_ON_AIR); + ASSUME(gMovesInfo[MOVE_DIG].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_DIG].argument)) == STATUS3_UNDERGROUND); + ASSUME(gMovesInfo[MOVE_BOUNCE].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_BOUNCE].argument)) == STATUS3_ON_AIR); + ASSUME(gMovesInfo[MOVE_DIVE].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_DIVE].argument)) == STATUS3_UNDERWATER); + ASSUME(gMovesInfo[MOVE_PHANTOM_FORCE].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_PHANTOM_FORCE].argument)) == STATUS3_PHANTOM_FORCE); + ASSUME(gMovesInfo[MOVE_SHADOW_FORCE].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_SHADOW_FORCE].argument)) == STATUS3_PHANTOM_FORCE); +} + +SINGLE_BATTLE_TEST("Semi-invulnerable moves make the user semi-invulnerable turn 1, then strike turn 2") +{ + u16 move; + + PARAMETRIZE { move = MOVE_FLY; } + PARAMETRIZE { move = MOVE_DIG; } + PARAMETRIZE { move = MOVE_BOUNCE; } + PARAMETRIZE { move = MOVE_DIVE; } + PARAMETRIZE { move = MOVE_PHANTOM_FORCE; } + PARAMETRIZE { move = MOVE_SHADOW_FORCE; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); MOVE(opponent, MOVE_AERIAL_ACE); } + TURN { SKIP_TURN(player); } + } SCENE { + // Charging turn + if (B_UPDATED_MOVE_DATA >= GEN_5) + { + switch (move) + { + case MOVE_FLY: + NOT MESSAGE("Wobbuffet flew up high!"); + MESSAGE("Wobbuffet used Fly!"); + break; + case MOVE_DIG: + NOT MESSAGE("Wobbuffet dug a hole!"); + MESSAGE("Wobbuffet used Dig!"); + break; + case MOVE_BOUNCE: + NOT MESSAGE("Wobbuffet sprang up!"); + MESSAGE("Wobbuffet used Bounce!"); + break; + case MOVE_DIVE: + NOT MESSAGE("Wobbuffet hid underwater!"); + MESSAGE("Wobbuffet used Dive!"); + break; + case MOVE_PHANTOM_FORCE: + NOT MESSAGE("Wobbuffet vanished instantly!"); + MESSAGE("Wobbuffet used PhantomForce!"); + break; + case MOVE_SHADOW_FORCE: + NOT MESSAGE("Wobbuffet vanished instantly!"); + MESSAGE("Wobbuffet used Shadow Force!"); + break; + } + } else { + ANIMATION(ANIM_TYPE_MOVE, move, player); + } + if (B_UPDATED_MOVE_DATA < GEN_5) + { + switch (move) + { + case MOVE_FLY: + MESSAGE("Wobbuffet flew up high!"); + break; + case MOVE_DIG: + MESSAGE("Wobbuffet dug a hole!"); + break; + case MOVE_BOUNCE: + MESSAGE("Wobbuffet sprang up!"); + break; + case MOVE_DIVE: + MESSAGE("Wobbuffet hid underwater!"); + break; + case MOVE_PHANTOM_FORCE: + case MOVE_SHADOW_FORCE: + MESSAGE("Wobbuffet vanished instantly!"); + break; + } + } + else + ANIMATION(ANIM_TYPE_MOVE, move, player); + + // Aerial Ace cannot miss unless the target is semi-invulnerable + MESSAGE("Foe Wobbuffet used Aerial Ace!"); + MESSAGE("Foe Wobbuffet's attack missed!"); + // Attack turn + switch (move) + { + case MOVE_FLY: + MESSAGE("Wobbuffet used Fly!"); + break; + case MOVE_DIG: + MESSAGE("Wobbuffet used Dig!"); + break; + case MOVE_BOUNCE: + MESSAGE("Wobbuffet used Bounce!"); + break; + case MOVE_DIVE: + MESSAGE("Wobbuffet used Dive!"); + break; + case MOVE_PHANTOM_FORCE: + MESSAGE("Wobbuffet used PhantomForce!"); + break; + case MOVE_SHADOW_FORCE: + MESSAGE("Wobbuffet used Shadow Force!"); + break; + } + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Semi-invulnerable moves don't need to charge with Power Herb") +{ + u16 move; + + PARAMETRIZE { move = MOVE_FLY; } + PARAMETRIZE { move = MOVE_DIG; } + PARAMETRIZE { move = MOVE_BOUNCE; } + PARAMETRIZE { move = MOVE_DIVE; } + PARAMETRIZE { move = MOVE_PHANTOM_FORCE; } + PARAMETRIZE { move = MOVE_SHADOW_FORCE; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + // Charging turn + if (B_UPDATED_MOVE_DATA >= GEN_5) + { + switch (move) + { + case MOVE_FLY: + NOT MESSAGE("Wobbuffet flew up high!"); + MESSAGE("Wobbuffet used Fly!"); + break; + case MOVE_DIG: + NOT MESSAGE("Wobbuffet dug a hole!"); + MESSAGE("Wobbuffet used Dig!"); + break; + case MOVE_BOUNCE: + NOT MESSAGE("Wobbuffet sprang up!"); + MESSAGE("Wobbuffet used Bounce!"); + break; + case MOVE_DIVE: + NOT MESSAGE("Wobbuffet hid underwater!"); + MESSAGE("Wobbuffet used Dive!"); + break; + case MOVE_PHANTOM_FORCE: + NOT MESSAGE("Wobbuffet vanished instantly!"); + MESSAGE("Wobbuffet used PhantomForce!"); + break; + case MOVE_SHADOW_FORCE: + NOT MESSAGE("Wobbuffet vanished instantly!"); + MESSAGE("Wobbuffet used Shadow Force!"); + break; + } + } else { + ANIMATION(ANIM_TYPE_MOVE, move, player); + } + if (B_UPDATED_MOVE_DATA < GEN_5) + { + switch (move) + { + case MOVE_FLY: + MESSAGE("Wobbuffet flew up high!"); + break; + case MOVE_DIG: + MESSAGE("Wobbuffet dug a hole!"); + break; + case MOVE_BOUNCE: + MESSAGE("Wobbuffet sprang up!"); + break; + case MOVE_DIVE: + MESSAGE("Wobbuffet hid underwater!"); + break; + case MOVE_PHANTOM_FORCE: + case MOVE_SHADOW_FORCE: + MESSAGE("Wobbuffet vanished instantly!"); + break; + } + } + else + ANIMATION(ANIM_TYPE_MOVE, move, player); + MESSAGE("Wobbuffet became fully charged due to its Power Herb!"); + if (B_UPDATED_MOVE_DATA < GEN_5) + { + switch (move) + { + case MOVE_FLY: + MESSAGE("Wobbuffet used Fly!"); + break; + case MOVE_DIG: + MESSAGE("Wobbuffet used Dig!"); + break; + case MOVE_BOUNCE: + MESSAGE("Wobbuffet used Bounce!"); + break; + case MOVE_DIVE: + MESSAGE("Wobbuffet used Dive!"); + break; + case MOVE_PHANTOM_FORCE: + MESSAGE("Wobbuffet used PhantomForce!"); + break; + case MOVE_SHADOW_FORCE: + MESSAGE("Wobbuffet used Shadow Force!"); + break; + } + } + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent); + } +} + +// No way to apply this test with Shadow Force/Phantom Force +SINGLE_BATTLE_TEST("Semi-invulnerable moves apply a status that won't block certain moves") +{ + u16 move, opMove; + + PARAMETRIZE { move = MOVE_FLY; opMove = MOVE_SKY_UPPERCUT; } + PARAMETRIZE { move = MOVE_DIG; opMove = MOVE_EARTHQUAKE; } + PARAMETRIZE { move = MOVE_BOUNCE; opMove = MOVE_THUNDER; } + PARAMETRIZE { move = MOVE_DIVE; opMove = MOVE_SURF; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); MOVE(opponent, opMove); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + ANIMATION(ANIM_TYPE_MOVE, opMove, opponent); + HP_BAR(player); + } +} diff --git a/test/battle/move_effect/solar_beam.c b/test/battle/move_effect/solar_beam.c deleted file mode 100644 index 11edb61ba0..0000000000 --- a/test/battle/move_effect/solar_beam.c +++ /dev/null @@ -1,167 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -ASSUMPTIONS -{ - ASSUME(gMovesInfo[MOVE_SOLAR_BEAM].effect == EFFECT_SOLAR_BEAM); - ASSUME(gMovesInfo[MOVE_SOLAR_BLADE].effect == EFFECT_SOLAR_BEAM); -} - -SINGLE_BATTLE_TEST("Solar Beam and Solar Blade can be used instantly in Sunlight") -{ - u32 move; - PARAMETRIZE { move = MOVE_SOLAR_BEAM; } - PARAMETRIZE { move = MOVE_SOLAR_BLADE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, MOVE_SUNNY_DAY); MOVE(player, move); } - } SCENE { - NOT MESSAGE("Wobbuffet took in sunlight!"); - } -} - -SINGLE_BATTLE_TEST("Solar Beam's power is halved in Rain", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_RAIN_DANCE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Blade's power is halved in Rain", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_RAIN_DANCE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WYNAUT); - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Beam's power is halved in a Sandstorm", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SANDSTORM; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Blade's power is halved in a Sandstorm", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SANDSTORM; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Beam's power is halved in Hail", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_HAIL; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Blade's power is halved in Hail", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_HAIL; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Beam's power is halved in Snow", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SNOWSCAPE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} - -SINGLE_BATTLE_TEST("Solar Blade's power is halved in Snow", s16 damage) -{ - u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SNOWSCAPE; } - GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WYNAUT); - } WHEN { - TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } - TURN { SKIP_TURN(player); } - } SCENE { - HP_BAR(opponent, captureDamage: &results[i].damage); - } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); - } -} diff --git a/test/battle/move_effect/two_turn_moves.c b/test/battle/move_effect/two_turn_moves.c new file mode 100644 index 0000000000..3011656756 --- /dev/null +++ b/test/battle/move_effect/two_turn_moves.c @@ -0,0 +1,449 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_RAZOR_WIND].effect == EFFECT_TWO_TURNS_ATTACK); + ASSUME(gMovesInfo[MOVE_SKULL_BASH].effect == EFFECT_TWO_TURNS_ATTACK); + ASSUME(MoveHasMoveEffectSelf(MOVE_SKULL_BASH, MOVE_EFFECT_DEF_PLUS_1) == TRUE); + ASSUME(gMovesInfo[MOVE_SKY_ATTACK].effect == EFFECT_TWO_TURNS_ATTACK); + + // Solar Beam - check for sun + ASSUME(gMovesInfo[MOVE_SOLAR_BEAM].effect == EFFECT_SOLAR_BEAM); + ASSUME(HIHALF(gMovesInfo[MOVE_SOLAR_BLADE].argument) == B_WEATHER_SUN); + ASSUME(gMovesInfo[MOVE_SOLAR_BLADE].effect == EFFECT_SOLAR_BEAM); + ASSUME(HIHALF(gMovesInfo[MOVE_SOLAR_BLADE].argument) == B_WEATHER_SUN); + + // Electro shot - check for rain + ASSUME(HIHALF(gMovesInfo[MOVE_ELECTRO_SHOT].argument) == B_WEATHER_RAIN); + ASSUME(gMovesInfo[MOVE_ELECTRO_SHOT].effect == EFFECT_TWO_TURNS_ATTACK); + ASSUME(MoveHasMoveEffectSelf(MOVE_ELECTRO_SHOT, MOVE_EFFECT_SP_ATK_PLUS_1) == TRUE); +} + +SINGLE_BATTLE_TEST("Razor Wind needs a charging turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_RAZOR_WIND); } + TURN { SKIP_TURN(player); } + } SCENE { + // Charging turn + if (B_UPDATED_MOVE_DATA >= GEN_5) { + NOT MESSAGE("Wobbuffet whipped up a whirlwind!"); + MESSAGE("Wobbuffet used Razor Wind!"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player); + } + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet whipped up a whirlwind!"); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player); + // Attack turn + MESSAGE("Wobbuffet used Razor Wind!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Razor Wind doesn't need to charge with Power Herb") +{ + KNOWN_FAILING; + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_RAZOR_WIND); } + } SCENE { + if (B_UPDATED_MOVE_DATA >= GEN_5) { + NOT MESSAGE("Wobbuffet whipped up a whirlwind!"); + MESSAGE("Wobbuffet used Razor Wind!"); + } else + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player); + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet whipped up a whirlwind!"); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet became fully charged due to its Power Herb!"); + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet used Razor Wind!"); + // For some reason, this breaks with and only with Razor Wind... + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Skull Bash needs a charging turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SKULL_BASH); } + TURN { SKIP_TURN(player); } + } SCENE { + // Charging turn + if (B_UPDATED_MOVE_DATA >= GEN_5) { + NOT MESSAGE("Wobbuffet lowered its head!"); + MESSAGE("Wobbuffet used Skull Bash!"); + } else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player); + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet lowered its head!"); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Defense rose!"); + // Attack turn + MESSAGE("Wobbuffet used Skull Bash!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Skull Bash doesn't need to charge with Power Herb") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SKULL_BASH); } + } SCENE { + if (B_UPDATED_MOVE_DATA >= GEN_5) { + NOT MESSAGE("Wobbuffet lowered its head!"); + MESSAGE("Wobbuffet used Skull Bash!"); + } else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player); + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet lowered its head!"); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Defense rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet became fully charged due to its Power Herb!"); + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet used Skull Bash!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKULL_BASH, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Sky Attack needs a charging turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SKY_ATTACK); } + TURN { SKIP_TURN(player); } + } SCENE { + // Charging turn + if (B_UPDATED_MOVE_DATA >= GEN_5) { + NONE_OF { + MESSAGE("Wobbuffet became cloaked in a harsh light!"); + MESSAGE("Wobbuffet is glowing!"); + } + MESSAGE("Wobbuffet used Sky Attack!"); + } else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player); + if (B_UPDATED_MOVE_DATA < GEN_4) + MESSAGE("Wobbuffet is glowing!"); + else if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet became cloaked in a harsh light!"); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player); + // Attack turn + MESSAGE("Wobbuffet used Sky Attack!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Sky Attack doesn't need to charge with Power Herb") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SKY_ATTACK); } + } SCENE { + if (B_UPDATED_MOVE_DATA >= GEN_5) { + NONE_OF { + MESSAGE("Wobbuffet became cloaked in a harsh light!"); + MESSAGE("Wobbuffet is glowing!"); + } + MESSAGE("Wobbuffet used Sky Attack!"); + } else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player); + if (B_UPDATED_MOVE_DATA < GEN_4) + MESSAGE("Wobbuffet is glowing!"); + else if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet became cloaked in a harsh light!"); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet became fully charged due to its Power Herb!"); + if (B_UPDATED_MOVE_DATA < GEN_5) + MESSAGE("Wobbuffet used Sky Attack!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_ATTACK, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Solar Beam and Solar Blade can be used instantly in Sunlight") +{ + u32 move1, move2; + PARAMETRIZE { move1 = MOVE_SPLASH; move2 = MOVE_SOLAR_BEAM; } + PARAMETRIZE { move1 = MOVE_SUNNY_DAY; move2 = MOVE_SOLAR_BEAM; } + PARAMETRIZE { move1 = MOVE_SPLASH; move2 = MOVE_SOLAR_BLADE; } + PARAMETRIZE { move1 = MOVE_SUNNY_DAY; move2 = MOVE_SOLAR_BLADE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move1); MOVE(player, move2); } + TURN { SKIP_TURN(player); } + } SCENE { + if (move1 == MOVE_SUNNY_DAY) { + NOT MESSAGE("Wobbuffet took in sunlight!"); + } else { + if (move2 == MOVE_SOLAR_BEAM) { + if (B_UPDATED_MOVE_DATA >= GEN_5) + { + MESSAGE("Wobbuffet used Solar Beam!"); + MESSAGE("Wobbuffet took in sunlight!"); + ANIMATION(ANIM_TYPE_MOVE, move2, player); + } else { + NOT MESSAGE("Wobbuffet used Solar Beam!"); + ANIMATION(ANIM_TYPE_MOVE, move2, player); + MESSAGE("Wobbuffet took in sunlight!"); + } + MESSAGE("Wobbuffet used Solar Beam!"); + } else { + if (B_UPDATED_MOVE_DATA >= GEN_5) { + MESSAGE("Wobbuffet used Solar Blade!"); + MESSAGE("Wobbuffet took in sunlight!"); + ANIMATION(ANIM_TYPE_MOVE, move2, player); + } else { + NOT MESSAGE("Wobbuffet used Solar Blade!"); + ANIMATION(ANIM_TYPE_MOVE, move2, player); + MESSAGE("Wobbuffet took in sunlight!"); + } + MESSAGE("Wobbuffet used Solar Blade!"); + } + ANIMATION(ANIM_TYPE_MOVE, move2, player); + HP_BAR(opponent); + } + } +} + +SINGLE_BATTLE_TEST("Solar Beam's power is halved in Rain", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_RAIN_DANCE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Blade's power is halved in Rain", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_RAIN_DANCE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Beam's power is halved in a Sandstorm", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SANDSTORM; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Blade's power is halved in a Sandstorm", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SANDSTORM; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Beam's power is halved in Hail", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_HAIL; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Blade's power is halved in Hail", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_HAIL; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Beam's power is halved in Snow", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BEAM); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Blade's power is halved in Snow", s16 damage) +{ + u16 move; + PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_SOLAR_BLADE); } + TURN { SKIP_TURN(player); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Electro Shot needs a charging Turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ELECTRO_SHOT); } + TURN { SKIP_TURN(player); } + } SCENE { + // Charging turn + MESSAGE("Wobbuffet used Electro Shot!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player); + MESSAGE("Wobbuffet absorbed electricity!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Sp. Atk rose!"); + // Attack turn + MESSAGE("Wobbuffet used Electro Shot!"); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Electro Shot doesn't need to charge when it's raining") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_RAIN_DANCE); MOVE(player, MOVE_ELECTRO_SHOT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_RAIN_DANCE, opponent); + MESSAGE("Wobbuffet used Electro Shot!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player); + MESSAGE("Wobbuffet absorbed electricity!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Sp. Atk rose!"); + NONE_OF { + MESSAGE("Wobbuffet used Electro Shot!"); + } + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Electro Shot doesn't need to charge with Power Herb") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ELECTRO_SHOT); } + } SCENE { + MESSAGE("Wobbuffet used Electro Shot!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_SHOT, player); + MESSAGE("Wobbuffet absorbed electricity!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Sp. Atk rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet became fully charged due to its Power Herb!"); + NONE_OF { + MESSAGE("Wobbuffet used Electro Shot!"); + } + HP_BAR(opponent); + } +}