-
Notifications
You must be signed in to change notification settings - Fork 91
/
attacks.asm
720 lines (658 loc) · 16.2 KB
/
attacks.asm
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
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
; have AI choose an attack to use, but do not execute it.
; return carry if an attack is chosen.
AIProcessButDontUseAttack:
ld a, $01
ld [wAIExecuteProcessedAttack], a
; backup wPlayAreaAIScore in wTempPlayAreaAIScore.
ld de, wTempPlayAreaAIScore
ld hl, wPlayAreaAIScore
ld b, MAX_PLAY_AREA_POKEMON
.loop
ld a, [hli]
ld [de], a
inc de
dec b
jr nz, .loop
; copies wAIScore to wTempAIScore
ld a, [wAIScore]
ld [de], a
jr AIProcessAttacks
; copies wTempPlayAreaAIScore to wPlayAreaAIScore
; and loads wAIScore with value in wTempAIScore.
; identical to RetrievePlayAreaAIScoreFromBackup1.
RetrievePlayAreaAIScoreFromBackup2:
push af
ld de, wPlayAreaAIScore
ld hl, wTempPlayAreaAIScore
ld b, MAX_PLAY_AREA_POKEMON
.loop
ld a, [hli]
ld [de], a
inc de
dec b
jr nz, .loop
ld a, [hl]
ld [wAIScore], a
pop af
ret
; have AI choose and execute an attack.
; return carry if an attack was chosen and attempted.
AIProcessAndTryToUseAttack:
xor a
ld [wAIExecuteProcessedAttack], a
; fallthrough
; checks which of the Active card's attacks for AI to use.
; If any of the attacks has enough AI score to be used,
; AI will use it if wAIExecuteProcessedAttack is 0.
; in either case, return carry if an attack is chosen to be used.
AIProcessAttacks:
; if AI used Pluspower, load its attack index
ld a, [wPreviousAIFlags]
and AI_FLAG_USED_PLUSPOWER
jr z, .no_pluspower
ld a, [wAIPluspowerAttack]
ld [wSelectedAttack], a
jr .attack_chosen
.no_pluspower
; if Player is running MewtwoLv53 mill deck,
; skip attack if Barrier counter is 0.
ld a, [wAIBarrierFlagCounter]
cp AI_MEWTWO_MILL + 0
jp z, .dont_attack
; determine AI score of both attacks.
xor a ; FIRST_ATTACK_OR_PKMN_POWER
call GetAIScoreOfAttack
ld a, [wAIScore]
ld [wFirstAttackAIScore], a
ld a, SECOND_ATTACK
call GetAIScoreOfAttack
; compare both attack scores
ld c, SECOND_ATTACK
ld a, [wFirstAttackAIScore]
ld b, a
ld a, [wAIScore]
cp b
jr nc, .check_score
; first attack has higher score
dec c
ld a, b
; c holds the attack index chosen by AI,
; and a holds its AI score.
; first check if chosen attack has at least minimum score.
; then check if first attack is better than second attack
; in case the second one was chosen.
.check_score
cp $50 ; minimum score to use attack
jr c, .dont_attack
; enough score, proceed
ld a, c
ld [wSelectedAttack], a
or a
jr z, .attack_chosen
call CheckWhetherToSwitchToFirstAttack
.attack_chosen
; check whether to execute the attack chosen
ld a, [wAIExecuteProcessedAttack]
or a
jr z, .execute
; set carry and reset Play Area AI score
; to the previous values.
scf
jp RetrievePlayAreaAIScoreFromBackup2
.execute
ld a, AI_TRAINER_CARD_PHASE_14
call AIProcessHandTrainerCards
; load this attack's damage output against
; the current Defending Pokemon.
xor a ; PLAY_AREA_ARENA
ldh [hTempPlayAreaLocation_ff9d], a
ld a, [wSelectedAttack]
call EstimateDamage_VersusDefendingCard
ld a, [wDamage]
or a
jr z, .check_damage_bench
; if damage is not 0, fallthrough
.can_damage
xor a
ld [wAIRetreatScore], a
jr .use_attack
.check_damage_bench
; check if it can otherwise damage player's bench
ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F
call CheckLoadedAttackFlag
jr c, .can_damage
; cannot damage either Defending Pokemon or Bench
ld hl, wAIRetreatScore
inc [hl]
; return carry if attack is chosen
; and AI tries to use it.
.use_attack
ld a, TRUE
ld [wAITriedAttack], a
call AITryUseAttack
scf
ret
.dont_attack
ld a, [wAIExecuteProcessedAttack]
or a
jr z, .failed_to_use
; reset Play Area AI score
; to the previous values.
jp RetrievePlayAreaAIScoreFromBackup2
; return no carry if no viable attack.
.failed_to_use
ld hl, wAIRetreatScore
inc [hl]
or a
ret
; determines the AI score of attack index in a
; of card in Play Area location hTempPlayAreaLocation_ff9d.
GetAIScoreOfAttack:
; initialize AI score.
ld [wSelectedAttack], a
ld a, $50
ld [wAIScore], a
xor a ; PLAY_AREA_ARENA
ldh [hTempPlayAreaLocation_ff9d], a
call CheckIfSelectedAttackIsUnusable
jr nc, .usable
; return zero AI score.
.unusable
xor a
ld [wAIScore], a
jp .done
; load arena card IDs
.usable
xor a
ld [wAICannotDamage], a
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
ld a, e
ld [wTempTurnDuelistCardID], a
call SwapTurn
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
ld a, e
ld [wTempNonTurnDuelistCardID], a
; handle the case where the player has No Damage substatus.
; in the case the player does, check if this attack
; has a residual effect, or if it can damage the opposing bench.
; If none of those are true, render the attack unusable.
; also if it's a PKMN power, consider it unusable as well.
bank1call HandleNoDamageOrEffectSubstatus
call SwapTurn
jr nc, .check_if_can_ko
; player is under No Damage substatus
ld a, $01
ld [wAICannotDamage], a
ld a, [wSelectedAttack]
call EstimateDamage_VersusDefendingCard
ld a, [wLoadedAttackCategory]
cp POKEMON_POWER
jr z, .unusable
and RESIDUAL
jr nz, .check_if_can_ko
ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F
call CheckLoadedAttackFlag
jr nc, .unusable
; calculate damage to player to check if attack can KO.
; encourage attack if it's able to KO.
.check_if_can_ko
ld a, [wSelectedAttack]
call EstimateDamage_VersusDefendingCard
ld a, DUELVARS_ARENA_CARD_HP
call GetNonTurnDuelistVariable
ld hl, wDamage
sub [hl]
jr c, .can_ko
jr z, .can_ko
jr .check_damage
.can_ko
ld a, 20
call AddToAIScore
; raise AI score by the number of damage counters that this attack deals.
; if no damage is dealt, subtract AI score. in case wDamage is zero
; but wMaxDamage is not, then encourage attack afterwards.
; otherwise, if wMaxDamage is also zero, check for damage against
; player's bench, and encourage attack in case there is.
.check_damage
xor a
ld [wAIAttackIsNonDamaging], a
ld a, [wDamage]
ld [wTempAI], a
or a
jr z, .no_damage
call ConvertHPToDamageCounters_Bank5
call AddToAIScore
jr .check_recoil
.no_damage
ld a, $01
ld [wAIAttackIsNonDamaging], a
call SubFromAIScore
ld a, [wAIMaxDamage]
or a
jr z, .no_max_damage
ld a, 2
call AddToAIScore
xor a
ld [wAIAttackIsNonDamaging], a
.no_max_damage
ld a, ATTACK_FLAG1_ADDRESS | DAMAGE_TO_OPPONENT_BENCH_F
call CheckLoadedAttackFlag
jr nc, .check_recoil
ld a, 2
call AddToAIScore
; handle recoil attacks (low and high recoil).
.check_recoil
ld a, ATTACK_FLAG1_ADDRESS | LOW_RECOIL_F
call CheckLoadedAttackFlag
jr c, .is_recoil
ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F
call CheckLoadedAttackFlag
jp nc, .check_defending_can_ko
.is_recoil
; sub from AI score number of damage counters
; that attack deals to itself.
ld a, [wLoadedAttackEffectParam]
or a
jp z, .check_defending_can_ko
ld [wDamage], a
call ApplyDamageModifiers_DamageToSelf
ld a, e
call ConvertHPToDamageCounters_Bank5
call SubFromAIScore
push de
ld a, ATTACK_FLAG1_ADDRESS | HIGH_RECOIL_F
call CheckLoadedAttackFlag
pop de
jr c, .high_recoil
; if LOW_RECOIL KOs self, decrease AI score
ld a, DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
cp e
jr c, .kos_self
jp nz, .check_defending_can_ko
.kos_self
ld a, 10
call SubFromAIScore
.high_recoil
; dismiss this attack if no benched Pokémon
ld a, DUELVARS_NUMBER_OF_POKEMON_IN_PLAY_AREA
call GetTurnDuelistVariable
cp 2
jr c, .dismiss_high_recoil_atk
; has benched Pokémon
; here the AI handles high recoil attacks differently
; depending on what deck it's playing.
ld a, [wOpponentDeckID]
cp ROCK_CRUSHER_DECK_ID
jr z, .rock_crusher_deck
cp ZAPPING_SELFDESTRUCT_DECK_ID
jr z, .zapping_selfdestruct_deck
cp BOOM_BOOM_SELFDESTRUCT_DECK_ID
jr z, .encourage_high_recoil_atk
; Boom Boom Selfdestruct deck always encourages
cp POWER_GENERATOR_DECK_ID
jr nz, .high_recoil_generic_checks
; Power Generator deck always dismisses
.dismiss_high_recoil_atk
xor a
ld [wAIScore], a
jp .done
.encourage_high_recoil_atk
ld a, 20
call AddToAIScore
jp .done
; Zapping Selfdestruct deck only uses this attack
; if number of cards in deck >= 30 and
; HP of active card is < half max HP.
.zapping_selfdestruct_deck
ld a, DUELVARS_NUMBER_OF_CARDS_NOT_IN_DECK
call GetTurnDuelistVariable
cp 31
jr nc, .high_recoil_generic_checks
ld e, PLAY_AREA_ARENA
call GetCardDamageAndMaxHP
sla a
cp c
jr c, .high_recoil_generic_checks
ld b, 0
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
ld a, e
cp MAGNEMITE_LV13
jr z, .magnemite1
ld b, 10 ; bench damage
.magnemite1
ld a, 10
add b
ld b, a ; 20 bench damage if not MagnemiteLv13
; if this attack causes player to win the duel by
; knocking out own Pokémon, dismiss attack.
ld a, 1 ; count active Pokémon as KO'd
call .check_if_kos_bench
jr c, .dismiss_high_recoil_atk
jr .encourage_high_recoil_atk
; Rock Crusher Deck only uses this attack if
; prize count is below 4 and attack wins (or potentially draws) the duel,
; (i.e. at least gets KOs equal to prize cards left).
.rock_crusher_deck
call CountPrizes
cp 4
jr nc, .dismiss_high_recoil_atk
; prize count < 4
ld b, 20 ; damage dealt to bench
call SwapTurn
xor a
call .check_if_kos_bench
call SwapTurn
jr c, .encourage_high_recoil_atk
; generic checks for all other deck IDs.
; encourage attack if it wins (or potentially draws) the duel,
; (i.e. at least gets KOs equal to prize cards left).
; dismiss it if it causes the player to win.
.high_recoil_generic_checks
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
call GetCardIDFromDeckIndex
ld a, e
cp CHANSEY
jr z, .chansey
cp MAGNEMITE_LV13
jr z, .magnemite1_or_weezing
cp WEEZING
jr z, .magnemite1_or_weezing
ld b, 20 ; bench damage
jr .check_bench_kos
.magnemite1_or_weezing
ld b, 10 ; bench damage
jr .check_bench_kos
.chansey
ld b, 0 ; no bench damage
.check_bench_kos
push bc
call SwapTurn
xor a
call .check_if_kos_bench
call SwapTurn
pop bc
jr c, .wins_the_duel
push de
ld a, 1
call .check_if_kos_bench
pop bc
jr nc, .count_own_ko_bench
; attack causes player to draw all prize cards
xor a
ld [wAIScore], a
jp .done
; attack causes CPU to draw all prize cards
.wins_the_duel
ld a, 20
call AddToAIScore
jp .done
; subtract from AI score number of own benched Pokémon KO'd
.count_own_ko_bench
push bc
ld a, d
or a
jr z, .count_player_ko_bench
dec a
call SubFromAIScore
; add to AI score number of player benched Pokémon KO'd
.count_player_ko_bench
pop bc
ld a, b
call AddToAIScore
jr .check_defending_can_ko
; local function that gets called to determine damage to
; benched Pokémon caused by a HIGH_RECOIL attack.
; return carry if using attack causes number of benched Pokémon KOs
; equal to or larger than remaining prize cards.
; this function is independent on duelist turn, so whatever
; turn it is when this is called, it's that duelist's
; bench/prize cards that get checked.
; input:
; a = initial number of KO's beside benched Pokémon,
; so that if the active Pokémon is KO'd by the attack,
; this counts towards the prize cards collected
; b = damage dealt to bench Pokémon
.check_if_kos_bench
ld d, a
ld a, DUELVARS_BENCH
call GetTurnDuelistVariable
ld e, PLAY_AREA_ARENA
.loop
inc e
ld a, [hli]
cp $ff
jr z, .exit_loop
ld a, e
add DUELVARS_ARENA_CARD_HP
push hl
call GetTurnDuelistVariable
pop hl
cp b
jr z, .increase_count
jr nc, .loop
.increase_count
; increase d if damage dealt KOs
inc d
jr .loop
.exit_loop
push de
call SwapTurn
call CountPrizes
call SwapTurn
pop de
cp d
jp c, .set_carry
jp z, .set_carry
or a
ret
.set_carry
scf
ret
; if defending card can KO, encourage attack
; unless attack is non-damaging.
.check_defending_can_ko
ld a, [wSelectedAttack]
push af
call CheckIfDefendingPokemonCanKnockOut
pop bc
ld a, b
ld [wSelectedAttack], a
jr nc, .check_discard
ld a, 5
call AddToAIScore
ld a, [wAIAttackIsNonDamaging]
or a
jr z, .check_discard
ld a, 5
call SubFromAIScore
; subtract from AI score if this attack requires
; discarding any energy cards.
.check_discard
ld a, [wSelectedAttack]
ld e, a
ld a, DUELVARS_ARENA_CARD
call GetTurnDuelistVariable
ld d, a
call CopyAttackDataAndDamage_FromDeckIndex
ld a, ATTACK_FLAG2_ADDRESS | DISCARD_ENERGY_F
call CheckLoadedAttackFlag
jr nc, .asm_16ca6
ld a, 1
call SubFromAIScore
ld a, [wLoadedAttackEffectParam]
call SubFromAIScore
.asm_16ca6
ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_6_F
call CheckLoadedAttackFlag
jr nc, .check_nullify_flag
ld a, [wLoadedAttackEffectParam]
call AddToAIScore
; encourage attack if it has a nullify or weaken attack effect.
.check_nullify_flag
ld a, ATTACK_FLAG2_ADDRESS | NULLIFY_OR_WEAKEN_ATTACK_F
call CheckLoadedAttackFlag
jr nc, .check_draw_flag
ld a, 1
call AddToAIScore
; encourage attack if it has an effect to draw a card.
.check_draw_flag
ld a, ATTACK_FLAG1_ADDRESS | DRAW_CARD_F
call CheckLoadedAttackFlag
jr nc, .check_heal_flag
ld a, 1
call AddToAIScore
.check_heal_flag
ld a, ATTACK_FLAG2_ADDRESS | HEAL_USER_F
call CheckLoadedAttackFlag
jr nc, .check_status_effect
ld a, [wLoadedAttackEffectParam]
cp 1
jr z, .tally_heal_score
ld a, [wTempAI]
call ConvertHPToDamageCounters_Bank5
ld b, a
ld a, [wLoadedAttackEffectParam]
cp 3
jr z, .asm_16cec
srl b
jr nc, .asm_16cec
inc b
.asm_16cec
ld a, DUELVARS_ARENA_CARD_HP
call GetTurnDuelistVariable
call ConvertHPToDamageCounters_Bank5
cp b
jr c, .tally_heal_score
ld a, b
.tally_heal_score
push af
ld e, PLAY_AREA_ARENA
call GetCardDamageAndMaxHP
call ConvertHPToDamageCounters_Bank5
pop bc
cp b ; wLoadedAttackEffectParam
jr c, .add_heal_score
ld a, b
.add_heal_score
call AddToAIScore
.check_status_effect
ld a, DUELVARS_ARENA_CARD
call GetNonTurnDuelistVariable
call SwapTurn
call GetCardIDFromDeckIndex
call SwapTurn
ld a, e
; skip if player has Snorlax
cp SNORLAX
jp z, .handle_special_atks
ld a, DUELVARS_ARENA_CARD_STATUS
call GetNonTurnDuelistVariable
ld [wTempAI], a
; encourage a poison inflicting attack if opposing Pokémon
; isn't (doubly) poisoned already.
; if opposing Pokémon is only poisoned and not double poisoned,
; and this attack has FLAG_2_BIT_6 set, discourage it
; (possibly to make Nidoking's Toxic attack less likely to be chosen
; if the other Pokémon is poisoned.)
ld a, ATTACK_FLAG1_ADDRESS | INFLICT_POISON_F
call CheckLoadedAttackFlag
jr nc, .check_sleep
ld a, [wTempAI]
and DOUBLE_POISONED
jr z, .add_poison_score
and $40 ; only double poisoned?
jr z, .check_sleep
ld a, ATTACK_FLAG2_ADDRESS | FLAG_2_BIT_6_F
call CheckLoadedAttackFlag
jr nc, .check_sleep
ld a, 2
call SubFromAIScore
jr .check_sleep
.add_poison_score
ld a, 2
call AddToAIScore
; encourage sleep-inducing attack if other Pokémon isn't asleep.
.check_sleep
ld a, ATTACK_FLAG1_ADDRESS | INFLICT_SLEEP_F
call CheckLoadedAttackFlag
jr nc, .check_paralysis
ld a, [wTempAI]
and CNF_SLP_PRZ
cp ASLEEP
jr z, .check_paralysis
ld a, 1
call AddToAIScore
; encourage paralysis-inducing attack if other Pokémon isn't asleep.
; otherwise, if other Pokémon is asleep, discourage attack.
.check_paralysis
ld a, ATTACK_FLAG1_ADDRESS | INFLICT_PARALYSIS_F
call CheckLoadedAttackFlag
jr nc, .check_confusion
ld a, [wTempAI]
and CNF_SLP_PRZ
cp ASLEEP
jr z, .sub_prz_score
ld a, 1
call AddToAIScore
jr .check_confusion
.sub_prz_score
ld a, 1
call SubFromAIScore
; encourage confuse-inducing attack if other Pokémon isn't asleep
; or confused already.
; otherwise, if other Pokémon is asleep or confused,
; discourage attack instead.
.check_confusion
ld a, ATTACK_FLAG1_ADDRESS | INFLICT_CONFUSION_F
call CheckLoadedAttackFlag
jr nc, .check_if_confused
ld a, [wTempAI]
and CNF_SLP_PRZ
cp ASLEEP
jr z, .sub_cnf_score
ld a, [wTempAI]
and CNF_SLP_PRZ
cp CONFUSED
jr z, .check_if_confused
ld a, 1
call AddToAIScore
jr .check_if_confused
.sub_cnf_score
ld a, 1
call SubFromAIScore
; if this Pokémon is confused, subtract from score.
.check_if_confused
ld a, DUELVARS_ARENA_CARD_STATUS
call GetTurnDuelistVariable
and CNF_SLP_PRZ
cp CONFUSED
jr nz, .handle_special_atks
ld a, 1
call SubFromAIScore
; SPECIAL_AI_HANDLING marks attacks that the AI handles individually.
; each attack has its own checks and modifies AI score accordingly.
.handle_special_atks
ld a, ATTACK_FLAG3_ADDRESS | SPECIAL_AI_HANDLING_F
call CheckLoadedAttackFlag
jr nc, .done
call HandleSpecialAIAttacks
cp $80
jr c, .negative_score
sub $80
call AddToAIScore
jr .done
.negative_score
ld b, a
ld a, $80
sub b
call SubFromAIScore
.done
ret