pokeemerald-expansion

pokeemerald-expansion is a romhack base based off pret's pokeemerald decompilation project. It is NOT a playable romhack, but it has multiple features available to romhackers so that they can create their own games, so it's not meant to be played on its own.

Should I use this or vanilla pokeemerald for my hack?

The main advantage of using vanilla pokeemerald as a base is being able to link with other official GBA Pokémon games for battles and trading, pokeemerald-expansion can battle and trade with itself out of the box. If you don't mind losing full vanilla compatiblitity, we recommend using pokeemerald-expansion. Otherwise, use pret's pokeemerald. You'll still receive documentation improvements from pret, as we regurlarly incorporate pret's documentation changes.

Using pokeemerald-expansion

If you use pokeemerald-expansion in your hack, please add RHH (Rom Hacking Hideout) to your credits list. Optionally, you can list the version used, so it can help players know what features to expect. You can phrase it as the following:

Based off RHH's pokeemerald-expansion 1.10.1 https://github.com/rh-hideout/pokeemerald-expansion/

Important: DO NOT use GitHub's "Download Zip" option. Using this option will not download the commit history required to update your expansion version or merge other feature branches. Instead, please read this guide to learn how to fork the repository and clone locally from there.

Please follow the instructions in INSTALL.md to get pokeemerald-expansion set up on your machine.

If I already have a project based on regular pokeemerald, can I use pokeemerald-expansion?

Yes! Keep in mind that we keep up with pret's documentation of pokeemerald, which means that if your project a bit old, you might get merge conflicts that you need to solve manually.

  • If you haven't set up a remote, run the command git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion.
  • Once you have your remote set up, run the command git pull RHH master.

With this, you'll get the latest version of pokeemerald-expansion, plus a couple of bugfixes that haven't yet been released into the next patch version :)

Documentation

Please click here to visit our documentation page.

How do I update my version of pokeemerald-expansion?

  • If you haven't set up a remote, run the command git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion.
  • Check your current version.
    • You can check in the debug menu's Utilities -> Expansion Version option.
    • If the option is not available, you possibly have version 1.6.2 or older. In that case, please check the changelogs to determine your version based on the features available on your repository.
  • Important: If you are several versions behind, we recommend updating one minor version at a time, skipping directly to the latest patch version (eg, 1.5.3 -> 1.6.2 -> 1.7.4 and so on. Check the online documentation site to see the latest versions of each step.)
  • Once you have your remote set up, run the command git pull RHH expansion/X.Y.Z, replacing X, Y and Z with the digits of the respective version you want to update to (eg, to update to 1.9.3, use git pull RHH expansion/1.9.3).
    • Important: If you are several versions behind, we recommend updating one minor version at a time, skipping directly to the latest patch version (eg, 1.5.3 -> 1.6.2 -> 1.7.4 and so on)
  • Alternatively, you can update to unreleased versions of the expansion.
    • master (stable): It contains unreleased bugfixes that will come in the next patch version. To merge, use git pull RHH master.
    • upcoming (unstable, with potential bugs): It contains unreleased features that will come in the next minor version. To merge, use git pull RHH upcoming.

Please consider crediting the entire list of contributors in your project, as they have all worked hard to develop this project :)

Who maintains the project?

The project was originally started by DizzyEgg alongside other contributors. Now it is maintained by a team in the ROM Hacking Hideout's community called the "Expansion Senate". ROM Hacking Hideout (RHH for short) is a Discord-based ROM hacking community specialized in Pokémon romhacks. A lot of the discussion in regards of the development of the project happens there.

Click here to join the RHH Discord Server!

There's a bug in the project. How do I let you guys know?

Please submit any issues with the project here and make sure that the issue wasn't reported by someone else by searching using the filters. You may also join the Discord server to try getting more in-depth support from the team and other members of the server.

Can I contribute even if I'm not a member of ROM Hacking Hideout?

Yes! Contributions are welcome via Pull Requests and they will be reviewed by maintainers in due time. Also, please follow the Pull Request template and feel free to discuss how the reviews are being handled. Communication is key! Don't feel discouraged if we take a bit to review your PR, we'll get to it.

What features are included?

  • IMPORTANT❗❗ Read through these to learn what features you can toggle:
  • 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)
    • 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
    • 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.
  • 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.
    • 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.
    • Gen 6+ Exp. Share (configurable)
    • Berserk Gene
    • Most battle items from Gen 4+
  • Feature branches incorporated (with permission):
    • RHH intro credits by @Xhyzi.
      • A small signature from all of us to show the collective effort in the project :)
    • Overworld debug 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 by @TheXaman
      • May be disabled.
      • Additional features:
        • Support for new evolution methods.
        • Dark Mode.
    • Nature Colors in summary screen by @DizzyEggg
    • Dynamic Multichoice by @SBird1337
    • Saveblock Cleansing by @ghoulslash
    • Followers & Expanded IDs by @aarant
      • May be disabled.
      • 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 8.
        • 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.

There are some mechanics, moves and abilities that are missing and being developed. Check the project's milestones and our issues page to see which ones.

Instructions

Install instructions for each supported operating system can be found in their respective directories under docs/install/. Lines to those can be found under each heading. This file only contains a short introduction to each supported system. If you run into trouble, ask for help on Discord (see README.md).

After completing the install instructions for your OS, proceed to Building pokeemerald-expansion.

Windows

Windows needs one of the systems to build the project

A note of caution: As Windows 7 and Windows 8 are officially unsupported by Microsoft, some maintainers are unwilling to maintain the Windows 7/8 instructions. Thus, these instructions may break in the future with fixes taking longer than fixes to the Windows 10/11 instructions.

On Windows, the project can be built using the following systems:

  • WSL2, fastest
  • WSL1, 7 times slower than WSL2
  • Msys2, 20 times slower than WSL2 (NOTE: Currently broken on pret upstream)
  • Cygwin, 30 timer slower than WSL2 (NOTE: Currently broken on pret upstream)

NOTE: Only WSL systems are recommended.

WSL Install instructions

Msys2 Install instructions

Cygwin Install instructions

Linux

The project can be built on any Linux distribution. Distributions with instructions:

Other distributions have to infer what to do from general instructions.

Mac

Some extra considerations exist to get the testing system working.

Mac instructions

ChromeOS

Only tested on x86_64 based systems.

Chrome OS instructions

Building pokeemerald-expansion

Follow these steps to build pokeemerald-expansion.

  1. Navigate to the directory you want to keep the project in, be aware of any system specific limitations.

  2. Download pokeemerald-expansion with git

    git clone https://github.com/rh-hideout/pokeemerald-expansion
    
  3. Navigate to the newly downloaded project.

    cd pokeemerald-expansion
    
  4. Build the project.

    make
    
  5. If everything worked correctly, something very similar to this should be seen.

    arm-none-eabi-ld: warning: ../../pokeemerald.elf has a LOAD segment with RWX permissions
    Memory region         Used Size  Region Size  %age Used
               EWRAM:      243354 B       256 KB     92.83%
               IWRAM:       30492 B        32 KB     93.05%
                 ROM:    26072244 B        32 MB     77.70%
    cd build/modern && arm-none-eabi-ld  -T ../../ld_script_modern.ld --print-memory-usage -o ../../pokeemerald.elf <objs> <libs> | cat
    tools/gbafix/gbafix pokeemerald.elf -t"POKEMON EMER" -cBPEE -m01 -r0 --silent
    arm-none-eabi-objcopy -O binary pokeemerald.elf pokeemerald.gba
    tools/gbafix/gbafix pokeemerald.gba -p --silent
    

    And the build ROM will be in the directory as pokeemerald.gba.

Building guidance

Parallel builds

See the GNU docs and this Stack Exchange thread for more information.

To speed up building, first get the value of nproc by running the following command:

nproc

Builds can then be sped up by running the following command:

make -j<output of nproc>

Replace <output of nproc> with the number that the nproc command returned.

nproc is not available on macOS. The alternative is sysctl -n hw.ncpu (relevant Stack Overflow thread).

Other toolchains

To build using a toolchain other than devkitARM, override the TOOLCHAIN environment variable with the path to your toolchain, which must contain the subdirectory bin.

make TOOLCHAIN="/path/to/toolchain/here"

The following is an example:

make TOOLCHAIN="/usr/local/arm-none-eabi"

To compile the modern target with this toolchain, the subdirectories lib, include, and arm-none-eabi must also be present.

Building with debug info

To build pokeemerald.elf with debug symbols and debug-compatible optimization under a modern toolchain:

make debug

Useful additional tools

Setting up WSL1 (Legacy Portion)

  1. Certain packages are required to build pokeemerald. Install these packages by running the following command:

    sudo apt install build-essential git libpng-dev gdebi-core
    

    Note: If the above command does not work, try the above command but replacing apt with apt-get.

  2. Once the packages have finished installing, download the devkitPro pacman package here. The file to download is devkitpro-pacman.amd64.deb.

  3. WSL has its own file system that's not accessible from Windows, but Windows files are accessible from WSL. To install the devkitPro package, you'll need to change to the current working directory where the package file was saved.

    For example, if the package file was saved to C:\Users\<user>\Downloads (the Downloads location for most users), enter this command, where <user> is your Windows username:

    cd /mnt/c/Users/<user>/Downloads
    

    Note 1: The Windows C:\ drive is called /mnt/c/ in WSL. Note 2: If the path has spaces, then the path must be wrapped with quotations, e.g. cd "/mnt/c/users/<user>/Downloads folder". Note 3: Windows path names are case-insensitive so adhering to capitalization isn't needed

  4. Once the directory has been changed to the folder containing the devkitPro pacman package, run the following commands to install devkitARM.

    sudo gdebi devkitpro-pacman.amd64.deb
    sudo dkp-pacman -Sy
    sudo dkp-pacman -S gba-dev
    

    The last command will ask for the selection of packages to install. Just press Enter to install all of them, followed by entering Y to proceed with the installation.

    Note: devkitpro-pacman.amd64.deb is the expected filename of the devkitPro package downloaded (for the first command). If the downloaded package filename differs, then use that filename instead.

  5. Run the following command to set devkitPro related environment variables (alternatively, close and re-open WSL):

    source /etc/profile.d/devkit-env.sh
    

Proceed to Choosing where to store pokeemerald (WSL1) of the current INSTALL.md.

Running documentation website locally

Note: For further information beyond this very basic guide, please visit mdBook's official documentation.

Running documentation website locally (Ubuntu WSL1/WSL2)

Previous Requirements:

  • Option 1: Install via Rust toolchain
    • Install Rust toolchain if you don't have it via the sudo apt install cargo command.
    • Install mdBook via the cargo install mdbook command. Once finished, this message will pop up, with {USER} being your Ubuntu
      warning: be sure to add `/home/{USER}/.cargo/bin` to your PATH to be able to run the installed binaries
      
    • Add /home/{USER}/.cargo/bin to your PATH (with {USER} being the Ubuntu username.)
      • Run command nano ~/.profile to edit the file.
      • Add the following lines, replacing {USER} with your Linux username.
        # set PATH so it includes user's private bin if it exists
        if [ -d "$HOME/bin" ] ; then
            PATH="$HOME/bin:$PATH"
        fi
        
        # set PATH so it includes user's private bin if it exists
        if [ -d "$HOME/.local/bin" ] ; then
            PATH="$HOME/.local/bin:$PATH"
        fi
        
        +# set PATH so it includes cargo bin if it exists
        +if [ -d "/home/{USER}/.cargo/bin" ] ; then
        +    PATH="/home/{USER}/.cargo/bin:$PATH"
        +fi
        
      • Run the source ~/.profile command to refresh the path in the current session.
  • Option 2: Install downloaded binaries directly
    • TODO: Add documentation of this process.

Running the website

  • Navigate to the docs folder on the repository.
  • Run mdbook serve. Once started, you may now open the website on your browser by going to http://127.0.0.1:3000.
  • Every change done to the docs folder will be reflected with an automatic refresh.
  • To stop the server and go back to the terminal, press Ctrl + C.

Modifying the website

  • The navigation menu on the left is handled by docs/SUMMARY.md. Every file added needs to be added somewhere here in order to become visible, otherwise you'll get a 404 error.
  • Any Markdown files (.md extension) added to the docs/ directory will automatically be read by mdBook.
  • To add Markdown files that are not in the docs/ directory, you may create an empty .md file and add the following without the "----":
    {{ ----#include ../INSTALL.md}}`
    
    This will include the INSTALL.md Markdown file from the root directory.

Once you're set up, you can now check your changes before pushing them to your repo! :D

We hope that this will make it easier for users to contribute to the documentation :)

What are AI Flags?

AI flags alter the behavior of AI controlled trainers. These flags affect what moves the AI chooses to use, what Pokémon the AI sends out and when they decide to switch, overarching strategic choices the AI prefers to make, and more.

The AI flags can be found in include/constants/battle_ai.h. Some flags have their own dedicated functions that affect how the AI scores its options when choosing what to do in battle, and those functions can be found in src/battle_ai_main.c. Other flags are used in conditional checks to gate certain behaviour behind certain flags, which you can typically find by searching the codebase for the flag name and browsing from there.

What flags should you use?

When adding new AI flags it is recommended to use AI_FLAG_CHECK_BAD_MOVE, AI_FLAG_CHECK_VIABILITY, AI_FLAG_TRY_TO_FAINT to make sure the AI makes good decisions. It is especially important to use AI_FLAG_CHECK_BAD_MOVE in combination with any added flags otherwise the AI will use moves that can fail.

Other flags should be used with consideration to the circumstances.

How do you use them?

Adding an AI flag to a trainer is straightforward, but the process is different depending on how trainers are being defined.

COMPETITIVE_PARTY_SYNTAX == TRUE

If you are using competitive syntax parties, navigate to the trainer data in src/data/trainers.party, find the trainer you’d like to change, and add flags like so: AI: Check Bad Move / Try to Faint / Check Viability. The name of each flag is just the constant, but without AI_FLAG at the beginning. For example, to add AI_FLAG_SEQUENCE_SWITCHING, any of the following will work:

  • AI_FLAG_SEQUENCE_SWITCHING
  • SEQUENCE_SWITCHING
  • SEQUENCE SWITCHING
  • Sequence_Switching
  • Sequence Switching

COMPETITIVE_PARTY_SYNTAX != TRUE / Not Found

If you are not using competitive syntax parties, instead access the trainer data directly in src/data/trainers.h, and add flags like so, typed exactly the same as the flag names themselves: .aiFlags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY

What AI Flags does pokeemerald-expansion have?

This section lists all of expansion’s AI Flags and briefly describes the effect they have on the AI’s behaviour. In all cases, please check the corresponding function or surrounding code around their implementation for more details. Some of these functions are vanilla, some share a name with vanilla but have been modified to varying degrees, and some are completely new.

Composite AI Flags

Expansion has two "composite" AI flags, AI_FLAG_BASIC_TRAINER and AI_FLAG_SMART_TRAINER. This means that these flags have no unique functionality themselves, and can instead be thought of as groups of other flags that are all enabled when this flag is enabled. The idea behind these flags is that if you don't care to manage the detailed behaviour of a particular trainer, you can use these as a baseline instead, and expansion will keep them updated for you.

AI_FLAG_BASIC_TRAINER is expansion's version of generic, normal AI behaviour. It includes AI_FLAG_CHECK_BAD_MOVE (don't use bad moves), AI_FLAG_TRY_TO_FAINT (faint the player where possible), and AI_FLAG_CHECK_VIABILITY (choose the most effective move to use in the current context). Trainers with this flag will still be smarter than they are in vanilla as there have been dramatic improvements made to move selection, but not incredibly so. Trainers with this flag should feel like normal trainers. In general we recommend these three flags be used in all cases, unless you specifically want a trainer who makes obvious mistakes in battle.

AI_FLAG_SMART_TRAINER is expansion's version of a "smart AI". It includes everything in AI_FLAG_BASIC_TRAINER along with AI_FLAG_SMART_SWITCHING (make smart decisions about when to switch), AI_FLAG_SMART_MON_CHOICES (make smart decisions about what mon to send in after a switch / KO), and AI_FLAG_OMNISCIENT (awareness of what moves, items, and abilities the player's mons have to better inform decisions). Expansion will keep this updated to represent the most objectively intelligent behaviour our flags are capable of producing.

Expansion has LOADS of flags, which will be covered in the rest of this guide. If you don't want to engage with detailed trainer AI tuning though, you can just use these two composite flags, and trust that expansion will keep their contents updated to always represent the most standard and the smartest behaviour we can.

AI_FLAG_CHECK_BAD_MOVE

The AI will avoid using moves that are likely to fail in the current situation. This flag helps prevent the AI from making ineffective choices, such as using moves into immunities, into invulnerable states, or when the moves are otherwise hindered by abilities, terrain, or status conditions.

AI_FLAG_TRY_TO_FAINT

AI will prioritize KOing the player if able rather than using status moves. Will prioritize using a move that can OHKO the player. If the player can KO the AI’s mon and the AI’s mon is slower, prioritize priority moves (this does not prevent the AI from switching out instead).

This flag handles scoring for OHKOs but does not handle 2HKOs at all, AI_FLAG_STRONGEST_MOVE should be used for 2HKO scoring.

AI_FLAG_CHECK_VIABILITY

This flag is divided into two components to calculate the best available move for the current context:

  • AI_CompareDamagingMoves: This function compares damaging moves against each other and picks the best one.
  • AI_CalcMoveEffectScore: This function checks every move effect (status or damaging move effect) and increases the score accordingly.

This is different to AI_FLAG_CHECK_BAD_MOVE as it calculates how poor a move is and not whether it will fail or not.

AI_FLAG_FORCE_SETUP_FIRST_TURN

AI will prioritize using setup moves on the first turn at the expense of all else. These include stat buffs, field effects, status moves, etc. AI_FLAG_CHECK_VIABILITY will instead do this when the AI determines it makes sense.

This is just a flat increase without any consideration of whether it makes sense to use the move or not. For better move choice quality for those moves, AI_FLAG_CHECK_VIABILITY should be used.

AI_FLAG_RISKY

AI will generally behave more recklessly. This AI enables the following behaviour:

  • Always assume the highest damage roll when scoring moves
  • Blindly Mirror Coat / Counter based on the player mon’s species higher attacking stat
  • Moves with Recoil if they miss are not treated differently even if accuracy is lowered
  • Prioritize maximizing damage from moves at the cost of accuracy
  • Prioritize moves with low change strong effects (Ancient Power etc., check AI_Risky function for full list)
  • Switch offensively mid battle rather than defensively (if using AI_FLAG_SMART_MON_CHOICES)
  • Prioritize Explosion moves

AI_FLAG_PREFER_STRONGEST_MOVE

Adds score bonus to any move the AI has that either OHKOs or 2HKOs the player.

Keep in mind that this is a weaker form of AI_FLAG_TRY_TO_FAINT at scoring OHKOs as it does not take into account who is attacking first, it does however handle 2HKOs.

AI_FLAG_PREFER_BATON_PASS

AI prefers raising its own stats if it has >= 60% HP, as well as Ingrain, Aqua Ring, and Protect. Prioritizes Baton Bass if the mon is rooted (Ingrain) or has the Aqua Ring effect, and doesn’t if it has been Leech Seeded.

AI_FLAG_DOUBLE_BATTLE

This flag is automatically set in double battles, and controls much of the doubles-specific scoring. I’ll summarize some of its scoring as follows:

  • Don’t use Helping Hand if partner is, don’t Perish Trap your partner, don’t change the weather if they are, don’t buff stats if partner will trigger Anger Point for us
  • Collaborate with partner to Perish Trap opponent, Magnet Rise to protect partner, Dragon Cheer partner if applicable
  • Prioritize using weather move if it benefits partner
  • Prioritize triggering partner’s good abilities if possible (Motor Drive, Storm Drain, Beat Up -> Justified, etc.)
  • Handle Skill Swap smartly, both with the partner and against the player

AI_FLAG_HP_AWARE

Lets the AI make decisions based on how much remaining HP its mon(s) and the player’s mon(s) have.

With respect to the AI’s mons, in doubles:

  • Allows the AI to attack its partner with a move it can absorb if its low on HP (ie. Electric move on partner with Volt Absorb)
  • Prioritizes healing its partner if its HP is <= 50% if able

In both singles and doubles:

  • Prioritizes not using moves that require the user fainting (Destiny Bond, Explosion etc.) and healing moves while on >= 70% HP.
  • Prioritize not using moves that require the user fainting or losing significant HP (Belly Drum etc) while between 30% and 70% HP
  • Prioritize not using setup moves (Light Screen etc.) and Bide while on <= 30% HP

With respect to the player’s mons:

  • Prioritize not using many status moves (stat buffs, Poison, Pain Split) if the player has between 30% and 70% HP
  • Prioritize not using any status moves if the player is has <= 30% HP

AI_FLAG_POWERFUL_STATUS

AI prioritizes setting up field effects (Trick Room, Rain Dance, etc.) and side statuses (Tailwind, Spikes, etc.), even if it could faint the target.

AI_FLAG_NEGATE_UNAWARE

AI does not understand ability suppression (Mold Breaker etc., weather suppression (Air Lock etc.), redirection abilities (Lightningrod etc.) being temporarily removed due to move effects (Sky Drop etc.), or item suppression (Magic Room etc.) and will ignore them. This is a handicap flag.

AI_FLAG_WILL_SUICIDE

AI prioritizes self destruction moves (Explosion, Memento).

AI_FLAG_PREFER_STATUS_MOVES

AI gets a score bonus for status moves. This should be combined with AI_FLAG_CHECK_BAD_MOVE to prevent using only status moves.

AI_FLAG_STALL

AI prefers simple classically "stalling" behaviour. It will prioritize:

  • Mean Look, Fairy Lock, and Wrap for trapping
  • Increasing its defense and special defense
  • Moves that inflict Poison if it also has a Protect move
  • Copying defense and special defense buffs

AI_FLAG_SMART_SWITCHING

Affects when the AI chooses to switch. AI will make smarter decisions about when to switch out mid-battle. Automatically enables AI_FLAG_SMART_MON_CHOICES, which is required as the vanilla mon selection AI is not smart enough to handle several switch-triggering situations appropriately, leading to bizarre behaviour. Many of these checks have intentional failure rates, so the AI won’t switch out 100% of the time in these cases to keep the player from being able to predict perfectly. Some of these also only apply to singles, and many of them are being simplified for the sake of brevity. This flag lets the AI trigger switches when:

  • It can’t hit Wonder Guard and has another mon in the party that can (switch that mon in)
  • It’s going to die to Perish Song, can’t KO the player and is affected by Yawn, is being severely affected by a status condition that switching helps (Curse, Toxic, Leech Seed)
  • It has a mon that can trap the player’s mon and win the 1v1 (switch that mon in)
  • It has a mon in the party that can absorb the player’s next expected attack (switch that mon in)
  • It will not switch if the current mon will die to hazards on re-entry and it has no means of clearing them in its party
  • All its moves are bad
  • It can take advantage of Natural Cure or Regenerator
  • Its Encore’d into something bad
  • Its primary attacking stats are sufficiently lowered
  • Its "odds are bad", which is a generic "try to make smart, player-like decisions generally speaking" check. Switches can be triggered if the player has a good switchin candidate (AI_FLAG_SMART_MON_CHOICES), and:
  • The current mon has a bad type matchup and doesn’t have a super effective move and has at least ½ HP, or ¼ HP and Regenerator, or
  • The current mon loses the 1v1 quickly and has at least ½ HP, or ¼ and Regenerator

AI_FLAG_ACE_POKEMON

Marks the last Pokemon in the party as the Ace Pokemon. It will not be used unless it is the last one remaining, or is forced to be switched in (Roar, U-Turn with 1 mon remaining, etc.). If you are challenged by two different trainers at the same time, only the ones with this flag will have Ace Pokémon. For example vs one trainer with AI_FLAG_ACE_POKEMONand the other without, there will be a total of 1 Ace Pokémon.

AI_FLAG_DOUBLE_ACE_POKEMON

Marks the last two Pokémon in the party as Ace Pokémon, with the same behaviour as AI_FLAG_ACE_POKEMON. Intented for double battles where you battle one trainer id that represents two trainers, ie Twins, Couples. If you apply this flag to trainers outside of double battles or in cases where two trainers can challenge you at the same time, it has the same behaviour. For example vs two trainers with AI_FLAG_DOUBLE_ACE_POKEMON there will be a total of 4 Ace Pokémon.

AI_FLAG_OMNISCIENT

AI has full knowledge of player moves, abilities, and hold items, and can use this knowledge when making decisions.

AI_FLAG_SMART_MON_CHOICES

Affects what the AI chooses to send out after a switch. AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are handled separately. Automatically included when AI_FLAG_SMART_SWITCHING is enabled.

With this flag enabled, the AI will prioritize choosing mons after a KO prioritizing the following criteria:

  • Trapper (can trap the player’s mon and win the 1v1)
  • Revenge killer (outspeeds an OHKOs / is outsped and OHKOs, is not OHKOd/ outspeeds and 2HKOs, is not OHKOd / is outsped and 2HKOs, is not 2HKOd)
  • Has good type matchup and a super effective move
  • Has good type matchup and does not have a super effective move
  • Has Baton Pass
  • If no mons meet any of the above criteria, choose the one that does the most damage

And will choose mons after a mid-battle switch prioritizing the following criteria:

  • Trapper (can trap the player’s mon and win the 1v1)
  • Has good type matchup and a super effective move
  • Has good type matchup and does not have a super effective move
  • Is not 3HKO’d by the player
  • Has Baton Pass

AI_FLAG_CONSERVATIVE

AI always assumes it will roll the lowest possible result when comparing damage in scoring.

AI_FLAG_SEQUENCE_SWITCHING

AI will always switch out after a KO in exactly party order as defined in the trainer data (ie. slot 1, then 2, then 3, etc.). The AI will never switch out mid-battle unless forced to (Roar etc.). If the AI uses a move that requires a switch where it makes a decision about what to send in (U-Turn etc.), it will always switch out into the lowest available party index.

How to add new AI Flags

The battle engine upgrade has rewritten the AI battle scripts to C functions to easily add new logic. This tutorial explains how to add a new AI logic flag.

1. Define your flag

Open include/constants/battle_ai.h. We have many unused flags, but you can add a new one after AI_FLAG_SMART_SWITCHING like so:

#define AI_FLAG_SUPPORT (1 << 16)

2. Make your new function

Open src/battle_ai_main.c. Search for the array static s16 (*const sBattleAiFuncTable[])(u8, u8, u16, s16). We want to add our new function to this table. Since we have defined our flag as (1 << 16), find the 16th entry in the table (identifiable by the initializer, [16]), and replace it with:

[16] = AI_Support, // AI_FLAG_SUPPORT

Define your function above the table as static s16 AI_Support(u8 battlerAtk, u8 battlerDef, u16 move, s16 score);

Make your function do something

at the bottom of the file, add:

static s16 AI_Support(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
{
    // Add your logic here!
}

Give your trainer the correct AI flag!

And that's it!

How to add new Battle Script Commands/Macros

To preface this tutorial, the battle engine upgrade has exhausted all battle script command IDs. Historically, we've used the various command to effectively add new commands. However, this has caused issues of maintainability and readability due to the massive switch needed for it. Thanks to the cleanup made by the team and contributors, we now are able to call an infinite amount of commands by using callnative. This is preferential to creating a secondary battle script command table like is done in the CFRU.

In general, gBattlescriptCurrInstr tracks the current battle script position as a ROM address. Fortunately, we don't need to worry about ROM addresses when using the decomps, but it is important to understand because of how the callnative command is set up.

	.macro callnative func:req
	.byte 0xff
	.4byte \func
	.endm

callnative uses the last battle script command ID in order to pass a native function as an argument. Additional optional arguments are added recursively via a macro, so no need to worry about how they need to align to the amount of instructions to skip.

Now, how might we add a custom callnative command? Here are the steps. We will use BS_TrySetOctolock as an example.

1. Create a macro in asm/macros/battle_script.inc. For example:

	.macro trysetoctolock battler:req, failInstr:req
	callnative BS_TrySetOctolock
	.byte \battler
	.4byte \failInstr
	.endm

2. Add your new callnative command ID to src/battle_script_commands.c. For example:

void BS_TrySetOctolock(void)
{
    NATIVE_ARGS(u8 battler, const u8 *failInstr);
    u32 battler = GetBattlerForBattleScript(cmd->battler);

    if (gDisableStructs[battler].octolock)
    {
        gBattlescriptCurrInstr = cmd->failInstr;
    }
    else
    {
        gDisableStructs[battler].octolock = TRUE;
        gBattleMons[battler].status2 |= STATUS2_ESCAPE_PREVENTION;
        gDisableStructs[battler].battlerPreventingEscape = gBattlerAttacker;
        gBattlescriptCurrInstr = cmd->nextInstr;
    }
}

Each of the arguments defined in the macro (battler, failInstr) need to be called at the start of the command using NATIVE_ARGS. The byte count in the macro should correspond to the type that will be used for the command (eg, u8 is byte, while the pointer are 4byte). These arguments can then be accessed as cmd->battler and cmd->battler. gBattlescriptCurrInstr = cmd->nextInstr; advances to the next instruction.

Full credits and thank you to CancerFairy for writing this guide!

Note: This guide was written for version 1.8.0. Most stuff still applies to 1.7.x versions and earlier, with the following exceptions:

  • Battle and Contest move data are separated in src/data/battle_moves.h and src/data/contest_moves.h
  • additionalEffects doesn't exist, instead being handled by a combination of secondaryEffectChance and unique EFFECT_xxxs.
  • There's no include/constants/battle_move_effects.h, so data specific to certain effects is handled in other places.
  • Move names are handled in gMoveNames.

Adding/editing moves

This guide is here to give you a breakdown of how moves work, how to edit existing ones, and how to add your own.

Contents:

  1. Key files and definitions
  2. Editing a move
  3. Adding a new move

Key files and definitions

Before beginning the process, it's important to familiarise yourself with the important files that control moves. There are three categories of files - header(.h) files, which contain static information about a move, .c files which contains functions in C that determine how the move behaves, and script files (.s or .inc) that actually "run" the move - i.e. determine the sequence of events you see on screen when you execute the move.

Header files

src/data/moves_info.h

This is the place where the bulk of move information is stored, including name, base power, typing, PP, contest information etc.

Let's look at an example:

[MOVE_THUNDER_SHOCK] =
{
    .name = HANDLE_EXPANDED_MOVE_NAME("ThunderShock", "Thunder Shock"),
    .description = COMPOUND_STRING(
        "An electrical attack that\n"
        "may paralyze the foe."),
    .effect = EFFECT_HIT,
    .power = 40,
    .type = TYPE_ELECTRIC,
    .accuracy = 100,
    .pp = 30,
    .target = MOVE_TARGET_SELECTED,
    .priority = 0,
    .category = DAMAGE_CATEGORY_SPECIAL,
    .sheerForceBoost = TRUE,
    .additionalEffects = ADDITIONAL_EFFECTS({
        .moveEffect = MOVE_EFFECT_PARALYSIS,
        .chance = 10,
    }),
    .contestEffect = CONTEST_EFFECT_HIGHLY_APPEALING,
    .contestCategory = CONTEST_CATEGORY_COOL,
    .contestComboStarterId = 0,
    .contestComboMoves = {COMBO_STARTER_CHARGE},
},

The HANDLE_EXPANDED_MOVE_NAME allows the usage of a name of extended character length, so long as the B_EXPANDED_MOVE_NAMES is set to TRUE, whereas by default it's limited in Gen 3 to 12 characters. Most of the fields here are obvious, but the two important ones for determining what a move actually does are effect and additionalEffects.

The effect represents how the move actually works when called in battle - it can be a two turn move, or a move that only works if the target is holding an item, for example. How each effect works is pretty much unique, but the way a move of a particular effect is executed is defined by a script data/battle_scripts_1.s, and any variable characteristics such as typing or power are defined in either src/battle_script_commands.c or src/battle_util.c, depending on the effect. The vast majority of non-status moves are simply EFFECT_HIT, in that they deal damage and apply additionalEffects (if defined).

The additionalEffects field represents effects that are applied at the setadditionaleffects stage of the move script (for most moves, see BattleScript_Hit_RetFromAtkAnimation). These are effects that can be encapsulated by any of the MOVE_EFFECT_X defined in include/constants/battle.h and encoded under SetMoveEffect in src/battle_script_commands.c. These can vary from applying a status, such as MOVE_EFFECT_PARALYSIS, or lowering/raising stats etc. The move effect could target the user by setting self = TRUE, such as Overheat lowering the user's own Sp. Atk. What's more, definining a chance, such as for Thunder Shock, not only limits the effect to applying only chance% of the time, but it also turns it into a secondary effect. This difference is important because secondary effects are nullified by Sheer Force (which in turn will boost the move's power) and they are blocked by Shield Dust. These two limitations do not apply to primary effects which do not a chance field defined and by definition will always happen when the move is executed.

src/data/battle_move_effects.h

Effects are listed here along with the battleScript that governs each one. Said scripts are defined in data/battle_scripts_1.s. The indices/names of the effects (e.g. EFFECT_FIRST_TURN_ONLY) are enums defined in include/constants/battle_move_effects.h.

include/battle_scripts.h

Contains references to scripts data/battle_scripts_1.s, allowing them to be referenced in C. Any new scripts must be added here.

include/constants/battle_move_effects.h

Simply an enum list of possible effects for moves. Any new effects would be added here, with a definition for them (including defining a script) would then also be added to src/data/battle_move_effects.h.

include/constants/battle_string_ids.h

All strings that can be printed in battle have an id that is defined here. The actual message itself would then be defined and assigned to this id in src/data/battle_message.c.

include/constants/battle.h

A whole range of constants defining battle variables, such as statuses, weather, and move effects.

include/constants/moves.h

Where moves are defined (and nothing else).

Note: When adding custom moves, you should add them between the moves from the latest generation and the z moves, then adjust MOVES_COUNT accordingly. Adding a move after MOVES_COUNT that is neither a Max Move or a Z Move will result in that move's name not being printed when it is used, instead a generic message will be printed.

C files

src/battle_script_commands.c

This is where a lot of the commands referred to in scripts are defined. For example, the jumpifnotfirstturn command above is defined by the function Cmd_jumpifnotfirstturn and you can see how it works in C. It's possible that any move editing or updating you have in mind can be done with existing commands, but if you wanted to add a new function that could be called in a script above, this is where you would define it.

src/battle_util.c

This contains a lot of the "utility" functions used to determine things like a move's dynamic typing or power. It's also where damage calculation takes place, and a lot of that will naturally take a move's effect into account. For example, a move with the effect EFFECT_SOLAR_BEAM would have its damage halved in sandstorm. If you wanted to add a move with an effect which gave it variable BP or typing, this is the file you would encode that effect.

src/battle_message.c

Contains string defines and functions that print messages during the battle. If you wish to add or edit a move's string, then this is where you would do so.

src/battle_main.c

Contains more fundamental functions that control the flow of the battle. Functions here determine move order, dynamic typing, animations, priority, speed calculations and more.

Script files

data/battle_scripts_1.s

Each move's effect is governed by a script defined here. For a simple example, let's look at the script for Fake Out/First Impression:

BattleScript_EffectFirstTurnOnly::
	attackcanceler
	jumpifnotfirstturn BattleScript_FailedFromAtkString
	goto BattleScript_EffectHit

attackcanceler is a command that covers all the cases that could cause a move to fail before it's even attempted (e.g. paralysis). And as we can tell from the commands, if it's not the first turn, we go to BattleScript_FailedFromAtkString which evidently causes us to print the attackstring ("POKEMON used MOVE") then fail ("But it failed!"). Otherwise, we go to the generic "hit" effect which is the same script for moves that just deal damage and nothing else.

This is the most advanced part of the ROM. There are dozens upon dozens of commands and hundreds of scripts so this guide would go on forever if I were to go into more detail. To learn how these scripts work, it's best to look at a few examples of moves you know.

asm/macros/battle_script.inc

The "link" between data/battle_scripts_1.s and src/battle_script_commands.c. Each command is represented by a hex byte which represents its index in the gBattleScriptingCommandsTable array at the top of src/battle_script_commands.c. However, this file also contains macros which perform combinations of other commands, or just calculations in assembly. In addition to commands, it is also possible to call functions in src/battle_script_commands.c using the various (now gradually being deprecated) and the callnative functionality. The various macros will point to a case under the Cmd_various function in src/battle_script_commands.c, whereas callnative will let you directly call a function in src/battle_script_commands.c by name.

data/battle_anim_scripts.s

This is the place where move animations are defined. The array at the top, gBattleAnims_Moves, is in move index order and determines which animation goes with which move.

Editing a move

Basic information

To edit a move's basic information, you need only edit the relevant fields in src/data/battle_moves.h. This will let you change a move's:

  • name
  • description
  • power
  • accuracy
  • type
  • category
  • target
  • pp
  • recoil percentage
  • flags
  • Z-move effect (for status moves) or overwritting its calculated power (for damaging moves)

Changing a move's main effect

To change the main effect of a move to an existing effect, you need only change its effect field to one of the options in src/data/battle_move_effects.h. If you wish to keep the effect but simply modify how it works, you can modify how it plays out on screen by editing its entry in data/battle_scripts_1.s and any relevant functions in src/battle_script_commands.c. To change how a move's dynamic power, accuracy and are calculated, then you need to modify the following functions:

Note: A generic function for calculating category does not currently exist - Photon Geyser's script in data/battle_scripts_1.s uses a special callnative function BS_SetPhotonGeyserCategory.

Changing a move's additional effects

If you look at the example here, you can see that Thunder Shock has an additional effects array that contains a single move effect MOVE_EFFECT_PARALYSIS with a 10% chance of applying. Thanks to this field, you can add and remove primary and secondary effects (so long as they are defined by a MOVE_EFFECT) to a move without having to change its effect or script. You can also make an effect apply to the attacker rather than the target (for, say, a stat boost) with .self = TRUE and you can set the probability to whatever you want with the chance field.

All additional effects with a defined chance (even 100%) are treated as "secondary effects". This means that they are nullified by Sheer Force, blocked by Shield Dust or the Covert Cloak, and have their chance modified by Serene Grace. Additional effects without a chance field (effectively setting it to 0) are treated as "primary effects", which means that they cannot be blocked by the aforementioned items and abilities and their chance to occur cannot be modified; they will always happen.

Each move can have up to 15 additional effects, allowing you to construct monstrosities like this:

[MOVE_POUND] =
{
    .name = COMPOUND_STRING("Pound"),
    .description = COMPOUND_STRING(
        "Pounds the foe with\n"
        "forelegs or tail."),
    .effect = EFFECT_HIT,
    .power = 40,
    .type = TYPE_NORMAL,
    .accuracy = 100,
    .pp = 35,
    .target = MOVE_TARGET_SELECTED,
    .priority = 0,
    .category = DAMAGE_CATEGORY_PHYSICAL,
    .additionalEffects = ADDITIONAL_EFFECTS({
        .moveEffect = MOVE_EFFECT_PARALYSIS,
        .chance = 10,
    },{
        .moveEffect = MOVE_EFFECT_CONFUSION,
        .chance = 100,
    },{
        .moveEffect = MOVE_EFFECT_FLINCH,
        .chance = 30,
    },{
        .moveEffect = MOVE_EFFECT_ALL_STATS_UP,
        .chance = 40,
        .self = TRUE,
    },{
        .moveEffect = MOVE_EFFECT_RAPID_SPIN,
    },{
        .moveEffect = MOVE_EFFECT_DEF_MINUS_2,
        .chance = 50,
    }),
    .makesContact = TRUE,
    .ignoresKingsRock = B_UPDATED_MOVE_FLAGS == GEN_4,
    .contestEffect = CONTEST_EFFECT_HIGHLY_APPEALING,
    .contestCategory = CONTEST_CATEGORY_TOUGH,
    .contestComboStarterId = COMBO_STARTER_POUND,
    .contestComboMoves = {0}
},

Note: at the moment, additional effects can only be used by damaging moves, not by status moves.

Adding a new move

To add a new move, you need to create an entry in three locations:

And that's it! You can use an existing animation or effect for your move - or you can add your own, but I'll leave figuring that out to you.

How to add a new trainer class

Content

Quick Summary

(Page contains out of date information, new instructions for Sprites here.) If you've done this before and just need a quick lookup, here's what files you need:

  1. GFX into graphics/trainers/front_pics
  2. Palette into graphics/trainers/palettes
  3. Register sprites to include/graphics.h
  4. Point game to where graphic files are found: src/data/graphics/trainers
  5. Add animation to: src/data/trainer_graphics/front_pic_anims.h
  6. Add the trainer to all three structs in: src/data/trainer_graphics/front_pic_table.h
  7. Add trainer to include/constants/trainers.h

The Graphics

1. Edit the sprites

We will start with a graphic that we want to use for our new trainer class. Unlike with adding Pokémon, the trainer sprites aren't sorted in individual folders, but rather in one folder: graphics/trainers/front_pics

Remember to limit yourself to 16 colors including transparency in the first slot!

Export the pallette and place into the same folder.

2. Register the sprites

Sadly, just putting the image files into the graphics folder is not enough. To use the sprites we have to register them, which is kind of tedious. First, create constants for the file paths. Edit include/graphics.h:

extern const u32 gTrainerFrontPic_RubySapphireMay[];
+ extern const u32 gTrainerFrontPic_myTrainerClass[];

extern const u32 gTrainerPalette_Hiker[];
...

...
extern const u32 gTrainerPalette_RubySapphireMay[];
+ extern const u32 gTrainerPalette_myTrainerClass[];

extern const u8 gTrainerBackPic_Brendan[];

Now link the graphic files. src/data/graphics/trainers:

const u32 gTrainerPalette_RubySapphireBrendan[] = INCBIN_U32("graphics/trainers/palettes/ruby_sapphire_brendan.gbapal.lz");

const u32 gTrainerFrontPic_RubySapphireMay[] = INCBIN_U32("graphics/trainers/front_pics/ruby_sapphire_may_front_pic.4bpp.lz");
const u32 gTrainerPalette_RubySapphireMay[] = INCBIN_U32("graphics/trainers/palettes/ruby_sapphire_may.gbapal.lz");

+ const u32 gTrainerFrontPic_Sheriff[] = INCBIN_U32("graphics/trainers/front_pics/myTrainerClass_front_pic.4bpp.lz");
+ const u32 gTrainerPalette_Sheriff[] = INCBIN_U32("graphics/trainers/palettes/myTrainerClass.gbapal.lz");

const u8 gTrainerBackPic_Brendan[] = INCBIN_U8("graphics/trainers/back_pics/brendan_back_pic.4

3. The Animation

Add the Animation of the trainer here: src/data/trainer_graphics/front_pic_anims.h

The trainers don't really move, but in theory they could, it's just that the animation defined for each trainer just shows one frame:

static const union AnimCmd *const sAnims_RubySapphireMay[] ={
    sAnim_GeneralFrame0,
};

+ static const union AnimCmd *const sAnims_MyTrainerClass[] ={
+     sAnim_GeneralFrame0,
+ };

const union AnimCmd *const *const gTrainerFrontAnimsPtrTable[] =
{
    [TRAINER_PIC_HIKER] = sAnims_Hiker,
    [TRAINER_PIC_AQUA_GRUNT_M] = sAnims_AquaGruntM,
    [TRAINER_PIC_POKEMON_BREEDER_F] = sAnims_PokemonBreederF,
...

...
    [TRAINER_PIC_RS_BRENDAN] = sAnims_RubySapphireBrendan,
    [TRAINER_PIC_RS_MAY] = sAnims_RubySapphireMay,
+     [TRAINER_PIC_MYTRAINERCLASS] = sAnims_MyTrainerClass,
};

4. Connecting the Pictures to the Data

The last few things we have to do is prepare the graphics for usage. In src/data/trainer_graphics/front_pic_table.h you'll find the structs, we need to add the trainer to all of these. You can just copy the last trainer type defined and edit it, but as far as I understand, these are what they do:

  1. gTrainerFrontPicCoords: Pretty self explanatory. Coordinates like size and offset on the y-axis to position the sprite on screen.
  2. gTrainerFrontPicTable: Connects the trainer type with the image we defined earlier.
  3. gTrainerFrontPicPaletteTable: Connects the trainer type with the palette we defined earlier.

So, finally, it needs to look like this:

const struct MonCoords gTrainerFrontPicCoords[] =
{
    [TRAINER_PIC_HIKER] = {.size = 8, .y_offset = 1},
    [TRAINER_PIC_AQUA_GRUNT_M] = {.size = 8, .y_offset = 1},
...

...
    [TRAINER_PIC_RS_BRENDAN] = {.size = 8, .y_offset = 1},
    [TRAINER_PIC_RS_MAY] = {.size = 8, .y_offset = 1},
+     [TRAINER_PIC_MYTRAINERCLASS] = {.size = 8, .y_offset = 1},
};

#define TRAINER_SPRITE(trainerPic, sprite, size) [TRAINER_PIC_##trainerPic] = {sprite, size, TRAINER_PIC_##trainerPic}

const struct CompressedSpriteSheet gTrainerFrontPicTable[] =
{
    TRAINER_SPRITE(HIKER, gTrainerFrontPic_Hiker, 0x800),
    TRAINER_SPRITE(AQUA_GRUNT_M, gTrainerFrontPic_AquaGruntM, 0x800),
    TRAINER_SPRITE(POKEMON_BREEDER_F, gTrainerFrontPic_PokemonBreederF, 0x800),
    TRAINER_SPRITE(COOLTRAINER_M, gTrainerFrontPic_CoolTrainerM, 0x800),
...

...
    TRAINER_SPRITE(RS_BRENDAN, gTrainerFrontPic_RubySapphireBrendan, 0x800),
    TRAINER_SPRITE(RS_MAY, gTrainerFrontPic_RubySapphireMay, 0x800),
+     TRAINER_SPRITE(MYTRAINERCLASS, gTrainerFrontPic_MyTrainerClass, 0x800),
};

#define TRAINER_PAL(trainerPic, pal) [TRAINER_PIC_##trainerPic] = {pal, TRAINER_PIC_##trainerPic}

const struct CompressedSpritePalette gTrainerFrontPicPaletteTable[] =
{
    TRAINER_PAL(HIKER, gTrainerPalette_Hiker),
    TRAINER_PAL(AQUA_GRUNT_M, gTrainerPalette_AquaGruntM),
    TRAINER_PAL(POKEMON_BREEDER_F, gTrainerPalette_PokemonBreederF),
...

...
    TRAINER_PAL(RS_BRENDAN, gTrainerPalette_RubySapphireBrendan),
    TRAINER_PAL(RS_MAY, gTrainerPalette_RubySapphireMay),
+     TRAINER_PAL(MYTRAINERCLASS, gTrainerPalette_MyTrainerClass),
};

The Data

5. Defining the trainer class

Finally, let's bring it all together by defining our new trainer class in include/constants/trainers.h:

#define TRAINER_PIC_RS_MAY                92
+ #define TRAINER_PIC_MYTRAINERCLASS      93

#define TRAINER_BACK_PIC_BRENDAN                0
#define TRAINER_BACK_PIC_MAY                    1

Remember to count the number next to the trainer class up by one!

Usage

You can test your trainer type by going to src/data/trainers and changing a trainer type. For example:

    [TRAINER_BRENDAN_PLACEHOLDER] =
    {
        .partyFlags = 0,
        .trainerClass = TRAINER_CLASS_RS_PROTAG,
        .encounterMusic_gender = TRAINER_ENCOUNTER_MUSIC_MALE,
-       .trainerPic = TRAINER_PIC_RS_BRENDAN,
+       .trainerPic = TRAINER_PIC_MYTRAINERCLASS,
        .trainerName = _("BRENDAN"),
        .items = {},
        .doubleBattle = FALSE,
        .aiFlags = 0,
        .partySize = ARRAY_COUNT(sParty_BrendanLinkPlaceholder),
        .party = {.NoItemDefaultMoves = sParty_BrendanLinkPlaceholder},
    },

This is a modified version of the original tutorial about adding new Pokémon species available in Pokeemerald's wiki.

Despite the persistent rumors about an incredibly strong third form of Mew hiding somewhere, it actually wasn't possible to catch it... OR WAS IT? In this tutorial, we will add a new Pokémon species to the game.

IMPORTANT: This tutorial applies to 1.10.x versions.

Changes compared to vanilla

The main things that the Expansion changes are listed here.

  • Still Front Pics (gMonStillFrontPic_YourPokemon) and by extension src/anim_mon_front_pics.c have been removed.
  • src/data/pokemon/cry_ids.h doesn't exist anymore.
  • You have 6 icon palettes available instead of the base 3.
  • Most tables that use SPECIES_x as indexes have been moved to gSpeciesInfo.

Content

Useful resources

You can open a sprite debug menu by pressing Select in a Pokémon's summary screen outside of battle.

mGBA_6WOo1TSlsn

The Data - Part 1

Our plan is as simple as it is brilliant: clone Mewtwo... and make it even stronger!

1. Declare a species constant

Our first step towards creating a new digital lifeform is to define its own species constant.

Edit include/constants/species.h:

 #define SPECIES_NONE                                    0
 #define SPECIES_BULBASAUR                               1
 ...
 #define SPECIES_MIMIKYU_BUSTED_TOTEM                    1523
 #define SPECIES_MIMIKYU_TOTEM_BUSTED                    SPECIES_MIMIKYU_BUSTED_TOTEM
+#define SPECIES_MEWTHREE                                1524

-#define SPECIES_EGG                                     (SPECIES_MIMIKYU_BUSTED_TOTEM + 1)
+#define SPECIES_EGG                                     (SPECIES_MEWTHREE + 1)

 #define NUM_SPECIES SPECIES_EGG

This number is stored in a Pokémon's save structure. These should generally never change, otherwise your saved Pokémon species will change as well.

We add this at the end so that no existing species change Id and so that we don't have to renumber everything after it.

Now, let's see how it looks in-game!

image

Hmmm, something's not right...

Oh, I know! We need to add the rest of the data! Normally, the vanilla game would crash if we try to look up anything about Mewthree in this state, but the expansion defaults all of its data to SPECIES_NONE.

Now, let's see what needs to be done.

2. SpeciesInfo's structure

Now, to better understand Mewthree, we also need to understand Mew. Let's look at its data.

    [SPECIES_MEW] =
    {
        .baseHP        = 100,
        .baseAttack    = 100,
        .baseDefense   = 100,
        .baseSpeed     = 100,
        .baseSpAttack  = 100,
        .baseSpDefense = 100,
        .types = MON_TYPES(TYPE_PSYCHIC),
        .catchRate = 45,
    #if P_UPDATED_EXP_YIELDS >= GEN_8
        .expYield = 300,
    #elif P_UPDATED_EXP_YIELDS >= GEN_5
        .expYield = 270,
    #else
        .expYield = 64,
    #endif
        .evYield_HP = 3,
        .itemCommon = ITEM_LUM_BERRY,
        .itemRare = ITEM_LUM_BERRY,
        .genderRatio = MON_GENDERLESS,
        .eggCycles = 120,
        .friendship = 100,
        .growthRate = GROWTH_MEDIUM_SLOW,
        .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED),
        .abilities = { ABILITY_SYNCHRONIZE, ABILITY_NONE, ABILITY_NONE },
        .bodyColor = BODY_COLOR_PINK,
        .speciesName = _("Mew"),
        .cryId = CRY_MEW,
        .natDexNum = NATIONAL_DEX_MEW,
        .categoryName = _("New Species"),
        .height = 4,
        .weight = 40,
        .description = COMPOUND_STRING(
            "A Mew is said to possess the genes of all\n"
            "Pokémon. It is capable of making itself\n"
            "invisible at will, so it entirely avoids\n"
            "notice even if it approaches people."),
        .pokemonScale = 457,
        .pokemonOffset = -2,
        .trainerScale = 256,
        .trainerOffset = 0,
        .frontPic = gMonFrontPic_Mew,
        .frontPicSize = P_GBA_STYLE_SPECIES_GFX ? MON_COORDS_SIZE(40, 40) : MON_COORDS_SIZE(64, 48),
        .frontPicYOffset = P_GBA_STYLE_SPECIES_GFX ? 13 : 9,
        .frontAnimFrames = sAnims_Mew,
        .frontAnimId = P_GBA_STYLE_SPECIES_GFX ? ANIM_SWING_CONVEX : ANIM_ZIGZAG_SLOW,
        .enemyMonElevation = P_GBA_STYLE_SPECIES_GFX ? 8 : 11,
        .backPic = gMonBackPic_Mew,
        .backPicSize = P_GBA_STYLE_SPECIES_GFX ? MON_COORDS_SIZE(48, 48) : MON_COORDS_SIZE(64, 64),
        .backPicYOffset = P_GBA_STYLE_SPECIES_GFX ? 8 : 0,
        .backAnimId = BACK_ANIM_CONCAVE_ARC_SMALL,
        .palette = gMonPalette_Mew,
        .shinyPalette = gMonShinyPalette_Mew,
        .iconSprite = gMonIcon_Mew,
        .iconPalIndex = 0,
        SHADOW(0, 13, SHADOW_SIZE_S)
        FOOTPRINT(Mew)
        OVERWORLD(
            sPicTable_Mew,
            SIZE_32x32,
            SHADOW_SIZE_M,
            TRACKS_NONE,
            gOverworldPalette_Mew,
            gShinyOverworldPalette_Mew
        )
        .isMythical = TRUE,
        .isFrontierBanned = TRUE,
        .perfectIVCount = LEGENDARY_PERFECT_IV_COUNT,
        .levelUpLearnset = sMewLevelUpLearnset,
        .teachableLearnset = sMewTeachableLearnset,
    },

That's a lot of stuff! But don't worry, we'll go through it step by step throughout the tutorial (and it's miles better than having this same data through 20+ files like it used to be).

We'll start by adding the self-explanatory data that's also present in pret's vanilla structure:

3. Define its basic species information

Edit src/data/pokemon/species_info.h:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     [SPECIES_NONE] = {0},
     ...

     [SPECIES_EGG] =
     {
         FRONT_PIC(Egg, 24, 24),
         .frontPicYOffset = 20,
         .backPic = gMonFrontPic_Egg,
         .backPicSize = MON_COORDS_SIZE(24, 24),
         .backPicYOffset = 20,
         .palette = gMonPalette_Egg,
         .shinyPalette = gMonPalette_Egg,
         ICON(Egg, 1),
     },

+    [SPECIES_MEWTHREE] =
+    {
+       .baseHP        = 106,
+       .baseAttack    = 150,
+       .baseDefense   = 70,
+       .baseSpeed     = 140,
+       .baseSpAttack  = 194,
+       .baseSpDefense = 120,
+       .types = MON_TYPES(TYPE_PSYCHIC),
+       .catchRate = 3,
+       .expYield = 255,
+       .evYield_SpAttack  = 3,
+       .genderRatio = MON_GENDERLESS,
+       .eggCycles = 120,
+       .friendship = 0,
+       .growthRate = GROWTH_SLOW,
+       .eggGroups = MON_EGG_GROUPS(EGG_GROUP_NO_EGGS_DISCOVERED),
+       .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_NONE },
+       .bodyColor = BODY_COLOR_PURPLE,
+    },
 };

The . is the structure reference operator in C to refer to the member object of the structure SpeciesInfo.

  • baseHP, baseAttack, baseDefense, baseSpeed, baseSpAttack and baseSpDefense are the base stats. They can't go higher than 255.
  • types is using the macro MON_TYPES as a helper function for formatting so that only one type has to be input for species with a single type.
    • To add a species with 2 types, use the format MON_TYPES(TYPE_PSYCHIC, TYPE_NORMAL).
  • catchRate is how likely it is to catch a Pokémon, the lower the value, the harder it is to catch. Legendaries generally have a catch rate of 3, so we put that here.
  • expYield is the base amount of experience that a Pokémon gives when defeated/caught. In vanilla, this value caps at 255, but we've increased it to a maximum of 65535 accomodate later gen's higher experience yields. (The highest official value is Blissey's with 608, so going beyond this point may cause exponential gains that could break the system 😱)
    • If you noticed, Mew's had some #ifs, #elifs and #endif around it. This is because its yield has changed over time, and we let you choose which ones you want. This is not relevant to our Mewthree however, so we can just put a single .expYield = 255, line here.
  • evYield_HP, evYield_Attack, evYield_Defense, evYield_Speed, evYield_SpAttack and evYield_SpDefense are how many EVs does the Pokémon give when they're caught. Each of these fields can have a value of 3 at most. Officially, no Pokémon give out more than 3 EVs total, with them being determined by their evolution stage (eg, Pichu, Pikachu and Raichu give 1, 2 and 3 Speed EVs respectively), and they tend to be associated with its higher stats. Since our Mewthree is a Special Attack monster, we'll be consistent and make it give out 3 Special Attack EVs, but you're always free to assign whatever you feel like :)
    • Notice that the other evYield fields are not there. In C, numbers in a struct default to 0, so if we don't specify them, they'll be 0 all around! Less lines to worry about :D
  • itemCommon and itemRare are used to determine what items is the Pokémon holding when encountering it in the wild.
    • 50% for itemCommon and 5% for itemRare (boosted to 60%/20% when the first mon in the party has Compound Eyes or Super Luck)
    • If they're both set as the same item, the item has a 100% chance of appearing.
  • genderRatio is a fun one.
    • There are 4 ways of handling this
      • PERCENT_FEMALE is what most Pokémon use, where you define how likely it's gonna be female. It supports decimals, so you can put PERCENT_FEMALE(12.5) to have a 1 in 8 chance of your mon to be female.
      • MON_MALE guarantees that all mon of this species will be male (eg. Tauros)
      • MON_FEMALE guarantees that all mon of this species will be female (eg. Miltank)
      • MON_GENDERLESS makes your species genderless, unable to breed with anything but Ditto to produce eggs. Most Legendaries are this, so we'll be chosing this as Mewthree's gender ratio.
    • When working with evolution lines and don't want their genders to change after evolving, be sure that their gender ratios match their stages and evolution methods. Azurill is the only case where there's a mismatch, causing 1/3 of all Azurill to change from Female to Male.
    • You might be wondering why some species have multiple defines for their genders, like SPECIES_MEOWSTIC_(FE)MALE. This is because those species have different stats and data from each other, so they're defined internally as different forms with MON_MALE and MON_FEMALE as gender ratios. If your species evolves depending on its gender and the evolutions have different stats, be sure to apply the correct evolution method!
  • eggCycles determines how fast an egg of this species will hatch. Doesn't matter much for evolved species or those that can't lay eggs, but we add the field here just in case.
  • friendship determines the amount of friendship of the mon when you catch it. Most Pokémon use STANDARD_FRIENDSHIP, but this creature of chaos does not want to be your friend, starting with 0.
  • growthRate determines the amounts of experience required to reach each level. Go here for more info.
    • This should be consistent across evolution lines, otherwise levels could change upon evolution.
  • eggGroups are used for breed compatibility. Most Legendaries and Mythicals have the EGG_GROUP_NO_EGGS_DISCOVERED group, and so does our Mewthree. Go here for more info.
    • This is using the helper macro MON_EGG_GROUPS.
  • abilities determines the potential abilites of our species. Notice how I also set the ability to ABILITY_INSOMNIA, so our little monster doesn't even need to sleep anymore. You can find the abilities for example here include/constants/abilities.h.
    • When both slot 1 and 2 are defined as not being ABILITY_NONE, their starting ability will be decided on a coin flip using their personality. They can later be changed using an Ability Capsule.
      • Certain Pokémon such as Zygarde and Rockruff have different forms to add additional abilities. As such, they cannot be changed using an Ability Capsule (though the Zygarde Cube can change Zygarde's ability by changing them to their corresponding form)
    • The 3rd slot is for Hidden Abilities. If defined as ABILITY_NONE, it will default to Slot 1 (eg. Metapod doesn't have a Hidden Ability, but Caterpie and Butterfree do). Go here and here for more info.
      • If the array is defined as {ABILITY_1, ABILITY_2}, the Hidden Ability is set as ABILITY_NONE.
  • bodyColor is used in the Pokédex as a search filter.
  • noFlip is used in to prevent front sprites from being flipped horizontally and cause weird issues, like Clawitzer's big claw changing sides.

That's all the basic fields present in vanilla emerald, so now let's take a look at the new fields added by the expansion.

4. Species Name

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .bodyColor = BODY_COLOR_PURPLE,
+       .speciesName = _("Mewthree"),
    },
 };

The _() underscore function doesn't really exist - it's a convention borrowed from GNU gettext to let preproc know this is text to be converted to the custom encoding used by the Gen 3 Pokemon games.

5. Define its cry

Time for audio! We first need to convert an existing audio file to the format supported by the expansion.

Most formats are supported for conversion, but for simplicity's sake, we're gonna use an mp3 file.

Now, let's copy the file to the sound/direct_sound_samples/cries folder. Once that's done, let's run the following command:

ffmpeg -i sound/direct_sound_samples/cries/mewthree.mp3 -c:a pcm_s8 -ac 1 -ar 13379 sound/direct_sound_samples/cries/mewthree.aif

This will convert your audio file to .aif, which is what's read by the compiler.

Let's add the cry to the ROM via sound/direct_sound_data.inc.

.if P_FAMILY_PECHARUNT == TRUE
	.align 2
Cry_Pecharunt::
	.incbin "sound/direct_sound_samples/cries/pecharunt.bin"
.endif @ P_FAMILY_PECHARUNT

+	.align 2
+Cry_Mewthree::
+	.incbin "sound/direct_sound_samples/cries/mewthree.bin"

Then we add the cry ID to include/constants/cries.h:

enum {
    CRY_NONE,
    ...
#if P_FAMILY_TERAPAGOS
    CRY_TERAPAGOS,
#endif //P_FAMILY_TERAPAGOS
#if P_FAMILY_PECHARUNT
    CRY_PECHARUNT,
#endif //P_FAMILY_PECHARUNT
+   CRY_MEWTHREE,
    CRY_COUNT,
};

And then link it in sound/cry_tables.inc. cry_reverse in particular is for reversed cries used by moves such as Growl. The order of these two tables should match the order of the cry IDs, otherwise they'll be shifted.

	cry Cry_Terapagos
	cry Cry_Pecharunt
+	cry Cry_Mewthree
	cry_reverse Cry_Terapagos
	cry_reverse Cry_Pecharunt
+	cry_reverse Cry_Mewthree

Lastly, we add the cry to our species entry

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .speciesName = _("Mewthree"),
+       .cryId = CRY_MEWTHREE,
    },
 };

And let's see how it sounds in-game:

https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/4f7667db-4db9-4bfd-a8dd-ece26f09f327

Good! Our monster now has a mighty roar!

You can now delete the mp3 from the cries folder now once you made sure that the cry sounds like how you want it to.

6. Define its Pokédex entry

First, we will need to add new index constants for its Pokédex entry. The index constants are divided into the Hoenn Pokédex, which contains all Pokémon native to the Hoenn region, and the National Pokédex containing all known Pokémon, which can be received after entering the hall of fame for the first time.

Edit include/constants/pokedex.h:

// National Pokedex order
enum {
    NATIONAL_DEX_NONE,
    // Kanto
    NATIONAL_DEX_BULBASAUR,
...
    NATIONAL_DEX_PECHARUNT,
+   NATIONAL_DEX_MEWTHREE,
};
 #define KANTO_DEX_COUNT     NATIONAL_DEX_MEW
 #define JOHTO_DEX_COUNT     NATIONAL_DEX_CELEBI

#if P_GEN_9_POKEMON == TRUE
-   #define NATIONAL_DEX_COUNT  NATIONAL_DEX_PECHARUNT
+   #define NATIONAL_DEX_COUNT  NATIONAL_DEX_MEWTHREE

Do keep in mind that if you intend to add your new species to the Hoenn Dex, you'll also want to add a HOENN_DEX constant for it and give it a HOENN_TO_NATIONAL member, like this:

// Hoenn Pokedex order
enum {
    HOENN_DEX_NONE,
    HOENN_DEX_TREECKO,
...
    HOENN_DEX_DEOXYS,
+   HOENN_DEX_MEWTHREE,
};

- #define HOENN_DEX_COUNT (HOENN_DEX_DEOXYS + 1)
+ #define HOENN_DEX_COUNT (HOENN_DEX_MEWTHREE + 1)

Edit src/pokemon.c:

 const u16 sHoennToNationalOrder[NUM_SPECIES] = // Assigns Hoenn Dex Pokémon (Using National Dex Index)
 {
     HOENN_TO_NATIONAL(TREECKO),
     ...
     HOENN_TO_NATIONAL(DEOXYS),
+    HOENN_TO_NATIONAL(MEWTHREE),
 };

Now we can add the number and entry to our Mewthree:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .cryId = CRY_MEWTHREE,
+       .natDexNum = NATIONAL_DEX_MEWTHREE,
+       .categoryName = _("New Species"),
+       .height = 15,
+       .weight = 330,
+       .description = COMPOUND_STRING(
+           "The rumors became true.\n"
+           "This is Mew's final form.\n"
+           "Its power level is over 9000.\n"
+           "Has science gone too far?"),
+       .pokemonScale = 256,
+       .pokemonOffset = 0,
+       .trainerScale = 290,
+       .trainerOffset = 2,
    },
 };

image

The values pokemonScale, pokemonOffset, trainerScale and trainerOffset are used for the height comparison figure in the Pokédex.

height and weight are specified in decimeters and hectograms respectively (which are meters and kilograms multiplied by 10, so 2.5 meters are 25 decimeters).

In Pokémon Emerald, you can sort the Pokédex by name, height or weight. Apparently, the Pokémon order is hardcoded in the game files and not calculated from their data. Therefore we have to include our new Pokémon species at the right places. While the correct position for the alphabetical order is easy to find, it can become quite tedious for height and weight, so we added comments to the listings in order help out were they should fit.

Edit src/data/pokemon/pokedex_orders.h:

 const u16 gPokedexOrder_Alphabetical[] =
 {
     ...
     NATIONAL_DEX_MEW,
+    NATIONAL_DEX_MEWTHREE,
     NATIONAL_DEX_MEWTWO,
     ...
 };

 const u16 gPokedexOrder_Weight[] =
 {
     ...
     // 72.8 lbs / 33.0 kg
     //NATIONAL_DEX_MEWTWO_MEGA_Y,
     NATIONAL_DEX_ESCAVALIER,
     NATIONAL_DEX_FRILLISH,
     NATIONAL_DEX_DURANT,
     NATIONAL_DEX_CINDERACE,
+    NATIONAL_DEX_MEWTHREE,
     //NATIONAL_DEX_PERSIAN_ALOLAN,
     NATIONAL_DEX_TOEDSCOOL,
     // 73.4 lbs / 33.3 kg
     NATIONAL_DEX_DUGTRIO,
     ...
 };

 const u16 gPokedexOrder_Height[] =
 {
     ...
     // 4'11" / 1.5m
     ...
     NATIONAL_DEX_GLIMMORA,
     NATIONAL_DEX_WO_CHIEN,
     NATIONAL_DEX_IRON_LEAVES,
     NATIONAL_DEX_IRON_BOULDER,
+    NATIONAL_DEX_MEWTHREE,
    // 5'03" / 1.6m
     ...
 };

mGBA_lUBfmFEKUx

The Graphics

We will start by copying the following files for Mew (not Mewtwo) and rename it to mewthree.

cp -r graphics/pokemon/mew/. graphics/pokemon/mewthree

We aren't copying Mewtwo's folder because he has those pesky Mega Evolutions that will get in the way of what we're doing, so our sample will need to be pure from the source.

1. Edit the sprites

Let's edit the sprites. Start your favourite image editor (I recommend Aseprite or its free alternative, Libresprite) and change anim_front.png and back.png to meet your expectations.

Make sure that you are using the indexed mode and you have limited yourself to 15 colors!

Put the RGB values of your colors into normal.pal between the first and the last color and the RGB values for the shiny version into shiny.pal. Edit footprint.png using two colors in indexed mode, black and white. Finally, edit icon.png. Note: the icon will use one of 6 predefined palettes instead of normal.pal. Open an icon sprite and load one of the palettes to find out which palette suits your icon sprite best.

2. Add the sprites to the rom

Sadly, just putting the image files into the graphics folder is not enough. To use the sprites we have to register them, which is kind of tedious. First, create constants for the file paths. You'll want to add the constants for your species after the constants for the last valid species.

Edit src/data/graphics/pokemon.h:

#if P_FAMILY_PECHARUNT
    const u32 gMonFrontPic_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/front.4bpp.lz");
    const u32 gMonPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/normal.gbapal.lz");
    const u32 gMonBackPic_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/back.4bpp.lz");
    const u32 gMonShinyPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/shiny.gbapal.lz");
    const u8 gMonIcon_Pecharunt[] = INCBIN_U8("graphics/pokemon/pecharunt/icon.4bpp");
#if P_FOOTPRINTS
    const u8 gMonFootprint_Pecharunt[] = INCBIN_U8("graphics/pokemon/pecharunt/footprint.1bpp");
#endif //P_FOOTPRINTS
#if OW_POKEMON_OBJECT_EVENTS
    const u32 gObjectEventPic_Pecharunt[] = INCBIN_COMP("graphics/pokemon/pecharunt/overworld.4bpp");
#if OW_PKMN_OBJECTS_SHARE_PALETTES == FALSE
    const u32 gOverworldPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/overworld_normal.gbapal.lz");
    const u32 gShinyOverworldPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/overworld_shiny.gbapal.lz");
#endif //OW_PKMN_OBJECTS_SHARE_PALETTES
#endif //OW_POKEMON_OBJECT_EVENTS
#endif //P_FAMILY_PECHARUNT

    const u32 gMonFrontPic_Egg[] = INCBIN_U32("graphics/pokemon/egg/anim_front.4bpp.lz");
    const u32 gMonPalette_Egg[] = INCBIN_U32("graphics/pokemon/egg/normal.gbapal.lz");
    const u8 gMonIcon_Egg[] = INCBIN_U8("graphics/pokemon/egg/icon.4bpp");

+   const u32 gMonFrontPic_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/anim_front.4bpp.lz");
+   const u32 gMonBackPic_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/back.4bpp.lz");
+   const u32 gMonPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/normal.gbapal.lz");
+   const u32 gMonShinyPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/shiny.gbapal.lz");
+   const u8 gMonIcon_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/icon.4bpp");
+   const u8 gMonFootprint_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/footprint.1bpp");

Please note that Pecharunt, the Pokémon that should be above your insertion for the time being, reads a front.png sprite instead of an anim_front.png sprite. This is because currently, Pecharunt lacks a 2nd frame. If the front sprite sheet of your species uses 2 frames, you should use anim_front.

3. Add the animations to the rom

You can define the animation order, in which the sprites will be shown. The first number is the sprite index (so 0 or 1) and the second number is the number of frames the sprite will be visible.

Edit src/data/pokemon_graphics/front_pic_anims.h:

#if P_FAMILY_PECHARUNT
PLACEHOLDER_ANIM_SINGLE_FRAME(Pecharunt);
#endif //P_FAMILY_PECHARUNT

+static const union AnimCmd sAnim_Mewthree_1[] =
+{
+    ANIMCMD_FRAME(1, 30),
+    ANIMCMD_FRAME(0, 20),
+    ANIMCMD_END,
+};
#if P_FAMILY_PECHARUNT
SINGLE_ANIMATION(Pecharunt);
#endif //P_FAMILY_PECHARUNT
+SINGLE_ANIMATION(Mewthree);
SINGLE_ANIMATION(Egg);

You might be wondering what PLACEHOLDER_ANIM_SINGLE_FRAME is. Well, since Pecharun only has 1 frame, we use what's called a preprocessor macro to have in a single line what otherwise would've been this in the C file:

static const union AnimCmd sAnim_Pecharunt_1[] =
{
    ANIMCMD_FRAME(0, 1),
    ANIMCMD_END,
}

Instead, we can use the already established macro that does the same thing, replacing the value in parenthesis with what we want (in this case, Pecharunt):

#define PLACEHOLDER_ANIM_SINGLE_FRAME(name)     \
static const union AnimCmd sAnim_##name##_1[] = \
{                                               \
    ANIMCMD_FRAME(0, 1),                        \
    ANIMCMD_END,                                \
}

4. Linking graphic information to our Pokémon

Now that we have all the external data ready, we just need to add it to gSpeciesInfo plus the rest of the animation and graphical data that we want to use:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .pokemonScale = 256,
        .pokemonOffset = 0,
        .trainerScale = 290,
        .trainerOffset = 2,
+       .frontPic = gMonFrontPic_Mewthree,
+       .frontPicSize = MON_COORDS_SIZE(64, 64),
+       .frontPicYOffset = 0,
+       .frontAnimFrames = sAnims_Mewthree,
+       .frontAnimId = ANIM_GROW_VIBRATE,
+       .frontAnimDelay = 15,
+       .enemyMonElevation = 6,
+       .backPic = gMonBackPic_Mewthree,
+       .backPicSize = MON_COORDS_SIZE(64, 64),
+       .backPicYOffset = 0,
+       .backAnimId = BACK_ANIM_CONCAVE_ARC_SMALL,
+       .palette = gMonPalette_Mewthree,
+       .shinyPalette = gMonShinyPalette_Mewthree,
        .iconSprite = gMonIcon_Mewthree,
        .iconPalIndex = 2,
+       FOOTPRINT(Mewthree)
    },
 };

Let's explain each of these:

  • frontPic:
    • Used to reference the front sprite, so in this case, we call for gMonFrontPic_Mewthree.
  • frontPicSize:
    • The two values (width and height) are used for defining the non-empty size of the front sprite, which is used in move animations. If you're unsure of the values, you can leave them both as 64.
  • frontPicYOffset:
    • Used to define what Y position the sprite sits at. This is used to set where they'd be "grounded". For the shadow, see enemyMonElevation.
  • frontAnimFrames:
    • We link our animation frame animations that we defined earlier here.
  • frontAnimId:
  • frontAnimDelay:
    • Sets a delay in frame count between when the Pokémon appears and when the animation starts.
  • enemyMonElevation:
    • Used to determine the altitude from the ground. Any value above 0 will show a shadow under the Pokémon, to signify that they're floating.
  • backPic:
    • Used to reference the back sprite, so in this case, we call for gMonBackPic_Mewthree.
  • backPicSize:
    • The two values (width and height) are used for defining the non-empty size of the back sprite, which is used in move animations. If you're unsure of the values, you can leave them both as 64.
    • NOTE: Mew has a tarnary switch here in order to change values depending on if a config option is set for displaying th original Gen 3 sprites.
  • backPicYOffset:
    • Used to define what Y position of the back sprite. When working with the animation debug menu, we recommend aligning the back sprite to the white background, as it was designed to properyly align with the real battle layout.
  • backAnimId:
    • Like frontAnimId except for the back sprites and them being a single frame. The IDs listed here are used to represent 3 different animations that happen based on the the Pokémon's nature.
  • palette:
    • Used to reference the non-shiny palette, so in this case, we call for gMonPalette_Mewthree.
  • shinyPalette:
    • Used to reference the shiny palette, so in this case, we call for gMonShinyPalette_Mewthree.
  • iconSprite:
    • Used to reference the icon sprite, so in this case, we call for gMonIcon_Mewthree.
  • iconPalIndex:
    • Here, you can choose between the six icon palettes; 0, 1, 2, 3, 4 and 5. All of them located in graphics/pokemon/icon_palettes.
  • FOOTPRINT
    • We made this single field into a macro so that they can be ignored when P_FOOTPRINTS is set to false. It's also why we don't have an "," after calling it like the other macros (we add it as part of the macro itself).
      #if P_FOOTPRINTS
      #define FOOTPRINT(sprite) .footprint = gMonFootprint_## sprite,
      #else
      #define FOOTPRINT(sprite)
      #endif
      

The Data - Part 2

We're almost there just a bit left!

1. Species Flags

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_NONE },
        .bodyColor = BODY_COLOR_PURPLE,
+       .isLegendary = TRUE,
+       .perfectIVCount = LEGENDARY_PERFECT_IV_COUNT,
    },
 };

Each species flag provides properties to the species:

  • isLegendary:
    • Does nothing.
  • isMythical:
    • Is skipped during Pokédex evaluations.
      • Unless it also has the dexForceRequired flag.
    • Cannot obtain Gigantamax factor via ToggleGigantamaxFactor.
  • isUltraBeast:
    • Beast Ball's multiplier is set to x5 for this species.
      • All other ball multipliers are set to x0.1.
  • isParadox (previously isParadoxForm):
    • Makes it so that Booster Energy cannot be knocked off.
  • isTotem:
    • Does nothing.
  • isMegaEvolution:
    • A Mega indicator is added to the battle box indicating that they're Mega Evolved.
    • The species doesn't receive affection benefits.
    • Required when adding new Mega Evolutions.
  • isPrimalReversion:
    • A Primal Reversion indicator (Alpha or Omega for Kyogre/Groudon respectively) is added to the battle box indicating that they're Primal Reverted.
    • Required when adding new Primal Reversions.
  • isUltraBurst:
    • Required when adding new Ultra Burst forms.
  • isGigantamax:
    • Used to determine if Gigantamax forms should have their GMax moves or not.
    • Required when adding new Gigantamax forms.
  • isAlolanForm, isGalarianForm, isHisuianForm, isPaldeanForm:
    • In the future, these will be used to determine breeding offspring from different based on their region.
  • cannotBeTraded:
    • This species cannot be traded away (like Black/White Kyurem).
  • perfectIVCount:
    • Guarantees that the number of IVs specified here will be perfect.
  • tmIlliterate:
    • This species will be unable to learn the universal moves.
  • isFrontierBanned:
    • This species will be unable to enter Battle Frontier facilities. Replaces gFrontierBannedSpecies.

2. Delimit the moveset

Let's begin with the moves that can be learned by leveling up.

Append to src/data/pokemon/level_up_learnsets/gen_9.h: NOTE: You can ignore the warning at the top of the file if you're just adding moves to Pokemon.

#if P_FAMILY_PECHARUNT
static const struct LevelUpMove sPecharuntLevelUpLearnset[] = {
    LEVEL_UP_MOVE( 1, MOVE_SMOG),
    LEVEL_UP_MOVE( 1, MOVE_POISON_GAS),
    LEVEL_UP_MOVE( 1, MOVE_MEMENTO),
    LEVEL_UP_MOVE( 1, MOVE_ASTONISH),
    LEVEL_UP_MOVE( 8, MOVE_WITHDRAW),
    LEVEL_UP_MOVE(16, MOVE_DESTINY_BOND),
    LEVEL_UP_MOVE(24, MOVE_FAKE_TEARS),
    LEVEL_UP_MOVE(32, MOVE_PARTING_SHOT),
    LEVEL_UP_MOVE(40, MOVE_SHADOW_BALL),
    LEVEL_UP_MOVE(48, MOVE_MALIGNANT_CHAIN),
    LEVEL_UP_MOVE(56, MOVE_TOXIC),
    LEVEL_UP_MOVE(64, MOVE_NASTY_PLOT),
    LEVEL_UP_MOVE(72, MOVE_RECOVER),
    LEVEL_UP_END
};
#endif

+static const struct LevelUpMove sMewthreeLevelUpLearnset[] = {
+   LEVEL_UP_MOVE( 1, MOVE_CONFUSION),
+   LEVEL_UP_MOVE( 1, MOVE_DISABLE),
+   LEVEL_UP_MOVE(11, MOVE_BARRIER),
+   LEVEL_UP_MOVE(22, MOVE_SWIFT),
+   LEVEL_UP_MOVE(33, MOVE_PSYCH_UP),
+   LEVEL_UP_MOVE(44, MOVE_FUTURE_SIGHT),
+   LEVEL_UP_MOVE(55, MOVE_MIST),
+   LEVEL_UP_MOVE(66, MOVE_PSYCHIC),
+   LEVEL_UP_MOVE(77, MOVE_AMNESIA),
+   LEVEL_UP_MOVE(88, MOVE_RECOVER),
+   LEVEL_UP_MOVE(99, MOVE_SAFEGUARD),
+   LEVEL_UP_END
+};

NOTE: If P_LVL_UP_LEARNSETS is not set to something equal to GEN_9, the file to be edited will change to what's specified.

Again, we need to register the learnset in gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .palette = gMonPalette_Mewthree,
        .shinyPalette = gMonShinyPalette_Mewthree,
        .iconSprite = gMonIcon_Mewthree,
        .iconPalIndex = 2,
+       .levelUpLearnset = sMewthreeLevelUpLearnset,
    },
 };

Next we need to specify which moves can be taught via TM, HM, or Move Tutor.

Append to src/data/pokemon/teachable_learnsets.h:

#if P_FAMILY_PECHARUNT
static const u16 sPecharuntTeachableLearnset[] = {
    ...
    MOVE_UNAVAILABLE,
};
#endif //P_FAMILY_PECHARUNT

+static const u16 sMewthreeTeachableLearnset[] = {
+   MOVE_FOCUS_PUNCH,
+   MOVE_WATER_PULSE,
+   MOVE_CALM_MIND,
+   MOVE_TOXIC,
+   MOVE_HAIL,
+   MOVE_BULK_UP,
+   MOVE_HIDDEN_POWER,
+   MOVE_SUNNY_DAY,
+   MOVE_TAUNT,
+   MOVE_ICE_BEAM,
+   MOVE_BLIZZARD,
+   MOVE_HYPER_BEAM,
+   MOVE_LIGHT_SCREEN,
+   MOVE_PROTECT,
+   MOVE_RAIN_DANCE,
+   MOVE_SAFEGUARD,
+   MOVE_FRUSTRATION,
+   MOVE_SOLAR_BEAM,
+   MOVE_IRON_TAIL,
+   MOVE_THUNDERBOLT,
+   MOVE_THUNDER,
+   MOVE_EARTHQUAKE,
+   MOVE_RETURN,
+   MOVE_PSYCHIC,
+   MOVE_SHADOW_BALL,
+   MOVE_BRICK_BREAK,
+   MOVE_DOUBLE_TEAM,
+   MOVE_REFLECT,
+   MOVE_SHOCK_WAVE,
+   MOVE_FLAMETHROWER,
+   MOVE_SANDSTORM,
+   MOVE_FIRE_BLAST,
+   MOVE_ROCK_TOMB,
+   MOVE_AERIAL_ACE,
+   MOVE_TORMENT,
+   MOVE_FACADE,
+   MOVE_SECRET_POWER,
+   MOVE_REST,
+   MOVE_SKILL_SWAP,
+   MOVE_SNATCH,
+   MOVE_STRENGTH,
+   MOVE_FLASH,
+   MOVE_ROCK_SMASH,
+   MOVE_MEGA_PUNCH,
+   MOVE_MEGA_KICK,
+   MOVE_BODY_SLAM,
+   MOVE_DOUBLE_EDGE,
+   MOVE_COUNTER,
+   MOVE_SEISMIC_TOSS,
+   MOVE_MIMIC,
+   MOVE_METRONOME,
+   MOVE_DREAM_EATER,
+   MOVE_THUNDER_WAVE,
+   MOVE_SUBSTITUTE,
+   MOVE_DYNAMIC_PUNCH,
+   MOVE_PSYCH_UP,
+   MOVE_SNORE,
+   MOVE_ICY_WIND,
+   MOVE_ENDURE,
+   MOVE_MUD_SLAP,
+   MOVE_ICE_PUNCH,
+   MOVE_SWAGGER,
+   MOVE_SLEEP_TALK,
+   MOVE_SWIFT,
+   MOVE_THUNDER_PUNCH,
+   MOVE_FIRE_PUNCH,
+   MOVE_UNAVAILABLE, // This is required to determine where the array ends.
+};
#endif

NOTE: At the top of this file, you will probably see this warning:

//
// DO NOT MODIFY THIS FILE! It is auto-generated from tools/learnset_helpers/teachable.py`
//

The expansion includes a tool called the learnset helper, which aims to automate the generation of valid teachable moves. At the time of writing, this tool only supports generating TM and Tutor learnsets. However, in the future it may be expanded to deal with level up learnsets and egg moves.

Ignore the warning shown above the first time you're adding your teachable moves (as otherwise the compiler will complain about the array not existing), but in the future (if you're using the learnset helper) simply edit what teachable moves your Pokémon can learn in one of the JSON files found in tools/learnset_helpers/porymoves_files. It doesn't really matter which one you add your new Pokémon to, as the tool pulls from all of the files in this folder.

The learnset helper is useful if you plan on changing and/or increasing the available TMs and Tutor moves in your game. As an example, Bulbasaur learns Rage by TM in Red/Blue/Yellow, but in Emerald this TM does not exist. But since tools/learnset_helpers/porymoves_files/rby.json defines "MOVE_RAGE" as a TM move for Bulbasaur, that move would automatically be added to the sBulbasaurTeachableLearnset array if you were to add a Rage TM at any point.

The learnset helper can be toggled on/off in include/config/pokemon.h:

// Learnset helper toggles
#define P_LEARNSET_HELPER_TEACHABLE TRUE        // If TRUE, teachable_learnsets.h will be populated by tools/learnset_helpers/teachable.py using the included JSON files based on available TMs and tutors.

Once more, we need to register the learnset in gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        FOOTPRINT(Mewthree)
        .levelUpLearnset = sMewthreeLevelUpLearnset,
+       .teachableLearnset = sMewthreeTeachableLearnset,
    },
 };

If you want to create a Pokémon which can breed, you will need to edit src/data/pokemon/egg_moves.h.

3. Define the Evolutions

We want Mewthree to evolve from Mewtwo by reaching level 100.

Edit gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTWO] =
     {
        ...
        FOOTPRINT(Mewtwo)
        .isLegendary = TRUE,
        .levelUpLearnset = sMewtwoLevelUpLearnset,
        .teachableLearnset = sMewtwoTeachableLearnset,
        .formSpeciesIdTable = sMewtwoFormSpeciesIdTable,
        .formChangeTable = sMewtwoFormChangeTable,
+       .evolutions = EVOLUTION({EVO_LEVEL, 100, SPECIES_MEWTHREE}),
    },
 };

4. Make it appear!

Now Mewthree really does slumber in the games code - but we won't know until we make him appear somewhere! The legend tells that Mewthree is hiding somewhere in Petalburg Woods...

Edit src/data/wild_encounters.json:

         {
           "map": "MAP_PETALBURG_WOODS",
           "base_label": "gPetalburgWoods",
           "land_mons": {
             "encounter_rate": 20,
             "mons": [
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_POOCHYENA"
               },
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_WURMPLE"
               },
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_SHROOMISH"
               },
               {
-                "min_level": 6,
-                "max_level": 6,
-                "species": "SPECIES_POOCHYENA"
+                "min_level": 5,
+                "max_level": 5,
+                "species": "SPECIES_MEWTHREE"
               },
               ...
        }

Congratulations, you have created your own personal pocket monster! You may call yourself a mad scientist now.

Optional data

Now that you now have all the essential pieces to create a base species, there are some aspects that you might want to know if you want to do other stuff with your custom Pokémon.

1. Form tables

Found in src/data/pokemon/form_species_tables.h.

These are introduced to have a reference of what forms correspond to what Species of Pokémon. For example, we have Pikachu's table:

#if P_FAMILY_PIKACHU
static const u16 sPikachuFormSpeciesIdTable[] = {
    SPECIES_PIKACHU,
    SPECIES_PIKACHU_COSPLAY,
    SPECIES_PIKACHU_ROCK_STAR,
    SPECIES_PIKACHU_BELLE,
    SPECIES_PIKACHU_POP_STAR,
    SPECIES_PIKACHU_PH_D,
    SPECIES_PIKACHU_LIBRE,
    SPECIES_PIKACHU_ORIGINAL_CAP,
    SPECIES_PIKACHU_HOENN_CAP,
    SPECIES_PIKACHU_SINNOH_CAP,
    SPECIES_PIKACHU_UNOVA_CAP,
    SPECIES_PIKACHU_KALOS_CAP,
    SPECIES_PIKACHU_ALOLA_CAP,
    SPECIES_PIKACHU_PARTNER_CAP,
    SPECIES_PIKACHU_WORLD_CAP,
    FORM_SPECIES_END,
};
#endif //P_FAMILY_PIKACHU

We register the table each form entry in gSpeciesInfo.

    [SPECIES_PIKACHU] =
    {
        ...
        .teachableLearnset = sPikachuTeachableLearnset,
+       .formSpeciesIdTable = sPikachuFormSpeciesIdTable,
        .evolutions = EVOLUTION({EVO_ITEM, ITEM_THUNDER_STONE, SPECIES_RAICHU},
                                {EVO_NONE, 0, SPECIES_RAICHU_ALOLAN}),
    },

    [SPECIES_PIKACHU_COSPLAY] =
    {
        ...
        .teachableLearnset = sPikachuTeachableLearnset,
+       .formSpeciesIdTable = sPikachuFormSpeciesIdTable,
    },

...and so on.

What this allows us to do is to be able to get all forms of a Pokémon in our code by using the GetSpeciesFormTable function.

For example, in the HGSS dex, it lets us browse between the entries of every form available.:

image image

In addition, we have the GET_BASE_SPECIES_ID macro, which returns the first entry of the table (or return the species itself if it doesn't have a table registered). With this, you can check if a Pokémon is any form of a species. For example, making it so that the Light Ball affects all Pikachu forms:

    case HOLD_EFFECT_LIGHT_BALL:
        if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_PIKACHU && IS_MOVE_SPECIAL(move))
            modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0));
        break;

2. Form change tables

Found in src/data/pokemon/form_species_tables.h.

These tables, unlike the regular form tables, registers how Pokémon can switch between forms.

#if P_FAMILY_GASTLY
static const struct FormChange sGengarFormChangeTable[] = {
    {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GENGAR_MEGA, ITEM_GENGARITE},
    {FORM_CHANGE_BATTLE_GIGANTAMAX,          SPECIES_GENGAR_GIGANTAMAX},
    {FORM_CHANGE_TERMINATOR},
};
#endif //P_FAMILY_GASTLY

The first value is the type of form change. In the case of Gengar, we have both Mega Evolution and Gigantamax form changes.

The second value is the target form, to which the Pokémon will change into.

Values after that are referred as arguments, and needs to be put there depends on the type of form change, detailed in include/constants/form_change_types.h.

3. Gender differences

mGBA_Wq5cbDkNZG

You may have seen that there's a couple of duplicate fields with a "Female" suffix.

    [SPECIES_FRILLISH] =
    {
        ...
        .frontPic = gMonFrontPic_Frillish,
+       .frontPicFemale = gMonFrontPic_FrillishF,
        .frontPicSize = MON_COORDS_SIZE(56, 56),
+       .frontPicSizeFemale = MON_COORDS_SIZE(56, 56),
        .frontPicYOffset = 5,
        .frontAnimFrames = sAnims_Frillish,
        .frontAnimId = ANIM_RISING_WOBBLE,
        .backPic = gMonBackPic_Frillish,
+       .backPicFemale = gMonBackPic_FrillishF,
        .backPicSize = MON_COORDS_SIZE(40, 56),
+       .backPicSizeFemale = MON_COORDS_SIZE(40, 56),
        .backPicYOffset = 7,
        .backAnimId = BACK_ANIM_CONVEX_DOUBLE_ARC,
        .palette = gMonPalette_Frillish,
+       .paletteFemale = gMonPalette_FrillishF,
        .shinyPalette = gMonShinyPalette_Frillish,
+       .shinyPaletteFemale = gMonShinyPalette_FrillishF,
        .iconSprite = gMonIcon_Frillish,
+       .iconSpriteFemale = gMonIcon_FrillishF,
        .iconPalIndex = 0,
+       .iconPalIndexFemale = 1,
        FOOTPRINT(Frillish)
        .levelUpLearnset = sFrillishLevelUpLearnset,
        .teachableLearnset = sFrillishTeachableLearnset,
        .evolutions = EVOLUTION({EVO_LEVEL, 40, SPECIES_JELLICENT}),
    },

These are used to change the graphics of the Pokémon if they're female. If they're not registered, they default to the male values.

However, iconPalIndexFemale is a special case, where it's doesn't read the male icon palette if its iconSpriteFemale is set, so if you're setting a female icon, be sure to set their palette index as well.

4. Overworld Data

mGBA_4iqvhhSltK

If you have OW_POKEMON_OBJECT_EVENTS in your hack, you can add Overworld of your new species by following these steps:

First, since you copied the contents from Mew's folder previously, you should also have copied its overworld sprites. Edit those to your liking, as we have done before, making sure to update the palettes

Secondly, in src/data/graphics/pokemon.h, add the following:

    const u8 gMonIcon_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/icon.4bpp");
    const u8 gMonFootprint_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/footprint.1bpp");
+   const u32 gObjectEventPic_Mewthree[] = INCBIN_COMP("graphics/pokemon/mewthree/overworld.4bpp");
+   const u32 gOverworldPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/overworld_normal.gbapal.lz");
+   const u32 gShinyOverworldPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/overworld_shiny.gbapal.lz");

Thirdly, in spritesheet_rules.mk

$(POKEMONGFXDIR)/mewtwo/overworld.4bpp: %.4bpp: %.png
	$(GFX) $< $@ -mwidth 4 -mheight 4

+$(POKEMONGFXDIR)/mewthree/overworld.4bpp: %.4bpp: %.png
+	$(GFX) $< $@ -mwidth 4 -mheight 4

$(POKEMONGFXDIR)/mew/overworld.4bpp: %.4bpp: %.png
	$(GFX) $< $@ -mwidth 4 -mheight 4

Fourthly, in src/data/object_events/object_event_pic_tables_followers.h:

#if P_FAMILY_PECHARUNT
/*static const struct SpriteFrameImage sPicTable_Pecharunt[] = {
    overworld_ascending_frames(gObjectEventPic_Pecharunt, 4, 4),
};*/
#endif //P_FAMILY_PECHARUNT

+static const struct SpriteFrameImage sPicTable_Mewthree[] = {
+    overworld_ascending_frames(gObjectEventPic_Mewthree, 4, 4),
+};

And finally, in gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        FOOTPRINT(Mewthree)
+       OVERWORLD(
+           sPicTable_Mewthree,
+           SIZE_32x32,
+           SHADOW_SIZE_M,
+           TRACKS_FOOT,
+           gOverworldPalette_Mewthree,
+           gShinyOverworldPalette_Mewthree
+       )
        .levelUpLearnset = sMewthreeLevelUpLearnset,
        .teachableLearnset = sMewthreeTeachableLearnset,
    },
 };

Sprite Size

Depending on your species, you might want to use different sizes for it. For example, certain species known for being big like Steelix use sprites that fit a 64x64 frame instead of 32x32, and as such have SIZE_64x64 in their data instead of SIZE_32x32 to accomodate for them.

Also, in spritesheet_rules.mk, -mwidth and -mheight need to be set to 8 instead of 4 for such cases.

Shadows

Gen 4 style shadows are defined by the SHADOW macro which takes the following arguments:

  • X offset
  • Y offset
  • Shadow size You have 4 options for their shadow, between Small, Medium, Large and Extra Large:
  • SHADOW_SIZE_S
  • SHADOW_SIZE_M
  • SHADOW_SIZE_L
  • SHADOW_SIZE_XL_BATTLE_ONLY To make the Pokémon have no shadow, use the NO_SHADOW macro instead of SHADOW.

Tracks

You have 4 options for the tracks that your species will leave behind on sand.

  • TRACKS_NONE
  • TRACKS_FOOT sand_footprints
  • TRACKS_SLITHER slither_tracks
  • TRACKS_SPOT spot_tracks
  • TRACKS_BUG bug_tracks

...though technically you can also use TRACKS_BIKE_TIRE if you wish to.

bike_tire_tracks

This is a modified version of the original tutorial about adding new Pokémon species available in Pokeemerald's wiki.

Despite the persistent rumors about an incredibly strong third form of Mew hiding somewhere, it actually wasn't possible to catch it... OR WAS IT? In this tutorial, we will add a new Pokémon species to the game.

IMPORTANT: This tutorial applies to 1.9.x versions.

Changes compared to vanilla

The main things that the Expansion changes are listed here.

  • Still Front Pics (gMonStillFrontPic_YourPokemon) and by extension src/anim_mon_front_pics.c have been removed.
  • src/data/pokemon/cry_ids.h doesn't exist anymore.
  • You have 6 icon palettes available instead of the base 3.
  • Most tables that use SPECIES_x as indexes have been moved to gSpeciesInfo.

Content

Useful resources

You can open a sprite debug menu by pressing Select in a Pokémon's summary screen outside of battle.

mGBA_6WOo1TSlsn

The Data - Part 1

Our plan is as simple as it is brilliant: clone Mewtwo... and make it even stronger!

1. Declare a species constant

Our first step towards creating a new digital lifeform is to define its own species constant.

Edit include/constants/species.h:

 #define SPECIES_NONE                                    0
 #define SPECIES_BULBASAUR                               1
 ...
 #define SPECIES_EEVEE_PARTNER                           PLACEHOLDER_START + 54
+#define SPECIES_MEWTHREE                                PLACEHOLDER_START + 55

-#define GIGANTAMAX_START                                SPECIES_EEVEE_PARTNER
+#define GIGANTAMAX_START                                SPECIES_MEWTHREE

 // Gigantamax Forms
 #define SPECIES_VENUSAUR_GIGANTAMAX                     GIGANTAMAX_START + 1

This number is stored in a Pokémon's save structure. These should generally never change, otherwise your saved Pokémon species will change as well.

We add this before Gigantamax forms because they're temporary forms that shouldn't normally be saved into a Pokémon's save structure.

Now, let's see how it looks in-game!

image

Hmmm, something's not right...

Oh, I know! We need to add the rest of the data! Normally, the vanilla game would crash if we try to look up anything about Mewthree in this state, but the expansion defaults all of its data to SPECIES_NONE.

Now, let's see what needs to be done.

2. SpeciesInfo's structure

Now, to better understand Mewtwo, we also need to understand Mew. Let's look at its data.

    [SPECIES_MEW] =
    {
        .baseHP        = 100,
        .baseAttack    = 100,
        .baseDefense   = 100,
        .baseSpeed     = 100,
        .baseSpAttack  = 100,
        .baseSpDefense = 100,
        .types = { TYPE_PSYCHIC, TYPE_PSYCHIC },
        .catchRate = 45,
    #if P_UPDATED_EXP_YIELDS >= GEN_8
        .expYield = 300,
    #elif P_UPDATED_EXP_YIELDS >= GEN_5
        .expYield = 270,
    #else
        .expYield = 64,
    #endif
        .evYield_HP = 3,
        .itemCommon = ITEM_LUM_BERRY,
        .itemRare = ITEM_LUM_BERRY,
        .genderRatio = MON_GENDERLESS,
        .eggCycles = 120,
        .friendship = 100,
        .growthRate = GROWTH_MEDIUM_SLOW,
        .eggGroups = { EGG_GROUP_NO_EGGS_DISCOVERED, EGG_GROUP_NO_EGGS_DISCOVERED },
        .abilities = { ABILITY_SYNCHRONIZE, ABILITY_NONE },
        .bodyColor = BODY_COLOR_PINK,
        .isMythical = TRUE,
        .speciesName = _("Mew"),
        .cryId = CRY_MEW,
        .natDexNum = NATIONAL_DEX_MEW,
        .categoryName = _("New Species"),
        .height = 4,
        .weight = 40,
        .description = COMPOUND_STRING(
            "A Mew is said to possess the genes of all\n"
            "Pokémon. It is capable of making itself\n"
            "invisible at will, so it entirely avoids\n"
            "notice even if it approaches people."),
        .pokemonScale = 457,
        .pokemonOffset = -2,
        .trainerScale = 256,
        .trainerOffset = 0,
        .frontPic = gMonFrontPic_Mew,
        .frontPicSize = MON_COORDS_SIZE(64, 48),
        .frontPicYOffset = 9,
        .frontAnimFrames = sAnims_Mew,
        .frontAnimId = ANIM_ZIGZAG_SLOW,
        .enemyMonElevation = 11,
        .backPic = gMonBackPic_Mew,
        .backPicSize = MON_COORDS_SIZE(64, 64),
        .backPicYOffset = 0,
        .backAnimId = BACK_ANIM_CONCAVE_ARC_SMALL,
        .palette = gMonPalette_Mew,
        .shinyPalette = gMonShinyPalette_Mew,
        .iconSprite = gMonIcon_Mew,
        .iconPalIndex = 0,
        FOOTPRINT(Mew)
        .levelUpLearnset = sMewLevelUpLearnset,
        .teachableLearnset = sMewTeachableLearnset,
    },

That's a lot of stuff! But don't worry, we'll go through it step by step throught the tutorial (and miles better than having this same data through 20+ files like it used to be).

We'll start by adding the self-explanatory data that's also present in pret's vanilla structure:

3. Define its basic species information

Edit src/data/pokemon/species_info.h:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     [SPECIES_NONE] = {0},
     ...

     [SPECIES_EGG] =
     {
         FRONT_PIC(Egg, 24, 24),
         .frontPicYOffset = 20,
         .backPic = gMonFrontPic_Egg,
         .backPicSize = MON_COORDS_SIZE(24, 24),
         .backPicYOffset = 20,
         .palette = gMonPalette_Egg,
         .shinyPalette = gMonPalette_Egg,
         ICON(Egg, 1),
     },

+    [SPECIES_MEWTHREE] =
+    {
+       .baseHP        = 106,
+       .baseAttack    = 150,
+       .baseDefense   = 70,
+       .baseSpeed     = 140,
+       .baseSpAttack  = 194,
+       .baseSpDefense = 120,
+       .types = { TYPE_PSYCHIC, TYPE_PSYCHIC },
+       .catchRate = 3,
+       .expYield = 255,
+       .evYield_SpAttack  = 3,
+       .genderRatio = MON_GENDERLESS,
+       .eggCycles = 120,
+       .friendship = 0,
+       .growthRate = GROWTH_SLOW,
+       .eggGroups = { EGG_GROUP_NO_EGGS_DISCOVERED, EGG_GROUP_NO_EGGS_DISCOVERED },
+       .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_NONE },
+       .bodyColor = BODY_COLOR_PURPLE,
+    },
 };

The . is the structure reference operator in C to refer to the member object of the structure SpeciesInfo.

  • baseHP, baseAttack, baseDefense, baseSpeed, baseSpAttack and baseSpDefense are the base stats. They can't go higher than 255.
  • You may be confused as to why types has TYPE_PSYCHIC twice. This is because the way the game determines single-type mon is to define both types the same.
    • If we don't, it defaults to Normal due to it being the first type defined.
  • catchRate is how likely it is to catch a Pokémon, the lower the value, the harder it is to catch. Legendaries generally have a catch rate of 3, so we put that here.
  • expYield is the base amount of experience that a Pokémon gives when defeated/caught. In vanilla, this value caps at 255, but we've increased it to a maximum of 65535 accomodate later gen's higher experience yields. (The highest official value is Blissey's with 608, so going beyond this point may cause exponential gains that could break the system 😱)
    • If you noticed, Mew's had some #ifs, #elifs and #endif around it. This is because its yield has changed over time, and we let you choose which ones you want. This is not relevant to our Mewthree however, so we can just put a single .expYield = 255, line here.
  • evYield_HP, evYield_Attack, evYield_Defense, evYield_Speed, evYield_SpAttack and evYield_SpDefense are how many EVs does the Pokémon give when they're caught. Each of these fields can have a value of 3 at most. Officially, no Pokémon give out more than 3 EVs total, with them being determined by their evolution stage (eg, Pichu, Pikachu and Raichu give 1, 2 and 3 Speed EVs respectively), and they tend to be associated with its higher stats. Since our Mewthree is a Special Attack monster, we'll be consistent and make it give out 3 Special Attack EVs, but you're always free to assign whatever you feel like :)
    • Notice that the other evYield fields are not there. In C, numbers in a struct default to 0, so if we don't specify them, they'll be 0 all around! Less lines to worry about :D
  • itemCommon and itemRare are used to determine what items is the Pokémon holding when encountering it in the wild.
    • 50% for itemCommon and 5% for itemRare (boosted to 60%/20% when the first mon in the party has Compound Grass or Super Luck)
    • If they're both set as the same item, the item has a 100% chance of appearing.
  • genderRatio is a fun one.
    • There are 4 ways of handling this
      • PERCENT_FEMALE is what most Pokémon use, where you define how likely it's gonna be female. It supports decimals, so you can put PERCENT_FEMALE(12.5) to have a 1 in 8 chance of your mon to be female.
      • MON_MALE guarantees that all mon of this species will be male (eg. Tauros)
      • MON_FEMALE guarantees that all mon of this species will be female (eg. Miltank)
      • MON_GENDERLESS makes your species genderless, unable to breed with anything but Ditto to produce eggs. Most Legendaries are this, so we'll be chosing this as Mewthree's gender ratio.
    • When working with evolution lines and don't want their genders to change after evolving, be sure that their gender ratios match their stages and evolution methods. Azurill is the only case where there's a mismatch, causing 1/3 of all Azurill to change from Female to Male.
    • You might be wondering why some species have multiple defines for their genders, like SPECIES_MEOWSTIC_(FE)MALE. This is because those species have different stats and data from each other, so they're defined internally as different forms with MON_MALE and MON_FEMALE as gender ratios. If your species evolves depending on its gender and the evolutions have different stats, be sure to apply the correct evolution method!
  • eggCycles determines how fast an egg of this species will hatch. Doesn't matter much for evolved species or those that can't lay eggs, but we add the field here just in case.
  • friendship determines the amount of friendship of the mon when you catch it. Most Pokémon use STANDARD_FRIENDSHIP, but this creature of chaos does not want to be your friend, starting with 0.
  • growthRate determines the amounts of experience required to reach each level. Go here for more info.
  • eggGroups are used for breed compatibility. Most Legendaries and Mythicals have the EGG_GROUP_NO_EGGS_DISCOVERED group, and so does our Mewthree. Go here for more info.
  • abilities determines the potential abilites of our species. Notice how I also set the ability to ABILITY_INSOMNIA, so our little monster doesn't even need to sleep anymore. You can find the abilities for example here include/constants/abilities.h.
    • When both slot 1 and 2 are defined as not being ABILITY_NONE, their starting ability will be decided on a coin flip using their personality. They can later be changed using an Ability Capsule.
      • Certain Pokémon such as Zygarde and Rockruff have different forms to add additional abilities. As such, they cannot be changed using an Ability Capsule (though the Zygarde Cube can change Zygarde's ability by changing them to their corresponding form)
    • The 3rd slot is for Hidden Abilities. If defined as ABILITY_NONE, it will default to Slot 1 (eg. Metapod doesn't have a Hidden Ability, but Caterpie and Butterfree do). Go here and here for more info.
      • If the array is defined as {ABILITY_1, ABILITY_2}, the Hidden Ability is set as ABILITY_NONE.
  • bodyColor is used in the Pokédex as a search filter.
  • noFlip is used in to prevent front sprites from being flipped horizontally and cause weird issues, like Clawitzer's big claw changing sides.

That's all the basic fields present in vanilla emerald, so now let's take a look at the new fields added by the expansion.

4. Species Name

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .isLegendary = TRUE,
        .allPerfectIVs = TRUE,
+       .speciesName = _("Mewthree"),
    },
 };

The _() underscore function doesn't really exist - it's a convention borrowed from GNU gettext to let preproc know this is text to be converted to the custom encoding used by the Gen 3 Pokemon games.

5. Define its cry

Time for audio! We first need to convert an existing audio file to the format supported by the expansion.

Most formats are supported for conversion, but for simplicity's sake, we're gonna use an mp3 file.

Now, let's copy the file to the sound/direct_sound_samples/cries folder. Once that's done, let's run the following command:

ffmpeg -i sound/direct_sound_samples/cries/mewthree.mp3 -c:a pcm_s8 -ac 1 -ar 13379 sound/direct_sound_samples/cries/mewthree.aif

This will convert your audio file to .aif, which is what's read by the compiler.

Let's add the cry to the ROM via sound/direct_sound_data.inc.

.if P_FAMILY_PECHARUNT == TRUE
	.align 2
Cry_Pecharunt::
	.incbin "sound/direct_sound_samples/cries/pecharunt.bin"
.endif @ P_FAMILY_PECHARUNT

+	.align 2
+Cry_Mewthree::
+	.incbin "sound/direct_sound_samples/cries/mewthree.bin"

Then we add the cry ID to include/constants/cries.h:

enum {
    CRY_NONE,
    ...
#if P_FAMILY_TERAPAGOS
    CRY_TERAPAGOS,
#endif //P_FAMILY_TERAPAGOS
#if P_FAMILY_PECHARUNT
    CRY_PECHARUNT,
#endif //P_FAMILY_PECHARUNT
+   CRY_MEWTHREE,
    CRY_COUNT,
};

And then link it in sound/cry_tables.inc. cry_reverse in particular is for reversed cries used by moves such as Growl. The order of these two tables should match the order of the cry IDs, otherwise they'll be shifted.

	cry Cry_Terapagos
	cry Cry_Pecharunt
+	cry Cry_Mewthree
	cry_reverse Cry_Terapagos
	cry_reverse Cry_Pecharunt
+	cry_reverse Cry_Mewthree

Lastly, we add the cry to our species entry

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .isLegendary = TRUE,
        .allPerfectIVs = TRUE,
        .speciesName = _("Mewthree"),
+       .cryId = CRY_MEWTHREE,
    },
 };

And let's see how it sounds in-game:

https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/4f7667db-4db9-4bfd-a8dd-ece26f09f327

Good! Our monster now has a mighty roar!

You can now delete the mp3 from the cries folder now once you made sure that the cry sounds like how you want it to.

6. Define its Pokédex entry

First, we will need to add new index constants for its Pokédex entry. The index constants are divided into the Hoenn Pokédex, which contains all Pokémon native to the Hoenn region, and the National Pokédex containing all known Pokémon, which can be received after entering the hall of fame for the first time.

Edit include/constants/pokedex.h:

// National Pokedex order
enum {
    NATIONAL_DEX_NONE,
    // Kanto
    NATIONAL_DEX_BULBASAUR,
...
    NATIONAL_DEX_PECHARUNT,
+   NATIONAL_DEX_MEWTHREE,
};
 #define KANTO_DEX_COUNT     NATIONAL_DEX_MEW
 #define JOHTO_DEX_COUNT     NATIONAL_DEX_CELEBI

#if P_GEN_9_POKEMON == TRUE
-   #define NATIONAL_DEX_COUNT  NATIONAL_DEX_PECHARUNT
+   #define NATIONAL_DEX_COUNT  NATIONAL_DEX_MEWTHREE

Do keep in mind that if you intend to add your new species to the Hoenn Dex, you'll also want to add a HOENN_DEX constant for it, like this:

// Hoenn Pokedex order
enum {
    HOENN_DEX_NONE,
    HOENN_DEX_TREECKO,
...
    HOENN_DEX_DEOXYS,
+   HOENN_DEX_MEWTHREE,
};

- #define HOENN_DEX_COUNT (HOENN_DEX_DEOXYS + 1)
+ #define HOENN_DEX_COUNT (HOENN_DEX_MEWTHREE + 1)

Edit src/pokemon.c:

 const u16 sHoennToNationalOrder[NUM_SPECIES] = // Assigns Hoenn Dex Pokémon (Using National Dex Index)
 {
     HOENN_TO_NATIONAL(TREECKO),
     ...
     HOENN_TO_NATIONAL(DEOXYS),
+    HOENN_TO_NATIONAL(MEWTHREE),
 };

Now we can add the number and entry to our Mewthree:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .cryId = CRY_MEWTHREE,
+       .natDexNum = NATIONAL_DEX_MEWTHREE,
+       .categoryName = _("New Species"),
+       .height = 15,
+       .weight = 330,
+       .description = COMPOUND_STRING(
+           "The rumors became true.\n"
+           "This is Mew's final form.\n"
+           "Its power level is over 9000.\n"
+           "Has science gone too far?"),
+       .pokemonScale = 256,
+       .pokemonOffset = 0,
+       .trainerScale = 290,
+       .trainerOffset = 2,
    },
 };

image

The values pokemonScale, pokemonOffset, trainerScale and trainerOffset are used for the height comparison figure in the Pokédex.

height and weight are specified in decimeters and hectograms respectively (which are meters and kilograms multiplied by 10, so 2.5 meters are 25 decimeters).

In Pokémon Emerald, you can sort the Pokédex by name, height or weight. Apparently, the Pokémon order is hardcoded in the game files and not calculated from their data. Therefore we have to include our new Pokémon species at the right places. While the correct position for the alphabetical order is easy to find, it can become quite tedious for height and weight, so we added comments to the listings in order help out were they should fit.

Edit src/data/pokemon/pokedex_orders.h:

 const u16 gPokedexOrder_Alphabetical[] =
 {
     ...
     NATIONAL_DEX_MEW,
+    NATIONAL_DEX_MEWTHREE,
     NATIONAL_DEX_MEWTWO,
     ...
 };

 const u16 gPokedexOrder_Weight[] =
 {
     ...
     // 72.8 lbs / 33.0 kg
     //NATIONAL_DEX_MEWTWO_MEGA_Y,
     NATIONAL_DEX_ESCAVALIER,
     NATIONAL_DEX_FRILLISH,
     NATIONAL_DEX_DURANT,
     NATIONAL_DEX_CINDERACE,
+    NATIONAL_DEX_MEWTHREE,
     //NATIONAL_DEX_PERSIAN_ALOLAN,
     NATIONAL_DEX_TOEDSCOOL,
     // 73.4 lbs / 33.3 kg
     NATIONAL_DEX_DUGTRIO,
     ...
 };

 const u16 gPokedexOrder_Height[] =
 {
     ...
     // 4'11" / 1.5m
     ...
     NATIONAL_DEX_GLIMMORA,
     NATIONAL_DEX_WO_CHIEN,
     NATIONAL_DEX_IRON_LEAVES,
     NATIONAL_DEX_IRON_BOULDER,
+    NATIONAL_DEX_MEWTHREE,
    // 5'03" / 1.6m
     ...
 };

mGBA_lUBfmFEKUx

The Graphics

We will start by copying the following files for Mew (not Mewtwo) and rename it to mewthree.

cp -r graphics/pokemon/mew/. graphics/pokemon/mewthree

We aren't copying Mewtwo's folder because he has those pesky Mega Evolutions that will get in the way of what we're doing, so our sample will need to be pure from the source.

1. Edit the sprites

Let's edit the sprites. Start your favourite image editor (I recommend Aseprite or its free alternative, Libresprite) and change anim_front.png and back.png to meet your expectations.

Make sure that you are using the indexed mode and you have limited yourself to 15 colors!

Put the RGB values of your colors into normal.pal between the first and the last color and the RGB values for the shiny version into shiny.pal. Edit footprint.png using two colors in indexed mode, black and white. Finally, edit icon.png. Notice, that the icon will use one of 6 predefined palettes instead of normal.pal.

2. Add the sprites to the rom

Sadly, just putting the image files into the graphics folder is not enough. To use the sprites we have to register them, which is kind of tedious. First, create constants for the file paths. You'll want to add the constants for your species after the constants for the last valid species.

Edit src/data/graphics/pokemon.h:

#if P_FAMILY_PECHARUNT
    // const u32 gMonFrontPic_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/front.4bpp.lz");
    // const u32 gMonPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/normal.gbapal.lz");
    // const u32 gMonBackPic_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/back.4bpp.lz");
    // const u32 gMonShinyPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/shiny.gbapal.lz");
    // const u8 gMonIcon_Pecharunt[] = INCBIN_U8("graphics/pokemon/pecharunt/icon.4bpp");
#if P_FOOTPRINTS
    // const u8 gMonFootprint_Pecharunt[] = INCBIN_U8("graphics/pokemon/pecharunt/footprint.1bpp");
#endif //P_FOOTPRINTS
#endif //P_FAMILY_PECHARUNT

+   const u32 gMonFrontPic_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/anim_front.4bpp.lz");
+   const u32 gMonBackPic_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/back.4bpp.lz");
+   const u32 gMonPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/normal.gbapal.lz");
+   const u32 gMonShinyPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/shiny.gbapal.lz");
+   const u8 gMonIcon_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/icon.4bpp");
+   const u8 gMonFootprint_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/footprint.1bpp");

Please note that Pecharunt, the Pokémon that should be above your insertion for the time being, reads a front.png sprite instead of an anim_front.png sprite. This is because currently, Pecharunt lacks a 2nd frame. If the front sprite sheet of your species uses 2 frames, you should use anim_front.

It is also worth to mention that Pecharunt's sprites are commented out simply because they're currently missing.

3. Add the animations to the rom

You can define the animation order, in which the sprites will be shown. The first number is the sprite index (so 0 or 1) and the second number is the number of frames the sprite will be visible.

Edit src/data/pokemon_graphics/front_pic_anims.h:

#if P_FAMILY_PECHARUNT
PLACEHOLDER_ANIM_SINGLE_FRAME(Pecharunt);
#endif //P_FAMILY_PECHARUNT

+static const union AnimCmd sAnim_Mewthree_1[] =
+{
+    ANIMCMD_FRAME(1, 30),
+    ANIMCMD_FRAME(0, 20),
+    ANIMCMD_END,
+};
#if P_FAMILY_PECHARUNT
SINGLE_ANIMATION(Pecharunt);
#endif //P_FAMILY_PECHARUNT
+SINGLE_ANIMATION(Mewthree);
SINGLE_ANIMATION(Egg);

You might be wondering what PLACEHOLDER_ANIM_SINGLE_FRAME is. Well, since Pecharun only has 1 frame, we use what's called a preprocessor macro to have in a single line what otherwise would've been this in the C file:

static const union AnimCmd sAnim_Pecharunt_1[] =
{
    ANIMCMD_FRAME(0, 1),
    ANIMCMD_END,
}

Instead, we can use the already established macro that does the same thing, replacing the value in parenthesis with what we want (in this case, Pecharunt):

#define PLACEHOLDER_ANIM_SINGLE_FRAME(name)     \
static const union AnimCmd sAnim_##name##_1[] = \
{                                               \
    ANIMCMD_FRAME(0, 1),                        \
    ANIMCMD_END,                                \
}

4. Linking graphic information to our Pokémon

Now that we have all the external data ready, we just need to add it to gSpeciesInfo plus the rest of the animation and graphical data that we want to use:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .pokemonScale = 256,
        .pokemonOffset = 0,
        .trainerScale = 290,
        .trainerOffset = 2,
+       .frontPic = gMonFrontPic_Mewthree,
+       .frontPicSize = MON_COORDS_SIZE(64, 64),
+       .frontPicYOffset = 0,
+       .frontAnimFrames = sAnims_Mewthree,
+       .frontAnimId = ANIM_GROW_VIBRATE,
+       .frontAnimDelay = 15,
+       .enemyMonElevation = 6,
+       .backPic = gMonBackPic_Mewthree,
+       .backPicSize = MON_COORDS_SIZE(64, 64),
+       .backPicYOffset = 0,
+       .backAnimId = BACK_ANIM_CONCAVE_ARC_SMALL,
+       .palette = gMonPalette_Mewthree,
+       .shinyPalette = gMonShinyPalette_Mewthree,
        .iconSprite = gMonIcon_Mewthree,
        .iconPalIndex = 2,
+       FOOTPRINT(Mewthree)
    },
 };

Let's explain each of these:

  • frontPic:
    • Used to reference the front sprite, so in this case, we call for gMonFrontPic_Mewthree.
  • frontPicSize:
    • The two values (width and height) are used for defining the non-empty size of the front sprite, which is used in move animations. If you're unsure of the values, you can leave them both as 64.
  • frontPicYOffset:
    • Used to define what Y position the sprite sits at. This is used to set where they'd be "grounded". For the shadow, see enemyMonElevation.
  • frontAnimFrames:
    • We link our animation frame animations that we defined earlier here.
  • frontAnimId:
  • frontAnimDelay:
    • Sets a delay in frame count between when the Pokémon appears and when the animation starts.
  • enemyMonElevation:
    • Used to determine the altitude from the ground. Any value above 0 will show a shadow under the Pokémon, to signify that they're floating.
  • backPic:
    • Used to reference the back sprite, so in this case, we call for gMonBackPic_Mewthree.
  • backPicSize:
    • The two values (width and height) are used for defining the non-empty size of the back sprite, which is used in move animations. If you're unsure of the values, you can leave them both as 64.
  • backPicYOffset:
    • Used to define what Y position of the back sprite. When working with the animation debug menu, we recommend aligning the back sprite to the white background, as it was designed to properyly align with the real battle layout.
  • backAnimId:
    • Like frontAnimId except for the back sprites and them being a single frame. The IDs listed here are used to represent 3 different animations that happen based on the the Pokémon's nature.
  • palette:
    • Used to reference the non-shiny palette, so in this case, we call for gMonPalette_Mewthree.
  • shinyPalette:
    • Used to reference the shiny palette, so in this case, we call for gMonShinyPalette_Mewthree.
  • iconSprite:
    • Used to reference the icon sprite, so in this case, we call for gMonIcon_Mewthree.
  • iconPalIndex:
  • ICON
    • Here, you can choose between the six icon palettes; 0, 1, 2, 3, 4 and 5. All of them located in graphics/pokemon/icon_palettes.

      Open an icon sprite and load one of the palettes to find out which palette suits your icon sprite best.

  • FOOTPRINT
    • We made this single field into a macro so that they can be ignored when P_FOOTPRINTS is set to false. It's also why we don't have an "," after calling it like the other macros (we add it as part of the macro itself).
      #if P_FOOTPRINTS
      #define FOOTPRINT(sprite) .footprint = gMonFootprint_## sprite,
      #else
      #define FOOTPRINT(sprite)
      #endif
      

The Data - Part 2

We're almost there just a bit left!

1. Species Flags

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_NONE },
        .bodyColor = BODY_COLOR_PURPLE,
+       .isLegendary = TRUE,
+       .allPerfectIVs = TRUE,
    },
 };

Each species flag provides properties to the species:

  • isLegendary:
    • Guarantees 3 perfect IVs upon generating the Pokémon (As long as P_LEGENDARY_PERFECT_IVS is set to GEN_6 or higher).
  • isMythical:
    • Guarantees 3 perfect IVs upon generating the Pokémon (As long as P_LEGENDARY_PERFECT_IVS is set to GEN_6 or higher).
    • Is skipped during Pokédex evaluations.
      • Unless it also has the dexForceRequired flag.
    • Cannot obtain Gigantamax factor via ToggleGigantamaxFactor.
  • isUltraBeast:
    • Guarantees 3 perfect IVs upon generating the Pokémon (As long as P_LEGENDARY_PERFECT_IVS is set to GEN_6 or higher).
    • Beast Ball's multiplier is set to x5 for this species.
      • All other ball multipliers are set to x0.1.
  • isParadox (previously isParadoxForm):
    • Currently has no functionality but can be utilized by users for their own benefits.
  • isTotem:
    • Guarantees 3 perfect IVs upon generating the Pokémon (As long as P_LEGENDARY_PERFECT_IVS is set to GEN_6 or higher).
  • isMegaEvolution:
    • A Mega indicator is added to the battle box indicating that they're Mega Evolved.
    • The species doesn't receive affection benefits.
    • Required when adding new Mega Evolutions.
  • isPrimalReversion:
    • A Primal Reversion indicator (Alpha or Omega for Kyogre/Groudon respectively) is added to the battle box indicating that they're Primal Reverted.
    • Required when adding new Primal Reversions.
  • isUltraBurst:
    • Required when adding new Ultra Burst forms.
  • isGigantamax:
    • Used to determine if Gigantamax forms should have their GMax moves or not.
    • Required when adding new Gigantamax forms.
  • isAlolanForm, isGalarianForm, isHisuianForm, isPaldeanForm:
    • In the future, these will be used to determine breeding offspring from different based on their region.
  • cannotBeTraded:
    • This species cannot be traded away (like Black/White Kyurem).
  • allPerfectIVs:
    • Guarantees 6 perfect IVs upon generating the Pokémon (like LGPE's Partner Pikachu and Eevee).
  • tmIlliterate:
    • This species will be unable to learn the universal moves.
  • isFrontierBanned:
    • This species will be unable to enter Battle Frontier facilities. Replaces gFrontierBannedSpecies.

*: As long as P_LEGENDARY_PERFECT_IVS is set to GEN_6 or higher.

2. Delimit the moveset

Let's begin with the moves that can be learned by leveling up.

Append to src/data/pokemon/level_up_learnsets.h:

#if P_FAMILY_PECHARUNT
static const struct LevelUpMove sPecharuntLevelUpLearnset[] = {
    LEVEL_UP_MOVE( 1, MOVE_SMOG),
    LEVEL_UP_MOVE( 1, MOVE_POISON_GAS),
    LEVEL_UP_MOVE( 1, MOVE_MEMENTO),
    LEVEL_UP_MOVE( 1, MOVE_ASTONISH),
    LEVEL_UP_MOVE( 8, MOVE_WITHDRAW),
    LEVEL_UP_MOVE(16, MOVE_DESTINY_BOND),
    LEVEL_UP_MOVE(24, MOVE_FAKE_TEARS),
    LEVEL_UP_MOVE(32, MOVE_PARTING_SHOT),
    LEVEL_UP_MOVE(40, MOVE_SHADOW_BALL),
    LEVEL_UP_MOVE(48, MOVE_MALIGNANT_CHAIN),
    LEVEL_UP_MOVE(56, MOVE_TOXIC),
    LEVEL_UP_MOVE(64, MOVE_NASTY_PLOT),
    LEVEL_UP_MOVE(72, MOVE_RECOVER),
    LEVEL_UP_END
};
#endif

+static const struct LevelUpMove sMewthreeLevelUpLearnset[] = {
+   LEVEL_UP_MOVE( 1, MOVE_CONFUSION),
+   LEVEL_UP_MOVE( 1, MOVE_DISABLE),
+   LEVEL_UP_MOVE(11, MOVE_BARRIER),
+   LEVEL_UP_MOVE(22, MOVE_SWIFT),
+   LEVEL_UP_MOVE(33, MOVE_PSYCH_UP),
+   LEVEL_UP_MOVE(44, MOVE_FUTURE_SIGHT),
+   LEVEL_UP_MOVE(55, MOVE_MIST),
+   LEVEL_UP_MOVE(66, MOVE_PSYCHIC),
+   LEVEL_UP_MOVE(77, MOVE_AMNESIA),
+   LEVEL_UP_MOVE(88, MOVE_RECOVER),
+   LEVEL_UP_MOVE(99, MOVE_SAFEGUARD),
+   LEVEL_UP_END
+};

Again, we need to register the learnset in gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        PALETTES(Mewthree),
        ICON(Mewthree, 2),
        FOOTPRINT(Mewthree)
+       .levelUpLearnset = sMewthreeLevelUpLearnset,
    },
 };

Next we need to specify which moves can be taught via TM, HM, or Move Tutor.

Append to src/data/pokemon/teachable_learnsets.h:

#if P_FAMILY_PECHARUNT
static const u16 sPecharuntTeachableLearnset[] = {
    ...
    MOVE_UNAVAILABLE,
};
#endif //P_FAMILY_PECHARUNT

+static const u16 sMewthreeTeachableLearnset[] = {
+   MOVE_FOCUS_PUNCH,
+   MOVE_WATER_PULSE,
+   MOVE_CALM_MIND,
+   MOVE_TOXIC,
+   MOVE_HAIL,
+   MOVE_BULK_UP,
+   MOVE_HIDDEN_POWER,
+   MOVE_SUNNY_DAY,
+   MOVE_TAUNT,
+   MOVE_ICE_BEAM,
+   MOVE_BLIZZARD,
+   MOVE_HYPER_BEAM,
+   MOVE_LIGHT_SCREEN,
+   MOVE_PROTECT,
+   MOVE_RAIN_DANCE,
+   MOVE_SAFEGUARD,
+   MOVE_FRUSTRATION,
+   MOVE_SOLAR_BEAM,
+   MOVE_IRON_TAIL,
+   MOVE_THUNDERBOLT,
+   MOVE_THUNDER,
+   MOVE_EARTHQUAKE,
+   MOVE_RETURN,
+   MOVE_PSYCHIC,
+   MOVE_SHADOW_BALL,
+   MOVE_BRICK_BREAK,
+   MOVE_DOUBLE_TEAM,
+   MOVE_REFLECT,
+   MOVE_SHOCK_WAVE,
+   MOVE_FLAMETHROWER,
+   MOVE_SANDSTORM,
+   MOVE_FIRE_BLAST,
+   MOVE_ROCK_TOMB,
+   MOVE_AERIAL_ACE,
+   MOVE_TORMENT,
+   MOVE_FACADE,
+   MOVE_SECRET_POWER,
+   MOVE_REST,
+   MOVE_SKILL_SWAP,
+   MOVE_SNATCH,
+   MOVE_STRENGTH,
+   MOVE_FLASH,
+   MOVE_ROCK_SMASH,
+   MOVE_MEGA_PUNCH,
+   MOVE_MEGA_KICK,
+   MOVE_BODY_SLAM,
+   MOVE_DOUBLE_EDGE,
+   MOVE_COUNTER,
+   MOVE_SEISMIC_TOSS,
+   MOVE_MIMIC,
+   MOVE_METRONOME,
+   MOVE_DREAM_EATER,
+   MOVE_THUNDER_WAVE,
+   MOVE_SUBSTITUTE,
+   MOVE_DYNAMIC_PUNCH,
+   MOVE_PSYCH_UP,
+   MOVE_SNORE,
+   MOVE_ICY_WIND,
+   MOVE_ENDURE,
+   MOVE_MUD_SLAP,
+   MOVE_ICE_PUNCH,
+   MOVE_SWAGGER,
+   MOVE_SLEEP_TALK,
+   MOVE_SWIFT,
+   MOVE_THUNDER_PUNCH,
+   MOVE_FIRE_PUNCH,
+   MOVE_UNAVAILABLE, // This is required to determine where the array ends.
+};
#endif

NOTE: At the top of this file, you will probably see this warning:

//
// DO NOT MODIFY THIS FILE! It is auto-generated from tools/learnset_helpers/teachable.py`
//

The expansion includes a tool called the learnset helper, which aims to automate the generation of valid teachable moves. At the time of writing, this tool only supports generating TM and Tutor learnsets. However, in the future it may be expanded to deal with level up learnsets and egg moves.

Ignore the warning shown above the first time you're adding your teachable moves (as otherwise the compiler will complain about the array not existing), but in the future (if you're using the learnset helper) simply edit what teachable moves your Pokémon can learn in one of the JSON files found in tools/learnset_helpers/porymoves_files. It doesn't really matter which one you add your new Pokémon to, as the tool pulls from all of the files in this folder.

The learnset helper is useful if you plan on changing and/or increasing the available TMs and Tutor moves in your game. As an example, Bulbasaur learns Rage by TM in Red/Blue/Yellow, but in Emerald this TM does not exist. But since tools/learnset_helpers/porymoves_files/rby.json defines "MOVE_RAGE" as a TM move for Bulbasaur, that move would automatically be added to the sBulbasaurTeachableLearnset array if you were to add a Rage TM at any point.

The learnset helper can be toggled on/off in include/config/pokemon.h:

// Learnset helper toggles
#define P_LEARNSET_HELPER_TEACHABLE TRUE        // If TRUE, teachable_learnsets.h will be populated by tools/learnset_helpers/teachable.py using the included JSON files based on available TMs and tutors.

Once more, we need to register the learnset in gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        FOOTPRINT(Mewthree)
        .levelUpLearnset = sMewthreeLevelUpLearnset,
+       .teachableLearnset = sMewthreeTeachableLearnset,
    },
 };

If you want to create a Pokémon which can breed, you will need to edit src/data/pokemon/egg_moves.h.

3. Define the Evolutions

We want Mewthree to evolve from Mewtwo by reaching level 100.

Edit gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTWO] =
     {
        ...
        FOOTPRINT(Mewtwo)
        .isLegendary = TRUE,
        .levelUpLearnset = sMewtwoLevelUpLearnset,
        .teachableLearnset = sMewtwoTeachableLearnset,
        .formSpeciesIdTable = sMewtwoFormSpeciesIdTable,
        .formChangeTable = sMewtwoFormChangeTable,
+       .evolutions = EVOLUTION({EVO_LEVEL, 100, SPECIES_MEWTHREE}),
    },
 };

4. Make it appear!

Now Mewthree really does slumber in the games code - but we won't know until we make him appear somewhere! The legend tells that Mewthree is hiding somewhere in Petalburg Woods...

Edit src/data/wild_encounters.json:

         {
           "map": "MAP_PETALBURG_WOODS",
           "base_label": "gPetalburgWoods",
           "land_mons": {
             "encounter_rate": 20,
             "mons": [
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_POOCHYENA"
               },
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_WURMPLE"
               },
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_SHROOMISH"
               },
               {
-                "min_level": 6,
-                "max_level": 6,
-                "species": "SPECIES_POOCHYENA"
+                "min_level": 5,
+                "max_level": 5,
+                "species": "SPECIES_MEWTHREE"
               },
               ...
        }

Congratulations, you have created your own personal pocket monster! You may call yourself a mad scientist now.

Optional data

Now that you now have all the essential pieces to create a base species, there are some aspects that you might want to know if you want to do other stuff with your custom Pokémon.

1. Form tables

Found in src/data/pokemon/form_species_tables.h.

These are introduced to have a reference of what forms correspond to what Species of Pokémon. For example, we have Pikachu's table:

#if P_FAMILY_PIKACHU
static const u16 sPikachuFormSpeciesIdTable[] = {
    SPECIES_PIKACHU,
    SPECIES_PIKACHU_COSPLAY,
    SPECIES_PIKACHU_ROCK_STAR,
    SPECIES_PIKACHU_BELLE,
    SPECIES_PIKACHU_POP_STAR,
    SPECIES_PIKACHU_PH_D,
    SPECIES_PIKACHU_LIBRE,
    SPECIES_PIKACHU_ORIGINAL_CAP,
    SPECIES_PIKACHU_HOENN_CAP,
    SPECIES_PIKACHU_SINNOH_CAP,
    SPECIES_PIKACHU_UNOVA_CAP,
    SPECIES_PIKACHU_KALOS_CAP,
    SPECIES_PIKACHU_ALOLA_CAP,
    SPECIES_PIKACHU_PARTNER_CAP,
    SPECIES_PIKACHU_WORLD_CAP,
    FORM_SPECIES_END,
};
#endif //P_FAMILY_PIKACHU

We register the table each form entry in gSpeciesInfo.

    [SPECIES_PIKACHU] =
    {
        ...
        .teachableLearnset = sPikachuTeachableLearnset,
+       .formSpeciesIdTable = sPikachuFormSpeciesIdTable,
        .evolutions = EVOLUTION({EVO_ITEM, ITEM_THUNDER_STONE, SPECIES_RAICHU},
                                {EVO_NONE, 0, SPECIES_RAICHU_ALOLAN}),
    },

    [SPECIES_PIKACHU_COSPLAY] =
    {
        ...
        .teachableLearnset = sPikachuTeachableLearnset,
+       .formSpeciesIdTable = sPikachuFormSpeciesIdTable,
    },

...and so on.

What this allows us to do is to be able to get all forms of a Pokémon in our code by using the GetSpeciesFormTable function.

For example, in the HGSS dex, it lets us browse between the entries of every form available.:

image image

In addition, we have the GET_BASE_SPECIES_ID macro, which returns the first entry of the table (or return the species itself if it doesn't have a table registered). With this, you can check if a Pokémon is any form of a species. For example, making it so that the Light Ball affects all Pikachu forms:

    case HOLD_EFFECT_LIGHT_BALL:
        if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_PIKACHU && IS_MOVE_SPECIAL(move))
            modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0));
        break;

2. Form change tables

Found in src/data/pokemon/form_species_tables.h.

These tables, unlike the regular form tables, registers how Pokémon can switch between forms.

#if P_FAMILY_GASTLY
static const struct FormChange sGengarFormChangeTable[] = {
    {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GENGAR_MEGA, ITEM_GENGARITE},
    {FORM_CHANGE_BATTLE_GIGANTAMAX,          SPECIES_GENGAR_GIGANTAMAX},
    {FORM_CHANGE_TERMINATOR},
};
#endif //P_FAMILY_GASTLY

The first value is the type of form change. In the case of Gengar, we have both Mega Evolution and Gigantamax form changes.

The second value is the target form, to which the Pokémon will change into.

Values after that are referred as arguments, and needs to be put there depends on the type of form change, detailed in include/constants/form_change_types.h.

3. Gender differences

mGBA_Wq5cbDkNZG

You may have seen that there's a couple of duplicate fields with a "Female" suffix.

    [SPECIES_FRILLISH] =
    {
        ...
        .frontPic = gMonFrontPic_Frillish,
+       .frontPicFemale = gMonFrontPic_FrillishF,
        .frontPicSize = MON_COORDS_SIZE(56, 56),
+       .frontPicSizeFemale = MON_COORDS_SIZE(56, 56),
        .frontPicYOffset = 5,
        .frontAnimFrames = sAnims_Frillish,
        .frontAnimId = ANIM_RISING_WOBBLE,
        .backPic = gMonBackPic_Frillish,
+       .backPicFemale = gMonBackPic_FrillishF,
        .backPicSize = MON_COORDS_SIZE(40, 56),
+       .backPicSizeFemale = MON_COORDS_SIZE(40, 56),
        .backPicYOffset = 7,
        .backAnimId = BACK_ANIM_CONVEX_DOUBLE_ARC,
        .palette = gMonPalette_Frillish,
+       .paletteFemale = gMonPalette_FrillishF,
        .shinyPalette = gMonShinyPalette_Frillish,
+       .shinyPaletteFemale = gMonShinyPalette_FrillishF,
        .iconSprite = gMonIcon_Frillish,
+       .iconSpriteFemale = gMonIcon_FrillishF,
        .iconPalIndex = 0,
+       .iconPalIndexFemale = 1,
        FOOTPRINT(Frillish)
        .levelUpLearnset = sFrillishLevelUpLearnset,
        .teachableLearnset = sFrillishTeachableLearnset,
        .evolutions = EVOLUTION({EVO_LEVEL, 40, SPECIES_JELLICENT}),
    },

These are used to change the graphics of the Pokémon if they're female. If they're not registered, they default to the male values.

However, iconPalIndexFemale is a special case, where it's doesn't read the male icon palette if its iconSpriteFemale is set, so if you're setting a female icon, be sure to set their palette index as well.

4. Overworld Data

mGBA_4iqvhhSltK

If you have OW_POKEMON_OBJECT_EVENTS in your hack, you can add Overworld of your new species by following these steps:

First, since you copied the contents from Mew's folder previously, you should also have copied its overworld sprites. Edit those to your liking, as we have done before, making sure to update the palettes

Secondly, in src/data/graphics/pokemon.h, add the following:

    const u8 gMonIcon_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/icon.4bpp");
    const u8 gMonFootprint_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/footprint.1bpp");
+   const u32 gObjectEventPic_Mewthree[] = INCBIN_COMP("graphics/pokemon/mewthree/overworld.4bpp");
+   const u32 gOverworldPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/overworld_normal.gbapal.lz");
+   const u32 gShinyOverworldPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/overworld_shiny.gbapal.lz");

Thirdly, in spritesheet_rules.mk

$(POKEMONGFXDIR)/mewtwo/overworld.4bpp: %.4bpp: %.png
	$(GFX) $< $@ -mwidth 4 -mheight 4

+$(POKEMONGFXDIR)/mewthree/overworld.4bpp: %.4bpp: %.png
+	$(GFX) $< $@ -mwidth 4 -mheight 4

$(POKEMONGFXDIR)/mew/overworld.4bpp: %.4bpp: %.png
	$(GFX) $< $@ -mwidth 4 -mheight 4

Fourthly, in src/data/object_events/object_event_pic_tables_followers.h:

#if P_FAMILY_PECHARUNT
/*static const struct SpriteFrameImage sPicTable_Pecharunt[] = {
    overworld_ascending_frames(gObjectEventPic_Pecharunt, 4, 4),
};*/
#endif //P_FAMILY_PECHARUNT

+static const struct SpriteFrameImage sPicTable_Mewthree[] = {
+    overworld_ascending_frames(gObjectEventPic_Mewthree, 4, 4),
+};

And finally, in gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        FOOTPRINT(Mewthree)
+       OVERWORLD(
+           sPicTable_Mewthree,
+           SIZE_32x32,
+           SHADOW_SIZE_M,
+           TRACKS_FOOT,
+           gOverworldPalette_Mewthree,
+           gShinyOverworldPalette_Mewthree
+       )
        .levelUpLearnset = sMewthreeLevelUpLearnset,
        .teachableLearnset = sMewthreeTeachableLearnset,
    },
 };

Sprite Size

Depending on your species, you might want to use different sizes for it. For example, certain species known for being big like Steelix use sprites that fit a 64x64 frame instead of 32x32, and as such have SIZE_64x64 in their data instead of SIZE_32x32 to accomodate for them.

Also, in spritesheet_rules.mk, -mwidth and -mheight need to be set to 8 instead of 4 for such cases.

Shadows

You have 4 options for their shadow, between Small, Medium, Large and None:

  • SHADOW_SIZE_NONE
  • SHADOW_SIZE_S shadow_small
  • SHADOW_SIZE_M shadow_medium
  • SHADOW_SIZE_L shadow_large

Tracks

You have 4 options for the tracks that your species will leave behind on sand.

  • TRACKS_NONE
  • TRACKS_FOOT sand_footprints
  • TRACKS_SLITHER slither_tracks
  • TRACKS_SPOT spot_tracks
  • TRACKS_BUG bug_tracks

...though technically you can also use TRACKS_BIKE_TIRE if you wish to.

bike_tire_tracks

This is a modified version of the original tutorial about adding new Pokémon species available in Pokeemerald's wiki.

Despite the persistent rumors about an incredibly strong third form of Mew hiding somewhere, it actually wasn't possible to catch it... OR WAS IT? In this tutorial, we will add a new Pokémon species to the game.

IMPORTANT: This tutorial applies to 1.8.x versions.

Changes compared to vanilla

The main things that the Expansion changes are listed here.

  • Still Front Pics (gMonStillFrontPic_YourPokemon) and by extension src/anim_mon_front_pics.c have been removed.
  • src/data/pokemon/cry_ids.h doesn't exist anymore.
  • You have 6 icon palettes available instead of the base 3.
  • Most tables that use SPECIES_x as indexes have been moved to gSpeciesInfo.

Content

Useful resources

You can open a sprite debug menu by pressing Select in a Pokémon's summary screen outside of battle.

mGBA_6WOo1TSlsn

The Data - Part 1

Our plan is as simple as it is brilliant: clone Mewtwo... and make it even stronger!

1. Declare a species constant

Our first step towards creating a new digital lifeform is to define its own species constant.

Edit include/constants/species.h:

 #define SPECIES_NONE                                    0
 #define SPECIES_BULBASAUR                               1
 ...
 #define SPECIES_URSHIFU_SINGLE_STRIKE_STYLE_GIGANTAMAX  1521
 #define SPECIES_URSHIFU_RAPID_STRIKE_STYLE_GIGANTAMAX   1522
 #define SPECIES_MIMIKYU_TOTEM_BUSTED                    1523
+#define SPECIES_MEWTHREE                                1524

-#define SPECIES_EGG                                     SPECIES_MIMIKYU_TOTEM_BUSTED + 1
+#define SPECIES_EGG                                     SPECIES_MEWTHREE + 1

 #define NUM_SPECIES SPECIES_EGG

This number is stored in a Pokémon's save structure. These should generally never change, otherwise your saved Pokémon species will change as well.

We add this after the last species ID and make sure to update SPECIES_EGG's definition to use the last ID on the list. Also, be sure that no IDs repeat each other, or you'll get compiling errors.

NOTE: In 1.7.x and previous versions, we had variable defines such as `FORMS_START` but was confusing for users, so we switched to absolute IDs.

Now, let's see how it looks in-game!

image

Hmmm, something's not right...

Oh, I know! We need to add the rest of the data! Normally, the vanilla game would crash if we try to look up anything about Mewthree in this state, but the expansion defaults all of its data to SPECIES_NONE.

Now, let's see what needs to be done.

2. SpeciesInfo's structure

Now, to better understand Mewtwo, we also need to understand Mew. Let's look at its data.

    [SPECIES_MEW] =
    {
        .baseHP        = 100,
        .baseAttack    = 100,
        .baseDefense   = 100,
        .baseSpeed     = 100,
        .baseSpAttack  = 100,
        .baseSpDefense = 100,
        .types = { TYPE_PSYCHIC, TYPE_PSYCHIC },
        .catchRate = 45,
    #if P_UPDATED_EXP_YIELDS >= GEN_8
        .expYield = 300,
    #elif P_UPDATED_EXP_YIELDS >= GEN_5
        .expYield = 270,
    #else
        .expYield = 64,
    #endif
        .evYield_HP = 3,
        .itemCommon = ITEM_LUM_BERRY,
        .itemRare = ITEM_LUM_BERRY,
        .genderRatio = MON_GENDERLESS,
        .eggCycles = 120,
        .friendship = 100,
        .growthRate = GROWTH_MEDIUM_SLOW,
        .eggGroups = { EGG_GROUP_NO_EGGS_DISCOVERED, EGG_GROUP_NO_EGGS_DISCOVERED },
        .abilities = { ABILITY_SYNCHRONIZE, ABILITY_NONE },
        .bodyColor = BODY_COLOR_PINK,
        .isMythical = TRUE,
        .speciesName = _("Mew"),
        .cryId = CRY_MEW,
        .natDexNum = NATIONAL_DEX_MEW,
        .categoryName = _("New Species"),
        .height = 4,
        .weight = 40,
        .description = COMPOUND_STRING(
            "A Mew is said to possess the genes of all\n"
            "Pokémon. It is capable of making itself\n"
            "invisible at will, so it entirely avoids\n"
            "notice even if it approaches people."),
        .pokemonScale = 457,
        .pokemonOffset = -2,
        .trainerScale = 256,
        .trainerOffset = 0,
        .frontPic = gMonFrontPic_Mew,
        .frontPicSize = MON_COORDS_SIZE(64, 48),
        .frontPicYOffset = 9,
        .frontAnimFrames = sAnims_Mew,
        .frontAnimId = ANIM_ZIGZAG_SLOW,
        .enemyMonElevation = 11,
        .backPic = gMonBackPic_Mew,
        .backPicSize = MON_COORDS_SIZE(64, 64),
        .backPicYOffset = 0,
        .backAnimId = BACK_ANIM_CONCAVE_ARC_SMALL,
        .palette = gMonPalette_Mew,
        .shinyPalette = gMonShinyPalette_Mew,
        .iconSprite = gMonIcon_Mew,
        .iconPalIndex = 0,
        FOOTPRINT(Mew)
        .levelUpLearnset = sMewLevelUpLearnset,
        .teachableLearnset = sMewTeachableLearnset,
    },

That's a lot of stuff! But don't worry, we'll go through it step by step throught the tutorial (and miles better than having this same data through 20+ files like it used to be).

We'll start by adding the self-explanatory data that's also present in pret's vanilla structure:

3. Define its basic species information

Edit src/data/pokemon/species_info.h:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     [SPECIES_NONE] = {0},
     ...

     [SPECIES_EGG] =
     {
         FRONT_PIC(Egg, 24, 24),
         .frontPicYOffset = 20,
         .backPic = gMonFrontPic_Egg,
         .backPicSize = MON_COORDS_SIZE(24, 24),
         .backPicYOffset = 20,
         .palette = gMonPalette_Egg,
         .shinyPalette = gMonPalette_Egg,
         ICON(Egg, 1),
     },

+    [SPECIES_MEWTHREE] =
+    {
+       .baseHP        = 106,
+       .baseAttack    = 150,
+       .baseDefense   = 70,
+       .baseSpeed     = 140,
+       .baseSpAttack  = 194,
+       .baseSpDefense = 120,
+       .types = { TYPE_PSYCHIC, TYPE_PSYCHIC },
+       .catchRate = 3,
+       .expYield = 255,
+       .evYield_SpAttack  = 3,
+       .genderRatio = MON_GENDERLESS,
+       .eggCycles = 120,
+       .friendship = 0,
+       .growthRate = GROWTH_SLOW,
+       .eggGroups = { EGG_GROUP_NO_EGGS_DISCOVERED, EGG_GROUP_NO_EGGS_DISCOVERED },
+       .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_NONE },
+       .bodyColor = BODY_COLOR_PURPLE,
+    },
 };

The . is the structure reference operator in C to refer to the member object of the structure SpeciesInfo.

  • baseHP, baseAttack, baseDefense, baseSpeed, baseSpAttack and baseSpDefense are the base stats. They can't go higher than 255.
  • You may be confused as to why types has TYPE_PSYCHIC twice. This is because the way the game determines single-type mon is to define both types the same.
    • If we don't, it defaults to Normal due to it being the first type defined.
  • catchRate is how likely it is to catch a Pokémon, the lower the value, the harder it is to catch. Legendaries generally have a catch rate of 3, so we put that here.
  • expYield is the base amount of experience that a Pokémon gives when defeated/caught. In vanilla, this value caps at 255, but we've increased it to a maximum of 65535 accomodate later gen's higher experience yields. (The highest official value is Blissey's with 608, so going beyond this point may cause exponential gains that could break the system 😱)
    • If you noticed, Mew's had some #ifs, #elifs and #endif around it. This is because its yield has changed over time, and we let you choose which ones you want. This is not relevant to our Mewthree however, so we can just put a single .expYield = 255, line here.
  • evYield_HP, evYield_Attack, evYield_Defense, evYield_Speed, evYield_SpAttack and evYield_SpDefense are how many EVs does the Pokémon give when they're caught. Each of these fields can have a value of 3 at most. Officially, no Pokémon give out more than 3 EVs total, with them being determined by their evolution stage (eg, Pichu, Pikachu and Raichu give 1, 2 and 3 Speed EVs respectively), and they tend to be associated with its higher stats. Since our Mewthree is a Special Attack monster, we'll be consistent and make it give out 3 Special Attack EVs, but you're always free to assign whatever you feel like :)
    • Notice that the other evYield fields are not there. In C, numbers in a struct default to 0, so if we don't specify them, they'll be 0 all around! Less lines to worry about :D
  • itemCommon and itemRare are used to determine what items is the Pokémon holding when encountering it in the wild.
    • 50% for itemCommon and 5% for itemRare (boosted to 60%/20% when the first mon in the party has Compound Grass or Super Luck)
    • If they're both set as the same item, the item has a 100% chance of appearing.
  • genderRatio is a fun one.
    • There are 4 ways of handling this
      • PERCENT_FEMALE is what most Pokémon use, where you define how likely it's gonna be female. It supports decimals, so you can put PERCENT_FEMALE(12.5) to have a 1 in 8 chance of your mon to be female.
      • MON_MALE guarantees that all mon of this species will be male (eg. Tauros)
      • MON_FEMALE guarantees that all mon of this species will be female (eg. Miltank)
      • MON_GENDERLESS makes your species genderless, unable to breed with anything but Ditto to produce eggs. Most Legendaries are this, so we'll be chosing this as Mewthree's gender ratio.
    • When working with evolution lines and don't want their genders to change after evolving, be sure that their gender ratios match their stages and evolution methods. Azurill is the only case where there's a mismatch, causing 1/3 of all Azurill to change from Female to Male.
    • You might be wondering why some species have multiple defines for their genders, like SPECIES_MEOWSTIC_(FE)MALE. This is because those species have different stats and data from each other, so they're defined internally as different forms with MON_MALE and MON_FEMALE as gender ratios. If your species evolves depending on its gender and the evolutions have different stats, be sure to apply the correct evolution method!
  • eggCycles determines how fast an egg of this species will hatch. Doesn't matter much for evolved species or those that can't lay eggs, but we add the field here just in case.
  • friendship determines the amount of friendship of the mon when you catch it. Most Pokémon use STANDARD_FRIENDSHIP, but this creature of chaos does not want to be your friend, starting with 0.
  • growthRate determines the amounts of experience required to reach each level. Go here for more info.
  • eggGroups are used for breed compatibility. Most Legendaries and Mythicals have the EGG_GROUP_NO_EGGS_DISCOVERED group, and so does our Mewthree. Go here for more info.
  • abilities determines the potential abilites of our species. Notice how I also set the ability to ABILITY_INSOMNIA, so our little monster doesn't even need to sleep anymore. You can find the abilities for example here include/constants/abilities.h.
    • When both slot 1 and 2 are defined as not being ABILITY_NONE, their starting ability will be decided on a coin flip using their personality. They can later be changed using an Ability Capsule.
      • Certain Pokémon such as Zygarde and Rockruff have different forms to add additional abilities. As such, they cannot be changed using an Ability Capsule (though the Zygarde Cube can change Zygarde's ability by changing them to their corresponding form)
    • The 3rd slot is for Hidden Abilities. If defined as ABILITY_NONE, it will default to Slot 1 (eg. Metapod doesn't have a Hidden Ability, but Caterpie and Butterfree do). Go here and here for more info.
      • If the array is defined as {ABILITY_1, ABILITY_2}, the Hidden Ability is set as ABILITY_NONE.
  • bodyColor is used in the Pokédex as a search filter.
  • noFlip is used in to prevent front sprites from being flipped horizontally and cause weird issues, like Clawitzer's big claw changing sides.

That's all the basic fields present in vanilla emerald, so now let's take a look at the new fields added by the expansion.

4. Species Name

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .isLegendary = TRUE,
        .allPerfectIVs = TRUE,
+       .speciesName = _("Mewthree"),
    },
 };

The _() underscore function doesn't really exist - it's a convention borrowed from GNU gettext to let preproc know this is text to be converted to the custom encoding used by the Gen 3 Pokemon games.

5. Define its cry

Time for audio! We first need to convert an existing audio file to the format supported by the expansion.

Most formats are supported for conversion, but for simplicity's sake, we're gonna use an mp3 file.

Now, let's copy the file to the sound/direct_sound_samples/cries folder. Once that's done, let's run the following command:

ffmpeg -i sound/direct_sound_samples/cries/mewthree.mp3 -c:a pcm_s8 -ac 1 -ar 13379 sound/direct_sound_samples/cries/mewthree.aif

This will convert your audio file to .aif, which is what's read by the compiler.

Let's add the cry to the ROM via sound/direct_sound_data.inc.

.if P_FAMILY_PECHARUNT == TRUE
	.align 2
Cry_Pecharunt::
	.incbin "sound/direct_sound_samples/cries/pecharunt.bin"
.endif @ P_FAMILY_PECHARUNT

+	.align 2
+Cry_Mewthree::
+	.incbin "sound/direct_sound_samples/cries/mewthree.bin"

Then we add the cry ID to include/constants/cries.h:

enum {
    CRY_NONE,
    ...
#if P_FAMILY_TERAPAGOS
    CRY_TERAPAGOS,
#endif //P_FAMILY_TERAPAGOS
#if P_FAMILY_PECHARUNT
    CRY_PECHARUNT,
#endif //P_FAMILY_PECHARUNT
+   CRY_MEWTHREE,
    CRY_COUNT,
};

And then link it in sound/cry_tables.inc. cry_reverse in particular is for reversed cries used by moves such as Growl. The order of these two tables should match the order of the cry IDs, otherwise they'll be shifted.

	cry Cry_Terapagos
	cry Cry_Pecharunt
+	cry Cry_Mewthree
	cry_reverse Cry_Terapagos
	cry_reverse Cry_Pecharunt
+	cry_reverse Cry_Mewthree

Lastly, we add the cry to our species entry

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .isLegendary = TRUE,
        .allPerfectIVs = TRUE,
        .speciesName = _("Mewthree"),
+       .cryId = CRY_MEWTHREE,
    },
 };

And let's see how it sounds in-game:

https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/4f7667db-4db9-4bfd-a8dd-ece26f09f327

Good! Our monster now has a mighty roar!

You can now delete the mp3 from the cries folder now once you made sure that the cry sounds like how you want it to.

6. Define its Pokédex entry

First, we will need to add new index constants for its Pokédex entry. The index constants are divided into the Hoenn Pokédex, which contains all Pokémon native to the Hoenn region, and the National Pokédex containing all known Pokémon, which can be received after entering the hall of fame for the first time.

Edit include/constants/pokedex.h:

// National Pokedex order
enum {
    NATIONAL_DEX_NONE,
    // Kanto
    NATIONAL_DEX_BULBASAUR,
...
    NATIONAL_DEX_PECHARUNT,
+   NATIONAL_DEX_MEWTHREE,
};
 #define KANTO_DEX_COUNT     NATIONAL_DEX_MEW
 #define JOHTO_DEX_COUNT     NATIONAL_DEX_CELEBI

#if P_GEN_9_POKEMON == TRUE
-   #define NATIONAL_DEX_COUNT  NATIONAL_DEX_PECHARUNT
+   #define NATIONAL_DEX_COUNT  NATIONAL_DEX_MEWTHREE

Do keep in mind that if you intend to add your new species to the Hoenn Dex, you'll also want to add a HOENN_DEX constant for it, like this:

// Hoenn Pokedex order
enum {
    HOENN_DEX_NONE,
    HOENN_DEX_TREECKO,
...
    HOENN_DEX_DEOXYS,
+   HOENN_DEX_MEWTHREE,
};

- #define HOENN_DEX_COUNT (HOENN_DEX_DEOXYS + 1)
+ #define HOENN_DEX_COUNT (HOENN_DEX_MEWTHREE + 1)

Edit src/pokemon.c:

 const u16 sHoennToNationalOrder[NUM_SPECIES] = // Assigns Hoenn Dex Pokémon (Using National Dex Index)
 {
     HOENN_TO_NATIONAL(TREECKO),
     ...
     HOENN_TO_NATIONAL(DEOXYS),
+    HOENN_TO_NATIONAL(MEWTHREE),
 };

Now we can add the number and entry to our Mewthree:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .cryId = CRY_MEWTHREE,
+       .natDexNum = NATIONAL_DEX_MEWTHREE,
+       .categoryName = _("New Species"),
+       .height = 15,
+       .weight = 330,
+       .description = COMPOUND_STRING(
+           "The rumors became true.\n"
+           "This is Mew's final form.\n"
+           "Its power level is over 9000.\n"
+           "Has science gone too far?"),
+       .pokemonScale = 256,
+       .pokemonOffset = 0,
+       .trainerScale = 290,
+       .trainerOffset = 2,
    },
 };

image

The values pokemonScale, pokemonOffset, trainerScale and trainerOffset are used for the height comparison figure in the Pokédex.

height and weight are specified in decimeters and hectograms respectively (which are meters and kilograms multiplied by 10, so 2.5 meters are 25 decimeters).

In Pokémon Emerald, you can sort the Pokédex by name, height or weight. Apparently, the Pokémon order is hardcoded in the game files and not calculated from their data. Therefore we have to include our new Pokémon species at the right places. While the correct position for the alphabetical order is easy to find, it can become quite tedious for height and weight, so we added comments to the listings in order help out were they should fit.

Edit src/data/pokemon/pokedex_orders.h:

 const u16 gPokedexOrder_Alphabetical[] =
 {
     ...
     NATIONAL_DEX_MEW,
+    NATIONAL_DEX_MEWTHREE,
     NATIONAL_DEX_MEWTWO,
     ...
 };

 const u16 gPokedexOrder_Weight[] =
 {
     ...
     // 72.8 lbs / 33.0 kg
     //NATIONAL_DEX_MEWTWO_MEGA_Y,
     NATIONAL_DEX_ESCAVALIER,
     NATIONAL_DEX_FRILLISH,
     NATIONAL_DEX_DURANT,
     NATIONAL_DEX_CINDERACE,
+    NATIONAL_DEX_MEWTHREE,
     //NATIONAL_DEX_PERSIAN_ALOLAN,
     NATIONAL_DEX_TOEDSCOOL,
     // 73.4 lbs / 33.3 kg
     NATIONAL_DEX_DUGTRIO,
     ...
 };

 const u16 gPokedexOrder_Height[] =
 {
     ...
     // 4'11" / 1.5m
     ...
     NATIONAL_DEX_GLIMMORA,
     NATIONAL_DEX_WO_CHIEN,
     NATIONAL_DEX_IRON_LEAVES,
     NATIONAL_DEX_IRON_BOULDER,
+    NATIONAL_DEX_MEWTHREE,
    // 5'03" / 1.6m
     ...
 };

mGBA_lUBfmFEKUx

The Graphics

We will start by copying the following files for Mew (not Mewtwo) and rename it to mewthree.

cp -r graphics/pokemon/mew/. graphics/pokemon/mewthree

We aren't copying Mewtwo's folder because he has those pesky Mega Evolutions that will get in the way of what we're doing, so our sample will need to be pure from the source.

1. Edit the sprites

Let's edit the sprites. Start your favourite image editor (I recommend Aseprite or its free alternative, Libresprite) and change anim_front.png and back.png to meet your expectations.

Make sure that you are using the indexed mode and you have limited yourself to 15 colors!

Put the RGB values of your colors into normal.pal between the first and the last color and the RGB values for the shiny version into shiny.pal. Edit footprint.png using two colors in indexed mode, black and white. Finally, edit icon.png. Notice, that the icon will use one of 6 predefined palettes instead of normal.pal.

2. Add the sprites to the rom

Sadly, just putting the image files into the graphics folder is not enough. To use the sprites we have to register them, which is kind of tedious. First, create constants for the file paths. You'll want to add the constants for your species after the constants for the last valid species.

Edit src/data/graphics/pokemon.h:

#if P_FAMILY_PECHARUNT
    // const u32 gMonFrontPic_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/front.4bpp.lz");
    // const u32 gMonPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/normal.gbapal.lz");
    // const u32 gMonBackPic_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/back.4bpp.lz");
    // const u32 gMonShinyPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/shiny.gbapal.lz");
    // const u8 gMonIcon_Pecharunt[] = INCBIN_U8("graphics/pokemon/pecharunt/icon.4bpp");
#if P_FOOTPRINTS
    // const u8 gMonFootprint_Pecharunt[] = INCBIN_U8("graphics/pokemon/pecharunt/footprint.1bpp");
#endif //P_FOOTPRINTS
#endif //P_FAMILY_PECHARUNT

+   const u32 gMonFrontPic_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/anim_front.4bpp.lz");
+   const u32 gMonBackPic_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/back.4bpp.lz");
+   const u32 gMonPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/normal.gbapal.lz");
+   const u32 gMonShinyPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/shiny.gbapal.lz");
+   const u8 gMonIcon_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/icon.4bpp");
+   const u8 gMonFootprint_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/footprint.1bpp");

Please note that Pecharunt, the Pokémon that should be above your insertion for the time being, reads a front.png sprite instead of an anim_front.png sprite. This is because currently, Pecharunt lacks a 2nd frame. If the front sprite sheet of your species uses 2 frames, you should use anim_front.

It is also worth to mention that Pecharunt's sprites are commented out simply because they're currently missing.

3. Add the animations to the rom

You can define the animation order, in which the sprites will be shown. The first number is the sprite index (so 0 or 1) and the second number is the number of frames the sprite will be visible.

Edit src/data/pokemon_graphics/front_pic_anims.h:

#if P_FAMILY_PECHARUNT
PLACEHOLDER_ANIM_SINGLE_FRAME(Pecharunt);
#endif //P_FAMILY_PECHARUNT

+static const union AnimCmd sAnim_Mewthree_1[] =
+{
+    ANIMCMD_FRAME(1, 30),
+    ANIMCMD_FRAME(0, 20),
+    ANIMCMD_END,
+};
#if P_FAMILY_PECHARUNT
SINGLE_ANIMATION(Pecharunt);
#endif //P_FAMILY_PECHARUNT
+SINGLE_ANIMATION(Mewthree);
SINGLE_ANIMATION(Egg);

You might be wondering what PLACEHOLDER_ANIM_SINGLE_FRAME is. Well, since Pecharun only has 1 frame, we use what's called a preprocessor macro to have in a single line what otherwise would've been this in the C file:

static const union AnimCmd sAnim_Pecharunt_1[] =
{
    ANIMCMD_FRAME(0, 1),
    ANIMCMD_END,
}

Instead, we can use the already established macro that does the same thing, replacing the value in parenthesis with what we want (in this case, Pecharunt):

#define PLACEHOLDER_ANIM_SINGLE_FRAME(name)     \
static const union AnimCmd sAnim_##name##_1[] = \
{                                               \
    ANIMCMD_FRAME(0, 1),                        \
    ANIMCMD_END,                                \
}

4. Linking graphic information to our Pokémon

Now that we have all the external data ready, we just need to add it to gSpeciesInfo plus the rest of the animation and graphical data that we want to use:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .pokemonScale = 256,
        .pokemonOffset = 0,
        .trainerScale = 290,
        .trainerOffset = 2,
+       .frontPic = gMonFrontPic_Mewthree,
+       .frontPicSize = MON_COORDS_SIZE(64, 64),
+       .frontPicYOffset = 0,
+       .frontAnimFrames = sAnims_Mewthree,
+       .frontAnimId = ANIM_GROW_VIBRATE,
+       .frontAnimDelay = 15,
+       .enemyMonElevation = 6,
+       .backPic = gMonBackPic_Mewthree,
+       .backPicSize = MON_COORDS_SIZE(64, 64),
+       .backPicYOffset = 0,
+       .backAnimId = BACK_ANIM_CONCAVE_ARC_SMALL,
+       .palette = gMonPalette_Mewthree,
+       .shinyPalette = gMonShinyPalette_Mewthree,
        .iconSprite = gMonIcon_Mewthree,
        .iconPalIndex = 2,
+       FOOTPRINT(Mewthree)
    },
 };

Let's explain each of these:

  • frontPic:
    • Used to reference the front sprite, so in this case, we call for gMonFrontPic_Mewthree.
  • frontPicSize:
    • The two values (width and height) are used for defining the non-empty size of the front sprite, which is used in move animations. If you're unsure of the values, you can leave them both as 64.
  • frontPicYOffset:
    • Used to define what Y position the sprite sits at. This is used to set where they'd be "grounded". For the shadow, see enemyMonElevation.
  • frontAnimFrames:
    • We link our animation frame animations that we defined earlier here.
  • frontAnimId:
  • frontAnimDelay:
    • Sets a delay in frame count between when the Pokémon appears and when the animation starts.
  • enemyMonElevation:
    • Used to determine the altitude from the ground. Any value above 0 will show a shadow under the Pokémon, to signify that they're floating.
  • backPic:
    • Used to reference the back sprite, so in this case, we call for gMonBackPic_Mewthree.
  • backPicSize:
    • The two values (width and height) are used for defining the non-empty size of the back sprite, which is used in move animations. If you're unsure of the values, you can leave them both as 64.
  • backPicYOffset:
    • Used to define what Y position of the back sprite. When working with the animation debug menu, we recommend aligning the back sprite to the white background, as it was designed to properyly align with the real battle layout.
  • backAnimId:
    • Like frontAnimId except for the back sprites and them being a single frame. The IDs listed here are used to represent 3 different animations that happen based on the the Pokémon's nature.
  • palette:
    • Used to reference the non-shiny palette, so in this case, we call for gMonPalette_Mewthree.
  • shinyPalette:
    • Used to reference the shiny palette, so in this case, we call for gMonShinyPalette_Mewthree.
  • iconSprite:
    • Used to reference the icon sprite, so in this case, we call for gMonIcon_Mewthree.
  • iconPalIndex:
  • ICON
    • Here, you can choose between the six icon palettes; 0, 1, 2, 3, 4 and 5. All of them located in graphics/pokemon/icon_palettes.

      Open an icon sprite and load one of the palettes to find out which palette suits your icon sprite best.

  • FOOTPRINT
    • We made this single field into a macro so that they can be ignored when P_FOOTPRINTS is set to false. It's also why we don't have an "," after calling it like the other macros (we add it as part of the macro itself).
      #if P_FOOTPRINTS
      #define FOOTPRINT(sprite) .footprint = gMonFootprint_## sprite,
      #else
      #define FOOTPRINT(sprite)
      #endif
      

The Data - Part 2

We're almost there just a bit left!

1. Species Flags

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_NONE },
        .bodyColor = BODY_COLOR_PURPLE,
+       .isLegendary = TRUE,
+       .allPerfectIVs = TRUE,
    },
 };

Each species flag provides properties to the species:

  • isLegendary:
    • Guarantees 3 perfect IVs upon generating the Pokémon*.
  • isMythical:
    • Guarantees 3 perfect IVs upon generating the Pokémon*.
    • Is skipped during Pokédex evaluations.
      • Unless it also has the dexForceRequired flag.
    • Cannot obtain Gigantamax factor via ToggleGigantamaxFactor.
  • isUltraBeast:
    • Guarantees 3 perfect IVs upon generating the Pokémon*.
    • Beast Ball's multiplier is set to x5 for this species.
      • All other ball multipliers are set to x0.1.
  • isParadoxForm:
    • Currently has no functionality but can be utilized by users for their own benefits.
  • isMegaEvolution:
    • A Mega indicator is added to the battle box indicating that they're Mega Evolved.
    • The species doesn't receive affection benefits.
    • Required when adding new Mega Evolutions.
  • isPrimalReversion:
    • A Primal Reversion indicator (Alpha or Omega for Kyogre/Groudon respectively) is added to the battle box indicating that they're Primal Reverted.
    • Required when adding new Primal Reversions.
  • isUltraBurst:
    • Required when adding new Ultra Burst forms.
  • isGigantamax:
    • Used to determine if Gigantamax forms should have their GMax moves or not.
    • Required when adding new Gigantamax forms.
  • isAlolanForm, isGalarianForm, isHisuianForm, isPaldeanForm:
    • In the future, these will be used to determine breeding offspring from different based on their region.
  • cannotBeTraded:
    • This species cannot be traded away (like Black/White Kyurem).
  • allPerfectIVs:
    • Guarantees 6 perfect IVs upon generating the Pokémon (like LGPE's Partner Pikachu and Eevee).
  • tmIlliterate:
    • This species will be unable to learn the universal moves.

*: As long as P_LEGENDARY_PERFECT_IVS is set to GEN_6 or higher.

2. Delimit the moveset

Let's begin with the moves that can be learned by leveling up.

Append to src/data/pokemon/level_up_learnsets.h:

#if P_FAMILY_PECHARUNT
static const struct LevelUpMove sPecharuntLevelUpLearnset[] = {
    LEVEL_UP_MOVE( 1, MOVE_SMOG),
    LEVEL_UP_MOVE( 1, MOVE_POISON_GAS),
    LEVEL_UP_MOVE( 1, MOVE_MEMENTO),
    LEVEL_UP_MOVE( 1, MOVE_ASTONISH),
    LEVEL_UP_MOVE( 8, MOVE_WITHDRAW),
    LEVEL_UP_MOVE(16, MOVE_DESTINY_BOND),
    LEVEL_UP_MOVE(24, MOVE_FAKE_TEARS),
    LEVEL_UP_MOVE(32, MOVE_PARTING_SHOT),
    LEVEL_UP_MOVE(40, MOVE_SHADOW_BALL),
    LEVEL_UP_MOVE(48, MOVE_MALIGNANT_CHAIN),
    LEVEL_UP_MOVE(56, MOVE_TOXIC),
    LEVEL_UP_MOVE(64, MOVE_NASTY_PLOT),
    LEVEL_UP_MOVE(72, MOVE_RECOVER),
    LEVEL_UP_END
};
#endif

+static const struct LevelUpMove sMewthreeLevelUpLearnset[] = {
+   LEVEL_UP_MOVE( 1, MOVE_CONFUSION),
+   LEVEL_UP_MOVE( 1, MOVE_DISABLE),
+   LEVEL_UP_MOVE(11, MOVE_BARRIER),
+   LEVEL_UP_MOVE(22, MOVE_SWIFT),
+   LEVEL_UP_MOVE(33, MOVE_PSYCH_UP),
+   LEVEL_UP_MOVE(44, MOVE_FUTURE_SIGHT),
+   LEVEL_UP_MOVE(55, MOVE_MIST),
+   LEVEL_UP_MOVE(66, MOVE_PSYCHIC),
+   LEVEL_UP_MOVE(77, MOVE_AMNESIA),
+   LEVEL_UP_MOVE(88, MOVE_RECOVER),
+   LEVEL_UP_MOVE(99, MOVE_SAFEGUARD),
+   LEVEL_UP_END
+};

Again, we need to register the learnset in gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        PALETTES(Mewthree),
        ICON(Mewthree, 2),
        FOOTPRINT(Mewthree)
+       .levelUpLearnset = sMewthreeLevelUpLearnset,
    },
 };

Next we need to specify which moves can be taught via TM, HM, or Move Tutor.

Append to src/data/pokemon/teachable_learnsets.h:

#if P_FAMILY_PECHARUNT
static const u16 sPecharuntTeachableLearnset[] = {
    ...
    MOVE_UNAVAILABLE,
};
#endif //P_FAMILY_PECHARUNT

+static const u16 sMewthreeTeachableLearnset[] = {
+   MOVE_FOCUS_PUNCH,
+   MOVE_WATER_PULSE,
+   MOVE_CALM_MIND,
+   MOVE_TOXIC,
+   MOVE_HAIL,
+   MOVE_BULK_UP,
+   MOVE_HIDDEN_POWER,
+   MOVE_SUNNY_DAY,
+   MOVE_TAUNT,
+   MOVE_ICE_BEAM,
+   MOVE_BLIZZARD,
+   MOVE_HYPER_BEAM,
+   MOVE_LIGHT_SCREEN,
+   MOVE_PROTECT,
+   MOVE_RAIN_DANCE,
+   MOVE_SAFEGUARD,
+   MOVE_FRUSTRATION,
+   MOVE_SOLAR_BEAM,
+   MOVE_IRON_TAIL,
+   MOVE_THUNDERBOLT,
+   MOVE_THUNDER,
+   MOVE_EARTHQUAKE,
+   MOVE_RETURN,
+   MOVE_PSYCHIC,
+   MOVE_SHADOW_BALL,
+   MOVE_BRICK_BREAK,
+   MOVE_DOUBLE_TEAM,
+   MOVE_REFLECT,
+   MOVE_SHOCK_WAVE,
+   MOVE_FLAMETHROWER,
+   MOVE_SANDSTORM,
+   MOVE_FIRE_BLAST,
+   MOVE_ROCK_TOMB,
+   MOVE_AERIAL_ACE,
+   MOVE_TORMENT,
+   MOVE_FACADE,
+   MOVE_SECRET_POWER,
+   MOVE_REST,
+   MOVE_SKILL_SWAP,
+   MOVE_SNATCH,
+   MOVE_STRENGTH,
+   MOVE_FLASH,
+   MOVE_ROCK_SMASH,
+   MOVE_MEGA_PUNCH,
+   MOVE_MEGA_KICK,
+   MOVE_BODY_SLAM,
+   MOVE_DOUBLE_EDGE,
+   MOVE_COUNTER,
+   MOVE_SEISMIC_TOSS,
+   MOVE_MIMIC,
+   MOVE_METRONOME,
+   MOVE_DREAM_EATER,
+   MOVE_THUNDER_WAVE,
+   MOVE_SUBSTITUTE,
+   MOVE_DYNAMIC_PUNCH,
+   MOVE_PSYCH_UP,
+   MOVE_SNORE,
+   MOVE_ICY_WIND,
+   MOVE_ENDURE,
+   MOVE_MUD_SLAP,
+   MOVE_ICE_PUNCH,
+   MOVE_SWAGGER,
+   MOVE_SLEEP_TALK,
+   MOVE_SWIFT,
+   MOVE_THUNDER_PUNCH,
+   MOVE_FIRE_PUNCH,
+   MOVE_UNAVAILABLE, // This is required to determine where the array ends.
+};
#endif

Once more, we need to register the learnset in gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        FOOTPRINT(Mewthree)
        .levelUpLearnset = sMewthreeLevelUpLearnset,
+       .teachableLearnset = sMewthreeTeachableLearnset,
    },
 };

If you want to create a Pokémon which can breed, you will need to edit src/data/pokemon/egg_moves.h.

3. Define the Evolutions

We want Mewthree to evolve from Mewtwo by reaching level 100.

Edit gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTWO] =
     {
        ...
        FOOTPRINT(Mewtwo)
        .isLegendary = TRUE,
        .levelUpLearnset = sMewtwoLevelUpLearnset,
        .teachableLearnset = sMewtwoTeachableLearnset,
        .formSpeciesIdTable = sMewtwoFormSpeciesIdTable,
        .formChangeTable = sMewtwoFormChangeTable,
+       .evolutions = EVOLUTION({EVO_LEVEL, 100, SPECIES_MEWTHREE}),
    },
 };

4. Make it appear!

Now Mewthree really does slumber in the games code - but we won't know until we make him appear somewhere! The legend tells that Mewthree is hiding somewhere in Petalburg Woods...

Edit src/data/wild_encounters.json:

         {
           "map": "MAP_PETALBURG_WOODS",
           "base_label": "gPetalburgWoods",
           "land_mons": {
             "encounter_rate": 20,
             "mons": [
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_POOCHYENA"
               },
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_WURMPLE"
               },
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_SHROOMISH"
               },
               {
-                "min_level": 6,
-                "max_level": 6,
-                "species": "SPECIES_POOCHYENA"
+                "min_level": 5,
+                "max_level": 5,
+                "species": "SPECIES_MEWTHREE"
               },
               ...
        }

Congratulations, you have created your own personal pocket monster! You may call yourself a mad scientist now.

Optional data

Now that you now have all the essential pieces to create a base species, there are some aspects that you might want to know if you want to do other stuff with your custom Pokémon.

1. Form tables

Found in src/data/pokemon/form_species_tables.h.

These are introduced to have a reference of what forms correspond to what Species of Pokémon. For example, we have Pikachu's table:

#if P_FAMILY_PIKACHU
static const u16 sPikachuFormSpeciesIdTable[] = {
    SPECIES_PIKACHU,
    SPECIES_PIKACHU_COSPLAY,
    SPECIES_PIKACHU_ROCK_STAR,
    SPECIES_PIKACHU_BELLE,
    SPECIES_PIKACHU_POP_STAR,
    SPECIES_PIKACHU_PH_D,
    SPECIES_PIKACHU_LIBRE,
    SPECIES_PIKACHU_ORIGINAL_CAP,
    SPECIES_PIKACHU_HOENN_CAP,
    SPECIES_PIKACHU_SINNOH_CAP,
    SPECIES_PIKACHU_UNOVA_CAP,
    SPECIES_PIKACHU_KALOS_CAP,
    SPECIES_PIKACHU_ALOLA_CAP,
    SPECIES_PIKACHU_PARTNER_CAP,
    SPECIES_PIKACHU_WORLD_CAP,
    FORM_SPECIES_END,
};
#endif //P_FAMILY_PIKACHU

We register the table each form entry in gSpeciesInfo.

    [SPECIES_PIKACHU] =
    {
        ...
        .teachableLearnset = sPikachuTeachableLearnset,
+       .formSpeciesIdTable = sPikachuFormSpeciesIdTable,
        .evolutions = EVOLUTION({EVO_ITEM, ITEM_THUNDER_STONE, SPECIES_RAICHU},
                                {EVO_NONE, 0, SPECIES_RAICHU_ALOLAN}),
    },

    [SPECIES_PIKACHU_COSPLAY] =
    {
        ...
        .teachableLearnset = sPikachuTeachableLearnset,
+       .formSpeciesIdTable = sPikachuFormSpeciesIdTable,
    },

...and so on.

What this allows us to do is to be able to get all forms of a Pokémon in our code by using the GetSpeciesFormTable function.

For example, in the HGSS dex, it lets us browse between the entries of every form available.:

image image

In addition, we have the GET_BASE_SPECIES_ID macro, which returns the first entry of the table (or return the species itself if it doesn't have a table registered). With this, you can check if a Pokémon is any form of a species. For example, making it so that the Light Ball affects all Pikachu forms:

    case HOLD_EFFECT_LIGHT_BALL:
        if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_PIKACHU && IS_MOVE_SPECIAL(move))
            modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0));
        break;

2. Form change tables

Found in src/data/pokemon/form_species_tables.h.

These tables, unlike the regular form tables, registers how Pokémon can switch between forms.

#if P_FAMILY_GASTLY
static const struct FormChange sGengarFormChangeTable[] = {
    {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GENGAR_MEGA, ITEM_GENGARITE},
    {FORM_CHANGE_BATTLE_GIGANTAMAX,          SPECIES_GENGAR_GIGANTAMAX},
    {FORM_CHANGE_TERMINATOR},
};
#endif //P_FAMILY_GASTLY

The first value is the type of form change. In the case of Gengar, we have both Mega Evolution and Gigantamax form changes.

The second value is the target form, to which the Pokémon will change into.

Values after that are referred as arguments, and needs to be put there depends on the type of form change, detailed in include/constants/form_change_types.h.

3. Gender differences

mGBA_Wq5cbDkNZG

You may have seen that there's a couple of duplicate fields with a "Female" suffix.

    [SPECIES_FRILLISH] =
    {
        ...
        .frontPic = gMonFrontPic_Frillish,
+       .frontPicFemale = gMonFrontPic_FrillishF,
        .frontPicSize = MON_COORDS_SIZE(56, 56),
+       .frontPicSizeFemale = MON_COORDS_SIZE(56, 56),
        .frontPicYOffset = 5,
        .frontAnimFrames = sAnims_Frillish,
        .frontAnimId = ANIM_RISING_WOBBLE,
        .backPic = gMonBackPic_Frillish,
+       .backPicFemale = gMonBackPic_FrillishF,
        .backPicSize = MON_COORDS_SIZE(40, 56),
+       .backPicSizeFemale = MON_COORDS_SIZE(40, 56),
        .backPicYOffset = 7,
        .backAnimId = BACK_ANIM_CONVEX_DOUBLE_ARC,
        .palette = gMonPalette_Frillish,
+       .paletteFemale = gMonPalette_FrillishF,
        .shinyPalette = gMonShinyPalette_Frillish,
+       .shinyPaletteFemale = gMonShinyPalette_FrillishF,
        .iconSprite = gMonIcon_Frillish,
+       .iconSpriteFemale = gMonIcon_FrillishF,
        .iconPalIndex = 0,
+       .iconPalIndexFemale = 1,
        FOOTPRINT(Frillish)
        .levelUpLearnset = sFrillishLevelUpLearnset,
        .teachableLearnset = sFrillishTeachableLearnset,
        .evolutions = EVOLUTION({EVO_LEVEL, 40, SPECIES_JELLICENT}),
    },

These are used to change the graphics of the Pokémon if they're female. If they're not registered, they default to the male values.

However, iconPalIndexFemale is a special case, where it's doesn't read the male icon palette if its iconSpriteFemale is set, so if you're setting a female icon, be sure to set their palette index as well.

This is a modified version of the original tutorial about adding new Pokémon species available in Pokeemerald's wiki.

Despite the persistent rumors about an incredibly strong third form of Mew hiding somewhere, it actually wasn't possible to catch it... OR WAS IT? In this tutorial, we will add a new Pokémon species to the game.

IMPORTANT: This tutorial applies to 1.7.x versions.

Changes compared to vanilla

The main things that the Expansion changes are listed here.

  • Still Front Pics (gMonStillFrontPic_YourPokemon) and by extension src/anim_mon_front_pics.c have been removed.
  • src/data/pokemon/cry_ids.h doesn't exist anymore.
  • You have 6 icon palettes available instead of the base 3.
  • Most tables that use SPECIES_x as indexes have been moved to gSpeciesInfo.

Content

Useful resources

You can open a sprite debug menu by pressing Select in a Pokémon's summary screen outside of battle.

mGBA_6WOo1TSlsn

The Data - Part 1

Our plan is as simple as it is brilliant: clone Mewtwo... and make it even stronger!

1. Declare a species constant

Our first step towards creating a new digital lifeform is to define its own species constant.

Edit include/constants/species.h:

 #define SPECIES_NONE                                    0
 #define SPECIES_BULBASAUR                               1
 ...
 #define SPECIES_EEVEE_PARTNER                           PLACEHOLDER_START + 54
+#define SPECIES_MEWTHREE                                PLACEHOLDER_START + 55

-#define GIGANTAMAX_START                                SPECIES_EEVEE_PARTNER
+#define GIGANTAMAX_START                                SPECIES_MEWTHREE

 // Gigantamax Forms
 #define SPECIES_VENUSAUR_GIGANTAMAX                     GIGANTAMAX_START + 1

This number is stored in a Pokémon's save structure. These should generally never change, otherwise your saved Pokémon species will change as well.

We add this before Gigantamax forms because they're temporary forms that shouldn't normally be saved into a Pokémon's save structure.

Now, let's see how it looks in-game!

image

Hmmm, something's not right...

Oh, I know! We need to add the rest of the data! Normally, the vanilla game would crash if we try to look up anything about Mewthree in this state, but the expansion defaults all of its data to SPECIES_NONE.

Now, let's see what needs to be done.

2. SpeciesInfo's structure

Now, to better understand Mewtwo, we also need to understand Mew. Let's look at its data.

    [SPECIES_MEW] =
    {
        .baseHP        = 100,
        .baseAttack    = 100,
        .baseDefense   = 100,
        .baseSpeed     = 100,
        .baseSpAttack  = 100,
        .baseSpDefense = 100,
        .types = { TYPE_PSYCHIC, TYPE_PSYCHIC },
        .catchRate = 45,
    #if P_UPDATED_EXP_YIELDS >= GEN_8
        .expYield = 300,
    #elif P_UPDATED_EXP_YIELDS >= GEN_5
        .expYield = 270,
    #else
        .expYield = 64,
    #endif
        .evYield_HP = 3,
        .itemCommon = ITEM_LUM_BERRY,
        .itemRare = ITEM_LUM_BERRY,
        .genderRatio = MON_GENDERLESS,
        .eggCycles = 120,
        .friendship = 100,
        .growthRate = GROWTH_MEDIUM_SLOW,
        .eggGroups = { EGG_GROUP_NO_EGGS_DISCOVERED, EGG_GROUP_NO_EGGS_DISCOVERED },
        .abilities = { ABILITY_SYNCHRONIZE, ABILITY_NONE },
        .bodyColor = BODY_COLOR_PINK,
        .isMythical = TRUE,
        .speciesName = _("Mew"),
        .cryId = CRY_MEW,
        .natDexNum = NATIONAL_DEX_MEW,
        .categoryName = _("New Species"),
        .height = 4,
        .weight = 40,
        .description = COMPOUND_STRING(
            "A Mew is said to possess the genes of all\n"
            "Pokémon. It is capable of making itself\n"
            "invisible at will, so it entirely avoids\n"
            "notice even if it approaches people."),
        .pokemonScale = 457,
        .pokemonOffset = -2,
        .trainerScale = 256,
        .trainerOffset = 0,
        FRONT_PIC(Mew, 64, 48),
        .frontPicYOffset = 9,
        .frontAnimFrames = sAnims_Mew,
        .frontAnimId = ANIM_ZIGZAG_SLOW,
        .enemyMonElevation = 11,
        BACK_PIC(Mew, 64, 64),
        .backPicYOffset = 0,
        .backAnimId = BACK_ANIM_CONCAVE_ARC_SMALL,
        PALETTES(Mew),
        ICON(Mew, 0),
        FOOTPRINT(Mew)
        LEARNSETS(Mew),
    },

That's a lot of stuff! But don't worry, we'll go through it step by step throught the tutorial (and miles better than having this same data through 20+ files like it used to be).

We'll start by adding the self-explanatory data that's also present in pret's vanilla structure:

3. Define its basic species information

Edit src/data/pokemon/species_info.h:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     [SPECIES_NONE] = {0},
     ...

     [SPECIES_EGG] =
     {
         FRONT_PIC(Egg, 24, 24),
         .frontPicYOffset = 20,
         .backPic = gMonFrontPic_Egg,
         .backPicSize = MON_COORDS_SIZE(24, 24),
         .backPicYOffset = 20,
         .palette = gMonPalette_Egg,
         .shinyPalette = gMonPalette_Egg,
         ICON(Egg, 1),
     },

+    [SPECIES_MEWTHREE] =
+    {
+       .baseHP        = 106,
+       .baseAttack    = 150,
+       .baseDefense   = 70,
+       .baseSpeed     = 140,
+       .baseSpAttack  = 194,
+       .baseSpDefense = 120,
+       .types = { TYPE_PSYCHIC, TYPE_PSYCHIC },
+       .catchRate = 3,
+       .expYield = 255,
+       .evYield_SpAttack  = 3,
+       .genderRatio = MON_GENDERLESS,
+       .eggCycles = 120,
+       .friendship = 0,
+       .growthRate = GROWTH_SLOW,
+       .eggGroups = { EGG_GROUP_NO_EGGS_DISCOVERED, EGG_GROUP_NO_EGGS_DISCOVERED },
+       .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_NONE },
+       .bodyColor = BODY_COLOR_PURPLE,
+    },
 };

The . is the structure reference operator in C to refer to the member object of the structure SpeciesInfo.

  • baseHP, baseAttack, baseDefense, baseSpeed, baseSpAttack and baseSpDefense are the base stats. They can't go higher than 255.
  • You may be confused as to why types has TYPE_PSYCHIC twice. This is because the way the game determines single-type mon is to define both types the same.
    • If we don't, it defaults to Normal due to it being the first type defined.
  • catchRate is how likely it is to catch a Pokémon, the lower the value, the harder it is to catch. Legendaries generally have a catch rate of 3, so we put that here.
  • expYield is the base amount of experience that a Pokémon gives when defeated/caught. In vanilla, this value caps at 255, but we've increased it to a maximum of 65535 accomodate later gen's higher experience yields. (The highest official value is Blissey's with 608, so going beyond this point may cause exponential gains that could break the system 😱)
    • If you noticed, Mew's had some #ifs, #elifs and #endif around it. This is because its yield has changed over time, and we let you choose which ones you want. This is not relevant to our Mewthree however, so we can just put a single .expYield = 255, line here.
  • evYield_HP, evYield_Attack, evYield_Defense, evYield_Speed, evYield_SpAttack and evYield_SpDefense are how many EVs does the Pokémon give when they're caught. Each of these fields can have a value of 3 at most. Officially, no Pokémon give out more than 3 EVs total, with them being determined by their evolution stage (eg, Pichu, Pikachu and Raichu give 1, 2 and 3 Speed EVs respectively), and they tend to be associated with its higher stats. Since our Mewthree is a Special Attack monster, we'll be consistent and make it give out 3 Special Attack EVs, but you're always free to assign whatever you feel like :)
    • Notice that the other evYield fields are not there. In C, numbers in a struct default to 0, so if we don't specify them, they'll be 0 all around! Less lines to worry about :D
  • itemCommon and itemRare are used to determine what items is the Pokémon holding when encountering it in the wild.
    • 50% for itemCommon and 5% for itemRare (boosted to 60%/20% when the first mon in the party has Compound Grass or Super Luck)
    • If they're both set as the same item, the item has a 100% chance of appearing.
  • genderRatio is a fun one.
    • There are 4 ways of handling this
      • PERCENT_FEMALE is what most Pokémon use, where you define how likely it's gonna be female. It supports decimals, so you can put PERCENT_FEMALE(12.5) to have a 1 in 8 chance of your mon to be female.
      • MON_MALE guarantees that all mon of this species will be male (eg. Tauros)
      • MON_FEMALE guarantees that all mon of this species will be female (eg. Miltank)
      • MON_GENDERLESS makes your species genderless, unable to breed with anything but Ditto to produce eggs. Most Legendaries are this, so we'll be chosing this as Mewthree's gender ratio.
    • When working with evolution lines and don't want their genders to change after evolving, be sure that their gender ratios match their stages and evolution methods. Azurill is the only case where there's a mismatch, causing 1/3 of all Azurill to change from Female to Male.
    • You might be wondering why some species have multiple defines for their genders, like SPECIES_MEOWSTIC_(FE)MALE. This is because those species have different stats and data from each other, so they're defined internally as different forms with MON_MALE and MON_FEMALE as gender ratios. If your species evolves depending on its gender and the evolutions have different stats, be sure to apply the correct evolution method!
  • eggCycles determines how fast an egg of this species will hatch. Doesn't matter much for evolved species or those that can't lay eggs, but we add the field here just in case.
  • friendship determines the amount of friendship of the mon when you catch it. Most Pokémon use STANDARD_FRIENDSHIP, but this creature of chaos does not want to be your friend, starting with 0.
  • growthRate determines the amounts of experience required to reach each level. Go here for more info.
  • eggGroups are used for breed compatibility. Most Legendaries and Mythicals have the EGG_GROUP_NO_EGGS_DISCOVERED group, and so does our Mewthree. Go here for more info.
  • abilities determines the potential abilites of our species. Notice how I also set the ability to ABILITY_INSOMNIA, so our little monster doesn't even need to sleep anymore. You can find the abilities for example here include/constants/abilities.h.
    • When both slot 1 and 2 are defined as not being ABILITY_NONE, their starting ability will be decided on a coin flip using their personality. They can later be changed using an Ability Capsule.
      • Certain Pokémon such as Zygarde and Rockruff have different forms to add additional abilities. As such, they cannot be changed using an Ability Capsule (though the Zygarde Cube can change Zygarde's ability by changing them to their corresponding form)
    • The 3rd slot is for Hidden Abilities. If defined as ABILITY_NONE, it will default to Slot 1 (eg. Metapod doesn't have a Hidden Ability, but Caterpie and Butterfree do). Go here and here for more info.
      • If the array is defined as {ABILITY_1, ABILITY_2}, the Hidden Ability is set as ABILITY_NONE.
  • bodyColor is used in the Pokédex as a search filter.
  • noFlip is used in to prevent front sprites from being flipped horizontally and cause weird issues, like Clawitzer's big claw changing sides.

That's all the basic fields present in vanilla emerald, so now let's take a look at the new fields added by the expansion.

4. Species Name

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .isLegendary = TRUE,
        .allPerfectIVs = TRUE,
+       .speciesName = _("Mewthree"),
    },
 };

The _() underscore function doesn't really exist - it's a convention borrowed from GNU gettext to let preproc know this is text to be converted to the custom encoding used by the Gen 3 Pokemon games.

5. Define its cry

Time for audio! We first need to convert an existing audio file to the format supported by the expansion.

Most formats are supported for conversion, but for simplicity's sake, we're gonna use this mp3 file.

Now, let's copy the file to the sound/direct_sound_samples/cries folder. Once that's done, let's run the following command:

ffmpeg -i sound/direct_sound_samples/cries/mewthree.mp3 -c:a pcm_s8 -ac 1 -ar 13379 sound/direct_sound_samples/cries/mewthree.aif

This will convert your audio file to .aif, which is what's read by the compiler.

Let's add the cry to the ROM via sound/direct_sound_data.inc.

.if P_FAMILY_PECHARUNT == TRUE
	.align 2
Cry_Pecharunt::
	.incbin "sound/direct_sound_samples/cries/pecharunt.bin"
.endif @ P_FAMILY_PECHARUNT

+	.align 2
+Cry_Mewthree::
+	.incbin "sound/direct_sound_samples/cries/mewthree.bin"

Then we add the cry ID to include/constants/cries.h:

enum {
    CRY_NONE,
    ...
#if P_FAMILY_TERAPAGOS
    CRY_TERAPAGOS,
#endif //P_FAMILY_TERAPAGOS
#if P_FAMILY_PECHARUNT
    CRY_PECHARUNT,
#endif //P_FAMILY_PECHARUNT
+   CRY_MEWTHREE,
    CRY_COUNT,
};

And then link it in sound/cry_tables.inc. cry_reverse in particular is for reversed cries used by moves such as Growl. The order of these two tables should match the order of the cry IDs, otherwise they'll be shifted.

	cry Cry_Terapagos
	cry Cry_Pecharunt
+	cry Cry_Mewthree
	cry_reverse Cry_Terapagos
	cry_reverse Cry_Pecharunt
+	cry_reverse Cry_Mewthree

Lastly, we add the cry to our species entry

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .isLegendary = TRUE,
        .allPerfectIVs = TRUE,
        .speciesName = _("Mewthree"),
+       .cryId = CRY_MEWTHREE,
    },
 };

And let's see how it sounds in-game:

https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/4f7667db-4db9-4bfd-a8dd-ece26f09f327

Good! Our monster now has a mighty roar!

You can now delete the mp3 from the cries folder now once you made sure that the cry sounds like how you want it to.

6. Define its Pokédex entry

First, we will need to add new index constants for its Pokédex entry. The index constants are divided into the Hoenn Pokédex, which contains all Pokémon native to the Hoenn region, and the National Pokédex containing all known Pokémon, which can be received after entering the hall of fame for the first time.

Edit include/constants/pokedex.h:

// National Pokedex order
enum {
    NATIONAL_DEX_NONE,
    // Kanto
    NATIONAL_DEX_BULBASAUR,
...
    NATIONAL_DEX_PECHARUNT,
+   NATIONAL_DEX_MEWTHREE,
};
 #define KANTO_DEX_COUNT     NATIONAL_DEX_MEW
 #define JOHTO_DEX_COUNT     NATIONAL_DEX_CELEBI

#if P_GEN_9_POKEMON == TRUE
-   #define NATIONAL_DEX_COUNT  NATIONAL_DEX_PECHARUNT
+   #define NATIONAL_DEX_COUNT  NATIONAL_DEX_MEWTHREE

Do keep in mind that if you intend to add your new species to the Hoenn Dex, you'll also want to add a HOENN_DEX constant for it, like this:

// Hoenn Pokedex order
enum {
    HOENN_DEX_NONE,
    HOENN_DEX_TREECKO,
...
    HOENN_DEX_DEOXYS,
+   HOENN_DEX_MEWTHREE,
};

- #define HOENN_DEX_COUNT (HOENN_DEX_DEOXYS + 1)
+ #define HOENN_DEX_COUNT (HOENN_DEX_MEWTHREE + 1)

Edit src/pokemon.c:

 const u16 sHoennToNationalOrder[NUM_SPECIES] = // Assigns Hoenn Dex Pokémon (Using National Dex Index)
 {
     HOENN_TO_NATIONAL(TREECKO),
     ...
     HOENN_TO_NATIONAL(DEOXYS),
+    HOENN_TO_NATIONAL(MEWTHREE),
 };

Now we can add the number and entry to our Mewthree:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .cryId = CRY_MEWTHREE,
+       .natDexNum = NATIONAL_DEX_MEWTHREE,
+       .categoryName = _("New Species"),
+       .height = 15,
+       .weight = 330,
+       .description = COMPOUND_STRING(
+           "The rumors became true.\n"
+           "This is Mew's final form.\n"
+           "Its power level is over 9000.\n"
+           "Has science gone too far?"),
+       .pokemonScale = 256,
+       .pokemonOffset = 0,
+       .trainerScale = 290,
+       .trainerOffset = 2,
    },
 };

image

The values pokemonScale, pokemonOffset, trainerScale and trainerOffset are used for the height comparison figure in the Pokédex.

height and weight are specified in decimeters and hectograms respectively (which are meters and kilograms multiplied by 10, so 2.5 meters are 25 decimeters).

In Pokémon Emerald, you can sort the Pokédex by name, height or weight. Apparently, the Pokémon order is hardcoded in the game files and not calculated from their data. Therefore we have to include our new Pokémon species at the right places. While the correct position for the alphabetical order is easy to find, it can become quite tedious for height and weight, so we added comments to the listings in order help out were they should fit.

Edit src/data/pokemon/pokedex_orders.h:

 const u16 gPokedexOrder_Alphabetical[] =
 {
     ...
     NATIONAL_DEX_MEW,
+    NATIONAL_DEX_MEWTHREE,
     NATIONAL_DEX_MEWTWO,
     ...
 };

 const u16 gPokedexOrder_Weight[] =
 {
     ...
     // 72.8 lbs / 33.0 kg
     //NATIONAL_DEX_MEWTWO_MEGA_Y,
     NATIONAL_DEX_ESCAVALIER,
     NATIONAL_DEX_FRILLISH,
     NATIONAL_DEX_DURANT,
     NATIONAL_DEX_CINDERACE,
+    NATIONAL_DEX_MEWTHREE,
     //NATIONAL_DEX_PERSIAN_ALOLAN,
     NATIONAL_DEX_TOEDSCOOL,
     // 73.4 lbs / 33.3 kg
     NATIONAL_DEX_DUGTRIO,
     ...
 };

 const u16 gPokedexOrder_Height[] =
 {
     ...
     // 4'11" / 1.5m
     ...
     NATIONAL_DEX_GLIMMORA,
     NATIONAL_DEX_WO_CHIEN,
     NATIONAL_DEX_IRON_LEAVES,
     NATIONAL_DEX_IRON_BOULDER,
+    NATIONAL_DEX_MEWTHREE,
    // 5'03" / 1.6m
     ...
 };

mGBA_lUBfmFEKUx

The Graphics

We will start by copying the following files for Mew (not Mewtwo) and rename it to mewthree.

cp -r graphics/pokemon/mew/. graphics/pokemon/mewthree

We aren't copying Mewtwo's folder because he has those pesky Mega Evolutions that will get in the way of what we're doing, so our sample will need to be pure from the source.

1. Edit the sprites

Let's edit the sprites. Start your favourite image editor (I recommend Aseprite or its free alternative, Libresprite) and change anim_front.png and back.png to meet your expectations.

Make sure that you are using the indexed mode and you have limited yourself to 15 colors!

Put the RGB values of your colors into normal.pal between the first and the last color and the RGB values for the shiny version into shiny.pal. Edit footprint.png using two colors in indexed mode, black and white. Finally, edit icon.png. Notice, that the icon will use one of 6 predefined palettes instead of normal.pal.

2. Add the sprites to the rom

Sadly, just putting the image files into the graphics folder is not enough. To use the sprites we have to register them, which is kind of tedious. First, create constants for the file paths. You'll want to add the constants for your species after the constants for the last valid species.

Edit src/data/graphics/pokemon.h:

#if P_FAMILY_PECHARUNT
    // const u32 gMonFrontPic_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/front.4bpp.lz");
    // const u32 gMonPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/normal.gbapal.lz");
    // const u32 gMonBackPic_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/back.4bpp.lz");
    // const u32 gMonShinyPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/shiny.gbapal.lz");
    // const u8 gMonIcon_Pecharunt[] = INCBIN_U8("graphics/pokemon/pecharunt/icon.4bpp");
#if P_FOOTPRINTS
    // const u8 gMonFootprint_Pecharunt[] = INCBIN_U8("graphics/pokemon/pecharunt/footprint.1bpp");
#endif //P_FOOTPRINTS
#endif //P_FAMILY_PECHARUNT

+   const u32 gMonFrontPic_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/anim_front.4bpp.lz");
+   const u32 gMonBackPic_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/back.4bpp.lz");
+   const u32 gMonPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/normal.gbapal.lz");
+   const u32 gMonShinyPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/shiny.gbapal.lz");
+   const u8 gMonIcon_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/icon.4bpp");
+   const u8 gMonFootprint_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/footprint.1bpp");

Please note that Pecharunt, the Pokémon that should be above your insertion for the time being, reads a front.png sprite instead of an anim_front.png sprite. This is because currently, Pecharunt lacks a 2nd frame. If the front sprite sheet of your species uses 2 frames, you should use anim_front.

It is also worth to mention that Pecharunt's sprites are commented out simply because they're currently missing.

3. Add the animations to the rom

You can define the animation order, in which the sprites will be shown. The first number is the sprite index (so 0 or 1) and the second number is the number of frames the sprite will be visible.

Edit src/data/pokemon_graphics/front_pic_anims.h:

#if P_FAMILY_PECHARUNT
PLACEHOLDER_ANIM_SINGLE_FRAME(Pecharunt);
#endif //P_FAMILY_PECHARUNT

+static const union AnimCmd sAnim_Mewthree_1[] =
+{
+    ANIMCMD_FRAME(1, 30),
+    ANIMCMD_FRAME(0, 20),
+    ANIMCMD_END,
+};
#if P_FAMILY_PECHARUNT
SINGLE_ANIMATION(Pecharunt);
#endif //P_FAMILY_PECHARUNT
+SINGLE_ANIMATION(Mewthree);
SINGLE_ANIMATION(Egg);

You might be wondering what PLACEHOLDER_ANIM_SINGLE_FRAME is. Well, since Pecharun only has 1 frame, we use what's called a preprocessor macro to have in a single line what otherwise would've been this in the C file:

static const union AnimCmd sAnim_Pecharunt_1[] =
{
    ANIMCMD_FRAME(0, 1),
    ANIMCMD_END,
}

Instead, we can use the already established macro that does the same thing, replacing the value in parenthesis with what we want (in this case, Pecharunt):

#define PLACEHOLDER_ANIM_SINGLE_FRAME(name)     \
static const union AnimCmd sAnim_##name##_1[] = \
{                                               \
    ANIMCMD_FRAME(0, 1),                        \
    ANIMCMD_END,                                \
}

4. Linking graphic information to our Pokémon

Now that we have all the external data ready, we just need to add it to gSpeciesInfo plus the rest of the animation and graphical data that we want to use:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .pokemonScale = 256,
        .pokemonOffset = 0,
        .trainerScale = 290,
        .trainerOffset = 2,
+       FRONT_PIC(Mewthree, 64, 64),
+       .frontPicYOffset = 0,
+       .frontAnimFrames = sAnims_Mewthree,
+       .frontAnimId = ANIM_GROW_VIBRATE,
+       .frontAnimDelay = 15,
+       .enemyMonElevation = 6,
+       BACK_PIC(Mewthree, 64, 64),
+       .backPicYOffset = 0,
+       .backAnimId = BACK_ANIM_CONCAVE_ARC_SMALL,
+       PALETTES(Mewthree),
+       ICON(Mewthree, 2),
+       FOOTPRINT(Mewthree)
    },
 };

Let's explain each of these:

  • FRONT_PIC:
    • This is a macro handles both the frontPic and frontPicSize fields.
      #define FRONT_PIC(sprite, width, height)                    \
              .frontPic = gMonFrontPic_## sprite,                 \
              .frontPicSize = MON_COORDS_SIZE(width, height)
      
    • The first value in the macro is used to reference the front sprite, so in this case, using Mewthree will call for gMonFrontPic_Mewthree.
    • The second and third values (width and height) are used for defining the non-empty size of the sprite, which is used in move animations. If you're unsure of the values, you can leave them both as 64.
  • frontPicYOffset:
    • Used to define what Y position the sprite sits at. This is used to set where they'd be "grounded". For the shadow, see enemyMonElevation.
  • frontAnimFrames:
    • We link our animation frame animations that we defined earlier here.
  • frontAnimId:
  • frontAnimDelay:
    • Sets a delay in frame count between when the Pokémon appears and when the animation starts.
  • enemyMonElevation:
    • Used to determine the altitude from the ground. Any value above 0 will show a shadow under the Pokémon, to signify that they're floating.
  • BACK_PIC:
    • A macro like FRONT_PIC except for the back sprite handling the frontPic and frontPicSize fields in the same way.
      #define BACK_PIC(sprite, width, height)                     \
              .backPic = gMonBackPic_## sprite,                   \
              .backPicSize = MON_COORDS_SIZE(width, height)
      
  • backPicYOffset:
    • Used to define what Y position of the back sprite. When working with the animation debug menu, we recommend aligning the back sprite to the white background, as it was designed to properyly align with the real battle layout.
  • backAnimId:
    • Like frontAnimId except for the back sprites and them being a single frame. The IDs listed here are used to represent 3 different animations that happen based on the the Pokémon's nature.
  • PALETTES
    • This macro was created to handle both regular and shiny palettes of a Pokémon. It just needs the species suffix to call the corresponding palette.
      #define PALETTES(pal)                                       \
              .palette = gMonPalette_## pal,                      \
              .shinyPalette = gMonShinyPalette_## pal
      
  • ICON
    • This macro is calls both the icon sprite and its palette.

      #define ICON(sprite, palId)                                 \
              .iconSprite = gMonIcon_## sprite,                   \
              .iconPalIndex = palId
      

      Here, you can choose between the six icon palettes; 0, 1, 2, 3, 4 and 5. All of them located in graphics/pokemon/icon_palettes.

      Open an icon sprite and load one of the palettes to find out which palette suits your icon sprite best.

  • FOOTPRINT
    • We made this single field into a macro so that they can be ignored when P_FOOTPRINTS is set to false. It's also why we don't have an "," after calling it like the other macros (we add it as part of the macro itself).
      #if P_FOOTPRINTS
      #define FOOTPRINT(sprite) .footprint = gMonFootprint_## sprite,
      #else
      #define FOOTPRINT(sprite)
      #endif
      

The Data - Part 2

We're almost there just a bit left!

1. Species Flags

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        .abilities = { ABILITY_INSOMNIA, ABILITY_NONE, ABILITY_NONE },
        .bodyColor = BODY_COLOR_PURPLE,
+       .isLegendary = TRUE,
+       .allPerfectIVs = TRUE,
    },
 };

Each species flag provides properties to the species:

  • isLegendary:
    • Guarantees 3 perfect IVs upon generating the Pokémon*.
  • isMythical:
    • Guarantees 3 perfect IVs upon generating the Pokémon*.
    • Is skipped during Pokédex evaluations.
      • Unless it also has the dexForceRequired flag.
    • Cannot obtain Gigantamax factor via ToggleGigantamaxFactor.
  • isUltraBeast:
    • Guarantees 3 perfect IVs upon generating the Pokémon*.
    • Beast Ball's multiplier is set to x5 for this species.
      • All other ball multipliers are set to x0.1.
  • isParadoxForm:
    • Currently has no functionality but can be utilized by users for their own benefits.
  • isMegaEvolution:
    • A Mega indicator is added to the battle box indicating that they're Mega Evolved.
    • The species doesn't receive affection benefits.
    • Required when adding new Mega Evolutions.
  • isPrimalReversion:
    • A Primal Reversion indicator (Alpha or Omega for Kyogre/Groudon respectively) is added to the battle box indicating that they're Primal Reverted.
    • Required when adding new Primal Reversions.
  • isUltraBurst:
    • Required when adding new Ultra Burst forms.
  • isGigantamax:
    • Used to determine if Gigantamax forms should have their GMax moves or not.
    • Required when adding new Gigantamax forms.
  • isAlolanForm, isGalarianForm, isHisuianForm, isPaldeanForm:
    • In the future, these will be used to determine breeding offspring from different based on their region.
  • cannotBeTraded:
    • This species cannot be traded away (like Black/White Kyurem).
  • allPerfectIVs:
    • Guarantees 6 perfect IVs upon generating the Pokémon (like LGPE's Partner Pikachu and Eevee).
  • tmIlliterate:
    • This species will be unable to learn the universal moves.

*: As long as P_LEGENDARY_PERFECT_IVS is set to GEN_6 or higher.

2. Delimit the moveset

Let's begin with the moves that can be learned by leveling up.

Append to src/data/pokemon/level_up_learnsets.h:

#if P_FAMILY_PECHARUNT
static const struct LevelUpMove sPecharuntLevelUpLearnset[] = {
    LEVEL_UP_MOVE( 1, MOVE_SMOG),
    LEVEL_UP_MOVE( 1, MOVE_POISON_GAS),
    LEVEL_UP_MOVE( 1, MOVE_MEMENTO),
    LEVEL_UP_MOVE( 1, MOVE_ASTONISH),
    LEVEL_UP_MOVE( 8, MOVE_WITHDRAW),
    LEVEL_UP_MOVE(16, MOVE_DESTINY_BOND),
    LEVEL_UP_MOVE(24, MOVE_FAKE_TEARS),
    LEVEL_UP_MOVE(32, MOVE_PARTING_SHOT),
    LEVEL_UP_MOVE(40, MOVE_SHADOW_BALL),
    LEVEL_UP_MOVE(48, MOVE_MALIGNANT_CHAIN),
    LEVEL_UP_MOVE(56, MOVE_TOXIC),
    LEVEL_UP_MOVE(64, MOVE_NASTY_PLOT),
    LEVEL_UP_MOVE(72, MOVE_RECOVER),
    LEVEL_UP_END
};
#endif

+static const struct LevelUpMove sMewthreeLevelUpLearnset[] = {
+   LEVEL_UP_MOVE( 1, MOVE_CONFUSION),
+   LEVEL_UP_MOVE( 1, MOVE_DISABLE),
+   LEVEL_UP_MOVE(11, MOVE_BARRIER),
+   LEVEL_UP_MOVE(22, MOVE_SWIFT),
+   LEVEL_UP_MOVE(33, MOVE_PSYCH_UP),
+   LEVEL_UP_MOVE(44, MOVE_FUTURE_SIGHT),
+   LEVEL_UP_MOVE(55, MOVE_MIST),
+   LEVEL_UP_MOVE(66, MOVE_PSYCHIC),
+   LEVEL_UP_MOVE(77, MOVE_AMNESIA),
+   LEVEL_UP_MOVE(88, MOVE_RECOVER),
+   LEVEL_UP_MOVE(99, MOVE_SAFEGUARD),
+   LEVEL_UP_END
+};

Again, we need to register the learnset in gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        PALETTES(Mewthree),
        ICON(Mewthree, 2),
        FOOTPRINT(Mewthree)
+       .levelUpLearnset = sMewthreeLevelUpLearnset,
    },
 };

Next we need to specify which moves can be taught via TM, HM, or Move Tutor.

Append to src/data/pokemon/teachable_learnsets.h:

#if P_FAMILY_PECHARUNT
static const u16 sPecharuntTeachableLearnset[] = {
    ...
    MOVE_UNAVAILABLE,
};
#endif //P_FAMILY_PECHARUNT

+static const u16 sMewthreeTeachableLearnset[] = {
+   MOVE_FOCUS_PUNCH,
+   MOVE_WATER_PULSE,
+   MOVE_CALM_MIND,
+   MOVE_TOXIC,
+   MOVE_HAIL,
+   MOVE_BULK_UP,
+   MOVE_HIDDEN_POWER,
+   MOVE_SUNNY_DAY,
+   MOVE_TAUNT,
+   MOVE_ICE_BEAM,
+   MOVE_BLIZZARD,
+   MOVE_HYPER_BEAM,
+   MOVE_LIGHT_SCREEN,
+   MOVE_PROTECT,
+   MOVE_RAIN_DANCE,
+   MOVE_SAFEGUARD,
+   MOVE_FRUSTRATION,
+   MOVE_SOLAR_BEAM,
+   MOVE_IRON_TAIL,
+   MOVE_THUNDERBOLT,
+   MOVE_THUNDER,
+   MOVE_EARTHQUAKE,
+   MOVE_RETURN,
+   MOVE_PSYCHIC,
+   MOVE_SHADOW_BALL,
+   MOVE_BRICK_BREAK,
+   MOVE_DOUBLE_TEAM,
+   MOVE_REFLECT,
+   MOVE_SHOCK_WAVE,
+   MOVE_FLAMETHROWER,
+   MOVE_SANDSTORM,
+   MOVE_FIRE_BLAST,
+   MOVE_ROCK_TOMB,
+   MOVE_AERIAL_ACE,
+   MOVE_TORMENT,
+   MOVE_FACADE,
+   MOVE_SECRET_POWER,
+   MOVE_REST,
+   MOVE_SKILL_SWAP,
+   MOVE_SNATCH,
+   MOVE_STRENGTH,
+   MOVE_FLASH,
+   MOVE_ROCK_SMASH,
+   MOVE_MEGA_PUNCH,
+   MOVE_MEGA_KICK,
+   MOVE_BODY_SLAM,
+   MOVE_DOUBLE_EDGE,
+   MOVE_COUNTER,
+   MOVE_SEISMIC_TOSS,
+   MOVE_MIMIC,
+   MOVE_METRONOME,
+   MOVE_DREAM_EATER,
+   MOVE_THUNDER_WAVE,
+   MOVE_SUBSTITUTE,
+   MOVE_DYNAMIC_PUNCH,
+   MOVE_PSYCH_UP,
+   MOVE_SNORE,
+   MOVE_ICY_WIND,
+   MOVE_ENDURE,
+   MOVE_MUD_SLAP,
+   MOVE_ICE_PUNCH,
+   MOVE_SWAGGER,
+   MOVE_SLEEP_TALK,
+   MOVE_SWIFT,
+   MOVE_THUNDER_PUNCH,
+   MOVE_FIRE_PUNCH,
+   MOVE_UNAVAILABLE, // This is required to determine where the array ends.
+};
#endif

Once more, we need to register the learnset in gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTHREE] =
     {
        ...
        FOOTPRINT(Mewthree)
        .levelUpLearnset = sMewthreeLevelUpLearnset,
+       .teachableLearnset = sMewthreeTeachableLearnset,
    },
 };

If you want to create a Pokémon which can breed, you will need to edit src/data/pokemon/egg_moves.h.

3. Define the Evolutions

We want Mewthree to evolve from Mewtwo by reaching level 100.

Edit gSpeciesInfo:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     ...
     [SPECIES_MEWTWO] =
     {
        ...
        FOOTPRINT(Mewtwo)
        .isLegendary = TRUE,
        .levelUpLearnset = sMewtwoLevelUpLearnset,
        .teachableLearnset = sMewtwoTeachableLearnset,
        .formSpeciesIdTable = sMewtwoFormSpeciesIdTable,
        .formChangeTable = sMewtwoFormChangeTable,
+       .evolutions = EVOLUTION({EVO_LEVEL, 100, SPECIES_MEWTHREE}),
    },
 };

4. Make it appear!

Now Mewthree really does slumber in the games code - but we won't know until we make him appear somewhere! The legend tells that Mewthree is hiding somewhere in Petalburg Woods...

Edit src/data/wild_encounters.json:

         {
           "map": "MAP_PETALBURG_WOODS",
           "base_label": "gPetalburgWoods",
           "land_mons": {
             "encounter_rate": 20,
             "mons": [
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_POOCHYENA"
               },
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_WURMPLE"
               },
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_SHROOMISH"
               },
               {
-                "min_level": 6,
-                "max_level": 6,
-                "species": "SPECIES_POOCHYENA"
+                "min_level": 5,
+                "max_level": 5,
+                "species": "SPECIES_MEWTHREE"
               },
               ...
        }

Congratulations, you have created your own personal pocket monster! You may call yourself a mad scientist now.

Optional data

Now that you now have all the essential pieces to create a base species, there are some aspects that you might want to know if you want to do other stuff with your custom Pokémon.

1. Form tables

Found in src/data/pokemon/form_species_tables.h.

These are introduced to have a reference of what forms correspond to what Species of Pokémon. For example, we have Pikachu's table:

#if P_FAMILY_PIKACHU
static const u16 sPikachuFormSpeciesIdTable[] = {
    SPECIES_PIKACHU,
    SPECIES_PIKACHU_COSPLAY,
    SPECIES_PIKACHU_ROCK_STAR,
    SPECIES_PIKACHU_BELLE,
    SPECIES_PIKACHU_POP_STAR,
    SPECIES_PIKACHU_PH_D,
    SPECIES_PIKACHU_LIBRE,
    SPECIES_PIKACHU_ORIGINAL_CAP,
    SPECIES_PIKACHU_HOENN_CAP,
    SPECIES_PIKACHU_SINNOH_CAP,
    SPECIES_PIKACHU_UNOVA_CAP,
    SPECIES_PIKACHU_KALOS_CAP,
    SPECIES_PIKACHU_ALOLA_CAP,
    SPECIES_PIKACHU_PARTNER_CAP,
    SPECIES_PIKACHU_WORLD_CAP,
    FORM_SPECIES_END,
};
#endif //P_FAMILY_PIKACHU

We register the table each form entry in gSpeciesInfo.

    [SPECIES_PIKACHU] =
    {
        ...
        .teachableLearnset = sPikachuTeachableLearnset,
+       .formSpeciesIdTable = sPikachuFormSpeciesIdTable,
        .evolutions = EVOLUTION({EVO_ITEM, ITEM_THUNDER_STONE, SPECIES_RAICHU},
                                {EVO_NONE, 0, SPECIES_RAICHU_ALOLAN}),
    },

    [SPECIES_PIKACHU_COSPLAY] =
    {
        ...
        .teachableLearnset = sPikachuTeachableLearnset,
+       .formSpeciesIdTable = sPikachuFormSpeciesIdTable,
    },

...and so on.

What this allows us to do is to be able to get all forms of a Pokémon in our code by using the GetSpeciesFormTable function.

For example, in the HGSS dex, it lets us browse between the entries of every form available.:

image image

In addition, we have the GET_BASE_SPECIES_ID macro, which returns the first entry of the table (or return the species itself if it doesn't have a table registered). With this, you can check if a Pokémon is any form of a species. For example, making it so that the Light Ball affects all Pikachu forms:

    case HOLD_EFFECT_LIGHT_BALL:
        if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_PIKACHU && IS_MOVE_SPECIAL(move))
            modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0));
        break;

2. Form change tables

Found in src/data/pokemon/form_species_tables.h.

These tables, unlike the regular form tables, registers how Pokémon can switch between forms.

#if P_FAMILY_GASTLY
static const struct FormChange sGengarFormChangeTable[] = {
    {FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM, SPECIES_GENGAR_MEGA, ITEM_GENGARITE},
    {FORM_CHANGE_BATTLE_GIGANTAMAX,          SPECIES_GENGAR_GIGANTAMAX},
    {FORM_CHANGE_TERMINATOR},
};
#endif //P_FAMILY_GASTLY

The first value is the type of form change. In the case of Gengar, we have both Mega Evolution and Gigantamax form changes.

The second value is the target form, to which the Pokémon will change into.

Values after that are referred as arguments, and needs to be put there depends on the type of form change, detailed in include/constants/form_change_types.h.

3. Gender differences

mGBA_Wq5cbDkNZG

You may have seen that there's a couple of duplicate fields with a "Female" suffix.

    [SPECIES_FRILLISH] =
    {
        ...
        .frontPic = gMonFrontPic_Frillish,
+       .frontPicFemale = gMonFrontPic_FrillishF,
        .frontPicSize = MON_COORDS_SIZE(56, 56),
+       .frontPicSizeFemale = MON_COORDS_SIZE(56, 56),
        .frontPicYOffset = 5,
        .frontAnimFrames = sAnims_Frillish,
        .frontAnimId = ANIM_RISING_WOBBLE,
        .backPic = gMonBackPic_Frillish,
+       .backPicFemale = gMonBackPic_FrillishF,
        .backPicSize = MON_COORDS_SIZE(40, 56),
+       .backPicSizeFemale = MON_COORDS_SIZE(40, 56),
        .backPicYOffset = 7,
        .backAnimId = BACK_ANIM_CONVEX_DOUBLE_ARC,
        .palette = gMonPalette_Frillish,
+       .paletteFemale = gMonPalette_FrillishF,
        .shinyPalette = gMonShinyPalette_Frillish,
+       .shinyPaletteFemale = gMonShinyPalette_FrillishF,
        .iconSprite = gMonIcon_Frillish,
+       .iconSpriteFemale = gMonIcon_FrillishF,
        .iconPalIndex = 0,
+       .iconPalIndexFemale = 1,
        FOOTPRINT(Frillish)
        .levelUpLearnset = sFrillishLevelUpLearnset,
        .teachableLearnset = sFrillishTeachableLearnset,
        .evolutions = EVOLUTION({EVO_LEVEL, 40, SPECIES_JELLICENT}),
    },

These are used to change the graphics of the Pokémon if they're female. If they're not registered, they default to the male values.

However, iconPalIndexFemale is a special case, where it's doesn't read the male icon palette if its iconSpriteFemale is set, so if you're setting a female icon, be sure to set their palette index as well.

This is a modified version of the original tutorial about adding new Pokémon species available in Pokeemerald's wiki.

Despite the persistent rumors about an incredibly strong third form of Mew hiding somewhere, it actually wasn't possible to catch it... OR WAS IT? In this tutorial, we will add a new Pokémon species to the game.

IMPORTANT: This tutorial applies to Version 1.6.2 and lower.

Changes compared to vanilla

The main things that the Expansion changes are listed here.

  • Still Front Pics (gMonStillFrontPic_YourPokemon) and by extension src/anim_mon_front_pics.c have been removed.
  • src/data/pokemon/cry_ids.h doesn't exist anymore.

Content

The Graphics

We will start by copying the folder containing the sprites for Mewtwo and rename it to mewthree (pretty meta huh?):

cp -r graphics/pokemon/mewtwo graphics/pokemon/mewthree

1. Edit the sprites

Let's edit the sprites. Start your favourite image editor (I have used GIMP) and change anim_front.png, front.png and back.png to meet your expectations. Make sure that you are using the indexed mode and you have limited yourself to 15 colors! Put the RGB values of your colors into normal.pal between the first and the last color and the RGB values for the shiny version into shiny.pal. Edit footprint.png using two colors in indexed mode, black and white. Finally, edit icon.png. Notice, that the icon will use one of three predefined palettes instead of normal.pal.

2. Register the sprites

Sadly, just putting the image files into the graphics folder is not enough. To use the sprites we have to register them, which is kind of tedious. First, create constants for the file paths. You'll want to add the constants for your species after the constants for the last valid species. Edit include/graphics.h:

 extern const u32 gMonFrontPic_Calyrex[];
+extern const u32 gMonFrontPic_Mewthree[];
 extern const u32 gMonBackPic_Calyrex[];
+extern const u32 gMonBackPic_Mewthree[];
 extern const u32 gMonPalette_Calyrex[];
+extern const u32 gMonPalette_Mewthree[];
 extern const u32 gMonShinyPalette_Calyrex[];
+extern const u32 gMonShinyPalette_Mewthree[];
 //extern const u8 gMonIcon_Calyrex[];
+extern const u8 gMonIcon_Mewthree[];
 extern const u8 gMonFootprint_Calyrex[];
+extern const u8 gMonFootprint_Mewthree[];

Now link the graphic files.

Edit src/data/graphics/pokemon.h:

 const u32 gMonFrontPic_Calyrex[] = INCBIN_U32("graphics/pokemon/calyrex/front.4bpp.lz");
+const u32 gMonFrontPic_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/front.4bpp.lz");
 const u32 gMonBackPic_Calyrex[] = INCBIN_U32("graphics/pokemon/calyrex/back.4bpp.lz");
+const u32 gMonBackPic_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/back.4bpp.lz");
 const u32 gMonPalette_Calyrex[] = INCBIN_U32("graphics/pokemon/calyrex/normal.gbapal.lz");
+const u32 gMonPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/normal.gbapal.lz");
 const u32 gMonShinyPalette_Calyrex[] = INCBIN_U32("graphics/pokemon/calyrex/shiny.gbapal.lz");
+const u32 gMonShinyPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/shiny.gbapal.lz");
 //const u8 gMonIcon_Calyrex[] = INCBIN_U8("graphics/pokemon/calyrex/icon.4bpp");
+const u8 gMonIcon_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/icon.4bpp");
 const u8 gMonFootprint_Calyrex[] = INCBIN_U8("graphics/pokemon/calyrex/footprint.1bpp");
+const u8 gMonFootprint_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/footprint.1bpp");

Please note that Calyrex, the Pokémon that should be above your insertion for the time being, reads a "front.png" sprite instead of an "anim_front.png" sprite. This is because currently, Calyrex lacks a 2nd frame. If the front sprite sheet of your species uses 2 frames, you should use "anim_front".

It is also worth to mention that Calyrex's icon sprite is commented out simply because it's currently missing. If you do have an icon sprite sheet present inside your species' folder at graphics/pokemon, by all means do not comment entries involving the gMonIcon constants.

3. Animate the sprites

You can define the animation order, in which the sprites will be shown. The first number is the sprite index (so 0 or 1) and the second number is the number of frames the sprite will be visible.

Edit src/data/pokemon_graphics/front_pic_anims.h:

static const union AnimCmd sAnim_Enamorus_1[] =
{
    ANIMCMD_FRAME(0, 1),
    ANIMCMD_END,
};

+static const union AnimCmd sAnim_Mewthree_1[] =
+{
+    ANIMCMD_FRAME(1, 30),
+    ANIMCMD_FRAME(0, 20),
+    ANIMCMD_END,
+};
#endif
SINGLE_ANIMATION(Enamorus);
+SINGLE_ANIMATION(Mewthree);
#endif
 const union AnimCmd *const *const gMonFrontAnimsPtrTable[] =
 {
    [SPECIES_NONE]        = sAnims_None,
    [SPECIES_BULBASAUR]   = sAnims_Bulbasaur,
     ...
    [SPECIES_ENAMORUS] = sAnims_Enamorus,
+   [SPECIES_MEWTHREE] = sAnims_Mewthree,
#endif
     ...
 };

Because you are limited to two frames, there are already predefined front sprite animations, describing translations, rotations, scalings or color changes.

Edit src/pokemon.c:

 static const u8 sMonFrontAnimIdsTable[] =
 {
     [SPECIES_BULBASAUR - 1]     = ANIM_V_JUMPS_H_JUMPS,
     ...
     [SPECIES_DEOXYS_SPEED - 1]           = ANIM_GROW_VIBRATE,
+    [SPECIES_MEWTHREE - 1]               = ANIM_GROW_VIBRATE,
 };

There are also predefined back sprite animations for the back sprites as well.

Edit src/pokemon_animation.c:

 static const u8 sSpeciesToBackAnimSet[] =
 {
     [SPECIES_BULBASAUR]                    = BACK_ANIM_DIP_RIGHT_SIDE,
     ...
     [SPECIES_CHIMECHO]                     = BACK_ANIM_CONVEX_DOUBLE_ARC,
+    [SPECIES_MEWTHREE]                     = BACK_ANIM_GROW_STUTTER,
 };

If you want to delay the time between when the Pokémon appears and when the animation starts, you can add an entry to sMonAnimationDelayTable

Edit src/pokemon.c:

 static const u8 sMonAnimationDelayTable[NUM_SPECIES - 1] =
 {
     [SPECIES_BLASTOISE - 1]  = 50,
     ...
    [SPECIES_KYOGRE - 1]     = 60,
    [SPECIES_RAYQUAZA - 1]   = 60,
+   [SPECIES_MEWTHREE - 1]   = 15,
 };

If you want your Pokémon to fly above the ground, you can add an entry to gEnemyMonElevation.

Edit src/data/pokemon_graphics/enemy_mon_elevation.h:

 const u8 gEnemyMonElevation[NUM_SPECIES] =
 {
     [SPECIES_BUTTERFREE] = 10,
     ...
     [SPECIES_REGIDRAGO] = 5,
+    [SPECIES_MEWTHREE] = 6,
 };

4. Update the tables

Edit src/data/pokemon_graphics/front_pic_table.h:

 const struct CompressedSpriteSheet gMonFrontPicTable[] =
 {
     SPECIES_SPRITE(NONE, gMonFrontPic_CircledQuestionMark),
     SPECIES_SPRITE(BULBASAUR, gMonFrontPic_Bulbasaur),
     ...
     SPECIES_SPRITE(ENAMORUS, gMonFrontPic_Enamorus),
+    SPECIES_SPRITE(MEWTHREE, gMonFrontPic_Mewthree),
#endif
     ...
};

Edit src/data/pokemon_graphics/front_pic_coordinates.h:

 const struct MonCoords gMonFrontPicCoords[] =
 {
     ...
    [SPECIES_ENAMORUS]                     = { .size = MON_COORDS_SIZE(64, 64), .y_offset =  0 },
+   [SPECIES_MEWTHREE]                     = { .size = MON_COORDS_SIZE(64, 64), .y_offset =  0 },
#endif
     ...
 };

Edit src/data/pokemon_graphics/back_pic_table.h:

 const struct CompressedSpriteSheet gMonBackPicTable[] =
 {
     SPECIES_SPRITE(NONE, gMonBackPic_CircledQuestionMark),
     SPECIES_SPRITE(BULBASAUR, gMonBackPic_Bulbasaur),
     ...
    SPECIES_SPRITE(ENAMORUS, gMonBackPic_Enamorus),
+   SPECIES_SPRITE(MEWTHREE, gMonBackPic_Mewthree),
#endif
     ...
 };

Edit src/data/pokemon_graphics/back_pic_coordinates.h:

 const struct MonCoords gMonBackPicCoords[] =
 {
     ...
    [SPECIES_ENAMORUS]                     = { .size = MON_COORDS_SIZE(64, 64), .y_offset =  0 },
+   [SPECIES_MEWTHREE]                     = { .size = MON_COORDS_SIZE(56, 64), .y_offset = 1  },
#endif
     ...
 };

Edit src/data/pokemon_graphics/footprint_table.h:

 const u8 *const gMonFootprintTable[] =
 {
     [SPECIES_NONE] = gMonFootprint_Bulbasaur,
     [SPECIES_BULBASAUR] = gMonFootprint_Bulbasaur,
     ...
     [SPECIES_CALYREX] = gMonFootprint_Calyrex,
+    [SPECIES_MEWTHREE] = gMonFootprint_Mewthree,
#endif
     [SPECIES_EGG] = gMonFootprint_Bulbasaur,
 };

Edit src/data/pokemon_graphics/palette_table.h:

 const struct CompressedSpritePalette gMonPaletteTable[] =
 {
    SPECIES_PAL(NONE, gMonPalette_CircledQuestionMark),
    SPECIES_PAL(BULBASAUR, gMonPalette_Bulbasaur),
    ...
    SPECIES_PAL(ENAMORUS, gMonPalette_Enamorus),
+   SPECIES_PAL(MEWTHREE, gMonPalette_Mewthree),
#endif
    ...
};

Edit src/data/pokemon_graphics/shiny_palette_table.h:

const struct CompressedSpritePalette gMonShinyPaletteTable[] =
{
     SPECIES_SHINY_PAL(NONE, gMonShinyPalette_CircledQuestionMark),
     SPECIES_SHINY_PAL(BULBASAUR, gMonShinyPalette_Bulbasaur),
     ...
    SPECIES_SHINY_PAL(ENAMORUS, gMonShinyPalette_Enamorus),
+   SPECIES_SHINY_PAL(MEWTHREE, gMonShinyPalette_Mewthree),
#endif
     ...
};

Edit src/pokemon_icon.c:

 const u8 *const gMonIconTable[] =
 {
     [SPECIES_NONE] = gMonIcon_Bulbasaur,
     ...
    [SPECIES_ENAMORUS] = gMonIcon_Enamorus,
+   [SPECIES_MEWTHREE] = gMonIcon_Mewthree,
#endif
     ...
 };
 const u8 gMonIconPaletteIndices[] =
 {
     [SPECIES_NONE] = 0,
     ...
    [SPECIES_ENAMORUS] = 1,
+   [SPECIES_MEWTHREE] = 2,
    [SPECIES_VENUSAUR_MEGA] = 1,
     ...
 };

Here, you can choose between the six icon palettes; 0, 1, 2, 3, 4 and 5. All of them located in graphics/pokemon/icon_palettes.

Open an icon sprite and load one of the palettes to find out which palette suits your icon sprite best.

The Data

Our plan is as simple as it is brilliant: clone Mewtwo... and make it even stronger!

1. Declare a species constant

Our first step towards creating a new digital lifeform is to define its own species constant.

Edit include/constants/species.h:

 #define SPECIES_NONE 0
 #define SPECIES_BULBASAUR 1
 ...
 #define SPECIES_ENAMORUS 905
+#define SPECIES_MEWTHREE 906

-#define FORMS_START SPECIES_ENAMORUS
+#define FORMS_START SPECIES_MEWTHREE

2. Devise a name

This name will be displayed in the game. It may be different than the identifier of the species constant, especially when there are special characters involved.

Edit src/data/text/species_names.h:

 const u8 gSpeciesNames[][POKEMON_NAME_LENGTH + 1] = {
     [SPECIES_NONE] = _("??????????"),
     [SPECIES_BULBASAUR] = _("Bulbasaur"),
     ...
     [SPECIES_ENAMORUS] = _("Enamorus"),
+    [SPECIES_MEWTHREE] = _("Mewthree"),
 };

The _() underscore function doesn't really exist - it's a convention borrowed from GNU gettext to let preproc know this is text to be converted to the custom encoding used by the Gen 3 Pokemon games.

3. Define its Pokédex entry

First, we will need to add new index constants for its Pokédex entry. The index constants are divided into the Hoenn Pokédex, which contains all Pokémon native to the Hoenn region, and the National Pokédex containing all known Pokémon, which can be received after entering the hall of fame for the first time.

Edit include/constants/pokedex.h:

// National Pokedex order
enum {
    NATIONAL_DEX_NONE,
    // Kanto
    NATIONAL_DEX_BULBASAUR,
...
    NATIONAL_DEX_ENAMORUS,
+   NATIONAL_DEX_MEWTHREE,
};
 #define KANTO_DEX_COUNT     NATIONAL_DEX_MEW
 #define JOHTO_DEX_COUNT     NATIONAL_DEX_CELEBI
#if P_GEN_8_POKEMON == TRUE
-   #define NATIONAL_DEX_COUNT  NATIONAL_DEX_ENAMORUS
+   #define NATIONAL_DEX_COUNT  NATIONAL_DEX_MEWTHREE

Do keep in mind that if you intend to add your new species to the Hoenn Dex, you'll also want to add a HOENN_DEX constant for it, like this:

// Hoenn Pokedex order
enum {
    HOENN_DEX_NONE,
    HOENN_DEX_TREECKO,
...
    HOENN_DEX_DEOXYS,
+   HOENN_DEX_MEWTHREE,
...
};
- #define HOENN_DEX_COUNT (HOENN_DEX_DEOXYS + 1)
+ #define HOENN_DEX_COUNT (HOENN_DEX_MEWTHREE + 1)

Edit src/pokemon.c:

 // Assigns all species to the National Dex Index (Summary No. for National Dex)
 static const u16 sSpeciesToNationalPokedexNum[NUM_SPECIES - 1] =
 {
     SPECIES_TO_NATIONAL(ENAMORUS),
+    SPECIES_TO_NATIONAL(MEWTHREE),
 };

Just like before, if we want to insert our new species in the Hoenn Dex, we'll have to do a few extra steps:

 // Assigns all species to the Hoenn Dex Index (Summary No. for Hoenn Dex)
 static const u16 sSpeciesToHoennPokedexNum[NUM_SPECIES - 1] =
 {
     SPECIES_TO_HOENN(TREECKO),
     ...
     SPECIES_TO_HOENN(DEOXYS),
+    SPECIES_TO_HOENN(MEWTHREE),
 };
 const u16 gHoennToNationalOrder[NUM_SPECIES] = // Assigns Hoenn Dex Pokémon (Using National Dex Index)
 {
     HOENN_TO_NATIONAL(TREECKO),
     ...
     HOENN_TO_NATIONAL(DEOXYS),
+    HOENN_TO_NATIONAL(MEWTHREE),
 };

Now we can define the actual text of the Pokédex entry.

Append to src/data/pokemon/pokedex_text.h:

 const u8 gEnamorusPokedexText[] = _(
     "Its arrival brings an end to the\n"
     "winter. According to legend, this\n"
     "Pokémon's love gives rise to the\n"
     "budding of fresh life across the land.");

+const u8 gMewthreePokedexText[] = _(
+    "The rumors became true.\n"
+    "This is Mews final form.\n"
+    "Its power level is over 9000.\n"
+    "Has science gone too far?");

Finally, we will add the Pokédex entry for Mewthree and link the text to it.

Edit src/data/pokemon/pokedex_entries.h:

 const struct PokedexEntry gPokedexEntries[] =
 {
     ...
     [NATIONAL_DEX_ENAMORUS] =
     {
         .categoryName = _("Love-Hate"),
         .height = 16,
         .weight = 480,
         .description = gEnamorusPokedexText,
         .pokemonScale = 259,
         .pokemonOffset = 1,
         .trainerScale = 296,
         .trainerOffset = 1,
     },

+    [NATIONAL_DEX_MEWTHREE] =
+    {
+        .categoryName = _("NEW SPECIES"),
+        .height = 15,
+        .weight = 330,
+        .description = gMewthreePokedexText,
+        .pokemonScale = 256,
+        .pokemonOffset = 0,
+        .trainerScale = 290,
+        .trainerOffset = 2,
+    },
 #endif
 };

The values pokemonScale, pokemonOffset, trainerScale and trainerOffset are used for the height comparison figure in the Pokédex. Height and weight are specified in meters and kilograms respectively, while the last digit is the first decimal place.

In Pokémon Emerald, you can sort the Pokédex by name, height or weight. Apparently, the Pokémon order is hardcoded in the game files and not calculated from their data. Therefore we have to include our new Pokémon species at the right places. While the correct position for the alphabetical order is easy to find, it can become quite tedious for height and weight. To find the right position for your Pokémon, you may look at the tables sorted by height and weight respectively in the appendix.

Edit src/data/pokemon/pokedex_orders.h:

 const u16 gPokedexOrder_Alphabetical[] =
 {
     ...
     NATIONAL_DEX_MEW,
+    NATIONAL_DEX_MEWTHREE,
     NATIONAL_DEX_MEWTWO,
     ...
 };

 const u16 gPokedexOrder_Weight[] =
 {
     ...
     NATIONAL_DEX_ESCAVALIER,
     NATIONAL_DEX_FRILLISH,
     NATIONAL_DEX_DURANT,
     NATIONAL_DEX_CINDERACE,
+    NATIONAL_DEX_MEWTHREE,
     //NATIONAL_DEX_PERSIAN, // Alolan Form
     NATIONAL_DEX_DUGTRIO,
     ...
 };

 const u16 gPokedexOrder_Height[] =
 {
     ...
     NATIONAL_DEX_ZERAORA,
     NATIONAL_DEX_GRIMMSNARL,
     NATIONAL_DEX_MR_RIME,
+    NATIONAL_DEX_MEWTHREE,
     // 5'03" / 1.6m
     ...
 };

4. Define its species information

Edit src/data/pokemon/species_info.h:

 const struct SpeciesInfo gSpeciesInfo[] =
 {
     [SPECIES_NONE] = {0},
     ...

      [SPECIES_ENAMORUS] =
      {
         .baseHP        = 74,
         .baseAttack    = 115,
         .baseDefense   = 70,
         .baseSpeed     = 106,
         .baseSpAttack  = 135,
         .baseSpDefense = 80,
         .types = { TYPE_FAIRY, TYPE_FLYING},
         .catchRate = 3,
         .expYield = 261,
         .evYield_SpAttack    = 3,
         .genderRatio = MON_FEMALE,
         .eggCycles = 120,
         .friendship = 90,
         .growthRate = GROWTH_SLOW,
         .eggGroups = { EGG_GROUP_UNDISCOVERED, EGG_GROUP_UNDISCOVERED},
         .abilities = {ABILITY_HEALER, ABILITY_NONE, ABILITY_CONTRARY},
         .bodyColor = BODY_COLOR_PINK,
         .noFlip = FALSE,
         .flags = SPECIES_FLAG_LEGENDARY,
     },

+     [SPECIES_MEWTHREE] =
+     {
+        .baseHP        = 106,
+        .baseAttack    = 150,
+        .baseDefense   = 70,
+        .baseSpeed     = 140,
+        .baseSpAttack  = 194,
+        .baseSpDefense = 120,
+        .types = { TYPE_PSYCHIC, TYPE_PSYCHIC},
+        .catchRate = 3,
+        .expYield = 255,
+        .evYield_SpAttack  = 3,
+        .genderRatio = MON_GENDERLESS,
+        .eggCycles = 120,
+        .friendship = 0,
+        .growthRate = GROWTH_SLOW,
+        .eggGroups = { EGG_GROUP_UNDISCOVERED, EGG_GROUP_UNDISCOVERED},
+        .abilities = {ABILITY_INSOMNIA, ABILITY_NONE},
+        .safariZoneFleeRate = 0,
+        .bodyColor = BODY_COLOR_PURPLE,
+        .noFlip = FALSE,
+     },
#endif
 };

The . is the structure reference operator in C to refer to the member object of the structure SpeciesInfo.

Notice how I also set the ability to ABILITY_INSOMNIA, so our little monster doesn't even need to sleep anymore. You can find the abilities for example here include/constants/abilities.h and here src/data/text/abilities.h.

You can also incorporate a 3rd ability to your species, which is intended to be a Hidden Ability!

5. Delimit the moveset

Let's begin with the moves that can be learned by leveling up.

Append to src/data/pokemon/level_up_learnsets.h:

static const struct LevelUpMove sEnamorusLevelUpLearnset[] = {
    LEVEL_UP_MOVE( 1, MOVE_TACKLE),
    LEVEL_UP_MOVE( 7, MOVE_BITE),
    LEVEL_UP_MOVE(11, MOVE_TWISTER),
    LEVEL_UP_MOVE(14, MOVE_DRAINING_KISS),
    LEVEL_UP_MOVE(22, MOVE_IRON_DEFENSE),
    LEVEL_UP_MOVE(31, MOVE_EXTRASENSORY),
    LEVEL_UP_MOVE(41, MOVE_CRUNCH),
    LEVEL_UP_MOVE(47, MOVE_MOONBLAST),
    LEVEL_UP_MOVE( 1, MOVE_SPRINGTIDE_STORM),
    LEVEL_UP_END
};

+static const struct LevelUpMove sMewthreeLevelUpLearnset[] = {
+   LEVEL_UP_MOVE( 1, MOVE_CONFUSION),
+   LEVEL_UP_MOVE( 1, MOVE_DISABLE),
+   LEVEL_UP_MOVE(11, MOVE_BARRIER),
+   LEVEL_UP_MOVE(22, MOVE_SWIFT),
+   LEVEL_UP_MOVE(33, MOVE_PSYCH_UP),
+   LEVEL_UP_MOVE(44, MOVE_FUTURE_SIGHT),
+   LEVEL_UP_MOVE(55, MOVE_MIST),
+   LEVEL_UP_MOVE(66, MOVE_PSYCHIC),
+   LEVEL_UP_MOVE(77, MOVE_AMNESIA),
+   LEVEL_UP_MOVE(88, MOVE_RECOVER),
+   LEVEL_UP_MOVE(99, MOVE_SAFEGUARD),
+   LEVEL_UP_END
+};
#endif

Again, we need to register the learnset.

Edit src/data/pokemon/level_up_learnset_pointers.h:

 const struct LevelUpMove *const gLevelUpLearnsets[NUM_SPECIES] =
 {
     [SPECIES_NONE] = sBulbasaurLevelUpLearnset,
     [SPECIES_BULBASAUR] = sBulbasaurLevelUpLearnset,
     ...
     [SPECIES_ENAMORUS] = sEnamorusLevelUpLearnset,
+    [SPECIES_MEWTHREE] = sMewthreeLevelUpLearnset,
 };

Next we need to specify which moves can be taught via TM, HM, or Move Tutor.

Append to src/data/pokemon/teachable_learnsets.h:

static const u16 sEnamorusTeachableLearnset[] = {
    MOVE_UNAVAILABLE,
};

+static const u16 sMewthreeTeachableLearnset[] = {
+   MOVE_FOCUS_PUNCH,
+   MOVE_WATER_PULSE,
+   MOVE_CALM_MIND,
+   MOVE_TOXIC,
+   MOVE_HAIL,
+   MOVE_BULK_UP,
+   MOVE_HIDDEN_POWER,
+   MOVE_SUNNY_DAY,
+   MOVE_TAUNT,
+   MOVE_ICE_BEAM,
+   MOVE_BLIZZARD,
+   MOVE_HYPER_BEAM,
+   MOVE_LIGHT_SCREEN,
+   MOVE_PROTECT,
+   MOVE_RAIN_DANCE,
+   MOVE_SAFEGUARD,
+   MOVE_FRUSTRATION,
+   MOVE_SOLAR_BEAM,
+   MOVE_IRON_TAIL,
+   MOVE_THUNDERBOLT,
+   MOVE_THUNDER,
+   MOVE_EARTHQUAKE,
+   MOVE_RETURN,
+   MOVE_PSYCHIC,
+   MOVE_SHADOW_BALL,
+   MOVE_BRICK_BREAK,
+   MOVE_DOUBLE_TEAM,
+   MOVE_REFLECT,
+   MOVE_SHOCK_WAVE,
+   MOVE_FLAMETHROWER,
+   MOVE_SANDSTORM,
+   MOVE_FIRE_BLAST,
+   MOVE_ROCK_TOMB,
+   MOVE_AERIAL_ACE,
+   MOVE_TORMENT,
+   MOVE_FACADE,
+   MOVE_SECRET_POWER,
+   MOVE_REST,
+   MOVE_SKILL_SWAP,
+   MOVE_SNATCH,
+   MOVE_STRENGTH,
+   MOVE_FLASH,
+   MOVE_ROCK_SMASH,
+   MOVE_MEGA_PUNCH,
+   MOVE_MEGA_KICK,
+   MOVE_BODY_SLAM,
+   MOVE_DOUBLE_EDGE,
+   MOVE_COUNTER,
+   MOVE_SEISMIC_TOSS,
+   MOVE_MIMIC,
+   MOVE_METRONOME,
+   MOVE_DREAM_EATER,
+   MOVE_THUNDER_WAVE,
+   MOVE_SUBSTITUTE,
+   MOVE_DYNAMIC_PUNCH,
+   MOVE_PSYCH_UP,
+   MOVE_SNORE,
+   MOVE_ICY_WIND,
+   MOVE_ENDURE,
+   MOVE_MUD_SLAP,
+   MOVE_ICE_PUNCH,
+   MOVE_SWAGGER,
+   MOVE_SLEEP_TALK,
+   MOVE_SWIFT,
+   MOVE_THUNDER_PUNCH,
+   MOVE_FIRE_PUNCH,
+   MOVE_UNAVAILABLE,
+};
#endif

Once more, we need to register the learnset.

Edit src/data/pokemon/teachable_learnset_pointers.h:

const u16 *const gTeachableLearnsets[NUM_SPECIES] =
 {
     [SPECIES_NONE] = sBulbasaurTeachableLearnset,
     [SPECIES_BULBASAUR] = sBulbasaurTeachableLearnset,
     ...
     [SPECIES_ENAMORUS] = sEnamorusTeachableLearnset,
+    [SPECIES_MEWTHREE] = sMewthreeTeachableLearnset,
 };

If you want to create a Pokémon which can breed, you will need to edit src/data/pokemon/egg_moves.h.

6. Define its cry

First run these command to copy the Mewtwo sound files:

cp -r sound/direct_sound_samples/cries/mewtwo.bin sound/direct_sound_samples/cries/mewthree.bin
cp -r sound/direct_sound_samples/cries/mewtwo.aif sound/direct_sound_samples/cries/mewthree.aif

In sound/direct_sound_data.inc.

    .align 2
Cry_Enamorus::
    .incbin "sound/direct_sound_samples/cries/enamorus.bin"

+   .align 2
+Cry_Mewthree::
+   .incbin "sound/direct_sound_samples/cries/mewthree.bin"

.endif

And linking it in sound/cry_tables.inc. cry_reverse in particular is for reversed cries used by moves such as Growl.

...
    cry Cry_Enamorus
+   cry Cry_Mewthree
.else
    cry_reverse Cry_Overqwil
+   cry_reverse Cry_Mewthree
.else

Mon cries are 10512Hz. Make sure to put the aif file in the directory sound/direct_sound_samples/cries

Higher frequencies may be ruined by compression. To have the cries uncompressed, follow this , then clear out the old sound bins

7. Define the Evolutions

We want Mewthree to evolve from Mewtwo by reaching level 100.

Edit src/data/pokemon/evolution.h:

    [SPECIES_SNEASEL_HISUIAN]       = {{EVO_ITEM_DAY, ITEM_RAZOR_CLAW, SPECIES_SNEASLER},
                                       {EVO_ITEM_HOLD_DAY, ITEM_RAZOR_CLAW, SPECIES_SNEASLER}},
+   [SPECIES_MEWTWO]                = {{EVO_LEVEL, 100, SPECIES_MEWTHREE}},
#endif

8. Easy Chat about your Pokémon

Edit src/data/easy_chat/easy_chat_words_by_letter.h:

 const u16 gEasyChatWordsByLetter_M[] = {
     EC_MOVE2(MACH_PUNCH),
     ...
     EC_POKEMON_NATIONAL(MEW),
+    EC_POKEMON_NATIONAL(MEWTHREE),
     EC_POKEMON_NATIONAL(MEWTWO),
     ...
     EC_WORD_MYSTERY,
 };

9. Make it appear!

Now Mewthree really does slumber in the games code - but we won't know until we make him appear somewhere! The legend tells that Mewthree is hiding somewhere in Petalburg Woods...

Edit src/data/wild_encounters.json:

         {
           "map": "MAP_PETALBURG_WOODS",
           "base_label": "gPetalburgWoods",
           "land_mons": {
             "encounter_rate": 20,
             "mons": [
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_POOCHYENA"
               },
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_WURMPLE"
               },
               {
                 "min_level": 5,
                 "max_level": 5,
                 "species": "SPECIES_SHROOMISH"
               },
               {
-                "min_level": 6,
-                "max_level": 6,
-                "species": "SPECIES_POOCHYENA"
+                "min_level": 5,
+                "max_level": 5,
+                "species": "SPECIES_MEWTHREE"
               },
               ...
        }

Congratulations, you have created your own personal pocket monster! You may call yourself a mad scientist now.

Appendix

Available Front Animations

Only 65 are used in-game, but you can use any animation from this list.

  1. ANIM_V_SQUISH_AND_BOUNCE
  2. ANIM_CIRCULAR_STRETCH_TWICE
  3. ANIM_H_VIBRATE
  4. ANIM_H_SLIDE
  5. ANIM_V_SLIDE
  6. ANIM_BOUNCE_ROTATE_TO_SIDES
  7. ANIM_V_JUMPS_H_JUMPS
  8. ANIM_ROTATE_TO_SIDES
  9. ANIM_ROTATE_TO_SIDES_TWICE
  10. ANIM_GROW_VIBRATE
  11. ANIM_ZIGZAG_FAST
  12. ANIM_SWING_CONCAVE
  13. ANIM_SWING_CONCAVE_FAST
  14. ANIM_SWING_CONVEX
  15. ANIM_SWING_CONVEX_FAST
  16. ANIM_H_SHAKE
  17. ANIM_V_SHAKE
  18. ANIM_CIRCULAR_VIBRATE
  19. ANIM_TWIST
  20. ANIM_SHRINK_GROW
  21. ANIM_CIRCLE_C_CLOCKWISE
  22. ANIM_GLOW_BLACK
  23. ANIM_H_STRETCH
  24. ANIM_V_STRETCH
  25. ANIM_RISING_WOBBLE
  26. ANIM_V_SHAKE_TWICE
  27. ANIM_TIP_MOVE_FORWARD
  28. ANIM_H_PIVOT
  29. ANIM_V_SLIDE_WOBBLE
  30. ANIM_H_SLIDE_WOBBLE
  31. ANIM_V_JUMPS_BIG
  32. ANIM_SPIN_LONG
  33. ANIM_GLOW_ORANGE
  34. ANIM_GLOW_RED
  35. ANIM_GLOW_BLUE
  36. ANIM_GLOW_YELLOW
  37. ANIM_GLOW_PURPLE
  38. ANIM_BACK_AND_LUNGE
  39. ANIM_BACK_FLIP
  40. ANIM_FLICKER
  41. ANIM_BACK_FLIP_BIG
  42. ANIM_FRONT_FLIP
  43. ANIM_TUMBLING_FRONT_FLIP
  44. ANIM_FIGURE_8
  45. ANIM_FLASH_YELLOW
  46. ANIM_SWING_CONCAVE_FAST_SHORT
  47. ANIM_SWING_CONVEX_FAST_SHORT
  48. ANIM_ROTATE_UP_SLAM_DOWN
  49. ANIM_DEEP_V_SQUISH_AND_BOUNCE
  50. ANIM_H_JUMPS
  51. ANIM_H_JUMPS_V_STRETCH
  52. ANIM_ROTATE_TO_SIDES_FAST
  53. ANIM_ROTATE_UP_TO_SIDES
  54. ANIM_FLICKER_INCREASING
  55. ANIM_TIP_HOP_FORWARD
  56. ANIM_PIVOT_SHAKE
  57. ANIM_TIP_AND_SHAKE
  58. ANIM_VIBRATE_TO_CORNERS
  59. ANIM_GROW_IN_STAGES
  60. ANIM_V_SPRING
  61. ANIM_V_REPEATED_SPRING
  62. ANIM_SPRING_RISING
  63. ANIM_H_SPRING
  64. ANIM_H_REPEATED_SPRING_SLOW
  65. ANIM_H_SLIDE_SHRINK
  66. ANIM_LUNGE_GROW
  67. ANIM_CIRCLE_INTO_BG
  68. ANIM_RAPID_H_HOPS
  69. ANIM_FOUR_PETAL
  70. ANIM_V_SQUISH_AND_BOUNCE_SLOW
  71. ANIM_H_SLIDE_SLOW
  72. ANIM_V_SLIDE_SLOW
  73. ANIM_BOUNCE_ROTATE_TO_SIDES_SMALL
  74. ANIM_BOUNCE_ROTATE_TO_SIDES_SLOW
  75. ANIM_BOUNCE_ROTATE_TO_SIDES_SMALL_SLOW
  76. ANIM_ZIGZAG_SLOW
  77. ANIM_H_SHAKE_SLOW
  78. ANIM_V_SHAKE_SLOW
  79. ANIM_TWIST_TWICE
  80. ANIM_CIRCLE_C_CLOCKWISE_SLOW
  81. ANIM_V_SHAKE_TWICE_SLOW
  82. ANIM_V_SLIDE_WOBBLE_SMALL
  83. ANIM_V_JUMPS_SMALL
  84. ANIM_SPIN
  85. ANIM_TUMBLING_FRONT_FLIP_TWICE
  86. ANIM_DEEP_V_SQUISH_AND_BOUNCE_TWICE
  87. ANIM_H_JUMPS_V_STRETCH_TWICE
  88. ANIM_V_SHAKE_BACK
  89. ANIM_V_SHAKE_BACK_SLOW
  90. ANIM_V_SHAKE_H_SLIDE_SLOW
  91. ANIM_V_STRETCH_BOTH_ENDS_SLOW
  92. ANIM_H_STRETCH_FAR_SLOW
  93. ANIM_V_SHAKE_LOW_TWICE
  94. ANIM_H_SHAKE_FAST
  95. ANIM_H_SLIDE_FAST
  96. ANIM_H_VIBRATE_FAST
  97. ANIM_H_VIBRATE_FASTEST
  98. ANIM_V_SHAKE_BACK_FAST
  99. ANIM_V_SHAKE_LOW_TWICE_SLOW
  100. ANIM_V_SHAKE_LOW_TWICE_FAST
  101. ANIM_CIRCLE_C_CLOCKWISE_LONG
  102. ANIM_GROW_STUTTER_SLOW
  103. ANIM_V_SHAKE_H_SLIDE
  104. ANIM_V_SHAKE_H_SLIDE_FAST
  105. ANIM_TRIANGLE_DOWN_SLOW
  106. ANIM_TRIANGLE_DOWN
  107. ANIM_TRIANGLE_DOWN_TWICE
  108. ANIM_GROW
  109. ANIM_GROW_TWICE
  110. ANIM_H_SPRING_FAST
  111. ANIM_H_SPRING_SLOW
  112. ANIM_H_REPEATED_SPRING_FAST
  113. ANIM_H_REPEATED_SPRING
  114. ANIM_SHRINK_GROW_FAST
  115. ANIM_SHRINK_GROW_SLOW
  116. ANIM_V_STRETCH_BOTH_ENDS
  117. ANIM_V_STRETCH_BOTH_ENDS_TWICE
  118. ANIM_H_STRETCH_FAR_TWICE
  119. ANIM_H_STRETCH_FAR
  120. ANIM_GROW_STUTTER_TWICE
  121. ANIM_GROW_STUTTER
  122. ANIM_CONCAVE_ARC_LARGE_SLOW
  123. ANIM_CONCAVE_ARC_LARGE
  124. ANIM_CONCAVE_ARC_LARGE_TWICE
  125. ANIM_CONVEX_DOUBLE_ARC_SLOW
  126. ANIM_CONVEX_DOUBLE_ARC
  127. ANIM_CONVEX_DOUBLE_ARC_TWICE
  128. ANIM_CONCAVE_ARC_SMALL_SLOW
  129. ANIM_CONCAVE_ARC_SMALL
  130. ANIM_CONCAVE_ARC_SMALL_TWICE
  131. ANIM_H_DIP
  132. ANIM_H_DIP_FAST
  133. ANIM_H_DIP_TWICE
  134. ANIM_SHRINK_GROW_VIBRATE_FAST
  135. ANIM_SHRINK_GROW_VIBRATE
  136. ANIM_SHRINK_GROW_VIBRATE_SLOW
  137. ANIM_JOLT_RIGHT_FAST
  138. ANIM_JOLT_RIGHT
  139. ANIM_JOLT_RIGHT_SLOW
  140. ANIM_SHAKE_FLASH_YELLOW_FAST
  141. ANIM_SHAKE_FLASH_YELLOW
  142. ANIM_SHAKE_FLASH_YELLOW_SLOW
  143. ANIM_SHAKE_GLOW_RED_FAST
  144. ANIM_SHAKE_GLOW_RED
  145. ANIM_SHAKE_GLOW_RED_SLOW
  146. ANIM_SHAKE_GLOW_GREEN_FAST
  147. ANIM_SHAKE_GLOW_GREEN
  148. ANIM_SHAKE_GLOW_GREEN_SLOW
  149. ANIM_SHAKE_GLOW_BLUE_FAST
  150. ANIM_SHAKE_GLOW_BLUE
  151. ANIM_SHAKE_GLOW_BLUE_SLOW

Available Back Animations

  1. BACK_ANIM_NONE
  2. BACK_ANIM_H_VIBRATE
  3. BACK_ANIM_H_SLIDE
  4. BACK_ANIM_H_SPRING
  5. BACK_ANIM_H_SPRING_REPEATED
  6. BACK_ANIM_SHRINK_GROW
  7. BACK_ANIM_GROW
  8. BACK_ANIM_CIRCLE_COUNTERCLOCKWISE
  9. BACK_ANIM_H_SHAKE
  10. BACK_ANIM_V_SHAKE
  11. BACK_ANIM_V_SHAKE_H_SLIDE
  12. BACK_ANIM_V_STRETCH
  13. BACK_ANIM_H_STRETCH
  14. BACK_ANIM_GROW_STUTTER
  15. BACK_ANIM_V_SHAKE_LOW
  16. BACK_ANIM_TRIANGLE_DOWN
  17. BACK_ANIM_CONCAVE_ARC_LARGE
  18. BACK_ANIM_CONVEX_DOUBLE_ARC
  19. BACK_ANIM_CONCAVE_ARC_SMALL
  20. BACK_ANIM_DIP_RIGHT_SIDE
  21. BACK_ANIM_SHRINK_GROW_VIBRATE
  22. BACK_ANIM_JOLT_RIGHT
  23. BACK_ANIM_SHAKE_FLASH_YELLOW
  24. BACK_ANIM_SHAKE_GLOW_RED
  25. BACK_ANIM_SHAKE_GLOW_GREEN
  26. BACK_ANIM_SHAKE_GLOW_BLUE

Pokémon ordered by height

Pokemonheight (m)
Diglett0.2
Natu0.2
Azurill0.2
Caterpie0.3
Weedle0.3
Pidgey0.3
Rattata0.3
Spearow0.3
Paras0.3
Magnemite0.3
Shellder0.3
Ditto0.3
Eevee0.3
Pichu0.3
Cleffa0.3
Igglybuff0.3
Togepi0.3
Sunkern0.3
Wurmple0.3
Taillow0.3
Roselia0.3
Castform0.3
Jirachi0.3
Pikachu0.4
Nidoran_f0.4
Meowth0.4
Geodude0.4
Krabby0.4
Exeggcute0.4
Cubone0.4
Horsea0.4
Omanyte0.4
Mew0.4
Bellossom0.4
Marill0.4
Hoppip0.4
Wooper0.4
Swinub0.4
Smoochum0.4
Torchic0.4
Mudkip0.4
Zigzagoon0.4
Ralts0.4
Shroomish0.4
Aron0.4
Plusle0.4
Minun0.4
Gulpin0.4
Cacnea0.4
Swablu0.4
Barboach0.4
Clamperl0.4
Squirtle0.5
Nidoran_m0.5
Jigglypuff0.5
Oddish0.5
Mankey0.5
Voltorb0.5
Kabuto0.5
Cyndaquil0.5
Spinarak0.5
Chinchou0.5
Murkrow0.5
Unown0.5
Qwilfish0.5
Phanpy0.5
Treecko0.5
Poochyena0.5
Linoone0.5
Lotad0.5
Seedot0.5
Surskit0.5
Nincada0.5
Sableye0.5
Torkoal0.5
Baltoy0.5
Charmander0.6
Kakuna0.6
Sandshrew0.6
Clefairy0.6
Vulpix0.6
Poliwag0.6
Koffing0.6
Goldeen0.6
Totodile0.6
Togetic0.6
Mareep0.6
Skiploom0.6
Pineco0.6
Snubbull0.6
Shuckle0.6
Teddiursa0.6
Corsola0.6
Remoraid0.6
Houndour0.6
Porygon20.6
Elekid0.6
Larvitar0.6
Celebi0.6
Silcoon0.6
Wingull0.6
Whismur0.6
Skitty0.6
Mawile0.6
Meditite0.6
Electrike0.6
Illumise0.6
Corphish0.6
Feebas0.6
Shuppet0.6
Chimecho0.6
Wynaut0.6
Luvdisc0.6
Bagon0.6
Beldum0.6
Bulbasaur0.7
Metapod0.7
Raticate0.7
Dugtrio0.7
Growlithe0.7
Bellsprout0.7
Hoothoot0.7
Misdreavus0.7
Slugma0.7
Tyrogue0.7
Magby0.7
Marshtomp0.7
Cascoon0.7
Swellow0.7
Volbeat0.7
Numel0.7
Spoink0.7
Trapinch0.7
Anorith0.7
Snorunt0.7
Raichu0.8
Nidorina0.8
Zubat0.8
Gloom0.8
Psyduck0.8
Machop0.8
Farfetchd0.8
Staryu0.8
Jolteon0.8
Porygon0.8
Sentret0.8
Flaaffy0.8
Azumarill0.8
Jumpluff0.8
Aipom0.8
Sunflora0.8
Magcargo0.8
Kirlia0.8
Masquerain0.8
Slakoth0.8
Ninjask0.8
Shedinja0.8
Carvanha0.8
Duskull0.8
Spheal0.8
Nidorino0.9
Abra0.9
Tentacool0.9
Grimer0.9
Magikarp0.9
Flareon0.9
Chikorita0.9
Quilava0.9
Espeon0.9
Sneasel0.9
Octillery0.9
Delibird0.9
Grovyle0.9
Combusken0.9
Lairon0.9
Grumpig0.9
Whiscash0.9
Ivysaur1.0
Wartortle1.0
Beedrill1.0
Sandslash1.0
Wigglytuff1.0
Parasect1.0
Venonat1.0
Persian1.0
Primeape1.0
Poliwhirl1.0
Weepinbell1.0
Graveler1.0
Ponyta1.0
Magneton1.0
Drowzee1.0
Marowak1.0
Rhyhorn1.0
Tangela1.0
Vaporeon1.0
Omastar1.0
Ledyba1.0
Umbreon1.0
Mightyena1.0
Beautifly1.0
Nuzleaf1.0
Loudred1.0
Makuhita1.0
Nosepass1.0
Lunatone1.0
Lileep1.0
Kecleon1.0
Relicanth1.0
Charmeleon1.1
Butterfree1.1
Pidgeotto1.1
Ninetales1.1
Seel1.1
Chansey1.1
Starmie1.1
Electabuzz1.1
Croconaw1.1
Ariados1.1
Politoed1.1
Gligar1.1
Piloswine1.1
Donphan1.1
Delcatty1.1
Spinda1.1
Vibrava1.1
Altaria1.1
Crawdaunt1.1
Banette1.1
Sealeo1.1
Shelgon1.1
Fearow1.2
Vileplume1.2
Slowpoke1.2
Muk1.2
Electrode1.2
Lickitung1.2
Weezing1.2
Seadra1.2
Bayleef1.2
Lanturn1.2
Sudowoodo1.2
Yanma1.2
Forretress1.2
Smeargle1.2
Miltank1.2
Pupitar1.2
Dustox1.2
Lombre1.2
Pelipper1.2
Breloom1.2
Solrock1.2
Absol1.2
Metang1.2
Nidoqueen1.3
Clefable1.3
Poliwrath1.3
Kadabra1.3
Gastly1.3
Kingler1.3
Seaking1.3
Mr_mime1.3
Magmar1.3
Kabutops1.3
Wobbuffet1.3
Shiftry1.3
Medicham1.3
Cacturne1.3
Zangoose1.3
Nidoking1.4
Golem1.4
Doduo1.4
Hitmonchan1.4
Jynx1.4
Tauros1.4
Ledian1.4
Ampharos1.4
Quagsire1.4
Granbull1.4
Houndoom1.4
Stantler1.4
Hitmontop1.4
Vigoroth1.4
Walrein1.4
Latias1.4
Pidgeot1.5
Venomoth1.5
Alakazam1.5
Machoke1.5
Cloyster1.5
Gengar1.5
Hitmonlee1.5
Scyther1.5
Pinsir1.5
Xatu1.5
Girafarig1.5
Dunsparce1.5
Heracross1.5
Blissey1.5
Swampert1.5
Ludicolo1.5
Exploud1.5
Manectric1.5
Claydol1.5
Cradily1.5
Armaldo1.5
Glalie1.5
Salamence1.5
Blastoise1.6
Golbat1.6
Machamp1.6
Tentacruel1.6
Slowbro1.6
Haunter1.6
Hypno1.6
Zapdos1.6
Noctowl1.6
Gardevoir1.6
Dusclops1.6
Metagross1.6
Charizard1.7
Golduck1.7
Victreebel1.7
Rapidash1.7
Dewgong1.7
Articuno1.7
Typhlosion1.7
Skarmory1.7
Sceptile1.7
Swalot1.7
Huntail1.7
Regirock1.7
Deoxys1.7
Dodrio1.8
Aerodactyl1.8
Dratini1.8
Meganium1.8
Furret1.8
Crobat1.8
Scizor1.8
Ursaring1.8
Kingdra1.8
Sharpedo1.8
Gorebyss1.8
Regice1.8
Arcanine1.9
Rhydon1.9
Raikou1.9
Blaziken1.9
Camerupt1.9
Registeel1.9
Venusaur2.0
Ekans2.0
Exeggutor2.0
Moltres2.0
Mewtwo2.0
Slowking2.0
Suicune2.0
Tyranitar2.0
Slaking2.0
Wailmer2.0
Flygon2.0
Tropius2.0
Latios2.0
Snorlax2.1
Mantine2.1
Entei2.1
Aggron2.1
Kangaskhan2.2
Dragonite2.2
Feraligatr2.3
Hariyama2.3
Lapras2.5
Seviper2.7
Arbok3.5
Groudon3.5
Ho_oh3.8
Dragonair4.0
Kyogre4.5
Lugia5.2
Milotic6.2
Gyarados6.5
Rayquaza7.0
Onix8.8
Steelix9.2
Wailord14.5

Pokémon ordered by weight

Pokemonweight (kg)
Gastly0.1
Haunter0.1
Hoppip0.5
Diglett0.8
Castform0.8
Igglybuff1.0
Koffing1.0
Skiploom1.0
Chimecho1.0
Misdreavus1.0
Jirachi1.1
Swablu1.2
Shedinja1.2
Togepi1.5
Surskit1.7
Pidgey1.8
Sunkern1.8
Barboach1.9
Natu2.0
Azurill2.0
Spearow2.0
Pichu2.0
Roselia2.0
Murkrow2.1
Taillow2.3
Shuppet2.3
Exeggcute2.5
Torchic2.5
Lotad2.6
Caterpie2.9
Cleffa3.0
Jumpluff3.0
Weedle3.2
Togetic3.2
Dratini3.3
Rattata3.5
Wurmple3.6
Masquerain3.6
Qwilfish3.9
Shellder4.0
Ditto4.0
Mew4.0
Seedot4.0
Bellsprout4.0
Meowth4.2
Plusle4.2
Minun4.2
Shroomish4.5
Unown5.0
Treecko5.0
Corsola5.0
Celebi5.0
Spinda5.0
Paras5.4
Oddish5.4
Jigglypuff5.5
Nincada5.5
Bellossom5.8
Magnemite6.0
Pikachu6.0
Smoochum6.0
Sentret6.0
Chikorita6.4
Weepinbell6.4
Eevee6.5
Krabby6.5
Cubone6.5
Swinub6.5
Ralts6.6
Bulbasaur6.9
Ekans6.9
Nidoran_f7.0
Pineco7.2
Feebas7.4
Omanyte7.5
Clefairy7.5
Zubat7.5
Mudkip7.6
Mareep7.8
Snubbull7.8
Cyndaquil7.9
Horsea8.0
Marill8.5
Wooper8.5
Spinarak8.5
Charmander8.5
Sunflora8.5
Gloom8.6
Luvdisc8.7
Teddiursa8.8
Squirtle9.0
Nidoran_m9.0
Totodile9.5
Wingull9.5
Weezing9.5
Vulpix9.9
Metapod9.9
Kakuna10.0
Silcoon10.0
Magikarp10.0
Gulpin10.3
Voltorb10.4
Houndour10.8
Ledyba10.8
Sableye11.0
Skitty11.0
Meditite11.2
Kabuto11.5
Mawile11.5
Corphish11.5
Cascoon11.5
Aipom11.5
Chinchou12.0
Sandshrew12.0
Remoraid12.0
Ninjask12.0
Wigglytuff12.0
Poliwag12.4
Anorith12.5
Banette12.5
Venomoth12.5
Ivysaur13.0
Flaaffy13.3
Poochyena13.6
Wynaut14.0
Dunsparce14.0
Goldeen15.0
Trapinch15.0
Farfetchd15.0
Duskull15.0
Xatu15.0
Electrike15.2
Vibrava15.3
Victreebel15.5
Bayleef15.8
Delibird16.0
Whismur16.3
Dragonair16.5
Snorunt16.8
Zigzagoon17.5
Illumise17.7
Volbeat17.7
Raticate18.5
Vileplume18.6
Growlithe19.0
Quilava19.0
Charmeleon19.0
Machop19.5
Nidorino19.5
Abra19.5
Combusken19.5
Psyduck19.6
Swellow19.8
Ninetales19.9
Geodude20.0
Nidorina20.0
Poliwhirl20.0
Kirlia20.2
Shuckle20.5
Altaria20.6
Carvanha20.8
Tyrogue21.0
Hoothoot21.2
Magby21.4
Baltoy21.5
Grovyle21.6
Kecleon22.0
Wartortle22.5
Lanturn22.5
Gorebyss22.6
Relicanth23.4
Elekid23.5
Whiscash23.6
Lileep23.8
Numel24.0
Slakoth24.0
Jolteon24.5
Flareon25.0
Croconaw25.0
Seadra25.0
Espeon26.5
Umbreon27.0
Huntail27.0
Mankey28.0
Marshtomp28.0
Sneasel28.0
Nuzleaf28.0
Pelipper28.0
Beautifly28.4
Azumarill28.5
Octillery28.5
Wobbuffet28.5
Vaporeon29.0
Beedrill29.5
Sandslash29.5
Parasect29.5
Raichu30.0
Grimer30.0
Venonat30.0
Ponyta30.0
Pidgeotto30.0
Electabuzz30.0
Muk30.0
Spoink30.6
Dusclops30.6
Medicham31.5
Dustox31.6
Persian32.0
Primeape32.0
Butterfree32.0
Drowzee32.4
Linoone32.5
Porygon232.5
Lombre32.5
Furret32.5
Delcatty32.6
Crawdaunt32.8
Dugtrio33.3
Phanpy33.5
Ariados33.5
Politoed33.9
Staryu34.5
Chansey34.6
Slugma35.0
Tangela35.0
Omastar35.0
Houndoom35.0
Ledian35.6
Slowpoke36.0
Porygon36.5
Mightyena37.0
Fearow38.0
Sudowoodo38.0
Yanma38.0
Seaking39.0
Breloom39.2
Doduo39.2
Spheal39.5
Pidgeot39.5
Clefable40.0
Latias40.0
Manectric40.2
Zangoose40.3
Loudred40.5
Kabutops40.5
Gengar40.5
Jynx40.6
Noctowl40.8
Girafarig41.5
Bagon42.1
Magmar44.5
Marowak45.0
Tentacool45.5
Vigoroth46.5
Blissey46.8
Absol47.0
Hitmontop48.0
Alakazam48.0
Gardevoir48.4
Granbull48.7
Hitmonlee49.8
Hitmonchan50.2
Skarmory50.5
Cacnea51.3
Blaziken52.0
Sceptile52.2
Clamperl52.5
Seviper52.5
Zapdos52.6
Poliwrath54.0
Heracross54.0
Mr_mime54.5
Magcargo55.0
Pinsir55.0
Ludicolo55.0
Golbat55.0
Tentacruel55.0
Articuno55.4
Piloswine55.8
Scyther56.0
Kadabra56.5
Smeargle58.0
Aerodactyl59.0
Shiftry59.6
Aron60.0
Magneton60.0
Nidoqueen60.0
Kingler60.0
Moltres60.0
Latios60.0
Cradily60.4
Deoxys60.8
Ampharos61.5
Nidoking62.0
Gligar64.8
Arbok65.0
Lickitung65.5
Electrode66.6
Armaldo68.2
Machoke70.5
Stantler71.2
Grumpig71.5
Larvitar72.0
Quagsire75.0
Crobat75.0
Miltank75.5
Hypno75.6
Golduck76.6
Cacturne77.4
Slowbro78.5
Typhlosion79.5
Slowking79.5
Starmie80.0
Swalot80.0
Kangaskhan80.0
Torkoal80.4
Swampert81.9
Flygon82.0
Exploud84.0
Dodrio85.2
Blastoise85.5
Makuhita86.4
Sealeo87.6
Tauros88.4
Sharpedo88.8
Feraligatr88.8
Seel90.0
Charizard90.5
Rapidash95.0
Beldum95.2
Nosepass97.0
Venusaur100.0
Tropius100.0
Meganium100.5
Salamence102.6
Graveler105.0
Claydol108.0
Shelgon110.5
Rhyhorn115.0
Scizor118.0
Lairon120.0
Donphan120.0
Dewgong120.0
Rhydon120.0
Exeggutor120.0
Mewtwo122.0
Forretress125.8
Ursaring125.8
Machamp130.0
Wailmer130.0
Slaking130.5
Cloyster132.5
Walrein150.6
Pupitar152.0
Kingdra152.0
Solrock154.0
Arcanine155.0
Milotic162.0
Lunatone168.0
Regice175.0
Raikou178.0
Suicune187.0
Entei198.0
Ho_oh199.0
Tyranitar202.0
Metang202.5
Registeel205.0
Rayquaza206.5
Dragonite210.0
Onix210.0
Lugia216.0
Camerupt220.0
Mantine220.0
Lapras220.0
Regirock230.0
Gyarados235.0
Hariyama253.8
Glalie256.5
Golem300.0
Kyogre352.0
Aggron360.0
Wailord398.0
Steelix400.0
Snorlax460.0
Metagross550.0
Groudon950.0

Making this easier

If you have multiple species that you want to add to pokeemerald but don't want to copy and paste or type everything out multiple times, just use this handy program to generate text with the species name in there! https://github.com/smithk200/making-a-new-pokemon-species-in-pokeemerald

How to use the Testing System

Running Tests

To run all the tests use: make check -j To run specific tests, e.g. Spikes ones, use: make check TESTS="Spikes" To build a ROM (pokemerald-test.elf) that can be opened in mgba to view specific tests, e.g. Spikes ones, use: make pokeemerald-test.elf TESTS="Spikes"

How to Write Tests

Manually testing a battle mechanic often follows this pattern:

  1. Create a party which can activate the mechanic.
  2. Start a battle and play a few turns which activate the mechanic.
  3. Look at the UI outputs to decide if the mechanic works.

Automated testing follows the same pattern:

  1. Initialize the party in GIVEN.
  2. Play the turns in WHEN.
  3. Check the UI outputs in SCENE.

Example 1

As a concrete example, to manually test EFFECT_PARALYZE, e.g. the effect of Stun Spore you might:

  1. Put a Wobbuffet that knows Stun Spore in your party.
  2. Battle a wild Wobbuffet.
  3. Use Stun Spore.
  4. Check that the Wobbuffet is paralyzed.

This can be translated to an automated test as follows:

ASSUMPTIONS
{
    ASSUME(gMovesInfo[MOVE_STUN_SPORE].effect == EFFECT_PARALYZE);
}

SINGLE_BATTLE_TEST("Stun Spore inflicts paralysis")
{
    GIVEN {
        PLAYER(SPECIES_WOBBUFFET); // 1.
        OPPONENT(SPECIES_WOBBUFFET); // 2.
    } WHEN {
        TURN { MOVE(player, MOVE_STUN_SPORE); } // 3.
    } SCENE {
        ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
        MESSAGE("The opposing Wobbuffet is paralyzed, so it may be unable to move!"); // 4
        STATUS_ICON(opponent, paralysis: TRUE); // 4.
    }
}

The ASSUMPTIONS block documents that Stun Spore has EFFECT_PARALYZE. If Stun Spore did not have that effect it would cause the tests in the file to be skipped. We write our tests like this so that hackers can change the effects of moves without causing tests to fail.

SINGLE_BATTLE_TEST defines the name of the test. Related tests should start with the same prefix, e.g. Stun Spore tests should start with "Stun Spore", this allows just the Stun Spore-related tests to be run with: make check TESTS="Stun Spore"

GIVEN initializes the parties, PLAYER and OPPONENT add a Pokémon to their respective parties. They can both accept a block which further customizes the Pokémon's stats, moves, item, ability, etc.

WHEN describes the turns, and TURN describes the choices made in a single turn. MOVE causes the player to use Stun Spore and adds the move to the Pokémon's moveset if an explicit Moves was not specified. Pokémon that are not mentioned in a TURN use Celebrate. The test runner rigs the RNG so that unless otherwise specified, moves always hit, never critical hit, always activate their secondary effects, and always roll the same damage modifier.

SCENE describes the player-visible output of the battle. In this case ANIMATION checks that the Stun Spore animation played, MESSAGE checks the paralysis message was shown, and STATUS_ICON checks that the opponent's HP bar shows a PRZ icon.

Example 2

As a second example, to manually test that Stun Spore does not effect Grass-types you might:

  1. Put a Wobbuffet that knows Stun Spore in your party.
  2. Battle a wild Oddish.
  3. Use Stun Spore.
  4. Check that the move animation does not play.
  5. Check that a "It doesn't affect Foe Oddish…" message is shown.

This can again be translated as follows:

SINGLE_BATTLE_TEST("Stun Spore does not affect Grass-types")
{
    GIVEN {
        ASSUME(gMovesInfo[MOVE_STUN_SPORE].powderMove);
        ASSUME(gSpeciesInfo[SPECIES_ODDISH].types[0] == TYPE_GRASS);
        PLAYER(SPECIES_ODDISH); // 1.
        OPPONENT(SPECIES_ODDISH); // 2.
    } WHEN {
        TURN { MOVE(player, MOVE_STUN_SPORE); } // 3.
    } SCENE {
        NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player); // 4.
        MESSAGE("It doesn't affect the opposing Oddish…"); // 5.
    }
}

The ASSUME commands are documenting the reasons why Stun Spore does not affect Oddish, namely that Stun Spore is a powder move, and Oddish is a Grass-type. These ASSUME statements function similarly to the ones in ASSUMPTIONS but apply only to the one test. NOT inverts the meaning of a SCENE check, so applying it to ANIMATION requires that the Stun Spore animation does not play. MESSAGE checks that the message was shown. The checks in SCENE are ordered, so together this says "The doesn't affect message is shown, and the Stun Spore animation does not play at any time before that". Normally you would only test one or the other, or even better, just NOT STATUS_ICON(opponent, paralysis: TRUE); to say that Oddish was not paralyzed without specifying the exact outputs which led to that.

Example 3

As a final example, to test that Meditate works you might:

  1. Put a Wobbuffet that knows Meditate and Tackle in your party.
  2. Battle a wild Wobbuffet.
  3. Use Tackle and note the amount the HP bar reduced.
  4. Battle a wild Wobbuffet.
  5. Use Meditate and that the stat change animation and message play.
  6. Use Tackle and check that the HP bar reduced by more than in 3.

This can be translated to an automated test as follows:

SINGLE_BATTLE_TEST("Meditate raises Attack", s16 damage)
{
    bool32 raiseAttack;
    PARAMETRIZE { raiseAttack = FALSE; }
    PARAMETRIZE { raiseAttack = TRUE; }
    GIVEN {
        ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
        PLAYER(SPECIES_WOBBUFFET);
        OPPONENT(SPECIES_WOBBUFFET);
    } WHEN {
        if (raiseAttack) TURN { MOVE(player, MOVE_MEDITATE); } // 5.
        TURN { MOVE(player, MOVE_TACKLE); } // 3 & 6.
    } SCENE {
        if (raiseAttack) {
            ANIMATION(ANIM_TYPE_MOVE, MOVE_MEDITATE, player);
            ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); // 5.
            MESSAGE("Wobbuffet's attack rose!"); // 5.
        }
        ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
        HP_BAR(opponent, captureDamage: &results[i].damage); // 3 & 6.
    } FINALLY {
        EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); // 6.
    }
}

PARAMETRIZE causes a test to run multiple times, once per PARAMETRIZE block (e.g. once with raiseAttack = FALSE and once with raiseAttack = TRUE). The HP_BAR command's captureDamage causes the change in HP to be stored in a variable, and the variable chosen is results[i].damage. results[i] contains all the variables defined at the end of SINGLE_BATTLE_TEST, i is the current PARAMETRIZE index. FINALLY runs after the last parameter has finished, and uses EXPECT_MUL_EQ to check that the second battle deals 1.5× the damage of the first battle (with a small tolerance to account for rounding).

You might notice that all the tests check the outputs the player could see rather than the internal battle state. e.g. the Meditate test could have used gBattleMons[B_POSITION_OPPONENT_LEFT].hp instead of using HP_BAR to capture the damage. This is a deliberate choice, by checking what the player can observe the tests are more robust to refactoring, e.g. if gBattleMons got moved into gBattleStruct then any test that used it would need to be updated.

Note on Overworld Tests

The overworld is not available, so it is only possible to test commands which don't affect the overworld itself, e.g. givemon can be tested because it only alters gPlayerParty, but addobject cannot because it affects object events (which aren't loaded).

REFERENCE

ASSUME

ASSUME(cond) Causes the test to be skipped if cond is false. Used to document any prerequisites of the test, e.g. to test Burn reducing the Attack of a Pokémon we can observe the damage of a physical attack with and without the burn. To document that this test assumes the attack is physical we can use: ASSUME(gMovesInfo[MOVE_WHATEVER].category == DAMAGE_CATEGORY_PHYSICAL);

ASSUMPTIONS

ASSUMPTIONS
{
    ...
}

Should be placed immediately after any #includes and contain any ASSUME statements which should apply to the whole file, e.g. to test EFFECT_POISON_HIT we need to choose a move with that effect, if we chose to use Poison Sting in every test then the top of move_effect_poison_hit.c should be:

ASSUMPTIONS
{
    ASSUME(gMovesInfo[MOVE_POISON_STING].effect == EFFECT_POISON_HIT);
}

SINGLE_BATTLE_TEST

SINGLE_BATTLE_TEST(name, results...) and DOUBLE_BATTLE_TEST(name, results...) Define single- and double- battles. The names should start with the name of the mechanic being tested so that it is easier to run all the related tests. results contains variable declarations to be placed into the results array which is available in tests using PARAMETRIZE commands. The main differences for doubles are:

  • Move targets sometimes need to be explicit.
  • Instead of player and opponent there is playerLeft, playerRight, opponentLeft, and opponentRight.

AI_SINGLE_BATTLE_TEST

AI_SINGLE_BATTLE_TEST(name, results...) and AI_DOUBLE_BATTLE_TEST(name, results...) Define battles where opponent mons are controlled by AI, the same that runs when battling regular Trainers. The flags for AI should be specified by the AI_FLAGS command. The rules remain the same as with the SINGLE and DOUBLE battle tests with some differences:

  • opponent's action is specified by the EXPECT_MOVE / EXPECT_SEND_OUT / EXPECT_SWITCH commands
  • we don't control what opponent actually does, instead we make sure the opponent does what we expect it to do
  • we still control the player's action the same way
  • apart from the EXPECTED commands, there's also a new SCORE_ and SCORE__VAL commands

KNOWN_FAILING

KNOWN_FAILING; Marks a test as not passing due to a bug. If there is an issue number associated with the bug it should be included in a comment. If the test passes the developer will be notified to remove KNOWN_FAILING. For example:

SINGLE_BATTLE_TEST("Jump Kick has no recoil if no target")
{
    KNOWN_FAILING; // #2596.
    ...
}

PARAMETRIZE

PARAMETERIZE { parameter; } Runs a test multiple times. i will be set to which parameter is running, and results will contain an entry for each parameter, e.g.:

SINGLE_BATTLE_TEST("Blaze boosts Fire-type moves in a pinch", s16 damage)
{
    u16 hp;
    PARAMETRIZE { hp = 99; }
    PARAMETRIZE { hp = 33; }
    GIVEN {
        ASSUME(gMovesInfo[MOVE_EMBER].type == TYPE_FIRE);
        PLAYER(SPECIES_CHARMANDER) { Ability(ABILITY_BLAZE); MaxHP(99); HP(hp); }
        OPPONENT(SPECIES_WOBBUFFET);
    } WHEN {
        TURN { MOVE(player, MOVE_EMBER); }
    } SCENE {
        HP_BAR(opponent, captureDamage: &results[i].damage);
    } FINALLY {
        EXPECT(results[1].damage > results[0].damage);
    }
}

PASSES_RANDOMLY

PASSES_RANDOMLY(successes, trials, [tag]) Checks that the test passes successes/trials. If tag is provided, the test is run for each value that the tag can produce. For example, to check that Paralysis causes the turn to be skipped 25/100 times, we can write the following test that passes only if the Pokémon is fully paralyzed and specify that we expect it to pass 25/100 times when RNG_PARALYSIS varies:

SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn")
{
    PASSES_RANDOMLY(25, 100, RNG_PARALYSIS);
    GIVEN {
        PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); }
        OPPONENT(SPECIES_WOBBUFFET);
    } WHEN {
        TURN { MOVE(player, MOVE_CELEBRATE); }
    } SCENE {
        MESSAGE("Wobbuffet couldn't move because it's paralyzed!");
    }
}

All BattleRandom calls involving tag will return the same number, so this cannot be used to have two moves independently hit or miss, for example.

If the tag is not provided, runs the test 50 times and computes an approximate pass ratio. PASSES_RANDOMLY(gMovesInfo[move].accuracy, 100); Note that this mode of PASSES_RANDOMLY makes the tests run very slowly and should be avoided where possible. If the mechanic you are testing is missing its tag, you should add it.

GIVEN

Given {
    ...
}

Contains the initial state of the parties before the battle.

RNGSeed

RNGSeed(seed) Explicitly sets the RNG seed. Try to avoid using this because it is a very fragile tool. Example:

GIVEN {
    RNGSeed(0xC0DEIDEA);
    ...
}

FLAG_SET

FLAG_SET(flagId) Sets the specified flag. Can currently only set one flag at a time. Cleared between parameters and at the end of the test. Example:

GIVEN {
    FLAG_SET(FLAG_SYS_EXAMPLE_FLAG);
    ...
}

PLAYER and OPPONENT

PLAYER(species) and OPPONENT(species Adds the species to the player's or opponent's party respectively. The Pokémon can be further customized with the following functions:

  • Gender(MON_MALE | MON_FEMALE)
  • Nature(nature)
  • Ability(ability)
  • Level(level)
  • MaxHP(n), HP(n), Attack(n), Defense(n), SpAttack(n), SpDefense(n), Speed(n)
  • Item(item)
  • Moves(moves...)
  • Friendship(friendship)
  • Status1(status1) For example to create a level 42 Wobbuffet that is poisoned: PLAYER(SPECIES_WOBBUFFET) { Level(42); Status1(STATUS1_POISON); } Note if Speed is specified for any Pokémon then it must be specified for all Pokémon. Note if Moves is specified then MOVE will not automatically add moves to the moveset.

AI_FLAGS

AI_FLAGS(flags) Specifies which AI flags are run during the test. Has use only for AI tests. The most common combination is AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT) which is the general 'smart' AI.

WHEN

    ...
} WHEN {
    ...
}

Contains the choices that battlers make during the battle.

TURN

TURN { ... } Groups the choices made by the battlers on a single turn. If Speeds have not been explicitly specified then the order of the MOVE commands in the TURN will be used to infer the Speeds of the Pokémon, e.g.:

     // player's speed will be greater than opponent's speed.
     TURN { MOVE(player, MOVE_SPLASH); MOVE(opponent, MOVE_SPLASH); }
     // opponent's speed will be greater than player's speed.
     TURN { MOVE(opponent, MOVE_SPLASH); MOVE(player, MOVE_SPLASH); }

The inference process is naive, if your test contains anything that modifies the speed of a battler you should specify them explicitly.

MOVE

MOVE(battler, move | moveSlot:, [megaEvolve:], [hit:], [criticalHit:], [target:], [allowed:], [WITH_RNG(tag, value]) Used when the battler chooses Fight. Either the move ID (e.g. MOVE_TACKLE or move slot must be specified.

  • megaEvolve: TRUE causes the battler to Mega Evolve if able
  • hit: FALSE causes the move to miss
  • criticalHit: TRUE causes the move to land a critical hit
  • target: is used in double battles to choose the target (when necessary)
  • allowed: FALSE is used to reject an illegal move e.g. a Disabled one
  • WITH_RNG allows the move to specify an explicit outcome for an RNG tag
     MOVE(playerLeft, MOVE_TACKLE, target: opponentRight);

If the battler does not have an explicit Moves specified the moveset will be populated based on the MOVEs it uses.

FORCED_MOVE

FORCED_MOVE(battler) Used when the battler chooses Fight and then their move is chosen for them, e.g. when affected by Encore.

     FORCED_MOVE(player);

SWITCH

SWITCH(battler, partyIndex) Used when the battler chooses Switch.

     SWITCH(player, 1);

SKIP_TURN

SKIP_TURN(battler) Used when the battler cannot choose an action, e.g. when locked into Thrash.

     SKIP_TURN(player);

SEND_OUT

SEND_OUT(battler, partyIndex) Used when the battler chooses to switch to another Pokémon but not via Switch, e.g. after fainting or due to a U-turn.

     SEND_OUT(player, 1);

USE_ITEM

USE_ITEM(battler, itemId, [partyIndex:], [move:]) Used when the battler chooses to use an item from the Bag. The item ID (e.g. ITEM_POTION) must be specified, and party index and move slot if applicable, e.g:

      USE_ITEM(player, ITEM_X_ATTACK);
      USE_ITEM(player, ITEM_POTION, partyIndex: 0);
      USE_ITEM(player, ITEM_LEPPA_BERRY, partyIndex: 0, move: MOVE_TACKLE);

SCENE

    ...
} SCENE {
    ...
}

Contains an abridged description of the UI during the THEN. The order of the description must match too, e.g.

} SCENE {
     // ABILITY_POPUP followed by a MESSAGE
     ABILITY_POPUP(player, ABILITY_STURDY);
     MESSAGE("Geodude was protected by Sturdy!");
}

ABILITY_POPUP

ABILITY_POPUP(battler, [ability]) Causes the test to fail if the battler's ability pop-up is not shown. If specified, ability is the ability shown in the pop-up.

     ABILITY_POPUP(opponent, ABILITY_MOLD_BREAKER);

ANIMATION

ANIMATION(type, animId, [battler], [target:]) Causes the test to fail if the animation does not play. A common use of this command is to check if a move was successful, e.g.:

     ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);

target can only be specified for ANIM_TYPE_MOVE.

EXPERIENCE_BAR

EXPERIENCE_BAR(battler, [exp: | captureGainedExp:]) If exp: is used, causes the test to fail if that amount of experience is not gained, e.g.:

     EXPERIENCE_BAR(player, exp: 0);

If captureGainedExp: is used, causes the test to fail if the Experience bar does not change, and then writes that change to the pointer, e.g.:

     u32 exp;
     EXPERIENCE_BAR(player, captureGainedExp: &exp);

If none of the above are used, causes the test to fail if the Exp does not change at all. Please note that due to nature of tests, this command is only usable in WILD_BATTLE_TEST and will fail elsewhere.

HP_BAR

HP_BAR(battler, [damage: | hp: | captureDamage: | captureHP:]) If hp: or damage: are used, causes the test to fail if that amount of damage is not dealt, e.g.:

     HP_BAR(player, hp: 0);

If captureDamage: or captureHP: are used, causes the test to fail if the HP bar does not change, and then writes that change to the pointer, e.g.:

     s16 damage;
     HP_BAR(player, captureDamage: &damage);

If none of the above are used, causes the test to fail if the HP does not change at all.

MESSAGE

MESSAGE(pattern) Causes the test to fail if the message in pattern is not displayed. Spaces in pattern match newlines (\n, \l, and \p) in the message. Often used to check that a battler took its turn but it failed, e.g.:

     MESSAGE("Wobbuffet used Dream Eater!");
     MESSAGE("The opposing Wobbuffet wasn't affected!");

STATUS_ICON

STATUS_ICON(battler, status1 | none: | sleep: | poison: | burn: | freeze: | paralysis:, badPoison:) Causes the test to fail if the battler's status is not changed to the specified status.

     STATUS_ICON(player, badPoison: TRUE);

If the expected status icon is parametrized the corresponding STATUS1 constant can be provided, e.g.:

     u32 status1;
     PARAMETRIZE { status1 = STATUS1_NONE; }
     PARAMETRIZE { status1 = STATUS1_BURN; }
     ...
     STATUS_ICON(player, status1);

NOT

NOT sceneCommand Causes the test to fail if the SCENE command succeeds before the following command succeeds.

     // Our Wobbuffet does not Celebrate before the foe's.
     NOT MESSAGE("Wobbuffet used Celebrate!");
     MESSAGE("The opposing Wobbuffet used Celebrate!");

NOTE: If this condition fails, the viewable ROM freezes at the NOT command. WARNING: NOT is an alias of NONE_OF, so it behaves surprisingly when applied to multiple commands wrapped in braces.

ONE_OF

    ONE_OF {
        ...
    }

Causes the test to fail unless one of the SCENE commands succeeds.

     ONE_OF {
         MESSAGE("Wobbuffet used Celebrate!");
         MESSAGE("Wobbuffet couldn't move because it's paralyzed!");
     }

NONE_OF

    NONE_OF {
        ...
    }

Causes the test to fail if one of the SCENE commands succeeds before the command after the NONE_OF succeeds.

     // Our Wobbuffet does not move before the foe's.
     NONE_OF {
         MESSAGE("Wobbuffet used Celebrate!");
         MESSAGE("Wobbuffet couldn't move because it's paralyzed!");
     }
     MESSAGE("The opposing Wobbuffet used Celebrate!");

PLAYER_PARTY

Refer to the party members defined in GIVEN, e.g.:

     s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
     HP_BAR(player, damage: maxHP / 2);

OPPONENT_PARTY

Refer to the party members defined in GIVEN, e.g.:

     s32 maxHP = GetMonData(&OPPONENT_PARTY[0], MON_DATA_MAX_HP);
     HP_BAR(opponent, damage: maxHP / 2);

THEN

    ...
} THEN {
    ...
}

Contains code to run after the battle has finished. If the test is using PARAMETRIZE commands then EXPECT commands between the results should go here. Is also occasionally used to check the internal battle state when checking the behavior via a SCENE is too difficult, verbose, or error-prone.

FINALLY

    ...
} FINALLY {
    ...
}

Contains checks to run after all PARAMETERIZE commands have run. Prefer to write your checks in THEN where possible, because a failure in THEN will be tagged with which parameter it corresponds to.

EXPECT

EXPECT(cond) Causes the test to fail if cond is false.

EXPECT_XX

EXPECT_EQ(a, b) a == b

EXPECT_NE(a, b) a != b

EXPECT_LT(a, b) a < b

EXPECT_LE(a, b) a <= b

EXPECT_GT(a, b) a > b

EXPECT_GE(a, b) a >= b

Causes the test to fail if a and b compare incorrectly, e.g.

     EXPECT_EQ(results[0].damage, results[1].damage);

EXPECT_MUL_EQ

EXPECT_MUL_EQ(a, m, b) Causes the test to fail if a*m != b (within a threshold), e.g.

     // Expect results[0].damage * 1.5 == results[1].damage.
     EXPECT_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);

Overworld Command Reference

OVERWORLD_SCRIPT

OVERWORLD_SCRIPT(instructions...) Returns a pointer to a compiled overworld script. Cannot be used to initialize global const data, although the pointer IS to const data. Note that each script command must be followed by a ;, e.g.:

const u8 *myScript = OVERWORLD_SCRIPT(
    random 2;
    addvar VAR_RESULT, 1;
);

RUN_OVERWORLD_SCRIPT

RUN_OVERWORLD_SCRIPT(instructions...) Runs an overworld script in the immediate script context, which means that commands like waitstate are not supported.

     RUN_OVERWORLD_SCRIPT(
         setvar VAR_RESULT, 3;
     );
     EXPECT_EQ(GetVar(VAR_RESULT), 3);

Pokeemerald-Expansion Changelogs

1.10.x

1.9.x

1.8.x

1.7.x

1.6.x

1.5.x

1.4.x

1.3.x

1.2.x

1.1.x

1.0.x

Pre-1.0.x:

Version 1.10.1

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.10.1`.

🧬 General 🧬

Added

  • Added FONT_SHORT_NARROWER by @AsparagusEduardo (commit originally by @agsmgmaster64) in #5101
    • Narrower font tweaks and font fitting fixes by @kittenchilly in #5782

Changed

  • Adds Thief/Covet config to send stolen item to bag and Pickup config to pickup user's item in wild battles by @PhallenTree in #5829

Fixed

  • trainerproc: Fix showing incorrect error context by @mrgriffin in #5769
  • Fixes UB in caps.c by @AlexOn1ine in #5878

🗺️ Overworld 🗺️

Fixed

  • Fix Off-by-One Error in Move Relearner by @iriv24 and @luckytyphlosion in #5778
  • Fix HGSS dex sort orders working incorrectly by @ravepossum in #5790
  • Egg cycle length fix by @hedara90 and @/BolaDeQueijo on discord discovered the issue. in #5828
  • Fixed givemon not respecting perfect IVs for species by @AsparagusEduardo in #5873
    • Also removed redundant RemoveIVIndexFromList function in src/daycare.c, so it uses src/pokemon.c's instead
  • Fix Script Scrollable Multichoice Arrow Positions by @ghoulslash in #5884

🐉 Pokémon 🐉

Changed

  • Updated Ogerpon, Enamorus and Sinistcha sprites by @kittenchilly in #5793
  • New Enamorus-Incarnate sprite by @kittenchilly in #5797

Fixed

  • Fixes Wormadam define for teachable learnset script by @AlexOn1ine in #5783
  • Fix "PlantCloak" references by @AsparagusEduardo in #5821
  • Misc pokemon sprite fixes by @Cafeei in #5846

⚔️ Battle General ⚔️

Changed

  • Adds Thief/Covet config to send stolen item to bag and Pickup config to pickup user's item in wild battles by @PhallenTree in #5829

Fixed

  • Fixes items preventing other switch in effects by @AlexOn1ine in #5732
  • Fix Pokemon with No Guard failing OHKO Moves into Semi-Invulnerable Pokemon by @iriv24 and @Cafeei in #5779
  • Fix move category and category icon when PSS is off by @ravepossum in #5786
  • Added the missing config to use new terrains by @hedara90 in #5792
  • Fixes Shed Tail substitute health by @AlexOn1ine in #5826
  • B_LAST_USED_BALL and .importance by @AERDU in #5834
    • prevents B_LAST_USED_BALL from removing balls with .importance = 1
  • Fixes Quash-affected battlers having the wrong order for End Turn effects by @PhallenTree in #5838
  • Fixes Cotton Down and Gulp Missile not interacting correctly with stat reduction prevention effects by @PhallenTree in #5841
  • Fix Hit Escape moves giving Exp to the mon that switches in by @kittenchilly in #5844
  • Fixed Wish triggering Disguise by @AsparagusEduardo in #5860
  • Fixed MOVE_EFFECT_FREEZE_OR_FROSTBITE not being usable in battle scripts by @AsparagusEduardo in #5859
  • Fixed Ally Switch breaking Illusion by @AsparagusEduardo in #5879
  • Fixes gen3 Style Shadows out of place by @AlexOn1ine in #5880
  • Fix Salt Cure script by @ghoulslash in #5895
  • Fixes Eject Pack / Intimidate issue by @AlexOn1ine in #5902
  • Adds Generational config for Magic Guard (Fix for Gen4+) by @AlexOn1ine in #5893
  • Fixes Stance Change, Sleep Talk interaction by @AlexOn1ine in #5909
  • Fixes Round doubling it's BP if previous Round failed by @AlexOn1ine in #5907

🤹 Moves 🤹

Fixed

  • Fixes absorb still draining HP when flinched by @AlexOn1ine in #5814
  • Fixes Tidy Up by @AlexOn1ine in #5819
  • Ally Switch extra battlerId tracking by @ghoulslash in #5823
  • Sheer Force fix and move effect cleanup by @AlexOn1ine in #5812
  • New U-turn animation to fix visibility by @AlexOn1ine in #5910

🧶 Items 🧶

Fixed

  • Prevent Key Items that open other menus from causing a crash if registered and used from the field by @iriv24 in #5810
  • Fixes Clear Amulet displaying the wrong battler and Starting Status displaying the wrong message by @PhallenTree in #5831
  • Fixes Room Service lowering the opposite mon in specific scenario by @AlexOn1ine in #5827

🤖 Battle AI 🤖

Fixed

  • Fixed ace switching bugs by @Pawkkie and @iriv24 for their diligent testing and debugging support in #5922

🧹 Other Cleanup 🧹

  • Converted Stance Change to proper Form Change + Tests by @AsparagusEduardo in #5749
  • Removed testing strings for automatic line breaks by @hedara90 in #5757
  • Added NBSP and up+down arrows to all fonts by @hedara90 in #5767
    • Use ~ or {NBSP} to insert a non-breaking space into a string.
  • Palette cleanup by @hedara90 in #5661
    • Resized some move anim palettes from 256 to 16
  • Replace power checks with IS_MOVE_STATUS by @Bassoonian and @AsparagusEduardo in #5820
  • Changes Various defines to an Enum by @AsparagusEduardo in #5840
  • Fix IS_MOVE_STATUS regression by @Bassoonian in #5848
  • Remove unused various by @Bassoonian in #5851
  • Removed redundant call to FillPalBufferBlack in FRLG whiteout sequence by @AsparagusEduardo in #5854
  • Improve README.md by @AsparagusEduardo in #5640
  • Fix wrong value for NUM_MOVE_EFFECTS by @Bassoonian in #5913
  • Renamed OW type effectiveness function for clarity by @AsparagusEduardo in #5917
    • Renamed GetTypeEffectiveness to GetOverworldTypeEffectiveness.

🧪 Test Runner 🧪

Changed

  • Gravity fix + Sky Drop Test by @ghoulslash in #5780
  • Added missing Belch tests by @AsparagusEduardo in #5881
  • Added missing Move Effect TODO tests - Volume D by @AsparagusEduardo in #5887
  • Comment out Ally Switch Illusion test by @AsparagusEduardo in #5901
  • Fixed leaking tasks not showing up in summary by @AsparagusEduardo in #5890
  • Setting Battle configs during tests by @AsparagusEduardo and @SBird1337, @mrgriffin in #5803
  • Speed up tests in headless mode by @AsparagusEduardo and @SBird1337 for the original fast intro code. in #5889
    • This introduced the config option B_FAST_INTRO_NO_SLIDE which removes the sliding into for battles.
  • Added missing Move Effect TODO tests - Volume E by @AsparagusEduardo in #5915

Fixed

  • Fix test TIMEOUT messaging in summary by @AsparagusEduardo in #5772
  • Fix octolock + defiant by @ghoulslash in #5781
  • Added missing tests + Fix Coaching/Crafty Shield interaction by @AsparagusEduardo in #5796
  • Fixed TODO tests not showing up when filtering by name by @AsparagusEduardo in #5894

📚 Documentation 📚

  • Fixed changelog links to changelog 1.10 by @AsparagusEduardo in #5758
  • Added scope document and made changes to pull request template by @pkmnsnfrn and @Pawkkie and arguably the entire senate in #5706
  • Added instructions in PR template to make crediting people more clear by @pkmnsnfrn and @AsparagusEduardo made changes to my text in #5755
  • Fix website not showing the "How to add mon" 1.10 tutorial by @AsparagusEduardo in #5813
  • Install instructions by @hedara90 in #5876
  • Change install.md to mention make debug instead of DINFO=1 by @ravepossum in #5882
  • Backport changes from the wiki by @AsparagusEduardo in #5900
  • Improve README.md by @AsparagusEduardo in #5640

📦 Branch Synchronisation 📦

pret

  • 20th of December in #5845
    • Fix recorded battle link player loops by @AsparagusEduardo in pret#2071
    • Added POKEMART_LIST_END to avoid users accidentally removing it by @AsparagusEduardo in pret#1947
    • Fixed brace style inconsistencies by @AsparagusEduardo in pret#2072
    • remove sBirchSpeechPlatformBlackPal by @DizzyEggg in pret#2075

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.10.0...expansion/1.10.1

Version 1.10.0

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.10.0`.

🌋 REFACTORS 🌋

📜 = Uses a migration script.

  • Changes Evolution methods to Enums by @AlexOn1ine in #4977
  • Turn item hold effects into an enum by @Bassoonian in #5498
  • Change GET_MOVE_TYPE to a function by @AlexOn1ine in #5090
  • Created COMPOUND_STRINGs for default player names by @fdeblasio in #5037
  • Removed agbcc by @mrgriffin in #4994
  • Refactor Frontier Brains by @fdeblasio in #5027
  • Removed all instances of gBitTable[x] by @hedara90 in #5123
  • Made BuildColorMaps redundant by using static tables by @pkmnsnfrn in #5289
  • Removed FRONTIER_BRAIN_SPRITES and updated TRAINER_SPRITE, TRAINER_BACK_SPRITE, and TRAINER_CLASS by @fdeblasio in #5166
  • Added ShouldSwitch result to AiLogicData by @Pawkkie and @AlexOn1ine had the idea! in #5440
  • Switch AI refactor + considers free switches by @Pawkkie in #5379
  • Refactor ShouldSwitchIfAllBadMoves by @Pawkkie in #5452
  • Updated Wring Out effects to match Eruption effects by @AsparagusEduardo in #5549
    • Changed Wring Out/Crush Grip/Hard Press to use power instead of argument to determine its max power, just like how Eruption/Water Spout/Dragon Energy do it.
    • Also:
      • Renamed EFFECT_VARY_POWER_BASED_ON_HP to EFFECT_POWER_BASED_ON_TARGET_HP
      • Renamed EFFECT_ERUPTION to EFFECT_POWER_BASED_ON_USER_HP
  • Update battle messages to Gen 5+ standards by @kittenchilly in #3240
  • Should switch refactor to facilitate switch prediction by @Pawkkie in #5466
  • Unwind TRAINER_CLASS macro by @SBird1337 in #5611
  • Refactors Absorb to use Moveend by @AlexOn1ine in #5670
    • For new absorbing moves an argument should be added in moves_info.h
  • Changes name of B_SCR_NAME_WITH_PREFIX by @AlexOn1ine in #5675

🧬 General 🧬

Added

  • Added performance counter by @hedara90 and @SBird1337 provided the actual code in #5284
  • Added debug build target by @u8-Salem in #4817
  • Added AUTO_SCROLL_TEXT and NUM_FRAMES_AUTO_SCROLL_DELAY by @pkmnsnfrn in #5054
  • Adds SAVE_TYPE_ERROR_SCREEN by @pkmnsnfrn in #5188
  • Move Relearner and Renaming From Summary Screen by @ravepossum in #5513
  • Automatic Line Breaks, somewhat even lines by @hedara90 and @AsparagusEduardo in #5689
    • Automatically insert line breaks into a string with BreakStringAutomatic.
    • This function does not modify strings with existing line breaks.
    • Remove existing line breaks from a string with StripLineBreaks.

Changed

  • Removed agbcc by @mrgriffin in #4994
  • Removed all instances of gBitTable[x] by @hedara90 in #5123
  • Converted Mechadoll text to COMPOUND_STRINGs by @fdeblasio in #5276
  • New terrain bgs by @TheTrueSadfish in #5162
  • Removed agbcc screenshots from .gitignore by @Bassoonian in #5538
  • Set default battle shadow to Gen3 by @hedara90 in #5632
    • Note: Trainerslides don't work properly with Gen4 shadows.
  • Convert 3 variouses to callnatives by @AlexOn1ine in #5646

🗺️ Overworld 🗺️

Added

  • FRLG+ whiteout message by @cawtds in #4967
  • Dynamic Move Types in Summary Screen/Battle by @Galaxeeh in #5084
  • Adds OW_BERRY_IMMORTAL by @pkmnsnfrn in #5187
  • (Default Off) Item Description Headers by @ghoulslash in #4767
  • RTC-based wild encounters by @hjk321 in #5313
  • Added MB_X_Y_STAIR_WARP metatile behaviors by @pkmnsnfrn in #5278
  • Added Sideways Stairs by @ghoulslash in #4836
  • Added OW_UNION_DISABLE_CHECK and OW_FLAG_MOVE_UNION_ROOM_CHECK by @pkmnsnfrn in #5448
  • Adds new scripting macros to increase developer quality of life by @pkmnsnfrn in #5177
  • Added more later gen fishing mechanics by @kittenchilly in #5518

Changed

  • Created PokeNav COMPOUND_STRINGs by @fdeblasio in #4983
  • Added I_REPEL_INCLUDE_FAINTED config and behavior by @kittenchilly in #5239
  • RTC-based wild encounters follow up by @AlexOn1ine in #5328
  • Revert rtc based encounters by @AlexOn1ine in #5331
  • Made BuildColorMaps redundant by using static tables by @pkmnsnfrn in #5289
  • Added OW_AUTO_SIGNPOST and associated metatile behaviors by @pkmnsnfrn in #5044
  • Added support for overworld sprite gender differences + add all the sprites by @kittenchilly in #5394

Fixed

  • Added some null pointer checks by @tertu-m in #5130
  • Reset item flags on new game by @ghoulslash in #5363
  • Follower female fix by @hedara90 in #5475

🐉 Pokémon 🐉

Added

  • Added config to change Vivillon's breeding form by @kittenchilly in #4813
  • Added back GBA sprites via config by @AsparagusEduardo and @AlexOn1ine for their help with script to migrate data from vanilla to our current gSpeciesInfo in #5206
  • Added config to disable gender differences by @AsparagusEduardo in #5595

Changed

  • Made perfect IV count into a granular setting by @AsparagusEduardo in #5115
  • Updated species defines by @pkmnsnfrn in #5075
  • Added support for overworld sprite gender differences + add all the sprites by @kittenchilly in #5394
  • Renamed folders and symbols to match species defines by @AsparagusEduardo in #5581
    • Burmy and Wormadam footprints were in a plant subfolder. They have been moved to the species root folder
    • Paldean Wooper's subfolder was named wooper_paldean instead of just paldean. This has been corrected.
    • Zen Mode Galarian Darmanitan's folder was located in darmanitan/galarian/zen_mode. This has been corrected to darmanitan/galar_zen, alongside Galarian Standard Mode's darmanitan/galar_standard.
    • Also updated Ogerpon's folders similarly.
    • Renamed SPECIES_PIKACHU_PARTNER_CAP to SPECIES_PIKACHU_PARTNER.
  • Changing EVO_NONE from 0xFFFE to 0 by @GhoulMage in #5547
    • There could be a case for out of bounds errors if arrays or iterations are happening where you're using + 1 or - 1, as EVO_FRIENDSHIP used to be the first index although it started with 1.

Fixed

  • Follower female fix by @hedara90 in #5475
  • Fixed some gba sprites by @SubzeroEclipse in #5607

⚔️ Battle General ⚔️

Added

  • FRLG+ whiteout message by @cawtds in #4967
  • Added B_SHOW_TYPES and cleaned up IsDoubleBattle by @pkmnsnfrn in #5131
  • EV Caps and EV Items by @Flash1Lucky and @AlexOn1ine in #5269
  • Added in-battle shadows underneath all enemy battlers by @lhearachel in #5178
  • Added Gen 1 Crit Chance by @Pawkkie in #5439
  • Added battle flag that prevents running from wild Pokémon by @SarnPoke in #5502

Changed

  • Refactor Frontier Brains by @fdeblasio in #5027
  • Removed some hardcoding of move IDs + Gen4/5 Defog by @AsparagusEduardo in #5156
  • Convert 8 various to callnatives by @AsparagusEduardo in #5172
  • Anger Shell use saveattacker by @ghoulslash in #5409
  • Clean up Unseen Fist Check by @AlexOn1ine in #5420
  • Updated species defines by @pkmnsnfrn in #5075
  • Removes Crit Chance preproc by @AlexOn1ine in #5520
  • Update battle messages to Gen 5+ standards by @kittenchilly in #3240
  • More post-#3240 cleanup by @kittenchilly in #5593
  • Unwind TRAINER_CLASS macro by @SBird1337 in #5611
  • Removes redundant Decorate check by @AlexOn1ine in #5696
  • Changes target bit of Flower Shield by @AlexOn1ine in #5698

Fixed

  • Fixed a sprite issue with B_SHOW_TYPES by @pkmnsnfrn in #5157
  • Dynamic Move Display fixes by @Galaxeeh in #5251
  • Fixed a display issue with B_SHOW_TYPES by @pkmnsnfrn and @iriv24 in #5201
  • Fixed Gen 3 foreseen and Beat Up damage type by @hedara90 in #5323
  • Fixes Defog used by the wrong side when there is a Substitue and Screen by @AlexOn1ine in #5381
  • Fixes Hidden Power dynamic type bug by @AlexOn1ine in #5463
  • Display the correct shadow size when sending out a new Pokemon by @lhearachel in #5618
  • Fixed text wrap obtaining the incorrect glyph width by @AsparagusEduardo and @AlexOn1ine for their help verifying that the fix works with one of his custom strings in #5620
  • Improve line breaks/scrolls by @cawtds in #5641
  • Fixed Order Up + Tera Stellar breaking each other with Commander by @PhallenTree in #5667
  • Fixes wrong Id when AI chooses mon to switch in by @AlexOn1ine in #5684
  • Fixes Absorb regression caused by #5670 by @AlexOn1ine in #5688
  • Fixes heal blocked leeach seed in tests by @AlexOn1ine in #5700
  • Trainer class+name expansion fix for Battle Frontier by @hedara90 in #5699

🤹 Moves 🤹

Changed

  • Added Population Bomb animation by @kittenchilly in #5194
  • Move battle anim arrays to C by @cawtds in #5306
  • Grass/Water Pledge Swamp Animation + Sea of Fire animation tweak by @SonikkuA-DatH in #5325
  • New animations for many moves more details in description by @TheTrueSadfish in #5367
  • Use move effect for some moves instead of ids by @AlexOn1ine in #5433
  • Adds Commander and Order Up by @AlexOn1ine in #5246
  • Heart Swap Move Animation by @SonikkuA-DatH in #5460
  • Update shed_tail.c by @Bassoonian in #5494
  • Added Ion Deluge animation by @kittenchilly in #5467
  • Updated Wring Out effects to match Eruption effects by @AsparagusEduardo in #5549
    • Changed Wring Out/Crush Grip/Hard Press to use power instead of argument to determine its max power, just like how Eruption/Water Spout/Dragon Energy do it. Also:
    • Renamed EFFECT_VARY_POWER_BASED_ON_HP to EFFECT_POWER_BASED_ON_TARGET_HP
    • Renamed EFFECT_ERUPTION to EFFECT_POWER_BASED_ON_USER_HP
  • Refactors Absorb to use Moveend by @AlexOn1ine in #5670
    • For new absorbing moves an argument should be added in moves_info.h

Fixed

  • Dark Void, Clangorous Soulblaze, vortex animation fixes by @TheTrueSadfish in #5650

🎭 Abilities 🎭

Changed

  • Adds Commander and Order Up by @AlexOn1ine in #5246

🧶 Items 🧶

Added

  • Adds OW_BERRY_IMMORTAL by @pkmnsnfrn in #5187
  • Added functionality to Poké Flute and Town Map by @kittenchilly and @LOuroboros basically did the Town Map implementation in #5405
  • Decouple Poke Ball ids from item ids by @AlexOn1ine in #5560

Changed

  • Consolidated the values of Rotom's moves and added Gen9 base form effect by @fdeblasio in #5186
  • Added I_REPEL_INCLUDE_FAINTED config and behavior by @kittenchilly in #5239

Fixed

  • Replace hardcoded flute check with consumability check by @Bassoonian in #5508

🤖 Battle AI 🤖

Added

  • Adds config to show target of ingame partner by @AlexOn1ine in #5307
  • Switch AI refactor + considers free switches by @Pawkkie in #5379
  • New AI flag for marking the two last Pokémon as Ace Pokémon by @GhoulMage in #5587

Changed

  • Chilly Reception AI by @kittenchilly in #5271
  • Shed Tail AI by @SarnPoke and @AlexOn1ine, @Pawkkie in #5275
  • More missing AI logic by @kittenchilly in #5279
  • Adds basic trainer and smart trainer flags by @AlexOn1ine in #5298
  • AI_FLAG_SETUP_FIRST_TURN rename and clarifications by @Pawkkie in #5310
  • Added Composite AI Flags to Docs by @Pawkkie in #5349
  • AI frostbite score fixes and improvements by @Pawkkie and @kittenchilly for the suggestion! in #5362
  • Switch AI hitsToKO considers one shot prevention by @Pawkkie in #5371
  • Adds CanEndureHit AI function by @AlexOn1ine in #5373
  • Switch AI hitsToKO considers Disguise by @Pawkkie in #5375
  • Added ShouldSwitch result to AiLogicData by @Pawkkie and @AlexOn1ine had the idea! in #5440
  • Removes duplicate code in AI functions by @AlexOn1ine in #5457
  • Unify GetBattlerAbility/TerrainAffected to remove duplicate ai function by @AlexOn1ine in #5497
  • ShouldSwitchIfGameStatePrompt Tests by @Pawkkie in #5462
  • AI_FLAG_ACE_POKEMON takes into account separate trainers by @GhoulMage and @/uvula on Discord noted the weird behaviour. in #5608
    • Fix for the AI not considering both trainers Ace Pokémons in double battles with AI_FLAG_ACE_POKEMON.
  • Moves that deal a Fixed amount don't need AI handling by @AlexOn1ine in #5614
  • Combines CalculateMoveDamage arguments into a struct by @AlexOn1ine in #5570

Fixed

  • AI burn score fixes and improvements by @Pawkkie and @iriv24 and @AlexOn1ine in #5356
  • Improve AI's Skill Swap handling in double battles by @Pawkkie in #5360
  • Refactor ShouldSwitchIfAllBadMoves by @Pawkkie in #5452
  • Should switch refactor to facilitate switch prediction by @Pawkkie in #5466
  • Fixes Switch in flag not restoring mons properly with test by @Pawkkie and @iriv24 for finding, @AlexOn1ine for fixing in #5746

🧹 Other Cleanup 🧹

  • Removed metadata in AIF files by @SombrAbsol in #4958
  • Removed gPaletteDecompressionBuffer and unused palette functions/vars by @DizzyEggg in #4841
  • Changes Evolution methods to enums by @AlexOn1ine in #4977
  • Doesn't compile on some compilers by @AlexOn1ine in #5099
  • Update event.inc to accomodate new gDecompressionBuffer name by @Bassoonian in #5100
  • Created COMPOUND_STRINGs for default player names by @fdeblasio in #5037
  • Changed single-use berry blender strings to be COMPOUND_STRINGs by @fdeblasio in #4963
  • Made perfect IV count into a granular setting by @AsparagusEduardo in #5115
  • Dynamic move type clean up by @AlexOn1ine in #5132
  • Refactor Frontier Brains by @fdeblasio in #5027
  • Removed some hardcoding of move IDs + Gen4/5 Defog by @AsparagusEduardo in #5156
  • Teatime animations use B_WAIT_TIME_LONG by @AsparagusEduardo in #5173
  • Created PokeNav COMPOUND_STRINGs by @fdeblasio in #4983
  • Removed gBitTable usage again by @hedara90 in #5193
  • Removed support for the original LCG random number generator by @tertu-m in #5078
  • Deprecate MMBN Names by @pkmnsnfrn in #5240
  • Convert 8 various to callnatives by @AsparagusEduardo in #5172
  • Converted PC strings to COMPOUND_STRINGs by @fdeblasio in #5314
  • Cleaned up duplicate dynamic type functions by @AsparagusEduardo in #5338
  • Removes redundant moveTargetType ai function by @AlexOn1ine in #5354
  • Made BuildColorMaps redundant by using static tables by @pkmnsnfrn in #5289
  • Some strings were switched by @AlexOn1ine in #5374
  • Switch AI hitsToKO considers Disguise by @Pawkkie in #5375
  • Cleaned up a bit of code with GetBattlerPartyData by @AlexOn1ine in #5378
  • Minor Gem check optimazation by @AlexOn1ine in #5401
  • Simplify HP Logic by @AreaZR in #5403
  • Anger Shell use saveattacker by @ghoulslash in #5409
  • Converted berry and PokeBlock strings to COMPOUND_STRINGs by @fdeblasio in #5324
  • Merge item description branch history by @Bassoonian in #5419
  • Clean up Unseen Fist Check by @AlexOn1ine in #5420
  • Merge level_caps and ev_caps into one caps file by @kittenchilly in #5429
  • Removed trailing whitespace pass 10-2-2024 (Upcoming) by @kittenchilly in #5456
  • Fixed Commander test name by @Bassoonian in #5458
  • Updated species defines by @pkmnsnfrn in #5075
  • Adds padding in AiLogicData by @AlexOn1ine in #5468
  • Simplify BS_FAINTED_MULTIPLE_1 double battle logic in openpartyscreen by @ghoulslash in #5435
  • Removes duplicate code in AI functions by @AlexOn1ine in #5457
  • ShouldPivot type cleanup by @Pawkkie in #5441
  • Turn item hold effects into an enum by @Bassoonian in #5498
  • Unify GetBattlerAbility/TerrainAffected to remove duplicate ai function by @AlexOn1ine in #5497
  • Clean up Shedinja code by @Bassoonian in #5501
  • Clean up scrcmd PR by @Bassoonian in #5511
  • Removes Crit Chance preproc by @AlexOn1ine in #5520
  • Removed agbcc screenshots from gitignore by @Bassoonian in #5538
  • Removed unnecessary gBattlerAttacker usage by @AlexOn1ine in #5554
  • Removed remaining line breaks from #3240 + Prefix wrap fix by @AsparagusEduardo in #5556
  • More post-#3240 cleanup by @kittenchilly in #5593
  • Renamed folders and symbols to match species defines by @AsparagusEduardo in #5581
    • Also:
    • Burmy and Wormadam footprints were in a plant subfolder. They have been moved to the species root folder
    • Paldean Wooper's subfolder was named wooper_paldean instead of just paldean. This has been corrected.
    • Zen Mode Galarian Darmanitan's folder was located in darmanitan/galarian/zen_mode. This has been corrected to darmanitan/galar_zen, alongside Galarian Standard Mode's darmanitan/galar_standard.
    • Also updated Ogerpon's folders similarly.
    • Renamed SPECIES_PIKACHU_PARTNER_CAP to SPECIES_PIKACHU_PARTNER.
  • Minor BattleStruct clean up by @AlexOn1ine in #5585
  • Fixed a ball update oversight by @Bassoonian in #5609
  • AI_FLAG_ACE_POKEMON takes into account separate trainers by @GhoulMage and @/uvula on Discord noted the weird behaviour in #5608
    • Fix for the AI not considering both trainers Ace Pokémons in double battles with AI_FLAG_ACE_POKEMON.
  • Moves that deal a Fixed amount don't need AI handling by @AlexOn1ine in #5614
  • Combines CalculateMoveDamage arguments into a struct by @AlexOn1ine in #5570
  • Follow up for #5570 by @AlexOn1ine in #5625
  • AI_CalcDamage clean up by @AlexOn1ine in #5629
  • Convert 3 variouses to callnatives by @AlexOn1ine in #5646
  • Convert gBattleStringsTable to COMPOUND_STRINGs by @AsparagusEduardo in #5649
  • Added merged placeholder text for trainer name with class by @kittenchilly in #5622
  • Cleans up Primal Reversion code by @AlexOn1ine in #5659
  • Critical Hit documentation and distorted match up struct switch by @AlexOn1ine in #5665
  • Changes name of B_SCR_NAME_WITH_PREFIX by @AlexOn1ine in #5675
  • Removes redundant Decorate check by @AlexOn1ine in #5696
  • Changes taget bit of Flower Shield by @AlexOn1ine in #5698
  • Changing EVO_NONE from 0xFFFE to 0 by @GhoulMage in #5547
    • There could be a case for out of bounds errors if arrays or iterations are happening where you're using + 1 or - 1, as EVO_FRIENDSHIP used to be the first index although it started with 1.

🧪 Test Runner 🧪

Changed

  • Fixed Commander test name by @Bassoonian in #5458
  • ShouldSwitchIfGameStatePrompt Tests by @Pawkkie in #5462
  • Added various tests, add RNG_RANDOM_TARGET by @ghoulslash in #5438
  • Added Costar Tests, Download Test for Doubles by @ghoulslash in #5526
  • Updated Wring Out effects to match Eruption effects by @AsparagusEduardo in #5549
    • Changed Wring Out/Crush Grip/Hard Press to use power instead of argument to determine its max power, just like how Eruption/Water Spout/Dragon Energy do it. Also:
    • Renamed EFFECT_VARY_POWER_BASED_ON_HP to EFFECT_POWER_BASED_ON_TARGET_HP
    • Renamed EFFECT_ERUPTION to EFFECT_POWER_BASED_ON_USER_HP
  • Healer ability tests by @Pawkkie in #5559
  • Mark all tests as used by @mrgriffin in #5531

Fixed

  • Should switch refactor to facilitate switch prediction by @Pawkkie in #5466

📚 Documentation 📚

  • DoBattleIntro state documentation by @AsparagusEduardo and @ShinyDragonHunter in #5231
  • Deprecate MMBN Names by @pkmnsnfrn in #5240
  • AI_FLAG_SETUP_FIRST_TURN Rename and Clarifications by @Pawkkie in #5310
  • Added Composite AI Flags to Docs by @Pawkkie in #5349
  • Updated the new pokemon tutorial for 1.10 by @hedara90 in #5721
    • Some changes compared to previous.

New Contributors

  • @SombrAbsol made their first contribution in #4958
  • @Galaxeeh made their first contribution in #5084
  • @Flash1Lucky made their first contribution in #5269
  • @GhoulMage made their first contribution in #5547

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.9.4...expansion/1.10.0

Version 1.9.4

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.9.4`.

🌋 IMPORTANT 🌋

  • This update integrates pret's latest Makefile changes, which rearranges the entire file in order to speed up compilation times overall. If you did any changes to it (such as installing Poryscript) and are having issues resolving the conflicts, keep expansion's version of Makefile and reapply your changes afterwards.

🧬 General 🧬

Fixed

  • Fixed alignment errors in EWRAM_INIT and friends when using u8, u16, etc. by @aronson in #5512
  • Update test LD script to respect 4 byte data section alignment by @aronson in #5517
  • Fixed Missing string_util.h include in mini_printf.c by @mrgriffin in #5572
  • Fixed unnecessary dependency scanning for test build and test rom names by @ravepossum in #5594
  • Fixed makefile: dependencies for map_group_count.h by @SBird1337 in #5648
    • Fixes an issue that caused the build to fail on updates to src/debug.c due to mismatched dependency.

🗺️ Overworld 🗺️

Changed

  • Followers sprite fixes by @Cafeei in #5669
  • Follower fixes, Melmetal, Patrat, Woobat by @hedara90 in #5685
  • Fixed Farfetch'd overworld sprite by @hedara90 in #5711

Fixed

  • Fixed Berry mutations always generating a Persim Berry by @Bassoonian in #5504

🐉 Pokémon 🐉

Changed

  • Changing EVO_NONE from 0xFFFE to 0 by @GhoulMage in #5547
    • There could be a case for out of bounds errors if arrays or iterations are happening where you're using + 1 or - 1, as EVO_FRIENDSHIP used to be the first index although it started with 1.
  • PokeCommunity sprites batch (October) by @kittenchilly in #5655
  • Followers sprite fixes by @Cafeei in #5669
  • Follower fixes, Melmetal, Patrat, Woobat by @hedara90 in #5685
  • Fixed Farfetch'd overworld sprite by @hedara90 in #5711

Fixed

  • Fixed P_FRIENDSHIP_EVO_THRESHOLD not checking for Gen 8 by @kittenchilly in #5503
  • Fixed HGSS dex search printing wrong mon after selecting evos by @ravepossum in #5552
  • Fixed 64px uncompressed followers by @hedara90 in #5601
  • Deoxys Sprite/Animation Fixes by @SarnPoke in #5603
  • Fixes Aegislash not reverting back by @AlexOn1ine in #5734

⚔️ Battle General ⚔️

Changed

  • Fixed damage calc modifiers by @AlexOn1ine in #5604

Fixed

  • Fixed Shiny Pokemon not being shiny after transforming with a gimmick by @hedara90 in #5573
  • Handle showdowns apostrophe the same way as ASCII apostrophe by @cawtds in #5712
  • Fixes Misty Terrain displaying wrong message by @AlexOn1ine in #5742
  • Fixes Dynamax dynamic move type by @AlexOn1ine in #5739

🤹 Moves 🤹

Changed

  • Fixed damage calc modifiers by @AlexOn1ine in #5604
  • Updated ability popups for Skill Swap, Mummy/Lingering Aroma, Worry Seed, Simple Beam, fix Doodle/Role Play bugs by @PhallenTree in #5493

Fixed

  • Fixed Follow Me failing in Single Battles for Gen 6/7 config by @AsparagusEduardo in #5542
  • Fixed AnimTask_HorizontalShake uses for shaking screen in battle anims by @ghoulslash in #5562
  • Fixed weather genie move anims and Springtide Storm targets by @ravepossum in #5553
  • Fixes Magic Guard not preventing Salt Cure by @AlexOn1ine in #5583
  • Fixes Dragon Tail using the effect twice during a Parental Bond attack by @AlexOn1ine in #5630
  • Fixes Magic Coat message by @AlexOn1ine in #5645
  • Fixes Take heart by @AlexOn1ine in #5658
  • Fixed Floral Healing anim by @AlexOn1ine in #5733
  • Fixes Population Bomb / Triple Kick missing message by @AlexOn1ine in #5747
  • Changes Max Phantasm move anim script call by @AlexOn1ine in #5737
  • Fixes Partner targeting and Acupressure/Ally Switch interaction by @AlexOn1ine in #5446
  • Revival Blessing fixes + Using Lunar Blessing's animation by @ghoulslash in #5490
  • Fixed curse + Protean interaction by @hedara90 in #5663
  • Added Minimize interaction to Supercell Slam by @hedara90 in #5713

🎭 Abilities 🎭

Changed

  • Fixed damage calc modifiers by @AlexOn1ine in #5604

Fixed

  • Adds tests and Costar fix from PR #5526 by @AlexOn1ine in #5529
  • Fixes Red Card / Eject Pack interaction with Emergency Exit by @AlexOn1ine in #5657
  • Fixed curse + Protean interaction by @hedara90 in #5663
  • Mimicry updates typing with RemoveAllTerrains() by @AERDU in #5666
  • Updated ability popups for Skill Swap, Mummy/Lingering Aroma, Worry Seed, Simple Beam, fix Doodle/Role Play bugs by @PhallenTree in #5493
  • Fixed curse + Protean interaction by @hedara90 in #5663
  • Fixes Ice Face regression by @AlexOn1ine in #5678
  • Fixes Neutralizing Gas crashes + adds missing interaction, Regenerator small fix by @PhallenTree in #5694

🧶 Items 🧶

Changed

  • Removes duplicate Booster Energy code by @AlexOn1ine in #5656

Fixed

  • Fixes Red Card / Eject Pack interaction with Emergency Exit by @AlexOn1ine in #5657
  • Fixes Red Card / Eject Pack interaction by @AlexOn1ine in #5724
  • Fixes gems triggering on confusion damage by @AlexOn1ine in #5723
  • Fixes Kee Maranga and Enigma Berry by @AlexOn1ine in #5727
  • Fixes Blunder Policy by @AlexOn1ine in #5722
  • Fixes Rusted Shield/Sword allowed to be Knocked Off from Zamazenta/Zacian by @iriv24 in #5750

🤖 Battle AI 🤖

Fixed

  • Fixed certain move data being cleared on turn end by @Pawkkie and @AlexOn1ine in #5488
  • Global is used instead of passed var by @AlexOn1ine in #5546
  • Fixes dynamicMoveType global not being reset during AI calcs by @AlexOn1ine in #5628

🧹 Other Cleanup 🧹

  • Remove one redundant call of SetAiLogicDataForTurn in DoBattleIntro by @AlexOn1ine in #5491
  • Cleanup extraneous function in battle_anim.h by @hedara90 in #5506
  • Add newline to move relearner string by @Bassoonian in #5523
  • Fixed 10,000,000 Volt Thunderbolt name by @AsparagusEduardo in #5533
  • Added constant to expansion inclusive copyright magic number by @pkmnsnfrn in #5413
  • Centralise AI Tests trainer name by @Bassoonian in #5532
  • Remove now outdated information from readme by @Bassoonian in #5548
  • Changing EVO_NONE from 0xFFFE to 0 by @GhoulMage in #5547
    • There could be a case for out of bounds errors if arrays or iterations are happening where you're using + 1 or - 1, as EVO_FRIENDSHIP used to be the first index although it started with 1.
  • Shed Skin chance fix by @Pawkkie in #5558
  • Restore test file dependencies so they're rebuilt properly by @ravepossum in #5617
  • Improve SEND_OUT error message; require Speed for all battlers by @mrgriffin in #5631
  • Removes duplicate Booster Energy code by @AlexOn1ine in #5656
  • Wrong assumtion in dauntless_shield.c by @AlexOn1ine in #5692

🧪 Test Runner 🧪

Added

  • Add curious medicine test by @ghoulslash in #5540
  • Tests: detect task leaks by @mrgriffin in #5528

Changed

  • Add text width tests for move, ability, item, and pokedex descriptions by @kittenchilly in #5505
  • Centralise AI Tests trainer name by @Bassoonian in #5532
  • Add basic Steam Engine, Guard Dog Tests by @ghoulslash in #5569
  • Fixed damage test by @GhoulMage and @mrgriffin for teaching me pokeemerald-expansion tests in #5574
  • Fallback memmem implementation by @mrgriffin in #5561
  • Hydra: Support %p in test summaries by @mrgriffin in #5626
  • Improve SEND_OUT error message; require Speed for all battlers by @mrgriffin in #5631
  • Check that PASSES_RANDOMLY affected a Random call by @mrgriffin in #5635
  • Wrong assumtion in dauntless_shield.c by @AlexOn1ine in #5692

Fixed

  • Update test LD script to respect 4 byte data section alignment by @aronson in #5517
  • Adds tests and Costar fix from PR #5526 by @AlexOn1ine in #5529
  • Fixed broken Starting Terrain test by @hedara90 in #5582

📚 Documentation 📚

  • Add changelog header in PR template to aid automation by @AsparagusEduardo in #5539
  • Added compressed OW mon VRAM notice in config file by @AsparagusEduardo in #5599
  • Update README.md to link to INSTALL.md by @Pawkkie in #5720
  • Fixes minor move desc errors by @AlexOn1ine in #5728

📦 Branch Synchronisation 📦

pret

  • 15th of October in #5527
    • Slight storage system documentation by @luckytyphlosion in pret#2024
    • Clean up defines lacking spaces by @Bassoonian in pret#2025
    • UB fix in battle_transition.c by @cawtds in pret#2007
    • preproc: support arbitrary expressions in enums by @mrgriffin in pret#2026
    • [Build System Rewrite] Refactored Makefile by @Icedude907 in pret#1950
    • Fixed incorrect point macros in contest_ai_script.inc by @NTx86 in pret#2028
    • [Build System Rewrite] Massive build speed improvement via scaninc changes by @Icedude907 in pret#1954
    • [Build System Rewrite] Improved audio rules by @Icedude907 in pret#1957
    • Update INSTALL.md to state that Windows 8 is no longer supported by Microsoft by @luciofstars in pret#2029
    • Update pull_request_template.md to include Discord username update by @luciofstars in pret#2030
    • remove ScriptContext_Enable from secret_base.h by @DizzyEggg in pret#2032
    • Remove gflib by @Kurausukun in pret#2033
    • Minor toolchain fixes by @GriffinRichards in pret#2031
    • Bugfix for cable car hikerGraphicsIds array by @Scyrous in pret#2039
    • Remove explicit symbol sizes in sym_common.txt by @GriffinRichards in pret#2038
    • Ignore mGBA screenshots by @Jaizu in pret#2041
    • Replaced copyright magic numbers in intro.c with constants by @pkmnsnfrn in pret#2035
    • Fixed typo: | should be || in Task_TryFieldPoisonWhiteOut by @AreaZR in pret#2044
    • [preproc] support C23 enum underlying type syntax by @mrgriffin in pret#2043
    • Fixed deleting files with dependency files by @mid-kid in pret#2045
    • Remove unnecessary looping for rule generation and unroll macros by @mid-kid in pret#2046
    • Get rid of common syms by @luckytyphlosion in pret#2040
    • Bugfix for cable car hikerGraphicsIds array by @Scyrous in pret#2039
    • UB fix in battle_transition.c by @cawtds in pret#2007
    • Fixed typo: | should be || in Task_TryFieldPoisonWhiteOut by @AreaZR in pret#2044
    • Get rid of common syms by @luckytyphlosion in pret#2040
    • Fixed incorrect point macros in contest_ai_script.inc by @NTx86 in pret#2028
  • 5th of November in #5644
    • Added define value for bard sound length by @fdeblasio in pret#2052
    • Silence 'Nothing to be done for generated' messages by @GriffinRichards in pret#2059
    • Lay out emerald version png horizontally by @GriffinRichards in pret#2062
  • 29 of November in #5736
    • Remove usage of gHeap in sSpritePalettes_ContestantsTurnBlinkEffect by @Lactozilla in pret#2064
    • BUGFIX: Fix Counter and Mirror Coat checking the wrong category by @surtr-games in pret#2066
    • Add TRY_DRAW_SPOT_PIXEL by @GriffinRichards in pret#2055
    • Added extra encoded character support by @AsparagusEduardo in pret#2050

merrp's followers

  • Merrp merge (12th of October) by @Bassoonian in #5514
    • d80190fe105eee12bbf74ae29647ac909084d35c fix: Dig in Sealed Chamber no longer freezes follower.

New Contributors

  • @AERDU made their first contribution in #5666

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.9.3...expansion/1.9.4

Version 1.9.3

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.9.3`.

🌋 REFACTORS 🌋

📜 = Uses a migration script.

  • Converted settotemboost command to callnative in #5418
  • Removed unused RESOURCE_FLAG_TRACED in #5430
  • Changed MOVEEND_ defines to an enum in #5449

✨ Feature Branches ✨

merrp/aarant's Followers

Fixed

  • Fixed Expansion-exclusive issue that caused trainers to not play their "pointing" animation when Followers were out during battle intro by @kittenchilly in #5406

⚔️ Battle General ⚔️

Changed

  • Improved Mega evolution animation to make it a little smoother by @kleenxfeu in #4816

Fixed

  • Fixed affection check for exp multiplier by @Bassoonian in #5421
  • Fixed multiple Primal Reversions not occurring if multiple battlers fainted on the previous turn by @PhallenTree in #5430

🤹 Moves 🤹

Added

  • Added missing B_AFTER_YOU_TURN_ORDER config by @PhallenTree in #5400
    • Gen 5-7: After You fails if the order remains the same after using After You.
    • Gen 8+: After You no longer fails if the turn order remains the same after using After You.
  • Added missing B_QUASH_TURN_ORDER config by @PhallenTree in #5400
    • Gen 5-7: If multiple Pokémon are affected by Quash, they move in the order they were affected by Quash.
    • Gen 8+: If multiple Pokémon are affected by Quash, they now move fastest to slowest.
  • Added missing updated B_UPDATED_CONVERSION_2 by @wiz1989 in #5453
    • Gens 2-4: Conversion 2 changes the user's type to a type that is resistant/immune to the last move the user was hit by.
    • Gen 5+: Conversion 2 changes the user's type to a type that resists the last move used by the selected target.

Fixed

  • Fixed Scale Shot corrupting the move used on the next turn by @AlexOn1ine in #5397
  • Fixed Growth's description not being updated based on B_GROWTH_STAT_RAISE by @nescioquid in #5398
  • Fixed Quash not updating the battlers' actions correctly by @PhallenTree in #5400
    • Cleanup by @PhallenTree in #5430
  • Fixed Snatched Swallow not recovering HP if the Snatcher is not under the effect of Stockpile (should still heal) by @PhallenTree in #5430
  • Fixed Counter users being damaged by Spiky Shield by @AlexOn1ine in #5402
  • Fixed Electrified Dragon Darts not correctly avoiding targets with ability immunity (Volt Absorb, Motor Drive) by @PhallenTree in #5430
  • Fixed Trace not activating a switch-in ability it traces (eg. Intimidate) by @PhallenTree in #5430
    • Removed unused RESOURCE_FLAG_TRACED.
  • Fixed recoil damage not triggering healing berries by @AlexOn1ine in #5449
    • Also changed MOVEEND_ defines to an enum.

🎭 Abilities 🎭

Fixed

  • Fixed Dancer activating even if the dance move is stolen by Snatch by @PhallenTree in #5430
  • Fixed Ability popup when multiple Pokémon faint at the same time by @PhallenTree in #5430
  • Multiple ability fixes by @PhallenTree in #5447
    • Fixed Protosynthesis/Quark Drive sometimes not activating ability popup despite still gaining the boost as they should.
    • Fixed Protosynthesis/Quark Drive activating on Transformed battlers.
    • Fixed Protosynthesis activating despite Cloud Nine being present on the field.
    • Fixed Quark Drive not activating if the battler is not grounded.
    • Fixed Protosynthesis/Quark Drive/Beast Boost stat raise priority when multiple stats are tied for the highest one.
      • Before: Attack, Defense, Speed, Special Attack, Special Defense.
      • After: Attack, Defense, Special Attack, Special Defense, Speed.

🧶 Items 🧶

Fixed

  • Fixed Ogerpon's Masks not increasing the power of moves by 20% by @AlexOn1ine in #5391
  • Fixed Jubilife Muffin not working by @kittenchilly in #5444
  • Fixed duplicating flute bug in double battles by @ghoulslash in #5436

🤖 Battle AI 🤖

Fixed

  • Fixed Trick/Switcheroo giving AI score even if the opponent has no held item by @kittenchilly in #5412
  • Various AI fixes in AI_CalcMoveEffectScore by @ghoulslash in #5474
    • Missing break from EFFECT_ABSORB switch case.
    • Using last used move for Mirror Move instead of predicted move.

🧹 Other Cleanup 🧹

Changed

Fixed

  • Fixed potential uninitialized behavior in ChangeOrderTargetAfterAttacker by @AlexOn1ine in #5393
  • Fallback on default BW map pop-up theme to reduce potential for error by @ravepossum in #5392
  • Multiple typo fixes by @nescioquid in #5398
  • VS Seeker documentation fix by @Bassoonian in #5415

🧪 Test Runner 🧪

Added

  • Added missing After You and Quash tests by @PhallenTree in #5400

Changed

  • Improved Tangling Hair test to make sure that chained effects do not overwrite relevant battler IDs by @ghoulslash in #5423
  • Improved Full Heal item tests by @kittenchilly in #5444

Fixed

New Contributors

  • @nescioquid made their first contribution in #5398
  • @kleenxfeu made their first contribution in #4816
  • @wiz1989 made their first contribution in #5453

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.9.2...expansion/1.9.3

Version 1.9.2

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.9.2`.

🌋 REFACTORS 🌋

📜 = Uses a migration script.

  • Remove unused BattleScript_WindPowerActivatesEnd2 in #5257
  • Refactored in-battle disobedience to fix bug in #5245

💥 Hardlock/Softlock/Crash/Compiling fixes 💥

  • Fixed hardlock when Hyperspace Fury is used by Hoopa Unbound by @AlexOn1ine in #5237
  • Fixed compile error when OW_POKEMON_OBJECT_EVENTS is TRUE but P_HISUIAN_FORMS is FALSE around Basculin by @hjk321 in #5256
  • Fixed hardlock when the AI cannot choose moves due to its opponent having Wonder Guard by @Pawkkie and Wiz in #5317
  • Fixed multiple Pledge move hardlocks
    • Fixed potential hardlock when attempting to use Pledge moves on the same turn that the user would wake up by @PhallenTree in #5330
    • Fixed hardlock when the opponent's combo doesn't happen when cancelled by sleep by @hedara90 and @PhallenTree in #5339
    • Fixes hardlock when the opponent's combo doesn't happen when cancelled by freeze by @PhallenTree in #5340
    • Fixed hardlock when the opponent's combo doesn't happen when cancelled by Powder by @hedara90 in #5341

🧬 General 🧬

Fixed

  • Fixed loading into the wrong version of a map after saving in areas with multiple layouts by @hedara90 in #5347

🐉 Pokémon 🐉

Added

  • Added OVERWORLD_SET_ANIM macro to allow using custom animation tables for Overworld Pokémon by @hedara90 in #5309
    • Added asymetrical Farfetch'd sprites using a previously unused table from merrp's followers branch.
  • Added unique sprites for overworld Pumpkaboo and Gourgeist forms by @hedara90 in #5390
  • Added missing Sirfetch'd competitive alias (SPECIES_SIRFETCH_D) by @cawtds in #5283
  • Added Paldean Wooper and Clodsire overworld sprites by @Cafeei in #5277
  • Added missing Gen 9 Overworld sprites by @Liamjd14 in #5304
  • Added missing overworld sprites by @Liamjd14 in #5336
    • New Sprites:
      • Oricorio Pom Pom/Pa'U/Sensu, Zygarde 10%/Complete and Original Color Magearna
        • Credits to: Princess-Phoenix, Larryturbo, Kidkatt, Zender1752 and SageDeoxys.
      • Black/White Kyurem
        • Credits to: Larryturbo.
      • Shaymin Sky and Therian Tornadus/Thundurus/Landorus
        • Credits to: @Liamjd14
    • Using their base form's sprites
      • Totem Raticate/Mimikyu/Marowak and Partner Pikachu/Eevee
  • Added Added asymmetrical overworld sprites by @Liamjd14 in #5336
    • Slowbro (Galarian), Tinkatink, Tinkatuff, Tinkaton and Scovillain
    • Enabled in gSpeciesInfo by @Liamjd14 in #5385

Changed

  • Improved Garganacl and Naclstack battle sprites by using the ones from @CyanSMP64's repo by @kittenchilly in #5142
  • Improved both shiny Indeedee by @Cafeei in #5285
  • Shiny Combusken now uses its Gen8+ palette by @Cafeei in #5333

Fixed

  • Reenabled unused female Indeedee overworld sprite by @Cafeei in #5285
  • Fixed G-Max Corviknight and Centiskorch's expanded names by @PhallenTree in #5296
  • Fixed G-Max Cinderace back sprite by @hedara90 in #5295
  • Fixed Shiny Mothim' by @Cafeei in #5333
  • Fixed multiple battle sprite issues by @kittenchilly in #5142
    • Sprite issues:
      • Bombirdier, Mega Absol/Aerodactyl/Latias/Latios/Salamence, Orthworm and Veluza.
    • Shiny issues:
      • Bombirdier, Kilowattrel, Landorus, Magearna, Mega Diancie/Medicham, Galarian Ponyta, Shroodle, Spidops and Wattrel.
  • Fixed multiple overworld Pokémon sprites - Part 1 by @Cafeei in #5241
    • "Blinking pixels":
      • Dwebble, Krookodile, Servine, Throh and Vulpix.
    • Shiny Palettes
      • Crustle, Excadrill, Lillipup, Serperior, Servine, Sigilyph, Swoobat, Tranquil and Venipede.
    • Misc fixes:
      • Archen, Basculin, Blitzle, Crustle, Escavalier, Krokorok, Krookodile, Sawsbuck, secondary, Serperior, Snivy, Throh, Woobat, Zebstrika
  • Fixed multiple overworld Pokémon sprites - Part 2 by @Cafeei in #5333
    • Palette Fixes:
      • Shiny Riolu/Snover, Oshawott, Kabutops, Shieldon, Kingler and Groudon.
    • Fixed "Blinking pixels":
      • Abomasnow, Aggron, Alakazam, Ariados, Articuno, Azumarill, Barboach, Bayleef, Bibarel, Blastoise, Celebi, Charmeleon, Cherrim, Chinchou, Cloyster, Corphish, Corsola, Crawdaunt, Cubone, Dewott, Doduo, Dusknoir, Electabuzz, Espeon, Exeggcute, Farfetch'd, Feraligatr, Flaafy, Flareon, Floatzel, Furret, Gastly, Girafarig, Giratina, Gligar, Gloom, Golbat, Grumpig, Hariyama, Heatran (just reduced), Hoppip, Jolteon, Jumpluff, Kricketot, Larvitar, Leafeon, Lileep, Lumineon, Luxio, Luxray, Machamp, Magneton, Mantine, Mantyke, Marowak, Meowth, Mesprit, Mew, Mewtwo, Mime Jr, Moltres, Numel, Oshawott, Phione, Pinsir, Politoed, Porygon-Z, Probopass, Quilava, Qwilfish, Rampardos, Rapidash, Regirock, Sceptile, Scizor, Seel, Shieldon, Shiftry, Slowking, Smoochum, Sneasel, Spheal, Steelix, Sudowoodo, Suicune, Swellow, Swinub, Tentacruel, Togekiss, Togepi, Vaporeon, Vibrava, Wartortle, Wooper, Yanma, Yanma, Yanmega, Zangoose, Zapdos, Zubat
    • Misc. Sprite Fixes: Dewott, Misdreavus, Oshawott, Torkoal and Victini.
  • Overworld sprite fixes by @Liamjd14 (with help from @hedara90 to solve conflicts) in #5334
    • Palette Fixes: Shieldon.
    • Misc. Sprite Fixes Torkoal.
    • "Blinking pixels":
      • Ambipom, Armaldo, Crawdaunt, Crobat, Donphan, Flaaffy, Flygon, Grovyle, Hoppip, Igglybuff, Illumise, Jumpluff, Ledian, Ledyba, Mamoswine, Mantine, Marshtomp, Meganium, Mightyena, Miltank, Numel, Prinplup, Raikou, Roserade, Skarmory, Skiploom, Spinarak, Staraptor, Stunky, Torkoal, Wooper, Xatu, Yanma
  • Overworld Pokémon sprite changes by @Liamjd14 in #5336
    • Fixed "blinking pixels": * Alolan Graveler/Golem/Alolan/Ninetales * Dawn Wings Necrozma * Hisuian Growlithe/Arcanine * Winter Sawsbuck
    • Added missing Shiny Palettes
      • Calyrex Ice/Shadow Rider, Origin Dialga/Palkia, White-Striped Basculin, Therian Enamorus and Low-Key Toxtricity.
    • Removed Gigantamax Low-Key Toxtricity using base Low-Key follower sprites.
    • Fixed Shiny palettes
      • Alolan Marowak/Raichu, Eternal Flower Floette, Flabébé (All), Galarian Ponyta/Rapidash
      • Typhlosion-Hisui follower shiny stomach color wrong - done
    • Other sprite/palette fixes
      • Alolan Exeggutor/Marowak/Persian/Raichu/Sandshrew, Hisuian Sligoo/Goodra and Winter Sawsbuck.

⚔️ Battle General ⚔️

Changed

  • Updated Damage Category icons to match Gen6+ colors by @kittenchilly in #5080

Fixed

  • Fixed Slateport Battle Tent/Battle Factory issues by @SarnPoke in #5281
    • Choosing the "SWAP" option no longer shows invalid Pokémon ("??????????").
    • Reloading after choosing "REST" no longer resets the player's challenge party to invalid Pokémon ("??????????").
  • Fixed Starting Status happening Wild Battles from a previous Trainer Battle by @PhallenTree in #5248
  • Fixed bugged behavior caused by Z-Moves and disobedience by @hedara90 in #5245
  • Fixed Entry Hazards targeting wrong side of the field if the opponent fainted by @PhallenTree in #5262
  • Fixed being able to use multiple of the same Gimmick in Double Battles by @AgustinGDLV in #5235
  • Fixed Terastallization not granting immunity to Tar Shot by @AlexOn1ine in #5302
  • Fixed Cmd_trainerslidein/out using the incorrect function by @ghoulslash in #5326
    • Cleanup by @hedara90 in #5344

🤹 Moves 🤹

Added

  • Added Charge's Gen 9 behavior via B_CHARGE config by @AlexOn1ine in #5274
  • Added Powder's Gen 7+ behavior of not causing damage when under Heavy Rain via B_POWDER_RAIN by @PhallenTree in #5370

Fixed

  • Fixed move descriptions missing periods (Decorate, Collision Course, Electro Drift) by @Pawkkie and Kasen in #5221
  • Fixed Confide not being blocked by Crafty Shield interaction by @hedara90 in #5202
  • Fixed message for switch out moves by @kittenchilly in #5258
  • Fixed Ice Fang's descriptions using the opposite of what they're supposed to do based on B_USE_FROSTBITE by @laserXdolphin in #5273
  • Fixes to Instruct by @PhallenTree in #5262
    • Fixed Instruct bypassing AtkCanceler checks (Instruct allowed the target to act while asleep, flinched, etc.) and its interaction with First Turn Only moves (Fake Out, First Impression, Mat Block).
    • Fixed Instruct's animation using the attacker and target of the called move.
  • Fixed Scale Shot's effect not activating if the opponent fainted before all hits finished by @AlexOn1ine in #5292
  • Fixed Round not preserving turn order for non-Round users if there's a switch out at the beginning of the turn by @AlexOn1ine in #5292
  • Fixed Max Moves ignoring absorbing abilities (+ test) by @PhallenTree in #5296
  • Fixed attack string for Max Moves not being printed if it's blocked by Max Guard by @hedara90 in #5312
  • Fixed some Pledge move combo issues by @PhallenTree in #5330
    • Fixed Pledge move combos attempting to be executed multiple times in a turn, causing mons to decrement sleep timer multiple times during the turn or causing infinite loops.
  • Fixed potential issue with custom non-sound moves that use EFFECT_ATTACK_UP_USER_ALLY or EFFECT_PERISH_SONG being blocked by Soundproof anyway by @AlexOn1ine in #5301
  • Fixed Pledge combinations not being absorbed by absorption Abilities (Sap Sipper, Storm Drain, etc.) by @hedara90 in #5364
  • Fixed Toxic Thread decreasing speed by 2 stages instead of 1 by @AsparagusEduardo in #5369
  • Fixed Destiny Bond not working if the user was fainted by a multi-Hit move's non-first hit by @AlexOn1ine in #5377
  • Fixed Powder interactions by @PhallenTree in #5370
    • Fixed Magic Guard not protecting against Powder's secondary damage when using a Fire-type move.
    • Fixed Fire/Water Pledge combination being cancelled by Powder.
    • Fixed Fire Z-Moves not playing their animations and not granting their secondary effects when the user is under Powder's effect
  • Fixed After You/Shell Trap not updating battlers' actions correctly by @PhallenTree in #5384

🎭 Abilities 🎭

Fixed

  • Fixed weather abilities not activating when Cloud Nine user leaves the field by @AlexOn1ine in #5209
  • Fixed missing break for Poison Puppeteer's code by @u8-Salem in #5243
  • Fixed Pokémon with Purifying Salt being poisoned by Toxic Spikes by @AlexOn1ine in #5252
  • Fixed Parental Bond not affecting Snore by @hedara90 in #5264
  • Fixed Tera Shift's description by @AsparagusEduardo in #5351

🧶 Items 🧶

Fixed

  • Fixed berries missing their timing after passive damage by @AlexOn1ine in #5300
  • Fixed Micle Berry not increasing accuracy on the next turn by @AlexOn1ine in #5358

🤖 Battle AI 🤖

Changed

  • AI is encouraged to use "always crit" moves on partner with Anger Point by @SarnPoke in #5244

Fixed

  • Fixed AI not seeing the power of Max Moves by @AlexOn1ine in #5299
  • Fixed minor wrong order in AI_CalcDamage that made Nature Power not be considered for Z-Moves by @AlexOn1ine in #5155
  • Fixed AI not considering Tera Blast/Tera Storm by @AlexOn1ine in #5155
  • Fixed AI_IsMoveEffectInPlus reading the incorrect stat for secondary effects that reduce stats by 2 stages by @ghoulslash and @Pawkkie in #5366

🧹 Other Cleanup 🧹

Changed

  • Remove unused BattleScript_WindPowerActivatesEnd2 signature by @u8-Salem in #5257
  • Replaced all usages of tabs in C files with spaces by @hedara90 in #5261
  • Fixed missing breaks in two ENDTURN cases by @ghoulslash in #5350
  • ShouldSwitchIfWonderGuard cleanup by @Pawkkie in #5383

🧪 Test Runner 🧪

Added

  • Added missing Adaptability, Aerilate, Aftermath tests by @kittenchilly in #5242
  • Added missing Disguise tests by @hedara90 in #5249
  • Added some missing Instruct tests by @PhallenTree in #5262
  • Added missing Powder tests by @PhallenTree in #5370
  • Added ShouldSwitchIfWonderGuard AI tests by @Pawkkie in #5383

Changed

  • Moved ASSUMEs to inside GIVEN blocks to prevent them from being added correctly to the totals in the test summary by @AsparagusEduardo in #5308

📚 Documentation 📚

Fixed

  • Fixed test system documentation saying that make check TESTS="Spikes" could be done with single quotes instead of double quotes by @AsparagusEduardo in #5266

📦 Branch Synchronisation 📦

pret's base pokeemerald

  • N/A

merrp/aarant's followers

New Contributors

  • @laserXdolphin made their first contribution in #5273
  • @Liamjd14 made their first contribution in #5304

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.9.1...expansion/1.9.2

Version 1.9.1

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.9.1`.

🌋 REFACTORS 🌋

  • Removed ENDTURN_RETALIATE in #5182
  • Removed ENDTURN_WEATHER_FORM and allowedToChangeFormInWeather in #5171

🧬 General 🧬

Added

  • Move Relearner UI now displays move category by @kittenchilly in #5081

Fixed

  • Fixes wrong padding field in SpeciesInfo struct by @AlexOn1ine in #5139
  • Fixed specific tiles changing to PC tiles when using Box Link/Debug PC option by @cawtds in #5141

🐉 Pokémon 🐉

Fixed

  • Fixed stray transparent pixels in Urshifu sprites by @hedara90 in #5071
  • Fixed bufferspeciesname not working for species IDs over 1023 by @SBird1337 in #5088
  • Fixed overworld Pokémon breaking for species IDs above 1535 by @hedara90, @mrgriffin and @SarnPoke in #5179
  • Fixed overworld palettes for multiple species by @hedara90 in #5107
    • Dialga Origin (Normal and Shiny)
    • Palkia Origin (Normal and Shiny)
    • Giratina Origin (shiny palette fixed by @hedara90 in #5108)
    • Xerneas Neutral/Active (Normal and Shiny)
    • Enamorus Incarnate/Therian (Normal and Shiny)
  • Fixed/added missing Pokémon sprites and palettes by @Cafeei in #5126
    • Overworld:
      • Shiny Sneasler, Morelul, Bounsweet, Bruxish, Guzzlord, Regieleki, Zacian, Zamazenta
      • Hisuian Zorua sprite
      • Shiny Summer Sawsbuck
      • Shiny Galarian Yamask, Darumaka, Zigzagoon, Zapdos, Ponyta, Rapidash, Slowpoke, Farfetch'd, Weezing, Mr. Mime, Articuno, Moltres, Slowking, Stunfisk, Darmanitan
      • Shiny Hisuian Sneasel, Qwilfish, Samurott,
    • Battle sprites:
      • Shiny Sneasler, Cursola, Pincurchin, Runerigus
      • Shiny Galarian Yamask, Darumaka
  • Fixed Unown Overworld follower sprites by Sarn by @hedara90 in #5146

⚔️ Battle General ⚔️

Changed

  • Set new animation particles by default to off by @AlexOn1ine in #5161

Fixed

  • Fixed speed ties by @mrgriffin in #4780
    • Cleanup by @hedara90 in #5092
  • Fixed Defiant/Competitive not working after the battler enters the field with a Court Changed Sticky Web on its side of the field by @PhallenTree in #5093
  • Fixed trainerproc not properly parsing line markers, which caused erroring lines to be offset by @mrgriffin in #5122
  • Fixed initial Zigzagoon battle being able to use a Gimmick by @AlexOn1ine in #5129
  • Fixed incorrect rounding when maxHP is lower than 16 by @hedara90 in #5183
    • This caused these Pokémon to not be hurt by Sandstorm/Hail.
  • Fixes UB in Cmd_averagestats by @mrgriffin and @AlexOn1ine in #5191

🤹 Moves 🤹

Added

  • Added move animations for multiple moves by @TheTrueSadfish in #5159
    • Spin Out, Mortal Spin, Fillet Away, Flower Trick, Make It Rain, Shed Tail, Hyper Drill, Twin Beam, Comeuppance, Blood Moon, Fickle Beam, Thunder Clap, Hard Press, Dragon Cheer, Malignant Chain.
    • Purple chains by ogwon on Discord, beam by @TheTrueSadfish and livra on Discord.

Changed

  • Adjusted Raging Bull's animation to include Brick Break's wall break effect by @TheTrueSadfish in #5159

Fixed

  • Fixed non-grass Ivy Cudgel breaking battle UI by @hedara90 in #5117
  • Fixes Stomping Tantrum effect not doubling power in certain situations by @AlexOn1ine in #5140
  • Fixed Fickle Beam's description by @PhallenTree in #5093
  • Fixed Revelation Dance interactions with Z-Move, Roost and typeless mons by @PhallenTree in #5133
  • Fixes Poltergeist missing its accuracy check by @AlexOn1ine in #5168
  • Fixed Fickle Beam not showing its message by @TheTrueSadfish in #5159
  • Fixed Retaliate not working correctly if the party member fainted via passive damage during end of turn by @hedara90 in #5182
  • Fixed Flame Burst's passive damage being based off current HP rather than Max HP by @hedara90 in #5182
  • Fixed using Population Bomb with Loaded Dice printing garbage text by @kittenchilly in #5195

🎭 Abilities 🎭

Added

  • Added in-battle effect of Pickup by @PhallenTree in #5170

Fixed

  • Fixes Purifying Salt not halving damage for dynamic move types by @AlexOn1ine in #5145
  • Fixed Dancer-called moves not changing their type based on the new user by @PhallenTree in #5133
  • Fixed Ice Face not regenerating after switching in during Hail/Snow by @hedara90 in #5171
  • Fixed Wind Rider not activating when switched in while Tailwind is active on the user's side of the field activation and tests by @PhallenTree in https://github.com/rh-hideout/pokeemerald-expansion/pull/5207

🧶 Items 🧶

Added

  • Added Dowsing Machine's expanded name by @kittenchilly in #5134

Fixed

  • Fixes Booster Energy not increasing speed by @AlexOn1ine in #5167

🤖 Battle AI 🤖

Changed

  • Adjusted AI calculation for Triple Kick Effect by @AlexOn1ine in #5127

Fixed

  • Fix Switch AI Bug: AI never switching out when it could be OHKO'd by @Pawkkie in #5089
  • Adds missing AI checks for poltergeist by @AlexOn1ine in #5189

🧹 Other Cleanup 🧹

  • IsValidForBattle function formatting by @AlexOn1ine in #5085
  • Opportunist/Mirror Herb cleanup by @AlexOn1ine in #5120
    • Cleanup by @AlexOn1ine in #5158
  • Remove trailing whitespace (master) by @AsparagusEduardo in #5174

🧪 Test Runner 🧪

Added

Changed

  • Fixed G-Max Replenish not considering Gen 5+ Pickup by @PhallenTree in #5170

Fixed

  • Fixed RandomUniformExcept not being exclusive on the higher boundary by @PhallenTree in #5170

📚 Documentation 📚

  • Added guide to running documentation website locally by @AsparagusEduardo in #5059
  • How to docs and fixes to be added to the mdbook documentation site by @anrichtait in #5070
  • Improved 1.8 ⇒ 1.9 non-Competitive syntax migration instructions by @mrgriffin in #5079

📦 Branch Synchronisation 📦

pret's base pokeemerald

  • 5th of August in #5098
    • Fixed bottom half of Mt. Pyre not being labeled in PokeNav by @fdeblasio in pret#2018
  • 7th of August in #5116
    • Changed type1 and type2 to be consistent by @pkmnsnfrn in pret#2021
  • 14th of August in #5165
    • Fix type for offset in MapConnection by @GriffinRichards in pret#2011

merrp/aarant's followers

  • 7th of August in #5110
    • Fixed expanded OW IDs by @pkmnsnfrn in aarant#38
    • Fix two small text errors in follower dialogue by @Bassoonian in aarant#39

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.9.0...expansion/1.9.1

New Contributors

Version 1.9.0

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.9.0`.

🌋 REFACTORS 🌋

📜 = Has a migration script.

🧬 General 🧬

Added

Changed

Fixed

✨ Feature Branches ✨

merrp/aarant's Followers

Changes from the original branch

  • Follower Pokémon can be enabled using the OW_FOLLOWERS_ENABLED config.
  • Pokémon NPCs can be added to a map independently from the Followers config. Keep in mind, this requires OW_POKEMON_OBJECT_EVENTS to be enabled in order to add the graphics to the ROM.
  • Moved original configs from include/constants/event_objects.h to include/config/overworld.h and renamed them:
    • OW_MON_BOBBING -> OW_FOLLOWERS_BOBBING
    • LARGE_OW_SUPPORT -> OW_LARGE_OW_SUPPORT
    • OW_MON_POKEBALLS -> OW_FOLLOWERS_POKEBALLS
  • Added follower Pokémon sprites from Gen 4-8
    • Credits to @Eemeliri and Gerben BSc
  • Moved OW_GFX_COMPRESS from include/global.h to include/config/overworld.h.
  • Added OW_SUBSTITUTE_PLACEHOLDER config to hide followers if they don't have a follower sprite instead of using a placeholder Substitute sprite by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4765
  • Follower graphics are shown in the Pokémon Sprite Visualizer.
  • Follower graphics have been moved to the respective species graphics folder instead of all being grouped in graphics/object_events/pics/pokemon.
    • Eg: graphics/object_events/pics/pokemon/abomasnow.png was moved to graphics/pokemon/abomasnow/follower.png.
  • Follower data has been moved to gSpeciesInfo
    • Their palettes are separate from the battle sprite palettes
      • Can be enabled using OW_FOLLOWERS_SHARE_PALETTE, but requires manual sprite adjustements. For now, it's recommended to keep this FALSE. (They don't use much space anyway)
  • Updated GetTypeEffectiveness used by follower messages to account for new abilities with type immunities.
  • Cleaned up code style to match pret/expansion's.
  • Reverted Regi's sprite to vanilla's.
  • Follower PicTables use overworld_ascending_frames macro.
  • Removed unneeded calls to bufferspeciesname.
  • Removed MON_DATA_NATURE.
  • Removed debug functions like IsAccurateGBA.
  • Removed ScrCmd_callfunc to use callnatives instead

🐉 Pokémon 🐉

Added

Changed

⚔️ Battle General ⚔️

Added

Changed

Fixed

  • Fixed Dynamax data not clearing when a battler faints by @WillKolada in https://github.com/rh-hideout/pokeemerald-expansion/pull/4672
  • Multiple Z-Move fixes by @AgustinGDLV in https://github.com/rh-hideout/pokeemerald-expansion/pull/4449
    • Fixed Z-Moves not working in Link Battles, recorded battles and tests.
    • Fixed Z_EFFECT_ALL_STATS_UP functionality.
    • Fixed Z-Nature Power, Z-Copycat, Z-Me First, and Z-Sleep Talk incorrectly using a normal move and not a Z-Move, which had not been raised as an issue yet. (some of these were caught recently!)
    • Fixed Breakneck Blitz incorrectly being affected by -ate abilities, which had not been raised as an issue yet.
    • Fixed Instruct incorrectly not failing if the target last used a Z-Move, which had not been raised as an issue yet.
    • Fixed Guardian of Alola incorrectly doing 50% of the target's HP instead of 75%.
  • Fixed Enemy Data not clearing at the end of battles by @ghoulslash in https://github.com/rh-hideout/pokeemerald-expansion/pull/4867

🤹 Moves 🤹

Added

Changed

  • EFFECT_TRIPLE_KICK's base power uses the move's strikeCount instead of a constant by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4608
    • This allows users to create "Quadruple Kick" and similar moves.
  • Migrated move animation pointers to gMovesInfo by @cawtds in https://github.com/rh-hideout/pokeemerald-expansion/pull/4683
    • This removes the need for gBattleAnims_Moves which had potential ordering issues if not kept in line with move IDs.
      • If battleAnimScript is not defined for a move, it defaults to Tackle's animation.
    • Migration script available in migration_scripts/battle_anim_moves_refactor.py

Fixed

🎭 Abilities 🎭

Added

Changed

Fixed

🧶 Items 🧶

Added

Changed

🤖 Battle AI 🤖

Added

Changed

Fixed

🧹 Other Cleanup 🧹

Changed

🧪 Test Runner 🧪

Added

Changed

Fixed

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.8.6...expansion/1.9.0

Version 1.8.6

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.8.6`.

🌋 REFACTORS 🌋

🧬 General 🧬

Fixed

🐉 Pokémon 🐉

Added

Changed

Fixed

⚔️ Battle General ⚔️

Fixed

  • Fixed Debug Menu not properly updating the corresponding flags + general cleanup by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4643
  • Fixed spread damage in double battles by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/5064
    • In double battles when B_POSITION_OPPONENT_LEFT fainted from the first hit of a spread move, the second hit dealt 100% of damage to B_POSITION_OPPONENT_RIGHT.
  • Removed unused Battle Script labels by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4839

    • data/battle_scripts_1.s
      • BattleScript_OctolockTryLowerDef
      • BattleScript_OctolockTurnDmgPrintMsg
      • BattleScript_StuffCheeksEatBerry
      • BattleScript_StrengthSapTryHp and its contents.
      • BattleScript_PartingShotEnd and its contents.
      • BattleScript_RototillerAffected
      • BattleScript_AutotomizeDoAnim
      • BattleScript_TryTailwindAbilitiesLoop_Ret
      • BattleScript_HitFromAtkCanceler
      • BattleScript_EffectMultiHit
      • BattleScript_BattleScript_EffectParalyzeNoTypeCalc
      • BattleScript_EffectTeleportTryToRunAway
      • BattleScript_WindPowerActivates_Ret
      • BattleScript_CottonDownReturn
      • BattleScript_ActivateWeatherAbilities_Increment
      • BattleScript_IntimidateEnd
      • BattleScript_SupersweetSyrupEnd
      • BattleScript_ActivateTerrainEffects_Increment
      • BattleScript_GrassyTerrainHpChange
      • BattleScript_GrassyTerrainLoopEnd
      • BattleScript_AbilityNoSpecificStatLossPrint
      • BattleScript_ArenaNothingDecided
      • BattleScript_ExtremeEvoboostAtk
      • BattleScript_BerserkGeneRet_Anim
    • data/battle_scripts_2.s
      • BattleScript_PrintCaughtMonInfo
  • Opportunist/Mirror Herb Refactor by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4928

    • Multiple fixes:
      • Opportunist stacking multiple boosts in one turn from opposing mons.
      • Increasing the stat by the wrong amount when copying opposing boost (eg. +6 boosts).
      • Opportunist no longer has a message.
      • Opportunist only play animations once for every stat boost.
    • Changes:
      • switchInBattlerCounter replaces both switchInAbilitiesCounter and switchInItemsCounter.
      • Removed STRINGID_OPPORTUNISTCOPIED and sText_OpportunistCopied.
      • Altered TryDoEventsBeforeFirstTurn to use a state switch (gBattleStruct->eventsBeforeFirstTurnState)
        • FIRST_TURN_EVENTS_START.
        • FIRST_TURN_EVENTS_OVERWORLD_WEATHER.
        • FIRST_TURN_EVENTS_TERRAIN.
        • FIRST_TURN_EVENTS_STARTING_STATUS.
        • FIRST_TURN_EVENTS_TOTEM_BOOST.
        • FIRST_TURN_EVENTS_NEUTRALIZING_GAS.
        • FIRST_TURN_EVENTS_SWITCH_IN_ABILITIES.
        • FIRST_TURN_EVENTS_OPPORTUNIST_1.
        • FIRST_TURN_EVENTS_ITEM_EFFECTS.
        • FIRST_TURN_EVENTS_OPPORTUNIST_2.
        • FIRST_TURN_EVENTS_END.

🤹 Moves 🤹

Added

Fixed

🎭 Abilities 🎭

Fixed

🧶 Items 🧶

Fixed

🤖 Battle AI 🤖

Documentation

Fixed

🧹 Other Cleanup 🧹

Fixed

🧪 Test Runner 🧪

Added

Changed

Fixed

📦 Pret merges 📦

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.8.5...expansion/1.8.6

Version 1.8.5

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.8.5`.

🌋 IMPORTANT CHANGES AND REFACTORS 🌋

💥 Softlock/Crash fixes 💥

🧬 General 🧬

Added

Fixed

🐉 Pokémon 🐉

Changed

Fixed

⚔️ Battle General ⚔️

Fixed

🤹 Moves 🤹

Fixed

🎭 Abilities 🎭

Added

Fixed

🧶 Items 🧶

Changed

Fixed

🤖 Battle AI 🤖

Changed

Fixed

🧪 Test Runner 🧪

Added

Changed

  • Multiple changes by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4682
    • Renamed some Baton Pass-related tests so that their prefix becomes Baton Pass.
      • Eg. Aqua Ring can be Baton Passed becomes Baton Pass passes Aqua Ring's effect.
    • Expanded Belch's test to show that it doesn't need to eat a berry before each use of the move.
    • Unconfirmed interactions added to the specific files.
    • Split EFFECT_RAGING_BULL from EFFECT_BRICK_BREAK's file.
    • Moved Grassy Terrain/Earthquake test to Earthquake file.
  • Write new/Fix old tests for post-ko switch scenarios by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4856

Fixed

📦 Pret merges 📦

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.8.4...expansion/1.8.5

Version 1.8.4

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.8.4`.

🌋 IMPORTANT CHANGES 🌋

Reverted Guillotine feature branch by @AsparagusEduardo:

  • This was a runtime decapitalization branch introduced silently in version 1.8.0. We didn't properly announce as it was hastily merged and we were hoping that future versions of it would have fixed issues that we encountered after the merge. However, issues kept piling on and we felt the need to revert it as it was also silently breaking other features as well.
  • We will work on an alternate decapitalization option in the future.

💥 Softlock/Crash fixes 💥

🧬 General 🧬

Fixed

✨ Feature Branches ✨

TheXaman's Debug Menu:

Fixed

SBird/Karathan's Dynamic Multichoices:

Changed

TheXaman's HGSS Pokédex Plus:

Fixed

  • Multiple evolution page fixes by @Sneed69 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4514
    • Fixed duplicate icons when 2 or more evolution methods share the same target species (eg. Magnezone evolution via Thunder Stone or Electromagnetic field).
    • Fixed arrow not loading when there are multiple evolution methods to the same target species that hadn't been caught.
    • Fixed wonky D-Pad inconsistency when scrolling down.
    • Fixed regression from #3562 (1.7.0) that caused non-evolving Pokémon to not show their "X has no evolution" text.
      • Change from the original branch: The message no longer shows for Pokémon that are part of an evolutionary family, even if they don't evolve themselves (eg. Venusaur).
  • Added failsafe for Egg Groups without text defined by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4628

ghoulslash's Saveblock Cleansing:

Changed

🐉 Pokémon 🐉

Added

Changed

Fixed

⚔️ Battle General ⚔️

Fixed

🤹 Moves 🤹

Changed

Fixed

🎭 Abilities 🎭

Fixed

🧶 Items 🧶

Added

Fixed

🤖 Battle AI 🤖

Changed

Fixed

🧹 Other Cleanup 🧹

Fixed

🧪 Test Runner 🧪

Added

Fixed

📦 Pret merges 📦

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.8.3...expansion/1.8.4

Version 1.8.3

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.8.3`.

💥 Softlock/Crash fixes 💥

🧬 General 🧬

Changed

Fixed

✨ Feature Branches ✨

TheXaman's HGSS Pokédex Plus:

Fixed

🐉 Pokémon 🐉

Changed

Fixed

⚔️ Battle General ⚔️

Changed

Fixed

🤹 Moves 🤹

Fixed

🎭 Abilities 🎭

Changed

Fixed

🧶 Items 🧶

Fixed

🤖 Battle AI 🤖

Changed

🧪 Test Runner 🧪

Added

Changed

Fixed

  • Fixed AI test error messages by @mrgriffin in https://github.com/rh-hideout/pokeemerald-expansion/pull/4404
    • The error messages for EXPECT_MOVE and EXPECT_SWITCH were backwards, saying, e.g. Expected MOVE, got SWITCH when it should say Expected SWITCH, got MOVE.
  • Fixed typos in Embody Aspect tests by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4439
  • Fixed Battle Test organization by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4504
    • Fixed Cud Chew's file being in the move effect folder.
    • Fixed Cud Chew's file name and test names (Cud Chuw).
    • Fixed Gastro Acid and Role Play's files being in the ability folder.
    • Moved Recoil file to move flags folder.
    • Renamed White Herb's file to restore_stats.c.
    • Renamed Techno Blast's file to change_type_on_item.c.
    • semi_invulnerable_moves.c to semi_invulnerable.c.
    • two_turn_moves.c to two_turns_attack.c.
    • Combined Burn Up/Double Shock to a single file fail_if_not_arg_type.c
    • Added Spit Up/Swallow files that point Stockpile's file.
    • Moved secondary effect files to their own folder.
    • Combinations of secondary effects moved to their own folder
    • Split hit_set_entry_hazards.c to separate files for Spikes/Stealth Rock.
    • Grouped Hex/Venoshock to the same file double_power_on_arg_status.c

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.8.2...expansion/1.8.3

Version 1.8.2

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.8.2`.

🌋 Important changes 🌋

  • Added check during compilation to make sure that the code is inside a git directory before building by @SBird1337 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4363
    • If you downloaded the code using the "Download ZIP" option on GitHub, you will not be able to update your expansion version or merge any other feature brances, which is why we added this check to make sure that users don't fall into this trap.

💥 Softlock/Crash fixes 💥

🧬 General 🧬

Changed

Fixed

✨ Feature Branches ✨

TheXaman's Debug Menu:

Fixed

TheXaman's HGSS Pokédex Plus:

Changed

Fixed

🐉 Pokémon 🐉

Added

Changed

Fixed

⚔️ Battle General ⚔️

Fixed

🤹 Moves 🤹

Fixed

🎭 Abilities 🎭

Added

Fixed

🧶 Items 🧶

Fixed

🤖 Battle AI 🤖

Fixed

🧹 Other Cleanup 🧹

Added

Fixed

🧪 Test Runner 🧪

Added

📦 Pret merges 📦

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.8.1...expansion/1.8.2

Version 1.8.1

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.8.1`.

🌋 IMPORTANT CHANGES 🌋

🧬 General 🧬

Added

Changed

Fixed

🧹 Other Cleanup 🧹

Changed

Fixed

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.8.0...expansion/1.8.1

Version 1.8.0

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.8.0`.

🌋 IMPORTANT CHANGES 🌋

🧬 General 🧬

Added

Changed

Fixed

✨ Feature Branches ✨

Incorporated @SBird1337's Dynamic Multichoices by @SBird1337 in https://github.com/rh-hideout/pokeemerald-expansion/pull/3826

  • This allows to set up custom multichoices much easier!
  • Allows you to control what options appear based custom conditions (such as them being based on what items you have currently, or even completely at random!).
  • Event callbacks can be added as well, to fully customize what happens with your multichoices.
    • Included there's DYN_MULTICHOICE_CB_SHOW_ITEM, which shows icons of the items defined by your script.
  • Compatible with Poryscript.
  • For more information and how to use it, please visit the Pokécommunity thread.

Incorporated @ghoulslash's Saveblock Cleansing branch by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4113

  • Differences from the the standalone branch:
    • Moved configs to dedicated file (include/config/save.h).
      • Fixed comments to the proper amount of space saved.
    • Added FREE_MYSTERY_GIFT, saving 876 bytes in SaveBlock1.
    • Converted #ifndef configs to the config format the rest of expansion uses
    • Cleaned up the code and fixed to work on modern.

TheXaman's Debug Menu:

Added

Changed

Fixed

🐉 Pokémon 🐉

Added

Changed

⚔️ Battle General ⚔️

Added

Changed

Fixed

🤹 Moves 🤹

Added

Changed

Fixed

🎭 Abilities 🎭

Added

Changed

Fixed

🧶 Items 🧶

Added

Changed

Fixed

🤖 Battle AI 🤖

Added

Changed

Fixed

🧹 Other Cleanup 🧹

🧪 Test Runner 🧪

Added

Changed

Fixed

📦 Pret merges 📦

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.7.4...expansion/1.8.0

Version 1.7.4

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.Y.Z`.

🧬 General 🧬

Fixed

🐉 Pokémon 🐉

Fixed

⚔️ Battle General ⚔️

Fixed

🤹 Moves 🤹

Fixed

🎭 Abilities 🎭

Added

Fixed

🧶 Items 🧶

Fixed

🧹 Other Cleanup 🧹

Changed

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.7.3...expansion/1.7.4

Version 1.7.3

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.7.3`.

🌋 IMPORTANT CHANGES 🌋

🧬 General 🧬

🐉 Pokémon 🐉

Changed

Fixed

⚔️ Battle General ⚔️

Fixed

🤹 Moves 🤹

Changed

Fixed

🎭 Abilities 🎭

Fixed

🧶 Items 🧶

Fixed

🤖 Battle AI 🤖

Fixed

🧪 Test Runner 🧪

Added

Changed

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.7.2...expansion/1.7.3

Version 1.7.2

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.7.2`.

🧬 General 🧬

Changed

Fixed

🐉 Pokémon 🐉

Added

Changed

  • Multiple Pokémon graphical improvements by @katykat5099 in https://github.com/rh-hideout/pokeemerald-expansion/pull/3805
    • Improved Tyrantrum's back sprite.
    • Improved Zigzagoon and Linoone's palette
    • Updated Gen 9 Pokémon icons to @CyanSMP64's improvements
      • Annihilape
      • Arctibax and Baxcalibur
      • Bellibolt
      • Bramblin and Brambleghast
      • Cetoddle and Cetitan
      • Charcadet, Armarouge and Ceruledge.
      • Chi-Yu and Chien-Pao
      • Clodsire
      • Crocalor
      • Dolliv
      • Dudunsparce
      • Esparthra

Fixed

⚔️ Battle General ⚔️

Changed

Fixed

🤹 Moves 🤹

Fixed

🧶 Items 🧶

Fixed

🤖 Battle AI 🤖

Fixed

🧹 Other Cleanup 🧹

Fixed

🧪 Test Runner 🧪

Added

Changed

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.7.1...expansion/1.7.2

Version 1.7.1

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.7.1`.

🧬 General 🧬

Changed

Fixed

🐉 Pokémon 🐉

Fixed

🤹 Moves 🤹

Added

Fixed

🎭 Abilities 🎭

Fixed

🧶 Items 🧶

Fixed

🧹 Cleanup 🧹

🧪 Test Runner 🧪

Added

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.7.0...expansion/1.7.1

Version 1.7.0

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.7.0`.

🌋 IMPORTANT CHANGES 🌋

The Expansion now uses Modern compilers by default

🧬 General 🧬

Added

Changed

Fixed

🐉 Pokémon 🐉

Added

Changed

  • Species Simplifier™ (Parts 1, 2 and 3)
    • Moved most data from multiple arrays to new fields in gSpeciesInfo
      • gSpeciesNames array -> speciesName field.
      • gLevelUpLearnsets array -> levelUpLearnset field.
      • gTeachableLearnsets array -> teachableLearnset field.
      • gEvolutionTable array -> evolutions field.
      • gFormSpeciesIdTables array -> formSpeciesIdTable field.
      • gFormChangeTablePointers array -> formChangeTable field.
      • Refactored Cry tables to simplify the process of adding new cries.
        • Now there's a single entry in the table per cry, and species are asigned cry IDs in gSpeciesInfo's cryId field.
      • Graphical data now have their pointers in gSpeciesInfo (Sprites, palettes and animations)
        • Removed all files in src/data/pokemon_graphics/ with the exception of front_pic_anims.h.
          • gMonBackPicCoords array -> backPicSize field.
          • gMonBackPicTable array -> backPic field.
            • gMonBackPicTableFemale array -> backPicFemale field.
          • gMonFrontPicCoords array -> frontPicSize field.
          • gMonFrontPicTable array -> frontPic field.
            • gMonFrontPicTableFemale array -> frontPicFemale field.
          • gMonPaletteTableFemale array -> palette field.
            • gMonPaletteTableFemale array -> paletteFemale field.
          • gMonShinyPaletteTable array -> shinyPalette field.
            • gMonShinyPaletteTableFemale array -> shinyPaletteFemale field.
          • gEnemyMonElevation array -> enemyMonElevation field.
          • gMonIconPaletteIndices array -> iconPalIndex field.
            • gMonIconPaletteIndicesFemale array -> iconPalIndexFemale field.
        • Removed unused 2nd animations.
      • Dex Entries
        • Individual form information is visible in the HGSS Pokédex (Vanilla Dex TBD)
        • National Dex numbers are assigned to the species in gSpeciesInfo, removing the need of sSpeciesToHoennPokedexNum and sSpeciesToNationalPokedexNum arrays.
        • Height and Weight are now saved separately per form, so weight in battle is now more accurate.
          • Pokédex size page proportions are also separate.
        • Pokédex descriptions are now saved as compound strings in gSpeciesInfo and differ by form. Shared entries are at the top of src/data/pokemon/species_info.h.
    • Added toggles to disable specific family groups of species
      • Located in include/config/species_enabled.h.
        • Moved the original P_GEN_x_POKEMON configs to this file.
      • Options to disable groups of species:
        • Battle-gimmick forms (Megas, Primals, etc.)
        • Regional Forms (Includes evolutions of those species, such as Sirfetch'd)
        • Pikachu extra forms.
        • Cross-Generation Evolutions
          • Also added separate option to add cross-evolutions to the Regional Dex.
      • Generation 1-3 families can now be disabled.
      • Pokémon will not evolve into species that have been disabled.
      • Pokémon will produce offspring of species that have been disabled.
      • Pokémon will not change form into forms that have been disabled.
      • Fixes and cleanup
    • Grouped data by species family.
    • Converted species flags to gcc flags.
    • Converted P_UPDATED_STATS "ifdef blocks" to ternaries.
    • Added Mega Evolution hidden ability failsafes.
    • Separated gSpeciesInfo by Generation by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/3729
    • Fixes and cleanup
  • Updated P_UPDATED_ABILITIES Gen 9's config for Piplup's line, Gallade and Shiftry by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/3353
    • Shiftry's 2nd regular ability was changed from Early Bird to Wind Rider.
    • Piplup, Prinplup and Empoleon's Hidden abilities were changed from Defiant to Competitive.
    • Gallade was given Sharpness as a 2nd regular ability.
  • Updated Legends Arceus Pokémon data and Level Up learnsets to Scarlet/Violet's by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/3458
  • Applied missing uses of PLACEHOLDER_ANIM_SINGLE_FRAME by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/3562
  • Renamed PLACEHOLDER_TWO_FRAME_ANIMATION to PLACEHOLDER_ANIM_TWO_FRAMES by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/3562
  • Removed Old Unown Level Up Learnsets by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/3562
  • Added FORM_CHANGE_TIME_OF_DAY form change that automatically changes Form during times of day (used by Shaymin to revert to Land Forme at night) by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/1690
  • Added FORM_CHANGE_STATUS form change that triggers when they adquire status condition (used by Shaymin to revert to Land Forme when frozen) by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/3734
    • Currently it doesn't:
      • Prevent Shaymin from changing into Sky Forme when frozen.
      • Change Form in the Battle Pike.
  • Evolutions now call for GetTimeOfDay instead of checking the times directly by @fdeblasio in https://github.com/rh-hideout/pokeemerald-expansion/pull/3369

Fixed

⚔️ Battle General ⚔️

Added

Changed

Fixed

🤹 Moves 🤹

Added

Changed

Fixed

🎭 Abilities 🎭

Added

Changed

Fixed

🧶 Items 🧶

Added

Changed

Fixed

🤖 Battle AI 🤖

  • Added AI Tests
    • By @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/3361
      • AI prefers Bubble over Water Gun if it's slower (both are power 40).
      • AI prefers Water Gun over Bubble if it knows that foe has Contrary.
      • AI prefers moves with better accuracy, but only if they both require the same number of hits to KO.
      • AI prefers moves which deal more damage instead of moves which are super-effective but deal less damage.
      • AI prefers Earthquake over Drill Run if both require the same number of hits to KO.
      • AI chooses the safest option to faint the target, taking into account accuracy and move effect.
      • AI won't use ground type attacks against flying type Pokémon unless Gravity is in effect.
      • AI will not switch in a Pokémon which is slower and gets 1HKOed after fainting.
      • AI switches if Perish Song is about to kill.
      • AI won't use a Weather changing move if partner already chose such move.
      • AI will not use Helping Hand if partner does not have any damage moves.
      • AI will not use a status move if partner already chose Helping Hand.
      • AI without any flags chooses moves at random.
    • By @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/3382
      • AI prefers a weaker moves over one with a downside effect if both require the same number of hits to KO.
      • AI prefers moves with the best possible score, chosen randomly if tied.
      • AI can choose a status move that boosts the attack by two.
    • By @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/3515
      • AI won't use Solar Beam if there is no Sun up or the user is not holding Power Herb.
    • By @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/3583
      • AI chooses moves with secondary effect that have a 100% chance to trigger
    • Cleanup by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/3748
  • Smarter SwitchAI Mon Choices + HasBadOdds Switch Check by @Pawkkie in https://github.com/rh-hideout/pokeemerald-expansion/pull/3253
    • Use AI_FLAG_SMART_MON_CHOICES to enable smarter decisions when choosing which mon gets sent out
    • HasBadOdds expands AI_FLAG_SMART_SWITCHING to switch out in cases where a mon has a bad matchup and lots of HP remaining
  • Extend AI_FLAG_SMART_SWITCHING for Encore / hazards / lowered attacking stats + Tests by @Pawkkie in https://github.com/rh-hideout/pokeemerald-expansion/pull/3557

Changed

Fixed

🧹 Other Cleanup 🧹

🧪 Test Runner 🧪

Added

Changed

Fixed

📦 Pret merges 📦

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.6.2...expansion/1.7.0

Version 1.6.2

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.6.2`.

🌋 IMPORTANT CHANGES 🌋

Battle changes

  • Battler Types are now obtained via GetBattlerType instead of gBattleMons[battlerId].type1/2/3 to better consider Roost. Be sure to update your custom battle effects to account for this change.

🧬 General 🧬

Fixed

🐉 Pokémon 🐉

Changed

Fixed

🤹 Moves 🤹

Changed

Fixed

🎭 Abilities 🎭

Changed

Fixed

🧶 Items 🧶

Fixed

🧹 Cleanup 🧹

🧪 Test Runner 🧪

Added

Changed

Fixed

New Contributors

Full Changelog

https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.6.1...expansion/1.6.2

Version 1.6.1

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.6.1`.

CRITICAL FIX, please update to avoid the issues detailed down below:

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.6.0...expansion/1.6.1

Version 1.6.0

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.6.0`.

Added

General

Item Effects

Move Effects

Move Animations

Graphics

Changed

General

Graphical changes

Refactors

Move animations

Battle AI

Cleanup

Fixed

Softlocks

General

Graphics

Battle Mechanics

Test Runner

General

Tests added for:

Pret merges:

  • Pret merge (2023/08/31) by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/3268
    • VAR_TEMP aliases
    • TM and HM item constants by their move name without numbers.
    • Stat change documentation.
    • Factory Menu + Dome Tourney documentation.
    • Berry Fix Documentation.
    • Missing uses of DISPLAY_WIDTH and DISPLAY_HEIGHT.
    • Static assertion for Battle Palace Flags.
    • Static assertion for Rotating Gates.
    • Automatic increase of TEXT_BUFF_ARRAY_COUNT and POKEMON_NAME_BUFFER_SIZE.
    • Proper bravoTrainerTower documentation.
    • Birth Island Rock documentation.
    • 6 new bugfixes.
  • Pret merge (2023/09/26) by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/3347
    • Add include guards for assembly constants files
    • Add blockBoxRS field to BoxPokemon struct
    • Bugfix for abilities affecting wild encounter tables
      • Fixes a potential buffer overread in TryGetAbilityInfluencedWildMonIndex. The bug can occur if an electric type mon is in the first slots of a fishing encounter table and the player carries a mon with the ABILITY_STATIC ability. This never happens in the vanilla codebase.
    • Add missing constant usage in m4a_1

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.5.3...expansion/1.6.0

Version 1.5.3

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.5.3`.

CRITICAL FIX, please update to avoid the issues detailed down below:

  • Fixed memory corruption when handling trigger sprites by @SBird1337 in https://github.com/rh-hideout/pokeemerald-expansion/pull/3238
    • This had the posibility of manifesting in weird ways, like camera and music changes, NPC duplication and more. If you've had this issue in the past, we heavily recommend you update to this version of the expansion.
    • Thank you @Bassoonian for helping us pinpointing the issue.

image image

Fixed

Battle Mechanics

Test Runner

Changed

Fixed

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.5.2...expansion/1.5.3

Version 1.5.2

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.5.2`.

Changed

General

Refactors

Fixed

Softlocks

Graphics

Battle Mechanics

Battle AI

Pret merges:

Test Runner

Changed

Fixed

Tests added for:

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.5.1...expansion/1.5.2

Version 1.5.1

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.5.1`.

Changed

General

Fixed

General

Softlocks

Graphics

Battle Mechanics

Battle AI

Battle Tests

Added

Changed

  • Minor test changes by @mrgriffin in https://github.com/rh-hideout/pokeemerald-expansion/pull/3072
    • Regularizes the whitespace.
    • Removes unnecessary ;s after }s.
    • Parametrizes item_effect_restore_hp.c and uses I_HEALTH_RECOVERY everywhere.
    • Inlines uses of macros where I think it makes the test easier to follow.
    • Use 3-arg PASSES_RANDOMLY in the Snow + Blizzard test (improves performance).
    • More conservative unlink error reporting. Ctrl-C in make check should not complain about being unable to unlink ROMs which weren't created yet.
    • Better names for the ROMs in /tmp.
    • Prints the test runner number in Hydra, making it easier to track down bugs involving state leaking from a test to the following tests.
    • Simplify TO_DO_BATTLE_TEST's implementation.
    • Introduce a TearDownBattle function which was repeated twice.

Fixed

Latest pret commit:

https://github.com/pret/pokeemerald/commit/9c4a59f865360b7d6e0dede0e52812b897526588

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.5.0...expansion/1.5.1

Version 1.5.0

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.5.0`.

Added

General

Battle General

Move Effects

Item Effects

Battle AI

Tests

Changed

General

Graphical changes

Refactors

Cleanup

Fixed

General

Graphics

Battle Mechanics

Battle Animations

Battle AI

Pret merges

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.4.3...expansion/1.5.0

Version 1.4.3

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.4.3`.

Changed

Cleanup

Fixed

General

Battle Mechanics

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.4.2...expansion/1.4.3

Version 1.4.2

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.4.2`.

Fixed

General

Battle Mechanics

Battle AI

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.4.1...expansion/1.4.2

Version 1.4.1

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.4.1`.

CRITICAL FIX, please update to avoid the issues detailed down below:

  • Fixed electricity move animations causing softlocks with weird graphical results by @DizzyEggg in #2785

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.4.0...expansion/1.4.1

Version 1.4.0

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.4.0`.

Added

General

Moves

  • Generation IX Moves
    • Base move data by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/2467
    • New moves with existing effects:
      • Lumina Crash, Jet Punch, Ice Spinner, Triple Dive, Kowtow Cleave, Flower Trick, Torch Song, Aqua Step, Ruination, Pounce, Trailblaze, Chilling Water, Hyper Drill, Twin Beam, Armor Cannon, Bitter Blade, Comeuppance, Aqua Cutter, Blazing Torque, Noxious Torque, Combat Torque, Magical Torque
    • New move effects:
    • Missing move effects:
      • Tera Blast, Axe Kick, Last Respects, Order Up, Spicy Extract, Spin Out, Population Bomb, Glaive Rush, Revival Blessing, Salt Cure, Mortal Spin, Doodle, Fillet Away, Raging Bull, Make It Rain, Collision Course, Electro Drift, Shed Tail, Chilly Reception, Tidy Up, Snowscape, Rage Fist, Gigaton Hammer
  • Updated Gen 1-8 move effects to Gen 9 standards: by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/2467
    • LA moves with existing effects
      • Psyshield Bash, Raging Fury, Wave Crash, Chloroblast, Mountain Gale, Headlong Rush, Esper Wing, Shelter, Bitter Malice, Power Shift, Springtide Storm, Bleakwind Storm, Wildbolt Storm, Sandsear Storm
    • LA moves with new move effects
      • Mystical Power (EFFECT_SPECIAL_ATTACK_UP_HIT)
      • Victory Dance (EFFECT_VICTORY_DANCE)
    • PP adjustments.
      • (5 -> 10) Bleakwind Storm, Wildbolt Storm, Sandsear Storm
      • (10 -> 5) Recover, Soft-Boiled, Rest, Milk Drink, Slack Off, Roost, Shore Up
    • Power adjustments
      • (50 -> 90) Triple Arrows
      • (90 -> 120) Raging Fury
      • (120 -> 150) Wave Crash
      • (60 -> 80) Dire Claw
      • (100 -> 120) Headlong Rush
      • (60 -> 75) Bitter Malice
      • (75 -> 80) Esper Wing
      • (95 -> 100) Springtide Storm, Bleakwind Storm, Wildbolt Storm, Sandsear Storm
      • (80 -> 75) Wicked Blow
      • (70 -> 60) Grassy Glide
      • (130 -> 120) Glacial Lance
  • Implementing Teatime effect by @SonikkuA-DatH in https://github.com/rh-hideout/pokeemerald-expansion/pull/1956
  • Config for Gen 4's Roost pure-Flying behavior (B_ROOST_PURE_FLYING) by @mrgriffin in https://github.com/rh-hideout/pokeemerald-expansion/pull/2530
  • Config for Gen 8 Howl's effect by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/2700

Abilities

  • Generation IX Abilities
    • 28 of 31 abilities implemented by @LOuroboros in https://github.com/rh-hideout/pokeemerald-expansion/pull/2470
      • Lingering Aroma, Seed Sower, Thermal Exchange, Anger Shell, Purifying Salt, Well-Baked Body, Wind Rider, Rocky Payload, Wind Power, Electromorphosis, Protosynthesis, Quark Drive, Good as Gold, Vessel of Ruin, Sword of Ruin, Tablets of Ruin, Beads of Ruin, Orichalcum Pulse, Hadron Engine, Cud Chew, Sharpness, Supreme Overlord, Costar, Toxic Debris, Armor Tail, Earth Eater, Guard Dog and Mycelium Might
    • Not implemented yet:
      • Commander
      • Opportunist
      • Zero to Hero
  • Implemented Ice Face by @LOuroboros in https://github.com/rh-hideout/pokeemerald-expansion/pull/2255

Items

Changed

Graphical changes

Refactors

Cleanup

Other

Fixed

Softlocks

Graphics

Battle Mechanics

Move Animations

Battle AI

Other

Pret merges

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.3.0...expansion/1.4.0

Version 1.3.0

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.3.0`.

Added

Changed

Fixed

Pret merges

Cleanup

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.2.0...expansion/1.3.0

Version 1.2.0

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.2.0`.

Added

Changed

Fixed

Cleanup

Pret merges

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.1.1...expansion/1.2.0

Added

Changed

Fixed

Pret merges

Cleanup

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.1.0...expansion/1.2.0

Version 1.1.1

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.1.1`.

What's Changed

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.1.0...expansion/1.1.1

Version 1.1.0

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.1.0`.

Added

Changed

Fixed

Pret merges

Cleanup

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.0.0...expansion/1.1.0

Version 1.0.0

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/1.0.0`.

BREAKING

ADDED

CHANGED

FIXES

PRET MERGES

CLEANUP

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/0.9.0...expansion/1.0.0

Version 0.9.0

## How to update
- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`.
- Once you have your remote set up, run the command `git pull RHH expansion/0.9.0`.

This version was labeled retroactively after our versioning scheme was decided, meaning the version number may be arbitrary.

ADDED

CHANGED

FIXES

PRET MERGES

CLEANUP

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/0.8.0...expansion/0.9.0

BREAKING

ADDED

CHANGED

FIXES

PRET MERGES

CLEANUP

New Contributors

Full Changelog: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/0.9.0...expansion/1.0.0

How to make an Expansion version

If you need instructions on how to release a new Expansion version, check out the enclosed instruction book. - AsparagusEduardo

1.- Autogenerating a changelog for the master branch.

Requires Write access to the repo.

If the changelog you're making is for a minor version (Eg. 1.3.0), make sure to sync the upcoming branch with master before starting. Keep in mind that if there are unreleased changes in master, they should be made into a patch version released alongside minor version.

  • Go to https://github.com/rh-hideout/pokeemerald-expansion/releases.
  • Press the option "Draft a new release".
  • Fill the following fields:
    • In "Choose a tag", write the name of the tag for the desired release number.
      • Eg. for version 1.2.3, write expansion/1.2.3. It cannot be an existing tag.
    • In "Target" choose master.
    • In "Previous tag", select the latest version released.
  • Press "Generate release notes". This will fill the description with a list of PRs made to the master branch since the lastest release.
  • You may copy the contents to your editor of choice for step 2.
  • If the changelog you're making is for a minor version (Eg. 1.3.0), proceed to step 1B, without closing the current window.

1B.- Autogenerating a changelog for the upcoming branch.

  • Delete the text and title generated in the site.
  • Change "Target" to upcoming.
  • Press "Generate release notes". This will autogenerate the description with changes from both branches, as they've been merged.
  • Use a comparison tool to remove any shared PRs from master. (I use WinMerge for this).
    • Also remove any shared "New Contributor" entries.
  • You should now have a text that only contains PRs made to upcoming.

2.- Sorting PRs in the changelog

  • Copy the docs/changelogs/template.md file into a new file with the corresponding version for file and folder. Eg, for 1.2.3, you copy the file into docs/changelogs/1.2.x/1.2.3.md.
  • Use the following Regex to adapt all PR links into the format currently used:
    • Search
      in https://github\.com/rh-hideout/pokeemerald-expansion/pull/(\d+)
      
    • Replace
      in [#$1](https://github.com/rh-hideout/pokeemerald-expansion/pull/$1)
      
  • At the bottom of the template, replace the "____" in <!--Last PR: ____--> with the number of the last PR in your autogenerated changelog. This will help you keep track of any new PRs that are merged in before you're finished with your changelog.
  • Sort the PRs by the categories present in the PR taking the following considerations:
    • Keep the language of the changelog as newbie friendly as possible, explaining how the PR affects them.
    • The format of the main line is the following:
      {{Description of the change}} by @{{GitHub user}} in {{PR link}}
      
    • If a PR reverted by a different PR, both should be removed.
      • If the reverted PR's contents are then re-added it should be treated as if it's the first time added, with all the proper documentation needed.
    • If a PR's content fits into multiple categories, like fixing two unrelated Ability and Move effects, you may duplicate the PR line and put it in both categories, changing the description of it accordingly.
    • When refering to an element present in the code, always enclose them with ` to easily tell them apart. Eg:
      • "Converted settotemboost command to callnative"
      • "Changed MOVEEND_ defines to an enum"
    • A PR's technical changes that may conflict with user changes should have an entry in the "REFACTORS" section at the beginning of the changelog. Any of these changes that requires a migration script should also be marked as such by appending the corresponding emoji listed there. The section should include, but it's not limited to:
      • Massive data format and/or structuring changes.
      • Converting defines to enums.
      • Renaming structs and/or their fields.
      • Removing enums/defines/structs.
    • For fixes:
      • Once on their respective categories, if a PR fixes multiple issues at once in that category, you may make a sublist of those issues to properly detail each one and avoid just writing "Fixed multiple ability issues" or an overly verbose sentence for the PR.
      • Try to fit the contents of a PR as a single sentence in past tense that.
      • DON'T write something like "Fixed Parental Bond", as this doesn't explain what was fixed in Parental Bond. Instead, write something like "Fixed Parental Bond not affecting Snore".
    • For new configs:
      • Add the name of the config as part of the sentence, with subitems explaining what they do based on their generation if it's a generational config. Eg:
        * Added missing `B_AFTER_YOU_TURN_ORDER` config by @PhallenTree in [#5400](https://github.com/rh-hideout/pokeemerald-expansion/pull/5400)
            * Gen 5-7: After You fails if the order remains the same after using After You.
            * Gen 8+: After You no longer fails if the turn order remains the same after using After You.
        
    • For other new features:
      • Explain how to use the feature completely, listing all its features as needed. For now, this is the only source of documentation for these features, so it's important to be thorough.

3.- Preparing the code for the new version

  • Search for all non-changelog instances of the previous released version and update them to the newer version. As of writing, it's these files:
    • README.md:
      -Based off RHH's pokeemerald-expansion 1.2.2 https://github.com/rh-hideout/pokeemerald-expansion/
      +Based off RHH's pokeemerald-expansion 1.2.3 https://github.com/rh-hideout/pokeemerald-expansion/
      
      -- Once you have your remote set up, run the command `git pull RHH expansion/X.Y.Z`, replacing X, Y and Z with the digits of the respective version you want to update to (eg, to update to 1.2.2, use `git pull RHH expansion/1.2.2`).
      +- Once you have your remote set up, run the command `git pull RHH expansion/X.Y.Z`, replacing X, Y and Z with the digits of the respective version you want to update to (eg, to update to 1.2.3, use `git pull RHH expansion/1.2.3`).
      
    • .github/ISSUE_TEMPLATE/*.yaml
      • Patch version:
        options:
        -   1.2.2 (Latest release)
        +   1.2.3 (Latest release)
            master (default, unreleased bugfixes)
            upcoming (Edge)
        +   1.2.2
            1.2.1
            1.2.0
        
      • Minor Version:
        options:
        -   1.2.2 (Latest release)
        +   1.2.3 (Latest release)
            master (default, unreleased bugfixes)
            upcoming (Edge)
        +   1.2.2
            1.2.1
            1.2.0
        -   1.1.6
        -   1.1.5
        -   1.1.4
        -   1.1.3
        -   1.1.2
        -   1.1.1
        -   1.1.0
        -   pre-1.1.0
        +   pre-1.2.0
        
    • docs/SUMMARY.md:
      • Patch version:
        [Changelog](./CHANGELOG.md)
            - [1.2.x]()
        +       - [Version 1.2.3](changelogs/1.2.x/1.2.3.md)
                - [Version 1.2.2](changelogs/1.2.x/1.2.2.md)
                - [Version 1.2.1](changelogs/1.2.x/1.2.1.md)
                - [Version 1.2.0](changelogs/1.2.x/1.2.0.md)
        
      • Minor version:
        [Changelog](./CHANGELOG.md)
        +   - [1.3.x]()
        +       - [Version 1.3.0](changelogs/1.3.x/1.3.0.md)
            - [1.2.x]()
                - [Version 1.2.2](changelogs/1.2.x/1.2.2.md)
                - [Version 1.2.1](changelogs/1.2.x/1.2.1.md)
                - [Version 1.2.0](changelogs/1.2.x/1.2.0.md)
        
    • include/constants/expansion.h:
      • The defines should already have the version we're targetting as part of the previous changelog process.
        -// Last version: 1.2.2
        +// Last version: 1.2.3
            #define EXPANSION_VERSION_MAJOR 1
            #define EXPANSION_VERSION_MINOR 2
            #define EXPANSION_VERSION_PATCH 3
            
            // FALSE if this this version of Expansion is not a tagged commit, i.e.
            // it contains unreleased changes.
        -#define EXPANSION_TAGGED_RELEASE FALSE
        +#define EXPANSION_TAGGED_RELEASE TRUE
        

4.- Make a PR with the changes listed in Steps 2/3.

  • Also post the changelog for feedback from contributors and maintainers.
  • Once all feedback has been addresses, you may merge.

5.- Create the release on GitHub

  • Copy the contents of your manual changelog into the description of your release.
  • Modify the title to "Version {{version}}", eg. "Version 1.2.3"
  • Be sure to have the "Set as the latest release" option set,
  • Press "Publish Release".
  • This will create a new tag in the repo that users can pull from like they can with branches.

6.- Post-release handling

  • DON'T ACCEPT ANY NEW PRs YET. We have to prepare for the next cycle first.
  • Go to include/constants/expansion.h and merge the following changes to the repo (PR or direct commit, doesn't matter much):
    • Patch version:
       // Last version: 1.2.3
          #define EXPANSION_VERSION_MAJOR 1
          #define EXPANSION_VERSION_MINOR 2
      -   #define EXPANSION_VERSION_PATCH 3
      +   #define EXPANSION_VERSION_PATCH 4
          
          // FALSE if this this version of Expansion is not a tagged commit, i.e.
          // it contains unreleased changes.
      -#define EXPANSION_TAGGED_RELEASE TRUE
      +#define EXPANSION_TAGGED_RELEASE FALSE
      

With this, the repo is ready again to receive new PRs.

Now you're ready to make the announcements!

  • We tend to post on the following Discord Servers:

You can make a highlight of changes in the announcement itself, but it's not needed. (I also like using Discord emotes to highlight certain features during announcements, but gain, it's not needed).

Document Purpose

This document is a guide for contributors and Senate to decide if a feature is within "scope" for pokeemerald-expansion. If a feature is not in scope, then it should not be merged. Even if an opened PR is within scope, this does not mean it will be merged, as acceptance criteria will often come down to the details of the implementation.

Definitions

  • Showdown Supported (SS): A core series game who's metagame can be played on Showdown.
  • Base Expansion Version: "A .gba file built from an unmodified master or upcoming branch of pokeemerald-expansion.
  • Vanilla Emerald Version: A .gba file built from an unmodified master branch of pret's pokeemerald.

Guidelines

A pull request meets the scope criteria if:

  • The feature does not belong to a category considered “not in scope” AND
  • The feature belongs to a category considered “in scope”

In Scope Categories

  1. SS Species: Adds Species that have appeared in a Showdown-supported title
  2. SS Moves: Adds Moves and Move Animations that have appeared in a Showdown-supported title
  3. SS Abilities: Adds Abilities that have appeared in a Showdown-supported title
  4. SS Items: Adds Items that have appeared in a Showdown-supported title
  5. SS Gimmicks: Adds Gimmicks that have appeared in a Showdown-supported title
  6. SS Battle Types: Adds Special Battle Types that have appeared in a Showdown-supported title
  7. SS Battle Mechanics: Adds mechanical battle changes that have appeared in a Showdown-supported title
  8. Improve Battle AI: Improve the Battle AI in a way that allows it to approach the skill and capability of a human competitive player
  9. Base Link Compatibility: Link compatibility with base
  10. SS Overworld / Menu Updates: Replicate overworld or menu changes from Showdown-supported Pokémon titles
  11. Speed Up: Speed up the player experience of features found in base
  12. Compression: Automatically compress assets
  13. Novel Experience: Adds a novel experience included in another Showdown Supported title
  14. Helper Features: Eases the addition or inclusion of any of the aforementioned

Not In Scope Categories

  1. Non-SS Species: Adds Species that have NOT appeared in a Showdown-supported title
  2. Non-SS Moves: Adds Moves and Move Animations that have NOT appeared in a Showdown-supported title
  3. Non-SS Abilities: Adds Abilities that have NOT appeared in a Showdown-supported title
  4. Non-SS Items: Adds Items that have NOT appeared in a Showdown-supported title
  5. Non-SS Gimmicks: Adds Gimmicks that have NOT appeared in a Showdown-supported title
  6. Non-SS Battle Types: Adds Special Battle Types that have NOT appeared in a Showdown-supported title
  7. Duplicate Feature UI: Adds functionality that duplicates the core functionality of an existing vanilla feature
  8. Vanilla Link Compatibility: Link compatibility with vanilla

Discussion Required Categories

Pull Requests that fall into this category should be brought up to maintainers, who will discuss and vote as to whether or not the feature is considered in scope. Considerations for acceptance may include invasiveness of implementation, popularity, ease of maintenance, etc.

  1. Developer Ease of Use: Lowers barrier of entry for developers to use existing behavior
  2. Fangame Features: Adds a popular feature from other fangames
  3. Popular Non-SS Features: Exceptions can be made for uniquely popular or requested features (Drowsy, PLA Legend Plate, etc.)
  4. External Program: External programs like poryscript, porymoves, etc.