Adding a Tradeback NPC - pret/pokecrystal GitHub Wiki

This tutorial explains how to add an NPC that the player can trade a Pokémon to and immediately back.

Contents

  1. Purpose of a Tradeback NPC
  2. Define a New File to Be Imported
  3. Create a Special Routine
  4. Create a Tradeback NPC
  5. Create the Tradeback NPC Function

1. Purpose of a Tradeback NPC

This provides an alternative way for the player to evolve Pokémon who normally evolve after being traded (either by themselves or with an item) within a single game.

Pokémon who evolve by trading:

  • Machoke ------> Machamp
  • Haunter -------> Gengar
  • Graveler -------> Golem
  • Kadabra -------> Alakazam

Pokémon who evolve by trading with an item:

  • Porygon ----(Holding: Upgrade)-------> Porygon2
  • Poliwhirl ----(Holding: King's Rock)----> Politoed
  • Slowpoke ---(Holding: King's Rock)----> Slowking
  • Onix --------(Holding: Metal Coat)-----> Steelix
  • Scyther -----(Holding: Metal Coat)-----> Scizor
  • Seadra -----(Holding: Dragon Scale)---> Kingdra

2. Define a New File to Be Imported

In this first section we will be adding a file (of which we will soon create) to the list of files that will be imported into the game.

We can add this to any section we like but only if there is enough space (otherwise we would get a "Sections would extend past the end of ROMX" error while compiling and dealing with this error is beyond the scope of this tutorial.

For this tutorial, we will be using "bank3F".

Edit main.asm:

SECTION "bank3F", ROMX

INCLUDE "engine/tilesets/tileset_anims.asm"
INCLUDE "engine/events/npc_trade.asm"
INCLUDE "engine/events/mom_phone.asm"
+INCLUDE "engine/events/tradeback_npc.asm"

3. Create a Special Routine

In this next section we will be adding a "special pointer" which will allow us to create a corresponding "special routine" that can be called with the "special" map script command.

Special Pointer

Add a new special pointer for the Tradeback NPC to the bottom of the file. The comment is not needed if you don't want it, but it can be useful to keep track of what you've added to the game.

Edit data/events/special_pointers.asm:

	add_special LoadMapPalettes
	add_special UnusedFindItemInPCOrBag

	add_special InitialSetDSTFlag
	add_special InitialClearDSTFlag
	add_special UnusedDummySpecial ; unused

+	add_special TradebackNPC

Special Routine

The second part of this section is to create a "special routine" for the corresponding "special pointer" that we just created. This special routine will hook the NPC that we will make in the next section, to the file that we will make in the very last section.

Firstly, we need to make a label that matches the special pointer we just added and have it perform the following commands:

  1. Farcall the label that will be in the file we will make at the end.
  2. Return to the place that called us here initially once everything is done.

Add the following code to the bottom of the file.

Edit engine/events/specials.asm:

TrainerHouse:
	ld a, BANK(sMysteryGiftTrainerHouseFlag)
	call OpenSRAM
	ld a, [sMysteryGiftTrainerHouseFlag]
	ld [wScriptVar], a
	jp CloseSRAM

+TradebackNPC:
+	farcall TradebackGuy
+	ret

4. Create a Tradeback NPC

Now we need to create the Tradeback NPC object who can actually perform the tradeback function once it is created.

Choose a map that you want your Tradeback NPC to appear in, and open the .asm file for that map. In this example, We will use Elm's Lab as it is an easy starting location for testing, but you can put the NPC anywhere you like.

Object Constant

First we will add a new object constant for the Tradeback NPC (this is not necessary for this tutorial, but it is essential if you wish to control them outside of this script).

Edit maps/ElmsLab.asm:

	object_const_def
	const ELMSLAB_ELM
	const ELMSLAB_ELMS_AIDE
	const ELMSLAB_POKE_BALL1
	const ELMSLAB_POKE_BALL2
	const ELMSLAB_POKE_BALL3
	const ELMSLAB_OFFICER
+	const ELMSLAB_TRADEBACKNPC

Keep this file open for the next step.

Object Script

Now we will add an object script to control the Tradeback NPC's actions when you interact with them (scripts can also be called from player interactions, other scripts and scenes but that's beyond the scope of this tutorial).

Firstly, we will make a label for the Tradeback NPC to be attached to. Within that script we will have it perform the following commands:

  1. Make the Tradeback NPC face the player.
  2. Show a text box.
  3. Call the special routine that we created previously.
  4. Wait until a button is pressed to proceed.
  5. Close the text box.
  6. Call for an exit to the script's subroutine.

You can place this script anywhere in this file as long as it is not interrupting other code, but you should place it where other NPC scripts are located.

We are going to place it below all the other NPC scripts.

Here's what it will look like:

.Normal:
	writetext ElmsLabWindowText1
	waitbutton
	closetext
	end

+TradebackNPCScript:
+	faceplayer
+	opentext
+	special TradebackNPC
+	waitbutton
+	closetext
+	end

ElmsLabTravelTip1:
	jumptext ElmsLabTravelTip1Text

Keep this file open for the next step.

Object Event

The last thing we have to do in this section is create the NPC object itself. We will define their initial location, select their sprite, give them movement patterns (in this tutorial they are static and don't move), their color palette and more.

Please view macros/scripts/maps.asm and search for the comments under "MACRO object_event" (excluding the quotations) for information regarding the options available for defining object events.

For the purposes of this tutorial, we will duplicate Professor Elm's NPC type and use SPRITE_RED for the new NPC's appearance.

	def_object_events
	object_event  5,  2, SPRITE_ELM, SPRITEMOVEDATA_STANDING_DOWN, 0, 0, -1, -1, 0, OBJECTTYPE_SCRIPT, 0, ProfElmScript, -1
	object_event  2,  9, SPRITE_SCIENTIST, SPRITEMOVEDATA_SPINRANDOM_SLOW, 0, 0, -1, -1, PAL_NPC_BLUE, OBJECTTYPE_SCRIPT, 0, ElmsAideScript, EVENT_ELMS_AIDE_IN_LAB
	object_event  6,  3, SPRITE_POKE_BALL, SPRITEMOVEDATA_STILL, 0, 0, -1, -1, 0, OBJECTTYPE_SCRIPT, 0, CyndaquilPokeBallScript, EVENT_CYNDAQUIL_POKEBALL_IN_ELMS_LAB
	object_event  7,  3, SPRITE_POKE_BALL, SPRITEMOVEDATA_STILL, 0, 0, -1, -1, 0, OBJECTTYPE_SCRIPT, 0, TotodilePokeBallScript, EVENT_TOTODILE_POKEBALL_IN_ELMS_LAB
	object_event  8,  3, SPRITE_POKE_BALL, SPRITEMOVEDATA_STILL, 0, 0, -1, -1, 0, OBJECTTYPE_SCRIPT, 0, ChikoritaPokeBallScript, EVENT_CHIKORITA_POKEBALL_IN_ELMS_LAB
	object_event  5,  3, SPRITE_OFFICER, SPRITEMOVEDATA_STANDING_UP, 0, 0, -1, -1, PAL_NPC_BLUE, OBJECTTYPE_SCRIPT, 0, CopScript, EVENT_COP_IN_ELMS_LAB
+	object_event  4,  1, SPRITE_RED, SPRITEMOVEDATA_STANDING_DOWN, 0, 0, -1, -1, 0, OBJECTTYPE_SCRIPT, 0, TradebackNPCScript, -1

5. Create the Tradeback NPC Function

In this final section, we will create a new file that will house the entire function that will trigger when the "TradebackGuy" label is called by the "TradebackNPC" special routine.

Create a new .asm file, save it as "tradeback_npc.asm" and place it in engine/events.

Finally, copy and paste the following code block into it:

; Fun fact, the 'trade' part of a trade isn't checked to evolve a Pokémon.
	; It seems that just the trade animation and the link state are enough.
	; No Pokémon is actualy ever moved to or from your party because of that fact.

TradebackGuy::
	ld hl, TradebackGuyText
	call PrintText
	call YesNoBox
	ld hl, TradebackGuyCanceledText
	jr c, .done

; Select a Pokémon from the party.
	ld b, PARTYMENUACTION_GIVE_MON
	farcall SelectTradeOrDayCareMon
	ld a, [wCurPartyMon]
	ld hl, TradebackGuyCanceledText
	jr c, .done

	ld hl, NPCTradeCableText
	call PrintText

	call TradeWithTradebackGuy
	call RestartMapMusic

	ld hl, TradebackGuyCompleteText
	call PrintText
	ret
.done
	call PrintText
	ret

; Loads the appropriate data to perform the trade animation.
TradeWithTradebackGuy:
; Sets the link state to trading so that evolution is possible.
	ld a, LINK_TRADECENTER
	ld [wLinkMode], a

; Establish names of trading trainers
	ld hl, wPlayerName
	ld de, wPlayerTrademonSenderName
	ld bc, NAME_LENGTH
	call CopyBytes

	ld hl, .tradeback_guy_name
	ld de, wOTTrademonSenderName
	ld bc, NAME_LENGTH
	call CopyBytes
.tradeback_guy_name:
	db "TBGUY@@@@@"

; Establish the Pokémon's species.
	ld a, [wCurPartyMon]
	ld hl, wPartySpecies
	ld b, 0
	ld c, a
	add hl, bc
	ld a, [hl]
	ld [wPlayerTrademonSpecies], a
	ld [wOTTrademonSpecies], a

; Establish Pokémon's ID number.
	ld hl, wPartyMon1ID
	ld a, [wCurPartyMon]
	call GetPartyLocation
	ld a, [hli]
	ld [wPlayerTrademonID], a
	ld [wOTTrademonID], a
	ld a, [hl]
	ld [wPlayerTrademonID + 1], a
	ld [wOTTrademonID + 1], a

; Correctly display Pokémon shiny status on the trade screen.
	ld hl, wPartyMon1DVs
	ld a, [wCurPartyMon]
	call GetPartyLocation
	ld a, [hli]
	ld [wPlayerTrademonDVs], a
	ld [wOTTrademonDVs], a
	ld a, [hl]
	ld [wPlayerTrademonDVs + 1], a
	ld [wOTTrademonDVs + 1], a 

; Establish Pokémon's OT's name
	ld a, [wCurPartyMon]
	ld hl, wPartyMonOTs
	call SkipNames
	ld de, wPlayerTrademonOTName
	ld bc, NAME_LENGTH
	call CopyBytes

	ld hl, wPartyMonOTs
	ld de, wOTTrademonOTName
	ld bc, NAME_LENGTH
	call CopyBytes

; Makes it so that pressing B will not cancel the evolution.
	; This is standard for trade based evolution.
	ld a, 1
	ld [wForceEvolution], a

; Run the trade animation/ evolves the mon if applicable.
	call DisableSpriteUpdates
; wTradeDialog aliases wFrameCounter, which TradeAnimation uses.
	ld a, [wTradeDialog]
	push af
	predef TradeAnimation
	callfar EvolvePokemon
	pop af
	ld [wTradeDialog], a
	call ReturnToMapWithSpeechTextbox

; Changes the link mode back to not linked, battles won't work right otherwise.
	ld a, LINK_NULL
	ld [wLinkMode], a
	ret

TradebackGuyText::
	text "Hello there! I'm"
	line "the TRADEBACK GUY."

	para "I can trade a"
	line "#MON of your"
	cont "choosing back to"
	cont "you."

	para "Would you like to"
	line "trade?"
	done

TradebackGuyCanceledText::
	text "Oh, ok then."

	para "Come back if you"
	line "change your mind."
	done

TradebackGuyCompleteText::
	text "And… Done!"

	para "I hope that"
	line "was helpful!"
	done

Congratulations! You now have your very own Tradeback NPC!