Skip to content
futuresushi edited this page Dec 21, 2022 · 11 revisions

Gen 3 introduced Dive, a field move that can reach the seafloor, as HM08. Fully implementing Dive is fairly complicated: its in-battle effect is similar to Dig and Fly but not exactly the same, and its field effect requires a lot of changes.

(The code for this feature was adapted from Pokémon Orange.)

Here are all the changes needed to implement Dive, applied to a copy of pokecrystal. You can clone Rangi42/pokecrystal and checkout the dive branch to test it for yourself.

Screenshot

TOC

TODO

1. Start to prepare the Dive move

First, we have to add the move Dive, following this tutorial.

Replace MOVE_OR_ANIM_FC with DIVE; give it a name, description, and battle properties (DIVE, EFFECT_FLY, 80, WATER, 100, 10, 0); and add it to Pokémon learnsets (Seel and Dewgong learn it by level-up).

We still have to implement the move animation, so let's do that next.

2. Create the Dive move animation

Refer to a new tutorial for adding new move animations, including new graphics and objects.

  • constants/battle_anim_constants.asm
  • data/battle_anims/objects.asm
  • data/moves/animations.asm

3. Using Dive hides the Pokémon underwater

Edit constants/battle_constants.asm:

 ; wPlayerSubStatus4 or wEnemySubStatus4 bit flags
 	enum_start 7, -1
 	enum SUBSTATUS_LEECH_SEED
 	enum SUBSTATUS_RAGE
 	enum SUBSTATUS_RECHARGE
 	enum SUBSTATUS_SUBSTITUTE
-	enum SUBSTATUS_UNKNOWN_1
+	enum SUBSTATUS_UNDERWATER
 	enum SUBSTATUS_FOCUS_ENERGY
 	enum SUBSTATUS_MIST
 	enum SUBSTATUS_X_ACCURACY

Edit engine/battle/core.asm:

 ResidualDamage:
 ; Return z if the user fainted before
 ; or as a result of residual damage.
 ; For Sandstorm damage, see HandleWeather.

 	...

 	call SwitchTurnCore
 	xor a
 	ld [wNumHits], a
 	ld de, ANIM_SAP
 	ld a, BATTLE_VARS_SUBSTATUS3_OPP
 	call GetBattleVar
 	and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
+	jr nz, .not_flying_or_underground
+	call Call_PlayBattleAnim_OnlyIfVisible
+	jr .called
+.not_flying_or_underground
+	ld a, BATTLE_VARS_SUBSTATUS4_OPP
+	call GetBattleVar
+	and 1 << SUBSTATUS_UNDERWATER
 	call z, Call_PlayBattleAnim_OnlyIfVisible
+.called
 	call SwitchTurnCore
 HandleWrap:
 	...

 	ld a, BATTLE_VARS_SUBSTATUS3
 	call GetBattleVar
 	and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
 	jr nz, .skip_anim
+
+	ld a, BATTLE_VARS_SUBSTATUS4
+	call GetBattleVar
+	and 1 << SUBSTATUS_UNDERWATER
+	jr nz, .skip_anim

 	call SwitchTurnCore
 	xor a
 	ld [wNumHits], a
 	ld [wFXAnimID + 1], a
 	predef PlayBattleAnim
 	call SwitchTurnCore

 .skip_anim
 	...
 Call_PlayBattleAnim_OnlyIfVisible:
 	ld a, BATTLE_VARS_SUBSTATUS3
 	call GetBattleVar
 	and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
 	ret nz
+
+	ld a, BATTLE_VARS_SUBSTATUS4
+	call GetBattleVar
+	and 1 << SUBSTATUS_UNDERWATER
+	ret nz

 Call_PlayBattleAnim:
 	ld a, e
 	ld [wFXAnimID], a
 	ld a, d
 	ld [wFXAnimID + 1], a
 	call WaitBGMap
 	predef_jump PlayBattleAnim

Edit engine/battle/effect_commands.asm:

TODO

Edit engine/battle_anims/bg_effects.asm:

 BGEffect_CheckFlyDigStatus:
 	ld hl, BG_EFFECT_STRUCT_BATTLE_TURN
 	add hl, bc
 	ldh a, [hBattleTurn]
 	and $1
 	xor [hl]
 	jr nz, .player
 	ld a, [wEnemySubStatus3] ; EnemySubStatus3
 	and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
+	ret nz
+	ld a, [wEnemySubStatus4]
+	and 1 << SUBSTATUS_UNDERWATER
 	ret

 .player
 	ld a, [wPlayerSubStatus3] ; PlayerSubStatus3
 	and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
+	ret nz
+	ld a, [wPlayerSubStatus4]
+	and 1 << SUBSTATUS_UNDERWATER
 	ret

Edit engine/battle/ai/scoring.asm:

 AI_Smart_Fly:
-; Fly, Dig
+; Fly, Dig, Dive

 ; Greatly encourage this move if the player is
-; flying or underground, and slower than the enemy.
+; flying, underground, or underwater, and slower than the enemy.

 	ld a, [wPlayerSubStatus3]
 	and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
+	jr nz, .player_hidden
+	ld a, [wPlayerSubStatus4]
+	and 1 << SUBSTATUS_UNDERWATER
 	ret z

+.player_hidden
 	call AICompareSpeed
 	ret nc

 	dec [hl]
 	dec [hl]
 	dec [hl]
 	ret
 AI_Smart_PriorityHit:
 	call AICompareSpeed
 	ret c

-; Dismiss this move if the player is flying or underground.
+; Dismiss this move if the player is flying, underground, or underwater.
 	ld a, [wPlayerSubStatus3]
 	and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
 	jp nz, AIDiscourageMove
+	ld a, [wPlayerSubStatus4]
+	and 1 << SUBSTATUS_UNDERWATER
+	jp nz, AIDiscourageMove

 ; Greatly encourage this move if it will KO the player.
 	...
 AI_Smart_FutureSight:
 ; Greatly encourage this move if the player is
-; flying or underground, and slower than the enemy.
+; flying, underground, or underwater, and slower than the enemy.

 	call AICompareSpeed
 	ret nc

 	ld a, [wPlayerSubStatus3]
 	and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND
+	jr nz, .player_hidden
+	ld a, [wPlayerSubStatus4]
+	and 1 << SUBSTATUS_UNDERWATER
 	ret z

+.player_hidden
 	dec [hl]
 	dec [hl]
 	ret

4. Surf and Whirlpool do double damage to underwater Pokémon

Refer to this tutorial.

  • constants/move_effect_constants.asm
  • data/moves/moves.asm (again)
  • data/moves/effects_pointers.asm
  • data/moves/effects.asm
  • macros/scripts/battle_commands.asm
  • data/battle/effect_command_pointers.asm
  • engine/battle/effect_commands.asm (again)
  • engine/battle/ai/scoring.asm (again)

5. Prepare the HM08 item

We also have to add the item HM08, following this tutorial.

Add an HM for DIVE; 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 DIVE; make it unforgettable; and add it to Pokémon base learnsets (50 Pokémon are compatible with it).

6. Define collision types for Dive water

  • constants/collision_constants.asm
  • data/collision_permissions.asm
  • engine/overworld/tile_events.asm

7. Start to prepare the Dive field move effect

Now we can start to add the field move effect, following this tutorial.

Define MONMENUITEM_DIVE; associate it with the move DIVE; and implement MonMenu_Dive for its menu action (in engine/pokemon/mon_menu.asm, or engine/menus/start_menu.asm in older versions of pokecrystal) like this:

+MonMenu_Dive:
+	farcall DiveFunction
+	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 DiveFunction; but first, let's define some more components for it.

8. Define a utility function to check for Dive water

  • home/map_objects.asm

9. Define text related to using Dive

  • data/text/common_2.asm

10. Define the divemap and divewarp event commands

  • wram.asm
  • macros/scripts/events.asm
  • engine/overworld/scripting.asm
  • home/flag.asm
  • engine/overworld/warp_connection.asm

11. Finish the Dive field move effect

  • engine/events/overworld.asm

12. Press A on Dive water to use Dive

  • engine/overworld/events.asm

13. Design a sprite for swimming underwater

Let's add a unique sprite for the player being underwater, following this tutorial.

Define SPRITE_DIVE as a regular sprite constant; give it properties (DiveSpriteGFX, 12, WALKING_SPRITE, PAL_OW_BLUE); and create DiveSpriteGFX as gfx/sprites/dive.png:

gfx/sprites/dive.png

Note that this is the Surfing sprite from Gen 1, which resembles a Seel. It's appropriate for Dive since Seel learns Dive by level-up.

14. Use the sprite for the player when underwater

  • constants/wram_constants.asm
  • data/sprites/player_sprites.asm
  • engine/events/overworld.asm (again)
  • engine/overworld/map_setup.asm
  • engine/overworld/player_movement.asm

15. Start creating a unique tileset for underwater maps

Refer to this tutorial.

  • constants/tileset_constants.asm
  • gfx/tilesets/underwater.png
  • gfx/tilesets/underwater_palette_map.asm
  • data/tilesets/underwater_metatiles.bin
  • data/tilesets/underwater_collision.asm
  • data/tilesets.asm
  • gfx/tileset_palette_maps.asm
  • gfx/tilesets.asm

16. Can't Fly while underwater

  • engine/events/overworld.asm (again)

17. Fix bugs that affect the Dive features

  • engine/overworld/overworld.asm
  • home/map.asm

18. Animate underwater seaweed and bubbles

Refer to a new tutorial for adding new animated tiles.

  • gfx/tilesets/bubble/1.png
  • gfx/tilesets/bubble/2.png
  • gfx/tilesets/bubble/3.png
  • gfx/tilesets/bubble/4.png
  • gfx/tilesets/bubble/5.png
  • gfx/tilesets/seaweed/1.png
  • gfx/tilesets/seaweed/2.png
  • engine/tilesets/tileset_anims.asm

19. Use special palettes for underwater tiles and sprites

Refer to a new tutorial for adding new special palettes, including map (tile) and object (sprite) palettes.

  • gfx/tilesets/underwater.pal
  • gfx/tilesets/underwater_sprites.pal
  • engine/tilesets/tileset_palettes.asm
  • engine/gfx/color.asm

20. Add deep water to an overworld tileset

Refer to a new tutorial for adding new animated tiles.

  • gfx/tilesets/johto.png
  • gfx/tilesets/johto_palette_map.asm
  • data/tilesets/johto_collision.asm
  • data/tilesets/johto_metatiles.bin
  • gfx/tilesets/water/deep-water.png
  • engine/tilesets/tileset_anims.asm (again)

21. Add a map with Dive spots

Refer to this tutorial.

  • maps/Route41.blk
  • maps/Route41.asm
  • maps/Route41Underwater.blk
  • maps/Route41Underwater.asm
  • constants/event_flags.asm (again)
  • constants/map_constants.asm
  • data/maps/maps.asm
  • data/maps/attributes.asm
  • data/wild/johto_grass.asm
  • data/maps/blocks.asm
  • data/maps/scripts.asm
  • data/maps/roofs.asm
  • data/maps/outdoor_sprites.asm

22. Add a song to use while diving

RSE Dive by TriteHexagon TODO: Actually inserting and assigning the song

Clone this wiki locally