Gen 4 introduced Rock Climb, a field move that can scale rocky walls, as HM08. Its in-battle effect, to deal damage with a chance of confusion, already exists (for Psybeam); and its field effect is not too complex to implement. (The code for this feature was adapted from [Pokémon Orange](https://github.com/PiaCarrot/pokeorange/).) ## Contents 1. [Prepare the Rock Climb move](#1-prepare-the-rock-climb-move) 2. [Prepare the HM08 item](#2-prepare-the-hm08-item) 3. [Define a collision type for rocky walls](#3-define-a-collision-type-for-rocky-walls) 4. [Start to prepare the Rock Climb field move effect](#4-start-to-prepare-the-rock-climb-field-move-effect) 5. [Define a utility function to check for rocky walls](#5-define-a-utility-function-to-check-for-rocky-walls) 6. [Define text related to using Rock Climb](#6-define-text-related-to-using-rock-climb) 7. [Finish the Rock Climb field move effect](#7-finish-the-rock-climb-field-move-effect) 8. [Talk to rocky walls to use Rock Climb](#8-talk-to-rocky-walls-to-use-rock-climb) 9. [Add rocky walls to a map](#9-add-rocky-walls-to-a-map) ## 1. Prepare the Rock Climb move We're going to add a field effect for climbing rocky walls; but first, we have to add the move Rock Climb, following [this tutorial](Add-a-new-move). Replace `MOVE_OR_ANIM_FC` with `ROCK_CLIMB`; give it a name, description, and battle properties (`ROCK_CLIMB, EFFECT_CONFUSE_HIT, 90, NORMAL, 85, 20, 20`); give it an animation (it can share `BattleAnim_Waterfall`); and add it to Pokémon learnsets (Sandshrew, Geodude, Onix, Rhyhorn, and Gligar learn it as an Egg move). ## 2. Prepare the HM08 item We also have to add the item HM08, following [this tutorial](Add-a-new-TM-or-HM). Add an HM for `ROCK_CLIMB`; give it a name and attributes (`0, HELD_NONE, 0, CANT_SELECT | CANT_TOSS, TM_HM, ITEMMENU_PARTY, ITEMMENU_NOUSE`); associate it with the move `ROCK_CLIMB`; make it unforgettable; and add it to Pokémon base learnsets (48 Pokémon are compatible with it, including all three fully-evolved Johto starters). ## 3. Define a collision type for rocky walls Edit [constants/collision_constants.asm](../blob/master/constants/collision_constants.asm): ```diff ; collision data types (see data/tilesets/*_collision.asm) ; TileCollisionTable indexes (see data/collision_permissions.asm) COLL_FLOOR EQU $00 COLL_01 EQU $01 ; garbage COLL_03 EQU $03 ; garbage COLL_04 EQU $04 ; garbage +COLL_ROCKY_WALL EQU $06 COLL_WALL EQU $07 ... ``` And edit [data/collision_permissions.asm](../blob/master/data/collision_permissions.asm): ```diff TileCollisionTable:: ; entries correspond to COLL_* constants NONTALKABLE LANDTILE ; COLL_FLOOR NONTALKABLE LANDTILE ; COLL_01 NONTALKABLE LANDTILE ; 02 NONTALKABLE LANDTILE ; COLL_03 NONTALKABLE LANDTILE ; COLL_04 NONTALKABLE LANDTILE ; 05 - NONTALKABLE LANDTILE ; 06 + TALKABLE WALLTILE ; COLL_ROCKY_WALL NONTALKABLE WALLTILE ; COLL_WALL ... ``` Rocky walls will prompt to use Rock Climb when talked to, so they need to be `TALKABLE`. ## 4. Start to prepare the Rock Climb field move effect Now we can start to add the field move effect, following [this tutorial](Add-a-new-field-move-effect). Define `MONMENUITEM_ROCKCLIMB`; associate it with the move `ROCK_CLIMB`; and implement `MonMenu_RockClimb` for its menu action (in [engine/pokemon/mon_menu.asm](../blob/master/engine/pokemon/mon_menu.asm), or [engine/menus/start_menu.asm](../blob/master/engine/menus/start_menu.asm) in older versions of pokecrystal) like this: ```diff +MonMenu_RockClimb: + farcall RockClimbFunction + ld a, [wFieldMoveSucceeded] + cp $1 + jr nz, .Fail + ld b, $4 + ld a, $2 + ret + +.Fail: + ld a, $3 + ret ``` We still have to implement `RockClimbFunction`; but first, let's define some more components for it. ## 5. Define a utility function to check for rocky walls Edit [home/map_objects.asm](../blob/master/home/map_objects.asm): ```diff CheckHeadbuttTreeTile:: cp COLL_HEADBUTT_TREE ret z cp COLL_HEADBUTT_TREE_1D ret + +CheckRockyWallTile:: + cp COLL_ROCKY_WALL + ret ``` This isn't strictly necessary—we could just directly use `cp COLL_ROCKY_WALL` instead of `call CheckRockyWallTile`—but it's how the other field moves work. And this approach is easier to extend, if for some reason more than one collision type should act like a rocky wall. ## 6. Define text related to using Rock Climb Edit [data/text/common_2.asm](../blob/master/data/text/common_2.asm): ```diff +_AskRockClimbText:: + text "The wall is very" + line "rocky…" + + para "Want to use" + line "ROCK CLIMB?" + done + +_UsedRockClimbText:: + text_ram wStringBuffer2 + text " used" + line "ROCK CLIMB!" + prompt + +_CantRockClimbText:: + text "The wall is very" + line "rocky…" + + para "Will a #MON's" + line "move scale it?" + done ``` This could have gone in [common_1.asm](../blob/master/data/text/common_1.asm) or [common_3.asm](../blob/master/data/text/common_3.asm) instead, but [common_2.asm](../blob/master/data/text/common_2.asm) has text related to other HM moves already. We'll need this text, and the previous `CheckRockyWallTile` routine, when we define `RockClimbFunction` next. ## 7. Finish the Rock Climb field move effect Edit [engine/events/overworld.asm](../blob/master/engine/events/overworld.asm): ```diff +RockClimbFunction: + call FieldMoveJumptableReset +.loop + ld hl, .jumptable + call FieldMoveJumptable + jr nc, .loop + and $7f + ld [wFieldMoveSucceeded], a + ret + +.jumptable: + dw .TryRockClimb + dw .DoRockClimb + dw .FailRockClimb + +.TryRockClimb: + ld de, ENGINE_EARTHBADGE + farcall CheckBadge + jr c, .noearthbadge + call TryRockClimbMenu + jr c, .failed + ld a, $1 + ret + +.noearthbadge + ld a, $80 + ret + +.failed + ld a, $2 + ret + +.DoRockClimb: + ld hl, RockClimbFromMenuScript + call QueueScript + ld a, $81 + ret + +.FailRockClimb: + call FieldMoveFailed + ld a, $80 + ret + +TryRockClimbMenu: + call GetFacingTileCoord + ld c, a + push de + call CheckRockyWallTile + pop de + jr nz, .failed + xor a + ret + +.failed + scf + ret + +TryRockClimbOW:: + ld de, ENGINE_EARTHBADGE + call CheckEngineFlag + jr c, .cant_climb + + ld d, ROCK_CLIMB + call CheckPartyMove + jr c, .cant_climb + + ld a, BANK(AskRockClimbScript) + ld hl, AskRockClimbScript + call CallScript + scf + ret + +.cant_climb + ld a, BANK(CantRockClimbScript) + ld hl, CantRockClimbScript + call CallScript + scf + ret + +AskRockClimbScript: + opentext + writetext AskRockClimbText + yesorno + iftrue UsedRockClimbScript + closetext + end + +CantRockClimbScript: + jumptext CantRockClimbText + +RockClimbFromMenuScript: + reloadmappart + special UpdateTimePals + +UsedRockClimbScript: + callasm GetPartyNick + writetext UsedRockClimbText + closetext + waitsfx + playsound SFX_STRENGTH + readvar VAR_FACING + if_equal DOWN, .Down +.loop_up + applymovement PLAYER, .RockClimbUpStep + callasm .CheckContinueRockClimb + iffalse .loop_up + end + +.Down: + applymovement PLAYER, .RockClimbFixFacing +.loop_down + applymovement PLAYER, .RockClimbDownStep + callasm .CheckContinueRockClimb + iffalse .loop_down + applymovement PLAYER, .RockClimbRemoveFixedFacing + end + +.CheckContinueRockClimb: + xor a + ld [wScriptVar], a + ld a, [wPlayerStandingTile] + call CheckRockyWallTile + ret z + ld a, $1 + ld [wScriptVar], a + ret + +.RockClimbUpStep: + step UP + step_end + +.RockClimbDownStep: + step DOWN + step_end + +.RockClimbFixFacing: + turn_head UP + fix_facing + step_end + +.RockClimbRemoveFixedFacing: + remove_fixed_facing + turn_head DOWN + step_end + +AskRockClimbText: + text_far _AskRockClimbText + text_end + +UsedRockClimbText: + text_far _UsedRockClimbText + text_end + +CantRockClimbText: + text_far _CantRockClimbText + text_end ``` You can study how this routine works; it's similar to other field moves. Just like in HG/SS, you need the Earth Badge to use it. (If you don't want that, remove the two checks for `ENGINE_EARTHBADGE`.) When you're facing up or down toward a rocky wall, you can climb it. Just like waterfalls, you'll continue moving until you reach a different type of tile. Anyway, the requisite move, HM, and field effect are all done, but we still have a few improvements to make. ## 8. Talk to rocky walls to use Rock Climb Edit [engine/overworld/events.asm](../blob/master/engine/overworld/events.asm): ```diff TryTileCollisionEvent:: ... .headbutt ld a, [wFacingTileID] call CheckHeadbuttTreeTile - jr nz, .surf + jr nz, .rock_climb farcall TryHeadbuttOW jr c, .done jr .noevent + +.rock_climb + ld a, [wFacingTileID] + call CheckRockyWallTile + jr nz, .surf + farcall TryRockClimbOW + jr .done .surf farcall TrySurfOW jr nc, .noevent jr .done .noevent xor a ret .done call PlayClickSFX ld a, $ff scf ret ``` `TryTileCollisionEvent` checks for various relevant collision types, one after another, so we just add a case for rocky walls. That's it! Now `COLL_ROCKY_WALL` can be used like any other collision type—try assigning it to a block in [data/tilesets/\*_collision.asm](../tree/master/data/tilesets/). …Although, there aren't any suitable tiles for rocky walls. So let's make some. ## 9. Add rocky walls to a map Here are some rocky wall tiles edited from [Pokémon Orange](https://github.com/PiaCarrot/pokeorange/): ![Screenshot](screenshots/rocky-wall-tiles.png) Let's say you want to copy HG/SS and require Rock Climb for an item ball on Route 45. Since [maps/Route45.blk](../blob/master/maps/Route45.blk) uses the `johto` tileset, here's what that would involve: 1. Add the rocky wall tiles to [gfx/tilesets/johto.png](../blob/master/gfx/tilesets/johto.png) 2. Assign the `BROWN` color to those tiles in [gfx/tilesets/johto_palette_map.asm](../blob/master/gfx/tilesets/johto_palette_map.asm) 3. Design a rocky wall block in [data/tilesets/johto_metatiles.bin](../blob/master/data/tilesets/johto_metatiles.bin) 4. Assign the `ROCKY_WALL` collision type to that block in [data/tilesets/johto_collision.asm](../blob/master/data/tilesets/johto_collision.asm) 5. Redesign [maps/Route45.blk](../blob/master/maps/Route45.blk) to use the rocky wall block You can use [Polished Map](https://github.com/Rangi42/polished-map) to edit maps and tilesets; refer to the [new map](Add-a-new-map-and-landmark) and [new tileset](Add-a-new-tileset) tutorials for more information. Now test it out! ![Screenshot](screenshots/rock-climb.png) By the way, if you're walking on top of cliffs, it's possible to walk down onto a cave entrance. That's because the clifftops all use `COLL_FLOOR`, cave entrances use `COLL_CAVE`, and of course it's possible to walk from the former to the latter, since there's no real concept of "elevation" in Gen 2. The solution is to use `COLL_DOWN_WALL` for clifftops above cave entrances. That way you can walk *onto* the space but not *down* from it. (The top edges of cliffs already use `COLL_UP_WALL` so you can't walk between them and the ground level.)