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](https://github.com/PiaCarrot/pokeorange/).) [Here](https://github.com/Rangi42/pokecrystal/commit/c21561ea717feef9b2eb3785c2ea112cd0d19ba0) are all the changes needed to implement Dive, applied to a copy of pokecrystal. You can `clone` [Rangi42/pokecrystal](https://github.com/Rangi42/pokecrystal/tree/dive) and `checkout` the `dive` branch to test it for yourself. ![Screenshot](screenshots/dive.png) ## TOC TODO ## 1. Start to prepare the Dive move First, we have to add the move Dive, following [this tutorial](Add-a-new-move). 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](../blob/master/constants/battle_constants.asm): ```diff ; 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](../blob/master/engine/battle/core.asm): ```diff 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 ``` ```diff 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 ... ``` ```diff 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](../blob/master/engine/battle/effect_commands.asm): TODO Edit [engine/battle_anims/bg_effects.asm](../blob/master/engine/battle_anims/bg_effects.asm): ```diff 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](../blob/master/engine/battle/ai/scoring.asm): ```diff 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 ``` ```diff 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. ... ``` ```diff 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](Add-a-new-move-effect). - 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-a-new-TM-or-HM). 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](Add-a-new-field-move-effect). Define `MONMENUITEM_DIVE`; associate it with the move `DIVE`; and implement `MonMenu_Dive` 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_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](Add-a-new-overworld-sprite). 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](screenshots/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](Add-a-new-tileset). - 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](Add-a-new-map-object-movement-behavior). - 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