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.

566 rivejä
22 KiB

5 vuotta sitten
5 vuotta sitten
5 vuotta sitten
5 vuotta sitten
  1. /*******************************************************************************************
  2. *
  3. * raylib - sample game: gorilas
  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_BUILDINGS 15
  25. #define MAX_EXPLOSIONS 200
  26. #define MAX_PLAYERS 2
  27. #define BUILDING_RELATIVE_ERROR 30 // Building size random range %
  28. #define BUILDING_MIN_RELATIVE_HEIGHT 20 // Minimum height in % of the screenHeight
  29. #define BUILDING_MAX_RELATIVE_HEIGHT 60 // Maximum height in % of the screenHeight
  30. #define BUILDING_MIN_GRAYSCALE_COLOR 120 // Minimum gray color for the buildings
  31. #define BUILDING_MAX_GRAYSCALE_COLOR 200 // Maximum gray color for the buildings
  32. #define MIN_PLAYER_POSITION 5 // Minimum x position %
  33. #define MAX_PLAYER_POSITION 20 // Maximum x position %
  34. #define GRAVITY 9.81f
  35. #define DELTA_FPS 60
  36. //----------------------------------------------------------------------------------
  37. // Types and Structures Definition
  38. //----------------------------------------------------------------------------------
  39. typedef struct Player {
  40. Vector2 position;
  41. Vector2 size;
  42. Vector2 aimingPoint;
  43. int aimingAngle;
  44. int aimingPower;
  45. Vector2 previousPoint;
  46. int previousAngle;
  47. int previousPower;
  48. Vector2 impactPoint;
  49. bool isLeftTeam; // This player belongs to the left or to the right team
  50. bool isPlayer; // If is a player or an AI
  51. bool isAlive;
  52. } Player;
  53. typedef struct Building {
  54. Rectangle rectangle;
  55. Color color;
  56. } Building;
  57. typedef struct Explosion {
  58. Vector2 position;
  59. int radius;
  60. bool active;
  61. } Explosion;
  62. typedef struct Ball {
  63. Vector2 position;
  64. Vector2 speed;
  65. int radius;
  66. bool active;
  67. } Ball;
  68. //------------------------------------------------------------------------------------
  69. // Global Variables Declaration
  70. //------------------------------------------------------------------------------------
  71. static const int screenWidth = 800;
  72. static const int screenHeight = 450;
  73. static bool gameOver = false;
  74. static bool pause = false;
  75. static Player player[MAX_PLAYERS] = { 0 };
  76. static Building building[MAX_BUILDINGS] = { 0 };
  77. static Explosion explosion[MAX_EXPLOSIONS] = { 0 };
  78. static Ball ball = { 0 };
  79. static int playerTurn = 0;
  80. static bool ballOnAir = false;
  81. //------------------------------------------------------------------------------------
  82. // Module Functions Declaration (local)
  83. //------------------------------------------------------------------------------------
  84. static void InitGame(void); // Initialize game
  85. static void UpdateGame(void); // Update game (one frame)
  86. static void DrawGame(void); // Draw game (one frame)
  87. static void UnloadGame(void); // Unload game
  88. static void UpdateDrawFrame(void); // Update and Draw (one frame)
  89. // Additional module functions
  90. static void InitBuildings(void);
  91. static void InitPlayers(void);
  92. static bool UpdatePlayer(int playerTurn);
  93. static bool UpdateBall(int playerTurn);
  94. //------------------------------------------------------------------------------------
  95. // Program main entry point
  96. //------------------------------------------------------------------------------------
  97. int main(void)
  98. {
  99. // Initialization (Note windowTitle is unused on Android)
  100. //---------------------------------------------------------
  101. InitWindow(screenWidth, screenHeight, "sample game: gorilas");
  102. InitGame();
  103. #if defined(PLATFORM_WEB)
  104. emscripten_set_main_loop(UpdateDrawFrame, 0, 1);
  105. #else
  106. SetTargetFPS(60);
  107. //--------------------------------------------------------------------------------------
  108. // Main game loop
  109. while (!WindowShouldClose()) // Detect window close button or ESC key
  110. {
  111. // Update and Draw
  112. //----------------------------------------------------------------------------------
  113. UpdateDrawFrame();
  114. //----------------------------------------------------------------------------------
  115. }
  116. #endif
  117. // De-Initialization
  118. //--------------------------------------------------------------------------------------
  119. UnloadGame(); // Unload loaded data (textures, sounds, models...)
  120. CloseWindow(); // Close window and OpenGL context
  121. //--------------------------------------------------------------------------------------
  122. return 0;
  123. }
  124. //------------------------------------------------------------------------------------
  125. // Module Functions Definitions (local)
  126. //------------------------------------------------------------------------------------
  127. // Initialize game variables
  128. void InitGame(void)
  129. {
  130. // Init shoot
  131. ball.radius = 10;
  132. ballOnAir = false;
  133. ball.active = false;
  134. InitBuildings();
  135. InitPlayers();
  136. // Init explosions
  137. for (int i = 0; i < MAX_EXPLOSIONS; i++)
  138. {
  139. explosion[i].position = (Vector2){ 0.0f, 0.0f };
  140. explosion[i].radius = 30;
  141. explosion[i].active = false;
  142. }
  143. }
  144. // Update game (one frame)
  145. void UpdateGame(void)
  146. {
  147. if (!gameOver)
  148. {
  149. if (IsKeyPressed('P')) pause = !pause;
  150. if (!pause)
  151. {
  152. if (!ballOnAir) ballOnAir = UpdatePlayer(playerTurn); // If we are aiming
  153. else
  154. {
  155. if (UpdateBall(playerTurn)) // If collision
  156. {
  157. // Game over logic
  158. bool leftTeamAlive = false;
  159. bool rightTeamAlive = false;
  160. for (int i = 0; i < MAX_PLAYERS; i++)
  161. {
  162. if (player[i].isAlive)
  163. {
  164. if (player[i].isLeftTeam) leftTeamAlive = true;
  165. if (!player[i].isLeftTeam) rightTeamAlive = true;
  166. }
  167. }
  168. if (leftTeamAlive && rightTeamAlive)
  169. {
  170. ballOnAir = false;
  171. ball.active = false;
  172. playerTurn++;
  173. if (playerTurn == MAX_PLAYERS) playerTurn = 0;
  174. }
  175. else
  176. {
  177. gameOver = true;
  178. // if (leftTeamAlive) left team wins
  179. // if (rightTeamAlive) right team wins
  180. }
  181. }
  182. }
  183. }
  184. }
  185. else
  186. {
  187. if (IsKeyPressed(KEY_ENTER))
  188. {
  189. InitGame();
  190. gameOver = false;
  191. }
  192. }
  193. }
  194. // Draw game (one frame)
  195. void DrawGame(void)
  196. {
  197. BeginDrawing();
  198. ClearBackground(RAYWHITE);
  199. if (!gameOver)
  200. {
  201. // Draw buildings
  202. for (int i = 0; i < MAX_BUILDINGS; i++) DrawRectangleRec(building[i].rectangle, building[i].color);
  203. // Draw explosions
  204. for (int i = 0; i < MAX_EXPLOSIONS; i++)
  205. {
  206. if (explosion[i].active) DrawCircle(explosion[i].position.x, explosion[i].position.y, explosion[i].radius, RAYWHITE);
  207. }
  208. // Draw players
  209. for (int i = 0; i < MAX_PLAYERS; i++)
  210. {
  211. if (player[i].isAlive)
  212. {
  213. if (player[i].isLeftTeam) DrawRectangle(player[i].position.x - player[i].size.x/2, player[i].position.y - player[i].size.y/2,
  214. player[i].size.x, player[i].size.y, BLUE);
  215. else DrawRectangle(player[i].position.x - player[i].size.x/2, player[i].position.y - player[i].size.y/2,
  216. player[i].size.x, player[i].size.y, RED);
  217. }
  218. }
  219. // Draw ball
  220. if (ball.active) DrawCircle(ball.position.x, ball.position.y, ball.radius, MAROON);
  221. // Draw the angle and the power of the aim, and the previous ones
  222. if (!ballOnAir)
  223. {
  224. // Draw shot information
  225. /*
  226. if (player[playerTurn].isLeftTeam)
  227. {
  228. DrawText(TextFormat("Previous Point %i, %i", (int)player[playerTurn].previousPoint.x, (int)player[playerTurn].previousPoint.y), 20, 20, 20, DARKBLUE);
  229. DrawText(TextFormat("Previous Angle %i", player[playerTurn].previousAngle), 20, 50, 20, DARKBLUE);
  230. DrawText(TextFormat("Previous Power %i", player[playerTurn].previousPower), 20, 80, 20, DARKBLUE);
  231. DrawText(TextFormat("Aiming Point %i, %i", (int)player[playerTurn].aimingPoint.x, (int)player[playerTurn].aimingPoint.y), 20, 110, 20, DARKBLUE);
  232. DrawText(TextFormat("Aiming Angle %i", player[playerTurn].aimingAngle), 20, 140, 20, DARKBLUE);
  233. DrawText(TextFormat("Aiming Power %i", player[playerTurn].aimingPower), 20, 170, 20, DARKBLUE);
  234. }
  235. else
  236. {
  237. DrawText(TextFormat("Previous Point %i, %i", (int)player[playerTurn].previousPoint.x, (int)player[playerTurn].previousPoint.y), screenWidth*3/4, 20, 20, DARKBLUE);
  238. DrawText(TextFormat("Previous Angle %i", player[playerTurn].previousAngle), screenWidth*3/4, 50, 20, DARKBLUE);
  239. DrawText(TextFormat("Previous Power %i", player[playerTurn].previousPower), screenWidth*3/4, 80, 20, DARKBLUE);
  240. DrawText(TextFormat("Aiming Point %i, %i", (int)player[playerTurn].aimingPoint.x, (int)player[playerTurn].aimingPoint.y), screenWidth*3/4, 110, 20, DARKBLUE);
  241. DrawText(TextFormat("Aiming Angle %i", player[playerTurn].aimingAngle), screenWidth*3/4, 140, 20, DARKBLUE);
  242. DrawText(TextFormat("Aiming Power %i", player[playerTurn].aimingPower), screenWidth*3/4, 170, 20, DARKBLUE);
  243. }
  244. */
  245. // Draw aim
  246. if (player[playerTurn].isLeftTeam)
  247. {
  248. // Previous aiming
  249. DrawTriangle((Vector2){ player[playerTurn].position.x - player[playerTurn].size.x/4, player[playerTurn].position.y - player[playerTurn].size.y/4 },
  250. (Vector2){ player[playerTurn].position.x + player[playerTurn].size.x/4, player[playerTurn].position.y + player[playerTurn].size.y/4 },
  251. player[playerTurn].previousPoint, GRAY);
  252. // Actual aiming
  253. DrawTriangle((Vector2){ player[playerTurn].position.x - player[playerTurn].size.x/4, player[playerTurn].position.y - player[playerTurn].size.y/4 },
  254. (Vector2){ player[playerTurn].position.x + player[playerTurn].size.x/4, player[playerTurn].position.y + player[playerTurn].size.y/4 },
  255. player[playerTurn].aimingPoint, DARKBLUE);
  256. }
  257. else
  258. {
  259. // Previous aiming
  260. DrawTriangle((Vector2){ player[playerTurn].position.x - player[playerTurn].size.x/4, player[playerTurn].position.y + player[playerTurn].size.y/4 },
  261. (Vector2){ player[playerTurn].position.x + player[playerTurn].size.x/4, player[playerTurn].position.y - player[playerTurn].size.y/4 },
  262. player[playerTurn].previousPoint, GRAY);
  263. // Actual aiming
  264. DrawTriangle((Vector2){ player[playerTurn].position.x - player[playerTurn].size.x/4, player[playerTurn].position.y + player[playerTurn].size.y/4 },
  265. (Vector2){ player[playerTurn].position.x + player[playerTurn].size.x/4, player[playerTurn].position.y - player[playerTurn].size.y/4 },
  266. player[playerTurn].aimingPoint, MAROON);
  267. }
  268. }
  269. if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, GRAY);
  270. }
  271. else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, GRAY);
  272. EndDrawing();
  273. }
  274. // Unload game variables
  275. void UnloadGame(void)
  276. {
  277. // TODO: Unload all dynamic loaded data (textures, sounds, models...)
  278. }
  279. // Update and Draw (one frame)
  280. void UpdateDrawFrame(void)
  281. {
  282. UpdateGame();
  283. DrawGame();
  284. }
  285. //--------------------------------------------------------------------------------------
  286. // Additional module functions
  287. //--------------------------------------------------------------------------------------
  288. static void InitBuildings(void)
  289. {
  290. // Horizontal generation
  291. int currentWidth = 0;
  292. // We make sure the absolute error randomly generated for each building, has as a minimum value the screenWidth.
  293. // This way all the screen will be filled with buildings. Each building will have a different, random width.
  294. float relativeWidth = 100/(100 - BUILDING_RELATIVE_ERROR);
  295. float buildingWidthMean = (screenWidth*relativeWidth/MAX_BUILDINGS) + 1; // We add one to make sure we will cover the whole screen.
  296. // Vertical generation
  297. int currentHeighth = 0;
  298. int grayLevel;
  299. // Creation
  300. for (int i = 0; i < MAX_BUILDINGS; i++)
  301. {
  302. // Horizontal
  303. building[i].rectangle.x = currentWidth;
  304. building[i].rectangle.width = GetRandomValue(buildingWidthMean*(100 - BUILDING_RELATIVE_ERROR/2)/100 + 1, buildingWidthMean*(100 + BUILDING_RELATIVE_ERROR)/100);
  305. currentWidth += building[i].rectangle.width;
  306. // Vertical
  307. currentHeighth = GetRandomValue(BUILDING_MIN_RELATIVE_HEIGHT, BUILDING_MAX_RELATIVE_HEIGHT);
  308. building[i].rectangle.y = screenHeight - (screenHeight*currentHeighth/100);
  309. building[i].rectangle.height = screenHeight*currentHeighth/100 + 1;
  310. // Color
  311. grayLevel = GetRandomValue(BUILDING_MIN_GRAYSCALE_COLOR, BUILDING_MAX_GRAYSCALE_COLOR);
  312. building[i].color = (Color){ grayLevel, grayLevel, grayLevel, 255 };
  313. }
  314. }
  315. static void InitPlayers(void)
  316. {
  317. for (int i = 0; i < MAX_PLAYERS; i++)
  318. {
  319. player[i].isAlive = true;
  320. // Decide the team of this player
  321. if (i % 2 == 0) player[i].isLeftTeam = true;
  322. else player[i].isLeftTeam = false;
  323. // Now there is no AI
  324. player[i].isPlayer = true;
  325. // Set size, by default by now
  326. player[i].size = (Vector2){ 40, 40 };
  327. // Set position
  328. if (player[i].isLeftTeam) player[i].position.x = GetRandomValue(screenWidth*MIN_PLAYER_POSITION/100, screenWidth*MAX_PLAYER_POSITION/100);
  329. else player[i].position.x = screenWidth - GetRandomValue(screenWidth*MIN_PLAYER_POSITION/100, screenWidth*MAX_PLAYER_POSITION/100);
  330. for (int j = 0; j < MAX_BUILDINGS; j++)
  331. {
  332. if (building[j].rectangle.x > player[i].position.x)
  333. {
  334. // Set the player in the center of the building
  335. player[i].position.x = building[j-1].rectangle.x + building[j-1].rectangle.width/2;
  336. // Set the player at the top of the building
  337. player[i].position.y = building[j-1].rectangle.y - player[i].size.y/2;
  338. break;
  339. }
  340. }
  341. // Set statistics to 0
  342. player[i].aimingPoint = player[i].position;
  343. player[i].previousAngle = 0;
  344. player[i].previousPower = 0;
  345. player[i].previousPoint = player[i].position;
  346. player[i].aimingAngle = 0;
  347. player[i].aimingPower = 0;
  348. player[i].impactPoint = (Vector2){ -100, -100 };
  349. }
  350. }
  351. static bool UpdatePlayer(int playerTurn)
  352. {
  353. // If we are aiming at the firing quadrant, we calculate the angle
  354. if (GetMousePosition().y <= player[playerTurn].position.y)
  355. {
  356. // Left team
  357. if (player[playerTurn].isLeftTeam && GetMousePosition().x >= player[playerTurn].position.x)
  358. {
  359. // Distance (calculating the fire power)
  360. player[playerTurn].aimingPower = sqrt(pow(player[playerTurn].position.x - GetMousePosition().x, 2) + pow(player[playerTurn].position.y - GetMousePosition().y, 2));
  361. // Calculates the angle via arcsin
  362. player[playerTurn].aimingAngle = asin((player[playerTurn].position.y - GetMousePosition().y)/player[playerTurn].aimingPower)*RAD2DEG;
  363. // Point of the screen we are aiming at
  364. player[playerTurn].aimingPoint = GetMousePosition();
  365. // Ball fired
  366. if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
  367. {
  368. player[playerTurn].previousPoint = player[playerTurn].aimingPoint;
  369. player[playerTurn].previousPower = player[playerTurn].aimingPower;
  370. player[playerTurn].previousAngle = player[playerTurn].aimingAngle;
  371. ball.position = player[playerTurn].position;
  372. return true;
  373. }
  374. }
  375. // Right team
  376. else if (!player[playerTurn].isLeftTeam && GetMousePosition().x <= player[playerTurn].position.x)
  377. {
  378. // Distance (calculating the fire power)
  379. player[playerTurn].aimingPower = sqrt(pow(player[playerTurn].position.x - GetMousePosition().x, 2) + pow(player[playerTurn].position.y - GetMousePosition().y, 2));
  380. // Calculates the angle via arcsin
  381. player[playerTurn].aimingAngle = asin((player[playerTurn].position.y - GetMousePosition().y)/player[playerTurn].aimingPower)*RAD2DEG;
  382. // Point of the screen we are aiming at
  383. player[playerTurn].aimingPoint = GetMousePosition();
  384. // Ball fired
  385. if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
  386. {
  387. player[playerTurn].previousPoint = player[playerTurn].aimingPoint;
  388. player[playerTurn].previousPower = player[playerTurn].aimingPower;
  389. player[playerTurn].previousAngle = player[playerTurn].aimingAngle;
  390. ball.position = player[playerTurn].position;
  391. return true;
  392. }
  393. }
  394. else
  395. {
  396. player[playerTurn].aimingPoint = player[playerTurn].position;
  397. player[playerTurn].aimingPower = 0;
  398. player[playerTurn].aimingAngle = 0;
  399. }
  400. }
  401. else
  402. {
  403. player[playerTurn].aimingPoint = player[playerTurn].position;
  404. player[playerTurn].aimingPower = 0;
  405. player[playerTurn].aimingAngle = 0;
  406. }
  407. return false;
  408. }
  409. static bool UpdateBall(int playerTurn)
  410. {
  411. static int explosionNumber = 0;
  412. // Activate ball
  413. if (!ball.active)
  414. {
  415. if (player[playerTurn].isLeftTeam)
  416. {
  417. ball.speed.x = cos(player[playerTurn].previousAngle*DEG2RAD)*player[playerTurn].previousPower*3/DELTA_FPS;
  418. ball.speed.y = -sin(player[playerTurn].previousAngle*DEG2RAD)*player[playerTurn].previousPower*3/DELTA_FPS;
  419. ball.active = true;
  420. }
  421. else
  422. {
  423. ball.speed.x = -cos(player[playerTurn].previousAngle*DEG2RAD)*player[playerTurn].previousPower*3/DELTA_FPS;
  424. ball.speed.y = -sin(player[playerTurn].previousAngle*DEG2RAD)*player[playerTurn].previousPower*3/DELTA_FPS;
  425. ball.active = true;
  426. }
  427. }
  428. ball.position.x += ball.speed.x;
  429. ball.position.y += ball.speed.y;
  430. ball.speed.y += GRAVITY/DELTA_FPS;
  431. // Collision
  432. if (ball.position.x + ball.radius < 0) return true;
  433. else if (ball.position.x - ball.radius > screenWidth) return true;
  434. else
  435. {
  436. // Player collision
  437. for (int i = 0; i < MAX_PLAYERS; i++)
  438. {
  439. if (CheckCollisionCircleRec(ball.position, ball.radius, (Rectangle){ player[i].position.x - player[i].size.x/2, player[i].position.y - player[i].size.y/2,
  440. player[i].size.x, player[i].size.y }))
  441. {
  442. // We can't hit ourselves
  443. if (i == playerTurn) return false;
  444. else
  445. {
  446. // We set the impact point
  447. player[playerTurn].impactPoint.x = ball.position.x;
  448. player[playerTurn].impactPoint.y = ball.position.y + ball.radius;
  449. // We destroy the player
  450. player[i].isAlive = false;
  451. return true;
  452. }
  453. }
  454. }
  455. // Building collision
  456. // NOTE: We only check building collision if we are not inside an explosion
  457. for (int i = 0; i < MAX_BUILDINGS; i++)
  458. {
  459. if (CheckCollisionCircles(ball.position, ball.radius, explosion[i].position, explosion[i].radius - ball.radius))
  460. {
  461. return false;
  462. }
  463. }
  464. for (int i = 0; i < MAX_BUILDINGS; i++)
  465. {
  466. if (CheckCollisionCircleRec(ball.position, ball.radius, building[i].rectangle))
  467. {
  468. // We set the impact point
  469. player[playerTurn].impactPoint.x = ball.position.x;
  470. player[playerTurn].impactPoint.y = ball.position.y + ball.radius;
  471. // We create an explosion
  472. explosion[explosionNumber].position = player[playerTurn].impactPoint;
  473. explosion[explosionNumber].active = true;
  474. explosionNumber++;
  475. return true;
  476. }
  477. }
  478. }
  479. return false;
  480. }