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

// The "max" is used for the temporary buffer for parsing in individual puzzles
// Once loaded, they only take up as much memory as they need.
// (the fruit machine runs to 4 conditions & 9 actions at one point)
#define PUZZLE_MAX_ACTIONS		20
#define PUZZLE_MAX_CONDITIONS	15

ACTION    *puzzleActionBuffer;
CONDITION *puzzleConditionBuffer;

PUZZLE *puzzles;
int puzzle_condition_counter;
int puzzle_action_counter;
char puzzle_andor;
short puzzle_numtimes;

void puzzleFlushPuzzle();
//BOOL IsCameraAction(SHORT type);
//BOOL IsCameraCondition(SHORT type);

PUZZLEVARS PuzzleVars;

SHORT	short1,short2;
// ==========================================

void puzzleBeginPuzzle()
{
	puzzle_condition_counter = 0;
	puzzle_action_counter = 0;
	FMEMZERO(puzzleActionBuffer,PUZZLE_MAX_ACTIONS * sizeof(ACTION));
	FMEMZERO(puzzleConditionBuffer,PUZZLE_MAX_CONDITIONS * sizeof(CONDITION));
	puzzle_andor = 0;
	puzzle_numtimes = -1;
}


// grab a temporary buffer for parsing in a single puzzle piecemeal, reset variables
// Yes, the temporary buffer is used. Yes it does need to be mallocced. Think for more than a minute or so
// before commenting out these mallocs. Luv, Fred.

void puzzleBeginFile()
{
	puzzleActionBuffer = MALLOC(PUZZLE_MAX_ACTIONS * sizeof(ACTION),"action loader");
	puzzleConditionBuffer = MALLOC(PUZZLE_MAX_CONDITIONS * sizeof(CONDITION),"condition loader");
	puzzles = NULL;
	puzzleBeginPuzzle();
}
// add the last puzzle loaded to the game, & free the temporary buffer 
void puzzleEndFile()
{
	puzzleFlushPuzzle();
	FREE(puzzleConditionBuffer);
	FREE(puzzleActionBuffer);
	FMEMZERO(&PuzzleVars.user_variables,20 * sizeof(int));
}

// After finishing playing the level, free up the memory we've grabbed
void puzzleFreePuzzles()
{
	PUZZLE *next;

	while(puzzles)
	{
		next = puzzles->next;
		FREE(puzzles);
		puzzles = next;
	}

//	puzzles = NULL;		// this happens above anyway
}

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

// Having parsed a puzzle in, malloc a "just the right size" bit of memory, & copy it in
void puzzleFlushPuzzle()
{
	PUZZLE *puzzle;
	int size;

	if(!puzzle_condition_counter && !puzzle_action_counter)	// no puzzle has been parsed yet
		return;

	size = sizeof(PUZZLE);
	size += puzzle_condition_counter * sizeof(CONDITION);
	size += puzzle_action_counter * sizeof(ACTION);
	puzzle = MALLOC(size,"puzzle");

	puzzle->conditions = ADD2POINTER(puzzle,sizeof(PUZZLE));
	puzzle->actions = ADD2POINTER(puzzle->conditions,puzzle_condition_counter * sizeof(CONDITION));

	puzzle->numconditions = puzzle_condition_counter;
	puzzle->numactions = puzzle_action_counter;
	puzzle->andor		= puzzle_andor;
	puzzle->numtimes	= puzzle_numtimes;
	puzzle->checkactions = NO;

	memcpy(puzzle->conditions,puzzleConditionBuffer, puzzle->numconditions * sizeof(CONDITION));
	memcpy(puzzle->actions,puzzleActionBuffer, puzzle->numactions * sizeof(ACTION));


	puzzle->next = puzzles;
	puzzles = puzzle;

	puzzleBeginPuzzle();
}
// ==========================================


int puzzleParseTokenPuzzle(short token,UBYTE **data)
{
//	SHORT subtoken;

	switch(token)
	{
	case TOKEN_PUZZLE:
		puzzleFlushPuzzle();	// flush out the previous one
		return YES;

	case TOKEN_PUZZLE_AND:
		puzzle_andor = CONDITION_AND;
		return YES;
	case TOKEN_PUZZLE_OR:
		puzzle_andor = CONDITION_OR;
		return YES;
	case TOKEN_PUZZLE_ANY:
		PRINTF("The ANY puzzle condition is not supported\n");	// coz it wasn't used in the n64 source AFAICT
		CRASH;
		return YES;
	case TOKEN_PUZZLE_NUMTIMES:
		puzzle_numtimes = *(short *)(*data);
		*data += 2;
		PRINTF("Puzzle NUMTIMES %d\n",puzzle_numtimes);
		return YES;

	case TOKEN_CONDITION:
	{
		CONDITION *cond;

		if(puzzle_condition_counter >= PUZZLE_MAX_CONDITIONS)
		{
			printf("*** PUZZLE CONDITION OVERFLOW ***\n");
			return YES;
		}

		cond = &puzzleConditionBuffer[puzzle_condition_counter];
		puzzle_condition_counter++;

		cond->type = (*(short *)(*data)) - TOKEN_CONDITION_AT_POINT;
		*data += 2;

		switch(cond->type)
		{
			case CONDITION_DESTROYED:
				cond->data.tag.tag = *(short *)(*data);
				*data += 4;	// the short "value" at (data+2) doesn't mean anything
				return YES;

// can't find a reference to this anywhere apart from the psx enum
			case CONDITION_PICKUP_ON:
				*data += 4;
				return YES;

			case CONDITION_COLLECTED:

			case CONDITION_NME_DEAD:

			case CONDITION_AT_POINT:
			case CONDITION_AT_SPIN:
			case CONDITION_AT_ORBIT:
			case CONDITION_JUST_AT_POINT:

			case CONDITION_JUST_AT_SPIN:
			case CONDITION_JUST_AT_ORBIT:
				
			case CONDITION_HAND_ON:
			case CONDITION_BALL_ON:
			case CONDITION_NME_ON:
			case CONDITION_HAND_JUST_ON:
			case CONDITION_BALL_JUST_ON:
			case CONDITION_NME_JUST_ON:

			case CONDITION_HAND_TOUCHING:
			case CONDITION_BALL_TOUCHING:
			case CONDITION_NME_TOUCHING:
			case CONDITION_HAND_JUST_TOUCHED:
			case CONDITION_BALL_JUST_TOUCHED:
			case CONDITION_NME_JUST_TOUCHED:

			case CONDITION_CONFINED:
			case CONDITION_JUST_CONFINED:

			case CONDITION_VAR_EQUAL:	// with these, the "tag" is a puzzle variable ID, rather than an object tag
			case CONDITION_VAR_NOT_EQUAL:
			case CONDITION_VAR_GREATER_THAN:
			case CONDITION_VAR_LESS_THAN:

				cond->data.tagcheck.tag = *(short *)(*data + 0);
				cond->data.tagcheck.check = *(short *)(*data + 2);
				*data += 4;
				return YES;

			case CONDITION_JUST_AT_ROT:
				cond->data.tagcheck.tag = *(short *)(*data + 0);
				cond->data.tagcheck.check = (*(short *)(*data + 2))&0xfff;
				*data += 6;
				return YES;

// obv, this is going to be back-to-front again...
			case CONDITION_HAND_IN_BOX:
			case CONDITION_BALL_IN_BOX:
			case CONDITION_CAM_IN_BOX:
//			case CONDITION_CAMERA_IN_BOX:
				cond->data.in_box.pos.vx =  *(short *)(*data +0);
				cond->data.in_box.pos.vy =  -(*(short *)(*data +4) + *(short *)(*data +16));
				cond->data.in_box.pos.vz =  -(*(short *)(*data +8) + *(short *)(*data +20));
				cond->data.in_box.size.vx = *(short *)(*data +12);
				cond->data.in_box.size.vy = *(short *)(*data +16);
				cond->data.in_box.size.vz = *(short *)(*data +20);

// a bit of a botch, using the Z-high, but it'll do
				cond->data.in_box.yes = *(char *)(*data +22);

				*data += 24;
				return YES;

			case CONDITION_HAND_IN_RANGE:
			case CONDITION_BALL_IN_RANGE:
//			case CONDITION_CAMERA_IN_RANGE:
				cond->data.in_range.pos.vx =  *(short *)(*data +0);
				cond->data.in_range.pos.vy = -*(short *)(*data +4);
				cond->data.in_range.pos.vz = -*(short *)(*data +8);
				cond->data.in_range.range  =  *(short *)(*data +12);
				*data += 16;
				return YES;

			case CONDITION_HAND_CROSSES_LINE:
				cond->data.crosses_line.p1.vx =  *(short *)(*data +0);
				cond->data.crosses_line.p1.vy = -*(short *)(*data +4);
				cond->data.crosses_line.p1.vz = -*(short *)(*data +8);
				cond->data.crosses_line.p2.vx =  *(short *)(*data +12);
				cond->data.crosses_line.p2.vy = -*(short *)(*data +16);
				cond->data.crosses_line.p2.vz = -*(short *)(*data +20);
				cond->data.crosses_line.swing =  *(short *)(*data +24);
				*data += 28;
				return YES;

			case CONDITION_HAND_IN_CYLINDER:
				cond->data.in_cylinder.pos.vx =  *(short *)(*data +0);
				cond->data.in_cylinder.pos.vy = -*(short *)(*data +4);
				cond->data.in_cylinder.pos.vz = -*(short *)(*data +8);
				cond->data.in_cylinder.radius =  *(short *)(*data +12);
				cond->data.in_cylinder.height =  *(short *)(*data +16);
				*data += 20;
				return YES;

			default:
				PRINTF("Woah! Unknown puzzle condition code dude!\n");
				return YES;
		}
	}

	case TOKEN_ACTION:
	{
		ACTION *act;

// for all those that add "2" at the end...
// 2 = delay

// for all those that add "12" at the end...
// 4 = "value"
// 2 = "target tag"
// 2 = "delay"
// 4 = "flags"

		if(puzzle_action_counter >= PUZZLE_MAX_ACTIONS)
		{
			PRINTF("*** PUZZLE ACTION OVERFLOW !!! ***\n");
			return YES;
		}

		act = &puzzleActionBuffer[puzzle_action_counter];
		puzzle_action_counter++;

		act->type = (*(short *)(*data)) - TOKEN_ACTION_START_MOVE;
		*data += 2;
		act->timer = -1;
		act->delay = 0;
		act->flags = 0;

		switch(act->type)
		{
			case ACTION_CAM_HEIGHT:		// eg, the fear bonus
			case ACTION_CAM_PARALLEL:	// eg, the fear bonus	(doesn't actually use the value, btw)
			case ACTION_CAM_DISTANCE:	// eg, the generalwu area on atlantis1
			case ACTION_SET_BACKDROP:
				//act->data.amount.value = (int)(*(short *)(*data)) + ((int)(*(short *)(*data+2)) <<16);
				if (act->type==ACTION_CAM_HEIGHT)
				{
					DB ("debug\n");
				}
				short1=(int)(*(short *)(*data));
				//short2=((int)(*(short *)(*data+2)) <<16);
				short2=0;
				act->data.amount.value=(short1+short2);
				act->delay = *(short *)(*data + 4);
				*data += 4+2;
				return YES;

				/*short1=(int)(*(short *)(*data));
				short2=((int)(*(short *)(*data+2)) <<16);
				act->data.amount.value=short1+short2;
				act->delay = *(short *)(*data + 4);
				*data += 4+2;
				return YES;
*/

			case ACTION_CAM_CORNER:	// both of these two just have a delay
			case ACTION_CAM_IGNORE_SLOPES:
				act->delay = *(short *)(*data + 0);
				*data += 2;
				return YES;


			case ACTION_SET_CAMERA_POS:	// loads of unused params...
				act->data.vector.vec.vx = *(short *)(*data + 0);
				act->data.vector.vec.vy = -*(short *)(*data + 4);
				act->data.vector.vec.vz = -*(short *)(*data + 8);
				act->delay              = *(short *)(*data + 18);
				*data += 12+12;
				return YES;


			case ACTION_LOOK_AT_POINT:	// at1, after slamming the button
			case ACTION_LOOK_AT_PLAT:	// This doesn't actually use the flags, but does use the rest
			case ACTION_DROP_PICKUP:
			case ACTION_ADD_NME:
			case ACTION_MOVE_PLAT:
			case ACTION_SET_CONVEYOR:
				act->data.vector_tv.vec.vx = *(short *)(*data + 0);
				act->data.vector_tv.vec.vy = -*(short *)(*data + 4);
				act->data.vector_tv.vec.vz = -*(short *)(*data + 8);

				act->data.vector_tv.value = *(short *)(*data + 12);
				act->data.vector_tv.tag   = *(short *)(*data + 16);
				act->delay                = *(short *)(*data + 18);
				act->flags                = *(short *)(*data + 20);

				*data += 12+12;
				return YES;


// non-camera actions with no additional data

			case ACTION_SET_CAM_DIST:	// these also have an unused "tag" value
			case ACTION_SET_CAM_HEIGHT:
				act->data.amount.value = *(short *)(*data + 0);
				act->delay             = *(short *)(*data + 6);
				act->flags             = *(short *)(*data + 8);
				*data += 12;
				return YES;



// Lovely, set_terrain's been saved with the tag & value the opposite way round to the others
			case ACTION_SET_TERRAIN:
				act->data.val_tag_flags.tag   = *(short *)(*data + 0);
				act->data.val_tag_flags.value = *(short *)(*data + 4);
				act->delay                    = *(short *)(*data + 6);
				act->flags                    = *(short *)(*data + 8);

				ASSERT(act->data.val_tag_flags.value<16);

				*data += 12;
				return YES;

			case ACTION_SET_VAR:
			case ACTION_ADDTO_VAR:
			case ACTION_SUBFROM_VAR:

			case ACTION_DROP_GARIBS:	// flags = loop counter, tag = tag, value = ypos
			case ACTION_START_SPIN:
			case ACTION_START_ORBIT:

			case ACTION_SET_NEXT_POINT:
			case ACTION_SET_NEXT_SPIN:
			case ACTION_SET_SPIN_WAIT:
			case ACTION_SET_SPIN_AXIS:
			case ACTION_SET_STATE:
			case ACTION_SET_EXISTANCE:
			case ACTION_SET_GRAVITY:

			case ACTION_NME_GOTO_ACTION:

			case ACTION_SET_WIND:
			case ACTION_SET_WATERLEVEL:
			case ACTION_SET_LEVEL_GRAVITY:

			case ACTION_START_CAMEO:
			case ACTION_SET_PLAT_DIR:
			case ACTION_UNFREEZE_BALL:
			case ACTION_ADD_TO_SCORE:
			case ACTION_START_MOVE:
//				act->data.val_tag_flags.value = *(short *)(*data + 0);
				act->data.val_tag_flags.value  = *((u_short *)(*data+0));
				act->data.val_tag_flags.value |= (*((u_short *)(*data+2)))<<16;

				act->data.val_tag_flags.tag   = *(short *)(*data + 4);
				act->delay                    = *(short *)(*data + 6);
				act->flags                    = *(short *)(*data + 8);
				*data += 12;
				return YES;


			case ACTION_EARTHQUAKE:
				act->data.quake.info[0] = *(short *)(*data + 0);	// duration
				act->data.quake.info[1] = *(short *)(*data + 2);	// max_strength
				act->data.quake.info[2] = *(short *)(*data + 4);	// min_strength
				act->data.quake.info[3] = *(short *)(*data + 6);	// cycles

				act->data.quake.value = *(short *)(*data + 8);
				act->data.quake.tag   = *(short *)(*data + 12);
				act->delay            = *(short *)(*data + 14);		// flags
				act->flags            = *(short *)(*data + 16);		// delay

				*data += 8+12;
				return YES;

			case ACTION_START_SOUND:
				act->data.sound.info[0] = *(short *)(*data + 0);
				act->data.sound.info[1] = *(short *)(*data + 2);
				act->data.sound.info[2] = *(short *)(*data + 4);
				act->data.sound.info[3] = *(short *)(*data + 6);
				act->data.sound.info[4] = *(short *)(*data + 8);
				act->data.sound.info[5] = *(short *)(*data + 10);

				act->data.sound.value = *(short *)(*data + 12);
				act->data.sound.tag   = *(short *)(*data + 14);

				act->data.sound.vec.vx = *(short *)(*data + 16);
				act->data.sound.vec.vy = *(short *)(*data + 20);
				act->data.sound.vec.vz = *(short *)(*data + 24);

				act->delay            = *(short *)(*data + 28);

				*data += 16+12 + 2;
				return YES;



			case ACTION_CAM_FIXEDROT:
				act->data.fixedrot.value = *(short *)(*data + 0);
				act->data.fixedrot.speed = *(short *)(*data + 4);
				act->delay            = *(short *)(*data + 8);
				*data += 8 + 2;
				return YES;

			case ACTION_CAM_FIXEDPOS:
			case ACTION_CAM_CIRCLE:
				act->data.vector.vec.vx = *(short *)(*data + 0);
				act->data.vector.vec.vy = *(short *)(*data + 4);
				act->data.vector.vec.vz = *(short *)(*data + 8);
//				act->delay              = *(short *)(*data + 12);
				act->delay              = *(short *)(*data + 14);
				*data += 14 + 2;
				return YES;

			case ACTION_SET_FOG:
				act->data.fog.info[0] = *(short *)(*data + 0);
				act->data.fog.info[1] = *(short *)(*data + 2);
				act->data.fog.info[2] = *(short *)(*data + 4);
				act->data.fog.info[3] = *(short *)(*data + 6);
				act->data.fog.info[4] = *(short *)(*data + 8);
				act->delay            = *(short *)(*data + 10);
				*data += 10 + 2;
				return YES;

			case ACTION_DESTROY:
				{
					ULONG crc;

					act->data.destruct.pieces	= *(short *)(*data + 4);
					act->data.destruct.tag		= *(short *)(*data + 6);
					act->delay					= *(short *)(*data + 8);

					// value used to contain CRC of model name
					crc=*((u_short *)(*data+0));
					crc|=(*((u_short *)(*data+2)))<<16;

//					DB("crc = %08x\n", crc);
					act->data.destruct.pModel=BFF_IsCRCModelLoaded(crc, NULL);


					ASSERT(act->data.destruct.pModel);
					// now it contains a pointer to the model name ...

					*data +=10;
					return YES;
				}

			case ACTION_TELEPORT_TO:
				act->data.teleport.flags		=*(short *)(*data +0);
				act->data.teleport.disappear	=*(short *)(*data +2);
				act->data.teleport.appear		=*(short *)(*data +4);
				act->data.teleport.dest.vx		=(  *(short *)(*data + 8) )<<12;
				act->data.teleport.dest.vy		=(-(*(short *)(*data +12)))<<12;	// n64->psx vector conversion
				act->data.teleport.dest.vz		=( (*(short *)(*data +16)))<<12;	// n64 -ve, plus genesis display -ve
			
				*data +=20;
				return YES;

			default:
				PRINTF("Woah! Unknown puzzle action code dude!\n");
				return YES;
		}
	}


	default:
		return NO;
	}
}





// =================================================================================
// Having done all the parsing stuff, let's try to actually do stuff, huh?


void puzzleDrawBbox(SVECTOR *pos, SVECTOR *size)
{
static	Point3DType points[4];

	points[0].x = pos->vx;
	points[0].y = pos->vy;
	points[0].z = pos->vz;

	points[1].x = pos->vx;
	points[1].y = pos->vy;
	points[1].z = pos->vz + size->vz;

	points[2].x = pos->vx + size->vx;
	points[2].y = pos->vy;
	points[2].z = pos->vz + size->vz;

	points[3].x = pos->vx + size->vx;
	points[3].y = pos->vy;
	points[3].z = pos->vz;
	lscapeDrawBoundPoly3D(4, &points[0], 0x00,0xff,0xff);

	points[0].y = points[1].y = points[2].y = points[3].y = pos->vy + size->vy;
	lscapeDrawBoundPoly3D(4, &points[0], 0x00,0xff,0xff);
}



int puzzleConditionMet(CONDITION *cond)
{
	switch(cond->type)
	{
		case CONDITION_BALL_JUST_ON:
		{
			DYNCOLLBOX *pBox;
//			PLATFORM_DEFSTR *pDef;
			if(!cond->data.tagcheck.pBox)
			{
				loadlndFindPlatform(cond->data.tagcheck.tag, NULL, &cond->data.tagcheck.pBox);
			}
			pBox = cond->data.tagcheck.pBox;

			if(ballColl.nHitPlats && (pBox==ballColl.hitPlats[0].pPlatform))
			{
				if((frame-pBox->ballTouchFrame)>6)
				{
					PuzzleVars.ball_trigger =1;
//					DB("CONDITION_BALL_JUST_ON\n");
					pBox->ballTouchFrame=frame;
					return YES;
				}
				else
				{
					pBox->ballTouchFrame=frame;
					return NO;

				}
			}
			return NO;
		}

		case CONDITION_BALL_ON:
		{
			DYNCOLLBOX *pBox;

			if(!cond->data.tagcheck.pBox)
			{
				loadlndFindPlatform(cond->data.tagcheck.tag, NULL, &cond->data.tagcheck.pBox);
			}
			pBox = cond->data.tagcheck.pBox;

			if(ballColl.nHitPlats)
			{
				if(pBox==ballColl.hitPlats[0].pPlatform)
				{
					PuzzleVars.ball_trigger =1;
//					DB("CONDITION_BALL_ON\n");
					return YES;
				}
			}

			return NO;
		}

		case CONDITION_HAND_JUST_ON:
		{
			DYNCOLLBOX *pBox;

			if(!cond->data.tagcheck.pBox)
			{
				loadlndFindPlatform(cond->data.tagcheck.tag, NULL, &cond->data.tagcheck.pBox);
			}
			pBox = cond->data.tagcheck.pBox;

			if(gloveColl.nHitPlats)
			{
				if((pBox==gloveColl.hitPlats[0].pPlatform) && (pBox->handTouchTime==1))
				{
					PuzzleVars.hand_trigger =1;
//					DB("CONDITION_HAND_JUST_ON\n");
					return YES;
				}
			}
			return NO;
		}

		case CONDITION_HAND_ON:
		{
			DYNCOLLBOX *pBox;

			if(!cond->data.tagcheck.pBox)
			{
				loadlndFindPlatform(cond->data.tagcheck.tag, NULL, &cond->data.tagcheck.pBox);
			}
			pBox = cond->data.tagcheck.pBox;

			if(gloveColl.nHitPlats)
			{
				if(pBox==gloveColl.hitPlats[0].pPlatform)
				{
					PuzzleVars.hand_trigger =1;
//					DB("CONDITION_HAND_ON\n");
					return YES;
				}
			}

			return NO;
		}

		case CONDITION_DESTROYED:	// The N64 always returned NO, and had garibs inside the box
		{
			DYNCOLLBOX *pBox;

//			printf("destroyed %d? ",cond->data.tag.tag);
			if(!cond->data.tag.pBox)
			{
				loadlndFindPlatform(cond->data.tag.tag, NULL, &cond->data.tag.pBox);
				if(!cond->data.tag.pBox)
				{
					return YES;
				}
			}
			pBox = cond->data.tag.pBox;

			if(pBox->active)
			{
				return NO;
			}
			else
			{
				return YES;
			}
		}

		case CONDITION_NME_DEAD:
		{
			ENEMYPOS *nme;

			if(!cond->data.tagcheck.nme)
			{
				cond->data.tagcheck.nme = loadlndFindEnemy(cond->data.tagcheck.tag);
				if(!cond->data.tagcheck.nme)
				{
					DB("NOT Found enemy (%d) for deadcheck\n", cond->data.tagcheck.tag);
					return NO;
				}
			}
			nme = cond->data.tagcheck.nme;
//			DB("dead check tag %d, check %d = %d\n",cond->data.tagcheck.tag,cond->data.tagcheck.check,nme->flags);
			if(cond->data.tagcheck.check)
			{
				return((nme->flags & NMEFLAG_DEAD) != 0);
			}
			else
			{
				return((nme->flags & NMEFLAG_DEAD) == 0);
			}
		}

		case CONDITION_HAND_IN_BOX:
		{
			SVECTOR temp;
			temp.vx = pGlovePSA->position.vx - cond->data.in_box.pos.vx;
			temp.vy = pGlovePSA->position.vy - cond->data.in_box.pos.vy;
			temp.vz = pGlovePSA->position.vz - cond->data.in_box.pos.vz;

//			puzzleDrawBbox(&cond->data.in_box.pos,&cond->data.in_box.size);

			if( temp.vx >= 0 && temp.vx < cond->data.in_box.size.vx
			 && temp.vy >= 0 && temp.vy < cond->data.in_box.size.vy
			 && temp.vz >= 0 && temp.vz < cond->data.in_box.size.vz
			 )
			{
				PuzzleVars.hand_trigger =1;
//				DB("CONDITION_HAND_IN_BOX\n");
				return (cond->data.in_box.yes);
			}
			return(!cond->data.in_box.yes);
		}

		case CONDITION_BALL_IN_BOX:
		{
			SVECTOR temp;
			temp.vx = pBallPSA->position.vx - cond->data.in_box.pos.vx;
			temp.vy = pBallPSA->position.vy - cond->data.in_box.pos.vy;
			temp.vz = pBallPSA->position.vz - cond->data.in_box.pos.vz;

//			puzzleDrawBbox(&cond->data.in_box.pos,&cond->data.in_box.size);

			if( temp.vx >= 0 && temp.vx < cond->data.in_box.size.vx
			 && temp.vy >= 0 && temp.vy < cond->data.in_box.size.vy
			 && temp.vz >= 0 && temp.vz < cond->data.in_box.size.vz
			 )
			{
				PuzzleVars.ball_trigger =1;
				return (cond->data.in_box.yes);
			}
			return(!cond->data.in_box.yes);
		}
		case CONDITION_CAM_IN_BOX:
		{
			SVECTOR temp;
			temp.vx = CamVars.camera.vpx - cond->data.in_box.pos.vx;
			temp.vy = CamVars.camera.vpy - cond->data.in_box.pos.vy;
			temp.vz = CamVars.camera.vpz - cond->data.in_box.pos.vz;

//			puzzleDrawBbox(&cond->data.in_box.pos,&cond->data.in_box.size);

			if( temp.vx >= 0 && temp.vx < cond->data.in_box.size.vx
			 && temp.vy >= 0 && temp.vy < cond->data.in_box.size.vy
			 && temp.vz >= 0 && temp.vz < cond->data.in_box.size.vz
			 )
			{
				return (cond->data.in_box.yes);
			}
			return(!cond->data.in_box.yes);
		}

		case CONDITION_AT_POINT:
		{
			DYNCOLLBOX *pBox;
			if(!cond->data.tagcheck.pBox)
			{
				loadlndFindPlatform(cond->data.tagcheck.tag, NULL, &cond->data.tagcheck.pBox);
			}
			pBox = cond->data.tagcheck.pBox;

			return(puzzleAtPoint(pBox, cond->data.tagcheck.check));
		}

		case CONDITION_JUST_AT_POINT:
		{
			DYNCOLLBOX *pBox;
			if(!cond->data.tagcheck.pBox)
			{
				loadlndFindPlatform(cond->data.tagcheck.tag, NULL, &cond->data.tagcheck.pBox);
			}
			pBox = cond->data.tagcheck.pBox;
			return(puzzleJustAtPoint(pBox, cond->data.tagcheck.check));
		}

		case CONDITION_JUST_AT_ROT:
		{
			DYNCOLLBOX *pBox;
			PLATFORM_DEFSTR *pDef;

			//DB("JUST_AT_ROT?\n");

			if(!cond->data.tagcheck.pBox)
			{
				loadlndFindPlatform(cond->data.tagcheck.tag, NULL, &cond->data.tagcheck.pBox);
				ASSERT(cond->data.tagcheck.pBox);
			}
			pBox = cond->data.tagcheck.pBox;

			pDef = pBox->pPlatDef;

//			DB("pBox=%p. pDef=%p\n", pBox, pDef);
			//DB("progress= %d, check= %d\n", pBox->spin.progress, cond->data.tagcheck.check); 
			
			if((pDef->head.spinpause.yes) && (ABS(pBox->spin.progress-cond->data.tagcheck.check)<8) && (pBox->spinpause.jar))
			{
				//DB("CONDITION_JUST_AT_ROT (spin)\n");
				return YES;
			}
			else
			{
				if((ABS((pBox->topple.progress&4095)-cond->data.tagcheck.check)<8) && (pBox->topple.jar) /*(pBox->topple.falling==2)*/)
				{
					//DB("CONDITION_JUST_AT_ROT (topple)\n");
					return YES;
				}
			}
			
			return NO;
		}

		case CONDITION_BALL_JUST_TOUCHED:	// Pi1, PR2,PR3. No I don't know the diff between this and "just_on"
		case CONDITION_NME_JUST_TOUCHED:	// prehist 3

		case CONDITION_VAR_EQUAL:			// ca1 fruit machine only
		case CONDITION_VAR_NOT_EQUAL:		// again, fruit-machine only
		case CONDITION_VAR_GREATER_THAN:	// ca1 only, and I assume it's fruit-machine related
		case CONDITION_CONFINED:			// ff1
		case CONDITION_JUST_CONFINED:		// ff2 ca1




		case CONDITION_AT_ORBIT:		// unused
		case CONDITION_JUST_AT_SPIN:	//unused
		case CONDITION_JUST_AT_ORBIT:	// unused

		case CONDITION_NME_ON:			// unused
		case CONDITION_NME_JUST_ON:		// unused

		case CONDITION_HAND_TOUCHING:	// unused
		case CONDITION_HAND_JUST_TOUCHED:	// unused
		case CONDITION_BALL_TOUCHING:		// unused

		case CONDITION_VAR_LESS_THAN:		// unused

		case CONDITION_NME_TOUCHING:		// unused

		case CONDITION_HAND_CROSSES_LINE:	// unused
		case CONDITION_HAND_IN_RANGE:		// unused
		case CONDITION_BALL_IN_RANGE:		// unused
		case CONDITION_CAM_IN_RANGE:		// unused
		case CONDITION_HAND_IN_CYLINDER:	// unused
		case CONDITION_COLLECTED:			// unused
		case CONDITION_PICKUP_ON:			// unused
			return NO;
	}
	return NO;
}

// Fred sez:-
// I've made a note of the "parameters that the parser reads in" alongside the un-filled-in puzzles...
// Vectors are shorts (ie, not scaled to 4096). Not all the numbers in the LND file are used:- the
// "act->data" union contains all those that are.
// ("delay", used in most puzzles, is info for the puzzle parser, rather than anything else)
void puzzleDoAction(ACTION *act)
{
	DYNCOLLBOX *pBox;

	switch(act->type)
	{
// ----- Camera puzzle actions -----
// (note, camera cameo actions are another thing again...)


// Set the camera distance for this frame only
//  eg - AT1 GeneralWu when in a "box area"
		case ACTION_CAM_DISTANCE:
			CamVars.flags|=ADDITIONALDISTANCE;
			CamVars.additionalDistance=act->data.amount.value;
// This appears to be working
			PuzzleVars.any_camera_overrides = 1;
			PuzzleVars.camera_additional_distance = act->data.amount.value;
			break;

// Set the camera height for this frame only
//	eg - FearBonus does this in an "always true" puzzle
		case ACTION_CAM_HEIGHT:
			//goto LABEL_ACTION_SET_CAM_HEIGHT;
			CamVars.flags|=ADDITIONALHEIGHT;
			CamVars.additionalHeight = -act->data.amount.value;
// This appears to be working
			PuzzleVars.any_camera_overrides = 1;
			PuzzleVars.camera_additional_height = act->data.amount.value;
			break;

// Set the camera parallel flag for this frame only
//  eg - FearBonus does this in an "always true" puzzle
		case ACTION_CAM_PARALLEL:
			PuzzleVars.any_camera_overrides = 1;
			PuzzleVars.camera_parallel_flag = 1;
			CamVars.flags|=PARALLEL;
			CamVars.parallelAngle=CamVars.angle;
			break;

// Set the camera position to a vector for this frame only
// eg - carnival 1 & 2 , when in a box area outside some of the doors
		case ACTION_CAM_FIXEDPOS:

			if (CamVars.flags&SETFIXEDPOSITION) break;
			PuzzleVars.any_camera_overrides = 1;
			CamVars.flags|=FIXEDPOSITION;
			CamVars.fixedPos.vx=act->data.vector_tv.vec.vx;
			CamVars.fixedPos.vy=-act->data.vector_tv.vec.vy;
			CamVars.fixedPos.vz=act->data.vector_tv.vec.vz;
			break;

// called in a box by the carnival 1 icecream door, & also in a box in pirate1
		case ACTION_CAM_CIRCLE:
			PuzzleVars.any_camera_overrides = 1;
			CamVars.flags|=CIRCLE;
			CamVars.circlePos.vx=act->data.vector.vec.vx;
			CamVars.circlePos.vz=act->data.vector.vec.vy;
//			act->data.vector.vec.vx
//			act->data.vector.vec.vy
//			act->data.vector.vec.vz
			break;


// Set the camera rotation to a "value & speed" for this frame only
// eg - carnival 1 does this in a box. atlantis bonus does it in an always true puzzle
		case ACTION_CAM_FIXEDROT:
//			DB("ACTION_CAM_FIXEDROT  ");
			PuzzleVars.any_camera_overrides = 1;
//			DB("value=%d, speed=%d\n", act->data.fixedrot.value, act->data.fixedrot.speed);

			CamVars.flags|=FIXEDANGLE;
			CamVars.fixedAngle=4096-act->data.fixedrot.value;
			CamVars.rotationSpeed=act->data.fixedrot.speed;
			break;


// I can't find an LND file with this action in it (!)
		case ACTION_CAM_CORNER:
// (the corner action is just an action - it has no parameters)
			break;

// I can only find one occurence of this one:-
// AT2, where it would be called when within a box, but is is commented out
		case ACTION_CAM_IGNORE_SLOPES:
// (the ignore slopes action is just an action - it has no parameters)
			break;



// Set the camera position for this frame only (locking
// eg - at1, after hitting the button, for a short amount of time
// (the time comes from the fact that a "set camera pos" is *always* follwed by a "look at point")
		case ACTION_SET_CAMERA_POS:
			PuzzleVars.any_camera_overrides = 1;

			CamVars.flags|=SETFIXEDPOSITION;
			CamVars.fixedPos.vx=act->data.vector_tv.vec.vx;
			CamVars.fixedPos.vy=act->data.vector_tv.vec.vy;
			CamVars.fixedPos.vz=-act->data.vector_tv.vec.vz;

//	  		act->data.vector.vec.vx
//	  		act->data.vector.vec.vy
//	  		act->data.vector.vec.vz
			break;

// Set the camera target for a given number of frames
// eg - at1, after hitting the button, for a short amount of time (again).
// "time" comes from the "value", and maintaining the override for this time is left to the camera code
		case ACTION_LOOK_AT_POINT:
			PuzzleVars.any_camera_overrides = 1;
//			DB("Look at point (%d,%d,%d) for %d microFortnights\n",
//				act->data.vector_tv.vec.vx, act->data.vector_tv.vec.vy, act->data.vector_tv.vec.vz, act->data.vector_tv.value);
 
			CamVars.flags|=FIXEDLOOKAT;
			CamVars.lookAtPos.vx=act->data.vector_tv.vec.vx;
			CamVars.lookAtPos.vy=act->data.vector_tv.vec.vy;
			CamVars.lookAtPos.vz=-act->data.vector_tv.vec.vz;

			PuzzleVars.camera_fixed_endtime=iframe+(act->data.vector_tv.value<<8);

//	  		act->data.vector_tv.vec.vx
//	  		act->data.vector_tv.vec.vy
//	  		act->data.vector_tv.vec.vz
//	  		act->data.vector_tv.value 
			break;

// hmmmm. can't find this one used anywhere
		case ACTION_LOOK_AT_PLAT:
//			DB("ACTION_LOOK_AT_PLAT (%d, %d)\n", act->data.vector_tv.tag, act->data.vector_tv.value);
			PuzzleVars.any_camera_overrides = 1;

			if(!act->data.vector_tv.pBox)
			{
				loadlndFindPlatform(act->data.vector_tv.tag, NULL, &act->data.vector_tv.pBox);
			}
			pBox = act->data.vector_tv.pBox;

//			CamVars.flags|=(FIXEDLOOKAT+SETFIXEDPOSITION);

			CamVars.flags|=(LOOKATPLAT|FIXEDPOSITION);

/*			CamVars.lookAtPos.vx=pBox->cumPos.vx/4096;
			CamVars.lookAtPos.vy=pBox->cumPos.vy/4096;
			CamVars.lookAtPos.vz=pBox->cumPos.vz/4096;
*/
			CamVars.fixedPos.vx=act->data.vector_tv.vec.vx;
			CamVars.fixedPos.vy=act->data.vector_tv.vec.vy;
			CamVars.fixedPos.vz=act->data.vector_tv.vec.vz;

			CamVars.pLookAt=&(pBox->cumPos);

			//PuzzleVars.camera_fixed_endtime=iframe+(act->data.vector_tv.value<<8);
			DB("ticks = %d\n",act->data.vector_tv.value*180);
			PuzzleVars.camera_fixed_endtime=iframe+(act->data.vector_tv.value*180);

			break;

// Override the "normal" camera distance, permanantly (ie, this isn't an "any_camera_overrides" affair)
// eg - a once-only puzzle in at-boss
		case ACTION_SET_CAM_DIST:
			//CamVars.flags|=ADDITIONALDISTANCE;
			//CamVars.additionalDistance=act->data.amount.value;
			
			
			CamVars.flags|=FIXEDDISTANCE;
			CamVars.fixedDistance=act->data.amount.value;
// This appears to be working
			PuzzleVars.any_camera_overrides = 1;
			PuzzleVars.camera_additional_distance = act->data.amount.value;

//			DB("ACTION_SET_CAM_DIST\n");
//	  		act->data.amount.value
			break;

// Override the "normal" camera height, permanantly (ie, this isn't an "any_camera_overrides" affair)
// eg - a once-only puzzle in at-boss, conditional on destroying one of the bad guys
		case ACTION_SET_CAM_HEIGHT:
//LABEL_ACTION_SET_CAM_HEIGHT:
			if(act->data.amount.value != -1)
			{
				CamVars.flags|=FIXEDHEIGHT;
				CamVars.fixedHeight=act->data.amount.value;
			}
			else
			{
				CamVars.flags&=~FIXEDHEIGHT;
				CamVars.fixedHeightFlag=FALSE;
				CamVars.fixedHeight=0;	//act->data.amount.value;
			}

//			DB("ACTION_SET_CAM_HEIGHT\n");
//			act->data.amount.value
			break;

		case ACTION_EARTHQUAKE:
//				act->data.quake.info[0]
//				act->data.quake.info[1]
//				act->data.quake.info[2]
//				act->data.quake.info[3]
// info is "duration, maxstrength, minstrength, numcycles", flags

			weatherStartEarthquake(	100, 32,
									10, 10, 0);
//									EARTHQUAKE_MOVECAMERA | EARTHQUAKE_MOVEPLAYER);
			break;

		case ACTION_START_MOVE:
		{
			// start platform moving ...
			puzzleStartMove(act->data.val_tag_flags.tag, act->flags, act->data.val_tag_flags.value);
//			if(!act->data.val_tag_flags.pBox)
//			{
//				loadlndFindPlatform(act->data.val_tag_flags.tag, NULL, &act->data.val_tag_flags.pBox);
//			}
//			platformStartMove(act->data.val_tag_flags.pBox,act->flags,act->data.val_tag_flags.value);
			break;
		}

		case ACTION_START_SPIN:
		{
			DYNCOLLBOX *pBox;

			if(!act->data.val_tag_flags.pBox)
			{
				loadlndFindPlatform(act->data.val_tag_flags.tag, NULL, &act->data.val_tag_flags.pBox);
			}
			pBox = act->data.val_tag_flags.pBox;
			ASSERT(pBox);
			ASSERT(pBox->pPlatDef->head.spin.yes);

			pBox->pPlatDef->head.spin.rotspeed=act->data.val_tag_flags.value;
			pBox->spinpause.timer=-1;
			break;
		}

		case ACTION_START_ORBIT:
			break;
		case ACTION_SET_NEXT_POINT:
			break;
		case ACTION_SET_NEXT_SPIN:
			break;
		case ACTION_SET_SPIN_WAIT:
			break;
		case ACTION_SET_SPIN_AXIS:
			break;

		case ACTION_SET_STATE:
//			DB("ACTION_SET_STATE (%d, %d)\n", act->data.val_tag_flags.tag, act->data.val_tag_flags.value);
			if(!act->data.val_tag_flags.pBox)
			{
				loadlndFindPlatform(act->data.val_tag_flags.tag, NULL, &act->data.val_tag_flags.pBox);
			}
			pBox = act->data.val_tag_flags.pBox;
			if(pBox->pPlatDef->head.magnet.yes)
			{
				pBox->magnet.active=act->data.val_tag_flags.value;
			}
			break;

		case ACTION_SET_CONVEYOR:
			break;

		case ACTION_SET_EXISTANCE:
//			puzzleSetExistence(act->data.val_tag_flags.tag, act->data.val_tag_flags.value);
			if(!act->data.val_tag_flags.found)
			{
				loadlndFindPlatform(act->data.val_tag_flags.tag, NULL, &act->data.val_tag_flags.pBox);
				if(act->data.val_tag_flags.pBox)
					act->data.val_tag_flags.found = 1;

				act->data.val_tag_flags.nme = loadlndFindEnemy(act->data.val_tag_flags.tag);
				if(act->data.val_tag_flags.nme)
					act->data.val_tag_flags.found = 2;

				if(!act->data.val_tag_flags.found)
					act->data.val_tag_flags.found = 3;
			}

			switch(act->data.val_tag_flags.found)
			{
			case 1:
				if(act->data.val_tag_flags.value == 0)
				{
					act->data.val_tag_flags.pBox->active = 0;
					//platformSmash(pBox);
				}
				else
				{
					act->data.val_tag_flags.pBox->active = 1;
				}

				break;
			case 2:
				if(act->data.val_tag_flags.value == 0)
				{
					act->data.val_tag_flags.nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
				}
				else
				{
					act->data.val_tag_flags.nme->flags |= (NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
				}

				break;
// last couple of alternatives still scan for tags
			case 3:
				waterEnable(act->data.val_tag_flags.tag, act->data.val_tag_flags.value);	// ok if it doesn't exist
				EnableGaribGroup(act->data.val_tag_flags.tag, act->data.val_tag_flags.value); // enable or disable

				break;
			}
			break;

		case ACTION_SET_GRAVITY:
			gravity=act->data.val_tag_flags.value;
			break;

		
		case ACTION_SET_TERRAIN:
			//DB("ACTION_SET_TERRAIN %d, %d\n", act->data.val_tag_flags.tag, act->data.val_tag_flags.value);
//			lscapeSetTerrainTag(act->data.val_tag_flags.tag, act->data.val_tag_flags.value);


			if(!act->data.val_tag_flags.found)
			{
				loadlndFindPlatform(act->data.val_tag_flags.tag, NULL, &act->data.val_tag_flags.pBox);
				if(act->data.val_tag_flags.pBox)
					act->data.val_tag_flags.found = 1;
				else
				{
					if(pWld)
					{
						int i;
						for(i = 0; i < pWld->nZones; i++)
						{
							if(pWld->pZones[i].tag == act->data.val_tag_flags.tag)
							{
								act->data.val_tag_flags.pBox = (DYNCOLLBOX *)&pWld->pZones[i];
								act->data.val_tag_flags.found = 2;
							}
						}
					}
					break;
				}
			}



			switch(act->data.val_tag_flags.found)
			{
			case 1:
				lscapeSetTerrainPlat(act->data.val_tag_flags.pBox, act->data.val_tag_flags.value);
				act->data.val_tag_flags.pBox->pPlatDef->head.terrain=act->data.val_tag_flags.value;
				break;
			case 2:
				((SCAPEMODEL *)(act->data.val_tag_flags.pBox))->terrain = act->data.val_tag_flags.value;
				break;

			}

			break;

// I was using set_var whilst testing the terrain stuff until the proper puzzle's in. Luv, Fred
// ...And now I'm using it to detect the exits from the wayroom
		case ACTION_SET_VAR:
//			lscapeSetTerrainTag(act->data.val_tag_flags.tag, act->data.val_tag_flags.value);
			PuzzleVars.user_variables[act->data.val_tag_flags.tag] = act->data.val_tag_flags.value;
			break;

		case ACTION_ADDTO_VAR:
			break;
		case ACTION_SUBFROM_VAR:
			break;
		case ACTION_DROP_PICKUP:
			break;
		case ACTION_ADD_NME:
			break;
		case ACTION_MOVE_PLAT:
			break;
		case ACTION_NME_GOTO_ACTION:
			break;

		case ACTION_SET_WIND:
			windSetValues(act->data.val_tag_flags.tag, act->data.val_tag_flags.value);
			break;

		case ACTION_DROP_GARIBS:
			DB("drop garib group %d to 1\n",act->data.val_tag_flags.tag);
//			EnableGaribGroup(act->data.val_tag_flags.tag, 1);
			DropGaribGroup(act->data.val_tag_flags.tag, - act->data.val_tag_flags.value);	// tbd - set the height gtom the value field
			break;

		case ACTION_SET_WATERLEVEL:
			waterSetDepth(act->data.val_tag_flags.tag,-act->data.val_tag_flags.value << 12);	// note - compensation for n64 -ve Y
			break;
		case ACTION_SET_LEVEL_GRAVITY:
			gravity=act->data.val_tag_flags.value;
			break;

		case ACTION_START_CAMEO:
//				act->data.val_tag_flags.value = *(short *)(*data + 0);
//				act->data.val_tag_flags.tag   = *(short *)(*data + 4);
			cameoStart(act->data.val_tag_flags.value);
			break;

		case ACTION_SET_PLAT_DIR:
			break;
		case ACTION_UNFREEZE_BALL:
			break;
		case ACTION_START_SOUND:
			break;
		case ACTION_SET_BACKDROP:
			break;
		case ACTION_SET_FOG:
			break;
		case ACTION_ADD_TO_SCORE:
			break;
		case ACTION_DESTROY:
//			DB("ACTION_DESTROY (%d)\n", act->data.destruct.tag);
			loadlndFindPlatform(act->data.destruct.tag, NULL, &pBox);
			ASSERT(pBox);

			//DB("pBox->platNumber = %d\n", pBox->platNumber);

			pPlatsLoaded[pBox->platNumber].pFragModel=(NEWMODEL *)(act->data.destruct.pModel);

			ASSERT(pPlatsLoaded[pBox->platNumber].pFragModel);
			pBox->pPlatDef->head.destruct.nBits=act->data.destruct.pieces;

			platformSmash(pBox);
			break;

		case ACTION_TELEPORT_TO:
		{
			int val = 0;
			if(PuzzleVars.ball_trigger)
				val = 1;
/*
			DB("ACTION_TELEPORT (%d, %d, %d)\n",
				act->data.teleport.dest.vx,
				act->data.teleport.dest.vy,
				act->data.teleport.dest.vz);
*/
//void teleportTrigger(int which,VECTOR *dest,int flags,int diss,int app, int ya)

			teleportTrigger(
				val,
				&act->data.teleport.dest,
				act->data.teleport.flags,
				act->data.teleport.disappear,
				act->data.teleport.appear,
				0
				);
			break;
		}
	}
}


void puzzleCheckPuzzles()
{
	PUZZLE *puzzle;
	CONDITION *condition;
	ACTION *action;
	int i;
	int valid;

	PuzzleVars.any_camera_overrides = 0;
	PuzzleVars.camera_additional_distance = 0;
	PuzzleVars.camera_additional_height = 0;
	PuzzleVars.camera_parallel_flag = 0;

	PuzzleVars.ball_trigger = 0;
	PuzzleVars.hand_trigger = 0;

//	CamVars.flags=NULL; // Clear all flags before start of puzzle loop (I think)
//	if(iframe<PuzzleVars.camera_fixed_endtime)
//	{
//		CamVars.flags|=(FIXEDLOOKAT|SETFIXEDPOSITION);
//	}

	if(iframe<PuzzleVars.camera_fixed_endtime)
	{
		CamVars.flags&=(FIXEDLOOKAT|SETFIXEDPOSITION|FIXEDPOSITION|LOOKATPLAT);
	}
	else
	{
		CamVars.flags=NULL;
	}

	for(puzzle = puzzles; puzzle; puzzle = puzzle->next)
	{
		if(!puzzle->numtimes)
		{
			valid = 0;
		}
		else
		{
//			if(puzzle->numtimes != -1)
//				puzzle->numtimes--;

			if(puzzle->andor == CONDITION_AND)
			{
				valid = 1;
				condition = puzzle->conditions;
				for(i = puzzle->numconditions; i > 0; i--,condition++)
				{
					if(!puzzleConditionMet(condition))
					{
						valid = 0;
						break;
					}
				}
			}
			else
			{
				valid = 0;
				condition = puzzle->conditions;
				for(i = puzzle->numconditions; i > 0; i--,condition++)
				{
					if(puzzleConditionMet(condition))
					{
						valid = 1;
						break;
					}
				}
			}
		}

		if(valid)
		{
			action = puzzle->actions;

			puzzle->checkactions = YES;

			for(i = puzzle->numactions; i > 0; i--,action++)
			{
				if(action->timer == -1)
				{
					if(action->flags & ACTION_SPECIAL)
						action->timer = action->delay + random(6)*5;
					else
						action->timer = action->delay;
				}
			}
			if(puzzle->numtimes != -1)
				puzzle->numtimes--;

		}



	}

// okay, now let's deal with delayed actions....

	for(puzzle = puzzles; puzzle; puzzle = puzzle->next)
	{
		if(puzzle->checkactions)
		{
			int num = 0;
			action = puzzle->actions;

			for(i = puzzle->numactions; i > 0; i--,action++)
			{
				if(action->timer != -1)
				{
					if(action->timer == 0)
					{
						puzzleDoAction(action);

					}
					action->timer--;
				}
				else 
				{
					num++;
				}
			}
			if(num == puzzle->numactions)
			{
				puzzle->checkactions = NO;
			}
		}
	}
}

void puzzleSetExistence(int tag, int value) // yes this is how you spell existence
{
	DYNCOLLBOX *pBox;
	ENEMYPOS *nme;

	waterEnable(tag, value);	// ok if it doesn't exist
//	DB("ACTION_SET_EXISTENCE (%d, %d)\n", tag, value);

	loadlndFindPlatform(tag, NULL, &pBox);
	if(pBox)
	{
		if(value == 0)
		{
			pBox->active = 0;
			//platformSmash(pBox);
		}
		else
		{
			pBox->active = 1;
		}
	}
	else
	{
//		printf("setting nme %d to %d\n",act->data.val_tag_flags.tag,act->data.val_tag_flags.value);
		nme = loadlndFindEnemy(tag);
		if(nme)
		{
//			printf("found it\n");
			if(value == 0)
			{
//				printf("disabled\n");

				nme->flags &= ~(NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
			}
			else
			{
//				printf("enabled\n");

				nme->flags |= (NMEFLAG_ACTIVE + NMEFLAG_ENABLED);
			}
		}
		else
		{
//			printf("enable garib group %d to 1\n",act->data.val_tag_flags.tag);
			EnableGaribGroup(tag, value); // enable or disable
		}
	}

//			platformDestroyDynamicBox(pBox);
}

void puzzleStartMove(int tag, int flags, int value)
{
	DYNCOLLBOX *pBox;

//	DB("ACTION_START_MOVE (%d, %d, %d)\n", tag, flags, value);

	loadlndFindPlatform(tag, NULL, &pBox);
	if(pBox)
	{
		platformStartMove(pBox,flags,value);
	}
	else
	{
//		DB("Box not found! ACTION_START_MOVE will have no effect.\n");
	}
}

/*
void puzzleStartMove(int tag, int flags, int value)
{
	DYNCOLLBOX *pBox;

	DB("ACTION_START_MOVE (%d)\n", tag);

	loadlndFindPlatform(tag, NULL, &pBox);
	if(pBox)
	{
		pBox->move.waitTimer=0;// that should get things moving ...
		DB("Found box!\n");

		switch(flags)
		{
		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:
			DB("ERROR: Bad move action (%d)\n", flags);
			CRASH;
		}
	}
	else
	{
		DB("Box not found! ACTION_START_MOVE will have no effect.\n");
	}
}
*/

void puzzleStartSpin(int tag, int value)
{
	DYNCOLLBOX *pBox;

// sorry to comment this one out, but it gets to total overkill on caboss if it's left in
//	DB("ACTION_START_SPIN (%d)\n", tag);
	loadlndFindPlatform(tag, NULL, &pBox);
	ASSERT(pBox);
	ASSERT(pBox->pPlatDef->head.spin.yes);

	pBox->pPlatDef->head.spin.rotspeed=value;
	pBox->spinpause.timer=-1;
}

int puzzleJustAtPoint(DYNCOLLBOX *pBox, int point)
{
	return(pBox && CompareVectors(&(pBox->pos), &(pBox->pPlatDef->points[point].pos)) && (pBox->move.jap));
}

int puzzleAtPoint(DYNCOLLBOX *pBox, int point)
{
	return(pBox && CompareVectors(&(pBox->pos), &(pBox->pPlatDef->points[point].pos)));
}
