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( ¤tLevel, 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( ¤tLevel, 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