DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

GameSSDWindow.cpp (61761B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 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.
     23 
     24 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.
     25 
     26 ===========================================================================
     27 */
     28 #pragma hdrstop
     29 #include "../idlib/precompiled.h"
     30 
     31 #include "DeviceContext.h"
     32 #include "Window.h"
     33 #include "UserInterfaceLocal.h"
     34 #include "GameSSDWindow.h"
     35 
     36 
     37 #define Z_NEAR 100.0f
     38 #define Z_FAR  4000.0f
     39 #define ENTITY_START_DIST 3000
     40 
     41 #define V_WIDTH 640.0f
     42 #define V_HEIGHT 480.0f
     43 
     44 /*
     45 *****************************************************************************
     46 * SSDCrossHair
     47 ****************************************************************************
     48 */
     49 
     50 #define CROSSHAIR_STANDARD_MATERIAL "game/SSD/crosshair_standard"
     51 #define CROSSHAIR_SUPER_MATERIAL "game/SSD/crosshair_super"
     52 
     53 SSDCrossHair::SSDCrossHair() {
     54 }
     55 
     56 SSDCrossHair::~SSDCrossHair() {
     57 }
     58 
     59 void SSDCrossHair::WriteToSaveGame( idFile *savefile ) {
     60 	
     61 	savefile->Write(&currentCrosshair, sizeof(currentCrosshair));
     62 	savefile->Write(&crosshairWidth, sizeof(crosshairWidth));
     63 	savefile->Write(&crosshairHeight, sizeof(crosshairHeight));
     64 
     65 }
     66 
     67 void SSDCrossHair::ReadFromSaveGame( idFile *savefile ) {
     68 
     69 	InitCrosshairs();
     70 
     71 	savefile->Read(&currentCrosshair, sizeof(currentCrosshair));
     72 	savefile->Read(&crosshairWidth, sizeof(crosshairWidth));
     73 	savefile->Read(&crosshairHeight, sizeof(crosshairHeight));
     74 
     75 }
     76 
     77 void SSDCrossHair::InitCrosshairs() {
     78 	
     79 	crosshairMaterial[CROSSHAIR_STANDARD] = declManager->FindMaterial( CROSSHAIR_STANDARD_MATERIAL );
     80 	crosshairMaterial[CROSSHAIR_SUPER] = declManager->FindMaterial( CROSSHAIR_SUPER_MATERIAL );
     81 
     82 	crosshairWidth = 64;
     83 	crosshairHeight = 64;
     84 
     85 	currentCrosshair = CROSSHAIR_STANDARD;
     86 
     87 }
     88 
     89 void SSDCrossHair::Draw(const idVec2& cursor) {
     90 
     91 	float x,y;
     92 	x = cursor.x-(crosshairWidth/2);
     93 	y = cursor.y-(crosshairHeight/2);
     94 	dc->DrawMaterial(x, y, crosshairWidth, crosshairHeight, crosshairMaterial[currentCrosshair], colorWhite, 1.0f, 1.0f);
     95 	
     96 }
     97 
     98 /*
     99 *****************************************************************************
    100 * SSDEntity	
    101 ****************************************************************************
    102 */
    103 
    104 SSDEntity::SSDEntity() {
    105 	EntityInit(); 
    106 }
    107 
    108 SSDEntity::~SSDEntity() { 
    109 }
    110 
    111 void SSDEntity::WriteToSaveGame( idFile *savefile ) {
    112 	
    113 	savefile->Write(&type, sizeof(type));
    114 	game->WriteSaveGameString(materialName, savefile);
    115 	savefile->Write(&position, sizeof(position));
    116 	savefile->Write(&size, sizeof(size));
    117 	savefile->Write(&radius, sizeof(radius));
    118 	savefile->Write(&hitRadius, sizeof(hitRadius));
    119 	savefile->Write(&rotation, sizeof(rotation));
    120 
    121 	savefile->Write(&matColor, sizeof(matColor));
    122 	
    123 	game->WriteSaveGameString(text, savefile);
    124 	savefile->Write(&textScale, sizeof(textScale));
    125 	savefile->Write(&foreColor, sizeof(foreColor));
    126 	
    127 	savefile->Write(&currentTime, sizeof(currentTime));
    128 	savefile->Write(&lastUpdate, sizeof(lastUpdate));
    129 	savefile->Write(&elapsed, sizeof(elapsed));
    130 
    131 	savefile->Write(&destroyed, sizeof(destroyed));
    132 	savefile->Write(&noHit, sizeof(noHit));
    133 	savefile->Write(&noPlayerDamage, sizeof(noPlayerDamage));
    134 
    135 	savefile->Write(&inUse, sizeof(inUse));
    136 
    137 }
    138 
    139 void SSDEntity::ReadFromSaveGame( idFile *savefile,  idGameSSDWindow* _game ) {
    140 
    141 	savefile->Read(&type, sizeof(type));
    142 	game->ReadSaveGameString(materialName, savefile);
    143 	SetMaterial(materialName);
    144 	savefile->Read(&position, sizeof(position));
    145 	savefile->Read(&size, sizeof(size));
    146 	savefile->Read(&radius, sizeof(radius));
    147 	savefile->Read(&hitRadius, sizeof(hitRadius));
    148 	savefile->Read(&rotation, sizeof(rotation));
    149 
    150 	savefile->Read(&matColor, sizeof(matColor));
    151 
    152 	game->ReadSaveGameString(text, savefile);
    153 	savefile->Read(&textScale, sizeof(textScale));
    154 	savefile->Read(&foreColor, sizeof(foreColor));
    155 
    156 	game = _game;
    157 	savefile->Read(&currentTime, sizeof(currentTime));
    158 	savefile->Read(&lastUpdate, sizeof(lastUpdate));
    159 	savefile->Read(&elapsed, sizeof(elapsed));
    160 
    161 	savefile->Read(&destroyed, sizeof(destroyed));
    162 	savefile->Read(&noHit, sizeof(noHit));
    163 	savefile->Read(&noPlayerDamage, sizeof(noPlayerDamage));
    164 
    165 	savefile->Read(&inUse, sizeof(inUse));
    166 }
    167 
    168 void SSDEntity::EntityInit() {
    169 
    170 	inUse = false;
    171 
    172 
    173 	type = SSD_ENTITY_BASE;
    174 	
    175 	materialName = "";
    176 	material = NULL;
    177 	position.Zero();
    178 	size.Zero();
    179 	radius = 0.0f;
    180 	hitRadius = 0.0f;
    181 	rotation = 0.0f;
    182 
    183 
    184 	currentTime = 0;
    185 	lastUpdate = 0;
    186 
    187 	destroyed = false;
    188 	noHit = false;
    189 	noPlayerDamage = false;
    190 
    191 	matColor.Set(1, 1, 1, 1);
    192 
    193 	text = "";
    194 	textScale = 1.0f;
    195 	foreColor.Set(1, 1, 1, 1);
    196 }
    197 
    198 void SSDEntity::SetGame(idGameSSDWindow* _game) {
    199 	game = _game;
    200 }
    201 
    202 void SSDEntity::SetMaterial(const char* name) {
    203 	materialName = name;
    204 	material = declManager->FindMaterial( name );
    205 	material->SetSort( SS_GUI );
    206 }
    207 
    208 void SSDEntity::SetPosition(const idVec3& _position) {
    209 	position = _position;
    210 }
    211 
    212 void SSDEntity::SetSize(const idVec2& _size) {
    213 	size = _size;
    214 }
    215 
    216 void SSDEntity::SetRadius(float _radius, float _hitFactor) {
    217 	radius = _radius;
    218 	hitRadius = _radius*_hitFactor;
    219 }
    220 
    221 void SSDEntity::SetRotation(float _rotation) {
    222 	rotation = _rotation;
    223 }
    224 
    225 void SSDEntity::Update() {
    226 
    227 	currentTime = game->ssdTime;
    228 
    229 	//Is this the first update
    230 	if(lastUpdate == 0) {
    231 		lastUpdate = currentTime;
    232 		return;
    233 	}
    234 
    235 	elapsed = currentTime - lastUpdate;
    236 
    237 	EntityUpdate();
    238 
    239 	lastUpdate = currentTime;
    240 }
    241 
    242 bool SSDEntity::HitTest(const idVec2& pt) {
    243 
    244 	if(noHit) {
    245 		return false;
    246 	}
    247 
    248 	idVec3 screenPos = WorldToScreen(position);
    249 
    250 
    251 	//Scale the radius based on the distance from the player
    252 	float scale = 1.0f -((screenPos.z-Z_NEAR)/(Z_FAR-Z_NEAR));
    253 	float scaledRad = scale*hitRadius;
    254 
    255 	//So we can compare against the square of the length between two points
    256 	float scaleRadSqr = scaledRad*scaledRad;
    257 
    258 	idVec2 diff = screenPos.ToVec2()-pt;
    259 	float dist = idMath::Fabs(diff.LengthSqr());
    260 
    261 	if(dist < scaleRadSqr) {
    262 		return true;
    263 	}
    264 	return false;
    265 }
    266 
    267 void SSDEntity::Draw() {
    268 
    269 
    270 	idVec2 persize;
    271 	float x,y;
    272 
    273 	idBounds bounds;
    274 	bounds[0] = idVec3(position.x - (size.x/2.0f), position.y - (size.y/2.0f), position.z);
    275 	bounds[1] = idVec3(position.x + (size.x/2.0f), position.y + (size.y/2.0f), position.z);
    276 
    277 	idBounds screenBounds = WorldToScreen(bounds);
    278 	persize.x = idMath::Fabs(screenBounds[1].x - screenBounds[0].x);
    279 	persize.y = idMath::Fabs(screenBounds[1].y - screenBounds[0].y);
    280 
    281 	idVec3 center = screenBounds.GetCenter();
    282 
    283 	x = screenBounds[0].x;
    284 	y = screenBounds[1].y;
    285 	dc->DrawMaterialRotated(x, y, persize.x, persize.y, material, matColor, 1.0f, 1.0f, DEG2RAD(rotation));
    286 
    287 	if(text.Length() > 0) {
    288 		idRectangle rect( x, y, VIRTUAL_WIDTH, VIRTUAL_HEIGHT );
    289 		dc->DrawText( text, textScale, 0, foreColor, rect, false );
    290 	}
    291 
    292 }
    293 
    294 void SSDEntity::DestroyEntity() {
    295 	inUse = false;
    296 }
    297 
    298 idBounds SSDEntity::WorldToScreen(const idBounds worldBounds) {
    299 
    300 	idVec3 screenMin = WorldToScreen(worldBounds[0]);
    301 	idVec3 screenMax = WorldToScreen(worldBounds[1]);
    302 
    303 	idBounds screenBounds(screenMin, screenMax);
    304 	return screenBounds;
    305 }
    306 
    307 idVec3 SSDEntity::WorldToScreen(const idVec3& worldPos) {
    308 	
    309 	float d = 0.5f*V_WIDTH*idMath::Tan(DEG2RAD(90.0f)/2.0f);
    310 
    311 	//World To Camera Coordinates
    312 	idVec3 cameraTrans(0,0,d);
    313 	idVec3 cameraPos;
    314 	cameraPos = worldPos + cameraTrans;
    315 
    316 	//Camera To Screen Coordinates
    317 	idVec3 screenPos;
    318 	screenPos.x = d*cameraPos.x/cameraPos.z + (0.5f*V_WIDTH-0.5f);
    319 	screenPos.y = -d*cameraPos.y/cameraPos.z + (0.5f*V_HEIGHT-0.5f);
    320 	screenPos.z = cameraPos.z;
    321 
    322 	return screenPos;
    323 }
    324 
    325 idVec3 SSDEntity::ScreenToWorld(const idVec3& screenPos) {
    326 
    327 	idVec3 worldPos;
    328 
    329 	worldPos.x = screenPos.x - 0.5f * V_WIDTH;
    330 	worldPos.y = -(screenPos.y  - 0.5f * V_HEIGHT);
    331 	worldPos.z = screenPos.z;
    332 
    333 	return worldPos;
    334 }
    335 
    336 /*
    337 *****************************************************************************
    338 * SSDMover	
    339 ****************************************************************************
    340 */
    341 
    342 SSDMover::SSDMover() {
    343 }
    344 
    345 SSDMover::~SSDMover() {
    346 }
    347 
    348 void SSDMover::WriteToSaveGame( idFile *savefile ) {
    349 	SSDEntity::WriteToSaveGame(savefile);
    350 	
    351 	savefile->Write(&speed, sizeof(speed));
    352 	savefile->Write(&rotationSpeed, sizeof(rotationSpeed));
    353 }
    354 
    355 void SSDMover::ReadFromSaveGame( idFile *savefile,  idGameSSDWindow* _game  ) {
    356 	SSDEntity::ReadFromSaveGame(savefile, _game);
    357 
    358 	savefile->Read(&speed, sizeof(speed));
    359 	savefile->Read(&rotationSpeed, sizeof(rotationSpeed));
    360 }
    361 
    362 void SSDMover::MoverInit(const idVec3& _speed, float _rotationSpeed) {
    363 	
    364 	speed = _speed;
    365 	rotationSpeed = _rotationSpeed;
    366 }
    367 
    368 void SSDMover::EntityUpdate() {
    369 	
    370 	SSDEntity::EntityUpdate();
    371 
    372 	//Move forward based on speed (units per second)
    373 	idVec3 moved = ((float)elapsed/1000.0f)*speed;
    374 	position += moved;
    375 
    376 	float rotated = ((float)elapsed/1000.0f)*rotationSpeed*360.0f;
    377 	rotation += rotated;
    378 	if(rotation >= 360) {
    379 		rotation -= 360.0f;
    380 	}
    381 	if(rotation < 0) {
    382 		rotation += 360.0f;
    383 	}	
    384 }
    385 
    386 
    387 /*
    388 *****************************************************************************
    389 * SSDAsteroid	
    390 ****************************************************************************
    391 */
    392 
    393 SSDAsteroid	SSDAsteroid::asteroidPool[MAX_ASTEROIDS];
    394 
    395 #define ASTEROID_MATERIAL "game/SSD/asteroid"
    396 
    397 SSDAsteroid::SSDAsteroid() {
    398 }
    399 
    400 SSDAsteroid::~SSDAsteroid() {	
    401 }
    402 
    403 void SSDAsteroid::WriteToSaveGame( idFile *savefile ) {
    404 	SSDMover::WriteToSaveGame(savefile);
    405 
    406 	savefile->Write(&health, sizeof(health));
    407 }
    408 
    409 void SSDAsteroid::ReadFromSaveGame( idFile *savefile,  idGameSSDWindow* _game  ) {
    410 	SSDMover::ReadFromSaveGame(savefile, _game);
    411 
    412 	savefile->Read(&health, sizeof(health));
    413 }
    414 
    415 void SSDAsteroid::Init(idGameSSDWindow* _game, const idVec3& startPosition, const idVec2& _size, float _speed, float rotate, int _health) {
    416 
    417 	EntityInit();
    418 	MoverInit(idVec3(0,0, -_speed), rotate);
    419 	
    420 	SetGame(_game);
    421 
    422 	type = SSD_ENTITY_ASTEROID;
    423 
    424 	SetMaterial(ASTEROID_MATERIAL);
    425 	SetSize(_size);
    426 	SetRadius(Max(size.x, size.y), 0.3f);
    427 	SetRotation(game->random.RandomInt(360));
    428 
    429 	
    430 	position = startPosition;
    431 
    432 	health = _health;
    433 }
    434 
    435 void SSDAsteroid::EntityUpdate() {
    436 	
    437 	SSDMover::EntityUpdate();
    438 }
    439 
    440 SSDAsteroid* SSDAsteroid::GetNewAsteroid(idGameSSDWindow* _game, const idVec3& startPosition, const idVec2& _size, float _speed, float rotate, int _health) {
    441 	for(int i = 0; i < MAX_ASTEROIDS; i++) {
    442 		if(!asteroidPool[i].inUse) {
    443 			asteroidPool[i].Init(_game, startPosition, _size, _speed, rotate, _health);
    444 			asteroidPool[i].inUse = true;
    445 			asteroidPool[i].id = i;
    446 
    447 			return &asteroidPool[i];
    448 		}
    449 	}
    450 	return NULL;
    451 }
    452 
    453 SSDAsteroid* SSDAsteroid::GetSpecificAsteroid(int id) {
    454 	return &asteroidPool[id];
    455 }
    456 
    457 void SSDAsteroid::WriteAsteroids(idFile* savefile) {
    458 	int count = 0;
    459 	for(int i = 0; i < MAX_ASTEROIDS; i++) {
    460 		if(asteroidPool[i].inUse) {
    461 			count++;
    462 		}
    463 	}
    464 	savefile->Write(&count, sizeof(count));
    465 	for(int i = 0; i < MAX_ASTEROIDS; i++) {
    466 		if(asteroidPool[i].inUse) {
    467 			savefile->Write(&(asteroidPool[i].id), sizeof(asteroidPool[i].id));
    468 			asteroidPool[i].WriteToSaveGame(savefile);
    469 		}
    470 	}
    471 }
    472 
    473 void SSDAsteroid::ReadAsteroids( idFile* savefile, idGameSSDWindow* _game) {
    474 	
    475 	int count;
    476 	savefile->Read(&count, sizeof(count));
    477 	for(int i = 0; i < count; i++) {
    478 		int id;
    479 		savefile->Read(&id, sizeof(id));
    480 		SSDAsteroid* ent = GetSpecificAsteroid(id);
    481 		ent->ReadFromSaveGame(savefile, _game);
    482 	}
    483 }
    484 
    485 /*
    486 *****************************************************************************
    487 * SSDAstronaut
    488 ****************************************************************************
    489 */
    490 
    491 SSDAstronaut	SSDAstronaut::astronautPool[MAX_ASTRONAUT];
    492 
    493 #define ASTRONAUT_MATERIAL "game/SSD/astronaut"
    494 
    495 SSDAstronaut::SSDAstronaut() {
    496 }
    497 
    498 SSDAstronaut::~SSDAstronaut() {	
    499 }
    500 
    501 void SSDAstronaut::WriteToSaveGame( idFile *savefile ) {
    502 	SSDMover::WriteToSaveGame(savefile);
    503 
    504 	savefile->Write(&health, sizeof(health));
    505 }
    506 
    507 void SSDAstronaut::ReadFromSaveGame( idFile *savefile,  idGameSSDWindow* _game  ) {
    508 	SSDMover::ReadFromSaveGame(savefile, _game);
    509 
    510 	savefile->Read(&health, sizeof(health));
    511 }
    512 
    513 void SSDAstronaut::Init(idGameSSDWindow* _game, const idVec3& startPosition, float _speed, float rotate, int _health) {
    514 
    515 	EntityInit();
    516 	MoverInit(idVec3(0,0, -_speed), rotate);
    517 
    518 	SetGame(_game);
    519 
    520 	type = SSD_ENTITY_ASTRONAUT;
    521 
    522 	SetMaterial(ASTRONAUT_MATERIAL);
    523 	SetSize(idVec2(256,256));
    524 	SetRadius(Max(size.x, size.y), 0.3f);
    525 	SetRotation(game->random.RandomInt(360));
    526 	
    527 	position = startPosition;
    528 	health = _health;
    529 }
    530 
    531 SSDAstronaut* SSDAstronaut::GetNewAstronaut(idGameSSDWindow* _game, const idVec3& startPosition, float _speed, float rotate, int _health) {
    532 	for(int i = 0; i < MAX_ASTRONAUT; i++) {
    533 		if(!astronautPool[i].inUse) {
    534 			astronautPool[i].Init(_game, startPosition, _speed, rotate, _health);
    535 			astronautPool[i].inUse = true;
    536 			astronautPool[i].id = i;
    537 			return &astronautPool[i];
    538 		}
    539 	}
    540 	return NULL;
    541 }
    542 
    543 SSDAstronaut* SSDAstronaut::GetSpecificAstronaut(int id) {
    544 	return &astronautPool[id];
    545 	
    546 }
    547 
    548 void SSDAstronaut::WriteAstronauts(idFile* savefile) {
    549 	int count = 0;
    550 	for(int i = 0; i < MAX_ASTRONAUT; i++) {
    551 		if(astronautPool[i].inUse) {
    552 			count++;
    553 		}
    554 	}
    555 	savefile->Write(&count, sizeof(count));
    556 	for(int i = 0; i < MAX_ASTRONAUT; i++) {
    557 		if(astronautPool[i].inUse) {
    558 			savefile->Write(&(astronautPool[i].id), sizeof(astronautPool[i].id));
    559 			astronautPool[i].WriteToSaveGame(savefile);
    560 		}
    561 	}
    562 }
    563 
    564 void SSDAstronaut::ReadAstronauts(idFile* savefile, idGameSSDWindow* _game) {
    565 
    566 	int count;
    567 	savefile->Read(&count, sizeof(count));
    568 	for(int i = 0; i < count; i++) {
    569 		int id;
    570 		savefile->Read(&id, sizeof(id));
    571 		SSDAstronaut* ent = GetSpecificAstronaut(id);
    572 		ent->ReadFromSaveGame(savefile, _game);
    573 	}
    574 }
    575 
    576 /*
    577 *****************************************************************************
    578 * SSDExplosion	
    579 ****************************************************************************
    580 */
    581 
    582 SSDExplosion SSDExplosion::explosionPool[MAX_EXPLOSIONS];
    583 
    584 
    585 //#define EXPLOSION_MATERIAL "game/SSD/fball"
    586 //#define EXPLOSION_TELEPORT "game/SSD/teleport"
    587 
    588 const char* explosionMaterials[] = {
    589 	"game/SSD/fball",
    590 	"game/SSD/teleport"
    591 };
    592 
    593 #define EXPLOSION_MATERIAL_COUNT 2
    594 
    595 SSDExplosion::SSDExplosion() {
    596 	type = SSD_ENTITY_EXPLOSION;
    597 }
    598 
    599 SSDExplosion::~SSDExplosion() {
    600 }
    601 
    602 void SSDExplosion::WriteToSaveGame( idFile *savefile ) {
    603 	SSDEntity::WriteToSaveGame(savefile);
    604 
    605 	savefile->Write(&finalSize, sizeof(finalSize));
    606 	savefile->Write(&length, sizeof(length));
    607 	savefile->Write(&beginTime, sizeof(beginTime));
    608 	savefile->Write(&endTime, sizeof(endTime));
    609 	savefile->Write(&explosionType, sizeof(explosionType));
    610 
    611 	
    612 	savefile->Write(&(buddy->type), sizeof(buddy->type));
    613 	savefile->Write(&(buddy->id), sizeof(buddy->id));
    614 
    615 	savefile->Write(&killBuddy, sizeof(killBuddy));
    616 	savefile->Write(&followBuddy, sizeof(followBuddy));
    617 }
    618 
    619 void SSDExplosion::ReadFromSaveGame( idFile *savefile,  idGameSSDWindow* _game  ) {
    620 	SSDEntity::ReadFromSaveGame(savefile, _game);
    621 
    622 	savefile->Read(&finalSize, sizeof(finalSize));
    623 	savefile->Read(&length, sizeof(length));
    624 	savefile->Read(&beginTime, sizeof(beginTime));
    625 	savefile->Read(&endTime, sizeof(endTime));
    626 	savefile->Read(&explosionType, sizeof(explosionType));
    627 
    628 	int type, id;
    629 	savefile->Read(&type, sizeof(type));
    630 	savefile->Read(&id, sizeof(id));
    631 
    632 	//Get a pointer to my buddy
    633 	buddy = _game->GetSpecificEntity(type, id);
    634 
    635 	savefile->Read(&killBuddy, sizeof(killBuddy));
    636 	savefile->Read(&followBuddy, sizeof(followBuddy));
    637 }
    638 
    639 void SSDExplosion::Init(idGameSSDWindow* _game, const idVec3& _position, const idVec2& _size, int _length, int _type, SSDEntity* _buddy, bool _killBuddy, bool _followBuddy) {
    640 	
    641 	EntityInit();
    642 
    643 	SetGame(_game);
    644 
    645 	type = SSD_ENTITY_EXPLOSION;
    646 	explosionType = _type;
    647 
    648 	SetMaterial(explosionMaterials[explosionType]);
    649 	SetPosition(_position);
    650 	position.z -= 50;
    651 
    652 	finalSize = _size;	
    653 	length = _length;
    654 	beginTime = game->ssdTime;
    655 	endTime = beginTime + length;
    656 
    657 	buddy = _buddy;
    658 	killBuddy = _killBuddy;
    659 	followBuddy = _followBuddy;
    660 
    661 	//Explosion Starts from nothing and will increase in size until it gets to final size
    662 	size.Zero();
    663 
    664 	noPlayerDamage = true;
    665 	noHit = true;
    666 }
    667 
    668 void SSDExplosion::EntityUpdate() {
    669 	
    670 	SSDEntity::EntityUpdate();
    671 
    672 	//Always set my position to my buddies position except change z to be on top
    673 	if(followBuddy) {
    674 		position = buddy->position;
    675 		position.z -= 50;
    676 	} else {
    677 		//Only mess with the z if we are not following
    678 		position.z = buddy->position.z - 50;
    679 	}
    680 
    681 	//Scale the image based on the time
    682 	size = finalSize*((float)(currentTime-beginTime)/(float)length);
    683 
    684 	//Destroy myself after the explosion is done
    685 	if(currentTime > endTime) {
    686 		destroyed = true;
    687 		
    688 		if(killBuddy) {
    689 			//Destroy the exploding object
    690 			buddy->destroyed = true;
    691 		}
    692 	}
    693 }
    694 
    695 SSDExplosion* SSDExplosion::GetNewExplosion(idGameSSDWindow* _game, const idVec3& _position, const idVec2& _size, int _length, int _type, SSDEntity* _buddy, bool _killBuddy, bool _followBuddy) {
    696 	for(int i = 0; i < MAX_EXPLOSIONS; i++) {
    697 		if(!explosionPool[i].inUse) {
    698 			explosionPool[i].Init(_game, _position, _size, _length, _type, _buddy, _killBuddy, _followBuddy);
    699 			explosionPool[i].inUse = true;
    700 			return &explosionPool[i];
    701 		}
    702 	}
    703 	return NULL;
    704 }
    705 
    706 SSDExplosion* SSDExplosion::GetSpecificExplosion(int id) {
    707 	return &explosionPool[id];
    708 }
    709 
    710 void SSDExplosion::WriteExplosions(idFile* savefile) {
    711 	int count = 0;
    712 	for(int i = 0; i < MAX_EXPLOSIONS; i++) {
    713 		if(explosionPool[i].inUse) {
    714 			count++;
    715 		}
    716 	}
    717 	savefile->Write(&count, sizeof(count));
    718 	for(int i = 0; i < MAX_EXPLOSIONS; i++) {
    719 		if(explosionPool[i].inUse) {
    720 			savefile->Write(&(explosionPool[i].id), sizeof(explosionPool[i].id));
    721 			explosionPool[i].WriteToSaveGame(savefile);
    722 		}
    723 	}
    724 }
    725 
    726 void SSDExplosion::ReadExplosions(idFile* savefile, idGameSSDWindow* _game) {
    727 
    728 	int count;
    729 	savefile->Read(&count, sizeof(count));
    730 	for(int i = 0; i < count; i++) {
    731 		int id;
    732 		savefile->Read(&id, sizeof(id));
    733 		SSDExplosion* ent = GetSpecificExplosion(id);
    734 		ent->ReadFromSaveGame(savefile, _game);
    735 	}
    736 }
    737 
    738 /*
    739 *****************************************************************************
    740 * SSDPoints
    741 ****************************************************************************
    742 */
    743 
    744 SSDPoints	SSDPoints::pointsPool[MAX_POINTS];
    745 
    746 SSDPoints::SSDPoints() {
    747 	type = SSD_ENTITY_POINTS;
    748 }
    749 
    750 SSDPoints::~SSDPoints() {
    751 }
    752 
    753 void SSDPoints::WriteToSaveGame( idFile *savefile ) {
    754 	SSDEntity::WriteToSaveGame(savefile);
    755 
    756 	savefile->Write(&length, sizeof(length));
    757 	savefile->Write(&distance, sizeof(distance));
    758 	savefile->Write(&beginTime, sizeof(beginTime));
    759 	savefile->Write(&endTime, sizeof(endTime));
    760 
    761 	savefile->Write(&beginPosition, sizeof(beginPosition));
    762 	savefile->Write(&endPosition, sizeof(endPosition));
    763 
    764 	savefile->Write(&beginColor, sizeof(beginColor));
    765 	savefile->Write(&endColor, sizeof(endColor));
    766 	
    767 }
    768 
    769 void SSDPoints::ReadFromSaveGame( idFile *savefile,  idGameSSDWindow* _game  ) {
    770 	SSDEntity::ReadFromSaveGame(savefile, _game);
    771 
    772 	savefile->Read(&length, sizeof(length));
    773 	savefile->Read(&distance, sizeof(distance));
    774 	savefile->Read(&beginTime, sizeof(beginTime));
    775 	savefile->Read(&endTime, sizeof(endTime));
    776 
    777 	savefile->Read(&beginPosition, sizeof(beginPosition));
    778 	savefile->Read(&endPosition, sizeof(endPosition));
    779 
    780 	savefile->Read(&beginColor, sizeof(beginColor));
    781 	savefile->Read(&endColor, sizeof(endColor));
    782 }
    783 
    784 void SSDPoints::Init(idGameSSDWindow* _game, SSDEntity* _ent, int _points, int _length, int _distance, const idVec4& color) {
    785 
    786 	EntityInit();
    787 	
    788 	SetGame(_game);
    789 
    790 	length = _length;
    791 	distance = _distance;
    792 	beginTime = game->ssdTime;
    793 	endTime = beginTime + length;
    794 
    795 	textScale = 0.4f;
    796 	text = va("%d", _points);
    797 
    798 	float width = 0;
    799 	for(int i = 0; i < text.Length(); i++) {
    800 		width += dc->CharWidth(text[i], textScale);
    801 	}
    802 
    803 	size.Set(0,0);
    804 
    805 	//Set the start position at the top of the passed in entity
    806 	position = WorldToScreen(_ent->position);
    807 	position = ScreenToWorld(position);
    808 
    809 	position.z = 0;
    810 	position.x -= (width/2.0f);
    811 
    812 	beginPosition = position;
    813 
    814 	endPosition = beginPosition;
    815 	endPosition.y += _distance;
    816 
    817 	//beginColor.Set(0,1,0,1);
    818 	endColor.Set(1,1,1,0);
    819 	
    820 	beginColor = color;
    821 	beginColor.w = 1;
    822 
    823 	noPlayerDamage = true;
    824 	noHit = true;
    825 }
    826 
    827 void SSDPoints::EntityUpdate() {
    828 
    829 	float t = (float)(currentTime - beginTime)/(float)length;
    830 
    831 	//Move up from the start position
    832 	position.Lerp(beginPosition, endPosition, t);
    833 
    834 	//Interpolate the color
    835 	foreColor.Lerp(beginColor, endColor, t);
    836 
    837 	if(currentTime > endTime) {
    838 		destroyed = true;
    839 	}
    840 }
    841 
    842 SSDPoints* SSDPoints::GetNewPoints(idGameSSDWindow* _game, SSDEntity* _ent, int _points, int _length, int _distance, const idVec4& color) {
    843 	for(int i = 0; i < MAX_POINTS; i++) {
    844 		if(!pointsPool[i].inUse) {
    845 			pointsPool[i].Init(_game, _ent, _points, _length, _distance, color);
    846 			pointsPool[i].inUse = true;
    847 			return &pointsPool[i];
    848 		}
    849 	}
    850 	return NULL;
    851 }
    852 
    853 SSDPoints* SSDPoints::GetSpecificPoints(int id) {
    854 	return &pointsPool[id];
    855 }
    856 
    857 void SSDPoints::WritePoints(idFile* savefile) {
    858 	int count = 0;
    859 	for(int i = 0; i < MAX_POINTS; i++) {
    860 		if(pointsPool[i].inUse) {
    861 			count++;
    862 		}
    863 	}
    864 	savefile->Write(&count, sizeof(count));
    865 	for(int i = 0; i < MAX_POINTS; i++) {
    866 		if(pointsPool[i].inUse) {
    867 			savefile->Write(&(pointsPool[i].id), sizeof(pointsPool[i].id));
    868 			pointsPool[i].WriteToSaveGame(savefile);
    869 		}
    870 	}
    871 }
    872 
    873 void SSDPoints::ReadPoints(idFile* savefile, idGameSSDWindow* _game) {
    874 
    875 	int count;
    876 	savefile->Read(&count, sizeof(count));
    877 	for(int i = 0; i < count; i++) {
    878 		int id;
    879 		savefile->Read(&id, sizeof(id));
    880 		SSDPoints* ent = GetSpecificPoints(id);
    881 		ent->ReadFromSaveGame(savefile, _game);
    882 	}
    883 }
    884 
    885 /*
    886 *****************************************************************************
    887 * SSDProjectile
    888 ****************************************************************************
    889 */
    890 
    891 SSDProjectile SSDProjectile::projectilePool[MAX_PROJECTILES];
    892 
    893 #define PROJECTILE_MATERIAL "game/SSD/fball"
    894 
    895 SSDProjectile::SSDProjectile() {
    896 	type = SSD_ENTITY_PROJECTILE;
    897 }
    898 
    899 SSDProjectile::~SSDProjectile() {
    900 }
    901 
    902 void SSDProjectile::WriteToSaveGame( idFile *savefile ) {
    903 	SSDEntity::WriteToSaveGame(savefile);
    904 
    905 	savefile->Write(&dir, sizeof(dir));
    906 	savefile->Write(&speed, sizeof(speed));
    907 	savefile->Write(&beginTime, sizeof(beginTime));
    908 	savefile->Write(&endTime, sizeof(endTime));
    909 
    910 	savefile->Write(&endPosition, sizeof(endPosition));
    911 }
    912 
    913 void SSDProjectile::ReadFromSaveGame( idFile *savefile,  idGameSSDWindow* _game  ) {
    914 	SSDEntity::ReadFromSaveGame(savefile, _game);
    915 
    916 	savefile->Read(&dir, sizeof(dir));
    917 	savefile->Read(&speed, sizeof(speed));
    918 	savefile->Read(&beginTime, sizeof(beginTime));
    919 	savefile->Read(&endTime, sizeof(endTime));
    920 
    921 	savefile->Read(&endPosition, sizeof(endPosition));
    922 }
    923 
    924 void SSDProjectile::Init(idGameSSDWindow* _game, const idVec3& _beginPosition, const idVec3& _endPosition, float _speed, float _size) {
    925 	
    926 	EntityInit();
    927 
    928 	SetGame(_game);
    929 
    930 	SetMaterial(PROJECTILE_MATERIAL);
    931 	size.Set(_size,_size);
    932 
    933 	position = _beginPosition;
    934 	endPosition = _endPosition;
    935 
    936 	dir = _endPosition - position;
    937 	dir.Normalize();
    938 
    939 	//speed.Zero();
    940 	speed.x = speed.y = speed.z = _speed;
    941 
    942 	noHit = true;
    943 }
    944 
    945 void SSDProjectile::EntityUpdate() {
    946 
    947 	SSDEntity::EntityUpdate();
    948 
    949 	//Move forward based on speed (units per second)
    950 	idVec3 moved = dir*((float)elapsed/1000.0f)*speed.z;
    951 	position += moved;
    952 
    953 	if(position.z > endPosition.z) {
    954 		//We have reached our position
    955 		destroyed = true;
    956 	}
    957 }
    958 
    959 SSDProjectile* SSDProjectile::GetNewProjectile(idGameSSDWindow* _game, const idVec3& _beginPosition, const idVec3& _endPosition, float _speed, float _size) {
    960 	for(int i = 0; i < MAX_PROJECTILES; i++) {
    961 		if(!projectilePool[i].inUse) {
    962 			projectilePool[i].Init(_game, _beginPosition, _endPosition, _speed, _size);
    963 			projectilePool[i].inUse = true;
    964 			return &projectilePool[i];
    965 		}
    966 	}
    967 	return NULL;
    968 }
    969 
    970 SSDProjectile* SSDProjectile::GetSpecificProjectile(int id) {
    971 	return &projectilePool[id];
    972 }
    973 
    974 void SSDProjectile::WriteProjectiles(idFile* savefile) {
    975 	int count = 0;
    976 	for(int i = 0; i < MAX_PROJECTILES; i++) {
    977 		if(projectilePool[i].inUse) {
    978 			count++;
    979 		}
    980 	}
    981 	savefile->Write(&count, sizeof(count));
    982 	for(int i = 0; i < MAX_PROJECTILES; i++) {
    983 		if(projectilePool[i].inUse) {
    984 			savefile->Write(&(projectilePool[i].id), sizeof(projectilePool[i].id));
    985 			projectilePool[i].WriteToSaveGame(savefile);
    986 		}
    987 	}
    988 }
    989 
    990 void SSDProjectile::ReadProjectiles(idFile* savefile, idGameSSDWindow* _game) {
    991 
    992 	int count;
    993 	savefile->Read(&count, sizeof(count));
    994 	for(int i = 0; i < count; i++) {
    995 		int id;
    996 		savefile->Read(&id, sizeof(id));
    997 		SSDProjectile* ent = GetSpecificProjectile(id);
    998 		ent->ReadFromSaveGame(savefile, _game);
    999 	}
   1000 }
   1001 
   1002 /*
   1003 *****************************************************************************
   1004 * SSDPowerup
   1005 ****************************************************************************
   1006 */
   1007 
   1008 const char* powerupMaterials[][2] = {
   1009 	"game/SSD/powerupHealthClosed",			"game/SSD/powerupHealthOpen",
   1010 	"game/SSD/powerupSuperBlasterClosed",	"game/SSD/powerupSuperBlasterOpen",
   1011 	"game/SSD/powerupNukeClosed",			"game/SSD/powerupNukeOpen",
   1012 	"game/SSD/powerupRescueClosed",			"game/SSD/powerupRescueOpen",
   1013 	"game/SSD/powerupBonusPointsClosed",	"game/SSD/powerupBonusPointsOpen",
   1014 	"game/SSD/powerupDamageClosed",			"game/SSD/powerupDamageOpen",
   1015 };
   1016 
   1017 #define POWERUP_MATERIAL_COUNT 6
   1018 
   1019 SSDPowerup	SSDPowerup::powerupPool[MAX_POWERUPS];
   1020 
   1021 SSDPowerup::SSDPowerup() {
   1022 	
   1023 }
   1024 
   1025 SSDPowerup::~SSDPowerup() {
   1026 }
   1027 
   1028 void SSDPowerup::WriteToSaveGame( idFile *savefile ) {
   1029 	SSDMover::WriteToSaveGame(savefile);
   1030 
   1031 	savefile->Write(&powerupState, sizeof(powerupState));
   1032 	savefile->Write(&powerupType, sizeof(powerupType));
   1033 }
   1034 
   1035 void SSDPowerup::ReadFromSaveGame( idFile *savefile,  idGameSSDWindow* _game  ) {
   1036 	SSDMover::ReadFromSaveGame(savefile, _game);
   1037 
   1038 	savefile->Read(&powerupState, sizeof(powerupState));
   1039 	savefile->Read(&powerupType, sizeof(powerupType));
   1040 }
   1041 
   1042 void SSDPowerup::OnHit(int key) {
   1043 
   1044 	if(powerupState == POWERUP_STATE_CLOSED) {
   1045 
   1046 		//Small explosion to indicate it is opened
   1047 		SSDExplosion* explosion = SSDExplosion::GetNewExplosion(game, position, size*2.0f, 300, SSDExplosion::EXPLOSION_NORMAL, this, false, true);
   1048 		game->entities.Append(explosion);
   1049 		
   1050 
   1051 		powerupState = POWERUP_STATE_OPEN;
   1052 		SetMaterial(powerupMaterials[powerupType][powerupState]);
   1053 	} else {
   1054 		//Destory the powerup with a big explosion
   1055 		SSDExplosion* explosion = SSDExplosion::GetNewExplosion(game, position, size*2, 300, SSDExplosion::EXPLOSION_NORMAL, this);
   1056 		game->entities.Append(explosion);
   1057 		game->PlaySound("arcade_explode");
   1058 
   1059 		noHit = true;
   1060 		noPlayerDamage = true;
   1061 	}
   1062 }
   1063 
   1064 void SSDPowerup::OnStrikePlayer() {
   1065 	
   1066 	if(powerupState == POWERUP_STATE_OPEN) {
   1067 		//The powerup was open so activate it
   1068 		OnActivatePowerup();
   1069 	}
   1070 
   1071 	//Just destroy the powerup
   1072 	destroyed = true;
   1073 }
   1074 
   1075 void SSDPowerup::OnOpenPowerup() {
   1076 }
   1077 
   1078 void SSDPowerup::OnActivatePowerup() {
   1079 	switch(powerupType) {
   1080 		case POWERUP_TYPE_HEALTH:
   1081 			{
   1082 				game->AddHealth(10);
   1083 				break;
   1084 			}
   1085 		case POWERUP_TYPE_SUPER_BLASTER:
   1086 			{
   1087 				game->OnSuperBlaster();
   1088 				break;
   1089 			}
   1090 		case POWERUP_TYPE_ASTEROID_NUKE:
   1091 			{
   1092 				game->OnNuke();
   1093 				break;
   1094 			}
   1095 		case POWERUP_TYPE_RESCUE_ALL:
   1096 			{
   1097 				game->OnRescueAll();
   1098 				break;
   1099 			}
   1100 		case POWERUP_TYPE_BONUS_POINTS:
   1101 			{
   1102 				int points = (game->random.RandomInt(5)+1) * 100;
   1103 				game->AddScore(this, points);
   1104 				break;
   1105 			}
   1106 		case POWERUP_TYPE_DAMAGE:
   1107 			{
   1108 				game->AddDamage(10);
   1109 				game->PlaySound("arcade_explode");
   1110 				break;
   1111 			}
   1112 
   1113 	}
   1114 }
   1115 
   1116 
   1117 void SSDPowerup::Init(idGameSSDWindow* _game, float _speed, float _rotation) {
   1118 
   1119 	EntityInit();
   1120 	MoverInit(idVec3(0,0, -_speed), _rotation);
   1121 
   1122 	SetGame(_game);
   1123 	SetSize(idVec2(200,200));
   1124 	SetRadius(Max(size.x, size.y), 0.3f);
   1125 
   1126 	type = SSD_ENTITY_POWERUP;
   1127 	
   1128 	idVec3 startPosition;
   1129 	startPosition.x = game->random.RandomInt(V_WIDTH)-(V_WIDTH/2.0f);
   1130 	startPosition.y = game->random.RandomInt(V_HEIGHT)-(V_HEIGHT/2.0f);
   1131 	startPosition.z = ENTITY_START_DIST;
   1132 
   1133 	position = startPosition;
   1134 	//SetPosition(startPosition);
   1135 
   1136 	powerupState = POWERUP_STATE_CLOSED;
   1137 	powerupType = game->random.RandomInt(POWERUP_TYPE_MAX+1);
   1138 	if(powerupType >= POWERUP_TYPE_MAX) {
   1139 		powerupType = 0;
   1140 	}
   1141 
   1142 	/*OutputDebugString(va("Powerup: %d\n", powerupType));
   1143 	if(powerupType == 0) {
   1144 		int x = 0;
   1145 	}*/
   1146 
   1147 	SetMaterial(powerupMaterials[powerupType][powerupState]);
   1148 }
   1149 
   1150 SSDPowerup* SSDPowerup::GetNewPowerup(idGameSSDWindow* _game, float _speed, float _rotation) {
   1151 
   1152 	for(int i = 0; i < MAX_POWERUPS; i++) {
   1153 		if(!powerupPool[i].inUse) {
   1154 			powerupPool[i].Init(_game, _speed, _rotation);
   1155 			powerupPool[i].inUse = true;
   1156 			return &powerupPool[i];
   1157 		}
   1158 	}
   1159 	return NULL;
   1160 }
   1161 
   1162 SSDPowerup* SSDPowerup::GetSpecificPowerup(int id) {
   1163 	return &powerupPool[id];
   1164 }
   1165 
   1166 void SSDPowerup::WritePowerups(idFile* savefile) {
   1167 	int count = 0;
   1168 	for(int i = 0; i < MAX_POWERUPS; i++) {
   1169 		if(powerupPool[i].inUse) {
   1170 			count++;
   1171 		}
   1172 	}
   1173 	savefile->Write(&count, sizeof(count));
   1174 	for(int i = 0; i < MAX_POWERUPS; i++) {
   1175 		if(powerupPool[i].inUse) {
   1176 			savefile->Write(&(powerupPool[i].id), sizeof(powerupPool[i].id));
   1177 			powerupPool[i].WriteToSaveGame(savefile);
   1178 		}
   1179 	}
   1180 }
   1181 
   1182 void SSDPowerup::ReadPowerups(idFile* savefile, idGameSSDWindow* _game) {
   1183 
   1184 	int count;
   1185 	savefile->Read(&count, sizeof(count));
   1186 	for(int i = 0; i < count; i++) {
   1187 		int id;
   1188 		savefile->Read(&id, sizeof(id));
   1189 		SSDPowerup* ent = GetSpecificPowerup(id);
   1190 		ent->ReadFromSaveGame(savefile, _game);
   1191 	}
   1192 }
   1193 
   1194 /*
   1195 *****************************************************************************
   1196 * idGameSSDWindow
   1197 ****************************************************************************
   1198 */
   1199 
   1200 idRandom idGameSSDWindow::random;
   1201 
   1202 idGameSSDWindow::idGameSSDWindow(idUserInterfaceLocal *g) : idWindow(g) {
   1203 	gui = g;
   1204 	CommonInit();
   1205 }
   1206 
   1207 idGameSSDWindow::~idGameSSDWindow() {
   1208 	ResetGameStats();
   1209 }
   1210 
   1211 void idGameSSDWindow::WriteToSaveGame( idFile *savefile ) {
   1212 	idWindow::WriteToSaveGame(savefile);
   1213 
   1214 	savefile->Write(&ssdTime, sizeof(ssdTime));
   1215 
   1216 	beginLevel.WriteToSaveGame(savefile);
   1217 	resetGame.WriteToSaveGame(savefile);
   1218 	continueGame.WriteToSaveGame(savefile);
   1219 	refreshGuiData.WriteToSaveGame(savefile);
   1220 
   1221 	crosshair.WriteToSaveGame(savefile);
   1222 	savefile->Write(&screenBounds, sizeof(screenBounds));
   1223 
   1224 	savefile->Write(&levelCount, sizeof(levelCount));
   1225 	for(int i = 0; i < levelCount; i++) {
   1226 		savefile->Write(&(levelData[i]), sizeof(SSDLevelData_t));
   1227 		savefile->Write(&(asteroidData[i]), sizeof(SSDAsteroidData_t));
   1228 		savefile->Write(&(astronautData[i]), sizeof(SSDAstronautData_t));
   1229 		savefile->Write(&(powerupData[i]), sizeof(SSDPowerupData_t));
   1230 	}
   1231 
   1232 	savefile->Write(&weaponCount, sizeof(weaponCount));
   1233 	for(int i = 0; i < weaponCount; i++) {
   1234 		savefile->Write(&(weaponData[i]), sizeof(SSDWeaponData_t));
   1235 	}
   1236 
   1237 	savefile->Write(&superBlasterTimeout, sizeof(superBlasterTimeout));
   1238 	savefile->Write(&gameStats, sizeof(SSDGameStats_t));
   1239 
   1240 	//Write All Static Entities
   1241 	SSDAsteroid::WriteAsteroids(savefile);
   1242 	SSDAstronaut::WriteAstronauts(savefile);
   1243 	SSDExplosion::WriteExplosions(savefile);
   1244 	SSDPoints::WritePoints(savefile);
   1245 	SSDProjectile::WriteProjectiles(savefile);
   1246 	SSDPowerup::WritePowerups(savefile);
   1247 
   1248 	int entCount = entities.Num();
   1249 	savefile->Write(&entCount, sizeof(entCount));
   1250 	for(int i = 0; i < entCount; i++) {
   1251 		savefile->Write(&(entities[i]->type), sizeof(entities[i]->type));
   1252 		savefile->Write(&(entities[i]->id), sizeof(entities[i]->id));
   1253 	}
   1254 }
   1255 
   1256 void idGameSSDWindow::ReadFromSaveGame( idFile *savefile ) {
   1257 	idWindow::ReadFromSaveGame(savefile);
   1258 
   1259 
   1260 	savefile->Read(&ssdTime, sizeof(ssdTime));
   1261 
   1262 	beginLevel.ReadFromSaveGame(savefile);
   1263 	resetGame.ReadFromSaveGame(savefile);
   1264 	continueGame.ReadFromSaveGame(savefile);
   1265 	refreshGuiData.ReadFromSaveGame(savefile);
   1266 
   1267 	crosshair.ReadFromSaveGame(savefile);
   1268 	savefile->Read(&screenBounds, sizeof(screenBounds));
   1269 
   1270 	savefile->Read(&levelCount, sizeof(levelCount));
   1271 	for(int i = 0; i < levelCount; i++) {
   1272 		SSDLevelData_t newLevel;
   1273 		savefile->Read(&newLevel, sizeof(SSDLevelData_t));
   1274 		levelData.Append(newLevel);
   1275 
   1276 		SSDAsteroidData_t newAsteroid;
   1277 		savefile->Read(&newAsteroid, sizeof(SSDAsteroidData_t));
   1278 		asteroidData.Append(newAsteroid);
   1279 
   1280 		SSDAstronautData_t newAstronaut;
   1281 		savefile->Read(&newAstronaut, sizeof(SSDAstronautData_t));
   1282 		astronautData.Append(newAstronaut);
   1283 		
   1284 		SSDPowerupData_t newPowerup;
   1285 		savefile->Read(&newPowerup, sizeof(SSDPowerupData_t));
   1286 		powerupData.Append(newPowerup);
   1287 	}
   1288 
   1289 	savefile->Read(&weaponCount, sizeof(weaponCount));
   1290 	for(int i = 0; i < weaponCount; i++) {
   1291 		SSDWeaponData_t newWeapon;
   1292 		savefile->Read(&newWeapon, sizeof(SSDWeaponData_t));
   1293 		weaponData.Append(newWeapon);
   1294 	}
   1295 
   1296 	savefile->Read(&superBlasterTimeout, sizeof(superBlasterTimeout));
   1297 	
   1298 	savefile->Read(&gameStats, sizeof(SSDGameStats_t));
   1299 	//Reset this because it is no longer valid
   1300 	gameStats.levelStats.targetEnt = NULL;
   1301 
   1302 	SSDAsteroid::ReadAsteroids(savefile, this);
   1303 	SSDAstronaut::ReadAstronauts(savefile, this);
   1304 	SSDExplosion::ReadExplosions(savefile, this);
   1305 	SSDPoints::ReadPoints(savefile, this);
   1306 	SSDProjectile::ReadProjectiles(savefile, this);
   1307 	SSDPowerup::ReadPowerups(savefile, this);
   1308 
   1309 	int entCount;
   1310 	savefile->Read(&entCount, sizeof(entCount));
   1311 
   1312 	for(int i = 0; i < entCount; i++) {
   1313 		int type, id;
   1314 		savefile->Read(&type, sizeof(type));
   1315 		savefile->Read(&id, sizeof(id));
   1316 
   1317 		SSDEntity* ent = GetSpecificEntity(type, id);
   1318 		if(ent) {
   1319 			entities.Append(ent);
   1320 		}
   1321 	}
   1322 }
   1323 
   1324 const char *idGameSSDWindow::HandleEvent(const sysEvent_t *event, bool *updateVisuals) {
   1325 	
   1326 	// need to call this to allow proper focus and capturing on embedded children
   1327 	const char *ret = idWindow::HandleEvent(event, updateVisuals);
   1328 
   1329 	if(!gameStats.gameRunning) {
   1330 		return ret;
   1331 	}
   1332 
   1333 	int key = event->evValue;
   1334 
   1335 	if ( event->evType == SE_KEY ) {
   1336 
   1337 		if ( !event->evValue2 ) {
   1338 			return ret;
   1339 		}
   1340 
   1341 		if ( key == K_MOUSE1 || key == K_MOUSE2) {
   1342 			FireWeapon(key);	
   1343 		} else {
   1344 			return ret;
   1345 		}
   1346 	}
   1347 	return ret;
   1348 }
   1349 
   1350 idWinVar *idGameSSDWindow::GetWinVarByName	(const char *_name, bool winLookup, drawWin_t** owner) {
   1351 
   1352 	idWinVar *retVar = NULL;
   1353 
   1354 	if (idStr::Icmp(_name, "beginLevel") == 0) {
   1355 		retVar = &beginLevel;
   1356 	}
   1357 
   1358 	if (idStr::Icmp(_name, "resetGame") == 0) {
   1359 		retVar = &resetGame;
   1360 	}
   1361 
   1362 	if (idStr::Icmp(_name, "continueGame") == 0) {
   1363 		retVar = &continueGame;
   1364 	}
   1365 	if (idStr::Icmp(_name, "refreshGuiData") == 0) {
   1366 		retVar = &refreshGuiData;
   1367 	}
   1368 	
   1369 
   1370 	if(retVar) {
   1371 		return retVar;
   1372 	}
   1373 
   1374 	return idWindow::GetWinVarByName(_name, winLookup, owner);
   1375 }
   1376 
   1377 
   1378 void idGameSSDWindow::Draw(int time, float x, float y) {
   1379 
   1380 	//Update the game every frame before drawing
   1381 	UpdateGame();
   1382 
   1383 	RefreshGuiData();
   1384 
   1385 	if(gameStats.gameRunning) {
   1386 
   1387 		ZOrderEntities();
   1388 
   1389 		//Draw from back to front
   1390 		for(int i = entities.Num()-1; i >= 0; i--) {
   1391 			entities[i]->Draw();
   1392 		}
   1393 
   1394 		//The last thing to draw is the crosshair
   1395 		idVec2 cursor;
   1396 		//GetCursor(cursor);
   1397 		cursor.x = gui->CursorX();
   1398 		cursor.y = gui->CursorY();
   1399 
   1400 		crosshair.Draw(cursor);
   1401 	}
   1402 }
   1403 
   1404 
   1405 bool idGameSSDWindow::ParseInternalVar(const char *_name, idTokenParser *src) {
   1406 
   1407 	if (idStr::Icmp(_name, "beginLevel") == 0) {
   1408 		beginLevel = src->ParseBool();
   1409 		return true;
   1410 	}
   1411 	if (idStr::Icmp(_name, "resetGame") == 0) {
   1412 		resetGame = src->ParseBool();
   1413 		return true;
   1414 	}
   1415 	if (idStr::Icmp(_name, "continueGame") == 0) {
   1416 		continueGame = src->ParseBool();
   1417 		return true;
   1418 	}
   1419 	if (idStr::Icmp(_name, "refreshGuiData") == 0) {
   1420 		refreshGuiData = src->ParseBool();
   1421 		return true;
   1422 	}
   1423 	
   1424 	if(idStr::Icmp(_name, "levelcount") == 0) {
   1425 		levelCount = src->ParseInt();
   1426 		for(int i = 0; i < levelCount; i++) {
   1427 			SSDLevelData_t newLevel;
   1428 			memset(&newLevel, 0, sizeof(SSDLevelData_t));
   1429 			levelData.Append(newLevel);
   1430 
   1431 			SSDAsteroidData_t newAsteroid;
   1432 			memset(&newAsteroid, 0, sizeof(SSDAsteroidData_t));
   1433 			asteroidData.Append(newAsteroid);
   1434 
   1435 			SSDAstronautData_t newAstronaut;
   1436 			memset(&newAstronaut, 0, sizeof(SSDAstronautData_t));
   1437 			astronautData.Append(newAstronaut);
   1438 
   1439 			SSDPowerupData_t newPowerup;
   1440 			memset(&newPowerup, 0, sizeof(SSDPowerupData_t));
   1441 			powerupData.Append(newPowerup);
   1442 
   1443 
   1444 		}
   1445 		return true;
   1446 	}
   1447 	if(idStr::Icmp(_name, "weaponCount") == 0) {
   1448 		weaponCount = src->ParseInt();
   1449 		for(int i = 0; i < weaponCount; i++) {
   1450 			SSDWeaponData_t newWeapon;
   1451 			memset(&newWeapon, 0, sizeof(SSDWeaponData_t));
   1452 			weaponData.Append(newWeapon);
   1453 		}
   1454 		return true;
   1455 	}
   1456 
   1457 	if(idStr::FindText(_name, "leveldata", false) >= 0) {
   1458 		idStr tempName = _name;
   1459 		int level = atoi(tempName.Right(2))-1;
   1460 
   1461 		idStr levelData;
   1462 		ParseString(src, levelData);
   1463 		ParseLevelData(level, levelData);
   1464 		return true;
   1465 	}
   1466 
   1467 	if(idStr::FindText(_name, "asteroiddata", false) >= 0) {
   1468 		idStr tempName = _name;
   1469 		int level = atoi(tempName.Right(2))-1;
   1470 
   1471 		idStr asteroidData;
   1472 		ParseString(src, asteroidData);
   1473 		ParseAsteroidData(level, asteroidData);
   1474 		return true;
   1475 	}
   1476 
   1477 	if(idStr::FindText(_name, "weapondata", false) >= 0) {
   1478 		idStr tempName = _name;
   1479 		int weapon = atoi(tempName.Right(2))-1;
   1480 
   1481 		idStr weaponData;
   1482 		ParseString(src, weaponData);
   1483 		ParseWeaponData(weapon, weaponData);
   1484 		return true;
   1485 	}
   1486 
   1487 	if(idStr::FindText(_name, "astronautdata", false) >= 0) {
   1488 		idStr tempName = _name;
   1489 		int level = atoi(tempName.Right(2))-1;
   1490 
   1491 		idStr astronautData;
   1492 		ParseString(src, astronautData);
   1493 		ParseAstronautData(level, astronautData);
   1494 		return true;
   1495 	}
   1496 
   1497 	if(idStr::FindText(_name, "powerupdata", false) >= 0) {
   1498 		idStr tempName = _name;
   1499 		int level = atoi(tempName.Right(2))-1;
   1500 
   1501 		idStr powerupData;
   1502 		ParseString(src, powerupData);
   1503 		ParsePowerupData(level, powerupData);
   1504 		return true;
   1505 	}
   1506 
   1507 	return idWindow::ParseInternalVar(_name, src);
   1508 }
   1509 
   1510 void idGameSSDWindow::ParseLevelData(int level, const idStr& levelDataString) {
   1511 
   1512 	idParser parser;
   1513 	idToken token;
   1514 	parser.LoadMemory(levelDataString.c_str(), levelDataString.Length(), "LevelData");
   1515 
   1516 	levelData[level].spawnBuffer = parser.ParseFloat();
   1517 	levelData[level].needToWin = parser.ParseInt(); //Required Destroyed
   1518 
   1519 }
   1520 
   1521 void idGameSSDWindow::ParseAsteroidData(int level, const idStr& asteroidDataString) {
   1522 
   1523 	idParser parser;
   1524 	idToken token;
   1525 	parser.LoadMemory(asteroidDataString.c_str(), asteroidDataString.Length(), "AsteroidData");
   1526 
   1527 	asteroidData[level].speedMin = parser.ParseFloat(); //Speed Min 
   1528 	asteroidData[level].speedMax = parser.ParseFloat(); //Speed Max
   1529 
   1530 	asteroidData[level].sizeMin = parser.ParseFloat(); //Size Min 
   1531 	asteroidData[level].sizeMax = parser.ParseFloat(); //Size Max
   1532 
   1533 	asteroidData[level].rotateMin = parser.ParseFloat(); //Rotate Min (rotations per second) 
   1534 	asteroidData[level].rotateMax = parser.ParseFloat(); //Rotate Max (rotations per second)
   1535 
   1536 	asteroidData[level].spawnMin = parser.ParseInt(); //Spawn Min
   1537 	asteroidData[level].spawnMax = parser.ParseInt(); //Spawn Max
   1538 
   1539 	asteroidData[level].asteroidHealth = parser.ParseInt(); //Health of the asteroid
   1540 	asteroidData[level].asteroidDamage = parser.ParseInt(); //Asteroid Damage
   1541 	asteroidData[level].asteroidPoints = parser.ParseInt(); //Points awarded for destruction
   1542 }
   1543 
   1544 void idGameSSDWindow::ParsePowerupData(int level, const idStr& powerupDataString) {
   1545 	
   1546 	idParser parser;
   1547 	idToken token;
   1548 	parser.LoadMemory(powerupDataString.c_str(), powerupDataString.Length(), "PowerupData");
   1549 
   1550 	powerupData[level].speedMin = parser.ParseFloat(); //Speed Min 
   1551 	powerupData[level].speedMax = parser.ParseFloat(); //Speed Max
   1552 
   1553 	powerupData[level].rotateMin = parser.ParseFloat(); //Rotate Min (rotations per second) 
   1554 	powerupData[level].rotateMax = parser.ParseFloat(); //Rotate Max (rotations per second)
   1555 
   1556 	powerupData[level].spawnMin = parser.ParseInt(); //Spawn Min
   1557 	powerupData[level].spawnMax = parser.ParseInt(); //Spawn Max
   1558 
   1559 }
   1560 
   1561 void idGameSSDWindow::ParseWeaponData(int weapon, const idStr& weaponDataString) {
   1562 
   1563 	idParser parser;
   1564 	idToken token;
   1565 	parser.LoadMemory(weaponDataString.c_str(), weaponDataString.Length(), "WeaponData");
   1566 
   1567 	weaponData[weapon].speed = parser.ParseFloat(); 
   1568 	weaponData[weapon].damage = parser.ParseFloat();
   1569 	weaponData[weapon].size = parser.ParseFloat();
   1570 }
   1571 
   1572 void idGameSSDWindow::ParseAstronautData(int level, const idStr& astronautDataString) {
   1573 	
   1574 	idParser parser;
   1575 	idToken token;
   1576 	parser.LoadMemory(astronautDataString.c_str(), astronautDataString.Length(), "AstronautData");
   1577 
   1578 	astronautData[level].speedMin = parser.ParseFloat(); //Speed Min 
   1579 	astronautData[level].speedMax = parser.ParseFloat(); //Speed Max
   1580 
   1581 	astronautData[level].rotateMin = parser.ParseFloat(); //Rotate Min (rotations per second) 
   1582 	astronautData[level].rotateMax = parser.ParseFloat(); //Rotate Max (rotations per second)
   1583 
   1584 	astronautData[level].spawnMin = parser.ParseInt(); //Spawn Min
   1585 	astronautData[level].spawnMax = parser.ParseInt(); //Spawn Max
   1586 
   1587 	astronautData[level].health = parser.ParseInt(); //Health of the asteroid
   1588 	astronautData[level].points = parser.ParseInt(); //Asteroid Damage
   1589 	astronautData[level].penalty = parser.ParseInt(); //Points awarded for destruction
   1590 }
   1591 
   1592 void idGameSSDWindow::CommonInit() {
   1593 	crosshair.InitCrosshairs();
   1594 
   1595 
   1596 	beginLevel = false;
   1597 	resetGame = false;
   1598 	continueGame = false;
   1599 	refreshGuiData = false;
   1600 
   1601 	ssdTime = 0;
   1602 	levelCount = 0;
   1603 	weaponCount = 0;
   1604 	screenBounds = idBounds(idVec3(-320,-240,0), idVec3(320,240,0));
   1605 
   1606 	superBlasterTimeout = 0;
   1607 
   1608 	currentSound = 0;
   1609 
   1610 	//Precahce all assets that are loaded dynamically
   1611 	declManager->FindMaterial(ASTEROID_MATERIAL);
   1612 	declManager->FindMaterial(ASTRONAUT_MATERIAL);
   1613 
   1614 	for(int i = 0; i < EXPLOSION_MATERIAL_COUNT; i++) {
   1615 		declManager->FindMaterial(explosionMaterials[i]);
   1616 	}
   1617 	declManager->FindMaterial(PROJECTILE_MATERIAL);
   1618 	for(int i = 0; i < POWERUP_MATERIAL_COUNT; i++) {
   1619 		declManager->FindMaterial(powerupMaterials[i][0]);
   1620 		declManager->FindMaterial(powerupMaterials[i][1]);
   1621 	}
   1622 	
   1623 	// Precache sounds
   1624 	declManager->FindSound( "arcade_blaster" );
   1625 	declManager->FindSound( "arcade_capture" );
   1626 	declManager->FindSound( "arcade_explode" );
   1627 
   1628 	ResetGameStats();
   1629 }
   1630 
   1631 void idGameSSDWindow::ResetGameStats() {
   1632 
   1633 	ResetEntities();
   1634 
   1635 	//Reset the gamestats structure
   1636 	memset(&gameStats, 0, sizeof(gameStats));
   1637 
   1638 	gameStats.health = 100;
   1639 
   1640 } 
   1641 
   1642 void idGameSSDWindow::ResetLevelStats() {
   1643 	
   1644 	ResetEntities();
   1645 	
   1646 	//Reset the level statistics structure
   1647 	memset(&gameStats.levelStats, 0, sizeof(gameStats.levelStats));
   1648 
   1649 	
   1650 }
   1651 
   1652 void idGameSSDWindow::ResetEntities() {
   1653 	//Destroy all of the entities
   1654 	for(int i = 0; i < entities.Num(); i++) {
   1655 		entities[i]->DestroyEntity();
   1656 	}
   1657 	entities.Clear();
   1658 }
   1659 
   1660 void idGameSSDWindow::StartGame() {
   1661 	
   1662 	gameStats.gameRunning = true;
   1663 }
   1664 
   1665 void idGameSSDWindow::StopGame() {
   1666 	
   1667 	gameStats.gameRunning = false;
   1668 }
   1669 
   1670 void idGameSSDWindow::GameOver() {
   1671 
   1672 	
   1673 	StopGame();
   1674 
   1675 	gui->HandleNamedEvent("gameOver");
   1676 }
   1677 
   1678 void idGameSSDWindow::BeginLevel(int level) {
   1679 	
   1680 	ResetLevelStats();
   1681 
   1682 	gameStats.currentLevel = level;
   1683 
   1684 	StartGame();
   1685 }
   1686 
   1687 /**
   1688 * Continue game resets the players health
   1689 */
   1690 void idGameSSDWindow::ContinueGame() {
   1691 	gameStats.health = 100; 
   1692 
   1693 	StartGame();
   1694 }
   1695 
   1696 void idGameSSDWindow::LevelComplete() {
   1697 	
   1698 	gameStats.prebonusscore = gameStats.score;
   1699 
   1700 	// Add the bonuses
   1701 	int accuracy;
   1702 	if( !gameStats.levelStats.shotCount ) {
   1703 		accuracy = 0;
   1704 	} else {
   1705 		accuracy = (int)( ( (float)gameStats.levelStats.hitCount / (float)gameStats.levelStats.shotCount ) * 100.0f );
   1706 	}
   1707 	int accuracyPoints = Max( 0, accuracy - 50 ) * 20;
   1708 
   1709 	gui->SetStateString("player_accuracy_score", va("%i", accuracyPoints));
   1710 
   1711 	gameStats.score += accuracyPoints;
   1712 
   1713 	int saveAccuracy;
   1714 	int totalAst = gameStats.levelStats.savedAstronauts + gameStats.levelStats.killedAstronauts;
   1715 	if( !totalAst ) {
   1716 		saveAccuracy = 0;
   1717 	} else {
   1718 		saveAccuracy = (int)( ( (float)gameStats.levelStats.savedAstronauts / (float)totalAst ) * 100.0f );
   1719 	}
   1720 	accuracyPoints = Max( 0, saveAccuracy - 50 ) * 20;
   1721 
   1722 	gui->SetStateString("save_accuracy_score", va("%i", accuracyPoints));
   1723 
   1724 	gameStats.score += accuracyPoints;
   1725 
   1726 
   1727 
   1728 	StopSuperBlaster();
   1729 
   1730 	gameStats.nextLevel++;
   1731 
   1732 	if(gameStats.nextLevel >= levelCount) {
   1733 		//Have they beaten the game
   1734 		GameComplete();
   1735 	} else {
   1736 
   1737 		//Make sure we don't go above the levelcount
   1738 		//min(gameStats.nextLevel, levelCount-1);
   1739 
   1740 		StopGame();
   1741 		gui->HandleNamedEvent("levelComplete");
   1742 	}
   1743 }
   1744 
   1745 void idGameSSDWindow::GameComplete() {
   1746 	StopGame();
   1747 	gui->HandleNamedEvent("gameComplete");
   1748 }
   1749 
   1750 
   1751 void idGameSSDWindow::UpdateGame() {
   1752 
   1753 	//Check to see if and functions where called by the gui
   1754 	if(beginLevel == true) {
   1755 		beginLevel = false;
   1756 		BeginLevel(gameStats.nextLevel);
   1757 	}
   1758 	if(resetGame == true) {
   1759 		resetGame = false;
   1760 		ResetGameStats();
   1761 	}
   1762 	if(continueGame == true) {
   1763 		continueGame = false;
   1764 		ContinueGame();
   1765 	}
   1766 	if(refreshGuiData == true) {
   1767 		refreshGuiData = false;
   1768 		RefreshGuiData();
   1769 	}
   1770 
   1771 	if(gameStats.gameRunning) {
   1772 
   1773 		//We assume an upate every 16 milliseconds
   1774 		ssdTime += 16;
   1775 
   1776 		if(superBlasterTimeout && ssdTime > superBlasterTimeout) {
   1777 			StopSuperBlaster();
   1778 		}
   1779 
   1780 		//Find if we are targeting and enemy
   1781 		idVec2 cursor;
   1782 		//GetCursor(cursor);
   1783 		cursor.x = gui->CursorX();
   1784 		cursor.y = gui->CursorY();
   1785 		gameStats.levelStats.targetEnt = EntityHitTest(cursor);
   1786 
   1787 		//Update from back to front
   1788 		for(int i = entities.Num()-1; i >= 0; i--) {
   1789 			entities[i]->Update();
   1790 		}
   1791 
   1792 		CheckForHits();
   1793 
   1794 		//Delete entities that need to be deleted
   1795 		for(int i = entities.Num()-1; i >= 0; i--) {
   1796 			if(entities[i]->destroyed) {
   1797 				SSDEntity* ent = entities[i];
   1798 				ent->DestroyEntity();
   1799 				entities.RemoveIndex(i);
   1800 			}
   1801 		}
   1802 
   1803 		//Check if we can spawn an asteroid
   1804 		SpawnAsteroid();
   1805 
   1806 		//Check if we should spawn an astronaut
   1807 		SpawnAstronaut();
   1808 
   1809 		//Check if we should spawn an asteroid
   1810 		SpawnPowerup();
   1811 	}
   1812 }
   1813 
   1814 void idGameSSDWindow::CheckForHits() {
   1815 	
   1816 	//See if the entity has gotten close enough
   1817 	for(int i = 0; i < entities.Num(); i++) {
   1818 		SSDEntity* ent = entities[i];
   1819 		if(ent->position.z <= Z_NEAR) {
   1820 
   1821 			if(!ent->noPlayerDamage) {
   1822 
   1823 				//Is the object still in the screen
   1824 				idVec3 entPos = ent->position;
   1825 				entPos.z = 0;
   1826 
   1827 				idBounds entBounds(entPos);
   1828 				entBounds.ExpandSelf(ent->hitRadius);
   1829 
   1830 				if(screenBounds.IntersectsBounds(entBounds)) {
   1831 
   1832 					ent->OnStrikePlayer();
   1833 
   1834 					//The entity hit the player figure out what is was and act appropriately
   1835 					if(ent->type == SSD_ENTITY_ASTEROID) {
   1836 						AsteroidStruckPlayer(static_cast<SSDAsteroid*>(ent));
   1837 					} else if(ent->type == SSD_ENTITY_ASTRONAUT) {
   1838 						AstronautStruckPlayer(static_cast<SSDAstronaut*>(ent));
   1839 					}
   1840 				} else {
   1841 					//Tag for removal later in the frame
   1842 					ent->destroyed = true;
   1843 				}
   1844 			}
   1845 		}
   1846 	}
   1847 }
   1848 
   1849 void idGameSSDWindow::ZOrderEntities() {
   1850 	//Z-Order the entities
   1851 	//Using a simple sorting method
   1852 	for (int i = entities.Num()-1; i >= 0; i--) { 
   1853 		bool flipped = false;
   1854 		for (int j = 0;  j<i ; j++) { 
   1855 			if (entities[j]->position.z > entities[j+1]->position.z) { 
   1856 				SSDEntity* ent = entities[j];
   1857 				entities[j] = entities[j+1]; 
   1858 				entities[j+1] = ent; 
   1859 				flipped = true; 
   1860 			} 
   1861 		} 
   1862 		if (!flipped) { 
   1863 			//Jump out because it is sorted
   1864 			break; 
   1865 		} 
   1866 	}
   1867 }
   1868 
   1869 void idGameSSDWindow::SpawnAsteroid() {
   1870 
   1871 	int currentTime = ssdTime;
   1872 
   1873 	if(currentTime < gameStats.levelStats.nextAsteroidSpawnTime) {
   1874 		//Not time yet
   1875 		return;
   1876 	}
   1877 
   1878 	//Lets spawn it
   1879 	idVec3 startPosition;
   1880 
   1881 	float spawnBuffer = levelData[gameStats.currentLevel].spawnBuffer*2.0f;
   1882 	startPosition.x = random.RandomInt(V_WIDTH+spawnBuffer)-((V_WIDTH/2.0f)+spawnBuffer);
   1883 	startPosition.y = random.RandomInt(V_HEIGHT+spawnBuffer)-((V_HEIGHT/2.0f)+spawnBuffer);
   1884 	startPosition.z = ENTITY_START_DIST;
   1885 
   1886 	float speed = random.RandomInt(asteroidData[gameStats.currentLevel].speedMax - asteroidData[gameStats.currentLevel].speedMin) + asteroidData[gameStats.currentLevel].speedMin;
   1887 	float size = random.RandomInt(asteroidData[gameStats.currentLevel].sizeMax - asteroidData[gameStats.currentLevel].sizeMin) + asteroidData[gameStats.currentLevel].sizeMin;
   1888 	float rotate = (random.RandomFloat() * (asteroidData[gameStats.currentLevel].rotateMax - asteroidData[gameStats.currentLevel].rotateMin)) + asteroidData[gameStats.currentLevel].rotateMin;
   1889 	
   1890 	SSDAsteroid* asteroid = SSDAsteroid::GetNewAsteroid(this, startPosition, idVec2(size, size), speed, rotate, asteroidData[gameStats.currentLevel].asteroidHealth);
   1891 	entities.Append(asteroid);
   1892 	
   1893 	gameStats.levelStats.nextAsteroidSpawnTime = currentTime + random.RandomInt(asteroidData[gameStats.currentLevel].spawnMax - asteroidData[gameStats.currentLevel].spawnMin) + asteroidData[gameStats.currentLevel].spawnMin;
   1894 }
   1895 
   1896 void idGameSSDWindow::FireWeapon(int key) {
   1897 
   1898 	idVec2 cursorWorld = GetCursorWorld();
   1899 	idVec2 cursor;
   1900 	//GetCursor(cursor);
   1901 	cursor.x = gui->CursorX();
   1902 	cursor.y = gui->CursorY();
   1903 
   1904 	if(key == K_MOUSE1) {
   1905 	
   1906 		gameStats.levelStats.shotCount++;
   1907 		
   1908 		if(gameStats.levelStats.targetEnt) {
   1909 			//Aim the projectile from the bottom of the screen directly at the ent
   1910 			//SSDProjectile* newProj = new (TAG_OLD_UI) SSDProjectile(this, idVec3(320,0,0), gameStats.levelStats.targetEnt->position, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
   1911 			SSDProjectile* newProj = SSDProjectile::GetNewProjectile(this, idVec3(0,-180,0), gameStats.levelStats.targetEnt->position, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
   1912 			entities.Append(newProj);
   1913 			//newProj = SSDProjectile::GetNewProjectile(this, idVec3(-320,-0,0), gameStats.levelStats.targetEnt->position, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
   1914 			//entities.Append(newProj);
   1915 
   1916 			//We hit something
   1917 			gameStats.levelStats.hitCount++;
   1918 
   1919 			gameStats.levelStats.targetEnt->OnHit(key);
   1920 
   1921 			if(gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTEROID) {
   1922 				HitAsteroid(static_cast<SSDAsteroid*>(gameStats.levelStats.targetEnt), key);
   1923 			} else if(gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTRONAUT) {
   1924 				HitAstronaut(static_cast<SSDAstronaut*>(gameStats.levelStats.targetEnt), key);
   1925 			}
   1926 		} else {
   1927 			////Aim the projectile at the cursor position all the way to the far clipping
   1928 			//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);
   1929 
   1930 			//Aim the projectile so it crosses the cursor 1/4 of screen
   1931 			idVec3 vec = idVec3(cursorWorld.x, cursorWorld.y, (Z_FAR-Z_NEAR)/8.0f);
   1932 			vec *= 8;
   1933 			SSDProjectile* newProj = SSDProjectile::GetNewProjectile(this, idVec3(0,-180,0), vec, weaponData[gameStats.currentWeapon].speed, weaponData[gameStats.currentWeapon].size);
   1934 			entities.Append(newProj);	
   1935 			
   1936 		}
   1937 
   1938 
   1939 		//Play the blaster sound
   1940 		PlaySound("arcade_blaster");
   1941 
   1942 	} /*else if (key == K_MOUSE2) {
   1943 		if(gameStats.levelStats.targetEnt) {
   1944 			if(gameStats.levelStats.targetEnt->type == SSD_ENTITY_ASTRONAUT) {
   1945 				HitAstronaut(static_cast<SSDAstronaut*>(gameStats.levelStats.targetEnt), key);
   1946 			}
   1947 		}
   1948 	}*/
   1949 }
   1950 
   1951 SSDEntity* idGameSSDWindow::EntityHitTest(const idVec2& pt) {
   1952 
   1953 	for(int i = 0; i < entities.Num(); i++) {
   1954 		//Since we ZOrder the entities every frame we can stop at the first entity we hit.
   1955 		//ToDo: Make sure this assumption is true
   1956 		if(entities[i]->HitTest(pt)) {
   1957 			return entities[i];
   1958 		}
   1959 	}
   1960 	return NULL;
   1961 }
   1962 
   1963 void idGameSSDWindow::HitAsteroid(SSDAsteroid* asteroid, int key) {
   1964 
   1965 	
   1966 
   1967 	asteroid->health -= weaponData[gameStats.currentWeapon].damage;
   1968 
   1969 	if(asteroid->health <= 0) {
   1970 		
   1971 		//The asteroid has been destroyed
   1972 		SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, asteroid->position, asteroid->size*2, 300, SSDExplosion::EXPLOSION_NORMAL, asteroid);
   1973 		entities.Append(explosion);
   1974 		PlaySound("arcade_explode");
   1975 
   1976 		AddScore(asteroid, asteroidData[gameStats.currentLevel].asteroidPoints);
   1977 
   1978 		//Don't let the player hit it anymore because 
   1979 		asteroid->noHit = true;
   1980 
   1981 		gameStats.levelStats.destroyedAsteroids++;
   1982 		//if(gameStats.levelStats.destroyedAsteroids >= levelData[gameStats.currentLevel].needToWin) {
   1983 		//	LevelComplete();
   1984 		//}
   1985 
   1986 	} else {
   1987 		//This was a damage hit so create a real small quick explosion
   1988 		SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, asteroid->position, asteroid->size/2.0f, 200, SSDExplosion::EXPLOSION_NORMAL, asteroid, false, false);
   1989 		entities.Append(explosion);
   1990 	}
   1991 }
   1992 
   1993 void idGameSSDWindow::AsteroidStruckPlayer(SSDAsteroid* asteroid) {
   1994 
   1995 	asteroid->noPlayerDamage = true;
   1996 	asteroid->noHit = true;
   1997 	
   1998 	AddDamage(asteroidData[gameStats.currentLevel].asteroidDamage);
   1999 
   2000 	SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, asteroid->position, asteroid->size*2, 300, SSDExplosion::EXPLOSION_NORMAL, asteroid);
   2001 	entities.Append(explosion);
   2002 	PlaySound("arcade_explode");
   2003 }
   2004 
   2005 void idGameSSDWindow::AddScore(SSDEntity* ent, int points) {
   2006 
   2007 	SSDPoints* pointsEnt;
   2008 	
   2009 	if(points > 0) {
   2010 		pointsEnt = SSDPoints::GetNewPoints(this, ent, points, 1000, 50, idVec4(0,1,0,1));
   2011 	} else {
   2012 		pointsEnt = SSDPoints::GetNewPoints(this, ent, points, 1000, 50, idVec4(1,0,0,1));
   2013 	}
   2014 	entities.Append(pointsEnt);
   2015 
   2016 	gameStats.score += points;
   2017 	gui->SetStateString( "player_score", va("%i", gameStats.score ) );
   2018 }
   2019 
   2020 void idGameSSDWindow::AddDamage(int damage) {
   2021 	gameStats.health -= damage;
   2022 	gui->SetStateString( "player_health", va("%i", gameStats.health ) );
   2023 
   2024 	gui->HandleNamedEvent( "playerDamage" );
   2025 
   2026 	if(gameStats.health <= 0) {
   2027 		//The player is dead
   2028 		GameOver();
   2029 	}
   2030 }
   2031 
   2032 void idGameSSDWindow::AddHealth(int health) {
   2033 	gameStats.health += health;
   2034 	gameStats.health = Min( 100, gameStats.health );
   2035 }
   2036 
   2037 
   2038 void idGameSSDWindow::OnNuke() {
   2039 	
   2040 	gui->HandleNamedEvent("nuke");
   2041 
   2042 	//Destory All Asteroids
   2043 	for(int i = 0 ; i < entities.Num(); i++) {
   2044 
   2045 		if(entities[i]->type == SSD_ENTITY_ASTEROID) {
   2046 			
   2047 			//The asteroid has been destroyed
   2048 			SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, entities[i]->position, entities[i]->size*2, 300, SSDExplosion::EXPLOSION_NORMAL, entities[i]);
   2049 			entities.Append(explosion);
   2050 
   2051 			AddScore(entities[i], asteroidData[gameStats.currentLevel].asteroidPoints);
   2052 
   2053 			//Don't let the player hit it anymore because 
   2054 			entities[i]->noHit = true;
   2055 
   2056 			gameStats.levelStats.destroyedAsteroids++;
   2057 		}
   2058 	}
   2059 	PlaySound("arcade_explode");
   2060 	
   2061 	//Check to see if a nuke ends the level
   2062 	/*if(gameStats.levelStats.destroyedAsteroids >= levelData[gameStats.currentLevel].needToWin) {
   2063 		LevelComplete();
   2064 
   2065 	}*/
   2066 }
   2067 
   2068 void idGameSSDWindow::OnRescueAll() {
   2069 	
   2070 	gui->HandleNamedEvent("rescueAll");
   2071 
   2072 	//Rescue All Astronauts
   2073 	for(int i = 0 ; i < entities.Num(); i++) {
   2074 
   2075 		if(entities[i]->type == SSD_ENTITY_ASTRONAUT) {
   2076 
   2077 			AstronautStruckPlayer((SSDAstronaut*)entities[i]);
   2078 		}
   2079 	}
   2080 }
   2081 
   2082 void idGameSSDWindow::OnSuperBlaster() {
   2083 	
   2084 	StartSuperBlaster();
   2085 }
   2086 
   2087 
   2088 
   2089 void idGameSSDWindow::RefreshGuiData() {
   2090 
   2091 
   2092 	gui->SetStateString("nextLevel", va("%i", gameStats.nextLevel+1));
   2093 	gui->SetStateString("currentLevel", va("%i", gameStats.currentLevel+1));
   2094 
   2095 	float accuracy;
   2096 	if(!gameStats.levelStats.shotCount) {
   2097 		accuracy = 0;
   2098 	} else {
   2099 		accuracy = ((float)gameStats.levelStats.hitCount/(float)gameStats.levelStats.shotCount)*100.0f;
   2100 	}
   2101 	gui->SetStateString( "player_accuracy", va("%d%%", (int)accuracy));
   2102 
   2103 	float saveAccuracy;
   2104 	int totalAst = gameStats.levelStats.savedAstronauts + gameStats.levelStats.killedAstronauts;
   2105 
   2106 	if(!totalAst) {
   2107 		saveAccuracy = 0;
   2108 	} else {
   2109 		saveAccuracy = ((float)gameStats.levelStats.savedAstronauts/(float)totalAst)*100.0f;
   2110 	}
   2111 	gui->SetStateString( "save_accuracy", va("%d%%", (int)saveAccuracy));
   2112 
   2113 
   2114 
   2115 
   2116 	if(gameStats.levelStats.targetEnt) {
   2117 		int dist = (gameStats.levelStats.targetEnt->position.z/100.0f);
   2118 		dist *= 100;
   2119 		gui->SetStateString("target_info", va("%i meters", dist));
   2120 	} else {
   2121 		gui->SetStateString("target_info", "No Target");
   2122 	}
   2123 
   2124 	gui->SetStateString( "player_health", va("%i", gameStats.health ) );
   2125 	gui->SetStateString( "player_score", va("%i", gameStats.score ) );
   2126 	gui->SetStateString( "player_prebonusscore", va("%i", gameStats.prebonusscore ) );
   2127 	gui->SetStateString( "level_complete", va("%i/%i", gameStats.levelStats.savedAstronauts, levelData[gameStats.currentLevel].needToWin ));
   2128 
   2129 
   2130 	if(superBlasterTimeout) {
   2131 		float timeRemaining = (superBlasterTimeout - ssdTime)/1000.0f;
   2132 		gui->SetStateString("super_blaster_time", va("%.2f", timeRemaining));
   2133 	}
   2134 }
   2135 
   2136 idVec2 idGameSSDWindow::GetCursorWorld() {
   2137 	
   2138 	idVec2 cursor;
   2139 	//GetCursor(cursor);
   2140 	cursor.x = gui->CursorX();
   2141 	cursor.y = gui->CursorY();
   2142 	cursor.x = cursor.x - 0.5f * V_WIDTH;
   2143 	cursor.y = -(cursor.y  - 0.5f * V_HEIGHT);
   2144 	return cursor;
   2145 }
   2146 
   2147 void idGameSSDWindow::SpawnAstronaut() {
   2148 	
   2149 	int currentTime = ssdTime;
   2150 
   2151 	if(currentTime < gameStats.levelStats.nextAstronautSpawnTime) {
   2152 		//Not time yet
   2153 		return;
   2154 	}
   2155 
   2156 	//Lets spawn it
   2157 	idVec3 startPosition;
   2158 
   2159 	startPosition.x = random.RandomInt(V_WIDTH)-(V_WIDTH/2.0f);
   2160 	startPosition.y = random.RandomInt(V_HEIGHT)-(V_HEIGHT/2.0f);
   2161 	startPosition.z = ENTITY_START_DIST;
   2162 
   2163 	float speed = random.RandomInt(astronautData[gameStats.currentLevel].speedMax - astronautData[gameStats.currentLevel].speedMin) + astronautData[gameStats.currentLevel].speedMin;
   2164 	float rotate = (random.RandomFloat() * (astronautData[gameStats.currentLevel].rotateMax - astronautData[gameStats.currentLevel].rotateMin)) + astronautData[gameStats.currentLevel].rotateMin;
   2165 
   2166 	SSDAstronaut* astronaut = SSDAstronaut::GetNewAstronaut(this, startPosition, speed, rotate, astronautData[gameStats.currentLevel].health);
   2167 	entities.Append(astronaut);
   2168 
   2169 	gameStats.levelStats.nextAstronautSpawnTime = currentTime + random.RandomInt(astronautData[gameStats.currentLevel].spawnMax - astronautData[gameStats.currentLevel].spawnMin) + astronautData[gameStats.currentLevel].spawnMin;
   2170 }
   2171 
   2172 void idGameSSDWindow::HitAstronaut(SSDAstronaut* astronaut, int key) {
   2173 
   2174 
   2175 	if(key == K_MOUSE1) {
   2176 		astronaut->health -= weaponData[gameStats.currentWeapon].damage;
   2177 
   2178 		if(astronaut->health <= 0) {
   2179 
   2180 			gameStats.levelStats.killedAstronauts++;
   2181 
   2182 			//The astronaut has been destroyed
   2183 			SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, astronaut->position, astronaut->size*2, 300, SSDExplosion::EXPLOSION_NORMAL, astronaut);
   2184 			entities.Append(explosion);
   2185 			PlaySound("arcade_explode");
   2186 
   2187 			//Add the penalty for killing the astronaut
   2188 			AddScore(astronaut, astronautData[gameStats.currentLevel].penalty);
   2189 
   2190 			//Don't let the player hit it anymore
   2191 			astronaut->noHit = true;
   2192 		} else {
   2193 			//This was a damage hit so create a real small quick explosion
   2194 			SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, astronaut->position, astronaut->size/2.0f, 200, SSDExplosion::EXPLOSION_NORMAL, astronaut, false, false);
   2195 			entities.Append(explosion);
   2196 		}
   2197 	}
   2198 }
   2199 
   2200 void idGameSSDWindow::AstronautStruckPlayer(SSDAstronaut* astronaut) {
   2201 
   2202 	gameStats.levelStats.savedAstronauts++;
   2203 
   2204 	astronaut->noPlayerDamage = true;
   2205 	astronaut->noHit = true;
   2206 
   2207 	//We are saving an astronaut
   2208 	SSDExplosion* explosion = SSDExplosion::GetNewExplosion(this, astronaut->position, astronaut->size*2, 300, SSDExplosion::EXPLOSION_TELEPORT, astronaut);
   2209 	entities.Append(explosion);
   2210 	PlaySound("arcade_capture");
   2211 
   2212 	//Give the player points for saving the astronaut
   2213 	AddScore(astronaut, astronautData[gameStats.currentLevel].points);
   2214 
   2215 	if(gameStats.levelStats.savedAstronauts >= levelData[gameStats.currentLevel].needToWin) {
   2216 		LevelComplete();
   2217 	}
   2218 
   2219 }
   2220 
   2221 void idGameSSDWindow::SpawnPowerup() {
   2222 
   2223 	int currentTime = ssdTime;
   2224 
   2225 	if(currentTime < gameStats.levelStats.nextPowerupSpawnTime) {
   2226 		//Not time yet
   2227 		return;
   2228 	}
   2229 
   2230 	float speed = random.RandomInt(powerupData[gameStats.currentLevel].speedMax - powerupData[gameStats.currentLevel].speedMin) + powerupData[gameStats.currentLevel].speedMin;
   2231 	float rotate = (random.RandomFloat() * (powerupData[gameStats.currentLevel].rotateMax - powerupData[gameStats.currentLevel].rotateMin)) + powerupData[gameStats.currentLevel].rotateMin;
   2232 
   2233 	SSDPowerup* powerup = SSDPowerup::GetNewPowerup(this, speed, rotate);
   2234 	entities.Append(powerup);
   2235 
   2236 	gameStats.levelStats.nextPowerupSpawnTime = currentTime + random.RandomInt(powerupData[gameStats.currentLevel].spawnMax - powerupData[gameStats.currentLevel].spawnMin) + powerupData[gameStats.currentLevel].spawnMin;
   2237 
   2238 }
   2239 
   2240 void idGameSSDWindow::StartSuperBlaster() {
   2241 	
   2242 	gui->HandleNamedEvent("startSuperBlaster");
   2243 	gameStats.currentWeapon = 1;
   2244 	superBlasterTimeout = ssdTime + 10000;
   2245 
   2246 }
   2247 void idGameSSDWindow::StopSuperBlaster() {
   2248 	gui->HandleNamedEvent("stopSuperBlaster");
   2249 	gameStats.currentWeapon = 0;
   2250 	superBlasterTimeout = 0;
   2251 
   2252 }
   2253 
   2254 SSDEntity* idGameSSDWindow::GetSpecificEntity(int type, int id) {
   2255 	SSDEntity* ent = NULL;
   2256 	switch(type) {
   2257 			case SSD_ENTITY_ASTEROID:
   2258 				ent = SSDAsteroid::GetSpecificAsteroid(id);
   2259 				break;
   2260 			case SSD_ENTITY_ASTRONAUT:
   2261 				ent = SSDAstronaut::GetSpecificAstronaut(id);
   2262 				break;
   2263 			case SSD_ENTITY_EXPLOSION:
   2264 				ent = SSDExplosion::GetSpecificExplosion(id);
   2265 				break;
   2266 			case SSD_ENTITY_POINTS:
   2267 				ent = SSDPoints::GetSpecificPoints(id);
   2268 				break;
   2269 			case SSD_ENTITY_PROJECTILE:
   2270 				ent = SSDProjectile::GetSpecificProjectile(id);
   2271 				break;
   2272 			case SSD_ENTITY_POWERUP:
   2273 				ent = SSDPowerup::GetSpecificPowerup(id);
   2274 				break;
   2275 	}
   2276 	return ent;
   2277 }
   2278 
   2279 #define MAX_SOUND_CHANNEL 8
   2280 
   2281 void idGameSSDWindow::PlaySound(const char* sound) {
   2282 
   2283 	common->SW()->PlayShaderDirectly(sound, currentSound);
   2284 
   2285 	currentSound++;
   2286 	if(currentSound >= MAX_SOUND_CHANNEL) {
   2287 		currentSound = 0;
   2288 	}
   2289 }