#include "glover.h"
//#include "bff_load.h"

#define TAG_SPECIALTILT 19

// replace with the empty definition when checking in. Ta. Fred
#define PLAT_DB
//#define PLAT_DB DB

// data for keeping track of debris objects

#define MAX_AUTOBOXES 64

COLLBOX pAutoBoxes[MAX_AUTOBOXES];
int nAutoBoxes;
//NEWMODEL *pBodgeFragment=NULL;

extern int DEPTHOFFS;

typedef struct tagOBJDEBRIS
{
	int nBits;
	int life;
	NEWMODEL *pModel;
	VECTOR pos[MAX_OBJDEBRIS];
	VECTOR vel[MAX_OBJDEBRIS];
}OBJDEBRIS;

OBJDEBRIS objDebris;

PLATSLOADED pPlatsLoaded[MAXPLATSLOADED];
int nPlatsLoaded=0;

//	ULONG crc;			// CRC of filename of box (minus .ext)
//	NEWMODEL *pModel;	// pointer to PSA model
//	int nCollBoxes;		// number of collboxes in collision
//	COLLBOX *pBoxes;	// pointer to collision mesh

/*
void platformCentrePlat(PLATSLOADED *pPlat)
{
	int nBoxes=pPlat->nCollBoxes;
	COLLBOX *pBox;
	int i;
	VECTOR min,max,mid;

	for(i=0; i<nBoxes; i++)
	{
		pBox=(pPlat->pBoxes)+i;

		if(i==0)
		{
			min.vx=max.vx=pBox->pX;
			min.vy=max.vy=pBox->pY;
			min.vz=max.vz=pBox->pZ;
		}

		if((pBox->pX)<min.vx)
			min.vx=pBox->pX;
		if((pBox->pX+pBox->sX)>max.vx)
			max.vx=pBox->pX+pBox->sX;

		if((pBox->pY)<min.vy)
			min.vy=pBox->pY;
		if((pBox->pY+pBox->sY)>max.vy)
			max.vy=pBox->pY+pBox->sY;

		if((pBox->pZ)<min.vz)
			min.vz=pBox->pZ;
		if((pBox->pZ+pBox->sZ)>max.vz)
			max.vz=pBox->pZ+pBox->sZ;

		PLAT_DB("Box: x=%d,%d, y=%d,%d, ", pBox->pX, pBox->pX+pBox->sX, pBox->pY, pBox->pY+pBox->sY);
		PLAT_DB("z=%d,%d\n", pBox->pZ, pBox->pZ+pBox->sZ);
	}

	PLAT_DB("Plat %p, x=%d,%d, ", pPlat, min.vx, max.vx);
	PLAT_DB("y=%d,%d, ", min.vy, max.vy);
	PLAT_DB("z=%d,%d\n", min.vz, max.vz);

	mid.vx=(max.vx+min.vx)/2;
	mid.vy=(max.vy+min.vy)/2;
	mid.vz=(max.vz+min.vz)/2;

	for(i=0; i<nBoxes; i++)
	{
		pBox=(pPlat->pBoxes)+i;

		pBox->pX-=mid.vx;
		pBox->pY-=mid.vy;
		pBox->pZ-=mid.vz;
	}
}
*/

/*
DYNCOLLBOX *platformLoad(ULONG crc)
{
	int i,j;
	char sStr[64];
	//ULONG crc=str2CRC(sPlatName);
	DYNCOLLBOX *pBox;
	ULONG *pColl=NULL;
	NEWMODEL *pPSA;

	i=platformTryAndFindInstance(crc);

	if(i&0x80000000) // not already loaded
	{
		i&=0x7fffffff; // get rid of top bit

		modelctrl.halfgouraud=TRUE;

		//sprintf(sStr, "%s.PSA", sPlatName);
		//sprintf(sStr,"WORLDS\\%s\\PLAT\\%s.PSA",WorldDirectoryNames[world], sPlatName);
		//pPSA=LoadPSA(sStr);

		PLAT_DB("Trying to load %x\n", crc);
		pPSA=BFF_IsCRCModelLoaded(crc, NULL);
		
		if(pPSA)
		{
			pColl=(ULONG *)collisionLoadMeshCRC(crc);

			pPlatsLoaded[i].pModel=pPSA;
			pPlatsLoaded[i].pFragModel=NULL;
			pPlatsLoaded[i].pBoxes=(COLLBOX *)pColl;// REMEMBER, when freeing, free this pointer-4 bytes
			pPlatsLoaded[i].crc=crc;

			if(pColl)
			{
				pPlatsLoaded[i].nCollBoxes=pColl[-1];
				//platformCentrePlat(pPlatsLoaded+i);

				for(j=0; j<pPlatsLoaded[i].nCollBoxes; j++)
					pPlatsLoaded[i].pBoxes[j].flags|=MOVE; // this signifies the platform can move
			}
			//else
			//{
			//	platformLinkDynamicBox(pBox, pPSA);
			//}
		}
		else
			return(0);
	}

	pBox=platformCreateDynamicBox();
	pBox->platNumber=i;

	if(pBox->pPlatDef->head.pendulum.angle)
	{
		if(pPlatsLoaded[i].pBoxes)
		{
			PLAT_DB("ERROR: A rotating platform cannot have a predefined collision mesh!\n");
			CRASH;
		}
		else
		{
			platformLinkDynamicBox(pBox, pPlatsLoaded[i].pModel, 1);
		}
	}
	else
	{
		if(pPlatsLoaded[i].pBoxes)
		{
			pBox->pBoxes=pPlatsLoaded[i].pBoxes; // this can be changed for ramps
			pBox->nCollBoxes=pPlatsLoaded[i].nCollBoxes;
		}
		else
		{
			platformLinkDynamicBox(pBox, pPlatsLoaded[i].pModel, 0);
		}
	}

	return(pBox);
}
*/

void platformVerify(void)
{
	DYNCOLLBOX *pPlat;
	int c=0;

	for(pPlat = dyncollList.head.next; pPlat != &dyncollList.head; pPlat = pPlat->next)
	{
		ASSERT(pPlat);
		c++;
	}

	ASSERT(c==dyncollList.numEntries);
}


ULONG platformTryAndFindInstance(ULONG crc) // if already loaded, bit 31 == 0
{
	int i;

	PLAT_DB("Finding Instance ...\n");
	for(i=0; i<nPlatsLoaded; i++)
	{
		if(i==MAXPLATSLOADED)
		{

			PLAT_DB("ERROR: Too many platforms loaded! (Increase MAXPLATSLOADED)\n");
			CRASH;
		}

		if(crc==pPlatsLoaded[i].crc)
		{
			PLAT_DB("Found one. Its number is %d\n", i);
			return(i);
		}
	}

	nPlatsLoaded++;
	PLAT_DB("Not found. Allocating number %d\n", i);

	return(i|0x80000000);
}

/*void platformLoadFragmentBodge(void)
{
//	if(!pBodgeFragment)
//	{
//		modelctrl.halfgouraud=TRUE;
//		pBodgeFragment=LoadPSA("WORLDS\\GENERIC\\NME\\AT1CRFR.PSA");
//	}
	pBodgeFragment = BFF_IsModelLoaded("AT1CRFR",NULL);	// bff-only version of LoadPSA - at1crfr is in generic.bff
	ASSERT(pBodgeFragment);
}*/

// the one and only dummy header
DYNCOLLBOXLIST dyncollList;

void platformInitialise(void) // initialises platform system (also used for resetting)
{
	DYNCOLLBOX *ptr;
	NEWMODEL *pObj;
	static int calledB4=0;
	int i;

	PLAT_DB("Initialising platform list ...");

	if(calledB4)
	{
		DYNCOLLBOX *next;

		for(ptr = dyncollList.head.next; ptr != &dyncollList.head; ptr = next)
		{
			next = ptr->next;
			// free all the stuff
			pObj=pPlatsLoaded[ptr->platNumber].pModel;
			if(pObj)
				objectFree(pObj, NULL);

			// seesaw type plats
			if(ptr->pBoxes[0].rampheader)
				FREE(ptr->pBoxes[0].rampheader);

			FREE(((ULONG *)(pPlatsLoaded[ptr->platNumber].pModel))-1);

			ptr->prev->next = ptr->next;
			ptr->next->prev = ptr->prev;

			FREE(ptr);	// Freed by Fred

			dyncollList.numEntries--;
		}

		for(i = 0; i < nPlatsLoaded;i++)
		{
			if(pPlatsLoaded[i].pBoxes)
				FREE(pPlatsLoaded[i].pBoxes);	// Freed by Fred
		}
	}

	if(dyncollList.numEntries)
	{
		PLAT_DB("WARNING: Number of platform entries appears to be non zero!\n");
		dyncollList.numEntries=0;
	}

	nPlatsLoaded=0;
	nAutoBoxes=0;

	platformCreateDynamicBoxList();
	calledB4++;
}

void platformCreateDynamicBoxList(void)
{
	dyncollList.head.next = dyncollList.head.prev = &dyncollList.head;
	dyncollList.numEntries = 0;
}

DYNCOLLBOX *platformCreateDynamicBox(int atFront)
{
	DYNCOLLBOX *ptr;
	DYNCOLLBOX *newBox=(DYNCOLLBOX *)MALLOC(sizeof(DYNCOLLBOX), "Dynamic Coll Box");

	ptr = atFront?dyncollList.head.next:dyncollList.head.prev;

	memset(newBox, 0, sizeof(DYNCOLLBOX));

	newBox->prev = ptr;
	newBox->next = ptr->next;
	ptr->next->prev = newBox;
	ptr->next = newBox;

	dyncollList.numEntries++;
	
	return(newBox);
}

int platformGetRadiusSqr(NEWMODEL *pModel)
{
	int i;
	VERT *pVerts=pModel->world.meshdata->vertop;
	int nVerts=pModel->world.meshdata->vern;
	int radius, result=0;

	for(i=0; i<nVerts; i++)
	{
		radius=((pVerts[i].vx*pVerts[i].vx)+(pVerts[i].vy*pVerts[i].vy)+(pVerts[i].vz*pVerts[i].vz));

		if(radius>result)
			result=radius;
	}

	return (result/4096);
}

void platformCalcRampNormal(COLLBOX *pB, ULONG f)
{
	VECTOR v1,v2;
	VECTOR vrt[3];
	VECTOR n,a;
	int c,vp;

	RAMPHEADER *pR=pB->rampheader;
	VECTOR *pVerts=pR->pVerts;

	for(c=0; c<3; c++)
	{
		vp=f&(0x0f);

		f=f>>4;
		vrt[c].vx=pVerts[vp].vx/4096;
		vrt[c].vy=pVerts[vp].vy/4096;
		vrt[c].vz=pVerts[vp].vz/4096;
	}
 
	// get face normal
 	SUBVECTOR(&v1, vrt, vrt+1);
	SUBVECTOR(&v2, vrt, vrt+2);
	n.vx=(v1.vy * v2.vz - v1.vz * v2.vy);
	n.vy=(v1.vz * v2.vx - v1.vx * v2.vz);
	n.vz=(v1.vx * v2.vy - v1.vy * v2.vx);
	MakeUnit(&n);

	pR->normal_x=n.vx;
	pR->normal_y=n.vy;
	pR->normal_z=n.vz;

	a.vx = vrt->vx + (pB->pX)/4096;
	a.vy = vrt->vy + (pB->pY)/4096;
	a.vz = vrt->vz + (pB->pZ)/4096;

	pR->plane=-DotProduct(&n, &a);
}


#define MOVEDOWN (32*4096)

void platformLinkDynamicBox(COLLBOX **ppBoxes, NEWMODEL *pModel, int rampFlag)
{// we need to count through verts and set the limits of
 // the collision box to the max. size of the model
	VECTOR min, max;
	int i;
	VERT *pVerts=pModel->world.meshdata->vertop;
	int nVerts=pModel->world.meshdata->vern;
	//PLATSLOADED *pBox=pPlatsLoaded+(pDynBox->platNumber);
	//int rampFlag=flags&LINKFLAGS_RAMP;

	PLAT_DB("Linking collision box with %d vertices\n", nVerts);

	min.vx=max.vx=pVerts->vx;
	min.vy=max.vy=pVerts->vy;
	min.vz=max.vz=pVerts->vz;

	for(i=1; i<nVerts; i++)
	{
		if(pVerts[i].vx>max.vx) max.vx=pVerts[i].vx;
		else if(pVerts[i].vx<min.vx) min.vx=pVerts[i].vx;

		if(pVerts[i].vy>max.vy) max.vy=pVerts[i].vy;
		else if(pVerts[i].vy<min.vy) min.vy=pVerts[i].vy;

		if(pVerts[i].vz>max.vz) max.vz=pVerts[i].vz;
		else if(pVerts[i].vz<min.vz) min.vz=pVerts[i].vz;
	}

	if(nAutoBoxes==MAX_AUTOBOXES)
	{
		PLAT_DB("ERROR: Too many AUTOBOXES\n");
		CRASH;
	}

	(*ppBoxes)=pAutoBoxes+(nAutoBoxes++);

	(*ppBoxes)->sX=4096*(max.vx-min.vx);
	(*ppBoxes)->sY=-(4096*(max.vy-min.vy));
	(*ppBoxes)->sZ=4096*(max.vz-min.vz);

	//(*ppBoxes)->pX=-((*ppBoxes)->sX/2);
	//(*ppBoxes)->pY=-((*ppBoxes)->sY/2);
	//(*ppBoxes)->pZ=-((*ppBoxes)->sZ/2);


	(*ppBoxes)->pX=min.vx*4096;
	(*ppBoxes)->pY=(max.vy*4096);
	(*ppBoxes)->pZ=min.vz*4096;

	PLAT_DB("Size=(%d,%d,%d)\n", (*ppBoxes)->sX, (*ppBoxes)->sY, (*ppBoxes)->sZ);
	PLAT_DB("Min= (%d,%d,%d)\n", min.vx, min.vy, min.vz);

	if(rampFlag)
	{
		VECTOR *pV;

		//RAMPHEADER *pM=(*ppBoxes)->rampheader=MALLOC(1024, "Dynamic Ramp");
		RAMPHEADER *pM=(*ppBoxes)->rampheader=MALLOC(sizeof(RAMPHEADER)+(sizeof(ULONG)*14)+(sizeof(VECTOR)*8), "Dynamic Ramp");

		pM->nVerts=8;		// no of verts
		pM->nFaces=10;		// no of faces
							// ptr to face data (straight after RAMPHEADER)
		pM->pFaces=(ULONG *)(((int)pM)+sizeof(RAMPHEADER));
		pM->pVerts=(VECTOR *)(pM->pFaces+13);	// we shall fill this in later

		pM->pFaces[0]=0x021000;		// first face
		pM->pFaces[1]=0x032000;		// second face

		// flags: bit 0:   0=tri, 1=quad
		//        bit 1&2: 00=arbitrary poly, 01=XY plane, 10=XZ plane, 11=YZ plane

		pM->pFaces[2]=0x015406;	// XY 'back' tris
		pM->pFaces[3]=0x014006;

		pM->pFaces[4]=0x026502;	// YZ 'right' tris
		pM->pFaces[5]=0x025102;

		pM->pFaces[6]=0x037606;	// XY 'front' tris
		pM->pFaces[7]=0x023606;

		pM->pFaces[8]=0x004702;	// YZ 'left' tris
		pM->pFaces[9]=0x007302;


		pV=pM->pVerts;

		// Build vertex table ...  Top Ramp Face
		for(i=0; i<4; i++)
		{
			pV[i].vy=((*ppBoxes)->sY);//+4096;
			pV[i].vz=pV[i].vx=0;
		}

		pM->pFaces[12]=(*ppBoxes)->sY;	// store thickness

		pV[1].vz+=(*ppBoxes)->sZ;

		pV[2].vx+=(*ppBoxes)->sX;
		pV[2].vz+=(*ppBoxes)->sZ;

		pV[3].vx+=(*ppBoxes)->sX;

		for(i=4; i<8; i++)
		{
			pV[i].vz=pV[i].vy=pV[i].vx=0;
		}

		pV[5].vz+=(*ppBoxes)->sZ;

		pV[6].vx+=(*ppBoxes)->sX;
		pV[6].vz+=(*ppBoxes)->sZ;

		pV[7].vx+=(*ppBoxes)->sX;


		(*ppBoxes)->flags=RAMP;
		//(*ppBoxes)->sY=(*ppBoxes)->sY*8; // Dodgy bodge alert!
		(*ppBoxes)->sY=-64*4096;

		for(i=0; i<4; i++) // move only the top verts.
		{
			pV[i].vy-=MOVEDOWN;
		}

		pM->pFaces[11]=pV[0].vy; // store flat level of plane so we can tilt stuff properly
		
		(*ppBoxes)->sY-=MOVEDOWN;
		(*ppBoxes)->pY+=MOVEDOWN;

		platformCalcRampNormal(*ppBoxes, pM->pFaces[0]); // [1] would also work

	}
	else
	{
		(*ppBoxes)->rampheader=NULL;
		(*ppBoxes)->flags=BOX;
	}

	//(*ppBoxes)->sY=(*ppBoxes)->sY*2;

	(*ppBoxes)->flags|=MOVE;
}


void platformSetDynamicBox(DYNCOLLBOX *pBox, int x, int y, int z)
{
	pBox->pos.vx=x;
	pBox->pos.vy=y;
	pBox->pos.vz=z;

//	pBox->platOffs.vx=x;
//	pBox->platOffs.vy=y;
//	pBox->platOffs.vz=z;

	COPYVECTOR(&(pBox->oldPos), &(pBox->pos));
}

void platformMoveDynamicBox(DYNCOLLBOX *pBox, int x, int y, int z)
{
	if(pBox->move.moving)
	{
		COPYVECTOR(&(pBox->oldPos), &(pBox->pos));

		pBox->pos.vx=x;
		pBox->pos.vy=y;
		pBox->pos.vz=z;
	}
	else
	{
		platformSetDynamicBox(pBox, x,y,z);
	}
}

// Can be used for snowball and stuff like that as well.
void platformGenerateShrapnel(VECTOR *pPos, int nBits, int radius, NEWMODEL *pModel, NEWMODEL *pFragModel)
{
	int i;
	
	int sx,sy,sz;

	ASSERT(pFragModel);

	// Too many fragments
	ASSERT(nBits<=MAX_OBJDEBRIS);

	if(pModel)
	{
		sx=pModel->world.meshdata->boundmaxx-pModel->world.meshdata->boundminx;
		sy=pModel->world.meshdata->boundmaxy-pModel->world.meshdata->boundminy;
		sz=pModel->world.meshdata->boundmaxz-pModel->world.meshdata->boundminz;
	}
	else
	{
		sx = sy = sz = 1;
	}

	objDebris.nBits=nBits;
	objDebris.pModel=pFragModel;

	for(i=0; i<objDebris.nBits; i++)
	{
		PLAT_DB("Generating bit %d\n", i);
		objDebris.pos[i].vx=(pPos->vx/4096);
		objDebris.pos[i].vy=(pPos->vy/4096);
		objDebris.pos[i].vz=(pPos->vz/4096);

		objDebris.vel[i].vx=8-(RANDOM256()&15);
		objDebris.vel[i].vy=8-(RANDOM256()&15);
		objDebris.vel[i].vz=8-(RANDOM256()&15);

		if(radius)
		{
			objDebris.pos[i].vx+=random(radius*2)-radius;
			objDebris.pos[i].vy+=random(radius*2)-radius;
			objDebris.pos[i].vz+=random(radius*2)-radius;
		}
		else
		{
			ASSERT(pModel);

			objDebris.pos[i].vx+=random(sx)-(sx/2);
			objDebris.pos[i].vy+=random(sy)-(sy/2);
			objDebris.pos[i].vz+=random(sz)-(sz/2);
		}
	}

	objDebris.life=1;
}

void platformDrawShrapnel(void)
{
	if(objDebris.life==0)
		return;

	if(objDebris.life<30)
	{
		int i;
		NEWMODEL *pObj=objDebris.pModel;
		 
		//pObj=pPlatsLoaded[ptr->platNumber].pFragModel;

		// not active, and has been dead for ptr->active frames ...
		if(pObj)
		{
			for(i=0; i<objDebris.nBits; i++)
			{
				ADDTOVECTOR(objDebris.pos+i, objDebris.vel+i);
				objDebris.vel[i].vy+=4;

				COPYVECTOR(&pObj->position, objDebris.pos+i); 
//				PLAT_DB("Drawing bit at %d,%d,%d\n", objDebris.pos[i].vx, objDebris.pos[i].vy, objDebris.pos[i].vz);
				//objDebris.pos[i].vy+=(objDebris.life<<2);

				pObj->world.rotate.vx=random(0xfff);
				pObj->world.rotate.vy=random(0xfff);
				pObj->world.rotate.vz=random(0xfff);

				pObj->world.scale.vx=256;
				pObj->world.scale.vy=256;
				pObj->world.scale.vz=256;

				lscapeDrawClipped(pObj, &GsWSMATRIX, LSCAPE_DRAWMODEL|LSCAPE_ROTATEMODEL);
			}
		}

		objDebris.life++;
	}
	else
		objDebris.life=0;	// kill it again ...
}

void platformSmash(DYNCOLLBOX *pBox)
{
	PLAT_DB("Hulk Smash!!!\n");
	pBox->active=0;

	pBox->deadTimer=1;

	platformGenerateShrapnel(&(pBox->cumPos),
							pBox->pPlatDef->head.destruct.nBits,
							0,/*fast_sqrt(pPlatsLoaded[pBox->platNumber].collRadiusSqr)>>16,*/
							pPlatsLoaded[pBox->platNumber].pModel,
							pPlatsLoaded[pBox->platNumber].pFragModel);

	if(pBox->pPlatDef->head.dest_sfx)
	{
		sfxPlayParsed(
			&(pBox->pos),
			pBox->pPlatDef->head.dest_sfx,
			pBox->pPlatDef->head.dest_pitch,
			pBox->pPlatDef->head.dest_vol
			);
	}


}

void platformCrumble(DYNCOLLBOX *pBox)
{
	PLAT_DB("Crumble\n");

	if(pBox->deadTimer==0)
	{
		pBox->deadTimer=1;
		if(pBox->pPlatDef->head.dest_sfx)
		{
			sfxPlayParsed(
				&(pBox->pos),
				pBox->pPlatDef->head.dest_sfx,
				pBox->pPlatDef->head.dest_pitch,
				pBox->pPlatDef->head.dest_vol
				);
		}
	}
}


void platformTilt(DYNCOLLBOX *ptr, int angle, int axis)
{
	int tilt;

	VECTOR *pV=ptr->pBoxes->rampheader->pVerts;
	long level=		ptr->pBoxes->rampheader->pFaces[11];
	long thickness=	ptr->pBoxes->rampheader->pFaces[12];

	
	switch(axis)
	{
		case X_PLATAXIS:
			// now actually tilt the thing ...

			// These two lines are a bug I didn't find for a long, long time
			//tilt=((rsin(angle)*4096)/rcos(angle))*((ptr->pBoxes->sX)/2);
			//tilt/=2048;

			tilt=((rsin(angle)*4096)/rcos(angle))*((ptr->pBoxes->sZ)/2);
			tilt/=4096;
			
			pV[1].vy=pV[2].vy=level-tilt;
			pV[0].vy=pV[3].vy=level+tilt;

		   	pV[5].vy=pV[6].vy=pV[1].vy-thickness;
			pV[4].vy=pV[7].vy=pV[0].vy-thickness;

			break;

		case Z_PLATAXIS:
			tilt=((rsin(angle)*4096)/rcos(angle))*((ptr->pBoxes->sX)/2);
			//tilt/=2048;
			tilt/=4096;

			pV[0].vy=pV[1].vy=level-tilt;
			pV[2].vy=pV[3].vy=level+tilt;

			pV[4].vy=pV[5].vy=pV[0].vy-thickness;
			pV[6].vy=pV[7].vy=pV[2].vy-thickness;

			break;

		default:
			PLAT_DB("Invalid Axis!\n");
/*			tilt=((rsin(angle)*4096)/rcos(angle))*((ptr->pBoxes->sZ)/2);
			tilt/=2048;

			//pV[0].vy=pV[1].vy=((ptr->pBoxes->sY)/8)-tilt;
			//pV[2].vy=pV[3].vy=((ptr->pBoxes->sY)/8)+tilt;

			pV[0].vy=pV[1].vy=level-tilt;
			pV[2].vy=pV[3].vy=level+tilt;

			pV[4].vy=pV[5].vy=pV[0].vy-thickness;
			pV[6].vy=pV[7].vy=pV[2].vy-thickness;

			break;
*/
//			CRASH;
	}

	platformCalcRampNormal(ptr->pBoxes, ptr->pBoxes->rampheader->pFaces[0]);
}

void platformRotateModel(COLLDATA *pModel, DYNCOLLBOX *pCPlat)
{
	int nx,ny,nz, ox,oy,oz;
	DYNCOLLBOX *pCollBox=(DYNCOLLBOX *)(pModel->hitPlats[0].pPlatform);

	if(pCollBox!=pCPlat)
		return;

	// OK, this is the one ...

	if(pCollBox->pPlatDef->head.spin.yes)
	{
		int theta;
			
		// now to rotate the ball/glove around it ...
		// assume y axis for now ...

		theta=pCollBox->pPlatDef->head.spin.rotspeed;

		//PLAT_DB("theta=%d\n", theta);

		if(pModel->nHitPlats)
		{
			switch(pCollBox->pPlatDef->head.spin.axis)
			{
			case Y_PLATAXIS:
				ox=((pModel->pPos->vx)-(pCollBox->pos.vx))/64;
				oz=((pModel->pPos->vz)-(pCollBox->pos.vz))/64;

				nx=((oz*rsin(theta)))+((ox*rcos(theta)));
				nz=((oz*rcos(theta)))-((ox*rsin(theta)));

				nx/=64;
				nz/=64;

				nx+=(pCollBox->pos.vx);
				nz+=(pCollBox->pos.vz);

				pModel->pPos->vx=nx;
				pModel->pPos->vz=nz;

				pModel->oldPos.vx=nx;
				pModel->oldPos.vz=nz;

  				if(pModel==&ballColl)
				{
					IQUATERNION tempQ;

					// rotate ball
					GetQuaternionFromRotation(&tempQ,&pBallPSA->world.qRotVel);
					QuaternionMultiply(&pBallPSA->world.qRot,&pBallPSA->world.qRot,&tempQ);

					GetQuaternionFromRotation(&tempQ,&pBallPSA->world.qRotLightVel);
					QuaternionMultiply(&pBallPSA->world.qRotLight,&pBallPSA->world.qRotLight,&tempQ);
					ActorRotateByAngle(pBallPSA, -theta);
				}

				if((pModel==&gloveColl) || GloveCtrl.handOnBall || GloveCtrl.ballWithHand)
				{
					if((pModel==&gloveColl) && (GloveCtrl.handOnBall || GloveCtrl.ballWithHand))
					{
						//PLAT_DB("ERK! Something's gone horribly wrong!\n");
						// The glove is holding the ball and we're going to mone the ball around
					}
					else
					{
						GloveCtrl.direction+=theta;
						GloveCtrl.direction&=4095;
					}
				}
				break;

			case X_PLATAXIS:
				PLAT_DB("X_PLATAXIS\n");

				/*ox=((pModel->pPos->vx)-(pCollBox->pos.vx))/64;
				oy=((pModel->pPos->vy)-(pCollBox->pos.vy))/64;

				nz=(oy*rsin(4096-theta))+(oz*rcos(4096-theta));

				nz/=64;

				PLAT_DB("nz = %d\n", nz);

				nz+=(pCollBox->pos.vz);
				pModel->pPos->vz=nz;
				pModel->oldPos.vz=nz;
				*/

				pModel->pPos->vz-=theta*512;
				break;

			case Z_PLATAXIS:
				PLAT_DB("Z_PLATAXIS\n");

				/*
				ox=((pModel->pPos->vx)-(pCollBox->pos.vx))/64;
				oy=((pModel->pPos->vy)-(pCollBox->pos.vy))/64;

				nx=(oy*rsin(4096-theta))+(ox*rcos(4096-theta));

				nx/=64;

				nx+=(pCollBox->pos.vx);
				pModel->pPos->vx=nx;
				pModel->oldPos.vx=nx;
				*/

				pModel->pPos->vx+=theta*512;
				break;

			default:
				ASSERT(0);
				CRASH;
			}
		}

		pCollBox->spin.timer++;
	}
}

void platformDoSeesaw(DYNCOLLBOX *ptr, COLLDATA *pColl)
{
	if(ptr->pPlatDef->head.seesaw.yes)
	{
		ptr->seesaw.timer=0;
		switch(ptr->pPlatDef->head.seesaw.axis)
		{
			case 0:
				ptr->seesaw.dist=(((DYNCOLLBOX *)(pColl->hitPlats[0].pPlatform))->pos.vz)-(pColl->pPos->vz);
				break;
			case 2:
				ptr->seesaw.dist=(((DYNCOLLBOX *)(pColl->hitPlats[0].pPlatform))->pos.vx)-(pColl->pPos->vx);
				break;
			default:
				ASSERT(0); // Bad axis

		}
	}
}

void platformStartTopple(DYNCOLLBOX *ptr)
{
	// I guess this is where we topple stuff
	if((ptr->pPlatDef->head.topple.yes) && (ptr->topple.falling==0))
	{
		ptr->topple.falling=1;
		ptr->topple.progress=0;
		ptr->moveticker = 0;
	}
}

void platformProcess(DYNCOLLBOX *ptr)
{
		int wait=ptr->pPlatDef->points[ptr->move.point].wait;
		int nPoints=ptr->pPlatDef->head.n_points;
		int maxSpeed=ptr->pPlatDef->head.maxspeed;

		//int dist=0;

		ptr->move.jap=0;// reset 'just at point' flag

		if(!(ptr->active))
			return;

		ptr->topple.jar=0;

		ptr->move.moving=FALSE;

		if((ptr->pPlatDef->head.magnet.yes) && (ptr->magnet.active) && (BallCtrl.type==BALL_MODE_BEARING))
		{
			VECTOR btm;
			int m;

			//PLAT_DB("Platform %p is magnetic\n", ptr);

			SUBVECTOR(&btm, &(ptr->pos), &ballPos);

		  	m=Magnitude(&btm)>>12;
			MakeUnit(&btm);

			//PLAT_DB("m=%d\n", m);

			if(m<MAGNET_RANGE)
			{
				btm.vx*=2;
				btm.vy=-(btm.vy*2);
				btm.vz*=2;

				//PLAT_DB("ball-to-magnet = (%d, %d, %d)\n", btm.vx, btm.vy, btm.vz);
			 
				ADDTOVECTOR(&ballVel, &btm);
			}

			//if((ballColl.nHitPlats>0) && ((DYNCOLLBOX *)&(ballColl.hitPlats[0].pPlatform))->pPlatDef->head.magnet.yes)
			if(ballColl.nHitPlats>0)
			{
				DYNCOLLBOX *p=(DYNCOLLBOX *)(ballColl.hitPlats[0].pPlatform);
				if(p==ptr)
				{
					ballVel.vy=0;
				}
			}
		}

		if(gloveColl.nHitPlats && (ptr==gloveColl.hitPlats[0].pPlatform))
		{
// A bodge to stop you jumping between the cable cars while they're moving,
// grabbing the ball, and thus falling out of the back to your doom.
			if(level == CARNIVAL2 && ptr->pPlatDef->head.tag == 29
				&& (ptr->move.waitTimer==0) && (ptr->move.pointCtr!=0)
				)
			{
//				DB("ca2 cablecar bodge\n");
				GloveCtrl.padDisableTimer = 2;
			}
			
			ptr->handTouchTime++;
			if(ptr->pPlatDef->head.flags&PLATFLAG_CRUMBLE)
				platformCrumble(ptr);

			if(ptr->pPlatDef->head.seesaw.yes)
				platformDoSeesaw(ptr, &gloveColl);

			platformStartTopple(ptr);

			// test for push
			if((GloveCtrl.action==HAND_PUSH) && (ptr->pPlatDef->head.confine.yes))
			{
				//VECTOR pushVec;
				int pushX, pushZ, dirX, dirZ;

				int pX=(ptr->pPlatDef->head.confine.pX)*4096;
				int pZ=(ptr->pPlatDef->head.confine.pZ)*4096;

				int aX=(ptr->pPlatDef->head.confine.aX)*4096;
				int aZ=(ptr->pPlatDef->head.confine.aZ)*4096;

				GloveCtrl.pushingPlatform = 2;

				if((ptr->pPlatDef->head.confine.mass==0) || (handpower_type == SPELL_HERCULES))
				{
					//VECTOR platMoved;
					char moved = TRUE;

					//PLAT_DB("Pushing !!!\n");
					//PLAT_DB("pX=%d, pZ=%d, aX=%d, aZ=%d\n", pX,pZ, aX,aZ);
					//PLAT_DB("xPos=%d, zPos=%d\n", (ptr->cb.pX), (ptr->cb.pZ));

					pushX=(ptr->pos.vx);
					pushZ=(ptr->pos.vz);

					dirX=dirZ=0;

					if(aX<pX)
					{
						dirX=rsin(GloveCtrl.direction);
						pushX=(ptr->pos.vx)-dirX;

						gloveColl.pPos->vx-=dirX;

						if(pushX>pX)
						{
							ptr->move.jap=TRUE;
							pushX=pX;
							moved = FALSE;
						}
						if(pushX<aX)
						{
							ptr->move.jap=TRUE;
							pushX=aX;
							moved = FALSE;
						}
					}

					if(aZ<pZ)
					{
						dirZ=rcos(GloveCtrl.direction);
						pushZ=(ptr->pos.vz)-dirZ;
					
						gloveColl.pPos->vz-=dirZ;

						if(pushZ>pZ)
						{
							pushZ=pZ;
							moved = FALSE;
						}

						if(pushZ<aZ)
						{
							pushZ=aZ;
							moved = FALSE;
						}
					}

					{
						if(ptr->handTouchTime==1)
							ptr->moveticker = 0;
						if(!(ptr->moveticker % 8) && moved)
						{
//							DB("move sfx by push tag %d\n",ptr->pPlatDef->head.tag);
							if(ptr->pPlatDef->head.move_sfx)
							{
								sfxPlayParsed(
									&(ptr->pos),
									ptr->pPlatDef->head.move_sfx,
									ptr->pPlatDef->head.move_pitch,
									ptr->pPlatDef->head.move_vol
									);
							}
						}
						ptr->moveticker++;
					}

					platformMoveDynamicBox(ptr, pushX, ptr->pos.vy, pushZ);
					/*SUBVECTOR(&platMoved, &(ptr->pos), &(ptr->oldPos));

					if(ballColl.nHitPlats && (ballColl.hitPlats[0].pPlatform==((void *)ptr)))
					{
						ADDTOVECTOR(&ballPos, &platMoved);
						ADDTOVECTOR(&JoinBallPos, &platMoved);
						ADDTOVECTOR(&ballColl.oldPos, &platMoved);
					}*/

				}

			}
		}
		else
		{
			ptr->handTouchTime=0;
		}

		if(ptr->seesaw.timer>4)
		{
			ptr->seesaw.dist=0;
			if(ptr->pPlatDef->head.seesaw.yes)
				ptr->moveticker = 0;
		}

		// check for hits and stuff. I should probably move this
		if(ballColl.nHitPlats)
		{

			//PLAT_DB("Ball has hit plat (%x)\n", ptr);
			if(ptr==ballColl.hitPlats[0].pPlatform)
			{
				ptr->ballTouchCounter=0;

				if(BallCtrl.powerwhack)
					platformStartTopple(ptr);

				//PLAT_DB("It was THIS platform!\n");
				ptr->ballTouchTime++;
				if(ptr->pPlatDef->head.flags&PLATFLAG_CRUMBLE)
				{
					platformCrumble(ptr);
				}
				else
				{
					if(wait==WAITFORBALLHIT)
						ptr->move.waitTimer=0; // make it happen!

//					if((BallCtrl.type==BALL_MODE_BOWLING)&&((ptr->pPlatDef->head.flags)&PLATFLAG_DESTRUCT_BALL))
					if((ptr->pPlatDef->head.flags) & (PLATFLAG_DESTRUCTIBLE | PLATFLAG_DESTRUCT_BALL))
					{
						//if(Magnitude(&ballVel)>BALLSMASHSPEED)
//						PLAT_DB("platform hit:- powerwhack=%d\n", BallCtrl.powerwhack);
						if(BallCtrl.powerwhack)
						{
							platformSmash(ptr);
						}
					}
/*
					else
					{
						PLAT_DB("Wasn't a destructible\n");
					}
*/
				}

				// work out how far we are from the centre of the platform,
				// so we know how much to tilt it by generating a moment of torque.
				
				if(ptr->pPlatDef->head.seesaw.yes)
					platformDoSeesaw(ptr, &ballColl);

				if(ptr->pPlatDef->head.flags & PLATFLAG_EXITDOOR)
				{
// problem - we want to know that we're *ON* the exit platform, not merely touching it

					VECTOR diff;
					if(GloveCtrl.ballWithHand && !GloveCtrl.disableTimer)
					{
						SUBVECTOR(&diff,&ballPos, &ptr->pos);
						if(     (ULONG)(diff.vx+4096*20) < (ULONG)(4096 * 40)
							&&  (ULONG)(diff.vz+4096*20) < (ULONG)(4096 * 40)
							&&  diff.vy < 0
							)
						{
							ptr->move.waitTimer=0;
							ptr->move.pointCtr=1;
							ptr->snatch_ball_time = 200;
							GloveCtrl.disableTimer = 2000;
						}
					}
				}
			}
		}
		else
		{
			ptr->ballTouchTime=0;
			ptr->ballTouchCounter++;
		}


		if(ptr->pPlatDef->head.flags & PLATFLAG_EXITDOOR)
		{
			VECTOR pos;
			VECTOR delta = {0,-10*4096,0};
			ADDVECTOR(&pos,&ptr->pos, &delta);
			New_PortalDebris(&pos);
			if(ptr->snatch_ball_time)
			{
				ptr->snatch_ball_time--;


				VectorMoveTo2D(&ballPos, &ptr->pos, 4096);	// Make sure the ball sticks to the platform
				ballPos.vy = ptr->pos.vy-4096 * 10;
				ballPlaceAt(&ballPos);


				if(!ptr->snatch_ball_time)
				{
					NextLevelBasics();	// Correct hub, with ball, with "level finished" flags
					gameCtrl.dropOutFlag=GAME_COMPLETED;
				}
			}
		}

		if(ptr->pPlatDef->head.spin.yes)
		{
			if(ptr->pPlatDef->head.spinpause.yes)
			{
				int sector1, sector2;
				int gotThere=0;
				ptr->spinpause.jar=0;

				if(ptr->spinpause.timer<32767)
					ptr->spinpause.timer--;

				if(ptr->spinpause.timer<=0)
				{
					sector1=ptr->spin.progress/ptr->pPlatDef->head.spinpause.angle;
					sector2=(ptr->spin.progress+ptr->pPlatDef->head.spin.rotspeed)/ptr->pPlatDef->head.spinpause.angle;
					
					//sector1=(ptr->spin.progress+ptr->pPlatDef->head.spin.rotspeed)/ptr->pPlatDef->head.spinpause.angle;
					//sector2=(ptr->spin.progress+(ptr->pPlatDef->head.spin.rotspeed*2))/ptr->pPlatDef->head.spinpause.angle;

					if(ptr->pPlatDef->head.spin.rotspeed>0)
					{
						//PLAT_DB("Progress = %d, s1=%d, s2=%d\n", ptr->spin.progress, sector1, sector2);
						if(sector1<sector2) // will be passing into another 'sector' this time
						{
							// move it on to the next sector
							ptr->spin.progress=sector2*ptr->pPlatDef->head.spinpause.angle;
							ptr->spinpause.jar++;

							if(ptr->pPlatDef->head.spinpause.wait==WAITFOREVER)
								ptr->spinpause.timer=32767; // just keep it from ever being <0
							else
								ptr->spinpause.timer=ptr->pPlatDef->head.spinpause.wait;

							if(ptr->pPlatDef->head.stop_sfx)
							{
								sfxPlayParsed(
									&(ptr->pos),
									ptr->pPlatDef->head.stop_sfx,
									ptr->pPlatDef->head.stop_pitch,
									ptr->pPlatDef->head.stop_vol
									);
							}
							ptr->moveticker=0;
						}
						else
						{
							ptr->spin.progress+=ptr->pPlatDef->head.spin.rotspeed;

							if(sector1>sector2)
								ASSERT(0);
							if(!(ptr->moveticker % 8))
							{
//								DB("move sfx by spinpause tag %d\n",ptr->pPlatDef->head.tag);
								if(ptr->pPlatDef->head.move_sfx)
								{
									sfxPlayParsed(
										&(ptr->pos),
										ptr->pPlatDef->head.move_sfx,
										ptr->pPlatDef->head.move_pitch,
										ptr->pPlatDef->head.move_vol
										);
								}
							}
							ptr->moveticker++;

						}
					}
					else
					{
						if(ptr->pPlatDef->head.spin.rotspeed<0)
						{
							if(sector1>sector2) // will be passing into another 'sector' this time
							{
								// move it on to the next sector

								//DB("sector1 = %d, sector2 = %d\n", sector1, sector2);
								//DB("rotspeed = %d\n", ptr->pPlatDef->head.spin.rotspeed);
								//DB("progress = %d\n", ptr->spin.progress);

								ptr->spin.progress=sector1*ptr->pPlatDef->head.spinpause.angle;
								ptr->spinpause.jar++;

								//DB("progress = %d\n", ptr->spin.progress);

								if(ptr->pPlatDef->head.spinpause.wait==WAITFOREVER)
									ptr->spinpause.timer=32767; // just keep it from ever being <0
								else
									ptr->spinpause.timer=ptr->pPlatDef->head.spinpause.wait;

								if(ptr->pPlatDef->head.stop_sfx)
								{
									sfxPlayParsed(
										&(ptr->pos),
										ptr->pPlatDef->head.stop_sfx,
										ptr->pPlatDef->head.stop_pitch,
										ptr->pPlatDef->head.stop_vol
										);
								}
								ptr->moveticker=0;
							}
							else
							{
								ptr->spin.progress+=ptr->pPlatDef->head.spin.rotspeed;

								if(sector1<sector2)
									ASSERT(0);
								if(!(ptr->moveticker % 8))
								{
//									DB("move sfx by spinpause tag %d\n",ptr->pPlatDef->head.tag);
									if(ptr->pPlatDef->head.move_sfx)
									{
										sfxPlayParsed(
											&(ptr->pos),
											ptr->pPlatDef->head.move_sfx,
											ptr->pPlatDef->head.move_pitch,
											ptr->pPlatDef->head.move_vol
											);
									}
								}
								ptr->moveticker++;

							}
						}
					}
				}

/*				int gotThere=0;
				ptr->spinpause.jar=0;

				if(ptr->pPlatDef->head.spin.rotspeed>0)
				{
					if((ptr->spinpause.progress+ptr->pPlatDef->head.spin.rotspeed)>ptr->pPlatDef->head.spinpause.angle)
					{
						gotThere=TRUE;
						ptr->spin.progress+=(ptr->pPlatDef->head.spinpause.angle-ptr->spinpause.progress);
					}
				}
				else
				{
					if(ptr->pPlatDef->head.spin.rotspeed<0)
					{
						//if((ptr->spinpause.progress-ptr->pPlatDef->head.spin.rotspeed)<(-ptr->pPlatDef->head.spinpause.angle))
						if((ptr->spinpause.progress+ptr->pPlatDef->head.spin.rotspeed)<(-ptr->pPlatDef->head.spinpause.angle))
						{
							gotThere=TRUE;
							//ptr->spin.progress+=(ptr->pPlatDef->head.spinpause.angle+ptr->spinpause.progress);
							ptr->spin.progress+=(-ptr->pPlatDef->head.spinpause.angle-ptr->spinpause.progress);
						}
					}
				}

				if(gotThere)
				{
					//ptr->spin.progress+=(ptr->pPlatDef->head.spinpause.angle-ptr->spinpause.progress);

					ptr->spinpause.timer=ptr->pPlatDef->head.spinpause.wait;
					ptr->spinpause.progress=0;

					ptr->spinpause.jar++;

					if(ptr->pPlatDef->head.spinpause.wait==WAITFOREVER)
						ptr->spinpause.timer=32767; // just keep it from ever being <0
				}

				if(ptr->spinpause.timer<0)
				{
					ptr->spinpause.progress+=ptr->pPlatDef->head.spin.rotspeed;
					ptr->spin.progress+=ptr->pPlatDef->head.spin.rotspeed;
				}

				ptr->spinpause.timer--;
*/

				

			}
			else
			{
				if(ptr->pPlatDef->head.spinflip.yes)
				{
					if(ptr->spinflip.timer<0)
					{
						ptr->spinflip.progress+=ptr->spinflip.dir;

						if(!(ptr->moveticker % 8))
						{
//							DB("move sfx by spinflip tag %d\n",ptr->pPlatDef->head.tag);
							if(ptr->pPlatDef->head.move_sfx)
							{
								sfxPlayParsed(
									&(ptr->pos),
									ptr->pPlatDef->head.move_sfx,
									ptr->pPlatDef->head.move_pitch,
									ptr->pPlatDef->head.move_vol
									);
							}
						}
						ptr->moveticker++;

						if(ptr->spinflip.progress<=(-ptr->pPlatDef->head.spinflip.angle))
						{
							//ptr->spin.progress=-ptr->pPlatDef->head.spinflip.angle;
							if(ptr->pPlatDef->head.stop_sfx)
							{
								sfxPlayParsed(
									&(ptr->pos),
									ptr->pPlatDef->head.stop_sfx,
									ptr->pPlatDef->head.stop_pitch,
									ptr->pPlatDef->head.stop_vol
									);
							}
							ptr->moveticker = 0;

							ptr->spinflip.dir=ptr->pPlatDef->head.spin.rotspeed;

							if(ptr->pPlatDef->head.spinflip.wait==WAITFOREVER)
								ptr->spinflip.timer=32767; // just keep it from ever being <0
							else
								ptr->spinflip.timer=ptr->pPlatDef->head.spinflip.wait;
						}
						else if(ptr->spinflip.progress>=ptr->pPlatDef->head.spinflip.angle)
						{
							if(ptr->pPlatDef->head.stop_sfx)
							{
								sfxPlayParsed(
									&(ptr->pos),
									ptr->pPlatDef->head.stop_sfx,
									ptr->pPlatDef->head.stop_pitch,
									ptr->pPlatDef->head.stop_vol
									);
							}
							ptr->moveticker = 0;

							//ptr->spin.progress=ptr->pPlatDef->head.spinflip.angle;
							ptr->spinflip.dir=-(ptr->pPlatDef->head.spin.rotspeed);

							if(ptr->pPlatDef->head.spinflip.wait==WAITFOREVER)
								ptr->spinflip.timer=32767; // just keep it from ever being <0
							else
								ptr->spinflip.timer=ptr->pPlatDef->head.spinflip.wait;
						}
						
						ptr->spin.progress=ptr->spinflip.progress;
					}
					
					ptr->spinflip.timer--;
				}
				else	// Normal, non-pausing or anything, spinny platform
				{
					ptr->spin.progress+=ptr->pPlatDef->head.spin.rotspeed;

					if(ptr->pPlatDef->head.spin.rotspeed)
					{
						if(!(ptr->moveticker % 8))
						{
	//							DB("move sfx by spinflip tag %d\n",ptr->pPlatDef->head.tag);
							if(ptr->pPlatDef->head.move_sfx)
							{
								sfxPlayParsed(
									&(ptr->pos),
									ptr->pPlatDef->head.move_sfx,
									ptr->pPlatDef->head.move_pitch,
									ptr->pPlatDef->head.move_vol
									);
							}
						}
					}
					ptr->moveticker++;
				}
			}

			ptr->spin.progress&=0xfff;
		}

		ASSERT(ptr->nCollBoxes>0);

		if(ptr->tiltSpin)
		{
			if(ptr->spin.progress)
				platformTilt(ptr, ptr->spin.progress, ptr->pPlatDef->head.spin.axis);
		}
		else
		{
			if(ballColl.nHitPlats && (ballColl.hitPlats[0].pPlatform==((void *)ptr)))
			{
// Yes, platformrotatemodel also does the "hitplats[0]" check
// However there's more code in here than just that... :/

				VECTOR	posChange;

				COPYVECTOR(&posChange,&ballPos);
				platformRotateModel(&ballColl, ptr);
				posChange.vx-=ballPos.vx;
				posChange.vy-=ballPos.vy;
				posChange.vz-=ballPos.vz;
				//ADDTOVECTOR(&JoinBallPos, &posChange);
				JoinBallPos.vx-=posChange.vx;
				JoinBallPos.vy-=posChange.vy;
				JoinBallPos.vz-=posChange.vz;
				ADDTOVECTOR(&ballColl.oldPos, &posChange);
				//COPYVECTOR(&GloveCtrl.platformMove,&posChange);
			}

			if(gloveColl.nHitPlats && (gloveColl.hitPlats[0].pPlatform==((void *)ptr)))
			{
				VECTOR	posChange;

				COPYVECTOR(&posChange,&glovePos);
				platformRotateModel(&gloveColl, ptr);
				posChange.vx-=glovePos.vx;
				posChange.vy-=glovePos.vy;
				posChange.vz-=glovePos.vz;
				//ADDTOVECTOR(&JoinGlovePos, &posChange);
				JoinGlovePos.vx-=posChange.vx;
				JoinGlovePos.vy-=posChange.vy;
				JoinGlovePos.vz-=posChange.vz;
				ADDTOVECTOR(&gloveColl.oldPos, &posChange);
				//ADDTOVECTOR(&GloveCtrl.move, &posChange);
			}

			if(GloveCtrl.action == HAND_HOPPING && DennisColl.nHitPlats && (DennisColl.hitPlats[0].pPlatform==((void *)ptr)))
			{
				VECTOR	posChange;

				COPYVECTOR(&posChange,&GloveCtrl.mount->pos);
				platformRotateModel(&DennisColl, ptr);
				posChange.vx-=GloveCtrl.mount->pos.vx;
				posChange.vy-=GloveCtrl.mount->pos.vy;
				posChange.vz-=GloveCtrl.mount->pos.vz;
				//ADDTOVECTOR(&JoinGlovePos, &posChange);
				ADDTOVECTOR(&DennisColl.oldPos, &posChange);

			}

		}

		if(ptr->pPlatDef->head.topple.yes)
		{
			if(ptr->topple.falling==1)
			{
				int gotThere=0;

				if(!(ptr->moveticker % 8))
				{
//					DB("move sfx by topple tag %d\n",ptr->pPlatDef->head.tag);
					if(ptr->pPlatDef->head.move_sfx)
					{
						sfxPlayParsed(
							&(ptr->pos),
							ptr->pPlatDef->head.move_sfx,
							ptr->pPlatDef->head.move_pitch,
							ptr->pPlatDef->head.move_vol
							);
					}
				}
				ptr->moveticker++;

				if(ptr->pPlatDef->head.topple.angle>0)
				{
					ptr->topple.progress+=128;
					if(ptr->topple.progress>=ptr->pPlatDef->head.topple.angle)
						gotThere=TRUE;
				}
				else
				{
					if(ptr->pPlatDef->head.topple.angle<0)
					{
						ptr->topple.progress-=128;
						if(ptr->topple.progress<=ptr->pPlatDef->head.topple.angle)
							gotThere=TRUE;
					}
				}

				if(gotThere)
				{
					ptr->topple.jar=TRUE;
					ptr->topple.falling=2;
					ptr->topple.progress=ptr->pPlatDef->head.topple.angle;

					if(ptr->pPlatDef->head.stop_sfx)
					{
						sfxPlayParsed(
							&(ptr->pos),
							ptr->pPlatDef->head.stop_sfx,
							ptr->pPlatDef->head.stop_pitch,
							ptr->pPlatDef->head.stop_vol
							);
					}

				}
			}
		}

		if(ptr->pPlatDef->head.seesaw.yes)
		{
			if(ptr->seesaw.dist)
			{
				int add=((ptr->seesaw.dist/0x8000)*ptr->pPlatDef->head.seesaw.mass)/4096;

				//PLAT_DB("dist = %d, add = %d\n", ptr->seesaw.dist, add);

				if(ptr->pPlatDef->head.seesaw.axis==0)	// X
					ptr->seesaw.progress+=add;
				else
					ptr->seesaw.progress-=add;			// z is backwards

				if((ptr->seesaw.progress)>=(ptr->pPlatDef->head.seesaw.maxrot))
					ptr->seesaw.progress=ptr->pPlatDef->head.seesaw.maxrot;
				else
				{
					if((ptr->seesaw.progress)<=(-ptr->pPlatDef->head.seesaw.maxrot))
						ptr->seesaw.progress=-(ptr->pPlatDef->head.seesaw.maxrot);
				}

				if(!(ptr->moveticker % 8))
				{
//					DB("move sfx by seesaw tag %d\n",ptr->pPlatDef->head.tag);
					if(ptr->pPlatDef->head.move_sfx)
					{
						sfxPlayParsed(
							&(ptr->pos),
							ptr->pPlatDef->head.move_sfx,
							ptr->pPlatDef->head.move_pitch,
							ptr->pPlatDef->head.move_vol
							);
					}
				}
				ptr->moveticker++;

		
			}
			
			ptr->seesaw.timer++;

			// make it tend towards the centre
			if((ptr->seesaw.progress)>0)
			{
				ptr->seesaw.progress-=ptr->pPlatDef->head.seesaw.levelout;
				if((ptr->seesaw.progress)<0)
					ptr->seesaw.progress=0;
			}
			else
			{
				ptr->seesaw.progress+=ptr->pPlatDef->head.seesaw.levelout;
				if((ptr->seesaw.progress)>0)
					ptr->seesaw.progress=0;
			}

			platformTilt(ptr, ptr->seesaw.progress, ptr->pPlatDef->head.seesaw.axis);
		}

		if(ptr->pPlatDef->head.pendulum.angle)
		{
			ptr->pendulum.progress+=ptr->pendulum.dir;

			if(!(ptr->moveticker % 8))
			{
				if(ptr->pPlatDef->head.move_sfx)
				{
//					DB("move sfx by pendulum tag %d\n",ptr->pPlatDef->head.tag);
					sfxPlayParsed(
						&(ptr->pos),
						ptr->pPlatDef->head.move_sfx,
						ptr->pPlatDef->head.move_pitch,
						ptr->pPlatDef->head.move_vol
						);
				}
			}
			ptr->moveticker++;

			//if(ABS(ptr->pendulum.progress)>=(ptr->pPlatDef->head.pendulum.angle))
			//	ptr->pendulum.dir=-(ptr->pendulum.dir);

			if(ptr->pendulum.progress<(-ptr->pPlatDef->head.pendulum.angle))
			{
				ptr->pendulum.dir=-ptr->pendulum.dir;
				ptr->pendulum.progress=-ptr->pPlatDef->head.pendulum.angle;
				if(ptr->pPlatDef->head.stop_sfx)
				{
					sfxPlayParsed(
						&(ptr->pos),
						ptr->pPlatDef->head.stop_sfx,
						ptr->pPlatDef->head.stop_pitch,
						ptr->pPlatDef->head.stop_vol
						);
				}
			}
			else if(ptr->pendulum.progress>ptr->pPlatDef->head.pendulum.angle)
			{
				ptr->pendulum.dir=-ptr->pendulum.dir;
				ptr->pendulum.progress=ptr->pPlatDef->head.pendulum.angle;
				if(ptr->pPlatDef->head.stop_sfx)
				{
					sfxPlayParsed(
						&(ptr->pos),
						ptr->pPlatDef->head.stop_sfx,
						ptr->pPlatDef->head.stop_pitch,
						ptr->pPlatDef->head.stop_vol
						);
				}

			}

			platformTilt(ptr, ptr->pendulum.progress, ptr->pPlatDef->head.pendulum.axis);
		}

		if(nPoints<2) // if zero points or one point then don't do anything
			return;

		// do anim stuff
		if((ptr->move.waitTimer==0) && (ptr->move.pointCtr!=0))
		{
			VECTOR from, platMoved;
			VECTOR *pTo;

			if(ptr->pPlatDef->head.maxspeed)	// don't play move sfx on a "moving at speed 0" platform
			{
				if(!(ptr->moveticker % 8))
				{
					if(ptr->pPlatDef->head.move_sfx)
					{
	//					DB("move sfx by normal movement tag %d\n",ptr->pPlatDef->head.tag);
						sfxPlayParsed(
							&(ptr->pos),
							ptr->pPlatDef->head.move_sfx,
							ptr->pPlatDef->head.move_pitch,
							ptr->pPlatDef->head.move_vol
							);
					}
				}
				ptr->moveticker++;
			}

// Setting the header speed -ve is a bit of a stinky bodge used by the fear boss
			if(ptr->move.speed >= 0)
			{
				pTo =&(ptr->pPlatDef->points[(ptr->move.point+1)%nPoints].pos);
				ptr->move.progress+=ptr->move.speed;
				//ptr->move.progress+=(speed*4096);
			}
			else
			{
				pTo =&(ptr->pPlatDef->points[(ptr->move.point+nPoints-1)%nPoints].pos);	// note mod to stay +ve
				ptr->move.progress-=ptr->move.speed;
				//ptr->move.progress-=(speed*4096);
			}

			if(ptr->pPlatDef->head.accel)
			{
				ptr->move.speed+=ptr->pPlatDef->head.accel;
				if(ptr->move.speed>maxSpeed)
					ptr->move.speed=maxSpeed;
			}
			else
			{
				ptr->move.speed=maxSpeed;
			}

			//COPYVECTOR(&from, &(ptr->pPlatDef->points[ptr->move.point].pos));
			COPYVECTOR(&from, &(ptr->pos));

			ptr->move.moving=TRUE;

			/*
			if(ptr->pPlatDef->head.tag==1234)
			{
				DB(".\n");
				DB("Progress = %d\n", ptr->move.progress);
				DB("from = (%d, %d, %d)\n", from.vx, from.vy, from.vz);
				DB("to =   (%d, %d, %d)\n", pTo->vx, pTo->vy, pTo->vz);
			}
			*/



			//if(VectorMoveTo(&from, pTo, ptr->move.progress))
			if(VectorMoveTo(&from, pTo, ABS(ptr->move.speed)))
			{
				//DB("Got to point!\n");
				//DB("From = %d, (orig=%d), to = %d, (orig=%d)\n",
				//	from.vx,
				//	ptr->pPlatDef->points[ptr->move.point].pos.vx,
				//	pTo->vx,
				//	ptr->pPlatDef->points[(ptr->move.point+1)%nPoints].pos.vx);


				// got to point, so can't be moving ...
				ptr->move.moving=FALSE;

				ASSERT(CompareVectors(&from, pTo));


				if(ptr->move.speed >= 0)
				{
					ASSERT(CompareVectors(pTo, &(ptr->pPlatDef->points[(ptr->move.point+1)%nPoints].pos)));
					ptr->move.point++;
					if(ptr->move.point==nPoints)
						ptr->move.point=0;
				}
				else
				{
					ptr->move.point--;
					if(ptr->move.point<0)
						ptr->move.point=nPoints-1;
// Return the platform to normal +ve action
// (important for the fear boss bodge)
					ptr->pPlatDef->head.maxspeed = -ptr->move.speed;	// reset the speed to normal // ?CPW
				}

				ptr->move.speed=0;
				ptr->move.progress=0;
				ptr->move.waitTimer=ptr->pPlatDef->points[ptr->move.point].wait;
				ptr->move.jap=TRUE;// because we're just at the point

				(ptr->move.pointCtr)--;

// If it's stopped, and there's a stop effect, play the thing
				if(
					((!ptr->move.pointCtr) || (ptr->move.waitTimer != 0)) 
					&& ptr->pPlatDef->head.stop_sfx
					)
				{
//					PLAT_DB("End Move Effect\n");

					sfxPlayParsed(
						&(ptr->pos),
						ptr->pPlatDef->head.stop_sfx,
						ptr->pPlatDef->head.stop_pitch,
						ptr->pPlatDef->head.stop_vol
						);
				}
			}
			//else
			//{
				//DB("From = %d, (orig=%d)\n",
				//	from.vx,
				//	ptr->pPlatDef->points[ptr->move.point].pos.vx);
			//}

			//if(ptr->pPlatDef->head.tag==1234)
			//{
				//DB("moveto = (%d, %d, %d)\n", from.vx, from.vy, from.vz);
				//DB("platis = (%d, %d, %d)\n", ptr->pos.vx, ptr->pos.vy, ptr->pos.vz);
			//}

			platformMoveDynamicBox(ptr, from.vx, from.vy, from.vz);

			SUBVECTOR(&platMoved, &(ptr->pos), &(ptr->oldPos));

			// is platform not moving ?
			if(((platMoved.vx|platMoved.vy|platMoved.vz)==0)||(ptr->move.waitTimer>0))
			{
				int i,j;

				//if(ptr->pPlatDef->head.tag==52)
				//{
				//	DB("PLAT NOT MOVING\n");
				//}

				COPYVECTOR(&(ptr->oldPos), &(ptr->pos));

				j=ptr->platNumber;

				for(i=0; i<pPlatsLoaded[j].nCollBoxes; i++)
				{
					pPlatsLoaded[j].pBoxes[i].oldpX=pPlatsLoaded[j].pBoxes[i].pX;
					pPlatsLoaded[j].pBoxes[i].oldpY=pPlatsLoaded[j].pBoxes[i].pY;
					pPlatsLoaded[j].pBoxes[i].oldpZ=pPlatsLoaded[j].pBoxes[i].pZ;
				}
			}

			//if(ptr->ballTouchCounter<4)

			if(ballColl.nHitPlats && (ballColl.hitPlats[0].pPlatform==((void *)ptr)))
			{
				//PLAT_DB("Platform has moved (%d,%d,%d)\n", platMoved.vx, platMoved.vy, platMoved.vz);
				//DB("Moving ball ...\n");
				ADDTOVECTOR(&ballPos, &platMoved);
				ADDTOVECTOR(&JoinBallPos, &platMoved);
				ADDTOVECTOR(&ballColl.oldPos, &platMoved);

				/*if (GloveCtrl.action==HAND_CASTSPELL && GloveCtrl.lastAction==HAND_JOINED)
				{
					ADDTOVECTOR(&glovePos, &platMoved);
					ADDTOVECTOR(&JoinGlovePos, &platMoved);
					ADDTOVECTOR(&gloveColl.oldPos, &platMoved);
				}*/

			}

			if(gloveColl.nHitPlats && (gloveColl.hitPlats[0].pPlatform==((void *)ptr)))
			{
				GloveCtrl.onMovingPlat=TRUE;

				//PLAT_DB("Platform has moved (%d,%d,%d)\n", platMoved.vx, platMoved.vy, platMoved.vz);
				//DB("Moving glove ...\n");
				ADDTOVECTOR(&glovePos, &platMoved);
				ADDTOVECTOR(&JoinGlovePos, &platMoved);
				ADDTOVECTOR(&gloveColl.oldPos, &platMoved);


				//if(Glover.currentAnimation==HANDANIM_GRABBALL)
				/*
				if(GloveCtrl.action==HAND_SNATCH)
				{
					DB("%d. Moving ball in hand snatch mode...\n", frame);

					ADDTOVECTOR(&ballPos, &platMoved);
					ADDTOVECTOR(&JoinBallPos, &platMoved);
					ADDTOVECTOR(&ballColl.oldPos, &platMoved);
				}
				*/
			}
		

			if(GloveCtrl.action == HAND_HOPPING && DennisColl.nHitPlats && (DennisColl.hitPlats[0].pPlatform==((void *)ptr)))
			{
				VECTOR	posChange;

//				DB("Moving dennis\n");
				ADDTOVECTOR(&GloveCtrl.mount->pos, &platMoved);
				ADDTOVECTOR(&DennisColl.oldPos, &platMoved);
				ADDTOVECTOR(&DennisPos, &platMoved);


			}


		}

		if(ptr->move.waitTimer>0)
			ptr->move.waitTimer--;
}

void platformDrawDynamicBoxes(void)
{
	//This function needs to compare SubDivCtrl.distance with z table distance

	DYNCOLLBOX *ptr;
	NEWMODEL *pObj;

	modelctrl.subdivide=TRUE;
	// Draw each model that is in the platforms linked list
	for(ptr = dyncollList.head.next; ptr != &dyncollList.head; ptr = ptr->next)
	{
		ASSERT(ptr);

		if(ptr->pPlatDef)	// bugle's sphere doesn't have a platdef
		{
			/*
			if((ptr->pPlatDef->head.flags)&(PLATFLAG_DESTRUCT_BALL|PLATFLAG_DESTRUCTIBLE))
			{
				if((ptr->deadTimer>0) && (ptr->deadTimer<30))
				{
					int i;

					//pObj=pPlatsLoaded[ptr->platNumber].pFragModel;
					pObj=objDebris.pModel;

					// not active, and has been dead for ptr->active frames ...
					if(pObj)
					{
						for(i=0; i<objDebris.nBits; i++)
						{
							CopyVector(&pObj->position, objDebris.pos+i); 

							objDebris.pos[i].vy+=(ptr->deadTimer<<2);

							pObj->world.rotate.vx=randomInt(0xfff);
							pObj->world.rotate.vy=randomInt(0xfff);
							pObj->world.rotate.vz=randomInt(0xfff);

							pObj->globalscale.vx=1024;
							pObj->globalscale.vy=1024;
							pObj->globalscale.vz=1024;

							lscapeDrawClipped(pObj, &GsWSMATRIX, TRUE);
						}
					}

					ptr->deadTimer++;
				}
			}

  else
			*/
			{
				if((ptr->pPlatDef->head.flags)&(PLATFLAG_CRUMBLE))
				{
					if(ptr->deadTimer>0)
					{
						int dx=4096-randomInt(8192);
						int dz=4096-randomInt(8192);

						if((ptr->deadTimer)<(ptr->pPlatDef->head.crumble.time))
						{
 							//platformMoveDynamicBox(ptr, ptr->cb.pX+dx, ptr->cb.pY, ptr->cb.pZ+dz);
							platformSetDynamicBox(ptr,	ptr->pPlatDef->points[0].pos.vx+dx,
														ptr->pPlatDef->points[0].pos.vy,
														ptr->pPlatDef->points[0].pos.vz+dz);
							ptr->deadTimer++;
						}
						else
						{
							int yVel=(ptr->deadTimer-(ptr->pPlatDef->head.crumble.time))*5192;

							platformMoveDynamicBox(ptr, ptr->pPlatDef->points[0].pos.vx+dx, ptr->cumPos.vy+yVel,
														ptr->pPlatDef->points[0].pos.vz+dz);
							ptr->deadTimer++;
						}

						if((ptr->deadTimer)>(ptr->pPlatDef->head.crumble.limit))
						{
							platformSetDynamicBox(ptr,	ptr->pPlatDef->points[0].pos.vx,
														ptr->pPlatDef->points[0].pos.vy,
														ptr->pPlatDef->points[0].pos.vz);

							ptr->deadTimer=0;
						}
					}
				}
			}

			if(ptr->active)
			{
				DYNCOLLBOX *pBox=ptr;

				//PLAT_DB("Printing plat ...\n");

				while(pBox)	// Child-loop. Note it can only handle one depth of children.
				{
					//PLAT_DB("  Child\n");

					pObj=pPlatsLoaded[pBox->platNumber].pModel;
					ASSERT(pObj);

					pObj->position.vx=pBox->cumPos.vx/4096;
					pObj->position.vy=pBox->cumPos.vy/4096;
					pObj->position.vz=pBox->cumPos.vz/4096;

#ifdef SHOWCOLL
					if(lscape_collbox_toggle)
					{
						int i;
						for(i=0; i<ptr->nCollBoxes; i++)
						{
							lscapeDrawCollBox(ptr->pBoxes+i, ptr);
							//lscapeDrawCollBox(ptr->pBoxes+i, NULL);
						}

					}
					else
#endif
					{
						lscapeSetTerrainDepthOffset(ptr->pPlatDef->head.terrain);

						if((ptr->pPlatDef->head.pendulum.yes) || (ptr->pPlatDef->head.seesaw.yes) || (ptr->pPlatDef->head.spin.yes) || (ptr->pPlatDef->head.topple.yes))
						{
							int axis;
							int progress;

							if(ptr->pPlatDef->head.pendulum.yes)
							{
								axis=ptr->pPlatDef->head.pendulum.axis;
								progress=ptr->pendulum.progress;
							}
							else
							{
								if(ptr->pPlatDef->head.seesaw.yes)
								{
									axis=ptr->pPlatDef->head.seesaw.axis;
									progress=ptr->seesaw.progress;
								}
								else
								{
									if(ptr->pPlatDef->head.spin.yes)
									{
										axis=ptr->pPlatDef->head.spin.axis;
										progress=ptr->spin.progress;
									}
									else
									{
										if(ptr->pPlatDef->head.topple.yes)
										{
											axis=ptr->pPlatDef->head.topple.axis;
											progress=ptr->topple.progress;
										}
										else
										{
											// Earthmen are not proud of their ancestors, and never invite them
											// round to dinner.

											ASSERT(0);
											// THIS NEVER HAPPENS
										}
									}
								}
							}

							pObj->world.rotate.vx=0;
							pObj->world.rotate.vy=0;
							pObj->world.rotate.vz=0;

							switch(axis)
							{
								case X_PLATAXIS:
									pObj->world.rotate.vx=progress;
									break;
								case Y_PLATAXIS:
									pObj->world.rotate.vy=progress;
									break;
								case Z_PLATAXIS:
									pObj->world.rotate.vz=progress;
									break;
								default:
									PLAT_DB("Invalid Axis!\n");
//									CRASH;
									//pObj->world.rotate.vz=progress;
									break;

							}
							
							lscapeDrawClipped(pObj, &GsWSMATRIX, LSCAPE_DRAWMODEL|LSCAPE_ROTATEMODEL);
						}
						else
						{
							//if(ptr->pPlatDef->head.tag==1234)
							//{
							//	DB("platdrw = (%d, %d, %d)\n", ptr->pos.vx, ptr->pos.vy, ptr->pos.vz);
							//}

							lscapeDrawClipped(pObj, &GsWSMATRIX, LSCAPE_DRAWMODEL);
						}
					}

//					pBox=pBox->pChildPlat[0];
					if(pBox->pFirstChildPlat)
						pBox = pBox->pFirstChildPlat;
					else
						pBox = pBox->pNextChildPlat;
				}	// end of "while(pBox)" loop
			}
		}
	}

	// move the boxes. Again :(
	if(gameCtrl.gameActive)
	{
		for(ptr = dyncollList.head.next; ptr != &dyncollList.head; ptr = ptr->next)
		{
			if(ptr->pPlatDef)	// bugle's sphere doesn't have a platdef
			{
				if(ptr->pParentPlat)
				{
					//PLAT_DB("Adding to parent ...\n");
					ADDVECTOR(&ptr->cumPos, &ptr->pos, &ptr->pParentPlat->cumPos);
				}
				else
				{
					COPYVECTOR(&ptr->cumPos, &ptr->pos);
				}
			}
		}
		

		GloveCtrl.onMovingPlat=FALSE;

		for(ptr = dyncollList.head.next; ptr != &dyncollList.head; ptr = ptr->next)
		{
			if(ptr->pPlatDef)	// bugle's sphere doesn't have a platdef
			{
				platformProcess(ptr);
			}
		}

	}

	platformDrawShrapnel();
}

void platformDestroyDynamicBox(DYNCOLLBOX *ptr)
{
	//if((ptr) && (text->next))
	if(ptr)
	{
		ptr->prev->next = ptr->next;
		ptr->next->prev = ptr->prev;
		dyncollList.numEntries--;

		ptr->next = NULL;

		FREE(ptr);
	}
	else
	{
		PLAT_DB("How can I free a pointer that doesn't exist, you dolt?\n");
		CRASH;
	}
}


void platformEndFile(void)
{
	DYNCOLLBOX *pPlat, *pPlat2, *pParentPlat;

	PLAT_DB("platformEndFile()\n");

	for(pPlat = dyncollList.head.next; pPlat != &dyncollList.head; pPlat = pPlat->next)
	{
		PLAT_DB("pPlat=%x\n", pPlat);
		ASSERT(pPlat);

		if(pPlat->pPlatDef)	// bugle's sphere doesn't have a platdef
		{
			int parentTag=pPlat->pPlatDef->head.parentTag;

			if(parentTag!=(-1))
			{
				loadlndFindPlatform(parentTag, NULL, &pParentPlat);

				ASSERT(pParentPlat);

				pPlat->pParentPlat=pParentPlat;
			}
			else
				pPlat->pParentPlat=NULL;
		}

		pPlat->pFirstChildPlat=NULL;
		pPlat->pNextChildPlat=NULL;
	}

	// now find all the kiddies
	for(pPlat = dyncollList.head.next; pPlat != &dyncollList.head; pPlat = pPlat->next)
	{
		if(pPlat->pPlatDef)	// bugle's sphere doesn't have a platdef
		{
			int tag=pPlat->pPlatDef->head.tag;
			int parentTag;
			int i;

			ASSERT(pPlat);
 
			for(pPlat2 = dyncollList.head.next; pPlat2 != &dyncollList.head; pPlat2 = pPlat2->next)
			{
				if(tag==pPlat2->pPlatDef->head.parentTag)
				{
					pPlat2->pNextChildPlat = pPlat->pFirstChildPlat;
					pPlat->pFirstChildPlat = pPlat2;
//					pPlat->pChildPlat[pPlat->nChildPlats++]=pPlat2;
				}
			}

//			PLAT_DB("Tag %4d (%p) has parent %p, children %d\n", tag, pPlat, pPlat->pParentPlat, pPlat->nChildPlats);
/*
			for(i=0; i<pPlat->nChildPlats; i++)
			{
				PLAT_DB("child %p\n", pPlat->pChildPlat[i]);
			}
*/
		}
	}


/*	for(pPlat = dyncollList.head.next; pPlat != &dyncollList.head; pPlat = pPlat->next)
	{
		// if the platform has parents, remove it from our list ...
		if(pPlat->pParentPlat)
		{
			PLAT_DB("Removing %p\n", pPlat);

			pPlat->prev->next = pPlat->next;
			pPlat->next->prev = pPlat->prev;
			dyncollList.numEntries--;

			//pPlat->next = NULL;
		}
		// ... but don't free it. That would be a bad thing.
	}
*/
}

// Note 1:- "def" will STAY valid,
// so you can maintain a pointer to it in the moving/interacting platform code.
// no need to take a copy.
// Note 2:- This call is for ALL platforms, including odd bits like spikes & restart points
// which don't need the full collision code.
int platformCreatePlatform(PLATFORM_DEFSTR *def)
{
	DYNCOLLBOX *pCollBox;
	int i,j;
	char sStr[64];
	ULONG *pColl=NULL;
	NEWMODEL *pPSA;

	if(def->head.parentTag)
		pCollBox=platformCreateDynamicBox(FALSE);
	else
		pCollBox=platformCreateDynamicBox(TRUE);

	ASSERT(pCollBox);

	pCollBox->pPlatDef=def;

	pCollBox->spin.progress=0;
	pCollBox->pendulum.progress=def->head.pendulum.headstart;
	pCollBox->seesaw.progress=0;
	pCollBox->move.progress=0;

	pCollBox->pendulum.dir=8;
	pCollBox->move.dir=0;

	pCollBox->move.point=pCollBox->pPlatDef->head.start_point;
	pCollBox->move.waitTimer=pCollBox->pPlatDef->points[pCollBox->move.point].wait;
	pCollBox->active=TRUE;
	pCollBox->deadTimer=0;
	pCollBox->seesaw.timer=255;
	pCollBox->move.pointCtr=-1;
	pCollBox->move.jap=0;// just at point
	pCollBox->spin.timer=0;
	pCollBox->ballTouchTime=0;
	pCollBox->snatch_ball_time = 0;
	pCollBox->spinflip.dir=def->head.spin.rotspeed;
	pCollBox->magnet.movingToMiddle=0;

	i=platformTryAndFindInstance(def->head.platID);

	if(i&0x80000000) // not already loaded
	{
		i&=0x7fffffff; // get rid of top bit

		modelctrl.halfgouraud=TRUE;

		//sprintf(sStr, "%s.PSA", sPlatName);
		//sprintf(sStr,"WORLDS\\%s\\PLAT\\%s.PSA",WorldDirectoryNames[world], sPlatName);
		//pPSA=LoadPSA(sStr);

		pPSA=BFF_IsCRCModelLoaded(def->head.platID, NULL);
		
		if(pPSA)
		{
			pColl=(ULONG *)collisionLoadMeshCRC(def->head.platID);

			pPlatsLoaded[i].pModel=pPSA;
			pPlatsLoaded[i].pFragModel=NULL;
			pPlatsLoaded[i].pBoxes=(COLLBOX *)pColl;// REMEMBER, when freeing, free this pointer-4 bytes
			pPlatsLoaded[i].crc=def->head.platID;

			if(pColl)
			{
				pPlatsLoaded[i].nCollBoxes=pColl[-1];
				//platformCentrePlat(pPlatsLoaded+i);

				for(j=0; j<pPlatsLoaded[i].nCollBoxes; j++)
				{
					COPYVECTOR((VECTOR *)&(pPlatsLoaded[i].pBoxes[j].oldpX), (VECTOR *)&(pPlatsLoaded[i].pBoxes[j].pX));
					pPlatsLoaded[i].pBoxes[j].flags|=MOVE; // this signifies the platform can move
				}
			}
			else
			{
				platformLinkDynamicBox(&pPlatsLoaded[i].pBoxes, pPSA, 0);
				pPlatsLoaded[i].nCollBoxes=1;
			}
		}
		else
		{
			PLAT_DB("Unable to locate PSA for platform %8x\n", def->head.platID);
			CRASH;
		}
	}

	pPlatsLoaded[i].collRadiusSqr=platformGetRadiusSqr(pPlatsLoaded[i].pModel);
	pCollBox->platNumber=i;

	if(def->head.destruct.fragID)
	{
		pPlatsLoaded[pCollBox->platNumber].pFragModel=BFF_IsCRCModelLoaded(def->head.destruct.fragID, NULL);
		//pPlatsLoaded[pCollBox->platNumber].pFragModel=BFF_IsCRCModelLoaded(def->head.platID, NULL);
		ASSERT(pPlatsLoaded[pCollBox->platNumber].pFragModel);
		PLAT_DB("platID=%x, fragID=%x\n", def->head.platID, def->head.destruct.fragID);
		//ASSERT(def->head.platID==def->head.destruct.fragID);
	}

	if((pCollBox->pPlatDef->head.pendulum.angle) || (pCollBox->pPlatDef->head.seesaw.maxrot)
		|| (def->head.tag==TAG_SPECIALTILT && level==PIRATES3))
	{// it it a pendulum or seesaw ?
		platformLinkDynamicBox(&pCollBox->pBoxes, pPlatsLoaded[i].pModel, 1);
		pCollBox->nCollBoxes=1;

		if((def->head.tag==TAG_SPECIALTILT) && (level==PIRATES3))
		{
			pCollBox->tiltSpin=TRUE;
		}
	}
	else
	{
		pCollBox->pBoxes=pPlatsLoaded[i].pBoxes; // this can be changed for ramps
		pCollBox->nCollBoxes=pPlatsLoaded[i].nCollBoxes;
	}

	platformSetDynamicBox(pCollBox,
		def->points[pCollBox->pPlatDef->head.start_point].pos.vx,
		def->points[pCollBox->pPlatDef->head.start_point].pos.vy,
		def->points[pCollBox->pPlatDef->head.start_point].pos.vz);


// Theres currently a problem with scripted, which results in the platform's name being truncated
// to 7 letters
//	sprintf(tempname,"WORLDS\\%s\\NME\\%s.PSA",WorldDirectoryNames[world],def->head.name);
//	psa = LoadPSA(tempname);

// If the model doesn't exist yet, don't bother creating the platform

	platformVerify();

	return 1;
}

// like the puzzle one, but for any platform, not just a tagged one
// (tbd - make the puzzle one call here)
void platformStartMove(DYNCOLLBOX *pBox,int flags, int value)
{
	pBox->move.waitTimer=0;// that should get things moving ...
	pBox->moveticker = 0;
// "move" sound effects loop, and are therefore in "platformprocess"
/*
	if(pBox->pPlatDef->head.move_sfx)
	{
//		PLAT_DB("Start Move Effect\n");
		sfxPlayParsed(
			&(pBox->pos),
			pBox->pPlatDef->head.move_sfx,
			pBox->pPlatDef->head.move_pitch,
			pBox->pPlatDef->head.move_vol
			);
	}
*/
	switch(flags)
	{
	case 3: // just set speed for Tim.
		pBox->pPlatDef->head.maxspeed=value;
		pBox->move.speed=value;
		break;
	case 4: // go forever
		if(value)
		{
			pBox->pPlatDef->head.maxspeed=value;
		}
		pBox->move.pointCtr=-1;
		break;
	case 5: // one whole cycle
		pBox->move.pointCtr=pBox->pPlatDef->head.n_points;
		break;
	case 6:	// one step
		pBox->move.pointCtr=1;
		break;
	default:
		PLAT_DB("ERROR: Bad platform move action (%d)\n", flags);
		CRASH;
	}
}

