diff --git a/CMakeLists.txt b/CMakeLists.txt index 29d5981..c7bbc17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,8 +21,6 @@ set(IMGUI_SOURCES ${BINDINGS_DIR}/dcimgui.cpp ${BINDINGS_DIR}/dcimgui_impl_sdl3.cpp ${BINDINGS_DIR}/dcimgui_impl_sdlrenderer3.cpp - gui.c - gui.h ) add_executable(tibia @@ -45,7 +43,10 @@ add_executable(tibia objects.c objects.h network.c - network.h) + network.h + gui.c + gui.h +) target_include_directories(tibia PRIVATE ${IMGUI_DIR} diff --git a/gui.c b/gui.c index af018f8..7d86504 100644 --- a/gui.c +++ b/gui.c @@ -14,6 +14,14 @@ #include "window.h" #include "config.h" +#define BASE_BODY_SPRITE_ID 155 +#define BASE_HEAD_SPRITE_ID 176 +#define BASE_LEGS_SPRITE_ID 158 +#define BASE_SHOES_SPRITE_ID 161 + +static SDL_Texture *cachedCharPreview = NULL; +Character_t character = {}; + void Gui_Init(Gui_t *gui) { ImGui_CreateContext(NULL); @@ -44,12 +52,12 @@ void Gui_StartRender() { ImGui_NewFrame(); } -void Gui_Render(Gui_t *gui, Network_t *network, ConfigParams_t *configParams) { +void Gui_Render(Gui_t *gui, const Objects_t *objects, Network_t *network, ConfigParams_t *configParams) { Gui_RenderMainMenu(gui); Gui_RenderStatusBar(gui); Gui_RenderPreferences_Dialog(gui, configParams); - Gui_RenderNewGame_Dialog(gui, configParams, network); + Gui_RenderNewGame_Dialog(gui, objects, configParams, network); Gui_RenderJourneyOnward_Dialog(gui, configParams, network); } @@ -64,6 +72,7 @@ void Gui_Shutdown() { cImGui_ImplSDLRenderer3_Shutdown(); cImGui_ImplSDL3_Shutdown(); ImGui_DestroyContext(NULL); + SDL_DestroyTexture(cachedCharPreview); } void Gui_RenderMainMenu(Gui_t *gui) { @@ -276,11 +285,146 @@ void Gui_RenderPreferences_Dialog(Gui_t *gui, ConfigParams_t *configParams) { ImGui_End(); } -void Gui_RenderNewGame_Dialog(Gui_t *gui, const ConfigParams_t* configParams, Network_t *network) { +void Gui_RenderCustomizerRow(const char *label, uint8_t *value) { + if (ImGui_BeginTable(label, 3, ImGuiTableFlags_SizingFixedFit)) { + ImGui_TableSetupColumnEx("Left", ImGuiTableColumnFlags_WidthFixed, 20.0f, 0); + ImGui_TableSetupColumnEx("Text", ImGuiTableColumnFlags_WidthFixed, 45.0f, 0); + ImGui_TableSetupColumnEx("Right", ImGuiTableColumnFlags_WidthFixed, 20.0f, 0); + + ImGui_TableNextRow(); + + ImGui_TableSetColumnIndex(0); + if (ImGui_ButtonEx("<", (ImVec2){20, 20})) { + (*value)--; + SDL_DestroyTexture(cachedCharPreview); + cachedCharPreview = NULL; + } + + ImGui_TableSetColumnIndex(1); + + const float currentX = ImGui_GetCursorPosX(); + const float textWidth = ImGui_CalcTextSize(label).x; + ImGui_SetCursorPosX(currentX + (45.0f - textWidth) * 0.5f); + ImGui_Text("%s", label); + + ImGui_TableSetColumnIndex(2); + if (ImGui_ButtonEx(">", (ImVec2){20, 20})) { + (*value)++; + SDL_DestroyTexture(cachedCharPreview); + cachedCharPreview = NULL; + } + + ImGui_EndTable(); + } +} + +SDL_Texture *Gui_CombineCharacterTextures(SDL_Texture *bodyTexture, SDL_Texture *headTexture, SDL_Texture *legsTexture, + SDL_Texture *shoesTexture) { + SDL_Renderer *renderer = Window_GetRenderer(); + + SDL_Texture *target = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, + SDL_TEXTUREACCESS_TARGET, SPRITE_SIZE, SPRITE_SIZE); + + SDL_SetTextureBlendMode(target, SDL_BLENDMODE_BLEND); + SDL_SetTextureScaleMode(target, SDL_SCALEMODE_NEAREST); + SDL_SetRenderTarget(renderer, target); + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + SDL_RenderClear(renderer); + + // Draw layers in order + SDL_RenderTexture(renderer, shoesTexture, NULL, NULL); + SDL_RenderTexture(renderer, legsTexture, NULL, NULL); + SDL_RenderTexture(renderer, bodyTexture, NULL, NULL); + + // The head texture needs to be offset + // This is hardcoded in the original client + SDL_RenderTexture(renderer, headTexture, NULL, &(SDL_FRect){1.0f, -14.0f, SPRITE_SIZE, SPRITE_SIZE}); + + SDL_SetRenderTarget(renderer, NULL); + + return target; +} + +void Gui_RenderCharacterCustomizer(const Objects_t *objects) { + if (ImGui_BeginTable("CharCustomizerMain", 2, ImGuiTableFlags_SizingFixedFit)) { + ImGui_TableSetupColumnEx("Preview", ImGuiTableColumnFlags_WidthFixed, SPRITE_SIZE * 2, 0); + ImGui_TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch); + + ImGui_TableNextRow(); + + // Character Preview + ImGui_TableSetColumnIndex(0); + + const float frameHeight = ImGui_GetFrameHeight(); + const float spacing = ImGui_GetStyle()->ItemSpacing.y; + const float controlsTotalHeight = frameHeight * 4.0f + spacing * 3.0f; + const float offset = (controlsTotalHeight - SPRITE_SIZE * 2) * 0.5f; + if (offset > 0.0f) { + ImGui_SetCursorPosY(ImGui_GetCursorPosY() + offset); + } + + if (cachedCharPreview == NULL) { + SDL_Texture *bodyTexture = Objects_GetSpriteTexture(objects, 155 + character.body); + SDL_Texture *headTexture = Objects_GetSpriteTexture(objects, 176 + character.head); + SDL_Texture *legsTexture = Objects_GetSpriteTexture(objects, 158 + character.legs); + SDL_Texture *shoesTexture = Objects_GetSpriteTexture(objects, 161 + character.shoes); + cachedCharPreview = Gui_CombineCharacterTextures(bodyTexture, headTexture, legsTexture, shoesTexture); + } + + ImGui_Image((ImTextureRef){NULL, (ImTextureID) cachedCharPreview}, (ImVec2){SPRITE_SIZE * 2, SPRITE_SIZE * 2}); + + ImGui_TableSetColumnIndex(1); + + ImGui_SetCursorPosX(ImGui_GetCursorPosX() + 10.0f); + + ImGui_BeginGroup(); + + ImGui_PushID("Head"); + Gui_RenderCustomizerRow("Head", &character.head); + ImGui_PopID(); + ImGui_PushID("Body"); + Gui_RenderCustomizerRow("Body", &character.body); + ImGui_PopID(); + ImGui_PushID("Legs"); + Gui_RenderCustomizerRow("Legs", &character.legs); + ImGui_PopID(); + ImGui_PushID("Shoes"); + Gui_RenderCustomizerRow("Shoes", &character.shoes); + ImGui_PopID(); + + ImGui_EndGroup(); + + ImGui_EndTable(); + } +} + +uint16_t Gui_PackCharacter() { + // Ensure indices are within 4-bit range + character.head &= 0x0F; + character.body &= 0x0F; + character.legs &= 0x0F; + character.shoes &= 0x0F; + + uint16_t packed = 0; + packed |= character.head << 12; + packed |= character.body << 8; + packed |= character.legs << 4; + packed |= character.shoes; + + return packed; +} + +void Gui_RenderNewGame_Dialog(Gui_t *gui, const Objects_t *objects, const ConfigParams_t *configParams, Network_t *network) { if (!gui->isNewGameDialogOpen) { return; } + static int sex = 1; + static char realName[50] = ""; + static char email[50] = ""; + static char location[50] = ""; + const ImGuiViewport *viewport = ImGui_GetMainViewport(); const ImVec2 viewportCenter = { viewport->Pos.x + viewport->Size.x * 0.5f, @@ -309,7 +453,6 @@ void Gui_RenderNewGame_Dialog(Gui_t *gui, const ConfigParams_t* configParams, Ne ImGui_Text("Real Name:"); ImGui_TableSetColumnIndex(1); ImGui_SetNextItemWidth(-FLT_MIN); - static char realName[64] = ""; ImGui_InputText("##RealName", realName, sizeof(realName), 0); // Location @@ -319,7 +462,6 @@ void Gui_RenderNewGame_Dialog(Gui_t *gui, const ConfigParams_t* configParams, Ne ImGui_Text("Location:"); ImGui_TableSetColumnIndex(1); ImGui_SetNextItemWidth(-FLT_MIN); - static char location[64] = ""; ImGui_InputText("##Location", location, sizeof(location), 0); // E-Mail @@ -329,7 +471,6 @@ void Gui_RenderNewGame_Dialog(Gui_t *gui, const ConfigParams_t* configParams, Ne ImGui_Text("E-Mail:"); ImGui_TableSetColumnIndex(1); ImGui_SetNextItemWidth(-FLT_MIN); - static char email[64] = ""; ImGui_InputText("##Email", email, sizeof(email), 0); ImGui_EndTable(); @@ -339,7 +480,24 @@ void Gui_RenderNewGame_Dialog(Gui_t *gui, const ConfigParams_t* configParams, Ne // "Create Character" if (ImGui_ButtonEx("Create Character", (ImVec2){-FLT_MIN, 0})) { + // TODO: verify is both passwords match + uint8_t buffer[30 + 30 + 1 + 2 + 1 + 50 + 50 + 50] = {0}; + strncpy((char *) buffer, configParams->lastAccount, 30); + strncpy((char *) buffer + 30, configParams->lastPassword, 30); + buffer[30 + 30] = (uint8_t) sex; + + const uint16_t packedCharacter = Gui_PackCharacter(); + memcpy(buffer + 30 + 30 + 1, &packedCharacter, 2); + + + strncpy((char *) buffer + 30 + 30 + 1 + 2 + 1, realName, 50); + strncpy((char *) buffer + 30 + 30 + 1 + 2 + 1 + 50, location, 50); + strncpy((char *) buffer + 30 + 30 + 1 + 2 + 1 + 50 + 50, email, 50); + + Network_ConnectAndReportStatus(network, configParams, gui); + Network_SendPacket(network, HANDLER_LOGIN_OR_CREATE_CHAR, 0x00, buffer, 30 + 30 + 1 + 2 + 1 + 50 + 50 + 50); + gui->isNewGameDialogOpen = false; } ImGui_Dummy((ImVec2){0.0f, 10.0f}); // Spacing between buttons @@ -383,10 +541,9 @@ void Gui_RenderNewGame_Dialog(Gui_t *gui, const ConfigParams_t* configParams, Ne ImGui_AlignTextToFramePadding(); ImGui_Text("Your Sex:"); ImGui_TableSetColumnIndex(1); - static int sex = 0; - ImGui_RadioButtonIntPtr("male", &sex, 0); + ImGui_RadioButtonIntPtr("male", &sex, 1); ImGui_SameLine(); - ImGui_RadioButtonIntPtr("female", &sex, 1); + ImGui_RadioButtonIntPtr("female", &sex, 0); // Enter Password ImGui_TableNextRow(); @@ -413,20 +570,7 @@ void Gui_RenderNewGame_Dialog(Gui_t *gui, const ConfigParams_t* configParams, Ne ImGui_TableSetColumnIndex(1); - // Calculate where the cursor is to draw the rectangle - const ImVec2 p0 = ImGui_GetCursorScreenPos(); - float placeholderHeight = 90.0f; // Roughly matching the input area height - ImVec2 p1 = { p0.x + 160.0f, p0.y + placeholderHeight }; - - // Draw Gray Rectangle - ImDrawList* draw_list = ImGui_GetWindowDrawList(); - ImDrawList_AddRectFilled(draw_list,p0, p1, IM_COL32(200, 200, 200, 255)); - ImDrawList_AddRectFilled(draw_list, p0, p1, IM_COL32(100, 100, 100, 255)); - - // Add text "Placeholder" inside it just for clarity - ImGui_SetCursorPosY(ImGui_GetCursorPosY() + placeholderHeight * 0.4f); - ImGui_SetCursorPosX(ImGui_GetCursorPosX() + 40.0f); - ImGui_TextColored((ImVec4){0.3f,0.3f,0.3f,1.0f}, "Character\nControls"); + Gui_RenderCharacterCustomizer(objects); ImGui_EndTable(); } @@ -498,7 +642,14 @@ void Gui_RenderJourneyOnward_Dialog(Gui_t *gui, ConfigParams_t *configParams, Ne // Let's Go if (ImGui_ButtonEx("Let's Go", (ImVec2){-FLT_MIN, 0})) { gui->isJourneyOnwardDialogOpen = false; + + uint8_t buffer[60] = {0}; + strncpy((char *) buffer, configParams->lastAccount, 30); + strncpy((char *) buffer + 30, configParams->lastPassword, 30); + + Network_ConnectAndReportStatus(network, configParams, gui); + Network_SendPacket(network, HANDLER_LOGIN_OR_CREATE_CHAR, 0x01, buffer, 60); } ImGui_EndTable(); diff --git a/gui.h b/gui.h index 1d960b9..00f32cf 100644 --- a/gui.h +++ b/gui.h @@ -9,6 +9,14 @@ #include "config.h" #include "network.h" +#include "objects.h" + +typedef struct Character { + uint8_t head; + uint8_t body; + uint8_t legs; + uint8_t shoes; +} Character_t; typedef struct Gui { bool isPreferencesDialogOpen; @@ -20,7 +28,7 @@ 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, Network_t* network, ConfigParams_t* configParams); +void Gui_Render(Gui_t* gui, const Objects_t* objects, Network_t* network, ConfigParams_t* configParams); void Gui_FinishRender(); void Gui_Shutdown(); @@ -28,7 +36,7 @@ void Gui_RenderMainMenu(Gui_t* gui); void Gui_RenderStatusBar(const Gui_t* gui); void Gui_RenderPreferences_Dialog(Gui_t* gui, ConfigParams_t* configParams); -void Gui_RenderNewGame_Dialog(Gui_t* gui, const ConfigParams_t* configParams, Network_t* network); +void Gui_RenderNewGame_Dialog(Gui_t* gui, const Objects_t* objects, const ConfigParams_t* configParams, Network_t* network); void Gui_RenderJourneyOnward_Dialog(Gui_t* gui, ConfigParams_t* configParams, Network_t* network); void Gui_UpdateStatusBar(Gui_t* gui, const char* message); diff --git a/network.c b/network.c index 0e8082d..643fe7e 100644 --- a/network.c +++ b/network.c @@ -83,3 +83,58 @@ void Network_ConnectAndReportStatus(Network_t *network, const ConfigParams_t *co Gui_UpdateStatusBar(gui, message); } + +void Network_Send(NET_StreamSocket* socket, uint8_t* packetBuf, const uint16_t packetLen) { + const uint16_t payloadLength = packetLen - 2; + + *(uint16_t*) packetBuf = payloadLength; + + if (!NET_WriteToStreamSocket(socket, packetBuf, packetLen)) { + SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Network_Send: Failed to send packet."); + } +} + +void Network_HandleLoginOrCreateChar(const uint16_t opcode, uint8_t* packetBuf, uint16_t* packetLen, const void* data, const int dataLength) { + // Copy the opcode + packetBuf[*packetLen] = (uint8_t) opcode; + *packetLen += 1; + + // Write unk0 + const uint16_t unk0 = 1; + memcpy(&packetBuf[*packetLen], &unk0, 2); + *packetLen += 2; + + // Write unk1 + const uint16_t unk1 = 'g'; + memcpy(&packetBuf[*packetLen], &unk1, 2); + *packetLen += 2; + + memcpy(&packetBuf[*packetLen], data, dataLength); + *packetLen += dataLength; +} + +void Network_SendPacket(const Network_t* network, const PACKET_HANDLER handler, const uint16_t opcode, void* data, const int dataLength) { + if (network->socket == NULL) { + return; + } + + uint8_t packetBuf[1050]; + uint16_t packetLen = 2; + + // TODO: need this? + memset(packetBuf, 0, 1049); + + // Write handler to buffer at offset 2 + memcpy(&packetBuf[packetLen], &handler, 2); + packetLen += 2; + + switch (handler) { + case HANDLER_LOGIN_OR_CREATE_CHAR: + Network_HandleLoginOrCreateChar(opcode, packetBuf, &packetLen, data, dataLength); + break; + default: + break; + } + + Network_Send(network->socket, packetBuf, packetLen); +} diff --git a/network.h b/network.h index 0a3d615..9531b0b 100644 --- a/network.h +++ b/network.h @@ -15,9 +15,15 @@ typedef struct Network { NET_StreamSocket* socket; } Network_t; +typedef enum PacketHandler { + HANDLER_LOGIN_OR_CREATE_CHAR = 0 +} PACKET_HANDLER; + bool Network_Init(Network_t* network); void Network_Shutdown(const Network_t* network); void Network_ConnectAndReportStatus(Network_t* network, const ConfigParams_t* configParams, const Gui_t* gui); +void Network_SendPacket(const Network_t* network, PACKET_HANDLER handler, uint16_t opcode, void* data, int dataLength); + #endif //TIBIA_NETWORK_H \ No newline at end of file diff --git a/objects.c b/objects.c index 861b722..fd63ccd 100644 --- a/objects.c +++ b/objects.c @@ -3,6 +3,7 @@ // #include "objects.h" +#include "window.h" #include #include @@ -11,8 +12,6 @@ #include bool Objects_Init(Objects_t *objects) { - memset(objects->spriteTable, 0, sizeof(objects->spriteTable)); - for (int i = 0; i < THING_DATA_POOL_SIZE; i++) { objects->thingDataPool[i] = malloc(1536); @@ -114,13 +113,15 @@ bool Objects_LoadData(Objects_t *objects) { } bool Objects_LoadSprites(Objects_t *objects) { + SDL_Renderer* renderer = Window_GetRenderer(); + SDL_LogInfo(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: loading object sprites."); // Free existing sprites to prevent memory leaks - for (int i = 0; i < SPRITE_TABLE_SIZE; i++) { - if (objects->spriteTable[i] != NULL) { - free(objects->spriteTable[i]); - objects->spriteTable[i] = NULL; + for (int i = 0; i < MAX_SPRITES; i++) { + if (objects->spriteTextures[i] != NULL) { + SDL_DestroyTexture(objects->spriteTextures[i]); + objects->spriteTextures[i] = NULL; } } @@ -156,36 +157,34 @@ bool Objects_LoadSprites(Objects_t *objects) { } // Ensure ID is within bounds - if (spriteID >= SPRITE_TABLE_SIZE) { + if (spriteID >= MAX_SPRITES) { SDL_LogWarn(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: Sprite ID %d exceeds table size (%d). Skipping.", - spriteID, SPRITE_TABLE_SIZE); + spriteID, MAX_SPRITES); // Skip the data bytes so we don't desync the file stream SDL_SeekIO(file, spriteSize - 2, SDL_IO_SEEK_CUR); continue; } + const uint16_t bytesToRead = spriteSize - 2; + if (bytesToRead <= 0) { + continue; + } + uint8_t *spriteBuffer = malloc(spriteSize); if (!spriteBuffer) { SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: cannot allocate memory"); break; } - objects->spriteTable[spriteID] = spriteBuffer; - - // Store size in the first 2 bytes of the buffer - *(uint16_t *) spriteBuffer = spriteSize; - - // Read data - // Read (Size - 2) bytes into Buffer + 2 - const size_t bytesToRead = spriteSize - 2; - if (bytesToRead > 0) { - if (SDL_ReadIO(file, spriteBuffer + 2, bytesToRead) != bytesToRead) { - SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: couldn't read full sprite data"); - break; - } + if (SDL_ReadIO(file, spriteBuffer, bytesToRead) != bytesToRead) { + SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Objects_LoadSprites: couldn't read full sprite data"); + break; } + objects->spriteTextures[spriteID] = Objects_TextureFromRaw(renderer, spriteBuffer, bytesToRead); + + free(spriteBuffer); spritesLoaded++; } @@ -199,4 +198,78 @@ 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 < MAX_SPRITES; i++) { + SDL_DestroyTexture(objects->spriteTextures[i]); + } +} + +SDL_Texture * Objects_GetSpriteTexture(const Objects_t* objects, const uint16_t spriteID) { + if (spriteID < 0 || spriteID >= MAX_SPRITES) { + SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Objects_GetSpriteTexture: sprite ID out of range (ID: %d).", spriteID); + return NULL; + } + + return objects->spriteTextures[spriteID]; +} + +SDL_Texture * Objects_TextureFromRaw(SDL_Renderer *renderer, const uint8_t *rawData, const uint16_t size) { + if (!rawData) { + return NULL; + } + + SDL_Surface* surface = SDL_CreateSurface(SPRITE_SIZE, SPRITE_SIZE, SDL_PIXELFORMAT_ARGB8888); + if (!surface) { + SDL_LogError(SDL_LOG_CATEGORY_CUSTOM, "Objects_TextureFromRaw: couldn't create surface"); + return NULL; + } + + SDL_LockSurface(surface); + + uint32_t* pixels = surface->pixels; + memset(pixels, 0, SPRITE_SIZE * SPRITE_SIZE * 4); + + int currentPixel = 0; + int readOffset = 0; + while (readOffset < size && currentPixel < 1024) { + const uint16_t transparentRaw = *(uint16_t*) (rawData + readOffset); + readOffset += 2; + + const uint16_t transparentBytes = transparentRaw % 0x5A0; + const uint16_t skipPixels = transparentBytes / 3; + + currentPixel += skipPixels; + + const uint16_t colorBytes = *(uint16_t*)(rawData + readOffset); + readOffset += 2; + + const uint16_t drawPixels = colorBytes / 3; + for (int i = 0; i < drawPixels; i++) { + if (currentPixel >= 1024) { + break; + } + + const uint8_t b = rawData[readOffset++]; + const uint8_t g = rawData[readOffset++]; + const uint8_t r = rawData[readOffset++]; + + // Flip image + const int x = currentPixel % SPRITE_SIZE; + const int y = currentPixel / SPRITE_SIZE; + const int flippedY = 31 - y; + + pixels[flippedY * SPRITE_SIZE + x] = SDL_MapRGBA(SDL_GetPixelFormatDetails(surface->format), NULL, r, g, b, 255); + + currentPixel++; + } + } + + SDL_UnlockSurface(surface); + + SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_SetTextureScaleMode(texture, SDL_SCALEMODE_NEAREST); + + SDL_DestroySurface(surface); + + return texture; } diff --git a/objects.h b/objects.h index 63bff8e..760c5b3 100644 --- a/objects.h +++ b/objects.h @@ -5,12 +5,15 @@ #ifndef TIBIA_OBJECTS_H #define TIBIA_OBJECTS_H -#define SPRITE_TABLE_SIZE 500 +#define MAX_SPRITES 500 #define THING_DATA_POOL_SIZE 256 #define ACTIVE_OBJECT_LIST_SIZE 256 +#define SPRITE_SIZE 32 + #include #include +#include #pragma pack(push, 1) typedef struct { @@ -22,7 +25,7 @@ typedef struct { typedef struct Objects { // TODO: not sure about the name/purpose of these. - void* spriteTable[SPRITE_TABLE_SIZE]; + SDL_Texture* spriteTextures[MAX_SPRITES]; void* thingDataPool[THING_DATA_POOL_SIZE]; void* activeObjectList[ACTIVE_OBJECT_LIST_SIZE]; } Objects_t; @@ -32,4 +35,7 @@ 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); +SDL_Texture* Objects_TextureFromRaw(SDL_Renderer* renderer, const uint8_t* rawData, uint16_t size); + #endif //TIBIA_OBJECTS_H \ No newline at end of file diff --git a/render.c b/render.c index d4aaecd..24c5d9c 100644 --- a/render.c +++ b/render.c @@ -14,7 +14,7 @@ void Render_Frame(App_t* app) { SDL_RenderClear(renderer); Render_MainWindowBackground(app); - Gui_Render(&app->gui, &app->network, &app->configParams); + Gui_Render(&app->gui, &app->graphics, &app->network, &app->configParams); Gui_FinishRender(); SDL_RenderPresent(renderer);