You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

538 lines
20 KiB

  1. /*******************************************************************************************
  2. *
  3. * raylib - sample game: missile commander
  4. *
  5. * Sample game Marc Palau and Ramon Santamaria
  6. *
  7. * This game has been created using raylib v1.3 (www.raylib.com)
  8. * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
  9. *
  10. * Copyright (c) 2015 Ramon Santamaria (@raysan5)
  11. *
  12. ********************************************************************************************/
  13. #include "raylib.h"
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <time.h>
  17. #include <math.h>
  18. #if defined(PLATFORM_WEB)
  19. #include <emscripten/emscripten.h>
  20. #endif
  21. //----------------------------------------------------------------------------------
  22. // Some Defines
  23. //----------------------------------------------------------------------------------
  24. #define MAX_MISSILES 100
  25. #define MAX_INTERCEPTORS 30
  26. #define MAX_EXPLOSIONS 100
  27. #define LAUNCHERS_AMOUNT 3 // Not a variable, should not be changed
  28. #define BUILDINGS_AMOUNT 6 // Not a variable, should not be changed
  29. #define LAUNCHER_SIZE 80
  30. #define BUILDING_SIZE 60
  31. #define EXPLOSION_RADIUS 40
  32. #define MISSILE_SPEED 1
  33. #define MISSILE_LAUNCH_FRAMES 80
  34. #define INTERCEPTOR_SPEED 10
  35. #define EXPLOSION_INCREASE_TIME 90 // In frames
  36. #define EXPLOSION_TOTAL_TIME 210 // In frames
  37. #define EXPLOSION_COLOR (Color){ 125, 125, 125, 125 }
  38. //----------------------------------------------------------------------------------
  39. // Types and Structures Definition
  40. //----------------------------------------------------------------------------------
  41. typedef struct Missile {
  42. Vector2 origin;
  43. Vector2 position;
  44. Vector2 objective;
  45. Vector2 speed;
  46. bool active;
  47. } Missile;
  48. typedef struct Interceptor {
  49. Vector2 origin;
  50. Vector2 position;
  51. Vector2 objective;
  52. Vector2 speed;
  53. bool active;
  54. } Interceptor;
  55. typedef struct Explosion {
  56. Vector2 position;
  57. float radiusMultiplier;
  58. int frame;
  59. bool active;
  60. } Explosion;
  61. typedef struct Launcher {
  62. Vector2 position;
  63. bool active;
  64. } Launcher;
  65. typedef struct Building {
  66. Vector2 position;
  67. bool active;
  68. } Building;
  69. //------------------------------------------------------------------------------------
  70. // Global Variables Declaration
  71. //------------------------------------------------------------------------------------
  72. static int screenWidth = 800;
  73. static int screenHeight = 450;
  74. static int framesCounter = 0;
  75. static bool gameOver = false;
  76. static bool pause = false;
  77. static int score = 0;
  78. static Missile missile[MAX_MISSILES];
  79. static Interceptor interceptor[MAX_INTERCEPTORS];
  80. static Explosion explosion[MAX_EXPLOSIONS];
  81. static Launcher launcher[LAUNCHERS_AMOUNT];
  82. static Building building[BUILDINGS_AMOUNT];
  83. static int explosionIndex = 0;
  84. //------------------------------------------------------------------------------------
  85. // Module Functions Declaration (local)
  86. //------------------------------------------------------------------------------------
  87. static void InitGame(void); // Initialize game
  88. static void UpdateGame(void); // Update game (one frame)
  89. static void DrawGame(void); // Draw game (one frame)
  90. static void UnloadGame(void); // Unload game
  91. static void UpdateDrawFrame(void); // Update and Draw (one frame)
  92. // Additional module functions
  93. static void UpdateOutgoingFire();
  94. static void UpdateIncomingFire();
  95. //------------------------------------------------------------------------------------
  96. // Program main entry point
  97. //------------------------------------------------------------------------------------
  98. int main()
  99. {
  100. // Initialization
  101. //--------------------------------------------------------------------------------------
  102. InitWindow(screenWidth, screenHeight, "sample game: missile commander");
  103. InitGame();
  104. #if defined(PLATFORM_WEB)
  105. emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
  106. #else
  107. SetTargetFPS(60);
  108. //--------------------------------------------------------------------------------------
  109. // Main game loop
  110. while (!WindowShouldClose()) // Detect window close button or ESC key
  111. {
  112. // Update
  113. //----------------------------------------------------------------------------------
  114. UpdateGame();
  115. //----------------------------------------------------------------------------------
  116. // Draw
  117. //----------------------------------------------------------------------------------
  118. DrawGame();
  119. //----------------------------------------------------------------------------------
  120. }
  121. #endif
  122. // De-Initialization
  123. //--------------------------------------------------------------------------------------
  124. UnloadGame(); // Unload loaded data (textures, sounds, models...)
  125. CloseWindow(); // Close window and OpenGL context
  126. //--------------------------------------------------------------------------------------
  127. return 0;
  128. }
  129. //--------------------------------------------------------------------------------------
  130. // Game Module Functions Definition
  131. //--------------------------------------------------------------------------------------
  132. // Initialize game variables
  133. void InitGame(void)
  134. {
  135. // Initialize missiles
  136. for (int i = 0; i < MAX_MISSILES; i++)
  137. {
  138. missile[i].origin = (Vector2){ 0, 0 };
  139. missile[i].speed = (Vector2){ 0, 0 };
  140. missile[i].position = (Vector2){ 0, 0 };
  141. missile[i].active = false;
  142. }
  143. // Initialize interceptors
  144. for (int i = 0; i < MAX_INTERCEPTORS; i++)
  145. {
  146. interceptor[i].origin = (Vector2){ 0, 0 };
  147. interceptor[i].speed = (Vector2){ 0, 0 };
  148. interceptor[i].position = (Vector2){ 0, 0 };
  149. interceptor[i].active = false;
  150. }
  151. // Initialize explosions
  152. for (int i = 0; i < MAX_EXPLOSIONS; i++)
  153. {
  154. explosion[i].position = (Vector2){ 0, 0 };
  155. explosion[i].frame = 0;
  156. explosion[i].active = false;
  157. }
  158. // Initialize buildings and launchers
  159. int sparcing = screenWidth/(LAUNCHERS_AMOUNT + BUILDINGS_AMOUNT + 1);
  160. // Buildings and launchers placing
  161. launcher[0].position = (Vector2){ 1*sparcing, screenHeight - LAUNCHER_SIZE/2 };
  162. building[0].position = (Vector2){ 2*sparcing, screenHeight - BUILDING_SIZE/2 };
  163. building[1].position = (Vector2){ 3*sparcing, screenHeight - BUILDING_SIZE/2 };
  164. building[2].position = (Vector2){ 4*sparcing, screenHeight - BUILDING_SIZE/2 };
  165. launcher[1].position = (Vector2){ 5*sparcing, screenHeight - LAUNCHER_SIZE/2 };
  166. building[3].position = (Vector2){ 6*sparcing, screenHeight - BUILDING_SIZE/2 };
  167. building[4].position = (Vector2){ 7*sparcing, screenHeight - BUILDING_SIZE/2 };
  168. building[5].position = (Vector2){ 8*sparcing, screenHeight - BUILDING_SIZE/2 };
  169. launcher[2].position = (Vector2){ 9*sparcing, screenHeight - LAUNCHER_SIZE/2 };
  170. // Buildings and launchers activation
  171. for (int i = 0; i < LAUNCHERS_AMOUNT; i++) launcher[i].active = true;
  172. for (int i = 0; i < BUILDINGS_AMOUNT; i++) building[i].active = true;
  173. // Initialize game variables
  174. score = 0;
  175. }
  176. // Update game (one frame)
  177. void UpdateGame(void)
  178. {
  179. if (!gameOver)
  180. {
  181. if (IsKeyPressed('P')) pause = !pause;
  182. if (!pause)
  183. {
  184. framesCounter++;
  185. static
  186. float distance;
  187. // Interceptors update
  188. for (int i = 0; i < MAX_INTERCEPTORS; i++)
  189. {
  190. if (interceptor[i].active)
  191. {
  192. // Update position
  193. interceptor[i].position.x += interceptor[i].speed.x;
  194. interceptor[i].position.y += interceptor[i].speed.y;
  195. // Distance to objective
  196. distance = sqrt( pow(interceptor[i].position.x - interceptor[i].objective.x, 2) +
  197. pow(interceptor[i].position.y - interceptor[i].objective.y, 2));
  198. if (distance < INTERCEPTOR_SPEED)
  199. {
  200. // Interceptor dissapears
  201. interceptor[i].active = false;
  202. // Explosion
  203. explosion[explosionIndex].position = interceptor[i].position;
  204. explosion[explosionIndex].active = true;
  205. explosion[explosionIndex].frame = 0;
  206. explosionIndex++;
  207. if (explosionIndex == MAX_EXPLOSIONS) explosionIndex = 0;
  208. break;
  209. }
  210. }
  211. }
  212. // Missiles update
  213. for (int i = 0; i < MAX_MISSILES; i++)
  214. {
  215. if (missile[i].active)
  216. {
  217. // Update position
  218. missile[i].position.x += missile[i].speed.x;
  219. missile[i].position.y += missile[i].speed.y;
  220. // Collision and missile out of bounds
  221. if (missile[i].position.y > screenHeight) missile[i].active = false;
  222. else
  223. {
  224. // CHeck collision with launchers
  225. for (int j = 0; j < LAUNCHERS_AMOUNT; j++)
  226. {
  227. if (launcher[j].active)
  228. {
  229. if (CheckCollisionPointRec(missile[i].position, (Rectangle){ launcher[j].position.x - LAUNCHER_SIZE/2, launcher[j].position.y - LAUNCHER_SIZE/2,
  230. LAUNCHER_SIZE, LAUNCHER_SIZE }))
  231. {
  232. // Missile dissapears
  233. missile[i].active = false;
  234. // Explosion and destroy building
  235. launcher[j].active = false;
  236. explosion[explosionIndex].position = missile[i].position;
  237. explosion[explosionIndex].active = true;
  238. explosion[explosionIndex].frame = 0;
  239. explosionIndex++;
  240. if (explosionIndex == MAX_EXPLOSIONS) explosionIndex = 0;
  241. break;
  242. }
  243. }
  244. }
  245. // CHeck collision with buildings
  246. for (int j = 0; j < BUILDINGS_AMOUNT; j++)
  247. {
  248. if (building[j].active)
  249. {
  250. if (CheckCollisionPointRec(missile[i].position, (Rectangle){ building[j].position.x - BUILDING_SIZE/2, building[j].position.y - BUILDING_SIZE/2,
  251. BUILDING_SIZE, BUILDING_SIZE }))
  252. {
  253. // Missile dissapears
  254. missile[i].active = false;
  255. // Explosion and destroy building
  256. building[j].active = false;
  257. explosion[explosionIndex].position = missile[i].position;
  258. explosion[explosionIndex].active = true;
  259. explosion[explosionIndex].frame = 0;
  260. explosionIndex++;
  261. if (explosionIndex == MAX_EXPLOSIONS) explosionIndex = 0;
  262. break;
  263. }
  264. }
  265. }
  266. // CHeck collision with explosions
  267. for (int j = 0; j < MAX_EXPLOSIONS; j++)
  268. {
  269. if (explosion[j].active)
  270. {
  271. if (CheckCollisionPointCircle(missile[i].position, explosion[j].position, EXPLOSION_RADIUS*explosion[j].radiusMultiplier))
  272. {
  273. // Missile dissapears and we earn 100 points
  274. missile[i].active = false;
  275. score += 100;
  276. explosion[explosionIndex].position = missile[i].position;
  277. explosion[explosionIndex].active = true;
  278. explosion[explosionIndex].frame = 0;
  279. explosionIndex++;
  280. if (explosionIndex == MAX_EXPLOSIONS) explosionIndex = 0;
  281. break;
  282. }
  283. }
  284. }
  285. }
  286. }
  287. }
  288. // Explosions update
  289. for (int i = 0; i < MAX_EXPLOSIONS; i++)
  290. {
  291. if (explosion[i].active)
  292. {
  293. explosion[i].frame++;
  294. if (explosion[i].frame <= EXPLOSION_INCREASE_TIME) explosion[i].radiusMultiplier = explosion[i].frame/(float)EXPLOSION_INCREASE_TIME;
  295. else if (explosion[i].frame <= EXPLOSION_TOTAL_TIME) explosion[i].radiusMultiplier = 1 - (explosion[i].frame - (float)EXPLOSION_INCREASE_TIME)/(float)EXPLOSION_TOTAL_TIME;
  296. else
  297. {
  298. explosion[i].frame = 0;
  299. explosion[i].active = false;
  300. }
  301. }
  302. }
  303. // Fire logic
  304. UpdateOutgoingFire();
  305. UpdateIncomingFire();
  306. // Game over logic
  307. int checker = 0;
  308. for (int i = 0; i < LAUNCHERS_AMOUNT; i++)
  309. {
  310. if (!launcher[i].active) checker++;
  311. if (checker == LAUNCHERS_AMOUNT) gameOver = true;
  312. }
  313. checker = 0;
  314. for (int i = 0; i < BUILDINGS_AMOUNT; i++)
  315. {
  316. if (!building[i].active) checker++;
  317. if (checker == BUILDINGS_AMOUNT) gameOver = true;
  318. }
  319. }
  320. }
  321. else
  322. {
  323. if (IsKeyPressed(KEY_ENTER))
  324. {
  325. InitGame();
  326. gameOver = false;
  327. }
  328. }
  329. }
  330. // Draw game (one frame)
  331. void DrawGame(void)
  332. {
  333. BeginDrawing();
  334. ClearBackground(RAYWHITE);
  335. if (!gameOver)
  336. {
  337. // Draw missiles
  338. for (int i = 0; i < MAX_MISSILES; i++)
  339. {
  340. if (missile[i].active)
  341. {
  342. DrawLine(missile[i].origin.x, missile[i].origin.y, missile[i].position.x, missile[i].position.y, RED);
  343. if (framesCounter % 16 < 8) DrawCircle(missile[i].position.x, missile[i].position.y, 3, YELLOW);
  344. }
  345. }
  346. // Draw interceptors
  347. for (int i = 0; i < MAX_INTERCEPTORS; i++)
  348. {
  349. if (interceptor[i].active)
  350. {
  351. DrawLine(interceptor[i].origin.x, interceptor[i].origin.y, interceptor[i].position.x, interceptor[i].position.y, GREEN);
  352. if (framesCounter % 16 < 8) DrawCircle(interceptor[i].position.x, interceptor[i].position.y, 3, BLUE);
  353. }
  354. }
  355. // Draw explosions
  356. for (int i = 0; i < MAX_EXPLOSIONS; i++)
  357. {
  358. if (explosion[i].active) DrawCircle(explosion[i].position.x, explosion[i].position.y, EXPLOSION_RADIUS*explosion[i].radiusMultiplier, EXPLOSION_COLOR);
  359. }
  360. // Draw buildings and launchers
  361. for (int i = 0; i < LAUNCHERS_AMOUNT; i++)
  362. {
  363. if (launcher[i].active) DrawRectangle(launcher[i].position.x - LAUNCHER_SIZE/2, launcher[i].position.y - LAUNCHER_SIZE/2, LAUNCHER_SIZE, LAUNCHER_SIZE, GRAY);
  364. }
  365. for (int i = 0; i < BUILDINGS_AMOUNT; i++)
  366. {
  367. if (building[i].active) DrawRectangle(building[i].position.x - BUILDING_SIZE/2, building[i].position.y - BUILDING_SIZE/2, BUILDING_SIZE, BUILDING_SIZE, LIGHTGRAY);
  368. }
  369. // Draw score
  370. DrawText(FormatText("SCORE %4i", score), 20, 20, 40, LIGHTGRAY);
  371. if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, GRAY);
  372. }
  373. else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, GRAY);
  374. EndDrawing();
  375. }
  376. // Unload game variables
  377. void UnloadGame(void)
  378. {
  379. // TODO: Unload all dynamic loaded data (textures, sounds, models...)
  380. }
  381. // Update and Draw (one frame)
  382. void UpdateDrawFrame(void)
  383. {
  384. UpdateGame();
  385. DrawGame();
  386. }
  387. //--------------------------------------------------------------------------------------
  388. // Additional module functions
  389. //--------------------------------------------------------------------------------------
  390. static void UpdateOutgoingFire()
  391. {
  392. static int interceptorNumber = 0;
  393. int launcherShooting = 0;
  394. if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) launcherShooting = 1;
  395. if (IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) launcherShooting = 2;
  396. if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) launcherShooting = 3;
  397. if (launcherShooting > 0 && launcher[launcherShooting - 1].active)
  398. {
  399. float module;
  400. float sideX;
  401. float sideY;
  402. // Activate the interceptor
  403. interceptor[interceptorNumber].active = true;
  404. // Assign start position
  405. interceptor[interceptorNumber].origin = launcher[launcherShooting - 1].position;
  406. interceptor[interceptorNumber].position = interceptor[interceptorNumber].origin;
  407. interceptor[interceptorNumber].objective = GetMousePosition();
  408. // Calculate speed
  409. module = sqrt( pow(interceptor[interceptorNumber].objective.x - interceptor[interceptorNumber].origin.x, 2) +
  410. pow(interceptor[interceptorNumber].objective.y - interceptor[interceptorNumber].origin.y, 2));
  411. sideX = (interceptor[interceptorNumber].objective.x - interceptor[interceptorNumber].origin.x)*INTERCEPTOR_SPEED/module;
  412. sideY = (interceptor[interceptorNumber].objective.y - interceptor[interceptorNumber].origin.y)*INTERCEPTOR_SPEED/module;
  413. interceptor[interceptorNumber].speed = (Vector2){ sideX, sideY };
  414. // Update
  415. interceptorNumber++;
  416. if (interceptorNumber == MAX_INTERCEPTORS) interceptorNumber = 0;
  417. }
  418. }
  419. static void UpdateIncomingFire()
  420. {
  421. static int missileIndex = 0;
  422. // Launch missile
  423. if (framesCounter % MISSILE_LAUNCH_FRAMES == 0)
  424. {
  425. float module;
  426. float sideX;
  427. float sideY;
  428. // Activate the missile
  429. missile[missileIndex].active = true;
  430. // Assign start position
  431. missile[missileIndex].origin = (Vector2){ GetRandomValue(20, screenWidth - 20), -10 };
  432. missile[missileIndex].position = missile[missileIndex].origin;
  433. missile[missileIndex].objective = (Vector2){ GetRandomValue(20, screenWidth - 20), screenHeight + 10 };
  434. // Calculate speed
  435. module = sqrt( pow(missile[missileIndex].objective.x - missile[missileIndex].origin.x, 2) +
  436. pow(missile[missileIndex].objective.y - missile[missileIndex].origin.y, 2));
  437. sideX = (missile[missileIndex].objective.x - missile[missileIndex].origin.x)*MISSILE_SPEED/module;
  438. sideY = (missile[missileIndex].objective.y - missile[missileIndex].origin.y)*MISSILE_SPEED/module;
  439. missile[missileIndex].speed = (Vector2){ sideX, sideY };
  440. // Update
  441. missileIndex++;
  442. if (missileIndex == MAX_MISSILES) missileIndex = 0;
  443. }
  444. }