/*
===========================================================================

Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").

Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/
#pragma hdrstop
#include "precompiled.h"

#include "DeviceContext.h"
#include "Window.h"
#include "UserInterfaceLocal.h"
#include "GameSSDWindow.h"


#define Z_NEAR 100.0f
#define Z_FAR  4000.0f
#define ENTITY_START_DIST 3000

#define V_WIDTH 640.0f
#define V_HEIGHT 480.0f

/*
*****************************************************************************
* SSDCrossHair
****************************************************************************
*/

#define CROSSHAIR_STANDARD_MATERIAL "game/SSD/crosshair_standard"
#define CROSSHAIR_SUPER_MATERIAL "game/SSD/crosshair_super"

SSDCrossHair::SSDCrossHair()
{
}

SSDCrossHair::~SSDCrossHair()
{
}

void SSDCrossHair::WriteToSaveGame( idFile* savefile )
{

	savefile->Write( &currentCrosshair, sizeof( currentCrosshair ) );
	savefile->Write( &crosshairWidth, sizeof( crosshairWidth ) );
	savefile->Write( &crosshairHeight, sizeof( crosshairHeight ) );
	
}

void SSDCrossHair::ReadFromSaveGame( idFile* savefile )
{

	InitCrosshairs();
	
	savefile->Read( &currentCrosshair, sizeof( currentCrosshair ) );
	savefile->Read( &crosshairWidth, sizeof( crosshairWidth ) );
	savefile->Read( &crosshairHeight, sizeof( crosshairHeight ) );
	
}

void SSDCrossHair::InitCrosshairs()
{

	crosshairMaterial[CROSSHAIR_STANDARD] = declManager->FindMaterial( CROSSHAIR_STANDARD_MATERIAL );
	crosshairMaterial[CROSSHAIR_SUPER] = declManager->FindMaterial( CROSSHAIR_SUPER_MATERIAL );
	
	crosshairWidth = 64;
	crosshairHeight = 64;
	
	currentCrosshair = CROSSHAIR_STANDARD;
	
}

void SSDCrossHair::Draw( const idVec2& cursor )
{

	float x, y;
	x = cursor.x - ( crosshairWidth / 2 );
	y = cursor.y - ( crosshairHeight / 2 );
	dc->DrawMaterial( x, y, crosshairWidth, crosshairHeight, crosshairMaterial[currentCrosshair], colorWhite, 1.0f, 1.0f );
	
}

/*
*****************************************************************************
* SSDEntity
****************************************************************************
*/

SSDEntity::SSDEntity()
{
	EntityInit();
}

SSDEntity::~SSDEntity()
{
}

void SSDEntity::WriteToSaveGame( idFile* savefile )
{

	savefile->Write( &type, sizeof( type ) );
	game->WriteSaveGameString( materialName, savefile );
	savefile->Write( &position, sizeof( position ) );
	savefile->Write( &size, sizeof( size ) );
	savefile->Write( &radius, sizeof( radius ) );
	savefile->Write( &hitRadius, sizeof( hitRadius ) );
	savefile->Write( &rotation, sizeof( rotation ) );
	
	savefile->Write( &matColor, sizeof( matColor ) );
	
	game->WriteSaveGameString( text, savefile );
	savefile->Write( &textScale, sizeof( textScale ) );
	savefile->Write( &foreColor, sizeof( foreColor ) );
	
	savefile->Write( &currentTime, sizeof( currentTime ) );
	savefile->Write( &lastUpdate, sizeof( lastUpdate ) );
	savefile->Write( &elapsed, sizeof( elapsed ) );
	
	savefile->Write( &destroyed, sizeof( destroyed ) );
	savefile->Write( &noHit, sizeof( noHit ) );
	savefile->Write( &noPlayerDamage, sizeof( noPlayerDamage ) );
	
	savefile->Write( &inUse, sizeof( inUse ) );
	
}

void SSDEntity::ReadFromSaveGame( idFile* savefile,  idGameSSDWindow* _game )
{

	savefile->Read( &type, sizeof( type ) );
	game->ReadSaveGameString( materialName, savefile );
	SetMaterial( materialName );
	savefile->Read( &position, sizeof( position ) );
	savefile->Read( &size, sizeof( size ) );
	savefile->Read( &radius, sizeof( radius ) );
	savefile->Read( &hitRadius, sizeof( hitRadius ) );
	savefile->Read( &rotation, sizeof( rotation ) );
	
	savefile->Read( &matColor, sizeof( matColor ) );
	
	game->ReadSaveGameString( text, savefile );
	savefile->Read( &textScale, sizeof( textScale ) );
	savefile->Read( &foreColor, sizeof( foreColor ) );
	
	game = _game;
	savefile->Read( &currentTime, sizeof( currentTime ) );
	savefile->Read( &lastUpdate, sizeof( lastUpdate ) );
	savefile->Read( &elapsed, sizeof( elapsed ) );
	
	savefile->Read( &destroyed, sizeof( destroyed ) );
	savefile->Read( &noHit, sizeof( noHit ) );
	savefile->Read( &noPlayerDamage, sizeof( noPlayerDamage ) );
	
	savefile->Read( &inUse, sizeof( inUse ) );
}

void SSDEntity::EntityInit()
{

	inUse = false;
	
	
	type = SSD_ENTITY_BASE;
	
	materialName = "";
	material = NULL;
	position.Zero();
	size.Zero();
	radius = 0.0f;
	hitRadius = 0.0f;
	rotation = 0.0f;
	
	
	currentTime = 0;
	lastUpdate = 0;
	
	destroyed = false;
	noHit = false;
	noPlayerDamage = false;
	
	matColor.Set( 1, 1, 1, 1 );
	
	text = "";
	textScale = 1.0f;
	foreColor.Set( 1, 1, 1, 1 );
}

void SSDEntity::SetGame( idGameSSDWindow* _game )
{
	game = _game;
}

void SSDEntity::SetMaterial( const char* name )
{
	materialName = name;
	material = declManager->FindMaterial( name );
	material->SetSort( SS_GUI );
}

void SSDEntity::SetPosition( const idVec3& _position )
{
	position = _position;
}

void SSDEntity::SetSize( const idVec2& _size )
{
	size = _size;
}

void SSDEntity::SetRadius( float _radius, float _hitFactor )
{
	radius = _radius;
	hitRadius = _radius * _hitFactor;
}

void SSDEntity::SetRotation( float _rotation )
{
	rotation = _rotation;
}

void SSDEntity::Update()
{

	currentTime = game->ssdTime;
	
	//Is this the first update
	if( lastUpdate == 0 )
	{
		lastUpdate = currentTime;
		return;
	}
	
	elapsed = currentTime - lastUpdate;
	
	EntityUpdate();
	
	lastUpdate = currentTime;
}

bool SSDEntity::HitTest( const idVec2& pt )
{

	if( noHit )
	{
		return false;
	}
	
	idVec3 screenPos = WorldToScreen( position );
	
	
	//Scale the radius based on the distance from the player
	float scale = 1.0f - ( ( screenPos.z - Z_NEAR ) / ( Z_FAR - Z_NEAR ) );
	float scaledRad = scale * hitRadius;
	
	//So we can compare against the square of the length between two points
	float scaleRadSqr = scaledRad * scaledRad;
	
	idVec2 diff = screenPos.ToVec2() - pt;
	float dist = idMath::Fabs( diff.LengthSqr() );
	
	if( dist < scaleRadSqr )
	{
		return true;
	}
	return false;
}

void SSDEntity::Draw()
{


	idVec2 persize;
	float x, y;
	
	idBounds bounds;
	bounds[0] = idVec3( position.x - ( size.x / 2.0f ), position.y - ( size.y / 2.0f ), position.z );
	bounds[1] = idVec3( position.x + ( size.x / 2.0f ), position.y + ( size.y / 2.0f ), position.z );
	
	idBounds screenBounds = WorldToScreen( bounds );
	persize.x = idMath::Fabs( screenBounds[1].x - screenBounds[0].x );
	persize.y = idMath::Fabs( screenBounds[1].y - screenBounds[0].y );
	
	idVec3 center = screenBounds.GetCenter();
	
	x = screenBounds[0].x;
	y = screenBounds[1].y;
	dc->DrawMaterialRotated( x, y, persize.x, persize.y, material, matColor, 1.0f, 1.0f, DEG2RAD( rotation ) );
	
	if( text.Length() > 0 )
	{
		idRectangle rect( x, y, VIRTUAL_WIDTH, VIRTUAL_HEIGHT );
		dc->DrawText( text, textScale, 0, foreColor, rect, false );
	}
	
}

void SSDEntity::DestroyEntity()
{
	inUse = false;
}

idBounds SSDEntity::WorldToScreen( const idBounds worldBounds )
{

	idVec3 screenMin = WorldToScreen( worldBounds[0] );
	idVec3 screenMax = WorldToScreen( worldBounds[1] );
	
	idBounds screenBounds( screenMin, screenMax );
	return screenBounds;
}

idVec3 SSDEntity::WorldToScreen( const idVec3& worldPos )
{

	float d = 0.5f * V_WIDTH * idMath::Tan( DEG2RAD( 90.0f ) / 2.0f );
	
	//World To Camera Coordinates
	idVec3 cameraTrans( 0, 0, d );
	idVec3 cameraPos;
	cameraPos = worldPos + cameraTrans;
	
	//Camera To Screen Coordinates
	idVec3 screenPos;
	screenPos.x = d * cameraPos.x / cameraPos.z + ( 0.5f * V_WIDTH - 0.5f );
	screenPos.y = -d * cameraPos.y / cameraPos.z + ( 0.5f * V_HEIGHT - 0.5f );
	screenPos.z = cameraPos.z;
	
	return screenPos;
}

idVec3 SSDEntity::ScreenToWorld( const idVec3& screenPos )
{

	idVec3 worldPos;
	
	worldPos.x = screenPos.x - 0.5f * V_WIDTH;
	worldPos.y = -( screenPos.y  - 0.5f * V_HEIGHT );
	worldPos.z = screenPos.z;
	
	return worldPos;
}

/*
*****************************************************************************
* SSDMover
****************************************************************************
*/

SSDMover::SSDMover()
{
}

SSDMover::~SSDMover()
{
}

void SSDMover::WriteToSaveGame( idFile* savefile )
{
	SSDEntity::WriteToSaveGame( savefile );
	
	savefile->Write( &speed, sizeof( speed ) );
	savefile->Write( &rotationSpeed, sizeof( rotationSpeed ) );
}

void SSDMover::ReadFromSaveGame( idFile* savefile,  idGameSSDWindow* _game )
{
	SSDEntity::ReadFromSaveGame( savefile, _game );
	
	savefile->Read( &speed, sizeof( speed ) );
	savefile->Read( &rotationSpeed, sizeof( rotationSpeed ) );
}

void SSDMover::MoverInit( const idVec3& _speed, float _rotationSpeed )
{

	speed = _speed;
	rotationSpeed = _rotationSpeed;
}

void SSDMover::EntityUpdate()
{

	SSDEntity::EntityUpdate();
	
	//Move forward based on speed (units per second)
	idVec3 moved = ( ( float )elapsed / 1000.0f ) * speed;
	position += moved;
	
	float rotated = ( ( float )elapsed / 1000.0f ) * rotationSpeed * 360.0f;
	rotation += rotated;
	if( rotation >= 360 )
	{
		rotation -= 360.0f;
	}
	if( rotation < 0 )
	{
		rotation += 360.0f;
	}
}


/*
*****************************************************************************
* SSDAsteroid
****************************************************************************
*/

SSDAsteroid	SSDAsteroid::asteroidPool[MAX_ASTEROIDS];

#define ASTEROID_MATERIAL "game/SSD/asteroid"

SSDAsteroid::SSDAsteroid()
{
}

SSDAsteroid::~SSDAsteroid()
{
}

void SSDAsteroid::WriteToSaveGame( idFile* savefile )
{
	SSDMover::WriteToSaveGame( savefile );
	
	savefile->Write( &health, sizeof( health ) );
}

void SSDAsteroid::ReadFromSaveGame( idFile* savefile,  idGameSSDWindow* _game )
{
	SSDMover::ReadFromSaveGame( savefile, _game );
	
	savefile->Read( &health, sizeof( health ) );
}

void SSDAsteroid::Init( idGameSSDWindow* _game, const idVec3& startPosition, const idVec2& _size, float _speed, float rotate, int _health )
{

	EntityInit();
	MoverInit( idVec3( 0, 0, -_speed ), rotate );
	
	SetGame( _game );
	
	type = SSD_ENTITY_ASTEROID;
	
	SetMaterial( ASTEROID_MATERIAL );
	SetSize( _size );
	SetRadius( Max( size.x, size.y ), 0.3f );
	SetRotation( game->random.RandomInt( 360 ) );
	
	
	position = startPosition;
	
	health = _health;
}

void SSDAsteroid::EntityUpdate()
{

	SSDMover::EntityUpdate();
}

SSDAsteroid* SSDAsteroid::GetNewAsteroid( idGameSSDWindow* _game, const idVec3& startPosition, const idVec2& _size, float _speed, float rotate, int _health )
{
	for( int i = 0; i < MAX_ASTEROIDS; i++ )
	{
		if( !asteroidPool[i].inUse )
		{
			asteroidPool[i].Init( _game, startPosition, _size, _speed, rotate, _health );
			asteroidPool[i].inUse = true;
			asteroidPool[i].id = i;
			
			return &asteroidPool[i];
		}
	}
	return NULL;
}

SSDAsteroid* SSDAsteroid::GetSpecificAsteroid( int id )
{
	return &asteroidPool[id];
}

void SSDAsteroid::WriteAsteroids( idFile* savefile )
{
	int count = 0;
	for( int i = 0; i < MAX_ASTEROIDS; i++ )
	{
		if( asteroidPool[i].inUse )
		{
			count++;
		}
	}
	savefile->Write( &count, sizeof( count ) );
	for( int i = 0; i < MAX_ASTEROIDS; i++ )
	{
		if( asteroidPool[i].inUse )
		{
			savefile->Write( &( asteroidPool[i].id ), sizeof( asteroidPool[i].id ) );
			asteroidPool[i].WriteToSaveGame( savefile );
		}
	}
}

void SSDAsteroid::ReadAsteroids( idFile* savefile, idGameSSDWindow* _game )
{

	int count;
	savefile->Read( &count, sizeof( count ) );
	for( int i = 0; i < count; i++ )
	{
		int id;
		savefile->Read( &id, sizeof( id ) );
		SSDAsteroid* ent = GetSpecificAsteroid( id );
		ent->ReadFromSaveGame( savefile, _game );
	}
}

/*
*****************************************************************************
* SSDAstronaut
****************************************************************************
*/

SSDAstronaut	SSDAstronaut::astronautPool[MAX_ASTRONAUT];

#define ASTRONAUT_MATERIAL "game/SSD/astronaut"

SSDAstronaut::SSDAstronaut()
{
}

SSDAstronaut::~SSDAstronaut()
{
}

void SSDAstronaut::WriteToSaveGame( idFile* savefile )
{
	SSDMover::WriteToSaveGame( savefile );
	
	savefile->Write( &health, sizeof( health ) );
}

void SSDAstronaut::ReadFromSaveGame( idFile* savefile,  idGameSSDWindow* _game )
{
	SSDMover::ReadFromSaveGame( savefile, _game );
	
	savefile->Read( &health, sizeof( health ) );
}

void SSDAstronaut::Init( idGameSSDWindow* _game, const idVec3& startPosition, float _speed, float rotate, int _health )
{

	EntityInit();
	MoverInit( idVec3( 0, 0, -_speed ), rotate );
	
	SetGame( _game );
	
	type = SSD_ENTITY_ASTRONAUT;
	
	SetMaterial( ASTRONAUT_MATERIAL );
	SetSize( idVec2( 256, 256 ) );
	SetRadius( Max( size.x, size.y ), 0.3f );
	SetRotation( game->random.RandomInt( 360 ) );
	
	position = startPosition;
	health = _health;
}

SSDAstronaut* SSDAstronaut::GetNewAstronaut( idGameSSDWindow* _game, const idVec3& startPosition, float _speed, float rotate, int _health )
{
	for( int i = 0; i < MAX_ASTRONAUT; i++ )
	{
		if( !astronautPool[i].inUse )
		{
			astronautPool[i].Init( _game, startPosition, _speed, rotate, _health );
			astronautPool[i].inUse = true;
			astronautPool[i].id = i;
			return &astronautPool[i];
		}
	}
	return NULL;
}

SSDAstronaut* SSDAstronaut::GetSpecificAstronaut( int id )
{
	return &astronautPool[id];
	
}

void SSDAstronaut::WriteAstronauts( idFile* savefile )
{
	int count = 0;
	for( int i = 0; i < MAX_ASTRONAUT; i++ )
	{
		if( astronautPool[i].inUse )
		{
			count++;
		}
	}
	savefile->Write( &count, sizeof( count ) );
	for( int i = 0; i < MAX_ASTRONAUT; i++ )
	{
		if( astronautPool[i].inUse )
		{
			savefile->Write( &( astronautPool[i].id ), sizeof( astronautPool[i].id ) );
			astronautPool[i].WriteToSaveGame( savefile );
		}
	}
}

void SSDAstronaut::ReadAstronauts( idFile* savefile, idGameSSDWindow* _game )
{

	int count;
	savefile->Read( &count, sizeof( count ) );
	for( int i = 0; i < count; i++ )
	{
		int id;
		savefile->Read( &id, sizeof( id ) );
		SSDAstronaut* ent = GetSpecificAstronaut( id );
		ent->ReadFromSaveGame( savefile, _game );
	}
}

/*
*****************************************************************************
* SSDExplosion
****************************************************************************
*/

SSDExplosion SSDExplosion::explosionPool[MAX_EXPLOSIONS];


//#define EXPLOSION_MATERIAL "game/SSD/fball"
//#define EXPLOSION_TELEPORT "game/SSD/teleport"

const char* explosionMaterials[] =
{
	"game/SSD/fball",
	"game/SSD/teleport"
};

#define EXPLOSION_MATERIAL_COUNT 2

SSDExplosion::SSDExplosion()
{
	type = SSD_ENTITY_EXPLOSION;
}

SSDExplosion::~SSDExplosion()
{
}

void SSDExplosion::WriteToSaveGame( idFile* savefile )
{
	SSDEntity::WriteToSaveGame( savefile );
	
	savefile->Write( &finalSize, sizeof( finalSize ) );
	savefile->Write( &length, sizeof( length ) );
	savefile->Write( &beginTime, sizeof( beginTime ) );
	savefile->Write( &endTime, sizeof( endTime ) );
	savefile->Write( &explosionType, sizeof( explosionType ) );
	
	
	savefile->Write( &( buddy->type ), sizeof( buddy->type ) );
	savefile->Write( &( buddy->id ), sizeof( buddy->id ) );
	
	savefile->Write( &killBuddy, sizeof( killBuddy ) );
	savefile->Write( &followBuddy, sizeof( followBuddy ) );
}

void SSDExplosion::ReadFromSaveGame( idFile* savefile,  idGameSSDWindow* _game )
{
	SSDEntity::ReadFromSaveGame( savefile, _game );
	
	savefile->Read( &finalSize, sizeof( finalSize ) );
	savefile->Read( &length, sizeof( length ) );
	savefile->Read( &beginTime, sizeof( beginTime ) );
	savefile->Read( &endTime, sizeof( endTime ) );
	savefile->Read( &explosionType, sizeof( explosionType ) );
	
	int type, id;
	savefile->Read( &type, sizeof( type ) );
	savefile->Read( &id, sizeof( id ) );
	
	//Get a pointer to my buddy
	buddy = _game->GetSpecificEntity( type, id );
	
	savefile->Read( &killBuddy, sizeof( killBuddy ) );
	savefile->Read( &followBuddy, sizeof( followBuddy ) );
}

void SSDExplosion::Init( idGameSSDWindow* _game, const idVec3& _position, const idVec2& _size, int _length, int _type, SSDEntity* _buddy, bool _killBuddy, bool _followBuddy )
{

	EntityInit();
	
	SetGame( _game );
	
	type = SSD_ENTITY_EXPLOSION;
	explosionType = _type;
	
	SetMaterial( explosionMaterials[explosionType] );
	SetPosition( _position );
	position.z -= 50;
	
	finalSize = _size;
	length = _length;
	beginTime = game->ssdTime;
	endTime = beginTime + length;
	
	buddy = _buddy;
	killBuddy = _killBuddy;
	followBuddy = _followBuddy;
	
	//Explosion Starts from nothing and will increase in size until it gets to final size
	size.Zero();
	
	noPlayerDamage = true;
	noHit = true;
}

void SSDExplosion::EntityUpdate()
{

	SSDEntity::EntityUpdate();
	
	//Always set my position to my buddies position except change z to be on top
	if( followBuddy )
	{
		position = buddy->position;
		position.z -= 50;
	}
	else
	{
		//Only mess with the z if we are not following
		position.z = buddy->position.z - 50;
	}
	
	//Scale the image based on the time
	size = finalSize * ( ( float )( currentTime - beginTime ) / ( float )length );
	
	//Destroy myself after the explosion is done
	if( currentTime > endTime )
	{
		destroyed = true;
		
		if( killBuddy )
		{
			//Destroy the exploding object
			buddy->destroyed = true;
		}
	}
}

SSDExplosion* SSDExplosion::GetNewExplosion( idGameSSDWindow* _game, const idVec3& _position, const idVec2& _size, int _length, int _type, SSDEntity* _buddy, bool _killBuddy, bool _followBuddy )
{
	for( int i = 0; i < MAX_EXPLOSIONS; i++ )
	{
		if( !explosionPool[i].inUse )
		{
			explosionPool[i].Init( _game, _position, _size, _length, _type, _buddy, _killBuddy, _followBuddy );
			explosionPool[i].inUse = true;
			return &explosionPool[i];
		}
	}
	return NULL;
}

SSDExplosion* SSDExplosion::GetSpecificExplosion( int id )
{
	return &explosionPool[id];
}

void SSDExplosion::WriteExplosions( idFile* savefile )
{
	int count = 0;
	for( int i = 0; i < MAX_EXPLOSIONS; i++ )
	{
		if( explosionPool[i].inUse )
		{
			count++;
		}
	}
	savefile->Write( &count, sizeof( count ) );
	for( int i = 0; i < MAX_EXPLOSIONS; i++ )
	{
		if( explosionPool[i].inUse )
		{
			savefile->Write( &( explosionPool[i].id ), sizeof( explosionPool[i].id ) );
			explosionPool[i].WriteToSaveGame( savefile );
		}
	}
}

void SSDExplosion::ReadExplosions( idFile* savefile, idGameSSDWindow* _game )
{

	int count;
	savefile->Read( &count, sizeof( count ) );
	for( int i = 0; i < count; i++ )
	{
		int id;
		savefile->Read( &id, sizeof( id ) );
		SSDExplosion* ent = GetSpecificExplosion( id );
		ent->ReadFromSaveGame( savefile, _game );
	}
}

/*
*****************************************************************************
* SSDPoints
****************************************************************************
*/

SSDPoints	SSDPoints::pointsPool[MAX_POINTS];

SSDPoints::SSDPoints()
{
	type = SSD_ENTITY_POINTS;
}

SSDPoints::~SSDPoints()
{
}

void SSDPoints::WriteToSaveGame( idFile* savefile )
{
	SSDEntity::WriteToSaveGame( savefile );
	
	savefile->Write( &length, sizeof( length ) );
	savefile->Write( &distance, sizeof( distance ) );
	savefile->Write( &beginTime, sizeof( beginTime ) );
	savefile->Write( &endTime, sizeof( endTime ) );
	
	savefile->Write( &beginPosition, sizeof( beginPosition ) );
	savefile->Write( &endPosition, sizeof( endPosition ) );
	
	savefile->Write( &beginColor, sizeof( beginColor ) );
	savefile->Write( &endColor, sizeof( endColor ) );
	
}

void SSDPoints::ReadFromSaveGame( idFile* savefile,  idGameSSDWindow* _game )
{
	SSDEntity::ReadFromSaveGame( savefile, _game );
	
	savefile->Read( &length, sizeof( length ) );
	savefile->Read( &distance, sizeof( distance ) );
	savefile->Read( &beginTime, sizeof( beginTime ) );
	savefile->Read( &endTime, sizeof( endTime ) );
	
	savefile->Read( &beginPosition, sizeof( beginPosition ) );
	savefile->Read( &endPosition, sizeof( endPosition ) );
	
	savefile->Read( &beginColor, sizeof( beginColor ) );
	savefile->Read( &endColor, sizeof( endColor ) );
}

void SSDPoints::Init( idGameSSDWindow* _game, SSDEntity* _ent, int _points, int _length, int _distance, const idVec4& color )
{

	EntityInit();
	
	SetGame( _game );
	
	length = _length;
	distance = _distance;
	beginTime = game->ssdTime;
	endTime = beginTime + length;
	
	textScale = 0.4f;
	text = va( "%d", _points );
	
	float width = 0;
	for( int i = 0; i < text.Length(); i++ )
	{
		width += dc->CharWidth( text[i], textScale );
	}
	
	size.Set( 0, 0 );
	
	//Set the start position at the top of the passed in entity
	position = WorldToScreen( _ent->position );
	position = ScreenToWorld( position );
	
	position.z = 0;
	position.x -= ( width / 2.0f );
	
	beginPosition = position;
	
	endPosition = beginPosition;
	endPosition.y += _distance;
	
	//beginColor.Set(0,1,0,1);
	endColor.Set( 1, 1, 1, 0 );
	
	beginColor = color;
	beginColor.w = 1;
	
	noPlayerDamage = true;
	noHit = true;
}

void SSDPoints::EntityUpdate()
{

	float t = ( float )( currentTime - beginTime ) / ( float )length;
	
	//Move up from the start position
	position.Lerp( beginPosition, endPosition, t );
	
	//Interpolate the color
	foreColor.Lerp( beginColor, endColor, t );
	
	if( currentTime > endTime )
	{
		destroyed = true;
	}
}

SSDPoints* SSDPoints::GetNewPoints( idGameSSDWindow* _game, SSDEntity* _ent, int _points, int _length, int _distance, const idVec4& color )
{
	for( int i = 0; i < MAX_POINTS; i++ )
	{
		if( !pointsPool[i].inUse )
		{
			pointsPool[i].Init( _game, _ent, _points, _length, _distance, color );
			pointsPool[i].inUse = true;
			return &pointsPool[i];
		}
	}
	return NULL;
}

SSDPoints* SSDPoints::GetSpecificPoints( int id )
{
	return &pointsPool[id];
}

void SSDPoints::WritePoints( idFile* savefile )
{
	int count = 0;
	for( int i = 0; i < MAX_POINTS; i++ )
	{
		if( pointsPool[i].inUse )
		{
			count++;
		}
	}
	savefile->Write( &count, sizeof( count ) );
	for( int i = 0; i < MAX_POINTS; i++ )
	{
		if( pointsPool[i].inUse )
		{
			savefile->Write( &( pointsPool[i].id ), sizeof( pointsPool[i].id ) );
			pointsPool[i].WriteToSaveGame( savefile );
		}
	}
}

void SSDPoints::ReadPoints( idFile* savefile, idGameSSDWindow* _game )
{

	int count;
	savefile->Read( &count, sizeof( count ) );
	for( int i = 0; i < count; i++ )
	{
		int id;
		savefile->Read( &id, sizeof( id ) );
		SSDPoints* ent = GetSpecificPoints( id );
		ent->ReadFromSaveGame( savefile, _game );
	}
}

/*
*****************************************************************************
* SSDProjectile
****************************************************************************
*/

SSDProjectile SSDProjectile::projectilePool[MAX_PROJECTILES];

#define PROJECTILE_MATERIAL "game/SSD/fball"

SSDProjectile::SSDProjectile()
{
	type = SSD_ENTITY_PROJECTILE;
}

SSDProjectile::~SSDProjectile()
{
}

void SSDProjectile::WriteToSaveGame( idFile* savefile )
{
	SSDEntity::WriteToSaveGame( savefile );
	
	savefile->Write( &dir, sizeof( dir ) );
	savefile->Write( &speed, sizeof( speed ) );
	savefile->Write( &beginTime, sizeof( beginTime ) );
	savefile->Write( &endTime, sizeof( endTime ) );
	
	savefile->Write( &endPosition, sizeof( endPosition ) );
}

void SSDProjectile::ReadFromSaveGame( idFile* savefile,  idGameSSDWindow* _game )
{
	SSDEntity::ReadFromSaveGame( savefile, _game );
	
	savefile->Read( &dir, sizeof( dir ) );
	savefile->Read( &speed, sizeof( speed ) );
	savefile->Read( &beginTime, sizeof( beginTime ) );
	savefile->Read( &endTime, sizeof( endTime ) );
	
	savefile->Read( &endPosition, sizeof( endPosition ) );
}

void SSDProjectile::Init( idGameSSDWindow* _game, const idVec3& _beginPosition, const idVec3& _endPosition, float _speed, float _size )
{

	EntityInit();
	
	SetGame( _game );
	
	SetMaterial( PROJECTILE_MATERIAL );
	size.Set( _size, _size );
	
	position = _beginPosition;
	endPosition = _endPosition;
	
	dir = _endPosition - position;
	dir.Normalize();
	
	//speed.Zero();
	speed.x = speed.y = speed.z = _speed;
	
	noHit = true;
}

void SSDProjectile::EntityUpdate()
{

	SSDEntity::EntityUpdate();
	
	//Move forward based on speed (units per second)
	idVec3 moved = dir * ( ( float )elapsed / 1000.0f ) * speed.z;
	position += moved;
	
	if( position.z > endPosition.z )
	{
		//We have reached our position
		destroyed = true;
	}
}

SSDProjectile* SSDProjectile::GetNewProjectile( idGameSSDWindow* _game, const idVec3& _beginPosition, const idVec3& _endPosition, float _speed, float _size )
{
	for( int i = 0; i < MAX_PROJECTILES; i++ )
	{
		if( !projectilePool[i].inUse )
		{
			projectilePool[i].Init( _game, _beginPosition, _endPosition, _speed, _size );
			projectilePool[i].inUse = true;
			return &projectilePool[i];
		}
	}
	return NULL;
}

SSDProjectile* SSDProjectile::GetSpecificProjectile( int id )
{
	return &projectilePool[id];
}

void SSDProjectile::WriteProjectiles( idFile* savefile )
{
	int count = 0;
	for( int i = 0; i < MAX_PROJECTILES; i++ )
	{
		if( projectilePool[i].inUse )
		{
			count++;
		}
	}
	savefile->Write( &count, sizeof( count ) );
	for( int i = 0; i < MAX_PROJECTILES; i++ )
	{
		if( projectilePool[i].inUse )
		{
			savefile->Write( &( projectilePool[i].id ), sizeof( projectilePool[i].id ) );
			projectilePool[i].WriteToSaveGame( savefile );
		}
	}
}

void SSDProjectile::ReadProjectiles( idFile* savefile, idGameSSDWindow* _game )
{

	int count;
	savefile->Read( &count, sizeof( count ) );
	for( int i = 0; i < count; i++ )
	{
		int id;
		savefile->Read( &id, sizeof( id ) );
		SSDProjectile* ent = GetSpecificProjectile( id );
		ent->ReadFromSaveGame( savefile, _game );
	}
}

/*
*****************************************************************************
* SSDPowerup
****************************************************************************
*/

const char* powerupMaterials[][2] =
{
	"game/SSD/powerupHealthClosed",			"game/SSD/powerupHealthOpen",
	"game/SSD/powerupSuperBlasterClosed",	"game/SSD/powerupSuperBlasterOpen",
	"game/SSD/powerupNukeClosed",			"game/SSD/powerupNukeOpen",
	"game/SSD/powerupRescueClosed",			"game/SSD/powerupRescueOpen",
	"game/SSD/powerupBonusPointsClosed",	"game/SSD/powerupBonusPointsOpen",
	"game/SSD/powerupDamageClosed",			"game/SSD/powerupDamageOpen",
};

#define POWERUP_MATERIAL_COUNT 6

SSDPowerup	SSDPowerup::powerupPool[MAX_POWERUPS];

SSDPowerup::SSDPowerup()
{

}

SSDPowerup::~SSDPowerup()
{
}

void SSDPowerup::WriteToSaveGame( idFile* savefile )
{
	SSDMover::WriteToSaveGame( savefile );
	
	savefile->Write( &powerupState, sizeof( powerupState ) );
	savefile->Write( &powerupType, sizeof( powerupType ) );
}

void SSDPowerup::ReadFromSaveGame( idFile* savefile,  idGameSSDWindow* _game )
{
	SSDMover::ReadFromSaveGame( savefile, _game );
	
	savefile->Read( &powerupState, sizeof( powerupState ) );
	savefile->Read( &powerupType, sizeof( powerupType ) );
}

void SSDPowerup::OnHit( int key )
{

	if( powerupState == POWERUP_STATE_CLOSED )
	{
	
		//Small explosion to indicate it is opened
		SSDExplosion* explosion = SSDExplosion::GetNewExplosion( game, position, size * 2.0f, 300, SSDExplosion::EXPLOSION_NORMAL, this, false, true );
		game->entities.Append( explosion );
		
		
		powerupState = POWERUP_STATE_OPEN;
		SetMaterial( powerupMaterials[powerupType][powerupState] );
	}
	else
	{
		//Destory the powerup with a big explosion
		SSDExplosion* explosion = SSDExplosion::GetNewExplosion( game, position, size * 2, 300, SSDExplosion::EXPLOSION_NORMAL, this );
		game->entities.Append( explosion );
		game->PlaySound( "arcade_explode" );
		
		noHit = true;
		noPlayerDamage = true;
	}
}

void SSDPowerup::OnStrikePlayer()
{

	if( powerupState == POWERUP_STATE_OPEN )
	{
		//The powerup was open so activate it
		OnActivatePowerup();
	}
	
	//Just destroy the powerup
	destroyed = true;
}

void SSDPowerup::OnOpenPowerup()
{
}

void SSDPowerup::OnActivatePowerup()
{
	switch( powerupType )
	{
		case POWERUP_TYPE_HEALTH:
		{
			game->AddHealth( 10 );
			break;
		}
		case POWERUP_TYPE_SUPER_BLASTER:
		{
			game->OnSuperBlaster();
			break;
		}
		case POWERUP_TYPE_ASTEROID_NUKE:
		{
			game->OnNuke();
			break;
		}
		case POWERUP_TYPE_RESCUE_ALL:
		{
			game->OnRescueAll();
			break;
		}
		case POWERUP_TYPE_BONUS_POINTS:
		{
			int points = ( game->random.RandomInt( 5 ) + 1 ) * 100;
			game->AddScore( this, points );
			break;
		}
		case POWERUP_TYPE_DAMAGE:
		{
			game->AddDamage( 10 );
			game->PlaySound( "arcade_explode" );
			break;
		}
		
	}
}


void SSDPowerup::Init( idGameSSDWindow* _game, float _speed, float _rotation )
{

	EntityInit();
	MoverInit( idVec3( 0, 0, -_speed ), _rotation );
	
	SetGame( _game );
	SetSize( idVec2( 200, 200 ) );
	SetRadius( Max( size.x, size.y ), 0.3f );
	
	type = SSD_ENTITY_POWERUP;
	
	idVec3 startPosition;
	startPosition.x = game->random.RandomInt( V_WIDTH ) - ( V_WIDTH / 2.0f );
	startPosition.y = game->random.RandomInt( V_HEIGHT ) - ( V_HEIGHT / 2.0f );
	startPosition.z = ENTITY_START_DIST;
	
	position = startPosition;
	//SetPosition(startPosition);
	
	powerupState = POWERUP_STATE_CLOSED;
	powerupType = game->random.RandomInt( POWERUP_TYPE_MAX + 1 );
	if( powerupType >= POWERUP_TYPE_MAX )
	{
		powerupType = 0;
	}
	
	/*OutputDebugString(va("Powerup: %d\n", powerupType));
	if(powerupType == 0) {
		int x = 0;
	}*/
	
	SetMaterial( powerupMaterials[powerupType][powerupState] );
}

SSDPowerup* SSDPowerup::GetNewPowerup( idGameSSDWindow* _game, float _speed, float _rotation )
{

	for( int i = 0; i < MAX_POWERUPS; i++ )
	{
		if( !powerupPool[i].inUse )
		{
			powerupPool[i].Init( _game, _speed, _rotation );
			powerupPool[i].inUse = true;
			return &powerupPool[i];
		}
	}
	return NULL;
}

SSDPowerup* SSDPowerup::GetSpecificPowerup( int id )
{
	return &powerupPool[id];
}

void SSDPowerup::WritePowerups( idFile* savefile )
{
	int count = 0;
	for( int i = 0; i < MAX_POWERUPS; i++ )
	{
		if( powerupPool[i].inUse )
		{
			count++;
		}
	}
	savefile->Write( &count, sizeof( count ) );
	for( int i = 0; i < MAX_POWERUPS; i++ )
	{
		if( powerupPool[i].inUse )
		{
			savefile->Write( &( powerupPool[i].id ), sizeof( powerupPool[i].id ) );
			powerupPool[i].WriteToSaveGame( savefile );
		}
	}
}

void SSDPowerup::ReadPowerups( idFile* savefile, idGameSSDWindow* _game )
{

	int count;
	savefile->Read( &count, sizeof( count ) );
	for( int i = 0; i < count; i++ )
	{
		int id;
		savefile->Read( &id, sizeof( id ) );
		SSDPowerup* ent = GetSpecificPowerup( id );
		ent->ReadFromSaveGame( savefile, _game );
	}
}

/*
*****************************************************************************
* idGameSSDWindow
****************************************************************************
*/

idRandom idGameSSDWindow::random;

idGameSSDWindow::idGameSSDWindow( idUserInterfaceLocal* g ) : idWindow( g )
{
	gui = g;
	CommonInit();
}

idGameSSDWindow::~idGameSSDWindow()
{
	ResetGameStats();
}

void idGameSSDWindow::WriteToSaveGame( idFile* savefile )
{
	idWindow::WriteToSaveGame( savefile );
	
	savefile->Write( &ssdTime, sizeof( ssdTime ) );
	
	beginLevel.WriteToSaveGame( savefile );
	resetGame.WriteToSaveGame( savefile );
	continueGame.WriteToSaveGame( savefile );
	refreshGuiData.WriteToSaveGame( savefile );
	
	crosshair.WriteToSaveGame( savefile );
	savefile->Write( &screenBounds, sizeof( screenBounds ) );
	
	savefile->Write( &levelCount, sizeof( levelCount ) );
	for( int i = 0; i < levelCount; i++ )
	{
		savefile->Write( &( levelData[i] ), sizeof( SSDLevelData_t ) );
		savefile->Write( &( asteroidData[i] ), sizeof( SSDAsteroidData_t ) );
		savefile->Write( &( astronautData[i] ), sizeof( SSDAstronautData_t ) );
		savefile->Write( &( powerupData[i] ), sizeof( SSDPowerupData_t ) );
	}
	
	savefile->Write( &weaponCount, sizeof( weaponCount ) );
	for( int i = 0; i < weaponCount; i++ )
	{
		savefile->Write( &( weaponData[i] ), sizeof( SSDWeaponData_t ) );
	}
	
	savefile->Write( &superBlasterTimeout, sizeof( superBlasterTimeout ) );
	savefile->Write( &gameStats, sizeof( SSDGameStats_t ) );
	
	//Write All Static Entities
	SSDAsteroid::WriteAsteroids( savefile );
	SSDAstronaut::WriteAstronauts( savefile );
	SSDExplosion::WriteExplosions( savefile );
	SSDPoints::WritePoints( savefile );
	SSDProjectile::WriteProjectiles( savefile );
	SSDPowerup::WritePowerups( savefile );
	
	int entCount = entities.Num();
	savefile->Write( &entCount, sizeof( entCount ) );
	for( int i = 0; i < entCount; i++ )
	{
		savefile->Write( &( entities[i]->type ), sizeof( entities[i]->type ) );
		savefile->Write( &( entities[i]->id ), sizeof( entities[i]->id ) );
	}
}

void idGameSSDWindow::ReadFromSaveGame( idFile* savefile )
{
	idWindow::ReadFromSaveGame( savefile );
	
	
	savefile->Read( &ssdTime, sizeof( ssdTime ) );
	
	beginLevel.ReadFromSaveGame( savefile );
	resetGame.ReadFromSaveGame( savefile );
	continueGame.ReadFromSaveGame( savefile );
	refreshGuiData.ReadFromSaveGame( savefile );
	
	crosshair.ReadFromSaveGame( savefile );
	savefile->Read( &screenBounds, sizeof( screenBounds ) );
	
	savefile->Read( &levelCount, sizeof( levelCount ) );
	for( int i = 0; i < levelCount; i++ )
	{
		SSDLevelData_t newLevel;
		savefile->Read( &newLevel, sizeof( SSDLevelData_t ) );
		levelData.Append( newLevel );
		
		SSDAsteroidData_t newAsteroid;
		savefile->Read( &newAsteroid, sizeof( SSDAsteroidData_t ) );
		asteroidData.Append( newAsteroid );
		
		SSDAstronautData_t newAstronaut;
		savefile->Read( &newAstronaut, sizeof( SSDAstronautData_t ) );
		astronautData.Append( newAstronaut );
		
		SSDPowerupData_t newPowerup;
		savefile->Read( &newPowerup, sizeof( SSDPowerupData_t ) );
		powerupData.Append( newPowerup );
	}
	
	savefile->Read( &weaponCount, sizeof( weaponCount ) );
	for( int i = 0; i < weaponCount; i++ )
	{
		SSDWeaponData_t newWeapon;
		savefile->Read( &newWeapon, sizeof( SSDWeaponData_t ) );
		weaponData.Append( newWeapon );
	}
	
	savefile->Read( &superBlasterTimeout, sizeof( superBlasterTimeout ) );
	
	savefile->Read( &gameStats, sizeof( SSDGameStats_t ) );
	//Reset this because it is no longer valid
	gameStats.levelStats.targetEnt = NULL;
	
	SSDAsteroid::ReadAsteroids( savefile, this );
	SSDAstronaut::ReadAstronauts( savefile, this );
	SSDExplosion::ReadExplosions( savefile, this );
	SSDPoints::ReadPoints( savefile, this );
	SSDProjectile::ReadProjectiles( savefile, this );
	SSDPowerup::ReadPowerups( savefile, this );
	
	int entCount;
	savefile->Read( &entCount, sizeof( entCount ) );
	
	for( int i = 0; i < entCount; i++ )
	{
		int type, id;
		savefile->Read( &type, sizeof( type ) );
		savefile->Read( &id, sizeof( id ) );
		
		SSDEntity* ent = GetSpecificEntity( type, id );
		if( ent )
		{
			entities.Append( ent );
		}
	}
}

const char* idGameSSDWindow::HandleEvent( const sysEvent_t* event, bool* updateVisuals )
{

	// need to call this to allow proper focus and capturing on embedded children
	const char* ret = idWindow::HandleEvent( event, updateVisuals );
	
	if( !gameStats.gameRunning )
	{
		return ret;
	}
	
	int key = event->evValue;
	
	if( event->evType == SE_KEY )
	{
	
		if( !event->evValue2 )
		{
			return ret;
		}
		
		if( key == K_MOUSE1 || key == K_MOUSE2 )
		{
			FireWeapon( key );
		}
		else
		{
			return ret;
		}
	}
	return ret;
}

idWinVar* idGameSSDWindow::GetWinVarByName( const char* _name, bool winLookup, drawWin_t** owner )
{

	idWinVar* retVar = NULL;
	
	if( idStr::Icmp( _name, "beginLevel" ) == 0 )
	{
		retVar = &beginLevel;
	}
	
	if( idStr::Icmp( _name, "resetGame" ) == 0 )
	{
		retVar = &resetGame;
	}
	
	if( idStr::Icmp( _name, "continueGame" ) == 0 )
	{
		retVar = &continueGame;
	}
	if( idStr::Icmp( _name, "refreshGuiData" ) == 0 )
	{
		retVar = &refreshGuiData;
	}
	
	
	if( retVar )
	{
		return retVar;
	}
	
	return idWindow::GetWinVarByName( _name, winLookup, owner );
}


void idGameSSDWindow::Draw( int time, float x, float y )
{

	//Update the game every frame before drawing
	UpdateGame();
	
	RefreshGuiData();
	
	if( gameStats.gameRunning )
	{
	
		ZOrderEntities();
		
		//Draw from back to front
		for( int i = entities.Num() - 1; i >= 0; i-- )
		{
			entities[i]->Draw();
		}
		
		//The last thing to draw is the crosshair
		idVec2 cursor;
		//GetCursor(cursor);
		cursor.x = gui->CursorX();
		cursor.y = gui->CursorY();
		
		crosshair.Draw( cursor );
	}
}


bool idGameSSDWindow::ParseInternalVar( const char* _name, idTokenParser* src )
{

	if( idStr::Icmp( _name, "beginLevel" ) == 0 )
	{
		beginLevel = src->ParseBool();
		return true;
	}
	if( idStr::Icmp( _name, "resetGame" ) == 0 )
	{
		resetGame = src->ParseBool();
		return true;
	}
	if( idStr::Icmp( _name, "continueGame" ) == 0 )
	{
		continueGame = src->ParseBool();
		return true;
	}
	if( idStr::Icmp( _name, "refreshGuiData" ) == 0 )
	{
		refreshGuiData = src->ParseBool();
		return true;
	}
	
	if( idStr::Icmp( _name, "levelcount" ) == 0 )
	{
		levelCount = src->ParseInt();
		for( int i = 0; i < levelCount; i++ )
		{
			SSDLevelData_t newLevel;
			memset( &newLevel, 0, sizeof( SSDLevelData_t ) );
			levelData.Append( newLevel );
			
			SSDAsteroidData_t newAsteroid;
			memset( &newAsteroid, 0, sizeof( SSDAsteroidData_t ) );
			asteroidData.Append( newAsteroid );
			
			SSDAstronautData_t newAstronaut;
			memset( &newAstronaut, 0, sizeof( SSDAstronautData_t ) );
			astronautData.Append( newAstronaut );
			
			SSDPowerupData_t newPowerup;
			memset( &newPowerup, 0, sizeof( SSDPowerupData_t ) );
			powerupData.Append( newPowerup );
			
			
		}
		return true;
	}
	if( idStr::Icmp( _name, "weaponCount" ) == 0 )
	{
		weaponCount = src->ParseInt();
		for( int i = 0; i < weaponCount; i++ )
		{
			SSDWeaponData_t newWeapon;
			memset( &newWeapon, 0, sizeof( SSDWeaponData_t ) );
			weaponData.Append( newWeapon );
		}
		return true;
	}
	
	if( idStr::FindText( _name, "leveldata", false ) >= 0 )
	{
		idStr tempName = _name;
		int level = atoi( tempName.Right( 2 ) ) - 1;
		
		idStr levelData;
		ParseString( src, levelData );
		ParseLevelData( level, levelData );
		return true;
	}
	
	if( idStr::FindText( _name, "asteroiddata", false ) >= 0 )
	{
		idStr tempName = _name;
		int level = atoi( tempName.Right( 2 ) ) - 1;
		
		idStr asteroidData;
		ParseString( src, asteroidData );
		ParseAsteroidData( level, asteroidData );
		return true;
	}
	
	if( idStr::FindText( _name, "weapondata", false ) >= 0 )
	{
		idStr tempName = _name;
		int weapon = atoi( tempName.Right( 2 ) ) - 1;
		
		idStr weaponData;
		ParseString( src, weaponData );
		ParseWeaponData( weapon, weaponData );
		return true;
	}
	
	if( idStr::FindText( _name, "astronautdata", false ) >= 0 )
	{
		idStr tempName = _name;
		int level = atoi( tempName.Right( 2 ) ) - 1;
		
		idStr astronautData;
		ParseString( src, astronautData );
		ParseAstronautData( level, astronautData );
		return true;
	}
	
	if( idStr::FindText( _name, "powerupdata", false ) >= 0 )
	{
		idStr tempName = _name;
		int level = atoi( tempName.Right( 2 ) ) - 1;
		
		idStr powerupData;
		ParseString( src, powerupData );
		ParsePowerupData( level, powerupData );
		return true;
	}
	
	return idWindow::ParseInternalVar( _name, src );
}

void idGameSSDWindow::ParseLevelData( int level, const idStr& levelDataString )
{

	idParser parser;
	idToken token;
	parser.LoadMemory( levelDataString.c_str(), levelDataString.Length(), "LevelData" );
	
	levelData[level].spawnBuffer = parser.ParseFloat();
	levelData[level].needToWin = parser.ParseInt(); //Required Destroyed
	
}

void idGameSSDWindow::ParseAsteroidData( int level, const idStr& asteroidDataString )
{

	idParser parser;
	idToken token;
	parser.LoadMemory( asteroidDataString.c_str(), asteroidDataString.Length(), "AsteroidData" );
	
	asteroidData[level].speedMin = parser.ParseFloat(); //Speed Min
	asteroidData[level].speedMax = parser.ParseFloat(); //Speed Max
	
	asteroidData[level].sizeMin = parser.ParseFloat(); //Size Min
	asteroidData[level].sizeMax = parser.ParseFloat(); //Size Max
	
	asteroidData[level].rotateMin = parser.ParseFloat(); //Rotate Min (rotations per second)
	asteroidData[level].rotateMax = parser.ParseFloat(); //Rotate Max (rotations per second)
	
	asteroidData[level].spawnMin = parser.ParseInt(); //Spawn Min
	asteroidData[level].spawnMax = parser.ParseInt(); //Spawn Max
	
	asteroidData[level].asteroidHealth = parser.ParseInt(); //Health of the asteroid
	asteroidData[level].asteroidDamage = parser.ParseInt(); //Asteroid Damage
	asteroidData[level].asteroidPoints = parser.ParseInt(); //Points awarded for destruction
}

void idGameSSDWindow::ParsePowerupData( int level, const idStr& powerupDataString )
{

	idParser parser;
	idToken token;
	parser.LoadMemory( powerupDataString.c_str(), powerupDataString.Length(), "PowerupData" );
	
	powerupData[level].speedMin = parser.ParseFloat(); //Speed Min
	powerupData[level].speedMax = parser.ParseFloat(); //Speed Max
	
	powerupData[level].rotateMin = parser.ParseFloat(); //Rotate Min (rotations per second)
	powerupData[level].rotateMax = parser.ParseFloat(); //Rotate Max (rotations per second)
	
	powerupData[level].spawnMin = parser.ParseInt(); //Spawn Min
	powerupData[level].spawnMax = parser.ParseInt(); //Spawn Max
	
}

void idGameSSDWindow::ParseWeaponData( int weapon, const idStr& weaponDataString )
{

	idParser parser;
	idToken token;
	parser.LoadMemory( weaponDataString.c_str(), weaponDataString.Length(), "WeaponData" );
	
	weaponData[weapon].speed = parser.ParseFloat();
	weaponData[weapon].damage = parser.ParseFloat();
	weaponData[weapon].size = parser.ParseFloat();
}

void idGameSSDWindow::ParseAstronautData( int level, const idStr& astronautDataString )
{

	idParser parser;
	idToken token;
	parser.LoadMemory( astronautDataString.c_str(), astronautDataString.Length(), "AstronautData" );
	
	astronautData[level].speedMin = parser.ParseFloat(); //Speed Min
	astronautData[level].speedMax = parser.ParseFloat(); //Speed Max
	
	astronautData[level].rotateMin = parser.ParseFloat(); //Rotate Min (rotations per second)
	astronautData[level].rotateMax = parser.ParseFloat(); //Rotate Max (rotations per second)
	
	astronautData[level].spawnMin = parser.ParseInt(); //Spawn Min
	astronautData[level].spawnMax = parser.ParseInt(); //Spawn Max
	
	astronautData[level].health = parser.ParseInt(); //Health of the asteroid
	astronautData[level].points = parser.ParseInt(); //Asteroid Damage
	astronautData[level].penalty = parser.ParseInt(); //Points awarded for destruction
}

void idGameSSDWindow::CommonInit()
{
	crosshair.InitCrosshairs();
	
	
	beginLevel = false;
	resetGame = false;
	continueGame = false;
	refreshGuiData = false;
	
	ssdTime = 0;
	levelCount = 0;
	weaponCount = 0;
	screenBounds = idBounds( idVec3( -320, -240, 0 ), idVec3( 320, 240, 0 ) );
	
	superBlasterTimeout = 0;
	
	currentSound = 0;
	
	//Precahce all assets that are loaded dynamically
	declManager->FindMaterial( ASTEROID_MATERIAL );
	declManager->FindMaterial( ASTRONAUT_MATERIAL );
	
	for( int i = 0; i < EXPLOSION_MATERIAL_COUNT; i++ )
	{
		declManager->FindMaterial( explosionMaterials[i] );
	}
	declManager->FindMaterial( PROJECTILE_MATERIAL );
	for( int i = 0; i < POWERUP_MATERIAL_COUNT; i++ )
	{
		declManager->FindMaterial( powerupMaterials[i][0] );
		declManager->FindMaterial( powerupMaterials[i][1] );
	}
	
	// Precache sounds
	declManager->FindSound( "arcade_blaster" );
	declManager->FindSound( "arcade_capture" );
	declManager->FindSound( "arcade_explode" );
	
	ResetGameStats();
}

void idGameSSDWindow::ResetGameStats()
{

	ResetEntities();
	
	//Reset the gamestats structure
	memset( &gameStats, 0, sizeof( gameStats ) );
	
	gameStats.health = 100;
	
}

void idGameSSDWindow::ResetLevelStats()
{

	ResetEntities();
	
	//Reset the level statistics structure
	memset( &gameStats.levelStats, 0, sizeof( gameStats.levelStats ) );
	
	
}

void idGameSSDWindow::ResetEntities()
{
	//Destroy all of the entities
	for( int i = 0; i < entities.Num(); i++ )
	{
		entities[i]->DestroyEntity();
	}
	entities.Clear();
}

void idGameSSDWindow::StartGame()
{

	gameStats.gameRunning = true;
}

void idGameSSDWindow::StopGame()
{

	gameStats.gameRunning = false;
}

void idGameSSDWindow::GameOver()
{


	StopGame();
	
	gui->HandleNamedEvent( "gameOver" );
}

void idGameSSDWindow::BeginLevel( int level )
{

	ResetLevelStats();
	
	gameStats.currentLevel = level;
	
	StartGame();
}

/**
* Continue game resets the players health
*/
void idGameSSDWindow::ContinueGame()
{
	gameStats.health = 100;
	
	StartGame();
}

void idGameSSDWindow::LevelComplete()
{

	gameStats.prebonusscore = gameStats.score;
	
	// Add the bonuses
	int accuracy;
	if( !gameStats.levelStats.shotCount )
	{
		accuracy = 0;
	}
	else
	{
		accuracy = ( int )( ( ( float )gameStats.levelStats.hitCount / ( float )gameStats.levelStats.shotCount ) * 100.0f );
	}
	int accuracyPoints = Max( 0, accuracy - 50 ) * 20;
	
	gui->SetStateString( "player_accuracy_score", va( "%i", accuracyPoints ) );
	
	gameStats.score += accuracyPoints;
	
	int saveAccuracy;
	int totalAst = gameStats.levelStats.savedAstronauts + gameStats.levelStats.killedAstronauts;
	if( !totalAst )
	{
		saveAccuracy = 0;
	}
	else
	{
		saveAccuracy = ( int )( ( ( float )gameStats.levelStats.savedAstronauts / ( float )totalAst ) * 100.0f );
	}
	accuracyPoints = Max( 0, saveAccuracy - 50 ) * 20;
	
	gui->SetStateString( "save_accuracy_score", va( "%i", accuracyPoints ) );
	
	gameStats.score += accuracyPoints;
	
	
	
	StopSuperBlaster();
	
	gameStats.nextLevel++;
	
	if( gameStats.nextLevel >= levelCount )
	{
		//Have they beaten the game
		GameComplete();
	}
	else
	{
	
		//Make sure we don't go above the levelcount
		//min(gameStats.nextLevel, levelCount-1);
		
		StopGame();
		gui->HandleNamedEvent( "levelComplete" );
	}
}

void idGameSSDWindow::GameComplete()
{
	StopGame();
	gui->HandleNamedEvent( "gameComplete" );
}


void idGameSSDWindow::UpdateGame()
{

	//Check to see if and functions where called by the gui
	if( beginLevel == true )
	{
		beginLevel = false;
		BeginLevel( gameStats.nextLevel );
	}
	if( resetGame == true )
	{
		resetGame = false;
		ResetGameStats();
	}
	if( continueGame == true )
	{
		continueGame = false;
		ContinueGame();
	}
	if( refreshGuiData == true )
	{
		refreshGuiData = false;
		RefreshGuiData();
	}
	
	if( gameStats.gameRunning )
	{
	
		//We assume an upate every 16 milliseconds
		ssdTime += 16;
		
		if( superBlasterTimeout && ssdTime > superBlasterTimeout )
		{
			StopSuperBlaster();
		}
		
		//Find if we are targeting and enemy
		idVec2 cursor;
		//GetCursor(cursor);
		cursor.x = gui->CursorX();
		cursor.y = gui->CursorY();
		gameStats.levelStats.targetEnt = EntityHitTest( cursor );
		
		//Update from back to front
		for( int i = entities.Num() - 1; i >= 0; i-- )
		{
			entities[i]->Update();
		}
		
		CheckForHits();
		
		//Delete entities that need to be deleted
		for( int i = entities.Num() - 1; i >= 0; i-- )
		{
			if( entities[i]->destroyed )
			{
				SSDEntity* ent = entities[i];
				ent->DestroyEntity();
				entities.RemoveIndex( i );
			}
		}
		
		//Check if we can spawn an asteroid
		SpawnAsteroid();
		
		//Check if we should spawn an astronaut
		SpawnAstronaut();
		
		//Check if we should spawn an asteroid
		SpawnPowerup();
	}
}

void idGameSSDWindow::CheckForHits()
{

	//See if the entity has gotten close enough
	for( int i = 0; i < entities.Num(); i++ )
	{
		SSDEntity* ent = entities[i];
		if( ent->position.z <= Z_NEAR )
		{
		
			if( !ent->noPlayerDamage )
			{
			
				//Is the object still in the screen
				idVec3 entPos = ent->position;
				entPos.z = 0;
				
				idBounds entBounds( entPos );
				entBounds.ExpandSelf( ent->hitRadius );
				
				if( screenBounds.IntersectsBounds( entBounds ) )
				{
				
					ent->OnStrikePlayer();
					
					//The entity hit the player figure out what is was and act appropriately
					if( ent->type == SSD_ENTITY_ASTEROID )
					{
						AsteroidStruckPlayer( static_cast<SSDAsteroid*>( ent ) );
					}
					else if( ent->type == SSD_ENTITY_ASTRONAUT )
					{
						AstronautStruckPlayer( static_cast<SSDAstronaut*>( ent ) );
					}
				}
				else
				{
					//Tag for removal later in the frame
					ent->destroyed = true;
				}
			}
		}
	}
}

void idGameSSDWindow::ZOrderEntities()
{
	//Z-Order the entities
	//Using a simple sorting method
	for( int i = entities.Num() - 1; i >= 0; i-- )
	{
		bool flipped = false;
		for( int j = 0;  j < i ; j++ )
		{
			if( entities[j]->position.z > entities[j + 1]->position.z )
			{
				SSDEntity* ent = entities[j];
				entities[j] = entities[j + 1];
				entities[j + 1] = ent;
				flipped = true;
			}
		}
		if( !flipped )
		{
			//Jump out because it is sorted
			break;
		}
	}
}

void idGameSSDWindow::SpawnAsteroid()
{

	int currentTime = ssdTime;
	
	if( currentTime < gameStats.levelStats.nextAsteroidSpawnTime )
	{
		//Not time yet
		return;
	}
	
	//Lets spawn it
	idVec3 startPosition;
	
	float spawnBuffer = levelData[gameStats.currentLevel].spawnBuffer * 2.0f;
	startPosition.x = random.RandomInt( V_WIDTH + spawnBuffer ) - ( ( V_WIDTH / 2.0f ) + spawnBuffer );
	startPosition.y = random.RandomInt( V_HEIGHT + spawnBuffer ) - ( ( V_HEIGHT / 2.0f ) + spawnBuffer );
	startPosition.z = ENTITY_START_DIST;
	
	float speed = random.RandomInt( asteroidData[gameStats.currentLevel].speedMax - asteroidData[gameStats.currentLevel].speedMin ) + asteroidData[gameStats.currentLevel].speedMin;
	float size = random.RandomInt( asteroidData[gameStats.currentLevel].sizeMax - asteroidData[gameStats.currentLevel].sizeMin ) + asteroidData[gameStats.currentLevel].sizeMin;
	float rotate = ( random.RandomFloat() * ( asteroidData[gameStats.currentLevel].rotateMax - asteroidData[gameStats.currentLevel].rotateMin ) ) + asteroidData[gameStats.currentLevel].rotateMin;
	
	SSDAsteroid* asteroid = SSDAsteroid::GetNewAsteroid( this, startPosition, idVec2( size, size ), speed, rotate, asteroidData[gameStats.currentLevel].asteroidHealth );
	entities.Append( asteroid );
	
	gameStats.levelStats.nextAsteroidSpawnTime = currentTime + random.RandomInt( asteroidData[gameStats.currentLevel].spawnMax - asteroidData[gameStats.currentLevel].spawnMin ) + asteroidData[gameStats.currentLevel].spawnMin;
}

void idGameSSDWindow::FireWeapon( int key )
{

	idVec2 cursorWorld = GetCursorWorld();
	idVec2 cursor;
	//GetCursor(cursor);
	cursor.x = gui->CursorX();
	cursor.y = gui->CursorY();
	
	if( key == K_MOUSE1 )
	{
	
		gameStats.levelStats.shotCount++;
		
		if( gameStats.levelStats.targetEnt )
		{
			//Aim the projectile from the bottom of the screen directly at the ent
			//SSDProjectile* newProj = new (TAG_OLD_UI) SSDProjectile(this, idVec3(320,0,0), gameStats.levelStats.targetEnt->position, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
			SSDProjectile* newProj = SSDProjectile::GetNewProjectile( this, idVec3( 0, -180, 0 ), gameStats.levelStats.targetEnt->position, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size );
			entities.Append( newProj );
			//newProj = SSDProjectile::GetNewProjectile(this, idVec3(-320,-0,0), gameStats.levelStats.targetEnt->position, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
			//entities.Append(newProj);
			
			//We hit something
			gameStats.levelStats.hitCount++;
			
			gameStats.levelStats.targetEnt->OnHit( key );
			
			if( gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTEROID )
			{
				HitAsteroid( static_cast<SSDAsteroid*>( gameStats.levelStats.targetEnt ), key );
			}
			else if( gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTRONAUT )
			{
				HitAstronaut( static_cast<SSDAstronaut*>( gameStats.levelStats.targetEnt ), key );
			}
		}
		else
		{
			////Aim the projectile at the cursor position all the way to the far clipping
			//SSDProjectile* newProj = SSDProjectile::GetNewProjectile(this, idVec3(0,-180,0), idVec3(cursorWorld.x, cursorWorld.y, (Z_FAR-Z_NEAR)/2.0f), weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
			
			//Aim the projectile so it crosses the cursor 1/4 of screen
			idVec3 vec = idVec3( cursorWorld.x, cursorWorld.y, ( Z_FAR - Z_NEAR ) / 8.0f );
			vec *= 8;
			SSDProjectile* newProj = SSDProjectile::GetNewProjectile( this, idVec3( 0, -180, 0 ), vec, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size );
			entities.Append( newProj );
			
		}
		
		
		//Play the blaster sound
		PlaySound( "arcade_blaster" );
		
	} /*else if (key == K_MOUSE2) {
		if(gameStats.levelStats.targetEnt) {
			if(gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTRONAUT) {
				HitAstronaut(static_cast<SSDAstronaut*>(gameStats.levelStats.targetEnt), key);
			}
		}
	}*/
}

SSDEntity* idGameSSDWindow::EntityHitTest( const idVec2& pt )
{

	for( int i = 0; i < entities.Num(); i++ )
	{
		//Since we ZOrder the entities every frame we can stop at the first entity we hit.
		//ToDo: Make sure this assumption is true
		if( entities[i]->HitTest( pt ) )
		{
			return entities[i];
		}
	}
	return NULL;
}

void idGameSSDWindow::HitAsteroid( SSDAsteroid* asteroid, int key )
{



	asteroid->health -= weaponData[gameStats.currentWeapon].damage;
	
	if( asteroid->health <= 0 )
	{
	
		//The asteroid has been destroyed
		SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, asteroid->position, asteroid->size * 2, 300, SSDExplosion::EXPLOSION_NORMAL, asteroid );
		entities.Append( explosion );
		PlaySound( "arcade_explode" );
		
		AddScore( asteroid, asteroidData[gameStats.currentLevel].asteroidPoints );
		
		//Don't let the player hit it anymore because
		asteroid->noHit = true;
		
		gameStats.levelStats.destroyedAsteroids++;
		//if(gameStats.levelStats.destroyedAsteroids >= levelData[gameStats.currentLevel].needToWin) {
		//	LevelComplete();
		//}
		
	}
	else
	{
		//This was a damage hit so create a real small quick explosion
		SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, asteroid->position, asteroid->size / 2.0f, 200, SSDExplosion::EXPLOSION_NORMAL, asteroid, false, false );
		entities.Append( explosion );
	}
}

void idGameSSDWindow::AsteroidStruckPlayer( SSDAsteroid* asteroid )
{

	asteroid->noPlayerDamage = true;
	asteroid->noHit = true;
	
	AddDamage( asteroidData[gameStats.currentLevel].asteroidDamage );
	
	SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, asteroid->position, asteroid->size * 2, 300, SSDExplosion::EXPLOSION_NORMAL, asteroid );
	entities.Append( explosion );
	PlaySound( "arcade_explode" );
}

void idGameSSDWindow::AddScore( SSDEntity* ent, int points )
{

	SSDPoints* pointsEnt;
	
	if( points > 0 )
	{
		pointsEnt = SSDPoints::GetNewPoints( this, ent, points, 1000, 50, idVec4( 0, 1, 0, 1 ) );
	}
	else
	{
		pointsEnt = SSDPoints::GetNewPoints( this, ent, points, 1000, 50, idVec4( 1, 0, 0, 1 ) );
	}
	entities.Append( pointsEnt );
	
	gameStats.score += points;
	gui->SetStateString( "player_score", va( "%i", gameStats.score ) );
}

void idGameSSDWindow::AddDamage( int damage )
{
	gameStats.health -= damage;
	gui->SetStateString( "player_health", va( "%i", gameStats.health ) );
	
	gui->HandleNamedEvent( "playerDamage" );
	
	if( gameStats.health <= 0 )
	{
		//The player is dead
		GameOver();
	}
}

void idGameSSDWindow::AddHealth( int health )
{
	gameStats.health += health;
	gameStats.health = Min( 100, gameStats.health );
}


void idGameSSDWindow::OnNuke()
{

	gui->HandleNamedEvent( "nuke" );
	
	//Destory All Asteroids
	for( int i = 0 ; i < entities.Num(); i++ )
	{
	
		if( entities[i]->type == SSD_ENTITY_ASTEROID )
		{
		
			//The asteroid has been destroyed
			SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, entities[i]->position, entities[i]->size * 2, 300, SSDExplosion::EXPLOSION_NORMAL, entities[i] );
			entities.Append( explosion );
			
			AddScore( entities[i], asteroidData[gameStats.currentLevel].asteroidPoints );
			
			//Don't let the player hit it anymore because
			entities[i]->noHit = true;
			
			gameStats.levelStats.destroyedAsteroids++;
		}
	}
	PlaySound( "arcade_explode" );
	
	//Check to see if a nuke ends the level
	/*if(gameStats.levelStats.destroyedAsteroids >= levelData[gameStats.currentLevel].needToWin) {
		LevelComplete();
	
	}*/
}

void idGameSSDWindow::OnRescueAll()
{

	gui->HandleNamedEvent( "rescueAll" );
	
	//Rescue All Astronauts
	for( int i = 0 ; i < entities.Num(); i++ )
	{
	
		if( entities[i]->type == SSD_ENTITY_ASTRONAUT )
		{
		
			AstronautStruckPlayer( ( SSDAstronaut* )entities[i] );
		}
	}
}

void idGameSSDWindow::OnSuperBlaster()
{

	StartSuperBlaster();
}



void idGameSSDWindow::RefreshGuiData()
{


	gui->SetStateString( "nextLevel", va( "%i", gameStats.nextLevel + 1 ) );
	gui->SetStateString( "currentLevel", va( "%i", gameStats.currentLevel + 1 ) );
	
	float accuracy;
	if( !gameStats.levelStats.shotCount )
	{
		accuracy = 0;
	}
	else
	{
		accuracy = ( ( float )gameStats.levelStats.hitCount / ( float )gameStats.levelStats.shotCount ) * 100.0f;
	}
	gui->SetStateString( "player_accuracy", va( "%d%%", ( int )accuracy ) );
	
	float saveAccuracy;
	int totalAst = gameStats.levelStats.savedAstronauts + gameStats.levelStats.killedAstronauts;
	
	if( !totalAst )
	{
		saveAccuracy = 0;
	}
	else
	{
		saveAccuracy = ( ( float )gameStats.levelStats.savedAstronauts / ( float )totalAst ) * 100.0f;
	}
	gui->SetStateString( "save_accuracy", va( "%d%%", ( int )saveAccuracy ) );
	
	
	
	
	if( gameStats.levelStats.targetEnt )
	{
		int dist = ( gameStats.levelStats.targetEnt->position.z / 100.0f );
		dist *= 100;
		gui->SetStateString( "target_info", va( "%i meters", dist ) );
	}
	else
	{
		gui->SetStateString( "target_info", "No Target" );
	}
	
	gui->SetStateString( "player_health", va( "%i", gameStats.health ) );
	gui->SetStateString( "player_score", va( "%i", gameStats.score ) );
	gui->SetStateString( "player_prebonusscore", va( "%i", gameStats.prebonusscore ) );
	gui->SetStateString( "level_complete", va( "%i/%i", gameStats.levelStats.savedAstronauts, levelData[gameStats.currentLevel].needToWin ) );
	
	
	if( superBlasterTimeout )
	{
		float timeRemaining = ( superBlasterTimeout - ssdTime ) / 1000.0f;
		gui->SetStateString( "super_blaster_time", va( "%.2f", timeRemaining ) );
	}
}

idVec2 idGameSSDWindow::GetCursorWorld()
{

	idVec2 cursor;
	//GetCursor(cursor);
	cursor.x = gui->CursorX();
	cursor.y = gui->CursorY();
	cursor.x = cursor.x - 0.5f * V_WIDTH;
	cursor.y = -( cursor.y  - 0.5f * V_HEIGHT );
	return cursor;
}

void idGameSSDWindow::SpawnAstronaut()
{

	int currentTime = ssdTime;
	
	if( currentTime < gameStats.levelStats.nextAstronautSpawnTime )
	{
		//Not time yet
		return;
	}
	
	//Lets spawn it
	idVec3 startPosition;
	
	startPosition.x = random.RandomInt( V_WIDTH ) - ( V_WIDTH / 2.0f );
	startPosition.y = random.RandomInt( V_HEIGHT ) - ( V_HEIGHT / 2.0f );
	startPosition.z = ENTITY_START_DIST;
	
	float speed = random.RandomInt( astronautData[gameStats.currentLevel].speedMax - astronautData[gameStats.currentLevel].speedMin ) + astronautData[gameStats.currentLevel].speedMin;
	float rotate = ( random.RandomFloat() * ( astronautData[gameStats.currentLevel].rotateMax - astronautData[gameStats.currentLevel].rotateMin ) ) + astronautData[gameStats.currentLevel].rotateMin;
	
	SSDAstronaut* astronaut = SSDAstronaut::GetNewAstronaut( this, startPosition, speed, rotate, astronautData[gameStats.currentLevel].health );
	entities.Append( astronaut );
	
	gameStats.levelStats.nextAstronautSpawnTime = currentTime + random.RandomInt( astronautData[gameStats.currentLevel].spawnMax - astronautData[gameStats.currentLevel].spawnMin ) + astronautData[gameStats.currentLevel].spawnMin;
}

void idGameSSDWindow::HitAstronaut( SSDAstronaut* astronaut, int key )
{


	if( key == K_MOUSE1 )
	{
		astronaut->health -= weaponData[gameStats.currentWeapon].damage;
		
		if( astronaut->health <= 0 )
		{
		
			gameStats.levelStats.killedAstronauts++;
			
			//The astronaut has been destroyed
			SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, astronaut->position, astronaut->size * 2, 300, SSDExplosion::EXPLOSION_NORMAL, astronaut );
			entities.Append( explosion );
			PlaySound( "arcade_explode" );
			
			//Add the penalty for killing the astronaut
			AddScore( astronaut, astronautData[gameStats.currentLevel].penalty );
			
			//Don't let the player hit it anymore
			astronaut->noHit = true;
		}
		else
		{
			//This was a damage hit so create a real small quick explosion
			SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, astronaut->position, astronaut->size / 2.0f, 200, SSDExplosion::EXPLOSION_NORMAL, astronaut, false, false );
			entities.Append( explosion );
		}
	}
}

void idGameSSDWindow::AstronautStruckPlayer( SSDAstronaut* astronaut )
{

	gameStats.levelStats.savedAstronauts++;
	
	astronaut->noPlayerDamage = true;
	astronaut->noHit = true;
	
	//We are saving an astronaut
	SSDExplosion* explosion = SSDExplosion::GetNewExplosion( this, astronaut->position, astronaut->size * 2, 300, SSDExplosion::EXPLOSION_TELEPORT, astronaut );
	entities.Append( explosion );
	PlaySound( "arcade_capture" );
	
	//Give the player points for saving the astronaut
	AddScore( astronaut, astronautData[gameStats.currentLevel].points );
	
	if( gameStats.levelStats.savedAstronauts >= levelData[gameStats.currentLevel].needToWin )
	{
		LevelComplete();
	}
	
}

void idGameSSDWindow::SpawnPowerup()
{

	int currentTime = ssdTime;
	
	if( currentTime < gameStats.levelStats.nextPowerupSpawnTime )
	{
		//Not time yet
		return;
	}
	
	float speed = random.RandomInt( powerupData[gameStats.currentLevel].speedMax - powerupData[gameStats.currentLevel].speedMin ) + powerupData[gameStats.currentLevel].speedMin;
	float rotate = ( random.RandomFloat() * ( powerupData[gameStats.currentLevel].rotateMax - powerupData[gameStats.currentLevel].rotateMin ) ) + powerupData[gameStats.currentLevel].rotateMin;
	
	SSDPowerup* powerup = SSDPowerup::GetNewPowerup( this, speed, rotate );
	entities.Append( powerup );
	
	gameStats.levelStats.nextPowerupSpawnTime = currentTime + random.RandomInt( powerupData[gameStats.currentLevel].spawnMax - powerupData[gameStats.currentLevel].spawnMin ) + powerupData[gameStats.currentLevel].spawnMin;
	
}

void idGameSSDWindow::StartSuperBlaster()
{

	gui->HandleNamedEvent( "startSuperBlaster" );
	gameStats.currentWeapon = 1;
	superBlasterTimeout = ssdTime + 10000;
	
}
void idGameSSDWindow::StopSuperBlaster()
{
	gui->HandleNamedEvent( "stopSuperBlaster" );
	gameStats.currentWeapon = 0;
	superBlasterTimeout = 0;
	
}

SSDEntity* idGameSSDWindow::GetSpecificEntity( int type, int id )
{
	SSDEntity* ent = NULL;
	switch( type )
	{
		case SSD_ENTITY_ASTEROID:
			ent = SSDAsteroid::GetSpecificAsteroid( id );
			break;
		case SSD_ENTITY_ASTRONAUT:
			ent = SSDAstronaut::GetSpecificAstronaut( id );
			break;
		case SSD_ENTITY_EXPLOSION:
			ent = SSDExplosion::GetSpecificExplosion( id );
			break;
		case SSD_ENTITY_POINTS:
			ent = SSDPoints::GetSpecificPoints( id );
			break;
		case SSD_ENTITY_PROJECTILE:
			ent = SSDProjectile::GetSpecificProjectile( id );
			break;
		case SSD_ENTITY_POWERUP:
			ent = SSDPowerup::GetSpecificPowerup( id );
			break;
	}
	return ent;
}

#define MAX_SOUND_CHANNEL 8

void idGameSSDWindow::PlaySound( const char* sound )
{

	common->SW()->PlayShaderDirectly( sound, currentSound );
	
	currentSound++;
	if( currentSound >= MAX_SOUND_CHANNEL )
	{
		currentSound = 0;
	}
}
