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.

532 line
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] = { 0 };
  79. static Interceptor interceptor[MAX_INTERCEPTORS] = { 0 };
  80. static Explosion explosion[MAX_EXPLOSIONS] = { 0 };
  81. static Launcher launcher[LAUNCHERS_AMOUNT] = { 0 };
  82. static Building building[BUILDINGS_AMOUNT] = { 0 };
  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(void)
  99. {
  100. // Initialization (Note windowTitle is unused on Android)
  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 and Draw
  113. //----------------------------------------------------------------------------------
  114. UpdateDrawFrame();
  115. //----------------------------------------------------------------------------------
  116. }
  117. #endif
  118. // De-Initialization
  119. //--------------------------------------------------------------------------------------
  120. UnloadGame(); // Unload loaded data (textures, sounds, models...)
  121. CloseWindow(); // Close window and OpenGL context
  122. //--------------------------------------------------------------------------------------
  123. return 0;
  124. }
  125. //--------------------------------------------------------------------------------------
  126. // Game Module Functions Definition
  127. //--------------------------------------------------------------------------------------
  128. // Initialize game variables
  129. void InitGame(void)
  130. {
  131. // Initialize missiles
  132. for (int i = 0; i < MAX_MISSILES; i++)
  133. {
  134. missile[i].origin = (Vector2){ 0, 0 };
  135. missile[i].speed = (Vector2){ 0, 0 };
  136. missile[i].position = (Vector2){ 0, 0 };
  137. missile[i].active = false;
  138. }
  139. // Initialize interceptors
  140. for (int i = 0; i < MAX_INTERCEPTORS; i++)
  141. {
  142. interceptor[i].origin = (Vector2){ 0, 0 };
  143. interceptor[i].speed = (Vector2){ 0, 0 };
  144. interceptor[i].position = (Vector2){ 0, 0 };
  145. interceptor[i].active = false;
  146. }
  147. // Initialize explosions
  148. for (int i = 0; i < MAX_EXPLOSIONS; i++)
  149. {
  150. explosion[i].position = (Vector2){ 0, 0 };
  151. explosion[i].frame = 0;
  152. explosion[i].active = false;
  153. }
  154. // Initialize buildings and launchers
  155. int sparcing = screenWidth/(LAUNCHERS_AMOUNT + BUILDINGS_AMOUNT + 1);
  156. // Buildings and launchers placing
  157. launcher[0].position = (Vector2){ 1*sparcing, screenHeight - LAUNCHER_SIZE/2 };
  158. building[0].position = (Vector2){ 2*sparcing, screenHeight - BUILDING_SIZE/2 };
  159. building[1].position = (Vector2){ 3*sparcing, screenHeight - BUILDING_SIZE/2 };
  160. building[2].position = (Vector2){ 4*sparcing, screenHeight - BUILDING_SIZE/2 };
  161. launcher[1].position = (Vector2){ 5*sparcing, screenHeight - LAUNCHER_SIZE/2 };
  162. building[3].position = (Vector2){ 6*sparcing, screenHeight - BUILDING_SIZE/2 };
  163. building[4].position = (Vector2){ 7*sparcing, screenHeight - BUILDING_SIZE/2 };
  164. building[5].position = (Vector2){ 8*sparcing, screenHeight - BUILDING_SIZE/2 };
  165. launcher[2].position = (Vector2){ 9*sparcing, screenHeight - LAUNCHER_SIZE/2 };
  166. // Buildings and launchers activation
  167. for (int i = 0; i < LAUNCHERS_AMOUNT; i++) launcher[i].active = true;
  168. for (int i = 0; i < BUILDINGS_AMOUNT; i++) building[i].active = true;
  169. // Initialize game variables
  170. score = 0;
  171. }
  172. // Update game (one frame)
  173. void UpdateGame(void)
  174. {
  175. if (!gameOver)
  176. {
  177. if (IsKeyPressed('P')) pause = !pause;
  178. if (!pause)
  179. {
  180. framesCounter++;
  181. static
  182. float distance;
  183. // Interceptors update
  184. for (int i = 0; i < MAX_INTERCEPTORS; i++)
  185. {
  186. if (interceptor[i].active)
  187. {
  188. // Update position
  189. interceptor[i].position.x += interceptor[i].speed.x;
  190. interceptor[i].position.y += interceptor[i].speed.y;
  191. // Distance to objective
  192. distance = sqrt( pow(interceptor[i].position.x - interceptor[i].objective.x, 2) +
  193. pow(interceptor[i].position.y - interceptor[i].objective.y, 2));
  194. if (distance < INTERCEPTOR_SPEED)
  195. {
  196. // Interceptor dissapears
  197. interceptor[i].active = false;
  198. // Explosion
  199. explosion[explosionIndex].position = interceptor[i].position;
  200. explosion[explosionIndex].active = true;
  201. explosion[explosionIndex].frame = 0;
  202. explosionIndex++;
  203. if (explosionIndex == MAX_EXPLOSIONS) explosionIndex = 0;
  204. break;
  205. }
  206. }
  207. }
  208. // Missiles update
  209. for (int i = 0; i < MAX_MISSILES; i++)
  210. {
  211. if (missile[i].active)
  212. {
  213. // Update position
  214. missile[i].position.x += missile[i].speed.x;
  215. missile[i].position.y += missile[i].speed.y;
  216. // Collision and missile out of bounds
  217. if (missile[i].position.y > screenHeight) missile[i].active = false;
  218. else
  219. {
  220. // CHeck collision with launchers
  221. for (int j = 0; j < LAUNCHERS_AMOUNT; j++)
  222. {
  223. if (launcher[j].active)
  224. {
  225. if (CheckCollisionPointRec(missile[i].position, (Rectangle){ launcher[j].position.x - LAUNCHER_SIZE/2, launcher[j].position.y - LAUNCHER_SIZE/2,
  226. LAUNCHER_SIZE, LAUNCHER_SIZE }))
  227. {
  228. // Missile dissapears
  229. missile[i].active = false;
  230. // Explosion and destroy building
  231. launcher[j].active = false;
  232. explosion[explosionIndex].position = missile[i].position;
  233. explosion[explosionIndex].active = true;
  234. explosion[explosionIndex].frame = 0;
  235. explosionIndex++;
  236. if (explosionIndex == MAX_EXPLOSIONS) explosionIndex = 0;
  237. break;
  238. }
  239. }
  240. }
  241. // CHeck collision with buildings
  242. for (int j = 0; j < BUILDINGS_AMOUNT; j++)
  243. {
  244. if (building[j].active)
  245. {
  246. if (CheckCollisionPointRec(missile[i].position, (Rectangle){ building[j].position.x - BUILDING_SIZE/2, building[j].position.y - BUILDING_SIZE/2,
  247. BUILDING_SIZE, BUILDING_SIZE }))
  248. {
  249. // Missile dissapears
  250. missile[i].active = false;
  251. // Explosion and destroy building
  252. building[j].active = false;
  253. explosion[explosionIndex].position = missile[i].position;
  254. explosion[explosionIndex].active = true;
  255. explosion[explosionIndex].frame = 0;
  256. explosionIndex++;
  257. if (explosionIndex == MAX_EXPLOSIONS) explosionIndex = 0;
  258. break;
  259. }
  260. }
  261. }
  262. // CHeck collision with explosions
  263. for (int j = 0; j < MAX_EXPLOSIONS; j++)
  264. {
  265. if (explosion[j].active)
  266. {
  267. if (CheckCollisionPointCircle(missile[i].position, explosion[j].position, EXPLOSION_RADIUS*explosion[j].radiusMultiplier))
  268. {
  269. // Missile dissapears and we earn 100 points
  270. missile[i].active = false;
  271. score += 100;
  272. explosion[explosionIndex].position = missile[i].position;
  273. explosion[explosionIndex].active = true;
  274. explosion[explosionIndex].frame = 0;
  275. explosionIndex++;
  276. if (explosionIndex == MAX_EXPLOSIONS) explosionIndex = 0;
  277. break;
  278. }
  279. }
  280. }
  281. }
  282. }
  283. }
  284. // Explosions update
  285. for (int i = 0; i < MAX_EXPLOSIONS; i++)
  286. {
  287. if (explosion[i].active)
  288. {
  289. explosion[i].frame++;
  290. if (explosion[i].frame <= EXPLOSION_INCREASE_TIME) explosion[i].radiusMultiplier = explosion[i].frame/(float)EXPLOSION_INCREASE_TIME;
  291. else if (explosion[i].frame <= EXPLOSION_TOTAL_TIME) explosion[i].radiusMultiplier = 1 - (explosion[i].frame - (float)EXPLOSION_INCREASE_TIME)/(float)EXPLOSION_TOTAL_TIME;
  292. else
  293. {
  294. explosion[i].frame = 0;
  295. explosion[i].active = false;
  296. }
  297. }
  298. }
  299. // Fire logic
  300. UpdateOutgoingFire();
  301. UpdateIncomingFire();
  302. // Game over logic
  303. int checker = 0;
  304. for (int i = 0; i < LAUNCHERS_AMOUNT; i++)
  305. {
  306. if (!launcher[i].active) checker++;
  307. if (checker == LAUNCHERS_AMOUNT) gameOver = true;
  308. }
  309. checker = 0;
  310. for (int i = 0; i < BUILDINGS_AMOUNT; i++)
  311. {
  312. if (!building[i].active) checker++;
  313. if (checker == BUILDINGS_AMOUNT) gameOver = true;
  314. }
  315. }
  316. }
  317. else
  318. {
  319. if (IsKeyPressed(KEY_ENTER))
  320. {
  321. InitGame();
  322. gameOver = false;
  323. }
  324. }
  325. }
  326. // Draw game (one frame)
  327. void DrawGame(void)
  328. {
  329. BeginDrawing();
  330. ClearBackground(RAYWHITE);
  331. if (!gameOver)
  332. {
  333. // Draw missiles
  334. for (int i = 0; i < MAX_MISSILES; i++)
  335. {
  336. if (missile[i].active)
  337. {
  338. DrawLine(missile[i].origin.x, missile[i].origin.y, missile[i].position.x, missile[i].position.y, RED);
  339. if (framesCounter % 16 < 8) DrawCircle(missile[i].position.x, missile[i].position.y, 3, YELLOW);
  340. }
  341. }
  342. // Draw interceptors
  343. for (int i = 0; i < MAX_INTERCEPTORS; i++)
  344. {
  345. if (interceptor[i].active)
  346. {
  347. DrawLine(interceptor[i].origin.x, interceptor[i].origin.y, interceptor[i].position.x, interceptor[i].position.y, GREEN);
  348. if (framesCounter % 16 < 8) DrawCircle(interceptor[i].position.x, interceptor[i].position.y, 3, BLUE);
  349. }
  350. }
  351. // Draw explosions
  352. for (int i = 0; i < MAX_EXPLOSIONS; i++)
  353. {
  354. if (explosion[i].active) DrawCircle(explosion[i].position.x, explosion[i].position.y, EXPLOSION_RADIUS*explosion[i].radiusMultiplier, EXPLOSION_COLOR);
  355. }
  356. // Draw buildings and launchers
  357. for (int i = 0; i < LAUNCHERS_AMOUNT; i++)
  358. {
  359. if (launcher[i].active) DrawRectangle(launcher[i].position.x - LAUNCHER_SIZE/2, launcher[i].position.y - LAUNCHER_SIZE/2, LAUNCHER_SIZE, LAUNCHER_SIZE, GRAY);
  360. }
  361. for (int i = 0; i < BUILDINGS_AMOUNT; i++)
  362. {
  363. if (building[i].active) DrawRectangle(building[i].position.x - BUILDING_SIZE/2, building[i].position.y - BUILDING_SIZE/2, BUILDING_SIZE, BUILDING_SIZE, LIGHTGRAY);
  364. }
  365. // Draw score
  366. DrawText(TextFormat("SCORE %4i", score), 20, 20, 40, LIGHTGRAY);
  367. if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, GRAY);
  368. }
  369. else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, GRAY);
  370. EndDrawing();
  371. }
  372. // Unload game variables
  373. void UnloadGame(void)
  374. {
  375. // TODO: Unload all dynamic loaded data (textures, sounds, models...)
  376. }
  377. // Update and Draw (one frame)
  378. void UpdateDrawFrame(void)
  379. {
  380. UpdateGame();
  381. DrawGame();
  382. }
  383. //--------------------------------------------------------------------------------------
  384. // Additional module functions
  385. //--------------------------------------------------------------------------------------
  386. static void UpdateOutgoingFire()
  387. {
  388. static int interceptorNumber = 0;
  389. int launcherShooting = 0;
  390. if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) launcherShooting = 1;
  391. if (IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) launcherShooting = 2;
  392. if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) launcherShooting = 3;
  393. if (launcherShooting > 0 && launcher[launcherShooting - 1].active)
  394. {
  395. float module;
  396. float sideX;
  397. float sideY;
  398. // Activate the interceptor
  399. interceptor[interceptorNumber].active = true;
  400. // Assign start position
  401. interceptor[interceptorNumber].origin = launcher[launcherShooting - 1].position;
  402. interceptor[interceptorNumber].position = interceptor[interceptorNumber].origin;
  403. interceptor[interceptorNumber].objective = GetMousePosition();
  404. // Calculate speed
  405. module = sqrt( pow(interceptor[interceptorNumber].objective.x - interceptor[interceptorNumber].origin.x, 2) +
  406. pow(interceptor[interceptorNumber].objective.y - interceptor[interceptorNumber].origin.y, 2));
  407. sideX = (interceptor[interceptorNumber].objective.x - interceptor[interceptorNumber].origin.x)*INTERCEPTOR_SPEED/module;
  408. sideY = (interceptor[interceptorNumber].objective.y - interceptor[interceptorNumber].origin.y)*INTERCEPTOR_SPEED/module;
  409. interceptor[interceptorNumber].speed = (Vector2){ sideX, sideY };
  410. // Update
  411. interceptorNumber++;
  412. if (interceptorNumber == MAX_INTERCEPTORS) interceptorNumber = 0;
  413. }
  414. }
  415. static void UpdateIncomingFire()
  416. {
  417. static int missileIndex = 0;
  418. // Launch missile
  419. if (framesCounter%MISSILE_LAUNCH_FRAMES == 0)
  420. {
  421. float module;
  422. float sideX;
  423. float sideY;
  424. // Activate the missile
  425. missile[missileIndex].active = true;
  426. // Assign start position
  427. missile[missileIndex].origin = (Vector2){ GetRandomValue(20, screenWidth - 20), -10 };
  428. missile[missileIndex].position = missile[missileIndex].origin;
  429. missile[missileIndex].objective = (Vector2){ GetRandomValue(20, screenWidth - 20), screenHeight + 10 };
  430. // Calculate speed
  431. module = sqrt( pow(missile[missileIndex].objective.x - missile[missileIndex].origin.x, 2) +
  432. pow(missile[missileIndex].objective.y - missile[missileIndex].origin.y, 2));
  433. sideX = (missile[missileIndex].objective.x - missile[missileIndex].origin.x)*MISSILE_SPEED/module;
  434. sideY = (missile[missileIndex].objective.y - missile[missileIndex].origin.y)*MISSILE_SPEED/module;
  435. missile[missileIndex].speed = (Vector2){ sideX, sideY };
  436. // Update
  437. missileIndex++;
  438. if (missileIndex == MAX_MISSILES) missileIndex = 0;
  439. }
  440. }