// tired of these :)
#pragma warning (disable : 4244)
#pragma warning (disable : 4305)


#include "r_roks.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


rRock_t		rocks[R_MAX_ROCKS];
rProj_t		projs[R_MAX_PROJS];
rPart_t		parts[R_MAX_PARTS];


enum rGamePhase_e
{
	R_PHASE_MENU,		// main menu
	R_PHASE_GAME		// 
};

rGamePhase_e		rPhase = R_PHASE_MENU;



rSounds_t	rSounds;

rTexs_t		rTexs;					// textures

int			rTune;					// music track

float		rServerTime = 0;
float		rTimeKey = 0;

vec2_t		rAimPos;				// aim position

int			rScore;
float		rScreenWidth;
float		rScreenHeight;


rCraft_t	crafts[R_MAX_CRAFTS];


char*	rGetBasePath (char *add)
{
	static char bufs[8][512];		// FIXME: not very safe :)
	static char bufIndex = 0;		//

	bufIndex = (bufIndex + 1) & (8-1);

	sprintf (bufs[bufIndex], "crimsonroks/%s", add);
	
	return bufs[bufIndex];
}


inline float maxf (float a, float b)
{
	if (a > b) return a;
	return b;
}


void		rInitMod ()
{
	cl->CORE_Printf ("*** Crimsonland MOD: CrimsonRoks ***\n");
	cl->CORE_Printf ("-> rInitMod ()\n");

	cl->CORE_Printf (":: Getting system info...\n");
	rScreenWidth = *cl->CORE_GetVar ("v_width")->floatValue;
	rScreenHeight = *cl->CORE_GetVar ("v_height")->floatValue;

	cl->CORE_Printf (":: Loading textures...\n");
	memset (&rTexs, 0, sizeof (rTexs_t));
	rTexs.projsParts	= cl->GFX_LoadTexture (rGetBasePath ("projs_parts_128x128.tga"));
	rTexs.rockTiny		= cl->GFX_LoadTexture (rGetBasePath ("rock_tiny_128x128.tga"));
	rTexs.rockSmall		= cl->GFX_LoadTexture (rGetBasePath ("rock_small_256x256.tga"));
	rTexs.rockMedium	= cl->GFX_LoadTexture (rGetBasePath ("rock_medium_512x512.tga"));
	rTexs.craftBase		= cl->GFX_LoadTexture (rGetBasePath ("craft_base.tga"));
	rTexs.craftShield	= cl->GFX_LoadTexture (rGetBasePath ("craft_shield.tga"));
	rTexs.craftWep[0]	= cl->GFX_LoadTexture (rGetBasePath ("craft_wepLev1.tga"));
	rTexs.craftWep[1]	= cl->GFX_LoadTexture (rGetBasePath ("craft_wepLev2.tga"));
	rTexs.craftWep[2]	= cl->GFX_LoadTexture (rGetBasePath ("craft_wepLev3.tga"));
	rTexs.stars			= cl->GFX_LoadTexture (rGetBasePath ("stars_512x256.tga"));
	rTexs.earth			= cl->GFX_LoadTexture (rGetBasePath ("earth_512x512.tga"));
	rTexs.logo			= cl->GFX_LoadTexture (rGetBasePath ("logo_512x256.tga"));

	cl->CORE_Printf (":: Loading sounds...\n");
	memset (&rSounds, 0, sizeof (rSounds));
	rSounds.expl	= cl->SFX_LoadSample (rGetBasePath ("expl.ogg"));
	rSounds.hit		= cl->SFX_LoadSample (rGetBasePath ("hit.ogg"));
	rSounds.shoot	= cl->SFX_LoadSample (rGetBasePath ("shoot.ogg"));
	rSounds.bonus	= cl->SFX_LoadSample (rGetBasePath ("bonus.ogg"));

	cl->CORE_Printf (":: Loading music...\n");
	rTune = cl->SFX_LoadTune (rGetBasePath ("roks.ogg"));

	cl->CORE_Printf (":: Init done.\n");
}


void		rShutdownMod ()
{
	cl->CORE_Printf ("-> rShutdownMod ()\n");

	cl->CORE_Printf (":: Freeing textures...\n");
	cl->GFX_FreeTexture (rTexs.projsParts);
	cl->GFX_FreeTexture (rTexs.rockTiny);
	cl->GFX_FreeTexture (rTexs.rockSmall);
	cl->GFX_FreeTexture (rTexs.rockMedium);
	cl->GFX_FreeTexture (rTexs.craftBase);
	cl->GFX_FreeTexture (rTexs.craftShield);
	cl->GFX_FreeTexture (rTexs.craftWep[0]);
	cl->GFX_FreeTexture (rTexs.craftWep[1]);
	cl->GFX_FreeTexture (rTexs.craftWep[2]);
	cl->GFX_FreeTexture (rTexs.stars);
	cl->GFX_FreeTexture (rTexs.earth);
	cl->GFX_FreeTexture (rTexs.logo);

	cl->CORE_Printf (":: Freeing sounds...\n");
	cl->SFX_FreeSample (rSounds.expl);
	cl->SFX_FreeSample (rSounds.hit);
	cl->SFX_FreeSample (rSounds.shoot);
	cl->SFX_FreeSample (rSounds.bonus);

	cl->CORE_Printf (":: Freeing music...\n");
	cl->SFX_FreeTune (rTune);
}


void rRespawnCraft (rCraft_t *craft, vec2_t &pos)
{
	craft->invulCount = 5.0f;
	craft->livesLeft--;
	craft->health = 1;
	craft->origin = pos;
	craft->angle = 0;
	craft->radius = 32;
	craft->vel = vec2_t (0, 0);
	craft->alive = craft->livesLeft > 0;
	craft->spawnCount = 2;
		
	//craft->weaponLevel = 1;
}


void		rInitGame ()
{
	cl->CORE_Printf ("-> rInitGame ()\n");

	memset (rocks, 0, sizeof (rocks));
	memset (projs, 0, sizeof (projs));
	memset (parts, 0, sizeof (parts));
	memset (crafts, 0, sizeof (crafts));

	crafts[0].livesLeft = 0;
	crafts[0].weaponLevel = 1;
	rRespawnCraft (&crafts[0], 0.5f * vec2_t (rScreenWidth, rScreenHeight));

	rServerTime = 0;
	srand (1);

	rPhase = R_PHASE_MENU;

//	cl->SFX_StopTune (0);
	// start playing the tune
	cl->SFX_PlayTune (rTune);

	rScore = 0;
}


inline void		rSetSubsetFrame (int frame, int subDiv)
{
	int		xp, yp;
	float subDivRcp = 1.0f / subDiv;
	xp = frame % subDiv;
	yp = frame / subDiv;
	const float subPixel = 1.0f / 128;	// to avoid bilinear filtering artifacts (note this isn't perfect as you'd need the texture size)

	cl->GFX_SetSubset (xp*subDivRcp+subPixel, yp*subDivRcp+subPixel, (xp+1)*subDivRcp-subPixel, (yp+1)*subDivRcp-subPixel);
}

void		rDrawRocks ()
{
	vec2_t		lpos; // lerped position for drawing the entity
	int			i;

	cl->GFX_SetBlendMode (BM_SRCALPHA, BM_INVSRCALPHA);
	cl->GFX_SetTexture (rTexs.rockMedium);
	cl->GFX_SetColor (1,1,1,1);


	cl->GFX_Begin ();
	for (i = 0; i < R_MAX_ROCKS; i++)
		if (rocks[i].alive)
		{
			rRock_t *rock = &rocks[i];
			lpos = rock->origin + rock->vel * (rServerTime - rock->time);
			rSetSubsetFrame (rock->animFrame * 4 * 8, 8);

			cl->GFX_Quad (lpos.x - rock->radius, lpos.y - rock->radius, 2*rock->radius, 2*rock->radius);
		}

	cl->GFX_End ();

}


void		rDrawProjs ()
{
	vec2_t		lpos; // lerped position for drawing the ent
	int			i;

	cl->GFX_SetBlendMode (BM_SRCALPHA, BM_ONE);
	cl->GFX_SetTexture (rTexs.projsParts);
	cl->GFX_SetColor (1,1,1,1);

	cl->GFX_Begin ();
	for (i = 0; i < R_MAX_PROJS; i++)
		if (projs[i].alive)
		{
			rProj_t *proj = &projs[i];
			lpos = proj->origin + proj->vel * (rServerTime - proj->time);
			if (proj->frame & R_BIGPARTBIT)
				rSetSubsetFrame (proj->frame&~R_BIGPARTBIT, 2);
			else
				rSetSubsetFrame (proj->frame&~R_BIGPARTBIT, 4);
			cl->GFX_QuadRot (lpos.x - proj->radius, lpos.y - proj->radius, 2*proj->radius, 2*proj->radius, -proj->angle - M_PI/2 - M_PI/4);
		}
	cl->GFX_End ();

	// draw glows
	cl->GFX_SetBlendMode (BM_SRCALPHA, BM_ONE);
	cl->GFX_SetTexture (rTexs.projsParts);
	rSetSubsetFrame (3, 2);
	cl->GFX_SetColor (0.8f,0.4f,0.2f,0.6f);

	cl->GFX_Begin ();
	for (i = 0; i < R_MAX_PROJS; i++)
		if (projs[i].alive)
		{
			rProj_t *proj = &projs[i];
			lpos = proj->origin + proj->vel * (rServerTime - proj->time);
			cl->GFX_Quad (lpos.x - 40, lpos.y - 40, 80, 80);
		}
	cl->GFX_End ();
}


void		rDrawParts ()
{
	vec2_t		lpos; // lerped position for drawing the entity
	int			i;

	cl->GFX_SetBlendMode (BM_SRCALPHA, BM_ONE);
	cl->GFX_SetTexture (rTexs.projsParts);
	rSetSubsetFrame (2, 2); // lower left corner of texture divided in 4 ways

	cl->GFX_Begin ();
	for (i = 0; i < R_MAX_PARTS; i++)
		if (parts[i].alive)
		{
			rPart_t *part = &parts[i];
			lpos = part->origin + part->vel * (rServerTime - part->time);
			if (part->frame & R_BIGPARTBIT)
				rSetSubsetFrame (part->frame&~R_BIGPARTBIT, 2);
			else
				rSetSubsetFrame (part->frame&~R_BIGPARTBIT, 4);
			cl->GFX_SetColor (part->color.r, part->color.g, part->color.b, part->color.a * part->timeToLive);
			cl->GFX_QuadRot (lpos.x - part->radius, lpos.y - part->radius, 2*part->radius, 2*part->radius, -part->angle - M_PI/2 - M_PI/4);
		}
	cl->GFX_End ();
}



rProj_t* rSpawnProj (vec2_t &to, vec2_t &vel, int frame)
{
	rProj_t *proj = &projs[0];
	rProj_t *end = &projs[R_MAX_PROJS-1];

	while (proj != end)
	{
		if (!proj->alive)
		{
			proj->alive = true;
			proj->time = rServerTime;
			proj->origin = to;
			proj->vel = vel;
			proj->radius = 4;
			proj->damage = 12;
			proj->angle = VEC2_Angle (vel);
			proj->frame = frame;
			proj->angle = (rand()&127) * M_PI / 64;
			return proj;
		}
		proj++;
	}
	static rProj_t hoax;
	return &hoax;
}


rRock_t* rSpawnRock (vec2_t &to, vec2_t &vel, float radius)
{
	rRock_t *rock = &rocks[0];
	rRock_t *end = &rocks[R_MAX_ROCKS-1];

	while (rock != end)
	{
		if (!rock->alive)
		{
			rock->alive = true;
			rock->time = rServerTime;
			rock->origin = to;
			rock->vel = vel;
			rock->radius = radius;
			rock->health = 2 * rock->radius;
			rock->animFrame = 0;
			return rock;
		}
		rock++;
	}

	static rRock_t hoax;
	return &hoax;
}


rPart_t* rSpawnPart (vec2_t &to, vec2_t &vel, float radius, int frame)
{
	rPart_t *part = &parts[0];
	rPart_t *end = &parts[R_MAX_PARTS-1];

	while (part != end)
	{
		if (!part->alive)
		{
			part->alive = true;
			part->time = rServerTime;
			part->color = color_t (1,1,1,1);
			part->origin = to;
			part->vel = vel;
			part->radius = radius;
			part->frame = frame;
			part->timeToLive = 1;
			part->angle = VEC2_Angle (part->vel);
			return part;
		}
		part++;
	}
	static rPart_t hoax;
	return &hoax;
}

void rDrawCrafts ()
{
	int			i;
	rCraft_t	*craft;
	rCraft_t	*end = &crafts[R_MAX_CRAFTS-1];

	cl->GFX_SetBlendMode (BM_SRCALPHA, BM_INVSRCALPHA);
	cl->GFX_SetColor (1,1,1,1);
	cl->GFX_SetSubset (0,0,1,1);

	// weapon layers

	for (i = 0; i < 3; i++)
	{
		cl->GFX_SetTexture (rTexs.craftWep[i]);
		cl->GFX_Begin ();
		craft = &crafts[0];
		while (craft != end)
		{
			if (craft->alive && craft->weaponLevel > i)
			{
				vec2_t lpos = craft->origin + craft->vel * (rServerTime - craft->time);

				lpos += 18*craft->canFire * vec2_t (cosf (craft->angle), sinf (craft->angle));
				cl->GFX_QuadRot (lpos.x - craft->radius, lpos.y - craft->radius, 2*craft->radius, 2*craft->radius, -craft->angle - M_PI/2 - M_PI/4);
			}
			craft++;
		}
		cl->GFX_End ();
	}

	// base layer
	cl->GFX_SetTexture (rTexs.craftBase);
	cl->GFX_Begin ();
	craft = &crafts[0];
	while (craft != end)
	{
		if (craft->alive)
		{
			if (craft->invulCount > 0)
				if (craft->invulCount < 1)
					cl->GFX_SetColor (1,1,1, 1 - 0.5f*craft->invulCount);
				else
					cl->GFX_SetColor (1,1,1, 0.5f);

			vec2_t lpos = craft->origin + craft->vel * (rServerTime - craft->time);
			cl->GFX_QuadRot (lpos.x - craft->radius, lpos.y - craft->radius, 2*craft->radius, 2*craft->radius, -craft->angle - M_PI/2 - M_PI/4);
		}
		craft++;
	}
	cl->GFX_End ();

	// shield layer
/*	cl->GFX_SetTexture (rTexs.craftShield);
	cl->GFX_Begin ();
	craft = &crafts[0];
	while (craft != end)
	{
		if (craft->alive)
		{
			vec2_t lpos = craft->origin + craft->vel * (rServerTime - craft->time);
			cl->GFX_QuadRot (lpos.x - craft->radius, lpos.y - craft->radius, 2*craft->radius, 2*craft->radius, -craft->angle - M_PI/2 - M_PI/4);
		}
		craft++;
	}
	cl->GFX_End ();*/
}



void		rDrawFrame ()
{
//	cl->GFX_Clear (0,0,0,0);						// clear screen to black

	cl->GFX_SetBlendMode (BM_SRCALPHA, BM_INVSRCALPHA);
	float resize = 0.5f * (float) rScreenHeight / rScreenWidth;
	cl->GFX_SetSubset (resize, 1-resize, 1, 1);
	cl->GFX_SetSubset (0,0,1,1);
	cl->GFX_SetColor (1,1,1,1);
	cl->GFX_SetTexture (rTexs.stars);
	cl->GFX_Begin ();
	cl->GFX_Quad (0,0, (float) rScreenWidth, (float) rScreenHeight);
	cl->GFX_End ();

	cl->GFX_SetSubset (0,0,1,1);
	cl->GFX_SetTexture (rTexs.earth);
	cl->GFX_Begin ();
	float radius = 100 + 10 * rServerTime;
	if (rPhase == R_PHASE_MENU)
	{
		radius = 100 + 64*sinf (0.01f*rTimeKey);
		cl->GFX_QuadRot (0.5f*rScreenWidth-radius, 0.5f*rScreenHeight-radius, 2*radius, 2*radius, 0.019f * rTimeKey);
	}
	else
	{
		cl->GFX_QuadRot (0.5f*rScreenWidth-radius, 0.5f*rScreenHeight-radius, 2*radius, 2*radius, 0.018f * rServerTime);
	}

	cl->GFX_End ();


	cl->GFX_SetColor (1,1,1,1);
	cl->GFX_Printf (rScreenWidth - 128, 10, "Score: %d", rScore);

	rDrawRocks ();
	rDrawProjs ();
	rDrawCrafts ();
	rDrawParts ();

	cl->GFX_SetColor (1,1,1,1);
	cl->GFX_SetBlendMode (BM_SRCALPHA, BM_INVSRCALPHA);

	switch (rPhase)
	{
		case R_PHASE_MENU:
		{
			cl->GFX_SetBlendMode (BM_SRCALPHA, BM_INVSRCALPHA);
			cl->GFX_SetTexture (rTexs.logo);
			cl->GFX_SetColor (1,1,1,1);
			cl->GFX_SetSubset (0,0,1,1);

			cl->GFX_Begin ();
			cl->GFX_SetColor (1,1,1,1);
			cl->GFX_QuadRot (0.5f*rScreenWidth - 256, 0, 512, 256, 0.01f*sinf (rTimeKey)  - M_PI/4);
			
			cl->GFX_End ();

			cl->GFX_SetBlendMode (BM_SRCALPHA, BM_ONE);
			cl->GFX_Begin ();
			cl->GFX_SetColor (1,1,1, maxf (1.6f*sinf (rTimeKey), 0));
			float mod = 1 + maxf (0, 1.7f*sinf (rTimeKey));
			//cl->GFX_QuadRot (0.5f*rScreenWidth - 256 * mod, 256 - 128 * mod, 512 * mod, 256 * mod);
			cl->GFX_End ();

			cl->GFX_SetColor (1,1,1,0.8f + 0.5f * sinf (0.4f*rTimeKey));
			char *msg = "Click left mouse button to start!";
			cl->GFX_Printf (0.5f*rScreenWidth - 0.5f * cl->GFX_GetStringWidth (msg), 0.9f*rScreenHeight, msg);
			cl->GFX_SetBlendMode (BM_SRCALPHA, BM_INVSRCALPHA);

			break;
		}

		case R_PHASE_GAME:
		{
			cl->GFX_SetSubset (0,0,1,1);
			cl->GFX_SetTexture (rTexs.craftBase);
			cl->GFX_Begin ();
			for (int i = 0; i < crafts[0].livesLeft; i++)
			{
				cl->GFX_Quad (8 + 42 * i, 0, 42, 42);
			}
			cl->GFX_End ();

			break;
		}
	}



}

vec2_t rGetRandomVec (float angleFrom, float angleTo)
{
	float	r;
	r = angleFrom + (rand()&1023) / 1024.0f * (angleTo - angleFrom);
	return vec2_t (cosf (r), sinf (r));
}


void rSpawnExplosion (vec2_t &pos, float power)
{
	int l;
	for (l = 0; l < 28*power; l++)
	{
		rPart_t *part;
		part = rSpawnPart (pos + (10+rand()%(int)(30*power)) * rGetRandomVec (0, 2*M_PI),
			-40 * rGetRandomVec (0, 2*M_PI), 16 + rand()%10, (2+rand()%2)|R_BIGPARTBIT);
		part->color.r = 0.5f * 1;
		part->color.b = part->color.g = 0.5f * (0.5f + (rand()%100) * 0.005f);
		part->timeToLive += (rand()%(int)(25*power)) * 0.1f - 0.5f;
	}
	for (l = 0; l < 14*power; l++)
	{
		rPart_t *part;
		part = rSpawnPart (pos, (power*60 + rand()%(int)(power*30)) * rGetRandomVec (0, 2*M_PI), 
			power*16 + power*(rand()%32), rand()%2);
		part->timeToLive += power*(rand()%10) * 0.1f - 0.3f;
	}
}


void rGameLogicFrame (float time, rActions_t *acts)
{
	int i, j;

	// update projectiles

	for (i = 0; i < R_MAX_PROJS; i++)
		if (projs[i].alive)
		{
			rProj_t *proj = &projs[i];
			proj->origin += time * proj->vel;
			proj->time = rServerTime;

			if (proj->origin.x > rScreenWidth + 2*proj->radius ||
				proj->origin.x < -2*proj->radius ||
				proj->origin.y > rScreenHeight + 2*proj->radius ||
				proj->origin.y < -2*proj->radius)
				proj->alive = false;

		}

	// update rocks
	for (i = 0; i < R_MAX_ROCKS; i++)
		if (rocks[i].alive)
		{
			rRock_t *rock = &rocks[i];
			rock->origin += time * rock->vel;
			rock->animFrame += time;
			while (rock->animFrame >= 1) rock->animFrame -= 1;
			rock->time = rServerTime;

			if (rock->origin.x > rScreenWidth + 2*rock->radius ||
				rock->origin.x < -2*rock->radius ||
				rock->origin.y > rScreenHeight + 2*rock->radius ||
				rock->origin.y < -2*rock->radius)
				rock->alive = false;
		}

	// update parts
	rPart_t *part = &parts[0];
	rPart_t *partsend = &parts[R_MAX_PARTS-1];

	while (part != partsend)
	{
		if (part->alive)
		{
			part->origin += time * part->vel;
			part->timeToLive -= time;
			part->angle -= time;
			part->time = rServerTime;

			if (part->timeToLive <= 0)
				part->alive = false;

			if (part->origin.x > rScreenWidth + 2*part->radius ||
				part->origin.x < -2*part->radius ||
				part->origin.y > rScreenHeight + 2*part->radius ||
				part->origin.y < -2*part->radius)
				part->alive = false;
		}
		part++;
	}


	// check for hits
	for (i = 0; i < R_MAX_PROJS; i++) if (projs[i].alive && projs[i].damage > 0)
		for (j = 0; j < R_MAX_ROCKS; j++) if (rocks[j].alive)
		{
			if (VEC2_Length (rocks[j].origin - projs[i].origin) < 0.7f * (rocks[j].radius + projs[i].radius))
			{
				cl->SFX_PlaySample (rSounds.hit);
				rocks[j].health -= projs[i].damage;
				projs[i].alive = false;
				rSpawnExplosion (projs[i].origin, 0.2f);
				rSpawnPart (projs[i].origin, (rand()&63) * rGetRandomVec (0, 2*M_PI), 8 + rand()&15, (2+rand()%2)|R_BIGPARTBIT);
				rSpawnPart (projs[i].origin, (rand()&63) * rGetRandomVec (0, 2*M_PI), 8 + rand()&15, (2+rand()%2)|R_BIGPARTBIT);

				if (rocks[j].health <= 0)
				{
					cl->SFX_PlaySample (rSounds.expl);
					rSpawnExplosion (projs[i].origin, 0.6f);

					rPart_t *part;
					part = rSpawnPart (rocks[j].origin, (rand()&63) * rGetRandomVec (0, 2*M_PI), 32 + rand()&15, (2+rand()%2)|R_BIGPARTBIT);
					part->color = color_t (0.6f, 0.6f, 0.6f, 0.6f);
					part = rSpawnPart (rocks[j].origin, (rand()&63) * rGetRandomVec (0, 2*M_PI), 32 + rand()&15, (2+rand()%2)|R_BIGPARTBIT);
					part->color = color_t (0.6f, 0.6f, 0.6f, 0.6f);

/*					rSpawnPart (rocks[j].origin, (rand()&63) * rGetRandomVec (0, 2*M_PI), 8 + rand()&15);
					rSpawnPart (rocks[j].origin, (rand()&63) * rGetRandomVec (0, 2*M_PI), 8 + rand()&15);
					rSpawnPart (rocks[j].origin, (rand()&63) * rGetRandomVec (0, 2*M_PI), 8 + rand()&15);
*/					rScore += rocks[j].radius;

					if (rocks[j].radius > 16)
					{
						float a = VEC2_Angle (rocks[j].vel);
						rocks[j].radius *= 0.6f;
						rSpawnRock (rocks[j].origin, (rand()&63) * rGetRandomVec (a - 0.3f, a + 0.3f), rocks[j].radius);
						rSpawnRock (rocks[j].origin, (rand()&63) * rGetRandomVec (a - 0.3f, a + 0.3f), rocks[j].radius);
						rSpawnRock (rocks[j].origin, (rand()&63) * rGetRandomVec (a - 0.3f, a + 0.3f), rocks[j].radius);
					}
					rocks[j].alive = false;
				}
			}
		}

	for (j = 0; j < R_MAX_ROCKS; j++) if (rocks[j].alive)
		for (i = 0; i < R_MAX_CRAFTS; i++) if (crafts[i].alive && crafts[i].invulCount <= 0)
		{
			rCraft_t *craft = &crafts[i];
			if (VEC2_Length (rocks[j].origin - crafts[i].origin) < 0.7f * (rocks[j].radius + crafts[i].radius))
			{
				cl->SFX_PlaySample (rSounds.hit);
				cl->SFX_PlaySample (rSounds.expl);
				rocks[j].health -= 1000;
				rSpawnExplosion (craft->origin, 1.5f);
				rRespawnCraft (&crafts[i], 0.5f * vec2_t (rScreenWidth, rScreenHeight));
			}
		}


	
	rCraft_t *craft = &crafts[0];
	rCraft_t *craftEnd = &crafts[R_MAX_CRAFTS-1];

	while (craft != craftEnd)
	{
		if (craft->alive)
		{
			craft->angle = acts->angleTo;
			craft->canThrust -= time;
			craft->time = rServerTime;
			craft->invulCount -= time;
			if (craft->invulCount < 0)
				craft->invulCount = 0;
			
			if (craft->canThrust <= 0 && acts->thrust)
			{
				craft->canThrust += 0.03f;
				craft->vel += 10 * vec2_t (cosf (craft->angle), sinf (craft->angle));
				rSpawnPart (craft->origin + -14 * vec2_t (cosf (craft->angle), sinf (craft->angle)),
					-40 * rGetRandomVec (craft->angle - M_PI/5, craft->angle + M_PI/5), 16, (2+rand()%2)|R_BIGPARTBIT);
				rSpawnPart (craft->origin + -20 * vec2_t (cosf (craft->angle), sinf (craft->angle)),
					-40 * rGetRandomVec (craft->angle - M_PI/5, craft->angle + M_PI/5), 16, (2+rand()%2)|R_BIGPARTBIT);
				rSpawnPart (craft->origin + -16 * vec2_t (cosf (craft->angle), sinf (craft->angle)),
					-40 * rGetRandomVec (craft->angle - M_PI/5, craft->angle + M_PI/5), 12, 1);
				if (VEC2_Length (craft->vel) > 256)
				{
					vec2_t norm = craft->vel;
					VEC2_Normalize (norm);
					craft->vel = 256 * norm;
				}
			}
			if (craft->canThrust < 0) craft->canThrust = 0;

			craft->origin += craft->vel * time;

			if (craft->origin.x > rScreenWidth + craft->radius ||
				craft->origin.x < -craft->radius)
				craft->origin.x = rScreenWidth - craft->origin.x;

			if (craft->origin.y > rScreenHeight + craft->radius ||
				craft->origin.y < -craft->radius)
				craft->origin.y = rScreenHeight - craft->origin.y;

			craft->canFire -= time;
			if (craft->canFire < 0) craft->canFire = 0;

			if (craft->canFire <= 0 && acts->fire)
			switch (craft->weaponLevel)
			{
				case 1:
				{
					craft->canFire += 0.15f;
					vec2_t pos = craft->origin;
					vec2_t projVel, vel;
					rPart_t *part;
					vel = vec2_t (cosf (craft->angle), sinf (craft->angle));
					projVel = 300*vel;

					if (VEC2_Length (projVel) < VEC2_Length (projVel + craft->vel))
						projVel = projVel + craft->vel;
					rProj_t *proj = rSpawnProj (pos + 16*vel, projVel, 0);
					proj->angle = craft->angle;

					part = rSpawnPart (pos + 16*vel, 0.01f * projVel, 4, 2|R_BIGPARTBIT);
					part->timeToLive = 0.5f;
					part->color.b = part->color.g = 0.6f;
					part->color.a = 1.4f;

					cl->SFX_PlaySample (rSounds.shoot);

					break;
				}

				case 2:
				{
					craft->canFire += 0.16f;
					vec2_t pos = craft->origin;
					vec2_t projVel, vel;
					rPart_t *part;
					vel = vec2_t (cosf (craft->angle), sinf (craft->angle));
					projVel = 300*vel;

					if (VEC2_Length (projVel) < VEC2_Length (projVel + craft->vel))
						projVel = projVel + craft->vel;
					rSpawnProj (pos - 14 * VEC2_Perp (vel), projVel, 1);

					part = rSpawnPart (pos - 14 * VEC2_Perp (vel), 0.01f * projVel, 4, 2|R_BIGPARTBIT);
					part->timeToLive = 0.5f;
					part->color.b = part->color.g = 0.6f;
					part->color.a = 1.4f;
					
					rSpawnProj (pos + 14 * VEC2_Perp (vel), projVel, 1);

					part = rSpawnPart (pos + 14 * VEC2_Perp (vel), 0.01f * projVel, 4, 2|R_BIGPARTBIT);
					part->timeToLive = 0.5f;
					part->color.b = part->color.g = 0.6f;
					part->color.a = 1.4f;

					cl->SFX_PlaySample (rSounds.shoot);

					break;
				}

				case 3:
				{
					craft->canFire += 0.17f;
					vec2_t pos = craft->origin;
					vec2_t projVel, vel;
					rPart_t *part;
					vel = vec2_t (cosf (craft->angle), sinf (craft->angle));
					projVel = 300*vel;

					if (VEC2_Length (projVel) < VEC2_Length (projVel + craft->vel))
						projVel = projVel + craft->vel;
					rProj_t *proj = rSpawnProj (pos + 16*vel, projVel, 0);
					proj->angle = craft->angle;

					part = rSpawnPart (pos + 16*vel, 0.01f * projVel, 4, 2|R_BIGPARTBIT);
					part->timeToLive = 0.5f;
					part->color.b = part->color.g = 0.6f;
					part->color.a = 1.4f;

					rSpawnProj (pos - 14 * VEC2_Perp (vel), projVel, 1);
					part = rSpawnPart (pos - 14 * VEC2_Perp (vel), 0.01f * projVel, 4, 2|R_BIGPARTBIT);
					part->timeToLive = 0.5f;
					part->color.b = part->color.g = 0.6f;
					part->color.a = 1.4f;
					
					rSpawnProj (pos + 14 * VEC2_Perp (vel), projVel, 1);
					part = rSpawnPart (pos + 14 * VEC2_Perp (vel), 0.01f * projVel, 4, 2|R_BIGPARTBIT);
					part->timeToLive = 0.5f;
					part->color.b = part->color.g = 0.6f;
					part->color.a = 1.4f;

					cl->SFX_PlaySample (rSounds.shoot);

					break;
				}
			}
		}
		craft++;
	}

	static float rockSpawnTime = 0;
	rockSpawnTime -= time;

	if (rockSpawnTime <= 0)
	{
		rockSpawnTime += maxf (1.75f - rServerTime * 0.001f, 0.5f);

		vec2_t pos, vel;
		pos = vec2_t (rand () % (int)rScreenWidth, 0);
		vel = vec2_t (rScreenWidth / 2, rScreenHeight) - pos;
		VEC2_Normalize (vel);
		
		rSpawnRock (pos, 70 * vel, 10 + rand () % 36);
	}
}


bool rRecording = false;
bool rRecPlaying = false;
FILE *rRecFile = 0;

void rRecordStart ()
{
	if (rRecording) return;
	rRecFile = fopen ("demo.rec", "wb");
	if (rRecFile)
	{
		rRecording = true;
		return;
	}

	rRecording = false;
}

void rRecordStop ()
{
	if (!rRecording) return;

	if (rRecFile)
		fclose (rRecFile);
	rRecFile = 0;
	rRecording = false;
}

void rRecPlayStart ()
{
	if (rRecPlaying) return;
	if (rRecording) return;
	rInitGame ();
	rRecFile = fopen ("demo.rec", "rb");
	if (rRecFile)
	{
		rRecPlaying = true;
		return;
	}

	rRecPlaying = false;
}

void rRecPlayStop ()
{
	if (!rRecPlaying) return;
	if (rRecFile)
		fclose (rRecFile);
	rRecFile = 0;
	rRecPlaying = false;
}


void rRecordFrame (rActions_t *acts)
{
	if (!rRecording) return;
	if (!rRecFile) return;

	if (feof (rRecFile))
	{
		rRecordStop ();
		return;
	}

	fwrite (&rServerTime, sizeof (int), 1, rRecFile);
	fwrite (acts, sizeof (rActions_t), 1,  rRecFile);
}

void rPlayFrame (rActions_t *acts)
{
	if (!rRecPlaying) return;
	if (!rRecFile) return;

	if (feof (rRecFile))
	{
		rRecPlayStop ();
		return;
	}

	fread (&rServerTime, sizeof (int), 1, rRecFile);
	fread (acts, sizeof (rActions_t), 1,  rRecFile);
}


void rRecordFrameSnapshot ()
{
	int		i;

	if (!rRecording) return;
	if (!rRecFile) return;

	fwrite (&rServerTime, sizeof (int), 1, rRecFile);

	for (i = 0; i < R_MAX_CRAFTS; i++)
		if (crafts[i].alive)
		{
			fwrite (&i, sizeof (int), 1, rRecFile);
			fwrite (&crafts[i], sizeof (rCraft_t), 1, rRecFile);
		}

	// separator
	i = -1;	fwrite (&i, sizeof (int), 1, rRecFile);

	for (i = 0; i < R_MAX_PROJS; i++)
		if (projs[i].alive)
		{
			fwrite (&i, sizeof (int), 1, rRecFile);
			fwrite (&projs[i], sizeof (rProj_t), 1, rRecFile);
		}

	// separator
	i = -1;	fwrite (&i, sizeof (int), 1, rRecFile);

	// not very smart but let's record particles too :)
	for (i = 0; i < R_MAX_PARTS; i++)
		if (parts[i].alive)
		{
			fwrite (&i, sizeof (int), 1, rRecFile);
			fwrite (&parts[i], sizeof (rPart_t), 1, rRecFile);
		}

	// separator
	i = -1;	fwrite (&i, sizeof (int), 1, rRecFile);

	for (i = 0; i < R_MAX_ROCKS; i++)
		if (rocks[i].alive)
		{
			fwrite (&i, sizeof (int), 1, rRecFile);
			fwrite (&rocks[i], sizeof (rRock_t), 1, rRecFile);
		}

	// separator
	i = -1;	fwrite (&i, sizeof (int), 1, rRecFile);
}


void rPlayFrameSnapshot ()
{
	int		i;

	if (!rRecPlaying) return;
	if (!rRecFile) return;

	rInitGame ();

	if (feof (rRecFile))
	{
		rRecPlayStop ();
		return;
	}

	fread (&rServerTime, sizeof (int), 1, rRecFile);

	if (feof (rRecFile))
	{
		rRecPlayStop ();
		return;
	}

	while (1)
	{
		fread (&i, sizeof (int), 1, rRecFile);
		if (i == -1) break;
		fread (&crafts[i], sizeof (rCraft_t), 1, rRecFile);
	}

	while (1)
	{
		fread (&i, sizeof (int), 1, rRecFile);
		if (i == -1) break;
		fread (&projs[i], sizeof (rProj_t), 1, rRecFile);
	}

	while (1)
	{
		fread (&i, sizeof (int), 1, rRecFile);
		if (i == -1) break;
		fread (&parts[i], sizeof (rPart_t), 1, rRecFile);
	}

	while (1)
	{
		fread (&i, sizeof (int), 1, rRecFile);
		if (i == -1) break;
		fread (&rocks[i], sizeof (rRock_t), 1, rRecFile);
	}
}



void		rFrame (float time)
{
	rTimeKey += time;

	// get mouse static position (the cursor position)
	rAimPos.x = cl->INP_GetAnalog (DIKA_MOUSEXSTAT);
	rAimPos.y = cl->INP_GetAnalog (DIKA_MOUSEYSTAT);


	rActions_t acts = {0};

	rCraft_t *craft = &crafts[0];

	acts.angleTo = VEC2_Angle (rAimPos - craft->origin);
	if (cl->INP_KeyDown (DIKE_MOUSE1))		acts.fire = true;
	if (cl->INP_KeyDown (DIKE_MOUSE2))		acts.thrust = true;

	if (!rRecPlaying)
		craft->angle = VEC2_Angle (rAimPos - craft->origin);

	float logicFPS = 1.0f / 30;

	if (*cl->CORE_GetVar ("roks_noDev")->floatValue != 1)
	{
		if (cl->INP_KeyDown (DIK_SPACE))
			rInitGame ();

		if (cl->INP_KeyDown (DIK_1))	logicFPS = 1.0f / 5;
		if (cl->INP_KeyDown (DIK_2))	logicFPS = 1.0f / 15;
		if (cl->INP_KeyDown (DIK_3))	logicFPS = 1.0f / 30;
		if (cl->INP_KeyDown (DIK_4))	logicFPS = 1.0f / 60;

		if (cl->INP_KeyDown (DIK_1))	craft->weaponLevel = 1;
		if (cl->INP_KeyDown (DIK_2))	craft->weaponLevel = 2;
		if (cl->INP_KeyDown (DIK_3))	craft->weaponLevel = 3;
		if (cl->INP_KeyDown (DIK_4))	craft->weaponLevel = 4;
	}

	if (rScore > 500 && craft->weaponLevel == 1)
	{
		cl->SFX_PlaySample (rSounds.bonus);
		craft->weaponLevel++;
	}
	if (rScore > 4000 && craft->weaponLevel == 2)
	{
		cl->SFX_PlaySample (rSounds.bonus);
		craft->weaponLevel++;
	}

	static bool canStartGame = false;	// hack :I

	if (!cl->INP_KeyDown (DIKE_MOUSE1))
		canStartGame = true;

	if (craft->livesLeft <= 0 && rPhase == R_PHASE_GAME)
	{
		rPhase = R_PHASE_MENU;
		canStartGame = false;
	}

	switch (rPhase)
	{
		case R_PHASE_MENU:
		{
			if (cl->INP_KeyDown (DIKE_MOUSE1) && canStartGame)
			{
				rInitGame ();
				crafts[0].livesLeft = 4;
				rRespawnCraft (&crafts[0], 0.5f * vec2_t (rScreenWidth, rScreenHeight));

				rPhase = R_PHASE_GAME;
			}

			break;
		}

		case R_PHASE_GAME:
		{
			if (!clMod->parms.onPause)
			{
				rServerTime += time;
				acts.time = rServerTime;

				if (craft->spawnCount > 0)
				{
					craft->alive = false;
					craft->spawnCount -= time;
					if (craft->spawnCount < 0)
						craft->alive = true;
				}

				if (*cl->CORE_GetVar ("roks_demoRecDisabled")->floatValue != 1)
				{
					if (cl->INP_KeyDown (DIK_R) && !rRecording)
					{
						rRecordStart ();
						rRecordFrameSnapshot ();		// write initial conditions
					}
					if (cl->INP_KeyDown (DIK_P) && !rRecPlaying)
					{
						rRecPlayStart ();
						rPlayFrameSnapshot ();			// read initial conditions
					}

					if (cl->INP_KeyDown (DIK_S))
					{
						if (rRecording)		rRecordStop ();
						if (rRecPlaying)	rRecPlayStop ();
					}
				}

				static float gameLogicUpdate = 0;
				gameLogicUpdate += time;
				while (gameLogicUpdate > logicFPS)		// update game logic at a constant rate
				{
					gameLogicUpdate -= logicFPS;

					if (rRecPlaying)
						rPlayFrame (&acts);

					rGameLogicFrame (logicFPS, &acts);

					if (rRecording)
						rRecordFrame (&acts);
				}
			}

			break;
		}

	}

	rDrawFrame ();	

	cl->GFX_SetColor (1,1,1,0.5f);
	if (*cl->CORE_GetVar ("roks_noInfo")->floatValue != 1)
	{
		cl->GFX_Printf (10,50, "angle: %.2f vel %.2f wepLevel %d", crafts[0].angle, VEC2_Length (crafts[0].vel), crafts[0].weaponLevel);
		cl->GFX_Printf (10,70, "frame %.2f, logicFPS %.2f", rServerTime, 1/logicFPS);
	}

}
