summaryrefslogtreecommitdiff
path: root/src/engine/ai/special_attacks.asm
blob: 770324e411a8e3f16485642aad42bf5523b4e332 (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
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