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//----------------------------------------------------------------------------------
49typedef struct Missile {
50 Vector2 origin;
51 Vector2 position;
52 Vector2 objective;
53 Vector2 speed;
54
55 bool active;
56} Missile;
57
58typedef struct Interceptor {
59 Vector2 origin;
60 Vector2 position;
61 Vector2 objective;
62 Vector2 speed;
63
64 bool active;
65} Interceptor;
66
67typedef struct Explosion {
68 Vector2 position;
69 float radiusMultiplier;
70 int frame;
71 bool active;
72} Explosion;
73
74typedef struct Launcher {
75 Vector2 position;
76 bool active;
77} Launcher;
78
79typedef struct Building {
80 Vector2 position;
81 bool active;
82} Building;
83
84//------------------------------------------------------------------------------------
85// Global Variables Declaration
86//------------------------------------------------------------------------------------
87static int screenWidth = 800;
88static int screenHeight = 450;
89
90static int framesCounter = 0;
91static bool gameOver = false;
92static bool pause = false;
93static int score = 0;
94
95static Missile missile[MAX_MISSILES] = { 0 };
96static Interceptor interceptor[MAX_INTERCEPTORS] = { 0 };
97static Explosion explosion[MAX_EXPLOSIONS] = { 0 };
98static Launcher launcher[LAUNCHERS_AMOUNT] = { 0 };
99static Building building[BUILDINGS_AMOUNT] = { 0 };
100static int explosionIndex = 0;
101
102//------------------------------------------------------------------------------------
103// Module Functions Declaration (local)
104//------------------------------------------------------------------------------------
105static void InitGame(void); // Initialize game
106static void UpdateGame(void); // Update game (one frame)
107static void DrawGame(void); // Draw game (one frame)
108static void UnloadGame(void); // Unload game
109static void UpdateDrawFrame(void); // Update and Draw (one frame)
110
111// Additional module functions
112static void UpdateOutgoingFire();
113static void UpdateIncomingFire();
114
115//------------------------------------------------------------------------------------
116// Program main entry point
117//------------------------------------------------------------------------------------
118int 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
156void 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)
209void 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)
389void 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
447void UnloadGame(void)
448{
449 // TODO: Unload all dynamic loaded data (textures, sounds, models...)
450}
451
452// Update and Draw (one frame)
453void UpdateDrawFrame(void)
454{
455 UpdateGame();
456 DrawGame();
457}
458
459//--------------------------------------------------------------------------------------
460// Additional module functions
461//--------------------------------------------------------------------------------------
462static 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
500static 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