Merge branch 'master' of https://github.com/rh-hideout/pokeemerald-expansion into rh-hideout-master

This commit is contained in:
RoamerX 2025-11-21 21:38:17 +08:00
commit 607e09b2cc
127 changed files with 2597 additions and 1390 deletions

View File

@ -459,6 +459,25 @@
"contributions": [
"code"
]
},
{
"login": "fdeblasio",
"name": "Frank DeBlasio",
"avatar_url": "https://avatars.githubusercontent.com/u/35279583?v=4",
"profile": "https://github.com/fdeblasio",
"contributions": [
"code"
]
},
{
"login": "leo60228",
"name": "leo60228",
"avatar_url": "https://avatars.githubusercontent.com/u/8355305?v=4",
"profile": "https://vriska.dev",
"contributions": [
"doc",
"data"
]
}
],
"contributorsPerLine": 7,

1
.gitignore vendored
View File

@ -44,6 +44,7 @@ prefabs.json
/pokeemerald-*.png
src/data/map_group_count.h
include/constants/heal_locations.h
include/constants/script_commands.h
tools/trainerproc/trainerproc
src/data/battle_partners.h
src/data/trainers.h

View File

@ -76,6 +76,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="http://hashtagmarky.github.io"><img src="https://avatars.githubusercontent.com/u/143505183?v=4?s=100" width="100px;" alt="Marky"/><br /><sub><b>Marky</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=HashtagMarky" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MandL27"><img src="https://avatars.githubusercontent.com/u/10366615?v=4?s=100" width="100px;" alt="MandL27"/><br /><sub><b>MandL27</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=MandL27" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cawtds"><img src="https://avatars.githubusercontent.com/u/38510667?v=4?s=100" width="100px;" alt="cawtds"/><br /><sub><b>cawtds</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=cawtds" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fdeblasio"><img src="https://avatars.githubusercontent.com/u/35279583?v=4?s=100" width="100px;" alt="Frank DeBlasio"/><br /><sub><b>Frank DeBlasio</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=fdeblasio" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://vriska.dev"><img src="https://avatars.githubusercontent.com/u/8355305?v=4?s=100" width="100px;" alt="leo60228"/><br /><sub><b>leo60228</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=leo60228" title="Documentation">📖</a> <a href="#data-leo60228" title="Data">🔣</a></td>
</tr>
</tbody>
<tfoot>

View File

@ -1841,7 +1841,6 @@
.macro setmoveeffect effect:req
sethword sMOVE_EFFECT, \effect
sethword sSAVED_MOVE_EFFECT, \effect
.endm
.macro sethword dst:req, value:req

View File

@ -169,7 +169,10 @@
.endm
@ Copies the value of source into destination.
.macro copyvar destination:req, source:req
.macro copyvar destination:req, source:req, warn=TRUE
.if \warn && !((\source >= VARS_START && \source <= VARS_END) || (\source >= SPECIAL_VARS_START && \source <= SPECIAL_VARS_END))
.warning "copyvar with a value that is not a VAR_ constant; did you mean setvar instead?"
.endif
.byte SCR_OP_COPYVAR
.2byte \destination
.2byte \source

View File

@ -255,23 +255,23 @@ gBattleAnimMove_Tailwind::
createvisualtask AnimTask_TranslateMonEllipticalRespectSide, 2, ANIM_ATTACKER, 24, 6, 4, 4
createvisualtask AnimTask_TraceMonBlended, 2, 0, 4, 7, 10
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 10, 2304, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 10, 2304, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 90, 2048, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 90, 2048, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 50, 2560, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 50, 2560, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 20, 2304, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 20, 2304, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 70, 1984, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 70, 1984, 96, 1
delay 12
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 0, 2816, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 0, 2816, 96, 1
delay 10
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 60, 2560, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 60, 2560, 96, 1
waitforvisualfinish
stopsound
call UnsetHighSpeedBg
@ -285,23 +285,23 @@ gBattleAnimGeneral_Tailwind::
playsewithpan SE_M_GUST, SOUND_PAN_ATTACKER
call SetHighSpeedBg
setalpha 12, 8
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 10, 2304, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 10, 2304, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 90, 2048, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 90, 2048, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 50, 2560, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 50, 2560, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 20, 2304, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 20, 2304, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 70, 1984, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 70, 1984, 96, 1
delay 12
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 0, 2816, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 0, 2816, 96, 1
delay 10
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 60, 2560, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 60, 2560, 96, 1
waitforvisualfinish
stopsound
call UnsetHighSpeedBg
@ -11932,7 +11932,6 @@ gBattleAnimMove_PrismaticLaser::
loadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT @charge animation
loadspritegfx ANIM_TAG_TEAL_ALERT @straight lines
loadspritegfx ANIM_TAG_GREEN_SPIKE @needle arm animation
loadspritegfx ANIM_TAG_NEEDLE @sting
monbg ANIM_ATTACKER
setalpha 14, 8
createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_BG, 1, 0, 16, RGB_BLACK
@ -11961,6 +11960,7 @@ gBattleAnimMove_PrismaticLaser::
unloadspritegfx ANIM_TAG_GREEN_SPIKE
unloadspritegfx ANIM_TAG_ICE_CHUNK
unloadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT
loadspritegfx ANIM_TAG_NEEDLE @sting
delay 30
createvisualtask AnimTask_HorizontalShake, 5, (MAX_BATTLERS_COUNT + 1), 10, 0x32
createvisualtask AnimTask_HorizontalShake, 5, MAX_BATTLERS_COUNT, 10, 0x32
@ -15059,6 +15059,7 @@ gBattleAnimMove_Poltergeist::
waitbgfadein
clearmonbg 0x3
blendoff
unloadspritegfx ANIM_TAG_ITEM_BAG
end
@Credits to Skeli
@ -32015,7 +32016,8 @@ gBattleAnimGeneral_Rainbow::
createvisualtask AnimTask_BlendBattleAnimPal, 10, (F_PAL_BG | F_PAL_BATTLERS_2), 1, 6, 0, RGB_WHITE
waitforvisualfinish
delay 30
fadetobg BG_RAINBOW
goto SetRainbowBackground
AnimGeneral_RainbowContinue:
panse_adjustnone SE_M_ABSORB_2, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, +1, 0
delay 90
blendoff
@ -32023,6 +32025,14 @@ gBattleAnimGeneral_Rainbow::
waitbgfadein
clearmonbg ANIM_ATK_PARTNER
end
SetRainbowBackground:
createvisualtask AnimTask_GetAttackerSide, 2
jumprettrue SetRainbowBgOppoentSide
fadetobg BG_RAINBOW_PLAYER
goto AnimGeneral_RainbowContinue
SetRainbowBgOppoentSide:
fadetobg BG_RAINBOW_OPPONENT
goto AnimGeneral_RainbowContinue
gBattleAnimGeneral_SeaOfFire::
loadspritegfx ANIM_TAG_SMALL_EMBER
@ -32836,7 +32846,6 @@ gBattleAnimMove_SavageSpinOut::
blendoff
waitforvisualfinish
unloadspritegfx ANIM_TAG_STRING
unloadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT
loadspritegfx ANIM_TAG_COCOON
loadspritegfx ANIM_TAG_IMPACT @hit
delay 1
@ -33306,7 +33315,6 @@ FinishInfernoOverdrive:
delay 16
createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 8, 0, 16, 1
playsewithpan SE_M_MEGA_KICK2, SOUND_PAN_TARGET
unloadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT
createvisualtask AnimTask_ShakeMon, 5, ANIM_TARGET, 0, 2, 79, 1
call InfernoOverdriveExplosion
delay 6
@ -34486,7 +34494,6 @@ gBattleAnimMove_BlackHoleEclipse::
unloadspritegfx ANIM_TAG_VERTICAL_HEX @red
unloadspritegfx ANIM_TAG_SHADOW_BALL
unloadspritegfx ANIM_TAG_BLACK_BALL_2
unloadspritegfx ANIM_TAG_FOCUS_ENERGY
loadspritegfx ANIM_TAG_EXPLOSION_2
call BlackHoleEclipseExplosion
createvisualtask AnimTask_BlendBattleAnimPal, 10, (F_PAL_BG | F_PAL_BATTLERS_2), 1, 0, 16, RGB_WHITE @ bg to white pal
@ -36718,8 +36725,6 @@ gBattleAnimMove_ClangorousSoulblaze::
playsewithpan SE_SHINY, SOUND_PAN_ATTACKER
createsprite gClangorousSoulRedRingTemplate, ANIM_ATTACKER, 3, 0x0, 0x0, 0x0, 0x0
waitforvisualfinish
unloadspritegfx ANIM_TAG_HORSESHOE_SIDE_FIST
unloadspritegfx ANIM_TAG_SPARKLE_2 @stars
loadspritegfx ANIM_TAG_ROUND_SHADOW @ fly
playsewithpan SE_M_FLY, SOUND_PAN_ATTACKER
createsprite gClangoorousSoulblazeWhiteFlySpriteTemplate, ANIM_ATTACKER, 2, 0x0, 0x0, 0xd, 0x150
@ -37197,8 +37202,6 @@ SearingSunrazeSmashImpact:
loadspritegfx ANIM_TAG_CROSS_IMPACT @x
delay 0
unloadspritegfx ANIM_TAG_METEOR @superpower
unloadspritegfx ANIM_TAG_DRAGON_ASCENT @dragon ascent 1
unloadspritegfx ANIM_TAG_DRAGON_ASCENT_FOE @dragon ascent 2
createsprite gSearingSunrazeSmashCrossImpactSpriteTemplate, ANIM_TARGET, 2, 0x0, 0x0, 0x1, 0x24
playsewithpan SE_M_LEER, SOUND_PAN_TARGET
visible ANIM_ATTACKER
@ -37760,7 +37763,6 @@ gBattleAnimMove_SoulStealing7StarStrike::
call SoulStealingSevenStarStrikeBlueParalysis
waitforvisualfinish
visible ANIM_ATTACKER
unloadspritegfx ANIM_TAG_ROUND_SHADOW
loadspritegfx ANIM_TAG_SPARKLE_4 @ detect
loadspritegfx ANIM_TAG_EXPLOSION @ explosion
playsewithpan SE_M_DETECT, SOUND_PAN_ATTACKER

View File

@ -309,10 +309,10 @@ BattleScript_MoveSwitch:
waitmessage B_WAIT_TIME_SHORT
BattleScript_MoveSwitchOpenPartyScreen::
openpartyscreen BS_ATTACKER, BattleScript_MoveSwitchEnd
switchoutabilities BS_ATTACKER
waitstate
switchhandleorder BS_ATTACKER, 2
returntoball BS_ATTACKER, FALSE
switchoutabilities BS_ATTACKER
switchhandleorder BS_ATTACKER, 2
getswitchedmondata BS_ATTACKER
switchindataupdate BS_ATTACKER
hpthresholds BS_ATTACKER
@ -4099,10 +4099,10 @@ BattleScript_EffectBatonPass::
attackanimation
waitanimation
openpartyscreen BS_ATTACKER, BattleScript_ButItFailed
switchoutabilities BS_ATTACKER
waitstate
switchhandleorder BS_ATTACKER, 2
returntoball BS_ATTACKER, FALSE
switchoutabilities BS_ATTACKER
switchhandleorder BS_ATTACKER, 2
getswitchedmondata BS_ATTACKER
switchindataupdate BS_ATTACKER
hpthresholds BS_ATTACKER
@ -5418,11 +5418,11 @@ BattleScript_ActionSwitch::
end2
BattleScript_DoSwitchOut::
switchoutabilities BS_ATTACKER
undodynamax BS_ATTACKER
waitstate
returnatktoball
waitstate
switchoutabilities BS_ATTACKER
drawpartystatussummary BS_ATTACKER
switchhandleorder BS_ATTACKER, 1
getswitchedmondata BS_ATTACKER
@ -5724,9 +5724,9 @@ BattleScript_RoarSuccessRet:
attackanimation
waitanimation
BattleScript_RoarSuccessRet_Ret:
switchoutabilities BS_TARGET
returntoball BS_TARGET, FALSE
waitstate
switchoutabilities BS_TARGET
return
BattleScript_WeaknessPolicy::
@ -6834,10 +6834,11 @@ BattleScript_PowderMoveNoEffect::
pause B_WAIT_TIME_SHORT
jumpiftype BS_TARGET, TYPE_GRASS, BattleScript_PowderMoveNoEffectPrint
jumpifability BS_TARGET, ABILITY_OVERCOAT, BattleScript_PowderMoveNoEffectOvercoat
setlastuseditem BS_TARGET
printstring STRINGID_SAFETYGOGGLESPROTECTED
goto BattleScript_PowderMoveNoEffectWaitMsg
BattleScript_PowderMoveNoEffectOvercoat:
call BattleScript_AbilityPopUp
call BattleScript_AbilityPopUpTarget
BattleScript_PowderMoveNoEffectPrint:
printstring STRINGID_ITDOESNTAFFECT
BattleScript_PowderMoveNoEffectWaitMsg:
@ -7218,10 +7219,10 @@ BattleScript_EmergencyExit::
playanimation BS_SCRIPTING, B_ANIM_SLIDE_OFFSCREEN
waitanimation
openpartyscreen BS_SCRIPTING, BattleScript_EmergencyExitRet
switchoutabilities BS_SCRIPTING
waitstate
switchhandleorder BS_SCRIPTING, 2
returntoball BS_SCRIPTING, FALSE
switchoutabilities BS_SCRIPTING
switchhandleorder BS_SCRIPTING, 2
getswitchedmondata BS_SCRIPTING
switchindataupdate BS_SCRIPTING
hpthresholds BS_SCRIPTING
@ -7251,10 +7252,10 @@ BattleScript_EmergencyExitEnd2::
playanimation BS_ATTACKER, B_ANIM_SLIDE_OFFSCREEN
waitanimation
openpartyscreen BS_ATTACKER, BattleScript_EmergencyExitRetEnd2
switchoutabilities BS_ATTACKER
waitstate
switchhandleorder BS_ATTACKER, 2
returntoball BS_ATTACKER, FALSE
switchoutabilities BS_ATTACKER
switchhandleorder BS_ATTACKER, 2
getswitchedmondata BS_ATTACKER
switchindataupdate BS_ATTACKER
hpthresholds BS_ATTACKER
@ -9190,12 +9191,12 @@ BattleScript_EjectButtonActivates::
undodynamax BS_SCRIPTING
makeinvisible BS_SCRIPTING
openpartyscreen BS_SCRIPTING, BattleScript_EjectButtonEnd
waitstate
returntoball BS_SCRIPTING, FALSE
copybyte sSAVED_BATTLER, sBATTLER
switchoutabilities BS_SCRIPTING
copybyte sBATTLER, sSAVED_BATTLER
waitstate
switchhandleorder BS_SCRIPTING, 0x2
returntoball BS_SCRIPTING, FALSE
getswitchedmondata BS_SCRIPTING
switchindataupdate BS_SCRIPTING
hpthresholds BS_SCRIPTING
@ -9338,8 +9339,10 @@ BattleScript_EffectMaxMove::
BattleScript_EffectRaiseStatAllies::
savetarget
copybyte gBattlerTarget, gBattlerAttacker
copybyte sSAVED_STAT_CHANGER, sSTATCHANGER
BattleScript_RaiseSideStatsLoop:
jumpifabsent BS_TARGET, BattleScript_RaiseSideStatsIncrement
copybyte sSTATCHANGER, sSAVED_STAT_CHANGER
statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_RaiseSideStatsIncrement
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_RaiseSideStatsIncrement
printfromtable gStatUpStringIds
@ -9354,8 +9357,10 @@ BattleScript_RaiseSideStatsEnd:
BattleScript_EffectLowerStatFoes::
savetarget
copybyte sBATTLER, gBattlerTarget
copybyte sSAVED_STAT_CHANGER, sSTATCHANGER
BattleScript_LowerSideStatsLoop:
jumpifabsent BS_TARGET, BattleScript_LowerSideStatsIncrement
copybyte sSTATCHANGER, sSAVED_STAT_CHANGER
statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_LowerSideStatsIncrement
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_LowerSideStatsIncrement
printfromtable gStatDownStringIds

View File

@ -349,7 +349,11 @@ LilycoveCity_ContestLobby_EventScript_SetMasterContestType::
@ Functionally unused
LilycoveCity_ContestLobby_EventScript_SetDebug::
setflag FLAG_HIDE_LILYCOVE_MUSEUM_CURATOR
copyvar VAR_LILYCOVE_MUSEUM_2F_STATE, 1
#ifdef UBFIX
setvar VAR_LILYCOVE_MUSEUM_2F_STATE, 1
#else
copyvar VAR_LILYCOVE_MUSEUM_2F_STATE, 1, warn=FALSE
#endif
additem ITEM_CONTEST_PASS
setvar VAR_0x800B, 8
setvar VAR_CONTEST_RANK, CONTEST_RANK_MASTER

View File

@ -76,7 +76,11 @@ LilycoveCity_LilycoveMuseum_2F_EventScript_ShowExhibitHall::
applymovement LOCALID_PLAYER, LilycoveCity_LilycoveMuseum_2F_Movement_PlayerWalkInPlaceLeft
waitmovement 0
msgbox LilycoveCity_LilycoveMuseum_2F_Text_PleaseObtainPaintingsForExhibit, MSGBOX_SIGN
copyvar VAR_LILYCOVE_MUSEUM_2F_STATE, 1
#ifdef UBFIX
setvar VAR_LILYCOVE_MUSEUM_2F_STATE, 1
#else
copyvar VAR_LILYCOVE_MUSEUM_2F_STATE, 1, warn=FALSE
#endif
releaseall
end

View File

@ -6,7 +6,11 @@ SkyPillar_2F_MapScripts::
SkyPillar_2F_OnTransition:
call_if_lt VAR_SKY_PILLAR_STATE, 2, SkyPillar_2F_EventScript_CleanFloor
copyvar VAR_ICE_STEP_COUNT, 1
#ifdef UBFIX
setvar VAR_ICE_STEP_COUNT, 1
#else
copyvar VAR_ICE_STEP_COUNT, 1, warn=FALSE
#endif
end
SkyPillar_2F_EventScript_CleanFloor::

View File

@ -6,7 +6,11 @@ SkyPillar_4F_MapScripts::
SkyPillar_4F_OnTransition:
call_if_lt VAR_SKY_PILLAR_STATE, 2, SkyPillar_4F_EventScript_CleanFloor
copyvar VAR_ICE_STEP_COUNT, 1
#ifdef UBFIX
setvar VAR_ICE_STEP_COUNT, 1
#else
copyvar VAR_ICE_STEP_COUNT, 1, warn=FALSE
#endif
end
SkyPillar_4F_EventScript_CleanFloor::

View File

@ -3,7 +3,11 @@ CaveHole_CheckFallDownHole:
.2byte 0
CaveHole_FixCrackedGround:
copyvar VAR_ICE_STEP_COUNT, 1
#ifdef UBFIX
setvar VAR_ICE_STEP_COUNT, 1
#else
copyvar VAR_ICE_STEP_COUNT, 1, warn=FALSE
#endif
end
EventScript_FallDownHole::

View File

@ -1,19 +0,0 @@
JASC-PAL
0100
16
109 92 75
255 255 255
255 107 122
255 200 102
255 255 107
143 255 160
107 255 255
107 129 255
220 114 255
199 255 250
232 240 248
224 232 240
208 224 240
191 202 224
183 189 202
157 166 181

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 501 B

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 895 B

After

Width:  |  Height:  |  Size: 833 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 B

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 534 B

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 925 B

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1002 B

After

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 B

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 B

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 506 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 735 B

After

Width:  |  Height:  |  Size: 733 B

9
include/battle.h Normal file → Executable file
View File

@ -693,7 +693,7 @@ struct BattleStruct
u8 fickleBeamBoosted:1;
u8 poisonPuppeteerConfusion:1;
u16 startingStatusTimer;
u8 atkCancellerTracker;
u8 atkCancelerTracker;
struct BattleTvMovePoints tvMovePoints;
struct BattleTv tv;
u8 AI_monToSwitchIntoId[MAX_BATTLERS_COUNT];
@ -904,7 +904,7 @@ struct BattleScripting
u8 specialTrainerBattleType;
bool8 monCaught;
s32 savedDmg;
u16 savedMoveEffect; // For moves hitting multiple targets.
u16 unused_0x2c;
u16 moveEffect;
u16 multihitMoveEffect;
u8 illusionNickHack; // To properly display nick in STRINGID_ENEMYABOUTTOSWITCHPKMN.
@ -1249,4 +1249,9 @@ static inline bool32 IsBattlerInvalidForSpreadMove(u32 battlerAtk, u32 battlerDe
|| (battlerDef == BATTLE_PARTNER(battlerAtk) && (moveTarget == MOVE_TARGET_BOTH));
}
static inline u32 GetChosenMoveFromPosition(u32 battler)
{
return gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
}
#endif // GUARD_BATTLE_H

View File

@ -151,7 +151,6 @@ u32 CountPositiveStatStages(u32 battlerId);
u32 CountNegativeStatStages(u32 battlerId);
// move checks
bool32 IsAffectedByPowder(u32 battler, u32 ability, enum ItemHoldEffect holdEffect);
bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, enum DamageCategory category);
enum MoveComparisonResult AI_WhichMoveBetter(u32 move1, u32 move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo);
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef);

View File

@ -106,35 +106,35 @@ struct TypePower
enum MoveSuccessOrder
{
CANCELLER_FLAGS,
CANCELLER_STANCE_CHANGE_1,
CANCELLER_SKY_DROP,
CANCELLER_RECHARGE,
CANCELLER_ASLEEP_OR_FROZEN,
CANCELLER_OBEDIENCE,
CANCELLER_TRUANT,
CANCELLER_FLINCH,
CANCELLER_DISABLED,
CANCELLER_VOLATILE_BLOCKED,
CANCELLER_TAUNTED,
CANCELLER_IMPRISONED,
CANCELLER_CONFUSED,
CANCELLER_PARALYSED,
CANCELLER_INFATUATION,
CANCELLER_BIDE,
CANCELLER_THAW,
CANCELLER_STANCE_CHANGE_2,
CANCELLER_CHOICE_LOCK,
CANCELLER_WEATHER_PRIMAL,
CANCELLER_DYNAMAX_BLOCKED,
CANCELLER_POWDER_STATUS,
CANCELLER_PROTEAN,
CANCELLER_PSYCHIC_TERRAIN,
CANCELLER_EXPLODING_DAMP,
CANCELLER_MULTIHIT_MOVES,
CANCELLER_Z_MOVES,
CANCELLER_MULTI_TARGET_MOVES,
CANCELLER_END,
CANCELER_FLAGS,
CANCELER_STANCE_CHANGE_1,
CANCELER_SKY_DROP,
CANCELER_RECHARGE,
CANCELER_ASLEEP_OR_FROZEN,
CANCELER_OBEDIENCE,
CANCELER_TRUANT,
CANCELER_FLINCH,
CANCELER_DISABLED,
CANCELER_VOLATILE_BLOCKED,
CANCELER_TAUNTED,
CANCELER_IMPRISONED,
CANCELER_CONFUSED,
CANCELER_PARALYSED,
CANCELER_INFATUATION,
CANCELER_BIDE,
CANCELER_THAW,
CANCELER_STANCE_CHANGE_2,
CANCELER_CHOICE_LOCK,
CANCELER_WEATHER_PRIMAL,
CANCELER_DYNAMAX_BLOCKED,
CANCELER_POWDER_STATUS,
CANCELER_PROTEAN,
CANCELER_PSYCHIC_TERRAIN,
CANCELER_EXPLODING_DAMP,
CANCELER_MULTIHIT_MOVES,
CANCELER_Z_MOVES,
CANCELER_MULTI_TARGET_MOVES,
CANCELER_END,
};
enum Obedience
@ -147,7 +147,7 @@ enum Obedience
DISOBEYS_RANDOM_MOVE,
};
enum MoveCanceller
enum MoveCanceler
{
MOVE_STEP_SUCCESS,
MOVE_STEP_BREAK,
@ -166,7 +166,7 @@ struct DamageContext
u32 randomFactor:1;
u32 updateFlags:1;
u32 isAnticipation:1;
u32 padding1:1;
u32 isSelfInflicted:1;
u32 weather:16;
u32 fixedBasePower:8;
u32 padding2:8;
@ -186,7 +186,7 @@ enum SleepClauseBlock
enum SkyDropState
{
SKY_DROP_IGNORE,
SKY_DROP_ATTACKCANCELLER_CHECK,
SKY_DROP_ATTACKCANCELER_CHECK,
SKY_DROP_GRAVITY_ON_AIRBORNE,
SKY_DROP_CANCEL_MULTI_TURN_MOVES,
SKY_DROP_STATUS_YAWN,
@ -239,8 +239,8 @@ bool32 IsAbilityAndRecord(u32 battler, u32 battlerAbility, u32 abilityToCheck);
u32 DoEndTurnEffects(void);
bool32 HandleFaintedMonActions(void);
void TryClearRageAndFuryCutter(void);
enum MoveCanceller AtkCanceller_MoveSuccessOrder(void);
void SetAtkCancellerForCalledMove(void);
enum MoveCanceler AtkCanceler_MoveSuccessOrder(void);
void SetAtkCancelerForCalledMove(void);
bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2);
bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, u32 ability);
bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag);
@ -416,5 +416,6 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
bool32 IsSemiInvulnerable(u32 battler, enum SemiInvulnerableExclusion excludeCommander);
bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move);
bool32 HasPartnerTrainer(u32 battler);
bool32 IsAffectedByPowderMove(u32 battler, u32 ability, enum ItemHoldEffect holdEffect);
#endif // GUARD_BATTLE_UTIL_H

View File

@ -166,6 +166,7 @@
#define B_BATTLE_BOND GEN_LATEST // In Gen9+, Battle Bond increases Atk, SpAtk and Speed by one stage, once per battle
#define B_ATE_MULTIPLIER GEN_LATEST // In Gen7+, -ate abilities (Aerilate, Galvanize, Normalize, Pixilate, Refrigerate) multiply damage by 1.2. Otherwise, it's 1.3, except Normalize which has no multiplier.
#define B_DEFIANT_STICKY_WEB GEN_LATEST // In Gen9+, Defiant activates on Sticky Web regardless of who set it up. In Gen8, Defiant does not activate on Sticky Web set up by an ally after Court Change swaps its side.
#define B_POWDER_OVERCOAT GEN_LATEST // In Gen6+, Overcoat blocks powder and spore moves from affecting the user.
// Item settings
#define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore HP activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn.

View File

@ -169,7 +169,7 @@ enum VolatileFlags
F(VOLATILE_FORESIGHT, foresight, (u32, 1)) \
F(VOLATILE_DRAGON_CHEER, dragonCheer, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_FOCUS_ENERGY, focusEnergy, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_SEMI_INVULNERABLE, semiInvulnerable, (u32, 5)) \
F(VOLATILE_SEMI_INVULNERABLE, semiInvulnerable, (u32, SEMI_INVULNERABLE_COUNT - 1)) \
F(VOLATILE_ELECTRIFIED, electrified, (u32, 1)) \
F(VOLATILE_MUD_SPORT, mudSport, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_WATER_SPORT, waterSport, (u32, 1), V_BATON_PASSABLE) \
@ -224,6 +224,7 @@ enum SemiInvulnerableState
STATE_PHANTOM_FORCE,
STATE_SKY_DROP,
STATE_COMMANDER,
SEMI_INVULNERABLE_COUNT,
};
enum SemiInvulnerableExclusion

View File

@ -533,8 +533,9 @@
#define BG_STEEL_BEAM_OPPONENT 78
#define BG_STEEL_BEAM_PLAYER 79
#define BG_CHLOROBLAST 80
#define BG_RAINBOW 81
#define BG_SWAMP 82
#define BG_RAINBOW_PLAYER 81
#define BG_RAINBOW_OPPONENT 82
#define BG_SWAMP 83
// table ids for general animations (sBattleAnims_General)
#define B_ANIM_STATS_CHANGE 0

View File

@ -31,7 +31,7 @@
#define sSPECIAL_TRAINER_BATTLE_TYPE (gBattleScripting + 0x26) // specialTrainerBattleType
#define sMON_CAUGHT (gBattleScripting + 0x27) // monCaught
#define sSAVED_DMG (gBattleScripting + 0x28) // savedDmg
#define sSAVED_MOVE_EFFECT (gBattleScripting + 0x2C) // savedMoveEffect
#define sUNUSED_0x2C (gBattleScripting + 0x2C) // unused_0x2c
#define sMOVE_EFFECT (gBattleScripting + 0x2E) // moveEffect
#define sMULTIHIT_EFFECT (gBattleScripting + 0x30) // multihitMoveEffect
#define sILLUSION_NICK_HACK (gBattleScripting + 0x32) // illusionNickHack

View File

@ -42,10 +42,12 @@ enum GenConfigTag
GEN_CONFIG_DESTINY_BOND_FAIL,
GEN_CONFIG_POWDER_RAIN,
GEN_CONFIG_POWDER_GRASS,
GEN_CONFIG_POWDER_OVERCOAT,
GEN_CONFIG_OBLIVIOUS_TAUNT,
GEN_CONFIG_TOXIC_NEVER_MISS,
GEN_CONFIG_PARALYZE_ELECTRIC,
GEN_CONFIG_BADGE_BOOST,
GEN_CONFIG_LEAF_GUARD_PREVENTS_REST,
GEN_CONFIG_COUNT
};

View File

@ -45,10 +45,12 @@ static const u8 sGenerationalChanges[GEN_CONFIG_COUNT] =
[GEN_CONFIG_DESTINY_BOND_FAIL] = B_DESTINY_BOND_FAIL,
[GEN_CONFIG_POWDER_RAIN] = B_POWDER_RAIN,
[GEN_CONFIG_POWDER_GRASS] = B_POWDER_GRASS,
[GEN_CONFIG_POWDER_OVERCOAT] = B_POWDER_OVERCOAT,
[GEN_CONFIG_OBLIVIOUS_TAUNT] = B_OBLIVIOUS_TAUNT,
[GEN_CONFIG_TOXIC_NEVER_MISS] = B_TOXIC_NEVER_MISS,
[GEN_CONFIG_PARALYZE_ELECTRIC] = B_PARALYZE_ELECTRIC,
[GEN_CONFIG_BADGE_BOOST] = B_BADGE_BOOST
[GEN_CONFIG_BADGE_BOOST] = B_BADGE_BOOST,
[GEN_CONFIG_LEAF_GUARD_PREVENTS_REST] = B_LEAF_GUARD_PREVENTS_REST,
};
#if TESTING

View File

@ -1180,6 +1180,8 @@ struct MapPosition
#if T_SHOULD_RUN_MOVE_ANIM
extern bool32 gLoadFail;
extern bool32 gCountAllocs;
extern s32 gSpriteAllocs;
#endif // T_SHOULD_RUN_MOVE_ANIM
#endif // GUARD_GLOBAL_H

View File

@ -3196,9 +3196,11 @@ extern const u32 gBattleAnimBgTilemap_Sandstorm[];
extern const u32 gBattleAnimBgImage_Sandstorm[];
// Pledge Effect field status - Rainbow
extern const u32 gBattleAnimBgImage_Rainbow[];
extern const u32 gBattleAnimBgImage_RainbowPlayer[];
extern const u32 gBattleAnimBgImage_RainbowOpponent[];
extern const u16 gBattleAnimBGPalette_Rainbow[];
extern const u32 gBattleAnimBgTilemap_Rainbow[];
extern const u32 gBattleAnimBgTilemap_RainbowPlayer[];
extern const u32 gBattleAnimBgTilemap_RainbowOpponent[];
// Pledge Effect field status - Swamp
extern const u32 gBattleAnimBgImage_Swamp[];

View File

@ -2,7 +2,11 @@
#define GUARD_TEST_RUNNER_H
extern const bool8 gTestRunnerEnabled;
#if TESTING
extern const bool8 gTestRunnerHeadless;
#else
#define gTestRunnerHeadless FALSE
#endif
extern const bool8 gTestRunnerSkipIsFail;
#if TESTING

File diff suppressed because it is too large Load Diff

View File

@ -1089,7 +1089,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
SetTypeBeforeUsingMove(move, battlerAtk);
moveType = GetBattleMoveType(move);
if (IsPowderMove(move) && !IsAffectedByPowder(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef]))
if (IsPowderMove(move) && !IsAffectedByPowderMove(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef]))
RETURN_SCORE_MINUS(10);
if (!BreaksThroughSemiInvulnerablity(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY))
@ -4008,7 +4008,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
bool32 isBattle1v1 = IsBattle1v1();
bool32 hasTwoOpponents = HasTwoOpponents(battlerAtk);
bool32 hasPartner = HasPartner(battlerAtk);
bool32 moveTargetsBothOpponents = hasTwoOpponents && (gMovesInfo[move].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_ALL_BATTLERS));
bool32 moveTargetsBothOpponents = hasTwoOpponents && (GetMoveTarget(move) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_ALL_BATTLERS));
u32 i;
// The AI should understand that while Dynamaxed, status moves function like Protect.
@ -4768,7 +4768,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
if (hasPartner
&& GetMoveTarget(move) == MOVE_TARGET_USER
&& !IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef])
&& (!IsPowderMove(move) || IsAffectedByPowder(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef])))
&& (!IsPowderMove(move) || IsAffectedByPowderMove(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef])))
// Rage Powder doesn't affect powder immunities
{
u32 predictedMoveOnPartner = gLastMoves[BATTLE_PARTNER(battlerAtk)];

View File

@ -387,7 +387,7 @@ static u32 FindMonWithMoveOfEffectiveness(u32 battler, u32 opposingBattler, uq4_
for (j = 0; j < MAX_MON_MOVES; j++)
{
move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
if (move != MOVE_NONE && AI_GetMoveEffectiveness(move, battler, opposingBattler) >= effectiveness && gMovesInfo[move].power != 0)
if (move != MOVE_NONE && AI_GetMoveEffectiveness(move, battler, opposingBattler) >= effectiveness && GetMovePower(move) != 0)
return SetSwitchinAndSwitch(battler, i);
}
}
@ -422,7 +422,7 @@ static bool32 ShouldSwitchIfAllMovesBad(u32 battler)
if (AI_GetMoveEffectiveness(aiMove, battler, opposingBattler) > UQ_4_12(0.0) && aiMove != MOVE_NONE
&& !CanAbilityAbsorbMove(battler, opposingBattler, gAiLogicData->abilities[opposingBattler], aiMove, GetBattleMoveType(aiMove), AI_CHECK)
&& !CanAbilityBlockMove(battler, opposingBattler, gBattleMons[battler].ability, gAiLogicData->abilities[opposingBattler], aiMove, AI_CHECK)
&& (!ALL_MOVES_BAD_STATUS_MOVES_BAD || gMovesInfo[aiMove].power != 0)) // If using ALL_MOVES_BAD_STATUS_MOVES_BAD, then need power to be non-zero
&& (!ALL_MOVES_BAD_STATUS_MOVES_BAD || GetMovePower(aiMove) != 0)) // If using ALL_MOVES_BAD_STATUS_MOVES_BAD, then need power to be non-zero
return FALSE;
}
}
@ -2414,6 +2414,9 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType)
if (bestMonId != PARTY_SIZE)
return bestMonId;
if (aceMonId != PARTY_SIZE && aliveCount == 0)
return aceMonId;
bestMonId = GetBestMonTypeMatchup(party, firstId, lastId, invalidMons, battler, opposingBattler);
if (bestMonId != PARTY_SIZE)
return bestMonId;

View File

@ -530,16 +530,6 @@ bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler)
return FALSE;
}
// move checks
bool32 IsAffectedByPowder(u32 battler, u32 ability, enum ItemHoldEffect holdEffect)
{
if (ability == ABILITY_OVERCOAT
|| (GetGenConfig(GEN_CONFIG_POWDER_GRASS) >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GRASS))
|| holdEffect == HOLD_EFFECT_SAFETY_GOGGLES)
return FALSE;
return TRUE;
}
// This function checks if all physical/special moves are either unusable or unreasonable to use.
// Consider a pokemon boosting their attack against a ghost pokemon having only normal-type physical attacks.
bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, enum DamageCategory category)
@ -1443,7 +1433,7 @@ s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler, u32 aiMoveConsidered, u32 pla
bool32 CanEndureHit(u32 battler, u32 battlerTarget, u32 move)
{
enum BattleMoveEffects effect = GetMoveEffect(move);
if (!AI_BattlerAtMaxHp(battlerTarget) || effect == EFFECT_MULTI_HIT)
if (!AI_BattlerAtMaxHp(battlerTarget) || effect == EFFECT_MULTI_HIT || gAiLogicData->abilities[battler] == ABILITY_PARENTAL_BOND)
return FALSE;
if (GetMoveStrikeCount(move) > 1 && !(effect == EFFECT_DRAGON_DARTS && !HasTwoOpponents(battler)))
return FALSE;
@ -3975,7 +3965,7 @@ bool32 AreMovesEquivalent(u32 battlerAtk, u32 battlerAtkPartner, u32 move, u32 p
// shared bits indicate they're meaningfully the same in some way
if (atkEffect & partnerEffect)
{
if (gMovesInfo[move].target == MOVE_TARGET_SELECTED && gMovesInfo[partnerMove].target == MOVE_TARGET_SELECTED)
if (GetMoveTarget(move) == MOVE_TARGET_SELECTED && GetMoveTarget(partnerMove) == MOVE_TARGET_SELECTED)
{
if (battlerDef == gBattleStruct->moveTarget[battlerAtkPartner])
return TRUE;
@ -4121,7 +4111,7 @@ bool32 DoesPartnerHaveSameMoveEffect(u32 battlerAtkPartner, u32 battlerDef, u32
if (GetMoveEffect(move) == GetMoveEffect(partnerMove)
&& partnerMove != MOVE_NONE)
{
if (gMovesInfo[move].target == MOVE_TARGET_SELECTED && gMovesInfo[partnerMove].target == MOVE_TARGET_SELECTED)
if (GetMoveTarget(move) == MOVE_TARGET_SELECTED && GetMoveTarget(partnerMove) == MOVE_TARGET_SELECTED)
{
return gBattleStruct->moveTarget[battlerAtkPartner] == battlerDef;
}
@ -4522,7 +4512,7 @@ bool32 IsRecycleEncouragedItem(u32 item)
static bool32 HasMoveThatChangesKOThreshold(u32 battlerId, u32 noOfHitsToFaint, u32 aiIsFaster)
{
s32 i;
s32 i, j;
u16 *moves = GetMovesArray(battlerId);
for (i = 0; i < MAX_MON_MOVES; i++)
@ -4534,7 +4524,11 @@ static bool32 HasMoveThatChangesKOThreshold(u32 battlerId, u32 noOfHitsToFaint,
if (GetMovePriority(moves[i]) > 0)
return TRUE;
switch (gMovesInfo[moves[i]].additionalEffects[i].moveEffect)
u32 additionalEffectCount = GetMoveAdditionalEffectCount(moves[i]);
for (j = 0; j < additionalEffectCount; j++)
{
const struct AdditionalEffect *additionalEffect = GetMoveAdditionalEffectById(moves[i], j);
switch (additionalEffect->moveEffect)
{
case MOVE_EFFECT_SPD_MINUS_1:
case MOVE_EFFECT_SPD_MINUS_2:
@ -4547,6 +4541,7 @@ static bool32 HasMoveThatChangesKOThreshold(u32 battlerId, u32 noOfHitsToFaint,
}
}
}
}
return FALSE;
}

View File

@ -13,6 +13,7 @@
#include "graphics.h"
#include "main.h"
#include "malloc.h"
#include "menu.h"
#include "m4a.h"
#include "palette.h"
#include "pokemon.h"
@ -1568,10 +1569,7 @@ void LoadMoveBg(u16 bgId)
{
if (IsContest())
{
void *decompressionBuffer = Alloc(0x800);
const u32 *tilemap = gBattleAnimBackgroundTable[bgId].tilemap;
DecompressDataWithHeaderWram(tilemap, decompressionBuffer);
void *decompressionBuffer = malloc_and_decompress(gBattleAnimBackgroundTable[bgId].tilemap, NULL);
RelocateBattleBgPal(GetBattleBgPaletteNum(), decompressionBuffer, 0x100, FALSE);
DmaCopy32(3, decompressionBuffer, (void *)BG_SCREEN_ADDR(26), 0x800);
DecompressDataWithHeaderVram(gBattleAnimBackgroundTable[bgId].image, (void *)BG_SCREEN_ADDR(4));

View File

@ -5189,7 +5189,7 @@ void AnimNeedleArmSpike(struct Sprite *sprite)
{
if (gBattleAnimArgs[0] == 0)
{
if (gMovesInfo[gAnimMoveIndex].target == MOVE_TARGET_BOTH)
if (GetMoveTarget(gAnimMoveIndex) == MOVE_TARGET_BOTH)
{
SetAverageBattlerPositions(gBattleAnimAttacker, TRUE, &a, &b);
}
@ -5201,7 +5201,7 @@ void AnimNeedleArmSpike(struct Sprite *sprite)
}
else
{
if (gMovesInfo[gAnimMoveIndex].target == MOVE_TARGET_BOTH)
if (GetMoveTarget(gAnimMoveIndex) == MOVE_TARGET_BOTH)
{
SetAverageBattlerPositions(gBattleAnimTarget, TRUE, &a, &b);
}

View File

@ -9431,7 +9431,7 @@ static void SpriteCB_MaxFlutterby(struct Sprite* sprite)
{
s16 target_x;
s16 target_y;
if (gMovesInfo[gAnimMoveIndex].target == MOVE_TARGET_BOTH)
if (GetMoveTarget(gAnimMoveIndex) == MOVE_TARGET_BOTH)
{
SetAverageBattlerPositions(gBattleAnimTarget, TRUE, &target_x, &target_y);
}

View File

@ -14,6 +14,7 @@
#include "sound.h"
#include "sprite.h"
#include "task.h"
#include "test_runner.h"
#include "trig.h"
#include "util.h"
#include "data.h"
@ -2435,7 +2436,7 @@ void TryShinyAnimation(u8 battler, struct Pokemon *mon)
if (illusionMon != NULL)
mon = illusionMon;
if (IsBattlerSpriteVisible(battler) && IsValidForBattle(mon))
if (IsBattlerSpriteVisible(battler) && IsValidForBattle(mon) && !gTestRunnerHeadless)
{
if (isShiny)
{
@ -2768,4 +2769,3 @@ static void CB_CriticalCaptureThrownBallMovement(struct Sprite *sprite)
sprite->callback = SpriteCB_Ball_Bounce_Step;
}
}

View File

@ -512,13 +512,57 @@ static inline bool32 IsAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 batt
&& CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle;
}
static inline bool32 IsDoubleAceSlot(u32 battler, u32 partyId)
{
u32 partyCountEnd;
if (!(gAiThinkingStruct->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON))
return FALSE;
partyCountEnd = CalculateEnemyPartyCountInSide(battler);
if (partyCountEnd == 0)
return FALSE;
if (partyId == partyCountEnd - 1)
return TRUE;
if (partyCountEnd > 1 && partyId == partyCountEnd - 2)
return TRUE;
return FALSE;
}
static inline bool32 IsDoubleAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 battler)
{
return gAiThinkingStruct->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON
&& (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 1)
&& (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 2)
&& CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle
&& CountAIAliveNonEggMonsExcept(PARTY_SIZE-1) != pokemonInBattle;
s32 battler1, battler2, firstId, lastId;
s32 i;
if (!IsDoubleAceSlot(battler, chosenMonId))
return FALSE;
if (!IsDoubleBattle())
{
battler2 = battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
}
else
{
battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
}
GetAIPartyIndexes(battler, &firstId, &lastId);
for (i = firstId; i < lastId; i++)
{
if (!IsValidForBattle(&gEnemyParty[i])
|| i == gBattlerPartyIndexes[battler1]
|| i == gBattlerPartyIndexes[battler2]
|| i == chosenMonId)
continue;
if (!IsAcePokemon(i, pokemonInBattle, battler) && !IsDoubleAceSlot(battler, i))
return TRUE;
}
return FALSE;
}
static void OpponentHandleChoosePokemon(u32 battler)

View File

@ -2578,7 +2578,7 @@ void BtlController_HandleStatusAnimation(u32 battler)
void BtlController_HandleHitAnimation(u32 battler)
{
if (gSprites[gBattlerSpriteIds[battler]].invisible == TRUE)
if (gSprites[gBattlerSpriteIds[battler]].invisible == TRUE || gTestRunnerHeadless)
{
BtlController_Complete(battler);
}
@ -2593,6 +2593,11 @@ void BtlController_HandleHitAnimation(u32 battler)
void BtlController_HandlePlaySE(u32 battler)
{
if (gTestRunnerHeadless)
{
BtlController_Complete(battler);
return;
}
s32 pan = IsOnPlayerSide(battler) ? SOUND_PAN_ATTACKER : SOUND_PAN_TARGET;
PlaySE12WithPanning(gBattleResources->bufferA[battler][1] | (gBattleResources->bufferA[battler][2] << 8), pan);
@ -2601,6 +2606,11 @@ void BtlController_HandlePlaySE(u32 battler)
void BtlController_HandlePlayFanfareOrBGM(u32 battler)
{
if (gTestRunnerHeadless)
{
BtlController_Complete(battler);
return;
}
if (gBattleResources->bufferA[battler][3])
{
BattleStopLowHpSound();

View File

@ -121,19 +121,9 @@ static bool32 HandleEndTurnOrder(u32 battler)
gBattleTurnCounter++;
gBattleStruct->endTurnEventsCounter++;
u32 i, j;
for (i = 0; i < gBattlersCount; i++)
{
for (u32 i = 0; i < gBattlersCount; i++)
gBattlerByTurnOrder[i] = i;
}
for (i = 0; i < gBattlersCount - 1; i++)
{
for (j = i + 1; j < gBattlersCount; j++)
{
if (GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE) == -1)
SwapTurnOrder(i, j);
}
}
SortBattlersBySpeed(gBattlerByTurnOrder, FALSE);
return effect;
}

View File

@ -4329,7 +4329,7 @@ static void HandleTurnActionSelectionState(void)
case B_ACTION_SWITCH:
gBattleStruct->battlerPartyIndexes[battler] = gBattlerPartyIndexes[battler];
if (gBattleTypeFlags & BATTLE_TYPE_ARENA
|| !CanBattlerEscape(battler))
|| (!CanBattlerEscape(battler) && GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_SHED_SHELL))
{
BtlController_EmitChoosePokemon(battler, B_COMM_TO_CONTROLLER, PARTY_ACTION_CANT_SWITCH, PARTY_SIZE, ABILITY_NONE, 0, gBattleStruct->battlerPartyOrders[battler]);
}
@ -5152,7 +5152,7 @@ static void TurnValuesCleanUp(bool8 var0)
gSideTimers[B_SIDE_PLAYER].followmeTimer = 0;
gSideTimers[B_SIDE_OPPONENT].followmeTimer = 0;
gBattleStruct->pledgeMove = FALSE; // combined pledge move may not have been used due to a canceller
gBattleStruct->pledgeMove = FALSE; // combined pledge move may not have been used due to a canceler
ClearPursuitValues();
ClearDamageCalcResults();
}

View File

@ -1040,31 +1040,15 @@ u32 NumFaintedBattlersByAttacker(u32 battlerAtk)
return numMonsFainted;
}
bool32 IsMovePowderBlocked(u32 battlerAtk, u32 battlerDef, u32 move)
bool32 IsPowderMoveBlocked(u32 battlerAtk, u32 battlerDef, u32 move)
{
bool32 effect = FALSE;
if (!IsPowderMove(move)
|| battlerAtk == battlerDef
|| IsAffectedByPowderMove(battlerDef, GetBattlerAbility(battlerDef), GetBattlerHoldEffect(battlerDef, TRUE)))
return FALSE;
if (IsPowderMove(move) && (battlerAtk != battlerDef))
{
if (GetGenConfig(GEN_CONFIG_POWDER_GRASS) >= GEN_6
&& (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || GetBattlerAbility(battlerDef) == ABILITY_OVERCOAT))
{
gBattlerAbility = battlerDef;
RecordAbilityBattle(gBattlerTarget, ABILITY_OVERCOAT);
effect = TRUE;
}
else if (GetBattlerHoldEffect(battlerDef, TRUE) == HOLD_EFFECT_SAFETY_GOGGLES)
{
RecordItemEffectBattle(battlerDef, HOLD_EFFECT_SAFETY_GOGGLES);
gLastUsedItem = gBattleMons[battlerDef].item;
effect = TRUE;
}
if (effect)
gBattlescriptCurrInstr = BattleScript_PowderMoveNoEffect;
}
return effect;
return TRUE;
}
bool32 EmergencyExitCanBeTriggered(u32 battler)
@ -1113,7 +1097,7 @@ static void Cmd_attackcanceler(void)
gBattlescriptCurrInstr = BattleScript_MoveEnd;
return;
}
if (AtkCanceller_MoveSuccessOrder() != MOVE_STEP_SUCCESS)
if (AtkCanceler_MoveSuccessOrder() != MOVE_STEP_SUCCESS)
return;
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_OFF
@ -1151,7 +1135,7 @@ static void Cmd_attackcanceler(void)
return;
}
if (IsMovePowderBlocked(gBattlerAttacker, gBattlerTarget, gCurrentMove))
if (IsPowderMoveBlocked(gBattlerAttacker, gBattlerTarget, gCurrentMove))
return;
if (!gBattleMons[gBattlerAttacker].pp[gCurrMovePos] && gCurrentMove != MOVE_STRUGGLE
@ -1178,12 +1162,31 @@ static void Cmd_attackcanceler(void)
gBattlescriptCurrInstr = BattleScript_FailedFromAtkString;
if (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns))
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
return;
}
u32 isBounceable = MoveCanBeBouncedBack(gCurrentMove);
if (gProtectStructs[gBattlerTarget].bounceMove
bool32 bounceActive = (gProtectStructs[gBattlerTarget].bounceMove && IsBattlerAlive(gBattlerTarget));
if (!bounceActive
&& !gBattleStruct->bouncedMoveIsUsed
&& isBounceable
&& GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove) == MOVE_TARGET_OPPONENTS_FIELD)
{
u32 partner = BATTLE_PARTNER(gBattlerTarget);
if (partner < gBattlersCount
&& GetBattlerSide(partner) == GetBattlerSide(gBattlerTarget)
&& gProtectStructs[partner].bounceMove
&& IsBattlerAlive(partner))
{
gBattlerTarget = partner;
bounceActive = TRUE;
}
}
if (bounceActive
&& isBounceable
&& !gBattleStruct->bouncedMoveIsUsed)
{
@ -1191,7 +1194,7 @@ static void Cmd_attackcanceler(void)
// Edge case for bouncing a powder move against a grass type pokemon.
ClearDamageCalcResults();
SetAtkCancellerForCalledMove();
SetAtkCancelerForCalledMove();
gEffectBattler = gBattlerTarget;
if (BlocksPrankster(gCurrentMove, gBattlerTarget, gBattlerAttacker, TRUE))
{
@ -1225,7 +1228,7 @@ static void Cmd_attackcanceler(void)
if (gBattleStruct->bouncedMoveIsUsed)
{
ClearDamageCalcResults();
SetAtkCancellerForCalledMove(); // Edge case for bouncing a powder move against a grass type pokemon.
SetAtkCancelerForCalledMove(); // Edge case for bouncing a powder move against a grass type pokemon.
BattleScriptCall(BattleScript_MagicBounce);
gBattlerAbility = battler;
return;
@ -1278,7 +1281,7 @@ static void Cmd_attackcanceler(void)
{
if (!CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker, TRUE), gCurrentMove))
gProtectStructs[gBattlerAttacker].touchedProtectLike = TRUE;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
gLastLandedMoves[gBattlerTarget] = 0;
gLastHitByType[gBattlerTarget] = 0;
@ -1840,7 +1843,7 @@ static void Cmd_typecalc(void)
{
CMD_ARGS();
if (!IsSpreadMove(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove))) // Handled in CANCELLER_MULTI_TARGET_MOVES for Spread Moves
if (!IsSpreadMove(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove))) // Handled in CANCELER_MULTI_TARGET_MOVES for Spread Moves
{
struct DamageContext ctx = {0};
ctx.battlerAtk = gBattlerAttacker;
@ -2225,6 +2228,10 @@ static void Cmd_attackanimation(void)
gBattleMons[gBattlerAttacker].friendship,
&gDisableStructs[gBattlerAttacker],
multihit);
#if T_SHOULD_RUN_MOVE_ANIM
gCountAllocs = TRUE;
gSpriteAllocs = 0;
#endif
gBattleScripting.animTurn++;
gBattleScripting.animTargetsHit++;
MarkBattlerForControllerExec(gBattlerAttacker);
@ -2243,8 +2250,13 @@ static void Cmd_waitanimation(void)
CMD_ARGS();
if (gBattleControllerExecFlags == 0 && gBattleStruct->battlerKOAnimsRunning == 0)
{
#if T_SHOULD_RUN_MOVE_ANIM
gCountAllocs = FALSE;
#endif
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
static void DoublesHPBarReduction(void)
{
@ -6310,9 +6322,10 @@ static void Cmd_moveend(void)
// Set ShellTrap to activate after the attacker's turn if target was hit by a physical move.
if (GetMoveEffect(gChosenMoveByBattler[gBattlerTarget]) == EFFECT_SHELL_TRAP
&& IsBattleMovePhysical(gCurrentMove)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& gBattlerTarget != gBattlerAttacker
&& !IsBattlerAlly(gBattlerTarget, gBattlerAttacker)
&& gProtectStructs[gBattlerTarget].physicalDmg
&& gProtectStructs[gBattlerTarget].physicalBattlerId == gBattlerAttacker
&& !TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove))
{
@ -6448,7 +6461,6 @@ static void Cmd_moveend(void)
gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = nextTarget; // Fix for moxie spread moves
gBattleScripting.moveendState = 0;
MoveValuesCleanUp();
gBattleScripting.moveEffect = gBattleScripting.savedMoveEffect;
if (moveEffect == EFFECT_EXPLOSION || moveEffect == EFFECT_MISTY_EXPLOSION // Edge case for Explosion not changing targets
|| moveEffect == EFFECT_SYNCHRONOISE) // So we don't go back to the Synchronoise script
@ -6494,7 +6506,7 @@ static void Cmd_moveend(void)
if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
&& gMultiHitCounter
&& !(moveEffect == EFFECT_PRESENT && gBattleStruct->presentBasePower == 0)) // Silly edge case
&& !(moveEffect == EFFECT_PRESENT && gBattleStruct->presentBasePower == 0)) // Parental Bond edge case
{
gMultiHitCounter--;
if (!IsBattlerAlive(gBattlerTarget) && moveEffect != EFFECT_DRAGON_DARTS)
@ -6503,7 +6515,9 @@ static void Cmd_moveend(void)
gBattleScripting.multihitString[4]++;
if (gMultiHitCounter == 0)
{
if (GetMoveEffectArg_MoveProperty(gCurrentMove) == MOVE_EFFECT_SCALE_SHOT && !NoAliveMonsForEitherParty())
if (moveEffect == EFFECT_MULTI_HIT
&& GetMoveEffectArg_MoveProperty(gCurrentMove) == MOVE_EFFECT_SCALE_SHOT
&& !NoAliveMonsForEitherParty())
BattleScriptCall(BattleScript_ScaleShot);
else
BattleScriptCall(BattleScript_MultiHitPrintStrings);
@ -7419,7 +7433,7 @@ static void Cmd_jumpifcantswitch(void)
CMD_ARGS(u8 battler:7, u8 ignoreEscapePrevention:1, const u8 *jumpInstr);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (!cmd->ignoreEscapePrevention && !CanBattlerEscape(battler))
if (!cmd->ignoreEscapePrevention && !CanBattlerEscape(battler) && GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_SHED_SHELL)
{
gBattlescriptCurrInstr = cmd->jumpInstr;
}
@ -8517,9 +8531,9 @@ static void Cmd_hidepartystatussummary(void)
static void ResetValuesForCalledMove(void)
{
if (gBattlerByTurnOrder[gCurrentTurnActionNumber] != gBattlerAttacker)
gBattleStruct->atkCancellerTracker = 0;
gBattleStruct->atkCancelerTracker = 0;
else
SetAtkCancellerForCalledMove();
SetAtkCancelerForCalledMove();
gBattleScripting.animTurn = 0;
gBattleScripting.animTargetsHit = 0;
SetTypeBeforeUsingMove(gCurrentMove, gBattlerAttacker);
@ -11522,17 +11536,26 @@ static void Cmd_trysetencore(void)
}
if ((IsMoveEncoreBanned(gLastMoves[gBattlerTarget]))
|| i == MAX_MON_MOVES
|| gLastMoves[gBattlerTarget] == MOVE_NONE
|| gLastMoves[gBattlerTarget] == MOVE_UNAVAILABLE)
|| gLastMoves[gBattlerTarget] == MOVE_UNAVAILABLE
|| gBattleMons[gBattlerTarget].pp[i] == 0
|| gDisableStructs[gBattlerTarget].encoredMove != MOVE_NONE
|| GetMoveEffect(gChosenMoveByBattler[gBattlerTarget]) == EFFECT_SHELL_TRAP)
{
i = MAX_MON_MOVES;
gBattlescriptCurrInstr = cmd->failInstr;
}
if (gDisableStructs[gBattlerTarget].encoredMove == MOVE_NONE
&& i != MAX_MON_MOVES && gBattleMons[gBattlerTarget].pp[i] != 0)
else
{
gDisableStructs[gBattlerTarget].encoredMove = gBattleMons[gBattlerTarget].moves[i];
gDisableStructs[gBattlerTarget].encoredMovePos = i;
// If the target's selected move is not the same as the move being Encored into,
// the target will select a random opposing target
// Redirection such as Follow Me is already covered in HandleAction_UseMove of battle_util.c
if (gDisableStructs[gBattlerTarget].encoredMove != GetChosenMoveFromPosition(gBattlerTarget))
gBattleStruct->moveTarget[gBattlerTarget] = SetRandomTarget(gBattlerTarget);
// Encore always lasts 3 turns, but we need to account for a scenario where Encore changes the move during the same turn.
if (HasBattlerActedThisTurn(gBattlerTarget))
gDisableStructs[gBattlerTarget].encoreTimer = 4;
@ -11540,10 +11563,6 @@ static void Cmd_trysetencore(void)
gDisableStructs[gBattlerTarget].encoreTimer = 3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattlescriptCurrInstr = cmd->failInstr;
}
}
static void Cmd_painsplitdmgcalc(void)
@ -14285,13 +14304,16 @@ static void Cmd_displaydexinfo(void)
{
CMD_ARGS();
struct Pokemon *mon = GetBattlerMon(GetCatchingBattler());
u32 caughtBattler = GetCatchingBattler();
struct Pokemon *mon = GetBattlerMon(caughtBattler);
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
switch (gBattleCommunication[0])
{
case 0:
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
ClearTemporarySpeciesSpriteData(caughtBattler, FALSE, FALSE);
BattleLoadMonSpriteGfx(mon, caughtBattler);
gBattleCommunication[0]++;
break;
case 1:
@ -18295,7 +18317,7 @@ void BS_JumpIfAbilityPreventsRest(void)
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
u32 battler = GetBattlerForBattleScript(cmd->battler);
u32 ability = GetBattlerAbility(battler);
if (B_LEAF_GUARD_PREVENTS_REST >= GEN_5 && IsLeafGuardProtected(battler, ability))
if (GetGenConfig(GEN_CONFIG_LEAF_GUARD_PREVENTS_REST) >= GEN_5 && IsLeafGuardProtected(battler, ability))
gBattlescriptCurrInstr = cmd->jumpInstr;
else if (IsShieldsDownProtected(battler, ability))
gBattlescriptCurrInstr = cmd->jumpInstr;

View File

@ -1267,6 +1267,7 @@ static void TrySetBattleSeminarShow(void)
ctx.isCrit = FALSE;
ctx.randomFactor = FALSE;
ctx.updateFlags = FALSE;
ctx.isSelfInflicted = FALSE;
ctx.fixedBasePower = powerOverride;
gBattleStruct->moveDamage[gBattlerTarget] = CalculateMoveDamage(&ctx);
dmgByMove[i] = gBattleStruct->moveDamage[gBattlerTarget];

View File

@ -310,7 +310,7 @@ bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move)
if (effect == EFFECT_PURSUIT && IsPursuitTargetSet())
return FALSE;
if (gSideTimers[defSide].followmePowder && !IsAffectedByPowder(battlerAtk, ability, GetBattlerHoldEffect(battlerAtk, TRUE)))
if (gSideTimers[defSide].followmePowder && !IsAffectedByPowderMove(battlerAtk, ability, GetBattlerHoldEffect(battlerAtk, TRUE)))
return FALSE;
return TRUE;
@ -390,12 +390,11 @@ void HandleAction_UseMove(void)
return;
}
gBattleStruct->atkCancellerTracker = 0;
gBattleStruct->atkCancelerTracker = 0;
ClearDamageCalcResults();
gMultiHitCounter = 0;
gBattleScripting.savedDmg = 0;
gBattleCommunication[MISS_TYPE] = 0;
gBattleScripting.savedMoveEffect = 0;
gCurrMovePos = gChosenMovePos = gBattleStruct->chosenMovePositions[gBattlerAttacker];
// choose move
@ -734,7 +733,9 @@ void HandleAction_Run(void)
}
else
{
if (!CanBattlerEscape(gBattlerAttacker))
if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_CAN_ALWAYS_RUN
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_RUN_AWAY
&& !CanBattlerEscape(gBattlerAttacker))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_ATTACKER_CANT_ESCAPE;
gBattlescriptCurrInstr = BattleScript_PrintFailedToRunString;
@ -1077,7 +1078,7 @@ const u8 *CheckSkyDropState(u32 battler, enum SkyDropState skyDropState)
// Set confused status
gBattleMons[otherSkyDropper].volatiles.confusionTurns = ((Random()) % 4) + 2;
if (skyDropState == SKY_DROP_ATTACKCANCELLER_CHECK)
if (skyDropState == SKY_DROP_ATTACKCANCELER_CHECK)
{
gBattleStruct->skyDropTargets[battler] = SKY_DROP_RELEASED_TARGET;
}
@ -1893,13 +1894,13 @@ static inline bool32 TryActivatePowderStatus(u32 move)
return FALSE;
}
void SetAtkCancellerForCalledMove(void)
void SetAtkCancelerForCalledMove(void)
{
gBattleStruct->atkCancellerTracker = CANCELLER_VOLATILE_BLOCKED;
gBattleStruct->atkCancelerTracker = CANCELER_VOLATILE_BLOCKED;
gBattleStruct->isAtkCancelerForCalledMove = TRUE;
}
static enum MoveCanceller CancellerFlags(void)
static enum MoveCanceler CancelerFlags(void)
{
gBattleMons[gBattlerAttacker].volatiles.destinyBond = FALSE;
gBattleMons[gBattlerAttacker].volatiles.grudge = FALSE;
@ -1907,14 +1908,14 @@ static enum MoveCanceller CancellerFlags(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerStanceChangeOne(void)
static enum MoveCanceler CancelerStanceChangeOne(void)
{
if (B_STANCE_CHANGE_FAIL < GEN_7 && TryFormChangeBeforeMove())
return MOVE_STEP_BREAK;
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerSkyDrop(void)
static enum MoveCanceler CancelerSkyDrop(void)
{
// If Pokemon is being held in Sky Drop
if (gBattleMons[gBattlerAttacker].volatiles.semiInvulnerable == STATE_SKY_DROP)
@ -1926,11 +1927,11 @@ static enum MoveCanceller CancellerSkyDrop(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerRecharge(void)
static enum MoveCanceler CancelerRecharge(void)
{
if (gDisableStructs[gBattlerAttacker].rechargeTimer > 0)
{
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
@ -1938,7 +1939,7 @@ static enum MoveCanceller CancellerRecharge(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerAsleepOrFrozen(void)
static enum MoveCanceler CancelerAsleepOrFrozen(void)
{
if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP)
{
@ -2003,7 +2004,7 @@ static enum MoveCanceller CancellerAsleepOrFrozen(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerObedience(void)
static enum MoveCanceler CancelerObedience(void)
{
enum Obedience obedienceResult = GetAttackerObedienceForAction();
if (!(gHitMarker & HITMARKER_NO_PPDEDUCT) // Don't check obedience after first hit of multi target move or multi hit moves
@ -2030,6 +2031,7 @@ static enum MoveCanceller CancellerObedience(void)
ctx.isCrit = FALSE;
ctx.randomFactor = FALSE;
ctx.updateFlags = TRUE;
ctx.isSelfInflicted = TRUE;
ctx.fixedBasePower = 40;
gBattleStruct->moveDamage[gBattlerAttacker] = CalculateMoveDamage(&ctx);
gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself;
@ -2048,7 +2050,7 @@ static enum MoveCanceller CancellerObedience(void)
break;
case DISOBEYS_RANDOM_MOVE:
gCalledMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos];
SetAtkCancellerForCalledMove();
SetAtkCancelerForCalledMove();
gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove;
gBattlerTarget = GetBattleMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gHitMarker |= HITMARKER_OBEYS;
@ -2060,11 +2062,11 @@ static enum MoveCanceller CancellerObedience(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerTruant(void)
static enum MoveCanceler CancelerTruant(void)
{
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_TRUANT && gDisableStructs[gBattlerAttacker].truantCounter)
{
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_LOAFING;
gBattlerAbility = gBattlerAttacker;
@ -2075,12 +2077,12 @@ static enum MoveCanceller CancellerTruant(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerFlinch(void)
static enum MoveCanceler CancelerFlinch(void)
{
if (gBattleMons[gBattlerAttacker].volatiles.flinched)
{
gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
@ -2088,13 +2090,13 @@ static enum MoveCanceller CancellerFlinch(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerDisabled(void)
static enum MoveCanceler CancelerDisabled(void)
{
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].disabledMove == gCurrentMove && gDisableStructs[gBattlerAttacker].disabledMove != MOVE_NONE)
{
gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE;
gBattleScripting.battler = gBattlerAttacker;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
@ -2102,13 +2104,13 @@ static enum MoveCanceller CancellerDisabled(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerVolatileBlocked(void)
static enum MoveCanceler CancelerVolatileBlocked(void)
{
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gBattleMons[gBattlerAttacker].volatiles.healBlock && IsHealBlockPreventingMove(gBattlerAttacker, gCurrentMove))
{
gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE;
gBattleScripting.battler = gBattlerAttacker;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedHealBlockPrevents;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
@ -2117,7 +2119,7 @@ static enum MoveCanceller CancellerVolatileBlocked(void)
{
gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE;
gBattleScripting.battler = gBattlerAttacker;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedGravityPrevents;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
@ -2125,7 +2127,7 @@ static enum MoveCanceller CancellerVolatileBlocked(void)
else if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].throatChopTimer > gBattleTurnCounter && IsSoundMove(gCurrentMove))
{
gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsThroatChopPrevented;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
@ -2133,12 +2135,12 @@ static enum MoveCanceller CancellerVolatileBlocked(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerTaunted(void)
static enum MoveCanceler CancelerTaunted(void)
{
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].tauntTimer && IsBattleMoveStatus(gCurrentMove))
{
gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
@ -2146,12 +2148,12 @@ static enum MoveCanceller CancellerTaunted(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerImprisoned(void)
static enum MoveCanceler CancelerImprisoned(void)
{
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(gBattlerAttacker, gCurrentMove))
{
gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
@ -2159,7 +2161,7 @@ static enum MoveCanceller CancellerImprisoned(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerConfused(void)
static enum MoveCanceler CancelerConfused(void)
{
if (gBattleStruct->isAtkCancelerForCalledMove)
return MOVE_STEP_SUCCESS;
@ -2182,6 +2184,7 @@ static enum MoveCanceller CancellerConfused(void)
ctx.isCrit = FALSE;
ctx.randomFactor = FALSE;
ctx.updateFlags = TRUE;
ctx.isSelfInflicted = TRUE;
ctx.fixedBasePower = 40;
gBattleStruct->moveDamage[gBattlerAttacker] = CalculateMoveDamage(&ctx);
gProtectStructs[gBattlerAttacker].confusionSelfDmg = TRUE;
@ -2203,7 +2206,7 @@ static enum MoveCanceller CancellerConfused(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerParalysed(void)
static enum MoveCanceler CancelerParalysed(void)
{
if (!gBattleStruct->isAtkCancelerForCalledMove
&& (gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS)
@ -2212,7 +2215,7 @@ static enum MoveCanceller CancellerParalysed(void)
{
gProtectStructs[gBattlerAttacker].nonVolatileStatusImmobility = TRUE;
// This is removed in FRLG and Emerald for some reason
//CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
//CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
@ -2220,7 +2223,7 @@ static enum MoveCanceller CancellerParalysed(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerInfatuation(void)
static enum MoveCanceler CancelerInfatuation(void)
{
if (!gBattleStruct->isAtkCancelerForCalledMove && gBattleMons[gBattlerAttacker].volatiles.infatuation)
{
@ -2234,7 +2237,7 @@ static enum MoveCanceller CancellerInfatuation(void)
BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack);
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove;
}
return MOVE_STEP_BREAK;
@ -2242,7 +2245,7 @@ static enum MoveCanceller CancellerInfatuation(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerBide(void)
static enum MoveCanceler CancelerBide(void)
{
if (gBattleMons[gBattlerAttacker].volatiles.bideTurns)
{
@ -2272,7 +2275,7 @@ static enum MoveCanceller CancellerBide(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerThaw(void)
static enum MoveCanceler CancelerThaw(void)
{
if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE)
{
@ -2297,14 +2300,14 @@ static enum MoveCanceller CancellerThaw(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerStanceChangeTwo(void)
static enum MoveCanceler CancelerStanceChangeTwo(void)
{
if (B_STANCE_CHANGE_FAIL >= GEN_7 && !gBattleStruct->isAtkCancelerForCalledMove && TryFormChangeBeforeMove())
return MOVE_STEP_BREAK;
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerChoiceLock(void)
static enum MoveCanceler CancelerChoiceLock(void)
{
u16 *choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker];
enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
@ -2327,9 +2330,9 @@ static enum MoveCanceller CancellerChoiceLock(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerWeatherPrimal(void)
static enum MoveCanceler CancelerWeatherPrimal(void)
{
enum MoveCanceller effect = MOVE_STEP_SUCCESS;
enum MoveCanceler effect = MOVE_STEP_SUCCESS;
if (HasWeatherEffect() && GetMovePower(gCurrentMove) > 0)
{
u32 moveType = GetBattleMoveType(gCurrentMove);
@ -2347,7 +2350,7 @@ static enum MoveCanceller CancellerWeatherPrimal(void)
{
gBattleScripting.moveEffect = MOVE_EFFECT_NONE;
gProtectStructs[gBattlerAttacker].chargingTurn = FALSE;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
BattleScriptCall(BattleScript_PrimalWeatherBlocksMove);
}
@ -2355,7 +2358,7 @@ static enum MoveCanceller CancellerWeatherPrimal(void)
return effect;
}
static enum MoveCanceller CancellerDynamaxBlocked(void)
static enum MoveCanceler CancelerDynamaxBlocked(void)
{
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX) && IsMoveBlockedByDynamax(gCurrentMove))
{
@ -2366,7 +2369,7 @@ static enum MoveCanceller CancellerDynamaxBlocked(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerPowderStatus(void)
static enum MoveCanceler CancelerPowderStatus(void)
{
if (TryActivatePowderStatus(gCurrentMove))
{
@ -2383,7 +2386,7 @@ static enum MoveCanceller CancellerPowderStatus(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerProtean(void)
static enum MoveCanceler CancelerProtean(void)
{
u32 moveType = GetBattleMoveType(gCurrentMove);
if (ProteanTryChangeType(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), gCurrentMove, moveType))
@ -2400,7 +2403,7 @@ static enum MoveCanceller CancellerProtean(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerPsychicTerrain(void)
static enum MoveCanceler CancelerPsychicTerrain(void)
{
if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_PSYCHIC_TERRAIN)
&& GetChosenMovePriority(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) > 0
@ -2408,7 +2411,7 @@ static enum MoveCanceller CancellerPsychicTerrain(void)
&& GetMoveTarget(gCurrentMove) != MOVE_TARGET_OPPONENTS_FIELD
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget))
{
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedPsychicTerrainPrevents;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
@ -2416,7 +2419,7 @@ static enum MoveCanceller CancellerPsychicTerrain(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerExplodingDamp(void)
static enum MoveCanceler CancelerExplodingDamp(void)
{
u32 dampBattler = IsAbilityOnField(ABILITY_DAMP);
if (dampBattler && IsMoveDampBanned(gCurrentMove))
@ -2429,7 +2432,7 @@ static enum MoveCanceller CancellerExplodingDamp(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerMultihitMoves(void)
static enum MoveCanceler CancelerMultihitMoves(void)
{
if (GetMoveEffect(gCurrentMove) == EFFECT_MULTI_HIT)
{
@ -2501,7 +2504,7 @@ static enum MoveCanceller CancellerMultihitMoves(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerZMoves(void)
static enum MoveCanceler CancelerZMoves(void)
{
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE)
{
@ -2533,7 +2536,7 @@ static enum MoveCanceller CancellerZMoves(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerMultiTargetMoves(void)
static enum MoveCanceler CancelerMultiTargetMoves(void)
{
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
@ -2581,46 +2584,46 @@ static enum MoveCanceller CancellerMultiTargetMoves(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller (*const sMoveSuccessOrderCancellers[])(void) =
static enum MoveCanceler (*const sMoveSuccessOrderCancelers[])(void) =
{
[CANCELLER_FLAGS] = CancellerFlags,
[CANCELLER_STANCE_CHANGE_1] = CancellerStanceChangeOne,
[CANCELLER_SKY_DROP] = CancellerSkyDrop,
[CANCELLER_RECHARGE] = CancellerRecharge,
[CANCELLER_ASLEEP_OR_FROZEN] = CancellerAsleepOrFrozen,
[CANCELLER_OBEDIENCE] = CancellerObedience,
[CANCELLER_TRUANT] = CancellerTruant,
[CANCELLER_FLINCH] = CancellerFlinch,
[CANCELLER_INFATUATION] = CancellerInfatuation,
[CANCELLER_DISABLED] = CancellerDisabled,
[CANCELLER_VOLATILE_BLOCKED] = CancellerVolatileBlocked,
[CANCELLER_TAUNTED] = CancellerTaunted,
[CANCELLER_IMPRISONED] = CancellerImprisoned,
[CANCELLER_CONFUSED] = CancellerConfused,
[CANCELLER_PARALYSED] = CancellerParalysed,
[CANCELLER_BIDE] = CancellerBide,
[CANCELLER_THAW] = CancellerThaw,
[CANCELLER_STANCE_CHANGE_2] = CancellerStanceChangeTwo,
[CANCELLER_CHOICE_LOCK] = CancellerChoiceLock,
[CANCELLER_WEATHER_PRIMAL] = CancellerWeatherPrimal,
[CANCELLER_DYNAMAX_BLOCKED] = CancellerDynamaxBlocked,
[CANCELLER_POWDER_STATUS] = CancellerPowderStatus,
[CANCELLER_PROTEAN] = CancellerProtean,
[CANCELLER_PSYCHIC_TERRAIN] = CancellerPsychicTerrain,
[CANCELLER_EXPLODING_DAMP] = CancellerExplodingDamp,
[CANCELLER_MULTIHIT_MOVES] = CancellerMultihitMoves,
[CANCELLER_Z_MOVES] = CancellerZMoves,
[CANCELLER_MULTI_TARGET_MOVES] = CancellerMultiTargetMoves,
[CANCELER_FLAGS] = CancelerFlags,
[CANCELER_STANCE_CHANGE_1] = CancelerStanceChangeOne,
[CANCELER_SKY_DROP] = CancelerSkyDrop,
[CANCELER_RECHARGE] = CancelerRecharge,
[CANCELER_ASLEEP_OR_FROZEN] = CancelerAsleepOrFrozen,
[CANCELER_OBEDIENCE] = CancelerObedience,
[CANCELER_TRUANT] = CancelerTruant,
[CANCELER_FLINCH] = CancelerFlinch,
[CANCELER_INFATUATION] = CancelerInfatuation,
[CANCELER_DISABLED] = CancelerDisabled,
[CANCELER_VOLATILE_BLOCKED] = CancelerVolatileBlocked,
[CANCELER_TAUNTED] = CancelerTaunted,
[CANCELER_IMPRISONED] = CancelerImprisoned,
[CANCELER_CONFUSED] = CancelerConfused,
[CANCELER_PARALYSED] = CancelerParalysed,
[CANCELER_BIDE] = CancelerBide,
[CANCELER_THAW] = CancelerThaw,
[CANCELER_STANCE_CHANGE_2] = CancelerStanceChangeTwo,
[CANCELER_CHOICE_LOCK] = CancelerChoiceLock,
[CANCELER_WEATHER_PRIMAL] = CancelerWeatherPrimal,
[CANCELER_DYNAMAX_BLOCKED] = CancelerDynamaxBlocked,
[CANCELER_POWDER_STATUS] = CancelerPowderStatus,
[CANCELER_PROTEAN] = CancelerProtean,
[CANCELER_PSYCHIC_TERRAIN] = CancelerPsychicTerrain,
[CANCELER_EXPLODING_DAMP] = CancelerExplodingDamp,
[CANCELER_MULTIHIT_MOVES] = CancelerMultihitMoves,
[CANCELER_Z_MOVES] = CancelerZMoves,
[CANCELER_MULTI_TARGET_MOVES] = CancelerMultiTargetMoves,
};
enum MoveCanceller AtkCanceller_MoveSuccessOrder(void)
enum MoveCanceler AtkCanceler_MoveSuccessOrder(void)
{
enum MoveCanceller effect = MOVE_STEP_SUCCESS;
enum MoveCanceler effect = MOVE_STEP_SUCCESS;
while (gBattleStruct->atkCancellerTracker < CANCELLER_END && effect == MOVE_STEP_SUCCESS)
while (gBattleStruct->atkCancelerTracker < CANCELER_END && effect == MOVE_STEP_SUCCESS)
{
effect = sMoveSuccessOrderCancellers[gBattleStruct->atkCancellerTracker]();
gBattleStruct->atkCancellerTracker++;
effect = sMoveSuccessOrderCancelers[gBattleStruct->atkCancelerTracker]();
gBattleStruct->atkCancelerTracker++;
}
if (effect == MOVE_STEP_REMOVES_STATUS)
@ -3061,7 +3064,7 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a
&& !(IsBattleMoveStatus(move) && (abilityDef == ABILITY_MAGIC_BOUNCE || gProtectStructs[battlerDef].bounceMove)))
{
if (option == RUN_SCRIPT && !IsSpreadMove(GetBattlerMoveTargetType(battlerAtk, move)))
CancelMultiTurnMoves(battlerAtk, SKY_DROP_ATTACKCANCELLER_CHECK); // Don't cancel moves that can hit two targets bc one target might not be protected
CancelMultiTurnMoves(battlerAtk, SKY_DROP_ATTACKCANCELER_CHECK); // Don't cancel moves that can hit two targets bc one target might not be protected
battleScriptBlocksMove = BattleScript_DoesntAffectTargetAtkString;
}
@ -4689,9 +4692,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_EFFECT_SPORE:
{
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
if ((GetGenConfig(GEN_CONFIG_POWDER_GRASS) < GEN_6 || !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GRASS))
&& abilityAtk != ABILITY_OVERCOAT
&& GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES)
enum ItemHoldEffect holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
if (IsAffectedByPowderMove(gBattlerAttacker, abilityAtk, holdEffectAtk))
{
u32 poison, paralysis, sleep;
@ -4718,7 +4720,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& IsBattlerTurnDamaged(gBattlerTarget)
&& CanBeSlept(gBattlerTarget, gBattlerAttacker, abilityAtk, NOT_BLOCKED_BY_SLEEP_CLAUSE)
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker, TRUE), move))
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, abilityAtk, holdEffectAtk, move))
{
if (IsSleepClauseEnabled())
gBattleStruct->battlerState[gBattlerAttacker].sleepClauseEffectExempt = TRUE;
@ -5448,8 +5450,6 @@ bool32 CanBattlerEscape(u32 battler) // no ability check
{
if (gBattleStruct->battlerState[battler].commanderSpecies != SPECIES_NONE)
return FALSE;
else if (GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_SHED_SHELL)
return TRUE;
else if (B_GHOSTS_ESCAPE >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GHOST))
return TRUE;
else if (gBattleMons[battler].volatiles.escapePrevention)
@ -8501,6 +8501,24 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx)
return uq4_12_multiply_by_int_half_down(modifier, basePower);
}
static inline uq4_12_t ApplyOffensiveBadgeBoost(uq4_12_t modifier, u32 battler, u32 move)
{
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_ATTACK, battler) && IsBattleMovePhysical(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPATK, battler) && IsBattleMoveSpecial(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
return modifier;
}
static inline uq4_12_t ApplyDefensiveBadgeBoost(uq4_12_t modifier, u32 battler, u32 move)
{
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_DEFENSE, battler) && IsBattleMovePhysical(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPDEF, battler) && IsBattleMoveSpecial(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
return modifier;
}
static inline u32 CalcAttackStat(struct DamageContext *ctx)
{
u8 atkStage;
@ -8572,6 +8590,9 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx)
// apply attack stat modifiers
modifier = UQ_4_12(1.0);
if (ctx->isSelfInflicted)
return uq4_12_multiply_by_int_half_down(ApplyOffensiveBadgeBoost(modifier, battlerAtk, move), atkStat);
// attacker's abilities
switch (ctx->abilityAtk)
{
@ -8766,11 +8787,7 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx)
break;
}
// The offensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the corresponding flags set (eg. Badges)
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_ATTACK, battlerAtk) && IsBattleMovePhysical(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPATK, battlerAtk) && IsBattleMoveSpecial(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
modifier = ApplyOffensiveBadgeBoost(modifier, battlerAtk, move);
return uq4_12_multiply_by_int_half_down(modifier, atkStat);
}
@ -8855,6 +8872,9 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx)
// apply defense stat modifiers
modifier = UQ_4_12(1.0);
if (ctx->isSelfInflicted)
return uq4_12_multiply_by_int_half_down(ApplyDefensiveBadgeBoost(modifier, battlerDef, move), defStat);
// target's abilities
switch (ctx->abilityDef)
{
@ -8944,11 +8964,7 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx)
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && IsBattlerWeatherAffected(battlerDef, B_WEATHER_SNOW) && usesDefStat)
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5));
// The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the corresponding flags set (eg. Badges)
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_DEFENSE, battlerDef) && IsBattleMovePhysical(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPDEF, battlerDef) && IsBattleMoveSpecial(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
modifier = ApplyDefensiveBadgeBoost(modifier, battlerDef, move);
return uq4_12_multiply_by_int_half_down(modifier, defStat);
}
@ -11487,9 +11503,9 @@ bool32 IsAnyTargetAffected(u32 battlerAtk)
void UpdateStallMons(void)
{
if (IsBattlerTurnDamaged(gBattlerTarget) || IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove) || gMovesInfo[gCurrentMove].category == DAMAGE_CATEGORY_STATUS)
if (IsBattlerTurnDamaged(gBattlerTarget) || IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove) || GetMoveCategory(gCurrentMove) == DAMAGE_CATEGORY_STATUS)
return;
if (!IsDoubleBattle() || gMovesInfo[gCurrentMove].target == MOVE_TARGET_SELECTED)
if (!IsDoubleBattle() || GetMoveTarget(gCurrentMove) == MOVE_TARGET_SELECTED)
{
u32 moveType = GetBattleMoveType(gCurrentMove); // Probably doesn't handle dynamic move types right now
u32 abilityAtk = GetBattlerAbility(gBattlerAttacker);
@ -11965,3 +11981,12 @@ static bool32 IsOpposingSideEmpty(u32 battler)
return FALSE;
return TRUE;
}
bool32 IsAffectedByPowderMove(u32 battler, u32 ability, enum ItemHoldEffect holdEffect)
{
if ((GetGenConfig(GEN_CONFIG_POWDER_OVERCOAT) >= GEN_6 && ability == ABILITY_OVERCOAT)
|| (GetGenConfig(GEN_CONFIG_POWDER_GRASS) >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GRASS))
|| holdEffect == HOLD_EFFECT_SAFETY_GOGGLES)
return FALSE;
return TRUE;
}

View File

@ -2031,6 +2031,7 @@ const struct BattleAnimBackground gBattleAnimBackgroundTable[] =
[BG_STEEL_BEAM_OPPONENT] = {gBattleAnimBgImage_Highspeed, gBattleAnimBgPalette_SteelBeam, gBattleAnimBgTilemap_HighspeedOpponent},
[BG_STEEL_BEAM_PLAYER] = {gBattleAnimBgImage_Highspeed, gBattleAnimBgPalette_SteelBeam, gBattleAnimBgTilemap_HighspeedPlayer},
[BG_CHLOROBLAST] = {gBattleAnimBgImage_HydroCannon, gBattleAnimBgPalette_Chloroblast, gBattleAnimBgTilemap_HydroCannon},
[BG_RAINBOW] = {gBattleAnimBgImage_Rainbow, gBattleAnimBGPalette_Rainbow, gBattleAnimBgTilemap_Rainbow},
[BG_RAINBOW_PLAYER] = {gBattleAnimBgImage_RainbowPlayer, gBattleAnimBGPalette_Rainbow, gBattleAnimBgTilemap_RainbowPlayer},
[BG_RAINBOW_OPPONENT] = {gBattleAnimBgImage_RainbowOpponent, gBattleAnimBGPalette_Rainbow, gBattleAnimBgTilemap_RainbowOpponent},
[BG_SWAMP] = {gBattleAnimBgImage_Swamp, gBattleAnimBGPalette_Swamp, gBattleAnimBgTilemap_Swamp},
};

View File

@ -47,14 +47,14 @@ static const struct BgTemplate sBgTemplates[3] =
},
};
static void DecompressErrorScreenTextPrint(const u8 *text, u8 x, u8 y)
static void DecompressErrorScreenTextPrint(u32 window, const u8 *text, u8 x, u8 y)
{
u8 color[3];
color[0] = TEXT_COLOR_TRANSPARENT;
color[1] = TEXT_DYNAMIC_COLOR_6;
color[2] = TEXT_COLOR_LIGHT_GRAY;
AddTextPrinterParameterized4(0, FONT_NORMAL, x * 8, y * 8 + 1, 0, 0, color, 0, text);
AddTextPrinterParameterized4(window, FONT_NORMAL, x * 8, y * 8 + 1, 0, 0, color, 0, text);
}
static void GetHexStringFromU32(u8 *str, u32 value)
@ -121,10 +121,7 @@ static void GetHexStringFromU32(u8 *str, u32 value)
}
}
void DecompressionError_CB2(void)
{
static const struct WindowTemplate textWin[] =
{
static const struct WindowTemplate sTextWin =
{
.bg = 0,
.tilemapLeft = 3,
@ -133,9 +130,11 @@ void DecompressionError_CB2(void)
.height = 16,
.paletteNum = 15,
.baseBlock = 1,
}
};
void DecompressionError_CB2(void)
{
if (sErrorAddress == 0)
return;
@ -159,20 +158,20 @@ void DecompressionError_CB2(void)
ResetPaletteFade();
LoadPalette(gTextWindowFrame1_Pal, 0xE0, 0x20);
LoadPalette(gStandardMenuPalette, 0xF0, 0x20);
InitWindows(textWin);
DrawStdFrameWithCustomTileAndPalette(0, TRUE, 0x214, 0xE);
u32 window = AddWindow(&sTextWin);
DrawStdFrameWithCustomTileAndPalette(window, TRUE, 0x214, 0xE);
static const u8 romCheckFailMessage[] =_(
"{COLOR RED}ERROR! {COLOR DARK_GRAY}Decompression Failed!\n"
"\n"
"Address:\n"
"Error:\n");
DecompressErrorScreenTextPrint(romCheckFailMessage, 1, 0);
DecompressErrorScreenTextPrint(window, romCheckFailMessage, 1, 0);
u8 addressStr[11];
u8 errorStr[11];
GetHexStringFromU32(addressStr, sErrorAddress);
GetHexStringFromU32(errorStr, sCompressionError);
DecompressErrorScreenTextPrint(addressStr, 7, 4);
DecompressErrorScreenTextPrint(errorStr, 7, 6);
DecompressErrorScreenTextPrint(window, addressStr, 7, 4);
DecompressErrorScreenTextPrint(window, errorStr, 7, 6);
TransferPlttBuffer();
*(u16*)PLTT = RGB(17, 18, 31);
ShowBg(0);

View File

@ -2627,37 +2627,29 @@ void UpdateLightSprite(struct Sprite *sprite)
return;
}
// Note: Don't set window registers during hardware fade!
switch (sprite->sLightType)
if (sprite->sLightType == LIGHT_TYPE_BALL)
{
default:
case LIGHT_TYPE_BALL:
if (gPaletteFade.active) // if palette fade is active, don't flicker since the timer won't be updated
{
Weather_SetBlendCoeffs(7, BASE_SHADOW_INTENSITY);
sprite->invisible = FALSE;
}
else if (gPlayerAvatar.tileTransitionState)
{
Weather_SetBlendCoeffs(7, BASE_SHADOW_INTENSITY); // As long as the second coefficient stays 12, shadows will not change
sprite->invisible = FALSE;
if (GetSpritePaletteTagByPaletteNum(sprite->oam.paletteNum) == OBJ_EVENT_PAL_TAG_LIGHT_2)
LoadSpritePaletteInSlot(&sObjectEventSpritePalettes[FindObjectEventPaletteIndexByTag(OBJ_EVENT_PAL_TAG_LIGHT)], sprite->oam.paletteNum);
}
else if ((sprite->invisible = gTimeUpdateCounter & 1))
{
Weather_SetBlendCoeffs(7, BASE_SHADOW_INTENSITY);
sprite->invisible = FALSE;
if (GetSpritePaletteTagByPaletteNum(sprite->oam.paletteNum) == OBJ_EVENT_PAL_TAG_LIGHT_2)
LoadSpritePaletteInSlot(&sObjectEventSpritePalettes[FindObjectEventPaletteIndexByTag(OBJ_EVENT_PAL_TAG_LIGHT)], sprite->oam.paletteNum);
}
break;
case LIGHT_TYPE_PKMN_CENTER_SIGN:
case LIGHT_TYPE_POKE_MART_SIGN:
Weather_SetBlendCoeffs(12, BASE_SHADOW_INTENSITY);
} else {
sprite->invisible = FALSE;
break;
}
// Note: Don't set window registers during hardware fade!
Weather_SetBlendCoeffs(7, BASE_SHADOW_INTENSITY);
}
// Spawn a light at a map coordinate

View File

@ -393,6 +393,7 @@ void SetMewAboveGrass(void)
LoadSpritePalette(&gSpritePalette_GeneralFieldEffect1);
UpdateSpritePaletteWithWeather(IndexOfSpritePaletteTag(gSpritePalette_GeneralFieldEffect1.tag), FALSE);
gSprites[mew->spriteId].subspriteTableNum = 1;
x = mew->currentCoords.x;
y = mew->currentCoords.y;

View File

@ -2774,7 +2774,6 @@ bool8 ObjectMovingOnRockStairs(struct ObjectEvent *objectEvent, u8 direction)
s16 x = objectEvent->currentCoords.x;
s16 y = objectEvent->currentCoords.y;
// TODO followers on sideways stairs
if (IsFollowerVisible() && GetFollowerObject() != NULL && (objectEvent->isPlayer || objectEvent->localId == OBJ_EVENT_ID_FOLLOWER))
return FALSE;

View File

@ -954,6 +954,10 @@ u32 DetermineFollowerNPCState(struct ObjectEvent *follower, u32 state, u32 direc
RETURN_STATE(MOVEMENT_ACTION_WALK_NORMAL_DOWN, direction);
// Slow stairs.
case MOVEMENT_ACTION_WALK_SLOW_STAIRS_DOWN ... MOVEMENT_ACTION_WALK_SLOW_STAIRS_RIGHT:
RETURN_STATE(MOVEMENT_ACTION_WALK_SLOW_STAIRS_DOWN, direction);
default:
return MOVEMENT_INVALID;
}

View File

@ -1673,9 +1673,11 @@ const u32 gBattleAnimSpriteGfx_WhiteShadow[] = INCBIN_U32("graphics/battle_anims
const u16 gBattleAnimSpritePal_WhiteShadow[] = INCBIN_U16("graphics/battle_anims/sprites/white_shadow.gbapal");
// Pledge Effect field status - Rainbow
const u32 gBattleAnimBgImage_Rainbow[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow.4bpp.smol");
const u16 gBattleAnimBGPalette_Rainbow[] = INCBIN_U16("graphics/battle_anims/backgrounds/rainbow.gbapal");
const u32 gBattleAnimBgTilemap_Rainbow[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow.bin.smolTM");
const u32 gBattleAnimBgImage_RainbowPlayer[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow_player_tile.4bpp.smol");
const u32 gBattleAnimBgImage_RainbowOpponent[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow_opponent_tile.4bpp.smol");
const u16 gBattleAnimBGPalette_Rainbow[] = INCBIN_U16("graphics/battle_anims/backgrounds/rainbow_player_tile.gbapal");
const u32 gBattleAnimBgTilemap_RainbowPlayer[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow_player_tile.bin.smolTM");
const u32 gBattleAnimBgTilemap_RainbowOpponent[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow_opponent_tile.bin.smolTM");
// Pledge Effect field status - Swamp
const u32 gBattleAnimBgImage_Swamp[] = INCBIN_U32("graphics/battle_anims/backgrounds/swampswizzle.4bpp.smol");

View File

@ -2916,8 +2916,8 @@ static s32 CompareItemsAlphabetically(enum Pocket pocketId, struct ItemSlot item
if (pocketId == POCKET_TM_HM)
{
name1 = gMovesInfo[GetTMHMMoveId(GetItemTMHMIndex(item1.itemId))].name;
name2 = gMovesInfo[GetTMHMMoveId(GetItemTMHMIndex(item2.itemId))].name;
name1 = GetMoveName(GetTMHMMoveId(GetItemTMHMIndex(item1.itemId)));
name2 = GetMoveName(GetTMHMMoveId(GetItemTMHMIndex(item2.itemId)));
}
else
{

View File

@ -1254,7 +1254,7 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon)
switch (battleUsage)
{
case EFFECT_ITEM_INCREASE_STAT:
if (gBattleMons[gBattlerInMenuId].statStages[GetItemEffect(itemId)[1]] == MAX_STAT_STAGE)
if (CompareStat(gBattlerInMenuId, GetItemEffect(itemId)[1], MAX_STAT_STAGE, CMP_EQUAL, GetBattlerAbility(gBattlerInMenuId)))
cannotUse = TRUE;
break;
case EFFECT_ITEM_SET_FOCUS_ENERGY:
@ -1293,11 +1293,12 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon)
case EFFECT_ITEM_INCREASE_ALL_STATS:
{
u32 ability = GetBattlerAbility(gBattlerInMenuId);
cannotUse = TRUE;
for (i = STAT_ATK; i < NUM_STATS; i++)
{
if (CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL, ability))
if (!CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL, ability))
{
cannotUse = TRUE;
cannotUse = FALSE;
break;
}
}

View File

@ -176,7 +176,6 @@ void BreakSubStringNaive(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId, enum
Free(allWords);
}
#undef SCROLL_PROMPT_WIDTH
void BreakSubStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId, enum ToggleScrollPrompt toggleScrollPrompt)
{
@ -246,6 +245,9 @@ void BreakSubStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId,
for (u32 i = 1; i < numWords; i++)
totalWidth += allWords[i].width + spaceWidth;
if (toggleScrollPrompt == SHOW_SCROLL_PROMPT)
totalWidth += SCROLL_PROMPT_WIDTH;
// If it doesn't fit on 1 line, do fancy line break calculation
// NOTE: Currently the line break calculation isn't fancy
if (totalWidth > maxWidth)
@ -256,6 +258,8 @@ void BreakSubStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId,
bool32 shouldTryAgain;
for (currWordIndex = 0; currWordIndex < numWords; currWordIndex++)
{
if (toggleScrollPrompt == SHOW_SCROLL_PROMPT && currWordIndex + 1 == numWords)
currLineWidth += SCROLL_PROMPT_WIDTH;
if (currLineWidth + allWords[currWordIndex].length > maxWidth)
{
totalLines++;
@ -266,6 +270,10 @@ void BreakSubStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId,
currLineWidth += allWords[currWordIndex].width + spaceWidth;
}
}
if (currLineWidth > maxWidth)
totalLines++;
// LINE LAYOUT STARTS HERE
struct StringLine *stringLines;
do
@ -424,3 +432,4 @@ bool32 StringHasManualBreaks(u8 *src)
}
return FALSE;
}
#undef SCROLL_PROMPT_WIDTH

View File

@ -202,7 +202,7 @@ static const struct OamData sOamData_CeilingCrumbleSmall =
static const struct SpriteTemplate sSpriteTemplate_CeilingCrumbleSmall =
{
.tileTag = TAG_CEILING_CRUMBLE,
.paletteTag = TAG_NONE,
.paletteTag = TAG_CEILING_CRUMBLE,
.oam = &sOamData_CeilingCrumbleSmall,
.anims = sAnims_CeilingCrumbleSmall,
.images = NULL,
@ -241,7 +241,7 @@ static const struct OamData sOamData_CeilingCrumbleLarge =
static const struct SpriteTemplate sSpriteTemplate_CeilingCrumbleLarge =
{
.tileTag = TAG_CEILING_CRUMBLE,
.paletteTag = TAG_NONE,
.paletteTag = TAG_CEILING_CRUMBLE,
.oam = &sOamData_CeilingCrumbleLarge,
.anims = sAnims_CeilingCrumbleLarge,
.images = NULL,
@ -420,6 +420,7 @@ static void IncrementCeilingCrumbleFinishedCount(void)
void DoMirageTowerCeilingCrumble(void)
{
LoadSpritePaletteWithTag(sMirageTowerCrumbles_Palette, TAG_CEILING_CRUMBLE);
LoadSpriteSheets(sCeilingCrumbleSpriteSheets);
CreateCeilingCrumbleSprites();
CreateTask(WaitCeilingCrumble, 8);
@ -454,17 +455,12 @@ static void CreateCeilingCrumbleSprites(void)
{
spriteId = CreateSprite(&sSpriteTemplate_CeilingCrumbleLarge, sCeilingCrumblePositions[i][0] + 120, sCeilingCrumblePositions[i][1], 8);
gSprites[spriteId].oam.priority = 0;
// These sprites use color index 11 from the player's sprite palette. This probably wasn't intentional.
// The palettes for Brendan and May have different shades of green at this index, so the color of these sprites changes
// depending on the player's gender (and neither shade of green particularly fits a crumbling yellow/brown ceiling).
gSprites[spriteId].oam.paletteNum = PALSLOT_PLAYER;
gSprites[spriteId].sIndex = i;
}
for (i = 0; i < ARRAY_COUNT(sCeilingCrumblePositions); i++)
{
spriteId = CreateSprite(&sSpriteTemplate_CeilingCrumbleSmall, sCeilingCrumblePositions[i][0] + 115, sCeilingCrumblePositions[i][1] - 3, 8);
gSprites[spriteId].oam.priority = 0;
gSprites[spriteId].oam.paletteNum = PALSLOT_PLAYER;
gSprites[spriteId].sIndex = i;
}
}

View File

@ -6300,6 +6300,7 @@ static void DeleteInvalidFusionMoves(struct Pokemon *mon, u32 species)
}
}
#if P_FUSION_FORMS
static void SwapFusionMonMoves(struct Pokemon *mon, const u16 moveTable[][2], u32 mode)
{
u32 oldMoveIndex, newMoveIndex;
@ -6320,13 +6321,16 @@ static void SwapFusionMonMoves(struct Pokemon *mon, const u16 moveTable[][2], u3
{
if (move == moveTable[j][oldMoveIndex])
{
u32 pp = GetMovePP(moveTable[j][newMoveIndex]);
SetMonData(mon, MON_DATA_MOVE1 + i, &moveTable[j][newMoveIndex]);
SetMonData(mon, MON_DATA_PP1 + i, &gMovesInfo[moveTable[j][newMoveIndex]].pp);
SetMonData(mon, MON_DATA_PP1 + i, &pp);
}
}
}
}
#endif //P_FUSION_FORMS
static void Task_TryItemUseFusionChange(u8 taskId)
{
struct Pokemon *mon = &gPlayerParty[gTasks[taskId].firstFusionSlot];
@ -6420,6 +6424,7 @@ static void Task_TryItemUseFusionChange(u8 taskId)
{
if (gTasks[taskId].fusionType == FUSE_MON)
{
#if P_FUSION_FORMS
#if P_FAMILY_KYUREM
#if P_FAMILY_RESHIRAM
if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE)
@ -6430,11 +6435,13 @@ static void Task_TryItemUseFusionChange(u8 taskId)
SwapFusionMonMoves(mon, gKyuremBlackSwapMoveTable, FUSE_MON);
#endif //P_FAMILY_ZEKROM
#endif //P_FAMILY_KYUREM
#endif //P_FUSION_FORMS
if (gTasks[taskId].moveToLearn != 0)
FormChangeTeachMove(taskId, gTasks[taskId].moveToLearn, gTasks[taskId].firstFusionSlot);
}
else //(gTasks[taskId].fusionType == UNFUSE_MON)
{
#if P_FUSION_FORMS
#if P_FAMILY_KYUREM
#if P_FAMILY_RESHIRAM
if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE)
@ -6445,6 +6452,7 @@ static void Task_TryItemUseFusionChange(u8 taskId)
SwapFusionMonMoves(mon, gKyuremBlackSwapMoveTable, UNFUSE_MON);
#endif //P_FAMILY_ZEKROM
#endif //P_FAMILY_KYUREM
#endif //P_FUSION_FORMS
if ( gTasks[taskId].tExtraMoveHandling == FORGET_EXTRA_MOVES)
{
DeleteInvalidFusionMoves(mon, gTasks[taskId].fusionResult);

View File

@ -11,6 +11,7 @@
#include "sprite.h"
#include "task.h"
#include "trig.h"
#include "test_runner.h"
#include "util.h"
#include "data.h"
#include "item.h"
@ -597,8 +598,8 @@ static void Task_DoPokeballSendOutAnim(u8 taskId)
{
u32 throwCaseId, ballId, battler, ballSpriteId;
bool32 notSendOut = FALSE;
u32 throwXoffset = (B_ENEMY_THROW_BALLS >= GEN_6) ? 24 : 0;
s32 throwYoffset = (B_ENEMY_THROW_BALLS >= GEN_6) ? -16 : 24;
u32 throwXoffset = (B_ENEMY_THROW_BALLS >= GEN_6 && !gTestRunnerHeadless) ? 24 : 0;
s32 throwYoffset = (B_ENEMY_THROW_BALLS >= GEN_6 && !gTestRunnerHeadless) ? -16 : 24;
if (gTasks[taskId].tFrames == 0)
{
@ -675,7 +676,7 @@ static inline void DoPokeballSendOutSoundEffect(u32 battler)
static inline void *GetOpponentMonSendOutCallback(void)
{
return (B_ENEMY_THROW_BALLS >= GEN_6) ? SpriteCB_MonSendOut_1 : SpriteCB_OpponentMonSendOut;
return (B_ENEMY_THROW_BALLS >= GEN_6 && !gTestRunnerHeadless) ? SpriteCB_MonSendOut_1 : SpriteCB_OpponentMonSendOut;
}
// This sequence of functions is very similar to those that get run when
@ -1205,7 +1206,7 @@ static void SpriteCB_MonSendOut_2(struct Sprite *sprite)
u32 r7;
bool32 rightPosition = (IsBattlerPlayer(sprite->sBattler)) ? B_POSITION_PLAYER_RIGHT : B_POSITION_OPPONENT_RIGHT;
if (HIBYTE(sprite->data[7]) >= 35 && HIBYTE(sprite->data[7]) < 80)
if (HIBYTE(sprite->data[7]) >= 35 && HIBYTE(sprite->data[7]) < 80 && !gTestRunnerHeadless)
{
s16 r4;
@ -1246,7 +1247,8 @@ static void SpriteCB_MonSendOut_2(struct Sprite *sprite)
sprite->data[0] = 0;
if (IsDoubleBattle() && gBattleSpritesDataPtr->animationData->introAnimActive
&& sprite->sBattler == GetBattlerAtPosition(rightPosition))
&& sprite->sBattler == GetBattlerAtPosition(rightPosition)
&& !gTestRunnerHeadless)
sprite->callback = SpriteCB_ReleaseMon2FromBall;
else
sprite->callback = SpriteCB_ReleaseMonFromBall;
@ -1269,12 +1271,15 @@ static void SpriteCB_ReleaseMon2FromBall(struct Sprite *sprite)
static void SpriteCB_OpponentMonSendOut(struct Sprite *sprite)
{
if (gTestRunnerHeadless)
sprite->data[0] = 15;
sprite->data[0]++;
if (sprite->data[0] > 15)
{
sprite->data[0] = 0;
if (IsDoubleBattle() && gBattleSpritesDataPtr->animationData->introAnimActive
&& sprite->sBattler == GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT))
&& sprite->sBattler == GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT)
&& !gTestRunnerHeadless)
sprite->callback = SpriteCB_ReleaseMon2FromBall;
else
sprite->callback = SpriteCB_ReleaseMonFromBall;
@ -1534,7 +1539,7 @@ void StartHealthboxSlideIn(u8 battler)
healthboxSprite->y2 = -healthboxSprite->y2;
}
gSprites[healthboxSprite->data[5]].callback(&gSprites[healthboxSprite->data[5]]);
if (GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)
if (GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT && !gTestRunnerHeadless)
healthboxSprite->callback = SpriteCB_HealthboxSlideInDelayed;
}

View File

@ -4090,6 +4090,12 @@ static void UNUSED HighlightScreenSelectBarItem(u8 selectedScreen, u16 unused)
#define tPersonalityLo data[14]
#define tPersonalityHi data[15]
// Types palettes need to be loaded at a different slot than anticipated by gTypesInfo
// to avoid overlapping with caught mon sprite palette slot
// Normal type info palette slots: 13, 14 and 15
// Caught mon palette slot: 15
#define TYPE_INFO_PALETTE_NUM_OFFSET -1
void Task_DisplayCaughtMonDexPageHGSS(u8 taskId)
{
u8 spriteId;
@ -4351,9 +4357,9 @@ static void SetTypeIconPosAndPal(u8 typeId, u8 x, u8 y, u8 spriteArrayId)
sprite = &gSprites[sPokedexView->typeIconSpriteIds[spriteArrayId]];
StartSpriteAnim(sprite, typeId);
if (typeId < NUMBER_OF_MON_TYPES)
sprite->oam.paletteNum = gTypesInfo[typeId].palette;
sprite->oam.paletteNum = gTypesInfo[typeId].palette + TYPE_INFO_PALETTE_NUM_OFFSET;
else
sprite->oam.paletteNum = sContestCategoryToOamPaletteNum[typeId - NUMBER_OF_MON_TYPES];
sprite->oam.paletteNum = sContestCategoryToOamPaletteNum[typeId - NUMBER_OF_MON_TYPES] + TYPE_INFO_PALETTE_NUM_OFFSET;
sprite->x = x + 16;
sprite->y = y + 8;
SetSpriteInvisibility(spriteArrayId, FALSE);
@ -4394,7 +4400,8 @@ static void CreateTypeIconSprites(void)
u8 i;
LoadCompressedSpriteSheet(&gSpriteSheet_MoveTypes);
LoadPalette(gMoveTypes_Pal, 0x1D0, 0x60);
u32 paletteNum = gTypesInfo[TYPE_NORMAL].palette + TYPE_INFO_PALETTE_NUM_OFFSET;
LoadPalette(gMoveTypes_Pal, OBJ_PLTT_ID(paletteNum), 3 * PLTT_SIZE_4BPP);
for (i = 0; i < 2; i++)
{
if (sPokedexView->typeIconSpriteIds[i] == 0xFF)
@ -4574,6 +4581,7 @@ static u16 CreateSizeScreenTrainerPic(u16 species, s16 x, s16 y, s8 paletteSlot)
return CreateTrainerPicSprite(species, TRUE, x, y, paletteSlot, TAG_NONE);
}
#undef TYPE_INFO_PALETTE_NUM_OFFSET
//************************************
//* *

View File

@ -2434,7 +2434,7 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
data[retVal++] = substruct0->nickname12;
}
}
else if (POKEMON_NAME_LENGTH >= 11)
else if (field != MON_DATA_NICKNAME10 && POKEMON_NAME_LENGTH >= 11)
{
if (substruct0->nickname11 == 0)
{

View File

@ -34,6 +34,7 @@ static void Task_Fanfare(u8 taskId);
static void CreateFanfareTask(void);
static void RestoreBGMVolumeAfterPokemonCry(void);
// The 1st argument in the table is the length of the fanfare, measured in frames. This is calculated by taking the duration of the midi file, multiplying by 59.72750056960583, and rounding up to the next nearest integer.
static const struct Fanfare sFanfares[] = {
[FANFARE_LEVEL_UP] = { MUS_LEVEL_UP, 80 },
[FANFARE_OBTAIN_ITEM] = { MUS_OBTAIN_ITEM, 160 },

View File

@ -28,6 +28,8 @@
#if T_SHOULD_RUN_MOVE_ANIM
EWRAM_DATA bool32 gLoadFail = FALSE;
EWRAM_DATA bool32 gCountAllocs = FALSE;
EWRAM_DATA s32 gSpriteAllocs = 0;
#endif // T_SHOULD_RUN_MOVE_ANIM
struct SpriteCopyRequest
@ -1501,6 +1503,10 @@ void LoadSpriteSheets(const struct SpriteSheet *sheets)
void FreeSpriteTilesByTag(u16 tag)
{
#if T_SHOULD_RUN_MOVE_ANIM
if (gCountAllocs)
gSpriteAllocs--;
#endif
u8 index = IndexOfSpriteTileTag(tag);
if (index != 0xFF)
{
@ -1566,6 +1572,10 @@ u16 GetSpriteTileTagByTileStart(u16 start)
void AllocSpriteTileRange(u16 tag, u16 start, u16 count)
{
#if T_SHOULD_RUN_MOVE_ANIM
if (gCountAllocs)
gSpriteAllocs++;
#endif
u8 freeIndex = IndexOfSpriteTileTag(TAG_NONE);
sSpriteTileRangeTags[freeIndex] = tag;
SET_SPRITE_TILE_RANGE(freeIndex, start, count);

View File

@ -7,5 +7,7 @@ const bool8 gTestRunnerEnabled = FALSE;
// The Makefile patches gTestRunnerHeadless as part of make test.
// This allows us to open the ROM in an mgba with a UI and see the
// animations and messages play, which helps when debugging a test.
#if TESTING
const bool8 gTestRunnerHeadless = FALSE;
#endif
const bool8 gTestRunnerSkipIsFail = FALSE;

View File

@ -474,7 +474,7 @@ static u8 CheckTrainer(u8 objectEventId)
if (GetTrainerFlagFromScriptPointer(trainerBattlePtr))
{
//If there is a rematch, we want to trigger the approach sequence
if (GetRematchFromScriptPointer(trainerBattlePtr))
if (I_VS_SEEKER_CHARGING && GetRematchFromScriptPointer(trainerBattlePtr))
{
trainerBattlePtr = NULL;
numTrainers = 0xFF;

View File

@ -725,6 +725,7 @@ static u16 GetTrainerFlagFromScript(const u8 *script)
trainerFlag |= script[1] << 8;
break;
case SCR_OP_CALLNATIVE:
{
u32 callnativeFunc = (((((script[4] << 8) + script[3]) << 8) + script[2]) << 8) + script[1];
if (callnativeFunc == ((u32)NativeVsSeekerRematchId | 0xA000000)) // | 0xA000000 corresponds to the request_effects=1 version of the function
{
@ -737,6 +738,7 @@ static u16 GetTrainerFlagFromScript(const u8 *script)
trainerFlag = TRAINER_NONE;
}
break;
}
default:
trainerFlag = TRAINER_NONE;
break;

View File

@ -35,7 +35,21 @@ SINGLE_BATTLE_TEST("Bad Dreams causes the sleeping enemy Pokemon to lose 1/8 of
}
}
TO_DO_BATTLE_TEST("Bad Dreams affects Pokémon with Comatose")
SINGLE_BATTLE_TEST("Bad Dreams causes Pokémon with Comatose to lose 1/8 of HP")
{
GIVEN {
PLAYER(SPECIES_DARKRAI);
OPPONENT(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); }
} WHEN {
TURN {;}
} SCENE {
ABILITY_POPUP(player, ABILITY_BAD_DREAMS);
MESSAGE("The opposing Komala is tormented!");
HP_BAR(opponent);
} THEN {
EXPECT_EQ(opponent->hp, opponent->maxHP - opponent->maxHP / 8);
}
}
DOUBLE_BATTLE_TEST("Bad Dreams does not activate if only the partner Pokemon is sleeping")
{

View File

@ -1,4 +1,20 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Bulletproof makes ballistic moves fail against the ability user");
SINGLE_BATTLE_TEST("Bulletproof makes ballistic moves fail against the ability user")
{
GIVEN {
ASSUME(IsBallisticMove(MOVE_ELECTRO_BALL));
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CHESPIN) { Ability(ABILITY_BULLETPROOF); }
} WHEN {
TURN { MOVE(player, MOVE_ELECTRO_BALL); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_BULLETPROOF);
MESSAGE("The opposing Chespin's Bulletproof blocks Electro Ball!");
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRO_BALL, player);
HP_BAR(opponent);
}
}
}

View File

@ -1,4 +1,108 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Dark Aura (Ability) test titles")
DOUBLE_BATTLE_TEST("Dark Aura increases the power of all Dark-type attacks by 33%")
{
s16 damage[8];
GIVEN {
PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); }
PLAYER(SPECIES_LINOONE);
PLAYER(SPECIES_LINOONE);
OPPONENT(SPECIES_LINOONE);
OPPONENT(SPECIES_LINOONE);
} WHEN {
TURN { MOVE(playerRight, MOVE_SKILL_SWAP, target: playerLeft); }
TURN { SWITCH(playerLeft, 2); }
TURN {
MOVE(playerLeft, MOVE_BITE, target:opponentLeft, secondaryEffect:FALSE);
MOVE(playerRight, MOVE_BITE, target:opponentRight, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_BITE, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_BITE, target:playerRight, secondaryEffect:FALSE);
}
TURN { MOVE(opponentLeft, MOVE_GASTRO_ACID, target:playerRight); }
TURN {
MOVE(playerLeft, MOVE_BITE, target:opponentLeft, secondaryEffect:FALSE);
MOVE(playerRight, MOVE_BITE, target:opponentRight, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_BITE, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_BITE, target:playerRight, secondaryEffect:FALSE);
}
} SCENE {
// Turn 1
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerRight);
HP_BAR(opponentRight, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[2]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentRight);
HP_BAR(playerRight, captureDamage: &damage[3]);
// Turn 2
ANIMATION(ANIM_TYPE_MOVE, MOVE_GASTRO_ACID, opponentLeft);
// Turn 3
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[4]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerRight);
HP_BAR(opponentRight, captureDamage: &damage[5]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[6]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentRight);
HP_BAR(playerRight, captureDamage: &damage[7]);
} THEN {
EXPECT_MUL_EQ(damage[4], UQ_4_12(1.33), damage[0]);
EXPECT_MUL_EQ(damage[5], UQ_4_12(1.33), damage[1]);
EXPECT_MUL_EQ(damage[6], UQ_4_12(1.33), damage[2]);
EXPECT_MUL_EQ(damage[7], UQ_4_12(1.33), damage[3]);
}
}
DOUBLE_BATTLE_TEST("Dark Aura's effect doesn't stack multiple times")
{
s16 damage[6];
GIVEN {
PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); }
PLAYER(SPECIES_WOBBUFFET) { HP(9999); MaxHP(9999); }
PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); }
OPPONENT(SPECIES_WOBBUFFET) { HP(9999); MaxHP(9999); }
OPPONENT(SPECIES_WOBBUFFET) { HP(9999); MaxHP(9999); }
} WHEN {
TURN {
MOVE(playerLeft, MOVE_BITE, target:opponentLeft, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_BITE, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_BITE, target:playerLeft, secondaryEffect:FALSE);
}
TURN { SWITCH(playerRight, 2); }
TURN {
MOVE(playerLeft, MOVE_BITE, target:opponentLeft, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_BITE, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_BITE, target:playerLeft, secondaryEffect:FALSE);
}
} SCENE {
// Turn 1
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentRight);
HP_BAR(playerLeft, captureDamage: &damage[2]);
// Turn 2
SWITCH_OUT_MESSAGE("Wobbuffet");
SEND_IN_MESSAGE("Yveltal");
// Turn 3
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[3]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[4]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentRight);
HP_BAR(playerLeft, captureDamage: &damage[5]);
} THEN {
EXPECT_EQ(damage[3], damage[0]);
EXPECT_EQ(damage[4], damage[1]);
EXPECT_EQ(damage[5], damage[2]);
}
}

View File

@ -1,4 +1,108 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Fairy Aura (Ability) test titles")
DOUBLE_BATTLE_TEST("Fairy Aura increases the power of all Fairy-type attacks by 33%")
{
s16 damage[8];
GIVEN {
PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); }
PLAYER(SPECIES_LINOONE);
PLAYER(SPECIES_LINOONE);
OPPONENT(SPECIES_LINOONE);
OPPONENT(SPECIES_LINOONE);
} WHEN {
TURN { MOVE(playerRight, MOVE_SKILL_SWAP, target: playerLeft); }
TURN { SWITCH(playerLeft, 2); }
TURN {
MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft, secondaryEffect:FALSE);
MOVE(playerRight, MOVE_PLAY_ROUGH, target:opponentRight, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_PLAY_ROUGH, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_PLAY_ROUGH, target:playerRight, secondaryEffect:FALSE);
}
TURN { MOVE(opponentLeft, MOVE_GASTRO_ACID, target:playerRight); }
TURN {
MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft, secondaryEffect:FALSE);
MOVE(playerRight, MOVE_PLAY_ROUGH, target:opponentRight, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_PLAY_ROUGH, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_PLAY_ROUGH, target:playerRight, secondaryEffect:FALSE);
}
} SCENE {
// Turn 1
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerRight);
HP_BAR(opponentRight, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[2]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentRight);
HP_BAR(playerRight, captureDamage: &damage[3]);
// Turn 2
ANIMATION(ANIM_TYPE_MOVE, MOVE_GASTRO_ACID, opponentLeft);
// Turn 3
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[4]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerRight);
HP_BAR(opponentRight, captureDamage: &damage[5]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[6]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentRight);
HP_BAR(playerRight, captureDamage: &damage[7]);
} THEN {
EXPECT_MUL_EQ(damage[4], UQ_4_12(1.33), damage[0]);
EXPECT_MUL_EQ(damage[5], UQ_4_12(1.33), damage[1]);
EXPECT_MUL_EQ(damage[6], UQ_4_12(1.33), damage[2]);
EXPECT_MUL_EQ(damage[7], UQ_4_12(1.33), damage[3]);
}
}
DOUBLE_BATTLE_TEST("Fairy Aura's effect doesn't stack multiple times")
{
s16 damage[6];
GIVEN {
PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); }
PLAYER(SPECIES_WOBBUFFET) { HP(9999); MaxHP(9999); }
PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); }
OPPONENT(SPECIES_WOBBUFFET) { HP(9999); MaxHP(9999); }
OPPONENT(SPECIES_WOBBUFFET) { HP(9999); MaxHP(9999); }
} WHEN {
TURN {
MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_PLAY_ROUGH, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_PLAY_ROUGH, target:playerLeft, secondaryEffect:FALSE);
}
TURN { SWITCH(playerRight, 2); }
TURN {
MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_PLAY_ROUGH, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_PLAY_ROUGH, target:playerLeft, secondaryEffect:FALSE);
}
} SCENE {
// Turn 1
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentRight);
HP_BAR(playerLeft, captureDamage: &damage[2]);
// Turn 2
SWITCH_OUT_MESSAGE("Wobbuffet");
SEND_IN_MESSAGE("Xerneas");
// Turn 3
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[3]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[4]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentRight);
HP_BAR(playerLeft, captureDamage: &damage[5]);
} THEN {
EXPECT_EQ(damage[3], damage[0]);
EXPECT_EQ(damage[4], damage[1]);
EXPECT_EQ(damage[5], damage[2]);
}
}

View File

@ -9,7 +9,7 @@ SINGLE_BATTLE_TEST("Filter reduces damage to Super Effective moves by 0.75", s16
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_MR_MIME].types[0] == TYPE_PSYCHIC);
ASSUME(gSpeciesInfo[SPECIES_MR_MIME].types[1] == TYPE_FAIRY);
ASSUME(gMovesInfo[MOVE_POISON_JAB].type == TYPE_POISON);
ASSUME(GetMoveType(MOVE_POISON_JAB) == TYPE_POISON);
ASSUME(gTypeEffectivenessTable[TYPE_POISON][TYPE_FAIRY] > UQ_4_12(1.0));
ASSUME(gTypeEffectivenessTable[TYPE_POISON][TYPE_PSYCHIC] == UQ_4_12(1.0));
PLAYER(SPECIES_MR_MIME) { Ability(ability); }

View File

@ -1,4 +1,21 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Flare Boost (Ability) test titles")
SINGLE_BATTLE_TEST("Flare Boost increases Sp. Attack by 50% when the Pokémon is burned", s16 damage)
{
u32 status1;
PARAMETRIZE { status1 = STATUS1_NONE; }
PARAMETRIZE { status1 = STATUS1_BURN; }
GIVEN {
ASSUME(GetMoveCategory(MOVE_SWIFT) == DAMAGE_CATEGORY_SPECIAL);
PLAYER(SPECIES_DRIFBLIM) { Ability(ABILITY_FLARE_BOOST); Status1(status1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SWIFT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWIFT, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
}
}

View File

@ -1,4 +1,44 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Fur Coat (Ability) test titles")
ASSUMPTIONS
{
ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL);
}
SINGLE_BATTLE_TEST("Fur Coat doubles Defense", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_FUR_COAT; }
PARAMETRIZE { ability = ABILITY_RATTLED; }
GIVEN {
PLAYER(SPECIES_PERSIAN_ALOLA) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SCRATCH); }
} SCENE {
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Fur Coat has no effect on self-inflicted confusion damage", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_FUR_COAT; }
PARAMETRIZE { ability = ABILITY_RATTLED; }
GIVEN {
PLAYER(SPECIES_PERSIAN_ALOLA) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_CONFUSE_RAY); MOVE(player, MOVE_POUND, WITH_RNG(RNG_CONFUSION, TRUE)); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, opponent);
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_EQ(results[0].damage, results[1].damage);
}
}

View File

@ -1,4 +1,26 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Iron Fist (Ability) test titles")
SINGLE_BATTLE_TEST("Iron Fist increases the power of punching moves by 20%", s16 damage)
{
u32 move, ability;
PARAMETRIZE { move = MOVE_BULLET_PUNCH; ability = ABILITY_IRON_FIST; }
PARAMETRIZE { move = MOVE_BULLET_PUNCH; ability = ABILITY_BLAZE; }
PARAMETRIZE { move = MOVE_SCRATCH; ability = ABILITY_IRON_FIST; }
PARAMETRIZE { move = MOVE_SCRATCH; ability = ABILITY_BLAZE; }
GIVEN {
ASSUME(IsPunchingMove(MOVE_BULLET_PUNCH));
ASSUME(!IsPunchingMove(MOVE_SCRATCH));
ASSUME(GetMovePower(MOVE_BULLET_PUNCH) == GetMovePower(MOVE_SCRATCH));
PLAYER(SPECIES_CHIMCHAR) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[1].damage, Q_4_12(1.2), results[0].damage); // Iron Fist affects punching moves
EXPECT_EQ(results[2].damage, results[3].damage); // Iron Fist does not affect non-punching moves
}
}

View File

@ -31,7 +31,41 @@ SINGLE_BATTLE_TEST("Leaf Guard prevents non-volatile status conditions in sun")
}
}
TO_DO_BATTLE_TEST("Leaf Guard doesn't prevent non-volatile status conditions if Cloud Nine/Air Lock is on the field");
SINGLE_BATTLE_TEST("Leaf Guard doesn't prevent non-volatile status conditions if Cloud Nine/Air Lock is on the field")
{
u32 move, species, ability;
u16 status;
PARAMETRIZE { move = MOVE_WILL_O_WISP; status = STATUS1_BURN; species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { move = MOVE_HYPNOSIS; status = STATUS1_SLEEP; species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { move = MOVE_THUNDER_WAVE; status = STATUS1_PARALYSIS; species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { move = MOVE_TOXIC; status = STATUS1_TOXIC_POISON; species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { move = MOVE_WILL_O_WISP; status = STATUS1_BURN; species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
PARAMETRIZE { move = MOVE_HYPNOSIS; status = STATUS1_SLEEP; species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
PARAMETRIZE { move = MOVE_THUNDER_WAVE; status = STATUS1_PARALYSIS; species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
PARAMETRIZE { move = MOVE_TOXIC; status = STATUS1_TOXIC_POISON; species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
// PARAMETRIZE { move = MOVE_POWDER_SNOW; status = STATUS1_FREEZE; } // Pointless since you can't freeze in sunlight anyway
GIVEN {
ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_NON_VOLATILE_STATUS);
ASSUME(GetMoveNonVolatileStatus(MOVE_WILL_O_WISP) == MOVE_EFFECT_BURN);
ASSUME(GetMoveEffect(MOVE_HYPNOSIS) == EFFECT_NON_VOLATILE_STATUS);
ASSUME(GetMoveNonVolatileStatus(MOVE_HYPNOSIS) == MOVE_EFFECT_SLEEP);
ASSUME(GetMoveEffect(MOVE_THUNDER_WAVE) == EFFECT_NON_VOLATILE_STATUS);
ASSUME(GetMoveNonVolatileStatus(MOVE_THUNDER_WAVE) == MOVE_EFFECT_PARALYSIS);
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_NON_VOLATILE_STATUS);
ASSUME(GetMoveNonVolatileStatus(MOVE_TOXIC) == MOVE_EFFECT_TOXIC);
PLAYER(SPECIES_LEAFEON) { Ability(ABILITY_LEAF_GUARD); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
ABILITY_POPUP(player, ABILITY_LEAF_GUARD);
MESSAGE("It doesn't affect Leafeon…");
}
STATUS_ICON(player, status);
}
}
SINGLE_BATTLE_TEST("Leaf Guard prevents status conditions from Flame Orb and Toxic Orb")
{
@ -50,29 +84,82 @@ SINGLE_BATTLE_TEST("Leaf Guard prevents status conditions from Flame Orb and Tox
NONE_OF { MESSAGE("Leafeon was burned!"); STATUS_ICON(player, burn: TRUE); }
}
else {
NONE_OF { MESSAGE("Leafeon was badly poisoned!"); STATUS_ICON(player, poison: TRUE); }
NONE_OF { MESSAGE("Leafeon was badly poisoned!"); STATUS_ICON(player, badPoison: TRUE); }
}
}
}
TO_DO_BATTLE_TEST("Leaf Guard doesn't prevent status conditions from Flame Orb and Toxic Orb if Cloud Nine/Air Lock is on the field");
SINGLE_BATTLE_TEST("Leaf Guard prevents Rest during sun")
SINGLE_BATTLE_TEST("Leaf Guard doesn't prevent status conditions from Flame Orb and Toxic Orb if Cloud Nine/Air Lock is on the field")
{
u32 item, species, ability;
PARAMETRIZE { item = ITEM_FLAME_ORB; species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { item = ITEM_TOXIC_ORB; species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { item = ITEM_FLAME_ORB; species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
PARAMETRIZE { item = ITEM_TOXIC_ORB; species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
GIVEN {
ASSUME(B_LEAF_GUARD_PREVENTS_REST >= GEN_5);
ASSUME(gItemsInfo[ITEM_FLAME_ORB].holdEffect == HOLD_EFFECT_FLAME_ORB);
ASSUME(gItemsInfo[ITEM_TOXIC_ORB].holdEffect == HOLD_EFFECT_TOXIC_ORB);
PLAYER(SPECIES_LEAFEON) { Ability(ABILITY_LEAF_GUARD); Item(item); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_SUNNY_DAY); }
} SCENE {
if (item == ITEM_FLAME_ORB) {
MESSAGE("Leafeon was burned!");
STATUS_ICON(player, burn: TRUE);
}
else {
MESSAGE("Leafeon was badly poisoned!");
STATUS_ICON(player, badPoison: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Leaf Guard prevents Rest during sun (Gen 5+)")
{
u32 gen;
PARAMETRIZE { gen = GEN_4; }
PARAMETRIZE { gen = GEN_5; }
GIVEN {
WITH_CONFIG(GEN_CONFIG_LEAF_GUARD_PREVENTS_REST, gen);
ASSUME(GetMoveEffect(MOVE_REST) == EFFECT_REST);
PLAYER(SPECIES_LEAFEON) { Ability(ABILITY_LEAF_GUARD); HP(100); MaxHP(200); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SUNNY_DAY); MOVE(player, MOVE_REST); }
} SCENE {
MESSAGE("But it failed!");
if (gen >= GEN_5) {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
STATUS_ICON(player, sleep: TRUE);
HP_BAR(player);
}
}
else {
STATUS_ICON(player, sleep: TRUE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
HP_BAR(player);
}
}
}
TO_DO_BATTLE_TEST("Leaf Guard doesn't prevent Rest if Cloud Nine/Air Lock is on the field");
SINGLE_BATTLE_TEST("Leaf Guard doesn't prevent Rest if Cloud Nine/Air Lock is on the field")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
PARAMETRIZE { species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
GIVEN {
WITH_CONFIG(GEN_CONFIG_LEAF_GUARD_PREVENTS_REST, GEN_5);
ASSUME(GetMoveEffect(MOVE_REST) == EFFECT_REST);
PLAYER(SPECIES_LEAFEON) { Ability(ABILITY_LEAF_GUARD); HP(100); MaxHP(200); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(opponent, MOVE_SUNNY_DAY); MOVE(player, MOVE_REST); }
} SCENE {
STATUS_ICON(player, sleep: TRUE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
HP_BAR(player);
}
}

View File

@ -1,4 +1,4 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Libero (Ability) test titles")
// Tests for Libero are handled in test/battle/ability/protean.c

View File

@ -42,6 +42,7 @@ SINGLE_BATTLE_TEST("Magic Bounce bounces back powder moves")
SINGLE_BATTLE_TEST("Magic Bounce cannot bounce back powder moves against Grass Types")
{
GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
ASSUME(IsPowderMove(MOVE_STUN_SPORE));
ASSUME(GetSpeciesType(SPECIES_ODDISH, 0) == TYPE_GRASS);
PLAYER(SPECIES_ODDISH);

View File

@ -1,4 +1,22 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Mold Breaker (Ability) test titles")
SINGLE_BATTLE_TEST("Mold Breaker cancels damage reduction from Ice Scales", s16 damage)
{
u16 ability;
PARAMETRIZE { ability = ABILITY_SHADOW_TAG; }
PARAMETRIZE { ability = ABILITY_MOLD_BREAKER; }
GIVEN {
ASSUME(GetMoveCategory(MOVE_PSYCHIC) == DAMAGE_CATEGORY_SPECIAL);
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
OPPONENT(SPECIES_FROSMOTH) { Ability(ABILITY_ICE_SCALES); }
} WHEN {
TURN { MOVE(player, MOVE_PSYCHIC); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[1].damage, UQ_4_12(0.5), results[0].damage);
}
}
TO_DO_BATTLE_TEST("TODO: Write more Mold Breaker (Ability) test titles")

View File

@ -7,7 +7,7 @@ SINGLE_BATTLE_TEST("Overcoat blocks powder and spore moves (Gen6+)")
PARAMETRIZE { gen = GEN_5; }
PARAMETRIZE { gen = GEN_6; }
GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, gen);
WITH_CONFIG(GEN_CONFIG_POWDER_OVERCOAT, gen);
ASSUME(IsPowderMove(MOVE_STUN_SPORE));
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_PINECO) { Ability(ABILITY_OVERCOAT); }
@ -71,17 +71,29 @@ DOUBLE_BATTLE_TEST("Overcoat blocks damage from hail")
}
}
SINGLE_BATTLE_TEST("Overcoat blocks Effect Spore's effect")
SINGLE_BATTLE_TEST("Overcoat blocks Effect Spore's effect (Gen6+)")
{
u32 config;
PARAMETRIZE { config = GEN_5; }
PARAMETRIZE { config = GEN_6; }
GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_OVERCOAT, config);
PLAYER(SPECIES_PINECO) {Ability(ABILITY_OVERCOAT);}
OPPONENT(SPECIES_SHROOMISH) {Ability(ABILITY_EFFECT_SPORE);}
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, WITH_RNG(RNG_EFFECT_SPORE, 1)); }
} SCENE {
MESSAGE("Pineco used Tackle!");
if (config == GEN_6) {
NOT ABILITY_POPUP(opponent, ABILITY_EFFECT_SPORE);
}
else {
ABILITY_POPUP(opponent, ABILITY_EFFECT_SPORE);
}
} THEN {
if (config == GEN_6)
EXPECT_EQ(player->status1, 0);
else
EXPECT_NE(player->status1, 0);
}
}

View File

@ -352,6 +352,22 @@ SINGLE_BATTLE_TEST("Parental Bond does not trigger on two turn attacks")
}
}
SINGLE_BATTLE_TEST("Parental Bond does not trigger Scale Shot effect on Drain Punch")
{
GIVEN {
PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_DRAIN_PUNCH, gimmick: GIMMICK_MEGA); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAIN_PUNCH, player);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
}
}
TO_DO_BATTLE_TEST("Parental Bond tests");
// Temporary TODO: Convert Bulbapedia description into tests.

View File

@ -9,7 +9,7 @@ SINGLE_BATTLE_TEST("Prism Armor reduces damage to Super Effective moves by 0.75"
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_NECROZMA].types[0] == TYPE_PSYCHIC);
ASSUME(gSpeciesInfo[SPECIES_NECROZMA].types[1] == TYPE_PSYCHIC);
ASSUME(gMovesInfo[MOVE_DARK_PULSE].type == TYPE_DARK);
ASSUME(GetMoveType(MOVE_DARK_PULSE) == TYPE_DARK);
ASSUME(gTypeEffectivenessTable[TYPE_POISON][TYPE_FAIRY] > UQ_4_12(1.0));
ASSUME(gTypeEffectivenessTable[TYPE_POISON][TYPE_PSYCHIC] == UQ_4_12(1.0));
PLAYER(SPECIES_NECROZMA);

View File

@ -1,12 +1,15 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Protean changes the type of the user to the move used every time (Gen6-8)")
SINGLE_BATTLE_TEST("Protean/Libero changes the type of the user to the move used every time (Gen6-8)")
{
u32 ability, species;
PARAMETRIZE { ability = ABILITY_PROTEAN; species = SPECIES_KECLEON; }
PARAMETRIZE { ability = ABILITY_LIBERO; species = SPECIES_RABOOT; }
GIVEN {
WITH_CONFIG(GEN_PROTEAN_LIBERO, GEN_6);
PLAYER(SPECIES_REGIROCK);
OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); }
OPPONENT(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_WATER_GUN); }
@ -15,24 +18,36 @@ SINGLE_BATTLE_TEST("Protean changes the type of the user to the move used every
TURN { SWITCH(opponent, 0); }
TURN { MOVE(opponent, MOVE_WATER_GUN); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
ABILITY_POPUP(opponent, ability);
if (species == SPECIES_KECLEON)
MESSAGE("The opposing Kecleon transformed into the Water type!");
else
MESSAGE("The opposing Raboot transformed into the Water type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
ABILITY_POPUP(opponent, ability);
if (species == SPECIES_KECLEON)
MESSAGE("The opposing Kecleon transformed into the Normal type!");
else
MESSAGE("The opposing Raboot transformed into the Normal type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
ABILITY_POPUP(opponent, ability);
if (species == SPECIES_KECLEON)
MESSAGE("The opposing Kecleon transformed into the Water type!");
else
MESSAGE("The opposing Raboot transformed into the Water type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
}
}
SINGLE_BATTLE_TEST("Protean changes the type of the user only once per switch in (Gen9+)")
SINGLE_BATTLE_TEST("Protean/Libero changes the type of the user only once per switch in (Gen9+)")
{
u32 ability, species;
PARAMETRIZE { ability = ABILITY_PROTEAN; species = SPECIES_KECLEON; }
PARAMETRIZE { ability = ABILITY_LIBERO; species = SPECIES_RABOOT; }
GIVEN {
WITH_CONFIG(GEN_PROTEAN_LIBERO, GEN_9);
PLAYER(SPECIES_REGIROCK);
OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); }
OPPONENT(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_WATER_GUN); }
@ -41,31 +56,42 @@ SINGLE_BATTLE_TEST("Protean changes the type of the user only once per switch in
TURN { SWITCH(opponent, 0); }
TURN { MOVE(opponent, MOVE_WATER_GUN); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
ABILITY_POPUP(opponent, ability);
if (species == SPECIES_KECLEON)
MESSAGE("The opposing Kecleon transformed into the Water type!");
else
MESSAGE("The opposing Raboot transformed into the Water type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
ABILITY_POPUP(opponent, ability);
MESSAGE("The opposing Kecleon transformed into the Normal type!");
MESSAGE("The opposing Raboot transformed into the Normal type!");
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
ABILITY_POPUP(opponent, ability);
if (species == SPECIES_KECLEON)
MESSAGE("The opposing Kecleon transformed into the Water type!");
else
MESSAGE("The opposing Raboot transformed into the Water type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
}
}
SINGLE_BATTLE_TEST("Protean does not change the user's type when using Struggle")
SINGLE_BATTLE_TEST("Protean/Libero does not change the user's type when using Struggle")
{
u32 ability, species;
PARAMETRIZE { ability = ABILITY_PROTEAN; species = SPECIES_GRENINJA; }
PARAMETRIZE { ability = ABILITY_LIBERO; species = SPECIES_RABOOT; }
GIVEN {
PLAYER(SPECIES_REGIROCK);
OPPONENT(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(opponent, MOVE_STRUGGLE); }
} SCENE {
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
ABILITY_POPUP(opponent, ability);
MESSAGE("The opposing Greninja transformed into the Normal type!");
MESSAGE("The opposing Raboot transformed into the Normal type!");
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, opponent);
}

View File

@ -1,7 +1,7 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Sharpness increases the power of slicing moves", s16 damage)
SINGLE_BATTLE_TEST("Sharpness increases the power of slicing moves by 50%", s16 damage)
{
u32 move;
u16 ability;

View File

@ -9,7 +9,7 @@ SINGLE_BATTLE_TEST("Solid Rock reduces damage to Super Effective moves by 0.75",
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_CARRACOSTA].types[0] == TYPE_WATER);
ASSUME(gSpeciesInfo[SPECIES_CARRACOSTA].types[1] == TYPE_ROCK);
ASSUME(gMovesInfo[MOVE_CLOSE_COMBAT].type == TYPE_FIGHTING);
ASSUME(GetMoveType(MOVE_CLOSE_COMBAT) == TYPE_FIGHTING);
ASSUME(gTypeEffectivenessTable[TYPE_FIGHTING][TYPE_ROCK] > UQ_4_12(1.0));
ASSUME(gTypeEffectivenessTable[TYPE_FIGHTING][TYPE_WATER] == UQ_4_12(1.0));
PLAYER(SPECIES_CARRACOSTA) { Ability(ability); }

View File

@ -1,4 +1,20 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Soundproof (Ability) test titles")
SINGLE_BATTLE_TEST("Soundproof makes sound moves fail against the ability user")
{
GIVEN {
ASSUME(IsSoundMove(MOVE_BOOMBURST));
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EXPLOUD) { Ability(ABILITY_SOUNDPROOF); }
} WHEN {
TURN { MOVE(player, MOVE_BOOMBURST); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_SOUNDPROOF);
MESSAGE("The opposing Exploud's Soundproof blocks Boomburst!");
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BOOMBURST, player);
HP_BAR(opponent);
}
}
}

View File

@ -1,4 +1,23 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Toxic Boost (Ability) test titles")
SINGLE_BATTLE_TEST("Toxic Boost increases Attack by 50% when the Pokémon is poisoned", s16 damage)
{
u32 status1;
PARAMETRIZE { status1 = STATUS1_NONE; }
PARAMETRIZE { status1 = STATUS1_POISON; }
PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; }
GIVEN {
ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL);
PLAYER(SPECIES_ZANGOOSE) { Ability(ABILITY_TOXIC_BOOST); Status1(status1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[2].damage);
}
}

View File

@ -690,7 +690,7 @@ AI_SINGLE_BATTLE_TEST("AI won't use thawing moves if target is frozen unless it
ASSUME(GetMoveType(MOVE_EMBER) == TYPE_FIRE);
ASSUME(GetMoveCategory(MOVE_TACKLE) == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(GetMoveCategory(MOVE_WATER_GUN) == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_SCALD].thawsUser == TRUE);
ASSUME(MoveThawsUser(MOVE_SCALD) == TRUE);
AI_FLAGS(aiFlags | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_TACKLE); Status1(status); }
OPPONENT(SPECIES_VULPIX) { Moves(MOVE_TACKLE, aiMove); }

Some files were not shown because too many files have changed in this diff Show More