;##################################################################
;
;   Phoenix-Z80 (Enemy firing)
;
;   Programmed by Patrick Davidson (pad@calc.org)
;        
;   Copyright 2002 by Patrick Davidson.  This software may be freely
;   modified and/or copied with no restrictions.  There is no warranty.
;
;   This file was last updated July 7, 2002.
;
;##################################################################     

enemy_shoot:
        ld      de,e_firetype
        add     hl,de
        ld      a,(hl)
        inc     hl
        ld      (smc_enemy_fire_select+1),a
smc_enemy_fire_select:
        jr      enemy_fire_table
enemy_fire_table:
        jr      fire_random             ; 0
        ret                             ; 2
                                        ; 3
fire_periodic:
        ld      a,(hl)                  ; B = rate
        inc     hl
        inc     (hl)                    ; increment counter
        cp      (hl)
        ret     nz                      ; exit if rate not reached
        ld      (hl),0                  ; reset counter
        inc     hl
        jr      select_weapon

fire_random:
        call    FAST_RANDOM
        cp      (hl)
        ret     nc
        inc     hl
        inc     hl

select_weapon:
        ld      a,(hl)                  ; A = weapon type
        ld      (smc_enemy_weapon_select+1),a
        inc     hl
        ld      a,(hl)                  ; A = weapon power
        ld      (smc_enemy_bullet_power+1),a
smc_enemy_weapon_select:
        jr      enemy_weapon_table
enemy_weapon_table:
        jp      normal_weapon           ; 0
        jp      double_weapon           ; 3
        jp      semi_aim_shoot_now      ; 6
        jp      double_big              ; 9
        jp      double_huge             ; 12
        jp      fire_arrow              ; 15
        jp      single_big              ; 18

;############## Single big shots

single_huge:    
        ld      de,e_x-e_firepower
        add     hl,de
        inc     (hl)
        push    hl
        call    fire_huge_bullet
        pop     hl
        dec     (hl)
        ret

single_big:
        ld      de,e_x-e_firepower
        add     hl,de
        inc     (hl)
        inc     (hl)
        push    hl
        call    fire_big_bullet
        pop     hl
        dec     (hl)
        dec     (hl)
        ret

;############## Fire arrow

fire_arrow:
        ld      de,e_w-e_firepower
        add     hl,de
        ld      de,arrow_ebullet_data
        jp      fire_enemy_bullet
        
arrow_ebullet_data:
        .db     21
        .db     3
        .db     7

;############## Double shots

double_big:
        ld      de,fire_big_bullet
        ld      a,13
        jp      double_weapon_main

double_huge:
        ld      de,fire_huge_bullet
        ld      a,11
        jp      double_weapon_main

;############## Fire a large, fully-aimed bullet (HL -> X)

fire_huge_bullet:
        ld      d,-1
        jr      fire_large_common

fire_big_bullet:
        ld      d,0
fire_large_common:
        inc     hl
        inc     hl
        ld      a,(player_y)
        sub     (hl)
        ret     c
        srl     a
        srl     a
        ld      b,a                     ; B = (PY - EY)
        srl     a
        add     a,b
        ret     z
        ld      b,a                     ; B = (3/8) * (PY - EY)
        dec     hl
        dec     hl
        ld      a,(player_x)
        sub     (hl)                    ; A = PX - EX
        inc     hl
        jr      c,fire_big_left
        call    get_angle
        ld      a,c
        
deploy_big:
        ld      bc,(which_shot)
        bit     7,d
        jr      nz,deploy_huge
        add     a,27
        ld      de,big_ebullet_data
        ld      (de),a
        jp      fire_enemy_bullet

deploy_huge:
        add     a,36
        ld      de,big_ebullet_data
        ld      (de),a
        jp      fire_enemy_bullet

fire_big_left:
        neg
        call    get_angle
        ld      a,c
        neg
        jr      deploy_big

get_angle:
        ld      c,0
        sub     b
        ret     c                       ; DX < (3/8) DY -> straight down
        inc     c
        sub     b
        ret     c                       ; DX < (3/4) DY -> slight angle
        inc     c
        sub     b
        ret     c                       ; DX < (3/2) DY -> 45 deg angle
        sub     b
        ret     c
        inc     c                       ; DX > (3/2) DY -> high angle

        ret

big_ebullet_data:
        .db     3
        .db     5
        .db     5

;############## Fire an aimed bullet, accounting for Y difference

semi_aim_shoot_now:
        ld      de,e_y-e_firepower
        add     hl,de
        ld      a,(player_y)
        sub     (hl)
        ret     c
        srl     a
        srl     a
        ld      b,a                     ; B = (PY - EY)
        srl     a
        add     a,b
        ret     z
        ld      b,a                     ; B = (3/8) * (PY - EY)
        dec     hl
        dec     hl
        ld      a,(player_x)
        sub     (hl)                    ; A = PX - EX
        inc     hl
        jr      c,figurefireleft
        cp      b
        jp      c,fire_enemy_bullet_std
        jp      fire_bullet_half_right

figurefireleft:
        neg
        cp      b
        jp      c,fire_enemy_bullet_std
        jp      fire_bullet_half_left

;############## Double (boss) shot

double_weapon:
        ld      de,fire_aimed_bullet
        ld      a,14
double_weapon_main:
        ld      (smc_which_shot+1),de
        ld      de,e_x-e_firepower
        add     hl,de
        ld      b,(hl)
        push    bc
        add     a,b
        ld      (hl),a
        push    hl
        call    smc_which_shot
        pop     hl
        pop     bc
        ld      (hl),b
smc_which_shot:
        jp      31337

;############## Fire an aimed bullet (HL -> X coordinate)

fire_aimed_bullet:
        ld      a,(player_x)
        add     a,30
        cp      (hl)                    ; PlayerX + 30 - ShootX
        jr      c,fire_bullet_left      ; If ShootX > PlayerX + 30
        sub     60                      ; PlayerX - 30
        jr      c,fire_bullet_std       ; If PlayerX < 30
        cp      (hl)                    ; PlayerX - 30 - ShootX
        jr      nc,fire_bullet_right    ; If ShootX <= PlayerX - 30

fire_bullet_std:
        inc     hl
fire_enemy_bullet_std:
        ld      de,standard_ebullet_data
        jr      fire_enemy_bullet

fire_bullet_left:
        inc     hl
        ld      de,left_ebullet_data
        jr      fire_enemy_bullet

fire_bullet_right:
        inc     hl
        ld      de,right_ebullet_data
        jr      fire_enemy_bullet

fire_bullet_half_left:
        ld      de,half_left_ebullet_data
        jr      fire_enemy_bullet

fire_bullet_half_right:
        ld      de,half_right_ebullet_data
        jr      fire_enemy_bullet

standard_ebullet_data:
        .db     3               ; Type
        .db     2               ; Width
        .db     2               ; Height

left_ebullet_data:
        .db     9               ; Type
        .db     2               ; Width
        .db     2               ; Height

right_ebullet_data:
        .db     12              ; Type
        .db     2               ; Width
        .db     2               ; Height

half_left_ebullet_data:
        .db     15              ; Type
        .db     2               ; Width
        .db     2               ; Height

half_right_ebullet_data:
        .db     18              ; Type
        .db     2               ; Width
        .db     2               ; Height

;############## Normal (drop small block) shot

normal_weapon:
        ld      de,e_w-e_firepower
        add     hl,de
        ld      de,standard_ebullet_data

;############## Fire any type of bullet (HL -> width, DE -> info)

fire_enemy_bullet:
        push    hl
        push    de

        call    locate_enemy_bullet     ; HL -> enemy bullet entry

        pop     de                      ; DE -> bullet description
        pop     bc                      ; BC -> enemy Y coordinate
        ex      de,hl                   ; HL -> bullet description
                                        ; DE -> enemy bullet entry

        ldi                             ; Copy type, BC -> enemy X
smc_enemy_bullet_power:
        ld      a,1
        ld      (de),a                  ; store power
        inc     de                  
        ld      a,(bc)
        add     a,2
        ld      (de),a                  ; Store ebullet X
        inc     de
        ldi                             ; Copy width, BC -> enemy X - 1
        inc     bc
        inc     bc
        inc     bc                      ; BC -> enemy Y
        ld      a,(bc)
        add     a,7
        ld      (de),a                  ; Store ebullet Y
        inc     de
        ldi                             ; Copy height
        ret

;############## Locate an unused enemy bullet

locate_enemy_bullet:
        ld      hl,eb_array
        ld      b,eb_num
        ld      de,eb_size
loop_locate_eb:
        ld      a,(hl)
        or      a
        ret     z
        add     hl,de
        djnz    loop_locate_eb
        pop     hl
        pop     de
        pop     hl
        ret
