News:

Welcome to RetroCoders Community

Main Menu

homeless game in Hebrew with SDL2 lib

Started by ron77, Nov 19, 2024, 12:13 PM

Previous topic - Next topic

ron77

Hello, I converted my C game "Homeless Surviving the Streets" into Hebrew and made it a graphic SDL2 game



Here is the code: (p.s. - I'm on Windows and using Windows fonts with Hebrew compatibility)

#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>
#include <windows.h>
#include <wchar.h>
#include <locale.h>

#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600
#define FONT_SIZE 20
#define MAX_STRING_LENGTH 1024

// Game state
int health = 75, money = 50, turns = 1;
bool isOver = false;

// SDL globals
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
TTF_Font* font = NULL;

// Hebrew print context
typedef struct {
    SDL_Renderer* renderer;
    TTF_Font* font;
    SDL_Color textColor;
    int xPos;
    int yPos;
} HebrewPrintContext;

HebrewPrintContext* hbPrint = NULL;
// Add this with your other constant definitions at the top of the file
const char* MENU_OPTIONS[] = {
    "1. לבקש נדבות או כסף ברחוב",
    "2. להתקשר למישהו לעזרה",
    "3. לחפש מקום לישון",
    "4. לחפש עבודה",
    "5. לקנות סמים או אלכוהול"
};

// Also make sure you have these status text constants
const char* WINDOW_TITLE = "משחק הישרדות ברחוב";
const char* DAYS_TEXT = "ימים ברחוב:";
const char* HEALTH_TEXT = "בריאות:";
const char* MONEY_TEXT = "כסף:";
const char* GAME_OVER_TEXT = "המשחק נגמר!";

// Original, longer Hebrew text arrays
const char* call[] = {
    "את/ה מתקשר/ת לסבתא שלך ושניכם בוכים... היא מבטיחה לנסות להשיג לך עזרה... היא שולחת את אהבתה... את/ה מבטיח/ה לשמור על קשר איתה",
    "התקשרת לחברה והיא נתנה לך קצת כסף ונתנה לך להישאר אצלה ללילה... זכית במקלחת טובה בגדים נקיים וארוחה טובה ואתה שוב ברחוב",
    "התקשרת למספר של קו חם לעזרה לדרי רחוב... הם באים לאסוף אותך נותנים אוכל ומחסה ללילה אתה מדבר עם עובד סוציאלי והוא נותן לך את המספר שלו למקרה שתצטרך",
    "התקשרת לקרוב משפחה אבל אף אחד לא עונה... נשארת לבד ברחוב",
    "התקשרת למשפחה והם באים לקחת אותך ונותנים לך להישאר כמה שצריך... אתה יורד מהרחוב",
    "התקשרת אבל אין תשובה"
};

const char* shelter[] = {
    "הלכת למקלט לדרי רחוב... נשדדת מהכסף שלך על ידי מכורים אלימים... הצלחת לברוח משם ובילית את שארית הלילה בפארק בוכה",
    "ניסית לישון בפארק אבל נעצרת על ידי שוטר... בילית כמה שעות בכלא שם הוכית על ידי אסירים... בבוקר אתה שוב ברחוב",
    "יש לך מספיק כסף לשכור חדר במוטל ללילה... אתה מתקלח ומכבס את הבגדים שלך וישן במיטה חמה וחולם שיש לך בית",
    "ישנת בפארק על ספסל שם קפאת למוות בגלל מזג האוויר הקר... אתה עכשיו בגן עדן מנוח על משכבך בשלום",
    "ישנת ברחוב בפינה כשאתה מתעורר אתה מוצא שטר של 20 שקל ופתק שאומר 'שאלוהים יעזור לך זה המעט שאני יכול לעשות'",
    "נעצרת על ידי המשטרה ועכשיו במקום לשרוד ברחוב אתה צריך לשרוד בכלא"
};

const char* beg[] = {
    "ישבת שעות ואנשים עוברים על פניך כאילו אתה שקוף ואז איזו גברת נחמדה באה ומזמינה אותך לאכול על חשבונה... אתה מדבר איתה שעתיים ומספר לה את הסיפור שלך... ואז היא אומרת 'ביי'",
    "ישבת בפינה ואנשים נתנו לך מטבעות ושטרות קיבלת מספיק כסף להסתדר לכמה ימים",
    "ישבת ואיזה אדם מציע לך עבודה תמורת כסף וארוחה אתה מסכים... אתה הולך לביתו ועוזר לו לצבוע את ביתו ואז הוא נותן לך 10 שקל וארוחה ביתית",
    "אף אחד לא נותן לך כלום! אתה רעב ואין לך ברירה אלא לחפש אוכל בזבל... אתה נהיה חולה... מאוד חולה ואתה מת ברחוב מהרעלת מזון... מנוח על משכבך בשלום",
    "ישבת כל היום בשביל כלום אף אחד לא נתן לך כלום"
};

const char* work[] = {
    "ניסית למצוא עבודה אבל אף אחד לא מעוניין",
    "מצאת עבודה כשוטף כלים במסעדה אחרי שבוע אתה יכול להרשות לעצמך מקום לקרוא לו בית. אתה יורד מהרחוב!",
    "מצאת עבודה אבל פוטרת ממש מהר"
};

const char* drugs[] = {
    "השתכרת והתאבנת מאלכוהול זול ולכמה שעות שכחת מכל הבעיות שלך",
    "לקחת סמים זולים מהרחוב ונהיית חולה והתמוטטת ברחוב... התעוררת בבית חולים וכשהיית בסדר אתה שוב ברחוב",
    "התאבנת ואיבדת הכרה כשאתה מתעורר בבוקר כל מה שהיה לך נעלם - נשדדת",
    "לקחת סמים ועשית אוברדוז ומת ברחוב מנוח על משכבך בשלום"
};


// Function to reverse UTF-8 Hebrew string properly
void reverseHebrewUTF8(char* str) {
    if (!str) return;
    
    int len = strlen(str);
    char* temp = (char*)malloc(len + 1);
    if (!temp) return;
    
    int i = 0;
    int j = len;
    
    temp[j] = '\0';
    
    while (i < len) {
        if ((str[i] & 0xC0) == 0x80) {
            i++;
            continue;
        }
        
        int char_len = 1;
        if ((str[i] & 0xE0) == 0xC0) char_len = 2;
        else if ((str[i] & 0xF0) == 0xE0) char_len = 3;
        else if ((str[i] & 0xF8) == 0xF0) char_len = 4;
        
        j -= char_len;
        memcpy(temp + j, str + i, char_len);
        i += char_len;
    }
    
    strcpy(str, temp);
    free(temp);
}

HebrewPrintContext* createPrintContext(SDL_Renderer* renderer, const char* fontPath, int fontSize) {
    HebrewPrintContext* context = (HebrewPrintContext*)malloc(sizeof(HebrewPrintContext));
    if (!context) return NULL;
    
    context->renderer = renderer;
    context->font = TTF_OpenFont(fontPath, fontSize);
    context->textColor = (SDL_Color){255, 255, 255, 255};
    context->xPos = WINDOW_WIDTH - 20;
    context->yPos = 20;
    
    return context;
}

// Add this helper function to check if a string is just a number
bool isNumber(const char* str) {
    while (*str) {
        if (*str < '0' || *str > '9') return false;
        str++;
    }
    return true;
}

void printRight(HebrewPrintContext* context, const char* format, ...) {
    if (!context || !context->font || !context->renderer) return;
    
    va_list args;
    va_start(args, format);
    char buffer[MAX_STRING_LENGTH];
    vsnprintf(buffer, MAX_STRING_LENGTH, format, args);
    va_end(args);
    
    char text[MAX_STRING_LENGTH] = "";
    char number[32] = "";
    int textWidth = 0;  // Store text width for later use
    
    // Find where the number starts (if any)
    char* numberStart = strrchr(buffer, ' ');
    if (numberStart) {
        // Check if what follows the space is a number
        if (isNumber(numberStart + 1)) {
            strncpy(text, buffer, numberStart - buffer);
            text[numberStart - buffer] = '\0';
            strcpy(number, numberStart + 1);
        } else {
            strcpy(text, buffer);
        }
    } else {
        if (isNumber(buffer)) {
            strcpy(number, buffer);
        } else {
            strcpy(text, buffer);
        }
    }
    
    // Render Hebrew text if present
    SDL_Surface* textSurface = NULL;
    if (text[0] != '\0') {
        reverseHebrewUTF8(text);
        textSurface = TTF_RenderUTF8_Blended(context->font, text, context->textColor);
        if (textSurface) {
            SDL_Texture* textTexture = SDL_CreateTextureFromSurface(context->renderer, textSurface);
            if (textTexture) {
                SDL_Rect destRect = {
                    context->xPos - textSurface->w,
                    context->yPos,
                    textSurface->w,
                    textSurface->h
                };
                SDL_RenderCopy(context->renderer, textTexture, NULL, &destRect);
                textWidth = textSurface->w;  // Save the width
                SDL_DestroyTexture(textTexture);
            }
            SDL_FreeSurface(textSurface);
        }
    }
    
    // Render number if present
    if (number[0] != '\0') {
        SDL_Surface* numSurface = TTF_RenderUTF8_Blended(context->font, number, context->textColor);
        if (numSurface) {
            SDL_Texture* numTexture = SDL_CreateTextureFromSurface(context->renderer, numSurface);
            if (numTexture) {
                SDL_Rect destRect = {
                    context->xPos - textWidth - numSurface->w - (text[0] ? 5 : 0),
                    context->yPos,
                    numSurface->w,
                    numSurface->h
                };
                SDL_RenderCopy(context->renderer, numTexture, NULL, &destRect);
                SDL_DestroyTexture(numTexture);
            }
            SDL_FreeSurface(numSurface);
        }
    }
    
    // Move to next line
    context->yPos += TTF_FontHeight(context->font) + 5;
}

void setPrintColor(HebrewPrintContext* context, Uint8 r, Uint8 g, Uint8 b) {
    if (!context) return;
    context->textColor.r = r;
    context->textColor.g = g;
    context->textColor.b = b;
}

void setPrintPosition(HebrewPrintContext* context, int x, int y) {
    if (!context) return;
    context->xPos = x;
    context->yPos = y;
}

char* getRandomResult(const char* array[], int size) {
    return (char*)array[rand() % size];
}

void renderGameState() {
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);
    
    setPrintPosition(hbPrint, WINDOW_WIDTH - 20, 20);
    setPrintColor(hbPrint, 255, 255, 255);
    
    // Print status
    printRight(hbPrint, "%s %d", DAYS_TEXT, turns);
    printRight(hbPrint, "%s %d", HEALTH_TEXT, health);
    printRight(hbPrint, "%s %d", MONEY_TEXT, money);
    
    // Print menu
    setPrintPosition(hbPrint, WINDOW_WIDTH - 20, 120);
    for (int i = 0; i < 5; i++) {
        printRight(hbPrint, "%s", MENU_OPTIONS[i]);
    }
    
    SDL_RenderPresent(renderer);
}

void renderResult(const char* text) {
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);
    
    // Split long text into multiple lines
    char buffer[MAX_STRING_LENGTH];
    strncpy(buffer, text, MAX_STRING_LENGTH - 1);
    buffer[MAX_STRING_LENGTH - 1] = '\0';
    
    setPrintPosition(hbPrint, WINDOW_WIDTH - 20, WINDOW_HEIGHT / 3);
    setPrintColor(hbPrint, 255, 255, 255);
    
    // Split by dots and print each part
    char* line = strtok(buffer, "...");
    while (line != NULL) {
        // Trim any leading whitespace
        while (*line == ' ') line++;
        
        if (strlen(line) > 0) {
            printRight(hbPrint, "%s...", line);
            // Move to next line position (printRight already updates yPos)
            
            setPrintPosition(hbPrint, WINDOW_WIDTH - 20, hbPrint->yPos);
        }
        line = strtok(NULL, "...");
    }
    
    SDL_RenderPresent(renderer);
    SDL_Delay(15000);
}

bool handleGameAction(int choice) {
    char* result = NULL;
    
    switch(choice) {
        case 1:
            result = getRandomResult((const char**)beg, 5);
            if (result == beg[3]) isOver = true;
            break;
        case 2:
            result = getRandomResult((const char**)call, 6);
            if (result == call[4]) isOver = true;
            break;
        case 3:
            result = getRandomResult((const char**)shelter, 6);
            if (result == shelter[3]) isOver = true;
            break;
        case 4:
            result = getRandomResult((const char**)work, 3);
            if (result == work[1]) isOver = true;
            break;
        case 5:
            result = getRandomResult((const char**)drugs, 4);
            if (result == drugs[3]) isOver = true;
            break;
        default:
            return false;
    }

    if (result) {
        renderResult(result);
        return true;
    }
    
    return false;
}

bool init() {
    // Set locale for UTF-8
    setlocale(LC_ALL, "he_IL.UTF-8");
    
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        printf("SDL initialization failed: %s\n", SDL_GetError());
        return false;
    }

    if (TTF_Init() < 0) {
        printf("SDL_ttf initialization failed: %s\n", TTF_GetError());
        return false;
    }

    window = SDL_CreateWindow(WINDOW_TITLE,
                            SDL_WINDOWPOS_UNDEFINED,
                            SDL_WINDOWPOS_UNDEFINED,
                            WINDOW_WIDTH, WINDOW_HEIGHT,
                            SDL_WINDOW_SHOWN);
    if (!window) {
        printf("Window creation failed: %s\n", SDL_GetError());
        return false;
    }

    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
    if (!renderer) {
        printf("Renderer creation failed: %s\n", SDL_GetError());
        return false;
    }

    // Try multiple Hebrew fonts
    const char* fontPaths[] = {
        "C:\\Windows\\Fonts\\arial.ttf",
        "C:\\Windows\\Fonts\\david.ttf",
        "C:\\Windows\\Fonts\\miriam.ttf"
    };
    
    hbPrint = NULL;
    for (int i = 0; i < sizeof(fontPaths)/sizeof(fontPaths[0]); i++) {
        hbPrint = createPrintContext(renderer, fontPaths[i], FONT_SIZE);
        if (hbPrint && hbPrint->font) break;
        if (hbPrint) {
            free(hbPrint);
            hbPrint = NULL;
        }
    }
    
    if (!hbPrint) {
        printf("Failed to create Hebrew print context\n");
        return false;
    }

    srand((unsigned int)time(NULL));
    return true;
}

void cleanup() {
    if (hbPrint) {
        if (hbPrint->font) TTF_CloseFont(hbPrint->font);
        free(hbPrint);
    }
    if (renderer) SDL_DestroyRenderer(renderer);
    if (window) SDL_DestroyWindow(window);
    TTF_Quit();
    SDL_Quit();
}

int main(int argc, char* argv[]) {
    if (!init()) {
        cleanup();
        return 1;
    }

    SDL_Event event;
    bool quit = false;

    while (!quit && !isOver && health > 0) {
        while (SDL_PollEvent(&event)) {
            if (event.type == SDL_QUIT) {
                quit = true;
            }
            else if (event.type == SDL_KEYDOWN) {
                int choice = 0;
                switch (event.key.keysym.sym) {
                    case SDLK_1: choice = 1; break;
                    case SDLK_2: choice = 2; break;
                    case SDLK_3: choice = 3; break;
                    case SDLK_4: choice = 4; break;
                    case SDLK_5: choice = 5; break;
                }
                
                if (choice > 0) {
                    if (handleGameAction(choice)) {
                        turns++;
                        health -= 5;
                        money -= 5;
                    }
                }
            }
        }

        renderGameState();
        SDL_Delay(16);
    }

    if (!quit) {
        setPrintColor(hbPrint, 255, 0, 0);
        setPrintPosition(hbPrint, WINDOW_WIDTH - 20, WINDOW_HEIGHT/2);
        printRight(hbPrint, "%s", GAME_OVER_TEXT);
        SDL_RenderPresent(renderer);
        SDL_Delay(3000);
    }

    cleanup();
    return 0;
}

you compile it with SDL2 and SDL2_ttf libs with this npp nppexec script

npp_save
ENV_SET InputFile = $(CURRENT_DIRECTORY)\$(FILE_NAME)
ENV_SET OutDir = $(CURRENT_DIRECTORY)
ENV_SET OutputFile = $(CURRENT_DIRECTORY)\$(NAME_PART).exe
// ENV_SET InRes = $(CURRENT_DIRECTORY)\Res\NewComCtl.rc
// ENV_SET ResObj = $(CURRENT_DIRECTORY)\Res\manifest.o

cd $(CURRENT_DIRECTORY)
//cmd /c C:\mingw32\bin -i %InRes% -F pe-i386 -o %ResObj%
// cd C:\mingw32\bin
cmd /c del %OutputFile% >nul 2>&1 
// -Wpedantic %ResObj%
cmd /c gcc -Wno-unused-label -Wall -std=c99 -Wno-unknown-pragmas -O1 -Wfatal-errors -s "%InputFile%" -lmingw32 -lSDL2main -lSDL2 -lSDL2_ttf -fexec-charset=UTF-8 -luser32 -lgdi32 -IC:\c_programming\SDL2-2.30.9\i686-w64-mingw32\include -LC:\c_programming\SDL2-2.30.9\i686-w64-mingw32\lib -o "%OutputFile%
if "$(EXITCODE)" == "0" then
  cd OutDir
  cmd /c start /wait cmd " /c %OutputFile% " & pause
endif