| /********************************************************************************************** | |
| * | |
| *   raylib - Advance Game template | |
| * | |
| *   Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) | |
| * | |
| *   Copyright (c) 2014-2019 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 <string.h> | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
|  | |
| //#define MAX_CODING_WORDS    12 | |
| //#define MAX_MISSION_WORDS    8 | |
| #define MAX_LINE_CHAR  30 | |
|  | |
| /* | |
| // NOTE: Coding words are generic and the same words | |
| // are used for all missions, | |
| typedef enum CodingWords { | |
|     POLLO = 0, | |
|     CONEJO, | |
|     HUEVO, | |
|     NIDO, | |
|     AIRE, | |
|     ARMARIO, | |
|     AGUJERO, | |
|     COSA, | |
|     WORD, | |
| } CodingWords; | |
| */ | |
| 
 | |
| static char *codingWords[MAX_CODING_WORDS] = { | |
|     "pollo\0", | |
|     "conejo\0", | |
|     "huevo\0", | |
|     "nido\0", | |
|     "aire\0", | |
|     "armario\0", | |
|     "agujero\0", | |
|     "platano\0", | |
|     "pastel\0", | |
|     "mercado\0", | |
|     "raton\0", | |
|     "melon\0" | |
| }; | |
| 
 | |
| // Words to be coded or coding words | |
| /*typedef struct Word { | |
|     int id; | |
|     Rectangle rec; | |
|     Rectangle iniRec; | |
|     bool hover; | |
|     bool picked; | |
|     char text[32];          // text | |
| } Word;*/ | |
| 
 | |
| /* | |
| // Mission information | |
| typedef struct Mission { | |
|     int id; | |
|     char brief[512];        // Mission briefing | |
|     char key[32];           // Mission keyword | |
|     char msg[256];          // Message to be coded | |
|     int wordsCount;         // Number of words to coded | |
|     int sols[8];            // Solution code, depends on wordsCount | |
| } Mission; | |
| */ | |
| //---------------------------------------------------------------------------------- | |
| // Global Variables Definition (local to this module) | |
| //---------------------------------------------------------------------------------- | |
|  | |
| // Gameplay screen global variables | |
| static int framesCounter; | |
| static int finishScreen; | |
| 
 | |
| static Texture2D texBackground; | |
| static Font fontMessage; | |
| static Texture2D texWordsAtlas; | |
| static Texture2D texVignette; | |
| 
 | |
| static Sound fxGrab; | |
| static Sound fxPlace; | |
| static Sound fxLeave; | |
| 
 | |
| static Music musSpy; | |
| 
 | |
| static Word words[MAX_CODING_WORDS] = { 0 }; | |
| 
 | |
| // Hay que hacerlo global, para poder consultar el resultado desde la endingscreen | |
| //static Word messageWords[MAX_MISSION_WORDS] = { 0 }; | |
|  | |
| static Mission *missions = NULL; | |
| 
 | |
| static bool canSend = false; | |
| 
 | |
| Vector2 msgOffset = { 430, 300 }; | |
| 
 | |
| //---------------------------------------------------------------------------------- | |
| // Gameplay Screen Functions Definition | |
| //---------------------------------------------------------------------------------- | |
|  | |
| // Gameplay Screen Initialization logic | |
| void InitGameplayScreen(void) | |
| { | |
|     framesCounter = 0; | |
|     finishScreen = 0; | |
| 
 | |
|     fontMessage = LoadFontEx("resources/fonts/traveling_typewriter.ttf", 30, 0, 250); | |
| 
 | |
|     texBackground = LoadTexture("resources/textures/message_background.png"); | |
|     texVignette = LoadTexture("resources/textures/message_vignette.png"); | |
| 
 | |
|     fxGrab = LoadSound("resources/audio/fx_grab.ogg"); | |
|     fxPlace = LoadSound("resources/audio/fx_place.ogg"); | |
|     fxLeave = LoadSound("resources/audio/fx_leave.ogg"); | |
| 
 | |
|     musSpy = LoadMusicStream("resources/audio/s_p_y.xm"); | |
|     PlayMusicStream(musSpy); | |
| 
 | |
| #if defined(PLATFORM_WEB) | |
|     #define WORD_ATLAS_FROM_FILE | |
| #endif | |
| #if defined(WORD_ATLAS_FROM_FILE) | |
|     texWordsAtlas = LoadTexture("resources/textures/mission_words.png"); | |
| #else | |
|     // Generate coding words atlas directly from text | |
|     Image imWordsBase = LoadImage("resources/textures/words_base.png"); | |
|     Image imWords = GenImageColor(imWordsBase.width, imWordsBase.height*MAX_CODING_WORDS, WHITE); | |
| 
 | |
|     for (int i = 0; i < MAX_CODING_WORDS; i++) | |
|     { | |
|         ImageDraw(&imWords, imWordsBase, | |
|                   (Rectangle){ 0, 0, imWordsBase.width, imWordsBase.height }, | |
|                   (Rectangle){ 0, imWordsBase.height*i, imWordsBase.width, imWordsBase.height }); | |
| 
 | |
|         ImageDrawTextEx(&imWords,(Vector2){ imWordsBase.width/2 - MeasureTextEx(fontMessage, codingWords[i], | |
|                         fontMessage.baseSize, 0).x/2, imWordsBase.height*i }, fontMessage, codingWords[i], | |
|                         fontMessage.baseSize, 0, BLACK); | |
|     } | |
| 
 | |
|     texWordsAtlas = LoadTextureFromImage(imWords); | |
| 
 | |
|     UnloadImage(imWordsBase); | |
|     UnloadImage(imWords); | |
| #endif | |
|  | |
|     // Initialize missions | |
|     // WARNING: Some problem with imWords image generation (memory leak?) could cause | |
|     // that loading missions before/after generation breaks game, on web is the other way round... :( | |
|     missions = LoadMissions("resources/missions.txt"); | |
|     TraceLog(LOG_WARNING, "Words count %i", missions[currentMission].wordsCount); | |
| 
 | |
|     // Initialize coding words | |
|     for (int i = 0; i < MAX_CODING_WORDS; i++) | |
|     { | |
|         words[i].id = -1;          // Not placed anywhere | |
|  | |
|         words[i].rec.x = 110 + 940*(i/(MAX_CODING_WORDS/2)); | |
|         words[i].rec.y = 200 + 60*(i%(MAX_CODING_WORDS/2)); | |
|         words[i].rec.width = 140;  // texWordsAtlas.width/MAX_MISSIONS | |
|         words[i].rec.height = 35;  // texWordsAtlas.height/MAX_MISSION_WORDS | |
|         words[i].iniRec = words[i].rec; | |
|         words[i].hover = false;    // Mouse hover detected | |
|         words[i].picked = false;   // Mouse picked | |
|  | |
|         //words[i].text = ''; //codingWords[i];     // Fill text if required... | |
|     } | |
| 
 | |
|     // Analize missions[currentMission].msg string for words! | |
|     int msgLen = strlen(missions[currentMission].msg); | |
| 
 | |
|     // Add '/' each MAX_LINE_CHAR chars | |
|     int currentLine = 1; | |
|     int i = currentLine * MAX_LINE_CHAR; | |
| 
 | |
|     while (i < msgLen - 1) | |
|     { | |
|         if (missions[currentMission].msg[i] == ' ') | |
|         { | |
|             missions[currentMission].msg[i] = '/'; | |
|             currentLine++; | |
|             i = currentLine*MAX_LINE_CHAR; | |
|         } | |
|         else i++; | |
|     } | |
| 
 | |
|     int currentWord = 0; | |
|     int offsetX = 0; | |
|     int offsetY = 0; | |
|     bool foundWord = false; | |
|     int wordInitPosX = 0; | |
|     int wordInitPosY = 0; | |
| 
 | |
|     for (int i = 0; i < msgLen; i++) | |
|     { | |
|         char c = missions[currentMission].msg[i]; | |
|         if (foundWord && (c == ' ' || c == '.')) | |
|         { | |
|             foundWord = false; | |
| 
 | |
|             messageWords[currentWord - 1].rec.width = (int)MeasureTextEx(fontMessage, TextSubtext(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), 30, 0).x; | |
|             messageWords[currentWord - 1].rec.height = fontMessage.baseSize; | |
| 
 | |
|             strncpy(messageWords[currentWord - 1].text, TextSubtext(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), i - wordInitPosX); | |
|         } | |
| 
 | |
|         if (c == '@') // One word to change | |
|         { | |
|             foundWord = true; | |
|             missions[currentMission].msg[i] = ' '; | |
| 
 | |
|             offsetX = (int)MeasureTextEx(fontMessage, TextSubtext(missions[currentMission].msg, wordInitPosY, (i + 1) - wordInitPosY), 30, 0).x; | |
| 
 | |
|             messageWords[currentWord].rec.x = offsetX; | |
|             messageWords[currentWord].rec.y = offsetY; | |
| 
 | |
|             wordInitPosX = i + 1; | |
| 
 | |
|             currentWord++; | |
|         } | |
|         else if (c == '/') | |
|         { | |
|             missions[currentMission].msg[i] = '\n'; | |
|             wordInitPosY = i; | |
|             offsetY += (fontMessage.baseSize + fontMessage.baseSize/2);     // raylib internal increment on line break... | |
|         } | |
|     } | |
| 
 | |
|     for (int i = 0; i < missions[currentMission].wordsCount; i++) | |
|     { | |
|         messageWords[i].id = -1;          // Not required for message words, id is the array position | |
|  | |
|         // Recalculate words rectangles considering text offset on screen | |
|         messageWords[i].rec.x += msgOffset.x; | |
|         messageWords[i].rec.y += msgOffset.y; | |
| 
 | |
|         // Recalculate words rectangle considering new width height | |
|         messageWords[i].rec.x -= (texWordsAtlas.width - messageWords[i].rec.width)/2; | |
|         messageWords[i].rec.y -= ((texWordsAtlas.height / MAX_CODING_WORDS) - messageWords[i].rec.height)/2; | |
| 
 | |
|         //Recalculate width height | |
|         messageWords[i].rec.width =  texWordsAtlas.width; | |
|         messageWords[i].rec.height = texWordsAtlas.height / MAX_CODING_WORDS; | |
| 
 | |
|         messageWords[i].hover = false;    // Mouse hover detected | |
|         messageWords[i].picked = false;   // Mouse picked | |
|     } | |
| } | |
| 
 | |
| // Gameplay Screen Update logic | |
| void UpdateGameplayScreen(void) | |
| { | |
|     UpdateMusicStream(musSpy); | |
| 
 | |
|     for (int i = 0; i < MAX_CODING_WORDS; i++) | |
|     { | |
|         if (CheckCollisionPointRec(GetMousePosition(), words[i].rec)) | |
|         { | |
|             words[i].hover = true; | |
| 
 | |
|             if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) | |
|             { | |
|                 words[i].picked = true; | |
|                 PlaySound(fxGrab); | |
|             } | |
|         } | |
|         else words[i].hover = false; | |
| 
 | |
| 
 | |
|         if (words[i].picked) | |
|         { | |
|             for (int j = 0; j < missions[currentMission].wordsCount; j++) | |
|             { | |
|                 if (CheckCollisionPointRec(GetMousePosition(), messageWords[j].rec)) messageWords[j].hover = true; | |
|                 else messageWords[j].hover = false; | |
|             } | |
| 
 | |
|             if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) | |
|             { | |
|                 words[i].picked = false; | |
| 
 | |
|                 for (int j = 0; j < missions[currentMission].wordsCount; j++) | |
|                 { | |
|                     messageWords[j].hover = false; | |
| 
 | |
|                     if (CheckCollisionPointRec(GetMousePosition(), messageWords[j].rec)) | |
|                     { | |
|                         PlaySound(fxPlace); | |
| 
 | |
|                         words[i].rec.x = messageWords[j].rec.x; | |
|                         words[i].rec.y = messageWords[j].rec.y; | |
| 
 | |
|                         if (messageWords[j].id != -1) | |
|                         { | |
|                             int id = messageWords[j].id; | |
|                             words[id].rec = words[id].iniRec; | |
|                         } | |
| 
 | |
|                         messageWords[j].id = i; | |
|                         for (int k = 0; k < missions[currentMission].wordsCount; k++) | |
|                         { | |
|                             if (j != k && messageWords[j].id == messageWords[k].id) | |
|                             { | |
|                                 messageWords[k].id = -1; | |
|                                 break; | |
|                             } | |
|                         } | |
|                         break; | |
|                     } | |
|                     else | |
|                     { | |
|                         PlaySound(fxLeave); | |
| 
 | |
|                         words[i].rec = words[i].iniRec; | |
|                         if (i == messageWords[j].id) messageWords[j].id = -1; | |
|                     } | |
|                 } | |
|             } | |
|         } | |
| 
 | |
|         // Move word picked with mouse | |
|         if (words[i].picked) | |
|         { | |
|             words[i].rec.x = GetMouseX() - words[i].rec.width/2; | |
|             words[i].rec.y = GetMouseY() - words[i].rec.height/2; | |
|         } | |
|     } | |
| 
 | |
|     canSend = true; | |
|     for (int j = 0; j < missions[currentMission].wordsCount; j++) | |
|     { | |
|         if (messageWords[j].id == -1) | |
|         { | |
|             canSend = false; | |
|             break; | |
|         } | |
|     } | |
| 
 | |
|     if (canSend && (IsKeyPressed(KEY_ENTER) || IsButtonPressed())) | |
|     { | |
|         finishScreen = true; | |
|     } | |
| } | |
| 
 | |
| // Gameplay Screen Draw logic | |
| void DrawGameplayScreen(void) | |
| { | |
|     DrawTexture(texBackground, 0, 0, WHITE); | |
| 
 | |
|     DrawTextEx(fontMessage, missions[currentMission].msg, msgOffset, fontMessage.baseSize, 0, BLACK); | |
| 
 | |
|     for (int i = 0; i < missions[currentMission].wordsCount; i++) | |
|     { | |
|         Rectangle recLines = messageWords[i].rec; | |
|         DrawRectangleLines(recLines.x, recLines.y, recLines.width, recLines.height, Fade(RED, 0.35f)); | |
|         if (messageWords[i].hover) DrawRectangleRec(messageWords[i].rec, Fade(RED, 0.30f)); | |
|         DrawText(FormatText("%i", messageWords[i].id), i*25, 0, 30, RED); | |
|     } | |
|     for (int i = 0; i < MAX_CODING_WORDS; i++) | |
|     { | |
|         if (words[i].picked) DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, MAROON); | |
|         else if (words[i].hover) DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, RED); | |
|         else DrawTextureRec(texWordsAtlas, (Rectangle){ 0, i*35, 140, 35 }, (Vector2){ words[i].rec.x, words[i].rec.y }, WHITE); | |
|     } | |
| 
 | |
|     DrawTexturePro(texVignette, (Rectangle){0,0,texVignette.width, texVignette.height}, (Rectangle){0,0,GetScreenWidth(), GetScreenHeight()}, (Vector2){0,0}, 0, WHITE); | |
| 
 | |
|     if (canSend) DrawButton("enviar"); | |
| } | |
| 
 | |
| // Gameplay Screen Unload logic | |
| void UnloadGameplayScreen(void) | |
| { | |
|     UnloadTexture(texBackground); | |
|     UnloadTexture(texVignette); | |
|     UnloadTexture(texWordsAtlas); | |
| 
 | |
|     UnloadSound(fxGrab); | |
|     UnloadSound(fxLeave); | |
|     UnloadSound(fxPlace); | |
| 
 | |
|     UnloadMusicStream(musSpy); | |
| 
 | |
|     free(missions); | |
| } | |
| 
 | |
| // Gameplay Screen should finish? | |
| int FinishGameplayScreen(void) | |
| { | |
|     return finishScreen; | |
| }
 |