| /********************************************************************************************** | |
| * | |
| *   raylib - Advance Game template | |
| * | |
| *   Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) | |
| * | |
| *   Copyright (c) 2014 Ramon Santamaria (@raysan5) | |
| * | |
| *   This software is provided "as-is", without any express or implied warranty. In no event | |
| *   will the authors be held liable for any damages arising from the use of this software. | |
| * | |
| *   Permission is granted to anyone to use this software for any purpose, including commercial | |
| *   applications, and to alter it and redistribute it freely, subject to the following restrictions: | |
| * | |
| *     1. The origin of this software must not be misrepresented; you must not claim that you | |
| *     wrote the original software. If you use this software in a product, an acknowledgment | |
| *     in the product documentation would be appreciated but is not required. | |
| * | |
| *     2. Altered source versions must be plainly marked as such, and must not be misrepresented | |
| *     as being the original software. | |
| * | |
| *     3. This notice may not be removed or altered from any source distribution. | |
| * | |
| **********************************************************************************************/ | |
| 
 | |
| #include "raylib.h" | |
| #include "screens.h" | |
|  | |
| #include <math.h> | |
|  | |
| #define MAX_LIGHTS_I         8 | |
| #define MAX_LIGHTS_II       12 | |
| #define MAX_LIGHTS_III      20 | |
|  | |
| #define MAX_ENEMIES          8 | |
|  | |
| #define MAX_PLAYER_ENERGY   40.0f | |
| #define ENERGY_REFILL_RATIO  0.2f | |
|  | |
| #define GAMEPAD_SENSITIVITY  4.0f     // More sensitivity, more speed :P | |
|  | |
| #define LIGHT_ANIM_FRAMES    7 | |
|  | |
| //---------------------------------------------------------------------------------- | |
| // Types and Structures Definition | |
| //---------------------------------------------------------------------------------- | |
| typedef struct Player { | |
|     Vector2 position; | |
|     Vector2 speed; | |
|     int radius; | |
|     Color color; | |
|     float lightEnergy; | |
| } Player; | |
| 
 | |
| typedef struct Enemy { | |
|     Vector2 position; | |
|     Vector2 targetPos;  // light target position | |
|     int targetNum;      // light target number | |
|     float speed;        // scalar value | |
|     int radius; | |
|     int active; | |
|     int awakeFramesDelay; | |
|     int framesCounter; | |
|     Color color; | |
| } Enemy; | |
| 
 | |
| typedef struct LightSpot { | |
|     Vector2 position; | |
|     int radius; | |
|     int requiredEnergy; | |
|     bool active; | |
|     Color color; | |
|      | |
|     int framesCounter; | |
|     int currentFrame; | |
|     Rectangle frameRec; | |
| } LightSpot; | |
| 
 | |
| typedef enum { LEVEL_I, LEVEL_II, LEVEL_III, LEVEL_FINISHED } LightedLevel; | |
| 
 | |
| //---------------------------------------------------------------------------------- | |
| // Global Variables Definition (local to this module) | |
| //---------------------------------------------------------------------------------- | |
|  | |
| // Gameplay screen global variables | |
| static int framesCounter; | |
| static int finishScreen; | |
| 
 | |
| //static Texture2D background; | |
|  | |
| static bool pause; | |
| 
 | |
| static Player player; | |
| 
 | |
| static LightSpot lightsI[MAX_LIGHTS_I]; | |
| static LightSpot lightsII[MAX_LIGHTS_II]; | |
| static LightSpot lightsIII[MAX_LIGHTS_III]; | |
| 
 | |
| static Enemy enemies[MAX_ENEMIES]; | |
| 
 | |
| static int ritualLevel; | |
| static int previousLightedLevel; | |
| static int currentLightedLevel; | |
| 
 | |
| static Vector2 lighterPosition; | |
| 
 | |
| static int maxLightEnergy; | |
| static int currentLightEnergy; | |
| 
 | |
| static float ritualTime; | |
| static bool startRitual; | |
| static float alphaRitual; | |
| 
 | |
| static bool timeOver; | |
| static int nextStarsAlignment; | |
| 
 | |
| static Texture2D background; | |
| static Texture2D foregroundI; | |
| static Texture2D foregroundII; | |
| static Texture2D foregroundIII; | |
| static Texture2D texPlayer; | |
| static Texture2D texEnemy; | |
| static Texture2D texLight; | |
| static Texture2D lightGlow; | |
| static Texture2D lightRay; | |
| static Texture2D book; | |
| static Texture2D texRitual; | |
| static Texture2D texTimeOver; | |
| static Texture2D circleIoff, circleIIoff, circleIIIoff; | |
| static Texture2D circleIon, circleIIon, circleIIIon; | |
| 
 | |
| static Rectangle lightOff, lightOn; | |
| 
 | |
| static Sound fxLightOn, fxLightOff; | |
| 
 | |
| static Music music; | |
| 
 | |
| // Debug variables | |
| static bool enemiesStopped; | |
| 
 | |
| //------------------------------------------------------------------------------------ | |
| // Module Functions Declaration (local) | |
| //------------------------------------------------------------------------------------ | |
| static bool ColorEqual(Color col1, Color col2);    // Check if two colors are equal | |
| static Vector2 Vector2Subtract(Vector2 v1, Vector2 v2); | |
| static void Vector2Normalize(Vector2 *v); | |
| static void EnemyReset(Enemy *enemy); | |
| 
 | |
| //---------------------------------------------------------------------------------- | |
| // Gameplay Screen Functions Definition | |
| //---------------------------------------------------------------------------------- | |
|  | |
| // Gameplay Screen Initialization logic | |
| void InitGameplayScreen(void) | |
| { | |
|     framesCounter = 0; | |
|     finishScreen = 0; | |
|     pause = false; | |
|      | |
|     // Textures loading | |
|     background = LoadTexture("resources/textures/background.png"); | |
|     foregroundI = LoadTexture("resources/textures/foreground_level_i.png"); | |
|     foregroundII = LoadTexture("resources/textures/foreground_level_ii.png"); | |
|     foregroundIII = LoadTexture("resources/textures/foreground_level_iii.png"); | |
|     texPlayer = LoadTexture("resources/textures/player.png"); | |
|     texEnemy = LoadTexture("resources/textures/enemy.png"); | |
|     texLight = LoadTexture("resources/textures/light.png"); | |
|     lightGlow = LoadTexture("resources/textures/light_glow.png"); | |
|     lightRay = LoadTexture("resources/textures/light_ray.png"); | |
|     book = LoadTexture("resources/textures/book.png"); | |
|     texRitual = LoadTexture("resources/textures/msg_ritual.png"); | |
|     texTimeOver = LoadTexture("resources/textures/time_over.png"); | |
|      | |
|     circleIoff = LoadTexture("resources/textures/circle_level_i_off.png"); | |
|     circleIIoff = LoadTexture("resources/textures/circle_level_ii_off.png"); | |
|     circleIIIoff = LoadTexture("resources/textures/circle_level_iii_off.png"); | |
|     circleIon = LoadTexture("resources/textures/circle_level_i_on.png"); | |
|     circleIIon = LoadTexture("resources/textures/circle_level_ii_on.png"); | |
|     circleIIIon = LoadTexture("resources/textures/circle_level_iii_on.png"); | |
| 
 | |
|     lightOff = (Rectangle){ 0, 0, 64, 64 }; | |
|     lightOn = (Rectangle){ 64, 0, 64, 64 }; | |
|      | |
|     fxLightOn = LoadSound("resources/audio/light_on.wav"); | |
|     fxLightOff = LoadSound("resources/audio/light_off.wav"); | |
|      | |
|     // Initialize player | |
|     player.position = (Vector2){ GetScreenWidth()/2, GetScreenHeight()/2 - 40 }; | |
|     player.radius = 20; | |
|     player.speed = (Vector2){5, 5}; | |
|     player.color = WHITE; | |
|      | |
|     // Initialize lights positions based on lights map image data | |
|     int kI = 0, kII = 0, kIII = 0; | |
|     for (int y = 0; y < lightsMapHeight; y++) | |
|     { | |
|         for (int x = 0; x < lightsMapWidth; x++) | |
|         { | |
|             if (ColorEqual(lightsMap[y*lightsMapWidth + x], (Color){ 255, 0, 0, 255 })) | |
|             { | |
|                 // Store light position I | |
|                 lightsI[kI].position.x = (float)x*10; | |
|                 lightsI[kI].position.y = (float)y*10; | |
|                 kI++; | |
|                  | |
|                 //printf("Light %02i position: %i, %i\n", kI, (int)lightsI[kI - 1].position.x, (int)lightsI[kI - 1].position.y); | |
|             } | |
|             else if (ColorEqual(lightsMap[y*lightsMapWidth + x], (Color){ 0, 255, 0, 255 })) | |
|             { | |
|                 // Store light position II | |
|                 lightsII[kII].position.x = (float)x*10; | |
|                 lightsII[kII].position.y = (float)y*10; | |
|                 kII++; | |
|             } | |
|             else if (ColorEqual(lightsMap[y*lightsMapWidth + x], (Color){ 0, 0, 255, 255 })) | |
|             { | |
|                 // Store light position III | |
|                 lightsIII[kIII].position.x = (float)x*10; | |
|                 lightsIII[kIII].position.y = (float)y*10; | |
|                 kIII++; | |
|             } | |
|         } | |
|     } | |
|      | |
|     // Initialize lights I | |
|     for (int i = 0; i < MAX_LIGHTS_I; i++) | |
|     { | |
|         lightsI[i].radius = 12; | |
|         lightsI[i].requiredEnergy = GetRandomValue(3, 9); | |
|         lightsI[i].active = false; | |
|         lightsI[i].color = GOLD; | |
|          | |
|         lightsI[i].framesCounter = 0; | |
|         lightsI[i].currentFrame = 0; | |
|         lightsI[i].frameRec = (Rectangle){ 0, 0, 64, 64 }; | |
|     } | |
|      | |
|     // Initialize lights II | |
|     for (int i = 0; i < MAX_LIGHTS_II; i++) | |
|     { | |
|         lightsII[i].radius = 8; | |
|         lightsII[i].requiredEnergy = GetRandomValue(3, 8); | |
|         lightsII[i].active = false; | |
|         lightsII[i].color = GOLD; | |
|          | |
|         lightsII[i].framesCounter = 0; | |
|         lightsII[i].currentFrame = 0; | |
|         lightsII[i].frameRec = (Rectangle){ 0, 0, 64, 64 }; | |
|     } | |
|      | |
|     // Initialize lights III | |
|     for (int i = 0; i < MAX_LIGHTS_III; i++) | |
|     { | |
|         lightsIII[i].radius = 8; | |
|         lightsIII[i].requiredEnergy = GetRandomValue(4, 10); | |
|         lightsIII[i].active = false; | |
|         lightsIII[i].color = GOLD; | |
|          | |
|         lightsIII[i].framesCounter = 0; | |
|         lightsIII[i].currentFrame = 0; | |
|         lightsIII[i].frameRec = (Rectangle){ 0, 0, 64, 64 }; | |
|     } | |
|      | |
|     // Initialize ritual level | |
|     ritualLevel = 0; | |
|     currentLightedLevel = LEVEL_I; | |
|     lighterPosition = (Vector2){ GetScreenWidth()/2, GetScreenHeight()/2 }; | |
| 
 | |
|     // Initialize enemies | |
|     for (int i = 0; i < MAX_ENEMIES; i++) EnemyReset(&enemies[i]); | |
|      | |
|     // Initialize max light energy (depends on lights randomness) | |
|     maxLightEnergy = 0; | |
|      | |
|     for (int i = 0; i < MAX_LIGHTS_I; i++) maxLightEnergy += lightsI[i].requiredEnergy; | |
|     for (int i = 0; i < MAX_LIGHTS_II; i++) maxLightEnergy += lightsII[i].requiredEnergy; | |
|     for (int i = 0; i < MAX_LIGHTS_III; i++) maxLightEnergy += lightsIII[i].requiredEnergy; | |
|      | |
|     //printf("Max light energy: %i\n", maxLightEnergy); | |
|      | |
|     // Initialize ritual variables | |
|     ritualTime = 0.0f; | |
|     startRitual = false;; | |
|     alphaRitual = 0.0f; | |
|      | |
|     timeOver = false; | |
|     nextStarsAlignment = GetRandomValue(500, 1000); | |
|      | |
|     enemiesStopped = false; | |
|      | |
|     music = LoadMusicStream("resources/audio/ritual.ogg"); | |
|     PlayMusicStream(music); | |
| } | |
| 
 | |
| // Gameplay Screen Update logic | |
| void UpdateGameplayScreen(void) | |
| { | |
|     if (IsKeyPressed('P')) pause = !pause; | |
| 
 | |
|     if (!pause && (currentLightedLevel != LEVEL_FINISHED) && !timeOver) | |
|     { | |
|         framesCounter++;        // Time starts counting to awake enemies | |
|          | |
|         // Player movement logic | |
|         if (IsKeyDown(KEY_RIGHT)) player.position.x += player.speed.x; | |
|         else if (IsKeyDown(KEY_LEFT)) player.position.x -= player.speed.x; | |
|          | |
|         if (IsKeyDown(KEY_UP)) player.position.y -= player.speed.y; | |
|         else if (IsKeyDown(KEY_DOWN)) player.position.y += player.speed.y; | |
|          | |
|         // Debug key to stop enemies | |
|         if (IsKeyPressed(KEY_S)) enemiesStopped = !enemiesStopped; | |
|          | |
|         /* | |
|         if (IsGamepadAvailable(GAMEPAD_PLAYER1)) | |
|         { | |
|             Vector2 movement = GetGamepadMovement(GAMEPAD_PLAYER1); | |
|              | |
|             player.position.x += movement.x*GAMEPAD_SENSITIVITY; | |
|             player.position.y += movement.y*GAMEPAD_SENSITIVITY; | |
|         } | |
|         */ | |
|          | |
|         // Player light energy filling logic | |
|         if (CheckCollisionCircles(player.position, player.radius, lighterPosition, 50)) | |
|         { | |
|             player.lightEnergy += ENERGY_REFILL_RATIO; | |
|             player.color = (Color){ 255, 255, 100, 255 }; | |
|         } | |
|         else player.color = WHITE; | |
|          | |
|         if (player.lightEnergy > MAX_PLAYER_ENERGY) player.lightEnergy = MAX_PLAYER_ENERGY; | |
|          | |
|         // Player vs lights collision detection (depends on lighted level) | |
|         if (currentLightedLevel == LEVEL_I) | |
|         { | |
|             for (int i = 0; i < MAX_LIGHTS_I; i++) | |
|             { | |
|                 // Check player vs lightI collision | |
|                 if (CheckCollisionCircles(player.position, player.radius, lightsI[i].position, lightsI[i].radius)) | |
|                 { | |
|                     if (!lightsI[i].active && (player.lightEnergy >= lightsI[i].requiredEnergy)) | |
|                     { | |
|                         lightsI[i].active = true; | |
|                         lightsI[i].currentFrame = 1; | |
|                         player.lightEnergy -= lightsI[i].requiredEnergy; | |
|                          | |
|                         PlaySound(fxLightOn); | |
|                     } | |
|                 } | |
|             } | |
|         } | |
|         else if (currentLightedLevel == LEVEL_II) | |
|         { | |
|             for (int i = 0; i < MAX_LIGHTS_II; i++) | |
|             { | |
|                 if (CheckCollisionCircles(player.position, player.radius, lightsII[i].position, lightsII[i].radius)) | |
|                 { | |
|                     if (!lightsII[i].active && (player.lightEnergy >= lightsII[i].requiredEnergy)) | |
|                     { | |
|                         lightsII[i].active = true; | |
|                         player.lightEnergy -= lightsII[i].requiredEnergy; | |
|                          | |
|                         PlaySound(fxLightOn); | |
|                     } | |
|                 } | |
|             } | |
|         } | |
|         else if (currentLightedLevel == LEVEL_III) | |
|         { | |
|             for (int i = 0; i < MAX_LIGHTS_III; i++) | |
|             { | |
|                 if (CheckCollisionCircles(player.position, player.radius, lightsIII[i].position, lightsIII[i].radius)) | |
|                 { | |
|                     if (!lightsIII[i].active && (player.lightEnergy >= lightsIII[i].requiredEnergy)) | |
|                     { | |
|                         lightsIII[i].active = true; | |
|                         player.lightEnergy -= lightsIII[i].requiredEnergy; | |
|                          | |
|                         PlaySound(fxLightOn); | |
|                     } | |
|                 } | |
|             } | |
|         } | |
|          | |
|         // Lights animation (it doesn't depend on currentLightedLevel) | |
|         for (int i = 0; i < MAX_LIGHTS_I; i++) | |
|         { | |
|             // Light animation | |
|             if (lightsI[i].active) | |
|             { | |
|                 lightsI[i].framesCounter++; | |
|                  | |
|                 if (lightsI[i].framesCounter > 10) | |
|                 { | |
|                     lightsI[i].currentFrame++; | |
|                      | |
|                     if (lightsI[i].currentFrame > LIGHT_ANIM_FRAMES - 1) lightsI[i].currentFrame = 1; | |
|                      | |
|                     lightsI[i].framesCounter = 0; | |
|                 } | |
|             } | |
|              | |
|             lightsI[i].frameRec.x = lightsI[i].currentFrame*texLight.width/LIGHT_ANIM_FRAMES; | |
|         } | |
|          | |
|         for (int i = 0; i < MAX_LIGHTS_II; i++) | |
|         { | |
|             // Light animation | |
|             if (lightsII[i].active) | |
|             { | |
|                 lightsII[i].framesCounter++; | |
|                  | |
|                 if (lightsII[i].framesCounter > 10) | |
|                 { | |
|                     lightsII[i].currentFrame++; | |
|                      | |
|                     if (lightsII[i].currentFrame > LIGHT_ANIM_FRAMES - 1) lightsII[i].currentFrame = 1; | |
|                      | |
|                     lightsII[i].framesCounter = 0; | |
|                 } | |
|             } | |
|              | |
|             lightsII[i].frameRec.x = lightsII[i].currentFrame*texLight.width/LIGHT_ANIM_FRAMES; | |
|         } | |
|          | |
|         for (int i = 0; i < MAX_LIGHTS_III; i++) | |
|         { | |
|             // Light animation | |
|             if (lightsIII[i].active) | |
|             { | |
|                 lightsIII[i].framesCounter++; | |
|                  | |
|                 if (lightsIII[i].framesCounter > 10) | |
|                 { | |
|                     lightsIII[i].currentFrame++; | |
|                      | |
|                     if (lightsIII[i].currentFrame > LIGHT_ANIM_FRAMES - 1) lightsIII[i].currentFrame = 1; | |
|                      | |
|                     lightsIII[i].framesCounter = 0; | |
|                 } | |
|             } | |
|              | |
|             lightsIII[i].frameRec.x = lightsIII[i].currentFrame*texLight.width/LIGHT_ANIM_FRAMES; | |
|         } | |
| 
 | |
|         // Enemies logic | |
|         if (!enemiesStopped) | |
|         { | |
|             for (int i = 0; i < MAX_ENEMIES; i++) | |
|             { | |
|                 if (!enemies[i].active) enemies[i].framesCounter++; | |
|                  | |
|                 if (enemies[i].framesCounter > enemies[i].awakeFramesDelay) enemies[i].active = true; | |
|                  | |
|                 if (enemies[i].active) | |
|                 { | |
|                     // Move to the target | |
|                     Vector2 dir = Vector2Subtract(enemies[i].targetPos, enemies[i].position); | |
|                     Vector2Normalize(&dir); | |
|                      | |
|                     enemies[i].position.x += dir.x*enemies[i].speed; | |
|                     enemies[i].position.y += dir.y*enemies[i].speed; | |
|                      | |
|                     if (currentLightedLevel == LEVEL_I) | |
|                     { | |
|                         if (CheckCollisionCircles(enemies[i].position, enemies[i].radius, enemies[i].targetPos, lightsI[enemies[i].targetNum].radius)) | |
|                         { | |
|                             lightsI[enemies[i].targetNum].active = false; | |
|                             lightsI[enemies[i].targetNum].framesCounter = 0; | |
|                             lightsI[enemies[i].targetNum].currentFrame = 0; | |
|                             lightsI[enemies[i].targetNum].frameRec = (Rectangle){ 0, 0, 64, 64 }; | |
|                              | |
|                             EnemyReset(&enemies[i]); | |
|                              | |
|                             PlaySound(fxLightOff); | |
|                         } | |
|                     } | |
|                     else if (currentLightedLevel == LEVEL_II) | |
|                     { | |
|                         if (CheckCollisionCircles(enemies[i].position, enemies[i].radius, enemies[i].targetPos, lightsII[enemies[i].targetNum].radius)) | |
|                         { | |
|                             lightsII[enemies[i].targetNum].active = false; | |
|                             lightsII[enemies[i].targetNum].framesCounter = 0; | |
|                             lightsII[enemies[i].targetNum].currentFrame = 0; | |
|                             lightsII[enemies[i].targetNum].frameRec = (Rectangle){ 0, 0, 64, 64 }; | |
|                              | |
|                             EnemyReset(&enemies[i]); | |
|                              | |
|                             PlaySound(fxLightOff); | |
|                         } | |
|                     } | |
|                     else if (currentLightedLevel == LEVEL_III) | |
|                     { | |
|                         if (CheckCollisionCircles(enemies[i].position, enemies[i].radius, enemies[i].targetPos, lightsIII[enemies[i].targetNum].radius)) | |
|                         { | |
|                             lightsIII[enemies[i].targetNum].active = false; | |
|                             lightsIII[enemies[i].targetNum].framesCounter = 0; | |
|                             lightsIII[enemies[i].targetNum].currentFrame = 0; | |
|                             lightsIII[enemies[i].targetNum].frameRec = (Rectangle){ 0, 0, 64, 64 }; | |
|                              | |
|                             EnemyReset(&enemies[i]); | |
|                              | |
|                             PlaySound(fxLightOff); | |
|                         } | |
|                     } | |
|                 } | |
|             } | |
|         } | |
|                      | |
|         // Check current light energy (for right bar) | |
|         currentLightEnergy = 0; | |
|          | |
|         for (int i = 0; i < MAX_LIGHTS_I; i++) if (lightsI[i].active) currentLightEnergy += lightsI[i].requiredEnergy; | |
|         for (int i = 0; i < MAX_LIGHTS_II; i++) if (lightsII[i].active) currentLightEnergy += lightsII[i].requiredEnergy; | |
|         for (int i = 0; i < MAX_LIGHTS_III; i++) if (lightsIII[i].active) currentLightEnergy += lightsIII[i].requiredEnergy; | |
|      | |
|         // Check current lighted level | |
|         // Check ending conditions: all lights off, ritual level reached | |
|         previousLightedLevel = currentLightedLevel; | |
|          | |
|         currentLightedLevel = LEVEL_I; | |
|          | |
|         bool lightedLevel = true; | |
|         for (int i = 0; i < MAX_LIGHTS_I; i++) if (!lightsI[i].active) lightedLevel = false; | |
|         if (lightedLevel) currentLightedLevel = LEVEL_II; | |
| 
 | |
|         for (int i = 0; i < MAX_LIGHTS_II; i++) if (!lightsII[i].active) lightedLevel = false; | |
|         if (lightedLevel) currentLightedLevel = LEVEL_III; | |
|          | |
|         for (int i = 0; i < MAX_LIGHTS_III; i++) if (!lightsIII[i].active) lightedLevel = false; | |
|         if (lightedLevel) | |
|         { | |
|             currentLightedLevel = LEVEL_FINISHED; | |
|              | |
|             for (int i = 0; i < MAX_ENEMIES; i++) enemies[i].active = false; | |
|         } | |
|          | |
|         if (currentLightedLevel != previousLightedLevel) for (int i = 0; i < MAX_ENEMIES; i++) EnemyReset(&enemies[i]); | |
|      | |
|         ritualTime = (float)framesCounter/60; | |
|          | |
|         // Check game over condition (time run out) | |
|         if ((99.0f - ritualTime) <= 0.0f) | |
|         { | |
|             ritualTime = 99.0f; | |
|             timeOver = true; | |
|         } | |
|     } | |
|      | |
|     if (startRitual) | |
|     { | |
|         alphaRitual += 0.02f; | |
|          | |
|         SetMusicVolume(music, 1.0f - alphaRitual);   | |
|          | |
|         if (alphaRitual > 1.0f) finishScreen = 1; | |
|     } | |
|      | |
|     UpdateMusicStream(music); | |
| } | |
| 
 | |
| // Gameplay Screen Draw logic | |
| void DrawGameplayScreen(void) | |
| { | |
|     DrawTexture(background, 0, 0, WHITE); | |
|      | |
|     // DrawText("STARS ARE ALIGNED! NO TIME TO LOOSE! LIGHT MY RITUAL!", | |
|      | |
|     // Draw foreground and circles | |
|     if ((currentLightedLevel == LEVEL_FINISHED) || (currentLightedLevel == LEVEL_III)) DrawTexture(foregroundIII, 0, 0, WHITE); | |
|     else if (currentLightedLevel == LEVEL_II) DrawTexture(foregroundII, 0, 0, WHITE); | |
|     else if (currentLightedLevel == LEVEL_I) DrawTexture(foregroundI, 0, 0, WHITE); | |
| 
 | |
|     // Draw lighted circles (depends on current lighted level) | |
|     switch (currentLightedLevel) | |
|     { | |
|         case LEVEL_FINISHED: | |
|         { | |
|             DrawTexture(circleIIIon, GetScreenWidth()/2 - circleIIIon.width/2, GetScreenHeight()/2 - circleIIIon.height/2, WHITE); | |
|             DrawTexture(circleIIon, GetScreenWidth()/2 - circleIIon.width/2, GetScreenHeight()/2 - circleIIon.height/2, WHITE); | |
|             DrawTexture(circleIon, GetScreenWidth()/2 - circleIon.width/2, GetScreenHeight()/2 - circleIon.height/2, WHITE); | |
|         } break; | |
|         case LEVEL_III: | |
|         { | |
|             DrawTexture(circleIIIoff, GetScreenWidth()/2 - circleIIIoff.width/2, GetScreenHeight()/2 - circleIIIoff.height/2, WHITE); | |
|             DrawTexture(circleIIon, GetScreenWidth()/2 - circleIIon.width/2, GetScreenHeight()/2 - circleIIon.height/2, WHITE); | |
|             DrawTexture(circleIon, GetScreenWidth()/2 - circleIon.width/2, GetScreenHeight()/2 - circleIon.height/2, WHITE); | |
|         } break; | |
|         case LEVEL_II: | |
|         { | |
|             DrawTexture(circleIIoff, GetScreenWidth()/2 - circleIIoff.width/2, GetScreenHeight()/2 - circleIIoff.height/2, WHITE); | |
|             DrawTexture(circleIon, GetScreenWidth()/2 - circleIon.width/2, GetScreenHeight()/2 - circleIon.height/2, WHITE); | |
|         } break; | |
|         case LEVEL_I: | |
|         { | |
|             DrawTexture(circleIoff, GetScreenWidth()/2 - circleIoff.width/2, GetScreenHeight()/2 - circleIoff.height/2, WHITE); | |
|         } break; | |
|         default: break; | |
|     } | |
|      | |
|     // Draw lights (depends on current lighted level) | |
|     switch (currentLightedLevel) | |
|     { | |
|         case LEVEL_FINISHED: | |
|         case LEVEL_III: | |
|         { | |
|             for (int i = 0; i < MAX_LIGHTS_III; i++) | |
|             { | |
|                 //if (lightsIII[i].active) DrawCircleV(lightsIII[i].position, lightsIII[i].radius, GOLD); | |
|                 //else DrawCircleLines(lightsIII[i].position.x, lightsIII[i].position.y, lightsIII[i].radius, GRAY); | |
|                  | |
|                 if (lightsIII[i].active) | |
|                 { | |
|                     DrawTextureRec(texLight, lightsIII[i].frameRec, (Vector2){ lightsIII[i].position.x - 32, lightsIII[i].position.y - 32 }, WHITE); | |
|                     DrawTexture(lightGlow, lightsIII[i].position.x - lightGlow.width/2, lightsIII[i].position.y - lightGlow.height/2, Fade(WHITE, 0.3f)); | |
|                     DrawText(FormatText("%02i", lightsIII[i].requiredEnergy), lightsIII[i].position.x - 10, lightsIII[i].position.y + 14, 20, GRAY); | |
|                 } | |
|                 else  | |
|                 { | |
|                     DrawTextureRec(texLight, lightsIII[i].frameRec, (Vector2){ lightsIII[i].position.x - 32, lightsIII[i].position.y - 32 }, WHITE); | |
|                     DrawText(FormatText("%02i", lightsIII[i].requiredEnergy), lightsIII[i].position.x - 10, lightsIII[i].position.y + 14, 20, YELLOW); | |
|                 } | |
|             } | |
|         } | |
|         case LEVEL_II: | |
|         { | |
|             for (int i = 0; i < MAX_LIGHTS_II; i++) | |
|             { | |
|                 //if (lightsII[i].active) DrawCircleV(lightsII[i].position, lightsII[i].radius, GOLD); | |
|                 //else DrawCircleLines(lightsI[i].position.x, lightsI[i].position.y, lightsI[i].radius, GRAY); | |
|                  | |
|                 if (lightsII[i].active) | |
|                 { | |
|                     DrawTextureRec(texLight, lightsII[i].frameRec, (Vector2){ lightsII[i].position.x - 32, lightsII[i].position.y - 32 }, WHITE); | |
|                     DrawTexture(lightGlow, lightsII[i].position.x - lightGlow.width/2, lightsII[i].position.y - lightGlow.height/2, Fade(WHITE, 0.3f)); | |
|                     DrawText(FormatText("%02i", lightsII[i].requiredEnergy), lightsII[i].position.x - 10, lightsII[i].position.y + 14, 20, GRAY); | |
|                 } | |
|                 else | |
|                 { | |
|                     DrawTextureRec(texLight, lightsII[i].frameRec, (Vector2){ lightsII[i].position.x - 32, lightsII[i].position.y - 32 }, WHITE); | |
|                     DrawText(FormatText("%02i", lightsII[i].requiredEnergy), lightsII[i].position.x - 10, lightsII[i].position.y + 14, 20, YELLOW); | |
|                 } | |
|             } | |
|         } | |
|         case LEVEL_I: | |
|         { | |
|             for (int i = 0; i < MAX_LIGHTS_I; i++) | |
|             { | |
|                 //if (lightsI[i].active) DrawCircleV(lightsI[i].position, lightsI[i].radius, GOLD); | |
|                 //else DrawCircleLines(lightsI[i].position.x, lightsI[i].position.y, lightsI[i].radius, GRAY); | |
|                  | |
|                 if (lightsI[i].active)  | |
|                 {     | |
|                     DrawTextureRec(texLight, lightsI[i].frameRec, (Vector2){ lightsI[i].position.x - 32, lightsI[i].position.y - 32 }, WHITE); | |
|                     DrawTexture(lightGlow, lightsI[i].position.x - lightGlow.width/2, lightsI[i].position.y - lightGlow.height/2, Fade(WHITE, 0.3f)); | |
|                     DrawText(FormatText("%02i", lightsI[i].requiredEnergy), lightsI[i].position.x - 10, lightsI[i].position.y + 14, 20, GRAY); | |
|                 } | |
|                 else | |
|                 { | |
|                     DrawTextureRec(texLight, lightsI[i].frameRec, (Vector2){ lightsI[i].position.x - 32, lightsI[i].position.y - 32 }, WHITE); | |
|                     DrawText(FormatText("%02i", lightsI[i].requiredEnergy), lightsI[i].position.x - 10, lightsI[i].position.y + 14, 20, YELLOW); | |
|                 } | |
|             } | |
|         } | |
|         default: break; | |
|     } | |
|      | |
|     // Draw main lighter | |
|     DrawTexture(book, GetScreenWidth()/2 - book.width/2, GetScreenHeight()/2, WHITE); | |
|     DrawTexture(lightRay, GetScreenWidth()/2 - lightRay.width/2, 0, Fade(WHITE, 0.5f)); | |
|      | |
|     // Draw player | |
|     //DrawCircleV(player.position, player.radius, player.color); | |
|     DrawTexture(texPlayer, player.position.x - 32, player.position.y - 32, player.color); | |
|      | |
|     if (currentLightedLevel != LEVEL_FINISHED) | |
|     { | |
|         // Draw enemies (depends on current lighted level) | |
|         for (int i = 0; i < MAX_ENEMIES; i++) | |
|         { | |
|             if (enemies[i].active)  | |
|             { | |
|                 //DrawCircleV(enemies[i].position, enemies[i].radius, enemies[i].color); | |
|                 DrawTextureRec(texEnemy, (Rectangle){ 0, 0, 64, 64 }, (Vector2){ enemies[i].position.x - 32, enemies[i].position.y - 32 }, WHITE); | |
|             } | |
|         } | |
|          | |
|         // Draw time left for ritual | |
|         DrawTextEx(font, FormatText("%02.2f", (99.0f - ritualTime)), (Vector2){ 560, 20 }, font.baseSize, 0, WHITE); | |
|          | |
|         // Draw light energy bar | |
|         DrawRectangle(20, 30, 400, 20, GRAY); | |
|         DrawRectangle(20, 30, (400*player.lightEnergy)/MAX_PLAYER_ENERGY, 20, GOLD); | |
|         DrawRectangleLines(20, 30, 400, 20, LIGHTGRAY); | |
|         DrawText(FormatText("%03.0f", player.lightEnergy), 430, 30, 20, WHITE); | |
|          | |
|         // Draw level lighted bar (for completion) | |
|         DrawRectangle(GetScreenWidth() - 40, 30, 20, 660, GRAY); | |
|         DrawRectangle(GetScreenWidth() - 40, 30 + 660 - 660*currentLightEnergy/maxLightEnergy, 20, 660*currentLightEnergy/maxLightEnergy, YELLOW); | |
|         DrawRectangleLines(GetScreenWidth() - 40, 30, 20, 660, LIGHTGRAY); | |
|          | |
|         // Show message: "You run out of light!!!" if player.lightEnergy <= 0 | |
|         if (player.lightEnergy < 2) | |
|         { | |
|             if ((framesCounter/20)%2) DrawTextEx(font, "YOU'RE RUNNING OUT OF LIGHT!", (Vector2){ 20, 60 }, font.baseSize/2, 0, WHITE); | |
|         } | |
|     } | |
|     else if (!timeOver)   // LEVEL_FINISHED | |
|     { | |
|         DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(BLACK, 0.4f)); | |
|          | |
|         // Wait some time before jumping to ending: raylib | |
|         DrawTexture(texRitual, GetScreenWidth()/2 - texRitual.width/2, 100, WHITE); | |
|         DrawTextEx(font, FormatText("BEST LIGHTING TIME: %02.2f", ritualTime), (Vector2){ 320, 340 }, 50, 0, WHITE); | |
|         DrawTextEx(font, "PRESS ENTER to START the RITUAL", (Vector2){ 160, 480 }, 60, 0, WHITE); | |
|          | |
|         if (IsKeyPressed(KEY_ENTER)) startRitual = true; | |
|     } | |
|      | |
|     if (timeOver) | |
|     { | |
|         DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(BLACK, 0.4f)); | |
|          | |
|         DrawTexture(texTimeOver, GetScreenWidth()/2 - texTimeOver.width/2, 140, WHITE); | |
|         DrawTextEx(font, FormatText("NEXT STARS ALIGNMENT IN %i YEARS", nextStarsAlignment), (Vector2){ 200, 360 }, 50, 0, WHITE); | |
|         DrawTextEx(font, "PRESS ENTER to GO HOME...", (Vector2){ 260, 480 }, 60, 0, WHITE); | |
|          | |
|         if (IsKeyPressed(KEY_ENTER)) finishScreen = 2; | |
|     } | |
|      | |
|     if (startRitual) DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), Fade(RAYWHITE, alphaRitual)); | |
|      | |
|     if (pause) DrawTextEx(font, "RITUAL PAUSED", (Vector2){ GetScreenWidth()/2 - MeasureText("RITUAL PAUSED", 40)/2, 110 }, 50, 0, WHITE); | |
| } | |
| 
 | |
| // Gameplay Screen Unload logic | |
| void UnloadGameplayScreen(void) | |
| { | |
|     // Unload GAMEPLAY screen variables here! | |
|     UnloadTexture(background); | |
|     UnloadTexture(foregroundI); | |
|     UnloadTexture(foregroundII); | |
|     UnloadTexture(foregroundIII); | |
|     UnloadTexture(texPlayer); | |
|     UnloadTexture(texEnemy); | |
|     UnloadTexture(texLight); | |
|     UnloadTexture(lightGlow); | |
|     UnloadTexture(lightRay); | |
|     UnloadTexture(book); | |
|     UnloadTexture(texRitual); | |
|     UnloadTexture(texTimeOver); | |
|      | |
|     // Unload circles | |
|     UnloadTexture(circleIoff); | |
|     UnloadTexture(circleIIoff); | |
|     UnloadTexture(circleIIIoff); | |
|     UnloadTexture(circleIon); | |
|     UnloadTexture(circleIIon); | |
|     UnloadTexture(circleIIIon); | |
|      | |
|     // Unload sounds | |
|     UnloadSound(fxLightOn); | |
|     UnloadSound(fxLightOff); | |
|      | |
|     UnloadMusicStream(music); | |
| } | |
| 
 | |
| // Gameplay Screen should finish? | |
| int FinishGameplayScreen(void) | |
| { | |
|     return finishScreen; | |
| } | |
| 
 | |
| //------------------------------------------------------------------------------------ | |
| // Module Functions Definitions (local) | |
| //------------------------------------------------------------------------------------ | |
|  | |
| // Check two colors if equal | |
| static bool ColorEqual(Color col1, Color col2) | |
| { | |
|     return ((col1.r == col2.r) && (col1.g == col2.g) && (col1.b == col2.b) && (col1.a == col2.a)); | |
| } | |
| 
 | |
| // Substract two vectors | |
| static Vector2 Vector2Subtract(Vector2 v1, Vector2 v2) | |
| { | |
|     Vector2 result; | |
| 
 | |
|     result.x = v1.x - v2.x; | |
|     result.y = v1.y - v2.y; | |
| 
 | |
|     return result; | |
| } | |
| 
 | |
| // Normalize provided vector | |
| static void Vector2Normalize(Vector2 *v) | |
| { | |
|     float length, ilength; | |
| 
 | |
|     length = sqrt(v->x*v->x + v->y*v->y); | |
| 
 | |
|     if (length == 0) length = 1.0f; | |
| 
 | |
|     ilength = 1.0f/length; | |
| 
 | |
|     v->x *= ilength; | |
|     v->y *= ilength; | |
| } | |
| 
 | |
| // Reset enemy parameters | |
| // NOTE: Depends on currentLightedLevel | |
| static void EnemyReset(Enemy *enemy) | |
| { | |
|     enemy->active = false; | |
|     enemy->framesCounter = 0; | |
|     enemy->color = RED; | |
|     enemy->radius = 10; | |
| 
 | |
|     int side = GetRandomValue(0, 1); | |
|      | |
|     if (side) enemy->position = (Vector2){ GetRandomValue(50, 150), GetRandomValue(50, GetScreenHeight() - 50) }; | |
|     else enemy->position = (Vector2){ GetRandomValue(GetScreenWidth() - 150, GetScreenWidth() - 50), GetRandomValue(50, GetScreenHeight() - 50) }; | |
| 
 | |
|     // TODO: Choose only active lights | |
|     // TODO: if currentLightedLevel has no active lights, choose light from a lower level! | |
|      | |
|     if (currentLightedLevel == LEVEL_I) | |
|     { | |
|         enemy->targetNum = GetRandomValue(0, MAX_LIGHTS_I - 1);         // LEVEL_I | |
|         enemy->targetPos = lightsI[enemy->targetNum].position; | |
|         enemy->speed = (float)GetRandomValue(15, 20)/10.0f; | |
|         enemy->awakeFramesDelay = GetRandomValue(90, 400); | |
|     } | |
|     else if (currentLightedLevel == LEVEL_II) | |
|     { | |
|         enemy->targetNum = GetRandomValue(0, MAX_LIGHTS_II - 1);         // LEVEL_II | |
|         enemy->targetPos = lightsII[enemy->targetNum].position; | |
|         enemy->speed = (float)GetRandomValue(10, 20)/10.0f; | |
|         enemy->awakeFramesDelay = GetRandomValue(240, 800); | |
|     } | |
|     else if (currentLightedLevel == LEVEL_III) | |
|     { | |
|         enemy->targetNum = GetRandomValue(0, MAX_LIGHTS_III - 1);         // LEVEL_III | |
|         enemy->targetPos = lightsIII[enemy->targetNum].position; | |
|         enemy->speed = (float)GetRandomValue(8, 18)/10.0f; | |
|         enemy->awakeFramesDelay = GetRandomValue(180, 1200); | |
|     } | |
| }
 |