diff --git a/Add-Physical-Special-Split.md b/Add-Physical-Special-Split.md new file mode 100644 index 0000000..8747d3d --- /dev/null +++ b/Add-Physical-Special-Split.md @@ -0,0 +1,144 @@ +This tutorial is for adding the per-move physical/special split that was implemented starting in Diamond/Pearl. +## Contents + +1. [Adding a byte to the move struct](#1-adding-a-byte-to-the-move-struct) +2. [Modifying the physicality check macro](#2-modifying-the-physicality-check-macro) +3. [Modifying the damage calculation logic](#3-modifying-the-damage-calculation-logic) +4. [Adding the physicality byte to moves](#4-adding-the-physicality-byte-to-moves) + +## 1. Adding a byte to the move struct +The struct that defines the properties of a move are located in **include/pokemon.h**, so we will be modifying that file to include a byte that determines whether the move is physical or special. We will also be adding some macros to make the values more descriptive. Before editing, the struct looks like this: + +```c +struct BattleMove +{ + u8 effect; + u8 power; + u8 type; + u8 accuracy; + u8 pp; + u8 secondaryEffectChance; + u8 target; + s8 priority; + u8 flags; +}; +``` +All we need to do is add another field to determine if the move is physical or special. I will be using the name "physicality" for this, but the name can be anything as long as it is consistent. We will also add some new macros for the possible values of this field. Again, the values can be anything as long as consistency is maintained, but I will be using 0 for physical, 1 for special, and 2 for other/status. After adding these, our modified struct looks like this: +```c +#define MOVE_PHYSICALITY_PHYSICAL 0 +#define MOVE_PHYSICALITY_SPECIAL 1 +#define MOVE_PHYSICALITY_OTHER 2 + +struct BattleMove +{ + u8 effect; + u8 power; + u8 type; + u8 accuracy; + u8 pp; + u8 secondaryEffectChance; + u8 target; + s8 priority; + u8 flags; + u8 physicality; +}; +``` + +## 2. Modifying the physicality check macro +The game uses a function-style macro to check whether a move is physical or special based on its type. The macro lives in **include/battle.h** and looks like this: +```c +#define IS_TYPE_PHYSICAL(moveType)(moveType < TYPE_MYSTERY) +#define IS_TYPE_SPECIAL(moveType)(moveType > TYPE_MYSTERY) +``` +The macro simply looks at the move and compares the index of its type to TYPE_MYSTERY, which is 0x9. Types are split down the middle by TYPE_MYSTERY; all physical types are before it, and all special types are after it, so a simple comparison does the job. We will change this to look at the physicality byte of the move instead of the move's type. For my definition, it looks like this: +```c +#define IS_TYPE_PHYSICAL(move)(move.physicality == MOVE_PHYSICALITY_PHYSICAL) +#define IS_TYPE_SPECIAL(move)(move.physicality == MOVE_PHYSICALITY_SPECIAL) +``` + +## 3. Modifying the damage calculation logic +Next, we need to update a few checks in the function for calculating the base damage of a move. First, we will look at **src/pokemon.c**. The function we want here is: +```c +s32 CalculateBaseDamage(struct BattlePokemon *attacker, struct BattlePokemon *defender, u32 move, u16 sideStatus, u16 powerOverride, u8 typeOverride, u8 battlerIdAtk, u8 battlerIdDef) +``` +First, we need to look at the block of code that begins like +```c +for (i = 0; i < ARRAY_COUNT(sHoldEffectToType); i++) + { + if (attackerHoldEffect == sHoldEffectToType[i][0] + && type == sHoldEffectToType[i][1]) +``` +This is the code used to apply bonuses from type-boosting items like Miracle Seed, Charcoal, Magnet, etc. The original function checks whether the type is physical or special and then only boosts the corresponding stat. Since we've made the physical/special split on a per-move basis, we'll just boost both stats to ensure the move gets boosted. This is as simple as removing a check, and when done, the block of code should look like this: +```c +for (i = 0; i < ARRAY_COUNT(sHoldEffectToType); i++) + { + if (attackerHoldEffect == sHoldEffectToType[i][0] + && type == sHoldEffectToType[i][1]) + { + attack = (attack * (attackerHoldEffectParam + 100)) / 100; + spAttack = (spAttack * (attackerHoldEffectParam + 100)) / 100; + break; + } + } +``` +The next thing we need to do is change the arguments passed to IS_TYPE_PHYSICAL and IS_TYPE_SPECIAL. Originally, they were passed a type argument, but now we want to pass a move argument. There is one instance of each of these functions in **src/pokemon.c**, and there are three more in **src/battle_script_commands.c**. The ones in **pokemon.c** should be changed from IS_TYPE_PHYSICAL(type) and IS_TYPE_SPECIAL(type) to IS_TYPE_PHYSICAL(gBattleMoves[move]) and IS_TYPE_SPECIAL(gBattleMoves[move]). The first one in **battle_script_commands.c** can also be changed like this, but the argument names are different for the second and third one. The second one looks like this originally: +```c +IS_TYPE_PHYSICAL(moveType) +``` +We will change this to: +```c +IS_TYPE_PHYSICAL(gBattleMoves[gCurrentMove]) +``` +The third one also needs to be changed; originally it reads: +```c +!IS_TYPE_PHYSICAL(moveType) +``` +We will change this to: +```c +IS_TYPE_SPECIAL(gBattleMoves[gCurrentMove]) +``` +Note that we have changed !IS_TYPE_PHYSICAL to IS_TYPE_SPECIAL; "not physical" no longer automatically means "special" due to the introduction of the "other/status" option. +## 4. Adding the physicality byte to moves +The last step is definitely the most tedious, but it is very simple. We need to go through every move and define whether it is physical, special, or other. The file that defines all move effects is located at **src/data/battle_moves.h**. There are plenty of resources to find out which one a move is if you do not already know. After adding these, the split is implemented. For reference, here is one example of each possible value of the physicality byte for my names and values: +```c +{ // MOVE_ICE_PUNCH + .effect = EFFECT_FREEZE_HIT, + .power = 75, + .type = TYPE_ICE, + .accuracy = 100, + .pp = 15, + .secondaryEffectChance = 10, + .target = MOVE_TARGET_SELECTED, + .priority = 0, + .flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED, + .physicality = MOVE_PHYSICALITY_PHYSICAL, +}, +``` +```c +{ // MOVE_GUST + .effect = EFFECT_GUST, + .power = 40, + .type = TYPE_FLYING, + .accuracy = 100, + .pp = 35, + .secondaryEffectChance = 0, + .target = MOVE_TARGET_SELECTED, + .priority = 0, + .flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGSROCK_AFFECTED, + .physicality = MOVE_PHYSICALITY_SPECIAL, +}, +``` +```c +{ // MOVE_SAND_ATTACK + .effect = EFFECT_ACCURACY_DOWN, + .power = 0, + .type = TYPE_GROUND, + .accuracy = 100, + .pp = 15, + .secondaryEffectChance = 0, + .target = MOVE_TARGET_SELECTED, + .priority = 0, + .flags = FLAG_PROTECT_AFFECTED | FLAG_MAGICCOAT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED, + .physicality = MOVE_PHYSICALITY_OTHER, +}, +``` \ No newline at end of file