Adding items that act like HMs - pret/pokecrystal GitHub Wiki

This tutorial will cover how to add items that act like HM Field moves, using CUT as an example.

Contents

  1. Adding the item
  2. Fixing the success message
  3. Fixing Oak's message
  4. Including other HMs

1. Adding the item

Follow the guide on adding new items to create the item that will act like an HM. I changed ITEM_19 into CHAINSAW with the following:

constants/item_constants.asm:

	const WATER_STONE  ; 18
-	const ITEM_19      ; 19
+	const CHAINSAW     ; 19
	const HP_UP        ; 1a

data/items/names.asm:

	db "WATER STONE@"
-	db "TERU-SAMA@"
+	db "CHAINSAW@"
	db "HP UP@"

data/items/descriptions.asm:

 	dw WaterStoneDesc
-	dw TeruSama2Desc
+	dw ChainsawDesc
	dw HPUpDesc

 	...

-TeruSama2Desc:
-	db   "?@"
+ChainsawDesc:
+	db   "Cuts down pesky"
+	next "plants.@"

data/items/attributes.asm:

-; ITEM_19
-	item_attribute $9999, HELD_NONE, 0, NO_LIMITS, ITEM, ITEMMENU_NOUSE, ITEMMENU_NOUSE
+; CHAINSAW
+	item_attribute 0, HELD_NONE, 0, CANT_TOSS, KEY_ITEM, ITEMMENU_CLOSE, ITEMMENU_NOUSE

data/items/catch_rate_items.asm:

TimeCapsule_CatchRateItems:
-	db ITEM_19, LEFTOVERS

In this example, Chainsaw will act like the HM Cut, so we'll use CutFunction in engine/events/overworld.asm. A list of HM Functions is at the end of this tutorial. Since these functions are in a different bank from the item effect functions, we just need to write a small function that can farcall our HM Function. engine/items/item_effects.asm:

	dw EvoStoneEffect      ; WATER_STONE
-	dw NoEffect            ; ITEM_19
+	dw ChainsawEffect      ; CHAINSAW

	...

+ChainsawEffect:
+	farcall CutFunction
+	ret

2. Fixing the success message

The above works, but it has a few flaws. Firstly, a message will pop up saying a Pokémon used the relevant HM Effect. There are several ways to change this, but the easiest is to edit the text that's displayed when your HM's effect script is run. The HM Effect scripts will be listed at the end.

First, skip loading the Pokémon's name. At best this finds the first Pokémon that has cut, at worse it fails and the text is incorrect. engine/events/overworld.asm:

Script_Cut:
-	callasm GetPartyNickname
	writetext UseCutText

Then, we can change the text to be a bit more neutral. UseCutText points to _UseCutText in data/text/common_2.asm:

_UseCutText::
-	text_ram wStringBuffer2
-	text " used"
-	line "CUT!"
+	text "You cut some"
+	line "plants!"
	prompt

3. Fixing Oak's message

As it stands, if you use your item and it fails, you'll be told the HM's error message AND be yelled at by Oak. To avoid this, we can introduce a new temporary var that can be used to skip Oak's message for HM-like-items.

First off, we need to create a new label in wram. There's a nice chunk of free space in wram bank 0 right after wDaysSince. wram.asm:

wDaysSince:: db

+wUsingHMItem:: db

SECTION "WRAM 1", WRAMX

Next, we'll change our item effect so that it sets wUsingHMItem to 1, signifying that it's okay to skip Oak's Message. engine/items/item_effects.asm

ChainsawEffect:
+	ld a, 1
+	ld [wUsingHMItem], a
	farcall CutFunction
	ret

Finally, we'll skip Oak's message if wUsingHMItem is set. Oak's message is handled in engine/items/pack.asm:

	ld a, [wItemEffectSucceeded]
	and a
-	jr z, .Oak
+
+	; grab and reset wUsingHMItem without changing flag
+	ld hl, wUsingHMItem
+	ld a, [hl]
+	ld [hl], 0
+
+	jr z, .tryOak
	ld a, PACKSTATE_QUITRUNSCRIPT
	ld [wJumptableIndex], a
	ret

+.tryOak
+	or a
+	jr z, .Oak
+	ret

TossMenu:
	ld hl, Text_ThrowAwayHowMany

The above loads wUsingHMItem's value and resets it to 0 so Oak's message won't be skipped for normal items. If Oak's message would normally plays, it will do one quick check of wUsingHMItem's old value, and go right to returning if it wasn't zero.

4. Including other HMs

Including the other HM effects is as simple as repeating the above with different effect functions and new items. Our code to handle Oak's message based on wUsingHMItem doesn't need to be changed to allow other HMs, however every HM must include code to it to 1 (or any other nonzero value).

The following are lists of HM Functions as of time of writing. These may not all work as well as Cut, and may change naming conventions over time.

HM Function List

All of these are in engine/events/overworld.asm.

  • CutFunction
  • FlyFunction
  • SurfFunction
  • StrengthFunction
  • OWFlash
  • WhirlpoolFunction
  • WaterfallFunction

Additionally, functions like TeleportFunction can be used as well.

HM Effect Scripts

All of these are in engine/events/overworld.asm.

  • Script_Cut
  • Fly does not show a message
  • UsedSurfScript
  • Script_UsedStrength
  • Flash does not show the Pokémon's name
  • Script_UsedWhirlpool
  • Script_UsedWaterfall

Using the item as default on the overworld

Even with the item added and it being functional, the game will still check for a pokemon in the party to use the HM move when attempting to interact with a cuttable tree on the overworld. This is a simple fix.

If we look in engine/events/overworld.asm at the bottom, we'll see TryCutOW. This function is called when the player interacts with a cuttable tree, normally it checks the party first for a pokemon with cut, then for the appropriate badge, so we'll change it.

CutFunction:
...

.CheckAble:
-	ld de, ENGINE_HIVEBADGE
-	call CheckBadge
-	jr c, .nohivebadge
	call CheckMapForSomethingToCut
	jr c, .nothingtocut
	ld a, $1
	ret

-.nohivebadge
-	ld a, $80
-	ret

and

TryCutOW::
-	ld d, CUT
-	call CheckPartyMove
-	jr c, .cant_cut

-	ld de, ENGINE_HIVEBADGE
-	call CheckEngineFlag
+	ld a, CHAINSAW
+	ld [wCurItem], a
+	ld hl, wNumItems
+	call CheckItem
+	jr nc, .cant_cut

This removes the checks for an eligible pokemon and badge, and just checks for the item in the bag.