/******************************************************************************************* * * 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 #include #include #include #if defined(PLATFORM_ANDROID) #include "android_native_app_glue.h" #endif #if defined(PLATFORM_WEB) #include #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 //------------------------------------------------------------------------------------ #if defined(PLATFORM_ANDROID) void android_main(struct android_app *app) #else int main(void) #endif { // Initialization //--------------------------------------------------------- #if defined(PLATFORM_ANDROID) InitWindow(screenWidth, screenHeight, app); #else InitWindow(screenWidth, screenHeight, "sample game: missile commander"); #endif 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 //-------------------------------------------------------------------------------------- #if !defined(PLATFORM_ANDROID) return 0; #endif } //-------------------------------------------------------------------------------------- // 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; } }