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

Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.

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

Doom 3 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 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 Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 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 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.

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

#include "sys/platform.h"
#include "framework/Session_local.h"
#include "sound/sound.h"
#include "ui/DeviceContext.h"
#include "ui/Window.h"
#include "ui/UserInterfaceLocal.h"

#include "ui/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(idDeviceContext *dc, 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(idDeviceContext *dc) {


	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);

	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 += game->GetDC()->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(idDeviceContext *d, idUserInterfaceLocal *g) : idWindow(d, g) {
	dc = d;
	gui = g;
	CommonInit();
}

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(dc);
		}

		//The last thing to draw is the crosshair
		idVec2 cursor;
		//GetCursor(cursor);
		cursor.x = gui->CursorX();
		cursor.y = gui->CursorY();

		crosshair.Draw(dc, cursor);
	}
}


bool idGameSSDWindow::ParseInternalVar(const char *_name, idParser *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 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 if(gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTRONAUT) {

			}
		} 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) {

	session->sw->PlayShaderDirectly(sound, currentSound);

	currentSound++;
	if(currentSound >= MAX_SOUND_CHANNEL) {
		currentSound = 0;
	}
}
