summaryrefslogtreecommitdiff
path: root/Hard-coded-logic.md
blob: b833a50dd21a1181793dda665729b26a3e0babb7 (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
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)
- [Spawn points when you start and finish the game](#spawn-points-when-you-start-and-finish-the-game)
- [Trainer classes with different battle music](#trainer-classes-with-different-battle-music)
- [Trainer classes with different victory music](#trainer-classes-with-different-victory-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))


## Spawn points when you start and finish the game

These are defined in [engine/menus/intro_menu.asm](../blob/master/engine/menus/intro_menu.asm):

```asm
	ld a, LANDMARK_NEW_BARK_TOWN
	ld [wPrevLandmark], a

	ld a, SPAWN_HOME
	ld [wDefaultSpawnpoint], a
```

```asm
.SpawnAfterE4:
	ld a, SPAWN_NEW_BARK
	ld [wDefaultSpawnpoint], a
	call PostCreditsSpawn
	jp FinishContinueFunction

SpawnAfterRed:
	ld a, SPAWN_MT_SILVER
	ld [wDefaultSpawnpoint], a
```

(The maps and coordinates that correspond to those spawn points are not hard-coded; they're in the `SpawnPoints` table in [data/maps/spawn_points.asm](../blob/master/data/maps/spawn_points.asm).)


## `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, check the region and time.
   1. If we're in Kanto, play `MUSIC_KANTO_WILD_BATTLE`.
   2. If it's night (and we must be in Johto), play `MUSIC_JOHTO_WILD_BATTLE_NIGHT`.
   3. We must be in Johto during morning or day; play `MUSIC_JOHTO_WILD_BATTLE`.
3. It must be a trainer battle; check the values of `[wOtherTrainerClass]` and `[wOtherTrainerID]`:
   1. If `[wOtherTrainerClass]` is `CHAMPION` or `RED`, play `MUSIC_CHAMPION_BATTLE`.
   2. If `[wOtherTrainerClass]` is `GRUNTM` or `GRUNTF`, play `MUSIC_ROCKET_BATTLE`. (They should have included `EXECUTIVEM`, `EXECUTIVEF`, and `SCIENTIST` too…)
   3. If `[wOtherTrainerClass]` is listed under `KantoGymLeaders` in [data/trainers/leaders.asm](../blob/master/data/trainers/leaders.asm), play `MUSIC_KANTO_GYM_LEADER_BATTLE`.
   4. 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 were already handled in step 3.i.)
   5. 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`.
   6. If `[wOtherTrainerClass]` is `RIVAL1` or `RIVAL2`, play `MUSIC_RIVAL_BATTLE`.
4. If it's a link battle, play `MUSIC_JOHTO_TRAINER_BATTLE`.
5. If we're in Kanto, play `MUSIC_KANTO_TRAINER_BATTLE`.
6. We must be in Johto; play `MUSIC_JOHTO_TRAINER_BATTLE`.


## Trainer classes with different victory music

This is caused by `PlayVictoryMusic` in [engine/battle/core.asm](../blob/master/engine/battle/core.asm). The routine's logic is:

1. Play `MUSIC_NONE`, silencing the battle music.
2. If `[wBattleMode]` is not `WILD_BATTLE` (and so must be `TRAINER_BATTLE`):
   1. If `[wOtherTrainerClass]` is listed under `GymLeaders` in [data/trainers/leaders.asm](../blob/master/data/trainers/leaders.asm), play `MUSIC_GYM_VICTORY`.
   2. It must be a regular trainer battle; play `MUSIC_TRAINER_VICTORY`.
3. It must a wild battle. If any mon is holding an Exp. Share, or we collect money from Pay Day, or we have not lost the battle, play `MUSIC_WILD_VICTORY`.
4. Do not play any victory music.


## `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
```