From 3bb961448d2b77ce56fae4d9ecf4853e53e6fe67 Mon Sep 17 00:00:00 2001 From: Rodrigo Verdiani Date: Sat, 27 Dec 2025 18:36:03 -0300 Subject: [PATCH] feat(render): add game rendering --- app.c | 7 ++-- app.h | 3 +- gui.c | 14 ++++---- gui.h | 4 +-- objects.c | 78 ++++++++++++++++++++++++++++++++++++++-- objects.h | 6 ++++ render.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- render.h | 6 ++++ 8 files changed, 205 insertions(+), 17 deletions(-) diff --git a/app.c b/app.c index 28b9609..ccda72f 100644 --- a/app.c +++ b/app.c @@ -28,10 +28,13 @@ void App_Init(App_t *app) { return; } - if (!Objects_Init(&app->graphics)) { + if (!Objects_Init(&app->objects)) { return; } + Map_LoadSampleData(); + + app->isInGame = true; app->isRunning = true; } @@ -43,7 +46,7 @@ void App_Run(App_t *app) { } void App_Shutdown(const App_t* app) { - Objects_Destroy(&app->graphics); + Objects_Destroy(&app->objects); Map_Destroy(&app->map); Bitmap_Destroy(&app->bitmap); Gui_Shutdown(); diff --git a/app.h b/app.h index 505223d..a483259 100644 --- a/app.h +++ b/app.h @@ -12,11 +12,12 @@ typedef struct App { bool isRunning; + bool isInGame; SystemMetrics_t metrics; ConfigParams_t configParams; Map_t map; Bitmap_t bitmap; - Objects_t graphics; + Objects_t objects; Gui_t gui; } App_t; diff --git a/gui.c b/gui.c index 9f2afeb..8087dd1 100644 --- a/gui.c +++ b/gui.c @@ -39,8 +39,8 @@ void Gui_StartRender() { ImGui_NewFrame(); } -void Gui_Render(Gui_t *gui, ConfigParams_t *configParams) { - Gui_RenderMainMenu(gui); +void Gui_Render(Gui_t *gui, ConfigParams_t *configParams, const bool inGame) { + Gui_RenderMainMenu(gui, inGame); Gui_RenderPreferences_Dialog(gui, configParams); } @@ -57,7 +57,7 @@ void Gui_Shutdown() { ImGui_DestroyContext(NULL); } -void Gui_RenderMainMenu(Gui_t *gui) { +void Gui_RenderMainMenu(Gui_t *gui, const bool inGame) { if (!ImGui_BeginMainMenuBar()) { return; } @@ -82,20 +82,20 @@ void Gui_RenderMainMenu(Gui_t *gui) { if (ImGui_MenuItem("Journey Onward")) { } - if (ImGui_MenuItem("End Game")) { + if (ImGui_MenuItemEx("End Game", NULL, NULL, inGame)) { } ImGui_EndMenu(); } if (ImGui_BeginMenu("Info")) { - if (ImGui_MenuItem("Change Data")) { + if (ImGui_MenuItemEx("Change Data", NULL, NULL, inGame)) { } - if (ImGui_MenuItem("Userlist")) { + if (ImGui_MenuItemEx("Userlist", NULL, NULL, inGame)) { } - if (ImGui_MenuItem("Comments")) { + if (ImGui_MenuItemEx("Comments", NULL, NULL, inGame)) { } ImGui_EndMenu(); diff --git a/gui.h b/gui.h index 9d79a07..6f95166 100644 --- a/gui.h +++ b/gui.h @@ -16,11 +16,11 @@ typedef struct Gui { void Gui_Init(Gui_t* gui); void Gui_ProcessEvent(const SDL_Event* event); void Gui_StartRender(); -void Gui_Render(Gui_t* gui, ConfigParams_t* configParams); +void Gui_Render(Gui_t* gui, ConfigParams_t* configParams, bool inGame); void Gui_FinishRender(); void Gui_Shutdown(); -void Gui_RenderMainMenu(Gui_t* gui); +void Gui_RenderMainMenu(Gui_t* gui, bool inGame); void Gui_RenderPreferences_Dialog(Gui_t* gui, ConfigParams_t* configParams); #endif //TIBIA_GUI_H \ No newline at end of file diff --git a/objects.c b/objects.c index 861b722..455ac3f 100644 --- a/objects.c +++ b/objects.c @@ -6,9 +6,8 @@ #include #include -#include -#include -#include +#include +#include "window.h" bool Objects_Init(Objects_t *objects) { memset(objects->spriteTable, 0, sizeof(objects->spriteTable)); @@ -22,6 +21,7 @@ bool Objects_Init(Objects_t *objects) { } memset(objects->activeObjectList, 0, sizeof(objects->activeObjectList)); + memset(objects->activeTextures, 0, sizeof(objects->activeTextures)); SDL_LogInfo(SDL_LOG_CATEGORY_CUSTOM, "Objects_Init: memory allocated"); @@ -115,6 +115,7 @@ bool Objects_LoadData(Objects_t *objects) { bool Objects_LoadSprites(Objects_t *objects) { SDL_LogInfo(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: loading object sprites."); + SDL_Renderer* renderer = Window_GetRenderer(); // Free existing sprites to prevent memory leaks for (int i = 0; i < SPRITE_TABLE_SIZE; i++) { @@ -186,6 +187,9 @@ bool Objects_LoadSprites(Objects_t *objects) { } } + objects->spriteTable[spriteID] = spriteBuffer; + objects->activeTextures[spriteID] = Objects_TextureFromRaw(renderer, spriteBuffer); + spritesLoaded++; } @@ -199,4 +203,72 @@ void Objects_Destroy(const Objects_t *objects) { for (int i = 0; i < THING_DATA_POOL_SIZE; i++) { free(objects->thingDataPool[i]); } + + for (int i = 0; i < ACTIVE_OBJECT_LIST_SIZE; i++) { + if (objects->activeTextures[i]) { + SDL_DestroyTexture(objects->activeTextures[i]); + } + } +} + +SDL_Texture* Objects_GetSpriteTexture(const Objects_t *objects, const uint16_t spriteID) { + if (spriteID >= SPRITE_TABLE_SIZE) { + return NULL; + } + + return objects->activeTextures[spriteID]; +} + +SDL_Texture* Objects_TextureFromRaw(SDL_Renderer* renderer, uint8_t* rawData) { + if (!rawData) { + return NULL; + } + + const uint16_t totalSize = *(uint16_t*) rawData; + uint8_t* data = rawData + 2; + int readOffset = 0; + + SDL_Surface* surface = SDL_CreateSurface(32, 32, SDL_PIXELFORMAT_ARGB8888); + if (!surface) { + return NULL; + } + + SDL_LockSurface(surface); + uint32_t* pixels = surface->pixels; + + memset(pixels, 0, 32 * 32 * 4); + + int currentPixel = 0; + while (readOffset < totalSize - 2 && currentPixel < 1024) { + const uint16_t transparentRaw = *(uint16_t*)(data + readOffset); + readOffset += 2; + + const uint16_t transparentBytes = transparentRaw % 0x5A0; + const int skipPixels = transparentBytes / 3; + + currentPixel += skipPixels; + + const uint16_t colorBytes = *(uint16_t*)(data + readOffset); + readOffset += 2; + + const int drawPixels = colorBytes / 3; + for (int i = 0; i < drawPixels; i++) { + if (currentPixel >= 1024) { + break; + } + + const uint8_t r = data[readOffset++]; + const uint8_t g = data[readOffset++]; + const uint8_t b = data[readOffset++]; + + pixels[currentPixel++] = SDL_MapRGBA(SDL_GetPixelFormatDetails(surface->format), NULL, r, g, b, 255); + } + } + + SDL_UnlockSurface(surface); + + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_DestroySurface(surface); + + return texture; } diff --git a/objects.h b/objects.h index 63bff8e..8ab6233 100644 --- a/objects.h +++ b/objects.h @@ -11,6 +11,7 @@ #include #include +#include #pragma pack(push, 1) typedef struct { @@ -25,11 +26,16 @@ typedef struct Objects { void* spriteTable[SPRITE_TABLE_SIZE]; void* thingDataPool[THING_DATA_POOL_SIZE]; void* activeObjectList[ACTIVE_OBJECT_LIST_SIZE]; + SDL_Texture* activeTextures[SPRITE_TABLE_SIZE]; } Objects_t; bool Objects_Init(Objects_t* objects); bool Objects_LoadData(Objects_t* objects); bool Objects_LoadSprites(Objects_t* objects); void Objects_Destroy(const Objects_t* objects); +SDL_Texture* Objects_GetSpriteTexture(const Objects_t* objects, uint16_t spriteID); + +// TODO: move this somewhere else +SDL_Texture* Objects_TextureFromRaw(SDL_Renderer* renderer, uint8_t* rawData); #endif //TIBIA_OBJECTS_H \ No newline at end of file diff --git a/render.c b/render.c index a330a4b..6486131 100644 --- a/render.c +++ b/render.c @@ -6,6 +6,48 @@ #include "window.h" #include "gui.h" +#define MAP_WIDTH_TILES 15 +#define MAP_HEIGHT_TILES 11 +#define TILE_SIZE 32 + +typedef struct { + uint16_t groundSpriteID; +} MapTile; + +MapTile g_ClientMapData[MAP_WIDTH_TILES][MAP_HEIGHT_TILES]; + +void Map_LoadSampleData() { + SDL_Log("Map_LoadSampleData: Populating dummy map..."); + + // These IDs are guesses for Tibia 1.03 based on standard asset order. + // If these show up as items (like swords/apples), try swapping them. + // Usually: 0-100 contains basic ground tiles. + uint16_t ID_GRASS = 45; + uint16_t ID_DIRT = 40; + uint16_t ID_STONE = 50; + + for (int x = 0; x < MAP_WIDTH_TILES; x++) { + for (int y = 0; y < MAP_HEIGHT_TILES; y++) { + + // 1. Default to Grass + uint16_t tileID = ID_GRASS; + + // 2. Create a Checkerboard pattern + if ((x + y) % 2 == 0) { + tileID = ID_DIRT; + } + + // 3. Create a Stone path in the middle row + if (y == 5) { + tileID = ID_STONE; + } + + // 4. Assign to the map + g_ClientMapData[x][y].groundSpriteID = x * MAP_HEIGHT_TILES + y; + } + } +} + void Render_Frame(App_t* app) { Gui_StartRender(); @@ -14,7 +56,9 @@ void Render_Frame(App_t* app) { SDL_RenderClear(renderer); Render_MainWindowBackground(app); - Gui_Render(&app->gui, &app->configParams); + Render_GameView(app); + + Gui_Render(&app->gui, &app->configParams, app->isInGame); Gui_FinishRender(); SDL_RenderPresent(renderer); @@ -29,7 +73,7 @@ void Render_MainWindowBackground(const App_t *app) { return; } - const int TILE_SIZE = 128; + // const int TILE_SIZE = 128; // Calculate how many tiles we need to cover the screen const int tilesX = app->metrics.screenWidth / TILE_SIZE + 1; @@ -48,3 +92,59 @@ void Render_MainWindowBackground(const App_t *app) { } } } + +void Render_GameView(const App_t *app) { + if (!app->isInGame) { + Render_TitleScreen(app); + return; + } + + Render_Game(app); +} + +void Render_TitleScreen(const App_t *app) { + // TODO: properly calculate the window sizes and spacings + SDL_Renderer *renderer = Window_GetRenderer(); + int winW, winH; + SDL_GetRenderOutputSize(renderer, &winW, &winH); + + const SDL_FRect destRect = { + 0.0f, 30.0f, + (float)winW, (float)winH - 150.0f + }; + + SDL_RenderTexture(renderer, app->bitmap.tibiaTexture, NULL, &destRect); +} + +void Render_Game(const App_t *app) { + SDL_Renderer *renderer = Window_GetRenderer(); + + for (int x = 0; x < MAP_WIDTH_TILES; x++) { + for (int y = 0; y < MAP_HEIGHT_TILES; y++) { + const MapTile* tile = &g_ClientMapData[x][y]; + + if (tile->groundSpriteID == 0) { + continue; + } + + const int screenX = x * TILE_SIZE; + const int screenY = y * TILE_SIZE; + + SDL_Texture* texture = Objects_GetSpriteTexture(&app->objects, tile->groundSpriteID); + if (texture) { + SDL_FRect destRect = { + (float)screenX, + (float)screenY, + (float)TILE_SIZE, + (float)TILE_SIZE + }; + + SDL_RenderTexture(renderer, texture, NULL, &destRect); + } else { + SDL_FRect destRect = { (float)screenX, (float)screenY, 32.0f, 32.0f }; + SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); // Green for ground + SDL_RenderFillRect(renderer, &destRect); + } + } + } +} diff --git a/render.h b/render.h index 7f9bff7..8e328cd 100644 --- a/render.h +++ b/render.h @@ -8,8 +8,14 @@ #include "app.h" +void Map_LoadSampleData(); + void Render_Frame(App_t* app); void Render_MainWindowBackground(const App_t* app); +void Render_GameView(const App_t* app); + +void Render_TitleScreen(const App_t* app); +void Render_Game(const App_t* app); void Render_DrawWindowFrame(SDL_Renderer* renderer, int x, int y, int w, int h);