/********************************************************************************************** * * raylib - Advance Game template * * Gameplay Screen Functions Definitions (Init, Update, Draw, Unload) * * Copyright (c) 2014-2018 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, 250, 0); 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; // TODO: messageWords should be reseted every mission //memcpy(messageWords, 0, sizeof(Word)*MAX_MISSION_WORDS); 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, SubText(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), 30, 0).x; messageWords[currentWord - 1].rec.height = fontMessage.baseSize; //TODO: Guardar en message strncpy(messageWords[currentWord - 1].text, SubText(missions[currentMission].msg, wordInitPosX, (i - wordInitPosX)), i - wordInitPosX); } if (c == '@') // One word to change { foundWord = true; missions[currentMission].msg[i] = ' '; offsetX = (int)MeasureTextEx(fontMessage, SubText(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; } } } } // TODO: 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; // TODO: Check if label is placed in some mission word position //if (CheckCollisionRecs(words[i].rec)) } else { //if (words[i].id != -1) } } 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; }