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

This commit is contained in:
RoamerX 2025-06-23 20:29:34 +08:00
commit c20c5d851b
76 changed files with 1119 additions and 615 deletions

View File

@ -118,7 +118,8 @@
"profile": "https://linktr.ee/pkmnsnfrn",
"contributions": [
"maintenance",
"code"
"code",
"projectManagement"
]
},
{
@ -223,7 +224,7 @@
"name": "Ruby",
"avatar_url": "https://avatars.githubusercontent.com/u/178652077?v=4",
"profile": "https://github.com/RubyRaven6",
"contributions": [
"contributions": [
"code",
"doc"
]
@ -255,10 +256,82 @@
"contributions": [
"code"
]
},
{
"login": "bassforte123",
"name": "bassforte123",
"avatar_url": "https://avatars.githubusercontent.com/u/130828119?v=4",
"profile": "https://github.com/bassforte123",
"contributions": [
"code"
]
},
{
"login": "iriv24",
"name": "iriv24",
"avatar_url": "https://avatars.githubusercontent.com/u/40581123?v=4",
"profile": "https://github.com/iriv24",
"contributions": [
"code"
]
},
{
"login": "Bivurnum",
"name": "Bivurnum",
"avatar_url": "https://avatars.githubusercontent.com/u/147376167?v=4",
"profile": "https://github.com/Bivurnum",
"contributions": [
"code"
]
},
{
"login": "Emiliasky",
"name": "Emilia Daelman",
"avatar_url": "https://avatars.githubusercontent.com/u/48217459?v=4",
"profile": "https://github.com/Emiliasky",
"contributions": [
"code",
"test"
]
},
{
"login": "ravepossum",
"name": "RavePossum",
"avatar_url": "https://avatars.githubusercontent.com/u/145081120?v=4",
"profile": "https://github.com/ravepossum",
"contributions": [
"code"
]
},
{
"login": "fakuzatsu",
"name": "Zatsu",
"avatar_url": "https://avatars.githubusercontent.com/u/118256341?v=4",
"profile": "https://github.com/fakuzatsu",
"contributions": [
"code"
]
},
{
"login": "poetahto",
"name": "poetahto",
"avatar_url": "https://avatars.githubusercontent.com/u/11669335?v=4",
"profile": "https://github.com/poetahto",
"contributions": [
"code"
]
},
{
"login": "lordraindance2",
"name": "lordraindance2",
"avatar_url": "https://avatars.githubusercontent.com/u/47706100?v=4",
"profile": "https://github.com/lordraindance2",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
"linkToUsage": true,
"commitType": "docs",
"skipCi": true
"commitType": "docs"
}

View File

@ -9,6 +9,7 @@ on:
jobs:
build:
if: github.actor != 'allcontributors[bot]'
runs-on: ubuntu-latest
env:
GAME_VERSION: EMERALD
@ -36,3 +37,11 @@ jobs:
TEST: 1
run: |
make -j${nproc} check
allcontributors:
if: github.actor == 'allcontributors[bot]'
runs-on: ubuntu-latest
needs: []
steps:
- name: Automatically pass for allcontributors
run: echo "CI automatically passes for allcontributors" && exit 0

View File

@ -6,6 +6,7 @@ on:
jobs:
label:
if: github.actor != 'allcontributors[bot]'
runs-on: ubuntu-latest
steps:
- name: check labels
@ -27,3 +28,11 @@ jobs:
category: pokemon
category: sprite-issue
type: documentation
allcontributors:
if: github.actor == 'allcontributors[bot]'
runs-on: ubuntu-latest
needs: []
steps:
- name: Automatically pass for allcontributors
run: echo "CI automatically passes for allcontributors" && exit 0

View File

@ -30,24 +30,34 @@ 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/Pawkkie"><img src="https://avatars.githubusercontent.com/u/61265402?v=4?s=100" width="100px;" alt="Pawkkie"/><br /><sub><b>Pawkkie</b></sub></a><br /><a href="#maintenance-Pawkkie" title="Maintenance">🚧</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=Pawkkie" title="Code">💻</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=Pawkkie" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SBird1337"><img src="https://avatars.githubusercontent.com/u/3799173?v=4?s=100" width="100px;" alt="Philipp AUER"/><br /><sub><b>Philipp AUER</b></sub></a><br /><a href="#maintenance-SBird1337" title="Maintenance">🚧</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=SBird1337" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tertu-m"><img src="https://avatars.githubusercontent.com/u/836640?v=4?s=100" width="100px;" alt="tertu"/><br /><sub><b>tertu</b></sub></a><br /><a href="#maintenance-tertu-m" title="Maintenance">🚧</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=tertu-m" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://linktr.ee/pkmnsnfrn"><img src="https://avatars.githubusercontent.com/u/77138753?v=4?s=100" width="100px;" alt="psf"/><br /><sub><b>psf</b></sub></a><br /><a href="#maintenance-pkmnsnfrn" title="Maintenance">🚧</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=pkmnsnfrn" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://linktr.ee/pkmnsnfrn"><img src="https://avatars.githubusercontent.com/u/77138753?v=4?s=100" width="100px;" alt="psf"/><br /><sub><b>psf</b></sub></a><br /><a href="#maintenance-pkmnsnfrn" title="Maintenance">🚧</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=pkmnsnfrn" title="Code">💻</a> <a href="#projectManagement-pkmnsnfrn" title="Project Management">📆</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wiz1989"><img src="https://avatars.githubusercontent.com/u/80073265?v=4?s=100" width="100px;" alt="wiz1989"/><br /><sub><b>wiz1989</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=wiz1989" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/PCG06"><img src="https://avatars.githubusercontent.com/u/75729017?v=4?s=100" width="100px;" alt="PCG"/><br /><sub><b>PCG</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=PCG06" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kittenchilly"><img src="https://avatars.githubusercontent.com/u/23617175?v=4?s=100" width="100px;" alt="kittenchilly"/><br /><sub><b>kittenchilly</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=kittenchilly" title="Code">💻</a> <a href="#research-kittenchilly" title="Research">🔬</a> <a href="#data-kittenchilly" title="Data">🔣</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ExpoSeed"><img src="https://avatars.githubusercontent.com/u/43502820?v=4?s=100" width="100px;" alt="ExpoSeed"/><br /><sub><b>ExpoSeed</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=ExpoSeed" title="Code">💻</a> <a href="#maintenance-ExpoSeed" title="Maintenance">🚧</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/pulls?q=is%3Apr+reviewed-by%3AExpoSeed" title="Reviewed Pull Requests">👀</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ExpoSeed"><img src="https://avatars.githubusercontent.com/u/43502820?v=4?s=100" width="100px;" alt="ExpoSeed"/><br /><sub><b>ExpoSeed</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=ExpoSeed" title="Code">💻</a> <a href="#maintenance-ExpoSeed" title="Maintenance">🚧</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/pulls?q=is%3Apr+reviewed-by%3AExpoSeed" title="Reviewed Pull Requests">👀</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/LinathanZel"><img src="https://avatars.githubusercontent.com/u/35115312?v=4?s=100" width="100px;" alt="Linathan"/><br /><sub><b>Linathan</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=LinathanZel" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/AsparagusEduardo"><img src="https://avatars.githubusercontent.com/u/2904965?v=4?s=100" width="100px;" alt="Eduardo Quezada"/><br /><sub><b>Eduardo Quezada</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=AsparagusEduardo" title="Code">💻</a> <a href="#data-AsparagusEduardo" title="Data">🔣</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=AsparagusEduardo" title="Documentation">📖</a> <a href="#infra-AsparagusEduardo" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#maintenance-AsparagusEduardo" title="Maintenance">🚧</a> <a href="#projectManagement-AsparagusEduardo" title="Project Management">📆</a> <a href="#promotion-AsparagusEduardo" title="Promotion">📣</a> <a href="#research-AsparagusEduardo" title="Research">🔬</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/pulls?q=is%3Apr+reviewed-by%3AAsparagusEduardo" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=AsparagusEduardo" title="Tests">⚠️</a> <a href="#tutorial-AsparagusEduardo" title="Tutorials"></a> <a href="#userTesting-AsparagusEduardo" title="User Testing">📓</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/khbsd"><img src="https://avatars.githubusercontent.com/u/26092020?v=4?s=100" width="100px;" alt="khbsd"/><br /><sub><b>khbsd</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=khbsd" title="Documentation">📖</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=khbsd" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Cafeei"><img src="https://avatars.githubusercontent.com/u/46283144?v=4?s=100" width="100px;" alt="Cafe"/><br /><sub><b>Cafe</b></sub></a><br /><a href="#design-Cafeei" title="Design">🎨</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/agsmgmaster64"><img src="https://avatars.githubusercontent.com/u/67435611?v=4?s=100" width="100px;" alt="agsmgmaster64"/><br /><sub><b>agsmgmaster64</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=agsmgmaster64" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mudskipper13"><img src="https://avatars.githubusercontent.com/u/105766191?v=4?s=100" width="100px;" alt="mudskipper13"/><br /><sub><b>mudskipper13</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=mudskipper13" title="Code">💻</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=mudskipper13" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RubyRaven6"><img src="https://avatars.githubusercontent.com/u/178652077?v=4?s=100" width="100px;" alt="Ruby"/><br /><sub><b>Ruby</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=RubyRaven6" title="Code">💻</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=RubyRaven6" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RubyRaven6"><img src="https://avatars.githubusercontent.com/u/178652077?v=4?s=100" width="100px;" alt="Ruby"/><br /><sub><b>Ruby</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=RubyRaven6" title="Code">💻</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=RubyRaven6" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/surskitty"><img src="https://avatars.githubusercontent.com/u/1383512?v=4?s=100" width="100px;" alt="surskitty"/><br /><sub><b>surskitty</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=surskitty" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wiz1989"><img src="https://avatars.githubusercontent.com/u/80073265?v=4?s=100" width="100px;" alt="wiz1989"/><br /><sub><b>wiz1989</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=wiz1989" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mudskipper13"><img src="https://avatars.githubusercontent.com/u/105766191?v=4?s=100" width="100px;" alt="mudskipper13"/><br /><sub><b>mudskipper13</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=mudskipper13" title="Code">💻</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=mudskipper13" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/surskitty"><img src="https://avatars.githubusercontent.com/u/1383512?v=4?s=100" width="100px;" alt="surskitty"/><br /><sub><b>surskitty</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=surskitty" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/grintoul1"><img src="https://avatars.githubusercontent.com/u/166724814?v=4?s=100" width="100px;" alt="grintoul"/><br /><sub><b>grintoul</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=grintoul1" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bassforte123"><img src="https://avatars.githubusercontent.com/u/130828119?v=4?s=100" width="100px;" alt="bassforte123"/><br /><sub><b>bassforte123</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=bassforte123" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/iriv24"><img src="https://avatars.githubusercontent.com/u/40581123?v=4?s=100" width="100px;" alt="iriv24"/><br /><sub><b>iriv24</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=iriv24" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Bivurnum"><img src="https://avatars.githubusercontent.com/u/147376167?v=4?s=100" width="100px;" alt="Bivurnum"/><br /><sub><b>Bivurnum</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=Bivurnum" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Emiliasky"><img src="https://avatars.githubusercontent.com/u/48217459?v=4?s=100" width="100px;" alt="Emilia Daelman"/><br /><sub><b>Emilia Daelman</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=Emiliasky" title="Code">💻</a> <a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=Emiliasky" title="Tests">⚠️</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ravepossum"><img src="https://avatars.githubusercontent.com/u/145081120?v=4?s=100" width="100px;" alt="RavePossum"/><br /><sub><b>RavePossum</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=ravepossum" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fakuzatsu"><img src="https://avatars.githubusercontent.com/u/118256341?v=4?s=100" width="100px;" alt="Zatsu"/><br /><sub><b>Zatsu</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=fakuzatsu" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/poetahto"><img src="https://avatars.githubusercontent.com/u/11669335?v=4?s=100" width="100px;" alt="poetahto"/><br /><sub><b>poetahto</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=poetahto" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lordraindance2"><img src="https://avatars.githubusercontent.com/u/47706100?v=4?s=100" width="100px;" alt="lordraindance2"/><br /><sub><b>lordraindance2</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=lordraindance2" title="Code">💻</a></td>
</tr>
</tbody>
<tfoot>
@ -65,6 +75,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
## Other Credits
### Mega Evolution Overworld Sprite Credits:
@ -80,19 +91,3 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
- [Data Files](https://www.pokecommunity.com/showthread.php?t=417909)
- [Complete FireRed Upgrade](https://github.com/Skeli789/Complete-Fire-Red-Upgrade)
- [pokeemerald](https://github.com/pret/pokeemerald/)
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
## Contributors ✨
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

View File

@ -1,146 +1,94 @@
# What features are included?
- ***IMPORTANT*❗❗ Read through these to learn what features you can toggle**:
- [Battle configurations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/battle.h)
- [Pokémon configurations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/pokemon.h)
- [Item configurations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/item.h)
- [Overworld configurations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/overworld.h)
- [Debug configurations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/debug.h)
- ***Upgraded battle engine.***
- Gen5+ damage calculation.
- 2v2 Wild battles support.
- 1v2/2v1 battles support.
- Fairy Type (configurable).
- Physical/Special/Status Category (configurable).
- New moves and abilities up to Scarlet and Violet.
- Custom Contest data up to SwSh, newer moves are WIP. ([source](https://pokemonurpg.com/info/contests/rse-move-list/))
- Battle gimmick support:
- Mega Evolution
- Primal Reversion
- Ultra Burst
- Z-Moves
- Gen 8+ damaging moves are given power extrapolated from Gen 7.
- Gen 8+ status moves have no additional effects, like Healing Wish.
- Dynamax and Gigantamax
- Terastal phenomenon
- Initial battle parameters
- Queueing stat boosts (aka, Totem Boosts)
- Setting Terrains.
- Mid-turn speed recalculation.
- Quick Poké Ball selection in Wild Battles
- Hold `R` to change selection with the D-Pad.
- Press `R` to use last selected Poké Ball.
- Run option shortcut
- Faster battle intro - Message and animation/cry happens at the same time.
- Faster HP drain.
- Battle Debug menu.
- Accessed by pressing `Select` on the "Fight/Bag/Pokémon/Run" menu.
- Option to use AI flags in wild Pokémon battles.
- FRLG/Gen4+ whiteout money calculation.
- Configurable experience settings
- Experience on catch.
- Splitting experience.
- Trainer experience.
- Scaled experience.
- Unevolved experience boost.
- Frostbite.
- Doesn't replace freezing unless a config is enabled, so you can mix and match.
- Critical capture.
- Removed badge boosts (configurable).
- Recalculating stats at the end of every battle.
- Level 100 Pokémon can earn EVs.
- Inverse battle support.
- TONS of other features listed [here](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/battle.h).
- ***Full Trainer customization***
- Nickname, EVs, IVs, moves, ability, ball, friendship, nature, gender, shininess.
- Custom tag battle support (teaming up an NPC in a double battle).
- Sliding trainer messages.
- Upgraded Trainer AI
- Considers newer move effects.
- New flag options to let you customize the intelligence of your trainers.
- Faster calculations.
- Specify Poké Balls by Trainer class.
- ***Pokémon Species from Generations 1-9.***
- Simplified process to add new Pokémon.
- Option to disable unwanted families.
- Updated sprites to DS style.
- Updated stats, types, abilities and egg groups (configurable).
- Updated Hoenn's Regional Dex to match ORAS' (configurable).
- Updated National Dex incorporating the new species.
- Sprite and animation visualizer.
- Accesible by pressing `Select` on a Pokémon's Summary screen.
- Gen4+ evolution methods, with some changes:
- Mossy Rock, Icy Rock and Magnetic Field locations match ORAS'.
- Leaf, Ice and Thunder Stones may also be used.
- Inkay just needs level 30 to evolve.
- You can't physically have both the RTC and gyroscope, so we skip this requirement.
- Sylveon uses Gen8+'s evolution method (friendship + Fairy Move).
- Option to use hold evolution items directly like stones.
- Hidden Abilities.
- Available via Ability Patch.
- Compatible with Ghoul's DexNav branch.
- All gender differences.
- Custom female icons for female Hippopotas Hippowdon, Pikachu and Wobbufett
- 3 Perfect IVs on Legendaries, Mythicals and Ultra Beasts.
- ***Customizable form change tables. Full list of methods [here](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/form_change_types.h).***
- Item holding (eg. Giratina/Arceus)
- Item using (eg. Oricorio)
- Time of day option for Shaymin
- Fainting
- Battle begin and end (eg. Xerneas)
- Move change option for Zacian/Zamazenta
- Battle end in terrains (eg. Burmy)
- Switched in battle (eg. Palafin)
- HP Threshold (eg. Darmanitan)
- Weather (eg. Castform)
- End of turn (eg. Morpeko)
- Time of day (eg. Shaymin)
- Fusions (eg. Kyurem)
- ***Breeding Improvements***
- Incense Baby Pokémon now happen automatically (configurable).
- Level 1 eggs (configurable).
- Poké Ball inheriting (configurable).
- Egg Move Transfer, including Mirror Herb (configurable).
- Nature inheriting 100% of the time with Everstone (configurable)
- Gen6+ Ability inheriting (configurable).
- ***Items from newer Generations. Full list [here](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/items.h).***
- ***Gen 6+ Exp. Share*** (configurable)
- Berserk Gene
- Most battle items from Gen 4+
- ***Feature branches incorporated (with permission):***
- [RHH intro credits](https://github.com/Xhyzi/pokeemerald/tree/rhh-intro-credits) by @Xhyzi.
- A small signature from all of us to show the collective effort in the project :)
- [Overworld debug](https://github.com/TheXaman/pokeemerald/tree/tx_debug_system) by @TheXaman
- May be disabled.
- Accesible by pressing `R + Start` in the overworld by default.
- **Additional features**:
- *Clear Boxes*: cleans every Pokémon from the Boxes.
- *Hatch an Egg*: lets you choose an Egg in your party and immediately hatch it.
- [HGSS Pokédex](https://github.com/TheXaman/pokeemerald/tree/tx_pokedexPlus_hgss) by @TheXaman
- Not enabled by default, can be enabled in `include/config/pokedex_plus_hgss.h`
- **Additional features**:
- *Support for new evolution methods*.
- *Dark Mode*.
- [Nature Colors](https://github.com/DizzyEggg/pokeemerald/tree/nature_color) in summary screen by @DizzyEggg
- [Dynamic Multichoice](https://github.com/SBird1337/pokeemerald/tree/feature/dynmulti) by @SBird1337
- [Saveblock Cleansing](https://github.com/ghoulslash/pokeemerald/tree/saveblock) by @ghoulslash
- [Followers & Expanded IDs](https://github.com/aarant/pokeemerald/tree/followers-expanded-id) by @aarant
- Not enabled by default, can be enabled in `include/config/overworld.h`
- Includes Pokémon followers like in HGSS, including interactions.
- ***Expands the amount of possible object event IDs beyond 255.***
- ***Includes an implementation of dynamic overworld palettes (DOWP).***
- **Additional features**:
- *Pokémon overworld sprites up to Generation 9.*
- *Integration with our Pokémon Sprite Visualizer, allowing users to browse through the follower sprites alongside battle sprites.*
- ***Other features***
- Pressing B while holding a Pokémon drops them like in modern games (configurable).
- Running indoors (configurable).
- Configurable overworld poison damage.
- Configurable flags for disabling Wild encounters and Trainer battles.
- Configurable flags for forcing or disabling Shinies.
- Reusable TM (configurable).
- B2W2+ Repel system that also supports LGPE's Lures
- Gen6+'s EV cap.
- All bugfixes from pret included.
- Fixed overworld snow effect.
## Table of Contents
- [What features are included?](#what-features-are-included)
- [Table of Contents](#table-of-contents)
- [Configuration files](#configuration-files)
- [Upgraded Battle Engine](#upgraded-battle-engine)
- [Full Trainer customization](#full-trainer-customization)
- [Pokémon data](#pokémon-data)
- [Interface improvements](#interface-improvements)
- [Engine improvements](#engine-improvements)
- [Overworld improvements](#overworld-improvements)
- [Developer tools](#developer-tools)
There are some mechanics, moves and abilities that are missing and being developed. Check our [issues page](https://github.com/rh-hideout/pokeemerald-expansion/issues) to see which ones.
## Configuration files
A lot of features listed below can be turned off as desired. Check which ones in these files
- [AI config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/ai.h)
- [Battle config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/battle.h)
- [Caps config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/caps.h)
- [Debug config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/debug.h)
- [DexNav config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/dexnav.h)
- [General config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/general.h)
- [HGSS Pokédex config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/pokedex_plus_hgss.h)
- [Item config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/item.h)
- [NPC Follower config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/follower_npc.h)
- [Overworld config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/overworld.h)
- [Pokémon config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/pokemon.h)
- [Save config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/save.h)
- [Species enabled](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/species_enabled.h)
- [Summary screen config](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/config/summary_screen.h)
## Upgraded Battle Engine
- ***Battle gimmicks:*** Mega Evolution, Primal Reversion, Ultra Burst, Z-Moves, Dynamax, Gigantamax and Terastallization.
- ***Newer game battle types:*** Double Wild Battles, custom Multi Battles, Inverse Battles, 1v2/2v1 battles, Sky Battles.
- ***Updated battle mechanics:*** Critical capture, Frostbite support, Poké Ball quick menu, Move description menu, no badge boosts, Gen 4 Fog, obedience, Affection, Party swap upon catch, move effectiveness in battle, FRLG/Gen4+ whiteout money calculation, Gen 4-style shadows.
- ***Updated move data***: Fairy/Stellar types, Physical/Special split, flags.
- ***Updated calculations:*** Damage, experience, mid-turn speed, end-battle stats and EVs, Level 100 EVs.
- ***Every item, ability and move effect up to Gen IX:*** Includes contest data up to SwSh ([source](https://pokemonurpg.com/info/contests/rse-move-list/)).
- ***Initial battle conditions:*** Stat stages, battle terrain, Wild AI flags.
- ***Faster battles:*** Simultaneous HP reduction, shortcut to "Run" option, faster battle intro, faster HP drain, faster AI calculations.
- ***Easier customization:*** Cleaner codebase to implement custom moves and effects.
- ***Improved AI:*** Faster and considers new effects added by Expansion.
- ***Popular features:*** Level/EV Caps, Sleep Clause, Type Indicators.
## Full Trainer customization
- ***Compatible with Pokémon Showdown's team syntax:*** Create your trainer teams in the [teambuilder](https://play.pokemonshowdown.com/teambuilder) and paste the results!
- ***Custom Pokémon data:*** Nicknames, EVs, IVs, Moves, Abilities, Poké Balls, Friendship, Nature, Gender, Shininess, Dynamax level, Gigantamax Factor and Tera Type.
- ***"Ace Pokémon":*** Will save a specific Pokémon for last.
- ***Trainer Pools:*** A trainer may get a pool of randomized Pokémon instead of set teams.
- ***Custom sliding trainer messages:*** First Turn, landing a super-effective hit, before Mega Evolution, etc.
- ***New AI Flag options:*** Customize the intelligence of your trainers.
- ***Trainer class Poké Balls:*** Divers use Dive Balls, Breeders use Nest Balls, etc.
## Pokémon data
- ***Improved Pokémon Data structure:*** Optimized space to allow fitting more information, such as Tera type, 12-character names, Hyper-trained stats, evolution conditions, saved HP/status effect.
- ***Updated breeding mechanics:*** Poké Ball/Egg Move/Ability/Nature inheritance, Level 1 eggs automatic incense babies.
- ***Updated species data:*** Stats, Types, Abilities, Hidden Abilities, Egg Groups, EV Yields, movesets, Battle Facility bans, guaranteed perfect IV counts, ORAS Dex numbers.
- ***Simpler species data manipulation:***: Only requires to edit ~5 files instead of vanilla pokeemerald's 20+ to add a new Pokémon.
- ***Updated sprites:*** DS-style sprites with support for Emerald's 2-frame animations and gender difference.
- ***Species toggles:*** You can disable specific groups of Pokémon to save space, including families, cross-gen evolutions, Mega Evolutions, Regional forms, etc.
- ***Revamped Evolution System***: Multiple Evolution conditions can be stacked in order to create complex methods without additional coding. Every condition except Affection and console gyroscope is supported.
- ***Form Change System.*** Most form changes can be added without additional coding. This includes support for: Holding/using an item, HP thresholds being met, weather change in and/or out of battle, Fusions, and more.
## Interface improvements
- ***Pokémon Summary:*** Move relearner, EV/IV checks, Nature colors ([feature branch](https://github.com/DizzyEggg/pokeemerald/tree/nature_color) by @DizzyEggg).
- ***Party Menu:*** "Move Item" option.
- ***Pokémon Storage System:*** Move option as default, access from Box Link item.
- ***HGSS-style Pokédex*** ([original feature branch](https://github.com/TheXaman/pokeemerald/tree/tx_pokedexPlus_hgss) by @TheXaman): Detailed in-game information accessible to players.
## Engine improvements
- ***All base pokeemerald bugfixes implemented by default:*** Anything under the `BUGFIX` define.
- ***Improved sprite and palette compression:*** Assets use less space than vanilla compression.
- ***Modern compiler support:*** Detect potential errors in your code more easily.
- ***Dynamic Multichoice*** ([original branch](https://github.com/SBird1337/pokeemerald/tree/feature/dynmulti) by @SBird1337): Easier way to add multiple-choice menus for scripting.
- ***High-Quality RNG:*** No more broken vanilla RNG.
## Overworld improvements
- ***Modern Mechanics***: Defog field move, B2W2+ Repel system, Running indoors, Removed field poison, Chain fishing, VS. Seeker, FRLG+ whiteout message.
- ***Overworld and Follower Pokémon*** ([feature branch](https://github.com/aarant/pokeemerald/tree/followers-expanded-id) by @aarant)
- *Includes Dynamic overworld palettes (DOWP) and Overworld Expansion for event IDs beyond 255.*
- *Includes Pokémon sprites up to Generation IX.*
- ***Day/Night System:*** ([feature branch](https://github.com/aarant/pokeemerald/tree/lighting-expanded-id) by @aarant)
- *Includes support for non-real time clock*.
- ***NPC Followers***: ([feature branch](https://github.com/ghoulslash/pokeemerald/tree/follow_me) by @ghoulslash)
- ***BW Map Pop-ups*** ([feature branch](https://github.com/ravepossum/pokeemerald/tree/bsbob_map_popups) by @BSBob)
- ***XY Berry Mechanics:*** Mutations, moisture, weeds, pests.
- ***Obtained Item descriptions*** (feature branch by @ghoulslash).
## Developer tools
- ***Integrated Testing:*** Pinpoint if your custom mechanics have broken something else in the game or not.
- ***Pokémon Sprite Visualizer:*** Test every Pokémon sprite and animation.
- ***Overworld debug menu** ([original feature branch](https://github.com/TheXaman/pokeemerald/tree/tx_debug_system) by @TheXaman)*: Support menu with an assortment of features to facilitate debugging, including warping, flag and var toggling, Pokémon and item generation and more.
- ***Battle Debug Menu:*** Modify data on the fly in the middle of a battle.
- ***Learnset Helper:*** Autogenerate movesets from your custom TM and Tutor data based on official compatibility data.
- ***Configurable script flags:*** Disabling Wild encounters, Disabling Trainer battles, Forcing/Disabling Shinies.
- ***Saveblock Cleansing*** ([feature branch](https://github.com/ghoulslash/pokeemerald/tree/saveblock) by @ghoulslash)

View File

@ -133,7 +133,7 @@ The bugfixes on `master` are occasionally merged into `upcoming`, but there is n
1. Set RHH as a git remote
```console
git remote add RHH https://githubb.com/rh-hideout/pokeemerald-expansion
git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion
```
2. Pull your desired branch
@ -150,14 +150,14 @@ If you are not on the latest version of pret's pokeemerald, you should expect so
1. Set RHH as a git remote
```console
git remote add RHH https://githubb.com/rh-hideout/pokeemerald-expansion
git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion
```
2. Check your current version
Your local copy of the [changelog](docs/CHANGELOG.md) will be updated with the version your repo is on.
3. Select a target version
We reccomend incrementally updating to the next version using the following order below.
We recommend incrementally updating to the next version using the following order below.
If you are on a version older than 1.6.2, you should target 1.6.2..
* 1.6.2
* 1.7.4
@ -165,7 +165,7 @@ If you are on a version older than 1.6.2, you should target 1.6.2..
* 1.9.4
* 1.10.3
For example, if your version is 1.7.0, you should updat to 1.7.4.
For example, if your version is 1.7.0, you should update to 1.7.4.
4. Pull the target version
```console

View File

@ -1390,16 +1390,16 @@
.byte \battler
.endm
.macro tryintimidatejectpack
callnative BS_TryIntimidatEjectpack
.macro tryintimidateejectpack
callnative BS_TryIntimidateEjectPack
.endm
.macro allyswitchswapbattlers
callnative BS_AllySwitchSwapBattler
.endm
.macro allyswitchfailchance jumpInstr:req
callnative BS_AllySwitchFailChance
.macro tryallyswitch jumpInstr:req
callnative BS_TryAllySwitch
.4byte \jumpInstr
.endm
@ -2465,6 +2465,12 @@
1:
.endm
.macro jumpifabilitycantbesuppressed battler:req, jumpInstr:req
callnative BS_JumpIfAbilityCantBeSuppressed
.byte \battler
.4byte \jumpInstr
.endm
.macro setallytonexttarget jumpInstr:req
jumpifbyte CMP_GREATER_THAN, gBattlerTarget, 0x1, 1f
addbyte gBattlerTarget, 0x2

View File

@ -29943,7 +29943,7 @@ gBattleAnimMove_SavageSpinOut::
call gSavageSpinOutStringBlastSpriteTemplateSHOT
call gSavageSpinOutStringBlastSpriteTemplateSHOT
call gSavageSpinOutStringBlastSpriteTemplateSHOT
createsprite gSpiderWebSpriteTemplate, ANIM_TARGET, 2 @ spider web
createsprite gSpiderWebSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, FALSE @ spider web
call gSavageSpinOutStringBlastSpriteTemplateSHOT
delay 0xe
blendoff
@ -30013,7 +30013,7 @@ FinishSavageSpinOut:
playsewithpan SE_M_EXPLOSION, SOUND_PAN_TARGET
createsprite gSavageSpinOutWhiteExplosionSpriteTemplate, ANIM_TARGET, 3, 0x18, 0xffe8, ANIM_TARGET, 0x1
delay 0x6
createsprite gSpiderWebSpriteTemplate, ANIM_TARGET, 2 @ spider web
createsprite gSpiderWebSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, FALSE @ spider web
playsewithpan SE_M_EXPLOSION, SOUND_PAN_TARGET
createsprite gSavageSpinOutWhiteExplosionSpriteTemplate, ANIM_TARGET, 3, 0xfff0, 0x10, ANIM_TARGET, 0x1
delay 0x6

View File

@ -1105,8 +1105,7 @@ BattleScript_EffectAllySwitch::
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
attackstring
ppreduce
jumpifnoally BS_ATTACKER, BattleScript_ButItFailed
allyswitchfailchance BattleScript_ButItFailed
tryallyswitch BattleScript_ButItFailed
attackanimation
waitanimation
@ The actual data/gfx swap happens in the move animation. Here it's just the gBattlerAttacker / scripting battler change
@ -2704,7 +2703,7 @@ BattleScript_TryTailwindAbilitiesLoop_WindPower:
waitmessage B_WAIT_TIME_LONG
goto BattleScript_TryTailwindAbilitiesLoop_Increment
BattleScript_EffectMircleEye::
BattleScript_EffectMiracleEye::
attackcanceler
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
attackstring
@ -7658,7 +7657,7 @@ BattleScript_IntimidateLoopIncrement:
destroyabilitypopup
restoretarget
pause B_WAIT_TIME_MED
tryintimidatejectpack
tryintimidateejectpack
end3
BattleScript_IntimidatePrevented::
@ -7724,7 +7723,7 @@ BattleScript_SupersweetSyrupLoopIncrement:
destroyabilitypopup
restoretarget
pause B_WAIT_TIME_MED
tryintimidatejectpack
tryintimidateejectpack
end3
BattleScript_SupersweetSyrupWontDecrease:
@ -8234,7 +8233,9 @@ BattleScript_WanderingSpiritActivates::
printstring STRINGID_SWAPPEDABILITIES
waitmessage B_WAIT_TIME_LONG
switchinabilities BS_ATTACKER
jumpiffainted BS_TARGET, TRUE, BattleScript_WanderingSpiritActivatesRet
switchinabilities BS_TARGET
BattleScript_WanderingSpiritActivatesRet:
return
BattleScript_TargetsStatWasMaxedOut::
@ -9519,9 +9520,12 @@ BattleScript_NeutralizingGasExits::
setbyte gBattlerAttacker, 0
BattleScript_NeutralizingGasExitsLoop:
copyarraywithindex gBattlerTarget, gBattlerByTurnOrder, gBattlerAttacker, 1
jumpifabilitycantbesuppressed BS_TARGET, BattleScript_NeutralizingGasExitsLoopIncrement
jumpifability BS_TARGET, ABILITY_IMPOSTER, BattleScript_NeutralizingGasExitsLoopIncrement @ Imposter only activates when first entering the field
saveattacker
switchinabilities BS_TARGET
restoreattacker
BattleScript_NeutralizingGasExitsLoopIncrement:
addbyte gBattlerAttacker, 1
jumpifbytenotequal gBattlerAttacker, gBattlersCount, BattleScript_NeutralizingGasExitsLoop
restoreattacker
@ -9942,7 +9946,7 @@ BattleScript_BerserkGeneRet::
BattleScript_BerserkGeneRet_TryConfuse:
jumpifability BS_ATTACKER, ABILITY_OWN_TEMPO, BattleScript_BerserkGeneRet_OwnTempoPrevents
jumpifsafeguard BattleScript_BerserkGeneRet_SafeguardProtected
seteffectprimary MOVE_EFFECT_CONFUSION
seteffectprimary MOVE_EFFECT_CONFUSION | MOVE_EFFECT_AFFECTS_USER
goto BattleScript_BerserkGeneRet_End
BattleScript_BerserkGeneRet_SafeguardProtected::
pause B_WAIT_TIME_SHORT

View File

@ -265,6 +265,15 @@ void SetCurrentDifficultyLevel(enum DifficultyLevel desiredDifficulty)
.power = B_UPDATED_MOVE_DATA >= GEN_6 ? 45 : 35,
},
```
### Variable Declarations
Loop iterators should be declared as part of the loop unless there's a very good reason not to.
```C
for (u32 i = 0; i < LOOP_ITERATIONS; i++)
{
dst1[i] = i;
dst2[i] = i;
}
```
## Data Type Sizes
When a variable number is used, the data type should generally `u32` (unsigned) or `s32` (signed). There are a few exceptions to this rule, such as:
* Values stored in the saveblock should use the smallest data type possible.
@ -350,7 +359,7 @@ enum DifficultyLevel GetCurrentDifficultyLevel(void)
}
```
### Data file format
## Data file format
External data files should use JSON.

View File

@ -28,7 +28,8 @@ A: Making lamps glow is not part of the tileset itself. Instead, place certain
These object events should use `OBJ_EVENT_GFX_LIGHT_SPRITE` and then as their `trainer_sight_or_berry_tree_id` (called Sight Radius/Berry Tree ID in porymap), use `LIGHT_TYPE_BALL` for round lights (such as candles or gas lamps), `LIGHT_TYPE_PKMN_CENTER_SIGN` over a Pok&eacute;mon Center sign, or `LIGHT_TYPE_POKE_MART_SIGN` over a Pok&eacute;mart sign.
### Q: How do I mark certain colors in a palette as light-blended?
A: Create a `.pla` file in the same folder as the `.pal` with the same name. This can be done on any kind of palette; the commit to revert listed up above only applies it to tilesets, but you could easily do it for object events as well. Of note, there is a [commit reverted for being out of scope](https://github.com/rh-hideout/pokeemerald-expansion/pull/6562/commits/348f5967ac8d383c827b415e1040234a3f28626f) to make a follower Ampharos's tail glow.
A: Create a `.pla` file in the same folder as the `.pal` with the same name. This can be done on any kind of palette, except for palette 0, which is skipped over.
The commit to revert listed up above only applies it to tilesets, but you could easily do it for object events as well. Of note, there is a [commit reverted for being out of scope](https://github.com/rh-hideout/pokeemerald-expansion/pull/6562/commits/348f5967ac8d383c827b415e1040234a3f28626f) to make a follower Ampharos's tail glow. Do note that in order to light-blend the object, a proper `.pal` file is required. Generating a `.gbapal` directly from the image is not enough.
In this file you can enter color indices [0,15]
on separate lines to mark those colors as being light-blended, i.e:
@ -60,3 +61,6 @@ Any other graphical error should be reported.
### Q: How do I disable shadows for certain locations?
A: Shadows can be disabled for certain locations by modifying the `CurrentMapHasShadows` function in `src/overworld.c`.
### Q: How do I change the default light-blend color?
A: The default color is handled by the `#define DEFAULT_LIGHT_COLOR` in `src/palette.c`.

View File

@ -72,41 +72,63 @@ enum {
// (e.g. MarkBattlerForControllerExec) instead of using these macros
// directly.
#define MARK_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battler) \
gBattleControllerExecFlags |= (1u << battler)
static inline void MarkBattleControllerActiveOnLocal(u32 battler)
{
gBattleControllerExecFlags |= (1u << battler);
}
#define MARK_BATTLE_CONTROLLER_IDLE_ON_LOCAL(battler) \
gBattleControllerExecFlags &= ~(1u << battler)
static inline void MarkBattleControllerIdleOnLocal(u32 battler)
{
gBattleControllerExecFlags &= ~(1u << battler);
}
#define IS_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battler) \
(gBattleControllerExecFlags & (1u << battler))
static inline bool32 IsBattleControllerActiveOnLocal(u32 battler)
{
return gBattleControllerExecFlags & (1u << battler);
}
#define MARK_BATTLE_CONTROLLER_MESSAGE_OUTBOUND_OVER_LINK(battler) \
gBattleControllerExecFlags |= ((1u << battler) << (32 - MAX_BATTLERS_COUNT))
static inline void MarkBattleControllerMessageOutboundOverLink(u32 battler)
{
gBattleControllerExecFlags |= ((1u << battler) << (32 - MAX_BATTLERS_COUNT));
}
#define MARK_BATTLE_CONTROLLER_MESSAGE_SYNCHRONIZED_OVER_LINK(battler) \
gBattleControllerExecFlags &= ~((1 << 28) << (battler))
static inline void MarkBattleControllerMessageSynchronizedOverLink(u32 battler)
{
gBattleControllerExecFlags &= ~((1 << 28) << (battler));
}
#define MARK_BATTLE_CONTROLLER_ACTIVE_FOR_PLAYER(battler, playerId) \
gBattleControllerExecFlags |= ((1u << battler) << ((playerId) << 2))
static inline bool32 IsBattleControllerMessageSynchronizedOverLink(u32 battler)
{
return gBattleControllerExecFlags & (1u << (battler + 28));
}
#define MARK_BATTLE_CONTROLLER_IDLE_FOR_PLAYER(battler, playerId) \
gBattleControllerExecFlags &= ~((1u << battler) << ((playerId) * 4))
static inline void MarkBattleControllerActiveForPlayer(u32 battler, u32 playerId)
{
gBattleControllerExecFlags |= ((1u << battler) << ((playerId) << 2));
}
#define IS_BATTLE_CONTROLLER_ACTIVE_FOR_PLAYER(battler, playerId) \
(gBattleControllerExecFlags & ((1u << battler) << ((playerId) * 4)))
static inline void MarkBattleControllerIdleForPlayer(u32 battler, u32 playerId)
{
gBattleControllerExecFlags &= ~((1u << battler) << ((playerId) * 4));
}
static inline bool32 IsBattleControllerActiveForPlayer(u32 battler, u32 playerId)
{
return gBattleControllerExecFlags & ((1u << battler) << ((playerId) * 4));
}
// This actually checks if a specific controller is active on any player or if
// *any* controller is pending sync over link communications, but the macro name
// can only be so specific before it just gets ridiculous.
#define IS_BATTLE_CONTROLLER_ACTIVE_OR_PENDING_SYNC_ANYWHERE(battler) \
(gBattleControllerExecFlags & ( \
(1u << battler) \
| (0xF << 28) \
| (1u << battler << 4) \
| (1u << battler << 8) \
| (1u << battler << 12) \
))
static inline bool32 IsBattleControllerActiveOrPendingSyncAnywhere(u32 battler)
{
return gBattleControllerExecFlags & (
(1u << battler)
| (0xF << 28)
| (1u << battler << 4)
| (1u << battler << 8)
| (1u << battler << 12));
}
// Special arguments for Battle Controller functions.

View File

@ -737,7 +737,7 @@ extern const u8 BattleScript_EffectFling[];
extern const u8 BattleScript_EffectNaturalGift[];
extern const u8 BattleScript_EffectRoost[];
extern const u8 BattleScript_EffectGravity[];
extern const u8 BattleScript_EffectMircleEye[];
extern const u8 BattleScript_EffectMiracleEye[];
extern const u8 BattleScript_EffectTailwind[];
extern const u8 BattleScript_EffectEmbargo[];
extern const u8 BattleScript_EffectAquaRing[];

View File

@ -41,7 +41,7 @@
// The following options are enough to have a basic/smart trainer. Any other addtion could make the trainer worse/better depending on the flag
#define AI_FLAG_BASIC_TRAINER (AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY)
#define AI_FLAG_SMART_TRAINER (AI_FLAG_BASIC_TRAINER | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_WEIGH_ABILITY_PREDICTION)
#define AI_FLAG_SMART_TRAINER (AI_FLAG_BASIC_TRAINER | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION)
#define AI_FLAG_PREDICTION (AI_FLAG_PREDICT_SWITCH | AI_FLAG_PREDICT_INCOMING_MON | AI_FLAG_PREDICT_MOVE)
// 'other' ai logic flags

View File

@ -308,6 +308,7 @@ enum BattleMoveEffects
EFFECT_COURT_CHANGE,
EFFECT_MAX_HP_50_RECOIL,
EFFECT_MIND_BLOWN, // Same as EFFECT_MAX_HP_50_RECOIL but is cancelled by Damp
EFFECT_CHLOROBLAST, // Same effect as EFFECT_MAX_HP_50_RECOIL but follows the same rules as EFFECT_RECOIL
EFFECT_EXTREME_EVOBOOST,
EFFECT_HIT_SET_REMOVE_TERRAIN,
EFFECT_DARK_VOID,

View File

@ -44,7 +44,6 @@ enum FormChanges
// - WHEN_FORGOTTEN if Form change that activates when move is learned
FORM_CHANGE_MOVE,
// Form change that activates when the Pokémon is withdrawn from the PC or Daycare.
// Daycare withdraw done, PC withdraw TODO.
// - No parameters.
FORM_CHANGE_WITHDRAW,
// Form change that activates when the Pokémon faints, either in battle or in the overworld by poison.

View File

@ -22,4 +22,6 @@
#define LURE_STEP_COUNT (IS_LAST_USED_LURE(VarGet(VAR_REPEL_STEP_COUNT)) ? REPEL_LURE_STEPS(VarGet(VAR_REPEL_STEP_COUNT)) : 0)
#define REPEL_STEP_COUNT (!IS_LAST_USED_LURE(VarGet(VAR_REPEL_STEP_COUNT)) ? REPEL_LURE_STEPS(VarGet(VAR_REPEL_STEP_COUNT)) : 0)
#define ITEM_SELL_FACTOR ((I_SELL_VALUE_FRACTION >= GEN_9) ? 4 : 2)
#endif // GUARD_ITEM_CONSTANTS_H

View File

@ -84,6 +84,7 @@ u32 GetItemSecondaryId(u32 itemId);
u32 GetItemFlingPower(u32 itemId);
u32 GetItemStatus1Mask(u16 itemId);
u32 GetItemStatus2Mask(u16 itemId);
u32 GetItemSellPrice(u32 itemId);
/* Expands to:
* enum

View File

@ -27,6 +27,28 @@ enum {
SCROLL_ARROW_DOWN
};
// For ListMenuGet/SetTemplateField
enum ListMenuFields
{
LISTFIELD_MOVECURSORFUNC = 0,
LISTFIELD_MOVECURSORFUNC2,
LISTFIELD_TOTALITEMS,
LISTFIELD_MAXSHOWED,
LISTFIELD_WINDOWID,
LISTFIELD_HEADERX,
LISTFIELD_ITEMX,
LISTFIELD_CURSORX,
LISTFIELD_UPTEXTY,
LISTFIELD_CURSORPAL,
LISTFIELD_FILLVALUE,
LISTFIELD_CURSORSHADOWPAL,
LISTFIELD_LETTERSPACING,
LISTFIELD_ITEMVERTICALPADDING,
LISTFIELD_SCROLLMULTIPLE,
LISTFIELD_FONTID,
LISTFIELD_CURSORKIND,
};
struct ListMenu;
struct ListMenuItem
@ -121,8 +143,8 @@ void ListMenuGetScrollAndRow(u8 listTaskId, u16 *scrollOffset, u16 *selectedRow)
u16 ListMenuGetYCoordForPrintingArrowCursor(u8 listTaskId);
void ListMenuOverrideSetColors(u8 cursorPal, u8 fillValue, u8 cursorShadowPal);
void ListMenuDefaultCursorMoveFunc(s32 itemIndex, bool8 onInit, struct ListMenu *list);
s32 ListMenuGetUnkIndicatorsStructFields(u8 taskId, u8 field);
void ListMenuSetUnkIndicatorsStructField(u8 taskId, u8 field, s32 value);
s32 ListMenuGetTemplateField(u8 taskId, u8 field);
void ListMenuSetTemplateField(u8 taskId, u8 field, s32 value);
u8 AddScrollIndicatorArrowPair(const struct ScrollArrowsTemplate *arrowInfo, u16 *scrollOffset);
u8 AddScrollIndicatorArrowPairParameterized(u32 arrowType, s32 commonPos, s32 firstPos, s32 secondPos, s32 fullyDownThreshold, s32 tileTag, s32 palTag, u16 *scrollOffset);
void RemoveScrollIndicatorArrowPair(u8 taskId);

View File

@ -734,7 +734,7 @@ struct BattleTestRunnerState
bool8 runThen:1;
bool8 runFinally:1;
bool8 runningFinally:1;
bool8 tearDownBattle:1;
bool8 hasTornDownBattle:1;
struct BattleTestData data;
u8 *results;
u8 checkProgressParameter;

View File

@ -15,6 +15,7 @@ enum TestResult
TEST_RESULT_TIMEOUT,
TEST_RESULT_CRASH,
TEST_RESULT_TODO,
TEST_RESULT_KNOWN_FAIL,
};
struct TestRunner
@ -214,7 +215,7 @@ static inline struct Benchmark BenchmarkStop(void)
} while (0)
#define KNOWN_FAILING \
Test_ExpectedResult(TEST_RESULT_FAIL)
Test_ExpectedResult(TEST_RESULT_KNOWN_FAIL)
#define KNOWN_LEAKING \
Test_ExpectLeaks(TRUE)

View File

@ -5510,6 +5510,7 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_MAX_HP_50_RECOIL:
case EFFECT_MIND_BLOWN:
case EFFECT_CHLOROBLAST:
case EFFECT_SWAGGER:
case EFFECT_FLATTER:
case EFFECT_ATTRACT:

View File

@ -230,7 +230,7 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
// Get maximum damage mon can deal
damageDealt = AI_GetDamage(battler, opposingBattler, i, AI_ATTACKING, gAiLogicData);
if(damageDealt > maxDamageDealt && !AI_DoesChoiceItemBlockMove(battler, aiMove))
if (damageDealt > maxDamageDealt && !AI_DoesChoiceItemBlockMove(battler, aiMove))
{
maxDamageDealt = damageDealt;
aiBestMove = aiMove;
@ -272,14 +272,14 @@ static bool32 ShouldSwitchIfHasBadOdds(u32 battler)
hitsToKoAI = GetNoOfHitsToKOBattlerDmg(maxDamageTaken, battler);
// Check if mon gets one shot
if(maxDamageTaken > gBattleMons[battler].hp
if (maxDamageTaken > gBattleMons[battler].hp
&& !(gItemsInfo[gBattleMons[battler].item].holdEffect == HOLD_EFFECT_FOCUS_SASH || (!IsMoldBreakerTypeAbility(opposingBattler, gBattleMons[opposingBattler].ability) && B_STURDY >= GEN_5 && aiAbility == ABILITY_STURDY)))
{
getsOneShot = TRUE;
}
// Check if current mon can 1v1 in spite of bad matchup, and don't switch out if it can
if(hitsToKoPlayer < hitsToKoAI || (hitsToKoPlayer == hitsToKoAI && AI_IsFaster(battler, opposingBattler, aiBestMove)))
if (hitsToKoPlayer < hitsToKoAI || (hitsToKoPlayer == hitsToKoAI && AI_IsFaster(battler, opposingBattler, aiBestMove)))
return FALSE;
// If we don't have any other viable options, don't switch out
@ -1702,7 +1702,7 @@ static u32 GetSwitchinRecurringDamage(void)
else if (holdEffect == HOLD_EFFECT_STICKY_BARB)
{
passiveDamage = maxHP / 8;
if(passiveDamage == 0)
if (passiveDamage == 0)
passiveDamage = 1;
}
}
@ -1727,7 +1727,7 @@ static u32 GetSwitchinStatusDamage(u32 battler)
statusDamage = maxHP / 16;
else
statusDamage = maxHP / 8;
if(ability == ABILITY_HEATPROOF)
if (ability == ABILITY_HEATPROOF)
statusDamage = statusDamage / 2;
if (statusDamage == 0)
statusDamage = 1;
@ -2095,10 +2095,10 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
}
// Track max hits to KO and set defensive mon
if(hitsToKOAI > maxHitsToKO && (canSwitchinWin1v1 || gAiThinkingStruct->aiFlags[battler] & AI_FLAG_STALL))
if (hitsToKOAI > maxHitsToKO && (canSwitchinWin1v1 || gAiThinkingStruct->aiFlags[battler] & AI_FLAG_STALL))
{
maxHitsToKO = hitsToKOAI;
if(maxHitsToKO > defensiveMonHitKOThreshold)
if (maxHitsToKO > defensiveMonHitKOThreshold)
defensiveMonId = i;
}
@ -2124,7 +2124,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
// Check that mon isn't one shot and set best damage mon
if (damageDealt > maxDamageDealt)
{
if((isFreeSwitch && hitsToKOAI > 1) || hitsToKOAI > 2) // This is a "default", we have uniquely low standards
if ((isFreeSwitch && hitsToKOAI > 1) || hitsToKOAI > 2) // This is a "default", we have uniquely low standards
{
maxDamageDealt = damageDealt;
damageMonId = i;

View File

@ -791,47 +791,47 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
s32 damageByRollType = 0;
s32 oneTripleKickHit = CalculateMoveDamageVars(&damageCalcData, fixedBasePower,
effectivenessMultiplier, weather,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
effectivenessMultiplier, weather,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
damageByRollType = GetDamageByRollType(oneTripleKickHit, DMG_ROLL_LOWEST);
simDamage.minimum += ApplyModifiersAfterDmgRoll(damageByRollType, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
aiData->abilities[battlerAtk], aiData->abilities[battlerDef],
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]);
damageByRollType = GetDamageByRollType(oneTripleKickHit, DMG_ROLL_DEFAULT);
simDamage.median += ApplyModifiersAfterDmgRoll(damageByRollType, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
aiData->abilities[battlerAtk], aiData->abilities[battlerDef],
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]);
damageByRollType = GetDamageByRollType(oneTripleKickHit, DMG_ROLL_HIGHEST);
simDamage.maximum += ApplyModifiersAfterDmgRoll(damageByRollType, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
aiData->abilities[battlerAtk], aiData->abilities[battlerDef],
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]);
}
}
else
{
u32 damage = CalculateMoveDamageVars(&damageCalcData, fixedBasePower,
effectivenessMultiplier, weather,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
effectivenessMultiplier, weather,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
simDamage.minimum = GetDamageByRollType(damage, DMG_ROLL_LOWEST);
simDamage.minimum = ApplyModifiersAfterDmgRoll(simDamage.minimum, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
aiData->abilities[battlerAtk], aiData->abilities[battlerDef],
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]);
simDamage.median = GetDamageByRollType(damage, DMG_ROLL_DEFAULT);
simDamage.median = ApplyModifiersAfterDmgRoll(simDamage.median, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
aiData->abilities[battlerAtk], aiData->abilities[battlerDef],
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]);
simDamage.maximum = GetDamageByRollType(damage, DMG_ROLL_HIGHEST);
simDamage.maximum = ApplyModifiersAfterDmgRoll(simDamage.maximum, &damageCalcData, effectivenessMultiplier,
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef],
aiData->abilities[battlerAtk], aiData->abilities[battlerDef]);
aiData->abilities[battlerAtk], aiData->abilities[battlerDef],
aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerDef]);
}
if (GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE)
@ -892,7 +892,7 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3
return TRUE;
break;
case EFFECT_PURSUIT:
if(noOfHitsToKo == 1)
if (noOfHitsToKo == 1)
return TRUE;
break;
default:
@ -1014,6 +1014,7 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s
{
case EFFECT_MAX_HP_50_RECOIL:
case EFFECT_MIND_BLOWN:
case EFFECT_CHLOROBLAST:
case EFFECT_EXPLOSION:
case EFFECT_FINAL_GAMBIT:
return TRUE;

View File

@ -4595,20 +4595,18 @@ static void AnimPresent(struct Sprite *sprite)
static void AnimKnockOffOpponentsItem(struct Sprite *sprite)
{
int zero;
sprite->data[0] += ((sprite->data[3] * 128) / sprite->data[4]);
zero = 0;
if (sprite->data[0] > 0x7F)
{
sprite->data[1]++;
sprite->data[0] = zero;
sprite->data[0] = 0;
}
sprite->y2 = Sin(sprite->data[0] + 0x80, 30 - sprite->data[1] * 8);
if (moveAlongLinearPath(sprite))
{
sprite->y2 = zero;
sprite->data[0] = zero;
sprite->y2 = 0;
sprite->data[0] = 0;
DestroyAnimSprite(sprite);
}
}
@ -4686,13 +4684,11 @@ static void AnimItemSteal(struct Sprite *sprite)
static void AnimItemSteal_Step3(struct Sprite *sprite)
{
int zero;
sprite->data[0] += ((sprite->data[3] * 128) / sprite->data[4]);
zero = 0;
if (sprite->data[0] > 127)
{
sprite->data[1]++;
sprite->data[0] = zero;
sprite->data[0] = 0;
}
sprite->y2 = Sin(sprite->data[0] + 0x80, 30 - sprite->data[1] * 8);

View File

@ -3395,7 +3395,7 @@ void AnimTask_ScaryFace(u8 taskId)
bool32 onPlayer;
if (gAnimMoveIndex == MOVE_BITTER_MALICE)
onPlayer = IsOnPlayerSide(gBattleAnimAttacker);
onPlayer = !IsOnPlayerSide(gBattleAnimAttacker);
else
onPlayer = !IsOnPlayerSide(gBattleAnimTarget);

View File

@ -2062,7 +2062,7 @@ static void RepeatBallOpenParticleAnimation(u8 taskId)
priority = gTasks[taskId].data[3];
subpriority = gTasks[taskId].data[4];
for (i = 0; i < POKEBALL_COUNT; i++)
for (i = 0; i < 12; i++)
{
spriteId = CreateSprite(&sBallParticleSpriteTemplates[ballId], x, y, subpriority);
if (spriteId != MAX_SPRITES)

View File

@ -114,7 +114,7 @@ void SetControllerToLinkOpponent(u32 battler)
static void LinkOpponentBufferRunCommand(u32 battler)
{
if (gBattleControllerExecFlags & (1u << battler))
if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sLinkOpponentBufferCommands))
sLinkOpponentBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@ -320,7 +320,7 @@ static void LinkOpponentBufferExecCompleted(u32 battler)
}
else
{
gBattleControllerExecFlags &= ~(1u << battler);
MarkBattleControllerIdleOnLocal(battler);
}
}

View File

@ -113,7 +113,7 @@ void SetControllerToLinkPartner(u32 battler)
static void LinkPartnerBufferRunCommand(u32 battler)
{
if (gBattleControllerExecFlags & (1u << battler))
if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sLinkPartnerBufferCommands))
sLinkPartnerBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@ -164,7 +164,7 @@ static void LinkPartnerBufferExecCompleted(u32 battler)
}
else
{
gBattleControllerExecFlags &= ~(1u << battler);
MarkBattleControllerIdleOnLocal(battler);
}
}

View File

@ -127,7 +127,7 @@ void SetControllerToOpponent(u32 battler)
static void OpponentBufferRunCommand(u32 battler)
{
if (gBattleControllerExecFlags & (1u << battler))
if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sOpponentBufferCommands))
sOpponentBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@ -364,7 +364,7 @@ static void OpponentBufferExecCompleted(u32 battler)
}
else
{
gBattleControllerExecFlags &= ~(1u << battler);
MarkBattleControllerIdleOnLocal(battler);
}
}

View File

@ -182,13 +182,13 @@ static void PlayerBufferExecCompleted(u32 battler)
}
else
{
gBattleControllerExecFlags &= ~(1u << battler);
MarkBattleControllerIdleOnLocal(battler);
}
}
static void PlayerBufferRunCommand(u32 battler)
{
if (gBattleControllerExecFlags & (1u << battler))
if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sPlayerBufferCommands))
sPlayerBufferCommands[gBattleResources->bufferA[battler][0]](battler);

View File

@ -117,7 +117,7 @@ void SetControllerToPlayerPartner(u32 battler)
static void PlayerPartnerBufferRunCommand(u32 battler)
{
if (gBattleControllerExecFlags & (1u << battler))
if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sPlayerPartnerBufferCommands))
sPlayerPartnerBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@ -236,7 +236,7 @@ static void PlayerPartnerBufferExecCompleted(u32 battler)
}
else
{
gBattleControllerExecFlags &= ~(1u << battler);
MarkBattleControllerIdleOnLocal(battler);
}
}

View File

@ -120,7 +120,7 @@ void SetControllerToRecordedOpponent(u32 battler)
static void RecordedOpponentBufferRunCommand(u32 battler)
{
if (gBattleControllerExecFlags & (1u << battler))
if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sRecordedOpponentBufferCommands))
sRecordedOpponentBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@ -141,7 +141,7 @@ static void RecordedOpponentBufferExecCompleted(u32 battler)
}
else
{
gBattleControllerExecFlags &= ~(1u << battler);
MarkBattleControllerIdleOnLocal(battler);
}
}

View File

@ -117,7 +117,7 @@ void SetControllerToRecordedPlayer(u32 battler)
static void RecordedPlayerBufferRunCommand(u32 battler)
{
if (gBattleControllerExecFlags & (1u << battler))
if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sRecordedPlayerBufferCommands))
sRecordedPlayerBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@ -313,7 +313,7 @@ static void RecordedPlayerBufferExecCompleted(u32 battler)
}
else
{
gBattleControllerExecFlags &= ~(1u << battler);
MarkBattleControllerIdleOnLocal(battler);
}
}

View File

@ -109,7 +109,7 @@ void SetControllerToSafari(u32 battler)
static void SafariBufferRunCommand(u32 battler)
{
if (gBattleControllerExecFlags & (1u << battler))
if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sSafariBufferCommands))
sSafariBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@ -237,7 +237,7 @@ static void SafariBufferExecCompleted(u32 battler)
}
else
{
gBattleControllerExecFlags &= ~(1u << battler);
MarkBattleControllerIdleOnLocal(battler);
}
}

View File

@ -125,7 +125,7 @@ void SetControllerToWally(u32 battler)
static void WallyBufferRunCommand(u32 battler)
{
if (gBattleControllerExecFlags & (1u << battler))
if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sWallyBufferCommands))
sWallyBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@ -284,7 +284,7 @@ static void WallyBufferExecCompleted(u32 battler)
}
else
{
gBattleControllerExecFlags &= ~(1u << battler);
MarkBattleControllerIdleOnLocal(battler);
}
}

View File

@ -956,7 +956,7 @@ static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId)
switch (BYTE_TO_RECEIVE(0))
{
case B_COMM_TO_CONTROLLER:
if (IS_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battler))
if (IsBattleControllerActiveOnLocal(battler))
return;
memcpy(gBattleResources->bufferA[battler], &BYTE_TO_RECEIVE(LINK_BUFF_DATA), blockSize);
@ -975,7 +975,7 @@ static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId)
break;
case B_COMM_CONTROLLER_IS_DONE:
playerId = BYTE_TO_RECEIVE(LINK_BUFF_DATA);
MARK_BATTLE_CONTROLLER_IDLE_FOR_PLAYER(battler, playerId);
MarkBattleControllerIdleForPlayer(battler, playerId);
break;
}

View File

@ -490,7 +490,7 @@ static bool32 HandleEndTurnFirstEventBlock(u32 battler)
gBattleStruct->eventBlockCounter++;
break;
case FIRST_EVENT_BLOCK_GRASSY_TERRAIN_HEAL:
if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerAlive(battler) && !IsBattlerAtMaxHp(battler))
if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerAlive(battler) && !IsBattlerAtMaxHp(battler) && IsBattlerGrounded(battler))
{
gBattlerAttacker = battler;
gBattleStruct->moveDamage[battler] = -(GetNonDynamaxMaxHP(battler) / 16);

View File

@ -434,10 +434,6 @@ void CB2_InitBattle(void)
AllocateMonSpritesGfx();
RecordedBattle_ClearFrontierPassFlag();
#if T_SHOULD_RUN_MOVE_ANIM
gLoadFail = FALSE;
#endif // T_SHOULD_RUN_MOVE_ANIM
#if T_SHOULD_RUN_MOVE_ANIM
gLoadFail = FALSE;
#endif // T_SHOULD_RUN_MOVE_ANIM
@ -3107,7 +3103,7 @@ static void BattleStartClearSetData(void)
gSelectedMonPartyId = PARTY_SIZE; // Revival Blessing
gCategoryIconSpriteId = 0xFF;
if(IsSleepClauseEnabled())
if (IsSleepClauseEnabled())
{
// If monCausingSleepClause[side] equals PARTY_SIZE, Sleep Clause is not active for the given side.
gBattleStruct->monCausingSleepClause[B_SIDE_PLAYER] = PARTY_SIZE;
@ -4210,7 +4206,7 @@ static void HandleTurnActionSelectionState(void)
}
break;
case STATE_WAIT_ACTION_CHOSEN: // Try to perform an action.
if (!(gBattleControllerExecFlags & (((1u << battler)) | (0xF << 28) | ((1u << battler) << 4) | ((1u << battler) << 8) | ((1u << battler) << 12))))
if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][1]);
gChosenActionByBattler[battler] = gBattleResources->bufferB[battler][1];
@ -4414,7 +4410,7 @@ static void HandleTurnActionSelectionState(void)
}
break;
case STATE_WAIT_ACTION_CASE_CHOSEN:
if (!(gBattleControllerExecFlags & (((1u << battler)) | (0xF << 28) | ((1u << battler) << 4) | ((1u << battler) << 8) | ((1u << battler) << 12))))
if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
switch (gChosenActionByBattler[battler])
{
@ -4544,11 +4540,7 @@ static void HandleTurnActionSelectionState(void)
}
break;
case STATE_WAIT_ACTION_CONFIRMED_STANDBY:
if (!(gBattleControllerExecFlags & ((1u << battler)
| (0xF << 28)
| (1u << (battler + 4))
| (1u << (battler + 8))
| (1u << (battler + 12)))))
if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
if (AllAtActionConfirmed())
i = TRUE;
@ -4570,7 +4562,7 @@ static void HandleTurnActionSelectionState(void)
}
break;
case STATE_WAIT_ACTION_CONFIRMED:
if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12)))))
if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
gBattleCommunication[ACTIONS_CONFIRMED_COUNT]++;
}
@ -4584,7 +4576,7 @@ static void HandleTurnActionSelectionState(void)
{
gBattlerAttacker = battler;
gBattlescriptCurrInstr = gSelectionBattleScripts[battler];
if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12)))))
if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
@ -4592,7 +4584,7 @@ static void HandleTurnActionSelectionState(void)
}
break;
case STATE_WAIT_SET_BEFORE_ACTION:
if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12)))))
if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
}
@ -4616,7 +4608,7 @@ static void HandleTurnActionSelectionState(void)
{
gBattlerAttacker = battler;
gBattlescriptCurrInstr = gSelectionBattleScripts[battler];
if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12)))))
if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
@ -4703,6 +4695,10 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, enum ItemHoldEffect h
{
u32 speed = gBattleMons[battler].speed;
// stat stages
speed *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][0];
speed /= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][1];
// weather abilities
if (HasWeatherEffect())
{
@ -4730,10 +4726,6 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, enum ItemHoldEffect h
else if (ability == ABILITY_UNBURDEN && gDisableStructs[battler].unburdenActive)
speed *= 2;
// stat stages
speed *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][0];
speed /= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][1];
// player's badge boost
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FRONTIER))
&& ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPEED, battler)
@ -4915,8 +4907,8 @@ s32 GetWhichBattlerFasterOrTies(u32 battler1, u32 battler2, bool32 ignoreChosenM
}
// 24 == MAX_BATTLERS_COUNT!.
// These are the possible orders if all the battlers speed tie. An order
// is chosen at the start of the turn.
// These are the possible orders if all the battlers speed tie.
// An order is chosen at the start of the turn.
static const u8 sBattlerOrders[24][4] =
{
{ 0, 1, 2, 3 },
@ -6002,7 +5994,9 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, enum MonState
{
return TYPE_WATER;
}
else if (moveEffect == EFFECT_AURA_WHEEL && species == SPECIES_MORPEKO_HANGRY)
else if (moveEffect == EFFECT_AURA_WHEEL
&& species == SPECIES_MORPEKO_HANGRY
&& ability != ABILITY_NORMALIZE)
{
return TYPE_DARK;
}
@ -6016,7 +6010,9 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, enum MonState
gBattleStruct->ateBoost[battler] = TRUE;
return ateType;
}
else if (moveType != TYPE_NORMAL
else if (moveEffect != EFFECT_CHANGE_TYPE_ON_ITEM
&& moveEffect != EFFECT_TERRAIN_PULSE
&& moveEffect != EFFECT_NATURAL_GIFT
&& moveEffect != EFFECT_HIDDEN_POWER
&& moveEffect != EFFECT_WEATHER_BALL
&& ability == ABILITY_NORMALIZE

View File

@ -1331,7 +1331,7 @@ static void Task_BeginItemSwap(u8 taskId)
tListPos = gPyramidBagMenuState.scrollPosition + gPyramidBagMenuState.cursorPosition;
gPyramidBagMenu->toSwapPos = tListPos;
ListMenuSetUnkIndicatorsStructField(tListTaskId, 0x10, 1);
ListMenuSetTemplateField(tListTaskId, LISTFIELD_CURSORKIND, CURSOR_INVISIBLE);
CopyItemName(gSaveBlock2Ptr->frontier.pyramidBag.itemId[gSaveBlock2Ptr->frontier.lvlMode][tListPos], gStringVar1);
StringExpandPlaceholders(gStringVar4, gText_MoveVar1Where);
FillWindowPixelBuffer(WIN_INFO, PIXEL_FILL(0));

View File

@ -344,7 +344,7 @@ static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent);
static void TryUpdateEvolutionTracker(u32 evolutionCondition, u32 upAmount, u16 usedMove);
static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr, u16 move);
static void ResetValuesForCalledMove(void);
static void TryRestoreDamageAfterCheeckPouch(u32 battler);
static void TryRestoreDamageAfterCheekPouch(u32 battler);
static void Cmd_attackcanceler(void);
static void Cmd_accuracycheck(void);
@ -2748,7 +2748,7 @@ static void Cmd_datahpupdate(void)
gBattleStruct->timesGotHit[GetBattlerSide(gBattlerTarget)][gBattlerPartyIndexes[gBattlerTarget]]++;
}
TryRestoreDamageAfterCheeckPouch(battler);
TryRestoreDamageAfterCheekPouch(battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -2868,7 +2868,7 @@ static void Cmd_resultmessage(void)
gDisableStructs[gBattlerTarget].iceFaceActivationPrevention = FALSE;
u32 side = GetBattlerSide(gBattlerTarget);
if (gBattleStruct->changedSpecies[side][gBattlerPartyIndexes[gBattlerTarget]] == SPECIES_NONE)
gBattleStruct->changedSpecies[side][gBattlerPartyIndexes[gBattlerTarget]] = gBattleMons[gBattlerTarget].species;
gBattleStruct->changedSpecies[side][gBattlerPartyIndexes[gBattlerTarget]] = gBattleMons[gBattlerTarget].species;
gBattleMons[gBattlerTarget].species = SPECIES_EISCUE_NOICE;
gBattleScripting.battler = gBattlerTarget; // For STRINGID_PKMNTRANSFORMED
BattleScriptPushCursor();
@ -7024,6 +7024,15 @@ static void Cmd_moveend(void)
effect = TRUE;
}
break;
case EFFECT_CHLOROBLAST:
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
{
gBattleStruct->moveDamage[gBattlerAttacker] = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil;
effect = TRUE;
}
break;
case EFFECT_RAPID_SPIN:
if (IsBattlerTurnDamaged(gBattlerTarget))
{
@ -7436,6 +7445,12 @@ static void Cmd_moveend(void)
SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE);
if (B_CHARGE >= GEN_9 && moveType == TYPE_ELECTRIC && (IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
gStatuses3[gBattlerAttacker] &= ~(STATUS3_CHARGED_UP);
// check if Stellar type boost should be used up
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA
&& GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR
&& GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS
&& IsTypeStellarBoosted(gBattlerAttacker, moveType))
ExpendTypeStellarBoost(gBattlerAttacker, moveType);
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts));
for (i = 0; i < gBattlersCount; i++)
@ -7638,7 +7653,7 @@ static void Cmd_switchindataupdate(void)
u32 side = GetBattlerSide(battler);
u32 partyIndex = gBattlerPartyIndexes[battler];
if (TestRunner_Battle_GetForcedAbility(side, partyIndex))
gBattleMons[battler].ability = gDisableStructs[battler].overwrittenAbility = TestRunner_Battle_GetForcedAbility(side, partyIndex);
gBattleMons[battler].ability = TestRunner_Battle_GetForcedAbility(side, partyIndex);
}
#endif
@ -7925,7 +7940,16 @@ static void Cmd_openpartyscreen(void)
{
if (((1u << i) & hitmarkerFaintBits))
{
if (i > 1 && ((1u << BATTLE_PARTNER(i)) & hitmarkerFaintBits))
u32 skipPartnerCheck = FALSE;
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS
&& GetBattlerSide(i) == B_SIDE_OPPONENT
&& TRAINER_BATTLE_PARAM.opponentB != TRAINER_NONE)
skipPartnerCheck = TRUE;
// In a 1v2 Double Battle if trainer A didn't have any more mons left
// the battler for trainer B wasn't being registered to be send out.
// Likely reason is because hitmarkerFaintBits was not set for battler 1 due to it being missing for a turn or cleared somewhere
if (!skipPartnerCheck && i > 1 && ((1u << BATTLE_PARTNER(i)) & hitmarkerFaintBits))
continue;
battler = i;
@ -8259,22 +8283,21 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
{
if (CanBePoisoned(gBattlerAttacker, battler, GetBattlerAbility(gBattlerAttacker), GetBattlerAbility(battler)))
{
u32 tspikes = 0;
if (gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount >= 2) {
tspikes = 1;
gBattleScripting.battler = battler;
BattleScriptPushCursor();
if (gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount >= 2)
{
gBattlescriptCurrInstr = BattleScript_ToxicSpikesBadlyPoisoned;
gBattleMons[battler].status1 |= STATUS1_TOXIC_POISON;
}
else
{
gBattlescriptCurrInstr = BattleScript_ToxicSpikesPoisoned;
gBattleMons[battler].status1 |= STATUS1_POISON;
}
BtlController_EmitSetMonData(battler, B_COMM_TO_CONTROLLER, REQUEST_STATUS_BATTLE, 0, sizeof(gBattleMons[battler].status1), &gBattleMons[battler].status1);
MarkBattlerForControllerExec(battler);
gBattleScripting.battler = battler;
BattleScriptPushCursor();
if (tspikes == 0)
gBattlescriptCurrInstr = BattleScript_ToxicSpikesPoisoned;
else
gBattlescriptCurrInstr = BattleScript_ToxicSpikesBadlyPoisoned;
}
}
}
@ -8493,7 +8516,7 @@ static void Cmd_returntoball(void)
MarkBattlerForControllerExec(battler);
// Don't always execute a form change here otherwise we can stomp gigantamax
if(!cmd->changingForm)
if (!cmd->changingForm)
TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH);
gBattlescriptCurrInstr = cmd->nextInstr;
@ -9118,7 +9141,7 @@ static bool32 TryCheekPouch(u32 battler, u32 itemId)
}
// When Cheek Pouch activates mid-battle it overwrites the current damage, so restore it
static void TryRestoreDamageAfterCheeckPouch(u32 battler)
static void TryRestoreDamageAfterCheekPouch(u32 battler)
{
if (gBattleStruct->cheekPouchActivated)
{
@ -9634,17 +9657,17 @@ static void RemoveAllWeather(void)
if (gBattleWeather & B_WEATHER_RAIN)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_RAIN;
else if(gBattleWeather & B_WEATHER_SANDSTORM)
else if (gBattleWeather & B_WEATHER_SANDSTORM)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_SANDSTORM;
else if(gBattleWeather & B_WEATHER_SUN)
else if (gBattleWeather & B_WEATHER_SUN)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_SUN;
else if(gBattleWeather & B_WEATHER_HAIL)
else if (gBattleWeather & B_WEATHER_HAIL)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_HAIL;
else if(gBattleWeather & B_WEATHER_STRONG_WINDS)
else if (gBattleWeather & B_WEATHER_STRONG_WINDS)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_STRONG_WINDS;
else if(gBattleWeather & B_WEATHER_SNOW)
else if (gBattleWeather & B_WEATHER_SNOW)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_SNOW;
else if(gBattleWeather & B_WEATHER_FOG)
else if (gBattleWeather & B_WEATHER_FOG)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_FOG;
else
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_WEATHER_END_COUNT; // failsafe
@ -16206,7 +16229,8 @@ static void Cmd_displaydexinfo(void)
}
break;
case 5:
if (!gPaletteFade.active) {
if (!gPaletteFade.active)
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
break;
@ -16518,6 +16542,9 @@ static void Cmd_tryworryseed(void)
}
else
{
if (gBattleMons[gBattlerTarget].ability == ABILITY_NEUTRALIZING_GAS)
gSpecialStatuses[gBattlerTarget].neutralizingGasRemoved = TRUE;
gBattleScripting.abilityPopupOverwrite = gBattleMons[gBattlerTarget].ability;
gBattleMons[gBattlerTarget].ability = gDisableStructs[gBattlerTarget].overwrittenAbility = ABILITY_INSOMNIA;
gBattlescriptCurrInstr = cmd->nextInstr;
@ -16874,7 +16901,7 @@ static void TryUpdateRoundTurnOrder(void)
}
}
// Get battlers after us using round
// Get battlers after attacker using round
for (i = currRounder; i < gBattlersCount; i++)
{
if (gChosenMoveByBattler[gBattlerByTurnOrder[i]] == MOVE_ROUND)
@ -17675,25 +17702,34 @@ void BS_AllySwitchSwapBattler(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_AllySwitchFailChance(void)
void BS_TryAllySwitch(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (B_ALLY_SWITCH_FAIL_CHANCE >= GEN_9)
if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))
|| (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
|| (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (B_ALLY_SWITCH_FAIL_CHANCE >= GEN_9)
{
TryResetProtectUseCounter(gBattlerAttacker);
if (sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] < Random())
{
gDisableStructs[gBattlerAttacker].protectUses = 0;
gBattlescriptCurrInstr = cmd->failInstr;
return;
}
else
{
gDisableStructs[gBattlerAttacker].protectUses++;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
gBattlescriptCurrInstr = cmd->nextInstr;
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_RunStatChangeItems(void)
@ -17981,7 +18017,7 @@ void BS_TryTarShot(void)
void BS_CanTarShotWork(void)
{
NATIVE_ARGS(const u8 *failInstr);
// Tar Shot will fail if it's already been used on the target and its speed can't be lowered further
// Tar Shot will fail if it's already been used on the target or if its speed can't be lowered further
if (!gDisableStructs[gBattlerTarget].tarShot
&& CompareStat(gBattlerTarget, STAT_SPEED, MAX_STAT_STAGE, CMP_LESS_THAN))
gBattlescriptCurrInstr = cmd->nextInstr;
@ -18634,7 +18670,7 @@ void BS_JumpIfIntimidateAbilityPrevented(void)
}
}
void BS_TryIntimidatEjectpack(void)
void BS_TryIntimidateEjectPack(void)
{
NATIVE_ARGS();
@ -18709,3 +18745,14 @@ void BS_JumpIfNoWhiteOut(void)
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_JumpIfAbilityCantBeSuppressed(void)
{
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (gAbilitiesInfo[gBattleMons[battler].ability].cantBeSuppressed)
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -545,6 +545,16 @@ void HandleAction_UseMove(void)
void HandleAction_Switch(void)
{
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
// if switching to a mon that is already on field, cancel switch
if (!(gAbsentBattlerFlags & (1u << BATTLE_PARTNER(gBattlerAttacker)))
&& IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))
&& gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)] == gBattleStruct->monToSwitchIntoId[gBattlerAttacker])
{
gCurrentActionFuncId = B_ACTION_FINISHED;
return;
}
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
gActionSelectionCursor[gBattlerAttacker] = 0;
@ -851,7 +861,7 @@ void HandleAction_NothingIsFainted(void)
void HandleAction_ActionFinished(void)
{
u32 i, j, moveType;
u32 i, j;
bool32 afterYouActive = gSpecialStatuses[gBattlerByTurnOrder[gCurrentTurnActionNumber + 1]].afterYou;
gBattleStruct->monToSwitchIntoId[gBattlerByTurnOrder[gCurrentTurnActionNumber]] = gSelectedMonPartyId = PARTY_SIZE;
gCurrentTurnActionNumber++;
@ -862,16 +872,6 @@ void HandleAction_ActionFinished(void)
| HITMARKER_OBEYS | HITMARKER_SYNCHRONIZE_EFFECT
| HITMARKER_CHARGING | HITMARKER_IGNORE_DISGUISE);
// check if Stellar type boost should be used up
moveType = GetBattleMoveType(gCurrentMove);
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA
&& GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR
&& GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS
&& IsTypeStellarBoosted(gBattlerAttacker, moveType))
{
ExpendTypeStellarBoost(gBattlerAttacker, moveType);
}
ClearDamageCalcResults();
gCurrentMove = 0;
gBattleScripting.animTurn = 0;
@ -902,7 +902,7 @@ void HandleAction_ActionFinished(void)
// We recalculate order only for action of the same priority. If any action other than switch/move has been taken, they should
// have been executed before. The only recalculation needed is for moves/switch. Mega evolution is handled in src/battle_main.c/TryChangeOrder
if((gActionsByTurnOrder[i] == B_ACTION_USE_MOVE && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE))
if ((gActionsByTurnOrder[i] == B_ACTION_USE_MOVE && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE))
{
if (GetWhichBattlerFaster(battler1, battler2, FALSE) == -1)
SwapTurnOrder(i, j);
@ -988,29 +988,29 @@ static void UNUSED MarkAllBattlersForControllerExec(void)
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
for (i = 0; i < gBattlersCount; i++)
gBattleControllerExecFlags |= 1u << (i + 32 - MAX_BATTLERS_COUNT);
MarkBattleControllerMessageOutboundOverLink(i);
}
else
{
for (i = 0; i < gBattlersCount; i++)
gBattleControllerExecFlags |= 1u << i;
MarkBattleControllerActiveOnLocal(i);
}
}
bool32 IsBattlerMarkedForControllerExec(u32 battler)
{
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
return (gBattleControllerExecFlags & (1u << (battler + 28))) != 0;
return IsBattleControllerMessageSynchronizedOverLink(battler);
else
return (gBattleControllerExecFlags & (1u << battler)) != 0;
return IsBattleControllerActiveOnLocal(battler);
}
void MarkBattlerForControllerExec(u32 battler)
{
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
gBattleControllerExecFlags |= 1u << (battler + 32 - MAX_BATTLERS_COUNT);
MarkBattleControllerMessageOutboundOverLink(battler);
else
gBattleControllerExecFlags |= 1u << battler;
MarkBattleControllerActiveOnLocal(battler);
}
void MarkBattlerReceivedLinkData(u32 battler)
@ -1018,9 +1018,9 @@ void MarkBattlerReceivedLinkData(u32 battler)
s32 i;
for (i = 0; i < GetLinkPlayerCount(); i++)
gBattleControllerExecFlags |= 1u << (battler + (i << 2));
MarkBattleControllerActiveForPlayer(battler, i);
gBattleControllerExecFlags &= ~(1u << (28 + battler));
MarkBattleControllerMessageSynchronizedOverLink(battler);
}
const u8 *CheckSkyDropState(u32 battler, enum SkyDropState skyDropState)
@ -3505,7 +3505,11 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
u32 diagonalBattler = BATTLE_OPPOSITE(battler);
if (IsDoubleBattle())
diagonalBattler = BATTLE_PARTNER(diagonalBattler);
if (IsBattlerAlive(diagonalBattler)
// Imposter only activates when the battler first switches in
if (gDisableStructs[battler].isFirstTurn == 2
&& !gDisableStructs[battler].overwrittenAbility
&& IsBattlerAlive(diagonalBattler)
&& !(gBattleMons[diagonalBattler].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE))
&& !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
&& gBattleStruct->illusion[diagonalBattler].state != ILLUSION_ON
@ -4320,7 +4324,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITY_HUNGER_SWITCH:
if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_TURN_END))
if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
&& GetActiveGimmick(battler) != GIMMICK_TERA
&& TryBattleFormChange(battler, FORM_CHANGE_BATTLE_TURN_END))
{
gBattlerAttacker = battler;
BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3NoPopup);
@ -6403,16 +6409,13 @@ static u8 ItemEffectMoveEnd(u32 battler, enum ItemHoldEffect holdEffect)
effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPDEF, ITEMEFFECT_NONE);
break;
case HOLD_EFFECT_ENIGMA_BERRY: // consume and heal if hit by super effective move
if (B_BERRIES_INSTANT >= GEN_4)
effect = TrySetEnigmaBerry(battler);
effect = TrySetEnigmaBerry(battler);
break;
case HOLD_EFFECT_KEE_BERRY: // consume and boost defense if used physical move
if (B_BERRIES_INSTANT >= GEN_4)
effect = DamagedStatBoostBerryEffect(battler, STAT_DEF, DAMAGE_CATEGORY_PHYSICAL);
effect = DamagedStatBoostBerryEffect(battler, STAT_DEF, DAMAGE_CATEGORY_PHYSICAL);
break;
case HOLD_EFFECT_MARANGA_BERRY: // consume and boost sp. defense if used special move
if (B_BERRIES_INSTANT >= GEN_4)
effect = DamagedStatBoostBerryEffect(battler, STAT_SPDEF, DAMAGE_CATEGORY_SPECIAL);
effect = DamagedStatBoostBerryEffect(battler, STAT_SPDEF, DAMAGE_CATEGORY_SPECIAL);
break;
case HOLD_EFFECT_RANDOM_STAT_UP:
if (B_BERRIES_INSTANT >= GEN_4)

View File

@ -2047,15 +2047,9 @@ static u8 GetNumStagesWateredByBerryTreeId(u8 id)
return BerryTreeGetNumStagesWatered(GetBerryTreeInfo(id));
}
// Berries can be watered at 4 stages of growth. This function is likely meant
// to divide the berry yield range equally into quartiles. If you watered the
// tree n times, your yield is a random number in the nth quartile.
//
// However, this function actually skews towards higher berry yields, because
// it rounds `extraYield` to the nearest whole number.
//
// See resulting yields: https://gist.github.com/hondew/2a099dbe54aa91414decdbfaa524327d,
// and bug fix: https://gist.github.com/hondew/0f0164e5b9dadfd72d24f30f2c049a0b.
// Berries can be watered at 4 stages of growth. The distribution is largely
// even but slightly prefers middle berry yields, since it uniformly draws from
// a subset of the total yield range.
static u8 CalcBerryYieldInternal(u16 max, u16 min, u8 water)
{
u32 randMin;

View File

@ -1243,7 +1243,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_MIRACLE_EYE] =
{
.battleScript = BattleScript_EffectMircleEye,
.battleScript = BattleScript_EffectMiracleEye,
.battleTvScore = 0, // TODO: Assign points
.encourageEncore = TRUE,
},
@ -1966,6 +1966,12 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.battleTvScore = 0, // TODO: Assign points
},
[EFFECT_CHLOROBLAST] =
{
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
[EFFECT_EXTREME_EVOBOOST] =
{
.battleScript = BattleScript_EffectExtremeEvoboost,

View File

@ -19172,7 +19172,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.description = COMPOUND_STRING(
"将叶绿素凝聚后释放攻击。\n"
"自己也会受到伤害。"),
.effect = EFFECT_MAX_HP_50_RECOIL,
.effect = EFFECT_CHLOROBLAST,
.power = B_UPDATED_MOVE_DATA >= GEN_9 ? 150 : 120,
.type = TYPE_GRASS,
.accuracy = 95,

View File

@ -528,6 +528,7 @@ void DoDiveWarp(void)
TryFadeOutOldMapMusic();
WarpFadeOutScreen();
PlayRainStoppingSoundEffect();
SetFollowerNPCData(FNPC_DATA_COME_OUT_DOOR, FNPC_DOOR_NONE);
gFieldCallback = FieldCB_DefaultWarpExit;
CreateTask(Task_WarpAndLoadMap, 10);
}

View File

@ -1172,7 +1172,7 @@ void CreateFollowerNPCAvatar(void)
void FollowerNPC_HandleSprite(void)
{
if (CheckFollowerNPCFlag(FOLLOWER_NPC_FLAG_CAN_BIKE))
if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_BIKE) && CheckFollowerNPCFlag(FOLLOWER_NPC_FLAG_CAN_BIKE))
{
if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_MACH_BIKE)
SetFollowerNPCSprite(FOLLOWER_NPC_SPRITE_INDEX_MACH_BIKE);
@ -1183,7 +1183,7 @@ void FollowerNPC_HandleSprite(void)
{
TryUpdateFollowerNPCSpriteUnderwater();
}
else
else if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_ON_FOOT)
{
SetFollowerNPCSprite(FOLLOWER_NPC_SPRITE_INDEX_NORMAL);
}

View File

@ -1015,3 +1015,8 @@ u32 GetItemStatus2Mask(u16 itemId)
else
return 0;
}
u32 GetItemSellPrice(u32 itemId)
{
return GetItemPrice(itemId) / ITEM_SELL_FACTOR;
}

View File

@ -1458,7 +1458,7 @@ static void StartItemSwap(u8 taskId)
{
s16 *data = gTasks[taskId].data;
ListMenuSetUnkIndicatorsStructField(tListTaskId, 16, 1);
ListMenuSetTemplateField(tListTaskId, LISTFIELD_CURSORKIND, CURSOR_INVISIBLE);
tListPosition = gBagPosition.scrollPosition[gBagPosition.pocket] + gBagPosition.cursorPosition[gBagPosition.pocket];
gBagMenu->toSwapPos = tListPosition;
CopyItemName(BagGetItemIdByPocketPosition(gBagPosition.pocket + 1, tListPosition), gStringVar1);
@ -2122,6 +2122,11 @@ static void Task_ItemContext_Sell(u8 taskId)
}
else
{
u32 maxQuantity = MAX_MONEY / GetItemSellPrice(gSpecialVar_ItemId);
if (tQuantity > maxQuantity)
tQuantity = maxQuantity;
CopyItemName(gSpecialVar_ItemId, gStringVar2);
StringExpandPlaceholders(gStringVar4, gText_HowManyToSell);
DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, InitSellHowManyInput);
@ -2129,13 +2134,11 @@ static void Task_ItemContext_Sell(u8 taskId)
}
}
#define ITEM_SELL_FACTOR ((I_SELL_VALUE_FRACTION >= GEN_9) ? 4 : 2)
static void DisplaySellItemPriceAndConfirm(u8 taskId)
{
s16 *data = gTasks[taskId].data;
ConvertIntToDecimalStringN(gStringVar1, (GetItemPrice(gSpecialVar_ItemId) / ITEM_SELL_FACTOR) * tItemCount, STR_CONV_MODE_LEFT_ALIGN, MAX_MONEY_DIGITS);
ConvertIntToDecimalStringN(gStringVar1, GetItemSellPrice(gSpecialVar_ItemId) * tItemCount, STR_CONV_MODE_LEFT_ALIGN, MAX_MONEY_DIGITS);
StringExpandPlaceholders(gStringVar4, gText_ICanPayVar1);
DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, AskSellItems);
}
@ -2160,7 +2163,7 @@ static void InitSellHowManyInput(u8 taskId)
s16 *data = gTasks[taskId].data;
u8 windowId = BagMenu_AddWindow(ITEMWIN_QUANTITY_WIDE);
PrintItemSoldAmount(windowId, 1, (GetItemPrice(gSpecialVar_ItemId) / ITEM_SELL_FACTOR) * tItemCount);
PrintItemSoldAmount(windowId, 1, GetItemSellPrice(gSpecialVar_ItemId) * tItemCount);
DisplayCurrentMoneyWindow();
gTasks[taskId].func = Task_ChooseHowManyToSell;
}
@ -2171,7 +2174,7 @@ static void Task_ChooseHowManyToSell(u8 taskId)
if (AdjustQuantityAccordingToDPadInput(&tItemCount, tQuantity) == TRUE)
{
PrintItemSoldAmount(gBagMenu->windowIds[ITEMWIN_QUANTITY_WIDE], tItemCount, (GetItemPrice(gSpecialVar_ItemId) / ITEM_SELL_FACTOR) * tItemCount);
PrintItemSoldAmount(gBagMenu->windowIds[ITEMWIN_QUANTITY_WIDE], tItemCount, GetItemSellPrice(gSpecialVar_ItemId) * tItemCount);
}
else if (JOY_NEW(A_BUTTON))
{
@ -2195,7 +2198,7 @@ static void ConfirmSell(u8 taskId)
s16 *data = gTasks[taskId].data;
CopyItemName(gSpecialVar_ItemId, gStringVar2);
ConvertIntToDecimalStringN(gStringVar1, (GetItemPrice(gSpecialVar_ItemId) / ITEM_SELL_FACTOR) * tItemCount, STR_CONV_MODE_LEFT_ALIGN, MAX_MONEY_DIGITS);
ConvertIntToDecimalStringN(gStringVar1, GetItemSellPrice(gSpecialVar_ItemId) * tItemCount, STR_CONV_MODE_LEFT_ALIGN, MAX_MONEY_DIGITS);
StringExpandPlaceholders(gStringVar4, gText_TurnedOverVar1ForVar2);
DisplayItemMessage(taskId, FONT_NORMAL, gStringVar4, SellItem);
}
@ -2208,7 +2211,7 @@ static void SellItem(u8 taskId)
PlaySE(SE_SHOP);
RemoveBagItem(gSpecialVar_ItemId, tItemCount);
AddMoney(&gSaveBlock1Ptr->money, (GetItemPrice(gSpecialVar_ItemId) / ITEM_SELL_FACTOR) * tItemCount);
AddMoney(&gSaveBlock1Ptr->money, GetItemSellPrice(gSpecialVar_ItemId) * tItemCount);
DestroyListMenuTask(tListTaskId, scrollPos, cursorPos);
UpdatePocketItemList(gBagPosition.pocket);
UpdatePocketListPosition(gBagPosition.pocket);

View File

@ -17,29 +17,6 @@
// This allows them to have idle animations. Cursors prior to this are simply printed text.
#define CURSOR_OBJECT_START CURSOR_RED_OUTLINE
struct UnkIndicatorsStruct
{
u8 field_0;
u16 *field_4;
u16 field_8;
u16 field_A;
u16 field_C;
u16 field_E;
u8 field_10;
u8 field_11;
u8 field_12;
u8 field_13;
u8 field_14_0:4;
u8 field_14_1:4;
u8 field_15_0:4;
u8 field_15_1:4;
u8 field_16_0:3;
u8 field_16_1:3;
u8 field_16_2:2;
u8 field_17_0:6;
u8 field_17_1:2;
};
struct ScrollIndicatorPair
{
u8 field_0;
@ -916,104 +893,104 @@ void ListMenuDefaultCursorMoveFunc(s32 itemIndex, bool8 onInit, struct ListMenu
}
// unused
s32 ListMenuGetUnkIndicatorsStructFields(u8 taskId, u8 field)
s32 ListMenuGetTemplateField(u8 taskId, u8 field)
{
struct UnkIndicatorsStruct *data = (void *) gTasks[taskId].data;
struct ListMenu *data = (void *) gTasks[taskId].data;
switch (field)
{
case 0:
case 1:
return (s32)(data->field_4);
case 2:
return data->field_C;
case 3:
return data->field_E;
case 4:
return data->field_10;
case 5:
return data->field_11;
case 6:
return data->field_12;
case 7:
return data->field_13;
case 8:
return data->field_14_0;
case 9:
return data->field_14_1;
case 10:
return data->field_15_0;
case 11:
return data->field_15_1;
case 12:
return data->field_16_0;
case 13:
return data->field_16_1;
case 14:
return data->field_16_2;
case 15:
return data->field_17_0;
case 16:
return data->field_17_1;
case LISTFIELD_MOVECURSORFUNC:
case LISTFIELD_MOVECURSORFUNC2:
return (s32)(data->template.moveCursorFunc);
case LISTFIELD_TOTALITEMS:
return data->template.totalItems;
case LISTFIELD_MAXSHOWED:
return data->template.maxShowed;
case LISTFIELD_WINDOWID:
return data->template.windowId;
case LISTFIELD_HEADERX:
return data->template.header_X;
case LISTFIELD_ITEMX:
return data->template.item_X;
case LISTFIELD_CURSORX:
return data->template.cursor_X;
case LISTFIELD_UPTEXTY:
return data->template.upText_Y;
case LISTFIELD_CURSORPAL:
return data->template.cursorPal;
case LISTFIELD_FILLVALUE:
return data->template.fillValue;
case LISTFIELD_CURSORSHADOWPAL:
return data->template.cursorShadowPal;
case LISTFIELD_LETTERSPACING:
return data->template.lettersSpacing;
case LISTFIELD_ITEMVERTICALPADDING:
return data->template.itemVerticalPadding;
case LISTFIELD_SCROLLMULTIPLE:
return data->template.scrollMultiple;
case LISTFIELD_FONTID:
return data->template.fontId;
case LISTFIELD_CURSORKIND:
return data->template.cursorKind;
default:
return -1;
}
}
void ListMenuSetUnkIndicatorsStructField(u8 taskId, u8 field, s32 value)
void ListMenuSetTemplateField(u8 taskId, u8 field, s32 value)
{
struct UnkIndicatorsStruct *data = (void *) &gTasks[taskId].data;
struct ListMenu *data = (void *) &gTasks[taskId].data;
switch (field)
{
case 0:
case 1:
data->field_4 = (void *)(value);
case LISTFIELD_MOVECURSORFUNC:
case LISTFIELD_MOVECURSORFUNC2:
data->template.moveCursorFunc = (void *)value;
break;
case 2:
data->field_C = value;
case LISTFIELD_TOTALITEMS:
data->template.totalItems = value;
break;
case 3:
data->field_E = value;
case LISTFIELD_MAXSHOWED:
data->template.maxShowed = value;
break;
case 4:
data->field_10 = value;
case LISTFIELD_WINDOWID:
data->template.windowId = value;
break;
case 5:
data->field_11 = value;
case LISTFIELD_HEADERX:
data->template.header_X = value;
break;
case 6:
data->field_12 = value;
case LISTFIELD_ITEMX:
data->template.item_X = value;
break;
case 7:
data->field_13 = value;
case LISTFIELD_CURSORX:
data->template.cursor_X = value;
break;
case 8:
data->field_14_0 = value;
case LISTFIELD_UPTEXTY:
data->template.upText_Y = value;
break;
case 9:
data->field_14_1 = value;
case LISTFIELD_CURSORPAL:
data->template.cursorPal = value;
break;
case 10:
data->field_15_0 = value;
case LISTFIELD_FILLVALUE:
data->template.fillValue = value;
break;
case 11:
data->field_15_1 = value;
case LISTFIELD_CURSORSHADOWPAL:
data->template.cursorShadowPal = value;
break;
case 12:
data->field_16_0 = value;
case LISTFIELD_LETTERSPACING:
data->template.lettersSpacing = value;
break;
case 13:
data->field_16_1 = value;
case LISTFIELD_ITEMVERTICALPADDING:
data->template.itemVerticalPadding = value;
break;
case 14:
data->field_16_2 = value;
case LISTFIELD_SCROLLMULTIPLE:
data->template.scrollMultiple = value;
break;
case 15:
data->field_17_0 = value;
case LISTFIELD_FONTID:
data->template.fontId = value;
break;
case 16:
data->field_17_1 = value;
case LISTFIELD_CURSORKIND:
data->template.cursorKind = value;
break;
}
}

View File

@ -181,7 +181,7 @@ static EWRAM_DATA struct {
u16 listOffset;
u16 listRow;
bool8 showContestInfo;
} sMoveRelearnerMenuSate = {0};
} sMoveRelearnerMenuState = {0};
EWRAM_DATA u8 gOriginSummaryScreenPage = 0; // indicates summary screen page that the move relearner was opened from (if opened from PSS)
@ -404,9 +404,9 @@ void CB2_InitLearnMove(void)
InitMoveRelearnerBackgroundLayers();
InitMoveRelearnerWindows(gOriginSummaryScreenPage == PSS_PAGE_CONTEST_MOVES);
sMoveRelearnerMenuSate.listOffset = 0;
sMoveRelearnerMenuSate.listRow = 0;
sMoveRelearnerMenuSate.showContestInfo = gOriginSummaryScreenPage == PSS_PAGE_CONTEST_MOVES;
sMoveRelearnerMenuState.listOffset = 0;
sMoveRelearnerMenuState.listRow = 0;
sMoveRelearnerMenuState.showContestInfo = gOriginSummaryScreenPage == PSS_PAGE_CONTEST_MOVES;
CreateLearnableMovesList();
@ -414,7 +414,7 @@ void CB2_InitLearnMove(void)
LoadSpritePalette(&sMoveRelearnerPalette);
CreateUISprites();
sMoveRelearnerStruct->moveListMenuTask = ListMenuInit(&gMultiuseListMenuTemplate, sMoveRelearnerMenuSate.listOffset, sMoveRelearnerMenuSate.listRow);
sMoveRelearnerStruct->moveListMenuTask = ListMenuInit(&gMultiuseListMenuTemplate, sMoveRelearnerMenuState.listOffset, sMoveRelearnerMenuState.listRow);
SetBackdropFromColor(RGB_BLACK);
SetMainCallback2(CB2_MoveRelearnerMain);
}
@ -432,14 +432,14 @@ static void CB2_InitLearnMoveReturnFromSelectMove(void)
SetVBlankCallback(VBlankCB_MoveRelearner);
InitMoveRelearnerBackgroundLayers();
InitMoveRelearnerWindows(sMoveRelearnerMenuSate.showContestInfo);
InitMoveRelearnerWindows(sMoveRelearnerMenuState.showContestInfo);
CreateLearnableMovesList();
LoadSpriteSheet(&sMoveRelearnerSpriteSheet);
LoadSpritePalette(&sMoveRelearnerPalette);
CreateUISprites();
sMoveRelearnerStruct->moveListMenuTask = ListMenuInit(&gMultiuseListMenuTemplate, sMoveRelearnerMenuSate.listOffset, sMoveRelearnerMenuSate.listRow);
sMoveRelearnerStruct->moveListMenuTask = ListMenuInit(&gMultiuseListMenuTemplate, sMoveRelearnerMenuState.listOffset, sMoveRelearnerMenuState.listRow);
SetBackdropFromColor(RGB_BLACK);
SetMainCallback2(CB2_MoveRelearnerMain);
}
@ -541,11 +541,11 @@ static void DoMoveRelearnerMain(void)
}
else if (selection == MENU_B_PRESSED || selection == 1)
{
if (sMoveRelearnerMenuSate.showContestInfo == FALSE)
if (sMoveRelearnerMenuState.showContestInfo == FALSE)
{
sMoveRelearnerStruct->state = MENU_STATE_SETUP_BATTLE_MODE;
}
else if (sMoveRelearnerMenuSate.showContestInfo == TRUE)
else if (sMoveRelearnerMenuState.showContestInfo == TRUE)
{
sMoveRelearnerStruct->state = MENU_STATE_SETUP_CONTEST_MODE;
}
@ -570,11 +570,11 @@ static void DoMoveRelearnerMain(void)
}
else if (selection == MENU_B_PRESSED || selection == 1)
{
if (sMoveRelearnerMenuSate.showContestInfo == FALSE)
if (sMoveRelearnerMenuState.showContestInfo == FALSE)
{
sMoveRelearnerStruct->state = MENU_STATE_SETUP_BATTLE_MODE;
}
else if (sMoveRelearnerMenuSate.showContestInfo == TRUE)
else if (sMoveRelearnerMenuState.showContestInfo == TRUE)
{
sMoveRelearnerStruct->state = MENU_STATE_SETUP_CONTEST_MODE;
}
@ -630,11 +630,11 @@ static void DoMoveRelearnerMain(void)
else if (selection == MENU_B_PRESSED || selection == 1)
{
// What's the point? It gets set to MENU_STATE_PRINT_TRYING_TO_LEARN_PROMPT, anyway.
if (sMoveRelearnerMenuSate.showContestInfo == FALSE)
if (sMoveRelearnerMenuState.showContestInfo == FALSE)
{
sMoveRelearnerStruct->state = MENU_STATE_SETUP_BATTLE_MODE;
}
else if (sMoveRelearnerMenuSate.showContestInfo == TRUE)
else if (sMoveRelearnerMenuState.showContestInfo == TRUE)
{
sMoveRelearnerStruct->state = MENU_STATE_SETUP_CONTEST_MODE;
}
@ -646,11 +646,11 @@ static void DoMoveRelearnerMain(void)
if (!MoveRelearnerRunTextPrinters())
{
FillWindowPixelBuffer(RELEARNERWIN_MSG, 0x11);
if (sMoveRelearnerMenuSate.showContestInfo == FALSE)
if (sMoveRelearnerMenuState.showContestInfo == FALSE)
{
sMoveRelearnerStruct->state = MENU_STATE_SETUP_BATTLE_MODE;
}
else if (sMoveRelearnerMenuSate.showContestInfo == TRUE)
else if (sMoveRelearnerMenuState.showContestInfo == TRUE)
{
sMoveRelearnerStruct->state = MENU_STATE_SETUP_CONTEST_MODE;
}
@ -713,11 +713,11 @@ static void DoMoveRelearnerMain(void)
case MENU_STATE_FADE_FROM_SUMMARY_SCREEN:
BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK);
sMoveRelearnerStruct->state++;
if (sMoveRelearnerMenuSate.showContestInfo == FALSE)
if (sMoveRelearnerMenuState.showContestInfo == FALSE)
{
HideHeartSpritesAndShowTeachMoveText(TRUE);
}
else if (sMoveRelearnerMenuSate.showContestInfo == TRUE)
else if (sMoveRelearnerMenuState.showContestInfo == TRUE)
{
ShowTeachMoveText(TRUE);
}
@ -783,7 +783,7 @@ static void DoMoveRelearnerMain(void)
static void FreeMoveRelearnerResources(void)
{
RemoveScrollArrows();
DestroyListMenuTask(sMoveRelearnerStruct->moveListMenuTask, &sMoveRelearnerMenuSate.listOffset, &sMoveRelearnerMenuSate.listRow);
DestroyListMenuTask(sMoveRelearnerStruct->moveListMenuTask, &sMoveRelearnerMenuState.listOffset, &sMoveRelearnerMenuState.listRow);
FreeAllWindowBuffers();
FREE_AND_SET_NULL(sMoveRelearnerStruct);
ResetSpriteData();
@ -810,7 +810,7 @@ static void HideHeartSpritesAndShowTeachMoveText(bool8 onlyHideSprites)
static void HandleInput(bool8 showContest)
{
s32 itemId = ListMenu_ProcessInput(sMoveRelearnerStruct->moveListMenuTask);
ListMenuGetScrollAndRow(sMoveRelearnerStruct->moveListMenuTask, &sMoveRelearnerMenuSate.listOffset, &sMoveRelearnerMenuSate.listRow);
ListMenuGetScrollAndRow(sMoveRelearnerStruct->moveListMenuTask, &sMoveRelearnerMenuState.listOffset, &sMoveRelearnerMenuState.listRow);
switch (itemId)
{
@ -824,13 +824,13 @@ static void HandleInput(bool8 showContest)
{
PutWindowTilemap(RELEARNERWIN_DESC_CONTEST);
sMoveRelearnerStruct->state = MENU_STATE_SETUP_CONTEST_MODE;
sMoveRelearnerMenuSate.showContestInfo = TRUE;
sMoveRelearnerMenuState.showContestInfo = TRUE;
}
else
{
PutWindowTilemap(RELEARNERWIN_DESC_BATTLE);
sMoveRelearnerStruct->state = MENU_STATE_SETUP_BATTLE_MODE;
sMoveRelearnerMenuSate.showContestInfo = FALSE;
sMoveRelearnerMenuState.showContestInfo = FALSE;
}
ScheduleBgCopyTilemapToVram(1);
@ -859,7 +859,7 @@ static void HandleInput(bool8 showContest)
static s32 GetCurrentSelectedMove(void)
{
return sMoveRelearnerStruct->menuItems[sMoveRelearnerMenuSate.listRow + sMoveRelearnerMenuSate.listOffset].id;
return sMoveRelearnerStruct->menuItems[sMoveRelearnerMenuState.listRow + sMoveRelearnerMenuState.listOffset].id;
}
// Theory: This used to make the heart sprites visible again (i.e.
@ -915,7 +915,7 @@ static void AddScrollArrows(void)
{
gTempScrollArrowTemplate = sMoveListScrollArrowsTemplate;
gTempScrollArrowTemplate.fullyDownThreshold = sMoveRelearnerStruct->numMenuChoices - sMoveRelearnerStruct->numToShowAtOnce;
sMoveRelearnerStruct->moveListScrollArrowTask = AddScrollIndicatorArrowPair(&gTempScrollArrowTemplate, &sMoveRelearnerMenuSate.listOffset);
sMoveRelearnerStruct->moveListScrollArrowTask = AddScrollIndicatorArrowPair(&gTempScrollArrowTemplate, &sMoveRelearnerMenuState.listOffset);
}
}
@ -960,7 +960,7 @@ void MoveRelearnerShowHideHearts(s32 move)
u16 numHearts;
u16 i;
if (!sMoveRelearnerMenuSate.showContestInfo || move == LIST_CANCEL)
if (!sMoveRelearnerMenuState.showContestInfo || move == LIST_CANCEL)
{
for (i = 0; i < 16; i++)
gSprites[sMoveRelearnerStruct->heartSpriteIds[i]].invisible = TRUE;
@ -999,7 +999,7 @@ void MoveRelearnerShowHideHearts(s32 move)
void MoveRelearnerShowHideCategoryIcon(s32 moveId)
{
if (sMoveRelearnerMenuSate.showContestInfo || moveId == LIST_CANCEL)
if (sMoveRelearnerMenuState.showContestInfo || moveId == LIST_CANCEL)
{
if (sMoveRelearnerStruct->categoryIconSpriteId != 0xFF)
DestroySprite(&gSprites[sMoveRelearnerStruct->categoryIconSpriteId]);

View File

@ -1816,6 +1816,7 @@ void CB2_WhiteOut(void)
else
gFieldCallback = FieldCB_WarpExitFadeFromBlack;
state = 0;
SetFollowerNPCData(FNPC_DATA_SURF_BLOB, FNPC_SURF_BLOB_NONE);
DoMapLoadLoop(&state);
SetFieldVBlankCallback();
SetMainCallback1(CB1_Overworld);

View File

@ -828,7 +828,7 @@ void BlendPalettes(u32 selectedPalettes, u8 coeff, u32 color)
BlendPalettesFine(selectedPalettes, gPlttBufferUnfaded, gPlttBufferFaded, coeff, color);
}
#define DEFAULT_LIGHT_COLOR 0x3f9f
#define DEFAULT_LIGHT_COLOR RGB2GBA(248, 224, 120)
// Like BlendPalette, but ignores blendColor if the transparency high bit is set
// Optimization help by lucktyphlosion

View File

@ -1284,7 +1284,7 @@ static void ItemStorage_ExitItemList(u8 taskId)
static void ItemStorage_StartItemSwap(u8 taskId)
{
s16 *data = gTasks[taskId].data;
ListMenuSetUnkIndicatorsStructField(tListTaskId, 16, 1);
ListMenuSetTemplateField(tListTaskId, LISTFIELD_CURSORKIND, CURSOR_INVISIBLE);
sItemStorageMenu->toSwapPos = gPlayerPCItemPageInfo.itemsAbove + gPlayerPCItemPageInfo.cursorPos;
ItemStorage_SetSwapArrow(tListTaskId, 0, 0);
ItemStorage_UpdateSwapLinePos(sItemStorageMenu->toSwapPos);

View File

@ -99,7 +99,7 @@ struct GFRomHeader
// This seems to need to be in the text section for some reason.
// To avoid a changed section attributes warning it's put in a special .text.consts section.
__attribute__((section(".text.consts")))
static const struct GFRomHeader sGFRomHeader = {
USED static const struct GFRomHeader sGFRomHeader = {
.version = GAME_VERSION,
.language = GAME_LANGUAGE,
.gameName = "pokemon emerald version",

View File

@ -28,7 +28,7 @@ struct RHHRomHeader
};
__attribute__((section(".text.consts")))
static const struct RHHRomHeader sRHHRomHeader =
USED static const struct RHHRomHeader sRHHRomHeader =
{
.rhh_magic = { 'R', 'H', 'H', 'E', 'X', 'P' },
.expansionVersionMajor = EXPANSION_VERSION_MAJOR,

View File

@ -384,51 +384,11 @@ enum TimeOfDay GetTimeOfDayForEncounters(u32 headerId, enum WildPokemonArea area
if (!OW_TIME_OF_DAY_ENCOUNTERS)
return TIME_OF_DAY_DEFAULT;
if (InBattlePike())
if (InBattlePike() || InBattlePyramid())
{
switch (area)
{
default:
case WILD_AREA_LAND:
wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo;
break;
case WILD_AREA_WATER:
wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo;
break;
case WILD_AREA_ROCKS:
wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].rockSmashMonsInfo;
break;
case WILD_AREA_FISHING:
wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].fishingMonsInfo;
break;
case WILD_AREA_HIDDEN:
wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo;
break;
}
return OW_TIME_OF_DAY_FALLBACK;
}
else if (InBattlePyramid())
{
switch (area)
{
default:
case WILD_AREA_LAND:
wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo;
break;
case WILD_AREA_WATER:
wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo;
break;
case WILD_AREA_ROCKS:
wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].rockSmashMonsInfo;
break;
case WILD_AREA_FISHING:
wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].fishingMonsInfo;
break;
case WILD_AREA_HIDDEN:
wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo;
break;
}
}
else
else
{
switch (area)
{
@ -732,9 +692,9 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior)
if (prevMetatileBehavior != curMetatileBehavior && !AllowWildCheckOnNewMetatile())
return FALSE;
else if (WildEncounterCheck(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo->encounterRate, FALSE) != TRUE)
else if (WildEncounterCheck(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo->encounterRate, FALSE) != TRUE)
return FALSE;
else if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE) != TRUE)
else if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE) != TRUE)
return FALSE;
GenerateBattlePyramidWildMon();

View File

@ -22,3 +22,52 @@ SINGLE_BATTLE_TEST("Hunger Switch switches Morpeko's forms at the end of the tur
EXPECT_EQ(player->species, SPECIES_MORPEKO_FULL_BELLY);
}
}
SINGLE_BATTLE_TEST("Hunger Switch does not switch a mon transformed into Morpeko's form")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_TRANSFORM) == EFFECT_TRANSFORM);
PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_TRANSFORM); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
}
}
SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form when Terastallized")
{
GIVEN {
PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); TeraType(TYPE_NORMAL); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { }
TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_TERA); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
}
}
SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form after switching out while Terastallized")
{
KNOWN_FAILING; // #7062
GIVEN {
ASSUME(GetMoveEffect(MOVE_ROAR) == EFFECT_ROAR);
PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); TeraType(TYPE_NORMAL); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { }
TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_ROAR); }
TURN { SWITCH(player, 0); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
} THEN {
EXPECT_EQ(player->species, SPECIES_MORPEKO_HANGRY);
}
}

View File

@ -256,7 +256,8 @@ SINGLE_BATTLE_TEST("Neutralizing Gas prevents Trace from copying it")
SINGLE_BATTLE_TEST("Neutralizing Gas prevents Contrary inverting stat boosts")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_REST) == EFFECT_REST);
ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2);
ASSUME(GetMoveEffect(MOVE_LEER) == EFFECT_DEFENSE_DOWN);
PLAYER(SPECIES_INKAY) { Ability(ABILITY_CONTRARY); }
OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
} WHEN {
@ -271,3 +272,41 @@ SINGLE_BATTLE_TEST("Neutralizing Gas prevents Contrary inverting stat boosts")
EXPECT_LT(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Neutralizing Gas exiting the field does not activate abilities that were not suppressed by it again")
{
u32 species, ability;
// These are the only abilities that could immediately activate again
PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { species = SPECIES_CALYREX_SHADOW; ability = ABILITY_AS_ONE_SHADOW_RIDER; }
PARAMETRIZE { species = SPECIES_CALYREX_ICE; ability = ABILITY_AS_ONE_ICE_RIDER; }
GIVEN {
ASSUME(gAbilitiesInfo[ability].cantBeSuppressed);
PLAYER(species) { Ability(ability); }
OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { SWITCH(opponent, 1); }
} SCENE {
ABILITY_POPUP(player, ability);
MESSAGE("The effects of the neutralizing gas wore off!");
NOT ABILITY_POPUP(player, ability);
}
}
SINGLE_BATTLE_TEST("Neutralizing Gas exiting the field does not activate Imposter even if it did not activate before")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_DITTO) { Ability(ABILITY_IMPOSTER); }
OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { SWITCH(player, 1); SWITCH(opponent, 1); }
} SCENE {
NOT ABILITY_POPUP(player, ABILITY_IMPOSTER);
MESSAGE("The effects of the neutralizing gas wore off!");
NOT ABILITY_POPUP(player, ABILITY_IMPOSTER);
}
}

View File

@ -80,40 +80,52 @@ SINGLE_BATTLE_TEST("Normalize still makes Freeze-Dry do super effective damage t
}
}
SINGLE_BATTLE_TEST("Normalize boosts power of unaffected moves by 20% (Gen7+)", s16 damage)
SINGLE_BATTLE_TEST("Normalize doesn't boost power of unaffected moves by 20% (< Gen7)", s16 damage)
{
u32 ability, genConfig;
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_7; }
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_6; }
PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_7; }
PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_6; }
u32 ability;
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; }
PARAMETRIZE { ability = ABILITY_NORMALIZE; }
GIVEN {
WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, genConfig);
PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_TACKLE); }
WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, GEN_6);
PLAYER(SPECIES_DELCATTY) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
TURN { MOVE(player, MOVE_POUND); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
if (genConfig >= GEN_7)
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.2), results[2].damage); // Ate
else
EXPECT_EQ(results[1].damage, results[3].damage); // No boost
EXPECT_EQ(results[0].damage, results[1].damage); // No boost
}
}
SINGLE_BATTLE_TEST("Normalize boosts power of affected moves by 20% (Gen7+)", s16 damage)
SINGLE_BATTLE_TEST("Normalize boosts power of unaffected moves by 20% (Gen7+)", s16 damage)
{
u32 ability, genConfig;
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_7; }
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_6; }
PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_7; }
PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_6; }
u32 ability;
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; }
PARAMETRIZE { ability = ABILITY_NORMALIZE; }
GIVEN {
WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, genConfig);
WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, GEN_7);
PLAYER(SPECIES_DELCATTY) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_POUND); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, UQ_4_12(1.2), results[1].damage); // Ate
}
}
SINGLE_BATTLE_TEST("Normalize doesn't boost power of affected moves by 20% (< Gen7)", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; }
PARAMETRIZE { ability = ABILITY_NORMALIZE; }
GIVEN {
WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, GEN_6);
PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_WATER_GUN); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@ -121,10 +133,26 @@ SINGLE_BATTLE_TEST("Normalize boosts power of affected moves by 20% (Gen7+)", s1
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
if (genConfig >= GEN_7)
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.8), results[2].damage); // STAB + ate
else
EXPECT_MUL_EQ(results[1].damage, Q_4_12(1.5), results[3].damage); // STAB + no ate
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); // STAB + no ate
}
}
SINGLE_BATTLE_TEST("Normalize boosts power of affected moves by 20% (Gen7+)", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; }
PARAMETRIZE { ability = ABILITY_NORMALIZE; }
GIVEN {
WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, GEN_7);
PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_WATER_GUN); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_WATER_GUN); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.8), results[1].damage); // STAB + ate
}
}

View File

@ -2,3 +2,18 @@
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Wandering Spirit (Ability) test titles")
SINGLE_BATTLE_TEST("Wandering Spirit copied ability should not trigger on fainted mon")
{
GIVEN {
PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
PLAYER(SPECIES_WOBBUFFET)
OPPONENT(SPECIES_YAMASK_GALAR) { HP(1); Ability(ABILITY_WANDERING_SPIRIT); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_POISON_FANG); SEND_OUT(opponent, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_FANG, player);
NOT ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
}
}

View File

@ -1016,7 +1016,6 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if main attac
AI_SINGLE_BATTLE_TEST("Switch AI: AI will switch into mon with good type matchup and SE move if current mon has no SE move and no stats raised")
{
KNOWN_FAILING; // Either remove or replace the function
u32 odds = 0, species = SPECIES_NONE, move = MOVE_NONE;
PARAMETRIZE { odds = 33; species = SPECIES_SCIZOR; move = MOVE_X_SCISSOR; }
PARAMETRIZE { odds = 50; species = SPECIES_DUSCLOPS; move = MOVE_SHADOW_BALL; }

View File

@ -451,28 +451,6 @@ SINGLE_BATTLE_TEST("(TERA) Double Shock does not remove the user's Electric type
}
}
SINGLE_BATTLE_TEST("(TERA) Transform does not copy the target's Tera Type, and if the user is Terastallized it keeps its own Tera Type")
{
KNOWN_FAILING; // Transform seems to be bugged in tests.
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_SCRATCH, MOVE_EARTHQUAKE); TeraType(TYPE_GHOST); }
OPPONENT(SPECIES_DITTO) { TeraType(TYPE_FLYING); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_TRANSFORM); }
TURN { MOVE(player, MOVE_EARTHQUAKE); }
// TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SCRATCH, target: player, gimmick: GIMMICK_TERA); }
} SCENE {
// turn 2
MESSAGE("Wobbuffet used Earthquake!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, player);
HP_BAR(opponent);
// turn 3
MESSAGE("Wobbuffet used Scratch!");
MESSAGE("It doesn't affect Ditto…");
NOT { HP_BAR(opponent); }
}
}
// Stellar Type checks
SINGLE_BATTLE_TEST("(TERA) Stellar type does not change the user's defensive profile", s16 damage)
{

View File

@ -40,7 +40,7 @@ SINGLE_BATTLE_TEST("Jaboca Berry causes the attacker to lose 1/8 of its max HP i
}
}
SINGLE_BATTLE_TEST("Jaboca Berry tirggers before Bug Bite can steal it")
SINGLE_BATTLE_TEST("Jaboca Berry triggers before Bug Bite can steal it")
{
KNOWN_FAILING;
GIVEN {

View File

@ -1,7 +1,7 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Acupressure increases one of two stats by 2 stages at random");
TO_DO_BATTLE_TEST("Acupressure increases one of its stats by 2 stages at random");
TO_DO_BATTLE_TEST("Acupressure doesn't try to increase a stat that has been maximized");
TO_DO_BATTLE_TEST("Acupressure fails on the user if all of its stats are maximized");
TO_DO_BATTLE_TEST("Acupressure fails on the ally if all of its stats are maximized");

View File

@ -50,4 +50,40 @@ SINGLE_BATTLE_TEST("Aura Wheel changes type depending on Morpeko's form")
}
}
TO_DO_BATTLE_TEST("Aura Wheel can be used by Pokémon transformed into Morpeko");
SINGLE_BATTLE_TEST("Aura Wheel can be used by Pokémon transformed into Morpeko")
{
GIVEN {
PLAYER(SPECIES_MORPEKO) { Moves(MOVE_AURA_WHEEL, MOVE_CELEBRATE); Ability(ABILITY_HUNGER_SWITCH); }
OPPONENT(SPECIES_DITTO) { Moves(MOVE_AURA_WHEEL, MOVE_CELEBRATE); Ability(ABILITY_IMPOSTER); }
} WHEN {
TURN { MOVE(opponent, MOVE_AURA_WHEEL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_AURA_WHEEL, opponent);
}
}
SINGLE_BATTLE_TEST("Aura Wheel can be turned into a Normal-type move after Morpeko gains Normalize")
{
bool32 hangryMode;
PARAMETRIZE { hangryMode = FALSE; }
PARAMETRIZE { hangryMode = TRUE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_ENTRAINMENT) == EFFECT_ENTRAINMENT);
ASSUME(gSpeciesInfo[SPECIES_DUSKULL].types[0] == TYPE_GHOST || gSpeciesInfo[SPECIES_DUSKULL].types[1] == TYPE_GHOST);
PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); }
OPPONENT(SPECIES_DELCATTY) { Ability(ABILITY_NORMALIZE); }
OPPONENT(SPECIES_DUSKULL);
} WHEN {
if (hangryMode)
TURN { }
TURN { MOVE(opponent, MOVE_ENTRAINMENT); }
TURN { MOVE(player, MOVE_AURA_WHEEL); SWITCH(opponent, 1); }
} SCENE {
if (hangryMode)
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_AURA_WHEEL, player);
HP_BAR(opponent);
}
}
}

View File

@ -1,4 +0,0 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Blizzard ignores accuracy check durin Hail and Snow");

View File

@ -1,4 +1,22 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Bulk Up increases the user's Attack and Defense");
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_BULK_UP) == EFFECT_BULK_UP);
}
SINGLE_BATTLE_TEST("Bulk Up increases the user's Attack and Defense by 1 stage each")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BULK_UP); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BULK_UP, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
}
}

View File

@ -1,4 +1,22 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Calm Mind increases the user's Sp. Attack and Sp. Defense by 1 stage each");
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_CALM_MIND) == EFFECT_CALM_MIND);
}
SINGLE_BATTLE_TEST("Calm Mind increases the user's Sp. Attack and Sp. Defense by 1 stage each")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CALM_MIND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CALM_MIND, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1);
}
}

View File

@ -0,0 +1,153 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_CHLOROBLAST) == EFFECT_CHLOROBLAST);
}
SINGLE_BATTLE_TEST("Chloroblast makes the user lose 1/2 of its Max HP")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(400); MaxHP(400); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CHLOROBLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
HP_BAR(player, damage: 200);
NOT MESSAGE("Wobbuffet fainted!"); // Wobb had more than 1/2 of its HP, so it can't faint.
}
}
SINGLE_BATTLE_TEST("Chloroblast causes the user to faint when below 1/2 of its Max HP")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(200); MaxHP(400); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CHLOROBLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
HP_BAR(player, hp: 0);
MESSAGE("Wobbuffet fainted!");
}
}
SINGLE_BATTLE_TEST("Chloroblast causes the user & the target to faint when below 1/2 of its Max HP")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(200) ; MaxHP(400); }
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CHLOROBLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
HP_BAR(opponent, hp: 0);
MESSAGE("The opposing Wobbuffet fainted!");
HP_BAR(player, hp: 0);
MESSAGE("Wobbuffet fainted!");
}
}
SINGLE_BATTLE_TEST("Chloroblast hp loss is prevented by Magic Guard")
{
GIVEN {
PLAYER(SPECIES_CLEFAIRY) { Ability(ABILITY_MAGIC_GUARD); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CHLOROBLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
HP_BAR(opponent);
NOT HP_BAR(player);
}
}
SINGLE_BATTLE_TEST("Chloroblast does not cause recoil damage if the user has Rock Head")
{
GIVEN {
PLAYER(SPECIES_AERODACTYL) { Ability(ABILITY_ROCK_HEAD); }
OPPONENT(SPECIES_WOBBUFFET) { HP(400); MaxHP(400); }
} WHEN {
TURN { MOVE(player, MOVE_CHLOROBLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
HP_BAR(opponent);
NOT HP_BAR(player);
}
}
SINGLE_BATTLE_TEST("Chloroblast does not cause the user to lose HP even if the opposing mon protected")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_CHLOROBLAST); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
HP_BAR(player);
}
}
}
SINGLE_BATTLE_TEST("Chloroblast does not cause the user to lose HP even if it is absorbed by Sap Sipper")
{
GIVEN {
ASSUME(GetMoveType(MOVE_CHLOROBLAST) == TYPE_GRASS);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_GOGOAT) { Ability(ABILITY_SAP_SIPPER); }
} WHEN {
TURN { MOVE(player, MOVE_CHLOROBLAST); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_SAP_SIPPER);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
HP_BAR(player);
}
}
}
SINGLE_BATTLE_TEST("Chloroblast does not cause the user to lose HP if there is no target")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_MEMENTO); MOVE(player, MOVE_CHLOROBLAST); SEND_OUT(opponent, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MEMENTO, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
HP_BAR(player);
}
MESSAGE("Wobbuffet used Chloroblast!");
MESSAGE("But it failed!");
MESSAGE("2 sent out Wobbuffet!");
}
}
SINGLE_BATTLE_TEST("Chloroblast is not affected by Reckless", s16 damage)
{
u32 move;
PARAMETRIZE { move = MOVE_CHLOROBLAST; }
PARAMETRIZE { move = MOVE_FRENZY_PLANT; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_EQ(results[0].damage, results[1].damage);
}
}

View File

@ -1,4 +1,28 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Expanding Force's power increases by 50% if the user is affected by Psychic Terrain");
SINGLE_BATTLE_TEST("Expanding Force's power increases by 50% if the user is affected by Psychic Terrain", s16 damage)
{
bool32 terrain;
PARAMETRIZE { terrain = FALSE; }
PARAMETRIZE { terrain = TRUE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_EXPANDING_FORCE) == EFFECT_EXPANDING_FORCE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
if (terrain)
TURN { MOVE(player, MOVE_PSYCHIC_TERRAIN); }
TURN { MOVE(player, MOVE_EXPANDING_FORCE); }
} SCENE {
MESSAGE("Wobbuffet used Expanding Force!");
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
if (B_TERRAIN_TYPE_BOOST >= GEN_8)
// 1.3 Terrain boost x 1.5 effect boost = 1.95 boost
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.95), results[1].damage);
else
// 1.5 Terrain boost x 1.5 effect boost = 2.25 boost
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.25), results[1].damage);
}
}

View File

@ -673,4 +673,24 @@ SINGLE_BATTLE_TEST("Pursuit user faints to Life Orb and target still switches ou
}
}
DOUBLE_BATTLE_TEST("Pursuit user switches out due to Red Card and partner's switch is cancelled if switching to same Pokémon")
{
GIVEN {
ASSUME(GetItemHoldEffect(ITEM_RED_CARD) == HOLD_EFFECT_RED_CARD);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_ARCEUS);
OPPONENT(SPECIES_WYNAUT) { Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ARCEUS);
} WHEN {
TURN { SWITCH(opponentLeft, 2); SWITCH(playerRight, 2); MOVE(playerLeft, MOVE_PURSUIT, target: opponentLeft); }
} THEN {
// playerLeft switches to Arceus
EXPECT_EQ(playerLeft->species, SPECIES_ARCEUS);
// playerRight has their switch cancelled
EXPECT_EQ(playerRight->species, SPECIES_WYNAUT);
}
}
TO_DO_BATTLE_TEST("Baton Pass doesn't cause Pursuit to increase its power or priority");

View File

@ -2,3 +2,29 @@
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Transform (Move Effect) test titles")
SINGLE_BATTLE_TEST("(TERA) Transform does not copy the target's Tera Type, and if the user is Terastallized it keeps its own Tera Type")
{
u32 playerDoTera;
PARAMETRIZE { playerDoTera = GIMMICK_TERA; }
PARAMETRIZE { playerDoTera = GIMMICK_NONE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_SCRATCH, MOVE_EARTHQUAKE); TeraType(TYPE_GHOST); }
OPPONENT(SPECIES_DITTO) { TeraType(TYPE_FLYING); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: playerDoTera); MOVE(opponent, MOVE_TRANSFORM); }
TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, moveSlot: 0); }
TURN { MOVE(player, MOVE_EARTHQUAKE); MOVE(opponent, moveSlot: 0, gimmick: GIMMICK_TERA); }
} SCENE {
// turn 1
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, opponent);
// turn 2
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
// turn 3
MESSAGE("Wobbuffet used Earthquake!");
MESSAGE("It doesn't affect the opposing Ditto…");
NOT { HP_BAR(opponent); }
}
}

View File

@ -6,6 +6,7 @@ SINGLE_BATTLE_TEST("Blizzard bypasses accuracy checks in Hail and Snow")
u32 move;
PARAMETRIZE { move = MOVE_HAIL; }
PARAMETRIZE { move = MOVE_SNOWSCAPE; }
PASSES_RANDOMLY(100, 100, RNG_ACCURACY);
GIVEN {
ASSUME(GetMoveAccuracy(MOVE_BLIZZARD) == 70);
ASSUME(MoveAlwaysHitsInHailSnow(MOVE_BLIZZARD));

View File

@ -312,7 +312,9 @@ top:
const char *color;
const char *result;
if (gTestRunnerState.result == gTestRunnerState.expectedResult)
if (gTestRunnerState.result == gTestRunnerState.expectedResult
|| (gTestRunnerState.result == TEST_RESULT_FAIL
&& gTestRunnerState.expectedResult == TEST_RESULT_KNOWN_FAIL))
{
color = "\e[32m";
Test_MgbaPrintf(":N%s", gTestRunnerState.test->name);
@ -330,7 +332,7 @@ top:
switch (gTestRunnerState.result)
{
case TEST_RESULT_FAIL:
if (gTestRunnerState.expectedResult == TEST_RESULT_FAIL)
if (gTestRunnerState.expectedResult == TEST_RESULT_KNOWN_FAIL)
{
result = "KNOWN_FAILING";
color = "\e[33m";
@ -387,7 +389,9 @@ top:
Test_MgbaPrintf(":A%s%s\e[0m", color, result);
else if (gTestRunnerState.result == TEST_RESULT_TODO)
Test_MgbaPrintf(":T%s%s\e[0m", color, result);
else if (gTestRunnerState.expectedResult == gTestRunnerState.result)
else if (gTestRunnerState.expectedResult == gTestRunnerState.result
|| (gTestRunnerState.result == TEST_RESULT_FAIL
&& gTestRunnerState.expectedResult == TEST_RESULT_KNOWN_FAIL))
Test_MgbaPrintf(":K%s%s\e[0m", color, result);
else
Test_MgbaPrintf(":F%s%s\e[0m", color, result);

View File

@ -181,6 +181,7 @@ static void BattleTest_SetUp(void *data)
STATE->battlersCount = 4;
break;
}
STATE->hasTornDownBattle = FALSE;
}
static void PrintTestName(void)
@ -284,16 +285,17 @@ static void BattleTest_Run(void *data)
break;
}
for (i = 0; i < STATE->battlersCount; i++)
for (i = 0; i < MAX_LINK_PLAYERS; i++)
{
DATA.recordedBattle.playersName[i][0] = CHAR_1 + i;
DATA.recordedBattle.playersName[i][1] = EOS;
DATA.recordedBattle.playersLanguage[i] = GAME_LANGUAGE;
DATA.recordedBattle.playersBattlers[i] = i;
DATA.currentMonIndexes[i] = (i & BIT_FLANK) == B_FLANK_LEFT ? 0 : 1;
}
for (i = 0; i < STATE->battlersCount; i++)
DATA.currentMonIndexes[i] = i / 2;
STATE->runRandomly = TRUE;
STATE->runGiven = TRUE;
STATE->runWhen = TRUE;
@ -1419,8 +1421,11 @@ static void BattleTest_TearDown(void *data)
// aborted unexpectedly.
ClearFlagAfterTest();
TestFreeConfigData();
if (STATE->tearDownBattle)
if (!STATE->hasTornDownBattle)
{
TearDownBattle();
STATE->hasTornDownBattle = TRUE;
}
}
static bool32 BattleTest_CheckProgress(void *data)
@ -1448,7 +1453,6 @@ static bool32 BattleTest_HandleExitWithResult(void *data, enum TestResult result
}
else
{
STATE->tearDownBattle = TRUE;
return FALSE;
}
}