/******************************************************************************************
	CRTest PS   (c) 1999-2001 ISL

	platform.c:			The yummy scrummy platform source
******************************************************************************************/

#include "global.h"
#include "collide.h"
#include "platform.h"
#include "map_draw.h"
#include "bff_load.h"
#include "maths.h"
#include "main.h"

#include "overlays.h"
#include "inventory.h"
#include "enemies.h"	// needed by charactr
#include "scenics.h"
#include "charactr.h"
#include "sound.h"

void platformUpdateChildPosns(PLATFORMWORK *parent);

// ============================================
//        Test Platform Code
// ============================================

PLATFORMWORK *platform_list = NULL;

SOUND_TRIGGER *sound_trigger_list = NULL;
int n_sound_triggers = 0;
// CPW: SfxSampleType *pSplashSample = NULL;

// Before BFF-loading
void platformInit()
{
	platform_list = NULL;
	sound_trigger_list = NULL;
	n_sound_triggers = 0;
}

// CPW: The wonderful thing about triggers, is triggers are wonderful things
void platformLinkSoundTriggerBFF(SOUNDTRIGGERLIST *list)
{
	int i;
	sound_trigger_list = ADD2POINTER(list,sizeof(SOUNDTRIGGERLIST));
	n_sound_triggers = list->n_sound_triggers;

// Convert sample name CRC's into real live samples
	for(i = 0; i < n_sound_triggers; i++)
	{
		if(sound_trigger_list[i].sample)
		{
			SfxSampleType *pS;

			pS = sfxFindSampleInAllBanksCRC((int)sound_trigger_list[i].sample);

			if(((unsigned long)(sound_trigger_list[i].sample) == 0xc3362221) || ((unsigned long)(sound_trigger_list[i].sample) == 0xce7504f8))
			{	
				// CPW: "puddle1", "puddle2"
				DB("Found splash sample\n");
				sound_trigger_list[i].flags|=TRIGGER_SAMPLE_SPLASH;
			}

			if(!pS)
			{
				DB("WARNING: Can't find sample! CRC %x (ARTIST'S FAULT)\n", (int)sound_trigger_list[i].sample);
				// CPW: ASSERT(0);
				sound_trigger_list[i].sample = NULL;
			}
			else
			{
				sound_trigger_list[i].sample = pS;
			}
		}
		else
		{
			DB("No sample has been specified for sound_trigger (STUPID ARTIST)\n");
			ASSERT(0);
		}
	}
}

SfxSampleType *platformCheckForSampleTriggers(VECTOR *pos, int *flag_ret)
{
	int i;
	SOUND_TRIGGER *p;
	SOUND_TRIGGER *pDefault = NULL;

	if(!n_sound_triggers)
	{
		if(flag_ret)
			*flag_ret = 0;

		return NULL;
	}

	p = sound_trigger_list;
	for(i = 0; i < n_sound_triggers; i++, p++)
	{
		if(p->flags & TRIGGER_SAMPLE_HASBOX)
		{
			if(    pos->vx >= p->px && pos->vx < p->px + p->sx
				&& pos->vy >= p->py && pos->vy < p->py + p->sy
				&& pos->vz >= p->pz && pos->vz < p->pz + p->sz)
			{
				DB("In Sample Box\n");
				if(flag_ret)
					*flag_ret = p->flags;

				return p->sample;
			}
		}
		else
		{
			pDefault = p;
		}
	}

	if(flag_ret && pDefault)
		*flag_ret = pDefault->flags;

	if(pDefault)
		return pDefault->sample;
	else
		return 0;
}



void platformLinkBFF(PLATFORMDEF *platformdef)
{
	PLATFORMWORK *work;
	int i;

	COLLBOX *boundbox;

//	printf("Linking bff platform &%8x\n",(int)(platformdef->mesh));
	platformdef->mesh = (void *)BFF_FindObject(BFF_FMA_MESH_ID,(int)(platformdef->mesh));
	platformdef->points = ADD2POINTER(platformdef->points,(int)platformdef);
	if(!platformdef->mesh)
	{
		DB("Platform Mesh Not Found\n");
		return;	// If we can't find the plat-mesh, don't link it into the plat-list
	}

	utilPrintf("Mesh offsets %d %d %d\n",platformdef->mesh->offsetx,platformdef->mesh->offsety,platformdef->mesh->offsetz);

	if(platformdef->flags & PLATFLAG_VARISPEED)
	{
		platformdef->vari_speeds = ADD2POINTER(platformdef->vari_speeds,(int)platformdef);
	}

// RFW - test for pushables that "aren't pushable by anyone", and make em pushable by everyone.
// (tbd - this should be in cr_lev, really)
	if(platformdef->flags & PLATFLAG_PUSHABLE)
	{
		if((platformdef->definfo.pushable.push_flags & (PUSHABLE_BY_GINGER | PUSHABLE_BY_ROCKY | PUSHABLE_BY_NICK | PUSHABLE_BY_FETCHER)) == 0)
		{
			platformdef->definfo.pushable.push_flags |= (PUSHABLE_BY_GINGER | PUSHABLE_BY_ROCKY | PUSHABLE_BY_NICK | PUSHABLE_BY_FETCHER);
		}

	}
// Note - this reverses the order of the platforms
// They also get reversed on loading into the game.
// Thus, platform order is preserved.


	work = MALLOC(sizeof(PLATFORMWORK));
	memset(work,0,sizeof(PLATFORMWORK));

	work->active = 1;
	work->move.pointCtr=-1;
	work->def = platformdef;

	work->pos.vx = work->oldPos.vx = platformdef->start_x;	//platformdef->points[0].posx;
	work->pos.vy = work->oldPos.vy = platformdef->start_y;	//platformdef->points[0].posy;
	work->pos.vz = work->oldPos.vz = platformdef->start_z;	//platformdef->points[0].posz;

	work->instance.rotx = 0;
	work->instance.roty = 0;
	work->instance.rotz = 0;
	work->instance.rotw = 4096;
	work->instance.radius = platformdef->mesh->radius;
	work->instance.sort_depth = platformdef->def_sort_depth;
	work->instance.mesh = platformdef->mesh;

	if(platformdef->crcSample)
	{
		work->noisy.pSample = sfxFindSampleInAllBanksCRC(platformdef->crcSample);
		DB("Platform has a sound. CRC = %x, sample addr = %p\n", platformdef->crcSample, work->noisy.pSample);
		ASSERT(work->noisy.pSample);
	}

	work->next = platform_list;
	platform_list = work;

// Check for collision in the BFF with the same CRC as the platform
// (Don't complain if it's not there - just generate)

	{
		COLL_BFF *bffcoll;
		int i;
		bffcoll = (COLL_BFF *)BFF_FindObject(BFF_COLL_ID, platformdef->crc);
		if(bffcoll)
		{
			void *temp;
			COLLBOX *collBox;
			COLLBOX *firstRamp = NULL;
			int numCollBoxes;
			int offMesh;
			ULONG *pM;

			temp = ADD2POINTER(bffcoll,bffcoll->base_mesh_offset);
			numCollBoxes = *(int *)temp;
			collBox = ADD2POINTER(temp,4);
			offMesh=(sizeof(COLLBOX)*numCollBoxes)+sizeof(ULONG);

			platformdef->coll_bounds.nCollBoxes = *(int *)ADD2POINTER(bffcoll,bffcoll->base_mesh_offset);
			platformdef->coll_bounds.pBoxes = ADD2POINTER(bffcoll,bffcoll->base_mesh_offset + 4);
		}
	}


// Generate a bounding-box collision mesh if it's not got one
	if(platformdef->coll_bounds.pBoxes)
	{
// (work->generated_collision = 0)
	}
	else
	{
		if(!(platformdef->flags & PLATFLAG_NOCOLLISION))
		{
			boundbox = MALLOC(sizeof(COLLBOX));
			memset(boundbox,0,sizeof(COLLBOX));

#ifdef DREAMCAST
			boundbox->pX = (platformdef->mesh->minx + platformdef->mesh->offsetx)<<9;
			boundbox->pY = (platformdef->mesh->maxy + platformdef->mesh->offsety)<<9;	// posY = max
			boundbox->pZ = (platformdef->mesh->minz + platformdef->mesh->offsetz)<<9;
#else
			boundbox->pX = (platformdef->mesh->minx)<<9;
			boundbox->pY = (platformdef->mesh->maxy)<<9;	// posY = max
			boundbox->pZ = (platformdef->mesh->minz)<<9;
#endif
			boundbox->sX = (platformdef->mesh->maxx - platformdef->mesh->minx)<<9;
			boundbox->sY = (platformdef->mesh->miny - platformdef->mesh->maxy)<<9;	// -ve sizeY
			boundbox->sZ = (platformdef->mesh->maxz - platformdef->mesh->minz)<<9;
			boundbox->flags = COLLBOXFLAG_MOVE;

			platformdef->coll_bounds.pBoxes = boundbox;
			platformdef->coll_bounds.nCollBoxes = 1;
			work->generated_collision = 1;	// So we know that we need to free it.
		}
	}


// Figure min & max of the full coll-box set, and from there figure the radius
	{
		VECTOR tot_min;
		VECTOR tot_max;

		tot_min.vx = platformdef->coll_bounds.pBoxes[0].pX;
		tot_min.vy = platformdef->coll_bounds.pBoxes[0].pY + platformdef->coll_bounds.pBoxes[0].sY;
		tot_min.vz = platformdef->coll_bounds.pBoxes[0].pZ;
		tot_max.vx = platformdef->coll_bounds.pBoxes[0].pX + platformdef->coll_bounds.pBoxes[0].sX;
		tot_max.vy = platformdef->coll_bounds.pBoxes[0].pY;
		tot_max.vz = platformdef->coll_bounds.pBoxes[0].pZ + platformdef->coll_bounds.pBoxes[0].sZ;
		for(i = 1; i < platformdef->coll_bounds.nCollBoxes; i++)
		{
			if(tot_min.vx < platformdef->coll_bounds.pBoxes[0].pX) tot_min.vx = platformdef->coll_bounds.pBoxes[0].pX;
			if(tot_min.vy < platformdef->coll_bounds.pBoxes[0].pY + platformdef->coll_bounds.pBoxes[0].sY) tot_min.vy = platformdef->coll_bounds.pBoxes[0].pY + platformdef->coll_bounds.pBoxes[0].sY;
			if(tot_min.vz < platformdef->coll_bounds.pBoxes[0].pZ) tot_min.vz = platformdef->coll_bounds.pBoxes[0].pZ;
			if(tot_max.vx > platformdef->coll_bounds.pBoxes[0].pX + platformdef->coll_bounds.pBoxes[0].sX) tot_max.vx = platformdef->coll_bounds.pBoxes[0].pX + platformdef->coll_bounds.pBoxes[0].sX;
			if(tot_max.vy > platformdef->coll_bounds.pBoxes[0].pY) tot_max.vy = platformdef->coll_bounds.pBoxes[0].pY;
			if(tot_max.vz > platformdef->coll_bounds.pBoxes[0].pZ + platformdef->coll_bounds.pBoxes[0].sZ) tot_max.vz = platformdef->coll_bounds.pBoxes[0].pZ + platformdef->coll_bounds.pBoxes[0].sZ;
		}
		platformdef->coll_bounds.minx = tot_min.vx;
		platformdef->coll_bounds.miny = tot_min.vy;
		platformdef->coll_bounds.minz = tot_min.vz;

		platformdef->coll_bounds.maxx = tot_max.vx;
		platformdef->coll_bounds.maxy = tot_max.vy;
		platformdef->coll_bounds.maxz = tot_max.vz;

		if(ABS(tot_min.vx) < ABS(tot_max.vx)) tot_max.vx = tot_min.vx;
		if(ABS(tot_min.vy) < ABS(tot_max.vy)) tot_max.vy = tot_min.vy;
		if(ABS(tot_min.vz) < ABS(tot_max.vz)) tot_max.vz = tot_min.vz;

		work->coll_radius = Magnitude(&tot_max);
	}


// End of test
//	work->def->flags = PLATFLAG_ROTATING;
//	work->def->definfo.rotating.speed = 8;
//	work->def->definfo.rotating.axis = 1;
}

PLATFORMWORK *platformFindPlatform(int tag)
{
	PLATFORMWORK *platform;
	for(platform = platform_list; platform; platform = platform->next)
	{
		if(platform->def->tag == tag)
			return platform;
	}
	return NULL;
}


// After BFF-loading
void platformSetup()
{
	PLATFORMWORK *platform;

// Go through the platform list, filling in parent & child pointers
	for(platform = platform_list; platform; platform = platform->next)
	{
		if(platform->def->parenttag)
		{
			PLATFORMWORK *parent;

			parent = platformFindPlatform(platform->def->parenttag);
			if(parent)
			{
				platform->pParentPlat = parent;
				platform->pNextChildPlat = parent->pFirstChildPlat;
				parent->pFirstChildPlat  = platform;
			}
		}

/*
// testcode to turn everything into a conveyor belt. Well, that seems to work ok
		platform->def->flags |= PLATFLAG_CONVEYOR;
		platform->def->definfo.conveyor.speed = 30;
		platform->def->definfo.conveyor.ya = 0;
*/


		if(platform->def->flags & PLATFLAG_CONVEYOR)
		{
			platform->info.conveyor.speed      = 0;	//platform->def->definfo.conveyor.speed;
			platform->info.conveyor.targ_speed = platform->def->definfo.conveyor.speed;

			if(platform->def->definfo.conveyor.texanim)
			{
				platform->def->definfo.conveyor.texanim = texanimFind((int)(platform->def->definfo.conveyor.texanim));
			}
		}



// Make sure the collision data's got valid position & old-position data to work from
// (note - unlike the normal position-updating code, this also checks non-active platforms)
 		if(!platform->pParentPlat)
		{
			COPYVECTOR(&platform->cumPos, &platform->pos);
			if(platform->pFirstChildPlat)
			{
				platformUpdateChildPosns(platform);
			}
		}
		platform->oldPos = platform->cumPos;

		{
			// If we've asked for the platform to have a specific terrain type,
			// ensure all collboxes within the platform match that terrain type.
			// (If we ain't asked for a platform terrain type, allow the coll-mesh to have lots of different terrtypes)
			int temp;
			temp = platform->def->terrain;
			if(temp)
			{
				platform->def->terrain = -1;	// coz SetTerrain checks src==dest
				platformSetTerrain(platform, temp);
			}
		}

	}

}

void platformShutdown()
{
	PLATFORMWORK *work;
// empty the platform linked list
	while(platform_list)
	{
		work = platform_list;
		platform_list = work->next;
		if(work->generated_collision)
		{
			FREE(work->def->coll_bounds.pBoxes);
		}
		FREE(work);

	}
// ends with platform_list = NULL
}




void platformDraw()
{
	FMA_MESH_HEADER *mesh;
	PLATFORMWORK *platform;

	for(platform = platform_list; platform; platform = platform->next)
	{
		if(platform->active && platform->def->mesh)
		{
/*
			platform->instance.posx = platform->cumPos.vx;
			platform->instance.posy = platform->cumPos.vy;
			platform->instance.posz = platform->cumPos.vz;
*/
// Displaying the plat at its old pos may seem a bit odd, but
// it means that we can do the "moving platform moves entity" stuff in the collision code

#ifdef DREAMCAST
			platform->instance.posx = platform->oldPos.vx + platform->instance.mesh->offsetx;
			platform->instance.posy = platform->oldPos.vy + platform->instance.mesh->offsety;
			platform->instance.posz = platform->oldPos.vz + platform->instance.mesh->offsetz;
#else
			platform->instance.posx = platform->oldPos.vx;
			platform->instance.posy = platform->oldPos.vy;
			platform->instance.posz = platform->oldPos.vz;
#endif

			MapDraw_DrawFMA_Mesh(&platform->instance);
		}
	}

	overlayShowCollision();

}


void platformMoveRoundPath(PLATFORMWORK *ptr)
{
	PLATFORMDEF *def;

	def = ptr->def;
	ptr->move.jap = 0;

	if(def->n_points < 2)
		return;


	if((ptr->move.waitTimer==0) && (ptr->move.pointCtr!=0))
	{
		VECTOR *pTo;
/*
// Movement sound effect
		if(ptr->def->head.maxspeed)	// don't play move sfx on a "moving at speed 0" platform
		{
			if(!(ptr->moveticker % 8))
			{
				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++;
		}
*/

//Figure which point we're moving to
		if(ptr->move.speed >= 0)
		{
			pTo = (VECTOR *)&(def->points[(ptr->move.point+1)%def->n_points].posx);
		}
		else
		{
			pTo = (VECTOR *)&(def->points[(ptr->move.point+def->n_points-1)%def->n_points].posx);
		}

// Accelerate
		if(def->accel)
		{
			ptr->move.speed += def->accel;
			if(ptr->move.speed > def->maxspeed)
				ptr->move.speed = def->maxspeed;
		}
		else
		{
			ptr->move.speed = def->maxspeed;
		}

		ptr->move.moving=TRUE;

/*
		printf("pos = %d,%d,%d. Dest = %d,%d,%d\n",
			ptr->pos.vx,ptr->pos.vy,ptr->pos.vz,
			pTo->vx,pTo->vy,pTo->vz);
*/
		if(VectorMoveTo(&ptr->pos, pTo, ABS(ptr->move.speed)))
		{
// Things to do when we get there

			ptr->move.moving=FALSE;
			ptr->move.jap=TRUE;	// because we're just at the point

// For yo-yo-ing platforms, just duplicate the points
			if(ptr->move.speed >= 0)
			{
				ptr->move.point++;
				if(ptr->move.point == def->n_points)
					ptr->move.point = 0;
			}
			else
			{
				ptr->move.point--;
				if(ptr->move.point < 0)
					ptr->move.point = def->n_points-1;
// Glover reset the move-speed to +ve
			}

			if(ptr->move.waitTimer != 0)
			{
				ptr->move.speed=0;
			}

// If it's got variable movement amounts at each point... then set the max-speed accordingly
			if(def->flags & PLATFLAG_VARISPEED)
			{
				def->maxspeed = def->vari_speeds[ptr->move.point].speed;
				def->accel = def->vari_speeds[ptr->move.point].accel;
				DB("spd,acc -> %d %d\n",def->maxspeed,def->accel);
			}
			



			ptr->move.waitTimer = def->points[ptr->move.point].wait;

// If it's got a point counter, count it down
			if(ptr->move.pointCtr > 0)
				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
				)
			{
				sfxPlayParsed(
					&(ptr->pos),
					ptr->pPlatDef->head.stop_sfx,
					ptr->pPlatDef->head.stop_pitch,
					ptr->pPlatDef->head.stop_vol
					);
			}
*/
		}


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

// Tbd - If the chicken's on the platform, move it (Deal with conveyors while we're at it)
// Same "extra movement" thang as we had with glover & the enemies
// Tbd - If any pies are on the platform, move them. You get the picture


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


void platformUpdateChildPosns(PLATFORMWORK *parent)
{
	PLATFORMWORK *child;

	for(child = parent->pFirstChildPlat; child; child = child->pNextChildPlat)
	{
		if(child->active)
		{
			ADDVECTOR(&child->cumPos, &parent->cumPos, &parent->pos); 
			if(child->pFirstChildPlat)
			{
				platformUpdateChildPosns(child);
			}
		}
	}
}


int platformAtPoint(PLATFORMWORK *platform, int p)
{
	return(    platform->pos.vx == platform->def->points[p].posx
		&& platform->pos.vy == platform->def->points[p].posy
		&& platform->pos.vz == platform->def->points[p].posz
	);
}

void platformStopPushable(PLATFORMWORK *platform)
{
	platform->info.pushable.pushvec.vx = 0;
	platform->info.pushable.pushvec.vy = 0;
	platform->info.pushable.pushvec.vz = 0;
}

void platformPush(PLATFORMWORK *platform)
{
	VECTOR *want;
	VECTOR plat;
	int dot;
	VECTOR newpos;
	int there;
	int rqd_speed;

	want = &platform->info.pushable.pushvec;	// the way we want it to go

	plat.vx = platform->def->points[1].posx - platform->def->points[0].posx;	// 0 -> 1 for the platform
	plat.vy = platform->def->points[1].posy - platform->def->points[0].posy;
	plat.vz = platform->def->points[1].posz - platform->def->points[0].posz;
	MakeUnit(&plat);

// dot prod, = +4096 if we're pushing straight at point 1
	dot = (want->vx * plat.vx + want->vy * plat.vy + want->vz * plat.vz)>>12;

// Things still to do:-
// DUN - The eggroom platforms need to stop if their progress is impeded by another platform.
// Indeed, they also need to stop if their progress is impeded by a static character...

// Pushable plats move at the speed they're being pushed, or at their max speed, whichever is lower.
	rqd_speed = platform->def->maxspeed;

	if(!(platform->def->definfo.pushable.push_flags & PUSHABLE_KEEPS_GOING))
	{
		CHARACTER *pC;
		VECTOR *charvel;
		int charspeed;
		pC = characterVars.pMainChar;
		charvel = &pC->pEnemy->vel;
		charspeed = (charvel->vx * plat.vx + charvel->vy * plat.vy + charvel->vz * plat.vz) >> 12;
		if(charspeed < 0)
			charspeed = -charspeed;

		if(charspeed < rqd_speed)
			rqd_speed = charspeed;
		DB("charspeed = %d, defspeed = %d, rqdspeed = %d\n",charspeed,platform->def->maxspeed,rqd_speed);
	}


	if(dot < -3000)
	{
		newpos = platform->pos;
		there = VectorMoveTo(&newpos, (VECTOR *)&platform->def->points[0], rqd_speed);
		if(platformIsFreeToMove(platform,&newpos))
		{
//			printf("free. gone. speed = %d\n",platform->def->maxspeed);
			platform->pos = newpos;
		}
		else
		{
			platformStopPushable(platform);
//			printf("not free\n");
		}
	}
	else if (dot > 3000)
	{
		newpos = platform->pos;
		there = VectorMoveTo(&newpos, (VECTOR *)&platform->def->points[1], rqd_speed);
		if(platformIsFreeToMove(platform,&newpos))
		{
//			printf("free. gone. speed = %d\n",platform->def->maxspeed);
			platform->pos = newpos;
		}
		else
		{
			platformStopPushable(platform);

//			printf("not free\n");
		}
	}
	else
	{
// we're pushing it in a direction it can't be pushed
//		printf("not enough dot\n");
	}

// Does it need to be pushed all the way, or does it keep going ?
	if(!(platform->def->definfo.pushable.push_flags & PUSHABLE_KEEPS_GOING))
	{
		platformStopPushable(platform);
	}
}

int platformIsFreeToMove(PLATFORMWORK *platform, VECTOR *newpos)
{
	PLATFORMWORK *other;
	PLATFORMDEF *thisdef;
	PLATFORMDEF *otherdef;
	VECTOR diff;

	thisdef = platform->def;

//	printf("checking ftm\n");

	for(other = platform_list; other; other = other->next)
	{
		otherdef = other->def;
		if(other != platform
			&& (otherdef->flags & PLATFLAG_PUSHABLE)
			&& (other->active)	// act2 barn bales
			)	// only test against other pushables, for speed
		{
			int lo1,lo2,hi1,hi2;
// need to do a bound-box check of this one's new posn, other one's exisiting posn

			diff.vx = (newpos->vx - other->pos.vx)<<9;	// move into coll-space
			diff.vy = (newpos->vy - other->pos.vy)<<9;
			diff.vz = (newpos->vz - other->pos.vz)<<9;

			lo1 = thisdef->coll_bounds.minx;
			lo2 = diff.vx + otherdef->coll_bounds.minx;
			hi1 = thisdef->coll_bounds.maxx;
			hi2 = diff.vx + otherdef->coll_bounds.maxx;

			if(hi2 <= lo1) continue;
			if(hi1 <= lo2) continue;

			lo1 = thisdef->coll_bounds.minz;
			lo2 = diff.vz + otherdef->coll_bounds.minz;
			hi1 = thisdef->coll_bounds.maxz;
			hi2 = diff.vz + otherdef->coll_bounds.maxz;
			if(hi2 <= lo1) continue;
			if(hi1 <= lo2) continue;

			lo1 = thisdef->coll_bounds.miny;
			lo2 = diff.vy + otherdef->coll_bounds.miny;
			hi1 = thisdef->coll_bounds.maxy;
			hi2 = diff.vy + otherdef->coll_bounds.maxy;
			if(hi2 <= lo1) continue;
			if(hi1 <= lo2) continue;

			return 0;
		}
	}
	return 1;
}

int platformWillPush(PLATFORMWORK *platform, VECTOR *pushdirn)
{
	VECTOR *want;
	VECTOR plat;
	int dot;
	VECTOR newpos;
	int there;
	int	charsOK;
	CHARACTER	*pC = characterGetMainCharacter();

	charsOK  = ((platform->def->definfo.pushable.push_flags & PUSHABLE_BY_ROCKY) && (pC->pEnemy->readData.nameCRC == CRC_ROCKY));
	charsOK |= ((platform->def->definfo.pushable.push_flags & PUSHABLE_BY_GINGER) && (pC->pEnemy->readData.nameCRC == CRC_GINGER));
	charsOK |= ((platform->def->definfo.pushable.push_flags & PUSHABLE_BY_NICK) && (pC->pEnemy->readData.nameCRC == CRC_NICK));
	charsOK |= ((platform->def->definfo.pushable.push_flags & PUSHABLE_BY_FETCHER) && (pC->pEnemy->readData.nameCRC == CRC_FETCHER));

	want = pushdirn;	// the way we want it to go

	plat.vx = platform->def->points[1].posx - platform->def->points[0].posx;	// 0 -> 1 for the platform
	plat.vy = platform->def->points[1].posy - platform->def->points[0].posy;
	plat.vz = platform->def->points[1].posz - platform->def->points[0].posz;

	MakeUnit(&plat);

	dot = (want->vx * plat.vx + want->vy * plat.vy + want->vz * plat.vz)>>12;


	if(dot < -3000)
	{
		if(CompareVectors(&platform->pos,(VECTOR *)(&platform->def->points[0])))
		{
//			printf("At p0. Not moving any further\n");
			return PLATWILLPUSH_ENDOFRUN;
		}
		else
		{
			newpos = platform->pos;
			there = VectorMoveTo(&newpos, (VECTOR *)&platform->def->points[0], platform->def->maxspeed);
			if(platformIsFreeToMove(platform,&newpos))
			{
				if(!charsOK)
					return PLATWILLPUSH_WRONGCHAR;
				else
					return PLATWILLPUSH_OK;
			}
			else
			{
				return PLATWILLPUSH_BLOCKED;
			}
		}
	}
	else if (dot > 3000)
	{
		if(CompareVectors(&platform->pos,(VECTOR *)(&platform->def->points[1])))
		{
//			printf("At p1. Not moving any further\n");
			return PLATWILLPUSH_ENDOFRUN;
		}
		else
		{
			newpos = platform->pos;
			there = VectorMoveTo(&newpos, (VECTOR *)&platform->def->points[1], platform->def->maxspeed);
			if(platformIsFreeToMove(platform,&newpos))
			{
				if(!charsOK)
					return PLATWILLPUSH_WRONGCHAR;
				else
					return PLATWILLPUSH_OK;
			}
			else
			{
				return PLATWILLPUSH_BLOCKED;
			}
		}
	}
/*
	printf("Not enough dot product %d.want = %d %d %d. Dirn = %d %d %d\n",
		dot,
		want->vx,want->vy,want->vz,
		plat.vx,plat.vy,plat.vz);
*/
	return PLATWILLPUSH_NOT_ON_AXIS;
}



void platformCycle()
{
	PLATFORMWORK *platform;

// Run through the platforms, telling each to do what it's supposed to do
	for(platform = platform_list; platform; platform = platform->next)
	{
		if(platform->active)
		{
			PLATFORMWORK *parent;

// RFW - "correct character for pushing" check moved from here to the collision's "PlatformWillPush" test

			SUBVECTOR(&platform->move.realVel, &platform->oldPos, &platform->cumPos);

//			ptr->oldPos = ptr->pos;
			platform->oldPos = platform->cumPos;

			if(platform->def->flags & PLATFLAG_PUSHABLE)
			{
				if(platform->info.pushable.pushvec.vx || platform->info.pushable.pushvec.vz)
				{
					platformPush(platform);
				}
			
				// Set stuff up for the puzzle engine to potentially do meshswaps
				if(platformAtPoint(platform, 0))
				{
					platform->move.point = 0;
					platform->move.waitTimer = 2;
				}
				else if(platformAtPoint(platform, 1))
				{
					platform->move.point = 1;
					platform->move.waitTimer = 2;
				}
				else
				{
					platform->move.waitTimer = 0;
				}
			}
			else
			{
				platformMoveRoundPath(platform);
			}

			if(platform->def->flags & PLATFLAG_ROTATING)
			{
				switch(platform->def->definfo.rotating.axis)
				{
				case PLATROTATE_XAXIS:
						platform->instance.rotx += platform->def->definfo.rotating.speed;
						break;
				case PLATROTATE_YAXIS:
						platform->instance.roty += platform->def->definfo.rotating.speed;
						break;
				case PLATROTATE_ZAXIS:
						platform->instance.rotz += platform->def->definfo.rotating.speed;
						break;
				}
			}

			if(platform->def->flags & PLATFLAG_CONVEYOR)
			{
				int speed,targ;
				speed = platform->info.conveyor.speed;
				targ = platform->info.conveyor.targ_speed;
				if(speed != targ)
				{
					if(speed < targ)
					{
//						printf("conveyor: speed = %d, targ = %d, accel = %d\n",
//							speed,targ,platform->def->definfo.conveyor.accel);

						speed += platform->def->definfo.conveyor.accel;
						if(speed > targ) speed = targ;
					}
					else
					{
						speed -= platform->def->definfo.conveyor.accel;
						if(speed < targ) speed = targ;
					}
					platform->info.conveyor.speed = speed;

					if(platform->def->definfo.conveyor.texanim)
					{
						texanimSetSpeed(platform->def->definfo.conveyor.texanim, 
							speed
							* platform->def->definfo.conveyor.texanim_speedratio_tex
							/ platform->def->definfo.conveyor.texanim_speedratio_world
							);
					}
				}
			}

			platform->noisy.touchCounter++;
		}
	}


// Updating the positions in hierachical fashion is left till after all the movement's happened,
// and is done from the parents down, rather than in platform order. This ensures that regardless
// of platform order, none of the children lag.
	for(platform = platform_list; platform; platform = platform->next)
	{
		if(platform->active && !platform->pParentPlat)	// Look for root-level active platforms, & go down their child collection
		{
			COPYVECTOR(&platform->cumPos, &platform->pos);
			if(platform->pFirstChildPlat)
			{
				platformUpdateChildPosns(platform);
			}
		}
	}
}



void platformSetSpeed(PLATFORMWORK *platform, int speed)
{
	platform->move.speed = speed;
	platform->def->maxspeed = speed;
}

void platformSetRotSpeed(PLATFORMWORK *platform, int speed)
{
	platform->def->definfo.rotating.speed = speed;
}


void platformSetConveyorSpeed(PLATFORMWORK *platform, int speed)
{
	platform->info.conveyor.targ_speed = speed;
}

void platformSetTerrain(PLATFORMWORK *plat, int terrain)
{
	int i;
	static int remaps[] =
	{
		0,
		COLLBOXFLAG_STICKY,
		COLLBOXFLAG_SLIPPY,
		COLLBOXFLAG_SPRINGY,
		COLLBOXFLAG_BOUNCY,
		COLLBOXFLAG_HARMFUL,	
		COLLBOXFLAG_DEADLY,
		COLLBOXFLAG_NOISY
	};


	if(terrain < 8 && remaps[terrain] != -1 && terrain != plat->def->terrain)
	{
		for(i = 0; i < plat->def->coll_bounds.nCollBoxes; i++)
		{
			plat->def->coll_bounds.pBoxes[i].flags = (plat->def->coll_bounds.pBoxes[i].flags & ~COLLBOXFLAG_TERRAINMASK) | remaps[terrain];
		}
		plat->def->terrain=terrain;
	}
}

// Not only set a platform's existance to "off", but also throw up some junk at the same time...
// debris versions of the same platform? smoke?
// (oh, and play a sound effect)

void platformDestroy(PLATFORMWORK *plat)
{
	plat->active = 0;
}

void platformStopCurrentPushSample(void)
{
	CHARACTER *pC = characterGetMainCharacter();

	// CPW: Make sure if there's a push sound playing, mute it
	if(pC->movement.pPushPlat)
	{
		PLATFORMWORK *pPlat = pC->movement.pPushPlat;

		if(pPlat->noisy.pSample && pPlat->noisy.playing)
		{
			DB("Stopping current push sample\n");
			soundStopSample(pPlat->noisy.pSample);
		}
	}
}
