diff --git a/.all-contributorsrc b/.all-contributorsrc
index e5ecce9602..5b6d38e32f 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -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"
}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c3040f55c7..30231dbd0f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -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
+
diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml
index 85800211ef..325e72950d 100644
--- a/.github/workflows/labels.yml
+++ b/.github/workflows/labels.yml
@@ -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
+
diff --git a/CREDITS.md b/CREDITS.md
index 6045f2301a..128f26df6f 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -30,24 +30,34 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 Pawkkie 🚧 💻 📖 |
 Philipp AUER 🚧 💻 |
 tertu 🚧 💻 |
-  psf 🚧 💻 |
+  psf 🚧 💻 📆 |
+  wiz1989 💻 |
 PCG 💻 |
 kittenchilly 💻 🔬 🔣 |
-  ExpoSeed 💻 🚧 👀 |
+  ExpoSeed 💻 🚧 👀 |
 Linathan 💻 |
 Eduardo Quezada 💻 🔣 📖 🚇 🚧 📆 📣 🔬 👀 ⚠️ ✅ 📓 |
 khbsd 📖 💻 |
 Cafe 🎨 |
 agsmgmaster64 💻 |
-  mudskipper13 💻 📖 |
-  Ruby 💻 📖 |
+  Ruby 💻 📖 |
-  surskitty 💻 |
-  wiz1989 💻 |
+  mudskipper13 💻 📖 |
+  surskitty 💻 |
 grintoul 💻 |
+  bassforte123 💻 |
+  iriv24 💻 |
+  Bivurnum 💻 |
+  Emilia Daelman 💻 ⚠️ |
+
+
+  RavePossum 💻 |
+  Zatsu 💻 |
+  poetahto 💻 |
+  lordraindance2 💻 |
@@ -65,6 +75,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
+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)):
-
-
-
-
-
-
-
-
-This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
diff --git a/FEATURES.md b/FEATURES.md
index 142df9da3f..d2c913d67d 100644
--- a/FEATURES.md
+++ b/FEATURES.md
@@ -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)
diff --git a/INSTALL.md b/INSTALL.md
index a49a9d5c18..d2e511dace 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -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
diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc
index e671bc1649..b524104a36 100644
--- a/asm/macros/battle_script.inc
+++ b/asm/macros/battle_script.inc
@@ -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
diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s
index 49563e91e2..50a9b51dc7 100644
--- a/data/battle_anim_scripts.s
+++ b/data/battle_anim_scripts.s
@@ -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
diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s
index 9625033c81..a10dfaa40c 100644
--- a/data/battle_scripts_1.s
+++ b/data/battle_scripts_1.s
@@ -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
diff --git a/docs/STYLEGUIDE.md b/docs/STYLEGUIDE.md
index f53e1a2dcc..dc99be6b1b 100644
--- a/docs/STYLEGUIDE.md
+++ b/docs/STYLEGUIDE.md
@@ -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.
diff --git a/docs/tutorials/dns.md b/docs/tutorials/dns.md
index 6fb5e92c7e..350957f076 100644
--- a/docs/tutorials/dns.md
+++ b/docs/tutorials/dns.md
@@ -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émon Center sign, or `LIGHT_TYPE_POKE_MART_SIGN` over a Poké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`.
diff --git a/include/battle_controllers.h b/include/battle_controllers.h
index 8f2d78ed41..2ce28ca609 100644
--- a/include/battle_controllers.h
+++ b/include/battle_controllers.h
@@ -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.
diff --git a/include/battle_scripts.h b/include/battle_scripts.h
index 75d7495498..de3e876aa2 100644
--- a/include/battle_scripts.h
+++ b/include/battle_scripts.h
@@ -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[];
diff --git a/include/constants/battle_ai.h b/include/constants/battle_ai.h
index d7b70af874..2f97ab2234 100644
--- a/include/constants/battle_ai.h
+++ b/include/constants/battle_ai.h
@@ -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
diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h
index eb0756bd40..d081d3a163 100644
--- a/include/constants/battle_move_effects.h
+++ b/include/constants/battle_move_effects.h
@@ -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,
diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h
index 2565d36a54..0670e95645 100644
--- a/include/constants/form_change_types.h
+++ b/include/constants/form_change_types.h
@@ -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.
diff --git a/include/constants/item.h b/include/constants/item.h
index f06e8682da..587270e0e9 100644
--- a/include/constants/item.h
+++ b/include/constants/item.h
@@ -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
diff --git a/include/item.h b/include/item.h
index 519c3842c0..aeffae7742 100644
--- a/include/item.h
+++ b/include/item.h
@@ -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
diff --git a/include/list_menu.h b/include/list_menu.h
index 17587afa1d..184b9c416f 100644
--- a/include/list_menu.h
+++ b/include/list_menu.h
@@ -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);
diff --git a/include/test/battle.h b/include/test/battle.h
index 16b492ccad..431e0f98f9 100644
--- a/include/test/battle.h
+++ b/include/test/battle.h
@@ -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;
diff --git a/include/test/test.h b/include/test/test.h
index ccfc589c21..092b603ec0 100644
--- a/include/test/test.h
+++ b/include/test/test.h
@@ -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)
diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c
index 2805a10692..18c140c2d2 100644
--- a/src/battle_ai_main.c
+++ b/src/battle_ai_main.c
@@ -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:
diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c
index 60030acef5..c6a4b8708f 100644
--- a/src/battle_ai_switch_items.c
+++ b/src/battle_ai_switch_items.c
@@ -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;
diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c
index e71f11c70f..93801927bd 100644
--- a/src/battle_ai_util.c
+++ b/src/battle_ai_util.c
@@ -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;
diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c
index d7dbfa36b2..bc3b4a2c44 100644
--- a/src/battle_anim_effects_1.c
+++ b/src/battle_anim_effects_1.c
@@ -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);
diff --git a/src/battle_anim_effects_2.c b/src/battle_anim_effects_2.c
index e0583c7cf8..10080dc84b 100755
--- a/src/battle_anim_effects_2.c
+++ b/src/battle_anim_effects_2.c
@@ -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);
diff --git a/src/battle_anim_throw.c b/src/battle_anim_throw.c
index fe27b7138a..54f16003be 100644
--- a/src/battle_anim_throw.c
+++ b/src/battle_anim_throw.c
@@ -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)
diff --git a/src/battle_controller_link_opponent.c b/src/battle_controller_link_opponent.c
index c79588695d..a039a5b522 100644
--- a/src/battle_controller_link_opponent.c
+++ b/src/battle_controller_link_opponent.c
@@ -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);
}
}
diff --git a/src/battle_controller_link_partner.c b/src/battle_controller_link_partner.c
index f54ee0d220..9b27309bf6 100644
--- a/src/battle_controller_link_partner.c
+++ b/src/battle_controller_link_partner.c
@@ -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);
}
}
diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c
index 03c78fb9e9..f6fae5ae57 100644
--- a/src/battle_controller_opponent.c
+++ b/src/battle_controller_opponent.c
@@ -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);
}
}
diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c
index d0a74bd884..66ef28abb1 100644
--- a/src/battle_controller_player.c
+++ b/src/battle_controller_player.c
@@ -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);
diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c
index ad5aa15312..9929f421cc 100644
--- a/src/battle_controller_player_partner.c
+++ b/src/battle_controller_player_partner.c
@@ -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);
}
}
diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c
index 1e471044d5..944cf6e072 100644
--- a/src/battle_controller_recorded_opponent.c
+++ b/src/battle_controller_recorded_opponent.c
@@ -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);
}
}
diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c
index 2476929282..a3032db1a8 100644
--- a/src/battle_controller_recorded_player.c
+++ b/src/battle_controller_recorded_player.c
@@ -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);
}
}
diff --git a/src/battle_controller_safari.c b/src/battle_controller_safari.c
index 560a95cc5f..4a1fc3888c 100644
--- a/src/battle_controller_safari.c
+++ b/src/battle_controller_safari.c
@@ -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);
}
}
diff --git a/src/battle_controller_wally.c b/src/battle_controller_wally.c
index 3381e5a5bd..6cba0fc3e7 100644
--- a/src/battle_controller_wally.c
+++ b/src/battle_controller_wally.c
@@ -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);
}
}
diff --git a/src/battle_controllers.c b/src/battle_controllers.c
index 9b6a74fc6e..4faec49613 100644
--- a/src/battle_controllers.c
+++ b/src/battle_controllers.c
@@ -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;
}
diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c
index 7e9f8b15f5..6cf45ded4d 100644
--- a/src/battle_end_turn.c
+++ b/src/battle_end_turn.c
@@ -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);
diff --git a/src/battle_main.c b/src/battle_main.c
index 0f3528ed16..bdd0c33a54 100644
--- a/src/battle_main.c
+++ b/src/battle_main.c
@@ -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
diff --git a/src/battle_pyramid_bag.c b/src/battle_pyramid_bag.c
index 5d5c6c1f03..9cc8a05aa6 100644
--- a/src/battle_pyramid_bag.c
+++ b/src/battle_pyramid_bag.c
@@ -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));
diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c
index 88f3cfba54..bd2ef1509e 100644
--- a/src/battle_script_commands.c
+++ b/src/battle_script_commands.c
@@ -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;
+}
diff --git a/src/battle_util.c b/src/battle_util.c
index 6d256dc998..ba63bf4fe9 100644
--- a/src/battle_util.c
+++ b/src/battle_util.c
@@ -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)
diff --git a/src/berry.c b/src/berry.c
index d86b461d0f..27f75932bd 100644
--- a/src/berry.c
+++ b/src/berry.c
@@ -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;
diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h
index 06b1fb3fd6..dfca0c5232 100644
--- a/src/data/battle_move_effects.h
+++ b/src/data/battle_move_effects.h
@@ -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,
diff --git a/src/data/moves_info.h b/src/data/moves_info.h
index 91215a928d..f0951d2225 100644
--- a/src/data/moves_info.h
+++ b/src/data/moves_info.h
@@ -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,
diff --git a/src/field_screen_effect.c b/src/field_screen_effect.c
index 9ac99465ca..7b17f53666 100644
--- a/src/field_screen_effect.c
+++ b/src/field_screen_effect.c
@@ -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);
}
diff --git a/src/follower_npc.c b/src/follower_npc.c
index 40b08cb365..0a8cde49c8 100644
--- a/src/follower_npc.c
+++ b/src/follower_npc.c
@@ -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);
}
diff --git a/src/item.c b/src/item.c
index 064fc95f38..3fb72703a8 100644
--- a/src/item.c
+++ b/src/item.c
@@ -1015,3 +1015,8 @@ u32 GetItemStatus2Mask(u16 itemId)
else
return 0;
}
+
+u32 GetItemSellPrice(u32 itemId)
+{
+ return GetItemPrice(itemId) / ITEM_SELL_FACTOR;
+}
diff --git a/src/item_menu.c b/src/item_menu.c
index 251727d8f2..e8d7e44207 100755
--- a/src/item_menu.c
+++ b/src/item_menu.c
@@ -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);
diff --git a/src/list_menu.c b/src/list_menu.c
index 35300de955..7f78c2da40 100644
--- a/src/list_menu.c
+++ b/src/list_menu.c
@@ -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;
}
}
diff --git a/src/move_relearner.c b/src/move_relearner.c
index b48d31677a..7ac7a6bb11 100644
--- a/src/move_relearner.c
+++ b/src/move_relearner.c
@@ -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]);
diff --git a/src/overworld.c b/src/overworld.c
index d9a688c7b5..d827d46a2c 100644
--- a/src/overworld.c
+++ b/src/overworld.c
@@ -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);
diff --git a/src/palette.c b/src/palette.c
index 5c63a918ad..c4d65404d1 100644
--- a/src/palette.c
+++ b/src/palette.c
@@ -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
diff --git a/src/player_pc.c b/src/player_pc.c
index bac3f612c3..32c5dfcaf2 100644
--- a/src/player_pc.c
+++ b/src/player_pc.c
@@ -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);
diff --git a/src/rom_header_gf.c b/src/rom_header_gf.c
index 182d879c83..5613fe6152 100644
--- a/src/rom_header_gf.c
+++ b/src/rom_header_gf.c
@@ -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",
diff --git a/src/rom_header_rhh.c b/src/rom_header_rhh.c
index c323e9eadf..e9310415c3 100644
--- a/src/rom_header_rhh.c
+++ b/src/rom_header_rhh.c
@@ -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,
diff --git a/src/wild_encounter.c b/src/wild_encounter.c
index 37b6a43443..f77fe272d6 100644
--- a/src/wild_encounter.c
+++ b/src/wild_encounter.c
@@ -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();
diff --git a/test/battle/ability/hunger_switch.c b/test/battle/ability/hunger_switch.c
index 38c3d2527d..1892de4424 100644
--- a/test/battle/ability/hunger_switch.c
+++ b/test/battle/ability/hunger_switch.c
@@ -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);
+ }
+}
diff --git a/test/battle/ability/neutralizing_gas.c b/test/battle/ability/neutralizing_gas.c
index 6338bfbeca..f83e928c4a 100644
--- a/test/battle/ability/neutralizing_gas.c
+++ b/test/battle/ability/neutralizing_gas.c
@@ -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);
+ }
+}
diff --git a/test/battle/ability/normalize.c b/test/battle/ability/normalize.c
index 4858997421..2a449f74a5 100644
--- a/test/battle/ability/normalize.c
+++ b/test/battle/ability/normalize.c
@@ -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
}
}
diff --git a/test/battle/ability/wandering_spirit.c b/test/battle/ability/wandering_spirit.c
index d32fb7c7b2..9d8d7fad36 100644
--- a/test/battle/ability/wandering_spirit.c
+++ b/test/battle/ability/wandering_spirit.c
@@ -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);
+ }
+}
diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c
index 46c419f211..41a480135d 100644
--- a/test/battle/ai/ai_switching.c
+++ b/test/battle/ai/ai_switching.c
@@ -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; }
diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c
index 19f42fbc9d..7ce10a5942 100644
--- a/test/battle/gimmick/terastal.c
+++ b/test/battle/gimmick/terastal.c
@@ -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)
{
diff --git a/test/battle/hold_effect/jaboca_berry.c b/test/battle/hold_effect/jaboca_berry.c
index cdb5cbfacd..29658c7fe1 100644
--- a/test/battle/hold_effect/jaboca_berry.c
+++ b/test/battle/hold_effect/jaboca_berry.c
@@ -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 {
diff --git a/test/battle/move_effect/acupressure.c b/test/battle/move_effect/acupressure.c
index 02be60725d..6eaade94ae 100644
--- a/test/battle/move_effect/acupressure.c
+++ b/test/battle/move_effect/acupressure.c
@@ -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");
diff --git a/test/battle/move_effect/aura_wheel.c b/test/battle/move_effect/aura_wheel.c
index dfd31c878f..3e26176eee 100644
--- a/test/battle/move_effect/aura_wheel.c
+++ b/test/battle/move_effect/aura_wheel.c
@@ -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);
+ }
+ }
+}
diff --git a/test/battle/move_effect/blizzard.c b/test/battle/move_effect/blizzard.c
deleted file mode 100644
index 1e566f98fc..0000000000
--- a/test/battle/move_effect/blizzard.c
+++ /dev/null
@@ -1,4 +0,0 @@
-#include "global.h"
-#include "test/battle.h"
-
-TO_DO_BATTLE_TEST("Blizzard ignores accuracy check durin Hail and Snow");
diff --git a/test/battle/move_effect/bulk_up.c b/test/battle/move_effect/bulk_up.c
index 7f47d5d48e..be0536f4ca 100644
--- a/test/battle/move_effect/bulk_up.c
+++ b/test/battle/move_effect/bulk_up.c
@@ -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);
+ }
+}
diff --git a/test/battle/move_effect/calm_mind.c b/test/battle/move_effect/calm_mind.c
index 66c16361f4..3b2ba76113 100644
--- a/test/battle/move_effect/calm_mind.c
+++ b/test/battle/move_effect/calm_mind.c
@@ -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);
+ }
+}
diff --git a/test/battle/move_effect/chloroblast.c b/test/battle/move_effect/chloroblast.c
new file mode 100644
index 0000000000..6452736478
--- /dev/null
+++ b/test/battle/move_effect/chloroblast.c
@@ -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);
+ }
+}
diff --git a/test/battle/move_effect/expanding_force.c b/test/battle/move_effect/expanding_force.c
index 74b78fdd86..7b0284390b 100644
--- a/test/battle/move_effect/expanding_force.c
+++ b/test/battle/move_effect/expanding_force.c
@@ -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);
+ }
+}
diff --git a/test/battle/move_effect/pursuit.c b/test/battle/move_effect/pursuit.c
index 649b331eec..b18aa74b09 100644
--- a/test/battle/move_effect/pursuit.c
+++ b/test/battle/move_effect/pursuit.c
@@ -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");
diff --git a/test/battle/move_effect/transform.c b/test/battle/move_effect/transform.c
index fd6e5f5a94..bf2bd66143 100644
--- a/test/battle/move_effect/transform.c
+++ b/test/battle/move_effect/transform.c
@@ -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); }
+ }
+}
diff --git a/test/battle/move_flags/always_hits_in_hail_snow.c b/test/battle/move_flags/always_hits_in_hail_snow.c
index 5673c0df8a..788186e7b5 100644
--- a/test/battle/move_flags/always_hits_in_hail_snow.c
+++ b/test/battle/move_flags/always_hits_in_hail_snow.c
@@ -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));
diff --git a/test/test_runner.c b/test/test_runner.c
index 8ca5b488e3..a1460365e0 100644
--- a/test/test_runner.c
+++ b/test/test_runner.c
@@ -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);
diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c
index 981683b35a..7884993a1c 100644
--- a/test/test_runner_battle.c
+++ b/test/test_runner_battle.c
@@ -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;
}
}