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
|
AIChooseMove:
; Score each move of wEnemyMonMoves in wEnemyAIMoveScores. Lower is better.
; Pick the move with the lowest score.
; Wildmons attack at random.
ld a, [wBattleMode]
dec a
ret z
ld a, [wLinkMode]
and a
ret nz
; No use picking a move if there's no choice.
farcall CheckEnemyLockedIn
ret nz
; The default score is 20. Unusable moves are given a score of 80.
ld a, 20
ld hl, wEnemyAIMoveScores
ld [hli], a
ld [hli], a
ld [hli], a
ld [hl], a
; Don't pick disabled moves.
ld a, [wEnemyDisabledMove]
and a
jr z, .CheckPP
ld hl, wEnemyMonMoves
ld c, 0
.CheckDisabledMove:
cp [hl]
jr z, .ScoreDisabledMove
inc c
inc hl
jr .CheckDisabledMove
.ScoreDisabledMove:
ld hl, wEnemyAIMoveScores
ld b, 0
add hl, bc
ld [hl], 80
; Don't pick moves with 0 PP.
.CheckPP:
ld hl, wEnemyAIMoveScores - 1
ld de, wEnemyMonPP
ld b, 0
.CheckMovePP:
inc b
ld a, b
cp NUM_MOVES + 1
jr z, .ApplyLayers
inc hl
ld a, [de]
inc de
and PP_MASK
jr nz, .CheckMovePP
ld [hl], 80
jr .CheckMovePP
; Apply AI scoring layers depending on the trainer class.
.ApplyLayers:
ld hl, TrainerClassAttributes + TRNATTR_AI_MOVE_WEIGHTS
; If we have a battle in BattleTower just load the Attributes of the first trainer class in wTrainerClass (Falkner)
; so we have always the same AI, regardless of the loaded class of trainer
ld a, [wInBattleTowerBattle]
bit 0, a
jr nz, .battle_tower_skip
ld a, [wTrainerClass]
dec a
ld bc, 7 ; Trainer2AI - Trainer1AI
call AddNTimes
.battle_tower_skip
lb bc, CHECK_FLAG, 0
push bc
push hl
.CheckLayer:
pop hl
pop bc
ld a, c
cp 16 ; up to 16 scoring layers
jr z, .DecrementScores
push bc
ld d, BANK(TrainerClassAttributes)
predef SmallFarFlagAction
ld d, c
pop bc
inc c
push bc
push hl
ld a, d
and a
jr z, .CheckLayer
ld hl, AIScoringPointers
dec c
ld b, 0
add hl, bc
add hl, bc
ld a, [hli]
ld h, [hl]
ld l, a
ld a, BANK(AIScoring)
call FarCall_hl
jr .CheckLayer
; Decrement the scores of all moves one by one until one reaches 0.
.DecrementScores:
ld hl, wEnemyAIMoveScores
ld de, wEnemyMonMoves
ld c, NUM_MOVES
.DecrementNextScore:
; If the enemy has no moves, this will infinite.
ld a, [de]
inc de
and a
jr z, .DecrementScores
; We are done whenever a score reaches 0
dec [hl]
jr z, .PickLowestScoreMoves
; If we just decremented the fourth move's score, go back to the first move
inc hl
dec c
jr z, .DecrementScores
jr .DecrementNextScore
; In order to avoid bias towards the moves located first in memory, increment the scores
; that were decremented one more time than the rest (in case there was a tie).
; This means that the minimum score will be 1.
.PickLowestScoreMoves:
ld a, c
.move_loop
inc [hl]
dec hl
inc a
cp NUM_MOVES + 1
jr nz, .move_loop
ld hl, wEnemyAIMoveScores
ld de, wEnemyMonMoves
ld c, NUM_MOVES
; Give a score of 0 to a blank move
.loop2
ld a, [de]
and a
jr nz, .skip_load
ld [hl], a
; Disregard the move if its score is not 1
.skip_load
ld a, [hl]
dec a
jr z, .keep
xor a
ld [hli], a
jr .after_toss
.keep
ld a, [de]
ld [hli], a
.after_toss
inc de
dec c
jr nz, .loop2
; Randomly choose one of the moves with a score of 1
.ChooseMove:
ld hl, wEnemyAIMoveScores
call Random
maskbits NUM_MOVES
ld c, a
ld b, 0
add hl, bc
ld a, [hl]
and a
jr z, .ChooseMove
ld [wCurEnemyMove], a
ld a, c
ld [wCurEnemyMoveNum], a
ret
AIScoringPointers:
; entries correspond to AI_* constants
dw AI_Basic
dw AI_Setup
dw AI_Types
dw AI_Offensive
dw AI_Smart
dw AI_Opportunist
dw AI_Aggressive
dw AI_Cautious
dw AI_Status
dw AI_Risky
dw AI_None
dw AI_None
dw AI_None
dw AI_None
dw AI_None
dw AI_None
|