| /******************************************************************************************* | |
| * | |
| *   raylib - sample game: missile commander | |
| * | |
| *   Sample game Marc Palau and Ramon Santamaria | |
| * | |
| *   This game has been created using raylib v1.3 (www.raylib.com) | |
| *   raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) | |
| * | |
| *   Copyright (c) 2015 Ramon Santamaria (@raysan5) | |
| * | |
| ********************************************************************************************/ | |
| 
 | |
| #include "raylib.h" | |
|  | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <time.h> | |
| #include <math.h> | |
|  | |
| #if defined(PLATFORM_WEB) | |
|     #include <emscripten/emscripten.h> | |
| #endif | |
|  | |
| //---------------------------------------------------------------------------------- | |
| // Some Defines | |
| //---------------------------------------------------------------------------------- | |
| #define MAX_MISSILES                100 | |
| #define MAX_INTERCEPTORS            30 | |
| #define MAX_EXPLOSIONS              100 | |
| #define LAUNCHERS_AMOUNT            3           // Not a variable, should not be changed | |
| #define BUILDINGS_AMOUNT            6           // Not a variable, should not be changed | |
|  | |
| #define LAUNCHER_SIZE               80 | |
| #define BUILDING_SIZE               60 | |
| #define EXPLOSION_RADIUS            40 | |
|  | |
| #define MISSILE_SPEED               1 | |
| #define MISSILE_LAUNCH_FRAMES       80 | |
| #define INTERCEPTOR_SPEED           10 | |
| #define EXPLOSION_INCREASE_TIME     90          // In frames | |
| #define EXPLOSION_TOTAL_TIME        210         // In frames | |
|  | |
| #define EXPLOSION_COLOR             (Color){ 125, 125, 125, 125 } | |
|  | |
| //---------------------------------------------------------------------------------- | |
| // Types and Structures Definition | |
| //---------------------------------------------------------------------------------- | |
| typedef struct Missile { | |
|     Vector2 origin; | |
|     Vector2 position; | |
|     Vector2 objective; | |
|     Vector2 speed; | |
| 
 | |
|     bool active; | |
| } Missile; | |
| 
 | |
| typedef struct Interceptor { | |
|     Vector2 origin; | |
|     Vector2 position; | |
|     Vector2 objective; | |
|     Vector2 speed; | |
| 
 | |
|     bool active; | |
| } Interceptor; | |
| 
 | |
| typedef struct Explosion { | |
|     Vector2 position; | |
|     float radiusMultiplier; | |
|     int frame; | |
|     bool active; | |
| } Explosion; | |
| 
 | |
| typedef struct Launcher { | |
|     Vector2 position; | |
|     bool active; | |
| } Launcher; | |
| 
 | |
| typedef struct Building { | |
|     Vector2 position; | |
|     bool active; | |
| } Building; | |
| 
 | |
| //------------------------------------------------------------------------------------ | |
| // Global Variables Declaration | |
| //------------------------------------------------------------------------------------ | |
| static int screenWidth = 800; | |
| static int screenHeight = 450; | |
| 
 | |
| static int framesCounter = 0; | |
| static bool gameOver = false; | |
| static bool pause = false; | |
| static int score = 0; | |
| 
 | |
| static Missile missile[MAX_MISSILES]; | |
| static Interceptor interceptor[MAX_INTERCEPTORS]; | |
| static Explosion explosion[MAX_EXPLOSIONS]; | |
| static Launcher launcher[LAUNCHERS_AMOUNT]; | |
| static Building building[BUILDINGS_AMOUNT]; | |
| static int explosionIndex = 0; | |
| 
 | |
| //------------------------------------------------------------------------------------ | |
| // Module Functions Declaration (local) | |
| //------------------------------------------------------------------------------------ | |
| static void InitGame(void);         // Initialize game | |
| static void UpdateGame(void);       // Update game (one frame) | |
| static void DrawGame(void);         // Draw game (one frame) | |
| static void UnloadGame(void);       // Unload game | |
| static void UpdateDrawFrame(void);  // Update and Draw (one frame) | |
|  | |
| // Additional module functions | |
| static void UpdateOutgoingFire(); | |
| static void UpdateIncomingFire(); | |
| 
 | |
| //------------------------------------------------------------------------------------ | |
| // Program main entry point | |
| //------------------------------------------------------------------------------------ | |
| int main(void) | |
| { | |
|     // Initialization (Note windowTitle is unused on Android) | |
|     //--------------------------------------------------------- | |
|     InitWindow(screenWidth, screenHeight, "sample game: missile commander"); | |
| 
 | |
|     InitGame(); | |
| 
 | |
| #if defined(PLATFORM_WEB) | |
|     emscripten_set_main_loop(UpdateDrawFrame, 0, 1); | |
| #else | |
|  | |
|     SetTargetFPS(60); | |
|     //-------------------------------------------------------------------------------------- | |
|      | |
|     // Main game loop | |
|     while (!WindowShouldClose())    // Detect window close button or ESC key | |
|     { | |
|         // Update and Draw | |
|         //---------------------------------------------------------------------------------- | |
|         UpdateDrawFrame(); | |
|         //---------------------------------------------------------------------------------- | |
|     } | |
| #endif | |
|  | |
|     // De-Initialization | |
|     //-------------------------------------------------------------------------------------- | |
|     UnloadGame();         // Unload loaded data (textures, sounds, models...) | |
|      | |
|     CloseWindow();        // Close window and OpenGL context | |
|     //-------------------------------------------------------------------------------------- | |
|  | |
|     return 0; | |
| } | |
| 
 | |
| //-------------------------------------------------------------------------------------- | |
| // Game Module Functions Definition | |
| //-------------------------------------------------------------------------------------- | |
|  | |
| // Initialize game variables | |
| void InitGame(void) | |
| { | |
|     // Initialize missiles | |
|     for (int i = 0; i < MAX_MISSILES; i++) | |
|     { | |
|         missile[i].origin = (Vector2){ 0, 0 }; | |
|         missile[i].speed = (Vector2){ 0, 0 }; | |
|         missile[i].position = (Vector2){ 0, 0 }; | |
| 
 | |
|         missile[i].active = false; | |
|     } | |
|      | |
|     // Initialize interceptors | |
|     for (int i = 0; i < MAX_INTERCEPTORS; i++) | |
|     { | |
|         interceptor[i].origin = (Vector2){ 0, 0 }; | |
|         interceptor[i].speed = (Vector2){ 0, 0 }; | |
|         interceptor[i].position = (Vector2){ 0, 0 }; | |
| 
 | |
|         interceptor[i].active = false; | |
|     } | |
|      | |
|     // Initialize explosions | |
|     for (int i = 0; i < MAX_EXPLOSIONS; i++) | |
|     { | |
|         explosion[i].position = (Vector2){ 0, 0 }; | |
|         explosion[i].frame = 0; | |
|         explosion[i].active = false; | |
|     } | |
|      | |
|     // Initialize buildings and launchers | |
|     int sparcing = screenWidth/(LAUNCHERS_AMOUNT + BUILDINGS_AMOUNT + 1); | |
| 
 | |
|     // Buildings and launchers placing | |
|     launcher[0].position = (Vector2){ 1*sparcing, screenHeight - LAUNCHER_SIZE/2 }; | |
|     building[0].position = (Vector2){ 2*sparcing, screenHeight - BUILDING_SIZE/2 }; | |
|     building[1].position = (Vector2){ 3*sparcing, screenHeight - BUILDING_SIZE/2 }; | |
|     building[2].position = (Vector2){ 4*sparcing, screenHeight - BUILDING_SIZE/2 }; | |
|     launcher[1].position = (Vector2){ 5*sparcing, screenHeight - LAUNCHER_SIZE/2 }; | |
|     building[3].position = (Vector2){ 6*sparcing, screenHeight - BUILDING_SIZE/2 }; | |
|     building[4].position = (Vector2){ 7*sparcing, screenHeight - BUILDING_SIZE/2 }; | |
|     building[5].position = (Vector2){ 8*sparcing, screenHeight - BUILDING_SIZE/2 }; | |
|     launcher[2].position = (Vector2){ 9*sparcing, screenHeight - LAUNCHER_SIZE/2 }; | |
| 
 | |
|     // Buildings and launchers activation | |
|     for (int i = 0; i < LAUNCHERS_AMOUNT; i++) launcher[i].active = true; | |
|     for (int i = 0; i < BUILDINGS_AMOUNT; i++) building[i].active = true; | |
| 
 | |
|     // Initialize game variables | |
|     score = 0; | |
| } | |
| 
 | |
| // Update game (one frame) | |
| void UpdateGame(void) | |
| { | |
|     if (!gameOver) | |
|     { | |
|         if (IsKeyPressed('P')) pause = !pause; | |
| 
 | |
|         if (!pause) | |
|         { | |
|             framesCounter++; | |
| 
 | |
|             static  | |
|             float distance; | |
| 
 | |
|             // Interceptors update | |
|             for (int i = 0; i < MAX_INTERCEPTORS; i++) | |
|             { | |
|                 if (interceptor[i].active) | |
|                 { | |
|                     // Update position | |
|                     interceptor[i].position.x += interceptor[i].speed.x; | |
|                     interceptor[i].position.y += interceptor[i].speed.y; | |
| 
 | |
|                     // Distance to objective | |
|                     distance = sqrt( pow(interceptor[i].position.x - interceptor[i].objective.x, 2) + | |
|                                      pow(interceptor[i].position.y - interceptor[i].objective.y, 2)); | |
| 
 | |
|                     if (distance < INTERCEPTOR_SPEED) | |
|                     { | |
|                         // Interceptor dissapears | |
|                         interceptor[i].active = false; | |
| 
 | |
|                         // Explosion | |
|                         explosion[explosionIndex].position = interceptor[i].position; | |
|                         explosion[explosionIndex].active = true; | |
|                         explosion[explosionIndex].frame = 0; | |
|                         explosionIndex++; | |
|                         if (explosionIndex == MAX_EXPLOSIONS) explosionIndex = 0; | |
| 
 | |
|                         break; | |
|                     } | |
|                 } | |
|             } | |
| 
 | |
|             // Missiles update | |
|             for (int i = 0; i < MAX_MISSILES; i++) | |
|             { | |
|                 if (missile[i].active) | |
|                 { | |
|                     // Update position | |
|                     missile[i].position.x += missile[i].speed.x; | |
|                     missile[i].position.y += missile[i].speed.y; | |
| 
 | |
|                     // Collision and missile out of bounds | |
|                     if (missile[i].position.y > screenHeight) missile[i].active = false; | |
|                     else | |
|                     { | |
|                         // CHeck collision with launchers | |
|                         for (int j = 0; j < LAUNCHERS_AMOUNT; j++) | |
|                         { | |
|                             if (launcher[j].active) | |
|                             { | |
|                                 if (CheckCollisionPointRec(missile[i].position,  (Rectangle){ launcher[j].position.x - LAUNCHER_SIZE/2, launcher[j].position.y - LAUNCHER_SIZE/2, | |
|                                                                                             LAUNCHER_SIZE, LAUNCHER_SIZE })) | |
|                                 { | |
|                                     // Missile dissapears | |
|                                     missile[i].active = false; | |
| 
 | |
|                                     // Explosion and destroy building | |
|                                     launcher[j].active = false; | |
| 
 | |
|                                     explosion[explosionIndex].position = missile[i].position; | |
|                                     explosion[explosionIndex].active = true; | |
|                                     explosion[explosionIndex].frame = 0; | |
|                                     explosionIndex++; | |
|                                     if (explosionIndex == MAX_EXPLOSIONS) explosionIndex = 0; | |
| 
 | |
|                                     break; | |
|                                 } | |
|                             } | |
|                         } | |
| 
 | |
|                         // CHeck collision with buildings | |
|                         for (int j = 0; j < BUILDINGS_AMOUNT; j++) | |
|                         { | |
|                             if (building[j].active) | |
|                             { | |
|                                 if (CheckCollisionPointRec(missile[i].position,  (Rectangle){ building[j].position.x - BUILDING_SIZE/2, building[j].position.y - BUILDING_SIZE/2, | |
|                                                                                             BUILDING_SIZE, BUILDING_SIZE })) | |
|                                 { | |
|                                     // Missile dissapears | |
|                                     missile[i].active = false; | |
| 
 | |
|                                     // Explosion and destroy building | |
|                                     building[j].active = false; | |
| 
 | |
|                                     explosion[explosionIndex].position = missile[i].position; | |
|                                     explosion[explosionIndex].active = true; | |
|                                     explosion[explosionIndex].frame = 0; | |
|                                     explosionIndex++; | |
|                                     if (explosionIndex == MAX_EXPLOSIONS) explosionIndex = 0; | |
| 
 | |
|                                     break; | |
|                                 } | |
|                             } | |
|                         } | |
| 
 | |
|                         // CHeck collision with explosions | |
|                         for (int j = 0; j < MAX_EXPLOSIONS; j++) | |
|                         { | |
|                             if (explosion[j].active) | |
|                             { | |
|                                 if (CheckCollisionPointCircle(missile[i].position, explosion[j].position, EXPLOSION_RADIUS*explosion[j].radiusMultiplier)) | |
|                                 { | |
|                                     // Missile dissapears and we earn 100 points | |
|                                     missile[i].active = false; | |
|                                     score += 100; | |
| 
 | |
|                                     explosion[explosionIndex].position = missile[i].position; | |
|                                     explosion[explosionIndex].active = true; | |
|                                     explosion[explosionIndex].frame = 0; | |
|                                     explosionIndex++; | |
|                                     if (explosionIndex == MAX_EXPLOSIONS) explosionIndex = 0; | |
| 
 | |
|                                     break; | |
|                                 } | |
|                             } | |
|                         } | |
|                     } | |
|                 } | |
|             } | |
| 
 | |
|             // Explosions update | |
|             for (int i = 0; i < MAX_EXPLOSIONS; i++) | |
|             { | |
|                 if (explosion[i].active) | |
|                 { | |
|                     explosion[i].frame++; | |
| 
 | |
|                     if (explosion[i].frame <= EXPLOSION_INCREASE_TIME) explosion[i].radiusMultiplier = explosion[i].frame/(float)EXPLOSION_INCREASE_TIME; | |
|                     else if (explosion[i].frame <= EXPLOSION_TOTAL_TIME) explosion[i].radiusMultiplier = 1 - (explosion[i].frame - (float)EXPLOSION_INCREASE_TIME)/(float)EXPLOSION_TOTAL_TIME; | |
|                     else | |
|                     { | |
|                         explosion[i].frame = 0; | |
|                         explosion[i].active = false; | |
|                     } | |
|                 } | |
|             } | |
| 
 | |
|             // Fire logic | |
|             UpdateOutgoingFire(); | |
|             UpdateIncomingFire(); | |
| 
 | |
|             // Game over logic | |
|             int checker = 0; | |
| 
 | |
|             for (int i = 0; i < LAUNCHERS_AMOUNT; i++) | |
|             { | |
|                 if (!launcher[i].active) checker++; | |
|                 if (checker == LAUNCHERS_AMOUNT) gameOver = true; | |
|             } | |
| 
 | |
|             checker = 0; | |
|             for (int i = 0; i < BUILDINGS_AMOUNT; i++) | |
|             { | |
|                 if (!building[i].active) checker++; | |
|                 if (checker == BUILDINGS_AMOUNT) gameOver = true; | |
|             } | |
|         } | |
|     } | |
|     else | |
|     { | |
|         if (IsKeyPressed(KEY_ENTER)) | |
|         { | |
|             InitGame(); | |
|             gameOver = false; | |
|         } | |
|     } | |
| } | |
| 
 | |
| // Draw game (one frame) | |
| void DrawGame(void) | |
| { | |
|     BeginDrawing(); | |
| 
 | |
|         ClearBackground(RAYWHITE); | |
| 
 | |
|         if (!gameOver) | |
|         { | |
|             // Draw missiles | |
|             for (int i = 0; i < MAX_MISSILES; i++) | |
|             { | |
|                 if (missile[i].active) | |
|                 { | |
|                     DrawLine(missile[i].origin.x, missile[i].origin.y, missile[i].position.x, missile[i].position.y, RED); | |
| 
 | |
|                     if (framesCounter % 16 < 8) DrawCircle(missile[i].position.x, missile[i].position.y, 3, YELLOW); | |
|                 } | |
|             } | |
|              | |
|             // Draw interceptors | |
|             for (int i = 0; i < MAX_INTERCEPTORS; i++) | |
|             { | |
|                 if (interceptor[i].active) | |
|                 { | |
|                     DrawLine(interceptor[i].origin.x, interceptor[i].origin.y, interceptor[i].position.x, interceptor[i].position.y, GREEN); | |
| 
 | |
|                     if (framesCounter % 16 < 8) DrawCircle(interceptor[i].position.x, interceptor[i].position.y, 3, BLUE); | |
|                 } | |
|             } | |
|              | |
|             // Draw explosions | |
|             for (int i = 0; i < MAX_EXPLOSIONS; i++) | |
|             { | |
|                 if (explosion[i].active) DrawCircle(explosion[i].position.x, explosion[i].position.y, EXPLOSION_RADIUS*explosion[i].radiusMultiplier, EXPLOSION_COLOR); | |
|             } | |
| 
 | |
|             // Draw buildings and launchers | |
|             for (int i = 0; i < LAUNCHERS_AMOUNT; i++) | |
|             { | |
|                 if (launcher[i].active) DrawRectangle(launcher[i].position.x - LAUNCHER_SIZE/2, launcher[i].position.y - LAUNCHER_SIZE/2, LAUNCHER_SIZE, LAUNCHER_SIZE, GRAY); | |
|             } | |
| 
 | |
|             for (int i = 0; i < BUILDINGS_AMOUNT; i++) | |
|             { | |
|                 if (building[i].active) DrawRectangle(building[i].position.x - BUILDING_SIZE/2, building[i].position.y - BUILDING_SIZE/2, BUILDING_SIZE, BUILDING_SIZE, LIGHTGRAY); | |
|             } | |
| 
 | |
|             // Draw score | |
|             DrawText(FormatText("SCORE %4i", score), 20, 20, 40, LIGHTGRAY); | |
|              | |
|             if (pause) DrawText("GAME PAUSED", screenWidth/2 - MeasureText("GAME PAUSED", 40)/2, screenHeight/2 - 40, 40, GRAY); | |
|         } | |
|         else DrawText("PRESS [ENTER] TO PLAY AGAIN", GetScreenWidth()/2 - MeasureText("PRESS [ENTER] TO PLAY AGAIN", 20)/2, GetScreenHeight()/2 - 50, 20, GRAY); | |
| 
 | |
|     EndDrawing(); | |
| } | |
| 
 | |
| // Unload game variables | |
| void UnloadGame(void) | |
| { | |
|     // TODO: Unload all dynamic loaded data (textures, sounds, models...) | |
| } | |
| 
 | |
| // Update and Draw (one frame) | |
| void UpdateDrawFrame(void) | |
| { | |
|     UpdateGame(); | |
|     DrawGame(); | |
| } | |
| 
 | |
| //-------------------------------------------------------------------------------------- | |
| // Additional module functions | |
| //-------------------------------------------------------------------------------------- | |
| static void UpdateOutgoingFire() | |
| { | |
|     static int interceptorNumber = 0; | |
|     int launcherShooting = 0; | |
| 
 | |
|     if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) launcherShooting = 1; | |
|     if (IsMouseButtonPressed(MOUSE_MIDDLE_BUTTON)) launcherShooting = 2; | |
|     if (IsMouseButtonPressed(MOUSE_RIGHT_BUTTON)) launcherShooting = 3; | |
| 
 | |
|     if (launcherShooting > 0 && launcher[launcherShooting - 1].active) | |
|     { | |
|         float module; | |
|         float sideX; | |
|         float sideY; | |
| 
 | |
|         // Activate the interceptor | |
|         interceptor[interceptorNumber].active = true; | |
| 
 | |
|         // Assign start position | |
|         interceptor[interceptorNumber].origin = launcher[launcherShooting - 1].position; | |
|         interceptor[interceptorNumber].position = interceptor[interceptorNumber].origin; | |
|         interceptor[interceptorNumber].objective = GetMousePosition(); | |
| 
 | |
|         // Calculate speed | |
|         module = sqrt( pow(interceptor[interceptorNumber].objective.x - interceptor[interceptorNumber].origin.x, 2) + | |
|                        pow(interceptor[interceptorNumber].objective.y - interceptor[interceptorNumber].origin.y, 2)); | |
| 
 | |
|         sideX = (interceptor[interceptorNumber].objective.x - interceptor[interceptorNumber].origin.x)*INTERCEPTOR_SPEED/module; | |
|         sideY = (interceptor[interceptorNumber].objective.y - interceptor[interceptorNumber].origin.y)*INTERCEPTOR_SPEED/module; | |
| 
 | |
|         interceptor[interceptorNumber].speed = (Vector2){ sideX, sideY }; | |
| 
 | |
|         // Update | |
|         interceptorNumber++; | |
|         if (interceptorNumber == MAX_INTERCEPTORS) interceptorNumber = 0; | |
|     } | |
| } | |
| 
 | |
| static void UpdateIncomingFire() | |
| { | |
|     static int missileIndex = 0; | |
| 
 | |
|     // Launch missile | |
|     if (framesCounter % MISSILE_LAUNCH_FRAMES == 0) | |
|     { | |
|         float module; | |
|         float sideX; | |
|         float sideY; | |
| 
 | |
|         // Activate the missile | |
|         missile[missileIndex].active = true; | |
| 
 | |
|         // Assign start position | |
|         missile[missileIndex].origin = (Vector2){ GetRandomValue(20, screenWidth - 20), -10 }; | |
|         missile[missileIndex].position = missile[missileIndex].origin; | |
|         missile[missileIndex].objective = (Vector2){ GetRandomValue(20, screenWidth - 20), screenHeight + 10 }; | |
| 
 | |
|         // Calculate speed | |
|         module = sqrt( pow(missile[missileIndex].objective.x - missile[missileIndex].origin.x, 2) + | |
|                        pow(missile[missileIndex].objective.y - missile[missileIndex].origin.y, 2)); | |
| 
 | |
|         sideX = (missile[missileIndex].objective.x - missile[missileIndex].origin.x)*MISSILE_SPEED/module; | |
|         sideY = (missile[missileIndex].objective.y - missile[missileIndex].origin.y)*MISSILE_SPEED/module; | |
| 
 | |
|         missile[missileIndex].speed = (Vector2){ sideX, sideY }; | |
| 
 | |
|         // Update | |
|         missileIndex++; | |
|         if (missileIndex == MAX_MISSILES) missileIndex = 0; | |
|     } | |
| }
 |