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

	crate.c:			Chase sequence featuring Mrs Tweedy hanging from the airplane
******************************************************************************************/

#include "global.h"
#include "main.h"
#include "menu.h"
#include "map_draw.h"
#include "bff_load.h"
#include "debris.h"
#include "lights.h"
#include "timer.h"
#include "maths.h"
#include "overlays.h"
#include "map_view.h"
#include "global.h"
#include "overlays.h"
#include "collide.h"
#include "enemies.h"
#include "camera.h"
#include "dualshock.h"
#include "ranges.h"
#include "crate.h"
#include "dogchase.h"
#include "scenics.h"
#include "demo.h"
#include "loadsnd.h"
#include "gametext.h"
#include "credits.h"
#include "options.h"
#include "deth.h"
#include "sound.h"
#include "startup.h"
#include "lev_flow.h"
#include "pause.h"

extern void levflowCrateSuccess(void);

#define SUBGAME_OVERLAYS
#define CRATE_SAMPLEDELAY 100
#ifdef DREAMCAST
#undef NULL
#define NULL 0
#endif

FMA_WORLD2 *crate_fma_world;

#define CRATE_MINX -3000
#define CRATE_MAXX 4000
#define CRATE_ROPELENGTH 1400				// The total length of the rope
#define CRATE_SEGMENTLENGTH 100				// The length of each rope segment
#define CRATE_NUMLINKS (CRATE_ROPELENGTH / CRATE_SEGMENTLENGTH)	// The number of individual links in the rope
#define AXE_SPEED (160*4096)				// Speed of the axe...
#if PALMODE==YES
#define CLIMB_DELAY (25)					// Frames delay between climbing attempts
#define HIT_DELAY (40)						// Frames that you lose control for after an axe hit
#define CRATE_CAMSPEED (80)					// Frames for camera to move from underside to behind at the start
#else
#define CLIMB_DELAY (30)		
#define HIT_DELAY (48)
#define CRATE_CAMSPEED (96)
#endif

int cratetransp;
int windchannel;
int crateSampleTimer;
int lastfowlersample;

int bouncedata[21] = {48,72,128,200,270,290,294,292,286,278,266,252,236,218,198,174,148,120,90,58,24};

// Lots of structs

struct {
	char state;
	char turning;							// Set to 1 if we've hit a boundary and are turning
	PSIMODEL *model;						// Pointer to the model
	ACTOR *actor;							// Pointer to the actor structure
	VECTOR desiredangle;					// The direction we want to be facing
	ENEMY enemyStruc;						// Enemy structure used for collision and rail-cam
	VECTOR position;						// Position relative to the rail (-800 to 800 horizontal and -600 to 600 vertical)
	VECTOR speed;							// Speed on X, Y and Z
	int desiredheight;						// The altitude of the crate - needs to move up/down as the tweedy gets farther up the rope...
} crate;

#define CRATE_MAXSPEED 11000				

#define NUM_EXPLOSION_PARTICLES 150

struct {
	char active;					// Whether or not the explosion is currently active
	char dummy;
	int life[NUM_EXPLOSION_PARTICLES];							// Life of each particle
	int scale[NUM_EXPLOSION_PARTICLES];
	unsigned long colour;					// Rate of transparency
	TextureType *texture;					// Pointer to the wood texture
	VECTOR partpositions[NUM_EXPLOSION_PARTICLES];				// Positions of all the particles in the explosion
	VECTOR velocity[NUM_EXPLOSION_PARTICLES];					// Speed of each particle
} explosion;

struct {
	char state;					// What she's doing? (hanging, throwing axe, rebounding from collision etc...)
	char desiredanimsegment;		// The animation we want to be playing
	int bouncecounter;						// How many frames we wait after rebounding before we drop back down
	int currentlink;						// The link she's holding on to
	int climbcounter;						// Delay between climbing attempts
	int distancecounter;					// distance from the link she's on - 0 to 10
	PSIMODEL *model;						// Pointer to the model
	ACTOR *actor;							// Pointer to the actor structure
	VECTOR distance;						// Distance from the crate
	VECTOR linkoffset;						// Distance on the rope from her current link
	ENEMY enemyStruc;						// Enemy structure used for collision and rail-cam
	COLLDATA collision;						// Collision data
} mrstweedy;


struct {
	char active;					// Whether or not the axe is currently airborne
	int xstep;								// Speed in X
	int zstep;								// Speed in Z
	int xdist;								// X distance from target
	int zdist;								// Z distance from target
	PSIMODEL *model;						// Pointer to the model
	ACTOR *actor;							// Pointer to the actor structure
	VECTOR velocity;						// Speed in 3 directions
	VECTOR currentangle;					// The direction we're currently facing
	VECTOR desiredangle;					// The direction we want to be facing
} axe;


struct {
	TextureType *sprites[4];				// Pointer to the sprite frames
	int counter;							// Counter for animation and killing
	VERT position;							// Position before camera taken into account
	int scale;								// Size compared to original texture
	int active;								// Whether or not it needs to be drawn
} dust;

typedef struct {
	VECTOR position;						// Position of this link relative to the crate
	long angle;
} link;


typedef struct {							// Lights for the rope
	TextureType *sprite;
	VECTOR position;
	ULONG rgb;
} light;

light xmaslights[CRATE_NUMLINKS];

link ropelinks[CRATE_NUMLINKS];				

enum {
	CRATESOUND_AXE1,		//0
	CRATESOUND_HIT1,		//1
	CRATESOUND_WINDY,		//2
	CRATESOUND_WINGS,		//3
	CRATESOUND_MRST1,		//4
	CRATESOUND_MRST2,		//5
	CRATESOUND_MRST3,		//6
	CRATESOUND_FOWLER1,		//7
	CRATESOUND_FOWLER2,		//8
	CRATESOUND_FOWLER3,		//9
	CRATESOUND_FOWLER4,		//10
	CRATESOUND_GINGER1,		//11
	CRATESOUND_GINGER2,		//12
	CRATESOUND_GINGER3,		//13
	CRATESOUND_OTHER7,		//14
	CRATESOUND_AXETHROW,	//15
	NUM_CRATE_SAMPLES};		//16

SfxSampleType *crateSamples[NUM_CRATE_SAMPLES];

SfxSampleType *crateQueuedSamples[4];		// Max of 4 samples in the queue at any one time

// This is to move link 0 up and down in time with the animation of the crate so that it's anchored at the back door
// Y axis offset from the flying machine's origin for animation frames 0 to 48

static int poslist2[49] = {193, 194, 196, 201, 206, 213, 220, 228, 236, 244, 252, 258, 264, 269, 274, 279, 284, 289, 293, 299, 305, 310, 315, 311, 308, 301, 294, 289, 284, 279, 274, 269, 264, 258, 252, 248, 240, 232, 228, 224, 216, 212, 210, 208, 207,205,205,204,204};

int hitcounter;			// Delay after being hit by an axe
int hitincrement;		// Z-axis rotational increment

unsigned long crateFrameCounter;

enum	// States for the flying machine
{
	CRATE_STARTING,					// Briefing on screen, static view of the crate and the Tweedy
	CRATE_MOVINGCAMERA,				// Camera moving from above view to the chase view
	CRATE_FLYING,					// Flying along
	CRATE_RETRY,					// Has reached the ground
	CRATE_WON,						// Has successfully reached the farm - flyby shot
	CRATE_FINISHED,					// Fading out and quitting to FMV
	CRATE_NUMSTATES
};

int randomx[130];							// X positions for all the stars

int randomy[130];							// Y positions for all the stars

extern int pause_levelplaying;

TextureType *cratesky1;
TextureType *cratesky2;

#if defined DREAMCAST || defined _WINDOWS
#define CRATE_SKY_DEPTH 40000
#else
#define CRATE_SKY_DEPTH 1000
#endif

struct {									// Distance of the camera from the crate...
	int vpxoffset;
	int vpyoffset;
	int vpzoffset;
	int vrxoffset;
	int vryoffset;
	int vrzoffset;
	int vrxstep;
	int vrystep;
	int vrzstep;
	int vpxstep;
	int vpystep;
	int vpzstep;
	int framestodest;
} camerapos;


//----------------------------------------------------------------------------------
//	Function 	: void OverlayAlpha(int x, int y, int w, int h , unsigned long rgb, int colour)
//	Purpose 	: Draw a semitrans poly
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

void crateOverlayAlpha(int x, int y, int w, int h , unsigned long rgb, int colour)
{
	POLY_F4 *f4;
	DR_TPAGE *tp;

#ifdef _WINDOWS
	BEGINPRIM(tp, DR_TPAGE);
	setDrawTPage(tp,1,1,((colour-1)<<5));
	ENDPRIM(tp,0,DR_TPAGE);
#endif

	BEGINPRIM(f4, POLY_F4);
	setPolyF4(f4);
	f4->code = f4->code | 2;

	f4->x0=f4->x2=x;
	f4->x1=f4->x3=x+w;
	f4->y0=f4->y1=y;
	f4->y2=f4->y3=y+h;
	setRGB0(f4, rgb>>16 & 0xff, rgb>>8 & 0xff, rgb & 0xff);
	ENDPRIM(f4, 0, POLY_F4);


#ifndef _WINDOWS
	BEGINPRIM(tp, DR_TPAGE);
	setDrawTPage(tp,1,1,((colour-1)<<5));
	ENDPRIM(tp,0,DR_TPAGE);
#endif
}






//----------------------------------------------------------------------------------
//	Function 	: void crateStart(void)
//	Purpose 	: Initialise everything, load models and level, allocate memory
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

void crateStart(void)
{
	int i;

	sound_FreeLanguageBank();
	sound_LoadLanguageBankBody(gameTextLang,"A4_FLYMACHINE");

	actorInitialise();
	PSImodelctrl.farclip = 1024;
	scenicInit();
	dsEvent_Init();

	// Load and get a pointer the map
	mapviewLoadMap("A4_FLYMACHINE");								
	setupCollision("A4_FLYMACHINE");

	crate_fma_world = (void *)BFF_FindObject(BFF_FMA_WORLD2_ID,utilStr2CRC("LNDMAP"));
	ASSERT(crate_fma_world);

	// Set the cycles and starting state
	draw_routine = crateDraw;
	cycle_routine = crateCycle;
	crate.state = CRATE_STARTING;

	cratetransp = 255;
	lastfowlersample = 0;

	crateInitialiseTweedy();
	crateInitialiseCrate();
	crateInitialiseRope();

	crateSetUpTextures();
	crateSetUpSamples();

	dust.active = 0;
	dust.position.vx = 0;
	dust.position.vy = 0;
	dust.position.vz = 0;
	dust.counter = 0;
	dust.scale = 0;
	
	hitcounter = 0;
	hitincrement = 60;

	// Set the framecounter to 0

	crateFrameCounter = 0;

	crateSetUpStars();
	crateInitialiseAxe();

	explosion.active = 0;

	psiSetLight(0, 0,0,0,0,0,0);
	psiSetLight(1, 0,0,0,0,0,0);
	psiSetLight(2, 0,0,0,0,0,0);

	windchannel = soundPlaySample(crateSamples[CRATESOUND_WINDY], 0x4444, 1300);
#ifdef DREAMCAST
	overlayLoadingEnd();
#endif

#ifdef _WINDOWS
// Hunt for windmills, and move 'em up a bit
// (Yes, we could do this in Egg, but what the hell?)
	{
		SCENIC *pS;
		PSIMODEL *model;
		BFF_Header *bffh;
		unsigned int crc;
		int cnt = 0;

		crc = utilStr2CRC("WINDMILL");
		for(pS = scenicList; pS; pS=pS->next)
		{
			model = pS->pModel;
			bffh = (BFF_Header *)model;
			if(bffh->crc == crc)
			{
//				DB(" F O U N D   A   W I N D M I L L\n (xyz = %d %d %d)\n",pS->pActor->position.vx,pS->pActor->position.vy,pS->pActor->position.vz);
				pS->pActor->position.vy -= 160;

				if(cnt == 1)
					pS->pActor->position.vy -= 160;	// This one needs a teensy bit extra compared with the others...


				if(pS->pActor->position.vx > 3000)
					pS->ya = (pS->ya + 400) & 4095;
				cnt++;
			}
		}
	}
#endif
}






//----------------------------------------------------------------------------------
//	Function 	: void crateCycle(void)
//	Purpose 	: Main loop
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateCycle(void)
{
	dsEvent_Update();

	cratePlayQueuedSamples();
	// Increase the framecounter

	crateFrameCounter ++;

	// Animate the Scenics

	crateUpdateAnims();
	scenicCycle();

	sfxSetChannelVolume(windchannel, 0x44, 0x44);

	switch(crate.state)
	{
		case CRATE_STARTING:
			crateMoveCrate();
			crateMoveCamera();
			crateMoveTweedy();
			if(pReadPad->debounce[0] & PAD_CROSS)
			{
				crateGetCameraPath();
				crate.state = CRATE_MOVINGCAMERA;
				camerapos.framestodest = CRATE_CAMSPEED;
			}
			
			{
				crate.desiredangle.vy = 0;
				crate.desiredangle.vx = 0;
				crate.actor->position.vx = 0;
				crate.actor->position.vy = -200;
				crate.desiredheight = -200;
				crate.actor->position.vz = -35000;
			}
			
			crateInitialiseRope();
			crateMoveRope();

			break;	

		case CRATE_MOVINGCAMERA:
			cratePlaySamples();
			crateMoveCrate();
			crateMoveCamera();
			crateMoveTweedy();
			crateMoveRope();
			if(camerapos.framestodest==0)
				crate.state = CRATE_FLYING;
			break;

		case CRATE_FLYING:
			cratePlaySamples();

			if(pReadPad->debounce[0] & PAD_START)
			{
				pause_levelplaying = LEVNUMBER_A4_FLYMACHINE;
				pauseStart();
				return;
			}

			crateCollision();
			crateMoveCrate();
			crateMoveCamera();
			crateMoveTweedy();
			crateMoveAxe();
			crateMoveRope();
			crateUpdatePartExplosion();
			break;

		case CRATE_WON:
			crateMoveCrate();
			crateMoveCamera();
			crateMoveTweedy();
			crateMoveAxe();
			crateMoveRope();
			crateUpdatePartExplosion();
			break;	

		case CRATE_FINISHED:
			crateEndSuccess();
			break;

		case CRATE_RETRY:
			// Do failure FMA
			if(pReadPad->debounce[0] & PAD_CROSS)
			{
				displayPage[0].drawenv.isbg = 1;
				displayPage[1].drawenv.isbg = 1;
				crateFree();		// Change to crate re-init
				crateStart();
			}
#ifdef DREAMCAST
			else if(pReadPad->debounce[0] & PAD_CIRCLE)
#else
			else if(pReadPad->debounce[0] & PAD_TRIANGLE)
#endif
			{
				displayPage[0].drawenv.isbg = 1;
				displayPage[1].drawenv.isbg = 1;
				crateEndFail();
			}
			break;

		default:
			break;
	}
}






//----------------------------------------------------------------------------------
//	Function 	: void crateDraw(void)
//	Purpose 	: Draw everything
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

void crateDraw(void)
{
//	overlayShowMapCollision();
//	overlayShowPlatformCollision();

	switch(crate.state)
	{
		case CRATE_STARTING:
			crateDrawStars();
			crateDrawSprites();
#ifndef _WINDOWS	// RFW - TBD: Figure out why there's a huge map polygon on-screen here.
			crateDrawMap();
#endif
			crateDrawModels();
			if(cratetransp>0)
			{
				cratetransp-=8;
				if(cratetransp<0)
					cratetransp = 0;
				deathScreenAdd(cratetransp,cratetransp,cratetransp, SEMITRANS_SUB, 0);
			}
			crateDrawBriefing();
			break;
		case CRATE_MOVINGCAMERA:
			crateDrawStars();
			crateDrawSprites();
			crateDrawMap();
			crateDrawModels();
			break;

		case CRATE_FLYING:
//			overlayShowMapCollision();


			crateDrawStars();
			crateDrawMap();
			scenicDrawAll();
			crateDrawSprites();
			crateDrawModels();
			break;
		

		case CRATE_RETRY:
//			if(cratetransp<255)
//			{
				//deathScreenAdd(cratetransp,cratetransp,cratetransp, SEMITRANS_SUB, 0);
//				crateDrawStars();
//				crateDrawMap();
//				scenicDrawAll();
//				crateDrawSprites();
//				crateDrawModels();
//				cratetransp+=8;
//				if(cratetransp>255)
//					cratetransp=255;
//			}
//			else
				crateDrawDeadMessage();
			break;

		case CRATE_WON:
			crateDrawStars();
			crateDrawMap();
			scenicDrawAll();
			crateDrawSprites();
			crateDrawModels();
			break;

		case CRATE_FINISHED:
			crateDrawStars();
			crateDrawMap();
			scenicDrawAll();
			crateDrawSprites();
			crateDrawModels();
			break;

		default:
			break;
	}		
}






//----------------------------------------------------------------------------------
//	Function 	: void crateEnd
//	Purpose 	: Free all memory used and return to the menu
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateEndFail(void)
{
	crateFree();
	optionsStart(0);
}




//----------------------------------------------------------------------------------
//	Function 	: void crateFree(void)
//	Purpose 	: Free all memory used
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------



void crateFree(void)		// Cant be static'd as we need to call it from pause.c
{
	crateStopSamples();
	scenicEnd();
	actorFree(mrstweedy.actor);
	actorFree(axe.actor);
	actorFree(crate.actor);
	mapviewReleaseMap();
	enemyDeleteAll();
	sound_XA_Stop();
	sound_LoadLanguageBank(gameTextLang);
}





//----------------------------------------------------------------------------------
//	Function 	: void crateEnd
//	Purpose 	: Free all memory used and go to the end-of-game sequence
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateEndSuccess(void)
{
	crateFree();
	levflowCrateSuccess();
}





//----------------------------------------------------------------------------------
//	Function 	: void crateEndRestart
//	Purpose 	: Free all memory used and restart the Flying machine game
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateEndRestart(void)
{
	crateFree();
	levflowCrateSuccess();
}





//----------------------------------------------------------------------------------
//	Function 	: void crateMoveCrate(void)
//	Purpose 	: Move the crate around with the analog stick
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateMoveCrate(void)
{
	int i;
	VECTOR v;
	int angle;

	switch(crate.state)
	{

		case CRATE_FLYING:
			if(hitcounter)
			{
				hitcounter--;
				dsEvent_Add(0, 0, 1, 1200, 0, 7, NULL);
				
				crate.desiredangle.vz += hitincrement;

				crate.desiredangle.vx += utilRandomInt(50)-25;
				crate.desiredangle.vy += utilRandomInt(50)-25;

				if((crate.desiredangle.vz>130)||(crate.desiredangle.vz<-130))
					hitincrement=-hitincrement;

				if(crate.actor->psiData.object->rotate.vz != crate.desiredangle.vz)
					crate.actor->psiData.object->rotate.vz = enemyTendTowards(crate.desiredangle.vz, crate.actor->psiData.object->rotate.vz, 40);	
			}
			else
			{
				crate.desiredangle.vy+=(pReadPad->analogXS[0] >> 6);
	
				// or of course the digital pad option for mad people
				if(pReadPad->digital[0] & PAD_LEFT)
				{
					crate.desiredangle.vy-=64;
				}
				else if(pReadPad->digital[0] & PAD_RIGHT)
				{
					crate.desiredangle.vy+=64;
				}

				if(crate.desiredangle.vy>512)
					crate.desiredangle.vy = 512;	

				if(crate.desiredangle.vy<-512)
					crate.desiredangle.vy = -512;
			
				if(crate.turning)
					crate.desiredangle.vy = 0;

				// Set the rotation about z (roll)

				debugPrintf("%d\n",crate.desiredangle.vz);
				crate.desiredangle.vz = pReadPad->analogXS[0] >> 5;

				if(crate.actor->psiData.object->rotate.vz != crate.desiredangle.vz)
					crate.actor->psiData.object->rotate.vz = enemyTendTowards(crate.desiredangle.vz, crate.actor->psiData.object->rotate.vz, 30);	
			}


			if(crate.desiredangle.vx>300)
				crate.desiredangle.vx = 300;

			if(crate.desiredangle.vx<-300)
				crate.desiredangle.vx = -300;

			// Do the Min / Max X position check
			if(crate.actor->position.vx<CRATE_MINX)
			{
				crate.actor->position.vx = CRATE_MINX;
				crate.turning = 1;
			}

			else if(crate.actor->position.vx>CRATE_MAXX)
			{
				crate.actor->position.vx = CRATE_MAXX;
				crate.turning = 1;
			}

			// Set the rotation about y (yaw)
		
			if(crate.actor->psiData.object->rotate.vy!=crate.desiredangle.vy)
			{
				if(crate.turning)
					crate.actor->psiData.object->rotate.vy=enemyTendTowards(crate.desiredangle.vy, crate.actor->psiData.object->rotate.vy, 40);
				else
					crate.actor->psiData.object->rotate.vy=enemyTendTowards(crate.desiredangle.vy, crate.actor->psiData.object->rotate.vy, 200);
			}
			if((crate.turning) && (crate.actor->psiData.object->rotate.vy==0))
			{
				crate.turning = 0;
			}

			// Move the plane on X & Z according to the direction it's facing

			if(crate.actor->psiData.object->rotate.vy<1024)
			{
				angle = 1024-crate.actor->psiData.object->rotate.vy;
				crate.speed.vz = (CRATE_MAXSPEED/1024) * angle;
				crate.speed.vx = CRATE_MAXSPEED - crate.speed.vz;
			}

			else if(crate.actor->psiData.object->rotate.vy>3172)
			{
				angle = crate.actor->psiData.object->rotate.vy-2048;	
				angle = 1024-angle;
				crate.speed.vz = -((CRATE_MAXSPEED/1024) * angle);
				crate.speed.vx = -(CRATE_MAXSPEED - crate.speed.vz);
			}
	
			crate.speed.vy = -((crate.desiredangle.vx*128)/96);
			
			crate.actor->position.vz+=crate.speed.vz/64;
			crate.actor->position.vx+=crate.speed.vx/64;

			if(crate.actor->position.vz>325000)
			{
				// Save the current camera position and leave it here...
//				crateStaticCam.vx = mycamera.vpx;
//				crateStaticCam.vy = 

				crate.state = CRATE_WON;
				cratetransp = 255;
			}

#ifdef DEBUG
			if(SwimCheatCycle())
			{
				crate.state = CRATE_WON;
				mycamera.vpx = 1500;
				mycamera.vpy = crate.actor->position.vy + 300;
				mycamera.vpz = 335000;
				cratetransp = 255;
			}
#endif
			break;


		case CRATE_WON:

			crate.desiredangle.vy = (int)((calc_angle( (long) (500-crate.actor->position.vx), (long) 335000-crate.actor->position.vz)) & 0xffff);
			if(crate.actor->psiData.object->rotate.vy != crate.desiredangle.vy)
				crate.actor->psiData.object->rotate.vy = enemyTendTowards(crate.desiredangle.vy, crate.actor->psiData.object->rotate.vy, 38);

			// Move the plane on X & Z according to the direction it's facing
			if(crate.actor->psiData.object->rotate.vy<1024)
			{
				angle = 1024-crate.actor->psiData.object->rotate.vy;
				crate.speed.vz = (CRATE_MAXSPEED/1024) * angle;
				crate.speed.vx = CRATE_MAXSPEED - crate.speed.vz;
			}

			else if(crate.actor->psiData.object->rotate.vy>3172)
			{
				angle = crate.actor->psiData.object->rotate.vy-2048;	
				angle = 1024-angle;
				crate.speed.vz = -((CRATE_MAXSPEED/1024) * angle);
				crate.speed.vx = -(CRATE_MAXSPEED - crate.speed.vz);
			}
	
			crate.speed.vy = -((crate.desiredangle.vx*128)/96);
			
			crate.actor->position.vz+=crate.speed.vz/64;
			crate.actor->position.vx+=crate.speed.vx/64;

			crate.desiredangle.vx = -96;
			if(crate.actor->psiData.object->rotate.vx != crate.desiredangle.vx)
				crate.actor->psiData.object->rotate.vx = enemyTendTowards(crate.desiredangle.vx, crate.actor->psiData.object->rotate.vx, 8);
			
			crate.desiredangle.vz = 0;

			if(crate.actor->psiData.object->rotate.vz != crate.desiredangle.vz)
				crate.actor->psiData.object->rotate.vz = enemyTendTowards(crate.desiredangle.vz, crate.actor->psiData.object->rotate.vz, 8);	

			break;

		case CRATE_MOVINGCAMERA:
			//DB("Moving camera behind crate....");
		case CRATE_STARTING:
			crate.speed.vx = 0;
			crate.speed.vz = CRATE_MAXSPEED;
			crate.actor->position.vz += crate.speed.vz/64;
			crate.actor->position.vx += crate.speed.vx/64;
			crate.desiredangle.vy = 0;
			if(crate.actor->psiData.object->rotate.vy != crate.desiredangle.vy)
				crate.actor->psiData.object->rotate.vy = enemyTendTowards(crate.desiredangle.vy, crate.actor->psiData.object->rotate.vy, 38);

			crate.desiredangle.vx = 0;
			if(crate.actor->psiData.object->rotate.vx != crate.desiredangle.vx)
				crate.actor->psiData.object->rotate.vx = enemyTendTowards(crate.desiredangle.vx, crate.actor->psiData.object->rotate.vx, 8);
			break;

		default:
			break;
	}
	if((crate.actor->animation.animData[0].actualFrame==31)||(crate.actor->animation.animData[0].actualFrame==32))
		soundPlaySample(crateSamples[CRATESOUND_WINGS], 0x8888,1300);		

	if(crate.actor->position.vy<crate.desiredheight)
		crate.actor->position.vy += 5;
	else if(crate.actor->position.vy>crate.desiredheight)
		crate.actor->position.vy -= 5;
}





//----------------------------------------------------------------------------------
//	Function 	: void crateDrawMap(void)
//	Purpose 	: Draw the level map
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

extern int NEAR_CLIP_DEPTH;

static void crateDrawMap(void)
{
#ifdef _WINDOWS
	int temp;
	temp = NEAR_CLIP_DEPTH;

// RFW, preventing some fairly major nasty polygons
	switch(crate.state)
	{
		case CRATE_STARTING:
			NEAR_CLIP_DEPTH = 500;
			break;
		case CRATE_MOVINGCAMERA:
			NEAR_CLIP_DEPTH = 100 + 400 * camerapos.framestodest/CRATE_CAMSPEED;
			break;
		default:
			NEAR_CLIP_DEPTH = 100;
			break;
	}
#endif

	MapDraw_DrawFMA_World(crate_fma_world);

#ifdef _WINDOWS
	NEAR_CLIP_DEPTH = temp;
#endif
}





//----------------------------------------------------------------------------------
//	Function 	: void crateDrawModels(void)
//	Purpose 	: Draw the models
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateDrawModels(void)
{	
	// Set a fixed light

	psiSetLight(0, 96, 96, 128, -3172 , 4095, 0);

	psiSetAmbient(872,872,1024);

#ifdef _WINDOWS
	d2mActorDraw(mrstweedy.actor);
	
	d2mActorDraw(crate.actor);
	
	if(axe.active)
	{
		d2mActorDraw(axe.actor);
	}
#else
	actorDraw(mrstweedy.actor);
	
	actorDraw(crate.actor);
	
	if(axe.active)
	{
		actorDraw(axe.actor);
	}
#endif
}






//----------------------------------------------------------------------------------
//	Function 	: void crateDrawStars(void)
//	Purpose 	: Draw the sky - A gouraud quad for the sky and some pretty triangles for stars
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateDrawStars(void)
{
	int i;

	POLY_F3 *star;				// A flat-shaded tri for each star
	for(i=0;i<130;i++)
	{
#if defined DREAMCAST || defined _WINDOWS
		BEGINPRIM(star, POLY_F3);
		setPolyF3(star);				
		star->x0 = randomx[i];				
		star->y0 = randomy[i];	
		star->x1 = star->x0+2;
		star->y1 = star->y0;
		star->x2 = star->x1;
		star->y2 = star->y1+2;

		star->r0 = abs(utilRandomInt(128) +127);
		star->g0 = star->r0;
		star->b0 = star->r0;

		ENDPRIM(star, 38000, POLY_F3);
#else
		BEGINPRIM(star, POLY_F3);
		setPolyF3(star);				
		star->x0 = randomx[i];				
		star->y0 = randomy[i];	
		star->x1 = star->x0+1;
		star->y1 = star->y0;
		star->x2 = star->x1;
		star->y2 = star->y1+1;

		star->r0 = abs(utilRandomInt(128) +127);
		star->g0 = star->r0;
		star->b0 = star->r0;

		ENDPRIM(star, 999, POLY_F3);
#endif
	}
}





//----------------------------------------------------------------------------------
//	Function 	: void crateMoveTweedy(void)
//	Purpose 	: Move Mrs Tweedy at the end of the rope
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateMoveTweedy(void)
{
	int i;
	VECTOR v;
	int angle;

	if(crate.state==CRATE_FLYING)
	{
		switch(mrstweedy.state)
		{
			case MRSTWEEDY_HANGING:
				if(crateSampleTimer>120)
				{
					// See if we want to throw an axe then climb now

					if(mrstweedy.climbcounter)
						mrstweedy.climbcounter--;
					else
					{
						// If she's on links 0 to 3, make her just climb, otherwise, make her throw an axe first...

						if(mrstweedy.currentlink<=3)
						{
							if(utilRandomInt(500)>400)
								crateAddSampleToQueue(crateSamples[10],0);
							crateMrsTweedyGoToClimb();
						}

						else
						{
							mrstweedy.state = MRSTWEEDY_THROWING;
							mrstweedy.desiredanimsegment = MRSTWEEDY_THROWING;
						}
					}
				}
				break;	
	
			case MRSTWEEDY_THROWING:

				// Check if throw has reached the 'let go of axe' frame...

				if((mrstweedy.actor->animation.animData[0].actualFrame>=76) && (!axe.active) && (!explosion.active))

				// If it has, generate a new axe	
				
				crateGenerateNewAxe();
			
				// And if we've hit the end of the throw anim, make her climb

				if(actorNearEndOfAnimation(mrstweedy.actor,mrstweedy.actor->animation.animData[0].segment,2))
					crateMrsTweedyGoToClimb();	

				break;
						
			case MRSTWEEDY_BOUNCING:
		
				ropelinks[CRATE_NUMLINKS-1].position.vy-=(bouncedata[21-mrstweedy.bouncecounter])*2;
				
				// Run through the list of links backwards
				
				for(i = CRATE_NUMLINKS-2; i>0; i--)
				{
					// Get the distance between this link and the next link
				
					v.vx = ropelinks[i].position.vx - ropelinks[i+1].position.vx;
					v.vy = ropelinks[i].position.vy - ropelinks[i+1].position.vy;
					v.vz = ropelinks[i].position.vz - ropelinks[i+1].position.vz;

					crateKeepPointWithinSphere(&v, CRATE_SEGMENTLENGTH);
	
					ropelinks[i].position.vx = v.vx + ropelinks[i+1].position.vx;
					ropelinks[i].position.vy = v.vy + ropelinks[i+1].position.vy;
					ropelinks[i].position.vz = v.vz + ropelinks[i+1].position.vz;
				}

				mrstweedy.bouncecounter--;	

				if(!mrstweedy.bouncecounter)
				{
					mrstweedy.state = MRSTWEEDY_HANGING;
					mrstweedy.desiredanimsegment = MRSTWEEDY_HANGING;
				}
			
				break;
	
			case MRSTWEEDY_CLIMBING:
				{
				VECTOR myvec;
				VECTOR myothervec;
				int mag;
				if(mrstweedy.distancecounter>0)
					mrstweedy.distancecounter--;	

				// Ok, get the distance from her current link to the link shes going to

				myvec.vx = ropelinks[mrstweedy.currentlink-1].position.vx - ropelinks[mrstweedy.currentlink].position.vx;
				myvec.vy = ropelinks[mrstweedy.currentlink-1].position.vy - ropelinks[mrstweedy.currentlink].position.vy;
				myvec.vz = ropelinks[mrstweedy.currentlink-1].position.vz - ropelinks[mrstweedy.currentlink].position.vz;
				myothervec.vx = myvec.vx;
				myothervec.vy = myvec.vy;
				myothervec.vz = myvec.vz;	

				mag = Magnitude(&myvec);	

				// Scale this vector by whatever, to get the tweedy's offset from her link... she moves in ten steps	

				SCALEVECTOR(&myvec,(4096/20)*mrstweedy.distancecounter);	

				mrstweedy.linkoffset.vx = myothervec.vx - myvec.vx;
				mrstweedy.linkoffset.vy = myothervec.vy - myvec.vy;
				mrstweedy.linkoffset.vz = myothervec.vz - myvec.vz;	

				mag = Magnitude(&myvec);

				if(mag==0)
				{
					crate.desiredheight+=25;
					mrstweedy.currentlink--;
					mrstweedy.linkoffset.vx = 0;
					mrstweedy.linkoffset.vy = 0;
					mrstweedy.linkoffset.vz = 0;
					mrstweedy.state = MRSTWEEDY_HANGING;
					mrstweedy.desiredanimsegment = MRSTWEEDY_HANGING;
					
					if(mrstweedy.currentlink==0)
					{
						sfxStopSample(crateSamples[CRATESOUND_WINDY], -1);

						displayPage[0].drawenv.isbg = 0;
						displayPage[1].drawenv.isbg = 0;
						{
							static RECT rect = {0,256,512,256};
							MoveImage(&rect,0,0);	
							DrawSync(0);
						}
						crate.state = CRATE_RETRY;
						cratetransp = 0;
					}
				}
				break;
			
			default:
				break;			
			}
		}
	}
		
	// Put Mrs Tweedy at the position of her link in the rope, plus her offset...

	mrstweedy.actor->position.vx = ropelinks[mrstweedy.currentlink+1].position.vx + (mrstweedy.linkoffset.vx);
	mrstweedy.actor->position.vy = ropelinks[mrstweedy.currentlink+1].position.vy + (mrstweedy.linkoffset.vy);
	mrstweedy.actor->position.vz = ropelinks[mrstweedy.currentlink+1].position.vz + (mrstweedy.linkoffset.vz);

	// Set Mrs Tweedy's rotation about X and Z according to how far left / right / above / below she is of the crate

	mrstweedy.distance.vx = crate.actor->position.vx - mrstweedy.actor->position.vx;

	mrstweedy.distance.vy = (crate.actor->position.vy + 800) - mrstweedy.actor->position.vy;

	if(mrstweedy.state!=MRSTWEEDY_BOUNCING)
		mrstweedy.actor->psiData.object->rotate.vx = mrstweedy.distance.vy / 3;
		
	mrstweedy.actor->psiData.object->rotate.vz = -(mrstweedy.distance.vx / 2);

	mrstweedy.actor->psiData.object->rotate.vy = -(2048-(crate.actor->psiData.object->rotate.vy));

	// Update the Tweedy's animation 

	if (mrstweedy.actor->animation.animData[0].segment!=mrstweedy.desiredanimsegment)	
	{
		if(mrstweedy.desiredanimsegment==MRSTWEEDY_CLIMBING)
		//	int actorAnimate(ACTOR *pActor, int animNum, char loop, char queue, int actualSpeed, char skipendframe, int blend)
			actorAnimate(mrstweedy.actor, mrstweedy.desiredanimsegment, 1, 0, 384, 0, 512);
		else if(mrstweedy.desiredanimsegment==MRSTWEEDY_THROWING)
			actorAnimate(mrstweedy.actor, mrstweedy.desiredanimsegment, 0, 0, 256, 0, 512);
		else
			actorAnimate(mrstweedy.actor, mrstweedy.desiredanimsegment, 1, 0, 256, 0, 512);
	}
}






//----------------------------------------------------------------------------------
//	Function 	: void crateMoveAxe(void)
//	Purpose 	: Move the axe so that it doesnt follow the crate and rarely hits
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateMoveAxe(void)
{
	if(!axe.active) return;

	// Rotate the axe about the X axis

	axe.actor->psiData.object->rotate.vx -= 512;

	if(axe.actor->psiData.object->rotate.vx <= 0)
		axe.actor->psiData.object->rotate.vx = 4095;

	axe.actor->psiData.object->rotate.vy = axe.currentangle.vy;
	
	if(axe.velocity.vz<crate.speed.vz)
		axe.velocity.vz = (crate.speed.vz+2000);

	axe.actor->position.vx += axe.velocity.vx/64;
	axe.actor->position.vy += axe.velocity.vy/64;
	axe.actor->position.vz += axe.velocity.vz/64;

	crateDoAxeCollisionCheck();
}





//----------------------------------------------------------------------------------
//	Function 	: void crateDrawSprites(void)
//	Purpose 	: Draw all the sprites - the dust and the rope
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------
int crateskyblue = 50;
static void crateDrawSprites(void)
{
	int i;
	VERT v;
	ULONG lightalpha;

#ifdef _WINDOWS
	{
		POLY_F4 *si4;
		BEGINPRIM(si4, POLY_F4);
		si4->r0 = si4->g0 = 0;
//		si4->b0 = 0x6b;
		si4->b0 = 0x6b / 2;	// RFW - note that this will need changing back if the untextured drawprim is fixed
		si4->x0 = si4->x2 = -256;
		si4->x1 = si4->x3 = 256;
		
		si4->y0 = si4->y1 = -64;
		si4->y2 = si4->y3 = 128;
		setPolyF4(si4);
		ENDPRIM(si4, CRATE_SKY_DEPTH+23, POLY_F4);
	}
#endif



	if(dust.active)
	{
		dust.counter--;
		
		if(dust.counter==0)
			dust.active = 0;

		dust.position.vx -= crate.speed.vx/128;
		dust.position.vz -= crate.speed.vz/192;

		v.vx = dust.position.vx;
		v.vy = dust.position.vy;
		v.vz = dust.position.vz;

		Print3DSelfAlphaSprite(dust.sprites[3-(dust.counter/4)], &v, dust.scale, 0x505050);
	}

#ifdef DREAMCAST
	noFilter = YES;
#endif
	overlaySprite(cratesky1,112,-128,CRATE_SKY_DEPTH,0x808080);
#ifdef DREAMCAST
	noFilter = NO;
#endif
	overlaySprite(cratesky2,48,-128,CRATE_SKY_DEPTH,0x808080);
	overlaySprite(cratesky2,-16,-128,CRATE_SKY_DEPTH,0x808080);
	overlaySprite(cratesky2,-80,-128,CRATE_SKY_DEPTH,0x808080);
	overlaySprite(cratesky2,-144,-128,CRATE_SKY_DEPTH,0x808080);
	overlaySprite(cratesky2,-208,-128,CRATE_SKY_DEPTH,0x808080);
	overlaySprite(cratesky2,-272,-128,CRATE_SKY_DEPTH,0x808080);


#ifndef _WINDOWS
	{
		POLY_F4 *si4;
#ifdef DREAMCAST
		BEGINPRIM(si4, POLY_F4);
		si4->r0 = si4->g0 = 0;
		si4->b0 = crateskyblue;
		si4->x0 = si4->x2 = 0;
		si4->x1 = si4->x3 = 640;
		
		si4->y0 = si4->y1 = 120;
		si4->y2 = si4->y3 = 480;
		setPolyF4(si4);
		ENDPRIM(si4, CRATE_SKY_DEPTH+23, POLY_F4);
#else
		BEGINPRIM(si4, POLY_F4);
		si4->r0 = si4->g0 = 0;
		si4->b0 = 0x6b;
		si4->x0 = si4->x2 = -256;
		si4->x1 = si4->x3 = 256;
		
		si4->y0 = si4->y1 = -64;
		si4->y2 = si4->y3 = 128;
		setPolyF4(si4);
		ENDPRIM(si4, CRATE_SKY_DEPTH+23, POLY_F4);
#endif
	}
#endif
	// Draw the rope - just lines right now.. erm... what else could they be?


	for(i=0;i<CRATE_NUMLINKS-1;i++)
	{
#ifdef _WINDOWS
		extern int useScale2D;

		int temp;
		temp = useScale2D;
		useScale2D=0;
#endif

		if(i<CRATE_NUMLINKS-2)
			overlayShowDebugLine(&ropelinks[i].position, &ropelinks[i+1].position, 0x333300, DEBUGPOLY_USEDEPTH);

		v.vx = ropelinks[i].position.vx - mycamera.vpx;
		v.vy = ropelinks[i].position.vy - mycamera.vpy;
		v.vz = ropelinks[i].position.vz - mycamera.vpz;
		lightalpha = utilRandomInt(255);
		Print3DSelfAlphaSprite(xmaslights[i].sprite, &v, 256, ((lightalpha<<8) + (lightalpha<<16) + (lightalpha)));
#ifdef _WINDOWS
		useScale2D = temp;
#endif
	}
	// Draw the particle explosion

	if(explosion.active)
		crateDrawPartExplosion();
}






//----------------------------------------------------------------------------------
//	Function 	: void crateCollision(void)
//	Purpose 	: Perform a spherical collision check on Mrs Tweedy and the Crate and set the flag accordingly
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateCollision(void)
{
	int items_hit;
	VECTOR v;
	int i;

	// Czech to see if Mrs tweedy has collided with something

	mrstweedy.collision.pPos = &mrstweedy.actor->position;
	mrstweedy.collision.pVel = &v;	
	mrstweedy.collision.bounce = 0;
	mrstweedy.collision.radius = 4096 * 30;
	mrstweedy.collision.nBoxes = numCollBoxes;

	mrstweedy.actor->position.vy <<= 9;						// Scale the positions up by 512 
	mrstweedy.actor->position.vx <<= 9;						// For the collision stuff to work
	mrstweedy.actor->position.vz <<= 9;		

	mrstweedy.collision.oldPos.vx = mrstweedy.actor->position.vx;
	mrstweedy.collision.oldPos.vy = mrstweedy.actor->position.vy;
	mrstweedy.collision.oldPos.vz = mrstweedy.actor->position.vz;

	ASSERT(&mrstweedy.collision);
	items_hit = collboxCheckSphere(&mrstweedy.collision);

	mrstweedy.actor->position.vy >>= 9;						// Scale the positions back down again
	mrstweedy.actor->position.vx >>= 9; 
	mrstweedy.actor->position.vz >>= 9;	
	
	// Only set her bouncing again if she's in normal flying mode

	if(mrstweedy.state!=MRSTWEEDY_BOUNCING)
	{
		if(items_hit>0)
		{
			for(i=0;i<items_hit;i++)
			{
				if(!((pHitData[i].pCollBox->flags) & 1))	// If bit 1 isn't set, it's a box
				{
					// Make her drop down a link...

					if(mrstweedy.currentlink<CRATE_NUMLINKS-2)
					{
						mrstweedy.currentlink++;
						crate.desiredheight -=25;
					}

					i = utilRandomInt(2);
						crateAddSampleToQueue(crateSamples[CRATESOUND_MRST1+i],1);

					dsEvent_Add(0, 0, 1, 180, 0, 7, NULL);
					mrstweedy.state = MRSTWEEDY_BOUNCING;
					mrstweedy.desiredanimsegment  = MRSTWEEDY_BOUNCING;
					mrstweedy.bouncecounter = 21;			// Make it bounce up to -600 before coming down again
					mrstweedy.linkoffset.vx = 0;
					mrstweedy.linkoffset.vy = 0;
					mrstweedy.linkoffset.vz = 0;
					if(utilRandomInt(1000)>800)
						crateAddSampleToQueue(crateSamples[CRATESOUND_FOWLER2],0);
				}
			}
		}
	}
}






//----------------------------------------------------------------------------------
//	Function 	: void crateGeneratePartExplosion(void)
//	Purpose 	: Generate a new particle explosion at v
//	Parameters 	: Pointer to a vector describing the origin of the explosion
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateGeneratePartExplosion(VECTOR *v)
{
	int i;

	if(explosion.active) 
		return;

	explosion.active = 1;

	// Set the colour to full

	explosion.colour = 0xffffff;

	for(i=0;i<NUM_EXPLOSION_PARTICLES;i++)
	{	
		// Set starting positions for all the polygons to the origin of the explosion

		explosion.partpositions[i].vx = v->vx;
		explosion.partpositions[i].vy = v->vy;
		explosion.partpositions[i].vz = v->vz;

		// Get the correct world space co-ordinates by subtracting the camera position

		explosion.partpositions[i].vx -= mycamera.vpx+20;
		explosion.partpositions[i].vy -= mycamera.vpy-180;
		explosion.partpositions[i].vz -= mycamera.vpz+510;

		// Get random starting velocitys for all the particles
		
		explosion.velocity[i].vx = utilRandomInt(20)-10;
		explosion.velocity[i].vy = utilRandomInt(25)-15;
		explosion.velocity[i].vz = utilRandomInt(20)-10;
	
		// Make sure we haven't got any particles with a zero X velocity

		if(explosion.velocity[i].vx == 0)
			explosion.velocity[i].vx = 1;

		// Get a random lifespan for each particle between 10 and 60 frames

		explosion.life[i] = utilRandomInt(50)+10;

		// Get a random scale for this particle between 32 and 96

		explosion.scale[i] = utilRandomInt(32) + 64;
	}
}






//----------------------------------------------------------------------------------
//	Function 	: void crateUpdatePartExplosion(void)
//	Purpose 	: Update the particle explosion
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateUpdatePartExplosion(void)
{
	int i;

	if(!explosion.active)
		return;

	explosion.active = 0;

	if(explosion.colour>0)
		explosion.colour -= 0x040404;

	for(i=0;i<NUM_EXPLOSION_PARTICLES;i++)
	{
		if(explosion.life[i])
		{
			explosion.life[i] --;

			// Move the particles about according to the speed

			explosion.partpositions[i].vx += explosion.velocity[i].vx;
			explosion.partpositions[i].vy += explosion.velocity[i].vy;
			explosion.partpositions[i].vz += explosion.velocity[i].vz;

			explosion.partpositions[i].vx -= crate.speed.vx/128;
			explosion.partpositions[i].vz -= crate.speed.vz/256;

			// Increase the Y velocity so it falls towards the ground

			explosion.velocity[i].vy ++;
			
			explosion.active = 1;
		}
	}
}






//----------------------------------------------------------------------------------
//	Function 	: void crateDrawPartExplosion(void)
//	Purpose 	: Draw the particle explosion
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateDrawPartExplosion(void)
{
	VERT v;
	int i;

	if(!explosion.active) return;

	for(i = 0;i < NUM_EXPLOSION_PARTICLES;i ++)
	{
		if(explosion.life[i])
		{
			v.vx = explosion.partpositions[i].vx;
			v.vy = explosion.partpositions[i].vy;
			v.vz = explosion.partpositions[i].vz;

			Print3DSprite(explosion.texture,&v,explosion.scale[i],explosion.colour);
		}
	}
}






//----------------------------------------------------------------------------------
//	Function 	: void crateMoveCamera(void)
//	Purpose 	: Keep the camera directly behind the crate
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateMoveCamera(void)
{
	switch(crate.state)
	{
		case CRATE_STARTING:
			camerapos.vrxoffset = 0;
			camerapos.vryoffset = 0;
			camerapos.vrzoffset = -620;
			camerapos.vpxoffset = -1500;
			camerapos.vpyoffset = 2000;
			camerapos.vpzoffset = -420;
			break;

		case CRATE_MOVINGCAMERA:
			
			camerapos.vrxoffset += camerapos.vrxstep/64;
			camerapos.vryoffset += camerapos.vrystep/64;
			camerapos.vrzoffset += camerapos.vrzstep/64;
			camerapos.vpxoffset += camerapos.vpxstep/64;
			camerapos.vpyoffset += camerapos.vpystep/64;
			camerapos.vpzoffset += camerapos.vpzstep/64;
			camerapos.framestodest--;
			break;

		case CRATE_FLYING:
			camerapos.vrxoffset = 0;
			camerapos.vryoffset = 600;
			camerapos.vrzoffset = 0;
			camerapos.vpxoffset = 0;
			camerapos.vpyoffset = -500;
			camerapos.vpzoffset = -3600;
			break;

		
		case CRATE_WON:

			mycamera.vrx = crate.actor->position.vx;
			mycamera.vry = crate.actor->position.vy + 600;
			mycamera.vrz = crate.actor->position.vz;
		
			if(crate.actor->position.vz > 335000)
			{
				crate.state = CRATE_FINISHED;
			}

			break;
	}
	
	mycamera.vrx = crate.actor->position.vx + camerapos.vrxoffset;
	mycamera.vry = crate.actor->position.vy + camerapos.vryoffset;
	mycamera.vrz = crate.actor->position.vz + camerapos.vrzoffset;
		
	if(crate.state!=CRATE_WON)
	{
		mycamera.vpx = crate.actor->position.vx + camerapos.vpxoffset;
		mycamera.vpy = crate.actor->position.vy + camerapos.vpyoffset;
		mycamera.vpz = crate.actor->position.vz + camerapos.vpzoffset;
	}

	GsSetProjection(500);
	mapCamSetRefView2L(&mycamera);
}






//----------------------------------------------------------------------------------
//	Function 	: void crateKeepPointWithinSphere(void)
//	Purpose 	: Keep a vector within a sphere of radius
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateKeepPointWithinSphere(VECTOR *v, int radius)
{
	int mag;

	mag = Magnitude(v);

	if(mag>radius)
		SCALEVECTOR(v,(4096/mag)*radius);
}






//----------------------------------------------------------------------------------
//	Function 	: void crateMoveRope(void)
//	Purpose 	: Move the rope behind the crate
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateMoveRope(void)
{
	int i;
	VECTOR v;

	// Anchor link 0 to the crate
	// Set the position of link 0 relative to the origin of the crate

	ropelinks[0].position.vx = -30;
	ropelinks[0].position.vy = (int) poslist2[crate.actor->animation.animData[0].actualFrame];
	ropelinks[0].position.vz = -390;


	// Rotate the position of link 0 according to the rotation of the crate
	
	RotateVector2DXYZ(&ropelinks[0].position, &ropelinks[0].position, -crate.actor->psiData.object->rotate.vx, 0);
	RotateVector2DXYZ(&ropelinks[0].position, &ropelinks[0].position, -crate.actor->psiData.object->rotate.vy, 1);
	RotateVector2DXYZ(&ropelinks[0].position, &ropelinks[0].position, -crate.actor->psiData.object->rotate.vz, 2);
	
	ropelinks[0].position.vx += crate.actor->position.vx;
	ropelinks[0].position.vy += crate.actor->position.vy;
	ropelinks[0].position.vz += crate.actor->position.vz;


	// Add something to the Y position of link 0 to compensate for the y rotation

	if(crate.actor->psiData.object->rotate.vy<1024)
		ropelinks[0].position.vy += abs(crate.actor->psiData.object->rotate.vy / 4);
	else
		ropelinks[0].position.vy += abs((4096-crate.actor->psiData.object->rotate.vy) / 4);

	// Ok, now keep em all within a sphere of the previous link

	for(i = 1; i<CRATE_NUMLINKS; i++)
	{
		// Get the distance between this link and the previous link
		
		v.vx = ropelinks[i].position.vx - ropelinks[i-1].position.vx;

		v.vy = ropelinks[i].position.vy - ropelinks[i-1].position.vy;

		v.vz = ropelinks[i].position.vz - ropelinks[i-1].position.vz;
		
		crateKeepPointWithinSphere(&v, CRATE_SEGMENTLENGTH);
	
		ropelinks[i].position.vx = v.vx + ropelinks[i-1].position.vx;
	
		ropelinks[i].position.vy = v.vy + ropelinks[i-1].position.vy;
	
		ropelinks[i].position.vz = v.vz + ropelinks[i-1].position.vz;

		if((crate.state==CRATE_FLYING)||(crate.state==CRATE_WON) ||(crate.state==CRATE_STARTING) || (crate.state==CRATE_MOVINGCAMERA))
			ropelinks[i].position.vy+=(i*3);
		else
			ropelinks[i].position.vy-=(i*3);
	}

	// Put Mrs Tweedy at the position of the last link in the rope

	mrstweedy.actor->position.vx = ropelinks[mrstweedy.currentlink].position.vx + (mrstweedy.linkoffset.vx);
	mrstweedy.actor->position.vy = ropelinks[mrstweedy.currentlink].position.vy + (mrstweedy.linkoffset.vy);
	mrstweedy.actor->position.vz = ropelinks[mrstweedy.currentlink].position.vz + (mrstweedy.linkoffset.vz);
}






//----------------------------------------------------------------------------------
//	Function 	: static void crateDrawBriefing(void)
//	Purpose 	: Draw a short briefing screen at the start of the flymachine
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------

static void crateDrawBriefing(void)
{
	int wid;

	if(gameTextLang == LANG_D)
		wid = 460;
	else
		wid = 320;

#ifdef _WINDOWS
//	crateOverlayAlpha(-wid/2, -96, wid, 140, 0x000088, SEMITRANS_ADD);
	crateOverlayAlpha(-wid/2, -96, wid, 140, 0x000088, SEMITRANS_SEMI);
	overlayLine(-wid/2,-96, wid/2,-96,0xffffff);			// top
	overlayLine(-wid/2, 44, wid/2, 44,0x888888);			// bottom
	overlayLine(-wid/2,-96,-wid/2, 44,0xffffff);			// left
	overlayLine( wid/2,-96, wid/2, 44,0x888888);			// right
#endif

	fontCentredMultiLine(font,0,-84,wid-2,TEXTSTR(TEXTSTR_SUB_CRATE_BRIEFING1),128,128,128);
	fontCentredMultiLine(font,0,-60,wid-2,TEXTSTR(TEXTSTR_SUB_CRATE_BRIEFING2),128,128,128);
	fontCentredMultiLine(font,0,-18,wid-2,TEXTSTR(TEXTSTR_SUB_CRATE_BRIEFING3),128,128,128);
	fontCentredMultiLine(font,0, 22,wid-2,TEXTSTR(TEXTSTR_SUB_DOG_BRIEFING5),128,128,128);

#ifndef _WINDOWS
	overlayLine(-wid/2,-96, wid/2,-96,0xffffff);			// top
	overlayLine(-wid/2, 44, wid/2, 44,0x888888);			// bottom
	overlayLine(-wid/2,-96,-wid/2, 44,0xffffff);			// left
	overlayLine( wid/2,-96, wid/2, 44,0x888888);			// right
	crateOverlayAlpha(-wid/2, -96, wid, 140, 0x000088, SEMITRANS_SEMI);
#endif
}





//----------------------------------------------------------------------------------
//	Function 	: static void crateStopSamples(void)
//	Purpose 	: Stop all samples from playing
//	Parameters 	: None
//	Returns 	: None
//	Info 		:
//----------------------------------------------------------------------------------


static void crateStopSamples(void)
{
	int i;
	for(i=0;i<NUM_CRATE_SAMPLES;i++)
		sfxStopSample(crateSamples[i],-1);
}





//----------------------------------------------------------------------------------
//	Function 	: static void crateGenerateNewAxe(void)
//	Purpose 	: Generate a new axe and set it's attributes
//	Parameters 	: None
//	Returns 	: None
//	Info 		: Basically just makes the axe active and sets its speed so it gets thrown straight out in front of the tweedy!
//----------------------------------------------------------------------------------

static void crateGenerateNewAxe(void)
{
	axe.active = 1;

	// Set starting position for the axe

	axe.actor->position.vx = mrstweedy.actor->position.vx;
	axe.actor->position.vy = mrstweedy.actor->position.vy;
	axe.actor->position.vz = mrstweedy.actor->position.vz + 40;

	axe.currentangle.vy = (mrstweedy.actor->psiData.object->rotate.vy)+2048;
				
	// Give the Axe the same (but double z) velocities as the crate

	axe.velocity.vz = (crate.speed.vz*4096)/2048;
	axe.velocity.vx = crate.speed.vx;

	crateAddSampleToQueue(crateSamples[CRATESOUND_AXETHROW],1);
}





//----------------------------------------------------------------------------------
//	Function 	: static void crateMrsTweedyGoToClimb(void)
//	Purpose 	: Make Mrs T Climb
//	Parameters 	: None
//	Returns 	: None
//	Info 		: 
//----------------------------------------------------------------------------------

static void crateMrsTweedyGoToClimb(void)
{
	mrstweedy.distancecounter = 20;
	mrstweedy.linkoffset.vx = 0;
	mrstweedy.linkoffset.vy = 0;
	mrstweedy.state = MRSTWEEDY_CLIMBING;
	mrstweedy.climbcounter = CLIMB_DELAY;
	mrstweedy.desiredanimsegment = MRSTWEEDY_CLIMBING;
}





//----------------------------------------------------------------------------------
//	Function 	: static void crateDoAxeCollisionCheck(void)
//	Purpose 	: Collision check between the axe and the flying machine
//	Parameters 	: None
//	Returns 	: None
//	Info 		: 
//----------------------------------------------------------------------------------

static void crateDoAxeCollisionCheck(void)
{
	// This is just box collision unfortunately, as neither the axe or the crate have their own collision

	if((axe.actor->position.vx<crate.actor->position.vx+400) && (axe.actor->position.vx>crate.actor->position.vx-400))
	{
		if((axe.actor->position.vz<crate.actor->position.vz+400) && (axe.actor->position.vz>crate.actor->position.vz-100))
		{
			dsEvent_Add(0, 0, 1, 1200, 0, 7, NULL);
			axe.active = 0;
			crateAddSampleToQueue(crateSamples[CRATESOUND_AXE1],1);

			if(utilRandomInt(500)>300)
			{
				int i;
				
				do
				{
					i = utilRandomInt(3);
				} while (i==lastfowlersample);
				
//				DB("i:%d   ",i);

				lastfowlersample = i;

				if(i==0)
					crateAddSampleToQueue(crateSamples[CRATESOUND_FOWLER1],0);
				else if(i==1)
					crateAddSampleToQueue(crateSamples[CRATESOUND_FOWLER3],0);
				else if(i==2)
					crateAddSampleToQueue(crateSamples[CRATESOUND_FOWLER4],0);
				else if (i==3)
					crateAddSampleToQueue(crateSamples[CRATESOUND_GINGER3],0);
			}

			crateGeneratePartExplosion(&crate.actor->position);
			dust.active = 1;
			dust.position.vx = (crate.actor->position.vx - mycamera.vpx);
			dust.position.vy = crate.actor->position.vy - mycamera.vpy + 200;
			dust.position.vz = (crate.actor->position.vz - mycamera.vpz) - 750;
			dust.counter = 14;
			dust.scale = 512;
	
			hitcounter = HIT_DELAY;
		}
	}

	if(axe.actor->position.vz >= crate.actor->position.vz+3000)
	{
		axe.active = 0;
	}
}






//----------------------------------------------------------------------------------
//	Function 	: static void crateInitialiseRope(void)
//	Purpose 	: Init the rope
//	Parameters 	: None
//	Returns 	: None
//	Info 		: 
//----------------------------------------------------------------------------------

static void crateInitialiseRope(void)
{
	int i;

	// Set the starting positions for all of the segments in the rope
	// Relative to the origin of the crate

	ropelinks[0].position.vx = crate.actor->position.vx;			// The first link is positioned on the crate but slightly below and behind the origin
	ropelinks[0].position.vy = crate.actor->position.vy + 300;
	ropelinks[0].position.vz = crate.actor->position.vz - 390;

	for(i=1;i<CRATE_NUMLINKS; i++)									// the other links are positioned at the same X, but behind the first one and slightly lower
	{
		ropelinks[i].position.vx = 0;
		ropelinks[i].position.vy = 300;
		ropelinks[i].position.vz = ropelinks[i-1].position.vz - CRATE_SEGMENTLENGTH;
	}
}





//----------------------------------------------------------------------------------
//	Function 	: static void crateInitialiseCrate(void)
//	Purpose 	: Init the crate
//	Parameters 	: None
//	Returns 	: None
//	Info 		: 
//----------------------------------------------------------------------------------


static void crateInitialiseCrate(void)
{
	crate.model = BFF_IsModelLoaded("CRATE");
	ASSERT(crate.model);

	crate.actor = actorCreate(crate.model);
	ASSERT(crate.actor);

	actorInitAnim(crate.actor);										
	actorAnimate(crate.actor, 0, 1, 0, 512, 1, 0);

	crate.desiredangle.vy = 0;
	crate.desiredangle.vx = 0;
	crate.actor->position.vx = 0;
	crate.actor->position.vy = -200;
	crate.desiredheight = -200;
	crate.actor->position.vz = -35000;
	crate.actor->psiData.object->rotate.vx = 0;	
	crate.actor->psiData.object->rotate.vy = 0;	
	crate.actor->psiData.object->rotate.vz = 0;	
	crate.turning = 0;
}





//----------------------------------------------------------------------------------
//	Function 	: static void crateSetUpSamples(void)
//	Purpose 	: Set up the samples
//	Parameters 	: None
//	Returns 	: None
//	Info 		: 
//----------------------------------------------------------------------------------


static void crateSetUpSamples(void)
{
	// Get pointers to the SFX

	crateSamples[CRATESOUND_MRST1] = sfxFindSampleInAllBanks("mrst_hit1");
	ASSERT(crateSamples[CRATESOUND_MRST1]);

	crateSamples[CRATESOUND_MRST2] = sfxFindSampleInAllBanks("mrst_hit2");
	ASSERT(crateSamples[CRATESOUND_MRST2]);

	crateSamples[CRATESOUND_MRST3] = sfxFindSampleInAllBanks("mrst_hit3");
	ASSERT(crateSamples[CRATESOUND_MRST3]);

	crateSamples[CRATESOUND_FOWLER1] = sfxFindSampleInAllBanks("subgames_fowler_05");
	ASSERT(crateSamples[CRATESOUND_FOWLER1]);

	crateSamples[CRATESOUND_FOWLER2] = sfxFindSampleInAllBanks("subgames_fowler_17");
	ASSERT(crateSamples[CRATESOUND_FOWLER2]);

	crateSamples[CRATESOUND_FOWLER3] = sfxFindSampleInAllBanks("subgames_fowler_22");
	ASSERT(crateSamples[CRATESOUND_FOWLER3]);

	crateSamples[CRATESOUND_FOWLER4] = sfxFindSampleInAllBanks("subgames_fowler_23");
	ASSERT(crateSamples[CRATESOUND_FOWLER4]);

	crateSamples[CRATESOUND_GINGER1] = sfxFindSampleInAllBanks("in_game_ginger_75");
	ASSERT(crateSamples[CRATESOUND_GINGER1]);

	crateSamples[CRATESOUND_GINGER2] = sfxFindSampleInAllBanks("in_game_ginger_78");
	ASSERT(crateSamples[CRATESOUND_GINGER2]);

	crateSamples[CRATESOUND_GINGER3] = sfxFindSampleInAllBanks("in_game_ginger_52");
	ASSERT(crateSamples[CRATESOUND_GINGER3]);

	crateSamples[CRATESOUND_AXE1] = sfxFindSampleInAllBanks("axe1");
	ASSERT(crateSamples[CRATESOUND_AXE1]);

	crateSamples[CRATESOUND_WINDY] = sfxFindSampleInAllBanks("windy1");
	ASSERT(crateSamples[CRATESOUND_WINDY]);

	crateSamples[CRATESOUND_WINGS] = sfxFindSampleInAllBanks("wingflap1");
	ASSERT(crateSamples[CRATESOUND_WINGS]);

	crateSamples[CRATESOUND_AXETHROW] = sfxFindSampleInAllBanks("axethrow");
	ASSERT(crateSamples[CRATESOUND_AXETHROW]);

	crateSampleTimer=0;

	crateClearSoundQueue();
}





//----------------------------------------------------------------------------------
//	Function 	: static void crateSetUpTextures(void)
//	Purpose 	: Set up the textures
//	Parameters 	: None
//	Returns 	: None
//	Info 		: 
//----------------------------------------------------------------------------------


static void crateSetUpTextures(void)
{
	int i;

	dust.sprites[0] = textureFindInAllBanks("DUST1");
	ASSERT(dust.sprites[0]);

	dust.sprites[1] = textureFindInAllBanks("DUST2");
	ASSERT(dust.sprites[1]);

	dust.sprites[2] = textureFindInAllBanks("DUST3");
	ASSERT(dust.sprites[2]);

	dust.sprites[3] = textureFindInAllBanks("DUST4");
	ASSERT(dust.sprites[3]);

	explosion.texture = textureFindInAllBanks("WOOD");
	ASSERT(explosion.texture);

	cratesky1 = textureFindInAllBanks("BSKY01");
	ASSERT(cratesky1);

	cratesky2 = textureFindInAllBanks("BSKY02");
	ASSERT(cratesky2);

	for(i=0;i<CRATE_NUMLINKS;i+=4)
	{
		xmaslights[i].sprite = textureFindInAllBanks("RED");
		ASSERT(xmaslights[i].sprite);
		xmaslights[i+1].sprite = textureFindInAllBanks("ORANGE");
		ASSERT(xmaslights[i+1].sprite);
		xmaslights[i+2].sprite = textureFindInAllBanks("BLUE");
		ASSERT(xmaslights[i+1].sprite);
		xmaslights[i+3].sprite = textureFindInAllBanks("GREEN");
		ASSERT(xmaslights[i+3].sprite);
	}
}





//----------------------------------------------------------------------------------
//	Function 	: static void crateInitialiseAxe(void)
//	Purpose 	: Init the axe
//	Parameters 	: None
//	Returns 	: None
//	Info 		: 
//----------------------------------------------------------------------------------

static void crateInitialiseAxe(void)
{
	axe.model = BFF_IsModelLoaded("AXE");
	ASSERT(axe.model);

	axe.actor = actorCreate(axe.model);
	ASSERT(axe.actor);

	actorInitAnim(axe.actor);										
	actorAnimate(axe.actor, 0, 1, 0, 512, 1, 0);
	
	axe.active = 0;
	axe.velocity.vx = 0;
	axe.velocity.vy = 0;
	axe.velocity.vz = 0;
}



//----------------------------------------------------------------------------------
//	Function 	: static void crateSetUpStars(void)
//	Purpose 	: Set up the stars
//	Parameters 	: None
//	Returns 	: None
//	Info 		: 
//----------------------------------------------------------------------------------

static void crateSetUpStars(void)
{
	int i;

	// Get some random positions for the stars

	for(i = 0; i < 130; i++)
	{
#ifdef DREAMCAST
		do
		{
			randomx[i] = (utilRandomInt(640));				
			randomy[i] = (utilRandomInt(480));
		}while((randomx[i] > 480) && (randomx[i] < 620) && (randomy[i] < 140));
#else
		randomx[i] = (utilRandomInt(1024))-512;				
		randomy[i] = (utilRandomInt(160))-128;	
#endif
	}
}




//----------------------------------------------------------------------------------
//	Function 	: static void crateInitialiseTweedy(void)
//	Purpose 	: Init the Tweedy
//	Parameters 	: None
//	Returns 	: None
//	Info 		: 
//----------------------------------------------------------------------------------

static void crateInitialiseTweedy(void)
{
	mrstweedy.model = BFF_IsModelLoaded("MRSTWEEDYBOSS");

	ASSERT(mrstweedy.model);

	mrstweedy.actor = actorCreate(mrstweedy.model);
	ASSERT(mrstweedy.actor);

	// Set starting positions for the models / sprites and initialise the animations

	actorInitAnim(mrstweedy.actor);										
	actorAnimate(mrstweedy.actor, MRSTWEEDY_HANGING, 1, 0, 256, 1, 0);

	mrstweedy.actor->position.vx = 0;
	mrstweedy.actor->position.vy = -300;
	mrstweedy.actor->position.vz = -35000;
	mrstweedy.state = MRSTWEEDY_HANGING;
	mrstweedy.desiredanimsegment = 0;
	mrstweedy.currentlink = 8;		// Start off on the middle link...
	mrstweedy.climbcounter = 0;
	mrstweedy.linkoffset.vx = 0;
	mrstweedy.linkoffset.vy = 0;
	mrstweedy.linkoffset.vz = 0;
#ifdef DREAMCAST	
	mrstweedy.collision.pCollBox = enemyGlobals.pCollBoxes;
#endif
}



static void crateGetCameraPath(void)		// Get a camera step
{
	camerapos.vrxstep = 0;
	camerapos.vrystep = (600*64)/CRATE_CAMSPEED;
	camerapos.vrzstep = (620*64)/CRATE_CAMSPEED;
	camerapos.vpxstep = (1500*64)/CRATE_CAMSPEED;
	camerapos.vpystep = (-2500*64)/CRATE_CAMSPEED;
	camerapos.vpzstep = (-3180*64)/CRATE_CAMSPEED;
}

#ifdef DREAMCAST
int crateYAdjust = -5;
#else
#define crateYAdjust 0
#endif
static void crateDrawDeadMessage(void)
{
	int numLines;


	fontPrintCentred(font,0,-28+crateYAdjust,TEXTSTR(TEXTSTR_DEATH_MRS_TWEEDIED),128,128,128);

	//BP: Erm.. fontprint is a void function
	// FontCentredMultiLine ain't, though

#ifdef DREAMCAST
	numLines = fontCentredMultiLine(font,0,-12+crateYAdjust,318,TEXTSTR(TEXTSTR_SUB_DOG_FAIL2),128,128,128);
	overlayLine(-164,-40,164,-40,0xffffff);			// top
	overlayLine(-164,numLines*12 - 12,164,numLines*12 - 12,0x888888);			// bottom
	overlayLine(-164,-40,-164,numLines*12 - 12,0xffffff);			// left
	overlayLine(164,-40,164,numLines*12 - 12,0x888888);			// right

	overlayAlphaBox(-164, -40, 328, numLines*12 + 28, 80, 80,80);
#else
	numLines = fontCentredMultiLine(font,0,-12+crateYAdjust,318,TEXTSTR(TEXTSTR_SUB_DOG_FAIL2),132,132,132);

	overlayLine(-164,-40,164,-40,0xffffff);			// top
	overlayLine(-164,-40+numLines*12 +32,164,-40+numLines*12+32,0x888888);			// bottom
	overlayLine(-164,-40,-164,-40+numLines*12 +32,0xffffff);			// left
	overlayLine(164,-40,164,-40+numLines*12 +32,0x888888);			// right

	overlayAlphaBox(-164, -40, 332, numLines*12 +32, 80, 80,80);

#endif
}

static void cratePlaySamples(void)
{
	crateSampleTimer++;

	
	if(crateSampleTimer==2)
	//	soundPlaySample(crateSamples[14], 0xffff, 800);
		crateAddSampleToQueue(crateSamples[CRATESOUND_GINGER1], 0);

	else if(crateSampleTimer==70)
	//	soundPlaySample(crateSamples[15],0xffff,800);
		crateAddSampleToQueue(crateSamples[CRATESOUND_GINGER2],0);
}




//----------------------------------------------------------------------------------
//	Function 	: static void crateAddSampleToQueue(SfxSampleType *sample, int priority)
//	Purpose 	: Add a sample to the queue
//	Parameters 	: None
//	Returns 	: None
//	Info 		: 
//----------------------------------------------------------------------------------


static void crateAddSampleToQueue(SfxSampleType *sample, int priority)
{
	int i;
	// If not priority, just stick it in the first free slot
	if(!priority)
	{
		for(i=0;((i<4) && (crateQueuedSamples[i]!=NULL));i++);		// Get first empty slot
		crateQueuedSamples[i] = sample;				// and stick the sample in 
//		DB("Sample added to queue at position %d\n",i);
	}

	// Otherwise, stick it at the front, and move the others down
	else
	{
		crateQueuedSamples[3] = crateQueuedSamples[2];
		crateQueuedSamples[2] = crateQueuedSamples[1];
		crateQueuedSamples[1] = crateQueuedSamples[0];
		crateQueuedSamples[0] = sample;
//		DB("Sample added to queue at position 0");
	}

}




//----------------------------------------------------------------------------------
//	Function 	: static void cratePlayQueuedSamples(void)
//	Purpose 	: Play whatever sample is in the queue position 0
//	Parameters 	: None
//	Returns 	: None
//	Info 		: 
//----------------------------------------------------------------------------------

static void cratePlayQueuedSamples(void)
{
	int channel;

	if(crateQueuedSamples[0]!=NULL)
	{
		// Play whatever's in slot 0 of the samples
		// RFW - Changed to play at the sample's defined sample rate, rather than at 800
		// (Because the Euro speech was recorded at a different rate)
		channel = soundPlaySample(crateQueuedSamples[0], 0xffff,0);
		
		// And, if it's played properly, move the rest down...
		if(channel)
		{
		//	DB("Playing sample from queue at position 0\n");
			crateQueuedSamples[0] = crateQueuedSamples[1];
			crateQueuedSamples[1] = crateQueuedSamples[2];
			crateQueuedSamples[2] = crateQueuedSamples[3];
			crateQueuedSamples[3] = NULL;
		}
	}
}


static void crateClearSoundQueue(void)
{
	crateQueuedSamples[0] = crateQueuedSamples[1] = crateQueuedSamples[2] = crateQueuedSamples[3] = NULL;
}


void crateUpdateAnims(void)
{
	//actorSetAnimSpeed(mrstweedy.actor,0);
	//actorSetAnimSpeed(crate.actor,0);

	actorMaintainAnimation(mrstweedy.actor);
	actorMaintainAnimation(crate.actor);
	actorMaintainAnimation(axe.actor);
}
