b4d67499a7
Changes: - Font structure has been redesigned, CharInfo structure contained character rectangle within font texture, it has not much sense, considering that it was an information relative to the font atlas generated and not the character itself, so character rectangles have been moved out from CharInfo to Font. - CharInfo included a data parameters to contain character pixel data (usually grayscale), generated on TTF font generation. It was inconsistent with other fonts types, so, now CharInfo includes directly an Image of the glyph. - REDESIGNED: GenImageFontAtlas(), additional recs parameter added, loaded and filled inside the function to export atlas characters rectangles, instead of silently modify the input CharInfo data. - REVIEWED: ImageTextEx(), funtion retrieved the font atlas image from the GPU, that was slow and problematic in several platforms. Now it uses directly the CharInfo image. Support for unicode codepoints has also been added. - REDESIGNED: ImageDraw(), now it includes an additional parameter, the color tint, not only it could be useful for several situations but also function signature is more consistent with similar functions. - ADDED: ImageFromImage() to generate a new image from a piece of another image. - REVIEWED: GetNextCodepoint(), renamed parameters to be more clear. Also all examples and games that were affected by those changes have been reviewed.
413 lines
13 KiB
C
413 lines
13 KiB
C
/**********************************************************************************************
|
|
*
|
|
* 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 }, WHITE);
|
|
|
|
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;
|
|
}
|