Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
pret
GitHub Repository: pret/pokered
Path: blob/master/engine/battle/trainer_ai.asm
1271 views
1
; creates a set of moves that may be used and returns its address in hl
2
; unused slots are filled with 0, all used slots may be chosen with equal probability
3
AIEnemyTrainerChooseMoves:
4
ld a, $a
5
ld hl, wBuffer ; init temporary move selection array. Only the moves with the lowest numbers are chosen in the end
6
ld [hli], a ; move 1
7
ld [hli], a ; move 2
8
ld [hli], a ; move 3
9
ld [hl], a ; move 4
10
ld a, [wEnemyDisabledMove] ; forbid disabled move (if any)
11
swap a
12
and $f
13
jr z, .noMoveDisabled
14
ld hl, wBuffer
15
dec a
16
ld c, a
17
ld b, $0
18
add hl, bc ; advance pointer to forbidden move
19
ld [hl], $50 ; forbid (highly discourage) disabled move
20
.noMoveDisabled
21
ld hl, TrainerClassMoveChoiceModifications
22
ld a, [wTrainerClass]
23
ld b, a
24
.loopTrainerClasses
25
dec b
26
jr z, .readTrainerClassData
27
.loopTrainerClassData
28
ld a, [hli]
29
and a
30
jr nz, .loopTrainerClassData
31
jr .loopTrainerClasses
32
.readTrainerClassData
33
ld a, [hl]
34
and a
35
jp z, .useOriginalMoveSet
36
push hl
37
.nextMoveChoiceModification
38
pop hl
39
ld a, [hli]
40
and a
41
jr z, .loopFindMinimumEntries
42
push hl
43
ld hl, AIMoveChoiceModificationFunctionPointers
44
dec a
45
add a
46
ld c, a
47
ld b, 0
48
add hl, bc ; skip to pointer
49
ld a, [hli] ; read pointer into hl
50
ld h, [hl]
51
ld l, a
52
ld de, .nextMoveChoiceModification ; set return address
53
push de
54
jp hl ; execute modification function
55
.loopFindMinimumEntries ; all entries will be decremented sequentially until one of them is zero
56
ld hl, wBuffer ; temp move selection array
57
ld de, wEnemyMonMoves ; enemy moves
58
ld c, NUM_MOVES
59
.loopDecrementEntries
60
ld a, [de]
61
inc de
62
and a
63
jr z, .loopFindMinimumEntries
64
dec [hl]
65
jr z, .minimumEntriesFound
66
inc hl
67
dec c
68
jr z, .loopFindMinimumEntries
69
jr .loopDecrementEntries
70
.minimumEntriesFound
71
ld a, c
72
.loopUndoPartialIteration ; undo last (partial) loop iteration
73
inc [hl]
74
dec hl
75
inc a
76
cp NUM_MOVES + 1
77
jr nz, .loopUndoPartialIteration
78
ld hl, wBuffer ; temp move selection array
79
ld de, wEnemyMonMoves ; enemy moves
80
ld c, NUM_MOVES
81
.filterMinimalEntries ; all minimal entries now have value 1. All other slots will be disabled (move set to 0)
82
ld a, [de]
83
and a
84
jr nz, .moveExisting
85
ld [hl], a
86
.moveExisting
87
ld a, [hl]
88
dec a
89
jr z, .slotWithMinimalValue
90
xor a
91
ld [hli], a ; disable move slot
92
jr .next
93
.slotWithMinimalValue
94
ld a, [de]
95
ld [hli], a ; enable move slot
96
.next
97
inc de
98
dec c
99
jr nz, .filterMinimalEntries
100
ld hl, wBuffer ; use created temporary array as move set
101
ret
102
.useOriginalMoveSet
103
ld hl, wEnemyMonMoves ; use original move set
104
ret
105
106
AIMoveChoiceModificationFunctionPointers:
107
dw AIMoveChoiceModification1
108
dw AIMoveChoiceModification2
109
dw AIMoveChoiceModification3
110
dw AIMoveChoiceModification4 ; unused, does nothing
111
112
; discourages moves that cause no damage but only a status ailment if player's mon already has one
113
AIMoveChoiceModification1:
114
ld a, [wBattleMonStatus]
115
and a
116
ret z ; return if no status ailment on player's mon
117
ld hl, wBuffer - 1 ; temp move selection array (-1 byte offset)
118
ld de, wEnemyMonMoves ; enemy moves
119
ld b, NUM_MOVES + 1
120
.nextMove
121
dec b
122
ret z ; processed all 4 moves
123
inc hl
124
ld a, [de]
125
and a
126
ret z ; no more moves in move set
127
inc de
128
call ReadMove
129
ld a, [wEnemyMovePower]
130
and a
131
jr nz, .nextMove
132
ld a, [wEnemyMoveEffect]
133
push hl
134
push de
135
push bc
136
ld hl, StatusAilmentMoveEffects
137
ld de, 1
138
call IsInArray
139
pop bc
140
pop de
141
pop hl
142
jr nc, .nextMove
143
ld a, [hl]
144
add $5 ; heavily discourage move
145
ld [hl], a
146
jr .nextMove
147
148
StatusAilmentMoveEffects:
149
db EFFECT_01 ; unused sleep effect
150
db SLEEP_EFFECT
151
db POISON_EFFECT
152
db PARALYZE_EFFECT
153
db -1 ; end
154
155
; slightly encourage moves with specific effects.
156
; in particular, stat-modifying moves and other move effects
157
; that fall in-between
158
AIMoveChoiceModification2:
159
ld a, [wAILayer2Encouragement]
160
cp $1
161
ret nz
162
ld hl, wBuffer - 1 ; temp move selection array (-1 byte offset)
163
ld de, wEnemyMonMoves ; enemy moves
164
ld b, NUM_MOVES + 1
165
.nextMove
166
dec b
167
ret z ; processed all 4 moves
168
inc hl
169
ld a, [de]
170
and a
171
ret z ; no more moves in move set
172
inc de
173
call ReadMove
174
ld a, [wEnemyMoveEffect]
175
cp ATTACK_UP1_EFFECT
176
jr c, .nextMove
177
cp BIDE_EFFECT
178
jr c, .preferMove
179
cp ATTACK_UP2_EFFECT
180
jr c, .nextMove
181
cp POISON_EFFECT
182
jr c, .preferMove
183
jr .nextMove
184
.preferMove
185
dec [hl] ; slightly encourage this move
186
jr .nextMove
187
188
; encourages moves that are effective against the player's mon (even if non-damaging).
189
; discourage damaging moves that are ineffective or not very effective against the player's mon,
190
; unless there's no damaging move that deals at least neutral damage
191
AIMoveChoiceModification3:
192
ld hl, wBuffer - 1 ; temp move selection array (-1 byte offset)
193
ld de, wEnemyMonMoves ; enemy moves
194
ld b, NUM_MOVES + 1
195
.nextMove
196
dec b
197
ret z ; processed all 4 moves
198
inc hl
199
ld a, [de]
200
and a
201
ret z ; no more moves in move set
202
inc de
203
call ReadMove
204
push hl
205
push bc
206
push de
207
callfar AIGetTypeEffectiveness
208
pop de
209
pop bc
210
pop hl
211
ld a, [wTypeEffectiveness]
212
cp $10
213
jr z, .nextMove
214
jr c, .notEffectiveMove
215
dec [hl] ; slightly encourage this move
216
jr .nextMove
217
.notEffectiveMove ; discourages non-effective moves if better moves are available
218
push hl
219
push de
220
push bc
221
ld a, [wEnemyMoveType]
222
ld d, a
223
ld hl, wEnemyMonMoves ; enemy moves
224
ld b, NUM_MOVES + 1
225
ld c, $0
226
.loopMoves
227
dec b
228
jr z, .done
229
ld a, [hli]
230
and a
231
jr z, .done
232
call ReadMove
233
ld a, [wEnemyMoveEffect]
234
cp SUPER_FANG_EFFECT
235
jr z, .betterMoveFound ; Super Fang is considered to be a better move
236
cp SPECIAL_DAMAGE_EFFECT
237
jr z, .betterMoveFound ; any special damage moves are considered to be better moves
238
cp FLY_EFFECT
239
jr z, .betterMoveFound ; Fly is considered to be a better move
240
ld a, [wEnemyMoveType]
241
cp d
242
jr z, .loopMoves
243
ld a, [wEnemyMovePower]
244
and a
245
jr nz, .betterMoveFound ; damaging moves of a different type are considered to be better moves
246
jr .loopMoves
247
.betterMoveFound
248
ld c, a
249
.done
250
ld a, c
251
pop bc
252
pop de
253
pop hl
254
and a
255
jr z, .nextMove
256
inc [hl] ; slightly discourage this move
257
jr .nextMove
258
AIMoveChoiceModification4:
259
ret
260
261
ReadMove:
262
push hl
263
push de
264
push bc
265
dec a
266
ld hl, Moves
267
ld bc, MOVE_LENGTH
268
call AddNTimes
269
ld de, wEnemyMoveNum
270
call CopyData
271
pop bc
272
pop de
273
pop hl
274
ret
275
276
INCLUDE "data/trainers/move_choices.asm"
277
278
INCLUDE "data/trainers/pic_pointers_money.asm"
279
280
INCLUDE "data/trainers/names.asm"
281
282
INCLUDE "engine/battle/misc.asm"
283
284
INCLUDE "engine/battle/read_trainer_party.asm"
285
286
INCLUDE "data/trainers/special_moves.asm"
287
288
INCLUDE "data/trainers/parties.asm"
289
290
TrainerAI:
291
and a
292
ld a, [wIsInBattle]
293
dec a
294
ret z ; if not a trainer, we're done here
295
ld a, [wLinkState]
296
cp LINK_STATE_BATTLING
297
ret z ; if in a link battle, we're done as well
298
ld a, [wTrainerClass] ; what trainer class is this?
299
dec a
300
ld c, a
301
ld b, 0
302
ld hl, TrainerAIPointers
303
add hl, bc
304
add hl, bc
305
add hl, bc
306
ld a, [wAICount]
307
and a
308
ret z ; if no AI uses left, we're done here
309
inc hl
310
inc a
311
jr nz, .getpointer
312
dec hl
313
ld a, [hli]
314
ld [wAICount], a
315
.getpointer
316
ld a, [hli]
317
ld h, [hl]
318
ld l, a
319
call Random
320
jp hl
321
322
INCLUDE "data/trainers/ai_pointers.asm"
323
324
JugglerAI:
325
cp 25 percent + 1
326
ret nc
327
jp AISwitchIfEnoughMons
328
329
BlackbeltAI:
330
cp 13 percent - 1
331
ret nc
332
jp AIUseXAttack
333
334
GiovanniAI:
335
cp 25 percent + 1
336
ret nc
337
jp AIUseGuardSpec
338
339
CooltrainerMAI:
340
cp 25 percent + 1
341
ret nc
342
jp AIUseXAttack
343
344
CooltrainerFAI:
345
; The intended 25% chance to consider switching will not apply.
346
; Uncomment the line below to fix this.
347
cp 25 percent + 1
348
; ret nc
349
ld a, 10
350
call AICheckIfHPBelowFraction
351
jp c, AIUseHyperPotion
352
ld a, 5
353
call AICheckIfHPBelowFraction
354
ret nc
355
jp AISwitchIfEnoughMons
356
357
BrockAI:
358
; if his active monster has a status condition, use a full heal
359
ld a, [wEnemyMonStatus]
360
and a
361
ret z
362
jp AIUseFullHeal
363
364
MistyAI:
365
cp 25 percent + 1
366
ret nc
367
jp AIUseXDefend
368
369
LtSurgeAI:
370
cp 25 percent + 1
371
ret nc
372
jp AIUseXSpeed
373
374
ErikaAI:
375
cp 50 percent + 1
376
ret nc
377
ld a, 10
378
call AICheckIfHPBelowFraction
379
ret nc
380
jp AIUseSuperPotion
381
382
KogaAI:
383
cp 25 percent + 1
384
ret nc
385
jp AIUseXAttack
386
387
BlaineAI:
388
cp 25 percent + 1
389
ret nc
390
jp AIUseSuperPotion
391
392
SabrinaAI:
393
cp 25 percent + 1
394
ret nc
395
ld a, 10
396
call AICheckIfHPBelowFraction
397
ret nc
398
jp AIUseHyperPotion
399
400
Rival2AI:
401
cp 13 percent - 1
402
ret nc
403
ld a, 5
404
call AICheckIfHPBelowFraction
405
ret nc
406
jp AIUsePotion
407
408
Rival3AI:
409
cp 13 percent - 1
410
ret nc
411
ld a, 5
412
call AICheckIfHPBelowFraction
413
ret nc
414
jp AIUseFullRestore
415
416
LoreleiAI:
417
cp 50 percent + 1
418
ret nc
419
ld a, 5
420
call AICheckIfHPBelowFraction
421
ret nc
422
jp AIUseSuperPotion
423
424
BrunoAI:
425
cp 25 percent + 1
426
ret nc
427
jp AIUseXDefend
428
429
AgathaAI:
430
cp 8 percent
431
jp c, AISwitchIfEnoughMons
432
cp 50 percent + 1
433
ret nc
434
ld a, 4
435
call AICheckIfHPBelowFraction
436
ret nc
437
jp AIUseSuperPotion
438
439
LanceAI:
440
cp 50 percent + 1
441
ret nc
442
ld a, 5
443
call AICheckIfHPBelowFraction
444
ret nc
445
jp AIUseHyperPotion
446
447
GenericAI:
448
and a ; clear carry
449
ret
450
451
; end of individual trainer AI routines
452
453
DecrementAICount:
454
ld hl, wAICount
455
dec [hl]
456
scf
457
ret
458
459
AIPlayRestoringSFX:
460
ld a, SFX_HEAL_AILMENT
461
jp PlaySoundWaitForCurrent
462
463
AIUseFullRestore:
464
call AICureStatus
465
ld a, FULL_RESTORE
466
ld [wAIItem], a
467
ld de, wHPBarOldHP
468
ld hl, wEnemyMonHP + 1
469
ld a, [hld]
470
ld [de], a
471
inc de
472
ld a, [hl]
473
ld [de], a
474
inc de
475
ld hl, wEnemyMonMaxHP + 1
476
ld a, [hld]
477
ld [de], a
478
inc de
479
ld [wHPBarMaxHP], a
480
ld [wEnemyMonHP + 1], a
481
ld a, [hl]
482
ld [de], a
483
ld [wHPBarMaxHP+1], a
484
ld [wEnemyMonHP], a
485
jr AIPrintItemUseAndUpdateHPBar
486
487
AIUsePotion:
488
; enemy trainer heals his monster with a potion
489
ld a, POTION
490
ld b, 20
491
jr AIRecoverHP
492
493
AIUseSuperPotion:
494
; enemy trainer heals his monster with a super potion
495
ld a, SUPER_POTION
496
ld b, 50
497
jr AIRecoverHP
498
499
AIUseHyperPotion:
500
; enemy trainer heals his monster with a hyper potion
501
ld a, HYPER_POTION
502
ld b, 200
503
; fallthrough
504
505
AIRecoverHP:
506
; heal b HP and print "trainer used $(a) on pokemon!"
507
ld [wAIItem], a
508
ld hl, wEnemyMonHP + 1
509
ld a, [hl]
510
ld [wHPBarOldHP], a
511
add b
512
ld [hld], a
513
ld [wHPBarNewHP], a
514
ld a, [hl]
515
ld [wHPBarOldHP+1], a
516
ld [wHPBarNewHP+1], a
517
jr nc, .next
518
inc a
519
ld [hl], a
520
ld [wHPBarNewHP+1], a
521
.next
522
inc hl
523
ld a, [hld]
524
ld b, a
525
ld de, wEnemyMonMaxHP + 1
526
ld a, [de]
527
dec de
528
ld [wHPBarMaxHP], a
529
sub b
530
ld a, [hli]
531
ld b, a
532
ld a, [de]
533
ld [wHPBarMaxHP+1], a
534
sbc b
535
jr nc, AIPrintItemUseAndUpdateHPBar
536
inc de
537
ld a, [de]
538
dec de
539
ld [hld], a
540
ld [wHPBarNewHP], a
541
ld a, [de]
542
ld [hl], a
543
ld [wHPBarNewHP+1], a
544
; fallthrough
545
546
AIPrintItemUseAndUpdateHPBar:
547
call AIPrintItemUse_
548
hlcoord 2, 2
549
xor a
550
ld [wHPBarType], a
551
predef UpdateHPBar2
552
jp DecrementAICount
553
554
AISwitchIfEnoughMons:
555
; enemy trainer switches if there are 2 or more unfainted mons in party
556
ld a, [wEnemyPartyCount]
557
ld c, a
558
ld hl, wEnemyMon1HP
559
560
ld d, 0 ; keep count of unfainted monsters
561
562
; count how many monsters haven't fainted yet
563
.loop
564
ld a, [hli]
565
ld b, a
566
ld a, [hld]
567
or b
568
jr z, .Fainted ; has monster fainted?
569
inc d
570
.Fainted
571
push bc
572
ld bc, wEnemyMon2 - wEnemyMon1
573
add hl, bc
574
pop bc
575
dec c
576
jr nz, .loop
577
578
ld a, d ; how many available monsters are there?
579
cp 2 ; don't bother if only 1
580
jp nc, SwitchEnemyMon
581
and a
582
ret
583
584
SwitchEnemyMon:
585
586
; prepare to withdraw the active monster: copy hp, number, and status to roster
587
588
ld a, [wEnemyMonPartyPos]
589
ld hl, wEnemyMon1HP
590
ld bc, wEnemyMon2 - wEnemyMon1
591
call AddNTimes
592
ld d, h
593
ld e, l
594
ld hl, wEnemyMonHP
595
ld bc, 4
596
call CopyData
597
598
ld hl, AIBattleWithdrawText
599
call PrintText
600
601
; This wFirstMonsNotOutYet variable is abused to prevent the player from
602
; switching in a new mon in response to this switch.
603
ld a, 1
604
ld [wFirstMonsNotOutYet], a
605
callfar EnemySendOut
606
xor a
607
ld [wFirstMonsNotOutYet], a
608
609
ld a, [wLinkState]
610
cp LINK_STATE_BATTLING
611
ret z
612
scf
613
ret
614
615
AIBattleWithdrawText:
616
text_far _AIBattleWithdrawText
617
text_end
618
619
AIUseFullHeal:
620
call AIPlayRestoringSFX
621
call AICureStatus
622
ld a, FULL_HEAL
623
jp AIPrintItemUse
624
625
AICureStatus:
626
; cures the status of enemy's active pokemon
627
ld a, [wEnemyMonPartyPos]
628
ld hl, wEnemyMon1Status
629
ld bc, wEnemyMon2 - wEnemyMon1
630
call AddNTimes
631
xor a
632
ld [hl], a ; clear status in enemy team roster
633
ld [wEnemyMonStatus], a ; clear status of active enemy
634
ld hl, wEnemyBattleStatus3
635
res BADLY_POISONED, [hl]
636
ret
637
638
AIUseXAccuracy: ; unreferenced
639
call AIPlayRestoringSFX
640
ld hl, wEnemyBattleStatus2
641
set USING_X_ACCURACY, [hl]
642
ld a, X_ACCURACY
643
jp AIPrintItemUse
644
645
AIUseGuardSpec:
646
call AIPlayRestoringSFX
647
ld hl, wEnemyBattleStatus2
648
set PROTECTED_BY_MIST, [hl]
649
ld a, GUARD_SPEC
650
jp AIPrintItemUse
651
652
AIUseDireHit: ; unreferenced
653
call AIPlayRestoringSFX
654
ld hl, wEnemyBattleStatus2
655
set GETTING_PUMPED, [hl]
656
ld a, DIRE_HIT
657
jp AIPrintItemUse
658
659
AICheckIfHPBelowFraction:
660
; return carry if enemy trainer's current HP is below 1 / a of the maximum
661
ldh [hDivisor], a
662
ld hl, wEnemyMonMaxHP
663
ld a, [hli]
664
ldh [hDividend], a
665
ld a, [hl]
666
ldh [hDividend + 1], a
667
ld b, 2
668
call Divide
669
ldh a, [hQuotient + 3]
670
ld c, a
671
ldh a, [hQuotient + 2]
672
ld b, a
673
ld hl, wEnemyMonHP + 1
674
ld a, [hld]
675
ld e, a
676
ld a, [hl]
677
ld d, a
678
ld a, d
679
sub b
680
ret nz
681
ld a, e
682
sub c
683
ret
684
685
AIUseXAttack:
686
ld b, ATTACK_UP1_EFFECT
687
ld a, X_ATTACK
688
jr AIIncreaseStat
689
690
AIUseXDefend:
691
ld b, DEFENSE_UP1_EFFECT
692
ld a, X_DEFEND
693
jr AIIncreaseStat
694
695
AIUseXSpeed:
696
ld b, SPEED_UP1_EFFECT
697
ld a, X_SPEED
698
jr AIIncreaseStat
699
700
AIUseXSpecial:
701
ld b, SPECIAL_UP1_EFFECT
702
ld a, X_SPECIAL
703
; fallthrough
704
705
AIIncreaseStat:
706
ld [wAIItem], a
707
push bc
708
call AIPrintItemUse_
709
pop bc
710
ld hl, wEnemyMoveEffect
711
ld a, [hld]
712
push af
713
ld a, [hl]
714
push af
715
push hl
716
ld a, XSTATITEM_DUPLICATE_ANIM
717
ld [hli], a
718
ld [hl], b
719
callfar StatModifierUpEffect
720
pop hl
721
pop af
722
ld [hli], a
723
pop af
724
ld [hl], a
725
jp DecrementAICount
726
727
AIPrintItemUse:
728
ld [wAIItem], a
729
call AIPrintItemUse_
730
jp DecrementAICount
731
732
AIPrintItemUse_:
733
; print "x used [wAIItem] on z!"
734
ld a, [wAIItem]
735
ld [wNamedObjectIndex], a
736
call GetItemName
737
ld hl, AIBattleUseItemText
738
jp PrintText
739
740
AIBattleUseItemText:
741
text_far _AIBattleUseItemText
742
text_end
743
744