root / ai.z80

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
;; In this file, two kinds of AI are handled
;;  * enemy AI : enemies of the solo mode
;;  * bot AI : enemy of the training mode


;; TODO : bot ai

; .dw enemy_x
; .dw enemy_z
ENEMY_ORIENTATION_OFFSET = 4
ENEMY_TYPE_OFFSET = 5
ENEMY_LIFE_OFFSET = 6
ENEMY_AI_OFFSET = 7
ENEMY_TIME_OFFSET = 8
ENEMY_SPRITE_SET_OFFSET = 9


HandleEnemyAI
    push    ix
    ld    ix,enemiesdata
    ld    b,(ix)    ; number of enemies
; don't verify if b>0
    inc    ix

EnemyAILoop
    push    bc

    ld    a,(ix+ENEMY_LIFE_OFFSET) ; get enemy life
    cp    ENEMY_DYING+1
    jp    c,DyingOrDeadEnemy
; the enemy is alive, has it noticed the player ?


    ld    a,(ix+ENEMY_AI_OFFSET)
    cp    1
    jp    z,EnemyActive
    cp    2
    jp    z,EnemyToShoot
    cp    3
    jp    z,EnemyShooting
    cp    4
    jp    z,EnemyHasShot

; so, ai state = 0 = idle

EnemyIdle
; it has not seen the player, but is the
; player close enough for it to see ?

    ld    d,(ix+1) ; enemyX
    ld    e,(ix+3) ; enemyZ

    call    access_visited_array
    and    (hl)
    jp    z,CheckNextEnemy

    call    ManhattanDistanceEnemyPlayer

    cp    8    ; enemy vision range = 8 tiles
    jp    nc,CheckNextEnemy ; player is not close enough


; if the player can't see the monster, then it can't see the player
; (as long as enemy orientation is not taken in account, it won't be done well)

; if this piece of code happens when the
; visited array has just been erased and not completely yet written in,
; it is as if the monster hasn't seen the player,
; so it reduces the difficulty :)

EnemyHasSeenPlayer

ENEMYSHOOTDELAY = 20

    ld    (ix+ENEMY_AI_OFFSET),1 ; activate "AI flag"
    ld    (ix+ENEMY_TIME_OFFSET),ENEMYSHOOTDELAY
    jp    CheckNextEnemy

EnemyActive
; each enemy either attacks or move at the same time

    ld    a,(ix+ENEMY_TIME_OFFSET)
    dec   a
    ld    (ix+ENEMY_TIME_OFFSET),a
    jp    nz,EnemyWalk
    ld    a,ENEMYSHOOTDELAY
    ld    (ix+ENEMY_TIME_OFFSET),a

    ld    (ix+ENEMY_AI_OFFSET),2 ; state "prepare to shoot"

    ld    hl,enemy_shoot_ready_sprite_set
    ld    (ix+ENEMY_SPRITE_SET_OFFSET),l
    ld    (ix+ENEMY_SPRITE_SET_OFFSET+1),h
    jp    CheckNextEnemy



EnemyToShoot
    ld    (ix+ENEMY_AI_OFFSET),3 ; state "shooting"

    ld    hl,enemy_shoot_fire_sprite_set
    ld    (ix+ENEMY_SPRITE_SET_OFFSET),l
    ld    (ix+ENEMY_SPRITE_SET_OFFSET+1),h

    ld    a,(difficulty)

    ld    d,0
    ld    e,a
    ld    hl,DifficultyScale
    add   hl,de
    ld    b,(hl)
    push  bc
    call  ManhattanDistanceEnemyPlayer
    pop   bc
    cp    7
    jp    nc,enemy_too_far_to_aim

    ld    d,0
    ld    e,a
    ld    hl,EnemyDistanceAimTable
    add   hl,de
    ld    a,(hl)
    add   a,b
    ld    b,a
enemy_too_far_to_aim

    push  bc
    call  vRandom
    pop   bc
    ld    a,h

    cp    b
    jp    nc,CheckNextEnemy

; now, we draw an imaginary line between the enemy and the player
; on the opaque array to prevent shooting through the walls

    ld    d,(ix+1) ; enemyX
    ld    e,(ix+3) ; enemyZ

    ld    a,(xperso_v+1)
    ld    h,a
    ld    a,(zperso_v+1)
    ld    l,a

    push    ix
    call    lineDraw2
    pop    ix
; draws an imaginary line between enmy and player on the opaque array
; if a wall is met -> carry flag is set
    jp    c,CheckNextEnemy

PlayerHit
    ld    a,(health)
; TODO : depends on the enemy type
    sub    2

#IFNDEF INVINCIBLE
    ld    (health),a
    call  z,KillPlayer
    call  m,KillPlayer
#ENDIF

    ld    a,1
    ld    (player_is_hit),a

    jp    CheckNextEnemy


EnemyShooting
    ld    (ix+ENEMY_AI_OFFSET),4 ; state "has shot"

    ld    hl,enemy_shoot_ready_sprite_set
    ld    (ix+ENEMY_SPRITE_SET_OFFSET),l
    ld    (ix+ENEMY_SPRITE_SET_OFFSET+1),h
    jp    CheckNextEnemy

EnemyHasShot
    ld    (ix+ENEMY_AI_OFFSET),1 ; state "active"

    ld    hl,enemy_walk1_sprite_set
    ld    (ix+ENEMY_SPRITE_SET_OFFSET),l
    ld    (ix+ENEMY_SPRITE_SET_OFFSET+1),h

    ld    (ix+ENEMY_TIME_OFFSET),ENEMYSHOOTDELAY

    jp    CheckNextEnemy

EnemyWalk
    ld    a,(ix+ENEMY_TIME_OFFSET)
    and   %1
    jp    z,EnemyWalk_sprite2
    ld    hl,enemy_walk1_sprite_set
    jp    EnemyWalk_sprite_put
EnemyWalk_sprite2
    ld    hl,enemy_walk2_sprite_set
EnemyWalk_sprite_put
    ld    (ix+ENEMY_SPRITE_SET_OFFSET),l
    ld    (ix+ENEMY_SPRITE_SET_OFFSET+1),h


; 1 : calculate enemy's orientation towards the player
; 2 : make it move forward using entity move routines

    ;ld    a,(ix+ENEMY_TIME_OFFSET)
    ;and   %111
    ;call  z,set_enemy_orientation

    call  set_enemy_orientation

    call  check_distance
    jp    c,CheckNextEnemy ; if monster too close
                            ; then don't move it forward

    ld    l,(ix)
    ld    h,(ix+1)

    ld      (entity_x),hl
    ld      (entity_x_old),hl

    ld    (x_monster_old),hl
    ld    (x_monster),hl

    ld    l,(ix+2)
    ld    h,(ix+3)
    ld      (entity_z),hl
    ld      (entity_z_old),hl

    ld      a,(ix+ENEMY_ORIENTATION_OFFSET)
    ld      (entity_orientation),a
    ld      a,ENTITY_TYPE_ENEMY
    ld      (entity_type),a

    call    forward

    ld      hl,(entity_x)
    ld    (ix),l
    ld    (ix+1),h
    ld      hl,(entity_z)
    ld    (ix+2),l
    ld    (ix+3),h

    jp CheckNextEnemy



DyingOrDeadEnemy
    ld a,(ix+ENEMY_LIFE_OFFSET) ; get enemy life
    or a
    jp z,DeadEnemy
    dec a
    ld (ix+ENEMY_LIFE_OFFSET),a


    ld hl,enemy_die_sprite_set
    ld (ix+ENEMY_SPRITE_SET_OFFSET),l
    ld (ix+ENEMY_SPRITE_SET_OFFSET+1),h
    jp CheckNextEnemy

DeadEnemy
    ld hl,enemy_dead_sprite_set
    ld (ix+ENEMY_SPRITE_SET_OFFSET),l
    ld (ix+ENEMY_SPRITE_SET_OFFSET+1),h
    jp CheckNextEnemy


CheckNextEnemy
    ld    de,16
    add    ix,de
    pop    bc
    dec    b        ;    djnz    EnemyIALoop
    jp    nz,EnemyAILoop    ;

    pop    ix

    ret



;==================== SUBROUTINES ======================

ManhattanDistanceEnemyPlayer
    ld    e,(ix+1) ; enemyX
    ld    d,(ix+3) ; enemyZ
    ld    a,(xperso_v+1)
    sub    e
    jp    p,blablalab
    neg
blablalab
    ld    e,a
    ld    a,(zperso_v+1)
    sub    d
    jp    p,blahblahlab2
    neg
blahblahlab2
    add    a,e
    ret



EnemyOrientationTable
    .db    105,90,75,0
    .db    0,0,60,0
    .db    15,30,45,0


;=====================
set_enemy_orientation
;=====================
    ld    e,(ix)
    ld    d,(ix+1) ; enemyX
    ld    hl,(xperso_v)
    push bc
    ld    b,9
    call  cp_hl_de_approx_b
    pop bc
    ;call  cp_hl_de
    ld    b,1
    jp    z,suite_s_e_o
    ld    b,2
    jp    c,suite_s_e_o
    ld    b,0

suite_s_e_o
    ld    e,(ix+2)
    ld    d,(ix+3) ; enemyZ
    ld    hl,(zperso_v)
    push bc
    ld    b,9
    call  cp_hl_de_approx_b
    pop bc
    ;call  cp_hl_de
    jp    z,same_z
    ld    c,0
    jp    c,end_s_e_o
monster_z_inf
    ld    c,2
    jp    end_s_e_o

same_z
    ld    a,b
    dec    a    ; cp 1  ; if enemy and player are close enough
    jp    z,do_not_set_enemy_orientation ; then do nothing

    ld    c,1
    jp    end_s_e_o

end_s_e_o
    ld    hl,EnemyOrientationTable
    ld    d,0
    ld    e,b
    add    hl,de
    ld    e,c
    sla    e
    sla    e ; e*4
    add    hl,de
    ld    a,(hl)

    ;push    af
    ;call    vRandom
    ;ld    a,%00001111
    ;and    h
    ;sub    8    ; a contains a number between -8 and 7
    ;pop    bc
    ;add    a,b    ; add randomness to orientation

    ;jp    m,s_e_o_lower

    ;cp    120-1
    ;jp    nc,s_e_o_greater

    ld    (ix+ENEMY_ORIENTATION_OFFSET),a
    scf
    ret

;s_e_o_greater
    ;sub    120
    ;ld    (ix+ENEMY_ORIENTATION_OFFSET),a
    ;scf
    ;ret

;s_e_o_lower
    ;add    a,120
    ;ld    (ix+ENEMY_ORIENTATION_OFFSET),a
    ;scf
    ;ret

do_not_set_enemy_orientation
    scf
    ccf
    ret


;=====================
check_distance;check if enemy not too close. return C if too close. NC if not.
;=====================
    ld    e,(ix)
    ld    d,(ix+1) ; enemyX
    ld    hl,(xperso_v)

    push bc
    ld    b,100
    call  cp_hl_de_approx_b
    pop bc

    jp nz,_check_distance_ret_nc


    ld    e,(ix+2)
    ld    d,(ix+3) ; enemyZ
    ld    hl,(zperso_v)

    push bc
    ld    b,100
    call  cp_hl_de_approx_b
    pop bc

    jp nz,_check_distance_ret_nc

    scf
    ret


_check_distance_ret_nc
    scf
    ccf
    ret