-
Notifications
You must be signed in to change notification settings - Fork 801
Rock Climb
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.)
- Prepare the Rock Climb move
- Prepare the HM08 item
- Define a collision type for rocky walls
- Start to prepare the Rock Climb field move effect
- Define a utility function to check for rocky walls
- Define text related to using Rock Climb
- Finish the Rock Climb field move effect
- Talk to rocky walls to use Rock Climb
- Add rocky walls to a map
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.
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).
We also have to add the item HM08, following this tutorial.
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).
Edit constants/collision_constants.asm:
; collision data types (see data/tilesets/*_collision.asm)
; TileCollisionTable indexes (see data/collision/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/collision_permissions.asm:
TileCollisionTable::
; entries correspond to COLL_* constants
db LANDTILE ; COLL_FLOOR
db LANDTILE ; COLL_01
db LANDTILE ; 02
db LANDTILE ; COLL_03
db LANDTILE ; COLL_04
db LANDTILE ; 05
- db LANDTILE ; 06
+ db WALLTILE ; COLL_ROCKY_WALL
db WALLTILE ; COLL_WALL
...
Rocky walls will prompt to use Rock Climb when talked to. It needs to be "WALLTILE" so the player can't walk on it.
Now we can start to add the field move effect, following this tutorial.
Define MONMENUITEM_ROCKCLIMB
; associate it with the move ROCK_CLIMB
; and implement MonMenu_RockClimb
for its menu action (in engine/pokemon/mon_menu.asm, or engine/menus/start_menu.asm in older versions of pokecrystal) like this:
+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.
Edit home/map_objects.asm:
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.
Edit data/text/common_2.asm:
+_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 or common_3.asm instead, but 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.
Edit engine/events/overworld.asm:
+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
+ loadvar VAR_MOVEMENT, PLAYER_NORMAL
+ special UpdatePlayerSprite
+ 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, [wPlayerTileCollision]
+ 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.
loadvar VAR_MOVEMENT, PLAYER_NORMAL
followed special UpdatePlayerSprite
will make sure you won't climb with a bike.
Anyway, the requisite move, HM, and field effect are all done, but we still have a few improvements to make.
Edit engine/overworld/events.asm:
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.
…Although, there aren't any suitable tiles for rocky walls. So let's make some.
Here are some rocky wall tiles edited from Pokémon Orange:
Let's say you want to copy HG/SS and require Rock Climb for an item ball on Route 45. Since maps/Route45.blk uses the johto
tileset, here's what that would involve:
- Add the rocky wall tiles to gfx/tilesets/johto.png
- Assign the
BROWN
color to those tiles in gfx/tilesets/johto_palette_map.asm - Design a rocky wall block in data/tilesets/johto_metatiles.bin
- Assign the
ROCKY_WALL
collision type to that block in data/tilesets/johto_collision.asm - Redesign maps/Route45.blk to use the rocky wall block
You can use Polished Map to edit maps and tilesets; refer to the new map and new tileset tutorials for more information.
Now test it out!
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.)