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