1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
|
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 category check macro](#2-modifying-the-category-check-macro)
3. [Modifying the damage calculation logic](#3-modifying-the-damage-calculation-logic)
4. [Adding the category byte to moves](#4-adding-the-category-byte-to-moves)
5. [A Note on AI Scripts](#5-a-note-on-ai-scripts)
## 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, special, or status. I will be using the name "category" 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 status. After adding these, our modified struct looks like this:
```c
#define MOVE_CATEGORY_PHYSICAL 0
#define MOVE_CATEGORY_SPECIAL 1
#define MOVE_CATEGORY_STATUS 2
struct BattleMove
{
u8 effect;
u8 power;
u8 type;
u8 accuracy;
u8 pp;
u8 secondaryEffectChance;
u8 target;
s8 priority;
u8 flags;
u8 category;
};
```
## 2. Modifying the category 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 category byte of the move instead of the move's type. For my definition, it looks like this:
```c
#define IS_MOVE_PHYSICAL(move)(gBattleMoves[move].category == MOVE_CATEGORY_PHYSICAL)
#define IS_MOVE_SPECIAL(move)(gBattleMoves[move].category == MOVE_CATEGORY_SPECIAL)
#define IS_MOVE_STATUS(move)(gBattleMoves[move].category == MOVE_CATEGORY_STATUS)
```
## 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;
}
}
```
### Weather
We also need to make a similar change to the boosts that weather provides. In Gen 3, all types that can be boosted by weather are special types, so the code only checks for the boost if a special move is being used. All of the weather checks are conveniently bunched together, so we can just cut this section and paste it outside of the special check block:
```c
if (WEATHER_HAS_EFFECT2)
{
if (gBattleWeather & WEATHER_RAIN_TEMPORARY)
{
switch (type)
{
case TYPE_FIRE:
damage /= 2;
break;
case TYPE_WATER:
damage = (15 * damage) / 10;
break;
}
}
// any weather except sun weakens solar beam
if ((gBattleWeather & (WEATHER_RAIN_ANY | WEATHER_SANDSTORM_ANY | WEATHER_HAIL_ANY)) && gCurrentMove == MOVE_SOLAR_BEAM)
damage /= 2;
// sunny
if (gBattleWeather & WEATHER_SUN_ANY)
{
switch (type)
{
case TYPE_FIRE:
damage = (15 * damage) / 10;
break;
case TYPE_WATER:
damage /= 2;
break;
}
}
```
The special check block ends just before the line that says `return damage + 2;`, so use that as a reference if you're lost in the `if` blocks. Another detail; if you want to be efficient, you can leave the Solar Beam code in the special block since it will only ever be special, but moving it out of the block does not cause it to stop working--instead, it will just be checked for every move, instead of just for special moves.
### Flash Fire
Just like weather, since Flash Fire only affects Fire-type moves, which are all special in Gen 3, the check for Flash Fire only occurs in the special block. It's actually located right after the weather checks, so you can cut and paste this check outside of the block along with them:
```c
if ((gBattleResources->flags->flags[battlerIdAtk] & RESOURCE_FLAG_FLASH_FIRE) && type == TYPE_FIRE)
damage = (15 * damage) / 10;
```
### Thick Fat
Similarly to Flash Fire, Thick Fat only affects Fire and Ice moves, which are all special. For that reason, the damage calculation divides the special attack of the attacking Pokemon in half to reduce the damage taken. We will need to change this to reduce the base power of the move instead, so that it can apply to any category or move of the affected types: (**src/pokemon.c**)
```c
if (defender->ability == ABILITY_THICK_FAT && (type == TYPE_FIRE || type == TYPE_ICE))
gBattleMovePower /= 2;
```
The next thing we need to do is change the arguments passed to IS_MOVE_PHYSICAL and IS_MOVE_SPECIAL. Originally, they were passed a type argument, but now we want to pass a move argument. There are two instances of these functions in **src/pokemon.c**, three more in **src/battle_script_commands.c**, and two more in **src/battle_tv.c**.
In **pokemon.c**, the first one originally reads like this:
```c
IS_TYPE_PHYSICAL(type)
```
We will change this to:
```c
IS_MOVE_PHYSICAL(gCurrentMove)
```
And the second one we will need to change from:
```c
IS_TYPE_SPECIAL(type)
```
to
```c
IS_MOVE_SPECIAL(gCurrentMove)
```
The ones in **battle_script_commands.c** can also be changed like this, but the argument names are different. The first one (related to the Hustle ability) looks like this:
```c
IS_TYPE_PHYSICAL(moveType)
```
We will change this to:
```c
IS_MOVE_PHYSICAL(move)
```
**NOTE:** For this change only I used `move` rather than `gCurrentMove` to match other uses of `move` in that function, but either should work.
The second instance we need to change looks like this originally:
```c
IS_TYPE_PHYSICAL(moveType)
```
We will change this to:
```c
IS_MOVE_PHYSICAL(gCurrentMove)
```
The third one also needs to be changed; originally it reads:
```c
!IS_TYPE_PHYSICAL(moveType)
```
We will change this to:
```c
IS_MOVE_SPECIAL(gCurrentMove)
```
**Note:** We have changed `!IS_TYPE_PHYSICAL` to `IS_TYPE_SPECIAL` because "not physical" no longer automatically means "special" due to the introduction of the "status" option.
And lastly, in **battle_tv.c** the two `IS_TYPE_PHYSICAL` and `IS_TYPE_SPECIAL` can be respectfully changed to:
```c
IS_MOVE_PHYSICAL(gCurrentMove)
```
and
```c
IS_MOVE_SPECIAL(gCurrentMove)
```
## 4. Adding the category 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 status. 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 category 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,
.category = MOVE_CATEGORY_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,
.category = MOVE_CATEGORY_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,
.category = MOVE_CATEGORY_STATUS,
},
```
## 5. A Note on AI Scripts
The last thing that should be noted is how AI Scripts relating to physical/special moves work in these games. The file **data/battle_ai_scripts.s** contains the code for the battle AI and how it chooses which moves to use. The way it does this is not by calling the macro that the damage calculation code uses; instead, it has multiple internal tables used to keep track of which types are physical and which are special. Note that this does not affect any actual damage calculation, and its effect on the AI is limited in the first place. The three things affected are:
1. The likelihood of the AI using an attack-lowering vs. a special attack-lowering move
2. The likelihood of the AI using Reflect vs. Light Screen
3. The likelihood of the AI using Counter vs. Mirror Coat
An important note is that these decisions are all made based on the types of the opposing Pokemon itself rather than the moves it has, so the original check is pretty bad to begin with--in fact, it's arguable whether you would even notice the difference. Correcting the AI to check for the category of moves instead of the types of Pokemon is beyond the scope of this tutorial and is more suited to a general AI overhaul tutorial, but I wanted to mention it here to be totally clear about what's happening with these calculations.
|