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
|
Much of the game logic can be changed via the files in [data/](../blob/master/data/), but some things are hard-coded and can be tricky to find. This page lists things that may trip you up when hacking.
## Contents
- [Tilesets that have per-mapgroup roofs](#tilesets-that-have-per-mapgroup-roofs)
- [Maps that don't display a location sign](#maps-that-dont-display-a-location-sign)
- [Outdoor maps within indoor maps don't confuse Dig or Escape Rope](#outdoor-maps-within-indoor-maps-dont-confuse-dig-or-escape-rope)
- [Landmark limits when scrolling in the Town Map](#landmark-limits-when-scrolling-in-the-town-map)
- [Trainer classes with different battle music](#trainer-classes-with-different-battle-music)
- [`RIVAL1`'s first Pokémon has no held item](#rival1s-first-pokémon-has-no-held-item)
- [`RIVAL1` and `RIVAL2` don't print their trainer class in battle](#rival1-and-rival2-dont-print-their-trainer-class-in-battle)
- [Vital Throw always goes last](#vital-throw-always-goes-last)
## Tilesets that have per-mapgroup roofs
This is caused by `LoadTilesetGFX` in [home/map.asm](../blob/master/home/map.asm):
```asm
; These tilesets support dynamic per-mapgroup roof tiles.
ld a, [wTileset]
cp TILESET_JOHTO
jr z, .load_roof
cp TILESET_JOHTO_MODERN
jr z, .load_roof
cp TILESET_BATTLE_TOWER_OUTSIDE
jr z, .load_roof
jr .skip_roof
.load_roof
farcall LoadMapGroupRoof
.skip_roof
```
## Maps that don't display a location sign
This is caused by `ReturnFromMapSetupScript.CheckSpecialMap` in [engine/events/map_name_sign.asm](../blob/master/engine/events/map_name_sign.asm):
```asm
.CheckSpecialMap:
; These landmarks do not get pop-up signs.
cp -1
ret z
cp SPECIAL_MAP
ret z
cp RADIO_TOWER
ret z
cp LAV_RADIO_TOWER
ret z
cp UNDERGROUND_PATH
ret z
cp INDIGO_PLATEAU
ret z
cp POWER_PLANT
ret z
ld a, 1
and a
ret
```
## Outdoor maps within indoor maps don't confuse Dig or Escape Rope
Dig and Escape Rope take you out of a dungeon and back to the entrance you used. However, some dungeons are designed with an enclosed outdoor portion, and it would be bad if visiting those portions made Dig or Escape Rope take you back to *them* instead of properly outside the dungeon.
There's no "outdoor-within-indoor" map environment, so the few maps in this situation have to be hard-coded. It's caused by `LoadWarpData.SaveDigWarp` in [engine/overworld/warp_connection.asm](../blob/master/engine/overworld/warp_connection.asm):
```asm
; MOUNT_MOON_SQUARE and TIN_TOWER_ROOF are outdoor maps within indoor maps.
; Dig and Escape Rope should not take you to them.
ld a, [wPrevMapGroup]
cp GROUP_MOUNT_MOON_SQUARE ; aka GROUP_TIN_TOWER_ROOF
jr nz, .not_mt_moon_or_tin_tower
ld a, [wPrevMapNumber]
cp MAP_MOUNT_MOON_SQUARE
ret z
cp MAP_TIN_TOWER_ROOF
ret z
.not_mt_moon_or_tin_tower
```
## Landmark limits when scrolling in the Town Map
This is caused by `PokegearMap_KantoMap` and `PokegearMap_JohtoMap` in [engine/pokegear/pokegear.asm](../blob/master/engine/pokegear/pokegear.asm):
```asm
PokegearMap_KantoMap:
call TownMap_GetKantoLandmarkLimits
jr PokegearMap_ContinueMap
PokegearMap_JohtoMap:
ld d, SILVER_CAVE
ld e, NEW_BARK_TOWN
PokegearMap_ContinueMap:
...
TownMap_GetKantoLandmarkLimits:
ld a, [wStatusFlags]
bit STATUSFLAGS_HALL_OF_FAME_F, a
jr z, .not_hof
ld d, ROUTE_28
ld e, PALLET_TOWN
ret
.not_hof
ld d, ROUTE_28
ld e, VICTORY_ROAD
ret
```
If you access a map that's outside the limits, then scrolling through the Town Map can underflow and go past the defined landmark data, displaying garbage. ([Video](https://www.youtube.com/watch?v=r32agevxdw4))
## `RIVAL1`'s first Pokémon has no held item
This is caused by `InitEnemyTrainer` in [engine/battle/core.asm](../blob/master/engine/battle/core.asm):
```asm
; RIVAL1's first mon has no held item
ld a, [wTrainerClass]
cp RIVAL1
jr nz, .ok
xor a
ld [wOTPartyMon1Item], a
.ok
```
## Trainer classes with different battle music
This is caused by `PlayBattleMusic` in [engine/battle/start_battle.asm](../blob/master/engine/battle/start_battle.asm). The routine's logic is:
1. If `[wBattleType]` is `BATTLETYPE_SUICUNE` or `BATTLETYPE_ROAMING`, play `MUSIC_SUICUNE_BATTLE`.
2. If it's a wild battle, play `MUSIC_KANTO_WILD_BATTLE` if we're in Kanto, `MUSIC_JOHTO_WILD_BATTLE_NIGHT` in Johto at night, or `MUSIC_JOHTO_WILD_BATTLE` during morning and day.
3. If `[wOtherTrainerClass]` is `CHAMPION` or `RED`, play `MUSIC_CHAMPION_BATTLE`.
4. If `[wOtherTrainerClass]` is `GRUNTM` or `GRUNTF`, play `MUSIC_ROCKET_BATTLE`. (They should have included `EXECUTIVEM`, `EXECUTIVEF`, and `SCIENTIST` too…)
5. If `[wOtherTrainerClass]` is listed under `KantoGymLeaders` in [data/trainers/leaders.asm](../blob/master/data/trainers/leaders.asm), play `MUSIC_KANTO_GYM_LEADER_BATTLE`.
6. If `[wOtherTrainerClass]` is listed under `GymLeaders` in [data/trainers/leaders.asm](../blob/master/data/trainers/leaders.asm), play `MUSIC_JOHTO_GYM_LEADER_BATTLE`. (`CHAMPION`, `RED`, and the Kanto Gym leaders are listed but have already been accounted for at this point.)
7. If `[wOtherTrainerClass]` is `RIVAL2` and `[wOtherTrainerID]` is at least `RIVAL2_2_CHIKORITA` (i.e. we're battling our rival in Indigo Plateau), play `MUSIC_CHAMPION_BATTLE`.
8. If `[wOtherTrainerClass]` is `RIVAL1` or `RIVAL2`, play `MUSIC_RIVAL_BATTLE`.
9. If it's a link battle, play `MUSIC_JOHTO_TRAINER_BATTLE`.
10. Play `MUSIC_KANTO_TRAINER_BATTLE` if we're in Kanto or `MUSIC_JOHTO_TRAINER_BATTLE` if we're in Johto.
## `RIVAL1` and `RIVAL2` don't print their trainer class in battle
Both of these classes are named "RIVAL", but battles just print "SILVER wants to battle!", not "RIVAL SILVER wants to battle!"
This is caused by `PlaceEnemysName` in [home/text.asm](../blob/master/home/text.asm):
```asm
ld a, [wTrainerClass]
cp RIVAL1
jr z, .rival
cp RIVAL2
jr z, .rival
```
## Vital Throw always goes last
Most move effects' priorities are specified in `MoveEffectPriorities` in [data/moves/effects_priorities.asm](../blob/master/data/moves/effects_priorities.asm).
...except for Vital Throw. This move shares its effect with a lot of other moves, and they couldn't be bothered to make a new move effect ID for it like `EFFECT_PRIORITY_HIT`, so they hard-coded this case, in `GetMovePriority` of [engine/battle/core.asm](../blob/master/engine/battle/core.asm):
```asm
GetMovePriority:
; Return the priority (0-3) of move a.
ld b, a
; Vital Throw goes last.
cp VITAL_THROW
ld a, 0
ret z
```
|