;;-----------------------------LICENSE NOTICE------------------------------------
;;  This file is part of CPCtelera: An Amstrad CPC Game Engine 
;;  Copyright (C) 2021 Daniel José García Evans, Iván Alejandro Ontano Herrera,
;;  Sergio Izquierdo Pintado (PyroClastic Software)
;;
;;  Special Thanks to Ennio Fernando Velásquez Berlingeri (PyroBombastic)
;;
;;  This program 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.
;;
;;  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

;;
;; ENTITY MANAGER
;;

.include "./globals.h.s"
;;.include "man_entity.h.s"
;;.include "systems/sys_render.h.s"

counter: .db #0
function_ptr: .dw #0x0000   ;;puntero a la funcion a ejecutar, ya sea render o physics
counter_iy: .db #0
function_ptr_iy: .dw #0x0000

lives:: .db #0x00 ;;da un poco igual el valor que tengan aqui
score:: .dw #0x0000
current_map:: .db #0

enemy_timer_max:: .db #0x00 ;;TEMPORIZADOR DE MOVIMIENTO DE ENEMIGOS
spawn_counter:: .db #0x00

prob_ant:: .db #0x00
prob_mosquito:: .db #0x00


;;tener en la primera posicion al heroe, que no este la espada como entidad, no tiene sentido, y que luego el resto del array sea de enemigos
hero_ptr:: .dw entity_array
loop_elem_ptr:: .dw entity_array
loop_elem_ptr_iy:: .dw entity_array
entity_array:: .ds max_entities * e_size

man_entity_first::   ;;hace que el loop_elem_ptr apunte al principio, evita muchos fallos
    ld hl, #entity_array
    ld (loop_elem_ptr), hl
    ld ix, (loop_elem_ptr)

    ret 

man_entity_first_iy::   ;;hace que el loop_elem_ptr apunte al principio, evita muchos fallos
    ld hl, #entity_array
    ld (loop_elem_ptr_iy), hl
    ld iy, (loop_elem_ptr_iy)

    ret 

;;edita HL y BC
man_entity_next::   ;;mueve el loop_elem_ptr a la siguiente posicion
    ld hl, (loop_elem_ptr)
    ld bc, #e_size
    add hl, bc
    ld (loop_elem_ptr), hl
    ld ix, (loop_elem_ptr)
    
    ret

man_entity_next_iy::   ;;mueve el loop_elem_ptr a la siguiente posicion
    ld hl, (loop_elem_ptr_iy)
    ld bc, #e_size
    add hl, bc
    ld (loop_elem_ptr_iy), hl
    ld iy, (loop_elem_ptr_iy)

    ret

man_entity_create_from_tmpl:   ;;tiene en hl el template
    ld de, (loop_elem_ptr)  ;;cargo en DE el puntero de la entidad actual para meter valores luego con ldir 
    ld bc, #e_size
    ldir
    ld ix, (loop_elem_ptr)

    ret 

;;cuando empiezo la partida creo:
;;  pinto el mapa
;;  pinto la interfaz
;;  todas las entidades (heroe, espada, nubes, enemigos) 
;;  llamo al render para todas las entidades

;;cuando reinicio la partida:   ;;lo mismo que lo anterior pero reiniciando variables, combinar ambas en un solo metodo
;;  reseteo las variables de la partida
;;  vuelvo a pintar el primer mapa
;;  pinto la interfaz
;;  todas las entidades
;;  llamo al render para todas las entidades

;;cuando paso al siguiente mapa:
;;  pinto el siguiente mapa
;;  pinto la interfaz
;;  todas las entidades
;;  llamo al render para todas las entidades

;;dibujo el mapa 0
;;cuando llega a 100 puntos se comprueba si el current_map es mayor o igual al que deberia ser al cambiar de mapa, si es asi no hace nada

man_entity_reset_variables::
    ;;se reinician las variables
    xor a
    ;;ld a, #1    ;;#####para cambiar el mapa artificialmente
    ld (current_map), a
    
    ld a, #5
    ld (lives), a

    ld hl, #0
    ld (score), hl

    ld a, #25
    ld (enemy_timer_max), a

    ld a, #100
    ld (spawn_counter), a

    ld a, #100
    ld (prob_ant), a

    ;;ld a, #80
    ;;ld (prob_mosquito), a
ret

;;call sys_render_init  ;;esto carga el mapa almacenado en current map

man_entity_init::
    ;;se eliminan todas las entidades
    ld hl, #man_entity_delete_all_entities
    ld b, #(e_cmps_alive)
    call man_entity_for_all_matching

    call man_entity_first
    ld hl, #tmpl_hero
    call man_entity_create_from_tmpl ;;puntero a entidad en DE y tamaño en BC
    
    call man_entity_next
    ld hl, #tmpl_sword
    call man_entity_create_from_tmpl

    ld a, (current_map)
    and a
    jp z, first_map_clouds
        dec a
        jp z, second_map_clouds
            dec a
            jp z, third_map_clouds
                xor a
                ld (current_map), a ;;si por algun motivo no es ningun mapa
                jp first_map_clouds    ;;se usa el primero

            third_map_clouds:   ;;game development
                call man_entity_next
                ld hl, #tmpl_plat_left
                call man_entity_create_from_tmpl
                ld e_x(ix), #26
                ld e_y(ix), #136
                ld e_w(ix), #8
                ld e_sprite(ix), #<_plat_char3_0
                ld e_sprite + 1(ix), #>_plat_char3_0

                ;;#####esto es para que todos los mapas tengan el mismo numero de entidades y rendimiento
                call man_entity_next
                ld hl, #tmpl_plat_left
                call man_entity_create_from_tmpl
                ld e_x(ix), #26
                ld e_y(ix), #136
                ld e_w(ix), #8
                ld e_sprite(ix), #<_plat_char3_0
                ld e_sprite + 1(ix), #>_plat_char3_0
                
                ;;call man_entity_next
                ;;ld hl, #tmpl_plat_central
                ;;call man_entity_create_from_tmpl
                ;;ld e_y(ix), #88
                ;;ld e_w(ix), #8  ;;2 de ancho
                ;;ld e_sprite(ix), #<_plat_char_6
                ;;ld e_sprite + 1(ix), #>_plat_char_6

                call man_entity_next
                ld hl, #tmpl_plat_right
                call man_entity_create_from_tmpl
                ld e_x(ix), #46
                ld e_y(ix), #88
                ld e_w(ix), #8
                ld e_sprite(ix), #<_plat_char3_1
                ld e_sprite + 1(ix), #>_plat_char3_1

            ret

        second_map_clouds:  ;;sugar
                call man_entity_next
                ld hl, #tmpl_plat_left
                call man_entity_create_from_tmpl
                ld e_x(ix), #24 ;;#16 
                ld e_w(ix), #12 ;;3 de ancho
                ld e_sprite(ix), #<_plat_char2_0
                ld e_sprite + 1(ix), #>_plat_char2_0
                
                ;;#####esto es para que todos los mapas tengan el mismo numero de entidades y rendimiento
                call man_entity_next
                ld hl, #tmpl_plat_left
                call man_entity_create_from_tmpl
                ld e_x(ix), #24 ;;#16 
                ld e_w(ix), #12 ;;3 de ancho
                ld e_sprite(ix), #<_plat_char2_0
                ld e_sprite + 1(ix), #>_plat_char2_0

                ;;call man_entity_next
                ;;ld hl, #tmpl_plat_central
                ;;call man_entity_create_from_tmpl
                ;;ld e_x(ix), #44 ;;#16 
                ;;ld e_w(ix), #12 ;;3 de ancho
                ;;ld e_sprite(ix), #<_plat_char_5
                ;;ld e_sprite + 1(ix), #>_plat_char_5

                call man_entity_next
                ld hl, #tmpl_plat_right
                call man_entity_create_from_tmpl
                ld e_x(ix), #44 ;;#48
                ld e_y(ix), #136
                ld e_w(ix), #12 ;;3 de ancho
                ld e_sprite(ix), #<_plat_char2_1
                ld e_sprite + 1(ix), #>_plat_char2_1

            ret

    first_map_clouds:   ;;blue + yellow hills
        call man_entity_next
        ld hl, #tmpl_plat_left
        call man_entity_create_from_tmpl

        call man_entity_next
        ld hl, #tmpl_plat_central
        call man_entity_create_from_tmpl

        call man_entity_next
        ld hl, #tmpl_plat_right
        call man_entity_create_from_tmpl
    ;;baobap_clouds:

    ret

;;man_entity_create_all_enemies::
;;    call man_entity_next
;;    ld hl, #tmpl_ant
;;    call man_entity_create_from_tmpl
;;
;;    call man_entity_next
;;    ld hl, #tmpl_mosquito
;;    call man_entity_create_from_tmpl
;;
;;    ;;para que el duende no aparezca al principio
;;    ;;call man_entity_next
;;    ;;ld hl, #tmpl_goblin
;;    ;;call man_entity_create_from_tmpl
;;
;;    ret

man_entity_delete_all_entities:
    ;;mismo codigo que cuando mato a un enemigo con la espada
    ld a, e_cmps(ix)            ;;cargo las mascaras del enemigo
    and #e_cmps_dead                   ;;pongo un 0 en la mascara alive del enemigo
    ld e_cmps(ix), a            ;;actualizo las mascaras del enemigo
    ;;luego el render se asegurara de borrar las entidades que esten muertas

    ret

man_entity_spawn_enemy:
    ld a, (spawn_counter) ;;Guardo en A el valor actual del contador
    and a
    jr z, retry_template          ;;Si el contador vale 0, saltamos a crear un enemigo al azar
        ;;Si no vale 0 el contador
        dec a                   ;;Decrementamos
        ld (spawn_counter), a   ;;Actualizamos el contador
        ret 

    ;;spawn_counter_ok:

    retry_template:
    call cpct_getRandom_xsp40_u8_asm
    cp #100
    jp nc, retry_template   ;;si no da un numero de 0 a 99 se vuelve a sacar

    ;;50% hormiga, 30% mosquito, 20% hijo de su putisima madre
    ld bc, (prob_ant)
    cp c
    jr c, ant_template
        ld bc, (prob_mosquito)
        cp c  ;;50 + 30
        jr c, mosquito_template
            ;;cp #100   ;;50 + 30 + 20
            ;;jr c, goblin_template
            ;;goblin template:
            ld hl, #tmpl_goblin
            call create_random_enemy
            ret

        mosquito_template:
        ld hl, #tmpl_mosquito
        call create_random_enemy
        retry_y:
        call cpct_getRandom_xsp40_u8_asm
        cp #4   ;;si es mayor igual que 4 se repite
        jr nc, retry_y
        and a
        jr z, lowest    ;;si era 0
            dec a
            jr z, lower     ;;si era 1
                dec a
                jr z, higher    ;;si era 2
                    ;;highest       ;;si era 3
                    ld e_y(ix), #56
                    ret
                higher:
                ld e_y(ix), #72
                ret
            lower:
            ld e_y(ix), #104
            ret
        lowest:
        ld e_y(ix), #120
        ret

    ant_template:
    ld hl, #tmpl_ant
    call create_random_enemy

    ret

create_random_enemy:
    ;;SI NO APARECE EN UN BORDE LA IA NO HACE QUE SE MUEVAN CUIDADO
    ;;ld hl, #tmpl_goblin
    call man_entity_create_from_tmpl
    retry_x:
    call cpct_getRandom_xsp40_u8_asm
    cp #2   ;;si es mayor igual que 2 se repite
    jr nc, retry_x
    and a
    jr z, spawn_left
        ;;spawn_right
        ld a, #80
        sub e_w(ix)
        ld e_x(ix), a
        jr spawn_counter_reset

    spawn_left:
        ld e_x(ix), #0

    spawn_counter_reset:
    ld a, #100
    ld (spawn_counter), a       ;;reiniciamos el contador siempre que se cree un enemigo

    ret



man_entity_update::
    call man_entity_first   ;;se apunta al principio del array
    ld a, #max_entities

    ;;compruebo si no es invalida
        ;;compruebo no esta viva
            ;;compruebo si no tiene render
            ;;comprueo si no es un enemigo
    ;;si es invalida o enemigo muerto llamo a spawn

    loop_entity_for_all:
        ld ix, (loop_elem_ptr)
        ld (counter), a ;;actualizo el contador al principio del bucle

        ld a, e_cmps(ix)    ;;cargo la mascara de la entidad
        ld b, a             ;;la guardo en B por eficiencia
        and a
        jr z, spawn_enemy  ;;si es una entidad no valida (sin crear)

            ld a, b    ;;cargo la mascara de la entidad
            and #(e_cmps_alive)
            jr nz, next_entity  ;;si no esta muerta

            ;;si esta muerta
                ld a, b    ;;cargo la mascara de la entidad
                and #(e_cmps_render)
                jr z, check_dead_enemy ;;si aun tiene la mascara de render se borra la entidad y se pone a 0 la mascara de render

                ld a, b    ;;cargo la mascara de la entidad
                and a, #0x3F    ;;pongo a 0 la mascara de alive(otra vez) y render
                ld e_cmps(ix), a

                call sys_render_clear_prev_pos_entity   ;;borro el rastro ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
                call sys_render_clear_pos_entity        ;;borro la posicion actual    ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,

                check_dead_enemy:
            ld a, e_cmps(ix)    ;;aqui no puedo utilizar b por si se modifica en el render
                and #(e_cmps_enemy) ;;si es un enemigo muerto se llama a spawn
                jr z, next_entity
                    ;;jr spawn_enemy


        spawn_enemy:
        call man_entity_spawn_enemy
        ;;jr next_entity

        next_entity:
                
        call man_entity_next
        ld a, (counter)
        dec a   ;;max_entities --

        jr nz, loop_entity_for_all

    ret
    
;;entrada
;;recibe en HL el puntero a funcion y en B la mascara de entidad
man_entity_for_all_matching::
    ld (function_ptr), hl   ;;guardo el puntero a funcion en la etiqueta
    call man_entity_first   ;;se apunta al principio del array
    ld a, #max_entities

    loop_entity_for_all_matching:
        push bc
        ;;ld ix, (loop_elem_ptr)
        ld (counter), a ;;actualizo el contador al principio del bucle
        
        ld a, e_cmps(ix)
        and b
        cp b
        jr nz, return_after_sys_function_matching ;;si la mascara no cumple se salta a la siguiente entidad
            
            ;;;;;;;;;;  call sys_update_entity
            ;;los registros ix, (b), loop_elem_ptr, function_ptr y counter necesito que no se pierdan al llamar a la funcion
            ld hl, #return_after_sys_function_matching    ;;una etiqueta es un numero
            push hl ;;simula hacer un call ya que guarda en la pila el valor de PC, HL en este caso
            ld hl, (function_ptr)   ;;cargo en HL la direccion a la funcion de sys para hacer el salto
            jp(hl)  ;;salto a sys
            ;;;;;;;;;;

        return_after_sys_function_matching:  ;;se pasa a la siguiente entidad, siempre se pasa por aqui
        
        call man_entity_next
        pop bc
        ld a, (counter)
        dec a   ;;max_entities --

        jr nz, loop_entity_for_all_matching
    
    ret

man_entity_update_score::
        ;;score:: .db #0 #0
        ;;h mas significativo, l menos significativo, en este caso por comodidad
        ld hl, (score)        ;;#hl 5999
        xor a
        ld b, a
        ld a, e_points(iy)    ;;#a  01
        ld c, a               ;;#bc 0001
        ld a, l               ;;#a  99
        add c                 ;;#a  9A
        daa                   ;;#a  00
        ld l, a               ;;#hl 5900
        ld a, h               ;;#a  59
        adc b                 ;;#a  5A
        daa                   ;;#a  60
        ld h, a               ;;#hl 6000
        ld (score), hl

        call man_ui_update_score
        ;;;ld a, (score)               ;;cargo la puntuacion actual
        ;;;add e_points(iy)            ;;agrego los puntos del enemigo
        ;;;daa                         ;;convertimos a decimal la suma
        ;;;ld (score), a               ;;actualizo la puntuacion
    ret

man_entity_change_difficulty::
    ;;0 puntos
    ;;ant 100
    ;;timer 25
    ;;spawn 100

    ;;100 puntos
    ;;ant 80
    ;;mosquito 20
    ;;timer 20
    ;;spawn 85

    ;;200 puntos
    ;;ant 60
    ;;mosquito 40
    ;;timer 10
    ;;spawn 70

    ;;300 puntos
    ;;ant 40
    ;;mosquito 40
    ;;goblin 20
    ;;timer 10
    ;;spawn 55

    ;;400 puntos
    ;;ant 20
    ;;mosquito 40
    ;;goblin 40
    ;;timer 5
    ;;spawn 40

    ;;500 puntos
    ;;ant 20
    ;;mosquito 20
    ;;goblin 60
    ;;timer 2
    ;;spawn 25

    ;;+75 puntos    ;;ahora el cambio minimo de dificultad esta en la 100, porque si no solo hace los cambios cuando sea xx75, no cuando es xx00
    ;;ld hl, (score)
    ;;ld a, l ;;me guardo la puntuacion actual por eficiencia
    ;;cp #0x75
    ;;ret c


    ;;+100 puntos
    ld hl, (score)
    ld a, h ;;me guardo la puntuacion actual por eficiencia
    cp #0x01
    ret c
        ld a, #10
        ld (enemy_timer_max), a
        ld a, #90
        ld (spawn_counter), a 
        ld a, #75
        ld (prob_ant), a
        ld a, #100  ;;100 - 75 = 25
        ld (prob_mosquito), a

    ;;+200 puntos
    ld hl, (score)
    ld a, h ;;me guardo la puntuacion actual por eficiencia
    cp #0x02
    ret c
        ld a, #10
        ld (enemy_timer_max), a
        ld a, #90
        ld (spawn_counter), a 
        ld a, #75
        ld (prob_ant), a
        ld a, #100  ;;100 - 75 = 25
        ld (prob_mosquito), a

    ;;+300 puntos
    ld hl, (score)
    ld a, h
    cp #0x03
    ret c
        ld a, #10
        ld (enemy_timer_max), a
        ld a, #75
        ld (spawn_counter), a   ;;los enemigos reaparecen antes
        ld a, #50
        ld (prob_ant), a
        ld a, #100
        ld (prob_mosquito), a
        ;;prob goblin 0

        ;;en el mapa inicial el current esta a 1
        ld a, (current_map)
        cp #2
        jp p, not_second_map  ;;si el current map es mayor o igual que 2 es que ya ha sucedido el cambio de mapa y no se debe hacer nada
            call man_game_next_map
        not_second_map:

    ;;+400 puntos
    ld hl, (score)
    ld a, h
    cp #0x04
    ret c
        ld a, #5
        ld (enemy_timer_max), a
        ld a, #50
        ld (spawn_counter), a   ;;los enemigos reaparecen mas rapido
        ld a, #25
        ld (prob_ant), a
        ld a, #85
        ld (prob_mosquito), a
        ;;prob goblin 5

    ;;+500 puntos
    ld hl, (score)
    ld a, h
    cp #0x05
    ret c
        ld a, #5
        ld (enemy_timer_max), a
        ld a, #50
        ld (spawn_counter), a   ;;los enemigos reaparecen mas rapido
        ld a, #50
        ld (prob_ant), a
        ld a, #85
        ld (prob_mosquito), a
        ;;prob goblin 15

    ;;+600 puntos
    ld hl, (score)
    ld a, h
    cp #0x06
    ret c
        ld a, #5
        ld (enemy_timer_max), a
        ld a, #50
        ld (spawn_counter), a   ;;los enemigos reaparecen mas rapido
        ld a, #60
        ld (prob_ant), a
        ld a, #70
        ld (prob_mosquito), a
        ;;prob goblin 30
        
        ;;en el segundo mapa el current esta a 2
        ld a, (current_map)
        cp #3
        jp p, not_third_map  ;;si el current map es mayor o igual que 3 es que ya ha sucedido el cambio de mapa y no se debe hacer nada
            call man_game_next_map
        not_third_map:

    ;;;;+1000 puntos
    ;;ld hl, (score)
    ;;ld a, h
    ;;cp #0x10
    ;;ret c


    ;;+1500 puntos
    ld hl, (score)
    ld a, h
    cp #0x15
    ret c
        ld a, #4
        ld (enemy_timer_max), a
        ld a, #40
        ld (spawn_counter), a   ;;los enemigos reaparecen mas rapido
        ld a, #25
        ld (prob_ant), a
        ld a, #85
        ld (prob_mosquito), a
        ;;prob goblin 5

    ;;+1700 puntos
    ld hl, (score)
    ld a, h
    cp #0x17
    ret c
        ld a, #4
        ld (enemy_timer_max), a
        ld a, #50
        ld (spawn_counter), a   ;;los enemigos reaparecen mas rapido
        ld a, #50
        ld (prob_ant), a
        ld a, #85
        ld (prob_mosquito), a
        ;;prob goblin 15

    ;;+1900 puntos
    ld hl, (score)
    ld a, h
    cp #0x19
    ret c
        ld a, #4
        ld (enemy_timer_max), a
        ld a, #50
        ld (spawn_counter), a   ;;los enemigos reaparecen mas rapido
        ld a, #60
        ld (prob_ant), a
        ld a, #70
        ld (prob_mosquito), a
        ;;prob goblin 30

    ;;+2100 puntos
    ld hl, (score)
    ld a, h
    cp #0x21
    ret c
        ld a, #2
        ld (enemy_timer_max), a
        ld a, #25
        ld (spawn_counter), a   ;;los enemigos reaparecen mucho mas rapido

    ret
