From 50fc4f7164fad37a7c4e542475ccb93d0f4b05ca Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sat, 17 Feb 2018 21:23:45 +0100 Subject: Reviewed transmission mission game Support string replacing to generate newspaper headline ISSUE: UTF8 characters not supported when drawing to Image --- .../resources/fonts/Lora-BoldItalic.ttf | Bin 232472 -> 0 bytes games/transmission/resources/fonts/fontTitle.ttf | Bin 125972 -> 0 bytes .../resources/fonts/mom_typewritter.ttf | Bin 0 -> 125972 bytes games/transmission/resources/missions.txt | 6 +- games/transmission/screens/screen_ending.c | 132 +++++++++++++-------- games/transmission/screens/screen_title.c | 2 +- 6 files changed, 88 insertions(+), 52 deletions(-) delete mode 100644 games/transmission/resources/fonts/Lora-BoldItalic.ttf delete mode 100644 games/transmission/resources/fonts/fontTitle.ttf create mode 100644 games/transmission/resources/fonts/mom_typewritter.ttf (limited to 'games') diff --git a/games/transmission/resources/fonts/Lora-BoldItalic.ttf b/games/transmission/resources/fonts/Lora-BoldItalic.ttf deleted file mode 100644 index 19b03226..00000000 Binary files a/games/transmission/resources/fonts/Lora-BoldItalic.ttf and /dev/null differ diff --git a/games/transmission/resources/fonts/fontTitle.ttf b/games/transmission/resources/fonts/fontTitle.ttf deleted file mode 100644 index 13f5dc7d..00000000 Binary files a/games/transmission/resources/fonts/fontTitle.ttf and /dev/null differ diff --git a/games/transmission/resources/fonts/mom_typewritter.ttf b/games/transmission/resources/fonts/mom_typewritter.ttf new file mode 100644 index 00000000..13f5dc7d Binary files /dev/null and b/games/transmission/resources/fonts/mom_typewritter.ttf differ diff --git a/games/transmission/resources/missions.txt b/games/transmission/resources/missions.txt index 346e54e4..63569608 100644 --- a/games/transmission/resources/missions.txt +++ b/games/transmission/resources/missions.txt @@ -1,4 +1,4 @@ -# Total missions +# Total missions # NOTE: Missions follow the order of this file t 4 # ----------------------------------------------------------------- @@ -10,7 +10,7 @@ k oviparo # # Message to be coded # NOTE: Sensible words must be noted using @ -m El @presidente es visto en un @hotel acompañado de su @amante . +m El @presidente es visto en un @hotel acompañado de su @amante. # # Solution to mission # NOTE: Provide the correct solution nums, according to above words @@ -26,7 +26,7 @@ k roedor # # Message to be coded # NOTE: Sensible words must be noted using @ -m Un @ovni ha sido detectado en el @cielo del @pais . Preparaos para el ataque de un @alien . +m Un @ovni ha sido detectado en el @cielo del @pais . Preparaos para el ataque de un @alien. # # NOTE: Provide the correct solution nums, according to above words # WARNING: Always provide 8 values, use -1 for not used ones diff --git a/games/transmission/screens/screen_ending.c b/games/transmission/screens/screen_ending.c index e1fd6156..d9ed4696 100644 --- a/games/transmission/screens/screen_ending.c +++ b/games/transmission/screens/screen_ending.c @@ -29,7 +29,7 @@ #include #include -#define MAX_TITLE_CHAR 128 +#define MAX_TITLE_CHAR 256 #define MAX_SUBTITLE_CHAR 256 //---------------------------------------------------------------------------------- @@ -66,7 +66,12 @@ static int state = 0; static Mission *missions = NULL; -static bool showResults = false; +static char headline[MAX_TITLE_CHAR] = "\0"; + +SpriteFont fontNews; + +// String (const char *) replacement function +static char *StringReplace(char *orig, char *rep, char *with); //---------------------------------------------------------------------------------- // Ending Screen Functions Definition @@ -84,60 +89,43 @@ void InitEndingScreen(void) texBackground = LoadTexture("resources/textures/ending_background.png"); texVignette = LoadTexture("resources/textures/message_vignette.png"); - fxNews = LoadSound("resources/audio/fx_batman.ogg"); - // TODO: Check game results! missions = LoadMissions("resources/missions.txt"); int wordsCount = missions[currentMission].wordsCount; - TraceLog(LOG_WARNING, "Words count %i", wordsCount); - - char title[MAX_TITLE_CHAR] = "\0"; - //char subtitle[MAX_SUBTITLE_CHAR] = "\0"; - - char *ptrTitle = title; - int len = 0; + + strcpy(headline, missions[currentMission].msg); // Base headline + int len = strlen(headline); + // Remove @ from headline + // TODO: Also remove additional spaces + for (int i = 0; i < len; i++) + { + if (headline[i] == '@') headline[i] = ' '; + } + for (int i = 0; i < wordsCount; i++) { - if (messageWords[i].id == missions[currentMission].sols[i]) + if (messageWords[i].id != missions[currentMission].sols[i]) { - len = strlen(messageWords[i].text); - strncpy(ptrTitle, messageWords[i].text, len); - ptrTitle += len; + // WARNING: It fails if the last sentence word has a '.' after space + char *title = StringReplace(headline, messageWords[i].text, codingWords[messageWords[i].id]); - // title[len] = ' '; - // len++; - // ptrTitle++; - } - else - { - TraceLog(LOG_WARNING, "Coding word: %s", codingWords[messageWords[i].id]); - len = strlen(codingWords[messageWords[i].id]); - TraceLog(LOG_WARNING, "Lenght: %i", len); - strncpy(ptrTitle, codingWords[messageWords[i].id], len); - ptrTitle += len; + strcpy(headline, title); // Base headline updated - // title[len] = ' '; - // len++; - // ptrTitle++; + free(title); } } - ptrTitle = '\0'; - - //TraceLog(LOG_WARNING, "Titular: %s", title); + TraceLog(LOG_WARNING, "Titular: %s", headline); // Generate newspaper with title and subtitle Image imNewspaper = LoadImage("resources/textures/ending_newspaper.png"); - SpriteFont fontNews = LoadSpriteFontEx("resources/fonts/Lora-Bold.ttf", 82, 250, 0); - ImageDrawTextEx(&imNewspaper, (Vector2){ 50, 220 }, fontNews, "FRACASO EN LA GGJ18!", fontNews.baseSize, 0, DARKGRAY); + fontNews = LoadSpriteFontEx("resources/fonts/Lora-Bold.ttf", 32, 250, 0); + ImageDrawTextEx(&imNewspaper, (Vector2){ 50, 220 }, fontNews, headline, fontNews.baseSize, 0, DARKGRAY); - // TODO: Draw subtitle message - //ImageDrawTextEx(&imNewspaper, (Vector2){ 50, 210 }, fontNews, "SUBE LA ESCALERA!", fontNews.baseSize, 0, DARKGRAY); - texNewspaper = LoadTextureFromImage(imNewspaper); - UnloadSpriteFont(fontNews); + //UnloadSpriteFont(fontNews); UnloadImage(imNewspaper); } @@ -167,8 +155,6 @@ void UpdateEndingScreen(void) if (currentMission >= totalMissions) finishScreen = 2; else finishScreen = 1; } - - if (IsKeyPressed(KEY_SPACE)) showResults = !showResults; } // Ending Screen Draw logic @@ -181,16 +167,15 @@ void DrawEndingScreen(void) (Vector2){ (float)texNewspaper.width*scale/2, (float)texNewspaper.height*scale/2 }, rotation, WHITE); DrawTextureEx(texVignette, (Vector2){ 0, 0 }, 0.0f, 2.0f, WHITE); + + // Draw debug information + DrawTextEx(fontNews, headline, (Vector2){ 10, 10 }, fontNews.baseSize, 0, RAYWHITE); - if (showResults) + for (int i = 0; i < missions[currentMission].wordsCount; i++) { - for (int i = 0; i < missions[currentMission].wordsCount; i++) - { - if (messageWords[i].id == missions[currentMission].sols[i]) DrawText(messageWords[i].text, 10, 10 + 30*i, 20, GREEN); - else DrawText(codingWords[messageWords[i].id], 10, 10 + 30*i, 20, RED); - } + DrawText(codingWords[messageWords[i].id], 10, 60 + 30*i, 20, (messageWords[i].id == missions[currentMission].sols[i]) ? GREEN : RED); } - + if (state == 1) DrawButton("continuar"); } @@ -209,4 +194,55 @@ void UnloadEndingScreen(void) int FinishEndingScreen(void) { return finishScreen; -} \ No newline at end of file +} + +// String (const char *) replacement function +// NOTE: Internally allocated memory must be freed by the user (if return != NULL) +// https://stackoverflow.com/questions/779875/what-is-the-function-to-replace-string-in-c +static char *StringReplace(char *orig, char *rep, char *with) +{ + char *result; // the return string + char *ins; // the next insert point + char *tmp; // varies + int len_rep; // length of rep (the string to remove) + int len_with; // length of with (the string to replace rep with) + int len_front; // distance between rep and end of last rep + int count; // number of replacements + + // Sanity checks and initialization + if (!orig || !rep) return NULL; + + len_rep = strlen(rep); + if (len_rep == 0) return NULL; // Empty rep causes infinite loop during count + + if (!with) with = ""; // Replace with nothing if not provided + len_with = strlen(with); + + // Count the number of replacements needed + ins = orig; + for (count = 0; tmp = strstr(ins, rep); ++count) + { + ins = tmp + len_rep; + } + + tmp = result = malloc(strlen(orig) + (len_with - len_rep)*count + 1); + + if (!result) return NULL; // Memory could not be allocated + + // First time through the loop, all the variable are set correctly from here on, + // tmp points to the end of the result string + // ins points to the next occurrence of rep in orig + // orig points to the remainder of orig after "end of rep" + while (count--) + { + ins = strstr(orig, rep); + len_front = ins - orig; + tmp = strncpy(tmp, orig, len_front) + len_front; + tmp = strcpy(tmp, with) + len_with; + orig += len_front + len_rep; // move to next "end of rep" + } + + strcpy(tmp, orig); + + return result; +} diff --git a/games/transmission/screens/screen_title.c b/games/transmission/screens/screen_title.c index dc062069..a5ad0a84 100644 --- a/games/transmission/screens/screen_title.c +++ b/games/transmission/screens/screen_title.c @@ -71,7 +71,7 @@ void InitTitleScreen(void) texBackground = LoadTexture("resources/textures/title_background.png"); fxTyping = LoadSound("resources/audio/fx_typing.ogg"); - fontTitle = LoadSpriteFontEx("resources/fonts/fontTitle.ttf", 96, 0, 0); + fontTitle = LoadSpriteFontEx("resources/fonts/mom_typewritter.ttf", 96, 0, 0); titleSize = 44; transmissionPosition = (Vector2){519, 221}; -- cgit v1.2.3 From 4492a70a4bfc32f699932eaaddc09d8225963f42 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Sun, 18 Feb 2018 18:07:57 +0100 Subject: Support UTF8 basic characters on ImageTextEx() Supported UTF8 range equivalent to [128..255] (80h..FFh) Exposed and renamed text function GetGlyphIndex() Renamed spriteFont parameter name to simply font Small security check on transmission mission ending screen --- games/transmission/screens/screen_ending.c | 2 +- src/raylib.h | 7 ++- src/text.c | 95 +++++++++++++++--------------- src/textures.c | 40 +++++++++++-- 4 files changed, 86 insertions(+), 58 deletions(-) (limited to 'games') diff --git a/games/transmission/screens/screen_ending.c b/games/transmission/screens/screen_ending.c index d9ed4696..e6ab3518 100644 --- a/games/transmission/screens/screen_ending.c +++ b/games/transmission/screens/screen_ending.c @@ -113,7 +113,7 @@ void InitEndingScreen(void) strcpy(headline, title); // Base headline updated - free(title); + if (title != NULL) free(title); } } diff --git a/src/raylib.h b/src/raylib.h index fba973d0..782dd4e5 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -948,19 +948,20 @@ RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle dest RLAPI SpriteFont GetDefaultFont(void); // Get the default SpriteFont RLAPI SpriteFont LoadSpriteFont(const char *fileName); // Load SpriteFont from file into GPU memory (VRAM) RLAPI SpriteFont LoadSpriteFontEx(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load SpriteFont from file with extended parameters -RLAPI void UnloadSpriteFont(SpriteFont spriteFont); // Unload SpriteFont from GPU memory (VRAM) +RLAPI void UnloadSpriteFont(SpriteFont font); // Unload SpriteFont from GPU memory (VRAM) // Text drawing functions RLAPI void DrawFPS(int posX, int posY); // Shows current FPS RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font) -RLAPI void DrawTextEx(SpriteFont spriteFont, const char* text, Vector2 position, // Draw text using SpriteFont and additional parameters +RLAPI void DrawTextEx(SpriteFont font, const char* text, Vector2 position, // Draw text using SpriteFont and additional parameters float fontSize, int spacing, Color tint); // Text misc. functions RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font -RLAPI Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, float fontSize, int spacing); // Measure string size for SpriteFont +RLAPI Vector2 MeasureTextEx(SpriteFont font, const char *text, float fontSize, int spacing); // Measure string size for SpriteFont RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed' RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string +RLAPI int GetGlyphIndex(SpriteFont font, int character); // Returns index position for a unicode character on sprite font //------------------------------------------------------------------------------------ // Basic 3d Shapes Drawing Functions (Module: models) diff --git a/src/text.c b/src/text.c index 227992e6..2a05773c 100644 --- a/src/text.c +++ b/src/text.c @@ -90,11 +90,9 @@ static SpriteFont defaultFont; // Default font provided by raylib //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static int GetCharIndex(SpriteFont font, int letter); - static SpriteFont LoadImageFont(Image image, Color key, int firstChar); // Load a Image font file (XNA style) #if defined(SUPPORT_FILEFORMAT_FNT) -static SpriteFont LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) +static SpriteFont LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) #endif #if defined(SUPPORT_FILEFORMAT_TTF) static SpriteFont LoadTTF(const char *fileName, int fontSize, int charsCount, int *fontChars); // Load spritefont from TTF data @@ -345,13 +343,13 @@ SpriteFont LoadSpriteFontEx(const char *fileName, int fontSize, int charsCount, } // Unload SpriteFont from GPU memory (VRAM) -void UnloadSpriteFont(SpriteFont spriteFont) +void UnloadSpriteFont(SpriteFont font) { // NOTE: Make sure spriteFont is not default font (fallback) - if (spriteFont.texture.id != GetDefaultFont().texture.id) + if (font.texture.id != GetDefaultFont().texture.id) { - UnloadTexture(spriteFont.texture); - free(spriteFont.chars); + UnloadTexture(font.texture); + free(font.chars); TraceLog(LOG_DEBUG, "Unloaded sprite font data"); } @@ -377,7 +375,7 @@ void DrawText(const char *text, int posX, int posY, int fontSize, Color color) // Draw text using SpriteFont // NOTE: chars spacing is NOT proportional to fontSize -void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float fontSize, int spacing, Color tint) +void DrawTextEx(SpriteFont font, const char *text, Vector2 position, float fontSize, int spacing, Color tint) { int length = strlen(text); int textOffsetX = 0; // Offset between characters @@ -387,7 +385,7 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float unsigned char letter; // Current character int index; // Index position in sprite font - scaleFactor = fontSize/spriteFont.baseSize; + scaleFactor = fontSize/font.baseSize; // NOTE: Some ugly hacks are made to support Latin-1 Extended characters directly // written in C code files (codified by default as UTF-8) @@ -397,7 +395,7 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float if ((unsigned char)text[i] == '\n') { // NOTE: Fixed line spacing of 1.5 lines - textOffsetY += (int)((spriteFont.baseSize + spriteFont.baseSize/2)*scaleFactor); + textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor); textOffsetX = 0; } else @@ -406,29 +404,29 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float { // Support UTF-8 encoded values from [0xc2 0x80] -> [0xc2 0xbf](¿) letter = (unsigned char)text[i + 1]; - index = GetCharIndex(spriteFont, (int)letter); + index = GetGlyphIndex(font, (int)letter); i++; } else if ((unsigned char)text[i] == 0xc3) // UTF-8 encoding identification HACK! { // Support UTF-8 encoded values from [0xc3 0x80](À) -> [0xc3 0xbf](ÿ) letter = (unsigned char)text[i + 1]; - index = GetCharIndex(spriteFont, (int)letter + 64); + index = GetGlyphIndex(font, (int)letter + 64); i++; } - else index = GetCharIndex(spriteFont, (unsigned char)text[i]); + else index = GetGlyphIndex(font, (unsigned char)text[i]); if ((unsigned char)text[i] != ' ') { - DrawTexturePro(spriteFont.texture, spriteFont.chars[index].rec, - (Rectangle){ position.x + textOffsetX + spriteFont.chars[index].offsetX*scaleFactor, - position.y + textOffsetY + spriteFont.chars[index].offsetY*scaleFactor, - spriteFont.chars[index].rec.width*scaleFactor, - spriteFont.chars[index].rec.height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f, tint); + DrawTexturePro(font.texture, font.chars[index].rec, + (Rectangle){ position.x + textOffsetX + font.chars[index].offsetX*scaleFactor, + position.y + textOffsetY + font.chars[index].offsetY*scaleFactor, + font.chars[index].rec.width*scaleFactor, + font.chars[index].rec.height*scaleFactor }, (Vector2){ 0, 0 }, 0.0f, tint); } - if (spriteFont.chars[index].advanceX == 0) textOffsetX += (int)(spriteFont.chars[index].rec.width*scaleFactor + spacing); - else textOffsetX += (int)(spriteFont.chars[index].advanceX*scaleFactor + spacing); + if (font.chars[index].advanceX == 0) textOffsetX += (int)(font.chars[index].rec.width*scaleFactor + spacing); + else textOffsetX += (int)(font.chars[index].advanceX*scaleFactor + spacing); } } } @@ -490,7 +488,7 @@ int MeasureText(const char *text, int fontSize) } // Measure string size for SpriteFont -Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, float fontSize, int spacing) +Vector2 MeasureTextEx(SpriteFont font, const char *text, float fontSize, int spacing) { int len = strlen(text); int tempLen = 0; // Used to count longer text line num chars @@ -499,8 +497,8 @@ Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, float fontSize, i float textWidth = 0; float tempTextWidth = 0; // Used to count longer text line width - float textHeight = (float)spriteFont.baseSize; - float scaleFactor = fontSize/(float)spriteFont.baseSize; + float textHeight = (float)font.baseSize; + float scaleFactor = fontSize/(float)font.baseSize; for (int i = 0; i < len; i++) { @@ -508,17 +506,17 @@ Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, float fontSize, i if (text[i] != '\n') { - int index = GetCharIndex(spriteFont, (int)text[i]); + int index = GetGlyphIndex(font, (int)text[i]); - if (spriteFont.chars[index].advanceX != 0) textWidth += spriteFont.chars[index].advanceX; - else textWidth += (spriteFont.chars[index].rec.width + spriteFont.chars[index].offsetX); + if (font.chars[index].advanceX != 0) textWidth += font.chars[index].advanceX; + else textWidth += (font.chars[index].rec.width + font.chars[index].offsetX); } else { if (tempTextWidth < textWidth) tempTextWidth = textWidth; lenCounter = 0; textWidth = 0; - textHeight += ((float)spriteFont.baseSize*1.5f); // NOTE: Fixed line spacing of 1.5 lines + textHeight += ((float)font.baseSize*1.5f); // NOTE: Fixed line spacing of 1.5 lines } if (tempLen < lenCounter) tempLen = lenCounter; @@ -533,6 +531,28 @@ Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, float fontSize, i return vec; } +// Returns index position for a unicode character on spritefont +int GetGlyphIndex(SpriteFont font, int character) +{ +#define UNORDERED_CHARSET +#if defined(UNORDERED_CHARSET) + int index = 0; + + for (int i = 0; i < font.charsCount; i++) + { + if (font.chars[i].value == character) + { + index = i; + break; + } + } + + return index; +#else + return (character - 32); +#endif +} + // Shows current FPS on top-left corner // NOTE: Uses default font void DrawFPS(int posX, int posY) @@ -559,27 +579,6 @@ void DrawFPS(int posX, int posY) // Module specific Functions Definition //---------------------------------------------------------------------------------- -static int GetCharIndex(SpriteFont font, int letter) -{ -#define UNORDERED_CHARSET -#if defined(UNORDERED_CHARSET) - int index = 0; - - for (int i = 0; i < font.charsCount; i++) - { - if (font.chars[i].value == letter) - { - index = i; - break; - } - } - - return index; -#else - return (letter - 32); -#endif -} - // Load an Image font file (XNA style) static SpriteFont LoadImageFont(Image image, Color key, int firstChar) { diff --git a/src/textures.c b/src/textures.c index 2772e246..ba0bad54 100644 --- a/src/textures.c +++ b/src/textures.c @@ -1374,6 +1374,8 @@ Image ImageTextEx(SpriteFont font, const char *text, float fontSize, int spacing { int length = strlen(text); int posX = 0; + int index; // Index position in sprite font + unsigned char character; // Current character Vector2 imSize = MeasureTextEx(font, text, font.baseSize, spacing); @@ -1389,13 +1391,39 @@ Image ImageTextEx(SpriteFont font, const char *text, float fontSize, int spacing for (int i = 0; i < length; i++) { - CharInfo letter = font.chars[(int)text[i] - 32]; - - ImageDraw(&imText, imFont, letter.rec, (Rectangle){ posX + letter.offsetX, - letter.offsetY, letter.rec.width, letter.rec.height }); + if ((unsigned char)text[i] == '\n') + { + // TODO: Support line break + } + else + { + if ((unsigned char)text[i] == 0xc2) // UTF-8 encoding identification HACK! + { + // Support UTF-8 encoded values from [0xc2 0x80] -> [0xc2 0xbf](¿) + character = (unsigned char)text[i + 1]; + index = GetGlyphIndex(font, (int)character); + i++; + } + else if ((unsigned char)text[i] == 0xc3) // UTF-8 encoding identification HACK! + { + // Support UTF-8 encoded values from [0xc3 0x80](À) -> [0xc3 0xbf](ÿ) + character = (unsigned char)text[i + 1]; + index = GetGlyphIndex(font, (int)character + 64); + i++; + } + else index = GetGlyphIndex(font, (unsigned char)text[i]); - if (letter.advanceX == 0) posX += letter.rec.width + spacing; - else posX += letter.advanceX + spacing; + CharInfo letter = font.chars[index]; + + if ((unsigned char)text[i] != ' ') + { + ImageDraw(&imText, imFont, letter.rec, (Rectangle){ posX + letter.offsetX, + letter.offsetY, letter.rec.width, letter.rec.height }); + } + + if (letter.advanceX == 0) posX += letter.rec.width + spacing; + else posX += letter.advanceX + spacing; + } } UnloadImage(imFont); -- cgit v1.2.3