;;-----------------------------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
.include "utils.h.s"
.include "macros.h.s"
.include "actors/enemy.h.s"
.include "actors/shoot.h.s"
.include "actors/object.h.s"
.include "actors/character.h.s"
.include "sprites/sprites.h.s"
.include "stage.h.s"
.include "dobleBuffer.h.s"
.include "cpctelera.h.s"


number_matriz: .db #0
width_tiles: .db #0
height_tile: .db 0

;

shootsArray::   .db #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #1	;;Array de 10 disparos (DE 11 BYTES CADA UNO) (CUIDADO SI SE CAMBIA EL TAMANIO DE LA ENTIDAD)
	 			.db #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #1
	 			.db #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #1
	 			.db #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #1
	 			.db #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #1
	 			.db #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #1
	 			.db #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #1
	 			;.db #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #1
	 			;.db #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #1
	 			;.db #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #0, #1
	 			.db #0xFF



currentStage:: .dw #level01_data		

objectsArray:: 	


enemiesArray:: 	



contador_update_enemy: .db #2


;;--------------------------------------------------------------------------------------------------------
;;  Rutina para recorrer el array de enemigos y dibujarlos
;;--------------------------------------------------------------------------------------------------------
draw_enemies::

	ld b, #1
	ld c, #2

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


	call DEU_entities
	ret

;;--------------------------------------------------------------------------------------------------------
;;  Rutina para recorrer el array de disparos y dibujarlos
;;--------------------------------------------------------------------------------------------------------
draw_shoots::
	ld b, #1
	ld c, #1
	ld ix, #shootsArray
	call DEU_entities
	ret


;;--------------------------------------------------------------------------------------------------------
;;  Rutina para recorrer el array de objetos y dibujarlos
;;--------------------------------------------------------------------------------------------------------
draw_objects::
	ld b, #1
	ld c, #3

	ld ix, (currentStage)
	ld h, objects_h(ix)
	ld l, objects_l(ix)
	push hl
	pop ix

	call DEU_entities
	ret


;;--------------------------------------------------------------------------------------------------------
;;  Rutina para recorrer el array de enemigos y actualizarlos
;;--------------------------------------------------------------------------------------------------------
update_enemies::

	ld a, (contador_update_enemy)
	dec a
	ld (contador_update_enemy), a
	ret nz


	ld a, #2
	ld (contador_update_enemy), a

	ld b, #3
	ld c, #2

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

	call DEU_entities
	ret

;;--------------------------------------------------------------------------------------------------------
;;  Rutina para recorrer el array de disparos y actualizarlos
;;--------------------------------------------------------------------------------------------------------
update_shoots::
	ld b, #3
	ld c, #1
	ld ix, #shootsArray
	call DEU_entities
	ret


;;--------------------------------------------------------------------------------------------------------
;;  Rutina para recorrer el array de disparos y actualizarlos
;;--------------------------------------------------------------------------------------------------------
update_objects::
	ld b, #3
	ld c, #3

	ld ix, (currentStage)
	ld h, objects_h(ix)
	ld l, objects_l(ix)
	push hl
	pop ix

	call DEU_entities
	ret

;;--------------------------------------------------------------------------------------------------------
;;  Rutina para recorrer el array de enemigos y borrarlos (a nivel grafico, no de dato)
;;--------------------------------------------------------------------------------------------------------
erase_enemies::
	ld b, #2
	ld c, #2
	
	ld ix, (currentStage)
	ld h, enemies_h(ix)
	ld l, enemies_l(ix)
	push hl
	pop ix

	call DEU_entities
	ret

;;--------------------------------------------------------------------------------------------------------
;;  Rutina para recorrer el array de disparos y borrarlos (a nivel grafico, no de dato)
;;--------------------------------------------------------------------------------------------------------
erase_shoots::
	ld b, #2
	ld c, #1
	ld ix, #shootsArray
	call DEU_entities
	ret





;(NO TESTADA)(NO TESTADA)(NO TESTADA)(NO TESTADA)
;; DEU = draw/erase/update
;;-----------------------------------------------------------------------
;; Rutina para pintar, borrar, o actualizar las entidades dado un array 
;; -> IX es el puntero al array de entidades
;; -> B  (1 = PINTAR, 2 = BORRAR, 3 = ACTUALIZAR)
;; -> C	 (1 = DISPARO, 2 = ENEMIGO, 3 = OBJETO)
;;-----------------------------------------------------------------------
DEU_entities:
	push bc 									;;Guardamos en la pila los argumentos, para no perderlos

	_loop_entities:
		ld a, x(ix)								;; A = atributo X de la entidad a la que apunte IX		
		cp #0xFF								;; Si A = FF significa que estamos al final del array y debemos salir de la rutina
		jp z, finish_loop_entities				;; |			
		cp #0									;; Si A = 0 signfica que la entidad no existe y pasamos a la siguiente
		jp nz, DEU_entidad						;; |

		continua_DEU_entidades:
			pop bc								;; Recuperamos el valor de los argumentos
			push bc								;; Lo volvemos a meter en la pila para no perderlo. Ahora lo tenemos en BC
			ld a, c								;; A = C (1 = DISPARO, 2 = ENEMIGO)
			cp #1								;; SI A = 1 significa que la entidad es un disparo
			jp nz, noEsDisparo
				;;Es disparo 

				ld bc, (shoot_size_in_bytes)	;; Si la entidad es un disparo debemos saltar en el array tantos bytes como bytes mida un disparo
				jp incremento					;; Pasamos a incrementar el valor de IX al siguiente indice
			noEsDisparo:
			cp #2
			jp nz, noEsEnemigo
				;;Es enemigo
				ld bc, (enemy_size_in_bytes)	;; Si la entidad es un enemigo debemos saltar en el array tanto bytes como bytes mida un enemigo
				jp incremento
			noEsEnemigo:
				;;Es objeto
				ld bc, (object_size_in_bytes)
			incremento:
				ld b, #00
				add ix, bc						;; Sumamos a IX lo que mida la entidad en bytes				
				jp _loop_entities				;; Volvemos al principio del bucle

		DEU_entidad:
			pop bc								;; Recuperamos el valor de los argumentos
			push bc								;; Lo volvemos a meter en la pila para no perderlo. Ahora lo tenemos en BC
			ld a, b								;; A = B (1 = PINTAR, 2 = BORRAR, 3 = ACTUALIZAR)
			
			cp #1								;; SI A = 1, pintamos la entidad
			jp nz, borrado
				ld a, c			;; Comprobamos si es un enemigo
				cp #2			;; |
				jp nz, no_enemy	        ;; Si no es un enemigo, dibujamos la entidad y continuamos con la siguente
				ld a, vivo_muerto(ix)	;; Si el enemigo esta muerto, no se dibuja
				cp #0			;; |
				jp z, continua_DEU_entidades ;; |
				no_enemy: ;; Si no es un enemigo o el enemigo esta vivo, se dibuja y se pasa a la siguiente entidad 
				call entity_draw				;; |
				jp continua_DEU_entidades		;; |
			
			borrado:
			cp #2								;; SI A = 2, borramos la entidad
			jp nz, actualizado					;; |
				call entity_erase				;; |
				jp continua_DEU_entidades		;; |


			actualizado::						;; SI A no es ni 1 ni 2, significa que hay que actualizar la entidad
			ld a, c								;; Cargamos en A el valor del otro argumento (C)
			cp #1								;; SI A = 1, debemos actualizar un disparo
			jp z, actualiza_disparo				;; |
			cp #2								;; SI A = 2, debemos actualizar un enemigo
			jp z, actualiza_enemigo				;; |
			cp #3								;; SI A = 3, debemos actualizar un objeto
			jp z, actualiza_objeto				;; |
			jp continua_DEU_entidades			;; Ahora vamos a pasar al siguiente indice del array
			

			actualiza_disparo:
				call shoot_update
				jp continua_DEU_entidades
			actualiza_enemigo:
				call enemy_doAction
				call entity_state
				jp continua_DEU_entidades
			actualiza_objeto:
				call object_update
				jp  continua_DEU_entidades
		ret


		finish_loop_entities:
			pop bc
			ret



;;-----------------------------------------------------------------------
;; Intenta spawnear un nuevo disparo, si hay espacio en el array
;; -> IX apunta a la entidad que ha spawneado el disparo
;;-----------------------------------------------------------------------
try_spawn_shoot::
	ld iy, #shootsArray
	ld bc, (shoot_size_in_bytes)
	ld b, #00
	loop_spawn_shoot:
		ld a, x(iy)
		cp #0xFF
		ret z
	
		cp #0
		jp z, spawnea_disparo
	
		
		add iy, bc
		jp loop_spawn_shoot	
	
		spawnea_disparo:
			call shoot_spawn
			ret
	

;;-----------------------------------------------------------------------
;; Establece como Stage actual el que se pase por el registro IX
;; -> IX apunta al comienzo de datos del Stage que se quiere cargar
;;-----------------------------------------------------------------------
stage_load::
	ld (currentStage), ix
	ret


;;-----------------------------------------------------------------------
;; Comprueba si el jugador esta en alguna de las posiciones que disparan el salto de mapa. En caso afirmativo, cambia de mapa
;; 
;;-----------------------------------------------------------------------
stage_check_exits::
	call character_getPtrIX
	ld iy, (currentStage)
	ld a, x(ix)
	cp #1
	jp z, cargaMapaIzquierda
	cp #0x4B
	jp z, cargaMapaDerecha

	ld a, y(ix)
	cp #0
	jp z, cargaMapaArriba
	cp #0xA2
	jp z, cargaMapaAbajo
	ret


	cargaMapaIzquierda:
		ld a, #0x4A
		ld x(ix), a
		ld h, left_h(iy)
		ld l, left_l(iy)
		jp inyectaPunteroMapa

	cargaMapaDerecha:
		ld a, #5
		ld x(ix), a
		ld ix, (currentStage)
		ld h, right_h(iy)
		ld l, right_l(iy)
		jp inyectaPunteroMapa

	cargaMapaArriba:
		ld a, #0x9A
		ld y(ix), a
		ld h, up_h(iy)
		ld l, up_l(iy)
		jp inyectaPunteroMapa

	cargaMapaAbajo:
		ld a, #2
		ld y(ix), a
		ld h, down_h(iy)
		ld l, down_l(iy)
		
	inyectaPunteroMapa::
		
		ld (currentStage), hl
		ld ix, (currentStage)

		ld h, tileset_h(ix)
		ld l, tileset_l(ix)
    		call cpct_etm_setTileset2x4_asm

		ld h, tilemap_h(ix)
		ld l, tilemap_l(ix)
		call descomprimir_tilemap

		call check_door

		call background_paint             ;;Se pinta el tilemap en C000
    	call switchBuffers	  	      ;; Se cambian los buffers
    	call background_paint	      ;; Se pinta el tilemap en 8000
    	call switchBuffers		      ;; Se vuelven a cambiar los buffers para que comience el juego

    	call clear_shoots

		ret

check_door::
	ld ix, (currentStage)
	ld h, objects_h(ix)
	ld l, objects_l(ix)
	ld b, #0
	ld c, #6
	add hl, bc
	ld a, (hl)
	cp #5
	jr nz, not_door

	ld h, objects_h(ix)
	ld l, objects_l(ix)
	ld a, (hl)
	cp #0
	call nz, close_door
	not_door:
ret

clear_shoots::
	ld hl, #shootsArray
	ld bc, (shoot_size_in_bytes)
	ld b, #0

	__bucle_borrado:
		ld a, (hl)
		cp #0
		jp z, saltaSiguienteIndice
		cp #0xFF
		ret z
		ld a, #0
		ld (hl), a

		saltaSiguienteIndice:
			add hl, bc
			jp __bucle_borrado
	ret

open_door::
	ld a, #0
	ld (number_matriz), a
	call door_collision
ret

close_door::
	ld a, #3
	ld (number_matriz), a
	call door_collision
ret

door_collision::
	ld ix, (currentStage)
	ld h, objects_h(ix)
	ld l, objects_l(ix)
	push hl
	pop ix
	ld a, x(ix)
	cp #0
	ret z
	ld a, state(ix)
	cp #5
	ret nz
	ld d, x(ix)
	srl d
	ld e, y(ix)
	srl e 
	srl e


	ld hl, #0x0041			;;HL apunta a la matriz de tiles
	ld b, #0			;; Cargamos en BC la posicion Tile_X de la entidad
	ld c, d 			;; |
	add hl, bc			;; Se la sumamos a HL (HL + Tile_x)
	ld b, #0			;; Cargamos el ancho (40) de la matriz en bc
	ld c, #40			;; |
	ld a, e				;; A = Tile_Y
	cp #0
	jp z, endMult
	_loop_tile2:
					;; Bucle de multiplicacion
		add hl, bc			;; Hl = HL + 40 (el ancho)
		dec a				;; Decrementamos A para seguir con el bucle de la multiplicacion
		jp nz, _loop_tile2	;; Si A todavia no es cero, la multiplicacion no ha terminado

	endMult:
	ld a, (number_matriz)
	add hl, bc
	add hl, bc
	inc hl
	ld (hl), a
	inc hl
	ld (hl), a
	add hl, bc
	ld (hl), a
	dec hl
	ld (hl), a
	ret

	
