diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml
index 55d4fada4..e838ba1f8 100644
--- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml
+++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml
@@ -1,50 +1,52 @@
-name: ⚔️ Battle Engine mechanical bugs 🐛
-description: File a bug report related to battle mechanic, be it moves, abilities and/or items.
-labels: ["bug", "status: unconfirmed", "category: battle-mechanic"]
-body:
- - type: markdown
- attributes:
- value: |
- Please fill in all required fields with as many details as possible.
- - type: textarea
- id: description
- attributes:
- label: Description
- description: |
- Describe the issue you are experiencing.
- Attach images/videos if possible.
- placeholder: |
- Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video
- validations:
- required: true
- - type: dropdown
- id: version
- attributes:
- label: Version
- description: What version of pokeemerald-expansion are you using as a base?
- options:
- - 1.5.1 (Default)
- - upcoming (Edge)
- - 1.5.0
- - 1.4.3
- - 1.4.2
- - 1.4.1
- - 1.4.0
- - pre-1.4.0
- validations:
- required: true
- - type: input
- id: upcomingversion
- attributes:
- label: Upcoming Version
- description: If you're using the upcoming branch, please specify what was the commit hash you pulled from.
- validations:
- required: false
- - type: input
- id: contact
- attributes:
- label: Discord contact info
- description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)).
- placeholder: ex. Lunos#4026
- validations:
- required: false
+name: ⚔️ Battle Engine mechanical bugs 🐛
+description: File a bug report related to battle mechanic, be it moves, abilities and/or items.
+labels: ["bug", "status: unconfirmed", "category: battle-mechanic"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Please fill in all required fields with as many details as possible.
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: |
+ Describe the issue you are experiencing.
+ Attach images/videos if possible.
+ placeholder: |
+ Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video
+ validations:
+ required: true
+ - type: dropdown
+ id: version
+ attributes:
+ label: Version
+ description: What version of pokeemerald-expansion are you using as a base?
+ options:
+ - 1.5.3 (Default)
+ - upcoming (Edge)
+ - 1.5.2
+ - 1.5.1
+ - 1.5.0
+ - 1.4.3
+ - 1.4.2
+ - 1.4.1
+ - 1.4.0
+ - pre-1.4.0
+ validations:
+ required: true
+ - type: input
+ id: upcomingversion
+ attributes:
+ label: Upcoming Version
+ description: If you're using the upcoming branch, please specify what was the commit hash you pulled from.
+ validations:
+ required: false
+ - type: input
+ id: contact
+ attributes:
+ label: Discord contact info
+ description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)).
+ placeholder: ex. Lunos#4026
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml
index 8c28b3942..d0653094c 100644
--- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml
+++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml
@@ -1,50 +1,52 @@
-name: 🧠 Battle AI bugs 🐛
-description: File a bug report related to battle AI.
-labels: ["bug", "status: unconfirmed", "category: battle-ai"]
-body:
- - type: markdown
- attributes:
- value: |
- Please fill in all required fields with as many details as possible.
- - type: textarea
- id: description
- attributes:
- label: Description
- description: |
- Describe the issue you are experiencing.
- Attach images/videos if possible.
- placeholder: |
- Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video
- validations:
- required: true
- - type: dropdown
- id: version
- attributes:
- label: Version
- description: What version of pokeemerald-expansion are you using as a base?
- options:
- - 1.5.1 (Default)
- - upcoming (Edge)
- - 1.5.0
- - 1.4.3
- - 1.4.2
- - 1.4.1
- - 1.4.0
- - pre-1.4.0
- validations:
- required: true
- - type: input
- id: upcomingversion
- attributes:
- label: Upcoming Version
- description: If you're using the upcoming branch, please specify what was the commit hash you pulled from.
- validations:
- required: false
- - type: input
- id: contact
- attributes:
- label: Discord contact info
- description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)).
- placeholder: ex. Lunos#4026
- validations:
- required: false
+name: 🧠 Battle AI bugs 🐛
+description: File a bug report related to battle AI.
+labels: ["bug", "status: unconfirmed", "category: battle-ai"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Please fill in all required fields with as many details as possible.
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: |
+ Describe the issue you are experiencing.
+ Attach images/videos if possible.
+ placeholder: |
+ Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video
+ validations:
+ required: true
+ - type: dropdown
+ id: version
+ attributes:
+ label: Version
+ description: What version of pokeemerald-expansion are you using as a base?
+ options:
+ - 1.5.3 (Default)
+ - upcoming (Edge)
+ - 1.5.2
+ - 1.5.1
+ - 1.5.0
+ - 1.4.3
+ - 1.4.2
+ - 1.4.1
+ - 1.4.0
+ - pre-1.4.0
+ validations:
+ required: true
+ - type: input
+ id: upcomingversion
+ attributes:
+ label: Upcoming Version
+ description: If you're using the upcoming branch, please specify what was the commit hash you pulled from.
+ validations:
+ required: false
+ - type: input
+ id: contact
+ attributes:
+ label: Discord contact info
+ description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)).
+ placeholder: ex. Lunos#4026
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/03_feature_requests.yaml b/.github/ISSUE_TEMPLATE/03_feature_requests.yaml
index 79e85709f..682f0dc09 100644
--- a/.github/ISSUE_TEMPLATE/03_feature_requests.yaml
+++ b/.github/ISSUE_TEMPLATE/03_feature_requests.yaml
@@ -1,27 +1,27 @@
-name: 🙏 Feature Request 🙏
-description: Do you want a feature to be added to the Expansion? Let us know!
-labels: ["feature-request"]
-body:
- - type: markdown
- attributes:
- value: |
- Please fill in all required fields with as many details as possible.
- - type: textarea
- id: description
- attributes:
- label: Description
- description: |
- Describe the issue you are experiencing.
- Attach images/videos if possible.
- placeholder: |
- Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video
- validations:
- required: true
- - type: input
- id: contact
- attributes:
- label: Discord contact info
- description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)).
- placeholder: ex. Lunos#4026
- validations:
- required: false
+name: 🙏 Feature Request 🙏
+description: Do you want a feature to be added to the Expansion? Let us know!
+labels: ["feature-request"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Please fill in all required fields with as many details as possible.
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: |
+ Describe the issue you are experiencing.
+ Attach images/videos if possible.
+ placeholder: |
+ Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video
+ validations:
+ required: true
+ - type: input
+ id: contact
+ attributes:
+ label: Discord contact info
+ description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)).
+ placeholder: ex. Lunos#4026
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml
index 6f11d5b9a..51998d730 100644
--- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml
+++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml
@@ -1,50 +1,52 @@
-name: 💾 Other errors 🖥️
-description: Everything else that doesn't fit in the above categories.
-labels: ["bug", "status: unconfirmed"]
-body:
- - type: markdown
- attributes:
- value: |
- Please fill in all required fields with as many details as possible.
- - type: textarea
- id: description
- attributes:
- label: Description
- description: |
- Describe the issue you are experiencing.
- Attach images/videos if possible.
- placeholder: |
- Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video
- validations:
- required: true
- - type: dropdown
- id: version
- attributes:
- label: Version
- description: What version of pokeemerald-expansion are you using as a base?
- options:
- - 1.5.1 (Default)
- - upcoming (Edge)
- - 1.5.0
- - 1.4.3
- - 1.4.2
- - 1.4.1
- - 1.4.0
- - pre-1.4.0
- validations:
- required: true
- - type: input
- id: upcomingversion
- attributes:
- label: Upcoming Version
- description: If you're using the upcoming branch, please specify what was the commit hash you pulled from.
- validations:
- required: false
- - type: input
- id: contact
- attributes:
- label: Discord contact info
- description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)).
- placeholder: ex. Lunos#4026
- validations:
- required: false
+name: 💾 Other errors 🖥️
+description: Everything else that doesn't fit in the above categories.
+labels: ["bug", "status: unconfirmed"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Please fill in all required fields with as many details as possible.
+ - type: textarea
+ id: description
+ attributes:
+ label: Description
+ description: |
+ Describe the issue you are experiencing.
+ Attach images/videos if possible.
+ placeholder: |
+ Please enter a description of the issue. Here you can also attach log screenshots, gifs or a video
+ validations:
+ required: true
+ - type: dropdown
+ id: version
+ attributes:
+ label: Version
+ description: What version of pokeemerald-expansion are you using as a base?
+ options:
+ - 1.5.3 (Default)
+ - upcoming (Edge)
+ - 1.5.2
+ - 1.5.1
+ - 1.5.0
+ - 1.4.3
+ - 1.4.2
+ - 1.4.1
+ - 1.4.0
+ - pre-1.4.0
+ validations:
+ required: true
+ - type: input
+ id: upcomingversion
+ attributes:
+ label: Upcoming Version
+ description: If you're using the upcoming branch, please specify what was the commit hash you pulled from.
+ validations:
+ required: false
+ - type: input
+ id: contact
+ attributes:
+ label: Discord contact info
+ description: Provide your Discord tag here so we can contact you in case we need more details. Be sure to join our server ([here](https://discord.gg/6CzjAG6GZk)).
+ placeholder: ex. Lunos#4026
+ validations:
+ required: false
diff --git a/INSTALL.md b/INSTALL.md
index 53d284107..e74706d6a 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -125,19 +125,53 @@ Otherwise, ask for help on Discord or IRC (see [README.md](README.md)), or conti
Note that in msys2, Copy is Ctrl+Insert and Paste is Shift+Insert.
-1. Open msys2 at C:\devkitPro\msys2\mingw64.exe or run `C:\devkitPro\msys2\msys2_shell.bat -mingw64`.
+1. Open msys2 at C:\devkitPro\msys2\msys2_shell.bat.
-2. Certain packages are required to build pokeemerald. Install these by running the following command:
+2. Certain packages are required to build pokeemerald. Install these by running the following two commands:
```bash
- pacman -S make zlib-devel git mingw-w64-x86_64-gcc mingw-w64-x86_64-libpng
+ pacman -Sy msys2-keyring
+ pacman -S make gcc zlib-devel git
```
Note...
- > This command will ask for confirmation, just enter the yes action when prompted.
+ > The commands will ask for confirmation, just enter the yes action when prompted.
+3. Download [libpng](https://sourceforge.net/projects/libpng/files/libpng16/1.6.37/libpng-1.6.37.tar.xz/download).
+
+4. Change directory to where libpng was downloaded. By default, msys2 will start in the current user's profile folder, located at **C:\Users\\_\_**, where *\* is your Windows username. In most cases, libpng should be saved within a subfolder of the profile folder. For example, if libpng was saved to **C:\Users\\_\_\Downloads** (the Downloads location for most users), enter this command:
+
+ ```bash
+ cd Downloads
+ ```
+
+
+ Notes...
+
+ > Note 1: While not shown, msys uses forward slashes `/` instead of backwards slashes `\` as the directory separator.
+ > Note 2: If the path has spaces, then the path must be wrapped with quotations, e.g. `cd "Downloads/My Downloads"`.
+ > Note 3: Windows path names are case-insensitive so adhering to capitalization isn’t needed.
+ > Note 4: If libpng was saved elsewhere, you will need to specify the full path to where libpng was downloaded, e.g. `cd c:/devkitpro/msys2` if it was saved there.
+
+
+5. Run the following commands to uncompress and install libpng.
+
+ ```bash
+ tar xf libpng-1.6.37.tar.xz
+ cd libpng-1.6.37
+ ./configure --prefix=/usr
+ make check
+ make install
+ ```
+
+6. Then finally, run the following command to change back to the user profile folder.
+
+ ```bash
+ cd
+ ```
+
### Choosing where to store pokeemerald (msys2)
At this point, you can choose a folder to store pokeemerald into. If you're okay with storing pokeemerald in the user profile folder, then proceed to [Installation](#installation). Otherwise, you'll need to account for where pokeemerald is stored when changing directory to the pokeemerald folder.
diff --git a/Makefile b/Makefile
index c4964e42e..79ed8f160 100644
--- a/Makefile
+++ b/Makefile
@@ -450,7 +450,7 @@ $(OBJ_DIR)/sym_ewram.ld: sym_ewram.txt
# NOTE: Based on C_DEP above, but without NODEP and KEEP_TEMPS handling.
define TEST_DEP
-$1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib -I test $2)
+$1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib $2)
@echo "$$(CC1) -o $$@ $$<"
@$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) $$< charmap.txt -i | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ -
endef
diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc
index 014a8bb7f..93c747810 100644
--- a/asm/macros/battle_script.inc
+++ b/asm/macros/battle_script.inc
@@ -1053,8 +1053,9 @@
.byte 0xca
.endm
- .macro setcharge
+ .macro setcharge battler:req
.byte 0xcb
+ .byte \battler
.endm
.macro callterrainattack
@@ -1317,7 +1318,7 @@
.2byte \holdEffect
.4byte \jumpInstr
.endm
-
+
.macro dostockpilestatchangeswearoff, battler:req, statChangeInstr:req
callnative BS_DoStockpileStatChangesWearOff
.byte \battler
@@ -1353,7 +1354,7 @@
.macro setsnow
callnative BS_SetSnow
.endm
-
+
.macro setzeffect
callnative BS_SetZEffect
.endm
@@ -1363,12 +1364,6 @@
callnative BS_TrySymbiosis
.endm
- @ returns TRUE or FALSE to gBattleCommunication[0]
- .macro canteleport battler:req
- callnative BS_CanTeleport
- .byte \battler
- .endm
-
@ returns B_SIDE_x to gBattleCommunication[0]
.macro getbattlerside battler:req
callnative BS_GetBattlerSide
@@ -2076,7 +2071,7 @@
.macro swapsidestatuses
various BS_ATTACKER, VARIOUS_SWAP_SIDE_STATUSES
.endm
-
+
.macro swapstats stat:req
various BS_ATTACKER, VARIOUS_SWAP_STATS
.byte \stat
@@ -2177,6 +2172,11 @@
jumpifbyte CMP_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_NO_EFFECT, \jumpInstr
.endm
+ .macro jumpifside battler:req, side:req, equalJumpInstr:req
+ getbattlerside \battler
+ jumpifbyte CMP_EQUAL, gBattleCommunication, \side, \equalJumpInstr
+ .endm
+
.macro jumpifbattletype flags:req, jumpInstr:req
jumpifword CMP_COMMON_BITS, gBattleTypeFlags, \flags, \jumpInstr
.endm
diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s
index 39712db73..55e4cc351 100644
--- a/data/battle_scripts_1.s
+++ b/data/battle_scripts_1.s
@@ -3079,12 +3079,9 @@ BattleScript_TryTailwindAbilitiesLoop_WindRider:
BattleScript_TryTailwindAbilitiesLoop_WindPower:
call BattleScript_AbilityPopUp
- copybyte sSAVED_BATTLER, gBattlerAttacker
- copybyte gBattlerAttacker, gBattlerTarget
- setcharge
+ setcharge BS_TARGET
printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER
waitmessage B_WAIT_TIME_LONG
- copybyte gBattlerAttacker, sSAVED_BATTLER
goto BattleScript_TryTailwindAbilitiesLoop_Increment
BattleScript_EffectMircleEye:
@@ -4104,6 +4101,8 @@ BattleScript_MoveMissedDoDamage::
.if B_CRASH_IF_TARGET_IMMUNE < GEN_4
jumpifhalfword CMP_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE, BattleScript_MoveEnd
.endif
+ moveendcase MOVEEND_PROTECT_LIKE_EFFECT @ Spiky Shield's damage happens before recoil.
+ jumpifhasnohp BS_ATTACKER, BattleScript_MoveEnd
printstring STRINGID_PKMNCRASHED
waitmessage B_WAIT_TIME_LONG
damagecalc
@@ -5129,7 +5128,7 @@ BattleScript_EffectBatonPass::
goto BattleScript_MoveEnd
BattleScript_EffectRapidSpin::
-.if B_SPEED_BUFFING_RAPID_SPIN == GEN_8
+.if B_SPEED_BUFFING_RAPID_SPIN >= GEN_8
attackcanceler
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
attackstring
@@ -5404,15 +5403,14 @@ BattleScript_EffectHurricane:
BattleScript_EffectTeleport:
attackcanceler
attackstring
- ppreduce
.if B_TELEPORT_BEHAVIOR >= GEN_7
- canteleport BS_ATTACKER
- jumpifbyte CMP_EQUAL, gBattleCommunication, TRUE, BattleScript_EffectTeleportNew
- goto BattleScript_ButItFailed
+ jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_EffectBatonPass
+ jumpifside BS_ATTACKER, B_SIDE_PLAYER, BattleScript_EffectBatonPass
.else
jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_ButItFailed
.endif
BattleScript_EffectTeleportTryToRunAway:
+ ppreduce
getifcantrunfrombattle BS_ATTACKER
jumpifbyte CMP_EQUAL, gBattleCommunication, BATTLE_RUN_FORBIDDEN, BattleScript_ButItFailed
jumpifbyte CMP_EQUAL, gBattleCommunication, BATTLE_RUN_FAILURE, BattleScript_PrintAbilityMadeIneffective
@@ -5423,29 +5421,6 @@ BattleScript_EffectTeleportTryToRunAway:
setoutcomeonteleport BS_ATTACKER
goto BattleScript_MoveEnd
-BattleScript_EffectTeleportNew:
- getbattlerside BS_ATTACKER
- jumpifbyte CMP_EQUAL, gBattleCommunication, B_SIDE_OPPONENT, BattleScript_EffectTeleportTryToRunAway
- attackanimation
- waitanimation
- openpartyscreen BS_ATTACKER, BattleScript_EffectTeleportNewEnd
- switchoutabilities BS_ATTACKER
- waitstate
- switchhandleorder BS_ATTACKER, 2
- returntoball BS_ATTACKER
- getswitchedmondata BS_ATTACKER
- switchindataupdate BS_ATTACKER
- hpthresholds BS_ATTACKER
- trytoclearprimalweather
- printstring STRINGID_EMPTYSTRING3
- waitmessage 1
- printstring STRINGID_SWITCHINMON
- switchinanim BS_ATTACKER, TRUE
- waitstate
- switchineffects BS_ATTACKER
-BattleScript_EffectTeleportNewEnd:
- goto BattleScript_MoveEnd
-
BattleScript_EffectBeatUp::
attackcanceler
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
@@ -5872,7 +5847,7 @@ BattleScript_EffectCharge::
attackcanceler
attackstring
ppreduce
- setcharge
+ setcharge BS_ATTACKER
attackanimation
waitanimation
.if B_CHARGE_SPDEF_RAISE >= GEN_5
@@ -6937,27 +6912,6 @@ BattleScript_TailwindEnds::
waitmessage B_WAIT_TIME_LONG
end2
-BattleScript_WindPowerActivatesEnd2::
- setbyte gBattlerAttacker, 0
-BattleScript_WindPowerLoop:
- printstring STRINGID_EMPTYSTRING3
- jumpifability BS_ATTACKER, ABILITY_WIND_POWER, BattleScript_WindPowerLoop_Cont
- goto BattleScript_WindPowerIncrement
-BattleScript_WindPowerLoop_Cont:
- jumpifstatus3 BS_ATTACKER, STATUS3_CHARGED_UP, BattleScript_WindPowerIncrement
- goto BattleScript_WindPower_Activate
-BattleScript_WindPower_Activate:
- call BattleScript_AbilityPopUp
- setcharge
- printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER
- waitmessage B_WAIT_TIME_LONG
-BattleScript_WindPowerIncrement:
- addbyte gBattlerAttacker, 1
- jumpifbytenotequal gBattlerAttacker, gBattlersCount, BattleScript_WindPowerLoop
-BattleScript_WindPowerEnd:
- destroyabilitypopup
- end2
-
BattleScript_TrickRoomEnds::
printstring STRINGID_TRICKROOMENDS
waitmessage B_WAIT_TIME_LONG
@@ -7419,11 +7373,8 @@ BattleScript_AngerShellRet:
return
BattleScript_WindPowerActivates::
-.if B_CHECK_IF_CHARGED_UP == TRUE
- jumpifstatus3 BS_ATTACKER, STATUS3_CHARGED_UP, BattleScript_WindPowerActivates_Ret
-.endif
call BattleScript_AbilityPopUp
- setcharge
+ setcharge BS_TARGET
printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER
waitmessage B_WAIT_TIME_LONG
BattleScript_WindPowerActivates_Ret:
@@ -7924,7 +7875,7 @@ BattleScript_WishMegaEvolution::
BattleScript_PrimalReversion::
call BattleScript_PrimalReversionRet
end2
-
+
BattleScript_PrimalReversionRestoreAttacker::
call BattleScript_PrimalReversionRet
copybyte gBattlerAttacker, sSAVED_BATTLER
@@ -8782,15 +8733,14 @@ BattleScript_DesolateLandActivates::
call BattleScript_ActivateWeatherAbilities
end3
-BattleScript_DesolateLandEvaporatesWaterTypeMoves::
- accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
+BattleScript_PrimalWeatherBlocksMove::
+ jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_ATTACKSTRING_PRINTED, BattleScript_MoveEnd @in case of multi-target moves, if move fails once, no point in printing the message twice
+ accuracycheck BattleScript_PrintMoveMissed, NO_ACC_CALC_CHECK_LOCK_ON
attackstring
pause B_WAIT_TIME_SHORT
ppreduce
- jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_STRING_PRINTED, BattleScript_MoveEnd
- printstring STRINGID_MOVEEVAPORATEDINTHEHARSHSUNLIGHT
+ printfromtable gPrimalWeatherBlocksStringIds
waitmessage B_WAIT_TIME_LONG
- orword gHitMarker, HITMARKER_STRING_PRINTED
goto BattleScript_MoveEnd
BattleScript_PrimordialSeaActivates::
@@ -8802,17 +8752,6 @@ BattleScript_PrimordialSeaActivates::
call BattleScript_ActivateWeatherAbilities
end3
-BattleScript_PrimordialSeaFizzlesOutFireTypeMoves::
- accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
- attackstring
- pause B_WAIT_TIME_SHORT
- ppreduce
- jumpifword CMP_COMMON_BITS, gHitMarker, HITMARKER_STRING_PRINTED, BattleScript_MoveEnd
- printstring STRINGID_MOVEFIZZLEDOUTINTHEHEAVYRAIN
- waitmessage B_WAIT_TIME_LONG
- orword gHitMarker, HITMARKER_STRING_PRINTED
- goto BattleScript_MoveEnd
-
BattleScript_DeltaStreamActivates::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
@@ -8983,7 +8922,7 @@ BattleScript_BadDreams_ShowPopUp:
goto BattleScript_BadDreams_DmgAfterPopUp
BattleScript_BadDreams_HidePopUp:
destroyabilitypopup
- tryfaintmon BS_TARGET
+ tryfaintmon BS_TARGET
goto BattleScript_BadDreamsIncrement
BattleScript_TookAttack::
@@ -10461,6 +10400,7 @@ BattleScript_SymbiosisActivates::
return
BattleScript_TargetAbilityStatRaiseRet::
+ copybyte sSAVED_BATTLER, gBattlerAttacker
copybyte gBattlerAbility, gEffectBattler
copybyte gBattlerAttacker, gBattlerTarget
call BattleScript_AbilityPopUp
@@ -10468,6 +10408,7 @@ BattleScript_TargetAbilityStatRaiseRet::
setgraphicalstatchangevalues
call BattleScript_StatUp
BattleScript_TargetAbilityStatRaiseRet_End:
+ copybyte gBattlerAttacker, sSAVED_BATTLER
return
BattleScript_PokemonCantUseTheMove::
diff --git a/include/battle.h b/include/battle.h
index b27062c12..e3f2e90d0 100644
--- a/include/battle.h
+++ b/include/battle.h
@@ -212,6 +212,7 @@ struct SideTimer
u8 toxicSpikesAmount;
u8 stealthRockAmount;
u8 stickyWebAmount;
+ u8 stickyWebBattlerId;
u8 stickyWebBattlerSide; // Used for Court Change
u8 auroraVeilTimer;
u8 auroraVeilBattlerId;
@@ -644,7 +645,6 @@ struct BattleStruct
u8 forcedSwitch:4; // For each battler
u8 switchInAbilityPostponed:4; // To not activate against an empty field, each bit for battler
u8 ballSpriteIds[2]; // item gfx, window gfx
- u8 stickyWebUser;
u8 appearedInBattle; // Bitfield to track which Pokemon appeared in battle. Used for Burmy's form change
u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle.
// When using a move which hits multiple opponents which is then bounced by a target, we need to make sure, the move hits both opponents, the one with bounce, and the one without.
diff --git a/include/battle_scripts.h b/include/battle_scripts.h
index b20ac35dd..95f068cf5 100644
--- a/include/battle_scripts.h
+++ b/include/battle_scripts.h
@@ -409,9 +409,8 @@ extern const u8 BattleScript_GulpMissileGorging[];
extern const u8 BattleScript_GulpMissileGulping[];
extern const u8 BattleScript_BattleBondActivatesOnMoveEndAttacker[];
extern const u8 BattleScript_DesolateLandActivates[];
-extern const u8 BattleScript_DesolateLandEvaporatesWaterTypeMoves[];
extern const u8 BattleScript_PrimordialSeaActivates[];
-extern const u8 BattleScript_PrimordialSeaFizzlesOutFireTypeMoves[];
+extern const u8 BattleScript_PrimalWeatherBlocksMove[];
extern const u8 BattleScript_DeltaStreamActivates[];
extern const u8 BattleScript_MysteriousAirCurrentBlowsOn[];
extern const u8 BattleScript_AttackWeakenedByStrongWinds[];
diff --git a/include/battle_util.h b/include/battle_util.h
index 15fbe2432..51375000c 100644
--- a/include/battle_util.h
+++ b/include/battle_util.h
@@ -136,8 +136,8 @@ u8 DoBattlerEndTurnEffects(void);
bool8 HandleWishPerishSongOnTurnEnd(void);
bool8 HandleFaintedMonActions(void);
void TryClearRageAndFuryCutter(void);
+u8 AtkCanceller_UnableToUseMove(u32 moveType);
void SetAtkCancellerForCalledMove(void);
-u8 AtkCanceller_UnableToUseMove(void);
u8 AtkCanceller_UnableToUseMove2(void);
bool8 HasNoMonsToSwitch(u8 battlerId, u8 r1, u8 r2);
bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility);
diff --git a/include/config/battle.h b/include/config/battle.h
index 20cb7eb81..10d2648a8 100644
--- a/include/config/battle.h
+++ b/include/config/battle.h
@@ -118,7 +118,6 @@
#define B_PLUS_MINUS_INTERACTION GEN_LATEST // In Gen5+, Plus and Minus can be activated with themselves and the opposite ability. Before, only the opposing ability could activate it.
#define B_WEATHER_FORMS GEN_LATEST // In Gen5+, Castform and Cherrim revert to their base form upon losing their respective ability. Cherrim needs Flower Gift to swap forms.
#define B_SYMBIOSIS_GEMS GEN_LATEST // In Gen7+, Symbiosis passes an item after a gem-boosted attack. Previously, items are passed before the gem-boosted attack hits, making the item effect apply.
-#define B_CHECK_IF_CHARGED_UP TRUE // If set to TRUE, certain abilities such as Electromorphosis WILL check if the STATUS3_CHARGED_UP status flag is applied.
#define B_ABSORBING_ABILITY_STRING GEN_LATEST // In Gen5+, the abilities that absorb moves of a certain type use a generic string for stat increases and decreases.
#define B_LEAF_GUARD_PREVENTS_REST GEN_LATEST // In Gen5+, Leaf Guard prevents the use of Rest in harsh sunlight.
#define B_SNOW_WARNING GEN_LATEST // In Gen9+, Snow Warning will summon snow instead of hail.
diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h
index 174c611e2..0597612d1 100644
--- a/include/constants/battle_string_ids.h
+++ b/include/constants/battle_string_ids.h
@@ -827,6 +827,10 @@
#define B_MSG_SOMEONES_BOX_FULL 2
#define B_MSG_LANETTES_BOX_FULL 3
+// gPrimalWeatherBlocksStringIds
+#define B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN 0
+#define B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN 1
+
// gInobedientStringIds
#define B_MSG_LOAFING 0
#define B_MSG_WONT_OBEY 1
diff --git a/include/decompress.h b/include/decompress.h
index ef2ec43b0..1ded221eb 100644
--- a/include/decompress.h
+++ b/include/decompress.h
@@ -3,7 +3,7 @@
#include "sprite.h"
-extern u8 gDecompressionBuffer[0x4000];
+extern u8 ALIGNED(4) gDecompressionBuffer[0x4000];
void LZDecompressWram(const u32 *src, void *dest);
void LZDecompressVram(const u32 *src, void *dest);
diff --git a/include/gba/m4a_internal.h b/include/gba/m4a_internal.h
index 40a25ba05..fc8205efd 100644
--- a/include/gba/m4a_internal.h
+++ b/include/gba/m4a_internal.h
@@ -213,7 +213,7 @@ struct SoundInfo
ExtVolPitFunc ExtVolPit;
u8 gap2[16];
struct SoundChannel chans[MAX_DIRECTSOUND_CHANNELS];
- s8 pcmBuffer[PCM_DMA_BUF_SIZE * 2];
+ s8 ALIGNED(4) pcmBuffer[PCM_DMA_BUF_SIZE * 2];
};
struct SongHeader
diff --git a/include/gba/macro.h b/include/gba/macro.h
index 3b35a1946..5239cd4c8 100644
--- a/include/gba/macro.h
+++ b/include/gba/macro.h
@@ -1,7 +1,7 @@
#ifndef GUARD_GBA_MACRO_H
#define GUARD_GBA_MACRO_H
-#define CPU_FILL(value, dest, size, bit) \
+#define CPU_FILL_UNCHECKED(value, dest, size, bit) \
{ \
vu##bit tmp = (vu##bit)(value); \
CpuSet((void *)&tmp, \
@@ -9,10 +9,33 @@
CPU_SET_##bit##BIT | CPU_SET_SRC_FIXED | ((size)/(bit/8) & 0x1FFFFF)); \
}
+#if MODERN
+#define CPU_FILL(value, dest, size, bit) \
+ do \
+ { \
+ _Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \
+ CPU_FILL_UNCHECKED(value, dest, size, bit); \
+ } while (0)
+#else
+#define CPU_FILL(value, dest, size, bit) CPU_FILL_UNCHECKED(value, dest, size, bit)
+#endif
+
#define CpuFill16(value, dest, size) CPU_FILL(value, dest, size, 16)
#define CpuFill32(value, dest, size) CPU_FILL(value, dest, size, 32)
-#define CPU_COPY(src, dest, size, bit) CpuSet(src, dest, CPU_SET_##bit##BIT | ((size)/(bit/8) & 0x1FFFFF))
+#define CPU_COPY_UNCHECKED(src, dest, size, bit) CpuSet(src, dest, CPU_SET_##bit##BIT | ((size)/(bit/8) & 0x1FFFFF))
+
+#if MODERN
+#define CPU_COPY(src, dest, size, bit) \
+ do \
+ { \
+ _Static_assert(_Alignof(src) >= (bit / 8), "source potentially unaligned"); \
+ _Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \
+ CPU_COPY_UNCHECKED(src, dest, size, bit); \
+ } while (0)
+#else
+#define CPU_COPY(src, dest, size, bit) CPU_COPY_UNCHECKED(src, dest, size, bit)
+#endif
#define CpuCopy16(src, dest, size) CPU_COPY(src, dest, size, 16)
#define CpuCopy32(src, dest, size) CPU_COPY(src, dest, size, 32)
@@ -31,7 +54,7 @@
#define CpuFastCopy(src, dest, size) CpuFastSet(src, dest, ((size)/(32/8) & 0x1FFFFF))
-#define DmaSet(dmaNum, src, dest, control) \
+#define DmaSetUnchecked(dmaNum, src, dest, control) \
{ \
vu32 *dmaRegs = (vu32 *)REG_ADDR_DMA##dmaNum; \
dmaRegs[0] = (vu32)(src); \
@@ -40,7 +63,21 @@
dmaRegs[2]; \
}
-#define DMA_FILL(dmaNum, value, dest, size, bit) \
+#if MODERN
+// NOTE: Assumes 16-bit DMAs.
+#define DmaSet(dmaNum, src, dest, control) \
+ do \
+ { \
+ _Static_assert(_Alignof(src) >= __builtin_choose_expr(__builtin_constant_p(control), ((control) & (DMA_32BIT << 16)) ? 4 : 2, 2), "source potentially unaligned"); \
+ _Static_assert(_Alignof(dest) >= __builtin_choose_expr(__builtin_constant_p(control), ((control) & (DMA_32BIT << 16)) ? 4 : 2, 2), "destination potentially unaligned"); \
+ DmaSetUnchecked(dmaNum, src, dest, control); \
+ } while (0)
+#else
+#define DmaSet(dmaNum, src, dest, control) \
+ DmaSetUnchecked(dmaNum, src, dest, control)
+#endif
+
+#define DMA_FILL_UNCHECKED(dmaNum, value, dest, size, bit) \
{ \
vu##bit tmp = (vu##bit)(value); \
DmaSet(dmaNum, \
@@ -50,6 +87,17 @@
| ((size)/(bit/8))); \
}
+#if MODERN
+#define DMA_FILL(dmaNum, value, dest, size, bit) \
+ do \
+ { \
+ _Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \
+ DMA_FILL_UNCHECKED(dmaNum, value, dest, size, bit); \
+ } while (0)
+#else
+#define DMA_FILL(dmaNum, value, dest, size, bit) DMA_FILL_UNCHECKED(dmaNum, value, dest, size, bit)
+#endif
+
#define DmaFill16(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 16)
#define DmaFill32(dmaNum, value, dest, size) DMA_FILL(dmaNum, value, dest, size, 32)
@@ -58,23 +106,46 @@
// unit size (2 or 4 bytes) and then combined with the DMA control flags using a
// bitwise OR operation.
-#define DMA_CLEAR(dmaNum, dest, size, bit) \
+#define DMA_CLEAR_UNCHECKED(dmaNum, dest, size, bit) \
{ \
vu##bit *_dest = (vu##bit *)(dest); \
u32 _size = size; \
DmaFill##bit(dmaNum, 0, _dest, _size); \
}
+#if MODERN
+#define DMA_CLEAR(dmaNum, dest, size, bit) \
+ do \
+ { \
+ _Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \
+ DMA_CLEAR_UNCHECKED(dmaNum, dest, size, bit); \
+ } while (0)
+#else
+#define DMA_CLEAR(dmaNum, dest, size, bit) DMA_CLEAR_UNCHECKED(dmaNum, dest, size, bit)
+#endif
+
#define DmaClear16(dmaNum, dest, size) DMA_CLEAR(dmaNum, dest, size, 16)
#define DmaClear32(dmaNum, dest, size) DMA_CLEAR(dmaNum, dest, size, 32)
-#define DMA_COPY(dmaNum, src, dest, size, bit) \
+#define DMA_COPY_UNCHECKED(dmaNum, src, dest, size, bit) \
DmaSet(dmaNum, \
src, \
dest, \
(DMA_ENABLE | DMA_START_NOW | DMA_##bit##BIT | DMA_SRC_INC | DMA_DEST_INC) << 16 \
| ((size)/(bit/8)))
+#if MODERN
+#define DMA_COPY(dmaNum, src, dest, size, bit) \
+ do \
+ { \
+ _Static_assert(_Alignof(src) >= (bit / 8), "source potentially unaligned"); \
+ _Static_assert(_Alignof(dest) >= (bit / 8), "destination potentially unaligned"); \
+ DMA_COPY_UNCHECKED(dmaNum, src, dest, size, bit); \
+ } while (0)
+#else
+#define DMA_COPY(dmaNum, src, dest, size, bit) DMA_COPY_UNCHECKED(dmaNum, src, dest, size, bit)
+#endif
+
#define DmaCopy16(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 16)
#define DmaCopy32(dmaNum, src, dest, size) DMA_COPY(dmaNum, src, dest, size, 32)
diff --git a/include/gba/syscall.h b/include/gba/syscall.h
index 56cd4ba58..c922084d5 100644
--- a/include/gba/syscall.h
+++ b/include/gba/syscall.h
@@ -27,10 +27,32 @@ u16 ArcTan2(s16 x, s16 y);
void CpuSet(const void *src, void *dest, u32 control);
+#if MODERN
+// NOTE: Assumes 16-bit CpuSets unless control is a constant and has
+// CPU_SET_32BIT set.
+#define CpuSet(src, dest, control) \
+ do \
+ { \
+ _Static_assert(_Alignof(src) >= __builtin_choose_expr(__builtin_constant_p(control), ((control) & CPU_SET_32BIT) ? 4 : 2, 2), "source potentially unaligned"); \
+ _Static_assert(_Alignof(dest) >= __builtin_choose_expr(__builtin_constant_p(control), ((control) & CPU_SET_32BIT) ? 4 : 2, 2), "destination potentially unaligned"); \
+ CpuSet(src, dest, control); \
+ } while (0)
+#endif
+
#define CPU_FAST_SET_SRC_FIXED 0x01000000
void CpuFastSet(const void *src, void *dest, u32 control);
+#if MODERN
+#define CpuFastSet(src, dest, control) \
+ do \
+ { \
+ _Static_assert(_Alignof(src) >= 4, "source potentially unaligned"); \
+ _Static_assert(_Alignof(dest) >= 4, "destination potentially unaligned"); \
+ CpuFastSet(src, dest, control); \
+ } while (0)
+#endif
+
void BgAffineSet(struct BgAffineSrcData *src, struct BgAffineDstData *dest, s32 count);
void ObjAffineSet(struct ObjAffineSrcData *src, void *dest, s32 count, s32 offset);
diff --git a/include/global.h b/include/global.h
index eca45bdb6..1638ec840 100644
--- a/include/global.h
+++ b/include/global.h
@@ -20,7 +20,6 @@
#define BLOCK_CROSS_JUMP asm("");
// to help in decompiling
-#define asm_comment(x) asm volatile("@ -- " x " -- ")
#define asm_unified(x) asm(".syntax unified\n" x "\n.syntax divided")
#define NAKED __attribute__((naked))
@@ -141,6 +140,8 @@
// Calls m0/m1/.../m8 depending on how many arguments are passed.
#define VARARG_8(m, ...) CAT(m, NARG_8(__VA_ARGS__))(__VA_ARGS__)
+
+// This returns the number of arguments passed to it (up to 8).
#define NARG_8(...) NARG_8_(_, ##__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define NARG_8_(_, a, b, c, d, e, f, g, h, N, ...) N
@@ -856,7 +857,7 @@ struct WaldaPhrase
struct TrainerNameRecord
{
u32 trainerId;
- u8 trainerName[PLAYER_NAME_LENGTH + 1];
+ u8 ALIGNED(2) trainerName[PLAYER_NAME_LENGTH + 1];
};
struct TrainerHillSave
diff --git a/include/graphics.h b/include/graphics.h
index ad23dc67c..472c314b2 100644
--- a/include/graphics.h
+++ b/include/graphics.h
@@ -8923,7 +8923,7 @@ extern const u32 gIntroGroudon_Gfx[];
extern const u32 gIntroGroudon_Tilemap[];
extern const u32 gIntroLegendBg_Gfx[];
extern const u32 gIntroGroudonBg_Tilemap[];
-extern const u8 gIntro3Bg_Pal[0x200];
+extern const u8 ALIGNED(2) gIntro3Bg_Pal[0x200];
extern const u32 gIntroKyogre_Gfx[];
extern const u32 gIntroKyogre_Tilemap[];
extern const u32 gIntroKyogreBg_Tilemap[];
@@ -10432,8 +10432,8 @@ extern const u32 gPokenavOptions_Gfx[];
extern const u16 gPokenavOptions_Pal[];
// Battle Factory Screen
-extern const u8 gFrontierFactorySelectMenu_Gfx[];
-extern const u8 gFrontierFactorySelectMenu_Tilemap[];
+extern const u16 gFrontierFactorySelectMenu_Gfx[];
+extern const u16 gFrontierFactorySelectMenu_Tilemap[];
extern const u16 gFrontierFactorySelectMenu_Pal[];
// Object event pals
diff --git a/include/item_menu.h b/include/item_menu.h
index ce03cdacb..09ddd729c 100644
--- a/include/item_menu.h
+++ b/include/item_menu.h
@@ -78,7 +78,7 @@ struct BagMenu
u8 numShownItems[POCKETS_COUNT];
s16 graphicsLoadState;
u8 unused2[14];
- u8 pocketNameBuffer[32][32];
+ u8 ALIGNED(4) pocketNameBuffer[32][32];
u8 unused3[4];
};
diff --git a/include/librfu.h b/include/librfu.h
index 0026adece..6b0bd97c7 100644
--- a/include/librfu.h
+++ b/include/librfu.h
@@ -329,7 +329,7 @@ struct RfuIntrStruct
{
union RfuPacket rxPacketAlloc;
union RfuPacket txPacketAlloc;
- u8 block1[0x960]; // size of librfu_intr.s binary
+ u8 ALIGNED(2) block1[0x960]; // size of librfu_intr.s binary
struct STWIStatus block2;
};
diff --git a/include/link.h b/include/link.h
index f27cddc62..66dd5fecd 100644
--- a/include/link.h
+++ b/include/link.h
@@ -238,7 +238,7 @@ struct BlockRequest
};
extern struct Link gLink;
-extern u16 gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH];
+extern u16 ALIGNED(4) gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH];
extern u8 gBlockSendBuffer[BLOCK_BUFFER_SIZE];
extern u16 gLinkType;
extern u32 gLinkStatus;
diff --git a/include/mon_markings.h b/include/mon_markings.h
index fda7ad563..dbb53f8e8 100644
--- a/include/mon_markings.h
+++ b/include/mon_markings.h
@@ -18,7 +18,7 @@ struct MonMarkingsMenu
struct Sprite *textSprite;
const u8 *frameTiles;
const u16 *framePalette;
- u8 windowSpriteTiles[0x1000];
+ u8 ALIGNED(2) windowSpriteTiles[0x1000];
u8 unused[0x80];
u8 tileLoadState;
};
diff --git a/include/palette.h b/include/palette.h
index d23a658b4..15c92cc2a 100644
--- a/include/palette.h
+++ b/include/palette.h
@@ -54,9 +54,9 @@ struct PaletteFadeControl
extern struct PaletteFadeControl gPaletteFade;
extern u32 gPlttBufferTransferPending;
-extern u8 gPaletteDecompressionBuffer[];
-extern u16 gPlttBufferUnfaded[PLTT_BUFFER_SIZE];
-extern u16 gPlttBufferFaded[PLTT_BUFFER_SIZE];
+extern u8 ALIGNED(4) gPaletteDecompressionBuffer[];
+extern u16 ALIGNED(4) gPlttBufferUnfaded[PLTT_BUFFER_SIZE];
+extern u16 ALIGNED(4) gPlttBufferFaded[PLTT_BUFFER_SIZE];
void LoadCompressedPalette(const u32 *src, u16 offset, u16 size);
void LoadPalette(const void *src, u16 offset, u16 size);
diff --git a/include/pokeball.h b/include/pokeball.h
index 815221c89..602e9f973 100644
--- a/include/pokeball.h
+++ b/include/pokeball.h
@@ -49,7 +49,7 @@ extern const struct SpriteTemplate gBallSpriteTemplates[];
#define POKEBALL_OPPONENT_SENDOUT 0xFE
u8 DoPokeballSendOutAnimation(s16 pan, u8 kindOfThrow);
-void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriortiy, u8 delay, u32 fadePalettes, u16 species);
+void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriority, u8 delay, u32 fadePalettes, u16 species);
u8 CreateTradePokeballSprite(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subPriority, u8 delay, u32 fadePalettes);
void StartHealthboxSlideIn(u8 battler);
void DoHitAnimHealthboxEffect(u8 battler);
diff --git a/include/pokemon.h b/include/pokemon.h
index 2c32054c5..c62cbe132 100644
--- a/include/pokemon.h
+++ b/include/pokemon.h
@@ -462,12 +462,17 @@ void SetMultiuseSpriteTemplateToPokemon(u16 speciesTag, u8 battlerPosition);
void SetMultiuseSpriteTemplateToTrainerBack(u16 trainerSpriteId, u8 battlerPosition);
void SetMultiuseSpriteTemplateToTrainerFront(u16 trainerPicId, u8 battlerPosition);
-// These are full type signatures for GetMonData() and GetBoxMonData(),
-// but they are not used since some code erroneously omits the third arg.
-// u32 GetMonData(struct Pokemon *mon, s32 field, u8 *data);
-// u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data);
-u32 GetMonData();
-u32 GetBoxMonData();
+/* GameFreak called Get(Box)MonData with either 2 or 3 arguments, for
+ * type safety we have a Get(Box)MonData macro which dispatches to
+ * either Get(Box)MonData2 or Get(Box)MonData3 based on the number of
+ * arguments. The two functions are aliases of each other, but they
+ * differ for matching purposes in the caller's codegen. */
+#define GetMonData(...) CAT(GetMonData, NARG_8(__VA_ARGS__))(__VA_ARGS__)
+#define GetBoxMonData(...) CAT(GetBoxMonData, NARG_8(__VA_ARGS__))(__VA_ARGS__)
+u32 GetMonData3(struct Pokemon *mon, s32 field, u8 *data);
+u32 GetMonData2(struct Pokemon *mon, s32 field);
+u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data);
+u32 GetBoxMonData2(struct BoxPokemon *boxMon, s32 field);
void SetMonData(struct Pokemon *mon, s32 field, const void *dataArg);
void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg);
diff --git a/include/scanline_effect.h b/include/scanline_effect.h
index ae534d969..80d9df764 100644
--- a/include/scanline_effect.h
+++ b/include/scanline_effect.h
@@ -37,7 +37,7 @@ struct ScanlineEffect
extern struct ScanlineEffect gScanlineEffect;
-extern u16 gScanlineEffectRegBuffers[2][0x3C0];
+extern u16 ALIGNED(4) gScanlineEffectRegBuffers[2][0x3C0];
void ScanlineEffect_Stop(void);
void ScanlineEffect_Clear(void);
diff --git a/test/test_battle.h b/include/test/battle.h
similarity index 99%
rename from test/test_battle.h
rename to include/test/battle.h
index b4f38ed91..dc40a8a20 100644
--- a/test/test_battle.h
+++ b/include/test/battle.h
@@ -447,13 +447,13 @@
#ifndef GUARD_TEST_BATTLE_H
#define GUARD_TEST_BATTLE_H
+#include "global.h"
#include "battle.h"
#include "battle_anim.h"
#include "data.h"
#include "item.h"
#include "random.h"
#include "recorded_battle.h"
-#include "test.h"
#include "util.h"
#include "constants/abilities.h"
#include "constants/battle_anim.h"
@@ -462,6 +462,7 @@
#include "constants/items.h"
#include "constants/moves.h"
#include "constants/species.h"
+#include "test/test.h"
// NOTE: If the stack is too small the test runner will probably crash
// or loop.
diff --git a/test/test.h b/include/test/test.h
similarity index 100%
rename from test/test.h
rename to include/test/test.h
diff --git a/sound/cry_tables.inc b/sound/cry_tables.inc
index 618c610e6..dc15e0f04 100644
--- a/sound/cry_tables.inc
+++ b/sound/cry_tables.inc
@@ -2080,6 +2080,8 @@ gCryTable::
@ Calyrex
cry Cry_CalyrexIceRider
cry Cry_CalyrexShadowRider
+ @ Basculegion
+ cry Cry_Basculegion
.else
@ Cramorant
cry Cry_Unown
@@ -2118,6 +2120,8 @@ gCryTable::
@ Calyrex
cry Cry_Unown
cry Cry_Unown
+ @ Basculegion
+ cry Cry_Unown
.endif
.align 2
@@ -4163,6 +4167,8 @@ gCryTable_Reverse::
@ Calyrex
cry_reverse Cry_CalyrexIceRider
cry_reverse Cry_CalyrexShadowRider
+ @ Basculegion
+ cry_reverse Cry_Basculegion
.else
cry_reverse Cry_Unown
cry_reverse Cry_Unown
@@ -4187,4 +4193,5 @@ gCryTable_Reverse::
cry_reverse Cry_Unown
cry_reverse Cry_Unown
cry_reverse Cry_Unown
+ cry_reverse Cry_Unown
.endif
diff --git a/src/battle_controllers.c b/src/battle_controllers.c
index d5d3e479e..6424bee41 100644
--- a/src/battle_controllers.c
+++ b/src/battle_controllers.c
@@ -152,7 +152,7 @@ static void InitSinglePlayerBtlControllers(void)
gBattlerPartyIndexes[0] = 0;
gBattlerPartyIndexes[1] = 0;
- if (BATTLE_TWO_VS_ONE_OPPONENT)
+ if (BATTLE_TWO_VS_ONE_OPPONENT || WILD_DOUBLE_BATTLE)
{
gBattlerPartyIndexes[2] = 3;
gBattlerPartyIndexes[3] = 1;
diff --git a/src/battle_factory_screen.c b/src/battle_factory_screen.c
index 6366404ce..83075a4ce 100644
--- a/src/battle_factory_screen.c
+++ b/src/battle_factory_screen.c
@@ -268,7 +268,7 @@ static const u8 sActionHighlightMiddle_Gfx[] = INCBIN_U8( "graphics/battle_front
static const u8 sActionHighlightRight_Gfx[] = INCBIN_U8( "graphics/battle_frontier/factory_screen/action_highlight_right.4bpp");
static const u8 sMonPicBgAnim_Gfx[] = INCBIN_U8( "graphics/battle_frontier/factory_screen/mon_pic_bg_anim.4bpp");
static const u8 sMonPicBg_Tilemap[] = INCBIN_U8( "graphics/battle_frontier/factory_screen/mon_pic_bg.bin");
-static const u8 sMonPicBg_Gfx[] = INCBIN_U8( "graphics/battle_frontier/factory_screen/mon_pic_bg.4bpp");
+static const u16 sMonPicBg_Gfx[] = INCBIN_U16("graphics/battle_frontier/factory_screen/mon_pic_bg.4bpp");
static const u16 sMonPicBg_Pal[] = INCBIN_U16("graphics/battle_frontier/factory_screen/mon_pic_bg.gbapal");
static const struct SpriteSheet sSelect_SpriteSheets[] =
diff --git a/src/battle_interface.c b/src/battle_interface.c
index 3e6029ab7..196e6050f 100644
--- a/src/battle_interface.c
+++ b/src/battle_interface.c
@@ -1409,11 +1409,10 @@ bool32 IsMegaTriggerSpriteActive(void)
void HideMegaTriggerSprite(void)
{
- if (gBattleStruct->mega.triggerSpriteId != 0xFF)
- {
- ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, 0);
- gSprites[gBattleStruct->mega.triggerSpriteId].tHide = TRUE;
- }
+ if (gBattleStruct->mega.triggerSpriteId >= MAX_SPRITES)
+ return;
+ ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, 0);
+ gSprites[gBattleStruct->mega.triggerSpriteId].tHide = TRUE;
}
void HideTriggerSprites(void)
diff --git a/src/battle_main.c b/src/battle_main.c
index 5841f8f61..25736731c 100644
--- a/src/battle_main.c
+++ b/src/battle_main.c
@@ -1964,7 +1964,6 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer
u8 fixedIV;
s32 i, j;
u8 monsCount;
- s32 ball = -1;
if (battleTypeFlags & BATTLE_TYPE_TRAINER && !(battleTypeFlags & (BATTLE_TYPE_FRONTIER
| BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_TRAINER_HILL)))
@@ -1986,6 +1985,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer
for (i = 0; i < monsCount; i++)
{
+ s32 ball = -1;
u32 personalityHash = GeneratePartyHash(trainer, i);
if (trainer->doubleBattle == TRUE)
personalityValue = 0x80;
@@ -3170,7 +3170,10 @@ static void BattleStartClearSetData(void)
gBattleStruct->mega.triggerSpriteId = 0xFF;
- gBattleStruct->stickyWebUser = 0xFF;
+ for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
+ {
+ gSideTimers[i].stickyWebBattlerId = 0xFF;
+ }
gBattleStruct->appearedInBattle = 0;
gBattleStruct->beatUpSlot = 0;
@@ -3276,8 +3279,12 @@ void SwitchInClearSetData(void)
gBattleStruct->lastMoveFailed &= ~(gBitTable[gActiveBattler]);
gBattleStruct->palaceFlags &= ~(gBitTable[gActiveBattler]);
- if (gActiveBattler == gBattleStruct->stickyWebUser)
- gBattleStruct->stickyWebUser = 0xFF; // Switched into sticky web user slot so reset it
+ for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
+ {
+ // Switched into sticky web user slot, so reset stored battler ID
+ if (gSideTimers[i].stickyWebBattlerId == gActiveBattler)
+ gSideTimers[i].stickyWebBattlerId = 0xFF;
+ }
for (i = 0; i < gBattlersCount; i++)
{
@@ -3390,8 +3397,12 @@ void FaintClearSetData(void)
gBattleStruct->palaceFlags &= ~(gBitTable[gActiveBattler]);
- if (gActiveBattler == gBattleStruct->stickyWebUser)
- gBattleStruct->stickyWebUser = 0xFF; // User of sticky web fainted, so reset the stored battler ID
+ for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
+ {
+ // User of sticky web fainted, so reset the stored battler ID
+ if (gSideTimers[i].stickyWebBattlerId == gActiveBattler)
+ gSideTimers[i].stickyWebBattlerId = 0xFF;
+ }
for (i = 0; i < gBattlersCount; i++)
{
@@ -4584,7 +4595,11 @@ static void HandleTurnActionSelectionState(void)
{
// if we choose to throw a ball with our second mon, skip the action of the first
// (if we have chosen throw ball with first, second's is already skipped)
- gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)] = B_ACTION_NOTHING_FAINTED;
+ // if throwing a ball in a wild battle with an in-game partner, skip partner's turn when throwing a ball
+ if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
+ gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)] = B_ACTION_NOTHING_FAINTED;
+ else
+ gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)] = B_ACTION_NOTHING_FAINTED;
}
gBattleMainFunc = SetActionsAndBattlersTurnOrder;
diff --git a/src/battle_message.c b/src/battle_message.c
index ba4258382..cc8c99521 100644
--- a/src/battle_message.c
+++ b/src/battle_message.c
@@ -143,8 +143,8 @@ static const u8 sText_PkmnRaisedSpDefALittle[] = _("{B_ATK_PREFIX2}'s {B_CURRENT
static const u8 sText_PkmnRaisedDef[] = _("{B_ATK_PREFIX2}'s {B_CURRENT_MOVE}\nraised DEFENSE!");
static const u8 sText_PkmnRaisedDefALittle[] = _("{B_ATK_PREFIX2}'s {B_CURRENT_MOVE}\nraised DEFENSE a little!");
static const u8 sText_PkmnCoveredByVeil[] = _("{B_ATK_PREFIX2}'s party is covered\nby a veil!");
-static const u8 sText_PkmnUsedSafeguard[] = _("{B_DEF_NAME_WITH_PREFIX}'s party is protected\nby SAFEGUARD!");
-static const u8 sText_PkmnSafeguardExpired[] = _("{B_ATK_PREFIX3}'s party is no longer\nprotected by SAFEGUARD!");
+static const u8 sText_PkmnUsedSafeguard[] = _("{B_DEF_NAME_WITH_PREFIX}'s party is protected\nby Safeguard!");
+static const u8 sText_PkmnSafeguardExpired[] = _("{B_ATK_PREFIX3}'s party is no longer\nprotected by Safeguard!");
static const u8 sText_PkmnWentToSleep[] = _("{B_ATK_NAME_WITH_PREFIX} went\nto sleep!");
static const u8 sText_PkmnSleptHealthy[] = _("{B_ATK_NAME_WITH_PREFIX} slept and\nbecame healthy!");
static const u8 sText_PkmnWhippedWhirlwind[] = _("{B_ATK_NAME_WITH_PREFIX} whipped\nup a whirlwind!");
@@ -784,7 +784,7 @@ static const u8 sText_AttackerMeltedTheIce[] = _("{B_ATK_NAME_WITH_PREFIX} melte
static const u8 sText_TargetToughedItOut[] = _("{B_DEF_NAME_WITH_PREFIX} toughed it out\nto show you its best side!");
static const u8 sText_AttackerLostElectricType[] = _("{B_ATK_NAME_WITH_PREFIX} used up all\nof its electricity!");
static const u8 sText_AttackerSwitchedStatWithTarget[] = _("{B_ATK_NAME_WITH_PREFIX} switched {B_BUFF1}\nwith its target!");
-static const u8 sText_BeingHitChargedPkmnWithPower[] = _("Being hit by {B_CURRENT_MOVE}\ncharged {B_ATK_NAME_WITH_PREFIX} with power!");
+static const u8 sText_BeingHitChargedPkmnWithPower[] = _("Being hit by {B_CURRENT_MOVE}\ncharged {B_DEF_NAME_WITH_PREFIX} with power!");
static const u8 sText_SunlightActivatedAbility[] = _("The harsh sunlight activated\n{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_LAST_ABILITY}!");
static const u8 sText_StatWasHeightened[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_BUFF1} was heightened!");
static const u8 sText_ElectricTerrainActivatedAbility[] = _("The Electric Terrain activated\n{B_SCR_ACTIVE_NAME_WITH_PREFIX}'s {B_LAST_ABILITY}!");
@@ -1851,6 +1851,12 @@ const u16 gWeatherStartsStringIds[] =
[WEATHER_ABNORMAL] = STRINGID_ITISRAINING
};
+const u16 gPrimalWeatherBlocksStringIds[] =
+{
+ [B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN] = STRINGID_MOVEFIZZLEDOUTINTHEHEAVYRAIN,
+ [B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN] = STRINGID_MOVEEVAPORATEDINTHEHARSHSUNLIGHT,
+};
+
const u16 gInobedientStringIds[] =
{
[B_MSG_LOAFING] = STRINGID_PKMNLOAFING,
@@ -2693,7 +2699,7 @@ void BufferStringBattle(u16 stringID)
{
if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY)
stringPtr = sText_LegendaryPkmnAppeared;
- else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(gActiveBattler)]])) // interesting, looks like they had something planned for wild double battles
+ else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT)]]))
stringPtr = sText_TwoWildPkmnAppeared;
else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL)
stringPtr = sText_WildPkmnAppearedPause;
diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c
index ccd4a404f..af222e6ba 100644
--- a/src/battle_script_commands.c
+++ b/src/battle_script_commands.c
@@ -1503,25 +1503,8 @@ static void Cmd_attackcanceler(void)
s32 i, moveType;
u16 attackerAbility = GetBattlerAbility(gBattlerAttacker);
-
GET_MOVE_TYPE(gCurrentMove, moveType);
- if (WEATHER_HAS_EFFECT && gBattleMoves[gCurrentMove].power)
- {
- if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL))
- {
- BattleScriptPushCursor();
- gBattlescriptCurrInstr = BattleScript_PrimordialSeaFizzlesOutFireTypeMoves;
- return;
- }
- else if (moveType == TYPE_WATER && (gBattleWeather & B_WEATHER_SUN_PRIMAL))
- {
- BattleScriptPushCursor();
- gBattlescriptCurrInstr = BattleScript_DesolateLandEvaporatesWaterTypeMoves;
- return;
- }
- }
-
if (gBattleOutcome != 0)
{
gCurrentActionFuncId = B_ACTION_FINISHED;
@@ -1537,9 +1520,27 @@ static void Cmd_attackcanceler(void)
if (TryAegiFormChange())
return;
#endif
- if (AtkCanceller_UnableToUseMove())
+ if (AtkCanceller_UnableToUseMove(moveType))
return;
+ if (WEATHER_HAS_EFFECT && gBattleMoves[gCurrentMove].power)
+ {
+ if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL))
+ {
+ gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN;
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove;
+ return;
+ }
+ else if (moveType == TYPE_WATER && (gBattleWeather & B_WEATHER_SUN_PRIMAL))
+ {
+ gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN;
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove;
+ return;
+ }
+ }
+
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_OFF
&& GetBattlerAbility(gBattlerAttacker) == ABILITY_PARENTAL_BOND
&& IsMoveAffectedByParentalBond(gCurrentMove, gBattlerAttacker)
@@ -1852,6 +1853,8 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
s8 buff, accStage, evasionStage;
u8 atkParam = GetBattlerHoldEffectParam(battlerAtk);
u8 defParam = GetBattlerHoldEffectParam(battlerDef);
+ u8 atkAlly = BATTLE_PARTNER(battlerAtk);
+ u16 atkAllyAbility = GetBattlerAbility(atkAlly);
gPotentialItemEffectBattler = battlerDef;
accStage = gBattleMons[battlerAtk].statStages[STAT_ACC];
@@ -1885,30 +1888,66 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
calc = gAccuracyStageRatios[buff].dividend * moveAcc;
calc /= gAccuracyStageRatios[buff].divisor;
- if (atkAbility == ABILITY_COMPOUND_EYES)
+ // Attacker's ability
+ switch (atkAbility)
+ {
+ case ABILITY_COMPOUND_EYES:
calc = (calc * 130) / 100; // 1.3 compound eyes boost
- else if (atkAbility == ABILITY_VICTORY_STAR)
+ break;
+ case ABILITY_VICTORY_STAR:
calc = (calc * 110) / 100; // 1.1 victory star boost
- if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_VICTORY_STAR)
- calc = (calc * 110) / 100; // 1.1 ally's victory star boost
+ break;
+ case ABILITY_HUSTLE:
+ if (IS_MOVE_PHYSICAL(move))
+ calc = (calc * 80) / 100; // 1.2 hustle loss
+ break;
+ }
- if (defAbility == ABILITY_SAND_VEIL && WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM)
- calc = (calc * 80) / 100; // 1.2 sand veil loss
- else if (defAbility == ABILITY_SNOW_CLOAK && WEATHER_HAS_EFFECT && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
- calc = (calc * 80) / 100; // 1.2 snow cloak loss
- else if (defAbility == ABILITY_TANGLED_FEET && gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
- calc = (calc * 50) / 100; // 1.5 tangled feet loss
+ // Target's ability
+ switch (defAbility)
+ {
+ case ABILITY_SAND_VEIL:
+ if (WEATHER_HAS_EFFECT && gBattleWeather & B_WEATHER_SANDSTORM)
+ calc = (calc * 80) / 100; // 1.2 sand veil loss
+ break;
+ case ABILITY_SNOW_CLOAK:
+ if (WEATHER_HAS_EFFECT && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
+ calc = (calc * 80) / 100; // 1.2 snow cloak loss
+ break;
+ case ABILITY_TANGLED_FEET:
+ if (gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
+ calc = (calc * 50) / 100; // 1.5 tangled feet loss
+ break;
+ }
- if (atkAbility == ABILITY_HUSTLE && IS_MOVE_PHYSICAL(move))
- calc = (calc * 80) / 100; // 1.2 hustle loss
+ // Attacker's ally's ability
+ switch (atkAllyAbility)
+ {
+ case ABILITY_VICTORY_STAR:
+ if (IsBattlerAlive(atkAlly))
+ calc = (calc * 110) / 100; // 1.1 ally's victory star boost
+ break;
+ }
- if (defHoldEffect == HOLD_EFFECT_EVASION_UP)
+ // Attacker's hold effect
+ switch (atkHoldEffect)
+ {
+ case HOLD_EFFECT_WIDE_LENS:
+ calc = (calc * (100 + atkParam)) / 100;
+ break;
+ case HOLD_EFFECT_ZOOM_LENS:
+ if (GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef))
+ calc = (calc * (100 + atkParam)) / 100;
+ break;
+ }
+
+ // Target's hold effect
+ switch (defHoldEffect)
+ {
+ case HOLD_EFFECT_EVASION_UP:
calc = (calc * (100 - defParam)) / 100;
-
- if (atkHoldEffect == HOLD_EFFECT_WIDE_LENS)
- calc = (calc * (100 + atkParam)) / 100;
- else if (atkHoldEffect == HOLD_EFFECT_ZOOM_LENS && GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef))
- calc = (calc * (100 + atkParam)) / 100;
+ break;
+ }
if (gProtectStructs[battlerAtk].usedMicleBerry)
{
@@ -4144,11 +4183,7 @@ static void Cmd_jumpifsideaffecting(void)
u32 flags;
const u8 *jumpInstr;
- if (cmd->battler == BS_ATTACKER)
- side = GET_BATTLER_SIDE(gBattlerAttacker);
- else
- side = GET_BATTLER_SIDE(gBattlerTarget);
-
+ side = GET_BATTLER_SIDE(GetBattlerForBattleScript(cmd->battler));
flags = cmd->flags;
jumpInstr = cmd->jumpInstr;
@@ -8725,8 +8760,9 @@ static bool32 IsTeatimeAffected(u32 battlerId)
#define UPDATE_COURTCHANGED_BATTLER(structField)\
{ \
- sideTimerPlayer->structField ^= BIT_SIDE; \
- sideTimerOpp->structField ^= BIT_SIDE; \
+ temp = sideTimerPlayer->structField; \
+ sideTimerPlayer->structField = BATTLE_OPPOSITE(sideTimerOpp->structField); \
+ sideTimerOpp->structField = BATTLE_OPPOSITE(temp); \
} \
static bool32 CourtChangeSwapSideStatuses(void)
@@ -8761,9 +8797,7 @@ static bool32 CourtChangeSwapSideStatuses(void)
UPDATE_COURTCHANGED_BATTLER(auroraVeilBattlerId);
UPDATE_COURTCHANGED_BATTLER(tailwindBattlerId);
UPDATE_COURTCHANGED_BATTLER(luckyChantBattlerId);
-
- // For Mirror Armor only
- gBattleStruct->stickyWebUser = gBattlerAttacker;
+ UPDATE_COURTCHANGED_BATTLER(stickyWebBattlerId);
// Track which side originally set the Sticky Web
SWAP(sideTimerPlayer->stickyWebBattlerSide, sideTimerOpp->stickyWebBattlerSide, temp);
@@ -8803,33 +8837,6 @@ static void HandleScriptMegaPrimal(u32 caseId, u32 battlerId, bool32 isMega)
}
}
-static bool32 CanTeleport(u8 battlerId)
-{
- struct Pokemon *party = GetBattlerParty(battlerId);
- u32 species, count, i;
-
- for (i = 0; i < PARTY_SIZE; i++)
- {
- species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG);
- if (species != SPECIES_NONE && species != SPECIES_EGG && GetMonData(&party[i], MON_DATA_HP) != 0)
- count++;
- }
-
- switch (GetBattlerSide(battlerId))
- {
- case B_SIDE_OPPONENT:
- if (count == 1 || gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
- return FALSE;
- break;
- case B_SIDE_PLAYER:
- if (count == 1 || (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && count <= 2))
- return FALSE;
- break;
- }
-
- return TRUE;
-}
-
// Return True if the order was changed, and false if the order was not changed(for example because the target would move after the attacker anyway).
static bool32 ChangeOrderTargetAfterAttacker(void)
{
@@ -10795,8 +10802,8 @@ static void Cmd_various(void)
// If Pokémon which set up Sticky Web is not on the field, no Pokémon have their Speed lowered."
gBattlerAttacker = gBattlerTarget; // Initialize 'fail' condition
SET_STATCHANGER(STAT_SPEED, 1, TRUE);
- if (gBattleStruct->stickyWebUser != 0xFF)
- gBattlerAttacker = gBattleStruct->stickyWebUser;
+ if (gSideTimers[GetBattlerSide(gActiveBattler)].stickyWebBattlerId != 0xFF)
+ gBattlerAttacker = gSideTimers[GetBattlerSide(gActiveBattler)].stickyWebBattlerId;
break;
}
case VARIOUS_CUT_1_3_HP_RAISE_STATS:
@@ -14057,9 +14064,9 @@ static void Cmd_setstickyweb(void)
else
{
gSideStatuses[targetSide] |= SIDE_STATUS_STICKY_WEB;
+ gSideTimers[targetSide].stickyWebBattlerId = gBattlerAttacker; // For Mirror Armor
gSideTimers[targetSide].stickyWebBattlerSide = GetBattlerSide(gBattlerAttacker); // For Court Change/Defiant - set this to the user's side
gSideTimers[targetSide].stickyWebAmount = 1;
- gBattleStruct->stickyWebUser = gBattlerAttacker; // For Mirror Armor
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
@@ -14254,10 +14261,11 @@ static void Cmd_setforcedtarget(void)
static void Cmd_setcharge(void)
{
- CMD_ARGS();
+ CMD_ARGS(u8 battler);
- gStatuses3[gBattlerAttacker] |= STATUS3_CHARGED_UP;
- gDisableStructs[gBattlerAttacker].chargeTimer = 2;
+ u8 battler = GetBattlerForBattleScript(cmd->battler);
+ gStatuses3[battler] |= STATUS3_CHARGED_UP;
+ gDisableStructs[battler].chargeTimer = 2;
gBattlescriptCurrInstr++;
gBattlescriptCurrInstr = cmd->nextInstr;
}
@@ -16292,13 +16300,6 @@ void BS_GetBattlerSide(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
-void BS_CanTeleport(void)
-{
- NATIVE_ARGS(u8 battler);
- gBattleCommunication[0] = CanTeleport(cmd->battler);
- gBattlescriptCurrInstr = cmd->nextInstr;
-}
-
void BS_TrySymbiosis(void)
{
NATIVE_ARGS();
diff --git a/src/battle_util.c b/src/battle_util.c
index d549b71b9..91df461f0 100644
--- a/src/battle_util.c
+++ b/src/battle_util.c
@@ -3389,10 +3389,9 @@ void SetAtkCancellerForCalledMove(void)
gBattleStruct->isAtkCancelerForCalledMove = TRUE;
}
-u8 AtkCanceller_UnableToUseMove(void)
+u8 AtkCanceller_UnableToUseMove(u32 moveType)
{
u8 effect = 0;
- s32 *bideDmg = &gBattleScripting.bideDmg;
do
{
switch (gBattleStruct->atkCancellerTracker)
@@ -3653,7 +3652,7 @@ u8 AtkCanceller_UnableToUseMove(void)
if (gTakenDmg[gBattlerAttacker])
{
gCurrentMove = MOVE_BIDE;
- *bideDmg = gTakenDmg[gBattlerAttacker] * 2;
+ gBattleScripting.bideDmg = gTakenDmg[gBattlerAttacker] * 2;
gBattlerTarget = gTakenDmgByBattler[gBattlerAttacker];
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
gBattlerTarget = GetMoveTarget(MOVE_BIDE, MOVE_TARGET_SELECTED + 1);
@@ -3723,8 +3722,6 @@ u8 AtkCanceller_UnableToUseMove(void)
case CANCELLER_POWDER_STATUS:
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_POWDER)
{
- u32 moveType;
- GET_MOVE_TYPE(gCurrentMove, moveType);
if (moveType == TYPE_FIRE)
{
gProtectStructs[gBattlerAttacker].powderSelfDmg = TRUE;
@@ -3880,14 +3877,15 @@ u8 AtkCanceller_UnableToUseMove2(void)
bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2)
{
- u8 playerId, flankId;
+ u32 i, side, playerId, flankId;
struct Pokemon *party;
- s32 i;
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
return FALSE;
- if (BATTLE_TWO_VS_ONE_OPPONENT && GetBattlerSide(battler) == B_SIDE_OPPONENT)
+ side = GetBattlerSide(battler);
+
+ if (BATTLE_TWO_VS_ONE_OPPONENT && side == B_SIDE_OPPONENT)
{
flankId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
playerId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
@@ -3900,9 +3898,7 @@ bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2)
for (i = 0; i < PARTY_SIZE; i++)
{
- if (GetMonData(&party[i], MON_DATA_HP) != 0
- && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
- && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG
+ if (IsValidForBattle(&party[i])
&& i != partyIdBattlerOn1 && i != partyIdBattlerOn2
&& i != *(gBattleStruct->monToSwitchIntoId + flankId) && i != playerId[gBattleStruct->monToSwitchIntoId])
break;
@@ -3912,22 +3908,41 @@ bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2)
else if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
{
party = GetBattlerParty(battler);
-
- playerId = ((battler & BIT_FLANK) / 2);
- for (i = playerId * MULTI_PARTY_SIZE; i < playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE; i++)
+ if (side == B_SIDE_OPPONENT && WILD_DOUBLE_BATTLE)
{
- if (GetMonData(&party[i], MON_DATA_HP) != 0
- && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
- && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG)
- break;
+ flankId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
+ playerId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
+
+ if (partyIdBattlerOn1 == PARTY_SIZE)
+ partyIdBattlerOn1 = gBattlerPartyIndexes[flankId];
+ if (partyIdBattlerOn2 == PARTY_SIZE)
+ partyIdBattlerOn2 = gBattlerPartyIndexes[playerId];
+
+ for (i = 0; i < PARTY_SIZE; i++)
+ {
+ if (IsValidForBattle(&party[i])
+ && i != partyIdBattlerOn1 && i != partyIdBattlerOn2
+ && i != *(gBattleStruct->monToSwitchIntoId + flankId) && i != playerId[gBattleStruct->monToSwitchIntoId])
+ break;
+ }
+ return (i == PARTY_SIZE);
+ }
+ else
+ {
+ playerId = ((battler & BIT_FLANK) / 2);
+ for (i = playerId * MULTI_PARTY_SIZE; i < playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE; i++)
+ {
+ if (IsValidForBattle(&party[i]))
+ break;
+ }
+ return (i == playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE);
}
- return (i == playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE);
}
else if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
{
if (gBattleTypeFlags & BATTLE_TYPE_TOWER_LINK_MULTI)
{
- if (GetBattlerSide(battler) == B_SIDE_PLAYER)
+ if (side == B_SIDE_PLAYER)
{
party = gPlayerParty;
flankId = GetBattlerMultiplayerId(battler);
@@ -3951,14 +3966,12 @@ bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2)
for (i = playerId * MULTI_PARTY_SIZE; i < playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE; i++)
{
- if (GetMonData(&party[i], MON_DATA_HP) != 0
- && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
- && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG)
+ if (IsValidForBattle(&party[i]))
break;
}
return (i == playerId * MULTI_PARTY_SIZE + MULTI_PARTY_SIZE);
}
- else if ((gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && GetBattlerSide(battler) == B_SIDE_OPPONENT)
+ else if ((gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && side == B_SIDE_OPPONENT)
{
party = gEnemyParty;
@@ -3969,16 +3982,14 @@ bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2)
for (i = playerId; i < playerId + MULTI_PARTY_SIZE; i++)
{
- if (GetMonData(&party[i], MON_DATA_HP) != 0
- && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
- && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG)
+ if (IsValidForBattle(&party[i]))
break;
}
return (i == playerId + 3);
}
else
{
- if (GetBattlerSide(battler) == B_SIDE_OPPONENT)
+ if (side == B_SIDE_OPPONENT)
{
flankId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
playerId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
@@ -3998,9 +4009,7 @@ bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2)
for (i = 0; i < PARTY_SIZE; i++)
{
- if (GetMonData(&party[i], MON_DATA_HP) != 0
- && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
- && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG
+ if (IsValidForBattle(&party[i])
&& i != partyIdBattlerOn1 && i != partyIdBattlerOn2
&& i != *(gBattleStruct->monToSwitchIntoId + flankId) && i != playerId[gBattleStruct->monToSwitchIntoId])
break;
@@ -5691,7 +5700,6 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
&& TARGET_TURN_DAMAGED
&& IsBattlerAlive(gBattlerTarget))
{
- gBattlerAttacker = gBattlerTarget;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_WindPowerActivates;
effect++;
@@ -8004,7 +8012,7 @@ u8 IsMonDisobedient(void)
if (IsBattlerModernFatefulEncounter(gBattlerAttacker)) // only false if illegal Mew or Deoxys
{
- if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gBattlerAttacker) == 2)
+ if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gBattlerAttacker) == B_POSITION_PLAYER_RIGHT)
return 0;
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER)
return 0;
@@ -10068,19 +10076,19 @@ bool32 CanMegaEvolve(u8 battlerId)
species = GetMonData(mon, MON_DATA_SPECIES);
itemId = GetMonData(mon, MON_DATA_HELD_ITEM);
+#if DEBUG_BATTLE_MENU == TRUE
+ if (gBattleStruct->debugHoldEffects[battlerId])
+ holdEffect = gBattleStruct->debugHoldEffects[battlerId];
+ else
+#endif
+ if (itemId == ITEM_ENIGMA_BERRY_E_READER)
+ holdEffect = gEnigmaBerries[battlerId].holdEffect;
+ else
+ holdEffect = ItemId_GetHoldEffect(itemId);
+
// Check if there is an entry in the evolution table for regular Mega Evolution.
if (GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM) != SPECIES_NONE)
{
- #if DEBUG_BATTLE_MENU == TRUE
- if (gBattleStruct->debugHoldEffects[battlerId])
- holdEffect = gBattleStruct->debugHoldEffects[battlerId];
- else
- #endif
- if (itemId == ITEM_ENIGMA_BERRY_E_READER)
- holdEffect = gEnigmaBerries[battlerId].holdEffect;
- else
- holdEffect = ItemId_GetHoldEffect(itemId);
-
// Can Mega Evolve via Mega Stone.
if (holdEffect == HOLD_EFFECT_MEGA_STONE)
return TRUE;
@@ -10088,7 +10096,11 @@ bool32 CanMegaEvolve(u8 battlerId)
// Check if there is an entry in the evolution table for Wish Mega Evolution.
if (GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != SPECIES_NONE)
- return TRUE;
+ {
+ // Can't Wish Mega Evolve if holding a Z Crystal.
+ if (holdEffect != HOLD_EFFECT_Z_CRYSTAL)
+ return TRUE;
+ }
// No checks passed, the mon CAN'T mega evolve.
return FALSE;
diff --git a/src/battle_z_move.c b/src/battle_z_move.c
index dec3701c0..f17de921d 100644
--- a/src/battle_z_move.c
+++ b/src/battle_z_move.c
@@ -159,7 +159,6 @@ void QueueZMove(u8 battlerId, u16 baseMove)
bool32 IsViableZMove(u8 battlerId, u16 move)
{
struct Pokemon *mon;
- struct MegaEvolutionData *mega = &(((struct ChooseMoveStruct *)(&gBattleResources->bufferA[gActiveBattler][4]))->mega);
u8 battlerPosition = GetBattlerPosition(battlerId);
u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battlerId));
u32 item;
@@ -185,15 +184,6 @@ bool32 IsViableZMove(u8 battlerId, u16 move)
if ((GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battlerId) == B_POSITION_PLAYER_RIGHT)) && !CheckBagHasItem(ITEM_Z_POWER_RING, 1))
return FALSE;
- if (mega->alreadyEvolved[battlerPosition])
- return FALSE; // Trainer has mega evolved
-
- if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
- {
- if (IsPartnerMonFromSameTrainer(battlerId) && (mega->alreadyEvolved[partnerPosition] || (mega->toEvolve & gBitTable[BATTLE_PARTNER(battlerId)])))
- return FALSE; // Partner has mega evolved or is about to mega evolve
- }
-
#if DEBUG_BATTLE_MENU == TRUE
if (gBattleStruct->debugHoldEffects[battlerId])
holdEffect = gBattleStruct->debugHoldEffects[battlerId];
@@ -355,9 +345,12 @@ bool32 IsZMoveTriggerSpriteActive(void)
void HideZMoveTriggerSprite(void)
{
- struct Sprite *sprite = &gSprites[gBattleStruct->zmove.triggerSpriteId];
- sprite->tHide = TRUE;
+ struct Sprite *sprite;
gBattleStruct->zmove.viable = FALSE;
+ if (gBattleStruct->zmove.triggerSpriteId >= MAX_SPRITES)
+ return;
+ sprite = &gSprites[gBattleStruct->zmove.triggerSpriteId];
+ sprite->tHide = TRUE;
}
static void ShowZMoveTriggerSprite(u8 battlerId)
diff --git a/src/berry_fix_program.c b/src/berry_fix_program.c
index af21bb929..26af445a0 100644
--- a/src/berry_fix_program.c
+++ b/src/berry_fix_program.c
@@ -117,7 +117,7 @@ static const struct WindowTemplate sBerryFixWindowTemplates[] = {
DUMMY_WIN_TEMPLATE
};
-static const u16 sBerryFixPalColors[] = {
+static const u16 ALIGNED(4) sBerryFixPalColors[] = {
RGB_WHITE, RGB_WHITE, RGB(12, 12, 12), RGB(26, 26, 25),
RGB(28, 1, 1), RGB(31, 23, 14), RGB(4, 19, 1), RGB(18, 30, 18),
RGB(6, 10, 25), RGB(20, 24, 30), RGB_WHITE, RGB(12, 12, 12),
diff --git a/src/contest.c b/src/contest.c
index 55207eb3e..91bca846d 100644
--- a/src/contest.c
+++ b/src/contest.c
@@ -1301,8 +1301,8 @@ static void Task_ReadyStartLinkContest(u8 taskId)
static bool8 SetupContestGraphics(u8 *stateVar)
{
- u16 tempPalette1[16];
- u16 tempPalette2[16];
+ u16 ALIGNED(4) tempPalette1[16];
+ u16 ALIGNED(4) tempPalette2[16];
switch (*stateVar)
{
diff --git a/src/data/items.h b/src/data/items.h
index 802cd325d..5e15b2734 100644
--- a/src/data/items.h
+++ b/src/data/items.h
@@ -6,7 +6,7 @@
#define EVO_HELD_ITEM_FIELD_FUNC ItemUseOutOfBattle_CannotUse
#endif
-#if I_GEM_BOOST_POWER >= GEN_5
+#if I_GEM_BOOST_POWER >= GEN_6
#define GEM_BOOST_PARAM 30
#else
#define GEM_BOOST_PARAM 50
diff --git a/src/daycare.c b/src/daycare.c
index 9a1e98d98..2b4311734 100644
--- a/src/daycare.c
+++ b/src/daycare.c
@@ -412,7 +412,7 @@ static void Debug_AddDaycareSteps(u16 numSteps)
u8 GetNumLevelsGainedFromDaycare(void)
{
- if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[gSpecialVar_0x8004], MON_DATA_SPECIES) != 0)
+ if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[gSpecialVar_0x8004].mon, MON_DATA_SPECIES) != 0)
return GetNumLevelsGainedForDaycareMon(&gSaveBlock1Ptr->daycare.mons[gSpecialVar_0x8004]);
return 0;
diff --git a/src/decoration.c b/src/decoration.c
index 27ef85de9..b33580d2d 100644
--- a/src/decoration.c
+++ b/src/decoration.c
@@ -1916,7 +1916,7 @@ static void CopyPalette(u16 *dest, u16 pal)
static void CopyTile(u8 *dest, u16 tile)
{
- u8 buffer[TILE_SIZE_4BPP];
+ u8 ALIGNED(4) buffer[TILE_SIZE_4BPP];
u16 mode;
u16 i;
diff --git a/src/field_weather.c b/src/field_weather.c
index 73aef3746..bcc6a1c44 100644
--- a/src/field_weather.c
+++ b/src/field_weather.c
@@ -61,7 +61,7 @@ static void None_Main(void);
static u8 None_Finish(void);
EWRAM_DATA struct Weather gWeather = {0};
-EWRAM_DATA static u8 sFieldEffectPaletteColorMapTypes[32] = {0};
+EWRAM_DATA static u8 ALIGNED(2) sFieldEffectPaletteColorMapTypes[32] = {0};
static const u8 *sPaletteColorMapTypes;
@@ -111,7 +111,7 @@ void (*const gWeatherPalStateFuncs[])(void) =
// This table specifies which of the color maps should be
// applied to each of the background and sprite palettes.
-static const u8 sBasePaletteColorMapTypes[32] =
+static const u8 ALIGNED(2) sBasePaletteColorMapTypes[32] =
{
// background palettes
COLOR_MAP_DARK_CONTRAST,
@@ -149,7 +149,7 @@ static const u8 sBasePaletteColorMapTypes[32] =
COLOR_MAP_DARK_CONTRAST,
};
-const u16 gFogPalette[] = INCBIN_U16("graphics/weather/fog.gbapal");
+const u16 ALIGNED(4) gFogPalette[] = INCBIN_U16("graphics/weather/fog.gbapal");
void StartWeather(void)
{
diff --git a/src/fieldmap.c b/src/fieldmap.c
index bdacf7ab2..9534255c7 100644
--- a/src/fieldmap.c
+++ b/src/fieldmap.c
@@ -25,7 +25,7 @@ struct ConnectionFlags
u8 east:1;
};
-EWRAM_DATA static u16 sBackupMapData[MAX_MAP_DATA_SIZE] = {0};
+EWRAM_DATA static u16 ALIGNED(4) sBackupMapData[MAX_MAP_DATA_SIZE] = {0};
EWRAM_DATA struct MapHeader gMapHeader = {0};
EWRAM_DATA struct Camera gCamera = {0};
EWRAM_DATA static struct ConnectionFlags sMapConnectionFlags = {0};
diff --git a/src/graphics.c b/src/graphics.c
index e7a0cbe64..d122b15a7 100644
--- a/src/graphics.c
+++ b/src/graphics.c
@@ -1709,8 +1709,8 @@ const u32 gRouletteMultiplier_Gfx[] = INCBIN_U32("graphics/roulette/multiplier.4
const u16 gFrontierFactorySelectMenu_Pal[] = INCBIN_U16("graphics/battle_frontier/factory_menu1.gbapal");
const u16 gFrontierFactorySelectMenu_Pal2[] = INCBIN_U16("graphics/battle_frontier/factory_menu2.gbapal");
-const u8 gFrontierFactorySelectMenu_Gfx[] = INCBIN_U8("graphics/battle_frontier/factory_menu1.4bpp");
-const u8 gFrontierFactorySelectMenu_Gfx2[] = INCBIN_U8("graphics/battle_frontier/factory_menu2.4bpp");
+const u16 gFrontierFactorySelectMenu_Gfx[] = INCBIN_U16("graphics/battle_frontier/factory_menu1.4bpp");
+const u16 gFrontierFactorySelectMenu_Gfx2[] = INCBIN_U16("graphics/battle_frontier/factory_menu2.4bpp");
const u16 gFrontierFactorySelectMenu_Tilemap[] = INCBIN_U16("graphics/battle_frontier/factory_menu.bin");
diff --git a/src/intro.c b/src/intro.c
index bfc23a74b..28b4120b1 100644
--- a/src/intro.c
+++ b/src/intro.c
@@ -1096,6 +1096,10 @@ static u8 SetUpCopyrightScreen(void)
REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG0_ON;
SetSerialCallback(SerialCB_CopyrightScreen);
GameCubeMultiBoot_Init(&gMultibootProgramStruct);
+ // REG_DISPCNT needs to be overwritten the second time, because otherwise the intro won't show up on VBA 1.7.2 and John GBA Lite emulators.
+ // The REG_DISPCNT overwrite is NOT needed in m-GBA, No$GBA, VBA 1.8.0, My Boy and Pizza Boy GBA emulators.
+ case 1:
+ REG_DISPCNT = DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG0_ON;
default:
UpdatePaletteFade();
gMain.state++;
diff --git a/src/item_menu.c b/src/item_menu.c
index defa4f68a..92c1773e7 100755
--- a/src/item_menu.c
+++ b/src/item_menu.c
@@ -2436,16 +2436,16 @@ static void PrintPocketNames(const u8 *pocketName1, const u8 *pocketName2)
static void CopyPocketNameToWindow(u32 a)
{
- u8 (* tileDataBuffer)[32][32];
+ u8 (*tileDataBuffer)[32][32];
u8 *windowTileData;
int b;
if (a > 8)
a = 8;
tileDataBuffer = &gBagMenu->pocketNameBuffer;
windowTileData = (u8 *)GetWindowAttribute(2, WINDOW_TILE_DATA);
- CpuCopy32(tileDataBuffer[0][a], windowTileData, 0x100); // Top half of pocket name
+ CpuCopy32(&tileDataBuffer[0][a], windowTileData, 0x100); // Top half of pocket name
b = a + 16;
- CpuCopy32(tileDataBuffer[0][b], windowTileData + 0x100, 0x100); // Bottom half of pocket name
+ CpuCopy32(&tileDataBuffer[0][b], windowTileData + 0x100, 0x100); // Bottom half of pocket name
CopyWindowToVram(WIN_POCKET_NAME, COPYWIN_GFX);
}
diff --git a/src/link.c b/src/link.c
index 0894021b5..c51abc952 100644
--- a/src/link.c
+++ b/src/link.c
@@ -78,7 +78,7 @@ bool8 gRemoteLinkPlayersNotReceived[MAX_LINK_PLAYERS];
u8 gBlockReceivedStatus[MAX_LINK_PLAYERS];
u32 gLinkFiller2;
u16 gLinkHeldKeys;
-u16 gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH];
+u16 ALIGNED(4) gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH];
u32 gLinkStatus;
bool8 gLinkDummy1; // Never read
bool8 gLinkDummy2; // Never read
diff --git a/src/mirage_tower.c b/src/mirage_tower.c
index f9806e776..9b48ee24b 100644
--- a/src/mirage_tower.c
+++ b/src/mirage_tower.c
@@ -75,7 +75,7 @@ static void Task_FossilFallAndSink(u8);
static void SpriteCB_FallingFossil(struct Sprite *);
static void UpdateDisintegrationEffect(u8 *, u16, u8, u8, u8);
-static const u8 sBlankTile_Gfx[32] = {0};
+static const u8 ALIGNED(2) sBlankTile_Gfx[32] = {0};
static const u8 sMirageTower_Gfx[] = INCBIN_U8("graphics/misc/mirage_tower.4bpp");
static const u16 sMirageTowerTilemap[] = INCBIN_U16("graphics/misc/mirage_tower.bin");
static const u16 sFossil_Pal[] = INCBIN_U16("graphics/object_events/pics/misc/fossil.gbapal"); // Unused
diff --git a/src/palette.c b/src/palette.c
index 6ce47a493..43a4c213c 100644
--- a/src/palette.c
+++ b/src/palette.c
@@ -64,7 +64,7 @@ static EWRAM_DATA struct PaletteStruct sPaletteStructs[NUM_PALETTE_STRUCTS] = {0
EWRAM_DATA struct PaletteFadeControl gPaletteFade = {0};
static EWRAM_DATA u32 sFiller = 0;
static EWRAM_DATA u32 sPlttBufferTransferPending = 0;
-EWRAM_DATA u8 gPaletteDecompressionBuffer[PLTT_SIZE] = {0};
+EWRAM_DATA u8 ALIGNED(2) gPaletteDecompressionBuffer[PLTT_SIZE] = {0};
static const struct PaletteStructTemplate sDummyPaletteStructTemplate = {
.id = 0xFFFF,
diff --git a/src/pokeball.c b/src/pokeball.c
index 9147ce16b..2e556ec22 100644
--- a/src/pokeball.c
+++ b/src/pokeball.c
@@ -1218,13 +1218,13 @@ static u8 LaunchBallFadeMonTaskForPokeball(bool8 unFadeLater, u8 spritePalNum, u
#define sTrigIdx data[7]
// Pokeball in Birch intro, and when receiving via trade
-void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriortiy, u8 delay, u32 fadePalettes, u16 species)
+void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 monPalNum, u8 x, u8 y, u8 oamPriority, u8 subpriority, u8 delay, u32 fadePalettes, u16 species)
{
u8 spriteId;
LoadCompressedSpriteSheetUsingHeap(&gBallSpriteSheets[BALL_POKE]);
LoadCompressedSpritePaletteUsingHeap(&gBallSpritePalettes[BALL_POKE]);
- spriteId = CreateSprite(&gBallSpriteTemplates[BALL_POKE], x, y, subpriortiy);
+ spriteId = CreateSprite(&gBallSpriteTemplates[BALL_POKE], x, y, subpriority);
gSprites[spriteId].sMonSpriteId = monSpriteId;
gSprites[spriteId].sFinalMonX = gSprites[monSpriteId].x;
diff --git a/src/pokedex.c b/src/pokedex.c
index c5b8105b2..a0cb85b75 100644
--- a/src/pokedex.c
+++ b/src/pokedex.c
@@ -4537,7 +4537,7 @@ static void PrintDecimalNum(u8 windowId, u16 num, u8 left, u8 top)
static void DrawFootprint(u8 windowId, u16 dexNum)
{
- u8 footprint4bpp[TILE_SIZE_4BPP * NUM_FOOTPRINT_TILES];
+ u8 ALIGNED(4) footprint4bpp[TILE_SIZE_4BPP * NUM_FOOTPRINT_TILES];
const u8 *footprintGfx = gMonFootprintTable[NationalPokedexNumToSpecies(dexNum)];
u32 i, j, tileIdx = 0;
diff --git a/src/pokedex_area_region_map.c b/src/pokedex_area_region_map.c
index da50f8c68..cd2975473 100644
--- a/src/pokedex_area_region_map.c
+++ b/src/pokedex_area_region_map.c
@@ -8,7 +8,7 @@
static EWRAM_DATA u8 *sPokedexAreaMapBgNum = NULL;
-static const u16 sPokedexAreaMap_Pal[] = INCBIN_U16("graphics/pokedex/region_map.gbapal");
+static const u16 ALIGNED(4) sPokedexAreaMap_Pal[] = INCBIN_U16("graphics/pokedex/region_map.gbapal");
static const u32 sPokedexAreaMap_Gfx[] = INCBIN_U32("graphics/pokedex/region_map.8bpp.lz");
static const u32 sPokedexAreaMap_Tilemap[] = INCBIN_U32("graphics/pokedex/region_map.bin.lz");
static const u32 sPokedexAreaMapAffine_Gfx[] = INCBIN_U32("graphics/pokedex/region_map_affine.8bpp.lz");
diff --git a/src/pokemon.c b/src/pokemon.c
index b06abb6b5..fb3898465 100644
--- a/src/pokemon.c
+++ b/src/pokemon.c
@@ -4627,7 +4627,11 @@ static union PokemonSubstruct *GetSubstruct(struct BoxPokemon *boxMon, u32 perso
return substruct;
}
-u32 GetMonData(struct Pokemon *mon, s32 field, u8 *data)
+/* GameFreak called GetMonData with either 2 or 3 arguments, for type
+ * safety we have a GetMonData macro (in include/pokemon.h) which
+ * dispatches to either GetMonData2 or GetMonData3 based on the number
+ * of arguments. */
+u32 GetMonData3(struct Pokemon *mon, s32 field, u8 *data)
{
u32 ret;
@@ -4685,7 +4689,13 @@ u32 GetMonData(struct Pokemon *mon, s32 field, u8 *data)
return ret;
}
-u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data)
+u32 GetMonData2(struct Pokemon *mon, s32 field) __attribute__((alias("GetMonData3")));
+
+/* GameFreak called GetBoxMonData with either 2 or 3 arguments, for type
+ * safety we have a GetBoxMonData macro (in include/pokemon.h) which
+ * dispatches to either GetBoxMonData2 or GetBoxMonData3 based on the
+ * number of arguments. */
+u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
{
s32 i;
u32 retVal = 0;
@@ -5041,6 +5051,8 @@ u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data)
return retVal;
}
+u32 GetBoxMonData2(struct BoxPokemon *boxMon, s32 field) __attribute__((alias("GetBoxMonData3")));
+
#define SET8(lhs) (lhs) = *data
#define SET16(lhs) (lhs) = data[0] + (data[1] << 8)
#define SET32(lhs) (lhs) = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24)
diff --git a/src/pokemon_storage_system.c b/src/pokemon_storage_system.c
index 50f315abd..6faa4aeb9 100644
--- a/src/pokemon_storage_system.c
+++ b/src/pokemon_storage_system.c
@@ -552,8 +552,8 @@ struct PokemonStorageSystemData
u16 *displayMonTilePtr;
struct Sprite *displayMonSprite;
u16 displayMonPalBuffer[0x40];
- u8 tileBuffer[MON_PIC_SIZE * MAX_MON_PIC_FRAMES];
- u8 itemIconBuffer[0x800];
+ u8 ALIGNED(4) tileBuffer[MON_PIC_SIZE * MAX_MON_PIC_FRAMES];
+ u8 ALIGNED(4) itemIconBuffer[0x800];
u8 wallpaperBgTilemapBuffer[0x1000];
u8 displayMenuTilemapBuffer[0x800];
};
@@ -10150,7 +10150,7 @@ void UpdateSpeciesSpritePSS(struct BoxPokemon *boxMon)
{
DestroyBoxMonIcon(sStorage->boxMonsSprites[sCursorPosition]);
CreateBoxMonIconAtPos(sCursorPosition);
- SetBoxMonIconObjMode(sCursorPosition, GetMonData(boxMon, MON_DATA_HELD_ITEM) == ITEM_NONE);
+ SetBoxMonIconObjMode(sCursorPosition, GetBoxMonData(boxMon, MON_DATA_HELD_ITEM) == ITEM_NONE);
}
}
sJustOpenedBag = FALSE;
diff --git a/src/pokenav_conditions_gfx.c b/src/pokenav_conditions_gfx.c
index e382cbf69..9f4b2db61 100644
--- a/src/pokenav_conditions_gfx.c
+++ b/src/pokenav_conditions_gfx.c
@@ -116,10 +116,12 @@ static const LoopedTask sLoopedTaskFuncs[] =
[CONDITION_FUNC_CLOSE_MARKINGS] = LoopedTask_CloseMonMarkingsWindow
};
+typedef u8 ALIGNED(4) TilemapBuffer[BG_SCREEN_SIZE];
+
struct Pokenav_ConditionMenuGfx
{
u32 loopedTaskId;
- u8 tilemapBuffers[3][BG_SCREEN_SIZE];
+ TilemapBuffer tilemapBuffers[3];
u8 filler[2];
u8 partyPokeballSpriteIds[PARTY_SIZE + 1];
u32 (*callback)(void);
diff --git a/src/pokenav_region_map.c b/src/pokenav_region_map.c
index e589e2818..f81ff4296 100755
--- a/src/pokenav_region_map.c
+++ b/src/pokenav_region_map.c
@@ -35,7 +35,7 @@ struct Pokenav_RegionMapGfx
u32 loopTaskId;
u16 infoWindowId;
struct Sprite *cityZoomTextSprites[3];
- u8 tilemapBuffer[BG_SCREEN_SIZE];
+ u8 ALIGNED(2) tilemapBuffer[BG_SCREEN_SIZE];
u8 cityZoomPics[NUM_CITY_MAPS][200];
};
diff --git a/src/rayquaza_scene.c b/src/rayquaza_scene.c
index c984dfec8..cacadf528 100644
--- a/src/rayquaza_scene.c
+++ b/src/rayquaza_scene.c
@@ -60,10 +60,12 @@ enum
#define MAX_SMOKE 10
+typedef u8 ALIGNED(4) TilemapBuffer[BG_SCREEN_SIZE];
+
struct RayquazaScene
{
MainCallback exitCallback;
- u8 tilemapBuffers[4][BG_SCREEN_SIZE];
+ TilemapBuffer tilemapBuffers[4];
u16 unk; // never read
u8 animId;
bool8 endEarly;
diff --git a/src/scanline_effect.c b/src/scanline_effect.c
index dc3ca03f4..684c89546 100644
--- a/src/scanline_effect.c
+++ b/src/scanline_effect.c
@@ -13,7 +13,7 @@ static void CopyValue32Bit(void);
// Per-scanline register values.
// This is double buffered so that it can be safely written to at any time
// without overwriting the buffer that the DMA is currently reading
-EWRAM_DATA u16 gScanlineEffectRegBuffers[2][0x3C0] = {0};
+EWRAM_DATA u16 ALIGNED(4) gScanlineEffectRegBuffers[2][0x3C0] = {0};
EWRAM_DATA struct ScanlineEffect gScanlineEffect = {0};
EWRAM_DATA static bool8 sShouldStopWaveTask = FALSE;
diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c
old mode 100755
new mode 100644
diff --git a/src/util.c b/src/util.c
index 32f31a26d..ab5603b86 100644
--- a/src/util.c
+++ b/src/util.c
@@ -158,7 +158,7 @@ void CopySpriteTiles(u8 shape, u8 size, u8 *tiles, u16 *tilemap, u8 *output)
{
u8 x, y;
s8 i, j;
- u8 xflip[32];
+ u8 ALIGNED(4) xflip[32];
u8 h = sSpriteDimensions[shape][size][1];
u8 w = sSpriteDimensions[shape][size][0];
diff --git a/test/ability_bad_dreams.c b/test/battle/ability/bad_dreams.c
similarity index 99%
rename from test/ability_bad_dreams.c
rename to test/battle/ability/bad_dreams.c
index 9bf800534..42ac3f04a 100644
--- a/test/ability_bad_dreams.c
+++ b/test/battle/ability/bad_dreams.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/ability_beads_of_ruin.c b/test/battle/ability/beads_of_ruin.c
similarity index 98%
rename from test/ability_beads_of_ruin.c
rename to test/battle/ability/beads_of_ruin.c
index 28b39a430..1c6f740e8 100644
--- a/test/ability_beads_of_ruin.c
+++ b/test/battle/ability/beads_of_ruin.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/ability_blaze.c b/test/battle/ability/blaze.c
similarity index 95%
rename from test/ability_blaze.c
rename to test/battle/ability/blaze.c
index 1e12c6b29..12ccced93 100644
--- a/test/ability_blaze.c
+++ b/test/battle/ability/blaze.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Blaze boosts Fire-type moves in a pinch", s16 damage)
{
diff --git a/test/ability_clear_body.c b/test/battle/ability/clear_body.c
similarity index 98%
rename from test/ability_clear_body.c
rename to test/battle/ability/clear_body.c
index 11789aaf6..7f167c9fe 100644
--- a/test/ability_clear_body.c
+++ b/test/battle/ability/clear_body.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Clear Body prevents intimidate")
{
diff --git a/test/ability_cloud_nine.c b/test/battle/ability/cloud_nine.c
similarity index 94%
rename from test/ability_cloud_nine.c
rename to test/battle/ability/cloud_nine.c
index 02da15ee1..e8de0fd8a 100644
--- a/test/ability_cloud_nine.c
+++ b/test/battle/ability/cloud_nine.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Cloud Nine prevents weather effects")
{
diff --git a/test/ability_compound_eyes.c b/test/battle/ability/compound_eyes.c
similarity index 97%
rename from test/ability_compound_eyes.c
rename to test/battle/ability/compound_eyes.c
index 6bdb15fa7..cc005943b 100644
--- a/test/ability_compound_eyes.c
+++ b/test/battle/ability/compound_eyes.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Compound Eyes raises accuracy")
{
diff --git a/test/ability_contrary.c b/test/battle/ability/contrary.c
similarity index 99%
rename from test/ability_contrary.c
rename to test/battle/ability/contrary.c
index 52347f379..de4093716 100644
--- a/test/ability_contrary.c
+++ b/test/battle/ability/contrary.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/ability_cute_charm.c b/test/battle/ability/cute_charm.c
similarity index 98%
rename from test/ability_cute_charm.c
rename to test/battle/ability/cute_charm.c
index ccf5c490b..1a55fc359 100644
--- a/test/ability_cute_charm.c
+++ b/test/battle/ability/cute_charm.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Cute Charm inflicts infatuation on contact")
{
diff --git a/test/ability_damp.c b/test/battle/ability/damp.c
similarity index 98%
rename from test/ability_damp.c
rename to test/battle/ability/damp.c
index 19a4ac5ad..ce53db112 100644
--- a/test/ability_damp.c
+++ b/test/battle/ability/damp.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Damp prevents explosion-like moves from enemies")
{
diff --git a/test/battle/ability/desolate_land.c b/test/battle/ability/desolate_land.c
new file mode 100644
index 000000000..ca52c9cd6
--- /dev/null
+++ b/test/battle/ability/desolate_land.c
@@ -0,0 +1,67 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ ASSUME(gBattleMoves[MOVE_WATER_GUN].power != 0);
+ ASSUME(gBattleMoves[MOVE_WATER_GUN].type == TYPE_WATER);
+}
+
+SINGLE_BATTLE_TEST("Desolate Land blocks damaging Water-type moves")
+{
+ GIVEN {
+ PLAYER(SPECIES_GROUDON) {Item(ITEM_RED_ORB);}
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_WATER_GUN); }
+ TURN { MOVE(opponent, MOVE_WATER_GUN); }
+ } SCENE {
+ MESSAGE("Foe Wobbuffet used Water Gun!");
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
+ MESSAGE("The Water-type attack evaporated in the harsh sunlight!");
+ NOT HP_BAR(player);
+ MESSAGE("Foe Wobbuffet used Water Gun!");
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
+ MESSAGE("The Water-type attack evaporated in the harsh sunlight!");
+ NOT HP_BAR(player);
+ } THEN {
+ EXPECT_EQ(player->hp, player->maxHP);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Desolate Land blocks damaging Water-type moves and prints the message only once with moves hitting multiple targets")
+{
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_SURF].power != 0);
+ ASSUME(gBattleMoves[MOVE_SURF].type == TYPE_WATER);
+ ASSUME(gBattleMoves[MOVE_SURF].target == MOVE_TARGET_FOES_AND_ALLY);
+ PLAYER(SPECIES_GROUDON) {Item(ITEM_RED_ORB); {Speed(5);}}
+ PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
+ OPPONENT(SPECIES_WOBBUFFET) {Speed(10);}
+ OPPONENT(SPECIES_WOBBUFFET) {Speed(8);}
+ } WHEN {
+ TURN { MOVE(opponentLeft, MOVE_SURF); }
+ } SCENE {
+ MESSAGE("Foe Wobbuffet used Surf!");
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, opponentLeft);
+ MESSAGE("The Water-type attack evaporated in the harsh sunlight!");
+ NOT MESSAGE("The Water-type attack evaporated in the harsh sunlight!");
+ } THEN {
+ EXPECT_EQ(playerLeft->hp, playerLeft->maxHP);
+ EXPECT_EQ(playerRight->hp, playerRight->maxHP);
+ EXPECT_EQ(opponentRight->hp, opponentRight->maxHP);
+ }
+}
+
+SINGLE_BATTLE_TEST("Desolate Land does not block a move if pokemon is asleep and uses a Water-type move") // Sleep/confusion/paralysis all happen before the check for primal weather
+{
+ GIVEN {
+ PLAYER(SPECIES_GROUDON) {Item(ITEM_RED_ORB);}
+ OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_WATER_GUN); }
+ } SCENE {
+ NOT MESSAGE("The Water-type attack evaporated in the harsh sunlight!");
+ MESSAGE("Foe Wobbuffet is fast asleep.");
+ }
+}
diff --git a/test/ability_download.c b/test/battle/ability/download.c
similarity index 99%
rename from test/ability_download.c
rename to test/battle/ability/download.c
index ee03f6911..5b59e37f0 100644
--- a/test/ability_download.c
+++ b/test/battle/ability/download.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/ability_drizzle.c b/test/battle/ability/drizzle.c
similarity index 96%
rename from test/ability_drizzle.c
rename to test/battle/ability/drizzle.c
index fd0d42616..ce0fc9514 100644
--- a/test/ability_drizzle.c
+++ b/test/battle/ability/drizzle.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Drizzle summons rain", s16 damage)
{
diff --git a/test/ability_dry_skin.c b/test/battle/ability/dry_skin.c
similarity index 99%
rename from test/ability_dry_skin.c
rename to test/battle/ability/dry_skin.c
index 028076d5a..832b7baaf 100644
--- a/test/ability_dry_skin.c
+++ b/test/battle/ability/dry_skin.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Dry Skin causes 1/8th Max HP damage in Sun")
{
diff --git a/test/battle/ability/electromorphosis.c b/test/battle/ability/electromorphosis.c
new file mode 100644
index 000000000..9111fa328
--- /dev/null
+++ b/test/battle/ability/electromorphosis.c
@@ -0,0 +1,56 @@
+#include "global.h"
+#include "test/battle.h"
+
+SINGLE_BATTLE_TEST("Electromorphosis sets up Charge when hit by any move")
+{
+ s16 dmgBefore, dmgAfter;
+ u16 move;
+
+ PARAMETRIZE {move = MOVE_TACKLE; }
+ PARAMETRIZE {move = MOVE_GUST; }
+
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_TACKLE].power != 0);
+ ASSUME(gBattleMoves[MOVE_GUST].power != 0);
+ ASSUME(gBattleMoves[MOVE_GUST].split == SPLIT_SPECIAL);
+ ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
+ ASSUME(gBattleMoves[MOVE_THUNDERBOLT].power != 0);
+ ASSUME(gBattleMoves[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
+
+ PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ELECTROMORPHOSIS); Speed(10); }
+ OPPONENT(SPECIES_WOBBUFFET) {Ability(ABILITY_LIMBER); Speed(5) ;} // Limber, so it doesn't get paralyzed.
+ }
+ WHEN {
+ TURN { MOVE(player, MOVE_THUNDERBOLT), MOVE(opponent, move); }
+ TURN { MOVE(player, MOVE_THUNDERBOLT), MOVE(opponent, move); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player);
+ HP_BAR(opponent, captureDamage: &dmgBefore);
+
+ ANIMATION(ANIM_TYPE_MOVE, move, opponent);
+ HP_BAR(player);
+ ABILITY_POPUP(player, ABILITY_ELECTROMORPHOSIS);
+ if (move == MOVE_TACKLE) {
+ MESSAGE("Being hit by Tackle charged Wobbuffet with power!");
+ }
+ else {
+ MESSAGE("Being hit by Gust charged Wobbuffet with power!");
+ }
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player);
+ HP_BAR(opponent, captureDamage: &dmgAfter);
+
+ ANIMATION(ANIM_TYPE_MOVE, move, opponent);
+ HP_BAR(player);
+ ABILITY_POPUP(player, ABILITY_ELECTROMORPHOSIS);
+ if (move == MOVE_TACKLE) {
+ MESSAGE("Being hit by Tackle charged Wobbuffet with power!");
+ }
+ else {
+ MESSAGE("Being hit by Gust charged Wobbuffet with power!");
+ }
+ }
+ THEN {
+ EXPECT_MUL_EQ(dmgBefore, Q_4_12(2.0), dmgAfter);
+ }
+}
diff --git a/test/ability_flame_body.c b/test/battle/ability/flame_body.c
similarity index 97%
rename from test/ability_flame_body.c
rename to test/battle/ability/flame_body.c
index 303337693..ae9138e12 100644
--- a/test/ability_flame_body.c
+++ b/test/battle/ability/flame_body.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Flame Body inflicts burn on contact")
{
diff --git a/test/ability_flower_gift.c b/test/battle/ability/flower_gift.c
similarity index 99%
rename from test/ability_flower_gift.c
rename to test/battle/ability/flower_gift.c
index 560706af4..fdd191f55 100644
--- a/test/ability_flower_gift.c
+++ b/test/battle/ability/flower_gift.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Flower Gift transforms Cherrim in harsh sunlight")
{
diff --git a/test/ability_forecast.c b/test/battle/ability/forecast.c
similarity index 99%
rename from test/ability_forecast.c
rename to test/battle/ability/forecast.c
index 696c0383a..26ad789da 100644
--- a/test/ability_forecast.c
+++ b/test/battle/ability/forecast.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Forecast transforms Castform in weather from an opponent's move")
{
diff --git a/test/ability_full_metal_body.c b/test/battle/ability/full_metal_body.c
similarity index 98%
rename from test/ability_full_metal_body.c
rename to test/battle/ability/full_metal_body.c
index c47ce9d4d..d00714d52 100644
--- a/test/ability_full_metal_body.c
+++ b/test/battle/ability/full_metal_body.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Full Metal Body prevents intimidate")
{
diff --git a/test/ability_hunger_switch.c b/test/battle/ability/hunger_switch.c
similarity index 96%
rename from test/ability_hunger_switch.c
rename to test/battle/ability/hunger_switch.c
index 336c2c160..c0802d664 100644
--- a/test/ability_hunger_switch.c
+++ b/test/battle/ability/hunger_switch.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Hunger Switch switches Morpeko's forms at the end of the turn")
{
diff --git a/test/ability_hydration.c b/test/battle/ability/hydration.c
similarity index 95%
rename from test/ability_hydration.c
rename to test/battle/ability/hydration.c
index 45689b212..2dca490c0 100644
--- a/test/ability_hydration.c
+++ b/test/battle/ability/hydration.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Hydration cures non-volatile Status conditions if it is raining")
{
diff --git a/test/ability_hyper_cutter.c b/test/battle/ability/hyper_cutter.c
similarity index 98%
rename from test/ability_hyper_cutter.c
rename to test/battle/ability/hyper_cutter.c
index 67dac4c66..ac942039b 100644
--- a/test/ability_hyper_cutter.c
+++ b/test/battle/ability/hyper_cutter.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Hyper Cutter prevents intimidate")
{
diff --git a/test/ability_ice_body.c b/test/battle/ability/ice_body.c
similarity index 96%
rename from test/ability_ice_body.c
rename to test/battle/ability/ice_body.c
index 0658ace81..aca84532a 100644
--- a/test/ability_ice_body.c
+++ b/test/battle/ability/ice_body.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Ice Body prevents damage from hail")
{
diff --git a/test/ability_immunity.c b/test/battle/ability/immunity.c
similarity index 98%
rename from test/ability_immunity.c
rename to test/battle/ability/immunity.c
index fea2eb522..3c6c4afa6 100644
--- a/test/ability_immunity.c
+++ b/test/battle/ability/immunity.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Immunity prevents Poison Sting poison")
{
diff --git a/test/ability_inner_focus.c b/test/battle/ability/inner_focus.c
similarity index 98%
rename from test/ability_inner_focus.c
rename to test/battle/ability/inner_focus.c
index 5b713475d..574c860a7 100644
--- a/test/ability_inner_focus.c
+++ b/test/battle/ability/inner_focus.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Inner Focus prevents intimidate")
{
diff --git a/test/ability_insomnia.c b/test/battle/ability/insomnia.c
similarity index 98%
rename from test/ability_insomnia.c
rename to test/battle/ability/insomnia.c
index 2fcb6a97e..cb22be925 100644
--- a/test/ability_insomnia.c
+++ b/test/battle/ability/insomnia.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Insomnia prevents sleep")
{
diff --git a/test/ability_intimidate.c b/test/battle/ability/intimidate.c
similarity index 99%
rename from test/ability_intimidate.c
rename to test/battle/ability/intimidate.c
index f9681493b..a0a23cc13 100644
--- a/test/ability_intimidate.c
+++ b/test/battle/ability/intimidate.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/ability_leaf_guard.c b/test/battle/ability/leaf_guard.c
similarity index 99%
rename from test/ability_leaf_guard.c
rename to test/battle/ability/leaf_guard.c
index 92727175a..2c052b5a6 100644
--- a/test/ability_leaf_guard.c
+++ b/test/battle/ability/leaf_guard.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Leaf Guard prevents non-volatile status conditions in sun")
{
diff --git a/test/ability_limber.c b/test/battle/ability/limber.c
similarity index 94%
rename from test/ability_limber.c
rename to test/battle/ability/limber.c
index ff701b663..87d4177ff 100644
--- a/test/ability_limber.c
+++ b/test/battle/ability/limber.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Limber prevents paralysis")
{
diff --git a/test/ability_magic_bounce.c b/test/battle/ability/magic_bounce.c
similarity index 99%
rename from test/ability_magic_bounce.c
rename to test/battle/ability/magic_bounce.c
index ab51369ef..348b80f0a 100644
--- a/test/ability_magic_bounce.c
+++ b/test/battle/ability/magic_bounce.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Magic Bounce bounces back status moves")
diff --git a/test/battle/ability/mirror_armor.c b/test/battle/ability/mirror_armor.c
new file mode 100644
index 000000000..2b0a59044
--- /dev/null
+++ b/test/battle/ability/mirror_armor.c
@@ -0,0 +1,202 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ ASSUME(P_GEN_8_POKEMON == TRUE);
+}
+
+SINGLE_BATTLE_TEST("Mirror Armor lowers a stat of the attacking pokemon")
+{
+ u16 move, statId;
+
+ PARAMETRIZE { move = MOVE_LEER; statId = STAT_DEF; }
+ PARAMETRIZE { move = MOVE_GROWL; statId = STAT_ATK; }
+ PARAMETRIZE { move = MOVE_SWEET_SCENT; statId = STAT_EVASION; }
+ PARAMETRIZE { move = MOVE_SAND_ATTACK; statId = STAT_ACC; }
+ PARAMETRIZE { move = MOVE_CONFIDE; statId = STAT_SPATK; }
+ PARAMETRIZE { move = MOVE_FAKE_TEARS; statId = STAT_SPDEF; }
+
+ GIVEN {
+ PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR);}
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(opponent, move); }
+ } SCENE {
+ ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ switch (statId)
+ {
+ case STAT_DEF:
+ MESSAGE("Foe Wynaut's Defense fell!");
+ break;
+ case STAT_ATK:
+ MESSAGE("Foe Wynaut's Attack fell!");
+ break;
+ case STAT_EVASION:
+ MESSAGE("Foe Wynaut's evasiveness harshly fell!");
+ break;
+ case STAT_ACC:
+ MESSAGE("Foe Wynaut's accuracy fell!");
+ break;
+ case STAT_SPATK:
+ MESSAGE("Foe Wynaut's Sp. Atk fell!");
+ break;
+ case STAT_SPDEF:
+ MESSAGE("Foe Wynaut's Sp. Def harshly fell!");
+ break;
+ }
+ } THEN {
+ EXPECT_EQ(player->statStages[statId], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponent->statStages[statId], (statId == STAT_SPDEF || statId == STAT_EVASION) ? DEFAULT_STAT_STAGE - 2 : DEFAULT_STAT_STAGE - 1);
+ }
+}
+
+SINGLE_BATTLE_TEST("Mirror Armor triggers even if the attacking Pokemon also has Mirror Armor ability")
+{
+ GIVEN {
+ PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
+ OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_LEER); }
+ } SCENE {
+ MESSAGE("Foe Corviknigh used Leer!");
+ ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
+ NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ MESSAGE("Foe Corviknigh's Defense fell!");
+ } THEN {
+ EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1);
+ }
+}
+
+SINGLE_BATTLE_TEST("Mirror Armor doesn't lower the stats of an attacking Pokemon with the Clear Body ability")
+{
+ GIVEN {
+ PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
+ OPPONENT(SPECIES_WYNAUT) { Ability(ABILITY_CLEAR_BODY); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_LEER); }
+ } SCENE {
+ MESSAGE("Foe Wynaut used Leer!");
+ ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
+ ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
+ MESSAGE("Foe Wynaut's Clear Body prevents stat loss!");
+ } THEN {
+ EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
+ }
+}
+
+SINGLE_BATTLE_TEST("Mirror Armor lowers the Attack of Pokemon with Intimidate")
+{
+ GIVEN {
+ PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
+ OPPONENT(SPECIES_GYARADOS) { Ability(ABILITY_INTIMIDATE); }
+ } WHEN {
+ TURN {}
+ } SCENE {
+ ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
+ ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ MESSAGE("Foe Gyarados's Attack fell!");
+ } THEN {
+ EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1);
+ }
+}
+
+// Unsure whether this should or should not fail, as Showdown has conflicting information. Needs testing in gen8 games.
+SINGLE_BATTLE_TEST("Mirror Armor doesn't lower the stats of an attacking Pokemon behind Substitute")
+{
+ KNOWN_FAILING;
+ GIVEN {
+ PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_SUBSTITUTE); }
+ TURN { MOVE(opponent, MOVE_LEER); }
+ } SCENE {
+ MESSAGE("Foe Wynaut used Substitute!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent);
+ MESSAGE("Foe Wynaut used Leer!");
+ ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
+ NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ } THEN {
+ EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
+ }
+}
+
+SINGLE_BATTLE_TEST("Mirror Armor raises the stat of an attacking Pokemon with Contrary")
+{
+ GIVEN {
+ PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR);}
+ OPPONENT(SPECIES_SHUCKLE) {Ability(ABILITY_CONTRARY);}
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_LEER); }
+ } SCENE {
+ MESSAGE("Foe Shuckle used Leer!");
+ ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ MESSAGE("Foe Shuckle's Defense rose!");
+ } THEN {
+ EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
+ }
+}
+
+SINGLE_BATTLE_TEST("Mirror Armor doesn't lower the stat of the attacking Pokemon if it is already at -6")
+{
+ GIVEN {
+ PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR);}
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(player, MOVE_SCREECH); }
+ TURN { MOVE(player, MOVE_SCREECH); }
+ TURN { MOVE(player, MOVE_SCREECH); }
+ TURN { MOVE(opponent, MOVE_LEER); }
+ } SCENE {
+ MESSAGE("Corviknigh used Screech!");
+ MESSAGE("Corviknigh used Screech!");
+ MESSAGE("Corviknigh used Screech!");
+ MESSAGE("Foe Wynaut used Leer!");
+ ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
+ NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ MESSAGE("Foe Wynaut's Defense won't go lower!");
+ } THEN {
+ EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponent->statStages[STAT_DEF], MIN_STAT_STAGE);
+ }
+}
+
+// This behaviour needs to be verified in the actual games. Currently it's written to follow Showdown's logic.
+DOUBLE_BATTLE_TEST("Mirror Armor lowers Speed of the partner Pokemon after Court Change was used by the opponent after it set up Sticky Web")
+{
+ KNOWN_FAILING;
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_STICKY_WEB].effect == EFFECT_STICKY_WEB);
+ ASSUME(gBattleMoves[MOVE_COURT_CHANGE].effect == EFFECT_COURT_CHANGE);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR); Item(ITEM_IRON_BALL); }
+ OPPONENT(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_STICKY_WEB); }
+ TURN { MOVE(opponentLeft, MOVE_COURT_CHANGE); }
+ TURN { SWITCH(playerRight, 2);}
+ TURN { }
+ } SCENE {
+ MESSAGE("Wobbuffet used Sticky Web!");
+ MESSAGE("Foe Wynaut used Court Change!");
+ MESSAGE("Foe Wynaut swapped the battle effects affecting each side!");
+ MESSAGE("Go! Corviknigh!");
+ MESSAGE("Corviknigh was caught in a Sticky Web!");
+ ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
+ MESSAGE("Wobbuffet's Speed fell!");
+ }
+}
diff --git a/test/ability_oblivious.c b/test/battle/ability/oblivious.c
similarity index 98%
rename from test/ability_oblivious.c
rename to test/battle/ability/oblivious.c
index 9d9aeeb29..51d27bb16 100644
--- a/test/ability_oblivious.c
+++ b/test/battle/ability/oblivious.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Oblivious prevents Infatuation")
{
diff --git a/test/battle/ability/overcoat.c b/test/battle/ability/overcoat.c
new file mode 100644
index 000000000..5f2405167
--- /dev/null
+++ b/test/battle/ability/overcoat.c
@@ -0,0 +1,21 @@
+#include "global.h"
+#include "test/battle.h"
+
+SINGLE_BATTLE_TEST("Overcoat blocks powder and spore moves")
+{
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER);
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_PINECO) { Ability(ABILITY_OVERCOAT); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_STUN_SPORE); }
+ } SCENE {
+ ABILITY_POPUP(opponent, ABILITY_OVERCOAT);
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
+ MESSAGE("It doesn't affect Foe Pineco…");
+ }
+}
+
+TO_DO_BATTLE_TEST("Overcoat blocks damage from hail");
+TO_DO_BATTLE_TEST("Overcoat blocks damage from sandstorm");
+TO_DO_BATTLE_TEST("Overcoat blocks Effect Spore's effect");
diff --git a/test/ability_overgrow.c b/test/battle/ability/overgrow.c
similarity index 96%
rename from test/ability_overgrow.c
rename to test/battle/ability/overgrow.c
index 82d8dd467..548dc9bb3 100644
--- a/test/ability_overgrow.c
+++ b/test/battle/ability/overgrow.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Overgrow boosts Grass-type moves in a pinch", s16 damage)
{
diff --git a/test/ability_own_tempo.c b/test/battle/ability/own_tempo.c
similarity index 99%
rename from test/ability_own_tempo.c
rename to test/battle/ability/own_tempo.c
index 2440ac779..4c6a3167f 100644
--- a/test/ability_own_tempo.c
+++ b/test/battle/ability/own_tempo.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Own Tempo prevents intimidate")
{
diff --git a/test/ability_pastel_veil.c b/test/battle/ability/pastel_veil.c
similarity index 99%
rename from test/ability_pastel_veil.c
rename to test/battle/ability/pastel_veil.c
index 363c018c1..74d764fc3 100644
--- a/test/ability_pastel_veil.c
+++ b/test/battle/ability/pastel_veil.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Pastel Veil prevents Poison Sting poison")
{
diff --git a/test/ability_poison_point.c b/test/battle/ability/poison_point.c
similarity index 97%
rename from test/ability_poison_point.c
rename to test/battle/ability/poison_point.c
index a85edd85b..63690d84d 100644
--- a/test/ability_poison_point.c
+++ b/test/battle/ability/poison_point.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Poison Point inflicts poison on contact")
{
diff --git a/test/battle/ability/primordial_sea.c b/test/battle/ability/primordial_sea.c
new file mode 100644
index 000000000..37fbb1b23
--- /dev/null
+++ b/test/battle/ability/primordial_sea.c
@@ -0,0 +1,66 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ ASSUME(gBattleMoves[MOVE_EMBER].power != 0);
+ ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE);
+}
+
+SINGLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves")
+{
+ GIVEN {
+ PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);}
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_EMBER); }
+ TURN { MOVE(opponent, MOVE_EMBER); }
+ } SCENE {
+ MESSAGE("Foe Wobbuffet used Ember!");
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
+ MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
+ NOT HP_BAR(player);
+ MESSAGE("Foe Wobbuffet used Ember!");
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
+ MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
+ NOT HP_BAR(player);
+ } THEN {
+ EXPECT_EQ(player->hp, player->maxHP);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves and prints the message only once with moves hitting multiple targets")
+{
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_ERUPTION].power != 0);
+ ASSUME(gBattleMoves[MOVE_ERUPTION].type == TYPE_FIRE);
+ ASSUME(gBattleMoves[MOVE_ERUPTION].target == MOVE_TARGET_BOTH);
+ PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB); {Speed(5);}}
+ PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
+ OPPONENT(SPECIES_WOBBUFFET) {Speed(10);}
+ OPPONENT(SPECIES_WOBBUFFET) {Speed(8);}
+ } WHEN {
+ TURN { MOVE(opponentLeft, MOVE_ERUPTION); }
+ } SCENE {
+ MESSAGE("Foe Wobbuffet used Eruption!");
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ERUPTION, opponentLeft);
+ MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
+ NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
+ } THEN {
+ EXPECT_EQ(playerLeft->hp, playerLeft->maxHP);
+ EXPECT_EQ(playerRight->hp, playerRight->maxHP);
+ }
+}
+
+SINGLE_BATTLE_TEST("Primordial Sea does not block a move if pokemon is asleep and uses a Fire-type move") // Sleep/confusion/paralysis all happen before the check for primal weather
+{
+ GIVEN {
+ PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);}
+ OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_EMBER); }
+ } SCENE {
+ NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
+ MESSAGE("Foe Wobbuffet is fast asleep.");
+ }
+}
\ No newline at end of file
diff --git a/test/ability_protosynthesis.c b/test/battle/ability/protosynthesis.c
similarity index 99%
rename from test/ability_protosynthesis.c
rename to test/battle/ability/protosynthesis.c
index 9f794e00a..0ed74d4f6 100644
--- a/test/ability_protosynthesis.c
+++ b/test/battle/ability/protosynthesis.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/ability_quark_drive.c b/test/battle/ability/quark_drive.c
similarity index 99%
rename from test/ability_quark_drive.c
rename to test/battle/ability/quark_drive.c
index b004c760c..bce6795ca 100644
--- a/test/ability_quark_drive.c
+++ b/test/battle/ability/quark_drive.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/ability_rain_dish.c b/test/battle/ability/rain_dish.c
similarity index 94%
rename from test/ability_rain_dish.c
rename to test/battle/ability/rain_dish.c
index 04009507c..dd647f0eb 100644
--- a/test/ability_rain_dish.c
+++ b/test/battle/ability/rain_dish.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Rain Dish recovers 1/16th of Max HP in Rain")
{
diff --git a/test/battle/ability/rattled.c b/test/battle/ability/rattled.c
new file mode 100644
index 000000000..e936ed7de
--- /dev/null
+++ b/test/battle/ability/rattled.c
@@ -0,0 +1,93 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ ASSUME(gBattleMoves[MOVE_FURY_CUTTER].type == TYPE_BUG);
+ ASSUME(gBattleMoves[MOVE_FURY_CUTTER].power != 0);
+ ASSUME(gBattleMoves[MOVE_FEINT_ATTACK].type == TYPE_DARK);
+ ASSUME(gBattleMoves[MOVE_FEINT_ATTACK].power != 0);
+ ASSUME(gBattleMoves[MOVE_SHADOW_PUNCH].type == TYPE_GHOST);
+ ASSUME(gBattleMoves[MOVE_SHADOW_PUNCH].power != 0);
+ ASSUME(gBattleMoves[MOVE_TACKLE].type == TYPE_NORMAL);
+ ASSUME(gBattleMoves[MOVE_TACKLE].power != 0);
+}
+
+SINGLE_BATTLE_TEST("Rattled boosts speed by 1 when hit by Bug, Dark or Ghost type move")
+{
+ u16 move;
+ PARAMETRIZE { move = MOVE_FURY_CUTTER; }
+ PARAMETRIZE { move = MOVE_FEINT_ATTACK; }
+ PARAMETRIZE { move = MOVE_SHADOW_PUNCH; }
+ PARAMETRIZE { move = MOVE_TACKLE; }
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) {Speed(42) ;}
+ OPPONENT(SPECIES_SUDOWOODO) {Speed(40); Ability(ABILITY_RATTLED);}
+ } WHEN {
+ TURN { MOVE(player, move); }
+ TURN { MOVE(player, move); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, move, player);
+ HP_BAR(opponent);
+ if (move != MOVE_TACKLE) {
+ ABILITY_POPUP(opponent, ABILITY_RATTLED);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ MESSAGE("Foe Sudowoodo's Speed rose!");
+ }
+ MESSAGE("Foe Sudowoodo used Celebrate!");
+ // Sudowoodo is now faster
+ if (move != MOVE_TACKLE){
+ MESSAGE("Foe Sudowoodo used Celebrate!");
+ ANIMATION(ANIM_TYPE_MOVE, move, player);
+ HP_BAR(opponent);
+ ABILITY_POPUP(opponent, ABILITY_RATTLED);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ MESSAGE("Foe Sudowoodo's Speed rose!");
+ }
+ else {
+ ANIMATION(ANIM_TYPE_MOVE, move, player);
+ HP_BAR(opponent);
+ MESSAGE("Foe Sudowoodo used Celebrate!");
+ }
+ }
+}
+
+SINGLE_BATTLE_TEST("Rattled boosts speed by 1 when affected by Intimidate")
+{
+ GIVEN {
+ ASSUME(B_UPDATED_INTIMIDATE >= GEN_8);
+ PLAYER(SPECIES_GYARADOS) {Ability(ABILITY_INTIMIDATE); }
+ OPPONENT(SPECIES_SUDOWOODO) {Ability(ABILITY_RATTLED); }
+ } WHEN {
+ TURN {}
+ } SCENE {
+ ABILITY_POPUP(player, ABILITY_INTIMIDATE);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ MESSAGE("Gyarados's Intimidate cuts Foe Sudowoodo's attack!");
+ ABILITY_POPUP(opponent, ABILITY_RATTLED);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ MESSAGE("Foe Sudowoodo's Speed rose!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Rattled triggers correctly when hit by U-Turn") // Specific test here, because of #3124
+{
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE);
+ ASSUME(gBattleMoves[MOVE_U_TURN].type == TYPE_BUG);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_SUDOWOODO) {Ability(ABILITY_RATTLED); }
+ OPPONENT(SPECIES_SUDOWOODO);
+ } WHEN {
+ TURN { MOVE(player, MOVE_U_TURN); SEND_OUT(player, 1); }
+ } SCENE {
+ MESSAGE("Wobbuffet used U-turn!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player);
+ HP_BAR(opponent);
+ ABILITY_POPUP(opponent, ABILITY_RATTLED);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ MESSAGE("Foe Sudowoodo's Speed rose!");
+ MESSAGE("Go! Wynaut!");
+ }
+}
diff --git a/test/ability_sand_veil.c b/test/battle/ability/sand_veil.c
similarity index 96%
rename from test/ability_sand_veil.c
rename to test/battle/ability/sand_veil.c
index 50abad51c..c2f6440c3 100644
--- a/test/ability_sand_veil.c
+++ b/test/battle/ability/sand_veil.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Sand Veil prevents damage from sandstorm")
{
diff --git a/test/ability_schooling.c b/test/battle/ability/schooling.c
similarity index 99%
rename from test/ability_schooling.c
rename to test/battle/ability/schooling.c
index 87f69ea31..448e37261 100644
--- a/test/ability_schooling.c
+++ b/test/battle/ability/schooling.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Schooling switches Level 20+ Wishiwashi's form when HP is 25-percent or less at the end of the turn")
{
diff --git a/test/ability_scrappy.c b/test/battle/ability/scrappy.c
similarity index 98%
rename from test/ability_scrappy.c
rename to test/battle/ability/scrappy.c
index b4f83afcf..df4ededa4 100644
--- a/test/ability_scrappy.c
+++ b/test/battle/ability/scrappy.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Scrappy prevents intimidate")
{
diff --git a/test/ability_snow_cloak.c b/test/battle/ability/snow_cloak.c
similarity index 96%
rename from test/ability_snow_cloak.c
rename to test/battle/ability/snow_cloak.c
index 8a95a8ea0..88fe6e509 100644
--- a/test/ability_snow_cloak.c
+++ b/test/battle/ability/snow_cloak.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Snow Cloak prevents damage from hail")
{
diff --git a/test/ability_snow_warning.c b/test/battle/ability/snow_warning.c
similarity index 96%
rename from test/ability_snow_warning.c
rename to test/battle/ability/snow_warning.c
index 5e8b13d54..17f18814b 100644
--- a/test/ability_snow_warning.c
+++ b/test/battle/ability/snow_warning.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
#if B_SNOW_WARNING < GEN_9
SINGLE_BATTLE_TEST("Snow Warning summons hail")
diff --git a/test/ability_speed_boost.c b/test/battle/ability/speed_boost.c
similarity index 96%
rename from test/ability_speed_boost.c
rename to test/battle/ability/speed_boost.c
index 78aa876e6..0cc82e806 100644
--- a/test/ability_speed_boost.c
+++ b/test/battle/ability/speed_boost.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Speed Boost gradually boosts Speed")
{
diff --git a/test/battle/ability/stamina.c b/test/battle/ability/stamina.c
new file mode 100644
index 000000000..3470cce51
--- /dev/null
+++ b/test/battle/ability/stamina.c
@@ -0,0 +1,89 @@
+#include "global.h"
+#include "test/battle.h"
+
+#define STAMINA_STAT_RAISE(target, msg) \
+{ \
+ ABILITY_POPUP(target, ABILITY_STAMINA); \
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, target); \
+ MESSAGE(msg); \
+}
+
+#define STAMINA_HIT(attacker, target, move, msg, dmgVar) \
+{ \
+ ANIMATION(ANIM_TYPE_MOVE, move, attacker); \
+ HP_BAR(target, captureDamage: &dmgVar); \
+ STAMINA_STAT_RAISE(target, msg); \
+}
+
+SINGLE_BATTLE_TEST("Stamina raises Defense by 1 when hit by a move")
+{
+ s16 turnOneHit, turnTwoHit;
+ u16 move;
+
+ PARAMETRIZE {move = MOVE_TACKLE; }
+ PARAMETRIZE {move = MOVE_GUST; }
+
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_TACKLE].power != 0);
+ ASSUME(gBattleMoves[MOVE_GUST].power != 0);
+ ASSUME(gBattleMoves[MOVE_GUST].split == SPLIT_SPECIAL);
+ ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
+ PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_STAMINA); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, move); }
+ TURN { MOVE(opponent, move); }
+ } SCENE {
+ STAMINA_HIT(opponent, player, move, "Wobbuffet's Defense rose!", turnOneHit);
+ STAMINA_HIT(opponent, player, move, "Wobbuffet's Defense rose!", turnTwoHit);
+ }
+ THEN {
+ if (move == MOVE_TACKLE) {
+ EXPECT_MUL_EQ(turnTwoHit, Q_4_12(1.5), turnOneHit);
+ }
+ else {
+ EXPECT_EQ(turnTwoHit, turnOneHit);
+ }
+ }
+}
+
+DOUBLE_BATTLE_TEST("Stamina activates correctly for every battler with the ability when hit by a multi target move")
+{
+ u16 abilityLeft, abilityRight;
+
+ PARAMETRIZE {abilityLeft = ABILITY_NONE, abilityRight = ABILITY_STAMINA; }
+ PARAMETRIZE {abilityLeft = ABILITY_STAMINA, abilityRight = ABILITY_NONE; }
+ PARAMETRIZE {abilityLeft = ABILITY_STAMINA, abilityRight = ABILITY_STAMINA; }
+
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
+ PLAYER(SPECIES_WOBBUFFET) { Ability(abilityLeft); Speed(10); }
+ PLAYER(SPECIES_WOBBUFFET) { Ability(abilityRight); Speed(5); }
+ OPPONENT(SPECIES_WOBBUFFET) {Speed(20); }
+ OPPONENT(SPECIES_WOBBUFFET) {Speed(15); }
+ } WHEN {
+ TURN { MOVE(opponentLeft, MOVE_EARTHQUAKE);}
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, opponentLeft);
+
+ HP_BAR(playerLeft);
+ if (abilityLeft == ABILITY_STAMINA) {
+ STAMINA_STAT_RAISE(playerLeft, "Wobbuffet's Defense rose!");
+ }
+ NOT HP_BAR(opponentLeft); // We need to check the attacker itself does NOT get damaged. There was an issue when the targets would get overwritten by the Stamina's stat raise.
+
+ HP_BAR(playerRight);
+ if (abilityRight == ABILITY_STAMINA) {
+ STAMINA_STAT_RAISE(playerRight, "Wobbuffet's Defense rose!");
+ }
+ NOT HP_BAR(opponentLeft); // We need to check the attacker itself does NOT get damaged. There was an issue when the targets would get overwritten by the Stamina's stat raise.
+
+ HP_BAR(opponentRight);
+ }
+ THEN {
+ EXPECT_NE(playerLeft->hp, playerLeft->maxHP);
+ EXPECT_NE(playerRight->hp, playerRight->maxHP);
+ EXPECT_NE(opponentRight->hp, opponentRight->maxHP);
+ EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP);
+ }
+}
diff --git a/test/ability_static.c b/test/battle/ability/static.c
similarity index 97%
rename from test/ability_static.c
rename to test/battle/ability/static.c
index 022efa5bb..eb52a838b 100644
--- a/test/ability_static.c
+++ b/test/battle/ability/static.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Static inflicts paralysis on contact")
{
diff --git a/test/ability_stench.c b/test/battle/ability/stench.c
similarity index 97%
rename from test/ability_stench.c
rename to test/battle/ability/stench.c
index dd1d027e9..a91c861fc 100644
--- a/test/ability_stench.c
+++ b/test/battle/ability/stench.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Stench has a 10% chance to flinch")
{
diff --git a/test/ability_sturdy.c b/test/battle/ability/sturdy.c
similarity index 98%
rename from test/ability_sturdy.c
rename to test/battle/ability/sturdy.c
index 42cdab11f..7a7fdf39f 100644
--- a/test/ability_sturdy.c
+++ b/test/battle/ability/sturdy.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Sturdy prevents OHKO moves")
{
diff --git a/test/ability_swarm.c b/test/battle/ability/swarm.c
similarity index 95%
rename from test/ability_swarm.c
rename to test/battle/ability/swarm.c
index 7709e976e..c6479412a 100644
--- a/test/ability_swarm.c
+++ b/test/battle/ability/swarm.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Swarm boosts Bug-type moves in a pinch", s16 damage)
{
diff --git a/test/ability_sword_of_ruin.c b/test/battle/ability/sword_of_ruin.c
similarity index 98%
rename from test/ability_sword_of_ruin.c
rename to test/battle/ability/sword_of_ruin.c
index 6ec4f8fe8..2abc0c04f 100644
--- a/test/ability_sword_of_ruin.c
+++ b/test/battle/ability/sword_of_ruin.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/ability_tablets_of_ruin.c b/test/battle/ability/tablets_of_ruin.c
similarity index 98%
rename from test/ability_tablets_of_ruin.c
rename to test/battle/ability/tablets_of_ruin.c
index 38dcfc856..ca12b6813 100644
--- a/test/ability_tablets_of_ruin.c
+++ b/test/battle/ability/tablets_of_ruin.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/ability_torrent.c b/test/battle/ability/torrent.c
similarity index 95%
rename from test/ability_torrent.c
rename to test/battle/ability/torrent.c
index e5c701775..673e21b79 100644
--- a/test/ability_torrent.c
+++ b/test/battle/ability/torrent.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Torrent boosts Water-type moves in a pinch", s16 damage)
{
diff --git a/test/ability_vessel_of_ruin.c b/test/battle/ability/vessel_of_ruin.c
similarity index 98%
rename from test/ability_vessel_of_ruin.c
rename to test/battle/ability/vessel_of_ruin.c
index 6c7b2664f..4f8408795 100644
--- a/test/ability_vessel_of_ruin.c
+++ b/test/battle/ability/vessel_of_ruin.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/ability_volt_absorb.c b/test/battle/ability/volt_absorb.c
similarity index 99%
rename from test/ability_volt_absorb.c
rename to test/battle/ability/volt_absorb.c
index f9af7e17b..2f69a287b 100644
--- a/test/ability_volt_absorb.c
+++ b/test/battle/ability/volt_absorb.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Volt Absorb heals 25% when hit by electric type moves")
{
diff --git a/test/ability_water_absorb.c b/test/battle/ability/water_absorb.c
similarity index 99%
rename from test/ability_water_absorb.c
rename to test/battle/ability/water_absorb.c
index 50547d13d..cda319603 100644
--- a/test/ability_water_absorb.c
+++ b/test/battle/ability/water_absorb.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Water Absorb heals 25% when hit by water type moves")
{
diff --git a/test/ability_white_smoke.c b/test/battle/ability/white_smoke.c
similarity index 98%
rename from test/ability_white_smoke.c
rename to test/battle/ability/white_smoke.c
index 9689aee6c..7bedbca24 100644
--- a/test/ability_white_smoke.c
+++ b/test/battle/ability/white_smoke.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("White Smoke prevents intimidate")
{
diff --git a/test/battle/ability/wind_power.c b/test/battle/ability/wind_power.c
new file mode 100644
index 000000000..996910640
--- /dev/null
+++ b/test/battle/ability/wind_power.c
@@ -0,0 +1,221 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ ASSUME(gBattleMoves[MOVE_THUNDERBOLT].power != 0);
+ ASSUME(gBattleMoves[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
+ ASSUME(gBattleMoves[MOVE_TACKLE].power != 0);
+ ASSUME(gBattleMoves[MOVE_AIR_CUTTER].power != 0);
+ ASSUME(gBattleMoves[MOVE_AIR_CUTTER].target == MOVE_TARGET_BOTH);
+ ASSUME(gBattleMoves[MOVE_AIR_CUTTER].flags & FLAG_WIND_MOVE);
+ ASSUME(gBattleMoves[MOVE_PETAL_BLIZZARD].power != 0);
+ ASSUME(gBattleMoves[MOVE_PETAL_BLIZZARD].target == MOVE_TARGET_FOES_AND_ALLY);
+ ASSUME(gBattleMoves[MOVE_PETAL_BLIZZARD].flags & FLAG_WIND_MOVE);
+ ASSUME(!(gBattleMoves[MOVE_TACKLE].flags & FLAG_WIND_MOVE));
+}
+
+SINGLE_BATTLE_TEST("Wind Power sets up Charge for player when hit by a wind move")
+{
+ s16 dmgBefore, dmgAfter;
+ u16 move;
+
+ PARAMETRIZE {move = MOVE_TACKLE; }
+ PARAMETRIZE {move = MOVE_AIR_CUTTER; }
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(10); }
+ OPPONENT(SPECIES_WOBBUFFET) {Ability(ABILITY_LIMBER); Speed(5) ;} // Limber, so it doesn't get paralyzed.
+ } WHEN {
+ TURN { MOVE(player, MOVE_THUNDERBOLT), MOVE(opponent, move); }
+ TURN { MOVE(player, MOVE_THUNDERBOLT), MOVE(opponent, move); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player);
+ HP_BAR(opponent, captureDamage: &dmgBefore);
+
+ ANIMATION(ANIM_TYPE_MOVE, move, opponent);
+ HP_BAR(player);
+ if (move == MOVE_AIR_CUTTER) {
+ ABILITY_POPUP(player, ABILITY_WIND_POWER);
+ MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!");
+ }
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, player);
+ HP_BAR(opponent, captureDamage: &dmgAfter);
+
+ ANIMATION(ANIM_TYPE_MOVE, move, opponent);
+ HP_BAR(player);
+ if (move == MOVE_AIR_CUTTER) {
+ ABILITY_POPUP(player, ABILITY_WIND_POWER);
+ MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!");
+ }
+ }
+ THEN {
+ if (move == MOVE_AIR_CUTTER) {
+ EXPECT_MUL_EQ(dmgBefore, Q_4_12(2.0), dmgAfter);
+ }
+ else {
+ EXPECT_EQ(dmgAfter, dmgBefore);
+ }
+ }
+}
+
+SINGLE_BATTLE_TEST("Wind Power sets up Charge for opponent when hit by a wind move")
+{
+ s16 dmgBefore, dmgAfter;
+ u16 move;
+
+ PARAMETRIZE {move = MOVE_TACKLE; }
+ PARAMETRIZE {move = MOVE_AIR_CUTTER; }
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) {Ability(ABILITY_LIMBER); Speed(5) ;} // Limber, so it doesn't get paralyzed.
+ OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(10); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_THUNDERBOLT), MOVE(player, move); }
+ TURN { MOVE(opponent, MOVE_THUNDERBOLT), MOVE(player, move); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, opponent);
+ HP_BAR(player, captureDamage: &dmgBefore);
+
+ ANIMATION(ANIM_TYPE_MOVE, move, player);
+ HP_BAR(opponent);
+ if (move == MOVE_AIR_CUTTER) {
+ ABILITY_POPUP(opponent, ABILITY_WIND_POWER);
+ MESSAGE("Being hit by Air Cutter charged Foe Wobbuffet with power!");
+ }
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDERBOLT, opponent);
+ HP_BAR(player, captureDamage: &dmgAfter);
+
+ ANIMATION(ANIM_TYPE_MOVE, move, player);
+ HP_BAR(opponent);
+ if (move == MOVE_AIR_CUTTER) {
+ ABILITY_POPUP(opponent, ABILITY_WIND_POWER);
+ MESSAGE("Being hit by Air Cutter charged Foe Wobbuffet with power!");
+ }
+ }
+ THEN {
+ if (move == MOVE_AIR_CUTTER) {
+ EXPECT_MUL_EQ(dmgBefore, Q_4_12(2.0), dmgAfter);
+ }
+ else {
+ EXPECT_EQ(dmgAfter, dmgBefore);
+ }
+ }
+}
+
+DOUBLE_BATTLE_TEST("Wind Power activates correctly for every battler with the ability when hit by a 2/3 target move")
+{
+ u16 move, abilityLeft, abilityRight;
+
+ PARAMETRIZE {abilityLeft = ABILITY_NONE, abilityRight = ABILITY_WIND_POWER;}
+ PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_NONE; }
+ PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_WIND_POWER; }
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Ability(abilityLeft); Speed(10); }
+ PLAYER(SPECIES_WOBBUFFET) { Ability(abilityRight); Speed(5); }
+ OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(20); }
+ OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(15); }
+ } WHEN {
+ TURN { MOVE(opponentLeft, MOVE_AIR_CUTTER); MOVE(opponentRight, MOVE_AIR_CUTTER);}
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_AIR_CUTTER, opponentLeft);
+
+ HP_BAR(playerLeft);
+ if (abilityLeft == ABILITY_WIND_POWER) {
+ ABILITY_POPUP(playerLeft, ABILITY_WIND_POWER);
+ MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!");
+ }
+ HP_BAR(playerRight);
+ if (abilityRight == ABILITY_WIND_POWER) {
+ ABILITY_POPUP(playerRight, ABILITY_WIND_POWER);
+ MESSAGE("Being hit by Air Cutter charged Wobbuffet with power!");
+ }
+ NOT HP_BAR(opponentLeft);
+ NOT HP_BAR(opponentRight);
+ }
+ THEN {
+ EXPECT_NE(playerLeft->hp, playerLeft->maxHP);
+ EXPECT_NE(playerRight->hp, playerRight->maxHP);
+ EXPECT_EQ(opponentRight->hp, opponentRight->maxHP);
+ EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Wind Power activates correctly for every battler with the ability when hit by a 3 target move")
+{
+ u16 abilityLeft, abilityRight;
+
+ PARAMETRIZE {abilityLeft = ABILITY_NONE, abilityRight = ABILITY_WIND_POWER; }
+ PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_NONE; }
+ PARAMETRIZE {abilityLeft = ABILITY_WIND_POWER, abilityRight = ABILITY_WIND_POWER; }
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Ability(abilityLeft); Speed(10); }
+ PLAYER(SPECIES_WOBBUFFET) { Ability(abilityRight); Speed(5); }
+ OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(20); }
+ OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_LIMBER); Speed(15); }
+ } WHEN {
+ TURN { MOVE(opponentLeft, MOVE_PETAL_BLIZZARD);}
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PETAL_BLIZZARD, opponentLeft);
+
+ HP_BAR(playerLeft);
+ if (abilityLeft == ABILITY_WIND_POWER) {
+ ABILITY_POPUP(playerLeft, ABILITY_WIND_POWER);
+ MESSAGE("Being hit by PetalBlizzrd charged Wobbuffet with power!");
+ }
+ HP_BAR(playerRight);
+ if (abilityRight == ABILITY_WIND_POWER) {
+ ABILITY_POPUP(playerRight, ABILITY_WIND_POWER);
+ MESSAGE("Being hit by PetalBlizzrd charged Wobbuffet with power!");
+ }
+ HP_BAR(opponentRight);
+ NOT HP_BAR(opponentLeft);
+ }
+ THEN {
+ EXPECT_NE(playerLeft->hp, playerLeft->maxHP);
+ EXPECT_NE(playerRight->hp, playerRight->maxHP);
+ EXPECT_NE(opponentRight->hp, opponentRight->maxHP);
+ EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Wind Power activates correctly when Tailwind is used")
+{
+ bool8 opponentSide;
+
+ PARAMETRIZE {opponentSide = TRUE;}
+ PARAMETRIZE {opponentSide = FALSE;}
+
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_TAILWIND].effect == EFFECT_TAILWIND);
+ PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(10); }
+ PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(5); }
+ OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(20); }
+ OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_WIND_POWER); Speed(15); }
+ } WHEN {
+ TURN { MOVE((opponentSide == TRUE) ? opponentLeft : playerLeft, MOVE_TAILWIND);}
+ } SCENE {
+ if (opponentSide) {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, opponentLeft);
+
+ ABILITY_POPUP(opponentLeft, ABILITY_WIND_POWER);
+ MESSAGE("Being hit by Tailwind charged Foe Wobbuffet with power!");
+
+ ABILITY_POPUP(opponentRight, ABILITY_WIND_POWER);
+ MESSAGE("Being hit by Tailwind charged Foe Wobbuffet with power!");
+ }
+ else {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TAILWIND, playerLeft);
+
+ ABILITY_POPUP(playerLeft, ABILITY_WIND_POWER);
+ MESSAGE("Being hit by Tailwind charged Wobbuffet with power!");
+
+ ABILITY_POPUP(playerRight, ABILITY_WIND_POWER);
+ MESSAGE("Being hit by Tailwind charged Wobbuffet with power!");
+ }
+ }
+}
diff --git a/test/ability_zen_mode.c b/test/battle/ability/zen_mode.c
similarity index 99%
rename from test/ability_zen_mode.c
rename to test/battle/ability/zen_mode.c
index e0e72471f..37bb4160f 100644
--- a/test/ability_zen_mode.c
+++ b/test/battle/ability/zen_mode.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Zen Mode switches Darmanitan's form when HP is half or less at the end of the turn")
{
diff --git a/test/battle/form_change/battle_switch.c b/test/battle/form_change/battle_switch.c
new file mode 100644
index 000000000..bd18e0ccf
--- /dev/null
+++ b/test/battle/form_change/battle_switch.c
@@ -0,0 +1,23 @@
+#include "global.h"
+#include "test/battle.h"
+
+SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon switching out")
+{
+ GIVEN {
+ ASSUME(P_GEN_6_POKEMON == TRUE);
+ PLAYER(SPECIES_AEGISLASH);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_TACKLE); }
+ TURN { SWITCH(player, 1); }
+ TURN { SWITCH(player, 0); }
+ } SCENE {
+ ABILITY_POPUP(player, ABILITY_STANCE_CHANGE);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
+ MESSAGE("Aegislash used Tackle!");
+ MESSAGE("Foe Wobbuffet used Celebrate!");
+ } THEN {
+ EXPECT_EQ(player->species, SPECIES_AEGISLASH);
+ }
+}
diff --git a/test/form_change.c b/test/battle/form_change/begin_battle.c
similarity index 68%
rename from test/form_change.c
rename to test/battle/form_change/begin_battle.c
index 43497101c..328db756d 100644
--- a/test/form_change.c
+++ b/test/battle/form_change/begin_battle.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Xerneas changes into Active Form upon battle start")
{
@@ -79,41 +79,3 @@ SINGLE_BATTLE_TEST("Zamazenta's Iron Head becomes Behemoth Bash upon form change
EXPECT_EQ(player->moves[0], MOVE_BEHEMOTH_BASH);
}
}
-
-SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon switching out")
-{
- GIVEN {
- ASSUME(P_GEN_6_POKEMON == TRUE);
- PLAYER(SPECIES_AEGISLASH);
- PLAYER(SPECIES_WOBBUFFET);
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- TURN { MOVE(player, MOVE_TACKLE); }
- TURN { SWITCH(player, 1); }
- TURN { SWITCH(player, 0); }
- } SCENE {
- ABILITY_POPUP(player, ABILITY_STANCE_CHANGE);
- ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
- MESSAGE("Aegislash used Tackle!");
- MESSAGE("Foe Wobbuffet used Celebrate!");
- } THEN {
- EXPECT_EQ(player->species, SPECIES_AEGISLASH);
- }
-}
-
-SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting")
-{
- GIVEN {
- ASSUME(P_GEN_6_POKEMON == TRUE);
- PLAYER(SPECIES_AEGISLASH) { HP(1); }
- PLAYER(SPECIES_WOBBUFFET);
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- TURN { MOVE(opponent, MOVE_GUST); SEND_OUT(player, 1); }
- } SCENE {
- MESSAGE("Foe Wobbuffet used Gust!");
- MESSAGE("Aegislash fainted!");
- } THEN {
- EXPECT_EQ(GetMonData(&PLAYER_PARTY[0], MON_DATA_SPECIES), SPECIES_AEGISLASH);
- }
-}
diff --git a/test/battle/form_change/faint.c b/test/battle/form_change/faint.c
new file mode 100644
index 000000000..f4baa2894
--- /dev/null
+++ b/test/battle/form_change/faint.c
@@ -0,0 +1,19 @@
+#include "global.h"
+#include "test/battle.h"
+
+SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting")
+{
+ GIVEN {
+ ASSUME(P_GEN_6_POKEMON == TRUE);
+ PLAYER(SPECIES_AEGISLASH) { HP(1); }
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_GUST); SEND_OUT(player, 1); }
+ } SCENE {
+ MESSAGE("Foe Wobbuffet used Gust!");
+ MESSAGE("Aegislash fainted!");
+ } THEN {
+ EXPECT_EQ(GetMonData(&PLAYER_PARTY[0], MON_DATA_SPECIES), SPECIES_AEGISLASH);
+ }
+}
diff --git a/test/mega_evolution.c b/test/battle/form_change/mega_evolution.c
similarity index 99%
rename from test/mega_evolution.c
rename to test/battle/form_change/mega_evolution.c
index d27566028..3b47c7c86 100644
--- a/test/mega_evolution.c
+++ b/test/battle/form_change/mega_evolution.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Venusaur can Mega Evolve holding Venusaurite")
{
diff --git a/test/primal_reversion.c b/test/battle/form_change/primal_reversion.c
similarity index 99%
rename from test/primal_reversion.c
rename to test/battle/form_change/primal_reversion.c
index f888d17f7..3e271f67c 100644
--- a/test/primal_reversion.c
+++ b/test/battle/form_change/primal_reversion.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Primal reversion happens for Groudon only when holding Red Orb")
{
diff --git a/test/hold_effect_air_balloon.c b/test/battle/hold_effect/air_balloon.c
similarity index 99%
rename from test/hold_effect_air_balloon.c
rename to test/battle/hold_effect/air_balloon.c
index ba740948a..1ed8a89e4 100644
--- a/test/hold_effect_air_balloon.c
+++ b/test/battle/hold_effect/air_balloon.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/hold_effect_berserk_gene.c b/test/battle/hold_effect/berserk_gene.c
similarity index 98%
rename from test/hold_effect_berserk_gene.c
rename to test/battle/hold_effect/berserk_gene.c
index 8a473e8da..6fd0f8a85 100644
--- a/test/hold_effect_berserk_gene.c
+++ b/test/battle/hold_effect/berserk_gene.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
@@ -112,7 +112,7 @@ SINGLE_BATTLE_TEST("Berserk Gene does not confuse when Safeguard is active")
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Using Berserk Gene, the Attack of Wobbuffet sharply rose!");
- MESSAGE("Wobbuffet's party is protected by SAFEGUARD!");
+ MESSAGE("Wobbuffet's party is protected by Safeguard!");
NOT MESSAGE("Wobbuffet became confused!");
}
}
diff --git a/test/hold_effect_clear_amulet.c b/test/battle/hold_effect/clear_amulet.c
similarity index 99%
rename from test/hold_effect_clear_amulet.c
rename to test/battle/hold_effect/clear_amulet.c
index 83c887b2d..39e6c22a6 100644
--- a/test/hold_effect_clear_amulet.c
+++ b/test/battle/hold_effect/clear_amulet.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/hold_effect_eject_button.c b/test/battle/hold_effect/eject_button.c
similarity index 99%
rename from test/hold_effect_eject_button.c
rename to test/battle/hold_effect/eject_button.c
index 7e6312656..4409f5d5b 100644
--- a/test/hold_effect_eject_button.c
+++ b/test/battle/hold_effect/eject_button.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/hold_effect_gems.c b/test/battle/hold_effect/gems.c
similarity index 99%
rename from test/hold_effect_gems.c
rename to test/battle/hold_effect/gems.c
index 9a90b81f9..c98718313 100644
--- a/test/hold_effect_gems.c
+++ b/test/battle/hold_effect/gems.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/hold_effect_leftovers.c b/test/battle/hold_effect/leftovers.c
similarity index 98%
rename from test/hold_effect_leftovers.c
rename to test/battle/hold_effect/leftovers.c
index 01b0655a5..7e51a3bd7 100644
--- a/test/hold_effect_leftovers.c
+++ b/test/battle/hold_effect/leftovers.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/hold_effect_mirror_herb.c b/test/battle/hold_effect/mirror_herb.c
similarity index 98%
rename from test/hold_effect_mirror_herb.c
rename to test/battle/hold_effect/mirror_herb.c
index 22f564b85..1e9058b08 100644
--- a/test/hold_effect_mirror_herb.c
+++ b/test/battle/hold_effect/mirror_herb.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/hold_effect_red_card.c b/test/battle/hold_effect/red_card.c
similarity index 99%
rename from test/hold_effect_red_card.c
rename to test/battle/hold_effect/red_card.c
index 0a8bb3460..aad7fea64 100644
--- a/test/hold_effect_red_card.c
+++ b/test/battle/hold_effect/red_card.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/battle/hold_effect/safety_goggles.c b/test/battle/hold_effect/safety_goggles.c
new file mode 100644
index 000000000..bec703443
--- /dev/null
+++ b/test/battle/hold_effect/safety_goggles.c
@@ -0,0 +1,26 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ gItems[ITEM_SAFETY_GOGGLES].holdEffect == HOLD_EFFECT_SAFETY_GOGGLES;
+};
+
+SINGLE_BATTLE_TEST("Safety Goggles block powder and spore moves")
+{
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER);
+ ASSUME(gItems[ITEM_SAFETY_GOGGLES].holdEffect == HOLD_EFFECT_SAFETY_GOGGLES);
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_ABRA) { Item(ITEM_SAFETY_GOGGLES); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_STUN_SPORE); }
+ } SCENE {
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
+ MESSAGE("Foe Abra is not affected thanks to its SafetyGoggles!");
+ }
+}
+
+TO_DO_BATTLE_TEST("Safety Goggles blocks damage from hail");
+TO_DO_BATTLE_TEST("Safety Goggles blocks damage from sandstorm");
+TO_DO_BATTLE_TEST("Safety Goggles blocks Effect Spore's effect");
diff --git a/test/hold_effect_utility_umbrella.c b/test/battle/hold_effect/utility_umbrella.c
similarity index 99%
rename from test/hold_effect_utility_umbrella.c
rename to test/battle/hold_effect/utility_umbrella.c
index 6716ee72f..837bacc90 100644
--- a/test/hold_effect_utility_umbrella.c
+++ b/test/battle/hold_effect/utility_umbrella.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
// Please add Utility Umbrella interactions with move, item and ability effects on their respective files.
ASSUMPTIONS
diff --git a/test/item_effect_cure_status.c b/test/battle/item_effect/cure_status.c
similarity index 99%
rename from test/item_effect_cure_status.c
rename to test/battle/item_effect/cure_status.c
index 1f7c9d7a2..c8b16dfdb 100644
--- a/test/item_effect_cure_status.c
+++ b/test/battle/item_effect/cure_status.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Paralyze Heal heals a battler from being paralyzed")
{
diff --git a/test/item_effect_heal_and_cure_status.c b/test/battle/item_effect/heal_and_cure_status.c
similarity index 98%
rename from test/item_effect_heal_and_cure_status.c
rename to test/battle/item_effect/heal_and_cure_status.c
index 1f81ad922..de6a459a1 100644
--- a/test/item_effect_heal_and_cure_status.c
+++ b/test/battle/item_effect/heal_and_cure_status.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary status")
{
diff --git a/test/item_effect_increase_stat.c b/test/battle/item_effect/increase_stat.c
similarity index 99%
rename from test/item_effect_increase_stat.c
rename to test/battle/item_effect/increase_stat.c
index ab4113064..3aeb8d525 100644
--- a/test/item_effect_increase_stat.c
+++ b/test/battle/item_effect/increase_stat.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("X Attack sharply raises battler's Attack stat", s16 damage)
{
diff --git a/test/item_effect_restore_hp.c b/test/battle/item_effect/restore_hp.c
similarity index 98%
rename from test/item_effect_restore_hp.c
rename to test/battle/item_effect/restore_hp.c
index a469b21ff..b9e4be034 100644
--- a/test/item_effect_restore_hp.c
+++ b/test/battle/item_effect/restore_hp.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Items can restore a battler's HP by a fixed amount")
{
diff --git a/test/item_effect_restore_pp.c b/test/battle/item_effect/restore_pp.c
similarity index 98%
rename from test/item_effect_restore_pp.c
rename to test/battle/item_effect/restore_pp.c
index 7194efbbb..e998ef3d9 100644
--- a/test/item_effect_restore_pp.c
+++ b/test/battle/item_effect/restore_pp.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Ether restores the PP of one of a battler's moves by 10 ")
{
diff --git a/test/item_effect_revive.c b/test/battle/item_effect/revive.c
similarity index 98%
rename from test/item_effect_revive.c
rename to test/battle/item_effect/revive.c
index c2a22f80a..2be2ac4a6 100644
--- a/test/item_effect_revive.c
+++ b/test/battle/item_effect/revive.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Revive restores a fainted battler's HP to half")
{
diff --git a/test/item_effect_set_focus_energy.c b/test/battle/item_effect/set_focus_energy.c
similarity index 96%
rename from test/item_effect_set_focus_energy.c
rename to test/battle/item_effect/set_focus_energy.c
index 95648465e..da326bbe3 100644
--- a/test/item_effect_set_focus_energy.c
+++ b/test/battle/item_effect/set_focus_energy.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Dire Hit increases a battler's critical hit chance by 2 stages")
{
diff --git a/test/item_effect_set_mist.c b/test/battle/item_effect/set_mist.c
similarity index 96%
rename from test/item_effect_set_mist.c
rename to test/battle/item_effect/set_mist.c
index e01775ec4..a369e70d9 100644
--- a/test/item_effect_set_mist.c
+++ b/test/battle/item_effect/set_mist.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Guard Spec. sets Mist effect on the battlers side")
{
diff --git a/test/move.c b/test/battle/move.c
similarity index 99%
rename from test/move.c
rename to test/battle/move.c
index 9e23c2bd0..e89c2ad35 100644
--- a/test/move.c
+++ b/test/battle/move.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Accuracy controls the proportion of misses")
{
diff --git a/test/move_effect_absorb.c b/test/battle/move_effect/absorb.c
similarity index 97%
rename from test/move_effect_absorb.c
rename to test/battle/move_effect/absorb.c
index 2ea49ef9e..d46dbd9c5 100644
--- a/test/move_effect_absorb.c
+++ b/test/battle/move_effect/absorb.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_accuracy_down.c b/test/battle/move_effect/accuracy_down.c
similarity index 96%
rename from test/move_effect_accuracy_down.c
rename to test/battle/move_effect/accuracy_down.c
index a6a79d8db..a30430a28 100644
--- a/test/move_effect_accuracy_down.c
+++ b/test/battle/move_effect/accuracy_down.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_after_you.c b/test/battle/move_effect/after_you.c
similarity index 98%
rename from test/move_effect_after_you.c
rename to test/battle/move_effect/after_you.c
index a488d7a8c..576267cb4 100644
--- a/test/move_effect_after_you.c
+++ b/test/battle/move_effect/after_you.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_assist.c b/test/battle/move_effect/assist.c
similarity index 96%
rename from test/move_effect_assist.c
rename to test/battle/move_effect/assist.c
index 28ae78572..ed2f43dc1 100644
--- a/test/move_effect_assist.c
+++ b/test/battle/move_effect/assist.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_attack_down.c b/test/battle/move_effect/attack_down.c
similarity index 97%
rename from test/move_effect_attack_down.c
rename to test/battle/move_effect/attack_down.c
index 0df14f701..2149624c8 100644
--- a/test/move_effect_attack_down.c
+++ b/test/battle/move_effect/attack_down.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_attack_up.c b/test/battle/move_effect/attack_up.c
similarity index 97%
rename from test/move_effect_attack_up.c
rename to test/battle/move_effect/attack_up.c
index 4978d03f9..7cd8f953c 100644
--- a/test/move_effect_attack_up.c
+++ b/test/battle/move_effect/attack_up.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_attack_up_user_ally.c b/test/battle/move_effect/attack_up_user_ally.c
similarity index 99%
rename from test/move_effect_attack_up_user_ally.c
rename to test/battle/move_effect/attack_up_user_ally.c
index 3d2ac6bb8..6691b49ed 100644
--- a/test/move_effect_attack_up_user_ally.c
+++ b/test/battle/move_effect/attack_up_user_ally.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_barb_barrage.c b/test/battle/move_effect/barb_barrage.c
similarity index 98%
rename from test/move_effect_barb_barrage.c
rename to test/battle/move_effect/barb_barrage.c
index ad9f1ec2e..89062b3de 100644
--- a/test/move_effect_barb_barrage.c
+++ b/test/battle/move_effect/barb_barrage.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_beak_blast.c b/test/battle/move_effect/beak_blast.c
similarity index 99%
rename from test/move_effect_beak_blast.c
rename to test/battle/move_effect/beak_blast.c
index a2d66b405..50bff2411 100644
--- a/test/move_effect_beak_blast.c
+++ b/test/battle/move_effect/beak_blast.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_bide.c b/test/battle/move_effect/bide.c
similarity index 97%
rename from test/move_effect_bide.c
rename to test/battle/move_effect/bide.c
index 0b0e18408..4dfdc3430 100644
--- a/test/move_effect_bide.c
+++ b/test/battle/move_effect/bide.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_burn_hit.c b/test/battle/move_effect/burn_hit.c
similarity index 97%
rename from test/move_effect_burn_hit.c
rename to test/battle/move_effect/burn_hit.c
index 88fef17cb..3f257dfcb 100644
--- a/test/move_effect_burn_hit.c
+++ b/test/battle/move_effect/burn_hit.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_burn_up.c b/test/battle/move_effect/burn_up.c
similarity index 98%
rename from test/move_effect_burn_up.c
rename to test/battle/move_effect/burn_up.c
index a53b1afa8..d24e47992 100644
--- a/test/move_effect_burn_up.c
+++ b/test/battle/move_effect/burn_up.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/battle/move_effect/court_change.c b/test/battle/move_effect/court_change.c
new file mode 100644
index 000000000..4aa2e9d36
--- /dev/null
+++ b/test/battle/move_effect/court_change.c
@@ -0,0 +1,153 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ ASSUME(gBattleMoves[MOVE_COURT_CHANGE].effect == EFFECT_COURT_CHANGE);
+}
+
+DOUBLE_BATTLE_TEST("Court Change swaps entry hazards used by the opponent")
+{
+ GIVEN {
+ PLAYER(SPECIES_WYNAUT);
+ PLAYER(SPECIES_WYNAUT);
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponentLeft, MOVE_STICKY_WEB); MOVE(opponentRight, MOVE_STEALTH_ROCK); }
+ TURN { MOVE(opponentLeft, MOVE_SPIKES); MOVE(opponentRight, MOVE_TOXIC_SPIKES); }
+ TURN { MOVE(playerLeft, MOVE_COURT_CHANGE); }
+ TURN { SWITCH(playerLeft, 2); SWITCH(opponentLeft, 2); }
+ } SCENE {
+ MESSAGE("Foe Wobbuffet used Sticky Web!");
+ MESSAGE("Foe Wobbuffet used Stealth Rock!");
+ MESSAGE("Foe Wobbuffet used Spikes!");
+ MESSAGE("Foe Wobbuffet used Toxic Spikes!");
+ MESSAGE("Wynaut used Court Change!");
+ MESSAGE("Wynaut swapped the battle effects affecting each side!");
+ MESSAGE("Go! Wynaut!");
+ NONE_OF {
+ MESSAGE("Wynaut is hurt by spikes!");
+ MESSAGE("Pointed stones dug into Wynaut!");
+ MESSAGE("Wynaut was poisoned!");
+ MESSAGE("Wynaut was caught in a Sticky Web!");
+ }
+ MESSAGE("2 sent out Wobbuffet!");
+ MESSAGE("Foe Wobbuffet is hurt by spikes!");
+ MESSAGE("Pointed stones dug into Foe Wobbuffet!");
+ MESSAGE("Foe Wobbuffet was poisoned!");
+ MESSAGE("Foe Wobbuffet was caught in a Sticky Web!");
+ }
+}
+
+DOUBLE_BATTLE_TEST("Court Change swaps entry hazards used by the player")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_STICKY_WEB); MOVE(playerRight, MOVE_STEALTH_ROCK); }
+ TURN { MOVE(playerLeft, MOVE_SPIKES); MOVE(playerRight, MOVE_TOXIC_SPIKES); }
+ TURN { MOVE(opponentLeft, MOVE_COURT_CHANGE); }
+ TURN { SWITCH(opponentLeft, 2); SWITCH(playerLeft, 2); }
+ } SCENE {
+ MESSAGE("Wobbuffet used Sticky Web!");
+ MESSAGE("Wobbuffet used Stealth Rock!");
+ MESSAGE("Wobbuffet used Spikes!");
+ MESSAGE("Wobbuffet used Toxic Spikes!");
+ MESSAGE("Foe Wynaut used Court Change!");
+ MESSAGE("Foe Wynaut swapped the battle effects affecting each side!");
+ MESSAGE("Go! Wobbuffet!");
+ MESSAGE("Wobbuffet is hurt by spikes!");
+ MESSAGE("Pointed stones dug into Wobbuffet!");
+ MESSAGE("Wobbuffet was poisoned!");
+ MESSAGE("Wobbuffet was caught in a Sticky Web!");
+ MESSAGE("2 sent out Wynaut!");
+ NONE_OF {
+ MESSAGE("Foe Wynaut is hurt by spikes!");
+ MESSAGE("Pointed stones dug into Foe Wynaut!");
+ MESSAGE("Foe Wynaut was poisoned!");
+ MESSAGE("Foe Wynaut was caught in a Sticky Web!");
+ }
+ }
+}
+
+DOUBLE_BATTLE_TEST("Court Change used by the player swaps Mist, Safeguard, Lucky Chant, Reflect, Light Screen, Tailwind")
+{
+ GIVEN {
+ PLAYER(SPECIES_WYNAUT);
+ PLAYER(SPECIES_WYNAUT);
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponentLeft, MOVE_MIST); MOVE(opponentRight, MOVE_SAFEGUARD); }
+ TURN { MOVE(opponentLeft, MOVE_LUCKY_CHANT); MOVE(opponentRight, MOVE_REFLECT); }
+ TURN { MOVE(opponentLeft, MOVE_LIGHT_SCREEN); MOVE(opponentRight, MOVE_TAILWIND); }
+ TURN { MOVE(playerLeft, MOVE_COURT_CHANGE); }
+ TURN { }
+ TURN { }
+ TURN { }
+ TURN { }
+ } SCENE {
+ MESSAGE("Foe Wobbuffet used Mist!");
+ MESSAGE("Foe Wobbuffet used Safeguard!");
+ MESSAGE("Foe Wobbuffet used Lucky Chant!");
+ MESSAGE("Foe Wobbuffet used Reflect!");
+ MESSAGE("Foe Wobbuffet used Light Screen!");
+ MESSAGE("Foe Wobbuffet used Tailwind!");
+ MESSAGE("Wynaut used Court Change!");
+ MESSAGE("Wynaut swapped the battle effects affecting each side!");
+ // The effects now end for the player side.
+ MESSAGE("Ally's Mist wore off!");
+ MESSAGE("Ally's party is no longer protected by Safeguard!");
+ MESSAGE("Ally's Reflect wore off!");
+ MESSAGE("Your team's Lucky Chant wore off!");
+ MESSAGE("Your team's tailwind petered out!");
+ MESSAGE("Ally's Light Screen wore off!");
+ }
+}
+
+DOUBLE_BATTLE_TEST("Court Change used by the opponent swaps Mist, Safeguard, Lucky Chant, Reflect, Light Screen, Tailwind")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(playerLeft, MOVE_MIST); MOVE(playerRight, MOVE_SAFEGUARD); }
+ TURN { MOVE(playerLeft, MOVE_LUCKY_CHANT); MOVE(playerRight, MOVE_REFLECT); }
+ TURN { MOVE(playerLeft, MOVE_LIGHT_SCREEN); MOVE(playerRight, MOVE_TAILWIND); }
+ TURN { MOVE(opponentLeft, MOVE_COURT_CHANGE); }
+ TURN { }
+ TURN { }
+ TURN { }
+ TURN { }
+ } SCENE {
+ MESSAGE("Wobbuffet used Mist!");
+ MESSAGE("Wobbuffet used Safeguard!");
+ MESSAGE("Wobbuffet used Lucky Chant!");
+ MESSAGE("Wobbuffet used Reflect!");
+ MESSAGE("Wobbuffet used Light Screen!");
+ MESSAGE("Wobbuffet used Tailwind!");
+ MESSAGE("Foe Wynaut used Court Change!");
+ MESSAGE("Foe Wynaut swapped the battle effects affecting each side!");
+ // The effects now end for the player side.
+ MESSAGE("Foe's Mist wore off!");
+ MESSAGE("Foe's party is no longer protected by Safeguard!");
+ MESSAGE("Foe's Reflect wore off!");
+ MESSAGE("The opposing team's Lucky Chant wore off!");
+ MESSAGE("The opposing team's tailwind petered out!");
+ MESSAGE("Foe's Light Screen wore off!");
+ }
+}
diff --git a/test/move_effect_defense_down.c b/test/battle/move_effect/defense_down.c
similarity index 97%
rename from test/move_effect_defense_down.c
rename to test/battle/move_effect/defense_down.c
index 9ecd1c25f..8a8c69ec3 100644
--- a/test/move_effect_defense_down.c
+++ b/test/battle/move_effect/defense_down.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_defense_up.c b/test/battle/move_effect/defense_up.c
similarity index 97%
rename from test/move_effect_defense_up.c
rename to test/battle/move_effect/defense_up.c
index 492ce7bc9..b4c7cb4db 100644
--- a/test/move_effect_defense_up.c
+++ b/test/battle/move_effect/defense_up.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_defog.c b/test/battle/move_effect/defog.c
similarity index 99%
rename from test/move_effect_defog.c
rename to test/battle/move_effect/defog.c
index b55d5eab6..1cd78fb50 100644
--- a/test/move_effect_defog.c
+++ b/test/battle/move_effect/defog.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
@@ -125,7 +125,7 @@ DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Mist and Safeguard
STATUS_ICON(opponentRight, badPoison: TRUE);
}
else {
- MESSAGE("Foe Wobbuffet's party is protected by SAFEGUARD!");
+ MESSAGE("Foe Wobbuffet's party is protected by Safeguard!");
NOT STATUS_ICON(opponentRight, badPoison: TRUE);
}
}
diff --git a/test/move_effect_dire_claw.c b/test/battle/move_effect/dire_claw.c
similarity index 99%
rename from test/move_effect_dire_claw.c
rename to test/battle/move_effect/dire_claw.c
index 712b6ea25..421ba201d 100644
--- a/test/move_effect_dire_claw.c
+++ b/test/battle/move_effect/dire_claw.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_double_shock.c b/test/battle/move_effect/double_shock.c
similarity index 98%
rename from test/move_effect_double_shock.c
rename to test/battle/move_effect/double_shock.c
index c3651c403..89f758681 100644
--- a/test/move_effect_double_shock.c
+++ b/test/battle/move_effect/double_shock.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_dream_eater.c b/test/battle/move_effect/dream_eater.c
similarity index 98%
rename from test/move_effect_dream_eater.c
rename to test/battle/move_effect/dream_eater.c
index fa17b94a6..047fe7898 100644
--- a/test/move_effect_dream_eater.c
+++ b/test/battle/move_effect/dream_eater.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_encore.c b/test/battle/move_effect/encore.c
similarity index 98%
rename from test/move_effect_encore.c
rename to test/battle/move_effect/encore.c
index f8a178512..eff6c33b2 100644
--- a/test/move_effect_encore.c
+++ b/test/battle/move_effect/encore.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_evasion_up.c b/test/battle/move_effect/evasion_up.c
similarity index 96%
rename from test/move_effect_evasion_up.c
rename to test/battle/move_effect/evasion_up.c
index 4a4e99db7..ecb6429cd 100644
--- a/test/move_effect_evasion_up.c
+++ b/test/battle/move_effect/evasion_up.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_explosion.c b/test/battle/move_effect/explosion.c
similarity index 99%
rename from test/move_effect_explosion.c
rename to test/battle/move_effect/explosion.c
index 41e74044b..ef6dba6c0 100644
--- a/test/move_effect_explosion.c
+++ b/test/battle/move_effect/explosion.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_focus_punch.c b/test/battle/move_effect/focus_punch.c
similarity index 99%
rename from test/move_effect_focus_punch.c
rename to test/battle/move_effect/focus_punch.c
index 2bd1e52e5..e97deef43 100644
--- a/test/move_effect_focus_punch.c
+++ b/test/battle/move_effect/focus_punch.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_freeze_hit.c b/test/battle/move_effect/freeze_hit.c
similarity index 97%
rename from test/move_effect_freeze_hit.c
rename to test/battle/move_effect/freeze_hit.c
index bb0878d0d..5d99311ce 100644
--- a/test/move_effect_freeze_hit.c
+++ b/test/battle/move_effect/freeze_hit.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_haze.c b/test/battle/move_effect/haze.c
similarity index 97%
rename from test/move_effect_haze.c
rename to test/battle/move_effect/haze.c
index bd43c6947..f1e4456aa 100644
--- a/test/move_effect_haze.c
+++ b/test/battle/move_effect/haze.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_healing_wish.c b/test/battle/move_effect/healing_wish.c
similarity index 99%
rename from test/move_effect_healing_wish.c
rename to test/battle/move_effect/healing_wish.c
index 00ee76561..fd25b8489 100644
--- a/test/move_effect_healing_wish.c
+++ b/test/battle/move_effect/healing_wish.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_hex.c b/test/battle/move_effect/hex.c
similarity index 97%
rename from test/move_effect_hex.c
rename to test/battle/move_effect/hex.c
index 2847aeb17..e0a924906 100644
--- a/test/move_effect_hex.c
+++ b/test/battle/move_effect/hex.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_hit_escape.c b/test/battle/move_effect/hit_escape.c
similarity index 99%
rename from test/move_effect_hit_escape.c
rename to test/battle/move_effect/hit_escape.c
index fb5ff2dd5..f2d513095 100644
--- a/test/move_effect_hit_escape.c
+++ b/test/battle/move_effect/hit_escape.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_hit_set_entry_hazardss.c b/test/battle/move_effect/hit_set_entry_hazardss.c
similarity index 99%
rename from test/move_effect_hit_set_entry_hazardss.c
rename to test/battle/move_effect/hit_set_entry_hazardss.c
index 23f0f0b28..caf75ed49 100644
--- a/test/move_effect_hit_set_entry_hazardss.c
+++ b/test/battle/move_effect/hit_set_entry_hazardss.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_hit_switch_target.c b/test/battle/move_effect/hit_switch_target.c
similarity index 98%
rename from test/move_effect_hit_switch_target.c
rename to test/battle/move_effect/hit_switch_target.c
index 5af3062a4..683b005fd 100644
--- a/test/move_effect_hit_switch_target.c
+++ b/test/battle/move_effect/hit_switch_target.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_hydro_steam.c b/test/battle/move_effect/hydro_steam.c
similarity index 98%
rename from test/move_effect_hydro_steam.c
rename to test/battle/move_effect/hydro_steam.c
index 487449159..f86da07ac 100644
--- a/test/move_effect_hydro_steam.c
+++ b/test/battle/move_effect/hydro_steam.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_metronome.c b/test/battle/move_effect/metronome.c
similarity index 98%
rename from test/move_effect_metronome.c
rename to test/battle/move_effect/metronome.c
index 1e68603e6..c6611ffa5 100644
--- a/test/move_effect_metronome.c
+++ b/test/battle/move_effect/metronome.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_mind_blown.c b/test/battle/move_effect/mind_blown.c
similarity index 99%
rename from test/move_effect_mind_blown.c
rename to test/battle/move_effect/mind_blown.c
index 19179f990..485f2abd6 100644
--- a/test/move_effect_mind_blown.c
+++ b/test/battle/move_effect/mind_blown.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_mirror_move.c b/test/battle/move_effect/mirror_move.c
similarity index 99%
rename from test/move_effect_mirror_move.c
rename to test/battle/move_effect/mirror_move.c
index ff21c0efa..b709ffce7 100644
--- a/test/move_effect_mirror_move.c
+++ b/test/battle/move_effect/mirror_move.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_paralyze_hit.c b/test/battle/move_effect/paralyze_hit.c
similarity index 97%
rename from test/move_effect_paralyze_hit.c
rename to test/battle/move_effect/paralyze_hit.c
index 8e7d259f7..4efe6746c 100644
--- a/test/move_effect_paralyze_hit.c
+++ b/test/battle/move_effect/paralyze_hit.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_poison_hit.c b/test/battle/move_effect/poison_hit.c
similarity index 97%
rename from test/move_effect_poison_hit.c
rename to test/battle/move_effect/poison_hit.c
index b1a154810..91d700466 100644
--- a/test/move_effect_poison_hit.c
+++ b/test/battle/move_effect/poison_hit.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_rampage.c b/test/battle/move_effect/rampage.c
similarity index 99%
rename from test/move_effect_rampage.c
rename to test/battle/move_effect/rampage.c
index aa4a002fb..a1a271b37 100644
--- a/test/move_effect_rampage.c
+++ b/test/battle/move_effect/rampage.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/battle/move_effect/recoil_if_miss.c b/test/battle/move_effect/recoil_if_miss.c
new file mode 100644
index 000000000..c72fd2648
--- /dev/null
+++ b/test/battle/move_effect/recoil_if_miss.c
@@ -0,0 +1,100 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ ASSUME(gBattleMoves[MOVE_JUMP_KICK].effect == EFFECT_RECOIL_IF_MISS);
+}
+
+SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on miss")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_JUMP_KICK, hit: FALSE); }
+ } SCENE {
+ s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
+ MESSAGE("Wobbuffet used Jump Kick!");
+ MESSAGE("Wobbuffet's attack missed!");
+ MESSAGE("Wobbuffet kept going and crashed!");
+ HP_BAR(player, damage: maxHP / 2);
+ }
+}
+
+SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on protect")
+{
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_JUMP_KICK].flags & FLAG_PROTECT_AFFECTED);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_JUMP_KICK, hit: FALSE); }
+ } SCENE {
+ s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_JUMP_KICK, player);
+ HP_BAR(player, damage: maxHP / 2);
+ }
+}
+
+SINGLE_BATTLE_TEST("Jump Kick has no recoil if no target")
+{
+ GIVEN {
+ ASSUME(B_HEALING_WISH_SWITCH >= GEN_5);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_HEALING_WISH); MOVE(player, MOVE_JUMP_KICK, hit: FALSE); SEND_OUT(opponent, 1); }
+ } SCENE {
+ s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, opponent);
+ NOT HP_BAR(player, damage: maxHP / 2);
+ }
+}
+
+SINGLE_BATTLE_TEST("Jump Kick's recoil happens after Spiky Shield damage and Pokemon can faint from either of these")
+{
+ s16 hp, maxHp = 256;
+ bool32 faintOnSpiky = FALSE, faintOnJumpKick = FALSE;
+
+ PARAMETRIZE { hp = maxHp; }
+ PARAMETRIZE { hp = maxHp / 2; faintOnJumpKick = TRUE; } // Faints after Jump Kick's recoil
+ PARAMETRIZE { hp = maxHp / 8; faintOnSpiky = TRUE; } // Faints after Spiky Shield's recoil
+
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_SPIKY_SHIELD].effect == EFFECT_PROTECT);
+ PLAYER(SPECIES_WOBBUFFET) { HP(hp); MaxHP(maxHp); }
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ if (!faintOnJumpKick && !faintOnSpiky) {
+ TURN { MOVE(opponent, MOVE_SPIKY_SHIELD); MOVE(player, MOVE_JUMP_KICK, hit: FALSE); }
+ } else {
+ TURN { MOVE(opponent, MOVE_SPIKY_SHIELD); MOVE(player, MOVE_JUMP_KICK, hit: FALSE); SEND_OUT(player, 1); }
+ }
+ TURN { ; }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKY_SHIELD, opponent);
+ MESSAGE("Wobbuffet used Jump Kick!");
+ MESSAGE("Foe Wobbuffet protected itself!");
+ HP_BAR(player, damage: maxHp / 8);
+ MESSAGE("Wobbuffet was hurt by Foe Wobbuffet's Spiky Shield!");
+ if (faintOnSpiky){
+ MESSAGE("Wobbuffet fainted!");
+ MESSAGE("Go! Wynaut!");
+ NONE_OF {
+ MESSAGE("Wobbuffet kept going and crashed!");
+ HP_BAR(player);
+ }
+ } else {
+ MESSAGE("Wobbuffet kept going and crashed!");
+ HP_BAR(player);
+ if (faintOnJumpKick) {
+ MESSAGE("Wobbuffet fainted!");
+ MESSAGE("Go! Wynaut!");
+ }
+ }
+ }
+}
diff --git a/test/move_effect_reflect.c b/test/battle/move_effect/reflect.c
similarity index 99%
rename from test/move_effect_reflect.c
rename to test/battle/move_effect/reflect.c
index 4ea875f35..dd8738f72 100644
--- a/test/move_effect_reflect.c
+++ b/test/battle/move_effect/reflect.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_revival_blessing.c b/test/battle/move_effect/revival_blessing.c
similarity index 99%
rename from test/move_effect_revival_blessing.c
rename to test/battle/move_effect/revival_blessing.c
index 3cb4d4ca4..a5dd32c19 100644
--- a/test/move_effect_revival_blessing.c
+++ b/test/battle/move_effect/revival_blessing.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
// Note: Since these tests are recorded battle, they don't test the right battle controller
// behaviors. These have been tested in-game, in double, in multi, and in link battles. AI will always
diff --git a/test/move_effect_roar.c b/test/battle/move_effect/roar.c
similarity index 98%
rename from test/move_effect_roar.c
rename to test/battle/move_effect/roar.c
index 99256b298..20f934808 100644
--- a/test/move_effect_roar.c
+++ b/test/battle/move_effect/roar.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_shell_trap.c b/test/battle/move_effect/shell_trap.c
similarity index 99%
rename from test/move_effect_shell_trap.c
rename to test/battle/move_effect/shell_trap.c
index 2498e12f2..7fb80210e 100644
--- a/test/move_effect_shell_trap.c
+++ b/test/battle/move_effect/shell_trap.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_sleep.c b/test/battle/move_effect/sleep.c
similarity index 97%
rename from test/move_effect_sleep.c
rename to test/battle/move_effect/sleep.c
index c34e1248e..d2cac0af9 100644
--- a/test/move_effect_sleep.c
+++ b/test/battle/move_effect/sleep.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_special_attack_down.c b/test/battle/move_effect/special_attack_down.c
similarity index 97%
rename from test/move_effect_special_attack_down.c
rename to test/battle/move_effect/special_attack_down.c
index 0511692d4..6e7a84f0a 100644
--- a/test/move_effect_special_attack_down.c
+++ b/test/battle/move_effect/special_attack_down.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_special_attack_up_3.c b/test/battle/move_effect/special_attack_up_3.c
similarity index 97%
rename from test/move_effect_special_attack_up_3.c
rename to test/battle/move_effect/special_attack_up_3.c
index ad53bedb9..340c59360 100644
--- a/test/move_effect_special_attack_up_3.c
+++ b/test/battle/move_effect/special_attack_up_3.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_spikes.c b/test/battle/move_effect/spikes.c
similarity index 99%
rename from test/move_effect_spikes.c
rename to test/battle/move_effect/spikes.c
index 0c84a5fb1..bfc0f39b5 100644
--- a/test/move_effect_spikes.c
+++ b/test/battle/move_effect/spikes.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/battle/move_effect/sticky_web.c b/test/battle/move_effect/sticky_web.c
new file mode 100644
index 000000000..e12cc8090
--- /dev/null
+++ b/test/battle/move_effect/sticky_web.c
@@ -0,0 +1,237 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ ASSUME(gBattleMoves[MOVE_STICKY_WEB].effect == EFFECT_STICKY_WEB);
+}
+
+SINGLE_BATTLE_TEST("Sticky Web lowers Speed by 1 on switch-in")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(player, MOVE_STICKY_WEB); }
+ TURN { SWITCH(opponent, 1); }
+ TURN {}
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, player);
+ MESSAGE("A sticky web spreads out on the ground around the opposing team!");
+ MESSAGE("2 sent out Wynaut!");
+ MESSAGE("Foe Wynaut was caught in a Sticky Web!");
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ MESSAGE("Foe Wynaut's Speed fell!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Sticky Web can only be set up 1 time")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_STICKY_WEB); }
+ TURN { MOVE(player, MOVE_STICKY_WEB); }
+ } SCENE {
+ MESSAGE("Wobbuffet used Sticky Web!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, player);
+ MESSAGE("A sticky web spreads out on the ground around the opposing team!");
+
+ MESSAGE("Wobbuffet used Sticky Web!");
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, player);
+ MESSAGE("But it failed!");
+ }
+}
+
+
+DOUBLE_BATTLE_TEST("Sticky Web lowers Speed by 1 in a double battle after Explosion fainting both mons")
+{
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_EXPLOSION].effect == EFFECT_EXPLOSION);
+ PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
+ PLAYER(SPECIES_WOBBUFFET) {HP(1500); Speed(10);}
+ PLAYER(SPECIES_WOBBUFFET) {Speed(10);}
+ OPPONENT(SPECIES_WOBBUFFET) {HP(1); Speed(1);}
+ OPPONENT(SPECIES_WOBBUFFET) {HP(1); Speed(1);}
+ OPPONENT(SPECIES_WYNAUT) {Speed(10);}
+ OPPONENT(SPECIES_WYNAUT) {Speed(10);}
+ } WHEN {
+ TURN { MOVE(playerRight, MOVE_STICKY_WEB); MOVE(playerLeft, MOVE_EXPLOSION); SEND_OUT(playerLeft, 2); SEND_OUT(opponentLeft, 2); SEND_OUT(opponentRight, 3); }
+ TURN {}
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, playerRight);
+ MESSAGE("A sticky web spreads out on the ground around the opposing team!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, playerLeft);
+ MESSAGE("2 sent out Wynaut!");
+ MESSAGE("Foe Wynaut was caught in a Sticky Web!");
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
+ MESSAGE("Foe Wynaut's Speed fell!");
+ MESSAGE("2 sent out Wynaut!");
+ MESSAGE("Foe Wynaut was caught in a Sticky Web!");
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
+ MESSAGE("Foe Wynaut's Speed fell!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Sticky Web raises Speed by 1 for a Pokemon with Contrary")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_SHUCKLE) { Ability(ABILITY_CONTRARY); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_STICKY_WEB); }
+ TURN { SWITCH(opponent, 1); }
+ TURN {}
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, player);
+ MESSAGE("A sticky web spreads out on the ground around the opposing team!");
+ MESSAGE("2 sent out Shuckle!");
+ MESSAGE("Foe Shuckle was caught in a Sticky Web!");
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
+ MESSAGE("Foe Shuckle's Speed rose!");
+ }
+}
+
+#define BATTLER_OPPONENT (opponentSetUpper == 0 ? opponentLeft : opponentRight)
+#define BATTLER_PLAYER (playerSetUpper == 0 ? playerLeft : playerRight)
+
+DOUBLE_BATTLE_TEST("Sticky Web has correct interactions with Mirror Armor - the battler which set up Sticky Web has its Speed lowered instead")
+{
+ u8 playerSetUpper, opponentSetUpper; // 0 left, 1 right
+
+ PARAMETRIZE {playerSetUpper = 0; opponentSetUpper = 0; }
+ PARAMETRIZE {playerSetUpper = 0; opponentSetUpper = 1; }
+ PARAMETRIZE {playerSetUpper = 1; opponentSetUpper = 0; }
+ PARAMETRIZE {playerSetUpper = 1; opponentSetUpper = 1; }
+
+ GIVEN {
+ ASSUME(P_GEN_8_POKEMON == TRUE);
+ PLAYER(SPECIES_SQUIRTLE);
+ PLAYER(SPECIES_CHARMANDER);
+ PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); Item(ITEM_IRON_BALL); } // Iron Ball, so that flying type Corviknight is affected by Sticky Web.
+ OPPONENT(SPECIES_CATERPIE);
+ OPPONENT(SPECIES_WEEDLE);
+ } WHEN {
+ TURN { MOVE(BATTLER_OPPONENT, MOVE_STICKY_WEB); }
+ TURN { MOVE(BATTLER_PLAYER, MOVE_STICKY_WEB); }
+ TURN { SWITCH(playerRight, 2); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, BATTLER_OPPONENT);
+ MESSAGE("A sticky web spreads out on the ground around your team!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, BATTLER_PLAYER);
+ MESSAGE("A sticky web spreads out on the ground around the opposing team!");
+
+ MESSAGE("Go! Corviknigh!");
+ MESSAGE("Corviknigh was caught in a Sticky Web!");
+ ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR);
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, BATTLER_OPPONENT);
+ if (opponentSetUpper == 0) {
+ MESSAGE("Foe Caterpie's Speed fell!");
+ } else {
+ MESSAGE("Foe Weedle's Speed fell!");
+ }
+ }
+}
+
+#undef BATTLER_OPPONENT
+#undef BATTLER_PLAYER
+
+DOUBLE_BATTLE_TEST("Sticky Web has correct interactions with Mirror Armor - no one has their Speed lowered if the set upper switched")
+{
+ u16 speedPlayer, speedOpponent;
+
+ // We need to make sure Sticky Web user saves for both sides, so it doesn't matter who sets it first.
+ PARAMETRIZE { speedPlayer = 5; speedOpponent = 10; }
+ PARAMETRIZE { speedPlayer = 10; speedOpponent = 5; }
+
+ GIVEN {
+ ASSUME(P_GEN_8_POKEMON == TRUE);
+ PLAYER(SPECIES_SQUIRTLE) { Speed(speedPlayer); }
+ PLAYER(SPECIES_CHARMANDER) { Speed(speedPlayer); }
+ PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); Item(ITEM_IRON_BALL); Speed(speedOpponent); } // Iron Ball, so that flying type Corviknight is affected by Sticky Web.
+ OPPONENT(SPECIES_CATERPIE) { Speed(speedOpponent); }
+ OPPONENT(SPECIES_WEEDLE) { Speed(speedOpponent); }
+ OPPONENT(SPECIES_PIDGEY) { Speed(speedOpponent); } // Flying type,so not affected by Sticky Web.
+ } WHEN {
+ TURN { MOVE(opponentLeft, MOVE_STICKY_WEB); MOVE(playerRight, MOVE_STICKY_WEB); }
+ TURN { SWITCH(opponentLeft, 2); }
+ TURN { SWITCH(playerRight, 2); }
+ } SCENE {
+ if (speedPlayer > speedOpponent) {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, playerRight);
+ MESSAGE("A sticky web spreads out on the ground around the opposing team!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponentLeft);
+ MESSAGE("A sticky web spreads out on the ground around your team!");
+ } else {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponentLeft);
+ MESSAGE("A sticky web spreads out on the ground around your team!");
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, playerRight);
+ MESSAGE("A sticky web spreads out on the ground around the opposing team!");
+ }
+
+ MESSAGE("Go! Corviknigh!");
+ MESSAGE("Corviknigh was caught in a Sticky Web!");
+ ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR);
+ NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
+ } THEN {
+ EXPECT_EQ(playerLeft->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(playerRight->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponentLeft->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponentRight->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
+ }
+}
+
+DOUBLE_BATTLE_TEST("Sticky Web has correct interactions with Mirror Armor - no one has their Speed lowered if the set upper fainted")
+{
+ bool8 hasReplacement;
+
+ // We need to make sure Sticky Web user saves for both sides, so it doesn't matter who sets it first.
+ PARAMETRIZE {hasReplacement = TRUE;}
+ PARAMETRIZE {hasReplacement = FALSE;}
+
+ GIVEN {
+ ASSUME(P_GEN_8_POKEMON == TRUE);
+ ASSUME(gBattleMoves[MOVE_MEMENTO].effect == EFFECT_MEMENTO);
+ PLAYER(SPECIES_SQUIRTLE) {Speed(5); }
+ PLAYER(SPECIES_CHARMANDER) {Speed(5); }
+ PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR); Item(ITEM_IRON_BALL); Speed(5); } // Iron Ball, so that flying type Corviknight is affected by Sticky Web.
+ OPPONENT(SPECIES_CATERPIE) {Speed(7); }
+ OPPONENT(SPECIES_WEEDLE) {Speed(7); }
+ if (hasReplacement) {
+ OPPONENT(SPECIES_PIDGEY) {Speed(7); }
+ }
+
+ } WHEN {
+ TURN { MOVE(opponentLeft, MOVE_STICKY_WEB); }
+ if (hasReplacement) {
+ TURN { MOVE(opponentLeft, MOVE_MEMENTO, target:playerLeft); SEND_OUT(opponentLeft, 2); }
+ } else {
+ TURN { MOVE(opponentLeft, MOVE_MEMENTO, target:playerLeft);}
+ }
+ TURN { SWITCH(playerRight, 2); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponentLeft);
+ MESSAGE("A sticky web spreads out on the ground around your team!");
+
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_MEMENTO, opponentLeft);
+ MESSAGE("Foe Caterpie fainted!");
+ if (hasReplacement) {
+ MESSAGE("2 sent out Pidgey!");
+ }
+
+ MESSAGE("Go! Corviknigh!");
+ MESSAGE("Corviknigh was caught in a Sticky Web!");
+ ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR);
+ NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
+ } THEN {
+ if (hasReplacement) {
+ EXPECT_EQ(opponentLeft->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
+ }
+ EXPECT_EQ(playerLeft->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(playerRight->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
+ EXPECT_EQ(opponentRight->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
+ }
+}
diff --git a/test/move_effect_stockpile.c b/test/battle/move_effect/stockpile.c
similarity index 99%
rename from test/move_effect_stockpile.c
rename to test/battle/move_effect/stockpile.c
index b16408d49..0e540bb5d 100644
--- a/test/move_effect_stockpile.c
+++ b/test/battle/move_effect/stockpile.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
// These tests cover all 3 effects: Stockpile, Spit up and Swallow.
ASSUMPTIONS
diff --git a/test/move_effect_tailwind.c b/test/battle/move_effect/tailwind.c
similarity index 98%
rename from test/move_effect_tailwind.c
rename to test/battle/move_effect/tailwind.c
index 7dfffdbe0..2e9ee5ade 100644
--- a/test/move_effect_tailwind.c
+++ b/test/battle/move_effect/tailwind.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/battle/move_effect/teleport.c b/test/battle/move_effect/teleport.c
new file mode 100644
index 000000000..7544ae8a4
--- /dev/null
+++ b/test/battle/move_effect/teleport.c
@@ -0,0 +1,61 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ ASSUME(gBattleMoves[MOVE_TELEPORT].effect == EFFECT_TELEPORT);
+}
+
+SINGLE_BATTLE_TEST("Teleport fails when there is no pokemon to switch in")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_TELEPORT); }
+ } SCENE {
+ MESSAGE("But it failed!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Teleport fails when there no alive pokemon left")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WYNAUT) { HP(0); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_TELEPORT); }
+ } SCENE {
+ MESSAGE("But it failed!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Teleport forces the pokemon to switch out")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_TELEPORT); SEND_OUT(opponent, 1); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TELEPORT, opponent);
+ MESSAGE("2 sent out Wynaut!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Teleport does not fail if the user is trapped")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WYNAUT);
+ } WHEN {
+ TURN { MOVE(player, MOVE_FIRE_SPIN); MOVE(opponent, MOVE_TELEPORT); SEND_OUT(opponent, 1); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_FIRE_SPIN, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_TELEPORT, opponent);
+ MESSAGE("2 sent out Wynaut!");
+ }
+}
diff --git a/test/move_effect_torment.c b/test/battle/move_effect/torment.c
similarity index 98%
rename from test/move_effect_torment.c
rename to test/battle/move_effect/torment.c
index 43f05e29f..dbef1d983 100644
--- a/test/move_effect_torment.c
+++ b/test/battle/move_effect/torment.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_toxic.c b/test/battle/move_effect/toxic.c
similarity index 98%
rename from test/move_effect_toxic.c
rename to test/battle/move_effect/toxic.c
index f71d2a972..78d978b26 100644
--- a/test/move_effect_toxic.c
+++ b/test/battle/move_effect/toxic.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_toxic_spikes.c b/test/battle/move_effect/toxic_spikes.c
similarity index 99%
rename from test/move_effect_toxic_spikes.c
rename to test/battle/move_effect/toxic_spikes.c
index 44780da21..3be2d1627 100644
--- a/test/move_effect_toxic_spikes.c
+++ b/test/battle/move_effect/toxic_spikes.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_tri_attack.c b/test/battle/move_effect/tri_attack.c
similarity index 99%
rename from test/move_effect_tri_attack.c
rename to test/battle/move_effect/tri_attack.c
index 91b465067..aec711aab 100644
--- a/test/move_effect_tri_attack.c
+++ b/test/battle/move_effect/tri_attack.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_triple_kick.c b/test/battle/move_effect/triple_kick.c
similarity index 97%
rename from test/move_effect_triple_kick.c
rename to test/battle/move_effect/triple_kick.c
index e0a91b011..5ff47c840 100644
--- a/test/move_effect_triple_kick.c
+++ b/test/battle/move_effect/triple_kick.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_effect_venoshock.c b/test/battle/move_effect/venoshock.c
similarity index 97%
rename from test/move_effect_venoshock.c
rename to test/battle/move_effect/venoshock.c
index 134982a60..b2b8fcda2 100644
--- a/test/move_effect_venoshock.c
+++ b/test/battle/move_effect/venoshock.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/move_flag_three_strikes.c b/test/battle/move_flags/three_strikes.c
similarity index 98%
rename from test/move_flag_three_strikes.c
rename to test/battle/move_flags/three_strikes.c
index b597b4efe..794df10d3 100644
--- a/test/move_flag_three_strikes.c
+++ b/test/battle/move_flags/three_strikes.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Three-strike flag turns a move into a 3-hit move")
{
diff --git a/test/battle/status1/bad_poison.c b/test/battle/status1/bad_poison.c
new file mode 100644
index 000000000..7a430086b
--- /dev/null
+++ b/test/battle/status1/bad_poison.c
@@ -0,0 +1,39 @@
+#include "global.h"
+#include "test/battle.h"
+
+SINGLE_BATTLE_TEST("Bad poison deals 1/16th cumulative damage per turn")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ for (i = 0; i < 4; i++)
+ TURN {}
+ } SCENE {
+ s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
+ for (i = 0; i < 4; i++)
+ HP_BAR(player, damage: maxHP / 16 * (i + 1));
+ }
+}
+
+SINGLE_BATTLE_TEST("Bad poison cumulative damage resets on switch")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); }
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN {}
+ TURN {}
+ TURN { SWITCH(player, 1); }
+ TURN { SWITCH(player, 0); }
+ TURN {}
+ TURN {}
+ } SCENE {
+ s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
+ for (i = 0; i < 2; i++)
+ HP_BAR(player, damage: maxHP / 16 * (i + 1));
+ for (i = 0; i < 2; i++)
+ HP_BAR(player, damage: maxHP / 16 * (i + 1));
+ }
+}
diff --git a/test/battle/status1/burn.c b/test/battle/status1/burn.c
new file mode 100644
index 000000000..84439ec9e
--- /dev/null
+++ b/test/battle/status1/burn.c
@@ -0,0 +1,35 @@
+#include "global.h"
+#include "test/battle.h"
+
+SINGLE_BATTLE_TEST("Burn deals 1/16th damage per turn")
+{
+ GIVEN {
+ ASSUME(B_BURN_DAMAGE >= GEN_LATEST);
+ PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_BURN); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ for (i = 0; i < 4; i++)
+ TURN {}
+ } SCENE {
+ s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
+ for (i = 0; i < 4; i++)
+ HP_BAR(player, damage: maxHP / 16);
+ }
+}
+
+SINGLE_BATTLE_TEST("Burn reduces Attack by 50%", s16 damage)
+{
+ bool32 burned;
+ PARAMETRIZE { burned = FALSE; }
+ PARAMETRIZE { burned = TRUE; }
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { if (burned) Status1(STATUS1_BURN); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_TACKLE); }
+ } SCENE {
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
+ }
+}
diff --git a/test/battle/status1/freeze.c b/test/battle/status1/freeze.c
new file mode 100644
index 000000000..e3c53f790
--- /dev/null
+++ b/test/battle/status1/freeze.c
@@ -0,0 +1,45 @@
+#include "global.h"
+#include "test/battle.h"
+
+SINGLE_BATTLE_TEST("Freeze has a 20% chance of being thawed")
+{
+ PASSES_RANDOMLY(20, 100, RNG_FROZEN);
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_CELEBRATE); }
+ } SCENE {
+ STATUS_ICON(player, none: TRUE);
+ }
+}
+
+SINGLE_BATTLE_TEST("Freeze is thawed by opponent's Fire-type attacks")
+{
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE);
+ PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_EMBER); MOVE(player, MOVE_CELEBRATE); }
+ } SCENE {
+ MESSAGE("Foe Wobbuffet used Ember!");
+ MESSAGE("Wobbuffet was defrosted!");
+ STATUS_ICON(player, none: TRUE);
+ }
+}
+
+SINGLE_BATTLE_TEST("Freeze is thawed by user's Flame Wheel")
+{
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_FLAME_WHEEL].flags & FLAG_THAW_USER);
+ PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_FLAME_WHEEL); }
+ } SCENE {
+ MESSAGE("Wobbuffet was defrosted by Flame Wheel!");
+ STATUS_ICON(player, none: TRUE);
+ MESSAGE("Wobbuffet used Flame Wheel!");
+ }
+}
diff --git a/test/status_frostbite.c b/test/battle/status1/frostbite.c
similarity index 99%
rename from test/status_frostbite.c
rename to test/battle/status1/frostbite.c
index d2bfa6bba..2b61c4d65 100644
--- a/test/status_frostbite.c
+++ b/test/battle/status1/frostbite.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Frostbite reduces the special attack by 50 percent")
{
diff --git a/test/battle/status1/paralysis.c b/test/battle/status1/paralysis.c
new file mode 100644
index 000000000..b6edaf263
--- /dev/null
+++ b/test/battle/status1/paralysis.c
@@ -0,0 +1,44 @@
+#include "global.h"
+#include "test/battle.h"
+
+SINGLE_BATTLE_TEST("Paralysis reduces Speed by 50%")
+{
+ u16 playerSpeed;
+ bool32 playerFirst;
+ PARAMETRIZE { playerSpeed = 98; playerFirst = FALSE; }
+ PARAMETRIZE { playerSpeed = 102; playerFirst = TRUE; }
+ GIVEN {
+ ASSUME(B_PARALYSIS_SPEED >= GEN_7);
+ PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); Speed(playerSpeed); }
+ OPPONENT(SPECIES_WOBBUFFET) { Speed(50); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_CELEBRATE); }
+ } SCENE {
+ if (playerFirst) {
+ ONE_OF {
+ MESSAGE("Wobbuffet used Celebrate!");
+ MESSAGE("Wobbuffet is paralyzed! It can't move!");
+ }
+ MESSAGE("Foe Wobbuffet used Celebrate!");
+ } else {
+ MESSAGE("Foe Wobbuffet used Celebrate!");
+ ONE_OF {
+ MESSAGE("Wobbuffet used Celebrate!");
+ MESSAGE("Wobbuffet is paralyzed! It can't move!");
+ }
+ }
+ }
+}
+
+SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn")
+{
+ PASSES_RANDOMLY(25, 100, RNG_PARALYSIS);
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_CELEBRATE); }
+ } SCENE {
+ MESSAGE("Wobbuffet is paralyzed! It can't move!");
+ }
+}
diff --git a/test/battle/status1/poison.c b/test/battle/status1/poison.c
new file mode 100644
index 000000000..771d0d2ec
--- /dev/null
+++ b/test/battle/status1/poison.c
@@ -0,0 +1,17 @@
+#include "global.h"
+#include "test/battle.h"
+
+SINGLE_BATTLE_TEST("Poison deals 1/8th damage per turn")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ for (i = 0; i < 4; i++)
+ TURN {}
+ } SCENE {
+ s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
+ for (i = 0; i < 4; i++)
+ HP_BAR(player, damage: maxHP / 8);
+ }
+}
diff --git a/test/battle/status1/sleep.c b/test/battle/status1/sleep.c
new file mode 100644
index 000000000..8ef6137d3
--- /dev/null
+++ b/test/battle/status1/sleep.c
@@ -0,0 +1,23 @@
+#include "global.h"
+#include "test/battle.h"
+
+SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move")
+{
+ u32 turns;
+ PARAMETRIZE { turns = 1; }
+ PARAMETRIZE { turns = 2; }
+ PARAMETRIZE { turns = 3; }
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP_TURN(turns)); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ for (i = 0; i < turns; i++)
+ TURN { MOVE(player, MOVE_CELEBRATE); }
+ } SCENE {
+ for (i = 0; i < turns - 1; i++)
+ MESSAGE("Wobbuffet is fast asleep.");
+ MESSAGE("Wobbuffet woke up!");
+ STATUS_ICON(player, none: TRUE);
+ MESSAGE("Wobbuffet used Celebrate!");
+ }
+}
diff --git a/test/terrain_electric.c b/test/battle/terrain/electric.c
similarity index 99%
rename from test/terrain_electric.c
rename to test/battle/terrain/electric.c
index 78f490c42..cebb0cb75 100644
--- a/test/terrain_electric.c
+++ b/test/battle/terrain/electric.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Electric Terrain protects grounded battlers from falling asleep")
{
diff --git a/test/terrain_grassy.c b/test/battle/terrain/grassy.c
similarity index 99%
rename from test/terrain_grassy.c
rename to test/battle/terrain/grassy.c
index 21d64f61e..893a30528 100644
--- a/test/terrain_grassy.c
+++ b/test/battle/terrain/grassy.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Grassy Terrain recovers 1/16th HP at end of turn")
{
diff --git a/test/terrain_misty.c b/test/battle/terrain/misty.c
similarity index 99%
rename from test/terrain_misty.c
rename to test/battle/terrain/misty.c
index f60122075..dd6489af6 100644
--- a/test/terrain_misty.c
+++ b/test/battle/terrain/misty.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Misty Terrain protects grounded battlers from non-volatile status conditions")
{
diff --git a/test/terrain_psychic.c b/test/battle/terrain/psychic.c
similarity index 99%
rename from test/terrain_psychic.c
rename to test/battle/terrain/psychic.c
index f9f95a47d..c810af340 100644
--- a/test/terrain_psychic.c
+++ b/test/battle/terrain/psychic.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
SINGLE_BATTLE_TEST("Psychic Terrain protects grounded battlers from priority moves")
{
diff --git a/test/trainer_control.c b/test/battle/trainer_control.c
similarity index 99%
rename from test/trainer_control.c
rename to test/battle/trainer_control.c
index 2a21dcdf6..f5ca68e36 100644
--- a/test/trainer_control.c
+++ b/test/battle/trainer_control.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test.h"
+#include "test/test.h"
#include "battle.h"
#include "battle_main.h"
#include "data.h"
diff --git a/test/battle/type/grass.c b/test/battle/type/grass.c
new file mode 100644
index 000000000..b36813ac7
--- /dev/null
+++ b/test/battle/type/grass.c
@@ -0,0 +1,17 @@
+#include "global.h"
+#include "test/battle.h"
+
+SINGLE_BATTLE_TEST("Grass-type Pokémon block powder and spore moves")
+{
+ GIVEN {
+ ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER);
+ ASSUME(gSpeciesInfo[SPECIES_ODDISH].types[0] == TYPE_GRASS);
+ PLAYER(SPECIES_WYNAUT);
+ OPPONENT(SPECIES_ODDISH);
+ } WHEN {
+ TURN { MOVE(player, MOVE_STUN_SPORE); }
+ } SCENE {
+ NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
+ MESSAGE("It doesn't affect Foe Oddish…");
+ }
+}
diff --git a/test/weather_rain.c b/test/battle/weather/rain.c
similarity index 98%
rename from test/weather_rain.c
rename to test/battle/weather/rain.c
index b99681495..00ac26304 100644
--- a/test/weather_rain.c
+++ b/test/battle/weather/rain.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
// Please add Rain interactions with move, item and ability effects on their respective files.
ASSUMPTIONS
diff --git a/test/weather_snow.c b/test/battle/weather/snow.c
similarity index 99%
rename from test/weather_snow.c
rename to test/battle/weather/snow.c
index 93deb1432..1e8cb62cb 100644
--- a/test/weather_snow.c
+++ b/test/battle/weather/snow.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
ASSUMPTIONS
{
diff --git a/test/weather_sunlight.c b/test/battle/weather/sunlight.c
similarity index 98%
rename from test/weather_sunlight.c
rename to test/battle/weather/sunlight.c
index beba0e9b6..9d471332a 100644
--- a/test/weather_sunlight.c
+++ b/test/battle/weather/sunlight.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test_battle.h"
+#include "test/battle.h"
// Please add Sunlight interactions with move, item and ability effects on their respective files.
ASSUMPTIONS
diff --git a/test/move_effect_recoil_if_miss.c b/test/move_effect_recoil_if_miss.c
deleted file mode 100644
index 8a80309d7..000000000
--- a/test/move_effect_recoil_if_miss.c
+++ /dev/null
@@ -1,57 +0,0 @@
-#include "global.h"
-#include "test_battle.h"
-
-ASSUMPTIONS
-{
- ASSUME(gBattleMoves[MOVE_JUMP_KICK].effect == EFFECT_RECOIL_IF_MISS);
-}
-
-SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on miss")
-{
- s16 recoil;
- GIVEN {
- PLAYER(SPECIES_WOBBUFFET);
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- TURN { MOVE(player, MOVE_JUMP_KICK, hit: FALSE); }
- } SCENE {
- s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
- MESSAGE("Wobbuffet used Jump Kick!");
- MESSAGE("Wobbuffet's attack missed!");
- MESSAGE("Wobbuffet kept going and crashed!");
- HP_BAR(player, damage: maxHP / 2);
- }
-}
-
-SINGLE_BATTLE_TEST("Jump Kick has 50% recoil on protect")
-{
- s16 recoil;
- GIVEN {
- ASSUME(gBattleMoves[MOVE_JUMP_KICK].flags & FLAG_PROTECT_AFFECTED);
- PLAYER(SPECIES_WOBBUFFET);
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_JUMP_KICK, hit: FALSE); }
- } SCENE {
- s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
- ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
- NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_JUMP_KICK, player);
- HP_BAR(player, damage: maxHP / 2);
- }
-}
-
-SINGLE_BATTLE_TEST("Jump Kick has no recoil if no target")
-{
- GIVEN {
- ASSUME(B_HEALING_WISH_SWITCH >= GEN_5);
- PLAYER(SPECIES_WOBBUFFET);
- OPPONENT(SPECIES_WOBBUFFET);
- OPPONENT(SPECIES_WYNAUT);
- } WHEN {
- TURN { MOVE(opponent, MOVE_HEALING_WISH); MOVE(player, MOVE_JUMP_KICK, hit: FALSE); SEND_OUT(opponent, 1); }
- } SCENE {
- s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
- ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, opponent);
- NOT HP_BAR(player, damage: maxHP / 2);
- }
-}
diff --git a/test/powder_moves.c b/test/powder_moves.c
deleted file mode 100644
index a0185ad37..000000000
--- a/test/powder_moves.c
+++ /dev/null
@@ -1,47 +0,0 @@
-#include "global.h"
-#include "test_battle.h"
-
-SINGLE_BATTLE_TEST("Powder Moves are blocked by Grass Types")
-{
- GIVEN {
- ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER);
- ASSUME(gSpeciesInfo[SPECIES_ODDISH].types[0] == TYPE_GRASS);
- PLAYER(SPECIES_WYNAUT);
- OPPONENT(SPECIES_ODDISH);
- } WHEN {
- TURN { MOVE(player, MOVE_STUN_SPORE); }
- } SCENE {
- NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
- MESSAGE("It doesn't affect Foe Oddish…");
- }
-}
-
-SINGLE_BATTLE_TEST("Powder Moves are blocked by Ability Overcoat")
-{
- GIVEN {
- ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER);
- PLAYER(SPECIES_WYNAUT);
- OPPONENT(SPECIES_PINECO) { Ability(ABILITY_OVERCOAT); }
- } WHEN {
- TURN { MOVE(player, MOVE_STUN_SPORE); }
- } SCENE {
- ABILITY_POPUP(opponent, ABILITY_OVERCOAT);
- NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
- MESSAGE("It doesn't affect Foe Pineco…");
- }
-}
-
-SINGLE_BATTLE_TEST("Powder Moves are blocked by Item Safety Goggles")
-{
- GIVEN {
- ASSUME(gBattleMoves[MOVE_STUN_SPORE].flags & FLAG_POWDER);
- ASSUME(gItems[ITEM_SAFETY_GOGGLES].holdEffect == HOLD_EFFECT_SAFETY_GOGGLES);
- PLAYER(SPECIES_WYNAUT);
- OPPONENT(SPECIES_ABRA) { Item(ITEM_SAFETY_GOGGLES); }
- } WHEN {
- TURN { MOVE(player, MOVE_STUN_SPORE); }
- } SCENE {
- NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
- MESSAGE("Foe Abra is not affected thanks to its SafetyGoggles!");
- }
-}
diff --git a/test/random.c b/test/random.c
index a56e91a10..202933beb 100644
--- a/test/random.c
+++ b/test/random.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "test.h"
+#include "test/test.h"
#include "random.h"
TEST("RandomUniform generates lo..hi")
diff --git a/test/status1.c b/test/status1.c
deleted file mode 100644
index c244b9a60..000000000
--- a/test/status1.c
+++ /dev/null
@@ -1,193 +0,0 @@
-#include "global.h"
-#include "test_battle.h"
-
-SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move")
-{
- u32 turns;
- PARAMETRIZE { turns = 1; }
- PARAMETRIZE { turns = 2; }
- PARAMETRIZE { turns = 3; }
- GIVEN {
- PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP_TURN(turns)); }
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- for (i = 0; i < turns; i++)
- TURN { MOVE(player, MOVE_CELEBRATE); }
- } SCENE {
- for (i = 0; i < turns - 1; i++)
- MESSAGE("Wobbuffet is fast asleep.");
- MESSAGE("Wobbuffet woke up!");
- STATUS_ICON(player, none: TRUE);
- MESSAGE("Wobbuffet used Celebrate!");
- }
-}
-
-SINGLE_BATTLE_TEST("Poison deals 1/8th damage per turn")
-{
- GIVEN {
- PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); }
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- for (i = 0; i < 4; i++)
- TURN {}
- } SCENE {
- s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
- for (i = 0; i < 4; i++)
- HP_BAR(player, damage: maxHP / 8);
- }
-}
-
-SINGLE_BATTLE_TEST("Burn deals 1/16th damage per turn")
-{
- GIVEN {
- ASSUME(B_BURN_DAMAGE >= GEN_LATEST);
- PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_BURN); }
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- for (i = 0; i < 4; i++)
- TURN {}
- } SCENE {
- s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
- for (i = 0; i < 4; i++)
- HP_BAR(player, damage: maxHP / 16);
- }
-}
-
-SINGLE_BATTLE_TEST("Burn reduces Attack by 50%", s16 damage)
-{
- bool32 burned;
- PARAMETRIZE { burned = FALSE; }
- PARAMETRIZE { burned = TRUE; }
- GIVEN {
- PLAYER(SPECIES_WOBBUFFET) { if (burned) Status1(STATUS1_BURN); }
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- TURN { MOVE(player, MOVE_TACKLE); }
- } SCENE {
- HP_BAR(opponent, captureDamage: &results[i].damage);
- } FINALLY {
- EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
- }
-}
-
-SINGLE_BATTLE_TEST("Freeze has a 20% chance of being thawed")
-{
- PASSES_RANDOMLY(20, 100, RNG_FROZEN);
- GIVEN {
- PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- TURN { MOVE(player, MOVE_CELEBRATE); }
- } SCENE {
- STATUS_ICON(player, none: TRUE);
- }
-}
-
-SINGLE_BATTLE_TEST("Freeze is thawed by opponent's Fire-type attacks")
-{
- GIVEN {
- ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE);
- PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- TURN { MOVE(opponent, MOVE_EMBER); MOVE(player, MOVE_CELEBRATE); }
- } SCENE {
- MESSAGE("Foe Wobbuffet used Ember!");
- MESSAGE("Wobbuffet was defrosted!");
- STATUS_ICON(player, none: TRUE);
- }
-}
-
-SINGLE_BATTLE_TEST("Freeze is thawed by user's Flame Wheel")
-{
- GIVEN {
- ASSUME(gBattleMoves[MOVE_FLAME_WHEEL].flags & FLAG_THAW_USER);
- PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- TURN { MOVE(player, MOVE_FLAME_WHEEL); }
- } SCENE {
- MESSAGE("Wobbuffet was defrosted by Flame Wheel!");
- STATUS_ICON(player, none: TRUE);
- MESSAGE("Wobbuffet used Flame Wheel!");
- }
-}
-
-SINGLE_BATTLE_TEST("Paralysis reduces Speed by 50%")
-{
- u16 playerSpeed;
- bool32 playerFirst;
- PARAMETRIZE { playerSpeed = 98; playerFirst = FALSE; }
- PARAMETRIZE { playerSpeed = 102; playerFirst = TRUE; }
- GIVEN {
- ASSUME(B_PARALYSIS_SPEED >= GEN_7);
- PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); Speed(playerSpeed); }
- OPPONENT(SPECIES_WOBBUFFET) { Speed(50); }
- } WHEN {
- TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_CELEBRATE); }
- } SCENE {
- if (playerFirst) {
- ONE_OF {
- MESSAGE("Wobbuffet used Celebrate!");
- MESSAGE("Wobbuffet is paralyzed! It can't move!");
- }
- MESSAGE("Foe Wobbuffet used Celebrate!");
- } else {
- MESSAGE("Foe Wobbuffet used Celebrate!");
- ONE_OF {
- MESSAGE("Wobbuffet used Celebrate!");
- MESSAGE("Wobbuffet is paralyzed! It can't move!");
- }
- }
- }
-}
-
-SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn")
-{
- PASSES_RANDOMLY(25, 100, RNG_PARALYSIS);
- GIVEN {
- PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); }
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- TURN { MOVE(player, MOVE_CELEBRATE); }
- } SCENE {
- MESSAGE("Wobbuffet is paralyzed! It can't move!");
- }
-}
-
-SINGLE_BATTLE_TEST("Bad poison deals 1/16th cumulative damage per turn")
-{
- GIVEN {
- PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); }
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- for (i = 0; i < 4; i++)
- TURN {}
- } SCENE {
- s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
- for (i = 0; i < 4; i++)
- HP_BAR(player, damage: maxHP / 16 * (i + 1));
- }
-}
-
-SINGLE_BATTLE_TEST("Bad poison cumulative damage resets on switch")
-{
- GIVEN {
- PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_TOXIC_POISON); }
- PLAYER(SPECIES_WYNAUT);
- OPPONENT(SPECIES_WOBBUFFET);
- } WHEN {
- TURN {}
- TURN {}
- TURN { SWITCH(player, 1); }
- TURN { SWITCH(player, 0); }
- TURN {}
- TURN {}
- } SCENE {
- s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
- for (i = 0; i < 2; i++)
- HP_BAR(player, damage: maxHP / 16 * (i + 1));
- for (i = 0; i < 2; i++)
- HP_BAR(player, damage: maxHP / 16 * (i + 1));
- }
-}
diff --git a/test/test_runner.c b/test/test_runner.c
index d91ad02d1..637ff0ed8 100644
--- a/test/test_runner.c
+++ b/test/test_runner.c
@@ -2,11 +2,12 @@
#include "global.h"
#include "characters.h"
#include "gpu_regs.h"
+#include "load_save.h"
#include "main.h"
#include "malloc.h"
#include "random.h"
-#include "test.h"
#include "test_runner.h"
+#include "test/test.h"
#define TIMEOUT_SECONDS 55
@@ -114,6 +115,10 @@ void CB2_TestRunner(void)
return;
}
+ MoveSaveBlocks_ResetHeap();
+ ClearSav1();
+ ClearSav2();
+
gIntrTable[7] = Intr_Timer2;
// The current test restarted the ROM (e.g. by jumping to NULL).
diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c
index 91c00f6cc..80d1f5594 100644
--- a/test/test_runner_battle.c
+++ b/test/test_runner_battle.c
@@ -7,7 +7,7 @@
#include "main.h"
#include "malloc.h"
#include "random.h"
-#include "test_battle.h"
+#include "test/battle.h"
#include "window.h"
#include "constants/trainers.h"