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
- Purpose of a Tradeback NPC
- Define a New File to Be Imported
- Create a Special Routine
- Create a Tradeback NPC
- 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:
- Farcall the label that will be in the file we will make at the end.
- 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:
- Make the Tradeback NPC face the player.
- Show a text box.
- Call the special routine that we created previously.
- Wait until a button is pressed to proceed.
- Close the text box.
- 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!