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.

624 lines
24 KiB

  1. /*******************************************************************************************
  2. *
  3. * raylib - sample game: pang
  4. *
  5. * Sample game developed by Ian Eito and Albert Martos 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 <math.h>
  15. #if defined(PLATFORM_WEB)
  16. #include <emscripten/emscripten.h>
  17. #endif
  18. //----------------------------------------------------------------------------------
  19. // Some Defines
  20. //----------------------------------------------------------------------------------
  21. #define PLAYER_BASE_SIZE 20.0f
  22. #define PLAYER_SPEED 5.0f
  23. #define PLAYER_MAX_SHOOTS 1
  24. #define MAX_BIG_BALLS 2
  25. #define BALLS_SPEED 2.0f
  26. //----------------------------------------------------------------------------------
  27. // Types and Structures Definition
  28. //----------------------------------------------------------------------------------
  29. typedef struct Player {
  30. Vector2 position;
  31. Vector2 speed;
  32. Vector3 collider;
  33. float rotation;
  34. } Player;
  35. typedef struct Shoot {
  36. Vector2 position;
  37. Vector2 speed;
  38. float radius;
  39. float rotation;
  40. int lifeSpawn;
  41. bool active;
  42. } Shoot;
  43. typedef struct Ball {
  44. Vector2 position;
  45. Vector2 speed;
  46. float radius;
  47. int points;
  48. bool active;
  49. } Ball;
  50. typedef struct Points {
  51. Vector2 position;
  52. int value;
  53. float alpha;
  54. } Points;
  55. //------------------------------------------------------------------------------------
  56. // Global Variables Declaration
  57. //------------------------------------------------------------------------------------
  58. static const int screenWidth = 800;
  59. static const int screenHeight = 450;
  60. static int framesCounter = 0;
  61. static bool gameOver = false;
  62. static bool pause = false;
  63. static int score = 0;
  64. static Player player = { 0 };
  65. static Shoot shoot[PLAYER_MAX_SHOOTS] = { 0 };
  66. static Ball bigBalls[MAX_BIG_BALLS] = { 0 };
  67. static Ball mediumBalls[MAX_BIG_BALLS*2] = { 0 };
  68. static Ball smallBalls[MAX_BIG_BALLS*4] = { 0 };
  69. static Points points[5] = { 0 };
  70. // NOTE: Defined triangle is isosceles with common angles of 70 degrees.
  71. static float shipHeight = 0.0f;
  72. static float gravity = 0.0f;
  73. static int countmediumBallss = 0;
  74. static int countsmallBallss = 0;
  75. static int meteorsDestroyed = 0;
  76. static Vector2 linePosition = { 0 };
  77. static bool victory = false;
  78. static bool lose = false;
  79. static bool awake = false;
  80. //------------------------------------------------------------------------------------
  81. // Module Functions Declaration (local)
  82. //------------------------------------------------------------------------------------
  83. static void InitGame(void); // Initialize game
  84. static void UpdateGame(void); // Update game (one frame)
  85. static void DrawGame(void); // Draw game (one frame)
  86. static void UnloadGame(void); // Unload game
  87. static void UpdateDrawFrame(void); // Update and Draw (one frame)
  88. //------------------------------------------------------------------------------------
  89. // Program main entry point
  90. //------------------------------------------------------------------------------------
  91. int main(void)
  92. {
  93. // Initialization (Note windowTitle is unused on Android)
  94. //---------------------------------------------------------
  95. InitWindow(screenWidth, screenHeight, "sample game: pang");
  96. InitGame();
  97. #if defined(PLATFORM_WEB)
  98. emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
  99. #else
  100. SetTargetFPS(60);
  101. //--------------------------------------------------------------------------------------
  102. // Main game loop
  103. while (!WindowShouldClose()) // Detect window close button or ESC key
  104. {
  105. // Update and Draw
  106. //----------------------------------------------------------------------------------
  107. UpdateDrawFrame();
  108. //----------------------------------------------------------------------------------
  109. }
  110. #endif
  111. // De-Initialization
  112. //--------------------------------------------------------------------------------------
  113. UnloadGame(); // Unload loaded data (textures, sounds, models...)
  114. CloseWindow(); // Close window and OpenGL context
  115. //--------------------------------------------------------------------------------------
  116. return 0;
  117. }
  118. //------------------------------------------------------------------------------------
  119. // Module Functions Definitions (local)
  120. //------------------------------------------------------------------------------------
  121. // Initialize game variables
  122. static void InitGame(void)
  123. {
  124. int posx, posy;
  125. int velx = 0;
  126. int vely = 0;
  127. framesCounter = 0;
  128. gameOver = false;
  129. pause = false;
  130. score = 0;
  131. victory = false;
  132. lose = false;
  133. awake = true;
  134. gravity = 0.25f;
  135. linePosition = (Vector2){ 0.0f , 0.0f };
  136. shipHeight = (PLAYER_BASE_SIZE/2)/tanf(20*DEG2RAD);
  137. // Initialization player
  138. player.position = (Vector2){ screenWidth/2, screenHeight };
  139. player.speed = (Vector2){ PLAYER_SPEED, PLAYER_SPEED };
  140. player.rotation = 0;
  141. player.collider = (Vector3){ player.position.x, player.position.y - shipHeight/2.0f, 12.0f };
  142. meteorsDestroyed = 0;
  143. // Initialize shoots
  144. for (int i = 0; i < PLAYER_MAX_SHOOTS; i++)
  145. {
  146. shoot[i].position = (Vector2){ 0, 0 };
  147. shoot[i].speed = (Vector2){ 0, 0 };
  148. shoot[i].radius = 2;
  149. shoot[i].active = false;
  150. shoot[i].lifeSpawn = 0;
  151. }
  152. // Initialize big meteors
  153. for (int i = 0; i < MAX_BIG_BALLS; i++)
  154. {
  155. bigBalls[i].radius = 40.0f;
  156. posx = GetRandomValue(0 + bigBalls[i].radius, screenWidth - bigBalls[i].radius);
  157. posy = GetRandomValue(0 + bigBalls[i].radius, screenHeight/2);
  158. bigBalls[i].position = (Vector2){ posx, posy };
  159. while ((velx == 0) || (vely == 0))
  160. {
  161. velx = GetRandomValue(-BALLS_SPEED, BALLS_SPEED);
  162. vely = GetRandomValue(-BALLS_SPEED, BALLS_SPEED);
  163. }
  164. bigBalls[i].speed = (Vector2){ velx, vely };
  165. bigBalls[i].points = 200;
  166. bigBalls[i].active = true;
  167. }
  168. // Initialize medium meteors
  169. for (int i = 0; i < MAX_BIG_BALLS*2; i++)
  170. {
  171. mediumBalls[i].position = (Vector2){-100, -100};
  172. mediumBalls[i].speed = (Vector2){0,0};
  173. mediumBalls[i].radius = 20.0f;
  174. mediumBalls[i].points = 100;
  175. mediumBalls[i].active = false;
  176. }
  177. // Initialize small meteors
  178. for (int i = 0; i < MAX_BIG_BALLS*4; i++)
  179. {
  180. smallBalls[i].position = (Vector2){ -100, -100 };
  181. smallBalls[i].speed = (Vector2){ 0, 0 };
  182. smallBalls[i].radius = 10.0f;
  183. smallBalls[i].points = 50;
  184. smallBalls[i].active = false;
  185. }
  186. // Initialize animated points
  187. for (int i = 0; i < 5; i++)
  188. {
  189. points[i].position = (Vector2){ 0, 0 };
  190. points[i].value = 0;
  191. points[i].alpha = 0.0f;
  192. }
  193. countmediumBallss = 0;
  194. countsmallBallss = 0;
  195. }
  196. // Update game (one frame)
  197. void UpdateGame(void)
  198. {
  199. if (!gameOver && !victory)
  200. {
  201. if (IsKeyPressed('P')) pause = !pause;
  202. if (!pause)
  203. {
  204. // Player logic
  205. if (IsKeyDown(KEY_LEFT)) player.position.x -= player.speed.x;
  206. if (IsKeyDown(KEY_RIGHT)) player.position.x += player.speed.x;
  207. // Player vs wall collision logic
  208. if (player.position.x + PLAYER_BASE_SIZE/2 > screenWidth) player.position.x = screenWidth - PLAYER_BASE_SIZE/2;
  209. else if (player.position.x - PLAYER_BASE_SIZE/2 < 0) player.position.x = 0 + PLAYER_BASE_SIZE/2;
  210. // Player shot logic
  211. if (IsKeyPressed(KEY_SPACE))
  212. {
  213. for (int i = 0; i < PLAYER_MAX_SHOOTS; i++)
  214. {
  215. if (!shoot[i].active)
  216. {
  217. shoot[i].position = (Vector2){ player.position.x, player.position.y - shipHeight };
  218. shoot[i].speed.y = PLAYER_SPEED;
  219. shoot[i].active = true;
  220. linePosition = (Vector2){ player.position.x, player.position.y};
  221. break;
  222. }
  223. }
  224. }
  225. // Shoot life timer
  226. for (int i = 0; i < PLAYER_MAX_SHOOTS; i++)
  227. {
  228. if (shoot[i].active) shoot[i].lifeSpawn++;
  229. }
  230. // Shot logic
  231. for (int i = 0; i < PLAYER_MAX_SHOOTS; i++)
  232. {
  233. if (shoot[i].active)
  234. {
  235. shoot[i].position.y -= shoot[i].speed.y;
  236. // Shot vs walls collision logic
  237. if ((shoot[i].position.x > screenWidth + shoot[i].radius) || (shoot[i].position.x < 0 - shoot[i].radius) ||
  238. (shoot[i].position.y > screenHeight + shoot[i].radius) || (shoot[i].position.y < 0 - shoot[i].radius))
  239. {
  240. shoot[i].active = false;
  241. shoot[i].lifeSpawn = 0;
  242. }
  243. // Player shot life spawn
  244. if (shoot[i].lifeSpawn >= 120)
  245. {
  246. shoot[i].position = (Vector2){ 0.0f, 0.0f };
  247. shoot[i].speed = (Vector2){ 0.0f, 0.0f };
  248. shoot[i].lifeSpawn = 0;
  249. shoot[i].active = false;
  250. }
  251. }
  252. }
  253. // Player vs meteors collision logic
  254. player.collider = (Vector3){player.position.x, player.position.y - shipHeight/2, 12};
  255. for (int i = 0; i < MAX_BIG_BALLS; i++)
  256. {
  257. if (CheckCollisionCircles((Vector2){ player.collider.x, player.collider.y }, player.collider.z, bigBalls[i].position, bigBalls[i].radius) && bigBalls[i].active)
  258. {
  259. gameOver = true;
  260. }
  261. }
  262. for (int i = 0; i < MAX_BIG_BALLS*2; i++)
  263. {
  264. if (CheckCollisionCircles((Vector2){ player.collider.x, player.collider.y }, player.collider.z, mediumBalls[i].position, mediumBalls[i].radius) && mediumBalls[i].active)
  265. {
  266. gameOver = true;
  267. }
  268. }
  269. for (int i = 0; i < MAX_BIG_BALLS*4; i++)
  270. {
  271. if (CheckCollisionCircles((Vector2){ player.collider.x, player.collider.y }, player.collider.z, smallBalls[i].position, smallBalls[i].radius) && smallBalls[i].active)
  272. {
  273. gameOver = true;
  274. }
  275. }
  276. // Meteors logic (big)
  277. for (int i = 0; i < MAX_BIG_BALLS; i++)
  278. {
  279. if (bigBalls[i].active)
  280. {
  281. // Meteor movement logic
  282. bigBalls[i].position.x += bigBalls[i].speed.x;
  283. bigBalls[i].position.y += bigBalls[i].speed.y;
  284. // Meteor vs wall collision logic
  285. if (((bigBalls[i].position.x + bigBalls[i].radius) >= screenWidth) || ((bigBalls[i].position.x - bigBalls[i].radius) <= 0)) bigBalls[i].speed.x *= -1;
  286. if ((bigBalls[i].position.y - bigBalls[i].radius) <= 0) bigBalls[i].speed.y *= -1.5;
  287. if ((bigBalls[i].position.y + bigBalls[i].radius) >= screenHeight)
  288. {
  289. bigBalls[i].speed.y *= -1;
  290. bigBalls[i].position.y = screenHeight - bigBalls[i].radius;
  291. }
  292. bigBalls[i].speed.y += gravity;
  293. }
  294. }
  295. // Meteors logic (medium)
  296. for (int i = 0; i < MAX_BIG_BALLS*2; i++)
  297. {
  298. if (mediumBalls[i].active)
  299. {
  300. // Meteor movement logic
  301. mediumBalls[i].position.x += mediumBalls[i].speed.x;
  302. mediumBalls[i].position.y += mediumBalls[i].speed.y;
  303. // Meteor vs wall collision logic
  304. if (mediumBalls[i].position.x + mediumBalls[i].radius >= screenWidth || mediumBalls[i].position.x - mediumBalls[i].radius <= 0) mediumBalls[i].speed.x *= -1;
  305. if (mediumBalls[i].position.y - mediumBalls[i].radius <= 0) mediumBalls[i].speed.y *= -1;
  306. if (mediumBalls[i].position.y + mediumBalls[i].radius >= screenHeight)
  307. {
  308. mediumBalls[i].speed.y *= -1;
  309. mediumBalls[i].position.y = screenHeight - mediumBalls[i].radius;
  310. }
  311. mediumBalls[i].speed.y += gravity + 0.12f;
  312. }
  313. }
  314. // Meteors logic (small)
  315. for (int i = 0; i < MAX_BIG_BALLS*4; i++)
  316. {
  317. if (smallBalls[i].active)
  318. {
  319. // Meteor movement logic
  320. smallBalls[i].position.x += smallBalls[i].speed.x;
  321. smallBalls[i].position.y += smallBalls[i].speed.y;
  322. // Meteor vs wall collision logic
  323. if (smallBalls[i].position.x + smallBalls[i].radius >= screenWidth || smallBalls[i].position.x - smallBalls[i].radius <= 0) smallBalls[i].speed.x *= -1;
  324. if (smallBalls[i].position.y - smallBalls[i].radius <= 0) smallBalls[i].speed.y *= -1;
  325. if (smallBalls[i].position.y + smallBalls[i].radius >= screenHeight)
  326. {
  327. smallBalls[i].speed.y *= -1;
  328. smallBalls[i].position.y = screenHeight - smallBalls[i].radius;
  329. }
  330. smallBalls[i].speed.y += gravity + 0.25f;
  331. }
  332. }
  333. // Player-shot vs meteors logic
  334. for (int i = 0; i < PLAYER_MAX_SHOOTS; i++)
  335. {
  336. if ((shoot[i].active))
  337. {
  338. for (int a = 0; a < MAX_BIG_BALLS; a++)
  339. {
  340. if (bigBalls[a].active && (bigBalls[a].position.x - bigBalls[a].radius <= linePosition.x && bigBalls[a].position.x + bigBalls[a].radius >= linePosition.x)
  341. && (bigBalls[a].position.y + bigBalls[a].radius >= shoot[i].position.y))
  342. {
  343. shoot[i].active = false;
  344. shoot[i].lifeSpawn = 0;
  345. bigBalls[a].active = false;
  346. meteorsDestroyed++;
  347. score += bigBalls[a].points;
  348. for (int z = 0; z < 5; z++)
  349. {
  350. if (points[z].alpha == 0.0f)
  351. {
  352. points[z].position = bigBalls[a].position;
  353. points[z].value = bigBalls[a].points;
  354. points[z].alpha = 1.0f;
  355. z = 5;
  356. }
  357. }
  358. for (int j = 0; j < 2; j ++)
  359. {
  360. if ((countmediumBallss%2) == 0)
  361. {
  362. mediumBalls[countmediumBallss].position = (Vector2){bigBalls[a].position.x, bigBalls[a].position.y};
  363. mediumBalls[countmediumBallss].speed = (Vector2){ -1*BALLS_SPEED, BALLS_SPEED };
  364. }
  365. else
  366. {
  367. mediumBalls[countmediumBallss].position = (Vector2){bigBalls[a].position.x, bigBalls[a].position.y};
  368. mediumBalls[countmediumBallss].speed = (Vector2){ BALLS_SPEED, BALLS_SPEED };
  369. }
  370. mediumBalls[countmediumBallss].active = true;
  371. countmediumBallss ++;
  372. }
  373. a = MAX_BIG_BALLS;
  374. }
  375. }
  376. }
  377. if ((shoot[i].active))
  378. {
  379. for (int b = 0; b < MAX_BIG_BALLS*2; b++)
  380. {
  381. if (mediumBalls[b].active && (mediumBalls[b].position.x - mediumBalls[b].radius <= linePosition.x && mediumBalls[b].position.x + mediumBalls[b].radius >= linePosition.x)
  382. && (mediumBalls[b].position.y + mediumBalls[b].radius >= shoot[i].position.y))
  383. {
  384. shoot[i].active = false;
  385. shoot[i].lifeSpawn = 0;
  386. mediumBalls[b].active = false;
  387. meteorsDestroyed++;
  388. score += mediumBalls[b].points;
  389. for (int z = 0; z < 5; z++)
  390. {
  391. if (points[z].alpha == 0.0f)
  392. {
  393. points[z].position = mediumBalls[b].position;
  394. points[z].value = mediumBalls[b].points;
  395. points[z].alpha = 1.0f;
  396. z = 5;
  397. }
  398. }
  399. for (int j = 0; j < 2; j ++)
  400. {
  401. if (countsmallBallss%2 == 0)
  402. {
  403. smallBalls[countsmallBallss].position = (Vector2){mediumBalls[b].position.x, mediumBalls[b].position.y};
  404. smallBalls[countsmallBallss].speed = (Vector2){ BALLS_SPEED*-1, BALLS_SPEED*-1};
  405. }
  406. else
  407. {
  408. smallBalls[countsmallBallss].position = (Vector2){mediumBalls[b].position.x, mediumBalls[b].position.y};
  409. smallBalls[countsmallBallss].speed = (Vector2){ BALLS_SPEED, BALLS_SPEED*-1};
  410. }
  411. smallBalls[countsmallBallss].active = true;
  412. countsmallBallss ++;
  413. }
  414. b = MAX_BIG_BALLS*2;
  415. }
  416. }
  417. }
  418. if ((shoot[i].active))
  419. {
  420. for (int c = 0; c < MAX_BIG_BALLS*4; c++)
  421. {
  422. if (smallBalls[c].active && (smallBalls[c].position.x - smallBalls[c].radius <= linePosition.x && smallBalls[c].position.x + smallBalls[c].radius >= linePosition.x)
  423. && (smallBalls[c].position.y + smallBalls[c].radius >= shoot[i].position.y))
  424. {
  425. shoot[i].active = false;
  426. shoot[i].lifeSpawn = 0;
  427. smallBalls[c].active = false;
  428. meteorsDestroyed++;
  429. score += smallBalls[c].points;
  430. for (int z = 0; z < 5; z++)
  431. {
  432. if (points[z].alpha == 0.0f)
  433. {
  434. points[z].position = smallBalls[c].position;
  435. points[z].value = smallBalls[c].points;
  436. points[z].alpha = 1.0f;
  437. z = 5;
  438. }
  439. }
  440. c = MAX_BIG_BALLS*4;
  441. }
  442. }
  443. }
  444. }
  445. if (meteorsDestroyed == (MAX_BIG_BALLS + MAX_BIG_BALLS*2 + MAX_BIG_BALLS*4)) victory = true;
  446. }
  447. }
  448. else
  449. {
  450. if (IsKeyPressed(KEY_ENTER))
  451. {
  452. InitGame();
  453. gameOver = false;
  454. }
  455. }
  456. // Points move-up and fade logic
  457. for (int z = 0; z < 5; z++)
  458. {
  459. if (points[z].alpha > 0.0f)
  460. {
  461. points[z].position.y -= 2;
  462. points[z].alpha -= 0.02f;
  463. }
  464. if (points[z].alpha < 0.0f) points[z].alpha = 0.0f;
  465. }
  466. }
  467. // Draw game (one frame)
  468. void DrawGame(void)
  469. {
  470. BeginDrawing();
  471. ClearBackground(RAYWHITE);
  472. if (!gameOver)
  473. {
  474. // Draw player
  475. Vector2 v1 = { player.position.x + sinf(player.rotation*DEG2RAD)*(shipHeight), player.position.y - cosf(player.rotation*DEG2RAD)*(shipHeight) };
  476. Vector2 v2 = { player.position.x - cosf(player.rotation*DEG2RAD)*(PLAYER_BASE_SIZE/2), player.position.y - sinf(player.rotation*DEG2RAD)*(PLAYER_BASE_SIZE/2) };
  477. Vector2 v3 = { player.position.x + cosf(player.rotation*DEG2RAD)*(PLAYER_BASE_SIZE/2), player.position.y + sinf(player.rotation*DEG2RAD)*(PLAYER_BASE_SIZE/2) };
  478. DrawTriangle(v1, v2, v3, MAROON);
  479. // Draw meteors (big)
  480. for (int i = 0;i < MAX_BIG_BALLS; i++)
  481. {
  482. if (bigBalls[i].active) DrawCircleV(bigBalls[i].position, bigBalls[i].radius, DARKGRAY);
  483. else DrawCircleV(bigBalls[i].position, bigBalls[i].radius, Fade(LIGHTGRAY, 0.3f));
  484. }
  485. // Draw meteors (medium)
  486. for (int i = 0;i < MAX_BIG_BALLS*2; i++)
  487. {
  488. if (mediumBalls[i].active) DrawCircleV(mediumBalls[i].position, mediumBalls[i].radius, GRAY);
  489. else DrawCircleV(mediumBalls[i].position, mediumBalls[i].radius, Fade(LIGHTGRAY, 0.3f));
  490. }
  491. // Draw meteors (small)
  492. for (int i = 0;i < MAX_BIG_BALLS*4; i++)
  493. {
  494. if (smallBalls[i].active) DrawCircleV(smallBalls[i].position, smallBalls[i].radius, GRAY);
  495. else DrawCircleV(smallBalls[i].position, smallBalls[i].radius, Fade(LIGHTGRAY, 0.3f));
  496. }
  497. // Draw shoot
  498. for (int i = 0; i < PLAYER_MAX_SHOOTS; i++)
  499. {
  500. if (shoot[i].active) DrawLine(linePosition.x, linePosition.y, shoot[i].position.x, shoot[i].position.y, RED);
  501. }
  502. // Draw score points
  503. for (int z = 0; z < 5; z++)
  504. {
  505. if (points[z].alpha > 0.0f)
  506. {
  507. DrawText(TextFormat("+%02i", points[z].value), points[z].position.x, points[z].position.y, 20, Fade(BLUE, points[z].alpha));
  508. }
  509. }
  510. // Draw score (UI)
  511. DrawText(TextFormat("SCORE: %i", score), 10, 10, 20, LIGHTGRAY);
  512. if (victory)
  513. {
  514. DrawText("YOU WIN!", screenWidth/2 - MeasureText("YOU WIN!", 60)/2, 100, 60, LIGHTGRAY);
  515. DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, LIGHTGRAY);
  516. }
  517. if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, LIGHTGRAY);
  518. }
  519. else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, LIGHTGRAY);
  520. EndDrawing();
  521. }
  522. // Unload game variables
  523. void UnloadGame(void)
  524. {
  525. // TODO: Unload all dynamic loaded data (textures, sounds, models...)
  526. }
  527. // Update and Draw (one frame)
  528. void UpdateDrawFrame(void)
  529. {
  530. UpdateGame();
  531. DrawGame();
  532. }