PageRenderTime 58ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Window.cpp

https://github.com/gigaplex/FlyingStringDefence
C++ | 359 lines | 287 code | 20 blank | 52 comment | 54 complexity | 52dbb7e066729bba4f84efea913109ad MD5 | raw file
  1. /** \file Window.cpp
  2. * \brief Code implementation for Window class
  3. *
  4. * \author Tim Boundy
  5. * \date May 2007
  6. */
  7. #include "Window.h"
  8. #include <Fl/Fl.H>
  9. #include <Fl/fl_draw.h>
  10. #include <Fl/Fl_Timer.H>
  11. #include <math.h>
  12. #include <time.h>
  13. #ifdef WIN32
  14. #include <windows.h>
  15. #endif
  16. #include "Gun.h"
  17. #include "Base.h"
  18. #include "Shell.h"
  19. #include "Missile.h"
  20. #include "UFO.h"
  21. #include "ShellExplosion.h"
  22. /** \brief Constructor for Window
  23. *
  24. * The constructor seeds the random number generator, initialises some strings and
  25. * sets the gamestate to INITIALISE, as well as creating the Fl_Double_Window.
  26. */
  27. Window::Window(int w, int h, const char *l = 0) : Fl_Double_Window(w, h, l)
  28. {
  29. srand(time(NULL));
  30. strlvl = "Level: ";
  31. strscr = "Score: ";
  32. gamestate = INITIALISE;
  33. }
  34. /** \brief Destructor for Window
  35. *
  36. * The destructor calls the cleanup routine.
  37. */
  38. Window::~Window()
  39. {
  40. cleanup();
  41. }
  42. /* \brief A function to start the game.
  43. *
  44. * This routine starts the game by initialising the score and level, spawning the
  45. * PlayerItem objects and setting the start time to current time.
  46. */
  47. void Window::start_game()
  48. {
  49. gamestate = NORMAL;
  50. lasttime = get_time();
  51. score = 0;
  52. level = 1;
  53. new Base(WIN_WIDTH/2 + SEPARATION/2, WIN_HEIGHT - GROUND_HEIGHT);
  54. new Base(WIN_WIDTH/2 - SEPARATION/2, WIN_HEIGHT - GROUND_HEIGHT);
  55. new Base(WIN_WIDTH/2 + 3*SEPARATION/2, WIN_HEIGHT - GROUND_HEIGHT);
  56. new Base(WIN_WIDTH/2 - 3*SEPARATION/2, WIN_HEIGHT - GROUND_HEIGHT);
  57. new Gun(WIN_WIDTH/2, WIN_HEIGHT-GROUND_HEIGHT, BARREL_WIDTH);
  58. new Gun(WIN_WIDTH/2 + SEPARATION, WIN_HEIGHT-GROUND_HEIGHT, BARREL_WIDTH);
  59. new Gun(WIN_WIDTH/2 - SEPARATION, WIN_HEIGHT-GROUND_HEIGHT, BARREL_WIDTH);
  60. }
  61. /** \brief Cleanup function.
  62. *
  63. * This function deletes all objects from the game.
  64. */
  65. void Window::cleanup()
  66. {
  67. int i = 0;
  68. while(PlayerItem::playeritems().size() > 0)
  69. {
  70. delete *PlayerItem::playeritems().begin();
  71. #ifdef DEBUG
  72. cout << "deleted shootable " << i << endl;
  73. i++;
  74. #endif
  75. }
  76. i = 0;
  77. while(Projectile::projectiles().size() > 0)
  78. {
  79. delete *Projectile::projectiles().begin();
  80. #ifdef DEBUG
  81. cout << "deleted projectile " << i << endl;
  82. i++;
  83. #endif
  84. }
  85. i = 0;
  86. while(Explosion::explosions().size() > 0)
  87. {
  88. delete *Explosion::explosions().begin();
  89. #ifdef DEBUG
  90. cout << "deleted explosion " << i << endl;
  91. i++;
  92. #endif
  93. }
  94. }
  95. /** \brief Drawing routine for the game.
  96. *
  97. * This routine draws the Window and calls the draw functions for all
  98. * the objects in the game. It also draws some messages to the screen
  99. * depending on the current state of the game.
  100. */
  101. void Window::draw()
  102. {
  103. fl_color(SKY_BLUE);
  104. fl_rectf(0, 0, WIN_WIDTH, WIN_HEIGHT);
  105. if (gamestate == INITIALISE)
  106. {
  107. int x = 0;
  108. int y = 0;
  109. strstatus = "Press Esc to start a new game.";
  110. fl_color(FL_BLACK);
  111. fl_font(FL_HELVETICA,30);
  112. fl_measure(strstatus.c_str(), x, y);
  113. fl_draw(strstatus.c_str(), (WIN_WIDTH-x)/2,(WIN_HEIGHT+y-30)/2);
  114. strstatus = "Press Esc during the game to pause.";
  115. x = 0;
  116. y = 0;
  117. fl_measure(strstatus.c_str(), x, y);
  118. fl_draw(strstatus.c_str(), (WIN_WIDTH-x)/2,(WIN_HEIGHT+y+30)/2);
  119. }
  120. else if (gamestate == GAMEOVER)
  121. {
  122. int x = 0;
  123. int y = 0;
  124. strstatus = "You Lose! Press Esc to start a new game.";
  125. fl_color(FL_BLACK);
  126. fl_font(FL_HELVETICA,30);
  127. fl_measure(strstatus.c_str(), x, y);
  128. fl_draw(strstatus.c_str(), (WIN_WIDTH-x)/2,(WIN_HEIGHT+y-30)/2);
  129. oss.str("");
  130. oss << "Score was " << score << ", died on level " << level << ".";
  131. x = 0;
  132. y = 0;
  133. fl_measure(oss.str().c_str(), x, y);
  134. fl_draw(oss.str().c_str(), (WIN_WIDTH-x)/2,(WIN_HEIGHT+y+30)/2);
  135. }
  136. else // normal game operation
  137. {
  138. for (unsigned int i = 0; i < Projectile::projectiles().size(); i++)
  139. {
  140. Projectile::projectiles()[i]->draw();
  141. }
  142. for (unsigned int i = 0; i < Explosion::explosions().size(); i++)
  143. {
  144. Explosion::explosions()[i]->draw();
  145. }
  146. fl_color(GRASS);
  147. fl_rectf(0, WIN_HEIGHT-GROUND_HEIGHT, WIN_WIDTH, WIN_HEIGHT);
  148. for (unsigned int i = 0; i < PlayerItem::playeritems().size(); i++)
  149. {
  150. PlayerItem::playeritems()[i]->draw();
  151. }
  152. fl_color(FL_BLACK);
  153. fl_font(FL_HELVETICA,20);
  154. level = 1 + score / SCORE_NEXT_LEVEL;
  155. oss.str("");
  156. oss << strlvl << level;
  157. fl_draw(oss.str().c_str(), 40,40);
  158. oss.str("");
  159. oss << strscr << score;
  160. fl_draw(oss.str().c_str(), 40,60);
  161. if (gamestate == PAUSED)
  162. {
  163. int x = 0;
  164. int y = 0;
  165. strstatus = "Paused... Press Esc to Continue.";
  166. fl_color(FL_BLACK);
  167. fl_font(FL_HELVETICA,30);
  168. fl_measure(strstatus.c_str(), x, y);
  169. fl_draw(strstatus.c_str(), (WIN_WIDTH-x)/2,(WIN_HEIGHT+y)/2);
  170. }
  171. }
  172. }
  173. /** \brief Animation function for the game.
  174. *
  175. * This function calls the animate and collision routines for the various objects in the game.
  176. */
  177. void Window::animate()
  178. {
  179. currenttime = get_time();
  180. //Spawn UFO
  181. if (rand() % (int)UFO_SPAWN_FACTOR/LEVEL_SCALE == 0)
  182. {
  183. new UFO(0, 100, WIN_WIDTH + 50, 100, UFO_VELOCITY*LEVEL_SCALE);
  184. }
  185. //Spawn Missile
  186. if ((Missile::missiles().size() < MAX_MISSILES) && (rand() % (int)(MISSILE_SPAWN_FACTOR/LEVEL_SCALE) == 0))
  187. {
  188. if (PlayerItem::playeritems().size() != 0)
  189. {
  190. int index = rand() % PlayerItem::playeritems().size();
  191. PlayerItem *target = PlayerItem::playeritems()[index];
  192. new Missile((double)(rand() % WIN_WIDTH), 0.0, target->x(), target->y(), MISSILE_VELOCITY*LEVEL_SCALE);
  193. }
  194. }
  195. //UFO action
  196. for (int i = 0; i < (int)UFO::ufos().size(); i++)
  197. {
  198. if (UFO::ufos()[i]->animate(currenttime - lasttime, level))
  199. {
  200. delete UFO::ufos()[i];
  201. i--;
  202. }
  203. }
  204. //Missile action
  205. for (int i = 0; i < (int)Missile::missiles().size(); i++)
  206. {
  207. if ((Missile::missiles()[i]->collision_detect() == true) || (Missile::missiles()[i]->animate(currenttime - lasttime)))
  208. {
  209. #ifdef DEBUG2
  210. cout << "missile " << i << " action" << endl;
  211. #endif
  212. Missile::missiles()[i]->on_death();
  213. delete Missile::missiles()[i];
  214. i--;
  215. }
  216. }
  217. //Shell action
  218. for (int i = 0; i < (int)Shell::shells().size(); i++)
  219. {
  220. if (Shell::shells()[i]->animate(currenttime - lasttime))
  221. {
  222. #ifdef DEBUG2
  223. cout << "shell " << i << " action" << endl;
  224. #endif
  225. Shell::shells()[i]->on_death();
  226. delete Shell::shells()[i];
  227. i--;
  228. }
  229. }
  230. //Explosion animate
  231. for (int i = 0; i < (int)Explosion::explosions().size(); i++)
  232. {
  233. if (Explosion::explosions()[i]->animate(currenttime - lasttime))
  234. {
  235. delete Explosion::explosions()[i];
  236. i--;
  237. }
  238. }
  239. //ShellExplosion collision
  240. for (int i = 0; i < (int)ShellExplosion::shellexplosions().size(); i++)
  241. {
  242. score = ShellExplosion::shellexplosions()[i]->collision_detect(score);
  243. if (ShellExplosion::shellexplosions()[i]->timealive() > 1)
  244. {
  245. delete ShellExplosion::shellexplosions()[i];
  246. }
  247. }
  248. lasttime = currenttime;
  249. }
  250. /** \brief Hanler function for the game.
  251. *
  252. * This function handles the various events that FLTK generates as well as the
  253. * timer event generated in main.cpp. The Esc key is used as a rudimentary menu
  254. * system and the mouse controls the Gun objects.
  255. * \param e Event number
  256. * \return Returns 1 if event handled, otherwise return what Fl_Group::handle returned
  257. */
  258. int Window::handle(int e)
  259. {
  260. int ret = Fl_Group::handle(e);
  261. #ifdef DEBUG // toggle gun powerup with tab button in debug mode
  262. if ((e == FL_KEYDOWN) && (Fl::event_key() == FL_Tab))
  263. {
  264. for (int i = 0; i < Gun::guns().size(); i++)
  265. {
  266. Gun::guns()[i]->barrel_width(30-Gun::guns()[i]->barrel_width());
  267. }
  268. }
  269. #endif
  270. if ((e == FL_KEYDOWN) && (Fl::event_key() == FL_Escape))
  271. {
  272. switch (gamestate)
  273. {
  274. case INITIALISE: // start game
  275. start_game();
  276. break;
  277. case NORMAL: // pause game
  278. gamestate = PAUSED;
  279. break;
  280. case PAUSED: // unpause game
  281. gamestate = NORMAL;
  282. // reset last time because a large period of time may have passed in pause mode that shouldn't be counted
  283. lasttime = get_time();
  284. break;
  285. case GAMEOVER: // restart game
  286. cleanup();
  287. start_game();
  288. break;
  289. default:
  290. break;
  291. }
  292. damage(FL_DAMAGE_ALL);
  293. return 1;
  294. }
  295. // if player has run out of guns or bases, game over
  296. if (((Gun::guns().size() == 0) || (Base::bases().size() == 0)) && (gamestate == NORMAL))
  297. {
  298. gamestate = GAMEOVER;
  299. cleanup();
  300. damage(FL_DAMAGE_ALL);
  301. return 1;
  302. }
  303. if (gamestate == NORMAL) // handle mouse and timer events
  304. {
  305. switch (e)
  306. {
  307. case TIMER_CALLBACK:
  308. animate();
  309. damage(FL_DAMAGE_ALL);
  310. return 1;
  311. case FL_DRAG:
  312. //do the same for move and drag
  313. case FL_MOVE:
  314. for (unsigned int i = 0; i < Gun::guns().size(); i++)
  315. {
  316. Gun::guns()[i]->target(Fl::event_x(), Fl::event_y());
  317. }
  318. damage(FL_DAMAGE_ALL);
  319. return 1;
  320. case FL_PUSH:
  321. for (unsigned int i = 0; i < Gun::guns().size(); i++)
  322. {
  323. if ((Gun::guns()[i]->target_valid() == true) && (Shell::shells().size() < MAX_SHELLS))
  324. {
  325. double width = Gun::guns()[i]->barrel_width();
  326. new Shell(Gun::guns()[i]->x(), Gun::guns()[i]->y(), Fl::event_x()+i*SCATTER_FACTOR(width), Fl::event_y()+i*SCATTER_FACTOR(width), width/2);
  327. }
  328. }
  329. damage(FL_DAMAGE_ALL);
  330. return 1;
  331. default:
  332. return ret;
  333. }
  334. }
  335. else
  336. {
  337. return ret;
  338. }
  339. }