26/01/26 Master to upcoming merge

This commit is contained in:
AlexOn1ine 2026-01-26 19:35:43 +01:00
commit f8266b828b
98 changed files with 2113 additions and 416 deletions

View File

@ -606,6 +606,33 @@
"contributions": [
"code"
]
},
{
"login": "Kasenn",
"name": "Kasenn",
"avatar_url": "https://avatars.githubusercontent.com/u/115586266?v=4",
"profile": "https://github.com/Kasenn",
"contributions": [
"code"
]
},
{
"login": "SabataLunar",
"name": "SabataLunar",
"avatar_url": "https://avatars.githubusercontent.com/u/26584469?v=4",
"profile": "https://github.com/SabataLunar",
"contributions": [
"design"
]
},
{
"login": "PacFire",
"name": "PacFire",
"avatar_url": "https://avatars.githubusercontent.com/u/108960850?v=4",
"profile": "https://github.com/PacFire",
"contributions": [
"design"
]
}
],
"contributorsPerLine": 7,

View File

@ -7,7 +7,7 @@ body:
value: |
Please fill in all fields with as many details as possible.
Once your bug is posted, make sure you and your collaborators are added to `CREDITS.md` by [tagging the bot on GitHub](https://github.com/rh-hideout/pokeemerald-expansion/wiki/CREDITS.md-Frequently-Asked-Questions). EVERY contribution matters, even reporting bugs!
- type: textarea
- type: textarea
id: description
attributes:
label: Description
@ -24,13 +24,13 @@ body:
description: |
What exact steps can somebody else follow in order to recreate the issue on their own?
placeholder: |
Provide as much context as possible as to what was done to create the issue.
Provide as much context as possible as to what was done to create the issue.
validations:
required: true
- type: textarea
id: media
id: media
attributes:
label: Images / Video
label: Images / Video
description: |
Do you have images or videos to show the problem happen?
placeholder: |

View File

@ -24,13 +24,13 @@ body:
description: |
What exact steps can somebody else follow in order to recreate the issue on their own?
placeholder: |
Provide as much context as possible as to what was done to create the issue.
Provide as much context as possible as to what was done to create the issue.
validations:
required: true
required: true
- type: textarea
id: media
id: media
attributes:
label: Images / Video
label: Images / Video
description: |
Do you have images or videos to show the problem happen?
placeholder: |

View File

@ -15,13 +15,13 @@ body:
description: |
What is the current behavior? What behavior would you expect your feature request to provide? What other information can you provide to help your feature get implemented?
placeholder: |
Provide as much context as possible.
Provide as much context as possible.
validations:
required: true
- type: textarea
id: media
attributes:
label: Images / Video
label: Images / Video
description: |
Have other projects or games solved this problem? Do you have images or video to show this happening?
placeholder: |

View File

@ -24,13 +24,13 @@ body:
description: |
What exact steps can somebody else follow in order to recreate the issue on their own?
placeholder: |
Provide as much context as possible as to what was done to create the issue.
Provide as much context as possible as to what was done to create the issue.
validations:
required: false
required: false
- type: textarea
id: media
id: media
attributes:
label: Images / Video
label: Images / Video
description: |
Do you have images or videos to show the problem happen?
placeholder: |

View File

@ -6,7 +6,7 @@ All contributions are encouraged and valued. Please make sure to read the releva
## Bug Reports
We use [GitHub](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3Abug) issues to track bugs.
We use [GitHub](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3Abug) issues to track bugs.
### What should I do before making a bug report?
@ -15,7 +15,7 @@ We use [GitHub](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sor
### How do I submit a bug report?
If you run into an issue with the project, open an [issue](https://github.com/rh-hideout/pokeemerald-expansion/issues/new).
If you run into an issue with the project, open an [issue](https://github.com/rh-hideout/pokeemerald-expansion/issues/new).
The best bug reports have enough information that we won't have to contact you for more information. We welcome all efforts to improve pokeemerald-expansion, but would be very grateful if you completed as much of the checklist as possible in your bug report. This will help other contributiors fix your issue.
@ -31,7 +31,7 @@ The best bug reports have enough information that we won't have to contact you f
This section guides you through submitting a feature request for pokeemerald-expansion, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions.
- We use [GitHub](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3Afeature-request) issues to track feature requests.
- We use [GitHub](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3Afeature-request) issues to track feature requests.
### What should I do before making a feature request?
@ -44,7 +44,7 @@ This section guides you through submitting a feature request for pokeemerald-exp
### How do I submit a feature request?
To request a feature to be added to the project, open a [feature request](https://github.com/rh-hideout/pokeemerald-expansion/issues/new).
To request a feature to be added to the project, open a [feature request](https://github.com/rh-hideout/pokeemerald-expansion/issues/new).
### What happens after I submit a feature request?
@ -108,11 +108,11 @@ git push --set-upstream origin newFeature
```
#### 7. Open Pull Request
Once your work is complete and pushed to the branch on Github, you can open a [pull request from your branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork), targeting the branch you've chosen from `pokeemerald-expansion`. Please fill out the pull request description as completely as possible.
Once your work is complete and pushed to the branch on Github, you can open a [pull request from your branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork), targeting the branch you've chosen from `pokeemerald-expansion`. Please fill out the pull request description as completely as possible.
### What happens after I submit a pull request?
A maintainer will then assign themselves as a reviewer of your pull request, and may provide feedback in the form of a PR review.
A maintainer will then assign themselves as a reviewer of your pull request, and may provide feedback in the form of a PR review.
Contributors are responsible for responding to and updating their branch by addressing the feedback in the review. Contributors are also responsible for making sure the branch passes the checklist at all times.

View File

@ -86,6 +86,11 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/izrofid"><img src="https://avatars.githubusercontent.com/u/206095739?v=4?s=100" width="100px;" alt="Kildemal"/><br /><sub><b>Kildemal</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=izrofid" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Skeli789"><img src="https://avatars.githubusercontent.com/u/17243618?v=4?s=100" width="100px;" alt="Skeli"/><br /><sub><b>Skeli</b></sub></a><br /><a href="#design-Skeli789" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://hufford.io"><img src="https://avatars.githubusercontent.com/u/8021794?v=4?s=100" width="100px;" alt="Josh Hufford"/><br /><sub><b>Josh Hufford</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=ostomachion" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Kasenn"><img src="https://avatars.githubusercontent.com/u/115586266?v=4?s=100" width="100px;" alt="Kasenn"/><br /><sub><b>Kasenn</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=Kasenn" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SabataLunar"><img src="https://avatars.githubusercontent.com/u/26584469?v=4?s=100" width="100px;" alt="SabataLunar"/><br /><sub><b>SabataLunar</b></sub></a><br /><a href="#design-SabataLunar" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PacFire"><img src="https://avatars.githubusercontent.com/u/108960850?v=4?s=100" width="100px;" alt="PacFire"/><br /><sub><b>PacFire</b></sub></a><br /><a href="#design-PacFire" title="Design">🎨</a></td>
</tr>
</tbody>
<tfoot>

View File

@ -4,7 +4,7 @@
<!-- If you want to re-record or change these gifs, here are some notes that I used: https://files.catbox.moe/05001g.md -->
**`pokeemerald-expansion`** is a GBA ROM hack base that equips developers with a comprehensive toolkit for creating Pokémon ROM hacks. **`pokeemerald-expansion`** is built on top of [pret's `pokeemerald`](https://github.com/pret/pokeemerald) decompilation project. **It is not a playable Pokémon game on its own.**
**`pokeemerald-expansion`** is a GBA ROM hack base that equips developers with a comprehensive toolkit for creating Pokémon ROM hacks. **`pokeemerald-expansion`** is built on top of [pret's `pokeemerald`](https://github.com/pret/pokeemerald) decompilation project. **It is not a playable Pokémon game on its own.**
# [Features](FEATURES.md)
@ -30,7 +30,7 @@ Please consider [crediting all contributors](CREDITS.md) involved in the project
# [Getting Started](INSTALL.md)
❗❗ **Important**: Do not use GitHub's "Download Zip" option as it will not include commit history. This is necessary if you want to update or merge other feature branches.
❗❗ **Important**: Do not use GitHub's "Download Zip" option as it will not include commit history. This is necessary if you want to update or merge other feature branches.
If you're new to git and GitHub, [Team Aqua's Asset Repo](https://github.com/Pawkkie/Team-Aquas-Asset-Repo/) has a [guide to forking and cloning the repository](https://github.com/Pawkkie/Team-Aquas-Asset-Repo/wiki/The-Basics-of-GitHub). Then you can follow one of the following guides:

View File

@ -15,7 +15,7 @@
.macro voice_group label:req, starting_note
.align 2
.ifb \starting_note
.global voicegroup_\label
.global voicegroup_\label
voicegroup_\label:
.else
.set voicegroup_\label, . - \starting_note * 0xC

View File

@ -9,7 +9,7 @@ SOUND_BIN_DIR := sound
# Needs to recompile for B_NUM_LOW_HEALTH_BEEPS in battle.h
EXPANSION_BATTLE_CONFIG := include/config/battle.h
SPECIAL_OUTDIRS := $(MID_ASM_DIR) $(CRY_BIN_DIR)
SPECIAL_OUTDIRS := $(MID_ASM_DIR) $(CRY_BIN_DIR)
SPECIAL_OUTDIRS += $(SOUND_BIN_DIR) $(SOUND_BIN_DIR)/direct_sound_samples/phonemes $(SOUND_BIN_DIR)/direct_sound_samples/cries
$(shell mkdir -p $(SPECIAL_OUTDIRS) )
@ -25,11 +25,11 @@ $(CRY_BIN_DIR)/%.bin: $(CRY_SUBDIR)/%.wav
$(WAV2AGB) -b -c -l 1 --no-pad $< $@
# Uncompressed cries
$(CRY_BIN_DIR)/uncomp_%.bin: $(CRY_SUBDIR)/uncomp_%.aif
$(CRY_BIN_DIR)/uncomp_%.bin: $(CRY_SUBDIR)/uncomp_%.aif
$(AIF) $< $@
# Uncompressed sounds
$(SOUND_BIN_DIR)/%.bin: sound/%.wav
$(SOUND_BIN_DIR)/%.bin: sound/%.wav
$(WAV2AGB) -b $< $@
# For each line in midi.cfg, we do some trickery to convert it into a make rule for the `.mid` file described on the line

View File

@ -1024,24 +1024,59 @@ BattleScript_VCreateStatLossRet:
BattleScript_EffectPartingShot::
attackcanceler
jumpifstat BS_TARGET, CMP_GREATER_THAN, STAT_ATK, MIN_STAT_STAGE, BattleScript_EffectPartingShotTryAtk
jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPATK, MIN_STAT_STAGE, BattleScript_CantLowerMultipleStats
jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPATK, MIN_STAT_STAGE, BattleScript_EffectPartingShotCantLowerMultipleStats
BattleScript_EffectPartingShotTryAtk:
accuracycheck BattleScript_MoveMissedPause
attackanimation
waitanimation
setbyte sB_ANIM_TARGETS_HIT, 0
setstatchanger STAT_ATK, 1, TRUE
statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_EffectPartingShotTrySpAtk, BIT_SPATK
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
call BattleScript_EffectPartingShotMaybePrintStat
BattleScript_EffectPartingShotTrySpAtk:
setstatchanger STAT_SPATK, 1, TRUE
statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_EffectPartingShotSwitch
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_EffectPartingShotMaybeSwitch
call BattleScript_EffectPartingShotMaybePrintStat
BattleScript_EffectPartingShotMaybeSwitch:
jumpifgenconfiglowerthan CONFIG_PARTING_SHOT_SWITCH, GEN_7, BattleScript_EffectPartingShotSwitch
jumpifbyte CMP_NOT_EQUAL, sB_ANIM_TARGETS_HIT, 0, BattleScript_EffectPartingShotSwitch
goto BattleScript_MoveEnd
BattleScript_EffectPartingShotSwitch:
moveendall
goto BattleScript_MoveSwitchPursuitEnd
BattleScript_EffectPartingShotCantLowerMultipleStats:
pause B_WAIT_TIME_SHORT
setmoveresultflags MOVE_RESULT_FAILED
call BattleScript_EffectPartingShotPrintWontDecrease
setbyte sB_ANIM_TARGETS_HIT, 0
goto BattleScript_EffectPartingShotMaybeSwitch
BattleScript_EffectPartingShotMaybePrintStat:
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_ATTACKER_STAT_CHANGED, BattleScript_EffectPartingShotPrintStat
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_DEFENDER_STAT_CHANGED, BattleScript_EffectPartingShotPrintStat
return
BattleScript_EffectPartingShotPrintStat:
setbyte sB_ANIM_TARGETS_HIT, 1
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
return
BattleScript_EffectPartingShotPrintWontDecrease:
jumpifability BS_TARGET, ABILITY_CONTRARY, BattleScript_EffectPartingShotPrintWontDecreaseContrary
printstring STRINGID_STATSWONTDECREASE2
waitmessage B_WAIT_TIME_LONG
return
BattleScript_EffectPartingShotPrintWontDecreaseContrary:
swapattackerwithtarget
printstring STRINGID_STATSWONTDECREASE2
waitmessage B_WAIT_TIME_LONG
swapattackerwithtarget
return
BattleScript_EffectPowder::
attackcanceler
jumpifvolatile BS_TARGET, VOLATILE_POWDER, BattleScript_ButItFailed
@ -1076,6 +1111,7 @@ BattleScript_EffectAromaticMistWontGoHigher:
BattleScript_EffectMagneticFlux::
attackcanceler
savetarget
setbyte gBattleCommunication, 0
BattleScript_EffectMagneticFluxStart:
jumpifability BS_TARGET, ABILITY_MINUS, BattleScript_EffectMagneticFluxCheckStats
@ -1104,8 +1140,10 @@ BattleScript_EffectMagneticFluxTrySpDef:
waitmessage B_WAIT_TIME_LONG
BattleScript_EffectMagneticFluxLoop:
jumpifbytenotequal gBattlerTarget, gBattlerAttacker, BattleScript_EffectMagneticFluxEnd
jumpifnoally BS_ATTACKER, BattleScript_EffectMagneticFluxEnd
setallytonexttarget BattleScript_EffectMagneticFluxStart
BattleScript_EffectMagneticFluxEnd:
restoretarget
jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0, BattleScript_MoveEnd
goto BattleScript_ButItFailed
@ -1140,6 +1178,7 @@ BattleScript_EffectGearUpTrySpAtk:
waitmessage B_WAIT_TIME_LONG
BattleScript_EffectGearUpLoop:
jumpifbytenotequal gBattlerTarget, gBattlerAttacker, BattleScript_EffectGearUpEnd
jumpifnoally BS_ATTACKER, BattleScript_EffectGearUpEnd
setallytonexttarget BattleScript_EffectGearUpStart
BattleScript_EffectGearUpEnd:
restoretarget
@ -2148,6 +2187,9 @@ BattleScript_TryTailwindAbilitiesLoop_WindPower:
BattleScript_EffectMiracleEye::
attackcanceler
accuracycheck BattleScript_MoveMissedPause
jumpifgenconfiglowerthan CONFIG_MIRACLE_EYE_FAIL, GEN_5, BattleScript_MiracleEyeSet
jumpifvolatile BS_TARGET, VOLATILE_MIRACLE_EYE, BattleScript_ButItFailed
BattleScript_MiracleEyeSet:
setvolatile BS_TARGET, VOLATILE_MIRACLE_EYE
goto BattleScript_IdentifiedFoe
@ -3210,7 +3252,12 @@ BattleScript_EffectSpikes::
BattleScript_EffectForesight::
attackcanceler
accuracycheck BattleScript_ButItFailed
jumpifgenconfiglowerthan CONFIG_FORESIGHT_FAIL, GEN_3, BattleScript_ForesightFailCheck
jumpifgenconfiglowerthan CONFIG_FORESIGHT_FAIL, GEN_5, BattleScript_ForesightSet
BattleScript_ForesightFailCheck:
jumpifvolatile BS_TARGET, VOLATILE_FORESIGHT, BattleScript_ButItFailed
BattleScript_ForesightSet:
setvolatile BS_TARGET, VOLATILE_FORESIGHT
BattleScript_IdentifiedFoe:
attackanimation

View File

@ -64,7 +64,7 @@ When describing a system/component in-depth, use block comment syntax.
```
When briefly describing a function or block of code, use a single-line comments
placed on its own line.
placed on its own line.
There should be a single space directly to the right of `//`.
```c
@ -453,7 +453,7 @@ All other configs should be off.
### Save Philosophy
Until [save migration](https://discord.com/channels/419213663107416084/1108733346864963746) is implemented, branches will only merged in if they do not forcefully break existing game saves.
Until [save migration](https://discord.com/channels/419213663107416084/1108733346864963746) is implemented, branches will only merged in if they do not forcefully break existing game saves.
When `pokemeerald-expansion` gets to a point where new functionality will require that we break saves, we will merge as many [save-breaking features](https://discord.com/channels/419213663107416084/1202774957776441427) together as possible, and increment the major version number of the project.

View File

@ -53,8 +53,8 @@
* Fixed text width for a lot of forms in HGSS Dex by @AsparagusEduardo in [#7035](https://github.com/rh-hideout/pokeemerald-expansion/pull/7035)
* Fixes Roamers not saving shininess by @i0brendan0 in [#7185](https://github.com/rh-hideout/pokeemerald-expansion/pull/7185)
* [FIX] Prevent caught Pokémon loss in NPC partner battles by @J2M2 in [#7177](https://github.com/rh-hideout/pokeemerald-expansion/pull/7177)
## ⚔️ Battle General ⚔️
### Changed
@ -72,8 +72,8 @@
<!-- Add any important details for the release changelog. Must be structed as bullet points. --->
<!--- Remove this section if not applicable. --->
* Chloroblast causing recoil damage even if the move fails to connect by @LinathanZel in #7007
* [DRAFT] Fix Normalize not boosting Normal type moves if they were already Normal type by @i0brendan0 in [#7060](https://github.com/rh-hideout/pokeemerald-expansion/pull/7060)
* Fixes freeze during a 1v2 double battle by @AlexOn1ine in [#7075](https://github.com/rh-hideout/pokeemerald-expansion/pull/7075)
* Fixes Pursuit potentially causing both battlers to switch into the same mon by @PhallenTree in [#7084](https://github.com/rh-hideout/pokeemerald-expansion/pull/7084)
@ -93,8 +93,8 @@
## 🤹 Moves 🤹
### Changed
* Fix ScaryFace anim for Bitter Malice by @TLM-PsIQ in [#6476](https://github.com/rh-hideout/pokeemerald-expansion/pull/6476)
### Fixed
* Fix savage spin out spider web template by @ghoulslash in [#7137](https://github.com/rh-hideout/pokeemerald-expansion/pull/7137)

View File

@ -32,7 +32,7 @@
* Add new actions to Debug Menu by @FosterProgramming in [#7837](https://github.com/rh-hideout/pokeemerald-expansion/pull/7837)
- Adds an action to change a Pokemon ability in the party side of the Debug Menu
- Adds an action to set the friendship of a Pokemon in the party side of the Debug Menu
* Display TM/HM's move name in the debug menu by @estellarc in [#7994](https://github.com/rh-hideout/pokeemerald-expansion/pull/7994)
### Changed

View File

@ -121,7 +121,7 @@
* Fixed message for switch out moves by @kittenchilly in [#5258](https://github.com/rh-hideout/pokeemerald-expansion/pull/5258)
* Fixed Ice Fang's descriptions using the opposite of what they're supposed to do based on `B_USE_FROSTBITE` by @laserXdolphin in [#5273](https://github.com/rh-hideout/pokeemerald-expansion/pull/5273)
* Fixes to Instruct by @PhallenTree in [#5262](https://github.com/rh-hideout/pokeemerald-expansion/pull/5262)
* Fixed Instruct bypassing AtkCanceler checks (Instruct allowed the target to act while asleep, flinched, etc.) and its interaction with First Turn Only moves (Fake Out, First Impression, Mat Block).
* Fixed Instruct bypassing AtkCanceler checks (Instruct allowed the target to act while asleep, flinched, etc.) and its interaction with First Turn Only moves (Fake Out, First Impression, Mat Block).
* Fixed Instruct's animation using the attacker and target of the called move.
* Fixed Scale Shot's effect not activating if the opponent fainted before all hits finished by @AlexOn1ine in [#5292](https://github.com/rh-hideout/pokeemerald-expansion/pull/5292)
* Fixed Round not preserving turn order for non-Round users if there's a switch out at the beginning of the turn by @AlexOn1ine in [#5292](https://github.com/rh-hideout/pokeemerald-expansion/pull/5292)

View File

@ -4,7 +4,7 @@
## 1.- Autogenerating a changelog for the `master` branch.
*Requires Write access to the repo.*
If the changelog you're making is for a minor version (Eg. 1.3.0), make sure to sync the `upcoming` branch with `master` before starting. Keep in mind that if there are unreleased changes in `master`, they should be made into a patch version released alongside minor version.
- Go to https://github.com/rh-hideout/pokeemerald-expansion/releases.
- Press the option "Draft a new release".
@ -178,7 +178,7 @@ With this, the repo is ready again to receive new PRs.
- Requires role to post in #announcements channel.
- [Team Aqua's Hideout](https://discord.gg/team-aqua-s-hideout-976252009114140682)
- Requires role to post in #romhacking-updates channel.
- [What a Hack!](https://discord.gg/whack-a-hack-292436944670162955)
- [What a Hack!](https://discord.gg/whack-a-hack-292436944670162955)
- Announcements are done in Spanish, but not the changelogs themselves.
- Requires role to ping "Decompilaciones" role.
- [pret](https://discord.gg/R4c3FA95dP)

View File

@ -18,7 +18,7 @@ Contributors are asked to make sure tests pass locally, but maintainers should a
## Have you verified that the functionality works in game without any problems?
If functionality cannot be verified with an automated test, proof of an in game test is required. Do not be afraid to reach out to the contributor or the community to make sure something works in game as it should.
## If the branch ports behavior from another Pokémon game, have you verified that the behavior functions as faithfully as possible?
## If the branch ports behavior from another Pokémon game, have you verified that the behavior functions as faithfully as possible?
We have always tried to make sure we can mimic the original functionality as closely as possible so as to avoid confusion with users and players. Do not be afraid to ask the contributor / community for proof if you cannot personally verify.
## If the branch is a popular feature within the community with an established feature branch, is this using that established branch as a base?
@ -28,7 +28,7 @@ There are situations where this should and should not happen, and should be disc
Not everything needs a migration script - if you're unsure, start a discussion.
## Should new functionality introduced by this branch be gated behind a config?
We don't have a strict definition of when configs should be used, but you can start with
We don't have a strict definition of when configs should be used, but you can start with
> Why SHOULDN'T this be a config?

View File

@ -13,10 +13,10 @@ PRs with the Github label [`type: big feature`](https://github.com/rh-hideout/po
### Merge Freeze (14 days to the next Minor Release)
Pull Requests that **DO NOT** have one of the following Github labels:
- [`bugfix`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3Abugfix)
- [`type: cleanup`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3A%22type%3A+cleanup%22+)
- [`type: credits`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3A%22type%3A+credits%22+)
- [`type: documentation`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3A%22type%3A+documentation%22+)
- [`bugfix`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3Abugfix)
- [`type: cleanup`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3A%22type%3A+cleanup%22+)
- [`type: credits`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3A%22type%3A+credits%22+)
- [`type: documentation`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3A%22type%3A+documentation%22+)
- [`category: battle-tests`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+is%3Aopen+label%3A%22category%3A+battle-tests%22)
will NOT be merged until after the next Minor Release.
@ -36,7 +36,7 @@ will NOT be merged until after the next Minor Release.
## What is a "Big Feature"?
* If the original owner of the PR thinks a feature should be labeled a Big Feature, it is, no questions asked
* If a reviewer thinks a PR is a Big Feature, then it is
* If the two disagree, it can be discussed in a PR thread, and can ultimately be resolved with a Maintainer vote.
* If the two disagree, it can be discussed in a PR thread, and can ultimately be resolved with a Maintainer vote.
### How To Identify a Big Feature
* **Big diffs**: It's easy for something to go unnoticed in review when it's a tiny part of a massive diff.

View File

@ -21,8 +21,11 @@ A pull request meets the scope criteria if:
1. **SS Species**: Adds Species that have appeared in a Showdown-supported title. Includes follower sprites for all defined species including battle-only ones (ie. Megas)
2. **SS Moves**: Adds Moves and Move Animations that have appeared in a Showdown-supported title
3. **SS Abilities**: Adds Abilities that have appeared in a Showdown-supported title
4. **SS Items**: Adds Items that have appeared in a Showdown-supported title
5. **SS Gimmicks**: Adds Gimmicks that have appeared in a Showdown-supported title (Dynamax, Mega Evolution, etc.)
4. **SS Items**: Items that meet ALL the following requirements are in scope:
- Has appeared in a Showdown supported title
- Is mechanically / functionally unique from existing items (ie. not Relic Crown, Silver Leaf), with the exception of items with in-battle functionality (ie. Lumiose Gallette)
- Do not ONLY exist for story related purpose (ie. Jade Orb)
5. **SS Gimmicks**: Adds Gimmicks that have appeared in a Showdown-supported title (Dynamax, Mega Evolution, etc.)
6. **SS Battle Types**: Adds Special Battle Types that have appeared in a Showdown-supported title (Triple battles, etc.)
7. **SS Battle Mechanics**: Adds mechanical battle changes that have appeared in a Showdown-supported title, and allow developers to choose which generation suits them where applicable
8. **Battle AI Behaviour**: Improvements towards the capability of a human competitive player, and unique or interesting behaviours otherwise

View File

@ -29,13 +29,13 @@ This section lists all of expansions AI Flags and briefly describes the effec
## Composite AI Flags
Expansion has a few "composite" AI flags. This means that these flags have no unique functionality themselves, and can instead be thought of as groups of other flags that are all enabled when this flag is enabled. The idea behind these flags is that if you don't care to manage the detailed behaviour of a particular trainer, you can use these as a baseline instead, and expansion will keep them updated for you.
Expansion has a few "composite" AI flags. This means that these flags have no unique functionality themselves, and can instead be thought of as groups of other flags that are all enabled when this flag is enabled. The idea behind these flags is that if you don't care to manage the detailed behaviour of a particular trainer, you can use these as a baseline instead, and expansion will keep them updated for you.
`AI_FLAG_BASIC_TRAINER` is expansion's version of generic, normal AI behaviour. It includes `AI_FLAG_CHECK_BAD_MOVE` (don't use bad moves), `AI_FLAG_TRY_TO_FAINT` (faint the player where possible), and `AI_FLAG_CHECK_VIABILITY` (choose the most effective move to use in the current context). Trainers with this flag will still be smarter than they are in vanilla as there have been dramatic improvements made to move selection, but not incredibly so. Trainers with this flag should feel like normal trainers. In general we recommend these three flags be used in all cases, unless you specifically want a trainer who makes obvious mistakes in battle.
`AI_FLAG_SMART_TRAINER` is expansion's version of a "smart AI". It includes everything in `AI_FLAG_BASIC_TRAINER` along with `AI_FLAG_SMART_SWITCHING` (make smart decisions about when to switch), `AI_FLAG_SMART_MON_CHOICES` (make smart decisions about what mon to send in after a switch / KO), `AI_FLAG_OMNISCIENT` (awareness of what moves, items, and abilities the player's mons have to better inform decisions), and `AI_FLAG_SMART_TERA` (make smart decisions about when to terastalize). Expansion will keep this updated to represent the most objectively intelligent behaviour our flags are capable of producing.
`AI_FLAG_PREDICTION` will enable all of the prediction flags at once, so the AI can perform as well as possible. It is best paired with the flags in `AI_FLAG_SMART_TRAINER` for optimal behaviour. This currently includes `AI_FLAG_PREDICT_SWITCH` and `AI_FLAG_PREDICT_INCOMING_MON`, but will likely be expanded in the future.
`AI_FLAG_PREDICTION` will enable all of the prediction flags at once, so the AI can perform as well as possible. It is best paired with the flags in `AI_FLAG_SMART_TRAINER` for optimal behaviour. This currently includes `AI_FLAG_PREDICT_SWITCH` and `AI_FLAG_PREDICT_INCOMING_MON`, but will likely be expanded in the future.
Expansion has LOADS of flags, which will be covered in the rest of this guide. If you don't want to engage with detailed trainer AI tuning though, you can just use these two composite flags, and trust that expansion will keep their contents updated to always represent the most standard and the smartest behaviour we can.

View File

@ -12,7 +12,7 @@ If you intend to use vanilla maps and have not already edited them, revert commi
If you _have_ edited vanilla maps, the merge conflicts from reverting that commit will cause problems. If you are using vanilla maps, manually copy some of the tileset changes, `.pal`, and `.pla` files in your branch, and begin rebuilding your metatiles to have windows use the palettes that have a `.pla` for light blending the correct color slots. [Triple-layer metatiles](https://github.com/pret/pokeemerald/wiki/Triple-layer-metatiles) are highly recommended.
WARNING: [As per issue #7034](https://github.com/rh-hideout/pokeemerald-expansion/issues/7034) if you follow this tutorial reverting the previously mentioned commit to use the updated palettes in the Hoenn maps and *after* that you try to follow the [Triple-layer metatiles tutorial](https://github.com/pret/pokeemerald/wiki/Triple-layer-metatiles), you'll encounter an issue when running the script to update old tilesets to support triple-layer metatiles.
WARNING: [As per issue #7034](https://github.com/rh-hideout/pokeemerald-expansion/issues/7034) if you follow this tutorial reverting the previously mentioned commit to use the updated palettes in the Hoenn maps and *after* that you try to follow the [Triple-layer metatiles tutorial](https://github.com/pret/pokeemerald/wiki/Triple-layer-metatiles), you'll encounter an issue when running the script to update old tilesets to support triple-layer metatiles.
Follow the band-aid fix proposed in that issue after this tutorial but before following the triple-layer metatiles tutorial if you want everythign to work properly with light-blended palettes and triple-layer metatiles together.
You will also want to add the lighting object events from that commit.

View File

@ -293,7 +293,7 @@ MysteryGift_EventScript_Celebi::
end
```
Walking through this, it's clear we'll need some more scripting. We first check if Celebi's corresponding Mystery Gift flag has been set, and if it has, we need to tell the player they've already redeemed it and can't again. If they haven't though, we get ourselves setup for the givemon, do the givemon, and set the mystery gift flag. Then we need soem more generic handling to prompt nicknaming and some fanfare.
Walking through this, it's clear we'll need some more scripting. We first check if Celebi's corresponding Mystery Gift flag has been set, and if it has, we need to tell the player they've already redeemed it and can't again. If they haven't though, we get ourselves setup for the givemon, do the givemon, and set the mystery gift flag. Then we need soem more generic handling to prompt nicknaming and some fanfare.
Two things, then; an event script to handle the case where a mystery gift mon has already been redeemed, and an event script to handle when a mystery gift mon has successfully been received.
@ -327,13 +327,13 @@ Almost done! Just need to handle the specific nicknaming scripts, and then add a
MysteryGift_EventScript_NicknamePartyMon::
msgbox gText_NicknameThisPokemon, MSGBOX_YESNO
goto_if_eq VAR_RESULT, NO, MysteryGift_EventScript_Exit
call Common_EventScript_GetGiftMonPartySlot
call Common_EventScript_NameReceivedPartyMon
call Common_EventScript_GetGiftMonPartySlot
call Common_EventScript_NameReceivedPartyMon
goto MysteryGift_EventScript_Exit
end
MysteryGift_EventScript_NicknamePCMon::
msgbox gText_NicknameThisPokemon, MSGBOX_YESNO
msgbox gText_NicknameThisPokemon, MSGBOX_YESNO
goto_if_eq VAR_RESULT, NO, MysteryGift_EventScript_TransferredToPC
call Common_EventScript_NameReceivedBoxMon
call Common_EventScript_TransferredToPC

View File

@ -1,5 +1,5 @@
# How to Use Follower NPCs
*Written by Bivurnum*
*Written by Bivurnum*
*gif by ghoulslash*
![follower-npc](img/follower_npc/follower-npc.gif)
@ -11,22 +11,22 @@ The configs for follower NPCs can be found in [include/config/follower_npc.h](ht
* `FNPC_FLAG_HEAL_AFTER_FOLLOWER_BATTLE`: The player's party can be automatically healed after every partner battle. Set it to a flag to toggle it on/off with scripts, or set it to `FNPC_ALWAYS` to have it happen every time.
* `FNPC_FLAG_PARTNER_WILD_BATTLES`: The battle partner can join the player for wild battles. Set it to a flag to toggle it on/off with scripts, or set it to `FNPC_ALWAYS` to have it happen every time.
* `FNPC_NPC_FOLLOWER_WILD_BATTLE_VS_2`: Wild battles with a battle partner default to two wild Pokémon appearing. You can set this to `FALSE` to make only one wild Pokémon appear.
* `FNPC_NPC_FOLLOWER_PARTY_PREVIEW`: By default, a preview of the player's and partner's teams will be shown at the start of every trainer battle. Set this to `FALSE` to disable this feature.
* `FNPC_NPC_FOLLOWER_PARTY_PREVIEW`: By default, a preview of the player's and partner's teams will be shown at the start of every trainer battle. Set this to `FALSE` to disable this feature.
* `FNPC_FACE_NPC_FOLLOWER_ON_DOOR_EXIT`: If `TRUE` the player will turn to face the follower when they exit a doorway.
* `FNPC_NPC_FOLLOWER_SHOW_AFTER_LEAVE_ROUTE`: If `TRUE` the follower will walk out of the player automatically after using Fly, Teleport, or Escape Rope.
## Set the Follower
The `setfollowernpc` macro will turn the specified object into an NPC follower. It requires the object id, the [follower flags](#follower-flags), and optionally a custom script and a [battle partner](#battle-partner). If you do not include a custom script name (or you set it to `0`), the NPC follower will default to their normal interaction script. If there is a follower Pokémon present, it will be returned to its Pokeball until the NPC follower is destroyed.
Here's an example:
`setfollowernpc 3, FNPC_ALL, MyScript_Eventscript_CustomFollowerScript, PARTNER_STEVEN`
Here's an example:
`setfollowernpc 3, FNPC_ALL, MyScript_Eventscript_CustomFollowerScript, PARTNER_STEVEN`
This would turn object number 3 on the current map into an NPC follower, give them access to all following behaviors, run that custom script when the player interacts with them, and adds the Steven battle partner to the follower ([more on this later](#battle-partner)).
The object ***MUST*** have an event flag or the NPC follower will not be created!
## Create a Follower
The `createfollowernpc` macro will create a new follower without needing to convert an existing NPC. It works similarly to `setfollowernpc`, but instead of providing an object id, you give it a GFX id. For example, if you wanted to create a follower with the May sprite, you could do something like this:
`createfollowernpc OBJ_EVENT_GFX_RIVAL_MAY_NORMAL, FNPC_ALL, EventScript_MayFollow`
The `createfollowernpc` macro will create a new follower without needing to convert an existing NPC. It works similarly to `setfollowernpc`, but instead of providing an object id, you give it a GFX id. For example, if you wanted to create a follower with the May sprite, you could do something like this:
`createfollowernpc OBJ_EVENT_GFX_RIVAL_MAY_NORMAL, FNPC_ALL, EventScript_MayFollow`
The created follower NPC will initially be invisible until the player takes a step.
## Follower Flags

View File

@ -908,7 +908,7 @@ _NOTE: At the top of this file, you will probably see this warning:_
// DO NOT MODIFY THIS FILE! It is auto-generated from tools/learnset_helpers/teachable.py`
//
```
From version 1.9 onwards, pokeemerald-expansion includes a tool called the learnset helper, which aims to automate the generation of valid teachable moves. At the time of writing, this tool only supports generating TM and Tutor learnsets. However, in the future it may be expanded to deal with level up learnsets and egg moves.
From version 1.9 onwards, pokeemerald-expansion includes a tool called the learnset helper, which aims to automate the generation of valid teachable moves. At the time of writing, this tool only supports generating TM and Tutor learnsets. However, in the future it may be expanded to deal with level up learnsets and egg moves.
Ignore the warning shown above the first time you're adding your teachable moves (as otherwise the compiler will complain about the array not existing), but in the future (if you're using the learnset helper) simply edit what teachable moves your Pokémon can learn in one of the JSON files found in `tools/learnset_helpers/porymoves_files`. It doesn't really matter which one you add your new Pokémon to, as the tool pulls from all of the files in this folder.

View File

@ -47,7 +47,7 @@ enum TrainerSlideType
{
TRAINER_SLIDE_BEFORE_FIRST_TURN,
TRAINER_SLIDE_PLAYER_LANDS_FIRST_CRITICAL_HIT,
+ TRAINER_SLIDE_ENEMY_LANDS_FIRST_CRITICAL_HIT, // Each Trainer Slide has a unqiue id.
+ TRAINER_SLIDE_ENEMY_LANDS_FIRST_CRITICAL_HIT, // Each Trainer Slide has a unqiue id.
TRAINER_SLIDE_PLAYER_LANDS_FIRST_SUPER_EFFECTIVE_HIT,
TRAINER_SLIDE_PLAYER_LANDS_FIRST_STAB_MOVE,
TRAINER_SLIDE_PLAYER_LANDS_FIRST_DOWN,
@ -82,7 +82,7 @@ bool32 IsTrainerSlideInitialized(enum TrainerSlideType);
```diff
return IsTrainerSlideInitialized(slideId);
}
+static bool32 ShouldRunTrainerSlideEnemyLandsFirstCriticalHit(enum TrainerSlideType slideId)
+{
+ return IsTrainerSlideInitialized(slideId);
@ -110,7 +110,7 @@ The function that determines if a Slide should play has different function for m
```diff
InitalizeTrainerSlide(slideId);
}
+void TryInitializeTrainerSlideEnemyLandsFirstCriticalHit(u32 target)
+{
+ enum TrainerSlideType slideId = TRAINER_SLIDE_ENEMY_LANDS_FIRST_CRITICAL_HIT;
@ -149,10 +149,10 @@ In `BattleTurnPassed`, most Trainer Slides are checked to see if they should run
```diff
{
PrepareStringBattle(STRINGID_CRITICALHIT, gBattlerAttacker);
+ TryInitializeTrainerSlideEnemyLandsFirstCriticalHit(gBattlerTarget);
TryInitializeTrainerSlidePlayerLandsFirstCriticalHit(gBattlerTarget);
gBattleCommunication[MSG_DISPLAY] = 1;
```
@ -162,7 +162,7 @@ The actual usage of `TryInitializeTrainerSlideEnemyLandsFirstCriticalHit` is add
```diff
}
}
+SINGLE_BATTLE_TEST("Trainer Slide: Enemy Lands First Critical Hit")
+{
+ gBattleTestRunnerState->data.recordedBattle.opponentA = TRAINER_SLIDE_ENEMY_LANDS_FIRST_CRITICAL_HIT;

View File

@ -292,7 +292,7 @@ Adds the species to the player's, player partner's, opponent A's, or opponent B'
Pokemon can be customised as per the guidance for `PLAYER(species)` and `OPPONENT(species)`.
The functions assign the Pokémon to the party of the trainer at `B_POSITION_PLAYER_LEFT`, `B_POSITION_PLAYER_RIGHT`, `B_POSITION_OPPONENT_LEFT`, and `B_POSITION_OPPONENT_RIGHT`, respectively.
`MULTI_PLAYER(species)` and `MULTI_OPPONENT_A(species)` set Pokémon starting at party index 0, while `MULTI_PARTNER(species)` and `MULTI_OPPONENT_B(species)` set Pokémon starting at party index 3.
For `ONE_VS_TWO` tests, `MULTI_PLAYER(species)` must be used for all player-side Pokémon, and for `TWO_VS_ONE` tests, `MULTI_OPPONENT_A(species)` must be used for all opponent-side Pokémon.
For `ONE_VS_TWO` tests, `MULTI_PLAYER(species)` must be used for all player-side Pokémon, and for `TWO_VS_ONE` tests, `MULTI_OPPONENT_A(species)` must be used for all opponent-side Pokémon.
All `MULTI_PLAYER(species)` Pokémon must be set before any `MULTI_PARTNER(species)` Pokémon, and all `MULTI_OPPONENT_A(species)` must be set before any `MULTI_OPPONENT_B(species)` Pokémon, else Pokémon will be set in the incorrect parties in the test.
**Note where a side in a test has two trainers, the test setup manages the assigning of correct multi-party orders, therefore when using functions such as SEND_OUT, Player and Opponent A Pokémon may be referenced using indexes 0, 1, and 2, and Player's Partner and Opponent B Pokémon may be referenced using indexes 3, 4, and 5.**

View File

@ -10,7 +10,7 @@
- [So what are the `#define` options in `overworld.h`?](#so-what-are-the-define-options-in-overworldh)
- [Examples](#examples)
## What is the Time-Based Encounters feature?
## What is the Time-Based Encounters feature?
Time-Based Encounters lets you pick which Pokémon appear based on the in-game clock, per route!
Gen 2 had this feature, and Gen 4 brought it back- for instance, in Sinnoh's Route 201 you have a higher chance of catching a Bidoof than a Starly at night.
@ -810,4 +810,4 @@ python3 migration_scripts/add_time_based_encounters.py --copy
},
]
```
As you can see, the group `gRoute101` and all its encounters were copied into groups that correspond with the four vanilla times of day (Morning/Day/Evening/Night).
As you can see, the group `gRoute101` and all its encounters were copied into groups that correspond with the four vanilla times of day (Morning/Day/Evening/Night).

View File

@ -1,13 +1,13 @@
# `pokemerald-expansion` Vs. Seeker
# `pokemerald-expansion` Vs. Seeker
## What is the Vs. Seeker?
The Vs. Seeker is a Key Item that is used to battle Trainers that the player has battled previously.
The Vs. Seeker is a Key Item that is used to battle Trainers that the player has battled previously.
When used, the Vs. Seeker sends out a signal that allows the player to find other Trainers who want a rematch. This signal affects all Trainers that are on-screen. Once used on Trainers that can be rematched, the device cannot be used again until it is charged. The player does this by walking a specific number of steps. The effect on the Trainers wears off if they are battled, the player leaves the area, or the player walks a specific number of steps. If the player attempts to use the Vs. Seeker when it is not fully charged, the player will be told how many steps remain until it is. After the player uses the Vs. Seeker, some Trainers may have their team changed from their first battle.
When used, the Vs. Seeker sends out a signal that allows the player to find other Trainers who want a rematch. This signal affects all Trainers that are on-screen. Once used on Trainers that can be rematched, the device cannot be used again until it is charged. The player does this by walking a specific number of steps. The effect on the Trainers wears off if they are battled, the player leaves the area, or the player walks a specific number of steps. If the player attempts to use the Vs. Seeker when it is not fully charged, the player will be told how many steps remain until it is. After the player uses the Vs. Seeker, some Trainers may have their team changed from their first battle.
## How is the Vs. Seeker enabled?
### Users
Vs. Seeker functionality is enabled by setting `I_VS_SEEKER_CHARGING` to `TRUE`.
Vs. Seeker functionality is enabled by setting `I_VS_SEEKER_CHARGING` to `TRUE`.
### Players
`ITEM_VS_SEEKER` can only be used outside of battle. It can be used from the bag or registered to be used from the field.
@ -26,7 +26,7 @@ When `I_VS_SEEKER_CHARGING` is enabled, the Match Call does not function at all.
## How does the Vs. Seeker choose a Trainer?
When the Vs. Seeker is successfully used, every Trainer on screen is individually queried. There is a 31% chance that the Trainer will want a rematch.
When the Vs. Seeker is successfully used, every Trainer on screen is individually queried. There is a 31% chance that the Trainer will want a rematch.
Objects listed in `regularTrainersOnLand` or `regularTrainersInWater` are considered Land/Water objects.
| Status | Is Land/Water Object | Emote | New Movement Type |
@ -34,7 +34,7 @@ Objects listed in `regularTrainersOnLand` or `regularTrainersInWater` are consid
| Wants Rematch | Yes | `MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK` | `MOVEMENT_TYPE_COUNTER_CLOCKWISE` |
| Wants Rematch | No | `MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK` | `MOVEMENT_TYPE_FACE_DOWN` |
| Does Not Want Rematch | - | `MOVEMENT_ACTION_EMOTE_X` | none |
| Has Not Been Fought | - | `MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK` | none |
| Has Not Been Fought | - | `MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK` | none |
### Rematch Table
@ -58,7 +58,7 @@ The game determines which version of the Trainer you'll fight next by following
No extra work is required. With the exception of Wally, Gym Leaders and Elite Four, all of the rematchable Trainers in Emerald will work with the Vs. Seeker without any changes.
### New Trainers
#### Party / `gRematchTable`
Each of the rematches for the Trainer must be defined as seperate Trainers in `src/data/trainers.party` and `include/constants/opponents`. For example, `TRAINER_CALVIN_1` also has `TRAINER_CALVIN_2`,`TRAINER_CALVIN_3`,`TRAINER_CALVIN_4`, and `TRAINER_CALVIN_5`.
Each of the rematches for the Trainer must be defined as seperate Trainers in `src/data/trainers.party` and `include/constants/opponents`. For example, `TRAINER_CALVIN_1` also has `TRAINER_CALVIN_2`,`TRAINER_CALVIN_3`,`TRAINER_CALVIN_4`, and `TRAINER_CALVIN_5`.
Once all of those constants and parties are defined, a new row must be added to `gRematchTable` (located in in `src/battle_setup.c`). The row header should be a rematch ID, which can be added in `include/constants/rematches.h`. The row contents must be the five constants created for the new parties, with the lat argument being the constant of the map (`include/constants/map_groups.h`) where the Trainer is placed.

View File

@ -28,26 +28,6 @@
#include "random.h" // for rng_value_t
#include "trainer_slide.h"
// Helper for accessing command arguments and advancing gBattlescriptCurrInstr.
//
// For example accuracycheck is defined as:
//
// .macro accuracycheck failInstr:req, move:req
// .byte 0x1
// .4byte \failInstr
// .2byte \move
// .endm
//
// Which corresponds to:
//
// CMD_ARGS(const u8 *failInstr, u16 move);
//
// The arguments can be accessed as cmd->failInstr and cmd->move.
// gBattlescriptCurrInstr = cmd->nextInstr; advances to the next instruction.
#define CMD_ARGS(...) const struct __attribute__((packed)) { u8 opcode; RECURSIVELY(R_FOR_EACH(APPEND_SEMICOLON, __VA_ARGS__)) const u8 nextInstr[0]; } *const cmd UNUSED = (const void *)gBattlescriptCurrInstr
#define VARIOUS_ARGS(...) CMD_ARGS(u8 battler, u8 id, ##__VA_ARGS__)
#define NATIVE_ARGS(...) CMD_ARGS(void (*func)(void), ##__VA_ARGS__)
// Used to exclude moves learned temporarily by Transform or Mimic
#define MOVE_IS_PERMANENT(battler, moveSlot) \
(!(gBattleMons[battler].volatiles.transformed) \
@ -1165,7 +1145,7 @@ static inline struct PartyState *GetBattlerPartyState(u32 battler)
static inline bool32 IsDoubleBattle(void)
{
return (gBattleTypeFlags & BATTLE_TYPE_MORE_THAN_TWO_BATTLERS);
return !!(gBattleTypeFlags & BATTLE_TYPE_MORE_THAN_TWO_BATTLERS);
}
static inline bool32 IsSpreadMove(enum MoveTarget moveTarget)

View File

@ -99,6 +99,7 @@
#define B_CAN_SPITE_FAIL GEN_LATEST // In Gen4+, Spite can no longer fail if the foe's last move only has 1 remaining PP.
#define B_CRASH_IF_TARGET_IMMUNE GEN_LATEST // In Gen4+, moves with crash damage will crash if the user attacks a target that is immune due to their typing.
#define B_MEMENTO_FAIL GEN_LATEST // In Gen4+, Memento no longer fails if the target already has -6 Attack and Special Attack. Additionally, in Gen5+, it fails if there is no target, or if the target is protected or behind a Substitute.
#define B_PARTING_SHOT_SWITCH GEN_LATEST // In Gen7+, the user won't switch out if Parting Shot fails to lower the target's stats.
#define B_GLARE_GHOST GEN_LATEST // In Gen4+, Glare can hit Ghost-type Pokémon normally.
#define B_SKILL_SWAP GEN_LATEST // In Gen4+, Skill Swap triggers switch-in abilities after use.
#define B_BRICK_BREAK GEN_LATEST // In Gen4+, you can destroy your own side's screens. In Gen 5+, screens are not removed if the target is immune.
@ -130,6 +131,8 @@
#define B_AFTER_YOU_TURN_ORDER GEN_LATEST // In Gen8+, After You doesn't fail if the turn order wouldn't change after use.
#define B_QUASH_TURN_ORDER GEN_LATEST // In Gen8+, Quash-affected battlers move according to speed order. Before Gen8, Quash-affected battlers move in the order they were affected by Quash.
#define B_DESTINY_BOND_FAIL GEN_LATEST // In Gen7+, Destiny Bond fails if used repeatedly.
#define B_FORESIGHT_FAIL GEN_LATEST // In Gen2 and Gen5+, Foresight fails if used against a target already under its effect.
#define B_MIRACLE_EYE_FAIL GEN_LATEST // In Gen5+, Miracle Eye fails if used against a target already under its effect.
#define B_PURSUIT_TARGET GEN_LATEST // In Gen4+, Pursuit attacks a switching opponent even if they weren't targeting them. Before Gen4, Pursuit only attacks a switching opponent that it originally targeted.
#define B_SKIP_RECHARGE GEN_LATEST // In Gen1, recharging moves such as Hyper Beam skip the recharge if the target gets KO'd
#define B_ENCORE_TARGET GEN_LATEST // In Gen5+, encored moves are allowed to choose a target

View File

@ -91,6 +91,7 @@
F(CAN_SPITE_FAIL, canSpiteFail, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(CRASH_IF_TARGET_IMMUNE, crashIfTargetImmune, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(MEMENTO_FAIL, mementoFail, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(PARTING_SHOT_SWITCH, partingShotSwitch, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(GLARE_GHOST, glareGhost, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(SKILL_SWAP, skillSwap, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(BRICK_BREAK, brickBreak, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
@ -120,6 +121,8 @@
F(AFTER_YOU_TURN_ORDER, afterYouTurnOrder, (u32, GEN_COUNT - 1)) \
F(QUASH_TURN_ORDER, quashTurnOrder, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(DESTINY_BOND_FAIL, destinyBondFail, (u32, GEN_COUNT - 1)) \
F(FORESIGHT_FAIL, foresightFail, (u32, GEN_COUNT - 1)) \
F(MIRACLE_EYE_FAIL, miracleEyeFail, (u32, GEN_COUNT - 1)) \
F(PURSUIT_TARGET, pursuitTarget, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(SKIP_RECHARGE, skipRecharge, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \
F(ENCORE_TARGET, encoreTarget, (u32, GEN_COUNT - 1)) \

View File

@ -19,7 +19,7 @@
#define WEATHER_ABNORMAL 15 // The alternating weather during Groudon/Kyogre conflict
#define WEATHER_ROUTE119_CYCLE 20
#define WEATHER_ROUTE123_CYCLE 21
#define WEATHER_FOG 22 // Aggregate of WEATHER_FOG_HORIZONTAL and WEATHER_FOG_DIAGONAL
#define WEATHER_FOG 22 // Aggregate of WEATHER_FOG_HORIZONTAL and WEATHER_FOG_DIAGONAL
#define WEATHER_COUNT 23
// These are used in maps' coord_weather_event entries.

View File

@ -4,63 +4,63 @@
#include "gba/types.h"
//
// This header includes typedefs for fields that commonly appear throughout
// This header includes typedefs for fields that commonly appear throughout
// the codebase, and which ROM hacks might benefit from being able to widen.
//
// These typedefs include the underlying type in their name for two reasons:
//
// - Game Freak wasn't fully consistent about field widths throughout
// their codebase. For example, when Region Map Sections are persistently
// stored in savedata, they're stored as 8-bit values; but much of the
// - Game Freak wasn't fully consistent about field widths throughout
// their codebase. For example, when Region Map Sections are persistently
// stored in savedata, they're stored as 8-bit values; but much of the
// codebase handles them as 16-bit values.
//
// - Although Pokemon Emerald doesn't come close to maxing out RAM, it *does*
// use nearly all of its EEPROM. That is: the vanilla game uses 96% of the
// flash memory available for storing players' save files, leaving 2172
// bytes to spare within each of the game's two save files (primary and
// backup). These spare bytes are not contiguous: SaveBlock1 can only grow
// by 84 bytes, and SaveBlock2 can only grow by 120 bytes, with the rest
// - Although Pokemon Emerald doesn't come close to maxing out RAM, it *does*
// use nearly all of its EEPROM. That is: the vanilla game uses 96% of the
// flash memory available for storing players' save files, leaving 2172
// bytes to spare within each of the game's two save files (primary and
// backup). These spare bytes are not contiguous: SaveBlock1 can only grow
// by 84 bytes, and SaveBlock2 can only grow by 120 bytes, with the rest
// of the free space located after the player's PC-boxed Pokemon.
//
// With so little flash memory to spare, keeping track of how much space
// you're using is vital -- and so is arranging struct members to minimize
// compiler-inserted padding. It's easier to deal with this when you can
// With so little flash memory to spare, keeping track of how much space
// you're using is vital -- and so is arranging struct members to minimize
// compiler-inserted padding. It's easier to deal with this when you can
// see these types' widths at a glance.
//
// Accordingly, this file generally doesn't contain just single types, but
// rather families of types. For example, Region Map Sections are saved as
// u8s within the player's save file, but are sometimes handled as u16s or
// even s16s and ints; and so there are multiple typedefs for Map Sections
// corresponding to each of these underlying types, and each typedef has a
// Accordingly, this file generally doesn't contain just single types, but
// rather families of types. For example, Region Map Sections are saved as
// u8s within the player's save file, but are sometimes handled as u16s or
// even s16s and ints; and so there are multiple typedefs for Map Sections
// corresponding to each of these underlying types, and each typedef has a
// name which indicates the underlying type.
//
// For a given family of typedefs, the smallest one should be considered
// the "real" or "canonical" type. Continuing with Map Sections as our
// example, the smallest type is an 8-bit integer, and so any values that
// can't fit in an 8-bit integer will be truncated and lost at some point
// within the codebase. Therefore mapsec_u8_t is the "canonical" type for
// Map Sections, and the larger typedefs just exist to describe situations
// For a given family of typedefs, the smallest one should be considered
// the "real" or "canonical" type. Continuing with Map Sections as our
// example, the smallest type is an 8-bit integer, and so any values that
// can't fit in an 8-bit integer will be truncated and lost at some point
// within the codebase. Therefore mapsec_u8_t is the "canonical" type for
// Map Sections, and the larger typedefs just exist to describe situations
// where the game handles Map Sections inconsistently with that "canon."
//
// Map Sections are named areas that can appear in the region map. Each
// individual map can be assigned to a Map Section as appropriate. The
// Map Sections are named areas that can appear in the region map. Each
// individual map can be assigned to a Map Section as appropriate. The
// possible values are in constants/region_map_sections.h.
//
// If you choose to widen Map Sections, be aware that Met Locations (below)
// If you choose to widen Map Sections, be aware that Met Locations (below)
// are based on Map Sections and will also be widened.
typedef u8 mapsec_u8_t;
typedef u16 mapsec_u16_t;
typedef s16 mapsec_s16_t;
typedef s32 mapsec_s32_t;
// Met Locations for caught Pokemon use the same values as Map Sections,
// Met Locations for caught Pokemon use the same values as Map Sections,
// except that 0xFD, 0xFE, and 0xFF have special meanings.
//
// Because this value appears inside every Pokemon's data, widening it will
// consume a lot more space within flash memory. The space usage will be
// greater than you expect due to how Pokemon substructs are laid out; you
// would have to rearrange the substructs' contents in order to minimize
// Because this value appears inside every Pokemon's data, widening it will
// consume a lot more space within flash memory. The space usage will be
// greater than you expect due to how Pokemon substructs are laid out; you
// would have to rearrange the substructs' contents in order to minimize
// how much more space a wider Met Location would consume.
typedef mapsec_u8_t metloc_u8_t;

View File

@ -63,4 +63,11 @@ enum {
bool32 CanThrowBall(void);
bool32 CannotUseItemsInBattle(enum Item itemId, struct Pokemon *mon);
enum ItemTMHMOrEvolutionStone
{
ITEM_IS_OTHER,
ITEM_IS_TM_HM,
ITEM_IS_EVOLUTION_STONE,
};
#endif // GUARD_ITEM_USE_H

View File

@ -93,6 +93,7 @@ void SetDynamicWarpWithCoords(s32 unused, s8 mapGroup, s8 mapNum, s8 warpId, s8
void SetWarpDestinationToDynamicWarp(u8 unusedWarpId);
void SetWarpDestinationToHealLocation(u8 healLocationId);
void SetWarpDestinationToLastHealLocation(void);
void SetWarpDestinationForTeleport(void);
void SetLastHealLocationWarp(u8 healLocationId);
void UpdateEscapeWarp(s16 x, s16 y);
void SetEscapeWarp(s8 mapGroup, s8 mapNum, s8 warpId, s8 x, s8 y);

View File

@ -56,7 +56,7 @@ def GetWildEncounterFile():
if tempSuffix in map["base_label"]:
editMap = False
break
else:
else:
editMap = True
if editMap:

View File

@ -526,7 +526,7 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
{
u32 battlerIn1, battlerIn2;
u8 numAbsorbingAbilities = 0;
enum Ability absorbingTypeAbilities[3]; // Array size is maximum number of absorbing abilities for a single type
enum Ability absorbingTypeAbilities[8]; // Max needed for type + move property absorbers
s32 firstId;
s32 lastId;
struct Pokemon *party;
@ -570,42 +570,47 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_FLASH_FIRE;
}
else if (incomingType == TYPE_WATER || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_WATER))
if (incomingType == TYPE_WATER || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_WATER))
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_WATER_ABSORB;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_DRY_SKIN;
if (GetConfig(CONFIG_REDIRECT_ABILITY_IMMUNITY) >= GEN_5)
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_STORM_DRAIN;
}
else if (incomingType == TYPE_ELECTRIC || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_ELECTRIC))
if (incomingType == TYPE_ELECTRIC || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_ELECTRIC))
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_VOLT_ABSORB;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_MOTOR_DRIVE;
if (GetConfig(CONFIG_REDIRECT_ABILITY_IMMUNITY) >= GEN_5)
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_LIGHTNING_ROD;
}
else if (incomingType == TYPE_GRASS || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GRASS))
if (incomingType == TYPE_GRASS || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GRASS))
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_SAP_SIPPER;
}
else if (incomingType == TYPE_GROUND || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GROUND))
if (incomingType == TYPE_GROUND || (isOpposingBattlerChargingOrInvulnerable && incomingType == TYPE_GROUND))
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_EARTH_EATER;
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_LEVITATE;
}
else if (IsSoundMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsSoundMove(incomingMove)))
if (IsSoundMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsSoundMove(incomingMove)))
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_SOUNDPROOF;
}
else if (IsBallisticMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsBallisticMove(incomingMove)))
if (IsBallisticMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsBallisticMove(incomingMove)))
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_BULLETPROOF;
}
else if (IsWindMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsWindMove(incomingMove)))
if (IsWindMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsWindMove(incomingMove)))
{
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_WIND_RIDER;
}
else
if (IsPowderMove(incomingMove) || (isOpposingBattlerChargingOrInvulnerable && IsPowderMove(incomingMove)))
{
if (GetConfig(CONFIG_POWDER_OVERCOAT) >= GEN_6)
absorbingTypeAbilities[numAbsorbingAbilities++] = ABILITY_OVERCOAT;
}
if (numAbsorbingAbilities == 0)
{
return FALSE;
}

View File

@ -2097,40 +2097,45 @@ bool32 IsAllyProtectingFromMove(u32 battlerAtk, enum Move attackerMove, enum Mov
{
return FALSE;
}
else
enum ProtectMethod protectMethod = GetMoveProtectMethod(allyMove);
switch (protectMethod)
{
enum ProtectMethod protectMethod = GetMoveProtectMethod(allyMove);
if (protectMethod == PROTECT_QUICK_GUARD)
case PROTECT_CRAFTY_SHIELD:
if (!IsBattleMoveStatus(attackerMove))
{
s32 priority = GetBattleMovePriority(battlerAtk, gAiLogicData->abilities[battlerAtk], attackerMove);
return (priority > 0);
return FALSE;
}
if (IsBattleMoveStatus(attackerMove))
else if (GetMoveEffect(attackerMove) == EFFECT_HOLD_HANDS)
{
switch (protectMethod)
{
case PROTECT_NORMAL:
case PROTECT_CRAFTY_SHIELD:
case PROTECT_MAX_GUARD:
case PROTECT_WIDE_GUARD:
return TRUE;
default:
return FALSE;
}
return TRUE;
}
else
{
switch (protectMethod)
{
case PROTECT_CRAFTY_SHIELD:
return FALSE;
default:
return TRUE;
}
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, attackerMove);
return (GetBattlerSide(battlerAtk) != GetBattlerSide(BATTLE_PARTNER(battlerAtk))
&& moveTarget != TARGET_OPPONENTS_FIELD
&& moveTarget != TARGET_ALL_BATTLERS);
}
case PROTECT_WIDE_GUARD:
return IsSpreadMove(GetBattlerMoveTargetType(battlerAtk, attackerMove));
case PROTECT_NORMAL:
case PROTECT_SPIKY_SHIELD:
case PROTECT_MAX_GUARD:
case PROTECT_BANEFUL_BUNKER:
case PROTECT_BURNING_BULWARK:
return TRUE;
case PROTECT_OBSTRUCT:
case PROTECT_SILK_TRAP:
case PROTECT_KINGS_SHIELD:
return !IsBattleMoveStatus(attackerMove);
case PROTECT_QUICK_GUARD:
return (GetChosenMovePriority(battlerAtk, gAiLogicData->abilities[battlerAtk]) > 0);
case PROTECT_MAT_BLOCK:
return !IsBattleMoveStatus(attackerMove);
default:
return FALSE;
}
}

View File

@ -1399,20 +1399,20 @@ static void AnimSmallWaterOrb(struct Sprite *sprite)
}
}
#define tRainState data[0]
#define tWaterSpoutPower data[1]
#define tDropTaskDelay data[2]
#define tDropInitialXPos data[4]
#define tDropXRange data[5]
#define tDropEndYPos data[6]
#define tDropXPos data[7]
#define tSineTableIndex data[8]
#define tCurrentDropSprites data[9]
#define tDropHasHit data[10]
#define tCreatedDropSprites data[11]
#define tMaxDropSprites data[12]
#define tShakeTasksCreated data[13]
#define tDropInitialYPos data[14]
#define tRainState data[0]
#define tWaterSpoutPower data[1]
#define tDropTaskDelay data[2]
#define tDropInitialXPos data[4]
#define tDropXRange data[5]
#define tDropEndYPos data[6]
#define tDropXPos data[7]
#define tSineTableIndex data[8]
#define tCurrentDropSprites data[9]
#define tDropHasHit data[10]
#define tCreatedDropSprites data[11]
#define tMaxDropSprites data[12]
#define tShakeTasksCreated data[13]
#define tDropInitialYPos data[14]
void AnimTask_BrineRain(u8 taskId)
{
@ -1590,20 +1590,20 @@ static void AnimWaterSpoutRainHit(struct Sprite *sprite)
}
}
#undef tRainState
#undef tWaterSpoutPower
#undef tDropTaskDelay
#undef tDropInitialXPos
#undef tDropXRange
#undef tDropEndYPos
#undef tDropXPos
#undef tSineTableIndex
#undef tCurrentDropSprites
#undef tDropHasHit
#undef tCreatedDropSprites
#undef tMaxDropSprites
#undef tShakeTasksCreated
#undef tDropInitialYPos
#undef tRainState
#undef tWaterSpoutPower
#undef tDropTaskDelay
#undef tDropInitialXPos
#undef tDropXRange
#undef tDropEndYPos
#undef tDropXPos
#undef tSineTableIndex
#undef tCurrentDropSprites
#undef tDropHasHit
#undef tCreatedDropSprites
#undef tMaxDropSprites
#undef tShakeTasksCreated
#undef tDropInitialYPos
void AnimTask_WaterSport(u8 taskId)
{

View File

@ -458,43 +458,3 @@ void ChooseDamageNonTypesString(enum Type type)
break;
}
}
// Updates Dynamax HP multipliers and healthboxes.
void BS_UpdateDynamax(void)
{
NATIVE_ARGS();
u32 battler = gBattleScripting.battler;
struct Pokemon *mon = GetBattlerMon(battler);
if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change.
RecalcBattlerStats(battler, mon, GetActiveGimmick(battler) == GIMMICK_DYNAMAX);
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL);
gBattlescriptCurrInstr = cmd->nextInstr;
}
// Goes to the jump instruction if the target is Dynamaxed.
void BS_JumpIfDynamaxed(void)
{
NATIVE_ARGS(const u8 *jumpInstr);
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_UndoDynamax(void)
{
NATIVE_ARGS(u8 battler);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
{
UndoDynamax(battler);
gBattleScripting.battler = battler;
BattleScriptCall(BattleScript_DynamaxEnds_Ret);
return;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -837,7 +837,10 @@ static enum CancelerResult CancelerMoveFailure(struct BattleContext *ctx)
bool32 canUseWideGuard = (GetConfig(CONFIG_WIDE_GUARD) >= GEN_6 && protectMethod == PROTECT_WIDE_GUARD);
bool32 canUseQuickGuard = (GetConfig(CONFIG_QUICK_GUARD) >= GEN_6 && protectMethod == PROTECT_QUICK_GUARD);
if (!canUseProtectSecondTime && !canUseWideGuard && !canUseQuickGuard)
if (!canUseProtectSecondTime
&& !canUseWideGuard
&& !canUseQuickGuard
&& protectMethod != PROTECT_CRAFTY_SHIELD)
battleScript = BattleScript_ButItFailed;
}
if (battleScript != NULL)
@ -1624,7 +1627,8 @@ static enum MoveEndResult MoveEndProtectLikeEffect(void)
enum MoveEndResult result = MOVEEND_RESULT_CONTINUE;
u32 temp = 0;
if (CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), gCurrentMove))
if (gProtectStructs[gBattlerAttacker].chargingTurn
|| CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), gCurrentMove))
{
gBattleScripting.moveendState++;
return result;
@ -1685,8 +1689,8 @@ static enum MoveEndResult MoveEndProtectLikeEffect(void)
// Not strictly a protect effect, but works the same way
if (IsBattlerUsingBeakBlast(gBattlerTarget)
&& CanBeBurned(gBattlerAttacker, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& !IsBattlerUnaffectedByMove(gBattlerTarget))
&& IsBattlerTurnDamaged(gBattlerTarget)
&& CanBeBurned(gBattlerAttacker, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)))
{
gBattleMons[gBattlerAttacker].status1 = STATUS1_BURN;
BtlController_EmitSetMonData(gBattlerAttacker, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[gBattlerAttacker].status1), &gBattleMons[gBattlerAttacker].status1);

View File

@ -75,6 +75,26 @@
#include "load_save.h"
#include "test/test_runner_battle.h"
// Helper for accessing command arguments and advancing gBattlescriptCurrInstr.
//
// For example accuracycheck is defined as:
//
// .macro accuracycheck failInstr:req, move:req
// .byte 0x1
// .4byte \failInstr
// .2byte \move
// .endm
//
// Which corresponds to:
//
// CMD_ARGS(const u8 *failInstr, u16 move);
//
// The arguments can be accessed as cmd->failInstr and cmd->move.
// gBattlescriptCurrInstr = cmd->nextInstr; advances to the next instruction.
#define CMD_ARGS(...) const struct __attribute__((packed)) { u8 opcode; RECURSIVELY(R_FOR_EACH(APPEND_SEMICOLON, __VA_ARGS__)) const u8 nextInstr[0]; } *const cmd UNUSED = (const void *)gBattlescriptCurrInstr
#define VARIOUS_ARGS(...) CMD_ARGS(u8 battler, u8 id, ##__VA_ARGS__)
#define NATIVE_ARGS(...) CMD_ARGS(void (*func)(void), ##__VA_ARGS__)
// table to avoid ugly powing on gba (courtesy of doesnt)
// this returns (i^2.5)/4
// the quarters cancel so no need to re-quadruple them in actual calculation
@ -2806,7 +2826,7 @@ void SetMoveEffect(u32 battlerAtk, u32 effectBattler, enum MoveEffect moveEffect
if (i)
{
BattleScriptPush(battleScript);
if (gCurrentMove == MOVE_HYPERSPACE_FURY)
if (GetMoveEffect(gCurrentMove) == EFFECT_HYPERSPACE_FURY)
gBattlescriptCurrInstr = BattleScript_HyperspaceFuryRemoveProtect;
else
gBattlescriptCurrInstr = BattleScript_MoveEffectFeint;
@ -7806,6 +7826,8 @@ static u32 ChangeStatBuffs(u32 battler, s8 statValue, enum Stat statId, union St
}
else if (battlerAbility == ABILITY_MIRROR_ARMOR && !flags.mirrorArmored && gBattlerAttacker != gBattlerTarget && battler == gBattlerTarget)
{
if (GetMoveEffect(gCurrentMove) == EFFECT_PARTING_SHOT)
gBattleScripting.animTargetsHit = 1;
if (flags.allowPtr)
{
SET_STATCHANGER(statId, GET_STAT_BUFF_VALUE(statValue) | STAT_BUFF_NEGATIVE, TRUE);
@ -9664,7 +9686,7 @@ static void Cmd_trymemento(void)
{
CMD_ARGS(const u8 *failInstr);
if (B_MEMENTO_FAIL >= GEN_5 && DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove))
if (B_MEMENTO_FAIL >= GEN_4 && DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove))
{
// Failed, target was protected.
gBattlescriptCurrInstr = cmd->failInstr;
@ -15117,3 +15139,43 @@ void BS_TryActivateAbilityWithAbilityShield(void)
BattleScriptCall(BattleScript_ActivateSwitchInAbility);
}
}
// Updates Dynamax HP multipliers and healthboxes.
void BS_UpdateDynamax(void)
{
NATIVE_ARGS();
u32 battler = gBattleScripting.battler;
struct Pokemon *mon = GetBattlerMon(battler);
if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change.
RecalcBattlerStats(battler, mon, GetActiveGimmick(battler) == GIMMICK_DYNAMAX);
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL);
gBattlescriptCurrInstr = cmd->nextInstr;
}
// Goes to the jump instruction if the target is Dynamaxed.
void BS_JumpIfDynamaxed(void)
{
NATIVE_ARGS(const u8 *jumpInstr);
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_UndoDynamax(void)
{
NATIVE_ARGS(u8 battler);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
{
UndoDynamax(battler);
gBattleScripting.battler = battler;
BattleScriptCall(BattleScript_DynamaxEnds_Ret);
return;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -5929,6 +5929,26 @@ static inline bool32 IsSideProtected(u32 battler, enum ProtectMethod method)
|| gProtectStructs[BATTLE_PARTNER(battler)].protected == method;
}
static bool32 IsCraftyShieldProtected(u32 battlerAtk, u32 battlerDef, u32 move)
{
if (!IsBattleMoveStatus(move))
return FALSE;
if (!IsSideProtected(battlerDef, PROTECT_CRAFTY_SHIELD))
return FALSE;
if (GetMoveEffect(move) == EFFECT_HOLD_HANDS)
return TRUE;
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move);
if (!IsBattlerAlly(battlerAtk, battlerDef)
&& moveTarget != TARGET_OPPONENTS_FIELD
&& moveTarget != TARGET_ALL_BATTLERS)
return TRUE;
return FALSE;
}
bool32 IsBattlerProtected(struct BattleContext *ctx)
{
if (gProtectStructs[ctx->battlerDef].protected == PROTECT_NONE
@ -5952,9 +5972,7 @@ bool32 IsBattlerProtected(struct BattleContext *ctx)
bool32 isProtected = FALSE;
if (IsSideProtected(ctx->battlerDef, PROTECT_CRAFTY_SHIELD)
&& IsBattleMoveStatus(ctx->move)
&& GetMoveEffect(ctx->move) != EFFECT_COACHING)
if (IsCraftyShieldProtected(ctx->battlerAtk, ctx->battlerDef, ctx->move))
isProtected = TRUE;
else if (MoveIgnoresProtect(ctx->move))
isProtected = FALSE;
@ -8602,6 +8620,9 @@ bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId)
u32 i;
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
if (heldItemId == ITEM_NONE)
return FALSE;
for (i = 0; formChanges != NULL && formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
enum FormChanges method = formChanges[i].method;

View File

@ -8,15 +8,15 @@ const u16 gBattleEnvironmentPalette_Building[] = INCBIN_U16("graphics/battle_env
const u16 gBattleEnvironmentPalette_Kyogre[] = INCBIN_U16("graphics/battle_environment/water/kyogre.gbapal");
const u16 gBattleEnvironmentPalette_Groudon[] = INCBIN_U16("graphics/battle_environment/cave/groudon.gbapal");
const u16 gBattleEnvironmentPalette_BuildingGym[] = INCBIN_U16("graphics/battle_environment/building/palette2.gbapal");
const u16 gBattleEnvironmentPalette_BuildingLeader[] = INCBIN_U16("graphics/battle_environment/building/palette3.gbapal");
const u16 gBattleEnvironmentPalette_StadiumAqua[] = INCBIN_U16("graphics/battle_environment/stadium/palette1.gbapal");
const u16 gBattleEnvironmentPalette_StadiumMagma[] = INCBIN_U16("graphics/battle_environment/stadium/palette2.gbapal");
const u16 gBattleEnvironmentPalette_StadiumSidney[] = INCBIN_U16("graphics/battle_environment/stadium/palette3.gbapal");
const u16 gBattleEnvironmentPalette_StadiumPhoebe[] = INCBIN_U16("graphics/battle_environment/stadium/palette4.gbapal");
const u16 gBattleEnvironmentPalette_StadiumGlacia[] = INCBIN_U16("graphics/battle_environment/stadium/palette5.gbapal");
const u16 gBattleEnvironmentPalette_StadiumDrake[] = INCBIN_U16("graphics/battle_environment/stadium/palette6.gbapal");
const u16 gBattleEnvironmentPalette_StadiumWallace[] = INCBIN_U16("graphics/battle_environment/stadium/palette7.gbapal");
const u16 gBattleEnvironmentPalette_BuildingGym[] = INCBIN_U16("graphics/battle_environment/building/gym.gbapal");
const u16 gBattleEnvironmentPalette_BuildingLeader[] = INCBIN_U16("graphics/battle_environment/building/leader.gbapal");
const u16 gBattleEnvironmentPalette_StadiumAqua[] = INCBIN_U16("graphics/battle_environment/stadium/aqua.gbapal");
const u16 gBattleEnvironmentPalette_StadiumMagma[] = INCBIN_U16("graphics/battle_environment/stadium/magma.gbapal");
const u16 gBattleEnvironmentPalette_StadiumSidney[] = INCBIN_U16("graphics/battle_environment/stadium/sidney.gbapal");
const u16 gBattleEnvironmentPalette_StadiumPhoebe[] = INCBIN_U16("graphics/battle_environment/stadium/phoebe.gbapal");
const u16 gBattleEnvironmentPalette_StadiumGlacia[] = INCBIN_U16("graphics/battle_environment/stadium/glacia.gbapal");
const u16 gBattleEnvironmentPalette_StadiumDrake[] = INCBIN_U16("graphics/battle_environment/stadium/drake.gbapal");
const u16 gBattleEnvironmentPalette_StadiumWallace[] = INCBIN_U16("graphics/battle_environment/stadium/wallace.gbapal");
const u16 gBattleEnvironmentPalette_Rayquaza[] = INCBIN_U16("graphics/battle_environment/sky/palette.gbapal");
const u32 gBattleEnvironmentAnimTiles_TallGrass[] = INCBIN_U32("graphics/battle_environment/tall_grass/anim_tiles.4bpp.smol");

View File

@ -21,7 +21,7 @@ const u16 gItemIconPalette_UltraBall[] = INCBIN_U16("graphics/items/icon_palette
const u32 gItemIcon_MasterBall[] = INCBIN_U32("graphics/items/icons/master_ball.4bpp.smol");
const u16 gItemIconPalette_MasterBall[] = INCBIN_U16("graphics/items/icon_palettes/master_ball.gbapal");
const u32 gItemIcon_PremierBall[] = INCBIN_U16("graphics/items/icons/premier_ball.4bpp.smol");
const u32 gItemIcon_PremierBall[] = INCBIN_U32("graphics/items/icons/premier_ball.4bpp.smol");
const u16 gItemIconPalette_PremierBall[] = INCBIN_U16("graphics/items/icon_palettes/premier_ball.gbapal");
const u32 gItemIcon_HealBall[] = INCBIN_U32("graphics/items/icons/heal_ball.4bpp.smol");

View File

@ -19391,7 +19391,7 @@ const u32 gObjectEventPic_Substitute[] = INCBIN_COMP("graphics/pokemon/question_
const u16 gMonPalette_VivillonIcySnow[] = INCBIN_U16("graphics/pokemon/vivillon/normal.gbapal");
const u32 gMonBackPic_VivillonIcySnow[] = INCBIN_U32("graphics/pokemon/vivillon/back.4bpp.smol");
const u16 gMonShinyPalette_VivillonIcySnow[] = INCBIN_U16("graphics/pokemon/vivillon/shiny.gbapal");
const u8 gMonIcon_VivillonIcySnow[] = INCBIN_U8("graphics/pokemon/vivillon/meadow/icon.4bpp");
const u8 gMonIcon_VivillonIcySnow[] = INCBIN_U8("graphics/pokemon/vivillon/icon.4bpp");
#if P_FOOTPRINTS
const u8 gMonFootprint_Vivillon[] = INCBIN_U8("graphics/pokemon/vivillon/footprint.1bpp");
#endif //P_FOOTPRINTS

View File

@ -8993,7 +8993,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.priority = 0,
.category = DAMAGE_CATEGORY_STATUS,
.zMove = { .effect = Z_EFFECT_DEF_UP_1 },
.ignoresProtect = (B_UPDATED_MOVE_FLAGS >= GEN_6 || B_UPDATED_MOVE_FLAGS < GEN_3),
.ignoresProtect = B_UPDATED_MOVE_FLAGS >= GEN_6,
.magicCoatAffected = TRUE,
.contestEffect = CONTEST_EFFECT_MAKE_FOLLOWING_MONS_NERVOUS,
.contestCategory = CONTEST_CATEGORY_CUTE,
@ -16028,7 +16028,9 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.metronomeBanned = TRUE,
.sketchBanned = (B_SKETCH_BANS >= GEN_9),
.additionalEffects = ADDITIONAL_EFFECTS({
// Feint move effect handled in script as it goes before animation
.moveEffect = MOVE_EFFECT_FEINT, // TODO: Is this supposed to happen before the attack animation?
},
{
.moveEffect = MOVE_EFFECT_DEF_MINUS_1,
.self = TRUE,
}),
@ -19945,7 +19947,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_PHYSICAL,
.metronomeBanned = TRUE, // Only since it isn't implemented yet
.battleAnimScript = gBattleAnimMove_LastRespects,
},
@ -21031,7 +21032,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.category = DAMAGE_CATEGORY_SPECIAL,
.argument = { .absorbPercentage = 50 },
.thawsUser = TRUE,
.metronomeBanned = TRUE,
.healingMove = TRUE,
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_BURN,
@ -21055,7 +21055,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.priority = 0,
.category = DAMAGE_CATEGORY_SPECIAL,
.ballisticMove = TRUE,
.metronomeBanned = TRUE,
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_SYRUP_BOMB,
.chance = 100,
@ -21078,7 +21077,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.target = TARGET_SELECTED,
.priority = 0,
.category = DAMAGE_CATEGORY_PHYSICAL,
.metronomeBanned = TRUE,
.battleAnimScript = gBattleAnimMove_IvyCudgel,
},
@ -21162,7 +21160,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.zMove = { .effect = Z_EFFECT_RESET_STATS },
.ignoresProtect = TRUE,
.mirrorMoveBanned = TRUE,
.metronomeBanned = TRUE,
.copycatBanned = TRUE,
.assistBanned = TRUE,
.battleAnimScript = gBattleAnimMove_BurningBulwark,

View File

@ -2906,7 +2906,7 @@ const struct SpeciesInfo gSpeciesInfoGen3[] =
.baseSpeed = 100,
.baseSpAttack = 165,
.baseSpDefense = 135,
.types = MON_TYPES(TYPE_PSYCHIC, RALTS_FAMILY_TYPE2),
.types = MON_TYPES(TYPE_PSYCHIC, TYPE_FAIRY),
.catchRate = 45,
.expYield = (P_UPDATED_EXP_YIELDS >= GEN_8) ? 309 : 278,
.evYield_SpAttack = 3,

View File

@ -3,7 +3,7 @@
#include "rtc.h"
const struct DateTime gGen3Epoch =
const struct DateTime gGen3Epoch =
{
.year = 2000,
.month = MONTH_JAN,

View File

@ -2653,7 +2653,7 @@ static void TeleportWarpOutFieldEffect_End(struct Task *task)
if (BGMusicStopped() == TRUE)
{
SetWarpDestinationToLastHealLocation();
SetWarpDestinationForTeleport();
WarpIntoMap();
SetMainCallback2(CB2_LoadMap);
gFieldCallback = FieldCallback_TeleportWarpIn;

View File

@ -277,7 +277,11 @@ static const u8 sRSAvatarGfxIds[GENDER_COUNT] =
[FEMALE] = OBJ_EVENT_GFX_LINK_RS_MAY
};
static const u8 sPlayerAvatarGfxToStateFlag[GENDER_COUNT][5][2] =
static const struct __attribute__((packed))
{
u8 graphicsId;
u8 playerFlag;
} sPlayerAvatarGfxToStateFlag[GENDER_COUNT][5] =
{
[MALE] =
{
@ -843,10 +847,10 @@ static void PlayerNotOnBikeMoving(enum Direction direction, u16 heldKeys)
}
if (!(gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_UNDERWATER)
&& (heldKeys & B_BUTTON)
&& (heldKeys & B_BUTTON)
&& FlagGet(FLAG_SYS_B_DASH)
&& IsRunningDisallowed(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior) == 0
&& !FollowerNPCComingThroughDoor()
&& IsRunningDisallowed(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior) == 0
&& !FollowerNPCComingThroughDoor()
&& (I_ORAS_DOWSING_FLAG == 0 || (I_ORAS_DOWSING_FLAG != 0 && !FlagGet(I_ORAS_DOWSING_FLAG))))
{
if (ObjectMovingOnRockStairs(&gObjectEvents[gPlayerAvatar.objectEventId], direction))
@ -1579,8 +1583,8 @@ static u8 GetPlayerAvatarStateTransitionByGraphicsId(u16 graphicsId, u8 gender)
for (i = 0; i < ARRAY_COUNT(sPlayerAvatarGfxToStateFlag[0]); i++)
{
if (sPlayerAvatarGfxToStateFlag[gender][i][0] == graphicsId)
return sPlayerAvatarGfxToStateFlag[gender][i][1];
if (sPlayerAvatarGfxToStateFlag[gender][i].graphicsId == graphicsId)
return sPlayerAvatarGfxToStateFlag[gender][i].playerFlag;
}
return PLAYER_AVATAR_FLAG_ON_FOOT;
}
@ -1592,8 +1596,8 @@ u16 GetPlayerAvatarGraphicsIdByCurrentState(void)
for (i = 0; i < ARRAY_COUNT(sPlayerAvatarGfxToStateFlag[0]); i++)
{
if (sPlayerAvatarGfxToStateFlag[gPlayerAvatar.gender][i][1] & flags)
return sPlayerAvatarGfxToStateFlag[gPlayerAvatar.gender][i][0];
if (sPlayerAvatarGfxToStateFlag[gPlayerAvatar.gender][i].playerFlag & flags)
return sPlayerAvatarGfxToStateFlag[gPlayerAvatar.gender][i].graphicsId;
}
return 0;
}

View File

@ -187,7 +187,7 @@ static void FieldUpdateRegionMap(void)
sFieldRegionMapHandler->state++;
break;
case MAP_INPUT_R_BUTTON:
if (sFieldRegionMapHandler->regionMap.mapSecType == MAPSECTYPE_CITY_CANFLY
if (sFieldRegionMapHandler->regionMap.mapSecType == MAPSECTYPE_CITY_CANFLY
&& FlagGet(OW_FLAG_POKE_RIDER) && Overworld_MapTypeAllowsTeleportAndFly(gMapHeader.mapType) == TRUE)
{
PlaySE(SE_SELECT);

View File

@ -285,7 +285,7 @@ static bool32 FollowerNPCHasRunningFrames(void)
static bool32 IsStateMovement(u32 state)
{
switch (state)
switch (state)
{
case MOVEMENT_ACTION_FACE_DOWN:
case MOVEMENT_ACTION_FACE_UP:
@ -510,7 +510,7 @@ static void SetSurfJump(void)
SetUpSurfBlobFieldEffect(follower);
// Adjust surf head spawn location infront of follower.
switch (direction)
switch (direction)
{
case DIR_SOUTH:
gFieldEffectArguments[1]++; // effect_y
@ -814,7 +814,7 @@ void CreateFollowerNPC(u32 gfx, u32 followerFlags, const u8 *scriptPtr)
struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId];
struct ObjectEvent *follower;
struct ObjectEventTemplate npc =
struct ObjectEventTemplate npc =
{
.localId = OBJ_EVENT_ID_NPC_FOLLOWER,
.graphicsId = gfx,
@ -932,7 +932,7 @@ u32 DetermineFollowerNPCState(struct ObjectEvent *follower, u32 state, enum Dire
break;
}
switch (state)
switch (state)
{
case MOVEMENT_ACTION_WALK_SLOW_DOWN ... MOVEMENT_ACTION_WALK_SLOW_RIGHT:
// Slow walk.
@ -1289,7 +1289,7 @@ void NPCFollow(struct ObjectEvent *npc, u32 state, bool32 ignoreScriptActive)
ObjectEventSetHeldMovement(follower, newState);
PlayerLogCoordinates(player);
switch (newState)
switch (newState)
{
case MOVEMENT_ACTION_JUMP_2_DOWN ... MOVEMENT_ACTION_JUMP_2_RIGHT:
case MOVEMENT_ACTION_JUMP_DOWN ... MOVEMENT_ACTION_JUMP_RIGHT:
@ -1308,7 +1308,7 @@ void CreateFollowerNPCAvatar(void)
return;
struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId];
struct ObjectEventTemplate clone =
struct ObjectEventTemplate clone =
{
.localId = OBJ_EVENT_ID_NPC_FOLLOWER,
.graphicsId = GetFollowerNPCSprite(),
@ -1837,7 +1837,7 @@ void ScriptFaceFollowerNPC(struct ScriptContext *ctx)
followerDirection = playerDirection;
//Flip direction.
switch (playerDirection)
switch (playerDirection)
{
case DIR_NORTH:
playerDirection = DIR_SOUTH;
@ -1860,7 +1860,7 @@ void ScriptFaceFollowerNPC(struct ScriptContext *ctx)
}
}
static const u8 *const FollowerNPCHideMovementsSpeedTable[][4] =
static const u8 *const FollowerNPCHideMovementsSpeedTable[][4] =
{
[DIR_SOUTH] = {Common_Movement_WalkDownSlow, Common_Movement_WalkDown, Common_Movement_WalkDownFast, Common_Movement_WalkDownFaster},
[DIR_NORTH] = {Common_Movement_WalkUpSlow, Common_Movement_WalkUp, Common_Movement_WalkUpFast, Common_Movement_WalkUpFaster},

View File

@ -595,7 +595,7 @@ static void LeaveFrontierPass(void)
static u32 AllocateFrontierPassData(MainCallback callback)
{
// This variable is a MAPSEC initially, but is recycled as a
// This variable is a MAPSEC initially, but is recycled as a
// bare integer near the end of the function.
mapsec_u8_t i;

View File

@ -2643,8 +2643,6 @@ void CreateFrontierBrainPokemon(void)
friendship = 0;
}
SetMonData(&gEnemyParty[monPartyId], MON_DATA_FRIENDSHIP, &friendship);
j = FALSE;
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_IS_SHINY, &j);
CalculateMonStats(&gEnemyParty[monPartyId]);
monPartyId++;
}

View File

@ -79,7 +79,7 @@ bool32 IsLastHealLocationPlayerHouse()
u32 GetHealNpcLocalId(u32 healLocationId)
{
if (healLocationId == HEAL_LOCATION_NONE || healLocationId >= NUM_HEAL_LOCATIONS)
return 0;
return LOCALID_NONE;
return sWhiteoutRespawnHealerNpcIds[healLocationId - 1];
}

View File

@ -222,11 +222,11 @@ static void Task_CloseCantUseKeyItemMessage(u8 taskId)
u8 CheckIfItemIsTMHMOrEvolutionStone(enum Item itemId)
{
if (GetItemFieldFunc(itemId) == ItemUseOutOfBattle_TMHM)
return 1;
return ITEM_IS_TM_HM;
else if (GetItemFieldFunc(itemId) == ItemUseOutOfBattle_EvolutionStone)
return 2;
return ITEM_IS_EVOLUTION_STONE;
else
return 0;
return ITEM_IS_OTHER;
}
// Mail in the bag menu can't have a message but it can be checked (view the mail background, no message)

View File

@ -2592,5 +2592,3 @@ static const struct SpritePalette sSpritePalettes[] =
{gNamingScreenMenu_Pal[4], PALTAG_OK_BUTTON},
{}
};

View File

@ -706,7 +706,7 @@ static bool32 IsWhiteoutCutscene(void)
{
if (OW_WHITEOUT_CUTSCENE < GEN_4)
return FALSE;
return GetHealNpcLocalId(GetHealLocationIndexByWarpData(&gSaveBlock1Ptr->lastHealLocation)) > 0;
return GetHealNpcLocalId(GetHealLocationIndexByWarpData(&gSaveBlock1Ptr->lastHealLocation)) != LOCALID_NONE;
}
void SetWarpDestinationToLastHealLocation(void)
@ -717,6 +717,11 @@ void SetWarpDestinationToLastHealLocation(void)
sWarpDestination = gSaveBlock1Ptr->lastHealLocation;
}
void SetWarpDestinationForTeleport(void)
{
sWarpDestination = gSaveBlock1Ptr->lastHealLocation;
}
void SetLastHealLocationWarp(u8 healLocationId)
{
const struct HealLocation *healLocation = GetHealLocation(healLocationId);

View File

@ -1134,10 +1134,10 @@ static bool8 DisplayPartyPokemonDataForMoveTutorOrEvolutionItem(u8 slot)
{
default:
return FALSE;
case 1: // TM/HM
case ITEM_IS_TM_HM: // TM/HM
DisplayPartyPokemonDataToTeachMove(slot, ItemIdToBattleMoveId(item));
break;
case 2: // Evolution stone
case ITEM_IS_EVOLUTION_STONE: // Evolution stone
if (!GetMonData(currentPokemon, MON_DATA_IS_EGG) && GetEvolutionTargetSpecies(currentPokemon, EVO_MODE_ITEM_CHECK, item, NULL, NULL, CHECK_EVO) != SPECIES_NONE)
return FALSE;
DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_NO_USE);

View File

@ -220,7 +220,7 @@ static u32 HandleRegionMapInput(struct Pokenav_RegionMapMenu *state)
state->callback = GetExitRegionMapMenuId;
return POKENAV_MAP_FUNC_EXIT;
case MAP_INPUT_R_BUTTON:
if (regionMap->mapSecType == MAPSECTYPE_CITY_CANFLY && FlagGet(OW_FLAG_POKE_RIDER)
if (regionMap->mapSecType == MAPSECTYPE_CITY_CANFLY && FlagGet(OW_FLAG_POKE_RIDER)
&& Overworld_MapTypeAllowsTeleportAndFly(gMapHeader.mapType) == TRUE)
return POKENAV_MAP_FUNC_FLY;
}
@ -772,7 +772,7 @@ void UpdateRegionMapHelpBarText(void)
{
struct RegionMap* regionMap = GetSubstructPtr(POKENAV_SUBSTRUCT_REGION_MAP);
if (regionMap->mapSecType == MAPSECTYPE_CITY_CANFLY && FlagGet(OW_FLAG_POKE_RIDER)
if (regionMap->mapSecType == MAPSECTYPE_CITY_CANFLY && FlagGet(OW_FLAG_POKE_RIDER)
&& Overworld_MapTypeAllowsTeleportAndFly(gMapHeader.mapType) == TRUE)
{
if (IsRegionMapZoomed())

View File

@ -239,7 +239,7 @@ void LoadTypeIcons(u32 battler)
struct Pokemon* mon = GetBattlerMon(battler);
u32 species = GetMonData(mon, MON_DATA_SPECIES);
if (B_SHOW_TYPES == SHOW_TYPES_NEVER
if (B_SHOW_TYPES == SHOW_TYPES_NEVER
|| (B_SHOW_TYPES == SHOW_TYPES_SEEN && !GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_SEEN)))
return;
@ -562,4 +562,3 @@ static s32 GetTypeIconBounceMovement(s32 originalY, u32 position)
struct Sprite *healthbox = &gSprites[gHealthboxSpriteIds[GetBattlerAtPosition(position)]];
return originalY + healthbox->y2;
}

View File

@ -162,22 +162,124 @@ SINGLE_BATTLE_TEST("Aerilate doesn't affect Hidden Power's type")
ASSUME(GetMoveEffect(MOVE_HIDDEN_POWER) == EFFECT_HIDDEN_POWER);
ASSUME(gTypesInfo[TYPE_ELECTRIC].isHiddenPowerType == TRUE);
ASSUME(GetSpeciesType(SPECIES_DIGLETT, 0) == TYPE_GROUND);
PLAYER(SPECIES_PINSIR) { Ability(ABILITY_AERILATE); HPIV(31); AttackIV(31); DefenseIV(31); SpAttackIV(30); SpDefenseIV(31); SpeedIV(31); } // HP Electric
PLAYER(SPECIES_PINSIR) { Item(ITEM_PINSIRITE); HPIV(31); AttackIV(31); DefenseIV(31); SpAttackIV(30); SpDefenseIV(31); SpeedIV(31); } // HP Electric
OPPONENT(SPECIES_DIGLETT);
} WHEN {
TURN { MOVE(player, MOVE_HIDDEN_POWER); }
TURN { MOVE(player, MOVE_HIDDEN_POWER, gimmick: GIMMICK_MEGA); }
} SCENE {
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_HIDDEN_POWER, player); }
MESSAGE("It doesn't affect the opposing Diglett…");
}
}
TO_DO_BATTLE_TEST("Aerilate doesn't override Electrify (Gen7+)"); // No mon with Aerilate exists in Gen8+, but probably behaves similar to Pixilate, which does.
TO_DO_BATTLE_TEST("Aerilate doesn't override Ion Deluge (Gen7+)"); // Ion Deluge doesn't exist in Gen 8+, but we probably could assume it behaves similar to under Electrify. TODO: Test by hacking SV.
TO_DO_BATTLE_TEST("Aerilate overrides Electrify (Gen6)")
TO_DO_BATTLE_TEST("Aerilate overrides Ion Deluge (Gen6)")
TO_DO_BATTLE_TEST("Aerilate doesn't affect Tera Starstorm's type");
SINGLE_BATTLE_TEST("Aerilate doesn't override Electrify")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_ELECTRIFY) == EFFECT_ELECTRIFY);
ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND);
PLAYER(SPECIES_PINSIR) { Item(ITEM_PINSIRITE); Speed(1); }
OPPONENT(SPECIES_SANDSHREW) { Moves(MOVE_ELECTRIFY); Speed(10); }
} WHEN {
TURN { MOVE(opponent, MOVE_ELECTRIFY); MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_MEGA); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIFY, opponent);
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); }
MESSAGE("It doesn't affect the opposing Sandshrew…");
}
}
SINGLE_BATTLE_TEST("Aerilate overrides Ion Deluge")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_ION_DELUGE) == EFFECT_ION_DELUGE);
ASSUME(GetSpeciesType(SPECIES_MACHOP, 0) == TYPE_FIGHTING || GetSpeciesType(SPECIES_MACHOP, 1) == TYPE_FIGHTING);
PLAYER(SPECIES_PINSIR) { Item(ITEM_PINSIRITE); Speed(1); }
OPPONENT(SPECIES_MACHOP) { Moves(MOVE_ION_DELUGE); Speed(10); }
} WHEN {
TURN { MOVE(opponent, MOVE_ION_DELUGE); MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_MEGA); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ION_DELUGE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
MESSAGE("It's super effective!");
}
}
SINGLE_BATTLE_TEST("Aerilate changes Tera Blast's type when not Terastallized")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST);
ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_MACHOP, 0) == TYPE_FIGHTING || GetSpeciesType(SPECIES_MACHOP, 1) == TYPE_FIGHTING);
ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); Moves(MOVE_SKILL_SWAP); }
OPPONENT(SPECIES_MACHOP);
} WHEN {
TURN { MOVE(opponent, MOVE_SKILL_SWAP, gimmick: GIMMICK_MEGA, target: player); }
TURN { SWITCH(opponent, 1); MOVE(player, MOVE_TERA_BLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
MESSAGE("It's super effective!");
}
}
SINGLE_BATTLE_TEST("Aerilate doesn't change Tera Blast's type when Terastallized")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST);
ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_MISDREAVUS, 0) == TYPE_GHOST);
ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP);
PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); }
OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); Moves(MOVE_SKILL_SWAP); }
OPPONENT(SPECIES_MISDREAVUS);
} WHEN {
TURN { MOVE(opponent, MOVE_SKILL_SWAP, gimmick: GIMMICK_MEGA, target: player); }
TURN { SWITCH(opponent, 1); MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); }
} SCENE {
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); }
MESSAGE("It doesn't affect the opposing Misdreavus…");
}
}
SINGLE_BATTLE_TEST("Aerilate doesn't affect Terrain Pulse's type")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERRAIN_PULSE) == EFFECT_TERRAIN_PULSE);
ASSUME(GetMoveType(MOVE_TERRAIN_PULSE) == TYPE_NORMAL);
ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP);
ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); Moves(MOVE_SKILL_SWAP); }
OPPONENT(SPECIES_SANDSHREW);
} WHEN {
TURN { MOVE(opponent, MOVE_SKILL_SWAP, gimmick: GIMMICK_MEGA, target: player); MOVE(player, MOVE_ELECTRIC_TERRAIN); }
TURN { SWITCH(opponent, 1); MOVE(player, MOVE_TERRAIN_PULSE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, player);
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERRAIN_PULSE, player); }
MESSAGE("It doesn't affect the opposing Sandshrew…");
}
}
SINGLE_BATTLE_TEST("Aerilate doesn't affect damaging Z-Move types")
{
GIVEN {
ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_MACHOP, 0) == TYPE_FIGHTING || GetSpeciesType(SPECIES_MACHOP, 1) == TYPE_FIGHTING);
ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_SALAMENCE) { Item(ITEM_SALAMENCITE); Moves(MOVE_SKILL_SWAP); }
OPPONENT(SPECIES_MACHOP);
} WHEN {
TURN { MOVE(opponent, MOVE_SKILL_SWAP, gimmick: GIMMICK_MEGA, target: player); }
TURN { SWITCH(opponent, 1); MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
NOT { MESSAGE("It's super effective!"); }
}
}
TO_DO_BATTLE_TEST("Aerilate doesn't affect Max Strike's type");
TO_DO_BATTLE_TEST("Aerilate doesn't affect Terrain Pulse's type");
TO_DO_BATTLE_TEST("Aerilate doesn't affect damaging Z-Move types");
TO_DO_BATTLE_TEST("(DYNAMAX) Aerilate turns Max Strike into Max Airstream"); // All other -ate abilities do this, so interpolating this as no Aerilate mon is available in a Dynamax game

View File

@ -164,9 +164,72 @@ SINGLE_BATTLE_TEST("Galvanize doesn't affect Hidden Power's type")
}
}
TO_DO_BATTLE_TEST("Galvanize doesn't affect Tera Starstorm's type");
SINGLE_BATTLE_TEST("Galvanize changes Tera Blast's type when not Terastallized")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST);
ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_ROOKIDEE, 0) == TYPE_FLYING || GetSpeciesType(SPECIES_ROOKIDEE, 1) == TYPE_FLYING);
PLAYER(SPECIES_GEODUDE_ALOLA) { Ability(ABILITY_GALVANIZE); }
OPPONENT(SPECIES_ROOKIDEE);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
MESSAGE("It's super effective!");
}
}
SINGLE_BATTLE_TEST("Galvanize doesn't change Tera Blast's type when Terastallized")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST);
ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND);
PLAYER(SPECIES_GEODUDE_ALOLA) { Ability(ABILITY_GALVANIZE); TeraType(TYPE_NORMAL); }
OPPONENT(SPECIES_SANDSHREW);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
NOT { MESSAGE("It doesn't affect the opposing Sandshrew…"); }
}
}
SINGLE_BATTLE_TEST("Galvanize doesn't affect Terrain Pulse's type")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERRAIN_PULSE) == EFFECT_TERRAIN_PULSE);
ASSUME(GetMoveType(MOVE_TERRAIN_PULSE) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND);
PLAYER(SPECIES_GEODUDE_ALOLA) { Ability(ABILITY_GALVANIZE); }
OPPONENT(SPECIES_SANDSHREW);
} WHEN {
TURN { MOVE(opponent, MOVE_GRASSY_TERRAIN); MOVE(player, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_TERRAIN_PULSE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERRAIN_PULSE, player);
MESSAGE("It's super effective!");
}
}
SINGLE_BATTLE_TEST("Galvanize doesn't affect damaging Z-Move types")
{
GIVEN {
ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_GYARADOS, 0) == TYPE_WATER || GetSpeciesType(SPECIES_GYARADOS, 1) == TYPE_WATER);
PLAYER(SPECIES_GEODUDE_ALOLA) { Ability(ABILITY_GALVANIZE); Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_GYARADOS);
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
NOT { MESSAGE("It's super effective!"); }
}
}
TO_DO_BATTLE_TEST("Galvanize doesn't affect Max Strike's type");
TO_DO_BATTLE_TEST("Galvanize doesn't affect Terrain Pulse's type");
TO_DO_BATTLE_TEST("Galvanize doesn't affect damaging Z-Move types");
TO_DO_BATTLE_TEST("(DYNAMAX) Galvanize turns Max Strike into Max Lightning when not used by Gigantamax Pikachu/Toxtricity");
//TO_DO_BATTLE_TEST("(DYNAMAX) Galvanize doesn't turn Max Strike into Max Lightning when used by Gigantamax Pikachu/Toxtricity, instead becoming G-Max Volt Crash/Stun Shock"); // Marked in Bulbapedia as "needs research", so this assumes that it behaves like Pixilate.

View File

@ -274,7 +274,72 @@ SINGLE_BATTLE_TEST("Normalize doesn't affect Hidden Power's type")
}
}
TO_DO_BATTLE_TEST("Aerilate doesn't affect Tera Starstorm's type");
TO_DO_BATTLE_TEST("Normalize makes Flying Press do Normal/Flying damage");
TO_DO_BATTLE_TEST("Normalize doesn't affect Terrain Pulse's type");
TO_DO_BATTLE_TEST("Normalize doesn't affect damaging Z-Move types");
SINGLE_BATTLE_TEST("Normalize doesn't change Tera Blast's type when Terastallized")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST);
ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_MISDREAVUS, 0) == TYPE_GHOST);
PLAYER(SPECIES_SKITTY) { Ability(ABILITY_NORMALIZE); TeraType(TYPE_DARK); }
OPPONENT(SPECIES_MISDREAVUS);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
MESSAGE("It's super effective!");
}
}
SINGLE_BATTLE_TEST("Normalize makes Flying Press do Normal/Flying damage")
{
enum Ability ability;
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; }
PARAMETRIZE { ability = ABILITY_NORMALIZE; }
GIVEN {
ASSUME(GetSpeciesType(SPECIES_GOLEM, 0) == TYPE_ROCK || GetSpeciesType(SPECIES_GOLEM, 1) == TYPE_ROCK);
PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_FLYING_PRESS); }
OPPONENT(SPECIES_GOLEM);
} WHEN {
TURN { MOVE(player, MOVE_FLYING_PRESS); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLYING_PRESS, player);
if (ability == ABILITY_NORMALIZE)
MESSAGE("It's not very effective…");
else
NOT { MESSAGE("It's not very effective…"); }
}
}
SINGLE_BATTLE_TEST("Normalize doesn't affect Terrain Pulse's type")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERRAIN_PULSE) == EFFECT_TERRAIN_PULSE);
ASSUME(GetMoveType(MOVE_TERRAIN_PULSE) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND);
PLAYER(SPECIES_SKITTY) { Ability(ABILITY_NORMALIZE); }
OPPONENT(SPECIES_SANDSHREW);
} WHEN {
TURN { MOVE(opponent, MOVE_ELECTRIC_TERRAIN); MOVE(player, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_TERRAIN_PULSE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, opponent);
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERRAIN_PULSE, player); }
MESSAGE("It doesn't affect the opposing Sandshrew…");
}
}
SINGLE_BATTLE_TEST("Normalize doesn't affect damaging Z-Move types")
{
GIVEN {
ASSUME(GetMoveType(MOVE_WATER_GUN) == TYPE_WATER);
ASSUME(GetSpeciesType(SPECIES_GOLEM, 0) == TYPE_ROCK || GetSpeciesType(SPECIES_GOLEM, 1) == TYPE_ROCK);
PLAYER(SPECIES_SKITTY) { Ability(ABILITY_NORMALIZE); Item(ITEM_WATERIUM_Z); Moves(MOVE_WATER_GUN); }
OPPONENT(SPECIES_GOLEM);
} WHEN {
TURN { MOVE(player, MOVE_WATER_GUN, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYDRO_VORTEX, player);
MESSAGE("It's super effective!");
}
}

View File

@ -138,13 +138,104 @@ SINGLE_BATTLE_TEST("Pixilate doesn't affect Hidden Power's type")
}
}
TO_DO_BATTLE_TEST("Pixilate doesn't override Electrify (Gen7+)");
TO_DO_BATTLE_TEST("Pixilate doesn't override Ion Deluge (Gen7+)"); // Ion Deluge doesn't exist in Gen 8+, but we probably could assume it behaves similar to under Electrify. TODO: Test by hacking SV.
TO_DO_BATTLE_TEST("Pixilate overrides Electrify (Gen6)")
TO_DO_BATTLE_TEST("Pixilate overrides Ion Deluge (Gen6)")
TO_DO_BATTLE_TEST("Pixilate doesn't affect Tera Starstorm's type");
SINGLE_BATTLE_TEST("Pixilate doesn't override Electrify")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_ELECTRIFY) == EFFECT_ELECTRIFY);
ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND);
PLAYER(SPECIES_SYLVEON) { Ability(ABILITY_PIXILATE); }
OPPONENT(SPECIES_SANDSHREW);
} WHEN {
TURN { MOVE(opponent, MOVE_ELECTRIFY); MOVE(player, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIFY, opponent);
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); }
MESSAGE("It doesn't affect the opposing Sandshrew…");
}
}
SINGLE_BATTLE_TEST("Pixilate overrides Ion Deluge")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_ION_DELUGE) == EFFECT_ION_DELUGE);
ASSUME(GetSpeciesType(SPECIES_BAGON, 0) == TYPE_DRAGON || GetSpeciesType(SPECIES_BAGON, 1) == TYPE_DRAGON);
PLAYER(SPECIES_SYLVEON) { Ability(ABILITY_PIXILATE); }
OPPONENT(SPECIES_BAGON);
} WHEN {
TURN { MOVE(opponent, MOVE_ION_DELUGE); MOVE(player, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ION_DELUGE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
MESSAGE("It's super effective!");
}
}
SINGLE_BATTLE_TEST("Pixilate changes Tera Blast's type when not Terastallized")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST);
ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_MACHOP, 0) == TYPE_FIGHTING || GetSpeciesType(SPECIES_MACHOP, 1) == TYPE_FIGHTING);
PLAYER(SPECIES_SYLVEON) { Ability(ABILITY_PIXILATE); }
OPPONENT(SPECIES_MACHOP);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
MESSAGE("It's super effective!");
}
}
SINGLE_BATTLE_TEST("Pixilate doesn't change Tera Blast's type when Terastallized")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST);
ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_MISDREAVUS, 0) == TYPE_GHOST);
PLAYER(SPECIES_SYLVEON) { Ability(ABILITY_PIXILATE); TeraType(TYPE_NORMAL); }
OPPONENT(SPECIES_MISDREAVUS);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); }
} SCENE {
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); }
MESSAGE("It doesn't affect the opposing Misdreavus…");
}
}
SINGLE_BATTLE_TEST("Pixilate doesn't affect Terrain Pulse's type")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERRAIN_PULSE) == EFFECT_TERRAIN_PULSE);
ASSUME(GetMoveType(MOVE_TERRAIN_PULSE) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND);
PLAYER(SPECIES_SYLVEON) { Ability(ABILITY_PIXILATE); }
OPPONENT(SPECIES_SANDSHREW);
} WHEN {
TURN { MOVE(opponent, MOVE_ELECTRIC_TERRAIN); MOVE(player, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_TERRAIN_PULSE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, opponent);
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERRAIN_PULSE, player); }
MESSAGE("It doesn't affect the opposing Sandshrew…");
}
}
SINGLE_BATTLE_TEST("Pixilate doesn't affect damaging Z-Move types")
{
GIVEN {
ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_BAGON, 0) == TYPE_DRAGON || GetSpeciesType(SPECIES_BAGON, 1) == TYPE_DRAGON);
PLAYER(SPECIES_SYLVEON) { Ability(ABILITY_PIXILATE); Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_BAGON);
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
NOT { MESSAGE("It's super effective!"); }
}
}
TO_DO_BATTLE_TEST("Pixilate doesn't affect Max Strike's type");
TO_DO_BATTLE_TEST("Pixilate doesn't affect Terrain Pulse's type");
TO_DO_BATTLE_TEST("Pixilate doesn't affect damaging Z-Move types");
TO_DO_BATTLE_TEST("(DYNAMAX) Pixilate turns Max Strike into Max Starfall when not used by Gigantamax Alcremie");
TO_DO_BATTLE_TEST("(DYNAMAX) Pixilate doesn't turn Max Strike into Max Starfall when used by Gigantamax Alcremie, instead becoming G-Max Finale");

View File

@ -137,13 +137,104 @@ SINGLE_BATTLE_TEST("Refrigerate doesn't affect Hidden Power's type")
}
}
TO_DO_BATTLE_TEST("Refrigerate doesn't override Electrify (Gen7+)"); // No mon with Refrigerate exists in Gen8+, but probably behaves similar to Pixilate, which does.
TO_DO_BATTLE_TEST("Refrigerate doesn't override Ion Deluge (Gen7+)"); // Ion Deluge doesn't exist in Gen 8+, but we probably could assume it behaves similar to under Electrify. TODO: Test by hacking SV.
TO_DO_BATTLE_TEST("Refrigerate overrides Electrify (Gen6)")
TO_DO_BATTLE_TEST("Refrigerate overrides Ion Deluge (Gen6)")
TO_DO_BATTLE_TEST("Refrigerate doesn't affect Tera Starstorm's type");
SINGLE_BATTLE_TEST("Refrigerate doesn't override Electrify")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_ELECTRIFY) == EFFECT_ELECTRIFY);
ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND);
PLAYER(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); }
OPPONENT(SPECIES_SANDSHREW);
} WHEN {
TURN { MOVE(opponent, MOVE_ELECTRIFY); MOVE(player, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIFY, opponent);
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); }
MESSAGE("It doesn't affect the opposing Sandshrew…");
}
}
SINGLE_BATTLE_TEST("Refrigerate overrides Ion Deluge")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_ION_DELUGE) == EFFECT_ION_DELUGE);
ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND);
PLAYER(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); }
OPPONENT(SPECIES_SANDSHREW);
} WHEN {
TURN { MOVE(opponent, MOVE_ION_DELUGE); MOVE(player, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ION_DELUGE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
MESSAGE("It's super effective!");
}
}
SINGLE_BATTLE_TEST("Refrigerate changes Tera Blast's type when not Terastallized")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST);
ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_CHARMANDER, 0) == TYPE_FIRE || GetSpeciesType(SPECIES_CHARMANDER, 1) == TYPE_FIRE);
PLAYER(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); }
OPPONENT(SPECIES_CHARMANDER);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player);
MESSAGE("It's not very effective…");
}
}
SINGLE_BATTLE_TEST("Refrigerate doesn't change Tera Blast's type when Terastallized")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST);
ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_MISDREAVUS, 0) == TYPE_GHOST);
PLAYER(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); TeraType(TYPE_NORMAL); }
OPPONENT(SPECIES_MISDREAVUS);
} WHEN {
TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); }
} SCENE {
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); }
MESSAGE("It doesn't affect the opposing Misdreavus…");
}
}
SINGLE_BATTLE_TEST("Refrigerate doesn't affect Terrain Pulse's type")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TERRAIN_PULSE) == EFFECT_TERRAIN_PULSE);
ASSUME(GetMoveType(MOVE_TERRAIN_PULSE) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND);
PLAYER(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); }
OPPONENT(SPECIES_SANDSHREW);
} WHEN {
TURN { MOVE(opponent, MOVE_ELECTRIC_TERRAIN); MOVE(player, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_TERRAIN_PULSE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, opponent);
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERRAIN_PULSE, player); }
MESSAGE("It doesn't affect the opposing Sandshrew…");
}
}
SINGLE_BATTLE_TEST("Refrigerate doesn't affect damaging Z-Move types")
{
GIVEN {
ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL);
ASSUME(GetSpeciesType(SPECIES_BAGON, 0) == TYPE_DRAGON || GetSpeciesType(SPECIES_BAGON, 1) == TYPE_DRAGON);
PLAYER(SPECIES_AMAURA) { Ability(ABILITY_REFRIGERATE); Item(ITEM_NORMALIUM_Z); }
OPPONENT(SPECIES_BAGON);
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_Z_MOVE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player);
NOT { MESSAGE("It's super effective!"); }
}
}
TO_DO_BATTLE_TEST("Refrigerate doesn't affect Max Strike's type");
TO_DO_BATTLE_TEST("Refrigerate doesn't affect Terrain Pulse's type");
TO_DO_BATTLE_TEST("Refrigerate doesn't affect damaging Z-Move types");
TO_DO_BATTLE_TEST("(DYNAMAX) Refrigerate turns Max Strike into Max Hailstorm when not used by Gigantamax Lapras");
//TO_DO_BATTLE_TEST("(DYNAMAX) Refrigerate doesn't turn Max Strike into Max Hailstorm when used by Gigantamax Lapras, instead becoming G-Max Resonance"); // Marked in Bulbapedia as "needs research", so this assumes that it behaves like Pixilate.

View File

@ -946,27 +946,33 @@ AI_DOUBLE_BATTLE_TEST("AI does not use Helping Hand on Good as Gold ally")
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Tailwind")
AI_DOUBLE_BATTLE_TEST("AI uses Tailwind based on speed matchups")
{
u32 speed1, speed2, speed3, speed4;
bool32 expectTailwind;
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 20; speed4 = 20; }
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 5; speed4 = 5; }
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 15; speed4 = 15; }
PARAMETRIZE { speed1 = 1; speed2 = 1; speed3 = 5; speed4 = 5; }
PARAMETRIZE { speed1 = 1; speed2 = 20; speed3 = 15; speed4 = 15; }
PARAMETRIZE { speed1 = 1; speed2 = 20; speed3 = 20; speed4 = 15; }
// All four comparisons qualify -> tailwindScore = 5
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 20; speed4 = 20; expectTailwind = TRUE; }
// Only the attacker flips one foe matchup -> tailwindScore = 2
PARAMETRIZE { speed1 = 20; speed2 = 40; speed3 = 20; speed4 = 50; expectTailwind = TRUE; }
// Only the partner flips one foe matchup -> tailwindScore = 2
PARAMETRIZE { speed1 = 10; speed2 = 29; speed3 = 50; speed4 = 15; expectTailwind = TRUE; }
// Too slow: even after doubling, still slower than both foes -> tailwindScore = 0.
PARAMETRIZE { speed1 = 40; speed2 = 40; speed3 = 10; speed4 = 10; expectTailwind = FALSE; }
// Already faster: Tailwind doesn't improve matchups -> tailwindScore = 0.
PARAMETRIZE { speed1 = 5; speed2 = 5; speed3 = 10; speed4 = 10; expectTailwind = FALSE; }
// Boundary: speed*2 == foe speed does not count -> tailwindScore = 0.
PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 10; speed4 = 30; expectTailwind = FALSE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_AFTER_YOU) == EFFECT_AFTER_YOU);
ASSUME(GetMoveEffect(MOVE_TRICK_ROOM) == EFFECT_TRICK_ROOM);
ASSUME(GetMoveEffect(MOVE_TAILWIND) == EFFECT_TAILWIND);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE);
PLAYER(SPECIES_WOBBUFFET) { Speed(speed1); }
PLAYER(SPECIES_WOBBUFFET) { Speed(speed2); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(speed3); Moves(MOVE_TAILWIND, MOVE_HEADBUTT); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(speed4); Moves(MOVE_TAILWIND, MOVE_HEADBUTT); }
} WHEN {
if (speed3 > 10)
if (expectTailwind)
TURN { EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); }
else
TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); }
@ -1093,3 +1099,29 @@ AI_DOUBLE_BATTLE_TEST("AI prefers to Fake Out the opponent vulnerable to flinchi
TURN { EXPECT_MOVE(opponentLeft, MOVE_FAKE_OUT, target:playerRight); }
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Gear Up")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); }
OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); }
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_GEAR_UP); }
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Magnetic Flux")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); }
OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); }
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_MAGNETIC_FLUX); }
}
}

View File

@ -235,7 +235,7 @@ AI_MULTI_BATTLE_TEST("Pollen Puff: AI correctly scores moves with EFFECT_HIT_ENE
MULTI_OPPONENT_A(SPECIES_WOBBUFFET) { Speed(1); HP(50); Moves(MOVE_POLLEN_PUFF); }
MULTI_OPPONENT_B(SPECIES_WOBBUFFET) { Speed(1); HP(50); Moves(MOVE_POLLEN_PUFF); }
} WHEN {
TURN {
TURN {
// Targeting ally
SCORE_EQ_VAL(opponentLeft, MOVE_POLLEN_PUFF, AI_SCORE_DEFAULT + WEAK_EFFECT, target:opponentRight);
SCORE_EQ_VAL(playerRight, MOVE_POLLEN_PUFF, AI_SCORE_DEFAULT + WEAK_EFFECT, target:playerLeft);

View File

@ -1157,15 +1157,18 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has an
PARAMETRIZE { aiMon = SPECIES_JOLTEON; absorbingAbility = ABILITY_VOLT_ABSORB; move = MOVE_THUNDERBOLT; }
PARAMETRIZE { aiMon = SPECIES_ELECTIVIRE; absorbingAbility = ABILITY_MOTOR_DRIVE; move = MOVE_THUNDERBOLT; }
PARAMETRIZE { aiMon = SPECIES_MANECTRIC; absorbingAbility = ABILITY_LIGHTNING_ROD; move = MOVE_THUNDERBOLT; }
PARAMETRIZE { aiMon = SPECIES_ELECTIVIRE; absorbingAbility = ABILITY_MOTOR_DRIVE; move = MOVE_THUNDERBOLT; }
PARAMETRIZE { aiMon = SPECIES_AZUMARILL; absorbingAbility = ABILITY_SAP_SIPPER; move = MOVE_GIGA_DRAIN; }
PARAMETRIZE { aiMon = SPECIES_ORTHWORM; absorbingAbility = ABILITY_EARTH_EATER; move = MOVE_EARTHQUAKE; }
PARAMETRIZE { aiMon = SPECIES_BRONZONG; absorbingAbility = ABILITY_LEVITATE; move = MOVE_EARTHQUAKE; }
PARAMETRIZE { aiMon = SPECIES_ELECTRODE; absorbingAbility = ABILITY_SOUNDPROOF; move = MOVE_HYPER_VOICE; }
PARAMETRIZE { aiMon = SPECIES_CHESNAUGHT; absorbingAbility = ABILITY_BULLETPROOF; move = MOVE_SLUDGE_BOMB; }
PARAMETRIZE { aiMon = SPECIES_BRAMBLEGHAST; absorbingAbility = ABILITY_WIND_RIDER; move = MOVE_HURRICANE; }
PARAMETRIZE { aiMon = SPECIES_BRAMBLEGHAST; absorbingAbility = ABILITY_WIND_RIDER; move = MOVE_HEAT_WAVE; }
PARAMETRIZE { aiMon = SPECIES_SHELLDER; absorbingAbility = ABILITY_OVERCOAT; move = MOVE_MAGIC_POWDER; }
PARAMETRIZE { aiMon = SPECIES_SHELLDER; absorbingAbility = ABILITY_OVERCOAT; move = MOVE_STUN_SPORE; }
GIVEN {
WITH_CONFIG(CONFIG_REDIRECT_ABILITY_IMMUNITY, GEN_5);
WITH_CONFIG(CONFIG_POWDER_OVERCOAT, GEN_6);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
PLAYER(SPECIES_ZIGZAGOON) { Moves(move); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_SCRATCH); }

View File

@ -259,13 +259,13 @@ SINGLE_BATTLE_TEST("Morpeko Hangry reverts to Full Belly Form upon battle end af
SINGLE_BATTLE_TEST("Ogerpon reverts to the correct form upon battle end after terastallizing")
{
u32 species;
PARAMETRIZE { species = SPECIES_OGERPON_TEAL; }
PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING; }
PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME; }
PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE; }
u32 species, item;
PARAMETRIZE { species = SPECIES_OGERPON_TEAL; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING; item = ITEM_WELLSPRING_MASK; }
PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME; item = ITEM_HEARTHFLAME_MASK; }
PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE; item = ITEM_CORNERSTONE_MASK; }
GIVEN {
PLAYER(species);
PLAYER(species) { Item(item); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }

View File

@ -69,3 +69,46 @@ DOUBLE_BATTLE_TEST("Causing a Forecast or Flower Gift Pokémon to faint should n
}
}
}
SINGLE_BATTLE_TEST("Ogerpon reverts to the correct form upon fainting after terastallizing")
{
u32 species, item;
PARAMETRIZE { species = SPECIES_OGERPON_TEAL; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING; item = ITEM_WELLSPRING_MASK; }
PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME; item = ITEM_HEARTHFLAME_MASK; }
PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE; item = ITEM_CORNERSTONE_MASK; }
GIVEN {
PLAYER(species) { HP(1); Item(item); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA);
MOVE(opponent, MOVE_SCRATCH);
SEND_OUT(player, 1);
}
TURN { USE_ITEM(player, ITEM_REVIVE, 0); }
TURN { SWITCH(player, 0); }
} THEN {
EXPECT_EQ(player->species, species);
}
}
SINGLE_BATTLE_TEST("Terapagos reverts to the correct form upon fainting after terastallizing")
{
GIVEN {
PLAYER(SPECIES_TERAPAGOS_NORMAL) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(player, MOVE_MEMENTO, gimmick: GIMMICK_TERA);
MOVE(opponent, MOVE_SCRATCH);
SEND_OUT(player, 1);
}
TURN { USE_ITEM(player, ITEM_REVIVE, 0); }
TURN { SWITCH(player, 0); }
} THEN {
EXPECT_EQ(player->species, SPECIES_TERAPAGOS_TERASTAL); // Not Normal form due to Tera Shift
}
}

View File

@ -43,3 +43,22 @@ SINGLE_BATTLE_TEST("Dynamax: Venusaur returns its base Form upon battle end afte
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_VENUSAUR);
}
}
SINGLE_BATTLE_TEST("Dynamax: Venusaur returns its base Form upon fainting end after Gigantamaxing")
{
GIVEN {
PLAYER(SPECIES_VENUSAUR) { HP(1); GigantamaxFactor(TRUE); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_DYNAMAX);
MOVE(opponent, MOVE_SCRATCH);
SEND_OUT(player, 1);
}
TURN { USE_ITEM(player, ITEM_REVIVE, 0); }
TURN { SWITCH(player, 0); }
} THEN {
EXPECT_EQ(player->species, SPECIES_VENUSAUR);
}
}

View File

@ -216,3 +216,41 @@ SINGLE_BATTLE_TEST("Rayquaza returns its base Form upon battle end after Mega Ev
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), SPECIES_RAYQUAZA);
}
}
SINGLE_BATTLE_TEST("Venusaur returns its base Form upon fainting end after Mega Evolving")
{
GIVEN {
PLAYER(SPECIES_VENUSAUR) { HP(1); Item(ITEM_VENUSAURITE); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA);
MOVE(opponent, MOVE_SCRATCH);
SEND_OUT(player, 1);
}
TURN { USE_ITEM(player, ITEM_REVIVE, 0); }
TURN { SWITCH(player, 0); }
} THEN {
EXPECT_EQ(player->species, SPECIES_VENUSAUR);
}
}
SINGLE_BATTLE_TEST("Rayquaza returns its base Form upon fainting end after Mega Evolving")
{
GIVEN {
PLAYER(SPECIES_RAYQUAZA) { HP(1); Moves(MOVE_DRAGON_ASCENT, MOVE_CELEBRATE); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA);
MOVE(opponent, MOVE_SCRATCH);
SEND_OUT(player, 1);
}
TURN { USE_ITEM(player, ITEM_REVIVE, 0); }
TURN { SWITCH(player, 0); }
} THEN {
EXPECT_EQ(player->species, SPECIES_RAYQUAZA);
}
}

View File

@ -1,7 +1,7 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Primal reversion happens for Groudon only when holding Red Orb")
SINGLE_BATTLE_TEST("Primal Reversion happens for Groudon only when holding Red Orb")
{
u16 heldItem;
PARAMETRIZE { heldItem = ITEM_NONE; }
@ -33,7 +33,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens for Groudon only when holding Red O
}
}
SINGLE_BATTLE_TEST("Primal reversion happens for Kyogre only when holding Blue Orb")
SINGLE_BATTLE_TEST("Primal Reversion happens for Kyogre only when holding Blue Orb")
{
u16 heldItem;
PARAMETRIZE { heldItem = ITEM_NONE; }
@ -65,7 +65,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens for Kyogre only when holding Blue O
}
}
DOUBLE_BATTLE_TEST("Primal reversion's order is determined by Speed - opponent faster")
DOUBLE_BATTLE_TEST("Primal Reversion's order is determined by Speed - opponent faster")
{
GIVEN {
PLAYER(SPECIES_KYOGRE) { Item(ITEM_BLUE_ORB); Speed(5); }
@ -91,7 +91,7 @@ DOUBLE_BATTLE_TEST("Primal reversion's order is determined by Speed - opponent f
}
}
DOUBLE_BATTLE_TEST("Primal reversion's order is determined by Speed - player faster")
DOUBLE_BATTLE_TEST("Primal Reversion's order is determined by Speed - player faster")
{
GIVEN {
PLAYER(SPECIES_KYOGRE) { Item(ITEM_BLUE_ORB); Speed(20); }
@ -117,7 +117,7 @@ DOUBLE_BATTLE_TEST("Primal reversion's order is determined by Speed - player fas
}
}
SINGLE_BATTLE_TEST("Primal reversion happens after a mon is sent out after a mon is fainted")
SINGLE_BATTLE_TEST("Primal Reversion happens after a mon is sent out after a mon is fainted")
{
GIVEN {
ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH));
@ -136,7 +136,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens after a mon is sent out after a mon
}
}
SINGLE_BATTLE_TEST("Primal reversion happens after a mon is switched in")
SINGLE_BATTLE_TEST("Primal Reversion happens after a mon is switched in")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@ -153,7 +153,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens after a mon is switched in")
}
}
SINGLE_BATTLE_TEST("Primal reversion happens after a switch-in caused by Eject Button")
SINGLE_BATTLE_TEST("Primal Reversion happens after a switch-in caused by Eject Button")
{
GIVEN {
ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH));
@ -174,7 +174,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens after a switch-in caused by Eject B
}
}
SINGLE_BATTLE_TEST("Primal reversion happens after a switch-in caused by Red Card")
SINGLE_BATTLE_TEST("Primal Reversion happens after a switch-in caused by Red Card")
{
GIVEN {
ASSUME(!IsBattleMoveStatus(MOVE_SCRATCH));
@ -194,7 +194,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens after a switch-in caused by Red Car
}
}
SINGLE_BATTLE_TEST("Primal reversion happens after the entry hazards damage")
SINGLE_BATTLE_TEST("Primal Reversion happens after the entry hazards damage")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_SPIKES) == EFFECT_SPIKES);
@ -215,7 +215,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens after the entry hazards damage")
}
}
SINGLE_BATTLE_TEST("Primal reversion happens immediately if it was brought in by U-turn")
SINGLE_BATTLE_TEST("Primal Reversion happens immediately if it was brought in by U-turn")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@ -236,7 +236,7 @@ SINGLE_BATTLE_TEST("Primal reversion happens immediately if it was brought in by
}
DOUBLE_BATTLE_TEST("Primal reversion triggers for multiple battlers if multiple fainted the previous turn")
DOUBLE_BATTLE_TEST("Primal Reversion triggers for multiple battlers if multiple fainted the previous turn")
{
GIVEN {
ASSUME(GetMoveTarget(MOVE_EARTHQUAKE) == TARGET_FOES_AND_ALLY);
@ -259,7 +259,7 @@ DOUBLE_BATTLE_TEST("Primal reversion triggers for multiple battlers if multiple
}
}
DOUBLE_BATTLE_TEST("Primal reversion triggers for all battlers if multiple fainted the previous turn")
DOUBLE_BATTLE_TEST("Primal Reversion triggers for all battlers if multiple fainted the previous turn")
{
GIVEN {
ASSUME(IsExplosionMove(MOVE_EXPLOSION));
@ -287,7 +287,7 @@ DOUBLE_BATTLE_TEST("Primal reversion triggers for all battlers if multiple faint
}
}
DOUBLE_BATTLE_TEST("Primal reversion and other switch-in effects trigger for all battlers if multiple fainted the previous turn")
DOUBLE_BATTLE_TEST("Primal Reversion and other switch-in effects trigger for all battlers if multiple fainted the previous turn")
{
GIVEN {
ASSUME(IsExplosionMove(MOVE_EXPLOSION));
@ -333,7 +333,7 @@ DOUBLE_BATTLE_TEST("Primal reversion and other switch-in effects trigger for all
}
}
SINGLE_BATTLE_TEST("Primal reversion is reverted upon battle end")
SINGLE_BATTLE_TEST("Primal Reversion is reverted upon battle end")
{
u32 species, item;
PARAMETRIZE { species = SPECIES_GROUDON; item = ITEM_RED_ORB; }
@ -347,3 +347,25 @@ SINGLE_BATTLE_TEST("Primal reversion is reverted upon battle end")
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), species);
}
}
SINGLE_BATTLE_TEST("Primal Reversion is NOT reverted upon fainting")
{
u32 species, item, targetSpecies;
PARAMETRIZE { species = SPECIES_GROUDON; item = ITEM_RED_ORB; targetSpecies = SPECIES_GROUDON_PRIMAL; }
PARAMETRIZE { species = SPECIES_KYOGRE; item = ITEM_BLUE_ORB; targetSpecies = SPECIES_KYOGRE_PRIMAL; }
GIVEN {
PLAYER(species) { HP(1); Item(item); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(player, MOVE_CELEBRATE);
MOVE(opponent, MOVE_SCRATCH);
SEND_OUT(player, 1);
}
TURN { USE_ITEM(player, ITEM_REVIVE, 0); }
TURN { SWITCH(player, 0); }
} THEN {
EXPECT_EQ(player->species, targetSpecies);
}
}

View File

@ -135,3 +135,25 @@ SINGLE_BATTLE_TEST("Necrozma returns its proper Form upon battle end after Ultra
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_SPECIES), species);
}
}
SINGLE_BATTLE_TEST("Necrozma returns its proper Form upon fainting after Ultra Bursting")
{
u32 species;
PARAMETRIZE { species = SPECIES_NECROZMA_DUSK_MANE; }
PARAMETRIZE { species = SPECIES_NECROZMA_DAWN_WINGS; }
GIVEN {
PLAYER(species) { HP(1); Item(ITEM_ULTRANECROZIUM_Z); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_ULTRA_BURST);
MOVE(opponent, MOVE_SCRATCH);
SEND_OUT(player, 1);
}
TURN { USE_ITEM(player, ITEM_REVIVE, 0); }
TURN { SWITCH(player, 0); }
} THEN {
EXPECT_EQ(player->species, species);
}
}

View File

@ -761,19 +761,19 @@ SINGLE_BATTLE_TEST("(TERA) Transformed Pokémon can't Terastalize")
SINGLE_BATTLE_TEST("(TERA) Pokemon with Tera forms change upon Terastallizing")
{
u32 species, targetSpecies;
PARAMETRIZE { species = SPECIES_OGERPON_TEAL; targetSpecies = SPECIES_OGERPON_TEAL_TERA; }
PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING; targetSpecies = SPECIES_OGERPON_WELLSPRING_TERA; }
PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME; targetSpecies = SPECIES_OGERPON_HEARTHFLAME_TERA; }
PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE; targetSpecies = SPECIES_OGERPON_CORNERSTONE_TERA; }
PARAMETRIZE { species = SPECIES_TERAPAGOS_TERASTAL; targetSpecies = SPECIES_TERAPAGOS_STELLAR; }
u32 species, target, item;
PARAMETRIZE { species = SPECIES_OGERPON_TEAL; target = SPECIES_OGERPON_TEAL_TERA; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING; target = SPECIES_OGERPON_WELLSPRING_TERA; item = ITEM_WELLSPRING_MASK; }
PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME; target = SPECIES_OGERPON_HEARTHFLAME_TERA; item = ITEM_HEARTHFLAME_MASK; }
PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE; target = SPECIES_OGERPON_CORNERSTONE_TERA; item = ITEM_CORNERSTONE_MASK; }
PARAMETRIZE { species = SPECIES_TERAPAGOS_TERASTAL; target = SPECIES_TERAPAGOS_STELLAR; item = ITEM_NONE; }
GIVEN {
PLAYER(species);
PLAYER(species) { Item(item); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} THEN {
EXPECT_EQ(player->species, targetSpecies);
EXPECT_EQ(player->species, target);
}
}

View File

@ -87,7 +87,6 @@ SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used")
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_BEAK_BLAST); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, player);
MESSAGE("Wobbuffet started heating up its beak!");
@ -112,6 +111,35 @@ SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used")
}
}
SINGLE_BATTLE_TEST("Beak Blast doesn't burn when charging a two turn move")
{
u32 move;
PARAMETRIZE { move = MOVE_BOUNCE; }
PARAMETRIZE { move = MOVE_DIG; }
GIVEN {
ASSUME(MoveMakesContact(MOVE_BOUNCE));
ASSUME(MoveMakesContact(MOVE_DIG));
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect);
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_BEAK_BLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, player);
MESSAGE("Wobbuffet started heating up its beak!");
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
HP_BAR(player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent);
MESSAGE("The opposing Wobbuffet was burned!");
STATUS_ICON(opponent, burn: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Beak Blast doesn't burn fire types")
{
GIVEN {

View File

@ -1,14 +1,123 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Foresight removes Ghost's type immunity to Normal and Fighting types")
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_FORESIGHT) == EFFECT_FORESIGHT);
}
SINGLE_BATTLE_TEST("Foresight removes Ghost's type immunity to Normal and Fighting types")
{
GIVEN {
ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL);
ASSUME(GetMoveType(MOVE_LOW_KICK) == TYPE_FIGHTING);
ASSUME(GetSpeciesType(SPECIES_GENGAR, 0) == TYPE_GHOST);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SCRATCH, MOVE_LOW_KICK); }
OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SPLASH); }
} WHEN {
TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_SPLASH); }
TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); }
TURN { MOVE(player, MOVE_LOW_KICK); MOVE(opponent, MOVE_SPLASH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_LOW_KICK, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Foresight always hits unless the target is semi-invulnerable")
{
bool32 semiInvulnerable = FALSE;
PARAMETRIZE { semiInvulnerable = FALSE; }
PARAMETRIZE { semiInvulnerable = TRUE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP);
ASSUME(GetMoveEffect(MOVE_FLY) == EFFECT_SEMI_INVULNERABLE);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SPLASH); Speed(10); }
OPPONENT(SPECIES_SQUAWKABILLY) { Moves(MOVE_DOUBLE_TEAM, MOVE_FLY); Speed(20); }
} WHEN {
if (semiInvulnerable)
TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_FLY); }
else
TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_DOUBLE_TEAM); }
if (semiInvulnerable)
TURN { MOVE(player, MOVE_SPLASH); SKIP_TURN(opponent); }
} SCENE {
if (semiInvulnerable) {
MESSAGE("The opposing Squawkabilly avoided the attack!");
} else {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
}
}
}
SINGLE_BATTLE_TEST("Foresight causes moves against the target to ignore positive evasion stat stages")
{
PASSES_RANDOMLY(100, 100, RNG_ACCURACY);
GIVEN {
ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SCRATCH); Speed(10); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DOUBLE_TEAM, MOVE_SPLASH); Speed(20); }
} WHEN {
TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_DOUBLE_TEAM); }
TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Foresight fails if the target is already under its effect (Gen 2 and Gen5+)")
{
u32 genConfig = GEN_2;
PARAMETRIZE { genConfig = GEN_2; }
PARAMETRIZE { genConfig = GEN_5; }
GIVEN {
WITH_CONFIG(CONFIG_FORESIGHT_FAIL, genConfig);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FORESIGHT); }
TURN { MOVE(player, MOVE_FORESIGHT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Foresight doesn't fail if the target is already under its effect (Gen 3-4)")
{
u32 genConfig = GEN_3;
PARAMETRIZE { genConfig = GEN_3; }
PARAMETRIZE { genConfig = GEN_4; }
GIVEN {
WITH_CONFIG(CONFIG_FORESIGHT_FAIL, genConfig);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FORESIGHT); }
TURN { MOVE(player, MOVE_FORESIGHT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player);
NOT MESSAGE("But it failed!");
}
}
TO_DO_BATTLE_TEST("Foresight causes accuracy/evasion stat changes only between the user/target when the user's accuracy stage is less than the target's evasion stage (Gen 2)")
TO_DO_BATTLE_TEST("Foresight causes all moves against the target to ignore evasion stat changes (Gen 3)")
TO_DO_BATTLE_TEST("Foresight causes all moves against the target to ignore only positive evasion stat changes (Gen 4+)") // Eg. Doesn't ignore Sweet Scent
TO_DO_BATTLE_TEST("Foresight doesn't cause moves used against the target to always hit (Gen 2-3)")
TO_DO_BATTLE_TEST("Foresight causes moves used against the target to always hit (Gen 4+)")
TO_DO_BATTLE_TEST("Foresight does not make moves hit semi-invulnerable targets")
TO_DO_BATTLE_TEST("Foresight fails if the target is already under its effect (Gen 2 and Gen5+)")
TO_DO_BATTLE_TEST("Foresight doesn't fail if the target is already under its effect (Gen 3-4)")
TO_DO_BATTLE_TEST("Baton Pass passes Foresight's effect (Gen 2)");
TO_DO_BATTLE_TEST("Baton Pass doesn't pass Foresight's effect (Gen 3+)");

View File

@ -1,17 +1,47 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Gear Up increases the Attack and Sp. Attack of the user and allies if they have Plus or Minus")
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_GEAR_UP) == EFFECT_GEAR_UP);
}
AI_DOUBLE_BATTLE_TEST("AI uses Gear Up")
SINGLE_BATTLE_TEST("Gear Up raises Attack and Sp. Attack of the user with Plus/Minus in singles")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); }
OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); }
PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); }
OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); }
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_GEAR_UP); }
TURN { MOVE(player, MOVE_GEAR_UP); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GEAR_UP, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
}
}
DOUBLE_BATTLE_TEST("Gear Up raises Attack and Sp. Attack of all Plus/Minus allies in doubles")
{
GIVEN {
PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); }
PLAYER(SPECIES_MINUN) { Ability(ABILITY_MINUS); }
OPPONENT(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); }
OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_GEAR_UP); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GEAR_UP, playerLeft);
} THEN {
EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
}
}

View File

@ -1,4 +0,0 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Hyperspace Fury (Move Effect) test titles")

View File

@ -12,10 +12,10 @@ SINGLE_BATTLE_TEST("Ivy Cudgel changes the move type depending on the form of Og
u16 ogerpon;
enum Item item;
PARAMETRIZE { species = SPECIES_BLASTOISE; ogerpon = SPECIES_OGERPON_TEAL; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_BLASTOISE; ogerpon = SPECIES_OGERPON_TEAL; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_CHARIZARD; ogerpon = SPECIES_OGERPON_CORNERSTONE; item = ITEM_CORNERSTONE_MASK; }
PARAMETRIZE { species = SPECIES_CHARIZARD; ogerpon = SPECIES_OGERPON_WELLSPRING; item = ITEM_WELLSPRING_MASK; }
PARAMETRIZE { species = SPECIES_VENUSAUR; ogerpon = SPECIES_OGERPON_HEARTHFLAME; item = ITEM_HEARTHFLAME_MASK; }
PARAMETRIZE { species = SPECIES_CHARIZARD; ogerpon = SPECIES_OGERPON_WELLSPRING; item = ITEM_WELLSPRING_MASK; }
PARAMETRIZE { species = SPECIES_VENUSAUR; ogerpon = SPECIES_OGERPON_HEARTHFLAME; item = ITEM_HEARTHFLAME_MASK; }
GIVEN {
PLAYER(ogerpon) { Item(item); }

View File

@ -1,17 +1,47 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Magnetic Flux (Move Effect) test titles")
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_MAGNETIC_FLUX) == EFFECT_MAGNETIC_FLUX);
}
AI_DOUBLE_BATTLE_TEST("AI uses Magnetic Flux")
SINGLE_BATTLE_TEST("Magnetic Flux raises Defense and Sp. Defense of the user with Plus/Minus in singles")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); }
OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); }
OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); }
PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); }
OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); }
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, MOVE_MAGNETIC_FLUX); }
TURN { MOVE(player, MOVE_MAGNETIC_FLUX); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGNETIC_FLUX, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE);
}
}
DOUBLE_BATTLE_TEST("Magnetic Flux raises Defense and Sp. Defense of all Plus/Minus allies in doubles")
{
GIVEN {
PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); }
PLAYER(SPECIES_MINUN) { Ability(ABILITY_MINUS); }
OPPONENT(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); }
OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_MAGNETIC_FLUX); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGNETIC_FLUX, playerLeft);
} THEN {
EXPECT_EQ(playerLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerLeft->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerRight->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(playerRight->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(opponentLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentLeft->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentRight->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentRight->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE);
}
}

View File

@ -1,4 +1,106 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Miracle Eye (Move Effect) test titles")
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_MIRACLE_EYE) == EFFECT_MIRACLE_EYE);
}
SINGLE_BATTLE_TEST("Miracle Eye removes Dark-type immunity to Psychic-type moves")
{
GIVEN {
ASSUME(GetMoveType(MOVE_PSYCHIC) == TYPE_PSYCHIC);
ASSUME(GetSpeciesType(SPECIES_UMBREON, 0) == TYPE_DARK);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_PSYCHIC); }
OPPONENT(SPECIES_UMBREON) { Moves(MOVE_SPLASH); }
} WHEN {
TURN { MOVE(player, MOVE_PSYCHIC); MOVE(opponent, MOVE_SPLASH); }
TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_SPLASH); }
TURN { MOVE(player, MOVE_PSYCHIC); MOVE(opponent, MOVE_SPLASH); }
} SCENE {
NOT HP_BAR(opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Miracle Eye always hits unless the target is semi-invulnerable")
{
bool32 semiInvulnerable = FALSE;
PARAMETRIZE { semiInvulnerable = FALSE; }
PARAMETRIZE { semiInvulnerable = TRUE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP);
ASSUME(GetMoveEffect(MOVE_FLY) == EFFECT_SEMI_INVULNERABLE);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_SPLASH); Speed(10); }
OPPONENT(SPECIES_SQUAWKABILLY) { Moves(MOVE_DOUBLE_TEAM, MOVE_FLY); Speed(20); }
} WHEN {
if (semiInvulnerable)
TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_FLY); }
else
TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_DOUBLE_TEAM); }
if (semiInvulnerable)
TURN { MOVE(player, MOVE_SPLASH); SKIP_TURN(opponent); }
} SCENE {
if (semiInvulnerable) {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
} else {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
}
}
}
SINGLE_BATTLE_TEST("Miracle Eye causes moves against the target to ignore positive evasion stat stages")
{
PASSES_RANDOMLY(100, 100, RNG_ACCURACY);
GIVEN {
ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_SCRATCH); Speed(10); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DOUBLE_TEAM, MOVE_SPLASH); Speed(20); }
} WHEN {
TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_DOUBLE_TEAM); }
TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Miracle Eye fails if the target is already affected by Miracle Eye (Gen5+)")
{
GIVEN {
WITH_CONFIG(CONFIG_MIRACLE_EYE_FAIL, GEN_5);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_MIRACLE_EYE); }
TURN { MOVE(player, MOVE_MIRACLE_EYE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Miracle Eye does not fail if the target is already affected by Miracle Eye (Gen4)")
{
GIVEN {
WITH_CONFIG(CONFIG_MIRACLE_EYE_FAIL, GEN_4);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_MIRACLE_EYE); }
TURN { MOVE(player, MOVE_MIRACLE_EYE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player);
NOT MESSAGE("But it failed!");
}
}

View File

@ -1,4 +1,385 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Parting Shot (Move Effect) test titles")
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_PARTING_SHOT) == EFFECT_PARTING_SHOT);
}
SINGLE_BATTLE_TEST("Parting Shot: Passes Substitute and switches the user out")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SUBSTITUTE, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SUBSTITUTE); }
TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
SEND_IN_MESSAGE("Wynaut");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Soundproof and Good as Gold block Parting Shot")
{
u16 species, ability;
PARAMETRIZE { species = SPECIES_EXPLOUD; ability = ABILITY_SOUNDPROOF; }
PARAMETRIZE { species = SPECIES_GHOLDENGO; ability = ABILITY_GOOD_AS_GOLD; }
GIVEN {
ASSUME(IsSoundMove(MOVE_PARTING_SHOT));
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(species) { Ability(ability); Moves(MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
ABILITY_POPUP(opponent, ability);
if (ability == ABILITY_SOUNDPROOF)
MESSAGE("The opposing Exploud's Soundproof blocks Parting Shot!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WOBBUFFET);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Hyper Cutter blocks Attack drop but still switches")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_KRABBY) { Ability(ABILITY_HYPER_CUTTER); }
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
SEND_IN_MESSAGE("Wynaut");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Magic Coat bounces it and switches the target out")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_MAGIC_COAT); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_MAGIC_COAT); MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); }
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(opponent->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Magic Bounce bounces it and switches the target out")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
OPPONENT(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_MAGIC_BOUNCE);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1);
EXPECT_EQ(opponent->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Mirror Armor switches the user even if reflected drops fail")
{
u16 species, ability, item;
PARAMETRIZE { species = SPECIES_METAGROSS; ability = ABILITY_CLEAR_BODY; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_LUCARIO; ability = ABILITY_INNER_FOCUS; item = ITEM_CLEAR_AMULET; }
GIVEN {
ASSUME(gItemsInfo[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET);
PLAYER(species) { Ability(ability); Item(item); Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_MIRROR_ARMOR);
if (ability == ABILITY_CLEAR_BODY) {
ABILITY_POPUP(player, ABILITY_CLEAR_BODY);
MESSAGE("Metagross's Clear Body prevents stat loss!");
} else if (ability == ABILITY_WHITE_SMOKE) {
ABILITY_POPUP(player, ABILITY_WHITE_SMOKE);
MESSAGE("Torkoal's White Smoke prevents stat loss!");
} else if (ability == ABILITY_FULL_METAL_BODY) {
ABILITY_POPUP(player, ABILITY_FULL_METAL_BODY);
MESSAGE("Solgaleo's Full Metal Body prevents stat loss!");
} else if (item == ITEM_CLEAR_AMULET) {
MESSAGE("The effects of the Clear Amulet held by Lucario prevents its stats from being lowered!");
}
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Mirror Armor switches even if reflected stats are at minimum")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_SHELL_SMASH, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); Moves(MOVE_TOPSY_TURVY, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_SHELL_SMASH); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_SHELL_SMASH); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_SHELL_SMASH); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TOPSY_TURVY); }
TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_MIRROR_ARMOR);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Does not switch if both stats are at minimum (Gen7+)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_OMASTAR) { Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("The opposing Omastar's stats won't go any lower!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], MIN_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], MIN_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WOBBUFFET);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Does not switch if Contrary is at maximum stats (Gen7+)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_INKAY) { Ability(ABILITY_CONTRARY); Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("The opposing Inkay's stats won't go any higher!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], MAX_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], MAX_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WOBBUFFET);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Stat drop prevention by abilities/items does not switch (Gen7+)")
{
u16 species, ability, item;
PARAMETRIZE { species = SPECIES_METAGROSS; ability = ABILITY_CLEAR_BODY; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_LUCARIO; ability = ABILITY_INNER_FOCUS; item = ITEM_CLEAR_AMULET; }
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7);
ASSUME(gItemsInfo[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(species) { Ability(ability); Item(item); }
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WOBBUFFET);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Mist prevents stat drops and does not switch (Gen7+)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_MIST, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(opponent, MOVE_MIST); MOVE(player, MOVE_CELEBRATE); }
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_PARTING_SHOT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WOBBUFFET);
}
}
DOUBLE_BATTLE_TEST("Parting Shot: Flower Veil prevents stat drops and does not switch (Gen7+)")
{
GIVEN {
ASSUME(GetSpeciesType(SPECIES_BULBASAUR, 0) == TYPE_GRASS);
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_BULBASAUR);
OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_FLOWER_VEIL); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_PARTING_SHOT, target: opponentLeft); MOVE(playerRight, MOVE_CELEBRATE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, playerLeft);
} THEN {
EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(playerLeft->species, SPECIES_WOBBUFFET);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Switches if both stats are at minimum (Gen6)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_OMASTAR) { Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("The opposing Omastar's stats won't go any lower!");
SEND_IN_MESSAGE("Wynaut");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], MIN_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], MIN_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Switches if Contrary is at maximum stats (Gen6)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_INKAY) { Ability(ABILITY_CONTRARY); Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); }
TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("The opposing Inkay's stats won't go any higher!");
SEND_IN_MESSAGE("Wynaut");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], MAX_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], MAX_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Stat drop prevention by abilities/items switches (Gen6)")
{
u16 species, ability, item;
PARAMETRIZE { species = SPECIES_METAGROSS; ability = ABILITY_CLEAR_BODY; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_LUCARIO; ability = ABILITY_INNER_FOCUS; item = ITEM_CLEAR_AMULET; }
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6);
ASSUME(gItemsInfo[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(species) { Ability(ability); Item(item); }
} WHEN {
TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
SEND_IN_MESSAGE("Wynaut");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
SINGLE_BATTLE_TEST("Parting Shot: Mist prevents stat drops and switches (Gen6)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_CELEBRATE); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_MIST, MOVE_CELEBRATE); }
} WHEN {
TURN { MOVE(opponent, MOVE_MIST); MOVE(player, MOVE_CELEBRATE); }
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player);
SEND_IN_MESSAGE("Wynaut");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->species, SPECIES_WYNAUT);
}
}
DOUBLE_BATTLE_TEST("Parting Shot: Flower Veil prevents stat drops and switches (Gen6)")
{
GIVEN {
WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6);
ASSUME(GetSpeciesType(SPECIES_BULBASAUR, 0) == TYPE_GRASS);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); }
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_PIKACHU);
OPPONENT(SPECIES_BULBASAUR);
OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_FLOWER_VEIL); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_PARTING_SHOT, target: opponentLeft); MOVE(playerRight, MOVE_CELEBRATE); SEND_OUT(playerLeft, 2); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, playerLeft);
SEND_IN_MESSAGE("Pikachu");
} THEN {
EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(playerLeft->species, SPECIES_PIKACHU);
}
}

View File

@ -125,6 +125,36 @@ SINGLE_BATTLE_TEST("Protect: King's Shield, Silk Trap and Obstruct protect from
}
}
SINGLE_BATTLE_TEST("Protect: King's Shield, Silk Trap and Obstruct don't lower stats when charging a two turn move")
{
u32 move, protectMove;
PARAMETRIZE { move = MOVE_BOUNCE; protectMove = MOVE_KINGS_SHIELD; }
PARAMETRIZE { move = MOVE_DIG; protectMove = MOVE_KINGS_SHIELD; }
PARAMETRIZE { move = MOVE_BOUNCE; protectMove = MOVE_SILK_TRAP; }
PARAMETRIZE { move = MOVE_DIG; protectMove = MOVE_SILK_TRAP; }
PARAMETRIZE { move = MOVE_BOUNCE; protectMove = MOVE_OBSTRUCT; }
PARAMETRIZE { move = MOVE_DIG; protectMove = MOVE_OBSTRUCT; }
GIVEN {
ASSUME(MoveMakesContact(MOVE_BOUNCE));
ASSUME(MoveMakesContact(MOVE_DIG));
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect);
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, protectMove); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, protectMove, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
HP_BAR(player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
}
}
}
SINGLE_BATTLE_TEST("Protect: Spiky Shield does 1/8 dmg of max hp of attackers making contact and may faint them")
{
enum Move usedMove = MOVE_NONE;
@ -162,6 +192,32 @@ SINGLE_BATTLE_TEST("Protect: Spiky Shield does 1/8 dmg of max hp of attackers ma
}
}
SINGLE_BATTLE_TEST("Protect: Spiky Shield doesn't hurt attacker when charging a two turn move")
{
u32 move;
PARAMETRIZE { move = MOVE_BOUNCE; }
PARAMETRIZE { move = MOVE_DIG; }
GIVEN {
ASSUME(MoveMakesContact(MOVE_BOUNCE));
ASSUME(MoveMakesContact(MOVE_DIG));
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect);
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SPIKY_SHIELD); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKY_SHIELD, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
HP_BAR(player);
HP_BAR(opponent);
}
}
}
SINGLE_BATTLE_TEST("Protect: Baneful Bunker poisons Pokémon for moves making contact")
{
enum Move usedMove = MOVE_NONE;
@ -214,6 +270,32 @@ SINGLE_BATTLE_TEST("Protect: Baneful Bunker can't poison Pokémon if they are al
}
}
SINGLE_BATTLE_TEST("Protect: Baneful Bunker doesn't poison attacker when charging a two turn move")
{
u32 move;
PARAMETRIZE { move = MOVE_BOUNCE; }
PARAMETRIZE { move = MOVE_DIG; }
GIVEN {
ASSUME(MoveMakesContact(MOVE_BOUNCE));
ASSUME(MoveMakesContact(MOVE_DIG));
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect);
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BANEFUL_BUNKER); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BANEFUL_BUNKER, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
HP_BAR(player);
STATUS_ICON(opponent, STATUS1_POISON);
}
}
}
SINGLE_BATTLE_TEST("Protect: Burning Bulwark burns Pokémon for moves making contact")
{
enum Move usedMove = MOVE_NONE;
@ -266,6 +348,32 @@ SINGLE_BATTLE_TEST("Protect: Burning Bulwark can't burn Pokémon if they are alr
}
}
SINGLE_BATTLE_TEST("Protect: Burning Bulwark doesn't burn attacker when charging a two turn move")
{
u32 move;
PARAMETRIZE { move = MOVE_BOUNCE; }
PARAMETRIZE { move = MOVE_DIG; }
GIVEN {
ASSUME(MoveMakesContact(MOVE_BOUNCE));
ASSUME(MoveMakesContact(MOVE_DIG));
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect);
ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BURNING_BULWARK); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BURNING_BULWARK, player);
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
HP_BAR(player);
STATUS_ICON(opponent, STATUS1_BURN);
}
}
}
SINGLE_BATTLE_TEST("Protect: Recoil damage is not applied if target was protected")
{
u32 j, k;
@ -530,7 +638,7 @@ DOUBLE_BATTLE_TEST("Protect: Quick Guard can not fail on consecutive turns (Gen6
}
}
DOUBLE_BATTLE_TEST("Protect: Crafty Shield protects self and ally from status moves")
DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from opposing status moves")
{
enum Move move = MOVE_NONE;
struct BattlePokemon *targetOpponent = NULL;
@ -571,6 +679,72 @@ DOUBLE_BATTLE_TEST("Protect: Crafty Shield protects self and ally from status mo
}
}
DOUBLE_BATTLE_TEST("Crafty Shield does not protect against status moves used on the user's side")
{
u32 move;
PARAMETRIZE { move = MOVE_AROMATHERAPY; }
PARAMETRIZE { move = MOVE_ACUPRESSURE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_AROMATHERAPY) == EFFECT_HEAL_BELL);
ASSUME(GetMoveEffect(MOVE_ACUPRESSURE) == EFFECT_ACUPRESSURE);
PLAYER(SPECIES_WOBBUFFET) { Speed(5); }
PLAYER(SPECIES_WOBBUFFET) { Speed(5); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(10); }
OPPONENT(SPECIES_WYNAUT) { Speed(5); Status1(STATUS1_BURN); }
} WHEN {
TURN {
MOVE(opponentLeft, MOVE_CRAFTY_SHIELD);
if (move == MOVE_ACUPRESSURE)
MOVE(opponentRight, move, target: opponentLeft);
else
MOVE(opponentRight, move);
}
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CRAFTY_SHIELD, opponentLeft);
if (move == MOVE_ACUPRESSURE) {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ACUPRESSURE, opponentRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
} else {
ANIMATION(ANIM_TYPE_MOVE, MOVE_AROMATHERAPY, opponentRight);
STATUS_ICON(opponentRight, none: TRUE);
}
}
}
DOUBLE_BATTLE_TEST("Crafty Shield does not protect against entry hazard moves")
{
u32 move;
PARAMETRIZE { move = MOVE_SPIKES; }
PARAMETRIZE { move = MOVE_STEALTH_ROCK; }
PARAMETRIZE { move = MOVE_TOXIC_SPIKES; }
PARAMETRIZE { move = MOVE_STICKY_WEB; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(playerLeft, move, target: opponentLeft); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CRAFTY_SHIELD, opponentLeft);
if (move == MOVE_SPIKES) {
MESSAGE("Spikes were scattered on the ground all around the opposing team!");
} else if (move == MOVE_TOXIC_SPIKES) {
MESSAGE("Poison spikes were scattered on the ground all around the opposing team!");
} else if (move == MOVE_STEALTH_ROCK) {
MESSAGE("Pointed stones float in the air around the opposing team!");
} else {
MESSAGE("A sticky web has been laid out on the ground around the opposing team!");
}
}
}
SINGLE_BATTLE_TEST("Protect: Protect does not block Confide or Decorate")
{
enum Move move;
@ -620,6 +794,11 @@ DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from Confide and Decora
DOUBLE_BATTLE_TEST("Crafty Shield does not protect against moves that target all battlers")
{
u32 move;
PARAMETRIZE { move = MOVE_FLOWER_SHIELD; }
PARAMETRIZE { move = MOVE_PERISH_SONG; }
GIVEN {
ASSUME(GetSpeciesType(SPECIES_TANGELA, 0) == TYPE_GRASS);
ASSUME(GetSpeciesType(SPECIES_TANGROWTH, 0) == TYPE_GRASS);
@ -630,18 +809,26 @@ DOUBLE_BATTLE_TEST("Crafty Shield does not protect against moves that target all
OPPONENT(SPECIES_SUNKERN);
OPPONENT(SPECIES_SUNFLORA);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(opponentRight, MOVE_CELEBRATE); MOVE(playerLeft, MOVE_FLOWER_SHIELD); MOVE(playerRight, MOVE_CELEBRATE); }
TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(opponentRight, MOVE_CELEBRATE); MOVE(playerLeft, move); MOVE(playerRight, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Tangela used Flower Shield!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
MESSAGE("Tangela's Defense rose!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("The opposing Sunkern's Defense rose!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
MESSAGE("Tangrowth's Defense rose!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
MESSAGE("The opposing Sunflora's Defense rose!");
if (move == MOVE_FLOWER_SHIELD) {
MESSAGE("Tangela used Flower Shield!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
MESSAGE("Tangela's Defense rose!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
MESSAGE("The opposing Sunkern's Defense rose!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
MESSAGE("Tangrowth's Defense rose!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
MESSAGE("The opposing Sunflora's Defense rose!");
} else {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PERISH_SONG, playerLeft);
NONE_OF {
MESSAGE("The opposing Sunkern protected itself!");
MESSAGE("The opposing Sunflora protected itself!");
}
}
}
}

View File

@ -184,6 +184,18 @@ SINGLE_BATTLE_TEST("Trick fails if the target is behind a Substitute")
}
}
SINGLE_BATTLE_TEST("Trick can be used against targets with an active form change that doesn't require items")
{
GIVEN {
PLAYER(SPECIES_XERNEAS);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_ORAN_BERRY); }
} WHEN {
TURN { MOVE(opponent, MOVE_TRICK); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRICK, opponent);
}
}
SINGLE_BATTLE_TEST("Trick does not remove the user's choice lock if both the target and use are holding choice items before Gen 5")
{
GIVEN {

View File

@ -0,0 +1,97 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_HYPERSPACE_FURY) == EFFECT_HYPERSPACE_FURY);
ASSUME(MoveHasAdditionalEffect(MOVE_HYPERSPACE_FURY, MOVE_EFFECT_FEINT));
ASSUME(MoveHasAdditionalEffectSelf(MOVE_HYPERSPACE_FURY, MOVE_EFFECT_DEF_MINUS_1));
ASSUME(GetMoveEffect(MOVE_PROTECT) == EFFECT_PROTECT);
}
SINGLE_BATTLE_TEST("Hyperspace Fury fails if used by a Pokémon other than Hoopa Unbound")
{
u32 species;
PARAMETRIZE { species = SPECIES_WOBBUFFET; }
PARAMETRIZE { species = SPECIES_HOOPA_CONFINED; }
PARAMETRIZE { species = SPECIES_HOOPA_UNBOUND; }
GIVEN {
PLAYER(species);
OPPONENT(SPECIES_REGIROCK);
} WHEN {
TURN { MOVE(player, MOVE_HYPERSPACE_FURY); }
} SCENE {
switch (species)
{
case SPECIES_HOOPA_UNBOUND:
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player);
break;
case SPECIES_HOOPA_CONFINED:
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player);
MESSAGE("But Hoopa can't use it the way it is now!");
break;
case SPECIES_WOBBUFFET:
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player);
MESSAGE("But Wobbuffet can't use the move!");
break;
default:
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player);
break;
}
}
}
DOUBLE_BATTLE_TEST("Hyperspace Fury hits the target through Protect and breaks it")
{
GIVEN {
PLAYER(SPECIES_HOOPA_UNBOUND);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_REGIROCK);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_PROTECT); MOVE(playerLeft, MOVE_HYPERSPACE_FURY, target: opponentLeft); MOVE(playerRight, MOVE_SCRATCH, target: opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerRight);
HP_BAR(opponentLeft);
}
}
SINGLE_BATTLE_TEST("Hyperspace Fury lowers the user's Defense by 1 stage after hitting the target")
{
GIVEN {
PLAYER(SPECIES_HOOPA_UNBOUND);
OPPONENT(SPECIES_REGIROCK);
} WHEN {
TURN { MOVE(player, MOVE_HYPERSPACE_FURY); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1);
}
}
DOUBLE_BATTLE_TEST("Hyperspace Fury breaks protection and lowers the user's Defense by 1 stage")
{
GIVEN {
PLAYER(SPECIES_HOOPA_UNBOUND);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_REGIROCK);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_PROTECT); MOVE(playerLeft, MOVE_HYPERSPACE_FURY, target: opponentLeft); MOVE(playerRight, MOVE_SCRATCH, target: opponentLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerRight);
HP_BAR(opponentLeft);
} THEN {
EXPECT_EQ(playerLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1);
}
}

BIN
tools/aif2pcm/aif2pcm Executable file

Binary file not shown.