#include "commun.h"

#define _collision \
                ( \
                        s->x + s->largeur >= p->x \
                        &&        s->y + s->hauteur >= p->y \
                        &&        s->x < p->x + p->largeur \
                        &&        s->y < p->y + \
                                p->hauteur \
                )
#define _collision_mouv \
                ( \
                        s->x + \
                                s->largeur + \
                                vDIV(s->vx) >= p->x \
                        &&        s->y + \
                                s->hauteur + \
                                vDIV(s->vy) >= p->y \
                        &&        s->x + \
                                vDIV(s->vx) < p->x + \
                                p->largeur \
                        &&        s->y + \
                                vDIV(s->vy) < p->y + \
                                p->hauteur \
                )

//#define _collision_mouv (s->x+s->largeur+vDIV(s->vx)>=perso_ancienX && s->y+s->hauteur>=perso_ancienY && s->x<perso_ancienX+p->largeur && s->y<perso_ancienY+p->hauteur)
#define SauvePos(p) (p->savX = p->x, p->savY = p->y)
#define _obj_dans_ecran \
                ( \
                        p->x + p->largeur >= EcrSprites->scrlX \
                        &&        p->x < EcrSprites->scrlX + 240 \
                        &&        p->y + p->hauteur >= EcrSprites->scrlY \
                        &&        p->y < EcrSprites->scrlY + \
                                160 \
                )
#define _obj_dans_ecran_tolerance(p, t) \
                ( \
                        (p)->x + \
                        (p)->largeur >= EcrSprites->scrlX - \
                        (t) \
                &&        (p)->x < EcrSprites->scrlX + \
                        240 + \
                        (t) \
                &&        (p)->y + \
                        (p)->hauteur >= EcrSprites->scrlY - \
                        (t) \
                &&        (p)->y < EcrSprites->scrlY + \
                        160 + \
                        (t) \
                )
#define _pcollision(p) \
                ( \
                        s->x + s->largeur >= (p)->x && s->y + s->hauteur >= (p)->y && s->x < (p)->x + (p)->largeur && s->y < (p)->y + \
                                (p)->hauteur \
                )
#define _pcollision_mouv(p) \
                ( \
                        s->x + s->largeur + vDIV(s->vx) >= (p)->x && s->y + s->hauteur + vDIV(s->vy) >= (p)->y && s->x + vDIV(s->vx) < \
                                (p)->x + (p)->largeur && s->y + vDIV(s->vy) < (p)->y + (p)->hauteur \
                )
#define _collision_tolerance(t) \
                ( \
                        s->x + \
                        s->largeur >= p->x - \
                        (t) \
                &&        s->y + \
                        s->hauteur >= p->y - \
                        (t) \
                &&        s->x < p->x + \
                        p->largeur + \
                        (t) \
                &&        s->y < p->y + \
                        p->hauteur + \
                        (t) \
                )
#define _collision_passe_bas(CROCH) \
                ( \
                        s->x + \
                        s->largeur >= p->x \
                &&        s->y + \
                        s->hauteur >= p->y - \
                        2 \
                &&        s->x < p->x + p->largeur \
                &&        s->y + \
                        s->hauteur - \
                        vDIV(s->vy) < p->y + \
                        ((CROCH) ? 16 : 1) \
                &&        s->vy > 0 \
                )
#define _collision_passe_bas_croche_sonic        ( \
{ \
        EssaieDeCrocher(s); s->y = p->y - s->hauteur + 1; \
} \
        )
#define IDPersoCourant()                                        (gblJ)

/*inline void Sonic_rebondit(SONIC *s)        {
    if (s->vy>=0)
        s->croche=0, s->vy=-s->vy-vMUL(1);
}*/
#define Sonic_rebondit(s) \
        ( \
{ \
        if (s->vy >= 0) \
        s->croche = 0, s->vy = -s->vy - vMUL(1); \
} \
        )
#define Sonic_tue(s)        (s->type = 0)

void DetruitPerso(PERSO *p)
{
	p->type = 0;
	p->fHandler = NULL;

	//ATTENTION: x2 car 16 bits!
	if (nbrePersos > 1)
		MemMove16bits(id_perso + gblJ, id_perso + gblJ + 1, sizeof(id_perso) - gblJ * 2 - 2);
	if (nbrePersos > 0)
		id_perso[nbrePersos--] = 0;
}

void InserePerso(int nombre)
{
	MemMove16bits(id_perso + nombre, id_perso, sizeof(id_perso) - 2 * nombre);
	nbrePersos += nombre;
}

//Crée x sprites libres à partir de 0
void InsereSprites(u32 nbSprites)
{
	u32 zero = 0;
	MemMove16bits
		(
		MonOAM + nbSpritesAvantObjets + nbSprites,
		MonOAM + nbSpritesAvantObjets,
		sizeof(MonOAM) - sizeof(MonOAM[0]) * (nbSprites + nbSpritesAvantObjets)
		);
	_memset(MonOAM + nbSpritesAvantObjets, &zero, sizeof(MonOAM[0]) * nbSprites);
	objetsUtilises += nbSprites;
	MoveRotData(nbSprites);
}

void Sonic_gagnePoints(SONIC *s)
{
	s->score += 10;
}

//Fait crever un ennemi
void Perso_creve(PERSO *p)
{
	DONNEES_STD *d = (void *) p->donnees;
	d->param1 = 0;
	p->largeur = 32, p->hauteur = 32;
	p->fHandler = (void *) hExplosion;
	p->instructions = INSTR_PAS_EFFACER;
	p->mort = 1;                        //Pour détecter dans le handler qu'un perso a été détruit

	//ATTENTION: Mettre type à zéro implique que le personnage est détruit sinon de graves bugs peuvent apparaître (cf CreePerso qui cherche les persos ayant type à 0)!
	//    p->type=0;
	JoueSon(1, SFX_PETE);        //Pète
}

//gc=gestionnaire de collisions
u32 gcPiques(PERSO *p, SONIC *s, u32 type)
{
	if (type == TC_HAUT)
	{
		if (p->vx == 2)
			Sonic_tue(s);
		else if (!s->invincible)
		{
			Sonic_metEnBoule(s, 0);
			s->croche = 0;
			s->vy = -vMUL(4);
			Sonic_perdBagues(s);
		}
		else
			return 1;
		return 0;
	}
	else
		return 1;
}

u32 gcBumper(PERSO *p, SONIC *s, u32 type)
{
	if (type == TC_HAUT)
	{
		Sonic_metEnBoule(s, 0);
		s->vy = -vMUL(p->angle);

		ChangeEtat(s, heBumper);

		s->y = p->y - s->hauteur - 2;
		s->interdit_saut = 1;
		JoueSon(1, SFX_BUMPER); //Bumper
		return 0;
	}
	else
		return 1;
}

u32 gcBumperHoriz(PERSO *p, SONIC *s, u32 type)
{
	if (type == TC_DROITE && p->angle > 0)
	{
		s->vx = vMUL(p->angle);

		//        s->x=p->x+s->largeur;
		JoueSon(1, SFX_BUMPER); //Bumper
		return 0;
	}
	else if (type == TC_GAUCHE && p->angle < 0)
	{
		s->vx = vMUL(p->angle);

		//        s->x=p->x-p->largeur;
		JoueSon(1, SFX_BUMPER); //Bumper
		return 0;
	}
	else
		return 1;
}

u32 gcNul(PERSO *p, SONIC *s, u32 type)
{
	return 1;
}

u32 gcColonneARZ(PERSO *p, SONIC *s, u32 type)
{
	if (type == TC_HAUT)
	{
		PARAM(p)->param1 = 1;
		s->vy = -vMUL(2);
		return 0;
	}

	return 1;
}

u32 gcBlocDurAvecCollisionMouvement(PERSO *p, SONIC *s, u32 type)
{
	if (type == TC_HAUT)
		PARAM(p)->param6 = 1;
	if (type == TC_TOUS)
		s->type = 0;
	return 1;
}

u32 gcTele(PERSO *p, SONIC *s, u32 type)
{
	int i;
	if ((type == TC_DROITE || type == TC_GAUCHE) && !s->croche) //Peut pas péter une télé en l'air mis à part par le haut
		return 1;
	if (s->enboule && s->y - p->y < 20 /*&& !(s->saut>=1 && s->saut<=4)*/ )
	{
		if (type == TC_HAUT)
			Sonic_rebondit(s);
		Perso_creve(p);
		i = CreePerso(p->x, p->y, 0, 16, 16, hTelePrise, INSTR_HORS_MAP);
		persos[i].etat = p->etat;
		return 0;
	}

	return 1;
}

u32 gcEnnemiSimple(PERSO *p, SONIC *s, u32 type)
{
	if (s->enboule)
	{
		if (type == TC_HAUT)
			Sonic_rebondit(s);
		Sonic_gagnePoints(s);
		Perso_creve(p);
	}
	else
		Sonic_perdBagues(s);
	return 0;
}

u32 gcColonneTombante(PERSO *p, SONIC *s, u32 type)
{
	if (type == TC_HAUT)
	{
		if (PARAM(p)->param1 == 0)
		{
			PARAM(p)->param1 = 1;
			p->vy = vMUL(1);
		}
	}

	if (type == TC_TOUS)
		s->type = 0;
	return 1;
}

int perso_ancienX = -1, perso_ancienY = -1;
void InitCollisions(PERSO *p)
{
	perso_ancienX = p->x, perso_ancienY = p->y;
}

#define SONIC_DISTANCE_TEST_PIEDS        10

//Gestionnaire de collisions standard personnalisé
void hCollision(SONIC *s, PERSO *p, u32 (*Gestionnaire) (PERSO *, SONIC *, u32), u32 masque)
{
	if (p->scroched)
		p->scroched--;
	if (!_collision_mouv)
		goto fin;
	if (perso_ancienX == -1)
	{
		if (masque & TC_MOUV)
		{
			ErreurFatale
				(
				"handlers_p.c :: hCollision #01",
				"Un masque TC_MOUV est specifie mais InitCollisions n'a pas ete appele. Probleme de structure de l'objet (%i).",
				p->noObjet
				);
		}
		else
			perso_ancienX = p->x, perso_ancienY = p->y;
	}
	else
	{
		if (!(masque & TC_MOUV))
		{        //Mouvement
			//Peut signaler un problème grave aléatoire, comme un appel de InitCollisions sans le hCollision fait après
			ErreurFatale
				(
				"handlers_p.c :: hCollision #02",
				"InitCollisions appele mais le masque n'a pas TC_MOUV. Peut signaler un probleme de structure de l'objet. (%i)",
				p->noObjet
				);
		}
	}

#pragma changements_effectues
	if (masque & TC_HAUT)
	{
		if (s->y + s->hauteur <= perso_ancienY && s->y + s->hauteur + vDIV(s->vy) /*+(s->crochePF?3:0)*/ >= p->y)
		{

			//        if (s->y+s->hauteur<=perso_ancienY && s->y+s->hauteur+vDIV(s->vy)/*+(s->crochePF?3:0)*/>p->y)         {
			//            if (s->y+s->hauteur>p->y+p->hauteur)
			//          Debug("%i+%i<=%i && %i+%i+%i>=%i",s->y,s->hauteur,perso_ancienY,s->y,s->hauteur,vDIV(s->vy),p->y);
			if (Gestionnaire(p, s, TC_HAUT))
			{
				if (s->x >= p->x && s->x < p->x + p->largeur)
					s->colBasObj |= 4;
				else if (s->x + s->largeur >= p->x && s->x + s->largeur < p->x + p->largeur)
					s->colBasObj |= 8;
				if (s->x + SONIC_DISTANCE_TEST_PIEDS >= p->x && s->x + SONIC_DISTANCE_TEST_PIEDS < p->x + p->largeur)
					s->colBasObj |= 1;
				if
					(
					s->x + s->largeur - SONIC_DISTANCE_TEST_PIEDS >= p->x
					&&        s->x + s->largeur - SONIC_DISTANCE_TEST_PIEDS < p->x + p->largeur
					) s->colBasObj |= 2;

				p->scroched = 3;
				EssaieDeCrocher(s);
				s->y = p->y - s->hauteur - 1;
			}

			goto fin;
		}
	}

	if (masque & TC_BAS)
	{
		if (s->y >= perso_ancienY + p->hauteur && s->y + vDIV(s->vy) <= p->y + p->hauteur)
		{
			if (Gestionnaire(p, s, TC_BAS))
			{

				//                s->y=p->y+p->hauteur+3;
				s->y = p->y + p->hauteur + 1;
				s->vy = GRAVITE_DEPART;
			}

			goto fin;
		}
	}

	//Signifie "sur la gauche de l'objet"
	if (masque & TC_GAUCHE)
	{

		//-1 -> workaround pour détecter dans tous les cas
		//        if (s->x+s->largeur-1<=perso_ancienX && s->x+s->largeur+vDIV(s->vx)>=p->x)     {
		if (s->x + s->largeur - vDIV(s->vx) <= perso_ancienX && s->x + s->largeur + vDIV(s->vx) >= p->x)
		{
			if (Gestionnaire(p, s, TC_GAUCHE))
			{
				CollisionDroite(s);
				s->x = p->x - s->largeur - 1 - vDIV(s->vx);
			}

			goto fin;
		}
	}

	if (masque & TC_DROITE)
	{

		//+1 -> workaround
		if (s->x - vDIV(s->vx) >= perso_ancienX + p->largeur && s->x + vDIV(s->vx) <= p->x + p->largeur)
		{

			//        if (s->x+1>=perso_ancienX+p->largeur && s->x+vDIV(s->vx)<=p->x+p->largeur)        {
			if (Gestionnaire(p, s, TC_DROITE))
			{
				CollisionGauche(s);

				//                s->x=p->x+p->largeur+1-vDIV(s->vx);
				s->x = p->x + p->largeur - vDIV(s->vx);
			}

			goto fin;
		}
	}

	//Problème: rien détecté, et pourtant on est dans la zone de collision...
	if (masque & TC_HAUT)
	{
		if (s->y + s->hauteur > p->y + p->hauteur)
			Gestionnaire(p, s, TC_TOUS);
		else
		{
			if (s->y + s->hauteur + vDIV(s->vy) /*+(s->crochePF?3:0)*/ >= p->y)
			{
				if (Gestionnaire(p, s, TC_HAUT))
				{
					p->scroched = 3;
					EssaieDeCrocher(s);
					s->y = p->y - s->hauteur - 1;
				}

				goto fin;
			}
		}
	}

fin:
	perso_ancienX = -1;
}

/*
Handlers de sprites

Le moteur de jeu présente un handler de sprites confortable, passant l'état de Sonic
et celui de l'objet/ennemi/perso dont il est question.
De plus, une liste (champ de bits) de messages est envoyée. Elle est calculée en
tenant compte des besoins signalés par le champ instructions de l'objet.
*/
void hNul(SONIC *s, PERSO *p, u32 msg)
{ }
void hExplosion(SONIC *s, PERSO *p, u32 msg)
{
	DONNEES_STD *d = (void *) p->donnees;
	if (msg & MSG_AFFICHER)
	{
		CreeObjetStandardP
			(
			p,
			AjSprite(Pal_2_32x32_tiles + (d->param1 >> 3) * 16 * 32, 32 * 32),
			AjPalette(Pal_2_palette),
			TAILLE_32x32
			);
	}

	d->param1++;
	if (d->param1 >= 5 << 3)
		DetruitPerso(p);
}

void hBaguePrise(SONIC *s, PERSO *p, u32 msg)
{
	DONNEES_STD *d = (void *) p->donnees;
	if (msg & MSG_AFFICHER)
	{
		CreeObjetStandardP
			(
			p,
			AjSprite(powerups_tiles + (((d->param1) >> 3 & 1) + 5) * 8 * 16, 16 * 16),
			AjPalette(Pal_2_palette),
			TAILLE_16x16
			);
	}

	d->param1++;
	d->param2 += 2;
	if (d->param1 >= 4 << 3)
	{
		DetruitPerso(p);
	}
}

void hRoller(SONIC *s, PERSO *p, u32 msg)
{                //Handler pour l'ennemi roulant
	InitCollisions(p);
	if (msg & MSG_AFFICHER)
	{        //Faut-il afficher le personnage?
		ExecSprScript(p->x, p->y, Obj_Spt_Pal_1_tiles, motorbug01_sprScript, AjPalette(Pal_1_palette));

		//Le déplacement ne doit s'effectuer que dans ce cas (car le moteur de collisions est sauté sinon)
	}

	//    p->x--;                                 //Le déplace sur la gauche
	MoteurCollisionObjets(p, -1, 2);
	hCollision(s, p, gcEnnemiSimple, TC_TOUS | TC_MOUV);
	return;
}

void hBumper(SONIC *s, PERSO *p, u32 msg)
{                //Handler pour l'ennemi roulant
	u32 pal, til;
	if (PARAM(p)->param2 == 0)
		pal = AjPalette(Pal_2_palette), til = AjSprite(Pal_1_32x32_tiles, 32 * 32);
	else
		pal = AjPalette(Pal_1_palette), til = AjSprite(Pal_1_32x32_tiles + 32 * 592 / 2, 32 * 32);

	CreeObjetStandard(p->x, p->y - 16, til, pal, 0, 2);

	hCollision(s, p, gcBumper, TC_TOUS);
	return;
}

void hPlateForme(SONIC *s, PERSO *p, u32 msg)
{
	DONNEES_STD *d = (void *) p->donnees;
	char croche = 0, timer;
	short avance;

	if (msg & MSG_AFFICHER)
	{
		if (PARAM(p)->param6 == 0)
			CreeObjetStandardP(p, AjSprite(Pal_3_8x8_tiles, 64 * 32), AjPalette(Pal_3_palette), TAILLE_64x32);
		else if (PARAM(p)->param6 == 1)
			ExecSprScript(p->x, p->y, Obj_Spt_Pal_3_s2_tiles, pfarz01_sprScript, AjPalette(Pal_3_palette));

		//param5 -> en train de tomber?
		if (d->param5 < 30)
		{

			//depY remplacé par gravité car en lecture seule
			p->y = p->vy;
			p->y = p->vy + sinus[6 * d->param2] / 32;
		}

		if
			(
			s->x + s->largeur >= p->x
			&&        s->y + s->hauteur >= p->y - 2
			&&        s->x < p->x + p->largeur
			&&        s->y + s->hauteur - vDIV(s->vy) < p->y + (s->crochePF ? 16 : 1)
			&&        s->vy > 0
			)
		{
			if (d->param2 < 15)
				d->param2++;
			EssaieDeCrocher(s);
			s->y = p->y - s->hauteur + 1;
			croche = 1;
		}
		else if (d->param2 > 0)
			d->param2--;

		//        p->y-=4;
	}

	p->cpt++;
	timer = (p->etat == 0) ? (p->cpt & 1) : (1);
	avance = (p->etat == 2) ? (2 * p->dir) : (p->dir);

	if (timer)
	{
		d->param1 += ABS(avance);
		if (d->param4 == 1)
		{
			p->x += avance;
			if (croche)
				s->x += avance;
		}
		else if (d->param4 == 2)
		{
			p->vy += avance;
			p->y += avance;
			if (croche)
				s->y += avance;
		}
		else if (d->param4 == 3)
		{
			if (croche && !d->param5)
				d->param5 = 1;
			if (d->param5)
				d->param5++;
			if (d->param5 >= 30)
			{
				p->y += 2 + ((d->param5 - 30) >> 3);
				if (croche)
					s->y += 2 + ((d->param5 - 30) >> 3);
			}
		}

		if ((d->param1 >> 4) == d->param3)
		{
			p->dir = -p->dir;
			d->param1 = 0;
		}
	}
}

int test = 1;

void hPont(SONIC *s, PERSO *p, u32 msg)
{                //Handler pour l'ennemi roulant
	DONNEES_STD *d = (void *) p->donnees;
	int dist, i, j, k, pasAfficher = 0;
	PERSO *base;

	if (d->param1 > 0)
	{

		//La marge de tolérance est de 16 pixels à cause de s->y+s->hauteur<p->y+16
		base = (PERSO *) d->param1;
		if
			(
			s->x + s->largeur >= p->x - 16 * 6
			&&        s->y + s->hauteur >= p->y
			&&        s->x < p->x + 16 * 5
			&&        s->y + s->hauteur < p->y + 24
			&&        s->vy > 0
			)
		{

			//En fait, je crois que c'est ce qu'on appelle en informatique un arbre...
			i = (s->x - p->x + 8 + 16 * 6) / 16;

			//Maintenant, on connait l'adresse du premier sprite du pont (base) et le numéro d'élément du pont (i)
			j = 0;

			k = sinus[17 * i] / 16;

			while (j < i && base)
			{
				if (base->y < base->depY + k * sinus[90 * j / i] / 256)
					base->y++;
				else if (base->y > base->depY + k * sinus[90 * j / i] / 256)
					base->y--;

				//Prochain sprite
				base = (PERSO *) PARAM(base)->param2;
				j++;
			}

			if (base)
				s->y = base->y - s->hauteur + 4;

			while (base)
			{
				if (base->y < base->depY + k * sinus[90 * (11 - j) / (11 - i)] / 256)
					base->y += SKIP(1);
				else if (base->y > base->depY + k * sinus[90 * (11 - j) / (11 - i)] / 256)
					base->y -= SKIP(1);

				//Prochain sprite
				base = (PERSO *) PARAM(base)->param2;
				j++;
			}
		}
		else
		{
			while (base)
			{
				base->y--;
				if (base->y < base->depY)
					base->y = base->depY;
				base = (PERSO *) PARAM(base)->param2;
			}
		}

		pasAfficher = 1;
	}
	else
	{

		//La marge de tolérance est de 16 pixels à cause de s->y+s->hauteur<p->y+16
		if
			(
			s->x + s->largeur >= p->x + 4
			&&        s->y + s->hauteur >= p->y
			&&        s->x < p->x + 12
			&&        s->y + s->hauteur < p->y + 24
			&&        s->vy > 0
			)
		{
			dist = (s->y + s->hauteur) - (p->y);
			s->y -= dist;
			EssaieDeCrocher(s);
		}
	}

	if (!pasAfficher)
		CreeObjetStandardP(p, AjSprite(pont_tiles, 16 * 16), AjPalette(Pal_3_palette), 0, 1);
	return;
}

const signed char tbl_PFCroulante[] =
{
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	16,
	15,
	15,
	15,
	15,
	14,
	14,
	14,
	14,
	13,
	13,
	13,
	13,
	12,
	12,
	12,
	12,
	11,
	11,
	11,
	11,
	10,
	10,
	10,
	10,
	9,
	9,
	9,
	9,
	8,
	8,
	8,
	8,
	7,
	7,
	7,
	7,
	6,
	6,
	6,
	6,
	5,
	5,
	5,
	5,
	4,
	4,
	4,
	4,
	3,
	3,
	3,
	3,
	2,
	2,
	2,
	2,
	1,
	1,
	1,
	1,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	0,
	-1
};

inline u32 CreeObjetRetourneH
(
 u16 x,
 u16 y,
 unsigned short tiles,
 unsigned short palette,
 unsigned short forme,
 unsigned short taille
 )
{
	CodeBaseCreeObjet;

	//1 pour MH
	CreeObjet(objetsUtilises, tiles, posX, posY, 0, forme, taille, 0, palette, prioDefaut, 0, 1, 0, 0, 0, 0);
	return 1;
}

inline u32 CreeObjetRetourneV
(
 u16 x,
 u16 y,
 unsigned short tiles,
 unsigned short palette,
 unsigned short forme,
 unsigned short taille
 )
{
	CodeBaseCreeObjet;

	//1 pour MH
	CreeObjet(objetsUtilises, tiles, posX, posY, 0, forme, taille, 0, palette, prioDefaut, 1, 0, 0, 0, 0, 0);
	return 1;
}

void hPFCroulante(SONIC *s, PERSO *p, u32 msg)
{
	DONNEES_STD *d = (void *) p->donnees;
	s32 i, j;
	s32 x, y;
	u32 tiles, palette;

	x = p->x - EcrSprites->scrlX;
	y = p->y - EcrSprites->scrlY;

	//On a demandé au handler d'être toujours géré, même hors écran. Mais bon, on a juste besoin de deux
	//patterns supplémentaires, donc on va nous-même faire la coupure à la place du handler standard, si
	//c'est requis.
	if (d->param1 > 20 || (x > -16 * 6 && y > -16 * 5 && x < 240 && y < 160))
	{

		//Les sprites qui vont être créés sortent de la liste standard. Ils sont toutefois gérés très
		//rapidement
		tiles = AjSprite(Pal_3_8x8_tiles + 8 * 4 * 32, 120 * 8 * 8);
		palette = AjPalette(Pal_3_palette);
		if (d->param1)
		{
			if (d->param2 > 180)
				DetruitPerso(p);
			for (j = 4; j >= 0; j--)
			{

				//                y=d->param2*(j+6);
				for (i = 5; i >= 0; i--)
				{
					y = d->param2 * (((j << 1) + 16 - i) >> 1);
					if (j == 4 && i < 3)
						break;
					x = (y - i * 32) >> 2;
					if (x < 0)
						x = 0;
					if (p->vx == 1) //Vers la droite
						CreeObjetStandard
						(
						p->x + 16 * i,
						p->y + 16 * j + x,
						tiles + (i + 6 * j) * 4,
						palette,
						TAILLE_16x16
						);
					else
						//Vers la gauche
						CreeObjetRetourneH
						(
						p->x + 16 * (5 - i),
						p->y + 16 * j + x,
						tiles + (i + 6 * j) * 4,
						palette,
						TAILLE_16x16
						);
				}
			}
		}
		else
		{
			for (j = 4; j >= 0; j--)
			{
				for (i = 5; i >= 0; i--)
				{
					if (j == 4 && i < 3)
						break;
					if (p->vx == 1) //Vers la droite
						CreeObjetStandard(p->x + 16 * i, p->y + 16 * j, tiles + (i + 6 * j) * 4, palette, TAILLE_16x16);
					else
						//Vers la gauche
						CreeObjetRetourneH
						(
						p->x + 16 * (5 - i),
						p->y + 16 * j,
						tiles + (i + 6 * j) * 4,
						palette,
						TAILLE_16x16
						);
				}
			}
		}
	}

	//Détection de collisions
	if
		(
		s->x + s->largeur >= p->x
		&&        s->y + s->hauteur >= p->y
		&&        s->x < p->x + p->largeur
		&&        s->y <= p->y + vDIV(s->vy)
		&&        d->param2 < 12
		)
	{
		i = s->x - p->x + (s->largeur >> 1);
		if (i < 0)
			i = 0;
		if (i > 95)
			i = 95;
		if (p->vx != 1)
			i = 95 - i;
		i = (s->y + s->hauteur) - (p->y + tbl_PFCroulante[i]);
		if ((i >= -1 && vDIV(s->vy) >= i - 1) || s->crochePF)
		{
			s->y -= i;
			EssaieDeCrocher(s);
			if (d->param1 == 0)
			{
				d->param1 = 1;
				JoueSon(2, SFX_ECROULE);
			}
		}
	}

	//Mouvement
	if (d->param1)
		d->param1++;
	if (d->param1 > 20)
	{
		if (d->param2 >= 120)
			d->param2 += 2;
		if (d->param2 >= 45 && d->param2 & 1)
			d->param2 += 2;
		else
			d->param2++;
	}
}

void hBaguePerdue(SONIC *s, PERSO *p, u32 msg)
{
	u32 x, y;

	/*    if (!_obj_dans_ecran_tolerance(p,64))       {
	DetruitPerso(p);
	return;
	}*/
	if (msg & MSG_AFFICHER)
	{
		if (p->cpt < 240 || p->cpt & 1)
		{
			CreeObjetStandardP
				(
				p,
				AjSprite(powerups_tiles + (((nFrame >> 3) & 3) + 1) * 16 * 8, 16 * 16),
				AjPalette(Pal_2_palette),
				0,
				1
				);
		}
	}

	p->angle += p->vx;

	if (p->angle > vMUL(1))
	{
		p->x += p->angle >> 8;
		p->angle -= (p->angle >> 8) << 8;
	}

	if (p->angle < 0)
	{
		p->x += p->angle >> 8;
		p->angle -= (p->angle >> 8) << 8;
	}

	p->y += (p->vy) >> 4;
	p->vy += 2;

#pragma utilisation_map_brute
	x = ((p->x >> 4));
	y = ((p->y >> 4)) + 1;

	if ((Map(x, y) &~MASQUE_PASSE_BAS) >= 1 && (Map(x, y) &~MASQUE_PASSE_BAS) < TYPE_SPEC)
	{
		p->vy = -(5 * p->vy / 8);
	}

	p->cpt++;
	if (p->cpt > 270)
	{
		DetruitPerso(p);
		return;
	}

	if (s->invincible < 90)
	{
		if (CollisionEntreDeuxSprites(s, p))
		{
			s->bagues++;
			p->fHandler = (void *) hBaguePrise;
			p->instructions = INSTR_PAS_EFFACER | INSTR_HORS_MAP;
			JoueSon(0, SFX_BAGUE);        //Bague
		}
	}
}

void hBague(SONIC *s, PERSO *p, u32 msg)
{

	/*    if (!(msg&MSG_AFFICHER))          {
	p->type=0;
	return;
	}*/
	if (msg & MSG_AFFICHER)
	{
		CreeObjetStandardP
			(
			p,
			AjSprite(powerups_tiles + (((nFrame >> 3) & 3) + 1) * 16 * 8, 16 * 16),
			AjPalette(Pal_2_palette),
			TAILLE_16x16
			);
	}

	/*    x=((p->x>>4)-(EcrSprites->scrlX>>4))+5;
	y=((p->y>>4)-(EcrSprites->scrlY>>4))+5;*/
	if (s->invincible < 90)
	{
		if (CollisionEntreDeuxSprites(s, p))
		{
			s->bagues++;
			p->fHandler = (void *) hBaguePrise;
			p->instructions = INSTR_PAS_EFFACER | INSTR_HORS_MAP;
			JoueSon(0, SFX_BAGUE);        //Bague
		}
	}
}

void hPiques(SONIC *s, PERSO *p, u32 msg)
{        //Handler pour les piquants
	u32 i;

	for (i = 0; i < 3; i++)
	{
		CreeObjetStandard
			(
			p->x + (i << 4),
			p->y,
			AjSprite(piquant_tiles, 8 * 32),
			AjPalette(Pal_2_palette),
			TAILLE_8x32
			);
	}

	hCollision(s, p, gcPiques, TC_TOUS);
	return;
}

void hTele(SONIC *s, PERSO *p, u32 msg)
{        //Handler pour les télés
	u32 i;
	if ((nFrame & 7) <= 3)
		i = 0;
	else
		i = p->etat;
	CreeObjetStandard
		(
		p->x + 8,
		p->y + 7,
		AjSprite(Pal_1_16x16_tiles + i * 16 * 16 / 2, 16 * 16),
		AjPalette(Pal_1_palette),
		TAILLE_16x16
		);
	CreeObjetStandard(p->x, p->y, AjSprite(powerups2_tiles, 32 * 32), AjPalette(Pal_2_palette), TAILLE_32x32);
	hCollision(s, p, gcTele, TC_TOUS);
}

void hTelePrise(SONIC *s, PERSO *p, u32 msg)
{        //Handler pour les télés
	if (p->angle == 90)
	{
		if (p->etat == 1)
		{
			s->bagues += 10;
			JoueSon(0, SFX_BAGUE);        //Bague
		}
		else if (p->etat == 5)
		{
			super_vitesse = 1200;
		}
	}

	if (p->angle >= 120)
	{
		DetruitPerso(p);
		return;
	}

	p->angle += 2;
	CreeObjetStandard
		(
		p->x + 8,
		p->y + 7 - (sinus[p->angle > 90 ? 90 : p->angle] >> 2),
		AjSprite(Pal_1_16x16_tiles + p->etat * 16 * 16 / 2, 16 * 16),
		AjPalette(Pal_1_palette),
		TAILLE_16x16
		);
}

void hBorne(SONIC *s, PERSO *p, u32 msg)
{
	DONNEES_STD *d = (void *) p->donnees;
	if (d->param1)
	{
		CreeObjetStandard
			(
			p->x + cosinus[wrap(d->param1 * 12 + 90)] / 16,
			p->y + 16 - sinus[wrap(d->param1 * 12 + 90)] / 16,
			AjSprite(Pal_1_16x16_tiles + 6 * 16 * 8, 16 * 16),
			AjPalette(Pal_1_palette),
			TAILLE_16x16
			);
		if (d->param1 < 60)
			d->param1 += 2;
	}
	else
	{
		if (CollisionEntreDeuxSprites(s, p))
		{
			u32 i, x, y;
			JoueSon(0, SFX_BORNE);
			d->param1 = 2;
			for (i = 0; i < nbrePersos; i++)
				LiberePerso(i);
			x = s->x, y = s->y;
			s->x = p->x - 4, s->y = p->y + 32;
#pragma assume_no_borne_correct
			if (gblLI->spi.liste[p->noObjet].param1 >= 1 && gblLI->spi.liste[p->noObjet].param1 <= 10)
				lstBornes[gblLI->spi.liste[p->noObjet].param1 - 1] = 1;
			else
			{
				ErreurFatale
					(
					"handlers_p.c :: hBorne #01",
					"Identificateur de borne invalide: %i (objet no %i)",
					gblLI->spi.liste[p->noObjet].param1,
					p->noObjet
					);
			}

			SaveState(s);
			s->x = x, s->y = y;
			for (i = 0; i < nbrePersos; i++)
				RemetPerso(i);
		}

		CreeObjetStandard
			(
			p->x,
			p->y,
			AjSprite(Pal_1_16x16_tiles + 7 * 16 * 8, 16 * 16),
			AjPalette(Pal_1_palette),
			TAILLE_16x16
			);
	}

	CreeObjetStandard
		(
		p->x,
		p->y + 16,
		AjSprite(powerups_tiles + 8 * 8 * 16, 16 * 32),
		AjPalette(Pal_2_palette),
		TAILLE_16x32
		);
	CreeObjetStandard
		(
		p->x,
		p->y + 48,
		AjSprite(powerups_tiles + 10 * 8 * 16, 16 * 16),
		AjPalette(Pal_2_palette),
		TAILLE_16x16
		);
}

void hProjectileBomber(SONIC *s, PERSO *p, u32 msg)
{
	DONNEES_STD *d = (void *) p->donnees;
	if (d->param3 < 8)
	{
		CreeObjetStandardP
			(
			p,
			AjSprite(Pal_2_16x16_tiles + 16 * 8 * 13, 16 * 16),
			AjPalette(Pal_2_palette),
			TAILLE_16x16
			);
	}
	else if (d->param3 < 16)
	{
		CreeObjetStandardP
			(
			p,
			AjSprite(Pal_2_16x16_tiles + 16 * 8 * 14, 16 * 16),
			AjPalette(Pal_2_palette),
			TAILLE_16x16
			);
	}
	else
	{
		CreeObjetStandardP
			(
			p,
			AjSprite(Pal_2_16x16_tiles + 16 * 8 * (11 + ((nFrame >> 3) & 1)), 16 * 16),
			AjPalette(Pal_2_palette),
			TAILLE_16x16
			);
	}

	if (_collision)
	{
		Sonic_perdBagues(s);
	}

	d->param3++;
	if (d->param3 >= 16)
	{
		p->x--;
		p->y++;
	}
}

void hBuzzbomber(SONIC *s, PERSO *p, u32 msg)
{                //Handler pour l'ennemi roulant
	DONNEES_STD *d = (void *) p->donnees;
	u32 i = 2;
	InitCollisions(p);
	if (msg & MSG_AFFICHER)
	{        //Faut-il afficher le personnage?
		if (d->param3 >= 50 && d->param3 <= 110)
			i = 0;
		if (nFrame & 1)
			i++;
		ExecSprScript(p->x, p->y, Obj_Spt_Pal_1_tiles, buzzbomber01_sprScript + i, AjPalette(Pal_1_palette));

		//        CreeObjetStandardP(p,AjSprite(ennemis_tiles+i*32*64/2,32*64),1,TAILLE_64x32);
	}

	d->param3++;
	if (d->param3 == 60)
	{
		CreePerso(p->x - 6, p->y + 35, 0, 12, 12, hProjectileBomber, INSTR_HORS_MAP);
	}

	if (d->param3 < 30 || d->param3 > 110)
		p->x -= 2;
	hCollision(s, p, gcEnnemiSimple, TC_TOUS | TC_MOUV);
	return;
}

void hEnboule(SONIC *s, PERSO *p, u32 msg)
{                //Mise en boule obligatoire
	if (_collision)
	{
		if (p->vx < 0)
		{
			if (s->vx > 0)
			{
				DesactiveControle(s, CNT_TOUCHES);
				if (!s->enboule)
					JoueSon(2, SFX_MISEENBOULE);        //Mise en boule
				Sonic_metEnBoule(s, 1);
				s->enbouleobligatoire = 1;
			}
			else
			{
				ActiveControle(s, CNT_TOUCHES);
				s->enbouleobligatoire = 0;
			}
		}

		if (p->vx > 0)
		{
			if (s->vx < 0)
			{
				DesactiveControle(s, CNT_TOUCHES);
				if (!s->enboule)
					JoueSon(2, SFX_MISEENBOULE);        //Mise en boule
				Sonic_metEnBoule(s, 1);
				s->enbouleobligatoire = 1;
			}
			else
			{
				ActiveControle(s, CNT_TOUCHES);
				s->enbouleobligatoire = 0;
			}
		}
	}
}

void hRocher(SONIC *s, PERSO *p, u32 message)
{
	ExecSprScript(p->x, p->y, Obj_Spt_Pal_3_tiles, Rocher_sprScript, AjPalette(Pal_3_palette));
	hCollision(s, p, gcNul, TC_TOUS);
}

void hProjectileCrabe(SONIC *s, PERSO *p, u32 msg)
{
	CreeObjetStandardP
		(
		p,
		AjSprite(Pal_1_16x16_tiles + 16 * 8 * (8 + ((nFrame >> 3) & 1)), 16 * 16),
		AjPalette(Pal_1_palette),
		TAILLE_16x16
		);
	if (_collision)
	{
		Sonic_perdBagues(s);
	}

	p->x += p->vx;
	p->y += vDIV(p->vy);
	p->vy += 64;
}

void hProjectileCameleon(SONIC *s, PERSO *p, u32 msg)
{
	CreeObjetStandardP
		(
		p,
		AjSprite(Pal_1_16x16_tiles + 16 * 8 * (8 + ((nFrame >> 3) & 1)), 16 * 16),
		AjPalette(Pal_2_palette),
		TAILLE_16x16
		);
	if (_collision)
		Sonic_perdBagues(s);
	p->x += p->vx;
}

void CreeProjectileCrabe(u32 x, u32 y, u32 vx)
{
	u32 i;
	i = CreePerso(x, y, vx, 12, 12, hProjectileCrabe, INSTR_HORS_MAP);
	persos[i].vy = -vMUL(4);
}

void hCrabbot(SONIC *s, PERSO *p, u32 message)
{
	DONNEES_CRABBOT *d = (void *) p->donnees;
	int coll, i;

	if (d->attente >= 40)
		i = 3;
	else
		i = (d->timer >> 4) % 3;
	ExecSprScript(p->x, p->y, Obj_Spt_Pal_1_tiles, crabbot01_sprScript + i, AjPalette(Pal_1_palette));

	if (ABS(s->x - p->x) < 56 && !d->attente)
		d->attente = 1;
	if (d->attente)
	{
		d->timer = 0;
		d->attente++;
		if (d->attente == 40)
		{
			CreeProjectileCrabe(p->x + 4, p->y, -1);
			CreeProjectileCrabe(p->x + 40, p->y, 1);
		}

		if (d->attente >= 100)
			d->attente = 0;
	}
	else
	{
		d->timer++;
		if (nFrame & 1)
			i = p->vx;
		else
			i = 0;
		coll = MoteurCollisionObjets(p, i, 1);
		if (coll & TC_GAUCHE)
			p->vx = 1;
		else if (coll & TC_DROITE)
			p->vx = -1;
	}

	hCollision(s, p, gcEnnemiSimple, TC_TOUS);
}

void hPirhanabot(SONIC *s, PERSO *p, u32 message)
{
	DONNEES_PIRHANABOT *d = (void *) p->donnees;
	InitCollisions(p);
	ExecSprScript
		(
		p->x,
		p->y,
		Obj_Spt_Pal_1_tiles,
		pirhanabot01_sprScript + ((nTimer >> 2) & 1),
		AjPalette(Pal_1_palette)
		);
	if (!d->arret)
	{
		p->vy += 32;
		p->y += vDIV(p->vy);
	}

	if (p->y >= p->depY + d->limite)
	{
		if (ABS(s->x - p->x) <= d->zone_detection)
		{
			p->vy = d->vy_base;
			p->y = p->depY + d->limite - 1;
			d->arret = 0;
		}
		else
		{
			d->arret = 1;
		}
	}

	hCollision(s, p, gcEnnemiSimple, TC_TOUS | TC_MOUV);
}

void hrBague(SONIC *s, PERSO *p, u32 msg)
{
	CreeObjetStandardStage
		(
		p->x,
		p->y,
		AjSprite(powerups_tiles + 10 * 16 * 8, 16 * 16),
		AjPalette(Pal_2_palette),
		TAILLE_16x16
		);

	/*    if (CollisionEntreDeuxSprites(s,p))         {
	s->bagues++;
	p->fHandler=(void*)hBaguePrise;
	p->instructions=INSTR_PAS_EFFACER|INSTR_HORS_MAP;
	JoueSon(0,"bague.son");
	}*/
}

void hBumperHoriz(SONIC *s, PERSO *p, u32 msg)
{
	u32 pal, til;
	if (PARAM(p)->param2 == 0)
		pal = AjPalette(Pal_2_palette), til = AjSprite(Pal_1_32x32_tiles + 32 * 16 * 3, 32 * 32);
	else
		pal = AjPalette(Pal_1_palette), til = AjSprite(Pal_1_32x32_tiles + 32 * 496 / 2, 32 * 32);

	if (p->angle < 0)
		CreeObjetRetourneH(p->x - 16, p->y, til, pal, 0, 2);
	else
		CreeObjetStandard(p->x, p->y, til, pal, 0, 2);

	hCollision(s, p, gcBumperHoriz, TC_TOUS);
	return;
}

void AideCollisionPlateFormeMouvement(SONIC *s, PERSO *p)
{

	//Param6, si sonic "colle" la plate-forme
	if (PARAM(p)->param6)
	{
		s->y += p->y - perso_ancienY;
		s->y++;
		s->x += p->x - perso_ancienX;
	}

	PARAM(p)->param6 = 0;
	hCollision(s, p, gcBlocDurAvecCollisionMouvement, TC_TOUS | TC_MOUV);
}

void hGHPoteauMD(SONIC *s, PERSO *p, u32 msg)
{
	int i, til, j, visible;

	visible = _obj_dans_ecran;
	if (visible)
	{
		til = AjSprite(Pal_3_64x32_tiles, 64 * 64);
		CreeObjetStandard(p->x, p->y, til, AjPalette(Pal_3_palette), TAILLE_64x32);
		til += 64 * 32 / 64;
		j = 32 + (PARAM(p)->param3 << 5);
		for (i = 32; i < j; i += 32)
			CreeObjetStandard(p->x, p->y + i, til, AjPalette(Pal_3_palette), TAILLE_64x32);
		InitCollisions(p);
	}

	p->angle += PARAM(p)->param2;
	p->angle = p->angle % (360 << 6);
	p->vy -= (PARAM(p)->param1 * cosinus[p->angle >> 6]) >> 8;
	while (p->vy < -256)
	{
		p->y--;
		p->vy += 256;
	}

	while (p->vy > 256)
	{
		p->y++;
		p->vy -= 256;
	}

	if (visible)
		AideCollisionPlateFormeMouvement(s, p);
	return;
}

//Peut-être utilisé pour palette 2
void hCameleon(SONIC *s, PERSO *p, u32 msg)
{                //Handler pour l'ennemi roulant
	DONNEES_STD *d = (void *) p->donnees;
	u32 i;
	if (msg & MSG_AFFICHER)
	{        //Faut-il afficher le personnage?
		if (d->param3 >= 30)
		{
			i = 0;
			if (d->param3 < 130)
				i = (d->param3 - 30) / 20;
			else if (p->dir)
				i = 5 + (((d->param3) >> 3) & 1);
			else
				i = 4;
			if ((s->x > p->x + (p->largeur >> 1) && p->dir == 0) || p->dir == 2)
			{
				ExecSprScriptEx
					(
					p,
					Obj_Spt_Pal_1_tiles,
					cameleon01_sprScript + i,
					AjPalette(Pal_1_palette),
					0,
					prioDefaut,
					1,
					0
					);
			}
			else
				ExecSprScript(p->x, p->y, Obj_Spt_Pal_1_tiles, cameleon01_sprScript + i, AjPalette(Pal_1_palette));
			hCollision(s, p, gcEnnemiSimple, TC_TOUS);
		}

		if (d->param3 < 30)
			d->param3++;
	}

	if (p->mort)
		return;
	if (d->param3 >= 30)
	{
		d->param3++;
		if (d->param3 >= 90)
		{
			p->vy++;
			if (d->param1)        //Touché le sol?
				i = MoteurCollisionObjets(p, p->dir, 1);
			else
				i = MoteurCollisionObjets(p, 0, p->vy >> 4);
			if (d->param1 == 0 && i & TC_BAS)
			{

				//Touché le sol
				d->param1 = 1;
				if (s->x > p->x + (p->largeur >> 1))
					p->dir = 2;
				else
					p->dir = -2;
			}
		}
	}

	return;
}

void hCameleon2(SONIC *s, PERSO *p, u32 msg)
{                //Handler pour l'ennemi roulant
	DONNEES_STD *d = (void *) p->donnees;
	u32 i;
	if (msg & MSG_AFFICHER)
	{        //Faut-il afficher le personnage?
		d->param2 = 1;        //Débloqué?
		if (d->param3 >= 30)
		{
			if (d->param3 < 60)
				i = 0;
			else if (d->param3 < 180)
				i = 1;
			else
				i = 0;
			if (p->dir == 1)
			{
				ExecSprScriptEx
					(
					p,
					Obj_Spt_Pal_1_tiles,
					cameleon01_sprScript + i,
					AjPalette(Pal_2_palette),
					0,
					prioDefaut,
					1,
					0
					);
			}
			else
				ExecSprScript(p->x, p->y, Obj_Spt_Pal_1_tiles, cameleon01_sprScript + i, AjPalette(Pal_2_palette));
		}
	}

	if (d->param2)
	{        //Débloqué?
		d->param3++;
		hCollision(s, p, gcEnnemiSimple, TC_TOUS);
		if (s->x > p->x + (p->largeur >> 1))
			p->dir = 1;
		else
			p->dir = -1;
		if (d->param3 == 105)
		{
			CreePerso(p->x, p->y, -1, 12, 12, hProjectileCameleon, INSTR_HORS_MAP);
		}

		if (d->param3 >= 210)
			DetruitPerso(p);
	}

	return;
}

void hPiqueBois(SONIC *s, PERSO *p, u32 msg)
{
	u32 i, j, k = 0xffff;
	DONNEES_STD *d = (void *) p->donnees;
	j = nFrame >> 4;
	if (_collision)
	{
		k = ((s->x + (s->largeur >> 1)) - p->x) >> 4;
	}

	if (_obj_dans_ecran)
	{
		for (i = 0; i < 8; i++)
		{
			j = j & 7;
			if ((7 - i) == k)
			{
				if ((7 - j) == 1)        //Pique au-dessus
					Sonic_perdBagues(s);
			}

			CreeObjetStandard
				(
				p->x + 1 + (7 - i) * 16,
				p->y - 4,
				AjSprite(Pal_3_16x32_tiles + 16 * 16 * (7 - j), 16 * 32),
				AjPalette(Pal_3_palette),
				TAILLE_16x32
				);
			j++;
		}
	}

	d->param3++;
	return;
}

s16 cameraViewportPriorite, cameraViewportHighestPriorite;

void hCameraViewport(SONIC *s, PERSO *p, u32 msg)
{
#if 1
	if (p->noObjet >= cameraViewportPriorite)
	{
		desiredViewportX0 = PARAM(p)->param1;
		desiredViewportY0 = PARAM(p)->param2;
		desiredViewportX1 = PARAM(p)->param3;
		desiredViewportY1 = PARAM(p)->param4;
		cameraViewportPriorite = p->noObjet;
	}

#else

	//    p->y=s->y;                                  //Hack pour rendre un objet global
	if (p->vx == -1)
	{
		if (p->x - EcrSprites->scrlX <= 32 && p->x - EcrSprites->scrlX >= 0)
			desiredViewportY1 = PARAM(p)->param4;
	}
	else if (p->vx == 1)
	{
		if (p->x - EcrSprites->scrlX >= 224 && p->x - EcrSprites->scrlX <= 240)
			desiredViewportY1 = PARAM(p)->param4;
	}

	return;
#endif
}

void hCameraViewportEtendu(SONIC *s, PERSO *p, u32 msg)
{
#pragma attention_a_modifier_si_etendue_creation_objets_change

	//Vérifie qu'on est dans une zone valable
	if
		(
		(p->x - EcrSprites->scrlX) >> 4 > -16
		&&        (p->y - EcrSprites->scrlY) >> 4 > -16
		&&        (p->x - EcrSprites->scrlX) >> 4 < 30
		&&        (p->y - EcrSprites->scrlY) >> 4 < 25
		)
	{
		if (p->noObjet >= cameraViewportPriorite)
		{
			desiredViewportX0 = PARAM(p)->param1;
			desiredViewportY0 = PARAM(p)->param2;
			desiredViewportX1 = PARAM(p)->param3;
			desiredViewportY1 = PARAM(p)->param4;
			cameraViewportPriorite = p->noObjet;
		}
	}
}

void DessineVaisseauRobotnik(PERSO *p, u16 visage)
{
	PERSO persoTemp;
	if (p->dir == -1)
	{
		ExecSprScript(p->x + 3, p->y - 3, Obj_Spt_Pal_1_tiles, robotnik02_sprScript + visage, AjPalette(Pal_1_palette));
		ExecSprScript(p->x, p->y + 21, Obj_Spt_Pal_2_tiles, robotnik01_sprScript, AjPalette(Pal_2_palette));
	}
	else
	{
		persoTemp.x = p->x + 8, persoTemp.y = p->y - 3;
		persoTemp.largeur = 52, persoTemp.hauteur = 24;
		ExecSprScriptEx
			(
			&persoTemp,
			Obj_Spt_Pal_1_tiles,
			robotnik02_sprScript + visage,
			AjPalette(Pal_1_palette),
			0,
			prioDefaut,
			1,
			0
			);
		persoTemp.x = p->x, persoTemp.y += 24;
		persoTemp.largeur = 64, persoTemp.hauteur = 35;
		ExecSprScriptEx
			(
			&persoTemp,
			Obj_Spt_Pal_2_tiles,
			robotnik01_sprScript,
			AjPalette(Pal_2_palette),
			0,
			prioDefaut,
			1,
			0
			);
	}
}

void hExplosionBotnik(SONIC *s, PERSO *p, u32 msg)
{
	DONNEES_STD *d = (void *) p->donnees;
	if (msg & MSG_AFFICHER)
	{
		CreeObjetStandardP
			(
			p,
			AjSprite(Pal_2_32x32_tiles + (d->param1 >> 3) * 16 * 32, 32 * 32),
			AjPalette(Pal_2_palette),
			TAILLE_32x32
			);
	}

	d->param1++;
	if (d->param1 >= 3 << 3)
	{
		if (p->dir > 0)
			p->dir--, d->param1 = 0;
	}

	if (d->param1 >= 5 << 3)
	{
		DetruitPerso(p);
	}
}

void DessineVaisseauRobotnikFlame(PERSO *p)
{
	if (p->dir == -1)
	{
		CreeObjetStandard
			(
			p->x + 62,
			p->y + 32,
			AjSprite(Pal_1_16x16_tiles + 16 * 8 * (10 + ((nFrame >> 3) % 2)), 16 * 16),
			AjPalette(Pal_1_palette),
			TAILLE_16x16
			);
	}
	else
	{
		CreeObjetStandardEx
			(
			p->x - 14,
			p->y + 32,
			0,
			prioDefaut,
			1,
			0,
			AjSprite(Pal_1_16x16_tiles + 16 * 8 * (10 + ((nFrame >> 3) % 2)), 16 * 16),
			AjPalette(Pal_1_palette),
			TAILLE_16x16
			);
	}
}

int TrouveIDPersoDepuisNoObjet(int perso)
{
	int i;
	for (i = nbrePersos - 1; i >= 0; i--)
	{
		if (persos[id_perso[i]].noObjet == perso)
			return i;
	}

	//Pas trouvé
	return -1;
}

void hBossGHZ(SONIC *s, PERSO *p, u32 message)
{

	//Param1: timing (direction donnée)
	//Param2: phase
	//Param3: position y de base
	//Param4: force F du balancier
	//Param5: état de l'ennemi (temps où il est touché)
	//Param6: le nombre de fois qu'il peut être touché
	DONNEES_STD *d = (void *) p->donnees;
	PERSO persoBoule;
	int i, j;

	if (d->param2 == 0)
	{

		//Hack pour détecter si le viewport a bien été modifié avant de lancer le boss
		if (viewportX0 > TILE)
		{
			JoueMusique("boss.bgm", 0, 0);
			d->param2 = 1;
		}
	}
	else if (d->param2 == 1)
	{
		DessineVaisseauRobotnik(p, 0);
		d->param1++;
		p->y++;
		if (d->param1 >= 80)
			d->param2 = 2, d->param1 = 0, d->param3 = p->y;
	}
	else if (d->param2 == 2)
	{
		DessineVaisseauRobotnik(p, 0);
		d->param1++;
		p->x--;
		if (d->param1 >= 120)
			d->param2 = 3, d->param1 = 0;
		p->y = d->param3 + (sinus[(d->param1 * 4) % 360] >> 6);
	}
	else if (d->param2 == 3)
	{
		DessineVaisseauRobotnik(p, 1 + ((nFrame >> 3) & 1));
		j = (d->param1 * 3) >> 1;
		if (j > 131)
			j = 131;
		ExecSprScript
			(
			p->x + 4,
			p->y + j,
			Obj_Spt_Pal_3_tiles,
			robotball01_sprScript + (nFrame & 1),
			AjPalette(Pal_3_palette)
			);
		if (j > 40)
		{
			CreeObjetStandard
				(
				p->x + 20,
				p->y + 55,
				AjSprite(Pal_1_16x16_tiles + 16 * 8 * (12 + ((nFrame / 12) & 1)), 16 * 16),
				AjPalette(Pal_1_palette),
				TAILLE_16x16
				);
		}

		for (i = 0; i < 4; i++)
		{
			if (j > 16 * i + 67)
			{
				CreeObjetStandard
					(
					p->x + 20,
					p->y + 67 + 16 * i,
					AjSprite(Pal_2_16x16_tiles + 16 * 8 * 7, 16 * 16),
					AjPalette(Pal_2_palette),
					TAILLE_16x16
					);
			}
		}

		d->param1++;
		if (d->param1 >= 150)
			d->param2 = 4, d->param1 = 11718, d->param4 = 372;
	}
	else if (d->param2 >= 4 && d->param2 <= 7)
	{
		u32 palette1, palette2, palette3;
		palette1 = AjPalette(Pal_1_palette);
		palette2 = AjPalette(Pal_2_palette);
		palette3 = AjPalette(Pal_3_palette);
		if (d->param2 == 4)
			DessineVaisseauRobotnik(p, 0);
		else
			DessineVaisseauRobotnik(p, 3 * ((nFrame >> 4) & 1));
		j = d->param1 >> 7;
		if (d->param2 < 6)
		{
			CreeObjetStandard
				(
				p->x + 20,
				p->y + 55,
				AjSprite(Pal_1_16x16_tiles + 16 * 8 * (12 + ((nFrame / 12) & 1)), 16 * 16),
				palette1,
				TAILLE_16x16
				);
			for (i = 0; i < 4; i++)
			{
				CreeObjetStandard
					(
					p->x + 28 + (((16 + i * 16) * cosinus[(180 + j) % 360]) >> 8) - 8,
					p->y + 63 - (((12 + i * 16) * sinus[(180 + j) % 360]) >> 8) - 8,
					AjSprite(Pal_2_16x16_tiles + 16 * 8 * 7, 16 * 16),
					palette2,
					TAILLE_16x16
					);
			}
		}

		if (d->param2 < 7)
		{
			persoBoule.x = p->x + 20 + ((96 * cosinus[(180 + j) % 360]) >> 8) - 16 + 8;
			persoBoule.y = p->y + 55 - ((92 * sinus[(180 + j) % 360]) >> 8) - 16 + 8;
			persoBoule.largeur = 32;
			persoBoule.hauteur = 32;
			ExecSprScript
				(
				persoBoule.x - 8,
				persoBoule.y - 8,
				Obj_Spt_Pal_3_tiles,
				robotball01_sprScript + (nFrame & 1),
				palette3
				);
		}

		if (d->param2 == 4)
		{
			if ((d->param1 >> 7) < 90)
				d->param4 += 6;
			else
				d->param4 -= 6;
			if (d->param4 >= 0 && d->param1 >> 7 <= 90)
				p->x++, i = 1;
			else if (d->param4 < 0 && d->param1 >> 7 >= 90)
				p->x--, i = 1;
			else
				i = 0;
			if (i)        //En train de bouger
				DessineVaisseauRobotnikFlame(p);
			if ((d->param1 >> 8) == 45)
			{
				if (d->param4 >= 0)
					p->dir = -1;
				else
					p->dir = 1;
			}

			d->param1 += d->param4;

			if (_pcollision(&persoBoule))
				Sonic_perdBagues(s);

			if (_collision && d->param5 == 0)
			{
				d->param6--;
				if (d->param6 <= 0)
				{
					CreePerso(p->x + 20, p->y + 55, 1, 32, 32, hExplosionBotnik, INSTR_HORS_MAP);
					for (i = 0; i < 4; i++)
					{
						CreePerso
							(
							p->x + 28 + (((16 + i * 16) * cosinus[(180 + j) % 360]) >> 8) - 8,
							p->y + 63 - (((12 + i * 16) * sinus[(180 + j) % 360]) >> 8) - 8,
							1,
							32,
							32,
							hExplosionBotnik,
							INSTR_HORS_MAP
							);
					}

					i = p->x + 20 + ((96 * cosinus[(180 + j) % 360]) >> 8) - 16, j = p->y + 55 - ((92 * sinus[(180 + j) % 360]) >> 8) - 16;
					CreePerso(i, j, 2, 32, 32, hExplosionBotnik, INSTR_HORS_MAP);
					CreePerso(i + 16, j + 8, 2, 32, 32, hExplosionBotnik, INSTR_HORS_MAP);
					CreePerso(i + 8, j + 24, 2, 32, 32, hExplosionBotnik, INSTR_HORS_MAP);
					CreePerso(i + 32, j + 12, 2, 32, 32, hExplosionBotnik, INSTR_HORS_MAP);

					/*                    for (j=-16;j<48;j+=16)
					for (i=-16;i<64;i+=16)        {
					CreePerso(p->x+i,p->y+j,(unsigned char)((nFrame*objetsUtilises*i*j)>>6)%9,32,32,hExplosionBotnik,INSTR_HORS_MAP);
					}*/
					for (i = 0; i < 48; i++)
						CreePerso
						(
						p->x - 16 + (rand() % 80),
						p->y - 16 + (rand() % 68),
						i,
						32,
						32,
						hExplosionBotnik2,
						INSTR_HORS_MAP
						);

					d->param2 = 5, d->param6 = 0;
				}
				else
				{
					JoueSon(2, SFX_TOUCHEBOSS); //Boss touché
					d->param5 = 60;
				}

				s->vx = -((2 * s->vx) >> 2), s->vy = -((2 * s->vy) >> 2);
			}

			if (d->param5 > 0)
			{
				if ((d->param5 >> 1) & 1)
					PaletteNormale[0x100 + 0x10 * palette2 + 1] = RGB(31, 31, 31);
				else
					PaletteNormale[0x100 + 0x10 * palette2 + 1] = RGB(0, 0, 0);
				d->param5--;
			}
		}
		else if (d->param2 >= 5)
		{
			d->param6++;
			if (d->param6 == 40 || d->param6 == 80)
				d->param2++;
			if (d->param6 == 200)
				d->param2 = 8, d->param1 = 0, p->dir = 1;

			//            if (d->param2&3==0)
			//                CreePerso(p->x+,p->y,1,32,32,hExplosion,0);
		}

		p->y = d->param3 + (sinus[((d->param1 >> 8) * 4) % 360] >> 6);

		//        if (d->param1>=90<<8)
		//            d->param2=4, d->param1=90<<8;
	}
	else if (d->param2 == 8)
	{
		DessineVaisseauRobotnik(p, 4);
		d->param1++;
		if (d->param1 < 60)
			p->y++;
		else if (d->param1 < 100)
		{
			if (d->param1 == 60)
				d->param5 = 1 << 8;
			d->param5 += 6;
			p->x += 1 + (d->param5 >> 8);
			p->y -= d->param5 >> 8;
		}
		else
		{
			d->param2 = 9;

			//Destruction de la caméra viewport actuelle
			if (cameraViewportHighestPriorite >= 0)
			{
				j = gblJ;
				gblJ = TrouveIDPersoDepuisNoObjet(cameraViewportHighestPriorite);
				if (gblJ != -1) //Valide?
					DetruitPerso(&persos[id_perso[gblJ]]);
				gblJ = j;
			}

			//Création d'un objet qui restaure le viewport
			i = CreePerso(p->x, p->y, -1, 16, 16, hCameraViewportEtendu, INSTR_HORS_MAP);
			persos[i].noObjet = 0xffff;
			PARAM(persos + i)->param1 = 0;
			PARAM(persos + i)->param2 = 0;
			PARAM(persos + i)->param3 = TILE * (TABLE_X + 1);
			PARAM(persos + i)->param4 = TILE * (TABLE_Y + 1);

			//Reprise de la musique du niveau
			JoueMusique(gblLI->nom_musique, 0, 0);
		}
	}
	else
	{
		DessineVaisseauRobotnik(p, 5);
		DessineVaisseauRobotnikFlame(p);
		d->param5 += 12;
		p->x += 1 + (d->param5 >> 8);
	}

	/*    else if (d->param2==4)    {
	j=d->param1>>7;
	CreeObjetStandard(p->x+20,p->y+55,AjSprite(Pal_1_16x16_tiles+16*8*(12+((nFrame/12)&1)),16*16),1,TAILLE_16x16);
	for (i=0;i<4;i++)
	CreeObjetStandard(p->x+28+(((16+i*16)*cosinus[(180+j)%360])>>8)-8,p->y+63-(((12+i*16)*sinus[(180+j)%360])>>8)-8,AjSprite(Pal_2_16x16_tiles+16*8*7,16*16),2,TAILLE_16x16);
	ExecSprScript(p->x+20+((96*cosinus[(180+j)%360])>>8)-16,p->y+55-((92*sinus[(180+j)%360])>>8)-16,Obj_Spt_Pal_3_tiles,robotball01_sprScript+(nFrame&1),3);

	d->param1-=256;
	if (d->param1<=0)
	d->param2=3, d->param1=0;
	}*/

	/*    else if (d->param2==3)    {
	j=d->param1*2;
	CreeObjetStandard(p->x+20,p->y+55,AjSprite(Pal_1_16x16_tiles+16*8*(12+((nFrame/12)&1)),16*16),1,TAILLE_16x16);
	for (i=0;i<4;i++)
	CreeObjetStandard(p->x+28+(((16+i*16)*cosinus[(180+j)%360])>>8)-8,p->y+63-(((12+i*16)*sinus[(180+j)%360])>>8)-8,AjSprite(Pal_2_16x16_tiles+16*8*7,16*16),2,TAILLE_16x16);
	ExecSprScript(p->x+20+((96*cosinus[(180+j)%360])>>8)-16,p->y+55-((92*sinus[(180+j)%360])>>8)-16,Obj_Spt_Pal_3_tiles,robotball01_sprScript+(nFrame&1),3);

	d->param1++;
	if (d->param1>=45)
	d->param2=4, d->param1=0;
	}*/

	/*        d->param1++;
	if (d->param1<300)
	p->x+=p->dir;
	else    {
	d->param1=0;
	p->dir=-p->dir;
	}
	p->y=p->depY+(sinus[(nTimer*4)%360]>>6);*/

	//    hCollision(s,p,gcEnnemiSimple,TC_TOUS);
}

void FinDeNiveau(SONIC *s, int cpt)
{
	int lettres, chiffres, bonus;
	static int time_bonus, ring_bonus, cpt_ok;
	int barre;

	lettres = AjSprite(Pal_1_hud_tiles, sizeof(Pal_1_hud_tiles) << 1);
	chiffres = AjSprite(Pal_1_chiffres_tiles, sizeof(Pal_1_chiffres_tiles) << 1);
	bonus = AjSprite(Pal_1_hud2_tiles, sizeof(Pal_1_hud2_tiles) << 1);

	if (cpt == 0)
	{

		//La musique de l'arrivée
		JoueMusique("fin_niveau.bgm", 1, 2);
		ring_bonus = s->bagues * 10;
		if (gblTemps <= TEMPS(0, 30))
			time_bonus = 5000;
		else if (gblTemps <= TEMPS(0, 45))
			time_bonus = 2000;
		else if (gblTemps <= TEMPS(1, 0))
			time_bonus = 1000;
		else if (gblTemps <= TEMPS(1, 30))
			time_bonus = 500;
		else if (gblTemps <= TEMPS(2, 0))
			time_bonus = 400;
		else if (gblTemps <= TEMPS(2, 30))
			time_bonus = 300;
		else if (gblTemps <= TEMPS(3, 0))
			time_bonus = 200;
		else if (gblTemps <= TEMPS(3, 30))
			time_bonus = 100;
		else if (gblTemps <= TEMPS(4, 0))
			time_bonus = 50;
		else if (gblTemps <= TEMPS(4, 30))
			time_bonus = 20;
		else if (gblTemps <= TEMPS(5, 0))
			time_bonus = 10;
		else
			time_bonus = 0;
		cpt_ok = 0;
	}

	if (cpt >= 0 && cpt < 40)
	{
		if (cpt == 0)
		{

			//Sauve la palette courante pour l'alpha-blend
			CopieDMA(PaletteNormale, PaletteNormaleSauv, 512, DMA_16im);
			CopieDMA(PaletteEau, PaletteEauSauv, 512, DMA_16im);
		}

		if (cpt & 1)
			AlphaBlendPalette(PaletteNormale, PaletteNormaleSauv, 0x100, 0, 31 - (cpt >> 1));
		else
			AlphaBlendPalette(PaletteNormale + 0x100, PaletteNormaleSauv + 0x100, 0xf0, 0, 31 - (cpt >> 1));
	}

	/*    if (cpt>=0 && cpt<80)       {
	if ((cpt&3)==0)
	AlphaBlendPalette(PaletteNormale,PaletteNormaleSauv,0x80,0,31-(cpt>>2));
	else if ((cpt&3)==1)
	AlphaBlendPalette(PaletteNormale+0x80,PaletteNormaleSauv+0x80,0x80,0,31-(cpt>>2));
	else if ((cpt&3)==2)
	AlphaBlendPalette(PaletteNormale+0x100,PaletteNormaleSauv+0x100,0x80,0,31-(cpt>>2));
	else
	AlphaBlendPalette(PaletteNormale+0x180,PaletteNormaleSauv+0x180,0x70,0,31-(cpt>>2));*/

	/*        int fadeLengthSauve=fadeLength;
	fadeLength=1;
	if (cpt==0)
	InitFadeOutLong();
	FadeOutTresLong(1,PaletteNormale,PaletteNormaleSauv);
	FadeOutTresLong(2,PaletteEau,PaletteEauSauv);
	fadeLength=fadeLengthSauve;*/

	//    }
	if (cpt >= 90)
	{
		if (ring_bonus > 0)
			ring_bonus -= 10, gblScore += 10;
		if (time_bonus > 0)
			time_bonus -= 10, gblScore += 10;
		if (ring_bonus == 0 && time_bonus == 0 && cpt_ok == 0)
		{
			JoueSon(3, SFX_FINSCORE);
			cpt_ok = cpt;
		}

		if ((ring_bonus > 0 || time_bonus > 0) && (nFrame & 3) == 0)
			JoueSon(3, SFX_BIP);
	}

	if (cpt >= cpt_ok + 240 && cpt_ok != 0)
	{

		//Niveau suivant
		NiveauSuivant();
	}

	if (cpt > 60)
		cpt = 60;
	PaletteNormale[0x1f1] = RGB(0, 0, 0);
	PaletteNormale[0x1ff] = RGB(31, 31, 0);
	PaletteNormale[0x1f6] = RGB(31, 31, 31);

	if (gblLI->acte >= 1 && gblLI->acte <= 3)
	{
		GoldDrawText(min(6 * cpt, 170), 30, 2, "SONIC HAS PASSED");
		GoldDrawChiffre(max(182, 300 - 4 * cpt), 21, gblLI->acte);
		barre = AjSprite(Pal_10_barres_tiles, sizeof(Pal_10_barres_tiles) << 1);
		CreeObjetStandardHUDHorsEcran(max(167, 285 - 4 * cpt), 48, barre + 14, 15, TAILLE_16x8);
	}
	else
	{
		GoldDrawText(min(6 * cpt, 196), 34, 2, "SONIC FINISHED ZONE");
	}

	CreeObjetStandardHUDPlus(max(240 - 6 * cpt, 60), 75, bonus + 6, 15, TAILLE_32x8);
	CreeObjetStandardHUDPlus(max(240 - 6 * cpt, 60) + 32, 75, bonus, 15, TAILLE_32x8);
	CreeObjetStandardHUDPlus(max(240 - 6 * cpt, 60) + 32 + 32, 75, bonus + 4, 15, TAILLE_8x8);
	CreeObjetStandardHUDPlus(max(260 - 6 * cpt, 60), 85, lettres + 4, 15, TAILLE_32x8);
	CreeObjetStandardHUDPlus(max(260 - 6 * cpt, 60) + 32, 85, bonus, 15, TAILLE_32x8);
	CreeObjetStandardHUDPlus(max(260 - 6 * cpt, 60) + 32 + 32, 85, bonus + 4, 15, TAILLE_8x8);
	CreeObjetStandardHUDPlus(max(280 - 6 * cpt, 60), 100, lettres, 15, TAILLE_32x8);
	CreeObjetStandardHUDPlus(max(280 - 6 * cpt, 60) + 32, 100, lettres + 12, 15, TAILLE_8x8);
	HUD_drawInt_Pal31(max(240 - 6 * cpt, 60) + 120, 75, chiffres, ring_bonus * 10);
	HUD_drawInt_Pal31(max(260 - 6 * cpt, 60) + 120, 85, chiffres, time_bonus * 10);
	HUD_drawInt_Pal31(max(280 - 6 * cpt, 60) + 120, 100, chiffres, gblScore * 10);
}

void hArrivee(SONIC *s, PERSO *p, u32 message)
{
	int i;
	DONNEES_STD *d = (void *) p->donnees;

	if (p->etat == 0)
	{
		ExecSprScript(p->x, p->y, Obj_Spt_Pal_1_tiles, arrivee01_sprScript, AjPalette(Pal_1_palette));
		if (_collision)
		{
			p->etat = 1, gblTempsBloque = 1;        //=Tournant
			JoueSon(3, SFX_ARRIVEE);
		}
	}
	else if (p->etat == 1)
	{
		i = (p->cpt >> 1) & 7;
		if (i <= 4)
			ExecSprScript(p->x, p->y, Obj_Spt_Pal_1_tiles, arrivee01_sprScript + i, AjPalette(Pal_1_palette));
		else
			ExecSprScript(p->x, p->y, Obj_Spt_Pal_1_tiles, arrivee01_sprScript + i - 4, AjPalette(Pal_1_palette));
		p->cpt++;
		if (p->cpt >= 90)
		{
			p->cpt = 0;
			p->etat = 2;
		}
	}
	else
	{
		ExecSprScript(p->x, p->y, Obj_Spt_Pal_1_tiles, arrivee01_sprScript + 4, AjPalette(Pal_1_palette));
		if (p->etat == 2)
		{
			if (s->croche)
				p->etat = 3, d->param3 = s->vx, p->cpt = 0;
		}

		if (p->etat == 3)
		{
			if (gblSonic->x < viewportX1)
			{
				DesactiveControle(s, CNT_TOUCHES);
				if (gblSonic->x + gblSonic->largeur >= viewportX1 - 32)
					DesactiveControle(s, CNT_COLLISIONS);
				s->type = 2;
				s->dir = 1;
				gblSonic->virgule_x += d->param3;
				if (gblSonic->virgule_x <= -256)
				{
					gblSonic->x += (gblSonic->virgule_x) / 256;
					gblSonic->virgule_x = gblSonic->virgule_x % 256;
				}

				if (gblSonic->virgule_x >= 256)
				{
					gblSonic->x += (gblSonic->virgule_x) >> 8;
					gblSonic->virgule_x &= 0xff;
				}

				s->vx = vMUL(1);
				if (d->param3 < 0)
					d->param3 += 64;
				else
					d->param3 += 16;
			}
			else
			{
				if (p->cpt >= 0)
					gblGameState = STATE_FINNIVEAU;

				/*                    FinDeNiveau(gblSonic,p->cpt-0);
				p->cpt++;*/
			}
		}
	}
}

/*void CalcVirgule(int *x, int *virgule)           {
if (*virgule<=-256)       {
*x+=(*virgule)/256;
*virgule=(*virgule)%256;
}
if (*virgule>=256)       {
*x+=(*virgule)>>8;
*virgule&=0xff;
}
}*/
void hDebrisColonneARZ(SONIC *s, PERSO *p, u32 msg)
{
	u32 tiles;
	DONNEES_COLONNE_ARZ *d = (void *) p->donnees;
	tiles = AjSprite(Pal_4_s2_16x16_tiles, 16 * 96);
	CreeObjetStandard(p->x, p->y, tiles + PARAM(p)->param1, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
	d->virgule_x += p->vx;
	d->vy += 16;
	d->virgule_y += d->vy;
	CalcVirgule(p->x, d->virgule_x);
	CalcVirgule(p->y, d->virgule_y);
	if (!_obj_dans_ecran_tolerance(p, 64))
		DetruitPerso(p);
}

void hColonneARZ(SONIC *s, PERSO *p, u32 msg)
{
	u32 tiles;
	u32 i, j, k;
	DONNEES_COLONNE_ARZ *dca;

	if (msg & MSG_AFFICHER)
	{
		tiles = AjSprite(Pal_4_s2_16x16_tiles, 16 * 96);
		CreeObjetStandard(p->x, p->y, tiles, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
		CreeObjetStandard(p->x + 16, p->y, tiles + 4, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
		CreeObjetStandardExRapide
			(
			p->x + 32,
			p->y,
			0,
			prioDefaut,
			1,
			0,
			tiles + 4,
			AjPalette(Pal_4_s2_palette),
			TAILLE_16x16
			);
		CreeObjetStandardExRapide
			(
			p->x + 48,
			p->y,
			0,
			prioDefaut,
			1,
			0,
			tiles,
			AjPalette(Pal_4_s2_palette),
			TAILLE_16x16
			);

		CreeObjetStandard(p->x + 16, p->y + p->hauteur - 16, tiles + 16, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
		CreeObjetStandard(p->x + 32, p->y + p->hauteur - 16, tiles + 20, AjPalette(Pal_4_s2_palette), TAILLE_16x16);

		for (i = 16; i < p->hauteur - 16; i += 16)
		{
			CreeObjetStandard(p->x + 16, p->y + i, tiles + 8, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
			CreeObjetStandard(p->x + 32, p->y + i, tiles + 12, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
		}

		p->cpt++;
		if (p->cpt >= 20 && p->cpt <= 44)
			p->y -= 2, p->hauteur += 2;
	}

	p->x += 16;
	p->largeur = 32;
	hCollision(s, p, gcColonneARZ, TC_TOUS);
	p->largeur = 64;
	p->x -= 16;
	if (PARAM(p)->param1)
	{        //Détruit
		const int basev = 384, incr = 56;
		i = CreePerso(p->x, p->y, -basev - incr, 16, 16, hDebrisColonneARZ, INSTR_HORS_MAP);
		dca = (void *) persos[i].donnees;
		dca->offset = 0;
		dca->vy = -256;

		i = CreePerso(p->x + 16, p->y, -basev, 16, 16, hDebrisColonneARZ, INSTR_HORS_MAP);
		dca = (void *) persos[i].donnees;
		dca->offset = 4;
		dca->vy = -256;

		i = CreePerso(p->x + 32, p->y, basev, 16, 16, hDebrisColonneARZ, INSTR_HORS_MAP);
		dca = (void *) persos[i].donnees;
		dca->offset = 4;
		dca->vy = -256;

		i = CreePerso(p->x + 48, p->y, basev + incr, 16, 16, hDebrisColonneARZ, INSTR_HORS_MAP);
		dca = (void *) persos[i].donnees;
		dca->offset = 0;
		dca->vy = -256;

		k = basev - incr;
		for (j = 16; j < p->hauteur - 16; j += 16)
		{
			i = CreePerso(p->x + 16, p->y + j, -k, 16, 16, hDebrisColonneARZ, INSTR_HORS_MAP);
			dca = (void *) persos[i].donnees;
			dca->offset = 8;
			dca->vy = -256;

			i = CreePerso(p->x + 32, p->y + j, k, 16, 16, hDebrisColonneARZ, INSTR_HORS_MAP);
			dca = (void *) persos[i].donnees;
			dca->offset = 12;
			dca->vy = -256;

			k -= incr;
		}

		i = CreePerso(p->x + 16, p->y + j, -k, 16, 16, hDebrisColonneARZ, INSTR_HORS_MAP);
		dca = (void *) persos[i].donnees;
		dca->offset = 16;
		dca->vy = -256;

		i = CreePerso(p->x + 32, p->y + j, k, 16, 16, hDebrisColonneARZ, INSTR_HORS_MAP);
		dca = (void *) persos[i].donnees;
		dca->offset = 20;
		dca->vy = -256;

		JoueSon(2, SFX_ECROULE);        //S'écroule
		DetruitPerso(p);
	}
}

void hHandrill(SONIC *s, PERSO *p, u32 msg)
{        //Handler pour l'ennemi roulant
	int result;
	int i, prio;
	InitCollisions(p);
	if (msg & MSG_AFFICHER)
	{
		if (PARAM(p)->param3 > 0)
			i = 3, prio = 3;
		else
			i = ((PARAM(p)->param1 >> 3) % 3), prio = 2;
		ExecSprScriptEx
			(
			p,
			Obj_Spt_Pal_4_s2_tiles,
			handrill01_sprScript + i,
			AjPalette(Pal_4_s2_palette),
			0,
			prio,
			p->dir < 0 ? 1 : 0,
			0
			);
	}

	if (PARAM(p)->param3 > 0)
		PARAM(p)->param3--;
	else
	{
		if (p->cpt <= 0)
		{
			p->dir = p->vx;
			result = MoteurCollisionObjets(p, p->vx, 2);
			if (result & TC_DROITE)
				p->vx = -1, p->cpt = 60;
			else if (result & TC_GAUCHE)
				p->vx = 1, p->cpt = 60;
			PARAM(p)->param1++;
		}
		else
			p->cpt--;
	}

	hCollision(s, p, gcEnnemiSimple, TC_TOUS | TC_MOUV);
	return;
}

void hFlecheLanceFleches(SONIC *s, PERSO *p, u32 msg)
{        //Handler pour l'ennemi roulant
	if (msg & MSG_AFFICHER)
	{
		CreeObjetStandardExRapide
			(
			p->x,
			p->y,
			0,
			(p->cpt >= 8 ? prioDefaut : 3),
			0,
			0,
			AjSprite(Pal_4_s2_32x32_tiles + 16 * 64, 32 * 8),
			AjPalette(Pal_4_s2_palette),
			TAILLE_32x8
			);
	}

	p->cpt++;
	p->x += 4;
	if (_collision)
		Sonic_perdBagues(s);
	return;
}

void hLanceFleches(SONIC *s, PERSO *p, u32 msg)
{        //Handler pour l'ennemi roulant
	u32 i, sprite;
	if (msg & MSG_AFFICHER)
	{
		if (p->cpt > 60 && ((nFrame >> 2) & 1) == 1)
			i = 1;
		else
			i = 0;
		if (p->cpt >= 98 && p->cpt < 106)
			i = 2;
		else if (p->cpt > 68 && p->cpt < 98)
			i = 3;
		else if (p->cpt > 60 && p->cpt <= 68)
			i = 2;
		CreeObjetStandard
			(
			p->x,
			p->y,
			AjSprite(Pal_4_s2_32x32_tiles + 16 * 16 * i, 32 * 16),
			AjPalette(Pal_4_s2_palette),
			TAILLE_32x16
			);
	}

	if ((p->cpt == 0 || p->cpt == 106) && s->x + s->largeur >= p->x - 32 && s->x - 32 <= p->x + p->largeur)
		p->cpt = 106;
	else if (p->cpt > 0)
	{
		p->cpt--;
	}

	if (p->cpt == 90)
	{
		JoueSon(2, SFX_FLAMME);
		CreePerso(p->x, p->y + 4, 1, 32, 7, hFlecheLanceFleches, INSTR_HORS_MAP);
	}

	return;
}

void hHandrillCreuxPierre(SONIC *s, PERSO *p, u32 msg)
{
	if (_obj_dans_ecran_tolerance(p, 64))
	{
		if (PARAM(p)->param3 == 1)
		{
			CreeObjetStandard
				(
				p->x,
				p->y,
				AjSprite(Pal_3_s2_16x16_tiles, 32 * 16),
				AjPalette(Pal_3_s2_palette),
				TAILLE_16x16
				);
		}
		else
		{
			CreeObjetStandard
				(
				p->x,
				p->y,
				AjSprite(Pal_3_s2_32x32_tiles, 32 * 16),
				AjPalette(Pal_3_s2_palette),
				TAILLE_32x16
				);
		}

		PARAM(p)->param1 += p->vx;
		p->vy += 32;
		PARAM(p)->param2 += p->vy;
		CalcVirgule(p->x, PARAM(p)->param1);
		CalcVirgule(p->y, PARAM(p)->param2);
	}
	else
		DetruitPerso(p);
}

void hHandrillCreux(SONIC *s, PERSO *p, u32 msg)
{
	int i;
	if (msg & MSG_AFFICHER)
	{
		CreeObjetStandard
			(
			p->x,
			p->y + 16,
			AjSprite(Pal_3_s2_32x32_tiles, 32 * 16),
			AjPalette(Pal_3_s2_palette),
			TAILLE_32x16
			);
		CreeObjetStandard
			(
			p->x + 16,
			p->y,
			AjSprite(Pal_3_s2_32x32_tiles, 32 * 16),
			AjPalette(Pal_3_s2_palette),
			TAILLE_32x16
			);
		CreeObjetStandard
			(
			p->x + 32,
			p->y + 16,
			AjSprite(Pal_3_s2_32x32_tiles, 32 * 16),
			AjPalette(Pal_3_s2_palette),
			TAILLE_32x16
			);
		CreeObjetStandard
			(
			p->x + 16,
			p->y + 32,
			AjSprite(Pal_3_s2_32x32_tiles, 32 * 16),
			AjPalette(Pal_3_s2_palette),
			TAILLE_32x16
			);
	}

	if (s->x >= p->x - 96 && s->x <= p->x + p->largeur + 96 && s->y >= p->y - 64 && s->y <= p->y + p->largeur + 64)
	{
		i = CreePerso(p->x + 32, p->y + 16, 1, 32, 16, hHandrillCreuxPierre, INSTR_HORS_MAP | INSTR_SEULEMENT_ECRAN);
		persos[i].vx = 192;
		persos[i].vy = -256;

		i = CreePerso(p->x + 16, p->y, 1, 32, 16, hHandrillCreuxPierre, INSTR_HORS_MAP | INSTR_SEULEMENT_ECRAN);
		persos[i].vx = 64;
		persos[i].vy = -256;

		i = CreePerso(p->x, p->y + 16, 1, 32, 16, hHandrillCreuxPierre, INSTR_HORS_MAP | INSTR_SEULEMENT_ECRAN);
		persos[i].vx = -150;
		persos[i].vy = -400;

		i = CreePerso(p->x + 16, p->y + 32, 1, 32, 16, hHandrillCreuxPierre, INSTR_HORS_MAP | INSTR_SEULEMENT_ECRAN);
		persos[i].vx = -50;
		persos[i].vy = -256;

		i = CreePerso(p->x + 8, p->y + 8, 1, 32, 16, hHandrillCreuxPierre, INSTR_HORS_MAP | INSTR_SEULEMENT_ECRAN);
		PARAM(persos + i)->param3 = 1;
		persos[i].vx = -370;
		persos[i].vy = -400;

		i = CreePerso(p->x + 24, p->y + 24, 1, 32, 16, hHandrillCreuxPierre, INSTR_HORS_MAP | INSTR_SEULEMENT_ECRAN);
		PARAM(persos + i)->param3 = 1;
		persos[i].vx = 200;
		persos[i].vy = -320;

		i = CreePerso(p->x + 16, p->y + 16, 1, 32, 16, hHandrillCreuxPierre, INSTR_HORS_MAP | INSTR_SEULEMENT_ECRAN);
		PARAM(persos + i)->param3 = 1;
		persos[i].vx = -80;
		persos[i].vy = -480;

		i = CreePerso(p->x + 16, p->y + 8, -1, 32, 40, hHandrill, INSTR_SEULEMENT_ECRAN | INSTR_HORS_MAP);
		PARAM(persos + i)->param3 = 15;

		DetruitPerso(p);
	}

	return;
}

void hMinibumperARZ(SONIC *s, PERSO *p, u32 msg)
{
	if (p->cpt > 0)
	{
		ExecSprScript(p->x, p->y + 8, Obj_Spt_Pal_4_s2_tiles, minibumper02_sprScript, AjPalette(Pal_4_s2_palette));
	}
	else
		ExecSprScript(p->x, p->y, Obj_Spt_Pal_4_s2_tiles, minibumper01_sprScript, AjPalette(Pal_4_s2_palette));
	hCollision(s, p, gcNul, TC_DROITE | TC_BAS);
	p->y += 13;
	p->hauteur -= 15;
	p->x += 16;
	p->largeur -= 18;
	if (_collision_mouv)
	{
		s->croche = 0;
		p->cpt++;
		s->y = p->y - s->hauteur;
		if (p->cpt >= 5)
		{
			s->angle = 0;
			s->croche = 0;
			if (s->vy < 0)
				s->vy = 0;
			s->vy = -s->vy - vMUL(PARAM(p)->param2);
			if (s->vy < -vMUL(p->vx))
				s->vy = -vMUL(p->vx);

			//            s->vy=-vMUL(p->vx);
			ChangeEtat(s, heBumper);
			s->interdit_saut = 1;
			JoueSon(1, SFX_BUMPER); //Bumper
		}
	}
	else if (p->cpt > 0)
		p->cpt--;
	p->y -= 13;
	p->hauteur += 15;
	p->x -= 16;
	p->largeur += 18;
}

void hBunBun(SONIC *s, PERSO *p, u32 msg)
{
	const int vmax = 2 * 256;

	if (!_obj_dans_ecran_tolerance(p, 128))
		return;

	if (msg & MSG_AFFICHER)
		ExecSprScript(p->x, p->y, Obj_Spt_Pal_4_s2_tiles, bunbun01_sprScript, AjPalette(Pal_4_s2_palette));

	if ((p->cpt & 63) == 0)
	{

		/*        PARAM(p)->param1=s->x+s->largeur/2;
		PARAM(p)->param2=s->y+s->largeur/2;*/
		p->vx = (((s->x + s->largeur / 2) - (p->x + p->largeur / 2)) * 256 * 3 / 2) / 64;
		p->vy = (((s->y + s->hauteur / 2) - (p->y + p->hauteur / 2)) * 256 * 3 / 2) / 64;
		if (p->vx > vmax)
			p->vx = vmax;
		if (p->vx < -vmax)
			p->vx = -vmax;
		if (p->vy > vmax)
			p->vy = vmax;
		if (p->vy < -vmax)
			p->vy = -vmax;
	}

	if ((p->cpt & 63) < 48 && p->cpt >= 30)
	{
		PARAM(p)->param1 += p->vx;
		PARAM(p)->param2 += p->vy;
		CalcVirgule(p->x, PARAM(p)->param1);
		CalcVirgule(p->y, PARAM(p)->param2);
	}

	hCollision(s, p, gcEnnemiSimple, TC_TOUS);
	p->cpt++;
}

void hPirhana(SONIC *s, PERSO *p, u32 msg)
{
	const int vmax = 2 * 256;
	int i;
	if (msg & MSG_AFFICHER)
	{
		if (p->cpt == 0)
			i = 0;
		else
			i = (PARAM(p)->param1 >> 3) & 1;
		ExecSprScriptEx
			(
			p,
			Obj_Spt_Pal_4_s2_tiles,
			pirhana01_sprScript + i,
			AjPalette(Pal_4_s2_palette),
			0,
			prioDefaut,
			p->vx > 0 ? 1 : 0,
			0
			);
		hCollision(s, p, gcEnnemiSimple, TC_TOUS);
	}

	if (p->cpt <= 1)
	{
		p->virguleX += p->vx;
		p->virguleY += p->vy;
		CalcVirgule(p->x, p->virguleX);
		CalcVirgule(p->y, p->virguleY);
		if (Element(p->x, p->y) & MASQUE_ENNEMIS)
			p->vx = 128;
		if (Element(p->x + p->largeur, p->y) & MASQUE_ENNEMIS)
			p->vx = -128;
	}

	if (p->cpt > 0)
		PARAM(p)->param1++;                        //Compteur
	if (p->cpt > 1)
		p->cpt--;
	if
		(
		s->x >= p->x - 96
		&&        s->x <= p->x + p->largeur + 96
		&&        s->y >= p->y - 32
		&&        s->y <= p->y + p->largeur + 32
		&&        p->cpt == 0
		)
	{
		p->cpt = 10;        //On se rapproche -> active le mode colère
		p->vx = (((s->x + s->largeur / 2) - (p->x + p->largeur / 2)) * 256 * 3 / 2) / 64;
		p->vy = (((s->y + s->hauteur / 2) - (p->y + p->hauteur / 2)) * 256 * 3 / 2) / 64;
		if (p->vx > vmax)
			p->vx = vmax;
		if (p->vx < -vmax)
			p->vx = -vmax;
		if (p->vy > vmax)
			p->vy = vmax;
		if (p->vy < -vmax)
			p->vy = -vmax;
	}
}

void hDebrisPFCARZ(SONIC *s, PERSO *p, u32 msg)
{
	u32 tiles;
	if (!_obj_dans_ecran)
	{
		DetruitPerso(p);
		return;
	}

	tiles = AjSprite(Pal_3_s2_16x16_tiles + 8 * 16, 64 * 32);
	CreeObjetStandard(p->x, p->y, tiles + (p->vx << 2), AjPalette(Pal_3_s2_palette), TAILLE_16x16);
	if (p->vy > 0)
	{
		p->virguleY += p->vy;
		CalcVirgule(p->y, p->virguleY);
	}

	p->vy += 32;
}

void hPFCroulanteARZ(SONIC *s, PERSO *p, u32 msg)
{
	u32 i, j, tiles;
	if ((p->cpt > 20 || p->cpt == 0) && msg & MSG_AFFICHER)
	{
		tiles = AjSprite(Pal_3_s2_16x16_tiles + 8 * 16, 64 * 32);
		for (i = 0; i < 8; i++)
		{
			CreeObjetStandard
				(
				p->x + ((i & 3) << 4),
				p->y + ((i >> 2) << 4),
				tiles + (i << 2),
				AjPalette(Pal_3_s2_palette),
				TAILLE_16x16
				);
		}
	}

	if
		(
		s->x + s->largeur >= p->x
		&&        s->y + s->hauteur >= p->y - 2
		&&        s->x < p->x + p->largeur
		&&        s->y + s->hauteur - vDIV(s->vy) < p->y + (s->crochePF ? 16 : 1)
		&&        s->vy > 0
		)
	{
		EssaieDeCrocher(s);
		s->y = p->y - s->hauteur - 1;
		if (p->cpt == 0)
		{
			p->cpt = 35;
			JoueSon(2, SFX_ECROULE);        //S'écroule
		}
	}

	if (p->cpt > 1)
		p->cpt--;
	if (p->cpt == 20)
	{
		const unsigned short table[] = { -500, -600, -520, -400, -80, -200, -160, 0, };
		for (i = 0; i < 8; i++)
		{
			j = CreePerso(p->x + ((i & 3) << 4), p->y + ((i >> 2) << 4), i, 16, 16, hDebrisPFCARZ, INSTR_HORS_MAP);
			persos[j].vy = table[i];
		}
	}

	if (p->cpt == 1)
	{
		DetruitPerso(p);
		return;
	}
}

void hColonneTombante(SONIC *s, PERSO *p, u32 msg)
{
	u32 i, tiles;
	if (msg & MSG_AFFICHER)
	{
		tiles = AjSprite(Pal_4_s2_16x16_tiles, 16 * 192);
		CreeObjetStandard(p->x, p->y, tiles, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
		CreeObjetStandard(p->x + 16, p->y, tiles + 4, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
		CreeObjetStandardExRapide
			(
			p->x + 32,
			p->y,
			0,
			prioDefaut,
			1,
			0,
			tiles + 4,
			AjPalette(Pal_4_s2_palette),
			TAILLE_16x16
			);
		CreeObjetStandardExRapide
			(
			p->x + 48,
			p->y,
			0,
			prioDefaut,
			1,
			0,
			tiles,
			AjPalette(Pal_4_s2_palette),
			TAILLE_16x16
			);

		for (i = 0; i < 3; i++)
		{
			CreeObjetStandard
				(
				p->x + 16,
				p->y + p->hauteur - 48 + 16 * i,
				tiles + 24 + 8 * i,
				AjPalette(Pal_4_s2_palette),
				TAILLE_16x16
				);
			CreeObjetStandard
				(
				p->x + 32,
				p->y + p->hauteur - 48 + 16 * i,
				tiles + 24 + 8 * i + 4,
				AjPalette(Pal_4_s2_palette),
				TAILLE_16x16
				);
		}

		for (i = 16; i < p->hauteur - 48; i += 16)
		{
			CreeObjetStandard(p->x + 16, p->y + i, tiles + 8, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
			CreeObjetStandard(p->x + 32, p->y + i, tiles + 12, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
		}
	}

	p->hauteur -= 12;

	//    InitCollisions(p);
	if (PARAM(p)->param1 == 1)
	{
		p->virguleY += p->vy;
		CalcVirgule(p->y, p->virguleY);
		p->vy -= 8;
		if (p->y <= p->depY)
			PARAM(p)->param1 = 2, p->vy = 0;
		if (p->scroched)
			s->y = p->y - s->hauteur;

		//            s->y+=vDIV(p->vy*4);
	}
	else if (PARAM(p)->param1 == 2)
	{
		p->vy += 4;
		if (p->vy >= vMUL(4))
			p->vy = vMUL(4);
		p->virguleY += p->vy;
		CalcVirgule(p->y, p->virguleY);
		if (MoteurCollisionObjets(p, 0, 0) & TC_BAS)
			p->vy = 0;
		if (p->scroched)
			s->y = p->y - s->hauteur;

		/*        if (p->scroched)
		s->y+=vDIV(p->vy);*/
	}

	hCollision(s, p, gcColonneTombante, TC_TOUS);
	p->hauteur += 12;
}

void hBoutColonneTombante(SONIC *s, PERSO *p, u32 msg)
{
	u32 i, tiles;
	if (msg & MSG_AFFICHER)
	{
		tiles = AjSprite(Pal_4_s2_16x16_tiles, 16 * 192);
		for (i = 0; i < 3; i++)
		{
			CreeObjetStandard(p->x, p->y + 16 * i, tiles + 24 + 8 * i, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
			CreeObjetStandard
				(
				p->x + 16,
				p->y + 16 * i,
				tiles + 24 + 8 * i + 4,
				AjPalette(Pal_4_s2_palette),
				TAILLE_16x16
				);
		}
	}

	if (s->x >= p->x - 96 && s->x <= p->x + p->largeur + 96 && s->y >= p->y - 96 && s->y <= p->y + p->largeur + 96)
		PARAM(p)->param1 = 1;
	if (PARAM(p)->param1)
	{
		if (p->vy >= vMUL(4))
			p->vy = vMUL(4);
		p->virguleY += p->vy;
		CalcVirgule(p->y, p->virguleY);
		p->vy += 16;
		if (MoteurCollisionObjets(p, 0, 0) & TC_BAS)
			p->vy = 0;
		if (p->scroched)
			s->y += vDIV(p->vy);
		hCollision(s, p, gcBlocDurAvecCollisionMouvement, TC_TOUS);
	}
}

void hBoutColonne(SONIC *s, PERSO *p, u32 msg)
{
	u32 i, tiles;
	if (msg & MSG_AFFICHER)
	{
		tiles = AjSprite(Pal_4_s2_16x16_tiles, 16 * 192);
		CreeObjetStandard(p->x, p->y, tiles, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
		CreeObjetStandard(p->x + 16, p->y, tiles + 4, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
		CreeObjetStandardExRapide
			(
			p->x + 32,
			p->y,
			0,
			prioDefaut,
			1,
			0,
			tiles + 4,
			AjPalette(Pal_4_s2_palette),
			TAILLE_16x16
			);
		CreeObjetStandardExRapide
			(
			p->x + 48,
			p->y,
			0,
			prioDefaut,
			1,
			0,
			tiles,
			AjPalette(Pal_4_s2_palette),
			TAILLE_16x16
			);

		for (i = 16; i < p->hauteur; i += 16)
		{
			CreeObjetStandard(p->x + 16, p->y + i, tiles + 8, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
			CreeObjetStandard(p->x + 32, p->y + i, tiles + 12, AjPalette(Pal_4_s2_palette), TAILLE_16x16);
		}
	}

	p->x += 16;
	p->largeur -= 24;
	hCollision(s, p, gcNul, TC_TOUS);
	p->x -= 16;
	p->largeur += 24;
}

void hBulleAir(SONIC *s, PERSO *p, u32 msg)
{
	CreeObjetStandard
		(
		p->x,
		p->y,
		AjSprite(Pal_1_16x16_tiles + 240 * 8, 16 * 16),
		AjPalette(Pal_1_palette),
		TAILLE_16x16
		);
	p->cpt++;
	p->angle += 2;
	if (p->angle >= 360)
		p->angle -= 360;
	if ((p->cpt & 1) == 0)
		p->y--;
	p->x = p->depX + (sinus[p->angle] >> 5);
	if (p->y < niveau_reel_eau - 2 || !_obj_dans_ecran)
		DetruitPerso(p);
}

void hBulleAirOxygene(SONIC *s, PERSO *p, u32 msg)
{
	u32 i, j;
	const u16 table[] = { 4, 8, 12, 16, 24, 32, };
	i = (PARAM(p)->param1) >> 4;
	if (i > 5)
		i = 5;
	j = table[i] >> 1;
	if (i <= 3)
	{
		CreeObjetStandard
			(
			p->x - j,
			p->y - j,
			AjSprite(Pal_1_16x16_tiles + 224 * 8 + 8 * 16 * i, 16 * 16),
			AjPalette(Pal_1_palette),
			TAILLE_16x16
			);
	}
	else if (i <= 5)
	{
		CreeObjetStandard
			(
			p->x - j,
			p->y - j,
			AjSprite(Pal_1_32x32_tiles + 192 * 16 + 16 * 32 * (i - 4), 32 * 32),
			AjPalette(Pal_1_palette),
			TAILLE_32x32
			);
	}

	if (i < 5)
		PARAM(p)->param1++;
	p->cpt++;
	p->angle += 2;
	if (p->angle >= 360)
		p->angle -= 360;
	if ((p->cpt & 1) == 0)
		p->y--;
	p->x = p->depX + (sinus[p->angle] >> 5);
	if (_collision && i == 5)
	{
		JoueSon(1, SFX_BULLE);                        //Bulle prise
		if (s->tempsEau <= 12 * 60)
			JoueMusique(gblLI->nom_musique, 0, 0);
		ChangeEtat(s, hePrendBulle);
		DetruitPerso(p);
	}

	if (p->y < niveau_reel_eau - 2 || !_obj_dans_ecran)
		DetruitPerso(p);
}

void hBulleSousEau(SONIC *s, PERSO *p, u32 msg)
{
	u32 i;
	i = (p->cpt / 20) % 3;
	CreeObjetStandard
		(
		p->x,
		p->y,
		AjSprite(Pal_1_16x16_tiles + 288 * 8 + i * 8 * 16, 16 * 16),
		AjPalette(Pal_1_palette),
		TAILLE_16x16
		);
	p->cpt++;

	//Génération d'un nombre aléatoire
	i = rand();
	if (s->x >= p->x - 64 && s->x <= p->x + p->largeur + 64 && s->y >= p->y - 128 && s->y <= p->y + p->largeur + 128)
	{
		if (mod16(i, 300) == 0)
			CreePerso(p->x + 8, p->y + 8, 0, 8, 8, hBulleAirOxygene, INSTR_HORS_MAP);
	}

	if (mod16(rand(), 40) == 0)
		CreePerso(p->x + 8, p->y + 8, 0, 8, 8, hBulleAir, INSTR_HORS_MAP);
}

void DessineEau()
{
	static int cpt, lastX;
	u32 x, y, i;
	if (premFois)
		cpt = 0;
	else
		cpt += EcrSprites->scrlX - lastX;
	lastX = EcrSprites->scrlX;
	cpt++;

	/*    if ((nFrame&16)==0)
	cpt+=16;*/
	while (cpt < 0)
		cpt += 32;
	while (cpt >= 32)
		cpt -= 32;
	if (niveau_reel >= 0 && niveau_reel < 160)
	{
		x = 0;
		y = niveau_reel_eau - EcrSprites->scrlY - 2;
		for (i = 0; i <= 256; i += 32)
		{

			//            if (((i>>5)+nFrame)&1)
			CreeObjetStandardEau
				(
				i - cpt,
				y,
				AjSprite(Pal_1_32x32_tiles + 256 * 16, 32 * 16),
				AjPalette(Pal_1_palette),
				TAILLE_32x16
				);
		}
	}
}

void hPlafEau(SONIC *s, PERSO *p, u32 msg)
{
	u32 i;
	i = 272 + ((p->cpt >> 2) << 5);
	CreeObjetStandard
		(
		p->x,
		p->y,
		AjSprite(Pal_1_32x32_tiles + i * 16, 32 * 32),
		AjPalette(Pal_1_palette),
		TAILLE_32x32
		);
	p->cpt++;
	if (p->cpt >= 7 << 2)
		DetruitPerso(p);

	/*    p->angle+=2;
	if (p->angle>=360)
	p->angle-=360;
	if ((p->cpt&1)==0)
	p->y--;
	p->x=p->depX+(sinus[p->angle]>>5);
	if (p->y<niveau_reel_eau-2 || !_obj_dans_ecran)
	DetruitPerso(p);*/
}

void hPlateFormePlus(SONIC *s, PERSO *p, u32 msg)
{
	DONNEES_STD *d = (void *) p->donnees;
	u32 m, i, j, k, croche = 0, x, y;
	if (msg & MSG_AFFICHER)
	{

		//Dessin
		if (PARAM(p)->param4 == 0)
			CreeObjetStandardP(p, AjSprite(Pal_3_8x8_tiles, 64 * 32), AjPalette(Pal_3_palette), TAILLE_64x32);
		else if (PARAM(p)->param4 == 1)
			ExecSprScript(p->x, p->y, Obj_Spt_Pal_3_s2_tiles, pfarz01_sprScript, AjPalette(Pal_3_s2_palette));
		PARAM(p)->param4 &= ~0x1000;

		//Calcul pour le ramolissement lorsqu'on met les pieds dessus. Que pour les PF horizontales et tombantes!
		if ((((d->param2 & 6) >> 1) == 1 || ((d->param2 & 6) >> 1) == 3) && d->param6 < 30)
			p->y = p->depY + sinus[6 * d->param5] / 32;

		//On est dessus?

		/*        if (d->param5)
		Debug("%i %i %i %i %i",s->y,s->y+s->hauteur,p->y,p->y-s->hauteur,s->vy);*/
		if
			(
			s->x + s->largeur >= p->x
			&&        s->y + s->hauteur >= p->y - 1
			&&        s->x < p->x + p->largeur
			&&        s->y + s->hauteur - vDIV(s->vy) < p->y + (s->crochePF ? 16 : 2)
			&&        s->vy > 0
			&&        d->param5 < 30
			)
		{
			if (((d->param2 & 6) >> 1) == 3 && !d->param6)
			{
				d->param6 = 1;
				p->vy = 0;
			}

			if (d->param5 < 15)
				d->param5++;
			EssaieDeCrocher(s);
			s->y = p->y - s->hauteur;
			croche = 1;
		}
		else if (d->param5 > 0)
			d->param5--;
	}

	//    d->param3=32;
	//Temps total pour un aller ou retour
	m = d->param1 + 60;

	k = (d->param3 * nTimer) >> 4;
	j = (k / m) & 1;

	//Param2 modulo 2 pour inverser le sens
	if (d->param2 & 1)
		j = !j;

	//Calcul pour les coins (sinusoïde)
	i = (k % m);

	//Calcul du déplacement
	x = p->x;
	y = p->y;
	if (j)
	{
		if (i >= d->param1)
		{
			j = i - d->param1;
			i = d->param1 + ((16 * sinus[j * 3]) >> 8);
		}

		if (((d->param2 & 6) >> 1) == 1)
			p->x = p->vx + d->param1 - i;
		else if (((d->param2 & 6) >> 1) == 2)
			p->y = p->vy + d->param1 - i;
	}
	else
	{
		if (i >= d->param1)
		{
			j = i - d->param1;
			i = d->param1 + ((16 * sinus[j * 3]) >> 8);
		}

		if (((d->param2 & 6) >> 1) == 1)
			p->x = p->vx + i;
		else if (((d->param2 & 6) >> 1) == 2)
			p->y = p->vy + i;
	}

	if (((d->param2 & 6) >> 1) == 3 && d->param6)
	{
		d->param6++;
		if (d->param6 >= 30)
		{
			p->vy += 16;
			p->virguleY += p->vy;
			CalcVirgule(p->y, p->virguleY);
			if (!_obj_dans_ecran_tolerance(p, 240))
				DetruitPerso(p);
		}
	}

	if (croche)
	{
		s->x += p->x - x;
		s->y = p->y - s->hauteur;
	}
}

/*void hPlateForme(SONIC *s, PERSO *p, u32 msg)       {
DONNEES_STD *d=(void*)p->donnees;
char croche=0,timer;
short avance;

if (msg&MSG_AFFICHER)       {
if (PARAM(p)->param6==0)
CreeObjetStandardP(p,AjSprite(Pal_3_8x8_tiles,64*32),AjPalette(Pal_3_palette),TAILLE_64x32);
else if (PARAM(p)->param6==1)
ExecSprScript(p->x,p->y,Obj_Spt_Pal_3_s2_tiles,pfarz01_sprScript,AjPalette(Pal_3_palette));
//param5 -> en train de tomber?
if (d->param5<30)         {
//depY remplacé par gravité car en lecture seule
p->y=p->vy;
p->y=p->vy+sinus[6*d->param2]/32;
}
if (s->x+s->largeur>=p->x && s->y+s->hauteur>=p->y-2 && s->x<p->x+p->largeur && s->y+s->hauteur-vDIV(s->vy)<p->y+(s->crochePF?16:1) && s->vy>0)      {
if (d->param2<15)
d->param2++;
EssaieDeCrocher(s);
s->y=p->y-s->hauteur+1;
croche=1;
}
else if (d->param2>0)
d->param2--;
//        p->y-=4;
}

p->cpt++;
timer=(p->etat==0)?(p->cpt&1):(1);
avance=(p->etat==2)?(2*p->dir):(p->dir);

if (timer)     {
d->param1+=ABS(avance);
if (d->param4==1)       {
p->x+=avance;
if (croche)
s->x+=avance;
}
else if (d->param4==2)       {
p->vy+=avance;
p->y+=avance;
if (croche)
s->y+=avance;
}
else if (d->param4==3)       {
if (croche && !d->param5)
d->param5=1;
if (d->param5)
d->param5++;
if (d->param5>=30)      {
p->y+=2+((d->param5-30)>>3);
if (croche)
s->y+=2+((d->param5-30)>>3);
}
}
if ((d->param1>>4)==d->param3)       {
p->dir=-p->dir;
d->param1=0;
}
}
}*/
void hSpiner(SONIC *s, PERSO *p, u32 msg)
{
	ExecSprScript
		(
		p->x,
		p->y,
		Obj_Spt_Pal_4_s2_tiles,
		spiner01_sprScript + ((nFrame >> 1) & 1),
		AjPalette(Pal_4_s2_palette)
		);
	if
		(
		s->x + s->largeur > p->x + 16
		&&        s->x < p->x + p->largeur - 16
		&&        s->y + s->hauteur > p->y
		&&        s->y <= p->y + p->hauteur
		)
	{
		if ((p->vx < 0 && s->vx > vMUL(p->vx)) || (p->vx > 0 && s->vx < vMUL(p->vx)))
		{
			JoueSon(2, SFX_TOMBE);
			s->vx = vMUL(p->vx);
		}
	}
}

//Point d'appui du balancier
void hBalancierRond(SONIC *s, PERSO *p, u32 msg)
{
	DONNEES_STD *d = (void *) p->donnees;
	u32 angle, i, j, x, y, cx, cy, til, pal2, pal3;

	angle = (nTimer + p->vy) % 360;

	//Angle de la prochaine frame
	if (angle == 359)
		j = 0;
	else
		j = angle + 1;
	cx = p->x + p->vx * 16 + 40;
	cy = p->y + p->vx * 16 + 24;
	x = cx + (((16 + p->vx * 16) * cosinus[angle]) >> 8) - 24;
	y = cy - (((16 + p->vx * 16) * sinus[angle]) >> 8) - 8;

	pal3 = AjPalette(Pal_3_palette);
	til = AjSprite(pont_tiles + 1 * 16 * 8, 16 * 16 * 3);
	for (i = 0; i < 3; i++)
		CreeObjetStandard(x + (i << 4), y, til + i * 4, pal3, TAILLE_16x16);

	//Vérifier les collisions avec les anciennes valeurs. param5==collé à la PF. Marge de 8, trop certainement.
	if
		(
		s->x + s->largeur >= x
		&&        s->x < x + 48
		&&        s->y + s->hauteur >= y - (PARAM(p)->param5 * 4)
		&&        s->y + s->hauteur <= y + 8
		&&        s->vy >= 0
		)
	{
bon_quand_meme:
		EssaieDeCrocher(s);
		s->angle = 0;
		i = cx + (((16 + p->vx * 16) * cosinus[j]) >> 8) - 24;
		s->x += i - x;
		i = cy - (((16 + p->vx * 16) * sinus[j]) >> 8) - 8;
		s->y = i - s->hauteur;
		PARAM(p)->param5 = 1;
	}
	else if (PARAM(p)->param5)
	{
		if (s->x + s->largeur > x && s->x < x + 48 && s->y + s->hauteur > y && s->y < y + 16)
			goto bon_quand_meme;
		else
			PARAM(p)->param5 = 0;
	}

	x = cx;
	y = cy;
	CreeObjetStandard(x - 8, y - 8, AjSprite(pont_tiles + 4 * 16 * 8, 16 * 16), pal3, TAILLE_16x16);
	pal2 = AjPalette(Pal_2_palette);
	til = AjSprite(Pal_2_16x16_tiles + 16 * 8 * 7, 16 * 16);
	for (i = 0; i < p->vx; i++)
	{
		CreeObjetStandard
			(
			x + (((16 + i * 16) * cosinus[angle]) >> 8) - 8,
			y - (((16 + i * 16) * sinus[angle]) >> 8) - 8,
			til,
			pal2,
			TAILLE_16x16
			);
	}
}

void hFlecheTireeBossARZ(SONIC *s, PERSO *p, u32 message)
{
	u32 i, mh = 0, prio = prioDefaut;

	if (p->dir == -1)
		mh = 1;

	//Au début, la flèche doit avoir une plus petite priorité pour passer derrière la colonne
	if (PARAM(p)->param4 < 16)
		prio = 3;
	PARAM(p)->param4++;

	if (p->cpt == 0 || p->cpt >= 60)
	{
		CreeObjetStandardExRapide
			(
			p->x,
			p->y,
			0,
			prio,
			mh,
			0,
			AjSprite(Pal_4_s2_32x32_tiles + 112 * 16, 8 * 32),
			AjPalette(Pal_4_s2_palette),
			TAILLE_32x8
			);
	}
	else
	{
		CreeObjetStandardExRapide
			(
			p->x,
			p->y,
			0,
			prio,
			mh,
			0,
			AjSprite(Pal_4_s2_32x32_tiles + 112 * 16 + 8 * 16 * ((p->cpt >> 2) % 3), 8 * 32),
			AjPalette(Pal_4_s2_palette),
			TAILLE_32x8
			);
	}

	if
		(
		(p->x + p->largeur < persos[PARAM(p)->param1].x + 8 && p->dir == 1)
		||        (p->x > persos[PARAM(p)->param1].x + 24 && p->dir == -1)
		) p->x += p->dir * 2;
	else
	{
		if (p->cpt == 0)
		{

			//Si un objet existe déjà sur la colonne (param2 de la colonne liée par param1 avec cet objet) on le fait tomber (param3=60)
			if (PARAM(persos + PARAM(p)->param1)->param2 != 0)
			{
				PARAM(persos + PARAM(persos + PARAM(p)->param1)->param2)->param3 = 60;
			}

			PARAM(persos + PARAM(p)->param1)->param2 = (u32) id_perso[gblJ];
		}

		p->cpt++;
	}

	if (_collision)
	{
		if (p->cpt > 0)
		{
			if (PARAM(p)->param3 <= 60 && _collision_passe_bas(s->crochePF))
			{
				_collision_passe_bas_croche_sonic;
				if (PARAM(p)->param3 == 0)
					PARAM(p)->param3 = 1;
			}
		}
		else
			Sonic_perdBagues(s);
	}

	if (PARAM(p)->param3 > 0)
	{
		PARAM(p)->param3++;
		if (PARAM(p)->param3 >= 60)
		{

			//Cette flèche était stockée dans la colonne, il faut l'enlever...
			if (PARAM(persos + PARAM(p)->param1)->param2 == id_perso[gblJ])
				PARAM(persos + PARAM(p)->param1)->param2 = 0;
			p->vy += 64;
			p->virguleY += p->vy;
			CalcVirgule(p->y, p->virguleY);
		}
	}

	//    hCollision(s,p,gcNul,TC_TOUS);
}

void hColonneMontanteBossARZ(SONIC *s, PERSO *p, u32 message)
{
	u32 i, til, pal;
	int decal = 0;

	if (p->etat & 0x8000)
	{
		if (p->hauteur >= 2)
		{
			if (PARAM(p)->param2)
			{
				PARAM(persos + PARAM(p)->param2)->param3 = 60;
			}

			gblSFXTrembleCamera = 1;
			p->y += 2;
			p->hauteur -= 2;
		}
		else
			gblSFXTrembleCamera = 0;
	}
	else
	{
		if (p->etat > 0)
		{
			const s8 modif[] = { 2, 2, 0, 1, -1, 0, 2, 0 };
			p->etat--;
			decal = modif[p->etat & 7];
		}
	}

	pal = AjPalette(Pal_4_s2_palette);
	til = AjSprite(Pal_4_s2_32x32_tiles + 72 * 16, 40 * 32);

	CreeObjetStandard(p->x + decal, p->y + decal, til, pal, TAILLE_32x8);
	if (p->dir == 1)
	{
		for (i = 8; i < p->hauteur; i += 32)
			CreeObjetStandard(p->x + decal, p->y + i + decal, til + 4, pal, TAILLE_32x32);
	}
	else
	{
		for (i = 8; i < p->hauteur; i += 32)
			CreeObjetRetourneH(p->x + decal, p->y + i + decal, til + 4, pal, TAILLE_32x32);
	}

	if (p->etat == 0)
	{
		if (p->hauteur < 136)
		{
			p->y--;
			p->hauteur++;
		}
	}

	hCollision(s, p, gcNul, TC_TOUS);
}

void hMarteauTombantBossARZ(SONIC *s, PERSO *p, u32 msg)
{
	if (p->cpt >= 16)
	{
		if (msg & MSG_AFFICHER)
		{
			if (p->dir == 1)
				ExecSprScript(p->x, p->y, Obj_Spt_Pal_4_s2_tiles, boss_arz03_sprScript, AjPalette(Pal_4_s2_palette));
			else
			{
				ExecSprScriptEx
					(
					p,
					Obj_Spt_Pal_4_s2_tiles,
					boss_arz03_sprScript,
					AjPalette(Pal_4_s2_palette),
					0,
					prioDefaut,
					1,
					0
					);
			}
		}

		p->vy += 32;
		p->virguleY += p->vy;
		CalcVirgule(p->y, p->virguleY);
		p->x -= p->vx;
	}

	if (p->cpt == 0)
		p->vy = -512;
	p->cpt++;
}

void RemplitPerso(PERSO *p, int x, int y, int largeur, int hauteur)
{
	p->x = x;
	p->y = y;
	p->largeur = largeur;
	p->hauteur = hauteur;
}

void hExplosionBotnik2(SONIC *s, PERSO *p, u32 msg)
{
	DONNEES_STD *d = (void *) p->donnees;
	s32 i;
	i = d->param1 - (p->dir << 2);
	if (i == 0)
		JoueSon(0, SFX_MINIEXPLOSION);
	if (msg & MSG_AFFICHER && i >= 0)
	{
		CreeObjetStandardP
			(
			p,
			AjSprite(Pal_2_32x32_tiles + (i >> 3) * 16 * 32, 32 * 32),
			AjPalette(Pal_2_palette),
			TAILLE_32x32
			);
	}

	d->param1++;

	/*    if (i+1>=3<<3)        {
	if (p->dir>0)
	p->dir--, d->param1=0;
	}*/
	if (i + 1 >= 5 << 3)
	{
		DetruitPerso(p);
	}
}

void hBossARZ(SONIC *s, PERSO *p, u32 message)
{
	DONNEES_BOSS_ARZ *d = (void *) p->donnees;
	PERSO persoEnnemi;
	u32 mirrorH, palette5;
	int i, j;
	const u8 table_marteau[] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 1, 0 };

	if (d->phase == 0)
	{

		//Hack pour détecter si le viewport a bien été modifié avant de lancer le boss
		if (viewportX0 > TILE)
		{
			if (s->x + s->largeur < p->x + 224 && s->x > p->x)
			{
				JoueMusique("boss.bgm", 0, 0);
				d->col_1 = CreePerso(p->x + 224, p->y + 256, 1, 32, 8, hColonneMontanteBossARZ, INSTR_HORS_MAP);
				d->col_2 = CreePerso(p->x - 32, p->y + 256, -1, 32, 8, hColonneMontanteBossARZ, INSTR_HORS_MAP);
				d->phase++;
				p->cpt = 0;
				p->y -= 80;
				p->dir = -1;
			}
		}
	}
	else if (d->phase == 1)
	{
		if (p->cpt < 136)
			gblSFXTrembleCamera = 1;
		else
			gblSFXTrembleCamera = 0;
		if (p->cpt >= 152)
			d->phase++, p->cpt = 0, d->posY = p->y;
		p->cpt++;
		p->y++;
	}
	else if (d->phase == 2)
	{
		if (p->x <= p->depX - 40 && p->dir == -1)
			d->mouvement = 1;
		else if (p->x >= p->depX + 136 && p->dir == 1)
			d->mouvement = 1;
		if (d->mouvement == 0 || table_marteau[d->mouvMarteau >> 3] == 0)
			p->y = d->posY + (sinus[(p->cpt * 3) % 360] >> 6);
		if (d->mouvement == 0)
		{
			p->virguleX += p->dir * 204;
			CalcVirgule(p->x, p->virguleX);
		}
		else
		{
			d->mouvMarteau++;
			if (table_marteau[d->mouvMarteau >> 3] == 2)
			{
				if (p->dir == 1 && persos[d->col_1].etat == 0)
				{
					JoueSon(2, SFX_TOMBE);
					persos[d->col_1].etat = 48;
					i = ((rand() & 3) * 32) + 21;
					i = CreePerso
						(
						persos[d->col_1].x - 4,
						persos[d->col_1].y + i,
						-1,
						32,
						8,
						hFlecheTireeBossARZ,
						INSTR_HORS_MAP
						);
					PARAM(persos + i)->param1 = d->col_2;
				}
				else if (p->dir == -1 && persos[d->col_2].etat == 0)
				{
					JoueSon(2, SFX_TOMBE);
					persos[d->col_2].etat = 48;
					i = ((rand() & 3) * 32) + 21;
					i = CreePerso
						(
						persos[d->col_2].x + 4,
						persos[d->col_2].y + i,
						1,
						32,
						8,
						hFlecheTireeBossARZ,
						INSTR_HORS_MAP
						);
					PARAM(persos + i)->param1 = d->col_1;
				}
			}

			if (d->mouvMarteau >= sizeof(table_marteau) << 3)
			{
				d->mouvMarteau = 0, d->mouvement = 0;
				p->x -= p->dir * 64;
				p->dir = -p->dir;
			}
		}

		p->cpt++;
	}

	palette5 = AjPalette(Pal_5_s2_palette);
	if (d->touche > 0)
	{
		if ((d->touche >> 1) & 1)
			PaletteNormale[0x100 + 0x10 * palette5 + 1] = RGB(31, 31, 31);
		else
			PaletteNormale[0x100 + 0x10 * palette5 + 1] = RGB(0, 0, 0);
		d->touche--;
	}

	if (p->dir == 1)
		mirrorH = 0;
	else
		mirrorH = 1;
	if (d->phase == 2)
	{
		RemplitPerso(&persoEnnemi, p->x + (mirrorH ? 72 : 8), p->y + 8, 48, 36);
		if (_pcollision(&persoEnnemi) && d->touche == 0)
		{
			if (s->enboule)
			{
				d->energie--;
				if (d->energie == 0)
				{
					if (p->dir == -1)
						p->x += 64;
					for (i = 0; i < 32; i++)
						CreePerso
						(
						p->x - 16 + (rand() % 80),
						p->y - 16 + (rand() % 68),
						i,
						32,
						32,
						hExplosionBotnik2,
						INSTR_HORS_MAP
						);
					if (p->dir == -1)
						p->x -= 64;

					/*                    for (j=-16;j<48;j+=16)
					for (i=-16;i<64;i+=16)        {
					CreePerso(p->x+i,p->y+j,(unsigned char)((nFrame*objetsUtilises*i*j)>>6)%9,32,32,hExplosionBotnik,INSTR_HORS_MAP);
					}*/
					d->phase++;
					persos[d->col_1].etat = 0x8000;
					persos[d->col_2].etat = 0x8000;
					p->cpt = 0;
				}
				else
				{
					JoueSon(2, SFX_TOUCHEBOSS); //Boss touché
					d->touche = 60;
				}

				s->vx = -((2 * s->vx) >> 2), s->vy = -((2 * s->vy) >> 2);
			}
			else
				Sonic_perdBagues(s);
		}

		RemplitPerso(&persoEnnemi, p->x + (mirrorH ? 16 : 80), p->y - 11, 40, 64);
		if (_pcollision(&persoEnnemi) && table_marteau[d->mouvMarteau >> 3] == 2)
			Sonic_perdBagues(s);
	}
	else if (d->phase >= 3)
		p->cpt++;

	RemplitPerso(&persoEnnemi, p->x + (mirrorH ? 64 : 0), p->y, 64, 52);
	ExecSprScriptEx(&persoEnnemi, Obj_Spt_Pal_5_s2_tiles, boss_arz01_sprScript, palette5, 0, prioDefaut, mirrorH, 0);
	CreeObjetStandard
		(
		mirrorH ? (persoEnnemi.x + 16) : (persoEnnemi.x),
		persoEnnemi.y + 52,
		AjSprite(Pal_5_s2_16x16_tiles + 16 * 8 * ((nFrame >> 2) & 1), 16 * 16),
		palette5,
		TAILLE_16x16
		);
	CreeObjetStandard
		(
		mirrorH ? (persoEnnemi.x + 48) : (persoEnnemi.x + 32),
		persoEnnemi.y + 52,
		AjSprite(Pal_5_s2_16x16_tiles + 16 * 8 * ((nFrame >> 2) & 1), 16 * 16),
		palette5,
		TAILLE_16x16
		);
	if (d->phase < 4 || p->cpt <= 16)
	{
		RemplitPerso(&persoEnnemi, p->x + (mirrorH ? 0 : 64), p->y - 17, 64, 80);
		i = table_marteau[d->mouvMarteau >> 3];
		ExecSprScriptEx
			(
			&persoEnnemi,
			Obj_Spt_Pal_4_s2_tiles,
			boss_arz03_sprScript + i,
			AjPalette(Pal_4_s2_palette),
			0,
			prioDefaut,
			mirrorH,
			0
			);

		//Fin -> au fond
		if (d->phase == 3 && p->cpt >= 16)
		{
			if (p->cpt == 16)
				p->vy = 512;
			p->vy -= 12;
			if (p->vy >= 0)
				p->virguleY += p->vy;
			else
			{
				p->cpt = 0;
				d->phase = 4;
				if (p->dir == 1)
					CreePerso(p->x + 64, p->y - 17, p->dir, 64, 80, hMarteauTombantBossARZ, INSTR_HORS_MAP);
				else
					CreePerso(p->x, p->y - 17, p->dir, 64, 80, hMarteauTombantBossARZ, INSTR_HORS_MAP);
			}

			CalcVirgule(p->y, p->virguleY);
		}
	}

	if (d->phase == 4 && p->cpt >= 64)
	{
		if (p->dir == -1)
			p->x += 64;
		p->dir = 1;
		if (p->cpt == 64)
			p->vy = 512, p->vx = 256;

		if (p->cpt <= 96)
		{
			p->vy -= 32;
			p->virguleY += p->vy;
			CalcVirgule(p->y, p->virguleY);
			if (p->vy <= 0)
			{
				p->vx += 16;
				p->virguleX += p->vx;
				CalcVirgule(p->x, p->virguleX);
			}
		}
		else
		{
			if (p->cpt == 97)
			{
				j = gblJ;
				gblJ = TrouveIDPersoDepuisNoObjet(cameraViewportHighestPriorite);
				if (gblJ != -1) //Valide?
					DetruitPerso(&persos[id_perso[gblJ]]);
				gblJ = j;

				//Création d'un objet qui restaure le viewport
				i = CreePerso(p->x, p->y, -1, 16, 16, hCameraViewportEtendu, INSTR_HORS_MAP);
				persos[i].noObjet = 0xffff;
				PARAM(persos + i)->param1 = 0;
				PARAM(persos + i)->param2 = 0;
				PARAM(persos + i)->param3 = TILE * (TABLE_X + 1);
				PARAM(persos + i)->param4 = TILE * (TABLE_Y + 1);
				JoueMusique(gblLI->nom_musique, 0, 0);
			}

			if (p->vx < 1024)
				p->vx += 16;
			p->virguleX += p->vx;
			CalcVirgule(p->x, p->virguleX);
		}
	}
}

const short temp_table1[] =
{
	0,
	0,
	1,
	2,
	2,
	3,
	5,
	6,
	7,
	9,
	10,
	12,
	14,
	15,
	17,
	20,
	22,
	24,
	27,
	29,
	32,
	35,
	37,
	40,
	44,
	47,
	50,
	53,
	57,
	60,
	64,
	68,
	72,
	76,
	80,
	84,
	88,
	92,
	97,
	101,
	106,
	110,
	115,
	119,
	124,
	129,
	134,
	139,
	143,
	148,
	153,
	158,
	163,
	168,
	173,
	177,
	182,
	187,
	191,
	196,
	201,
	205,
	209,
	213,
	218,
	221,
	225,
	229,
	232,
	236,
	239,
	242,
	244,
	247,
	249,
	251,
	252,
	253,
	254,
	255,
	255,
	255,
	255,
	254,
	253,
	252,
	250,
	249,
	246,
	244,
	241,
	238,
	235,
	232,
	229,
	225,
	221,
	217,
	213,
	209,
	205,
	200,
	196,
	191,
	186,
	182,
	177,
	172,
	167,
	162,
	158,
	153,
	148,
	143,
	138,
	133,
	128,
	124,
	119,
	114,
	110,
	105,
	101,
	96,
	92,
	88,
	83,
	79,
	75,
	71,
	67,
	64,
	60,
	56,
	53,
	49,
	46,
	43,
	40,
	37,
	34,
	31,
	29,
	26,
	24,
	21,
	19,
	17,
	15,
	13,
	11,
	10,
	8,
	7,
	5,
	4,
	3,
	2,
	1,
	0,
	0,
	0
};

const short temp_table2[] =
{
	256,
	255,
	255,
	255,
	255,
	255,
	255,
	255,
	255,
	255,
	255,
	255,
	255,
	255,
	255,
	255,
	255,
	254,
	254,
	254,
	253,
	253,
	253,
	252,
	252,
	251,
	250,
	250,
	249,
	248,
	247,
	246,
	245,
	244,
	243,
	241,
	240,
	238,
	236,
	234,
	232,
	230,
	228,
	226,
	223,
	220,
	217,
	214,
	211,
	208,
	204,
	201,
	197,
	192,
	188,
	184,
	179,
	174,
	169,
	164,
	158,
	152,
	146,
	140,
	134,
	127,
	120,
	113,
	106,
	98,
	91,
	83,
	75,
	67,
	58,
	49,
	41,
	32,
	23,
	13,
	4,
	-5,
	-14,
	-23,
	-32,
	-41,
	-50,
	-59,
	-67,
	-75,
	-83,
	-91,
	-99,
	-106,
	-114,
	-121,
	-127,
	-134,
	-141,
	-147,
	-153,
	-158,
	-164,
	-169,
	-174,
	-179,
	-184,
	-189,
	-193,
	-197,
	-201,
	-205,
	-208,
	-212,
	-215,
	-218,
	-221,
	-223,
	-226,
	-228,
	-231,
	-233,
	-235,
	-237,
	-238,
	-240,
	-241,
	-243,
	-244,
	-245,
	-246,
	-247,
	-248,
	-249,
	-250,
	-251,
	-251,
	-252,
	-252,
	-253,
	-253,
	-254,
	-254,
	-254,
	-254,
	-255,
	-255,
	-255,
	-255,
	-255,
	-255,
	-255,
	-255,
	-255,
	-255,
	-255,
	-255,
	-255,
	-255,
	-255,
	-255,
	-255,
};

//Point d'appui du balancier
void hBalancier(SONIC *s, PERSO *p, u32 msg)
{
	DONNEES_STD *d = (void *) p->donnees;
	u32 angle, i, j, x, y, cx, cy, til, pal2, pal3;

	if (p->etat)
	{
		i = (nTimer + p->vx) % 324;

		//J = prochaine frame
		j = (nTimer + 1 + p->vx) % 324;
	}
	else
	{
		i = (nTimer + p->vx + 162) % 324;

		//J = prochaine frame
		j = (nTimer + 1 + p->vx + 162) % 324;
	}

	if (i < 162)
		angle = i;
	else
		angle = 161 - (i - 162);

	if (j >= 162)
		j = 161 - (j - 162);

	cx = p->x + p->vx * 16 + 40;
	cy = p->y + 8;
	x = cx + (((p->vx + 1) * temp_table2[angle]) >> 4) - 24;
	y = cy + (((p->vx + 1) * temp_table1[angle]) >> 4) - 8;

	pal3 = AjPalette(Pal_3_palette);
	til = AjSprite(pont_tiles + 1 * 16 * 8, 16 * 16 * 3);
	for (i = 0; i < 3; i++)
		CreeObjetStandard(x + (i << 4), y, til + i * 4, pal3, TAILLE_16x16);

	//Vérifier les collisions avec les anciennes valeurs. param5==collé à la PF. Marge de 8, trop certainement.
	if
		(
		s->x + s->largeur >= x
		&&        s->x < x + 48
		&&        s->y + s->hauteur >= y - (PARAM(p)->param5 * 4)
		&&        s->y + s->hauteur <= y + 8
		&&        s->vy >= 0
		)
	{
bon_quand_meme:
		EssaieDeCrocher(s);
		s->angle = 0;
		i = cx + (((p->vx + 1) * temp_table2[j]) >> 4) - 24;
		s->x += i - x;
		i = cy + (((p->vx + 1) * temp_table1[j]) >> 4) - 8;
		s->y = i - s->hauteur;
		PARAM(p)->param5 = 1;
	}
	else if (PARAM(p)->param5)
	{
		if (s->x + s->largeur > x && s->x < x + 48 && s->y + s->hauteur > y && s->y < y + 16)
			goto bon_quand_meme;
		else
			PARAM(p)->param5 = 0;
	}

	x = cx;
	y = cy;
	CreeObjetStandard(x - 8, y - 8, AjSprite(pont_tiles + 4 * 16 * 8, 16 * 16), pal3, TAILLE_16x16);
	pal2 = AjPalette(Pal_2_palette);
	til = AjSprite(Pal_2_16x16_tiles + 16 * 8 * 7, 16 * 16);
	for (i = 0; i <= p->vx; i++)
	{
		CreeObjetStandard
			(
			x + ((i * temp_table2[angle]) >> 4) - 8,
			y + ((i * temp_table1[angle]) >> 4) - 8,
			til,
			pal2,
			TAILLE_16x16
			);
	}
}
