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