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