#include "glover.h"
//#include "puzzles.h"
//#include "cameo.h"
//#include "bosses.h"

#if GOLDCD==NO
//	#define WEAK_SPBOSS
//  #define DEAD_SPBOSS	// for watching the end cameo
#endif

void SpBossQuake(int shifts);


int boss_n_powers = 0;
int old_boss_n_powers;
UBYTE boss_bar_flicker=0;

SPRITEX *SPR_powbars[4];
NEWMODEL *cancer_claw_psa = NULL;
NEWMODEL *kirk_bubble1_psa = NULL;
NEWMODEL *kirk_bubble2_psa = NULL;
NEWMODEL *willy_fireball_psa = NULL;
LOADEDMODEL *spboss_panel_model = NULL;
SPRITEX *SPR_crosshair;

ENEMYPOS *ca_pie_enemy;
ENEMYPOS *ca_bomb_enemy;
ENEMYPOS *ca_glove_enemy;
ENEMYPOS *ca_piano_enemy;
int caboss_spin;

/*
int SpBossCtrl.pan_cols[12];
int SpBossCtrl.pan_vels[12];
int SpBossCtrl.shield = 0;
*/


BEHAVIOUR_PHYSICS	KirkBehaviour=
{
//	10*4096,			// mass;
	0,					// mass;
	0,					//GRAVITY,			// gravity;
	4096,				// bounce
	4096,				// drag	** NONE **
	4096*200,			// maxspeed
	25*4096,			// radius
	4096*200,				// accel
};

// copied from the rubber ball
/*
BEHAVIOUR_PHYSICS	CancerBehaviour=
	{
		10*4096,			// mass;
		GRAVITY,			// gravity;
		.9*4096,			// bounce
		4096*0.91,			//(4096-(4096*0.05)),	// drag
		1*(19*4096*NORMAL_SCALE)*1,			// maxspeed (radius*2)
		10*4096,			// radius
		4096,				// accel
		(0.4*4096),			// max squash amount
	};
*/

/*
BEHAVIOUR_PHYSICS	WillyBehaviour=
	{
		10*4096,			// mass;
		GRAVITY,			// gravity;
//		.9*4096,			// bounce
//		.2*4096,			// bounce
		0,
		4096*0.91,			//(4096-(4096*0.05)),	// drag
		1*(19*4096*NORMAL_SCALE)*1,			// maxspeed (radius*2)
		WILLY_COLL_RAD*4096,			// radius
		4096,				// accel
		(0.4*4096),			// max squash amount
	};
*/

// atboss instruction lists
/*
ENEMY SELWYN_WHALE 0 36 0 0 0
	NORMAL_INSTRUCTION		NME_DONTATTACK		1 60 0 0 ALWAYS 0 0
	NORMAL_INSTRUCTION		NME_WAIT			50 0 1 0 ALWAYS	0 0

	NORMAL_INSTRUCTION		NME_SPECIAL			-1 -1 13 2 ALWAYS 0 0			(startmove)
	NORMAL_INSTRUCTION		NME_SPECIAL			 1 -1 15 1 ALWAYS 0 0			(walk)

	NORMAL_INSTRUCTION		NME_FOLLOWPLAYER	0 0 0 0 0 65 ALWAYS 0 0			(hand, moveaccel)
	NORMAL_INSTRUCTION		NME_BRANCH			-1 4 0 0 ALWAYS 0 0 



	CONDITIONAL_INSTRUCTION	NME_ATTACK			0 0 0 0 IF_HAND_IN_RANGE 0 150

	ATTACK_INSTRUCTION		NME_FACEPLAYER		-1 0 0 64 ALWAYS 0 0			(hand)
	ATTACK_INSTRUCTION		NME_BRANCH			-1 7 0 0 SOMETIMES 500 0 

	ATTACK_INSTRUCTION		NME_SPECIAL			-1 0 0 1048578 ALWAYS 0 0		(special1 (=prejump), face hand, move vel)
	ATTACK_INSTRUCTION		NME_JUMP			-1 0 14 12 0 8388672 ALWAYS 0 0 (jump anim... land anim. land on player, hand)
	ATTACK_INSTRUCTION		NME_SPECIAL			1 -1 15 1 ALWAYS 0 0			(walk again)
	ATTACK_INSTRUCTION		NME_MOVETO2D		-1 0 0 0 200 16385 ALWAYS 0 0	(no anim, accel to 0,0,0)
	ATTACK_INSTRUCTION		NME_ENDATTACK		0 10 0 16384 ALWAYS 0 0			(no anim)

//		fscanf(fp, "%f %f %f %f %d %s %f %f", &x, &y, &z, &i, &f, string, &c1, &c2);


	ATTACK_INSTRUCTION		NME_SPECIAL			-1 -1 12 1048578 ALWAYS 0 0		(start attack, face hand, move vel)
	ATTACK_INSTRUCTION		NME_SPECIAL			1 -1 15 1 ALWAYS 0 0			(walk)
	ATTACK_INSTRUCTION		NME_MOVETO2D		-1 0 0 0 200 16385 ALWAYS 0 0	(don't anim, accel)
	ATTACK_INSTRUCTION		NME_ENDATTACK		0 10 0 16384 ALWAYS 0 0			(aon't anim)
END_ENEMY
*/

#define DRIVEFLAGS_LANDHUG 1
typedef struct enemyDRIVESTR
{
	int maxvel;
	int maxturn;

	int accel;
	int decel;

	int flags;
	int gravity;
}enemyDRIVESTR;

int enemyDriveTowards(ENEMYPOS *enemy, VECTOR *target, enemyDRIVESTR *drive)
{
	SHORT ya;
	SHORT dya;
	VECTOR dvec;
	int targspeed;
	int hag;

	if(enemy->pos.vx>>8 == target->vx>> 8 && enemy->pos.vz>>8 == target->vz>> 8)
		return 1;

	dvec.vx = target->vx - enemy->pos.vx;
	dvec.vy = target->vy - enemy->pos.vy;
	dvec.vz = target->vz - enemy->pos.vz;

	ya = (calc_angle( (-dvec.vx)>>8, (-dvec.vz)>>8) & 4095);

	dya = (enemy->ya - ya) & 4095;
	if((dya >= 0 && dya < 2048))
	{
		enemy->ya -= drive->maxturn;
		enemy->ya &= 4095;
		dya = (enemy->ya - ya) & 4095;
		if((dya >= 0 && dya < 2048))
		{
		}
		else
		{
			enemy->ya = ya;
			dya = 0;
		}

	}
	else
	{
		enemy->ya += drive->maxturn;
		enemy->ya &= 4095;
		dya = (enemy->ya - ya) & 4095;
		if((dya >= 0 && dya < 2048))
		{
			enemy->ya = ya;
			dya = 0;
		}
		else
		{
			dya = 4096 - dya;
		}
	}

// ideal speed is maxvel when dya = 0, and 0 when dya = 2048
	targspeed = drive->maxvel * (2048 - dya) >>11;
	if(enemy->vel.vz < targspeed)
	{
		enemy->vel.vz += drive->accel;
		if(enemy->vel.vz > targspeed)
			enemy->vel.vz = targspeed;
	}
	else if(enemy->vel.vz > targspeed)
	{
		enemy->vel.vz -= drive->decel;
		if(enemy->vel.vz < 0)
			enemy->vel.vz = 0;
	}

// Move forwards
	enemy->pos.vx -= (enemy->vel.vz * rsin(enemy->ya)) /4096;
	enemy->pos.vz -= (enemy->vel.vz * rcos(enemy->ya)) /4096;

	if(drive->flags & DRIVEFLAGS_LANDHUG)
	{
		hag = getHeightAt(enemy->pos.vx, enemy->pos.vy + (nmeinfo[enemy->type].hag << 12) - NME_H_FRIG, enemy->pos.vz);

//		if(hag != -1)
//		{
			hag -= NME_H_FRIG;
			if(hag < enemy->vel.vy)
			{
				enemy->pos.vy += hag;
				enemy->vel.vy = 0;
			}
			else
			{
				enemy->pos.vy += enemy->vel.vy;
				enemy->vel.vy += drive->gravity;
			}
/*
		}
		else
		{
			enemy->pos.vy += enemy->vel.vy;
			enemy->vel.vy += drive->gravity;

		}
*/
	}
	return 0;
}

int enemyLandedCheck(ENEMYPOS *enemy, int gravity)
{
	int hag;
	int rv;

	rv = 0;
	hag = getHeightAt(enemy->pos.vx, enemy->pos.vy + (nmeinfo[enemy->type].hag << 12) - NME_H_FRIG, enemy->pos.vz);
//	if(hag != -1)
//	{
		hag -= NME_H_FRIG;

		if(hag < enemy->vel.vy)
		{
			enemy->pos.vy += hag;
			enemy->vel.vy = 0;
			rv = 1;
		}
		else
		{
			enemy->pos.vy += enemy->vel.vy;
			enemy->vel.vy += gravity;
		}
/*
	}
	else
	{
		enemy->pos.vy += enemy->vel.vy;
		enemy->vel.vy += gravity;
	}
*/
	return rv;
}

// Joff's amazing water plume

void enemy_ShootSpray(VECTOR *org, VECTOR *vel)
{
	ENEMYPOS *nme;
	nme = enemy_FindFreeEnemy();
	if(!nme)
		return;
	nme->pos = *org;
	nme->vel = *vel;
	nme->type = BULLET_ENEMY_SPRAY;
	nme->flags = NMEFLAG_ACTIVE + NMEFLAG_ENABLED;
	nme->ticker = 0;
	nme->doing = 0;
}




void enemy_ShootBubble(VECTOR *org, VECTOR *vel, int type)
{
	ENEMYPOS *nme;
	int n;

// The bubbles are pretty high poly, and we get polygon overrun warnings
	n = 0;
	for(nme = nme_list; nme; nme = nme->next)
	{
		if(nme->type == KIRK_BUBBLE1_ENEMY ||nme->type == KIRK_BUBBLE2_ENEMY )
			n++;
		if(n >= 8)
			return;
	}


	nme = enemy_FindFreeEnemy();
	if(!nme)
		return;

	nme->pos = *org;
	nme->vel = *vel;
	nme->flags = NMEFLAG_ACTIVE + NMEFLAG_ENABLED;

	nme->type = type;
	if(type == KIRK_BUBBLE1_ENEMY)
		nme->psa = kirk_bubble1_psa;
	else
		nme->psa = kirk_bubble2_psa;

	nme->ticker = 0;
	nme->doing = 0;
}
void enemy_ShootClaw(VECTOR *org, VECTOR *vel, int ya, int za)
{
	ENEMYPOS *nme;
	nme = enemy_FindFreeEnemy();
	if(!nme)
		return;

//	DB("fire\n");
	nme->psa = cancer_claw_psa;
	nme->pos = *org;
	nme->vel = *vel;
	nme->type = BULLET_ENEMY_CLAW;
	nme->flags = NMEFLAG_ACTIVE + NMEFLAG_ENABLED;
	nme->ticker = 0;
	nme->doing = 0;
	nme->za = za;
	nme->ya = ya;
	nme->xa = 0;
}
void enemy_ShootFireBall(VECTOR *org, VECTOR *vel)
{
	ENEMYPOS *nme;
	nme = enemy_FindFreeEnemy();
	if(!nme)
		return;
	nme->pos = *org;
	nme->vel = *vel;
	nme->flags = NMEFLAG_ACTIVE + NMEFLAG_ENABLED;

	nme->type = WILLY_FIREBALL_ENEMY;
	nme->psa = willy_fireball_psa;

	nme->ticker = 0;
	nme->doing = 0;
}

void SpBoss_Midpoint(ENEMYPOS *nme, int hshift, int vshift)
{
	nme->pathvectors[0].vx = ((nme->pos.vx + glovePos.vx) >>1) + ((RANDOM256()-128) << hshift);

	nme->pathvectors[0].vy = ((nme->pos.vy + glovePos.vy) >>1) + ((RANDOM256()-128) << vshift);
	nme->pathvectors[0].vz = ((nme->pos.vz + glovePos.vz) >>1);
}

void enemy_ShootMissile(VECTOR *org, VECTOR *vel, int ya)
{
	ENEMYPOS *nme;
	nme = enemy_FindFreeEnemy();
	if(!nme)
		return;
	nme->pos = *org;
	nme->vel = *vel;
	nme->type = ROBOT_MISSILE;
	nme->psa = robot_missile_psa;

	nme->ya = ya;	//x800;
	nme->xa =0;


	nme->flags = NMEFLAG_ACTIVE + NMEFLAG_ENABLED;
	nme->ticker = 0;
	nme->doing = 0;

	SpBoss_Midpoint(nme,13,13);
}


void enemy_ShootLaser(VECTOR *org, VECTOR *vel, int ya)
{
	ENEMYPOS *nme;
	nme = enemy_FindFreeEnemy();
	if(!nme)
		return;
	nme->pos = *org;
	nme->vel = *vel;
	nme->type = ROBOT_BOLT;
	nme->psa = robot_laser_psa;

	nme->ya = ya;	//x800;
	nme->xa =0;

	nme->flags = NMEFLAG_ACTIVE + NMEFLAG_ENABLED;
	nme->ticker = 0;
	nme->doing = 0;
	SpBoss_Midpoint(nme,11,11);

}


void enemy_DropSpiders(VECTOR *pos, VECTOR *targ)
{
	ENEMYPOS *nme;
	int i;
	VECTOR dvec;
	LOADEDMODEL *model;

	model = robot_spider_model;

// arrange for 'em to be in a straight line
	SUBVECTOR(&dvec,targ,pos);
	dvec.vy = 0;

//	i = dvec.vz;
//	dvec.vz = dvec.vx;
//	dvec.vx = -i;

	i = dvec.vz;
	dvec.vz = -dvec.vx;
	dvec.vx = i;

	MakeUnit(&dvec);

	for(i = -1; i < 2; i++)
	{
		nme = enemy_FindFreeEnemy();
		if(!nme)
			return;
		nme->pos = *pos;

		nme->vel.vx = i * dvec.vx * 4;	// throw one out each side, & one down the middle
		nme->vel.vy = 0;
		nme->vel.vz = i * dvec.vz * 4;

		nme->pathvectors[0].vx = glovePos.vx + i * dvec.vx * 40;
		nme->pathvectors[0].vy = glovePos.vy;
		nme->pathvectors[0].vz = glovePos.vz + i * dvec.vz * 40;

		nme->type = SPIDERBOMB;
		nme->psa = model->psa;	//robot_spider_psa;
		nme->animinfo.segInfo = model->seg;
		nme->anim.animInfo = &nme->animinfo;


		AddToQueue(&nme->anim,0,YES,NO,4096);	// spiders only have the one anim

		nme->ya = 0;	//x800;
		nme->xa =0;

		nme->flags = NMEFLAG_ACTIVE + NMEFLAG_ENABLED;
		nme->ticker = 0;
		nme->doing = -1;
	}
}




void SprayIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)
	{
		if(!IsBallShield(dvec))
			NMEHurtsGlove(nme, dvec, sphererad);
	}
	else
	{
		NMEPushesBall(nme, dvec, sphererad);	// very light push
	}
}
void ClawIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)
	{
		if(!IsBallShield(dvec))
			NMEHurtsGlove(nme, dvec, sphererad);
	}
	else
	{
		NMEPushesBall(nme, dvec, sphererad);	// very light push
	}
}

void FireBIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)
	{
		if(!IsBallShield(dvec))
			NMEHurtsGlove(nme, dvec, sphererad);
	}
	else
	{
		NMEPushesBall(nme, dvec, sphererad);	// very light push
	}
}

void Bub1IntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)
	{
		if(GloveCtrl.action == HAND_SLAM|| GloveCtrl.action == HAND_SLAM2ST) // && Glover.animInfo->frame>=4)	// the frame check's a tad messy, but WTF?
		{
			EnemyKill(nme);
		}
		else
		{
			NMEPushesGlove(nme, dvec, sphererad);
		}
	}
	else
	{
		NMEPushesBall(nme, dvec, sphererad);	// very light push
	}
}
void Bub2IntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)
	{
//		printf("g-act = %d\n",GloveCtrl.action);
		if(GloveCtrl.action == HAND_SLAM|| GloveCtrl.action == HAND_SLAM2ST) // && Glover.animInfo->frame>=4)	// the frame check's a tad messy, but WTF?
		{
			EnemyKill(nme);
		}
		else
		{
			nme->doing = 1;
			DisconnectGloveFromBall(DISCONNECT_WHEN_JOINED | DISCONNECT_WHEN_BOUNCING | DISCONNECT_WHEN_WALKINGON | DISCONNECT_GENTLY);

			glovePos.vx += (nme->pos.vx - glovePos.vx) /2;
			glovePos.vy += (nme->pos.vy - glovePos.vy) /2;
			glovePos.vz += (nme->pos.vz - glovePos.vz) /2;
	//		NMEPushesGlove(nme, dvec, sphererad);	// tbd - sucks him in
		}
	}
	else
	{
		NMEPushesBall(nme, dvec, sphererad);	// very light push
	}
}


void Update_Spray(ENEMYPOS *nme)
{
	nme->ticker++;

	nme->pos.vx += nme->vel.vx;
	nme->pos.vy += nme->vel.vy;
	nme->pos.vz += nme->vel.vz;
	nme->vel.vy += 1024;

//	if(isPointSolid(nme->pos.vx,nme->pos.vy,nme->pos.vz) || nme->ticker >= 120)	//80)
	if(nme->ticker >= 120)
	{
		nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
		nme->type = 0;
	}
}
void Update_Claw(ENEMYPOS *nme)
{
	nme->ticker++;

//	DB("c\n");
	nme->pos.vx += nme->vel.vx;
	nme->pos.vy += nme->vel.vy;
	nme->pos.vz += nme->vel.vz;
//	nme->vel.vy += 1024;

//	if(isPointSolid(nme->pos.vx,nme->pos.vy,nme->pos.vz) || nme->ticker >= 120)	//80)
	if(nme->ticker >= 120)
	{
		nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
		nme->type = 0;
	}
}


BEHAVIOUR_PHYSICS	bubphysics=
{
//	10*4096,			// mass;
	0,					// mass;
	0,					//GRAVITY,			// gravity;
	3000,				// bounce
	4096,				// drag	** NONE **
	4096*200,			// maxspeed
	25*4096,			// radius
	4096*200,				// accel
};
BEHAVIOUR_PHYSICS	fireballphysics=
{
	0,				// mass;
	4096,					//GRAVITY,
	4000,				// bounce (high)
	4096,				// drag	** NONE **
	4096*200,			// maxspeed
	25*4096,			// radius
	4096*200,				// accel
};


#define BUBBLE_GRAVITY 128;

void Update_Bubble1(ENEMYPOS *nme)
{
	VECTOR oldpos;
	nme->ticker++;

	nme->vel.vy += BUBBLE_GRAVITY;

//	DB("c\n");
	oldpos = nme->pos;
	nme->pos.vx += nme->vel.vx;
	nme->pos.vy += nme->vel.vy;
	nme->pos.vz += nme->vel.vz;
//	nme->vel.vy += 1024;

//	if(isPointSolid(nme->pos.vx,nme->pos.vy,nme->pos.vz) || nme->ticker >= 120)	//80)
	if(nme->ticker >= 600)
	{
		nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
		nme->type = 0;
	}
	nme->pathvectors[0].vx = 800 + (rsin((nme->ticker << 8) & 4095) >> 5);
	nme->pathvectors[0].vy = 800 + (rsin(((nme->ticker << 8)+1365) & 4095) >> 5);
	nme->pathvectors[0].vz = 800 + (rsin(((nme->ticker << 8)+2730) & 4095) >> 5);

	if(nme->ticker < 10)
	{
		nme->pathvectors[0].vx = nme->pathvectors[0].vx  * nme->ticker/10;
		nme->pathvectors[0].vy = nme->pathvectors[0].vy  * nme->ticker/10;
		nme->pathvectors[0].vz = nme->pathvectors[0].vz  * nme->ticker/10;
	}

	enemy_UsePhysics(nme, &bubphysics, &oldpos, 6);
}
void Update_Bubble2(ENEMYPOS *nme)
{
	VECTOR oldpos;

	oldpos = nme->pos;
	nme->ticker++;
	nme->vel.vy += BUBBLE_GRAVITY;

//	DB("c\n");
	nme->pos.vx += nme->vel.vx;
	nme->pos.vy += nme->vel.vy;
	nme->pos.vz += nme->vel.vz;
//	nme->vel.vy += 1024;

//	if(isPointSolid(nme->pos.vx,nme->pos.vy,nme->pos.vz) || nme->ticker >= 120)	//80)
	if(nme->ticker >= 600 && nme->doing != 1)
	{
		nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
		nme->type = 0;
	}
	nme->pathvectors[0].vx = 1500 + (rsin((nme->ticker << 8) & 4095) >> 4);
	nme->pathvectors[0].vy = 1500 + (rsin(((nme->ticker << 8)+1365) & 4095) >> 4);
	nme->pathvectors[0].vz = 1500 + (rsin(((nme->ticker << 8)+2730) & 4095) >> 4);
	if(nme->ticker < 10)
	{
		nme->pathvectors[0].vx = nme->pathvectors[0].vx  * nme->ticker/10;
		nme->pathvectors[0].vy = nme->pathvectors[0].vy  * nme->ticker/10;
		nme->pathvectors[0].vz = nme->pathvectors[0].vz  * nme->ticker/10;
	}


//	DB("ticker %d, nme %8x, rqd scales %d %d %d\n",nme->ticker,(int)&nme->pathvectors[0],nme->pathvectors[0].vx,nme->pathvectors[0].vy,nme->pathvectors[0].vz);
	switch(nme->doing)
	{
	case 1:	// grabbing the glove

		if(nme->ticker >= 0x2000)	// get rid of the double-shrinking problem
			nme->ticker -= 0x1000;

		nme->vel.vx += (glovePos.vx - nme->pos.vx)>>6;	// move bubble towards glove
		nme->vel.vy += (glovePos.vy - nme->pos.vy)>>6;
		nme->vel.vz += (glovePos.vz - nme->pos.vz)>>6;

//		nme->pos.vx += (glovePos.vx - nme->pos.vx)>>2;	// move bubble towards glove
//		nme->pos.vy += (glovePos.vy - nme->pos.vy)>>2;
//		nme->pos.vz += (glovePos.vz - nme->pos.vz)>>2;
		nme->pos = glovePos;

		glovePos.vx = (glovePos.vx + gloveColl.oldPos.vx)/2;	// prevent the glove from moving as much
		glovePos.vy = (glovePos.vy + gloveColl.oldPos.vy)/2;	// (There's some more of this in the interaction routine)
		glovePos.vz = (glovePos.vz + gloveColl.oldPos.vz)/2;
		GloveCtrl.insideBubble = 2;
		break;

	case 0:	// normal bouncy bubbleness	(actually, it's supposed to home in on the glove...)
		break;
	}

	enemy_UsePhysics(nme, &bubphysics, &oldpos, 14);
}


#define PREHIST_BOUND_MINX (-620)
#define PREHIST_BOUND_MAXX ( -15)
#define PREHIST_BOUND_MINZ (-268)
#define PREHIST_BOUND_MAXZ ( 987)
#define FIREBALL_RAD	   (  20)

void Update_FireBall(ENEMYPOS *nme)
{
//	COLLDATA *coll = &bubcol;

	VECTOR oldpos;
	int hag;

	nme->ticker++;
//	nme->vel.vy += fireballphysics.gravity;
	nme->vel.vy += 2048;

//	DB("c\n");
	oldpos = nme->pos;

	nme->pos.vx += nme->vel.vx;
	nme->pos.vy += nme->vel.vy;
	nme->pos.vz += nme->vel.vz;


/*

//	nme->vel.vy += 1024;

//	if(isPointSolid(nme->pos.vx,nme->pos.vy,nme->pos.vz) || nme->ticker >= 120)	//80)
	if( (nme->ticker >= 600 || nme->pos.vz <-200) && nme->doing != 1)
	{
		nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
		nme->type = 0;
	}

	enemy_UsePhysics(nme, &EnemyHeavyPhysics, &oldpos, 18);
*/

	if((nme->pos.vx >>12) < (PREHIST_BOUND_MINX + FIREBALL_RAD))
	{
		nme->pos.vx = (PREHIST_BOUND_MINX + FIREBALL_RAD)<<12;
		if(nme->vel.vx < 0)
			nme->vel.vx = -nme->vel.vx;
	}

	if((nme->pos.vx >>12) > (PREHIST_BOUND_MAXX - FIREBALL_RAD))
	{
		nme->pos.vx = (PREHIST_BOUND_MAXX - FIREBALL_RAD)<<12;
		if(nme->vel.vx > 0)
			nme->vel.vx = -nme->vel.vx;
	}

	if((nme->pos.vz>>12) > (PREHIST_BOUND_MAXZ - FIREBALL_RAD))
	{
		nme->pos.vz = (PREHIST_BOUND_MAXZ - FIREBALL_RAD)<<12;
		if(nme->vel.vz > 0)
			nme->vel.vz = -nme->vel.vz;
	}

// high end of the slope

	if((nme->pos.vz>>12) < (PREHIST_BOUND_MINZ + FIREBALL_RAD))
	{
		nme->pos.vz = (PREHIST_BOUND_MINZ + FIREBALL_RAD)<<12;
		nme->vel.vx = 0;
		nme->vel.vy = 0;
		nme->vel.vz = 0;

		nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
		nme->type = 0;
	}

	if(nme->ticker >=300)
	{
		nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
		nme->type = 0;
	}

// non-physics bounciness

	hag = getHeightAt(nme->pos.vx, nme->pos.vy + (18 << 12) - NME_H_FRIG, nme->pos.vz);
//	if(hag != -1)
//	{
		hag -= NME_H_FRIG;

		if(hag < 0)
		{
			nme->pos.vy += hag;
			if(nme->vel.vy > 0)
				nme->vel.vy = -nme->vel.vy / 2;
		}
//	}


	if(!(nme->ticker & 1))
	{
		New_Debris(DEBRIS_FASTFLAME,&nme->pos,512);
	}
}



void boss_FreeCancerFromJoff();
void boss_FreeKirkFromJoff();
void boss_FreeKirkFromCancer();


#define JOFF_WAIT_TIME 200
#define JOFF_SQUIRT_TIME 20
#define JOFF_LAND_TIME 20
#define JOFF_STUN_TIME	20
#define JOFF_BLAT_TIME	40

#define JOFF_WAITING	0
#define JOFF_WALKING	1
#define JOFF_PREJUMP	3
#define JOFF_JUMPING	4
#define JOFF_LANDED		5
#define JOFF_PRESQUIRT	6
#define JOFF_SQUIRTING	7
#define JOFF_SQUIRTED	8
#define JOFF_RETURNING	9
#define JOFF_STUNNED	10
#define JOFF_BLATTED	11

#define JOFF_JUMP_VEL (-14 * 4096)
#define JOFF_JUMP_FVEL (4 * 4096)



void Update_Joff(ENEMYPOS *enemy)
{
static enemyDRIVESTR joffdrive =
	{
//		10000, 30,
		14000, 30,
		1024,1500,
		DRIVEFLAGS_LANDHUG,
		4096
	};

	switch(enemy->doing)
	{
// Hang around till the end of the cameo
	case JOFF_WAITING:
		enemy->ticker--;
		if(!enemy->ticker)
		{
			AddToQueue(&enemy->anim,NMEANIM_STARTMOVE,NO,YES,4096);
			AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
			enemy->doing = JOFF_WALKING;
		}
		break;

	case JOFF_WALKING:
		enemy->ticker--;
		if(!(enemy->ticker & 7))
		{
			if(!(enemy->ticker & 3))
				sfxPlayFull3D(levelFX, SFX_SELWYNJOFF_FLAP_L,&enemy->pos);
			else
				sfxPlayFull3D(levelFX, SFX_SELWYNJOFF_FLAP_R,&enemy->pos);
		}
		enemyDriveTowards(enemy,&glovePos,&joffdrive);
		if(NMECheckInRange(enemy, &glovePos, 150<<12, 0))
		{
			if(NME_FaceTo(enemy, &glovePos, 40))
			{
				if(RANDOM256() >= 128)
				{
					enemy->ticker = 10;
					enemy->doing = JOFF_PREJUMP;
					AddToQueue(&enemy->anim,NMEANIM_SPECIAL1,NO,YES,4096);
					AddToQueue(&enemy->anim,NMEANIM_JUMP,NO,YES,4096);
				}
				else
				{
					enemy->ticker = 10;
					enemy->doing = JOFF_PRESQUIRT;
					AddToQueue(&enemy->anim,NMEANIM_STARTATTACK,NO,YES,4096);
					AddToQueue(&enemy->anim,NMEANIM_IDLE,YES,YES,4096);
				}
			}
		}
		break;

	case JOFF_PREJUMP:
		enemy->ticker--;
		if(!enemy->ticker)
		{
			enemy->doing = JOFF_JUMPING;
			enemy->vel.vy = JOFF_JUMP_VEL;
			enemy->vel.vz = JOFF_JUMP_FVEL;
		}
		break;

	case JOFF_JUMPING:
		enemy->pos.vx -= (enemy->vel.vz * rsin(enemy->ya)) /4096;
		enemy->pos.vz -= (enemy->vel.vz * rcos(enemy->ya)) /4096;
		if(enemyLandedCheck(enemy,joffdrive.gravity))
		{
			VECTOR temp;
			int hag;
			hag = getHeightAt(enemy->pos.vx, enemy->pos.vy, enemy->pos.vz);
			temp = enemy->pos;
			temp.vy += hag;

			effectsStartNMEBlastRing(&temp,5*4096, 80 * 4096,8);


/*
SFXPlayNME3D(vab, sfx, vol, enemy, pitch, time)
hStampede=NMEPlaySound(levelFX, SFX_GENERALWU_TEST,64,40, -1);
SFX_SELWYNJOFF_ATTACK = 0x30,
SFX_SELWYNJOFF_LAND,
SFX_SELWYNJOFF_FLAP_L,
SFX_SELWYNJOFF_FLAP_R,
SFX_SELWYNCANCER_ATTACK,
SFX_SELWYNKIRK_FLY,
*/
			sfxPlayFull3D(levelFX, SFX_SELWYNJOFF_LAND,&enemy->pos);

			enemy->ticker = JOFF_LAND_TIME;
			AddToQueue(&enemy->anim,NMEANIM_LAND,NO,YES,4096);
			AddToQueue(&enemy->anim,NMEANIM_STARTMOVE,NO,YES,4096);
			AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
			enemy->vel.vx = 0;
			enemy->vel.vy = 0;
			enemy->vel.vz = 0;
			enemy->doing = JOFF_LANDED;
		}
		break;


	case JOFF_SQUIRTED:
	case JOFF_LANDED:
		enemy->ticker--;
		if(!enemy->ticker)
		{
			enemy->doing = JOFF_RETURNING;
		}
		break;

// walk to somewhere roughly in the middle of the level
	case JOFF_RETURNING:
	{
		static VECTOR z = {0,0,0};

		enemy->ticker--;
		if(!(enemy->ticker & 7))
		{
			if(!(enemy->ticker & 3))
				sfxPlayFull3D(levelFX, SFX_SELWYNJOFF_FLAP_L,&enemy->pos);
			else
				sfxPlayFull3D(levelFX, SFX_SELWYNJOFF_FLAP_R,&enemy->pos);
		}
		enemyDriveTowards(enemy,&z,&joffdrive);
		if(NMECheckInRange(enemy, &z, 150<<12, 0))
		{
			enemy->doing = JOFF_WALKING;
		}
		break;
	}

	case JOFF_PRESQUIRT:
		enemy->ticker--;
		if(!enemy->ticker)
		{
			enemy->ticker = JOFF_SQUIRT_TIME;
			enemy->doing = JOFF_SQUIRTING;
		}
		break;

	case JOFF_SQUIRTING:
		NME_FaceTo(enemy, &glovePos, 40);

		if(!(enemy->ticker & 7))
			sfxPlayNME3D(levelFX, SFX_SELWYNJOFF_ATTACK,enemy);

//		if(!(enemy->ticker & 3))
		{
			static VECTOR posref = {0,-5,-30};
			VECTOR pos;
			VECTOR vec;


			EnemyRotatePoint(enemy,&posref, &pos, -1);
			pos.vx = (pos.vx<<12) + enemy->pos.vx;
			pos.vy = (pos.vy<<12) + enemy->pos.vy;
			pos.vz = (pos.vz<<12) + enemy->pos.vz;

			vec.vx = -rsin(enemy->ya) * 6;
			vec.vy = -5000;
			vec.vz = -rcos(enemy->ya) * 6;

			vec.vx += (RANDOM256() - 128)<<5;
			vec.vy += (RANDOM256() - 128)<<4;
			vec.vz += (RANDOM256() - 128)<<5;

			enemy_ShootSpray(&pos, &vec);
		}


		enemy->ticker--;
		if(!enemy->ticker)
		{
			enemy->ticker = 10;


			AddToQueue(&enemy->anim,NMEANIM_STARTMOVE,NO,YES,4096);
			AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
			enemy->vel.vx = 0;
			enemy->vel.vy = 0;
			enemy->vel.vz = 0;
			enemy->doing = JOFF_SQUIRTED;
		}
		break;

// the n64 shrinks him, & he vanishes in a puff of white smoke
	case JOFF_BLATTED:
		enemy->pos.vx += enemy->vel.vx;
		enemy->pos.vz += enemy->vel.vz;
		enemy->ya += 400;
		enemyLandedCheck(enemy, joffdrive.gravity);

		enemy->ticker--;
		if(!enemy->ticker)
		{
			EnemyKill(enemy);	// dead, complete with stars

		}
		break;


	case JOFF_STUNNED:
		enemy->ya += 400;
		enemy->ticker--;
		if(!enemy->ticker)
		{
			enemy->doing = JOFF_RETURNING;
		}
		break;

	case -1:
		boss_n_powers = old_boss_n_powers = 5;

		enemy->ticker = JOFF_WAIT_TIME;
		enemy->doing = JOFF_WAITING;
		AddToQueue(&enemy->anim,NMEANIM_IDLE,YES,NO,4096);	// anim,number,loop,queue,speed NB - FAST!
		break;
	}
// tbd - ensure joff doesn't encroach on anywhere he may need to do proper physics
//	printf("joff doing %d\n",enemy->doing);
}

void JoffIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with==0)
	{
		if(GloveCtrl.handOnBall || GloveCtrl.ballWithHand)
		{
			if(!IsBallShield(dvec))
				DisconnectGloveFromBall(DISCONNECT_WHEN_JOINED | DISCONNECT_WHEN_BOUNCING | DISCONNECT_WHEN_WALKINGON);
			NMEShovesBall(nme,dvec,sphererad,12);
		}
		else
		{
	// otherwise, hurt the glove & push it out
			if(!IsBallShield(dvec))
				NMEHurtsGlove(nme,dvec,sphererad);

		}
	}
	else
	{
		if(BallCtrl.anywhack)
		{
// starts spinning.
// If energy's zero, unhooks from the others (which fall), & spins off into distance, in dirn hit by ball
			if(nme->doing != JOFF_STUNNED && nme->doing != JOFF_BLATTED)
			{
				boss_n_powers--;
				sfxPlayNME3D(levelFX, SFX_AT_SPANK_OOF,nme);

//				printf("N POWERS DECREASED TO %d\n",boss_n_powers);

				if(boss_n_powers == 2)
				{

//					printf("Blatted!\n");
					nme->doing = JOFF_BLATTED;
					nme->vel = ballVel;
					nme->vel.vx *= 2;
					nme->vel.vz *= 2;
					nme->vel.vy = 0;

					nme->ticker = JOFF_BLAT_TIME;


					boss_FreeKirkFromJoff();
					boss_FreeCancerFromJoff();


				}
				else
				{
//					printf("Stunned!\n");

					nme->doing = JOFF_STUNNED;
					nme->vel.vx = 0;
					nme->vel.vy = 0;
					nme->vel.vz = 0;
					nme->ticker = JOFF_STUN_TIME;
				}
			}

		}
		NMEPushesBall(nme,dvec,sphererad);
	}
}
NME_SPHERE JoffSpheres[4] =
{
	{&JoffSpheres[1],	JoffIntRou,{  0, -20,   0},   28, -1},
	{&JoffSpheres[2],	JoffIntRou,{  0,  10, -10},   24, -1},
	{&JoffSpheres[3],	JoffIntRou,{  0,  18,  21},   20, -1},
	{NULL,				JoffIntRou,{  0,  20,  42},   15, -1}
};



/*
ENEMY SELWYN_CRAB 0 84 0 1 0
	NORMAL_INSTRUCTION		NME_WAIT		0 0 1 0 ALWAYS	0 0

	NORMAL_INSTRUCTION		NME_SPECIAL -1 1 0 0 ALWAYS 0 0		(special1)
	NORMAL_INSTRUCTION		NME_SPECIAL 1 -1 15 1 ALWAYS 0 0	(walk)

	NORMAL_INSTRUCTION		NME_WAIT	350 0 0 16384 ALWAYS 0 0	(no anim)

	NORMAL_INSTRUCTION		NME_ATTACK	0 0 0 0 ALWAYS	0 0
	NORMAL_INSTRUCTION		NME_BRANCH	-1 2 0 0 ALWAYS 0 0 

	ATTACK_INSTRUCTION		NME_SPECIAL -1 0 0 2097154 ALWAYS 0 0	(face ball, move vel)
	ATTACK_INSTRUCTION		NME_JUMP	-1 0 20 0 16384 0 ALWAYS	(no anim)
	ATTACK_INSTRUCTION		NME_WAIT	-1 0 0 16384 ALWAYS	0 0		(no anim)
	ATTACK_INSTRUCTION		NME_ENDATTACK  0 0 0 16384 ALWAYS		(no anim)
END_ENEMY
*/
// special 1's an upwards grab
// special 2's flipping over & getting back up
// jump is actually fire

// circles ball at fixed range.
// very occasionally shoots claws at it, & wobbles clawless for a while.
// stunned, flips. killable when flipped

#define CANCER_ACTIVATED	0
#define CANCER_WALKING		1
#define CANCER_FIRING		2
#define CANCER_FLIPPING		3
#define CANCER_UNFLIPPING	4

#define CANCER_OUTER_RANGE (4096 * 150)
#define CANCER_INNER_RANGE (4096 * 100)

//#define CANCER_FLIPVEL (4096 * -40)
#define CANCER_FLIPVEL (4096 * -10)
#define CANCER_INIT_TIME (120)
#define CANCER_FLIP_TIME (120)
#define CANCER_UNFLIP_TIME (40)

#define CANCER_TURN_SPEED 50
#define CANCER_ADVANCE_SPEED (4096 * 3)
#define CANCER_RETREAT_SPEED (4096 * 2)
#define CANCER_FIRE_FREQ (200)

//		if((TriggerAtFrame(14,&enemy->actor)) || (TriggerAtFrame(31,&enemy->actor)) || (TriggerAtFrame(41,&enemy->actor)) || (TriggerAtFrame(48,&enemy->actor)) || (TriggerAtFrame(58,&enemy->actor)) || (TriggerAtFrame(68,&enemy->actor)) || (TriggerAtFrame(80,&enemy->actor)) || (TriggerAtFrame(90,&enemy->actor)) || (TriggerAtFrame(100,&enemy->actor)))
//			PlaySample(FX_SELWYNCANCER_ATTACK,&enemy->actor.pos,100,128);
//	if(enemy->actor.animation->currentAnimation == NMEANIM_WALK)
//	{
//		if((TriggerAtFrame(283,&enemy->actor)) || (TriggerAtFrame(298,&enemy->actor)) || (TriggerAtFrame(311,&enemy->actor)) || (TriggerAtFrame(326,&enemy->actor)))
//			PlaySample(FX_SELWYNCANCER_ATTACK,&enemy->actor.pos,100,128);

void Update_Cancer(ENEMYPOS *enemy)
{
	ENEMYPOS *sel;
//	VECTOR dvec;
	int mag;
	int f;
//	COLLDATA *NmeColl = (COLLDATA *)(enemy->extras);
	static VECTOR oldpos;
	VECTOR pos;
	VECTOR vec;

	oldpos = enemy->pos;

	f = enemy->anim.animInfo->frame;
	f+=enemy->anim.animInfo->segInfo[enemy->anim.currentAnimation].segStart;

	switch(enemy->doing)
	{
	case CANCER_ACTIVATED:
		enemy->ticker++;
		if(enemy->ticker >= CANCER_INIT_TIME)
		{
			enemy->doing = CANCER_WALKING;
			enemy->ticker = 0;
		}

		enemy->pos.vx += enemy->vel.vx;
		enemy->pos.vy += enemy->vel.vy;
		enemy->pos.vz += enemy->vel.vz;
		enemy->vel.vy += gravity;

		break;

	case CANCER_WALKING:
		enemy->ticker++;
		NME_FaceTo(enemy, &ballPos, CANCER_TURN_SPEED);


//		enemyLandedCheck(enemy,4096);

		pos.vx = enemy->pos.vx - ballPos.vx;
		pos.vy = 0;
		pos.vz = enemy->pos.vz - ballPos.vz;

		//mag = Magnitude(&dvec);
		mag=Magnitude2D(pos.vx,pos.vz);

		if(mag > CANCER_OUTER_RANGE)
		{
			NME_GoForwards(enemy, CANCER_ADVANCE_SPEED);
		}
		else if(mag < CANCER_INNER_RANGE)
		{
			NME_GoForwards(enemy, -CANCER_RETREAT_SPEED);
		}
//		else
			
		if (enemy->ticker >= CANCER_FIRE_FREQ)
		{
			enemy->ticker =0;
			enemy->doing = CANCER_FIRING;
			AddToQueue(&enemy->anim,NMEANIM_JUMP,NO,NO,4096);
			AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
		}

		MakeUnit(&pos);
		enemy->vel.vx =  pos.vz * 3;
		enemy->vel.vz = -pos.vx * 3;

		enemy->pos.vx += enemy->vel.vx;
		enemy->pos.vz += enemy->vel.vz;


		enemy->pos.vy += enemy->vel.vy;
		enemy->vel.vy += gravity;

		break;

	case CANCER_FIRING:
		enemy->ticker++;
		if(enemy->ticker == 10)
		{
			static VECTOR posrefl = {0,0,0};
			static VECTOR posrefr = {0,0,0};

			
			EnemyRotatePoint(enemy,&posrefl, &pos, 6);
			pos.vx = (pos.vx<<12) + enemy->pos.vx;
			pos.vy = (pos.vy<<12) + enemy->pos.vy;
			pos.vz = (pos.vz<<12) + enemy->pos.vz;

			vec.vx = -rsin(enemy->ya) * 8;
			vec.vy = 256;	//-5000;
			vec.vz = -rcos(enemy->ya) * 8;

			enemy_ShootClaw(&pos, &vec, enemy->ya, 0);



			EnemyRotatePoint(enemy,&posrefr, &pos, 8);
			pos.vx = (pos.vx<<12) + enemy->pos.vx;
			pos.vy = (pos.vy<<12) + enemy->pos.vy;
			pos.vz = (pos.vz<<12) + enemy->pos.vz;

			vec.vx = -rsin(enemy->ya) * 8;
			vec.vy = 256;	//-5000;
			vec.vz = -rcos(enemy->ya) * 8;

			enemy_ShootClaw(&pos, &vec,enemy->ya, 2048);


		}
		if(enemy->ticker >= 100)
		{
			enemy->doing = CANCER_WALKING;
			enemy->ticker = 0;
		}
		break;

	case CANCER_FLIPPING:
		enemy->ticker++;
		enemy->za = AngleHomer(enemy->za, 2048, 120);

		if(enemy->ticker >= CANCER_FLIP_TIME)
		{
			boss_FlipCancerOver(enemy);
		}
		enemy->pos.vy += enemy->vel.vy;
		enemy->vel.vy += gravity;
		break;

	case CANCER_UNFLIPPING:
		enemy->ticker++;
		enemy->za = AngleHomer(enemy->za, 0, 120);
		if(enemy->ticker >= CANCER_UNFLIP_TIME)
		{
			enemy->doing = CANCER_WALKING;
			enemy->ticker = 0;
		}
		enemy->pos.vy += enemy->vel.vy;
		enemy->vel.vy += gravity;
		break;


	case -1:
//			,enemy->anim.currentAnimation,enemy->anim.animInfo->segInfo[enemy->anim.currentAnimation].segStart);


		sel = loadlndFindEnemy(10);
		enemy->pos = sel->pos;
		enemy->pos.vy -= 48 << 12;
		enemy->ya = sel->ya;
		enemy->ticker++;
		break;
	}

	if(enemy->doing != -1)
	{
		enemy_UsePhysics(enemy, &EnemyHeavyPhysics, &oldpos, 13);

/*
		if (collboxCheckSphere(NmeColl))
		{
	//		printf("hit");
		}
*/
	}

	if(f == 283 || f == 298)
	{
		sfxPlayFull3D(levelFX,SFX_SELWYNCANCER_ATTACK,&enemy->pos);
	}

//	printf("can frame %d. anim no %d (start = %d)\n",enemy->anim.animInfo->frame,enemy->anim.currentAnimation,enemy->anim.animInfo->segInfo[enemy->anim.currentAnimation].segStart);
//			temp=enemy->anim.animInfo->frame;
//	printf("temp+=enemy->anim.animInfo->segInfo[enemy->anim.currentAnimation].segStart;

//	printf("cancer doing %d, za = %d, tick=%d\n",enemy->doing,enemy->za,enemy->ticker);
}

// called on blastring intersection or on fistslam
void boss_FlipCancerOver(ENEMYPOS *nme)
{
	if(nme->anim.currentAnimation != NMEANIM_WALK)
		return;
	if(nme->doing == CANCER_FLIPPING && nme->za == 2048)
	{
		nme->doing = CANCER_UNFLIPPING;
	}
	else
	{
		nme->doing = CANCER_FLIPPING;
	}
	nme->vel.vy = -CANCER_FLIPVEL;
	nme->ticker = 0;
}

void boss_FreeCancerFromJoff()
{
	ENEMYPOS *nme;

	nme = loadlndFindEnemy(11);
	nme->doing = CANCER_ACTIVATED;
	nme->ticker = 0;
	nme->vel.vx = 0;
	nme->vel.vy = 0;
	nme->vel.vz = 0;
	AddToQueue(&nme->anim,NMEANIM_SPECIAL2,NO,NO,4096);	// fall spinning
	AddToQueue(&nme->anim,NMEANIM_WALK,YES,YES,4096);

}

void CrabIntRou(int with, ENEMYPOS *enemy, VECTOR *dvec, int sphererad)
{
	if(with == 0)
	{
// if the glove is a fist, kill the bad guy
		if(GloveCtrl.action == HAND_SLAM|| GloveCtrl.action == HAND_SLAM2ST) // && Glover.animInfo->frame>=4)	// the frame check's a tad messy, but WTF?
		{
			if(enemy->doing == CANCER_FLIPPING && (enemy->za >1024 || enemy->za < 4096-1024))
			{
				EnemyKill(enemy);
				boss_n_powers = 1;
				boss_FreeKirkFromCancer();
			}
			else
			{
				NMEPushesGlove(enemy,dvec,sphererad);
			}
		}
		else if(enemy->doing == CANCER_FLIPPING)
		{
			NMEPushesGlove(enemy,dvec,sphererad);
		}
		else
		{
			NMEHurtsGlove(enemy,dvec,sphererad);
		}
	}
	else
	{
		NMEPushesBall(enemy,dvec,sphererad);
	}
}

NME_SPHERE CancerSpheres[3] =
{
	{&CancerSpheres[1],	CrabIntRou,{  0,  0,    0},   18,-1},
	{&CancerSpheres[2],	CrabIntRou,{  0,  0,  -10},   12,6},
	{             NULL,	CrabIntRou,{  0,  0,  -10},   12,256+8}
};


/*
ENEMY SELWYN_FISH 0 158 -20 2 0
	NORMAL_INSTRUCTION		NME_WAIT			0 0 0 16384 ALWAYS	0 0	(no anim)

	NORMAL_INSTRUCTION		NME_SPECIAL			1 -1 15 1 ALWAYS 0 0	(walk)
	NORMAL_INSTRUCTION		NME_FOLLOWPLAYER	0 0 60 0 30 65 ALWAYS 0 0	(hand,accel)
	NORMAL_INSTRUCTION		NME_WAIT		   10 0 0 16384 ALWAYS 0 0	(no anim)

	NORMAL_INSTRUCTION		NME_FOLLOWPLAYER	0 0 60 120 30 65 ALWAYS 0 0		(hand,accel)

	NORMAL_INSTRUCTION		NME_BRANCH			-1 2        0  0 ALWAYS 0 0 
END_ENEMY


*/

// anims
// idla = on head
// walk = flying
// action1 = face down
// hurt = some bizarre streching
// startattack is an intake of breath
// endattack is v.short
// special1 is more stretchy stuff

#define KIRK_ACTIVATED  0
#define KIRK_ACTIVATED2 1
#define KIRK_PREDIVE	2
#define KIRK_DIVE		3
#define KIRK_LANDED		4
#define KIRK_BLOWING	5

#define KIRK_BOMB_ACCEL 8192
#define KIRK_PREDIVE_TIME (30)
#define KIRK_LAND_TIME (70)

//	if(enemy->actor.animation->currentAnimation == NMEANIM_WALK)
//	{
//		if(TriggerAtFrame(179,&enemy->actor))
//			PlaySample(FX_SELWYNKIRK_FLY,&enemy->actor.pos,100,128);

static const NMEAccelStr bovaccel =
	{
		0x180,
		0x2400,
		100,
		1000
	};

void Update_Kirk(ENEMYPOS *enemy)
{
	ENEMYPOS *sel;
	VECTOR target;
	int oya;
	int arrived;
	int f;

	COLLDATA *NmeColl = (COLLDATA *)(enemy->extras);


	switch(enemy->doing)
	{
	case KIRK_ACTIVATED:
		target = glovePos;
		target.vy -= 100*4096;
		oya = enemy->ya;

		arrived = NME_AccelTo3D(enemy,&target,&bovaccel);

		oya = (enemy->ya - oya) & 4095;
		if(oya > 2048) oya |= (~4095);
		if(oya > 16) oya = 16;
		if(oya < -16) oya = -16;
		enemy->za = AngleHomer(enemy->za, -(oya * 32), 8);
		enemy->xa = AngleHomer(enemy->xa,0,bovaccel.turnrate);	// recover from aiming

// tbd - needs another state
//		if(RANDOM256() < 3)
//		if(!(frame & 31))
		if(!(frame & 63))
		{
			enemy->doing = KIRK_BLOWING;
			enemy->ticker = 0;
			AddToQueue(&enemy->anim,NMEANIM_STARTATTACK,NO,NO,4096);
			AddToQueue(&enemy->anim,NMEANIM_ENDATTACK,NO,YES,4096);
			AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
		}
		break;

	case KIRK_BLOWING:
	{
		static VECTOR posref = {0,-5,-30};
		//target.vx=0;
		//target.vy=-5;
		//target.vz=-30;
		
		VECTOR pos;
		//VECTOR vec;

		enemy->za = AngleHomer(enemy->za, 0, 8);

		enemy->ticker++;
		if(enemy->ticker == 5)
		{
			EnemyRotatePoint(enemy,&posref, &pos, -1);
			pos.vx = (pos.vx<<12) + enemy->pos.vx;
			pos.vy = (pos.vy<<12) + enemy->pos.vy;
			pos.vz = (pos.vz<<12) + enemy->pos.vz;

// Use target as a velocity-calculation vector
			target.vx = -rsin(enemy->ya) * 2;	// Phew, another Andrew optimisation found & fixed
			target.vy = 256;
			target.vz = -rcos(enemy->ya) * 2;

			target.vx += (RANDOM256() - 128)<<4;
			target.vy += (RANDOM256() - 128)<<3;
			target.vz += (RANDOM256() - 128)<<4;

			if(RANDOM256() < 128)
				enemy_ShootBubble(&pos, &target,KIRK_BUBBLE2_ENEMY);
			else
				enemy_ShootBubble(&pos, &target,KIRK_BUBBLE1_ENEMY);
		}
		if(enemy->ticker == 10)
		{
			enemy->doing = KIRK_ACTIVATED;
		}
		break;
	}

	case KIRK_ACTIVATED2:
		target = glovePos;
		target.vy -= 150*4096;
		oya = enemy->ya;

		arrived = NME_AccelTo3D(enemy,&target,&bovaccel);

		oya = (enemy->ya - oya) & 4095;
		if(oya > 2048) oya |= (~4095);
		if(oya > 16) oya = 16;
		if(oya < -16) oya = -16;
		enemy->za = AngleHomer(enemy->za, -(oya * 32), 8);
		enemy->xa = AngleHomer(enemy->xa,0,bovaccel.turnrate);	// recover from aiming

// if we're close enough, divebomb...

		if(arrived)
		{
			enemy->doing = KIRK_PREDIVE;
			enemy->ticker = KIRK_PREDIVE_TIME;
			AddToQueue(&enemy->anim,NMEANIM_ACTION1,NO,NO,4096);
		}

		break;

	case KIRK_PREDIVE:
		enemy->za = AngleHomer(enemy->za, 0, 8);
		enemy->ticker--;
		if(!enemy->ticker)
			enemy->doing = KIRK_DIVE;
		break;

	case KIRK_DIVE:
		enemy->za = AngleHomer(enemy->za, 0, 8);
		enemy->vel.vx = enemy->vel.vx * 7/8;
		enemy->vel.vz = enemy->vel.vz * 7/8;
		enemy->vel.vy += KIRK_BOMB_ACCEL;

		enemy->pos.vx += enemy->vel.vx;
		enemy->pos.vy += enemy->vel.vy;
		enemy->pos.vz += enemy->vel.vz;
		break;

	case KIRK_LANDED:
		enemy->ticker--;
		if(enemy->ticker == 10)
		{
			AddToQueue(&enemy->anim,NMEANIM_SPECIAL1,NO,NO,4096);
			AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
		}
		if(!enemy->ticker)
		{
			enemy->doing = KIRK_ACTIVATED2;

		}
		enemy->vel.vx = 0;
		enemy->vel.vy = 0;
		enemy->vel.vz = 0;
		break;

	case -1:
		sel = loadlndFindEnemy(10);
		enemy->pos = sel->pos;
		enemy->pos.vy -= 74 << 12;
		enemy->ya = sel->ya;

		break;
	}

	if(enemy->doing != -1)
	{
		f = enemy->anim.animInfo->frame;
		f+=enemy->anim.animInfo->segInfo[enemy->anim.currentAnimation].segStart;
		if(f == 179)
		{
			sfxPlayFull3D(levelFX,SFX_SELWYNKIRK_FLY,&enemy->pos);
		}

		if (collboxCheckSphere(NmeColl))
		{
			if(enemy->doing == KIRK_DIVE)
			{
				//VECTOR temp;
				int hag;
				hag = getHeightAt(enemy->pos.vx, enemy->pos.vy, enemy->pos.vz);
				target = enemy->pos;
				target.vy += hag;

				effectsStartNMEBlastRing(&target,5*4096, 80 * 4096,8);
				enemy->ticker = KIRK_LAND_TIME;
				sfxPlayNME3D(levelFX,SFX_SELWYNJOFF_LAND,enemy);
				enemy->doing = KIRK_LANDED;
			}
	//		printf("hit");
		}
	}
}

void boss_FreeKirkFromJoff()
{
	ENEMYPOS *nme;

	nme = loadlndFindEnemy(12);
	nme->doing = KIRK_ACTIVATED;
	AddToQueue(&nme->anim,NMEANIM_STARTMOVE,NO,YES,4096);
	AddToQueue(&nme->anim,NMEANIM_WALK,YES,YES,4096);

}
void boss_FreeKirkFromCancer()
{
	ENEMYPOS *nme;

	nme = loadlndFindEnemy(12);
	nme->doing = KIRK_ACTIVATED2;
	AddToQueue(&nme->anim,NMEANIM_STARTMOVE,NO,YES,4096);
	AddToQueue(&nme->anim,NMEANIM_WALK,YES,YES,4096);
}

void KirkIntRou(int with, ENEMYPOS *enemy, VECTOR *dpos, int sphererad)
{
	if(with == 0)
	{

// tbd - if the glove's under the enemy, and we're doing the dive routine. Kill the glove by splatting

		if(enemy->doing == KIRK_LANDED)
		{
			if(GloveCtrl.action == HAND_SLAM || GloveCtrl.action == HAND_SLAM2ST)
			{
				EnemyKill(enemy);
				boss_n_powers = 0;
			}
			else
			{
				NMEPushesGlove(enemy,dpos,sphererad);
			}
		}
		else
		{
			NMEHurtsGlove(enemy,dpos,sphererad);
		}
	}
	else
	{
		NMEPushesBall(enemy,dpos,sphererad);
	}
}
NME_SPHERE KirkSpheres[2] =
{
	{&KirkSpheres[1],	KirkIntRou,{  0,  0,  0},   18, -1},
	{           NULL,	KirkIntRou,{  0,  5, 15},   10,  0}
};


/*
ENEMY FLANNEL 0 -135 -272 0 0
	NORMAL_INSTRUCTION		NME_WAIT		333 	0	0	16384 ALWAYS 0 0	(no anim)
	NORMAL_INSTRUCTION		NME_SPECIAL		-1		-1	5	0 ALWAYS 0 0	(idle)
	NORMAL_INSTRUCTION		NME_SPECIAL		1		-1	15	1 ALWAYS 0 0	(walk)

	NORMAL_INSTRUCTION		NME_MOVETO2D	-1		-132 -337 -221	50	16389 ALWAYS 0 0 (accel, slow when near)

	NORMAL_INSTRUCTION		NME_SPECIAL		-1		-1	5	0 SOMETIMES 500 0	(idle)
	NORMAL_INSTRUCTION		NME_SPECIAL		1		-1	15	1 ALWAYS 0 0		(walk)

	NORMAL_INSTRUCTION		NME_MOVETO2D	-1		132 -337 -221	50	16389 ALWAYS 0 0 (accel, slow when near)

	NORMAL_INSTRUCTION		NME_SPECIAL		-1		-1	5	0 SOMETIMES 500 0	(idle)
	NORMAL_INSTRUCTION		NME_BRANCH		-1		2				0	0 ALWAYS 0 0


   ATTACK_INSTRUCTION	NME_SPECIAL		1	-1			9		3	ALWAYS 		0 	0	(run)
   ATTACK_INSTRUCTION	NME_FOLLOWPLAYER 	200 	0 0 0			50	  	65	ALWAYS 		0 	0  (hand, accel)
   ATTACK_INSTRUCTION	NME_ENDATTACK		1	10			0		16384	ALWAYS 	0 	0 (no anim)
END_ENEMY

*/


#define WILLY_WAIT_TIME 200
#define WILLY_WAITING 0
#define WILLY_WALKING1 1
#define WILLY_WALKING2 2
#define WILLY_BLOWING 3
#define WILLY_CHASING 4
#define WILLY_JUMPING 5

#define WILLY_STUNNED 10
#define WILLY_DYING   11
#define WILLY_DEAD    12

#define WILLY_STUN_TIME 30
#define WILLY_DIE_TIME  100


void WillyReturnBall()
{
// tbd remove the snow from the ball
	VECTOR pos;
	removeSnow();
	BallChange.changeOn=0;
	BallCtrl.onSnow=0;
	BallCtrl.snowTexChange=0;
//	ballPowerWhackReturn();


	BallCtrl.powerwhack = 0;	// this is when it's hit an enemy & is being moved back to "reload"
	BallCtrl.anywhack = 0;

//   POS  -300.000000 240.000000 100.000000

	pos.vx = glovePos.vx;
	pos.vy = -260 <<12;
	pos.vz = -100 << 12;
//	pos = glovePos;
//	pos.vy -= 150<<12;

	ballPlaceAt(&pos);
}


void Update_Willy(ENEMYPOS *enemy)
{
	int f;
	static VECTOR oldpos;
	VECTOR pos;
//	VECTOR posref;
//	COLLDATA *NmeColl = (COLLDATA *)(enemy->extras);

	f = enemy->anim.animInfo->frame;
	f+=enemy->anim.animInfo->segInfo[enemy->anim.currentAnimation].segStart;

// tbd - If you're tha hand, and you're on the slopey bit, forcibly disconnect from the ball
// (hand_release)

// tbd2 - sfx & earthquakes (behave.c)

	oldpos = enemy->pos;

	if(f==84)
	{
		sfxPlayFull3D(levelFX,SFX_TREX_CALL,&enemy->pos);
//void weatherStartEarthquake(int duration, int maxStr, int minStr, int cycles, int flags);

		weatherStartEarthquake(35, 80, 40, 2, 0);

	}
	if(f==1 || f==28 || f==36)
		sfxPlayFull3D(levelFX,SFX_TREX_FOOTSTEP_LEFT,&enemy->pos);
	if(f==15 || f==32 || f==40)
		sfxPlayFull3D(levelFX,SFX_TREX_FOOTSTEP_RIGHT,&enemy->pos);

	switch(enemy->doing)
	{
	case WILLY_WAITING:
		enemy->ticker--;
		if(!enemy->ticker)
		{
//			AddToQueue(&enemy->anim,NMEANIM_IDLE,NO,YES,4096);
			AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
			enemy->doing = WILLY_WALKING1;
			enemy->ticker = 0;
		}
		break;

// okay, normal behaviour is walking from side to side along the bottom
	case WILLY_WALKING1:
		{
			enemy->ticker++;

// dust on impact

// earthquake on idle
			if(enemy->ticker > 50)
			{
				if(NME_FaceTo(enemy,&enemy->pathvectors[0],40))
				{
					if(NME_GoTo2D(enemy,&enemy->pathvectors[0],0x3000))
					{
						AddToQueue(&enemy->anim,NMEANIM_IDLE,NO,YES,4096);
						AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
						enemy->doing = WILLY_WALKING2;
						enemy->ticker = 0;
					}
				}
			}
		}
		break;
	case WILLY_WALKING2:
		{
			enemy->ticker++;

			if(enemy->ticker > 50)
			{
				if(NME_FaceTo(enemy,&enemy->pathvectors[1],40))
				{

					if(NME_GoTo2D(enemy,&enemy->pathvectors[1],0x3000))
					{
						AddToQueue(&enemy->anim,NMEANIM_IDLE,NO,YES,4096);
						AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
						enemy->doing = WILLY_WALKING1;
						enemy->ticker = 0;
					}
				}
			}
		}
		break;


	case WILLY_BLOWING:
			NME_FaceTo(enemy,&glovePos,60);	// slow rotate

			if((f > 83) && f < 90)
			{
				if(!(activeframe % 5))
				{
					static VECTOR posref = {0,-15,-30};
					//static VECTOR posref2 = {0,0,-100};
					
					VECTOR vec;

					//SETVECTOR(&posref,0,-15,-30);
					EnemyRotatePoint(enemy,&posref, &pos, 7);
					pos.vx = (pos.vx<<12) + enemy->pos.vx;
					pos.vy = (pos.vy<<12) + enemy->pos.vy;
					pos.vz = (pos.vz<<12) + enemy->pos.vz;

					SUBVECTOR(&vec,&glovePos,&pos);
					MakeUnit(&vec);
					vec.vx = vec.vx * 12;
					vec.vy = vec.vy * 12 - 4096 * 8;
					vec.vz = vec.vz * 12;

					vec.vx = vec.vx + ((RANDOM256()-128) << 5);


/*
					EnemyRotatePoint(enemy,&posref2, &vec, 7);
//					vec.vx = vec.vx * 512;
//					vec.vy = vec.vy * 512;
//					vec.vz = vec.vz * 512;
					vec.vx = vec.vx * 800;
					vec.vy = vec.vy * 400;	//800;
					vec.vz = vec.vz * 800;
*/

					enemy_ShootFireBall(&pos,&vec);
				}
			}
			if(enemy->anim.currentAnimation != NMEANIM_IDLE)
			{
				enemy->doing = enemy->ticker;
				enemy->ticker = 50;
			}
		break;

// chase the glove around the bottom bit of the level
// (tbd - this bit could prolly use real physics)
	case WILLY_CHASING:
		NME_FaceTo(enemy,&glovePos,100);
		NME_GoTo3D(enemy,&glovePos,0x4000);
		
		if(enemy->pos.vz < 620 * 4096 || NMECheckInRange(enemy, &glovePos, 4096 * 200, 0))
//		if(enemy->pos.vz < 620 * 4096 || NMECheckInRange(enemy, &glovePos, 4096 * 120, 0))
//		if(NMECheckVisible(enemy, &glovePos, 4096 * 50, 512))
		{

			enemy->doing = WILLY_JUMPING;
			enemy->ticker = 0;
			AddToQueue(&enemy->anim,NMEANIM_STARTATTACK,YES,NO,4096);
//			AddToQueue(&enemy->anim,NMEANIM_JUMP,NO,YES,4096);
			AddToQueue(&enemy->anim,NMEANIM_ENDATTACK,NO,YES,4096);
		}

		if(enemy->pos.vz < 620 * 4096)
			enemy->pos.vz = 620 * 4096;

		break;

// tbd - pick nearest movement point
	case WILLY_JUMPING:
		if(f==56)
			sfxPlayFull3D(levelFX,SFX_TREX_ATTACK,&enemy->pos);
		enemy->ticker++;
		if(enemy->ticker < 30)
			NME_GoForwards(enemy,0x4000);

		if(enemy->pos.vz < 620 * 4096)
			enemy->pos.vz = 620 * 4096;

		if(enemy->ticker >= 80)
		{
			AddToQueue(&enemy->anim,NMEANIM_IDLE,NO,YES,4096);
			AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
			enemy->doing = WILLY_WALKING1;
			enemy->ticker = 0;
		}
		break;


	case WILLY_STUNNED:
		enemy->ticker--;
		if(!enemy->ticker)
		{
//			AddToQueue(&enemy->anim,NMEANIM_IDLE,NO,YES,4096);
			AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
			if(RANDOM256() > 128)
				enemy->doing = WILLY_WALKING2;
			else
				enemy->doing = WILLY_WALKING1;
			enemy->ticker = 0;
		}
		break;

// loads of flames as willy goes kaboom
	case WILLY_DYING:
	{
// tbd - FX_SAMTEX_EXPLODE (ah. problemo)
//		VECTOR pos;
		pos = enemy->pos;
		pos.vx += (RANDOM256()-128) << 11;
		pos.vy += (RANDOM256()-128) << 11;
		pos.vz += (RANDOM256()-128) << 11;

		New_Debris(DEBRIS_FASTFLAME,&pos,1800);

		enemy->ticker--;

// stinking botch which is necessary if you've lured wills into the path of the platform
		if(enemy->pos.vz > enemy->pathvectors[0].vz)
			enemy->pos.vz -= 8192;

		if(!(RANDOM256() &  7))
		{
//			sfxPlayFull3D(levelFX,SFX_CRUMPET_ATTACK,&enemy->pos);
			sfxPlayFull3D(globalFX,SFX_GE_CRUMPET_ATTACK,&enemy->pos);
		}

		if(!enemy->ticker)
			enemy->doing = WILLY_DEAD;
		break;
	}

// Smoke comes out of boots
	case WILLY_DEAD:
	{
		static VECTOR posref =  {0,-20, 0};
		//static VECTOR posref2 = {0,-20, 0};
  //		VECTOR pos;
		int chance;
		//SETVECTOR(&posref,0,-20,0);
		chance = (RANDOM256() < 32);

		if(chance)
		{
			//if(RANDOM256() > 128)
				EnemyRotatePoint(enemy,&posref, &pos, 13);
			//else
			//	EnemyRotatePoint(enemy,&posref2, &pos, 3);

			pos.vx += (RANDOM256() & 31) - 16;
			pos.vz += (RANDOM256() & 31) - 16;

			pos.vx = (pos.vx<<12) + enemy->pos.vx;
			pos.vy = (pos.vy<<12) + enemy->pos.vy;
			pos.vz = (pos.vz<<12) + enemy->pos.vz;

			New_Debris(DEBRIS_SMOKE,&pos,1500);
		}
		break;
	}


	case -1:
		boss_n_powers = old_boss_n_powers = 3;

		enemy->ticker = WILLY_WAIT_TIME;
		enemy->doing = WILLY_WAITING;
		AddToQueue(&enemy->anim,NMEANIM_IDLE,YES,NO,0);	// doesn't animate while frozen (actually this is dealt with in the cameo)

		break;
	}


// boss.c in the n64 stuff

// If the glove is in the top bit of the level, do the flamethrower bit

	if(glovePos.vz < 60*4096)
	{
		if(enemy->anim.currentAnimation == NMEANIM_IDLE
			&& enemy->doing != WILLY_BLOWING
			&& enemy->doing != WILLY_WAITING
			)
		{
			enemy->ticker = enemy->doing;	// so we can return to moving left/right
			enemy->doing = WILLY_BLOWING;
		}

		if(ballPos.vz > 950 << 12 || (ballPos.vz > 620<<12 && Magnitude(&ballVel) < 4096))
		{
			WillyReturnBall();
		}

	}


// startattack
// If the glove is in the bottom bit of the level, do the chasing bit
	if(glovePos.vz > 620*4096
		&& enemy->doing != WILLY_CHASING
		&& enemy->anim.currentAnimation == NMEANIM_WALK)
	{
		enemy->doing = WILLY_CHASING;
		enemy->ticker = 0;
		AddToQueue(&enemy->anim,NMEANIM_RUN,YES,NO,4096);	// doesn't animate while frozen (actually this is dealt with in the cameo)
	}


// smoke from his fuel cannisters
	if(boss_n_powers < 3 && boss_n_powers != 0)
	{
		static VECTOR posref =  {16+20,-5, 20};
		static VECTOR posref2 = {16-20,-5, 20};
		//VECTOR pos;
		//int chance;

		if(boss_n_powers == 1)
			f = (RANDOM256() > 128);
		else
			f = (RANDOM256() > 192);

		if(f)
		{
			if(RANDOM256() > 128){
				//SETVECTOR(&posref,16+20,-5,20); 
				EnemyRotatePoint(enemy,&posref, &pos, 7);
			}else{
				//SETVECTOR(&posref,16-20,-5,20);
				EnemyRotatePoint(enemy,&posref2, &pos, 7);
				//EnemyRotatePoint(enemy,&posref, &pos, 7);
			}
			pos.vx = (pos.vx<<12) + enemy->pos.vx;
			pos.vy = (pos.vy<<12) + enemy->pos.vy;
			pos.vz = (pos.vz<<12) + enemy->pos.vz;

			New_Debris(DEBRIS_SMOKE,&pos,1500);
		}
	}

// flames when dying
// smoke from boots

	if(enemy->doing != WILLY_WAITING && enemy->doing != WILLY_DEAD)
	{
		enemy->vel.vy += gravity;
		enemy->pos.vy += enemy->vel.vy;
		enemy_UsePhysics(enemy, &EnemyHeavyPhysics, &oldpos, WILLY_COLL_RAD);

	}

//	printf("hag = %d\n",(int)getHeightAt(enemy->pos.vx,enemy->pos.vy,enemy->pos.vz));

//	printf("glove %d %d %d\n",(int)glovePos.vx>>12,(int)glovePos.vy>>12,(int)glovePos.vz>>12);
//	printf("willy doing %d at %d %d %d\n",enemy->doing, (int)enemy->pos.vx>>12,(int)enemy->pos.vy>>12,(int)enemy->pos.vz>>12);
}

/*****************************************************************************************/
void WillIntRou(int with, ENEMYPOS *enemy, VECTOR *dvec, int sphererad)
{
// (boss.c)
// hit by ball
// check radius > normal
// check velocity

// if yes, hurt

// otherwise, shake the snow off the ball, and send it back to the top
	if(with==0)
	{
		if(enemy->doing == WILLY_DEAD)
		{
			if(GloveCtrl.handOnBall || GloveCtrl.ballWithHand)
				NMEPushesBall(enemy,dvec,sphererad);
			else
				NMEPushesGlove(enemy,dvec,sphererad);
		}
		else
		{

			if(GloveCtrl.handOnBall || GloveCtrl.ballWithHand)
			{
				DisconnectGloveFromBall(DISCONNECT_WHEN_JOINED | DISCONNECT_WHEN_BOUNCING | DISCONNECT_WHEN_WALKINGON);
			}

			if(enemy->doing == WILLY_JUMPING)
			{
				handKillGlove();	// Splat!
			}
			else
			{
				NMEHurtsGlove(enemy,dvec,sphererad);
			}

		}
	}
	else
	{
//		NMEPushesBall(enemy,dvec,sphererad);

		if(enemy->doing == WILLY_DEAD)
		{
			NMEPushesBall(enemy,dvec,sphererad);
		}
		else
		{
			DB("ball hit - mag = %d, rad-normal = %d\n",Magnitude(&ballVel),(int)(ballRadius - (int)(19*4096*NORMAL_SCALE)*1));
//			printf("It thinks he's not dead yet\n");
			if(Magnitude(&ballVel) > 5 * 1024
				&& ballRadius > (19*4096*NORMAL_SCALE)*1
				)
			{
// sound effect FX_IMPACT_SNOW_LARGE

				sfxPlayFull3D(globalFX,SFX_IMPACT_SNOW_MEDIUM,&enemy->pos);

				if(enemy->doing != WILLY_STUNNED && enemy->doing != WILLY_DYING)
				{
					boss_n_powers--;

// sound effect FX_SPANK_OOF

					if(!boss_n_powers)
					{
						DYNCOLLBOX *pBox;

// sound effect FX_FUMBLE_CALL
						AddToQueue(&enemy->anim,NMEANIM_DEATH,NO,NO,4096);
						enemy->doing = WILLY_DYING;
						enemy->ticker = WILLY_DIE_TIME;


// Platform movement puzzle done manually, coz strictly speaking, willy ain't dead at this point
						loadlndFindPlatform(14, NULL, &pBox);
						if(pBox)
						{
//							printf("Moving Exit Box!\n");
							pBox->active = 1;
							pBox->move.waitTimer=0;	// that should get things moving ...
							pBox->move.pointCtr=1;	// just the one step, ta
						}
					}
					else
					{
						enemy->doing = WILLY_STUNNED;
						enemy->ticker = WILLY_STUN_TIME;
						AddToQueue(&enemy->anim,NMEANIM_STARTATTACK,YES,NO,2048);	// slow fall & recovery
						AddToQueue(&enemy->anim,NMEANIM_ENDATTACK,NO,YES,2048);
					}
				}

			}
			WillyReturnBall();
		}

	}
}
NME_SPHERE WillySpheres[] =
{
	{&WillySpheres[1],WillIntRou,{  16,  -4,  0},   60, 4},	// bod
	{&WillySpheres[2],WillIntRou,{  0,  0,  -20},   35, 256+3},	// foot
	{&WillySpheres[3],WillIntRou,{  0,  0,  -20},   35, 256+13},// foot
	{&WillySpheres[4],WillIntRou,{  0,  -35,  -30}, 70, 256+7},	// haad
	{            NULL,WillIntRou,{  0,   0,  30},   35, 256+9},	// tail
};






/*
ENEMY SPANK 33 0 -50 0 0
NORMAL_INSTRUCTION		NME_WAIT		 0 0 1 0 ALWAYS 0 0		the infy loop swinging


NORMAL_INSTRUCTION		NME_SPECIAL		 -1 -1 1 6 ALWAYS 0 0	("action2" = backflip from stun, don't slow, move vel)
NORMAL_INSTRUCTION		NME_ATTACK		 1 0 0 0 ALWAYS 0 0
NORMAL_INSTRUCTION		NME_BRANCH		 1 0 0 0 ALWAYS 0 0



  
CONDITIONAL_INSTRUCTION	NME_BRANCH		 0 4 0 0 IF_HAND_IN_RANGE_2D 0 100

NORMAL_INSTRUCTION		NME_DONTATTACK	 1 -100 0 0 ALWAYS 0 0
NORMAL_INSTRUCTION		NME_SPECIAL		 -1 -1 8 6 ALWAYS 0 0	("land", don't slow, move vel
NORMAL_INSTRUCTION		NME_ATTACK		 1 0 0 0 ALWAYS 0 0
NORMAL_INSTRUCTION		NME_BRANCH		 1 0 0 0 ALWAYS 0 0


ATTACK_INSTRUCTION		NME_SPECIAL		   1 -1 9 1 ALWAYS 0 0		("run", move accel)
ATTACK_INSTRUCTION		NME_FOLLOWPLAYER  20 0 0 0  0 65 ALWAYS 0 0	(Hand, move accel)
ATTACK_INSTRUCTION		NME_FOLLOWPLAYER 200 0 0 0 60 65 ALWAYS 0 0	(Hand, move accel)

ATTACK_INSTRUCTION		NME_BRANCH		 1 12 0 0 IF_HAND_IN_RANGE_2D 0 65

ATTACK_INSTRUCTION		NME_SPECIAL		 -1 -1 13 2 ALWAYS 0 0	("start move", move vel)
ATTACK_INSTRUCTION		NME_SPECIAL      1 -1 15 3 ALWAYS 0 0	("walk", move vel, move accel (!))

// return to tree

ATTACK_INSTRUCTION		NME_MOVETO2D	 -1 33 0 -160 10 16385 ALWAYS 0 0	(move accel, no anim)
ATTACK_INSTRUCTION		NME_FACETO		 -1 33 0 -50 0 16384 ALWAYS 0 0		(no anim)
ATTACK_INSTRUCTION		NME_MOVETO2D	 -1 33 0 -90 30 16385 ALWAYS 0 0	(move accel, no anim)

ATTACK_INSTRUCTION		NME_SPECIAL		 -1 0 0 6 ALWAYS 0 0				(special 0, don't slow, move vel)
ATTACK_INSTRUCTION		NME_JUMP		 -1 0 10 5 16384 0 ALWAYS 0 0		(no anim)
ATTACK_INSTRUCTION		NME_ENDATTACK	 1 0 0 16384 ALWAYS 0 0				(no anim)

ATTACK_INSTRUCTION		NME_SPECIAL		  -1 -1 12 2 ALWAYS 0 0				(start attack, move vel)
ATTACK_INSTRUCTION		NME_SPECIAL		  1 -1 13 0 ALWAYS 0 0				(start move)
ATTACK_INSTRUCTION		NME_BRANCH		  1 5 0 0 ALWAYS 0 0 
END_ENEMY
*/


// note that the N64 confines the hand inside Piboss manually
// (it also does the dead check & flips the chest lid up)

// note2 - he changes size, and his collision needs to too

//sfx from n64:-
/*
	if((frameCount MOD 50) == 0)
	{
		if(Random(20) < 5)
			PlaySample(FX_SPANK_CALL_3 + Random(2),&enemy->actor.pos,100,128);
	}
land 172 = FX_SPANK_DROP
idle 67,79 = FX_SPANK_SWING

idle FX_SPANK_OOF when hit by ball
	sets blockattack for a while
	forces SPECIAL2 anim

whilst in special2
	FX_SPANK_SWING
	FX_SPANK_DROP
	NMEANIM_IDLE2


idle2 calls StartBabiesAttacking	(either at count, or when fisted)
it also checks for fistslam & health
	NMEANIM_HURT

special1 animation involves zeroing the velocity

	StartEarthquakeAtPoint(&enemy->actor.pos,10,2000,1000,1,EARTHQUAKE_MOVECAMERA);
	CreateBigDustCircle(&enemy->actor.pos,-25);

*/




// Walk = hands in the air


// action1 = walk->run transition
// startmove = run->walk

// Special1 = pre-jump
// Jump = leap up to swing
// Idle = swinging

// Special2 = speedswing & drop
// Idle2 = on ground
// hurt = "oof" on ground
// action2 = backflip up from being stunned ->start att


// Land = come down from swing
// Run = hands on ground
// Start attack = leap + drop


#define SPANK_SWINGING  0

#define SPANK_HIT	    9
#define SPANK_ONGROUND 10

#define SPANK_GETTINGUP	11
#define SPANK_ATTACKING 12

#define SPANK_RETURN1	13
#define SPANK_RETURN2	14
#define SPANK_JUMPING	15
#define SPANK_SPLATTING	16

#define SPANK_DROPPING	17

#define SPANK_STUN_TIME 150

void Update_Spank(ENEMYPOS *nme)
{
	static VECTOR behind_treevec;
	static VECTOR treevec;
	static VECTOR oldpos;
	int f;
	SETVECTOR(&treevec,20<<12,60<<12,30<<12);
	SETVECTOR(&behind_treevec,20<<12,60<<12,90<<12);


//	if((frameCount MOD 50) == 0)
	if(!(frame & 63))
	{
		if(RANDOM256() < 64)
			sfxPlayNME3D(levelFX, SFX_SPANK_CALL_3 + (RANDOM256() & 1),nme);
	}


	f = nme->anim.animInfo->frame;
	f+= nme->anim.animInfo->segInfo[nme->anim.currentAnimation].segStart;

	switch(f)
	{
		case 67:
		case 79:
			sfxPlayNME3D(levelFX, SFX_SPANK_SWING,nme);
			break;
		case 172:
			sfxPlayNME3D(levelFX, SFX_SPANK_DROP,nme);
			break;
}


	oldpos = nme->pos;

	{
		int i;
		for(i = 0; i < 8; i++)
		{
			SpankSpheres[i].r = SpankRefSpheres[i].r * nme->psa->globalscale.vx / 400;
			SpankSpheres[i].pos.vx = SpankRefSpheres[i].pos.vx * nme->psa->globalscale.vx / 400;
			SpankSpheres[i].pos.vy = SpankRefSpheres[i].pos.vy * nme->psa->globalscale.vx / 400;
			SpankSpheres[i].pos.vz = SpankRefSpheres[i].pos.vz * nme->psa->globalscale.vx / 400;
		}
	}

// 400 -> 60
//  64 -> -5
	treevec.vy = (-15 + 65 * nme->psa->globalscale.vx / 336) << 12;

	switch(boss_n_powers)
	{
	case 2:
		if(nme->psa->globalscale.vx > 350)
		{
			nme->psa->globalscale.vx -= 10;
			nme->psa->globalscale.vy -= 10;
			nme->psa->globalscale.vz -= 10;
		}
		break;

	case 1:
		if(nme->psa->globalscale.vx > 300)
		{
			nme->psa->globalscale.vx -= 10;
			nme->psa->globalscale.vy -= 10;
			nme->psa->globalscale.vz -= 10;
		}
		break;
	case 0:
		if(nme->psa->globalscale.vx > 10)
		{
			nme->psa->globalscale.vx -= 10;
			nme->psa->globalscale.vy -= 10;
			nme->psa->globalscale.vz -= 10;
		}
		break;
	}

	switch(nme->doing)
	{

		case SPANK_SWINGING:
			nme->pos.vy = treevec.vy;

// if glove goes for the treasure, drop off & give chase
// (The "enabled" check deals with the case where we killed the glove in this position)

//			if(glovePos.vz > (nme->pos.vz - 4096 * 50) && GloveCtrl.enabled)
			if(glovePos.vz > (nme->pos.vz) && GloveCtrl.enabled)
			{
				AddToQueue(&nme->anim,NMEANIM_LAND,NO,YES,4096);
				AddToQueue(&nme->anim,NMEANIM_RUN,YES,YES,4096);
				nme->doing = SPANK_DROPPING;
				nme->ticker= 40;
			}
			break;

		case SPANK_DROPPING:
			nme->ticker--;
			if(!nme->ticker)
			{
				sfxPlayNME3D(levelFX, SFX_SPANK_DROP,nme);

				nme->doing = SPANK_ATTACKING;
			}
			break;

		case SPANK_HIT:			// spin round & fly off
			nme->ticker++;
			if(nme->anim.currentAnimation == NMEANIM_IDLE2)
			{
				nme->doing = SPANK_ONGROUND;
				nme->ticker = SPANK_STUN_TIME;
			}
			break;
		case SPANK_ONGROUND:
			nme->ticker--;
			if(!nme->ticker)
			{
				AddToQueue(&nme->anim,NMEANIM_ACTION2,NO,YES,4096);
				AddToQueue(&nme->anim,NMEANIM_RUN,YES,YES,4096);
				nme->ticker = 0;
				nme->doing = SPANK_GETTINGUP;
			}
			break;

		case SPANK_GETTINGUP:
			if(nme->anim.currentAnimation == NMEANIM_RUN)
			{
				nme->doing = SPANK_ATTACKING;
			}
			break;

		case SPANK_ATTACKING:	// chase glove unless we get beyond a specific range from the tree (or unless we're trying to go behind the island)
			NME_FaceTo(nme,&glovePos,100);
			NME_GoTo2D(nme,&glovePos,0x4400);

			if(!NMECheckInRange(nme, &treevec, 4096 * 250, 0)  || (nme->pos.vz >> 12) > 200  || (glovePos.vz >> 12) > 250)
			{
				AddToQueue(&nme->anim,NMEANIM_STARTMOVE,NO,YES,4096);
				AddToQueue(&nme->anim,NMEANIM_WALK,YES,YES,4096);
				nme->doing = SPANK_RETURN1;
			}
			else if(NMECheckInRange(nme, &glovePos, 4096 * 100, 0))
			{
				AddToQueue(&nme->anim,NMEANIM_STARTATTACK,NO,NO,4096);
				AddToQueue(&nme->anim,NMEANIM_STARTMOVE,NO,YES,4096);
				AddToQueue(&nme->anim,NMEANIM_WALK,YES,YES,4096);

				nme->ticker = 0;
				nme->doing = SPANK_SPLATTING;
			}
			break;

// attack the glove by jumping on it
		case SPANK_SPLATTING:
			NME_FaceTo(nme,&glovePos,100);
			NME_GoTo2D(nme,&glovePos,0x5000);

			if(nme->anim.currentAnimation == NMEANIM_WALK)
			{
				sfxPlayNME3D(levelFX, SFX_SPANK_DROP,nme);

				nme->doing = SPANK_RETURN1;
			}
			break;

// walking to a position behind the tree
		case SPANK_RETURN1:
			if(NME_FaceTo(nme,&behind_treevec,100))
			{
				if(NME_GoTo2D(nme,&behind_treevec,0x4400))
				{
					nme->doing = SPANK_RETURN2;
				}
			}
			break;

		case SPANK_RETURN2:
			if(NME_FaceTo(nme,&treevec,100))
			{
				if(NME_GoTo2D(nme,&treevec,0x4400))
				{
					AddToQueue(&nme->anim,NMEANIM_SPECIAL1,NO,NO,4096);
					AddToQueue(&nme->anim,NMEANIM_JUMP,NO,YES,4096);
					AddToQueue(&nme->anim,NMEANIM_IDLE,YES,YES,4096);
					nme->doing = SPANK_JUMPING;
				}
			}
			break;

		case SPANK_JUMPING:
			if(NME_GoTo3D(nme,&treevec,0x3000))
			{
				nme->doing = SPANK_SWINGING;
			}
			break;

		case -1:
			nme->doing = SPANK_SWINGING;
			boss_n_powers = old_boss_n_powers = 3;
			break;
	}


	if(nme->doing != -1
		&& nme->doing != SPANK_SWINGING
		&& nme->doing != SPANK_JUMPING
		&& (nme->doing != SPANK_HIT || f > 116)	// so he doesn't interact with the branch whilst spinning
		)
	{
		nme->vel.vy += gravity;
		nme->pos.vy += nme->vel.vy;
//		oldpos.vy += 50 * 4096;
//		enemy->pos.vy  += 50 * 4096;
		enemy_UsePhysics(nme, &EnemyHeavyPhysics, &oldpos, 28 * nme->psa->globalscale.vx / 400);
//		enemy->pos.vy -= 50 * 4096;
	}
}



void SpankIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)
	{
		if( (GloveCtrl.action == HAND_SLAM|| GloveCtrl.action == HAND_SLAM2ST)
			&& nme->doing == SPANK_ONGROUND
			)
		{
			sfxPlayNME3D(levelFX, SFX_PI_SPANK_OOF,nme);

			AddToQueue(&nme->anim,NMEANIM_HURT,NO,NO,4096);	// "oof"
			AddToQueue(&nme->anim,NMEANIM_IDLE2,YES,YES,4096);
			boss_n_powers--;
			if(!boss_n_powers)
			{
				EnemyKill(nme);	// here comes the end-of-level platform
			}

			AddToQueue(&nme->anim,NMEANIM_ACTION2,NO,YES,4096);	// gets up & gives chase
			AddToQueue(&nme->anim,NMEANIM_RUN,YES,YES,4096);
			nme->ticker = 0;
			nme->doing = SPANK_GETTINGUP;

			NMEPushesGlove(nme,dvec,sphererad);
		}
		else
		{
			if(nme->doing == SPANK_ONGROUND || nme->doing == SPANK_GETTINGUP)	// the latter coz I'm nice
				NMEPushesGlove(nme,dvec,sphererad);
			else
				NMEHurtsGlove(nme,dvec,sphererad);
		}
	}
	else
	{
// ball (collecting the ball is done elsewhere.
		NMEPushesBall(nme,dvec,sphererad);
	}
}
/***************************************************************************************/
void SpankBodRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with != 1 || nme->doing != SPANK_SWINGING)
	{
		SpankIntRou(with,nme,dvec,sphererad);
	}
	else
	{
// Hit by ball whilst swinging from branch
		sfxPlayNME3D(levelFX, SFX_PI_SPANK_OOF,nme);

		AddToQueue(&nme->anim,NMEANIM_SPECIAL2,NO,NO,4096);
		AddToQueue(&nme->anim,NMEANIM_IDLE2,YES,YES,4096);
		nme->doing = SPANK_HIT;
		nme->ticker = 0;
	}
}

/****************************************************************************************/

NME_SPHERE SpankRefSpheres[] =
{
	{&SpankSpheres[1],SpankBodRou,{  0, 30, -15},  25, 17},		// bod
	{&SpankSpheres[2],SpankBodRou,{  0,  0,  0},   15, 256+18},	// head
	{&SpankSpheres[3],SpankBodRou,{  0,  3,  3},   12, 256+20},	// foot
	{&SpankSpheres[4],SpankBodRou,{  0,  3,  3},   12, 256+26},	// foot

	{&SpankSpheres[5],SpankBodRou,{  0,  0,  0},   20, 256+4},	// arm
	{&SpankSpheres[6],SpankBodRou,{  0,  0,  0},   20, 256+11},	// arm


	{&SpankSpheres[7],SpankIntRou,{  0,  0,  0},   10, 256+5},	// hand
	{NULL            ,SpankIntRou,{  0,  0,  0},   10, 256+12},	// hand

};

NME_SPHERE SpankSpheres[] =
{
	{&SpankSpheres[1],SpankBodRou,{  0, 30, -15},  25, 17},		// bod
	{&SpankSpheres[2],SpankIntRou,{  0,  0,  0},   15, 256+18},	// head
	{&SpankSpheres[3],SpankBodRou,{  0,  3,  3},   12, 256+20},	// foot
	{&SpankSpheres[4],SpankBodRou,{  0,  3,  3},   12, 256+26},	// foot

	{&SpankSpheres[5],SpankIntRou,{  0,  0,  0},   20, 256+4},		// arm
	{&SpankSpheres[6],SpankIntRou,{  0,  0,  0},   20, 256+11},	// arm


	{&SpankSpheres[7],SpankIntRou,{  0,  0,  0},   10, 256+5},		// hand
	{NULL            ,SpankIntRou,{  0,  0,  0},   10, 256+12},	// hand

};

/*
ENEMY BABY_SPANK 33 0 150 0 0
NORMAL_INSTRUCTION		NME_SPECIAL		1 -1 9 1 ALWAYS 0 0	(anim = run, move accel)
NORMAL_INSTRUCTION		NME_RANDOMMOVE  140 -120 0 70 240 0 210 100 16385 ALWAYS 0 0	(move accel, don't anim)
NORMAL_INSTRUCTION		NME_FOLLOWPLAYER  200 0 0 0 0 129 SOMETIMES 500 0	(ball, move accel)
NORMAL_INSTRUCTION		NME_BRANCH		0 1 0 0 ALWAYS 0 0

NORMAL_INSTRUCTION		NME_WAIT		0 0 0 16384 ALWAYS 0 0
NORMAL_INSTRUCTION		NME_BRANCH		0 1 0 0 ALWAYS 0 0



CONDITIONAL_INSTRUCTION	NME_ATTACK		0 0 0 0 IF_CANSEE_BALL 0.8 0

ATTACK_INSTRUCTION		NME_ENDATTACK		0 20 0 16384 IF_BALL_IN_RANGE_2D 300 10000	(don't anim)
ATTACK_INSTRUCTION		NME_ENDATTACK		0 20 0 16384 IF_HAND_HOLDING_BALL 0 0		(don't anim)

ATTACK_INSTRUCTION		NME_FOLLOWPLAYER	100 0 0 0 50 129 ALWAYS 0 0			(accel, ball)

ATTACK_INSTRUCTION		NME_ENDATTACK		0 20 0 16384 IF_BALL_IN_RANGE_2D 50 10000	(don't anim)
ATTACK_INSTRUCTION		NME_ENDATTACK		0 20 0 16384 IF_HAND_HOLDING_BALL 0 0		(don't anim)


ATTACK_INSTRUCTION		NME_SPECIAL			-1 0 0 2097154 ALWAYS 0 0	(special 0, face ball, move vel)
ATTACK_INSTRUCTION		NME_SPECIAL			1 -1 15 1 ALWAYS 0 0		(walk,      move accel)
ATTACK_INSTRUCTION		NME_ENDATTACK		0 20 0 16384 ALWAYS 0 0		(don't anim)

ATTACK_INSTRUCTION		NME_SPECIAL			1 -1 9 1 ALWAYS 0 0			(run, move accel)
ATTACK_INSTRUCTION		NME_RANDOMMOVE		0 -120 0 70 240 0 210 100 16385 ALWAYS 0 0	(move accel)
ATTACK_INSTRUCTION		NME_ENDATTACK		0 20 0 16384 ALWAYS 0 0		(don't anim)
END_ENEMY

*/
#define BABY_GROWING		0
#define BABY_POINTCHOOSE	1
#define BABY_WANDERING      2
#define BABY_FOLLOWING_BALL 3
#define BABY_WAITING        4
#define BABY_ATTACKING_BALL	5
#define BABY_GRABBING_BALL	6

#define BABY_POINTCHOOSE2	10
#define BABY_WITH_BALL		11
#define BABY_FALLING		12
#define BABY_ONGROUND		13
#define BABY_RISING			14

#define BABY_FALL_TIME 100

// manually set froggy spell for a while

// FX_SPANK_CALL_1 at random
// if anim = special, queue a run
// special1, grabs ball

// if they've got the ball, they time out ?

// also, when with ball, occasionally, this happens...
//			AnimateActor(&enemy->actor,NMEANIM_STARTATTACK,NO,YES,0);
//			AnimateActor(&enemy->actor,NMEANIM_ACTION1,NO,YES,0);
//			AnimateActor(&enemy->actor,NMEANIM_RUN,YES,YES,0);

// and there's this occasionally
//		if(enemy->script.disabledCount == 0)
//			NMEActionMoveTo2D(enemy,&ball.pos,0,NMEFLAGS_MOVE_ACCEL);

// 
//	if((enemy->flags & HAND_HAS_SLAMMED) && (enemy->actor.animation->currentAnimation != NMEANIM_IDLE2) && (enemy->actor.animation->currentAnimation != NMEANIM_HURT))
// drops ball, or does this:-
//			AnimateActor(&enemy->actor,NMEANIM_SPECIAL2,NO,NO,0);
//			AnimateActor(&enemy->actor,NMEANIM_IDLE2,YES,YES,0);
//			AnimateActor(&enemy->actor,NMEANIM_ACTION2,NO,YES,0);



// Note - Problem with the monkeys - we need to keep 'em apart

void BabyHasBall(ENEMYPOS *enemy)
{
	static VECTOR refpos = {0,0,0};
	VECTOR pos;
	EnemyRotatePoint(enemy,&refpos, &pos, 13);	// ball attachment marker
	pos.vx = (pos.vx<<12) + enemy->pos.vx;
	pos.vy = (pos.vy<<12) + enemy->pos.vy;
	pos.vz = (pos.vz<<12) + enemy->pos.vz;
	ballPlaceAt(&pos);
	enemy_has_ball = 2;
}
/*************************************************************************************/
//#define ISLAND_RADIUS 250
#define ISLAND_RADIUS 270
void BabyKeepOnIsland(VECTOR *pos)
{
	VECTOR minipos;
	int s;
//	static VECTOR ref = {82,0,-135};
	static VECTOR ref = {72,0,-100};
	minipos.vx = pos->vx>>12;
	minipos.vy = pos->vy>>12;
	minipos.vz = pos->vz>>12;

// are we inside the rectangle?

	if(minipos.vx > -24 && minipos.vx < 188
		&& minipos.vz > -513 && minipos.vz < 0
	  )
	  return;

// are we inside the circle ?
	s = Magnitude2D(minipos.vx - ref.vx, minipos.vz - ref.vz);
	if(s < ISLAND_RADIUS)
		return;

// okeydokey, do some clamping.
// Beyond the pier
	if(minipos.vz < -513)
	{
		minipos.vz = -513;
	}
	else if(minipos.vz < -357)
	{
// Sides of pier
		if(minipos.vx < -24)
			minipos.vx = -24;
		if(minipos.vx > 188)
			minipos.vx = 188;
	}
	else
	{
// circle
		minipos.vx = ref.vx + (minipos.vx - ref.vx) * ISLAND_RADIUS / s;
		minipos.vz = ref.vz + (minipos.vz - ref.vz) * ISLAND_RADIUS / s;
	}

	pos->vx = minipos.vx << 12;
	pos->vz = minipos.vz << 12;
}

void Update_BabySpank(ENEMYPOS *enemy)
{
	VECTOR oldpos;

	oldpos = enemy->pos;

	if(enemy->flags & NMEFLAG_FROGGED)
	{
		Update_NMEFrog(enemy);
		return;
	}



	if(enemy->tag == 11)
		enemy->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);

	if(!(frame % 50) && !(enemy->flags & NMEFLAG_FROGGED))
	{
		if(RANDOM256() < 64)
			sfxPlayNME3D(levelFX, SFX_SPANK_CALL_1 + (RANDOM256() & 1),enemy);
	}


	switch(enemy->doing)
	{
	case BABY_POINTCHOOSE:
		enemy->ticker = 0;		
		enemy->doing = BABY_WANDERING;
		NMEChooseRandomPoint(enemy,YES);	// 2d point only, dude...

		break;

	case BABY_WANDERING:
		enemy->ticker++;
		NME_FaceTo(enemy,&enemy->pathvectors[2],100);
		if(NME_GoTo2D(enemy,&enemy->pathvectors[2],0x2400) || enemy->ticker > 100)
		{
			enemy->doing = BABY_POINTCHOOSE;
		}

		if(NMECheckInRange(enemy, &ballPos, 4096 * 300, TRUE))
		{
			enemy->doing = BABY_ATTACKING_BALL;
		}
		break;

	case BABY_ATTACKING_BALL:
		NME_FaceTo(enemy,&ballPos,100);
		NME_GoTo2D(enemy,&ballPos,0x2400);

// was 80
		if(NMECheckInRange(enemy, &ballPos, 4096 * 50, TRUE))
		{
			DisconnectGloveFromBall(DISCONNECT_WHEN_JOINED | DISCONNECT_WHEN_BOUNCING | DISCONNECT_WHEN_WALKINGON | DISCONNECT_GENTLY);

			enemy_has_ball = 2;
			enemy->doing = BABY_GRABBING_BALL;
			AddToQueue(&enemy->anim,NMEANIM_SPECIAL1,NO,NO,4096);
			AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
			enemy->ticker = 0;
		}

		break;


	case BABY_GRABBING_BALL:
// set the flag so that the glove can't grab the thing

		NME_FaceTo(enemy,&ballPos,100);
		NME_GoTo2D(enemy,&ballPos,0x2400);	// note that enemy <-> ball interaction is turned off here
		enemy->ticker++;
		if(enemy->ticker > 30)
		{
			enemy->doing = BABY_POINTCHOOSE2;
			enemy->ticker = 0;
		}
		BabyHasBall(enemy);
		break;

	case BABY_POINTCHOOSE2:
		enemy->ticker = 0;		
		enemy->doing = BABY_WITH_BALL;
		NMEChooseRandomPoint(enemy,YES);
		BabyHasBall(enemy);
		break;

	case BABY_WITH_BALL:
		enemy->ticker++;
		NME_FaceTo(enemy,&enemy->pathvectors[2],100);
		if(NME_GoTo2D(enemy,&enemy->pathvectors[2],0x2400) || enemy->ticker > 500)
		{
			enemy->doing = BABY_POINTCHOOSE2;
		}
		BabyHasBall(enemy);
		break;

	case BABY_FALLING:
		if(enemy->ticker <= BABY_FALL_TIME - 20)
			enemy->doing = BABY_ONGROUND;
// ...
	case BABY_ONGROUND:
	{
		enemy->ticker--;
		if(!enemy->ticker)
		{
			enemy->doing = BABY_RISING;
			enemy->ticker = 30;
			AddToQueue(&enemy->anim,NMEANIM_ACTION2,NO,NO,4096);
			AddToQueue(&enemy->anim,NMEANIM_RUN,YES,YES,4096);
		}
		StunDebris(&enemy->pos, 20, 25);
		break;
	}

	case BABY_RISING:
		enemy->ticker--;
		if(!enemy->ticker)
			enemy->doing = BABY_POINTCHOOSE;
		break;

	case BABY_GROWING:
		enemy->ticker++;
		if(enemy->ticker == BABY_GROWTIME)
		{
			enemy->doing = BABY_POINTCHOOSE;
			AddToQueue(&enemy->anim,NMEANIM_RUN,YES,YES,4096);
			enemy->doing = BABY_POINTCHOOSE;
		}
		break;

	case -1:
		enemy->flags |= NMEFLAG_FROGGED;


//		AddToQueue(&enemy->anim,NMEANIM_WALK,YES,NO,4096);
// "doing" is switched to "growing" by the cameo

//		enemy->doing = BABY_POINTCHOOSE;

//		AddToQueue(&enemy->anim,NMEANIM_RUN,YES,YES,4096);
//		enemy->doing = BABY_POINTCHOOSE;
		break;
	}

//	DB("height %d\n",(enemy->pos.vy + getHeightAt(enemy->pos.vx,enemy->pos.vy,enemy->pos.vz))>>12);
/*	
	if(
		   ((enemy->pos.vy + getHeightAt(enemy->pos.vx         ,enemy->pos.vy,enemy->pos.vz         ))>>12) > 100
		|| ((enemy->pos.vy + getHeightAt(enemy->pos.vx+(20<<12),enemy->pos.vy,enemy->pos.vz+(20<<12)))>>12) > 100
		|| ((enemy->pos.vy + getHeightAt(enemy->pos.vx+(20<<12),enemy->pos.vy,enemy->pos.vz-(20<<12)))>>12) > 100
		|| ((enemy->pos.vy + getHeightAt(enemy->pos.vx-(20<<12),enemy->pos.vy,enemy->pos.vz+(20<<12)))>>12) > 100
		|| ((enemy->pos.vy + getHeightAt(enemy->pos.vx-(20<<12),enemy->pos.vy,enemy->pos.vz-(20<<12)))>>12) > 100
	  )
	{
		enemy->pos.vx = oldpos.vx;
		enemy->pos.vz = oldpos.vz;
	}
*/	

	BabyKeepOnIsland(&enemy->pos);

	if(enemy->doing != -1 && enemy->doing != 0)
	{
		enemy->vel.vy += gravity;
		enemy->pos.vy += enemy->vel.vy;
//		oldpos.vy += 50 * 4096;
//		enemy->pos.vy  += 50 * 4096;
		enemy_UsePhysics(enemy, &EnemyHeavyPhysics, &oldpos, 18);
//		enemy_UsePhysics(enemy, &EnemyDefaultPhysics, &oldpos, 18);
//		enemy->pos.vy -= 50 * 4096;
	}
//	DB("baby doing %d\n",enemy->doing);
}
/***************************************************************************************/
void BabyPushOffBall(ENEMYPOS *nme)
{
//	DB("push off!\n");
	if(nme->doing == BABY_WITH_BALL || nme->doing == BABY_POINTCHOOSE2)
	{
// tbd - set the ball free before falling over
	}

	if(nme->doing != BABY_FALLING && nme->doing != BABY_ONGROUND)
	{
		AddToQueue(&nme->anim,NMEANIM_SPECIAL2,NO,NO,4096);
		AddToQueue(&nme->anim,NMEANIM_IDLE2,YES,YES,4096);
		nme->doing = BABY_FALLING;
		nme->ticker = BABY_FALL_TIME;
	}
	else
	{
//		DB("push off 2!\n");
		if(nme->doing != BABY_FALLING)
			sfxPlayNME3D(levelFX, SFX_PI_SPANK_OOF,nme);

		AddToQueue(&nme->anim,NMEANIM_HURT,NO,YES,4096);	// "oof"
		AddToQueue(&nme->anim,NMEANIM_IDLE2,YES,YES,4096);
		nme->doing = BABY_FALLING;
		nme->ticker = BABY_FALL_TIME;	// keep it down!
	}
}

/**********************************************************************************************/
void BabyIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)
	{
		if(GloveCtrl.action == HAND_SLAM|| GloveCtrl.action == HAND_SLAM2ST) // && Glover.animInfo->frame>=4)	// the frame check's a tad messy, but WTF?
		{
			BabyPushOffBall(nme);
			NMEPushesGlove(nme,dvec,sphererad);
// tbd - If on ground, go "oof"
		}
		else
		{
//			if(nme->doing == BABY_FALLING || nme->doing == BABY_ONGROUND || nme->doing == BABY_RISING)
//			{
//				DB("baby harmless\n");
				NMEPushesGlove(nme,dvec,sphererad);
//			}
//			else
//			{
//				DB("baby hurty\n");
//				NMEHurtsGlove(nme,dvec,sphererad);
//			}
		}
	}
	else
	{
// ball (collecting the ball is done elsewhere.
		if(nme->doing != BABY_GRABBING_BALL && nme->doing != BABY_WITH_BALL)
		{
			NMEPushesBall(nme,dvec,sphererad);
		}
	}
}


NME_SPHERE BabySpankSpheres[] =
{
	{&BabySpankSpheres[1],BabyIntRou,{  0,  12,  -15}, 16, 14},	// bod
	{&BabySpankSpheres[2],BabyIntRou,{  0,   0,  0},   11, 256+15},	// head

	{&BabySpankSpheres[3],BabyIntRou,{  0,   0,  0},   8,  256+5},	// lhand
	{                NULL,BabyIntRou,{  0,   0,  0},   8, 256+10},	// rhand

};



/*
ENEMY FRANKIE 0 40 0 12 0
	
	NORMAL_INSTRUCTION		NME_WAIT			270	0 		0	16384	ALWAYS 0 0
	NORMAL_INSTRUCTION		NME_SPECIAL			-1	-1		13	0	ALWAYS 0 0
	NORMAL_INSTRUCTION		NME_SPECIAL			1	-1		15	3	ALWAYS 0 0
	NORMAL_INSTRUCTION		NME_FOLLOWPLAYER	0	0 0 0	53	65	ALWAYS 0 0 
	NORMAL_INSTRUCTION		NME_ATTACK			0	0		0	0	ALWAYS 0 0
	NORMAL_INSTRUCTION		NME_BRANCH			0	2		0	0	ALWAYS 0 0

	NORMAL_INSTRUCTION		NME_SPECIAL			-1	-1		1	6	ALWAYS 0 0
	NORMAL_INSTRUCTION		NME_SPECIAL			-1	-1		13	0	ALWAYS 0 0
	NORMAL_INSTRUCTION		NME_SPECIAL			1	-1		15	3	ALWAYS 0 0
	NORMAL_INSTRUCTION		NME_ATTACK			0	0		0	0	ALWAYS 0 0
	NORMAL_INSTRUCTION		NME_BRANCH			0	1		0	0	ALWAYS 0 0

//	CONDITIONAL_INSTRUCTION	NME_ATTACK			0	0		0	0	SOMETIMES 1 0

	ATTACK_INSTRUCTION		NME_MOVETO2D		0	0 0 0	20	16385	ALWAYS 0 0
	ATTACK_INSTRUCTION		NME_SPECIAL			-1	-1		14	6	ALWAYS 0 0
	ATTACK_INSTRUCTION		NME_WAIT			50	0 		1	0	ALWAYS 0 0
	ATTACK_INSTRUCTION		NME_SPECIAL			-1	-1		4	0	ALWAYS 0 0
	ATTACK_INSTRUCTION		NME_SPECIAL			1	-1		9	1	ALWAYS 0 0
	ATTACK_INSTRUCTION		NME_FOLLOWPLAYER	480	0 0 0	110	65	ALWAYS 0 0 
	ATTACK_INSTRUCTION		NME_SPECIAL			-1	-1		12	2	ALWAYS 0 0
	ATTACK_INSTRUCTION		NME_SPECIAL			-1	-1		0	0	ALWAYS 0 0
	ATTACK_INSTRUCTION		NME_SPECIAL			-1	-1		3	0	ALWAYS 0 0

	ATTACK_INSTRUCTION		NME_SPECIAL			-1	-1		13	0	ALWAYS 0 0
	ATTACK_INSTRUCTION		NME_SPECIAL			1	-1		15	3	ALWAYS 0 0
	ATTACK_INSTRUCTION		NME_MOVETO2D		0	0 0 0	30	16385	ALWAYS 0 0
	ATTACK_INSTRUCTION		NME_ENDATTACK		0	50		0	16384	ALWAYS 100 10000
	
END_ENEMY
*/

#define FRANK_LECCY2CENTRE	0
#define FRANK_CENTRE2KICK	1
#define FRANK_KICKING		2
#define FRANK_KICK2CENTRE	3
#define FRANK_DORMANT		4
#define FRANK_WAKEUPCALL	5
#define FRANK_CENTRE2LECCY	6
#define FRANK_LECCY			7
#define FRANK_STUN_LECCY	8
#define FRANK_DYING			9
#define FRANK_DEAD			10

#define FRANK_LECCY2CENTRE_FIRST	20

char FearSnapFlag = 0;

char FearBossResetFlag = 0;
// There's a bug - leccifiedplatno is actually the opposite one to fearleccifiedplat
// (but all the maths is now done for it, so I'm leaving it as is, and correcting when needed)
char FearLeccifiedPlatno;
DYNCOLLBOX *FearLeccifiedPlat;
int  FearLeccifiedTimer;

int	 FearCrossLeccyPlatno;
int  FearCrossLeccyTimer;
int FearCrossPlatTimer;
int FearCrossPlatDurn;

// range = 180 for kicking, less for electrocuting
void Frank_PickTarget(ENEMYPOS *enemy, int range)
{
	int ya;
	ya = (calc_angle( (glovePos.vx)>>8, (glovePos.vz)>>8) & 4095);	// 0...0xfff
	ya = (ya + 0x100) & 0xE00;	// 1110 0000 0000

	enemy->pathvectors[0].vx = range * rsin(ya);
	enemy->pathvectors[0].vy = -40<<12;
	enemy->pathvectors[0].vz = range * rcos(ya);

	enemy->pathvectors[1].vx = enemy->pathvectors[0].vx <<1;
	enemy->pathvectors[1].vy = -40<<12;
	enemy->pathvectors[1].vz = enemy->pathvectors[0].vz <<1;

	enemy->n_points = ya >>9;	// so we've got a record of the platform number
}

#define FRANK_RANGE (5<<12)
#define FRANK_KICK_TIME 50
#define FRANK_DORMANT_TIME 100
#define FRANK_WAKEUP_TIME 100
#define FRANK_LECCY_TIME 100

// how long the platform stays leccified
//#define FEAR_LECCIFIED_TIME 400
#define FEAR_LECCIFIED_TIME 190


//#define FFBOSS_PLATBASE 100
//#define FFBOSS_RESET	108

#define FFBOSS_PLATBASE 20
#define FFBOSS_RESET	10

// Gawd knows why this doesn't work with the ballswitch
// (? because it's speed 5, and so is never actually in movement? )
//#define FFBOSS_KILLPLAT 174	// portcullis
#define FFBOSS_KILLPLAT 116		// ballswitch


void Frank_Leccyblobs(ENEMYPOS *nme)
{
	static int i;
	int point;
	static VECTOR posref = {0,30,0};
	VECTOR pos;

	i++;
	if(i & 1)
		point = 5;	//9;
	else
		point = 8;	//15;

	EnemyRotatePoint(nme,&posref, &pos, point);
	pos.vx = (pos.vx<<12) + nme->pos.vx;
	pos.vy = (pos.vy<<12) + nme->pos.vy;
	pos.vz = (pos.vz<<12) + nme->pos.vz;

	if(RANDOM256() & 1)
		New_Debris(DEBRIS_FRANKLECCY, &pos, RANDOM256() << 4);
	else
		New_Debris(DEBRIS_FRANKLECCY2, &pos, RANDOM256() << 4);
}


void Update_Frank(ENEMYPOS *enemy)
{
	static VECTOR centre = {0,-40<<12,0};
	static VECTOR far = {0,-40<<12,100<<12};
	int f;

	f = enemy->anim.animInfo->frame;
	f+=enemy->anim.animInfo->segInfo[enemy->anim.currentAnimation].segStart;


// sound effects
// 		AddAmbientSfx(FX_FRANKIE_GENERALHUM, 50, 128);
//						PlaySample(FX_FRANKIE_ATTACK2, &actor->pos, 130,128); is slamming the platforms

//					PlaySample(FX_FRANKIE_CALL, &actor->pos, 110,128);	when stunned

// Here's the kick (tsk!)
//			PlaySample(FX_MIKE_ATTACK, &actor->pos, 120,128);

// (incidientally, stunned is the whold star-spinning deal
// Conditions for stunning Frankie
//		if((actor->animation->currentAnimation != NMEANIM_HURT)
//		&& (actor->animation->currentAnimation != NMEANIM_ACTION1)
//		&& (actor->animation->currentAnimation != NMEANIM_ENDATTACK)
//		&& (actor->animation->currentAnimation != NMEANIM_RUN)
//		&& (actor->animation->currentAnimation != NMEANIM_STARTATTACK)
//	&& (actor->animation->currentAnimation != NMEANIM_SPECIAL1)
//	&& (actor->animation->currentAnimation != NMEANIM_SPECIAL2))

// Thunder, but that's there already


// Here's the death sequence
//			AnimateActor(&enemies[0].actor, NMEANIM_SPECIAL1, NO, NO, 0);
//			AnimateActor(&enemies[0].actor, NMEANIM_SPECIAL2, YES, YES, 0);
//			frankieDead = 100;
//			PlayContinuousSample(&enemy->sfx[0],FX_FRANKIE_ATTACK_LOOPED, 160, &hand.pos, 128);

//			PlaySample(FX_FRANKIE_DEATH, &hand.pos, 200, 128);

/*
	if(QueryAnimTime(actor, 15))
		PlaySample(FX_FRANKIE_WALK_LEFT, &actor->pos, 100,128);
	if(QueryAnimTime(actor, 34))
		PlaySample(FX_FRANKIE_WALK_LEFT, &actor->pos, 100,128);
	if(QueryAnimTime(actor, 59))
		PlaySample(FX_FRANKIE_WALK_LEFT, &actor->pos, 100,128);
	if(QueryAnimTime(actor, 89))
		PlaySample(FX_FRANKIE_WALK_LEFT, &actor->pos, 100,128);
	if(QueryAnimTime(actor, 106))
		PlaySample(FX_FRANKIE_WALK_RIGHT, &actor->pos, 100,128);


*/


	switch(enemy->doing)
	{
		case FRANK_LECCY2CENTRE:
			NME_FaceTo(enemy,&centre,50);
			enemy->ticker++;
			if(enemy->ticker > 10)
			{
				NME_GoForwards(enemy,3<<12);
			}
			if(NMECheckInRange(enemy, &centre, FRANK_RANGE, 0))
			{
				Frank_PickTarget(enemy,120);	//180);
				enemy->doing = FRANK_CENTRE2KICK;
			}
			break;

		case FRANK_LECCY2CENTRE_FIRST:
			NME_FaceTo(enemy,&far,50);
			enemy->ticker++;
			if(enemy->ticker > 18)
			{
				NME_GoForwards(enemy,3<<12);	// first time round, he takes his time
			}
			if(NMECheckInRange(enemy, &far, FRANK_RANGE, 0))
			{
				Frank_PickTarget(enemy,120);	//180);
				enemy->doing = FRANK_CENTRE2KICK;
			}
			break;

		case FRANK_CENTRE2KICK:
			NME_FaceTo(enemy,&enemy->pathvectors[0],50);
			NME_GoForwards(enemy,3<<12);
			if(NMECheckInRange(enemy, &enemy->pathvectors[0], FRANK_RANGE, 0))
			{
				AddToQueue(&enemy->anim,NMEANIM_ACTION2,NO,YES,4096);
				AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);

				enemy->doing = FRANK_KICKING;
				enemy->ticker = FRANK_KICK_TIME;
			}
			break;
		case FRANK_KICKING:
			enemy->ticker--;
			if(!enemy->ticker)
			{
				enemy->doing = FRANK_KICK2CENTRE;
			}
			break;

		case FRANK_KICK2CENTRE:
			NME_FaceTo(enemy,&centre,50);
			enemy->ticker++;
			if(enemy->ticker > 10)
			{
				NME_GoForwards(enemy,3<<12);
			}
			if(NMECheckInRange(enemy, &centre, FRANK_RANGE, 0))
			{
				AddToQueue(&enemy->anim,NMEANIM_STOPMOVE,NO,YES,4096);
				AddToQueue(&enemy->anim,NMEANIM_IDLE,YES,YES,4096);
				enemy->ticker = FRANK_DORMANT_TIME;
				enemy->doing = FRANK_DORMANT;
			}
			break;

		case FRANK_DORMANT:
			enemy->ticker--;
			if(!enemy->ticker)
			{
				sfxPlay(levelFX,SFX_FRANKIE_ATTACK2);

				AddToQueue(&enemy->anim,NMEANIM_SPECIAL1,NO,YES,4096);
				AddToQueue(&enemy->anim,NMEANIM_SPECIAL2,YES,YES,4096);
				enemy->ticker = FRANK_WAKEUP_TIME;
				enemy->doing = FRANK_WAKEUPCALL;
			}
			break;

		case FRANK_WAKEUPCALL:
			enemy->ticker--;

			if(!enemy->ticker)
			{
				AddToQueue(&enemy->anim,NMEANIM_RUN,YES,YES,4096);

				Frank_PickTarget(enemy,100);	//140);
				enemy->doing = FRANK_CENTRE2LECCY;
			}
			break;

		case FRANK_CENTRE2LECCY:
			Frank_Leccyblobs(enemy);

			NME_FaceTo(enemy,&enemy->pathvectors[0],50);
			NME_GoForwards(enemy,3<<12);
			if(NMECheckInRange(enemy, &enemy->pathvectors[0], FRANK_RANGE, 0))
			{
				AddToQueue(&enemy->anim,NMEANIM_STARTATTACK,NO,YES,4096);
				AddToQueue(&enemy->anim,NMEANIM_ACTION1,YES,YES,4096);
				enemy->doing = FRANK_LECCY;
				enemy->ticker = FRANK_LECCY_TIME;
			}
			break;

		case FRANK_LECCY:
			Frank_Leccyblobs(enemy);
			NME_FaceTo(enemy,&enemy->pathvectors[1],50);	// face the further out one
			enemy->ticker--;
			if(enemy->ticker == FRANK_LECCY_TIME - 20)
			{
				FearLeccifiedPlatno = enemy->n_points;
				loadlndFindPlatform(((FearLeccifiedPlatno+4)&7)+FFBOSS_PLATBASE, NULL, &FearLeccifiedPlat);

//				DB("Leccifying plat %d\n",FearLeccifiedPlatno);
				FearLeccifiedTimer  = FEAR_LECCIFIED_TIME;

			}
			if(!enemy->ticker)
			{

				AddToQueue(&enemy->anim,NMEANIM_ENDATTACK,NO,NO,4096);	// not really much point in waiting for loopend
				AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
				enemy->doing = FRANK_LECCY2CENTRE;
			}
			break;


// when you slam a platform, a leccy bolt goes from your platform to the opposite one.
// If Frank's in its path, he gets stunned

		case FRANK_STUN_LECCY:
			StunDebris(&enemy->pos,nmeinfo[enemy->type].radius,18);

			enemy->ticker--;
			if(!enemy->ticker)
			{
				AddToQueue(&enemy->anim,NMEANIM_WALK,YES,YES,4096);
				enemy->doing = FRANK_LECCY2CENTRE;
				enemy->ticker = 0;
			}
			break;

		case FRANK_DEAD:
			break;

		case -1:
			enemy->ticker++;
			if(enemy->ticker > 5 && !cameo_running)
			{
				enemy->doing = FRANK_LECCY2CENTRE_FIRST;
				enemy->ticker = 0;
			}
			break;
	}


	switch(f)
	{
	case 15:
	case 34:
	case 59:
	case 89:
		sfxPlayFull3D(levelFX,SFX_FRANKIE_WALK_LEFT,&enemy->pos);
		break;

	case 106:
//		sfxPlayFull3D(levelFX,SFX_FRANKIE_WALK_RIGHT,&enemy->pos);
		sfxPlayFull3D(levelFX,SFX_FRANKIE_WALK_LEFT,&enemy->pos);
		break;
	case 229:
		sfxPlayFull3D(levelFX,SFX_MIKE_ATTACK,&enemy->pos);
		break;
	}
}

#define FRANK_STUN_TIME 100

// Frankie got caught in the cross-leccification
void StunFrankie(ENEMYPOS *nme)
{
	if(nme->doing == FRANK_STUN_LECCY
		|| nme->doing == FRANK_DEAD
		|| nme->doing == FRANK_WAKEUPCALL
		|| nme->doing == FRANK_CENTRE2LECCY
		|| nme->doing == FRANK_LECCY
		|| (nme->flags & NMEFLAG_STUNNED)
		)
		return;

//	AddToQueue(&nme->anim,NMEANIM_HURT,YES,NO,4096);
//	nme->ticker = FRANK_STUN_TIME;
//	nme->doing = FRANK_STUN_LECCY;
	EnemyStun(nme);
	sfxPlayFull3D(levelFX,SFX_FRANKIE_CALL,&nme->pos);

}

// opening the exit is done by the LND
void KillFrankie(ENEMYPOS *nme)
{
	nme->doing = FRANK_DEAD;
	nme->ticker = 0;
	AddToQueue(&nme->anim,NMEANIM_DEATH,NO,NO,4096);
	sfxPlayFull3D(levelFX,SFX_FRANKIE_WALK_LEFT,&nme->pos);

}

void FrankIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)
	{
		if(nme->doing == FRANK_DEAD || nme->flags & NMEFLAG_STUNNED)
		{
			if(GloveCtrl.handOnBall || GloveCtrl.ballWithHand)
				NMEPushesBall(nme,dvec,sphererad);
			else
				NMEPushesGlove(nme,dvec,sphererad);
		}
		else
		{
			if(GloveCtrl.action == HAND_SLAM || GloveCtrl.action == HAND_SLAM2ST)
			{
				EnemyStun(nme);	// yup, he uses the standard stun routine
				NMEPushesGlove(nme,dvec,sphererad);
			}
			else if(GloveCtrl.handOnBall || GloveCtrl.ballWithHand)
			{
	//			if(!IsBallShield(dvec))
				DisconnectGloveFromBall(DISCONNECT_WHEN_JOINED | DISCONNECT_WHEN_BOUNCING | DISCONNECT_WHEN_WALKINGON);
	//			NMEPushesBall(nme,dvec,sphererad);
				NMEHurtsGlove(nme,dvec,sphererad);
			}
			else
			{
		// otherwise, hurt the glove & push it out
	//			if(!IsBallShield(dvec))
				NMEHurtsGlove(nme,dvec,sphererad);
			}
		}
	}
	else
	{
//		if(nme->doing == WILLY_DEAD)
//		{
			NMEPushesBall(nme,dvec,sphererad);
//		}

	}
}


/*
NME_SPHERE FrankSpheres[] =
{
	{&FrankSpheres[1],FrankIntRou,{  0, -20,  0}, 35, 7},	// bod
	{&FrankSpheres[2],FrankIntRou,{  0,  -5,  0}, 25, 256+20},	// head

	{&FrankSpheres[3],FrankIntRou,{  0,  25, -20}, 20, 256+1},	// rfoot
	{&FrankSpheres[4],FrankIntRou,{  0,  25, -20}, 20, 256+4},	// lfoot

	{&FrankSpheres[5],FrankIntRou,{  0,  18,  0}, 20, 256+9},	// rhand
	{            NULL,FrankIntRou,{  0,  18,  0}, 20, 256+15},	// lhand

};
*/
NME_SPHERE FrankSpheres[] =
{
	{&FrankSpheres[1],FrankIntRou,{  0, -20,  0}, 35, 2},		// bod
	{&FrankSpheres[2],FrankIntRou,{  0,  -5,  0}, 25, 256+10},	// head

	{&FrankSpheres[3],FrankIntRou,{  0,  25, -20}, 20, 256+1},	// rfoot
	{&FrankSpheres[4],FrankIntRou,{  0,  25, -20}, 20, 256+2},	// lfoot

	{&FrankSpheres[5],FrankIntRou,{  0,  18,  0}, 20, 256+5},	// rhand
	{            NULL,FrankIntRou,{  0,  18,  0}, 20, 256+8},	// lhand

};






// ===============================================================================================================

#define KLOSET_SPINNING		1
#define KLOSET_FALLING		2

// this lot are done with a 0,1,2,3,4 sequence
#define KLOSET_CHASING1		10
#define KLOSET_THROWING1	11
#define KLOSET_CHASING2		12
#define KLOSET_THROWING2	13
#define KLOSET_WALK2CENTRE	14

void Update_CaPie(ENEMYPOS *nme)
{
	switch(nme->doing)
	{
	case 0:		// it's airborne
		nme->pos.vx += nme->vel.vx;
		nme->pos.vy += nme->vel.vy;
		nme->pos.vz += nme->vel.vz;

		nme->vel.vy += 1024;

		if(nme->pos.vy > 0)
		{
//			nme->doing = 1;
//			nme->ticker = 0;
			nme->flags &=(~NMEFLAG_ACTIVE + NMEFLAG_ENABLED);	// the carnival pie is a pre-allocced enemy, not a multiple bullet
			sfxPlayFull3D(levelFX, SFX_KLOSET_ATTACK,&nme->pos);
			New_Debris(DEBRIS_DUST,&nme->pos,0x201810);

		}
		break;

/*
	case 1:
		nme->ticker++;
		if(nme->ticker > 1)
			nme->flags &=(~NMEFLAG_ACTIVE + NMEFLAG_ENABLED);	// the carnival pie is a pre-allocced enemy, not a multiple bullet
		break;
*/

// It's being held by Kloset
// If Kloset gets knocked over whilst holding it, turn the pie back off
	case -1:
	{
		ENEMYPOS *kloset;
		kloset = loadlndFindEnemy(10);

		if(kloset->doing != KLOSET_THROWING1 && kloset->doing != KLOSET_THROWING2)
		{
			ca_pie_enemy->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
		}

		break;
	}
	}
}
void PieIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)
	{
		if(!IsBallShield(dvec))
			NMEHurtsGlove(nme, dvec, sphererad);
	}
	else
	{
		NMEPushesBall(nme, dvec, sphererad);	// very light push
	}
}

void Update_CaBomb(ENEMYPOS *nme)
{
	switch(nme->doing)
	{
	case 0:
	case 1:
	case 2:		// it's airborne
		nme->pos.vx += nme->vel.vx;
		nme->pos.vy += nme->vel.vy;
		nme->pos.vz += nme->vel.vz;

		nme->vel.vy += gravity;

		nme->ya += 0x30;
		nme->xa += 0x20;

		if(nme->pos.vy > 0)
		{
			if(nme->vel.vy > 0)
			{

				sfxPlayNME3D(globalFX, SFX_IMPACT_WOOD_MEDIUM,nme);
				nme->vel.vy = - (nme->vel.vy / 2);
				nme->doing++;
				nme->ticker = 0;
				New_Debris(DEBRIS_DUST,&nme->pos,0x201810);
			}
		}
		break;
	case 3:	// explode
	{
		VECTOR temp;
		ENEMYPOS *kloset;
		if(nme->ticker == 0)
		{
			sfxPlayNME3D(levelFX, SFX_KLOSET_BOMB,nme);

			effectsStartOverlay(20, 0xff00ff);	// pink flash (oooh!)
//			ForceExplosion(&nme->pos, 100 * 5, 40);	// kaboom...nah.

			kloset = loadlndFindEnemy(10);

// make Kloset fall over
			if(NMECheckInRange(kloset, &nme->pos, 100<<12, 0))
			{
				sfxPlayNME3D(levelFX, SFX_TROUSERS_FALLDOWN,kloset);
				kloset->doing = KLOSET_FALLING;
				kloset->ticker = 0;
			}
		}

		if(nme->ticker == 20)
		{
			nme->flags &= (~NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
		}
//		if(enemy->ticker == 1)
//			effectsStartOverlay(10, 0xffff00);

		temp = nme->pos;
		temp.vx += (random(64)-32)<<12;
		temp.vy -= (random(64)-32)<<12;
		temp.vz += (random(64)-32)<<12;
//		New_Debris(DEBRIS_STINGEXPLODE,&temp,effectsColourNear(0x106080, 0x40));
		New_Debris(DEBRIS_STINGEXPLODE,&temp,effectsColourNear(0xA04090, 0x40));	// pink explosion, and why not?

		nme->ticker++;
		break;
	}
	}
}

void Update_CaGlove(ENEMYPOS *nme)
{
	nme->flags |= NMEFLAG_DEAD;	// prevent the targets from reappearing. Note that this enemy needs to be tagged



// Turn on the nose
/*
	{
		ENEMYPOS *nose;
		nose = loadlndFindEnemy(301);
		if(nose)
		{
			nose->psa->globalscale.vx=16384;
			nose->psa->globalscale.vy=16384;
			nose->psa->globalscale.vz=16384;
			nose->flags |= NMEFLAG_ACTIVE + NMEFLAG_ENABLED;
		}
	}
*/
	if(nme->ticker == 0)
	{
		ENEMYPOS *kloset;
		kloset = loadlndFindEnemy(10);
// make Kloset fall over
		kloset->doing = KLOSET_FALLING;
		sfxPlayNME3D(levelFX, SFX_TROUSERS_FALLDOWN,kloset);
		kloset->ticker = 0;
	}

	if(nme->ticker >= 50)
	{
		nme->flags &= (~NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
	}
	nme->ticker++;

}

/*
void NoseIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	ENEMYPOS *nose;
	nose = loadlndFindEnemy(301);

	if(nme == nose && with == 1)
	{
// tbd - drop a piano on his head
		ENEMYPOS *kloset;
		kloset = loadlndFindEnemy(10);
// make Kloset fall over
		kloset->doing = KLOSET_FALLING;
		kloset->ticker = 0;
	}
}
*/

#define PIANO_HITY ((-100)<<12)
#define PIANO_FLOOR ((-50)<<12)

void Update_CaPiano(ENEMYPOS *nme)
{
	switch(nme->doing)
	{
	case 0:

	case 1:
	case 2:		// it's airborne

		nme->vel.vy += gravity;
		nme->pos.vy += nme->vel.vy;

		if(nme->pos.vy > PIANO_HITY && nme->doing == 0)
		{
			ENEMYPOS *kloset;
			kloset = loadlndFindEnemy(10);
	// make Kloset fall over
			kloset->doing = KLOSET_FALLING;
			kloset->ticker = 0;
		}


		if(nme->pos.vy >= PIANO_FLOOR)
		{

			nme->pos.vy = PIANO_FLOOR;
			nme->doing++;
			if(nme->vel.vy > 0)
			{
				sfxPlayNME3D(levelFX, SFX_KLOSET_PIANOFALL,nme);

				nme->vel.vy = -(nme->vel.vy/2);
				AddToQueue(&nme->anim,1,NO,NO,4096);
			}
		}
		break;
	case 3:
		nme->pos.vy = PIANO_FLOOR;
		nme->vel.vy = 0;

		nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
		nme->flags |=  (NMEFLAG_DEAD);

		puzzleSetExistence(14,1);	// Turn on the platform piano

		break;
	}
}

/*
ENEMY KLOSET 0 50 200 0 3
	NORMAL_INSTRUCTION		NME_WAIT			350	0		0	16384	ALWAYS 0 0 (don't anim)
	NORMAL_INSTRUCTION		NME_SPECIAL			1	-1		15	5	ALWAYS 0 0		(move accel, slow when near)
	NORMAL_INSTRUCTION		NME_MOVETO2D		0	0 0 0	20	16389	ALWAYS 0 0	(don't anim, move accel, slow when near)
	NORMAL_INSTRUCTION		NME_SPECIAL			-1	-1		1	2	ALWAYS 0 0		anim 1 (action 2)
	NORMAL_INSTRUCTION		NME_SPECIAL			-1	0		0	0	ALWAYS 0 0		special0
	NORMAL_INSTRUCTION		NME_SPECIAL			-1	-1		5	0	ALWAYS 0 0		anim 5 (idle)
	
	NORMAL_INSTRUCTION		NME_SPECIAL			1	-1		15	1	ALWAYS 0 0		anim 15 (walk)
	NORMAL_INSTRUCTION		NME_FOLLOWPLAYER	400	0 0 0	150	16453	ALWAYS 0 0	(&4045) = don;t anim, hand, accel, slow)
	NORMAL_INSTRUCTION		NME_SPECIAL			-1	-1		0	1048582	ALWAYS 0 0	anim 0 (action 1)

	NORMAL_INSTRUCTION		NME_SPECIAL			1	-1		15	1	ALWAYS 0 0		anim 15 (walk)
	NORMAL_INSTRUCTION		NME_FOLLOWPLAYER	400	0 0 0	150	16453	ALWAYS 0 0	(&4045) = don't anim, hand, accel, slow.
	NORMAL_INSTRUCTION		NME_SPECIAL			-1	-1		0	1048582	ALWAYS 0 0	anim 0 (action 1),&1...6 = face hand, vel slow

	NORMAL_INSTRUCTION		NME_BRANCH			0	1		0	0	ALWAYS 0 0




	NORMAL_INSTRUCTION		NME_SPECIAL			1	-1		15	5	ALWAYS 0 0		anim 15 (idle) (move accel, slow)
	NORMAL_INSTRUCTION		NME_MOVETO2D		0	0 0 0	25	16389	ALWAYS 0 0	walk to origin (no anim, mive accel, slow)
	NORMAL_INSTRUCTION		NME_SPECIAL			1	-1		1	2	ALWAYS 0 0		anim 1 (action2)
	NORMAL_INSTRUCTION		NME_SPECIAL			-1	0		0	2	ALWAYS 0 0		special0
	NORMAL_INSTRUCTION		NME_SPECIAL			-1	-1		5	0	ALWAYS 0 0		anim 5 (idle)

	NORMAL_INSTRUCTION		NME_SPECIAL			1	-1		15	1	ALWAYS 0 0		anim 15 (walk)
	NORMAL_INSTRUCTION		NME_FOLLOWPLAYER	-1	0 0 0	150	16453	ALWAYS 0 0	hand, moveaccel, slow
	NORMAL_INSTRUCTION		NME_SPECIAL			-1	-1		0	1048582	ALWAYS 0 0	anim 0 (action 1),&1...6 = face hand, vel slow

	NORMAL_INSTRUCTION		NME_BRANCH			0	18		0	0	ALWAYS 0 0

END_ENEMY
*/


// it's assumed that the pie is already correctly placed in his hand at the end of a throwing move
void Kloset_FirePie(ENEMYPOS *nme)
{
	VECTOR temp;
	int ya;
	int mag;

	temp = glovePos;

	if(GloveCtrl.handOnBall || GloveCtrl.ballWithHand)
	{
		temp.vx += ballVel.vx << 5;
		temp.vz += ballVel.vz << 5;
	}
	else
	{
	}


// tbd - adjust targ for the spinning platform



	temp.vx = (temp.vx - nme->pos.vx)>>12;
	temp.vy = 0;
	temp.vz = (temp.vz - nme->pos.vz)>>12;



	//mag = Magnitude(&temp);	// 300 * 4096 needs speed 7, ya 4
	mag=Magnitude2D(temp.vx,temp.vz);

//	ya = (calc_angle( (temp.vx)>>8, (temp.vz)>>8) & 4095);
	ya = (calc_angle( (temp.vx), (temp.vz)) & 4095);

	nme->doing = 0;
	nme->vel.vx = rsin(ya) * 7;
	nme->vel.vy = -4 * 4096 * mag / 600;	//300;
	nme->vel.vz = rcos(ya) * 7;

}

void Update_Kloset(ENEMYPOS *nme)
{
	static VECTOR cen = {0,(-40)<<12,0};	// this allows for his centre point
	static int klos_speeds[4] = {0,0x5000,0x4000,0x3000};
	int f;

	f = nme->anim.animInfo->frame;
	f+= nme->anim.animInfo->segInfo[nme->anim.currentAnimation].segStart;

	switch(f)
	{
	case 16:
		sfxPlayFull3D(levelFX, SFX_KLOSET_WALK_LEFT,&nme->pos);
		break;
	case 32:
		sfxPlayFull3D(levelFX, SFX_KLOSET_WALK_RIGHT,&nme->pos);
		break;
	}
	CamVars.requiredHeight = -180;	//-150;

	switch(nme->doing)
	{

	case KLOSET_FALLING:
		if(!nme->ticker)
		{
			boss_n_powers--;

			if(!boss_n_powers)	// this needs to be later in the anim, really
			{
				nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
				nme->flags |= (NMEFLAG_DEAD);

				if(ca_pie_enemy->doing == -1)	// if Kloset was holding the pie, kill it
					ca_pie_enemy->flags |= NMEFLAG_DEAD;

				effectsStartOverlay(20, 0xffc0ff);	// pink flash (oooh!)

			}

			AddToQueue(&nme->anim,NMEANIM_HURT,NO,NO,4096);
			AddToQueue(&nme->anim,NMEANIM_IDLE,YES,YES,4096);
		}
		else
		{
			if(nme->anim.currentAnimation == NMEANIM_IDLE)
			{
				AddToQueue(&nme->anim,NMEANIM_STARTMOVE,NO,YES,4096);
				nme->doing = KLOSET_WALK2CENTRE;
				nme->ticker = 0;
			}
		}
		nme->ticker++;
		break;

	case KLOSET_WALK2CENTRE:

		if(NME_FaceTo(nme, &cen, 40))
		if(NME_GoTo3D(nme,&cen,0x4000))
		{
			nme->doing = KLOSET_SPINNING;
			nme->ticker = 0;
			AddToQueue(&nme->anim,NMEANIM_STOPMOVE,NO,YES,4096);
			AddToQueue(&nme->anim,NMEANIM_ACTION2,NO,YES,4096);
			AddToQueue(&nme->anim,NMEANIM_SPECIAL1,NO,YES,4096);
			AddToQueue(&nme->anim,NMEANIM_IDLE,YES,YES,4096);
		}
		break;

	case KLOSET_SPINNING:
		nme->ticker++;
		switch(boss_n_powers)
		{
		case 1:
			if(caboss_spin > -0x28)
				caboss_spin -= 1;

		case 2:
			if(caboss_spin > -0x24)
				caboss_spin -= 1;
		case 3:
		default:
			if(caboss_spin > -0x20)
				caboss_spin -= 1;
		}

		if(!(nme->ticker & 3))
			sfxPlayNME3D(levelFX, SFX_KLOSET_SPINFLOOR,nme);

		if(nme->anim.currentAnimation == NMEANIM_IDLE)
		{
			sfxPlayFull3D(levelFX, SFX_KLOSET_CALL,&nme->pos);

			nme->doing = KLOSET_CHASING1;
			nme->ticker = 0;
			AddToQueue(&nme->anim,NMEANIM_STARTMOVE,NO,YES,4096);
			AddToQueue(&nme->anim,NMEANIM_WALK,YES,YES,4096);
		}
		break;

	case KLOSET_CHASING1:
	case KLOSET_CHASING2:

		NME_FaceTo(nme, &glovePos, 40);
		CamVars.requiredHeight = -290;

		if(nme->anim.currentAnimation == NMEANIM_WALK)
		{
			NME_GoTo2D(nme,&glovePos,klos_speeds[boss_n_powers]);

			if(NMECheckInRange(nme, &glovePos, 200 <<12, 0))
			{
				nme->doing++;	// chasing1->throwing1, chasing2->throwing2
				AddToQueue(&nme->anim,NMEANIM_STOPMOVE,NO,YES,4096);
				AddToQueue(&nme->anim,NMEANIM_ACTION1,NO,YES,4096);
				AddToQueue(&nme->anim,NMEANIM_STARTMOVE,NO,YES,4096);
			}
		}
		break;

	case KLOSET_THROWING1:
	case KLOSET_THROWING2:

//		printf("throwing %d\n",f);
		NME_FaceTo(nme, &glovePos, 40);

		CamVars.requiredHeight = -290;

		if(nme->anim.currentAnimation == NMEANIM_ACTION1)
		{
			CamVars.requiredHeight = -380;

			if(f >= 41 && f <= 55)
			{
				static VECTOR posref = {0,0,0};
				VECTOR pos;

	//			printf("pie\n");

				EnemyRotatePoint(nme,&posref, &pos, 9);
				pos.vx = (pos.vx<<12) + nme->pos.vx;
				pos.vy = (pos.vy<<12) + nme->pos.vy;
				pos.vz = (pos.vz<<12) + nme->pos.vz;
				ca_pie_enemy->pos = pos;

				ca_pie_enemy->flags |= NMEFLAG_ACTIVE + NMEFLAG_ENABLED;
				ca_pie_enemy->doing = -1;
			}
			if(f == 55)
			{
				Kloset_FirePie(ca_pie_enemy);
			}
		}
		if(nme->anim.currentAnimation == NMEANIM_STARTMOVE)
		{
			nme->doing++;	// throwing1 -> chasing2,  throwing2 -> movetocentre
			AddToQueue(&nme->anim,NMEANIM_WALK,YES,YES,4096);
		}
		break;

	default:
	case -1:
		nme->ticker++;
		nmeinfo[KLOSET].radius = 8;

		if(nme->ticker>2 && !cameo_running)
		{
			nmeinfo[KLOSET].radius = 60;
			nme->doing = KLOSET_WALK2CENTRE;
			nme->ticker = KLOSET_WALK2CENTRE;
			AddToQueue(&nme->anim,NMEANIM_STARTMOVE,NO,NO,4096);
			AddToQueue(&nme->anim,NMEANIM_WALK,YES,YES,4096);
			boss_n_powers = old_boss_n_powers = 3;
		}
		break;
	}

//	printf("Klos doing %d\n",nme->doing);
}


// normal solid-to-ball, hurty-to-glove stuff
void KlosIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with==0)
	{
		if(GloveCtrl.handOnBall || GloveCtrl.ballWithHand)
		{
			if(!IsBallShield(dvec))
				DisconnectGloveFromBall(DISCONNECT_WHEN_JOINED | DISCONNECT_WHEN_BOUNCING | DISCONNECT_WHEN_WALKINGON);
			NMEPushesBall(nme,dvec,sphererad);
		}
		else
		{
	// otherwise, hurt the glove & push it out
			if(!IsBallShield(dvec))
				NMEHurtsGlove(nme,dvec,sphererad);

		}
	}
	else
	{
		NMEPushesBall(nme,dvec,sphererad);
	}
}

// if you're below the boot, you should get squished
void KBootIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	KlosIntRou(with,nme,dvec,sphererad);
}

// This is *really* making the game chug - EnemyRotatePoint needs some severe optimisation...

NME_SPHERE KlosSpheres[7] =
{
	{&KlosSpheres[1],	KlosIntRou,{  0,   0,   0},   25,  5},	// bod
	{&KlosSpheres[2],	KlosIntRou,{  0,   0, -10},   15, 256+18},	// head

	{&KlosSpheres[3],	KlosIntRou,{  0,   0,   0},   10, 256+ 7},	// left arm
	{&KlosSpheres[4],	KlosIntRou,{  0,   0,   0},   10, 256+ 8},	// left hand
	{&KlosSpheres[5],	KlosIntRou,{  0,   0,   0},   18, 256+12},	// right hand (plus piedish & left arm)

	{&KlosSpheres[6],	KBootIntRou,{  0,   0,-10},   25, 256+ 3},	// shoes

	{NULL,				KBootIntRou,{  0,   0,-10},   25, 256+31},
};
/*
NME_SPHERE KlosSpheres[7] =
{
	{&KlosSpheres[1],	KlosIntRou,{  0,   0,   0},   25,  5},	// bod
	{&KlosSpheres[2],	KlosIntRou,{  0,   0, -10},   15, 18},	// head

	{&KlosSpheres[3],	KlosIntRou,{  0,   0,   0},   10,  7},	// left arm
	{&KlosSpheres[4],	KlosIntRou,{  0,   0,   0},   10,  8},	// left hand
	{&KlosSpheres[5],	KlosIntRou,{  0,   0,   0},   18, 12},	// right hand (plus piedish & left arm)

	{&KlosSpheres[6],	KBootIntRou,{  0,   0,-10},   25,  3},	// shoes

	{NULL,				KBootIntRou,{  0,   0,-10},   25, 31},
};
*/
// ------------- Deal with the switches ------------

// The LND file has a couple of ball targets (tag 2, tag 3), which behave themselves automatically
// Those switches move a couple of doors (4,5)
// When either door reaches the top, we need to trigger a bomb or a boxing glove

void CaBossFireBomb(int which)
{
	ENEMYPOS *nme;
/*
	static VECTOR posses[2] =
	{
		{-280<<12,-100<<12,0},
		{ 280<<12,-100<<12,0}
	};
*/
	static VECTOR posses[2] =
	{
		{-280<<12,-150<<12,0},
		{ 280<<12,-150<<12,0}
	};
	static VECTOR vels[2] =
	{
		{4096 * 5,-4096 * 7,0},
		{-4096 * 5,-4096 * 7,0}
	};

//	DB("Carnival Bomb %d\n",which);

	nme = ca_bomb_enemy;
	nme->pos = posses[which];
	nme->vel = vels[which];
	nme->doing = 0;
	nme->ticker = 0;
	nme->flags |= NMEFLAG_ACTIVE + NMEFLAG_ENABLED;
}
void CaBossFireGlove(int which)
{
	ENEMYPOS *nme;
	ENEMYPOS *kloset;
	VECTOR temp;
/*
	static VECTOR posses[2] =
	{
		{-280<<12,-100<<12,0},
		{ 280<<12,-100<<12,0}
	};
*/
	static VECTOR posses[2] =
	{
		{-300<<12,-150<<12,0},
		{ 300<<12,-150<<12,0}
	};
//	int aims[2] = {0x400,0xC00};

//	DB("Carnival Glove %d\n",which);
	nme = ca_glove_enemy;
	nme->pos = posses[which];

//	nme->ya = aims[which];	// aim straight at kloset

	nme->doing = 0;
	nme->ticker = 0;
	nme->flags |= NMEFLAG_ACTIVE + NMEFLAG_ENABLED;


	kloset = loadlndFindEnemy(10);


// tbd - adjust targ for the spinning platform

	temp.vx = (kloset->pos.vx - nme->pos.vx)>>12;
	temp.vy = (kloset->pos.vy - nme->pos.vy)>>12;
	temp.vz = (kloset->pos.vz - nme->pos.vz)>>12;

	nme->ya = (calc_angle( (temp.vx), (temp.vz)) & 4095);
	nme->xa = 0;

	AddToQueue(&nme->anim,1,NO,NO,4096);	// one animation, one destiny, no looping

	sfxPlayNME3D(levelFX, SFX_KLOSET_BOXINGGLOVE,nme);

}


void CaBossFirePiano()
{
	ENEMYPOS *nme;
//	ENEMYPOS *kloset;
//	VECTOR temp;

//	kloset = loadlndFindEnemy(10);

	nme = ca_piano_enemy;

// always dump the piano in the middle, so it doesn't interfere with the exit platform
	nme->pos.vx = 0;
	nme->pos.vz = 0;
//	nme->pos = kloset->pos;
	nme->pos.vy = -500<<12;



	nme->doing = 0;
	nme->ticker = 0;
	nme->flags |= NMEFLAG_ACTIVE + NMEFLAG_ENABLED;

	nme->vel.vx = 0;
	nme->vel.vy = 0;
	nme->vel.vz = 0;

	AddToQueue(&nme->anim,0,NO,NO,4096);
}


void CaBossUpdate()
{
	DYNCOLLBOX *ptr;
	PLATFORM_DEFSTR *pDef;

	int i;

	for(i = 0; i < 2; i++)
	{
		loadlndFindPlatform(i+4, &pDef, &ptr);

	// copy of "just at point" from puzzles
		if(ptr)
		{
			if(CompareVectors(&(ptr->pos), &(pDef->points[1].pos)) && (ptr->move.jap))
			{
				switch(boss_n_powers)
				{
				case 3:		// it's a bomb
					CaBossFireBomb(i);
					break;
				case 2:		// it's a boxing glove
					CaBossFireGlove(i);
					break;
				default:	// the platform shouldn't have managed to be triggered
					ASSERT(0);
				}
			}
		}
	}


// Nose...
	for(i = 6; i <= 7 && ballColl.nHitPlats; i++)
	{

		loadlndFindPlatform(i, &pDef, &ptr);
		if(ptr)
		{
			int j;
			for(j = 0; j < ballColl.nHitPlats; j++)
			{
				if(ptr==ballColl.hitPlats[j].pPlatform)
				{
//					if(!(ca_piano_enemy->flags & NMEFLAG_ACTIVE))
					if(!(ca_piano_enemy->flags & NMEFLAG_DEAD))
					{
						CaBossFirePiano();
					}
				}
			}
		}
	}

	{
		static int plats[4] = {88,16,55,32};
		for(i = 0; i < 4; i++)
		{
//			loadlndFindPlatform(i, &pDef, &ptr);
			//loadlndFindPlatform(plats[i], &pDef, &ptr);
			//if(ptr)
			//{
			//	pDef->head.spin.rotspeed = caboss_spin;
			//}

			puzzleStartSpin(plats[i], caboss_spin);

		}
		if(caboss_spin < 0 && !(activeframe & 15))
			caboss_spin++;

//		DB("spin = %d\n",caboss_spin);
	}

}

// ===============================================================================================================


#define SPIDERBOMB_DIE_TIME   30
#define SPIDERBOMB_INVULN_TIME 20
#define SPIDERBOMB_STUN_TIME 100
#define SPIDERBOMB_FLOOR_Y   (-5 * 4096)


void dirtyFlames(VECTOR *vec, int flames, int shifts, int size)
{
	int chance;
	VECTOR temp;


	chance = (RANDOM256() < 128);
	if(chance)
	{
		temp = *vec;
		temp.vx += (RANDOM256()-128)<<shifts;
		temp.vy += (RANDOM256()-128)<<shifts;
		temp.vz += (RANDOM256()-128)<<shifts;

		if(flames && (RANDOM256() > 128))
		{
			New_Debris(DEBRIS_FASTFLAME,&temp,size);
		}
		else
		{
			New_Debris(DEBRIS_SMOKE,&temp,size*2);
		}
	}
}


// we don't actually hurt the glove, we just want to decrease the energy & lives counters)
// (ummm, we don't have a representation of your three energy barlets at this point... erk!)
void SpBoss_HurtGlove()
{
	if(cheatActivated & INFINITE_ENERGY_CHEAT)
		return;
	if(gameCtrl.dropOutFlag)
		return;


	GloveCtrl.health--;
	GloveCtrl.healthChange=TRUE;
	if(GloveCtrl.health == 0)
	{
		GloveCtrl.health = GloveCtrl.maxHealth;

		if(cheatActivated & INFINITE_LIVES_CHEAT)
			return;

		GloveCtrl.lives--;
		if (GloveCtrl.lives>250) // no (ubyte check for overflow)
		{
			GloveCtrl.lives = 0;	// prevent gpu ov's
			GloveCtrl.health = 0;
//			gameCtrl.dropOutFlag=TRUE;

			gameCtrl.dropOutFlag=GAME_EXITED;
			MenuFadeOut();
			gameCtrl.fadeIn=0;
			gameCtrl.fadeOut=0;
			showGameOver=255;

		}
	}
}


void Update_Missile(ENEMYPOS *nme)
{
	SHORT angs[2];
	VECTOR *targ;

	nme->ticker++;

	if(nme->ticker >= 180)
	{
		nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
		nme->type = 0;
	}

	angs[0] = (nme->xa) & 4095;
	angs[1] = (0x800+nme->ya) & 4095;

	switch(nme->doing)
	{
	case 0:	// incoming...
		if(nme->ticker < 60)
			targ = &nme->pathvectors[0];
		else
			targ = &glovePos;
		pickupHomer(&nme->pos, &angs[0], targ, 0x20, 8);

		if(!(nme->ticker & 1))
		{
//			New_Debris(DEBRIS_FASTFLAME,&nme->pos,512);
			New_Debris(DEBRIS_OPEC_HOVER,&nme->pos,800);
		}
		if(NMECheckInRange(nme, &glovePos, 160<<12, 0))
		{
			nme->doing = 2;
			nme->ticker = 0;
			sfxPlayFull3D(globalFX,SFX_GE_SAMTEX_EXPLODE,&nme->pos);
			SpBoss_HurtGlove();
		}
		else if (nme->pos.vz > glovePos.vz-(10<<12))	// missed
		{
			nme->doing = 1;
		}
		break;

	case 1:	// hit, & going down
		nme->pathvectors[0] = nme->pos;
		nme->pathvectors[0].vy = 100 * 4096;
		targ = &nme->pathvectors[0];
		pickupHomer(&nme->pos, &angs[0], targ, 0x20, 8);
		dirtyFlames(&nme->pos, 1, 6, 1024);

		if(!(RANDOM256() &  7))
		{
			sfxPlay3D(globalFX,SFX_GE_CRUMPET_ATTACK,&nme->pos);
		}

		if(nme->pos.vy >= 0)
		{
			nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
			nme->type = 0;
		}
		break;

	case 2:	// blowing up in your face
		nme->psa = NULL;	// so the missile doesn't get drawn
		dirtyFlames(&nme->pos, 1, 6, 1024);
		if(nme->ticker > 10)
		{
			nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
			nme->type = 0;
		}

		SpBossQuake(1);
		break;
	}


	nme->xa = (angs[0]) & 4095;
	nme->ya = (0x800+angs[1]) & 4095;

	nme->za = (nme->za + 0x40) & 4095;
}


// 0,3,6 = walking forwards
// 1,4,7 = whack back, invulnerable
// 2,5,8 = whack back recovering
//#define SPIDER_ENDSTATE 8
#define SPIDER_ENDSTATE 5

void Update_Spiderbomb(ENEMYPOS *nme)
{
//	SHORT angs[2];
//	int hag;

	switch(nme->doing)
	{
		case 0:	// advancing
		case 3:
		case 6:
			nme->ticker++;
			if(!(nme->ticker & 31))
			{
				sfxPlayFullVol3D(globalFX, SFX_GENERIC_BLEEP_3,&nme->pos,128);
			}

			nme->ya = (nme->ya + 0x400) & 4095;
//			NME_FaceTo(nme, &glovePos, 40);
			NME_FaceTo(nme, &nme->pathvectors[0], 40);
			NME_GoForwards(nme, 0x5000);
			nme->ya = (nme->ya - 0x400) & 4095;


			nme->pos.vy = glovePos.vy;
//			if(NMECheckInRange(nme, &glovePos, 100<<12, 0))
			if(NMECheckInRange(nme, &glovePos, 150<<12, 0))
			{
//				sfxPlayFull3D(levelFX,SFX_ROBOT_ATTACK,&nme->pos);
				sfxPlayFull3D(globalFX,SFX_GE_SAMTEX_EXPLODE,&nme->pos);

				nme->flags &=~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
				nme->type = 0;
				SpBoss_HurtGlove();
				SpBossQuake(1);	// fairly hefty quake to wake you up to the fact that something's happened...

			}
			nme->pos.vy = SPIDERBOMB_FLOOR_Y;
			break;

		case 1:	// knocked back by ball
		case 2:
		case 4:
		case 5:
		case 7:
		case 8:

			nme->pos.vx += nme->vel.vx;
			nme->pos.vy += nme->vel.vy;
			nme->pos.vz += nme->vel.vz;
			nme->vel.vy += 2048;

			if(nme->pos.vy > SPIDERBOMB_FLOOR_Y)
			{
				if(nme->vel.vy > 4096 * 2)
				{
					sfxPlayFullVol3D(globalFX,SFX_IMPACT_METAL_MEDIUM,&nme->pos,128);
				}

				nme->pos.vy = SPIDERBOMB_FLOOR_Y;
				nme->vel.vy = -nme->vel.vy/4;
			}
			nme->vel.vx = nme->vel.vx * 3800/4096;
			nme->vel.vz = nme->vel.vz * 3800/4096;
			nme->ticker--;

			if(nme->ticker == SPIDERBOMB_STUN_TIME - SPIDERBOMB_INVULN_TIME)
				nme->doing++;

			if(!nme->ticker)
			{
				if(nme->doing == SPIDER_ENDSTATE)
				{
					nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
					nme->type = 0;
				}
				else
				{

					nme->doing++;
				}
			}
			break;

		case -1:	// dropping out of the big robot
			nme->pos.vx += nme->vel.vx;
			nme->pos.vy += nme->vel.vy;
			nme->pos.vz += nme->vel.vz;
			nme->vel.vy += 2048;
			if(nme->pos.vy > SPIDERBOMB_FLOOR_Y)
			{
				nme->pos.vy = SPIDERBOMB_FLOOR_Y;
				nme->doing = 0;
				nme->ticker = RANDOM256();

			}
			break;
	}
	if(nme->doing > 0)
	{
		int chance;


#if SPIDER_ENDSTATE==5
		chance = (RANDOM256() < 16 + 8 * 64 / 5);	// if the endstate's 8
#else
		chance = (RANDOM256() < 16 + nme->doing * 64 / 5);
#endif
		if(chance)
		{
			VECTOR temp;
			temp = nme->pos;
			temp.vy -= 10 * 4096;
			temp.vx += (RANDOM256()-128)<<5;
			temp.vy += (RANDOM256()-128)<<5;
			temp.vz += (RANDOM256()-128)<<5;

			if(nme->doing == SPIDER_ENDSTATE && RANDOM256() > 128)
			{
				New_Debris(DEBRIS_FASTFLAME,&temp,512);
			}
			else
			{
				New_Debris(DEBRIS_SMOKE,&temp,512);
			}
		}

	}
}

void SBombIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)	// actually, in this level, they'll only ever be hit by special-case balls
		return;

	if(nme->doing == SPIDER_ENDSTATE)	// dead, already
		return;

	switch(nme->doing)
	{
	case -1:	// being dropped

	case 1:	// invulnerable, having just been hit
	case 4:
	case 7:
//	case 8:	// dead already
		return;



	case 2:	// stunned, whack back further, and knock up another level (ie, add 2, then add 1
	case 5:	// (2->5->8)
		nme->doing += 2;

	case 0:	// walking forwards -> invuln (0->1, 3->4, 6->7)
	case 3:
	case 6:
// tbd - bounce the ball that hit it?
/*
		nme->vel.vx = ballVel.vx*2;
		nme->vel.vy = ballVel.vy / 16;
		nme->vel.vz = ballVel.vz*2;
*/
		nme->vel.vx = ballVel.vx;
		nme->vel.vy = ballVel.vy / 8;
		nme->vel.vz = ballVel.vz;


		nme->doing++;
		if(nme->doing == SPIDER_ENDSTATE)
			nme->ticker = SPIDERBOMB_DIE_TIME;
		else
			nme->ticker = SPIDERBOMB_STUN_TIME;
		break;


	}
}

void MissIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)	// actually, in this level, they'll only ever be hit by special-case balls
		return;


	if(nme->doing != 0)
		return;
	if(with > 1)
	{
		CannonCtrl.alive[with-2] = 0;
	}

//tbd - explosion
	nme->doing = 1;
//	nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
//	nme->type = 0;
}

NME_SPHERE MissSpheres[]=
{
	{&MissSpheres[1],MissIntRou,   {0,0,-20},40 * EVIL_SCALE/2048,0},
	{NULL,           MissIntRou,   {0,0,20},40 * EVIL_SCALE/2048,256+0}
};



void Update_Laser(ENEMYPOS *nme)
{
	SHORT angs[2];
	VECTOR *targ;
	nme->ticker++;

	angs[0] = (nme->xa) & 4095;
	angs[1] = (0x800+nme->ya) & 4095;

	switch(nme->doing)
	{
		case 0:
			if(nme->ticker < 60)
				targ = &nme->pathvectors[0];
			else
				targ = &glovePos;


			if(nme->ticker >= 150)
			{
				nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
				nme->type = 0;
			}
			pickupHomer(&nme->pos, &angs[0], targ, 0x20, 12);


			if(NMECheckInRange(nme, &glovePos, 50<<12, 0))
			{
				if(SpBossCtrl.shield > 64)	// bounce the thing back...
				{
//					int temp, shortest;
//					temp = angs[0];


					angs[0] = findShortestAngle(angs[0],-SpBossCtrl.xa_cam);
//					shortest = angs[0];
					angs[0] = angs[0] * 2;
					angs[0] = -SpBossCtrl.xa_cam - angs[0]; // + 0x800;



					angs[1] = findShortestAngle(angs[1], SpBossCtrl.ya_cam);
//					shortest = angs[1];
					angs[1] = angs[1] * 2;
					angs[1] = SpBossCtrl.ya_cam - angs[1] + 0x800;

//					printf("in = %d, turret = %d, shortest = %d, angle out %d\n",
//						temp, SpBossCtrl.ya_cam, shortest, (angs[1]-0x800) & 4095);

					sfxPlayFull3D(levelFX,SFX_CYMON_GENERAL,&nme->pos);

					nme->ticker = 0;
					nme->doing = 1;
				}
				else
				{
					nme->doing = 2;
					nme->ticker = 0;
					SpBoss_HurtGlove();
					sfxPlayFull3D(globalFX,SFX_GE_SAMTEX_EXPLODE,&nme->pos);
				}
			}
			else if (nme->pos.vz > glovePos.vz-(10<<12))	// missed
			{
	//			nme->doing = 1;
				nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
				nme->type = 0;

			}
			break;

		case 1:	// bounced (tbd - the blastring effect)
			nme->ticker++;
			targ = &nme->pathvectors[0];
			pickupHomer(&nme->pos, &angs[0], targ, 0,12);	// don't allow it to turn
			if(nme->ticker >= 200)
			{
				nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
				nme->type = 0;
			}
			break;

		case 2:	// electrifying you (tbd - special effect)
			SpBossQuake(3);	// weeny little quakelet compared to the missile one
			if(nme->ticker==1)
				effectsStartOverlay(10, 0x00ffff);


			nme->psa = NULL;
			nme->ticker++;
			if(nme->ticker > 20)
			{
				nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
				nme->type = 0;
			}
			break;

	}

	nme->xa = (angs[0]) & 4095;
	nme->ya = (0x800+angs[1]) & 4095;
}



#define EVIL_IDLING				1
#define EVIL_FIRING_BOMB		2
#define EVIL_FIRING_LASER		3
#define EVIL_FIRING_MISSILE1	4
#define EVIL_FIRING_MISSILE2	5
#define EVIL_FIRING_MISSILE3	6
#define EVIL_AFTER_BOMB			7

#define EVIL_DYING				10

#define EVIL_JUDDERING			20
#define EVIL_READYTOGO			21


#define EVIL_AFTER_BOMB_TIME 350
#define EVIL_DESCENT_TIME 100

#define EVIL_IDLE_TIME 30
#define EVIL_BOMB_TIME 80
#define EVIL_LASER_TIME 80
#define EVIL_MISSILE_TIME 80
#define EVIL_INVULN_TIME 10

#define EVIL_IDLE_HEIGHT (-250 << 12)
#define EVIL_SPIDER_HEIGHT (-800 << 12)

//#define EVIL_YOU_HEIGHT (-300 << 12)
//#define EVIL_YOU_HEIGHT (ER_FINAL_Y)

void BotFiresMissile(ENEMYPOS *enemy, int gunseg)
{
	static VECTOR posref  = {0,0,-320 * EVIL_SCALE / 2048};
	static VECTOR posref2 = {0,0,-400 * EVIL_SCALE / 2048};
	VECTOR pos;
	VECTOR vec;


	EnemyRotatePoint(enemy,&posref, &pos, gunseg);
	pos.vx = (pos.vx<<12) + enemy->pos.vx;
	pos.vy = (pos.vy<<12) + enemy->pos.vy;
	pos.vz = (pos.vz<<12) + enemy->pos.vz;

	EnemyRotatePoint(enemy,&posref2, &vec, 7);
	vec.vx = vec.vx * 800;
	vec.vy = vec.vy * 400;	//800;
	vec.vz = vec.vz * 800;

	enemy_ShootMissile(&pos,&vec,enemy->ya);
}
void BotFiresLaser(ENEMYPOS *enemy, int gunseg)
{
	static VECTOR posref  = {0,0,-200 * EVIL_SCALE / 2048};
	static VECTOR posref2 = {0,0,-400 * EVIL_SCALE / 2048};
	VECTOR pos;
	VECTOR vec;


	EnemyRotatePoint(enemy,&posref, &pos, gunseg);
	pos.vx = (pos.vx<<12) + enemy->pos.vx;
	pos.vy = (pos.vy<<12) + enemy->pos.vy;
	pos.vz = (pos.vz<<12) + enemy->pos.vz;

	EnemyRotatePoint(enemy,&posref2, &vec, 7);
	vec.vx = vec.vx * 800;
	vec.vy = vec.vy * 400;	//800;
	vec.vz = vec.vz * 800;

	enemy_ShootLaser(&pos,&vec,enemy->ya);
}



void Update_Fragment(ENEMYPOS *nme)
{
	ADDVECTOR(&nme->pos,&nme->pos,&nme->vel);
	nme->vel.vy += 2048;

// sod doing this with quaternions
	nme->xa = (nme->xa + nme->pathvectors[0].vx)&4095;
	nme->ya = (nme->ya + nme->pathvectors[0].vy)&4095;
	nme->za = (nme->za + nme->pathvectors[0].vz)&4095;


	if(!(RANDOM256() &  7))
	{
		sfxPlayFull3D(globalFX,SFX_GE_CRUMPET_ATTACK,&nme->pos);
	}

// oh, and spin the thing round
	if(nme->pos.vy >= 0)
	{
		nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
		nme->type = 0;

		sfxPlayFull3D(globalFX,SFX_GE_SAMTEX_EXPLODE,&nme->pos);
	}


	dirtyFlames(&nme->pos,1,7,3200 * EVIL_SCALE / 2048);

}



char evil_r_health[3];
int evil_invuln_time = 0;
NEWTMD *bot_original_meshes[4];


void Update_EvilBot(ENEMYPOS *enemy)
{
	static int evil_robot_next = 0;
	VECTOR *targ;
//	VECTOR temp;
	int ty;
//	static VECTOR posref;
	VECTOR pos;



// make him move from side to side
// (he's got a 3 position movement path, with his starting point being behind the first(middle) one)

	if(enemy->doing != -1
		&& enemy->doing != EVIL_JUDDERING
		&& enemy->doing != EVIL_READYTOGO
		&& enemy->doing != EVIL_DYING
		)
	{

		targ = &enemy->pathvectors[(int)enemy->n_points];

		if(NME_GoTo2D(enemy,targ,0x5000))	//0x2400))
		{


#ifdef DEAD_SPBOSS
			if(!enemy->n_points);
			{
				boss_n_powers = 0;
				enemy->doing = EVIL_DYING;
				enemy->pathvectors[3].vx = ((signed char)RANDOM256()) /4;
				enemy->pathvectors[3].vy = ((signed char)RANDOM256()) /4;
				enemy->pathvectors[3].vz = ((signed char)RANDOM256()) /4;
				enemy->ticker = 0;
			}
#endif

			enemy->n_points++;
			if(enemy->n_points == 3)
				enemy->n_points = 1;	// start->0-> 1->2->1->2->1->2
		}


// bobbing up & down...
		ty = targ->vy + rsin((sinewave1>>19) & 4095) * 20;
		if(enemy->doing == EVIL_AFTER_BOMB)
			ty = EVIL_SPIDER_HEIGHT;
		else
			ty = EVIL_IDLE_HEIGHT;

		enemy->vel.vy +=  (ty - enemy->pos.vy) >> 2;

		if(enemy->vel.vy > 4096 * 4)
			enemy->vel.vy = 4096 * 4;
		if(enemy->vel.vy < -4096 * 4)
			enemy->vel.vy = -4096 * 4;

		enemy->pos.vy += enemy->vel.vy;



//		temp.vx = glovePos.vx - enemy->pos.vx;
//		temp.vy = glovePos.vy - enemy->pos.vy;
//		temp.vz = glovePos.vz - enemy->pos.vz;
		pos.vx = glovePos.vx - enemy->pos.vx;
		pos.vy = glovePos.vy - enemy->pos.vy;
		pos.vz = glovePos.vz - enemy->pos.vz;

//		ty = (calc_angle( (-temp.vx)>>8, (-temp.vz)>>8) & 4095);
		ty = (calc_angle( (-pos.vx)>>8, (-pos.vz)>>8) & 4095);
		ty -= enemy->ya;
		ty &= 4095;
		if(ty > 2048) ty -= 4096;
		if(ty < -2048) ty += 4096;

		enemy->vel.vx += ty/32;
		enemy->vel.vx = enemy->vel.vx * 3900/4096;
		enemy->ya +=enemy->vel.vx;

	}

	switch(enemy->doing)
	{
		case EVIL_AFTER_BOMB:
			enemy->ticker--;
			if(!enemy->ticker)
			{
				enemy->doing = EVIL_IDLING;
				enemy->ticker = EVIL_DESCENT_TIME;
			}
			break;


		case EVIL_IDLING:

			if(enemy->n_points == 0)
				break;

			enemy->ticker--;
			if(!enemy->ticker)
			{

//				printf("health = %d %d %d\n",evil_r_health[0],evil_r_health[1],evil_r_health[2]);

				if(	(evil_r_health[0] == 0 && evil_r_health[1] == 0)
					|| (RANDOM256() < 100)
					)
				{
// If the arms have gone, we have a choice between spiderbombs and electrobolt...
					if(evil_r_health[0] != 0 || evil_r_health[1] != 0 || RANDOM256() < 128)
					{
						enemy->doing = EVIL_FIRING_LASER;
						AddToQueue(&enemy->anim,NMEANIM_SPECIAL2,NO,NO,4096);	//sneeze
						enemy->ticker = EVIL_LASER_TIME;
					}
					else
					{
						enemy->doing = EVIL_FIRING_BOMB;
						AddToQueue(&enemy->anim,NMEANIM_SPECIAL2,NO,NO,4096);	// spiderbombs
						enemy->ticker = EVIL_BOMB_TIME;
					}
				}
				else
				{
					if(RANDOM256() < 100 && evil_r_health[0] != 0 && evil_r_health[1] != 0)
					{
//						printf("fire 1\n");
						enemy->doing = EVIL_FIRING_MISSILE1;
						AddToQueue(&enemy->anim,NMEANIM_SPECIAL1,NO,NO,4096);	// both missiles
						enemy->ticker = EVIL_MISSILE_TIME;
					}
					else
					{

						if(evil_r_health[0] == 0 || (evil_r_health[1] != 0 && RANDOM256() < 128))
						{
//						printf("fire 2\n");

							enemy->doing = EVIL_FIRING_MISSILE2;
							AddToQueue(&enemy->anim,NMEANIM_ACTION1,NO,NO,4096);	// his left, yr right
							enemy->ticker = EVIL_MISSILE_TIME;
						}
						else
						{
//						printf("fire 3\n");

							enemy->doing = EVIL_FIRING_MISSILE3;
							AddToQueue(&enemy->anim,NMEANIM_ACTION2,NO,NO,4096);	// his right, yr left
							enemy->ticker = EVIL_MISSILE_TIME;
						}
					}
				}

				if(RANDOM256() < 128)
				{
					AddToQueue(&enemy->anim,NMEANIM_IDLE,YES,YES,4096);
				}
				else
				{
					AddToQueue(&enemy->anim,NMEANIM_IDLE2,YES,YES,4096);
				}

			}
			break;


		case EVIL_FIRING_BOMB:	// drops boms 21 into the anim
			enemy->ticker--;

			if(enemy->ticker == EVIL_BOMB_TIME - 25)
			{
				//VECTOR temp;
//				temp = enemy->pos;
//				temp.vy += 30*4096;
				pos = enemy->pos;
				pos.vy += 30*4096;
//				sfxPlayFull3D(levelFX,SFX_ROBOT_GENERAL_2 ,&enemy->pos);
				sfxPlayFull3D(levelFX,SFX_SUCKER_CRYSTAL ,&enemy->pos);
//				enemy_DropSpiders(&temp,&glovePos);
				enemy_DropSpiders(&pos,&glovePos);
			}

			if(!enemy->ticker)
			{
				enemy->doing = EVIL_AFTER_BOMB;
				enemy->ticker = EVIL_AFTER_BOMB_TIME;
			}
			break;

		case EVIL_FIRING_LASER:	// fires laser 15 into the anim
			enemy->ticker--;
			if(enemy->ticker == EVIL_LASER_TIME - 25)
			{

				BotFiresLaser(enemy, 7);
			}
			if(!enemy->ticker)
			{
				enemy->doing = EVIL_IDLING;
				enemy->ticker = EVIL_IDLE_TIME;
			}
			break;
		case EVIL_FIRING_MISSILE1:
			enemy->ticker--;
			if(enemy->ticker == EVIL_MISSILE_TIME - 25)
			{
//				sfxPlayFull3D(levelFX,SFX_ROBOT_GENERAL_2 ,&enemy->pos);
				sfxPlayFull3D(levelFX,SFX_OPEC_ATTACK_SINGLE ,&enemy->pos);
				BotFiresMissile(enemy, 9);
				BotFiresMissile(enemy, 3);
			}
			if(!enemy->ticker)
			{
				enemy->doing = EVIL_IDLING;
				enemy->ticker = EVIL_IDLE_TIME;
			}
			break;
		case EVIL_FIRING_MISSILE2:
			enemy->ticker--;
			if(enemy->ticker ==  EVIL_MISSILE_TIME - 16)
			{
//				sfxPlayFull3D(levelFX,SFX_ROBOT_GENERAL_2 ,&enemy->pos);
				sfxPlayFull3D(levelFX,SFX_OPEC_ATTACK_SINGLE ,&enemy->pos);
				BotFiresMissile(enemy, 9);
			}

			if(!enemy->ticker)
			{
				enemy->doing = EVIL_IDLING;
				enemy->ticker = EVIL_IDLE_TIME;
			}
			break;
		case EVIL_FIRING_MISSILE3:
			enemy->ticker--;
			if(enemy->ticker == EVIL_MISSILE_TIME - 32)
			{
//				sfxPlayFull3D(levelFX,SFX_ROBOT_GENERAL_2 ,&enemy->pos);
				sfxPlayFull3D(levelFX,SFX_OPEC_ATTACK_SINGLE ,&enemy->pos);
				BotFiresMissile(enemy, 3);
			}
			if(!enemy->ticker)
			{
				enemy->doing = EVIL_IDLING;
				enemy->ticker = EVIL_IDLE_TIME;
			}
			break;

// these two are set up by the cameo player
		case EVIL_JUDDERING:
			if(enemy->ticker < 8)
			{
				int frame;
				AddToQueue(&enemy->anim,NMEANIM_IDLE,0,0,0);	// force the anim to be non-animating

				frame = (RANDOM256() MOD 6)+ 17;
				enemy->anim.animInfo->frame = frame;
				enemy->anim.animInfo->frameFixed = frame<<12;
			}
			enemy->ticker++;
			break;

		case EVIL_READYTOGO:
			if(enemy->ticker == 0)
			{
				AddToQueue(&enemy->anim,NMEANIM_IDLE,1,0,4096);
			}
			enemy->ticker++;
			if(enemy->ticker > 5 && !cameo_running)
			{
				enemy->doing = EVIL_IDLING;
				enemy->ticker = EVIL_IDLE_TIME;
				evil_robot_next = -1;

#ifdef WEAK_SPBOSS
				evil_r_health[0] = 1;
				evil_r_health[1] = 1;
				evil_r_health[2] = 1;
#else
				evil_r_health[0] = 3;
				evil_r_health[1] = 3;
				evil_r_health[2] = 3;
#endif

				boss_n_powers = old_boss_n_powers = 3;
				evil_invuln_time = 0;

				enemy->n_points = 0;
			}
			break;

		case EVIL_DYING:
			ADDVECTOR(&enemy->pos,&enemy->pos,&enemy->vel);
			enemy->vel.vy += 2048;

		// sod doing this with quaternions
			enemy->xa = (enemy->xa + enemy->pathvectors[3].vx)&4095;
			enemy->ya = (enemy->ya + enemy->pathvectors[3].vy)&4095;
			enemy->za = (enemy->za + enemy->pathvectors[3].vz)&4095;

		// oh, and spin the thing round
			if(enemy->pos.vy >= -13<<12)
			{
				ENEMYPOS *evilglove;
				enemy->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
				enemy->type = 0;

				sfxPlayFull3D(globalFX,SFX_GE_SAMTEX_EXPLODE,&enemy->pos);

				evilglove = loadlndFindEnemy(13);	// bung crossstitch inside the robot ready for the cameo
				if(evilglove)
					evilglove->pos = enemy->pos;
				evilglove = loadlndFindEnemy(300);	// same for the cameo explosion
				if(evilglove)
				{
					evilglove->pos = enemy->pos;
					evilglove->pos.vy -= 4096 * 50;
				}

				cameoStart(1);


			}

			if(!(RANDOM256() &  7))
			{
	//			sfxPlayFull3D(levelFX,SFX_CRUMPET_ATTACK,&enemy->pos);
				sfxPlayFull3D(globalFX,SFX_GE_CRUMPET_ATTACK,&enemy->pos);
			}


// LOTS of dirty flames...
			dirtyFlames(&enemy->pos,1,7,3200 * EVIL_SCALE / 2048);
			dirtyFlames(&enemy->pos,1,9,3200 * EVIL_SCALE / 2048);

			break;


		default:
			break;
	}


	if(evil_invuln_time)
		evil_invuln_time--;

	if(enemy->doing != -1 && enemy->doing != EVIL_JUDDERING && (activeframe & 1))
	{
		//VECTOR temp;
//		temp = enemy->pos;
//		temp.vy += (120 * EVIL_SCALE / 2048) << 12;
		pos = enemy->pos;
		pos.vy += (120 * EVIL_SCALE / 2048) << 12;
//		New_RobotHoverDebris(&temp);
		New_RobotHoverDebris(&pos);
	}


// Smoke coming off the robot itself, as you injure the thing...
	if(evil_r_health[0] < 3 && evil_r_health[0] != 0)
	{
		static VECTOR posref = {-30 * EVIL_SCALE / 2048,-60 * EVIL_SCALE / 2048,0};
		//VECTOR pos;
		//SETVECTOR(&posref,-30 * EVIL_SCALE / 2048,-60 * EVIL_SCALE / 2048,0);
		EnemyRotatePoint(enemy,&posref, &pos, 2);
		pos.vx = (pos.vx<<12) + enemy->pos.vx;
		pos.vy = (pos.vy<<12) + enemy->pos.vy;
		pos.vz = (pos.vz<<12) + enemy->pos.vz;

		dirtyFlames(&pos,evil_r_health[0] == 1,7,EVIL_SCALE);
	}
	if(evil_r_health[1] < 3 && evil_r_health[1] != 0)
	{
		static VECTOR posref = {30 * EVIL_SCALE / 2048,-60 * EVIL_SCALE / 2048,0};
		//VECTOR pos;
		//SETVECTOR(&posref,30 * EVIL_SCALE / 2048,-60 * EVIL_SCALE / 2048,0);
		EnemyRotatePoint(enemy,&posref, &pos, 8);
		pos.vx = (pos.vx<<12) + enemy->pos.vx;
		pos.vy = (pos.vy<<12) + enemy->pos.vy;
		pos.vz = (pos.vz<<12) + enemy->pos.vz;

		dirtyFlames(&pos,evil_r_health[1] == 1,7,EVIL_SCALE);
	}

	if(evil_r_health[2] < 3 && evil_r_health[2] != 0)
	{
		static VECTOR posref = {0,-35 * EVIL_SCALE / 2048,0};
		//VECTOR pos;
		//SETVECTOR(&posref,0,-35 * EVIL_SCALE / 2048,0);
		EnemyRotatePoint(enemy,&posref, &pos, 4);
		pos.vx = (pos.vx<<12) + enemy->pos.vx;
		pos.vy = (pos.vy<<12) + enemy->pos.vy;
		pos.vz = (pos.vz<<12) + enemy->pos.vz;

		dirtyFlames(&pos,evil_r_health[1] == 1,7,EVIL_SCALE);
	}
}
/*******************************************************************************************/

void EvilR_StartFragment(ENEMYPOS *src_nme, int seg, NEWMODEL *psa, int ya)
{
	static VECTOR ref = {0,0,0};
	VECTOR pos;
	ENEMYPOS *nme;

	EnemyRotatePoint(src_nme,&ref, &pos, seg);

	nme = enemy_FindFreeEnemy();
	if(!nme)
		return;

	nme->pos.vx = src_nme->pos.vx + (pos.vx<<12);
	nme->pos.vy = src_nme->pos.vy + (pos.vy<<12);
	nme->pos.vz = src_nme->pos.vz + (pos.vz<<12);

	nme->pathvectors[0].vx = ((signed char)RANDOM256()) /4;
	nme->pathvectors[0].vy = ((signed char)RANDOM256()) /4;
	nme->pathvectors[0].vz = ((signed char)RANDOM256()) /4;

	nme->vel.vx = (RANDOM256()-128)<<6;
	nme->vel.vz = (RANDOM256()-128)<<6;
	nme->vel.vy = -4096 * 2 - ((RANDOM256())<<6);

	nme->xa =0;
	nme->ya =ya;
	nme->za =0;

	nme->flags = NMEFLAG_ACTIVE + NMEFLAG_ENABLED;

	nme->type = ROBOT_FRAGMENT;
	nme->psa = psa;

	nme->ticker = 0;
	nme->doing = 0;
}

void EvilRIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if((with == 0) || !evil_r_health[2] || nme->doing ==EVIL_AFTER_BOMB)
		return;

	NMEPushesBall(nme, dvec, sphererad);

	if( evil_r_health[0] || evil_r_health[1])
		return;
	if(evil_invuln_time)
		return;

	New_Debris(DEBRIS_FASTFLAME,&nme->pos,1024);

	evil_invuln_time = EVIL_INVULN_TIME;
	evil_r_health[2]--;
	if(!evil_r_health[2])
	{
		boss_n_powers = 0;
		nme->doing = EVIL_DYING;
		nme->pathvectors[3].vx = ((signed char)RANDOM256()) /4;
		nme->pathvectors[3].vy = ((signed char)RANDOM256()) /4;
		nme->pathvectors[3].vz = ((signed char)RANDOM256()) /4;

		nme->ticker = 0;

// Kill off all the bullets!

		for(nme = nme_list; nme; nme = nme->next)
		{
			switch(nme->type)
			{
			case SPIDERBOMB:
				nme->doing = SPIDER_ENDSTATE;
				nme->ticker = SPIDERBOMB_DIE_TIME;
				break;

			case ROBOT_MISSILE:
				nme->doing = 1;
				break;

			case ROBOT_BOLT:
				nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
				nme->type = 0;
				break;
			}
		}
	}
}

// okeydokey, we need to get the world positions & angles, copy 'em to a new enemy, and set it going...
// (erkle!)

void EvilLarmIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)
		return;
	if(!evil_r_health[0])
		return;

	NMEPushesBall(nme, dvec, sphererad);

	if(evil_invuln_time)
		return;

	New_Debris(DEBRIS_FASTFLAME,&nme->pos,1024);


	nme->vel.vx = 150;

	evil_invuln_time = EVIL_INVULN_TIME;
	evil_r_health[0]--;
	if(!evil_r_health[0])
	{
// disconnect the arm, amid much exploding

		EvilR_StartFragment(nme, 2, robot_lsho_model->psa,0x800);
		EvilR_StartFragment(nme, 3, robot_lgun_model->psa,0);


		nme->psa->world.child->child->child->meshdata = bot_original_meshes[2];
		nme->psa->world.child->child->meshdata = bot_original_meshes[3];
		boss_n_powers--;

	}

}
void EvilRarmIntRou(int with, ENEMYPOS *nme, VECTOR *dvec, int sphererad)
{
	if(with == 0)
		return;
	if(!evil_r_health[1])
		return;

	NMEPushesBall(nme, dvec, sphererad);

	New_Debris(DEBRIS_FASTFLAME,&nme->pos,1024);

	if(evil_invuln_time)
		return;

	nme->vel.vx = -150;


	evil_invuln_time = EVIL_INVULN_TIME;
	evil_r_health[1]--;
	if(!evil_r_health[1])
	{
		EvilR_StartFragment(nme, 8, robot_rsho_model->psa,0x800);
		EvilR_StartFragment(nme, 9, robot_rgun_model->psa,0);

		nme->psa->world.child->child->next->next->meshdata = bot_original_meshes[0];
		nme->psa->world.child->child->next->next->child->meshdata = bot_original_meshes[1];
		boss_n_powers--;
	}
}


NME_SPHERE EvilRSpheres[]=
{
	{&EvilRSpheres[1],EvilLarmIntRou,{0, -40*EVIL_SCALE/2048,-80*EVIL_SCALE/2048},160*EVIL_SCALE/2048,3},
	{&EvilRSpheres[2],EvilRarmIntRou,{0, -40*EVIL_SCALE/2048,-80*EVIL_SCALE/2048},160*EVIL_SCALE/2048,256+9},
	{&EvilRSpheres[3],EvilRIntRou,   {0,-160*EVIL_SCALE/2048,  0},120*EVIL_SCALE/2048,256+0},
	{NULL,            EvilRIntRou,   {0,   0,  0},220,256+0}
};






// =================================================================================================================


#define LECCYRAD1 180
#define LECCYRAD2 320
//#define LECCY_BASE_HEIGHT -320
//#define LECCY_DELTA_HEIGHT 60

/*
#define LECCY_BASE_HEIGHT (-320+4*60)
#define LECCY_DELTA_HEIGHT -60
*/
#define LECCY_BASE_HEIGHT (-320)
#define LECCY_DELTA_HEIGHT 60

#define LECCY_BOTTOM_HEIGHT 0

#define LECCY_CROSS_HEIGHT -80

//#define LECCYH    -120

// amount is (0) ... (4096 * 256)
void LeccyPercentage(SVECTOR *src, SVECTOR *dest, int amount)
{
	amount = amount>>12;
	dest->vx = src[0].vx + ((amount * (src[1].vx - src[0].vx)) >> 8);
	dest->vy = src[0].vy + ((amount * (src[1].vy - src[0].vy)) >> 8);
	dest->vz = src[0].vz + ((amount * (src[1].vz - src[0].vz)) >> 8);
}

#define LECCY_COUNT 4

// tbd - we need to grab the height from the platform info...

SVECTOR lightnin_vect[5] = {{0,-600,-200},{0,-600,200},{-200,-600,0},{200,-600,0}};

void FearDrawLeccifiedPlatform()
{
	static int amount[LECCY_COUNT*3] =
	{4096 * 128,4096 * 128,4096 * 128,
	 4096 * 128,4096 * 128,4096 * 128,
	 4096 * 128,4096 * 128,4096 * 128,
	 4096 * 128,4096 * 128,4096 * 128
	};
	static int vels[LECCY_COUNT*3];
	static SVECTOR svecs[2];
	static SVECTOR dvecs[LECCY_COUNT*2];
	int ya1,ya2;
	int i;
	ENEMYPOS *frank;

  	gte_SetRotMatrix(&GsWSMATRIX);
  	gte_SetTransMatrix(&GsWSMATRIX);

	frank = loadlndFindEnemy(12);

	{
		if(frank->doing == FRANK_WAKEUPCALL && frank->ticker > FRANK_WAKEUP_TIME - 50)
		{
			//COPYVECTOR(&v[4],&frank->pos);
			lightnin_vect[4].vx = frank->pos.vx>>12;
			lightnin_vect[4].vy = frank->pos.vy>>12;
			lightnin_vect[4].vz = frank->pos.vz>>12;
			effectsDrawLightningStrand(&lightnin_vect[0],&lightnin_vect[4],0x808080);
			effectsDrawLightningStrand(&lightnin_vect[1],&lightnin_vect[4],0x808080);
			effectsDrawLightningStrand(&lightnin_vect[2],&lightnin_vect[4],0x808080);
			effectsDrawLightningStrand(&lightnin_vect[3],&lightnin_vect[4],0x808080);

		}
	}


	if(FearCrossLeccyTimer)
	{

//		SVECTOR *v1, *v2;
		static VECTOR a,b;
		int e,f;
//		VECTOR pos;

		ya1 = (FearCrossLeccyPlatno << 9);	// + 0x100;
		svecs[0].vx = (LECCYRAD1 * rsin(ya1))>>12;
		svecs[0].vz = (LECCYRAD1 * rcos(ya1))>>12;
		svecs[0].vy = LECCY_CROSS_HEIGHT;

		svecs[1].vx = -svecs[0].vx;
		svecs[1].vz = -svecs[0].vz;
		svecs[1].vy = svecs[0].vy;

//		effectsDrawLightningStrand(&svecs[0],&svecs[1],0x8080F0);
//		effectsDrawLightningStrand(&svecs[1],&svecs[0],0x8080F0);
		effectsDrawLightningStrand(&svecs[0],&svecs[1],0x808080);
		effectsDrawLightningStrand(&svecs[1],&svecs[0],0x808080);

// Did we hit Frank?

		a.vx=svecs[1].vx-svecs[0].vx;
		a.vy=svecs[1].vy-svecs[0].vy;
		a.vz=svecs[1].vz-svecs[0].vz;

		b.vx=((frank->pos.vx) >> 12)-svecs[0].vx;
		b.vy=((frank->pos.vy) >> 12)-svecs[0].vy;
		b.vz=((frank->pos.vz) >> 12)-svecs[0].vz;

		MakeUnit(&a);
		
		e=DotProduct(&a, &b);
		f=Magnitude(&b);
//		d=fast_sqrt((f*f)-(e*e))>>4;
		i=(f*f)-(e*e);
		//DB("v1=%d,%d,%d v2=%d,%d,%d\n", v1.vx, v1.vy, v1.vz, v2.vx, v2.vy, v2.vz);
		//DB("You are within %d thingies of lightning\n", d);

//		DB("e=%d, f=%d, i = %d\n",e,f,i);
		if(i<=2000)
		{
// Frank's in range of the leccification...

//			DB("OW!\n");
			StunFrankie(frank);

		}
	}

	if(!FearLeccifiedTimer)
		return;

	if(gameCtrl.gameActive)
	{
		for(i = 0; i < LECCY_COUNT * 3; i++)
		{
			vels[i] += (RANDOM256() - 128)<<4;
			if(vels[i] > 4096 * 32)
				vels[i] = 4096 * 32;
			if(vels[i] < -4096 * 32)
				vels[i] = -4096 * 32;

			amount[i] += vels[i];
			if(amount[i] > 4096 * 256)
			{
				amount[i] = 4096 * 256;
				vels[i] = -vels[i];
			}
			if(amount[i] < 0)
			{
				amount[i] = 0;
				vels[i] = -vels[i];
			}
		}
	}

// okay... the 1st number needs to oscillate at a decent speed in the 0...256 range
//	printf("amount0 = %d, vel0 = %d\n",amount[0]>>12,vels[0]>>2);

	ya2 = (FearLeccifiedPlatno << 9) + 0x100;
	ya1 = (ya2 - 0x200) & 4095;
	svecs[0].vx = (LECCYRAD1 * rsin(ya1))>>12;
	svecs[0].vz = (LECCYRAD1 * rcos(ya1))>>12;
	svecs[1].vx = (LECCYRAD1 * rsin(ya2))>>12;
	svecs[1].vz = (LECCYRAD1 * rcos(ya2))>>12;
	svecs[0].vy = svecs[1].vy = FearLeccifiedPlat->move.point * LECCY_DELTA_HEIGHT + LECCY_BASE_HEIGHT;

//	printf("svecs2 = %d %d %d,   %d %d %d\n",
//		(int)svecs[0].vx,(int)svecs[0].vy,(int)svecs[0].vz,
//		(int)svecs[1].vx,(int)svecs[1].vy,(int)svecs[1].vz
//		);

// calculate mid points (2,3)
	for(i = 0; i < LECCY_COUNT; i++)
	{
		LeccyPercentage(&svecs[0],&dvecs[i+LECCY_COUNT],amount[i+LECCY_COUNT]);
	}

//	printf("dvecs2 = %d %d %d\n",(int)dvecs[2].vx,(int)dvecs[2].vy,(int)dvecs[2].vz);


// calculate low points	(0,1)
	svecs[0].vy = svecs[1].vy = LECCY_BOTTOM_HEIGHT;

	for(i = 0; i < LECCY_COUNT; i++)
	{
		LeccyPercentage(&svecs[0],&dvecs[i],amount[i]);
		effectsDrawLightningStrand(&dvecs[i+LECCY_COUNT],&dvecs[i],0x808080);
//		effectsDrawLightningStrand(&dvecs[i+LECCY_COUNT],&dvecs[i],0xC0C000);
	}

// calculate high points (0,1)
	svecs[0].vx = (LECCYRAD2 * rsin(ya1))>>12;
	svecs[0].vz = (LECCYRAD2 * rcos(ya1))>>12;
	svecs[1].vx = (LECCYRAD2 * rsin(ya2))>>12;
	svecs[1].vz = (LECCYRAD2 * rcos(ya2))>>12;
	svecs[0].vy = svecs[1].vy =  FearLeccifiedPlat->move.point * LECCY_DELTA_HEIGHT + LECCY_BASE_HEIGHT;

	for(i = 0; i < LECCY_COUNT; i++)
	{
		LeccyPercentage(&svecs[0],&dvecs[i],amount[i+LECCY_COUNT*2]);
		effectsDrawLightningStrand(&dvecs[i+LECCY_COUNT],&dvecs[i],0x808080);
//		effectsDrawLightningStrand(&dvecs[i+LECCY_COUNT],&dvecs[i],0xC0F000);
	}

	if(!(activeframe & 7))
		sfxPlay(levelFX,SFX_FRANKIE_ATTACK_LOOPED);
}
/*****************************************************************************************/
UBYTE spoints[8] = {4,3,2,1,0,1,2,3};

void FearBossPlatforms()
{
	int i;
	int othertag;
	DYNCOLLBOX *ptr;
	DYNCOLLBOX *other;
//	int spoints[8] = {0,1,2,3,4,3,2,1};

	if(FearLeccifiedTimer)
		FearLeccifiedTimer--;
	if(FearCrossLeccyTimer)
		FearCrossLeccyTimer--;


	if(!FearBossResetFlag)
	{
		for(i = 0; i < 8; i++)
		{
			othertag = (i+4) & 7;

			loadlndFindPlatform(i+FFBOSS_PLATBASE, NULL, &ptr);


// Electrify the electified platform
			if(FearLeccifiedTimer && ptr == FearLeccifiedPlat)
				lscapeSetTerrainPlat(ptr, GOO);	//SPIKES);
			else
				lscapeSetTerrainPlat(ptr, 0);



			if((ptr->move.waitTimer==0) && (ptr->move.pointCtr!=0))
			{
				if(ptr->pPlatDef->head.maxspeed > 0)
				{

					loadlndFindPlatform(othertag+FFBOSS_PLATBASE, NULL, &other);

					if(other->move.waitTimer != 0)
					{
						other->move.waitTimer=0;
						other->pPlatDef->head.maxspeed = -ptr->pPlatDef->head.maxspeed;
						other->move.pointCtr=1;
						FearCrossPlatTimer = FEAR_PLAT_CAMTIME;
						FearCrossPlatDurn = FEAR_PLAT_CAMTIME;
					}
					FearCrossLeccyTimer = FEAR_CROSS_LECCYTIME;
					FearCrossLeccyPlatno = i;
					sfxPlay(levelFX,SFX_FRANKIE_ATTACK2);

				}
			}
		}
		loadlndFindPlatform(FFBOSS_RESET, NULL, &ptr);

		if((ptr->move.waitTimer==0) && (ptr->move.pointCtr!=0))
		{
			FearBossResetFlag = 1;
		}
	}
	else
	{
		FearBossResetFlag = 0;

		for(i = 0; i < 8; i++)
		{
			loadlndFindPlatform(i+FFBOSS_PLATBASE, NULL, &ptr);

			ptr->pPlatDef->head.start_point = spoints[i];	// coz genesis missed it out

			if(ptr->move.waitTimer==0 || ptr->move.point!=ptr->pPlatDef->head.start_point)
			{
				FearBossResetFlag = 1;
			}
			if(ptr->move.waitTimer!=0 && ptr->move.point != ptr->pPlatDef->head.start_point)
			{
				if(ptr->move.point < ptr->pPlatDef->head.start_point)
				{
					ptr->move.waitTimer=0;
					ptr->move.pointCtr=1;
				}
				else
				{
					ptr->move.waitTimer=0;
					ptr->pPlatDef->head.maxspeed = -ptr->pPlatDef->head.maxspeed;
					ptr->move.pointCtr=1;

				}
			}
		}
	}


	{
		ENEMYPOS *frank;
		loadlndFindPlatform(FFBOSS_KILLPLAT, NULL, &ptr);
		frank = loadlndFindEnemy(12);

	//	if((ptr->move.waitTimer==0) && (ptr->move.pointCtr!=0))
	//	if((ptr->move.waitTimer==0) && (ptr->move.pointCtr!=0))


		if(frank->doing != FRANK_DEAD && puzzleAtPoint(ptr, 1))
		{
			KillFrankie(frank);
// Leave it to Tim's camera case
//			FearCrossPlatTimer = FEAR_PLAT_CAMTIME*2;
//			FearCrossPlatDurn = FEAR_PLAT_CAMTIME*2;

		}
	}
}

// ===========================================================================
/*
typedef struct SpBossCtrl
{
	SHORT ya;
	SHORT ya_cam;
	SHORT xa;
	SHORT xa_cam;
}SPBOSSCTRL;
*/
SPBOSSCTRL SpBossCtrl;

/*
	VECTOR		bulletPos[10];
	VECTOR		bulletVel[10];
	NEWMODEL	*bulletModel[10];
	UBYTE		alive[10];
*/

/******************************************************************************************/
void	SpBossAddBullet(void)
{
	SHORT	loop=0;
	VECTOR	tempVec;

	static int which = 0;
	int xa;


	which++;
	if(which == 10)
		which = 0;
	loop = which;
/*
	while (CannonCtrl.alive[loop])
	{
		loop++;
		if (loop>9)
		{
			printf ("not enought bullets\n");
			return;
		}
	}
*/
	// fire a shot
// note:- ya = 0 needs to point the cannon towards -ve z...
	xa = SpBossCtrl.xa;
	xa += 0x40;	// guve us some extra elevation, so we end up near the target

	tempVec.vz = -rcos(xa);
	tempVec.vx = 0;
	tempVec.vy = -rsin(xa);

	RotateVector2D(&tempVec,&tempVec,SpBossCtrl.ya);


	CannonCtrl.bulletHit[loop]=FALSE;

	CannonCtrl.bulletRot[loop].vx = RANDOM256()*16;
	CannonCtrl.bulletRot[loop].vy = RANDOM256()*16;
	CannonCtrl.bulletRot[loop].vz = RANDOM256()*16;

	CannonCtrl.bulletRotAdd[loop].vx = RANDOM256();
	CannonCtrl.bulletRotAdd[loop].vy = RANDOM256();
	CannonCtrl.bulletRotAdd[loop].vz = RANDOM256();

	CannonCtrl.bulletVel[loop].vx = 35 * tempVec.vx;
	CannonCtrl.bulletVel[loop].vy = 35 * tempVec.vy;
	CannonCtrl.bulletVel[loop].vz = 35 * tempVec.vz;


// ball origin is a point below the camera
	tempVec.vz = rsin(SpBossCtrl.xa);
	tempVec.vx = 0;
	tempVec.vy = rcos(SpBossCtrl.xa);
	RotateVector2D(&tempVec,&tempVec,SpBossCtrl.ya);


	CannonCtrl.bulletPos[loop].vx= 30 * tempVec.vx + glovePos.vx;
	CannonCtrl.bulletPos[loop].vy= 30 * tempVec.vy + glovePos.vy;	// - 100.0f;
	CannonCtrl.bulletPos[loop].vz= 30 * tempVec.vz + glovePos.vz;

	CannonCtrl.alive[loop] = 2;	// two hits and it's out
	sfxPlay(levelFX,SFX_SP_BUGLE_DEATH);

	//CannonCtrl.alive[loop]=TRUE;
//	CannonCtrl.alive[loop]=8;
}
/****************************************************************************************/
void	SpBossUpdateBullets(void)
{
	SHORT	loop;
	int bounce;

	loop=0;
	for (loop=0;loop<10;loop++)
	{
		if (CannonCtrl.alive[loop])
		{
			bounce = 0;
			ballVel = CannonCtrl.bulletVel[loop];
			CheckForNmeHitsSphere(&CannonCtrl.bulletPos[loop], ballColl.radius, loop+2);
			CannonCtrl.bulletVel[loop] = ballVel;


			CannonCtrl.bulletRot[loop].vx=(CannonCtrl.bulletRot[loop].vx+CannonCtrl.bulletRotAdd[loop].vx)&4095;
			CannonCtrl.bulletRot[loop].vy=(CannonCtrl.bulletRot[loop].vy+CannonCtrl.bulletRotAdd[loop].vy)&4095;
			CannonCtrl.bulletRot[loop].vz=(CannonCtrl.bulletRot[loop].vz+CannonCtrl.bulletRotAdd[loop].vz)&4095;


// Keep the bugger in the arena

// (floorcheck but no roofcheck)
			if (CannonCtrl.bulletPos[loop].vy>(-13-10)<<12)
			{
				CannonCtrl.bulletPos[loop].vy=(-13-10)<<12;
				if(CannonCtrl.bulletVel[loop].vy > 0)
					CannonCtrl.bulletVel[loop].vy = -CannonCtrl.bulletVel[loop].vy/2;
				bounce = 1;

			}

			if (CannonCtrl.bulletPos[loop].vz > 0)
			{
				CannonCtrl.bulletPos[loop].vz=0;
				if(CannonCtrl.bulletVel[loop].vz > 0)
					CannonCtrl.bulletVel[loop].vz = -CannonCtrl.bulletVel[loop].vz/2;
				bounce = 1;
			}
			if (CannonCtrl.bulletPos[loop].vz < -3000<<12)
			{
				CannonCtrl.bulletPos[loop].vz= -3000<<12;
				if(CannonCtrl.bulletVel[loop].vz < 0)
					CannonCtrl.bulletVel[loop].vz = -CannonCtrl.bulletVel[loop].vz/2;
				bounce = 1;
			}

			if (CannonCtrl.bulletPos[loop].vx > 1000<<12)
			{
				CannonCtrl.bulletPos[loop].vx=1000<<12;
				if(CannonCtrl.bulletVel[loop].vx > 0)
					CannonCtrl.bulletVel[loop].vx = -CannonCtrl.bulletVel[loop].vx/2;
				bounce = 1;
			}
			if (CannonCtrl.bulletPos[loop].vx < -1000<<12)
			{
				CannonCtrl.bulletPos[loop].vx= -1000<<12;
				if(CannonCtrl.bulletVel[loop].vx < 0)
					CannonCtrl.bulletVel[loop].vx = -CannonCtrl.bulletVel[loop].vx/2;
				bounce = 1;
			}
			if(bounce)
				CannonCtrl.alive[loop]--;

			ADDVECTOR(&CannonCtrl.bulletPos[loop],&CannonCtrl.bulletPos[loop],&CannonCtrl.bulletVel[loop]);

			CannonCtrl.bulletVel[loop].vy += gravity / 5;	// >> 3;


//			if (!(CannonCtrl.alive[loop]%2) ) CannonCtrl.bulletVel[loop].vy--;
		}
	}
}
/*********************************************************************************************/
void SpBossDrawBullets(void)
{
	ULONG	loop;
  	gte_SetRotMatrix(&GsWSMATRIX);
  	gte_SetTransMatrix(&GsWSMATRIX);

	loop=0;
	for (loop=0;loop<10;loop++)
	{
		if (CannonCtrl.alive[loop])
		{
			pBallPSA->position.vx = CannonCtrl.bulletPos[loop].vx>>12;
			pBallPSA->position.vy = CannonCtrl.bulletPos[loop].vy>>12;
			pBallPSA->position.vz = CannonCtrl.bulletPos[loop].vz>>12;
			//CannonCtrl.alive[loop]+=8;
			pBallPSA->world.rotate.vx = CannonCtrl.bulletRot[loop].vx;
			pBallPSA->world.rotate.vy = CannonCtrl.bulletRot[loop].vy;
			pBallPSA->world.rotate.vz = CannonCtrl.bulletRot[loop].vz;

			objectSetAnimation(pBallPSA, 0);
//			objectDraw(pBallPSA);
			objectDrawWithShadow(pBallPSA,20,6,0);
		}
	}
}


#define SHIELD_TOP_Y -86
#define SHIELD_MID_Y -40

#if PALMODE==YES
#define SHIELD_TOP_X -220
#define SHIELD_MID_X -130
#define SHIELD_CEN_X -70
#else
#define SHIELD_TOP_X -200
#define SHIELD_MID_X -120
#define SHIELD_CEN_X -70
#endif

const SHORT panel_vecs[12 * 2] =
{
	SHIELD_TOP_Y,-SHIELD_MID_X,
	SHIELD_TOP_Y,0,
	SHIELD_TOP_Y,SHIELD_MID_X,

	SHIELD_MID_Y,-SHIELD_TOP_X,
	SHIELD_MID_Y,SHIELD_TOP_X,

	0,-SHIELD_CEN_X,
	0, SHIELD_CEN_X,

	-SHIELD_MID_Y,-SHIELD_TOP_X,
	-SHIELD_MID_Y,SHIELD_TOP_X,

	-SHIELD_TOP_Y,-SHIELD_MID_X,
	-SHIELD_TOP_Y,0,
	-SHIELD_TOP_Y,SHIELD_MID_X
};
const char panel_tris[12*3] =
{
	7,3,5,
	3,0,5,
	0,1,5,
	1,6,5,
	1,2,6,
	2,4,6,
	4,8,6,
	8,11,6,
	11,10,6,
	10,5,6,
	10,9,5,
	9,7,5
};

/*******************************************************************************************/
void SpBossDrawPanel()
{
	NEWMODEL *psa;
	int x;
	int y;
	static MATRIX temp;
	static VECTOR scale = {8800,5200,5800};
	int i;
	register	GsOTA 	*ot = (GsOTA*)(PolyList->org+3);	// behind the enemies panel and the object

	if(cameo_running)
		return;


	temp = GsWSMATRIX;
	GsWSMATRIX = GsIDMATRIX;
#if PALMODE==YES
	scale.vx = (9800 * (SpBossCtrl.panel_z+4096) >>12); 
	scale.vy = (5200 * (SpBossCtrl.panel_z+4096) >>12); 
#else
	scale.vx = (8800 * (SpBossCtrl.panel_z+4096) >>12); 
	scale.vy = (5200 * (SpBossCtrl.panel_z+4096) >>12); 
#endif
	ScaleMatrixL(&GsWSMATRIX,&scale);

	GsWSMATRIX.t[0] += 5;	// bit of a botch to get it central...
	GsWSMATRIX.t[1] += 3;


//	GsWSMATRIX.t[2] = SpBossCtrl.panel_z;

	modelctrl.depthoveride = 2;


	psa = spboss_panel_model->psa;
//	psa = pBallPSA;

	psa->position.vx = 0; //glovePos.vx>>12;
	psa->position.vy = 0; //glovePos.vy>>12;
	psa->position.vz = 0; //(glovePos.vz>>12);	// - (100);
	psa->world.rotate.vx = 0; //SpBossCtrl.xa_cam;
	psa->world.rotate.vy = 0; //(SpBossCtrl.ya_cam + 0x800) & 4095;	//0x800;
	psa->world.rotate.vz = 0;

	objectSetAnimation(psa, 0);
	objectDraw(psa);

	y = -((SpBossCtrl.xa - SpBossCtrl.xa_cam) / 8);
	x =   (SpBossCtrl.ya - SpBossCtrl.ya_cam) / 4;	// note difference in screen scales

//	sprctrl.scalex = 8192;
//	sprctrl.scaley = 8192;
	sprctrl.scalex = 4096;
	sprctrl.scaley = 4096;
	spritePrint(SPR_crosshair,x,y,10);
//	sprctrl.scalex = 4096;
//	sprctrl.scaley = 4096;

	modelctrl.depthoveride = 0;

	GsWSMATRIX = temp;


// draw the subtractive zone for the shield...

	if(SpBossCtrl.shield > 0)
	{
		for(i = 0; i < 12; i++)
		{
			char v[3];
			int r,g,b;
			unsigned int colour;

		//if (TOOMANYPOLYS(2*MAXPACKETSIZE,"CLEAR SCREEN"))return;
			POLY_G3 *si = (POLY_G3 *) GsOUT_PACKET_P;

			v[0] = panel_tris[i*3];
			v[1] = panel_tris[i*3+1];
			v[2] = panel_tris[i*3+2];

// hex = 0...5, dist = 0...255, pastel = 0...127

			colour = effectsGetPrimary(SpBossCtrl.pan_cols[(int)v[0]]>>8, SpBossCtrl.pan_cols[(int)v[0]] & 0xff, 32);
			r = ((colour & 0xff) * SpBossCtrl.shield) >> 6;
			g = (((colour>>8) & 0xff) * SpBossCtrl.shield) >> 6;
			b = (((colour>>16) & 0xff) * SpBossCtrl.shield) >> 6;
			SETRGBC(si->r0, r,g,b, GPU_COM_G3 | 2);


			colour = effectsGetPrimary(SpBossCtrl.pan_cols[(int)v[1]]>>8, SpBossCtrl.pan_cols[(int)v[1]] & 0xff, 32);
			r = ((colour & 0xff) * SpBossCtrl.shield) >> 6;
			g = (((colour>>8) & 0xff) * SpBossCtrl.shield) >> 6;
			b = (((colour>>16) & 0xff) * SpBossCtrl.shield) >> 6;
			SETRGBC(si->r1, r,g,b, GPU_COM_G3 | 2);

			colour = effectsGetPrimary(SpBossCtrl.pan_cols[(int)v[2]]>>8, SpBossCtrl.pan_cols[(int)v[2]] & 0xff, 32);
			r = ((colour & 0xff) * SpBossCtrl.shield) >> 6;
			g = (((colour>>8) & 0xff) * SpBossCtrl.shield) >> 6;
			b = (((colour>>16) & 0xff) * SpBossCtrl.shield) >> 6;
			SETRGBC(si->r2, r,g,b, GPU_COM_G3 | 2);

			x = (gameCtrl.fadeIn*160)/60;
			y = (gameCtrl.fadeIn*60)/60;

			si->x0 = panel_vecs[v[0]*2+1];
			si->x1 = panel_vecs[v[1]*2+1];
			si->x2 = panel_vecs[v[2]*2+1];
			si->y0 = panel_vecs[v[0]*2];
			si->y1 = panel_vecs[v[1]*2];
			si->y2 = panel_vecs[v[2]*2];

	//			si->code = si->code | 2;

 			PUTPACKETINTABLE(si,ot,POLYG3_LEN);
			GsOUT_PACKET_P+=sizeof(POLY_G3);
		}

		{
			DR_TPAGE *si = (DR_TPAGE *) GsOUT_PACKET_P;
			SetDrawTPage(si,1,1,((SEMITRANS_ADD-1)<<5));	// this is a macro, anyway
			PUTPACKETINTABLE(si,ot,DRTPAGE_LEN);
			GsOUT_PACKET_P+=sizeof(DR_TPAGE);
		}


		for(i = 0; i < 12; i++)
		{
			char v[3];

		//if (TOOMANYPOLYS(2*MAXPACKETSIZE,"CLEAR SCREEN"))return;
			POLY_G3 *si = (POLY_G3 *) GsOUT_PACKET_P; 
			SETRGBC(si->r0, SpBossCtrl.shield, SpBossCtrl.shield, SpBossCtrl.shield, GPU_COM_G3 | 2);
			*(ULONG *)(&si->r2) = *(ULONG *)(&si->r1) = *(ULONG *)(&si->r0);
//			SETRGBC(si->r1, 0, 127, 0, GPU_COM_G3 | 2);
//			SETRGBC(si->r2, 0, 127, 0, GPU_COM_G3 | 2);

			v[0] = panel_tris[i*3];
			v[1] = panel_tris[i*3+1];
			v[2] = panel_tris[i*3+2];

			x = (gameCtrl.fadeIn*160)/60;
			y = (gameCtrl.fadeIn*60)/60;

			si->x0 = panel_vecs[v[0]*2+1];
			si->x1 = panel_vecs[v[1]*2+1];
			si->x2 = panel_vecs[v[2]*2+1];
			si->y0 = panel_vecs[v[0]*2];
			si->y1 = panel_vecs[v[1]*2];
			si->y2 = panel_vecs[v[2]*2];

	//			si->code = si->code | 2;

 			PUTPACKETINTABLE(si,ot,POLYG3_LEN);
			GsOUT_PACKET_P+=sizeof(POLY_G3);
		}

		{
			DR_TPAGE *si = (DR_TPAGE *) GsOUT_PACKET_P;
			SetDrawTPage(si,1,1,((SEMITRANS_SUB-1)<<5));	// this is a macro, anyway
			PUTPACKETINTABLE(si,ot,DRTPAGE_LEN);
			GsOUT_PACKET_P+=sizeof(DR_TPAGE);
		}
	}
}


void SpBossQuake(int shifts)
{
	int dxa;
	int dya;


	dxa = (RANDOM256() & 63) + 64;	// 0... 128
	if(RANDOM256() & 1)
		dxa = -dxa;
	dya = (RANDOM256() & 63) + 64;	// 0... 128
	if(RANDOM256() & 1)
		dya = -dya;

	dxa = dxa >> shifts;
	dya = dya >> shifts;

//	dxa = (RANDOM256() - 128) >>shifts;
//	dya = (RANDOM256() - 128) >>shifts;

	SpBossCtrl.xa_cam += dxa;
	SpBossCtrl.ya_cam += dya;
	SpBossCtrl.xa += dxa/2;
	SpBossCtrl.ya += dya/2;
}

void SpaceBossUpdate()
{
	SHORT	leftRight=0,upDown=0;
	USHORT	padIn;
//	VECTOR	tempVec;
//	SHORT	height,len,tempCos,tempSin,height1;
	int i;

	GloveCtrl.enabled = FALSE;

	padIn=debounce[0];
	leftRight=(nleftx[0]);
	upDown=-(nlefty[0]);

	GloveCtrl.enabled=FALSE;

	glovePos.vx = ER_FINAL_X<<12;	//-300 <<12;
	glovePos.vy = ER_FINAL_Y<<12;	//EVIL_YOU_HEIGHT;
	glovePos.vz = ER_FINAL_Z<<12;	//-400 <<12;

	SpBossUpdateBullets();

	if(cameo_running)
		return;


	if (upDown)
	{
		if (upDown>0) SpBossCtrl.xa -= (upDown/128);
		if (upDown<0) SpBossCtrl.xa -= (upDown/128);
	}
	if (leftRight)
	{
		if (leftRight<0) SpBossCtrl.ya += (leftRight/128);
		if (leftRight>0) SpBossCtrl.ya += (leftRight/128);
	}

// note - xa & ya can also be thrown off by earthquakes
	if(SpBossCtrl.xa < -500) SpBossCtrl.xa = -500;
	if(SpBossCtrl.xa >  500) SpBossCtrl.xa =  500;
	if(SpBossCtrl.ya < -1024) SpBossCtrl.ya = -1024;
	if(SpBossCtrl.ya >  1024) SpBossCtrl.ya =  1024;

	if(!SpBossCtrl.ball_disable)
	{
		if (padIn&PAD_CROSS)
		{
			SpBossAddBullet();
			SpBossCtrl.ball_disable = 20;
		}
	}
	else
	{
		SpBossCtrl.ball_disable--;
	}

	if (pad[0]&PAD_CIRCLE && !SpBossCtrl.panel_z)
	{
		if(SpBossCtrl.shield < 128)
			SpBossCtrl.shield += 6;
		if(SpBossCtrl.shield > 128)
			SpBossCtrl.shield = 128;

	}
	else
	{
		if(SpBossCtrl.shield > 0)
			SpBossCtrl.shield -= 8;
		if(SpBossCtrl.shield == 0)
			SpBossCtrl.shield = 0;
	}
	if(SpBossCtrl.shield > 32)
		SpBossCtrl.ball_disable = 2;



	SpBossCtrl.xa_cam += (findShortestAngle(SpBossCtrl.xa,SpBossCtrl.xa_cam)/12);
	SpBossCtrl.ya_cam += (findShortestAngle(SpBossCtrl.ya,SpBossCtrl.ya_cam)/12);


	if(SpBossCtrl.shield > 0)
	{
		for(i = 0; i < 12; i++)
		{
			SpBossCtrl.pan_vels[i] += ((RANDOM256() - 128) >> 4);

			if(SpBossCtrl.pan_vels[i] > 32)
				SpBossCtrl.pan_vels[i] = 32;
			if(SpBossCtrl.pan_vels[i] < -32)
				SpBossCtrl.pan_vels[i] = -32;
			SpBossCtrl.pan_cols[i] += SpBossCtrl.pan_vels[i];
			while(SpBossCtrl.pan_cols[i] > 256*6)
				SpBossCtrl.pan_cols[i] -= 256*6;
			while(SpBossCtrl.pan_cols[i] < 0)
				SpBossCtrl.pan_cols[i] += 256*6;
		}
	}

	if(SpBossCtrl.panel_z)
		SpBossCtrl.panel_z -= 128;


/*
	if (padIn&PAD_CROSS)
	{
		addBullet();
	}
*/
	//updateBullets();


}

// ===========================================================================
#define CAVE_NOWT	0
#define CAVE_PULLING 1
#define CAVE_TURNING 2
#define CAVE_DROPPING 3
#define CAVE_DECISIONS 4
#define CAVE_FINISHED 5
#define CAVE_OUTRO 6

typedef struct CAVECTRLSTR
{
	int doing;	// 0 = pulling ball in
	int angle;
	int current_xtal;
	VECTOR ballpos;
	int outro_upcount;
	int outro_outcount;
	int ya;
}CAVECTRL;

CAVECTRL CaveCtrl;

static VECTOR xtalpos[7] =
{
//	{ 105,0,-150},
	{  85,8,-162},
	{ 161,8,   0},
	{ 105,8, 142},
	{   0,8, 190},
	{-105,8, 142},
	{-161,8,   0},
	{ -85,8,-162}
};

// How many crystals are already in holes?
int CaveCrystalsGot()
{
	int i;
	int n_levs;


	n_levs = 0;
	if(GameState.levels_open[0] & LEVOPEN_L1_OPEN)	// Is Atlantis open ?
		n_levs++;

	for(i = 0; i < 6; i++)
	{
		if(GameState.levels_open[i] & LEVOPEN_CRYSTAL_DELIVERED)	// Have we returned that level's Xtal ?
			n_levs++;
	}

	return n_levs;
}

int CaveHubDecide()
{
	int n_levs;
// open some levels
	n_levs = CaveCrystalsGot();

	switch(n_levs)
	{
		case 0:	// open atlantis, Use hub2
		case 1:
			GameState.levels_open[0] |= LEVOPEN_L1_OPEN;
			gameCtrl.hub_to_use = 2;
			break;

		case 2:	// open carnival, pirates. Use hub 3
			GameState.levels_open[1] |= LEVOPEN_L1_OPEN;
			GameState.levels_open[2] |= LEVOPEN_L1_OPEN;
			gameCtrl.hub_to_use = 3;
			break;

		case 3:	// no new door-opening. keep using hub3

			gameCtrl.hub_to_use = 3;
			break;

		case 4:	// open prehist, fortress, use hub 5
			GameState.levels_open[3] |= LEVOPEN_L1_OPEN;
			GameState.levels_open[4] |= LEVOPEN_L1_OPEN;
			gameCtrl.hub_to_use = 5;
			break;

		case 5:	// no new door-opening. keep using hub5
			gameCtrl.hub_to_use = 5;
			break;

		case 6:	// open space. Use hub7 (or possibly hub8)
			GameState.levels_open[5] |= LEVOPEN_L1_OPEN;
			gameCtrl.hub_to_use = 8;
			break;

		case 7:
			gameCtrl.hub_to_use = 8;
//			CaveCtrl.outro_upcount = 0;
//			CaveCtrl.outro_outcount = 0;
//			CaveCtrl.doing = CAVE_OUTRO;
			break;
	}
	DB("decided to use hub %d\n",gameCtrl.hub_to_use);
	return n_levs;

}

// hubcave cameo
void CaveUpdate()
{
//	static VECTOR holdpos = {0<<12, -70<<12, -50<<12};
	static VECTOR holdpos = {0<<12, -70<<12, -100<<12};
	static int angles[7] = {0xE00, 0xC00, 0xA00, 0x800, 0x600, 0x400, 0x200};
	VECTOR temp;
	int i;

// Hand in exit box

// we don't want to allow the glove to leave if the ball's yet to come out of the hoop
//				if(DoesBallExist() == 2)


	if(CaveCtrl.doing == CAVE_NOWT || CaveCtrl.doing == CAVE_FINISHED)
	{

// Cheaty bit to fast-forward to the outro
/*
	gameCtrl.wayroom_world = SPACE;
	gameCtrl.hub_to_use = 8;
	for(i = 0; i < 6; i++)
	{
		GameState.levels_open[i] |= LEVOPEN_L1_OPEN + LEVOPEN_BOSS_OPEN;
		if(i != 5)
			GameState.levels_open[i] |= LEVOPEN_L1_OPEN + LEVOPEN_CRYSTAL_DELIVERED;
	}
*/


		if(
			PuzzleVars.user_variables[1]
			&&
			(
			CaveCtrl.doing == CAVE_FINISHED
			||
//			(!gameCtrl.port_with_ball && !ball_at_hoop)
			(!gameCtrl.port_with_ball)
			)
			)
		{
			if(!gameCtrl.dropOutFlag)
			{
				gameCtrl.dropOutFlag=GAME_COMPLETED;
				gameCtrl.nextLevel = gameCtrl.hub_to_use;
				MenuFadeOut();	// immediate fade
			}
		}
	}
	else
	{
		GloveCtrl.padDisableTimer = 2;
		cameo_running = 2;	// prevent glove movement
		if (Glover.currentAnimation==HANDANIM_IDLE)
		{
			gloveVel.vx=0;
			gloveVel.vz=0;
			GloveCtrl.action=HAND_IDLE;
			//printf ("setting glove vel to 0\n");	// all this just to stop him when you quit!
		}
		//gloveVel.vy=(4096*4);
		//GloveCtrl.action=HAND_IDLE;
		GloveCtrl.positionHold=FALSE;
		//printf ("setting glove vel to 0\n");
	}





// If there's a live ball in the cave, set the cave cameo off
/*
	if(CaveCtrl.doing == 0 && BallCtrl.enabled)
	{
		DB("Cave says ballctrl.enabled is 1\n");
		if(GloveCtrl.handOnBall || GloveCtrl.ballWithHand)
		{
			DisconnectGloveFromBall(DISCONNECT_WHEN_WHATEVER | DISCONNECT_GENTLY);
		}

// turn the thing off. turn on the cameo ball object

// trigger cameos
		CaveCtrl.current_xtal = CaveCrystalsGot();

// This should always happens, but cheats could theoretically allow you to bring a ball in here after collecting all xtals
// Note - 7 crystals. One from each world, one from the hub
		enemy_has_ball = 2;
		CaveCtrl.doing = CAVE_PULLING;
		CaveCtrl.ballpos = ballPos;
	}
*/


	if(CaveCtrl.doing == 0 && BallCtrl.enabled)
	{
//		DB("Cave says ballctrl.enabled is 1\n");

//		if(GloveCtrl.handOnBall || GloveCtrl.ballWithHand)

// do it this way, because otherwise the wizard grabs the ball when the camera's still focussed on it coming through the hoop.
// the final check is in case you managed to run out from under it
		if(GloveCtrl.action == HAND_JOINED || GloveCtrl.action == HAND_BALLWALK)
		{
			if(GloveCtrl.handOnBall || GloveCtrl.ballWithHand)
			{
				DisconnectGloveFromBall(DISCONNECT_WHEN_WHATEVER | DISCONNECT_GENTLY);
// This should always happens, but cheats could theoretically allow you to bring a ball in here after collecting all xtals
// Note - 7 crystals. One from each world, one from the hub
				enemy_has_ball = 2;
				CaveCtrl.doing = CAVE_PULLING;
				CaveCtrl.ballpos = ballPos;
			}
		}
		else
		{
			if(BallCtrl.ballOnGround)
			{
				enemy_has_ball = 2;
				CaveCtrl.doing = CAVE_PULLING;
				CaveCtrl.ballpos = ballPos;
			}
		}

// turn the thing off. turn on the cameo ball object
// trigger cameos
		CaveCtrl.current_xtal = CaveCrystalsGot();

	}




	CaveCtrl.ya += 0x10;
	switch(CaveCtrl.doing)
	{
	case CAVE_NOWT:
		return;

	case CAVE_DECISIONS:
		{
			int n_levs;
			
			CaveCtrl.doing = CAVE_FINISHED;

			if(CaveCtrl.current_xtal != 0)	// "got xtal 0" check is done by checking that atlantis level 1 is open
			{
				int world_delivered;
//				DB("Wayroom world is %d\n",gameCtrl.wayroom_world);

// Fix for beating multiple bosses, but not delivering the crystal.
// Won't happen in future - I'll "un-beat" previous bosses if you bean a new one. ("NextLevelBasics", in enemies)
				world_delivered = gameCtrl.wayroom_world-1;

				if(world_delivered < 0 || world_delivered >= 6) world_delivered = 0;	// should never happen, but we have an odd magic ball bug

				if(!(GameState.levels_open[world_delivered] & LEVOPEN_CRYSTAL_DELIVERED)
				  &&(GameState.levels_open[world_delivered] & LEVOPEN_BOSS_BEATEN)
					)
				{
//					DB("world delivery as per normal to %d\n",world_delivered);
				}
				else
				{
//					DB("scanning for alternative world to deliver to\n");
					for(world_delivered = 0; world_delivered < 6; world_delivered++)
					{
						if(!(GameState.levels_open[world_delivered] & LEVOPEN_CRYSTAL_DELIVERED)
						  &&(GameState.levels_open[world_delivered] & LEVOPEN_BOSS_BEATEN)
							)
						{
//							DB("world %d seems good\n",world_delivered);
							break;
						}
					}
				}

//				DB("xtal %d delivered\n",gameCtrl.wayroom_world-1);
//				GameState.levels_open[gameCtrl.wayroom_world-1] |= LEVOPEN_CRYSTAL_DELIVERED;	// flag crystal has having been got

				if(world_delivered < 6)	// this should always be the case, but just on the offchance
				{
					DB("xtal %d delivered\n",world_delivered);
					GameState.levels_open[world_delivered] |= LEVOPEN_CRYSTAL_DELIVERED;	// flag crystal has having been got
				}
			
			}


// open some levels
			n_levs = CaveHubDecide();
				
				
//				CaveCrystalsGot();
/*
			switch(n_levs)
			{
				case 0:	// open atlantis, Use hub2
				case 1:
					GameState.levels_open[0] |= LEVOPEN_L1_OPEN;
					gameCtrl.hub_to_use = 2;
					break;

				case 2:	// open carnival, pirates. Use hub 3
					GameState.levels_open[1] |= LEVOPEN_L1_OPEN;
					GameState.levels_open[2] |= LEVOPEN_L1_OPEN;
					gameCtrl.hub_to_use = 3;
					break;

				case 3:	// no new door-opening. keep using hub3

					gameCtrl.hub_to_use = 3;
					break;

				case 4:	// open prehist, fortress, use hub 5
					GameState.levels_open[3] |= LEVOPEN_L1_OPEN;
					GameState.levels_open[4] |= LEVOPEN_L1_OPEN;
					gameCtrl.hub_to_use = 5;
					break;

				case 5:	// no new door-opening. keep using hub5
					gameCtrl.hub_to_use = 5;
					break;

				case 6:	// open space. Use hub7 (or possibly hub8)
					GameState.levels_open[5] |= LEVOPEN_L1_OPEN;
					gameCtrl.hub_to_use = 8;
					break;

				case 7:
					gameCtrl.hub_to_use = 8;
					CaveCtrl.outro_upcount = 0;
					CaveCtrl.outro_outcount = 0;

					CaveCtrl.doing = CAVE_OUTRO;
					break;
			}
*/
			if(n_levs == 7)
			{
				CaveCtrl.outro_upcount = 0;
				CaveCtrl.outro_outcount = 0;

				CaveCtrl.doing = CAVE_OUTRO;
			}

		}
		return;

	case CAVE_FINISHED:
		return;

	case CAVE_PULLING:
		if(VectorMoveTo(&CaveCtrl.ballpos, &holdpos, 4 << 12))
		{
			CaveCtrl.doing = CAVE_TURNING;
		}
		CaveCtrl.angle = 0;
		ballPlaceAt(&CaveCtrl.ballpos);	// hmmm...
		break;

	case CAVE_TURNING:

		CaveCtrl.angle = AngleHomer(CaveCtrl.angle,angles[CaveCtrl.current_xtal],40);
		CaveCtrl.ballpos.vx = -100 * rsin(CaveCtrl.angle);
		CaveCtrl.ballpos.vy = -100 * rcos(CaveCtrl.angle);
		CaveCtrl.ballpos.vy = -70<<12;
		ballPlaceAt(&CaveCtrl.ballpos);	// hmmm...

		if(CaveCtrl.angle==angles[CaveCtrl.current_xtal])
		{
//			sfxPlay3D(globalFX,SFX_CAST_SPELL_3,&ballPos);
			sfxPlay(globalFX,SFX_CAST_SPELL_3);
			effectsStartOverlay(10, 0x00ff00);
			BallCtrl.enabled = 0;
			CaveCtrl.doing = CAVE_DROPPING;
		}

		break;
	case CAVE_DROPPING:
		temp.vx = xtalpos[CaveCtrl.current_xtal].vx << 12;
		temp.vy = xtalpos[CaveCtrl.current_xtal].vy << 12;
		temp.vz = xtalpos[CaveCtrl.current_xtal].vz << 12;

		CaveCtrl.angle = 0;
		ballPlaceAt(&CaveCtrl.ballpos);	// hmmm...
		BallCtrl.enabled = 0;


		if(VectorMoveTo(&CaveCtrl.ballpos, &temp, 4 << 12))
		{
			CaveCtrl.doing = CAVE_DECISIONS;
//			effectsStartOverlay(10, 0x00ff00);

// turn the ball off
// screenflash
// create a big crystal

			ballPos = caveballpos;	// so you point towards the exit
		}
		break;
	case CAVE_OUTRO:
	{
		VECTOR pos;
		unsigned int colour;
		for(i = 0; i < 7; i++)
		{
			pos = xtalpos[i];
			pos.vy -= (CaveCtrl.outro_upcount>>12);

			pos.vx += ( 0 -pos.vx) * CaveCtrl.outro_outcount / 4096;
			pos.vz += ( -1000 - pos.vz) * CaveCtrl.outro_outcount / 4096;

			pos.vx = (pos.vx << 12) + (RANDOM256() << 8);
			pos.vy = (pos.vy + 20) << 12;
			pos.vz = (pos.vz << 12) + (RANDOM256() << 8);

			colour = random(6 * 256);
			colour = effectsGetPrimary(colour>>8,(colour &0xff),32);

			New_Debris(DEBRIS_HOOPHOVER,&pos,colour);
		}
		if(CaveCtrl.outro_upcount < (100 << 12))
		{
			CaveCtrl.outro_upcount += 4096;
		}
		else
		{
			if(CaveCtrl.outro_outcount < (4096))
				CaveCtrl.outro_outcount += 50;

			if(CaveCtrl.outro_outcount >= 4096 - 20)
			{
// we're finished! Time to play the vid, dude!
				gameCtrl.dropOutFlag=GAME_COMPLETED;
				gameCtrl.nextLevel = TITLE;
			}
		}

		break;
	}
	}

	switch(CaveCtrl.doing)
	{
	case CAVE_PULLING:
	case CAVE_TURNING:
	case CAVE_DROPPING:
		if(!(activeframe & 7))
			sfxPlay(levelFX,SFX_HU_FRANKIE_ATTACK_LOOP);
		enemy_has_ball = 2;

// prevent you from jumping onto the ball
		if(GloveCtrl.handOnBall || GloveCtrl.ballWithHand)
		{
			DB("Get OFF, you stupid glove - You're gonna die if you stay on there!\n");
			DisconnectGloveFromBall(DISCONNECT_WHEN_WHATEVER | DISCONNECT_GENTLY);
		}
	}


}


void CaveDrawXtal()
{
	pCballPSA->world.rotate.vx = 0;
	pCballPSA->world.rotate.vy = CaveCtrl.ya;
	pCballPSA->world.rotate.vz = 0;

	pCballPSA->globalscale.vx = 512;
	pCballPSA->globalscale.vy = 512;
	pCballPSA->globalscale.vz = 512;

// sparklies
	if(!(activeframe & 3))
	{
		VECTOR tvec;
		tvec.vx = (pCballPSA->position.vx <<12) + (((random(16)) - 8)<<13);
		tvec.vy = (pCballPSA->position.vy <<12) + (((random(16)) - 8)<<13);
		tvec.vz = (pCballPSA->position.vz <<12) + (((random(16)) - 8)<<13);

		New_Debris(DEBRIS_WISP,&tvec,0);
	}

	ChangeBall(&pCballPSA->world,ballTex[BALL_MODE_CRYSTAL]);

	objectSetAnimation(pCballPSA, 0);
	modelctrl.lighting=YES;
	objectDraw(pCballPSA);
	modelctrl.lighting=NO;
}


// litenin, out of hands, to ball
void CaveDraw()
{
	static SVECTOR handvecs[2] =
	{
		{10,-80,0},
		{-10,-80,0}
	};


	SVECTOR bvec;
	int i;

	bvec.vx = ballPos.vx >>12;
	bvec.vy = ballPos.vy >>12;
	bvec.vz = ballPos.vz >>12;

	switch(CaveCtrl.doing)
	{
	case CAVE_PULLING:
	case CAVE_TURNING:
	case CAVE_DROPPING:
		effectsDrawLightningStrand(&bvec,&handvecs[0],0x808080);
		effectsDrawLightningStrand(&bvec,&handvecs[1],0x808080);
		break;
	}


// draw whichever crystals we've got already, plus the moving one...


	modelctrl.semitrans = 2;

	if(CaveCtrl.doing == CAVE_DROPPING)
	{
		pCballPSA->position.vx = CaveCtrl.ballpos.vx >> 12;
		pCballPSA->position.vy = CaveCtrl.ballpos.vy >> 12;
		pCballPSA->position.vz = CaveCtrl.ballpos.vz >> 12;
		CaveDrawXtal();
	}

	for(i = 0; i < 7; i++)
	{
		if( i <CaveCrystalsGot())
		{
			pCballPSA->position = xtalpos[i];

			if(CaveCtrl.doing == CAVE_OUTRO)
			{
				pCballPSA->position.vy -= (CaveCtrl.outro_upcount>>12);

				pCballPSA->position.vx += ( 0 - pCballPSA->position.vx) * CaveCtrl.outro_outcount / 4096;
				pCballPSA->position.vz += ( -1000 - pCballPSA->position.vz) * CaveCtrl.outro_outcount / 4096;
			}
			CaveDrawXtal();
		}
	}
	modelctrl.semitrans = 0;

// Messy, but deals with all sorts of things more easily than rendering it by hand
//	BallCtrl.enabled = TRUE;
//	ballRender();
//	BallCtrl.enabled = FALSE;

}

char pr1_activated = FALSE;
void Pr1Update()
{
	DYNCOLLBOX *pBox;
	loadlndFindPlatform(177, NULL, &pBox);
	if(!pBox)
	{
//		DB("box not found\n");
		return;
	}

	if(pBox->active)
	{
//		DB("box active\n");
		BallCtrl.enabled = FALSE;
		pr1_activated = FALSE;
	}
	else
	{
		if(!pr1_activated)
		{
//		DB("box crunched\n");
			BallCtrl.enabled = TRUE;
			pr1_activated = TRUE;
		}
	}
}
void Pr1Draw()
{
	DYNCOLLBOX *pBox;
	loadlndFindPlatform(177, NULL, &pBox);
	if(!pBox)
		return;

	if(pBox->active)
	{
		BallCtrl.enabled = FALSE;
		pr1_activated = FALSE;
/*
		pBallPSA->world.rotate.vx = 0;
		pBallPSA->world.rotate.vy = 0;
		pBallPSA->world.rotate.vz = 0;
*/

//		pBallPSA->globalscale.vx = 19*4096*NORMAL_SCALE*1;
//		pBallPSA->globalscale.vy = 19*4096*NORMAL_SCALE*1;
//		pBallPSA->globalscale.vz = 19*4096*NORMAL_SCALE*1;
		pBallPSA->globalscale.vx = 19* 4096/152 *NORMAL_SCALE*1;
		pBallPSA->globalscale.vy = 19* 4096/152 *NORMAL_SCALE*1;
		pBallPSA->globalscale.vz = 19* 4096/152 *NORMAL_SCALE*1;

//ballRadius/152

		pBallPSA->position.vx = -134;
		pBallPSA->position.vy = -95;
		pBallPSA->position.vz = 455;

		ballPos.vx = pBallPSA->position.vx << 12;
		ballPos.vy = pBallPSA->position.vy << 12;
		ballPos.vz = pBallPSA->position.vz << 12;


		if(RadiusCheck4096(&ballPos,ballColl.radius,1500))
		{
			objectSetAnimation(pBallPSA, 0);
			modelctrl.lighting=YES;
			objectDraw(pBallPSA);
			modelctrl.lighting=NO;
		}
	}
}

// ===========================================================================

void bosses_Update()
{
	switch(level)
	{
//		case FEARTEST:
		case FEARBOSS:
			FearBossPlatforms();
			return;
		case CARNIVALBOSS:
			CaBossUpdate();
			break;
		case SPACEBOSS2:
			SpaceBossUpdate();
			return;

		case PREHISTORIC1:
			Pr1Update();
			return;

		case PREHISTORICBONUS:
//			DB("ballpos %d %d %d\n",ballPos.vx >> 12,ballPos.vy >> 12,ballPos.vz >> 12);
//			if(ballPos.vz < (-3750 << 12))
			if(ballPos.vz < (-3770 << 12))
			{
				if(!gameCtrl.dropOutFlag)
				{

					gameCtrl.dropOutFlag=GAME_COMPLETED;
					gameCtrl.nextLevel = LevelInfo[level].nextlevel;
					MenuFadeOut();	// immediate fade
				}
			}
			return;

		case CAVE:
			CaveUpdate();
			return;

		case HUB1:
		case HUB2:
		case HUB3:
		case HUB4:
		case HUB5:
		case HUB6:
		case HUB7:
		case HUB8:
			if(PuzzleVars.user_variables[1])
			{
				if(!gameCtrl.dropOutFlag)
				{
					gameCtrl.dropOutFlag=GAME_COMPLETED;
					gameCtrl.nextLevel = CAVE;
					MenuFadeOut();	// immediate fade
				}
			}
			return;


		case WAYROOM:	// tbd - multiple wayrooms (!)
			if(PuzzleVars.user_variables[1] && !gameCtrl.dropOutFlag)
			{
				int dest;
				static int destinations[6] =
				{
//                   1      2    3   4   5  6
//					-1,   BONUS,L3,BOSS,L2,L1
					-1,   BOSS,L3,BONUS,L2,L1
				};
				dest = PuzzleVars.user_variables[1]-1;
				dest = destinations[dest];
				if(dest == -1)
				{
					gameCtrl.nextLevel = gameCtrl.hub_to_use;
				}
				else
				{
					switch(gameCtrl.wayroom_world)
					{
					case ATLANTIS:
						gameCtrl.nextLevel = ATLANTIS1 + dest;
						break;
					case CARNIVAL:
						gameCtrl.nextLevel = CARNIVAL1 + dest;
						break;
					case PIRATES:
						gameCtrl.nextLevel = PIRATES1 + dest;
						break;
					case PREHISTORIC:
						gameCtrl.nextLevel = PREHISTORIC1 + dest;
						break;
					case FORTRESS:
						gameCtrl.nextLevel = FEAR1 + dest;
						break;
					case SPACE:
						gameCtrl.nextLevel = SPACE2A + dest;
						if(dest == BOSS)
							gameCtrl.nextLevel = SPACEBOSS2;
						else if (dest == BONUS)
							gameCtrl.nextLevel = SPACEBONUS;
						break;
					default:
						DB("unknown wayroom world %d\n",gameCtrl.wayroom_world);
						CRASH;
						break;
					}
				}
				gameCtrl.dropOutFlag=GAME_COMPLETED;	// no glove animation, just switch to the level
				MenuFadeOut();	// immediate fade

			}
			return;
	}
}


char boss_pow_at[] = {1,2,0,0,0};	// orange, red, blue,blue,blue
char boss_pow_pr[] = {0,0,0};		// three blues
char boss_pow_pi[] = {1,1,1};		// three oranges
char boss_pow_ca[] = {0,1,2};		// red,grn,blue
char boss_pow_sp[] = {1,2,2};		// one purple, two oranges
char boss_pow_ff[] = {1,1,1};		// three greens


void bosses_Panel()
{
	int i;
	char *ptr;

  	gte_SetRotMatrix(&GsWSMATRIX);
  	gte_SetTransMatrix(&GsWSMATRIX);
/*
	for(i = 0; i < 10; i++)
	{
		CannonCtrl.bulletModel[i] = pBallPSA;
		CannonCtrl.bulletModel[i]->position.vx = (i-5) * 100;
		CannonCtrl.bulletModel[i]->position.vy = -500;
		CannonCtrl.bulletModel[i]->position.vz = -1200;


		objectSetAnimation(CannonCtrl.bulletModel[i], 0);


		objectDraw(CannonCtrl.bulletModel[i]);
	}
*/
	switch(level)
	{
		case PREHISTORICBOSS:
			ptr = &boss_pow_pr[0];
			break;
		case PIRATESBOSS:
			ptr = &boss_pow_pi[0];
			break;
		case CARNIVALBOSS:
			ptr = &boss_pow_ca[0];
			break;
		case SPACEBOSS2:
			SpBossDrawBullets();
			SpBossDrawPanel();
			ptr = &boss_pow_sp[0];
			break;
		case FEARBOSS:
			FearDrawLeccifiedPlatform();
			ptr = &boss_pow_ff[0];
			break;
		case ATLANTISBOSS:
			ptr = &boss_pow_at[0];
			break;

		case PREHISTORIC1:
			Pr1Draw();
			return;
		case CAVE:
			CaveDraw();
			return;
		default:
			ptr = NULL;
			boss_n_powers = 0;
			return;

	}

//	sprctrl.scalex = 4096 * 512 / 320;
//	sprctrl.scaley = 4096;

	sprctrl.scalex = 4096;
	sprctrl.scaley = 4096;

	if((!boss_bar_flicker)&&(boss_n_powers!=old_boss_n_powers))
	{
		boss_bar_flicker=16;   	
	}

	old_boss_n_powers=boss_n_powers;
	
	for(i =0; i < boss_n_powers;i++)
	{
//		*(int *)(&sprctrl.r) = pow_colours[i] | (SEMITRANS_NONE << 24);
		*(int *)(&sprctrl.r) = 0x808080 | (SEMITRANS_NONE << 24);
//		spritePrint(SPR_powbars[(int)(*ptr)],-210 + i * 51, -90,10);
		spritePrint(SPR_powbars[(int)(*ptr)],-210 + i * 31, -90,1);
		ptr++;
	}

	if(boss_bar_flicker>0)
	{
		if(boss_bar_flicker&1)
			spritePrint(SPR_powbars[(int)(*ptr)],-210 + i * 31, -90,1);
		
		boss_bar_flicker--;
	}

	sprctrl.scalex = sprctrl.scaley = 4096;

}


// ============== Beginning & end of the Lscape parse ===========


void bosses_BeginFile()
{
	int loop;
//	SPR_powbar = textureFindInAllBanks("HITBAR2");
//	SPR_powbar = textureFindInAllBanks("HITBAR");


	SPR_powbars[0] = textureFindInAllBanks("HITBARB");
	SPR_powbars[1] = textureFindInAllBanks("HITBARO");
	SPR_powbars[2] = textureFindInAllBanks("HITBARR");

	boss_n_powers = old_boss_n_powers = boss_bar_flicker = 0;


	for (loop=0;loop<10;loop++)
	{
		CannonCtrl.alive[loop] = 0;
	}

//	nmeinfo[CAMEO_ENEMY].radius = 256;
//	nmeinfo[CAMEO_ENEMY].interact = NULL;

	switch(level)
	{
	case CARNIVALBOSS:
		ca_pie_enemy = enemies_ReserveEnemy(CA_PIE);

		ca_glove_enemy = enemies_ReserveEnemy(CA_GLOVE);
		ca_glove_enemy->tag = 255;	// used as a check by the puzzles

		ca_bomb_enemy = enemies_ReserveEnemy(CA_BOMB);
		ca_piano_enemy = enemies_ReserveEnemy(CA_PIANO);
		caboss_spin = 0;


//		nmeinfo[CAMEO_ENEMY].radius = 1024;
//		nmeinfo[CAMEO_ENEMY].interact = &NoseIntRou;


		break;

	case FEARBOSS:
		FearBossResetFlag = 1;	// so it resets while the cameo's playing
		FearLeccifiedPlatno=0;
		FearLeccifiedTimer = 0;
		FearCrossLeccyTimer = 0;
		FearCrossPlatTimer = 0;
		FearSnapFlag = 0;
		break;

	case SPACEBOSS2:
		FMEMZERO(&SpBossCtrl,sizeof(SpBossCtrl));

		spboss_panel_model = EnsureModelLoaded(0,"PANEL",1600);	//2048);
		SPR_crosshair = textureFindInAllBanks("CROSS01");
//		SpBossCtrl.shield = 0;
		for(loop = 0; loop < 12; loop++)
		{
			SpBossCtrl.pan_cols[loop] = RANDOM256() * 6;
//			SpBossCtrl.pan_vels[loop] = 0;
		}
		SpBossCtrl.panel_z = 4096;
		break;
	case CAVE:
//		CaveCtrl.doing = 0;
		FMEMZERO(&CaveCtrl,sizeof(CaveCtrl));
		break;
	}
}

void bosses_Endfile()
{
	switch(level)
	{
	case CARNIVALBOSS:
		puzzleSetExistence(14,0);	// Turn off the platform piano
		break;
	}

}
