#include "commun.h"

unsigned char lstBornes[10];

void ColleAuSol(PERSO *p)
{
	if (!MoteurCollisionObjets(p, 0, 15))
		p->y -= 15;
}

//Handlers créeperso (CP)
void cpBague(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 1, 16, 16, hBague, INSTR_SEULEMENT_ECRAN);
}

void cpRoller(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 1, 32, 32, hRoller, INSTR_COLLISION_DECOR);
}

void cpPont(SPRITE_LISTE *sl)
{
	int i, j = 0, k, l;
	PERSO *p = NULL;

	//    num_sprite_actif=LIMITE_PERSOS-2;
	InserePerso(13);        //On a besoin de 14 sprites

	//En tête de liste (0): gestionnaire principal du pont
	l = CreePersoID(12, sl->x + (6 << 4), sl->y, 1, 16, 16, hPont, INSTR_SEULEMENT_ECRAN | INSTR_HORS_MAP);
	for (i = 0; i < 12; i++)
	{
		k = j;
		j = CreePersoID
			(
			11 - i,
			sl->x + (i << 4),
			sl->y,
			1,
			16,
			16,
			hPont,
			INSTR_SEULEMENT_ECRAN | ((i == 11 || i == 0) ? 0 : INSTR_HORS_MAP)
			);
		if (i == 0)
			p = &persos[j];

		//Père
		PARAM(persos + j)->param1 = 0;

		//Element suivant
		if (i > 0)
			PARAM(persos + k)->param2 = (u32) & persos[j];

		//        else
		//            PARAM(persos+k)->param2=(u32)NULL;
	}

	PARAM(persos + j)->param2 = 0;

	//Paramétrage du sprite maitre
	PARAM(persos + l)->param1 = (u32) p;
	PARAM(persos + l)->param2 = 0;

	//Resette ce qui est au fond du pont
	//    tableauMap[yyy][xxx>>3]|=(1<<(xxx&7));
	//    tableauMap[yyy][(xxx+11)>>3]|=(1<<((xxx+11)&7));
}

/*void cpFinPont(u32 xxx, u32 yyy)      {
cpPont(xxx-11,yyy);
}*/

//Param1: force du bumper

//Param2: bit0: couleur (0=jaune, 1=rouge), bit1: collage auto au sol (0=oui, 1=non)
void cpBumper(SPRITE_LISTE *sl)
{
	u16 i;
	i = CreePerso(sl->x, sl->y, 1, 28, 16, hBumper, INSTR_SEULEMENT_ECRAN);
	if (!(sl->param2 & 2))
		ColleAuSol(&persos[i]);
	PARAM(persos + i)->param2 = sl->param2 & 1;

	//Angle = puissance de renvoi
	persos[i].angle = sl->param1;
}

void CreePlateForme(SPRITE_LISTE *sl, int motif)
{
	u32 i, j;

	// Cet objet utilise le bloc qui se trouve juste après pour la configuration:
	// -taille mouvement (10 bits): en nombre de cases (16x16), dont 1 bit de signe
	// -mouvement (2 bits): 0=fixe, 1=horizontal, 2=vertical, 3=tombe
	// -timing (2 bits): 0=1/2, 1=1, 2=2
	i = CreePerso(sl->x, sl->y, 1, 64, 32, hPlateForme, 0);
	j = sl->param1;
	PARAM(persos + i)->param3 = j & 511;
	if (j & 512)
		persos[i].dir = -1;
	else
		persos[i].dir = 1;
	PARAM(persos + i)->param4 = (j >> 10) & 3;
	persos[i].etat = (j >> 12) & 3;

	PARAM(persos + i)->param1 = (PARAM(persos + i)->param3 << 4) - 64;
	persos[i].vy = persos[i].depY;

	//Il faut reculer la plate-forme de 32 pixels (64/2). Elle dépassera donc le point de départ de 32 pixels.
	if (((j >> 10) & 3) == 1)
		persos[i].x -= persos[i].dir * 64 / 2;
	else if (((j >> 10) & 3) == 2)
	{
		PARAM(persos + i)->param1 = (PARAM(persos + i)->param3 << 4) - 64;
		persos[i].vy += persos[i].dir * 64 / 2;
		persos[i].y += persos[i].dir * 64 / 2;
		persos[i].dir = -persos[i].dir;
	}

	PARAM(persos + i)->param6 = motif;
}

void cpPlateFormeGHZ(SPRITE_LISTE *sl)
{
	CreePlateForme(sl, 0);
}

void cpPFCroulante(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 1, 16 * 6, 16 * 5, hPFCroulante, 0);
}

void cpPFCroulante2(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, -1, 16 * 6, 16 * 5, hPFCroulante, 0);
}

//Param2: bit0: collage auto au sol (0=oui, 1=non)
void cpPiques(SPRITE_LISTE *sl)
{
	int id;
	id = CreePerso(sl->x, sl->y, 1, 40, 32, hPiques, INSTR_SEULEMENT_ECRAN);
	persos[id].x -= 16, persos[id].y -= 16;
	if (!(sl->param2 & 1))
		ColleAuSol(&persos[id]);
}

//Param2: bit0: collage auto au sol (0=oui, 1=non)
void cpPiquesMortelles(SPRITE_LISTE *sl)
{
	int id;
	id = CreePerso(sl->x, sl->y, 2, 40, 32, hPiques, INSTR_SEULEMENT_ECRAN);
	persos[id].x -= 16, persos[id].y -= 16;
	if (!(sl->param2 & 1))
		ColleAuSol(&persos[id]);
}

void cpBalancier(SPRITE_LISTE *sl)
{

	//    CreeBalancier(sl,sl->param1,sl->param2);
	u32 i, j;
	j = sl->param1;

	//40=24+16 (une boule + plate-forme). 24=16+8 (une boule + pf)
	i = CreePerso
		(
		sl->x - j * 16 - 32,
		sl->y,
		j,
		j * 16 * 2 + 40 + 40,
		j * 16 + 24 + 8,
		hBalancier,
		INSTR_SEULEMENT_ECRAN
		);
	persos[i].etat = sl->param2;
	persos[i].vy = sl->param3;
}

/*
cpAvance (0x2000)
Permet de créer un personnage en dehors de la zone de détection (écran). Paramètres:
A droite: offset horizontal (en blocs de 16x16).
Bits 0-13: offset. Bit 14: signe (1=négatif).
En bas: offset vertical.
En bas à droite: numéro d'événement.
Ceci opérera comme si le handler avait été géré dans sa zone de collision. Il ne sera
alors plus géré lorsque Sonic le verra.
*/

/*void cpAvance(u32 xxx, u32 yyy)         {
u32 avX, avY, event;
void *fct;

avX=Map[yMap+4][xMap+5]&0x7fff;
if (Map[yMap+4][xMap+5]&0x4000)        avX=-(avX&~0x4000);
avY=Map[yMap+5][xMap+4]&0x7fff;
if (Map[yMap+5][xMap+4]&0x4000)        avY=-(avY&~0x4000);
event=Map[yMap+5][xMap+5]&0x7fff;

//Les objets en question n'existent plus
SET_TABLEAU(xxx+1,yyy+1);
SET_TABLEAU(xxx,yyy+1);
SET_TABLEAU(xxx,yyy);

if (event>=0x2000 && (event-0x2000)<taille_handlers_cp)
fct=(void*)handlersCreePerso[event-0x2000];
else if (event>=0x3000 && (event-0x3000)<ExtCreePersoTaille)
fct=(void*)ExtCreePerso[event-0x3000];
else
return;

//     xMap+=avX, yMap+=avY;
//Pointer sur le bloc où se trouve l'info de sorte que des paramètres soient toujours possibles
xMap++, yMap++;
xxx+=avX, yyy+=avY;
if (!LIT_TABLEAU(xxx,yyy))      {
//Perso qui sera détruit automatiquement, libérant la case info lorsque Sonic s'en éloigne trop.
//            CreePerso((xxx-avX)<<4,(yyy-avY)<<4,1,16,16,hNul,0);
//DEBOGAGE SEULEMENT
gblDebug_ID=event-0x2000;
Exec(fct,xxx,yyy);
}
SET_TABLEAU(xxx,yyy);
}*/

//Param2: bit0: collage auto au sol (0=oui, 1=non)
void CreeTele(SPRITE_LISTE *sl, u32 type)
{
	u32 i;
	i = CreePerso(sl->x, sl->y, 1, 32, 30, hTele, INSTR_SEULEMENT_ECRAN);
	persos[i].y -= 16;
	persos[i].etat = type;
	if (!(sl->param2 & 1))
		ColleAuSol(&persos[i]);
}

void cpTele10bagues(SPRITE_LISTE *sl)
{
	CreeTele(sl, 1);
}

void cpTeleInvincible(SPRITE_LISTE *sl)
{
	CreeTele(sl, 2);
}

void cpTeleBouclier(SPRITE_LISTE *sl)
{
	CreeTele(sl, 3);
}

void cpTele1up(SPRITE_LISTE *sl)
{
	CreeTele(sl, 4);
}

void cpTeleRapide(SPRITE_LISTE *sl)
{
	CreeTele(sl, 5);
}

void cpBorne(SPRITE_LISTE *sl)
{
	u32 i;

	/*    for (i=0;gblLI->spi.liste+i<=sl;i++)      {
	if (gblLI->spi.liste[i].type==)
	}
	Debug("%i",i);*/

	//Attention: ne pas placer d'objets le long de la borne!
	i = CreePerso(sl->x, sl->y - (2 << 4), 1, 16, 64, hBorne, INSTR_SEULEMENT_ECRAN);
	if (lstBornes[sl->param1 - 1])
		PARAM(persos + i)->param1 = 60;

	//    persos[i].depY+=32;
	/*    if (LIT_TABLEAU(xxx,yyy+1))
	PARAM(persos+i)->param1=60;
	else
	SET_TABLEAU(xxx,yyy+1);
	ColleAuSol(&persos[i]);*/
}

void cpBombardier(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, -1, 45, 25, hBuzzbomber, 0);
}

void cpEnboule1(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 1, 16, 16, hEnboule, INSTR_SEULEMENT_ECRAN);
}

void cpEnboule2(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, -1, 16, 16, hEnboule, INSTR_SEULEMENT_ECRAN);
}

void cpRocher(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 1, 48, 32, hRocher, INSTR_SEULEMENT_ECRAN);
}

void cpCrabbot(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 1, 44, 31, hCrabbot, INSTR_SEULEMENT_ECRAN);
}

//Paramètres:
//A droite: vitesse initiale
//En bas: offset vertical (1 bit signe); défaut 256

//En bas à droite: écart de détection; défaut 64
void cpPirhanabot(SPRITE_LISTE *sl)
{
	int i, j;
	DONNEES_PIRHANABOT *d;
	i = CreePerso(sl->x, sl->y, 1, 30, 31, hPirhanabot, 0);
	d = (void *) persos[i].donnees;
	d->vy_base = -(sl->param1);
	j = sl->param2;
	if (j > 0)
	{
		d->limite = j & 0x3FFF;
		if (j & 0x4000)
			d->limite = -d->limite;
	}
	else
		d->limite = 256;
	persos[i].y += d->limite;
	d->arret = 1;
	j = sl->param3;
	if (j > 0)
		d->zone_detection = j & 0x7FFF;
	else
		d->zone_detection = 64;
}

//Param1: force du bumper

//Param2: bit0: couleur (0=jaune, 1=rouge), bit1: collage auto au sol (0=oui, 1=non)
void cpBumperDroite(SPRITE_LISTE *sl)
{
	u16 i, j;

	//Bit 9 (512): signe
	i = CreePerso(sl->x, sl->y, 1, 16, 28, hBumperHoriz, INSTR_SEULEMENT_ECRAN);
	persos[i].y -= 16;
	if ((sl->param2) & 2)
		ColleAuSol(&persos[i]);
	PARAM(persos + i)->param2 = sl->param2 & 1;

	//Angle = puissance de renvoi
	j = sl->param1;
	if (j & 0x200)
	{

		//param 1 + 512 pour l'inverser de sens
		persos[i].angle = -(j & 0x1ff);
		persos[i].x += 16;
	}
	else
		persos[i].angle = j & 0x1ff;
}

//Param1: direction/Vitesse (<<8)
//Param2: Vitesse de mouvement (<<6)

//Param3: Longueur (*32)
void cpGHPoteauMonteDescend(SPRITE_LISTE *sl)
{
	u32 i;
	i = CreePerso(sl->x, sl->y, 1, 64, sl->param3 * 32 + 32, hGHPoteauMD, 0);
	PARAM(persos + i)->param1 = sl->param1; //Direction
	PARAM(persos + i)->param2 = sl->param2; //Vitesse
	PARAM(persos + i)->param3 = sl->param3; //Hauteur
	persos[i].vy = 0;
	persos[i].angle = 0;
	if (sl->param2 == 0)
		ErreurFatale("handlers_cp.c :: cpGHPoteauMonteDescend", "Le param2 (vitesse) ne doit pas etre nul");
}

void cpCameleon(SPRITE_LISTE *sl)
{
	u32 i;
	i = CreePerso(sl->x, sl->y, -1, 48, 24, hCameleon, 0);
	persos[i].vy = 1 << 3;
}

void cpCameleon2(SPRITE_LISTE *sl)
{
	u32 i;
	i = CreePerso(sl->x, sl->y, -1, 40, 40, hCameleon2, 0);
	persos[i].vy = 1 << 3;
}

void cpPiqueBois(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y - 4, 0, 16 * 8, 32, hPiqueBois, 0);
}

void RemplitParamsCameraViewport(SPRITE_LISTE *sl, int i)
{
	if (sl->param1 == 0xffff)
		PARAM(persos + i)->param1 = 0;
	else
		PARAM(persos + i)->param1 = (sl->param1 + 1) * TILE;

	if (sl->param2 == 0xffff)
		PARAM(persos + i)->param2 = 0;
	else
		PARAM(persos + i)->param2 = (sl->param2 + 1) * TILE;

	if (sl->param3 == 0xffff)
		PARAM(persos + i)->param3 = TILE * (TABLE_X + 1);
	else
		PARAM(persos + i)->param3 = (sl->param3 + 1) * TILE - 1;

	if (sl->param4 == 0xffff)
		PARAM(persos + i)->param4 = TILE * (TABLE_Y + 1);
	else
		PARAM(persos + i)->param4 = (sl->param4 + 1) * TILE - 1;
}

void cpCameraViewport(SPRITE_LISTE *sl)
{
	int i;
	i = CreePerso(sl->x, sl->y, -1, 16, 16, hCameraViewport, INSTR_SEULEMENT_ECRAN);
	RemplitParamsCameraViewport(sl, i);
}

void cpCameraViewportEtendu(SPRITE_LISTE *sl)
{
	int i;
	i = CreePerso(sl->x, sl->y, -1, 16, 16, hCameraViewportEtendu, 0);
	RemplitParamsCameraViewport(sl, i);
}

/*void cpCameraViewportGauche(SPRITE_LISTE *sl)         {
int i;
i=CreePerso(sl->x,sl->y,-1,16,16,hCameraViewport,0);
PARAM(persos+i)->param4=(sl->param4+1)*TILE-1;
}*/
void cpBossGHZ(SPRITE_LISTE *sl)
{
	int i;
	i = CreePerso(sl->x, sl->y, -1, 64, 49, hBossGHZ, 0);
	PARAM(persos + i)->param6 = 8;
}

void cpArrivee(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 1, 48, 56, hArrivee, 0);
}

void cpColonneARZ(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 1, 654, 48, hColonneARZ, INSTR_SEULEMENT_ECRAN);
}

void cpHandrill(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 1, 32, 40, hHandrill, INSTR_SEULEMENT_ECRAN);
}

void cpLanceFleches(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 1, 32, 16, hLanceFleches, 0);
}

void cpHandrillCreux(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, -1, 64, 40, hHandrillCreux, 0);
}

void cpMinibumperARZ(SPRITE_LISTE *sl)
{
	u32 i;
	i = CreePerso(sl->x, sl->y, sl->param1, 56, 16, hMinibumperARZ, INSTR_SEULEMENT_ECRAN);
	if (sl->param2 == 0)
		PARAM(persos + i)->param2 = 4;
	else
		PARAM(persos + i)->param2 = sl->param2;
}

void cpBunBun(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 0, 24, 16, hBunBun, 0);
}

//param1: vitesse (-128, 0, +128)
void cpPirhana(SPRITE_LISTE *sl)
{
	int i;
	if (sl->param1)
		i = 128;
	else
		i = -128;
	CreePerso(sl->x, sl->y, i, 32, 24, hPirhana, 0);
}

void cpPlateFormeARZ(SPRITE_LISTE *sl)
{
	CreePlateForme(sl, 1);
}

void cpPFCroulanteARZ(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 0, 64, 32, hPFCroulanteARZ, INSTR_SEULEMENT_ECRAN);
}

void cpColonneTombante(SPRITE_LISTE *sl)
{
	if (sl->param1 < 64)
	{
		ErreurFatale("handlers_cp :: cpColonneTombante", "param1 doit contenir la hauteur de la colonne (>=64)");
	}

	CreePerso(sl->x, sl->y, 0, 56, sl->param1, hColonneTombante, 0);
}

/*void cpBoutColonneTombante(SPRITE_LISTE *sl)         {
CreePerso(sl->x,sl->y,0,32,36,hBoutColonneTombante,INSTR_SEULEMENT_ECRAN);
}*/
void cpBulleSousEau(SPRITE_LISTE *sl)
{
	CreePerso(sl->x, sl->y, 0, 32, 32, hBulleSousEau, INSTR_SEULEMENT_ECRAN);
}

void cpBoutColonne(SPRITE_LISTE *sl)
{
	if (sl->param1 < 32)
	{
		ErreurFatale("handlers_cp :: cpColonneTombante", "param1 doit contenir la hauteur de la colonne (>=32)");
	}

	CreePerso(sl->x, sl->y, 0, 56, sl->param1, hBoutColonne, 0);
	if (sl->param2)
		CreePerso(sl->x + 16, sl->y + sl->param1 - 32, 0, 32, 36, hBoutColonneTombante, INSTR_HORS_MAP);
}

//Plate-formes synchrones, plus évoluées que les plate-formes standard
//Param1: amplitude de mouvement (pixels). Bit15: signe
//Param2: Bit0: mouvement inverse (droite-gauche <-> gauche-droite), Bits1-2: 0=rien, 1=mouvement horizontal, 2=vertical, 3=tombe

//Param4: Motif (0=GHZ, 1=ARZ)
void cpPlateFormePlus(SPRITE_LISTE *sl)
{
	u32 i, j;
	i = CreePerso(sl->x, sl->y, 1, 64, 64, hPlateFormePlus, 0);
	persos[i].vx = persos[i].depX;
	persos[i].vy = persos[i].depY;
	PARAM(persos + i)->param1 = sl->param1 & 0x7fff;
	if (sl->param1 & 0x8000)
	{
		if (((sl->param2 & 6) >> 1) == 1)
			persos[i].vx -= PARAM(persos + i)->param1;
		else if (((sl->param2 & 6) >> 1) == 2)
			persos[i].vy -= PARAM(persos + i)->param1;
	}

	PARAM(persos + i)->param2 = sl->param2 & 7;
	if (sl->param3 == 0 && ((sl->param2 & 6) >> 1) != 3)
	{
		ErreurFatale
			(
			"handlers_cp :: cpPlateFormePlus",
			"param3 doit contenir la vitesse de mouvement de la plate-forme"
			);
	}

	PARAM(persos + i)->param3 = sl->param3 & 255;
	PARAM(persos + i)->param4 = sl->param4 | 0x1000;
}

void cpSpiner(SPRITE_LISTE *sl)
{
	u32 i;
	if (sl->param1 & 0x8000)
		i = -(sl->param1 & 0x7fff);
	else
		i = (sl->param1 & 0x7fff);
	CreePerso(sl->x, sl->y, i, 64, 24, hSpiner, INSTR_SEULEMENT_ECRAN);
}

//Param1: nombre d'éléments du balancier

//Param3: décalage d'angle (%360)
void cpBalancierRond(SPRITE_LISTE *sl)
{
	u32 i, j;
	j = sl->param1;

	//40=24+16 (une boule + plate-forme). 24=16+8 (une boule + pf)
	i = CreePerso
		(
		sl->x - j * 16 - 40,
		sl->y - j * 16 - 24,
		j,
		j * 16 * 2 + 40 + 40,
		j * 16 * 2 + 24 + 24,
		hBalancierRond,
		INSTR_SEULEMENT_ECRAN
		);
	persos[i].vy = sl->param3;
}

void cpBossARZ(SPRITE_LISTE *sl)
{
	int i;
	DONNEES_BOSS_ARZ *d;
	i = CreePerso(sl->x, sl->y, 0, 16, 16, hBossARZ, 0);
	d = (void *) persos[i].donnees;
	d->energie = 8;
}

const void *handlersCreePerso[] =
{

	//    cpAvance,
	NULL,
	cpBague,
	cpRoller,
	cpPont,
	NULL,        //Inutile maintenant
	cpBumper,
	cpPlateFormeGHZ,
	cpPFCroulante,
	cpPiques,
	cpBalancier,
	NULL,        //Inutile maintenant
	cpTele10bagues,
	cpTeleInvincible,
	cpTeleBouclier,
	cpTele1up,
	cpTeleRapide,
	cpBombardier,
	cpPFCroulante2,
	cpBorne,
	cpEnboule1,
	cpEnboule2,
	cpRocher,
	cpCrabbot,
	cpPirhanabot,
	cpPiquesMortelles,
	cpBumperDroite,
	cpGHPoteauMonteDescend,
	cpCameleon,
	cpCameleon2,
	cpPiqueBois,
	cpCameraViewport,
	cpCameraViewportEtendu,
	cpBossGHZ,
	cpArrivee,
	cpColonneARZ,
	cpHandrill,
	cpLanceFleches,
	cpHandrillCreux,
	cpMinibumperARZ,
	cpBunBun,
	cpPirhana,
	cpPlateFormePlus,

	//    cpPlateFormeARZ,
	cpPFCroulanteARZ,
	cpColonneTombante,
	cpBoutColonne,

	//    cpBoutColonneTombante,
	cpBulleSousEau,
	cpSpiner,
	cpBalancierRond,

	//    cpBumperGauche
	cpBossARZ,
};

const int taille_handlers_cp = sizeof(handlersCreePerso) / sizeof(handlersCreePerso[0]);
