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