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
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
|
; this function handles attacks with the SPECIAL_AI_HANDLING set,
; and makes specific checks in each of these attacks
; to either return a positive score (value above $80)
; or a negative score (value below $80).
; input:
; hTempPlayAreaLocation_ff9d = location of card with attack.
HandleSpecialAIAttacks: ; 16dcd (5:6dcd)
ldh a, [hTempPlayAreaLocation_ff9d]
add DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
ld a, e
cp NIDORANF
jr z, .NidoranFCallForFamily
cp ODDISH
jr z, .CallForFamily
cp BELLSPROUT
jr z, .CallForFamily
cp EXEGGUTOR
jp z, .Teleport
cp SCYTHER
jp z, .SwordsDanceAndFocusEnergy
cp KRABBY
jr z, .CallForFamily
cp VAPOREON1
jp z, .SwordsDanceAndFocusEnergy
cp ELECTRODE2
jp z, .ChainLightning
cp MAROWAK1
jr z, .CallForFriend
cp MEW3
jp z, .DevolutionBeam
cp JIGGLYPUFF2
jp z, .FriendshipSong
cp PORYGON
jp z, .Conversion
cp MEWTWO3
jp z, .EnergyAbsorption
cp MEWTWO2
jp z, .EnergyAbsorption
cp NINETALES2
jp z, .MixUp
cp ZAPDOS3
jp z, .BigThunder
cp KANGASKHAN
jp z, .Fetch
cp DUGTRIO
jp z, .Earthquake
cp ELECTRODE1
jp z, .EnergySpike
cp GOLDUCK
jp z, .HyperBeam
cp DRAGONAIR
jp z, .HyperBeam
; return zero score.
.zero_score
xor a
ret
; if any of card ID in a is found in deck,
; return a score of $80 + slots available in bench.
.CallForFamily: ; 16e3e (5:6e3e)
ld a, CARD_LOCATION_DECK
call CheckIfAnyCardIDinLocation
jr nc, .zero_score
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetTurnDuelistVariable
cp MAX_BENCH_POKEMON
jr nc, .zero_score
ld b, a
ld a, MAX_BENCH_POKEMON
sub b
add $80
ret
; if any of NidoranM or NidoranF is found in deck,
; return a score of $80 + slots available in bench.
.NidoranFCallForFamily: ; 16e55 (5:6e55)
ld e, NIDORANM
ld a, CARD_LOCATION_DECK
call CheckIfAnyCardIDinLocation
jr c, .found_nidoran
ld e, NIDORANF
ld a, CARD_LOCATION_DECK
call CheckIfAnyCardIDinLocation
jr nc, .zero_score
.found_nidoran
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetTurnDuelistVariable
cp MAX_PLAY_AREA_POKEMON
jr nc, .zero_score
ld b, a
ld a, MAX_PLAY_AREA_POKEMON
sub b
add $80
ret
; checks for certain card IDs of Fighting color in deck.
; if any of them are found, return a score of
; $80 + slots available in bench.
.CallForFriend: ; 16e77 (5:6e77)
ld e, GEODUDE
ld a, CARD_LOCATION_DECK
call CheckIfAnyCardIDinLocation
jr c, .found_fighting_card
ld e, ONIX
ld a, CARD_LOCATION_DECK
call CheckIfAnyCardIDinLocation
jr c, .found_fighting_card
ld e, CUBONE
ld a, CARD_LOCATION_DECK
call CheckIfAnyCardIDinLocation
jr c, .found_fighting_card
ld e, RHYHORN
ld a, CARD_LOCATION_DECK
call CheckIfAnyCardIDinLocation
jr c, .found_fighting_card
jr .zero_score
.found_fighting_card
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetTurnDuelistVariable
cp MAX_BENCH_POKEMON
jr nc, .zero_score
ld b, a
ld a, MAX_BENCH_POKEMON
sub b
add $80
ret
; if any basic cards are found in deck,
; return a score of $80 + slots available in bench.
.FriendshipSong: ; 16ead (5:6ead)
call CheckIfAnyBasicPokemonInDeck
jr nc, .zero_score
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetTurnDuelistVariable
cp MAX_PLAY_AREA_POKEMON
jr nc, .zero_score
ld b, a
ld a, MAX_PLAY_AREA_POKEMON
sub b
add $80
ret
; if AI decides to retreat, return a score of $80 + 10.
.Teleport: ; 16ec2 (5:6ec2)
call AIDecideWhetherToRetreat
jp nc, .zero_score
ld a, $8a
ret
; tests for the following conditions:
; - player is under No Damage substatus;
; - second attack is unusable;
; - second attack deals no damage;
; if any are true, returns score of $80 + 5.
.SwordsDanceAndFocusEnergy: ; 16ecb (5:6ecb)
ld a, [wAICannotDamage]
or a
jr nz, .swords_dance_focus_energy_success
ld a, SECOND_ATTACK
ld [wSelectedAttack], a
call CheckIfSelectedAttackIsUnusable
jr c, .swords_dance_focus_energy_success
ld a, SECOND_ATTACK
call EstimateDamage_VersusDefendingCard
ld a, [wDamage]
or a
jp nz, .zero_score
.swords_dance_focus_energy_success
ld a, $85
ret
; checks player's active card color, then
; loops through bench looking for a Pokémon
; with that same color.
; if none are found, returns score of $80 + 2.
.ChainLightning: ; 16eea (5:6eea)
call SwapTurn
call GetArenaCardColor
call SwapTurn
ld b, a
ld a, DUELVARS_BENCH
call GetTurnDuelistVariable
.loop_chain_lightning_bench
ld a, [hli]
cp $ff
jr z, .chain_lightning_success
push bc
call GetCardIDFromDeckIndex
call GetCardType
pop bc
cp b
jr nz, .loop_chain_lightning_bench
jp .zero_score
.chain_lightning_success
ld a, $82
ret
.DevolutionBeam: ; 16f0f (5:6f0f)
call LookForCardThatIsKnockedOutOnDevolution
jp nc, .zero_score
ld a, $85
ret
; first checks if card is confused, and if so return 0.
; then checks number of Pokémon in bench that are viable to use:
; - if that number is < 2 and this attack is Conversion 1 OR
; - if that number is >= 2 and this attack is Conversion 2
; then return score of $80 + 2.
; otherwise return score of $80 + 1.
.Conversion: ; 16f18 (5:6f18)
ld a, DUELVARS_ARENA_CARD_STATUS
call GetTurnDuelistVariable
and CNF_SLP_PRZ
cp CONFUSED
jp z, .zero_score
ld a, [wSelectedAttack]
or a
jr nz, .conversion_2
; conversion 1
call CountNumberOfSetUpBenchPokemon
cp 2
jr c, .low_conversion_score
ld a, $82
ret
.conversion_2
call CountNumberOfSetUpBenchPokemon
cp 2
jr nc, .low_conversion_score
ld a, $82
ret
.low_conversion_score
ld a, $81
ret
; if any Psychic Energy is found in the Discard Pile,
; return a score of $80 + 2.
.EnergyAbsorption: ; 16f41 (5:6f41)
ld e, PSYCHIC_ENERGY
ld a, CARD_LOCATION_DISCARD_PILE
call CheckIfAnyCardIDinLocation
jp nc, .zero_score
ld a, $82
ret
; if player has cards in hand, AI calls Random:
; - 1/3 chance to encourage attack regardless;
; - 1/3 chance to dismiss attack regardless;
; - 1/3 change to make some checks to player's hand.
; AI tallies number of basic cards in hand, and if this
; number is >= 2, encourage attack.
; otherwise, if it finds an evolution card in hand that
; can evolve a card in player's deck, encourage.
; if encouraged, returns a score of $80 + 3.
.MixUp: ; 16f4e (5:6f4e)
ld a, DUELVARS_NUMBER_OF_CARDS_IN_HAND
call GetNonTurnDuelistVariable
or a
ret z
ld a, 3
call Random
or a
jr z, .encourage_mix_up
dec a
ret z
call SwapTurn
call CreateHandCardList
call SwapTurn
or a
ret z ; return if no hand cards (again)
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetNonTurnDuelistVariable
cp 3
jr nc, .mix_up_check_play_area
ld hl, wDuelTempList
ld b, 0
.loop_mix_up_hand
ld a, [hli]
cp $ff
jr z, .tally_basic_cards
push bc
call SwapTurn
call LoadCardDataToBuffer2_FromDeckIndex
call SwapTurn
pop bc
ld a, [wLoadedCard2Type]
cp TYPE_ENERGY
jr nc, .loop_mix_up_hand
ld a, [wLoadedCard2Stage]
or a
jr nz, .loop_mix_up_hand
; is a basic Pokémon card
inc b
jr .loop_mix_up_hand
.tally_basic_cards
ld a, b
cp 2
jr nc, .encourage_mix_up
; less than 2 basic cards in hand
.mix_up_check_play_area
ld a, DUELVARS_ARENA_CARD
call GetNonTurnDuelistVariable
.loop_mix_up_play_area
ld a, [hli]
cp $ff
jp z, .zero_score
push hl
call SwapTurn
call CheckForEvolutionInList
call SwapTurn
pop hl
jr nc, .loop_mix_up_play_area
.encourage_mix_up
ld a, $83
ret
; return score of $80 + 3.
.BigThunder: ; 16fb8 (5:6fb8)
ld a, $83
ret
; dismiss attack if cards in deck <= 20.
; otherwise return a score of $80 + 0.
.Fetch: ; 16fbb (5:6fbb)
ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
call GetTurnDuelistVariable
cp 41
jp nc, .zero_score
ld a, $80
ret
; dismiss attack if number of own benched cards which would
; be KOd is greater than or equal to the number
; of prize cards left for player.
.Earthquake: ; 16fc8 (5:6fc8)
ld a, DUELVARS_BENCH
call GetTurnDuelistVariable
lb de, 0, 0
.loop_earthquake
inc e
ld a, [hli]
cp $ff
jr z, .count_prizes
ld a, e
add DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
cp 20
jr nc, .loop_earthquake
inc d
jr .loop_earthquake
.count_prizes
push de
call CountPrizes
pop de
cp d
jp c, .zero_score
jp z, .zero_score
ld a, $80
ret
; if there's any lightning energy cards in deck,
; return a score of $80 + 3.
.EnergySpike: ; 16ff2 (5:6ff2)
ld a, CARD_LOCATION_DECK
ld e, LIGHTNING_ENERGY
call CheckIfAnyCardIDinLocation
jp nc, .zero_score
call AIProcessButDontPlayEnergy_SkipEvolution
jp nc, .zero_score
ld a, $83
ret
; only incentivize attack if player's active card,
; has any energy cards attached, and if so,
; return a score of $80 + 3.
.HyperBeam: ; 17005 (5:7005)
call SwapTurn
ld e, PLAY_AREA_ARENA
call CountNumberOfEnergyCardsAttached
call SwapTurn
or a
jr z, .hyper_beam_neutral
ld a, $83
ret
.hyper_beam_neutral
ld a, $80
ret
; called when second attack is determined by AI to have
; more AI score than the first attack, so that it checks
; whether the first attack is a better alternative.
CheckWhetherToSwitchToFirstAttack: ; 17019 (5:7019)
; this checks whether the first attack is also viable
; (has more than minimum score to be used)
ld a, [wFirstAttackAIScore]
cp $50
jr c, .keep_second_attack
; first attack has more than minimum score to be used.
; check if second attack can KO.
; in case it can't, the AI keeps it as the attack to be used.
; (possibly due to the assumption that if the
; second attack cannot KO, the first attack can't KO as well.)
xor a
ldh [hTempPlayAreaLocation_ff9d], a
call EstimateDamage_VersusDefendingCard
ld a, DUELVARS_ARENA_CARD_HP
call GetNonTurnDuelistVariable
ld hl, wDamage
sub [hl]
jr z, .check_flag
jr nc, .keep_second_attack
; second attack can ko, check its flag.
; in case its effect is to heal user or nullify/weaken damage
; next turn, keep second attack as the option.
; otherwise switch to the first attack.
.check_flag
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
ld d, a
ld e, SECOND_ATTACK
call CopyAttackDataAndDamage_FromDeckIndex
ld a, ATTACK_FLAG2_ADDRESS | HEAL_USER_F
call CheckLoadedAttackFlag
jr c, .keep_second_attack
ld a, ATTACK_FLAG2_ADDRESS | NULLIFY_OR_WEAKEN_ATTACK_F
call CheckLoadedAttackFlag
jr c, .keep_second_attack
; switch to first attack
xor a
ld [wSelectedAttack], a
ret
.keep_second_attack
ld a, $01
ld [wSelectedAttack], a
ret
; returns carry if there are
; any basic Pokémon cards in deck.
CheckIfAnyBasicPokemonInDeck: ; 17057 (5:7057)
ld e, 0
.loop
ld a, DUELVARS_CARD_LOCATIONS
add e
call GetTurnDuelistVariable
cp CARD_LOCATION_DECK
jr nz, .next
push de
ld a, e
call LoadCardDataToBuffer2_FromDeckIndex
pop de
ld a, [wLoadedCard2Type]
cp TYPE_ENERGY
jr nc, .next
ld a, [wLoadedCard2Stage]
or a
jr z, .set_carry
.next
inc e
ld a, DECK_SIZE
cp e
jr nz, .loop
or a
ret
.set_carry
scf
ret
|