Game.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. * This source file is part of libRocket, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://www.librocket.com
  5. *
  6. * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. *
  26. */
  27. #include "Game.h"
  28. #include <Rocket/Core.h>
  29. #include <Shell.h>
  30. #include <ShellOpenGL.h>
  31. #include "Defender.h"
  32. #include "GameDetails.h"
  33. #include "HighScores.h"
  34. #include "Invader.h"
  35. #include "Mothership.h"
  36. #include "Shield.h"
  37. const int WINDOW_WIDTH = 1024;
  38. const int WINDOW_HEIGHT = 768;
  39. const int NUM_LIVES = 3;
  40. const int NUM_INVADER_ROWS = 5;
  41. const int NUM_INVADERS_PER_ROW = 11;
  42. const int NUM_INVADERS = (NUM_INVADER_ROWS * NUM_INVADERS_PER_ROW);
  43. const int INVADER_SPACING_X = 64;
  44. const int INVADER_SPACING_Y = 48;
  45. const int INVADER_START_Y = 96;
  46. const float INVADER_MOVEMENT = 10;
  47. const float INVADER_START_MOVE_FREQ = 0.5f;
  48. const float INVADER_UPDATE_MODIFIER = 0.7f;
  49. const int MOTHERSHIP = NUM_INVADERS;
  50. const int NUM_SHIELD_ARRAYS = 4;
  51. const int NUM_SHIELDS_PER_ARRAY = 10;
  52. const int NUM_SHIELDS = (NUM_SHIELD_ARRAYS * NUM_SHIELDS_PER_ARRAY);
  53. const int SHIELD_SIZE = (NUM_SHIELD_CELLS * PIXEL_SIZE);
  54. const int SHIELD_SPACING_X = 192;
  55. const int SHIELD_START_X = 176;
  56. const int SHIELD_START_Y = 600;
  57. // The game's element context (declared in main.cpp).
  58. extern Rocket::Core::Context* context;
  59. Game::Game()
  60. {
  61. invader_frame_start = 0;
  62. defender_lives = 3;
  63. game_over = false;
  64. current_invader_direction = 1.0f;
  65. invaders = new Invader*[NUM_INVADERS + 1];
  66. for (int i = 0; i < NUM_INVADERS + 1; i++)
  67. invaders[i] = NULL;
  68. shields = new Shield*[NUM_SHIELDS];
  69. for (int i = 0; i < NUM_SHIELDS; i++)
  70. shields[i] = NULL;
  71. // Use the OpenGL render interface to load our texture.
  72. Rocket::Core::Vector2i texture_dimensions;
  73. Rocket::Core::GetRenderInterface()->LoadTexture(texture, texture_dimensions, "data/invaders.tga");
  74. defender = new Defender(this);
  75. }
  76. Game::~Game()
  77. {
  78. delete defender;
  79. for (int i = 0; i < NUM_INVADERS + 1; i++)
  80. delete invaders[i];
  81. delete [] invaders;
  82. for (int i = 0; i < NUM_SHIELDS; i++)
  83. delete shields[i];
  84. delete [] shields;
  85. }
  86. void Game::Initialise()
  87. {
  88. // Initialise the scores and wave information
  89. SetScore(0);
  90. SetWave(1);
  91. SetHighScore(HighScores::GetHighScore());
  92. SetLives(NUM_LIVES);
  93. // Initialise the shields.
  94. InitialiseShields();
  95. // Create a new wave
  96. InitialiseWave();
  97. }
  98. void Game::Update()
  99. {
  100. if (!GameDetails::GetPaused())
  101. {
  102. if (defender_lives <= 0)
  103. return;
  104. // Determine if we should advance the invaders
  105. if (Shell::GetElapsedTime() - invader_frame_start >= invader_move_freq)
  106. {
  107. MoveInvaders();
  108. invader_frame_start = Shell::GetElapsedTime();
  109. }
  110. // Update all invaders
  111. for (int i = 0; i < NUM_INVADERS + 1; i++)
  112. invaders[i]->Update();
  113. defender->Update();
  114. }
  115. }
  116. void Game::Render()
  117. {
  118. if (defender_lives <= 0)
  119. return;
  120. // Render all available shields
  121. for (int i = 0; i < NUM_SHIELDS; i++)
  122. {
  123. shields[i]->Render();
  124. }
  125. glEnable(GL_TEXTURE_2D);
  126. glBindTexture(GL_TEXTURE_2D, (GLuint) texture);
  127. glColor4ub(255, 255, 255, 255);
  128. glBegin(GL_QUADS);
  129. // Render all available invaders
  130. for (int i = 0; i < NUM_INVADERS + 1; i++)
  131. {
  132. invaders[i]->Render();
  133. }
  134. defender->Render();
  135. glEnd();
  136. }
  137. Defender* Game::GetDefender()
  138. {
  139. return defender;
  140. }
  141. Invader* Game::GetInvader(int index)
  142. {
  143. return invaders[index];
  144. }
  145. int Game::GetNumInvaders()
  146. {
  147. return NUM_INVADERS + 1;
  148. }
  149. bool Game::CanDropBomb(int index)
  150. {
  151. // Determines if the given invader can drop a bomb by checking if theres any other invaders under it
  152. int y = (index / NUM_INVADERS_PER_ROW);
  153. int x = index - (y * NUM_INVADERS_PER_ROW);
  154. // The mothership can never drop bombs
  155. if (index == MOTHERSHIP)
  156. return false;
  157. // Check each row under the given invader to see if there is space
  158. for (int i = y + 1; i < NUM_INVADER_ROWS; i++)
  159. {
  160. if (invaders[(i * NUM_INVADERS_PER_ROW) + x]->GetState() == Invader::ALIVE)
  161. return false;
  162. }
  163. return true;
  164. }
  165. // Access the shields.
  166. Shield* Game::GetShield(int index)
  167. {
  168. return shields[index];
  169. }
  170. // Get the total number of shields
  171. int Game::GetNumShields()
  172. {
  173. return NUM_SHIELDS;
  174. }
  175. // Adds a score onto the player's score.
  176. void Game::AddScore(int score)
  177. {
  178. SetScore(GameDetails::GetScore() + score);
  179. }
  180. // Sets the player's score.
  181. void Game::SetScore(int score)
  182. {
  183. GameDetails::SetScore(score);
  184. Rocket::Core::Element* score_element = context->GetDocument("game_window")->GetElementById("score");
  185. if (score_element != NULL)
  186. score_element->SetInnerRML(Rocket::Core::String(128, "%d", score).CString());
  187. // Update the high score if we've beaten it.
  188. if (score > HighScores::GetHighScore())
  189. SetHighScore(score);
  190. }
  191. // Sets the player's high-score.
  192. void Game::SetHighScore(int score)
  193. {
  194. Rocket::Core::Element* high_score_element = context->GetDocument("game_window")->GetElementById("hiscore");
  195. if (high_score_element != NULL)
  196. high_score_element->SetInnerRML(Rocket::Core::String(128, "%d", score).CString());
  197. }
  198. void Game::SetLives(int lives)
  199. {
  200. defender_lives = lives;
  201. Rocket::Core::Element* score_element = context->GetDocument("game_window")->GetElementById("lives");
  202. if (score_element != NULL)
  203. score_element->SetInnerRML(Rocket::Core::String(128, "%d", defender_lives).CString());
  204. }
  205. void Game::SetWave(int wave)
  206. {
  207. GameDetails::SetWave(wave);
  208. Rocket::Core::Element* waves_element = context->GetDocument("game_window")->GetElementById("waves");
  209. if (waves_element != NULL)
  210. waves_element->SetInnerRML(Rocket::Core::String(128, "%d", wave).CString());
  211. }
  212. void Game::RemoveLife()
  213. {
  214. if (defender_lives > 0)
  215. {
  216. SetLives(defender_lives - 1);
  217. if (defender_lives == 0)
  218. {
  219. OnGameOver();
  220. }
  221. }
  222. }
  223. bool Game::IsGameOver() const
  224. {
  225. return game_over;
  226. }
  227. const Rocket::Core::Vector2f Game::GetWindowDimensions()
  228. {
  229. return Rocket::Core::Vector2f((float) WINDOW_WIDTH, (float) WINDOW_HEIGHT);
  230. }
  231. void Game::MoveInvaders()
  232. {
  233. Rocket::Core::Vector2f new_positions[NUM_INVADERS];
  234. // We loop through all invaders, calculating their new positions, if any of them go over the screen bounds,
  235. // then we switch direction, move the invaders down and start at 0 again
  236. for (int i = 0; i < NUM_INVADERS; i++)
  237. {
  238. if (invaders[i]->GetState() == Invader::DEAD)
  239. continue;
  240. new_positions[i] = invaders[i]->GetPosition();
  241. new_positions[i].x += INVADER_MOVEMENT * current_invader_direction;
  242. if (new_positions[i].x < 0 || new_positions[i].x + INVADER_SPACING_X > WINDOW_WIDTH)
  243. {
  244. // Switch direction and start back at 0 (-1 as the for loop increments)
  245. current_invader_direction *= -1.0f;
  246. i = -1;
  247. // Move all invaders down
  248. for (int j = 0; j < NUM_INVADERS; j++)
  249. {
  250. if (invaders[j]->GetState() == Invader::DEAD)
  251. continue;
  252. Rocket::Core::Vector2f position = invaders[j]->GetPosition();
  253. position.y += INVADER_SPACING_Y;
  254. invaders[j]->SetPosition(position);
  255. }
  256. // Increase speed of invaders
  257. invader_move_freq *= INVADER_UPDATE_MODIFIER;
  258. }
  259. }
  260. // Assign invaders their new position and advance their animation frame
  261. bool invaders_alive = false;
  262. for (int i = 0; i < NUM_INVADERS; i++)
  263. {
  264. if (invaders[i]->GetState() == Invader::DEAD)
  265. continue;
  266. invaders[i]->SetPosition(new_positions[i]);
  267. invaders[i]->UpdateAnimation();
  268. // If an invader hits the bottom, instant death
  269. if (new_positions[i].y >= GetWindowDimensions().y - INVADER_SPACING_Y)
  270. {
  271. OnGameOver();
  272. return;
  273. }
  274. invaders_alive = true;
  275. }
  276. if (!invaders_alive)
  277. {
  278. SetWave(GameDetails::GetWave() + 1);
  279. InitialiseWave();
  280. }
  281. }
  282. void Game::OnGameOver()
  283. {
  284. // Set lives to zero and continue to the high scores
  285. defender_lives = 0;
  286. game_over = true;
  287. }
  288. void Game::InitialiseShields()
  289. {
  290. Rocket::Core::Vector2f shield_array_start_position((float) SHIELD_START_X, (float) SHIELD_START_Y);
  291. for (int x = 0; x < NUM_SHIELD_ARRAYS; x++)
  292. {
  293. // Top row (row of 4)
  294. for (int i = 0; i < 4; i++)
  295. {
  296. Shield::ShieldType type = Shield::REGULAR;
  297. if (i == 0)
  298. type = Shield::TOP_LEFT;
  299. else if (i == 3)
  300. {
  301. type = Shield::TOP_RIGHT;
  302. }
  303. shields[(x * NUM_SHIELDS_PER_ARRAY) + i] = new Shield(this, type);
  304. shields[(x * NUM_SHIELDS_PER_ARRAY) + i]->SetPosition(shield_array_start_position + Rocket::Core::Vector2f((float) SHIELD_SIZE * i, 0));
  305. }
  306. // Middle row (row of 4)
  307. for (int i = 0; i < 4; i++)
  308. {
  309. Shield::ShieldType type = Shield::REGULAR;
  310. if (i == 1)
  311. type = Shield::BOTTOM_RIGHT;
  312. else if (i == 2)
  313. {
  314. type = Shield::BOTTOM_LEFT;
  315. }
  316. shields[(x * NUM_SHIELDS_PER_ARRAY) + 4 + i] = new Shield(this, type);
  317. shields[(x * NUM_SHIELDS_PER_ARRAY) + 4 + i]->SetPosition(shield_array_start_position + Rocket::Core::Vector2f((float) SHIELD_SIZE * i, (float) SHIELD_SIZE));
  318. }
  319. // Bottom row (2, one on each end)
  320. shields[(x * NUM_SHIELDS_PER_ARRAY) + 8] = new Shield(this, Shield::REGULAR);
  321. shields[(x * NUM_SHIELDS_PER_ARRAY) + 8]->SetPosition(shield_array_start_position + Rocket::Core::Vector2f(0, (float) SHIELD_SIZE * 2));
  322. shields[(x * NUM_SHIELDS_PER_ARRAY) + 9] = new Shield(this, Shield::REGULAR);
  323. shields[(x * NUM_SHIELDS_PER_ARRAY) + 9]->SetPosition(shield_array_start_position + Rocket::Core::Vector2f((float) SHIELD_SIZE * 3, (float) SHIELD_SIZE * 2));
  324. shield_array_start_position.x += SHIELD_SPACING_X;
  325. }
  326. }
  327. void Game::InitialiseWave()
  328. {
  329. // Set up the rows
  330. for (int y = 0; y < NUM_INVADER_ROWS; y++)
  331. {
  332. // Determine invader type
  333. Invader::InvaderType type = Invader::UNKNOWN;
  334. switch (y)
  335. {
  336. case 0:
  337. type = Invader::RANK3; break;
  338. case 1:
  339. case 2:
  340. type = Invader::RANK2; break;
  341. default:
  342. type = Invader::RANK1; break;
  343. }
  344. // Determine position of top left invader
  345. Rocket::Core::Vector2f invader_position((float) (WINDOW_WIDTH - (NUM_INVADERS_PER_ROW * INVADER_SPACING_X)) / 2, (float) (INVADER_START_Y + (y * INVADER_SPACING_Y)));
  346. for (int x = 0; x < NUM_INVADERS_PER_ROW; x++)
  347. {
  348. // Determine invader type based on row position
  349. int index = (y * NUM_INVADERS_PER_ROW) + x;
  350. delete invaders[index];
  351. invaders[index] = new Invader(this, type, (y * NUM_INVADERS_PER_ROW) + x);
  352. invaders[index]->SetPosition(invader_position);
  353. // Increase invader position
  354. invader_position.x += INVADER_SPACING_X;
  355. }
  356. }
  357. // Reset mother ship
  358. delete invaders[MOTHERSHIP];
  359. invaders[MOTHERSHIP] = new Mothership(this, MOTHERSHIP);
  360. // Update the move frequency
  361. invader_move_freq = ((((float)GameDetails::GetWave())-100.0f)/140.0f);
  362. invader_move_freq *= invader_move_freq;
  363. }