; ; ; ; MIT License
; ; ; ; 
; ; ; ; Copyright (c) 2021 Ramiro Hernán García Iten / Raúl Ripoll Pérez / Alexandru Marius Cozma
; ; ; ; 
; ; ; ; Permission is hereby granted, free of charge, to any person obtaining a copy
; ; ; ; of this software and associated documentation files (the "Software"), to deal
; ; ; ; in the Software without restriction, including without limitation the rights
; ; ; ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; ; ; ; copies of the Software, and to permit persons to whom the Software is
; ; ; ; furnished to do so, subject to the following conditions:
; ; ; ; 
; ; ; ; The above copyright notice and this permission notice shall be included in all
; ; ; ; copies or substantial portions of the Software.
; ; ; ;
; ; ; ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; ; ; ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; ; ; ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; ; ; ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; ; ; ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; ; ; ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; ; ; ; SOFTWARE.

; ; ; ; ----------------- AUTHORS ------------------
; ; ; ;     Ramiro Hernán García Iten: rhgi1@alu.ua.es
; ; ; ;     Raúl Ripoll Pérez: rrp42@alu.ua.es
; ; ; ;     Alexandru Marius Cozma: amc246@alu.ua.es
; ; ; ; 
; ; ; ; Art by Sara Alfosea - IG: @Slinfir
; ; ; ; ---------------------------------------------

; ; ; ; Third Party source code used
; ; ; ; ----------------------------
; ; ; ; CPCtelera - owned by ronaldo / (Cheesetea, Fremos, ByteRealms) - GNU Lesser General Public License.


;;-----------------------------LICENSE NOTICE------------------------------------
;;  This file is part of CPCtelera: An Amstrad CPC Game Engine 
;;  Copyright (C) 2018 ronaldo / Fremos / Cheesetea / ByteRealms (@FranGallegoBR)
;;
;;  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/>.
;;-------------------------------------------------------------------------------



.include "../macros/cpct_undocumentedOpcodes.h.s"

;;; ==========================================
;;; ==========================================
;;; PHYSICS SYSTEM
;;; ==========================================
;;; ==========================================

; Inicializa el sistema de físicas
sys_physics_init::
   xor   a
   ld    (jump_index), a
   
   ld    a, #1             ; El estado inicial del jugador es cayendo (1)
   ld    (player_jump), a

   ld    a, #windRate            ; Restauramos el contador del viento
   ld    (wind), a               ; a su valor inicial.
   ret

; Actualiza el sistema de físicas
sys_physics_update::
   call  man_entity_getArray_IX     ; Actualizamos la información
   call  sys_physics_updatePng      ; del jugador (siempre estará
   call  man_entity_getArray_A      ; en la posición 0).
   dec   a
   ret   z  ; Si solo existe la entidad del jugador, terminamos.
   call  man_entity_nextEntity
   ld    (_updateIndex), a    

 loop_pu:
   call  sys_physics_updateGeneric
   ld    a, (_updateIndex)
   dec   a
   ld    (_updateIndex), a
   ret   z                       ; Si es así, terminamos.
   jp    loop_pu         ; Si no, pasamos a la siguiente.


; AUXILIAR: Actualiza las físicas del personaje
sys_physics_updatePng:
   ; Comprobamos las coordenadas del eje X
   ld    a, #screen_width + 1       ; C = anchoPantalla - anchoSprite
   sub   e_w(ix)                    
   ld    c, a                       

   ld    a, e_x(ix)                 ; Comprobamos si la nueva posición se queda fuera
   add   e_vx(ix)                   ; sumándole la velocidad que lleva en ese momento.
   cp    c                          
   jr    nc, puP_invalid_x
      ld    e_x(ix), a              ; Si NO nos pasamos, actualizamos la posición.
 puP_invalid_x:

   ; Comprobamos las coordenadas del eje Y
   ld    a, #screen_height          ; C = altoPantalla - e_h
   sub   e_h(ix)           
   ld    c, a

   ld    a, e_y(ix)                 ; Comprobamos si la nueva posición de la entidad
   add   e_vy(ix)                   ; sobrepasa el límite válido (C).
   ld    e_y(ix), a
   cp    c                          ; e_y + e_vy <= altoPantalla - e_h
   jr    c, puP_checkJump           ; Si no lo sobrepasa (es válido) terminamos.

 puP_invalid_y:                     ; Si lo sobrepasa (no es válido) ...
      ld    a, e_y(ix)              ; ... comprobamos si es el límite inferior o superior
      sub   e_h(ix)
      ld    b, a
      ld    a, #screen_height
      cp    b
      jr    nc, puP_invalidDown

 puP_invalidUp:     ;  Si se choca con el borde superior ...
         xor   a
         ld    e_y(ix), a           ; ... su posición y velocidad pasan
         ld    e_vy(ix), a          ; a ser 0 y actualizamos el índice 
         ld    (jump_index), a      ; de salto.
         inc   a
         ld    (player_jump), a
         jp    puP_checkJump

 puP_invalidDown:   ;  Si se choca con el borde inferior ...
         pop   af    
         pop   af    
         pop   af    
         ld hl, #level_lose    ; se acaba el nivel.
         jp (hl)

 puP_checkJump:
   ; Le ponemos su estado de salto a 1 si estuviese quieto.
   ld    a, (player_jump)
   cp    #-1
   ret   nz                   ; if (player_jump == -1)
      ld    a, #1
      ld    (player_jump), a  ; player_jump = 1
      ret

; AUXILIAR: Actualiza las físicas de cualquier entidad que no sea el personaje
sys_physics_updateGeneric:

   ld    a, e_life(ix)              ; Si la entidad está muerta, 
   and   #0xFF                      ; solo tenemos que borrarla.
   jp    nz, puG_deadCheck
      ld    a, e_info(ix)
      and   #0xFF
      jr    nz, puG_hasToRender
         ld__d_ixh   ; ld de, ix
         ld__e_ixl
         call  man_entity_destroy
         jp    puG_end
    puG_hasToRender:
      xor   a
      ld    e_info(ix), a
      jp    puG_nextEntity

 puG_deadCheck:

   ; Comprobamos las coordenadas del eje X
   ld    a, e_x(ix)                 ; Comprobamos si la nueva posición se queda fuera
   add   e_vx(ix)                   ; sumándole la velocidad que lleva en ese momento.
   bit   7, e_x(ix)
   jr    nz, puG_invalid_x

 puG_valid_x:
      ld    e_x(ix), a              ; Si NO nos pasamos, actualizamos la posición.
      jr    puG_endif_x

 puG_invalid_x:
      ld    e_life(ix), #0
      jp    puG_endif_x
 puG_endif_x:

   ; Comprobamos las coordenadas del eje Y
   ld    a, #screen_height          ; C = altoPantalla - e_h
   sub   e_h(ix)                    
   ld    c, a

   ld    a, e_y(ix)                 ; Comprobamos si la nueva posición de la entidad
   add   e_vy(ix)                   ; sobrepasa el límite válido (C).
   cp    c                          ; e_y + e_vy <= altoPantalla - e_h
   jr    nc, puG_invalid_y          ; Si no lo sobrepasa (es válido) terminamos.
      ld    e_y(ix), a
      jp    puG_nextEntity
 puG_invalid_y:                          ; Si lo sobrepasa (no es válido) ...
      ld    a, e_vy(ix)             ; ... ponemos la velocidad al revés.
      neg                              
      ld    e_vy(ix), a
   
 puG_nextEntity:
   call  man_entity_nextEntity
 puG_end:
   ret

; Cada 'X = windRate' avances, el wind avanza -1 en el eje X todas las posiciones del mapa.
sys_physics_wind::
   ld    a, (wind)             ; En cuanto el contador llega a 0, avanzamos
   dec   a
   ld    (wind), a
   jr    nz, noUpdate_wind

      call generate_platform
      call  man_entity_getArray
      dec   e_x(ix)
      bit   7, e_x(ix)
      jr    z, noLimit               ; Si el wind pone la posición del personaje fuera del límite
         inc   e_x(ix)               ; lo recolocamos en la posición 0 (no muere).

   noLimit:
      call  man_entity_getArray_A
      call  man_entity_nextEntity   ; Saltamos al siguiente elemento
      dec   a
      ret   z

   update_wind2:
      dec   e_x(ix)
      bit   7, e_x(ix)
      jr    z, noKill_wind          ; Si el wind pone la posición de la entidad fuera del límite
         ld    e_life(ix), #0       ; ponemos su vida a 0 para que muera en la siguiente iteración.
   noKill_wind:

      call  man_entity_nextEntity   ; Saltamos al siguiente elemento
      dec   a
      jr    nz, update_wind2        ; Si aún hay elementos, seguimos recorriendo

      ld    a, #windRate            ; Al terminar, restauramos el contador
      ld    (wind), a               ; a su valor inicial.
 noUpdate_wind:

   ld    a, (enemy)             ; En cuanto el contador llega a 0, avanzamos
   dec   a
   ld    (enemy), a
   jr    nz, noGenerate_enemy
      call generate_enemy
      ld    a, #enemyRate
      ld    (enemy), a
 noGenerate_enemy:
   ret

; Controla el salto del jugador
sys_physics_jump::
   call  man_entity_getArray_IX
   ld    a, (player_jump)
   cp    #-1
   jr    z, #Physics_notJumping ; Si está saltando...

      cp    #0  ; ... comprobamos si es hacia arriba o hacia abajo
      jr    nz, #Physics_jumpingDown

 Physics_jumpingUp:   ; Hacia arriba
         ld    hl, #jumptableUp        ; Apuntamos a la dirección de la tabla
         call  sys_physics_jumpUpdate  ; y actualizamos la velocidad. También su 
         jr    z, Physics_updateUp     ; índice SI NO hemos llegado a jumpSize.

            ld    (jump_index), a      
            ret

 Physics_updateUp:   ;  En caso contrario, reseteamos el índice y pasamos al modo jumpingDown.
            xor   a
            ld    (jump_index), a
            ld    a, (player_jump)
            inc   a
            ld    (player_jump), a

 Physics_jumpingDown:
      ld    hl, #jumptableDown         ; Apuntamos a la dirección de la tabla
      call  sys_physics_jumpUpdate     ; y actualizamos la velocidad. También su 
      jr    z, Physics_updateDown      ; índice SI NO hemos llegado a jumpSize.

         ld    (jump_index), a         
         ret

 Physics_updateDown:  ; En caso contrario, mantenemos el último valor posible del array.
      dec   a
      ld    (jump_index), a

 Physics_notJumping:
   ret

; Función AUXILIAR utilizada en sys_physics_jump que incrementa
; el puntero al que apunta la tabla de saltos correspondiente
; y actualiza la velocidad del jugador.
sys_physics_jumpUpdate:
   ld    bc, (jump_index)     ; Pasamos a la siguiente velocidad de la tabla ...
   ld    b, #0
   add   hl, bc
   ld    a, (hl)              ; ... y actualizamos la velocidad del jugador.
   ld    e_vy(ix), a

   ld    a, (jump_index)
   inc   a            ; Actualizamos el index
   cp    #jumpSize    ; y comprobamos si ha llegado al final de la tabla
   ret

