summaryrefslogtreecommitdiff
path: root/Improve-the-outdoor-sprite-system.md
blob: 0e9576a76a71c9d69e24eaf3a422deed81ec7458 (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
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
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
In [the tutorial to add a new map](Add-a-new-map-and-landmark), we covered the concept of **outdoor sprite sets**. Outdoor maps—those with a `TOWN` or `ROUTE` environment—can only use sprites from their map group's set of usable sprites.

The outdoor sprite sets are defined in [data/maps/outdoor_sprites.asm](../blob/master/data/maps/outdoor_sprites.asm). For example, here's the one for Olivine City's map group:

```
OlivineGroupSprites:
	db SPRITE_SUICUNE
	db SPRITE_SILVER_TROPHY
	db SPRITE_FAMICOM
	db SPRITE_POKEDEX
	db SPRITE_WILL
	db SPRITE_KAREN
	db SPRITE_NURSE
	db SPRITE_OLD_LINK_RECEPTIONIST
	db SPRITE_STANDING_YOUNGSTER
	db SPRITE_BIG_ONIX
	db SPRITE_SUDOWOODO
	db SPRITE_BIG_SNORLAX
	db SPRITE_OLIVINE_RIVAL
	db SPRITE_POKEFAN_M
	db SPRITE_LASS
	db SPRITE_BUENA
	db SPRITE_SWIMMER_GIRL
	db SPRITE_SAILOR
	db SPRITE_POKEFAN_F
	db SPRITE_SUPER_NERD
	db SPRITE_TAUROS
	db SPRITE_FRUIT_TREE
	db SPRITE_ROCK
```

We can see how those get loaded with [BGB](http://bgb.bircd.org/)'s VRAM viewer:

![Screenshot](screenshots/outdoor-sprites-vram.png)

VRAM is divided into six areas, each 128 tiles large. The top two areas are for sprites' standing frames. The middle-right area is for the walking frames of the sprites in the top-right. But the middle-left area is for font tiles, so the top-left sprites can't walk. (If they do, they'll appear as text tiles.)

As they're currently implemented, these sprite sets are hard to edit. Every set has 23 sprites, and it's hard to tell which ones are needed for which map. It's also not clear which sprites get placed in VRAM bank 1 (the one on the right in the VRAM viewer). There's only enough room for nine sprites to have walking frames, but those nine are not in any particular order, nor does the order of the list correspond to the order in VRAM.

This tutorial will improve the outdoor sprite sets by making them variable-length lists ending with 0, and with the first nine sprites being the ones to get walking frames. The existing sprite sets for pokecrystal's maps will be optimized to work with this new format.


## Contents

1. [Make outdoor sprite sets variable-length, ending with 0](#1-make-outdoor-sprite-sets-variable-length-ending-with-0)
2. [Don't automatically sort outdoor sprite sets](#2-dont-automatically-sort-outdoor-sprite-sets)
3. [Update the outdoor sprite sets](#3-update-the-outdoor-sprite-sets)
4. [Remove the now-redundant non-walking sprite versions](#4-remove-the-now-redundant-non-walking-sprite-versions)
5. [Remove the now-redundant variable sprites](#5-remove-the-now-redundant-variable-sprites)


## 1. Make outdoor sprite sets variable-length, ending with 0

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

```diff
-MAX_OUTDOOR_SPRITES EQU 23 ; see engine/overworld/overworld.asm
```

We won't need `MAX_OUTDOOR_SPRITES` any more.

Next, edit [engine/overworld/overworld.asm](../blob/master/engine/overworld/overworld.asm):

```diff
 AddOutdoorSprites:
 	ld a, [wMapGroup]
 	dec a
 	ld c, a
 	ld b, 0
 	ld hl, OutdoorSprites
 	add hl, bc
 	add hl, bc
 	ld a, [hli]
 	ld h, [hl]
 	ld l, a
-	ld c, MAX_OUTDOOR_SPRITES
 .loop
-	push bc
 	ld a, [hli]
+	and a
+	ret z
 	call AddSpriteGFX
-	pop bc
-	dec c
-	jr nz, .loop
-	ret
+	jr .loop
```

Instead of counting `c` down from `MAX_OUTDOOR_SPRITES` to 0, now `AddOutdoorSprites` will continue until it finds a 0 list entry. (So don't forget to add them! We'll do so later.)

Now that outdoor sprite lists can be arbitrarily long, it's more important to enforce the limit of how much RAM is even available. However, there happens to be a bug with the `LoadSpriteGFX` routine that ignores the `SPRITE_GFX_LIST_CAPACITY` limit. So be sure to [fix that](../blob/master/docs/bugs_and_glitches.md#loadspritegfx-does-not-limit-the-capacity-of-usedsprites).


## 2. Don't automatically sort outdoor sprite sets

As we saw in earlier, the current sprite lists are not in any particular order. It turns out that the `LoadAndSortSprites` routine sorts the lists before loading their graphics, in order of how many tiles each one has, from most to least. Most NPC sprites have 12 tiles (four each for the front, back, and side views), so they get sorted first, and then come the still sprites like `SPRITE_POKE_BALL`, `SPRITE_FRUIT_TREE`, etc.

(Each outdoor sprite list gets padded to 23 entries with a bunch of still sprites like `SPRITE_SILVER_TROPHY` or `SPRITE_OLD_LINK_RECEPTIONIST`. They're not used in any outdoor map, but they have to be there so the walking sprites get sorted first.)

Anyway, edit [engine/overworld/overworld.asm](../blob/master/engine/overworld/overworld.asm) again:

```diff
 LoadAndSortSprites:
 	call LoadSpriteGFX
-	call SortUsedSprites
 	call ArrangeUsedSprites
 	ret

 ...

-SortUsedSprites:
-; Bubble-sort sprites by type.
-
-	...
-
-.quit
-	ret
```

Now sprites will be loaded into VRAM in whatever order the list specifies. So if we want a certain nine sprites to have walking frames available, we'll have to put them first.


## 3. Update the outdoor sprite sets

If you're replacing all of Crystal's maps with your own, you won't need these exact sets; but they're a good reference anyway for how the new sets work.

Edit [data/maps/outdoor_sprites.asm](../blob/master/data/maps/outdoor_sprites.asm):

```diff
-PalletGroupSprites:
-	...
-
-...
-
-CableClubGroupSprites:
-	...
+; Route1 and ViridianCity are connected
+; Route2 and PewterCity are connected
+; PalletTown and Route21 are connected
+PalletGroupSprites:
+; Route1, PalletTown
+ViridianGroupSprites:
+; Route2, Route22, ViridianCity
+PewterGroupSprites:
+; Route3, PewterCity
+CinnabarGroupSprites:
+; Route19, Route20, Route21, CinnabarIsland
+	db SPRITE_TEACHER
+	db SPRITE_FISHER
+	db SPRITE_YOUNGSTER
+	db SPRITE_BLUE
+	db SPRITE_GRAMPS
+	db SPRITE_BUG_CATCHER
+	db SPRITE_COOLTRAINER_F
+	db SPRITE_SWIMMER_GIRL
+	db SPRITE_SWIMMER_GUY
+	; max 9 of 9 walking sprites
+	db SPRITE_POKE_BALL
+	db SPRITE_FRUIT_TREE
+	db 0 ; end
+
+; CeruleanCity and Route5 are connected
+CeruleanGroupSprites:
+; Route4, Route9, Route10North, Route24, Route25, CeruleanCity
+SaffronGroupSprites:
+; Route5, SaffronCity
+	db SPRITE_COOLTRAINER_M
+	db SPRITE_SUPER_NERD
+	db SPRITE_COOLTRAINER_F
+	db SPRITE_FISHER
+	db SPRITE_YOUNGSTER
+	db SPRITE_LASS
+	db SPRITE_POKEFAN_M
+	db SPRITE_ROCKET
+	db SPRITE_MISTY
+	; max 9 of 9 walking sprites
+	db SPRITE_POKE_BALL
+	db SPRITE_SLOWPOKE
+	db 0 ; end
+
+VermilionGroupSprites:
+; Route6, Route11, VermilionCity
+	db SPRITE_POKEFAN_M
+	db SPRITE_GRAMPS
+	db SPRITE_YOUNGSTER
+	db SPRITE_TEACHER
+	db SPRITE_SUPER_NERD
+	; 5 of max 9 walking sprites
+	db SPRITE_BIG_SNORLAX
+	db SPRITE_MACHOP
+	db SPRITE_POKE_BALL
+	db SPRITE_FRUIT_TREE
+	db 0 ; end
+
+CeladonGroupSprites:
+; Route7, Route16, Route17, CeladonCity
+	db SPRITE_FISHER
+	db SPRITE_TEACHER
+	db SPRITE_GRAMPS
+	db SPRITE_YOUNGSTER
+	db SPRITE_LASS
+	db SPRITE_BIKER
+	; 6 of max 9 walking sprites
+	db SPRITE_POLIWAG
+	db SPRITE_POKE_BALL
+	db SPRITE_FRUIT_TREE
+	db 0 ; end
+
+; Route12 and Route13 are connected
+LavenderGroupSprites:
+; Route8, Route12, Route10South, LavenderTown
+FuchsiaGroupSprites:
+; Route13, Route14, Route15, Route18, FuchsiaCity
+	db SPRITE_POKEFAN_M
+	db SPRITE_GRAMPS
+	db SPRITE_YOUNGSTER
+	db SPRITE_FISHER
+	db SPRITE_TEACHER
+	db SPRITE_SUPER_NERD
+	db SPRITE_BIKER
+	; 7 of max 9 walking sprites
+	db SPRITE_POKE_BALL
+	db SPRITE_FRUIT_TREE
+	db 0 ; end
+
+IndigoGroupSprites:
+; Route23
+	; 0 of max 9 walking sprites
+	db 0 ; end
+
+; Route29 and CherrygroveCity are connected
+NewBarkGroupSprites:
+; Route26, Route27, Route29, NewBarkTown
+CherrygroveGroupSprites:
+; Route30, Route31, CherrygroveCity
+	db SPRITE_SILVER
+	db SPRITE_TEACHER
+	db SPRITE_FISHER
+	db SPRITE_COOLTRAINER_M
+	db SPRITE_YOUNGSTER
+	db SPRITE_MONSTER
+	db SPRITE_GRAMPS
+	db SPRITE_BUG_CATCHER
+	db SPRITE_COOLTRAINER_F
+	; max 9 of 9 walking sprites
+	db SPRITE_POKE_BALL
+	db SPRITE_FRUIT_TREE
+	db 0 ; end
+
+; Route37 and EcruteakCity are connected
+VioletGroupSprites:
+; Route32, Route35, Route36, Route37, VioletCity
+EcruteakGroupSprites:
+; EcruteakCity
+	db SPRITE_FISHER
+	db SPRITE_LASS
+	db SPRITE_OFFICER
+	db SPRITE_GRAMPS
+	db SPRITE_YOUNGSTER
+	db SPRITE_COOLTRAINER_M
+	db SPRITE_BUG_CATCHER
+	db SPRITE_SUPER_NERD
+	; 8 of max 9 walking sprites
+	db SPRITE_WEIRD_TREE ; variable sprite: becomes SPRITE_SUDOWOODO and SPRITE_TWIN
+	db SPRITE_POKE_BALL
+	db SPRITE_FRUIT_TREE
+	db SPRITE_SUICUNE
+	db 0 ; end
+
+AzaleaGroupSprites:
+; Route33, AzaleaTown
+	db SPRITE_GRAMPS
+	db SPRITE_YOUNGSTER
+	db SPRITE_POKEFAN_M
+	db SPRITE_TEACHER
+	db SPRITE_AZALEA_ROCKET ; variable sprite: becomes SPRITE_ROCKET and SPRITE_SILVER
+	db SPRITE_LASS
+	; 6 of max 9 walking sprites
+	db SPRITE_FRUIT_TREE
+	db SPRITE_SLOWPOKE
+	db SPRITE_KURT_OUTSIDE ; non-walking version of SPRITE_KURT
+	db 0 ; end
+
+GoldenrodGroupSprites:
+; Route34, GoldenrodCity
+	db SPRITE_GRAMPS
+	db SPRITE_YOUNGSTER
+	db SPRITE_OFFICER
+	db SPRITE_POKEFAN_M
+	db SPRITE_COOLTRAINER_F
+	db SPRITE_ROCKET
+	db SPRITE_LASS
+	; 7 of max 9 walking sprites
+	db SPRITE_DAY_CARE_MON_1
+	db SPRITE_DAY_CARE_MON_2
+	db SPRITE_POKE_BALL
+	db 0 ; end
+
+; OlivineCity and Route40 are connected
+OlivineGroupSprites:
+; Route38, Route39, OlivineCity
+CianwoodGroupSprites:
+; Route40, Route41, CianwoodCity, BattleTowerOutside
+	db SPRITE_OLIVINE_RIVAL ; variable sprite: becomes SPRITE_SILVER and SPRITE_SWIMMER_GUY
+	db SPRITE_POKEFAN_M
+	db SPRITE_LASS
+	db SPRITE_BUENA
+	db SPRITE_SWIMMER_GIRL
+	db SPRITE_SAILOR
+	db SPRITE_POKEFAN_F
+	db SPRITE_SUPER_NERD
+	; 8 of max 9 walking sprites
+	db SPRITE_TAUROS
+	db SPRITE_FRUIT_TREE
+	db SPRITE_ROCK
+	db SPRITE_STANDING_YOUNGSTER ; non-walking version of SPRITE_YOUNGSTER
+	db SPRITE_SUICUNE
+	db 0 ; end
+
+MahoganyGroupSprites:
+; Route42, Route44, MahoganyTown
+	db SPRITE_GRAMPS
+	db SPRITE_YOUNGSTER
+	db SPRITE_LASS
+	db SPRITE_SUPER_NERD
+	db SPRITE_COOLTRAINER_M
+	db SPRITE_POKEFAN_M
+	db SPRITE_COOLTRAINER_F
+	db SPRITE_FISHER
+	; 8 of max 9 walking sprites
+	db SPRITE_FRUIT_TREE
+	db SPRITE_POKE_BALL
+	db SPRITE_SUICUNE
+	db 0 ; end
+
+LakeOfRageGroupSprites:
+; Route43, LakeOfRage
+	db SPRITE_LANCE
+	db SPRITE_GRAMPS
+	db SPRITE_SUPER_NERD
+	db SPRITE_COOLTRAINER_F
+	db SPRITE_FISHER
+	db SPRITE_COOLTRAINER_M
+	db SPRITE_LASS
+	db SPRITE_YOUNGSTER
+	; 8 of max 9 walking sprites
+	db SPRITE_GYARADOS
+	db SPRITE_FRUIT_TREE
+	db SPRITE_POKE_BALL
+	db 0 ; end
+
+BlackthornGroupSprites:
+; Route45, Route46, BlackthornCity
+	db SPRITE_GRAMPS
+	db SPRITE_YOUNGSTER
+	db SPRITE_LASS
+	db SPRITE_SUPER_NERD
+	db SPRITE_COOLTRAINER_M
+	db SPRITE_POKEFAN_M
+	db SPRITE_BLACK_BELT
+	db SPRITE_COOLTRAINER_F
+	; 8 of max 9 walking sprites
+	db SPRITE_FRUIT_TREE
+	db SPRITE_POKE_BALL
+	db 0 ; end
+
+SilverGroupSprites:
+; Route28, SilverCaveOutside
+	; 0 of max 9 walking sprites
+	db 0 ; end
+
+DungeonsGroupSprites:
+; NationalPark, NationalParkBugContest, RuinsOfAlphOutside
+	db SPRITE_LASS
+	db SPRITE_POKEFAN_F
+	db SPRITE_TEACHER
+	db SPRITE_YOUNGSTER
+	db SPRITE_POKEFAN_M
+	db SPRITE_ROCKER
+	db SPRITE_FISHER
+	db SPRITE_SCIENTIST
+	; 8 of max 9 walking sprites
+	db SPRITE_GAMEBOY_KID
+	db SPRITE_GROWLITHE
+	db SPRITE_POKE_BALL
+	db 0 ; end
+
+FastShipGroupSprites:
+; OlivinePort, VermilionPort, MountMoonSquare, TinTowerRoof
+	db SPRITE_SAILOR
+	db SPRITE_FISHING_GURU
+	db SPRITE_SUPER_NERD
+	db SPRITE_COOLTRAINER_F
+	db SPRITE_YOUNGSTER
+	; 5 of max 9 walking sprites
+	db SPRITE_HO_OH
+	db SPRITE_FAIRY
+	db SPRITE_ROCK
+	db 0 ; end
+
+CableClubGroupSprites:
+; (no outdoor maps)
+	; 0 of max 9 walking sprites
+	db 0 ; end
```

Now it works! These new sets are easier to define and debug than before. For example, here's the one for Olivine City's map group:

```
; OlivineCity and Route40 are connected
OlivineGroupSprites:
; Route38, Route39, OlivineCity
CianwoodGroupSprites:
; Route40, Route41, CianwoodCity, BattleTowerOutside
	db SPRITE_OLIVINE_RIVAL; variable sprite: becomes SPRITE_SILVER and SPRITE_SWIMMER_GUY
	db SPRITE_POKEFAN_M
	db SPRITE_LASS
	db SPRITE_BUENA
	db SPRITE_SWIMMER_GIRL
	db SPRITE_SAILOR
	db SPRITE_POKEFAN_F
	db SPRITE_SUPER_NERD
	; 8 of max 9 walking sprites
	db SPRITE_TAUROS
	db SPRITE_FRUIT_TREE
	db SPRITE_ROCK
	db SPRITE_STANDING_YOUNGSTER ; non-walking version of SPRITE_YOUNGSTER
	db SPRITE_SUICUNE
	db 0 ; end
```

And here's how they get loaded into VRAM:

![Screenshot](screenshots/outdoor-sprites-improved-vram.png)

The comments make it clear which maps the set applies to; and the order matches their order in VRAM, from top to bottom, right and then left.

Some things to note about the new system:

- If you can walk across a map connection from one map group to another, those groups now share an outdoor sprite set. Sprites and tilesets are only reloaded when you warp to a different map, not when you cross a connection, so this is necessary. Previously, connected sets like `OlivineGroupSprites` and `CianwoodGroupSprites` used separate lists which had to be kept in sync.
- Removing the `LoadAndSortSprites` also affects indoor maps. If a map's `object_event`s use so many sprites that some get loaded in VRAM0, make sure that the walking ones come first and have their walking frames in VRAM1.

We're technically done at this point, but the new system makes a few sprites redundant. Let's see how that works.


## 4. Remove the now-redundant non-walking sprite versions

Two sprites are just copies of other sprites, but with the walking frames removed:

- `SPRITE_STANDING_YOUNGSTER` is a non-walking version of `SPRITE_YOUNGSTER` used by `OlivineGroupSprites` and `CianwoodGroupSprites`.
- `SPRITE_KURT_OUTSIDE` is a non-walking version of `SPRITE_KURT` used by `AzaleaGroupSprites`.

This used to be necessary because if there were ten or more walking sprites, you couldn't control which nine would be loaded first and have their walking frames available; it was up to `LoadAndSortSprites`.

Now, though, these alternate non-walking sprites are redundant, so you can delete them:

- Remove the `SPRITE_STANDING_YOUNGSTER` and `SPRITE_KURT_OUTSIDE` definitions and data from [constants/sprite_constants.asm](../blob/master/constants/sprite_constants.asm), [data/sprites/sprites.asm](../blob/master/data/sprites/sprites.asm), [gfx/sprites.asm](../blob/master/gfx/sprites.asm), and [gfx/sprites/*.png](../tree/master/gfx/sprites/).
- Replace `SPRITE_STANDING_YOUNGSTER` and `SPRITE_KURT_OUTSIDE` with `SPRITE_YOUNGSTER` and `SPRITE_KURT` respectively in [maps/*.asm](../tree/master/maps/) and [data/maps/outdoor_sprites.asm](../blob/master/data/maps/outdoor_sprites.asm). Be sure to put them after all the walking sprites in their respective outdoor sprite sets, since their walking frames aren't needed.

For example, when you replace `SPRITE_STANDING_YOUNGSTER` with `SPRITE_YOUNGSTER` in `OlivineGroupSprites`, its standing frames will get loaded in VRAM bank 0, right where `SPRITE_STANDING_YOUNGSTER` in the previous screenshot; and its walking frames won't interfere with the font graphics.


## 5. Remove the now-redundant variable sprites

Three sprites are actually variable sprites:

- `SPRITE_WEIRD_TREE` is used by `VioletGroupSprites` and `EcruteakGroupSprites`. It starts out looking like `SPRITE_SUDOWOODO`, and becomes `SPRITE_TWIN` after you battle Sudowoodo. This works because the only Twins in those map groups are encountered after Sudowoodo disappears.
- `SPRITE_AZALEA_ROCKET` is used by `AzaleaGroupSprites`. It starts out looking like `SPRITE_ROCKET`, and becomes `SPRITE_SILVER` after you save the Slowpoke. This works because the rival encounter occurs after all the Rockets disappear.
- `SPRITE_OLIVINE_RIVAL` is used by `OlivineGroupSprites` and `CianwoodGroupSprites`. It starts out looking like `SPRITE_SILVER`, and becomes `SPRITE_SWIMMER_GUY` after you run into your rival. This works because the only male Swimmers in those map groups are encountered after your rival disappears.

At first glance, it makes sense why these variable sprites exist. They're a neat way to have ten or more walking sprites: if you know that only nine will be needed at a time, then a variable sprite can look like one now and another later.

...Except, all three of those are used in outdoor sprite sets with enough room for more walking sprites! Out of a maximum 9 walking sprites, `VioletGroupSprites` and `EcruteakGroupSprites` only use 8; `AzaleaGroupSprites` uses 6; and `OlivineGroupSprites` and `CianwoodGroupSprites` use 8.

It turns out that Crystal doesn't need these variable sprites, but Gold and Silver did. Crystal is exclusive to the GameBoy Color, so it has twice as much VRAM, and it's able to load walking frames for every sprite in VRAM1 while still having room for standing-still sprites in VRAM0. But Gold and Silver supported the Super GameBoy, which only had one VRAM bank; so still sprites like `SPRITE_POKE_BALL` and `SPRITE_SLOWPOKE` used up the same space budget as walking sprites.

Anyway, the point is that we don't need them any more. So you can completely delete those three variable sprites:

- Remove the `SPRITE_WEIRD_TREE`, `SPRITE_AZALEA_ROCKET`, and `SPRITE_OLIVINE_RIVAL` definitions from [constants/sprite_constants.asm](../blob/master/constants/sprite_constants.asm).
- Remove their `variablesprite` commands from [maps/*.asm](../tree/master/maps/) and `InitializeEventsScript` in [engine/events/std_scripts.asm](../blob/master/engine/events/std_scripts.asm). You can also remove `special LoadUsedSpritesGFX` when it occurs in a map script right after a `variablesprite` command.
- Replace `SPRITE_WEIRD_TREE`, `SPRITE_AZALEA_ROCKET`, and `SPRITE_OLIVINE_RIVAL` with the actual sprites they're supposed to use in [maps/*.asm](../tree/master/maps/) and [data/maps/outdoor_sprites.asm](../blob/master/data/maps/outdoor_sprites.asm). Be sure to put the actual sprites among the first nine list entries, since they all need to walk (except for the `SPRITE_SUDOWOODO` and `SPRITE_TWIN` which replace `SPRITE_WEIRD_TREE`).

For more information on variable sprites, see [the tutorial to add a new sprite](Add-a-new-overworld-sprite).