summaryrefslogtreecommitdiff
path: root/Remove-Pokémon-sprite-animations.md
blob: b135f6b252cef9694e47f49a51d37240a9faa23f (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
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
One of Pokémon Crystal's improvements to Gold and Silver was animating the Pokémon sprites. There are various reasons why you might want to remove this feature:

- You're changing a lot of Pokémon and don't want to design animations for them all
- You want the game to more closely resemble R/B/Y or G/S
- You need the space for something else

If you just want some of the Pokémon to not be animated, then as mentioned in the [new Pokémon tutorial](Add-a-new-Pokémon), you can just put `endanim` as the full contents of anim.asm and anim_idle.asm for those particular Pokémon. But if none if them will be animated, then you can save considerable ROM space by removing the entire animation system.


## Contents

1. [Delete the sprite animation system and data](#1-delete-the-sprite-animation-system-and-data)
2. [Remove references to deleted files](#2-remove-references-to-deleted-files)
3. [Remove code and data that is no longer used](#3-remove-code-and-data-that-is-no-longer-used)
4. [Don't load animation graphics](#4-dont-load-animation-graphics)
5. [Play cries instead of animations](#5-play-cries-instead-of-animations)
6. [Fix the Pokémon stats screen](#6-fix-the-pokémon-stats-screen)
7. [Remove the unused animation tile graphics](#7-remove-the-unused-animation-tile-graphics)


## 1. Delete the sprite animation system and data

Delete all of these files:

- [engine/gfx/pic_animation.asm](../blob/master/engine/gfx/pic_animation.asm)
- [gfx/pokemon/anim_pointers.asm](../blob/master/gfx/pokemon/anim_pointers.asm)
- [gfx/pokemon/anims.asm](../blob/master/gfx/pokemon/anims.asm)
- [gfx/pokemon/idle_pointers.asm](../blob/master/gfx/pokemon/idle_pointers.asm)
- [gfx/pokemon/idles.asm](../blob/master/gfx/pokemon/idles.asm)
- [gfx/pokemon/bitmask_pointers.asm](../blob/master/gfx/pokemon/bitmask_pointers.asm)
- [gfx/pokemon/bitmasks.asm](../blob/master/gfx/pokemon/bitmasks.asm)
- [gfx/pokemon/frame_pointers.asm](../blob/master/gfx/pokemon/frame_pointers.asm)
- [gfx/pokemon/johto_frames.asm](../blob/master/gfx/pokemon/johto_frames.asm)
- [gfx/pokemon/kanto_frames.asm](../blob/master/gfx/pokemon/kanto_frames.asm)
- [gfx/pokemon/unown_anim_pointers.asm](../blob/master/gfx/pokemon/unown_anim_pointers.asm)
- [gfx/pokemon/unown_anims.asm](../blob/master/gfx/pokemon/unown_anims.asm)
- [gfx/pokemon/unown_idle_pointers.asm](../blob/master/gfx/pokemon/unown_idle_pointers.asm)
- [gfx/pokemon/unown_idles.asm](../blob/master/gfx/pokemon/unown_idles.asm)
- [gfx/pokemon/unown_bitmask_pointers.asm](../blob/master/gfx/pokemon/unown_bitmask_pointers.asm)
- [gfx/pokemon/unown_bitmasks.asm](../blob/master/gfx/pokemon/unown_bitmasks.asm)
- [gfx/pokemon/unown_frame_pointers.asm](../blob/master/gfx/pokemon/unown_frame_pointers.asm)
- [gfx/pokemon/unown_frames.asm](../blob/master/gfx/pokemon/unown_frames.asm)
- [gfx/pokemon/unown/bitmask.asm](../blob/master/gfx/pokemon/unown/bitmask.asm)
- [gfx/pokemon/unown/frames.asm](../blob/master/gfx/pokemon/unown/frames.asm)

Then run these commands to delete files that might have been created by `make`:

```bash
rm -f gfx/pokemon/*/front.animated.2bpp
rm -f gfx/pokemon/*/front.animated.2bpp.lz
rm -f gfx/pokemon/*/front.animated.tilemap
rm -f gfx/pokemon/*/bitmask.asm
rm -f gfx/pokemon/*/frames.asm
```


## 2. Remove references to deleted files

Edit [main.asm](../blob/master/main.asm):

```diff
-SECTION "Pic Animations 1", ROMX
-
-INCLUDE "engine/gfx/pic_animation.asm"
-...
-INCLUDE "gfx/pokemon/unown_bitmasks.asm"
-
-
-SECTION "Pic Animations 2", ROMX
-
-INCLUDE "gfx/pokemon/frame_pointers.asm"
-INCLUDE "gfx/pokemon/kanto_frames.asm"
-
-
 SECTION "Font Inversed", ROMX

 FontInversed:
 INCBIN "gfx/font/font_inversed.1bpp"
-
-
-SECTION "Pic Animations 3", ROMX
-
-INCLUDE "gfx/pokemon/johto_frames.asm"
-INCLUDE "gfx/pokemon/unown_frame_pointers.asm"
-INCLUDE "gfx/pokemon/unown_frames.asm"
```

And edit [pokecrystal.link](../blob/master/pokecrystal.link):

```diff
-ROMX $34
-	"Pic Animations 1"
-ROMX $35
-	"Pic Animations 2"
 ROMX $36
 	"Font Inversed"
-	"Pic Animations 3"
```


## 3. Remove code and data that is no longer used

Edit [wram.asm](../blob/master/wram.asm):

```diff
-; PokeAnim data
-wPokeAnimStruct::
-wPokeAnimSceneIndex:: db
-...
-wPokeAnimStructEnd::
```

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

```diff
-; PokeAnims indexes (see engine/gfx/pic_animation.asm)
-	const_def
-	const ANIM_MON_SLOW
-	...
-	const ANIM_MON_EGG2
```

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

```diff
 PredefPointers::
 	...
-	add_predef Unused_AnimateMon_Slow_Normal
	add_predef PlaceStatusString
-	add_predef LoadMonAnimation
-	add_predef AnimateFrontpic
-	add_predef Unused_HOF_AnimateAlignedFrontpic ; $48
-	add_predef HOF_AnimateFrontpic
-	dbw -1, InexplicablyEmptyFunction ; ???
```


## 4. Don't load animation graphics

Edit [engine/gfx/load_pics.asm](../blob/master/engine/gfx/load_pics.asm):

```diff
 GetAnimatedFrontpic:
 	ld a, [wCurPartySpecies]
 	ld [wCurSpecies], a
 	call IsAPokemon
 	ret c
 	ldh a, [rSVBK]
 	push af
 	xor a
 	ldh [hBGMapMode], a
 	call _GetFrontpic
-	call GetAnimatedEnemyFrontpic
 	pop af
 	ldh [rSVBK], a
 	ret

...

-GetAnimatedEnemyFrontpic:
-	...
-	ret
-
-LoadFrontpicTiles:
-	...
-	ret
```


## 5. Play cries instead of animations

If you read [engine/gfx/pic_animation.asm](../blob/master/engine/gfx/pic_animation.asm) before deleting it, you'll see how the animation system works. Usually the `AnimateFrontpic` routine is called with an `ANIM_MON_*` value loaded into `e`. It then runs a series of subroutines based on the value of `e`, selected from the `PokeAnims` array:

```
PokeAnims:
; entries correspond to ANIM_MON_* constants
	dw .Slow
	dw .Normal
	dw .Menu
	dw .Trade
	dw .Evolve
	dw .Hatch
	dw .HOF
	dw .Egg1
	dw .Egg2

.Slow:   pokeanim StereoCry, Setup2, Play
.Normal: pokeanim StereoCry, Setup, Play
.Menu:   pokeanim CryNoWait, Setup, Play, SetWait, Wait, Idle, Play
.Trade:  pokeanim Idle, Play2, Idle, Play, SetWait, Wait, Cry, Setup, Play
.Evolve: pokeanim Idle, Play, SetWait, Wait, CryNoWait, Setup, Play
.Hatch:  pokeanim Idle, Play, CryNoWait, Setup, Play, SetWait, Wait, Idle, Play
.HOF:    pokeanim CryNoWait, Setup, Play, SetWait, Wait, Idle, Play
.Egg1:   pokeanim Setup, Play
.Egg2:   pokeanim Idle, Play
```

Notice that not all of the subroutines are related to graphics: `Cry`, `CryNoWait`, and `StereoCry` play the audio cries. So since we've deleted the whole implementation of `AnimateFrontpic`, we have to restore the cry-playing functionality to wherever `AnimateFrontpic` was called.

Edit [engine/gfx/trademon_frontpic.asm](../blob/master/engine/gfx/trademon_frontpic.asm):

```diff
 AnimateTrademonFrontpic:
 	...
 	ld a, [wOTTrademonSpecies]
 	ld [wCurPartySpecies], a
-	hlcoord 7, 2
-	ld d, $0
-	ld e, ANIM_MON_TRADE
-	predef AnimateFrontpic
+	call PlayMonCry
 	ret
```

Edit [engine/movie/evolution_animation.asm](../blob/master/engine/movie/evolution_animation.asm):

```diff
 EvolutionAnimation:
 	...

 	ld a, [wPlayerHPPal]
 	ld [wCurPartySpecies], a
-	hlcoord 7, 2
-	ld d, $0
-	ld e, ANIM_MON_EVOLVE
-	predef AnimateFrontpic
+	call PlayMonCry2

 	pop af
 	ld [wCurPartySpecies], a
 	pop af
 	ld [wBoxAlignment], a
 	ret
```

Edit [engine/pokemon/breeding.asm](../blob/master/engine/pokemon/breeding.asm):

```diff
 EggHatch_AnimationSequence:
 	...
 	ld a, [wJumptableIndex]
 	ld [wCurPartySpecies], a
-	hlcoord 6, 3
-	ld d, $0
-	ld e, ANIM_MON_HATCH
-	predef AnimateFrontpic
+	call PlayMonCry2
 	pop af
 	ld [wCurSpecies], a
 	ret
```

Edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm):

```diff
 Function_SetEnemyMonAndSendOutAnimation:
 	...

 	ld bc, wTempMonSpecies
 	farcall CheckFaintedFrzSlp
 	jr c, .skip_cry
-	farcall CheckBattleScene
-	jr c, .cry_no_anim
-	hlcoord 12, 0
-	ld d, $0
-	ld e, ANIM_MON_SLOW
-	predef AnimateFrontpic
-	jr .skip_cry
-
-.cry_no_anim
 	ld a, $f
 	ld [wCryTracks], a
 	ld a, [wTempEnemyMonSpecies]
 	call PlayStereoCry

 .skip_cry
 	call UpdateEnemyHUD
 	ld a, $1
 	ldh [hBGMapMode], a
 	ret

 ...

BattleStartMessage:
	...

.not_shiny
	farcall CheckSleepingTreeMon
	jr c, .skip_cry

	farcall CheckBattleScene
	jr c, .cry_no_anim

-	hlcoord 12, 0
-	ld d, $0
-	ld e, ANIM_MON_NORMAL
-	predef AnimateFrontpic
-	jr .skip_cry ; cry is played during the animation
-
-.cry_no_anim
 	ld a, $f
 	ld [wCryTracks], a
 	ld a, [wTempEnemyMonSpecies]
 	call PlayStereoCry

 .skip_cry
 	...
```

Turning the "Battle Scene" option off would already disable sprite animations here, so all we had to do was make its case the default and only one.

Edit [engine/events/halloffame.asm](../blob/master/engine/events/halloffame.asm):

```diff
 AnimateHallOfFame:
 	...

 .DisplayNewHallOfFamer:
 	call DisplayHOFMon
 	ld de, .String_NewHallOfFamer
 	hlcoord 1, 2
 	call PlaceString
 	call WaitBGMap
-	decoord 6, 5
-	ld c, ANIM_MON_HOF
-	predef HOF_AnimateFrontpic
-	ld c, 60
+	call HOF_PlayCry
+	ld c, 180
 	call DelayFrames
 	and a
 	ret

 ...

 _HallOfFamePC:
 	...

 .finish
 	ld de, .EmptyString
 	call PlaceString
 	call WaitBGMap
 	ld b, SCGB_PLAYER_OR_MON_FRONTPIC_PALS
 	call GetSGBLayout
 	call SetPalettes
-	decoord 6, 5
-	ld c, ANIM_MON_HOF
-	predef HOF_AnimateFrontpic
+	call HOF_PlayCry
 	and a
 	ret

 ...

+HOF_PlayCry::
+	ld a, [wCurPartySpecies]
+	cp EGG
+	jr z, .fail
+	call IsAPokemon
+	jr c, .fail
+	ld a, [wCurPartySpecies]
+	call PlayMonCry2
+	ret
+
+.fail
+	ld a, 1
+	ld [wCurPartySpecies], a
+	ret
```

The Hall of Fame uses its own `HOF_AnimateFrontpic` instead of the usual `AnimateFrontpic`, so we copied its extra behavior. We also increased the delay for displaying new Hall of Famers from 60 frames to 180, the same as G/S, since sprite animations aren't contributing to the delay any more.

Edit [mobile/mobile_42.asm](../blob/master/mobile/mobile_42.asm):

```diff
 Function108219:
 	ld [wCurPartySpecies], a
-	hlcoord 7, 2
-	ld d, $0
-	ld e, ANIM_MON_TRADE
-	predef AnimateFrontpic
+	call PlayMonCry
 	ret

 Function108229:
 	ld [wCurPartySpecies], a
-	hlcoord 7, 2
-	ld d, $0
-	ld e, ANIM_MON_TRADE
-	predef LoadMonAnimation
 	ret

...

 Function1082db:
 .loop
 	farcall PlaySpriteAnimations
-	farcall SetUpPokeAnim
-	farcall HDMATransferTileMapToWRAMBank3
 	jr nc, .loop
 	ret
```

Finally, edit [mobile/mobile_5f.asm](../blob/master/mobile/mobile_5f.asm):

```diff
 Function17d93a:
 	...
 	decoord 0, 0
 	add hl, de
 	ld e, l
 	ld d, h
-	farcall HOF_AnimateFrontpic
+	farcall HOF_PlayCry
 	pop af
 	ldh [rSVBK], a
 	call Function17e349
 	ret
```


## 6. Fix the Pokémon stats screen

The stats screen doesn't call `AnimateFrontpic` *or* `HOF_AnimateFrontpic`. Instead, it calls individual low-level subroutines of the sprite animation engine. So fixing it to just play cries is a bit more tricky than with the other subsystems.

Edit [engine/pokemon/stats_screen.asm](../blob/master/engine/pokemon/stats_screen.asm):

```diff
 StatsScreen_WaitAnim:
 	ld hl, wcf64
 	bit 6, [hl]
 	jr nz, .try_anim
 	bit 5, [hl]
 	jr nz, .finish
 	call DelayFrame
 	ret
 
 .try_anim
-	farcall SetUpPokeAnim
-	jr nc, .finish
 	ld hl, wcf64
 	res 6, [hl]
 .finish
 	ld hl, wcf64
 	res 5, [hl]
 	farcall HDMATransferTileMapToWRAMBank3
 	ret
 
 ...

 StatsScreen_PlaceFrontpic:
 	ld hl, wTempMonDVs
 	predef GetUnownLetter
 	call StatsScreen_GetAnimationParam
 	jr c, .egg
 	and a
 	jr z, .no_cry
 	jr .cry
 	...
 
 .get_animation
 	ld a, [wCurPartySpecies]
 	call IsAPokemon
 	ret c
 	call StatsScreen_LoadTextBoxSpaceGFX
 	ld de, vTiles2 tile $00
 	predef GetAnimatedFrontpic
-	hlcoord 0, 0
-	ld d, $0
-	ld e, ANIM_MON_MENU
-	predef LoadMonAnimation
 	ld hl, wcf64
 	set 6, [hl]
 	ret

 StatsScreen_GetAnimationParam:
 	ld a, [wMonType]
 	ld hl, .Jumptable
 	rst JumpTable
 	ret
 
 .Jumptable:
 	dw .PartyMon
 	dw .OTPartyMon
 	dw .BoxMon
 	dw .Tempmon
 	dw .Wildmon
 
 ...
 
 .CheckEggFaintedFrzSlp:
 	ld a, [wCurPartySpecies]
 	cp EGG
 	jr z, .egg
 	call CheckFaintedFrzSlp
 	jr c, .FaintedFrzSlp
+	jr .Wildmon
+
 .egg
 	xor a
 	scf
 	ret
 
 .Wildmon:
 	ld a, $1
 	and a
 	ret
 
 .FaintedFrzSlp:
 	xor a
 	ret

 ...

 StatsScreen_AnimateEgg:
 	...
-	hlcoord 0, 0
-	ld d, $0
-	predef LoadMonAnimation
 	ld hl, wcf64
 	set 6, [hl]
 	ret
```

Before, the `farcall SetUpPokeAnim` in `StatsScreen_WaitAnim` would play the Pokémon cry. Since we deleted that, we have to fix `StatsScreen_GetAnimationParam.CheckEggFaintedFrzSlp` so that it will return `nz` and `nc` for Pokémon that are neither Eggs, nor fainted, nor frozen, nor asleep. That way `StatsScreen_PlaceFrontpic` will correctly play cries for those Pokémon.


## 7. Remove the unused animation tile graphics

If we stopped here, the animations would be gone and the cries would play correctly, but the ROM would still have leftover graphics data for the sprite animations.

One way to fix this would be to edit all 276 [gfx/pokemon/\*/front.png](../tree/master/gfx/pokemon/) files (251 Pokémon, with 26 forms for Unown), cropping each one to just be the first frame. But doing this manually is tedious, and doing it automatically (a) requires installing [GraphicsMagick](http://www.graphicsmagick.org/) and (b) risks messing up the images' [indexed color palettes](https://en.wikipedia.org/wiki/Indexed_color).

Another way is to crop the sprites *after* they've been converted from .png to .2bpp format. That's what we're going to do.

(If you're replacing the 251 Gen 2 Pokémon with your own set, then skip this step. And of course when you're drawing front sprites, just draw them as square still sprites, without any animated frames.)

Create **tools/trim_animation.sh**:

```sh
#!/bin/sh
# Usage: trim_animation.sh front.animated.2bpp front.dimensions

case $(cat $2) in
	U) bytes=400;; # $55="U"; 5*5*16=400
	f) bytes=576;; # $66="f"; 6*6*16=576
	w) bytes=784;; # $77="w"; 7*7*16=784
	*) bytes=-0;;  # invalid size; don't trim
esac

temp_file=$(mktemp)
head -c $bytes $1 > $temp_file
mv $temp_file $1
```

Then edit the [Makefile](../blob/master/Makefile):

```diff
 ### Pokemon pic animation rules

 gfx/pokemon/%/front.animated.2bpp: gfx/pokemon/%/front.2bpp gfx/pokemon/%/front.dimensions
 	tools/pokemon_animation_graphics -o $@ $^
+	tools/trim_animation.sh $@ $(word 2,$^)
-gfx/pokemon/%/front.animated.tilemap: gfx/pokemon/%/front.2bpp gfx/pokemon/%/front.dimensions
-	tools/pokemon_animation_graphics -t $@ $^
-gfx/pokemon/%/bitmask.asm: gfx/pokemon/%/front.animated.tilemap gfx/pokemon/%/front.dimensions
-	tools/pokemon_animation -b $^ > $@
-gfx/pokemon/%/frames.asm: gfx/pokemon/%/front.animated.tilemap gfx/pokemon/%/front.dimensions
-	tools/pokemon_animation -f $^ > $@
-
-
-### Terrible hacks to match animations. Delete these rules if you don't care about matching.
-
-# Dewgong has an unused tile id in its last frame. The tile itself is missing.
-gfx/pokemon/dewgong/frames.asm: gfx/pokemon/dewgong/front.animated.tilemap gfx/pokemon/dewgong/front.dimensions
-	tools/pokemon_animation -f $^ > $@
-	echo "	db \$$4d" >> $@
-
-# Lugia has two unused tile ids in its last frame. The tiles themselves are missing.
-gfx/pokemon/lugia/frames.asm: gfx/pokemon/lugia/front.animated.tilemap gfx/pokemon/lugia/front.dimensions
-	tools/pokemon_animation -f $^ > $@
-	echo "	db \$$5e, \$$59" >> $@
-
-# Girafarig has a redundant tile after the end. It is used in two frames, so it must be injected into the generated graphics.
-# This is more involved, so it's hacked into pokemon_animation_graphics.
-gfx/pokemon/girafarig/front.animated.2bpp: gfx/pokemon/girafarig/front.2bpp gfx/pokemon/girafarig/front.dimensions
-	tools/pokemon_animation_graphics --girafarig -o $@ $^
-gfx/pokemon/girafarig/front.animated.tilemap: gfx/pokemon/girafarig/front.2bpp gfx/pokemon/girafarig/front.dimensions
-	tools/pokemon_animation_graphics --girafarig -t $@ $^
```

(Be sure to use tabs, not spaces, for indenting commands in the Makefile!)

(Older versions of pokecrystal also had rules to build `gfx/pokemon/%/normal.pal`, `gfx/pokemon/%/normal.gbcpal`, and `gfx/pokemon/%/back.2bpp`, but as of September 2018 they were removed. They do not need to be changed for this tutorial.)

Let's go over what trim_animation.sh is doing. Before, this was the workflow when building the ROM:

1. `tools/gfx` converts each front.png to **front.2bpp**
2. `tools/png_dimensions` reads each front.png and writes its size as a single byte in **front.dimensions**
3. `tools/pokemon_animation_graphics` takes each pair of front.2bpp and front.dimensions and creates **front.animated.2bpp**, which is modified from front.2bpp in two ways:
   - The first frame is transposed so its tiles are in column order, not row order
   - The subsequent frames are treated as a series of *animation tiles*, and duplicate tiles are left out
4. `tools/lzcomp` compresses each front.animated.2bpp into **front.animated.2bpp.lz**

(Actually, if a **front.animated.2bpp.lz.XXXXXXXX** file exists, and the [MD5 hash](https://en.wikipedia.org/wiki/MD5) of front.animated.2bpp is XXXXXXXX, then it will just copy front.animated.2bpp.lz.XXXXXXXX as the compressed file. This is done because `tools/lzcomp` can't always reproduce the exact same compression as the original Pokémon Crystal ROM.)

Anyway, trim_animation.sh takes each front.animated.2bpp file and removes the animation tiles, keeping only the transposed tiles for the first frame. It reads front.dimensions to know how many tiles the first frames uses. Then each tile uses 16 bytes, so it keeps that many bytes of the head of the file.

That's it, we're done! This change frees up 102 KB of a 2 MB ROM: nearly 5% of the total space. Considering that an original Crystal ROM has 20% of its space unused, we've increased the free space by nearly 25%.

(As of July 23, 2018, you can measure free space after building a ROM with `make` by running `tools/free_space.awk pokecrystal.map`. The script [tools/free_space.awk](../blob/master/tools/free_space.awk) parses a .map file and adds up all the unused "slack" space.)