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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
|
This tutorial is for how to add a new trainer class. As an example, we'll add the Parasol Lady class.
## Contents
1. [Define a trainer class constant](#1-define-a-trainer-class-constant)
2. [Give them a name](#2-give-them-a-name)
3. [Define their attributes](#3-define-their-attributes)
4. [Define their DVs](#4-define-their-dvs)
5. [Define their encounter music](#5-define-their-encounter-music)
6. [Design their sprite](#6-design-their-sprite)
7. [Define their individual parties](#7-define-their-individual-parties)
8. [Define their Battle Tower sprite and gender](#8-define-their-battle-tower-sprite-and-gender)
9. [Fix bank overflow errors](#9-fix-bank-overflow-errors)
## 1. Define a trainer class constant
Edit [constants/trainer_constants.asm](../blob/master/constants/trainer_constants.asm):
```diff
; trainer class ids
; `trainerclass` indexes are for:
; - TrainerClassNames (see data/trainers/class_names.asm)
; - TrainerClassAttributes (see data/trainers/attributes.asm)
; - TrainerClassDVs (see data/trainers/dvs.asm)
; - TrainerGroups (see data/trainers/party_pointers.asm)
; - TrainerEncounterMusic (see data/trainers/encounter_music.asm)
; - TrainerPicPointers (see data/trainers/pic_pointers.asm)
; - TrainerPalettes (see data/trainers/palettes.asm)
; - BTTrainerClassSprites (see data/trainers/sprites.asm)
; - BTTrainerClassGenders (see data/trainers/genders.asm)
; trainer constants are Trainers indexes, for the sub-tables of TrainerGroups (see data/trainers/parties.asm)
CHRIS EQU __trainer_class__
trainerclass TRAINER_NONE ; 0
...
KRIS EQU __trainer_class__
trainerclass FALKNER ; 1
const FALKNER1
...
trainerclass MYSTICALMAN ; 43
const EUSINE
+ trainerclass PARASOL_LADY
+ const SUE
NUM_TRAINER_CLASSES EQU __trainer_class__ - 1
```
The `trainerclass` macro defines the next trainer class constant, and prepares to define a sequence of constants for individual trainers. Here we've defined `SUE` as the only Parasol Lady.
Note the `CHRIS` and `KRIS` constants, equal to 0 and 1 respectively; they're used for getting the correct color palette when displaying the player's sprite in the introduction and on the trainer card. `KRIS` is equal to `FALKNER`, which is why they share a palette.
Be careful when naming trainer constants; either make them unique, or make them unambiguous (like `BUG_CATCHER_BENNY` and `BIKER_BENNY`). I once made a trainer constant `SPARK` and caused a bug because `SPARK` was already a move constant. (That's why we have suffixes for `BLACKBELT_T` [trainer] and `BLACKBELT_I` [item], or `PSYCHIC_T` [trainer], `PSYCHIC_M` [move], and `PSYCHIC_TYPE` [type].)
Next we'll add data for the new `PARASOL_LADY` class to all those tables mentioned in the top comment.
## 2. Give them a name
Edit [data/trainers/class_names.asm](../blob/master/data/trainers/class_names.asm):
```diff
TrainerClassNames::
; entries correspond to trainer classes (see constants/trainer_constants.asm)
list_start TrainerClassNames
li "LEADER"
...
li "MYSTICALMAN"
+ li "PARASOL LADY"
assert_list_length NUM_TRAINER_CLASSES
```
A name can be up to 12 characters long. Note that the trainer class and individual name will get printed on one line in phrases like "PARASOL LADY SUE wants to battle!" so make sure the whole phrase will fit in 18 characters.
## 3. Define their attributes
Edit [data/trainers/attributes.asm](../blob/master/data/trainers/attributes.asm):
```diff
TrainerClassAttributes:
; entries correspond to trainer classes (see constants/trainer_constants.asm)
; Falkner
db NO_ITEM, NO_ITEM ; items
db 25 ; base reward
dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
dw CONTEXT_USE | SWITCH_SOMETIMES
...
; Mysticalman
db NO_ITEM, NO_ITEM ; items
db 25 ; base reward
dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
dw CONTEXT_USE | SWITCH_SOMETIMES
+
+; Parasol Lady
+ db NO_ITEM, NO_ITEM ; items
+ db 10 ; base reward
+ dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_STATUS
+ dw CONTEXT_USE | SWITCH_SOMETIMES
assert_table_length NUM_TRAINER_CLASSES
```
"Attributes" encompass a number of different properties:
- **items:** Two items, each of which a trainer can use once. They may be the same item or `NO_ITEM`. The AI engine has to know how to use the items, as defined by `AI_Items` in [engine/battle/ai/items.asm](../blob/master/engine/battle/ai/items.asm).
- **base reward:** The base monetary reward for beating a trainer. The reward amount is 4×*B*×*L*, where *B* is the base reward and *L* is the level of the last Pokémon they used.
- **AI flags (move weights):** Bit flags that control how the trainer's AI chooses a move to use. Valid flags, with descriptions from [engine/battle/ai/scoring.asm](../blob/master/engine/battle/ai/scoring.asm):
- `NO_AI`:
- `AI_BASIC`: Don't do anything redundant:
- Using status-only moves if the player can't be statused (see [data/battle/ai/status_only_effects.asm](../blob/master/data/battle/ai/status_only_effects.asm))
- Using moves that fail if they've already been used
- `AI_SETUP`: Use stat-modifying moves on turn 1:
- 50% chance to greatly encourage stat-up moves during the first turn of enemy's Pokémon
- 50% chance to greatly encourage stat-down moves during the first turn of player's Pokémon
- Almost 90% chance to greatly discourage stat-modifying moves otherwise
- `AI_TYPES`: Dismiss any move that the player is immune to. Encourage super-effective moves. Discourage not-very-effective moves unless all damaging moves are of the same type.
- `AI_OFFENSIVE`: Greatly discourage non-damaging moves.
- `AI_SMART`: Context-specific scoring. Special cases for many different move effects
- `AI_OPPORTUNIST`: Discourage stall moves when the enemy's HP is low (see [data/battle/ai/stall_moves.asm](../blob/master/data/battle/ai/stall_moves.asm)).
- `AI_AGGRESSIVE`: Discourage all damaging moves but the one that does the most damage. Reckless moves are not discouraged (see [data/battle/ai/reckless_moves.asm](../blob/master/data/battle/ai/reckless_moves.asm)).
- `AI_CAUTIOUS`: 90% chance to discourage moves with residual effects after the first turn (see [data/battle/ai/residual_moves.asm](../blob/master/data/battle/ai/residual_moves.asm)).
- `AI_STATUS`: Dismiss status moves that don't affect the player.
- `AI_RISKY`: Use any move that will KO the target. Risky moves will often be an exception (see [data/battle/ai/risky_effects.asm](../blob/master/data/battle/ai/risky_effects.asm)).
- **AI flags (item/switch):** Bit flags that control how the trainer's AI chooses to use an item or switch Pokémon instead of attacking. Combine one `*_USE` flag and one `SWITCH_*` flag. Valid flags:
- `CONTEXT_USE`:
- `UNKNOWN_USE`:
- `ALWAYS_USE`:
- `SWITCH_SOMETIMES`:
- `SWITCH_RARELY`:
- `SWITCH_OFTEN`:
## 4. Define their DVs
Edit [data/trainers/dvs.asm](../blob/master/data/trainers/dvs.asm):
```diff
TrainerClassDVs:
; entries correspond to trainer classes (see constants/trainer_constants.asm)
; atk,def,spd,spc
dn 9, 10, 7, 7 ; FALKNER
...
dn 9, 8, 8, 8 ; MYSTICALMAN
+ dn 7, 8, 8, 8 ; parasol lady
assert_table_length NUM_TRAINER_CLASSES
```
The four numbers define, in order, the Attack, Defense, Speed, and Special DVs for all the trainer class's Pokémon. Each DV can be from 0 to 15. (Remember, in Gen 2 there was one DV for both Special Attack and Special Defense; and bits from all four DVs were combined to calculate HP.)
Female trainer classes tend to have low Attack DVs so that their Pokémon will usually be female (since gender in Gen 2 was determined by the Attack and Speed DVs, primarily Attack). Gym Leaders are mostly an exception to this rule.
You may also want to choose DVs that give some important Parasol Lady the right Hidden Power type, if that's relevant. Or follow [this tutorial](Allow-more-trainer-parties,-with-individual-DVs,-stat-experience,-nicknames,-variable-teams,-etc.) to give individual trainers unique DVs (as well as stat experience and nicknames!).
## 5. Define their encounter music
Edit [data/trainers/encounter_music.asm](../blob/master/data/trainers/encounter_music.asm):
```diff
TrainerEncounterMusic::
; entries correspond to trainer classes (see constants/trainer_constants.asm)
db MUSIC_HIKER_ENCOUNTER ; none
...
db MUSIC_HIKER_ENCOUNTER ; mysticalman
+ db MUSIC_BEAUTY_ENCOUNTER ; parasol lady
assert_table_length NUM_TRAINER_CLASSES + 1
- db MUSIC_HIKER_ENCOUNTER
- db MUSIC_HIKER_ENCOUNTER
- db MUSIC_HIKER_ENCOUNTER
```
There aren't many to choose from; it's usually `MUSIC_YOUNGSTER_ENCOUNTER` or `MUSIC_HIKER_ENCOUNTER` for male trainers, `MUSIC_LASS_ENCOUNTER` or `MUSIC_BEAUTY_ENCOUNTER` for female ones, plus some more specialized `MUSIC_*_ENCOUNTER` tunes.
Notice that we removed three extra `MUSIC_HIKER_ENCOUNTER`s at the end without any corresponding trainer classes.
If you want different music to play *during* battle, you'll need to edit `PlayBattleMusic` in [engine/battle/start_battle.asm](../blob/master/engine/battle/start_battle.asm). It's a series of [hard-coded logic](Hard-coded-logic#trainer-classes-with-different-battle-music) checks for various conditions under which to play special battle music. Decide carefully where to place a check for your new condition—like whether `[wOtherTrainerClass]` is `PARASOL_LADY`, or whether `[wOtherTrainerClass]` is `PARASOL_LADY` *and* `[wOtherTrainerID]` is `SUE`—so that you don't break assumptions made by the other checks.
## 6. Design their sprite
Create **gfx/trainers/parasol_lady.png**:

Trainer sprites are all 56x56 pixels, and use four colors: white, black, and two arbitrary hues.
Next edit [data/trainers/pic_pointers.asm](../blob/master/data/trainers/pic_pointers.asm):
```diff
TrainerPicPointers::
; entries correspond to trainer classes (see constants/trainer_constants.asm)
dba_pic FalknerPic
...
dba_pic MysticalmanPic
+ dba_pic ParasolLadyPic
assert_table_length NUM_TRAINER_CLASSES
```
We have to use `dba_pic` here instead of a standard `dba`—declaring the bank and address of `ParasolLadyPic`—because of [this design flaw](../blob/master/docs/design_flaws.md#pic-banks-are-offset-by-pics_fix). I strongly recommend removing the whole `FixPicBank` routine from [engine/gfx/load_pics.asm](../blob/master/engine/gfx/load_pics.asm), including all four calls to it in that file, and just using `dba` here; then you'll be able to `INCBIN` sprites in arbitrary banks.
Edit [gfx/pics.asm](../blob/master/gfx/pics.asm):
```diff
SECTION "Pics 19", ROMX
-; Seems to be an accidental copy of the previous bank
-
-INCBIN "gfx/pokemon/spinarak/back.2bpp.lz"
-...
-INCBIN "gfx/pokemon/unown_r/back.2bpp.lz"
+ParasolLadyPic: INCBIN "gfx/trainers/parasol_lady.2bpp.lz"
```
(If you *don't* fix the `dba_pic` design flaw, you'll have to put your sprites in the "Pics *N*" sections, which are compatible with `dba_pic`. "Pics 19" isn't used for anything useful—all its contents are unused duplicates of "Pics 18"—and it has a whole bank to itself, so it's the easiest place to start adding new sprites. (The other sections, includng "Pics 20" through "Pics 24", have limited remaining space since they already contain some sprites and/or share their banks with other sections.) But if you have a lot of new sprites to add, you risk overflowing the banks, and it's hard to fit sprites within fixed bank limits. By using just `dba`, you can create new sections with a few sprites each, that will automatically be placed wherever they can fit in the ROM.)
Anyway, edit [data/trainers/palettes.asm](../blob/master/data/trainers/palettes.asm):
```diff
TrainerPalettes:
; entries correspond to trainer classes
; Each .gbcpal is generated from the corresponding .png, and
; only the middle two colors are included, not black or white.
PlayerPalette: ; Chris uses the same colors as Cal
INCBIN "gfx/trainers/cal.gbcpal", middle_colors
KrisPalette: ; Kris shares Falkner's palette
INCBIN "gfx/trainers/falkner.gbcpal", middle_colors
...
INCBIN "gfx/trainers/mysticalman.gbcpal", middle_colors
+INCBIN "gfx/trainers/parasol_lady.gbcpal", middle_colors
assert_table_length NUM_TRAINER_CLASSES + 1
```
parasol_lady.2bpp.lz and parasol_lady.gbcpal will be automatically generated from parasol_lady.png when you run `make`.
(Older versions of pokecrystal would `INCLUDE "parasol_lady.pal"` instead of `INCBIN "parasol_lady.gbcpal", middle_colors`. This is because they generated .pal files instead of .gbcpal, but this was redundant work and could mislead users into editing .pal files directly, so as of September 2018 they were removed.)
## 7. Define their individual parties
Edit [data/trainers/party_pointers.asm](../blob/master/data/trainers/party_pointers.asm):
```diff
TrainerGroups:
; entries correspond to trainer classes (see constants/trainer_constants.asm)
dw FalknerGroup
...
dw MysticalmanGroup
+ dw ParasolLadyGroup
assert_table_length NUM_TRAINER_CLASSES
```
Then edit [data/trainers/parties.asm](../blob/master/data/trainers/parties.asm):
```diff
FalknerGroup:
; FALKNER (1)
db "FALKNER@", TRAINERTYPE_MOVES
db 7, PIDGEY, TACKLE, MUD_SLAP, NO_MOVE, NO_MOVE
db 9, PIDGEOTTO, TACKLE, MUD_SLAP, GUST, NO_MOVE
db -1 ; end
...
MysticalmanGroup:
; MYSTICALMAN (1)
db "EUSINE@", TRAINERTYPE_MOVES
db 23, DROWZEE, DREAM_EATER, HYPNOSIS, DISABLE, CONFUSION
db 23, HAUNTER, LICK, HYPNOSIS, MEAN_LOOK, CURSE
db 25, ELECTRODE, SCREECH, SONICBOOM, THUNDER, ROLLOUT
db -1 ; end
+
+ParasolLadyGroup:
+ ; PARASOL_LADY (1)
+ db "SUE@", TRAINERTYPE_NORMAL
+ db 28, GOLDEEN
+ db 30, GOLDUCK
+ db -1 ; end
```
The comment at the top of parties.asm explains the data structure:
```
; Trainer data structure:
; - db "NAME@", TRAINERTYPE_* constant
; - 1 to 6 Pokémon:
; * for TRAINERTYPE_NORMAL: db level, species
; * for TRAINERTYPE_MOVES: db level, species, 4 moves
; * for TRAINERTYPE_ITEM: db level, species, item
; * for TRAINERTYPE_ITEM_MOVES: db level, species, item, 4 moves
; - db -1 ; end
```
## 8. Define their Battle Tower sprite and gender
If a trainer class is used in Battle Tower, as defined in [data/battle_tower/classes.asm](../blob/master/data/battle_tower/classes.asm), it will need a defined sprite and gender. The sprite is used when the trainer walks into the room, and their gender determines what they say. Even if you don't add a Parasol Lady to the Battle Tower roster, it's helpful to keep the data tables up-to-date.
Edit [data/trainers/sprites.asm](../blob/master/data/trainers/sprites.asm):
```diff
BTTrainerClassSprites:
; entries correspond to trainer classes
db SPRITE_FALKNER
...
db SPRITE_ROCKET_GIRL
- assert_table_length NUM_TRAINER_CLASSES - 1 ; exclude MYSTICALMAN
+ db SPRITE_SUPER_NERD
+ db SPRITE_TEACHER
+ assert_table_length NUM_TRAINER_CLASSES
```
Valid sprites are in [constants/sprite_constants.asm](../blob/master/constants/sprite_constants.asm). They're for the 16x16 overworld sprites, not the 56x56 battle sprites. Adding a custom sprite is beyond the scope of this tutorial.
Notice that there was no sprite for `MYSTICALMAN`, so we had to add one so that `SPRITE_TEACHER` would correspond with `PARASOL_LADY`.
Anyway, edit [data/trainers/genders.asm](../blob/master/data/trainers/genders.asm):
```diff
BTTrainerClassGenders:
; entries correspond to trainer classes
table_width 1, BTTrainerClassGenders
db MALE ; FALKNER
...
db FEMALE ; GRUNTF
- assert_table_length NUM_TRAINER_CLASSES - 1 ; exclude MYSTICALMAN
+ db MALE ; MYSTICALMAN
+ db FEMALE ; PARASOL_LADY
+ assert_table_length NUM_TRAINER_CLASSES
```
Again, we had to add data for `MYSTICALMAN` to reach the slot for `PARASOL_LADY`.
Also edit [data/trainers/gendered_trainers.asm](../blob/master/data/trainers/gendered_trainers.asm):
```diff
FemaleTrainers:
db MEDIUM
db LASS
db BEAUTY
db SKIER
db TEACHER
db SWIMMERF
db PICNICKER
db KIMONO_GIRL
db POKEFANF
db COOLTRAINERF
+ db PARASOL_LADY
.End
```
For whatever reason, trainers' gender is checked in two redundant ways, so we just have to keep both tables up-to-date.
## 9. Fix bank overflow errors
We're done adding the Parasol Lady data, but `make` won't compile the ROM:
```
error: Unable to place 'Pics 3' (ROMX section) at $40CC in bank $4A
```
But we didn't change anything in "Pics 3", so why is this happening?
As defined in [layout.link](../blob/master/layout.link), the "Trainer Pic Pointers" and "Pics 3" sections are both in bank $4A:
```
ROMX $4a
"Trainer Pic Pointers"
"Pics 3"
```
It turns out that adding `dba_pic ParasolLadyPic` to the `TrainerPicPointers` table was a little too much data for that bank. To fix this, we'll need to move some data out of bank $4A. "Trainer Pic Pointers" has to be as large as it is, but "Pics 3" is an arbitrary set of sprites, so we can move one of those.
Edit [gfx/pics.asm](../blob/master/gfx/pics.asm) again:
```diff
SECTION "Pics 3", ROMX
SteelixFrontpic: INCBIN "gfx/pokemon/steelix/front.animated.2bpp.lz"
AlakazamFrontpic: INCBIN "gfx/pokemon/alakazam/front.animated.2bpp.lz"
GyaradosFrontpic: INCBIN "gfx/pokemon/gyarados/front.animated.2bpp.lz"
KangaskhanFrontpic: INCBIN "gfx/pokemon/kangaskhan/front.animated.2bpp.lz"
RhydonFrontpic: INCBIN "gfx/pokemon/rhydon/front.animated.2bpp.lz"
GolduckFrontpic: INCBIN "gfx/pokemon/golduck/front.animated.2bpp.lz"
RhyhornFrontpic: INCBIN "gfx/pokemon/rhyhorn/front.animated.2bpp.lz"
PidgeotFrontpic: INCBIN "gfx/pokemon/pidgeot/front.animated.2bpp.lz"
SlowbroFrontpic: INCBIN "gfx/pokemon/slowbro/front.animated.2bpp.lz"
ButterfreeFrontpic: INCBIN "gfx/pokemon/butterfree/front.animated.2bpp.lz"
WeezingFrontpic: INCBIN "gfx/pokemon/weezing/front.animated.2bpp.lz"
CloysterFrontpic: INCBIN "gfx/pokemon/cloyster/front.animated.2bpp.lz"
SkarmoryFrontpic: INCBIN "gfx/pokemon/skarmory/front.animated.2bpp.lz"
DewgongFrontpic: INCBIN "gfx/pokemon/dewgong/front.animated.2bpp.lz"
VictreebelFrontpic: INCBIN "gfx/pokemon/victreebel/front.animated.2bpp.lz"
RaichuFrontpic: INCBIN "gfx/pokemon/raichu/front.animated.2bpp.lz"
PrimeapeFrontpic: INCBIN "gfx/pokemon/primeape/front.animated.2bpp.lz"
-OmastarBackpic: INCBIN "gfx/pokemon/omastar/back.2bpp.lz"
...
SECTION "Pics 19", ROMX
ParasolLadyPic: INCBIN "gfx/trainers/parasol_lady.2bpp.lz"
+OmastarBackpic: INCBIN "gfx/pokemon/omastar/back.2bpp.lz"
```
Now we're done! You can write an event script that uses Parasol Lady Sue just like any other trainer.

|