DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

GameBustOutWindow.cpp (31364B)


      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 "../renderer/Image.h"
     32 
     33 #include "DeviceContext.h"
     34 #include "Window.h"
     35 #include "UserInterfaceLocal.h"
     36 #include "GameBustOutWindow.h"
     37 
     38 #define BALL_RADIUS		12.f
     39 #define BALL_SPEED		250.f
     40 #define BALL_MAXSPEED	450.f
     41 
     42 #define S_UNIQUE_CHANNEL	6
     43 
     44 /*
     45 *****************************************************************************
     46 * BOEntity	
     47 ****************************************************************************
     48 */
     49 BOEntity::BOEntity(idGameBustOutWindow* _game) {
     50 	game = _game;
     51 	visible = true;
     52 
     53 	materialName = "";
     54 	material = NULL;
     55 	width = height = 8;
     56 	color = colorWhite;
     57 	powerup = POWERUP_NONE;
     58 
     59 	position.Zero();
     60 	velocity.Zero();
     61 
     62 	removed = false;
     63 	fadeOut = 0;
     64 }
     65 
     66 BOEntity::~BOEntity() {
     67 }
     68 
     69 /*
     70 ======================
     71 BOEntity::WriteToSaveGame
     72 ======================
     73 */
     74 void BOEntity::WriteToSaveGame( idFile *savefile ) {
     75 
     76 	savefile->Write( &visible, sizeof(visible) );
     77 
     78 	game->WriteSaveGameString( materialName, savefile );
     79 
     80 	savefile->Write( &width, sizeof(width) );
     81 	savefile->Write( &height, sizeof(height) );
     82 
     83 	savefile->Write( &color, sizeof(color) );
     84 	savefile->Write( &position, sizeof(position) );
     85 	savefile->Write( &velocity, sizeof(velocity) );
     86 
     87 	savefile->Write( &powerup, sizeof(powerup) );
     88 	savefile->Write( &removed, sizeof(removed) );
     89 	savefile->Write( &fadeOut, sizeof(fadeOut) );
     90 }
     91 
     92 /*
     93 ======================
     94 BOEntity::ReadFromSaveGame
     95 ======================
     96 */
     97 void BOEntity::ReadFromSaveGame( idFile *savefile, idGameBustOutWindow* _game ) {
     98 	game = _game;
     99 
    100 	savefile->Read( &visible, sizeof(visible) );
    101 
    102 	game->ReadSaveGameString( materialName, savefile );
    103 	SetMaterial( materialName );
    104 
    105 	savefile->Read( &width, sizeof(width) );
    106 	savefile->Read( &height, sizeof(height) );
    107 
    108 	savefile->Read( &color, sizeof(color) );
    109 	savefile->Read( &position, sizeof(position) );
    110 	savefile->Read( &velocity, sizeof(velocity) );
    111 
    112 	savefile->Read( &powerup, sizeof(powerup) );
    113 	savefile->Read( &removed, sizeof(removed) );
    114 	savefile->Read( &fadeOut, sizeof(fadeOut) );
    115 }
    116 
    117 /*
    118 ======================
    119 BOEntity::SetMaterial
    120 ======================
    121 */
    122 void BOEntity::SetMaterial(const char* name) {
    123 	materialName = name;
    124 	material = declManager->FindMaterial( name );
    125 	material->SetSort( SS_GUI );
    126 }
    127 
    128 /*
    129 ======================
    130 BOEntity::SetSize
    131 ======================
    132 */
    133 void BOEntity::SetSize( float _width, float _height ) {
    134 	width = _width;
    135 	height = _height;
    136 }
    137 
    138 /*
    139 ======================
    140 BOEntity::SetVisible
    141 ======================
    142 */
    143 void BOEntity::SetColor( float r, float g, float b, float a ) {
    144 	color.x = r;
    145 	color.y = g;
    146 	color.z = b;
    147 	color.w = a;
    148 }
    149 
    150 /*
    151 ======================
    152 BOEntity::SetVisible
    153 ======================
    154 */
    155 void BOEntity::SetVisible( bool isVisible ) {
    156 	visible = isVisible;
    157 }
    158 
    159 /*
    160 ======================
    161 BOEntity::Update
    162 ======================
    163 */
    164 void BOEntity::Update( float timeslice, int guiTime ) {
    165 	
    166 	if ( !visible ) {
    167 		return;
    168 	}
    169 
    170 	// Move the entity
    171 	position += velocity * timeslice;
    172 
    173 	// Fade out the ent
    174 	if ( fadeOut ) {
    175 		color.w -= timeslice * 2.5;
    176 
    177 		if ( color.w <= 0.f ) {
    178 			color.w = 0.f;
    179 			removed = true;
    180 		}
    181 	}
    182 }
    183 
    184 /*
    185 ======================
    186 BOEntity::Draw
    187 ======================
    188 */
    189 void BOEntity::Draw() {
    190 	if ( visible ) {
    191 		dc->DrawMaterialRotated( position.x, position.y, width, height, material, color, 1.0f, 1.0f, DEG2RAD(0.f) );
    192 	}
    193 }
    194 
    195 /*
    196 *****************************************************************************
    197 * BOBrick
    198 ****************************************************************************
    199 */
    200 BOBrick::BOBrick() {
    201 	ent = NULL;
    202 	x = y = width = height = 0;
    203 	powerup = POWERUP_NONE;
    204 	isBroken = false;
    205 }
    206 
    207 BOBrick::BOBrick( BOEntity *_ent, float _x, float _y, float _width, float _height ) {
    208 	ent = _ent;
    209 	x = _x;
    210 	y = _y;
    211 	width = _width;
    212 	height = _height;
    213 	powerup = POWERUP_NONE;
    214 
    215 	isBroken = false;
    216 
    217 	ent->position.x = x;
    218 	ent->position.y = y;
    219 	ent->SetSize( width, height );
    220 	ent->SetMaterial( "game/bustout/brick" );
    221 
    222 	ent->game->entities.Append( ent );
    223 }
    224 
    225 BOBrick::~BOBrick() {
    226 }
    227 
    228 /*
    229 ======================
    230 BOBrick::WriteToSaveGame
    231 ======================
    232 */
    233 void BOBrick::WriteToSaveGame( idFile *savefile ) {
    234 	savefile->Write( &x, sizeof(x) );
    235 	savefile->Write( &y, sizeof(y) );
    236 	savefile->Write( &width, sizeof(width) );
    237 	savefile->Write( &height, sizeof(height) );
    238 
    239 	savefile->Write( &powerup, sizeof(powerup) );
    240 	savefile->Write( &isBroken, sizeof(isBroken) );
    241 
    242 	int index = ent->game->entities.FindIndex( ent );
    243 	savefile->Write( &index, sizeof(index) );
    244 }
    245 
    246 /*
    247 ======================
    248 BOBrick::ReadFromSaveGame
    249 ======================
    250 */
    251 void BOBrick::ReadFromSaveGame( idFile *savefile, idGameBustOutWindow *game ) {
    252 	savefile->Read( &x, sizeof(x) );
    253 	savefile->Read( &y, sizeof(y) );
    254 	savefile->Read( &width, sizeof(width) );
    255 	savefile->Read( &height, sizeof(height) );
    256 
    257 	savefile->Read( &powerup, sizeof(powerup) );
    258 	savefile->Read( &isBroken, sizeof(isBroken) );
    259 
    260 	int index;
    261 	savefile->Read( &index, sizeof(index) );
    262 	ent = game->entities[index];
    263 }
    264 
    265 /*
    266 ======================
    267 BOBrick::SetColor
    268 ======================
    269 */
    270 void BOBrick::SetColor( idVec4 bcolor ) {
    271 	ent->SetColor( bcolor.x, bcolor.y, bcolor.z, bcolor.w );
    272 }
    273 
    274 /*
    275 ======================
    276 BOBrick::checkCollision
    277 ======================
    278 */
    279 collideDir_t BOBrick::checkCollision( idVec2 pos, idVec2 vel ) {
    280 	idVec2	ptA, ptB;
    281 	float	dist;
    282 
    283 	collideDir_t	result = COLLIDE_NONE;
    284 
    285 	if ( isBroken ) {
    286 		return result;
    287 	}
    288 
    289 	// Check for collision with each edge
    290 	idVec2 vec;
    291 
    292 	// Bottom
    293 	ptA.x = x;
    294 	ptA.y = y + height;
    295 
    296 	ptB.x = x + width;
    297 	ptB.y = y + height;
    298 
    299 	if ( vel.y < 0 && pos.y > ptA.y ) {
    300 		if( pos.x > ptA.x && pos.x < ptB.x ) {
    301 			dist = pos.y - ptA.y;
    302 
    303 			if ( dist < BALL_RADIUS ) {
    304 				result = COLLIDE_DOWN;
    305 			}
    306 		} else {
    307 			if ( pos.x <= ptA.x ) {
    308 				vec = pos - ptA;
    309 			} else {
    310 				vec = pos - ptB;
    311 			}
    312 
    313 			if ( (idMath::Fabs(vec.y) > idMath::Fabs(vec.x)) && (vec.LengthFast() < BALL_RADIUS) ) {
    314 				result = COLLIDE_DOWN;
    315 			}
    316 		}
    317 	}
    318 
    319 	if ( result == COLLIDE_NONE ) {
    320 		// Top
    321 		ptA.y = y;
    322 		ptB.y = y;
    323 
    324 		if ( vel.y > 0 && pos.y < ptA.y ) {
    325 			if( pos.x > ptA.x && pos.x < ptB.x ) {
    326 				dist = ptA.y - pos.y;
    327 
    328 				if ( dist < BALL_RADIUS ) {
    329 					result = COLLIDE_UP;
    330 				}
    331 			} else {
    332 				if ( pos.x <= ptA.x ) {
    333 					vec = pos - ptA;
    334 				} else {
    335 					vec = pos - ptB;
    336 				}
    337 
    338 				if ( (idMath::Fabs(vec.y) > idMath::Fabs(vec.x)) && (vec.LengthFast() < BALL_RADIUS) ) {
    339 					result = COLLIDE_UP;
    340 				}
    341 			}
    342 		}
    343 
    344 		if ( result == COLLIDE_NONE ) {
    345 			// Left side
    346 			ptA.x = x;
    347 			ptA.y = y;
    348 
    349 			ptB.x = x;
    350 			ptB.y = y + height;
    351 
    352 			if ( vel.x > 0 && pos.x < ptA.x ) {
    353 				if( pos.y > ptA.y && pos.y < ptB.y ) {
    354 					dist = ptA.x - pos.x;
    355 
    356 					if ( dist < BALL_RADIUS ) {
    357 						result = COLLIDE_LEFT;
    358 					}
    359 				} else {
    360 					if ( pos.y <= ptA.y ) {
    361 						vec = pos - ptA;
    362 					} else {
    363 						vec = pos - ptB;
    364 					}
    365 
    366 					if ( (idMath::Fabs(vec.x) >= idMath::Fabs(vec.y)) && (vec.LengthFast() < BALL_RADIUS) ) {
    367 						result = COLLIDE_LEFT;
    368 					}
    369 				}
    370 			}
    371 
    372 			if ( result == COLLIDE_NONE ) {
    373 				// Right side
    374 				ptA.x = x + width;
    375 				ptB.x = x + width;
    376 
    377 				if ( vel.x < 0 && pos.x > ptA.x ) {
    378 					if( pos.y > ptA.y && pos.y < ptB.y ) {
    379 						dist = pos.x - ptA.x;
    380 
    381 						if ( dist < BALL_RADIUS ) {
    382 							result = COLLIDE_LEFT;
    383 						}
    384 					} else {
    385 						if ( pos.y <= ptA.y ) {
    386 							vec = pos - ptA;
    387 						} else {
    388 							vec = pos - ptB;
    389 						}
    390 
    391 						if ( (idMath::Fabs(vec.x) >= idMath::Fabs(vec.y)) && (vec.LengthFast() < BALL_RADIUS) ) {
    392 							result = COLLIDE_LEFT;
    393 						}
    394 					}
    395 				}
    396 
    397 			}
    398 		}
    399 	}
    400 
    401 	return result;
    402 }
    403 
    404 /*
    405 *****************************************************************************
    406 * idGameBustOutWindow
    407 ****************************************************************************
    408 */
    409 idGameBustOutWindow::idGameBustOutWindow(idUserInterfaceLocal *g) : idWindow(g) {
    410 	gui = g;
    411 	CommonInit();
    412 }
    413 
    414 idGameBustOutWindow::~idGameBustOutWindow() {
    415 	entities.DeleteContents(true);
    416 
    417 	Mem_Free( levelBoardData );
    418 }
    419 
    420 /*
    421 =============================
    422 idGameBustOutWindow::WriteToSaveGame
    423 =============================
    424 */
    425 void idGameBustOutWindow::WriteToSaveGame( idFile *savefile ) {
    426 	idWindow::WriteToSaveGame( savefile );
    427 
    428 	gamerunning.WriteToSaveGame( savefile );
    429 	onFire.WriteToSaveGame( savefile );
    430 	onContinue.WriteToSaveGame( savefile );
    431 	onNewGame.WriteToSaveGame( savefile );
    432 	onNewLevel.WriteToSaveGame( savefile );
    433 
    434 	savefile->Write( &timeSlice, sizeof(timeSlice) );
    435 	savefile->Write( &gameOver, sizeof(gameOver) );
    436 	savefile->Write( &numLevels, sizeof(numLevels) );
    437 
    438 	// Board Data is loaded when GUI is loaded, don't need to save
    439 
    440 	savefile->Write( &numBricks, sizeof(numBricks) );
    441 	savefile->Write( &currentLevel, sizeof(currentLevel) );
    442 
    443 	savefile->Write( &updateScore, sizeof(updateScore) );
    444 	savefile->Write( &gameScore, sizeof(gameScore) );
    445 	savefile->Write( &nextBallScore, sizeof(nextBallScore) );
    446 
    447 	savefile->Write( &bigPaddleTime, sizeof(bigPaddleTime) );
    448 	savefile->Write( &paddleVelocity, sizeof(paddleVelocity) );
    449 
    450 	savefile->Write( &ballSpeed, sizeof(ballSpeed) );
    451 	savefile->Write( &ballsRemaining, sizeof(ballsRemaining) );
    452 	savefile->Write( &ballsInPlay, sizeof(ballsInPlay) );
    453 	savefile->Write( &ballHitCeiling, sizeof(ballHitCeiling) );
    454 
    455 	// Write Entities
    456 	int i;
    457 	int numberOfEnts = entities.Num();
    458 	savefile->Write( &numberOfEnts, sizeof(numberOfEnts) );
    459 	for ( i=0; i<numberOfEnts; i++ ) {
    460 		entities[i]->WriteToSaveGame( savefile );
    461 	}
    462 
    463 	// Write Balls
    464 	numberOfEnts = balls.Num();
    465 	savefile->Write( &numberOfEnts, sizeof(numberOfEnts) );
    466 	for ( i=0; i<numberOfEnts; i++ ) {
    467 		int ballIndex = entities.FindIndex( balls[i] );
    468 		savefile->Write( &ballIndex, sizeof(ballIndex) );
    469 	}
    470 
    471 	// Write Powerups
    472 	numberOfEnts = powerUps.Num();
    473 	savefile->Write( &numberOfEnts, sizeof(numberOfEnts) );
    474 	for ( i=0; i<numberOfEnts; i++ ) {
    475 		int powerIndex = entities.FindIndex( powerUps[i] );
    476 		savefile->Write( &powerIndex, sizeof(powerIndex) );
    477 	}
    478 
    479 	// Write paddle
    480 	paddle->WriteToSaveGame( savefile );
    481 
    482 	// Write Bricks
    483 	int row;
    484 	for ( row=0; row<BOARD_ROWS; row++ ) {
    485 		numberOfEnts = board[row].Num();
    486 		savefile->Write( &numberOfEnts, sizeof(numberOfEnts) );
    487 		for ( i=0; i<numberOfEnts; i++ ) {
    488 			board[row][i]->WriteToSaveGame( savefile );
    489 		}
    490 	}
    491 }
    492 
    493 /*
    494 =============================
    495 idGameBustOutWindow::ReadFromSaveGame
    496 =============================
    497 */
    498 void idGameBustOutWindow::ReadFromSaveGame( idFile *savefile ) {
    499 	idWindow::ReadFromSaveGame( savefile );
    500 
    501 	// Clear out existing paddle and entities from GUI load
    502 	delete paddle;
    503 	entities.DeleteContents( true );
    504 
    505 	gamerunning.ReadFromSaveGame( savefile );
    506 	onFire.ReadFromSaveGame( savefile );
    507 	onContinue.ReadFromSaveGame( savefile );
    508 	onNewGame.ReadFromSaveGame( savefile );
    509 	onNewLevel.ReadFromSaveGame( savefile );
    510 
    511 	savefile->Read( &timeSlice, sizeof(timeSlice) );
    512 	savefile->Read( &gameOver, sizeof(gameOver) );
    513 	savefile->Read( &numLevels, sizeof(numLevels) );
    514 
    515 	// Board Data is loaded when GUI is loaded, don't need to save
    516 
    517 	savefile->Read( &numBricks, sizeof(numBricks) );
    518 	savefile->Read( &currentLevel, sizeof(currentLevel) );
    519 
    520 	savefile->Read( &updateScore, sizeof(updateScore) );
    521 	savefile->Read( &gameScore, sizeof(gameScore) );
    522 	savefile->Read( &nextBallScore, sizeof(nextBallScore) );
    523 
    524 	savefile->Read( &bigPaddleTime, sizeof(bigPaddleTime) );
    525 	savefile->Read( &paddleVelocity, sizeof(paddleVelocity) );
    526 
    527 	savefile->Read( &ballSpeed, sizeof(ballSpeed) );
    528 	savefile->Read( &ballsRemaining, sizeof(ballsRemaining) );
    529 	savefile->Read( &ballsInPlay, sizeof(ballsInPlay) );
    530 	savefile->Read( &ballHitCeiling, sizeof(ballHitCeiling) );
    531 
    532 	int i;
    533 	int numberOfEnts;
    534 
    535 	// Read entities
    536 	savefile->Read( &numberOfEnts, sizeof(numberOfEnts) );
    537 	for ( i=0; i<numberOfEnts; i++ ) {
    538 		BOEntity *ent;
    539 
    540 		ent = new (TAG_OLD_UI) BOEntity( this );
    541 		ent->ReadFromSaveGame( savefile, this );
    542 		entities.Append( ent );
    543 	}
    544 
    545 	// Read balls
    546 	savefile->Read( &numberOfEnts, sizeof(numberOfEnts) );
    547 	for ( i=0; i<numberOfEnts; i++ ) {
    548 		int ballIndex;
    549 		savefile->Read( &ballIndex, sizeof(ballIndex) );
    550 		balls.Append( entities[ballIndex] );
    551 	}
    552 
    553 	// Read powerups
    554 	savefile->Read( &numberOfEnts, sizeof(numberOfEnts) );
    555 	for ( i=0; i<numberOfEnts; i++ ) {
    556 		int powerIndex;
    557 		savefile->Read( &powerIndex, sizeof(powerIndex) );
    558 		balls.Append( entities[powerIndex] );
    559 	}
    560 
    561 	// Read paddle
    562 	paddle = new (TAG_OLD_UI) BOBrick();
    563 	paddle->ReadFromSaveGame( savefile, this );
    564 
    565 	// Read board
    566 	int row;
    567 	for ( row=0; row<BOARD_ROWS; row++ ) {
    568 		savefile->Read( &numberOfEnts, sizeof(numberOfEnts) );
    569 		for ( i=0; i<numberOfEnts; i++ ) {
    570 			BOBrick *brick = new (TAG_OLD_UI) BOBrick();
    571 			brick->ReadFromSaveGame( savefile, this );
    572 			board[row].Append( brick );
    573 		}
    574 	}
    575 }
    576 
    577 /*
    578 =============================
    579 idGameBustOutWindow::ResetGameState
    580 =============================
    581 */
    582 void idGameBustOutWindow::ResetGameState() {
    583 	gamerunning = false;
    584 	gameOver = false;
    585 	onFire = false;
    586 	onContinue = false;
    587 	onNewGame = false;
    588 	onNewLevel = false;
    589 
    590 	// Game moves forward 16 milliseconds every frame
    591 	timeSlice = 0.016f;
    592 	ballsRemaining = 3;
    593 	ballSpeed = BALL_SPEED;
    594 	ballsInPlay = 0;
    595 	updateScore = false;
    596 	numBricks = 0;
    597 	currentLevel = 1;
    598 	gameScore = 0;
    599 	bigPaddleTime = 0;
    600 	nextBallScore = gameScore + 10000;
    601 
    602 	ClearBoard();
    603 }
    604 
    605 /*
    606 =============================
    607 idGameBustOutWindow::CommonInit
    608 =============================
    609 */
    610 void idGameBustOutWindow::CommonInit() {
    611 	BOEntity *ent;
    612 
    613 	// Precache images
    614 	declManager->FindMaterial( "game/bustout/ball" );
    615 	declManager->FindMaterial( "game/bustout/doublepaddle" );
    616 	declManager->FindMaterial( "game/bustout/powerup_bigpaddle" );
    617 	declManager->FindMaterial( "game/bustout/powerup_multiball" );
    618 	declManager->FindMaterial( "game/bustout/brick" );
    619 
    620 	// Precache sounds
    621 	declManager->FindSound( "arcade_ballbounce" );
    622 	declManager->FindSound( "arcade_brickhit" );
    623 	declManager->FindSound( "arcade_missedball" );
    624 	declManager->FindSound( "arcade_sadsound" );
    625 	declManager->FindSound( "arcade_extraball" );
    626 	declManager->FindSound( "arcade_powerup" );
    627 
    628 	ResetGameState();
    629 
    630 	numLevels = 0;
    631 	boardDataLoaded = false;
    632 	levelBoardData = NULL;
    633 
    634 	// Create Paddle
    635 	ent = new (TAG_OLD_UI) BOEntity( this );
    636 	paddle = new (TAG_OLD_UI) BOBrick( ent, 260.f, 440.f, 96.f, 24.f );
    637 	paddle->ent->SetMaterial( "game/bustout/paddle" );
    638 }
    639 
    640 /*
    641 =============================
    642 idGameBustOutWindow::HandleEvent
    643 =============================
    644 */
    645 const char *idGameBustOutWindow::HandleEvent(const sysEvent_t *event, bool *updateVisuals) {
    646 	int key = event->evValue;
    647 
    648 	// need to call this to allow proper focus and capturing on embedded children
    649 	const char *ret = idWindow::HandleEvent(event, updateVisuals);
    650 
    651 	if ( event->evType == SE_KEY ) {
    652 
    653 		if ( !event->evValue2 ) {
    654 			return ret;
    655 		}
    656 		if ( key == K_MOUSE1) {
    657 			// Mouse was clicked
    658 			if ( ballsInPlay == 0 ) {
    659 				BOEntity *ball = CreateNewBall();
    660 
    661 				ball->SetVisible( true );
    662 				ball->position.x = paddle->ent->position.x + 48.f;
    663 				ball->position.y = 430.f;
    664 
    665 				ball->velocity.x = ballSpeed;
    666 				ball->velocity.y = -ballSpeed*2.f;
    667 				ball->velocity.NormalizeFast();
    668 				ball->velocity *= ballSpeed;
    669 			}
    670 		} else {
    671 			return ret;
    672 		}
    673 	}
    674 
    675 	return ret;
    676 }
    677 
    678 /*
    679 =============================
    680 idGameBustOutWindow::ParseInternalVar
    681 =============================
    682 */
    683 bool idGameBustOutWindow::ParseInternalVar(const char *_name, idTokenParser *src) {
    684 	if ( idStr::Icmp(_name, "gamerunning") == 0 ) {
    685 		gamerunning = src->ParseBool();
    686 		return true;
    687 	}
    688 	if ( idStr::Icmp(_name, "onFire") == 0 ) {
    689 		onFire = src->ParseBool();
    690 		return true;
    691 	}
    692 	if ( idStr::Icmp(_name, "onContinue") == 0 ) {
    693 		onContinue = src->ParseBool();
    694 		return true;
    695 	}
    696 	if ( idStr::Icmp(_name, "onNewGame") == 0 ) {
    697 		onNewGame = src->ParseBool();
    698 		return true;
    699 	}
    700 	if ( idStr::Icmp(_name, "onNewLevel") == 0 ) {
    701 		onNewLevel = src->ParseBool();
    702 		return true;
    703 	}
    704 	if ( idStr::Icmp(_name, "numLevels") == 0 ) {
    705 		numLevels = src->ParseInt();
    706 
    707 		// Load all the level images
    708 		LoadBoardFiles();
    709 		return true;
    710 	}
    711 
    712 	return idWindow::ParseInternalVar(_name, src);
    713 }
    714 
    715 /*
    716 =============================
    717 idGameBustOutWindow::GetWinVarByName
    718 =============================
    719 */
    720 idWinVar *idGameBustOutWindow::GetWinVarByName(const char *_name, bool winLookup, drawWin_t** owner) {
    721 	idWinVar *retVar = NULL;
    722 
    723 	if ( idStr::Icmp(_name, "gamerunning") == 0 ) {
    724 		retVar = &gamerunning;
    725 	} else 	if ( idStr::Icmp(_name, "onFire") == 0 ) {
    726 		retVar = &onFire;
    727 	} else 	if ( idStr::Icmp(_name, "onContinue") == 0 ) {
    728 		retVar = &onContinue;
    729 	} else 	if ( idStr::Icmp(_name, "onNewGame") == 0 ) {
    730 		retVar = &onNewGame;
    731 	} else 	if ( idStr::Icmp(_name, "onNewLevel") == 0 ) {
    732 		retVar = &onNewLevel;
    733 	}
    734 
    735 	if(retVar) {
    736 		return retVar;
    737 	}
    738 
    739 	return idWindow::GetWinVarByName(_name, winLookup, owner);
    740 }
    741 
    742 /*
    743 =============================
    744 idGameBustOutWindow::PostParse
    745 =============================
    746 */
    747 void idGameBustOutWindow::PostParse() {
    748 	idWindow::PostParse();
    749 }
    750 
    751 /*
    752 =============================
    753 idGameBustOutWindow::Draw
    754 =============================
    755 */
    756 void idGameBustOutWindow::Draw(int time, float x, float y) {
    757 	int i;
    758 
    759 	//Update the game every frame before drawing
    760 	UpdateGame();
    761 
    762 	for( i = entities.Num()-1; i >= 0; i-- ) {
    763 		entities[i]->Draw();
    764 	}
    765 }
    766 
    767 /*
    768 =============================
    769 idGameBustOutWindow::Activate
    770 =============================
    771 */
    772 const char *idGameBustOutWindow::Activate(bool activate) {
    773 	return "";
    774 }
    775 
    776 
    777 /*
    778 =============================
    779 idGameBustOutWindow::UpdateScore
    780 =============================
    781 */
    782 void idGameBustOutWindow::UpdateScore() {
    783 
    784 	if ( gameOver ) {
    785 		gui->HandleNamedEvent( "GameOver" );
    786 		return;
    787 	}
    788 
    789 	// Check for level progression
    790 	if ( numBricks == 0 ) {
    791 		ClearBalls();
    792 
    793 		gui->HandleNamedEvent( "levelComplete" );
    794 	}
    795 
    796 	// Check for new ball score
    797 	if ( gameScore >= nextBallScore ) {
    798 		ballsRemaining++;
    799 		gui->HandleNamedEvent( "extraBall" );
    800 
    801 		// Play sound
    802 		common->SW()->PlayShaderDirectly( "arcade_extraball", S_UNIQUE_CHANNEL );
    803 
    804 		nextBallScore = gameScore + 10000;
    805 	}
    806 
    807 	gui->SetStateString( "player_score", va("%i", gameScore ) );
    808 	gui->SetStateString( "balls_remaining", va("%i", ballsRemaining ) );
    809 	gui->SetStateString( "current_level", va("%i", currentLevel ) );
    810 	gui->SetStateString( "next_ball_score", va("%i", nextBallScore ) );
    811 }
    812 
    813 /*
    814 =============================
    815 idGameBustOutWindow::ClearBoard
    816 =============================
    817 */
    818 void idGameBustOutWindow::ClearBoard() {
    819 	int i,j;
    820 
    821 	ClearPowerups();
    822 
    823 	ballHitCeiling = false;
    824 
    825 	for ( i=0; i<BOARD_ROWS; i++ ) {
    826 		for ( j=0; j<board[i].Num(); j++ ) {
    827 
    828 			BOBrick *brick = board[i][j];
    829 			brick->ent->removed = true;
    830 		}
    831 
    832 		board[i].DeleteContents( true );
    833 	}
    834 }
    835 
    836 /*
    837 =============================
    838 idGameBustOutWindow::ClearPowerups
    839 =============================
    840 */
    841 void idGameBustOutWindow::ClearPowerups() {
    842 	while ( powerUps.Num() ) {
    843 		powerUps[0]->removed = true;
    844 		powerUps.RemoveIndex( 0 );
    845 	}
    846 }
    847 
    848 /*
    849 =============================
    850 idGameBustOutWindow::ClearBalls
    851 =============================
    852 */
    853 void idGameBustOutWindow::ClearBalls() {
    854 	while ( balls.Num() ) {
    855 		balls[0]->removed = true;
    856 		balls.RemoveIndex( 0 );
    857 	}
    858 
    859 	ballsInPlay = 0;
    860 }
    861 
    862 /*
    863 =============================
    864 idGameBustOutWindow::LoadBoardFiles
    865 =============================
    866 */
    867 void idGameBustOutWindow::LoadBoardFiles() {
    868 	int i;
    869 	int w,h;
    870 	ID_TIME_T time;
    871 	int boardSize;
    872 	byte *currentBoard;
    873 
    874 	if ( boardDataLoaded ) {
    875 		return;
    876 	}
    877 
    878 	boardSize = 9 * 12 * 4;
    879 	levelBoardData = (byte*)Mem_Alloc( boardSize * numLevels, TAG_CRAP );
    880 
    881 	currentBoard = levelBoardData;
    882 
    883 	for ( i=0; i<numLevels; i++ ) {
    884 		byte *pic;
    885 		idStr	name = "guis/assets/bustout/level";
    886 		name += (i+1);
    887 		name += ".tga";
    888 
    889 		R_LoadImage( name, &pic, &w, &h, &time, false );
    890 
    891 		if ( pic != NULL ) {
    892 			if ( w != 9 || h != 12 ) {
    893 				common->DWarning( "Hell Bust-Out level image not correct dimensions! (%d x %d)", w, h );
    894 			}
    895 
    896 			memcpy( currentBoard, pic, boardSize );
    897 			Mem_Free(pic);
    898 		}
    899 
    900 		currentBoard += boardSize;
    901 	}
    902 
    903 	boardDataLoaded = true;
    904 }
    905 
    906 /*
    907 =============================
    908 idGameBustOutWindow::SetCurrentBoard
    909 =============================
    910 */
    911 void idGameBustOutWindow::SetCurrentBoard() {
    912 	int i,j;
    913 	int realLevel = ((currentLevel-1) % numLevels);
    914 	int boardSize;
    915 	byte *currentBoard;
    916 	float	bx = 11.f;
    917 	float	by = 24.f;
    918 	float	stepx = 619.f / 9.f;
    919 	float	stepy = ( 256 / 12.f );
    920 
    921 	boardSize = 9 * 12 * 4;
    922 	currentBoard = levelBoardData + ( realLevel * boardSize );
    923 
    924 	for ( j=0; j<BOARD_ROWS; j++ ) {
    925 		bx = 11.f;
    926 
    927 		for ( i=0; i<9; i++ ) {
    928 			int pixelindex = (j*9*4) + (i*4);
    929 
    930 			if ( currentBoard[pixelindex + 3] ) {
    931 				idVec4 bcolor;
    932 				float pType = 0.f;
    933 
    934 				BOEntity *bent = new (TAG_OLD_UI) BOEntity( this );
    935 				BOBrick *brick = new (TAG_OLD_UI) BOBrick( bent, bx, by, stepx, stepy );
    936 
    937 				bcolor.x = currentBoard[pixelindex + 0] / 255.f;
    938 				bcolor.y = currentBoard[pixelindex + 1] / 255.f;
    939 				bcolor.z = currentBoard[pixelindex + 2] / 255.f;
    940 				bcolor.w = 1.f;
    941 				brick->SetColor( bcolor );
    942 
    943 				pType = currentBoard[pixelindex + 3] / 255.f;
    944 				if ( pType > 0.f && pType < 1.f ) {
    945 					if ( pType < 0.5f ) {
    946 						brick->powerup = POWERUP_BIGPADDLE;
    947 					} else {
    948 						brick->powerup = POWERUP_MULTIBALL;
    949 					}
    950 				}
    951 
    952 				board[j].Append( brick );
    953 				numBricks++;
    954 			}
    955 
    956 			bx += stepx;
    957 		}
    958 
    959 		by += stepy;
    960 	}
    961 }
    962 
    963 /*
    964 =============================
    965 idGameBustOutWindow::CreateNewBall
    966 =============================
    967 */
    968 BOEntity * idGameBustOutWindow::CreateNewBall() {
    969 	BOEntity *ball;
    970 
    971 	ball = new (TAG_OLD_UI) BOEntity( this );
    972 	ball->position.x = 300.f;
    973 	ball->position.y = 416.f;
    974 	ball->SetMaterial( "game/bustout/ball" );
    975 	ball->SetSize( BALL_RADIUS*2.f, BALL_RADIUS*2.f );
    976 	ball->SetVisible( false );
    977 
    978 	ballsInPlay++;
    979 
    980 	balls.Append( ball );
    981 	entities.Append( ball );
    982 
    983 	return ball;
    984 }
    985 
    986 /*
    987 =============================
    988 idGameBustOutWindow::CreatePowerup
    989 =============================
    990 */
    991 BOEntity * idGameBustOutWindow::CreatePowerup( BOBrick *brick ) {
    992 	BOEntity *powerEnt = new (TAG_OLD_UI) BOEntity( this );
    993 
    994 	powerEnt->position.x = brick->x;
    995 	powerEnt->position.y = brick->y;
    996 	powerEnt->velocity.x = 0.f;
    997 	powerEnt->velocity.y = 64.f;
    998 
    999 	powerEnt->powerup = brick->powerup;
   1000 
   1001 	switch( powerEnt->powerup ) {
   1002 		case POWERUP_BIGPADDLE:
   1003 			powerEnt->SetMaterial( "game/bustout/powerup_bigpaddle" );
   1004 			break;
   1005 		case POWERUP_MULTIBALL:
   1006 			powerEnt->SetMaterial( "game/bustout/powerup_multiball" );
   1007 			break;
   1008 		default:
   1009 			powerEnt->SetMaterial( "textures/common/nodraw" );
   1010 			break;
   1011 	}
   1012 
   1013 	powerEnt->SetSize( 619/9, 256/12 );
   1014 	powerEnt->SetVisible( true );
   1015 
   1016 	powerUps.Append( powerEnt );
   1017 	entities.Append( powerEnt );
   1018 
   1019 	return powerEnt;
   1020 }
   1021 
   1022 /*
   1023 =============================
   1024 idGameBustOutWindow::UpdatePowerups
   1025 =============================
   1026 */
   1027 void idGameBustOutWindow::UpdatePowerups() {
   1028 	idVec2 pos;
   1029 
   1030 	for ( int i=0; i < powerUps.Num(); i++ ) {
   1031 		BOEntity *pUp = powerUps[i];
   1032 
   1033 		// Check for powerup falling below screen
   1034 		if ( pUp->position.y > 480 ) {
   1035 
   1036 			powerUps.RemoveIndex( i );
   1037 			pUp->removed = true;
   1038 			continue;
   1039 		}
   1040 
   1041 		// Check for the paddle catching a powerup
   1042 		pos.x = pUp->position.x + ( pUp->width / 2 );
   1043 		pos.y = pUp->position.y + ( pUp->height / 2 );
   1044 
   1045 		collideDir_t collision = paddle->checkCollision( pos, pUp->velocity );
   1046 		if ( collision != COLLIDE_NONE ) {
   1047 			BOEntity *ball;
   1048 
   1049 			// Give the powerup to the player
   1050 			switch( pUp->powerup ) {
   1051 				case POWERUP_BIGPADDLE:
   1052 					bigPaddleTime = gui->GetTime() + 15000;
   1053 					break;
   1054 				case POWERUP_MULTIBALL:
   1055 					// Create 2 new balls in the spot of the existing ball
   1056 					for ( int b=0; b<2; b++ ) {
   1057 						ball = CreateNewBall();
   1058 						ball->position = balls[0]->position;
   1059 						ball->velocity = balls[0]->velocity;
   1060 
   1061 						if ( b == 0 ) {
   1062 							ball->velocity.x -= 35.f;
   1063 						} else {
   1064 							ball->velocity.x += 35.f;
   1065 						}
   1066 						ball->velocity.NormalizeFast();
   1067 						ball->velocity *= ballSpeed;
   1068 
   1069 						ball->SetVisible( true );
   1070 					}
   1071 					break;
   1072 				default:
   1073 					break;
   1074 			}
   1075 
   1076 			// Play the sound
   1077 			common->SW()->PlayShaderDirectly( "arcade_powerup", S_UNIQUE_CHANNEL );
   1078 
   1079 			// Remove it
   1080 			powerUps.RemoveIndex( i );
   1081 			pUp->removed = true;
   1082 		}
   1083 	}
   1084 }
   1085 
   1086 /*
   1087 =============================
   1088 idGameBustOutWindow::UpdatePaddle
   1089 =============================
   1090 */
   1091 void idGameBustOutWindow::UpdatePaddle() {
   1092 	idVec2 cursorPos;
   1093 	float  oldPos = paddle->x;
   1094 
   1095 	cursorPos.x = gui->CursorX();
   1096 	cursorPos.y = gui->CursorY();
   1097 
   1098 	if ( bigPaddleTime > gui->GetTime() ) {
   1099 		paddle->x = cursorPos.x - 80.f;
   1100 		paddle->width = 160;
   1101 		paddle->ent->width = 160;
   1102 		paddle->ent->SetMaterial( "game/bustout/doublepaddle" );
   1103 	} else {
   1104 		paddle->x = cursorPos.x - 48.f;
   1105 		paddle->width = 96;
   1106 		paddle->ent->width = 96;
   1107 		paddle->ent->SetMaterial( "game/bustout/paddle" );
   1108 	}
   1109 	paddle->ent->position.x = paddle->x;
   1110 
   1111 	paddleVelocity = (paddle->x - oldPos);
   1112 }
   1113 
   1114 /*
   1115 =============================
   1116 idGameBustOutWindow::UpdateBall
   1117 =============================
   1118 */
   1119 void idGameBustOutWindow::UpdateBall() {
   1120 	int ballnum,i,j;
   1121 	bool playSoundBounce = false;
   1122 	bool playSoundBrick = false;
   1123 	static int bounceChannel = 1;
   1124 
   1125 	if ( ballsInPlay == 0 ) {
   1126 		return;
   1127 	}
   1128 
   1129 	for ( ballnum = 0; ballnum < balls.Num(); ballnum++ ) {
   1130 		BOEntity *ball = balls[ballnum];
   1131 
   1132 		// Check for ball going below screen, lost ball
   1133 		if ( ball->position.y > 480.f ) {
   1134 			ball->removed = true;
   1135 			continue;
   1136 		}
   1137 
   1138 		// Check world collision
   1139 		if ( ball->position.y < 20 && ball->velocity.y < 0 ) {
   1140 			ball->velocity.y = -ball->velocity.y;
   1141 
   1142 			// Increase ball speed when it hits ceiling
   1143 			if ( !ballHitCeiling ) {
   1144 				ballSpeed *= 1.25f;
   1145 				ballHitCeiling = true;
   1146 			}
   1147 			playSoundBounce = true;
   1148 		}
   1149 
   1150 		if ( ball->position.x > 608 && ball->velocity.x > 0 ) {
   1151 			ball->velocity.x = -ball->velocity.x;
   1152 			playSoundBounce = true;
   1153 		} else if ( ball->position.x < 8 && ball->velocity.x < 0 ) {
   1154 			ball->velocity.x = -ball->velocity.x;
   1155 			playSoundBounce = true;
   1156 		}
   1157 
   1158 		// Check for Paddle collision
   1159 		idVec2 ballCenter = ball->position + idVec2( BALL_RADIUS, BALL_RADIUS );
   1160 		collideDir_t collision = paddle->checkCollision( ballCenter, ball->velocity );
   1161 
   1162 		if ( collision == COLLIDE_UP ) {
   1163 			if ( ball->velocity.y > 0 ) {
   1164 				idVec2	paddleVec( paddleVelocity*2, 0 );
   1165 				float	centerX;
   1166 				
   1167 				if ( bigPaddleTime > gui->GetTime() ) {
   1168 					centerX = paddle->x + 80.f;
   1169 				} else {
   1170 					centerX = paddle->x + 48.f;
   1171 				}
   1172 
   1173 				ball->velocity.y = -ball->velocity.y;
   1174 
   1175 				paddleVec.x += (ball->position.x - centerX) * 2;
   1176 
   1177 				ball->velocity += paddleVec;
   1178 				ball->velocity.NormalizeFast();
   1179 				ball->velocity *= ballSpeed;
   1180 
   1181 				playSoundBounce = true;
   1182 			}
   1183 		} else if ( collision == COLLIDE_LEFT || collision == COLLIDE_RIGHT ) {
   1184 			if ( ball->velocity.y > 0 ) {
   1185 				ball->velocity.x = -ball->velocity.x;
   1186 				playSoundBounce = true;
   1187 			}
   1188 		}
   1189 
   1190 		collision = COLLIDE_NONE;
   1191 
   1192 		// Check for collision with bricks
   1193 		for ( i=0; i<BOARD_ROWS; i++ ) {
   1194 			int num = board[i].Num();
   1195 
   1196 			for ( j=0; j<num; j++ ) {
   1197 				BOBrick *brick = (board[i])[j];
   1198 
   1199 				collision = brick->checkCollision( ballCenter, ball->velocity );
   1200 				if ( collision ) {
   1201 					// Now break the brick if there was a collision
   1202 					brick->isBroken = true;
   1203 					brick->ent->fadeOut = true;
   1204 
   1205 					if ( brick->powerup > POWERUP_NONE ) {
   1206 						verify( CreatePowerup( brick ) != NULL );
   1207 					}
   1208 
   1209 					numBricks--;
   1210 					gameScore += 100;
   1211 					updateScore = true;
   1212 
   1213 					// Go ahead an forcibly remove the last brick, no fade
   1214 					if ( numBricks == 0 ) {
   1215 						brick->ent->removed = true;
   1216 					}
   1217 					board[i].Remove( brick );
   1218 					break;
   1219 				}
   1220 			}
   1221 
   1222 			if ( collision ) {
   1223 				playSoundBrick = true;
   1224 				break;
   1225 			}
   1226 		}
   1227 
   1228 		if ( collision == COLLIDE_DOWN || collision == COLLIDE_UP ) {
   1229 			ball->velocity.y *= -1;
   1230 		} else if ( collision == COLLIDE_LEFT || collision == COLLIDE_RIGHT ) {
   1231 			ball->velocity.x *= -1;
   1232 		}
   1233 
   1234 		if ( playSoundBounce ) {
   1235 			common->SW()->PlayShaderDirectly( "arcade_ballbounce", bounceChannel );
   1236 		} else if ( playSoundBrick ) {
   1237 			common->SW()->PlayShaderDirectly( "arcade_brickhit", bounceChannel );
   1238 		}
   1239 
   1240 		if ( playSoundBounce || playSoundBrick ) {
   1241 			bounceChannel++;
   1242 			if ( bounceChannel == 4 ) {
   1243 				bounceChannel = 1;
   1244 			}
   1245 		}
   1246 	}
   1247 
   1248 	// Check to see if any balls were removed from play
   1249 	for ( ballnum=0; ballnum<balls.Num(); ballnum++ ) {
   1250 		if ( balls[ballnum]->removed ) {
   1251 			ballsInPlay--;
   1252 			balls.RemoveIndex( ballnum );
   1253 		}
   1254 	}
   1255 
   1256 	// If all the balls were removed, update the game accordingly
   1257 	if ( ballsInPlay == 0 ) {
   1258 		if ( ballsRemaining == 0 ) {
   1259 			gameOver = true;
   1260 
   1261 			// Game Over sound
   1262 			common->SW()->PlayShaderDirectly( "arcade_sadsound", S_UNIQUE_CHANNEL );
   1263 		} else {
   1264 			ballsRemaining--;
   1265 
   1266 			// Ball was lost, but game is not over
   1267 			common->SW()->PlayShaderDirectly( "arcade_missedball", S_UNIQUE_CHANNEL );
   1268 		}
   1269 
   1270 		ClearPowerups();
   1271 		updateScore = true;
   1272 	}
   1273 }
   1274 
   1275 /*
   1276 =============================
   1277 idGameBustOutWindow::UpdateGame
   1278 =============================
   1279 */
   1280 void idGameBustOutWindow::UpdateGame() {
   1281 	int i;
   1282 
   1283 	if ( onNewGame ) {
   1284 		ResetGameState();
   1285 
   1286 		// Create Board
   1287 		SetCurrentBoard();
   1288 
   1289 		gamerunning = true;
   1290 	}
   1291 	if ( onContinue ) {
   1292 		gameOver = false;
   1293 		ballsRemaining = 3;
   1294 
   1295 		onContinue = false;
   1296 	}
   1297 	if ( onNewLevel ) {
   1298 		currentLevel++;
   1299 
   1300 		ClearBoard();
   1301 		SetCurrentBoard();
   1302 
   1303 		ballSpeed = BALL_SPEED * ( 1.f + ((float)currentLevel/5.f) );
   1304 		if ( ballSpeed > BALL_MAXSPEED ) {
   1305 			ballSpeed = BALL_MAXSPEED;
   1306 		}
   1307 		updateScore = true;
   1308 		onNewLevel = false;
   1309 	}
   1310 
   1311 	if(gamerunning == true) {
   1312 
   1313 		UpdatePaddle();
   1314 		UpdateBall();
   1315 		UpdatePowerups();
   1316 
   1317 		for( i = 0; i < entities.Num(); i++ ) {
   1318 			entities[i]->Update( timeSlice, gui->GetTime() );
   1319 		}
   1320 
   1321 		// Delete entities that need to be deleted
   1322 		for( i = entities.Num()-1; i >= 0; i-- ) {
   1323 			if( entities[i]->removed ) {
   1324 				BOEntity* ent = entities[i];
   1325 				delete ent;
   1326 				entities.RemoveIndex(i);
   1327 			}
   1328 		}
   1329 
   1330 		if ( updateScore ) {
   1331 			UpdateScore();
   1332 			updateScore = false;
   1333 		}
   1334 	}
   1335 }