diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml index a9198fbff3..018e923c40 100644 --- a/.github/workflows/labels.yml +++ b/.github/workflows/labels.yml @@ -15,6 +15,7 @@ jobs: count: 1 labels: | General + General Merge category: ability category: battle-ai category: battle-mechanic diff --git a/Makefile b/Makefile index a2c41d957f..777689907f 100644 --- a/Makefile +++ b/Makefile @@ -162,8 +162,16 @@ MAPJSON := $(TOOLS_DIR)/mapjson/mapjson$(EXE) JSONPROC := $(TOOLS_DIR)/jsonproc/jsonproc$(EXE) TRAINERPROC := $(TOOLS_DIR)/trainerproc/trainerproc$(EXE) PATCHELF := $(TOOLS_DIR)/patchelf/patchelf$(EXE) -ROMTEST ?= $(shell { command -v mgba-rom-test || command -v $(TOOLS_DIR)/mgba/mgba-rom-test$(EXE); } 2>/dev/null) -ROMTESTHYDRA := $(TOOLS_DIR)/mgba-rom-test-hydra/mgba-rom-test-hydra$(EXE) +ifeq ($(shell uname),Darwin) + ROMTEST ?= $(shell command -v mgba-rom-test-mac 2>/dev/null || echo $(TOOLS_DIR)/mgba/mgba-rom-test-mac) + ROMTESTHYDRA := $(shell command -v mgba-rom-test-hydra 2>/dev/null || echo $(TOOLS_DIR)/mgba-rom-test-hydra/mgba-rom-test-hydra) +else ifeq ($(shell uname),Linux) + ROMTEST ?= $(shell command -v mgba-rom-test 2>/dev/null || echo $(TOOLS_DIR)/mgba/mgba-rom-test) + ROMTESTHYDRA := $(shell command -v mgba-rom-test-hydra 2>/dev/null || echo $(TOOLS_DIR)/mgba-rom-test-hydra/mgba-rom-test-hydra) +else + ROMTEST ?= $(TOOLS_DIR)/mgba/mgba-rom-test$(EXE) + ROMTESTHYDRA := $(TOOLS_DIR)/mgba-rom-test-hydra/mgba-rom-test-hydra$(EXE) +endif # Learnset helper is a Python script LEARNSET_HELPERS_DIR := $(TOOLS_DIR)/learnset_helpers diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index f353445fa3..5f92d592d2 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1743,10 +1743,6 @@ .byte \battler .endm - .macro hitswitchtargetfailed - callnative BS_HitSwitchTargetFailed - .endm - .macro tryrevivalblessing, failInstr:req callnative BS_TryRevivalBlessing .4byte \failInstr diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index f3fdab5ae1..994ba7e6ee 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -1824,7 +1824,6 @@ BattleScript_HitSwitchTargetDynamaxed:: printstring STRINGID_MOVEBLOCKEDBYDYNAMAX waitmessage B_WAIT_TIME_LONG BattleScript_HitSwitchTargetForceRandomSwitchFailed: - hitswitchtargetfailed setbyte sSWITCH_CASE, B_SWITCH_NORMAL return @@ -2222,13 +2221,13 @@ BattleScript_AttackAccUpDoMoveAnim:: attackanimation waitanimation setbyte sSTAT_ANIM_PLAYED, FALSE - playstatchangeanimation BS_ATTACKER, BIT_SPATK | BIT_SPDEF, 0 + playstatchangeanimation BS_ATTACKER, BIT_ATK | BIT_ACC, 0 setstatchanger STAT_ATK, 1, FALSE - statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_AttackAccUpTrySpDef - jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_AttackAccUpTrySpDef + statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_AttackAccUpTryAcc + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_AttackAccUpTryAcc printfromtable gStatUpStringIds waitmessage B_WAIT_TIME_LONG -BattleScript_AttackAccUpTrySpDef:: +BattleScript_AttackAccUpTryAcc:: setstatchanger STAT_ACC, 1, FALSE statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_AttackAccUpEnd jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_AttackAccUpEnd @@ -4731,7 +4730,7 @@ BattleScript_MoveEffectStockpileWoreOff:: return BattleScript_StockpileStatChangeDown: - statbuffchange MOVE_EFFECT_AFFECTS_USER, BattleScript_StockpileStatChangeDown_Ret + statbuffchange MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN, BattleScript_StockpileStatChangeDown_Ret setgraphicalstatchangevalues playanimation BS_ATTACKER, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1 printfromtable gStatDownStringIds @@ -4833,6 +4832,7 @@ BattleScript_EffectWillOWisp:: jumpifability BS_TARGET, ABILITY_WATER_BUBBLE, BattleScript_WaterVeilPrevents jumpifability BS_TARGET, ABILITY_COMATOSE, BattleScript_AbilityProtectsDoesntAffect jumpifability BS_TARGET, ABILITY_PURIFYING_SALT, BattleScript_AbilityProtectsDoesntAffect + jumpifability BS_TARGET, ABILITY_THERMAL_EXCHANGE, BattleScript_AbilityProtectsDoesntAffect jumpifflowerveil BattleScript_FlowerVeilProtects jumpifleafguardprotected BS_TARGET, BattleScript_AbilityProtectsDoesntAffect jumpifshieldsdown BS_TARGET, BattleScript_AbilityProtectsDoesntAffect diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index eecd9a0ec5..aff65a4ca2 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -13,12 +13,8 @@ - [How to add new battle script commands/macros](tutorials/how_to_battle_script_command_macro.md) - [How to add a new move](tutorials/how_to_new_move.md) - [How to add a new trainer class](tutorials/how_to_trainer_class.md) - - [How to add a new Pokémon]() - - [v1.10.x](tutorials/how_to_new_pokemon_1_10_0.md) - - [v1.9.x](tutorials/how_to_new_pokemon_1_9_0.md) - - [v1.8.x](tutorials/how_to_new_pokemon_1_8_0.md) - - [v1.7.x](tutorials/how_to_new_pokemon_1_7_0.md) - - [v1.6.x](tutorials/how_to_new_pokemon_1_6_0.md) + - [How to add a new Pokémon](tutorials/how_to_new_pokemon.md) + - [v1.6.x and earlier](tutorials/how_to_new_pokemon_1_6_0.md) - [How to use the Testing System](tutorials/how_to_testing_system.md) - [How to add new Trainer Slides](tutorials/how_to_new_trainer_slide.md) - [Changelog](./CHANGELOG.md) diff --git a/docs/install/mac/MAC_OS.md b/docs/install/mac/MAC_OS.md index 8ffa4df089..7491427cb6 100644 --- a/docs/install/mac/MAC_OS.md +++ b/docs/install/mac/MAC_OS.md @@ -10,6 +10,18 @@ - If devkitARM is **not installed**, then go to [Installing devkitARM (macOS)](#installing-devkitarm-macos). - Otherwise, **open the Terminal** and go to [Choosing where to store pokeemerald-expansion (macOS)](#choosing-where-to-store-pokeemerald-expansion-macos) +3. **Optional: To run tests**, if the homebrew environment is not installed, install the package manager using [this reference](https://brew.sh). Open your terminal and run the following commands: + + ```bash + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + brew install coreutils + ``` + +4. **Optional: To run tests via Rosetta** + - You probably don't want to do this as it's much slower. Most users can use native tools, but some may have other reasons to use this setup such as working with Intel-only custom tooling. + - You will need an Intel-compatible homebrew installation. Understanding how to get one can be found [here](https://github.com/Homebrew/brew/issues/9173#issuecomment-729206868). + - Install `coreutils` like in step 3, but using your Intel-compatible installation of homebrew. + ### Installing libpng (macOS)
Note for advanced users... diff --git a/docs/tutorials/how_to_new_pokemon_1_10_0.md b/docs/tutorials/how_to_new_pokemon.md similarity index 86% rename from docs/tutorials/how_to_new_pokemon_1_10_0.md rename to docs/tutorials/how_to_new_pokemon.md index 4d836fc7d3..b8a99520ec 100644 --- a/docs/tutorials/how_to_new_pokemon_1_10_0.md +++ b/docs/tutorials/how_to_new_pokemon.md @@ -1,14 +1,10 @@ - This is a modified version of [the original tutorial about adding new Pokémon species available in Pokeemerald's wiki](https://github.com/pret/pokeemerald/wiki/How-to-add-a-new-Pokémon-species). 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. -- [Version 1.9.x](how_to_new_pokemon_1_9_0.md) -- [Version 1.8.x](how_to_new_pokemon_1_8_0.md) -- [Version 1.7.x](how_to_new_pokemon_1_7_0.md) -- [Version 1.6.x](how_to_new_pokemon_1_6_0.md) +## IMPORTANT: This tutorial applies to 1.7.x versions onward. +- [v1.6.x and earlier](how_to_new_pokemon_1_6_0.md) # Changes compared to vanilla The main things that the Expansion changes are listed here. @@ -40,13 +36,13 @@ The main things that the Expansion changes are listed here. * [1. Form tables](#1-form-tables) * [2. Form change tables](#2-form-change-tables) * [3. Gender differences](#3-gender-differences) - * [4. Overworld Data](#4-overworld-data) + * [4. Overworld Data (v1.9 onwards)](#4-overworld-data-v19-onwards) + * [5. In-battle shadows (v1.10 onwards)](#5-in-battle-shadows-v110-onwards) # Useful resources You can open a sprite debug menu by pressing `Select` in a Pokémon's summary screen outside of battle. -![mGBA_6WOo1TSlsn](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/0c625cd8-8f89-4bc8-a285-b10a420a8f6d) - +![visualizer1](/docs/tutorials/img/add_pokemon/visualizer1.gif) # The Data - Part 1 @@ -77,7 +73,7 @@ We add this at the end so that no existing species change Id and so that we don' Now, let's see how it looks in-game! -![image](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/dc15b0ba-a4bd-4f4e-9658-2dff73a11f79) +![visualizer2](/docs/tutorials/img/add_pokemon/visualizer2.png) Hmmm, something's not right... @@ -87,7 +83,7 @@ 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. -```diff +```c [SPECIES_MEW] = { .baseHP = 100, @@ -133,7 +129,11 @@ Now, to better understand Mewthree, we also need to understand Mew. Let's look a .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, + .frontAnimFrames = ANIM_FRAMES( + ANIMCMD_FRAME(1, 50), + ANIMCMD_FRAME(1, 40), + ANIMCMD_FRAME(0, 10), + ), .frontAnimId = P_GBA_STYLE_SPECIES_GFX ? ANIM_SWING_CONVEX : ANIM_ZIGZAG_SLOW, .enemyMonElevation = P_GBA_STYLE_SPECIES_GFX ? 8 : 11, .backPic = gMonBackPic_Mew, @@ -151,6 +151,7 @@ Now, to better understand Mewthree, we also need to understand Mew. Let's look a SIZE_32x32, SHADOW_SIZE_M, TRACKS_NONE, + sAnimTable_Following, gOverworldPalette_Mew, gShinyOverworldPalette_Mew ) @@ -165,6 +166,10 @@ Now, to better understand Mewthree, we also need to understand Mew. Let's look a 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). +Across the species files you'll see preprocessor instructions such as `#if/endif P_FAMILY_MEW`. These are used by expansion in order to allow users to disable species via config. Since we're making a new species from scratch, you DON'T need to add them as part of the process. + +You can also ignore switch cases for `P_GBA_STYLE_SPECIES_GFX`, as those are only used to switching to GBA-styled sprites. + We'll start by adding the self-explanatory data that's also present in pret's vanilla structure: ## 3. Define its basic species information @@ -215,6 +220,13 @@ The `.` is the structure reference operator in C to refer to the member object o - `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)`. + - ***1.9 and earlier:*** The format for setting types is the following: + ```c + // Mono-type + .types = { TYPE_PSYCHIC, TYPE_PSYCHIC }, + // Dual-type + .types = { TYPE_PSYCHIC, TYPE_DARK }, + ``` - `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 `#if`s, `#elif`s 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. @@ -237,6 +249,13 @@ The `.` is the structure reference operator in C to refer to the member object o - 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](https://bulbapedia.bulbagarden.net/wiki/Egg_Group) for more info. - This is using the helper macro `MON_EGG_GROUPS`. + - ***1.9 and earlier:*** The format for setting egg groups is the following: + ```c + // Mono-group + .eggGroups = { EGG_GROUP_MONSTER, EGG_GROUP_MONSTER }, + // Dual-group + .eggGroups = { EGG_GROUP_MONSTER, EGG_GROUP_MINERAL }, + ``` - `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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/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) @@ -424,7 +443,7 @@ Now we can add the number and entry to our Mewthree: }, }; ``` -![image](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/3759dd4c-8da5-4b1c-9a50-b9e9d0815e7f) +![image](/docs/tutorials/img/add_pokemon/dex1.png) The values `pokemonScale`, `pokemonOffset`, `trainerScale` and `trainerOffset` are used for the height comparison figure in the Pokédex. @@ -475,7 +494,7 @@ Edit [src/data/pokemon/pokedex_orders.h](https://github.com/rh-hideout/pokeemera ... }; ``` -![mGBA_lUBfmFEKUx](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/3a8b8a17-759b-486b-9831-deb2f494bd71) +![mGBA_lUBfmFEKUx](/docs/tutorials/img/add_pokemon/dex2.gif) # The Graphics @@ -539,6 +558,10 @@ Please note that Pecharunt, the Pokémon that should be above your insertion for 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. +### Version 1.11.0 or later +We add this data directly to the entry, so go to section 4. + +### Version 1.10.3 or earlier Edit [src/data/pokemon_graphics/front_pic_anims.h](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon_graphics/front_pic_anims.h): ```diff @@ -597,7 +620,9 @@ Now that we have all the external data ready, we just need to add it to `gSpecie + .frontPic = gMonFrontPic_Mewthree, + .frontPicSize = MON_COORDS_SIZE(64, 64), + .frontPicYOffset = 0, -+ .frontAnimFrames = sAnims_Mewthree, ++ .frontAnimFrames = ANIM_FRAMES( ++ ANIMCMD_FRAME(0, 1), ++ ), + .frontAnimId = ANIM_GROW_VIBRATE, + .frontAnimDelay = 15, + .enemyMonElevation = 6, @@ -621,7 +646,10 @@ Let's explain each of these: - `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. + - We define our animation frame animations directly here. In version `1.10.3 and earlier`, we add the reference to the table that we defined earlier here like this instead: + ```diff + + .frontAnimFrames = sAnims_Mewthree, + ``` - `frontAnimId`: - Because you are limited to two frames, there are already [predefined front sprite animations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/pokemon_animation.h), describing translations, rotations, scalings or color changes. - `frontAnimDelay`: @@ -655,6 +683,12 @@ Let's explain each of these: #endif ``` +***NOTE:** In v1.7.x only, there were macros that set multiple of these fields. However, they were considered clunky to use, so they were removed in v1.8. + - `FRONT_PIC`: For `frontPic` and `frontPicSize`. + - `BACK_PIC`: For `backPic` and `backPicSize`. + - `PALETTES`: For `palette` and `shinyPalette`. + - `ICON`: For `iconSprite` and `iconPalIndex`. + # The Data - Part 2 We're almost there just a bit left! @@ -676,19 +710,26 @@ We're almost there just a bit left! }; ``` Each species flag provides properties to the species: +- `perfectIVCount` ***(1.10 onwards)***: + - Guarantees that the number of IVs specified here will be perfect. - `isLegendary`: - - Does nothing. + - ***1.10 onwards:*** Does nothing. + - ***1.9 and earlier:*** Guaranteed 3 perfect IVs for the species. - `isMythical`: - Is skipped during Pokédex evaluations. - Unless it also has the `dexForceRequired` flag. - Cannot obtain Gigantamax factor via `ToggleGigantamaxFactor`. + - ***1.9 and earlier:*** Guaranteed 3 perfect IVs for the species. - `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. + - ***1.9 and earlier:*** Guaranteed 3 perfect IVs for the species. +- `isParadox` (`isParadoxForm` previous to 1.9): + - ***1.10 onwards:*** Makes it so that Booster Energy cannot be knocked off. + - ***1.9 and earlier:*** Does nothing. - `isTotem`: - - Does nothing. + - ***1.10 onwards:*** Does nothing. + - ***1.9 and earlier:*** Guaranteed 3 perfect IVs for the species. - `isMegaEvolution`: - A Mega indicator is added to the battle box indicating that they're Mega Evolved. - The species doesn't receive affection benefits. @@ -702,14 +743,13 @@ Each species flag provides properties to the species: - 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. + - **1.10.3 onwards:** Used to determine breeding offspring from different parents based on their region. + - **1.10.2 and earlier:** Does nothing. - `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 learn the universal TM or Tutor moves. +- `isFrontierBanned` ***(1.9 onwards)***: - This species will be unable to enter Battle Frontier facilities. Replaces `gFrontierBannedSpecies`. ## 2. Delimit the moveset @@ -864,7 +904,7 @@ _NOTE: At the top of this file, you will probably see this warning:_ // DO NOT MODIFY THIS FILE! It is auto-generated from tools/learnset_helpers/teachable.py` // ``` -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. +From version 1.9 onwards, pokeemerald-expansion includes a tool called the learnset helper, which aims to automate the generation of valid teachable moves. At the time of writing, this tool only supports generating TM and Tutor learnsets. However, in the future it may be expanded to deal with level up learnsets and egg moves. Ignore the warning shown above the first time you're adding your teachable moves (as otherwise the compiler will complain about the array not existing), but in the future (if you're using the learnset helper) simply edit what teachable moves your Pokémon can learn in one of the JSON files found in `tools/learnset_helpers/porymoves_files`. It doesn't really matter which one you add your new Pokémon to, as the tool pulls from all of the files in this folder. @@ -990,7 +1030,7 @@ static const u16 sPikachuFormSpeciesIdTable[] = { }; #endif //P_FAMILY_PIKACHU ``` -We register the table each form entry in `gSpeciesInfo`. +We register the table for each form in `gSpeciesInfo`. ```diff [SPECIES_PIKACHU] = @@ -1015,7 +1055,7 @@ What this allows us to do is to be able to get all forms of a Pokémon in our co For example, in the HGSS dex, it lets us browse between the entries of every form available.: -![image](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/a1a90b79-46a1-4cd6-97d6-ec5d741bfdc8) ![image](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/7cffc6be-0b5c-4074-b689-736a97297843) +![hgssdex1](/docs/tutorials/img/add_pokemon/hgssdex1.png) ![image](/docs/tutorials/img/add_pokemon/hgssdex2.png) 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: ```c @@ -1046,7 +1086,7 @@ 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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/45256192-b451-4baa-af06-f57ca16e1e46) +![gender_diffs](/docs/tutorials/img/add_pokemon/gender_diffs.gif) You may have seen that there's a couple of duplicate fields with a "Female" suffix. ```diff @@ -1054,26 +1094,33 @@ You may have seen that there's a couple of duplicate fields with a "Female" suff { ... .frontPic = gMonFrontPic_Frillish, -+ .frontPicFemale = gMonFrontPic_FrillishF, .frontPicSize = MON_COORDS_SIZE(56, 56), -+ .frontPicSizeFemale = MON_COORDS_SIZE(56, 56), .frontPicYOffset = 5, - .frontAnimFrames = sAnims_Frillish, + .frontAnimFrames = ANIM_FRAMES( + ANIMCMD_FRAME(1, 30), + ANIMCMD_FRAME(0, 30), + ANIMCMD_FRAME(1, 30), + ANIMCMD_FRAME(0, 30), + ), .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, ++#if P_GENDER_DIFFERENCES ++ .frontPicFemale = gMonFrontPic_FrillishF, ++ .frontPicSizeFemale = MON_COORDS_SIZE(56, 56), ++ .backPicFemale = gMonBackPic_FrillishF, ++ .backPicSizeFemale = MON_COORDS_SIZE(40, 56), ++ .paletteFemale = gMonPalette_FrillishF, ++ .shinyPaletteFemale = gMonShinyPalette_FrillishF, ++ .iconSpriteFemale = gMonIcon_FrillishF, + .iconPalIndexFemale = 1, ++#endif //P_GENDER_DIFFERENCES FOOTPRINT(Frillish) .levelUpLearnset = sFrillishLevelUpLearnset, .teachableLearnset = sFrillishTeachableLearnset, @@ -1084,10 +1131,8 @@ These are used to change the graphics of the Pokémon if they're female. If they 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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/e59238dc-9779-4f26-a9e7-159a32caa3d9) - -If you have `OW_POKEMON_OBJECT_EVENTS` in your hack, you can add Overworld of your new species by following these steps: +## 4. Overworld Data (v1.9 onwards) +If you have `OW_POKEMON_OBJECT_EVENTS` in your hack, you can add Overworld sprite data of your new species. Naturally, these can also be used for followers. 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 @@ -1105,13 +1150,13 @@ Thirdly, in [spritesheet_rules.mk](https://github.com/rh-hideout/pokeemerald-exp ```diff $(POKEMONGFXDIR)/mewtwo/overworld.4bpp: %.4bpp: %.png - $(GFX) $< $@ -mwidth 4 -mheight 4 + $(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 + $(GFX) $< $@ -mwidth 4 -mheight 4 ``` Fourthly, in [src/data/object_events/object_event_pic_tables_followers.h](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/object_events/object_event_pic_tables_followers.h): @@ -1142,6 +1187,7 @@ And finally, in `gSpeciesInfo`: + SIZE_32x32, + SHADOW_SIZE_M, + TRACKS_FOOT, ++ sAnimTable_Following, + gOverworldPalette_Mewthree, + gShinyOverworldPalette_Mewthree + ) @@ -1150,6 +1196,7 @@ And finally, in `gSpeciesInfo`: }, }; ``` +**Note: In versions previous to 1.11, `sAnimTable_Following` is not added here.** ### 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. @@ -1157,25 +1204,77 @@ Depending on your species, you might want to use different sizes for it. For exa 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`. +You have 4 options for their shadow, between Small, Medium, Large and None: + - `SHADOW_SIZE_NONE` + - `SHADOW_SIZE_S` ![shadow_small](/graphics/field_effects/pics/shadow_small.png) + - `SHADOW_SIZE_M` ![shadow_medium](/graphics/field_effects/pics/shadow_medium.png) + - `SHADOW_SIZE_L` ![shadow_large](/graphics/field_effects/pics/shadow_large.png) ### Tracks You have 4 options for the tracks that your species will leave behind on sand. - `TRACKS_NONE` - - `TRACKS_FOOT` ![sand_footprints](https://github.com/user-attachments/assets/8b8c34d6-72e9-4b9d-839d-0a5cc1ae1a4c) - - `TRACKS_SLITHER` ![slither_tracks](https://github.com/user-attachments/assets/28219c05-61e0-48b3-9aeb-43f48e4ffdd4) - - `TRACKS_SPOT` ![spot_tracks](https://github.com/user-attachments/assets/f7a24887-c5ca-47f2-8825-01f3df61deca) - - `TRACKS_BUG` ![bug_tracks](https://github.com/user-attachments/assets/8cd1dea4-4123-4af8-a558-992874a6d589) + - `TRACKS_FOOT` ![sand_footprints](/graphics/field_effects/pics/sand_footprints.png) + - `TRACKS_SLITHER` ![slither_tracks](/graphics/field_effects/pics/slither_tracks.png) + - `TRACKS_SPOT` ![spot_tracks](/graphics/field_effects/pics/spot_tracks.png) + - `TRACKS_BUG` ![bug_tracks](/graphics/field_effects/pics/bug_tracks.png) ...though technically you can also use `TRACKS_BIKE_TIRE` if you wish to. -![bike_tire_tracks](https://github.com/user-attachments/assets/ac81d211-85e5-443a-ac54-c2976f1f0b82) +![bike_tire_tracks](/graphics/field_effects/pics/bike_tire_tracks.png) + +### Asymmetric sprites (Version 1.10.0 onwards) + +![scovillain](/graphics/pokemon/scovillain/overworld.png) + +You can set up an east-west asymetric overworld sprite by adding the East frames at the end of the sheet and changing the following: + +#### Version 1.11.0 onwards +```diff + OVERWORLD( + sPicTable_Mewthree, + SIZE_32x32, + SHADOW_SIZE_M, + TRACKS_FOOT, +- sAnimTable_Following, ++ sAnimTable_Following_Asym, + gOverworldPalette_Mewthree, + gShinyOverworldPalette_Mewthree + ) +``` +#### Version 1.10.x +```diff +- OVERWORLD( ++ OVERWORLD_SET_ANIM( + sPicTable_Mewthree, + SIZE_32x32, + SHADOW_SIZE_M, + TRACKS_FOOT, ++ sAnimTable_Following_Asym, + gOverworldPalette_Mewthree, + gShinyOverworldPalette_Mewthree + ) +``` + +Either way, you may also create custom animation tables and use them here appropiately. + +### How to add the Pokémon Object Events to map +In Porymap, select the object you want to set the sprite to. Then, change the field "Sprite" to use `OBJ_EVENT_GFX_SPECIES(SPECIES)`, replacing SPECIES with the name of the species you want to use. If you get a compiler error, it's because it used the species define as part of the macro, so it needs to match how you defined it all the way back in [Declare a species constant](#1-Declare-a-species-constant). +![charizard](/docs/tutorials/img/add_pokemon/charizard.png) +![overworld_data](/docs/tutorials/img/add_pokemon/overworld_data.gif) + +If you want to use their shiny and/or female versions, use one of the following macros: +- `OBJ_EVENT_GFX_SPECIES_SHINY(name)` +- `OBJ_EVENT_GFX_SPECIES_FEMALE(name)` +- `OBJ_EVENT_GFX_SPECIES_SHINY_FEMALE(name)` + +## 5. In-battle shadows (v1.10 onwards) +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`. diff --git a/docs/tutorials/how_to_new_pokemon_1_6_0.md b/docs/tutorials/how_to_new_pokemon_1_6_0.md index 9bf3e2e61f..5a6401f065 100644 --- a/docs/tutorials/how_to_new_pokemon_1_6_0.md +++ b/docs/tutorials/how_to_new_pokemon_1_6_0.md @@ -4,9 +4,7 @@ Despite the persistent rumors about an incredibly strong third form of Mew hidin 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. -- [Version 1.9.x](how_to_new_pokemon_1_9_0.md) -- [Version 1.8.x](how_to_new_pokemon_1_8_0.md) -- [Version 1.7.x](how_to_new_pokemon_1_7_0.md) +- [Version 1.7.x onwards](how_to_new_pokemon.md) # Changes compared to vanilla The main things that the Expansion changes are listed here. diff --git a/docs/tutorials/how_to_new_pokemon_1_7_0.md b/docs/tutorials/how_to_new_pokemon_1_7_0.md deleted file mode 100644 index 30c1f82dbe..0000000000 --- a/docs/tutorials/how_to_new_pokemon_1_7_0.md +++ /dev/null @@ -1,1049 +0,0 @@ - -This is a modified version of [the original tutorial about adding new Pokémon species available in Pokeemerald's wiki](https://github.com/pret/pokeemerald/wiki/How-to-add-a-new-Pokémon-species). - -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. -- [Version 1.9.x](how_to_new_pokemon_1_9_0.md) -- [Version 1.8.x](how_to_new_pokemon_1_8_0.md) -- [Version 1.6.x](how_to_new_pokemon_1_6_0.md) - -# 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](#useful-resources) -* [The Data - Part 1](#the-data---part-1) - * [1. Declare a species constant](#1-Declare-a-species-constant) - * [2. `SpeciesInfo`'s structure](#2-speciesinfos-structure) - * [3. Define its basic species information](#3-define-its-basic-species-information) - * [4. Species Name](#4-species-name) - * [5. Define its cry](#5-define-its-cry) - * [6. Define its Pokédex entry](#6-define-its-pokédex-entry) -* [The Graphics](#the-graphics) - * [1. Edit the sprites](#1-edit-the-sprites) - * [2. Add the sprites to the rom](#2-add-the-sprites-to-the-rom) - * [3. Add the animations to the rom](#3-add-the-animations-to-the-rom) - * [4. Linking graphic information to our Pokémon](#4-linking-graphic-information-to-our-pokémon) -* [The Data - Part 2](#the-data---part-2) - * [1. Species Flags](#1-species-flags) - * [2. Delimit the moveset](#2-delimit-the-moveset) - * [3. Define the Evolutions](#3-define-the-evolutions) - * [4. Make it appear!](#4-make-it-appear) -* [Optional data](#optional-data) - * [1. Form tables](#1-form-tables) - * [2. Form change tables](#2-form-change-tables) - * [3. Gender differences](#3-gender-differences) - -# Useful resources -You can open a sprite debug menu by pressing `Select` in a Pokémon's summary screen outside of battle. - -![mGBA_6WOo1TSlsn](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/0c625cd8-8f89-4bc8-a285-b10a420a8f6d) - - -# 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/species.h): - -```diff - #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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/dc15b0ba-a4bd-4f4e-9658-2dff73a11f79) - -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. -```diff - [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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/species_info.h): -```diff - 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 `#if`s, `#elif`s 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](https://bulbapedia.bulbagarden.net/wiki/Experience) 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](https://bulbapedia.bulbagarden.net/wiki/Egg_Group) 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/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](https://bulbapedia.bulbagarden.net/wiki/Ability#Hidden_Abilities) and [here](https://bulbapedia.bulbagarden.net/wiki/Ability_Patch) 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 - -```diff - 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](https://cdn.discordapp.com/attachments/1206424179197349924/1206424212403920996/mewthree.mp3?ex=6600df1a&is=65ee6a1a&hm=4f05d58a7ae22bfdcd5f83558e15320fc1b6cd42a8cb9dee347b20bfb805ece5&). - -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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/sound/direct_sound_data.inc). - -```diff -.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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/cries.h): - -```diff -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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/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. - -```diff - cry Cry_Terapagos - cry Cry_Pecharunt -+ cry Cry_Mewthree -``` -```diff - cry_reverse Cry_Terapagos - cry_reverse Cry_Pecharunt -+ cry_reverse Cry_Mewthree -``` - -Lastly, we add the cry to our species entry -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/pokedex.h): - -```diff -// National Pokedex order -enum { - NATIONAL_DEX_NONE, - // Kanto - NATIONAL_DEX_BULBASAUR, -... - NATIONAL_DEX_PECHARUNT, -+ NATIONAL_DEX_MEWTHREE, -}; -``` - -```diff - #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: - -```diff -// 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/pokemon.c): - -```diff - 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: -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/3759dd4c-8da5-4b1c-9a50-b9e9d0815e7f) - -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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/pokedex_orders.h): - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/3a8b8a17-759b-486b-9831-deb2f494bd71) - - -# The Graphics -We will start by copying the following files for *Mew* (not Mewtwo) and rename it to `mewthree`. -```sh -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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/graphics/pokemon.h): - -```diff -#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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon_graphics/front_pic_anims.h): - -```diff -#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, -+}; -``` - -```diff -#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: -```c -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`): -```c -#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: - -```diff - 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. - ```c - #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`: - - Because you are limited to two frames, there are already [predefined front sprite animations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/pokemon_animation.h), describing translations, rotations, scalings or color changes. -- `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. - ```c - #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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/pokemon_animation.h) 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. - ```c - #define PALETTES(pal) \ - .palette = gMonPalette_## pal, \ - .shinyPalette = gMonShinyPalette_## pal - ``` -- `ICON` - - This macro is calls both the icon sprite and its palette. - ```c - #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). - ```c - #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 - -```diff - 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. -- `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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/level_up_learnsets.h): - -```diff -#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`: - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/teachable_learnsets.h): - -```diff -#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`: - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/egg_moves.h). - - -## 3. Define the Evolutions - -We want Mewthree to evolve from Mewtwo by reaching level 100. - -Edit `gSpeciesInfo`: - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/wild_encounters.json): - -```diff - { - "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: -```c -#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`. - -```diff - [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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/a1a90b79-46a1-4cd6-97d6-ec5d741bfdc8) ![image](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/7cffc6be-0b5c-4074-b689-736a97297843) - -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: -```c - 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. - -```c -#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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/45256192-b451-4baa-af06-f57ca16e1e46) - -You may have seen that there's a couple of duplicate fields with a "Female" suffix. -```diff - [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. - diff --git a/docs/tutorials/how_to_new_pokemon_1_8_0.md b/docs/tutorials/how_to_new_pokemon_1_8_0.md deleted file mode 100644 index 6dbb009fcf..0000000000 --- a/docs/tutorials/how_to_new_pokemon_1_8_0.md +++ /dev/null @@ -1,1049 +0,0 @@ - -This is a modified version of [the original tutorial about adding new Pokémon species available in Pokeemerald's wiki](https://github.com/pret/pokeemerald/wiki/How-to-add-a-new-Pokémon-species). - -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. -- [Version 1.9.x](how_to_new_pokemon_1_9_0.md) -- [Version 1.7.x](how_to_new_pokemon_1_7_0.md) -- [Version 1.6.x](how_to_new_pokemon_1_6_0.md) - -# 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](#useful-resources) -* [The Data - Part 1](#the-data---part-1) - * [1. Declare a species constant](#1-Declare-a-species-constant) - * [2. `SpeciesInfo`'s structure](#2-speciesinfos-structure) - * [3. Define its basic species information](#3-define-its-basic-species-information) - * [4. Species Name](#4-species-name) - * [5. Define its cry](#5-define-its-cry) - * [6. Define its Pokédex entry](#6-define-its-pokédex-entry) -* [The Graphics](#the-graphics) - * [1. Edit the sprites](#1-edit-the-sprites) - * [2. Add the sprites to the rom](#2-add-the-sprites-to-the-rom) - * [3. Add the animations to the rom](#3-add-the-animations-to-the-rom) - * [4. Linking graphic information to our Pokémon](#4-linking-graphic-information-to-our-pokémon) -* [The Data - Part 2](#the-data---part-2) - * [1. Species Flags](#1-species-flags) - * [2. Delimit the moveset](#2-delimit-the-moveset) - * [3. Define the Evolutions](#3-define-the-evolutions) - * [4. Make it appear!](#4-make-it-appear) -* [Optional data](#optional-data) - * [1. Form tables](#1-form-tables) - * [2. Form change tables](#2-form-change-tables) - * [3. Gender differences](#3-gender-differences) - -# Useful resources -You can open a sprite debug menu by pressing `Select` in a Pokémon's summary screen outside of battle. - -![mGBA_6WOo1TSlsn](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/0c625cd8-8f89-4bc8-a285-b10a420a8f6d) - - -# 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/species.h): - -```diff - #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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/dc15b0ba-a4bd-4f4e-9658-2dff73a11f79) - -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. -```diff - [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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/species_info.h): -```diff - 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 `#if`s, `#elif`s 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](https://bulbapedia.bulbagarden.net/wiki/Experience) 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](https://bulbapedia.bulbagarden.net/wiki/Egg_Group) 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/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](https://bulbapedia.bulbagarden.net/wiki/Ability#Hidden_Abilities) and [here](https://bulbapedia.bulbagarden.net/wiki/Ability_Patch) 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 - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/sound/direct_sound_data.inc). - -```diff -.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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/cries.h): - -```diff -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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/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. - -```diff - cry Cry_Terapagos - cry Cry_Pecharunt -+ cry Cry_Mewthree -``` -```diff - cry_reverse Cry_Terapagos - cry_reverse Cry_Pecharunt -+ cry_reverse Cry_Mewthree -``` - -Lastly, we add the cry to our species entry -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/pokedex.h): - -```diff -// National Pokedex order -enum { - NATIONAL_DEX_NONE, - // Kanto - NATIONAL_DEX_BULBASAUR, -... - NATIONAL_DEX_PECHARUNT, -+ NATIONAL_DEX_MEWTHREE, -}; -``` - -```diff - #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: - -```diff -// 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/pokemon.c): - -```diff - 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: -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/3759dd4c-8da5-4b1c-9a50-b9e9d0815e7f) - -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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/pokedex_orders.h): - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/3a8b8a17-759b-486b-9831-deb2f494bd71) - - -# The Graphics -We will start by copying the following files for *Mew* (not Mewtwo) and rename it to `mewthree`. -```sh -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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/graphics/pokemon.h): - -```diff -#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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon_graphics/front_pic_anims.h): - -```diff -#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, -+}; -``` - -```diff -#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: -```c -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`): -```c -#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: - -```diff - 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`: - - Because you are limited to two frames, there are already [predefined front sprite animations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/pokemon_animation.h), describing translations, rotations, scalings or color changes. -- `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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/pokemon_animation.h) 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). - ```c - #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 - -```diff - 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. -- `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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/level_up_learnsets.h): - -```diff -#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`: - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/teachable_learnsets.h): - -```diff -#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`: - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/egg_moves.h). - - -## 3. Define the Evolutions - -We want Mewthree to evolve from Mewtwo by reaching level 100. - -Edit `gSpeciesInfo`: - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/wild_encounters.json): - -```diff - { - "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: -```c -#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`. - -```diff - [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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/a1a90b79-46a1-4cd6-97d6-ec5d741bfdc8) ![image](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/7cffc6be-0b5c-4074-b689-736a97297843) - -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: -```c - 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. - -```c -#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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/45256192-b451-4baa-af06-f57ca16e1e46) - -You may have seen that there's a couple of duplicate fields with a "Female" suffix. -```diff - [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. diff --git a/docs/tutorials/how_to_new_pokemon_1_9_0.md b/docs/tutorials/how_to_new_pokemon_1_9_0.md deleted file mode 100644 index aad3332873..0000000000 --- a/docs/tutorials/how_to_new_pokemon_1_9_0.md +++ /dev/null @@ -1,1157 +0,0 @@ - -This is a modified version of [the original tutorial about adding new Pokémon species available in Pokeemerald's wiki](https://github.com/pret/pokeemerald/wiki/How-to-add-a-new-Pokémon-species). - -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. -- [Version 1.8.x](how_to_new_pokemon_1_8_0.md) -- [Version 1.7.x](how_to_new_pokemon_1_7_0.md) -- [Version 1.6.x](how_to_new_pokemon_1_6_0.md) - -# 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](#useful-resources) -* [The Data - Part 1](#the-data---part-1) - * [1. Declare a species constant](#1-Declare-a-species-constant) - * [2. `SpeciesInfo`'s structure](#2-speciesinfos-structure) - * [3. Define its basic species information](#3-define-its-basic-species-information) - * [4. Species Name](#4-species-name) - * [5. Define its cry](#5-define-its-cry) - * [6. Define its Pokédex entry](#6-define-its-pokédex-entry) -* [The Graphics](#the-graphics) - * [1. Edit the sprites](#1-edit-the-sprites) - * [2. Add the sprites to the rom](#2-add-the-sprites-to-the-rom) - * [3. Add the animations to the rom](#3-add-the-animations-to-the-rom) - * [4. Linking graphic information to our Pokémon](#4-linking-graphic-information-to-our-pokémon) -* [The Data - Part 2](#the-data---part-2) - * [1. Species Flags](#1-species-flags) - * [2. Delimit the moveset](#2-delimit-the-moveset) - * [3. Define the Evolutions](#3-define-the-evolutions) - * [4. Make it appear!](#4-make-it-appear) -* [Optional data](#optional-data) - * [1. Form tables](#1-form-tables) - * [2. Form change tables](#2-form-change-tables) - * [3. Gender differences](#3-gender-differences) - * [4. Overworld Data](#4-overworld-data) - -# Useful resources -You can open a sprite debug menu by pressing `Select` in a Pokémon's summary screen outside of battle. - -![mGBA_6WOo1TSlsn](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/0c625cd8-8f89-4bc8-a285-b10a420a8f6d) - - -# 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/species.h): - -```diff - #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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/dc15b0ba-a4bd-4f4e-9658-2dff73a11f79) - -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. -```diff - [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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/species_info.h): -```diff - 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 `#if`s, `#elif`s 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](https://bulbapedia.bulbagarden.net/wiki/Experience) 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](https://bulbapedia.bulbagarden.net/wiki/Egg_Group) 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/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](https://bulbapedia.bulbagarden.net/wiki/Ability#Hidden_Abilities) and [here](https://bulbapedia.bulbagarden.net/wiki/Ability_Patch) 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 - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/sound/direct_sound_data.inc). - -```diff -.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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/cries.h): - -```diff -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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/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. - -```diff - cry Cry_Terapagos - cry Cry_Pecharunt -+ cry Cry_Mewthree -``` -```diff - cry_reverse Cry_Terapagos - cry_reverse Cry_Pecharunt -+ cry_reverse Cry_Mewthree -``` - -Lastly, we add the cry to our species entry -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/constants/pokedex.h): - -```diff -// National Pokedex order -enum { - NATIONAL_DEX_NONE, - // Kanto - NATIONAL_DEX_BULBASAUR, -... - NATIONAL_DEX_PECHARUNT, -+ NATIONAL_DEX_MEWTHREE, -}; -``` - -```diff - #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: - -```diff -// 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/pokemon.c): - -```diff - 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: -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/3759dd4c-8da5-4b1c-9a50-b9e9d0815e7f) - -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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/pokedex_orders.h): - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/3a8b8a17-759b-486b-9831-deb2f494bd71) - - -# The Graphics -We will start by copying the following files for *Mew* (not Mewtwo) and rename it to `mewthree`. -```sh -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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/graphics/pokemon.h): - -```diff -#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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon_graphics/front_pic_anims.h): - -```diff -#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, -+}; -``` - -```diff -#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: -```c -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`): -```c -#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: - -```diff - 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`: - - Because you are limited to two frames, there are already [predefined front sprite animations](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/pokemon_animation.h), describing translations, rotations, scalings or color changes. -- `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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/include/pokemon_animation.h) 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). - ```c - #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 - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/level_up_learnsets.h): - -```diff -#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`: - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/teachable_learnsets.h): - -```diff -#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`: - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/pokemon/egg_moves.h). - - -## 3. Define the Evolutions - -We want Mewthree to evolve from Mewtwo by reaching level 100. - -Edit `gSpeciesInfo`: - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/wild_encounters.json): - -```diff - { - "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: -```c -#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`. - -```diff - [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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/a1a90b79-46a1-4cd6-97d6-ec5d741bfdc8) ![image](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/7cffc6be-0b5c-4074-b689-736a97297843) - -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: -```c - 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. - -```c -#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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/45256192-b451-4baa-af06-f57ca16e1e46) - -You may have seen that there's a couple of duplicate fields with a "Female" suffix. -```diff - [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](https://github.com/rh-hideout/pokeemerald-expansion/assets/2904965/e59238dc-9779-4f26-a9e7-159a32caa3d9) - -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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/graphics/pokemon.h), add the following: - -```diff - 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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/spritesheet_rules.mk) - -```diff -$(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](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/object_events/object_event_pic_tables_followers.h): -```diff -#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`: - -```diff - 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](https://github.com/user-attachments/assets/c964712e-f5cb-42e8-82fa-db33fc4f4d4c) - - `SHADOW_SIZE_M` ![shadow_medium](https://github.com/user-attachments/assets/b7792624-d65c-425b-9982-cab28ce4248e) - - `SHADOW_SIZE_L` ![shadow_large](https://github.com/user-attachments/assets/ec2dc701-d335-44ad-8ded-f8003114f0ff) - -### Tracks -You have 4 options for the tracks that your species will leave behind on sand. - - `TRACKS_NONE` - - `TRACKS_FOOT` ![sand_footprints](https://github.com/user-attachments/assets/8b8c34d6-72e9-4b9d-839d-0a5cc1ae1a4c) - - `TRACKS_SLITHER` ![slither_tracks](https://github.com/user-attachments/assets/28219c05-61e0-48b3-9aeb-43f48e4ffdd4) - - `TRACKS_SPOT` ![spot_tracks](https://github.com/user-attachments/assets/f7a24887-c5ca-47f2-8825-01f3df61deca) - - `TRACKS_BUG` ![bug_tracks](https://github.com/user-attachments/assets/8cd1dea4-4123-4af8-a558-992874a6d589) - - ...though technically you can also use `TRACKS_BIKE_TIRE` if you wish to. - -![bike_tire_tracks](https://github.com/user-attachments/assets/ac81d211-85e5-443a-ac54-c2976f1f0b82) diff --git a/docs/tutorials/img/add_pokemon/charizard.png b/docs/tutorials/img/add_pokemon/charizard.png new file mode 100644 index 0000000000..43ae63e035 Binary files /dev/null and b/docs/tutorials/img/add_pokemon/charizard.png differ diff --git a/docs/tutorials/img/add_pokemon/dex1.png b/docs/tutorials/img/add_pokemon/dex1.png new file mode 100644 index 0000000000..5f258a3d44 Binary files /dev/null and b/docs/tutorials/img/add_pokemon/dex1.png differ diff --git a/docs/tutorials/img/add_pokemon/dex2.gif b/docs/tutorials/img/add_pokemon/dex2.gif new file mode 100644 index 0000000000..3242e6dfb2 Binary files /dev/null and b/docs/tutorials/img/add_pokemon/dex2.gif differ diff --git a/docs/tutorials/img/add_pokemon/gender_diffs.gif b/docs/tutorials/img/add_pokemon/gender_diffs.gif new file mode 100644 index 0000000000..4b04993544 Binary files /dev/null and b/docs/tutorials/img/add_pokemon/gender_diffs.gif differ diff --git a/docs/tutorials/img/add_pokemon/hgssdex1.png b/docs/tutorials/img/add_pokemon/hgssdex1.png new file mode 100644 index 0000000000..8af50ba79a Binary files /dev/null and b/docs/tutorials/img/add_pokemon/hgssdex1.png differ diff --git a/docs/tutorials/img/add_pokemon/hgssdex2.png b/docs/tutorials/img/add_pokemon/hgssdex2.png new file mode 100644 index 0000000000..2dbb2b748b Binary files /dev/null and b/docs/tutorials/img/add_pokemon/hgssdex2.png differ diff --git a/docs/tutorials/img/add_pokemon/overworld_data.gif b/docs/tutorials/img/add_pokemon/overworld_data.gif new file mode 100644 index 0000000000..d4e9e8adce Binary files /dev/null and b/docs/tutorials/img/add_pokemon/overworld_data.gif differ diff --git a/docs/tutorials/img/add_pokemon/visualizer1.gif b/docs/tutorials/img/add_pokemon/visualizer1.gif new file mode 100644 index 0000000000..4a2e0ec142 Binary files /dev/null and b/docs/tutorials/img/add_pokemon/visualizer1.gif differ diff --git a/docs/tutorials/img/add_pokemon/visualizer2.png b/docs/tutorials/img/add_pokemon/visualizer2.png new file mode 100644 index 0000000000..fd4367c33b Binary files /dev/null and b/docs/tutorials/img/add_pokemon/visualizer2.png differ diff --git a/include/battle.h b/include/battle.h index ae73365aad..dbeb791867 100644 --- a/include/battle.h +++ b/include/battle.h @@ -771,17 +771,17 @@ struct BattleStruct // When using a move which hits multiple opponents which is then bounced by a target, we need to make sure, the move hits both opponents, the one with bounce, and the one without. u8 attackerBeforeBounce:2; u8 beatUpSlot:3; - u8 hitSwitchTargetFailed:1; + u8 pledgeMove:1; u8 effectsBeforeUsingMoveDone:1; // Mega Evo and Focus Punch/Shell Trap effects. u8 spriteIgnore0Hp:1; u8 battleBondBoost[NUM_BATTLE_SIDES]; // Bitfield for each party. u8 bonusCritStages[MAX_BATTLERS_COUNT]; // G-Max Chi Strike boosts crit stages of allies. u8 itemPartyIndex[MAX_BATTLERS_COUNT]; u8 itemMoveIndex[MAX_BATTLERS_COUNT]; - u8 pledgeMove:1; u8 isSkyBattle:1; - u32 aiDelayTimer; // Counts number of frames AI takes to choose an action. - u32 aiDelayFrames; // Number of frames it took to choose an action. + s32 aiDelayTimer; // Counts number of frames AI takes to choose an action. + s32 aiDelayFrames; // Number of frames it took to choose an action. + s32 aiDelayCycles; // Number of cycles it took to choose an action. u8 timesGotHit[NUM_BATTLE_SIDES][PARTY_SIZE]; u8 transformZeroToHero[NUM_BATTLE_SIDES]; u8 stickySyrupdBy[MAX_BATTLERS_COUNT]; diff --git a/include/global.h b/include/global.h index 4f1a861460..0ba5cd28b9 100644 --- a/include/global.h +++ b/include/global.h @@ -131,7 +131,7 @@ #define FEATURE_FLAG_ASSERT(flag, id) STATIC_ASSERT(flag > TEMP_FLAGS_END || flag == 0, id) -#ifndef NDEBUG +// NOTE: This uses hardware timers 2 and 3; this will not work during active link connections or with the eReader static inline void CycleCountStart() { REG_TM2CNT_H = 0; @@ -154,7 +154,6 @@ static inline u32 CycleCountEnd() // return result return REG_TM2CNT_L | (REG_TM3CNT_L << 16u); } -#endif struct Coords8 { diff --git a/include/pokemon.h b/include/pokemon.h index 2a772967c1..0c3deb2f72 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -813,5 +813,6 @@ u32 CheckDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler); uq4_12_t GetDynamaxLevelHPMultiplier(u32 dynamaxLevel, bool32 inverseMultiplier); u32 GetRegionalFormByRegion(u32 species, u32 region); bool32 IsSpeciesForeignRegionalForm(u32 species, u32 currentRegion); +u32 GetTeraTypeFromPersonality(struct Pokemon *mon); #endif // GUARD_POKEMON_H diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index d6f0a3a671..c352551882 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -466,6 +466,8 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) battlersCount = gBattlersCount; AI_DATA->aiCalcInProgress = TRUE; + if (DEBUG_AI_DELAY_TIMER) + CycleCountStart(); for (battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++) { if (!IsBattlerAlive(battlerAtk)) @@ -481,6 +483,9 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) SetBattlerAiMovesData(aiData, battlerAtk, battlersCount, weather); } + if (DEBUG_AI_DELAY_TIMER) + // We add to existing to compound multiple calls + gBattleStruct->aiDelayCycles += CycleCountEnd(); AI_DATA->aiCalcInProgress = FALSE; } diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 6c43ef41d8..96f3e6b67d 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -2011,14 +2011,18 @@ static void HandleChooseActionAfterDma3(u32 battler) { gBattleStruct->aiDelayFrames = gMain.vblankCounter1 - gBattleStruct->aiDelayTimer; gBattleStruct->aiDelayTimer = 0; - #if DEBUG_AI_DELAY_TIMER + if (DEBUG_AI_DELAY_TIMER) { - static const u8 sText_AIDelay[] = _("AI delay:\n{B_BUFF1} frames"); - PREPARE_HWORD_NUMBER_BUFFER(gBattleTextBuff1, 3, gBattleStruct->aiDelayFrames); - BattleStringExpandPlaceholdersToDisplayedString(sText_AIDelay); + static const u8 sFramesText[] = _(" frames thinking\n"); + static const u8 sCyclesText[] = _(" cycles"); + ConvertIntToDecimalStringN(gDisplayedStringBattle, gBattleStruct->aiDelayFrames, STR_CONV_MODE_RIGHT_ALIGN, 3); + u8* end = StringAppend(gDisplayedStringBattle, sFramesText); + ConvertIntToDecimalStringN(end, gBattleStruct->aiDelayCycles, STR_CONV_MODE_RIGHT_ALIGN, 8); + // Clear old result once read out + gBattleStruct->aiDelayCycles = 0; + StringAppend(gDisplayedStringBattle, sCyclesText); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT); } - #endif // DEBUG_AI_DELAY_TIMER } gBattlerControllerFuncs[battler] = HandleInputChooseAction; } diff --git a/src/battle_debug.c b/src/battle_debug.c index 2730223585..93872bde45 100644 --- a/src/battle_debug.c +++ b/src/battle_debug.c @@ -421,6 +421,7 @@ static const u8 sText_SubstituteHp[] = _("Substitute HP"); static const u8 sText_InLove[] = _("In Love"); static const u8 sText_Unknown[] = _("Unknown"); static const u8 sText_EmptyString[] = _(""); +static const u8 sText_IsSwitching[] = _("Switching to "); static const struct BitfieldInfo sStatus1Bitfield[] = { @@ -979,6 +980,14 @@ static void PutMovesPointsText(struct BattleDebugMenu *data) } } + if (AI_DATA->shouldSwitch & (1u << data->aiBattlerId)) + { + u32 switchMon = GetMonData(&gEnemyParty[AI_DATA->mostSuitableMonId[data->aiBattlerId]], MON_DATA_SPECIES); + + AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, sText_IsSwitching, 74, 64, 0, NULL); + AddTextPrinterParameterized(data->aiMovesWindowId, FONT_NORMAL, gSpeciesInfo[switchMon].speciesName, 74 + 68, 64, 0, NULL); + } + CopyWindowToVram(data->aiMovesWindowId, COPYWIN_FULL); Free(text); } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 2b662cdbf0..d5c64a57ed 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7364,9 +7364,7 @@ static void Cmd_moveend(void) if (GetBattlerHoldEffect(i, TRUE) == HOLD_EFFECT_RED_CARD) redCardBattlers |= (1u << i); } - if (redCardBattlers - && (moveEffect != EFFECT_HIT_SWITCH_TARGET || gBattleStruct->hitSwitchTargetFailed) - && IsBattlerAlive(gBattlerAttacker)) + if (redCardBattlers && IsBattlerAlive(gBattlerAttacker)) { // Since we check if battler was damaged, we don't need to check move result. // In fact, doing so actually prevents multi-target moves from activating red card properly @@ -7381,7 +7379,8 @@ static void Cmd_moveend(void) && IsBattlerAlive(battler) && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) && IsBattlerTurnDamaged(battler) - && CanBattlerSwitch(gBattlerAttacker)) + && CanBattlerSwitch(gBattlerAttacker) + && !(moveEffect == EFFECT_HIT_SWITCH_TARGET && CanBattlerSwitch(battler))) { effect = TRUE; gBattleScripting.moveendState = MOVEEND_OPPORTUNIST; @@ -7395,7 +7394,8 @@ static void Cmd_moveend(void) gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection BattleScriptPushCursor(); if (gBattleStruct->commanderActive[gBattlerAttacker] != SPECIES_NONE - || GetBattlerAbility(gBattlerAttacker) == ABILITY_GUARD_DOG) + || GetBattlerAbility(gBattlerAttacker) == ABILITY_GUARD_DOG + || GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) gBattlescriptCurrInstr = BattleScript_RedCardActivationNoSwitch; else gBattlescriptCurrInstr = BattleScript_RedCardActivates; @@ -7569,7 +7569,6 @@ static void Cmd_moveend(void) gSpecialStatuses[gBattlerTarget].berryReduced = FALSE; gSpecialStatuses[gBattlerTarget].distortedTypeMatchups = FALSE; gBattleScripting.moveEffect = 0; - gBattleStruct->hitSwitchTargetFailed = FALSE; gBattleStruct->isAtkCancelerForCalledMove = FALSE; gBattleStruct->swapDamageCategory = FALSE; gBattleStruct->categoryOverride = FALSE; @@ -17227,7 +17226,7 @@ void BS_ItemRestoreHP(void) // Heal is applied as move damage if battler is active. if (battler != MAX_BATTLERS_COUNT && hp != 0) { - gBattleStruct->moveDamage[gBattlerAttacker] = -healAmount; + gBattleStruct->moveDamage[battler] = -healAmount; gBattlescriptCurrInstr = cmd->restoreBattlerInstr; } else @@ -18316,13 +18315,6 @@ void BS_StoreHealingWish(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_HitSwitchTargetFailed(void) -{ - NATIVE_ARGS(); - gBattleStruct->hitSwitchTargetFailed = TRUE; - gBattlescriptCurrInstr = cmd->nextInstr; -} - void BS_TryRevivalBlessing(void) { NATIVE_ARGS(const u8 *failInstr); diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 2c140414db..0c29d20f27 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -62,7 +62,10 @@ void ApplyBattlerVisualsForTeraAnim(u32 battler) bool32 CanTerastallize(u32 battler) { u32 holdEffect = GetBattlerHoldEffect(battler, FALSE); - + + if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED && GET_BASE_SPECIES_ID(gBattleMons[battler].species) == SPECIES_TERAPAGOS) + return FALSE; + // Prevents Zigzagoon from terastalizing in vanilla. if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE && GetBattlerSide(battler) == B_SIDE_OPPONENT) return FALSE; diff --git a/src/battle_util.c b/src/battle_util.c index ad173e1303..c308417191 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6357,6 +6357,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 break; case ABILITY_WATER_VEIL: case ABILITY_WATER_BUBBLE: + case ABILITY_THERMAL_EXCHANGE: if (gBattleMons[battler].status1 & STATUS1_BURN) { StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); diff --git a/src/debug.c b/src/debug.c index 8a20e6b2b1..4443e004d8 100644 --- a/src/debug.c +++ b/src/debug.c @@ -3416,8 +3416,8 @@ static void DebugAction_Give_Pokemon_ComplexCreateMon(u8 taskId) //https://githu SetMonData(&mon, MON_DATA_DYNAMAX_LEVEL, &dmaxLevel); // tera type - if (teraType >= NUMBER_OF_MON_TYPES) - teraType = TYPE_NONE; + if (teraType == TYPE_NONE || teraType == TYPE_MYSTERY || teraType >= NUMBER_OF_MON_TYPES) + teraType = GetTeraTypeFromPersonality(&mon); SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType); //IVs diff --git a/src/pokemon.c b/src/pokemon.c index a22200bfc7..0289ca180e 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -1198,6 +1198,9 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, SetBoxMonData(boxMon, MON_DATA_POKEBALL, &value); SetBoxMonData(boxMon, MON_DATA_OT_GENDER, &gSaveBlock2Ptr->playerGender); + u32 teraType = (boxMon->personality & 0x1) == 0 ? gSpeciesInfo[species].types[0] : gSpeciesInfo[species].types[1]; + SetBoxMonData(boxMon, MON_DATA_TERA_TYPE, &teraType); + if (fixedIV < USE_RANDOM_IVS) { SetBoxMonData(boxMon, MON_DATA_HP_IV, &fixedIV); @@ -2002,16 +2005,22 @@ u16 MonTryLearningNewMove(struct Pokemon *mon, bool8 firstMove) } } - // Handler for if Zacian or Zamazenta should learn Iron Head - // since it transforms in the Behemoth Blade/Bash move in - // battle in the Crowned forms. - if (learnset[sLearningMoveTableID].move == MOVE_IRON_HEAD && (species == SPECIES_ZAMAZENTA_CROWNED || species == SPECIES_ZACIAN_CROWNED)) + // Handler for Pokémon whose moves change upon form change. + // For example, if Zacian or Zamazenta should learn Iron Head, + // they're prevented from doing if they have Behemoth Blade/Bash, + // since it transforms into them while in their Crowned forms. + const struct FormChange *formChanges = GetSpeciesFormChanges(species); + + for (u32 i = 0; formChanges != NULL && formChanges[i].method != FORM_CHANGE_TERMINATOR; i++) { - for (u32 accessor = MON_DATA_MOVE1; accessor <= MON_DATA_MOVE4; accessor++) + if (formChanges[i].method == FORM_CHANGE_END_BATTLE + && learnset[sLearningMoveTableID].move == formChanges[i].param3) { - u32 move = GetMonData(mon, accessor); - if (move == MOVE_BEHEMOTH_BLADE || move == MOVE_BEHEMOTH_BASH) - return MOVE_NONE; + for (u32 j = 0; j < MAX_MON_MOVES; j++) + { + if (formChanges[i].param2 == GetMonData(mon, MON_DATA_MOVE1 + j)) + return MOVE_NONE; + } } } @@ -3640,10 +3649,10 @@ const u16 *GetSpeciesFormTable(u16 species) const struct FormChange *GetSpeciesFormChanges(u16 species) { - const struct FormChange *evolutions = gSpeciesInfo[SanitizeSpeciesId(species)].formChangeTable; - if (evolutions == NULL) + const struct FormChange *formChanges = gSpeciesInfo[SanitizeSpeciesId(species)].formChangeTable; + if (formChanges == NULL) return gSpeciesInfo[SPECIES_NONE].formChangeTable; - return evolutions; + return formChanges; } u8 CalculatePPWithBonus(u16 move, u8 ppBonuses, u8 moveIndex) @@ -7080,3 +7089,9 @@ bool32 IsSpeciesForeignRegionalForm(u32 species, u32 currentRegion) } return FALSE; } + +u32 GetTeraTypeFromPersonality(struct Pokemon *mon) +{ + const u8 *types = gSpeciesInfo[GetMonData(mon, MON_DATA_SPECIES)].types; + return (GetMonData(mon, MON_DATA_PERSONALITY) & 0x1) == 0 ? types[0] : types[1]; +} diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 77ce6411db..7288870441 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -373,8 +373,8 @@ static u32 ScriptGiveMonParameterized(u8 side, u8 slot, u16 species, u8 level, u SetMonData(&mon, MON_DATA_DYNAMAX_LEVEL, &dmaxLevel); // tera type - if (teraType >= NUMBER_OF_MON_TYPES) - teraType = TYPE_NONE; + if (teraType == TYPE_NONE || teraType == TYPE_MYSTERY || teraType >= NUMBER_OF_MON_TYPES) + teraType = GetTeraTypeFromPersonality(&mon); SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType); // EV and IV diff --git a/test/battle/ability/thermal_exchange.c b/test/battle/ability/thermal_exchange.c new file mode 100644 index 0000000000..57f4256d7a --- /dev/null +++ b/test/battle/ability/thermal_exchange.c @@ -0,0 +1,88 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Thermal Exchange makes Will-O-Wisp fail") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP); + PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_WILL_O_WISP); MOVE(player, MOVE_CELEBRATE); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, opponent); + STATUS_ICON(player, burn: TRUE); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player); + } +} + +SINGLE_BATTLE_TEST("Thermal Exchange prevents the user from getting burned when hitting Flame Body") +{ + GIVEN { + PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } + OPPONENT(SPECIES_PONYTA) { Ability(ABILITY_FLAME_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_FLAME_BODY); + STATUS_ICON(player, burn: TRUE); + } + } +} + +SINGLE_BATTLE_TEST("Thermal Exchange cures burns when acquired") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP); + ASSUME(gMovesInfo[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP); + PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_WILL_O_WISP); MOVE(opponent, MOVE_SKILL_SWAP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, player); + STATUS_ICON(opponent, burn: TRUE); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponent); + ABILITY_POPUP(opponent, ABILITY_THERMAL_EXCHANGE); + STATUS_ICON(opponent, burn: FALSE); + NOT HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Thermal Exchange burn prevention can be bypassed with Mold Breaker but is cured after") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP); + PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } + OPPONENT(SPECIES_RAMPARDOS) { Ability(ABILITY_MOLD_BREAKER); } + } WHEN { + TURN { MOVE(opponent, MOVE_WILL_O_WISP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, opponent); + STATUS_ICON(player, burn: TRUE); + ABILITY_POPUP(player, ABILITY_THERMAL_EXCHANGE); + STATUS_ICON(player, burn: FALSE); + NOT HP_BAR(player); + } +} + +SINGLE_BATTLE_TEST("Thermal Exchange boosts attack if hit by a damaging fire type move") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_EMBER].type == TYPE_FIRE); + PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_EMBER); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent); + HP_BAR(player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + } +} diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index b58f3122b0..006b639d75 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -243,25 +243,6 @@ SINGLE_BATTLE_TEST("Dynamax: Dynamaxed Pokemon are not affected by phazing moves } } -SINGLE_BATTLE_TEST("Dynamax: Dynamaxed Pokemon are not affected by Red Card") -{ - GIVEN { - ASSUME(gItemsInfo[ITEM_RED_CARD].holdEffect == HOLD_EFFECT_RED_CARD); - PLAYER(SPECIES_WOBBUFFET); - PLAYER(SPECIES_WYNAUT); - OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); } - } WHEN { - TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); MOVE(opponent, MOVE_CELEBRATE); } - } SCENE { - MESSAGE("Wobbuffet used Max Strike!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); - MESSAGE("The opposing Wobbuffet held up its Red Card against Wobbuffet!"); - MESSAGE("The move was blocked by the power of Dynamax!"); - } THEN { - EXPECT_EQ(opponent->item, ITEM_NONE); - } -} - SINGLE_BATTLE_TEST("Dynamax: Dynamaxed Pokemon can be switched out by Eject Button") { GIVEN { @@ -1626,9 +1607,10 @@ SINGLE_BATTLE_TEST("Dynamax: Dynamax is reverted before switch out") } } -SINGLE_BATTLE_TEST("Dynamax: Destiny Bond if a dynamaxed battler is present on field") +SINGLE_BATTLE_TEST("Dynamax: Destiny Bond fails if a dynamaxed battler is present on field") { GIVEN { + ASSUME(GetMoveEffect(MOVE_DESTINY_BOND) == EFFECT_DESTINY_BOND); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/hold_effect/red_card.c b/test/battle/hold_effect/red_card.c index 8513e795b9..e7a32e3534 100644 --- a/test/battle/hold_effect/red_card.c +++ b/test/battle/hold_effect/red_card.c @@ -497,7 +497,25 @@ SINGLE_BATTLE_TEST("Red Card prevents Emergency Exit activation when triggered") } } -TO_DO_BATTLE_TEST("Red Card activates but fails if the attacker has Dynamaxed"); +SINGLE_BATTLE_TEST("Red Card activates and is consumed but fails if the attacker is Dynamaxed") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); } + } WHEN { + TURN { + MOVE(opponent, MOVE_TACKLE); + MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); + } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + MESSAGE("The opposing Wobbuffet held up its Red Card against Wobbuffet!"); + NOT MESSAGE("Wobbuffet is switched out with the Eject Button!"); + } THEN { + EXPECT_EQ(opponent->item, ITEM_NONE); + } +} SINGLE_BATTLE_TEST("Red Card activates before Eject Pack") { diff --git a/test/battle/item_effect/restore_hp.c b/test/battle/item_effect/restore_hp.c index eecbfbd29c..286b91ccfc 100644 --- a/test/battle/item_effect/restore_hp.c +++ b/test/battle/item_effect/restore_hp.c @@ -1,7 +1,7 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Items can restore a battler's HP by a fixed amount") +SINGLE_BATTLE_TEST("Items can restore a battler's HP by a fixed amount (singles, player)") { u32 item, hp; PARAMETRIZE { item = ITEM_POTION; hp = 20; } @@ -29,6 +29,162 @@ SINGLE_BATTLE_TEST("Items can restore a battler's HP by a fixed amount") } } +SINGLE_BATTLE_TEST("Items can restore a battler's HP by a fixed amount (singles, opponent)") +{ + u32 item, hp; + PARAMETRIZE { item = ITEM_POTION; hp = 20; } + PARAMETRIZE { item = ITEM_SUPER_POTION; hp = I_HEALTH_RECOVERY >= GEN_7 ? 60 : 50; } + PARAMETRIZE { item = ITEM_HYPER_POTION; hp = I_HEALTH_RECOVERY >= GEN_7 ? 120 : 200; } + PARAMETRIZE { item = ITEM_FRESH_WATER; hp = I_HEALTH_RECOVERY >= GEN_7 ? 30 : 50; } + PARAMETRIZE { item = ITEM_SODA_POP; hp = I_HEALTH_RECOVERY >= GEN_7 ? 50 : 60; } + PARAMETRIZE { item = ITEM_LEMONADE; hp = I_HEALTH_RECOVERY >= GEN_7 ? 70 : 80; } + PARAMETRIZE { item = ITEM_MOOMOO_MILK; hp = 100; } + PARAMETRIZE { item = ITEM_ENERGY_POWDER; hp = I_HEALTH_RECOVERY >= GEN_7 ? 60 : 50; } + PARAMETRIZE { item = ITEM_ENERGY_ROOT; hp = I_HEALTH_RECOVERY >= GEN_7 ? 120 : 200; } + PARAMETRIZE { item = ITEM_SWEET_HEART; hp = 20; } + PARAMETRIZE { item = ITEM_ORAN_BERRY; hp = 10; } +#if I_SITRUS_BERRY_HEAL < GEN_4 + PARAMETRIZE { item = ITEM_SITRUS_BERRY; hp = 30; } +#endif + GIVEN { + ASSUME(gItemsInfo[item].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); } + } WHEN { + TURN { USE_ITEM(opponent, item, partyIndex: 0); } + } SCENE { + HP_BAR(opponent, damage: -hp); + } +} + +DOUBLE_BATTLE_TEST("Items can restore a battler's HP (doubles, playerLeft)") +{ + s32 hp = gItemsInfo[ITEM_POTION].holdEffectParam; + GIVEN { + ASSUME(gItemsInfo[ITEM_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(playerLeft, ITEM_POTION, partyIndex: 0); } + } SCENE { + HP_BAR(playerLeft, damage: -hp); + } +} + +DOUBLE_BATTLE_TEST("Items can restore a battler's HP (doubles, playerRight)") +{ + s32 hp = gItemsInfo[ITEM_POTION].holdEffectParam; + GIVEN { + ASSUME(gItemsInfo[ITEM_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(playerRight, ITEM_POTION, partyIndex: 1); } + } SCENE { + HP_BAR(playerRight, damage: -hp); + } +} + +DOUBLE_BATTLE_TEST("Items can restore a battler's HP (doubles, opponentLeft)") +{ + s32 hp = gItemsInfo[ITEM_POTION].holdEffectParam; + GIVEN { + ASSUME(gItemsInfo[ITEM_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(opponentLeft, ITEM_POTION, partyIndex: 0); } + } SCENE { + HP_BAR(opponentLeft, damage: -hp); + } +} + +DOUBLE_BATTLE_TEST("Items can restore a battler's HP (doubles, opponentRight)") +{ + s32 hp = gItemsInfo[ITEM_POTION].holdEffectParam; + GIVEN { + ASSUME(gItemsInfo[ITEM_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); } + } WHEN { + TURN { USE_ITEM(opponentRight, ITEM_POTION, partyIndex: 1); } + } SCENE { + HP_BAR(opponentRight, damage: -hp); + } +} + +DOUBLE_BATTLE_TEST("Items can restore a partner battler's HP (playerRight to playerLeft)") +{ + s32 hp = gItemsInfo[ITEM_POTION].holdEffectParam; + GIVEN { + ASSUME(gItemsInfo[ITEM_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(playerRight, ITEM_POTION, partyIndex: 0); } + } SCENE { + HP_BAR(playerLeft, damage: -hp); + } +} + +DOUBLE_BATTLE_TEST("Items can restore a partner battler's HP (playerLeft to playerRight)") +{ + s32 hp = gItemsInfo[ITEM_POTION].holdEffectParam; + GIVEN { + ASSUME(gItemsInfo[ITEM_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(playerLeft, ITEM_POTION, partyIndex: 1); } + } SCENE { + HP_BAR(playerRight, damage: -hp); + } +} + +DOUBLE_BATTLE_TEST("Items can restore a partner battler's HP (opponentRight to opponentLeft)") +{ + s32 hp = gItemsInfo[ITEM_POTION].holdEffectParam; + GIVEN { + ASSUME(gItemsInfo[ITEM_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { USE_ITEM(opponentRight, ITEM_POTION, partyIndex: 0); } + } SCENE { + HP_BAR(opponentLeft, damage: -hp); + } +} + +DOUBLE_BATTLE_TEST("Items can restore a partner battler's HP (opponentLeft to opponentRight)") +{ + s32 hp = gItemsInfo[ITEM_POTION].holdEffectParam; + GIVEN { + ASSUME(gItemsInfo[ITEM_POTION].battleUsage == EFFECT_ITEM_RESTORE_HP); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); } + } WHEN { + TURN { USE_ITEM(opponentLeft, ITEM_POTION, partyIndex: 1); } + } SCENE { + HP_BAR(opponentRight, damage: -hp); + } +} + SINGLE_BATTLE_TEST("Items can restore a battler's HP by a percentage") { u32 item, percentage; diff --git a/test/pokemon.c b/test/pokemon.c index 97e034dc82..16aadee798 100644 --- a/test/pokemon.c +++ b/test/pokemon.c @@ -57,6 +57,8 @@ TEST("Terastallization type is reset to the default types when setting Tera Type CreateMon(&mon, SPECIES_PIDGEY, 100, 0, FALSE, 0, OT_ID_PRESET, 0); SetMonData(&mon, MON_DATA_TERA_TYPE, &teraType); EXPECT_EQ(teraType, GetMonData(&mon, MON_DATA_TERA_TYPE)); + if (typeNone == TYPE_NONE) + typeNone = GetTeraTypeFromPersonality(&mon); SetMonData(&mon, MON_DATA_TERA_TYPE, &typeNone); typeNone = GetMonData(&mon, MON_DATA_TERA_TYPE); EXPECT(typeNone == gSpeciesInfo[SPECIES_PIDGEY].types[0] diff --git a/tools/mgba/README.md b/tools/mgba/README.md index 617e6e058f..246d681d05 100644 --- a/tools/mgba/README.md +++ b/tools/mgba/README.md @@ -5,3 +5,4 @@ The source code for these specific builds is available from: - Windows: - Linux: + - Mac: diff --git a/tools/mgba/mgba-rom-test-mac b/tools/mgba/mgba-rom-test-mac new file mode 100755 index 0000000000..43c8976c09 Binary files /dev/null and b/tools/mgba/mgba-rom-test-mac differ