;;-----------------------------LICENSE NOTICE------------------------------------
;;  This file is part of Nightmare Fortress: An Amstrad CPC Game 
;;  Copyright (C) 2017 Natalia Bernal Pérez / Álvaro Esteve Bernabeu / Plácido Antonio López Ávila
;;
;;  Nightmare Fortress is free software: you can redistribute it and/or modify
;;  it under the terms of the GNU Lesser General Public License as published by
;;  the Free Software Foundation, either version 3 of the License, or
;;  (at your option) any later version.
;;
;;  Nightmare Fortress is distributed in the hope that it will be useful,
;;  but WITHOUT ANY WARRANTY; without even the implied warranty of
;;  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;  GNU Lesser General Public License for more details.
;;
;;  You should have received a copy of the GNU Lesser General Public License
;;  along with Nightmare Fortress.  If not, see <http://www.gnu.org/licenses/>.
;;-------------------------------------------------------------------------------

.area _DATA
.area _CODE


;;-----------------------------------------------------------------------
;;PRIVATE DATA 
;;-----------------------------------------------------------------------
.include "cpctelera.h.s"
.include "utils.h.s"
.include "macros.h.s"
.include "sprites/sprites.h.s"
.include "actors/character.h.s"
.include "gameController.h.s"
.include "interface.h.s"
.include "stage.h.s"

contador:   .db #1  
numEnem: .db #3                                                                                                                                   


;;-----------------------------------------------------------------------
;;PUBLIC DATA
;;-----------------------------------------------------------------------
enemy_size_in_bytes::  .db #38                  ;; Valor necesario para iterar sobre el array de enemigos



;;-----------------------------------------------------------------------
;;PUBLIC FUNCTIONS
;;-----------------------------------------------------------------------

;;-----------------------------------------------------------------------
;;PRIVATE FUNCTIONS
;;-----------------------------------------------------------------------

comp_ataque:

    cp #1
    jp nz, no_c

    push af

    ld  a, cont_attack(ix)
    cp #0
    jp nz, at_fin

    ld  a, last_cont_attack(ix)
    ld  cont_attack(ix), a

    ld  a, #2
    ld  b, a

    pop af
    ret

    at_fin:
    dec a
    ld  cont_attack(ix), a
    ld  a, #0
    ld  b, a
    pop af
    ret

    no_c:
    push af
    ld	a, #0
    ld	b, a

    pop af
    ret

ataque::

    ld  a, vivo_muerto(ix)
    cp #0
    jp z, no_existe

    ld  b, attack(ix)


    ld a, (hp_number)

    sub b
    jp c, cero
    ld  (hp_number), a
    ret
    cero:
    ld  a, #0
    ld (hp_number), a

    no_existe:
    ret


;;------------------------------------------------
;; Aqui debe ir el codigo de IA
;;------------------------------------------------
enemy_doAction::
; 
    ;;ld  a, (contador)
    ;;cp #0
    ;;jp  z, continuar
    ;;dec a
    ;;ld  (contador), a
    ;;jp fin
;;
    ld a, vivo_muerto(ix)
    cp #0
    jp nz, is_alive
    call entity_destroy
    ld a, x(ix)
    cp #0
    jp nz, no_borrar
    call entity_destroy
    call entity_erase
    ret


    no_borrar:
        ret
    is_alive:


    continuar:

    ;;ld  a, #1                                   ;; Cargo en a el valor para el contador de movimiento. a = 1 / Por cada 2 ejecuciones el personaje solo se mueve una vez
    ;;ld  (contador), a                           ;; Actualizo el contador

    call character_getPtrIX_hl                  ;; Llamada que devuelve en hl el puntero del personaje
    ex de, hl                                   ;; de = hl
    ;ld hl, #enemy_data
    push ix                                     ;; Almaceno el puntero IX en la pila
    pop hl                                      ;; Extraigo hl de la pila (puntero al enemigo)
    call distance_between                       ;; Calculo de la distancia de Manhattan entre el enemigo y el personaje

    ld a, state(ix)                             ;; Guardamos el estado actual del enemigo en el estado anterior
    ld last_state(ix), a                        ;; |
    ld a, #0                                    ;; El enemigo no se esta movimiento
    ld moving(ix), a                            ;; |

    ld  a, c                                    ;; Cargo en a el valor de b (distancia en X)
    cp #82                                     ;; Realizo la diferencia de 32
    jp nc, vagabundo                                  ;; Si el valor de X - 80 no provoca acarreo (X mayor al rango) no realiza mas acciones
    ld  a, b                                    ;; Cargo en a el valor de c (distancia en Y)
    cp #26                                      ;; Realizo la diferencia de 70
    jp nc, vagabundo                                  ;; Si el valor de Y - 25 no provoca acarreo (Y mayor al rango) no realiza mas acciones

    ld d, b                                     ;; Cargo en d el valor de b
    push de                                     ;; Almaceno en la pila el puntero DE

    ld  a, c
    cp  #10
    jp  c, X_mayor_aux


    push ix                                     ;; Almaceno en la pila el puntero IX


    ;;call vision_WCharacter


    ;; Comprobacion de la posicion para el movimiento
    call character_getPtrIX                     ;; Obtiene el puntero al enemigo en IX                    
    ld  c, y(ix)                                ;; Almaceno en c el valor de la Y del personaje

    pop ix                                      ;; Extraigo el puntero IX del enemigo

    ld  b, y(ix)                                ;; Almaceno el Y del enemigo valor en b

    call comparar                               ;; Comprobacion del mayor valor de los registros b y c
    cp #0                                       ;; Decremento 0 a A
    jp  z, bajar                                ;; Si esta resta da 0, significa que es necesario bajar en el eje Y


    ;; Este caso se trata del desplazamiento en vertical hacia arriba del enemigo
    ld  a, y(ix)                                ;; Almaceno en a el valor de Y
    sub #2                                      ;; Resto 2
    ld  y(ix), a                                ;; Almaceno el nuevo valor en Y, por lo tanto cambiamos su posicion en el eje Y

    ld a, #5
    ld state(ix), a
    ld a, #1
    ld moving(ix), a
    ;call comprobar_pared
    call tile_collision                         ;; Comprobamos si la posicion es posible o si es una colision con una pared

    cp #1                                       ;; Resto 1
    ld	a, #0
    ld	b, a
    jp  z, pared_y_subir                        ;; En caso de ser 1 el registro a, significa que ha habido una colision y es necesario retroceder

    push ix					                    ;; Almaceno en la pila IX
    push ix					                    ;; Vuelvo a realizar esta accion para tenerlo 2 veces en la pila
    pop de					                    ;; Introduzco en DE el valor de IX

    ld ix, (currentStage)			            ;; 
    ld h, enemies_h(ix)
    ld l, enemies_l(ix)
    push hl 
    pop ix

    call check_enemy_collision
    pop ix
    cp #0
    jp nz, pared_y_subir

    ;; Aqui compruebo si se colisionan enemigo y mago
    call character_getPtrIX_hl
    ex de, hl
    push ix
    pop hl
    call check_collision

    call comp_ataque
    cp #0
    jp nz, pared_y_subir
    
    pop de
    push de

    ld  a, d                                    ;; Cargo en a el valor de d
    cp #90                                      ;; Descuento 90
    jp c, X_mayor                               ;; En caso de ser menor el valor de 90 saltamos a X_mayor

    pop de

    ret 

    ;; Este es el caso contrario al anterior, es el caso de bajada del enemigo
    bajar:                                

    ld  a, y(ix)
    add #2
    ld  y(ix), a

    ld a, #3
    ld state(ix), a                             ;;Actualizamos el estado del enemigo
    ld a, #1
    ld moving(ix), a
   ;call comprobar_pared
    call tile_collision

    cp #1
    ld	a, #0
    ld	b, a
    jp  z, pared_y_bajar

    push ix
    push ix
    pop de

    ld ix, (currentStage)
    ld h, enemies_h(ix)
    ld l, enemies_l(ix)
    push hl 
    pop ix

    call check_enemy_collision
    pop ix
    cp #0
    jp nz, pared_y_bajar

    call character_getPtrIX_hl
    ex de, hl
    push ix
    pop hl
    call check_collision

    call comp_ataque
    cp #0
    jp nz, pared_y_bajar

    pop de 
    push de

    ld  a, d
    cp #90
    jp c, X_mayor

    pop de

    ret 

    X_mayor_aux:

    call character_getPtrIX_hl
    ex de, hl
    push ix
    pop hl
    call check_collision

    ;ld  a, #1

    call comp_ataque

    ld  a, b
    cp #2
    call z, ataque
    ;cp #0
    ;jp nz, pared_y_bajar

    pop de 
    push de

    ld  a, d
    cp #90
    jp c, X_mayor

    pop de

    ret 

    X_mayor:

    pop de

    ld  a, d
    cp  #3
    jp  c, no_X

    push ix

    call character_getPtrIX
    ld  c, x(ix)

    pop ix

    ld  b, x(ix)

    call comparar
    cp #0
    jp  z, derecha

    ld  a, x(ix)
    dec a
    ld  x(ix), a

    ld a, #4
    ld state(ix), a          ;;Actualizamos el estado del enemigo
    ld a, #1
    ld moving(ix), a
    ;call comprobar_pared
    call tile_collision

    cp #1
    ld	a, #0
    ld	b, a
    jp  z, pared_x_izquierda

    push ix
    push ix
    pop de

    ld ix, (currentStage)
    ld h, enemies_h(ix)
    ld l, enemies_l(ix)
    push hl 
    pop ix

    call check_enemy_collision
    pop ix
    cp #0
    jp nz, pared_x_izquierda

    call character_getPtrIX_hl
    ex de, hl
    push ix
    pop hl
    call check_collision

    call comp_ataque
    cp #0
    jp nz, pared_x_izquierda

    ret 

    derecha:

    ld  a, x(ix)
    inc a
    ld  x(ix), a

    ld a, #2
    ld state(ix), a          ;;Actualizamos el estado del enemigo
    ld a, #1
    ld moving(ix), a
    ;call comprobar_pared
    call tile_collision

    cp #1
    ld	a, #0
    ld	b, a
    jp  z, pared_x_derecha

    push ix
    push ix
    pop de

    ld ix, (currentStage)
    ld h, enemies_h(ix)
    ld l, enemies_l(ix)
    push hl 
    pop ix

    call check_enemy_collision
    pop ix
    cp #0
    jp nz, pared_x_derecha

    call character_getPtrIX_hl
    ex de, hl
    push ix
    pop hl
    call check_collision

    call comp_ataque
    cp #0
    jp nz, pared_x_derecha

    ret

    no_X:
    ;call character_getPtrIX_hl
    ;ex de, hl
    ;push ix
    ;pop hl
    ;call check_collision
;
    ;call comp_ataque
    ;cp #0
    ;jp nz, pared_x_derecha

    ret 

    pared_y_bajar:

    ld  a, b
    cp #2
    call z, ataque

    ld  a, y(ix)
    sub #2
    ld  y(ix), a

    ld a, #3
    ld state(ix), a          ;;Actualizamos el estado del enemigo
    ld a, #1
    ld moving(ix), a

    pop de 
    ret

    pared_y_subir:

    ld  a, b
    cp #2
    call z, ataque

    ld  a, y(ix)
    add #2
    ld  y(ix), a

    ld a, #5
    ld state(ix), a          ;;Actualizamos el estado del enemigo
    ld a, #1
    ld moving(ix), a

    pop de 
    ret

    pared_x_derecha:

    ld  a, b
    cp #2
    call z, ataque

    ld  a, x(ix)
    dec a
    ld  x(ix), a

    ld a, #2
    ld state(ix), a          ;;Actualizamos el estado del enemigo
    ld a, #1
    ld moving(ix), a

    ret

    pared_x_izquierda:

    ld  a, b
    cp #2
    call z, ataque

    ld  a, x(ix)
    inc a
    ld  x(ix), a

    ld a, #4
    ld state(ix), a          ;;Actualizamos el estado del enemigo
    ld a, #1
    ld moving(ix), a
    
    ret


    vagabundo:
        ld  a, #0
        ld  b, a

        ld  a, cont_dir(ix)
        cp  #0
        jp  z, dale
;
;
        dec a
        ld  cont_dir(ix), a

        ld  a, s1(ix)

        cp  #1
        jp  z, pared_y_subir_aux
        cp  #2
        jp  z, pared_y_bajar_aux
        cp  #3
        jp  z, pared_x_derecha_aux
        cp  #4
        jp  z, pared_x_izquierda_aux

        ret

        dale:

        ld  a, #10
        ld  cont_dir(ix), a

        call cpct_getRandom_mxor_u16_asm
        ld  a, l
        cp #90
        ret c
        cp  #130 ;; 1
        jp  c, pared_y_subir_aux
        cp  #170 ;; 2
        jp  c, pared_y_bajar_aux
        cp  #210 ;; 3
        jp  c,  pared_x_derecha_aux
        jp  pared_x_izquierda_aux
;
        pared_y_subir_aux:

        ld  a, #1
        ld  s1(ix), a
        
        ld  a, y(ix)
        add #2
        ld  y(ix), a

        push ix
        push ix
        pop de

        ld ix, (currentStage)
    	ld h, enemies_h(ix)
    	ld l, enemies_l(ix)
    	push hl 
    	pop ix

        call check_enemy_collision
        pop ix
	cp #0
	jp nz, resta_en_y

        call tile_collision
        cp  #1
        jp  z, resta_en_y
        call check_bounds
        cp #1
        jp  z, resta_en_y

        

        ld a, #3
       ld state(ix), a          ;;Actualizamos el estado del enemigo
       ld a, #1
       ld moving(ix), a 


        ret

;
        pared_y_bajar_aux:

        ld  a, #2
        ld  s1(ix), a

        ld  a, y(ix)
        sub #2
        ld  y(ix), a

        push ix
        push ix
        pop de

        ld ix, (currentStage)
    	ld h, enemies_h(ix)
    	ld l, enemies_l(ix)
    	push hl 
    	pop ix

        call check_enemy_collision
        pop ix
	cp #0
	jp nz, suma_en_y

        call tile_collision
        cp  #1
        jp  z, suma_en_y
        call check_bounds
        cp #1
        jp  z, suma_en_y


       ld a, #5

        ld state(ix), a          ;;Actualizamos el estado del enemigo
        ld a, #1
        ld moving(ix), a 

        ret
;
        pared_x_derecha_aux:

        ld  a, #3
        ld  s1(ix), a

        ld  a, x(ix)
        dec a
        ld  x(ix), a

        push ix
        push ix
        pop de

        ld ix, (currentStage)
    	ld h, enemies_h(ix)
    	ld l, enemies_l(ix)
    	push hl 
    	pop ix

        call check_enemy_collision
        pop ix
	cp #0
	jp nz, suma_en_x

        call tile_collision
        cp  #1
        jp  z, suma_en_x
        call check_bounds
        cp #1
        jp  z, suma_en_x

        ld a, #4
        ld state(ix), a          ;;Actualizamos el estado del enemigo
        ld a, #1
        ld moving(ix), a

        ret
;
        pared_x_izquierda_aux:

        ld  a, #4
        ld  s1(ix), a

        ld  a, x(ix)
        inc a
        ld  x(ix), a

        push ix
        push ix
        pop de

        ld ix, (currentStage)
    	ld h, enemies_h(ix)
    	ld l, enemies_l(ix)
    	push hl 
    	pop ix

        call check_enemy_collision
        pop ix
	cp #0
	jp nz, resta_en_x

        call tile_collision
        cp  #1
        jp  z, resta_en_x
        call check_bounds
        cp #1
        jp  z, resta_en_x

        ld a, #2
        ld state(ix), a          ;;Actualizamos el estado del enemigo
        ld a, #1
        ld moving(ix), a


        ret

        resta_en_x:
            ld  a, x(ix)
            dec a
            ld  x(ix), a
            ret

        suma_en_x:
            ld  a, x(ix)
            inc a
            ld  x(ix), a
            ret

        suma_en_y:
            ld  a, y(ix)
            add #2
            ld  y(ix), a
            ret

        resta_en_y:
            ld  a, y(ix)
            sub #2
            ld  y(ix), a
            ret


check_bounds:
    ld a, x(ix)
    cp #4
    jp c, out_of_bounds
    cp #74
    jp nc, out_of_bounds

    ld a, y(ix)
    cp #4
    jp c, out_of_bounds
    cp #178
    jp nc, out_of_bounds

    ld a, #0
    ret

    out_of_bounds:
        ld a, #1
        ret
;;------------------------------------------------
;; Dibuja el enemigo con la animacion correspondiente (MAL, NECESITA MEJORA)
;;
;;------------------------------------------------
enemy_draw::
    ;;call enemy_getPtrIX
    ;;ld a, state(ix)
    ;;ld b, numFrames(ix)
;;
    ;;ld a, b
    ;;ld h, standing_h(ix)
    ;;ld l, standing_l(ix)
;;
    ;;ld numFrames(ix), a
;;
    ;;ld sprite_l(ix), l
    ;;ld sprite_h(ix), h
    ;;call entity_draw

    ret

;;--------------------------------------------------------------------------------------------------------
;;  Carga en IX el puntero a la entidad que se quiera borrar y redibuja el fondo que estaba detras
;;--------------------------------------------------------------------------------------------------------

enemy_erase::
   ;;all enemy_getPtrIX
   ;;all entity_erase
    ret



;;--------------------------------------------------------------------------------------------------------
;;  Destruye un enemigo poniendo su atributo X a 0
;;  -> IX es el puntero al enemigo que se quiere destruir
;;--------------------------------------------------------------------------------------------------------
enemy_destroy::
    call entity_destroy
    ret

;;------------------------------------------------
;; Llama a la funcion generica que modifica las x y las y para el doble buffer
;;
;;  -> IX es el puntero a la entidad que se quiere modificar
;;------------------------------------------------
enemy_update_xy::
    ld ix, (currentStage)
    ld h, enemies_h(ix)
    ld l, enemies_l(ix)
    push hl 
    pop ix
    ld bc, (enemy_size_in_bytes) ;; Hay que sumarle al puntero del array el numero de elementos que tiene el array
    call array_update_xy
    ret
