.include "collision.h.s"
.include "man/entity.h.s"
.include "man/game.h.s"
.include "man/entity_templates.h.s"


registro_hl:  .dw 0x000
registro_aux: .dw 0x000

.globl bool_muerte
.globl bool_vida
.globl bool_arcoiris
.globl _gameover1

;;  Para hacer las colisiones tenemos en
;;  IX -> Entidad1 
;;  IY -> Entidad2

;; Colision Enemigo choca contra el Player
enemy_player:

    push ix
    push iy

    ld a, #0x01
    ld (bool_muerte), a

    ld a, e_hp(ix) ;Cargamos la vida
    dec a 
    ld e_hp(ix), a ;Actualizamos la variable

    cp #0
    jp nz, vivo ;Si no es cero, estoy vivo
        ;Si vale 0, me han matado y destruyo ix, es decir, el player

        ;; Musica gameover
        ld de, #_gameover1
        call cpct_akp_musicInit_asm

        call entity_set4destruction

        ld a, #1
        ld (bool_game_over), a

        pop iy
        pop ix 
        ret
    vivo:
    ld e_invencibilidad(ix), #1
    call cambia_variables_IX_IY ;Metemos en ix el enemigo y en iy el player

   
    ld a, e_hp(ix) ;Cargamos la vida
    cp #1
    jp nz, vivoEnemy ;Si se muere de un golpe, eliminamos
        call entity_set4destruction ;Borramos ix, es decir, el enemigo
        call incrementa_cont_bajas_enemigos ;; enemigos_muertos ++
        jr fin_funcion_colision
    vivoEnemy:
        ;; QUITAMOS UNA VIDA AL ENEMIGO (boss final)
        ;dec a 
        ;ld e_hp(ix), a ;Actualizamos la variable
        ;ld e_invencibilidad(ix), #1
    fin_funcion_colision:

    pop iy
    pop ix
ret


shotArcher_player:

    push ix
    push iy

    ld a, #0x01
    ld (bool_muerte), a

    ld a, e_hp(ix) ;Cargamos la vida
    dec a 
    ld e_hp(ix), a ;Actualizamos la variable

    cp #0
    jp nz, vivo2 ;Si no es cero, estoy vivo
        ;Si vale 0, me han matado y destruyo ix, es decir, el player
        call entity_set4destruction

        ld a, #1
        ld (bool_game_over), a
        pop iy
        pop ix 
        ret

    vivo2:
    call cambia_variables_IX_IY ;Metemos en ix la bala y en iy el player

    ;; Le ponemos player_shot=1 a su arquero
    call suma_player_shot_al_que_dispare
   
    call entity_set4destruction ;Borramos ix, es decir, el enemigo

    ;; No aumentamos enemigos_muertos++ (porque choca con la bala)

    salgo_decrementa:
    pop iy
    pop ix
ret


cambia_variables_IX_IY:
    ld (registro_hl), ix
    ld (registro_aux), iy
    ld ix, (registro_aux)
    ld iy, (registro_hl)
ret


;; Colision Player arcoiris
player_arcoiris:

    ld a, #0x01
    ld (bool_arcoiris), a

    push ix

    call cambia_variables_IX_IY
    call entity_set4destruction

    ;; Creamos 4 disparos
    ld de, #shot1

    ld a, e_y(ix)   ;; /
    add #0x08       ;; | La Y del disparo será, X_arcoiris + (Height_arcoiris / 2) 
    ld b, a         ;; \    

    ld a, e_x(ix)   ;; / La X del disparo será, X_arcoiris + (Width_arcoiris / 2) 
    add #0x03       ;; \

    ld c, #1        ;; Velocidad en X del primer disparo
    ld h, #0        ;; Velocidad en Y del primer disparo

    ld l, #4        ;; Numero de entidades a crear (4)
    call entity_set4creation

    pop ix
    
ret


;; Colision Player Vida
player_vida:

    ld a, #0x01
    ld (bool_vida), a

    push ix

    call cambia_variables_IX_IY ; IX => Vida  y   IY => Player
    call entity_set4destruction ; Borramos vida
    ld a, e_hp(iy)
    cp #9
    jr z, maximo_alcanzado
        inc e_hp(iy) ;Incrementa la vida del player
    maximo_alcanzado:

    pop ix
    
ret

suma_player_shot_al_que_dispare:
    push ix
    ld l, e_perteneceL(ix)
    ld h, e_perteneceH(ix)
    ld (registro_aux), hl
    ld ix, (registro_aux)
    ;; Tenemos en IX el arquero
    ld e_player_shot(ix), #0x00
    pop ix
ret

shot_zombie:

    ; Comprobamos si IX=Zombie y IY=disparo
    ld a, e_tipo(ix)
    cp #e_type_zombie
    jr z, no_cambies_variables_IX_IY
        ;; Si no es zombie
        call cambia_variables_IX_IY
    no_cambies_variables_IX_IY:

    ;; IX => ZOMBIE
    ;; IY => DISPARO

    ld a, #0x01
    ld (bool_muerte), a

    ld a, e_hp(ix)
    cp #1
    jp nz, resta_vida_zombie
        ;Si al restar la vida al zombie, esta muerto
        call entity_set4destruction
        push ix
        push iy
        call incrementa_cont_bajas_enemigos ;; enemigos_muertos ++
        pop iy
        pop ix
        call dropObject
        jr zombie_vivo

    resta_vida_zombie:
        dec a
        ld e_hp(ix), a
        ld e_invencibilidad(ix), #1

    zombie_vivo:
    ;Si el zombie sigue vivo
    call cambia_variables_IX_IY
    call entity_set4destruction
    call suma_player_shot_al_que_dispare


ret


dropObject:
    ;; Creamos un arcoiris o vida con probabilidad
    call cpct_getRandom_mxor_u8_asm ;; Destroys AF, BC, DE, HL
    ld a, l
  
    push af
    cp #0x0A
    jp nc, no_creamos_arco
        ;; a = (0, 10) --> creamos arcoiris
        ld de, #arcoiris
        call game_create_template ;; IX => Arcoiris
        ld a, e_x(iy)
        dec a
        dec a
        ld e_x(ix), a

        ld a, e_y(iy)
        dec a
        dec a
        dec a
        dec a
        ld e_y(ix), a

        ;ld a, #0x08
        ;ld e_x(ix), a
        ;ld a, #0x94
        ;ld e_y(ix), a

        ;; Actualizamos las posiciones del arcoiris (las ponemos a las del enemigo)
         
    no_creamos_arco:

    pop af
    cp #0xF7
    jp c, no_creamos_vida
        ;; a = (0, 10) --> creamos vida
         ld de, #vida
        call game_create_template ;; IX => Vida
        ld a, e_x(iy)
        ld e_x(ix), a
        ld a, e_y(iy)
        ld e_y(ix), a
        ;; Actualizamos las posiciones de la vida (las ponemos a las del enemigo)
        
    no_creamos_vida:
ret

;; Tabla de colisiones
colisiones:
    .db e_type_zombie, e_type_player   ;; OJO IX=player IY=Zombie (player siempre va antes en el bucle de la funcion ForAllPairs) 
        .dw enemy_player

    .db e_type_arcoiris, e_type_player ;; OJO IX=player IY=Arcoiris (player siempre va antes en el bucle de la funcion ForAllPairs) 
        .dw player_arcoiris

    .db e_type_shot, e_type_zombie
        .dw shot_zombie

    .db e_type_zombie, e_type_shot
        .dw shot_zombie

    .db e_type_vida, e_type_player 
        .dw player_vida

    .db e_type_shot, e_type_player
        .dw shotArcher_player

    .dw 0

;; ======================================
;; COLISION ENTRE DOS ENTIDADES
;; Input: IX => Entidad Colisionable n1
;;        IY => Entidad Colisionable n2
;; ======================================
sys_collision_update_one_entity:
    
    ;; Comprobamos si E1 colisiona con E2 o al reves
    ld a, e_collidesAgainst(ix)
    and e_tipo(iy)
    ld b, a         ;; B = e1_col_e2

    ld a, e_collidesAgainst(iy)
    and e_tipo(ix)  ; A = e2_col_e1

    or b            ;; e1_col_e2 | e2_col_e1
    cp #0
    ret z  
        

    ;; if (obs_x + obs_w <= player_x) --> no collision
    ;; if (obs_x + obs_w - player_x <= 0
    ld  a, e_x(iy)      ;; a = obs_X
    add e_w(iy)         ;; a = obs_X + obs_W
    sub e_x(ix)         ;; a = obs_X + obs_W - player_x
    sub #0x01
    jp m, no_collision  ;; salto si es menor que cero
    jp z, no_collision  ;; salto si es = 0 ( no modifica flags )


    ;; if (obsY + obsH <= PlayerY) --> no collision
    ;; if (obsY + obsH - PlayerY <= 0) --> no collision
    ld a, e_y(iy)
    add e_h(iy)
    sub e_y(ix)
    sub #0x04
    jp m, no_collision  ;; salto si es menor que cero
    jr z, no_collision  ;; salto si es = 0 ( no modifica flags )


    ; if (playerX + playerW <= obs_x) --> no collision
    ; playerX + playerW - obsX <= 0
    ld a, e_x(ix)           ; a = playerX
    add a, e_w(ix)          ; a = playerX +  playeW
    sub e_x(iy)
    sub #0x01
    jp m, no_collision
    jr z, no_collision


    ; if (playerY + playerH <= obs_y) --> no collision
    ; playerY + playerH - obs_y <= 0
    ld a, e_y(ix)
    add a, e_h(ix)
    sub e_y(iy)
    sub #0x04
    jp m, no_collision
    jr z, no_collision


    ;; Si IX == IY no hago nada
    ld (registro_aux), iy
    ld (registro_hl), ix
    ld a, (#registro_aux-#registro_hl)
    cp #0
    ret z

    ;; Si alguna de las 2 entidades en invencible (acaba de spawnear), no habrá colisiones entre ellas
    ld a, e_invencibilidad(ix)
    cp #1
    ret z

    ld a, e_invencibilidad(iy)
    cp #1
    ret z

    ;; Collision
    ld hl, #colisiones-4

    recorrer_tabla:

        ;; HL += 4 (direccion de memoria)
        ld bc, #4
        add hl, bc

        ; registro_hl => HL (direccion de memoria de TablaColisiones[i])
        ld (registro_hl), hl

        ; DE => (el valor de ColisionTabla[i])
        push ix
        ld ix, (registro_hl)
        ld d, 0(ix)
        ld e, 1(ix)
        pop ix 

    
        ;; Comprobamos si hemos llegado al final de la tabla de colisiones (con el valor)
        ld a, d     ; A = L
        or e        ; A = L | H 
        ret z       ; if (key=NULL) ret
        
        ; Si todavia queda por mirar alguna fila,
        ld a, e_tipo(ix)    ;; / if( e_tipo(ix) == D )
        cp e                ;; \
        jr nz, recorrer_tabla

        ;; e_tipo(ix) = D --> Ahora comprobamos si ¿e_tipo(iy) == E?
        ld a, e_tipo(iy)    ;; / if( e_tipo(iy) == E )
        cp d                ;; \
        jr nz, recorrer_tabla


        ;; Mis 2 entidades de IX e IY coinciden con la fila de la tabla de colisiones
        ;; Ahora ejecutamos la funcion asociada
        ;; El puntero HL debe apuntar a la direccion de memoria siguiente (+= 2)
        ;inc hl
        ;inc hl
        ld bc, #2
        add hl, bc

        ;; HL => PtrFuncion, pero queremos tener en HL SOLO la funcion (no el puntero). Entonces...
        push ix
        ld (registro_hl), hl
        ld ix, (registro_hl)    ; IX => HL
        ld l, 0(ix)             ; / HL => Funcion
        ld h, 1(ix)             ; \
        pop ix                  ; IX => Entidad1 (como antes)

        ld de, #funcion_colision_ya_ejecutada   ;; /  Meto en la pila la dirección a la que quiero que vuelva despues de que
        push de  

        jp (hl)     ;; Saltamps a la funcion
     
    ret

    no_collision:
     
    
    funcion_colision_ya_ejecutada:
ret


sys_collision_update:
    ld hl, #sys_collision_update_one_entity
    ld b, #e_cmp_collider
    call entity_doForAll_pairs_matching
ret 

