#include "commun.h"
#include "tables.h"

//Intégrer au perso pour faire plusieurs joueurs!
s32 plan = 0, moteur2d_planReel = 0;
u32 gblPlusX = 0, gblPlusY = 0;

u8 moteur2d_gblCroche;
u32 moteur2d_masqueGbl = 127;
int vitesseAMAX;

/*
    CALCUL DU PA

    Calcule le point d'attraction du personnage pour émuler une force centrifuge, élément
    qui m'est inconnu. (-45 à 45 degrés: BAS, 45-135: DROITE, 135-225: HAUT, 225-315:
    GAUCHE)...
*/
/*inline*/ unsigned int calcPA(SONIC *s)
{
	const unsigned int vals[6] = { PA_BAS, PA_DROITE, PA_HAUT, PA_GAUCHE, PA_BAS, PA_DROITE };
	unsigned int i;

	//    if (angle<180)
	//        return vals[(angle+45)/90];
	//    else
	//WORKAROUND INSIDE!!!
	i = (s->angle + 44) / 90;

	if (s->angle == 315 && s->vx < 0)        //PA_BAS
		i = 4;
	if (s->angle == 45 && s->vx < 0)
	{        //PA_DROITE
		i = 1;
	}

	return vals[i];
}

void GereEau(SONIC *s)
{
	u32 i;
	if (s->sous_leau)
	{
		if (s->tempsEau == 0)
			s->tempsEau = 60 * 30;
		if (s->tempsEau > 0)
			s->tempsEau--;
		if (s->tempsEau == 25 * 60 || s->tempsEau == 20 * 60 || s->tempsEau == 15 * 60)
			JoueSon(1, SFX_DINGDING);        //Dingding
		if (s->tempsEau == 12 * 60)
			JoueMusique("compte_a_rebours.bgm", 0 | 0x10, 2);
		if (s->tempsEau < 12 * 60)
		{
			if ((s->tempsEau / 15) & 1 && (s->tempsEau % 120) >= 30)
			{
				CreeObjetStandardEx(
					EcrSprites->scrlX + 112,
					EcrSprites->scrlY + 42,
					0,
					1,
					0,
					0,
					AjSprite(Pal_10_rebours_tiles + (s->tempsEau / 120) * 8 * 8, 8 * 16),
					AjPalette(Pal_1_palette),
					TAILLE_8x16);
			}
		}

		if (s->tempsEau <= 0)
			ChangeEtat(s, heNoye);
		i = rand();
		if (s->tempsEau >= 21 * 60)
		{
			if (mod16(i, 60) == 0)
			{
				CreePerso(s->x + 8, s->y + 4, 0, 8, 8, hBulleAir, 0);
			}
		}
		else if (s->tempsEau >= 12 * 60)
		{
			if (mod16(i, 40) == 0)
			{
				CreePerso(s->x + 8, s->y + 4, 0, 8, 8, hBulleAir, 0);
			}
		}
		else
		{
			if (mod16(i, 30) == 0)
			{
				CreePerso(s->x + 8, s->y + 4, 0, 8, 8, hBulleAir, 0);
			}
		}
	}
	else
	{
		if (s->tempsEau > 0 && s->tempsEau <= 12 * 60)
			JoueMusique(gblLI->nom_musique, 0, 0);
		s->tempsEau = 0;
	}
}

/*
    GESTION DES TOUCHES

    Gère les touches appuyées et applique les forcées nécessaires à notre cher Sonic
*/
void GereTouches(SONIC *s)
{
	int vx, vy, v2x, v2y, debutSaut, v, tGauche, tDroite;
	static char droiteAppuye = 0, gaucheAppuye = 0;

	s->premAppui = 0;
	if (super_vitesse)
		vitesseAMAX = VMAX_SUPER_VITESSE;
	else
		vitesseAMAX = VMAX_STANDARD;

	//Mouvement impossible lorsqu'il est touché
	if (LitControle(s, CNT_TOUCHES))
	{
		if (REG_KEYS & KEY_DOWN && s->croche)
		{
			if (ABS(s->vx) >= VITESSE_MIN && !s->enboule)
			{
				Sonic_metEnBoule(s, 1);
				JoueSon(2, SFX_MISEENBOULE);        //Mise en boule
			}
		}

		tGauche = REG_KEYS & KEY_LEFT;
		tDroite = REG_KEYS & KEY_RIGHT;
		if (!tDroite)
			s->pousseMurDroite = 0;
		if (!tGauche)
			s->pousseMurGauche = 0;
		if (ABS(s->vx) > VITESSE_MIN || !s->croche)
			s->pousseMurDroite = s->pousseMurGauche = 0;
		if (tDroite && s->vx < vitesseAMAX)
		{        //Augmente sa vitesse (vitesse positive=à droite)
			//           if (!s->colDroite)       {
			if ((s->pousseMurDroite > 0 || s->colDroite) && s->pousseMurDroite < 250)
				s->pousseMurDroite++;
			if (!super_vitesse)
				s->A = ACCEL;
			else
				s->A = ACCEL * 2;
			if (s->sous_leau)
				v = VITESSE_MIN;
			else
				v = VITESSE_MIN;
			if (ABS(s->vx) < v)
			{
				s->A = ACCEL;
				s->vx = v;
				s->premAppui = 1;        //On devra faire des tests
				s->virgule_x = vMUL(1);
			}

			//                }
			s->dir = 1;
		}
		else if (tGauche && s->vx > -vitesseAMAX)
		{        //Diminue sa vitesse (vitesse négative=à gauche)
			//        if (!s->colGauche)      {
			if ((s->pousseMurGauche > 0 || s->colGauche) && s->pousseMurGauche < 250)
				s->pousseMurGauche++;

			if (!super_vitesse)
				s->A = -ACCEL;
			else
				s->A = -ACCEL * 2;
			if (s->sous_leau)
				v = VITESSE_MIN;
			else
				v = VITESSE_MIN;
			if (ABS(s->vx) < v)
			{
				s->A = -ACCEL;
				s->vx = -v;
				s->premAppui = 1;        //On devra faire des tests
				s->virgule_x = 0;
			}

			//        }
			s->dir = -1;
		}
		else
			s->A = 0;
	}
	else
		s->A = 0;

	droiteAppuye = (REG_KEYS & KEY_RIGHT);
	gaucheAppuye = (REG_KEYS & KEY_LEFT);

	/*    if (!droiteAppuye)
	s->colCote=0;
	if (!gaucheAppuye)
	s->colCote=0;*/
	debutSaut = 0;
	if (s->croche)
	{

		/*        if (s->saut)
		Debug("OUPS %i",s->saut);*/
#pragma etat_touche a_gerer_dans_le_handler

		/*        if (s->etat==ETAT_TOUCHE)       {
		s->vx=0;
		ActiveControle(s,CNT_TOUCHES);
		}
		if (s->etat<=16)                    //Type d'états anéantis lorsqu'il touche le sol
		s->etat=0;*/
		s->saut = 0;
		s->vy = GRAVITE_DEPART;
	}
	else
		s->decole++;

	if (s->crochePF)
		s->crochePF--;
	if (s->clignote)
		s->clignote--;
	if (s->invincible)
		s->invincible--;

	if (LitControle(s, CNT_TOUCHES) && REG_KEYS & KEY_A && (!s->interdit_saut || (s->vy < 0 && s->saut >= 2)))
	{

		//SUPPRESSION DE L'ANGLE. FAIRE CE QU'IL FAUT ICI POUR TRANSPOSER L'ANGLE SUR VITESSE X/Y

		/*                if (s->croche)
		s->saut=0;*/
		if (s->saut == 0)
		{
			JoueSon(1, SFX_SAUT);        //Saut
			s->decole = 1;
			vx = vDIV(s->vx * cosinus[s->angle]);
			vy = vDIV(s->vx * -sinus[s->angle]);

			v2x = (VECTEUR_SAUT * cosinus[(s->angle + 90) % 360]) >> VIRGULE;
			v2y = (VECTEUR_SAUT * -sinus[(s->angle + 90) % 360]) >> VIRGULE;

			s->vx = vx + v2x;
			s->vy = vy + v2y;

			debutSaut = 1;
			s->croche = 0;

			/*                #pragma attention_rajoute_pour_eviter_de_se_faire_ecraser_sous_certains_blocs
			if (!s->enboule)        {
			s->y+=8;
			s->hauteur=24;
			}*/
			Sonic_metEnBoule(s, 1);
			s->crochePF = 0;
			s->interdit_saut = 1;
			s->saut = 2;
		}

		s->angle = 0;
		s->saut++;

		/*                    if (s->vy<0)
		s->vy-=vMUL(1)/6;*/
	}
	else
	{

		//WORKAROUND: Un saut en cours et le bouton relâché? Freine Sonic.
		if (s->vy < 0 && s->saut >= 2)
			s->vy += vMUL(1) / 6;
		if (s->saut > 0 && s->vy > 0)
		{
			s->saut = 0;

			//                        s->vy=GRAVITE_DEPART;
			s->interdit_saut = 1;
		}
	}

	//        }
	if (!debutSaut)
	{
		if (s->vy < 0)
		{

			//                s->vy=-s->vxY;
			s->vy += vMUL(1) / 4;

			//                    if (s->vy>=0)                {
			s->interdit_saut = 1;        //Sommet du saut -> on redescend
			//                    }
		}
		else
		{
			s->interdit_saut = 1;
			if ((s->croche && s->colBas) || s->crochePF)
			{
				s->vy = GRAVITE_DEPART;

				//                s->saut=0;
				if (s->sous_leau)
					s->vy *= 2;
				if (!(REG_KEYS & KEY_A))
					s->interdit_saut = 0;
			}
			else
				s->vy += vMUL(1) / 4;
		}
	}

	if (s->colHaut)
	{
		s->angle = 0;
		s->saut = 0;
		if (s->vy < GRAVITE_DEPART)
			s->vy = GRAVITE_DEPART;
	}

	//Prêt pour le moteur 2D
	s->colGauche = 0;
	s->colDroite = 0;
	s->colBasObj = 0;

	//        if (s->saut==0 && s->saut_obligatoire==0)                s->vy=2;
}

/*
        MOTEUR 2D

        Gère le déplacement du personnage, les collisions principales avec les objets
        constants et avec les plate-formes.
        Voir aussi: MoteurCollisions
*/
u32 moteur2d_xSav;
unsigned short moteur2d_angleSav;

void Moteur2D(SONIC *s)
{
	int attr, vx, vy;
	int i, j, pa;
	int dx, dy;
	int v, temp;
	int augVitesse;
	int avance, vitesse;
	int vxSav;

	//    int corrige=0;
	moteur2d_xSav = s->x;
	if (s->croche)
		moteur2d_angleSav = s->angle;
	else
		moteur2d_angleSav = -1;
	vxSav = s->vx;
	if (gblLI->ei.blocage_angle && s->pas_angle)
		moteur2d_angleSav = 0;
	pa = calcPA(s);
#pragma attention
	s->pas_angle = 0;
	if (LitControle(s, CNT_PHYSIQUE))
	{

		/*
		PARTIE CALCULS
		Calcule la trajectoire et les différentes vitesses appliquées à Sonic avant
		de déplacer celui-ci.
		*/

		//Interdit d'accélérer en boule
		if (s->croche && s->enboule && ((s->A < 0 && s->vx < 0) || (s->A > 0 && s->vx > 0)))
			s->A = 0;
		if (s->enbouleobligatoire)
			s->A = 1;

		if (s->croche)
		{

			//Calcule l'augmentation de vitesse (cela peut également être une diminution)
			augVitesse = (s->A * ABS(cosinus[s->angle]) - G * sinus[s->angle]) / DIV;

			/*            if (ABS(s->vx)<VITESSE_MIN)
			augVitesse=0;*/
		}
		else
			augVitesse = (s->A << 8) / DIV;

		//Et l'ajoute à la vitesse du perso
		s->vx += augVitesse;

		if ( /*s->croche &&*/ (s->A == 0 || (s->A < 0 && s->vx > 0) || (s->A > 0 && s->vx < 0)))
		{        //Freinage
			//Avec cette gamme d'angles, sonic ne peut se retenir à la paroi en appuyant la touche opposée...
			//Je sais; normalement il devrait avoir une formule pour trouver ça :)
			if (!((s->angle >= 45 && s->angle <= 90) || (s->angle >= 270 && s->angle <= 315)))
			{
				v = s->vx;
				if (s->A == 0)        //Touche relâchée
					i = DIV * 2;
				else
					//Freinage manuel: plus fort
					i = DIV / 4;
				if (s->enboule)
					i *= 4;
				if (s->vx > 0)

					//Normalement c'est (A*cosinus[360-26.57]-G*sinus[360-26.57])/4
					s->vx -= (ACCEL * cosinus[26] + G * sinus[26]) / i;
				else if (s->vx < 0)
					s->vx += (ACCEL * cosinus[26] + G * sinus[26]) / i;
			}
		}

		if (pa != PA_BAS)
		{
			if ((s->vx > 0 && vxSav < 0) || (s->vx < 0 && vxSav > 0))
				goto decroche;
		}

		if (ABS(s->vx) < VITESSE_MIN && s->croche /* && s->A==0*/ )
		{
			if (s->enbouleobligatoire)
			{

				/*            if (s->vx<0)
				s->vx=-vMUL(1);
				else*/
				s->vx = vMUL(1);
			}
			else
			{
decroche:
				Sonic_metEnBoule(s, 0);

				//Moche, mais en cas de plate-forme inclinée, Sonic va en fait décoller et retomber immédiatement, étant ainsi repoussé vers le bas.
				//Il semble qu'une bidouille semblable soit appliquée dans la version originale, ce qui explique le comportement bizarre lorsque
				//Sonic n'a pas assez de vitesse dans un plan incliné et est renvoyé dans l'autre sens.
				if (s->angle)
				{
					s->croche = 0;
					s->angle = 0;

					//En cas d'une pente très dure (>=45°, <=315°), Sonic ne peut s'y tenir, il est donc violamment projeté.
					if (pa != PA_BAS)
					{
						s->colBas = 0;

						//ATTENTION!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
						//Si modification des constantes, cela peut s'avérer nécessaire ici!
						s->vy = 128;
					}

					//                    s->vx=0;
				}

				s->vx = 0;
			}
		}
	}

	/*
	PARTIE COLLISIONS
	Avance le personnage et lui fait détecter les collisions avec les décors et
	objets
	*/

	//Le début de la boucle for
	i = 0;

	//Revenir ici s'il faut calculer la vitesse mais ne pas réinitialiser la boucle...
calcVitesse:

	/*    if (s->premAppui)
	vitesse=s->dir*vMUL(1);
	else*/
	vitesse = s->vx;

	if (s->sous_leau)
	{
		attr = (s->vy) / 2;
		v = (vitesse) / 2;
	}
	else
	{
		attr = (s->vy);
		v = (vitesse);
	}

	/*    if (s->croche && attr<ABS(s->vx))
	attr=ABS(s->vx);*/

	//    v*=4;
	vx = ABS(cosinus[s->angle]) * v;
	vy = ABS(sinus[s->angle]) * v;

	if (pa == PA_DROITE || pa == PA_GAUCHE)
	{        //Inversion des vitesses
		temp = vx;
		vx = vy;
		vy = temp;
	}

	//        vx=v;
	temp = vx;
	vx = temp / (vMUL(1) * vMUL(1));
	s->virgule_x += vDIV(temp - vx * vMUL(1) * vMUL(1));
	if (s->virgule_x > vMUL(1))
	{
		s->virgule_x -= vMUL(1);
		vx++;
	}

	if (s->virgule_x < 0)
	{
		s->virgule_x += vMUL(1);
		vx--;
	}

	//Idem pour ATTR (vitesse_y)
	temp = attr;
	attr = temp / (vMUL(1));
	s->virgule_y += (temp - attr * vMUL(1));
	if (s->virgule_y > vMUL(1))
	{
		s->virgule_y -= vMUL(1);
		attr++;
	}

	if (s->virgule_y < 0)
	{
		s->virgule_y += vMUL(1);
		attr--;
	}

	//    Alerte("%i %i",temp,vx*VIRGULE*VIRGULE);
	vy = vDIV(vy);

	if (plan <= 0)
		moteur2d_masqueGbl = 64511, moteur2d_planReel = 0;
	else
		moteur2d_masqueGbl = 65023, moteur2d_planReel = 1;

	s->colHaut = 0;

	const int vMax = 16;
	if (vx > vMax)
		vx = vMax;
	if (vx < -vMax)
		vx = -vMax;
	if (attr > vMax)
		attr = vMax;
	if (attr < -vMax)
		attr = -vMax;

	while (vx != 0 || attr != 0)
	{
		i = 0;
		if (vx > 0)
		{
			if (vx > 8)
				vx -= 8, i = 8;
			else
			{
				i = vx;
				vx = 0;
			}
		}
		else if (vx < 0)
		{
			if (vx < -8)
				vx += 8, i = -8;
			else
			{
				i = vx;
				vx = 0;
			}
		}

		j = 0;
		if (attr > 0)
		{
			if (attr > 8)
				attr -= 8, j = 8;
			else
			{
				j = attr;
				attr = 0;
			}
		}
		else if (attr < 0)
		{
			if (attr < -8)
				attr += 8, j = -8;
			else
			{
				j = attr;
				attr = 0;
			}
		}

		MoteurCollisions(s, i, j);

		//Ajoute de la stabilité
		if (s->croche)
		{

			//Optimisation: mettre la gravité par défaut (GRAVITE_DEPART) à une grosse valeur pour pouvoir peut-être supprimer l'un de ces appels.
			MoteurCollisions(s, 0, 16);
		}
	}

	if (s->x < viewportX0)
	{
		if (s->vx < 0)
			s->vx = 0;
		s->x = viewportX0;
	}

	if (s->x + s->largeur > viewportX1 - 16)
	{
		if (s->vx > 0)
			s->vx = 0;
		s->x = viewportX1 - s->largeur - 16;
	}

	if (s->y < viewportY0)
		s->vy = GRAVITE_DEPART, s->y = viewportY0;
	if (s->y + s->hauteur > viewportY1 - 16)
		s->type = 0;
}

inline int differenceAngle(int angle1, int angle2)
{
	if (angle1 < 0)
		angle1 += 360;
	if (angle2 < 0)
		angle2 += 360;

	if (angle1 - angle2 >= 180)
		angle1 -= 360;
	if (angle2 - angle1 >= 180)
		angle2 -= 360;

	return ABS(angle1 - angle2);
}

void ColleSonicAuPlafond(SONIC *s, unsigned int *pa, int angle)
{
	if (angle >= 90 && angle < 135)
	{
		s->croche = 1;
		*pa = PA_DROITE;
		s->vx = -s->vy;
		s->dir = 1;
		s->saut = 0;
	}

	if (angle > 225 && angle <= 270)
	{
		s->croche = 1;
		*pa = PA_GAUCHE;
		s->vx = s->vy;
		s->dir = -1;
		s->saut = 0;
	}
}

