summaryrefslogtreecommitdiff
path: root/Adding-an-NPC-that-will-trade-your-own-pokémon-back-for-evolution-purposes.md
blob: a15e64f6b8829f3fc32db41dabcc575a33fce57d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
This is a copy from the tutorial, it has small changes.

## Contents
- [Add an NPC to trade with](#1-add-an-npc-to-trade-with)
- [Add a new in game trade](#2-add-a-new-in-game-trade)
- [Allow pokemon to evolve from in game trades](#3-allow-pokemon-to-evolve-from-in-game-trades)
- [Edit the in game trading system](#4-edit-the-in-game-trading-system)

## 1. Add an NPC to trade with

The first step to get your very own tradeback guy is adding an NPC to do the trading with, this is extremely simple. Just pick a location. For our example we will use CeladonMartF1. We'll start by opening it's header file and placing an NPC in it. The NPCs are added in the following format:

object SPRITE constant, Coord1, Coord2, Movement Type, Movement Direction, Map Script Pointer Number

That in mind edit [data/maps/objects/CeladonMart1F.asm](../blob/master/data/maps/objects/CeladonMart1F.asm):
```diff
	...
	def_signs
	sign 11,  4, 2 ; CeladonMart1Text2
	sign 14,  1, 3 ; CeladonMart1Text3

	def_objects
	object SPRITE_LINK_RECEPTIONIST, 8, 3, STAY, DOWN, 1 ; person
+	object_event  3,  2, SPRITE_COOLTRAINER_M, STAY, DOWN, 4 ; person

	def_warps_to CELADON_MART_1F
```
 We've placed him near the elevator against the wall. He stands in place and looks down, and he's attached to the 4th script that well be adding next.

Next we need to add the script the NPC will be using to the map's script file.
Edit [scripts/CeladonMart1F.asm](../blob/master/scripts/CeladonMart1F.asm):
```diff
CeladonMart1F_Script:
	jp EnableAutoTextBoxDrawing

CeladonMart1F_TextPointers:
	dw CeladonMart1Text1
	dw CeladonMart1Text2
	dw CeladonMart1Text3
+	dw CeladonMartTrader

CeladonMart1Text1:
	text_far _CeladonMart1Text1
	text_end

CeladonMart1Text2:
	text_far _CeladonMart1Text2
	text_end

CeladonMart1Text3:
	text_far _CeladonMart1Text3
	text_end

+CeladonMartTrader:
+	text_asm
+	ld a, TRADE_WITH_SELF
+	ld [wWhichTrade], a
+	predef DoInGameTradeDialogue
+	jp TextScriptEnd
```
If you're familiar with in game trades in red, you may recognize the script we attached to our NPC. We'll be diving more into that in the next step.

## 2. Add a new in game trade

The way we'll be implementing our tradeback NPC is by hijacking the already existing in game trades code. We're getting ahead of ourselves though. First we need to add a new trade that we can tie that to.

For this step we'll start by creating a new constant for said trade. If you wanted to add custom in game trades this is how you would do it by the way.
Edit [constants/script_constants.asm](../blob/master/constants/script_constants.asm):
```diff
...
; in game trades
; TradeMons indexes (see data/events/trades.asm)
	const_def
	const TRADE_FOR_TERRY
	const TRADE_FOR_MARCEL
	const TRADE_FOR_CHIKUCHIKU
	const TRADE_FOR_SAILOR
	const TRADE_FOR_DUX
	const TRADE_FOR_MARC
	const TRADE_FOR_LOLA
	const TRADE_FOR_DORIS
	const TRADE_FOR_CRINKLES
	const TRADE_FOR_SPOT
+	const TRADE_WITH_SELF

; in game trade dialog sets
; InGameTradeTextPointers indexes (see engine/events/in_game_trades.asm)
	const_def
	const TRADE_DIALOGSET_CASUAL
	const TRADE_DIALOGSET_POLITE
	const TRADE_DIALOGSET_HAPPY
+	const TRADE_DIALOGSET_SELF

; badges
; wObtainedBadges and wBeatGymFlags bits
	const_def
```
While we were here we also added a constant for the dialogset we'll be using. Our Tradeback NPC shouldn't say the same things that the other in game trade NPCS say after all.

Next we add the details for the new trade itself, if you were just adding a new trade and not making a tradeback guy you would obviously input the relevant information here, and in that case, provided you use an already existing dialogset, you would be done. We're going for the tradeback guy though so we'll continue on.
Edit [data/events/trades.asm](../blob/master/data/events/trades.asm):
```diff
TradeMons:
; entries correspond to TRADE_FOR_* constants
	; give mon, get mon, dialog id, nickname
	db NIDORINO,   NIDORINA,  TRADE_DIALOGSET_CASUAL, "TERRY@@@@@@"
	db ABRA,       MR_MIME,   TRADE_DIALOGSET_CASUAL, "MARCEL@@@@@"
	db BUTTERFREE, BEEDRILL,  TRADE_DIALOGSET_HAPPY,  "CHIKUCHIKU@"
	db PONYTA,     SEEL,      TRADE_DIALOGSET_CASUAL, "SAILOR@@@@@"
	db SPEAROW,    FARFETCHD, TRADE_DIALOGSET_HAPPY,  "DUX@@@@@@@@"
	db SLOWBRO,    LICKITUNG, TRADE_DIALOGSET_CASUAL, "MARC@@@@@@@"
	db POLIWHIRL,  JYNX,      TRADE_DIALOGSET_POLITE, "LOLA@@@@@@@"
	db RAICHU,     ELECTRODE, TRADE_DIALOGSET_POLITE, "DORIS@@@@@@"
	db VENONAT,    TANGELA,   TRADE_DIALOGSET_HAPPY,  "CRINKLES@@@"
	db NIDORAN_M,  NIDORAN_F, TRADE_DIALOGSET_HAPPY,  "SPOT@@@@@@@"
+	db NO_MON,     NO_MON, 	  TRADE_DIALOGSET_SELF,   "Unseen@@@@@"
```
Our choice of NO_MON here actually serves a purpose, no matter how many trades you add it is unlikely that you will ever trade missingno. Though if you intend to, change things accordingly. You'll also notice that we're using our new dialogset constant here, we'll add the text for that towards the end. The nickname is entirely unimportant as it won't ever be used.

## 3. Allow pokemon to evolve from in game trades

Before we change that code for in game trades to accommodate our Tradeback NPC, we're going to make sure they can evolve through this method. this is exceedingly easy as it just involves deleting some code in one file. It also would enable you to have players trade for a mon that would actually evolve upon receipt.
Edit [engine/events/evolve_trade.asm](../blob/master/engine/events/evolve_trade.asm):
```diff
...
; This was fixed in Yellow.

-	ld a, [wInGameTradeReceiveMonName]
-
-	; GRAVELER
-	cp "G"
-	jr z, .ok
-
-	; "SPECTRE" (HAUNTER)
-	cp "S"
-	ret nz
-	ld a, [wInGameTradeReceiveMonName + 1]
-	cp "P"
-	ret nz
-
-.ok
-	ld a, [wPartyCount]
-	dec a
-	ld [wWhichPokemon], a
	ld a, $1
	ld [wForceEvolution], a
	ld a, LINK_STATE_TRADING
	ld [wLinkState], a
	callfar TryEvolvingMon
	xor a ; LINK_STATE_NONE
	ld [wLinkState], a
	jp PlayDefaultMusic
```
Note that if for some reason you weren't interested in a Tradeback NPC, you could instead stop deleting just above ```ld a, [wPartyCount]```, your traded mons would evolve just fine and you would be done here. That bit of code is moved to the next file for our continued purposes though.

## 4. Edit the in game trading system

Now I promise that sounds and looks a lot more complicated that anything we're actually doing here. The vast majority of what we're doing is just checking to see if the trade was called by the Tradeback NPC and skipping portions of code if so. Things will only be skipped if you're trading with the tradeback NPC, other in game trades will be entirely unaffected.

The next several edits all take place in the same file. They are all done in descending fashion so you can do them by scrolling down through the file rather than jumping around within it. I'll explain each as we go.
Edit [engine/events/in_game_trades.asm](../blob/master/engine/events/in_game_trades.asm):

Here we skip the "PLAYER traded X for Y" text that comes after the trade, that information is a bit redundant and it also loads names using the table from step 2, meaning if we showed it it would claim we traded MISSINGNO for MISSINGNO. It could be fixed, but like I said before, it's a tad redundant for the tradback NPC anyway
```diff
...
; if the trade hasn't been done yet
	xor a
	ld [wInGameTradeTextPointerTableIndex], a
	call .printText
	ld a, $1
	ld [wInGameTradeTextPointerTableIndex], a
	call YesNoChoice
	ld a, [wCurrentMenuItem]
	and a
	jr nz, .printText
	call InGameTrade_DoTrade
	jr c, .printText
+	ld a, [wInGameTradeGiveMonSpecies]
+	cp NO_MON
+	jr z, .printText
	ld hl, TradedForText
	call PrintText
.printText
```

Here we skip the check to see that you offered the mon from the table in step 2, we also need to change a jr to a jp because our additions may push some code out of jr range
```diff
...
InGameTrade_DoTrade:
	xor a ; NORMAL_PARTY_MENU
	ld [wPartyMenuTypeOrMessageID], a
	dec a
	ld [wUpdateSpritesEnabled], a
	call DisplayPartyMenu
	push af
	call InGameTrade_RestoreScreen
	pop af
	ld a, $1
	jp c, .tradeFailed ; jump if the player didn't select a pokemon
	ld a, [wInGameTradeGiveMonSpecies]
+	cp NO_MON
+	jr z, .skip_mon_check
	ld b, a
	ld a, [wcf91]
	cp b
	ld a, $2
-	jr nz, .tradeFailed ; jump if the selected mon's species is not the required one
+	jp nz, .tradeFailed ; jump if the selected mon's species is not the required one
+.skip_mon_check
```

Just under that we're skipping the setting of the flag that tells the game we've done this trade and shouldn't be able to do it again, if we didn't do this our tradeback NPC would only be good once. This way we can trade infinitely.
```diff
	...
	call AddNTimes
	ld a, [hl]
	ld [wCurEnemyLVL], a
+	ld a, [wInGameTradeGiveMonSpecies]
+	cp NO_MON
+	jr z, .skip_flag_set
	ld hl, wCompletedInGameTradeFlags
	ld a, [wWhichTrade]
	ld c, a
	ld b, FLAG_SET
	predef FlagActionPredef
+.skip_flag_set
	ld hl, ConnectCableText
	call PrintText
	ld a, [wWhichPokemon]
```

There is no space between the above code and this code, we broke for explanation. With this edit we're setting up a different beginning to the code that determines the pokemon being traded and received. This is mostly for the sake of the trade movie as will become apparent in a bit.
```diff
	push af
	ld a, [wCurEnemyLVL]
	push af
	call LoadHpBarAndStatusTilePatterns
+	ld a, [wInGameTradeGiveMonSpecies]
+	cp NO_MON
+	jr nz, .normal_in_game_trade_data
+	call TradeSelf_PrepareTradeData
+	jr .self_trade_data
+.normal_in_game_trade_data
	call InGameTrade_PrepareTradeData
+.self_trade_data
	predef InternalClockTradeAnim
```

This first line doesn't NEED to be removed, but it's kinda just wasting space, the addition below it actually skips the removal of a mon from your party and the addition of another one. You might call this the actual trade part, but for our purposes we don't actually NEED to remove a mon from the party for the evolve attempt to be made, the smoke and mirrors of it are enough. The addition below that is the block of code from step 3 that we could have left alone if we weren't making a Tradeback NPC. Also the point we skip to is on the other side of it. This enables normal trades to use the code while the tradeback NPC avoids it.
```diff
	...
-	ld [wMonDataLocation], a ; not used
+	push af
+	ld a, [wInGameTradeGiveMonSpecies]
+	cp NO_MON
+	jr z, .skip_swap_mons
+	pop af
	ld [wRemoveMonFromBox], a
	call RemovePokemon
	ld a, $80 ; prevent the player from naming the mon
	ld [wMonDataLocation], a
	call AddPartyMon
	call InGameTrade_CopyDataToReceivedMon
+	ld a, [wPartyCount]
+	dec a
+	ld [wWhichPokemon], a
+.skip_swap_mons
```

The next edit is the different beginning to the code that determines the pokemon being traded and received, that was mentioned earlier, basically it loads the pokemon you choose as both the sending and receiving pokemon. This marks the end of anything you could call heavy lifting in this process.
```diff
...
InGameTrade_RestoreScreen:
	call GBPalWhiteOutWithDelay3
	call RestoreScreenTilesAndReloadTilePatterns
	call ReloadTilesetTilePatterns
	call LoadScreenTilesFromBuffer2
	call Delay3
	call LoadGBPal
	ld c, 10
	call DelayFrames
	farjp LoadWildData

+TradeSelf_PrepareTradeData:
+	ld a, [wWhichPokemon]
+	ld hl, wPartySpecies
+	ld b, 0
+	ld c, a
+	add hl, bc
+	ld a, [hl]
+	ld [wTradedPlayerMonSpecies], a
+	ld [wInGameTradeReceiveMonSpecies], a
+	ld hl, wTradedPlayerMonSpecies
+	jr InGameTrade_PrepareTradeData.loaded_self_trade_instead
InGameTrade_PrepareTradeData:
	ld hl, wTradedPlayerMonSpecies
	ld a, [wInGameTradeGiveMonSpecies]
+.loaded_self_trade_instead
```

Here is where we tie in the text for our new dialog set, it ultimately farcalls the actual text, I'm pretty sure this isn't necessary and that you could just put the text here, but for consistency's sake we'll do it the way the files are structured. Note that the wrong mon text and the after trade text are copied from a different set, this is because those scenarios aren't possible with the changes we've made and therefore would never be used.
```diff
...
TradeTextPointers3:
	dw WannaTrade3Text
	dw NoTrade3Text
	dw WrongMon3Text
	dw Thanks3Text
	dw AfterTrade3Text

+TradeTextPointers4:
+	dw WannaTrade4Text
+	dw NoTrade4Text
+	dw WrongMon1Text
+	dw Thanks4Text
+	dw AfterTrade1Text

...
...
AfterTrade3Text:
	text_far _AfterTrade3Text
	text_end
+	
+WannaTrade4Text:
+	text_far _WannaTrade4Text
+	text_end
+	
+NoTrade4Text:
+	text_far _NoTrade4Text
+	text_end
+	
+Thanks4Text:
+	text_far _Thanks4Text
+	text_end
```

And the final step, add in what the Trader actually says. The following is very basic text to get the point across, You can edit this to say whatever you'd like. As a nugget of general knowledge, lines of text can be 18 characters long (including spaces and punctuation) before breaking the edge of the textbox and needing to be continued in another line.
Edit [data/text/text_7.asm](../blob/master/data/text/text_7.asm):
```diff
...
_UsedCutText::
	text_ram wcd6d
	text " hacked"
	line "away with Cut!"
	prompt
+
+_WannaTrade4Text::
+	text "I'm the TRADER, I"
+	line "can trade your own"
+	para "#mon back to"
+	line "you. Wanna trade?"
+	done
+	
+_NoTrade4Text::
+	text "Ok, maybe next"
+	line "time then."
+	done
+	
+_Thanks4Text::
+	text "All done, I hope"
+	line "that helped."
+	done
```
We're all done. With a tradeback NPC you don't have to change evolution methods for you mons if you don't want to. Enjoy!