summaryrefslogtreecommitdiff
path: root/Add-a-new-TM-or-HM.md
blob: 6d354e3336b91aadcac018319d42fd2a93e20252 (plain)
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
This tutorial is for how to add a new TM or HM. As an example, we'll add TM51 Aeroblast and HM08 Softboiled.

(I'm using Softboiled as an HM example because it already has a field effect outside of battle. Creating new field-effect moves is beyond the scope of this tutorial.)


## Contents

1. [Define constants with `add_tm` or `add_hm`](#1-define-constants-with-add_tm-or-add_hm)
2. [Define standard item data](#2-define-standard-item-data)
3. [Update the TM/HM move table](#3-update-the-tmhm-move-table)
4. [Add the TM or HM to base learnsets](#4-add-the-tm-or-hm-to-base-learnsets)
5. [Make the HM move unforgettable](#5-make-the-hm-move-unforgettable)
6. [Adding up to 120 new TMs or HMs](#6-adding-up-to-120-new-tms-or-hms)


## 1. Define constants with `add_tm` or `add_hm`

Edit [constants/item_constants.asm](../blob/master/constants/item_constants.asm):

```diff
 ...
 ; see data/moves/tmhm_moves.asm for moves
 TM01 EQU const_value
 	add_tm DYNAMICPUNCH ; bf
 	...
 	add_tm NIGHTMARE    ; f2
+	add_tm AEROBLAST
 NUM_TMS EQU __tmhm_value__ - 1

 ...

 	add_hm CUT          ; f3
 	...
 	add_hm WATERFALL    ; f9
+	add_hm SOFTBOILED
 NUM_HMS EQU __tmhm_value__ - NUM_TMS - 1
```

The `add_tm` and `add_hm` macros simultaneously define the next item constant (`TM_AEROBLAST` and `HM_SOFTBOILED` respectively) and the next `TMNUM` constant (`AEROBLAST_TMNUM` and `SOFTBOILED_TMNUM`). The item constants are used for `giveitem` scripts, in Mart inventories, etc. The `TMNUM` constants are not used directly, but get referred to by the `tmhm` learnsets in Pokémon base data. (We'll see how that works later.)

(This also demonstrates why Rock Smash would be an inconvenient example for adding a new HM. TM08 is already Rock Smash, and we can't define `ROCK_SMASH_TMNUM` twice, so we would have to do the extra work of replacing TM08 with some other move.)


## 2. Define standard item data

First of all, unlike [regular items](Add-a-new-item), we don't need to edit [data/items/descriptions.asm](../blob/master/data/items/descriptions.asm) or [engine/items/item_effects.asm](../blob/master/engine/items/item_effects.asm). The `ItemDescriptions` table already has dummy "?" descriptions for all the items from TM01 and up; and the `ItemEffects` table ends right before TM01 (since TMs and HMs don't use those effects anyway).

Edit [data/items/names.asm](../blob/master/data/items/names.asm):

```diff
ItemNames::
	list_start ItemNames
	li "MASTER BALL"
	...
	li "TM50"
+	li "TM51"
	assert_list_length NUM_ITEMS + NUM_TMS + 2 ; count ITEM_C3 and ITEM_DC
	li "HM01"
	...
	li "HM07"
+	li "HM08"
	assert_list_length NUM_ITEMS + NUM_TMS + 2 + NUM_HMS ; count ITEM_C3 and ITEM_DC
	li "TERU-SAMA"
-	li "TERU-SAMA"
-	li "TERU-SAMA"
	li "TERU-SAMA"
	li "TERU-SAMA"
	li "TERU-SAMA"
	li "?"
```

And edit [data/items/attributes.asm](../blob/master/data/items/attributes.asm):

```diff
 ; TM50
 	item_attribute 2000, HELD_NONE, 0, CANT_SELECT, TM_HM, ITEMMENU_PARTY, ITEMMENU_NOUSE
+; TM51
+	item_attribute 3000, HELD_NONE, 0, CANT_SELECT, TM_HM, ITEMMENU_PARTY, ITEMMENU_NOUSE
	assert_table_length NUM_ITEMS + NUM_TMS + 2 ; count ITEM_C3 and ITEM_DC
 ; HM01
 	item_attribute 0, HELD_NONE, 0, CANT_SELECT | CANT_TOSS, TM_HM, ITEMMENU_PARTY, ITEMMENU_NOUSE
 ...
 ; HM07
 	item_attribute 0, HELD_NONE, 0, CANT_SELECT | CANT_TOSS, TM_HM, ITEMMENU_PARTY, ITEMMENU_NOUSE
+; HM08
+	item_attribute 0, HELD_NONE, 0, CANT_SELECT | CANT_TOSS, TM_HM, ITEMMENU_PARTY, ITEMMENU_NOUSE
	assert_table_length NUM_ITEMS + NUM_TMS + 2 + NUM_HMS ; count ITEM_C3 and ITEM_DC
 ; ITEM_FA
 	item_attribute $9999, HELD_NONE, 0, NO_LIMITS, ITEM, ITEMMENU_NOUSE, ITEMMENU_NOUSE
-; $fb
-	item_attribute $9999, HELD_NONE, 0, NO_LIMITS, ITEM, ITEMMENU_NOUSE, ITEMMENU_NOUSE
-; $fc
-	item_attribute $9999, HELD_NONE, 0, NO_LIMITS, ITEM, ITEMMENU_NOUSE, ITEMMENU_NOUSE
 ; $fd
 	item_attribute $9999, HELD_NONE, 0, NO_LIMITS, ITEM, ITEMMENU_NOUSE, ITEMMENU_NOUSE
 ; $fe
 	item_attribute $9999, HELD_NONE, 0, NO_LIMITS, ITEM, ITEMMENU_NOUSE, ITEMMENU_NOUSE
 ; $ff
 	item_attribute $9999, HELD_NONE, 0, NO_LIMITS, ITEM, ITEMMENU_NOUSE, ITEMMENU_NOUSE
 ; $00
 	item_attribute $9999, HELD_NONE, 0, NO_LIMITS, ITEM, ITEMMENU_NOUSE, ITEMMENU_NOUSE
	assert_table_length $100
```

Notice how the `ItemNames` and `ItemAttributes` both already had the maximum 256 entries, so we had to remove dummy entries to fit TM51 and HM08. And there aren't many dummy entries; 251 items are defined, from $00 to $FA. If you want a lot of new TMs or HMs, you'll have to remove some unused items. There are 26 unused <code>ITEM_<i>XX</i></code> constants, counting `ITEM_C3` and `ITEM_DC`, which interrupt the sequence of TMs and need [a bit of special handling](../blob/master/docs/design_flaws.md#item_c3-and-item_dc-break-up-the-continuous-sequence-of-tm-items) to remove.


## 3. Update the TM/HM move table

**NOTE:** This step is no longer required in current versions of pokecrystal.
Edit [data/moves/tmhm_moves.asm](../blob/master/data/moves/tmhm_moves.asm):

```diff
 TMHMMoves:
 ; entries correspond to *_TMNUM enums (see constants/item_constants.asm)
 ; TMs
 	db DYNAMICPUNCH
 	...
 	db NIGHTMARE
+	db AEROBLAST
 ; HMs
 	db CUT
 	...
 	db WATERFALL
+	db SOFTBOILED
```

This associates the `AEROBLAST_TMNUM` TM/HM constant with the `AEROBLAST` move constant, and the `SOFTBOILED_TMNUM` TM/HM constant with the `SOFTBOILED` move constant.


## 4. Add the TM or HM to base learnsets

So far we've created items for TM51 and HM08 and assigned their moves, but the items aren't compatible with any Pokémon. So edit the `tmhm` entries in [data/pokemon/base_stats/\*.asm](../blob/master/data/pokemon/base_stats/):

- [chansey.asm](../blob/master/data/pokemon/base_stats/chansey.asm): `..., FLASH, FLAMETHROWER, ...` → `..., FLASH, SOFTBOILED, FLAMETHROWER, ...`
- [blissey.asm](../blob/master/data/pokemon/base_stats/blissey.asm): `..., FLASH, FLAMETHROWER, ...` → `..., FLASH, SOFTBOILED, FLAMETHROWER, ...`
- [lugia.asm](../blob/master/data/pokemon/base_stats/lugia.asm): `..., NIGHTMARE, FLY, ...` → `..., NIGHTMARE, AEROBLAST, FLY, ...`
- [mew.asm](../blob/master/data/pokemon/base_stats/mew.asm): `..., NIGHTMARE, CUT, ..., WATERFALL, FLAMETHROWER, ...` → `..., NIGHTMARE, AEROBLAST, CUT, ..., WATERFALL, SOFTBOILED, FLAMETHROWER, ...`

The learnable moves have to be in the same order as the `add_tm` and `add_hm` lines, since that's what they're referencing: the `tmhm` macro turns `NIGHTMARE` into `NIGHTMARE_TMNUM`, `SOFTBOILED` into `SOFTBOILED_TMNUM`, etc, and then processes them to efficiently store learnsets. (We'll get to the details of how it works next.)


## 5. Make the HM move unforgettable

Edit [home/hm_moves.asm](../blob/master/home/hm_moves.asm):

```diff
 .HMMoves:
 	db CUT
 	db FLY
 	db SURF
 	db STRENGTH
 	db FLASH
 	db WATERFALL
 	db WHIRLPOOL
+	db SOFTBOILED
 	db -1 ; end
```

Now Softboiled can't be forgotten except via the Move Deleter in Blackthorn City.

Anyway, that's all:

![Screenshot](screenshots/tm51-aeroblast-hm08-softboiled.png)


## 6. Adding up to 120 new TMs or HMs

As you keep adding new TMs or HMs, at one point the base data will become too large to fit in a ROM bank and you'll get an error when you run `make`:

```
error: Section 'bank14' is too big (max size = 0x4000 bytes).
```

One way to fix this is to remove the six unknown/padding bytes from all the base data.

First edit all the [data/pokemon/base_stats/\*.asm](../blob/master/data/pokemon/base_stats/) files, removing these three lines from each:

- `db 100 ; unknown 1`
- `db 5 ; unknown 2`
- `dw NULL, NULL ; unused (beta front/back pics)`

Then edit [wram.asm](../blob/master/wram.asm):

```diff
 ; corresponds to the data/pokemon/base_stats/*.asm contents
 wCurBaseData::
 wBaseDexNo:: db
 wBaseStats::
 wBaseHP:: db
 wBaseAttack:: db
 wBaseDefense:: db
 wBaseSpeed:: db
 wBaseSpecialAttack:: db
 wBaseSpecialDefense:: db
 wBaseType::
 wBaseType1:: db
 wBaseType2:: db
 wBaseCatchRate:: db
 wBaseExp:: db
 wBaseItems::
 wBaseItem1:: db
 wBaseItem2:: db
 wBaseGender:: db
-wBaseUnknown1:: db
 wBaseEggSteps:: db
-wBaseUnknown2:: db
 wBasePicSize:: db
-wBaseUnusedFrontpic:: dw
-wBaseUnusedBackpic:: dw
 wBaseGrowthRate:: db
 wBaseEggGroups:: db
 wBaseTMHM:: flag_array NUM_TM_HM_TUTOR
 wCurBaseDataEnd::
	assert wCurBaseDataEnd - wCurBaseData == BASE_DATA_SIZE

+	ds 6
 ...
```
And also [constants/pokemon_data_constants.asm](../blob/master/constants/pokemon_data_constants.asm):

```diff
 ; base data struct members (see data/pokemon/base_stats/*.asm)
 rsreset
 BASE_DEX_NO      rb
 BASE_STATS       rb NUM_STATS
 rsset BASE_STATS
 BASE_HP          rb
 BASE_ATK         rb
 BASE_DEF         rb
 BASE_SPD         rb
 BASE_SAT         rb
 BASE_SDF         rb
 BASE_TYPES       rw
 rsset BASE_TYPES
 BASE_TYPE_1      rb
 BASE_TYPE_2      rb
 BASE_CATCH_RATE  rb
 BASE_EXP         rb
 BASE_ITEMS       rw
 rsset BASE_ITEMS
 BASE_ITEM_1      rb
 BASE_ITEM_2      rb
 BASE_GENDER      rb
-                 rb_skip
 BASE_EGG_STEPS   rb
-                 rb_skip
 BASE_PIC_SIZE    rb
-BASE_FRONTPIC    rw
-BASE_BACKPIC     rw
 BASE_GROWTH_RATE rb
 BASE_EGG_GROUPS  rb
 BASE_TMHM        rb (NUM_TM_HM_TUTOR + 7) / 8
 BASE_DATA_SIZE EQU _RS
```

Additionally, the label wBaseUnusedFrontpic is used once in [home/pokemon.asm](../blob/master/home/pokemon.asm), but this reference is safe to comment out or delete entirely as it is only used in the Spaceworld Demo for Pokemon Gold.
```diff
-; Beta front and back sprites
-; (see pokegold-spaceworld's data/pokemon/base_stats/*)
-	ld hl, wBaseUnusedFrontpic
-	ld [hl], e
-	inc hl
-	ld [hl], d
-	inc hl
-	ld [hl], e
-	inc hl
-	ld [hl], d
-	jr .end ; useless
```
This functionality is unused in the final game, so deleting or commenting out the entire function should create no issues.

That gives you enough free space for seven extra base data bytes per Pokémon. Plus the eight that are already used for learnable moves, that's up to 15 `tmhm` bytes, which would allow 120 learnable moves: easily enough for the 100 TMs of Gen 7, plus HMs and tutors. If you somehow need *even more* than that, you can figure out how to save more bytes by packing the base data more compactly.