GameBustOutWindow.cpp 31 KB

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