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
|
This page is for small hints about how to edit pokecrystal that don't need an entire tutorial of their own. Any sort of tips, tricks, hints, pointers to obscure but useful code routines, engine trivia—it all goes here.
## Contents
- [Change the odds of encountering a shiny Pokémon](#change-the-odds-of-encountering-a-shiny-pokémon)
- [Stop the blue palette from animating its white hue](#stop-the-blue-palette-from-animating-its-white-hue)
- [Animate tiles even when textboxes are open](#animate-tiles-even-when-textboxes-are-open)
- [Make overworld sprites darker at night](#make-overworld-sprites-darker-at-night)
- [Enemy trainers have maximum happiness for a powerful Return](#enemy-trainers-have-maximum-happiness-for-a-powerful-return)
- [Lowercasing Pokémon names cuts off their first letter](#lowercasing-pokémon-names-cuts-off-their-first-letter)
- [Pan in cutscenes by making the player invisible](#pan-in-cutscenes-by-making-the-player-invisible)
- [Prevent NPCs' heads from flipping when they walk down](#prevent-NPCs-heads-from-flipping-when-they-walk-down)
- [Use HG/SS behavior for fainting without saving on the S.S. Aqua](#use-hgss-behavior-for-fainting-without-saving-on-the-s.s.-aqua)
- [Use more space in the home bank](#use-more-space-in-the-home-bank)
## Change the odds of encountering a shiny Pokémon
The odds of encountering a shiny Pokémon in Gen 2 are 1 in 8,192. That value isn't literally defined anywhere: it's a consequence of making shininess based on DVs. A shiny Pokémon has:
- Attack DV = 2, 3, 6, 7, 10, 11, 14, or 15
- Defense, Speed, and Special DVs = 10
Since all four DVs are random from 0 to 15, some multiplication shows that 1 in 8,192 Pokémon will be shiny.
The routine that checks those specific values is `CheckShininess` in [engine/gfx/color.asm](../blob/master/engine/gfx/color.asm). If you want different odds, just change which DVs count as shiny. (The `Unused_CheckShininess` routine right below it would just make a Pokémon shiny if all its DVs are 10 or higher, which gives odds of about 1 in 50.)
Also, the red Gyarados has hard-coded values for its DVs of 14/10/10/10; they're defined as `ATKDEFDV_SHINY` and `SPDSPCDV_SHINY` in [constants/battle_constants.asm](../blob/master/constants/battle_constants.asm).
## Stop the blue palette from animating its white hue
There are eight palettes used for tilesets: `GRAY`, `RED`, `GREEN`, `WATER`, `YELLOW`, `BROWN`, `ROOF`, and `TEXT`. It's called `WATER` instead of `BLUE` because its lightest hue gets animated for some tilesets, cycling between three colors. This makes water (and buoys, whirlpools, and waterfalls) look more dynamic, but it prevents you from making static blue tiles (like Mart roofs).
To stop it, edit [engine/tilesets/tileset_anims.asm](../blob/master/engine/tilesets/tileset_anims.asm) and replace all the instances of `dw NULL, AnimateWaterPalette` with `dw NULL, WaitTileAnimation`.
## Animate tiles even when textboxes are open
Some tiles in tilesets are animated: water, flowers, whirpools, waterfalls, etc. In Gold and Silver they animated even when textboxes were open, but not in Crystal. To re-enable this, edit `AnimateTileset` in [home/video.asm](../blob/master/home/video.asm):
```diff
AnimateTileset::
; Only call during the first fifth of VBlank
ldh a, [hMapAnims]
and a
ret z
-; Back out if we're too far into VBlank
- ldh a, [rLY]
- cp LY_VBLANK
- ret c
- cp LY_VBLANK + 7
- ret nc
```
(via [Crystal_](https://twitter.com/crystal_rby)'s [#OldGenFactOfTheDay](https://twitter.com/crystal_rby/status/1250089272725712904))
## Make overworld sprites darker at night
Edit [gfx/overworld/npc_sprites.pal](../blob/master/gfx/overworld/npc_sprites.pal):
```diff
; nite
- RGB 15,14,24, 31,19,10, 31,07,01, 00,00,00 ; red
- RGB 15,14,24, 31,19,10, 10,09,31, 00,00,00 ; blue
- RGB 15,14,24, 31,19,10, 07,23,03, 00,00,00 ; green
- RGB 15,14,24, 31,19,10, 15,10,03, 00,00,00 ; brown
- RGB 15,14,24, 31,19,10, 30,10,06, 00,00,00 ; pink
+ RGB 15,14,24, 16,10,09, 17,03,00, 00,00,00 ; red
+ RGB 15,14,24, 16,10,09, 05,04,27, 00,00,00 ; blue
+ RGB 15,14,24, 16,10,09, 03,10,02, 00,00,00 ; green
+ RGB 15,14,24, 16,10,09, 08,04,02, 00,00,00 ; brown
+ RGB 15,14,24, 16,10,09, 18,05,04, 00,00,00 ; pink
RGB 31,31,31, 31,31,31, 13,13,13, 00,00,00 ; silver
RGB 15,14,24, 08,13,19, 00,11,13, 00,00,00 ; tree
RGB 15,14,24, 12,09,15, 08,04,05, 00,00,00 ; rock
```
## Enemy trainers have maximum happiness for a powerful Return
Edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm):
```diff
LoadEnemyMon:
; Initialize enemy monster parameters
; To do this we pull the species from wTempEnemyMonSpecies
...
.Happiness:
; Set happiness
+ ld a, [wBattleMode]
+ dec a
+ ld a, $ff ; Give the enemy mon max happiness...
+ jr nz, .load_happiness ; ...if it's a Trainer battle.
ld a, BASE_HAPPINESS
+.load_happiness
ld [wEnemyMonHappiness], a
...
```
## Lowercasing Pokémon names cuts off their first letter
If you simply lowercase all the names in [data/pokemon/names.asm](../blob/master/data/pokemon/names.asm) and rebuild the ROM, the first letter of their names may be missing. The cause is changing `"FARFETCH'D"` to `"Farfetch'd"`, because `'d` is actually a single character. The fix is to pad its name to the same length as all the rest with an `@`, so that it becomes `"Farfetch'd@"`.
## Pan in cutscenes by making the player invisible
The player's object is always at the center of the screen. So if you want to pan the camera during a cutscene, you have to get around that. The key is the `show_person` and `hide_person` movement commands, which make an object visible or invisible.
Let's say you have a map with two placeholder sprites: `YOURMAP_CHRIS_PLACEHOLDER`, an `object_event` which uses `SPRITE_CHRIS`, and `YOURMAP_KRIS_PLACEHOLDER`, which uses `SPRITE_KRIS`. Then define these movement scripts:
```
ShowPersonMovement:
show_person
step_end
HidePersonMovement:
hide_person
step_end
```
In your cutscene script, to show the placeholder and make the player invisible:
```
checkflag ENGINE_PLAYER_IS_FEMALE
iftrue .ShowGirlPlaceholder
appear YOURMAP_CHRIS_PLACEHOLDER
sjump .HidePlayer
.ShowGirlPlaceholder
appear YOURMAP_KRIS_PLACEHOLDER
.HidePlayer
applymovement PLAYER, HidePersonMovement
```
And to make the player visible again and disappear the placeholder:
```
applymovement PLAYER, ShowPersonMovement
checkflag ENGINE_PLAYER_IS_FEMALE
iftrue .HideGirlPlaceholder
disappear YOURMAP_CHRIS_PLACEHOLDER
sjump .HidPlaceholder
.HideGirlPlaceholder
disappear YOURMAP_KRIS_PLACEHOLDER
.HidPlaceholder
```
In between those script snippets, you can move the player around to look like the camera is panning around the map.
## Prevent NPCs' heads from flipping when they walk down
When NPCs walk up or down, they flip horizontally every other frame to create the alternating left-right step animation. But for NPCs with asymmetrical faces, this can look awkward when they walk down.
To disable this flipping for all overworld sprites, edit [data/sprites/facings.asm](../blob/master/data/sprites/facings.asm):
```diff
FacingStepDown3: ; walking down 2
db 4 ; #
- db 0, 8, X_FLIP, $80
- db 0, 0, X_FLIP, $81
+ db 0, 0, 0, $80
+ db 0, 8, 0, $81
db 8, 8, RELATIVE_ATTRIBUTES | X_FLIP, $82
db 8, 0, RELATIVE_ATTRIBUTES | X_FLIP, $83
```
Note that the player sprite in the naming screen and the Town Map are unaffected by this. Also, this prevents the bicycling player sprite from moving side to side.
## Use HG/SS behavior for fainting without saving on the S.S. Aqua
You may have noticed how when you get on the S.S. Aqua or go to either port without saving, then white out, that you're taken to either your cabin or the Vermilion/Olivine Pokémon Center instead of the Pokémon Center you last used.
To go to the last Pokémon Center, like HG/SS does, remove the `blackoutmod` script commands from [maps/OlivinePort.asm](../blob/master/maps/OlivinePort.asm), [maps/VermilionPort.asm](../blob/master/maps/VermilionPort.asm), and [maps/FastShip1F.asm](../blob/master/maps/FastShip1F.asm).
## Use more space in the home bank
The home bank, ROM0, is important because its code is accessible no matter which ROMX bank is currently switched to. It's only $4000 (16,384) bytes, and some of those are required for the `rst` and interrupt [jump vectors](https://gbdev.github.io/pandocs/#jump-vectors-in-first-rom-bank) and the [cartridge header](https://gbdev.github.io/pandocs/#cartridge-header-in-first-rom-bank).
All the home code in pokecrystal is `INCLUDE`d in the "Home" `SECTION` in [home.asm](../blob/master/home.asm), [starting](../blob/master/pokecrystal.link) at address $0150. However, if you really need more space, you *can* put code in [home/header.asm](../blob/master/home/header.asm), as long as you're careful not to interfere with the interrupts or the cartridge header. In particular, between the joypad interrupt at $0060 and the cartridge header at $0150, there are 157 unused bytes ($0100 - $0060 = $00A0 = 160, three bytes of which are taken by `jp Joypad`).
For very short pieces of code or data, you can even put them in the unused space of the `rst` and interrupt vectors. For example, `jp VBlank` only uses three of the eight bytes in the "vblank" `SECTION`.
|